-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathupcoming.py
executable file
·169 lines (157 loc) · 6.52 KB
/
upcoming.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# Used to generate upcoming events
from __future__ import unicode_literals
from datetime import timedelta, date, datetime
from django.utils.timezone import make_aware, get_default_timezone
from django.utils.six.moves import xrange
from events.utils.common import inc_month
class UpcomingEvents(object):
def __init__(self, event, now, finish, num=5):
self.event = event
self.finish = finish # datetime.datetime
self.now = now # datetime.datetime
self.num = num
self.events = []
def get_upcoming_events(self):
"""
Repeats an event and returns 'num' (or fewer)
upcoming events from 'now'.
"""
if self.event.repeats('NEVER'):
has_ended = False
now_gt_start = self.now > self.event.l_start_date
now_gt_end = self.now > self.event.end_date
if now_gt_end or now_gt_start:
has_ended = True
has_not_started = self.event.l_start_date > self.finish
if has_ended or has_not_started:
return self.events
self.events.append((self.event.l_start_date, self.event))
return self.events
if self.event.repeats('WEEKDAY'):
self._weekday()
elif self.event.repeats('MONTHLY'):
self._monthly()
elif self.event.repeats('YEARLY'):
self._yearly()
else:
self._others()
return self.events
def we_should_stop(self, start, start_):
"""
Checks 'start' to see if we should stop collecting upcoming events.
'start' should be a datetime.datetime, 'start_' should be the same
as 'start', but it should be a datetime.date to allow comparison
w/ end_repeat.
"""
if start > self.finish or \
self.event.end_repeat is not None and \
start_ > self.event.end_repeat:
return True
else:
return False
def _yearly(self):
num = self.num
year = self.now.year
if self.event.l_start_date > self.now: # event starts in the future
year = self.event.l_start_date.year
elif self.now.month > self.event.l_start_date.month:
year += 1
# The event occurs this month, so check the day to see if passed
elif self.now.month == self.event.l_start_date.month:
# if the current day is > event day, or if the event was today but
# finished, increment the year.
if self.now.day > self.event.l_start_date.day or \
self.now.day == self.event.l_start_date.day and \
self.now.time() > self.event.l_start_date.time():
year += 1
month = self.event.l_start_date.month
day = self.event.l_start_date.day
while num:
try:
start = make_aware(
datetime(year, month, day), get_default_timezone()
)
# change to date() so we can compare to event.end_repeat
start_ = date(year, month, day)
# value error most likely means that the event's start date doesn't
# appear this calendar month
except ValueError:
year += 1
continue
if self.we_should_stop(start, start_):
return
self.events.append((start, self.event))
year += 1
num -= 1
def _monthly(self):
num = self.num
start = self.event.l_start_date
year = self.now.year
month = self.now.month
day = self.event.l_start_date.day
if self.event.l_start_date > self.now: # event starts in the future
year = self.event.l_start_date.year
month = self.event.l_start_date.month
while num:
try:
start = start.replace(year=year, month=month)
if self.now > start: # if event already happened
month, year = inc_month(month, year)
continue
# change to date() so we can compare to event.end_repeat
start_ = date(year, month, day)
# value error most likely means that the event's start date doesn't
# appear this calendar month
except ValueError:
month, year = inc_month(month, year)
continue
if self.we_should_stop(start, start_):
return
self.events.append((start, self.event))
month, year = inc_month(month, year)
num -= 1
def _weekday(self):
start = self.event.l_start_date
if not self.event.l_start_date > self.now:
start = start.replace(
year=self.now.year, month=self.now.month, day=self.now.day
)
while start.weekday() > 4:
start += timedelta(days=1)
if start < self.now:
start += timedelta(days=1)
while start.weekday() > 4:
start += timedelta(days=1)
for i in xrange(self.num):
# change to date() so we can compare to event.end_repeat
start_ = date(start.year, start.month, start.day)
if self.we_should_stop(start, start_):
return
while start.weekday() > 4:
start += timedelta(days=1)
self.events.append((start, self.event))
start += timedelta(days=1)
def _others(self):
repeat = {'WEEKLY': 7, 'BIWEEKLY': 14, 'DAILY': 1}
if self.event.repeats('DAILY'):
if self.event.l_start_date > self.now:
start = self.event.l_start_date
elif self.event.l_start_date.time() < self.now.time():
# has it already begun? Then no longer upcoming, so ++day
start = self.now
start += timedelta(days=1)
else:
start = self.now
else:
start = self.event.l_start_date
end = self.event.l_end_date
while start < self.now or end <= self.now:
start += timedelta(days=repeat[self.event.repeat])
end += timedelta(days=repeat[self.event.repeat])
for i in xrange(self.num):
# change to date() so we can compare to event.end_repeat
start_ = date(start.year, start.month, start.day)
if self.we_should_stop(start, start_):
return
self.events.append((start, self.event))
start += timedelta(days=repeat[self.event.repeat])