PushService: Fixed load error
[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 PLUGIN = "Plugin"
44 OPTION = "Option"
45
46 SERVICES_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.plugins = []
58                 
59                 self.pushcallbacks = {}
60                 self.pusherrbacks = {}
61                 
62                 # Read module files from subfolders
63                 self.servicemodules = self.loadModules(SERVICES_PATH, ServiceBase)
64                 self.pluginmodules = 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 plugin 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 getPlugins(self):
110                 return self.plugins or []
111
112         def getPlugin(self, idx):
113                 if idx < len(self.plugins):
114                         return self.plugins[idx]
115                 else:
116                         return None
117
118         def getAvlPlugins(self):
119                 plist = []
120                 if self.pluginmodules:
121                         pluginclasses = [ plugin.getClass() for plugin in self.plugins] if self.plugins else []
122                         for name, module in self.pluginmodules.iteritems():
123                                 if module.forceSingle():
124                                         # We have to check if there is already a plugin instance
125                                         if name in pluginclasses:
126                                                 # A plugin instance already exists
127                                                 continue
128                                 plist.append( (name, module) )
129                         plist.sort()
130                 return plist
131
132         def getPluginInstances(self):
133                 return [( plugin.getNameId(), plugin ) for plugin in self.getPlugins() ]
134
135         def addPlugin(self, module):
136                 id = None
137                 plugin = module and self.instantiateModule( module )
138                 if plugin:
139                         plugin.setEnable(True)
140                         self.plugins.append( plugin )
141                         self.plugins.sort( key=lambda x: ( x.getUniqueID() ) )
142                         id = plugin.getUniqueID()
143                 return id
144
145         def removePlugin(self, plugin):
146                 if plugin in self.plugins:
147                         self.plugins.remove( plugin )
148
149
150         ######################################
151         # Config
152         def copyto(self, destination):
153                 destination.services = self.services
154                 destination.plugins = self.plugins
155                 destination.servicemodules = self.servicemodules
156                 destination.pluginmodules = self.pluginmodules
157
158         def copyfrom(self, source):
159                 self.services = source.services
160                 self.plugins = source.plugins
161                 self.servicemodules = source.servicemodules
162                 self.pluginmodules = source.pluginmodules
163
164         def load(self):
165                 # Read xml config file
166                 root = self.readXML()
167                 if root:
168                         services = []
169                         plugins = []
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 plugin list
199                                                                 instances.append(instance)
200                                 return instances
201                         services = parse( root, SERVICE, self.servicemodules )
202                         plugins = parse( root, PLUGIN, self.pluginmodules )
203                         
204                         self.services = services
205                         self.plugins = plugins
206                 else:
207                         self.services = []
208                         self.plugins = []
209
210         def save(self):
211                 # Generate List in RAM
212                 root = None
213                 services = self.services
214                 plugins = self.plugins
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 plugin
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 plugins:
237                         root = build( root, plugins, PLUGIN)
238                 
239                 self.writeXML( root )
240
241
242         ######################################
243         # Plugin 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 Plugins
250                 for plugin in self.getPlugins():
251                         if plugin.getEnable():
252                                 plugin.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 Plugins
260                 for plugin in self.getPlugins():
261                         if plugin.getEnable():
262                                 plugin.end()
263
264         def run(self):
265                 print _("PushService started: ") + strftime( _("%d.%m.%Y %H:%M"), localtime() )
266                 
267                 plugins = self.plugins
268                 self.pushcallbacks = {}
269                 self.pusherrbacks = {}
270                 
271                 # Loop over all Plugins
272                 if plugins:
273                         for plugin in plugins:
274                                 if plugin.getEnable():
275                                         print _("PushService running: ") + str( plugin.getName() )
276                                         
277                                         try:
278                                                 # Run plugin
279                                                 ret = plugin.run(
280                                                                 boundFunction(self.runcallback, plugin),
281                                                                 boundFunction(self.runcallback, plugin) )
282                                         except Exception, e:
283                                                 print _("PushService Plugin 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, plugin, *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(plugin, subject, body, attachments)
305
306         def runerrback(self, plugin, *args):
307                 print _("Plugin %s returned error(s)") % plugin.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, plugin, subject, text="", attachments=[]):
315                 services = self.services
316                 if not services:
317                         # Fallback to PopUp
318                         module = self.pluginmodules.get("PopUp", None)
319                         popup = self.instantiateModule(module)
320                         # Missing but not necessary: popup.begin() -> popup.push(...) -> popup.end()
321                         services = [popup]
322                 if services:
323                         for service in services:
324                                 try:
325                                         service.push(
326                                                         boundFunction(self.pushcallback, service, plugin),
327                                                         boundFunction(self.pusherrback, service, plugin),
328                                                         plugin.getName(),
329                                                         subject, text, attachments )
330                                 except Exception, e:
331                                         print _("PushService Service push() exception")
332                                         exc_type, exc_value, exc_traceback = sys.exc_info()
333                                         traceback.print_exception(exc_type, exc_value, exc_traceback, file=sys.stdout)
334
335         def pushcallback(self, service, plugin, *args):
336                 key = (service, plugin)
337                 if key not in self.pushcallbacks:
338                         self.pushcallbacks[key] = list(args)
339                 else:
340                         self.pushcallbacks[key].extend(list(args))
341                 self.pushcheckbacks(key)
342
343         def pusherrback(self, service, plugin, *args):
344                 print _("Service %s returned error(s)") % service.getName()
345                 for arg in args:
346                         if isinstance(arg, Exception):
347                                 print str(arg.type), str(arg.value)
348                         elif arg:
349                                 print str(arg)
350                 key = (service, plugin)
351                 if key not in self.pusherrbacks:
352                         self.pusherrbacks[key] = list(args)
353                 else:
354                         self.pusherrbacks[key].extend(list(args))
355                 self.pushcheckbacks(key)
356
357         def pushcheckbacks(self, key):
358                 callparam = self.pushcallbacks.get(key, [])
359                 cntcall = len(callparam)
360                 errparam = self.pusherrbacks.get(key, [])
361                 cnterr = len(errparam)
362                 
363                 # Check if all services already called and returned
364                 if ( len(self.services) == (cntcall + cnterr) ):
365                         service, plugin = key
366                         if plugin:
367                                 # Check if no error is logged
368                                 if ( cnterr == 0 ):
369                                         plugin.callback()
370                                 else:
371                                         plugin.errback()