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

Last change on this file was 8703, checked in by Juanma, 7 months ago

sync with git

File size: 30.0 KB
Line 
1#!/usr/bin/python3
2import sys
3import os
4import threading
5import syslog
6import pkgutil
7import lliurexstore.plugins as plugins
8import json
9import random
10import time
11from queue import Queue as pool
12######
13#Ver. 1.0 of storeManager.py
14# This class manages the store and the related plugins
15# It's implemented as an action-drived class.
16# There're four(five) main actions and each of them could execute and undeterminated number of subprocess in their respective thread
17# Each of these actions returns EVER a list of dictionaries.
18#####
19
20class StoreManager():
21        def __init__(self,*args,**kwargs):
22                self.dbg=False
23                if 'dbg' in kwargs.keys() and self.dbg==False:
24                        self.dbg=kwargs['dbg']
25                self.autostart=True
26                if 'autostart' in kwargs.keys():
27                        self.autostart=kwargs['autostart']
28                        self._debug("Autostart actions: %s"%self.autostart)
29                self._propagate_dbg=False
30                self.store=None
31                self.cache="%s/.cache/lliurex-store"%os.environ['HOME']
32                self.cache_data="%s/data"%self.cache
33                if not os.path.isdir(self.cache_data):
34                        os.makedirs(self.cache_data)
35                self.cache_completion="%s/bash_completion"%self.cache_data
36                self.related_actions={
37                                        'load':['load'],
38                                        'search':['search','get_info','pkginfo'],
39                                        'list':['list','get_info','pkginfo'],
40                                        'info':['list','get_info','pkginfo'],
41                                        'list_sections':['list_sections'],
42                                        'install':['search','get_info','pkginfo','install'],
43                                        'remove':['search','get_info','pkginfo','remove']
44                                        }
45                self.cli_mode=[]                        #List that controls cli_mode for plugins
46                self.autostart_actions=[]       #List with actions marked as autostart by plugins
47                self.postaction_actions=[]      #List with actions that will be launched after other actions
48                self.required_parms={}
49                self.threads={}                         #Dict with the functions that must execute each action
50                self.static={}                          #Dict with the functions that must execute each action
51                self.threads_progress={}                        #"" "" "" the progress for each launched thread
52                self.running_threads={}                 #"" "" "" the running threads
53                self.plugins_registered={}              #Dict with the relation between plugins and actions
54                self.register_action_progress={}                #Dict with the progress for each function/parent_action pair
55                self.action_progress={}                 #Progress of global actions based on average progress of individual processes
56                self.extra_actions={}           #Dict with the actions managed by plugins and no defined on the main class as related_actions
57                self.result={}                          #Result of the actions
58                self.lock=threading.Lock()              #locker for functions related to threads (get_progress, is_action_running...)
59                self.main(**kwargs)
60        #def __init__
61
62        def main(self,**kwargs):
63                self._define_functions_for_threads()    #Function that loads the dictionary self.threads
64                self.__init_plugins__(**kwargs)                 #Function that loads the plugins
65                self.execute_action('load')             #Initial load of the store
66                th=threading.Thread(target=self._autostart_actions)
67                th.start()
68        #def main
69
70        def _autostart_actions(self):
71                if self.autostart:
72                        for autostart_action in self.autostart_actions:
73                                self._debug("Autostart %s"%(autostart_action))
74                                self.execute_action(autostart_action)
75
76        ####
77        #Load and register the plugins from plugin dir
78        ####
79        def __init_plugins__(self,**kwargs):
80                package=plugins
81                for importer, mod, ispkg in pkgutil.walk_packages(path=package.__path__, prefix=package.__name__+'.',onerror=lambda x: None):
82                        import_mod='from %s import *'%mod
83                        try:
84                                self._debug("Importing %s"%mod)
85                                exec (import_mod)
86                        except Exception as e:
87                                print("Import failed for %s"%mod)
88                                print("Reason; %s"%e)
89
90                for mod in (sys.modules.keys()):
91                        if 'plugins.' in mod:
92                                class_actions={}
93                                plugin_name_up=mod.split('.')[-1]
94                                plugin_name=plugin_name_up.lower()
95                                self._debug("Initializing %s"%plugin_name)
96                                sw_cli_mode=False
97                                try:
98                                        target_class=eval(plugin_name)()
99                                        class_actions=target_class.register()
100                                        if 'disabled' in target_class.__dict__.keys():
101                                                if target_class.disabled==True:
102                                                        self._debug("Disabling plugin %s"%plugin_name)
103                                                        continue
104                                                if target_class.disabled==None:
105                                                        self._debug("Plugin %s will set its status"%plugin_name)
106                                                        pass
107                                                else:
108                                                        #Time to check if plugin is disabled or enabled by parm
109                                                        #Values for the plugins_registered dict must be the same as the parm name that enables the plugin
110                                                        for class_action,class_plugin_name in class_actions.items():
111                                                                class_plugin=class_plugin_name
112                                                                break
113                                                        if class_plugin in kwargs.keys():
114                                                                if kwargs[class_plugin]==True:
115                                                                        if target_class.disabled:
116                                                                                self._debug("Disabling plugin %s"%plugin_name)
117                                                                                continue
118                                                                else:
119                                                                        self._debug("Disabling plugin %s"%plugin_name)
120                                                                        continue
121                                                        else:
122                                                                self._debug("Disabling plugin %s"%plugin_name)
123                                                                continue
124                                        if 'cli_mode' in target_class.__dict__.keys():
125                                                if 'cli' in kwargs.keys():
126                                                        sw_cli_mode=True
127                                                        self._debug("Enabling cli mode for %s"%plugin_name)
128                                        if 'autostart_actions' in target_class.__dict__.keys():
129                                                self.autostart_actions.append(target_class.__dict__['autostart_actions'])
130                                        if 'requires' in target_class.__dict__.keys():
131                                                self.required_parms.update(target_class.__dict__['requires'])
132                                        if 'postaction_actions' in target_class.__dict__.keys():
133                                                self.postaction_actions.append({target_class:target_class.__dict__['postaction_actions']})
134                                except Exception as e:
135                                        print ("Can't initialize %s %s"%(mod,target_class))
136                                        print ("Reason: %s"%e)
137                                        pass
138                       
139                                for action in class_actions.keys():
140                                        if action not in self.plugins_registered:
141                                                self.plugins_registered[action]={}
142                                        full_module_name='plugins.'+plugin_name_up+'.'+plugin_name
143                                        self.plugins_registered[action].update({class_actions[action]:full_module_name})
144                                        if sw_cli_mode:
145                                                self.cli_mode.append(full_module_name)
146
147                self._debug(str(self.plugins_registered))
148        #def __init_plugins__
149
150        def set_debug(self,dbg=True):
151                self.dbg=dbg
152                self._debug ("Debug enabled")
153        #def set_debug
154
155        def _debug(self,msg=''):
156                if self.dbg==1:
157                        print ('DEBUG Store: %s'%msg)
158        #def _debug
159
160        def _log(self,msg=None):
161                if msg:
162                        syslog.openlog('lliurex-store')
163                        syslog.syslog(msg)
164                        self._debug(msg)
165        ####
166        #dict of actions/related functions for threading
167        ####
168        def _define_functions_for_threads(self):
169                self.threads['load']="threading.Thread(target=self._load_Store)"
170                self.threads['get_info']="threading.Thread(target=self._get_App_Info,daemon=True,args=args,kwargs=kwargs)"
171                self.threads['pkginfo']="threading.Thread(target=self._get_Extended_App_Info,daemon=True,args=args,kwargs=kwargs)"
172                self.threads['search']='threading.Thread(target=self._search_Store,daemon=True,args=args,kwargs=kwargs)'
173                self.threads['list']='threading.Thread(target=self._search_Store,daemon=True,args=args,kwargs=kwargs)'
174#               self.threads['list']='threading.Thread(target=self._get_editors_pick,args=args,kwargs=kwargs)'
175                self.threads['info']='threading.Thread(target=self._search_Store,daemon=True,args=args,kwargs=kwargs)'
176                self.threads['install']='threading.Thread(target=self._install_remove_App,daemon=True,args=args,kwargs=kwargs)'
177                self.threads['remove']='threading.Thread(target=self._install_remove_App,daemon=True,args=args,kwargs=kwargs)'
178                self.threads['list_sections']='threading.Thread(target=self._list_sections,daemon=True,args=args,kwargs=kwargs)'
179                self.static['random']='self._get_editors_pick(kwargs=kwargs)'
180        #def _define_functions_for_threads
181
182        ####
183        #Launch the appropiate threaded function for the desired action
184        #Input:
185        #  - action to be executed
186        #  - parms for the action
187        #Action must be a kwarg but for retrocompatibility reasons we keep it as an arg
188        ####
189        def execute_action(self,action,*args,**kwargs):
190                autostart_action=False
191                #Check for autolaunchable actions
192                if type(action)==type({}):
193                        autostart_action=True
194                        aux_action=list(action.keys())[0]
195                        kwargs.update({"action":aux_action})
196                        (key,value)=action[aux_action].split('=')
197                        kwargs.update({key:value})
198                        action=aux_action
199                else:
200                        kwargs.update({"action":action})
201                        if action in self.required_parms.keys():
202                                (key,value)=self.required_parms[action].split('=')
203                                kwargs.update({key:value})
204                self._debug("Launching action: %s with args %s and kwargs %s"%(action,args,kwargs))
205                if self.is_action_running('load'):
206                        self._join_action('load')
207                        self._debug("Total apps: %s"%str(len(self.store.get_apps())))
208                        self._debug("Resumed action %s"%action)
209                sw_track_status=False
210                if action not in self.threads.keys():
211                        #Attempt to add a new action managed by a plugin
212                        self._debug("Attempting to find a plugin for action %s"%action)
213                        if action in self.plugins_registered.keys():
214                                for package_type,plugin in self.plugins_registered[action].items():
215                                        self.action_progress[action]=0
216                                        if kwargs:
217                                                kargs={}
218                                                for arg_name in kwargs:
219                                                        try:
220                                                                kargs.update({arg_name:eval(kwargs[arg_name])})
221                                                        except:
222                                                                kargs.update({arg_name:kwargs[arg_name]})
223                                                kwargs=kargs.copy()
224                                        self.threads[action]='threading.Thread(target=self._execute_class_method(action,package_type).execute_action,daemon=True,args=[],kwargs=kwargs)'
225                                        break
226                                self._debug('Plugin for %s found: %s'%(action,self.plugins_registered[action]))
227                                if not autostart_action:
228                                        self.related_actions.update({action:[action]})
229                                sw_track_status=True
230                if action in self.threads.keys():
231                        if self.is_action_running(action):
232                                #join thread if we're performing the same action
233                                self._debug("Waiting for current action %s to end"%s)
234                                self.running_threads[action].join()
235                        try:
236                                self.action_progress[action]=0
237                                self.result[action]={}
238                                self.running_threads.update({action:eval(self.threads[action])})
239                                self.running_threads[action].start()
240                                if not self.running_threads[action].is_alive():
241                                        self._debug("Relaunching!!!!!!")
242                                        self.running_threads.update({action:eval(self.threads[action])})
243                                        self.running_threads[action].start()
244                                if sw_track_status:
245                                        self.result[action]['status']={'status':0,'msg':''}
246                                else:
247                                        self.result[action]['status']={'status':-1,'msg':''}
248                                self._debug("Thread %s for action %s launched"%(self.running_threads[action],action))
249                                self._debug("Thread count: %s"%(threading.active_count()))
250
251                        except Exception as e:
252                                self._debug("Can't launch thread for action: %s"%action)
253                                self._debug("Reason: %s"%e)
254                                pass
255                elif action in self.static.keys():
256                                self.action_progress[action]=0
257                                self.result[action].update({'data':eval(self.static[action])})
258                                self.result[action].update({'status':{'status':0,'msg':''}})
259                                self.action_progress[action]=100
260
261                else:
262                        self._debug("No function associated with action %s"%action)
263                        pass
264        #def execute_action
265
266        def _execute_class_method(self,action,package_type,*args,launchedby=None,**kwargs):
267                exe_function=None
268                if not package_type:
269                        package_type="*"
270                if action in self.plugins_registered.keys():
271                        self._debug("Plugin for %s: %s"%(action,self.plugins_registered[action][package_type]))
272                        try:
273                                self._debug(self.plugins_registered[action][package_type]+"("+','.join(args)+")")
274                                exe_function=eval(self.plugins_registered[action][package_type]+"("+','.join(args)+")")
275                        except Exception as e:
276                                self._debug("Can't launch execute_method for class %s"%e)
277                                pass
278                        if self._propagate_dbg:
279                                exe_function.set_debug(self.dbg)
280                        if self.plugins_registered[action][package_type] in self.cli_mode:
281                                exe_function.cli_mode=True
282                        self._register_action_progress(action,exe_function,launchedby)
283                else:
284                        self._debug("No plugin for action: %s"%action)
285                        pass
286                if kwargs:
287                        self._debug("Parms: %s"%kwargs)
288                        pass
289                return (exe_function)
290        #def _execute_class_method
291
292        ###
293        #Tell if a a action is running
294        #Input:
295        #  - action to monitorize
296        #Output:
297        #  - status true/false
298        ###
299        def is_action_running(self,searched_action=None):
300                status=False
301                action_list=[]
302                if searched_action:
303                        action_list.append(searched_action)
304                else:
305                        action_list=self.related_actions.keys()
306
307                for action in action_list:
308                        if action in self.static.keys():
309                                if self.action_progress[action]!=100:
310                                        status=True
311                        if action in self.running_threads.keys():
312                                if self.running_threads[action].is_alive():
313                                        status=True
314                                        break
315                                else:
316                                        if action in self.related_actions.keys():
317                                                for related_action in self.related_actions[action]:
318                                                        if related_action in self.running_threads.keys():
319                                                                if self.running_threads[related_action].is_alive():
320                                                                        status=True
321                                                                        break
322                return(status)
323        #def is_action_running
324
325        ####
326        #Joins an action till finish
327        #Input:
328        #  - action to join
329        ####
330        def _join_action(self,action):
331                self._debug("Joining action: %s"%action)
332                try:
333                        self.running_threads[action].join()
334                except Exception as e:
335                        self._debug("Unable to join thread for: %s"%action)
336                        self._debug("Reason: %s"%e)
337                        pass
338                finally:               
339                        if action in self.running_threads.keys():
340                                del(self.running_threads[action])
341        #def _join_action
342
343        ####
344        #Register the method and action/parent_action pair in the progress dict
345        #Input:
346        #  - action launched
347        #  - function (a reference to the function)
348        #  - parent_action that owns the action (if any)
349        ####
350        def _register_action_progress(self,action,function,parent_action=None):
351                if action in self.register_action_progress.keys():
352                        self._debug("Appended process for action: %s and function: %s"%(action,function))
353                        self.register_action_progress[action].append(function)
354                else:
355                        self._debug("Registered process for action: %s and function %s"%(action,function))
356                        self.register_action_progress[action]=[function]
357                if parent_action:
358                        self._debug("Registered process for Parent Action: %s-%s and function: %s"%(action,parent_action,function))
359                        if parent_action in self.threads_progress.keys():
360                                self.threads_progress[parent_action].update({action:function})
361                        else:
362                                self.threads_progress[parent_action]={action:function}
363        #def _register_action_progress
364
365        ####
366        #Get the progress of the executed actions
367        #Input
368        #  - action or none if we want all of the progress
369        #Output:
370        #  - Dict of results indexed by actions
371        ####
372        def get_progress(self,action=None):
373                progress={'search':0,'list':0,'install':0,'remove':0,'load':0,'list_sections':0}
374                action_list=[]
375                if action in self.static.keys():
376                        pass
377                else:
378                        if action in self.register_action_progress.keys():
379                                action_list=[action]
380                        else:
381                                action_list=self.register_action_progress.keys()
382                        self.lock.acquire() #prevent that any thread attempts to change the iterator
383                        for parent_action in self.related_actions.keys():
384                                if self.is_action_running(parent_action):
385                                        for action in action_list:
386                                                if parent_action in self.threads_progress.keys():
387                                                        acum_progress=0
388                                                        for threadfunction,function in self.threads_progress[parent_action].items():
389                                                                acum_progress=acum_progress+function.progress
390               
391                                                        count=len(self.related_actions[parent_action])
392                                                        self.action_progress[parent_action]=round(acum_progress/count,0)
393                                                        progress[parent_action]=self.action_progress[parent_action]
394                                else:
395                                        #put a 100% just in case
396                                        if parent_action in self.action_progress.keys():
397                                                self.action_progress[parent_action]=100
398                        self.lock.release()
399                return(self.action_progress)
400        #def get_progress
401
402        ####
403        #Gets the result of an action
404        #Input:
405        #  - action
406        #Output:
407        #  - Dict of results indexed by actions
408        ####
409        def get_result(self,action=None):
410                self.lock.acquire() #Prevent changes on results from threads
411                result={}
412                if action==None:
413                        for res in self.result.keys():
414                                if res!='load':
415                                        if 'data' in self.result[res]:
416                                                result[res]=self.result[res]['data']
417                                        else:
418                                                result[res]=[]
419                else:
420                        self._debug("Checking result for action %s"%action)
421                        if self.is_action_running(action):
422                                self._join_action(action)
423                        result[action]=[]
424                        if action in self.result:
425                                if 'data' in self.result[action]:
426                                        result[action]=self.result[action]['data']
427                                        if len(self.result[action]['data'])<1:
428                                                self._debug("ERROR NO DATA")
429                                                self._debug("ERROR NO DATA")
430                                                self._debug("ERROR NO DATA")
431                                                self._debug("ERROR NO DATA")
432                                                result[action]=[""]
433                                else:
434                                        result[action]=[""]
435                self.lock.release()
436                if action in self.extra_actions.keys():
437                        self._load_Store()
438                return(result)
439        #def get_result
440
441        ####
442        #Gets the status of an action
443        #Input.
444        # - action
445        #Output:
446        # - Status dict of the action
447        ####
448        def get_status(self,action=None):
449                self.lock.acquire()
450                self._debug("Checking status for action %s"%action)
451                result={}
452                if action in self.result:
453                        result=self.result[action]['status']
454                        try:
455                                err_file=open('/usr/share/lliurex-store/files/error.json').read()
456                                err_codes=json.loads(err_file)
457                                err_code=str(result['status'])
458                                if err_code in err_codes:
459                                        result['msg']=err_codes[err_code]
460                                else:
461                                        result['msg']=u"Unknown error"
462                        except:
463                                        result['msg']=u"Unknown error"
464                self.lock.release()
465#               print("RESULT %s: %s"%(action,result))
466                return(result)
467        #def get_status
468
469        ####
470        #Loads the store
471        ####
472        def _load_Store(self):
473                action='load'
474                #Load appstream metada first
475                package_type='*'
476                load_function=self._execute_class_method(action,package_type,launchedby=None)
477                self.store=load_function.execute_action(action=action,store=self.store)['data']
478                #Once appstream is loaded load the appstream plugins for other package types (snap, appimage...)
479                store_pool=pool()
480                threads=[]
481                for package_type in self.plugins_registered[action]:
482                        if package_type!='*':
483                                th=threading.Thread(target=self._th_load_store, args = (store_pool,action,package_type))
484                                threads.append(th)
485                                th.start()
486                for thread in threads:
487                        try:
488                                thread.join()
489                        except:
490                                pass
491                while store_pool.qsize():
492                        self.store=store_pool.get()
493                with open(self.cache_completion,'w') as f:
494                        for app in self.store.get_apps():
495                                f.write("%s\n"%app.get_pkgname_default())
496
497        #def _load_Store
498
499        def _th_load_store(self,store_pool,action,package_type):
500                load_function=self._execute_class_method(action,package_type,launchedby=None)
501                store_pool.put(load_function.execute_action(action=action,store=self.store)['data'])
502
503        ####
504        #Return a random array of applications
505        #Input:
506        #  - exclude_sections=[] -> Array of sections that will not be included
507        #  - include_sections=[] -> Array of sections that will be included
508        #  - max_results -> Max number of apps to include
509        #Output:
510        #  - Dict with the related info
511        ####
512        def _get_editors_pick(self,*args,**kwargs):
513               
514                def load_applist():
515                        attempts=0
516                        sw_include=True
517                        tmp_app=random.choice(tmp_applist)
518                        while tmp_app in applist or tmp_app.get_state()==1:
519                                tmp_app=random.choice(tmp_applist)
520                                attempts+=1
521                                if attempts==9:
522                                        tmp_app=None
523                                        break
524                        if tmp_app:
525                                if exclude_sections or include_sections:
526                                        tmp_app_sec=tmp_app.get_categories()
527                                        for sec in exclude_sections:
528                                                sw_include=True
529                                                if sec in tmp_app_sec:
530                                                        sw_include=False
531                                                        break
532                                        for sec in include_sections:
533                                                sw_include=False
534                                                if sec in tmp_app_sec:
535                                                        sw_include=True
536                                                        break
537
538                                if sw_include:
539                                        while tmp_app in applist or tmp_app.get_state()==1:
540                                                tmp_app=random.choice(tmp_applist)
541                                                attempts+=1
542                                                if attempts==9:
543                                                        tmp_app=None
544                                                        break
545                                else:
546                                        tmp_app=None
547                        return(tmp_app)
548
549                def select_applist():
550                        start_point=random.randint(0,total_apps)
551                        end_point=start_point+10
552                        if end_point>total_apps:
553                                diff=end_point-total_apps
554                                end_point=total_apps
555                                start_point-=diff
556                        tmp_applist=apps_in_store[start_point:end_point]
557                        return(tmp_applist)
558                exclude_sections=[]
559                include_sections=[]
560                max_results=10
561                kargs=kwargs['kwargs'].copy()
562                if 'exclude_sections' in kargs.keys():
563                        exclude_sections=kargs['exclude_sections'].split(',')
564                        self._debug("Exclude sections %s"%exclude_sections)
565                if 'include_sections' in kargs.keys():
566                        include_sections=kargs['include_sections'].split(',')
567                        self._debug("Only sections %s"%include_sections)
568                if 'max_results' in kargs.keys():
569                        if kargs['max_results']:
570                                max_results=kargs['max_results']
571                applist=[]
572                apps_in_store=self.store.get_apps()
573                cont=0
574                total_apps=len(apps_in_store)-1
575                tmp_applist=select_applist()
576                while cont<max_results:
577                        tmp_app=load_applist()
578                        attempts=0
579                        while tmp_app==None:
580                                tmp_applist=select_applist()
581                                tmp_app=load_applist()
582                                attempts+=1
583                                if attempts>max_results*10:
584                                        break
585                        if tmp_app:
586                                applist.append(tmp_app)
587                        cont+=1
588                #Now transform applist into an app_info list
589                appinfo=self._get_App_Info(applist)
590                return(appinfo)
591
592        ####
593        #Loads the info related to one app
594        #Input:
595        #  - List of App objects
596        #Output:
597        #  - Dict with the related info
598        ####
599        def _get_App_Info(self,applist,launchedby=None):
600                action='get_info'
601                info_function=self._execute_class_method(action,None,launchedby=launchedby)
602                info_applist=info_function.execute_action(action,applist)
603                self._debug("Info collected")
604                return(info_applist)
605        #def _get_App_Info
606
607        ####
608        #Loads the extended info related to one app (slower)
609        #Input:
610        #  - Dict off Apps (as returned by _get_app_info)
611        #Output:
612        #  - Dict with the related info
613        ####
614        def _get_Extended_App_Info(self,info_applist,launchedby=None,fullsearch=True,channel=''):
615                #Check if there's any plugin for the distinct type of packages
616                action='pkginfo'
617                types_dict={}
618                result={}
619                result['data']=[]
620                result['status']={'status':0,'msg':''}
621                processed=[]
622                for app_info in info_applist:
623                        info_function=self._execute_class_method(action,'*',launchedby=launchedby)
624                        info_result=info_function.execute_action(action,applist=[app_info])
625                        self._debug(info_result)
626                        if info_result['status']['status']==0 and info_result['data'][0]['state']:
627                                result['data'].extend(info_result['data'])
628                        elif info_result['status']['status']==0:
629                                app_info=info_result['data'][0]
630                        #Get channel
631                        available_channels=self._check_package_type(app_info)
632                        for package_type in available_channels:
633                                if app_info['component']!='':
634                                        if app_info['id'] in processed:
635                                                self._debug("App %s processed"%app_info['id'])
636                                                continue
637
638                                if package_type in types_dict:
639                                        types_dict[package_type].append(app_info)
640                                else:
641                                        types_dict[package_type]=[app_info]
642                                processed.append(app_info['id'])
643                for package_type in types_dict:
644                        self._debug("Checking plugin for %s %s"%(action,package_type))
645                        if package_type in self.plugins_registered[action]:
646                                #Only seach full info if it's required
647                                if (fullsearch==False and package_type=='deb'):
648                                        result['data'].extend(types_dict[package_type])
649                                        continue
650                                self._debug("Retrieving info for %s"%types_dict[package_type])
651                                info_function=self._execute_class_method(action,package_type,launchedby=launchedby)
652                                result['data'].extend(info_function.execute_action(action,types_dict[package_type])['data'])
653                        else:
654                                result['data'].append(app_info)
655                return(result)
656        #def _get_Extended_App_Info
657
658        def _list_sections(self,searchItem='',action='list_sections',launchedby=None):
659                result={}
660                self._debug("Retrieving all sections")
661                data={}
662                status={}
663                if action in self.plugins_registered.keys():
664                        self._debug("Plugin for generic search: %s"%self.plugins_registered[action]['*'])
665                        finder=self.plugins_registered[action][('*')]
666                        search_function=eval(finder+"()")
667                        result=search_function.execute_action(self.store,action,searchItem)
668                        status=result['status']
669                        data=result['data']
670                else:
671                        print("No plugin for action %s"%action)
672                self.result[action]['data']=data
673                self.result[action]['status']=status
674                self._debug("Sections: %s"%self.result[action]['data'])
675                self._debug("Status: %s"%self.result[action]['status'])
676
677        ####
678        #Search the store
679        #Input:
680        #  - string search
681        #Output:
682        #  - List of dicts with all the info
683        ####
684        def _search_Store(self,*args,**kwargs):
685                search_item=args[0]
686                return_msg=False
687                action='search'
688                if 'action' in kwargs.keys():
689                        action=kwargs['action']
690                launchedby=None
691                if 'launchedby' in kwargs.keys():
692                        launchedby=kwargs['launchedby']
693                max_results=0
694                if 'max_results' in kwargs.keys():
695                        max_results=kwargs['max_results'] 
696                fullsearch=False
697                if 'fullsearch' in kwargs.keys():
698                        fullsearch=kwargs['fullsearch']
699                result={}
700                tmp_applist=[]
701                if action=='list_sections':
702                        search_item=''
703                elif action=='info':
704                        fullsearch=True
705                if not launchedby:
706                        launchedby=action
707                #Set the exact match to false for search method
708                exact_match=True
709                if (launchedby=='search'):
710                                exact_match=False
711                target_channel=''
712                if '=' in search_item:
713                        target_channel=search_item.split('=')[-1]
714                        search_item=search_item.split('=')[0]
715                for package_type in self.plugins_registered[action]:
716                        self._debug("Searching package type %s"%package_type)
717                        search_function=self._execute_class_method(action,'*',launchedby=launchedby)
718                        result.update(search_function.execute_action(self.store,action,search_item,exact_match,max_results))
719                tmp_applist=result['data']
720                status=result['status']
721                realAction=action
722                if status['status']==0:
723                        #1.- Get appstream metadata (faster)
724                        subordinate_action='get_info'
725                        self.result[subordinate_action]={}
726                        result=self._get_App_Info(tmp_applist,launchedby)
727                        self._debug("Add result for %s"%subordinate_action)
728                        self.result[subordinate_action]=result
729                        if fullsearch:
730                                #2.- Get rest of metadata (slower)
731                                self._debug("Target channel: %s"%target_channel)
732                                result=self._get_Extended_App_Info(result['data'],launchedby,fullsearch,target_channel)
733                                if launchedby:
734                                        realAction=launchedby
735                                        self._debug("Assigned results of %s to %s"%(action,realAction))
736                                if (result['status']['status']==0) or (result['status']['status']==9):
737                                        return_msg=True
738                                        if fullsearch:
739                                                result['status']['status']=0
740                                else:
741                                        self._debug(result)
742                                        return_msg=False
743                else:
744                        return_msg=False
745                self.result[launchedby]['data']=result['data']
746                self.result[launchedby]['status']=result['status']
747                return(return_msg)
748        #def _search_Store
749
750        ####
751        #Install or remove an app
752        #Input:
753        #  - String with the app name
754        #Output:
755        #  - Result of the operation
756        ####
757        def _install_remove_App(self,*args,**kwargs):
758                appName=args[0]
759                if 'action' in kwargs.keys():
760                        action=kwargs['action']
761                self._log("Attempting to %s %s"%(action,appName))
762                result={}
763                return_msg=False
764                if (self._search_Store(appName,action='search',fullsearch=True,launchedby=action)):
765                        info_applist=self.result[action]['data']
766                        types_dict={}
767                        #Check if package is installed if we want to remove it or vice versa
768                        for app_info in info_applist:
769                        #Appstream doesn't get the right status in all cases so we rely on the mechanisms given by the different plugins.
770                                if (action=='install' and app_info['state']=='installed') or (action=='remove' and app_info['state']=='available'):
771                                        if (action=='remove' and app_info['state']=='available'):
772                                                        self.result[action]['status']={app_info['package']:3}
773                                                        self.result[action]['status']={'status':3}
774                                        else:
775                                                self.result[action]['status']={app_info['package']:4}
776                                                self.result[action]['status']={'status':4}
777                                                pass
778                                        return_msg=False
779                                        types_dict={}
780                                        break
781                                processed=[]
782                                available_channels=self._check_package_type(app_info)
783                                for package_type in available_channels:
784                                        if app_info['component']!='':
785                                                if app_info['id'] in processed:
786                                                        self._debug("App %s processed"%app_info['id'])
787                                                        continue
788
789                                        if package_type in types_dict:
790                                                types_dict[package_type].append(app_info)
791                                        else:
792                                                types_dict[package_type]=[app_info]
793                                        processed.append(app_info['id'])
794
795                        for package_type in types_dict:
796                                self._debug("Checking plugin for %s %s"%(action,package_type))
797                                if package_type in self.plugins_registered[action]:
798                                        install_function=self._execute_class_method(action,package_type,launchedby=action)
799                                        if package_type=='zmd':
800                                        #If it's a zmd the zomando must be present in the system
801                                                zmd_info=[]
802                                                for zmd_bundle in types_dict[package_type]:
803                                                        zmdInfo={}
804                                                        self._debug("Cheking presence of zmd %s"%zmd_bundle['package'])
805                                                        zmd='/usr/share/zero-center/zmds/'+app_info['package']+'.zmd'
806                                                        if not os.path.exists(zmd):
807                                                                zmdInfo['package']=zmd_bundle['package']
808                                                                zmd_info.append(zmdInfo)
809                                                if zmd_info:
810                                                        self._debug("Installing needed packages")
811                                                        install_depends_function=self._execute_class_method(action,"deb",launchedby=action)
812                                                        result=install_depends_function.execute_action(action,zmd_info)
813                                                       
814                                        result=install_function.execute_action(action,types_dict[package_type])
815                                        self.result[action]=result
816                                        #Deprecated. Earlier versions stored the "app" object so here the app could get marked as installed/removed without neeed of import or query anything
817                                        #Python>=3.6 don't let us to store the app object in a queue (needed for the GUI) so this code becames deprecated.
818#                                       if result['status']['status']==0:
819                                                #Mark the apps as installed or available
820#                                               for app in types_dict[package_type]:
821#                                                       if action=='install':
822#                                                               app['appstream_id'].set_state(1)
823#                                                               self._debug("App state changed to installed")
824#                                                       else:
825#                                                               app['appstream_id'].set_state(2)
826#                                                               self._debug("App state changed to available")
827                                        for app in types_dict[package_type]:
828                                                self._execute_postactions(action,app['package'])
829                                        return_msg=True
830                self._log("Result %s: %s"%(action,self.result[action]))
831                return(return_msg)
832        #def install_App
833       
834        ####
835        #Check the package type
836        #Input:
837        # - AppInfo dict (element of the list returned by _get_app_info)
838        #Output:
839        # - String with the type (deb, sh, zmd...)
840        ####
841        def _check_package_type(self,app_info):
842                #Standalone installers must have the subcategory "installer"
843                #Zomandos must have the subcategory "Zomando"
844                self._debug("Checking package type for app "+app_info['name'])
845                return_msg=[]
846                if app_info['bundle']:
847                        return_msg.extend(app_info['bundle'])
848                else:
849                        if "Zomando" in app_info['categories']:
850                                return_msg.append("zmd")
851                        if 'component' in app_info.keys():
852                                if app_info['component']!='':
853                                        return_msg.append('deb')
854                #Standalone installers must have an installerUrl field loaded from a bundle type=script description
855                        if app_info['installerUrl']!='':
856                                return_msg.append("sh")
857                return(return_msg)
858        #def _check_package_type
859
860        def _execute_postactions(self,action,app):
861                for postaction in self.postaction_actions:
862                        for plugin,actions in postaction.items():
863                                for key,val in actions.items():
864                                        if action==key:
865                                                self._debug("Application: %s"%app)
866                                                plugin.execute_action(key,applist=app)
Note: See TracBrowser for help on using the repository browser.