Пример #1
0
    def _neighbours_rdf(self, mimetype):
        query = '''
                 SELECT * WHERE {
                    <%(uri)s>  ?p ?o .
                 }
         ''' % {
            'uri': self.uri
        }
        g = Graph()
        g.bind('prov', Namespace('http://www.w3.org/ns/prov#'))
        for r in _database.query(query)['results']['bindings']:
            if r['o']['type'] == 'literal':
                g.add((URIRef(self.uri), URIRef(r['p']['value']),
                       Literal(r['o']['value'])))
            else:
                g.add((URIRef(self.uri), URIRef(r['p']['value']),
                       URIRef(r['o']['value'])))

        query2 = '''
                 SELECT * WHERE {
                    ?s ?p <%(uri)s> .
                 }
         ''' % {
            'uri': self.uri
        }
        for r in _database.query(query2)['results']['bindings']:
            g.add((URIRef(r['s']['value']), URIRef(r['p']['value']),
                   URIRef(self.uri)))

        return Response(
            g.serialize(format=LDAPI.get_rdf_parser_for_mimetype(mimetype)),
            status=200,
            mimetype=mimetype)
Пример #2
0
def lodge_reportingsystem():
    """Insert a ReportingSystem into the provenance _database"""
    # only accept RDF documents
    acceptable_mimes = LDAPI.get_rdf_mimetypes_list()
    ct = request.content_type
    if ct not in acceptable_mimes:
        return api_functions.client_error_response(
            'The ReportingSystem posted is not encoded with a valid RDF Content-Type. Must be one of: '
            + ', '.join(acceptable_mimes) + '.')

    # validate ReportingSystem
    rs = class_reportingsystems.IncomingReportingSystem(request)
    if not rs.valid():
        return api_functions.client_error_response(
            'The ReportingSystem posted is not valid for the following reasons: '
            + ', '.join(rs.error_messages) + '.')

    # get the ReportingSystem's URI
    rs.determine_uri()

    rs.generate_named_graph_metadata()

    # store the ReportingSystem
    if not rs.stored():
        return api_functions.server_error_response(
            'ReportingSystem posted is valid but cannot be stored for the following reasons: '
            + ', '.join(rs.error_messages) + '.')

    # reply to sender
    return Response(rs.uri, status=201, mimetype='text/plain')
Пример #3
0
 def render(self, view, mimetype):
     if view == 'neighbours':
         if mimetype in LDAPI.get_rdf_mimetypes_list():
             return self._neighbours_rdf(mimetype)
         elif mimetype == 'text/html':
             self._get_details()
             return self._neighbours_html()
     elif view == 'prov':
         if mimetype in LDAPI.get_rdf_mimetypes_list():
             return Response(self._prov_rdf().serialize(
                 format=LDAPI.get_rdf_parser_for_mimetype(mimetype)),
                             status=200,
                             mimetype=mimetype)
         elif mimetype == 'text/html':
             self._get_details()
             return self._prov_html()
Пример #4
0
def lodge_agent():
    """Insert an Agent into the provenance _database"""
    # only accept RDF documents
    acceptable_mimes = LDAPI.get_rdf_mimetypes_list()
    ct = request.content_type
    if ct not in acceptable_mimes:
        return api_functions.client_error_response(
            'The Agent posted is not encoded with a valid RDF Content-Type. Must be one of: '
            + ', '.join(acceptable_mimes) + '.')

    # validate Agent
    sr = class_agents.IncomingAgent(request.data, request.content_type)
    if not sr.valid():
        return api_functions.client_error_response(
            'The Agent posted is not valid for the following reasons: ' +
            ', '.join(sr.error_messages) + '.')

    # get the Agent's URI
    sr.determine_uri()

    # store the Agent
    if not sr.stored():
        return api_functions.server_error_response(
            'The Agent posted is valid but cannot be stored for the following reasons: '
            + ', '.join(sr.error_messages) + '.')

    # reply to sender
    return Response(sr.uri, status=201, mimetype='text/plain')
Пример #5
0
 def render(self, view, mimetype):
     if mimetype == 'text/html':
         self._get_details()
         return self._html()
     else:
         self._get_details()
         return Response(self._rdf().serialize(
             format=LDAPI.get_rdf_parser_for_mimetype(mimetype)),
                         mimetype=mimetype)
