1
by Alex Chiang
Initial commit. |
1 |
#!/usr/bin/env python
|
2 |
#
|
|
3 |
# laika prints a summary of the Launchpad bugs you touched this week
|
|
4 |
#
|
|
5 |
# man's best friend guides you through Launchpad
|
|
6 |
# 'Laika died within hours after launch' => an aptly named tool ;)
|
|
7 |
# http://en.wikipedia.org/wiki/Laika
|
|
8 |
#
|
|
9 |
# Copyright 2010 Alex Chiang <achiang@canonical.com>
|
|
4
by Paul Hummer
Tabs make baby Jesus cry in his manger |
10 |
#
|
1
by Alex Chiang
Initial commit. |
11 |
# This program is distributed under the terms of the
|
12 |
# GNU General Public License version 2.
|
|
13 |
||
5
by Paul Hummer
Created Report class, using it now instead of the functional |
14 |
import datetime |
12
by Paul Hummer
Created OptionParser for, uh, you know, parsing options. |
15 |
from optparse import OptionParser |
1
by Alex Chiang
Initial commit. |
16 |
import os |
17 |
import re |
|
3
by Alex Chiang
* Add command line parsing for: |
18 |
import sys |
5
by Paul Hummer
Created Report class, using it now instead of the functional |
19 |
|
20 |
from launchpadlib.launchpad import Launchpad |
|
21 |
||
19
by Paul Hummer
If we're going to use a constant, we should make it _appear_ like a constant |
22 |
UTCNOW = datetime.datetime.utcnow() |
1
by Alex Chiang
Initial commit. |
23 |
|
5
by Paul Hummer
Created Report class, using it now instead of the functional |
24 |
|
25 |
class Report(object): |
|
26 |
'''An activity report for a specified Launchpad user.
|
|
27 |
||
28 |
In order of decreasing importance, search and display activity on
|
|
29 |
bugs assigned to me, then on bugs that I actually commented upon,
|
|
30 |
and finally bugs that I just opened, but did nothing further with.
|
|
31 |
||
32 |
Avoids duplication of activity. So if the status was changed on
|
|
33 |
a bug assigned to you and you also commented on it, only report the
|
|
34 |
status change.
|
|
35 |
'''
|
|
36 |
||
11
by Paul Hummer
Moved my_bugs to Report.bugs |
37 |
bugs = {} |
38 |
||
16
by Paul Hummer
Moved the window var to be a Report method |
39 |
def __init__(self, user, window): |
13
by Paul Hummer
Fixed a bug where I did something stupid in the last refactoring branch and made it so that you couldn't specify the user. It was always launchpad.me |
40 |
cachedir = "/home/" + user + "/.launchpadlib/cache/" |
41 |
self.launchpad = Launchpad.login_with('laika', 'production', cachedir) |
|
42 |
||
43 |
self.user = self.launchpad.people[user] |
|
16
by Paul Hummer
Moved the window var to be a Report method |
44 |
self.window = window |
5
by Paul Hummer
Created Report class, using it now instead of the functional |
45 |
|
9
by Paul Hummer
Clearly, I didn't test my last few changes. I need some tests |
46 |
def print_header(self, header): |
7
by Paul Hummer
Moved print_header to be Report.print_header |
47 |
print "==", header, "==" |
48 |
||
9
by Paul Hummer
Clearly, I didn't test my last few changes. I need some tests |
49 |
def in_window(self, date): |
6
by Paul Hummer
Moved in_window to Report |
50 |
'''Timezones do not exist, all datetime objects have to be naive.
|
51 |
||
52 |
Time zone aware means broken.
|
|
53 |
http://www.enricozini.org/2009/debian/using-python-datetime/
|
|
54 |
'''
|
|
16
by Paul Hummer
Moved the window var to be a Report method |
55 |
win = datetime.timedelta(self.window) |
6
by Paul Hummer
Moved in_window to Report |
56 |
date = date.replace(tzinfo=None) |
19
by Paul Hummer
If we're going to use a constant, we should make it _appear_ like a constant |
57 |
delta = UTCNOW - date |
6
by Paul Hummer
Moved in_window to Report |
58 |
return delta <= win |
59 |
||
9
by Paul Hummer
Clearly, I didn't test my last few changes. I need some tests |
60 |
def print_bugid(self, task): |
8
by Paul Hummer
Moved print_bugid to Report.print_bugid |
61 |
'''Using this interface adds the bug to global bug list.'''
|
62 |
ago = "" |
|
11
by Paul Hummer
Moved my_bugs to Report.bugs |
63 |
self.bugs[task.bug.id] = 1 |
19
by Paul Hummer
If we're going to use a constant, we should make it _appear_ like a constant |
64 |
delta = UTCNOW - task.bug.date_last_updated.replace(tzinfo=None) |
8
by Paul Hummer
Moved print_bugid to Report.print_bugid |
65 |
if delta.days > 0: |
66 |
ago = "%d day%s" % (delta.days, "s" if delta.days > 1 else "") |
|
67 |
||
68 |
hours = delta.seconds / 3600 |
|
69 |
if hours > 0: |
|
70 |
ago += ", " if ago else "" |
|
71 |
ago += "%d hour%s" % (hours, "s" if hours > 1 else "") |
|
72 |
||
73 |
minutes = (delta.seconds - (hours * 3600)) / 60 |
|
74 |
if minutes > 0: |
|
75 |
ago += ", " if ago else "" |
|
76 |
ago += "%d minute%s" % (minutes, "s" if minutes > 1 else "") |
|
77 |
||
78 |
print task.title |
|
79 |
print "https://launchpad.net/bugs/" + str(task.bug.id) |
|
80 |
print "last updated", ago, "ago" |
|
81 |
||
5
by Paul Hummer
Created Report class, using it now instead of the functional |
82 |
def print_assignments(self): |
83 |
statuses = ['closed', 'fix_released', 'fix_committed', |
|
84 |
'in_progress', 'triaged', 'confirmed', 'created'] |
|
85 |
||
86 |
tasks = self.user.searchTasks(assignee=self.user) |
|
7
by Paul Hummer
Moved print_header to be Report.print_header |
87 |
self.print_header("Assigned Bugs") |
5
by Paul Hummer
Created Report class, using it now instead of the functional |
88 |
|
89 |
for t in tasks: |
|
11
by Paul Hummer
Moved my_bugs to Report.bugs |
90 |
if self.bugs.has_key(t.bug.id): |
5
by Paul Hummer
Created Report class, using it now instead of the functional |
91 |
continue
|
92 |
updates = [] |
|
93 |
for s in statuses: |
|
94 |
attr = 'date_' + s |
|
95 |
date = getattr(t, attr) |
|
96 |
if not date: |
|
97 |
continue
|
|
6
by Paul Hummer
Moved in_window to Report |
98 |
if self.in_window(date): |
5
by Paul Hummer
Created Report class, using it now instead of the functional |
99 |
updates.append( |
100 |
"\t" + s + ": " + re.sub("\s.*$", "", str(date))) |
|
101 |
||
102 |
if updates: |
|
10
by Paul Hummer
Fixed references te print_bugid to be self.print_bugid |
103 |
self.print_bugid(t) |
5
by Paul Hummer
Created Report class, using it now instead of the functional |
104 |
for u in updates: |
105 |
print u |
|
106 |
print
|
|
107 |
print
|
|
108 |
||
109 |
def print_comments(self): |
|
110 |
tasks = self.user.searchTasks(bug_commenter=self.user) |
|
7
by Paul Hummer
Moved print_header to be Report.print_header |
111 |
self.print_header("Commented Bugs") |
5
by Paul Hummer
Created Report class, using it now instead of the functional |
112 |
for t in tasks: |
11
by Paul Hummer
Moved my_bugs to Report.bugs |
113 |
if self.bugs.has_key(t.bug.id): |
5
by Paul Hummer
Created Report class, using it now instead of the functional |
114 |
continue
|
115 |
for m in t.bug.messages: |
|
116 |
if m.owner_link != self.user.self_link: |
|
117 |
continue
|
|
6
by Paul Hummer
Moved in_window to Report |
118 |
if self.in_window(m.date_created): |
10
by Paul Hummer
Fixed references te print_bugid to be self.print_bugid |
119 |
self.print_bugid(t) |
5
by Paul Hummer
Created Report class, using it now instead of the functional |
120 |
print
|
121 |
break
|
|
122 |
print
|
|
123 |
||
124 |
def print_reported(self): |
|
125 |
tasks = self.user.searchTasks(bug_reporter=self.user) |
|
7
by Paul Hummer
Moved print_header to be Report.print_header |
126 |
self.print_header("Reported Bugs") |
5
by Paul Hummer
Created Report class, using it now instead of the functional |
127 |
for t in tasks: |
11
by Paul Hummer
Moved my_bugs to Report.bugs |
128 |
if self.bugs.has_key(t.bug.id): |
5
by Paul Hummer
Created Report class, using it now instead of the functional |
129 |
continue
|
6
by Paul Hummer
Moved in_window to Report |
130 |
if self.in_window(t.bug.date_created): |
10
by Paul Hummer
Fixed references te print_bugid to be self.print_bugid |
131 |
self.print_bugid(t) |
5
by Paul Hummer
Created Report class, using it now instead of the functional |
132 |
print
|
133 |
||
134 |
def render(self): |
|
135 |
self.print_assignments() |
|
136 |
self.print_comments() |
|
137 |
self.print_reported() |
|
138 |
||
1
by Alex Chiang
Initial commit. |
139 |
|
21
by Paul Hummer
No more args to main |
140 |
def main(): |
4
by Paul Hummer
Tabs make baby Jesus cry in his manger |
141 |
|
12
by Paul Hummer
Created OptionParser for, uh, you know, parsing options. |
142 |
parser = OptionParser() |
143 |
parser.add_option('-u', '--user', dest='user', |
|
144 |
default=os.getenv('USER'), |
|
145 |
help='Specify the Launchpad user id. ' |
|
146 |
'Defaults to authenticated token owner.') |
|
147 |
parser.add_option('-w', '--window', dest='window', type='int', |
|
148 |
default=8, |
|
149 |
help='Number af days of past activity to look for. ' |
|
150 |
'Defaults to 8 days') |
|
151 |
||
152 |
options, arguments = parser.parse_args() |
|
5
by Paul Hummer
Created Report class, using it now instead of the functional |
153 |
|
18
by Paul Hummer
I messed up... when I moved window, I didn't specify it in the instantiator |
154 |
report = Report(options.user, options.window) |
5
by Paul Hummer
Created Report class, using it now instead of the functional |
155 |
report.render() |
3
by Alex Chiang
* Add command line parsing for: |
156 |
|
157 |
if __name__ == "__main__": |
|
21
by Paul Hummer
No more args to main |
158 |
main() |