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

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.1.1release-4.1.2release-4.2.2ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since 05b8e01 was 05b8e01, checked in by Adam Washington <adam.washington@…>, 7 years ago

Remove magic string from models.py

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