METADATA_KEY_PROV_TYPE = "prov_type" METADATA_KEY_IDENTIFIER = "identifier" METADATA_KEY_NAMESPACES = "namespaces" METADATA_KEY_TYPE_MAP = "type_map" METADATA_KEY_IDENTIFIER_ORIGINAL = "identifier_original" class NoDataBaseAdapterException(ProvDbException):
    """
    Thrown, if no database adapter argument is passed to the api class.
    """
    pass class InvalidArgumentTypeException(ProvDbException):
    """
    Thrown, if an invalid argument is passed to any api method.
    """
    pass class InvalidProvRecordException(ProvDbException):
    """"
    Thrown, if an invalid record is passed to any api method.
    """
    pass class ParseException(ConverterException):
    """
    Thrown, if a given statement could not ne parsed.
    """
    pass class NotFoundException(DatabaseException):
    """
    Thrown, if record or relation could not be found in database.
    """
    pass def form_string(content):
    """
    Take a string or BufferedReader as argument and transform the string into a ProvDocument

    :param content: Takes a sting or BufferedReader
    :return: ProvDocument
    """
    if isinstance(content, ProvDocument):
        return content
    elif isinstance(content, BufferedReader):
        content = reduce(lambda total, a: total + a, content.readlines())

    if type(content) is six.binary_type:
        content_str = content[0:15].decode()
        if content_str.find("{") > -1:
            return ProvDocument.deserialize(content=content, format='json')
        if content_str.find('<?xml') > -1:
            return ProvDocument.deserialize(content=content, format='xml')
        elif content_str.find('document') > -1:
            return ProvDocument.deserialize(content=content, format='provn')

    raise ParseException("Unsupported input type {}".format(type(content))) def to_json(document=None):
    """
    Try to convert a ProvDocument into the json representation

    :param document:
    :type document: prov.model.ProvDocument
    :return: Json string of the document
    :rtype: str
    """
    if document is None:
        raise NoDocumentException()
    return document.serialize(format='json') def to_provn(document=None):
    """
    Try to convert a document into a provn representation

    :param document: Prov document to convert
    :type document: prov.model.ProvDocument
    :return: The prov-n str
    :rtype: str
    :raise: NoDocumentException
    """
    if document is None:
        raise NoDocumentException()
    return document.serialize(format='provn') def to_xml(document=None):
    """
    Try to convert a document into an xml string

    :param document: The ProvDocument to convert
    :param document: ProvDocument
    :return: The xml string
    :rtype: str
    """
    if document is None:
        raise NoDocumentException()
    return document.serialize(format='xml') def encode_json_representation(value):
    """
    Get the type of a value

    :param value:
    :return:
    """
    if isinstance(value, Literal):
        return literal_json_representation(value)
    elif isinstance(value, datetime):
        return {'type': 'xsd:dateTime'}
    elif isinstance(value, QualifiedName):
        # TODO Manage prefix in the whole structure consistently
        # TODO QName export
        return {'type': str(PROV_QUALIFIEDNAME)}
    elif isinstance(value, Identifier):
        return {'type': 'xsd:anyURI'}
    elif type(value) in LITERAL_XSDTYPE_MAP:
        return {'type': LITERAL_XSDTYPE_MAP[type(value)]}
    else:
        return None def add_namespaces_to_bundle(prov_bundle, metadata):
    """
    Add all namespaces in the metadata_dict to the provided bundle

    :param prov_bundle:
    :param metadata:
    :return: None
    """
    namespaces = dict()
    try:
        namespace_str = metadata[METADATA_KEY_NAMESPACES]
    except ValueError:
        SerializerException("No valid namespace provided, should be a string of a dict: {}".format(metadata))
        return

    if type(namespace_str) is str:
        io = StringIO(namespace_str)
        namespaces = json.load(io)
    elif type(namespace_str) is dict:
        namespaces = namespace_str
    elif type(namespace_str) is list:
        for entry in namespace_str:
            if type(entry) is str:
                io = StringIO(entry)
                namespaces.update(json.load(io))
            else:
                raise SerializerException(
                    "Namespaces metadata should returned as json string dict or list of json strings not as {}".format(
                        type(namespace_str)))

    else:
        raise SerializerException(
            "Namespaces metadata should returned as json string dict or list of json strings not as  {}".format(
                type(namespace_str)))

    for prefix, uri in namespaces.items():
        if prefix is not None and uri is not None:
            if prefix != 'default':
                prov_bundle.add_namespace(Namespace(prefix, uri))
            else:
                prov_bundle.set_default_namespace(uri)
        else:
            SerializerException("No valid namespace provided for the metadata: {}".format(metadata)) def create_prov_record(bundle, prov_type, prov_id, properties, type_map):
    """

    :param bundle:
    :param prov_type: valid prov type like prov:Entry as string
    :param prov_id: valid id as string like <namespace>:<name>
    :param properties: dict{attr_name:attr_value} dict with all properties (prov and additional)
    :param type_map: dict{attr_name:type_str} Contains the type information for each property (only if type is necessary)
    :return: ProvRecord
    """
    # Parse attributes
    if isinstance(properties, dict):
        properties_list = properties.items()
    elif isinstance(properties, list):
        properties_list = properties
    else:
        raise SerializerException(
            "Please provide properties as list[(key,value)] or dict your provided: {}".format(
                properties.__class__.__name__))

    attributes = dict()
    other_attributes = []
    # this is for the multiple-entity membership hack to come
    membership_extra_members = None
    for attr_name, values in properties_list:

        attr = (
            PROV_ATTRIBUTES_ID_MAP[attr_name]
            if attr_name in PROV_ATTRIBUTES_ID_MAP
            else bundle.valid_qualified_name(attr_name)
        )
        if attr in PROV_ATTRIBUTES:
            if isinstance(values, list):
                # only one value is allowed
                if len(values) > 1:
                    # unless it is the membership hack
                    if prov_type == PROV_MEMBERSHIP and \
                                    attr == PROV_ATTR_ENTITY:
                        # This is a membership relation with
                        # multiple entities
                        # HACK: create multiple membership
                        # relations, one x each entity

                        # Store all the extra entities
                        membership_extra_members = values[1:]
                        # Create the first membership relation as
                        # normal for the first entity
                        value = values[0]
                    else:
                        error_msg = (
                            'The prov package does not support PROV'
                            ' attributes having multiple values.'
                        )
                        logger.error(error_msg)
                        raise SerializerException(error_msg)
                else:
                    value = values[0]
            else:
                value = values

            if attr in PROV_ATTRIBUTE_QNAMES:
                value = (bundle.valid_qualified_name(value))
            elif isinstance(value, datetime):
                value = value
            else:
                parse_xsd_datetime(value)

            attributes[attr] = value
        else:
            value_type = None
            if type_map:
                value_type = type_map.get(attr_name)

            if isinstance(values, list):
                other_attributes.extend(
                    (
                        attr,
                        decode_json_representation(value, value_type, bundle)
                    )
                    for value in values
                )
            else:
                # single value
                other_attributes.append(
                    (
                        attr,
                        decode_json_representation(values, value_type, bundle)
                    )
                )
    record = bundle.new_record(
        prov_type, prov_id, attributes, other_attributes
    )
    # HACK: creating extra (unidentified) membership relations
    if membership_extra_members:
        collection = attributes[PROV_ATTR_COLLECTION]
        for member in membership_extra_members:
            bundle.membership(
                collection, bundle.valid_qualified_name(member)
            )
    return record PROV_ATTR_BASE_CLS = {
    PROV_ATTR_ENTITY: ProvEntity,
    PROV_ATTR_ACTIVITY: ProvActivity,
    PROV_ATTR_TRIGGER: ProvEntity,
    PROV_ATTR_INFORMED: ProvActivity,
    PROV_ATTR_INFORMANT: ProvActivity,
    PROV_ATTR_STARTER: ProvActivity,
    PROV_ATTR_ENDER: ProvActivity,
    PROV_ATTR_AGENT: ProvAgent,
    PROV_ATTR_PLAN: ProvEntity,
    PROV_ATTR_DELEGATE: ProvAgent,
    PROV_ATTR_RESPONSIBLE: ProvAgent,
    PROV_ATTR_GENERATED_ENTITY: ProvEntity,
    PROV_ATTR_USED_ENTITY: ProvEntity,
    PROV_ATTR_GENERATION: ProvElement,
    PROV_ATTR_USAGE: ProvElement,
    PROV_ATTR_SPECIFIC_ENTITY: ProvEntity,
    PROV_ATTR_GENERAL_ENTITY: ProvEntity,
    PROV_ATTR_ALTERNATE1: ProvEntity,
    PROV_ATTR_ALTERNATE2: ProvEntity,
    PROV_ATTR_BUNDLE:ProvElement,
    PROV_ATTR_INFLUENCEE: ProvElement,
    PROV_ATTR_INFLUENCER: ProvElement,
    PROV_ATTR_COLLECTION: ProvEntity
} def serialize_namespace(namespace: Namespace):
    prefix = namespace.prefix

    if prefix == "" or prefix is None:
        prefix = "default"
    return {str(prefix): str(namespace.uri)}
