2"""Copyright (c) 2005-2016, University of Oxford.
5University of Oxford means the Chancellor, Masters and Scholars of the
6University of Oxford, having an administrative office at Wellington
7Square, Oxford OX1 2JD, UK.
9This file is part of Chaste.
11Redistribution and use in source and binary forms, with or without
12modification, are permitted provided that the following conditions are met:
13 * Redistributions of source code must retain the above copyright notice,
14 this list of conditions and the following disclaimer.
15 * Redistributions
in binary form must reproduce the above copyright notice,
16 this list of conditions
and the following disclaimer
in the documentation
17 and/
or other materials provided
with the distribution.
18 * Neither the name of the University of Oxford nor the names of its
19 contributors may be used to endorse
or promote products derived
from this
20 software without specific prior written permission.
22THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS"
23AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
26LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
28GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35This module abstracts the interface to RDF metadata about CellML models.
37The RdfProcessor
class below pretends to be the module itself, so all its properties
38are available at module-level,
and these should typically be called by users.
40It also provides sets METADATA_NAMES
and STIMULUS_NAMES, which contain the local names
41of terms
in the ontology that can annotate variables,
and the subset of those names
42which define properties of the stimulus current (but
not the current itself), respectively.
49from cStringIO import StringIO
51# We now only support rdflib for RDF processing
62 """Implements CellML metadata functionality using the RDFLib library."""
64 """Create the wrapper."""
67 sys.modules[name] = self
74 rdflib_major_version = int(rdflib.__version__[0])
75 if rdflib_major_version >= 3:
78 self.
Graph = rdflib.ConjunctiveGraph
81 """Provide access to real module-level variables as though they're class properties."""
83 baseget = super(RdfProcessor, self).__getattribute__
84 module = baseget(
'_module')
85 if baseget(
'_initializing')
and not name[:2] ==
'__' == name[-2:]:
86 setattr(self,
'_initializing',
False)
90 except AttributeError:
91 return getattr(module, name)
94 pycml.DEBUG(
'cellml-metadata', *args)
97 """Load the Oxford metadata ontology the first time it's needed."""
98 pycml_path = os.path.dirname(os.path.realpath(__file__))
99 oxmeta_ttl = os.path.join(pycml_path,
'oxford-metadata.ttl')
100 oxmeta_rdf = os.path.join(pycml_path,
'oxford-metadata.rdf')
104 if os.stat(oxmeta_ttl).st_mtime > os.stat(oxmeta_rdf).st_mtime + 10.0:
107 g.parse(oxmeta_ttl, format=
'turtle')
109 print >> sys.stderr,
'Unable to convert metadata from Turtle format to RDF/XML.'
110 print >> sys.stderr,
'Probably you need to upgrade rdflib to version 4.\nDetails of error:'
112 g.serialize(oxmeta_rdf, format=
'xml')
115 g.parse(oxmeta_rdf, format=
'xml')
117 annotation_terms = list(g.subjects(rdflib.RDF.type, rdflib.URIRef(pycml.NSS[
'oxmeta']+
u'Annotation')))
125 """Fake a module-level constant as a property for lazy loading."""
132 """Fake a module-level constant as a property for lazy loading."""
138 """Create a new RDF store for the given CellML model.
139 The new store will be available as self.
_models[cellml_model].
144 """Add statements to the model's graph from the given serialized RDF."""
146 g.parse(StringIO(rdf_text))
147 rdf_model = self.
_models[cellml_model]
152 """Serialize the RDF model for this CellML model to XML."""
153 return self.
_models[cellml_model].serialize()
156 """Get the RDF graph of the given CellML model.
158 If this model is already
in our map,
return the existing RDF store.
159 Otherwise, extract metadata
from all RDF elements
in the cellml_model,
160 create a new RDF graph
from these,
and delete the original elements.
162 if not cellml_model
in self.
_models:
163 rdf_blocks = cellml_model.xml_xpath(
u'//rdf:RDF')
165 for rdf_block
in rdf_blocks:
166 rdf_text = rdf_block.xml()
168 rdf_block.xml_parent.xml_remove_child(rdf_block)
169 return self.
_models[cellml_model]
172 """The given model is being deleted / no longer needed."""
173 if cellml_model
in self.
_models:
175 self.
_debug(
'Clearing RDF state for model', cellml_model.name)
178 """Ensure the RDF serialized into the given CellML model is up-to-date.
180 If we have done any metadata processing on the given model, will serialize
181 our RDF store into the rdf:RDF element child of the model.
183 if cellml_model
in self.
_models:
185 rdf_blocks = cellml_model.xml_xpath(
u'//rdf:RDF')
187 pycml.LOG(
'cellml-metadata', logging.WARNING,
'Removing existing RDF in model.')
188 for rdf_block
in rdf_blocks:
189 rdf_block.xml_parent.xml_remove_child(rdf_block)
192 rdf_doc = pycml.amara.parse(rdf_text)
193 cellml_model.xml_append(rdf_doc.RDF)
198 """Create an RDF node.
200 node_content, if given, must either be a tuple (qname, namespace_uri),
201 or a string,
in which case it
is interpreted
as a literal RDF node.
203 Alternatively, fragment_id may be given to refer to a cmeta:id within the
206 If neither are given, a blank node
is created.
209 node = rdflib.URIRef(str(
'#'+fragment_id))
211 if type(node_content) == types.TupleType:
212 qname, nsuri = node_content
213 if nsuri[-1]
not in [
'#',
'/']:
215 ns = rdflib.Namespace(nsuri)
216 prefix, local_name = pycml.SplitQName(qname)
217 node = ns[local_name]
218 elif type(node_content)
in types.StringTypes:
219 node = rdflib.Literal(node_content)
221 raise ValueError(
"Don't know how to make a node from " + str(node_content)
222 +
" of type " + type(node_content))
224 node = rdflib.BNode()
228 """Create a fragment identifier that hasn't already been used.
230 If base_id hasn't been used, it will be returned. Otherwise, underscores will
231 be added until a unique id is obtained.
241 """Add a statement to the model."""
242 self.
_debug(
"add_statement(", source,
",", property,
",", target,
")")
244 rdf_model.add((source, property, target))
247 """Add a statement to the model, avoiding duplicates.
249 Any existing statements with the same source
and property will first be removed.
251 self._debug("replace_statement(", source,
",", property,
",", target,
")")
253 rdf_model.set((source, property, target))
256 """Remove all statements matching (source,property,target).
258 Any of these may be None to match anything.
260 self._debug("remove_statements(", source,
",", property,
",", target,
")")
262 rdf_model.remove((source, property, target))
265 """Get the target of property from source.
267 Returns None if no such target exists. Throws
if there
is more than one match.
269 If the target
is a literal node, returns its string value. Otherwise returns an RDF node.
273 target = rdf_model.value(subject=source, predicate=property, any=
False)
274 except rdflib.exceptions.UniquenessError:
275 raise ValueError(
"Too many targets for source " + str(source) +
" and property " + str(property))
276 if isinstance(target, rdflib.Literal):
278 self.
_debug(
"get_target(", source,
",", property,
") -> ",
"'" + str(target) +
"'")
282 """Get a list of all targets of property from source.
284 If no such targets exist, returns an empty list.
285 If property is None, targets of any property will be returned.
286 Alternatively
if source
is None, targets of the given property
from any source will be found.
288 For each target,
if it
is a literal node then its string value
is given.
289 Otherwise the list will contain an RDF node.
292 targets = list(rdf_model.objects(subject=source, predicate=property))
293 for i, target
in enumerate(targets):
294 if isinstance(target, rdflib.Literal):
295 targets[i] = str(target)
299 """Find variables in the cellml_model with the given property, and optionally value.
301 property (and value
if given) should be a suitable input
for create_rdf_node.
303 Will
return a list of cellml_variable instances.
305 self._debug("find_variables(", property,
",", value,
")")
311 for result
in rdf_model.subjects(property, value):
312 assert isinstance(result, rdflib.URIRef),
"Non-resource annotated."
314 assert uri[0] ==
'#',
"Annotation found on non-local URI"
316 var_objs = cellml_model.xml_xpath(
u'*/cml:variable[@cmeta:id="%s"]' % var_id)
317 assert len(var_objs) == 1,
"Didn't find a unique variable with ID " + var_id
318 vars.append(var_objs[0])
322 """Return an iterator over all RDF triples in the model."""
324 for triple
in rdf_model:
328 """Given a URI reference RDF node and namespace URI, return the local part.
330 Will raise an exception
if node
is not a URI reference unless not_uri_ok
is True.
331 Will
raise an exception
if the node doesn
't live in the given namespace, unless
332 wrong_ns_ok is True. In both cases,
if the error
is suppressed the empty string
333 will be returned instead.
336 if not isinstance(node, rdflib.URIRef):
338 raise ValueError(
"Cannot extract namespace member for a non-URI RDF node.")
339 if node.startswith(nsuri):
340 local_part = node[len(nsuri):]
341 elif not wrong_ns_ok:
342 raise ValueError(
"Node is not in correct namespace.")
343 self.
_debug(
"namespace_member(", node,
",", nsuri,
") = ", local_part)
352if __name__ ==
'__main__':