Commit d4aa8f19 authored by Frédéric Escudié's avatar Frédéric Escudié
Browse files

Add DateParameter.

Fix bug on parameters.
parent dfb9cffd
...@@ -102,7 +102,7 @@ class Component(object): ...@@ -102,7 +102,7 @@ class Component(object):
def add_parameter(self, name, help, default=None, type=types.StringType, choices=None, def add_parameter(self, name, help, default=None, type=types.StringType, choices=None,
required=False, flag=None, group="default", display_name=None, add_to=None): required=False, flag=None, group="default", display_name=None, add_to=None):
new_param = Parameter(name, help, flag=flag, default=default, type=type, choices=choices, new_param = ParameterFactory.factory(name, help, flag=flag, default=default, type=type, choices=choices,
required=required, group=group, display_name=display_name) required=required, group=group, display_name=display_name)
# if this input should be added to a particular parameter # if this input should be added to a particular parameter
if add_to: if add_to:
...@@ -131,7 +131,7 @@ class Component(object): ...@@ -131,7 +131,7 @@ class Component(object):
def add_output_file(self, name, help, file_format="any", filename=None, group="default", display_name=None, add_to=None): def add_output_file(self, name, help, file_format="any", filename=None, group="default", display_name=None, add_to=None):
filename = os.path.basename(filename) filename = os.path.basename(filename)
new_param = OutputFile(name, help, default=os.path.join(self.output_directory, filename), new_param = OutputFile(name, help, default=os.path.join(self.output_directory, filename),
file_format=file_format, group=group, display_name=display_name) file_format=file_format, group=group, display_name=display_name)
# if this input should be added to a particular parameter # if this input should be added to a particular parameter
if add_to: if add_to:
......
...@@ -260,7 +260,7 @@ class AbstractParameter(object): ...@@ -260,7 +260,7 @@ class AbstractParameter(object):
# Set parameter type # Set parameter type
if type == "date": if type == "date":
self.type = types.MemberDescriptorType self.type = date
elif type == "multiple": elif type == "multiple":
self.type = "multiple" self.type = "multiple"
elif isinstance(type, types.FunctionType): elif isinstance(type, types.FunctionType):
...@@ -303,7 +303,7 @@ class AbstractIOFile(object): ...@@ -303,7 +303,7 @@ class AbstractIOFile(object):
class MultiParameter(dict, AbstractParameter): class MultiParameter(dict, AbstractParameter):
def __init__(self, name, help, required=False, flag=None, group="default", display_name=None): def __init__(self, name, help, required=False, flag=None, group="default", display_name=None):
AbstractParameter.__init__(self, name, help, required=required, type="multiple", flag=flag, group=group, AbstractParameter.__init__(self, name, help, required=required, type="multiple", flag=flag, group=group,
display_name=display_name) display_name=display_name)
...@@ -338,7 +338,7 @@ class MultiParameter(dict, AbstractParameter): ...@@ -338,7 +338,7 @@ class MultiParameter(dict, AbstractParameter):
class MultiParameterList(list, AbstractParameter): class MultiParameterList(list, AbstractParameter):
def __init__(self, name, help, required=False, flag=None, group="default", display_name=None): def __init__(self, name, help, required=False, flag=None, group="default", display_name=None):
AbstractParameter.__init__(self, name, help, required=required, type="multiple", flag=flag, AbstractParameter.__init__(self, name, help, required=required, type="multiple", flag=flag,
action="append", group=group, display_name=display_name) action="append", group=group, display_name=display_name)
...@@ -347,9 +347,9 @@ class MultiParameterList(list, AbstractParameter): ...@@ -347,9 +347,9 @@ class MultiParameterList(list, AbstractParameter):
def add_sub_parameter(self, param): def add_sub_parameter(self, param):
param_flag = param.flag[2:] param_flag = param.flag[2:]
if self.type == "multiple": if self.type == "multiple":
if param.required: req = [param_flag] if param.required: req = [param_flag]
else: req = [] else: req = []
self.type = MultipleParameters({param_flag: param.type}, req, self.type = MultipleParameters({param_flag: param.type}, req,
{param_flag: param.choices}, {}, {param_flag: param.default}, {param_flag: param.choices}, {}, {param_flag: param.default},
{param_flag: param.action}) {param_flag: param.action})
if self.action == "append": if self.action == "append":
...@@ -372,13 +372,31 @@ class MultiParameterList(list, AbstractParameter): ...@@ -372,13 +372,31 @@ class MultiParameterList(list, AbstractParameter):
self.sub_parameters.append(param) self.sub_parameters.append(param)
class ParameterFactory(object):
@staticmethod
def factory(*args, **kwargs):
if not kwargs.has_key( "type" ):
return StrParameter( *args, **kwargs )
if kwargs["type"] == "int" or kwargs["type"] is types.IntType:
return IntParameter( *args, **kwargs )
elif kwargs["type"] == "bool" or kwargs["type"] is types.BooleanType:
return BoolParameter( *args, **kwargs )
elif kwargs["type"] == "float" or kwargs["type"] is types.FloatType:
return FloatParameter( *args, **kwargs )
elif kwargs["type"] == "date" or kwargs["type"] == date:
return DateParameter( *args, **kwargs )
else:
return StrParameter( *args, **kwargs )
def noneException(*args, **kwargs): def noneException(*args, **kwargs):
raise Exception( "The parameter value is None." ) raise Exception( "The parameter value is None." )
class BoolParameter(int, AbstractParameter): class BoolParameter(int, AbstractParameter):
def __new__(self, name, help, default=None, type=types.BooleanType, choices=None, required=False, def __new__(self, name, help, default=False, type=types.BooleanType, choices=None, required=False,
flag=None, sub_parameters=None, group="default", display_name=None): flag=None, sub_parameters=None, group="default", display_name=None):
bool_default = False if default == None else bool(default) bool_default = False if default == None else bool(default)
val = int.__new__(self, bool_default) val = int.__new__(self, bool_default)
...@@ -393,24 +411,24 @@ class BoolParameter(int, AbstractParameter): ...@@ -393,24 +411,24 @@ class BoolParameter(int, AbstractParameter):
def __init__(self, name, help, default=None, type=types.BooleanType, choices=None, required=False, def __init__(self, name, help, default=None, type=types.BooleanType, choices=None, required=False,
flag=None, sub_parameters=None, group="default", display_name=None): flag=None, sub_parameters=None, group="default", display_name=None):
AbstractParameter.__init__(self, name, help, flag=flag, default=default, type=type, choices=choices, required=required, AbstractParameter.__init__(self, name, help, flag=flag, default=bool(default), type=type, choices=choices, required=required,
action="store", sub_parameters=sub_parameters, group=group, display_name=display_name) action="store", sub_parameters=sub_parameters, group=group, display_name=display_name)
def __str__(self):
if self.is_None:
noneException()
return str(bool(self))
def __eq__(self, other): def __eq__(self, other):
if other.__class__.__name__ == "NoneType": if other.__class__.__name__ == "NoneType":
return self.is_None return self.is_None
elif self.is_None: elif self.is_None:
return False return False
else: else:
return int(self) == int(other) return int(self) == int(bool(other))
def __ne__(self, other): def __ne__(self, other):
if other.__class__.__name__ == "NoneType": return not (self == other)
return not self.is_None
elif self.is_None:
return True
else:
return int(self) != int(other)
def __nonzero__(self): def __nonzero__(self):
if self.is_None: if self.is_None:
...@@ -448,12 +466,7 @@ class IntParameter(int, AbstractParameter): ...@@ -448,12 +466,7 @@ class IntParameter(int, AbstractParameter):
return int(self) == int(other) return int(self) == int(other)
def __ne__(self, other): def __ne__(self, other):
if other.__class__.__name__ == "NoneType": return not (self == other)
return not self.is_None
elif self.is_None:
return True
else:
return int(self) != int(other)
def __nonzero__(self): def __nonzero__(self):
if self.is_None: if self.is_None:
...@@ -462,12 +475,12 @@ class IntParameter(int, AbstractParameter): ...@@ -462,12 +475,12 @@ class IntParameter(int, AbstractParameter):
return self != 0 return self != 0
class FloatParameter(int, AbstractParameter): class FloatParameter(float, AbstractParameter):
def __new__(self, name, help, default=None, type=types.FloatType, choices=None, required=False, def __new__(self, name, help, default=None, type=types.FloatType, choices=None, required=False,
flag=None, sub_parameters=None, group="default", display_name=None): flag=None, sub_parameters=None, group="default", display_name=None):
float_default = 0.0 if default == None else float(default) float_default = 0.0 if default == None else float(default)
val = int.__new__(self, float_default) val = float.__new__(self, float_default)
val.is_None = False val.is_None = False
if default == None: if default == None:
val.is_None = True val.is_None = True
...@@ -491,12 +504,7 @@ class FloatParameter(int, AbstractParameter): ...@@ -491,12 +504,7 @@ class FloatParameter(int, AbstractParameter):
return float(self) == float(other) return float(self) == float(other)
def __ne__(self, other): def __ne__(self, other):
if other.__class__.__name__ == "NoneType": return not (self == other)
return not self.is_None
elif self.is_None:
return True
else:
return float(self) != float(other)
def __nonzero__(self): def __nonzero__(self):
if self.is_None: if self.is_None:
...@@ -534,12 +542,7 @@ class StrParameter(str, AbstractParameter): ...@@ -534,12 +542,7 @@ class StrParameter(str, AbstractParameter):
return str(self) == str(other) return str(self) == str(other)
def __ne__(self, other): def __ne__(self, other):
if other.__class__.__name__ == "NoneType": return not(self == other)
return not self.is_None
elif self.is_None:
return True
else:
return str(self) != str(other)
def __nonzero__(self): def __nonzero__(self):
if self.is_None: if self.is_None:
...@@ -548,20 +551,58 @@ class StrParameter(str, AbstractParameter): ...@@ -548,20 +551,58 @@ class StrParameter(str, AbstractParameter):
return (True if str(self) else False) return (True if str(self) else False)
class ParameterFactory(object): class DateParameter(datetime.datetime, AbstractParameter):
@staticmethod
def factory(*args, **kwargs):
if not kwargs.has_key( "type" ):
return StrParameter( *args, **kwargs )
if kwargs["type"] == "int" or kwargs["type"] is types.IntType: def __new__(self, name, help, default=None, type=date, choices=None, required=False,
return IntParameter( *args, **kwargs ) flag=None, sub_parameters=None, group="default", display_name=None):
elif kwargs["type"] == "bool" or kwargs["type"] is types.BooleanType : date_default = datetime.datetime.today()
return BoolParameter( *args, **kwargs ) if default != None and issubclass(default.__class__, datetime.datetime):
elif kwargs["type"] == "float" or kwargs["type"] is types.FloatType : date_default = default
return FloatParameter( *args, **kwargs ) elif default != None:
date_default = date(default)
val = datetime.datetime.__new__(self, date_default.year, date_default.month, date_default.day)
if default != None:
val.is_None = False
else: else:
return StrParameter( *args, **kwargs ) val.is_None = True
for attr in val.__dict__:
value = getattr(val, attr)
if callable(value) and attr not in ["__new__", "__init__", "__getattribute__", "__eq__", "__ne__", "__nonzero__"]:
setattr(val, attr, noneException)
return val
def __init__(self, name, help, default=None, type=date, choices=None, required=False,
flag=None, sub_parameters=None, group="default", display_name=None):
if default != None and not issubclass(default.__class__, datetime.datetime):
date_default = date(default)
default = datetime.datetime(date_default.year, date_default.month, date_default.day)
AbstractParameter.__init__(self, name, help, flag=flag, default=default, type=type, choices=choices, required=required,
action="store", sub_parameters=sub_parameters, group=group, display_name=display_name)
def __eq__(self, other):
if other.__class__.__name__ == "NoneType":
return self.is_None
elif self.is_None:
return False
else:
return datetime.date(self.year, self.month, self.day) == other
def __ne__(self, other):
return not( self == other )
def __nonzero__(self):
if self.is_None:
return False
else:
return (True if datetime.date(self.year, self.month, self.day) else False)
class AbstractInputFile(AbstractIOFile):
"""
@summary : Parent of all InputFile(s) parameters.
"""
pass
class AbstractOutputFile(AbstractIOFile): class AbstractOutputFile(AbstractIOFile):
...@@ -595,12 +636,12 @@ class InputFile(StrParameter, AbstractInputFile): ...@@ -595,12 +636,12 @@ class InputFile(StrParameter, AbstractInputFile):
class OutputFile(StrParameter, AbstractOutputFile): class OutputFile(StrParameter, AbstractOutputFile):
def __new__(self, name, help, file_format="any", default="", choices=None, def __new__(self, name, help, file_format="any", default="", choices=None,
required=False, flag=None, group="default", display_name=None): required=False, flag=None, group="default", display_name=None):
return StrParameter.__new__(self, name, help, flag=flag, default=default, type="localfile", choices=choices, return StrParameter.__new__(self, name, help, flag=flag, default=default, type="localfile", choices=choices,
required=required, group=group, display_name=display_name) required=required, group=group, display_name=display_name)
def __init__(self, name, help, file_format="any", default="", choices=None, def __init__(self, name, help, file_format="any", default="", choices=None,
required=False, flag=None, group="default", display_name=None): required=False, flag=None, group="default", display_name=None):
AbstractIOFile.__init__(self, file_format) AbstractIOFile.__init__(self, file_format)
StrParameter.__init__(self, name, help, flag=flag, default=default, type="localfile", choices=choices, StrParameter.__init__(self, name, help, flag=flag, default=default, type="localfile", choices=choices,
...@@ -612,7 +653,7 @@ class ParameterList(list, AbstractParameter): ...@@ -612,7 +653,7 @@ class ParameterList(list, AbstractParameter):
def __init__(self, name, help, default=None, type=types.StringType, choices=None, required=False, def __init__(self, name, help, default=None, type=types.StringType, choices=None, required=False,
flag=None, sub_parameters=None, group="default", display_name=None): flag=None, sub_parameters=None, group="default", display_name=None):
if default == None: default = [] if default == None: default = []
AbstractParameter.__init__(self, name, help, flag=flag, default=default, type=type, choices=choices, required=required, AbstractParameter.__init__(self, name, help, flag=flag, default=default, type=type, choices=choices, required=required,
action="append", sub_parameters=sub_parameters, group=group, display_name=display_name) action="append", sub_parameters=sub_parameters, group=group, display_name=display_name)
if default.__class__.__name__ == "str": if default.__class__.__name__ == "str":
return list.__init__(self, [default]) return list.__init__(self, [default])
......
...@@ -238,13 +238,13 @@ class Workflow(threading.Thread): ...@@ -238,13 +238,13 @@ class Workflow(threading.Thread):
return [file_path, True] return [file_path, True]
except: except:
return [input, False] return [input, False]
def _set_parameters(self, args): def _set_parameters(self, args):
parameters = self.get_parameters() parameters = self.get_parameters()
for param in parameters: for param in parameters:
try: args[param.name] = args[param.name].encode('ascii','ignore') try: args[param.name] = args[param.name].encode('ascii','ignore')
except: pass except: pass
if param.__class__ == StrParameter or param.__class__ == IntParameter or param.__class__ == FloatParameter or param.__class__ == BoolParameter: if param.__class__ == StrParameter or param.__class__ == IntParameter or param.__class__ == FloatParameter or param.__class__ == BoolParameter or param.__class__ == DateParameter:
if args[param.name]: default = args[param.name] if args[param.name]: default = args[param.name]
else: default = param.default else: default = param.default
new_param = ParameterFactory.factory(param.name, param.help, default=default, type=param.type, choices=param.choices, new_param = ParameterFactory.factory(param.name, param.help, default=default, type=param.type, choices=param.choices,
...@@ -259,7 +259,7 @@ class Workflow(threading.Thread): ...@@ -259,7 +259,7 @@ class Workflow(threading.Thread):
except: is_local = False except: is_local = False
if not is_uri and not is_local and param.type.__name__ == "inputfile" or param.type.__name__ == "browsefile": if not is_uri and not is_local and param.type.__name__ == "inputfile" or param.type.__name__ == "browsefile":
default = os.path.join(self.jflow_config_reader.get_tmp_directory(), (args[param.name])) default = os.path.join(self.jflow_config_reader.get_tmp_directory(), (args[param.name]))
else: else:
default = param.default default = param.default
new_param = InputFile(param.name, param.help, file_format=param.file_format, default=default, type=param.type, new_param = InputFile(param.name, param.help, file_format=param.file_format, default=default, type=param.type,
choices=param.choices, required=param.required, flag=param.flag, choices=param.choices, required=param.required, flag=param.flag,
...@@ -274,15 +274,15 @@ class Workflow(threading.Thread): ...@@ -274,15 +274,15 @@ class Workflow(threading.Thread):
# handle url inputs # handle url inputs
for path in path2test: for path in path2test:
new_url, is_uri = self._download_urlfile(path) new_url, is_uri = self._download_urlfile(path)
if is_uri: if is_uri:
default_vals.append(new_url) default_vals.append(new_url)
path2test.remove(path) path2test.remove(path)
# handle localfile # handle localfile
for path in path2test: for path in path2test:
if os.path.isfile(path): if os.path.isfile(path):
default_vals.append(path) default_vals.append(path)
path2test.remove(path) path2test.remove(path)
# handle upload inputs # handle upload inputs
if param.type.__name__ == "inputfile" or param.type.__name__ == "browsefile": if param.type.__name__ == "inputfile" or param.type.__name__ == "browsefile":
for path in path2test: for path in path2test:
default_vals.append(os.path.join(self.jflow_config_reader.get_tmp_directory(), (path))) default_vals.append(os.path.join(self.jflow_config_reader.get_tmp_directory(), (path)))
...@@ -316,7 +316,7 @@ class Workflow(threading.Thread): ...@@ -316,7 +316,7 @@ class Workflow(threading.Thread):
final_sub_args[sub_param.name] = sub_param.default final_sub_args[sub_param.name] = sub_param.default
final_vals.append(final_sub_args) final_vals.append(final_sub_args)
self.__setattr__(param.name, final_vals) self.__setattr__(param.name, final_vals)
def get_execution_graph(self): def get_execution_graph(self):
graph = Graph() graph = Graph()
for cpt in self.components: for cpt in self.components:
...@@ -326,7 +326,7 @@ class Workflow(threading.Thread): ...@@ -326,7 +326,7 @@ class Workflow(threading.Thread):
or issubclass( ioparameter.__class__, InputFileList) or issubclass( ioparameter.__class__, OutputFileList) ): or issubclass( ioparameter.__class__, InputFileList) or issubclass( ioparameter.__class__, OutputFileList) ):
graph.add_edge({ioparameter.component_nameid, ioparameter.parent_component_nameid}) graph.add_edge({ioparameter.component_nameid, ioparameter.parent_component_nameid})
return graph return graph
def set_stderr(self): def set_stderr(self):
if hasattr(self, "stderr") and self.stderr is not None: if hasattr(self, "stderr") and self.stderr is not None:
os.rename( self.stderr, os.path.join(self.directory, str(time.time()) + "wf_stderr.old") ) os.rename( self.stderr, os.path.join(self.directory, str(time.time()) + "wf_stderr.old") )
...@@ -339,12 +339,12 @@ class Workflow(threading.Thread): ...@@ -339,12 +339,12 @@ class Workflow(threading.Thread):
file_handler.setFormatter(formatter) file_handler.setFormatter(formatter)
logger.addHandler(file_handler) logger.addHandler(file_handler)
self.stderr = stderr self.stderr = stderr
@staticmethod @staticmethod
def config_parser(arg_lines): def config_parser(arg_lines):
for arg in arg_lines: for arg in arg_lines:
yield arg yield arg
def get_errors(self): def get_errors(self):
error = { error = {
"title" : "", "title" : "",
......
#
# Copyright (C) 2012 INRA
#
# 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/>.
#
from jflow.workflow import Workflow
from jflow.parameter import InputFileList, InputFile
class Test (Workflow):
def get_description(self):
return "use for test jflow core modifications"
def define_parameters(self, parameters_section=None):
self.add_multiple_parameter_list("multi1", "Multiple.")
self.add_parameter("test_A1", "Multiple float null.", add_to="multi1", type="float")
self.add_parameter("test_A2", "Multiple float default 1.", add_to="multi1", type="float", default=1)
self.add_parameter("test_A3", "Multiple int default 2.", add_to="multi1", type=int, default=2)
self.add_parameter("test_A4", "Multiple int null.", add_to="multi1", type=int)
self.add_parameter("test_A5", "Multiple date default str '20/08/2014'.", add_to="multi1", type="date", default="20/08/2014")
self.add_parameter("test_A6", "date default null.", add_to="multi1", type="date")
self.add_parameter("test_A7", "Multiple string default str 'None'.", add_to="multi1", default="None")
self.add_parameter("test_A8", "Multiple string null.", add_to="multi1")
self.add_parameter("test_A9", "Multiple bool default True.", add_to="multi1", type="bool", default=True)
self.add_parameter("test_A10", "Multiple bool default 3.", add_to="multi1", type="bool", default=3)
self.add_parameter("test_A11", "Multiple bool default null.", add_to="multi1", type="bool")
self.add_input_file("test_A12", "Multiple InputFile default null.", add_to="multi1")
self.add_input_file("test_A13", "Multiple InputFile default application.properties.", add_to="multi1", default="application.properties")
self.add_parameter("test_B1", "float null.", type="float")
self.add_parameter("test_B2", "float default 1.", type="float", default=1)
self.add_parameter("test_B3", "int default 2.", type=int, default=2)
self.add_parameter("test_B4", "int null.", type=int)
self.add_parameter("test_B5", "date default str 20/08/2014.", type="date", default="20/08/2014")
self.add_parameter("test_B6", "date default null.", type="date")
self.add_parameter("test_B7", "string default str None.", default="None")
self.add_parameter("test_B8", "string null.")
self.add_parameter("test_B9", "bool default True.", type="bool", default=True)
self.add_parameter("test_B10", "bool default false.", type="bool", default=False)
self.add_parameter("test_B11", "bool default null.", type="bool")
self.add_input_file("test_B12", "Multiple InputFile default null.")
self.add_input_file("test_B13", "Multiple InputFile default application.properties.", default="application.properties")
self.add_multiple_parameter_list("multi2", "Multiple required.", required=True)
self.add_parameter("valid", "Multiple required.", add_to="multi2", default="ok")
self.add_parameter("test_C1", "Multiple float null.", add_to="multi2", type="float")
self.add_parameter("test_C2", "Multiple float default 1.", add_to="multi2", type="float", default=1)
self.add_parameter("test_C3", "Multiple int default 2.", add_to="multi2", type=int, default=2)
self.add_parameter("test_C4", "Multiple int null.", add_to="multi2", type=int)
self.add_parameter("test_C5", "Multiple date default str '20/08/2014'.", add_to="multi2", type="date", default="20/08/2014")
self.add_parameter("test_C6", "date default null.", add_to="multi2", type="date")
self.add_parameter("test_C7", "Multiple string default str 'None'.", add_to="multi2", default="None")
self.add_parameter("test_C8", "Multiple string null.", add_to="multi2")
self.add_parameter("test_C9", "Multiple bool default True.", add_to="multi2", type="bool", default=True)
self.add_parameter("test_C10", "Multiple bool default 3.", add_to="multi2", type="bool", default=3)
self.add_parameter("test_C11", "Multiple bool default null.", add_to="multi2", type="bool")
self.add_input_file("test_C12", "Multiple InputFile default null.", add_to="multi2")
self.add_input_file("test_C13", "Multiple InputFile default application.properties.", add_to="multi2", default="application.properties")##### Pb localfile and urlfile
def process(self):
print "###################### Workflow ############################"
for attr in self.params_order:
value = getattr(self, attr)
print attr
print value
print "class : " + value.__class__.__name__
print "is None : " + str(value is None)
print "== None : " + str(value == None)
print ""
test_parameters = self.add_component("TestComponentParameters", [
self.test_B1,
self.test_B2,
self.test_B3,
self.test_B4,
self.test_B5,
self.test_B6,
self.test_B7,
self.test_B8,
self.test_B9,
self.test_B10,
self.test_B11,
self.test_B12,
self.test_B13,
self.multi2[0]["test_C1"],
self.multi2[0]["test_C2"],
self.multi2[0]["test_C3"],
self.multi2[0]["test_C4"],
self.multi2[0]["test_C5"],
self.multi2[0]["test_C6"],
self.multi2[0]["test_C7"],
self.multi2[0]["test_C8"],