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

Last change on this file since 7473 was 7473, checked in by Juanma, 21 months ago

implement method random to get a random list of apps

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