Source code for nxstools.nxsparser
# This file is part of nexdatas - Tango Server for NeXus data writer
#
# Copyright (C) 2012-2018 DESY, Jan Kotanski <jkotan@mail.desy.de>
#
# nexdatas is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# nexdatas is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with nexdatas. If not, see <http://www.gnu.org/licenses/>.
#
""" Command-line tool for ascess to the nexdatas configuration server """
import sys
import json
import fnmatch
import xml.etree.ElementTree as et
import lxml.etree
from lxml.etree import XMLParser
if sys.version_info > (3,):
unicode = str
def _parseString(text):
if sys.version_info > (3,):
return et.fromstring(
bytes(text, "UTF-8"),
parser=XMLParser(collect_ids=False))
else:
return et.fromstring(
text,
parser=XMLParser(collect_ids=False))
def _tostr(text):
""" converts text to str type
:param text: text
:type text: :obj:`bytes` or :obj:`unicode`
:returns: text in str type
:rtype: :obj:`str`
"""
if hasattr(text, "tostring"):
text = text.tostring()
if isinstance(text, str):
return text
elif sys.version_info > (3,):
return str(text, encoding="utf8")
else:
return str(text)
def _toxml(node):
""" provides xml content of the whole node
:param node: DOM node
:type node: :class:`xml.dom.Node`
:returns: xml content string
:rtype: :obj:`str`
"""
if sys.version_info > (3,):
xml = _tostr(et.tostring(node, encoding='unicode', method='xml'))
else:
xml = _tostr(et.tostring(node, encoding='utf8', method='xml'))
if xml.startswith("<?xml version='1.0' encoding='utf8'?>"):
xml = str(xml[38:])
return xml
[docs]class ESRFConverter(object):
""" ESRF xml configuration converter """
def __init__(self):
""" constructor """
self.__allowed_tags = [
"group", "field", "link", "attribute", "doc",
"strategy", "datasource"]
self.__amap = {
"NX_class": "type",
"groupName": "name",
"NAPItype": "type",
"ref": "target"
}
[docs] def convert(self, text):
""" converts ESRF xml configuration to nxsdatawriter format
:param text: input xml string
:type text: :obj:`str`
:returns: output xml string
:rtype: :obj:`str`
"""
tree = _parseString(text)
if tree.tag == "definition":
definition = tree
else:
definition = lxml.etree.Element('definition')
definition.append(tree)
self._convert(tree)
return _toxml(definition)
def _convert(self, tree):
""" converts ESRF xml etree configuration to nxsdatawriter format
:param tree: etree Element node
:type tree: :class:`lxml.etree.Element`
"""
if tree.tag not in self.__allowed_tags:
if "name" not in tree.attrib.keys():
tree.attrib["name"] = tree.tag
tree.tag = "field"
for ikey, okey in self.__amap.items():
if ikey in tree.attrib.keys() \
and okey not in tree.attrib.keys():
tree.attrib[okey] = tree.attrib.pop(ikey)
if 'ESRF_description' in tree.attrib.keys():
text = tree.attrib.pop('ESRF_description')
doc = lxml.etree.Element('doc')
doc.text = text
tree.append(doc)
if 'record' in tree.attrib.keys():
text = tree.attrib.pop('record')
mode = lxml.etree.Element("strategy")
mode.attrib["mode"] = text.upper()
tree.append(mode)
for tr in tree:
self._convert(tr)
[docs]class ParserTools(object):
""" configuration server adapter
"""
@classmethod
def _getPureText(cls, node):
""" collects text from text child nodes
:param node: parent node
:type node: :obj:`xml.etree.ElementTree.Element`
"""
if node is not None:
tnodes = ([node.text] if node.text else []) \
+ [child.tail for child in node if child.tail]
return unicode("".join(tnodes)).strip()
return ""
@classmethod
def _getText(cls, node):
""" provides xml content of the node
:param node: DOM node
:type node: :class:`lxml.etree.Element`
:returns: xml content string
:rtype: :obj:`str`
"""
if not node:
return
xmlc = _toxml(node)
start = xmlc.find('>')
end = xmlc.rfind('<')
if start == -1 or end < start:
return ""
return xmlc[start + 1:end].replace("<", "<").replace(">", ">"). \
replace(""", "\"").replace("&", "&")
[docs] @classmethod
def getRecord(cls, node):
""" fetches record name or query from datasource node
:param node: datasource node
:type node: :class:`lxml.etree.Element`
:returns: record name or query
:rtype: :obj:`str`
"""
withRec = ["CLIENT", "TANGO"]
withResult = ["PYEVAL"]
withQuery = ["DB"]
if node.tag == 'datasource':
dsource = node
else:
dsource = node.find(".//datasource")
dstype = dsource.attrib["type"]
res = ''
if dstype and dstype in withRec:
host = None
port = None
dname = None
rname = None
member = None
device = dsource.findall("device")
if device is not None and len(device) > 0:
host = device[0].get("hostname")
port = device[0].get("port")
dname = device[0].get("name")
member = device[0].get("member")
surfix = ""
prefix = ""
if member or member != 'attribute':
if member == 'property':
prefix = '@'
elif member == 'command':
surfix = '()'
record = dsource.findall("record")
if record is not None and len(record) > 0:
rname = record[0].get("name")
if rname:
if dname:
if host:
if not port:
port = '10000'
res = '%s:%s/%s/%s%s%s' % (
host, port, dname, prefix, rname, surfix)
else:
res = '%s/%s%s%s' % (dname, prefix, rname, surfix)
else:
res = rname
return res
elif dstype and dstype in withQuery:
query = dsource.find(".//query")
if len(query.text) and query.text.strip():
return query.text.strip() or ""
elif dstype and dstype in withResult:
result = dsource.findall("result")
cresult = dsource.find(".//result")
if result is not None and len(result) > 0:
rname = result[0].get("name", "result")
tres = "ds.%s = " % rname
if len(cresult.text) and cresult.text.strip():
teres = cresult.text.strip() or ""
lres = teres.split("\n")
for re in lres:
if re.strip().startswith(tres):
res = re.strip()[len(tres):]
return res
[docs] @classmethod
def mergeDefinitions(cls, xmls):
""" merges the xmls list of definitions xml strings
to one output xml string
:param xmls: a list of xml string with definitions
:type xmls: :obj:`list` <:obj:`str`>
:returns: one output xml string
:rtype: :obj:`str`
"""
rxml = ""
if xmls:
indom1 = _parseString(xmls[0])
for xmlc in xmls[1:]:
definition = _parseString(xmlc)
if definition.tag == "definition":
definitions = [definition]
else:
definitions = definition.findall("definition")
for defin in definitions:
for tag in defin:
indom1.append(tag)
rxml = _toxml(indom1)
return rxml
[docs] @classmethod
def parseDataSources(cls, xmlc):
""" provides datasources and its records from xml string
:param xmlc: xml string
:type xmlc: :obj:`str`
:returns: list of datasource descriptions
:rtype: :obj:`list` <:obj:`dict` <:obj:`str`, :obj:`str`>>
"""
indom = _parseString(xmlc)
return cls.__getDataSources(indom)
@classmethod
def __getDataSources(cls, node, direct=False):
""" provides datasources and its records from xml string
:param xmlc: xml string
:type xmlc: :obj:`str`
:returns: list of datasource descriptions
:rtype: :obj:`list` <:obj:`dict` <:obj:`str`, :obj:`str`>>
"""
dss = []
if direct:
dss.extend(node.findall("datasource"))
else:
if node.tag == "datasource":
dss.append(node)
dss.extend(node.findall(".//datasource"))
dslist = []
for ds in dss:
if ds.tag == 'datasource':
dstype = ds.attrib["type"]
dsname = ds.attrib["name"]
record = cls.getRecord(ds)
dslist.append({
"source_type": dstype,
"source_name": dsname,
"source": record,
})
return dslist
@classmethod
def __getPath(cls, node):
""" provides node path
:param node: etree node
:type node: :class:`lxml.etree.Element`
:returns: node path
:rtype: :obj:`str`
"""
name = cls.__getAttr(node, "name")
if not name:
return ""
attr = False
while node.getparent() is not None:
onode = node
node = node.getparent()
if onode.tag == "attribute":
attr = True
else:
attr = False
if node.tag not in ["group", "field"]:
return name
else:
gname = cls.__getAttr(node, "name")
if not gname:
gname = cls.__getAttr(node, "type")
if len(gname) > 2:
gname = gname[2:]
if attr:
name = gname + "@" + name
else:
name = gname + "/" + name
attr = False
return name
@classmethod
def __getFullPath(cls, node):
""" provides node path
:param node: etree node
:type node: :class:`lxml.etree.Element`
:returns: node path
:rtype: :obj:`str`
"""
name = cls.__getAttr(node, "name")
if not name:
return ""
attr = False
while node.getparent() is not None:
onode = node
node = node.getparent()
if onode.tag == "attribute":
attr = True
else:
attr = False
if node.tag not in ["group", "field"]:
return name
else:
gname = cls.__getAttr(node, "name")
nxtype = cls.__getAttr(node, "type")
if not gname:
if len(nxtype) > 2:
gname = nxtype[2:]
if attr:
name = gname + "@" + name
elif nxtype:
name = "%s:%s/%s" % (gname, nxtype, name)
else:
name = gname + "/" + name
attr = False
return name
@classmethod
def __getAttr(cls, node, name, tag=False):
""" provides value of attirbute
:param node: etree node
:type node: :class:`lxml.etree.Element`
:returns: attribute value
:rtype: :obj:`str`
"""
if name in node.attrib:
return node.attrib[name]
elif tag:
if node.tag == "attribute":
atnodes = [node]
else:
atnodes = []
atnodes.extend(node.findall("attribute"))
text = None
for at in atnodes:
if cls.__getAttr(at, "name") == name:
text = str(cls._getPureText(at)).strip()
if not text:
dss = cls.__getDataSources(at)
text = " ".join(["$datasources.%s" % ds for ds in dss])
return text
else:
return None
@classmethod
def __getAllAttr(cls, node, exclude=None):
""" provides value of attirbute
:param node: etree node
:type node: :class:`lxml.etree.Element`
:returns: attribute dictionary
:rtype: :obj:<:obj:`str`, :obj:`any`>
"""
res = {}
exclude = exclude or []
for name in node.attrib.keys():
if name not in exclude:
res[name] = node.attrib[name]
return res
@classmethod
def __getShape(cls, node):
""" provides node shape
:param node: etree node
:type node: :class:`lxml.etree.Element`
:returns: shape list
:rtype: :obj:`list` <:obj:`int`>
"""
rank = int(node.attrib["rank"])
# shape = ['*'] * rank
shape = [None] * rank
dims = node.findall("dim")
for dim in dims:
index = int(dim.attrib["index"])
if "value" in dim.attrib:
try:
value = int(dim.attrib["value"])
except ValueError:
value = str(dim.attrib["value"])
shape[index - 1] = value
else:
dss = dim.findall("datasource")
if dss:
value = dss[0].get("name")
if not value:
value = '__unnamed__'
shape[index - 1] = "$datasources.%s" % value
else:
tnodes = " ".join(
([dim.text] if dim.text else []) +
[child.tail for child in dim if child.tail])
value = ("".join(tnodes)).strip()
try:
value = int(value)
except Exception:
value = value.strip()
if not value:
value = None
shape[index - 1] = value
return shape
@classmethod
def __getChildrenByTagName(cls, parent, name):
""" provides direct children by tag name
:param parent: parent node
:type parent: :class:`lxml.etree.Element`
:param name: tag name
:type name: :obj:`str`
:returns: list of children
:rtype: :obj:`list` <:class:`lxml.etree.Element`>
"""
return [ch for ch in parent.findall(name) if ch.tag == name]
[docs] @classmethod
def parseFields(cls, xmlc):
""" provides datasources and its records from xml string
:param xmlc: xml string
:type xmlc: :obj:`str`
:returns: list of datasource descriptions
:rtype: :obj:`list` < :obj:`dict` <:obj:`str`, `any`> >
"""
tagname = "field"
indom = _parseString(xmlc)
nodes = []
if indom.tag == tagname:
nodes.append(indom)
nodes.extend(indom.findall(".//%s" % tagname))
taglist = []
for nd in nodes:
if nd.tag == tagname:
nxtype = cls.__getAttr(nd, "type")
units = cls.__getAttr(nd, "units")
value = cls._getPureText(nd) or None
trtype = cls.__getAttr(nd, "transformation_type", True)
trvector = cls.__getAttr(nd, "vector", True)
troffset = cls.__getAttr(nd, "offset", True)
trdependson = cls.__getAttr(nd, "depends_on", True)
nxpath = cls.__getPath(nd)
fullnxpath = cls.__getFullPath(nd)
dnodes = cls.__getChildrenByTagName(nd, "dimensions")
shape = cls.__getShape(dnodes[0]) if dnodes else None
docnodes = cls.__getChildrenByTagName(nd, "doc")
doc = cls._getPureText((docnodes[0])) if docnodes else None
stnodes = cls.__getChildrenByTagName(nd, "strategy")
strategy = cls.__getAttr(stnodes[0], "mode") \
if stnodes else None
sfdinfo = {
"strategy": strategy,
"nexus_path": nxpath,
"full_nexus_path": fullnxpath,
}
fdinfo = {
"nexus_type": nxtype,
"units": units,
"shape": shape,
"trans_type": trtype,
"trans_vector": trvector,
"trans_offset": troffset,
"depends_on": trdependson,
"value": value,
"doc": doc
}
fdinfo.update(sfdinfo)
otherinfo = cls.__getAllAttr(nd, list(fdinfo.keys()))
fdinfo.update(otherinfo)
dss = cls.__getDataSources(nd, direct=True)
if dss:
for ds in dss:
ds.update(fdinfo)
taglist.append(ds)
nddss = cls.__getChildrenByTagName(nd, "datasource")
for ndds in nddss:
sdss = cls.__getDataSources(ndds, direct=True)
if sdss:
for sds in sdss:
sds.update(sfdinfo)
sds["source_name"] \
= "\\" + sds["source_name"]
taglist.append(sds)
else:
taglist.append(fdinfo)
return taglist
[docs] @classmethod
def parseAttributes(cls, xmlc):
""" provides datasources and its records from xml string
:param xmlc: xml string
:type xmlc: :obj:`str`
:returns: list of datasource descriptions
:rtype: :obj:`list` < :obj:`dict` <:obj:`str`, `any`> >
"""
tagname = "attribute"
indom = _parseString(xmlc)
nodes = []
if indom.tag == tagname:
nodes.append(indom)
nodes.extend(indom.findall(".//%s" % tagname))
taglist = []
for nd in nodes:
if nd.tag == tagname:
nxtype = cls.__getAttr(nd, "type")
units = cls.__getAttr(nd, "units")
value = cls._getPureText(nd) or None
trtype = cls.__getAttr(nd, "transformation_type", True)
trvector = cls.__getAttr(nd, "vector", True)
troffset = cls.__getAttr(nd, "offset", True)
trdependson = cls.__getAttr(nd, "depends_on", True)
nxpath = cls.__getPath(nd)
fullnxpath = cls.__getFullPath(nd)
dnodes = cls.__getChildrenByTagName(nd, "dimensions")
shape = cls.__getShape(dnodes[0]) if dnodes else None
stnodes = cls.__getChildrenByTagName(nd, "strategy")
strategy = cls.__getAttr(stnodes[0], "mode") \
if stnodes else None
sfdinfo = {
"strategy": strategy,
"nexus_path": nxpath,
"full_nexus_path": fullnxpath,
}
fdinfo = {
"nexus_type": nxtype,
"units": units,
"shape": shape,
"trans_type": trtype,
"trans_vector": trvector,
"trans_offset": troffset,
"depends_on": trdependson,
"value": value
}
fdinfo.update(sfdinfo)
dss = cls.__getDataSources(nd, direct=True)
if dss:
for ds in dss:
ds.update(fdinfo)
taglist.append(ds)
nddss = cls.__getChildrenByTagName(nd, "datasource")
for ndds in nddss:
sdss = cls.__getDataSources(ndds, direct=True)
if sdss:
for sds in sdss:
sds.update(sfdinfo)
sds["source_name"] \
= "\\" + sds["source_name"]
taglist.append(sds)
else:
taglist.append(fdinfo)
return taglist
[docs] @classmethod
def parseLinks(cls, xmlc):
""" provides datasources and its records from xml string
:param xmlc: xml string
:type xmlc: :obj:`str`
:returns: list of datasource descriptions
:rtype: :obj:`list` < :obj:`dict` <:obj:`str`, `any`> >
"""
tagname = "link"
indom = _parseString(xmlc)
nodes = []
if indom.tag == tagname:
nodes.append(indom)
nodes.extend(indom.findall(".//%s" % tagname))
taglist = []
for nd in nodes:
if nd.tag == tagname:
target = cls.__getAttr(nd, "target")
value = cls._getPureText(nd) or None
nxpath = cls.__getPath(nd)
stnodes = cls.__getChildrenByTagName(nd, "strategy")
strategy = cls.__getAttr(stnodes[0], "mode") \
if stnodes else None
sfdinfo = {
"strategy": strategy,
"nexus_path": "[%s]" % nxpath,
}
fdinfo = {
"value": value
}
fdinfo.update(sfdinfo)
dss = cls.__getDataSources(nd, direct=True)
if dss:
for ds in dss:
ds.update(fdinfo)
taglist.append(ds)
nddss = cls.__getChildrenByTagName(nd, "datasource")
for ndds in nddss:
sdss = cls.__getDataSources(ndds, direct=True)
if sdss:
for sds in sdss:
sds.update(sfdinfo)
sds["source_name"] \
= "\\" + sds["source_name"]
taglist.append(sds)
else:
taglist.append(fdinfo)
if target and target.strip():
fdinfo2 = dict(fdinfo)
fdinfo2["nexus_path"] = "\\-> %s" % target
taglist.append(fdinfo2)
return taglist
[docs] @classmethod
def parseRecord(cls, xmlc):
""" provides source record from xml string
:param xmlc: xml string
:type xmlc: :obj:`str`
:returns: source record
:rtype: :obj:`str`
"""
indom = _parseString(xmlc)
return cls.getRecord(indom)
[docs]class TableTools(object):
""" configuration server adapter
"""
def __init__(self, description, nonone=None, headers=None, filters=None):
""" constructor
:param description: description list
:type description: :obj:`list` <:obj:`str`>
:param nonone: list of parameters which have to exist to be shown
:type nonone: :obj:`list` <:obj:`str`>
:param headers: list of output parameters
:type headers: :obj:`list` <:obj:`str`>
:param filters: filters for first column names
:type filters: :obj:`list` <:obj:`str`>
"""
#: (:obj:`list` <:obj:`str`>)
#: list of parameters which have to exist to be shown
self.__nonone = nonone or []
#: (:obj:`list` <:obj:`str`>)
#: description list
self.__description = []
#: (:obj:`dict` <:obj:`str` , :obj:`int`>) header sizes
self.__hdsizes = {}
#: (:obj:`list` <:obj:`str`>) table headers
self.headers = [
'nexus_path',
'nexus_type',
'strategy',
'shape',
'units',
'depends_on',
'trans_type',
'trans_vector',
'trans_offset',
'source_name',
'source_type',
'source',
'value'
]
#: (:obj:`list` <:obj:`str`>) filter list
self.filters = []
if headers:
self.headers = headers
if filters:
self.filters = filters
#: (:obj:`str`) table title
self.title = None
self.loadDescription(description)
[docs] def loadDescription(self, description):
""" loads description
:param description: description list
:type description: :obj:`list` <:obj:`str`>
"""
if self.headers:
hkey = self.headers[0]
for desc in description:
if desc is None:
self.__description.append(desc)
continue
skip = False
found = False
field = desc.get("nexus_path", "").split('/')[-1]
value = desc.get("value", "")
if field == 'depends_on' and value:
desc["depends_on"] = "[%s]" % value
for hd in self.__nonone:
vl = desc.get(hd, "")
if isinstance(vl, (list, tuple)):
vl = self.__toString(vl)
if not vl:
skip = True
break
if self.filters and hkey in desc.keys():
vl = desc[hkey]
found = False
for df in self.filters:
found = fnmatch.filter([vl], df)
if found:
break
if not found:
skip = True
continue
elif not self.filters:
found = True
if not skip and found:
self.__description.append(desc)
for desc in self.__description:
if desc is None:
continue
for hd, vl in desc.items():
if hd not in self.__nonone or vl:
if hd not in self.__hdsizes.keys():
self.__hdsizes[hd] = max(len(hd) + 1, 5)
if isinstance(vl, (list, tuple)):
vl = self.__toString(vl)
if not isinstance(vl, str):
vl = str(vl)
if self.__hdsizes[hd] <= len(vl):
self.__hdsizes[hd] = len(vl) + 1
@classmethod
def __toString(cls, lst):
""" converts list to string
:param lst: given list
:type lst: :obj:`list` <:obj:`str`>
:returns: list in string representation
:rtype: :obj:`str`
"""
res = []
for it in lst:
res.append(it or "*")
return str(res)
[docs] def generateList(self):
""" generate row lists of table
:returns: table rows
:rtype: :obj:`list` <:obj:`str`>
"""
lst = [""]
if self.title is not None:
lst.append(self.title)
lst.append("-" * len(self.title))
lst.append("")
headers = [hd for hd in self.headers if hd in self.__hdsizes.keys()]
line = ""
for hd in headers:
line += "=" * (self.__hdsizes[hd] - 1) + " "
lst.append(line)
line = ""
for hd in headers:
line += hd + " " * (self.__hdsizes[hd] - len(hd))
lst.append(line)
line = ""
for hd in headers:
line += "=" * (self.__hdsizes[hd] - 1) + " "
lst.append(line)
for desc in self.__description:
line = ""
if desc is None:
for hd in headers:
line += "=" * (self.__hdsizes[hd] - 1) + " "
lst.append(line.rstrip())
continue
line = ""
for hd in headers:
vl = desc[hd] if hd in desc else None
if isinstance(vl, (list, tuple)):
vl = self.__toString(vl)
elif vl is None:
vl = ""
elif not isinstance(vl, str):
vl = str(vl)
line += vl + " " * (self.__hdsizes[hd] - len(vl))
lst.append(line.rstrip())
line = ""
for hd in headers:
line += "=" * (self.__hdsizes[hd] - 1) + " "
lst.append(line)
lst.append("")
return lst
[docs]class TableDictTools(object):
""" configuration server adapter
"""
def __init__(self, description, nonone=None):
""" constructor
:param description: description list
:type description: :obj:`list` <:obj:`str`>
:param nonone: list of parameters which have to exist to be shown
:type nonone: :obj:`list` <:obj:`str`>
"""
#: (:obj:`list` <:obj:`str`>)
#: description list
self.__description = description
#: (:obj:`list` <:obj:`str`>)
#: headers
self.headers = [
'Timer',
'DataSourceSelection',
'ComponentSelection',
'ComponentPreselection',
'DataSourcePreselection',
'UserData',
'AppendEntry',
'ConfigDevice',
'WriterDevice',
'Door',
'DynamicComponents',
'ComponentsFromMntGrp',
'DefaultDynamicLinks',
'DefaultDynamicPath',
'UnplottedComponents',
'OptionalComponents',
'ConfigVariables',
# 'TimeZone',
# 'Version',
# 'OrderedChannels',
# 'PreselectingDataSources',
# 'MntGrpConfiguration',
# 'ChannelProperties',
# 'MntGrp',
]
self.headertypenames = {
'MntGrp': ('str', None),
'Timer': ('list', 'Timer(s)'),
'ComponentSelection':
('tdict', 'Detector Components'),
'DataSourceSelection': (
'tdict', 'Pool/Dynamic Detector Components'),
'ComponentPreselection': (
'tdict', 'Descriptive Components'),
'DataSourcePreselection': (
'tdict', 'Descriptive Dynamic Components'),
'UserData': ('dict', 'User Data'),
'AppendEntry': ('str', None),
'ConfigDevice': ('str', None),
'WriterDevice': ('str', None),
'UnplottedComponents': ('list', 'Unplotted Components'),
'MntGrpConfiguration': ('str', None),
'Version': ('str', None),
'TimeZone': ('str', None),
'Door': ('str', None),
'DynamicComponents': ('str', None),
'PreselectingDataSources': ('list', None),
'OrderedChannels': ('list', None),
'DefaultDynamicLinks': ('str', None),
'ConfigVariables': ('str', None),
'ComponentsFromMntGrp': ('str', None),
'ChannelProperties': ('str', None),
'OptionalComponents': ('list', None),
'DefaultDynamicPath': ('str', None),
}
self.orderpar = 'OrderedChannels'
self.__order = []
self.typemethods = {
'str': self._getstr,
'list': self._getlist,
'tdict': self._gettdict,
'dict': self._getdict,
}
#: (:obj:`str`) table title
self.title = None
self.maxnamesize = max(
[len(hd) for hd in self.headers] +
[len(self.headertypenames[hd][1])
for hd in self.headers
if self.headertypenames[hd][1]]
)
def _getstr(self, name, value):
space = " " * (self.maxnamesize - len(name))
sep = ":" if name != " " else name
return ["%s%s %s%s" % (name, sep, space, value)]
def _getlist(self, name, value):
space = " " * (self.maxnamesize - len(name))
svalue = ", ".join(json.loads(value)) if value else ""
sep = ":" if name != " " else name
return ["%s%s %s%s" % (name, sep, space, svalue)]
def _gettdict(self, name, value):
space = " " * (self.maxnamesize - len(name))
dvl = json.loads(value)
svalue = ""
if dvl:
lst = [key for key in dvl.keys() if dvl[key]]
if self.__order:
lst1 = [el for el in self.__order if el in lst]
lst1.extend(sorted(list(set(lst) - set(lst1))))
lst = lst1
else:
lst = sorted(lst)
svalue = ", ".join(lst)
sep = ":" if name != " " else name
return ["%s%s %s%s" % (name, sep, space, svalue)]
def _getdict(self, name, value):
space = " " * (self.maxnamesize - len(name))
sep = ":" if name != " " else name
return ["%s%s %s%s" % (name, sep, space, value)]
@classmethod
def __toString(cls, lst):
""" converts list to string
:param lst: given list
:type lst: :obj:`list` <:obj:`str`>
:returns: list in string representation
:rtype: :obj:`str`
"""
res = []
for it in lst:
res.append(it or "*")
return str(res)
[docs] def generateList(self):
""" generate row lists of table
:returns: table rows
:rtype: :obj:`list` <:obj:`str`>
"""
self.maxnamesize = max(
[len(hd) for hd in self.headers] +
[len(self.headertypenames[hd][1])
for hd in self.headers
if self.headertypenames[hd][1]]
)
lst = [""]
if self.title is not None:
lst.append(self.title)
lst.append("-" * len(self.title))
lst.append("")
tb = len(lst)
lst.append("")
for desc in self.__description:
if self.orderpar in desc.keys():
self.__order = json.loads(desc[self.orderpar])
else:
self.__order = []
for hd in self.headers:
if hd in desc.keys():
htp, name = self.headertypenames[hd]
method = self.typemethods[htp]
lst.extend(method(name or hd, desc[hd]))
maxsize = max(len(el) for el in lst)
lst.append(
"=" * (self.maxnamesize + 1) + " " +
"=" * (maxsize - 2 - self.maxnamesize))
lst[tb] = lst[-1]
return lst