Enh: (Grégory Starck) still code clean for pyro wrapper in satellitelinks.
[shinken.git] / shinken / daterange.py
blobe0bee4feee0ca8a83a27c8ac36f437957cec5270
1 #!/usr/bin/env python
2 #Copyright (C) 2009-2010 :
3 # Gabes Jean, naparuba@gmail.com
4 # Gerhard Lausser, Gerhard.Lausser@consol.de
6 #This file is part of Shinken.
8 #Shinken is free software: you can redistribute it and/or modify
9 #it under the terms of the GNU Affero General Public License as published by
10 #the Free Software Foundation, either version 3 of the License, or
11 #(at your option) any later version.
13 #Shinken is distributed in the hope that it will be useful,
14 #but WITHOUT ANY WARRANTY; without even the implied warranty of
15 #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 #GNU Affero General Public License for more details.
18 #You should have received a copy of the GNU Affero General Public License
19 #along with Shinken. If not, see <http://www.gnu.org/licenses/>.
21 from shinken.util import *
24 #Get the day number (like 27 in July tuesday 27 2010 for call:
25 #2010, july, tuesday, -1 (last tuesday of july 2010)
26 def find_day_by_weekday_offset(year, month, weekday, offset):
27 #et the id if teh weekday (1 for tuesday)
28 weekday_id = Daterange.get_weekday_id(weekday)
29 if weekday_id is None:
30 return None
32 #same for month
33 month_id = Daterange.get_month_id(month)
34 if month_id is None:
35 return None
37 #thanks calendar :)
38 cal = calendar.monthcalendar(year, month_id)
40 #If we ask for a -1 day, just reverse cal
41 if offset < 0:
42 offset = abs(offset)
43 cal.reverse()
45 #ok go for it
46 nb_found = 0
47 try:
48 for i in xrange(0, offset + 1):
49 #in cal 0 mean "there are no day here :)"
50 if cal[i][weekday_id] != 0:
51 nb_found += 1
52 if nb_found == offset:
53 return cal[i][weekday_id]
54 return None
55 except:
56 return None
59 def find_day_by_offset(year, month, offset):
60 month_id = Daterange.get_month_id(month)
61 if month_id is None:
62 return None
63 (tmp, days_in_month) = calendar.monthrange(year, month_id)
64 if offset >= 0:
65 return min(offset, days_in_month)
66 else:
67 return max(1, days_in_month + offset + 1)
70 class Timerange:
71 #entry is like 00:00-24:00
72 def __init__(self, entry):
73 entries = entry.split('-')
74 start = entries[0]
75 end = entries[1]
76 sentries = start.split(':')
77 self.hstart = int(sentries[0])
78 self.mstart = int(sentries[1])
79 eentries = end.split(':')
80 self.hend = int(eentries[0])
81 self.mend = int(eentries[1])
83 def __str__(self):
84 return str(self.__dict__)
86 def get_sec_from_morning(self):
87 return self.hstart*3600 + self.mstart*60
90 def get_first_sec_out_from_morning(self):
91 #If start at 0:0, the min out is the end
92 if self.hstart == 0 and self.mstart == 0:
93 return self.hend*3600 + self.mend*60
94 return 0
97 def is_time_valid(self, t):
98 sec_from_morning = get_sec_from_morning(t)
99 return self.hstart*3600 + self.mstart* 60 <= sec_from_morning <= self.hend*3600 + self.mend* 60
103 class Daterange:
104 weekdays = {'monday' : 0, 'tuesday' : 1, 'wednesday' : 2, 'thursday' : 3, \
105 'friday' : 4, 'saturday' : 5, 'sunday': 6 }
106 months = {'january' : 1, 'february': 2, 'march' : 3, 'april' : 4, 'may' : 5, \
107 'june' : 6, 'july' : 7, 'august' : 8, 'september' : 9, \
108 'october' : 10, 'november' : 11, 'december' : 12}
109 def __init__(self, syear, smon, smday, swday, swday_offset,
110 eyear, emon, emday, ewday, ewday_offset, skip_interval, other):
111 self.syear = int(syear)
112 self.smon = smon
113 self.smday = int(smday)
114 self.swday = swday
115 self.swday_offset = int(swday_offset)
116 self.eyear = int(eyear)
117 self.emon = emon
118 self.emday = int(emday)
119 self.ewday = ewday
120 self.ewday_offset = int(ewday_offset)
121 self.skip_interval = int(skip_interval)
122 self.other = other
123 self.timeranges = []
125 for timeinterval in other.split(','):
126 self.timeranges.append(Timerange(timeinterval.strip()))
127 self.is_valid_today = False
130 def __str__(self):
131 return ''#str(self.__dict__)
134 #By default, daterange are correct
135 def is_correct(self):
136 return True
139 def get_month_id(cls, month):
140 try:
141 return Daterange.months[month]
142 except:
143 return None
144 get_month_id = classmethod(get_month_id)
147 #@memoized
148 def get_month_by_id(cls, id):
149 id = id % 12
150 for key in Daterange.months:
151 if id == Daterange.months[key]:
152 return key
153 return None
154 get_month_by_id = classmethod(get_month_by_id)
157 def get_weekday_id(cls, weekday):
158 try:
159 return Daterange.weekdays[weekday]
160 except:
161 return None
162 get_weekday_id = classmethod(get_weekday_id)
165 def get_weekday_by_id(cls, id):
166 id = id % 7
167 for key in Daterange.weekdays:
168 if id == Daterange.weekdays[key]:
169 return key
170 return None
171 get_weekday_by_id = classmethod(get_weekday_by_id)
175 def get_start_and_end_time(self, ref=None):
176 print "Not implemented"
179 def is_time_valid(self, t):
180 if self.is_time_day_valid(t):
181 for tr in self.timeranges:
182 if tr.is_time_valid(t):
183 return True
184 return False
187 def get_min_sec_from_morning(self):
188 mins = []
189 for tr in self.timeranges:
190 mins.append(tr.get_sec_from_morning())
191 return min(mins)
194 def get_min_sec_out_from_morning(self):
195 mins = []
196 for tr in self.timeranges:
197 mins.append(tr.get_first_sec_out_from_morning())
198 return min(mins)
201 def get_min_from_t(self, t):
202 if self.is_time_valid(t):
203 return t
204 t_day_epoch = get_day(t)
205 tr_mins = self.get_min_sec_from_morning()
206 return t_day_epoch + tr_mins
209 def is_time_day_valid(self, t):
210 (start_time, end_time) = self.get_start_and_end_time(t)
211 # print "My class", self.__class__
212 # print "Search for t", time.asctime(time.localtime(start_time))
213 # print "Start time, endtime", time.asctime(time.localtime(start_time)), time.asctime(time.localtime(end_time))
214 if start_time <= t <= end_time:
215 return True
216 else:
217 return False
220 def is_time_day_invalid(self, t):
221 (start_time, end_time) = self.get_start_and_end_time(t)
222 if start_time <= t <= end_time:
223 return False
224 else:
225 return True
228 #def have_future_tiremange_valid(self, t):
229 # starts = []
230 # for tr in self.timeranges:
231 # tr_start = tr.hstart * 3600 + tr.mstart * 3600
232 # if tr_start >= sec_from_morning:
233 # return True
234 # return False
237 def get_next_future_timerange_valid(self, t):
238 sec_from_morning = get_sec_from_morning(t)
239 starts = []
240 for tr in self.timeranges:
241 tr_start = tr.hstart * 3600 + tr.mstart * 60
242 if tr_start >= sec_from_morning:
243 starts.append(tr_start)
244 if starts != []:
245 return min(starts)
246 else:
247 return None
250 def get_next_future_timerange_invalid(self, t):
251 #print 'Call for get_next_future_timerange_invalid from ', time.asctime(time.localtime(t))
252 sec_from_morning = get_sec_from_morning(t)
253 #print 'sec from morning', sec_from_morning
254 ends = []
255 for tr in self.timeranges:
256 tr_start = tr.hstart * 3600 + tr.mstart * 60
257 if tr_start >= sec_from_morning:
258 ends.append(tr_start)
259 tr_end = tr.hend * 3600 + tr.mend * 60
260 if tr_end >= sec_from_morning:
261 ends.append(tr_end)
262 #print "Ends:", ends
263 #Remove the last second of the day for 00->24h"
264 if 86400 in ends:
265 ends.remove(86400)
266 if ends != []:
267 return min(ends)
268 else:
269 return None
272 def get_next_valid_day(self, t):
273 if self.get_next_future_timerange_valid(t) is None:
274 #this day is finish, we check for next period
275 (start_time, end_time) = self.get_start_and_end_time(get_day(t)+86400)
276 else:
277 (start_time, end_time) = self.get_start_and_end_time(t)
279 #print self.__class__
280 #print "Get next valid day start/end for", time.asctime(time.localtime(t))
281 #print "Start", time.asctime(time.localtime(start_time))
282 #print "End", time.asctime(time.localtime(end_time))
284 if t <= start_time:
285 return get_day(start_time)
287 if self.is_time_day_valid(t):
288 return get_day(t)
289 return None
293 def get_next_valid_time_from_t(self, t):
294 #print "DR Get next valid from:", time.asctime(time.localtime(t))
295 #print "DR Get next valid from:", t
296 if self.is_time_valid(t):
297 return t
299 #print "DR Get next valid from:", time.asctime(time.localtime(t))
300 #First we search fot the day of t
301 t_day = self.get_next_valid_day(t)
302 sec_from_morning = self.get_min_sec_from_morning()
304 #print "Search for t", time.asctime(time.localtime(t))
305 #print "DR: next day", time.asctime(time.localtime(t_day))
306 #print "DR: sec from morning", time.asctime(time.localtime(sec_from_morning))
308 #We search for the min of all tr.start > sec_from_morning
309 starts = []
310 for tr in self.timeranges:
311 tr_start = tr.hstart * 3600 + tr.mstart * 3600
312 if tr_start >= sec_from_morning:
313 starts.append(tr_start)
315 #tr can't be valid, or it will be return at the begining
316 sec_from_morning = self.get_next_future_timerange_valid(t)
317 #print "DR: sec from morning", time.asctime(time.localtime(sec_from_morning))
318 #print "Sec from morning", t_day
319 if sec_from_morning is not None:
320 if t_day is not None and sec_from_morning is not None:
321 return t_day + sec_from_morning
323 #Then we search for the next day of t
324 #The sec will be the min of the day
325 t = get_day(t)+86400
326 t_day2 = self.get_next_valid_day(t)
327 sec_from_morning = self.get_next_future_timerange_valid(t_day2)
328 if t_day2 is not None and sec_from_morning is not None:
329 return t_day2 + sec_from_morning
330 else:
331 #I'm not find any valid time
332 return None
335 def get_next_invalid_day(self, t):
336 #print 'DR: get_next_invalid_day for', time.asctime(time.localtime(t))
337 if self.is_time_day_invalid(t):
338 return t
340 next_future_timerange_invalid = self.get_next_future_timerange_invalid(t)
341 #print "next_future_timerange_invalid:", next_future_timerange_invalid
343 #If today there is no more unavalable timerange, search the next day
344 if next_future_timerange_invalid is None:
345 #print 'DR: get_next_future_timerange_invalid is None'
346 #this day is finish, we check for next period
347 (start_time, end_time) = self.get_start_and_end_time(get_day(t)+86400)
348 else:
349 #print 'DR: get_next_future_timerange_invalid is', time.asctime(time.localtime(next_future_timerange_invalid))
350 (start_time, end_time) = self.get_start_and_end_time(t)
351 #res = get_day(t) + next_future_timerange_invalid
352 #print "Early return"
353 #return res
355 #print 'DR:Start:', time.asctime(time.localtime(start_time))
356 #print 'DR:End:', time.asctime(time.localtime(end_time))
357 #The next invalid day can be t day if there a possible
358 #invalid time range (timerange is not 00->24
359 if next_future_timerange_invalid != None:
360 if start_time <= t <= end_time:
361 #print "Early Return next invalid day:", time.asctime(time.localtime(get_day(t)))
362 return get_day(t)
363 if start_time >= t :
364 return get_day(start_time)
365 else:#Else, there is no possibility than in our start_time<->end_time we got
366 #any invalid time (full period out). So it's end_time+1 sec (tomorow of end_time)
367 #print "Full period out, got end_time", time.asctime(time.localtime(get_day(end_time +1)))
368 return get_day(end_time +1)
370 return None
373 def get_next_invalid_time_from_t(self, t):
374 #print 'DR:get_next_invalid_time_from_t', time.asctime(time.localtime(t))
375 if not self.is_time_valid(t):
376 #print "DR: cool, t is invalid", time.asctime(time.localtime(t))
377 return t
378 #else:
379 # print "DR: Arg, t is valid", time.asctime(time.localtime(t))
381 #First we search fot the day of t
382 t_day = self.get_next_invalid_day(t)
383 #print "Get next invalid day:", time.asctime(time.localtime(t_day))
384 #print "Is valid day?", self.is_time_valid(t_day)
386 #We search for the min of all tr.start > sec_from_morning
387 #starts = []
388 #for tr in self.timeranges:
389 # tr_start = tr.hstart * 3600 + tr.mstart * 3600
390 # if tr_start >= sec_from_morning:
391 # starts.append(tr_start)
393 #tr can't be valid, or it will be return at the begining
394 sec_from_morning = self.get_next_future_timerange_invalid(t)
395 #print "TOTO sec_from_morning:", sec_from_morning
396 #Ok we've got a next invalid day and a invalid possibility in
397 #timerange, so the next invalid is this day+sec_from_morning
398 #print "T_day", t_day, "Sec from morning", sec_from_morning
399 if t_day is not None and sec_from_morning is not None:
400 return t_day + sec_from_morning + 1
402 #We've got a day but no sec_from_morning : the timerange is full (0->24h)
403 #so the next invalid is this day at the day_start
404 if t_day is not None and sec_from_morning == None:
405 return t_day
407 #Then we search for the next day of t
408 #The sec will be the min of the day
409 t = get_day(t)+86400
410 t_day2 = self.get_next_invalid_day(t)
411 sec_from_morning = self.get_next_future_timerange_invalid(t_day2)
412 if t_day2 is not None and sec_from_morning is not None:
413 return t_day2 + sec_from_morning + 1
415 if t_day2 is not None and sec_from_morning == None:
416 return t_day2
417 else:
418 #I'm not find any valid time
419 return None
424 #ex: 2007-01-01 - 2008-02-01
425 class CalendarDaterange(Daterange):
426 def get_start_and_end_time(self, ref=None):
427 start_time = get_start_of_day(self.syear, int(self.smon), self.smday)
428 end_time = get_end_of_day(self.eyear, int(self.emon), self.emday)
429 return (start_time, end_time)
433 #Like tuesday 00:00-24:00
434 class StandardDaterange(Daterange):
435 def __init__(self, day, other):
436 self.other = other
437 self.timeranges = []
439 for timeinterval in other.split(','):
440 self.timeranges.append(Timerange(timeinterval.strip()))
441 self.day = day
442 self.is_valid_today = False
445 #It's correct only if the weekday (sunday, etc) is a valid one
446 def is_correct(self):
447 b = self.day in Daterange.weekdays
448 if not b:
449 print "Error : %s is not a valid day" % self.day
450 return b
453 def get_start_and_end_time(self, ref=None):
454 now = time.localtime(ref)
455 self.syear = now.tm_year
456 self.month = now.tm_mon
457 #month_start_id = now.tm_mon
458 #month_start = Daterange.get_month_by_id(month_start_id)
459 self.wday = now.tm_wday
460 day_id = Daterange.get_weekday_id(self.day)
461 today_morning = get_start_of_day(now.tm_year, now.tm_mon, now.tm_mday)
462 tonight = get_end_of_day(now.tm_year, now.tm_mon, now.tm_mday)
463 day_diff = (day_id - now.tm_wday) % 7
464 return (today_morning + day_diff*86400, tonight + day_diff*86400)
466 #thusday 3 february
467 class MonthWeekDayDaterange(Daterange):
468 #It's correct only if the weekday (sunday, etc) is a valid one
469 def is_correct(self):
470 b = True
471 b &= self.swday in Daterange.weekdays
472 if not b:
473 print "Error : %s is not a valid day" % self.swday
475 b &= self.ewday in Daterange.weekdays
476 if not b:
477 print "Error : %s is not a valid day" % self.ewday
479 return b
482 def get_start_and_end_time(self, ref=None):
483 now = time.localtime(ref)
485 if self.syear == 0:
486 self.syear = now.tm_year
487 month_id = Daterange.get_month_id(self.smon)
488 day_start = find_day_by_weekday_offset(self.syear, self.smon, self.swday, self.swday_offset)
489 start_time = get_start_of_day(self.syear, month_id, day_start)
491 if self.eyear == 0:
492 self.eyear = now.tm_year
493 month_end_id = Daterange.get_month_id(self.emon)
494 day_end = find_day_by_weekday_offset(self.eyear, self.emon, self.ewday, self.ewday_offset)
495 end_time = get_end_of_day(self.eyear, month_end_id, day_end)
497 now_epoch = time.mktime(now)
498 if start_time > end_time: #the period is between years
499 if now_epoch > end_time:#check for next year
500 day_end = find_day_by_weekday_offset(self.eyear + 1, self.emon, self.ewday, self.ewday_offset)
501 end_time = get_end_of_day(self.eyear + 1, month_end_id, day_end)
502 else:#it s just that start was the last year
503 day_start = find_day_by_weekday_offset(self.syear - 1, self.smon, self.swday, self.swday_offset)
504 start_time = get_start_of_day(self.syear - 1, month_id, day_start)
505 else:
506 if now_epoch > end_time:#just have to check for next year if necessery
507 day_start = find_day_by_weekday_offset(self.syear + 1, self.smon, self.swday, self.swday_offset)
508 start_time = get_start_of_day(self.syear + 1, month_id, day_start)
509 day_end = find_day_by_weekday_offset(self.eyear + 1, self.emon, self.ewday, self.ewday_offset)
510 end_time = get_end_of_day(self.eyear + 1, month_end_id, day_end)
512 return (start_time, end_time)
516 class MonthDateDaterange(Daterange):
517 def get_start_and_end_time(self, ref=None):
518 now = time.localtime(ref)
519 if self.syear == 0:
520 self.syear = now.tm_year
521 month_start_id = Daterange.get_month_id(self.smon)
522 day_start = find_day_by_offset(self.syear, self.smon, self.smday)
523 start_time = get_start_of_day(self.syear, month_start_id, day_start)
525 if self.eyear == 0:
526 self.eyear = now.tm_year
527 month_end_id = Daterange.get_month_id(self.emon)
528 day_end = find_day_by_offset(self.eyear, self.emon, self.emday)
529 end_time = get_end_of_day(self.eyear, month_end_id, day_end)
531 now_epoch = time.mktime(now)
532 if start_time > end_time: #the period is between years
533 if now_epoch > end_time:#check for next year
534 day_end = find_day_by_offset(self.eyear + 1, self.emon, self.emday)
535 end_time = get_end_of_day(self.eyear + 1, month_end_id, day_end)
536 else:#it s just that start was the last year
537 day_start = find_day_by_offset(self.syear-1, self.smon, self.emday)
538 start_time = get_start_of_day(self.syear-1, month_start_id, day_start)
539 else:
540 if now_epoch > end_time:#just have to check for next year if necessery
541 day_start = find_day_by_offset(self.syear+1, self.smon, self.emday)
542 start_time = get_start_of_day(self.syear+1, month_start_id, day_start)
543 day_end = find_day_by_offset(self.eyear+1, self.emon, self.emday)
544 end_time = get_end_of_day(self.eyear+1, month_end_id, day_end)
546 return (start_time, end_time)
549 class WeekDayDaterange(Daterange):
550 def get_start_and_end_time(self, ref=None):
551 now = time.localtime(ref)
553 #If no year, it's our year
554 if self.syear == 0:
555 self.syear = now.tm_year
556 month_start_id = now.tm_mon
557 month_start = Daterange.get_month_by_id(month_start_id)
558 day_start = find_day_by_weekday_offset(self.syear, month_start, self.swday, self.swday_offset)
559 start_time = get_start_of_day(self.syear, month_start_id, day_start)
561 #Same for end year
562 if self.eyear == 0:
563 self.eyear = now.tm_year
564 month_end_id = now.tm_mon
565 month_end = Daterange.get_month_by_id(month_end_id)
566 day_end = find_day_by_weekday_offset(self.eyear, month_end, self.ewday, self.ewday_offset)
567 end_time = get_end_of_day(self.eyear, month_end_id, day_end)
569 #Maybe end_time is before start. So look for the
570 #next month
571 if start_time > end_time:
572 month_end_id = month_end_id + 1
573 if month_end_id > 12:
574 month_end_id = 1
575 self.eyear += 1
576 month_end = Daterange.get_month_by_id(month_end_id)
577 day_end = find_day_by_weekday_offset(self.eyear, month_end, self.ewday, self.ewday_offset)
578 end_time = get_end_of_day(self.eyear, month_end_id, day_end)
580 now_epoch = time.mktime(now)
581 #But maybe we look not ethouth far. We should add a month
582 if end_time < now_epoch:
583 month_end_id = month_end_id + 1
584 month_start_id = month_start_id + 1
585 if month_end_id > 12:
586 month_end_id = 1
587 self.eyear += 1
588 if month_start_id > 12:
589 month_start_id = 1
590 self.syear += 1
591 #First start
592 month_start = Daterange.get_month_by_id(month_start_id)
593 day_start = find_day_by_weekday_offset(self.syear, month_start, self.swday, self.swday_offset)
594 start_time = get_start_of_day(self.syear, month_start_id, day_start)
595 #Then end
596 month_end = Daterange.get_month_by_id(month_end_id)
597 day_end = find_day_by_weekday_offset(self.eyear, month_end, self.ewday, self.ewday_offset)
598 end_time = get_end_of_day(self.eyear, month_end_id, day_end)
600 return (start_time, end_time)
603 class MonthDayDaterange(Daterange):
604 def get_start_and_end_time(self, ref=None):
605 now = time.localtime(ref)
606 if self.syear == 0:
607 self.syear = now.tm_year
608 month_start_id = now.tm_mon
609 month_start = Daterange.get_month_by_id(month_start_id)
610 day_start = find_day_by_offset(self.syear, month_start, self.smday)
611 start_time = get_start_of_day(self.syear, month_start_id, day_start)
613 if self.eyear == 0:
614 self.eyear = now.tm_year
615 month_end_id = now.tm_mon
616 month_end = Daterange.get_month_by_id(month_end_id)
617 day_end = find_day_by_offset(self.eyear, month_end, self.emday)
618 end_time = get_end_of_day(self.eyear, month_end_id, day_end)
620 now_epoch = time.mktime(now)
622 if start_time > end_time:
623 month_end_id = month_end_id + 1
624 if month_end_id > 12:
625 month_end_id = 1
626 self.eyear += 1
627 day_end = find_day_by_offset(self.eyear, month_end, self.emday)
628 end_time = get_end_of_day(self.eyear, month_end_id, day_end)
630 if end_time < now_epoch:
631 month_end_id = month_end_id + 1
632 month_start_id = month_start_id + 1
633 if month_end_id > 12:
634 month_end_id = 1
635 self.eyear += 1
636 if month_start_id > 12:
637 month_start_id = 1
638 self.syear += 1
640 #For the start
641 month_start = Daterange.get_month_by_id(month_start_id)
642 day_start = find_day_by_offset(self.syear, month_start, self.smday)
643 start_time = get_start_of_day(self.syear, month_start_id, day_start)
645 #For the end
646 month_end = Daterange.get_month_by_id(month_end_id)
647 day_end = find_day_by_offset(self.eyear, month_end, self.emday)
648 end_time = get_end_of_day(self.eyear, month_end_id, day_end)
650 return (start_time, end_time)