Skip to content

Commit

Permalink
Merge pull request #147 from biosimulators/att-changes-split
Browse files Browse the repository at this point in the history
Att changes split
  • Loading branch information
luciansmith committed Jul 26, 2023
2 parents 9440caf + 580ec7e commit d227222
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 65 deletions.
2 changes: 1 addition & 1 deletion biosimulators_utils/_version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '0.1.184'
__version__ = '0.1.185'
141 changes: 77 additions & 64 deletions biosimulators_utils/sedml/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,7 @@ def apply_changes_to_xml_model(model, model_etree, sed_doc=None, working_dir=Non
"""

# First pass: Must-be-XML changes:
non_xml_changes = []
for change in model.changes:
if isinstance(change, AddElementModelChange):
parents = eval_xpath(model_etree, change.target, change.target_namespaces)
Expand Down Expand Up @@ -480,88 +481,100 @@ def apply_changes_to_xml_model(model, model_etree, sed_doc=None, working_dir=Non
parent = element.getparent()
parent.remove(element)

elif isinstance(change, ModelAttributeChange) or isinstance(change, ComputeModelChange):
change.model = model
elif isinstance(change, ModelAttributeChange):
obj_xpath, sep, attr = change.target.rpartition('/@')
if sep != '/@':
change.model = model
non_xml_changes.append(change)
continue
# get object to change
obj_xpath, sep, attr = change.target.rpartition('/@')
if sep != '/@':
raise NotImplementedError('target ' + change.target + ' cannot be changed by XML manipulation, as the target '
'is not an attribute of a model element')
objs = eval_xpath(model_etree, obj_xpath, change.target_namespaces)
if validate_unique_xml_targets and len(objs) != 1:
raise ValueError('xpath {} must match a single object'.format(obj_xpath))

ns_prefix, _, attr = attr.rpartition(':')
if ns_prefix:
ns = change.target_namespaces.get(ns_prefix, None)
if ns is None:
raise ValueError('No namespace is defined with prefix `{}`'.format(ns_prefix))
attr = '{{{}}}{}'.format(ns, attr)

# change value
for obj in objs:
obj.set(attr, change.new_value)

elif isinstance(change, ComputeModelChange):
# get the values of model variables referenced by compute model changes
if variable_values is None:
model_etrees = {model.id: model_etree}
iter_variable_values = get_values_of_variable_model_xml_targets_of_model_change(change, sed_doc, model_etrees, working_dir)
else:
iter_variable_values = variable_values

# calculate new value
new_value = calc_compute_model_change_new_value(change, variable_values=iter_variable_values, range_values=range_values)
if new_value == int(new_value):
new_value = str(int(new_value))
else:
new_value = str(new_value)

# get object to change
obj_xpath, sep, attr = change.target.rpartition('/@')
if sep != '/@':
# Save this for the next pass:
change.model = model
change.new_value = new_value
non_xml_changes.append(change)
continue
objs = eval_xpath(model_etree, obj_xpath, change.target_namespaces)
if validate_unique_xml_targets and len(objs) != 1:
raise ValueError('xpath {} must match a single object'.format(obj_xpath))

ns_prefix, _, attr = attr.rpartition(':')
if ns_prefix:
ns = change.target_namespaces.get(ns_prefix, None)
if ns is None:
raise ValueError('No namespace is defined with prefix `{}`'.format(ns_prefix))
attr = '{{{}}}{}'.format(ns, attr)

# change value
for obj in objs:
obj.set(attr, new_value)

else:
raise NotImplementedError('Change{} of type {} is not supported.'.format(
' ' + change.name if change.name else '', change.__class__.__name__))

# Interlude: set up the preprocessed task, if there's a set_value_executor
preprocessed_task = None
if set_value_executer:
if preprocessed_task_sub_executer:
model_etree.write(model.source,
xml_declaration=True,
encoding="utf-8",
standalone=False,
pretty_print=True)

if preprocessed_task_sub_executer:
preprocessed_task = preprocessed_task_sub_executer()
preprocessed_task = preprocessed_task_sub_executer()

# Second pass: changes that might need to be interpreter-based:
for change in model.changes:
# Second pass: changes that need to be interpreter-based:
for change in non_xml_changes:
if isinstance(change, ModelAttributeChange):
if set_value_executer:
set_value_executer(change.model, change.target, None, change.new_value, preprocessed_task)
if not set_value_executer:
raise NotImplementedError('target ' + change.target + ' cannot be changed by XML manipulation, as the target '
'is not an attribute of a model element')
else:
# get object to change
obj_xpath, sep, attr = change.target.rpartition('/@')
if sep != '/@':
raise NotImplementedError('target ' + change.target + ' cannot be changed by XML manipulation, as the target '
'is not an attribute of a model element')
objs = eval_xpath(model_etree, obj_xpath, change.target_namespaces)
if validate_unique_xml_targets and len(objs) != 1:
raise ValueError('xpath {} must match a single object'.format(obj_xpath))

ns_prefix, _, attr = attr.rpartition(':')
if ns_prefix:
ns = change.target_namespaces.get(ns_prefix, None)
if ns is None:
raise ValueError('No namespace is defined with prefix `{}`'.format(ns_prefix))
attr = '{{{}}}{}'.format(ns, attr)

# change value
for obj in objs:
obj.set(attr, change.new_value)
set_value_executer(change.model, change.target, None, change.new_value, preprocessed_task)

elif isinstance(change, ComputeModelChange):
# get the values of model variables referenced by compute model changes
if variable_values is None:
model_etrees = {model.id: model_etree}
iter_variable_values = get_values_of_variable_model_xml_targets_of_model_change(change, sed_doc, model_etrees, working_dir)
else:
iter_variable_values = variable_values

# calculate new value
new_value = calc_compute_model_change_new_value(change, variable_values=iter_variable_values, range_values=range_values)
if new_value == int(new_value):
new_value = str(int(new_value))
else:
new_value = str(new_value)

if set_value_executer:
set_value_executer(change.model, change.target, change.symbol, new_value, preprocessed_task)
else:
# get object to change
obj_xpath, sep, attr = change.target.rpartition('/@')
if sep != '/@':
raise NotImplementedError('target ' + change.target + ' cannot be changed by XML manipulation, as the target '
'is not an attribute of a model element')
objs = eval_xpath(model_etree, obj_xpath, change.target_namespaces)
if validate_unique_xml_targets and len(objs) != 1:
raise ValueError('xpath {} must match a single object'.format(obj_xpath))

ns_prefix, _, attr = attr.rpartition(':')
if ns_prefix:
ns = change.target_namespaces.get(ns_prefix, None)
if ns is None:
raise ValueError('No namespace is defined with prefix `{}`'.format(ns_prefix))
attr = '{{{}}}{}'.format(ns, attr)

# change value
for obj in objs:
obj.set(attr, new_value)
obj_xpath, sep, attr = change.target.rpartition('/@')
if not set_value_executer:
raise NotImplementedError('target ' + change.target + ' cannot be changed by XML manipulation, as the target '
'is not an attribute of a model element')
set_value_executer(change.model, change.target, change.symbol, change.new_value, preprocessed_task)

return preprocessed_task

Expand Down

0 comments on commit d227222

Please sign in to comment.