IncompleteTasksFilter   A
last analyzed

Complexity

Total Complexity 2

Size/Duplication

Total Lines 11
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
dl 0
loc 11
rs 10
c 1
b 0
f 0
wmc 2

2 Methods

Rating   Name   Duplication   Size   Complexity  
A isMatch() 0 2 1
A __init__() 0 2 1
1
import re
2
from datetime import date, datetime, timedelta
3
4
5
class BaseFilter(object):
6
    """
7
    The abstract base class for different kind of task-list filters.
8
9
    """
10
11
    def __init__(self, text):
12
        """
13
        Initialize a new BaseFilter objects.
14
15
        The required text argument (str) becomes the "text" instance attribute
16
        of the object.
17
18
        """
19
        self.text = text
20
21
    def __hash__(self):
22
        return hash(self.text)
23
24
    def __str__(self):
25
        return "Filter:{}".format(self.__class__.__name__)
26
27
    __repr__ = __str__
28
29
    def isMatch(self, task):
30
        """
31
        Determine whether the supplied task (arg 'task') satisfies the filter.
32
33
        In this base class, the test always returns True.
34
35
        """
36
        return True
37
38
    def __eq__(self, other):
39
        """
40
        Evaluates objects as equal if their type and self.text attr are the same.
41
42
        """
43
        if not other:
44
            return False
45
        if type(other) == type(self):
46
            return self.text == other.text
47
        return False
48
49
50
class AllTasksFilter(BaseFilter):
51
    """
52
    Task list filter that returns all tasks.
53
54
    """
55
56
    def __init__(self):
57
        BaseFilter.__init__(self, 'All')
58
59
60
class IncompleteTasksFilter(BaseFilter):
61
    """
62
    Task list filter that removes any completed tasks.
63
64
    """
65
66
    def __init__(self):
67
        BaseFilter.__init__(self, 'Incomplete')
68
69
    def isMatch(self, task):
70
        return not task.is_complete
71
72
73
class UncategorizedTasksFilter(BaseFilter):
74
    """
75
    Task list filter permitting incomplete tasks without project or context.
76
77
    """
78
79
    def __init__(self):
80
        BaseFilter.__init__(self, 'Uncategorized')
81
82
    def isMatch(self, task):
83
        return (not task.contexts) and (not task.projects)
84
85
86
class CompleteTasksFilter(BaseFilter):
87
    """
88
    Task list filter that removes any uncompleted tasks from the list.
89
90
    """
91
92
    def __init__(self):
93
        BaseFilter.__init__(self, 'Complete')
94
95
    def isMatch(self, task):
96
        return task.is_complete
97
98
99
class ContextFilter(BaseFilter):
100
    """
101
    Task list filter allowing only incomplete tasks with the selected context.
102
103
    """
104
105
    def __init__(self, context):
106
        BaseFilter.__init__(self, context)
107
108
    def isMatch(self, task):
109
        return self.text in task.contexts
110
111
112
class ProjectFilter(BaseFilter):
113
    """
114
    Task list filter allowing only incomplete tasks with the selected project.
115
116
    """
117
118
    def __init__(self, project):
119
        BaseFilter.__init__(self, project)
120
121
    def isMatch(self, task):
122
        return self.text in task.projects
123
124
125
class DueFilter(BaseFilter):
126
    """
127
    Due list filter for ranges
128
129
    """
130
131
    def __init__(self, dueRange):
132
        BaseFilter.__init__(self, dueRange)
133
134
    def isMatch(self, task):
135
        return self.text in task.dueRanges
136
137
138
class DueTodayFilter(BaseFilter):
139
    """
140
    Task list filter allowing only incomplete tasks that are due today.
141
142
    """
143
144
    def __init__(self):
145
        BaseFilter.__init__(self, "Today")
146
147
    def isMatch(self, task):
148
        if (not task.due):
149
            return False
150
        else:
151
            self.due_date = task.due
152
            today = datetime.combine(date.today(), datetime.min.time())
153
            return self.due_date == today
154
155
156
class DueTomorrowFilter(BaseFilter):
157
    """
158
    Task list filter allowing only incomplete tasks that are due tomorrow.
159
160
    """
