diff --git a/.travis.yml b/.travis.yml index ba6c228c..6c8e3474 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,18 +12,21 @@ matrix: python: "3.4" - os: linux python: "3.5" + - os: linux + python: "3.6" - os: osx language: generic env: - - OSXENV=3.5 -# Keep only one osx branch active for now -# since currently osx builds on travis -# are frequently stalled or indefinitely delayed. -# - os: osx -# language: generic -# env: -# - OSXENV=2.7 + - OSXENV=3.5.0 + - os: osx + language: generic + env: + - OSXENV=3.6.0 + - os: osx + language: generic + env: + - OSXENV=2.7.14 install: - export PYVER=${TRAVIS_PYTHON_VERSION:0:1} @@ -40,18 +43,20 @@ install: fi; - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then - if [[ "$OSXENV" == "2.7" ]]; then - brew install python@2; - virtualenv venv -p python; - source venv/bin/activate; - export PYCMD=python; - export PIPCMD=pip; - else - brew upgrade python; - source venv/bin/activate; - export PYCMD=python3; - export PIPCMD=pip3; - fi; + brew update; + brew install pyenv; + brew upgrade pyenv; + brew install pyenv-virtualenv; + eval "$(pyenv init -)"; + eval "$(pyenv virtualenv-init -)"; + pyenv install $OSXENV; + pyenv virtualenv $OSXENV venv; + pyenv activate venv; + which python; + python --version; + which pip; + export PYCMD=python; + export PIPCMD=pip; fi; - $PIPCMD install lxml enum34 pyyaml rdflib diff --git a/appveyor.yml b/appveyor.yml index ad4a08c2..124dd818 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -4,10 +4,14 @@ environment: PYVER: "2.7" - PYTHON: "C:\\Python35" PYVER: "3.5" + - PYTHON: "C:\\Python36" + PYVER: "3.6" - PYTHON: "C:\\Python27-x64" PYVER: "2.7" - PYTHON: "C:\\Python35-x64" PYVER: "3.5" + - PYTHON: "C:\\Python36-x64" + PYVER: "3.6" build: false diff --git a/doc/odml_ontology/root-ontology.ttl b/doc/odml_ontology/root-ontology.ttl index e65f5a91..dfb3c5eb 100644 --- a/doc/odml_ontology/root-ontology.ttl +++ b/doc/odml_ontology/root-ontology.ttl @@ -56,7 +56,7 @@ xsd:date rdf:type rdfs:Datatype ; ### https://g-node.org/projects/odml-rdf#hasValue :hasValue rdf:type owl:ObjectProperty ; rdfs:domain :Property ; - rdfs:range rdf:Bag ; + rdfs:range rdf:Seq ; rdfs:label "hasValue"^^xsd:string . @@ -94,7 +94,7 @@ xsd:date rdf:type rdfs:Datatype ; ### https://g-node.org/projects/odml-rdf#isValueOf :isValueOf rdf:type owl:ObjectProperty ; - rdfs:domain rdf:Bag ; + rdfs:domain rdf:Seq ; rdfs:range :Property ; rdfs:label "isValueOf"^^xsd:string . @@ -211,15 +211,15 @@ xsd:date rdf:type rdfs:Datatype ; # Classes ################################################################# -### http://www.w3.org/1999/02/22-rdf-syntax-ns#Bag -rdf:Bag rdf:type owl:Class ; +### http://www.w3.org/1999/02/22-rdf-syntax-ns#Seq +rdf:Seq rdf:type owl:Class ; rdfs:subClassOf [ rdf:type owl:Restriction ; owl:onProperty :isValueOf ; owl:someValuesFrom :Property ] ; - rdfs:comment "The class of unordered containers." ; + rdfs:comment "The class of ordered containers." ; rdfs:isDefinedBy rdf: ; - rdfs:label "Bag" . + rdfs:label "Seq" . ### https://g-node.org/projects/odml-rdf#Cell @@ -346,7 +346,7 @@ rdf:Bag rdf:type owl:Class ; rdfs:subClassOf owl:Thing , [ rdf:type owl:Restriction ; owl:onProperty :hasValue ; - owl:someValuesFrom rdf:Bag + owl:someValuesFrom rdf:Seq ] , [ rdf:type owl:Restriction ; owl:onProperty :isPropertyOf ; diff --git a/odml/tools/rdf_converter.py b/odml/tools/rdf_converter.py index 2f250c75..b313d156 100644 --- a/odml/tools/rdf_converter.py +++ b/odml/tools/rdf_converter.py @@ -5,6 +5,7 @@ from io import StringIO from os.path import dirname, abspath from rdflib import Graph, Literal, URIRef +from rdflib.graph import Seq from rdflib.namespace import XSD, RDF import odml @@ -123,11 +124,27 @@ def save_element(self, e, node=None): elif isinstance(fmt, Property.__class__) and \ k == 'value' and len(getattr(e, k)) > 0: values = getattr(e, k) - bag = URIRef(odmlns + str(uuid.uuid4())) - self.g.add((bag, RDF.type, RDF.Bag)) - self.g.add((curr_node, fmt.rdf_map(k), bag)) + seq = URIRef(odmlns + str(uuid.uuid4())) + self.g.add((seq, RDF.type, RDF.Seq)) + self.g.add((curr_node, fmt.rdf_map(k), seq)) + # rdflib so far does not respect RDF:li item order + # in RDF:Seq on loading so we have to use custom + # numbered Node elements for now. Once rdflib upgrades + # this should be reversed to RDF:li again! + # see https://github.com/RDFLib/rdflib/issues/280 + # -- keep until supported + # bag = URIRef(odmlns + str(uuid.uuid4())) + # self.g.add((bag, RDF.type, RDF.Bag)) + # self.g.add((curr_node, fmt.rdf_map(k), bag)) + # for v in values: + # self.g.add((bag, RDF.li, Literal(v))) + + counter = 1 for v in values: - self.g.add((bag, RDF.li, Literal(v))) + pred = "%s_%s" % (str(RDF), counter) + self.g.add((seq, URIRef(pred), Literal(v))) + counter = counter + 1 + # adding entities' properties else: val = getattr(e, k) @@ -258,9 +275,20 @@ def parse_property(self, prop_uri): elems = list(self.g.objects(subject=prop_uri, predicate=attr[1])) if attr[0] == "value" and len(elems) > 0: prop_attrs[attr[0]] = [] + + # rdflib does not respect order with RDF.li items yet, see comment above + # support both RDF.li and rdf:_nnn for now. + # Remove rdf:_nnn once rdflib respects RDF.li order in an RDF.Seq obj. values = list(self.g.objects(subject=elems[0], predicate=RDF.li)) - for v in values: - prop_attrs[attr[0]].append(v.toPython()) + if len(values) > 0: + for v in values: + prop_attrs[attr[0]].append(v.toPython()) + else: + # rdf:__nnn part + valseq = Seq(graph=self.g, subject=elems[0]) + for seqitem in valseq: + prop_attrs[attr[0]].append(seqitem.toPython()) + elif attr[0] == "id": prop_attrs[attr[0]] = prop_uri.split("#", 1)[1] else: diff --git a/test/resources/version_conversion.yaml b/test/resources/version_conversion.yaml index dab5948a..c331cd29 100644 --- a/test/resources/version_conversion.yaml +++ b/test/resources/version_conversion.yaml @@ -11,10 +11,10 @@ Document: properties: - name: prop_name values: - - dtype: !!python/object/apply:odml.dtypes.DType + - dtype: - string value: '[''one'', ''two'']' - - dtype: !!python/object/apply:odml.dtypes.DType + - dtype: - int value: '1' odml-version: '1' diff --git a/test/resources/version_conversion_int.yaml b/test/resources/version_conversion_int.yaml index f392dc50..f9dab1df 100644 --- a/test/resources/version_conversion_int.yaml +++ b/test/resources/version_conversion_int.yaml @@ -14,7 +14,7 @@ Document: values: - dtype: string - definition: def val 1 - dtype: !!python/object/apply:odml.dtypes.DType + dtype: - string filename: filename val 1 reference: ref val 1 @@ -22,7 +22,7 @@ Document: unit: arbitrary value: '1' - definition: def val 2 - dtype: !!python/object/apply:odml.dtypes.DType + dtype: - int filename: filename val 2 reference: ref val 2 @@ -30,7 +30,7 @@ Document: unit: arbitrary 2 value: '2' - definition: def val 3 - dtype: !!python/object/apply:odml.dtypes.DType + dtype: - string filename: filename val 3 reference: ref val 3 @@ -44,7 +44,7 @@ Document: values: - dtype: string - definition: def val 1 - dtype: !!python/object/apply:odml.dtypes.DType + dtype: - int filename: filename val 1 reference: ref val 1 @@ -52,7 +52,7 @@ Document: unit: arbitrary value: '1' - definition: def val 1 - dtype: !!python/object/apply:odml.dtypes.DType + dtype: - int filename: filename val 1 reference: ref val 1 @@ -60,7 +60,7 @@ Document: unit: arbitrary value: '2' - definition: def val 1 - dtype: !!python/object/apply:odml.dtypes.DType + dtype: - int filename: filename val 1 reference: ref val 1 @@ -68,7 +68,7 @@ Document: unit: arbitrary value: '3' - definition: def val 1 - dtype: !!python/object/apply:odml.dtypes.DType + dtype: - int filename: filename val 1 reference: ref val 1 @@ -76,7 +76,7 @@ Document: unit: arbitrary value: '4' - definition: def val 1 - dtype: !!python/object/apply:odml.dtypes.DType + dtype: - int filename: filename val 1 reference: ref val 1 @@ -84,7 +84,7 @@ Document: unit: arbitrary value: '4' - definition: def val 1 - dtype: !!python/object/apply:odml.dtypes.DType + dtype: - int filename: filename val 1 reference: ref val 1 @@ -92,7 +92,7 @@ Document: unit: arbitrary value: '4' - definition: def val 1 - dtype: !!python/object/apply:odml.dtypes.DType + dtype: - int filename: filename val 1 reference: ref val 1 @@ -100,7 +100,7 @@ Document: unit: arbitrary value: '4' - definition: def val 1 - dtype: !!python/object/apply:odml.dtypes.DType + dtype: - int filename: filename val 1 reference: ref val 1 @@ -132,7 +132,7 @@ Document: values: - dtype: string - definition: def val 1 - dtype: !!python/object/apply:odml.dtypes.DType + dtype: - int filename: filename val 1 reference: ref val 1 @@ -140,7 +140,7 @@ Document: unit: arbitrary value: '4' - definition: def val 1 - dtype: !!python/object/apply:odml.dtypes.DType + dtype: - int filename: filename val 1 reference: ref val 1 @@ -148,7 +148,7 @@ Document: unit: arbitrary value: '4' - definition: def val 1 - dtype: !!python/object/apply:odml.dtypes.DType + dtype: - int filename: filename val 1 reference: ref val 1 @@ -156,7 +156,7 @@ Document: unit: arbitrary value: '4' - definition: def val 1 - dtype: !!python/object/apply:odml.dtypes.DType + dtype: - int filename: filename val 1 reference: ref val 1 diff --git a/test/test_parser_odml.py b/test/test_parser_odml.py index 1f695a68..67864eb9 100644 --- a/test/test_parser_odml.py +++ b/test/test_parser_odml.py @@ -85,8 +85,11 @@ def test_rdf_file(self): self.rdf_writer.write_file(self.odml_doc, self.rdf_file) rdf_doc = self.rdf_reader.from_file(self.rdf_file, "turtle") - # RDF does not preserve the order of sections, - # need to check the attributes by hand. + self.assertEqual(self.odml_doc, rdf_doc[0]) + + # RDF does not preserve the order of sections or properties, + # check the attributes by hand to make sure everything + # was correctly imported. self.assertEqual(len(rdf_doc), 1) self.assertEqual(rdf_doc[0].author, self.odml_doc.author) self.assertEqual(rdf_doc[0].version, self.odml_doc.version) diff --git a/test/test_rdf_writer.py b/test/test_rdf_writer.py index 74fe195f..9666a01b 100644 --- a/test/test_rdf_writer.py +++ b/test/test_rdf_writer.py @@ -90,6 +90,8 @@ def test_adding_values(self): w = RDFWriter([doc]) w.convert_to_rdf() self.assertEqual(len(list(w.g.subject_objects(predicate=RDF.li))), 0) + self.assertEqual(len(list( + w.g.subject_objects(predicate=URIRef("%s_1" % str(RDF))))), 0) doc = parse(""" s1[t1] @@ -98,14 +100,22 @@ def test_adding_values(self): w = RDFWriter([doc]) w.convert_to_rdf() - self.assertEqual(len(list(w.g.subjects(predicate=RDF.li, object=Literal("val")))), 1) + self.assertEqual(len(list(w.g.subjects(predicate=RDF.li, + object=Literal("val")))), 0) + self.assertEqual(len(list(w.g.subjects(predicate=URIRef("%s_1" % str(RDF)), + object=Literal("val")))), 1) doc.sections[0].properties[0].append("val2") w = RDFWriter([doc]) w.convert_to_rdf() - self.assertEqual(len(list(w.g.subject_objects(predicate=RDF.li))), 2) - self.assertEqual(len(list(w.g.subjects(predicate=RDF.li, object=Literal("val")))), 1) - self.assertEqual(len(list(w.g.subjects(predicate=RDF.li, object=Literal("val2")))), 1) + self.assertEqual(len(list(w.g.subject_objects(predicate=RDF.li))), 0) + self.assertEqual(len(list(w.g.subjects(predicate=RDF.li, object=Literal("val")))), 0) + self.assertEqual(len(list(w.g.subjects(predicate=RDF.li, object=Literal("val2")))), 0) + + self.assertEqual(len(list(w.g.subjects(predicate=URIRef("%s_1" % str(RDF)), + object=Literal("val")))), 1) + self.assertEqual(len(list(w.g.subjects(predicate=URIRef("%s_2" % str(RDF)), + object=Literal("val2")))), 1) doc = parse(""" s1[t1] @@ -117,7 +127,9 @@ def test_adding_values(self): w = RDFWriter([doc]) w.convert_to_rdf() - self.assertEqual(len(list(w.g.subjects(predicate=RDF.li, object=Literal("val")))), 3) + self.assertEqual(len(list(w.g.subjects(predicate=RDF.li, object=Literal("val")))), 0) + self.assertEqual(len(list(w.g.subjects(predicate=URIRef("%s_1" % str(RDF)), + object=Literal("val")))), 3) def test_section_subclass(self): p = os.path.join(dirname(dirname(abspath(__file__))), 'doc', 'section_subclasses.yaml')