Source code for Goulib.units

__author__ = "Marc Nicole"
__copyright__ = "Copyright 2015, Marc Nicole"
__credits__ = [""]
__license__ = "LGPL"

from pint import UnitRegistry  # https://pypi.python.org/pypi/Pint/
from inspect import isfunction, getsource

# defines V as a way to express any physical value (and Currency)
ureg = UnitRegistry()
V = ureg.Quantity


[docs]def magnitudeIn(self, unit): return self.to(unit).magnitude
V.__call__ = magnitudeIn
[docs]class Row(): """ a Raw is internally a dict of {'colname1':value,'colname2',value...} """
[docs] def __init__(self, row): # row has the same format as internal self.row = row
[docs] def __getitem__(self, col): try: v = self.row[col] if isfunction(v): # in that case, one must resolve the calculation v = v() return v except KeyError: return None
[docs] def __setitem__(self, col, value): self.row[col] = value
[docs] def isfunc(self, col): try: return isfunction(self.row[col]) except KeyError: return False
def _repr_html_(self, col, convertToUnits='', withoutUnits=False): if self.isfunc(col): c = self.row[col] background = 'background-color: #f1f1c1;' title = 'title="'+getsource(c)+'"' else: background = '' title = '' try: disp = self[col] if convertToUnits == '' else self[col].to( convertToUnits) if withoutUnits: return '<td colspan=2 style="text-align:right;'+background+'" '+title+'>%f</td>' % disp.magnitude else: return '<td style="text-align:right;'+background+'" '+title+'>%f</td><td>%s</td>' % (disp.magnitude, disp.units) except Exception as e: return '<td colspan=2 style="background-color:#ffe5e5;" title="'+str(e)+'">'+str(self[col])+'</td>'
[docs]class Table(): """ to create a table, use the following syntax t = Table ('myTable', ['col1', 'col2', 'col3'], [ 'distance', V(20,'m'), V(30,'m'), V(40,'m'), ('speed','m/s'),V(2,'m/min'), V(5,'m/s'), V(10,'inch/s') ]) the row label can be a tuple of ('label','unit'). in that case, all the values of the row will be converted to 'unit' (if possible) and the cells won't diplay the units saving space in the future, the label could be replaced by a dictionary having more properties like {'label':<label>, 'comment':<comment>....} --------- internally, the rows are represented as {'<the label>':{'col1':<value>, 'col2':<value> ....}, '<the next label>' """
[docs] def __init__(self, name, cols, cells): if isinstance(name, tuple): self.name = name[0] self.defaultUnits = name[1] else: self.name = name self.defaultUnits = '' self.cols = [] self.colUnits = {} self.rows = {} self.rowUnits = {} self.rowLabels = [] # in order to keep the natural order of the table self.format = format for c in cols: if isinstance(c, tuple): unit = c[1] c = c[0] self.colUnits[c] = unit self.cols.append(c) i = iter(cells) tableOk = False # just here to check consistency of rows try: while True: tableOk = True label = next(i) if isinstance(label, tuple): unit = label[1] label = label[0] self.rowUnits[label] = unit self.rowLabels.append(label) row = {} tableOk = False for col in cols: row[col] = next(i) self.rows[label] = Row(row) tableOk = True except StopIteration: if not tableOk: raise Exception( 'the cells array has inconsistent number of cells regarding the number of columns passed in cols')
[docs] def setCell(self, row, col, value): if isinstance(row, tuple): self.rowUnits[row[0]] = row[1] row = row[0] if isinstance(col, tuple): self.colUnits[col[0]] = col[1] col = col[0] if row not in self.rowLabels: self.rowLabels.append(row) self.rows[row] = Row({}) if col not in self.cols: self.cols.append(col) self.rows[row][col] = value
[docs] def __getitem__(self, key): return self.rows[key]
def _repr_html_(self): html = '<table border="1"><caption> '+self.name + \ ' '+self.defaultUnits+'</caption><tr><th></th>' for col in self.cols: unit = ' ['+self.colUnits[col]+']' if col in self.colUnits else '' html += '<th colspan="2">'+col+unit+'</th>' html += '</tr><tr>' for label in self.rowLabels: if label in self.rowUnits: units = self.rowUnits[label] html += '<td>'+label+'['+units+']</td>' else: units = self.defaultUnits html += '<td>'+label+'</td>' r = self.rows[label] for col in self.cols: noUnits = False if label in self.rowUnits: units = self.rowUnits[label] noUnits = True if col in self.colUnits: units = self.colUnits[col] noUnits = True html += r._repr_html_(col, convertToUnits=units, withoutUnits=noUnits) html += '</tr>' html += '</table>' return html
[docs] def appendCol(self, colname, values): """ appends a column at the right of the table :parameter values: is a dict of {<row>:V(...),...} """ self.cols.append(colname) for rowname in values: self.rows[rowname][colname] = values[rowname]
[docs] def appendRow(self, label, values, unit=None,): """ appends a new row of data :params label: the label of the row :params values: an array of values. must be the same size as ne number of columns of the table :params unit: if specified, the unit that will be used to display the row """ assert len(values) == len(self.cols), "the values array has {} elements and the table has {} columns".format( len(values), len(self.cols)) self.rowLabels.append(label) r = {self.cols[i]: values[i] for i in range(len(values))} self.rows[label] = Row(r) if unit: self.rowUnits[label] = unit
[docs] def appendColFromObj(self, colname, obj, default=None): """ add a new column by searching in obj all properties that have the same name as the row names if a row is not found, then the default is used""" values = {} for label in self.rowLabels: v = getattr(obj, label, default) values[label] = v self.appendCol(colname, values)
[docs]class View():
[docs] def __init__(self, table, rows=[], cols=[], rowUnits={}, name=''): self.table = table self.rows = rows if rows != [] else table.rowLabels self.name = name if name != '' else table.name if cols == []: self.cols = table.cols else: self.cols = cols self.rowUnits = rowUnits
def _repr_html_(self): html = '<table border="1"><caption> '+self.name+'</caption><tr><th></th>' for col in self.cols: html += '<th colspan="2">'+col+'</th>' html += '</tr><tr>' for row in self.rows: if row in self.rowUnits: units = self.rowUnits[row] html += '<td>'+row+'['+units+']</td>' else: units = '' html += '<td>'+row+'</td>' r = self.table.rows[row] for col in self.cols: html += r._repr_html_(col, convertToUnits=units, withoutUnits=row in self.rowUnits) html += '</tr>' html += '</table>' return html