Changeset View
Changeset View
Standalone View
Standalone View
src/bugzillaintegration/duplicatefinderjob.cpp
Show All 21 Lines | |||||
22 | #include "drkonqi_debug.h" | 22 | #include "drkonqi_debug.h" | ||
23 | 23 | | |||
24 | #include "backtracegenerator.h" | 24 | #include "backtracegenerator.h" | ||
25 | #include "parser/backtraceparser.h" | 25 | #include "parser/backtraceparser.h" | ||
26 | #include "debuggermanager.h" | 26 | #include "debuggermanager.h" | ||
27 | #include "drkonqi.h" | 27 | #include "drkonqi.h" | ||
28 | #include "parsebugbacktraces.h" | 28 | #include "parsebugbacktraces.h" | ||
29 | 29 | | |||
30 | DuplicateFinderJob::DuplicateFinderJob(const QList<int> &bugIds, BugzillaManager *manager, QObject *parent) | 30 | DuplicateFinderJob::DuplicateFinderJob(const QList<Bugzilla::Bug::Ptr> &bugs, BugzillaManager *manager, QObject *parent) | ||
31 | : KJob(parent), | 31 | : KJob(parent), | ||
32 | m_manager(manager), | 32 | m_manager(manager), | ||
33 | m_bugIds(bugIds) | 33 | m_bugs(bugs) | ||
34 | { | 34 | { | ||
35 | qCDebug(DRKONQI_LOG) << "Possible duplicates:" << m_bugIds; | 35 | qCDebug(DRKONQI_LOG) << "Possible duplicates:" << m_bugs.size(); | ||
36 | connect(m_manager, &BugzillaManager::bugReportFetched, this, &DuplicateFinderJob::slotBugReportFetched); | 36 | connect(m_manager, &BugzillaManager::bugReportFetched, | ||
37 | connect(m_manager, &BugzillaManager::bugReportError, this, &DuplicateFinderJob::slotBugReportError); | 37 | this, &DuplicateFinderJob::slotBugReportFetched); | ||
38 | connect(m_manager, &BugzillaManager::bugReportError, | ||||
39 | this, &DuplicateFinderJob::slotError); | ||||
40 | | ||||
41 | connect(m_manager, &BugzillaManager::commentsFetched, | ||||
42 | this, &DuplicateFinderJob::slotCommentsFetched); | ||||
43 | connect(m_manager, &BugzillaManager::commentsError, | ||||
44 | this, &DuplicateFinderJob::slotError); | ||||
38 | } | 45 | } | ||
39 | 46 | | |||
40 | DuplicateFinderJob::~DuplicateFinderJob() | 47 | DuplicateFinderJob::~DuplicateFinderJob() | ||
41 | { | 48 | { | ||
42 | } | 49 | } | ||
43 | 50 | | |||
44 | void DuplicateFinderJob::start() | 51 | void DuplicateFinderJob::start() | ||
45 | { | 52 | { | ||
46 | analyzeNextBug(); | 53 | analyzeNextBug(); | ||
47 | } | 54 | } | ||
48 | 55 | | |||
49 | DuplicateFinderJob::Result DuplicateFinderJob::result() const | 56 | DuplicateFinderJob::Result DuplicateFinderJob::result() const | ||
50 | { | 57 | { | ||
51 | return m_result; | 58 | return m_result; | ||
52 | } | 59 | } | ||
53 | 60 | | |||
54 | void DuplicateFinderJob::analyzeNextBug() | 61 | void DuplicateFinderJob::analyzeNextBug() | ||
55 | { | 62 | { | ||
56 | if (m_bugIds.isEmpty()) { | 63 | if (m_bugs.isEmpty()) { | ||
57 | emitResult(); | 64 | emitResult(); | ||
58 | return; | 65 | return; | ||
59 | } | 66 | } | ||
60 | 67 | | |||
61 | const int bugId = m_bugIds.takeFirst(); | 68 | #warning fixme should we really mutate the bug? maybe just hold the comments manually. specifically we can throw away the comments but could hold the bugs in memory | ||
62 | qCDebug(DRKONQI_LOG) << "Fetching:" << bugId; | 69 | #warning holding the comments in the bug isn't too too expensive, but still a waste | ||
63 | m_manager->fetchBugReport(bugId, this); | 70 | m_bug = m_bugs.takeFirst(); | ||
71 | qCDebug(DRKONQI_LOG) << "Fetching:" << m_bug->id(); | ||||
72 | m_manager->fetchComments(m_bug, this); | ||||
64 | } | 73 | } | ||
65 | 74 | | |||
66 | void DuplicateFinderJob::fetchBug(const QString &bugId) | 75 | void DuplicateFinderJob::fetchBug(int bugId) | ||
67 | { | 76 | { | ||
68 | bool ok; | 77 | if (bugId > 0) { | ||
69 | const int num = bugId.toInt(&ok); | | |||
70 | if (ok) { | | |||
71 | qCDebug(DRKONQI_LOG) << "Fetching:" << bugId; | 78 | qCDebug(DRKONQI_LOG) << "Fetching:" << bugId; | ||
72 | m_manager->fetchBugReport(num, this); | 79 | m_manager->fetchBugReport(bugId, this); | ||
73 | } else { | 80 | } else { | ||
74 | qCDebug(DRKONQI_LOG) << "Bug id not valid:" << bugId; | 81 | qCDebug(DRKONQI_LOG) << "Bug id not valid:" << bugId; | ||
75 | analyzeNextBug(); | 82 | analyzeNextBug(); | ||
76 | } | 83 | } | ||
77 | } | 84 | } | ||
78 | 85 | | |||
79 | void DuplicateFinderJob::slotBugReportFetched(const BugReport &bug, QObject *owner) | 86 | void DuplicateFinderJob::slotBugReportFetched(const Bugzilla::Bug::Ptr &bug, QObject *owner) | ||
80 | { | 87 | { | ||
81 | if (this != owner) { | 88 | if (this != owner) { | ||
82 | return; | 89 | return; | ||
83 | } | 90 | } | ||
84 | 91 | | |||
85 | ParseBugBacktraces parse(bug, this); | 92 | #warning kinda code dupe from analyzeNextBug | ||
93 | m_bug = bug; | ||||
94 | qCDebug(DRKONQI_LOG) << "Fetching:" << m_bug->id(); | ||||
95 | m_manager->fetchComments(m_bug, this); | ||||
96 | } | ||||
97 | | ||||
98 | void DuplicateFinderJob::slotCommentsFetched(const QList<Bugzilla::Comment::Ptr> &comments, QObject *owner) | ||||
99 | { | ||||
100 | if (this != owner) { | ||||
101 | return; | ||||
102 | } | ||||
103 | | ||||
104 | // NOTE: we do not hold the comments in our bug object, once they go out | ||||
105 | // of scope they are gone again. We have no use for keeping them in memory | ||||
106 | // a user might look at 3 out of 20 bugs, and for those we can simply | ||||
107 | // request the comments again instead of holding the potentially very large | ||||
108 | // comments in memory. | ||||
109 | | ||||
110 | ParseBugBacktraces parse(comments, this); | ||||
86 | parse.parse(); | 111 | parse.parse(); | ||
87 | 112 | | |||
88 | BacktraceGenerator *btGenerator = DrKonqi::debuggerManager()->backtraceGenerator(); | 113 | BacktraceGenerator *btGenerator = DrKonqi::debuggerManager()->backtraceGenerator(); | ||
89 | const ParseBugBacktraces::DuplicateRating rating = parse.findDuplicate(btGenerator->parser()->parsedBacktraceLines()); | 114 | const ParseBugBacktraces::DuplicateRating rating = parse.findDuplicate(btGenerator->parser()->parsedBacktraceLines()); | ||
90 | qCDebug(DRKONQI_LOG) << "Duplicate rating:" << rating; | 115 | qCDebug(DRKONQI_LOG) << "Duplicate rating:" << rating; | ||
91 | 116 | | |||
92 | //TODO handle more cases here | 117 | //TODO handle more cases here | ||
93 | if (rating != ParseBugBacktraces::PerfectDuplicate) { | 118 | if (rating != ParseBugBacktraces::PerfectDuplicate) { | ||
94 | qCDebug(DRKONQI_LOG) << "Bug" << bug.bugNumber() << "most likely not a duplicate:" << rating; | 119 | qCDebug(DRKONQI_LOG) << "Bug" << m_bug->id() << "most likely not a duplicate:" << rating; | ||
95 | analyzeNextBug(); | 120 | analyzeNextBug(); | ||
96 | return; | 121 | return; | ||
97 | } | 122 | } | ||
98 | 123 | | |||
124 | bool unknownStatus = (m_bug->status() == Bugzilla::Bug::Status::Unknown); | ||||
125 | bool unknownResolution = (m_bug->resolution() == Bugzilla::Bug::Resolution::Unknown); | ||||
126 | | ||||
99 | //The Bug is a duplicate, now find out the status and resolution of the existing report | 127 | //The Bug is a duplicate, now find out the status and resolution of the existing report | ||
100 | if (bug.resolutionValue() == BugReport::Duplicate) { | 128 | if (m_bug->resolution() == Bugzilla::Bug::Resolution::DUPLICATE) { | ||
101 | qCDebug(DRKONQI_LOG) << "Found duplicate is a duplicate itself."; | 129 | qCDebug(DRKONQI_LOG) << "Found duplicate is a duplicate itself."; | ||
102 | if (!m_result.duplicate) { | 130 | if (!m_result.duplicate) { | ||
103 | m_result.duplicate = bug.bugNumberAsInt(); | 131 | m_result.duplicate = m_bug->id(); | ||
104 | } | 132 | } | ||
105 | fetchBug(bug.markedAsDuplicateOf()); | 133 | fetchBug(m_bug->dupe_of()); | ||
106 | } else if ((bug.statusValue() == BugReport::UnknownStatus) || (bug.resolutionValue() == BugReport::UnknownResolution)) { | 134 | #warning fixme can unknown happen even or could it before how does this work now | ||
135 | } else if (unknownStatus || unknownResolution) { | ||||
107 | qCDebug(DRKONQI_LOG) << "Either the status or the resolution is unknown."; | 136 | qCDebug(DRKONQI_LOG) << "Either the status or the resolution is unknown."; | ||
108 | qCDebug(DRKONQI_LOG) << "Status \"" << bug.bugStatus() << "\" known:" << (bug.statusValue() != BugReport::UnknownStatus); | 137 | qCDebug(DRKONQI_LOG) << "Status \"" << m_bug->status() << "\" known:" << !unknownStatus; | ||
109 | qCDebug(DRKONQI_LOG) << "Resolution \"" << bug.resolution() << "\" known:" << (bug.resolutionValue() != BugReport::UnknownResolution); | 138 | qCDebug(DRKONQI_LOG) << "Resolution \"" << m_bug->resolution() << "\" known:" << !unknownResolution; | ||
110 | analyzeNextBug(); | 139 | analyzeNextBug(); | ||
111 | } else { | 140 | } else { | ||
112 | if (!m_result.duplicate) { | 141 | if (!m_result.duplicate) { | ||
113 | m_result.duplicate = bug.bugNumberAsInt(); | 142 | m_result.duplicate = m_bug->id(); | ||
114 | } | 143 | } | ||
115 | m_result.parentDuplicate = bug.bugNumberAsInt(); | 144 | m_result.parentDuplicate = m_bug->id(); | ||
116 | m_result.status = bug.statusValue(); | 145 | m_result.status = m_bug->status(); | ||
117 | m_result.resolution = bug.resolutionValue(); | 146 | m_result.resolution = m_bug->resolution(); | ||
118 | qCDebug(DRKONQI_LOG) << "Found duplicate information (id/status/resolution):" << bug.bugNumber() << bug.bugStatus() << bug.resolution(); | 147 | qCDebug(DRKONQI_LOG) << "Found duplicate information (id/status/resolution):" | ||
148 | << m_bug->id() << m_bug->status() << m_bug->resolution(); | ||||
119 | emitResult(); | 149 | emitResult(); | ||
120 | } | 150 | } | ||
121 | } | 151 | } | ||
122 | 152 | | |||
123 | void DuplicateFinderJob::slotBugReportError(const QString &message, QObject *owner) | 153 | void DuplicateFinderJob::slotError(const QString &message, QObject *owner) | ||
124 | { | 154 | { | ||
125 | if (this != owner) { | 155 | if (this != owner) { | ||
126 | return; | 156 | return; | ||
127 | } | 157 | } | ||
128 | qCDebug(DRKONQI_LOG) << "Error fetching bug:" << message; | 158 | qCDebug(DRKONQI_LOG) << "Error fetching bug:" << message; | ||
129 | analyzeNextBug(); | 159 | analyzeNextBug(); | ||
130 | } | 160 | } | ||
161 | |