Пример #6
0
def instance():
    """
    Responds with one of a number of HTTP responses according to an allowed model and format of this object in the graph

    :return: and HTTP response
    """
    # must have the URI of an object in the graph
    instance_uri = request.args.get('_uri')
    try:
        g = get_class_object(instance_uri)

        if not g:
            return client_error_response(
                'No URI of an object in the provenance database was supplied. '
                'Expecting a query string argument \'_uri\'.')
    except ConnectionError:
        return render_template('error_db_connection.html'), 500

    # the URI is of something in the graph so now we validate the requested model and format
    # find the class of the URI
    for s, p, o in g:
        if str(p) == 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type':
            # validate this request's model and format
            class_uri = str(o)
            views_formats = objects_functions.get_classes_views_formats().get(
                class_uri)
            try:
                view, mime_format = LDAPI.get_valid_view_and_format(
                    request.args.get('_view'), request.args.get('_format'),
                    views_formats)

                # if alternates model, return this info from file
                if view == 'alternates':
                    instance_uri_encoded = urllib.parse.quote_plus(
                        request.args.get('_uri'))
                    class_uri_encoded = urllib.parse.quote_plus(class_uri)
                    del views_formats['renderer']
                    return api_functions.render_alternates_view(
                        class_uri, class_uri_encoded, instance_uri,
                        instance_uri_encoded, views_formats, mime_format)
                else:
                    # chooses a class to render this instance based on the specified renderer in
                    # classes_views_formats.json
                    # no need for further validation as instance_uri, model & format are already validated
                    renderer = getattr(__import__('model'),
                                       views_formats['renderer'])
                    endpoints = {
                        'instance': url_for('.instance'),
                        'sparql': url_for('api.sparql')
                    }
                    return renderer(instance_uri,
                                    endpoints).render(view, mime_format)

            except LdapiParameterError as e:
                return client_error_response(e)
Пример #7
0
 def render(self, view, mimetype):
     if view == 'reg':
         # is an RDF format requested?
         if mimetype in LDAPI.get_rdf_mimetypes_list():
             # it is an RDF format so make the graph for serialization
             self._make_dpr_graph(view)
             rdflib_format = LDAPI.get_rdf_parser_for_mimetype(mimetype)
             return Response(self.g.serialize(format=rdflib_format),
                             status=200,
                             mimetype=mimetype)
         elif mimetype == 'text/html':
             return render_template(
                 'class_register.html',
                 class_name=self.request.args.get('_uri'),
                 register=self.register)
     else:
         return Response(
             'The requested model model is not valid for this class',
             status=400,
             mimetype='text/plain')
Пример #8
0
    def _convert_proms_pingback_to_rdf(self):
        PROMS = Namespace('http://promsns.org/def/proms#')
        self.graph.bind('proms', PROMS)

        # type this pingback specifically
        self.graph.add(
            (URIRef(self.named_graph_uri), RDF.type, PROMS.PromsPingback))

        # convert the data to RDF (just de-serialise it)
        self.graph += Graph().parse(data=self.request.data,
                                    format=LDAPI.get_rdf_parser_for_mimetype(
                                        self.request.mimetype))
Пример #9
0
 def render(self, view, mimetype):
     if view == 'neighbours':
         # no work to be done as we have already loaded the triples
         if mimetype in LDAPI.get_rdf_mimetypes_list():
             return self._neighbours_rdf(mimetype)
         elif mimetype == 'text/html':
             self._get_details()
             return self._neighbours_html()
     elif view == 'prov':
         if mimetype in LDAPI.get_rdf_mimetypes_list():
             return Response(self._prov_rdf().serialize(
                 format=LDAPI.get_rdf_parser_for_mimetype(mimetype)),
                             status=200,
                             mimetype=mimetype)
         # TODO: pre-render the viz.js image and serve static image
         # elif mimetype == 'image/svg':
         #     return Response(
         #
         #     )
         elif mimetype == 'text/html':
             self._get_details()
             return self._prov_html()
