2 """Copyright (c) 2005-2016, University of Oxford.
5 University of Oxford means the Chancellor, Masters and Scholars of the
6 University of Oxford, having an administrative office at Wellington
7 Square, Oxford OX1 2JD, UK.
9 This file is part of Chaste.
11 Redistribution and use in source and binary forms, with or without
12 modification, 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.
22 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
26 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
28 GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31 OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 This module abstracts the interface to RDF metadata about CellML models.
37 The RdfProcessor class below pretends to be the module itself, so all its properties
38 are available at module-level, and these should typically be called by users.
40 It also provides sets METADATA_NAMES and STIMULUS_NAMES, which contain the local names
41 of terms in the ontology that can annotate variables, and the subset of those names
42 which define properties of the stimulus current (but not the current itself), respectively.
49 from cStringIO
import StringIO
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)
352 if __name__ ==
'__main__':