~bughelper-dev/python-launchpad-bugs/main

53.1.35 by Markus Korn
Sorry for the big diff, will explain the changes later; this basically fixes Bug 175946 and adds a new way of filtering buglists - unfortunatly my system broke and I lost the revision history, only had my daily plain-text diff
1
import urllib
2
import urlparse
103.1.38 by Markus Korn
* launchpadbugs/basebuglistfilter.py: datereported.__init__() raise
3
from datetime import datetime
103.1.14 by Markus Korn
Added function to filter buglists based on Bug.date_reported (LP: #185357)
4
from operator import eq as OP_EQ, gt as OP_GT, lt as OP_LT
53.1.74 by Markus Korn
launchpadbugs/lpconstants.py: added BUGLIST-class; launchpadbugs/basebuglistfilter.py: added 'component'-filteroption
5
6
from lpconstants import BUGLIST
103.1.14 by Markus Korn
Added function to filter buglists based on Bug.date_reported (LP: #185357)
7
from bugbase import Bug as LPBug
103.1.38 by Markus Korn
* launchpadbugs/basebuglistfilter.py: datereported.__init__() raise
8
from exceptions import PythonLaunchpadBugsValueError
53.1.35 by Markus Korn
Sorry for the big diff, will explain the changes later; this basically fixes Bug 175946 and adds a new way of filtering buglists - unfortunatly my system broke and I lost the revision history, only had my daily plain-text diff
9
    
10
def minbug(bug, minbug):
11
    return int(bug) >= minbug and bug
12
        
13
def filterbug(bug, buglist):
14
    return int(bug) not in buglist and bug
15
    
74 by Brian Murray
added bzr branch filtering to basebuglistfilter.py
16
def hasbranch(bug, cls=None):
17
    try:
18
        br = bug.branches
19
        if not br:
20
            return False
21
        else:
22
            return True and bug
23
    except AttributeError:
24
        if cls:
25
            try:
26
                bug = cls(bug)
27
                br = bug.branches
28
                if not br:
29
                    return False
30
                else:
31
                    return True and bug
32
            except:
33
                raise RuntimeError, "Something went wrong"
34
53.1.35 by Markus Korn
Sorry for the big diff, will explain the changes later; this basically fixes Bug 175946 and adds a new way of filtering buglists - unfortunatly my system broke and I lost the revision history, only had my daily plain-text diff
35
def reporter(bug, reporter, cls=None):
36
    try:
37
        return bug.reporter == reporter and bug
38
    except AttributeError:
39
        if cls:
40
            try:
41
                bug = cls(bug)
42
                return x.reporter == reporter and bug
43
            except:
44
                raise RuntimeError, "Something went wrong"
45
        else:
46
            raise NotImplementedError, "Please define your own reporter-filterfunction"
47
            
53.1.41 by Markus Korn
launchpadbugs/basebuglistfilter.py:
48
            
49
#this is a HACK-filter to get current task-row
50
def get_current_task(bug, listurl, cls=None):
51
    try:
52
        t = bug.infotable
53
    except AttributeError:
54
        if cls:
168 by Markus Korn
* debian/changelog: updated changelog for most recent changes
55
            bug = cls(bug)
56
            t = bug.infotable
53.1.41 by Markus Korn
launchpadbugs/basebuglistfilter.py:
57
        else:
58
            raise NotImplementedError, "Please define your own get_current_task-filterfunction"
59
    
60
    if t._current is None:
53.1.75 by Markus Korn
launchpadbugs.basebuglistfilter.get_current_task(): try to always find a 'current' task - needed to avoid errors in bugnumbers
61
        idx_fallback = None
53.1.41 by Markus Korn
launchpadbugs/basebuglistfilter.py:
62
        y = t.current_from_listurl(listurl)
63
        for idx, a in enumerate(t):
64
            if a.is_current(y):
65
                t._current = idx
66
                break
53.1.75 by Markus Korn
launchpadbugs.basebuglistfilter.get_current_task(): try to always find a 'current' task - needed to avoid errors in bugnumbers
67
            if a.target == y[2]:
68
                idx_fallback = idx
69
        else:
70
            # there is no way to find the currrent task if we are searching
71
            # an package-unspecific list like https://bugs.edge.launchpad.net/ubuntu/+bugs
72
            # as a fallback take the last one with the right target, in this case,
73
            # take the last one where .target == 'ubuntu'
74
            
75
            # if idx_fallback is still None, this is worse an error message, because in this case
76
            # parsing went wrong
77
            assert not idx_fallback is None, "parsing of '%s' went wrong" %bug.url
78
            t._current = idx_fallback
53.1.41 by Markus Korn
launchpadbugs/basebuglistfilter.py:
79
    return bug
80
            
53.1.35 by Markus Korn
Sorry for the big diff, will explain the changes later; this basically fixes Bug 175946 and adds a new way of filtering buglists - unfortunatly my system broke and I lost the revision history, only had my daily plain-text diff
81
def lastcomment(bug, cls=None, **comment_def):
82
    """
53.1.36 by Markus Korn
launchpadbugs/basebuglistfilter.py:
83
    comment_def["user"], type: str or lphelper.user or "reporter"
53.1.35 by Markus Korn
Sorry for the big diff, will explain the changes later; this basically fixes Bug 175946 and adds a new way of filtering buglists - unfortunatly my system broke and I lost the revision history, only had my daily plain-text diff
84
    comment_def["before"], type: datetime
85
    comment_def["after"], type: datetime
86
    comment_def["operation"], in ["|","&"]
87
    """
88
    if not comment_def:
89
        raise TypeError, "lastcomment-function needs at least one definitional argument"
53.1.60 by Markus Korn
launchpadbugs/basebuglistfilter.py: lastcomment(): added an additional option 'at' to search on a given date
90
    if not set(comment_def.keys()) <= set(["user","before","after","operation", "at"]):
53.1.35 by Markus Korn
Sorry for the big diff, will explain the changes later; this basically fixes Bug 175946 and adds a new way of filtering buglists - unfortunatly my system broke and I lost the revision history, only had my daily plain-text diff
91
        raise TypeError, "unknown definitional argument for lastcomment"
53.1.60 by Markus Korn
launchpadbugs/basebuglistfilter.py: lastcomment(): added an additional option 'at' to search on a given date
92
    if "at" in comment_def.keys():
93
        if "before" in comment_def.keys() or "after" in comment_def.keys():
94
            raise TypeError, "'at' and 'after' or 'before' is not allowed"
53.1.35 by Markus Korn
Sorry for the big diff, will explain the changes later; this basically fixes Bug 175946 and adds a new way of filtering buglists - unfortunatly my system broke and I lost the revision history, only had my daily plain-text diff
95
    conditions = []
96
    operation = comment_def.get("operation","|")
97
    if operation not in ("|","&"):
98
        raise ValueError, "unknown operation"
99
    try:
53.1.45 by Markus Korn
launchpadbugs/basebuglistfilter.py: fixed IndexError in lastcomment(); launchpadbugs/lptime.py: added LPTime.convert_lastcomment_time()
100
        c = bug.comments
101
        if not c:
102
            return False
103
        lastcomment = c[-1]
53.1.35 by Markus Korn
Sorry for the big diff, will explain the changes later; this basically fixes Bug 175946 and adds a new way of filtering buglists - unfortunatly my system broke and I lost the revision history, only had my daily plain-text diff
104
    except AttributeError:
105
        if cls:
106
            try:
107
                bug = cls(bug)
108
                c = bug.comments
109
                if not c:
110
                    return False
111
                lastcomment = c[-1]
112
            except:
113
                raise RuntimeError, "Something went wrong"
114
        else:
115
            raise NotImplementedError, "Please define your own reporter-filterfunction"
116
    if "user" in comment_def:
53.1.36 by Markus Korn
launchpadbugs/basebuglistfilter.py:
117
        if comment_def["user"] == "reporter":
118
            comment_def["user"] = bug.reporter
53.1.35 by Markus Korn
Sorry for the big diff, will explain the changes later; this basically fixes Bug 175946 and adds a new way of filtering buglists - unfortunatly my system broke and I lost the revision history, only had my daily plain-text diff
119
        conditions.append(lastcomment.user == comment_def["user"])
53.1.60 by Markus Korn
launchpadbugs/basebuglistfilter.py: lastcomment(): added an additional option 'at' to search on a given date
120
    if "at" in comment_def:
121
        conditions.append(lastcomment.date.timetuple()[:3] == comment_def["at"].timetuple()[:3])
53.1.35 by Markus Korn
Sorry for the big diff, will explain the changes later; this basically fixes Bug 175946 and adds a new way of filtering buglists - unfortunatly my system broke and I lost the revision history, only had my daily plain-text diff
122
    if "before" in comment_def:
123
        conditions.append(lastcomment.date <= comment_def["before"])
124
    if "after" in comment_def:
125
        conditions.append(lastcomment.date >= comment_def["after"])
53.1.36 by Markus Korn
launchpadbugs/basebuglistfilter.py:
126
    #print conditions
53.1.35 by Markus Korn
Sorry for the big diff, will explain the changes later; this basically fixes Bug 175946 and adds a new way of filtering buglists - unfortunatly my system broke and I lost the revision history, only had my daily plain-text diff
127
    if operation == "&":
128
        return not (False in conditions) and bug
129
    else:
130
        return True in conditions and bug
70.1.1 by Markus Korn
first approach to add datereported-filter
131
        
151 by Brian Murray
launchpadbugs/basebuglistfilter.py: merging thekorn's patch for bug 254071
132
class date_filter(object):
133
    DATEREPORTED = "date"
134
    DATEUPDATED = "date_updated"
135
136
    def __init__(self, date_def, date_kind, cls=None):
103.1.14 by Markus Korn
Added function to filter buglists based on Bug.date_reported (LP: #185357)
137
        """
138
        date_def = ("=" or ">" or "<" , date (type of datetime))
139
        """
140
        abort = False
141
        try:
142
            if not issubclass(cls, LPBug):
143
                abort = True
144
        except TypeError:
145
            if not issubclass(cls.cls, LPBug):
146
                abort = True
147
        if abort:
148
            raise TypeError
149
            
70.1.2 by Markus Korn
datereported is now a class: this allows us to store the number of checked elements
150
        self.cls = cls
103.1.14 by Markus Korn
Added function to filter buglists based on Bug.date_reported (LP: #185357)
151
        op, value = date_def
152
        self.op = {"=": OP_EQ, "<": OP_LT, ">": OP_GT}[op]
70.1.8 by Markus Korn
launchpadbugs/basebuglistfilter.py: removed obsolete code and debugging statements; check datereported-arguments in the constructor
153
        self.value = value.date()
103.1.39 by Markus Korn
* launchpadbugs/basebuglistfilter.py: Another check in datereported if the
154
        today = datetime.today().date()
155
        if self.value > today:
151 by Brian Murray
launchpadbugs/basebuglistfilter.py: merging thekorn's patch for bug 254071
156
            raise PythonLaunchpadBugsValueError({"date_filter(%s)" %date_kind: "The given date (%s) is in the future" %self.value})
103.1.39 by Markus Korn
* launchpadbugs/basebuglistfilter.py: Another check in datereported if the
157
        elif self.value == today and op == ">":
151 by Brian Murray
launchpadbugs/basebuglistfilter.py: merging thekorn's patch for bug 254071
158
            raise PythonLaunchpadBugsValueError({"date_filter(%s)" %date_kind: "You can not search for bugs reported in the future (op='%s', date='%s')" %(op, self.value)})
103.1.14 by Markus Korn
Added function to filter buglists based on Bug.date_reported (LP: #185357)
159
        self.date_state = False
151 by Brian Murray
launchpadbugs/basebuglistfilter.py: merging thekorn's patch for bug 254071
160
        self.kind = date_kind
70.1.2 by Markus Korn
datereported is now a class: this allows us to store the number of checked elements
161
        
162
    def __call__(self, bug):
103.1.14 by Markus Korn
Added function to filter buglists based on Bug.date_reported (LP: #185357)
163
        if not isinstance(bug, LPBug):
70.1.2 by Markus Korn
datereported is now a class: this allows us to store the number of checked elements
164
            bug = self.cls(bug)
151 by Brian Murray
launchpadbugs/basebuglistfilter.py: merging thekorn's patch for bug 254071
165
        try:
166
            date = getattr(bug, self.kind)
167
        except AttributeError:
168
            raise ValueError, "Unable to filter the list of bugs by '%s'" %self.kind
169
        date = date.date()
170
        result = self.op(date, self.value)
103.1.14 by Markus Korn
Added function to filter buglists based on Bug.date_reported (LP: #185357)
171
        if result:
172
            self.date_state = True
173
        elif self.date_state:
151 by Brian Murray
launchpadbugs/basebuglistfilter.py: merging thekorn's patch for bug 254071
174
            if self.kind == date_filter.DATEREPORTED:
175
                #Task<->Bug issue: double check all tasks
176
                x = map(lambda i: self.op(i.date_created.date(), self.value), bug.infotable)
177
                if True in x:
178
                    self.date_state = True
179
                    return bug
103.1.14 by Markus Korn
Added function to filter buglists based on Bug.date_reported (LP: #185357)
180
            self.date_state = False
70.1.2 by Markus Korn
datereported is now a class: this allows us to store the number of checked elements
181
            raise StopFiltering
103.1.14 by Markus Korn
Added function to filter buglists based on Bug.date_reported (LP: #185357)
182
        return result and bug
70.1.1 by Markus Korn
first approach to add datereported-filter
183
151 by Brian Murray
launchpadbugs/basebuglistfilter.py: merging thekorn's patch for bug 254071
184
        
185
class datereported(date_filter):
186
    """ class to ensure backward compatibility """
187
    def __init__(self, date_def, cls=None):
188
        date_filter.__init__(self, date_def, date_filter.DATEREPORTED, cls)
189
190
class dateupdated(date_filter):
191
    """ class to ensure backward compatibility """
192
    def __init__(self, date_def, cls=None):
193
        date_filter.__init__(self, date_def, date_filter.DATEUPDATED, cls)    
194
53.1.35 by Markus Korn
Sorry for the big diff, will explain the changes later; this basically fixes Bug 175946 and adds a new way of filtering buglists - unfortunatly my system broke and I lost the revision history, only had my daily plain-text diff
195
class StopFiltering(StopIteration):
196
    pass
197
    
198
class LPBugListFilter(object):
199
    
100.1.1 by Markus Korn
Started working on a saner BugList implementation:
200
    def __init__(self, func=None):
201
        self._functions = func or []
53.1.35 by Markus Korn
Sorry for the big diff, will explain the changes later; this basically fixes Bug 175946 and adds a new way of filtering buglists - unfortunatly my system broke and I lost the revision history, only had my daily plain-text diff
202
        self._func_cache = self._functions[:]
203
        
204
    @property
205
    def functions(self):
206
        return self._functions
207
        
208
    def reset(self):
209
        self._functions = self._func_cache
210
211
def parse_options(url_opt):
212
    result = {}
213
    if url_opt:
214
        for i in urllib.unquote_plus(url_opt).split("&"):
215
            k = i.split("=")
216
            try:
217
                result[k[0]].add(k[1])
218
            except:
219
                result[k[0]] = set([k[1]])
220
    return result
221
    
222
223
class URLBugListFilter(LPBugListFilter):
224
    
225
    ADD = 1
226
    OVERWRITE = 2
227
    CONFLICTS_ERROR = 3
53.1.41 by Markus Korn
launchpadbugs/basebuglistfilter.py:
228
    
229
    OPTION_DICT = { "status": "field.status:list",
230
                    "importance": "field.importance:list",
75 by Brian Murray
added URL filters resolving bug 141338
231
                    "tag": "field.tag",
232
                    "assignee": "field.assignee",
53.1.41 by Markus Korn
launchpadbugs/basebuglistfilter.py:
233
                    "reporter": "field.bug_reporter",
95 by Brian Murray
launchpadbugs/basebuglistfilter.py: updated broken bug_supervisor filter
234
# the UI now refers to the bug contact as a bug supervisor and bughelper never used contact from what I saw
235
# could we drop contact from the dictionary? - bdmurray 20080613
236
                    "contact": "field.bug_supervisor",
237
                    "supervisor": "field.bug_supervisor",
75 by Brian Murray
added URL filters resolving bug 141338
238
                    "commenter": "field.bug_commenter",
239
                    "subscriber": "field.subscriber",
240
                    "duplicates": "field.omit_dupes",
241
                    "component": "field.component",
242
                    "cve": "field.has_cve",
243
                    "has-patch": "field.has_patch",
103.1.13 by Markus Korn
Added 'milestone' as URLBugListFilter option (LP: #200457)
244
                    "upstream-status": "field.status_upstream",
103.1.15 by Markus Korn
launchpadbugs/basebuglistfilter.py: added 'orderby' to URLBugListFilter.OPTION_DICT
245
                    "milestone": "field.milestone:list",
116 by Markus Korn
* launchpadbugs/basebuglistfilter.py: added 'batch' to valid options for
246
                    "orderby": "orderby",
247
                    "batch": "batch"}
72 by Brian Murray
added launchpad url filtering for bugs with patches or an upstream status
248
                    
53.1.41 by Markus Korn
launchpadbugs/basebuglistfilter.py:
249
        
250
    def __init__(self, url_opt="", func=[], opt_type=CONFLICTS_ERROR):
251
        
252
        self._conflicts_error = opt_type
53.1.35 by Markus Korn
Sorry for the big diff, will explain the changes later; this basically fixes Bug 175946 and adds a new way of filtering buglists - unfortunatly my system broke and I lost the revision history, only had my daily plain-text diff
253
        self.__url_opt = parse_options(url_opt)
53.1.41 by Markus Korn
launchpadbugs/basebuglistfilter.py:
254
        self.__baseurl = ()
53.1.35 by Markus Korn
Sorry for the big diff, will explain the changes later; this basically fixes Bug 175946 and adds a new way of filtering buglists - unfortunatly my system broke and I lost the revision history, only had my daily plain-text diff
255
        self.__url_cache = self.__url_opt.copy()
256
        LPBugListFilter.__init__(self, func)
257
        
258
    def __call__(self, baseurl):
259
        self.baseurl = baseurl
260
        return self
261
        
262
    def reset(self):
263
        self._functions = self._func_cache
264
        self.__url_opt = self.__url_cache
265
        
266
    def get_baseurl(self):
267
        assert self.__baseurl, "needs baseurl"
268
        x = list(self.__baseurl[:3])
269
        x.append(self.urlopt)
270
        x.append("")
271
        return urlparse.urlunsplit(x)
272
        
273
    def set_baseurl(self, baseurl):
274
        self.__baseurl = urlparse.urlsplit(baseurl)
53.1.41 by Markus Korn
launchpadbugs/basebuglistfilter.py:
275
        self._add_default_urlopt(baseurl)
53.1.35 by Markus Korn
Sorry for the big diff, will explain the changes later; this basically fixes Bug 175946 and adds a new way of filtering buglists - unfortunatly my system broke and I lost the revision history, only had my daily plain-text diff
276
    baseurl = property(get_baseurl, set_baseurl)
277
        
278
    @property
279
    def urlopt(self):
280
        return urllib.urlencode(self.__url_opt,True)
281
        
53.1.41 by Markus Korn
launchpadbugs/basebuglistfilter.py:
282
    def add_option(self, option, value):
283
        if not option in URLBugListFilter.OPTION_DICT:
284
            raise ValueError
53.1.74 by Markus Korn
launchpadbugs/lpconstants.py: added BUGLIST-class; launchpadbugs/basebuglistfilter.py: added 'component'-filteroption
285
        if option == "component":
286
            value = set([BUGLIST.COMPONENT_DICT[i] for i in value])
53.1.41 by Markus Korn
launchpadbugs/basebuglistfilter.py:
287
        self._add_option({URLBugListFilter.OPTION_DICT[option]: value})
288
        
289
        
290
    def _add_default_urlopt(self, url):
53.1.35 by Markus Korn
Sorry for the big diff, will explain the changes later; this basically fixes Bug 175946 and adds a new way of filtering buglists - unfortunatly my system broke and I lost the revision history, only had my daily plain-text diff
291
        u = urlparse.urlsplit(url)
292
        r = parse_options(u[3])
53.1.41 by Markus Korn
launchpadbugs/basebuglistfilter.py:
293
        self._add_option(r)
294
        
295
    def _add_option(self, r):
296
        if self._conflicts_error == URLBugListFilter.ADD:
53.1.35 by Markus Korn
Sorry for the big diff, will explain the changes later; this basically fixes Bug 175946 and adds a new way of filtering buglists - unfortunatly my system broke and I lost the revision history, only had my daily plain-text diff
297
            for i,k in r.iteritems():
298
                try:
299
                    self.__url_opt[i] = self.__url_opt[i].union(k)
300
                except KeyError:
301
                    self.__url_opt[i] = k
53.1.41 by Markus Korn
launchpadbugs/basebuglistfilter.py:
302
        elif self._conflicts_error == URLBugListFilter.OVERWRITE:
53.1.35 by Markus Korn
Sorry for the big diff, will explain the changes later; this basically fixes Bug 175946 and adds a new way of filtering buglists - unfortunatly my system broke and I lost the revision history, only had my daily plain-text diff
303
            self.__url_opt = r
53.1.41 by Markus Korn
launchpadbugs/basebuglistfilter.py:
304
        elif self._conflicts_error == URLBugListFilter.CONFLICTS_ERROR:
53.1.35 by Markus Korn
Sorry for the big diff, will explain the changes later; this basically fixes Bug 175946 and adds a new way of filtering buglists - unfortunatly my system broke and I lost the revision history, only had my daily plain-text diff
305
            for i,k in r.iteritems():
145 by Markus Korn
* launchpadbugs/basebuglistfilter.py fixed method to check for conflicting
306
                if isinstance(k, set) and len(k) == 1:
307
                    k = k.pop()
53.1.35 by Markus Korn
Sorry for the big diff, will explain the changes later; this basically fixes Bug 175946 and adds a new way of filtering buglists - unfortunatly my system broke and I lost the revision history, only had my daily plain-text diff
308
                if i in self.__url_opt:
309
                    if not self.__url_opt[i] == k:
145 by Markus Korn
* launchpadbugs/basebuglistfilter.py fixed method to check for conflicting
310
                        try:
311
                            if k in self.__url_opt[i]:
312
                                pass
313
                        except:
314
                            raise ValueError, "ValueError: conflict filter options (%s)" %i
53.1.35 by Markus Korn
Sorry for the big diff, will explain the changes later; this basically fixes Bug 175946 and adds a new way of filtering buglists - unfortunatly my system broke and I lost the revision history, only had my daily plain-text diff
315
                else:
316
                    self.__url_opt[i] = k
317
        else:
318
            raise ValueError, "unknown type=%s" %type
319
        
320
        
321
322
323
        
324
#some test-cases
325
if __name__ == '__main__':
53.1.36 by Markus Korn
launchpadbugs/basebuglistfilter.py:
326
    import connector as Connector
53.1.35 by Markus Korn
Sorry for the big diff, will explain the changes later; this basically fixes Bug 175946 and adds a new way of filtering buglists - unfortunatly my system broke and I lost the revision history, only had my daily plain-text diff
327
    tBug = Connector.ConnectBug(method="text")
328
    tBuglist = Connector.ConnectBugList(method="text")
329
    from datetime import datetime
330
    
331
    f = LPBugListFilter([lambda x: minbug(x, 175000)])#, lambda x: lastcomment(x, tBug, user="thekorn", before=datetime(2007,10,16))])
332
    bugs = [172802,
333
 137574,
334
 177608,
335
 146377,
336
 175946,
337
 162814,
338
 173005,
339
 177202,
340
 116243,
341
 138837,
342
 174103,
343
 172882,
344
 172637,
345
 172670]
53.1.36 by Markus Korn
launchpadbugs/basebuglistfilter.py:
346
    print f.filter(bugs)
347
     
348
    f = URLBugListFilter(func=(lambda x: lastcomment(x, tBug, user="reporter"),))
349
    bugs = tBuglist(f("https://bugs.edge.launchpad.net/python-launchpad-bugs/+bugs?field.searchtext=&orderby=-importance&search=Search&field.status%3Alist=NEW&field.status%3Alist=INCOMPLETE_WITH_RESPONSE&field.status%3Alist=CONFIRMED&field.status%3Alist=TRIAGED&field.status%3Alist=INPROGRESS&field.status%3Alist=FIXCOMMITTED&field.assignee=&field.bug_reporter=&field.omit_dupes=on&field.has_patch=&field.has_no_package="))
350
    print bugs, len(bugs)
53.1.35 by Markus Korn
Sorry for the big diff, will explain the changes later; this basically fixes Bug 175946 and adds a new way of filtering buglists - unfortunatly my system broke and I lost the revision history, only had my daily plain-text diff
351