From a73414d73141841512d8b055f2d1d91f9108957d Mon Sep 17 00:00:00 2001 From: Anna Bobasheva <33026767+AnnaBobasheva@users.noreply.github.com> Date: Fri, 3 Jan 2025 13:12:45 +0100 Subject: [PATCH 1/3] Synch "main" and "develoip" to merge the commit histories (#18) * Addition of documenttaion pages (#16) * Updated Sphinx documentation New Index.rst page, Old index.rst page became corese.rst. Added new pages: python_api/api_root.rst with autogenerated API doc. Added python_api/bridges.rst page The example1.ipynb notebook is rendered as a uset_guide. INSTALL.md is rendered as an installation page * Added the installation of the packages from the docs/requirements.txt in case a new extension is added. * Moved VIESION.txt to src/pycorese to update the version only in one place. * Minor fixes and updates * Update pypi.yml Added verbosity to the twine command * Update pyproject.toml Had to add the content type for README.md, otherwise it didn't render correctly on Pypi. * Update pypi.yml Removed the verbosity from the twine command. * Revert "Update pypi.yml" This reverts commit 77c6597852d5c76a3cd38f39dcc813d6b7e8074a. * Revert "Update pypi.yml" This reverts commit ebde89cb26a7173215276b5be216d833f7ebac91. * Revert "Update pyproject.toml" This reverts commit 00e3c76e7cfad2b520608c72f3c9ee176b0ddf93. * Squashed commit of the following: commit 6e1dfec7545076c8e4e8ec5df45df334c8d175f2 Author: Anna Bobasheva <33026767+AnnaBobasheva@users.noreply.github.com> Date: Mon Dec 23 18:17:12 2024 +0100 Update pyproject.toml readme = { file = "README.md", content-type = "text/markdown" } commit 32eafa36b01bd09d3477af4942695b198efaa051 Author: Anna Bobasheva <33026767+AnnaBobasheva@users.noreply.github.com> Date: Mon Dec 23 16:38:37 2024 +0100 Revert "Merge branch 'main' into develop" This reverts commit 85d2fd016d39113a1a7b0640ce6091ee1bbe9712, reversing changes made to 13ab5bc97a62e1585646ddc0f856d836595e113e. commit 85d2fd016d39113a1a7b0640ce6091ee1bbe9712 Merge: 13ab5bc 4f73eca Author: Anna Bobasheva <33026767+AnnaBobasheva@users.noreply.github.com> Date: Mon Dec 23 16:28:40 2024 +0100 Merge branch 'main' into develop commit 13ab5bc97a62e1585646ddc0f856d836595e113e Author: Anna Bobasheva <33026767+AnnaBobasheva@users.noreply.github.com> Date: Mon Dec 23 15:22:45 2024 +0100 Feature/update docs (#15) * Updated Sphinx documentation New Index.rst page, Old index.rst page became corese.rst. Added new pages: python_api/api_root.rst with autogenerated API doc. Added python_api/bridges.rst page The example1.ipynb notebook is rendered as a uset_guide. INSTALL.md is rendered as an installation page * Updated pyproject.toml * Moved VIESION.txt to src/pycorese to update the version only in one place. * Minor fixes and updates commit 203b617575b07748b598f56394019d40b96c0639 Author: Anna Bobasheva <33026767+AnnaBobasheva@users.noreply.github.com> Date: Tue Dec 17 10:34:59 2024 +0100 Revert "Update sphinx-dev.yml" This reverts commit 9324b1a3e5259395c00d7462e97af4066ce5d6a8. commit 9324b1a3e5259395c00d7462e97af4066ce5d6a8 Author: Anna Bobasheva <33026767+AnnaBobasheva@users.noreply.github.com> Date: Tue Dec 17 10:27:17 2024 +0100 Update sphinx-dev.yml Added the installation of the packages from the docs/requirements.txt in case a new extension is added. commit 8094112c909cfc62733cb5fb75893bbdc785c601 Author: Anna Bobasheva <33026767+AnnaBobasheva@users.noreply.github.com> Date: Mon Dec 16 11:36:03 2024 +0100 Update requirements.txt commit 771db55918568a8537c15de464a46e31e3a062f1 Author: Anna Bobasheva <33026767+AnnaBobasheva@users.noreply.github.com> Date: Mon Dec 16 11:32:04 2024 +0100 Updated Sphinx documentation (#14) * UPdated Sphinx documentation New Index.rst page, Old index.rst page became corese.rst. Added new pages: python_api/api_root.rst with autogenerated API doc. Added python_api/bridges.rst page The example1.ipynb notebook is rendered as a uset_guide. INSTALL.md is rendered as an installation page * Update pyproject.toml commit a373b1ecc5724d64922c1c7f42ab74ad6335b1f5 Author: Anna Bobasheva <33026767+AnnaBobasheva@users.noreply.github.com> Date: Fri Dec 6 17:46:59 2024 +0100 Update INSTALL.md commit d922cd326b8131382edd4ecce8242215a0d51576 Author: Anna Bobasheva <33026767+AnnaBobasheva@users.noreply.github.com> Date: Fri Dec 6 17:31:07 2024 +0100 Updated README files, dependencies, and downgraded version (#12) commit 78be7a7fa40b9bba3938725233b1a93fcbf3ca3a Author: Anna Bobasheva <33026767+AnnaBobasheva@users.noreply.github.com> Date: Tue Dec 3 18:48:09 2024 +0100 Feature/add example notebook (#11) * updated example dir and notebook * Update example1.ipynb * Changed GoogleColab link commit 466eab96ed15426b340da491f5fd3ae4f1f3c267 Author: Jean Luc Szpyrka Date: Tue Nov 5 14:31:48 2024 +0100 call CoreseInfo.getVersion() instead of returning a static sting commit 5376a84df073ca60c456fc0f96db569687fb7b89 Author: Jean Luc Szpyrka Date: Tue Nov 5 14:30:39 2024 +0100 maven-artifact python package not necessary commit 041342d39080b940c62a0c8f18e63e1e1749d29e Author: Jean Luc Szpyrka Date: Tue Nov 5 14:30:17 2024 +0100 typo commit e3601c8c5fb0e0031e2d61029d48d990187462d4 Merge: e27d995 9adb0b2 Author: Jean Luc Szpyrka Date: Mon Nov 4 14:40:28 2024 +0100 Merge branch 'main' into develop commit e27d995b178a86eeefe3414a191d2ef2c6a01bf4 Merge: 1eb7e43 be337dd Author: Jean Luc Szpyrka Date: Mon Nov 4 14:22:51 2024 +0100 Merge branch 'main' into develop commit 1eb7e437dc092469708c95f33041b9c274f8f6a8 Merge: 7cda584 5d62426 Author: Jean Luc Szpyrka Date: Mon Nov 4 14:21:34 2024 +0100 Merge branch 'release/v1.0.1' into develop * Synch "develop" and "main" to re0run Sphinx pages creation (#17) * typo * maven-artifact python package not necessary * call CoreseInfo.getVersion() instead of returning a static sting * Feature/add example notebook (#11) * updated example dir and notebook * Update example1.ipynb * Changed GoogleColab link * Updated README files, dependencies, and downgraded version (#12) * Update INSTALL.md * Updated Sphinx documentation (#14) * UPdated Sphinx documentation New Index.rst page, Old index.rst page became corese.rst. Added new pages: python_api/api_root.rst with autogenerated API doc. Added python_api/bridges.rst page The example1.ipynb notebook is rendered as a uset_guide. INSTALL.md is rendered as an installation page * Update pyproject.toml * Update requirements.txt * Update sphinx-dev.yml Added the installation of the packages from the docs/requirements.txt in case a new extension is added. * Revert "Update sphinx-dev.yml" This reverts commit 9324b1a3e5259395c00d7462e97af4066ce5d6a8. * Feature/update docs (#15) * Updated Sphinx documentation New Index.rst page, Old index.rst page became corese.rst. Added new pages: python_api/api_root.rst with autogenerated API doc. Added python_api/bridges.rst page The example1.ipynb notebook is rendered as a uset_guide. INSTALL.md is rendered as an installation page * Updated pyproject.toml * Moved VIESION.txt to src/pycorese to update the version only in one place. * Minor fixes and updates * Revert "Merge branch 'main' into develop" This reverts commit 85d2fd016d39113a1a7b0640ce6091ee1bbe9712, reversing changes made to 13ab5bc97a62e1585646ddc0f856d836595e113e. * Update pyproject.toml readme = { file = "README.md", content-type = "text/markdown" } --------- Co-authored-by: Jean Luc Szpyrka --------- Co-authored-by: Jean Luc Szpyrka From 04752c4a2cdfc6c37ed476f393b0eb24738f05ae Mon Sep 17 00:00:00 2001 From: Anna Bobasheva <33026767+AnnaBobasheva@users.noreply.github.com> Date: Fri, 17 Jan 2025 14:50:00 +0100 Subject: [PATCH 2/3] Updated pycorese to wrap corese-core v4.6.1 Changed corese-core version Adapted CoreseAPI and Java bridge classes to the updates in corese-core Refactored CoreseAPI Added new methods to CoreseAPI Updated example notebook Updated the docs --- build.gradle.kts | 2 +- docs/source/python_api/index.rst | 13 +- examples/example1.ipynb | 584 +++++-------------------------- src/pycorese/VERSION.txt | 2 +- src/pycorese/api.py | 401 +++++++++++++++------ src/pycorese/corese_version.py | 4 +- src/pycorese/jpype_bridge.py | 4 +- src/pycorese/py4J_bridge.py | 6 +- 8 files changed, 400 insertions(+), 616 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 58c765c..b8eb5c1 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -3,7 +3,7 @@ */ -val coreseVersion = "4.6.0" +val coreseVersion = "4.6.1" val corese_core= "corese-core-$coreseVersion-jar-with-dependencies.jar" val source="https://repo.maven.apache.org/maven2/fr/inria/corese/corese-core/$coreseVersion" val dest="./build/libs" diff --git a/docs/source/python_api/index.rst b/docs/source/python_api/index.rst index b847899..794c7f6 100644 --- a/docs/source/python_api/index.rst +++ b/docs/source/python_api/index.rst @@ -4,7 +4,7 @@ Python API Reference =================================== -**pycorese** is a Python wrapper for accessing and manipulating RDF data with Corese features connected by one of the java bridge packages: ``py4j`` or ``jpype``. +**pycorese** is a Python wrapper for accessing and manipulating RDF data with Corese features connected by one of the Java bridge packages: ``py4j`` or ``jpype``. .. note:: @@ -21,6 +21,8 @@ HIgh-level API is a set of convenience methods to facilitate the common tasks of .. automodule:: pycorese.api :members: CoreseAPI :undoc-members: + :member-order: bysource + .. contents:: @@ -70,15 +72,6 @@ For the details of these classes and their methods, please refer to the `Corese Corese ``fr.inria.corese.core.shacl.Shacl`` object. -.. autoattribute:: pycorese.api.CoreseAPI.DataManager - :annotation: - - Corese ``fr.inria.corese.core.storage.api.dataManager.DataManager`` object. - -.. autoattribute:: pycorese.api.CoreseAPI.CoreseGraphDataManager - :annotation: - - Corese ``fr.inria.corese.core.storage.CoreseGraphDataManager`` object. .. toctree:: :maxdepth: 2 diff --git a/examples/example1.ipynb b/examples/example1.ipynb index 0de6786..bc11756 100644 --- a/examples/example1.ipynb +++ b/examples/example1.ipynb @@ -57,12 +57,12 @@ }, "outputs": [ { - "name": "stdout", + "name": "stderr", "output_type": "stream", "text": [ - "openjdk version \"11.0.25\" 2024-10-15\n", - "OpenJDK Runtime Environment (build 11.0.25+9-post-Ubuntu-1ubuntu122.04)\n", - "OpenJDK 64-Bit Server VM (build 11.0.25+9-post-Ubuntu-1ubuntu122.04, mixed mode, sharing)\n" + "openjdk version \"11.0.21\" 2023-10-17 LTS\n", + "OpenJDK Runtime Environment Microsoft-8519785 (build 11.0.21+9-LTS)\n", + "OpenJDK 64-Bit Server VM Microsoft-8519785 (build 11.0.21+9-LTS, mixed mode)\n" ] } ], @@ -91,7 +91,7 @@ }, "outputs": [], "source": [ - "!pip install pycorese" + "%pip install pycorese" ] }, { @@ -105,7 +105,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -118,7 +118,8 @@ "name": "stdout", "output_type": "stream", "text": [ - "beatles.rdf beatles-validator.ttl\n" + "beatles-validator.ttl\n", + "beatles.rdf\n" ] } ], @@ -150,18 +151,47 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 4, "metadata": { "id": "wN4TDhjXe64p" }, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2025-01-15 22:42:58,987 - INFO - Py4J: Loading CORESE...\n", + "2025-01-15 22:43:02,334 - INFO - Py4J: CORESE is loaded\n" + ] + } + ], "source": [ "#%%timeit -n 1 -r 1\n", "from pycorese.api import CoreseAPI\n", "\n", "python_to_java_bridge = 'py4j'\n", "corese = CoreseAPI(java_bridge=python_to_java_bridge)\n", - "corese.loadCorese()" + "_ = corese.loadCorese()" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'4.6.1'" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "corese.coreseVersion()" ] }, { @@ -196,15 +226,8 @@ "outputs": [ { "data": { - "application/vnd.google.colaboratory.intrinsic+json": { - "summary": "{\n \"name\": \"results\",\n \"rows\": 5,\n \"fields\": [\n {\n \"column\": \"subject\",\n \"properties\": {\n \"dtype\": \"string\",\n \"num_unique_values\": 3,\n \"samples\": [\n \"http://example.com/Please_Please_Me\",\n \"http://example.com/McCartney\",\n \"http://example.com/Imagine\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"p\",\n \"properties\": {\n \"dtype\": \"string\",\n \"num_unique_values\": 2,\n \"samples\": [\n \"http://example.com/date\",\n \"http://example.com/artist\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"o\",\n \"properties\": {\n \"dtype\": \"string\",\n \"num_unique_values\": 5,\n \"samples\": [\n \"http://example.com/Paul_McCartney\",\n \"1970-04-17\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n }\n ]\n}", - "type": "dataframe", - "variable_name": "results" - }, "text/html": [ - "\n", - "
\n", - "
\n", + "
\n", "\n", - "\n", - " \n", - "
\n", - "\n", - "\n", - "
\n", - " \n", - "\n", - "\n", - "\n", - " \n", - "
\n", - "\n", - "
\n", - " \n", - " \n", - " \n", - "
\n", - "\n", - "
\n", - "
\n" + "" ], "text/plain": [ " subject p \\\n", @@ -572,7 +331,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 7, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -603,7 +362,7 @@ "corese.resetRuleEngine(graph)\n", "query = \"select * where {?s a ?type} order by ?type\"\n", "print(corese.sparqlSelect(graph, query=query))\n", - "print(\"Graph size: \", graph.graphSize())\n" + "print(\"Graph size: \", graph.size())\n" ] }, { @@ -617,7 +376,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 8, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -635,8 +394,8 @@ } ], "source": [ - "corese.loadRuleEngine(graph, profile=corese.RuleEngine.Profile.RDFS)\n", - "print(\"Graph size: \", graph.graphSize())" + "corese.loadRuleEngine(graph, profile='rdfs')\n", + "print(\"Graph size: \", graph.size())" ] }, { @@ -650,7 +409,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 9, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -684,7 +443,7 @@ "source": [ "query = \"select * where {?s a ?type} order by ?type\"\n", "print(corese.sparqlSelect(graph, query=query))\n", - "print(\"Graph size: \", graph.graphSize())" + "print(\"Graph size: \", graph.size())" ] }, { @@ -707,7 +466,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 10, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -742,11 +501,11 @@ } ], "source": [ - "prefixes = '@prefix ex: '\n", - "contruct = '''CONSTRUCT {?A_Beatle a ex:BandMember }\n", + "construct = '''@prefix ex: \n", + " CONSTRUCT {?A_Beatle a ex:BandMember }\n", " WHERE { ex:The_Beatles ex:member ?A_Beatle}'''\n", "\n", - "results = corese.sparqlConstruct(graph, prefixes=prefixes, query=contruct)\n", + "results = corese.sparqlConstruct(graph, query=construct)\n", "\n", "print(results)" ] @@ -762,7 +521,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 11, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -933,9 +692,9 @@ "@prefix xsh: .\n", "@prefix sh: .\n", "\n", - " a sh:ValidationResult ;\n", + " a sh:ValidationResult ;\n", " sh:focusNode ;\n", - " sh:resultMessage \"Fail at: [sh:minCount 1 ;\\n sh:nodeKind sh:IRI ;\\n sh:path ]\" ;\n", + " sh:resultMessage \"Fail at: [sh:minCount 1 ;\\r\\n sh:nodeKind sh:IRI ;\\r\\n sh:path ]\" ;\n", " sh:resultPath ;\n", " sh:resultSeverity sh:Violation ;\n", " sh:sourceConstraintComponent sh:MinCountConstraintComponent ;\n", @@ -944,15 +703,14 @@ "\n", "[a sh:ValidationReport ;\n", " sh:conforms false ;\n", - " sh:result ] .\n", + " sh:result ] .\n", "\n", "\n" ] } ], "source": [ - "prefixes = '@prefix ex: '\n", - "report = corese.shaclValidate(graph, shacl_shape_ttl=data_shape_path, prefixes=prefixes)\n", + "report = corese.shaclValidate(graph, shacl_shape_ttl=data_shape_path)\n", "\n", "print(report)" ] @@ -980,15 +738,8 @@ "outputs": [ { "data": { - "application/vnd.google.colaboratory.intrinsic+json": { - "summary": "{\n \"name\": \"report_dataframe\",\n \"rows\": 1,\n \"fields\": [\n {\n \"column\": \"o\",\n \"properties\": {\n \"dtype\": \"string\",\n \"num_unique_values\": 1,\n \"samples\": [\n \"urn:uuid:66d7b5ea-0065-4f84-b0e4-d65ba0b16a11\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"type\",\n \"properties\": {\n \"dtype\": \"string\",\n \"num_unique_values\": 1,\n \"samples\": [\n \"http://www.w3.org/ns/shacl#ValidationResult\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"focusNode\",\n \"properties\": {\n \"dtype\": \"string\",\n \"num_unique_values\": 1,\n \"samples\": [\n \"http://example.com/Love_Me_Do\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"resultMessage\",\n \"properties\": {\n \"dtype\": \"string\",\n \"num_unique_values\": 1,\n \"samples\": [\n \"Fail at: [sh:minCount 1 ;\\n sh:nodeKind sh:IRI ;\\n sh:path ]\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"resultPath\",\n \"properties\": {\n \"dtype\": \"string\",\n \"num_unique_values\": 1,\n \"samples\": [\n \"http://example.com/performer\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"resultSeverity\",\n \"properties\": {\n \"dtype\": \"string\",\n \"num_unique_values\": 1,\n \"samples\": [\n \"http://www.w3.org/ns/shacl#Violation\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"sourceConstraintComponent\",\n \"properties\": {\n \"dtype\": \"string\",\n \"num_unique_values\": 1,\n \"samples\": [\n \"http://www.w3.org/ns/shacl#MinCountConstraintComponent\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"sourceShape\",\n \"properties\": {\n \"dtype\": \"string\",\n \"num_unique_values\": 1,\n \"samples\": [\n \"_:b9\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"value\",\n \"properties\": {\n \"dtype\": \"string\",\n \"num_unique_values\": 1,\n \"samples\": [\n \"0\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n }\n ]\n}", - "type": "dataframe", - "variable_name": "report_dataframe" - }, "text/html": [ - "\n", - "
\n", - "
\n", + "
\n", "\n", - "\n", - " \n", - "
\n", - "\n", - "\n", - "
\n", - " \n", - " \n", - " \n", - "
\n", - "\n", - "
\n", - "
\n" + "" ], "text/plain": [ - " type \\\n", - "o \n", - "urn:uuid:66d7b5ea-0065-4f84-b0e4-d65ba0b16a11 http://www.w3.org/ns/shacl#ValidationResult \n", - "\n", - " focusNode \\\n", - "o \n", - "urn:uuid:66d7b5ea-0065-4f84-b0e4-d65ba0b16a11 http://example.com/Love_Me_Do \n", + " type focusNode \\\n", + "0 http://www.w3.org/ns/shacl#ValidationResult http://example.com/Love_Me_Do \n", "\n", - " resultMessage \\\n", - "o \n", - "urn:uuid:66d7b5ea-0065-4f84-b0e4-d65ba0b16a11 Fail at: [sh:minCount 1 ;\n", - " sh:nodeKind sh:IRI... \n", + " resultMessage \\\n", + "0 Fail at: [sh:minCount 1 ;\n", + " sh:nodeKind sh:IR... \n", "\n", - " resultPath \\\n", - "o \n", - "urn:uuid:66d7b5ea-0065-4f84-b0e4-d65ba0b16a11 http://example.com/performer \n", + " resultPath resultSeverity \\\n", + "0 http://example.com/performer http://www.w3.org/ns/shacl#Violation \n", "\n", - " resultSeverity \\\n", - "o \n", - "urn:uuid:66d7b5ea-0065-4f84-b0e4-d65ba0b16a11 http://www.w3.org/ns/shacl#Violation \n", - "\n", - " sourceConstraintComponent \\\n", - "o \n", - "urn:uuid:66d7b5ea-0065-4f84-b0e4-d65ba0b16a11 http://www.w3.org/ns/shacl#MinCountConstraintC... \n", - "\n", - " sourceShape value \n", - "o \n", - "urn:uuid:66d7b5ea-0065-4f84-b0e4-d65ba0b16a11 _:b9 0 " + " sourceConstraintComponent sourceShape value \n", + "0 http://www.w3.org/ns/shacl#MinCountConstraintC... _:b9 0 " ] }, "execution_count": 14, @@ -1264,7 +851,7 @@ { "data": { "text/plain": [ - "JavaObject id=o37" + "JavaObject id=o40" ] }, "execution_count": 15, @@ -1298,7 +885,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 16, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -1346,12 +933,12 @@ "id": "o41J9o5We64u" }, "source": [ - "Wer can add some more detailes for the album *Help!* and see what was added." + "We can add some more detailes for the album *Help!* and see what was added." ] }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 17, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -1363,10 +950,10 @@ { "data": { "text/plain": [ - "JavaObject id=o46" + "JavaObject id=o51" ] }, - "execution_count": 16, + "execution_count": 17, "metadata": {}, "output_type": "execute_result" } @@ -1394,7 +981,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 18, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -1430,10 +1017,17 @@ "\n", "results = exec.query(query)\n", "\n", - "results_ttl = corese.ResultFormat.create(results, corese.ResultFormat.TURTLE_FORMAT)\n", + "results_ttl = corese.ResultFormat.create(results, corese.ResultFormat.TURTLE)\n", "\n", "print(results_ttl)" ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { @@ -1442,7 +1036,7 @@ "toc_visible": true }, "kernelspec": { - "display_name": "Python 3", + "display_name": "corese-python", "language": "python", "name": "python3" }, @@ -1456,7 +1050,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.14" + "version": "3.13.0" } }, "nbformat": 4, diff --git a/src/pycorese/VERSION.txt b/src/pycorese/VERSION.txt index 9faa1b7..0ea3a94 100644 --- a/src/pycorese/VERSION.txt +++ b/src/pycorese/VERSION.txt @@ -1 +1 @@ -0.1.5 +0.2.0 diff --git a/src/pycorese/api.py b/src/pycorese/api.py index c333fd0..9c6fa28 100644 --- a/src/pycorese/api.py +++ b/src/pycorese/api.py @@ -3,6 +3,7 @@ import os import re from collections import namedtuple +from typing import Optional def _isFile(input: str): file_path_pattern = r'^(?:[a-zA-Z]:\\|\.{1,2}[\\\/]|\/)?(?:[\w\-\s]+[\\\/]?)+[\w\-\s]+\.[\w]+$' @@ -15,6 +16,10 @@ def _isFile(input: str): return False +def _is_url(input: str): + url_pattern = r'^https?://.*\.[\w]+$' + return re.match(url_pattern, input) is not None + def _is_rdf_xml(content): rdf_xml_pattern = r'^\s*<\?xml.*\?>.* str|None: @@ -86,28 +91,14 @@ def coreseVersion(self)-> str|None: return self._bridge.coreseVersion() - def unloadCorese(self): + def loadCorese(self) -> Optional[object]: """ - Explicitly unload Corese library. + Load Corese library into JVM and expose the Corese classes. - It's not necessary to call this method, as the library is automatically - unloaded when the Python interpreter exits. - - Warning + Returns ------- - After unloading Corese bridged by ``JPype`` it is not possible to restart it. - """ - self._bridge.unloadCorese() - - self.java_gateway = None - - self.Graph = None - self.QueryProcess = None - self.ResultFormat = None - self.Load = None - - def loadCorese(self) -> None: - """Load Corese library into JVM and expose the Corese classes. + object + Java Gateway object if the library is loaded successfully. Otherwise, returns None. """ #TODO: refactor if self.java_bridge == 'py4j': @@ -128,15 +119,16 @@ def loadCorese(self) -> None: # them without listing every single one of them here self.Graph = self._bridge.Graph self.Load = self._bridge.Load + self.Loader = self._bridge.Loader self.QueryProcess = self._bridge.QueryProcess self.ResultFormat = self._bridge.ResultFormat self.RuleEngine = self._bridge.RuleEngine self.Transformer = self._bridge.Transformer # Classes to manage Graph(s) with different storage options - self.DataManager = self._bridge.DataManager - self.CoreseGraphDataManager = self._bridge.CoreseGraphDataManager - self.CoreseGraphDataManagerBuilder = self._bridge.CoreseGraphDataManagerBuilder + self._DataManager = self._bridge.DataManager + self._CoreseGraphDataManager = self._bridge.CoreseGraphDataManager + self._CoreseGraphDataManagerBuilder = self._bridge.CoreseGraphDataManagerBuilder # Classes to manage SHACL validation self.Shacl = self._bridge.Shacl @@ -149,57 +141,90 @@ def loadCorese(self) -> None: 'http://www.w3.org/ns/shacl#' ) - self.SHACL_REPORT_QUERY='''SELECT ?o ?p ?s - WHERE { ?o a sh:ValidationResult. - ?o ?p ?s. }''' + self.SHACL_REPORT_QUERY=f'''@prefix sh: <{self.Namespaces.SHACL}> . + SELECT ?o ?p ?s + WHERE {{ ?o a sh:ValidationResult. + ?o ?p ?s.}}''' + + return self.java_gateway + + def unloadCorese(self): + """ + Explicitly unload Corese library. + + It's not necessary to call this method, as the library is automatically + unloaded when the Python interpreter exits. + + Warning + ------- + After unloading Corese bridged by ``JPype`` it is not possible to restart it. + """ + self._bridge.unloadCorese() + + self.java_gateway = None + self.Graph = None + self.QueryProcess = None + self.ResultFormat = None + self.Load = None #TODO: Add support for the other RDF formats - def loadRDF(self, rdf: str, graph=None)-> object: + def loadRDF(self, rdf: str, graph: Optional[object] = None)-> object: """ Load RDF file/string into Corese graph. Supported formats are RDF/XML and Turtle. Parameters ---------- rdf : str - Path to the RDF file or a string with RDF content. + Path or URL of an RDF file or a string with RDF content. graph : object, optional - Corese object of either ``fr.inria.corese.core.Graph`` or ``fr.inria.core.storage.CoreseGraphDataManager`` type. + Corese ``fr.inria.corese.core.Graph`` object. If an object is not provided (default), new Graph and GraphManager will be created. Returns ------- object - Corese ``fr.inria.core.storage.CoreseGraphDataManager`` object. + Corese ``fr.inria.core.Graph`` object. """ if not self.java_gateway: self.loadCorese() assert self.Graph, 'Corese classes are not loaded properly.' assert self.Load, 'Corese classes are not loaded properly.' - assert self.CoreseGraphDataManagerBuilder, 'Corese classes are not loaded properly.' - if not graph: - graph = self.Graph() + #TODO: add support for DataManager(s) for different storage options + # the option has to come as a parameter + # if not graph: + # graph = self.Graph() + # graph_mgr = self.CoreseGraphDataManagerBuilder().build() + # else: + # graph_mgr = self.CoreseGraphDataManagerBuilder().graph(graph).build() + # + # ld = self.Load.create(graph, graph_mgr) + # ... + # return graph_mgr.getGraph() - graph_mgr = self.CoreseGraphDataManagerBuilder().build() + graph = graph or self.Graph() - ld = self.Load().create(graph, graph_mgr) + ld = self.Load.create(graph) + #TODO: add support for a URL if _isFile(rdf): ld.parse(rdf) + elif _is_url(rdf): + ld.parse(rdf) else: if _is_rdf_xml(rdf): - ld.loadString(rdf, self.Load.RDFXML_FORMAT) + ld.loadString(rdf, self.Loader.format.RDFXML_FORMAT) elif _is_turtle(rdf): - ld.loadString(rdf, self.Load.TURTLE_FORMAT) + ld.loadString(rdf, self.Loader.format.TURTLE_FORMAT) else: raise ValueError('Unsupported RDF format. Only RDF/XML and Turtle are supported by this version') - return graph_mgr + return graph.getGraph() def loadRuleEngine(self, graph: object, - profile: object, + profile: str, replace:bool = False)-> object: """ Load the rule engine for a given graph. @@ -207,13 +232,9 @@ def loadRuleEngine(self, graph: object, Parameters ---------- graph : object - Corese Graph or DataManager object - profile : object - Profile object for the rule engine. Accepted values: - ``RuleEngine.Profile.RDFS``, - ``RuleEngine.Profile.OWLRL``, - ``RuleEngine.Profile.OWLRL_LITE``, - ``RuleEngine.Profile.OWLRL_EXT`` + Corese ``fr.inria.corese.core.Graph`` object + profile : str + Profile the rule engine. Accepted values: Accepted values: *rdfs*, *owlrl*, *owlrl_lite*, *owlrl_ext* replace : bool, optional Replace the existing rule engine. Default is False. @@ -225,7 +246,17 @@ def loadRuleEngine(self, graph: object, assert self.RuleEngine, 'Corese classes are not loaded properly.' assert graph, 'Graph object is required.' assert profile, 'Profile object is required.' - #TODO: assert profile is valid + + if profile == 'rdfs': + profile = self.RuleEngine.Profile.RDFS + elif profile == 'owlrl_lite': + profile = self.RuleEngine.Profile.OWLRL_LITE + elif profile == 'owlrl_ext': + profile = self.RuleEngine.Profile.OWLRL_EXT + elif profile == 'owlrl': + profile = self.RuleEngine.Profile.OWLRL + else: + raise ValueError('Invalid profile. Accepted values are: "rdfs", "owlrl_lite", "owlrl_ext", "owlrl"') if replace: self.resetRuleEngine(graph) @@ -244,7 +275,7 @@ def resetRuleEngine(self, graph: object)-> None: Parameters ---------- graph : object - Corese Graph or DataManager object + Corese ``fr.inria.corese.core.Graph`` object """ assert self.RuleEngine, 'Corese classes are not loaded properly.' assert graph, 'Graph object is required.' @@ -252,29 +283,89 @@ def resetRuleEngine(self, graph: object)-> None: rule_engine = self.RuleEngine.create(graph.getGraph()) rule_engine.remove() + def parsePrefixes(self, query: str)-> dict: + """ + Parse a query string to extract a dictionary of (prefix, namespace) pairs + + Parameters + ---------- + query : str + Query string that may contain PREFIX declarations + + Returns + ------- + dict + Dictionary of (prefix, namespace) pairs or an empty dictionary if no prefixes are found. + """ + pattern = re.compile(r'PREFIX\s+(\w+):\s+<\s*(.*?)\s*>') + matches = pattern.findall(query) + prefixes = {prefix + ':' : url for prefix, url in matches} + return prefixes + + def _applyPrefixes(self, query_result: object|pd.DataFrame, ns: dict|None)-> object|pd.DataFrame: + """ + Substitute long namespace names in the URIs with prefixes. This method can be applied either + to a Corese query result map or a DataFrame. + + applying prefixes to a DataFrame is faster since it is done in python. Applying prefixes to a map + is slower since it is done in the Java process for each value. + + Parameters + ---------- + query_result : object or pd.DataFrame + Query result in Corese format or a DataFrame. + ns : dict, optional + Dictionary of (prefix, namespace) pairs. Default is None. + + Returns + ------- + object or pd.DataFrame + Query result with prefixes applied. + """ + + if not isinstance(ns, dict): + return query_result + + if isinstance(query_result, pd.DataFrame): + # prefix in the DataFrame -fast + return query_result.fillna('')\ + .replace(ns.values(), ns.keys(), regex=True)\ + .replace('',pd.NA,regex = True) + else: + # assume it's a map output of the query -slow + for i, row in enumerate(query_result.getMappingList()): + for j, var in enumerate(row.getList()): + if not var.isEmpty() and var.size() > 1 and var[1].isURI(): + for prefix, namespace in ns.items(): + if var[1].contains(namespace): + new_uri = var[1].stringValue().replace(namespace, prefix) + var[1].setValue(new_uri) + + return query_result + def sparqlSelect(self, graph: object, - prefixes: str|list|None = None, query: str ='SELECT * WHERE {?s ?p ?o} LIMIT 5', - return_dataframe: bool =True)-> object|pd.DataFrame: + return_dataframe: bool =True, + post_apply_prefixes: bool = True)-> object|pd.DataFrame: """ Execute SPARQL SELECT or ASK query on Corese graph. Optionally return the result as DataFrame. Parameters ---------- graph : object - Corese Graph or DataManager object - prefixes : str or list, optional - namespace prefixes. Default is None. + Corese ``fr.inria.corese.core.Graph`` object query : str, optional SPARQL query. By default five first triples of the graph are returned. return_dataframe : bool, optional Return the result as a DataFrame. Default is True. + post_apply_prefixes : bool, optional + Substitute long namespaces with prefixes defined in the query . Default is True. Returns ------- object or pd.DataFrame Result of the SPARQL query in CSV-formatted ``fr.inria.core.print.ResultFormat`` - object or a DataFrame. + object or a ``pandas.DataFrame``. """ assert self.QueryProcess, 'Corese classes are not loaded properly.' assert self.ResultFormat, 'Corese classes are not loaded properly.' @@ -282,20 +373,21 @@ def sparqlSelect(self, graph: object, if not graph: raise ValueError('Graph object is required.') - #TODO: extract method to create a prefix string - if not prefixes: - prefixes = '' - if isinstance(prefixes, list): - prefixes = '\n'.join(prefixes) + # create a dictionary of (prefix, namespace) pairs + ns = self.parsePrefixes(query) exec = self.QueryProcess.create(graph) - map = exec.query('\n'.join([prefixes, query]) ) + map = exec.query(query) # to keep it simple for now return the result in CSV format result = self.ResultFormat.create(map, self.ResultFormat.SPARQL_RESULTS_CSV) + # or return a DataFrame if return_dataframe: - return self.toDataFrame(result) + if post_apply_prefixes: + return self._applyPrefixes(self.toDataFrame(result), ns) + else: + return self.toDataFrame(result) return result @@ -329,9 +421,8 @@ def toDataFrame(self, queryResult: object, return df - #TODO: add timeout - def sparqlConstruct(self, graph: object, - prefixes: str|list|None = None, + #TODO: add timeout parameter + def sparqlConstruct(self, graph: Optional[object] = None, query: str ='', merge: bool=False)-> object: """ @@ -341,14 +432,12 @@ def sparqlConstruct(self, graph: object, Parameters ---------- - graph : object - Corese Graph or DataManager object - prefixes : str or list, optional - namespace prefixes. Default is None. + graph : object, optional + Corese ``fr.inria.corese.core.Graph`` object. If not provided (default), a new graph is created. query : str, optional - SPARQL query. Default is empty string resulting in empty graph. + SPARQL query. Defaults to an empty string, resulting in an empty graph. merge : bool, optional - Option to merge the result with the existing graph. Default is False. + Option to merge the result with the existing graph passed in the parameters. Default is False. Returns ------- @@ -359,16 +448,10 @@ def sparqlConstruct(self, graph: object, assert self.ResultFormat, 'Corese classes are not loaded properly.' if not graph: - raise ValueError('Graph object is required.') - - #todo: extract method to create a prefix string - if not prefixes: - prefixes = '' - if isinstance(prefixes, list): - prefixes = '\n'.join(prefixes) + graph = self.Graph() exec = self.QueryProcess.create(graph) - map = exec.query('\n'.join([prefixes, query]) ) + map = exec.query(query) if merge: graph.getGraph().merge(map.getGraph()) @@ -377,6 +460,7 @@ def sparqlConstruct(self, graph: object, return result + def toTurtle(self, rdf:object)-> str: """ Convert RDF/XML to Turtle format. @@ -401,9 +485,8 @@ def toTurtle(self, rdf:object)-> str: #TODO: ASk Remi what are the acceptable shacl formats def shaclValidate(self, graph: object, - prefixes: str|list|None = None, - shacl_shape_ttl: str ='', - return_dataframe = False)-> object: + shacl_shape_ttl: str ='', + return_dataframe = False)-> str: """ Validate RDF graph against SHACL shape. @@ -412,9 +495,7 @@ def shaclValidate(self, graph: object, Parameters ---------- graph : object - Corese Graph or DataManager object - prefixes : str or list, optional - namespace prefixes. Default is None. + Corese ``fr.inria.corese.core.Graph`` object shacl_shape_ttl : str, optional SHACL shape in Turtle format. If not provided, the validation will be skipped. return_dataframe : bool, optional @@ -422,20 +503,11 @@ def shaclValidate(self, graph: object, Returns ------- - object + str SHACL validation report in Turtle format. """ assert self.Shacl, 'Corese classes are not loaded properly.' - prefix_shacl = f'@prefix sh: <{self.Namespaces.SHACL}> .' - - if not prefixes: - prefixes = '' - if isinstance(prefixes, list): - prefixes = '\n'.join(prefixes) - - prefixes = '\n'.join([prefixes, prefix_shacl]) - shapeGraph = self.Graph() ld = self.Load.create(shapeGraph) @@ -444,8 +516,8 @@ def shaclValidate(self, graph: object, ld.parse(shacl_shape_ttl) else: # Load shape graph from string - ld.loadString('\n'.join([prefixes, shacl_shape_ttl]), - self.Load.TURTLE_FORMAT) + ld.loadString(shacl_shape_ttl, + self.Loader.format.TURTLE_FORMAT) # Evaluation shacl = self.Shacl(graph.getGraph(), shapeGraph) @@ -473,19 +545,140 @@ def shaclReportToDataFrame(self, validation_report: str)-> pd.DataFrame: pd.DataFrame Validation report as a DataFrame. """ - prefix_shacl = f'@prefix sh: <{self.Namespaces.SHACL}> .' validation_report_graph = self.loadRDF(validation_report) - report = self.sparqlSelect(validation_report_graph, prefix_shacl, self.SHACL_REPORT_QUERY) + report = self.sparqlSelect(validation_report_graph, self.SHACL_REPORT_QUERY) report = report.pivot(index='o', columns='p', values='s') report.columns = [uri.split('#')[-1] for uri in report.columns] - #TODO cleanup the report + report.reset_index(drop=True, inplace=True) return report + #TODO: add a named graph parameter + def addTriple(self, graph: object, subject: str, predicate: str, obj: str)-> object: + """ + Add a triple to the default Corese graph. + + Parameters + ---------- + graph : object + Corese ``fr.inria.corese.core.Graph`` object + subject : str + Subject of the triple. Must be a URI. + predicate : str + Predicate of the triple. Must be a URI. + obj : str + Object of the triple. Must be a URI or a literal. + + Returns + ------- + object + Corese ``fr.inria.corese.core.Graph`` object with the new triple. + """ + subject = graph.addResource(subject) + predicate = graph.addProperty(predicate) + + if obj.startswith('http'): + obj = graph.addResource(obj) + else: + obj = graph.addLiteral(obj) + + graph.addEdge(subject, predicate, obj) + + return graph + + def removeTriple(self, graph: object, subject: str, predicate: str, obj: str)-> object: + """ + Remove a triple from the default Corese graph. + + Parameters + ---------- + graph : object + Corese ``fr.inria.corese.core.Graph`` object + subject : str + Subject of the triple. Must be a URI. + predicate : str + Predicate of the triple. Must be a URI. + obj : str + Object of the triple. Must be a URI or a literal. + + Returns + ------- + object + Corese ``fr.inria.corese.core.Graph`` object without the triple. + """ + subject = graph.getResource(subject) + predicate = graph.getResource(predicate) + + if obj.startswith('http'): + obj = graph.getResource(obj) + else: + obj = graph.getLiteral(obj) + + graph.delete(subject, predicate, obj) + + return graph + + def getTripleObject(self, graph, subject:str, predicate:str)->Optional[str]: + """ + Get the object of a triple. It can be a URI or a literal. + + Parameters + ---------- + + Return + ------ + str + String representation of the object of the triple or None if the triple does not exist. + """ + subject = graph.getResource(subject) + predicate = graph.getResource(predicate) + + obj = graph.getEdgesRDF4J(subject, predicate, None, None) + + if obj.iterator().hasNext(): + return obj.iterator().next().getObjectNode().getLabel() + + return None + + def exportRDF(self, graph:object, path:str, format:str ='turtle', overwrite:bool=False)-> None: + """ + Export Corese graph to an RDF file. Only RDF/XML and Turtle are supported by this version. + + Parameters + ---------- + graph : object + Corese ``fr.inria.corese.core.Graph`` object. + path : str + Path to the output RDF file. + format : str, optional + RDF format. Default is Turtle. Accepted values are + *turtle*, *ttl*, *xml*, and *rdfxml*. + + overwrite : bool, optional + Overwrite the file if it exists. Default is False. + """ + + format = format.lower() + + if 'xml' in format or 'rdf' in format: + format = self.Transformer.RDFXML + elif 'ttl' in format or 'turtle' in format: + format = self.Transformer.TURTLE + else: + raise ValueError('Unsupported RDF format. Only RDF/XML and Turtle are supported by this version') + + if os.path.exists(path) and not overwrite: + raise FileExistsError(f'{path} already exists. Set overwrite=True to overwrite the file.') + + transformer = self.Transformer.create(graph, format) + transformer.write(path) + + #TODO: apply prefixes to the output file + if __name__ == "__main__": diff --git a/src/pycorese/corese_version.py b/src/pycorese/corese_version.py index aea2a14..7b68eb3 100644 --- a/src/pycorese/corese_version.py +++ b/src/pycorese/corese_version.py @@ -1,6 +1,6 @@ # -# corese lib dependancy package version +# corese lib dependency package version # -corese_version = "4.6.0" +corese_version = "4.6.1" diff --git a/src/pycorese/jpype_bridge.py b/src/pycorese/jpype_bridge.py index f79514b..476ffd9 100644 --- a/src/pycorese/jpype_bridge.py +++ b/src/pycorese/jpype_bridge.py @@ -76,7 +76,7 @@ def coreseVersion(self): pass if version is None: - loggingWarning(f"JPype: the CORESE library is too old. coreseVersion() is available since 4.6.0 only.") + logging.warning(f"JPype: the CORESE library is too old. coreseVersion() is available since 4.6.0 only.") return version @@ -132,6 +132,7 @@ def loadCorese(self, memory_allocation=None) -> jpype: # Import of class from fr.inria.corese.core import Graph # type: ignore from fr.inria.corese.core.load import Load # type: ignore + from fr.inria.corese.core.api import Loader # type: ignore from fr.inria.corese.core.logic import RDF, RDFS # type: ignore from fr.inria.corese.core.print import ResultFormat # type: ignore from fr.inria.corese.core.query import QueryProcess # type: ignore @@ -151,6 +152,7 @@ def loadCorese(self, memory_allocation=None) -> jpype: self.Graph = Graph self.Load = Load + self.Loader = Loader self.QueryProcess = QueryProcess self.ResultFormat = ResultFormat self.RDF = RDF diff --git a/src/pycorese/py4J_bridge.py b/src/pycorese/py4J_bridge.py index f4f9aa6..b3055f6 100644 --- a/src/pycorese/py4J_bridge.py +++ b/src/pycorese/py4J_bridge.py @@ -63,12 +63,13 @@ def coreseVersion(self): """ version = None try: - version = corese.java_gateway.jvm.fr.inria.corese.core.util.CoreseInfo.getVersion() + version = self.java_gateway.jvm.fr.inria.corese.core.util.CoreseInfo.getVersion() + except: pass if version is None: - logging.Warning(f"Py4j: the CORESE library is too old. coreseVersion() is available since 4.6.0 only.") + logging.warning(f"Py4j: the CORESE library is too old. coreseVersion() is available since 4.6.0 only.") return version @@ -119,6 +120,7 @@ def loadCorese(self, memory_allocation=None) -> JavaGateway: self.Graph = self.java_gateway.jvm.fr.inria.corese.core.Graph self.Load = self.java_gateway.jvm.fr.inria.corese.core.load.Load + self.Loader = self.java_gateway.jvm.fr.inria.corese.core.api.Loader self.QueryProcess = self.java_gateway.jvm.fr.inria.corese.core.query.QueryProcess self.ResultFormat = self.java_gateway.jvm.fr.inria.corese.core.print.ResultFormat self.RDF = self.java_gateway.jvm.fr.inria.corese.core.logic.RDF From 28bb6a338be22729225c1351375e8d13025cf38f Mon Sep 17 00:00:00 2001 From: Anna Bobasheva <33026767+AnnaBobasheva@users.noreply.github.com> Date: Fri, 17 Jan 2025 15:24:15 +0100 Subject: [PATCH 3/3] Update CHANGES.md --- CHANGES.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 35c31d6..f8ae2ed 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,12 @@ # Changelog +## Version 0.2.0 + +- wrapped corese-core version 4.6.1 +- fixed broken code in CoreseAPI and Java bridge classes +- refactored CoreseAPI for usage simplification +- updated example notebook + ## Version 0.1.5 - added corese-python/pycorese API documentation