diff --git a/src/python/PyBis/README.md b/src/python/PyBis/README.md index 34b4c698bd8844b81caabc829982cc9410d43d23..b4db8bb03b8caf6dba85e80ce2473e86f8659e95 100644 --- a/src/python/PyBis/README.md +++ b/src/python/PyBis/README.md @@ -288,6 +288,49 @@ tag.get_samples() tag.delete() ``` +## Vocabualry and VocabularyTerms + +Vocabulary consists of many VocabularyTerms. They are used to control the Terms in a Property field. So for example, you want to add a property called **Animal** to a Sample and you want to control which terms are used in this Property. For this you need to do a couple of steps: + +1. create a new vocabulary *AnimalVocabulary* +2. add terms to that vocabulary: *Cat, Dog, Mouse* +3. create a new PropertyType (e.g. *Animal*) of DataType *CONTROLLEDVOCABULARY* and assign the *AnimalVocabulary* to it +4. create a new SampleType (e.g. *Pet*) and *assign* the created PropertyType to that Sample type. +5. If you now create a new Sample of type *Pet* you will be able to add a property *Animal* to it which only accepts the terms *Cat, Dog* or *Mouse*. + + +**create new Vocabulary with three VocabularyTerms** + +``` +voc = o.new_vocabulary( + code = 'BBB', + description = 'description of vocabulary aaa', + urlTemplate = 'https://ethz.ch', + terms = [ + { "code": 'term_code1', "label": "term_label1", "description": "term_description1"}, + { "code": 'term_code2', "label": "term_label2", "description": "term_description2"}, + { "code": 'term_code3', "label": "term_label3", "description": "term_description3"} + ] +) +voc.save() +``` + +**create additional VocabularyTerms** + +``` +term = o.new_term( + code='TERM_CODE_XXX', + vocabularyCode='BBB', + label='here comes a label', + description='here is a meandingful description' +) +term.save() +``` + +**fetching Vocabulary and VocabularyTerms** + + + # Requirements and organization diff --git a/src/python/PyBis/pybis/attribute.py b/src/python/PyBis/pybis/attribute.py index 5500dc4a4d0970863fe709bf959c866f718c1be7..8ff071d3453876ec8c02f835d32ecc14c7354224 100644 --- a/src/python/PyBis/pybis/attribute.py +++ b/src/python/PyBis/pybis/attribute.py @@ -59,6 +59,9 @@ class AttrHolder(): if isinstance(self.__dict__['_' + attr], dict): self.__dict__['_' + attr].pop('@id') + elif attr == 'vocabularyCode': + self.__dict__['_'+attr] = data.get('permId', {}).get(attr, None) + elif attr in ["space"]: d = data.get(attr, None) if d is not None: @@ -239,10 +242,10 @@ class AttrHolder(): "@type": "as.dto.common.update.IdListUpdateValue" } - elif attr == 'description': - # alway update description - up_obj['description'] = { - "value": self.__dict__['_description'], + elif attr in 'description label official ordinal'.split(): + # alway update common fields + up_obj[attr] = { + "value": self.__dict__['_'+attr], "isModified": True, "@type": "as.dto.common.update.FieldUpdateValue" } @@ -269,7 +272,7 @@ class AttrHolder(): value = self.__dict__.get('_' + attr, {}) if value is None: pass - elif len(value) == 0: + elif isinstance(value, dict) and len(value) == 0: # value is {}: it means that we want this attribute to be # deleted, not updated. up_obj[attr2ids[attr]] = { diff --git a/src/python/PyBis/pybis/definitions.py b/src/python/PyBis/pybis/definitions.py index 583838d41d7f948bc19e89310453947356b60c0b..81bd2c1e426879de0044b60319ddf9d130f0a354 100644 --- a/src/python/PyBis/pybis/definitions.py +++ b/src/python/PyBis/pybis/definitions.py @@ -1,4 +1,12 @@ def openbis_definitions(entity): + """ + attrs_new: Attributes, that can appear when creating new entities + attrs_up: Attributes that can be updated + attrs: Attributes that are displayed when fetched + multi: multivalue-elements which appear in an entity. E.g. parents or children in a Sample. + identifier: to update entities, the identifier must be specified. Usually identityName + "Id" + (Entity-Name in camel-case, starting with lowercase letter, with «Id» added) + """ entities = { "Space": { "attrs_new": "code description".split(), @@ -6,6 +14,10 @@ def openbis_definitions(entity): "attrs": "code permId description registrator registrationDate modificationDate".split(), "multi": "".split(), "identifier": "spaceId", + "create": { "@type": "as.dto.space.create.SpaceCreation"}, + "update": { "@type": "as.dto.space.upate.SpaceUpdate"}, + "delete": { "@type": "as.dto.space.delete.SpaceDeletionOptions"}, + "fetch": { "@type": "as.dto.space.fetchoptions.SpaceFetchOptions"}, }, "Project": { "attrs_new": "code description space attachments".split(), @@ -78,6 +90,28 @@ def openbis_definitions(entity): "multi": "".split(), "identifier": "tagId", }, + "Vocabulary": { + "attrs_new": "code description managedInternally internalNameSpace chosenFromList urlTemplate".split(), + "attrs_up": "description managedInternally internalNameSpace chosenFromList urlTemplate".split(), + "attrs": "code description managedInternally internalNameSpace chosenFromList urlTemplate registrator registrationDate modificationDate".split(), + "multi": "".split(), + "identifier": "vocabularyId", + "create": { "@type": "as.dto.vocabulary.create.VocabularyCreation"}, + "update": { "@type": "as.dto.vocabulary.upate.VocabularyUpdate"}, + "delete": { "@type": "as.dto.vocabulary.delete.VocabularyDeletionOptions"}, + "fetch": { "@type": "as.dto.vocabulary.fetchoptions.VocabularyFetchOptions"}, + }, + "VocabularyTerm": { + "attrs_new": "code vocabularyCode label description official ordinal".split(), + "attrs_up": "label description official ordinal".split(), + "attrs": "code vocabularyCode label description official ordinal registrationDate modificationDate registrator".split(), + "multi": "".split(), + "identifier": "vocabularyTermId", + "create": { "@type": "as.dto.vocabulary.create.VocabularyTermCreation"}, + "update": { "@type": "as.dto.vocabulary.upate.VocabularyTermUpdate"}, + "delete": { "@type": "as.dto.vocabulary.delete.VocabularyTermDeletionOptions"}, + "fetch": { "@type": "as.dto.vocabulary.fetchoptions.VocabularyTermFetchOptions"}, + }, "Plugin": { "attrs_new": "name description available script available script pluginType pluginKind entityKinds".split(), "attrs_up": "description, available script available script pluginType pluginKind entityKinds".split(), @@ -133,6 +167,8 @@ def openbis_definitions(entity): } return entities[entity] +get_definition_for_entity = openbis_definitions # Alias + fetch_option = { "space": {"@type": "as.dto.space.fetchoptions.SpaceFetchOptions"}, @@ -195,4 +231,31 @@ fetch_option = { "history": {"@type": "as.dto.history.fetchoptions.HistoryEntryFetchOptions"}, "dataStore": {"@type": "as.dto.datastore.fetchoptions.DataStoreFetchOptions"}, "plugin": {"@type": "as.dto.plugin.fetchoptions.PluginFetchOptions"}, + "vocabulary": { + "@type": "as.dto.vocabulary.fetchoptions.VocabularyFetchOptions", + "terms": { + "@type": "as.dto.vocabulary.fetchoptions.VocabularyTermFetchOptions" + }, + }, + "vocabularyTerm": {"@type": "as.dto.vocabulary.fetchoptions.VocabularyTermFetchOptions"}, } + +def get_fetchoption_for_entity(entity): + entity = entity[0].lower() + entity[1:] # make first character lowercase + try: + return fetch_option[entity] + except KeyError as e: + return {} + +def get_type_for_entity(entity, action): + if action not in "create update delete fetch".split(): + raise ValueError('unknown action: {}'.format(action)) + + definition = openbis_definitions(entity) + return definition[action] + +def get_method_for_entity(entity, action): + if action == "Vocabulary": + return "{}Vocabularies".format(action) + + return "{}{}s".format(action, entity) diff --git a/src/python/PyBis/pybis/pybis.py b/src/python/PyBis/pybis/pybis.py index 3657e3b8e50ed8e2bf3ec0409f578b45afc889df..979071b985dd6e18135ce5eef7705518d7c0c7e9 100644 --- a/src/python/PyBis/pybis/pybis.py +++ b/src/python/PyBis/pybis/pybis.py @@ -28,11 +28,11 @@ from tabulate import tabulate from . import data_set as pbds from .utils import parse_jackson, check_datatype, split_identifier, format_timestamp, is_identifier, is_permid, nvl, VERBOSE -from .utils import extract_permid, extract_code,extract_deletion,extract_identifier,extract_nested_identifier,extract_nested_permid,extract_property_assignments,extract_role_assignments,extract_person, extract_person_details,extract_id,extract_userId +from .utils import extract_attr, extract_permid, extract_code,extract_deletion,extract_identifier,extract_nested_identifier,extract_nested_permid,extract_property_assignments,extract_role_assignments,extract_person, extract_person_details,extract_id,extract_userId from .property import PropertyHolder, PropertyAssignments from .vocabulary import Vocabulary, VocabularyTerm from .openbis_object import OpenBisObject -from .definitions import fetch_option +from .definitions import get_definition_for_entity, fetch_option, get_fetchoption_for_entity, get_type_for_entity, get_method_for_entity # import the various openBIS entities from .things import Things @@ -555,6 +555,9 @@ class Openbis: "get_tag(tagId)", "new_tag(code, description)", "get_terms()", + "get_term()", + "get_vocabularies()", + "get_vocabulary()", "new_person(userId, space)", "get_persons()", "get_person(userId)", @@ -1623,7 +1626,24 @@ class Openbis: } ] } - self._post_request(self.as_v3, request) + resp = self._post_request(self.as_v3, request) + + + def delete_openbis_entity(self, entity, objectId, reason='No reason given'): + method = get_method_for_entity(entity, 'delete') + delete_options = get_type_for_entity(entity, 'delete') + delete_options['reason'] = reason + + request = { + "method": method, + "params": [ + self.token, + [ objectId ], + delete_options + ] + } + resp = self._post_request(self.as_v3, request) + return def get_deletions(self): @@ -1774,7 +1794,8 @@ class Openbis: return request def get_terms(self, vocabulary=None): - """ Returns information about vocabulary, including its controlled vocabulary + """ Returns information about existing vocabulary terms. + If a vocabulary code is provided, it only returns the terms of that vocabulary. """ search_request = {} @@ -1787,30 +1808,139 @@ class Openbis: }] }) - fetch_options = { - "vocabulary": {"@type": "as.dto.vocabulary.fetchoptions.VocabularyFetchOptions"}, - "@type": "as.dto.vocabulary.fetchoptions.VocabularyTermFetchOptions" - } + fetchopts = fetch_option['vocabularyTerm'] request = { "method": "searchVocabularyTerms", - "params": [self.token, search_request, fetch_options] + "params": [self.token, search_request, fetchopts] } resp = self._post_request(self.as_v3, request) - attrs = 'code label description vocabulary registrationDate modificationDate official ordinal'.split() + attrs = 'code vocabularyCode label description registrationDate modificationDate official ordinal'.split() if len(resp['objects']) == 0: terms = DataFrame(columns=attrs) else: objects = resp['objects'] - parse_jackson(resp) + parse_jackson(objects) terms = DataFrame(objects) + terms['vocabularyCode'] = terms['permId'].map(extract_attr('vocabularyCode')) terms['registrationDate'] = terms['registrationDate'].map(format_timestamp) terms['modificationDate'] = terms['modificationDate'].map(format_timestamp) - return Things(self, 'term', terms[attrs], 'permId') - return VocabularyTerm(terms) + return Things(self, 'term', terms[attrs], + identifier_name='code', additional_identifier='vocabularyCode') + + + def new_term(self, code, vocabularyCode, label=None, description=None): + return VocabularyTerm( + self, data=None, + code=code, vocabularyCode=vocabularyCode, + label=label, description=description + ) + + + def get_term(self, code, vocabularyCode=None, only_data=False): + entity_def = get_definition_for_entity('VocabularyTerm') + search_request = { + "code": code, + "vocabularyCode": vocabularyCode, + "@type": "as.dto.vocabulary.id.VocabularyTermPermId" + } + fetchopts = get_fetchoption_for_entity('VocabularyTerm') + for opt in ['registrator']: + fetchopts[opt] = get_fetchoption_for_entity(opt) + + request = { + "method": 'getVocabularyTerms', + "params": [ + self.token, + [search_request], + fetchopts + ], + } + resp = self._post_request(self.as_v3, request) + + if resp is None or len(resp) == 0: + raise ValueError("no VocabularyTerm found with code='{}' and vocabularyCode='{}'".format(code, vocabularyCode)) + else: + parse_jackson(resp) + for ident in resp: + if only_data: + return resp[ident] + else: + return VocabularyTerm(self, resp[ident]) + + + def get_any_entity(self, identifier, entity, only_data=False, method=None): + + entity_def = get_definition_for_entity(entity) + + # guess the method to fetch an entity + if method is None: + method = 'get' + entity + 's' + + search_request = search_request_for_identifier(identifier, entity) + fetchopts = get_fetchoption_for_entity(entity) + + request = { + "method": method, + "params": [ + self.token, + [search_request], + fetchopts + ], + } + resp = self._post_request(self.as_v3, request) + + if resp is None or len(resp) == 0: + raise ValueError('no {} found with identifier: {}'.format(entity, identifier)) + else: + parse_jackson(resp) + for ident in resp: + if only_data: + return resp[ident] + else: + # get a dynamic instance of that class + klass = globals()[entity] + return klass(self, resp[ident]) + + + def get_vocabularies(self): + """ Returns information about vocabulary + """ + + search_request = {} + + fetchopts = fetch_option['vocabulary'] + for option in ['registrator']: + fetchopts[option] = fetch_option[option] + + request = { + "method": "searchVocabularies", + "params": [self.token, search_request, fetchopts] + } + resp = self._post_request(self.as_v3, request) + + attrs = 'code description managedInternally internalNameSpace chosenFromList urlTemplate registrator registrationDate modificationDate'.split() + + if len(resp['objects']) == 0: + vocs = DataFrame(columns=attrs) + else: + objects = resp['objects'] + parse_jackson(resp) + vocs = DataFrame(objects) + vocs['registrationDate'] = vocs['registrationDate'].map(format_timestamp) + vocs['modificationDate'] = vocs['modificationDate'].map(format_timestamp) + vocs['registrator'] = vocs['registrator'].map(extract_person) + + return Things(self, 'vocabulary', vocs[attrs], 'code') + + + def get_vocabulary(self, code, only_data=False): + """ Returns the details of a given vocabulary (including vocabulary terms) + """ + return self.get_any_entity(code, 'Vocabulary', only_data=only_data, method='getVocabularies') def new_tag(self, code, description=None): @@ -2275,7 +2405,7 @@ class Openbis: for key in ['parents','children','container','components']: fetchopts[key] = {"@type": "as.dto.sample.fetchoptions.SampleFetchOptions"} - sample_request = { + request = { "method": "getSamples", "params": [ self.token, @@ -2284,7 +2414,7 @@ class Openbis: ], } - resp = self._post_request(self.as_v3, sample_request) + resp = self._post_request(self.as_v3, request) parse_jackson(resp) if resp is None or len(resp) == 0: @@ -2449,6 +2579,26 @@ class Openbis: entityType=entityType, propertyType=propertyType, **kwargs ) + def new_vocabulary(self, code, terms, managedInternally=False, internalNameSpace=False, chosenFromList=True, **kwargs): + """ Creates a new vocabulary + Usage:: + new_vocabulary( + code = 'vocabulary_code', + description = '', + terms = [ + { "code": "term1", "label": "label1", "description": "description1" }, + { "code": "term2", "label": "label2", "description": "description2" }, + ] + ) + """ + kwargs['code'] = code + kwargs['managedInternally'] = managedInternally + kwargs['internalNameSpace'] = internalNameSpace + kwargs['chosenFromList'] = chosenFromList + return Vocabulary(self, data=None, terms=terms, **kwargs) + + + def _get_dss_url(self, dss_code=None): """ internal method to get the downloadURL of a datastore. """ diff --git a/src/python/PyBis/pybis/space.py b/src/python/PyBis/pybis/space.py index 742f633326fdbb0b1d1062e19a7c5042b4ab764f..1be04c0d6d7a805bb7d9a5ddd648f1f139bfaa5c 100644 --- a/src/python/PyBis/pybis/space.py +++ b/src/python/PyBis/pybis/space.py @@ -57,7 +57,7 @@ class Space(OpenBisObject): return self.openbis.new_sample(space=self, **kwargs) def delete(self, reason): - self.openbis.delete_entity('Space', self.permId, reason) + self.openbis.delete_entity(entity='Space', id=self.permId, reason=reason) if VERBOSE: print("Space {} has been sucsessfully deleted.".format(self.permId)) def save(self): diff --git a/src/python/PyBis/pybis/things.py b/src/python/PyBis/pybis/things.py index eceb421dc27340fedc5054decf0a80d75b298ec5..131ea6baccb075b5406fa4f9cd7fab09f244b0c5 100644 --- a/src/python/PyBis/pybis/things.py +++ b/src/python/PyBis/pybis/things.py @@ -5,11 +5,13 @@ class Things(): """ - def __init__(self, openbis_obj, entity, df, identifier_name='code'): + def __init__(self, openbis_obj, entity, df, + identifier_name='code', additional_identifier=None): self.openbis = openbis_obj self.entity = entity self.df = df self.identifier_name = identifier_name + self.additional_identifier = additional_identifier def __repr__(self): return tabulate(self.df, headers=list(self.df)) @@ -101,6 +103,11 @@ class Things(): return Things(self.openbis, 'dataset', DataFrame(), 'permId') def __getitem__(self, key): + """ elegant way to fetch a certain element from the displayed list. + If an integer value is given, we choose the row. + If the key is a list, we return the desired columns (normal dataframe behaviour) + If the key is a non-integer value, we treat it as a primary-key lookup + """ if self.df is not None and len(self.df) > 0: row = None if isinstance(key, int): @@ -115,7 +122,14 @@ class Things(): if row is not None: # invoke the openbis.get_<entity>() method - return getattr(self.openbis, 'get_' + self.entity)(row[self.identifier_name].values[0]) + if self.additional_identifier is None: + return getattr(self.openbis, 'get_' + self.entity)(row[self.identifier_name].values[0]) + ## get an entry using two keys + else: + return getattr(self.openbis, 'get_' + self.entity)( + row[self.identifier_name].values[0], + row[self.additional_identifier].values[0] + ) def __iter__(self): for item in self.df[[self.identifier_name]][self.identifier_name].iteritems(): diff --git a/src/python/PyBis/pybis/utils.py b/src/python/PyBis/pybis/utils.py index 6f78cf181e2e3543d802d61dbad4ba236365bbf5..bb9a32240bdfa160453f6dba0edf72e261bd1223 100644 --- a/src/python/PyBis/pybis/utils.py +++ b/src/python/PyBis/pybis/utils.py @@ -153,12 +153,12 @@ def extract_deletion(obj): def extract_attr(attr): - def attr(obj): + def attr_func(obj): if isinstance(obj, dict): return obj.get(attr, '') else: return str(obj) - return attr + return attr_func def extract_identifier(ident): diff --git a/src/python/PyBis/pybis/vocabulary.py b/src/python/PyBis/pybis/vocabulary.py index cefb8368729af2d846fa1993c2597a577a930769..d97c4aa2f675bc44dbef6e5cccc69028b9b8347c 100644 --- a/src/python/PyBis/pybis/vocabulary.py +++ b/src/python/PyBis/pybis/vocabulary.py @@ -1,63 +1,187 @@ from .utils import VERBOSE from .openbis_object import OpenBisObject +from .attribute import AttrHolder +from .definitions import openbis_definitions, fetch_option +import json -class Vocabulary(): +class Vocabulary(OpenBisObject): - def __init__(self, data): - self.data = data + def __init__(self, openbis_obj, data=None, terms=None, **kwargs): + self.__dict__['openbis'] = openbis_obj + self.__dict__['a'] = AttrHolder(openbis_obj, 'Vocabulary') + + if data is not None: + self._set_data(data) + self.__dict__['terms'] = data['terms'] + + if terms is not None: + self.__dict__['terms'] = terms + + if self.is_new: + allowed_attrs = openbis_definitions(self.entity)['attrs_new'] + for key in kwargs: + if key not in allowed_attrs: + raise ValueError( + "{} is an unknown Vocabulary attribute. Allowed attributes are: {}".format( + key, ", ".join(allowed_attrs) + ) + ) + + if kwargs is not None: + for key in kwargs: + setattr(self, key, kwargs[key]) + + + def get_terms(self): + """ Returns the VocabularyTerms of the given Vocabulary. + """ + return self.openbis.get_terms(vocabulary=self.code) + + def add_term(self, code, label=None, description=None): + """ Adds a term to this Vocabulary. + If Vocabulary is already persistent, it is added by adding a new VocabularyTerm object. + If Vocabulary is new, the term is added to the list of terms + """ + if self.is_new: + self.__dict__['terms'].append({ + "code": code, + "label": label, + "description": description + }) + else: + pass + + + def save(self): + if self.is_new: + request = self._new_attrs('createVocabularies') + # add the VocabularyTerm datatype + terms = self.__dict__['terms'] + for term in terms: + term["@type"]= "as.dto.vocabulary.create.VocabularyTermCreation" + request['params'][1][0]['terms'] = terms + resp = self.openbis._post_request(self.openbis.as_v3, request) + + if VERBOSE: print("Vocabulary successfully created.") + data = self.openbis.get_vocabulary(resp[0]['permId'], only_data=True) + self._set_data(data) + return self + + else: + request = self._up_attrs('updateVocabularies') + self.openbis._post_request(self.openbis.as_v3, request) + if VERBOSE: print("Vocabulary successfully updated.") + data = self.openbis.get_vocabulary(self.permId, only_data=True) + self._set_data(data) + + +class VocabularyTerm(OpenBisObject): + + def __init__(self, openbis_obj, data=None, **kwargs): + self.__dict__['openbis'] = openbis_obj + self.__dict__['a'] = AttrHolder(openbis_obj, 'VocabularyTerm') + + + if data is not None: + self._set_data(data) + + if kwargs is not None: + for key in kwargs: + setattr(self, key, kwargs[key]) - @property - def terms_kv(self): - return [ - {voc["code"]:voc["label"] } - for voc - in sorted(self.data['objects'], key=lambda v: v["ordinal"]) - ] @property - def terms(self): - return [ - voc["code"] - for voc - in sorted(self.data['objects'], key=lambda v: v["ordinal"]) - ] - - - def _repr_html_(self): - html = """ - <table border="1" class="dataframe"> - <thead> - <tr style="text-align: right;"> - <th>vocabulary term</th> - <th>label</th> - <th>description</th> - <th>vocabulary</th> - </tr> - </thead> - <tbody> + def vocabularyCode(self): + if self.is_new: + return self.__dict__['a'].vocabularyCode + else: + return self.data['permId']['vocabularyCode'] + + + def _up_attrs(self): + """ AttributeTerms behave quite differently to all other openBIS entities, + that's why we need to override this method """ + attrs = {} + for attr in 'label description'.split(): + attrs[attr] = { + "value": getattr(self, attr), + "isModified": True, + "@type": "as.dto.common.update.FieldUpdateValue" + } - for voc in sorted( - self.data['objects'], - key=lambda v: (v["permId"]["vocabularyCode"], v["ordinal"]) - ): + attrs["vocabularyTermId"] = self.vocabularyTermId() + attrs["@type"] = "as.dto.vocabulary.update.VocabularyTermUpdate" + request = { + "method": "updateVocabularyTerms", + "params": [ + self.openbis.token, + [attrs] + ] + } + return request - html += "<tr> <td>{}</td> <td>{}</td> <td>{}</td> <td>{}</td> </tr>".format( - voc['code'], - voc['label'], - voc['description'], - voc['permId']['vocabularyCode'] - ) - html += """ - </tbody> - </table> + def _new_attrs(self): + attrs = { + "@type": "as.dto.vocabulary.create.VocabularyTermCreation", + "vocabularyId": self.vocabularyTermId() + } + for attr in 'code label description'.split(): + attrs[attr] = getattr(self, attr) + + request = { + "method": "createVocabularyTerms", + "params": [ + self.openbis.token, + [attrs] + ] + } + return request + + + def vocabularyTermId(self): + """ needed for updating a term. """ - return html + if self.is_new: + return { + "permId": getattr(self, 'vocabularyCode'), + "@type": "as.dto.vocabulary.id.VocabularyPermId" + } + else: + permId = self.data['permId'] + permId.pop('@id', None) + return permId -class VocabularyTerm(OpenBisObject): + def save(self): + if self.is_new: + request = self._new_attrs() + resp = self.openbis._post_request(self.openbis.as_v3, request) + + if VERBOSE: print("Vocabulary Term successfully created.") + data = self.openbis.get_term( + code=resp[0]['code'], + vocabularyCode=resp[0]['vocabularyCode'], + only_data=True + ) + self._set_data(data) + return self + + else: + request = self._up_attrs() + self.openbis._post_request(self.openbis.as_v3, request) + if VERBOSE: print("Vocabulary Term successfully updated.") + data = self.openbis.get_term( + code=self.code, + vocabularyCode=self.vocabularyCode, + only_data=True + ) + self._set_data(data) - def __init__(self, data): - self.data = data + def delete(self, reason='no particular reason'): + self.openbis.delete_openbis_entity( + entity='VocabularyTerm', objectId=self.data['permId'], reason=reason + ) + if VERBOSE: print("VocabularyTerm successfully deleted.") diff --git a/src/python/PyBis/tests/test_vocabulary.py b/src/python/PyBis/tests/test_vocabulary.py new file mode 100644 index 0000000000000000000000000000000000000000..6ab6145bbb56cc1ffe18d71d12ddf18bcb5db072 --- /dev/null +++ b/src/python/PyBis/tests/test_vocabulary.py @@ -0,0 +1,18 @@ +import json +import random +import re + +import pytest +import time + + +def test_create_delete_vocabulay_terms(openbis_instance): + o=openbis_instance + + terms = o.get_terms() + assert terms is not None + assert terms.df is not None + + + + diff --git a/src/vagrant/jupyter-bis/README.md b/src/vagrant/jupyter-bis/README.md index 8294a53f8ce3c44bff687b9f873ee7b435af2aed..4be7e4122be0934c35d6c2bbf957cad8d9656ff3 100644 --- a/src/vagrant/jupyter-bis/README.md +++ b/src/vagrant/jupyter-bis/README.md @@ -83,6 +83,7 @@ This process describes how to upgrade your openBIS instance in your virtual mach 10. do the openBIS upgrade: `bin/upgrade.sh` 11. switch back to vagrant user: `logout` or CTRL-D 11. start openBIS: `sync/initialize/start_services.sh` +12. optionally delete previous installations (to save diskspace): `rm -rf backup/*` ## start openBIS and JupyterHub