- renamed destination attribute to location so we match default timer.xml a little...
[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         def __init__(self, id, *args, **kwargs):
11                 self.id = id
12                 self._afterevent = []
13                 self.setValues(*args, **kwargs)
14
15         def __eq__(self, other):
16                 try:
17                         return self.id == other.id
18                 except AttributeError:
19                         return False
20
21         def __ne__(self, other):
22                 return not self.__eq__(other)
23
24         def __copy__(self):
25                 return self.__class__(
26                         self.id,
27                         self.name,
28                         self.name,
29                         self.enabled,
30                         timespan = self.timespan,
31                         services = self.services,
32                         offset = self.offset,
33                         afterevent = self.afterevent,
34                         exclude = (self.getExcludedTitle(), self.getExcludedShort(), self.getExcludedDescription(), self.getExcludedDays()),
35                         include = (self.getIncludedTitle(), self.getIncludedShort(), self.getIncludedDescription(), self.getIncludedDays()),
36                         matchCount = self.matchCount,
37                         matchLeft = self.matchLeft,
38                         matchLimit = self.matchLimit,
39                         matchFormatString = self.matchFormatString,
40                         lastBegin = self.lastBegin,
41                         justplay = self.justplay,
42                         avoidDuplicateDescription = self.avoidDuplicateDescription,
43                         bouquets = self.bouquets,
44                         tags = self.tags
45                 )
46
47         def __deepcopy__(self, memo):
48                 return self.__class__(
49                         self.id,
50                         self.name,
51                         self.name,
52                         self.enabled,
53                         timespan = self.timespan,
54                         services = self.services[:],
55                         offset = self.offset and self.offset[:],
56                         afterevent = self.afterevent[:],
57                         exclude = (self.getExcludedTitle(), self.getExcludedShort(), self.getExcludedDescription(), self.exclude[3][:]),
58                         include = (self.getIncludedTitle(), self.getIncludedShort(), self.getIncludedDescription(), self.include[3][:]),
59                         matchCount = self.matchCount,
60                         matchLeft = self.matchLeft,
61                         matchLimit = self.matchLimit,
62                         matchFormatString = self.matchFormatString,
63                         lastBegin = self.lastBegin,
64                         justplay = self.justplay,
65                         avoidDuplicateDescription = self.avoidDuplicateDescription,
66                         bouquets = self.bouquets[:],
67                         tags = self.tags[:]
68                 )
69
70         def clone(self):
71                 return self.__deepcopy__({})
72
73         def setValues(self, name, match, enabled, timespan = None, services = None, offset = None, \
74                         afterevent = [], exclude = None, maxduration = None, destination = None, \
75                         include = None, matchCount = 0, matchLeft = 0, matchLimit = '', matchFormatString = '', \
76                         lastBegin = 0, justplay = False, avoidDuplicateDescription = False, bouquets = None, \
77                         tags = None):
78                 self.name = name
79                 self.match = match
80                 self.timespan = timespan
81                 self.services = services
82                 self.offset = offset
83                 self.afterevent = afterevent
84                 self.exclude = exclude
85                 self.include = include
86                 self.maxduration = maxduration
87                 self.enabled = enabled
88                 self.destination = destination
89                 self.matchCount = matchCount
90                 self.matchLeft = matchLeft
91                 self.matchLimit = matchLimit
92                 self.matchFormatString = matchFormatString
93                 self.lastBegin = lastBegin
94                 self.justplay = justplay
95                 self.avoidDuplicateDescription = avoidDuplicateDescription
96                 self.bouquets = bouquets
97                 self.tags = tags or []
98
99         def calculateDayspan(self, begin, end, ignore = None):
100                 if end[0] < begin[0] or (end[0] == begin[0] and end[1] <= begin[1]):
101                         return (begin, end, True)
102                 else:
103                         return (begin, end, False)
104
105         def setTimespan(self, timespan):
106                 if timespan is None or len(timespan) and timespan[0] is None:
107                         self._timespan = (None,)
108                 else:
109                         self._timespan = self.calculateDayspan(*timespan)
110
111         def getTimespan(self):
112                 return self._timespan
113
114         timespan = property(getTimespan, setTimespan)
115
116         def setExclude(self, exclude):
117                 if exclude:
118                         self._exclude = (
119                                 [re_compile(x) for x in exclude[0]],
120                                 [re_compile(x) for x in exclude[1]],
121                                 [re_compile(x) for x in exclude[2]],
122                                 exclude[3]
123                         )
124                 else:
125                         self._exclude = ([], [], [], [])
126
127         def getExclude(self):
128                 return self._exclude
129
130         exclude = property(getExclude, setExclude)
131
132         def setInclude(self, include):
133                 if include:
134                         self._include = (
135                                 [re_compile(x) for x in include[0]],
136                                 [re_compile(x) for x in include[1]],
137                                 [re_compile(x) for x in include[2]],
138                                 include[3]
139                         )
140                 else:
141                         self._include = ([], [], [], [])
142
143         def getInclude(self):
144                 return self._include
145
146         include = property(getInclude, setInclude)
147
148         def setServices(self, services):
149                 if services:
150                         self._services = services
151                 else:
152                         self._services = []
153
154         def getServices(self):
155                 return self._services
156
157         services = property(getServices, setServices)
158
159         def setBouquets(self, bouquets):
160                 if bouquets:
161                         self._bouquets = bouquets
162                 else:
163                         self._bouquets = []
164
165         def getBouquets(self):
166                 return self._bouquets
167
168         bouquets = property(getBouquets, setBouquets)
169
170         def setAfterEvent(self, afterevent):
171                 del self._afterevent[:]
172                 if len(afterevent):
173                         for definition in afterevent:
174                                 action, timespan = definition
175                                 if timespan is None:
176                                         self._afterevent.append((action, (None,)))
177                                 else:
178                                         self._afterevent.append((action, self.calculateDayspan(*timespan)))
179
180         def getCompleteAfterEvent(self):
181                 return self._afterevent
182
183         afterevent = property(getCompleteAfterEvent, setAfterEvent)
184
185         def hasTimespan(self):
186                 return self.timespan[0] is not None
187
188         def getTimespanBegin(self):
189                 return '%02d:%02d' % (self.timespan[0][0], self.timespan[0][1])
190
191         def getTimespanEnd(self):
192                 return '%02d:%02d' % (self.timespan[1][0], self.timespan[1][1])
193
194         def checkAnyTimespan(self, time, begin = None, end = None, haveDayspan = False):
195                 if begin is None:
196                         return False
197
198                 # Check if we span a day
199                 if haveDayspan:
200                         # Check if begin of event is later than our timespan starts
201                         if time.tm_hour > begin[0] or (time.tm_hour == begin[0] and time.tm_min >= begin[1]):
202                                 # If so, event is in our timespan
203                                 return False
204                         # Check if begin of event is earlier than our timespan end
205                         if time.tm_hour < end[0] or (time.tm_hour == end[0] and time.tm_min <= end[1]):
206                                 # If so, event is in our timespan
207                                 return False
208                         return True
209                 else:
210                         # Check if event begins earlier than our timespan starts 
211                         if time.tm_hour < begin[0] or (time.tm_hour == begin[0] and time.tm_min < begin[1]):
212                                 # Its out of our timespan then
213                                 return True
214                         # Check if event begins later than our timespan ends
215                         if time.tm_hour > end[0] or (time.tm_hour == end[0] and time.tm_min > end[1]):
216                                 # Its out of our timespan then
217                                 return True
218                         return False
219
220         def checkTimespan(self, begin):
221                 return self.checkAnyTimespan(begin, *self.timespan)
222
223         def hasDuration(self):
224                 return self.maxduration is not None
225
226         def getDuration(self):
227                 return self.maxduration/60
228
229         def checkDuration(self, length):
230                 if self.maxduration is None:
231                         return False
232                 return length > self.maxduration
233
234         def getFullServices(self):
235                 list = self.services[:]
236
237                 from enigma import eServiceReference, eServiceCenter
238                 serviceHandler = eServiceCenter.getInstance()
239                 for bouquet in self.bouquets:
240                         myref = eServiceReference(str(bouquet))
241                         mylist = serviceHandler.list(myref)
242                         if mylist is not None:
243                                 while 1:
244                                         s = mylist.getNext()
245                                         # TODO: I wonder if its sane to assume we get services here (and not just new lists)
246                                         # We can ignore markers & directorys here because they won't match any event's service :-)
247                                         if s.valid():
248                                                 # strip all after last :
249                                                 value = s.toString()
250                                                 pos = value.rfind(':')
251                                                 if pos != -1:
252                                                         value = value[:pos+1]
253
254                                                 list.append(value)
255                                         else:
256                                                 break
257
258                 return list
259
260         def checkServices(self, service):
261                 if len(self.services) or len(self.bouquets): 
262                         return service not in self.getFullServices()
263                 return False
264
265         def getExcludedTitle(self):
266                 return [x.pattern for x in self.exclude[0]]
267
268         def getExcludedShort(self):
269                 return [x.pattern for x in self.exclude[1]]
270
271         def getExcludedDescription(self):
272                 return [x.pattern for x in self.exclude[2]]
273
274         def getExcludedDays(self):
275                 return self.exclude[3]
276
277         def getIncludedTitle(self):
278                 return [x.pattern for x in self.include[0]]
279
280         def getIncludedShort(self):
281                 return [x.pattern for x in self.include[1]]
282
283         def getIncludedDescription(self):
284                 return [x.pattern for x in self.include[2]]
285
286         def getIncludedDays(self):
287                 return self.include[3]
288
289         def checkExcluded(self, title, short, extended, dayofweek):
290                 if len(self.exclude[3]):
291                         list = [x for x in self.exclude[3]]
292                         if "weekend" in list:
293                                 list.extend(["5", "6"])
294                         if "weekday" in list:
295                                 list.extend(["0", "1", "2", "3", "4"])
296                         if dayofweek in list:
297                                 return True
298
299                 for exclude in self.exclude[0]:
300                         if exclude.search(title):
301                                 return True
302                 for exclude in self.exclude[1]:
303                         if exclude.search(short):
304                                 return True
305                 for exclude in self.exclude[2]:
306                         if exclude.search(extended):
307                                 return True
308                 return False
309
310         def checkIncluded(self, title, short, extended, dayofweek):
311                 if len(self.include[3]):
312                         list = [x for x in self.include[3]]
313                         if "weekend" in list:
314                                 list.extend(["5", "6"])
315                         if "weekday" in list:
316                                 list.extend(["0", "1", "2", "3", "4"])
317                         if dayofweek not in list:
318                                 return True
319
320                 for include in self.include[0]:
321                         if not include.search(title):
322                                 return True
323                 for include in self.include[1]:
324                         if not include.search(short):
325                                 return True
326                 for include in self.include[2]:
327                         if not include.search(extended):
328                                 return True
329
330                 return False
331
332         def checkFilter(self, title, short, extended, dayofweek):
333                 if self.checkExcluded(title, short, extended, dayofweek):
334                         return True
335
336                 return self.checkIncluded(title, short, extended, dayofweek)
337
338         def hasOffset(self):
339                 return self.offset is not None
340
341         def isOffsetEqual(self):
342                 return self.offset[0] == self.offset[1]
343
344         def applyOffset(self, begin, end):
345                 if self.offset is None:
346                         return (begin, end)
347                 return (begin - self.offset[0], end + self.offset[1])
348
349         def getOffsetBegin(self):
350                 return self.offset[0]/60
351
352         def getOffsetEnd(self):
353                 return self.offset[1]/60
354
355         def hasAfterEvent(self):
356                 return len(self.afterevent)
357
358         def hasAfterEventTimespan(self):
359                 for afterevent in self.afterevent:
360                         if afterevent[1][0] is not None:
361                                 return True
362                 return False
363
364         def getAfterEventTimespan(self, end):
365                 for afterevent in self.afterevent:
366                         if not self.checkAnyTimespan(end, *afterevent[1]):
367                                 return afterevent[0]
368                 return None
369
370         def getAfterEvent(self):
371                 for afterevent in self.afterevent:
372                         if afterevent[1][0] is None:
373                                 return afterevent[0]
374                 return None
375
376         def getEnabled(self):
377                 return self.enabled and "yes" or "no"
378
379         def getJustplay(self):
380                 return self.justplay and "1" or "0"
381
382         def hasDestination(self):
383                 return self.destination is not None
384
385         def hasCounter(self):
386                 return self.matchCount != 0
387
388         def hasCounterFormatString(self):
389                 return self.matchFormatString != ''
390
391         def getCounter(self):
392                 return self.matchCount
393
394         def getCounterLeft(self):
395                 return self.matchLeft
396
397         def getCounterLimit(self):
398                 return self.matchLimit
399
400         def getCounterFormatString(self):
401                 return self.matchFormatString
402
403         def checkCounter(self, timestamp):
404                 # 0-Count is considered "unset"
405                 if self.matchCount == 0:
406                         return False
407
408                 # Check if event is in current timespan (we can only manage one!)
409                 limit = strftime(self.matchFormatString, timestamp)
410                 if limit != self.matchLimit:
411                         return True
412
413                 if self.matchLeft > 0:
414                         self.matchLeft -= 1
415                         return False
416                 return True
417
418         def update(self, begin, timestamp):
419                 # Only update limit when we have new begin
420                 if begin > self.lastBegin:
421                         self.lastBegin = begin
422
423                         # Update Counter:
424                         # %m is Month, %U is week (sunday), %W is week (monday)
425                         newLimit = strftime(self.matchFormatString, timestamp)
426
427                         if newLimit != self.matchLimit:
428                                 self.matchLeft = self.matchCount
429                                 self.matchLimit = newLimit
430
431         def getLastBegin(self):
432                 return self.lastBegin
433
434         def getAvoidDuplicateDescription(self):
435                 return self.avoidDuplicateDescription
436
437         def hasTags(self):
438                 return len(self.tags) > 0
439
440         def __repr__(self):
441                 return ''.join([
442                         '<AutomaticTimer ',
443                         self.name,
444                         ' (',
445                         ', '.join([
446                                         str(self.match),
447                                         str(self.timespan),
448                                         str(self.services),
449                                         str(self.offset),
450                                         str(self.afterevent),
451                                         str(([x.pattern for x in self.exclude[0]],
452                                                 [x.pattern for x in self.exclude[1]],
453                                                 [x.pattern for x in self.exclude[2]],
454                                                 self.exclude[3]
455                                         )),
456                                         str(([x.pattern for x in self.include[0]],
457                                                 [x.pattern for x in self.include[1]],
458                                                 [x.pattern for x in self.include[2]],
459                                                 self.include[3]
460                                         )),
461                                         str(self.maxduration),
462                                         str(self.enabled),
463                                         str(self.destination),
464                                         str(self.matchCount),
465                                         str(self.matchLeft),
466                                         str(self.matchLimit),
467                                         str(self.matchFormatString),
468                                         str(self.lastBegin),
469                                         str(self.justplay),
470                                         str(self.avoidDuplicateDescription),
471                                         str(self.bouquets),
472                                         str(self.tags)
473                          ]),
474                          ")>"
475                 ])