# -*- coding: utf-8 -*-
#
#
# TheVirtualBrain-Scientific Package. This package holds all simulators, and
# analysers necessary to run brain-simulations. You can use it stand alone or
# in conjunction with TheVirtualBrain-Framework Package. See content of the
# documentation-folder for more details. See also http://www.thevirtualbrain.org
#
# (c) 2012-2023, Baycrest Centre for Geriatric Care ("Baycrest") and others
#
# This program 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.
# This program 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 this
# program. If not, see <http://www.gnu.org/licenses/>.
#
#
# CITATION:
# When using The Virtual Brain for scientific publications, please cite it as explained here:
# https://www.thevirtualbrain.org/tvb/zwei/neuroscience-publications
#
#
"""
Functions that inform a user about the state of a traited class or object.
Some of these functions are here so that they won't clutter the core trait implementation.
"""
import uuid
import numpy
import typing
try:
from docutils.core import publish_parts
except ImportError:
def publish_parts(param1, _param2):
return param1
[docs]def auto_docstring(cls):
""" generate a docstring for the new class in which the Attrs are documented """
header = 'Traited class [{}.{}]'.format(cls.__module__, cls.__name__)
doc = [
header,
len(header) * '^',
'',
]
if cls.__doc__ is not None:
doc.extend([cls.__doc__, ''])
doc.extend([
'Attributes declared',
'"""""""""""""""""""',
''
])
# a rst definition list for all attributes
for attr_name in cls.declarative_attrs:
attr = getattr(cls, attr_name)
# the standard repr of the attribute
doc.append('{} : {}'.format(attr_name, str(attr)))
# and now the doc property
for line in attr.doc.splitlines():
doc.append(' ' + line.lstrip())
doc.append('')
if cls.declarative_props:
doc.extend([
'',
'Properties declared',
'"""""""""""""""""""',
''
])
for prop_name in cls.declarative_props:
prop = getattr(cls, prop_name)
# the standard repr
doc.append(' {} : {}'.format(prop_name, str(prop)))
# now fish the docstrings
for line in prop.attr.doc.splitlines():
doc.append(' ' + line.lstrip())
if prop.fget.__doc__ is not None:
for line in prop.fget.__doc__.splitlines():
doc.append(' ' + line.lstrip())
doc = '\n'.join(doc)
return doc
[docs]def narray_summary_info(ar, ar_name='', condensed=False):
# type: (numpy.ndarray, str, bool) -> typing.Dict[str, str]
"""
A 2 column table represented as a dict of str->str
"""
key_none = 'is None'
key_empty = 'is empty'
key_min_max = '[min, median, max]'
key_nan = 'has NaN'
key_shape = 'shape'
key_type = 'dtype'
if ar is None:
return {f'{ar_name} {key_none}': 'True'} if ar_name else {key_none: 'True'}
if ar.size == 0:
return {f'{ar_name} {key_empty}': 'True'} if ar_name else {key_empty: 'True'}
ret = {key_shape: str(ar.shape),
key_type: str(ar.dtype)}
if ar.dtype.kind in 'iufc':
has_nan = numpy.isnan(ar).any()
if has_nan:
ret[key_nan] = 'True'
ret[key_min_max] = '[{:g}, {:g}, {:g}]'.format(ar.min(), numpy.median(ar), ar.max())
if condensed:
condensed_desc = ""
if ar.shape == (1,):
condensed_desc += str(ar.item())
else:
for key in [key_min_max, key_type, key_shape, key_empty, key_nan]:
if key in ret:
condensed_desc += f' {key} = {ret[key]}'
return {ar_name: condensed_desc} if ar_name else {'array': condensed_desc}
if ar_name:
return {ar_name + ' ' + k: v for k, v in ret.items()}
else:
return ret
[docs]def narray_describe(ar):
# type: (numpy.ndarray) -> str
summary = narray_summary_info(ar)
ret = []
for k in sorted(summary):
ret.append('{:<12}{}'.format(k, summary[k]))
return '\n'.join(ret)
# these are here and not on HasTraits just so that that class is not
# complicated by irrelevant string formatting
[docs]def trait_object_str(self):
cls = type(self)
summary = self.summary_info()
result = ['{} ('.format(cls.__name__)]
maxlenk = max(len(k) for k in summary)
for k in sorted(summary):
result.append(' {:.<{}} {}'.format(k + ' ', maxlenk, summary[k]))
result.append(')')
return '\n'.join(result)
[docs]def trait_object_repr_html(self):
cls = type(self)
subtitle = None
if hasattr(self, "dfun"):
subtitle = self.dfun.__doc__
elif hasattr(cls, "__doc_old__"):
subtitle = cls.__doc_old__
result = [
'<table>',
'<thead><h3>{}</h3></thead>'.format(cls.__name__),
'<tbody>',
'<tr><td colspan="2"><p>{}</p></td></tr>'.format(prepare_html(subtitle)) if subtitle is not None else "",
'<tr><th></th><th style="text-align:left;width:80%">value</th></tr>',
]
summary = self.summary_info()
for k in sorted(summary):
row_fmt = '<tr><td>{}</td><td style="text-align:left;"><pre>{}</pre></td></tr>'
result.append(row_fmt.format(k, summary[k]))
result += ['</tbody></table>']
return '\n'.join(result)
[docs]def convert_rst_to_html(doc):
"""
Convert from rst to html that can be rendered by Mathjax
"""
kwargs = {
'writer_name': 'html',
'settings_overrides': {
'_disable_config': True,
'report_level': 5,
'math_output': "MathJax /dummy.js",
},
}
return publish_parts(doc, **kwargs)['html_body']
[docs]def prepare_html(doc):
# type: (str) -> str
"""
Create html (that can be further enhanced by MathJax) from the description received as parameter
"""
try:
html_id = uuid.uuid1()
html = convert_rst_to_html(doc)
html = html.replace('div class="document"', f'div class="document" id="{html_id}"', 1)
html += fr'<script>MathJax.Hub.Queue(["Typeset", MathJax.Hub, "{html_id}"]);</script>'
except Exception:
html = str(doc)
return html