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

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalcmagnetic_scattrelease-4.2.2ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since 908f090 was 908f090, checked in by lewis, 7 years ago

Add S(Q) plugin models to correct drop down

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