diff --git a/src/caoscrawler/identifiable_adapters.py b/src/caoscrawler/identifiable_adapters.py index 59adbf3b8d490b0c2eb676f84147d89d3836e8fe..98281c4d8a8b25bff52dfef527712a015764b008 100644 --- a/src/caoscrawler/identifiable_adapters.py +++ b/src/caoscrawler/identifiable_adapters.py @@ -650,15 +650,17 @@ class CaosDBIdentifiableAdapter(IdentifiableAdapter): reg = self._get_registered_for_rt(prt) if reg is not None: registered.append(reg) + # TODO we might in future want to check whether the registered identifiables are the same if len(registered) > 1: - raise RuntimeError("Multiple registered identifiables found.") + raise RuntimeError("Multiple registered identifiables found for a Record " + f"with the following parents: {record.parents}") elif len(registered) == 1: return registered[0] else: return None - def _get_registered_for_rt(self, rt): + def _get_registered_for_rt(self, rt: db.RecordType): """ returns the registered identifiable for the given RecordType or the registered identifiable of the first parent @@ -671,8 +673,10 @@ class CaosDBIdentifiableAdapter(IdentifiableAdapter): for parent in rt.parents: prt = _retrieve_RecordType(id=parent.id, name=parent.name) registered.append(self._get_registered_for_rt(prt)) + # TODO we might in future want to check whether the registered identifiables are the same if len(registered) > 1: - raise RuntimeError("Multiple registered identifiables found.") + raise RuntimeError("Multiple registered identifiables found for the RecordType " + f" {rt.name} with the following parents: {rt.parents}") elif len(registered) == 1: return registered[0] else: diff --git a/src/doc/concepts.rst b/src/doc/concepts.rst index f6a7c547a5a424b6d8c03c58d881f9c7fce7382c..15b7812f0b47c83fe07355b90b6d0748783a9391 100644 --- a/src/doc/concepts.rst +++ b/src/doc/concepts.rst @@ -94,20 +94,26 @@ reference the object to be identified. You can also use the wildcard "*" as RecordType name in the configuration which will only require, that ANY Record references the Record at hand. -If a Record has multiple parents, only one of them must have an registered identifiable. + +Instead of defining registered identifiables for a RecordType directly, they can be +defined for their parents. I.e. if there is no registered identifiable for a RecordType, +then it will be checked whether there is a parent that has one. +If multiple recordtypes exist in the inheritance chain with a registered identifiable, then +the one that is closest to the direct parent is used. In case of multiple inheritance, only one branch must have registered identifiables. Reasoning: -If there are mutliple registered identifiables that could be used to identify a given record, then only a single -one of them is used, it might be that the existence check returns a different result than if another one would -be used. This would allow for unpredictable and inconsistent behavior(Example: one registered identifiable +If there would be mutliple registered identifiables that could be used to identify a given record and only a single +one of them would used, it might be that the existence check returns a different result than if the other one would +be used. This would allow for unpredictable and inconsistent behavior (Example: one registered identifiable contains the name another one property date. Using the name might imply that the record does not exist and using the date might imply that it does. Thus, for any Record the registered identifiable must be unique). -Anlogous Example: If you tinnk in the context, of relational databases, there can always only be a foreign key +Anlogous Example: If you think in the context, of relational databases, there can always only be a foreign key associated with one table. -When no registered identifiable exist for the direct parents, registered identifiables may be used -from their parents. If multiple recordtypes exist in the inheritance chain with a registered identifiable, then -the one that is closest to the direct parent is used. In case of multiple inheritance, only one branch must have registered identifiables. +Note: +In case of using the registered identifiable of a parent, the identifiable will be created by using the parent RecordType. Example: The +registered identifiable is defined for the parent "Experiment" and the RecordType at hand "LaseExperiment" is a child of "Experiment". +Then the identifiable will construct a query that searches for "Experiment" Records (and not "LaseExperiment" Records). Identified Records diff --git a/unittests/test_identifiable_adapters.py b/unittests/test_identifiable_adapters.py index 57a1c8561649ef259537ec3c86c1e01403413f93..8fbbf07e467a46a48cb3a1e6d648c7ec74c7d2b3 100644 --- a/unittests/test_identifiable_adapters.py +++ b/unittests/test_identifiable_adapters.py @@ -54,6 +54,7 @@ def mock_retrieve_RecordType(id, name): "Experiment": db.RecordType(name="Experiment"), "Lab": db.RecordType(name="Lab"), "Analysis": db.RecordType(name="Analysis"), + "MetaAnalysis": db.RecordType(name="MetaAnalysis").add_parent("Analysis"), "Measurement": db.RecordType(name="Measurement").add_parent("Experiment") }[name] @@ -322,8 +323,8 @@ def test_get_registered_identifiable(): with pytest.raises(RuntimeError): registered = ident.get_registered_identifiable(rec) - # Test the same but with an additional parent that also has a registered identifiable - rec = db.Record().add_parent(name="Measurement").add_parent(name="Experiment") + # Test the same but with an additional parent that has a parent with a registered identifiable + rec = db.Record().add_parent(name="MetaAnalysis").add_parent(name="Experiment") with pytest.raises(RuntimeError): registered = ident.get_registered_identifiable(rec)