Merge lp:~jcsackett/lp-release-manager-tools/filter-canonical into lp:~sinzui/lp-release-manager-tools/trunk

Proposed by j.c.sackett
Status: Merged
Merged at revision: 21
Proposed branch: lp:~jcsackett/lp-release-manager-tools/filter-canonical
Merge into: lp:~sinzui/lp-release-manager-tools/trunk
Diff against target: 164 lines (+91/-14)
1 file modified
report_branch_contributions.py (+91/-14)
To merge this branch: bzr merge lp:~jcsackett/lp-release-manager-tools/filter-canonical
Reviewer Review Type Date Requested Status
Curtis Hovey code Approve
Review via email: mp+203546@code.launchpad.net

Commit message

Adds date filtering as an option instead of author filtering; provides filtering out commits by Canonical employees within that range.

Description of the change

This branch makes several additions to `report_branch_contributions.py`:
* It adds a date based log formatter, so that commits made within a specified date range can be found.
* It separates finding the matched_revisions from analyzing their changes, for use in juju-reports
* It introduces an additional option to filter out commits made by Canonical employees, so we can find community based commits.

To post a comment you must log in.
Revision history for this message
Curtis Hovey (sinzui) wrote :

Thank you Jon.

I have a couple questions, but I don't see anything blocks your branch.

I prefer datetime.datetime.utcnow() because Lp tends to normalise dates as utc. This wont be true for dates in the revisions, i think. Does utcnow() break your work?

The end_date is not inclusive is it. I need to pass tomorrow's date to get today's date?

review: Needs Information (code)
Revision history for this message
j.c.sackett (jcsackett) wrote :

utcnow is fine. I've made that change.

end_date, currently, *is* inclusive (<= end_date). Do we want it to be exclusive?

26. By j.c.sackett

Comments from review.

Revision history for this message
Curtis Hovey (sinzui) wrote :

