[WebIf]: Fix some bugs in timer handling
[enigma2-plugins.git] / pushservice / src / PushServiceBase.py
1 #######################################################################
2 #
3 #    Push Service for Enigma-2
4 #    Coded by betonme (c) 2012 <glaserfrank(at)gmail.com>
5 #    Support: http://www.i-have-a-dreambox.com/wbb2/thread.php?threadid=167779
6 #
7 #    This program is free software; you can redistribute it and/or
8 #    modify it under the terms of the GNU General Public License
9 #    as published by the Free Software Foundation; either version 2
10 #    of the License, or (at your option) any later version.
11 #
12 #    This program is distributed in the hope that it will be useful,
13 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
14 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 #    GNU General Public License for more details.
16 #
17 #######################################################################
18
19 import os, sys, traceback
20 from time import localtime, strftime
21
22 # Config
23 from Components.config import config
24
25 # XML
26 from xml.etree.cElementTree import Element, SubElement, Comment
27 from Tools.XMLTools import stringToXML
28
29 # Tools
30 from Tools.Directories import resolveFilename, SCOPE_PLUGINS
31 from Tools.BoundFunction import boundFunction
32
33 # Plugin internal
34 from . import _
35 from Modules import Modules
36 from ConfigFile import ConfigFile
37 from ServiceBase import ServiceBase
38 from ControllerBase import ControllerBase
39
40
41 # Constants
42 SERVICE = "Service"
43 CONTROLLER = "Controller"
44 OPTION = "Option"
45
46 SERVICE_PATH = os.path.join( resolveFilename(SCOPE_PLUGINS), "Extensions/PushService/Services/" )
47 CONTROLLER_PATH = os.path.join( resolveFilename(SCOPE_PLUGINS), "Extensions/PushService/Controller/" )
48
49
50 class PushServiceBase(Modules, ConfigFile):
51
52         def __init__(self, path=""):
53                 Modules.__init__(self)
54                 ConfigFile.__init__(self)
55                 
56                 self.services = []
57                 self.controllers = []
58                 
59                 self.pushcallbacks = {}
60                 self.pusherrbacks = {}
61                 
62                 # Read module files from subfolders
63                 self.servicemodules = self.loadModules(SERVICE_PATH, ServiceBase)
64                 self.controllermodules = self.loadModules(CONTROLLER_PATH, ControllerBase)
65
66
67         ######################################
68         # Setter / Getter
69         def getServices(self):
70                 return self.services or []
71
72         def getService(self, idx):
73                 if idx < len(self.services):
74                         return self.services[idx]
75                 else:
76                         return None
77
78         def getAvlServices(self):
79                 slist = []
80                 if self.servicemodules:
81                         serviceclasses = [ service.getClass() for service in self.services] if self.services else []
82                         for name, module in self.servicemodules.iteritems():
83                                 if module.forceSingle():
84                                         # We have to check if there is already a plugin instance
85                                         if name in serviceclasses:
86                                                 # A service instance already exists
87                                                 continue
88                                 slist.append( (name, module) )
89                         slist.sort()
90                 return slist
91
92         def getServiceInstances(self):
93                 return [( service.getNameId(), service ) for service in self.getServices() ]
94
95         def addService(self, module):
96                 id = None
97                 service = module and self.instantiateModule( module )
98                 if service:
99                         service.setEnable(True)
100                         self.services.append( service )
101                         self.services.sort( key=lambda x: ( x.getUniqueID() ) )
102                         id = service.getUniqueID()
103                 return id
104
105         def removeService(self, service):
106                 if service in self.services:
107                         self.services.remove( service )
108
109         def getControllers(self):
110                 return self.controllers or []
111
112         def getController(self, idx):
113                 if idx < len(self.controllers):
114                         return self.controllers[idx]
115                 else:
116                         return None
117
118         def getAvlControllers(self):
119                 plist = []
120                 if self.controllermodules:
121                         controllerclasses = [ controller.getClass() for controller in self.controllers] if self.controllers else []
122                         for name, module in self.controllermodules.iteritems():
123                                 if module.forceSingle():
124                                         # We have to check if there is already a controller instance
125                                         if name in controllerclasses:
126                                                 # A controller instance already exists
127                                                 continue
128                                 plist.append( (name, module) )
129                         plist.sort()
130                 return plist
131
132         def getControllerInstances(self):
133                 return [( controller.getNameId(), controller ) for controller in self.getControllers() ]
134
135         def addController(self, module):
136                 id = None
137                 controller = module and self.instantiateModule( module )
138                 if controller:
139                         controller.setEnable(True)
140                         self.controllers.append( controller )
141                         self.controllers.sort( key=lambda x: ( x.getUniqueID() ) )
142                         id = controller.getUniqueID()
143                 return id
144
145         def removeController(self, controller):
146                 if controller in self.controllers:
147                         self.controllers.remove( controller )
148
149
150         ######################################
151         # Config
152         def copyto(self, destination):
153                 destination.services = self.services
154                 destination.controllers = self.controllers
155                 destination.servicemodules = self.servicemodules
156                 destination.controllermodules = self.controllermodules
157
158         def copyfrom(self, source):
159                 self.services = source.services
160                 self.controllers = source.controllers
161                 self.servicemodules = source.servicemodules
162                 self.controllermodules = source.controllermodules
163
164         def load(self):
165                 # Read xml config file
166                 root = self.readXML()
167                 if root:
168                         services = []
169                         controllers = []
170                         
171                         # Reset the unique id counters
172                         ServiceBase.resetUniqueID()
173                         ControllerBase.resetUniqueID()
174                         # Parse Config
175                         def parse(root, typ, modules):
176                                 instances = []
177                                 if root:
178                                         for element in root.findall(typ):
179                                                 name = element.get("name", "")
180                                                 enable = element.get("enable", "True")
181                                                 if name:
182                                                         module = modules.get(name, None)
183                                                         instance = self.instantiateModule(module)
184                                                         if instance:
185                                                                 instance.setEnable(eval(enable))
186                                                                 
187                                                                 # Set instance options
188                                                                 options = []
189                                                                 for option in element.findall(OPTION):
190                                                                         key = option.get("key", "")
191                                                                         value = option.text
192                                                                         if key and value:
193                                                                                 options.append((key, value))
194                                                                 
195                                                                 if options:
196                                                                         instance.setOptions(options)
197                                                                 
198                                                                 # Append to active controller list
199                                                                 instances.append(instance)
200                                 return instances
201                         services = parse( root, SERVICE, self.servicemodules )
202                         controllers = parse( root, CONTROLLER, self.controllermodules )
203                         
204                         self.services = services
205                         self.controllers = controllers
206                 else:
207                         self.services = []
208                         self.controllers = []
209
210         def save(self):
211                 # Generate List in RAM
212                 root = None
213                 services = self.services
214                 controllers = self.controllers
215                 
216                 # Build Header
217                 from plugin import NAME, VERSION
218                 root = Element(NAME)
219                 root.set('version', VERSION)
220                 root.append(Comment(_("Don't edit this manually unless you really know what you are doing")))
221                 
222                 # Build Body
223                 def build(root, instances, typ):
224                         for instance in instances:
225                                 # Add module
226                                 element = SubElement( root, typ, name = stringToXML(instance.getName()), enable = stringToXML(instance.getStringEnable()) )
227                                 # Add options
228                                 options = instance.getStringOptions()
229                                 if options:
230                                         for key, value, description in options:
231                                                 SubElement( element, OPTION, key = stringToXML(key) ).text = stringToXML(value)
232                         return root
233                 
234                 if services:
235                         root = build( root, services, SERVICE)
236                 if controllers:
237                         root = build( root, controllers, CONTROLLER)
238                 
239                 self.writeXML( root )
240
241
242         ######################################
243         # Controller handling
244         def begin(self):
245                 # Loop over all Services
246                 for service in self.getServices():
247                         if service.getEnable():
248                                 service.begin()
249                 # Loop over all Controllers
250                 for controller in self.getControllers():
251                         if controller.getEnable():
252                                 controller.begin()
253
254         def end(self):
255                 # Loop over all Services
256                 for service in self.getServices():
257                         if service.getEnable():
258                                 service.end()
259                 # Loop over all Controllers
260                 for controller in self.getControllers():
261                         if controller.getEnable():
262                                 controller.end()
263
264         def run(self):
265                 print _("PushService started: ") + strftime( _("%d.%m.%Y %H:%M"), localtime() )
266                 
267                 controllers = self.controllers
268                 self.pushcallbacks = {}
269                 self.pusherrbacks = {}
270                 
271                 # Loop over all Controllers
272                 if controllers:
273                         for controller in controllers:
274                                 if controller.getEnable():
275                                         print _("PushService running: ") + str( controller.getName() )
276                                         
277                                         try:
278                                                 # Run controller
279                                                 ret = controller.run(
280                                                                 boundFunction(self.runcallback, controller),
281                                                                 boundFunction(self.runcallback, controller) )
282                                         except Exception, e:
283                                                 print _("PushService controller run() exception")
284                                                 exc_type, exc_value, exc_traceback = sys.exc_info()
285                                                 traceback.print_exception(exc_type, exc_value, exc_traceback, file=sys.stdout)
286
287         def runcallback(self, controller, *args):
288                 services = self.services
289                 subject, body, attachments = "", "", []
290                 
291                 # Parse return value(s)
292                 if args:
293                         if len(args) == 3:
294                                 subject, body, attachments = args
295                         elif len(args) == 2:
296                                 # No attachments given
297                                 subject, body = args
298                         else:
299                                 # Only header returned
300                                 subject = args
301                         
302                         if subject:
303                                 # Push notification
304                                 self.push(controller, subject, body, attachments)
305
306         def runerrback(self, controller, *args):
307                 print _("controller %s returned error(s)") % controller.getName()
308                 for arg in args:
309                         if isinstance(arg, Exception):
310                                 print str(arg.type), str(arg.value)
311                         elif arg:
312                                 print str(arg)
313
314         def push(self, controller, subject, text="", attachments=[]):
315                 print "push"
316                 services = self.services
317                 if not services:
318                         # Fallback to PopUp
319                         module = self.controllermodules.get("PopUp", None)
320                         popup = self.instantiateModule(module)
321                         # Missing but not necessary: popup.begin() -> popup.push(...) -> popup.end()
322                         services = [popup]
323                 if services:
324                         for service in services:
325                                 if service.getEnable():
326                                         try:
327                                                 service.push(
328                                                                 boundFunction(self.pushcallback, service, controller),
329                                                                 boundFunction(self.pusherrback, service, controller),
330                                                                 controller.getName(),
331                                                                 subject, text, attachments )
332                                         except Exception, e:
333                                                 print _("PushService Service push() exception")
334                                                 exc_type, exc_value, exc_traceback = sys.exc_info()
335                                                 traceback.print_exception(exc_type, exc_value, exc_traceback, file=sys.stdout)
336
337         def pushcallback(self, service, controller, *args):
338                 print "pushcallback"
339                 key = (service, controller)
340                 if key not in self.pushcallbacks:
341                         self.pushcallbacks[key] = list(args)
342                 else:
343                         self.pushcallbacks[key].extend(list(args))
344                 self.pushcheckbacks(key)
345
346         def pusherrback(self, service, controller, *args):
347                 print "pusherrback"
348                 print _("Service %s returned error(s)") % service.getName()
349                 for arg in args:
350                         if isinstance(arg, Exception):
351                                 print str(arg.type), str(arg.value)
352                         elif arg:
353                                 print str(arg)
354                 key = (service, controller)
355                 if key not in self.pusherrbacks:
356                         self.pusherrbacks[key] = list(args)
357                 else:
358                         self.pusherrbacks[key].extend(list(args))
359                 self.pushcheckbacks(key)
360
361         def pushcheckbacks(self, key):
362                 print "pushcheckbacks"
363                 callparam = self.pushcallbacks.get(key, [])
364                 cntcall = len(callparam)
365                 errparam = self.pusherrbacks.get(key, [])
366                 cnterr = len(errparam)
367                 cntservices = len( [ service for service in self.services if service.getEnable() ] )
368                 
369                 # Check if all services already called and returned
370                 if ( cntservices == (cntcall + cnterr) ):
371                         service, controller = key
372                         if controller:
373                                 # Check if no error is logged
374                                 if ( cnterr == 0 ):
375                                         print "controller.callback()"
376                                         controller.callback()
377                                 else:
378                                         controller.errback()
379                                         print "controller.errback()"