import json
import logging
import os
from collections import namedtuple
from io import StringIO
from uuid import uuid4
from prov.constants import PROV_ATTRIBUTES, PROV_MENTION, PROV_BUNDLE, PROV_LABEL, PROV_TYPE
from prov.model import ProvDocument, ProvEntity, ProvBundle, ProvRecord, ProvElement, ProvRelation, QualifiedName, \
    ProvAssociation, PROV_REC_CLS, ProvActivity, ProvAgent, PROV_AGENT,PROV_ENTITY,PROV_ACTIVITY, PROV_ATTR_AGENT,PROV_ATTR_ACTIVITY, PROV_ATTR_ENTITY,PROV_ATTR_BUNDLE
from provdbconnector.db_adapters.baseadapter import METADATA_KEY_PROV_TYPE, METADATA_KEY_IDENTIFIER, \
    METADATA_KEY_NAMESPACES, \
    METADATA_KEY_TYPE_MAP, METADATA_KEY_IDENTIFIER_ORIGINAL
from provdbconnector.exceptions.provapi import NoDataBaseAdapterException, InvalidArgumentTypeException, \
    InvalidProvRecordException
from provdbconnector.exceptions.utils import ParseException
from provdbconnector.exceptions.database import NotFoundException
from provdbconnector.utils.converter import form_string, to_json, to_provn, to_xml
from provdbconnector.utils.serializer import encode_json_representation, add_namespaces_to_bundle, create_prov_record, \
    PROV_ATTR_BASE_CLS, serialize_namespace

        bundle_entity = self.get_element(identifier)

        doc = ProvDocument()
        doc.add_record(bundle_entity)#Add bundle entity to document

        prov_bundle = doc.bundle(identifier=bundle_entity.identifier)

        # Include namespace uri into the identifier to support e.g. different default namespaces
        global_identifier = identifier.namespace.uri + identifier.localpart
        bundle_records = self._adapter.get_bundle_records(global_identifier)

        for record in bundle_records:
            self._parse_record(prov_bundle, record)

        return prov_bundle

    def save_bundle(self,prov_bundle):
        """
        Public method to save a bundle

        .. code:: python

            doc = ProvDocument()

            bundle = doc.bundle("ex:bundle1")
            # Save the bundle
            prov_db.save_bundle(bundle)

        :param prov_bundle:
        :type prov_bundle: prov.model.ProvBundle
        :return:
        """

        if not isinstance(prov_bundle, ProvBundle):
            raise InvalidArgumentTypeException()
        if isinstance(prov_bundle, ProvDocument):
            raise  InvalidArgumentTypeException()

        # create bundle entity
        bundle_record = ProvEntity(prov_bundle.document, identifier=prov_bundle.identifier, attributes={PROV_TYPE: PROV_BUNDLE})
        self.save_element(prov_element=bundle_record)

        return self._save_bundle_internal(prov_bundle)

    def _save_bundle_internal(self, prov_bundle):
        """
        Private method to create a bundle in the database

        :param prov_bundle: ProvBundle
        :type prov_bundle: prov.model.ProvBundle
        :return bundle_id: The bundle from the database adapter
        :rtype: str
        """
        if not isinstance(prov_bundle, ProvBundle):
            raise InvalidArgumentTypeException()

        bundle_id = str(uuid4())
        # create nodes
        for record in prov_bundle.get_records(ProvElement):
            self.save_element(prov_element=record, bundle_id=bundle_id)

        # create relations
        for relation in prov_bundle.get_records(ProvRelation):

            self.save_relation(relation, bundle_id)

        return bundle_id

    def save_relation(self, prov_relation, bundle_id=None):
        """
        Saves a relation between 2 nodes that are already in the database.

        .. code:: python

            doc = ProvDocument()

            activity    = doc.activity("ex:yourActivity")
            entity      = doc.entity("ex:yourEntity")
            wasGeneratedBy = entity.wasGeneratedBy("ex:yourAgent")

            # Save the elements
            rel_id = prov_db.save_relation(wasGeneratedBy)


        :param prov_relation: The ProvRelation instance
        :type prov_relation: ProvRelation
        :param bundle_id
        :type bundle_id: str
        :return: Relation id
        :rtype: str
        """

        if not isinstance(prov_relation, ProvRelation):
            raise InvalidArgumentTypeException(
                "prov_relation was {}, expected: {}".format(type(prov_relation), type(ProvRelation)))


        # get from and to node
        from_tuple, to_tuple = prov_relation.formal_attributes[:2]
        from_qualified_name = from_tuple[1]
        to_qualified_name = to_tuple[1]

        # if target or origin record is unknown, save node "Unknown"
        if from_qualified_name is None:
            uid = uuid4()
            from_qualified_name = prov_relation.bundle.valid_qualified_name("prov:Unknown-{}".format(uid))
            del uid

        if to_qualified_name is None:
            uid = uuid4()
            to_qualified_name = prov_relation.bundle.valid_qualified_name("prov:Unknown-{}".format(uid))
            del uid

        # Ensure that the from and to node exists
        relation_cls = PROV_REC_CLS[prov_relation.get_type()]
        from_type,to_type = relation_cls.FORMAL_ATTRIBUTES[:2]

        # get the class types
        from_type_cls = PROV_ATTR_BASE_CLS[from_type]

        to_type_cls = PROV_ATTR_BASE_CLS[to_type]