Пример #10
0
def register():
    """
    Responds with a Register model response for classes listed in the graph

    Supported classes statically loaded from classes_views_formats.json
    In the future, we will dynamically work out which classes are supported.

    :param class_name: the name of a class of object in the graph db
    :return: an HTTP message based on a particular model and format of the class
    """

    # check for a class URI
    uri = request.args.get('_uri')
    # ensure the class URI is one of the classes in the views_formats
    class_uris = objects_functions.get_class_uris()
    if uri not in class_uris:
        return client_error_response(
            'No URI of a class in the provenance database was supplied. Expecting a query string argument \'_uri\' '
            'equal to one of the following: ' +
            ', '.join(class_uris)
        )

    # validate this request's model and format
    class_uri = 'http://purl.org/linked-data/registry#Register'
    views_formats = objects_functions.get_classes_views_formats().get(class_uri)
    try:
        view, mime_format = LDAPI.get_valid_view_and_format(
            request.args.get('_view'),
            request.args.get('_format'),
            views_formats
        )
    except LdapiParameterError as e:
        return client_error_response(e)

    # if alternates model, return this info from file
    if view == 'alternates':
        del views_formats['renderer']
        return api_functions.render_alternates_view(uri, uriparse.parse.quote_plus(uri), None, None, views_formats, mime_format)

    # get the register of this class of thing from the provenance database
    try:
        class_register = get_class_register(uri)
    except ConnectionError:
        return render_template('error_db_connection.html'), 500

    # since everything's valid, use the Renderer to return a response
    endpoints = {
        'instance': url_for('.instance'),
        'sparql': url_for('api.sparql')
    }
    return model.RegisterRenderer(request, uri, endpoints, class_register).render(view, mime_format)
Пример #11
0
def lodge_report():
    """Insert a Report into the provenance database

    This function should be edited if any custom actions are to be undertaken by PROMS on reciept of a Report, such as
    Ppingbacks
    """
    # only accept RDF documents
    acceptable_mimes = LDAPI.get_rdf_mimetypes_list()
    ct = request.content_type
    if ct not in acceptable_mimes:
        return api_functions.client_error_response(
            'The Report posted is not encoded with a valid RDF Content-Type. Must be one of: '
            + ', '.join(acceptable_mimes) + '.')

    # validate Report, using the controller validator class, IncomingReport
    r = class_reports.IncomingReport(request)
    if not r.valid():
        return api_functions.client_error_response(
            'The Report posted is not valid for the following reasons: ' +
            ', '.join(r.error_messages) + '.')

    # generate Named Graph metadata
    r.determine_uri()
    r.generate_named_graph_metadata()

    # store the Report
    # since it is valid and NG metadata has been built, we can store it
    if not r.stored():
        return api_functions.server_error_response(
            'Report posted is valid but cannot be stored for the following reasons: '
            + ', '.join(r.error_messages) + '.')

    # Custom Actions
    # Only try to any custom actions, e.g. Pingbacks, if the module is turned on
    if conf.MODULES.get('pingbacks').get('valid'):
        # kick off any Pingbacks for this Report, as per chosen Pingbacks strategies
        # TODO: split Pingbacks process off into another thread
        from modules.pingbacks.engine import Engine
        e = Engine(r.graph, r.uri, url_for('modelx.instance'),
                   url_for('.sparql'))

    # reply to sender
    return r.uri, 201
Пример #12
0
    def valid(self):
        """Validates an incoming Pingback using direct _tests using the Pingbacks RuleSet"""
        # PROV Pingbacks can only be of mimtype text/uri-list
        if self.request.mimetype == 'text/uri-list':
            print(self.request.headers)
            conformant_pingback = ProvPingback(self.request)

            # ensure that this Pingback has the URI(s) of the Resource(s) it is for
            if self.request.args.get('resource_uri') is None:
                error_message = 'No resource URI is indicated for this pingback. Pingbacks sent to a PROMS Server ' \
                                'instance must be posted to ' \
                                'http://{POROMS_INTANCE}/function/lodge-pingback?resource_uri={RESOURCE_URI}'
                if self.error_messages is not None:
                    self.error_messages.append(error_message)
                else:
                    self.error_messages = [error_message]

                return False
            elif not LDAPI.is_a_uri(self.request.args.get('resource_uri')):
                error_message = 'The resource URI indicated for this pingback does not validate as a URI'
                if self.error_messages is not None:
                    self.error_messages.append(error_message)
                else:
                    self.error_messages = [error_message]

                return False

        # PROMS Pingbacks can only be of an RDF mimetype
        else:
            conformant_pingback = PromsPingback(self.request,
                                                self.pingback_endpoint)

        if not conformant_pingback.passed:
            self.error_messages = conformant_pingback.fail_reasons
            return False

        return True
