diff --git a/CHANGELOG.md b/CHANGELOG.md index 4090ab0aa73c5c32caab38cf8008c11a9d33a84d..f73831ddc3c0c98120d2d964972e0c1fa35173fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,23 +2,43 @@ All notable changes to this project will be documented in this file. -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - ## [Unreleased] ## ### Added ### +* Empty string support (See caosdb-server#33) + ### Changed ### ### Deprecated ### +### Removed ### + ### Fixed ### - Replaced deprecated Logger.warn() method. -## [0.2.4] +### Security ### + +## [0.3.0] - 2020-04-24## + +### Added ### + +* `apiutils.apply_to_ids` -- a helper which applies a function to all ids which + are used by an entity (own entity, parents, properties, references etc.). + +### Changed ### + +### Deprecated ### + +### Fixed ### + +* import bugs in apiutils + +## [0.2.4] -- 2020-04-23 ### Added diff --git a/RELEASE_GUIDELINES.md b/RELEASE_GUIDELINES.md index 48eb270e6f5dc29b4d9ee85d275d6fe7eba1ffd6..02be5c1ad19f6a3a405fb08d62e23dab350ad445 100644 --- a/RELEASE_GUIDELINES.md +++ b/RELEASE_GUIDELINES.md @@ -1,4 +1,4 @@ -# Release Guidelines for the CaosDB MySQL Backend +# Release Guidelines for the CaosDB Python Client Library This document specifies release guidelines in addition to the generel release guidelines of the CaosDB Project @@ -28,7 +28,9 @@ guidelines of the CaosDB Project 6. Delete the release branch. -7. Publish the release by executing `./release.sh` with uploads the caosdb +7. Remove possibly existing `./dist` directory with old release. + +8. Publish the release by executing `./release.sh` with uploads the caosdb module to the Python Package Index [pypi.org](https://pypi.org). -8. Merge the master branch back into the dev branch. +9. Merge the master branch back into the dev branch. diff --git a/setup.py b/setup.py index 042892ead90e2d68488aa2535c26ceec87b5b185..9e090d6eea97ff209dea886965a6303b865a2031 100755 --- a/setup.py +++ b/setup.py @@ -46,10 +46,11 @@ from setuptools import find_packages, setup ######################################################################## MAJOR = 0 -MINOR = 2 -MICRO = 5 -PRE = "dev" +MINOR = 3 +MICRO = 1 +PRE = "" # e.g. rc0, alpha.1, 0.beta-23 ISRELEASED = False + if PRE: VERSION = "{}.{}.{}-{}".format(MAJOR, MINOR, MICRO, PRE) else: diff --git a/src/caosdb/apiutils.py b/src/caosdb/apiutils.py index f3887099a93d50740a3b2825bc6e8a9677509079..bd279fcfe5c394b9b5cff787169cff5b9f2d3031 100644 --- a/src/caosdb/apiutils.py +++ b/src/caosdb/apiutils.py @@ -5,6 +5,8 @@ # # Copyright (C) 2018 Research Group Biomedical Physics, # Max-Planck-Institute for Dynamics and Self-Organization Göttingen +# Copyright (C) 2020 Timm Fitschen <t.fitschen@indiscale.com> +# Copyright (C) 2020 IndiScale GmbH <info@indiscale.com> # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -34,7 +36,8 @@ from subprocess import call from caosdb.common.datatype import (BOOLEAN, DATETIME, DOUBLE, FILE, INTEGER, REFERENCE, TEXT) from caosdb.common.models import (Container, Entity, File, Property, Query, - Record, RecordType, get_config) + Record, RecordType, get_config, + execute_query, is_reference) def new_record(record_type, name=None, description=None, @@ -85,11 +88,11 @@ def new_record(record_type, name=None, description=None, def id_query(ids): q = "FIND Entity with " + " OR ".join(["id={}".format(id) for id in ids]) - return db.execute_query(q) + return execute_query(q) def retrieve_entities_with_ids(entities): - collection = db.Container() + collection = Container() step = 20 for i in range(len(entities)//step+1): @@ -669,3 +672,36 @@ def describe_diff(olddiff, newdiff, name=None, as_update=True): "version of {}\n\n".format(name))+description return description + + +def apply_to_ids(entities, func): + """ Apply a function to all ids. + + All ids means the ids of the entities themselves but also to all parents, + properties and referenced entities. + + Parameters + ---------- + entities : list of Entity + func : function with one parameter. + """ + for entity in entities: + _apply_to_ids_of_entity(entity, func) + + +def _apply_to_ids_of_entity(entity, func): + entity.id = func(entity.id) + + for par in entity.parents: + par.id = func(par.id) + + for prop in entity.properties: + prop.id = func(prop.id) + isref = is_reference(prop.datatype) + + if isref: + if isinstance(prop.value, list): + prop.value = [func(el) for el in prop.value] + else: + if prop.value is not None: + prop.value = func(prop.value) diff --git a/src/caosdb/common/datatype.py b/src/caosdb/common/datatype.py index 8578574af6cc874550ca3d4a53ab90cd3946e87a..246485c3957462fc98ac83f9d904413ad7518302 100644 --- a/src/caosdb/common/datatype.py +++ b/src/caosdb/common/datatype.py @@ -110,7 +110,7 @@ def get_id_of_datatype(datatype): raise AmbiguityException( "Name {} did not lead to unique result; Missing " "implementation".format(datatype)) - elif len(res) == 1: + elif len(res) != 1: raise EntityDoesNotExistError( "No RecordType named {}".format(datatype)) diff --git a/src/caosdb/common/models.py b/src/caosdb/common/models.py index 84cdb96b4c0ae359dbeb249b9ae0744a06aa87e8..77638589d3ed4cd9dcb63c1935bcf1c2a34283ce 100644 --- a/src/caosdb/common/models.py +++ b/src/caosdb/common/models.py @@ -38,8 +38,8 @@ from sys import hexversion from tempfile import NamedTemporaryFile from warnings import warn -from lxml import etree - +from caosdb.common.datatype import (BOOLEAN, DATETIME, DOUBLE, FILE, INTEGER, + LIST, REFERENCE, TEXT, is_reference) from caosdb.common.utils import uuid, xml2str from caosdb.configuration import get_config from caosdb.connection.connection import get_connection @@ -51,8 +51,9 @@ from caosdb.exceptions import (AmbiguityException, AuthorizationException, EntityHasNoDatatypeError, TransactionError, UniqueNamesError, UnqualifiedParentsError, UnqualifiedPropertiesError, URITooLongException) -from caosdb.common.datatype import (BOOLEAN, DATETIME, DOUBLE, FILE, INTEGER, - LIST, REFERENCE, TEXT, is_reference) +from lxml import etree + +from .datatype import is_reference _ENTITY_URI_SEGMENT = "Entity" @@ -653,7 +654,7 @@ class Entity(object): if self.description is not None: xml.set("description", str(self.description)) - if self.value is not None and self.value != "": + if self.value is not None: if isinstance(self.value, Entity): if self.value.id is not None: xml.text = str(self.value.id) @@ -672,11 +673,15 @@ class Entity(object): v_elem.text = str(v.name) else: v_elem.text = str(v) + elif v == "": + v_elem.append(etree.Element("EmptyString")) elif v is None: pass else: v_elem.text = str(v) xml.append(v_elem) + elif self.value == "": + xml.append(etree.Element("EmptyString")) else: xml.text = str(self.value) @@ -944,33 +949,31 @@ class Entity(object): def _parse_col_values(cdt, vals): matcher = re.compile(r"^(?P<col>[^<]+)<(?P<dt>[^>]+)>$") m = matcher.match(cdt) - try: - if m: - col = m.group("col") - dt = m.group("dt") + if m: + col = m.group("col") + dt = m.group("dt") - if col == "LIST": - ret = list() - add = ret.append - else: - return vals + if col == "LIST": + ret = list() + add = ret.append + else: + return vals - for v in vals: - if dt == DOUBLE: - add(float(v)) - elif dt == TEXT or dt == DATETIME: + for v in vals: + if dt == DOUBLE: + add(float(v)) + elif dt == TEXT or dt == DATETIME: + add(v) + else: + try: + add(int(v)) + except (ValueError, TypeError): add(v) - else: - try: - add(int(v)) - except BaseException: - add(v) - return ret - - except BaseException: - traceback.print_exc(file=sys.stderr) + return ret + if len(vals) == 1: + return vals[0] return vals @@ -3566,10 +3569,13 @@ def _parse_single_xml_element(elem): Entity._from_xml(entity, elem) return entity + elif elem.tag.lower() == "emptystring": + return "" elif elem.tag.lower() == "value": - if elem.text is None: + if len(elem) == 1 and elem[0].tag.lower() == "emptystring": + return "" + elif elem.text is None or elem.text.strip() == "": return None - return str(elem.text.strip()) elif elem.tag.lower() == "querytemplate": return QueryTemplate._from_xml(elem) diff --git a/unittests/record.py b/unittests/record.py deleted file mode 100644 index ac20f1ce08b249035c1ebd0f64d41bdb8f7ac346..0000000000000000000000000000000000000000 --- a/unittests/record.py +++ /dev/null @@ -1,34 +0,0 @@ -# -*- encoding: utf-8 -*- -# -# ** header v3.0 -# This file is a part of the CaosDB Project. -# -# Copyright (C) 2018 Research Group Biomedical Physics, -# Max-Planck-Institute for Dynamics and Self-Organization Göttingen -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see <https://www.gnu.org/licenses/>. -# -# ** end header -# -# Test record for test_apiutils -# A. Schlemmer, 02/2018 - -import caosdb as db -from lxml import etree - - -# This is Record ID 171179 from the current CaosDB -testrecord = db.Record._from_xml(db.Record(), etree.fromstring(""" -<Record id="171179"><TransactionBenchmark>\n </TransactionBenchmark><Parent id="163454" name="LeapExperiment" description="Experiment for the LEAP project."/><Property id="163414" name="experimentId" description="A unique identifier for experiments" datatype="TEXT" importance="FIX" flag="inheritance:FIX">KCdSYWJiaXQnLCAnZXhfdml2bycsICdzeW5jaHJvbml6YXRpb25fbWFwJywgTm9uZSwgJzIwMTctMDgtMTEnLCBOb25lLCBOb25lKQ==</Property><Property id="163442" name="date" description="A calendar date. When something took place." datatype="DATETIME" importance="FIX" flag="inheritance:FIX">2017-08-11</Property><Property id="163399" name="vivoness" description="How the experiment was done: IN_VIVO or EX_VIVO." datatype="TEXT" importance="FIX" flag="inheritance:FIX">ex_vivo</Property><Property id="163418" name="species" description="The species of the experimental animal." datatype="TEXT" importance="FIX" flag="inheritance:FIX">Rabbit</Property><Property id="163449" name="ExperimentSeries" description="A collection of [Experiment] records which have the same [aim]." datatype="ExperimentSeries" importance="FIX" flag="inheritance:FIX">163386</Property><Property id="163397" name="MultiRecRecording" datatype="LIST<MultiRecRecording>" importance="FIX" flag="inheritance:FIX"><Value>171242</Value><Value>170651</Value><Value>171701</Value><Value>172074</Value><Value>171408</Value><Value>171687</Value><Value>170791</Value><Value>171115</Value><Value>171893</Value><Value>171237</Value><Value>171309</Value><Value>170899</Value><Value>171048</Value><Value>171077</Value><Value>171723</Value><Value>171085</Value><Value>171812</Value><Value>171829</Value><Value>171669</Value><Value>170695</Value><Value>170875</Value><Value>171751</Value><Value>171412</Value><Value>172204</Value><Value>170997</Value><Value>171490</Value><Value>171504</Value><Value>170977</Value><Value>171895</Value><Value>172233</Value><Value>171261</Value><Value>171318</Value><Value>171014</Value><Value>171132</Value><Value>171575</Value><Value>172217</Value><Value>171638</Value><Value>170656</Value><Value>171927</Value><Value>172232</Value><Value>171864</Value><Value>171564</Value><Value>171090</Value><Value>171633</Value><Value>171456</Value><Value>171755</Value><Value>170641</Value><Value>172144</Value><Value>171872</Value><Value>171619</Value><Value>171180</Value><Value>172019</Value><Value>171193</Value><Value>171069</Value><Value>171643</Value><Value>171789</Value><Value>170785</Value><Value>170904</Value><Value>171130</Value><Value>171098</Value><Value>171241</Value><Value>171796</Value><Value>171776</Value><Value>171175</Value><Value>171112</Value><Value>171514</Value><Value>171854</Value><Value>170962</Value><Value>171818</Value><Value>170745</Value><Value>171149</Value><Value>170639</Value><Value>171508</Value><Value>170800</Value><Value>171141</Value><Value>171414</Value><Value>171325</Value><Value>172168</Value><Value>171184</Value><Value>172083</Value><Value>172029</Value><Value>171556</Value><Value>172195</Value><Value>170789</Value><Value>171300</Value><Value>172230</Value><Value>171720</Value><Value>171341</Value><Value>171937</Value><Value>171673</Value><Value>172106</Value><Value>171281</Value><Value>171897</Value><Value>171108</Value><Value>172014</Value><Value>170867</Value><Value>171531</Value><Value>171674</Value><Value>172077</Value><Value>170944</Value><Value>171228</Value><Value>172207</Value><Value>171039</Value><Value>171778</Value><Value>170794</Value><Value>171646</Value><Value>172027</Value><Value>171279</Value><Value>171618</Value><Value>171233</Value><Value>170811</Value><Value>171357</Value><Value>172180</Value><Value>171585</Value><Value>170774</Value><Value>171625</Value><Value>171310</Value><Value>171855</Value><Value>172084</Value><Value>171395</Value><Value>171955</Value><Value>172216</Value><Value>171705</Value><Value>171312</Value><Value>171659</Value><Value>172192</Value><Value>171157</Value><Value>171978</Value><Value>170707</Value><Value>171250</Value><Value>171929</Value><Value>171709</Value><Value>170898</Value><Value>170723</Value><Value>172060</Value><Value>171693</Value><Value>172036</Value><Value>172162</Value><Value>171460</Value><Value>172065</Value><Value>171174</Value><Value>171461</Value><Value>172093</Value><Value>171200</Value><Value>172007</Value><Value>172102</Value><Value>171368</Value><Value>171761</Value><Value>172157</Value><Value>171354</Value><Value>170698</Value><Value>171254</Value><Value>171740</Value><Value>171303</Value><Value>170874</Value><Value>171248</Value><Value>171698</Value><Value>171191</Value><Value>171948</Value><Value>171920</Value><Value>171319</Value><Value>170649</Value><Value>171731</Value><Value>170643</Value><Value>171007</Value><Value>172186</Value><Value>171831</Value><Value>172128</Value><Value>172154</Value><Value>170841</Value><Value>171530</Value><Value>171142</Value><Value>171857</Value><Value>170982</Value><Value>171852</Value><Value>171574</Value><Value>171136</Value><Value>172107</Value><Value>171819</Value><Value>171746</Value><Value>171036</Value><Value>171331</Value><Value>170682</Value><Value>171779</Value><Value>171747</Value><Value>171383</Value><Value>170748</Value><Value>170691</Value><Value>170820</Value><Value>170829</Value><Value>171888</Value><Value>172098</Value><Value>172199</Value><Value>171290</Value><Value>170935</Value><Value>171056</Value><Value>171131</Value><Value>171415</Value><Value>171105</Value><Value>171135</Value><Value>171371</Value><Value>171640</Value><Value>171534</Value><Value>171012</Value><Value>172047</Value><Value>172081</Value><Value>171666</Value><Value>172089</Value><Value>172063</Value><Value>171449</Value><Value>171159</Value><Value>171808</Value><Value>171932</Value><Value>170664</Value><Value>172000</Value><Value>171220</Value><Value>170955</Value><Value>170740</Value><Value>171350</Value><Value>171528</Value><Value>171081</Value><Value>171199</Value><Value>171711</Value><Value>171446</Value><Value>171607</Value><Value>171292</Value><Value>170762</Value><Value>171896</Value><Value>170810</Value><Value>170990</Value><Value>171332</Value><Value>171080</Value><Value>171075</Value><Value>172130</Value><Value>171525</Value><Value>171993</Value><Value>172091</Value><Value>171924</Value><Value>172153</Value><Value>170661</Value><Value>171190</Value><Value>171021</Value><Value>172158</Value><Value>171980</Value><Value>171480</Value><Value>171598</Value><Value>171827</Value><Value>172166</Value><Value>171392</Value><Value>171512</Value><Value>171008</Value><Value>171610</Value><Value>171692</Value><Value>171908</Value><Value>171733</Value><Value>170866</Value><Value>171100</Value></Property><Property id="163401" name="PulsarProtocolLog" description="A log from Pulsar. This is the generic super type. Sub types will represent the log of a particular protocol." datatype="LIST<PulsarProtocolLog>" importance="FIX" flag="inheritance:FIX"><Value>171750</Value><Value>171019</Value><Value>171765</Value><Value>171269</Value><Value>170700</Value><Value>171665</Value><Value>171496</Value><Value>170954</Value><Value>170780</Value><Value>171313</Value><Value>171202</Value></Property><Property id="163429" name="ACQRawData" description="A file which is the direct/raw output of Acqknowledge." datatype="ACQRawData" importance="FIX" flag="inheritance:FIX">45506</Property><Property id="163456" name="syncMapValuesFiles" description="DESCRIBE ME!" datatype="LIST<FILE>" importance="FIX" flag="inheritance:FIX"><Value>45508</Value><Value>45509</Value><Value>45510</Value><Value>45511</Value><Value>45512</Value><Value>45513</Value><Value>45514</Value><Value>45515</Value><Value>45516</Value><Value>45517</Value><Value>45518</Value><Value>45519</Value><Value>45520</Value><Value>45521</Value><Value>45522</Value><Value>45523</Value><Value>45524</Value><Value>45525</Value><Value>45526</Value><Value>45527</Value><Value>45528</Value><Value>45529</Value><Value>45530</Value><Value>45531</Value><Value>45532</Value></Property></Record> -""")) diff --git a/unittests/test_apiutils.py b/unittests/test_apiutils.py index 5476059344407c0c08e86dc8c15fae73b12a23c6..df6fe52d5b6f7ceddea791167a2355d7b3bf2fc8 100644 --- a/unittests/test_apiutils.py +++ b/unittests/test_apiutils.py @@ -5,6 +5,8 @@ # # Copyright (C) 2018 Research Group Biomedical Physics, # Max-Planck-Institute for Dynamics and Self-Organization Göttingen +# Copyright (C) 2020 Timm Fitschen <t.fitschen@indiscale.com> +# Copyright (C) 2020 IndiScale GmbH <info@indiscale.com> # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -25,9 +27,10 @@ # A. Schlemmer, 02/2018 import caosdb as db -from .record import testrecord +from caosdb.apiutils import apply_to_ids import pickle import tempfile +from .test_property import testrecord def test_convert_object(): @@ -42,3 +45,20 @@ def test_pickle_object(): f.seek(0) rn2 = pickle.load(f) assert r2.date == rn2.date + + +def test_apply_to_ids(): + parent = db.RecordType(id=3456) + rec = db.Record(id=23) + p = db.Property(id=23345, datatype=db.INTEGER) + rec.add_parent(parent) + rec.add_property(p) + + def invert(id_): + return id_ * -1 + apply_to_ids([rec], invert) + + assert invert(3456) == -3456 + assert rec.parents[0].id == -3456 + assert rec.properties[0].id == -23345 + assert rec.id == -23 diff --git a/unittests/test_datatype.py b/unittests/test_datatype.py index 84f3ac7fd77e0f3fda464c5377b682ae67bd54e4..7cc5fd41c74ece160cac44b8e4386061fefd244f 100644 --- a/unittests/test_datatype.py +++ b/unittests/test_datatype.py @@ -25,6 +25,7 @@ from caosdb.common import datatype def test_list(): assert db.LIST("RT") == "LIST<RT>" + assert db.LIST(db.RecordType("bla")) == "LIST<bla>" def test_list_utilites(): diff --git a/unittests/test_entity.py b/unittests/test_entity.py index c3862ae253b8fa8ad5640ae79c1088a385fb752b..d877e1ab7267977a7e73d65033d6f02c16ea5521 100644 --- a/unittests/test_entity.py +++ b/unittests/test_entity.py @@ -22,42 +22,36 @@ # ** end header # """Tests for the Entity class.""" +# pylint: disable=missing-docstring import unittest -from caosdb import Entity, configure_connection +from caosdb import (INTEGER, Entity, Property, Record, RecordType, + configure_connection) from caosdb.connection.mockup import MockUpServerConnection -# pylint: disable=missing-docstring -from nose.tools import assert_equal as eq -from nose.tools import assert_is_not_none as there -from nose.tools import assert_true as tru - -def setup_module(): - there(Entity) - configure_connection(url="unittests", username="testuser", - password="testpassword", timeout=200, - implementation=MockUpServerConnection) +class TestEntity(unittest.TestCase): -def hat(obj, attr): - tru(hasattr(obj, attr)) - + def setUp(self): + self.assertIsNotNone(Entity) + configure_connection(url="unittests", username="testuser", + password="testpassword", timeout=200, + implementation=MockUpServerConnection) -class TestEntity(unittest.TestCase): def test_instance_variables(self): entity = Entity() - hat(entity, "role") - hat(entity, "id") - hat(entity, "name") - hat(entity, "description") - hat(entity, "parents") - hat(entity, "properties") + self.assertTrue(hasattr(entity, "role")) + self.assertTrue(hasattr(entity, "id")) + self.assertTrue(hasattr(entity, "name")) + self.assertTrue(hasattr(entity, "description")) + self.assertTrue(hasattr(entity, "parents")) + self.assertTrue(hasattr(entity, "properties")) def test_role(self): entity = Entity(role="TestRole") - eq(entity.role, "TestRole") + self.assertEqual(entity.role, "TestRole") entity.role = "TestRole2" - eq(entity.role, "TestRole2") + self.assertEqual(entity.role, "TestRole2") def test_instanciation(self): self.assertRaises(Exception, Entity()) diff --git a/unittests/test_property.py b/unittests/test_property.py index 1d41a200fecb5c9b8e314fecc81ef75b45a6ec79..76ebe8052fa8104992d6f31fc7f73a4065de1215 100644 --- a/unittests/test_property.py +++ b/unittests/test_property.py @@ -23,35 +23,52 @@ # """Tests for the Property class.""" # pylint: disable=missing-docstring -from nose.tools import (assert_is_not_none as there, assert_true as tru, - assert_equal as eq) -from caosdb import Entity, Property, configure_connection -from caosdb.connection.mockup import MockUpServerConnection +from lxml import etree +from caosdb import Entity, Property, Record - -def setup_module(): - there(Property) - configure_connection(url="unittests", username="testuser", - password="testpassword", timeout=200, - implementation=MockUpServerConnection) - - -def hat(obj, attr): - tru(hasattr(obj, attr)) +parser = etree.XMLParser(remove_comments=True) +testrecord = Record._from_xml(Record(), + etree.parse("unittests/test_record.xml", + parser).getroot()) def test_is_entity(): prop = Property() - tru(isinstance(prop, Entity)) + assert isinstance(prop, Entity) def test_instance_variables(): prop = Property() - hat(prop, "datatype") - hat(prop, "unit") - hat(prop, "value") + assert hasattr(prop, "datatype") + assert hasattr(prop, "unit") + assert hasattr(prop, "value") + + +def test_null_empty_text_value_1(): + assert testrecord.get_property("LISTofTEXT").value == ["One", "Two", "Three", None, ""] + + +def test_null_empty_text_value_2(): + assert testrecord.get_property("NULLTEXT1").value is None + + +def test_null_empty_text_value_3(): + assert testrecord.get_property("NULLTEXT2").value is None + + +def test_null_empty_text_value_4(): + assert testrecord.get_property("EMPTYTEXT1").value == "" + + +def test_null_empty_text_value_5(): + assert testrecord.get_property("EMPTYTEXT2").value == "" + + +def test_list_of_references_with_null(): + assert testrecord.get_property("MultiRecRecording").value[0] is None + assert testrecord.get_property("MultiRecRecording").value[1] == 170651 def test_role(): prop = Property() - eq(prop.role, "Property") + assert prop.role == "Property" diff --git a/unittests/test_record.py b/unittests/test_record.py index 001f91a726ae8c532a35a6d4875f434bedd3bbce..3b4eb4bbd34110706f7b0b1e88daac6c070dc1c6 100644 --- a/unittests/test_record.py +++ b/unittests/test_record.py @@ -26,40 +26,24 @@ # pylint: disable=missing-docstring import unittest -from nose.tools import assert_equal as eq -from nose.tools import assert_is_not_none as there -from nose.tools import assert_true as tru - -from caosdb import Entity, Record, configure_connection -from caosdb.connection.mockup import MockUpServerConnection - - -def setup_module(): - there(Record) - configure_connection(url="unittests", username="testuser", - password="testpassword", timeout=200, - implementation=MockUpServerConnection) - - -def hat(obj, attr): - tru(hasattr(obj, attr)) +from caosdb import Entity, Record def test_is_entity(): record = Record() - tru(isinstance(record, Entity)) + assert isinstance(record, Entity) def test_role(): record = Record() - eq(record.role, "Record") + assert record.role == "Record" class TestRecord(unittest.TestCase): def test_property_access(self): rec = Record() rec.add_property("Prop") - self.assertIsNone(rec.get_property("Pop")) - self.assertIsNotNone(rec.get_property("Prop")) - self.assertIsNotNone(rec.get_property("prop")) - self.assertIsNotNone(rec.get_property("prOp")) + assert rec.get_property("Pop") is None + assert rec.get_property("Prop") is not None + assert rec.get_property("prop") is not None + assert rec.get_property("prOp") is not None diff --git a/unittests/test_record.xml b/unittests/test_record.xml new file mode 100644 index 0000000000000000000000000000000000000000..5567e59050fdbcd07ce9b13cdc640c7bccf6c165 --- /dev/null +++ b/unittests/test_record.xml @@ -0,0 +1,330 @@ +<Record id="171179"> + <TransactionBenchmark></TransactionBenchmark> + <Parent description="Experiment for the LEAP project." id="163454" name="LeapExperiment"/> + <Property datatype="TEXT" description="A unique identifier for experiments" flag="inheritance:FIX" id="163414" importance="FIX" name="experimentId">KCdSYWJiaXQnLCAnZXhfdml2bycsICdzeW5jaHJvbml6YXRpb25fbWFwJywgTm9uZSwgJzIwMTctMDgtMTEnLCBOb25lLCBOb25lKQ==</Property> + <Property datatype="LIST<TEXT>" id="568765" importance="FIX" name="LISTofTEXT"> + <Value>One</Value> + <Value>Two</Value> + <Value>Three</Value> + <Value></Value><!-- NULL Value--> + <Value> + <EmptyString/> + </Value> + </Property> + <Property datatype="TEXT" id="568765" importance="FIX" name="EMPTYTEXT1"> + <Value> + <EmptyString/> + </Value> + </Property> + <Property datatype="TEXT" id="568765" importance="FIX" name="EMPTYTEXT2"> + <EmptyString/> + </Property> + <Property datatype="TEXT" id="568765" importance="FIX" name="NULLTEXT1"></Property> + <Property datatype="TEXT" id="568765" importance="FIX" name="NULLTEXT2"> + <Value></Value><!-- NULL Value --> + </Property> + <Property datatype="DATETIME" description="A calendar date. When something took place." flag="inheritance:FIX" id="163442" importance="FIX" name="date">2017-08-11</Property> + <Property datatype="TEXT" description="How the experiment was done: IN_VIVO or EX_VIVO." flag="inheritance:FIX" id="163399" importance="FIX" name="vivoness">ex_vivo</Property> + <Property datatype="TEXT" description="The species of the experimental animal." flag="inheritance:FIX" id="163418" importance="FIX" name="species">Rabbit</Property> + <Property datatype="ExperimentSeries" description="A collection of [Experiment] records which have the same [aim]." flag="inheritance:FIX" id="163449" importance="FIX" name="ExperimentSeries">163386</Property> + <Property datatype="LIST<MultiRecRecording>" flag="inheritance:FIX" id="163397" importance="FIX" name="MultiRecRecording"> + <Value></Value> + <Value>170651</Value> + <Value>171701</Value> + <Value>172074</Value> + <Value>171408</Value> + <Value>171687</Value> + <Value>170791</Value> + <Value>171115</Value> + <Value>171893</Value> + <Value>171237</Value> + <Value>171309</Value> + <Value>170899</Value> + <Value>171048</Value> + <Value>171077</Value> + <Value>171723</Value> + <Value>171085</Value> + <Value>171812</Value> + <Value>171829</Value> + <Value>171669</Value> + <Value>170695</Value> + <Value>170875</Value> + <Value>171751</Value> + <Value>171412</Value> + <Value>172204</Value> + <Value>170997</Value> + <Value>171490</Value> + <Value>171504</Value> + <Value>170977</Value> + <Value>171895</Value> + <Value>172233</Value> + <Value>171261</Value> + <Value>171318</Value> + <Value>171014</Value> + <Value>171132</Value> + <Value>171575</Value> + <Value>172217</Value> + <Value>171638</Value> + <Value>170656</Value> + <Value>171927</Value> + <Value>172232</Value> + <Value>171864</Value> + <Value>171564</Value> + <Value>171090</Value> + <Value>171633</Value> + <Value>171456</Value> + <Value>171755</Value> + <Value>170641</Value> + <Value>172144</Value> + <Value>171872</Value> + <Value>171619</Value> + <Value>171180</Value> + <Value>172019</Value> + <Value>171193</Value> + <Value>171069</Value> + <Value>171643</Value> + <Value>171789</Value> + <Value>170785</Value> + <Value>170904</Value> + <Value>171130</Value> + <Value>171098</Value> + <Value>171241</Value> + <Value>171796</Value> + <Value>171776</Value> + <Value>171175</Value> + <Value>171112</Value> + <Value>171514</Value> + <Value>171854</Value> + <Value>170962</Value> + <Value>171818</Value> + <Value>170745</Value> + <Value>171149</Value> + <Value>170639</Value> + <Value>171508</Value> + <Value>170800</Value> + <Value>171141</Value> + <Value>171414</Value> + <Value>171325</Value> + <Value>172168</Value> + <Value>171184</Value> + <Value>172083</Value> + <Value>172029</Value> + <Value>171556</Value> + <Value>172195</Value> + <Value>170789</Value> + <Value>171300</Value> + <Value>172230</Value> + <Value>171720</Value> + <Value>171341</Value> + <Value>171937</Value> + <Value>171673</Value> + <Value>172106</Value> + <Value>171281</Value> + <Value>171897</Value> + <Value>171108</Value> + <Value>172014</Value> + <Value>170867</Value> + <Value>171531</Value> + <Value>171674</Value> + <Value>172077</Value> + <Value>170944</Value> + <Value>171228</Value> + <Value>172207</Value> + <Value>171039</Value> + <Value>171778</Value> + <Value>170794</Value> + <Value>171646</Value> + <Value>172027</Value> + <Value>171279</Value> + <Value>171618</Value> + <Value>171233</Value> + <Value>170811</Value> + <Value>171357</Value> + <Value>172180</Value> + <Value>171585</Value> + <Value>170774</Value> + <Value>171625</Value> + <Value>171310</Value> + <Value>171855</Value> + <Value>172084</Value> + <Value>171395</Value> + <Value>171955</Value> + <Value>172216</Value> + <Value>171705</Value> + <Value>171312</Value> + <Value>171659</Value> + <Value>172192</Value> + <Value>171157</Value> + <Value>171978</Value> + <Value>170707</Value> + <Value>171250</Value> + <Value>171929</Value> + <Value>171709</Value> + <Value>170898</Value> + <Value>170723</Value> + <Value>172060</Value> + <Value>171693</Value> + <Value>172036</Value> + <Value>172162</Value> + <Value>171460</Value> + <Value>172065</Value> + <Value>171174</Value> + <Value>171461</Value> + <Value>172093</Value> + <Value>171200</Value> + <Value>172007</Value> + <Value>172102</Value> + <Value>171368</Value> + <Value>171761</Value> + <Value>172157</Value> + <Value>171354</Value> + <Value>170698</Value> + <Value>171254</Value> + <Value>171740</Value> + <Value>171303</Value> + <Value>170874</Value> + <Value>171248</Value> + <Value>171698</Value> + <Value>171191</Value> + <Value>171948</Value> + <Value>171920</Value> + <Value>171319</Value> + <Value>170649</Value> + <Value>171731</Value> + <Value>170643</Value> + <Value>171007</Value> + <Value>172186</Value> + <Value>171831</Value> + <Value>172128</Value> + <Value>172154</Value> + <Value>170841</Value> + <Value>171530</Value> + <Value>171142</Value> + <Value>171857</Value> + <Value>170982</Value> + <Value>171852</Value> + <Value>171574</Value> + <Value>171136</Value> + <Value>172107</Value> + <Value>171819</Value> + <Value>171746</Value> + <Value>171036</Value> + <Value>171331</Value> + <Value>170682</Value> + <Value>171779</Value> + <Value>171747</Value> + <Value>171383</Value> + <Value>170748</Value> + <Value>170691</Value> + <Value>170820</Value> + <Value>170829</Value> + <Value>171888</Value> + <Value>172098</Value> + <Value>172199</Value> + <Value>171290</Value> + <Value>170935</Value> + <Value>171056</Value> + <Value>171131</Value> + <Value>171415</Value> + <Value>171105</Value> + <Value>171135</Value> + <Value>171371</Value> + <Value>171640</Value> + <Value>171534</Value> + <Value>171012</Value> + <Value>172047</Value> + <Value>172081</Value> + <Value>171666</Value> + <Value>172089</Value> + <Value>172063</Value> + <Value>171449</Value> + <Value>171159</Value> + <Value>171808</Value> + <Value>171932</Value> + <Value>170664</Value> + <Value>172000</Value> + <Value>171220</Value> + <Value>170955</Value> + <Value>170740</Value> + <Value>171350</Value> + <Value>171528</Value> + <Value>171081</Value> + <Value>171199</Value> + <Value>171711</Value> + <Value>171446</Value> + <Value>171607</Value> + <Value>171292</Value> + <Value>170762</Value> + <Value>171896</Value> + <Value>170810</Value> + <Value>170990</Value> + <Value>171332</Value> + <Value>171080</Value> + <Value>171075</Value> + <Value>172130</Value> + <Value>171525</Value> + <Value>171993</Value> + <Value>172091</Value> + <Value>171924</Value> + <Value>172153</Value> + <Value>170661</Value> + <Value>171190</Value> + <Value>171021</Value> + <Value>172158</Value> + <Value>171980</Value> + <Value>171480</Value> + <Value>171598</Value> + <Value>171827</Value> + <Value>172166</Value> + <Value>171392</Value> + <Value>171512</Value> + <Value>171008</Value> + <Value>171610</Value> + <Value>171692</Value> + <Value>171908</Value> + <Value>171733</Value> + <Value>170866</Value> + <Value>171100</Value> + </Property> + <Property datatype="LIST<PulsarProtocolLog>" description="A log from Pulsar. This is the generic super type. Sub types will represent the log of a particular protocol." flag="inheritance:FIX" id="163401" importance="FIX" name="PulsarProtocolLog"> + <Value>171750</Value> + <Value>171019</Value> + <Value>171765</Value> + <Value>171269</Value> + <Value>170700</Value> + <Value>171665</Value> + <Value>171496</Value> + <Value>170954</Value> + <Value>170780</Value> + <Value>171313</Value> + <Value>171202</Value> + </Property> + <Property datatype="ACQRawData" description="A file which is the direct/raw output of Acqknowledge." flag="inheritance:FIX" id="163429" importance="FIX" name="ACQRawData">45506</Property> + <Property datatype="LIST<FILE>" description="DESCRIBE ME!" flag="inheritance:FIX" id="163456" importance="FIX" name="syncMapValuesFiles"> + <Value>45508</Value> + <Value>45509</Value> + <Value>45510</Value> + <Value>45511</Value> + <Value>45512</Value> + <Value>45513</Value> + <Value>45514</Value> + <Value>45515</Value> + <Value>45516</Value> + <Value>45517</Value> + <Value>45518</Value> + <Value>45519</Value> + <Value>45520</Value> + <Value>45521</Value> + <Value>45522</Value> + <Value>45523</Value> + <Value>45524</Value> + <Value>45525</Value> + <Value>45526</Value> + <Value>45527</Value> + <Value>45528</Value> + <Value>45529</Value> + <Value>45530</Value> + <Value>45531</Value> + <Value>45532</Value> + </Property> +</Record>