import os
from collections import OrderedDict
import copy
import numpy as np
from quantity import Quantities
from smo.web.exceptions import *
from smo.model.actions import ActionBar
from SmoWeb.settings import MEDIA_ROOT
tmpFolderPath = os.path.join (MEDIA_ROOT, 'tmp')
[docs]class Field(object):
"""
Abstract base class for all the field types.
"""
# Tracks each time an instance is created. Used to retain order.
creation_counter = 0
def __init__(self, label = "", description = "", show = None):
"""
:param str label: the text label used in the user interface usually in front of the field
:param str description: description to show as tooltip when hovering over the field label
:param str show: expression (as a string), which is evaluated on the client side and is used to
dynamically show and hide a field, based on the values of other fields. The
other fields in the model are referenced by prefixing them with ``self.``
"""
self.label = label
self.description = description
self.show = show
if (self.show is not None):
self.show = self.show.replace('"', '\'')
# Increase the creation counter, and save our local copy.
self.creation_counter = Field.creation_counter
Field.creation_counter += 1
[docs] def parseValue(self, value):
"""
:param value: value to parse
Checks if the ``value`` is of valid type for this field type, and, if not,
attempts to convert it into one.
For example if the Field is of type :class:`Quantity`\ ('Length')
then parseValue((2, 'mm')) will return 2e-3 (in the base SI unit 'm') which
can be assigned to the field. Used implicitly by the
:func:`smo.model.model.NumericalModel.__setattr__` method
"""
raise NotImplementedError
[docs] def getValueRepr(self, value):
"""
Converts the value of the field to a form suitable for JSON serialization
"""
raise NotImplementedError
[docs]class Quantity(Field):
'''
Represents a physical quantity (e.g. Length, Time, Mass etc.). Allows values to
be set using units e.g. (2, 'km')
'''
def __init__(self, type = 'Dimensionless', default = None, minValue = None, maxValue = None, inputBoxWidth = None, *args, **kwargs):
"""
:param str type: the quantity type (Length, Mass, Time etc.)
:param default: default value for the field. Could be value in SI unit or tuple (value, unit) like (2, 'mm').
:param minValue: the minimum allowed value for the field. Could be value in SI unit or tuple (value, unit) like (2, 'mm').
:param maxValue: the maximum allowed value for the field. Could be value in SI unit or tuple (value, unit) like (2, 'mm').
"""
super(Quantity, self).__init__(*args, **kwargs)
self.type = type
if (minValue is None):
self.minValue = Quantities[self.type].get('minValue', 1e-99)
else:
self.minValue = self.parseValue(minValue)
if (maxValue is None):
self.maxValue = Quantities[self.type].get('maxValue', 1e99)
else:
self.maxValue = self.parseValue(maxValue)
if (inputBoxWidth is None):
inputBoxWidth = 120
self.inputBoxWidth = inputBoxWidth
if (default is None):
self.default = 1.0
if ('defDispUnit' in Quantities[self.type].keys()):
self.defaultDispUnit = Quantities[self.type]['defDispUnit']
else:
self.defaultDispUnit = Quantities[self.type]['SIUnit']
elif (isinstance(default, tuple) and len(default) == 2):
self.default = self.parseValue(default)
self.defaultDispUnit = default[1]
elif (isinstance(default, (float, int))):
self.default = default
if ('defDispUnit' in Quantities[self.type].keys()):
self.defaultDispUnit = Quantities[self.type]['defDispUnit']
else:
self.defaultDispUnit = Quantities[self.type]['SIUnit']
else:
raise ValueError("To set quantity default value you should either use a number or a tuple e.g. (2, 'mm')")
def parseValue(self, value):
if (isinstance(value, tuple) and len(value) == 2):
unitDef = Quantities[self.type]['units'][value[1]]
unitOffset = 0 if ('offset' not in unitDef.keys()) else unitDef['offset']
return value[0] * unitDef['mult'] + unitOffset
elif (isinstance(value, (float, np.float32, np.float64))):
return float(value)
elif (isinstance(value, int)):
return float(value)
else:
raise ValueError("To set quantity value you should either use a number or a tuple e.g. (2, 'mm')")
def getValueRepr(self, value):
return value
def toFormDict(self):
fieldDict = super(Quantity, self).toFormDict()
fieldDict['type'] = 'Quantity'
fieldDict['quantity'] = self.type
fieldDict['defaultDispUnit'] = self.defaultDispUnit
fieldDict['minValue'] = self.minValue
fieldDict['maxValue'] = self.maxValue
unitsList = []
for key in Quantities[self.type]['units'].keys():
unitsList.append([key, Quantities[self.type]['units'][key]])
fieldDict['units'] = unitsList
fieldDict['title'] = Quantities[self.type]['title']
fieldDict['nominalValue'] = Quantities[self.type]['nominalValue']
fieldDict['SIUnit'] = Quantities[self.type]['SIUnit']
fieldDict['inputBoxWidth'] = self.inputBoxWidth
return fieldDict
class Integer(Field):
"""
Integer field
"""
def __init__(self, default = None, minValue = None, maxValue = None, inputBoxWidth = None, *args, **kwargs):
super(Integer, self).__init__(*args, **kwargs)
if (minValue is None):
minValue = 0
self.minValue = self.parseValue(minValue)
if (maxValue is None):
maxValue = 1e6
self.maxValue = self.parseValue(maxValue)
if (inputBoxWidth is None):
inputBoxWidth = 120
self.inputBoxWidth = inputBoxWidth
if (default is None):
default = 1
self.default = self.parseValue(default)
def parseValue(self, value):
return int(value)
def getValueRepr(self, value):
return value
def toFormDict(self):
fieldDict = super(Integer, self).toFormDict()
fieldDict['type'] = 'Integer'
fieldDict['minValue'] = self.minValue
fieldDict['maxValue'] = self.maxValue
fieldDict['inputBoxWidth'] = self.inputBoxWidth
return fieldDict
class Complex(Field):
"""
Complex number field
"""
def __init__(self, default = None, *args, **kwargs):
super(Complex, self).__init__(*args, **kwargs)
if (default is None):
default = (0+1j)
self.default = self.parseValue(default)
def parseValue(self, value):
if type(value) == list:
sign = ''
if float(value[1]) >= 0:
sign = '+'
value = '(' + str(value[0]) + sign + str(value[1]) + 'j)'
return complex(value)
def getValueRepr(self, value):
return [value.real, value.imag]
def toFormDict(self):
fieldDict = super(Complex, self).toFormDict()
fieldDict['type'] = 'Complex'
return fieldDict
[docs]class String(Field):
"""
Represents a string field
"""
def __init__(self, default = None, maxLength = None, multiline = None, inputBoxWidth = None, showTooltip = False, *args, **kwargs):
"""
:param str default: default value
:param int maxLength: the maximum number of characters in the string
:param bool multiline: whether line breaks are allowed in the string
:param inputBoxWidth: sets the input box width in pixels
:param showTooltip: specifies if tooltip with the string value should be displayed
"""
super(String, self).__init__(*args, **kwargs)
if (default is None):
self.default = "..."
else:
self.default = self.parseValue(default)
if (multiline is None):
self.multiline = False
else:
self.multiline = multiline
if (maxLength is None):
self.maxLength = 100
else:
self.maxLength = maxLength
if (inputBoxWidth is None):
inputBoxWidth = 120
self.inputBoxWidth = inputBoxWidth
self.showTooltip = showTooltip
def parseValue(self, value):
return value
def getValueRepr(self, value):
if (self.multiline == True):
resultStr = "";
for i in range(len(value)/10):
resultStr += value[:10]
resultStr += '\n'
value = value[10:]
if len(value)>0:
resultStr += value[:]
else: resultStr=resultStr[:-1]
return resultStr
else:
return value
def toFormDict(self):
fieldDict = super(String, self).toFormDict()
fieldDict['type'] = 'String'
fieldDict['inputBoxWidth'] = self.inputBoxWidth
fieldDict['multiline'] = self.multiline
fieldDict['showTooltip'] = self.showTooltip
return fieldDict
[docs]class Boolean(Field):
"""
Represents a boolean value (True or False)
"""
def __init__(self, default = None, *args, **kwargs):
""":param bool default: default value"""
super(Boolean, self).__init__(*args, **kwargs)
if (default is None):
self.default = True;
else:
self.default = self.parseValue(default)
def parseValue(self, value):
if ((value is True) or (value is False)):
return value
elif (value == 'true' or value == 'True'):
return True
elif (value == 'false' or value == 'False'):
return False
else:
raise ValueError("To set boolean field you should either use boolean value or string")
def getValueRepr(self, value):
return value
def toFormDict(self):
fieldDict = super(Boolean, self).toFormDict()
fieldDict['type'] = 'Boolean'
return fieldDict
[docs]class Choices(Field):
"""
Allows the user to make a choice from a list of options
"""
def __init__(self, options, default = None, *args, **kwargs):
"""
:param options: dictionary or ordered dictionary containing the possible
choices represented by (value, label) pairs. The label is used in the
display combo-box
:param default: default value (from options)
"""
super(Choices, self).__init__(*args, **kwargs)
self.options = options
if (default is None):
self.default = options.keys()[0]
else:
self.default = self.parseValue(default)
def parseValue(self, value):
if (value in self.options.keys()):
return value
else:
raise ValueError('Illegal value {0} for choices variable!'.format(value))
def getValueRepr(self, value):
return value
def toFormDict(self):
fieldDict = super(Choices, self).toFormDict()
fieldDict['type'] = 'Choices'
optionsList = []
for key in self.options.keys():
optionsList.append([key, self.options[key]])
fieldDict['options'] = optionsList
return fieldDict
[docs]class ObjectReference(Field):
"""
Object reference
"""
def __init__(self, targetContainer, default, *args, **kwargs):
super(ObjectReference, self).__init__(*args, **kwargs)
self.targetContainer = targetContainer
self.default = self.parseValue(default)
def parseValue(self, value):
if (isinstance(value, (str, unicode))):
try:
return self.targetContainer[value]
except KeyError:
raise KeyError("The reference target container for field {0} has no object with key '{1}'".
format(self.name, value))
elif (isinstance(value, dict) and '_key' in value):
return value
else:
raise(ArgumentTypeError('The value to set a reference must be a string (key), or dictionary (having a _key field)'))
def getValueRepr(self, value):
return value['_key']
def toFormDict(self):
fieldDict = super(ObjectReference, self).toFormDict()
fieldDict['type'] = 'Choices'
optionsList = []
for key in self.targetContainer.keys():
optionsList.append([key, self.targetContainer[key]['label']])
fieldDict['options'] = optionsList
# fieldDict['options'] = {key : value['label'] for key, value in self.targetContainer.iteritems()}
return fieldDict
[docs]class RecordArray(Field):
"""
Composite input field for representing a structured table (array of records)
"""
def __init__(self, structTuple = None, numRows = 1, empty = False, toggle = True, *args, **kwargs):
"""
:param structTuple: tuple defining the structure of the
record array. It consists of ``(name, type)`` pairs,
where ``name`` is the column name, and ``type`` is one of the basic
field types (:class:`Quantity`, :class:`String`, :class:`Boolean` etc.)
:param int numRows: the initial number of rows in the table
:param bool empty: indicates whether the record array can be emptied
:param bool toggle: indicates if the array can be toggled in edit mode
Example::
compositePipe = RecordArray(
(
('name', String(maxLength = 20)),
('length', Quantity('Length')),
('diameter', Quantity('Length')),
), label='composite pipe'
)
"""
super(RecordArray, self).__init__(*args, **kwargs)
if (empty == False):
if (numRows == 0):
raise ValueError('Array with 0 rows cannot be restricted to not be empty.')
self.empty = empty
self.numRows = numRows
self.toggle = toggle
if (structTuple is None):
raise ValueError('The structure of the array is not defined')
if (len(structTuple) == 0):
raise ValueError('The structure of the array is not defined')
structDict = OrderedDict(structTuple)
self.fieldList = []
typeList = []
for name, field in structDict.items():
structField = field
structField.name = name
self.fieldList.append(structField)
if isinstance(field, Integer):
typeList.append((field.name, np.int))
elif isinstance(field, Quantity):
typeList.append((field.name, np.float64))
elif isinstance(field, Boolean):
typeList.append((field.name, np.dtype(bool)))
elif (isinstance(field, String) or isinstance(field, Choices)):
typeList.append((field.name, np.dtype('S' + str(field.maxLength))))
self.dtype = np.dtype(typeList)
self.defaultRow = []
for field in self.fieldList:
self.defaultRow.append(field.default)
self.defaultRow = tuple(self.defaultRow)
@property
def default(self):
default = np.zeros((self.numRows,), dtype = self.dtype)
for i in range(self.numRows):
default[i] = self.defaultRow
return default
def parseValue(self, value):
if (isinstance(value, np.ndarray)):
return value
elif (isinstance(value, list)):
array = np.zeros((len(value),), dtype = self.dtype)
i = 0
for elem in value:
if isinstance(elem, list):
array[i] = tuple(elem)
else:
raise ArgumentTypeError('Trying to set row of RecordArray from non-list object')
i += 1
return array
else:
raise ArgumentTypeError('The value of RecordArray must be a numpy structured array or a list of lists')
def getValueRepr(self, value):
return value.tolist()
def toFormDict(self):
fieldDict = super(RecordArray, self).toFormDict()
fieldDict['type'] = 'RecordArray'
jsonFieldList = []
for field in self.fieldList:
jsonFieldList.append(field.toFormDict())
fieldDict['fields'] = jsonFieldList
fieldDict['defaultRow'] = self.defaultRow
fieldDict['empty'] = self.empty
fieldDict['toggle'] = self.toggle
return fieldDict
[docs]class HdfStorage(Field):
"""
Field specifying HDF storage. Its value is dataset name
"""
def __init__(self, default = None, hdfFile = None, hdfGroup = None, datasetColumns = None, *args, **kwargs):
"""
:param hdfFile: name of HDF file
:param hdfGroup: path to HDF group
:param datasetColumns: list of names of dataset columns comprising the value of the field using HDF storage
"""
super(HdfStorage, self).__init__(*args, **kwargs)
if (default is None):
self.default = ''
else:
self.default = self.parseValue(default)
if (hdfFile is None or hdfGroup is None):
raise ValueError('Hdf file or hdf group is undefined')
hdfFile = os.path.join(tmpFolderPath, hdfFile)
self.hdfFile = hdfFile
self.hdfGroup = hdfGroup
self.datasetColumns = datasetColumns
self.show = 'false'
def parseValue(self, value):
if (isinstance(value, str)):
return value
else:
raise ValueError('Dataset name must be a string')
def getValueRepr(self, value):
return value
def toFormDict(self):
fieldDict = super(HdfStorage, self).toFormDict()
fieldDict['type'] = 'HdfStorage'
fieldDict['hdfFile'] = self.hdfFile
fieldDict['hdfGroup'] = self.hdfGroup
fieldDict['datasetColumns'] = self.datasetColumns
return fieldDict
[docs]class DataSeriesView(Field):
"""
Composite output field for representing a table or plot
"""
def __init__(self, structTuple = None, visibleColumns = None, useHdfStorage = False, storage = None, *args, **kwargs):
"""
:param structTuple: tuple defining the structure of the
view data. It consists of ``(name, type)`` pairs,
where ``name`` is the column name, and ``type`` is one of the basic
field types (:class:`Quantity`, :class:`String`, :class:`Boolean` etc.)
Example::
( ('pressure', Quantity('Pressure')),
('temperature', Quantity('Temperature')) )
:param visibleColumns: list of integers specifying which columns are visible in the view
:param useHdfStorage: indicates if data is to be stored in HDF
:param storage: name of HdfStorage field
"""
super(DataSeriesView, self).__init__(*args, **kwargs)
if (structTuple is None):
raise ValueError('The data structure is not defined.')
if (len(structTuple) == 0):
raise ValueError('The data structure is not defined.')
structDict = OrderedDict(structTuple)
self.fieldList = []
typeList = []
self.dataLabels = []
fieldNames = []
for name, field in structDict.items():
field.name = name
fieldNames.append(name)
if (field.label != ""):
self.dataLabels.append(field.label)
else:
self.dataLabels.append(name)
self.fieldList.append(field)
if isinstance(field, Quantity):
typeList.append((name, np.float64))
else:
raise ValueError('Unsupported type for a data series')
self.dtype = np.dtype(typeList)
if (visibleColumns is None):
self.visibleColumns = [n for n in range(len(self.dataLabels))]
else:
self.visibleColumns = visibleColumns
self.useHdfStorage = useHdfStorage
if (useHdfStorage == True):
if (storage is None):
raise ValueError('Storage field name is undefined')
self.storage = storage
@property
def default(self):
if (self.useHdfStorage == True):
return self.storage
else:
return np.zeros((1,), dtype = self.dtype)
def parseValue(self, value):
if (self.useHdfStorage == True):
if (isinstance(value, str)):
return value
else:
raise ValueError('Storage field name must be a string')
else:
if (isinstance(value, np.ndarray)):
return value
elif (isinstance(value, list)):
array = np.zeros((len(value),), dtype = self.dtype)
i = 0
for elem in value:
if isinstance(elem, list):
array[i] = tuple(elem)
else:
raise ArgumentTypeError('Trying to set row of View from non-list object')
i += 1
return array
else:
raise ArgumentTypeError('The value of View must be a numpy structured array or a list of lists')
def getValueRepr(self, value):
if (self.useHdfStorage == True):
return value
else:
return value.tolist()
def toFormDict(self):
fieldDict = super(DataSeriesView, self).toFormDict()
fieldDict['type'] = 'View'
jsonFieldList = []
for field in self.fieldList:
jsonFieldList.append(field.toFormDict())
fieldDict['fields'] = jsonFieldList
fieldDict['labels'] = self.dataLabels
fieldDict['visibleColumns'] = self.visibleColumns
fieldDict['useHdfStorage'] = self.useHdfStorage
return fieldDict
[docs]class TableView(DataSeriesView):
"""
Field for visualization of table data
"""
def __init__(self, structTuple = None, options = None, *args, **kwargs):
"""
:param structTuple: tuple defining the structure of the view data. \
It consists of ``(name, type)`` pairs, \
where ``name`` is the column name, and ``type`` is the :class:`Quantity` field type
Example::
( ('pressure', Quantity('Pressure')),
('temperature', Quantity('Temperature')) )
:param dict options: additional options to be passed
"""
if (options is None):
self.options = {}
else:
if (isinstance(options, dict)):
self.options = options
else:
raise ArgumentTypeError('Options passed to TableView must be a dictionary object')
super(TableView, self).__init__(structTuple = structTuple, *args, **kwargs)
def toFormDict(self):
fieldDict = super(TableView, self).toFormDict()
if ('title' not in self.options.keys()):
self.options['title'] = self.label
fieldDict['options'] = self.options
fieldDict['type'] = 'TableView'
return fieldDict
[docs]class PlotView(DataSeriesView):
"""
Field for creating interactive plots
"""
def __init__(self, structTuple = None, xlog = None, ylog = None, options = None, *args, **kwargs):
"""
:param structTuple: tuple defining the structure of the view data. \
It consists of ``(name, type)`` pairs, \
where ``name`` is the column name, and ``type`` is the :class:`Quantity` field type
Example::
( ('pressure', Quantity('Pressure')),
('temperature', Quantity('Temperature')) )
:param bool xlog: use logarithmic scale for x axis
:param bool ylog: use logarithmic scale for y axis
:param dict options: additional options to be passed
"""
if (xlog is None):
self.xlog = False
else:
self.xlog = xlog
if (ylog is None):
self.ylog = False
else:
self.ylog = ylog
if (options is None):
self.options = {}
else:
if (isinstance(options, dict)):
self.options = options
else:
raise ArgumentTypeError('Options passed to TableView must be a dictionary object')
super(PlotView, self).__init__(structTuple = structTuple, *args, **kwargs)
def toFormDict(self):
fieldDict = super(PlotView, self).toFormDict()
if ('title' not in self.options.keys()):
self.options['title'] = self.label
if ('width' not in self.options.keys()):
self.options['width'] = 700
if ('height' not in self.options.keys()):
self.options['height'] = 400
self.options['labels'] = self.dataLabels
if ('xlabel' not in self.options.keys()):
self.options['xlabel'] = self.dataLabels[0]
if ('ylabel' not in self.options.keys()):
self.options['ylabel'] = None
if ('labelsDivWidth' not in self.options.keys()):
self.options['labelsDivWidth'] = 400
self.options['labelsSeparateLines'] = True
if (self.xlog):
self.options['axes'] = { 'x' : {'logscale': True} }
if (self.ylog):
self.options['logscale'] = True
fieldDict['options'] = self.options
fieldDict['type'] = 'PlotView'
return fieldDict
# class VariationTable(TableView):
# def __init__(self, default = None, dataLabels = None, quantities = None,
# visibleColumns = None, options = None, parentCollection = None, fieldName = None, *args, **kwargs):
# if (fieldName is None):
# raise ValueError('VariationTable constructor must be passed the name of the Variation Table field.')
# if (parentCollection is None):
# raise ValueError('VariationTable constructor must be passed a parent MongoDB collection.')
# else:
# self.collection = parentCollection[fieldName]
#
# if (default is None):
# default = VariationTableValue(recordId = '', newRow = [], collection = self.collection, varTableName = fieldName)
# else:
# default = self.parseValue(default)
#
# super(VariationTable, self).__init__(default, dataLabels, quantities,
# visibleColumns, options, *args, **kwargs)
#
# def parseValue(self, valueObj):
# if (isinstance(valueObj, VariationTableValue)):
# return valueObj
# else:
# raise ArgumentTypeError('The value of VariationTable must be a VariationTableValue object')
#
# def getValueRepr(self, valueObj):
# extendedData = valueObj.value.tolist()
# extendedData.insert(0, self.dataLabels)
# return extendedData
[docs]class Image(Field):
"""
Field for displaying an image
"""
def __init__(self, default = "", width = None, height = None, *args, **kwargs):
"""
:param str default: path to image source
:param int width: image width in pixels
:param int height: image height in pixels
"""
super(Image, self).__init__(*args, **kwargs)
#if default is None:
# raise ArgumentError("Image field constructor must be passed string argument 'default' specifying image source path.")
#else:
self.default = default
self.width = width
self.height = height
def parseValue(self, value):
return value
def getValueRepr(self, value):
return value
def toFormDict(self):
fieldDict = super(Image, self).toFormDict()
fieldDict['type'] = 'Image'
fieldDict['width'] = self.width
fieldDict['height'] = self.height
return fieldDict
[docs]class MPLPlot(Field):
"""
Field for displaying a matplotlib plot
"""
def __init__(self, width = None, height = None, *args, **kwargs):
"""
:param int width: image width in pixels
:param int height: image height in pixels
"""
super(MPLPlot, self).__init__(*args, **kwargs)
self.width = width
self.height = height
@property
def default(self):
import pylab as plt
fig = plt.Figure(facecolor = 'white')
#fig.set_size_inches(self.width / float(fig.dpi), self.height / float(fig.dpi))
ax = fig.add_subplot(111)
return ax
def getValueRepr(self, value):
import os, tempfile
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
from SmoWeb.settings import MEDIA_ROOT
# Create the tmp file
fileHandler, absFilePath = tempfile.mkstemp('.png', dir = os.path.join(MEDIA_ROOT, 'tmp'))
imagePath = os.path.join('media', os.path.relpath(absFilePath, MEDIA_ROOT))
# Save the plot to the tmp file
canvas = FigureCanvas(value.figure)
canvas.print_png(absFilePath)
# Close the tmp file
os.close(fileHandler)
return imagePath
def parseValue(self, value):
return value
def toFormDict(self):
fieldDict = super(MPLPlot, self).toFormDict()
fieldDict['type'] = 'MPLPlot'
fieldDict['width'] = self.width
fieldDict['height'] = self.height
return fieldDict
class TextArea(Field):
"""
Represents a text area read-only field
"""
def __init__(self, default = None, width = None, height = None, showTooltip = False, *args, **kwargs):
"""
:param str default: default value
:param int width: the width of the text area
:param int height: the height of the text area
:param showTooltip: specifies if tooltip with the string value should be displayed
"""
super(TextArea, self).__init__(*args, **kwargs)
if (default is None):
self.default = "..."
else:
self.default = self.parseValue(default)
self.width = width
self.height = height
self.showTooltip = showTooltip
def parseValue(self, value):
return value
def getValueRepr(self, value):
return value
def toFormDict(self):
fieldDict = super(TextArea, self).toFormDict()
fieldDict['type'] = 'TextArea'
fieldDict['width'] = self.width
fieldDict['height'] = self.height
fieldDict['showTooltip'] = self.showTooltip
return fieldDict
[docs]class Port(Field):
"""
Used to create ports for linking to other components
"""
def __init__(self, klass, *args, **kwargs):
Field.__init__(self, *args, **kwargs)
self.klass = klass
[docs]class SubModelGroup(Field):
"""
Used to include field-group or supergroup from a sub-model
:param klass: the sub-model class
:param group: the sub-model group to be included
"""
def __init__(self, klass, content, *args, **kwargs):
Field.__init__(self, *args, **kwargs)
self.klass = klass
self.useSubmodelGroup = False
if (isinstance(content, basestring)):
groupName = content
self.useSubmodelGroup = True
# Search in basic and super groups
if (groupName in klass.declared_basicGroups):
self.group = klass.declared_basicGroups[groupName]
elif (groupName in klass.declared_superGroups):
self.group = klass.declared_superGroups[groupName]
else:
raise AttributeError('Class {} has no declared group {}'.format(klass.__name__, groupName))
elif (isinstance(content, list)):
fields = []
for fieldName in content:
field = klass.declared_fields[fieldName]
fields.append(field)
self.group = FieldGroup(fields)
self.group.name = 'FG'
def copyByName(self):
newObject = copy.copy(self)
return newObject
def resolve(self, resDict):
pass
[docs]class Group(object):
"""Abstract group class"""
# Tracks each time an instance is created. Used to retain order.
creation_counter = 0
def __init__(self, label = "", show = None):
self.label = label
self.show = show
if (self.show is not None):
self.show = self.show.replace('"', '\'')
# Increase the creation counter, and save our local copy.
self.creation_counter = Group.creation_counter
Group.creation_counter += 1
[docs]class BasicGroup(Group):
"""Abstract class for group of fields"""
def __init__(self, fields = None, hideContainer = False, *args, **kwargs):
super(BasicGroup, self).__init__(*args, **kwargs)
self.fields = []
if (fields is not None):
for field in fields:
self.fields.append(field)
self.hideContainer = hideContainer
def copyByName(self):
newObject = copy.copy(self)
newObject.fields = [field.name for field in self.fields]
return newObject
def resolve(self, resDict):
for i in range(len(self.fields)):
if (isinstance(self.fields[i], basestring)):
self.fields[i] = resDict[self.fields[i]]
[docs]class FieldGroup(BasicGroup):
"""Represents a group of fields of all basic types except for PlotView and TableView"""
pass
[docs]class ViewGroup(BasicGroup):
"""Represents a group of fields of type PlotView and/or TableView"""
pass
[docs]class SuperGroup(Group):
"""Represents a group of FieldGroup and/or ViewGroup groups"""
def __init__(self, groups = None, *args, **kwargs):
super(SuperGroup, self).__init__(*args, **kwargs)
self.groups = [] if (groups is None) else groups
def copyByName(self):
newObject = copy.copy(self)
newObject.groups = [group.name for group in self.groups]
return newObject
def resolve(self, resDict):
for i in range(len(self.groups)):
if (isinstance(self.groups[i], basestring)):
self.groups[i] = resDict[self.groups[i]]
[docs]class ModelView(object):
"""
Represents a view of the numerical model, comprised of super-groups and a bar of buttons for performing actions.
:param str ioType: the type of the view. It may be *input*,
requiring the user to enter input data, or *output*, displaying the results of the computation
:param list superGroups: a list of ``SuperGroup`` objects
:param ActionBar actionBar: an ``ActionBar`` object
:param bool autoFetch: used to specify whether the view should be loaded automatically at the client
"""
def __init__(self, ioType, superGroups, actionBar = None, autoFetch = False, keepDefaultDefs = False):
self.ioType = ioType
self.superGroups = superGroups
if actionBar is None:
actionBar = ActionBar()
self.actionBar = actionBar
self.autoFetch = autoFetch
self.keepDefaultDefs = keepDefaultDefs
def copyByName(self):
newObject = copy.copy(self)
newObject.superGroups = [group.name for group in self.superGroups]
return newObject
def resolve(self, resDict):
for i in range(len(self.superGroups)):
if (isinstance(self.superGroups[i], basestring)):
self.superGroups[i] = resDict[self.superGroups[i]]
[docs]class ModelDescription(object):
"""
Description of the numerical model
:param str text: the description text displayed on the model page
:param str asTooltip: custom tooltip description of the model. If ``None``, ``text`` is used instead.
"""
def __init__(self, text, asTooltip = None, show = True):
self.text = text
self.show = show
if (asTooltip is None):
self.asTooltip = text
else:
self.asTooltip = asTooltip