source: sasview/src/sas/sasgui/perspectives/fitting/models.py @ bac3988

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalccostrafo411magnetic_scattrelease-4.1.1release-4.1.2release-4.2.2release_4.0.1ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since bac3988 was bac3988, checked in by ajj, 9 years ago

Fixing category lists and list of models in GUI

  • Property mode set to 100644
File size: 15.3 KB
RevLine 
[f0d720b]1"""
2    Utilities to manage models
3"""
4import wx
5import imp
6import os
7import sys
8import math
9import os.path
10# Time is needed by the log method
11import time
12import logging
13import py_compile
14import shutil
[f66d9d1]15from sas.sasgui.guiframe.events import StatusEvent
[f0d720b]16# Explicitly import from the pluginmodel module so that py2exe
17# places it in the distribution. The Model1DPlugin class is used
18# as the base class of plug-in models.
19from sas.models.pluginmodel import Model1DPlugin
20from sas.models.BaseComponent import BaseComponent
[d85c194]21from sas.sasgui.guiframe.CategoryInstaller import CategoryInstaller
[f0d720b]22from sasmodels.sasview_model import make_class
[f66d9d1]23import sasmodels.core
24
25
[f0d720b]26PLUGIN_DIR = 'plugin_models'
27
28def get_model_python_path():
29    """
30        Returns the python path for a model
31    """
32    return os.path.dirname(__file__)
33
34
35def log(message):
36    """
37        Log a message in a file located in the user's home directory
38    """
39    dir = os.path.join(os.path.expanduser("~"), '.sasview', PLUGIN_DIR)
40    out = open(os.path.join(dir, "plugins.log"), 'a')
41    out.write("%10g%s\n" % (time.clock(), message))
42    out.close()
43
44
45def _check_plugin(model, name):
46    """
47    Do some checking before model adding plugins in the list
48
49    :param model: class model to add into the plugin list
50    :param name:name of the module plugin
51
52    :return model: model if valid model or None if not valid
53
54    """
55    #Check if the plugin is of type Model1DPlugin
56    if not issubclass(model, Model1DPlugin):
57        msg = "Plugin %s must be of type Model1DPlugin \n" % str(name)
58        log(msg)
59        return None
60    if model.__name__ != "Model":
61        msg = "Plugin %s class name must be Model \n" % str(name)
62        log(msg)
63        return None
64    try:
65        new_instance = model()
66    except:
67        msg = "Plugin %s error in __init__ \n\t: %s %s\n" % (str(name),
68                                                             str(sys.exc_type),
69                                                             sys.exc_info()[1])
70        log(msg)
71        return None
72
73    if hasattr(new_instance, "function"):
74        try:
75            value = new_instance.function()
76        except:
77            msg = "Plugin %s: error writing function \n\t :%s %s\n " % \
78                    (str(name), str(sys.exc_type), sys.exc_info()[1])
79            log(msg)
80            return None
81    else:
82        msg = "Plugin  %s needs a method called function \n" % str(name)
83        log(msg)
84        return None
85    return model
86
87
88def find_plugins_dir():
89    """
90        Find path of the plugins directory.
91        The plugin directory is located in the user's home directory.
92    """
93    dir = os.path.join(os.path.expanduser("~"), '.sasview', PLUGIN_DIR)
94
95    # If the plugin directory doesn't exist, create it
96    if not os.path.isdir(dir):
97        os.makedirs(dir)
98
99    # Find paths needed
100    try:
101        # For source
102        if os.path.isdir(os.path.dirname(__file__)):
103            p_dir = os.path.join(os.path.dirname(__file__), PLUGIN_DIR)
104        else:
105            raise
106    except:
107        # Check for data path next to exe/zip file.
108        #Look for maximum n_dir up of the current dir to find plugins dir
109        n_dir = 12
110        p_dir = None
111        f_dir = os.path.join(os.path.dirname(__file__))
112        for i in range(n_dir):
113            if i > 1:
114                f_dir, _ = os.path.split(f_dir)
115            plugin_path = os.path.join(f_dir, PLUGIN_DIR)
116            if os.path.isdir(plugin_path):
117                p_dir = plugin_path
118                break
119        if not p_dir:
120            raise
121    # Place example user models as needed
122    if os.path.isdir(p_dir):
123        for file in os.listdir(p_dir):
124            file_path = os.path.join(p_dir, file)
125            if os.path.isfile(file_path):
126                if file.split(".")[-1] == 'py' and\
127                    file.split(".")[0] != '__init__':
128                    if not os.path.isfile(os.path.join(dir, file)):
129                        shutil.copy(file_path, dir)
130
131    return dir
132
133
134class ReportProblem:
135    """
136        Class to check for problems with specific values
137    """
138    def __nonzero__(self):
139        type, value, traceback = sys.exc_info()
140        if type is not None and issubclass(type, py_compile.PyCompileError):
141            print "Problem with", repr(value)
142            raise type, value, traceback
143        return 1
144
145report_problem = ReportProblem()
146
147
148def compile_file(dir):
149    """
150    Compile a py file
151    """
152    try:
153        import compileall
154        compileall.compile_dir(dir=dir, ddir=dir, force=1,
155                               quiet=report_problem)
156    except:
157        return sys.exc_info()[1]
158    return None
159
160
161def _findModels(dir):
162    """
163    """
164    # List of plugin objects
165    plugins = {}
166    dir = find_plugins_dir()
167    # Go through files in plug-in directory
168    #always recompile the folder plugin
169    if not os.path.isdir(dir):
170        msg = "SasView couldn't locate Model plugin folder."
171        msg += """ "%s" does not exist""" % dir
172        logging.warning(msg)
173        return plugins
174    else:
175        log("looking for models in: %s" % str(dir))
176        compile_file(dir)
177        logging.info("plugin model dir: %s" % str(dir))
178    try:
179        list = os.listdir(dir)
180        for item in list:
181            toks = os.path.splitext(os.path.basename(item))
182            if toks[1] == '.py' and not toks[0] == '__init__':
183                name = toks[0]
184
185                path = [os.path.abspath(dir)]
186                file = None
187                try:
188                    (file, path, info) = imp.find_module(name, path)
189                    module = imp.load_module(name, file, item, info)
190                    if hasattr(module, "Model"):
191                        try:
192                            if _check_plugin(module.Model, name) != None:
193                                plugins[name] = module.Model
194                        except:
195                            msg = "Error accessing Model"
196                            msg += "in %s\n  %s %s\n" % (name,
197                                                         str(sys.exc_type),
198                                                         sys.exc_info()[1])
199                            log(msg)
200                except:
201                    msg = "Error accessing Model"
202                    msg += " in %s\n  %s %s \n" % (name,
203                                                   str(sys.exc_type),
204                                                   sys.exc_info()[1])
205                    log(msg)
206                finally:
207
208                    if not file == None:
209                        file.close()
210    except:
211        # Don't deal with bad plug-in imports. Just skip.
212        msg = "Could not import model plugin: %s" % sys.exc_info()[1]
213        log(msg)
214    return plugins
215
216
217class ModelList(object):
218    """
219    Contains dictionary of model and their type
220    """
221    def __init__(self):
222        """
223        """
224        self.mydict = {}
225
226    def set_list(self, name, mylist):
227        """
228        :param name: the type of the list
229        :param mylist: the list to add
230
231        """
232        if name not in self.mydict.keys():
233            self.reset_list(name, mylist)
234
235    def reset_list(self, name, mylist):
236        """
237        :param name: the type of the list
238        :param mylist: the list to add
239        """
240        self.mydict[name] = mylist
241
242    def get_list(self):
243        """
244        return all the list stored in a dictionary object
245        """
246        return self.mydict
247
248
249class ModelManagerBase:
250    """
251        Base class for the model manager
252    """
253    ## external dict for models
254    model_combobox = ModelList()
255    ## Dictionary of form factor models
256    form_factor_dict = {}
257    ## dictionary of structure factor models
258    struct_factor_dict = {}
259    ##list of structure factors
260    struct_list = []
261    ##list of model allowing multiplication by a structure factor
262    multiplication_factor = []
263    ##list of multifunctional shapes (i.e. that have user defined number of levels
264    multi_func_list = []
265    ## list of added models -- currently python models found in the plugin dir.
266    plugins = []
267    ## Event owner (guiframe)
268    event_owner = None
269    last_time_dir_modified = 0
270
271    def __init__(self):
272        """
273        """
274        self.model_dictionary = {}
275        self.stored_plugins = {}
276        self._getModelList()
277
278    def findModels(self):
279        """
280        find  plugin model in directory of plugin .recompile all file
281        in the directory if file were modified
282        """
283        temp = {}
284        if self.is_changed():
285            return  _findModels(dir)
286        logging.info("plugin model : %s" % str(temp))
287        return temp
288
289    def _getModelList(self):
290        """
291        List of models we want to make available by default
292        for this application
293
294        :return: the next free event ID following the new menu events
295
296        """
297
298        # regular model names only
[f66d9d1]299        base_message = "Unable to load model {0}"
[f0d720b]300        self.model_name_list = []
301
302        #Build list automagically from sasmodels package
[f66d9d1]303        for mod_name in sasmodels.core.list_models():
304            mod_def = sasmodels.core.load_model_info(mod_name)
[bac3988]305            self.model_dictionary[mod_def['name']] = make_class(mod_def,dtype=None,namestyle='name')
[f66d9d1]306            if mod_def['structure_factor'] == True:
[bac3988]307                self.struct_list.append(self.model_dictionary[mod_def['name']])
[f66d9d1]308            if mod_def['variant_info'] is not None:
[bac3988]309                self.multi_func_list.append(self.model_dictionary[mod_def['name']])
310            else:
311                self.model_name_list.append(mod_def['name'])
[f66d9d1]312            if mod_def['ER'] is not None:
[bac3988]313                self.multiplication_factor.append(self.model_dictionary[mod_def['name']])
[f0d720b]314
315        #Looking for plugins
316        self.stored_plugins = self.findModels()
317        self.plugins = self.stored_plugins.values()
318        for name, plug in self.stored_plugins.iteritems():
319            self.model_dictionary[name] = plug
320
321        self._get_multifunc_models()
322
323        return 0
324
325    def is_changed(self):
326        """
327        check the last time the plugin dir has changed and return true
328         is the directory was modified else return false
329        """
330        is_modified = False
331        plugin_dir = find_plugins_dir()
332        if os.path.isdir(plugin_dir):
333            temp = os.path.getmtime(plugin_dir)
334            if  self.last_time_dir_modified != temp:
335                is_modified = True
336                self.last_time_dir_modified = temp
337
338        return is_modified
339
340    def update(self):
341        """
342        return a dictionary of model if
343        new models were added else return empty dictionary
344        """
345        new_plugins = self.findModels()
346        if len(new_plugins) > 0:
347            for name, plug in  new_plugins.iteritems():
348                if name not in self.stored_plugins.keys():
349                    self.stored_plugins[name] = plug
350                    self.plugins.append(plug)
351                    self.model_dictionary[name] = plug
352            self.model_combobox.set_list("Customized Models", self.plugins)
353            return self.model_combobox.get_list()
354        else:
355            return {}
356
[f66d9d1]357    def plugins_reset(self):
[f0d720b]358        """
359        return a dictionary of model
360        """
361        self.plugins = []
362        new_plugins = _findModels(dir)
363        for name, plug in  new_plugins.iteritems():
364            for stored_name, stored_plug in self.stored_plugins.iteritems():
365                if name == stored_name:
366                    del self.stored_plugins[name]
367                    del self.model_dictionary[name]
368                    break
369            self.stored_plugins[name] = plug
370            self.plugins.append(plug)
371            self.model_dictionary[name] = plug
372
373        self.model_combobox.reset_list("Customized Models", self.plugins)
374        return self.model_combobox.get_list()
375
376    def _on_model(self, evt):
377        """
378        React to a model menu event
379
380        :param event: wx menu event
381
382        """
383        if int(evt.GetId()) in self.form_factor_dict.keys():
384            from sas.models.MultiplicationModel import MultiplicationModel
385            self.model_dictionary[MultiplicationModel.__name__] = MultiplicationModel
386            model1, model2 = self.form_factor_dict[int(evt.GetId())]
387            model = MultiplicationModel(model1, model2)
388        else:
389            model = self.struct_factor_dict[str(evt.GetId())]()
390
391
392    def _get_multifunc_models(self):
393        """
394        Get the multifunctional models
395        """
396        for item in self.plugins:
397            try:
398                # check the multiplicity if any
399                if item.multiplicity_info[0] > 1:
400                    self.multi_func_list.append(item)
401            except:
402                # pass to other items
403                pass
404
405    def get_model_list(self):
406        """
407        return dictionary of models for fitpanel use
408
409        """
410        ## Model_list now only contains attribute lists not category list.
411        ## Eventually this should be in one master list -- read in category
412        ## list then pull those models that exist and get attributes then add
413        ## to list ..and if model does not exist remove from list as now
414        ## and update json file.
415        ##
416        ## -PDB   April 26, 2014
417
418#        self.model_combobox.set_list("Shapes", self.shape_list)
419#        self.model_combobox.set_list("Shape-Independent",
420#                                     self.shape_indep_list)
421        self.model_combobox.set_list("Structure Factors", self.struct_list)
422        self.model_combobox.set_list("Customized Models", self.plugins)
423        self.model_combobox.set_list("P(Q)*S(Q)", self.multiplication_factor)
424        self.model_combobox.set_list("multiplication",
425                                     self.multiplication_factor)
426        self.model_combobox.set_list("Multi-Functions", self.multi_func_list)
427        return self.model_combobox.get_list()
428
429    def get_model_name_list(self):
430        """
431        return regular model name list
432        """
433        return self.model_name_list
434
435    def get_model_dictionary(self):
436        """
437        return dictionary linking model names to objects
438        """
439        return self.model_dictionary
440
441
442class ModelManager(object):
443    """
444    implement model
445    """
446    __modelmanager = ModelManagerBase()
447    cat_model_list = [model_name for model_name \
448                      in __modelmanager.model_dictionary.keys() \
449                      if model_name not in __modelmanager.stored_plugins.keys()]
450
451    CategoryInstaller.check_install(model_list=cat_model_list)
452    def findModels(self):
453        return self.__modelmanager.findModels()
454
455    def _getModelList(self):
456        return self.__modelmanager._getModelList()
457
458    def is_changed(self):
459        return self.__modelmanager.is_changed()
460
461    def update(self):
462        return self.__modelmanager.update()
463
[f66d9d1]464    def plugins_reset(self):
465        return self.__modelmanager.plugins_reset()
[f0d720b]466
467    def populate_menu(self, modelmenu, event_owner):
468        return self.__modelmanager.populate_menu(modelmenu, event_owner)
469
470    def _on_model(self, evt):
471        return self.__modelmanager._on_model(evt)
472
473    def _get_multifunc_models(self):
474        return self.__modelmanager._get_multifunc_models()
475
476    def get_model_list(self):
477        return self.__modelmanager.get_model_list()
478
479    def get_model_name_list(self):
480        return self.__modelmanager.get_model_name_list()
481
482    def get_model_dictionary(self):
483        return self.__modelmanager.get_model_dictionary()
Note: See TracBrowser for help on using the repository browser.