~rockstar/laika/more-cleanup

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()