Пример #13
0
def lodge_report():
    """Insert a Report into the provenance database"""
    # only accept RDF documents
    acceptable_mimes = LDAPI.get_rdf_mimetypes_list()
    ct = request.content_type
    if ct not in acceptable_mimes:
        return api_functions.client_error_response(
            'The Report posted is not encoded with a valid RDF Content-Type. Must be one of: '
            + ', '.join(acceptable_mimes) + '.')

    # validate Report
    r = class_reports.IncomingReport(request)
    print(str(r))
    if not r.valid():
        return api_functions.client_error_response(
            'The Report posted is not valid for the following reasons: ' +
            ', '.join(r.error_messages) + '.')

    # get the Report's URI
    r.determine_uri()

    r.generate_named_graph_metadata()

    # store the Report
    if not r.stored():
        return api_functions.server_error_response(
            'Report posted is valid but cannot be stored for the following reasons: '
            + ', '.join(r.error_messages) + '.')

    # kick off any Pingbacks for this Report, as per chosen Pingbacks strategies
    # TODO: split this off into another thread
    from modules.pingbacks.engine import Engine
    e = Engine(r.graph, r.uri, url_for('modelx.instance'), url_for('.sparql'))

    # reply to sender
    return r.uri, 201
Пример #14
0
def render_alternates_view(class_uri, class_uri_encoded, instance_uri,
                           instance_uri_encoded, views_formats, mimetype):
    """Renders an HTML table, a JSON object string or a serialised RDF representation of the alternate views of an
    object"""
    if mimetype == 'application/json':
        del views_formats[
            'renderer']  # the renderer used is not for public consumption!
        return Response(json.dumps(views_formats),
                        status=200,
                        mimetype='application/json')
    elif mimetype in LDAPI.get_rdf_mimetypes_list():
        g = Graph()
        LDAPI_O = Namespace('http://promsns.org/def/ldapi#')
        g.bind('ldapi', LDAPI_O)
        DCT = Namespace('http://purl.org/dc/terms/')
        g.bind('dct', DCT)

        class_uri_ref = URIRef(uriparse.unquote_plus(class_uri))

        if instance_uri:
            instance_uri_ref = URIRef(instance_uri)
            g.add((instance_uri_ref, RDF.type, class_uri_ref))
        else:
            g.add((class_uri_ref, RDF.type, LDAPI_O.ApiResource))

        # alternates model
        alternates_view = BNode()
        g.add((alternates_view, RDF.type, LDAPI_O.View))
        g.add((alternates_view, DCT.title,
               Literal('alternates', datatype=XSD.string)))
        g.add((class_uri_ref, LDAPI_O.view, alternates_view))

        # default model
        default_view = BNode()
        g.add((default_view, DCT.title, Literal('default',
                                                datatype=XSD.string)))
        g.add((class_uri_ref, LDAPI_O.defaultView, default_view))
        default_title = views_formats['default']

        # the ApiResource is incorrectly assigned to the class URI
        for view_name, formats in views_formats.items():
            if view_name == 'alternates':
                for f in formats:
                    g.add((alternates_view,
                           URIRef('http://purl.org/dc/terms/format'),
                           Literal(f, datatype=XSD.string)))
            elif view_name == 'default':
                pass
            elif view_name == 'renderer':
                pass
            else:
                x = BNode()
                if view_name == default_title:
                    g.add((default_view, RDF.type, x))
                g.add((class_uri_ref, LDAPI_O.view, x))
                g.add((x, DCT.title, Literal(view_name, datatype=XSD.string)))
                for f in formats:
                    g.add((x, URIRef('http://purl.org/dc/terms/format'),
                           Literal(f, datatype=XSD.string)))

        rdflib_format = [
            item[1] for item in LDAPI.MIMETYPES_PARSERS if item[0] == mimetype
        ][0]
        return Response(g.serialize(format=rdflib_format),
                        status=200,
                        mimetype=mimetype)
    else:  # HTML
        return render_template('alternates_view.html',
                               class_uri=class_uri,
                               class_uri_encoded=class_uri_encoded,
                               instance_uri=instance_uri,
                               instance_uri_encoded=instance_uri_encoded,
                               views_formats=views_formats)
