forgot about encoding, searchType and searchCase in copy functions,
[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, encoding = 'UTF-8', searchType = "partial", searchCase = "insensitive"):
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                 self.encoding = encoding
66                 self.searchType = searchType
67                 self.searchCase = searchCase
68
69 ### Attributes / Properties
70
71         def setAfterEvent(self, afterevent):
72                 if afterevent is not self._afterevent:
73                         del self._afterevent[:]
74
75                 Len = len(afterevent)
76                 if Len:
77                         for x in range(0, Len):
78                                 action, timespan = afterevent[x]
79
80                                 # Remove original entry
81                                 del afterevent[x]
82                                 # If the second argument is a tuple we assume the entry is already parsed
83                                 if isinstance(timespan, tuple):
84                                         self._afterevent.insert(x, (action, timespan))
85                                 elif timespan is None:
86                                         self._afterevent.insert(x, (action, (None,)))
87                                 else:
88                                         self._afterevent.insert(x, (action, self.calculateDayspan(*timespan)))
89
90         afterevent = property(lambda self: self._afterevent, setAfterEvent)
91
92         def setBouquets(self, bouquets):
93                 if bouquets:
94                         self._bouquets = bouquets
95                 else:
96                         self._bouquets = []
97
98         bouquets = property(lambda self: self._bouquets , setBouquets)
99
100         def setEncoding(self, encoding):
101                 if encoding:
102                         self._encoding = encoding
103
104         encoding = property(lambda self: self._encoding, setEncoding)
105
106         def setExclude(self, exclude):
107                 if exclude:
108                         self._exclude = (
109                                 [re_compile(x) for x in exclude[0]],
110                                 [re_compile(x) for x in exclude[1]],
111                                 [re_compile(x) for x in exclude[2]],
112                                 exclude[3]
113                         )
114                 else:
115                         self._exclude = ([], [], [], [])
116
117         exclude = property(lambda self: self._exclude, setExclude)
118
119         def setInclude(self, include):
120                 if include:
121                         self._include = (
122                                 [re_compile(x) for x in include[0]],
123                                 [re_compile(x) for x in include[1]],
124                                 [re_compile(x) for x in include[2]],
125                                 include[3]
126                         )
127                 else:
128                         self._include = ([], [], [], [])
129
130         include = property(lambda self: self._include, setInclude)
131
132         def setSearchCase(self, case):
133                 assert case in ("sensitive", "insensitive"), "search case must be sensitive or insensitive"
134                 self._searchCase = case
135
136         searchCase = property(lambda self: self._searchCase, setSearchCase)
137
138         def setSearchType(self, type):
139                 assert type in ("exact", "partial"), "search type must be exact or partial"
140                 self._searchType = type
141
142         searchType = property(lambda self: self._searchType, setSearchType)
143
144         def setServices(self, services):
145                 if services:
146                         self._services = services
147                 else:
148                         self._services = []
149
150         services = property(lambda self: self._services, setServices)
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(lambda self: self._timespan, 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         getAvoidDuplicateDescription = lambda self: self.avoidDuplicateDescription
280
281         getBouquets = lambda self: self._bouquets
282
283         getCompleteAfterEvent = lambda self: self._afterevent
284
285         getCounter = lambda self: self.matchCount
286         getCounterFormatString = lambda self: self.matchFormatString
287         getCounterLeft = lambda self: self.matchLeft
288         getCounterLimit = lambda self: self.matchLimit
289
290         # 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 ;-)
291         getDestination = lambda self: self.destination is not None
292
293         getDuration = lambda self: self.maxduration/60
294
295         getEnabled = lambda self: self.enabled and "yes" or "no"
296
297         getExclude = lambda self: self._exclude
298         getExcludedDays = lambda self: self.exclude[3]
299         getExcludedDescription = lambda self: [x.pattern for x in self.exclude[2]]
300         getExcludedShort = lambda self: [x.pattern for x in self.exclude[1]]
301         getExcludedTitle = lambda self: [x.pattern for x in self.exclude[0]]
302
303         getInclude = lambda self: self._include
304         getIncludedTitle = lambda self: [x.pattern for x in self.include[0]]
305         getIncludedShort = lambda self: [x.pattern for x in self.include[1]]
306         getIncludedDescription = lambda self: [x.pattern for x in self.include[2]]
307         getIncludedDays = lambda self: self.include[3]
308
309         getJustplay = lambda self: self.justplay and "1" or "0"
310
311         getLastBegin = lambda self: self.lastBegin
312
313         getMatch = lambda self: self.match
314         getName = lambda self: self.name
315
316         getOffsetBegin = lambda self: self.offset[0]/60
317         getOffsetEnd = lambda self: self.offset[1]/60
318
319         getServices = lambda self: self._services
320
321         getTags = lambda self: self.tags
322
323         getTimespan = lambda self: self._timespan
324         getTimespanBegin = lambda self: '%02d:%02d' % (self.timespan[0][0], self.timespan[0][1])
325         getTimespanEnd = lambda self: '%02d:%02d' % (self.timespan[1][0], self.timespan[1][1])
326
327         isOffsetEqual = lambda self: self.offset[0] == self.offset[1]
328
329 ### Actual functionality
330
331         def applyOffset(self, begin, end):
332                 if self.offset is None:
333                         return (begin, end)
334                 return (begin - self.offset[0], end + self.offset[1])
335
336         def checkCounter(self, timestamp):
337                 # 0-Count is considered "unset"
338                 if self.matchCount == 0:
339                         return False
340
341                 # Check if event is in current timespan (we can only manage one!)
342                 limit = strftime(self.matchFormatString, timestamp)
343                 if limit != self.matchLimit:
344                         return True
345
346                 if self.matchLeft > 0:
347                         return False
348                 return True
349
350         def checkDuration(self, length):
351                 if self.maxduration is None:
352                         return False
353                 return length > self.maxduration
354
355         def checkExcluded(self, title, short, extended, dayofweek):
356                 if len(self.exclude[3]):
357                         list = [x for x in self.exclude[3]]
358                         if "weekend" in list:
359                                 list.extend(["5", "6"])
360                         if "weekday" in list:
361                                 list.extend(["0", "1", "2", "3", "4"])
362                         if dayofweek in list:
363                                 return True
364
365                 for exclude in self.exclude[0]:
366                         if exclude.search(title):
367                                 return True
368                 for exclude in self.exclude[1]:
369                         if exclude.search(short):
370                                 return True
371                 for exclude in self.exclude[2]:
372                         if exclude.search(extended):
373                                 return True
374                 return False
375
376         def checkFilter(self, title, short, extended, dayofweek):
377                 if self.checkExcluded(title, short, extended, dayofweek):
378                         return True
379
380                 return self.checkIncluded(title, short, extended, dayofweek)
381
382         def checkIncluded(self, title, short, extended, dayofweek):
383                 if len(self.include[3]):
384                         list = [x for x in self.include[3]]
385                         if "weekend" in list:
386                                 list.extend(["5", "6"])
387                         if "weekday" in list:
388                                 list.extend(["0", "1", "2", "3", "4"])
389                         if dayofweek not in list:
390                                 return True
391
392                 for include in self.include[0]:
393                         if not include.search(title):
394                                 return True
395                 for include in self.include[1]:
396                         if not include.search(short):
397                                 return True
398                 for include in self.include[2]:
399                         if not include.search(extended):
400                                 return True
401
402                 return False
403
404         def checkServices(self, service):
405                 if len(self.services) or len(self.bouquets):
406                         if service in self.services:
407                                 return False
408
409                         from enigma import eServiceReference, eServiceCenter
410                         serviceHandler = eServiceCenter.getInstance()
411                         for bouquet in self.bouquets:
412                                 myref = eServiceReference(str(bouquet))
413                                 mylist = serviceHandler.list(myref)
414                                 if mylist is not None:
415                                         while 1:
416                                                 s = mylist.getNext()
417                                                 # TODO: I wonder if its sane to assume we get services here (and not just new lists)
418                                                 # We can ignore markers & directorys here because they won't match any event's service :-)
419                                                 if s.valid():
420                                                         # strip all after last :
421                                                         value = s.toString()
422                                                         pos = value.rfind(':')
423                                                         if pos != -1:
424                                                                 value = value[:pos+1]
425
426                                                         if service is value:
427                                                                 return False
428                                                 else:
429                                                         break
430                         return True
431                 return False
432
433         def checkTimespan(self, begin):
434                 return self.checkAnyTimespan(begin, *self.timespan)
435
436         def decrementCounter(self):
437                 if self.matchCount and self.matchLeft > 0:
438                         self.matchLeft -= 1
439
440         def getAfterEvent(self):
441                 for afterevent in self.afterevent:
442                         if afterevent[1][0] is None:
443                                 return afterevent[0]
444                 return None
445
446         def getAfterEventTimespan(self, end):
447                 for afterevent in self.afterevent:
448                         if not self.checkAnyTimespan(end, *afterevent[1]):
449                                 return afterevent[0]
450                 return None
451
452 ### Misc
453
454         def __copy__(self):
455                 return self.__class__(
456                         self.id,
457                         self.name,
458                         self.match,
459                         self.enabled,
460                         timespan = self.timespan,
461                         services = self.services,
462                         offset = self.offset,
463                         afterevent = self.afterevent,
464                         exclude = (self.getExcludedTitle(), self.getExcludedShort(), self.getExcludedDescription(), self.getExcludedDays()),
465                         maxduration = self.maxduration,
466                         destination = self.destination,
467                         include = (self.getIncludedTitle(), self.getIncludedShort(), self.getIncludedDescription(), self.getIncludedDays()),
468                         matchCount = self.matchCount,
469                         matchLeft = self.matchLeft,
470                         matchLimit = self.matchLimit,
471                         matchFormatString = self.matchFormatString,
472                         lastBegin = self.lastBegin,
473                         justplay = self.justplay,
474                         avoidDuplicateDescription = self.avoidDuplicateDescription,
475                         bouquets = self.bouquets,
476                         tags = self.tags,
477                         encoding = self.encoding,
478                         searchType = self.searchType,
479                         searchCase = self.searchCase
480                 )
481
482         def __deepcopy__(self, memo):
483                 return self.__class__(
484                         self.id,
485                         self.name,
486                         self.match,
487                         self.enabled,
488                         timespan = self.timespan,
489                         services = self.services[:],
490                         offset = self.offset and self.offset[:],
491                         afterevent = self.afterevent[:],
492                         exclude = (self.getExcludedTitle(), self.getExcludedShort(), self.getExcludedDescription(), self.exclude[3][:]),
493                         maxduration = self.maxduration,
494                         destination = self.destination,
495                         include = (self.getIncludedTitle(), self.getIncludedShort(), self.getIncludedDescription(), self.include[3][:]),
496                         matchCount = self.matchCount,
497                         matchLeft = self.matchLeft,
498                         matchLimit = self.matchLimit,
499                         matchFormatString = self.matchFormatString,
500                         lastBegin = self.lastBegin,
501                         justplay = self.justplay,
502                         avoidDuplicateDescription = self.avoidDuplicateDescription,
503                         bouquets = self.bouquets[:],
504                         tags = self.tags[:],
505                         encoding = self.encoding,
506                         searchType = self.searchType,
507                         searchCase = self.searchCase
508                 )
509
510         def __eq__(self, other):
511                 if isinstance(other, AutoTimerComponent):
512                         return self.id == other.id
513                 return False
514
515         def __lt__(self, other):
516                 if isinstance(other, AutoTimerComponent):
517                         return self.name.lower() < other.name.lower()
518                 return False
519
520         def __ne__(self, other):
521                 return not self.__eq__(other)
522
523         def __repr__(self):
524                 return ''.join([
525                         '<AutomaticTimer ',
526                         self.name,
527                         ' (',
528                         ', '.join([
529                                         str(self.match),
530                                         str(self.encoding),
531                                         str(self.searchCase),
532                                         str(self.searchType),
533                                         str(self.timespan),
534                                         str(self.services),
535                                         str(self.offset),
536                                         str(self.afterevent),
537                                         str(([x.pattern for x in self.exclude[0]],
538                                                 [x.pattern for x in self.exclude[1]],
539                                                 [x.pattern for x in self.exclude[2]],
540                                                 self.exclude[3]
541                                         )),
542                                         str(([x.pattern for x in self.include[0]],
543                                                 [x.pattern for x in self.include[1]],
544                                                 [x.pattern for x in self.include[2]],
545                                                 self.include[3]
546                                         )),
547                                         str(self.maxduration),
548                                         str(self.enabled),
549                                         str(self.destination),
550                                         str(self.matchCount),
551                                         str(self.matchLeft),
552                                         str(self.matchLimit),
553                                         str(self.matchFormatString),
554                                         str(self.lastBegin),
555                                         str(self.justplay),
556                                         str(self.avoidDuplicateDescription),
557                                         str(self.bouquets),
558                                         str(self.tags)
559                          ]),
560                          ")>"
561                 ])
562