161
162
    def __init__(self):
163
        BaseFilter.__init__(self, "Tomorrow")
164
165
    def isMatch(self, task):
166
        if not task.due:
167
            return False
168
        else:
169
            due_date = task.due
170
            today = datetime.combine(date.today(), datetime.min.time())
171
            return today < due_date <= today + timedelta(days=1)
172
173
174
class DueThisWeekFilter(BaseFilter):
175
    """
176
    Task list filter allowing only incomplete tasks that are due this week.
177
178
    """
179
180
    def __init__(self):
181
        BaseFilter.__init__(self, "This week")
182
183
    def isMatch(self, task):
184
        if not task.due:
185
            return False
186
        else:
187
            due_date = task.due
188
            today = datetime.combine(date.today(), datetime.min.time())
189
            return today <= due_date <= today + timedelta((6 - today.weekday()) % 7)
190
191
192
class DueThisMonthFilter(BaseFilter):
193
    """
194
    Task list filter allowing only incomplete tasks that are due this month.
195
196
    """
197
198
    def __init__(self):
199
        BaseFilter.__init__(self, "This month")
200
201
    def isMatch(self, task):
202
        if not task.due:
203
            return False
204
        else:
205
            due_date = task.due
206
            today = datetime.combine(date.today(), datetime.min.time())
207
            if today.month == 12:
208
                last_day_of_month = today.replace(day=31)
209
            else:
210
                last_day_of_month = today.replace(month=today.month + 1, day=1) - timedelta(days=1)
211
            return today <= due_date <= last_day_of_month
212
213
214
class DueOverdueFilter(BaseFilter):
215
    """
216
    Task list filter allowing only incomplete tasks that are overdue.
217
218
    """
219
220
    def __init__(self):
221
        BaseFilter.__init__(self, "Overdue")
222
223
    def isMatch(self, task):
224
        if not task.due:
225
            return False
226
        else:
227
            due_date = task.due
228
            today = datetime.combine(date.today(), datetime.min.time())
229
            return due_date < today
230
231
232
class HasProjectsFilter(BaseFilter):
233
    """
234
    Task list filter allowing only incomplete tasks with the selected project.
235
236
    """
237
238
    def __init__(self):
239
        BaseFilter.__init__(self, 'Projects')
240
241
    def isMatch(self, task):
242
        return task.projects
243
244
245
class HasContextsFilter(BaseFilter):
246
    """
247
    Task list filter allowing only tasks tagged with some project.
248
249
    """
250
251
    def __init__(self):
252
        BaseFilter.__init__(self, 'Contexts')
253
254
    def isMatch(self, task):
255
        return task.contexts
256
257
258
class HasPriorityFilter(BaseFilter):
259
    """
260
    Task list filter allowing only tasks with a priority set
261
262
    """
263
264
    def __init__(self):
265
        BaseFilter.__init__(self, 'Priorities')
266
267
    def isMatch(self, task):
268
        return task.priority
269
270
271
class HasDueDateFilter(BaseFilter):
272
    """
273
    Task list filter allowing only complete tasks with due date in due ranges.
274
275
    """
276
277
    def __init__(self):
278
        BaseFilter.__init__(self, 'DueRange')
279
280
    def isMatch(self, task):
281
        return task.due
282
283
284
class HasDueRangesFilter(BaseFilter):
285
    """
286
    Task list filter allowing only complete tasks with due date in due ranges.
287
288
    """
289
290
    def __init__(self):
291
        BaseFilter.__init__(self, 'DueRange')
292
293
    def isMatch(self, task):
294
        return task.dueRanges
