├── examples
├── __init__.py
├── tests
│ ├── __init__.py
│ └── test_examples.py
├── simple_example.py
├── simple_example_influence.py
├── horsemeat_example.py
├── bundle_example.py
├── simple_example_with_neo4j.py
├── merge_example.py
├── simple_example_with_neo4j_graph.svg
├── merge_fail_example.py
├── file_buffer_example.py
├── complex_example_with_neo4j.py
└── file_buffer_example_primer.json
├── hound.yml
├── provdbconnector
├── exceptions
│ ├── __init__.py
│ ├── provapi.py
│ ├── utils.py
│ └── database.py
├── utils
│ ├── __init__.py
│ ├── validator.py
│ └── converter.py
├── db_adapters
│ ├── __init__.py
│ ├── neo4j
│ │ ├── __init__.py
│ │ └── cypher_commands.py
│ ├── in_memory
│ │ └── __init__.py
│ └── baseadapter.py
├── tests
│ ├── resources
│ │ ├── __init__.py
│ │ ├── primer.provn
│ │ ├── primer.json
│ │ └── primer.provx
│ ├── utils
│ │ ├── __init__.py
│ │ ├── test_validator.py
│ │ └── test_converter.py
│ ├── db_adapters
│ │ ├── __init__.py
│ │ ├── neo4j
│ │ │ ├── __init__.py
│ │ │ └── test_neo4jadapter.py
│ │ └── in_memory
│ │ │ ├── __init__.py
│ │ │ └── test_simple_in_memory.py
│ ├── __init__.py
│ └── examples.py
└── __init__.py
├── docs
├── readme.rst
├── diagrams
│ ├── Readme.md
│ ├── Class-Diagram.xml
│ ├── Process-Save-Document-Overview.xml
│ └── Process-Save-Document.xml
├── modules.rst
├── index.rst
├── provdbconnector.db_adapters.in_memory.rst
├── provdbconnector.tests.db_adapters.neo4j.rst
├── provdbconnector.rst
├── provdbconnector.tests.db_adapters.in_memory.rst
├── provdbconnector.db_adapters.rst
├── _images
│ └── test_cases
│ │ ├── test_datatypes.svg
│ │ ├── test_long_literals.svg
│ │ ├── test_19_merge_record.svg
│ │ ├── test_1_save_element.svg
│ │ ├── test_27_save_bundle.svg
│ │ ├── test_4_get_record.svg
│ │ ├── test_21_merge_record_complex_fail.svg
│ │ ├── test_22_merge_record_metadata.svg
│ │ ├── test_8_get_records_by_filter.svg
│ │ ├── test_15_delete_by_filter_with_properties.svg
│ │ ├── test_20_merge_record_complex.svg
│ │ ├── test_10_get_records_by_filter_with_metadata.svg
│ │ ├── test_collections.svg
│ │ ├── test_16_delete_by_filter_with_metadata.svg
│ │ ├── test_23_merge_relation.svg
│ │ ├── test_2_save_relation.svg
│ │ ├── test_6_get_relation.svg
│ │ ├── test_26_merge_relation_metadata.svg
│ │ ├── test_24_merge_relation_complex.svg
│ │ ├── test_25_merge_relation_complex_fail.svg
│ │ ├── test_11_get_records_tail.svg
│ │ ├── test_9_get_records_by_filter_with_properties.svg
│ │ ├── test_13_get_bundle_records.svg
│ │ ├── test_12_get_records_tail_recursive.svg
│ │ ├── test_bundles1.svg
│ │ └── test_bundles2.svg
├── provdbconnector.tests.db_adapters.rst
├── provdbconnector.tests.utils.rst
├── provdbconnector.db_adapters.neo4j.rst
├── provdbconnector.tests.rst
├── provdbconnector.utils.rst
├── provdbconnector.exceptions.rst
├── test_howto.rst
├── development.rst
├── changelog.rst
├── Makefile
└── make.bat
├── requirements.txt
├── setup.cfg
├── .github
├── dependabot.yml
└── workflows
│ ├── pythonpackage-publish.yml
│ └── pythonpackage-test.yml
├── .coveragerc
├── docker-compose.yml
├── .travis_docs.sh
├── Makefile
├── .travis.yml
├── setup.py
├── .gitignore
├── README.rst
└── LICENSE
/examples/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/tests/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/hound.yml:
--------------------------------------------------------------------------------
1 | python:
2 | enabled: true
--------------------------------------------------------------------------------
/provdbconnector/exceptions/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/provdbconnector/utils/__init__.py:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/docs/readme.rst:
--------------------------------------------------------------------------------
1 | .. include:: ./../README.rst
--------------------------------------------------------------------------------
/provdbconnector/db_adapters/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/provdbconnector/tests/resources/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/provdbconnector/tests/utils/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/provdbconnector/db_adapters/neo4j/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/provdbconnector/tests/db_adapters/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/provdbconnector/tests/db_adapters/neo4j/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | prov==2.0.0
2 | neo4j==5.6.0
3 |
--------------------------------------------------------------------------------
/provdbconnector/tests/db_adapters/in_memory/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/setup.cfg:
--------------------------------------------------------------------------------
1 | [metadata]
2 | description-file = README.md
3 |
--------------------------------------------------------------------------------
/provdbconnector/db_adapters/in_memory/__init__.py:
--------------------------------------------------------------------------------
1 | from provdbconnector.db_adapters.in_memory.simple_in_memory import SimpleInMemoryAdapter
2 |
--------------------------------------------------------------------------------
/docs/diagrams/Readme.md:
--------------------------------------------------------------------------------
1 | #Diagrams
2 |
3 | All diagrams were created with [Draw.io](https://draw.io).
4 | If you update the diagrams please update the .svg image also a
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: pip
4 | directory: "/"
5 | schedule:
6 | interval: daily
7 | time: "04:00"
8 | open-pull-requests-limit: 10
9 |
--------------------------------------------------------------------------------
/provdbconnector/utils/validator.py:
--------------------------------------------------------------------------------
1 | import logging
2 | log = logging.getLogger(__name__)
3 |
4 |
5 | class Validator(object):
6 | """
7 | Class to do some validation, not implemented yet
8 | """
9 | pass
10 |
--------------------------------------------------------------------------------
/.coveragerc:
--------------------------------------------------------------------------------
1 | [run]
2 | source = provdbconnector
3 |
4 | [report]
5 | exclude_lines =
6 | pragma: no cover
7 | def __repr__
8 | raise AssertionError
9 | raise NotImplementedError
10 | if __name__ == .__main__.:
11 |
--------------------------------------------------------------------------------
/docs/modules.rst:
--------------------------------------------------------------------------------
1 | provdbconnector modules
2 | =======================
3 |
4 | .. toctree::
5 | :maxdepth: 1
6 |
7 | provdbconnector.db_adapters
8 | provdbconnector.utils
9 | provdbconnector.exceptions
10 | provdbconnector.tests
11 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3'
2 | services:
3 | neo4j:
4 | platform: linux/x86_64/v8
5 | image: neo4j:3.5
6 | environment:
7 | - NEO4J_AUTH=none
8 | ports:
9 | - "7474:7474"
10 | - "7373:7373"
11 | - "7687:7687"
12 |
--------------------------------------------------------------------------------
/provdbconnector/tests/__init__.py:
--------------------------------------------------------------------------------
1 | from provdbconnector.tests.db_adapters.test_baseadapter import AdapterTestTemplate
2 | from provdbconnector.tests.test_prov_db import ProvDbTestTemplate
3 | import unittest
4 |
5 |
6 | def additional_tests():
7 | from examples.tests.test_examples import ExamplesTest
8 | return unittest.defaultTestLoader.loadTestsFromTestCase(ExamplesTest)
9 |
10 |
--------------------------------------------------------------------------------
/provdbconnector/tests/utils/test_validator.py:
--------------------------------------------------------------------------------
1 | import unittest
2 |
3 |
4 | class ValidatorTests(unittest.TestCase):
5 | """
6 | Test the validator class
7 |
8 | """
9 | def setUp(self):
10 | """
11 | Setup validator
12 | """
13 | pass
14 |
15 | def tearDown(self):
16 | """
17 | Delete validator
18 | """
19 | pass
20 |
21 |
--------------------------------------------------------------------------------
/provdbconnector/__init__.py:
--------------------------------------------------------------------------------
1 | from provdbconnector.prov_db import ProvDb
2 |
3 | from provdbconnector.exceptions.provapi import ProvDbException
4 | from provdbconnector import db_adapters
5 | from provdbconnector.db_adapters.neo4j.neo4jadapter import Neo4jAdapter
6 | from provdbconnector.db_adapters.neo4j.neo4jadapter import NEO4J_USER, NEO4J_PASS, NEO4J_HOST, NEO4J_HTTP_PORT, NEO4J_BOLT_PORT
7 |
8 | from provdbconnector.db_adapters.in_memory import SimpleInMemoryAdapter
9 |
--------------------------------------------------------------------------------
/.travis_docs.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | sphinx-apidoc -o docs provdbconnector
4 | sphinx-build -q -a -b html -d docs/build/doctrees docs/ docs/build/html &> travis-doc-test.txt
5 | TEST=$(grep 'failed' travis-doc-test.txt | LC_ALL=C.UTF-8 wc -m)
6 | echo "Lenght of errors = $TEST"
7 | if test $TEST -gt 0
8 | then
9 | echo "Error during build docs "
10 | grep 'failed' travis-doc-test.txt
11 | exit 2
12 | else
13 | echo "Build docs complete"
14 | fi
15 |
--------------------------------------------------------------------------------
/docs/index.rst:
--------------------------------------------------------------------------------
1 | .. PROV Database Connector documentation master file, created by
2 | sphinx-quickstart on Mon Oct 24 11:47:37 2016.
3 | You can adapt this file completely to your liking, but it should at least
4 | contain the root `toctree` directive.
5 |
6 | Contents
7 | ========
8 |
9 | .. toctree::
10 | :maxdepth: 2
11 |
12 | readme.rst
13 | development.rst
14 | changelog.rst
15 | test_howto.rst
16 | provdbconnector.rst
17 | modules.rst
18 |
19 | Indices and tables
20 | ==================
21 |
22 | * :ref:`genindex`
23 | * :ref:`modindex`
24 | * :ref:`search`
25 |
26 |
--------------------------------------------------------------------------------
/docs/provdbconnector.db_adapters.in_memory.rst:
--------------------------------------------------------------------------------
1 | provdbconnector.db_adapters.in_memory package
2 | =============================================
3 |
4 | Submodules
5 | ----------
6 |
7 | provdbconnector.db_adapters.in_memory.simple_in_memory module
8 | -------------------------------------------------------------
9 |
10 | .. automodule:: provdbconnector.db_adapters.in_memory.simple_in_memory
11 | :members:
12 | :undoc-members:
13 | :show-inheritance:
14 |
15 |
16 | Module contents
17 | ---------------
18 |
19 | .. automodule:: provdbconnector.db_adapters.in_memory
20 | :members:
21 | :undoc-members:
22 | :show-inheritance:
23 |
--------------------------------------------------------------------------------
/docs/provdbconnector.tests.db_adapters.neo4j.rst:
--------------------------------------------------------------------------------
1 | provdbconnector.tests.db_adapters.neo4j package
2 | ===============================================
3 |
4 | Submodules
5 | ----------
6 |
7 | provdbconnector.tests.db_adapters.neo4j.test_neo4jadapter module
8 | ----------------------------------------------------------------
9 |
10 | .. automodule:: provdbconnector.tests.db_adapters.neo4j.test_neo4jadapter
11 | :members:
12 | :undoc-members:
13 | :show-inheritance:
14 |
15 |
16 | Module contents
17 | ---------------
18 |
19 | .. automodule:: provdbconnector.tests.db_adapters.neo4j
20 | :members:
21 | :undoc-members:
22 | :show-inheritance:
23 |
--------------------------------------------------------------------------------
/docs/provdbconnector.rst:
--------------------------------------------------------------------------------
1 | provdbconnector package
2 | =======================
3 |
4 | Subpackages
5 | -----------
6 |
7 | .. toctree::
8 |
9 | provdbconnector.db_adapters
10 | provdbconnector.exceptions
11 | provdbconnector.tests
12 | provdbconnector.utils
13 |
14 | Submodules
15 | ----------
16 |
17 | provdbconnector.prov_db module
18 | ------------------------------
19 |
20 | .. automodule:: provdbconnector.prov_db
21 | :members:
22 | :undoc-members:
23 | :show-inheritance:
24 |
25 |
26 | Module contents
27 | ---------------
28 |
29 | .. automodule:: provdbconnector
30 | :members:
31 | :undoc-members:
32 | :show-inheritance:
33 |
--------------------------------------------------------------------------------
/provdbconnector/exceptions/provapi.py:
--------------------------------------------------------------------------------
1 | class ProvDbException(Exception):
2 | """
3 | Base exception class for all api exceptions.
4 | """
5 | pass
6 |
7 |
8 | class NoDataBaseAdapterException(ProvDbException):
9 | """
10 | Thrown, if no database adapter argument is passed to the api class.
11 | """
12 | pass
13 |
14 |
15 | class InvalidArgumentTypeException(ProvDbException):
16 | """
17 | Thrown, if an invalid argument is passed to any api method.
18 | """
19 | pass
20 |
21 |
22 | class InvalidProvRecordException(ProvDbException):
23 | """"
24 | Thrown, if an invalid record is passed to any api method.
25 | """
26 | pass
27 |
--------------------------------------------------------------------------------
/docs/provdbconnector.tests.db_adapters.in_memory.rst:
--------------------------------------------------------------------------------
1 | provdbconnector.tests.db_adapters.in_memory package
2 | ===================================================
3 |
4 | Submodules
5 | ----------
6 |
7 | provdbconnector.tests.db_adapters.in_memory.test_simple_in_memory module
8 | ------------------------------------------------------------------------
9 |
10 | .. automodule:: provdbconnector.tests.db_adapters.in_memory.test_simple_in_memory
11 | :members:
12 | :undoc-members:
13 | :show-inheritance:
14 |
15 |
16 | Module contents
17 | ---------------
18 |
19 | .. automodule:: provdbconnector.tests.db_adapters.in_memory
20 | :members:
21 | :undoc-members:
22 | :show-inheritance:
23 |
--------------------------------------------------------------------------------
/docs/provdbconnector.db_adapters.rst:
--------------------------------------------------------------------------------
1 | provdbconnector.db_adapters package
2 | ===================================
3 |
4 | Subpackages
5 | -----------
6 |
7 | .. toctree::
8 |
9 | provdbconnector.db_adapters.in_memory
10 | provdbconnector.db_adapters.neo4j
11 |
12 | Submodules
13 | ----------
14 |
15 | provdbconnector.db_adapters.baseadapter module
16 | ----------------------------------------------
17 |
18 | .. automodule:: provdbconnector.db_adapters.baseadapter
19 | :members:
20 | :undoc-members:
21 | :show-inheritance:
22 |
23 |
24 | Module contents
25 | ---------------
26 |
27 | .. automodule:: provdbconnector.db_adapters
28 | :members:
29 | :undoc-members:
30 | :show-inheritance:
31 |
--------------------------------------------------------------------------------
/docs/_images/test_cases/test_datatypes.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/_images/test_cases/test_long_literals.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/_images/test_cases/test_19_merge_record.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/_images/test_cases/test_1_save_element.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/_images/test_cases/test_27_save_bundle.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/_images/test_cases/test_4_get_record.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/_images/test_cases/test_21_merge_record_complex_fail.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/_images/test_cases/test_22_merge_record_metadata.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/_images/test_cases/test_8_get_records_by_filter.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/_images/test_cases/test_15_delete_by_filter_with_properties.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/provdbconnector.tests.db_adapters.rst:
--------------------------------------------------------------------------------
1 | provdbconnector.tests.db_adapters package
2 | =========================================
3 |
4 | Subpackages
5 | -----------
6 |
7 | .. toctree::
8 |
9 | provdbconnector.tests.db_adapters.in_memory
10 | provdbconnector.tests.db_adapters.neo4j
11 |
12 | Submodules
13 | ----------
14 |
15 | provdbconnector.tests.db_adapters.test_baseadapter module
16 | ---------------------------------------------------------
17 |
18 | .. automodule:: provdbconnector.tests.db_adapters.test_baseadapter
19 | :members:
20 | :undoc-members:
21 | :show-inheritance:
22 |
23 |
24 | Module contents
25 | ---------------
26 |
27 | .. automodule:: provdbconnector.tests.db_adapters
28 | :members:
29 | :undoc-members:
30 | :show-inheritance:
31 |
--------------------------------------------------------------------------------
/provdbconnector/exceptions/utils.py:
--------------------------------------------------------------------------------
1 | from .provapi import ProvDbException
2 |
3 |
4 | class ConverterException(ProvDbException):
5 | """
6 | Base exception class for document converter.
7 | """
8 | pass
9 |
10 |
11 | class ParseException(ConverterException):
12 | """
13 | Thrown, if a given statement could not ne parsed.
14 | """
15 | pass
16 |
17 |
18 | class NoDocumentException(ConverterException):
19 | """
20 | Thrown, if no document argument is passed.
21 | """
22 | pass
23 |
24 |
25 | class SerializerException(ProvDbException):
26 | """
27 | Base exception class for serializer.
28 | """
29 | pass
30 |
31 |
32 | class ValidatorException(ProvDbException):
33 | """
34 | Base exception class for validator.
35 | """
36 | pass
37 |
--------------------------------------------------------------------------------
/docs/provdbconnector.tests.utils.rst:
--------------------------------------------------------------------------------
1 | provdbconnector.tests.utils package
2 | ===================================
3 |
4 | Submodules
5 | ----------
6 |
7 | provdbconnector.tests.utils.test_converter module
8 | -------------------------------------------------
9 |
10 | .. automodule:: provdbconnector.tests.utils.test_converter
11 | :members:
12 | :undoc-members:
13 | :show-inheritance:
14 |
15 | provdbconnector.tests.utils.test_validator module
16 | -------------------------------------------------
17 |
18 | .. automodule:: provdbconnector.tests.utils.test_validator
19 | :members:
20 | :undoc-members:
21 | :show-inheritance:
22 |
23 |
24 | Module contents
25 | ---------------
26 |
27 | .. automodule:: provdbconnector.tests.utils
28 | :members:
29 | :undoc-members:
30 | :show-inheritance:
31 |
--------------------------------------------------------------------------------
/.github/workflows/pythonpackage-publish.yml:
--------------------------------------------------------------------------------
1 | on:
2 | release:
3 | types: [published]
4 | jobs:
5 | build:
6 | runs-on: ubuntu-latest
7 | steps:
8 | - uses: actions/checkout@v3
9 | - name: Set up Python ${{ matrix.python-version }}
10 | uses: actions/setup-python@v4
11 | with:
12 | python-version: ${{ matrix.python-version }}
13 | - name: Install dependencies
14 | run: |
15 | python -m pip install --upgrade pip
16 | pip install '.[test]'
17 | pip install '.[docs]'
18 | - name: Build
19 | run: |
20 | python setup.py build
21 | - name: Publish a Python distribution to PyPI
22 | uses: pypa/gh-action-pypi-publish@release/v1
23 | with:
24 | password: ${{ secrets.PYPI_API_TOKEN }}
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/docs/_images/test_cases/test_20_merge_record_complex.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/simple_example.py:
--------------------------------------------------------------------------------
1 | from prov.model import ProvDocument
2 | from provdbconnector import ProvDb
3 | from provdbconnector.db_adapters.in_memory import SimpleInMemoryAdapter
4 |
5 | prov_api = ProvDb(adapter=SimpleInMemoryAdapter, auth_info=None)
6 |
7 | # create the prov document
8 | prov_document = ProvDocument()
9 | prov_document.add_namespace("ex", "http://example.com")
10 |
11 | prov_document.agent("ex:Bob")
12 | prov_document.activity("ex:Alice")
13 |
14 | prov_document.association("ex:Alice", "ex:Bob")
15 |
16 | document_id = prov_api.save_document(prov_document)
17 |
18 | print(prov_api.get_document_as_provn(document_id))
19 |
20 | # Output:
21 | #
22 | # document
23 | # prefix
24 | # ex < http: // example.com >
25 | #
26 | # agent(ex:Bob)
27 | # activity(ex:Alice, -, -)
28 | # wasAssociatedWith(ex:Alice, ex:Bob, -)
29 | # endDocument
30 |
--------------------------------------------------------------------------------
/examples/simple_example_influence.py:
--------------------------------------------------------------------------------
1 | from prov.model import ProvDocument
2 | from provdbconnector import ProvDb
3 | from provdbconnector.db_adapters.in_memory import SimpleInMemoryAdapter
4 |
5 | prov_api = ProvDb(adapter=SimpleInMemoryAdapter, auth_info=None)
6 |
7 | # create the prov document
8 | prov_document = ProvDocument()
9 | prov_document.add_namespace("ex", "http://example.com")
10 |
11 | prov_document.agent("ex:Bob")
12 | prov_document.activity("ex:Alice")
13 |
14 | prov_document.influence("ex:Alice", "ex:Bob")
15 |
16 | document_id = prov_api.save_document(prov_document)
17 |
18 | print(prov_api.get_document_as_provn(document_id))
19 |
20 | # Output:
21 | #
22 | # document
23 | # prefix ex < http: // example.com >
24 | #
25 | # agent(ex: Bob)
26 | # activity(ex: Alice, -, -)
27 | # wasInfluencedBy(ex: Alice, ex: Bob)
28 | # endDocument
29 |
--------------------------------------------------------------------------------
/docs/provdbconnector.db_adapters.neo4j.rst:
--------------------------------------------------------------------------------
1 | provdbconnector.db_adapters.neo4j package
2 | =========================================
3 |
4 | Submodules
5 | ----------
6 |
7 | provdbconnector.db_adapters.neo4j.cypher_commands module
8 | --------------------------------------------------------
9 |
10 | .. automodule:: provdbconnector.db_adapters.neo4j.cypher_commands
11 | :members:
12 | :undoc-members:
13 | :show-inheritance:
14 |
15 | provdbconnector.db_adapters.neo4j.neo4jadapter module
16 | -----------------------------------------------------
17 |
18 | .. automodule:: provdbconnector.db_adapters.neo4j.neo4jadapter
19 | :members:
20 | :undoc-members:
21 | :show-inheritance:
22 |
23 |
24 | Module contents
25 | ---------------
26 |
27 | .. automodule:: provdbconnector.db_adapters.neo4j
28 | :members:
29 | :undoc-members:
30 | :show-inheritance:
31 |
--------------------------------------------------------------------------------
/docs/provdbconnector.tests.rst:
--------------------------------------------------------------------------------
1 | provdbconnector.tests package
2 | =============================
3 |
4 | Subpackages
5 | -----------
6 |
7 | .. toctree::
8 |
9 | provdbconnector.tests.db_adapters
10 | provdbconnector.tests.resources
11 | provdbconnector.tests.utils
12 |
13 | Submodules
14 | ----------
15 |
16 | provdbconnector.tests.examples module
17 | -------------------------------------
18 |
19 | .. automodule:: provdbconnector.tests.examples
20 | :members:
21 | :undoc-members:
22 | :show-inheritance:
23 |
24 | provdbconnector.tests.test_prov_db module
25 | -----------------------------------------
26 |
27 | .. automodule:: provdbconnector.tests.test_prov_db
28 | :members:
29 | :undoc-members:
30 | :show-inheritance:
31 |
32 |
33 | Module contents
34 | ---------------
35 |
36 | .. automodule:: provdbconnector.tests
37 | :members:
38 | :undoc-members:
39 | :show-inheritance:
40 |
--------------------------------------------------------------------------------
/docs/_images/test_cases/test_10_get_records_by_filter_with_metadata.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/provdbconnector.utils.rst:
--------------------------------------------------------------------------------
1 | provdbconnector.utils package
2 | =============================
3 |
4 | Submodules
5 | ----------
6 |
7 | provdbconnector.utils.converter module
8 | --------------------------------------
9 |
10 | .. automodule:: provdbconnector.utils.converter
11 | :members:
12 | :undoc-members:
13 | :show-inheritance:
14 |
15 | provdbconnector.utils.serializer module
16 | ---------------------------------------
17 |
18 | .. automodule:: provdbconnector.utils.serializer
19 | :members:
20 | :undoc-members:
21 | :show-inheritance:
22 |
23 | provdbconnector.utils.validator module
24 | --------------------------------------
25 |
26 | .. automodule:: provdbconnector.utils.validator
27 | :members:
28 | :undoc-members:
29 | :show-inheritance:
30 |
31 |
32 | Module contents
33 | ---------------
34 |
35 | .. automodule:: provdbconnector.utils
36 | :members:
37 | :undoc-members:
38 | :show-inheritance:
39 |
--------------------------------------------------------------------------------
/docs/provdbconnector.exceptions.rst:
--------------------------------------------------------------------------------
1 | provdbconnector.exceptions package
2 | ==================================
3 |
4 | Submodules
5 | ----------
6 |
7 | provdbconnector.exceptions.database module
8 | ------------------------------------------
9 |
10 | .. automodule:: provdbconnector.exceptions.database
11 | :members:
12 | :undoc-members:
13 | :show-inheritance:
14 |
15 | provdbconnector.exceptions.provapi module
16 | -----------------------------------------
17 |
18 | .. automodule:: provdbconnector.exceptions.provapi
19 | :members:
20 | :undoc-members:
21 | :show-inheritance:
22 |
23 | provdbconnector.exceptions.utils module
24 | ---------------------------------------
25 |
26 | .. automodule:: provdbconnector.exceptions.utils
27 | :members:
28 | :undoc-members:
29 | :show-inheritance:
30 |
31 |
32 | Module contents
33 | ---------------
34 |
35 | .. automodule:: provdbconnector.exceptions
36 | :members:
37 | :undoc-members:
38 | :show-inheritance:
39 |
--------------------------------------------------------------------------------
/.github/workflows/pythonpackage-test.yml:
--------------------------------------------------------------------------------
1 |
2 | on:
3 | push:
4 | branches:
5 | - master
6 | pull_request:
7 | branches:
8 | - master
9 |
10 | jobs:
11 | test:
12 |
13 | runs-on: ubuntu-latest
14 | strategy:
15 | matrix:
16 | python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"]
17 |
18 | steps:
19 | - uses: actions/checkout@v3
20 | - name: Set up Python ${{ matrix.python-version }}
21 | uses: actions/setup-python@v4
22 | with:
23 | python-version: ${{ matrix.python-version }}
24 | - name: Install dependencies
25 | run: |
26 | python -m pip install --upgrade pip
27 | pip install '.[test]'
28 | pip install '.[docs]'
29 | - name: Build
30 | run: |
31 | python setup.py build
32 | - name: Test with pytest
33 | run: |
34 | docker-compose build
35 | docker-compose up -d
36 | sleep 20s
37 | coverage run --source=provdbconnector setup.py test
38 |
39 |
--------------------------------------------------------------------------------
/examples/horsemeat_example.py:
--------------------------------------------------------------------------------
1 | from provdbconnector import ProvDb
2 | from provdbconnector import Neo4jAdapter
3 | import os
4 | import pkg_resources
5 |
6 | # Data is from: https://provenance.ecs.soton.ac.uk/store/documents/75490/
7 |
8 |
9 | NEO4J_USER = os.environ.get('NEO4J_USERNAME', 'neo4j')
10 | NEO4J_PASS = os.environ.get('NEO4J_PASSWORD', 'neo4jneo4j')
11 | NEO4J_HOST = os.environ.get('NEO4J_HOST', 'localhost')
12 | NEO4J_BOLT_PORT = os.environ.get('NEO4J_BOLT_PORT', '7687')
13 |
14 | # Auth info
15 | auth_info = {"user_name": NEO4J_USER,
16 | "user_password": NEO4J_PASS,
17 | "host": NEO4J_HOST + ":" + NEO4J_BOLT_PORT
18 | }
19 |
20 |
21 | # create the api
22 | prov_api = ProvDb(adapter=Neo4jAdapter, auth_info=auth_info)
23 |
24 | # create the prov document from examples
25 | prov_document_buffer = pkg_resources.resource_stream("examples", "horsemeat_example.json")
26 |
27 | # Save document
28 | document_id = prov_api.save_document(prov_document_buffer)
29 | # This is similar to:
30 | # prov_api.create_document_from_json(prov_document_buffer)
31 |
32 | # get document
33 | print(prov_api.get_document_as_provn(document_id))
--------------------------------------------------------------------------------
/examples/bundle_example.py:
--------------------------------------------------------------------------------
1 | from prov.model import ProvDocument
2 | from provdbconnector import ProvDb
3 | from provdbconnector.db_adapters.in_memory import SimpleInMemoryAdapter
4 |
5 | prov_api = ProvDb(adapter=SimpleInMemoryAdapter, auth_info=None)
6 |
7 | # create the prov document
8 | prov_document = ProvDocument()
9 | prov_document.add_namespace("ex", "http://example.com")
10 |
11 | prov_document.agent("ex:Bob")
12 | prov_document.activity("ex:Alice")
13 |
14 | prov_document.association("ex:Alice", "ex:Bob")
15 | # create bundle
16 | b1 = prov_document.bundle("ex:bundle1")
17 | b1.agent("ex:Yoda")
18 |
19 | b2 = prov_document.bundle("ex:bundle2")
20 | b2.agent("ex:Jabba the Hutt")
21 |
22 | document_id = prov_api.save_document(prov_document)
23 |
24 | print(prov_api.get_document_as_provn(document_id))
25 |
26 | # Output:
27 | #
28 | # document
29 | # prefix ex
30 | #
31 | # agent(ex:Bob)
32 | # activity(ex:Alice, -, -)
33 | # wasAssociatedWith(ex:Alice, ex:Bob, -)
34 | # bundle ex:bundle2
35 | # prefix ex
36 | #
37 | # agent(ex:Jabba the Hutt)
38 | # endBundle
39 | # bundle ex:bundle1
40 | # prefix ex
41 | #
42 | # agent(ex:Yoda)
43 | # endBundle
44 | # endDocument
45 |
--------------------------------------------------------------------------------
/examples/simple_example_with_neo4j.py:
--------------------------------------------------------------------------------
1 | from prov.model import ProvDocument
2 | from provdbconnector import ProvDb
3 | from provdbconnector import Neo4jAdapter
4 | import os
5 |
6 | # create the api
7 |
8 | NEO4J_USER = os.environ.get('NEO4J_USERNAME', 'neo4j')
9 | NEO4J_PASS = os.environ.get('NEO4J_PASSWORD', 'neo4jneo4j')
10 | NEO4J_HOST = os.environ.get('NEO4J_HOST', 'localhost')
11 | NEO4J_BOLT_PORT = os.environ.get('NEO4J_BOLT_PORT', '7687')
12 |
13 | auth_info = {"user_name": NEO4J_USER,
14 | "user_password": NEO4J_PASS,
15 | "host": NEO4J_HOST + ":" + NEO4J_BOLT_PORT
16 | }
17 |
18 | prov_api = ProvDb(adapter=Neo4jAdapter, auth_info=auth_info)
19 |
20 | # create the prov document
21 | prov_document = ProvDocument()
22 | prov_document.add_namespace("ex", "http://example.com")
23 |
24 | prov_document.agent("ex:Bob")
25 | prov_document.activity("ex:Alice")
26 |
27 | prov_document.association("ex:Alice", "ex:Bob")
28 |
29 | document_id = prov_api.save_document(prov_document)
30 |
31 | print(prov_api.get_document_as_provn(document_id))
32 |
33 | # Output:
34 | #
35 | # document
36 | # prefix
37 | # ex < http: // example.com >
38 | #
39 | # agent(ex:Bob)
40 | # activity(ex:Alice, -, -)
41 | # wasAssociatedWith(ex:Alice, ex:Bob, -)
42 | # endDocument
43 |
--------------------------------------------------------------------------------
/examples/merge_example.py:
--------------------------------------------------------------------------------
1 | from prov.model import ProvDocument
2 | from provdbconnector import ProvDb
3 | from provdbconnector.db_adapters.in_memory import SimpleInMemoryAdapter
4 |
5 | prov_api = ProvDb(adapter=SimpleInMemoryAdapter, auth_info=None)
6 |
7 | # create the prov first document
8 | first_prov_document = ProvDocument()
9 | first_prov_document .add_namespace("ex", "http://example.com")
10 |
11 | first_prov_document .agent("ex:Bob")
12 | first_prov_document .activity("ex:Alice")
13 |
14 | first_prov_document .association("ex:Alice", "ex:Bob")
15 |
16 | first_document_id = prov_api.save_document(first_prov_document)
17 |
18 | #Create the second prov document and merge the ex:Bob entry
19 | second_prov_document = ProvDocument()
20 | second_prov_document.add_namespace("ex", "http://example.com")
21 |
22 | second_prov_document.agent("ex:Bob", other_attributes={"ex:age": 42})
23 |
24 | second_document_id = prov_api.save_document(second_prov_document)
25 |
26 |
27 | #Query the first document ID but get the Bob with the age property back (so successfully merged)
28 |
29 | print(prov_api.get_document_as_provn(first_document_id))
30 |
31 | # Output:
32 | #
33 | # document
34 | # prefix
35 | # ex < http: // example.com >
36 | #
37 | # activity(ex:Alice, -, -)
38 | # agent(ex:Bob, [ex:age = 42])
39 | # wasAssociatedWith(ex:Alice, ex:Bob, -)
40 | # endDocument
41 |
--------------------------------------------------------------------------------
/provdbconnector/exceptions/database.py:
--------------------------------------------------------------------------------
1 | from .provapi import ProvDbException
2 |
3 |
4 | class AdapterException(ProvDbException):
5 | """
6 | Base exception class for database adapters.
7 | """
8 | pass
9 |
10 |
11 | class InvalidOptionsException(AdapterException):
12 | """
13 | Thrown, if passed argument for adapter is invalid.
14 | """
15 | pass
16 |
17 |
18 | class AuthException(AdapterException):
19 | """
20 | Thrown, if database adapter could not establish a connection with given credentials to the database.
21 | """
22 | pass
23 |
24 |
25 | class DatabaseException(AdapterException):
26 | """
27 | Thrown, if method could not performed on database.
28 | """
29 | pass
30 |
31 |
32 | class CreateRecordException(DatabaseException):
33 | """
34 | Thrown, if record could not be saved in database.
35 | """
36 | pass
37 |
38 |
39 | class CreateRelationException(DatabaseException):
40 | """
41 | Thrown, if relation could not be saved in database.
42 | """
43 | pass
44 |
45 |
46 | class NotFoundException(DatabaseException):
47 | """
48 | Thrown, if record or relation could not be found in database.
49 | """
50 | pass
51 |
52 |
53 | class MergeException(DatabaseException):
54 | """
55 | Thrown, if a record or relation can't get merged
56 | """
57 | pass
58 |
--------------------------------------------------------------------------------
/docs/test_howto.rst:
--------------------------------------------------------------------------------
1 | .. _test_howto:
2 |
3 | Testing Howto
4 | -------------
5 | To run the test local follow the next steps
6 |
7 | 1. Setup your env
8 | ~~~~~~~~~~~~~~~~~
9 |
10 | .. include:: ./development.rst
11 | :start-after: Setup
12 | :end-before: Execute tests
13 |
14 | 2. Start your neo4j setup
15 | ~~~~~~~~~~~~~~~~~~~~~~~~~
16 |
17 | The tests require a running neo4j 3.0+ instance
18 | The simples way do start neo4j ist to use the docker image provided by neo4j
19 |
20 | .. code:: sh
21 |
22 | docker run \
23 | --publish=7474:7474 --publish=7687:7687 \
24 | --volume=$HOME/neo4j/data:/data \
25 | neo4j:3.0
26 |
27 |
28 | Then open a browser `http://localhost:7474` and set the password to **neo4jneo4j**
29 | Alternative you can set the env. variables:
30 |
31 | - NEO4J_USERNAME: Default: neo4j
32 | - NEO4J_PASSWORD: Default: neo4jneo4j
33 | - NEO4J_HOST: Default: localhost
34 | - NEO4J_BOLT_PORT: Default: 7687
35 | - NEO4J_HTTP_PORT: Default: 7474
36 |
37 | Alternative use docker-compose
38 |
39 | .. code:: sh
40 |
41 | docker-compose up
42 |
43 | 3. Run your tests
44 | ~~~~~~~~~~~~~~~~~
45 |
46 | .. code:: sh
47 |
48 | # Change env
49 | source env/bin/activate
50 | #Start tests
51 | make test
52 |
53 | .. note::
54 | If some tests fail because of certificate issues, delete or rename the known_hosts file in ~/.neo4j.
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | .PHONY: clean-pyc clean-build docs clean
2 |
3 | help:
4 | @echo "setup - basic setup and install"
5 | @echo "dev-setup - basic setup for developers"
6 | @echo "clean-build - remove build artifacts"
7 | @echo "clean-pyc - remove Python file artifacts"
8 | @echo "test - run tests quickly with the default Python"
9 | @echo "coverage - check code coverage quickly with the default Python"
10 | @echo "docs - generate Sphinx HTML documentation, including API docs"
11 | @echo "release - package and upload a release"
12 | @echo "dist - package"
13 |
14 | setup:
15 | pip install -U pip setuptools
16 | pip install '.'
17 |
18 | dev-setup:
19 | mkdir -p docs/_static
20 | pip install -U pip setuptools
21 | pip install -e '.[dev]'
22 |
23 | clean: clean-build clean-pyc
24 | rm -fr htmlcov/
25 |
26 | clean-build:
27 | rm -fr build/
28 | rm -fr dist/
29 | rm -fr *.egg-info
30 |
31 | clean-pyc:
32 | find . -name '*.pyc' -exec rm -f {} +
33 | find . -name '*.pyo' -exec rm -f {} +
34 |
35 | test:
36 | python setup.py test
37 |
38 | coverage:
39 | coverage run --source provdbconnector setup.py test
40 | coverage report -m
41 | coverage html
42 |
43 | docs:
44 | $(MAKE) -C docs clean
45 | sphinx-apidoc -o docs provdbconnector
46 | sphinx-build -a -b html -d docs/build/doctrees docs/ docs/build/html
47 |
48 | docs-travis:
49 | $(MAKE) -C docs clean
50 | ./.travis_docs.sh
51 |
52 | release: clean
53 | python setup.py sdist upload
54 |
55 | dist: clean
56 | python setup.py sdist
57 |
--------------------------------------------------------------------------------
/examples/simple_example_with_neo4j_graph.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: python
2 | sudo: required
3 | python:
4 | - 3.6
5 | - 3.7
6 | - 3.8
7 | - 3.9
8 | services:
9 | - docker
10 | before_install:
11 | - docker-compose build
12 | - docker-compose up -d
13 | install:
14 | - pip install '.[test]'
15 | - pip install '.[docs]'
16 | script:
17 | - curl --output /dev/null --silent --head --fail http://localhost:7474
18 | - coverage run --source=provdbconnector setup.py test
19 | - make docs-travis
20 | - ". ./.travis_docs.sh"
21 | after_success:
22 | - coveralls
23 | - docker-compose down
24 |
25 | jobs:
26 | include:
27 | - stage: pypi release
28 | if: tag IS present
29 | python: "3.8"
30 | script:
31 | echo "Building project"
32 | python setup.py build
33 | deploy:
34 | provider: pypi
35 | user: __token__
36 | password:
37 | secure: P7X9mQZR/2zOkDShQTSv09rbg4LgmR8ODoTnvE0d6oAOZM+Y6rWGL8u1dLxzw98anNlqbXd/5sjK6PsZ3h5Ola2WJ6KIGJAiP8stDt0BfGjW1Y3B2JocqdOZb+QOquqP3OlNHYgdL54hDjS0HsLqEhNJzBPUUPY0uOy1NZN8uf4DGMuVkNaDo/m5SpGbQPee7xOblGEMRqjyRISYAv1jkxzworzZQMQw6TwwqtZbBi6p3RMMz8SaUClIcTRlXS+O5QaEehHB+xxRMMG/fETAlZmcmByX5Px+YXX25FhlaTm9+GUQIa6/1XzxK03eyin3DwUPkJPUZqQ08NMWU5ZqD6+ITXKC/wF0A3OYqMvQWbwmRZvvebd8VBcSgzHiijXopsoBCpiQrb1wi+YfEzAwDyAjzR1qSavTjtHrkVkJik6QV2KN0Sa/SnIWEPK83b9kfNxnKRhSAzePuUp/bBr0tISvWj4S80sghui992FQEoXJV7aVBQ2SiRG6ynN6W/CbtRpPDL2NSnuM3K2Xt1vysG9iYW1zs9RVGjaVaclbR6JS1DXAdTR1pOTTWVFn67BR9nNMqYZf0M9mll+Iq3nfNbWdAIR/3g9U3syIpr4SiHUJ0YGodL7SX2t/lrbTR/WRPMF6SIE+5i8IDWbuBWF7+bEZbL3OFCwGmWH9y6fA3d8=
38 | on:
39 | tags: true
40 |
--------------------------------------------------------------------------------
/examples/merge_fail_example.py:
--------------------------------------------------------------------------------
1 | from prov.model import ProvDocument
2 | from provdbconnector import ProvDb
3 | from provdbconnector.exceptions.database import MergeException
4 | from provdbconnector.db_adapters.in_memory import SimpleInMemoryAdapter
5 |
6 | prov_api = ProvDb(adapter=SimpleInMemoryAdapter, auth_info=None)
7 |
8 | # create the prov first document
9 | first_prov_document = ProvDocument()
10 | first_prov_document .add_namespace("ex", "http://example.com")
11 |
12 | first_prov_document .agent("ex:Bob", other_attributes={"ex:last_name": "Meier"})
13 | first_prov_document .activity("ex:Alice")
14 |
15 | first_prov_document .association("ex:Alice", "ex:Bob")
16 |
17 | first_document_id = prov_api.save_document(first_prov_document)
18 |
19 | #Create the second prov document and merge the ex:Bob entry
20 | second_prov_document = ProvDocument()
21 | second_prov_document.add_namespace("ex", "http://example.com")
22 |
23 | second_prov_document.agent("ex:Bob", other_attributes={"ex:age": 42, "ex:last_name": "Müller"})
24 |
25 | try:
26 | second_document_id = prov_api.save_document(second_prov_document)
27 | except MergeException as e:
28 | print ("Got the merge exception: {}".format(e))
29 |
30 | # Query the first document ID and get only the last_name = Meier back. The merge was not successful.
31 |
32 | print(prov_api.get_document_as_provn(first_document_id))
33 |
34 | # Output:
35 | #
36 | # document
37 | # prefix
38 | # ex < http: // example.com >
39 | #
40 | # activity(ex:Alice, -, -)
41 | # agent(ex:Bob, [ex:last_name = "Meier"])
42 | # wasAssociatedWith(ex:Alice, ex:Bob, -)
43 | # endDocument
--------------------------------------------------------------------------------
/docs/_images/test_cases/test_collections.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/tests/test_examples.py:
--------------------------------------------------------------------------------
1 | import unittest
2 |
3 |
4 | class ExamplesTest(unittest.TestCase):
5 | """
6 | This test is only to load the example and check if the examples are still running
7 |
8 | """
9 |
10 | def test_bundle_example(self):
11 | """
12 | Test the bundle example
13 |
14 | """
15 | import examples.bundle_example
16 |
17 | def test_test_complex_example_with_neo4j(self):
18 | """
19 | Test the neo4j example
20 |
21 | """
22 | import examples.complex_example_with_neo4j
23 |
24 | def test_file_buffer_example(self):
25 | """
26 | Test the file buffer example
27 |
28 | """
29 | import examples.file_buffer_example
30 |
31 | def test_simple_example(self):
32 | """
33 | Test the basic example
34 |
35 | """
36 | import examples.simple_example
37 |
38 | def test_simple_example_influence(self):
39 | """
40 | Test the basic example with influence by relation
41 |
42 | """
43 | import examples.simple_example_influence
44 |
45 | def test_simple_example_with_neo4j(self):
46 | """
47 | Test the basic neo4j example
48 |
49 | """
50 | import examples.simple_example_with_neo4j
51 |
52 | def test_merge_example(self):
53 | """
54 | Test the merge example
55 |
56 | """
57 | import examples.merge_example
58 |
59 | def test_merge_fail_example(self):
60 | """
61 | Test the merge fail example
62 |
63 | """
64 | import examples.merge_fail_example
65 |
66 | def test_horsemeat_example(self):
67 | """
68 | Test the merge fail example
69 | """
70 | import examples.horsemeat_example
--------------------------------------------------------------------------------
/docs/_images/test_cases/test_16_delete_by_filter_with_metadata.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | try:
3 | from setuptools import setup, find_packages
4 | except ImportError:
5 | from distutils.core import setup, find_packages
6 |
7 | tests_require = [
8 | 'coverage',
9 | 'coveralls'
10 | ]
11 |
12 | docs_require = [
13 | 'Sphinx>=1.3.5',
14 | 'recommonmark>=0.4.0',
15 | 'sphinx-rtd-theme>=0.1.9',
16 | 'sphinxcontrib-napoleon>=0.4.4',
17 | 'sphinxcontrib-httpdomain>=1.5.0',
18 | 'mock',
19 | ]
20 |
21 | setup(
22 | name='prov-db-connector',
23 | version='0.5.1',
24 | description='PROV Database Connector',
25 | keywords=[
26 | 'provenance', 'graph', 'model', 'PROV', 'PROV-DM', 'PROV-JSON', 'JSON',
27 | 'PROV-XML', 'PROV-N'
28 | ],
29 | author='DLR, Stefan Bieliauskas, Martin Stoffers',
30 | author_email='opensource@dlr.de, sb@conts.de, martin.stoffers@studserv.uni-leipzig.de',
31 | url='https://github.com/DLR-SC/prov-db-connector',
32 | classifiers=[
33 | 'Development Status :: 1 - Planning',
34 | 'Topic :: Software Development :: Libraries :: Python Modules',
35 | 'License :: OSI Approved :: Apache Software License',
36 | 'Programming Language :: Python :: 3.6',
37 | 'Programming Language :: Python :: 3.7',
38 | 'Programming Language :: Python :: 3.8',
39 | 'Programming Language :: Python :: 3.9'
40 | ],
41 | license="Apache License 2.0",
42 |
43 | packages=find_packages(),
44 | package_dir={
45 | 'provdbconnector': 'provdbconnector'
46 | },
47 | include_package_data=True,
48 | zip_safe=False,
49 | install_requires=[
50 | "prov==2.0.0",
51 | "neo4j==5.6.0"
52 | ],
53 | extras_require={
54 | 'test': tests_require,
55 | 'dev': tests_require + docs_require,
56 | 'docs': docs_require,
57 | },
58 |
59 | test_suite='provdbconnector.tests',
60 | )
61 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | .eggs/
5 | *$py.class
6 |
7 | # C extensions
8 | *.so
9 |
10 | # Distribution / packaging
11 | .Python
12 | env/
13 | bin/
14 | build/
15 | develop-eggs/
16 | dist/
17 | eggs/
18 | build/
19 | develop-eggs/
20 | dist/
21 | downloads/
22 | eggs/
23 | .eggs/
24 | lib/
25 | lib64/
26 | parts/
27 | sdist/
28 | var/
29 | *.egg-info/
30 | .installed.cfg
31 | *.egg
32 |
33 | # PyInstaller
34 | # Usually these files are written by a python script from a template
35 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
36 | *.manifest
37 | *.spec
38 |
39 | # Installer logs
40 | pip-log.txt
41 | pip-delete-this-directory.txt
42 | travis-doc-test.txt
43 |
44 | # Unit test / coverage reports
45 | htmlcov/
46 | .tox/
47 | .coverage
48 | .cache
49 | nosetests.xml
50 | coverage.xml
51 |
52 | # Translations
53 | *.mo
54 |
55 | # Mr Developer
56 | .mr.developer.cfg
57 | .project
58 | .pydevproject
59 |
60 | # Rope
61 | .ropeproject
62 |
63 | #IDE
64 |
65 | .idea
66 |
67 |
68 | # Django stuff:
69 | *.log
70 | *.pot
71 |
72 | # Sphinx documentation
73 | doc/_build/
74 | .coverage.*
75 | .cache
76 | nosetests.xml
77 | coverage.xml
78 | *,cover
79 | .hypothesis/
80 |
81 | # Translations
82 | *.mo
83 | *.pot
84 |
85 | # Django stuff:
86 | *.log
87 | local_settings.py
88 |
89 | # Flask stuff:
90 | instance/
91 | .webassets-cache
92 |
93 | # Scrapy stuff:
94 | .scrapy
95 |
96 | # Sphinx documentation
97 | docs/_build/
98 |
99 | # PyBuilder
100 | target/
101 |
102 | # IPython Notebook
103 | .ipynb_checkpoints
104 |
105 | # pyenv
106 | .python-version
107 |
108 | # celery beat schedule file
109 | celerybeat-schedule
110 |
111 | # dotenv
112 | .env
113 |
114 | # virtualenv
115 | venv/
116 | ENV/
117 |
118 | # Spyder project settings
119 | .spyderproject
120 |
121 | .vscode
122 | *.code-workspace
123 |
--------------------------------------------------------------------------------
/docs/_images/test_cases/test_23_merge_relation.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/_images/test_cases/test_2_save_relation.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/_images/test_cases/test_6_get_relation.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/_images/test_cases/test_26_merge_relation_metadata.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/_images/test_cases/test_24_merge_relation_complex.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/_images/test_cases/test_25_merge_relation_complex_fail.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/provdbconnector/tests/resources/primer.provn:
--------------------------------------------------------------------------------
1 | document
2 | prefix foaf
3 | prefix dcterms
4 | prefix ex
5 | entity(ex:article,[dcterms:title = "Crime rises in cities" %% xsd:string])
6 | entity(ex:articleV1)
7 | entity(ex:articleV2)
8 | entity(ex:dataSet1)
9 | entity(ex:dataSet2)
10 | entity(ex:regionList)
11 | entity(ex:composition)
12 | entity(ex:chart1)
13 | entity(ex:chart2)
14 | entity(ex:blogEntry)
15 | activity(ex:compile,-,-)
16 | activity(ex:compile2,-,-)
17 | activity(ex:compose,-,-)
18 | activity(ex:correct,2012-03-31T09:21:00.000+01:00,2012-04-01T15:21:00.000+01:00)
19 | activity(ex:illustrate,-,-)
20 | used(ex:compose,ex:dataSet1,-)
21 | used(ex:compose,ex:regionList,-)
22 | wasGeneratedBy(ex:composition,ex:compose,-)
23 | used(ex:illustrate,ex:composition,-)
24 | wasGeneratedBy(ex:chart1,ex:illustrate,-)
25 | wasGeneratedBy(ex:chart1,ex:compile,2012-03-02T10:30:00.000Z)
26 | wasGeneratedBy(ex:chart2,ex:compile2,2012-04-01T15:21:00.000+01:00)
27 | agent(ex:derek,[prov:type = 'prov:Person', foaf:givenName = "Derek" %% xsd:string, foaf:mbox = "" %% xsd:string])
28 | wasAssociatedWith(ex:compose,ex:derek,-)
29 | wasAssociatedWith(ex:illustrate,ex:derek,-)
30 | agent(ex:chartgen,[prov:type = 'prov:Organization', foaf:name = "Chart Generators Inc" %% xsd:string])
31 | actedOnBehalfOf(ex:derek,ex:chartgen,ex:compose)
32 | wasAttributedTo(ex:chart1, ex:derek)
33 | used(ex:compose,ex:dataSet1,-,[prov:role = 'ex:dataToCompose'])
34 | used(ex:compose,ex:regionList,-,[prov:role = 'ex:regionsToAggregateBy'])
35 | wasGeneratedBy(ex:dataSet2,ex:correct,-)
36 | used(ex:correct,ex:dataSet1,-)
37 | wasDerivedFrom(ex:dataSet2, ex:dataSet1,[prov:type = 'prov:Revision'])
38 | wasDerivedFrom(ex:chart2, ex:dataSet2)
39 | wasDerivedFrom(ex:blogEntry, ex:article,[prov:type = 'prov:Quotation'])
40 | specializationOf(ex:articleV1,ex:article)
41 | wasDerivedFrom(ex:articleV1, ex:dataSet1)
42 | specializationOf(ex:articleV2,ex:article)
43 | wasDerivedFrom(ex:articleV2, ex:dataSet2)
44 | alternateOf(ex:articleV2,ex:articleV1)
45 | endDocument
46 |
--------------------------------------------------------------------------------
/provdbconnector/tests/db_adapters/in_memory/test_simple_in_memory.py:
--------------------------------------------------------------------------------
1 | from provdbconnector.exceptions.database import InvalidOptionsException
2 | from provdbconnector.db_adapters.in_memory import SimpleInMemoryAdapter
3 | from provdbconnector.prov_db import ProvDb
4 | from provdbconnector.tests import AdapterTestTemplate
5 | from provdbconnector.tests import ProvDbTestTemplate
6 |
7 |
8 | class SimpleInMemoryAdapterTest(AdapterTestTemplate):
9 | """
10 | This class implements the AdapterTestTemplate and only override some functions.
11 |
12 | """
13 | def setUp(self):
14 | """
15 | Connect to your database
16 |
17 | """
18 | self.instance = SimpleInMemoryAdapter()
19 | self.instance.connect(None)
20 |
21 | def test_connect_invalid_options(self):
22 | """
23 | Test your connect function with invalid data
24 |
25 | """
26 | auth_info = {"invalid": "Invalid"}
27 | with self.assertRaises(InvalidOptionsException):
28 | self.instance.connect(auth_info)
29 |
30 | def clear_database(self):
31 | """
32 | Clear the database
33 |
34 | """
35 | self.instance.all_nodes = dict()
36 | self.instance.all_relations= dict()
37 |
38 | def tearDown(self):
39 | """
40 | Delete your instance
41 |
42 | """
43 | del self.instance
44 |
45 |
46 | class SimpleInMemoryAdapterProvDbTests(ProvDbTestTemplate):
47 | """
48 | This is the high level test for the SimpleInMemoryAdapter
49 |
50 | """
51 | def setUp(self):
52 | """
53 | Setup a ProvDb instance
54 | """
55 | self.provapi = ProvDb(api_id=1, adapter=SimpleInMemoryAdapter, auth_info=None)
56 |
57 | def clear_database(self):
58 | """
59 | Clear function get called before each test starts
60 |
61 | """
62 | self.provapi._adapter.all_nodes = dict()
63 | self.provapi._adapter.all_relations = dict()
64 |
65 | def tearDown(self):
66 | """
67 | Delete prov api instance
68 | """
69 | del self.provapi
70 |
--------------------------------------------------------------------------------
/docs/diagrams/Class-Diagram.xml:
--------------------------------------------------------------------------------
1 | 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
--------------------------------------------------------------------------------
/docs/diagrams/Process-Save-Document-Overview.xml:
--------------------------------------------------------------------------------
1 | 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
--------------------------------------------------------------------------------
/examples/file_buffer_example.py:
--------------------------------------------------------------------------------
1 | from provdbconnector import ProvDb
2 | from provdbconnector.db_adapters.in_memory import SimpleInMemoryAdapter
3 | import pkg_resources
4 |
5 | # create the api
6 | prov_api = ProvDb(adapter=SimpleInMemoryAdapter, auth_info=None)
7 |
8 | # create the prov document from examples
9 | prov_document_buffer = pkg_resources.resource_stream("examples", "file_buffer_example_primer.json")
10 |
11 | # Save document
12 | document_id = prov_api.save_document(prov_document_buffer)
13 | # This is similar to:
14 | # prov_api.create_document_from_json(prov_document_buffer)
15 |
16 | # get document
17 | print(prov_api.get_document_as_provn(document_id))
18 |
19 | # Output:
20 |
21 | # document
22 | # prefix
23 | # foaf < http: // xmlns.com / foaf / 0.1 / >
24 | # prefix
25 | # dcterms < http: // purl.org / dc / terms / >
26 | # prefix
27 | # ex < http: // example / >
28 | #
29 | # specializationOf(ex:articleV2, ex:article)
30 | # specializationOf(ex:articleV1, ex:article)
31 | # wasDerivedFrom(ex:blogEntry, ex:article, -, -, -, [prov:type = 'prov:Quotation'])
32 | # alternateOf(ex:articleV2, ex:articleV1)
33 | # wasDerivedFrom(ex:articleV1, ex:dataSet1, -, -, -)
34 | # wasDerivedFrom(ex:articleV2, ex:dataSet2, -, -, -)
35 | # wasDerivedFrom(ex:dataSet2, ex:dataSet1, -, -, -, [prov:type = 'prov:Revision'])
36 | # used(ex:correct, ex:dataSet1, -)
37 | # used(ex:compose, ex:dataSet1, -, [prov:role = "ex:dataToCompose"])
38 | # wasDerivedFrom(ex:chart2, ex:dataSet2, -, -, -)
39 | # wasGeneratedBy(ex:dataSet2, ex:correct, -)
40 | # used(ex:compose, ex:regionList, -, [prov:role = "ex:regionsToAggregateBy"])
41 | # used(ex:illustrate, ex:composition, -)
42 | # wasGeneratedBy(ex:composition, ex:compose, -)
43 | # wasAttributedTo(ex:chart1, ex:derek)
44 | # wasGeneratedBy(ex:chart1, ex:compile, 2012 - 03 - 02
45 | # T10:30:00)
46 | # wasGeneratedBy(ex:chart1, ex:illustrate, -)
47 | # wasAssociatedWith(ex:compose, ex:derek, -)
48 | # wasAssociatedWith(ex:illustrate, ex:derek, -)
49 | # actedOnBehalfOf(ex:derek, ex:chartgen, ex:compose)
50 | # entity(ex:article, [dcterms:title = "Crime rises in cities"])
51 | # entity(ex:articleV1)
52 | # entity(ex:articleV2)
53 | # entity(ex:dataSet1)
54 | # entity(ex:dataSet2)
55 | # entity(ex:regionList)
56 | # entity(ex:composition)
57 | # entity(ex:chart1)
58 | # entity(ex:chart2)
59 | # entity(ex:blogEntry)
60 | # activity(ex:compile, -, -)
61 | # activity(ex:compile2, -, -)
62 | # activity(ex:compose, -, -)
63 | # activity(ex:correct, 2012 - 03 - 31
64 | # T09:21:00, 2012 - 04 - 01
65 | # T15:21:00)
66 | # activity(ex:illustrate, -, -)
67 | # agent(ex:derek, [foaf:mbox = "", foaf:givenName = "Derek", prov:type = 'prov:Person'])
68 | # agent(ex:chartgen, [foaf:name = "Chart Generators Inc", prov:type = 'prov:Organization'])
69 | # endDocument
70 |
--------------------------------------------------------------------------------
/docs/diagrams/Process-Save-Document.xml:
--------------------------------------------------------------------------------
1 | 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
--------------------------------------------------------------------------------
/examples/complex_example_with_neo4j.py:
--------------------------------------------------------------------------------
1 | from provdbconnector import ProvDb
2 | from provdbconnector import Neo4jAdapter
3 | from prov.tests.examples import primer_example
4 | import os
5 |
6 | # create the api
7 |
8 | NEO4J_USER = os.environ.get('NEO4J_USERNAME', 'neo4j')
9 | NEO4J_PASS = os.environ.get('NEO4J_PASSWORD', 'neo4jneo4j')
10 | NEO4J_HOST = os.environ.get('NEO4J_HOST', 'localhost')
11 | NEO4J_BOLT_PORT = os.environ.get('NEO4J_BOLT_PORT', '7687')
12 |
13 | auth_info = {"user_name": NEO4J_USER,
14 | "user_password": NEO4J_PASS,
15 | "host": NEO4J_HOST + ":" + NEO4J_BOLT_PORT
16 | }
17 |
18 | prov_api = ProvDb(adapter=Neo4jAdapter, auth_info=auth_info)
19 |
20 | # create the prov document from examples
21 | prov_document = primer_example()
22 |
23 | # Save document
24 | document_id = prov_api.save_document(prov_document)
25 |
26 | # get document
27 | print(prov_api.get_document_as_provn(document_id))
28 |
29 | # Output:
30 |
31 | # document
32 | # prefix
33 | # foaf < http: // xmlns.com / foaf / 0.1 / >
34 | # prefix
35 | # dcterms < http: // purl.org / dc / terms / >
36 | # prefix
37 | # ex < http: // example / >
38 | #
39 | # specializationOf(ex:articleV2, ex:article)
40 | # specializationOf(ex:articleV1, ex:article)
41 | # wasDerivedFrom(ex:blogEntry, ex:article, -, -, -, [prov:type = 'prov:Quotation'])
42 | # alternateOf(ex:articleV2, ex:articleV1)
43 | # wasDerivedFrom(ex:articleV1, ex:dataSet1, -, -, -)
44 | # wasDerivedFrom(ex:articleV2, ex:dataSet2, -, -, -)
45 | # wasDerivedFrom(ex:dataSet2, ex:dataSet1, -, -, -, [prov:type = 'prov:Revision'])
46 | # used(ex:correct, ex:dataSet1, -)
47 | # used(ex:compose, ex:dataSet1, -, [prov:role = "ex:dataToCompose"])
48 | # wasDerivedFrom(ex:chart2, ex:dataSet2, -, -, -)
49 | # wasGeneratedBy(ex:dataSet2, ex:correct, -)
50 | # used(ex:compose, ex:regionList, -, [prov:role = "ex:regionsToAggregateBy"])
51 | # used(ex:illustrate, ex:composition, -)
52 | # wasGeneratedBy(ex:composition, ex:compose, -)
53 | # wasAttributedTo(ex:chart1, ex:derek)
54 | # wasGeneratedBy(ex:chart1, ex:compile, 2012 - 03 - 02
55 | # T10:30:00)
56 | # wasGeneratedBy(ex:chart1, ex:illustrate, -)
57 | # wasAssociatedWith(ex:compose, ex:derek, -)
58 | # wasAssociatedWith(ex:illustrate, ex:derek, -)
59 | # actedOnBehalfOf(ex:derek, ex:chartgen, ex:compose)
60 | # entity(ex:article, [dcterms:title = "Crime rises in cities"])
61 | # entity(ex:articleV1)
62 | # entity(ex:articleV2)
63 | # entity(ex:dataSet1)
64 | # entity(ex:dataSet2)
65 | # entity(ex:regionList)
66 | # entity(ex:composition)
67 | # entity(ex:chart1)
68 | # entity(ex:chart2)
69 | # entity(ex:blogEntry)
70 | # activity(ex:compile, -, -)
71 | # activity(ex:compile2, -, -)
72 | # activity(ex:compose, -, -)
73 | # activity(ex:correct, 2012 - 03 - 31
74 | # T09:21:00, 2012 - 04 - 01
75 | # T15:21:00)
76 | # activity(ex:illustrate, -, -)
77 | # agent(ex:derek, [foaf:mbox = "", foaf:givenName = "Derek", prov:type = 'prov:Person'])
78 | # agent(ex:chartgen, [foaf:name = "Chart Generators Inc", prov:type = 'prov:Organization'])
79 | # endDocument
80 |
--------------------------------------------------------------------------------
/docs/_images/test_cases/test_11_get_records_tail.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/_images/test_cases/test_9_get_records_by_filter_with_properties.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/provdbconnector/tests/db_adapters/neo4j/test_neo4jadapter.py:
--------------------------------------------------------------------------------
1 | import unittest
2 |
3 | from provdbconnector.exceptions.database import InvalidOptionsException, AuthException
4 | from provdbconnector import Neo4jAdapter, NEO4J_USER, NEO4J_PASS, NEO4J_HOST, NEO4J_BOLT_PORT
5 | from provdbconnector.prov_db import ProvDb
6 | from provdbconnector.tests import AdapterTestTemplate
7 | from provdbconnector.tests import ProvDbTestTemplate
8 |
9 |
10 | class Neo4jAdapterTests(AdapterTestTemplate):
11 | """
12 | This test extends from AdapterTestTemplate and provide a common set for the neo4j adapter
13 | """
14 | def setUp(self):
15 | """
16 | Setup the test
17 | """
18 | self.instance = Neo4jAdapter()
19 | auth_info = {"user_name": NEO4J_USER,
20 | "user_password": NEO4J_PASS,
21 | "host": NEO4J_HOST + ":" + NEO4J_BOLT_PORT
22 | }
23 | self.instance.connect(auth_info)
24 | session = self.instance._create_session()
25 | session.run("MATCH (x) DETACH DELETE x")
26 |
27 | @unittest.skip(
28 | "Skipped because the server configuration currently is set to 'no password', so the authentication will never fail")
29 | def test_connect_fails(self):
30 | """
31 | Try to connect with the wrong password
32 | """
33 | auth_info = {"user_name": NEO4J_USER,
34 | "user_password": 'xxxxxx',
35 | "host": NEO4J_HOST + ":" + NEO4J_BOLT_PORT
36 | }
37 | self.instance.connect(auth_info)
38 | with self.assertRaises(AuthException):
39 | self.instance.connect(auth_info)
40 |
41 | def test_connect_invalid_options(self):
42 | """
43 | Try to connect with some invalid arguments
44 | """
45 | auth_info = {"u": NEO4J_USER,
46 | "p": 'xxxxxx',
47 | "h": NEO4J_HOST + ":" + NEO4J_BOLT_PORT
48 | }
49 | with self.assertRaises(InvalidOptionsException):
50 | self.instance.connect(auth_info)
51 |
52 | def tearDown(self):
53 | """
54 | Delete all data on the database
55 | :return:
56 | """
57 | session = self.instance._create_session()
58 | session.run("MATCH (x) DETACH DELETE x")
59 | del self.instance
60 |
61 |
62 | class Neo4jAdapterProvDbTests(ProvDbTestTemplate):
63 | """
64 | High level api test for the neo4j adapter
65 | """
66 | def setUp(self):
67 | self.auth_info = {"user_name": NEO4J_USER,
68 | "user_password": NEO4J_PASS,
69 | "host": NEO4J_HOST + ":" + NEO4J_BOLT_PORT
70 | }
71 | self.provapi = ProvDb(api_id=1, adapter=Neo4jAdapter, auth_info=self.auth_info)
72 |
73 | def clear_database(self):
74 | """
75 | This function get called before each test starts
76 |
77 | """
78 | session = self.provapi._adapter._create_session()
79 | session.run("MATCH (x) DETACH DELETE x")
80 |
81 | def tearDown(self):
82 | """
83 | Delete all data in the database
84 | """
85 | session = self.provapi._adapter._create_session()
86 | session.run("MATCH (x) DETACH DELETE x")
87 | del self.provapi
88 |
--------------------------------------------------------------------------------
/docs/_images/test_cases/test_13_get_bundle_records.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/development.rst:
--------------------------------------------------------------------------------
1 | Development
2 | ===========
3 |
4 | Contribute
5 | ----------
6 |
7 | Please, fork the code on Github and develop your feature in a new branch split from the develop branch.
8 | Commit your code to the main project by sending a pull request onto the develop branch
9 |
10 | * Issue Tracker: https://github.com/DLR-SC/prov-db-connector/issues
11 | * Source Code: https://github.com/DLR-SC/prov-db-connector
12 |
13 | Setup
14 | -----
15 |
16 | .. code:: sh
17 |
18 | # Clone project
19 | git clone git@github.com:DLR-SC/prov-db-connector.git
20 | cd prov-db-connector
21 |
22 | # Setup virtual environment
23 | virtualenv -p /usr/bin/python3.4 env
24 | source env/bin/activate
25 |
26 | # Install dependencies
27 | make dev-setup
28 |
29 | Execute tests
30 | -------------
31 |
32 | .. code:: sh
33 |
34 | make test
35 |
36 | Coverage report
37 | ---------------
38 |
39 | .. code:: sh
40 |
41 | make coverage
42 |
43 | Compile documentation
44 | ---------------------
45 |
46 | .. code:: sh
47 |
48 | make docs
49 |
50 | Create new database adapters
51 | ----------------------------
52 |
53 | The database adapters are the binding class to the actual database.
54 | If you are consider to build your own adapter please keep in mind:
55 |
56 | * All adapters **must** enhance the :py:class:`~provdbconnector.db_adapters.baseadapter.Baseadapter` class.
57 | * You **must** implement all specified functions in BaseAdapter
58 | * You **should** test it via the :py:class:`~provdbconnector.tests.db_adapters.test_baseadapter.AdapterTestTemplate` class template.
59 | * You **should** test it also via the :py:class:`~provdbconnector.tests.test_provapi.ProvDbTestTemplate` class template.
60 |
61 | 1. - Create your database adapter
62 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
63 |
64 | First you must create a class that extend from :py:class:`~provdbconnector.db_adapters.baseadapter.Baseadapter` and implement all functions.
65 |
66 | .. literalinclude:: ../provdbconnector/db_adapters/in_memory/simple_in_memory.py
67 | :linenos:
68 | :lines: 1-30
69 | :language: python
70 | :emphasize-lines: 26-30
71 |
72 | 2. - Create test suites
73 | ~~~~~~~~~~~~~~~~~~~~~~~
74 |
75 | To test your adapter you should create two test suits:
76 |
77 | * :py:class:`~provdbconnector.tests.db_adapters.in_memory.test_simple_in_memory.SimpleInMemoryAdapterTest` : Unit test for the low level functions in your adapter.
78 | * For further introduction on testing your database adapter have a look at the :ref:`test_howto`.
79 | * :py:class:`~provdbconnector.tests.db_adapters.in_memory.test_simple_in_memory.SimpleInMemoryAdapterProvDbTests` : Integration test for the adapter with the api.
80 |
81 | See this example tests for the :py:class:`~provdbconnector.db_adapters.in_memory.simple_in_memory.SimpleInMemoryAdapter`
82 |
83 | .. literalinclude:: ../provdbconnector/tests/db_adapters/in_memory/test_simple_in_memory.py
84 | :linenos:
85 | :language: python
86 |
87 |
88 | 3. - Implement your adapter logic
89 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
90 |
91 | The last step is to create your logic inside the :py:class:`~provdbconnector.db_adapters.in_memory.simple_in_memory.SimpleInMemoryAdapter` for example the save_record and get_record functions:
92 |
93 | Now you are ready to implement all other functions.
94 |
95 | .. note::
96 | If you don't know where should you start
97 | Start with the first test and try to implement functions successively according to the tests
98 | and look into the documentation of the :py:class:`~provdbconnector.tests.db_adapters.test_baseadapter.AdapterTestTemplate`
99 |
100 | .. literalinclude:: ../provdbconnector/db_adapters/in_memory/simple_in_memory.py
101 | :linenos:
102 | :lines: 32-56, 105-115
103 | :language: python
104 |
105 |
--------------------------------------------------------------------------------
/provdbconnector/utils/converter.py:
--------------------------------------------------------------------------------
1 | from functools import reduce
2 | from io import BufferedReader
3 | from provdbconnector.exceptions.utils import ParseException, NoDocumentException
4 |
5 | import six
6 | from prov.model import ProvDocument
7 |
8 | import logging
9 | log = logging.getLogger(__name__)
10 |
11 |
12 | def form_string(content):
13 | """
14 | Take a string or BufferedReader as argument and transform the string into a ProvDocument
15 |
16 | :param content: Takes a sting or BufferedReader
17 | :return: ProvDocument
18 | """
19 | if isinstance(content, ProvDocument):
20 | return content
21 | elif isinstance(content, BufferedReader):
22 | content = reduce(lambda total, a: total + a, content.readlines())
23 |
24 | if type(content) is six.binary_type:
25 | content_str = content[0:15].decode()
26 | if content_str.find("{") > -1:
27 | return ProvDocument.deserialize(content=content, format='json')
28 | if content_str.find(' -1:
29 | return ProvDocument.deserialize(content=content, format='xml')
30 | elif content_str.find('document') > -1:
31 | return ProvDocument.deserialize(content=content, format='provn')
32 |
33 | raise ParseException("Unsupported input type {}".format(type(content)))
34 |
35 |
36 | def to_json(document=None):
37 | """
38 | Try to convert a ProvDocument into the json representation
39 |
40 | :param document:
41 | :type document: prov.model.ProvDocument
42 | :return: Json string of the document
43 | :rtype: str
44 | """
45 | if document is None:
46 | raise NoDocumentException()
47 | return document.serialize(format='json')
48 |
49 |
50 | def from_json(json=None):
51 | """
52 | Try to convert a json string into a document
53 |
54 | :param json: The json str
55 | :type json: str
56 | :return: Prov Document
57 | :rtype: prov.model.ProvDocument
58 | :raise: NoDocumentException
59 | """
60 | if json is None:
61 | raise NoDocumentException()
62 | return ProvDocument.deserialize(source=json, format='json')
63 |
64 |
65 | def to_provn(document=None):
66 | """
67 | Try to convert a document into a provn representation
68 |
69 | :param document: Prov document to convert
70 | :type document: prov.model.ProvDocument
71 | :return: The prov-n str
72 | :rtype: str
73 | :raise: NoDocumentException
74 | """
75 | if document is None:
76 | raise NoDocumentException()
77 | return document.serialize(format='provn')
78 |
79 |
80 | def from_provn(provn_str=None):
81 | """
82 | Try to convert a provn string into a ProvDocument
83 |
84 | :param provn_str: The string to convert
85 | :type provn_str: str
86 | :return: The Prov document
87 | :rtype: ProvDocument
88 | :raises: NoDocumentException
89 | """
90 | if provn_str is None:
91 | raise NoDocumentException()
92 | return ProvDocument.deserialize(source=provn_str, format='provn')
93 |
94 |
95 | def to_xml(document=None):
96 | """
97 | Try to convert a document into an xml string
98 |
99 | :param document: The ProvDocument to convert
100 | :param document: ProvDocument
101 | :return: The xml string
102 | :rtype: str
103 | """
104 | if document is None:
105 | raise NoDocumentException()
106 | return document.serialize(format='xml')
107 |
108 |
109 | def from_xml(xml_str=None):
110 | """
111 | Try to convert a xml string into a ProvDocument
112 |
113 | :param xml_str: The xml string
114 | :type xml_str: str
115 | :return: The Prov document
116 | :rtype: ProvDocument
117 | """
118 | if xml_str is None:
119 | raise NoDocumentException()
120 | return ProvDocument.deserialize(source=xml_str, format='xml')
121 |
--------------------------------------------------------------------------------
/provdbconnector/tests/utils/test_converter.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | from xml.etree import ElementTree
3 | import pkg_resources
4 | import json
5 |
6 | from prov.model import ProvDocument
7 | from prov.tests import examples
8 | from provdbconnector.utils.converter import to_json, from_json, to_provn, from_provn, to_xml, from_xml, form_string
9 | from provdbconnector.exceptions.utils import NoDocumentException, ParseException
10 |
11 |
12 | class ConverterTests(unittest.TestCase):
13 | """
14 | Test the convert class
15 | """
16 |
17 | def setUp(self):
18 | # Reading testfiles from prov package according to:
19 | # http://stackoverflow.com/questions/6028000/python-how-to-read-a-static-file-from-inside-a-package
20 | #
21 | # Assuming your template is located inside your module's package at this path:
22 | # /templates/temp_file
23 | # the correct way to read your template is to use pkg_resources package from setuptools distribution:
24 | test_resources = {
25 | 'xml': {'package': 'provdbconnector', 'file': '/tests/resources/primer.provx'},
26 | 'json': {'package': 'provdbconnector', 'file': '/tests/resources/primer.json'},
27 | 'provn': {'package': 'provdbconnector', 'file': '/tests/resources/primer.provn'}
28 | }
29 | self.test_files = dict(
30 | (key, pkg_resources.resource_stream(val['package'], val['file'])) for key, val in test_resources.items())
31 | self.prov_document = examples.primer_example()
32 |
33 | def tearDown(self):
34 | """
35 | Close all files
36 | """
37 | [self.test_files[k].close() for k in self.test_files.keys()]
38 |
39 | def test_form_string(self):
40 | """
41 | Test the convert from string
42 | """
43 | result = form_string(self.test_files["json"])
44 | self.assertIsNotNone(result)
45 | self.assertIsInstance(result, ProvDocument)
46 |
47 | result = form_string(self.test_files["xml"])
48 | self.assertIsNotNone(result)
49 | self.assertIsInstance(result, ProvDocument)
50 |
51 | with self.assertRaises(NotImplementedError):
52 | form_string(self.test_files["provn"])
53 |
54 | with self.assertRaises(ParseException):
55 | form_string("A funny string but no xml, json or prov string")
56 |
57 | def test_to_json(self):
58 | """
59 | Test the convert to json
60 | """
61 | self.assertRaises(NoDocumentException, lambda: to_json())
62 | json_document = to_json(self.prov_document)
63 | self.assertIsInstance(json_document, str)
64 | try:
65 | json.loads(json_document)
66 | except ValueError:
67 | self.fail("Invalid JSON")
68 |
69 | def test_from_json(self):
70 | """
71 | Test the convert from json
72 | """
73 | self.assertRaises(NoDocumentException, lambda: from_json())
74 | prov = from_json(self.test_files['json'])
75 | self.assertIsInstance(prov, ProvDocument)
76 |
77 | def test_to_provn(self):
78 | """
79 | Test the convert to prov-n
80 | """
81 | self.assertRaises(NoDocumentException, lambda: to_provn())
82 | provn_document = to_provn(self.prov_document)
83 | self.assertIsInstance(provn_document, str)
84 | # Validate that string is in provn format
85 |
86 | def test_from_provn(self):
87 | """
88 | Test the convert from prov-n
89 | """
90 | self.assertRaises(NoDocumentException, lambda: from_provn())
91 | # currently the prov lib don't support from_provn
92 |
93 | with self.assertRaises(NotImplementedError):
94 | from_provn(self.test_files['provn'])
95 |
96 | # self.assertIsInstance(prov, ProvDocument)
97 |
98 | def test_to_xml(self):
99 | """
100 | Test the convert to xml
101 | """
102 | self.assertRaises(NoDocumentException, lambda: to_xml())
103 | xml_document = to_xml(self.prov_document)
104 | self.assertIsInstance(xml_document, str)
105 | ElementTree.fromstring(xml_document)
106 |
107 | def test_from_xml(self):
108 | """
109 | Test the convert from xml
110 | """
111 | self.assertRaises(NoDocumentException, lambda: from_xml())
112 | prov = from_xml(self.test_files['xml'])
113 | self.assertIsInstance(prov, ProvDocument)
114 |
--------------------------------------------------------------------------------
/docs/changelog.rst:
--------------------------------------------------------------------------------
1 | Changelog
2 | =========
3 | Version 0.3.x
4 | -----------
5 |
6 | - Upgraded prov to 1.5.3 .. #73: https://github.com/DLR-SC/prov-db-connector/pull/73
7 | - Upgraded neo4j-driver to 1.7.0 .. #70: https://github.com/DLR-SC/prov-db-connector/pull/70
8 |
9 |
10 | Version 0.3.1
11 | -----------
12 |
13 | - Upgraded neo4j-driver to 1.6.2 .. #67: https://github.com/DLR-SC/prov-db-connector/pull/67
14 | - Enhanced error handling neo4j-adapater
15 | - Automatic pipi release on git tag
16 |
17 | Version 0.3
18 | -----------
19 |
20 | - **Changed ``provdb.create_*`` to ``provdb.save_*``** because we can’t
21 | guarantee that the db-adapter actual create a new node, document,
22 | relation. Maybe the adapter merges your properties into existing
23 | data, behavior is still the same.
24 | - **Renamed files provDb.py into prov\_db.py**
25 | - Enhanced the ``prov:Mention`` support. If you create a bundle link
26 | (``prov:Mention``) the destination bundle entity will be
27 | automatically created. For example: \`\`\`python
28 |
29 | from prov.tests.examples import bundles2
30 |
31 | doc = bundles2() bundle = list(doc.get\_records()).pop() #I know, the
32 | get\_record function return a set, so it can happen that you get the
33 | wrong bundle here (alice:bundle5 is correct)
34 | prov\_api.save\_bundle(bundle) \`\`\`
35 |
36 | - Add ability to save relations between elements that doesn’t exist.
37 | For example, on a empty database:
38 |
39 | .. code:: python
40 |
41 | doc = ProvDocument()
42 | relation = doc.wasGeneratedBy("ex:Entity", "ex:Activity")
43 |
44 | #Works now fine. The ex:entity and ex:Activity elements will be created automatically
45 | provapi.save_relation(relation)
46 |
47 | - Removed node type “Unknown” for relations with unknown nodes. (The
48 | prov-db-adapter now detects which type the relation implicitly mean.
49 |
50 | .. code:: python
51 |
52 | doc = ProvDocument()
53 | relation = doc.wasGeneratedBy("ex:Entity", -)
54 |
55 | # Creates a Activity with a random identifier as destions for the relation
56 | provapi.save_relation(relation)
57 |
58 | - Introduced new methods
59 |
60 | **prov\_db.save\_relation(prov\_relation)**
61 |
62 | .. code:: python
63 |
64 |
65 | doc = ProvDocument()
66 |
67 | activity = doc.activity("ex:yourActivity")
68 | entity = doc.entity("ex:yourEntity")
69 | wasGeneratedBy = entity.wasGeneratedBy("ex:yourAgent")
70 |
71 | # Save the elements
72 | rel_id = prov_db.save_relation(wasGeneratedBy)
73 |
74 | **prov\_db.save\_element(prov\_element, [bundle\_id])**
75 |
76 | .. code:: python
77 |
78 |
79 | doc = ProvDocument()
80 |
81 | agent = doc.agent("ex:yourAgent")
82 | activity = doc.activity("ex:yourActivity")
83 | entity = doc.entity("ex:yourEntity")
84 |
85 | # Save the elements
86 | agent_id = prov_db.save_element(agent)
87 | activity_id = prov_db.save_element(activity)
88 | entity_id = prov_db.save_element(entity)
89 |
90 | **prov\_db.get\_element(identifier)**
91 |
92 | .. code:: python
93 |
94 |
95 | doc = ProvDocument()
96 |
97 | identifier = QualifiedName(doc, "ex:yourAgent")
98 |
99 | prov_element = prov_db.get_element(identifier)
100 |
101 | **prov\_db.save\_record(prov\_record, [bundle\_id])**
102 |
103 | .. code:: python
104 |
105 |
106 | doc = ProvDocument()
107 |
108 | agent = doc.agent("ex:Alice")
109 | ass_rel = doc.association("ex:Alice", "ex:Bob")
110 |
111 | # Save the elements
112 | agent_id = prov_db.save_record(agent)
113 | relation_id = prov_db.save_record(ass_rel)
114 |
115 | **prov\_api.save\_bundle(prov\_bundle)**
116 |
117 | .. code:: python
118 |
119 |
120 | doc = ProvDocument()
121 |
122 | bundle = doc.bundle("ex:bundle1")
123 | # Save the bundle
124 | prov_db.save_bundle(bundle)
125 |
126 | **prov\_db.get\_elements([ProvCLS])**
127 |
128 | .. code:: python
129 |
130 | from prov.model import ProvEntity, ProvAgent, ProvActivity
131 |
132 | document_with_all_entities = prov_db.get_elements(ProvEntity)
133 | document_with_all_agents = prov_db.get_elements(ProvAgent)
134 | document_with_all_activities = prov_db.get_elements(ProvActivity)
135 |
136 | print(document_with_all_entities)
137 | print(document_with_all_agents)
138 | print(document_with_all_activities)
139 |
140 | **prov\_db.get\_bundle(identifier)**
141 |
142 | .. code:: python
143 |
144 | doc = ProvDocument()
145 | bundle_name = doc.valid_qualified_name("ex:YourBundleName")
146 | # get the bundle
147 | prov_bundle = prov_db.get_bundle(bundle_name)
148 | doc.add_bundle(prov_bundle)
149 |
--------------------------------------------------------------------------------
/provdbconnector/db_adapters/neo4j/cypher_commands.py:
--------------------------------------------------------------------------------
1 | NEO4J_TEST_CONNECTION = """MATCH (n) RETURN count(n) as count"""
2 |
3 | # create
4 | NEO4J_CREATE_DOCUMENT_NODE_RETURN_ID = """
5 | CYPHER 3.5
6 | CREATE (node { }) RETURN ID(node) as ID"""
7 | NEO4J_CREATE_NODE_SET_PART = "SET node.`{attr_name}` = {{`{attr_name}`}}"
8 | NEO4J_CREATE_NODE_SET_PART_MERGE_ATTR = "SET node.`{attr_name}` = (CASE WHEN not exists(node.`{attr_name}`) THEN [{{`{attr_name}`}}] ELSE node.`{attr_name}` + {{`{attr_name}`}} END)"
9 | NEO4J_CREATE_NODE_MERGE_CHECK_PART = """WITH CASE WHEN check = 0 THEN (CASE WHEN EXISTS(node.`{attr_name}`) AND node.`{attr_name}` <> {{`{attr_name}`}} THEN 1 ELSE 0 END) ELSE 1 END as check , node"""
10 | NEO4J_CREATE_NODE_RETURN_ID = """
11 | CYPHER 3.5
12 | MERGE (node:{label} {{{formal_attributes}}})
13 | WITH 0 as check, node
14 | {merge_check_statement}
15 | {set_statement}
16 | RETURN ID(node) as ID, check """ # args: provType, values
17 | NEO4J_CREATE_RELATION_RETURN_ID = """
18 | CYPHER 3.5
19 | MATCH
20 | (from{{`meta:identifier`:'{from_identifier}'}}),
21 | (to{{`meta:identifier`:'{to_identifier}'}})
22 | MERGE
23 | (from)-[r:{relation_type} {{{formal_attributes}}}]->(to)
24 | WITH 0 as check, r as node
25 | {merge_check_statement}
26 | {set_statement}
27 | RETURN
28 | ID(node) as ID, check
29 | """ # args: provType, values
30 | # get
31 | NEO4J_GET_RECORDS_BY_PROPERTY_DICT = """
32 | CYPHER 3.5
33 | MATCH (d {{{filter_dict}}} )-[r]-(x {{{filter_dict}}})
34 | RETURN DISTINCT r as re
35 | //Get all nodes that are alone without connections to other nodes
36 | UNION
37 | MATCH (a {{{filter_dict}}})
38 | RETURN DISTINCT a as re
39 | """
40 | NEO4J_GET_RECORDS_TAIL_BY_FILTER = """
41 | CYPHER 3.5
42 | MATCH (x {{{filter_dict}}})-[r *{depth}]-(y)
43 | RETURN DISTINCT y as re
44 | UNION
45 | MATCH (x {{{filter_dict}}})-[r *{depth}]-(y)
46 | WITH REDUCE(output = [], r IN r | output + r) AS flat
47 | UNWIND flat as re
48 | RETURN DISTINCT re
49 | """
50 |
51 | NEO4J_GET_BUNDLE_RECORDS = """
52 | CYPHER 3.5
53 | MATCH (x {`meta:identifier`: {`meta:identifier`}})-[r *1]-(y)
54 | WHERE ALL (rel in r WHERE rel.`prov:type` = 'prov:bundleAssociation')
55 | RETURN DISTINCT y as re
56 | UNION
57 | //get all relations between the nodes
58 | MATCH (origin {`meta:identifier`: {`meta:identifier`}})-[r *1]-(x)-[r_return *1]-(y)-[r_2 *1]-(origin {`meta:identifier`: {`meta:identifier`}})
59 | WHERE ALL (rel in r WHERE rel.`prov:type` = 'prov:bundleAssociation')
60 | AND ALL (rel in r_2 WHERE rel.`prov:type` = 'prov:bundleAssociation')
61 | WITH REDUCE(output = [], r IN r_return | output + r) AS flat
62 | UNWIND flat as re
63 | RETURN DISTINCT re
64 | //get all mentionof relations
65 | UNION
66 | MATCH (bundle_1 {`meta:identifier`: {`meta:identifier`}})-[r *1]-(x)-[r_return *1]-(y)-[r_2 *1]-(bundle_2)
67 | WHERE ALL (rel in r WHERE rel.`prov:type` = 'prov:bundleAssociation')
68 | AND ALL (rel in r_2 WHERE rel.`prov:type` = 'prov:bundleAssociation')
69 | AND ALL (rel in r_return WHERE rel.`meta:prov_type` = 'prov:Mention' and startNode(rel) = x)
70 | WITH REDUCE(output = [], r IN r_return | output + r) AS flat
71 | UNWIND flat as re
72 | RETURN DISTINCT re
73 |
74 | """
75 |
76 | NEO4J_GET_RECORD_RETURN_NODE = """
77 | CYPHER 3.5
78 | MATCH (node) WHERE ID(node)={record_id} RETURN node"""
79 | NEO4J_GET_RELATION_RETURN_NODE = """
80 | CYPHER 3.5
81 | MATCH ()-[relation]-() WHERE ID(relation)={relation_id} RETURN relation"""
82 |
83 | # delete
84 | NEO4J_DELETE__NODE_BY_ID = """
85 | CYPHER 3.5
86 | MATCH (x) Where ID(x) = {node_id} DETACH DELETE x """
87 | NEO4J_DELETE_NODE_BY_PROPERTIES = """
88 | CYPHER 3.5
89 | MATCH (n {{{filter_dict}}}) DETACH DELETE n"""
90 | NEO4J_DELETE_BUNDLE_NODE_BY_ID = """
91 | CYPHER 3.5
92 | MATCH (b) WHERE id(b)=toInt({bundle_id}) DELETE b """
93 | NEO4J_DELETE_RELATION_BY_ID = """
94 | CYPHER 3.5
95 | MATCH ()-[r]-() WHERE id(r) = {relation_id} DELETE r"""
96 |
--------------------------------------------------------------------------------
/examples/file_buffer_example_primer.json:
--------------------------------------------------------------------------------
1 | {
2 | "wasAssociatedWith": {
3 | "_:wAW3963": {
4 | "prov:activity": "ex:illustrate",
5 | "prov:agent": "ex:derek"
6 | },
7 | "_:wAW3962": {
8 | "prov:activity": "ex:compose",
9 | "prov:agent": "ex:derek"
10 | }
11 | },
12 | "specializationOf": {
13 | "_:sO11133": {
14 | "prov:generalEntity": "ex:article",
15 | "prov:specificEntity": "ex:articleV2"
16 | },
17 | "_:sO11132": {
18 | "prov:generalEntity": "ex:article",
19 | "prov:specificEntity": "ex:articleV1"
20 | }
21 | },
22 | "wasAttributedTo": {
23 | "_:wAT3855": {
24 | "prov:agent": "ex:derek",
25 | "prov:entity": "ex:chart1"
26 | }
27 | },
28 | "wasGeneratedBy": {
29 | "_:wGB7794": {
30 | "prov:activity": "ex:compile2",
31 | "prov:time": "2012-04-01T15:21:00.000+01:00",
32 | "prov:entity": "ex:chart2"
33 | },
34 | "_:wGB7795": {
35 | "prov:activity": "ex:correct",
36 | "prov:entity": "ex:dataSet2"
37 | },
38 | "_:wGB7792": {
39 | "prov:activity": "ex:illustrate",
40 | "prov:entity": "ex:chart1"
41 | },
42 | "_:wGB7793": {
43 | "prov:activity": "ex:compile",
44 | "prov:time": "2012-03-02T10:30:00.000Z",
45 | "prov:entity": "ex:chart1"
46 | },
47 | "_:wGB7791": {
48 | "prov:activity": "ex:compose",
49 | "prov:entity": "ex:composition"
50 | }
51 | },
52 | "entity": {
53 | "ex:composition": {},
54 | "ex:regionList": {},
55 | "ex:blogEntry": {},
56 | "ex:chart1": {},
57 | "ex:dataSet2": {},
58 | "ex:chart2": {},
59 | "ex:dataSet1": {},
60 | "ex:article": {
61 | "dcterms:title": {
62 | "$": "Crime rises in cities",
63 | "type": "xsd:string"
64 | }
65 | },
66 | "ex:articleV2": {},
67 | "ex:articleV1": {}
68 | },
69 | "prefix": {
70 | "xsd": "http://www.w3.org/2001/XMLSchema#",
71 | "foaf": "http://xmlns.com/foaf/0.1/",
72 | "prov": "http://www.w3.org/ns/prov#",
73 | "ex": "http://example/",
74 | "dcterms": "http://purl.org/dc/terms/"
75 | },
76 | "alternateOf": {
77 | "_:aO49": {
78 | "prov:alternate2": "ex:articleV2",
79 | "prov:alternate1": "ex:articleV1"
80 | }
81 | },
82 | "wasDerivedFrom": {
83 | "_:wDF21119": {
84 | "prov:generatedEntity": "ex:articleV2",
85 | "prov:usedEntity": "ex:dataSet2"
86 | },
87 | "_:wDF21116": {
88 | "prov:generatedEntity": "ex:chart2",
89 | "prov:usedEntity": "ex:dataSet2"
90 | },
91 | "_:wDF21115": {
92 | "prov:generatedEntity": "ex:dataSet2",
93 | "prov:type": {
94 | "$": "prov:Revision",
95 | "type": "prov:QUALIFIED_NAME"
96 | },
97 | "prov:usedEntity": "ex:dataSet1"
98 | },
99 | "_:wDF21118": {
100 | "prov:generatedEntity": "ex:articleV1",
101 | "prov:usedEntity": "ex:dataSet1"
102 | },
103 | "_:wDF21117": {
104 | "prov:generatedEntity": "ex:blogEntry",
105 | "prov:type": {
106 | "$": "prov:Quotation",
107 | "type": "prov:QUALIFIED_NAME"
108 | },
109 | "prov:usedEntity": "ex:article"
110 | }
111 | },
112 | "used": {
113 | "_:u6219": {
114 | "prov:activity": "ex:compose",
115 | "prov:role": {
116 | "$": "ex:dataToCompose",
117 | "type": "prov:QUALIFIED_NAME"
118 | },
119 | "prov:entity": "ex:dataSet1"
120 | },
121 | "_:u6217": {
122 | "prov:activity": "ex:compose",
123 | "prov:entity": "ex:regionList"
124 | },
125 | "_:u6218": {
126 | "prov:activity": "ex:illustrate",
127 | "prov:entity": "ex:composition"
128 | },
129 | "_:u6216": {
130 | "prov:activity": "ex:compose",
131 | "prov:entity": "ex:dataSet1"
132 | },
133 | "_:u6221": {
134 | "prov:activity": "ex:correct",
135 | "prov:entity": "ex:dataSet1"
136 | },
137 | "_:u6220": {
138 | "prov:activity": "ex:compose",
139 | "prov:role": {
140 | "$": "ex:regionsToAggregateBy",
141 | "type": "prov:QUALIFIED_NAME"
142 | },
143 | "prov:entity": "ex:regionList"
144 | }
145 | },
146 | "agent": {
147 | "ex:derek": {
148 | "prov:type": {
149 | "$": "prov:Person",
150 | "type": "prov:QUALIFIED_NAME"
151 | },
152 | "foaf:givenName": {
153 | "$": "Derek",
154 | "type": "xsd:string"
155 | },
156 | "foaf:mbox": {
157 | "$": "\u003cmailto:derek@example.org\u003e",
158 | "type": "xsd:string"
159 | }
160 | },
161 | "ex:chartgen": {
162 | "prov:type": {
163 | "$": "prov:Organization",
164 | "type": "prov:QUALIFIED_NAME"
165 | },
166 | "foaf:name": {
167 | "$": "Chart Generators Inc",
168 | "type": "xsd:string"
169 | }
170 | }
171 | },
172 | "actedOnBehalfOf": {
173 | "_:aOBO259": {
174 | "prov:activity": "ex:compose",
175 | "prov:delegate": "ex:derek",
176 | "prov:responsible": "ex:chartgen"
177 | }
178 | },
179 | "activity": {
180 | "ex:illustrate": {},
181 | "ex:correct": {
182 | "prov:startTime": "2012-03-31T09:21:00.000+01:00",
183 | "prov:endTime": "2012-04-01T15:21:00.000+01:00"
184 | },
185 | "ex:compile2": {},
186 | "ex:compile": {},
187 | "ex:compose": {}
188 | }
189 | }
--------------------------------------------------------------------------------
/provdbconnector/tests/resources/primer.json:
--------------------------------------------------------------------------------
1 | {
2 | "wasAssociatedWith": {
3 | "_:wAW3963": {
4 | "prov:activity": "ex:illustrate",
5 | "prov:agent": "ex:derek"
6 | },
7 | "_:wAW3962": {
8 | "prov:activity": "ex:compose",
9 | "prov:agent": "ex:derek"
10 | }
11 | },
12 | "specializationOf": {
13 | "_:sO11133": {
14 | "prov:generalEntity": "ex:article",
15 | "prov:specificEntity": "ex:articleV2"
16 | },
17 | "_:sO11132": {
18 | "prov:generalEntity": "ex:article",
19 | "prov:specificEntity": "ex:articleV1"
20 | }
21 | },
22 | "wasAttributedTo": {
23 | "_:wAT3855": {
24 | "prov:agent": "ex:derek",
25 | "prov:entity": "ex:chart1"
26 | }
27 | },
28 | "wasGeneratedBy": {
29 | "_:wGB7794": {
30 | "prov:activity": "ex:compile2",
31 | "prov:time": "2012-04-01T15:21:00.000+01:00",
32 | "prov:entity": "ex:chart2"
33 | },
34 | "_:wGB7795": {
35 | "prov:activity": "ex:correct",
36 | "prov:entity": "ex:dataSet2"
37 | },
38 | "_:wGB7792": {
39 | "prov:activity": "ex:illustrate",
40 | "prov:entity": "ex:chart1"
41 | },
42 | "_:wGB7793": {
43 | "prov:activity": "ex:compile",
44 | "prov:time": "2012-03-02T10:30:00.000Z",
45 | "prov:entity": "ex:chart1"
46 | },
47 | "_:wGB7791": {
48 | "prov:activity": "ex:compose",
49 | "prov:entity": "ex:composition"
50 | }
51 | },
52 | "entity": {
53 | "ex:composition": {},
54 | "ex:regionList": {},
55 | "ex:blogEntry": {},
56 | "ex:chart1": {},
57 | "ex:dataSet2": {},
58 | "ex:chart2": {},
59 | "ex:dataSet1": {},
60 | "ex:article": {
61 | "dcterms:title": {
62 | "$": "Crime rises in cities",
63 | "type": "xsd:string"
64 | }
65 | },
66 | "ex:articleV2": {},
67 | "ex:articleV1": {}
68 | },
69 | "prefix": {
70 | "xsd": "http://www.w3.org/2001/XMLSchema#",
71 | "foaf": "http://xmlns.com/foaf/0.1/",
72 | "prov": "http://www.w3.org/ns/prov#",
73 | "ex": "http://example/",
74 | "dcterms": "http://purl.org/dc/terms/"
75 | },
76 | "alternateOf": {
77 | "_:aO49": {
78 | "prov:alternate2": "ex:articleV2",
79 | "prov:alternate1": "ex:articleV1"
80 | }
81 | },
82 | "wasDerivedFrom": {
83 | "_:wDF21119": {
84 | "prov:generatedEntity": "ex:articleV2",
85 | "prov:usedEntity": "ex:dataSet2"
86 | },
87 | "_:wDF21116": {
88 | "prov:generatedEntity": "ex:chart2",
89 | "prov:usedEntity": "ex:dataSet2"
90 | },
91 | "_:wDF21115": {
92 | "prov:generatedEntity": "ex:dataSet2",
93 | "prov:type": {
94 | "$": "prov:Revision",
95 | "type": "prov:QUALIFIED_NAME"
96 | },
97 | "prov:usedEntity": "ex:dataSet1"
98 | },
99 | "_:wDF21118": {
100 | "prov:generatedEntity": "ex:articleV1",
101 | "prov:usedEntity": "ex:dataSet1"
102 | },
103 | "_:wDF21117": {
104 | "prov:generatedEntity": "ex:blogEntry",
105 | "prov:type": {
106 | "$": "prov:Quotation",
107 | "type": "prov:QUALIFIED_NAME"
108 | },
109 | "prov:usedEntity": "ex:article"
110 | }
111 | },
112 | "used": {
113 | "_:u6219": {
114 | "prov:activity": "ex:compose",
115 | "prov:role": {
116 | "$": "ex:dataToCompose",
117 | "type": "prov:QUALIFIED_NAME"
118 | },
119 | "prov:entity": "ex:dataSet1"
120 | },
121 | "_:u6217": {
122 | "prov:activity": "ex:compose",
123 | "prov:entity": "ex:regionList"
124 | },
125 | "_:u6218": {
126 | "prov:activity": "ex:illustrate",
127 | "prov:entity": "ex:composition"
128 | },
129 | "_:u6216": {
130 | "prov:activity": "ex:compose",
131 | "prov:entity": "ex:dataSet1"
132 | },
133 | "_:u6221": {
134 | "prov:activity": "ex:correct",
135 | "prov:entity": "ex:dataSet1"
136 | },
137 | "_:u6220": {
138 | "prov:activity": "ex:compose",
139 | "prov:role": {
140 | "$": "ex:regionsToAggregateBy",
141 | "type": "prov:QUALIFIED_NAME"
142 | },
143 | "prov:entity": "ex:regionList"
144 | }
145 | },
146 | "agent": {
147 | "ex:derek": {
148 | "prov:type": {
149 | "$": "prov:Person",
150 | "type": "prov:QUALIFIED_NAME"
151 | },
152 | "foaf:givenName": {
153 | "$": "Derek",
154 | "type": "xsd:string"
155 | },
156 | "foaf:mbox": {
157 | "$": "\u003cmailto:derek@example.org\u003e",
158 | "type": "xsd:string"
159 | }
160 | },
161 | "ex:chartgen": {
162 | "prov:type": {
163 | "$": "prov:Organization",
164 | "type": "prov:QUALIFIED_NAME"
165 | },
166 | "foaf:name": {
167 | "$": "Chart Generators Inc",
168 | "type": "xsd:string"
169 | }
170 | }
171 | },
172 | "actedOnBehalfOf": {
173 | "_:aOBO259": {
174 | "prov:activity": "ex:compose",
175 | "prov:delegate": "ex:derek",
176 | "prov:responsible": "ex:chartgen"
177 | }
178 | },
179 | "activity": {
180 | "ex:illustrate": {},
181 | "ex:correct": {
182 | "prov:startTime": "2012-03-31T09:21:00.000+01:00",
183 | "prov:endTime": "2012-04-01T15:21:00.000+01:00"
184 | },
185 | "ex:compile2": {},
186 | "ex:compile": {},
187 | "ex:compose": {}
188 | }
189 | }
--------------------------------------------------------------------------------
/provdbconnector/tests/resources/primer.provx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Crime rises in cities
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | 2012-03-31T09:21:00.000+01:00
20 | 2012-04-01T15:21:00.000+01:00
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | 2012-03-02T10:30:00.000Z
47 |
48 |
49 |
50 |
51 | 2012-04-01T15:21:00.000+01:00
52 |
53 |
54 | prov:Person
55 | Derek
56 | <mailto:derek@example.org>
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 | prov:Organization
68 | Chart Generators Inc
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 | ex:dataToCompose
83 |
84 |
85 |
86 |
87 | ex:regionsToAggregateBy
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 | prov:Revision
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | prov:Quotation
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
--------------------------------------------------------------------------------
/docs/_images/test_cases/test_12_get_records_tail_recursive.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/provdbconnector/db_adapters/baseadapter.py:
--------------------------------------------------------------------------------
1 | import logging
2 | from collections import namedtuple
3 |
4 | log = logging.getLogger(__name__).addHandler(logging.NullHandler())
5 |
6 | METADATA_PARENT_ID = "parent_id"
7 | METADATA_KEY_PROV_TYPE = "prov_type"
8 | METADATA_KEY_IDENTIFIER = "identifier"
9 | METADATA_KEY_IDENTIFIER_ORIGINAL = "identifier_original"
10 | METADATA_KEY_NAMESPACES = "namespaces"
11 | METADATA_KEY_TYPE_MAP = "type_map"
12 |
13 | # Return types for adapter classes
14 | DbDocument = namedtuple("DbDocument", "document, bundles")
15 | DbBundle = namedtuple("DbBundle", "records, bundle_record")
16 |
17 | DbRecord = namedtuple("DbRecord", "attributes, metadata")
18 | DbRelation = namedtuple("DbRelation", "attributes, metadata")
19 |
20 |
21 | class BaseAdapter():
22 | """
23 | Interface class for a prov database adapter
24 | """
25 |
26 | def __init__(self, *args, **kwargs):
27 | pass
28 |
29 | def connect(self, authentication_info):
30 | """
31 | Establish the database connection / login into the database
32 |
33 | :param authentication_info: a custom dict with credentials
34 | :type authentication_info: dict
35 | :return: Indicate whether the connection was successful
36 | :rtype: boolean
37 | :raise InvalidOptionsException:
38 | """
39 | raise NotImplementedError("Abstract method")
40 |
41 | def save_element(self, attributes, metadata):
42 | """
43 | Saves a entity, activity or entity into the database
44 |
45 | :param attributes: Attributes as dict for the record. Be careful you have to encode the dict
46 | :type attributes: dict
47 | :param metadata: Metadata as dict for the record. Be careful you have to encode the dict but you can be sure that all meta keys are always there
48 | :type metadata: dict
49 | :return: Record id
50 | :rtype: str
51 | """
52 | raise NotImplementedError("Abstract method")
53 |
54 | def save_relation(self, from_node, to_node, attributes, metadata):
55 | """
56 | Create a relation between 2 nodes
57 |
58 | :param from_node: The identifier
59 | :type from_node: str
60 | :param to_node: The identifier for the destination node
61 | :type: to_node: str
62 | :param attributes: Attributes as dict for the record. Be careful you have to encode the dict
63 | :type attributes: dict
64 | :param metadata: Metadata as dict for the record. Be careful you have to encode the dict but you can be sure that all meta keys are always there
65 | :type metadata: dict
66 | :return: Record id
67 | :rtype: str
68 | """
69 | raise NotImplementedError("Abstract method")
70 |
71 | def get_records_by_filter(self, attributes_dict=None, metadata_dict=None):
72 | """
73 | Returns all records (nodes and relations) based on a filter dict.
74 | The filter dict's are and AND combination but only the start node must fulfill the conditions.
75 | The result should contain all associated relations and nodes together
76 |
77 | :param attributes_dict:
78 | :type attributes_dict: dict
79 | :param metadata_dict:
80 | :type metadata_dict: dict
81 | :return: list of relations and nodes
82 | :rtype: list
83 | """
84 | raise NotImplementedError("Abstract method")
85 |
86 | def get_records_tail(self, attributes_dict=None, metadata_dict=None, depth=None):
87 | """
88 | Returns all connected nodes and relations based on a filter.
89 | The filter is an AND combination and this describes the filter only for the origin nodes.
90 |
91 | :param attributes_dict:
92 | :type attributes_dict: dict
93 | :param metadata_dict:
94 | :type metadata_dict: dict
95 | :param depth:
96 | :type depth: int
97 | :return: a list of relations and nodes
98 | :rtype: list
99 | """
100 | raise NotImplementedError("Abstract method")
101 |
102 | def get_bundle_records(self, bundle_identifier):
103 | """
104 | Returns the relations and nodes for a specific bundle identifier.
105 | Please use the bundle association to get all bundle nodes.
106 | Only the relations belongs to the bundle where the start AND end node belong also to the bundle.
107 | Except the prov:Mention see: W3C bundle links
108 |
109 | :param bundle_identifier: The bundle identifier
110 | :type bundle_identifier: str
111 | :return: list of nodes and bundles
112 | :rtype: list
113 | """
114 | raise NotImplementedError("Abstract method")
115 |
116 | def get_record(self, record_id):
117 | """
118 | Return a single record
119 |
120 | :param record_id: The id
121 | :type record_id: str
122 | :return: DbRecord
123 | :rtype: DbRecord
124 | """
125 | raise NotImplementedError("Abstract method")
126 |
127 | def get_relation(self, relation_id):
128 | """
129 | Returns a single relation
130 |
131 | :param relation_id: The id
132 | :type relation_id: str
133 | :return: DbRelation
134 | :rtype: DbRelation
135 | """
136 | raise NotImplementedError("Abstract method")
137 |
138 | def delete_records_by_filter(self, attributes_dict, metadata_dict):
139 | """
140 | Delete records by filter
141 |
142 | :param attributes_dict:
143 | :type attributes_dict: dict
144 | :param metadata_dict:
145 | :type metadata_dict: dict
146 | :return: Indicates whether the deletion was successful
147 | :rtype: boolean
148 | :raise NotFoundException:
149 | """
150 | raise NotImplementedError("Abstract method")
151 |
152 | def delete_record(self, record_id):
153 | """
154 | Delete a single record
155 |
156 | :param record_id:
157 | :type record_id: str
158 | :return: Indicates whether the deletion was successful
159 | :rtype: boolean
160 | :raise NotFoundException:
161 | """
162 | raise NotImplementedError("Abstract method")
163 |
164 | def delete_relation(self, relation_id):
165 | """
166 | Delete a single relation
167 |
168 | :param relation_id:
169 | :type relation_id: str
170 | :return: Indicates whether the deletion was successful
171 | :rtype: boolean
172 | :raise NotFoundException:
173 | """
174 | raise NotImplementedError("Abstract method")
175 |
--------------------------------------------------------------------------------
/README.rst:
--------------------------------------------------------------------------------
1 | PROV Database Connector
2 | =======================
3 |
4 | Introduction
5 | ------------
6 |
7 | .. image:: https://zenodo.org/badge/68604048.svg
8 | :target: https://zenodo.org/badge/latestdoi/68604048
9 | :alt: Latest release
10 | .. image:: https://badge.fury.io/py/prov-db-connector.svg
11 | :target: https://pypi.python.org/pypi/prov-db-connector
12 | :alt: PyPI version
13 | .. image:: https://travis-ci.org/DLR-SC/prov-db-connector.svg?branch=master
14 | :target: https://travis-ci.org/DLR-SC/prov-db-connector
15 | :alt: Build Status
16 | .. image:: https://coveralls.io/repos/github/DLR-SC/prov-db-connector/badge.svg?branch=master
17 | :target: https://coveralls.io/github/DLR-SC/prov-db-connector?branch=master
18 | :alt: Coverage Status
19 | .. image:: https://pyup.io/repos/github/dlr-sc/prov-db-connector/shield.svg
20 | :target: https://pyup.io/repos/github/dlr-sc/prov-db-connector/
21 | :alt: Updates
22 |
23 |
24 |
25 | This python module provides a general interface to save `W3C-PROV `_ documents into databases.
26 | Currently we support the `Neo4j `_ graph database.
27 |
28 | We transform a PROV document into a graph structure and the result looks like this:
29 |
30 | .. figure:: https://cdn.rawgit.com/dlr-sc/prov-db-connector/master/docs/_images/test_cases/test_prov_primer_example.svg
31 | :align: center
32 | :scale: 50 %
33 | :alt: Complex example in Neo4j
34 |
35 | Complex example in Neo4j
36 |
37 | See full documentation at: `prov-db-connector.readthedocs.io `_
38 |
39 | Installation
40 | ------------
41 |
42 | PyPi
43 | ~~~~
44 |
45 | Install it by running::
46 |
47 | pip install prov-db-connector
48 |
49 | You can view `prov-db-connector on PyPi's package index `_
50 |
51 | Source
52 | ~~~~~~
53 |
54 | .. code:: sh
55 |
56 | # Clone project
57 | git clone git@github.com:DLR-SC/prov-db-connector.git
58 | cd prov-db-connector
59 |
60 | # Setup virtual environment
61 | virtualenv -p /usr/bin/python3.4 env
62 | source env/bin/activate
63 |
64 | # Install dependencies and package into virtual enviroment
65 | make setup
66 |
67 | Usage
68 | -----
69 |
70 | Save and get prov document example
71 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
72 |
73 |
74 | .. code-block:: python
75 |
76 | from prov.model import ProvDocument
77 | from provdbconnector import ProvDb
78 | from provdbconnector.db_adapters.in_memory import SimpleInMemoryAdapter
79 |
80 | prov_api = ProvDb(adapter=SimpleInMemoryAdapter, auth_info=None)
81 |
82 | # create the prov document
83 | prov_document = ProvDocument()
84 | prov_document.add_namespace("ex", "http://example.com")
85 |
86 | prov_document.agent("ex:Bob")
87 | prov_document.activity("ex:Alice")
88 |
89 | prov_document.association("ex:Alice", "ex:Bob")
90 |
91 | document_id = prov_api.save_document(prov_document)
92 |
93 | print(prov_api.get_document_as_provn(document_id))
94 |
95 | # Output:
96 | #
97 | # document
98 | # prefix
99 | # ex < http: // example.com >
100 | #
101 | # agent(ex:Bob)
102 | # activity(ex:Alice, -, -)
103 | # wasAssociatedWith(ex:Alice, ex:Bob, -)
104 | # endDocument
105 |
106 |
107 | File Buffer example
108 | ~~~~~~~~~~~~~~~~~~~
109 |
110 |
111 | .. code-block:: python
112 |
113 | from provdbconnector import ProvDb
114 | from provdbconnector.db_adapters.in_memory import SimpleInMemoryAdapter
115 | import pkg_resources
116 |
117 | # create the api
118 | prov_api = ProvDb(adapter=SimpleInMemoryAdapter, auth_info=None)
119 |
120 | # create the prov document from examples
121 | prov_document_buffer = pkg_resources.resource_stream("examples", "file_buffer_example_primer.json")
122 |
123 | # Save document
124 | document_id = prov_api.save_document(prov_document_buffer)
125 | # This is similar to:
126 | # prov_api.create_document_from_json(prov_document_buffer)
127 |
128 | # get document
129 | print(prov_api.get_document_as_provn(document_id))
130 |
131 | # Output:
132 |
133 | # document
134 | # prefix
135 | # foaf < http: // xmlns.com / foaf / 0.1 / >
136 | # prefix
137 | # dcterms < http: // purl.org / dc / terms / >
138 | # prefix
139 | # ex < http: // example / >
140 | #
141 | # specializationOf(ex:articleV2, ex:article)
142 | # specializationOf(ex:articleV1, ex:article)
143 | # wasDerivedFrom(ex:blogEntry, ex:article, -, -, -, [prov:type = 'prov:Quotation'])
144 | # alternateOf(ex:articleV2, ex:articleV1)
145 | # wasDerivedFrom(ex:articleV1, ex:dataSet1, -, -, -)
146 | # wasDerivedFrom(ex:articleV2, ex:dataSet2, -, -, -)
147 | # wasDerivedFrom(ex:dataSet2, ex:dataSet1, -, -, -, [prov:type = 'prov:Revision'])
148 | # used(ex:correct, ex:dataSet1, -)
149 | # used(ex:compose, ex:dataSet1, -, [prov:role = "ex:dataToCompose"])
150 | # wasDerivedFrom(ex:chart2, ex:dataSet2, -, -, -)
151 | # wasGeneratedBy(ex:dataSet2, ex:correct, -)
152 | # used(ex:compose, ex:regionList, -, [prov:role = "ex:regionsToAggregateBy"])
153 | # used(ex:illustrate, ex:composition, -)
154 | # wasGeneratedBy(ex:composition, ex:compose, -)
155 | # wasAttributedTo(ex:chart1, ex:derek)
156 | # wasGeneratedBy(ex:chart1, ex:compile, 2012 - 03 - 02
157 | # T10:30:00)
158 | # wasGeneratedBy(ex:chart1, ex:illustrate, -)
159 | # wasAssociatedWith(ex:compose, ex:derek, -)
160 | # wasAssociatedWith(ex:illustrate, ex:derek, -)
161 | # actedOnBehalfOf(ex:derek, ex:chartgen, ex:compose)
162 | # entity(ex:article, [dcterms:title = "Crime rises in cities"])
163 | # entity(ex:articleV1)
164 | # entity(ex:articleV2)
165 | # entity(ex:dataSet1)
166 | # entity(ex:dataSet2)
167 | # entity(ex:regionList)
168 | # entity(ex:composition)
169 | # entity(ex:chart1)
170 | # entity(ex:chart2)
171 | # entity(ex:blogEntry)
172 | # activity(ex:compile, -, -)
173 | # activity(ex:compile2, -, -)
174 | # activity(ex:compose, -, -)
175 | # activity(ex:correct, 2012 - 03 - 31
176 | # T09:21:00, 2012 - 04 - 01
177 | # T15:21:00)
178 | # activity(ex:illustrate, -, -)
179 | # agent(ex:derek, [foaf:mbox = "", foaf:givenName = "Derek", prov:type = 'prov:Person'])
180 | # agent(ex:chartgen, [foaf:name = "Chart Generators Inc", prov:type = 'prov:Organization'])
181 | # endDocument
182 |
183 |
184 | You find all examples in the `examples `_ folder
185 |
186 |
187 | Release
188 | -------
189 | Create a new release on github, please use the semver standard for the version number
190 |
191 |
192 | License
193 | -------
194 |
195 | See `LICENSE `_ file
196 |
197 |
198 |
--------------------------------------------------------------------------------
/docs/Makefile:
--------------------------------------------------------------------------------
1 | # Makefile for Sphinx documentation
2 | #
3 |
4 | # You can set these variables from the command line.
5 | SPHINXOPTS =
6 | SPHINXBUILD = sphinx-build
7 | PAPER =
8 | BUILDDIR = build
9 |
10 | # Internal variables.
11 | PAPEROPT_a4 = -D latex_paper_size=a4
12 | PAPEROPT_letter = -D latex_paper_size=letter
13 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
14 | # the i18n builder cannot share the environment and doctrees with the others
15 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
16 |
17 | .PHONY: help
18 | help:
19 | @echo "Please use \`make ' where is one of"
20 | @echo " html to make standalone HTML files"
21 | @echo " dirhtml to make HTML files named index.html in directories"
22 | @echo " singlehtml to make a single large HTML file"
23 | @echo " pickle to make pickle files"
24 | @echo " json to make JSON files"
25 | @echo " htmlhelp to make HTML files and a HTML help project"
26 | @echo " qthelp to make HTML files and a qthelp project"
27 | @echo " applehelp to make an Apple Help Book"
28 | @echo " devhelp to make HTML files and a Devhelp project"
29 | @echo " epub to make an epub"
30 | @echo " epub3 to make an epub3"
31 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
32 | @echo " latexpdf to make LaTeX files and run them through pdflatex"
33 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
34 | @echo " text to make text files"
35 | @echo " man to make manual pages"
36 | @echo " texinfo to make Texinfo files"
37 | @echo " info to make Texinfo files and run them through makeinfo"
38 | @echo " gettext to make PO message catalogs"
39 | @echo " changes to make an overview of all changed/added/deprecated items"
40 | @echo " xml to make Docutils-native XML files"
41 | @echo " pseudoxml to make pseudoxml-XML files for display purposes"
42 | @echo " linkcheck to check all external links for integrity"
43 | @echo " doctest to run all doctests embedded in the documentation (if enabled)"
44 | @echo " coverage to run coverage check of the documentation (if enabled)"
45 | @echo " dummy to check syntax errors of document sources"
46 |
47 | .PHONY: clean
48 | clean:
49 | rm -rf $(BUILDDIR)/*
50 |
51 | .PHONY: html
52 | html:
53 | sphinx-apidoc -o . ../provdbconnector
54 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
55 | @echo
56 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
57 |
58 | .PHONY: dirhtml
59 | dirhtml:
60 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
61 | @echo
62 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
63 |
64 | .PHONY: singlehtml
65 | singlehtml:
66 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
67 | @echo
68 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
69 |
70 | .PHONY: pickle
71 | pickle:
72 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
73 | @echo
74 | @echo "Build finished; now you can process the pickle files."
75 |
76 | .PHONY: json
77 | json:
78 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
79 | @echo
80 | @echo "Build finished; now you can process the JSON files."
81 |
82 | .PHONY: htmlhelp
83 | htmlhelp:
84 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
85 | @echo
86 | @echo "Build finished; now you can run HTML Help Workshop with the" \
87 | ".hhp project file in $(BUILDDIR)/htmlhelp."
88 |
89 | .PHONY: qthelp
90 | qthelp:
91 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
92 | @echo
93 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \
94 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
95 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/PROVDatabaseConnector.qhcp"
96 | @echo "To view the help file:"
97 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/PROVDatabaseConnector.qhc"
98 |
99 | .PHONY: applehelp
100 | applehelp:
101 | $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp
102 | @echo
103 | @echo "Build finished. The help book is in $(BUILDDIR)/applehelp."
104 | @echo "N.B. You won't be able to view it unless you put it in" \
105 | "~/Library/Documentation/Help or install it in your application" \
106 | "bundle."
107 |
108 | .PHONY: devhelp
109 | devhelp:
110 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
111 | @echo
112 | @echo "Build finished."
113 | @echo "To view the help file:"
114 | @echo "# mkdir -p $$HOME/.local/share/devhelp/PROVDatabaseConnector"
115 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/PROVDatabaseConnector"
116 | @echo "# devhelp"
117 |
118 | .PHONY: epub
119 | epub:
120 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
121 | @echo
122 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
123 |
124 | .PHONY: epub3
125 | epub3:
126 | $(SPHINXBUILD) -b epub3 $(ALLSPHINXOPTS) $(BUILDDIR)/epub3
127 | @echo
128 | @echo "Build finished. The epub3 file is in $(BUILDDIR)/epub3."
129 |
130 | .PHONY: latex
131 | latex:
132 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
133 | @echo
134 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
135 | @echo "Run \`make' in that directory to run these through (pdf)latex" \
136 | "(use \`make latexpdf' here to do that automatically)."
137 |
138 | .PHONY: latexpdf
139 | latexpdf:
140 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
141 | @echo "Running LaTeX files through pdflatex..."
142 | $(MAKE) -C $(BUILDDIR)/latex all-pdf
143 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
144 |
145 | .PHONY: latexpdfja
146 | latexpdfja:
147 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
148 | @echo "Running LaTeX files through platex and dvipdfmx..."
149 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
150 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
151 |
152 | .PHONY: text
153 | text:
154 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
155 | @echo
156 | @echo "Build finished. The text files are in $(BUILDDIR)/text."
157 |
158 | .PHONY: man
159 | man:
160 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
161 | @echo
162 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
163 |
164 | .PHONY: texinfo
165 | texinfo:
166 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
167 | @echo
168 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
169 | @echo "Run \`make' in that directory to run these through makeinfo" \
170 | "(use \`make info' here to do that automatically)."
171 |
172 | .PHONY: info
173 | info:
174 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
175 | @echo "Running Texinfo files through makeinfo..."
176 | make -C $(BUILDDIR)/texinfo info
177 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
178 |
179 | .PHONY: gettext
180 | gettext:
181 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
182 | @echo
183 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
184 |
185 | .PHONY: changes
186 | changes:
187 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
188 | @echo
189 | @echo "The overview file is in $(BUILDDIR)/changes."
190 |
191 | .PHONY: linkcheck
192 | linkcheck:
193 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
194 | @echo
195 | @echo "Link check complete; look for any errors in the above output " \
196 | "or in $(BUILDDIR)/linkcheck/output.txt."
197 |
198 | .PHONY: doctest
199 | doctest:
200 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
201 | @echo "Testing of doctests in the sources finished, look at the " \
202 | "results in $(BUILDDIR)/doctest/output.txt."
203 |
204 | .PHONY: coverage
205 | coverage:
206 | $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage
207 | @echo "Testing of coverage in the sources finished, look at the " \
208 | "results in $(BUILDDIR)/coverage/python.txt."
209 |
210 | .PHONY: xml
211 | xml:
212 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
213 | @echo
214 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml."
215 |
216 | .PHONY: pseudoxml
217 | pseudoxml:
218 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
219 | @echo
220 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
221 |
222 | .PHONY: dummy
223 | dummy:
224 | $(SPHINXBUILD) -b dummy $(ALLSPHINXOPTS) $(BUILDDIR)/dummy
225 | @echo
226 | @echo "Build finished. Dummy builder generates no files."
227 |
--------------------------------------------------------------------------------
/docs/make.bat:
--------------------------------------------------------------------------------
1 | @ECHO OFF
2 |
3 | REM Command file for Sphinx documentation
4 |
5 | if "%SPHINXBUILD%" == "" (
6 | set SPHINXBUILD=sphinx-build
7 | )
8 | set BUILDDIR=build
9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
10 | set I18NSPHINXOPTS=%SPHINXOPTS% .
11 | if NOT "%PAPER%" == "" (
12 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
13 | set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
14 | )
15 |
16 | if "%1" == "" goto help
17 |
18 | if "%1" == "help" (
19 | :help
20 | echo.Please use `make ^` where ^ is one of
21 | echo. html to make standalone HTML files
22 | echo. dirhtml to make HTML files named index.html in directories
23 | echo. singlehtml to make a single large HTML file
24 | echo. pickle to make pickle files
25 | echo. json to make JSON files
26 | echo. htmlhelp to make HTML files and a HTML help project
27 | echo. qthelp to make HTML files and a qthelp project
28 | echo. devhelp to make HTML files and a Devhelp project
29 | echo. epub to make an epub
30 | echo. epub3 to make an epub3
31 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
32 | echo. text to make text files
33 | echo. man to make manual pages
34 | echo. texinfo to make Texinfo files
35 | echo. gettext to make PO message catalogs
36 | echo. changes to make an overview over all changed/added/deprecated items
37 | echo. xml to make Docutils-native XML files
38 | echo. pseudoxml to make pseudoxml-XML files for display purposes
39 | echo. linkcheck to check all external links for integrity
40 | echo. doctest to run all doctests embedded in the documentation if enabled
41 | echo. coverage to run coverage check of the documentation if enabled
42 | echo. dummy to check syntax errors of document sources
43 | goto end
44 | )
45 |
46 | if "%1" == "clean" (
47 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
48 | del /q /s %BUILDDIR%\*
49 | goto end
50 | )
51 |
52 |
53 | REM Check if sphinx-build is available and fallback to Python version if any
54 | %SPHINXBUILD% 1>NUL 2>NUL
55 | if errorlevel 9009 goto sphinx_python
56 | goto sphinx_ok
57 |
58 | :sphinx_python
59 |
60 | set SPHINXBUILD=python -m sphinx.__init__
61 | %SPHINXBUILD% 2> nul
62 | if errorlevel 9009 (
63 | echo.
64 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
65 | echo.installed, then set the SPHINXBUILD environment variable to point
66 | echo.to the full path of the 'sphinx-build' executable. Alternatively you
67 | echo.may add the Sphinx directory to PATH.
68 | echo.
69 | echo.If you don't have Sphinx installed, grab it from
70 | echo.http://sphinx-doc.org/
71 | exit /b 1
72 | )
73 |
74 | :sphinx_ok
75 |
76 |
77 | if "%1" == "html" (
78 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
79 | if errorlevel 1 exit /b 1
80 | echo.
81 | echo.Build finished. The HTML pages are in %BUILDDIR%/html.
82 | goto end
83 | )
84 |
85 | if "%1" == "dirhtml" (
86 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
87 | if errorlevel 1 exit /b 1
88 | echo.
89 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
90 | goto end
91 | )
92 |
93 | if "%1" == "singlehtml" (
94 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
95 | if errorlevel 1 exit /b 1
96 | echo.
97 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
98 | goto end
99 | )
100 |
101 | if "%1" == "pickle" (
102 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
103 | if errorlevel 1 exit /b 1
104 | echo.
105 | echo.Build finished; now you can process the pickle files.
106 | goto end
107 | )
108 |
109 | if "%1" == "json" (
110 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
111 | if errorlevel 1 exit /b 1
112 | echo.
113 | echo.Build finished; now you can process the JSON files.
114 | goto end
115 | )
116 |
117 | if "%1" == "htmlhelp" (
118 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
119 | if errorlevel 1 exit /b 1
120 | echo.
121 | echo.Build finished; now you can run HTML Help Workshop with the ^
122 | .hhp project file in %BUILDDIR%/htmlhelp.
123 | goto end
124 | )
125 |
126 | if "%1" == "qthelp" (
127 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
128 | if errorlevel 1 exit /b 1
129 | echo.
130 | echo.Build finished; now you can run "qcollectiongenerator" with the ^
131 | .qhcp project file in %BUILDDIR%/qthelp, like this:
132 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\PROVDatabaseConnector.qhcp
133 | echo.To view the help file:
134 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\PROVDatabaseConnector.ghc
135 | goto end
136 | )
137 |
138 | if "%1" == "devhelp" (
139 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
140 | if errorlevel 1 exit /b 1
141 | echo.
142 | echo.Build finished.
143 | goto end
144 | )
145 |
146 | if "%1" == "epub" (
147 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
148 | if errorlevel 1 exit /b 1
149 | echo.
150 | echo.Build finished. The epub file is in %BUILDDIR%/epub.
151 | goto end
152 | )
153 |
154 | if "%1" == "epub3" (
155 | %SPHINXBUILD% -b epub3 %ALLSPHINXOPTS% %BUILDDIR%/epub3
156 | if errorlevel 1 exit /b 1
157 | echo.
158 | echo.Build finished. The epub3 file is in %BUILDDIR%/epub3.
159 | goto end
160 | )
161 |
162 | if "%1" == "latex" (
163 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
164 | if errorlevel 1 exit /b 1
165 | echo.
166 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
167 | goto end
168 | )
169 |
170 | if "%1" == "latexpdf" (
171 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
172 | cd %BUILDDIR%/latex
173 | make all-pdf
174 | cd %~dp0
175 | echo.
176 | echo.Build finished; the PDF files are in %BUILDDIR%/latex.
177 | goto end
178 | )
179 |
180 | if "%1" == "latexpdfja" (
181 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
182 | cd %BUILDDIR%/latex
183 | make all-pdf-ja
184 | cd %~dp0
185 | echo.
186 | echo.Build finished; the PDF files are in %BUILDDIR%/latex.
187 | goto end
188 | )
189 |
190 | if "%1" == "text" (
191 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
192 | if errorlevel 1 exit /b 1
193 | echo.
194 | echo.Build finished. The text files are in %BUILDDIR%/text.
195 | goto end
196 | )
197 |
198 | if "%1" == "man" (
199 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
200 | if errorlevel 1 exit /b 1
201 | echo.
202 | echo.Build finished. The manual pages are in %BUILDDIR%/man.
203 | goto end
204 | )
205 |
206 | if "%1" == "texinfo" (
207 | %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
208 | if errorlevel 1 exit /b 1
209 | echo.
210 | echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
211 | goto end
212 | )
213 |
214 | if "%1" == "gettext" (
215 | %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
216 | if errorlevel 1 exit /b 1
217 | echo.
218 | echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
219 | goto end
220 | )
221 |
222 | if "%1" == "changes" (
223 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
224 | if errorlevel 1 exit /b 1
225 | echo.
226 | echo.The overview file is in %BUILDDIR%/changes.
227 | goto end
228 | )
229 |
230 | if "%1" == "linkcheck" (
231 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
232 | if errorlevel 1 exit /b 1
233 | echo.
234 | echo.Link check complete; look for any errors in the above output ^
235 | or in %BUILDDIR%/linkcheck/output.txt.
236 | goto end
237 | )
238 |
239 | if "%1" == "doctest" (
240 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
241 | if errorlevel 1 exit /b 1
242 | echo.
243 | echo.Testing of doctests in the sources finished, look at the ^
244 | results in %BUILDDIR%/doctest/output.txt.
245 | goto end
246 | )
247 |
248 | if "%1" == "coverage" (
249 | %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage
250 | if errorlevel 1 exit /b 1
251 | echo.
252 | echo.Testing of coverage in the sources finished, look at the ^
253 | results in %BUILDDIR%/coverage/python.txt.
254 | goto end
255 | )
256 |
257 | if "%1" == "xml" (
258 | %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml
259 | if errorlevel 1 exit /b 1
260 | echo.
261 | echo.Build finished. The XML files are in %BUILDDIR%/xml.
262 | goto end
263 | )
264 |
265 | if "%1" == "pseudoxml" (
266 | %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml
267 | if errorlevel 1 exit /b 1
268 | echo.
269 | echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml.
270 | goto end
271 | )
272 |
273 | if "%1" == "dummy" (
274 | %SPHINXBUILD% -b dummy %ALLSPHINXOPTS% %BUILDDIR%/dummy
275 | if errorlevel 1 exit /b 1
276 | echo.
277 | echo.Build finished. Dummy builder generates no files.
278 | goto end
279 | )
280 |
281 | :end
282 |
--------------------------------------------------------------------------------
/provdbconnector/tests/examples.py:
--------------------------------------------------------------------------------
1 | from prov.tests.examples import primer_example as primer_example, \
2 | primer_example_alternate, \
3 | w3c_publication_1, \
4 | w3c_publication_2, \
5 | bundles1, \
6 | bundles2, \
7 | collections, \
8 | long_literals, \
9 | datatypes
10 |
11 | from datetime import datetime
12 | from collections import namedtuple
13 |
14 | from prov.constants import PROV_RECORD_IDS_MAP, PROV
15 | from prov.model import ProvDocument, ProvActivity, Literal, Identifier
16 | from provdbconnector.db_adapters.baseadapter import METADATA_KEY_NAMESPACES, METADATA_KEY_PROV_TYPE, \
17 | METADATA_KEY_TYPE_MAP, METADATA_KEY_IDENTIFIER
18 |
19 |
20 | def prov_db_unknown_prov_typ_example():
21 | doc = ProvDocument()
22 | doc.add_namespace("ex", "https://example.com")
23 | doc.entity(identifier="ex:Entity1")
24 | doc.entity(identifier="ex:Entity2")
25 | doc.influence(influencee="ex:Entity1", influencer="ex:Entity2")
26 | return doc
27 |
28 | def prov_default_namespace_example(ns_postfix: str):
29 | doc = ProvDocument()
30 | doc.set_default_namespace("https://example.com/{0}".format(ns_postfix))
31 | doc.entity(identifier="Entity1")
32 | return doc
33 |
34 |
35 |
36 | def attributes_dict_example():
37 | """
38 | Retuns a example dict with some different attributes
39 |
40 | :return: dict with attributes
41 | :rtype: dict
42 | """
43 | attributes = dict()
44 | attributes.update({"ex:individual attribute": "Some value"})
45 | attributes.update({"ex:int value": 99})
46 | attributes.update({"ex:double value": 99.33})
47 | attributes.update({"ex:date value": datetime.strptime('Jun 1 2005 1:33PM', '%b %d %Y %I:%M%p')})
48 | attributes.update({"ex:list value": ["list", "of", "strings"]})
49 | attributes.update({"ex:dict value": {"dict": "value"}})
50 |
51 | return attributes
52 |
53 |
54 | def base_connector_bundle_parameter_example():
55 | """
56 | This example returns a dict with example arguments for a db_adapter
57 |
58 | :return: dict {attributes, metadata}
59 | :rtype: dict
60 | """
61 | doc = ProvDocument()
62 | doc.add_namespace("ex", "http://example.com")
63 | attributes = dict()
64 | attributes.update({"prov:type": "prov:Bundle"})
65 |
66 | namespaces = dict()
67 | namespaces.update({"ex": "http://example.com"})
68 |
69 | type_map = dict()
70 | type_map.update({"int value": "int"})
71 | type_map.update({"date value": "xds:datetime"})
72 |
73 | metadata = dict()
74 |
75 | metadata.update({METADATA_KEY_PROV_TYPE: doc.valid_qualified_name("prov:Entity")})
76 | metadata.update({METADATA_KEY_IDENTIFIER: doc.valid_qualified_name("ex:bundle name")})
77 | metadata.update({METADATA_KEY_TYPE_MAP: type_map})
78 | metadata.update({METADATA_KEY_NAMESPACES: namespaces})
79 |
80 | return_data = dict()
81 | return_data.update({"attributes": attributes})
82 | return_data.update({"metadata": metadata})
83 | return return_data
84 |
85 |
86 | def base_connector_record_parameter_example():
87 | """
88 | Returns a dict with attributes and metadata for a simple node
89 |
90 | :return:dict with attributes metadata
91 | :rtype: dict
92 | """
93 | doc = ProvDocument()
94 |
95 | namespaces = dict()
96 | namespaces.update({"ex": "http://example.com"})
97 | namespaces.update({"custom": "http://custom.com"})
98 |
99 | type_map = dict()
100 | type_map.update({"int value": "int"})
101 | type_map.update({"date value": "xds:datetime"})
102 |
103 | metadata = dict()
104 |
105 | metadata.update({METADATA_KEY_PROV_TYPE: doc.valid_qualified_name("prov:Activity")})
106 | metadata.update({METADATA_KEY_IDENTIFIER: doc.valid_qualified_name("prov:example_node")})
107 | metadata.update({METADATA_KEY_TYPE_MAP: type_map})
108 | metadata.update({METADATA_KEY_NAMESPACES: namespaces})
109 |
110 | return_data = dict()
111 | return_data.update({"attributes": attributes_dict_example()})
112 | return_data.update({"metadata": metadata})
113 |
114 | return return_data
115 |
116 |
117 | def base_connector_relation_parameter_example():
118 | """
119 | Returns a example with a start nodes (attributes, metadata) and also a relation dict with attributes metadata
120 |
121 | :return: dict
122 | :rtype: dict
123 | """
124 | doc = ProvDocument()
125 | doc.add_namespace("ex", "http://example.com")
126 | doc.add_namespace("custom", "http://custom.com")
127 |
128 | namespaces = dict()
129 | namespaces.update({"ex": "http://example.com"})
130 | namespaces.update({"custom": "http://custom.com"})
131 |
132 | type_map = dict()
133 | type_map.update({"int value": "int"})
134 | type_map.update({"date value": "xds:datetime"})
135 |
136 | metadata = dict()
137 |
138 | metadata.update({METADATA_KEY_PROV_TYPE: PROV_RECORD_IDS_MAP["mentionOf"]})
139 | metadata.update({METADATA_KEY_IDENTIFIER: "identifier for the relation"})
140 | metadata.update({METADATA_KEY_TYPE_MAP: type_map})
141 | metadata.update({METADATA_KEY_NAMESPACES: namespaces})
142 |
143 | return_data = dict()
144 | return_data.update({"attributes": attributes_dict_example()})
145 | return_data.update({"metadata": metadata})
146 | return_data.update({"from_node": doc.valid_qualified_name("ex:Yoda")})
147 | return_data.update({"to_node": doc.valid_qualified_name("ex:Luke Skywalker")})
148 | return_data.update({"doc": doc})
149 |
150 | return return_data
151 |
152 |
153 | def base_connector_merge_example():
154 | """
155 | This example returns a namedtuple with a from_node relation and to_node
156 | to test the merge behavior
157 |
158 | :return: namedtuple(from_node, relation, to_node)
159 | :rtype: namedtuple
160 | """
161 | # noinspection PyPep8Naming
162 | ReturnData = namedtuple("base_connector_merge_example_return_data", "from_node,relation,to_node")
163 | example_relation = base_connector_relation_parameter_example()
164 |
165 | example_node_a = base_connector_record_parameter_example()
166 | example_node_b = base_connector_record_parameter_example()
167 |
168 | example_node_a["metadata"][METADATA_KEY_IDENTIFIER] = example_relation["from_node"]
169 | example_node_b["metadata"][METADATA_KEY_IDENTIFIER] = example_relation["to_node"]
170 |
171 | return ReturnData(example_node_a, example_relation, example_node_b)
172 |
173 |
174 | def prov_api_record_example():
175 | """
176 | This is a more complex record example
177 |
178 | :return:
179 | """
180 | doc = ProvDocument()
181 | doc.add_namespace("ex", "http://example.com")
182 | doc.add_namespace("custom", "http://custom.com")
183 |
184 | attributes = attributes_dict_example()
185 | del attributes[
186 | "ex:dict value"] # remove dict value because it is not allowed in a prov_record, but for low level adapter tests necessary
187 | del attributes[
188 | "ex:list value"] # remove dict value because it is not allowed in a prov_record, but for low level adapter tests necessary
189 | attributes.update({"ex:Qualified name ": doc.valid_qualified_name("custom:qualified name")})
190 | attributes.update({"ex:Qualified name 2": "ex:unqualified_name"})
191 | attributes.update({"ex:Literal": Literal("test literal", langtag="en")})
192 | attributes.update({"ex:Literal 2": Literal("test literal with datatype", langtag="en",
193 | datatype=PROV["InternationalizedString"])})
194 | attributes.update({"ex:identifier type": Identifier("http://example.com/#test")})
195 |
196 | expected_attributes = dict()
197 | for key, value in attributes.items():
198 | new_key = doc.valid_qualified_name(key)
199 | expected_attributes.update({new_key: value})
200 |
201 | # The prov lib don't require to auto convert string values into qualified names
202 | # valid_name = doc.valid_qualified_name("ex:Qualified name 2")
203 | # expected_attributes[valid_name] = doc.valid_qualified_name("ex:unqualified_name")
204 |
205 | namespaces = dict()
206 | namespaces.update({"ex": "http://example.com"})
207 | namespaces.update({"custom": "http://custom.com"})
208 | namespaces.update({"prov": "http://www.w3.org/ns/prov#"})
209 |
210 | type_map = dict()
211 | type_map.update({"ex:date value": {"type": "xsd:dateTime"}})
212 | type_map.update({"ex:double value": {"type": "xsd:double"}})
213 | type_map.update({"ex:int value": {"type": "xsd:int"}})
214 |
215 | type_map.update({"ex:Qualified name ": {'type': 'prov:QUALIFIED_NAME'}})
216 | # type_map.update({"ex:Qualified name 2":{'type': 'prov:QUALIFIED_NAME'}}) #The prov lib don't require to auto convert strings into qualified names
217 | type_map.update({"ex:Literal": {'lang': 'en'}})
218 | type_map.update({"ex:Literal 2": {'lang': 'en'}})
219 | type_map.update({"ex:identifier type": {'type': 'xsd:anyURI'}})
220 |
221 | metadata = dict()
222 | metadata.update({METADATA_KEY_PROV_TYPE: PROV_RECORD_IDS_MAP["activity"]})
223 | metadata.update({METADATA_KEY_IDENTIFIER: doc.valid_qualified_name("ex:record")})
224 | metadata.update({METADATA_KEY_NAMESPACES: namespaces})
225 | metadata.update({METADATA_KEY_TYPE_MAP: type_map})
226 |
227 | record = ProvActivity(doc, "ex:record", attributes)
228 | # noinspection PyPep8Naming
229 | Example = namedtuple("prov_api_metadata_record_example", "metadata, attributes, prov_record, expected_attributes")
230 |
231 | return Example(metadata, attributes, record, expected_attributes)
232 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "{}"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright {yyyy} {name of copyright owner}
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/docs/_images/test_cases/test_bundles1.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/_images/test_cases/test_bundles2.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------