source: lliurex-store/trunk/fuentes/python3-lliurex-store.install/usr/share/lliurexstore/storeManager.py @ 7435

Last change on this file since 7435 was 7435, checked in by Juanma, 22 months ago

fix typo

File size: 23.2 KB
Line 
1#!/usr/bin/python3
2import sys
3import os
4import threading
5import syslog
6import pkgutil
7import lliurexstore.plugins as plugins
8import json
9######
10#Ver. 1.0 of storeManager.py
11# This class manages the store and the related plugins
12# It's implemented as an action-drived class.
13# There're four(five) main actions and each of them could execute and undeterminated number of subprocess in their respective thread
14# Each of these actions returns EVER a list of dictionaries.
15#####
16
17class StoreManager():
18        def __init__(self,*args,**kwargs):
19                self.dbg=False
20                if 'dbg' in kwargs.keys():
21                        self.dbg=kwargs['dbg']
22                self._propagate_dbg=False
23                self.store=None
24                self.stores={}
25                self.related_actions={
26                                        'load':['load'],
27                                        'search':['search','get_info','pkginfo'],
28                                        'list':['list','get_info','pkginfo'],
29                                        'info':['list','get_info','pkginfo'],
30                                        'list_sections':['list_sections'],
31                                        'install':['search','get_info','pkginfo','install'],
32                                        'remove':['search','get_info','pkginfo','remove']
33                                        }
34                self.cli_mode=[]                        #List that controls cli_mode for plugins
35                self.threads={}                         #Dict with the functions that must execute each action
36                self.threads_progress={}                        #"" "" "" the progress for each launched thread
37                self.running_threads={}                 #"" "" "" the running threads
38                self.plugins_registered={}              #Dict with the relation between plugins and actions
39                self.register_action_progress={}                #Dict with the progress for each function/parent_action pair
40                self.action_progress={}                 #Progress of global actions based on average progress of individual processes
41                self.extra_actions={}           #Dict with the actions managed by plugins and no defined on the main class as related_actions
42                self.result={}                          #Result of the actions
43                self.lock=threading.Lock()              #locker for functions related to threads (get_progress, is_action_running...)
44                self.main(**kwargs)
45        #def __init__
46
47        def main(self,**kwargs):
48                self._define_functions_for_threads()    #Function that loads the dictionary self.threads
49                self.__init_plugins__(**kwargs)                 #Function that loads the plugins
50                self.execute_action('load')             #Initial load of the store
51        #def main
52
53        ####
54        #Load and register the plugins from plugin dir
55        ####
56        def __init_plugins__(self,**kwargs):
57                package=plugins
58                for importer, mod, ispkg in pkgutil.walk_packages(path=package.__path__, prefix=package.__name__+'.',onerror=lambda x: None):
59                        import_mod='from %s import *'%mod
60                        try:
61                                self._debug("Importing %s"%mod)
62                                exec (import_mod)
63                        except Exception as e:
64                                print("Import failed for %s"%mod)
65                                print("Reason; %s"%e)
66
67                for mod in (sys.modules.keys()):
68                        if 'plugins.' in mod:
69                                class_actions={}
70                                plugin_name_up=mod.split('.')[-1]
71                                plugin_name=plugin_name_up.lower()
72                                self._debug("Initializing %s"%plugin_name)
73                                sw_cli_mode=False
74                                try:
75                                        target_class=eval(plugin_name)()
76                                        class_actions=target_class.register()
77                                        if 'disabled' in target_class.__dict__.keys():
78                                                if target_class.disabled==True:
79                                                        self._debug("Disabling plugin %s"%plugin_name)
80                                                        continue
81                                                if target_class.disabled==None:
82                                                        self._debug("Plugin %s will set its status"%plugin_name)
83                                                else:
84                                                        #Time to check if plugin is disabled or enabled by parm
85                                                        #Values for the plugins_registered dict must be the same as the parm name that enables the plugin
86                                                        for key,value in class_actions.items():
87                                                                val=value
88                                                                break
89                                                        if val in kwargs.keys():
90                                                                if kwargs[val]==True:
91                                                                        if target_class.disabled:
92                                                                                self._debug("Disabling plugin %s"%plugin_name)
93                                                                                continue
94                                                                else:
95                                                                        self._debug("Disabling plugin %s"%plugin_name)
96                                                                        continue
97                                                        else:
98                                                                self._debug("Disabling plugin %s"%plugin_name)
99                                                                continue
100                                        if 'cli_mode' in target_class.__dict__.keys():
101                                                if 'cli' in kwargs.keys():
102                                                        sw_cli_mode=True
103                                                        self._debug("Enabling cli mode for %s"%plugin_name)
104                                except Exception as e:
105                                        print ("Can't initialize %s %s"%(mod,target_class))
106                                        print ("Reason: %s"%e)
107                                        pass
108                       
109                                for action in class_actions.keys():
110                                        if action not in self.plugins_registered:
111                                                self.plugins_registered[action]={}
112                                        full_module_name='plugins.'+plugin_name_up+'.'+plugin_name
113                                        self.plugins_registered[action].update({class_actions[action]:full_module_name})
114                                        if sw_cli_mode:
115                                                self.cli_mode.append(full_module_name)
116
117                self._debug(str(self.plugins_registered))
118        #def __init_plugins__
119
120        def set_debug(self,dbg=True):
121                self.dbg=dbg
122                self._debug ("Debug enabled")
123        #def set_debug
124
125        def _debug(self,msg=''):
126                if self.dbg==1:
127                        print ('DEBUG Store: %s'%msg)
128        #def _debug
129
130        def _log(self,msg=None):
131                if msg:
132                        syslog.openlog('lliurex-store')
133                        syslog.syslog(msg)
134                        self._debug(msg)
135        ####
136        #dict of actions/related functions for threading
137        ####
138        def _define_functions_for_threads(self):
139                self.threads['load']="threading.Thread(target=self._load_Store)"
140                self.threads['get_info']="threading.Thread(target=self._get_App_Info,args=args,kwargs=kwargs)"
141                self.threads['pkginfo']="threading.Thread(target=self._get_Extended_App_Info,args=args,kwargs=kwargs)"
142                self.threads['search']='threading.Thread(target=self._search_Store,args=args,kwargs=kwargs)'
143                self.threads['list']='threading.Thread(target=self._search_Store,args=args,kwargs=kwargs)'
144                self.threads['info']='threading.Thread(target=self._search_Store,args=args,kwargs=kwargs)'
145                self.threads['install']='threading.Thread(target=self._install_remove_App,args=args,kwargs=kwargs)'
146                self.threads['remove']='threading.Thread(target=self._install_remove_App,args=args,kwargs=kwargs)'
147                self.threads['list_sections']='threading.Thread(target=self._list_sections,args=args,kwargs=kwargs)'
148        #def _define_functions_for_threads
149
150        ####
151        #Launch the appropiate threaded function for the desired action
152        #Input:
153        #  - action to be executed
154        #  - parms for the action
155        ####
156        def execute_action(self,action,*args,**kwargs):
157                #Action must be a kwarg but for retrocompatibility reasons we keep it as an arg
158                kwargs.update({"action":action})
159                self._debug("Launching action: %s with args %s and kwargs %s"%(action,args,kwargs))
160                if self.is_action_running('load'):
161                        self._join_action('load')
162                        self._debug("Resumed action %s"%action)
163                sw_track_status=False
164                if action not in self.threads.keys():
165                        #Attempt to add a new action managed by a plugin
166                        self._debug("Attempting to find a plugin for action %s"%action)
167                        if action in self.plugins_registered.keys():
168                                for package_type,plugin in self.plugins_registered[action].items():
169                                        self.action_progress[action]=0
170                                        self.threads[action]='threading.Thread(target=self._execute_class_method(action,package_type,action).execute_action,args=[action],kwargs={kwargs})'
171                                        break
172                                self._debug('Plugin for %s found: %s'%(action,self.plugins_registered[action]))
173                                self.related_actions.update({action:[action]})
174                                sw_track_status=True
175                if action in self.threads.keys():
176                        if self.is_action_running(action):
177                                #join thread if we're performing the same action
178                                self._debug("Waiting for current action %s to end"%s)
179                                self.running_threads[action].join()
180                        try:
181                                if action in self.action_progress.keys():
182                                        self.action_progress[action]=0
183                                self.action_progress[action]=0
184                                self.result[action]={}
185                                self.running_threads[action]=eval(self.threads[action])
186                                self.running_threads[action].start()
187                                if sw_track_status:
188                                        self.result[action]['status']={'status':0,'msg':''}
189                                else:
190                                        self.result[action]['status']={'status':-1,'msg':''}
191                                self._debug("Thread %s for action %s launched"%(self.running_threads[action],action))
192
193                        except Exception as e:
194                                self._debug("Can't launch thread for action: %s"%action)
195                                self._debug("Reason: %s"%e)
196                                pass
197                else:
198                        self._debug("No function associated with action %s"%action)
199        #def execute_action
200
201        ####
202        #Launch the appropiate class function
203        #Input:
204        #  - class action to be executed
205        #  - parms for the action
206        #  - parent action that demands the execution
207        #Output
208        #  - The class method to execute
209        ####
210        def _execute_class_method(self,action,package_type,*args,launchedby=None,**kwargs):
211                exe_function=None
212                if not package_type:
213                        package_type="*"
214                if action in self.plugins_registered:
215                        self._debug("Plugin for %s: %s"%(action,self.plugins_registered[action][package_type]))
216                        exe_function=eval(self.plugins_registered[action][package_type]+"("+','.join(args)+")")
217                        if self._propagate_dbg:
218                                exe_function.set_debug()
219                        if self.plugins_registered[action][package_type] in self.cli_mode:
220                                exe_function.cli_mode=True
221                        self._register_action_progress(action,exe_function,launchedby)
222                else:
223                        self._debug("No plugin for action: %s"%action)
224                if kwargs:
225                        self._debug("Parms: %s"%kwargs)
226                return (exe_function)
227        #def _execute_class_method
228
229        ###
230        #Tell if a a action is running
231        #Input:
232        #  - action to monitorize
233        #Output:
234        #  - status true/false
235        ###
236        def is_action_running(self,searched_action=None):
237                status=False
238                action_list=[]
239                if searched_action:
240                        action_list.append(searched_action)
241                else:
242                        action_list=self.related_actions.keys()
243
244                for action in action_list:
245                        if action in self.running_threads.keys():
246                                if self.running_threads[action].is_alive():
247                                        status=True
248                                        break
249                                else:
250                                        if action in self.related_actions.keys():
251                                                for related_action in self.related_actions[action]:
252                                                        if related_action in self.running_threads.keys():
253                                                                if self.running_threads[related_action].is_alive():
254                                                                        status=True
255                                                                        break
256                return(status)
257        #def is_action_running
258
259        ####
260        #Joins an action till finish
261        #Input:
262        #  - action to join
263        ####
264        def _join_action(self,action):
265                self._debug("Joining action: %s"%action)
266                try:
267                        print("T: %s"%self.running_threads[action])
268                        self.running_threads[action].join()
269                except Exception as e:
270                        self._debug("Unable to join thread for: %s"%action)
271                        self._debug("Reason: %s"%e)
272                finally:               
273                        if action in self.running_threads.keys():
274                                del(self.running_threads[action])
275        #def _join_action
276
277        ####
278        #Register the method and action/parent_action pair in the progress dict
279        #Input:
280        #  - action launched
281        #  - function (a reference to the function)
282        #  - parent_action that owns the action (if any)
283        ####
284        def _register_action_progress(self,action,function,parent_action=None):
285                if action in self.register_action_progress.keys():
286                        self._debug("Appended process for action: %s and function: %s"%(action,function))
287                        self.register_action_progress[action].append(function)
288                else:
289                        self._debug("Registered process for action: %s and function %s"%(action,function))
290                        self.register_action_progress[action]=[function]
291                if parent_action:
292                        self._debug("Registered process for Parent Action: %s-%s and function: %s"%(action,parent_action,function))
293                        if parent_action in self.threads_progress.keys():
294                                self.threads_progress[parent_action].update({action:function})
295                        else:
296                                self.threads_progress[parent_action]={action:function}
297        #def _register_action_progress
298
299        ####
300        #Get the progress of the executed actions
301        #Input
302        #  - action or none if we want all of the progress
303        #Output:
304        #  - Dict of results indexed by actions
305        ####
306        def get_progress(self,action=None):
307                progress={'search':0,'list':0,'install':0,'remove':0,'load':0,'list_sections':0}
308                action_list=[]
309                if action in self.register_action_progress.keys():
310                        action_list=[action]
311                else:
312                        action_list=self.register_action_progress.keys()
313                self.lock.acquire() #prevent that any thread attempts to change the iterator
314                for parent_action in self.related_actions.keys():
315                        if self.is_action_running(parent_action):
316                                for action in action_list:
317                                        if parent_action in self.threads_progress.keys():
318                                                acum_progress=0
319                                                for threadfunction,function in self.threads_progress[parent_action].items():
320                                                        acum_progress=acum_progress+function.progress
321       
322                                                count=len(self.related_actions[parent_action])
323                                                self.action_progress[parent_action]=round(acum_progress/count,0)
324                                                progress[parent_action]=self.action_progress[parent_action]
325                        else:
326                                #put a 100% just in case
327                                if parent_action in self.action_progress.keys():
328                                        self.action_progress[parent_action]=100
329                self.lock.release()
330                return(self.action_progress)
331        #def get_progress
332
333        ####
334        #Gets the result of an action
335        #Input:
336        #  - action
337        #Output:
338        #  - Dict of results indexed by actions
339        ####
340        def get_result(self,action=None):
341                self.lock.acquire() #Prevent changes on results from threads
342                result={}
343                if action==None:
344                        for res in self.result.keys():
345                                if res!='load':
346                                        if 'data' in self.result[res]:
347                                                result[res]=self.result[res]['data']
348                                        else:
349                                                result[res]=[]
350                else:
351                        self._debug("Checking result for action %s"%action)
352                        if self.is_action_running(action):
353                                self._join_action(action)
354                        result[action]=None
355                        if action in self.result:
356                                if 'data' in self.result[action]:
357                                        result[action]=self.result[action]['data']
358                                else:
359                                        result[action]=[]
360                self.lock.release()
361                if action in self.extra_actions.keys():
362                        self._load_Store()
363                return(result)
364        #def get_result
365
366        ####
367        #Gets the status of an action
368        #Input.
369        # - action
370        #Output:
371        # - Status dict of the action
372        ####
373        def get_status(self,action=None):
374                self.lock.acquire()
375                self._debug("Checking status for action %s"%action)
376                result={}
377                if action in self.result:
378                        result=self.result[action]['status']
379                        try:
380                                err_file=open('/usr/share/lliurex-store/files/error.json').read()
381                                err_codes=json.loads(err_file)
382                                err_code=str(result['status'])
383                                if err_code in err_codes:
384                                        result['msg']=err_codes[err_code]
385                                else:
386                                        result['msg']=u"Unknown error"
387                        except:
388                                        result['msg']=u"Unknown error"
389                self.lock.release()
390                return(result)
391        #def get_status
392
393        ####
394        #Loads the store
395        ####
396        def _load_Store(self):
397                action='load'
398                #Load appstream metada first
399                package_type='*'
400                load_function=self._execute_class_method(action,package_type,launchedby=None)
401                self.store=load_function.execute_action(action=action,store=self.store)['data']
402                #Once appstream is loaded load the appstream plugins for other package types (snap, appimage...)
403                for package_type in self.plugins_registered[action]:
404                        if package_type!='*':
405                                load_function=self._execute_class_method(action,package_type,launchedby=None)
406                                self.store=load_function.execute_action(action=action,store=self.store)['data']
407        #def _load_Store
408
409        ####
410        #Loads the info related to one app
411        #Input:
412        #  - List of App objects
413        #Output:
414        #  - Dict with the related info
415        ####
416        def _get_App_Info(self,applist,launchedby=None):
417                action='get_info'
418                info_function=self._execute_class_method(action,None,launchedby=launchedby)
419                info_applist=info_function.execute_action(self.store,action,applist)
420                return(info_applist)
421        #def _get_App_Info
422
423        ####
424        #Loads the extended info related to one app (slower)
425        #Input:
426        #  - Dict off Apps (as returned by _get_app_info)
427        #Output:
428        #  - Dict with the related info
429        ####
430        def _get_Extended_App_Info(self,info_applist,launchedby=None,fullsearch=True,channel=''):
431                #Check if there's any plugin for the distinct type of packages
432                action='pkginfo'
433                types_dict={}
434                result={}
435                result['data']=[]
436                result['status']={'status':0,'msg':''}
437                processed=[]
438                for app_info in info_applist:
439                        if channel:
440                                types_dict[channel]=[app_info]
441                        else:
442                                available_channels=self._check_package_type(app_info)
443                                for package_type in available_channels:
444                                        if app_info['component']!='':
445                                                if app_info['id'] in processed:
446                                                        self._debug("App %s processed"%app_info['id'])
447                                                        continue
448
449                                        if package_type in types_dict:
450                                                types_dict[package_type].append(app_info)
451                                        else:
452                                                types_dict[package_type]=[app_info]
453                                        processed.append(app_info['id'])
454                for package_type in types_dict:
455                        self._debug("Checking plugin for %s %s"%(action,package_type))
456                        if package_type in self.plugins_registered[action]:
457                                #Only seach full info if it's required
458                                if (fullsearch==False and package_type=='deb'):
459                                        result['data'].extend(types_dict[package_type])
460                                        continue
461                                self._debug("Retrieving info for %s"%types_dict[package_type])
462                                info_function=self._execute_class_method(action,package_type,launchedby=launchedby)
463                                result['data'].extend(info_function.execute_action(action,types_dict[package_type])['data'])
464                        else:
465                                result['data'].append(app_info)
466                return(result)
467        #def _get_Extended_App_Info
468
469        def _list_sections(self,searchItem='',action='list_sections',launchedby=None):
470                result={}
471                self._debug("Retrieving all sections")
472                data={}
473                status={}
474                if action in self.plugins_registered.keys():
475                        self._debug("Plugin for generic search: %s"%self.plugins_registered[action]['*'])
476                        finder=self.plugins_registered[action][('*')]
477                        search_function=eval(finder+"()")
478                        result=search_function.execute_action(self.store,action,searchItem)
479                        status=result['status']
480                        data=result['data']
481                else:
482                        print("No plugin for action %s"%action)
483                self.result[action]['data']=data
484                self.result[action]['status']=status
485                self._debug("Sections: %s"%self.result[action]['data'])
486                self._debug("Status: %s"%self.result[action]['status'])
487
488        ####
489        #Search the store
490        #Input:
491        #  - string search
492        #Output:
493        #  - List of dicts with all the info
494        ####
495        def _search_Store(self,*args,**kwargs):
496                search_item=args[0]
497                return_msg=False
498                action='search'
499                if 'action' in kwargs.keys():
500                        action=kwargs['action']
501                launchedby=None
502                if 'launchedby' in kwargs.keys():
503                        launchedby=kwargs['launchedby']
504                max_results=0
505                if 'max_results' in kwargs.keys():
506                        max_results=kwargs['max_results'] 
507                fullsearch=False
508                if 'fullsearch' in kwargs.keys():
509                        fullsearch=kwargs['fullsearch']
510                result={}
511                tmp_applist=[]
512                if action=='list_sections':
513                        search_item=''
514                elif action=='info':
515                        fullsearch=True
516                if not launchedby:
517                        launchedby=action
518                #Set the exact match to false for search method
519                exact_match=True
520                if (launchedby=='search'):
521                                exact_match=False
522                target_channel=''
523                if '=' in search_item:
524                        target_channel=search_item.split('=')[-1]
525                        search_item=search_item.split('=')[0]
526                for package_type in self.plugins_registered[action]:
527                        self._debug("Searching package type %s"%package_type)
528                        search_function=self._execute_class_method(action,'*',launchedby=launchedby)
529                        result.update(search_function.execute_action(self.store,action,search_item,exact_match,max_results))
530                tmp_applist=result['data']
531                status=result['status']
532                realAction=action
533                if status['status']==0:
534                        #1.- Get appstream metadata (faster)
535                        subordinate_action='get_info'
536                        self.result[subordinate_action]={}
537                        result=self._get_App_Info(tmp_applist,launchedby)
538                        self._debug("Add result for %s"%subordinate_action)
539                        self.result[subordinate_action]=result
540                        if fullsearch:
541                                #2.- Get rest of metadata (slower)
542                                subordinate_action='pkginfo'
543                                self._debug("Target channel: %s"%target_channel)
544                                result=self._get_Extended_App_Info(result['data'],launchedby,fullsearch,target_channel)
545                                if launchedby:
546                                        realAction=launchedby
547                                        self._debug("Assigned results of %s to %s"%(action,realAction))
548                                if (result['status']['status']==0) or (result['status']['status']==9):
549                                        return_msg=True
550                                        if fullsearch:
551                                                result['status']['status']=0
552                                else:
553                                        return_msg=False
554                else:
555                        return_msg=False
556                self.result[launchedby]['data']=result['data']
557                self.result[launchedby]['status']=result['status']
558                return(return_msg)
559        #def _search_Store
560
561        ####
562        #Install or remove an app
563        #Input:
564        #  - String with the app name
565        #Output:
566        #  - Result of the operation
567        ####
568        def _install_remove_App(self,*args,**kwargs):
569                appName=args[0]
570                if 'action' in kwargs.keys():
571                        action=kwargs['action']
572                self._log("Attempting to %s %s"%(action,appName))
573                result={}
574                return_msg=False
575                if (self._search_Store(appName,action='search',fullsearch=True,launchedby=action)):
576                        info_applist=self.result[action]['data']
577                        types_dict={}
578                        #Check if package is installed if we want to remove it or vice versa
579                        for app_info in info_applist:
580                        #Appstream doesn't get the right status in all cases so we rely on the mechanisms given by the different plugins.
581                                if (action=='install' and app_info['state']=='installed') or (action=='remove' and app_info['state']=='available'):
582                                        if (action=='remove' and app_info['state']=='available'):
583                                                        self.result[action]['status']={app_info['package']:3}
584                                                        self.result[action]['status']={'status':3}
585                                        else:
586                                                self.result[action]['status']={app_info['package']:4}
587                                                self.result[action]['status']={'status':4}
588                                                pass
589                                        return_msg=False
590                                        types_dict={}
591                                        break
592                                processed=[]
593                                available_channels=self._check_package_type(app_info)
594                                for package_type in available_channels:
595                                        if app_info['component']!='':
596                                                if app_info['id'] in processed:
597                                                        self._debug("App %s processed"%app_info['id'])
598                                                        continue
599
600                                        if package_type in types_dict:
601                                                types_dict[package_type].append(app_info)
602                                        else:
603                                                types_dict[package_type]=[app_info]
604                                        processed.append(app_info['id'])
605
606                        for package_type in types_dict:
607                                self._debug("Checking plugin for %s %s"%(action,package_type))
608                                if package_type in self.plugins_registered[action]:
609                                        install_function=self._execute_class_method(action,package_type,launchedby=action)
610                                        if package_type=='zmd':
611                                        #If it's a zmd the zomando must be present in the system
612                                                zmd_info=[]
613                                                for zmd_bundle in types_dict[package_type]:
614                                                        zmdInfo={}
615                                                        self._debug("Cheking presence of zmd %s"%zmd_bundle['package'])
616                                                        zmd='/usr/share/zero-center/zmds/'+app_info['package']+'.zmd'
617                                                        if not os.path.exists(zmd):
618                                                                zmdInfo['package']=zmd_bundle['package']
619                                                                zmd_info.append(zmdInfo)
620                                                if zmd_info:
621                                                        self._debug("Installing needed packages")
622                                                        install_depends_function=self._execute_class_method(action,"deb",launchedby=action)
623                                                        result=install_depends_function.execute_action(action,zmd_info)
624                                                       
625                                        result=install_function.execute_action(action,types_dict[package_type])
626                                        self.result[action]=result
627                                        #Deprecated. Earlier versions stored the "app" object so here the app could get marked as installed/removed without neeed of import or query anything
628                                        #Python>=3.6 don't let us to store the app object in a queue (needed for the GUI) so this code becames deprecated.
629#                                       if result['status']['status']==0:
630                                                #Mark the apps as installed or available
631#                                               for app in types_dict[package_type]:
632#                                                       if action=='install':
633#                                                               app['appstream_id'].set_state(1)
634#                                                               self._debug("App state changed to installed")
635#                                                       else:
636#                                                               app['appstream_id'].set_state(2)
637#                                                               self._debug("App state changed to available")
638                                        return_msg=True
639                self._log("Result %s: %s"%(action,self.result[action]))
640                return(return_msg)
641        #def install_App
642       
643        ####
644        #Check the package type
645        #Input:
646        # - AppInfo dict (element of the list returned by _get_app_info)
647        #Output:
648        # - String with the type (deb, sh, zmd...)
649        ####
650        def _check_package_type(self,app_info):
651                #Standalone installers must have the subcategory "installer"
652                #Zomandos must have the subcategory "Zomando"
653                self._debug("Checking package type for app "+app_info['name'])
654                return_msg=[]
655                if app_info['bundle']:
656                        return_msg.extend(app_info['bundle'])
657                else:
658                        if "Zomando" in app_info['categories']:
659                                return_msg.append("zmd")
660                        if 'component' in app_info.keys():
661                                if app_info['component']!='':
662                                        return_msg.append('deb')
663                #Standalone installers must have an installerUrl field loaded from a bundle type=script description
664                        if app_info['installerUrl']!='':
665                                return_msg.append("sh")
666                return(return_msg)
667        #def _check_package_type
Note: See TracBrowser for help on using the repository browser.