"""
decisionTable.DecisionTable
---------------------------
Main package class with all the logic.
If main package will need additional code, this class should get
fragmented first!
To use main class:
>>> from decisionTable import DecisionTable
>>> table = DecisionTable(tableString)
"""
from __future__ import absolute_import
from . import view
class DecisionTable(object):
"""
Attributes:
tableString (str): Main table representer of headers and decisions.
wildcardSymbol (str,optional): Is any value on decision table.
parentSymbol (str,optional): Is parent value (one level up on decision table).
Args:
header (array of str): header strings from tableString.
decisions (array of (array of str)): Decisions rows from tableString.
__wildcardSymbol (str) : Wild card symbol
__parentSymbol (str) : Parent symbol
"""
def __init__(self,tableString,wildcardSymbol='*',parentSymbol='.'):
self.header = []
self.decisions = []
self.__wildcardSymbol = None
self.__parentSymbol = None
self.__setWildcardSymbol(wildcardSymbol)
self.__setParentSymbol(parentSymbol)
self.header, self.decisions = self.__tableStringParser(tableString)
self.decisions = self.__replaceSpecialValues(self.decisions)
def __setWildcardSymbol(self,value):
"""self.__wildcardSymbol variable setter"""
errors = []
if not value is str and not value.split():
errors.append('wildcardSymbol_ERROR : Symbol : must be char or string!')
else:
self.__wildcardSymbol = value
if errors:
view.Tli.showErrors('SymbolError', errors)
def __setParentSymbol(self,value):
"""self.__parentSymbol variable setter"""
errors = []
if not value is str and not value.split():
errors.append('parentSymbol_ERROR : Symbol : must be char or string!')
else:
self.__parentSymbol = value
if errors:
view.Tli.showErrors('SymbolError', errors)
def __tableStringParser(self,tableString):
"""
Will parse and check tableString parameter for any invalid strings.
Args:
tableString (str): Standard table string with header and decisions.
Raises:
ValueError: tableString is empty.
ValueError: One of the header element is not unique.
ValueError: Missing data value.
ValueError: Missing parent data.
Returns:
Array of header and decisions::
print(return)
[
['headerVar1', ... ,'headerVarN'],
[
['decisionValue1', ... ,'decisionValueN'],
[<row2 strings>],
...
[<rowN strings>]
]
]
"""
error = []
header = []
decisions = []
if tableString.split() == []:
error.append('Table variable is empty!')
else:
tableString = tableString.split('\n')
newData = []
for element in tableString:
if element.strip():
newData.append(element)
for element in newData[0].split():
if not element in header:
header.append(element)
else:
error.append('Header element: '+element+' is not unique!')
for i, tableString in enumerate(newData[2:]):
split = tableString.split()
if len(split) == len(header):
decisions.append(split)
else:
error.append('Row: {}==> missing: {} data'.format(
str(i).ljust(4),
str(len(header)-len(split)).ljust(2))
)
if error:
view.Tli.showErrors('TableStringError',error)
else:
return [header,decisions]
def __replaceSpecialValues(self,decisions):
"""
Will replace special values in decisions array.
Args:
decisions (array of array of str): Standard decision array format.
Raises:
ValueError: Row element don't have parent value.
Returns:
New decision array with updated values.
"""
error = []
for row, line in enumerate(decisions):
if '.' in line:
for i, element in enumerate(line):
if row == 0:
error.append("Row: {}colume: {}==> don't have parent value".format(str(row).ljust(4),str(i).ljust(4)))
if element==self.__parentSymbol:
if decisions[row-1][i] == '.':
error.append("Row: {}Colume: {}==> don't have parent value".format(str(row).ljust(4),str(i).ljust(4)))
decisions[row][i]=decisions[row-1][i]
if error:
view.Tli.showErrors('ReplaceSpecialValuesError',error)
else:
return decisions
def __toString(self,values):
"""
Will replace dict values with string values
Args:
values (dict): Dictionary of values
Returns:
Updated values dict
"""
for key in values:
if not values[key] is str:
values[key] = str(values[key])
return values
def __valueKeyWithHeaderIndex(self,values):
"""
This is hellper function, so that we can mach decision values with row index
as represented in header index.
Args:
values (dict): Normaly this will have dict of header values and values from decision
Return:
>>> return()
{
values[headerName] : int(headerName index in header array),
...
}
"""
machingIndexes = {}
for index, name in enumerate(self.header):
if name in values:
machingIndexes[index] = values[name]
return machingIndexes
def __checkDecisionParameters(self,result,**values):
"""
Checker of decision parameters, it will raise ValueError if finds something wrong.
Args:
result (array of str): See public decision methods
**values (array of str): See public decision methods
Raise:
ValueError: Result array none.
ValueError: Values dict none.
ValueError: Not find result key in header.
ValueError: Result value is empty.
Returns:
Error array values
"""
error = []
if not result:
error.append('Function parameter (result array) should contain one or more header string!')
if not values:
error.append('Function parameter (values variables) should contain one or more variable')
for header in result:
if not header in self.header:
error.append('String ('+header+') in result is not in header!')
for header in values:
if not header in self.header:
error.append('Variable ('+header+') in values is not in header!')
elif not values[header].split():
error.append('Variable ('+header+') in values is empty string')
if error:
return error
def __getDecision(self,result,multiple=False,**values):
"""
The main method for decision picking.
Args:
result (array of str): What values you want to get in return array.
multiple (bolean, optional): Do you want multiple result if it finds many maching decisions.
**values (dict): What should finder look for, (headerString : value).
Returns: Maped result values with finded elements in row/row.
"""
values = self.__toString(values)
__valueKeyWithHeaderIndex = self.__valueKeyWithHeaderIndex(values)
errors = self.__checkDecisionParameters(result,**values)
if errors:
view.Tli.showErrors('ParametersError', errors)
machingData = {}
for line in self.decisions:
match = True
for index in __valueKeyWithHeaderIndex:
if line[index] != __valueKeyWithHeaderIndex[index]:
if line[index] != self.__wildcardSymbol:
match = False
break
if match:
if multiple:
for header in result:
if header not in machingData:
machingData[header] = [line[self.header.index(header)]]
else:
machingData[header].append(line[self.header.index(header)])
else:
for header in result:
machingData[header] = line[self.header.index(header)]
return machingData
if multiple:
if machingData:
return machingData
#Return none if not found (not string so
#not found value can be recognized
return dict((key, None) for key in result)
def decisionCall(self,callback,result,**values):
"""
The decision method with callback option. This method will find matching row, construct
a dictionary and call callback with dictionary.
Args:
callback (function): Callback function will be called when decision will be finded.
result (array of str): Array of header string
**values (dict): What should finder look for, (headerString : value).
Example:
>>> def call(header1,header2):
>>> print(header1,header2)
>>>
>>> table = DecisionTable('''
>>> header1 header2
>>> ===============
>>> value1 value2
>>> ''')
>>>
>>> header1, header2 = table.decision(
>>> call,
>>> ['header1','header2'],
>>> header1='value1',
>>> header2='value2'
>>> )
(value1 value2)
"""
callback(**self.__getDecision(result,**values))
def decision(self,result,**values):
"""
The decision method with callback option. This method will find matching row, construct
a dictionary and call callback with dictionary.
Args:
callback (function): Callback function will be called when decision will be finded.
result (array of str): Array of header string
**values (dict): What should finder look for, (headerString : value).
Returns:
Arrays of finded values strings
Example:
>>> table = DecisionTable('''
>>> header1 header2
>>> ===============
>>> value1 value2
>>> ''')
>>>
>>> header1, header2 = table.decision(
>>> ['header1','header2'],
>>> header1='value1',
>>> header2='value2'
>>> )
>>> print(header1,header2)
(value1 value2)
"""
data = self.__getDecision(result,**values)
data = [ data[value] for value in result]
if len(data) == 1:
return data[0]
else:
return data
def allDecisions(self,result,**values):
"""
Joust like self.decision but for multiple finded values.
Returns:
Arrays of arrays of finded elements or if finds only one mach, array of strings.
"""
data = self.__getDecision(result,multiple=True,**values)
data = [ data[value] for value in result]
if len(data) == 1:
return data[0]
else:
return data