295
296
297
class SimpleTextFilter(BaseFilter):
298
    r"""
299
    Task list filter allowing only tasks whose string matches a filter string.
300
301
    This filter allows for basic and/or/not conditions in the filter string.
302
    For the syntax see SimpleTextFilter.isMatch.
303
304
    User documentation:
305
306
    This filter can handle basic and/or/not conditions. The syntax is as
307
    follows:
308
309
    :AND   :   ',' or whitespace (' ')
310
    :OR    :   '|'
311
    :NOT   :   prefixed '~' or '!'
312
313
    These operators follow the following order of precedence: OR, AND, NOT.
314
    So, for example:
315
316
    :'work job1 | @home':                Either  (matches 'work'
317
                                                 AND 'job1')
318
                                        OR      (matches '@home')
319
320
    :'norweigan blue ~dead | !parrot':  Either  (matches 'norweigan'
321
                                                 AND 'blue'
322
                                                 AND does NOT match 'dead')
323
                                        OR      (does NOT match 'parrot')
324
325
    Terms match the beginning of words, so:
326
327
    - 'cleese' Does NOT match 'johncleese'
328
329
    Since the python re module is used, most of the escaped regex
330
    characters will also work when attached to one of the (comma- or space-
331
    delimited) strings. E.g.:
332
333
    - john\b will match 'john' but not 'johncleese'
334
    - 2014-\d\d-07 will match '2014-03-07' but not '2014-ja-07'
335
336
    - '(B)' will match '(B) nail its feet to the perch'.
337
    """
338
339
    def __init__(self, text):
340
        BaseFilter.__init__(self, text)
341
        self.re = SimpleTextFilter.compile(text)
342
343
    # Terms are split by "," " " or "|".  Retain only a "|" for the re.
344
    _splitter = re.compile(r'\s*(\|)\s*|[,\s]+()', re.U)
345
    # Escape anything that's not alphanumeric or a backslash
346
    _escaper = re.compile(r'(\\\Z|[^\w\\])', re.U)
347
    # Characters that negate a term
348
    _negates = ('!', '~')
349
350
    @staticmethod
351
    def _term2re(term):
352
        # Don't translate separators
353
        if term is None or term == '|' or term == '':
354
            return term or ''
355
356
        # Check for negated  term
357
        negate = term.startswith(SimpleTextFilter._negates)
358
        if negate:
359
            term = term[1:]
360
361
        # If the term is a word (starts with alpha), then only match beginnings of words
362
        beginning = r'\b' if re.match(r'\b', term) else r'\B'
363
364
        # Ignore special meaning of characters like "*", "+", "(", "[", etc.
365
        term = SimpleTextFilter._escaper.sub(r'\\\1', term)
366
367
        # Return a pattern that will match the beginning of a word or not,
368
        # anywhere in the string, without consuming any of the string.
369
        return r'^(?' + ('!' if negate else '=') + r'.*' + beginning + term + r')'
370
371
    @staticmethod
372
    def compile(searchString):
373
        r"""
374
        Return the user's searchString compiled to a regular expression.
375
376
        Example terms: @call +work (A) carrots
377
        Term may be prefixed with ! or ~ for negation.
378
        Terms may be combined with "," or " " (AND) or with "|" (OR).
379
        Terms only match the beginning of a word in the task.
380
        Terms are case-insensitive.
381
        Expressions may NOT be nested with parentheses.
382
        Only \-character special regular expression sets are allowed, everything else is escaped.
383
        """
384
        if not searchString:
385
            return None
386
387
        terms = SimpleTextFilter._splitter.split(searchString)
388
        terms = [SimpleTextFilter._term2re(term) for term in terms]
389
390
        return re.compile("".join(terms), re.I | re.U)
391
392
    def isMatch(self, task):
393
        """ Return a boolean based on whether the supplied task satisfies self.text. """
394
        return self.re.match(task.text) if self.re else True
395
396
    def __str__(self):
397
        return "SimpleTextFilter({})".format(self.text)
398
399
400
class FutureFilter(BaseFilter):
401
    def __init__(self):
402
        BaseFilter.__init__(self, 'Future')
403
404
    def isMatch(self, task):
405
        return not task.is_future
406
407
408
class PriorityFilter(BaseFilter):
409
    """
410
    Task list filter allowing only tasks with a certain priority
411
412
    """
413
414
    def __init__(self, priority):
415
        BaseFilter.__init__(self, priority)
416
417
    def isMatch(self, task):
418
        return self.text in task.priority
419
420
421
class VisibleFilter(BaseFilter):
422
    """
423
    Task list filter allowing only not hidden tasks
424
425
    """
426
427
    def __init__(self):
428
        BaseFilter.__init__(self, 'Visible')
429
430
    def isMatch(self, task):
431
        return not task.hidden
432