Commit ff6092f1 authored by M. Huang's avatar M. Huang
Browse files

unify attribute and metadata printing for ArrayDataset. also TableDataset

parent 4848df5e
......@@ -14,11 +14,14 @@ P_YAML = $(RESDIR)/$(PRODUCT).yml
B_YAML = $(RESDIR)/$(B_PRODUCT).yml
P_TEMPLATE = $(RESDIR)
B_TEMPLATE = $(RESDIR)
OTHERS = ArrayDataset_DataModel
OTHERS_PY=$(shell $(PYEXE) -S -c "print('$(OTHERS)'.lower())").py
OTHERS_YAML = $(RESDIR)/$(OTHERS).yml
ARRAYD = ArrayDataset_DataModel
ARRAYD_PY=$(shell $(PYEXE) -S -c "print('$(ARRAYD)'.lower())").py
ARRAYD_YAML = $(RESDIR)/$(ARRAYD).yml
TABLED = TableDataset_DataModel
TABLED_PY=$(shell $(PYEXE) -S -c "print('$(TABLED)'.lower())").py
TABLED_YAML = $(RESDIR)/$(TABLED).yml
py: $(PYDIR)/$(B_PY) $(PYDIR)/$(P_PY) $(PYDIR)/$(OTHERS_PY)
py: $(PYDIR)/$(B_PY) $(PYDIR)/$(P_PY) $(PYDIR)/$(ARRAYD_PY) $(PYDIR)/$(TABLED_PY)
$(PYDIR)/$(P_PY): $(PYDIR)/yaml2python.py $(P_YAML) $(P_TEMPLATE)/$(PRODUCT).template $(PYDIR)/$(B_PY)
$(PYEXE) -m fdi.dataset.yaml2python -y $(RESDIR) -t $(P_TEMPLATE) -o $(PYDIR) $(Y)
......@@ -27,7 +30,7 @@ $(PYDIR)/$(P_PY): $(PYDIR)/yaml2python.py $(P_YAML) $(P_TEMPLATE)/$(PRODUCT).tem
$(PYDIR)/$(B_PY): $(PYDIR)/yaml2python.py $(B_YAML) $(B_TEMPLATE)/$(B_PRODUCT).template
$(PYEXE) -m fdi.dataset.yaml2python -y $(RESDIR) -t $(P_TEMPLATE) -o $(PYDIR) $(Y)
$(PYDIR)/$(OTHERS_PY): $(PYDIR)/yaml2python.py $(OTHERS_YAML) $(RESDIR)/$(OTHERS).template
$(PYDIR)/$(ARRAYD_PY) $(PYDIR)/$(TABLED_PY): $(PYDIR)/yaml2python.py $(ARRAYD_YAML) $(RESDIR)/$(ARRAYD).template $(TABLED_YAML) $(RESDIR)/$(TABLED).template
$(PYEXE) -m fdi.dataset.yaml2python -y $(RESDIR) -t $(RESDIR) -o $(PYDIR) $(Y)
yamlupgrade:
......
......@@ -125,9 +125,12 @@ class ArrayDataset(GenericDataset, Iterable):
"""
self.getData().remove(*args, **kwargs)
def __repr__(self):
return self.toString(level=2)
def toString(self, level=0,
tablefmt='rst', tablefmt1='simple', tablefmt2='simple',
widths=None, matprint=None, trans=True, **kwds):
width=0, param_widths=None, matprint=None, trans=True, **kwds):
""" matprint: an external matrix print function
trans: print 2D matrix transposed. default is True.
"""
......@@ -137,19 +140,29 @@ class ArrayDataset(GenericDataset, Iterable):
cn = self.__class__.__name__
if level > 1:
vs, us, ts, ds, fs, gs, cs = exprstrs(self, '_data')
return cn + '(%s ' % vs + \
', '.join(str(k)+': '+str(v.value if hasattr(v, 'value') else v)
for k, v in self.meta.items()) + ')'
s = cn + '(' + \
self.meta.toString(
level=level,
tablefmt=tablefmt, tablefmt1=tablefmt1, tablefmt2=tablefmt2,
width=width, param_widths=param_widths,
**kwds)
if 0:
s += ', '.join(
str(k)+': '+str(v.value if hasattr(v, 'value') else v)
for k, v in self.meta.items()) + ')'
# set wiidth=0 level=2 to inhibit \n
vs, us, ts, ds, fs, gs, cs = exprstrs(
self, '_data', width=0, level=level)
# '{ %s (%s) <%s>, "%s", default %s, tcode=%s}' %\
#(vs, us, ts, ds, fs, cs)
return '%s data= %s)' % (s, vs)
s = '=== %s (%s) ===\n' % (cn, self.description if hasattr(
self, 'descripion') else '')
toshow = {'meta': self.__getstate__()['meta'],
'data': self.__getstate__()['data'],
}
s += mstr(toshow, level=level,
s += mstr(toshow, level=level, width=width,
tablefmt=tablefmt, tablefmt1=tablefmt1, tablefmt2=tablefmt2,
excpt=['description'], **kwds)
......@@ -183,3 +196,6 @@ class Column(ArrayDataset, ColumnListener):
table = TableDataset()
table.addColumn("Energy",Column(data=[1,2,3,4],description="desc",unit='eV'))
"""
def __init__(self, *args, typ_='Column', **kwds):
super().__init__(*args, typ_=typ_, **kwds)
......@@ -239,12 +239,10 @@ def value2parameter(name, value, descriptor):
)
elif im['data_type'] == 'finetime':
from .dateparameter import DateParameter
cs = im['typecode'] if 'typecode' in im else None
ret = DateParameter(value=value,
description=im['description'],
default=fs,
valid=gs,
typecode=cs
)
elif DataTypes[im['data_type']] in ['int', 'float', 'Vector', 'Vector2D', 'Quaternion']:
from .numericparameter import NumericParameter
......
......@@ -48,7 +48,7 @@ class BaseProduct( AbstractComposite, Copyable, Serializable, EventSender):
=====
BaseProduct class schema 1.6 inheriting [None].
Automatically generated from fdi/dataset/resources/BaseProduct.yml on 2021-06-08 12:39:02.779795.
Automatically generated from fdi/dataset/resources/BaseProduct.yml on 2021-06-17 22:21:01.798753.
Description:
FDI base class data model
......
......@@ -92,7 +92,7 @@ class GenericDataset(Dataset, Typed, DataWrapper):
def toString(self, level=0,
tablefmt='rst', tablefmt1='simple', tablefmt2='simple',
widths=None, matprint=None, trans=True, **kwds):
param_widths=None, width=0, matprint=None, trans=True, **kwds):
""" matprint: an external matrix print function
trans: print 2D matrix transposed. default is True.
"""
......@@ -102,7 +102,8 @@ class GenericDataset(Dataset, Typed, DataWrapper):
'{ %s, description = "%s", meta = %s }' % \
(str(self.data), str(self.description), self.meta.toString(
tablefmt=tablefmt, tablefmt1=tablefmt1, tablefmt2=tablefmt2,
level=level, matprint=matprint, trans=trans, **kwds))
level=level, width=width, param_widths=param_widths,
matprint=matprint, trans=trans, **kwds))
s = '=== %s (%s) ===\n' % (cn, self.description if hasattr(
self, 'descripion') else '')
......
......@@ -24,13 +24,13 @@ class DataContainer(Annotatable, Copyable, DeepEqual, Container, Sized):
data: a Container. Default is None.
"""
#print(__name__ + str(kwds))
super(DataContainer, self).__init__(**kwds)
if data is None or issubclass(data.__class__, Container):
self.setData(data)
else:
raise TypeError('DataContainer needs a Container to initialize, not ' +
type(data).__name__)
super().__init__(**kwds)
@property
def data(self):
......@@ -83,15 +83,20 @@ class DataWrapper(DataContainer, Quantifiable):
Implemented from AbstractDataWrapper.
"""
def __xrepr__(self):
return self.__class__.__name__ + \
'{ %s <%s>, description = "%s" }' % \
(str(self.getData()), str(self.unit), str(self.description))
def __init__(self, *args, **kwds):
"""
"""
super().__init__(*args, **kwds)
class DataWrapperMapper():
""" Object holding a map of data wrappers. """
def __init__(self, *args, **kwds):
"""
"""
super().__init__(*args, **kwds)
def getDataWrappers(self):
""" Gives the data wrappers, mapped by name. """
return self._sets
......@@ -16,14 +16,21 @@ class DateParameter(Parameter, Typecoded):
""" has a FineTime as the value.
"""
def __init__(self, value=None, description='UNKNOWN', default=0, valid=None, typecode='Q', **kwds):
def __init__(self,
value=None,
description='UNKNOWN',
default=0,
valid=None,
**kwds):
"""
if value and typecode are both given, typecode will be overwritten by value.format.
Set up a parameter whose value is a point in TAI time.
"""
self.setTypecode(typecode if typecode else FineTime.DEFAULT_FORMAT)
# 'Q' is unsigned long long (8byte) integer.
typecode = 'Q'
# this will set default then set value.
super(DateParameter, self).__init__(
value=value, description=description, typ_='finetime', default=default, valid=valid, **kwds)
value=value, description=description, typ_='finetime', default=default, valid=valid, typecode=typecode, **kwds)
def setValue(self, value):
""" accept any type that a FineTime does.
......
......@@ -171,7 +171,7 @@ class FineTime(Copyable, DeepEqual, Serializable):
width=0, **kwds):
""" Returns a String representation of this object according to self.format.
prints like 2019-02-17T12:43:04.577000 TAI(...)"""
tais = str(self.tai) if hasattr(self, 'tai') else 'Unknown_TAI'
tais = str(self.tai) if hasattr(self, 'tai') else 'Unknown'
if level == 0:
if width:
tstr = self.isoutc(
......@@ -188,6 +188,8 @@ class FineTime(Copyable, DeepEqual, Serializable):
else:
tstr = self.isoutc() + ' TAI(%s)' % tais
s = tstr
elif level == 2:
s = self.isoutc() + ' TAI(%s)' % tais
else:
s = tais
return s
......
......@@ -14,7 +14,7 @@ from .annotatable import Annotatable
from .classes import Classes
from .typed import Typed
from .invalid import INVALID
from ..utils.common import exprstrs, wls, mstr, t2l
from ..utils.common import exprstrs, wls, bstr, t2l
from tabulate import tabulate
......@@ -27,35 +27,44 @@ logger = logging.getLogger(__name__)
# logger.debug('level %d' % (logger.getEffectiveLevel()))
ParameterClasses = {
'AbstractParameter': dict(value=None,
description='UNKNOWN'),
'Parameter': dict(value=None,
description='UNKNOWN',
typ_='',
default=None,
valid=None),
'NumericParameter': dict(value=None,
description='UNKNOWN',
typ_='',
unit=None,
default=None,
valid=None,
typecode=None),
'DateParameter': dict(value=None,
description='UNKNOWN',
default='',
valid=None,
typecode=None),
'StringParameter': dict(value=None,
description='UNKNOWN',
default='',
valid=None,
typecode='B'),
Parameter_Attr_Defaults = {
'AbstractParameter': dict(
value=None,
description='UNKNOWN'
),
'Parameter': dict(
value=None,
description='UNKNOWN',
typ_='',
default=None,
valid=None
),
'NumericParameter': dict(
value=None,
description='UNKNOWN',
typ_='',
default=None,
unit=None,
valid=None,
typecode=None
),
'DateParameter': dict(
value=None,
description='UNKNOWN',
default=0,
valid=None,
),
'StringParameter': dict(
value=None,
description='UNKNOWN',
default='',
valid=None,
typecode=None
),
}
......@@ -84,7 +93,10 @@ class AbstractParameter(Annotatable, Copyable, DeepEqual, DatasetEventSender, Se
Default value=None, description='UNKNOWN'
"""
def __init__(self, value=None, description='UNKNOWN', **kwds):
def __init__(self,
value=None,
description='UNKNOWN',
**kwds):
""" Constructed with no argument results in a parameter of
None value and 'UNKNOWN' description ''.
With a signle argument: arg -> value, 'UNKNOWN' as default-> description.
......@@ -96,6 +108,7 @@ class AbstractParameter(Annotatable, Copyable, DeepEqual, DatasetEventSender, Se
description=description, **kwds)
self.setValue(value)
self._defaults = Parameter_Attr_Defaults[self.__class__.__name__]
def accept(self, visitor):
""" Adds functionality to classes of this type."""
......@@ -251,7 +264,13 @@ class Parameter(AbstractParameter, Typed):
value=default, description='UNKNOWN'
"""
def __init__(self, value=None, description='UNKNOWN', typ_='', default=None, valid=None, **kwds):
def __init__(self,
value=None,
description='UNKNOWN',
typ_='',
default=None,
valid=None,
**kwds):
""" invoked with no argument results in a parameter of
None value and 'UNKNOWN' description ''. typ_ DataTypes[''], which is None.
With a signle argument: arg -> value, 'UNKNOWN'-> description. ParameterTypes-> typ_, hex values have integer typ_.
......@@ -515,9 +534,9 @@ f With two positional arguments: arg1-> value, arg2-> description. Parame
def toString(self, level=0,
tablefmt='rst', tablefmt1='simple', tablefmt2='simple',
alist=False, **kwds):
width=0, alist=False, **kwds):
ret = exprstrs(self, level=level, **kwds)
ret = exprstrs(self, level=level, width=width, **kwds)
if alist:
return ret
vs, us, ts, ds, fs, gs, cs = ret
......@@ -553,12 +572,13 @@ class MetaData(Composite, Copyable, Serializable, ParameterListener, DatasetEven
Note that replacing a parameter with the same name,
will keep the order. """
def __init__(self, copy=None, **kwds):
def __init__(self, copy=None, defaults=None, **kwds):
super(MetaData, self).__init__(**kwds)
if copy:
# not implemented ref https://stackoverflow.com/questions/10640642/is-there-a-decent-way-of-creating-a-copy-constructor-in-python
logger.error('use copy.copy() insteadof MetaData(copy)')
raise ValueError('use copy.copy() insteadof MetaData(copy)')
else:
self._defaults = [] if defaults is None else defaults
return
def accept(self, visitor):
......@@ -592,7 +612,8 @@ class MetaData(Composite, Copyable, Serializable, ParameterListener, DatasetEven
def __repr__(self):
return ydump(self.__getstate__(), default_flow_style=True)
# return ydump(self.__getstate__(), default_flow_style=True)
return self.toString(level=3)
def remove(self, name):
""" add eventhandling """
......@@ -613,19 +634,19 @@ class MetaData(Composite, Copyable, Serializable, ParameterListener, DatasetEven
def toString(self, level=0,
tablefmt='rst', tablefmt1='simple', tablefmt2='simple',
widths=None, **kwds):
param_widths=None, width=0, **kwds):
""" return string representation of metada.
level: 0 is the most detailed, 2 is the least,
tablefmt: format string in packae ``tabulate``.
widths: controls how the attributes of every parameter are displayed in the table cells. If is set to -1, there is no cell-width limit. For finer control set a dictionary of parameter attitute names and how many characters wide its tsble cell is, 0 for ommiting the attributable. Default is
tablefmt: format string in packae ``tabulate``, for level==0, tablefmt1 for level1, tablefmt2: format of 2D table data.
param_widths: controls how the attributes of every parameter are displayed in the table cells. If is set to -1, there is no cell-width limit. For finer control set a dictionary of parameter attitute names and how many characters wide its tsble cell is, 0 for ommiting the attributable. Default is
``{'name': 8, 'value': 17, 'unit': 7, 'type': 8,
'valid': 25, 'default': 17, 'code': 4, 'description': 15}``
"""
if widths is None:
widths = {'name': 15, 'value': 20, 'unit': 7, 'type': 8,
'valid': 23, 'default': 17, 'code': 4, 'description': 21}
if param_widths is None:
param_widths = {'name': 15, 'value': 20, 'unit': 7, 'type': 8,
'valid': 23, 'default': 17, 'code': 4, 'description': 21}
tab = []
# N parameters per row for level 1
N = 3
......@@ -635,17 +656,23 @@ class MetaData(Composite, Copyable, Serializable, ParameterListener, DatasetEven
att = {}
for (k, v) in self._sets.items():
att['name'] = str(k)
# limit cell width for level=0,1
att['value'], att['unit'], att['type'], att['description'],\
att['default'], att['valid'], att['code'] = v.toString(
level=level, width=0 if level > 1 else 1,
tablefmt=tablefmt, tablefmt1=tablefmt1, tablefmt2=tablefmt2,
alist=True)
if level == 0:
l = tuple(att[n] for n in MetaHeaders) if widths == -1 else \
tuple(wls(att[n], w) for n, w in widths.items() if w != 0)
l = tuple(att[n] for n in MetaHeaders) if param_widths == -1 else \
tuple(wls(att[n], w)
for n, w in param_widths.items() if w != 0)
tab.append(l)
elif level == 1:
ps = '%s= %s' % (att['name'], att['value'])
# s += mstr(self, level=level, tablefmt = tablefmt, tablefmt=tablefmt, tablefmt1=tablefmt1, tablefmt2=tablefmt2,depth=1, **kwds)
tab.append(wls(ps, 80//N))
# s += mstr(self, level=level, tablefmt = tablefmt, \
# tablefmt=tablefmt, tablefmt1=tablefmt1, \
# tablefmt2=tablefmt2,depth=1, **kwds)
if 0:
row.append(wls(ps, 80//N))
i += 1
......@@ -653,17 +680,20 @@ class MetaData(Composite, Copyable, Serializable, ParameterListener, DatasetEven
tab.append(row)
i, row = 0, []
else:
tab.append(att['name'])
n = att['name']
if n in self._defaults and self._defaults[n]['default'] == v.value:
pass
else:
ps = '%s=%s' % (att['name'], att['value'])
#tab.append(wls(ps, 80//N))
tab.append(ps)
lsnr = self.listeners.toString(level=level,
tablefmt=tablefmt, tablefmt1=tablefmt1,
tablefmt2=tablefmt2,
**kwds)
# tab.append(att['name'])
# write out the table
if level == 0:
headers = MetaHeaders if widths == -1 else \
[n for n, w in zip(MetaHeaders, widths) if w != 0]
headers = MetaHeaders if param_widths == -1 else \
[n for n, w in zip(MetaHeaders, param_widths) if w != 0]
fmt = tablefmt
s += tabulate(tab, headers=headers, tablefmt=fmt, missingval='',
disable_numparse=True)
......@@ -674,8 +704,21 @@ class MetaData(Composite, Copyable, Serializable, ParameterListener, DatasetEven
s += tabulate(t, headers=headers, tablefmt=fmt, missingval='',
disable_numparse=True)
else:
s += ', '.join(tab)
return '%s, listeners = %s' % (s, lsnr)
s = ', '.join(tab) if len(tab) else 'Default Meta'
if len(self.listeners):
l = ', listeners = %s' % \
self.listeners.toString(level=level,
tablefmt=tablefmt, tablefmt1=tablefmt1,
tablefmt2=tablefmt2,
**kwds)
else:
l = '.'
return s + l
lsnr = self.listeners.toString(level=level,
tablefmt=tablefmt, tablefmt1=tablefmt1,
tablefmt2=tablefmt2,
**kwds)
return '\n%s\n%s-listeners = %s' % (s, cn, lsnr) if len(tab) else \
'%s %s-listeners = %s' % ('(No Parameter.)', cn, lsnr)
......
......@@ -14,9 +14,12 @@ class MetaDataHolder(object):
"""
def __init__(self, meta=None, **kwds):
""" Adds MetaData to the class.
with defaults set to self.zInfo['metadata'].
"""
if meta is None:
meta = metadata.MetaData()
defs = self.zInfo['metadata'] if hasattr(self, 'zInfo') else None
meta = metadata.MetaData(defaults=defs)
self.setMeta(meta)
super(MetaDataHolder, self).__init__(**kwds)
......
......@@ -38,6 +38,11 @@ def ndprint(data, trans=True, maxElem=50, **kwds):
"""
if data is None:
return 'None'
try:
if len(data) == 0:
return str(data)
except TypeError:
pass
# dim, maxdim, and s are to be used as nonlocal variables in run()
# to overcome python2's lack of nonlocal type this method is usded
......
......@@ -13,12 +13,24 @@ logger = logging.getLogger(__name__)
class NumericParameter(Parameter, Quantifiable):
""" has a number as the value, a unit, and a typecode.
""" A Parameter that has a number as the value, a unit, and a typecode.
"""
def __init__(self, value=None, description='UNKNOWN', typ_='', default=None, valid=None, **kwds):
def __init__(self,
value=None,
description='UNKNOWN',
typ_='',
default=None,
unit=None,
valid=None,
typecode=None,
**kwds):
""" Set up a parameter whose value is a or a list of numbers.
typ_: type of the parameter value.
"""
super(NumericParameter, self).__init__(
value=value, description=description, typ_=typ_, default=default, valid=valid, **kwds)
value=value, description=description, typ_=typ_, default=default, unit=unit, valid=valid, typecode=typecode, **kwds)
def __getstate__(self):
""" Can be encoded with serializableEncoder """
......
......@@ -20,7 +20,7 @@ logger = logging.getLogger(__name__)
class Product(BaseProduct):
""" Product class schema 1.6 inheriting ['BaseProduct'].
Automatically generated from fdi/dataset/resources/Product.yml on 2021-06-08 12:39:02.808289.
Automatically generated from fdi/dataset/resources/Product.yml on 2021-06-16 15:34:51.375313.
Description:
Project level product
......
......@@ -19,9 +19,8 @@ class Quantifiable(Typecoded):
"""
self.setUnit(unit)
self.setTypecode(typecode)
#print(__name__ + str(kwds))
super(Quantifiable, self).__init__(**kwds)
super(Quantifiable, self).__init__(typecode=typecode, **kwds)
@property
def unit(self):
......
......@@ -159,6 +159,7 @@ class Serializable(object):
"""
def __init__(self, **kwds):
super(Serializable, self).__init__(**kwds)
sc = self.__class__
# print('@@@ ' + sc.__name__, str(issubclass(sc, dict)))
......
......@@ -14,10 +14,16 @@ class StringParameter(Parameter, Typecoded):
""" has a unicode string as the value, a typecode for length and char.
"""
def __init__(self, value=None, description='UNKNOWN', valid=None, default='', typecode='B', **kwds):
def __init__(self,
value=None,
description='UNKNOWN',
default='',
valid=None,
typecode='B',
**kwds):
self.setTypecode(typecode)
super(StringParameter, self).__init__(
value=value, description=description, typ_='string', default=default, valid=valid, **kwds)
value=value, description=description, typ_='string', default=default, valid=valid, typecode=typecode, **kwds)
def __getstate__(self):
""" Can be encoded with serializableEncoder """
......
......@@ -8,7 +8,7 @@ from .dataset import Dataset
from .indexed import Indexed
try:
from .arraydataset_datamodel import Model
from .tabledataset_datamodel import Model
except ImportError:
Model = {'metadata': {}}
......@@ -126,9 +126,21 @@ class TableDataset(Dataset, TableModel):
"""
self._list = []
super(TableDataset, self).__init__(
**kwds) # initialize data, meta, unit
self.setData(data)
# collect MDPs from args-turned-local-variables.
metasToBeInstalled = OrderedDict(
itertools.filterfalse(
lambda x: x[0] in ('self', '__class__', 'zInfo', 'kwds'),
locals().items())
)
global Model
if zInfo is None:
zInfo = Model