better leader/follower support
This commit is contained in:
parent
626184fee7
commit
4ec269f2b9
|
@ -71,12 +71,7 @@ class TiramisuOptionOption:
|
||||||
return self.schema['type'] == 'array'
|
return self.schema['type'] == 'array'
|
||||||
|
|
||||||
def isleader(self):
|
def isleader(self):
|
||||||
if '.' in self._path:
|
return self.config.isleader(self._path)
|
||||||
parent_schema = self.config.get_schema(self._path.rsplit('.', 1)[0])
|
|
||||||
if parent_schema['type'] == 'array':
|
|
||||||
leader = next(iter(parent_schema['properties'].keys()))
|
|
||||||
return leader == self._path
|
|
||||||
return False
|
|
||||||
|
|
||||||
def isfollower(self):
|
def isfollower(self):
|
||||||
return self.config.isfollower(self._path)
|
return self.config.isfollower(self._path)
|
||||||
|
@ -109,6 +104,13 @@ class TiramisuOptionOption:
|
||||||
# FIXME
|
# FIXME
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def pattern(self):
|
||||||
|
if self._path in self.form:
|
||||||
|
return self.form[self._path].get('pattern')
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
class TiramisuOptionProperty:
|
class TiramisuOptionProperty:
|
||||||
# config.option(path).property
|
# config.option(path).property
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
|
@ -268,6 +270,9 @@ class TiramisuOptionValue(_Value):
|
||||||
|
|
||||||
def set(self, value):
|
def set(self, value):
|
||||||
type_ = self.schema['type']
|
type_ = self.schema['type']
|
||||||
|
leader_old_value = undefined
|
||||||
|
if self.config.isleader(self.path):
|
||||||
|
leader_old_value = self.config.get_value(self.path)
|
||||||
remote = self.form.get(self.path, {}).get('remote', False)
|
remote = self.form.get(self.path, {}).get('remote', False)
|
||||||
if self.index is None and self.schema.get('isMulti', False):
|
if self.index is None and self.schema.get('isMulti', False):
|
||||||
if not isinstance(value, list):
|
if not isinstance(value, list):
|
||||||
|
@ -287,14 +292,13 @@ class TiramisuOptionValue(_Value):
|
||||||
self.config.modify_value(self.path,
|
self.config.modify_value(self.path,
|
||||||
self.index,
|
self.index,
|
||||||
value,
|
value,
|
||||||
remote)
|
remote,
|
||||||
|
leader_old_value)
|
||||||
self._display_warnings(self.path, value, type_, self.schema['name'])
|
self._display_warnings(self.path, value, type_, self.schema['name'])
|
||||||
|
|
||||||
def reset(self):
|
def reset(self):
|
||||||
remote = self.form.get(self.path, {}).get('remote', False)
|
|
||||||
self.config.delete_value(self.path,
|
self.config.delete_value(self.path,
|
||||||
self.index,
|
self.index)
|
||||||
remote)
|
|
||||||
|
|
||||||
def default(self):
|
def default(self):
|
||||||
return self.schema.get('value')
|
return self.schema.get('value')
|
||||||
|
@ -354,8 +358,8 @@ class TiramisuOptionDescription(_Option):
|
||||||
return TiramisuOptionProperty(self.config,
|
return TiramisuOptionProperty(self.config,
|
||||||
self.path,
|
self.path,
|
||||||
self.model.get(self.path, {}))
|
self.model.get(self.path, {}))
|
||||||
if subfunc == 'value':
|
if subfunc == 'value' and self.schema['type'] == 'array':
|
||||||
return TiramisuOptionValue(self.config,
|
return TiramisuLeadershipValue(self.config,
|
||||||
self.schema,
|
self.schema,
|
||||||
self.model,
|
self.model,
|
||||||
self.form,
|
self.form,
|
||||||
|
@ -371,6 +375,30 @@ class TiramisuOptionDescription(_Option):
|
||||||
raise PropertiesOptionError(None, None, None, opt_type='optiondescription')
|
raise PropertiesOptionError(None, None, None, opt_type='optiondescription')
|
||||||
|
|
||||||
|
|
||||||
|
class TiramisuLeadershipValue:
|
||||||
|
def __init__(self,
|
||||||
|
config,
|
||||||
|
schema,
|
||||||
|
model,
|
||||||
|
form,
|
||||||
|
temp,
|
||||||
|
path):
|
||||||
|
self.config = config
|
||||||
|
self.schema = schema
|
||||||
|
self.model = model
|
||||||
|
self.form = form
|
||||||
|
self.temp = temp
|
||||||
|
self.path = path
|
||||||
|
|
||||||
|
def len(self):
|
||||||
|
return len(self.config.get_value(self.schema['properties'][0]))
|
||||||
|
|
||||||
|
def pop(self,
|
||||||
|
index: int) -> None:
|
||||||
|
leadership_path = self.schema['properties'][0]
|
||||||
|
self.config.delete_value(leadership_path, index)
|
||||||
|
|
||||||
|
|
||||||
class TiramisuOption:
|
class TiramisuOption:
|
||||||
# config.option(path) (with path == Option)
|
# config.option(path) (with path == Option)
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
|
@ -520,9 +548,8 @@ class Config:
|
||||||
self.model = json['model']
|
self.model = json['model']
|
||||||
self.form = json['form']
|
self.form = json['form']
|
||||||
# support pattern
|
# support pattern
|
||||||
for key, option in json['form'].items():
|
for key, option in self.form.items():
|
||||||
if key != 'null':
|
if key != 'null' and 'pattern' in option:
|
||||||
if 'pattern' in option:
|
|
||||||
option['pattern'] = re.compile(option['pattern'])
|
option['pattern'] = re.compile(option['pattern'])
|
||||||
self.temp = {}
|
self.temp = {}
|
||||||
self.schema = json['schema']
|
self.schema = json['schema']
|
||||||
|
@ -556,18 +583,22 @@ class Config:
|
||||||
index: Optional[int],
|
index: Optional[int],
|
||||||
value: Any,
|
value: Any,
|
||||||
remote: bool) -> None:
|
remote: bool) -> None:
|
||||||
|
self.manage_updates('add',
|
||||||
|
path,
|
||||||
|
index,
|
||||||
|
value)
|
||||||
self.updates_value('add',
|
self.updates_value('add',
|
||||||
path,
|
path,
|
||||||
index,
|
index,
|
||||||
value,
|
value,
|
||||||
remote,
|
remote)
|
||||||
None)
|
|
||||||
|
|
||||||
def modify_value(self,
|
def modify_value(self,
|
||||||
path: str,
|
path: str,
|
||||||
index: Optional[int],
|
index: Optional[int],
|
||||||
value: Any,
|
value: Any,
|
||||||
remote: bool) -> None:
|
remote: bool,
|
||||||
|
leader_old_value: Any) -> None:
|
||||||
schema = self.get_schema(path)
|
schema = self.get_schema(path)
|
||||||
if value and isinstance(value, list) and value[-1] is undefined:
|
if value and isinstance(value, list) and value[-1] is undefined:
|
||||||
new_value = schema.get('defaultvalue')
|
new_value = schema.get('defaultvalue')
|
||||||
|
@ -578,23 +609,31 @@ class Config:
|
||||||
new_value = schema_value[len_value - 1]
|
new_value = schema_value[len_value - 1]
|
||||||
value[-1] = new_value
|
value[-1] = new_value
|
||||||
|
|
||||||
|
self.manage_updates('modify',
|
||||||
|
path,
|
||||||
|
index,
|
||||||
|
value)
|
||||||
self.updates_value('modify',
|
self.updates_value('modify',
|
||||||
path,
|
path,
|
||||||
index,
|
index,
|
||||||
value,
|
value,
|
||||||
remote,
|
remote,
|
||||||
None)
|
False,
|
||||||
|
leader_old_value)
|
||||||
|
|
||||||
def delete_value(self,
|
def delete_value(self,
|
||||||
path: str,
|
path: str,
|
||||||
index: Optional[int],
|
index: Optional[int]) -> None:
|
||||||
remote: bool) -> None:
|
remote = self.form.get(path, {}).get('remote', False)
|
||||||
|
self.manage_updates('delete',
|
||||||
|
path,
|
||||||
|
index,
|
||||||
|
None)
|
||||||
self.updates_value('delete',
|
self.updates_value('delete',
|
||||||
path,
|
path,
|
||||||
index,
|
index,
|
||||||
None,
|
None,
|
||||||
remote,
|
remote)
|
||||||
None)
|
|
||||||
|
|
||||||
def get_properties(self,
|
def get_properties(self,
|
||||||
model,
|
model,
|
||||||
|
@ -638,6 +677,16 @@ class Config:
|
||||||
schema = schema['properties'][root_path]
|
schema = schema['properties'][root_path]
|
||||||
return schema
|
return schema
|
||||||
|
|
||||||
|
def isleader(self,
|
||||||
|
path):
|
||||||
|
if '.' in path:
|
||||||
|
parent_schema = self.get_schema(path.rsplit('.', 1)[0])
|
||||||
|
if parent_schema['type'] == 'array':
|
||||||
|
leader = next(iter(parent_schema['properties'].keys()))
|
||||||
|
return leader == path
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def isfollower(self,
|
def isfollower(self,
|
||||||
path: str) -> bool:
|
path: str) -> bool:
|
||||||
if '.' in path:
|
if '.' in path:
|
||||||
|
@ -652,16 +701,18 @@ class Config:
|
||||||
path: str,
|
path: str,
|
||||||
index: Optional[int]) -> bool:
|
index: Optional[int]) -> bool:
|
||||||
for property_, needs in {'hidden': True, 'display': False}.items():
|
for property_, needs in {'hidden': True, 'display': False}.items():
|
||||||
|
if index is not None and property_ in self.temp.get(path, {}).get(str(index), {}):
|
||||||
|
return self.temp[path][str(index)][property_]
|
||||||
if property_ in self.temp.get(path, {}):
|
if property_ in self.temp.get(path, {}):
|
||||||
return self.temp[path][property_]
|
return self.temp[path][property_]
|
||||||
else:
|
elif self.isfollower(path):
|
||||||
if self.isfollower(path):
|
|
||||||
if self.model.get(path, {}).get('null', {}).get(property_, None) == needs:
|
if self.model.get(path, {}).get('null', {}).get(property_, None) == needs:
|
||||||
return True
|
return True
|
||||||
elif self.model.get(path, {}).get(property_, None) == needs:
|
elif self.model.get(path, {}).get(property_, None) == needs:
|
||||||
return True
|
return True
|
||||||
|
if index is not None:
|
||||||
index = str(index)
|
index = str(index)
|
||||||
if index != 'None' and index in self.model.get(path, {}) and self.model.get(path, {}).get(index, {}).get(property_, None) == needs:
|
if self.model.get(path, {}).get(index, {}).get(property_) == needs:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -683,7 +734,7 @@ class Config:
|
||||||
index = str(index)
|
index = str(index)
|
||||||
if 'delete' in self.temp.get(path, {}):
|
if 'delete' in self.temp.get(path, {}):
|
||||||
value = None
|
value = None
|
||||||
elif index in self.temp.get(path, {}) and 'delete' in self.temp[path][index]:
|
elif self.temp.get(path, {}).get(index, {}).get('delete') == True:
|
||||||
value = None
|
value = None
|
||||||
elif 'value' in self.temp.get(path, {}).get(index, {}):
|
elif 'value' in self.temp.get(path, {}).get(index, {}):
|
||||||
value = self.temp[path]
|
value = self.temp[path]
|
||||||
|
@ -703,6 +754,8 @@ class Config:
|
||||||
value = schema.get('default')
|
value = schema.get('default')
|
||||||
if value is None and schema.get('isSubMulti', False):
|
if value is None and schema.get('isSubMulti', False):
|
||||||
value = []
|
value = []
|
||||||
|
if isinstance(value, list):
|
||||||
|
value = value.copy()
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def get_owner(self,
|
def get_owner(self,
|
||||||
|
@ -721,19 +774,17 @@ class Config:
|
||||||
index = str(index)
|
index = str(index)
|
||||||
if self.is_hidden(path, index):
|
if self.is_hidden(path, index):
|
||||||
raise PropertiesOptionError(None, None, None, opt_type='option')
|
raise PropertiesOptionError(None, None, None, opt_type='option')
|
||||||
if index in value:
|
if 'owner' in value.get(index, {}):
|
||||||
owner = value[index]['owner']
|
owner = value[index]['owner']
|
||||||
else:
|
else:
|
||||||
owner = 'default'
|
owner = 'default'
|
||||||
return owner
|
return owner
|
||||||
|
|
||||||
def updates_value(self,
|
def manage_updates(self,
|
||||||
action: str,
|
action,
|
||||||
path: str,
|
path,
|
||||||
index: Optional[int],
|
index,
|
||||||
value: Optional[Any],
|
value):
|
||||||
remote: bool,
|
|
||||||
leadership: Optional[str]) -> None:
|
|
||||||
update_last_action = False
|
update_last_action = False
|
||||||
if self.updates:
|
if self.updates:
|
||||||
last_body = self.updates[-1]
|
last_body = self.updates[-1]
|
||||||
|
@ -745,8 +796,7 @@ class Config:
|
||||||
update_last_action = True
|
update_last_action = True
|
||||||
elif index == None and action == 'delete':
|
elif index == None and action == 'delete':
|
||||||
for update in reversed(self.updates):
|
for update in reversed(self.updates):
|
||||||
if leadership is None and update['name'] == path or \
|
if update['name'] == path:
|
||||||
leadership and path.startswith(leadership + '.'):
|
|
||||||
del self.updates[-1]
|
del self.updates[-1]
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
|
@ -777,6 +827,15 @@ class Config:
|
||||||
if index is not None:
|
if index is not None:
|
||||||
data['index'] = index
|
data['index'] = index
|
||||||
self.updates.append(data)
|
self.updates.append(data)
|
||||||
|
|
||||||
|
def updates_value(self,
|
||||||
|
action: str,
|
||||||
|
path: str,
|
||||||
|
index: Optional[int],
|
||||||
|
value: Optional[Any],
|
||||||
|
remote: bool,
|
||||||
|
default_value: bool=False,
|
||||||
|
leader_old_value: Optional[Any]=undefined) -> None:
|
||||||
if 'pattern' in self.form.get(path, {}) and (not isinstance(value, list) or undefined not in value):
|
if 'pattern' in self.form.get(path, {}) and (not isinstance(value, list) or undefined not in value):
|
||||||
match = self.test_value(path, value, remote)
|
match = self.test_value(path, value, remote)
|
||||||
else:
|
else:
|
||||||
|
@ -792,7 +851,7 @@ class Config:
|
||||||
# leader or standard option
|
# leader or standard option
|
||||||
# set value to default value
|
# set value to default value
|
||||||
value = self.default_value(path)
|
value = self.default_value(path)
|
||||||
self.temp[path] = {'value': value, 'owner': 'default'}
|
self._set_temp_value(path, None, value, 'default')
|
||||||
if self.option(path).option.isleader():
|
if self.option(path).option.isleader():
|
||||||
# if leader, set follower to default value
|
# if leader, set follower to default value
|
||||||
leadership_path = path.rsplit('.', 1)[0]
|
leadership_path = path.rsplit('.', 1)[0]
|
||||||
|
@ -800,37 +859,98 @@ class Config:
|
||||||
iter_leadership = list(parent_schema['properties'].keys())
|
iter_leadership = list(parent_schema['properties'].keys())
|
||||||
for follower in iter_leadership[1:]:
|
for follower in iter_leadership[1:]:
|
||||||
# delete all values
|
# delete all values
|
||||||
self.temp[follower] = {'delete': True}
|
self._del_temp_value(follower, None)
|
||||||
|
value = self.get_value(path)
|
||||||
elif self.option(path).option.isleader():
|
elif self.option(path).option.isleader():
|
||||||
# if remove an indexed leader value
|
# if remove an indexed leader value
|
||||||
old_value = self.option(path).value.get()
|
leader_value = self.option(path).value.get()
|
||||||
old_value.pop(index)
|
leader_value.pop(index)
|
||||||
self.temp[path] = {'value': old_value, 'owner': 'tmp'}
|
max_value = len(leader_value)
|
||||||
|
self._set_temp_value(path, None, leader_value, 'tmp')
|
||||||
leadership_path = path.rsplit('.', 1)[0]
|
leadership_path = path.rsplit('.', 1)[0]
|
||||||
parent_schema = self.get_schema(leadership_path)
|
parent_schema = self.get_schema(leadership_path)
|
||||||
iter_leadership = list(parent_schema['properties'].keys())
|
iter_leadership = list(parent_schema['properties'].keys())
|
||||||
for follower in iter_leadership[1:]:
|
for follower in iter_leadership[1:]:
|
||||||
# remove value for this index and reduce len
|
# remove value for this index and reduce len
|
||||||
#FIXME on ne reduce pas la longueur !!!!
|
new_temp = {}
|
||||||
self.temp.setdefault(follower, {})[str(index)] = {'delete': True}
|
for idx in range(max_value):
|
||||||
|
if index == idx:
|
||||||
|
continue
|
||||||
|
cur_index = idx
|
||||||
|
if index < cur_index:
|
||||||
|
cur_index = -1
|
||||||
|
if 'delete' in self.temp.get(follower, {}):
|
||||||
|
#FIXME copier les attributs hidden, ... depuis self.temp[follower][index] ?
|
||||||
|
new_temp[str(cur_index)] = {'delete': True}
|
||||||
|
elif self.temp.get(follower, {}).get(str(idx)) is not None:
|
||||||
|
new_temp[str(cur_index)] = self.temp[follower][str(idx)]
|
||||||
|
elif self.model.get(follower, {}).get(str(idx)) is not None:
|
||||||
|
new_temp[str(cur_index)] = self.model[follower][str(idx)]
|
||||||
|
if self.model[follower].get(str(max_value)) is not None:
|
||||||
|
# FIXME copier les attributs hidden, ... depuis self.temp[follower][index] ?
|
||||||
|
new_temp[str(max_value)] = {'delete': True}
|
||||||
|
self.temp[follower] = new_temp
|
||||||
|
value = leader_value
|
||||||
|
index = None
|
||||||
else:
|
else:
|
||||||
# it's a follower with index
|
# it's a follower with index
|
||||||
self.temp.setdefault(path, {})[str(index)] = {'delete': True}
|
self.temp.setdefault(path, {})[str(index)] = {'delete': True}
|
||||||
|
self._del_temp_value(path, index)
|
||||||
|
value = self.get_value(path, index)
|
||||||
|
default_value = True
|
||||||
elif index is None:
|
elif index is None:
|
||||||
# set a value for a not follower option
|
# set a value for a not follower option
|
||||||
self.temp[path] = {'value': value, 'owner': 'tmp'}
|
if default_value is True:
|
||||||
|
self.model[path]['value'] = value
|
||||||
|
else:
|
||||||
|
self._set_temp_value(path, None, value, 'tmp')
|
||||||
else:
|
else:
|
||||||
# set a value for a follower option
|
# set a value for a follower option
|
||||||
self.temp.setdefault(path, {})[str(index)] = {'value': value, 'owner': 'tmp'}
|
self.temp.setdefault(path, {})[str(index)] = {'value': value, 'owner': 'tmp'}
|
||||||
self.set_dependencies(path, value)
|
if default_value is True:
|
||||||
|
self.model[path][str(index)]['value'] = value
|
||||||
|
else:
|
||||||
|
self._set_temp_value(path, index, value, 'tmp')
|
||||||
|
self.set_dependencies(path, value, index=index)
|
||||||
self.set_not_equal(path, value)
|
self.set_not_equal(path, value)
|
||||||
self.do_copy(path, value)
|
self.do_copy(path, value)
|
||||||
|
if leader_old_value is not undefined and len(leader_old_value) < len(value):
|
||||||
|
# if leader and length is change, display/hide follower from follower's default value
|
||||||
|
index = len(value) - 1
|
||||||
|
parent_path = '.'.join(path.split('.')[:-1])
|
||||||
|
followers = list(self.option(parent_path).list())[1:]
|
||||||
|
for follower in followers:
|
||||||
|
follower_path = follower.option.path()
|
||||||
|
follower_value = self.option(follower_path, index).value.get()
|
||||||
|
self.set_dependencies(follower_path, follower_value, None, index)
|
||||||
|
|
||||||
|
def _set_temp_value(self, path, index, value, owner):
|
||||||
|
if index is not None:
|
||||||
|
obj = self.temp.setdefault(path, {}).setdefault(str(index), {})
|
||||||
|
else:
|
||||||
|
obj = self.temp.setdefault(path, {})
|
||||||
|
if 'delete' in obj:
|
||||||
|
del obj['delete']
|
||||||
|
obj['value'] = value
|
||||||
|
obj['owner'] = owner
|
||||||
|
|
||||||
|
def _del_temp_value(self, path, index):
|
||||||
|
if index is not None:
|
||||||
|
obj = self.temp.setdefault(path, {}).setdefault(str(index), {})
|
||||||
|
else:
|
||||||
|
obj = self.temp.setdefault(path, {})
|
||||||
|
for key in ['value', 'owner']:
|
||||||
|
if key in obj:
|
||||||
|
del obj[key]
|
||||||
|
obj['delete'] = True
|
||||||
|
|
||||||
def default_value(self, path):
|
def default_value(self, path):
|
||||||
schema = self.get_schema(path)
|
schema = self.get_schema(path)
|
||||||
value = schema.get('value');
|
value = schema.get('value')
|
||||||
if value is None and schema.get('isMulti', False):
|
if value is None and schema.get('isMulti', False):
|
||||||
value = []
|
value = []
|
||||||
|
elif isinstance(value, list):
|
||||||
|
value = value.copy()
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def updates_data(self, data):
|
def updates_data(self, data):
|
||||||
|
@ -862,7 +982,8 @@ class Config:
|
||||||
def set_dependencies(self,
|
def set_dependencies(self,
|
||||||
path: str,
|
path: str,
|
||||||
ori_value: Any,
|
ori_value: Any,
|
||||||
force_hide: bool=False) -> None:
|
force_hide: bool=False,
|
||||||
|
index: Optional[int]=None) -> None:
|
||||||
dependencies = self.form.get(path, {}).get('dependencies', {})
|
dependencies = self.form.get(path, {}).get('dependencies', {})
|
||||||
if dependencies:
|
if dependencies:
|
||||||
if ori_value in dependencies['expected']:
|
if ori_value in dependencies['expected']:
|
||||||
|
@ -877,12 +998,12 @@ class Config:
|
||||||
else:
|
else:
|
||||||
hidden = action == 'hide'
|
hidden = action == 'hide'
|
||||||
for expected_path in expected_actions:
|
for expected_path in expected_actions:
|
||||||
self.temp.setdefault(expected_path, {})['hidden'] = hidden
|
if index is not None:
|
||||||
if 'value' in self.temp[expected_path]:
|
self.temp.setdefault(expected_path, {}).setdefault(str(index), {})['hidden'] = hidden
|
||||||
value = self.temp[expected_path]['value']
|
|
||||||
else:
|
else:
|
||||||
value = self.model[expected_path].get('value')
|
self.temp.setdefault(expected_path, {})['hidden'] = hidden
|
||||||
self.set_dependencies(expected_path, value, hidden)
|
value = self.get_value(expected_path, index)
|
||||||
|
self.set_dependencies(expected_path, value, hidden, index)
|
||||||
|
|
||||||
def set_not_equal(self,
|
def set_not_equal(self,
|
||||||
path: str,
|
path: str,
|
||||||
|
@ -957,9 +1078,14 @@ class Config:
|
||||||
# FIXME follower!
|
# FIXME follower!
|
||||||
owner = self.get_owner(opt, None)
|
owner = self.get_owner(opt, None)
|
||||||
if owner == 'default':
|
if owner == 'default':
|
||||||
# do not change in this.temp, it's default value
|
# do not change in self.temp, it's default value
|
||||||
self.model[opt]['value'] = value
|
if self.model[opt]['value'] != value:
|
||||||
|
if isinstance(value, list):
|
||||||
|
value = value.copy()
|
||||||
|
# self.model[opt]['value'] = value
|
||||||
|
remote = self.form.get(opt, {}).get('remote', False)
|
||||||
|
self.updates_value('modify', opt, None, value, remote, True)
|
||||||
|
|
||||||
def send_data(self,
|
def send_data(self,
|
||||||
updates):
|
updates):
|
||||||
raise NotImplementedError('please implement send_data function')
|
raise NotImplementedError('please implement send_data method')
|
||||||
|
|
Loading…
Reference in New Issue