Thank you Jon. We do want *inclusive* dates. LGTM.

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'report_branch_contributions.py'
2--- report_branch_contributions.py 2014-01-15 22:22:38 +0000
3+++ report_branch_contributions.py 2014-01-28 17:16:16 +0000
4@@ -3,6 +3,7 @@
5
6 from __future__ import print_function
7
8+import datetime
9 from optparse import OptionParser
10 import re
11 import subprocess
12@@ -22,6 +23,43 @@
13 DELETES_PATTERN = re.compile(r"(\d+) deletion")
14
15
16+class DateLogFormatter(log.LogFormatter):
17+ """Log all commits between two dates"""
18+
19+ supports_merge_revisions = False
20+
21+ def __init__(self, start_date, end_date, filter_canonical=False):
22+ super(DateLogFormatter, self).__init__(to_file=None)
23+ self.start_date = start_date
24+ self.end_date = end_date
25+ self.current_rev = None
26+ self.matched_revs = []
27+ self.filter = filter_canonical
28+
29+ def _only_canonical_contributors(self, rev):
30+ # Determine if the only authors of the commit are from Canonical
31+ canonical = True
32+ for author in rev.get_apparent_authors():
33+ email = parse_username(author)
34+ if '@canonical' not in author:
35+ canonical = False
36+ break
37+ return canonical
38+
39+ def log_revision(self, lr):
40+ """Log a revision that made within the supplied dates.
41+
42+ :param lr: The LogRevision to check.
43+ """
44+ if self.filter and self._only_canonical_contributors(lr.rev):
45+ # If we're filtering, and this revision has only canonical
46+ # contributors, there's no need to check the revision date.
47+ return
48+ rev_date = datetime.datetime.fromtimestamp(lr.rev.timestamp)
49+ if rev_date >= self.start_date and rev_date <= self.end_date:
50+ self.matched_revs.append(lr.rev)
51+
52+
53 class AuthorLogFormatter(log.LogFormatter):
54 """Log contributions by an author."""
55
56@@ -64,22 +102,24 @@
57 return diffstat_proc.communicate()[0].strip()
58
59
60-def show_contributions(branch, author, options):
61+def get_contributions(branch, log_formatter, start_revno, end_revno=None):
62 logger = log.Logger(
63 branch, {
64 "direction": "forward",
65- "start_revision": options.start_revno,
66- "end_revision": options.end_revno or branch.revno(),
67+ "start_revision": start_revno,
68+ "end_revision": end_revno or branch.revno(),
69 "levels": 0,
70 })
71- log_formatter = AuthorLogFormatter(author)
72 logger.show(log_formatter)
73+ return log_formatter.matched_revs
74+
75+def show_contributions(branch, matched_revs, options):
76 total_commits = 0
77 total_inserts = 0
78 total_delta = 0
79 try:
80 branch.lock_read()
81- for rev in log_formatter.matched_revs:
82+ for rev in matched_revs:
83 total_commits += 1
84 revid = rev.revision_id
85 diffstat = get_diffstat(
86@@ -95,18 +135,49 @@
87 print("Revision %s: %s (delta: %d)" % (revno, diffstat, delta))
88 finally:
89 branch.unlock()
90-
91 if options.verbose:
92- print("Total %d commits for %s adding %d lines with a delta of %+d" % (
93- total_commits, author, total_inserts, total_delta))
94+ if options.dates:
95+ print("Total %d commits from %s to %s adding %d lines with a delta of %+d" % (
96+ total_commits, params[0], params[1], total_inserts, total_delta))
97+ else:
98+ print("Total %d commits for %s adding %d lines with a delta of %+d" % (
99+ total_commits, params, total_inserts, total_delta))
100 else:
101 print("%d:%d:%+d" % (total_commits, total_inserts, total_delta))
102
103
104+def get_author(args, branch):
105+ if len(args) == 1:
106+ author = args[0]
107+ else:
108+ author = extract_email_address(branch.get_config_stack().get("email"))
109+ return author
110+
111+
112+def get_dates(args):
113+ fmt = '%Y-%m-%d'
114+ if len(args) == 1:
115+ start_date = datetime.datetime.strptime(args[0], fmt)
116+ end_date = datetime.datetime.utcnow()
117+ elif len(args) == 2:
118+ start_date = datetime.datetime.strptime(args[0], fmt)
119+ end_date = datetime.datetime.strptime(args[1], fmt)
120+ else:
121+ start_date = datetime.datetime(1990, 1, 1)
122+ end_date = datetime.datetime.utcnow()
123+ return (start_date, end_date)
124+
125+
126 def main(args):
127- usage = "%prog [options] [AUTHOR]"
128+ usage = "%prog [options] [AUTHOR|START_DATE] [END_DATE]"
129 parser = OptionParser(usage=usage)
130 parser.add_option(
131+ '-d', '--dates', dest='dates', action='store_true', default=False,
132+ help='Find revisions by date, not by author.')
133+ parser.add_option(
134+ '-c', dest='filter_canonical', action='store_true', default=False,
135+ help="Filter out revisions contributed only by Canonical developers.")
136+ parser.add_option(
137 '-s', '--start-revno', dest='start_revno', type='int', default=1900,
138 help='Search starting from revno, 1900 is default.')
139 parser.add_option(
140@@ -119,15 +190,21 @@
141 '-v', '--verbose', action='store_true', default=False,
142 help='Show detailed information about each revision.')
143 options, args = parser.parse_args(args)
144- if len(args) > 1:
145+ if ((len(args) > 1 and not options.dates) or
146+ (len(args) > 2 and options.dates)):
147 parser.print_help()
148 return 1
149 branch = Branch.open(options.branch)
150- if len(args) == 1:
151- author = args[0]
152+ if options.dates:
153+ params = get_dates(args)
154+ params += (options.filter_canonical,)
155+ log_formatter = DateLogFormatter(*params)
156 else:
157- author = extract_email_address(branch.get_config_stack().get("email"))
158- show_contributions(branch, author, options)
159+ params = get_author(args, branch)
160+ log_formatter = AuthorLogFormatter(params)
161+ matched_revs = get_contributions(
162+ branch, log_formatter, options.start_revno, options.end_revno)
163+ show_contributions(branch, matched_revs, options)
164 return 0
165
166

Subscribers

People subscribed via source and target branches

to all changes: