1 from datetime import datetime, timedelta, time
5 def maybe_resize(image_path):
6 if not os.path.exists(image_path):
8 image = Image.open(image_path)
9 if max(image.size) > 1000:
10 # no sense storing images that large
11 factor = 1000. / max(image.size)
13 (int(image.size[0]*factor), int(image.size[1]*factor)),
15 image.save(image_path)
19 from models import Diffusion, Schedule, Nonstop
22 # get program of today minus a few hours, as radio days are not from
23 # midnight to midnigth but from 5am to 5am
24 program = day_program(now - timedelta(hours=Schedule.DAY_HOUR_START),
25 prefetch_sounds=False, prefetch_categories=False, include_nonstop=False)
26 program = [x for x in program if not x.datetime > now]
32 if program and program[-1].datetime + timedelta(minutes=program[-1].get_duration()) > now:
33 current_slot = program[-1]
34 if isinstance(current_slot, Schedule):
35 emission = current_slot.emission
36 elif isinstance(current_slot, Diffusion):
37 episode = current_slot.episode
38 emission = episode.emission
40 for nonstop in Nonstop.objects.all():
41 if (nonstop.start < nonstop.end and (
42 now.time() >= nonstop.start and now.time() < nonstop.end)) or \
43 (nonstop.start > nonstop.end and (
44 now.time() >= nonstop.start or now.time() < nonstop.end)):
45 current_slot = nonstop
50 return {'emission': emission,
53 'current_slot': current_slot}
56 def period_program(date_start, date_end, prefetch_sounds=True,
57 prefetch_categories=True, include_nonstop=True):
58 from models import Diffusion, Schedule, Nonstop, WeekdayMixin, SoundFile, Absence
60 diffusions = Diffusion.objects.select_related().filter(
61 datetime__range=(date_start, date_end)).order_by('datetime')
62 if prefetch_categories:
63 diffusions = diffusions.prefetch_related('episode__emission__categories')
64 diffusions = [x for x in diffusions if x.datetime >= date_start and
65 x.datetime < date_end]
69 for soundfile in SoundFile.objects.select_related().filter(podcastable=True,
70 fragment=False, episode__in=[x.episode for x in diffusions]):
71 soundfiles[soundfile.episode_id] = soundfile
73 for diffusion in diffusions:
74 diffusion.episode.main_sound = soundfiles.get(diffusion.episode.id)
76 # the secondary sortkey puts schedules that happens everyweek after
77 # specific ones, this will be useful later on, when multiple schedules
78 # happen at the same time and we have to remove the least specific.
79 period_schedules = Schedule.objects.select_related().order_by('datetime', 'weeks')
80 if prefetch_categories:
81 period_schedules = period_schedules.prefetch_related('emission__categories')
84 current_date = date_start
85 while current_date < date_end:
86 week_day = current_date.weekday()
87 week_no = ((current_date.day-1) // 7)
88 day_schedules = [x for x in period_schedules if x.get_weekday() == week_day and x.match_week(week_no)]
89 for schedule in day_schedules:
90 schedule.datetime = datetime(
91 current_date.year, current_date.month, current_date.day,
92 schedule.datetime.hour, schedule.datetime.minute) + \
93 timedelta(days=schedule.datetime.weekday()-current_date.weekday())
94 if week_day == 6 and schedule.datetime.weekday() == 0:
95 # on Sundays we can have Sunday->Monday night programming, and
96 # we need to get them on the right date.
97 schedule.datetime += timedelta(days=7)
98 program.extend(day_schedules)
99 current_date += timedelta(days=1)
102 for absence in Absence.objects.filter(datetime__range=(date_start, date_end)):
103 absences[absence.datetime] = True
105 for i, schedule in enumerate(program):
109 # look for a diffusion matching this schedule
110 d = [x for x in diffusions if x.datetime.timetuple()[:5] == schedule.datetime.timetuple()[:5]]
112 diffusions.remove(d[0])
114 for j, other_schedule in enumerate(program[i+1:]):
115 # remove other emissions scheduled at the same time
116 if other_schedule.datetime.timetuple()[:5] == schedule.datetime.timetuple()[:5]:
117 program[i+1+j] = None
121 if schedule.datetime in absences and isinstance(program[i], Schedule):
125 # here we are with remaining diffusions, those that were not overriding a
127 for diffusion in diffusions:
128 program = [x for x in program if x is not None]
130 just_before_program = [x for x in program if x.datetime < diffusion.datetime][-1]
131 new_diff_index = program.index(just_before_program)+1
135 program.insert(new_diff_index, diffusion)
137 # cut (or even remove) programs that started earlier but continued over
138 # this program start time
141 previous = program[i-1]
142 previous_endtime = previous.datetime + timedelta(minutes=previous.get_duration())
143 if previous_endtime > diffusion.datetime:
144 previous.duration = (diffusion.datetime - previous.datetime).seconds / 60
145 if previous.duration <= 0:
149 # push back (or remove) programs that started before this program ends
150 # (this may be unnecessary as next step does that again for all
152 diffusion_endtime = diffusion.datetime + timedelta(minutes=diffusion.get_duration())
154 while i < len(program)-1:
155 next_prog = program[i+1]
156 if next_prog.datetime < diffusion_endtime:
157 diff = diffusion_endtime - next_prog.datetime
158 if (diff.seconds/60) >= next_prog.get_duration():
161 next_prog.datetime = diffusion_endtime
162 next_prog.duration = next_prog.get_duration() - (diff.seconds/60)
165 # remove overlapping programs
166 program = [x for x in program if x is not None]
167 for i, slot in enumerate(program):
171 slot_end = slot.datetime + timedelta(minutes=slot.get_duration())
174 while j < len(program)-1:
176 if slot_end > program[j].datetime:
180 program = [x for x in program if x is not None]
182 if not include_nonstop:
185 # last step is adding nonstop zones between slots
186 nonstops = list(Nonstop.objects.all().order_by('start'))
187 nonstops = [x for x in nonstops if x.start != x.end]
188 dawn = time(Schedule.DAY_HOUR_START, 0)
189 first_of_the_day = [x for x in nonstops if x.start <= dawn][-1]
190 nonstops = nonstops[nonstops.index(first_of_the_day):] + nonstops[:nonstops.index(first_of_the_day)]
192 class NonstopSlot(WeekdayMixin):
193 def __init__(self, nonstop, dt):
195 self.title = nonstop.title
196 self.slug = nonstop.slug
197 self.label = self.title
198 self.nonstop = nonstop
201 return '<Nonstop Slot %r>' % self.label
203 def get_duration(self):
208 def get_serie(cls, start, end):
212 last_added_end = None
213 for nonstop in nonstops:
214 nonstop_day_start = start.replace(hour=nonstop.start.hour, minute=nonstop.start.minute)
215 nonstop_day_end = start.replace(hour=nonstop.end.hour, minute=nonstop.end.minute)
216 nonstop_day_start += timedelta(days=delta_day)
217 nonstop_day_end += timedelta(days=delta_day)
218 if nonstop.start > nonstop.end:
219 nonstop_day_end += timedelta(days=1)
221 if nonstop_day_start < end and nonstop_day_end > start:
225 dt = nonstop_day_start
226 cells.append(cls(nonstop, dt))
227 last_added_end = nonstop_day_end
228 if nonstop.start > nonstop.end:
229 # we just added a midnight crossing slot, future slots
230 # should be one day later.
232 if nonstop.start < dawn and nonstop.end > dawn and start.time() < dawn:
233 dt = dt.replace(hour=dawn.hour, minute=dawn.minute)
234 cells.append(cls(nonstop, dt))
236 cells.sort(key=lambda x: x.datetime)
237 if last_added_end and last_added_end < end:
238 # we used all nonstop slots and we still did not reach the end;
239 # let's go for one more turn.
240 cells.extend(cls.get_serie(last_added_end, end))
244 first_day_start = datetime(*date_start.replace(hour=6, minute=0).timetuple()[:5])
245 last_day_end = datetime(*date_end.replace(hour=5, minute=0).timetuple()[:5])
248 program[0:0] = NonstopSlot.get_serie(first_day_start, program[0].datetime)
251 while i < len(program)-1:
253 if not isinstance(slot, NonstopSlot):
254 slot_end = slot.datetime + timedelta(minutes=slot.get_duration())
255 next_slot = program[i+1]
257 if slot_end < next_slot.datetime:
258 next_day_start = next_slot.datetime.replace(hour=5, minute=0)
259 if slot_end < next_day_start and next_slot.datetime > next_day_start:
260 nonstop_day_slots = NonstopSlot.get_serie(slot_end, next_day_start)
261 nonstop_next_day_slots = NonstopSlot.get_serie(next_day_start, next_slot.datetime)
262 if nonstop_day_slots and nonstop_next_day_slots and \
263 nonstop_day_slots[-1].label == nonstop_next_day_slots[0].label:
264 nonstop_next_day_slots = nonstop_next_day_slots[1:]
265 program[i+1:i+1] = nonstop_day_slots + nonstop_next_day_slots
267 program[i+1:i+1] = NonstopSlot.get_serie(slot_end, next_slot.datetime)
273 NonstopSlot.get_serie(program[-1].datetime +
274 timedelta(minutes=program[-1].get_duration()),
279 def day_program(date, prefetch_sounds=True, prefetch_categories=True,
280 include_nonstop=True):
281 date_start = datetime(*date.timetuple()[:3])
282 date_end = date_start + timedelta(days=1)
283 return period_program(date_start, date_end,
284 prefetch_sounds=prefetch_sounds,
285 prefetch_categories=prefetch_categories,
286 include_nonstop=include_nonstop)