From 720f6b1dbf284d7d7390d6095a11f80abeb7af35 Mon Sep 17 00:00:00 2001 From: yshalivskyy Date: Mon, 17 Jul 2017 18:40:40 +0300 Subject: [PATCH 1/4] [rdf] changes to the odml core module --- odml/doc.py | 23 +++++++++++----- odml/format.py | 59 +++++++++++++++++++++++++++++++++++++++-- odml/property.py | 21 ++++++++++----- odml/section.py | 25 ++++++++++------- odml/tools/xmlparser.py | 16 +++++------ 5 files changed, 109 insertions(+), 35 deletions(-) diff --git a/odml/doc.py b/odml/doc.py index 596e7a27..b039d2b6 100644 --- a/odml/doc.py +++ b/odml/doc.py @@ -1,6 +1,8 @@ # -*- coding: utf-8 -import odml.dtypes as dtypes +import uuid + import odml.base as base +import odml.dtypes as dtypes import odml.format as format import odml.terminology as terminology from odml.tools.doc_inherit import inherit_docstring, allow_inherit_docstring @@ -14,22 +16,32 @@ class Document(base._baseobj): class BaseDocument(base.sectionable, Document): """ A represenation of an odML document in memory. - Its odml attributes are: *author*, *date*, *version* and *repository*. - A Document behaves very much like a section, except that it cannot hold properties. """ _format = format.Document - def __init__(self, author=None, date=None, version=None, repository=None): + def __init__(self, author=None, date=None, version=None, repository=None, id=None): super(BaseDocument, self).__init__() + self._id = str(uuid.uuid4()) self._author = author self._date = date # date must be a datetime self._version = version self._repository = repository + @property + def id(self): + """ + The uuid for the document. + """ + return self._id + + @id.setter + def id(self, new_value): + self._id = new_value + @property def author(self): """ @@ -78,7 +90,6 @@ def finalize(self): """ This needs to be called after the document is set up from parsing it will perform additional operations, that need the complete document. - In particular, this method will resolve all *link* and *include* attributes accordingly. """ @@ -96,4 +107,4 @@ def get_terminology_equivalent(self): if self.repository is None: return None term = terminology.load(self.repository) - return term + return term \ No newline at end of file diff --git a/odml/format.py b/odml/format.py index 7f9c40b2..6f3f4a38 100644 --- a/odml/format.py +++ b/odml/format.py @@ -1,18 +1,36 @@ +from rdflib import Namespace + import odml + """ A module providing general format information and mappings of xml-attributes to their python class equivalents """ - class Format(object): _map = {} _rev_map = None + _rdf_map = {} + _rdf_type = None + _ns = Namespace("https://g-node.org/projects/odml-rdf#") def map(self, name): """ Maps an odml name to a python name """ return self._map.get(name, name) + def rdf_map(self, name): + """ Maps a python name to a odml rdf namespace """ + return self._rdf_map.get(name, name) + + def rdf_type(self): + """ Return rdf type of an object """ + return self._rdf_type + + @staticmethod + def namespace(): + """ Return current link to current odml namespace""" + return Format._ns + def revmap(self, name): """ Maps a python name to an odml name """ if self._rev_map is None: @@ -33,7 +51,10 @@ def create(self, *args, **kargs): class Property(Format): _name = "property" + _ns = Format._ns + _rdf_type = _ns.Property _args = { + 'id': 0, 'name': 1, 'value': 0, 'unit': 0, @@ -48,11 +69,23 @@ class Property(Format): 'dependencyvalue': 'dependency_value', 'type': 'dtype' } + _rdf_map = { + 'id': _ns.id, + 'name': _ns.name, + 'definition': _ns.definition, + 'dtype': _ns.dtype, + 'unit': _ns.unit, + 'uncertainty': _ns.uncertainty, + 'value': _ns.hasValue + } class Section(Format): _name = "section" + _ns = Format._ns + _rdf_type = _ns.Section _args = { + 'id': 0, 'type': 1, 'name': 0, 'definition': 0, @@ -67,11 +100,24 @@ class Section(Format): 'section': 'sections', 'property': 'properties', } + _rdf_map = { + 'id': _ns.id, + 'name': _ns.name, + 'definition': _ns.definition, + 'type': _ns.type, + 'repository': _ns.terminology, + 'reference': _ns.reference, + 'sections': _ns.hasSection, + 'properties': _ns.hasProperty, + } class Document(Format): _name = "odML" + _ns = Format._ns + _rdf_type = _ns.Document _args = { + 'id': 0, 'version': 0, 'author': 0, 'date': 0, @@ -81,10 +127,19 @@ class Document(Format): _map = { 'section': 'sections' } + _rdf_map = { + 'id': _ns.id, + 'author': _ns.author, + 'date': _ns.date, + # 'doc_version': _ns.docversion, # discuss about the changes to the data model + 'repository': _ns.terminology, + 'sections': _ns.hasSection, + 'version': _ns['doc-version'], + } Document = Document() Section = Section() Property = Property() -__all__ = [Document, Section, Property] +__all__ = [Document, Section, Property] \ No newline at end of file diff --git a/odml/property.py b/odml/property.py index a5846d83..6890e5e9 100644 --- a/odml/property.py +++ b/odml/property.py @@ -1,8 +1,10 @@ # -*- coding: utf-8 +import uuid + import odml.base as base -import odml.format as frmt import odml.dtypes as dtypes +import odml.format as frmt from odml.tools.doc_inherit import inherit_docstring, allow_inherit_docstring @@ -17,11 +19,10 @@ class BaseProperty(base.baseobject, Property): def __init__(self, name, value=None, parent=None, unit=None, uncertainty=None, reference=None, definition=None, - dependency=None, dependency_value=None, dtype=None): + dependency=None, dependency_value=None, dtype=None, id=None): """ Create a new Property with a single value. The method will try to infer the value's dtype from the type of the value if not explicitly stated. - Example for a property with >>> p = Property("property1", "a string") >>> p.dtype @@ -46,6 +47,7 @@ def __init__(self, name, value=None, parent=None, unit=None, if dtype is not given, the type is deduced from the values """ # TODO validate arguments + self._id = str(uuid.uuid4()) self._name = name self._parent = None self._value = [] @@ -59,6 +61,14 @@ def __init__(self, name, value=None, parent=None, unit=None, self.value = value self.parent = parent + @property + def id(self): + return self._id + + @id.setter + def id(self, new_value): + self._id = new_value + @property def name(self): return self._name @@ -74,10 +84,8 @@ def __repr__(self): def dtype(self): """ The data type of the value - If the data type is changed, it is tried, to convert the value to the new type. - If this doesn't work, the change is refused. This behaviour can be overridden by directly accessing the *_dtype* attribute and adjusting the *data* attribute manually. @@ -224,7 +232,6 @@ def dependency_value(self, new_value): def remove(self, value): """ Remove a value from this property and unset its parent. - Raises a TypeError if this would cause the property not to hold any value at all. This can be circumvented by using the *_values* property. """ @@ -284,4 +291,4 @@ def __len__(self): return len(self._value) def __getitem__(self, key): - return self._value[key] + return self._value[key] \ No newline at end of file diff --git a/odml/section.py b/odml/section.py index 407bf31a..aa853e6f 100644 --- a/odml/section.py +++ b/odml/section.py @@ -1,12 +1,14 @@ # -*- coding: utf-8 +import uuid + import odml.base as base import odml.format as format import odml.terminology as terminology +from odml.doc import BaseDocument # this is supposedly ok, as we only use it for an isinstance check from odml.property import Property # it MUST however not be used to create any Property objects from odml.tools.doc_inherit import inherit_docstring, allow_inherit_docstring -from odml.doc import BaseDocument class Section(base._baseobj): @@ -17,7 +19,7 @@ class Section(base._baseobj): class BaseSection(base.sectionable, Section): """ An odML Section """ type = None - id = None + # id = None _link = None _include = None reference = None # the *import* property @@ -27,7 +29,8 @@ class BaseSection(base.sectionable, Section): _format = format.Section def __init__(self, name, type=None, parent=None, - definition=None, reference=None): + definition=None, reference=None, id=None): + self._id = str(uuid.uuid4()) self._parent = None self._name = name self._props = base.SmartList() @@ -42,6 +45,14 @@ def __repr__(self): return "
" % (self._name, self.type, len(self._sections)) + @property + def id(self): + return self._id + + @id.setter + def id(self, new_value): + self._id = new_value + @property def name(self): return self._name @@ -94,15 +105,12 @@ def include(self, new_value): def link(self): """ Specifies a softlink, i.e. a path within the document - When the merge()-method is called, the link will be resolved creating according copies of the section referenced by the link attribute. - When the unmerge() method is called (happens when running clean()) the link is unresolved, i.e. all properties and sections that are completely equivalent to the merged object will be removed. (They will be restored accordingly when calling merge()). - When changing the *link* attribute, the previously merged section is unmerged, and the new reference will be immediately resolved. To avoid this side-effect, directly change the *_link* attribute. @@ -312,9 +320,7 @@ def contains(self, obj): def merge(self, section=None): """ Merges this section with another *section* - See also: :py:attr:`odml.section.BaseSection.link` - If section is none, sets the link/include attribute (if _link or _include are set), causing the section to be automatically merged to the referenced section. @@ -378,7 +384,6 @@ def is_merged(self): Returns True if the section is merged with another one (e.g. through :py:attr:`odml.section.BaseSection.link` or :py:attr:`odml.section.BaseSection.include`) - The merged object can be accessed through the *_merged* attribute. """ return self._merged is not None @@ -388,4 +393,4 @@ def can_be_merged(self): """ Returns True if either a *link* or an *include* attribute is specified """ - return self._link is not None or self._include is not None + return self._link is not None or self._include is not None \ No newline at end of file diff --git a/odml/tools/xmlparser.py b/odml/tools/xmlparser.py index 9030a9a3..86a83619 100644 --- a/odml/tools/xmlparser.py +++ b/odml/tools/xmlparser.py @@ -1,15 +1,14 @@ #!/usr/bin/env python """ The XML parsing module. - Parses odML files. Can be invoked standalone: - python -m odml.tools.xmlparser file.odml """ # TODO make this module a parser class, allow arguments (e.g. # skip_errors=1 to parse even broken documents) -import sys import csv +import sys + try: from StringIO import StringIO except ImportError: @@ -18,7 +17,6 @@ from lxml import etree as ET from lxml.builder import E # this is needed for py2exe to include lxml completely -from lxml import _elementpath as _dummy try: unicode = unicode @@ -49,10 +47,10 @@ def to_csv(val): def from_csv(value_string): - if len(value_string) == 0: - return [] if value_string[0] == "[": value_string = value_string[1:-1] + if len(value_string) == 0: + return [] stream = StringIO(value_string) stream.seek(0) reader = csv.reader(stream, dialect="excel") @@ -164,9 +162,7 @@ class ParserException(Exception): class XMLReader(object): """ A reader to parse xml-files or strings into odml data structures - Usage: - >>> doc = XMLReader().fromFile(open("file.odml")) """ @@ -298,7 +294,7 @@ def parse_tag(self, root, fmt, insert_children=True, create=None): obj = create(args=arguments, text=''.join(text), children=children) for k, v in arguments.items(): - if hasattr(obj, k) and getattr(obj, k) is None: + if hasattr(obj, k) and (getattr(obj, k) is None or k == 'id'): try: setattr(obj, k, v) except Exception as e: @@ -353,4 +349,4 @@ def parse_value(self, root, fmt): if len(args) < 1: parser.print_help() else: - dumper.dumpDoc(load(args[0])) + dumper.dumpDoc(load(args[0])) \ No newline at end of file From 1c80fd9b577b8074790ef96847c5ae46abc5e284 Mon Sep 17 00:00:00 2001 From: yshalivskyy Date: Mon, 17 Jul 2017 18:42:49 +0300 Subject: [PATCH 2/4] [rdf] add rdflib to setup.py and travis.yml --- .travis.yml | 4 ++-- setup.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 19a49155..e39fca9f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,7 +17,7 @@ matrix: install: - export PYVER=${TRAVIS_PYTHON_VERSION:0:1} - pip install --upgrade coveralls - - pip install lxml enum34 + - pip install lxml enum34 rdflib script: - python setup.py build @@ -28,4 +28,4 @@ script: fi; after_success: -- if [ $COVERALLS = 1 ]; then coveralls; fi; +- if [ $COVERALLS = 1 ]; then coveralls; fi; \ No newline at end of file diff --git a/setup.py b/setup.py index 4878975b..149ffa79 100644 --- a/setup.py +++ b/setup.py @@ -14,7 +14,7 @@ with open('README.rst') as f: description_text = f.read() -install_req = ["lxml"] +install_req = ["lxml", "rdflib"] if sys.version_info < (3, 4): install_req += ["enum34"] @@ -28,4 +28,4 @@ test_suite='test', install_requires=install_req, long_description=description_text, - ) + ) \ No newline at end of file From fba563ce3b89efc030c7c7a4e0e1465d5b80915a Mon Sep 17 00:00:00 2001 From: yshalivskyy Date: Tue, 18 Jul 2017 14:02:53 +0300 Subject: [PATCH 3/4] [rdf] remove id.setter --- odml/doc.py | 4 ---- odml/format.py | 3 ++- odml/property.py | 4 ---- odml/section.py | 4 ---- odml/tools/xmlparser.py | 5 ++++- 5 files changed, 6 insertions(+), 14 deletions(-) diff --git a/odml/doc.py b/odml/doc.py index b039d2b6..6a7ea704 100644 --- a/odml/doc.py +++ b/odml/doc.py @@ -38,10 +38,6 @@ def id(self): """ return self._id - @id.setter - def id(self, new_value): - self._id = new_value - @property def author(self): """ diff --git a/odml/format.py b/odml/format.py index 6f3f4a38..0e272aed 100644 --- a/odml/format.py +++ b/odml/format.py @@ -7,6 +7,7 @@ and mappings of xml-attributes to their python class equivalents """ + class Format(object): _map = {} _rev_map = None @@ -142,4 +143,4 @@ class Document(Format): Section = Section() Property = Property() -__all__ = [Document, Section, Property] \ No newline at end of file +__all__ = [Document, Section, Property] diff --git a/odml/property.py b/odml/property.py index 6890e5e9..c6ffe331 100644 --- a/odml/property.py +++ b/odml/property.py @@ -65,10 +65,6 @@ def __init__(self, name, value=None, parent=None, unit=None, def id(self): return self._id - @id.setter - def id(self, new_value): - self._id = new_value - @property def name(self): return self._name diff --git a/odml/section.py b/odml/section.py index aa853e6f..2d6570d3 100644 --- a/odml/section.py +++ b/odml/section.py @@ -49,10 +49,6 @@ def __repr__(self): def id(self): return self._id - @id.setter - def id(self, new_value): - self._id = new_value - @property def name(self): return self._name diff --git a/odml/tools/xmlparser.py b/odml/tools/xmlparser.py index 86a83619..3bee0615 100644 --- a/odml/tools/xmlparser.py +++ b/odml/tools/xmlparser.py @@ -296,7 +296,10 @@ def parse_tag(self, root, fmt, insert_children=True, create=None): for k, v in arguments.items(): if hasattr(obj, k) and (getattr(obj, k) is None or k == 'id'): try: - setattr(obj, k, v) + if k == 'id' and v is not None: + obj._id = v + else: + setattr(obj, k, v) except Exception as e: self.warn("cannot set '%s' property on <%s>: %s" % (k, root.tag, repr(e)), root) From 484186f2e340fe0e19f0a9463ff6ad5de873574d Mon Sep 17 00:00:00 2001 From: yshalivskyy Date: Wed, 19 Jul 2017 19:26:07 +0300 Subject: [PATCH 4/4] [rdf] removing id attr from constructors --- odml/doc.py | 2 +- odml/property.py | 2 +- odml/section.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/odml/doc.py b/odml/doc.py index 6a7ea704..bfcd6229 100644 --- a/odml/doc.py +++ b/odml/doc.py @@ -23,7 +23,7 @@ class BaseDocument(base.sectionable, Document): _format = format.Document - def __init__(self, author=None, date=None, version=None, repository=None, id=None): + def __init__(self, author=None, date=None, version=None, repository=None): super(BaseDocument, self).__init__() self._id = str(uuid.uuid4()) self._author = author diff --git a/odml/property.py b/odml/property.py index c6ffe331..70da20e9 100644 --- a/odml/property.py +++ b/odml/property.py @@ -19,7 +19,7 @@ class BaseProperty(base.baseobject, Property): def __init__(self, name, value=None, parent=None, unit=None, uncertainty=None, reference=None, definition=None, - dependency=None, dependency_value=None, dtype=None, id=None): + dependency=None, dependency_value=None, dtype=None): """ Create a new Property with a single value. The method will try to infer the value's dtype from the type of the value if not explicitly stated. diff --git a/odml/section.py b/odml/section.py index 2d6570d3..8b99a58c 100644 --- a/odml/section.py +++ b/odml/section.py @@ -29,7 +29,7 @@ class BaseSection(base.sectionable, Section): _format = format.Section def __init__(self, name, type=None, parent=None, - definition=None, reference=None, id=None): + definition=None, reference=None): self._id = str(uuid.uuid4()) self._parent = None self._name = name