diff --git a/obis/src/python/integration_tests/integration_tests.py b/obis/src/python/integration_tests/integration_tests.py index 4651f7e527716beaeefbe88432e9f1a5b10ae0a7..540a467e9bcd251330ee91ca4f461deced31482b 100644 --- a/obis/src/python/integration_tests/integration_tests.py +++ b/obis/src/python/integration_tests/integration_tests.py @@ -11,6 +11,7 @@ import subprocess from subprocess import PIPE from subprocess import SubprocessError from contextlib import contextmanager +from random import randrange from pybis import Openbis @@ -31,6 +32,7 @@ def test_obis(tmpdir): o = Openbis('https://obisserver:8443', verify_certificates=False) o.login('admin', 'admin', save_token=True) + setup_masterdata(o) output_buffer = '=================== 1. Global settings ===================\n' if os.path.exists('~/.obis'): @@ -322,6 +324,18 @@ def test_obis(tmpdir): timeout = True assert timeout == True + output_buffer = '=================== 22. changing identifier ===================\n' + settings = create_repository_and_commit(tmpdir, o, 'data14', '/DEFAULT/BIGDATA2') + move_sample(o, settings['object']['permId'], 'BIGDATA') + try: + settings = commit_new_change(tmpdir, o, 'data14') + assert settings['object']['id'] == '/BIGDATA/BIGDATA2' + finally: + move_sample(o, settings['object']['permId'], 'DEFAULT') + with cd('data14'): assert get_settings()['object']['permId'] is not None + cmd('obis object set id=/DEFAULT/DEFAULT') + with cd('data14'): assert get_settings()['object']['permId'] is not None + def assert_file_paths(files, expected_paths): paths = list(map(lambda file: file['path'], files)) @@ -373,5 +387,57 @@ def assert_matching(settings, data_set, tmpdir, path): assert content_copy['gitRepositoryId'] == settings['repository']['id'] if settings['object']['id'] is not None: assert data_set['sample']['identifier']['identifier'] == settings['object']['id'] + assert data_set['sample']['permId']['permId'] == settings['object']['permId'] if settings['collection']['id'] is not None: assert data_set['experiment']['identifier']['identifier'] == settings['collection']['id'] + assert data_set['experiment']['permId']['permId'] == settings['collection']['permId'] + +def move_sample(o, sample_permId, space): + field_update_value = { + "@type": "as.dto.common.update.FieldUpdateValue", + "value": { + "@type": "as.dto.space.id.SpacePermId", + "permId": space, + }, + "isModified": True, + } + o.update_sample(sample_permId, space=field_update_value) + +def create_repository_and_commit(tmpdir, o, repo_name, object_id): + cmd('obis init ' + repo_name) + with cd(repo_name): + cmd('touch file') + result = cmd('obis status') + assert '? file' in result + cmd('obis object set id=' + object_id) + result = cmd('obis commit -m \'commit-message\'') + settings = get_settings() + assert settings['repository']['external_dms_id'].startswith('ADMIN-' + socket.gethostname().upper()) + assert len(settings['repository']['id']) == 36 + assert "Created data set {}.".format(settings['repository']['data_set_id']) in result + data_set = o.get_dataset(settings['repository']['data_set_id']).data + assert_matching(settings, data_set, tmpdir, 'obis_data/' + repo_name) + return settings + +def commit_new_change(tmpdir, o, repo_name): + with cd(repo_name): + filename = 'file' + str(randrange(100000)) + cmd('touch ' + filename) + result = cmd('obis status') + assert '? ' + filename in result + result = cmd('obis commit -m \'commit-message\'') + settings = get_settings() + assert settings['repository']['external_dms_id'].startswith('ADMIN-' + socket.gethostname().upper()) + assert len(settings['repository']['id']) == 36 + assert "Created data set {}.".format(settings['repository']['data_set_id']) in result + data_set = o.get_dataset(settings['repository']['data_set_id']).data + assert_matching(settings, data_set, tmpdir, 'obis_data/' + repo_name) + return settings + + +def setup_masterdata(o): + spaces = o.get_spaces() + if 'BIGDATA' not in o.get_spaces().df.code.values: + o.new_space(code='BIGDATA').save() + if '/DEFAULT/BIGDATA2' not in o.get_samples().df.identifier.values: + o.new_sample(type="UNKNOWN", code='BIGDATA2', space='DEFAULT').save() diff --git a/obis/src/python/obis/dm/commands/openbis_command.py b/obis/src/python/obis/dm/commands/openbis_command.py index 734282e8ae65563faa14df55a11600109c3236e2..599d66ca2400e5dda99a7e35d4daca1bb4814b6d 100644 --- a/obis/src/python/obis/dm/commands/openbis_command.py +++ b/obis/src/python/obis/dm/commands/openbis_command.py @@ -60,12 +60,18 @@ class OpenbisCommand(object): def object_id(self): return self.config_dict['object']['id'] + def object_permId(self): + return self.config_dict['object']['permId'] + def set_object_id(self, value): self.config_dict['object']['id'] = value def collection_id(self): return self.config_dict['collection']['id'] + def collection_permId(self): + return self.config_dict['collection']['permId'] + def set_collection_id(self, value): self.config_dict['collection']['id'] = value diff --git a/obis/src/python/obis/dm/commands/openbis_sync.py b/obis/src/python/obis/dm/commands/openbis_sync.py index e6e1476a2ec4cdaad5075a5cd252d009be5853f7..3cd70b28335d97e0b7e0ca6d4890620a796d2898 100644 --- a/obis/src/python/obis/dm/commands/openbis_sync.py +++ b/obis/src/python/obis/dm/commands/openbis_sync.py @@ -50,8 +50,7 @@ class OpenbisSync(OpenbisCommand): if result.failure(): return result commit_id = result.output - sample_id = self.object_id() - experiment_id = self.collection_id() + sample_id, experiment_id = self._update_and_get_object_or_collection_id() contents = GitRepoFileInfo(self.git_wrapper).contents(git_annex_hash_as_checksum=self.git_annex_hash_as_checksum()) try: data_set = self.openbis.new_git_data_set(data_set_type, top_level_path, commit_id, repository_id, external_dms.code, @@ -62,8 +61,40 @@ class OpenbisSync(OpenbisCommand): return CommandResult(returncode=-1, output=str(e)), None - def commit_metadata_updates(self, msg_fragment=None): - return self.data_mgmt.commit_metadata_updates(msg_fragment) + def _update_and_get_object_or_collection_id(self): + """ Updates identifier of object / collection in case it has changed in openBIS + if the permId is available. Returns sample / experiment identifier.""" + sample_id = self.object_id() + experiment_id = self.collection_id() + if self.object_permId() is not None: + sample_id = self.openbis.get_sample(self.object_permId()).identifier + if sample_id != self.object_id(): + self.settings_resolver.object.set_value_for_parameter('id', sample_id, 'local') + # permId is cleared when the id is set - set it again + self.settings_resolver.object.set_value_for_parameter('permId', self.object_permId(), 'local') + self.commit_metadata_updates("object identifier changed in openBIS") + if self.collection_permId() is not None: + experiment_id = self.openbis.get_experiment(self.collection_permId()).identifier + if experiment_id != self.collection_id(): + self.settings_resolver.collection.set_value_for_parameter('id', experiment_id, 'local') + # permId is cleared when the id is set - set it again + self.settings_resolver.collection.set_value_for_parameter('permId', self.collection_permId(), 'local') + self.commit_metadata_updates("collection identifier changed in openBIS") + return sample_id, experiment_id + + def _storePermId(self): + if self.object_permId() is None and self.object_id() is not None: + sample = self.openbis.get_sample(self.object_id()) + self.settings_resolver.object.set_value_for_parameter('permId', sample.permId, 'local') + self.commit_metadata_updates("object permId", omit_usersettings=False) + if self.collection_permId() is None and self.collection_id() is not None: + experiment = self.openbis.get_experiment(self.collection_id()) + self.settings_resolver.collection.set_value_for_parameter('permId', experiment.permId, 'local') + self.commit_metadata_updates("collection permId", omit_usersettings=False) + + + def commit_metadata_updates(self, msg_fragment=None, omit_usersettings=True): + return self.data_mgmt.commit_metadata_updates(msg_fragment, omit_usersettings=omit_usersettings) def prepare_repository_id(self): @@ -157,10 +188,14 @@ class OpenbisSync(OpenbisCommand): self.commit_metadata_updates() + # store permId of object / collection so we can use those as a reference in the future + self._storePermId() + # Update data set id as last commit so we can easily revert it on failure self.settings_resolver.repository.set_value_for_parameter('data_set_id', data_set_code, 'local') self.commit_metadata_updates("data set id") # create a data set, using the existing data set as a parent, if there is one result, data_set = self.create_data_set(data_set_code, external_dms, repository_id, ignore_parent) + return result diff --git a/obis/src/python/obis/dm/config.py b/obis/src/python/obis/dm/config.py index 763404934fcc43312498e1170247f905a0da35ad..a2766838598ed8113034b539651007c7d98fe503 100644 --- a/obis/src/python/obis/dm/config.py +++ b/obis/src/python/obis/dm/config.py @@ -9,6 +9,7 @@ Configuration for obis. Created by Chandrasekhar Ramakrishnan on 2017-02-10. Copyright (c) 2017 Chandrasekhar Ramakrishnan. All rights reserved. """ +import abc import json import os @@ -78,8 +79,10 @@ class ConfigEnv(object): def __init__(self): self.locations = {} self.params = {} + self.rules = [] self.initialize_locations() self.initialize_params() + self.initialize_rules() def initialize_locations(self): self.add_location(ConfigLocation(['global'], 'user_home', '.obis')) @@ -120,17 +123,31 @@ class ConfigEnv(object): def is_usersetting(self): return True + def add_rule(self, rule): + self.rules.append(rule) + + def initialize_rules(self): + pass + class CollectionEnv(ConfigEnv): def initialize_params(self): self.add_param(ConfigParam(name='id', private=False, ignore_global=True)) + self.add_param(ConfigParam(name='permId', private=False, ignore_global=True)) + + def initialize_rules(self): + self.add_rule(ClearPermIdRule()) class ObjectEnv(ConfigEnv): def initialize_params(self): self.add_param(ConfigParam(name='id', private=False, ignore_global=True)) + self.add_param(ConfigParam(name='permId', private=False, ignore_global=True)) + + def initialize_rules(self): + self.add_rule(ClearPermIdRule()) class DataSetEnv(ConfigEnv): @@ -244,6 +261,9 @@ class ConfigResolver(object): with open(config_path, "w") as f: json.dump(location_config_dict, f, sort_keys=True, indent=4) + for rule in self.env.rules: + rule.on_set(self, name, value, loc) + def set_value_for_json_parameter(self, json_param_name, name, value, loc): """Set one field for the json parameter :param json_param_name: Name of the json parameter @@ -331,10 +351,10 @@ class SettingsResolver(object): combined_dict[resolver.categoty] = resolver.config_dict(local_only=local_only) return combined_dict - def local_public_properties_paths(self, get_usersettings=False): + def local_public_properties_paths(self, omit_usersettings=True): paths = [] for resolver in self.resolvers: - if get_usersettings == resolver.is_usersetting(): + if omit_usersettings == False or resolver.is_usersetting() == False: paths.append(resolver.local_public_properties_path()) return paths @@ -349,3 +369,19 @@ class SettingsResolver(object): def set_location_search_order(self, order): for resolver in self.resolvers: resolver.location_search_order = order + + +class SettingRule(object): + """ Setting rules can react to setting changes and trigger further actions. """ + + @abc.abstractmethod + def on_set(self, setting, value): + pass + + +class ClearPermIdRule(SettingRule): + """ When the user sets a new id, the permId might be invalid, so it will be cleared. """ + + def on_set(self, config_resolver, name, value, loc): + if name == "id": + config_resolver.set_value_for_parameter("permId", None, loc) diff --git a/obis/src/python/obis/dm/data_mgmt.py b/obis/src/python/obis/dm/data_mgmt.py index c5ec2315d74ec31164d741764a018293e22f077e..339bb2f40b8016b119352f3ba0cace732c649a85 100644 --- a/obis/src/python/obis/dm/data_mgmt.py +++ b/obis/src/python/obis/dm/data_mgmt.py @@ -365,8 +365,8 @@ class GitDataMgmt(AbstractDataMgmt): output += sync_status.output return CommandResult(returncode=0, output=output) - def commit_metadata_updates(self, msg_fragment=None): - properties_paths = self.settings_resolver.local_public_properties_paths() + def commit_metadata_updates(self, msg_fragment=None, omit_usersettings=True): + properties_paths = self.settings_resolver.local_public_properties_paths(omit_usersettings=omit_usersettings) total_status = '' for properties_path in properties_paths: status = self.git_wrapper.git_status(properties_path).output.strip()