fix possible bsod since last revision
[vuplus_dvbapp-plugin] / autotimer / src / AutoTimerComponent.py
1 # Format Counter
2 from time import strftime
3
4 # regular expression
5 from re import compile as re_compile
6
7 class AutoTimerComponent(object):
8         """AutoTimer Component which also handles validity checks"""
9
10         """
11          Initiate
12         """
13         def __init__(self, id, name, match, enabled, *args, **kwargs):
14                 self.id = id
15                 self._afterevent = []
16                 self.setValues(name, match, enabled, *args, **kwargs)
17
18         """
19          Unsets all Attributes
20         """
21         def clear(self, id = -1, enabled = False):
22                 self.id = id
23                 self.setValues('', '', enabled)
24
25         """
26          Create a deep copy of this instance 
27         """
28         def clone(self):
29                 return self.__deepcopy__({})
30
31         """
32          Hook needed for WebIf
33         """
34         def getEntry(self):
35                 return self
36
37         """
38          Keeps init small and helps setting many values at once
39         """
40         def setValues(self, name, match, enabled, timespan = None, services = None, offset = None, \
41                         afterevent = [], exclude = None, maxduration = None, destination = None, \
42                         include = None, matchCount = 0, matchLeft = 0, matchLimit = '', matchFormatString = '', \
43                         lastBegin = 0, justplay = False, avoidDuplicateDescription = 0, bouquets = None, \
44                         tags = None):
45                 self.name = name
46                 self.match = match
47                 self.enabled = enabled
48                 self.timespan = timespan
49                 self.services = services
50                 self.offset = offset
51                 self.afterevent = afterevent
52                 self.exclude = exclude
53                 self.maxduration = maxduration
54                 self.destination = destination
55                 self.include = include
56                 self.matchCount = matchCount
57                 self.matchLeft = matchLeft
58                 self.matchLimit = matchLimit
59                 self.matchFormatString = matchFormatString
60                 self.lastBegin = lastBegin
61                 self.justplay = justplay
62                 self.avoidDuplicateDescription = avoidDuplicateDescription
63                 self.bouquets = bouquets
64                 self.tags = tags or []
65
66 ### Attributes / Properties
67
68         def getCompleteAfterEvent(self):
69                 return self._afterevent
70
71         def setAfterEvent(self, afterevent):
72                 del self._afterevent[:]
73                 if len(afterevent):
74                         for definition in afterevent:
75                                 action, timespan = definition
76                                 if timespan is None:
77                                         self._afterevent.append((action, (None,)))
78                                 else:
79                                         self._afterevent.append((action, self.calculateDayspan(*timespan)))
80
81         afterevent = property(getCompleteAfterEvent, setAfterEvent)
82
83         def setBouquets(self, bouquets):
84                 if bouquets:
85                         self._bouquets = bouquets
86                 else:
87                         self._bouquets = []
88
89         def getBouquets(self):
90                 return self._bouquets
91
92         bouquets = property(getBouquets, setBouquets)
93
94         def setExclude(self, exclude):
95                 if exclude:
96                         self._exclude = (
97                                 [re_compile(x) for x in exclude[0]],
98                                 [re_compile(x) for x in exclude[1]],
99                                 [re_compile(x) for x in exclude[2]],
100                                 exclude[3]
101                         )
102                 else:
103                         self._exclude = ([], [], [], [])
104
105         def getExclude(self):
106                 return self._exclude
107
108         exclude = property(getExclude, setExclude)
109
110         def setInclude(self, include):
111                 if include:
112                         self._include = (
113                                 [re_compile(x) for x in include[0]],
114                                 [re_compile(x) for x in include[1]],
115                                 [re_compile(x) for x in include[2]],
116                                 include[3]
117                         )
118                 else:
119                         self._include = ([], [], [], [])
120
121         def getInclude(self):
122                 return self._include
123
124         include = property(getInclude, setInclude)
125
126         def setName(self, name):
127                 self.name = name
128
129         def getMatch(self):
130                 return self._match
131
132         def setMatch(self, match):
133                 # XXX: a sanity check might be useful...
134                 self._match = match
135
136         match = property(getMatch, setMatch)
137
138         def getServices(self):
139                 return self._services
140
141         def setServices(self, services):
142                 if services:
143                         self._services = services
144                 else:
145                         self._services = []
146
147         services = property(getServices, setServices)
148
149         def getTimespan(self):
150                 return self._timespan
151
152         def setTimespan(self, timespan):
153                 if timespan is None or len(timespan) and timespan[0] is None:
154                         self._timespan = (None,)
155                 else:
156                         self._timespan = self.calculateDayspan(*timespan)
157
158         timespan = property(getTimespan, setTimespan)
159
160 ### See if Attributes are set
161
162         def hasAfterEvent(self):
163                 return len(self.afterevent)
164
165         def hasAfterEventTimespan(self):
166                 for afterevent in self.afterevent:
167                         if afterevent[1][0] is not None:
168                                 return True
169                 return False
170
171         def hasCounter(self):
172                 return self.matchCount != 0
173
174         def hasCounterFormatString(self):
175                 return self.matchFormatString != ''
176
177         def hasDestination(self):
178                 return self.destination is not None
179
180         def hasDuration(self):
181                 return self.maxduration is not None
182
183         def hasTags(self):
184                 return len(self.tags) > 0
185
186         def hasTimespan(self):
187                 return self.timespan[0] is not None
188
189         def hasOffset(self):
190                 return self.offset is not None
191
192 ### Helper
193
194         """
195          Returns a tulple of (input begin, input end, begin earlier than end)
196         """
197         def calculateDayspan(self, begin, end, ignore = None):
198                 if end[0] < begin[0] or (end[0] == begin[0] and end[1] <= begin[1]):
199                         return (begin, end, True)
200                 else:
201                         return (begin, end, False)
202
203         """
204          Returns if a given timestruct is in a timespan
205         """
206         def checkAnyTimespan(self, time, begin = None, end = None, haveDayspan = False):
207                 if begin is None:
208                         return False
209
210                 # Check if we span a day
211                 if haveDayspan:
212                         # Check if begin of event is later than our timespan starts
213                         if time.tm_hour > begin[0] or (time.tm_hour == begin[0] and time.tm_min >= begin[1]):
214                                 # If so, event is in our timespan
215                                 return False
216                         # Check if begin of event is earlier than our timespan end
217                         if time.tm_hour < end[0] or (time.tm_hour == end[0] and time.tm_min <= end[1]):
218                                 # If so, event is in our timespan
219                                 return False
220                         return True
221                 else:
222                         # Check if event begins earlier than our timespan starts 
223                         if time.tm_hour < begin[0] or (time.tm_hour == begin[0] and time.tm_min < begin[1]):
224                                 # Its out of our timespan then
225                                 return True
226                         # Check if event begins later than our timespan ends
227                         if time.tm_hour > end[0] or (time.tm_hour == end[0] and time.tm_min > end[1]):
228                                 # Its out of our timespan then
229                                 return True
230                         return False
231
232         """
233          Returns a list of all allowed services by listing the bouquets
234         """
235         def getFullServices(self):
236                 list = self.services[:]
237
238                 from enigma import eServiceReference, eServiceCenter
239                 serviceHandler = eServiceCenter.getInstance()
240                 for bouquet in self.bouquets:
241                         myref = eServiceReference(str(bouquet))
242                         mylist = serviceHandler.list(myref)
243                         if mylist is not None:
244                                 while 1:
245                                         s = mylist.getNext()
246                                         # TODO: I wonder if its sane to assume we get services here (and not just new lists)
247                                         # We can ignore markers & directorys here because they won't match any event's service :-)
248                                         if s.valid():
249                                                 # strip all after last :
250                                                 value = s.toString()
251                                                 pos = value.rfind(':')
252                                                 if pos != -1:
253                                                         value = value[:pos+1]
254
255                                                 list.append(value)
256                                         else:
257                                                 break
258
259                 return list
260
261         """
262          Called when a timer based on this component was added
263         """
264         def update(self, begin, timestamp):
265                 # Only update limit when we have new begin
266                 if begin > self.lastBegin:
267                         self.lastBegin = begin
268
269                         # Update Counter:
270                         # %m is Month, %U is week (sunday), %W is week (monday)
271                         newLimit = strftime(self.matchFormatString, timestamp)
272
273                         if newLimit != self.matchLimit:
274                                 self.matchLeft = self.matchCount
275                                 self.matchLimit = newLimit
276
277 ### Makes saving Config easier
278
279         def getAvoidDuplicateDescription(self):
280                 return self.avoidDuplicateDescription
281
282         def getCounter(self):
283                 return self.matchCount
284
285         def getCounterFormatString(self):
286                 return self.matchFormatString
287
288         def getCounterLeft(self):
289                 return self.matchLeft
290
291         def getCounterLimit(self):
292                 return self.matchLimit
293
294         def getDestination(self):
295                 # XXX: as this function was not added by me (ritzMo) i'll leave it like this but i'm not really sure if this is right ;-)
296                 return self.destination is not None
297
298         def getDuration(self):
299                 return self.maxduration/60
300
301         def getEnabled(self):
302                 return self.enabled and "yes" or "no"
303
304         def getExcludedDays(self):
305                 return self.exclude[3]
306
307         def getExcludedDescription(self):
308                 return [x.pattern for x in self.exclude[2]]
309
310         def getExcludedShort(self):
311                 return [x.pattern for x in self.exclude[1]]
312
313         def getExcludedTitle(self):
314                 return [x.pattern for x in self.exclude[0]]     
315
316         def getIncludedTitle(self):
317                 return [x.pattern for x in self.include[0]]
318
319         def getIncludedShort(self):
320                 return [x.pattern for x in self.include[1]]
321
322         def getIncludedDescription(self):
323                 return [x.pattern for x in self.include[2]]
324
325         def getIncludedDays(self):
326                 return self.include[3]
327
328         def getJustplay(self):
329                 return self.justplay and "1" or "0"
330
331         def getLastBegin(self):
332                 return self.lastBegin
333
334         def getName(self):
335                 return self.name
336
337         def getOffsetBegin(self):
338                 return self.offset[0]/60
339
340         def getOffsetEnd(self):
341                 return self.offset[1]/60
342
343         def getTags(self):
344                 return self.tags
345
346         def getTimespanBegin(self):
347                 return '%02d:%02d' % (self.timespan[0][0], self.timespan[0][1])
348
349         def getTimespanEnd(self):
350                 return '%02d:%02d' % (self.timespan[1][0], self.timespan[1][1])
351
352         def isOffsetEqual(self):
353                 return self.offset[0] == self.offset[1]
354         
355 ### Actual functionality
356
357         def applyOffset(self, begin, end):
358                 if self.offset is None:
359                         return (begin, end)
360                 return (begin - self.offset[0], end + self.offset[1])
361
362         def checkCounter(self, timestamp):
363                 # 0-Count is considered "unset"
364                 if self.matchCount == 0:
365                         return False
366
367                 # Check if event is in current timespan (we can only manage one!)
368                 limit = strftime(self.matchFormatString, timestamp)
369                 if limit != self.matchLimit:
370                         return True
371
372                 if self.matchLeft > 0:
373                         return False
374                 return True
375
376         def checkDuration(self, length):
377                 if self.maxduration is None:
378                         return False
379                 return length > self.maxduration
380         
381         def checkExcluded(self, title, short, extended, dayofweek):
382                 if len(self.exclude[3]):
383                         list = [x for x in self.exclude[3]]
384                         if "weekend" in list:
385                                 list.extend(["5", "6"])
386                         if "weekday" in list:
387                                 list.extend(["0", "1", "2", "3", "4"])
388                         if dayofweek in list:
389                                 return True
390
391                 for exclude in self.exclude[0]:
392                         if exclude.search(title):
393                                 return True
394                 for exclude in self.exclude[1]:
395                         if exclude.search(short):
396                                 return True
397                 for exclude in self.exclude[2]:
398                         if exclude.search(extended):
399                                 return True
400                 return False
401
402         def checkFilter(self, title, short, extended, dayofweek):
403                 if self.checkExcluded(title, short, extended, dayofweek):
404                         return True
405
406                 return self.checkIncluded(title, short, extended, dayofweek)
407
408         def checkIncluded(self, title, short, extended, dayofweek):
409                 if len(self.include[3]):
410                         list = [x for x in self.include[3]]
411                         if "weekend" in list:
412                                 list.extend(["5", "6"])
413                         if "weekday" in list:
414                                 list.extend(["0", "1", "2", "3", "4"])
415                         if dayofweek not in list:
416                                 return True
417
418                 for include in self.include[0]:
419                         if not include.search(title):
420                                 return True
421                 for include in self.include[1]:
422                         if not include.search(short):
423                                 return True
424                 for include in self.include[2]:
425                         if not include.search(extended):
426                                 return True
427
428                 return False
429
430         def checkServices(self, service):
431                 if len(self.services) or len(self.bouquets): 
432                         return service not in self.getFullServices()
433                 return False
434
435         def checkTimespan(self, begin):
436                 return self.checkAnyTimespan(begin, *self.timespan)
437
438         def decrementCounter(self)
439                 if self.matchCount and self.matchLeft > 0:
440                         self.matchLeft -= 1
441
442         def getAfterEvent(self):
443                 for afterevent in self.afterevent:
444                         if afterevent[1][0] is None:
445                                 return afterevent[0]
446                 return None
447
448         def getAfterEventTimespan(self, end):
449                 for afterevent in self.afterevent:
450                         if not self.checkAnyTimespan(end, *afterevent[1]):
451                                 return afterevent[0]
452                 return None
453
454 ### Misc
455
456         def __copy__(self):
457                 return self.__class__(
458                         self.id,
459                         self.name,
460                         self.match,
461                         self.enabled,
462                         timespan = self.timespan,
463                         services = self.services,
464                         offset = self.offset,
465                         afterevent = self.afterevent,
466                         exclude = (self.getExcludedTitle(), self.getExcludedShort(), self.getExcludedDescription(), self.getExcludedDays()),
467                         maxduration = self.maxduration,
468                         destination = self.destination,
469                         include = (self.getIncludedTitle(), self.getIncludedShort(), self.getIncludedDescription(), self.getIncludedDays()),
470                         matchCount = self.matchCount,
471                         matchLeft = self.matchLeft,
472                         matchLimit = self.matchLimit,
473                         matchFormatString = self.matchFormatString,
474                         lastBegin = self.lastBegin,
475                         justplay = self.justplay,
476                         avoidDuplicateDescription = self.avoidDuplicateDescription,
477                         bouquets = self.bouquets,
478                         tags = self.tags
479                 )
480
481         def __deepcopy__(self, memo):
482                 return self.__class__(
483                         self.id,
484                         self.name,
485                         self.match,
486                         self.enabled,
487                         timespan = self.timespan,
488                         services = self.services[:],
489                         offset = self.offset and self.offset[:],
490                         afterevent = self.afterevent[:],
491                         exclude = (self.getExcludedTitle(), self.getExcludedShort(), self.getExcludedDescription(), self.exclude[3][:]),
492                         maxduration = self.maxduration,
493                         destination = self.destination,
494                         include = (self.getIncludedTitle(), self.getIncludedShort(), self.getIncludedDescription(), self.include[3][:]),
495                         matchCount = self.matchCount,
496                         matchLeft = self.matchLeft,
497                         matchLimit = self.matchLimit,
498                         matchFormatString = self.matchFormatString,
499                         lastBegin = self.lastBegin,
500                         justplay = self.justplay,
501                         avoidDuplicateDescription = self.avoidDuplicateDescription,
502                         bouquets = self.bouquets[:],
503                         tags = self.tags[:]
504                 )
505
506         def __eq__(self, other):
507                 if isinstance(other, AutoTimerComponent):
508                         return self.id == other.id
509                 return False
510
511         def __lt__(self, other):
512                 if isinstance(other, AutoTimerComponent):
513                         return self.name.lower() < other.name.lower()
514                 return False
515
516         def __ne__(self, other):
517                 return not self.__eq__(other)
518
519         def __repr__(self):
520                 return ''.join([
521                         '<AutomaticTimer ',
522                         self.name,
523                         ' (',
524                         ', '.join([
525                                         str(self.match),
526                                         str(self.timespan),
527                                         str(self.services),
528                                         str(self.offset),
529                                         str(self.afterevent),
530                                         str(([x.pattern for x in self.exclude[0]],
531                                                 [x.pattern for x in self.exclude[1]],
532                                                 [x.pattern for x in self.exclude[2]],
533                                                 self.exclude[3]
534                                         )),
535                                         str(([x.pattern for x in self.include[0]],
536                                                 [x.pattern for x in self.include[1]],
537                                                 [x.pattern for x in self.include[2]],
538                                                 self.include[3]
539                                         )),
540                                         str(self.maxduration),
541                                         str(self.enabled),
542                                         str(self.destination),
543                                         str(self.matchCount),
544                                         str(self.matchLeft),
545                                         str(self.matchLimit),
546                                         str(self.matchFormatString),
547                                         str(self.lastBegin),
548                                         str(self.justplay),
549                                         str(self.avoidDuplicateDescription),
550                                         str(self.bouquets),
551                                         str(self.tags)
552                          ]),
553                          ")>"
554                 ])
555