Viewing file: Model.py (30.34 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
######################################################################## # $Header: /var/local/cvsroot/4Suite/Ft/Server/Server/Xslt/Model.py,v 1.87 2005/04/06 23:05:46 jkloth Exp $ """ XSLT and XPath extensions supporting the 4Suite RDF API
Copyright 2004 Fourthought, Inc. (USA). Detailed license and copyright information: http://4suite.org/COPYRIGHT Project home, documentation, distributions: http://4suite.org/ """
import cStringIO, time from string import strip
from Ft.Lib import Time as FtTime from Ft.Rdf import Model from Ft.Rdf import OBJECT_TYPE_UNKNOWN, OBJECT_TYPE_LITERAL, OBJECT_TYPE_RESOURCE from Ft.Rdf.Statement import Statement from Ft.Rdf.Parsers import Versa from Ft.Rdf.Triclops import ParseNsMappings from Ft.Server import RESERVED_NAMESPACE from Ft.Server.Common.ResourceTypes import * from Ft.Xml.XPath import Conversions, Util from Ft.Xml.Xslt import XsltElement, ContentInfo, AttributeInfo
from Ns import RDF_NS import FtssXsltBase
VB_PARAM = (RESERVED_NAMESPACE, 'versa-variable-bindings')
OBJ_TYPE_LOOKUP = {'RESOURCE': OBJECT_TYPE_RESOURCE, 'LITERAL': OBJECT_TYPE_LITERAL}
def _serializeStatements(stmts, writer): """ Use a DOM writer to serialize a given list of statements as returned from a complete. Used by various functions in this module. """ for stmt in stmts: writer.startElement(u'Statement') writer.startElement(u'Subject') writer.text(stmt.subject) writer.endElement(u'Subject') writer.startElement(u'Predicate') writer.text(stmt.predicate) writer.endElement(u'Predicate') writer.startElement(u'Object') writer.text(stmt.object) writer.endElement(u'Object') writer.startElement(u'StatementUri') writer.text(stmt.uri) writer.endElement(u'StatementUri') writer.startElement(u'Scope') writer.text(stmt.scope) writer.endElement(u'Scope') writer.endElement(u'Statement')
# -- extension functions ----------------------------------------------
def RdfComplete(context, subj, pred, obj=None): """ Runs a complete on the repository model, which matches patterns among the triples in the model. You provide whichever of the subject, predicate and object you'd like to match, and use the frdf:any() function as a wildcard. The return value is a generated node set which has one or more "Statement" elements, each with 5 children: "Subject", "Predicate", "Object", "StatementUri", "Scope" which represent the respective components of the RDF statement.
for example:
frdf:complete("http://spam.com", frdf:any(), frdf:any())
would return all statements with "http://spam.com" as the subjects. """ repo = context.processor._repository
model = repo.getModel() subj = not not subj and Conversions.StringValue(subj) or None pred = not not pred and Conversions.StringValue(pred) or None if obj is not None: obj = Conversions.StringValue(obj) proc = context.processor #print repr(subj), repr(pred), repr(obj) stmts = model.complete(subj, pred, obj) #print stmts proc.pushResultTree(context.currentInstruction.baseUri) try: _serializeStatements(stmts, proc.writers[-1]) finally: rtf = proc.popResult() return rtf.childNodes
def RdfAny(context): return None
def RdfContains(context, subj, pred, obj=None): """ Runs a conatins on the repository model, which matches patterns among the triples in the model. You provide whichever of the subject, predicate and object you'd like to match, and use the frdf:any() function as a wildcard. The return value is a boolean indicating whether or not a matching statement exists. """ # could be None from frdf:any() subj = subj and Conversions.StringValue(subj) pred = pred and Conversions.StringValue(pred) obj = obj and Conversions.StringValue(obj)
model = context.processor._repository.getModel() result = model.containsPattern(subj, pred, obj) return Conversions.BooleanValue(result)
def RdfVersaQueryFunction(context, query, scope=None): """Perform a Versa Query as a function, return a node-set representing the outside list."""
processor = context.processor
query = Conversions.StringValue(query) if scope: scope = Conversions.StringValue(scope) try: print "Compiling Versa: %r" % query exp = Versa.Compile(query) except: import traceback st = cStringIO.StringIO() traceback.print_exc(file=st) processor.xslMessage(st.getvalue()) raise
repo = processor._repository var_bindings = context.processor.extensionParams.get(VB_PARAM, {}) con = Versa.CreateContext(model=repo.getModel(), nsMapping=context.processorNss, varBindings=var_bindings, scope=scope) try: print "Evaluating Query" now = time.time() results = exp.evaluate(con) print "Time to evaluate: ", time.time() - now except: #Error import traceback st = cStringIO.StringIO() traceback.print_exc(file=st) processor.xslMessage(st.getvalue()) raise #print results
# Use an RTF to create a node-set processor.pushResultTree(context.currentInstruction.baseUri) try: WriteVersaResults(processor, results) finally: rtf = processor.popResult() return [rtf]
# -- extension elements -----------------------------------------------
class RdfBaseElement(XsltElement):
content = ContentInfo.Empty
#FIXME: Make type an enum, not any AVT legalAttrs = { 's' : AttributeInfo.Expression(description='The subject'), 'p' : AttributeInfo.Expression(description='The predicate'), 'o' : AttributeInfo.Expression(description='The object'), 'type' : AttributeInfo.Expression( description='The type (or xml:lang) of the object'), 'scope' : AttributeInfo.StringExpression( description='The scope of the statement'), 'stmt-uri' : AttributeInfo.StringExpression( description='The statement uri'), }
def getStatementInfo(self, context): if self._s: subj = self._s.evaluate(context) if isinstance(subj, list): subjects = map(lambda x: Conversions.StringValue(x), subj) else: subjects = [Conversions.StringValue(subj)] else: subjects = [None]
if self._p: pred = self._p.evaluate(context) if isinstance(pred, list): predicates = map(lambda x: Conversions.StringValue(x), pred) else: predicates = [Conversions.StringValue(pred)] else: predicates = [None]
if self._o: obj = self._o.evaluate(context) if isinstance(obj, list): objects = map(lambda x: Conversions.StringValue(x), obj) else: objects = [Conversions.StringValue(obj)] else: objects = [None]
if self._type: type_ = OBJ_TYPE_LOOKUP[self._type.evaluate(context)] else: type_ = OBJECT_TYPE_UNKNOWN
if self._stmt_uri: statementUri = Conversions.StringValue(self._stmt_uri.evaluate(context)) else: statementUri = ''
if self._scope: scope = Conversions.StringValue(self._scope.evaluate(context)) else: scope = '' return (subjects, predicates, objects, scope, statementUri, type_)
class RdfPatternBaseElement(RdfBaseElement):
legalAttrs = RdfBaseElement.legalAttrs.copy() legalAttrs.update({ 's-flags' : AttributeInfo.ChoiceAvt( ['normal', 'regex', 'ignore-case', 'regex-ignore-case'], default='normal', description=('Indicates the method for matching the value of' ' the subject argument')), 'p-flags' : AttributeInfo.ChoiceAvt( ['normal', 'regex', 'ignore-case', 'regex-ignore-case'], default='normal', description=('Indicates the method for matching the value of' ' the predicate argument')), 'o-flags' : AttributeInfo.ChoiceAvt( ['normal', 'regex', 'ignore-case', 'regex-ignore-case'], default='normal', description=('Indicates the method for matching the value of' ' the object argument')), 'scope-flags' : AttributeInfo.ChoiceAvt( ['normal', 'regex', 'ignore-case', 'regex-ignore-case'], default='normal', description=('Indicates the method for matching the value of' ' the scope argument')), 'stmt-uri-flags' : AttributeInfo.ChoiceAvt( ['normal', 'regex', 'ignore-case', 'regex-ignore-case'], default='normal', description=('Indicates the method for matching the value of' ' the statement URI argument')), })
def getRdfPatternCriteria(self, context):
(subj, pred, obj, scope, stmtUri, t) = self.getStatementInfo(context)
criteria = [] for s in subj: for p in pred: for o in obj: criteria.append((s, p, o))
flag_dict = {} if stmtUri: flag_dict['statementUri'] = stmtUri if scope: flag_dict['scope'] = scope
flag_val = {'normal' : Model.NORMAL, 'regex' : Model.REGEX, 'ignore-case' : Model.IGNORE_CASE, 'regex-ignore-case' : Model.REGEX | Model.IGNORE_CASE, } flags = self._s_flags.evaluate(context) flag_dict['subjectFlags'] = flag_val[flags]
flags = self._p_flags.evaluate(context) flag_dict['predicateFlags'] = flag_val[flags]
flags = self._o_flags.evaluate(context) flag_dict['objectFlags'] = flag_val[flags]
flags = self._scope_flags.evaluate(context) flag_dict['scopeFlags'] = flag_val[flags]
flags = self._stmt_uri_flags.evaluate(context) flag_dict['statementUriFlags'] = flag_val[flags]
return criteria, flag_dict
class RdfCompleteElement(RdfPatternBaseElement):
def instantiate(self, context, processor): context.setProcessState(self)
criteria, flag_dict = self.getRdfPatternCriteria(context) #print criteria, flag_dict
repo = context.processor._repository model = repo.getModel()
stmts = [] for c in criteria: subset = model.complete(*c, **flag_dict) stmts.extend(subset) #print stmts
_serializeStatements(stmts, processor.writers[-1])
return
class RdfRemoveElement(RdfPatternBaseElement): """ Removes statements to the repository model. The subject, predicates and objects can be strings or node sets node sets are converted to a list of strings by taking the string value of each item. String values are treated a list with one string. The cartesian product of statements that come from the list of subject, predicate and object strings and that match the given scope and statement URI are all removed from the model. """
def instantiate(self, context, processor): context.setProcessState(self)
criteria, flag_dict = self.getRdfPatternCriteria(context)
repo = context.processor._repository model = repo.getModel()
for c in criteria: model.removePattern(*c, **flag_dict)
return
class RdfAddElement(RdfBaseElement): """ Adds statements to the repository model. The subject, predicates and objects can be strings or node sets node sets are converted to a list of strings by taking the string value of each item. String values are treated a list with one string. The cartesian product of statements that come from the list of subject, predicate and object strings are all added to the model using the given scope and statement URI. """
def instantiate(self, context, processor): context.setProcessState(self)
subj, pred, obj, scope, stmtUri, typ = self.getStatementInfo(context)
stmts = [] for s in subj: for p in pred: for o in obj: stmts.append(Statement(s, p, o, stmtUri, scope, objectType=typ))
repo = context.processor._repository model = repo.getModel() model.add(stmts) return
class RdfVersaAssignElement(XsltElement): """ Execute a Versa query and assign the result to a Versa variable, for use in further queries. The variable becomes bound in the Versa context for the current stylesheet invokation, and thus will be defined on all Versa queries performed subsequently, through <frdf:versa-query> or <frdf:versa-assign>.
The content of the frdf:versa-assign element is a template for the extra namespace mappings for the query (see /ftss/dashboard/NsMappings.xml for an example of the xml format).
For example, if you have the following:
<frdf:versa-assign query="type(rdfs:Class)" name="classes"/>
You can later execute
<frdf:versa-query query="$classes - rdfs:label -> *"/> """
content = ContentInfo.Template legalAttrs = { 'query' : AttributeInfo.StringAvt( required=1, description='The Versa query string'), 'name' : AttributeInfo.QNameAvt( required=1, description=('Name of the Versa variable to which to' ' assign results of this query')), 'scope' : AttributeInfo.StringAvt( description='Narrow the versa query to the given scope'), }
def instantiate(self, context, processor): context.setProcessState(self) queryString = self._query.evaluate(context) ename = self._name.evaluate(context) scope = self._scope.evaluate(context)
print "Compiling Versa query: %s" % queryString try: exp = Versa.Compile(queryString) except: #Error import traceback st = cStringIO.StringIO() traceback.print_exc(file=st) processor.xslMessage(st.getvalue()) raise
if self.children: processor.pushResultTree(self.baseUri) self.processChildren(context, processor) node = processor.popResult() newMappings = ParseNsMappings(node, self.namespaces) self.namespaces.update(newMappings)
repo = context.processor._repository if not context.processor.extensionParams.has_key(VB_PARAM): context.processor.extensionParams[VB_PARAM] = {} var_bindings = context.processor.extensionParams[VB_PARAM] con = Versa.CreateContext(model=repo.getModel(), nsMapping=self.namespaces, varBindings=var_bindings, scope=scope)
print "Evaluating Query for Assignment" try: now = time.time() results = exp.evaluate(con) print "Time to evaluate: ", time.time() - now except: #Error import traceback st = cStringIO.StringIO() traceback.print_exc(file=st) processor.xslMessage(st.getvalue()) raise print "Finished evaluating query"
context.processor.extensionParams[VB_PARAM][ename] = results return
def _writeResults(self, processor, results): WriteVersaResults(processor, results) return
class RdfVersaQueryElement(XsltElement): """ Execute a Versa query and put the results in serialized XML form to the XSLT processor output.
The content of the frdf:versa-query element is a template for the extra namespace mappings for the query (see /ftss/dashboard/NsMappings.xml for an example of the xml format).
The form of the Versa output is as documented in http://rdfinference.org/versa-xml. """ content = ContentInfo.Template legalAttrs = { 'query' : AttributeInfo.StringAvt( required=1, description='The query string'), 'scope' : AttributeInfo.StringAvt( description='Narrow the versa query to the given scope'), }
def instantiate(self, context, processor): context.setProcessState(self)
queryString = self._query.evaluate(context) scope = self._scope.evaluate(context) repo = context.processor._repository print "Compiling Versa: %s" % queryString try: exp = Versa.Compile(queryString) except: #Error import traceback st = cStringIO.StringIO() traceback.print_exc(file=st) processor.xslMessage(st.getvalue()) raise
if self.children: processor.pushResultTree(self.baseUri) self.processChildren(context, processor) node = processor.popResult() newMappings = ParseNsMappings(node, self.namespaces) self.namespaces.update(newMappings)
#add dashboard namemapping mappings #newMappings = ParseNsMappings(repo.fetchResource('/ftss/dashboard/NsMappings.xml').getContent(), {}) #self.namespaces.update(newMappings)
var_bindings = context.processor.extensionParams.get(VB_PARAM, {}) con = Versa.CreateContext(model=repo.getModel(), nsMapping=self.namespaces, varBindings=var_bindings, scope=scope)
print "Evaluating Query" try: now = time.time() results = exp.evaluate(con) print "Time to evaluate: ", time.time() - now except: #Error import traceback st = cStringIO.StringIO() traceback.print_exc(file=st) processor.xslMessage(st.getvalue()) raise print "Finished evaluating query"
self._writeResults(processor, results) return
def _writeResults(self, processor, results): WriteVersaResults(processor, results) return
from Ft.Rdf import Model DUMMY_MODEL = Model.Model(None) from Ft.Rdf.Parsers.Versa import DataTypes def WriteVersaResults(processor, results): # By giving a namespace, the elements are created in the null-namespace writer = processor.writers[-1] if DataTypes.IsList(results): writer.startElement(u'List') for result in results: WriteVersaResults(processor, result) writer.endElement(u'List') elif DataTypes.IsSet(results): writer.startElement(u'Set') for result in DataTypes.ToList(results): WriteVersaResults(processor, result) writer.endElement(u'Set') elif DataTypes.IsResource(results): if DUMMY_MODEL.isBnodeLabel(str(results)): elem = u'BlankNode' else: elem = u'Resource' writer.startElement(elem) writer.text(unicode(results)) writer.endElement(elem) elif DataTypes.IsString(results): writer.startElement(u'String') writer.text(unicode(results)) writer.endElement(u'String') elif DataTypes.IsNumber(results): writer.startElement(u'Number') writer.text(unicode(results)) writer.endElement(u'Number') elif DataTypes.IsBoolean(results): writer.startElement(u'Boolean') writer.text(unicode(results)) writer.endElement(u'Boolean') else: raise TypeError('Unknown Versa result type %s' % type(results))
class DeserializeAndAddElement(XsltElement): """ Deserializes an RDF document into statements, and adds them to a scope
The content of the frdf:deserialize-and-add element is a template for the RDF/XML source from which the statements are parsed. """
content = ContentInfo.Template
legalAttrs = { 'path' : AttributeInfo.UriReferenceAvt( required=1, description=('The path of the RDF resource in the' ' repository')), 'base-path' : AttributeInfo.UriReferenceAvt( description='The base to use for relative paths'), }
def instantiate(self, context, processor): context.setProcessState(self)
path = self._path.evaluate(context) basePath = self._base_path.evaluate(context)
stream = cStringIO.StringIO() params = processor.writers[-1]._outputParams.clone() params.method = (None, u'xml') processor.addHandler(params, stream) self.processChildren(context, processor) processor.removeHandler()
enc = processor.writers[-1]._outputParams.encoding or 'utf-8' text = unicode(stream.getvalue(), enc)
print "Deserialize RDF" #print "Scope: %s" % scope
if not basePath: basePath = processor.extensionParams.get((RESERVED_NAMESPACE, 'path'), '/') base = FtssXsltBase.FetchBaseObject(processor, basePath) obj = base.fetchResource(path) obj.deserializeAndAdd(text) return
class DeserializeAndRemoveElement(XsltElement): """ Deserializes an RDF document into statements, and removes any statements in the given scope with matching triple values.
The content of the frdf:deserialize-and-remove element is a template for the RDF/XML source from which the statements are parsed. """
content = ContentInfo.Template
legalAttrs = { 'path' : AttributeInfo.UriReferenceAvt( required=1, description=('The path of the RDF resource in the' ' repository')), 'base-path' : AttributeInfo.UriReferenceAvt( description='The base to use for relative paths'), }
def instantiate(self, context, processor): context.setProcessState(self)
path = self._path.evaluate(context) basePath = self._base_path.evaluate(context)
stream = cStringIO.StringIO() params = processor.writers[-1]._outputParams.clone() params.method = (None, u'xml') processor.addHandler(params, stream) self.processChildren(context, processor) processor.removeHandler()
enc = processor.writers[-1]._outputParams.encoding or 'utf8' text = unicode(stream.getvalue(), enc)
print "Deserialize RDF" print "PATH: %s" % path
if not basePath: basePath = processor.extensionParams.get((RESERVED_NAMESPACE, 'path'), '/') base = FtssXsltBase.FetchBaseObject(processor, basePath) obj = base.fetchResource(path) obj.deserializeAndRemove(text) return
class RdfVisualizeElement(XsltElement): """ This extension element uses GraphViz to generate a diagram of a given resource, a set of resources in a particular scope, or of the entire system model. It adds an HTML imagemap (a chunk of text that can be placed in a MAP element) to the result tree, and has the side effect of generating in the repository an SVG or JPEG image resource representing the corresponding diagram.
output-path specifies the repo path of the diagram to generate. If the resource exists already, its content will be overwritten.
If provided, resourcePath must to refer to either a specific resource in the system model or an RDF document in the repository. If it refers to an RDF document, then all RDF statements with that document's URI as their scope will be graphed. If the resourcePath refers to any other kind of resource, then only statements about it will be graphed. If no resourcePath is specified, then the entire system model will be graphed.
The image map text placed in the result tree uses the uriFormat string to generate (by appying the uris of all resources against this string) the links for all resources in the image. imagemap information is generated and returned only if the uriFormat string is specified.
Most RDF graphs are width heavy and the visualizer can be told to rotate the image 90 degrees which can be helpful far larger graphs (which could great widths). This is specified by the rotate attribute. If present, the graph is rotated, otherwise it isn't.
The max-arcs attribute specifies the maximum number of arcs. If the graph requires more arcs, then nothing will be generated at all.
This element can also take a versa query result as its child nodes. In this case, all the resources in the result are graphed and resource-path is ignored. """
content = ContentInfo.Template legalAttrs = { 'graph-vis' : AttributeInfo.UriReferenceAvt( required=1, description=("The directory in which 'graphViz'" " executable is located.")), 'output-path' : AttributeInfo.UriReferenceAvt( required=1, description=('The name of the diagram resource to be' ' created')), 'svg' : AttributeInfo.YesNoAvt( default='no', description='generate an svg diagram?'), 'scoped' : AttributeInfo.YesNoAvt( default="no", description=('Whether to render RDF documents as' 'scoped mini-graphs')), 'map-name' : AttributeInfo.IdAvt( description='The ID to use for the image-map'), 'uri-format' : AttributeInfo.StringAvt( description=('A format string to use for the image-map links.' ' Every uri in the graph is applied against this' ' string.')), 'max-arcs' : AttributeInfo.NumberAvt( default=300, description='The maximum number of arcs to allow'), 'rotate' : AttributeInfo.YesNoAvt( default='no', description=('Whether to rotate the graph image' ' by 90 degrees.')), 'namespaces' : AttributeInfo.StringAvt( default='/ftss/dashboard/NsMappings.xml', description=('The location in repository of a namespace mapping' ' document to use for retrieving additional' ' namespaces to truncate predicate names with')), 'resourcePath' : AttributeInfo.StringAvt( description='A resource in the repository to graph'), }
def instantiate(self, context, processor): context.setProcessState(self)
repo = context.processor._repository model = repo.getModel()
graphViz = self._graph_vis.evaluate(context) svg = self._svg.evaluate(context)
try: resourcePath = self._resourcePath.evaluate(context) except: resourcePath = None
namespaces = self._namespaces.evaluate(context) if not strip(graphViz): raise ValueError("graphViz executable path not given: ", graphViz) outputPath = self._output_path.evaluate(context)
scpd = self._scoped.evaluate(context) print "scpd: ", scpd
mapName = self._map_name.evaluate(context) uriFormat = self._uri_format.evaluate(context) rotate = self._rotate.evaluate(context) maxArcs = self._max_arcs.evaluate(context)
from Ft.Rdf import Triclops engine = Triclops.RDFGraphVizEngine(graphViz, mapName, uriFormat, maxArcs) nsMappings = repo.fetchResource('/ftss/dashboard/NsMappings.xml') newMapDict = ParseNsMappings(nsMappings.getContent(), self.namespaces) if self.children: # graph from versa results processor.pushResultTree(self.baseUri) self.processChildren(context, processor) node = processor.popResult() mapInfo = engine.resourceViewer(model, scoped=scpd, rotate=rotate, nsDict=newMapDict, resultsNode=node)
elif resourcePath: mapInfo = engine.resourceViewer(model, scoped=scpd, rotate=rotate, resourceUri=resourcePath, nsDict=newMapDict )
if svg: fsvg = open(engine.outputSvg) svgcontent = fsvg.read() print "creating a graphviz svg diagram file of size: ", len(svgcontent) svgPath = outputPath[:outputPath.find('.')] + '.svg' if repo.hasResource(svgPath): repo.fetchResource(svgPath).setContent(svgcontent) else: repo.createRawFile(svgPath, 'image/svg-xml', svgcontent) fsvg.close()
else: fd = open(engine.outputJpeg) content = fd.read() print "creating a graphviz image file of size: ", len(content) if repo.hasResource(outputPath): repo.fetchResource(outputPath).setContent(content) else: repo.createRawFile(outputPath, 'image/jpeg', content) fd.close()
if uriFormat: if svg: writer = processor.writers[-1] writer.startElement(u'embed') src = u'%s?time=%s'%(outputPath, FtTime.FromPythonTime()) writer.attribute(u'src', src) writer.attribute(u'width', unicode(engine.svgWidth)) writer.attribute(u'height', unicode(engine.svgHeight)) writer.endElement(u'embed') else: processor.writers[-1].text(mapInfo, escapeOutput=0)
return
ExtFunctions = { (RDF_NS, 'any'): RdfAny, (RDF_NS, 'complete'): RdfComplete, (RDF_NS, 'contains'): RdfContains, (RDF_NS, 'versa-query'): RdfVersaQueryFunction, }
ExtElements = { (RDF_NS, 'remove'): RdfRemoveElement, (RDF_NS, 'complete'): RdfCompleteElement, (RDF_NS, 'add'): RdfAddElement, (RDF_NS, 'versa-query'): RdfVersaQueryElement, (RDF_NS, 'versa-assign'): RdfVersaAssignElement, (RDF_NS, 'visualize'): RdfVisualizeElement, (RDF_NS, 'deserialize-and-add'): DeserializeAndAddElement, (RDF_NS, 'deserialize-and-remove'): DeserializeAndRemoveElement, }
|