Пример #15
0
class IncomingPingback(IncomingClass):
    acceptable_mimes = LDAPI.get_rdf_mimetypes_list() + ['text/uri-list']

    def __init__(self, request):
        IncomingClass.__init__(self, request)

        self.pingback_endpoint = request.url

        self.determine_uri()
        self._generate_named_graph_uri()

    def valid(self):
        """Validates an incoming Pingback using direct tests using the Pingbacks RuleSet"""
        # PROV Pingbacks can only be of mimtype text/uri-list
        if self.request.mimetype == 'text/uri-list':
            print((self.request.headers))
            conformant_pingback = ProvPingback(self.request)

            # ensure that this Pingback has the URI(s) of the Resource(s) it is for
            if self.request.args.get('resource_uri') is None:
                error_message = 'No resource URI is indicated for this pingback. Pingbacks sent to a PROMS Server ' \
                                'instance must be posted to ' \
                                'http://{POROMS_INTANCE}/function/lodge-pingback?resource_uri={RESOURCE_URI}'
                if self.error_messages is not None:
                    self.error_messages.append(error_message)
                else:
                    self.error_messages = [error_message]

                return False
            elif not LDAPI.is_a_uri(self.request.args.get('resource_uri')):
                error_message = 'The resource URI indicated for this pingback does not validate as a URI'
                if self.error_messages is not None:
                    self.error_messages.append(error_message)
                else:
                    self.error_messages = [error_message]

                return False

        # PROMS Pingbacks can only be of an RDF mimetype
        else:
            conformant_pingback = PromsPingback(self.request, self.pingback_endpoint)

        if not conformant_pingback.passed:
            self.error_messages = conformant_pingback.fail_reasons
            return False

        return True

    def determine_uri(self):
        pass  # no need for this!

    def _generate_named_graph_uri(self):
        self.named_graph_uri = settings.PINGBACK_NAMED_GRAPH_BASE_URI + str(uuid.uuid4())

    def convert_pingback_to_rdf(self):
        # the URI of the Named Graph for this Pingback must have been generated before doing this
        # so we can refer to the graph
        if self.named_graph_uri is not None:
            self.graph = Graph()
            # PROV Pingbacks can only be of mimtype text/uri-list
            if self.request.mimetype == 'text/uri-list':
                self._convert_prov_pingback_to_rdf()
            # PROMS Pingbacks can only be of an RDF mimetype
            else:
                self._convert_proms_pingback_to_rdf()
        else:
            raise Exception('The Incoming Pingback must have had a URI generated for it by PROMS Server before the data'
                            'for it is stored. The function determine_uri() generated the URI.')

    def _convert_prov_pingback_to_rdf(self):
        # every URI in the PROV-AQ message is treated as a provenance statement about the resource
        PROV = Namespace('http://www.w3.org/ns/prov#')
        self.graph.bind('prov', PROV)

        for uri_line in self.request.data.split('\n'):
            self.graph.add((
                URIRef(self.request.args.get('resource_uri')),
                PROV.has_provenance,
                URIRef(uri_line)
            ))

        # if there are Link headers about other resources, create DCT provenance indicators for them too
        if self.request.headers.get('Link'):
            for link_header in self.request.headers.get('Link').split(','):
                uri, rel, anchor = link_header.split(';')
                self.graph.add((
                    URIRef(uri.strip('<>')),
                    URIRef(rel.strip().replace('rel=', '').strip('"')),
                    URIRef(anchor.strip().replace('anchor=', '').strip('"'))
                ))

    def _convert_proms_pingback_to_rdf(self):
        PROMS = Namespace('http://promsns.org/def/proms#')
        self.graph.bind('proms', PROMS)

        # type this pingback specifically
        self.graph.add((
            URIRef(self.named_graph_uri),
            RDF.type,
            PROMS.PromsPingback
        ))

        # convert the data to RDF (just de-serialise it)
        self.graph += Graph().parse(
            data=self.request.data,
            format=LDAPI.get_rdf_parser_for_mimetype(self.request.mimetype)
        )

    def generate_named_graph_metadata(self):
        # add graph metadata, regardless of the type of Pingback
        PROV = Namespace('http://www.w3.org/ns/prov#')
        self.graph.bind('prov', PROV)

        PROMS = Namespace('http://promsns.org/def/proms#')
        self.graph.bind('proms', PROMS)

        DCT = Namespace('http://purl.org/dc/terms/')
        self.graph.bind('dct', DCT)

        # ... the date this Pingback was sent to this PROMS Server
        self.graph.add((
            URIRef(self.named_graph_uri),
            DCT.dateSubmitted,
            Literal(datetime.now().isoformat(), datatype=XSD.dateTime)
        ))
        # ... who contributed this Pingback
        self.graph.add((
            URIRef(self.named_graph_uri),
            DCT.contributor,
            URIRef(self.request.remote_addr)
        ))

        # TODO: add other useful metadata variables gleaned from the HTTP message headers

        # PROV Pingbacks can only be of mimtype text/uri-list
        if self.request.mimetype == 'text/uri-list':
            self._generate_prov_pingback_named_graph_metadata()
        else:
            self._generate_proms_pingback_named_graph_metadata()

    def _generate_prov_pingback_named_graph_metadata(self):
        PROMS = Namespace('http://promsns.org/def/proms#')
        self.graph.bind('proms', PROMS)

        # type this pingback specifically
        self.graph.add((
            URIRef(self.named_graph_uri),
            RDF.type,
            PROMS.ProvAqPingbackNamedGraph
        ))

    def _generate_proms_pingback_named_graph_metadata(self):
        PROMS = Namespace('http://promsns.org/def/proms#')
        self.graph.bind('proms', PROMS)

        # type this pingback specifically
        self.graph.add((
            URIRef(self.named_graph_uri),
            RDF.type,
            PROMS.PromsPingbackNamedGraph
        ))
