Lattice2/lattice2ValueSeriesGenerator.py
2015-12-20 04:00:42 +03:00

194 lines
11 KiB
Python

#***************************************************************************
#* *
#* Copyright (c) 2015 - Victor Titov (DeepSOIC) *
#* <vv.titov@gmail.com> *
#* *
#* This program is free software; you can redistribute it and/or modify *
#* it under the terms of the GNU Lesser General Public License (LGPL) *
#* as published by the Free Software Foundation; either version 2 of *
#* the License, or (at your option) any later version. *
#* for detail see the LICENCE text file. *
#* *
#* 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 Library General Public License for more details. *
#* *
#* You should have received a copy of the GNU Library General Public *
#* License along with this program; if not, write to the Free Software *
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
#* USA *
#* *
#***************************************************************************
__title__="Value Series generator module"
__author__ = "DeepSOIC"
__url__ = ""
__doc__ = "Value Series generator module: utility module to attach value generator to document object"
import math
import lattice2Executer
from latticeCommon import ParaConfusion
class ValueSeriesGenerator:
def __init__(self, docObj):
self.documentObject = docObj
self.source_modes = ["Values Property","Spreadsheet", "Generator"]
self.gen_modes = ['SpanN','StepN','SpanStep', 'Random']
self.gen_laws = ['Linear','Exponential']
self.readonlynessDict = {}
def addProperties(self, groupname, groupname_gen, valuesdoc):
# _addProperty(proptype , propname , defvalue, group, tooltip)
self._addProperty("App::PropertyStringList" ,"Values" , None, groupname, valuesdoc)
self._addProperty("App::PropertyEnumeration","ValuesSource" , self.source_modes, groupname, "Select where to take the value series from.")
self._addProperty("App::PropertyLink" ,"SpreadsheetLink", None, groupname, "Link to spreadsheet to take values from.")
self._addProperty("App::PropertyString" ,"CellStart" , 'A1', groupname, "Starting cell of first value (the rest are scanned downwards till an empty cell is encountered)")
self._addProperty("App::PropertyEnumeration","GeneratorMode" , self.gen_modes, groupname_gen,"")
self._addProperty("App::PropertyEnumeration","DistributionLaw", self.gen_laws, groupname_gen,"")
self._addProperty("App::PropertyFloat" ,"SpanStart" , 1.0, groupname_gen, "Starting value for value series generator")
self._addProperty("App::PropertyFloat" ,"SpanEnd" , 7.0, groupname_gen, "Ending value for value series generator")
self._addProperty("App::PropertyBool" ,"EndInclusive" , True, groupname_gen, "If True, the last value in series will equal SpanEnd. If False, the value equal to SpanEnd will be dropped.")
self._addProperty("App::PropertyFloat" ,"Step" , 1.0, groupname_gen, "Step for value generator. For exponential law, it is a natural logarithm of change ratio.")
self._addProperty("App::PropertyFloat" ,"Count" , 7.0, groupname_gen, "Number of values to generate")
def _addProperty(self, proptype, propname, defvalue, group, tooltip):
if hasattr(self.documentObject, propname):
return
self.documentObject.addProperty(proptype, propname, group, tooltip)
if defvalue is not None:
setattr(self.documentObject, propname, defvalue)
def updateReadonlyness(self):
obj = self.documentObject
m = obj.GeneratorMode
src = obj.ValuesSource
genOn = obj.ValuesSource == "Generator"
self._setPropertyWritable("Values" , src == "Values Property" )
self._setPropertyWritable("ValuesSource" , True )
self._setPropertyWritable("SpreadsheetLink" , src == "Spreadsheet" )
self._setPropertyWritable("CellStart" , src == "Spreadsheet" )
self._setPropertyWritable("GeneratorMode" , genOn )
self._setPropertyWritable("DistributionLaw" , genOn )
self._setPropertyWritable("SpanStart" , genOn )
self._setPropertyWritable("SpanEnd" , genOn and not(m == "StepN") )
self._setPropertyWritable("EndInclusive" , genOn )
self._setPropertyWritable("Step" , genOn and not(m == "SpanN" or m == "Random"))
self._setPropertyWritable("Count" , genOn and not(m == "SpanStep") )
def setPropertyWritable(self, propname, bool_writable):
'''setPropertyWritable(self, propname, bool_writable): Use to force a property read-only
(for example, when the property is driven by a link). If set to be writable, the read-onlyness
will be set according to series generator logic.'''
self.readonlynessDict[propname] = bool_writable
def _setPropertyWritable(self, propname, bool_writable, suppress_warning = False):
if self.readonlynessDict.has_key(propname):
if bool_writable == False and self.readonlynessDict[propname] == False and not suppress_warning:
lattice2Executer.warning(self.documentObject, "Property "+propname+" is being driven by generator, and something else (e.g., a link). Generator has priority.")
bool_writable = bool_writable and self.readonlynessDict[propname]
self.documentObject.setEditorMode(propname, 0 if bool_writable else 1)
def execute(self):
obj = self.documentObject #shortcut
values = [] #list to be filled with values, that are giong to be written to obj.Values
if obj.ValuesSource == "Generator":
#read out span and convert it to linear law
if obj.DistributionLaw == 'Linear':
vStart = float(obj.SpanStart)
vEnd = float(obj.SpanEnd)
elif obj.DistributionLaw == 'Exponential':
vSign = 1 if obj.SpanStart > 0.0 else -1.0
vStart = math.log(obj.SpanStart * vSign)
if obj.SpanEnd * vSign < ParaConfusion:
raise ValueError(obj.Name+": Wrong SpanEnd value. It is either zero, or of different sign compared to SpanStart. In exponential distribution, it is not allowed.")
vEnd = math.log(obj.SpanEnd * vSign)
else:
raise ValueError(obj.Name+": distribution law not implemented: "+obj.DistributionLaw)
if obj.GeneratorMode == 'SpanN':
n = obj.Count
if obj.EndInclusive:
n -= 1
if n == 0:
n = 1
obj.Step = (vEnd - vStart)/n
elif obj.GeneratorMode == 'StepN':
n = obj.Count
if obj.EndInclusive:
n -= 1
vEnd = vStart + obj.Step*n
if obj.DistributionLaw == 'Linear':
obj.SpanEnd = vEnd
elif obj.DistributionLaw == 'Exponential':
obj.SpanEnd = math.exp(vEnd)*vSign
else:
raise ValueError(obj.Name+": distribution law not implemented: "+obj.DistributionLaw)
elif obj.GeneratorMode == 'SpanStep':
nfloat = float((vEnd - vStart) / obj.Step)
n = math.trunc(nfloat - ParaConfusion) + 1
if obj.EndInclusive and abs(nfloat-round(nfloat)) <= ParaConfusion:
n = n + 1
obj.Count = n
elif obj.GeneratorMode == 'Random':
pass
else:
raise ValueError(obj.Name+": Generator mode "+obj.GeneratorMode+" is not implemented")
# Generate the actual array. We can use Step and N directly to
# completely avoid mode logic, since we had updated them
# cache properties into variables
# vStart,vEnd are already in sync
vStep = float(obj.Step)
n = int(obj.Count)
# Generate the values
if obj.GeneratorMode == 'Random':
import random
list_evenDistrib = [vStart + (vEnd-vStart)*random.random() for i in range(0, n)]
else:
list_evenDistrib = [vStart + vStep*i for i in range(0, n)]
if obj.DistributionLaw == 'Linear':
values = list_evenDistrib
elif obj.DistributionLaw == 'Exponential':
values = [math.exp(v)*vSign for v in list_evenDistrib]
else:
raise ValueError(obj.Name+": distribution law not implemented: "+obj.DistributionLaw)
elif obj.ValuesSource == "Spreadsheet":
#parse address
addr = obj.CellStart
#assuming only two letter column
if addr[1].isalpha():
col = addr[0:2]
row = addr[2:]
else:
col = addr[0:1]
row = addr[1:]
row = int(row)
#loop until the value can't be read out
values = []
while True:
try:
values.append( obj.SpreadsheetLink.get(col+str(row)) )
except ValueError:
break
row += 1
elif obj.ValuesSource == "Values Property":
pass
else:
raise ValueError(obj.Name+": values source mode not implemented: "+obj.ValuesSource)
# finally. Fill in the values.
if obj.ValuesSource != "Values Property":
obj.Values = [str(v) for v in values]