Пример #16
0
def instance():
    """
    Responds with one of a number of HTTP responses according to an allowed model and format of this object in the graph

    :return: and HTTP response
    """
    # must have the URI of an object in the graph
    instance_uri = request.args.get('_uri')
    try:
        g = get_class_object(instance_uri)

        if not g:
            return client_error_response(
                'No URI of an object in the provenance _database was supplied. '
                'Expecting a query string argument \'_uri\'.')
    except ConnectionError:
        return render_template('error_db_connection.html'), 500

    # the URI is of something in the graph so now we validate the requested model and format
    # find the class of the URI
    q = '''
    SELECT DISTINCT ?o WHERE {
        <%s> a ?o .
    }
    ''' % instance_uri
    for r in g.query(q):
        class_uri = str(str(r['o']))
        # first preference render
        if class_uri in [
            'http://www.w3.org/ns/prov#Activity',
            'http://www.w3.org/ns/prov#Agent',
            'http://www.w3.org/ns/prov#Entity',
            'http://promsns.org/def/proms#BasicReport',
            'http://promsns.org/def/proms#ExternalReport',
            'http://promsns.org/def/proms#InternalReport',
            'http://promsns.org/def/proms#ReportingSystem'
        ]:
            # validate this request's model and format
            views_formats = objects_functions.get_classes_views_formats().get(class_uri)
            try:
                view, mime_format = LDAPI.get_valid_view_and_format(
                            request.args.get('_view'),
                            request.args.get('_format'),
                            views_formats
                        )

                # if alternates model, return this info from file
                if view == 'alternates':
                    instance_uri_encoded = uriparse.quote_plus(request.args.get('_uri'))
                    class_uri_encoded = uriparse.quote_plus(class_uri)
                    del views_formats['renderer']
                    return api_functions.render_alternates_view(
                        class_uri,
                        class_uri_encoded,
                        instance_uri,
                        instance_uri_encoded,
                        views_formats,
                        mime_format
                    )
                else:
                    # chooses a class to render this instance based on the specified renderer in
                    # classes_views_formats.json
                    # no need for further validation as instance_uri, model & format are already validated
                    renderer = getattr(__import__('model'), views_formats['renderer'])
                    endpoints = {
                        'instance': url_for('.instance'),
                        'sparql': url_for('api.sparql')
                    }
                    return renderer(
                        instance_uri,
                        endpoints
                    ).render(view, mime_format)

            except LdapiParameterError as e:
                return client_error_response(e)