diff --git a/README.md b/README.md
index 580cfce..8cf4486 100644
--- a/README.md
+++ b/README.md
@@ -15,13 +15,297 @@ Or get the newest development version via:
pip install git+https://github.com/sensein/ProvSense/.git
```
-## Quick start
+## Usage
+For example files, see `example` directory.
+
+### 1️⃣ Compare Two Knowledge Graph Strings
+You can compare two JSON-LD/Turtle/N-Triples formatted knowledge graphs as strings.
```Python
-from ProvSense.app import hello_world
+from ProvSense.app import compare_items
+
+# KG string comparison
+src = """{ "@context": { "ex": "http://example.org/" }, "@id": "ex:Person1", "@type": "ex:Person", "ex:name": "Alice" }"""
+
+dst = """{ "@context": { "ex": "http://example.org/" }, "@id": "ex:Person1", "@type": "ex:Person", "ex:name": "BoB" }"""
+
+# Compare and print results
+print(compare_items(src, dst))
+
+```
+**Output:**
+```python
+[
+ {'subject': 'http://example.org/Person1',
+ 'property': 'http://example.org/name',
+ 'src_value': 'Alice',
+ 'dst_value': 'BoB'},
+ {'subject': 'http://example.org/Person1',
+ 'property': 'http://example.org/name',
+ 'src_value': 'Alice',
+ 'dst_value': 'BoB'}
+]
+```
+
+### 2️⃣ Compare Two Knowledge Graph Files
+Pass file paths to compare JSON-LD, TTL, or NT files.
+
+```python
+from ProvSense.app import compare_items
+print(compare_items("test_src.jsonld", "test_dst.jsonld"))
+
+```
+**Output:**
+```python
+[
+ {'subject': 'urn:uuid:550e8400-e29b-41d4-a716-446655440000',
+ 'property': 'http://example.org/worksAt',
+ 'src_value': 'http://example.org/Company',
+ 'dst_value': 'http://example.org/CompanyY'},
+ {'subject': 'urn:uuid:550e8400-e29b-41d4-a716-446655440000',
+ 'property': 'http://example.org/worksAt',
+ 'src_value': 'http://example.org/Company',
+ 'dst_value': 'http://example.org/CompanyY'},
+ {'subject': 'urn:uuid:550e8400-e29b-41d4-a716-446655440000',
+ 'property': 'http://example.org/location',
+ 'src_value': None,
+ 'dst_value': 'New York'}
+]
+
+```
+### 3️⃣ Compare Two Directories
+Compare all JSON-LD, TTL, and NT files in two folders recursively.
-hello_world()
+Note: The source and destination folders must contain files with matching filenames for a valid comparison.
+```python
+from ProvSense.app import compare_items
+print(compare_items("src", "dst"))
+```
+
+**Output:**
+```python
+
+{'test_file.jsonld': [{'subject': 'urn:uuid:550e8400-e29b-41d4-a716-446655440000',
+ 'property': 'http://example.org/worksAt',
+ 'src_value': 'http://example.org/CompanyX',
+ 'dst_value': 'http://example.org/CompanyY'},
+ {'subject': 'urn:uuid:550e8400-e29b-41d4-a716-446655440000',
+ 'property': 'http://example.org/location',
+ 'src_value': None,
+ 'dst_value': 'New York'},
+ {'subject': 'urn:uuid:550e8400-e29b-41d4-a716-446655440000',
+ 'property': 'http://example.org/worksAt',
+ 'src_value': 'http://example.org/CompanyX',
+ 'dst_value': 'http://example.org/CompanyY'}],
+ 'test_file_b.jsonld': [{'subject': 'http://dbpedia.org/resource/Albert_Einstein',
+ 'property': 'http://schema.org/affiliation',
+ 'src_value': 'http://dbpedia.org/resource/Princeton_University',
+ 'dst_value': 'http://dbpedia.org/resource/ETH_Zurich'},
+ {'subject': 'http://dbpedia.org/resource/Albert_Einstein',
+ 'property': 'http://schema.org/affiliation',
+ 'src_value': 'http://dbpedia.org/resource/Princeton_University',
+ 'dst_value': 'http://dbpedia.org/resource/University_of_Berlin'},
+ {'subject': 'http://dbpedia.org/resource/Albert_Einstein',
+ 'property': 'http://schema.org/affiliation',
+ 'src_value': 'http://dbpedia.org/resource/ETH_Zurich',
+ 'dst_value': 'http://dbpedia.org/resource/Princeton_University'},
+ {'subject': 'http://dbpedia.org/resource/Albert_Einstein',
+ 'property': 'http://schema.org/affiliation',
+ 'src_value': 'http://dbpedia.org/resource/ETH_Zurich',
+ 'dst_value': 'http://dbpedia.org/resource/University_of_Berlin'},
+ {'subject': 'http://dbpedia.org/resource/Albert_Einstein',
+ 'property': 'http://schema.org/nationality',
+ 'src_value': 'http://dbpedia.org/resource/Switzerland',
+ 'dst_value': 'http://dbpedia.org/resource/Germany'},
+ {'subject': 'http://dbpedia.org/resource/Albert_Einstein',
+ 'property': 'http://schema.org/nationality',
+ 'src_value': 'http://dbpedia.org/resource/Switzerland',
+ 'dst_value': 'http://dbpedia.org/resource/United_States'},
+ {'subject': 'http://dbpedia.org/resource/Albert_Einstein',
+ 'property': 'http://schema.org/knowsAbout',
+ 'src_value': 'http://dbpedia.org/resource/Quantum_mechanics',
+ 'dst_value': 'http://dbpedia.org/resource/Brownian_motion'},
+ {'subject': 'http://dbpedia.org/resource/Albert_Einstein',
+ 'property': 'http://schema.org/knowsAbout',
+ 'src_value': 'http://dbpedia.org/resource/Quantum_mechanics',
+ 'dst_value': 'http://dbpedia.org/resource/Theory_of_relativity'},
+ {'subject': 'http://dbpedia.org/resource/Albert_Einstein',
+ 'property': 'http://schema.org/affiliation',
+ 'src_value': 'http://dbpedia.org/resource/University_of_Berlin',
+ 'dst_value': 'http://dbpedia.org/resource/Princeton_University'},
+ {'subject': 'http://dbpedia.org/resource/Albert_Einstein',
+ 'property': 'http://schema.org/affiliation',
+ 'src_value': 'http://dbpedia.org/resource/University_of_Berlin',
+ 'dst_value': 'http://dbpedia.org/resource/ETH_Zurich'},
+ {'subject': 'http://dbpedia.org/resource/Albert_Einstein',
+ 'property': 'http://schema.org/sameAs',
+ 'src_value': 'https://www.wikidata.org/wiki/Q937',
+ 'dst_value': 'https://en.wikipedia.org/wiki/Albert_Einstein'},
+ {'subject': 'http://dbpedia.org/resource/Albert_Einstein',
+ 'property': 'http://schema.org/nationality',
+ 'src_value': 'http://dbpedia.org/resource/Germany',
+ 'dst_value': 'http://dbpedia.org/resource/Switzerland'},
+ {'subject': 'http://dbpedia.org/resource/Albert_Einstein',
+ 'property': 'http://schema.org/nationality',
+ 'src_value': 'http://dbpedia.org/resource/Germany',
+ 'dst_value': 'http://dbpedia.org/resource/United_States'},
+ {'subject': 'http://dbpedia.org/resource/Albert_Einstein',
+ 'property': 'http://schema.org/knowsAbout',
+ 'src_value': 'http://dbpedia.org/resource/Brownian_motion',
+ 'dst_value': 'http://dbpedia.org/resource/Quantum_mechanics'},
+ {'subject': 'http://dbpedia.org/resource/Albert_Einstein',
+ 'property': 'http://schema.org/knowsAbout',
+ 'src_value': 'http://dbpedia.org/resource/Brownian_motion',
+ 'dst_value': 'http://dbpedia.org/resource/Theory_of_relativity'},
+ {'subject': 'http://dbpedia.org/resource/Albert_Einstein',
+ 'property': 'http://schema.org/alternateName',
+ 'src_value': 'Einstein',
+ 'dst_value': 'Prof. Einstein'},
+ {'subject': 'http://dbpedia.org/resource/Albert_Einstein',
+ 'property': 'http://schema.org/alternateName',
+ 'src_value': 'Prof. Einstein',
+ 'dst_value': 'Einstein'},
+ {'subject': 'http://dbpedia.org/resource/Albert_Einstein',
+ 'property': 'http://schema.org/knowsAbout',
+ 'src_value': 'http://dbpedia.org/resource/Theory_of_relativity',
+ 'dst_value': 'http://dbpedia.org/resource/Quantum_mechanics'},
+ {'subject': 'http://dbpedia.org/resource/Albert_Einstein',
+ 'property': 'http://schema.org/knowsAbout',
+ 'src_value': 'http://dbpedia.org/resource/Theory_of_relativity',
+ 'dst_value': 'http://dbpedia.org/resource/Brownian_motion'},
+ {'subject': 'http://dbpedia.org/resource/Albert_Einstein',
+ 'property': 'http://schema.org/nationality',
+ 'src_value': 'http://dbpedia.org/resource/United_States',
+ 'dst_value': 'http://dbpedia.org/resource/Switzerland'},
+ {'subject': 'http://dbpedia.org/resource/Albert_Einstein',
+ 'property': 'http://schema.org/nationality',
+ 'src_value': 'http://dbpedia.org/resource/United_States',
+ 'dst_value': 'http://dbpedia.org/resource/Germany'},
+ {'subject': 'http://dbpedia.org/resource/Albert_Einstein',
+ 'property': 'http://schema.org/sameAs',
+ 'src_value': 'https://en.wikipedia.org/wiki/Albert_Einstein',
+ 'dst_value': 'https://www.wikidata.org/wiki/Q937'},
+ {'subject': 'http://dbpedia.org/resource/Albert_Einstein',
+ 'property': 'http://schema.org/affiliation',
+ 'src_value': 'http://dbpedia.org/resource/ETH_Zurich',
+ 'dst_value': 'http://dbpedia.org/resource/Princeton_University'},
+ {'subject': 'http://dbpedia.org/resource/Albert_Einstein',
+ 'property': 'http://schema.org/affiliation',
+ 'src_value': 'http://dbpedia.org/resource/University_of_Berlin',
+ 'dst_value': 'http://dbpedia.org/resource/Princeton_University'},
+ {'subject': 'http://dbpedia.org/resource/Albert_Einstein',
+ 'property': 'http://schema.org/affiliation',
+ 'src_value': 'http://dbpedia.org/resource/Princeton_University',
+ 'dst_value': 'http://dbpedia.org/resource/ETH_Zurich'},
+ {'subject': 'http://dbpedia.org/resource/Albert_Einstein',
+ 'property': 'http://schema.org/affiliation',
+ 'src_value': 'http://dbpedia.org/resource/University_of_Berlin',
+ 'dst_value': 'http://dbpedia.org/resource/ETH_Zurich'},
+ {'subject': 'http://dbpedia.org/resource/Albert_Einstein',
+ 'property': 'http://schema.org/nationality',
+ 'src_value': 'http://dbpedia.org/resource/Germany',
+ 'dst_value': 'http://dbpedia.org/resource/Switzerland'},
+ {'subject': 'http://dbpedia.org/resource/Albert_Einstein',
+ 'property': 'http://schema.org/nationality',
+ 'src_value': 'http://dbpedia.org/resource/United_States',
+ 'dst_value': 'http://dbpedia.org/resource/Switzerland'},
+ {'subject': 'http://dbpedia.org/resource/Albert_Einstein',
+ 'property': 'http://schema.org/knowsAbout',
+ 'src_value': 'http://dbpedia.org/resource/Brownian_motion',
+ 'dst_value': 'http://dbpedia.org/resource/Quantum_mechanics'},
+ {'subject': 'http://dbpedia.org/resource/Albert_Einstein',
+ 'property': 'http://schema.org/knowsAbout',
+ 'src_value': 'http://dbpedia.org/resource/Theory_of_relativity',
+ 'dst_value': 'http://dbpedia.org/resource/Quantum_mechanics'},
+ {'subject': 'http://dbpedia.org/resource/Albert_Einstein',
+ 'property': 'http://schema.org/affiliation',
+ 'src_value': 'http://dbpedia.org/resource/Princeton_University',
+ 'dst_value': 'http://dbpedia.org/resource/University_of_Berlin'},
+ {'subject': 'http://dbpedia.org/resource/Albert_Einstein',
+ 'property': 'http://schema.org/affiliation',
+ 'src_value': 'http://dbpedia.org/resource/ETH_Zurich',
+ 'dst_value': 'http://dbpedia.org/resource/University_of_Berlin'},
+ {'subject': 'http://dbpedia.org/resource/Albert_Einstein',
+ 'property': 'http://schema.org/sameAs',
+ 'src_value': 'https://en.wikipedia.org/wiki/Albert_Einstein',
+ 'dst_value': 'https://www.wikidata.org/wiki/Q937'},
+ {'subject': 'http://dbpedia.org/resource/Albert_Einstein',
+ 'property': 'http://schema.org/nationality',
+ 'src_value': 'http://dbpedia.org/resource/Switzerland',
+ 'dst_value': 'http://dbpedia.org/resource/Germany'},
+ {'subject': 'http://dbpedia.org/resource/Albert_Einstein',
+ 'property': 'http://schema.org/nationality',
+ 'src_value': 'http://dbpedia.org/resource/United_States',
+ 'dst_value': 'http://dbpedia.org/resource/Germany'},
+ {'subject': 'http://dbpedia.org/resource/Albert_Einstein',
+ 'property': 'http://schema.org/knowsAbout',
+ 'src_value': 'http://dbpedia.org/resource/Quantum_mechanics',
+ 'dst_value': 'http://dbpedia.org/resource/Brownian_motion'},
+ {'subject': 'http://dbpedia.org/resource/Albert_Einstein',
+ 'property': 'http://schema.org/knowsAbout',
+ 'src_value': 'http://dbpedia.org/resource/Theory_of_relativity',
+ 'dst_value': 'http://dbpedia.org/resource/Brownian_motion'},
+ {'subject': 'http://dbpedia.org/resource/Albert_Einstein',
+ 'property': 'http://schema.org/alternateName',
+ 'src_value': 'Prof. Einstein',
+ 'dst_value': 'Einstein'},
+ {'subject': 'http://dbpedia.org/resource/Albert_Einstein',
+ 'property': 'http://schema.org/alternateName',
+ 'src_value': 'Einstein',
+ 'dst_value': 'Prof. Einstein'},
+ {'subject': 'http://dbpedia.org/resource/Albert_Einstein',
+ 'property': 'http://schema.org/knowsAbout',
+ 'src_value': 'http://dbpedia.org/resource/Quantum_mechanics',
+ 'dst_value': 'http://dbpedia.org/resource/Theory_of_relativity'},
+ {'subject': 'http://dbpedia.org/resource/Albert_Einstein',
+ 'property': 'http://schema.org/knowsAbout',
+ 'src_value': 'http://dbpedia.org/resource/Brownian_motion',
+ 'dst_value': 'http://dbpedia.org/resource/Theory_of_relativity'},
+ {'subject': 'http://dbpedia.org/resource/Albert_Einstein',
+ 'property': 'http://schema.org/nationality',
+ 'src_value': 'http://dbpedia.org/resource/Switzerland',
+ 'dst_value': 'http://dbpedia.org/resource/United_States'},
+ {'subject': 'http://dbpedia.org/resource/Albert_Einstein',
+ 'property': 'http://schema.org/nationality',
+ 'src_value': 'http://dbpedia.org/resource/Germany',
+ 'dst_value': 'http://dbpedia.org/resource/United_States'},
+ {'subject': 'http://dbpedia.org/resource/Albert_Einstein',
+ 'property': 'http://schema.org/sameAs',
+ 'src_value': 'https://www.wikidata.org/wiki/Q937',
+ 'dst_value': 'https://en.wikipedia.org/wiki/Albert_Einstein'},
+ {'subject': 'http://dbpedia.org/resource/Brownian_motion',
+ 'property': 'http://schema.org/name',
+ 'src_value': 'Brownian',
+ 'dst_value': 'Brownian Motion'},
+ {'subject': 'http://dbpedia.org/resource/Brownian_motion',
+ 'property': 'http://schema.org/name',
+ 'src_value': 'Brownian',
+ 'dst_value': 'Brownian Motion'},
+ {'subject': 'http://dbpedia.org/resource/Theory_of_relativity',
+ 'property': 'http://schema.org/name',
+ 'src_value': 'Relativity',
+ 'dst_value': 'Theory of Relativity'},
+ {'subject': 'http://dbpedia.org/resource/Theory_of_relativity',
+ 'property': 'http://schema.org/name',
+ 'src_value': 'Relativity',
+ 'dst_value': 'Theory of Relativity'}]}
+```
+
+## Running Tests
+
+To run all tests in the project:
+```sh
+python -m unittest discover src/tests
+```
+
+To run a specific test file:
+```sh
+python -m unittest src/tests/test_ttl_conversion.py
+```
+
+To run tests with verbose output:
+```sh
+python -m unittest -v src/tests/test_ttl_conversion.py
```
## To do:
-- [ ] A
-- [ ] lot
+- [ ] Add LLM for advanced comparison
\ No newline at end of file
diff --git a/example/Example.ipynb b/example/Example.ipynb
new file mode 100644
index 0000000..9e76f5c
--- /dev/null
+++ b/example/Example.ipynb
@@ -0,0 +1,634 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "id": "df466b1e-f37c-4306-bcc4-7b0172fcfc38",
+ "metadata": {},
+ "source": [
+ "## Example Usage\n",
+ "\n",
+ "This notebook provides a step-by-step guide on how to use the ProvSense application. You'll find practical examples demonstrating its key features, helping you integrate and leverage ProvSense effectively in your workflow."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "id": "45d39ee1-67c8-4cd9-a8f1-4bccc2cd83be",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "!python -m venv provsensetest"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "id": "17836767-0dce-43cf-9fae-d0df96c99e48",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "!source provsensetest/bin/activate"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "id": "3be25e47-cade-4af1-ae7b-4fd5c345868f",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "'/opt/anaconda3/bin/python'"
+ ]
+ },
+ "execution_count": 4,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "import sys\n",
+ "import pandas as pd\n",
+ "sys.executable"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "id": "987001f1-f4e5-44e3-9dc8-8b4cb8aeb164",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Requirement already satisfied: ProvSense in /opt/anaconda3/lib/python3.11/site-packages (1.0.2)\n",
+ "Requirement already satisfied: click<9.0.0,>=8.1.7 in /opt/anaconda3/lib/python3.11/site-packages (from ProvSense) (8.1.7)\n",
+ "Requirement already satisfied: rdflib<8.0.0,>=7.1.3 in /opt/anaconda3/lib/python3.11/site-packages (from ProvSense) (7.1.3)\n",
+ "Requirement already satisfied: sparqlwrapper<3.0.0,>=2.0.0 in /opt/anaconda3/lib/python3.11/site-packages (from ProvSense) (2.0.0)\n",
+ "Requirement already satisfied: pyparsing<4,>=2.1.0 in /opt/anaconda3/lib/python3.11/site-packages (from rdflib<8.0.0,>=7.1.3->ProvSense) (3.1.1)\n"
+ ]
+ }
+ ],
+ "source": [
+ "!pip install --upgrade ProvSense"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "180fb161-491a-4b81-9611-ffca048976d1",
+ "metadata": {},
+ "source": [
+ "## CLI-Usage"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "id": "1a14f6cc-aaf4-448e-a522-4fd503b4eea5",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Usage: provsense-cli [OPTIONS] COMMAND [ARGS]...\n",
+ "\n",
+ " CLI commands for the ProvSense\n",
+ "\n",
+ "Options:\n",
+ " --help Show this message and exit.\n",
+ "\n",
+ "Commands:\n",
+ " compare Compare changes in knowledge graph files (JSON-LD, Turtle)...\n"
+ ]
+ }
+ ],
+ "source": [
+ "!provsense-cli "
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "50bf4ebb-0483-43d2-be3a-35c69bd7a313",
+ "metadata": {},
+ "source": [
+ "### Compare Options"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "id": "340966b9-2889-4a93-a8ac-67a363b4b2fc",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Usage: provsense-cli compare [OPTIONS]\n",
+ "\n",
+ " Compare changes in knowledge graph files (JSON-LD, Turtle) across different\n",
+ " input sources.\n",
+ "\n",
+ " This function detects and analyzes differences between RDF data sources,\n",
+ " supporting comparisons at the folder, file, or direct string level.\n",
+ "\n",
+ " Options:\n",
+ "\n",
+ " - --source (str): The first input source, which can be a folder\n",
+ " path, file path, or RDF string. If providing a string, ensure it is\n",
+ " correctly formatted in JSON-LD or Turtle.\n",
+ "\n",
+ " - --destination (str): The second input source, which can be a\n",
+ " folder path, file path, or RDF string. If providing a string, ensure\n",
+ " it is correctly formatted in JSON-LD or Turtle.\n",
+ "\n",
+ " Returns: dict: A dictionary containing the comparison results\n",
+ " highlighting the differences.\n",
+ "\n",
+ " Raises: FileNotFoundError: If the specified folder or file does not\n",
+ " exist. ValueError: If an unsupported input type is provided.\n",
+ "\n",
+ " Example Usage: - Compare two folders:\n",
+ "\n",
+ " `$ cli compare --source \"folder1\" --destination \"folder2\"`\n",
+ "\n",
+ " - Compare two files:\n",
+ "\n",
+ " `$ cli compare --source \"file1.ttl\" --destination \"file2.ttl\"`\n",
+ "\n",
+ " - Compare two RDF strings:\n",
+ "\n",
+ " `$ cli compare --source '{\"@context\": \"http://schema.org\", \"name\":\n",
+ " \"Alice\"}' --destination '{\"@context\": \"http://schema.org\", \"name\":\n",
+ " \"Bob\"}'`\n",
+ "\n",
+ "Options:\n",
+ " --source TEXT Source path (file/folder) or string (e.g., JSON-LD/TTL)\n",
+ " for comparison. If providing a string, ensure it is\n",
+ " properly formatted. [required]\n",
+ " --destination TEXT Destination path (file/folder) or string (e.g., JSON-\n",
+ " LD/TTL) for comparison. If providing a string, ensure it\n",
+ " is properly formatted. [required]\n",
+ " --help Show this message and exit.\n"
+ ]
+ }
+ ],
+ "source": [
+ "!provsense-cli -- compare --help"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "7f8844d3-eea2-4f81-8c2e-0b8bdc5fa55e",
+ "metadata": {},
+ "source": [
+ "### KGs string comparison"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "id": "c96b400d-c662-4abe-88fc-74dfa0d2bbc5",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "2025-02-07 16:14:26,606 - INFO - Comparing strings: { \"@context\": { \"ex\": \"http://example.org/\" }, \"@id\": \"ex:Person1\", \"@type\": \"ex:Person\", \"ex:name\": \"Alice\" } with { \"@context\": { \"ex\": \"http://example.org/\" }, \"@id\": \"ex:Person1\", \"@type\": \"ex:Person\", \"ex:name\": \"Alice1\" }.\n",
+ "2025-02-07 16:14:26,606 - INFO - Checking the input types, i.e, whether it is ttl, json-ld or nt\n",
+ "2025-02-07 16:14:26,612 - INFO - Checking the input types, i.e, whether it is ttl, json-ld or nt\n",
+ "[{'subject': 'http://example.org/Person1', 'property': 'http://example.org/name', 'src_value': 'Alice', 'dst_value': 'Alice1'}]\n",
+ "Comparing { \"@context\": { \"ex\": \"http://example.org/\" }, \"@id\": \"ex:Person1\", \"@type\": \"ex:Person\", \"ex:name\": \"Alice\" } with { \"@context\": { \"ex\": \"http://example.org/\" }, \"@id\": \"ex:Person1\", \"@type\": \"ex:Person\", \"ex:name\": \"Alice1\" }.\n"
+ ]
+ }
+ ],
+ "source": [
+ "!provsense-cli -- compare --source '{ \"@context\": { \"ex\": \"http://example.org/\" }, \"@id\": \"ex:Person1\", \"@type\": \"ex:Person\", \"ex:name\": \"Alice\" }' --destination '{ \"@context\": { \"ex\": \"http://example.org/\" }, \"@id\": \"ex:Person1\", \"@type\": \"ex:Person\", \"ex:name\": \"Alice1\" }' "
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "4eccd352-6711-4baf-95e3-bc3b0c4b20e4",
+ "metadata": {},
+ "source": [
+ "### File comparison"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "id": "c8cab79f-2042-4fe1-860a-eb03b7820b49",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "2025-02-07 16:14:26,864 - INFO - Comparing files: test_src.jsonld with test_dst.jsonld.\n",
+ "2025-02-07 16:14:26,864 - INFO - Checking the input types, i.e, whether it is ttl, json-ld or nt\n",
+ "2025-02-07 16:14:26,869 - INFO - Checking the input types, i.e, whether it is ttl, json-ld or nt\n",
+ "[{'subject': 'urn:uuid:550e8400-e29b-41d4-a716-446655440000', 'property': 'http://example.org/worksAt', 'src_value': 'http://example.org/Company', 'dst_value': 'http://example.org/CompanyY'}, {'subject': 'urn:uuid:550e8400-e29b-41d4-a716-446655440000', 'property': 'http://example.org/location', 'src_value': None, 'dst_value': 'New York'}]\n",
+ "Comparing test_src.jsonld with test_dst.jsonld.\n"
+ ]
+ }
+ ],
+ "source": [
+ "!provsense-cli -- compare --source test_src.jsonld --destination test_dst.jsonld"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "f5195dc4-7c4d-4801-8613-626af5c56989",
+ "metadata": {},
+ "source": [
+ "### Compare KG triples present in a folder"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "id": "6403a33d-eae1-438e-9190-6a0216611f68",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "2025-02-07 16:14:27,125 - INFO - Comparing directories: src with dst.\n",
+ "2025-02-07 16:14:27,125 - INFO - Processing files: src/test_file.jsonld with dst/test_file.jsonld\n",
+ "2025-02-07 16:14:27,125 - INFO - Checking the input types, i.e, whether it is ttl, json-ld or nt\n",
+ "2025-02-07 16:14:27,130 - INFO - Checking the input types, i.e, whether it is ttl, json-ld or nt\n",
+ "2025-02-07 16:14:27,178 - INFO - Processing files: src/test_file_b.jsonld with dst/test_file_b.jsonld\n",
+ "2025-02-07 16:14:27,178 - INFO - Checking the input types, i.e, whether it is ttl, json-ld or nt\n",
+ "2025-02-07 16:14:27,179 - INFO - Checking the input types, i.e, whether it is ttl, json-ld or nt\n",
+ "{'test_file.jsonld': [{'subject': 'urn:uuid:550e8400-e29b-41d4-a716-446655440000', 'property': 'http://example.org/worksAt', 'src_value': 'http://example.org/CompanyX', 'dst_value': 'http://example.org/CompanyY'}, {'subject': 'urn:uuid:550e8400-e29b-41d4-a716-446655440000', 'property': 'http://example.org/location', 'src_value': None, 'dst_value': 'New York'}], 'test_file_b.jsonld': [{'subject': 'http://dbpedia.org/resource/Albert_Einstein', 'property': 'http://schema.org/sameAs', 'src_value': 'https://www.wikidata.org/wiki/Q937', 'dst_value': 'https://en.wikipedia.org/wiki/Albert_Einstein'}, {'subject': 'http://dbpedia.org/resource/Albert_Einstein', 'property': 'http://schema.org/nationality', 'src_value': 'http://dbpedia.org/resource/Germany', 'dst_value': 'http://dbpedia.org/resource/Switzerland'}, {'subject': 'http://dbpedia.org/resource/Albert_Einstein', 'property': 'http://schema.org/nationality', 'src_value': 'http://dbpedia.org/resource/Germany', 'dst_value': 'http://dbpedia.org/resource/United_States'}, {'subject': 'http://dbpedia.org/resource/Albert_Einstein', 'property': 'http://schema.org/affiliation', 'src_value': 'http://dbpedia.org/resource/University_of_Berlin', 'dst_value': 'http://dbpedia.org/resource/Princeton_University'}, {'subject': 'http://dbpedia.org/resource/Albert_Einstein', 'property': 'http://schema.org/affiliation', 'src_value': 'http://dbpedia.org/resource/University_of_Berlin', 'dst_value': 'http://dbpedia.org/resource/ETH_Zurich'}, {'subject': 'http://dbpedia.org/resource/Albert_Einstein', 'property': 'http://schema.org/nationality', 'src_value': 'http://dbpedia.org/resource/Switzerland', 'dst_value': 'http://dbpedia.org/resource/Germany'}, {'subject': 'http://dbpedia.org/resource/Albert_Einstein', 'property': 'http://schema.org/nationality', 'src_value': 'http://dbpedia.org/resource/Switzerland', 'dst_value': 'http://dbpedia.org/resource/United_States'}, {'subject': 'http://dbpedia.org/resource/Albert_Einstein', 'property': 'http://schema.org/knowsAbout', 'src_value': 'http://dbpedia.org/resource/Brownian_motion', 'dst_value': 'http://dbpedia.org/resource/Quantum_mechanics'}, {'subject': 'http://dbpedia.org/resource/Albert_Einstein', 'property': 'http://schema.org/knowsAbout', 'src_value': 'http://dbpedia.org/resource/Brownian_motion', 'dst_value': 'http://dbpedia.org/resource/Theory_of_relativity'}, {'subject': 'http://dbpedia.org/resource/Albert_Einstein', 'property': 'http://schema.org/knowsAbout', 'src_value': 'http://dbpedia.org/resource/Quantum_mechanics', 'dst_value': 'http://dbpedia.org/resource/Brownian_motion'}, {'subject': 'http://dbpedia.org/resource/Albert_Einstein', 'property': 'http://schema.org/knowsAbout', 'src_value': 'http://dbpedia.org/resource/Quantum_mechanics', 'dst_value': 'http://dbpedia.org/resource/Theory_of_relativity'}, {'subject': 'http://dbpedia.org/resource/Albert_Einstein', 'property': 'http://schema.org/knowsAbout', 'src_value': 'http://dbpedia.org/resource/Theory_of_relativity', 'dst_value': 'http://dbpedia.org/resource/Brownian_motion'}, {'subject': 'http://dbpedia.org/resource/Albert_Einstein', 'property': 'http://schema.org/knowsAbout', 'src_value': 'http://dbpedia.org/resource/Theory_of_relativity', 'dst_value': 'http://dbpedia.org/resource/Quantum_mechanics'}, {'subject': 'http://dbpedia.org/resource/Albert_Einstein', 'property': 'http://schema.org/affiliation', 'src_value': 'http://dbpedia.org/resource/Princeton_University', 'dst_value': 'http://dbpedia.org/resource/University_of_Berlin'}, {'subject': 'http://dbpedia.org/resource/Albert_Einstein', 'property': 'http://schema.org/affiliation', 'src_value': 'http://dbpedia.org/resource/Princeton_University', 'dst_value': 'http://dbpedia.org/resource/ETH_Zurich'}, {'subject': 'http://dbpedia.org/resource/Albert_Einstein', 'property': 'http://schema.org/alternateName', 'src_value': 'Einstein', 'dst_value': 'Prof. Einstein'}, {'subject': 'http://dbpedia.org/resource/Albert_Einstein', 'property': 'http://schema.org/alternateName', 'src_value': 'Prof. Einstein', 'dst_value': 'Einstein'}, {'subject': 'http://dbpedia.org/resource/Albert_Einstein', 'property': 'http://schema.org/nationality', 'src_value': 'http://dbpedia.org/resource/United_States', 'dst_value': 'http://dbpedia.org/resource/Germany'}, {'subject': 'http://dbpedia.org/resource/Albert_Einstein', 'property': 'http://schema.org/nationality', 'src_value': 'http://dbpedia.org/resource/United_States', 'dst_value': 'http://dbpedia.org/resource/Switzerland'}, {'subject': 'http://dbpedia.org/resource/Albert_Einstein', 'property': 'http://schema.org/affiliation', 'src_value': 'http://dbpedia.org/resource/ETH_Zurich', 'dst_value': 'http://dbpedia.org/resource/University_of_Berlin'}, {'subject': 'http://dbpedia.org/resource/Albert_Einstein', 'property': 'http://schema.org/affiliation', 'src_value': 'http://dbpedia.org/resource/ETH_Zurich', 'dst_value': 'http://dbpedia.org/resource/Princeton_University'}, {'subject': 'http://dbpedia.org/resource/Albert_Einstein', 'property': 'http://schema.org/sameAs', 'src_value': 'https://en.wikipedia.org/wiki/Albert_Einstein', 'dst_value': 'https://www.wikidata.org/wiki/Q937'}, {'subject': 'http://dbpedia.org/resource/Brownian_motion', 'property': 'http://schema.org/name', 'src_value': 'Brownian', 'dst_value': 'Brownian Motion'}, {'subject': 'http://dbpedia.org/resource/Theory_of_relativity', 'property': 'http://schema.org/name', 'src_value': 'Relativity', 'dst_value': 'Theory of Relativity'}]}\n",
+ "Comparing src with dst.\n"
+ ]
+ }
+ ],
+ "source": [
+ "!provsense-cli -- compare --source src --destination dst"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "a4e76403-52a7-4871-b513-6a67e09c31a7",
+ "metadata": {},
+ "source": [
+ "## Programmatic Usage"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "id": "895d36a9-d76e-4a42-b6e6-8c0acf47266e",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from ProvSense.app import compare_items"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "id": "60021bd1-48ff-4300-9b46-594405b60041",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "2025-02-07 16:14:27,355 - INFO - Comparing strings: { \"@context\": { \"ex\": \"http://example.org/\" }, \"@id\": \"ex:Person1\", \"@type\": \"ex:Person\", \"ex:name\": \"Alice\" } with { \"@context\": { \"ex\": \"http://example.org/\" }, \"@id\": \"ex:Person1\", \"@type\": \"ex:Person\", \"ex:name\": \"BoB\" }.\n",
+ "2025-02-07 16:14:27,356 - INFO - Checking the input types, i.e, whether it is ttl, json-ld or nt\n",
+ "2025-02-07 16:14:27,362 - INFO - Checking the input types, i.e, whether it is ttl, json-ld or nt\n"
+ ]
+ },
+ {
+ "data": {
+ "text/html": [
+ "
\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " subject | \n",
+ " property | \n",
+ " src_value | \n",
+ " dst_value | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " | 0 | \n",
+ " http://example.org/Person1 | \n",
+ " http://example.org/name | \n",
+ " Alice | \n",
+ " BoB | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
"
+ ],
+ "text/plain": [
+ " subject property src_value dst_value\n",
+ "0 http://example.org/Person1 http://example.org/name Alice BoB"
+ ]
+ },
+ "execution_count": 12,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "\n",
+ "#KG string compare\n",
+ "src = \"\"\"{ \"@context\": { \"ex\": \"http://example.org/\" }, \"@id\": \"ex:Person1\", \"@type\": \"ex:Person\", \"ex:name\": \"Alice\" }\"\"\"\n",
+ "\n",
+ "dst = \"\"\"{ \"@context\": { \"ex\": \"http://example.org/\" }, \"@id\": \"ex:Person1\", \"@type\": \"ex:Person\", \"ex:name\": \"BoB\" }\"\"\"\n",
+ "\n",
+ "pd.DataFrame(compare_items(src,dst))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
+ "id": "d80247ed-7a3b-4637-a4e5-a9c975f26ad2",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "2025-02-07 16:14:27,419 - INFO - Comparing files: test_src.jsonld with test_dst.jsonld.\n",
+ "2025-02-07 16:14:27,419 - INFO - Checking the input types, i.e, whether it is ttl, json-ld or nt\n",
+ "2025-02-07 16:14:27,420 - INFO - Checking the input types, i.e, whether it is ttl, json-ld or nt\n"
+ ]
+ },
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " subject | \n",
+ " property | \n",
+ " src_value | \n",
+ " dst_value | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " | 0 | \n",
+ " urn:uuid:550e8400-e29b-41d4-a716-446655440000 | \n",
+ " http://example.org/worksAt | \n",
+ " http://example.org/Company | \n",
+ " http://example.org/CompanyY | \n",
+ "
\n",
+ " \n",
+ " | 1 | \n",
+ " urn:uuid:550e8400-e29b-41d4-a716-446655440000 | \n",
+ " http://example.org/location | \n",
+ " None | \n",
+ " New York | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
"
+ ],
+ "text/plain": [
+ " subject property \\\n",
+ "0 urn:uuid:550e8400-e29b-41d4-a716-446655440000 http://example.org/worksAt \n",
+ "1 urn:uuid:550e8400-e29b-41d4-a716-446655440000 http://example.org/location \n",
+ "\n",
+ " src_value dst_value \n",
+ "0 http://example.org/Company http://example.org/CompanyY \n",
+ "1 None New York "
+ ]
+ },
+ "execution_count": 13,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "#kg file compare\n",
+ "pd.DataFrame(compare_items(\"test_src.jsonld\",\"test_dst.jsonld\"))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 14,
+ "id": "70f0c65e-c83a-4d11-b590-f9f12f342d6f",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "2025-02-07 16:14:27,449 - INFO - Comparing directories: src with dst.\n",
+ "2025-02-07 16:14:27,451 - INFO - Processing files: src/test_file_b.jsonld with dst/test_file_b.jsonld\n",
+ "2025-02-07 16:14:27,453 - INFO - Checking the input types, i.e, whether it is ttl, json-ld or nt\n",
+ "2025-02-07 16:14:27,456 - INFO - Checking the input types, i.e, whether it is ttl, json-ld or nt\n",
+ "2025-02-07 16:14:27,496 - INFO - Processing files: src/test_file.jsonld with dst/test_file.jsonld\n",
+ "2025-02-07 16:14:27,497 - INFO - Checking the input types, i.e, whether it is ttl, json-ld or nt\n",
+ "2025-02-07 16:14:27,498 - INFO - Checking the input types, i.e, whether it is ttl, json-ld or nt\n"
+ ]
+ },
+ {
+ "data": {
+ "text/plain": [
+ "{'test_file_b.jsonld': [{'subject': 'http://dbpedia.org/resource/Albert_Einstein',\n",
+ " 'property': 'http://schema.org/alternateName',\n",
+ " 'src_value': 'Prof. Einstein',\n",
+ " 'dst_value': 'Einstein'},\n",
+ " {'subject': 'http://dbpedia.org/resource/Albert_Einstein',\n",
+ " 'property': 'http://schema.org/sameAs',\n",
+ " 'src_value': 'https://www.wikidata.org/wiki/Q937',\n",
+ " 'dst_value': 'https://en.wikipedia.org/wiki/Albert_Einstein'},\n",
+ " {'subject': 'http://dbpedia.org/resource/Albert_Einstein',\n",
+ " 'property': 'http://schema.org/alternateName',\n",
+ " 'src_value': 'Einstein',\n",
+ " 'dst_value': 'Prof. Einstein'},\n",
+ " {'subject': 'http://dbpedia.org/resource/Albert_Einstein',\n",
+ " 'property': 'http://schema.org/sameAs',\n",
+ " 'src_value': 'https://en.wikipedia.org/wiki/Albert_Einstein',\n",
+ " 'dst_value': 'https://www.wikidata.org/wiki/Q937'},\n",
+ " {'subject': 'http://dbpedia.org/resource/Albert_Einstein',\n",
+ " 'property': 'http://schema.org/nationality',\n",
+ " 'src_value': 'http://dbpedia.org/resource/United_States',\n",
+ " 'dst_value': 'http://dbpedia.org/resource/Germany'},\n",
+ " {'subject': 'http://dbpedia.org/resource/Albert_Einstein',\n",
+ " 'property': 'http://schema.org/nationality',\n",
+ " 'src_value': 'http://dbpedia.org/resource/United_States',\n",
+ " 'dst_value': 'http://dbpedia.org/resource/Switzerland'},\n",
+ " {'subject': 'http://dbpedia.org/resource/Albert_Einstein',\n",
+ " 'property': 'http://schema.org/knowsAbout',\n",
+ " 'src_value': 'http://dbpedia.org/resource/Theory_of_relativity',\n",
+ " 'dst_value': 'http://dbpedia.org/resource/Brownian_motion'},\n",
+ " {'subject': 'http://dbpedia.org/resource/Albert_Einstein',\n",
+ " 'property': 'http://schema.org/knowsAbout',\n",
+ " 'src_value': 'http://dbpedia.org/resource/Theory_of_relativity',\n",
+ " 'dst_value': 'http://dbpedia.org/resource/Quantum_mechanics'},\n",
+ " {'subject': 'http://dbpedia.org/resource/Albert_Einstein',\n",
+ " 'property': 'http://schema.org/knowsAbout',\n",
+ " 'src_value': 'http://dbpedia.org/resource/Brownian_motion',\n",
+ " 'dst_value': 'http://dbpedia.org/resource/Theory_of_relativity'},\n",
+ " {'subject': 'http://dbpedia.org/resource/Albert_Einstein',\n",
+ " 'property': 'http://schema.org/knowsAbout',\n",
+ " 'src_value': 'http://dbpedia.org/resource/Brownian_motion',\n",
+ " 'dst_value': 'http://dbpedia.org/resource/Quantum_mechanics'},\n",
+ " {'subject': 'http://dbpedia.org/resource/Albert_Einstein',\n",
+ " 'property': 'http://schema.org/affiliation',\n",
+ " 'src_value': 'http://dbpedia.org/resource/University_of_Berlin',\n",
+ " 'dst_value': 'http://dbpedia.org/resource/Princeton_University'},\n",
+ " {'subject': 'http://dbpedia.org/resource/Albert_Einstein',\n",
+ " 'property': 'http://schema.org/affiliation',\n",
+ " 'src_value': 'http://dbpedia.org/resource/University_of_Berlin',\n",
+ " 'dst_value': 'http://dbpedia.org/resource/ETH_Zurich'},\n",
+ " {'subject': 'http://dbpedia.org/resource/Albert_Einstein',\n",
+ " 'property': 'http://schema.org/nationality',\n",
+ " 'src_value': 'http://dbpedia.org/resource/Germany',\n",
+ " 'dst_value': 'http://dbpedia.org/resource/United_States'},\n",
+ " {'subject': 'http://dbpedia.org/resource/Albert_Einstein',\n",
+ " 'property': 'http://schema.org/nationality',\n",
+ " 'src_value': 'http://dbpedia.org/resource/Germany',\n",
+ " 'dst_value': 'http://dbpedia.org/resource/Switzerland'},\n",
+ " {'subject': 'http://dbpedia.org/resource/Albert_Einstein',\n",
+ " 'property': 'http://schema.org/knowsAbout',\n",
+ " 'src_value': 'http://dbpedia.org/resource/Quantum_mechanics',\n",
+ " 'dst_value': 'http://dbpedia.org/resource/Theory_of_relativity'},\n",
+ " {'subject': 'http://dbpedia.org/resource/Albert_Einstein',\n",
+ " 'property': 'http://schema.org/knowsAbout',\n",
+ " 'src_value': 'http://dbpedia.org/resource/Quantum_mechanics',\n",
+ " 'dst_value': 'http://dbpedia.org/resource/Brownian_motion'},\n",
+ " {'subject': 'http://dbpedia.org/resource/Albert_Einstein',\n",
+ " 'property': 'http://schema.org/affiliation',\n",
+ " 'src_value': 'http://dbpedia.org/resource/Princeton_University',\n",
+ " 'dst_value': 'http://dbpedia.org/resource/University_of_Berlin'},\n",
+ " {'subject': 'http://dbpedia.org/resource/Albert_Einstein',\n",
+ " 'property': 'http://schema.org/affiliation',\n",
+ " 'src_value': 'http://dbpedia.org/resource/Princeton_University',\n",
+ " 'dst_value': 'http://dbpedia.org/resource/ETH_Zurich'},\n",
+ " {'subject': 'http://dbpedia.org/resource/Albert_Einstein',\n",
+ " 'property': 'http://schema.org/affiliation',\n",
+ " 'src_value': 'http://dbpedia.org/resource/ETH_Zurich',\n",
+ " 'dst_value': 'http://dbpedia.org/resource/University_of_Berlin'},\n",
+ " {'subject': 'http://dbpedia.org/resource/Albert_Einstein',\n",
+ " 'property': 'http://schema.org/affiliation',\n",
+ " 'src_value': 'http://dbpedia.org/resource/ETH_Zurich',\n",
+ " 'dst_value': 'http://dbpedia.org/resource/Princeton_University'},\n",
+ " {'subject': 'http://dbpedia.org/resource/Albert_Einstein',\n",
+ " 'property': 'http://schema.org/nationality',\n",
+ " 'src_value': 'http://dbpedia.org/resource/Switzerland',\n",
+ " 'dst_value': 'http://dbpedia.org/resource/United_States'},\n",
+ " {'subject': 'http://dbpedia.org/resource/Albert_Einstein',\n",
+ " 'property': 'http://schema.org/nationality',\n",
+ " 'src_value': 'http://dbpedia.org/resource/Switzerland',\n",
+ " 'dst_value': 'http://dbpedia.org/resource/Germany'},\n",
+ " {'subject': 'http://dbpedia.org/resource/Brownian_motion',\n",
+ " 'property': 'http://schema.org/name',\n",
+ " 'src_value': 'Brownian',\n",
+ " 'dst_value': 'Brownian Motion'},\n",
+ " {'subject': 'http://dbpedia.org/resource/Theory_of_relativity',\n",
+ " 'property': 'http://schema.org/name',\n",
+ " 'src_value': 'Relativity',\n",
+ " 'dst_value': 'Theory of Relativity'}],\n",
+ " 'test_file.jsonld': [{'subject': 'urn:uuid:550e8400-e29b-41d4-a716-446655440000',\n",
+ " 'property': 'http://example.org/worksAt',\n",
+ " 'src_value': 'http://example.org/CompanyX',\n",
+ " 'dst_value': 'http://example.org/CompanyY'},\n",
+ " {'subject': 'urn:uuid:550e8400-e29b-41d4-a716-446655440000',\n",
+ " 'property': 'http://example.org/location',\n",
+ " 'src_value': None,\n",
+ " 'dst_value': 'New York'}]}"
+ ]
+ },
+ "execution_count": 14,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "#kg directory compare\n",
+ "compare_items(\"src\",\"dst\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "85a04bde-9e2e-4ffb-9e41-d9ac9d919442",
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "37c99416-d02e-4b21-b939-fc49ed869e77",
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "10e126f3-c42d-4583-bf90-509a9c747b51",
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3 (ipykernel)",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.11.7"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/example/dst/test_file.jsonld b/example/dst/test_file.jsonld
new file mode 100644
index 0000000..b69f83f
--- /dev/null
+++ b/example/dst/test_file.jsonld
@@ -0,0 +1,13 @@
+{
+ "@context": {
+ "ex": "http://example.org/",
+ "uuid": "urn:uuid:"
+ },
+ "@id": "uuid:550e8400-e29b-41d4-a716-446655440000",
+ "@type": "ex:Person",
+ "ex:name": "Alice",
+ "ex:location": "New York",
+ "ex:worksAt": {
+ "@id": "ex:CompanyY"
+ }
+}
diff --git a/example/dst/test_file_b.jsonld b/example/dst/test_file_b.jsonld
new file mode 100644
index 0000000..26e4edd
--- /dev/null
+++ b/example/dst/test_file_b.jsonld
@@ -0,0 +1,46 @@
+{
+ "@context": {
+ "schema": "http://schema.org/",
+ "foaf": "http://xmlns.com/foaf/0.1/",
+ "dbo": "http://dbpedia.org/ontology/",
+ "dbr": "http://dbpedia.org/resource/",
+ "ex": "http://example.org/"
+ },
+ "@id": "dbr:Albert_Einstein",
+ "@type": "schema:Person",
+ "schema:name": "Albert Einstein",
+ "schema:alternateName": ["Einstein", "Prof. Einstein"],
+ "schema:birthDate": "1879-03-14",
+ "schema:deathDate": "1955-04-18",
+ "schema:birthPlace": {
+ "@id": "dbr:Ulm",
+ "schema:name": "Ulm, Kingdom of Württemberg, German Empire"
+ },
+ "schema:nationality": [
+ { "@id": "dbr:Germany", "schema:name": "Germany" },
+ { "@id": "dbr:Switzerland", "schema:name": "Switzerland" },
+ { "@id": "dbr:United_States", "schema:name": "United States" }
+ ],
+ "schema:affiliation": [
+ { "@id": "dbr:Princeton_University", "schema:name": "Princeton University" },
+ { "@id": "dbr:ETH_Zurich", "schema:name": "ETH Zurich" },
+ { "@id": "dbr:University_of_Berlin", "schema:name": "University of Berlin" }
+ ],
+ "schema:knowsAbout": [
+ { "@id": "dbr:Theory_of_relativity", "schema:name": "Theory of Relativity" },
+ { "@id": "dbr:Quantum_mechanics", "schema:name": "Quantum Mechanics" },
+ { "@id": "dbr:Brownian_motion", "schema:name": "Brownian Motion" }
+ ],
+ "schema:award": [
+ {
+ "@id": "dbr:Nobel_Prize_in_Physics",
+ "schema:name": "Nobel Prize in Physics",
+ "schema:dateReceived": "1921"
+ }
+ ],
+ "foaf:depiction": "https://upload.wikimedia.org/wikipedia/commons/d/d3/Albert_Einstein_Head.jpg",
+ "schema:sameAs": [
+ "https://en.wikipedia.org/wiki/Albert_Einstein",
+ "https://www.wikidata.org/wiki/Q937"
+ ]
+}
diff --git a/example/src/test_file.jsonld b/example/src/test_file.jsonld
new file mode 100644
index 0000000..df3e81b
--- /dev/null
+++ b/example/src/test_file.jsonld
@@ -0,0 +1,12 @@
+{
+ "@context": {
+ "ex": "http://example.org/",
+ "uuid": "urn:uuid:"
+ },
+ "@id": "uuid:550e8400-e29b-41d4-a716-446655440000",
+ "@type": "ex:Person",
+ "ex:name": "Alice",
+ "ex:worksAt": {
+ "@id": "ex:CompanyX"
+ }
+}
diff --git a/example/src/test_file_b.jsonld b/example/src/test_file_b.jsonld
new file mode 100644
index 0000000..2d10869
--- /dev/null
+++ b/example/src/test_file_b.jsonld
@@ -0,0 +1,46 @@
+{
+ "@context": {
+ "schema": "http://schema.org/",
+ "foaf": "http://xmlns.com/foaf/0.1/",
+ "dbo": "http://dbpedia.org/ontology/",
+ "dbr": "http://dbpedia.org/resource/",
+ "ex": "http://example.org/"
+ },
+ "@id": "dbr:Albert_Einstein",
+ "@type": "schema:Person",
+ "schema:name": "Albert Einstein",
+ "schema:alternateName": ["Einstein", "Prof. Einstein"],
+ "schema:birthDate": "1879-03-14",
+ "schema:deathDate": "1955-04-18",
+ "schema:birthPlace": {
+ "@id": "dbr:Ulm",
+ "schema:name": "Ulm, Kingdom of Württemberg, German Empire"
+ },
+ "schema:nationality": [
+ { "@id": "dbr:Germany", "schema:name": "Germany" },
+ { "@id": "dbr:Switzerland", "schema:name": "Switzerland" },
+ { "@id": "dbr:United_States", "schema:name": "United States" }
+ ],
+ "schema:affiliation": [
+ { "@id": "dbr:Princeton_University", "schema:name": "Princeton University" },
+ { "@id": "dbr:ETH_Zurich", "schema:name": "ETH Zurich" },
+ { "@id": "dbr:University_of_Berlin", "schema:name": "University of Berlin" }
+ ],
+ "schema:knowsAbout": [
+ { "@id": "dbr:Theory_of_relativity", "schema:name": "Relativity" },
+ { "@id": "dbr:Quantum_mechanics", "schema:name": "Quantum Mechanics" },
+ { "@id": "dbr:Brownian_motion", "schema:name": "Brownian" }
+ ],
+ "schema:award": [
+ {
+ "@id": "dbr:Nobel_Prize_in_Physics",
+ "schema:name": "Nobel Prize in Physics",
+ "schema:dateReceived": "1921"
+ }
+ ],
+ "foaf:depiction": "https://upload.wikimedia.org/wikipedia/commons/d/d3/Albert_Einstein_Head.jpg",
+ "schema:sameAs": [
+ "https://en.wikipedia.org/wiki/Albert_Einstein",
+ "https://www.wikidata.org/wiki/Q937"
+ ]
+}
diff --git a/example/test_data.ttl b/example/test_data.ttl
new file mode 100644
index 0000000..f9e38b3
--- /dev/null
+++ b/example/test_data.ttl
@@ -0,0 +1,4 @@
+@prefix ex: .
+ ex:PersonA ex:name "Alice" .
+ ex:PersonA ex:worksAt ex:CompanyY .
+ ex:CompanyY ex:location "New York" .
\ No newline at end of file
diff --git a/example/test_dst.jsonld b/example/test_dst.jsonld
new file mode 100644
index 0000000..b69f83f
--- /dev/null
+++ b/example/test_dst.jsonld
@@ -0,0 +1,13 @@
+{
+ "@context": {
+ "ex": "http://example.org/",
+ "uuid": "urn:uuid:"
+ },
+ "@id": "uuid:550e8400-e29b-41d4-a716-446655440000",
+ "@type": "ex:Person",
+ "ex:name": "Alice",
+ "ex:location": "New York",
+ "ex:worksAt": {
+ "@id": "ex:CompanyY"
+ }
+}
diff --git a/example/test_src.jsonld b/example/test_src.jsonld
new file mode 100644
index 0000000..6531af5
--- /dev/null
+++ b/example/test_src.jsonld
@@ -0,0 +1,12 @@
+{
+ "@context": {
+ "ex": "http://example.org/",
+ "uuid": "urn:uuid:"
+ },
+ "@id": "uuid:550e8400-e29b-41d4-a716-446655440000",
+ "@type": "ex:Person",
+ "ex:name": "Alice",
+ "ex:worksAt": {
+ "@id": "ex:Company"
+ }
+}
diff --git a/poetry.lock b/poetry.lock
new file mode 100644
index 0000000..704f376
--- /dev/null
+++ b/poetry.lock
@@ -0,0 +1,707 @@
+# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
+
+[[package]]
+name = "cfgv"
+version = "3.4.0"
+description = "Validate configuration and produce human readable error messages."
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"},
+ {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"},
+]
+
+[[package]]
+name = "click"
+version = "8.1.7"
+description = "Composable command line interface toolkit"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"},
+ {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"},
+]
+
+[package.dependencies]
+colorama = {version = "*", markers = "platform_system == \"Windows\""}
+
+[[package]]
+name = "colorama"
+version = "0.4.6"
+description = "Cross-platform colored terminal text."
+optional = false
+python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
+files = [
+ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
+ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
+]
+
+[[package]]
+name = "coverage"
+version = "7.6.9"
+description = "Code coverage measurement for Python"
+optional = false
+python-versions = ">=3.9"
+files = [
+ {file = "coverage-7.6.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:85d9636f72e8991a1706b2b55b06c27545448baf9f6dbf51c4004609aacd7dcb"},
+ {file = "coverage-7.6.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:608a7fd78c67bee8936378299a6cb9f5149bb80238c7a566fc3e6717a4e68710"},
+ {file = "coverage-7.6.9-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:96d636c77af18b5cb664ddf12dab9b15a0cfe9c0bde715da38698c8cea748bfa"},
+ {file = "coverage-7.6.9-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d75cded8a3cff93da9edc31446872d2997e327921d8eed86641efafd350e1df1"},
+ {file = "coverage-7.6.9-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f7b15f589593110ae767ce997775d645b47e5cbbf54fd322f8ebea6277466cec"},
+ {file = "coverage-7.6.9-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:44349150f6811b44b25574839b39ae35291f6496eb795b7366fef3bd3cf112d3"},
+ {file = "coverage-7.6.9-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:d891c136b5b310d0e702e186d70cd16d1119ea8927347045124cb286b29297e5"},
+ {file = "coverage-7.6.9-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:db1dab894cc139f67822a92910466531de5ea6034ddfd2b11c0d4c6257168073"},
+ {file = "coverage-7.6.9-cp310-cp310-win32.whl", hash = "sha256:41ff7b0da5af71a51b53f501a3bac65fb0ec311ebed1632e58fc6107f03b9198"},
+ {file = "coverage-7.6.9-cp310-cp310-win_amd64.whl", hash = "sha256:35371f8438028fdccfaf3570b31d98e8d9eda8bb1d6ab9473f5a390969e98717"},
+ {file = "coverage-7.6.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:932fc826442132dde42ee52cf66d941f581c685a6313feebed358411238f60f9"},
+ {file = "coverage-7.6.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:085161be5f3b30fd9b3e7b9a8c301f935c8313dcf928a07b116324abea2c1c2c"},
+ {file = "coverage-7.6.9-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ccc660a77e1c2bf24ddbce969af9447a9474790160cfb23de6be4fa88e3951c7"},
+ {file = "coverage-7.6.9-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c69e42c892c018cd3c8d90da61d845f50a8243062b19d228189b0224150018a9"},
+ {file = "coverage-7.6.9-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0824a28ec542a0be22f60c6ac36d679e0e262e5353203bea81d44ee81fe9c6d4"},
+ {file = "coverage-7.6.9-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4401ae5fc52ad8d26d2a5d8a7428b0f0c72431683f8e63e42e70606374c311a1"},
+ {file = "coverage-7.6.9-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:98caba4476a6c8d59ec1eb00c7dd862ba9beca34085642d46ed503cc2d440d4b"},
+ {file = "coverage-7.6.9-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ee5defd1733fd6ec08b168bd4f5387d5b322f45ca9e0e6c817ea6c4cd36313e3"},
+ {file = "coverage-7.6.9-cp311-cp311-win32.whl", hash = "sha256:f2d1ec60d6d256bdf298cb86b78dd715980828f50c46701abc3b0a2b3f8a0dc0"},
+ {file = "coverage-7.6.9-cp311-cp311-win_amd64.whl", hash = "sha256:0d59fd927b1f04de57a2ba0137166d31c1a6dd9e764ad4af552912d70428c92b"},
+ {file = "coverage-7.6.9-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:99e266ae0b5d15f1ca8d278a668df6f51cc4b854513daab5cae695ed7b721cf8"},
+ {file = "coverage-7.6.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9901d36492009a0a9b94b20e52ebfc8453bf49bb2b27bca2c9706f8b4f5a554a"},
+ {file = "coverage-7.6.9-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abd3e72dd5b97e3af4246cdada7738ef0e608168de952b837b8dd7e90341f015"},
+ {file = "coverage-7.6.9-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff74026a461eb0660366fb01c650c1d00f833a086b336bdad7ab00cc952072b3"},
+ {file = "coverage-7.6.9-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65dad5a248823a4996724a88eb51d4b31587aa7aa428562dbe459c684e5787ae"},
+ {file = "coverage-7.6.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:22be16571504c9ccea919fcedb459d5ab20d41172056206eb2994e2ff06118a4"},
+ {file = "coverage-7.6.9-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f957943bc718b87144ecaee70762bc2bc3f1a7a53c7b861103546d3a403f0a6"},
+ {file = "coverage-7.6.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0ae1387db4aecb1f485fb70a6c0148c6cdaebb6038f1d40089b1fc84a5db556f"},
+ {file = "coverage-7.6.9-cp312-cp312-win32.whl", hash = "sha256:1a330812d9cc7ac2182586f6d41b4d0fadf9be9049f350e0efb275c8ee8eb692"},
+ {file = "coverage-7.6.9-cp312-cp312-win_amd64.whl", hash = "sha256:b12c6b18269ca471eedd41c1b6a1065b2f7827508edb9a7ed5555e9a56dcfc97"},
+ {file = "coverage-7.6.9-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:899b8cd4781c400454f2f64f7776a5d87bbd7b3e7f7bda0cb18f857bb1334664"},
+ {file = "coverage-7.6.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:61f70dc68bd36810972e55bbbe83674ea073dd1dcc121040a08cdf3416c5349c"},
+ {file = "coverage-7.6.9-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a289d23d4c46f1a82d5db4abeb40b9b5be91731ee19a379d15790e53031c014"},
+ {file = "coverage-7.6.9-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e216d8044a356fc0337c7a2a0536d6de07888d7bcda76febcb8adc50bdbbd00"},
+ {file = "coverage-7.6.9-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c026eb44f744acaa2bda7493dad903aa5bf5fc4f2554293a798d5606710055d"},
+ {file = "coverage-7.6.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e77363e8425325384f9d49272c54045bbed2f478e9dd698dbc65dbc37860eb0a"},
+ {file = "coverage-7.6.9-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:777abfab476cf83b5177b84d7486497e034eb9eaea0d746ce0c1268c71652077"},
+ {file = "coverage-7.6.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:447af20e25fdbe16f26e84eb714ba21d98868705cb138252d28bc400381f6ffb"},
+ {file = "coverage-7.6.9-cp313-cp313-win32.whl", hash = "sha256:d872ec5aeb086cbea771c573600d47944eea2dcba8be5f3ee649bfe3cb8dc9ba"},
+ {file = "coverage-7.6.9-cp313-cp313-win_amd64.whl", hash = "sha256:fd1213c86e48dfdc5a0cc676551db467495a95a662d2396ecd58e719191446e1"},
+ {file = "coverage-7.6.9-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:ba9e7484d286cd5a43744e5f47b0b3fb457865baf07bafc6bee91896364e1419"},
+ {file = "coverage-7.6.9-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e5ea1cf0872ee455c03e5674b5bca5e3e68e159379c1af0903e89f5eba9ccc3a"},
+ {file = "coverage-7.6.9-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d10e07aa2b91835d6abec555ec8b2733347956991901eea6ffac295f83a30e4"},
+ {file = "coverage-7.6.9-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:13a9e2d3ee855db3dd6ea1ba5203316a1b1fd8eaeffc37c5b54987e61e4194ae"},
+ {file = "coverage-7.6.9-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c38bf15a40ccf5619fa2fe8f26106c7e8e080d7760aeccb3722664c8656b030"},
+ {file = "coverage-7.6.9-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:d5275455b3e4627c8e7154feaf7ee0743c2e7af82f6e3b561967b1cca755a0be"},
+ {file = "coverage-7.6.9-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:8f8770dfc6e2c6a2d4569f411015c8d751c980d17a14b0530da2d7f27ffdd88e"},
+ {file = "coverage-7.6.9-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8d2dfa71665a29b153a9681edb1c8d9c1ea50dfc2375fb4dac99ea7e21a0bcd9"},
+ {file = "coverage-7.6.9-cp313-cp313t-win32.whl", hash = "sha256:5e6b86b5847a016d0fbd31ffe1001b63355ed309651851295315031ea7eb5a9b"},
+ {file = "coverage-7.6.9-cp313-cp313t-win_amd64.whl", hash = "sha256:97ddc94d46088304772d21b060041c97fc16bdda13c6c7f9d8fcd8d5ae0d8611"},
+ {file = "coverage-7.6.9-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:adb697c0bd35100dc690de83154627fbab1f4f3c0386df266dded865fc50a902"},
+ {file = "coverage-7.6.9-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:be57b6d56e49c2739cdf776839a92330e933dd5e5d929966fbbd380c77f060be"},
+ {file = "coverage-7.6.9-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1592791f8204ae9166de22ba7e6705fa4ebd02936c09436a1bb85aabca3e599"},
+ {file = "coverage-7.6.9-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4e12ae8cc979cf83d258acb5e1f1cf2f3f83524d1564a49d20b8bec14b637f08"},
+ {file = "coverage-7.6.9-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb5555cff66c4d3d6213a296b360f9e1a8e323e74e0426b6c10ed7f4d021e464"},
+ {file = "coverage-7.6.9-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:b9389a429e0e5142e69d5bf4a435dd688c14478a19bb901735cdf75e57b13845"},
+ {file = "coverage-7.6.9-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:592ac539812e9b46046620341498caf09ca21023c41c893e1eb9dbda00a70cbf"},
+ {file = "coverage-7.6.9-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a27801adef24cc30871da98a105f77995e13a25a505a0161911f6aafbd66e678"},
+ {file = "coverage-7.6.9-cp39-cp39-win32.whl", hash = "sha256:8e3c3e38930cfb729cb8137d7f055e5a473ddaf1217966aa6238c88bd9fd50e6"},
+ {file = "coverage-7.6.9-cp39-cp39-win_amd64.whl", hash = "sha256:e28bf44afa2b187cc9f41749138a64435bf340adfcacb5b2290c070ce99839d4"},
+ {file = "coverage-7.6.9-pp39.pp310-none-any.whl", hash = "sha256:f3ca78518bc6bc92828cd11867b121891d75cae4ea9e908d72030609b996db1b"},
+ {file = "coverage-7.6.9.tar.gz", hash = "sha256:4a8d8977b0c6ef5aeadcb644da9e69ae0dcfe66ec7f368c89c72e058bd71164d"},
+]
+
+[package.dependencies]
+tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""}
+
+[package.extras]
+toml = ["tomli"]
+
+[[package]]
+name = "distlib"
+version = "0.3.9"
+description = "Distribution utilities"
+optional = false
+python-versions = "*"
+files = [
+ {file = "distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87"},
+ {file = "distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403"},
+]
+
+[[package]]
+name = "exceptiongroup"
+version = "1.2.2"
+description = "Backport of PEP 654 (exception groups)"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"},
+ {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"},
+]
+
+[package.extras]
+test = ["pytest (>=6)"]
+
+[[package]]
+name = "filelock"
+version = "3.16.1"
+description = "A platform independent file lock."
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0"},
+ {file = "filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435"},
+]
+
+[package.extras]
+docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4.1)"]
+testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "diff-cover (>=9.2)", "pytest (>=8.3.3)", "pytest-asyncio (>=0.24)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.26.4)"]
+typing = ["typing-extensions (>=4.12.2)"]
+
+[[package]]
+name = "identify"
+version = "2.6.3"
+description = "File identification library for Python"
+optional = false
+python-versions = ">=3.9"
+files = [
+ {file = "identify-2.6.3-py2.py3-none-any.whl", hash = "sha256:9edba65473324c2ea9684b1f944fe3191db3345e50b6d04571d10ed164f8d7bd"},
+ {file = "identify-2.6.3.tar.gz", hash = "sha256:62f5dae9b5fef52c84cc188514e9ea4f3f636b1d8799ab5ebc475471f9e47a02"},
+]
+
+[package.extras]
+license = ["ukkonen"]
+
+[[package]]
+name = "iniconfig"
+version = "2.0.0"
+description = "brain-dead simple config-ini parsing"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"},
+ {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"},
+]
+
+[[package]]
+name = "isodate"
+version = "0.7.2"
+description = "An ISO 8601 date/time/duration parser and formatter"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "isodate-0.7.2-py3-none-any.whl", hash = "sha256:28009937d8031054830160fce6d409ed342816b543597cece116d966c6d99e15"},
+ {file = "isodate-0.7.2.tar.gz", hash = "sha256:4cd1aa0f43ca76f4a6c6c0292a85f40b35ec2e43e315b59f06e6d32171a953e6"},
+]
+
+[[package]]
+name = "jinja2"
+version = "3.1.4"
+description = "A very fast and expressive template engine."
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"},
+ {file = "jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"},
+]
+
+[package.dependencies]
+MarkupSafe = ">=2.0"
+
+[package.extras]
+i18n = ["Babel (>=2.7)"]
+
+[[package]]
+name = "markupsafe"
+version = "3.0.2"
+description = "Safely add untrusted strings to HTML/XML markup."
+optional = false
+python-versions = ">=3.9"
+files = [
+ {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"},
+ {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"},
+ {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579"},
+ {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d"},
+ {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb"},
+ {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b"},
+ {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c"},
+ {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171"},
+ {file = "MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50"},
+ {file = "MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a"},
+ {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d"},
+ {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93"},
+ {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832"},
+ {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84"},
+ {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca"},
+ {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798"},
+ {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e"},
+ {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4"},
+ {file = "MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d"},
+ {file = "MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b"},
+ {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf"},
+ {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225"},
+ {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028"},
+ {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8"},
+ {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c"},
+ {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557"},
+ {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22"},
+ {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48"},
+ {file = "MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30"},
+ {file = "MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87"},
+ {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd"},
+ {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430"},
+ {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094"},
+ {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396"},
+ {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79"},
+ {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a"},
+ {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca"},
+ {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c"},
+ {file = "MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1"},
+ {file = "MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f"},
+ {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c"},
+ {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb"},
+ {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c"},
+ {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d"},
+ {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe"},
+ {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5"},
+ {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a"},
+ {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9"},
+ {file = "MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6"},
+ {file = "MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f"},
+ {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a"},
+ {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff"},
+ {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13"},
+ {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144"},
+ {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29"},
+ {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0"},
+ {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0"},
+ {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178"},
+ {file = "MarkupSafe-3.0.2-cp39-cp39-win32.whl", hash = "sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f"},
+ {file = "MarkupSafe-3.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a"},
+ {file = "markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0"},
+]
+
+[[package]]
+name = "mypy"
+version = "1.13.0"
+description = "Optional static typing for Python"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "mypy-1.13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6607e0f1dd1fb7f0aca14d936d13fd19eba5e17e1cd2a14f808fa5f8f6d8f60a"},
+ {file = "mypy-1.13.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8a21be69bd26fa81b1f80a61ee7ab05b076c674d9b18fb56239d72e21d9f4c80"},
+ {file = "mypy-1.13.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7b2353a44d2179846a096e25691d54d59904559f4232519d420d64da6828a3a7"},
+ {file = "mypy-1.13.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0730d1c6a2739d4511dc4253f8274cdd140c55c32dfb0a4cf8b7a43f40abfa6f"},
+ {file = "mypy-1.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:c5fc54dbb712ff5e5a0fca797e6e0aa25726c7e72c6a5850cfd2adbc1eb0a372"},
+ {file = "mypy-1.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:581665e6f3a8a9078f28d5502f4c334c0c8d802ef55ea0e7276a6e409bc0d82d"},
+ {file = "mypy-1.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3ddb5b9bf82e05cc9a627e84707b528e5c7caaa1c55c69e175abb15a761cec2d"},
+ {file = "mypy-1.13.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:20c7ee0bc0d5a9595c46f38beb04201f2620065a93755704e141fcac9f59db2b"},
+ {file = "mypy-1.13.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3790ded76f0b34bc9c8ba4def8f919dd6a46db0f5a6610fb994fe8efdd447f73"},
+ {file = "mypy-1.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:51f869f4b6b538229c1d1bcc1dd7d119817206e2bc54e8e374b3dfa202defcca"},
+ {file = "mypy-1.13.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5c7051a3461ae84dfb5dd15eff5094640c61c5f22257c8b766794e6dd85e72d5"},
+ {file = "mypy-1.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:39bb21c69a5d6342f4ce526e4584bc5c197fd20a60d14a8624d8743fffb9472e"},
+ {file = "mypy-1.13.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:164f28cb9d6367439031f4c81e84d3ccaa1e19232d9d05d37cb0bd880d3f93c2"},
+ {file = "mypy-1.13.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a4c1bfcdbce96ff5d96fc9b08e3831acb30dc44ab02671eca5953eadad07d6d0"},
+ {file = "mypy-1.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:a0affb3a79a256b4183ba09811e3577c5163ed06685e4d4b46429a271ba174d2"},
+ {file = "mypy-1.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a7b44178c9760ce1a43f544e595d35ed61ac2c3de306599fa59b38a6048e1aa7"},
+ {file = "mypy-1.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5d5092efb8516d08440e36626f0153b5006d4088c1d663d88bf79625af3d1d62"},
+ {file = "mypy-1.13.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de2904956dac40ced10931ac967ae63c5089bd498542194b436eb097a9f77bc8"},
+ {file = "mypy-1.13.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:7bfd8836970d33c2105562650656b6846149374dc8ed77d98424b40b09340ba7"},
+ {file = "mypy-1.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:9f73dba9ec77acb86457a8fc04b5239822df0c14a082564737833d2963677dbc"},
+ {file = "mypy-1.13.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:100fac22ce82925f676a734af0db922ecfea991e1d7ec0ceb1e115ebe501301a"},
+ {file = "mypy-1.13.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7bcb0bb7f42a978bb323a7c88f1081d1b5dee77ca86f4100735a6f541299d8fb"},
+ {file = "mypy-1.13.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bde31fc887c213e223bbfc34328070996061b0833b0a4cfec53745ed61f3519b"},
+ {file = "mypy-1.13.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:07de989f89786f62b937851295ed62e51774722e5444a27cecca993fc3f9cd74"},
+ {file = "mypy-1.13.0-cp38-cp38-win_amd64.whl", hash = "sha256:4bde84334fbe19bad704b3f5b78c4abd35ff1026f8ba72b29de70dda0916beb6"},
+ {file = "mypy-1.13.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0246bcb1b5de7f08f2826451abd947bf656945209b140d16ed317f65a17dc7dc"},
+ {file = "mypy-1.13.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7f5b7deae912cf8b77e990b9280f170381fdfbddf61b4ef80927edd813163732"},
+ {file = "mypy-1.13.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7029881ec6ffb8bc233a4fa364736789582c738217b133f1b55967115288a2bc"},
+ {file = "mypy-1.13.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3e38b980e5681f28f033f3be86b099a247b13c491f14bb8b1e1e134d23bb599d"},
+ {file = "mypy-1.13.0-cp39-cp39-win_amd64.whl", hash = "sha256:a6789be98a2017c912ae6ccb77ea553bbaf13d27605d2ca20a76dfbced631b24"},
+ {file = "mypy-1.13.0-py3-none-any.whl", hash = "sha256:9c250883f9fd81d212e0952c92dbfcc96fc237f4b7c92f56ac81fd48460b3e5a"},
+ {file = "mypy-1.13.0.tar.gz", hash = "sha256:0291a61b6fbf3e6673e3405cfcc0e7650bebc7939659fdca2702958038bd835e"},
+]
+
+[package.dependencies]
+mypy-extensions = ">=1.0.0"
+tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
+typing-extensions = ">=4.6.0"
+
+[package.extras]
+dmypy = ["psutil (>=4.0)"]
+faster-cache = ["orjson"]
+install-types = ["pip"]
+mypyc = ["setuptools (>=50)"]
+reports = ["lxml"]
+
+[[package]]
+name = "mypy-extensions"
+version = "1.0.0"
+description = "Type system extensions for programs checked with the mypy type checker."
+optional = false
+python-versions = ">=3.5"
+files = [
+ {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"},
+ {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"},
+]
+
+[[package]]
+name = "nodeenv"
+version = "1.9.1"
+description = "Node.js virtual environment builder"
+optional = false
+python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
+files = [
+ {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"},
+ {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"},
+]
+
+[[package]]
+name = "packaging"
+version = "24.2"
+description = "Core utilities for Python packages"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"},
+ {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"},
+]
+
+[[package]]
+name = "pdoc"
+version = "14.7.0"
+description = "API Documentation for Python Projects"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "pdoc-14.7.0-py3-none-any.whl", hash = "sha256:72377a907efc6b2c5b3c56b717ef34f11d93621dced3b663f3aede0b844c0ad2"},
+ {file = "pdoc-14.7.0.tar.gz", hash = "sha256:2d28af9c0acc39180744ad0543e4bbc3223ecba0d1302db315ec521c51f71f93"},
+]
+
+[package.dependencies]
+Jinja2 = ">=2.11.0"
+MarkupSafe = "*"
+pygments = ">=2.12.0"
+
+[package.extras]
+dev = ["hypothesis", "mypy", "pdoc-pyo3-sample-library (==1.0.11)", "pygments (>=2.14.0)", "pytest", "pytest-cov", "pytest-timeout", "ruff", "tox", "types-pygments"]
+
+[[package]]
+name = "platformdirs"
+version = "4.3.6"
+description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`."
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"},
+ {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"},
+]
+
+[package.extras]
+docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"]
+test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)"]
+type = ["mypy (>=1.11.2)"]
+
+[[package]]
+name = "pluggy"
+version = "1.5.0"
+description = "plugin and hook calling mechanisms for python"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"},
+ {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"},
+]
+
+[package.extras]
+dev = ["pre-commit", "tox"]
+testing = ["pytest", "pytest-benchmark"]
+
+[[package]]
+name = "pre-commit"
+version = "3.8.0"
+description = "A framework for managing and maintaining multi-language pre-commit hooks."
+optional = false
+python-versions = ">=3.9"
+files = [
+ {file = "pre_commit-3.8.0-py2.py3-none-any.whl", hash = "sha256:9a90a53bf82fdd8778d58085faf8d83df56e40dfe18f45b19446e26bf1b3a63f"},
+ {file = "pre_commit-3.8.0.tar.gz", hash = "sha256:8bb6494d4a20423842e198980c9ecf9f96607a07ea29549e180eef9ae80fe7af"},
+]
+
+[package.dependencies]
+cfgv = ">=2.0.0"
+identify = ">=1.0.0"
+nodeenv = ">=0.11.1"
+pyyaml = ">=5.1"
+virtualenv = ">=20.10.0"
+
+[[package]]
+name = "pygments"
+version = "2.18.0"
+description = "Pygments is a syntax highlighting package written in Python."
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"},
+ {file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"},
+]
+
+[package.extras]
+windows-terminal = ["colorama (>=0.4.6)"]
+
+[[package]]
+name = "pyparsing"
+version = "3.2.1"
+description = "pyparsing module - Classes and methods to define and execute parsing grammars"
+optional = false
+python-versions = ">=3.9"
+files = [
+ {file = "pyparsing-3.2.1-py3-none-any.whl", hash = "sha256:506ff4f4386c4cec0590ec19e6302d3aedb992fdc02c761e90416f158dacf8e1"},
+ {file = "pyparsing-3.2.1.tar.gz", hash = "sha256:61980854fd66de3a90028d679a954d5f2623e83144b5afe5ee86f43d762e5f0a"},
+]
+
+[package.extras]
+diagrams = ["jinja2", "railroad-diagrams"]
+
+[[package]]
+name = "pytest"
+version = "8.3.4"
+description = "pytest: simple powerful testing with Python"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6"},
+ {file = "pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761"},
+]
+
+[package.dependencies]
+colorama = {version = "*", markers = "sys_platform == \"win32\""}
+exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""}
+iniconfig = "*"
+packaging = "*"
+pluggy = ">=1.5,<2"
+tomli = {version = ">=1", markers = "python_version < \"3.11\""}
+
+[package.extras]
+dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"]
+
+[[package]]
+name = "pytest-cov"
+version = "5.0.0"
+description = "Pytest plugin for measuring coverage."
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "pytest-cov-5.0.0.tar.gz", hash = "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857"},
+ {file = "pytest_cov-5.0.0-py3-none-any.whl", hash = "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652"},
+]
+
+[package.dependencies]
+coverage = {version = ">=5.2.1", extras = ["toml"]}
+pytest = ">=4.6"
+
+[package.extras]
+testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"]
+
+[[package]]
+name = "pyyaml"
+version = "6.0.2"
+description = "YAML parser and emitter for Python"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"},
+ {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"},
+ {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"},
+ {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"},
+ {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"},
+ {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"},
+ {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"},
+ {file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"},
+ {file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"},
+ {file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"},
+ {file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"},
+ {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"},
+ {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"},
+ {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"},
+ {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"},
+ {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"},
+ {file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"},
+ {file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"},
+ {file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"},
+ {file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"},
+ {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"},
+ {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"},
+ {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"},
+ {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"},
+ {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"},
+ {file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"},
+ {file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"},
+ {file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"},
+ {file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"},
+ {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"},
+ {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"},
+ {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"},
+ {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"},
+ {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"},
+ {file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"},
+ {file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"},
+ {file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"},
+ {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"},
+ {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"},
+ {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"},
+ {file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"},
+ {file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"},
+ {file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"},
+ {file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"},
+ {file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"},
+ {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"},
+ {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"},
+ {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"},
+ {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"},
+ {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"},
+ {file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"},
+ {file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"},
+ {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"},
+]
+
+[[package]]
+name = "rdflib"
+version = "7.1.3"
+description = "RDFLib is a Python library for working with RDF, a simple yet powerful language for representing information."
+optional = false
+python-versions = "<4.0.0,>=3.8.1"
+files = [
+ {file = "rdflib-7.1.3-py3-none-any.whl", hash = "sha256:5402310a9f0f3c07d453d73fd0ad6ba35616286fe95d3670db2b725f3f539673"},
+ {file = "rdflib-7.1.3.tar.gz", hash = "sha256:f3dcb4c106a8cd9e060d92f43d593d09ebc3d07adc244f4c7315856a12e383ee"},
+]
+
+[package.dependencies]
+isodate = {version = ">=0.7.2,<1.0.0", markers = "python_version < \"3.11\""}
+pyparsing = ">=2.1.0,<4"
+
+[package.extras]
+berkeleydb = ["berkeleydb (>=18.1.0,<19.0.0)"]
+html = ["html5rdf (>=1.2,<2)"]
+lxml = ["lxml (>=4.3,<6.0)"]
+networkx = ["networkx (>=2,<4)"]
+orjson = ["orjson (>=3.9.14,<4)"]
+
+[[package]]
+name = "ruff"
+version = "0.3.7"
+description = "An extremely fast Python linter and code formatter, written in Rust."
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "ruff-0.3.7-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:0e8377cccb2f07abd25e84fc5b2cbe48eeb0fea9f1719cad7caedb061d70e5ce"},
+ {file = "ruff-0.3.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:15a4d1cc1e64e556fa0d67bfd388fed416b7f3b26d5d1c3e7d192c897e39ba4b"},
+ {file = "ruff-0.3.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d28bdf3d7dc71dd46929fafeec98ba89b7c3550c3f0978e36389b5631b793663"},
+ {file = "ruff-0.3.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:379b67d4f49774ba679593b232dcd90d9e10f04d96e3c8ce4a28037ae473f7bb"},
+ {file = "ruff-0.3.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c060aea8ad5ef21cdfbbe05475ab5104ce7827b639a78dd55383a6e9895b7c51"},
+ {file = "ruff-0.3.7-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:ebf8f615dde968272d70502c083ebf963b6781aacd3079081e03b32adfe4d58a"},
+ {file = "ruff-0.3.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d48098bd8f5c38897b03604f5428901b65e3c97d40b3952e38637b5404b739a2"},
+ {file = "ruff-0.3.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:da8a4fda219bf9024692b1bc68c9cff4b80507879ada8769dc7e985755d662ea"},
+ {file = "ruff-0.3.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c44e0149f1d8b48c4d5c33d88c677a4aa22fd09b1683d6a7ff55b816b5d074f"},
+ {file = "ruff-0.3.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:3050ec0af72b709a62ecc2aca941b9cd479a7bf2b36cc4562f0033d688e44fa1"},
+ {file = "ruff-0.3.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:a29cc38e4c1ab00da18a3f6777f8b50099d73326981bb7d182e54a9a21bb4ff7"},
+ {file = "ruff-0.3.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:5b15cc59c19edca917f51b1956637db47e200b0fc5e6e1878233d3a938384b0b"},
+ {file = "ruff-0.3.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:e491045781b1e38b72c91247cf4634f040f8d0cb3e6d3d64d38dcf43616650b4"},
+ {file = "ruff-0.3.7-py3-none-win32.whl", hash = "sha256:bc931de87593d64fad3a22e201e55ad76271f1d5bfc44e1a1887edd0903c7d9f"},
+ {file = "ruff-0.3.7-py3-none-win_amd64.whl", hash = "sha256:5ef0e501e1e39f35e03c2acb1d1238c595b8bb36cf7a170e7c1df1b73da00e74"},
+ {file = "ruff-0.3.7-py3-none-win_arm64.whl", hash = "sha256:789e144f6dc7019d1f92a812891c645274ed08af6037d11fc65fcbc183b7d59f"},
+ {file = "ruff-0.3.7.tar.gz", hash = "sha256:d5c1aebee5162c2226784800ae031f660c350e7a3402c4d1f8ea4e97e232e3ba"},
+]
+
+[[package]]
+name = "sparqlwrapper"
+version = "2.0.0"
+description = "SPARQL Endpoint interface to Python"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "SPARQLWrapper-2.0.0-py3-none-any.whl", hash = "sha256:c99a7204fff676ee28e6acef327dc1ff8451c6f7217dcd8d49e8872f324a8a20"},
+ {file = "SPARQLWrapper-2.0.0.tar.gz", hash = "sha256:3fed3ebcc77617a4a74d2644b86fd88e0f32e7f7003ac7b2b334c026201731f1"},
+]
+
+[package.dependencies]
+rdflib = ">=6.1.1"
+
+[package.extras]
+dev = ["mypy (>=0.931)", "pandas (>=1.3.5)", "pandas-stubs (>=1.2.0.48)", "setuptools (>=3.7.1)"]
+docs = ["sphinx (<5)", "sphinx-rtd-theme"]
+keepalive = ["keepalive (>=0.5)"]
+pandas = ["pandas (>=1.3.5)"]
+
+[[package]]
+name = "tomli"
+version = "2.2.1"
+description = "A lil' TOML parser"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"},
+ {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"},
+ {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a"},
+ {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee"},
+ {file = "tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e"},
+ {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4"},
+ {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106"},
+ {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8"},
+ {file = "tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff"},
+ {file = "tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b"},
+ {file = "tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea"},
+ {file = "tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8"},
+ {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192"},
+ {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222"},
+ {file = "tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77"},
+ {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6"},
+ {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd"},
+ {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e"},
+ {file = "tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98"},
+ {file = "tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4"},
+ {file = "tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7"},
+ {file = "tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c"},
+ {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13"},
+ {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281"},
+ {file = "tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272"},
+ {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140"},
+ {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2"},
+ {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744"},
+ {file = "tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec"},
+ {file = "tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69"},
+ {file = "tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"},
+ {file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"},
+]
+
+[[package]]
+name = "typing-extensions"
+version = "4.12.2"
+description = "Backported and Experimental Type Hints for Python 3.8+"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"},
+ {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"},
+]
+
+[[package]]
+name = "virtualenv"
+version = "20.28.0"
+description = "Virtual Python Environment builder"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "virtualenv-20.28.0-py3-none-any.whl", hash = "sha256:23eae1b4516ecd610481eda647f3a7c09aea295055337331bb4e6892ecce47b0"},
+ {file = "virtualenv-20.28.0.tar.gz", hash = "sha256:2c9c3262bb8e7b87ea801d715fae4495e6032450c71d2309be9550e7364049aa"},
+]
+
+[package.dependencies]
+distlib = ">=0.3.7,<1"
+filelock = ">=3.12.2,<4"
+platformdirs = ">=3.9.1,<5"
+
+[package.extras]
+docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"]
+test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"]
+
+[metadata]
+lock-version = "2.0"
+python-versions = "^3.10"
+content-hash = "eb25016648182fb737303603c73abc08868e3771a85298c87e369fbbf5404291"
diff --git a/pyproject.toml b/pyproject.toml
index 5142261..9056fe7 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "ProvSense"
-version = "0.0.1"
+version = "1.0.1"
description = "ProvSense is a Python library for managing knowledge graph provenance. It enables efficient comparison of files, tracking changes, and enforcing provenance rules to ensure data integrity and traceability. Ideal for researchers and developers, ProvSense promotes transparency and accountability in data-driven workflows."
authors = [
"tekrajchhetri ",
@@ -15,7 +15,7 @@ repository = "https://github.com/sensein/ProvSense/"
documentation = "https://sensein.group/brainkbdocs/"
keywords = ["python", "data-integrity", "file-comparison", "data-governance", "provenance", "knowledge-graph", "provenance-tracking", "reproducibility", "lineage"]
classifiers = [
- "Development Status :: 3 - Alpha",
+ "Development Status :: 5 - Production/Stable",
"Programming Language :: Python :: 3.10",
# "License :: OSI Approved :: Apache Software License",
# "Operating System :: OS Independent"
@@ -25,6 +25,8 @@ packages = [{include = "ProvSense", from = "src"}]
[tool.poetry.dependencies]
python = "^3.10"
click = "^8.1.7"
+sparqlwrapper = "^2.0.0"
+rdflib = "^7.1.3"
[tool.poetry.group.dev]
optional = true
@@ -97,7 +99,7 @@ convention = "google"
"src/tests/**/*.py" = []
[tool.poetry.scripts]
-ProvSense-cli = "ProvSense.cli:main"
+provsense-cli = "ProvSense.cli:cli"
[tool.ruff.format]
quote-style = "double"
diff --git a/src/ProvSense/app.py b/src/ProvSense/app.py
index b457dc5..71ad3d8 100644
--- a/src/ProvSense/app.py
+++ b/src/ProvSense/app.py
@@ -1,11 +1,105 @@
-"""This script defines a simple function that prints 'Hello World!' to the console.
+"""This script defines functions for comparing source with destination.
-It then calls this function if the script is run as the main program.
+It provides functionality for folder-to-folder, file-to-file, and string-to-string comparisons.
"""
+from pathlib import Path
+from typing import Union
+import logging
+from .shared import compare_graph, convert_to_nt, convert_single_file_to_nt
+
+logger = logging.getLogger(__name__)
+
+def compare_items(source: Union[str, Path], destination: Union[str, Path] ) -> dict:
+ """
+ Compares the content of `source` and `destination`, which can be file paths
+ (to files or directories) or strings representing knowledge graphs (in JSON-LD or TTL format).
+
+ Args:
+
+ - source (Union[str, Path]): The source item, which can be either:
+
+ - A file or directory path (`Path`).
+ - A string representation of a knowledge graph (JSON-LD/TTL).
+
+ - destination (Union[str, Path]): The destination item, which can be either:
+
+ - A file or directory path (`Path`).
+ - A string representation of a knowledge graph (JSON-LD/TTL).
+
+ Returns:
+
+ - dict: A dictionary containing the comparison results.
+
+ Notes:
+
+ - If `source` and `destination` are paths, the function will compare their contents.
+
+ - If they are strings, it assumes they are knowledge graphs and processes them accordingly.
+
+ - The function should be extended to include logic for structural or semantic comparisons.
+
+ Example:
+ >>> source = '{ "@context": { "ex": "http://example.org/" }, "@id": "ex:Person1", "@type": "ex:Person", "ex:name": "Alice" }'
+
+ >>> destination = '{ "@context": { "ex": "http://example.org/" }, "@id": "ex:Person2", "@type": "ex:Person", "ex:name": "Bob" }'
+
+ >>> compare_items(source, destination)
+ {'differences': [...]}
+
+ """
+
+ if isinstance(source, str) and isinstance(destination, str):
+ if not (Path(source).exists() or Path(destination).exists()):
+ logger.info(f"Comparing strings: {source} with {destination}.")
+ return compare_graph(convert_to_nt(source), convert_to_nt(destination))
+
+ # Convert to Path objects if they're strings
+ src = Path(source) if isinstance(source, str) else source
+ dst = Path(destination) if isinstance(destination, str) else destination
+
+ # Validate existence
+ if not src.exists():
+ logger.error(f"Source path does not exist: {source}")
+ raise FileNotFoundError(f"Source path does not exist: {source}")
+ if not dst.exists():
+ logger.error(f"Destination path does not exist: {destination}")
+ raise FileNotFoundError(f"Destination path does not exist: {destination}")
+
+ # Handle file to file comparison
+ if src.is_file() and dst.is_file():
+ logger.info(f"Comparing files: {src} with {dst}.")
+ return compare_graph(convert_single_file_to_nt(source), convert_single_file_to_nt(destination))
+
+ elif src.is_dir() and dst.is_dir():
+ logger.info(f"Comparing directories: {src} with {dst}.")
+ differences = {}
+
+ valid_extensions = {".jsonld", ".ttl", ".nt"}
+
+ src_files = {f.name: f for f in src.glob("*") if f.is_file() and f.suffix in valid_extensions}
+ dst_files = {f.name: f for f in dst.glob("*") if f.is_file() and f.suffix in valid_extensions}
+
+ # Compare matching files
+ for filename in src_files.keys() | dst_files.keys(): # Union of both file sets
+ src_file = src_files.get(filename)
+ dst_file = dst_files.get(filename)
+
+ if src_file and dst_file:
+ logger.info(f"Processing files: {src_file} with {dst_file}")
+ differences[filename] = compare_graph(
+ convert_single_file_to_nt(src_file),
+ convert_single_file_to_nt(dst_file),
+ )
+ elif src_file and not dst_file:
+ logger.warning(f"File missing in destination: {filename}")
+ differences[filename] = {"status": "missing in destination"}
+ elif dst_file and not src_file:
+ logger.warning(f"File missing in source: {filename}")
+ differences[filename] = {"status": "missing in source"}
+
+ return differences
+ else:
+ logger.error("Source and destination must be of the same type (both files, both folders, or both strings)")
+ raise ValueError("Source and destination must be of the same type (both files, both folders, or both strings)")
-def hello_world() -> None:
- """Prints hello world."""
- print("Hello World!")
-if __name__ == "__main__":
- hello_world()
\ No newline at end of file
diff --git a/src/ProvSense/cli.py b/src/ProvSense/cli.py
index 8f2dae3..883d905 100644
--- a/src/ProvSense/cli.py
+++ b/src/ProvSense/cli.py
@@ -1,17 +1,83 @@
-"""This module defines CLI commands for the PipePal application."""
+"""
+This module defines CLI commands for the ProvSense application.
+"""
import click
-
-from .app import hello_world as hw_function # Renamed to avoid conflict
-
+from .app import compare_items
@click.group()
-def main() -> None:
- """Define the main CLI group."""
+@click.pass_context
+def cli(ctx):
+ """CLI commands for the ProvSense"""
pass
-@main.command()
-def hello_world() -> None:
- """Execute the hello_world command from the app module."""
- hw_function()
+@cli.command()
+@click.option(
+ '--source',
+ required=True,
+ help=(
+ "Source path (file/folder) or string (e.g., JSON-LD/TTL) for comparison. "
+ "If providing a string, ensure it is properly formatted."
+ )
+)
+
+@click.option(
+ '--destination',
+ required=True,
+ help=(
+ "Destination path (file/folder) or string (e.g., JSON-LD/TTL) for comparison. "
+ "If providing a string, ensure it is properly formatted."
+ )
+)
+
+
+def compare(source: str, destination: str) -> dict:
+ """
+ Compare changes in knowledge graph files (JSON-LD, Turtle) across different input sources.
+
+ This function detects and analyzes differences between RDF data sources, supporting comparisons
+ at the folder, file, or direct string level.
+
+ Options:
+
+ - --source (str):
+ The first input source, which can be a folder path, file path, or RDF string.
+ If providing a string, ensure it is correctly formatted in JSON-LD or Turtle.
+
+ - --destination (str):
+ The second input source, which can be a folder path, file path, or RDF string.
+ If providing a string, ensure it is correctly formatted in JSON-LD or Turtle.
+
+ Returns:
+ dict: A dictionary containing the comparison results highlighting the differences.
+
+ Raises:
+ FileNotFoundError: If the specified folder or file does not exist.
+ ValueError: If an unsupported input type is provided.
+
+ Example Usage:
+ - Compare two folders:
+
+ `$ cli compare --source "folder1" --destination "folder2"`
+
+ - Compare two files:
+
+ `$ cli compare --source "file1.ttl" --destination "file2.ttl"`
+
+ - Compare two RDF strings:
+
+ `$ cli compare --source '{"@context": "http://schema.org", "name": "Alice"}' --destination '{"@context": "http://schema.org", "name": "Bob"}'`
+ """
+ result = compare_items(source=source, destination=destination)
+ click.echo(result)
+
+ try:
+ # compare_items(source, destination, type) to be implemented.
+ click.echo(f"Comparing {source} with {destination}.")
+ except Exception as e:
+ click.echo(f"Error: {str(e)}", err=True)
+ raise click.Abort()
+
+if __name__ == "__main__":
+ cli()
\ No newline at end of file
diff --git a/src/ProvSense/shared.py b/src/ProvSense/shared.py
new file mode 100644
index 0000000..bc5eed5
--- /dev/null
+++ b/src/ProvSense/shared.py
@@ -0,0 +1,374 @@
+# -*- coding: utf-8 -*-
+# -----------------------------------------------------------------------------
+# DISCLAIMER: This software is provided "as is" without any warranty,
+# express or implied, including but not limited to the warranties of
+# merchantability, fitness for a particular purpose, and non-infringement.
+#
+# In no event shall the authors or copyright holders be liable for any
+# claim, damages, or other liability, whether in an action of contract,
+# tort, or otherwise, arising from, out of, or in connection with the
+# software or the use or other dealings in the software.
+# -----------------------------------------------------------------------------
+
+# @Author : Tek Raj Chhetri
+# @Email : tekraj@mit.edu
+# @Web : https://tekrajchhetri.com/
+# @File : shared.py
+# @Software: PyCharm
+
+
+import logging
+import os
+import json
+from rdflib import Graph, Dataset
+from rdflib.query import Result
+
+logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
+
+logger = logging.getLogger(__name__)
+
+import os
+
+def read_file(path):
+ """
+ Read the content of a file (JSON-LD, Turtle, or N-Triples) and return it as a string.
+
+ This function reads the file as plain text, regardless of its format.
+ It supports JSON-LD (.jsonld), Turtle (.ttl), and N-Triples (.nt) files.
+
+ Args:
+ path (str): The file path to the RDF file.
+
+ Returns:
+ str: The raw content of the file as a string.
+
+ Raises:
+ FileNotFoundError: If the specified file does not exist.
+ ValueError: If the file format is unsupported.
+
+ Example:
+ >>> with open("example.jsonld", "w") as f:
+ ... f.write('{"@context": "http://schema.org", "name": "Alice"}')
+
+ >>> read_file("example.jsonld")
+ '{"@context": "http://schema.org", "name": "Alice"}'
+ """
+ if not os.path.isfile(path):
+ raise FileNotFoundError(f"File not found: {path}")
+
+ # Determine file format based on extension
+ file_extension = os.path.splitext(path)[1].lower()
+ if file_extension not in {'.jsonld', '.ttl', '.nt'}:
+ raise ValueError(f"Unsupported file format: {file_extension}")
+
+ # Read file content
+ with open(path, 'r', encoding='utf-8') as file:
+ return file.read()
+
+def detect_rdf_format(data: str):
+ """
+ Detect the format of a given RDF data string.
+
+ This function analyzes the provided RDF string and determines whether it is in
+ Turtle (TTL), JSON-LD, or N-Triples (NT) format. If the format cannot be identified,
+ it returns 'Unknown'.
+
+ Args:
+ data (str): The RDF data as a string.
+
+ Returns:
+ str: The detected format, which can be one of the following:
+ - 'turtle' for Turtle (TTL) format.
+ - 'json-ld' for JSON-LD format.
+ - 'nt' for N-Triples (NT) format.
+ - 'Unknown' if the format cannot be determined.
+
+ Examples:
+ Example 1 (Turtle format):
+ Input:
+ @prefix ex: .
+ ex:PersonA ex:name "Alice" .
+ ex:PersonA ex:worksAt ex:CompanyY .
+ ex:CompanyY ex:location "New York" .
+ Output:
+ 'turtle'
+
+ Example 2 (N-Triples format):
+ Input:
+ "Alice" .
+ .
+ "New York" .
+ Output:
+ 'nt'
+
+ Example 3 (JSON-LD format):
+ Input:
+ {
+ "@context": "http://example.org/",
+ "@id": "urn:uuid:550e8400-e29b-41d4-a716-446655440000",
+ "@type": "Person",
+ "name": "Alice",
+ "worksAt": {
+ "@id": "CompanyY"
+ }
+ }
+ Output:
+ 'json-ld'
+ """
+ logger.info("Checking the input types, i.e, whether it is ttl, json-ld or nt")
+
+ # try to read Ntriple string
+ # Note the order is important particularly for nt and ttl, so do not move this code.
+ try:
+ graph = Graph()
+ graph.parse(data=data, format="nt")
+ return "nt"
+ except Exception:
+ pass # Not N-Triples
+
+ # try to read json-ld string
+ try:
+ json_data = json.loads(data)
+ if isinstance(json_data, dict) and any(key in json_data for key in ["@context", "@id", "@type", "@graph"]):
+ return "json-ld"
+ except json.JSONDecodeError:
+ pass # Not JSON-LD, continue checking other formats
+
+ # Try parsing as Turtle to check if the input string is in turtle data
+ try:
+ graph = Graph()
+ graph.parse(data=data, format="turtle")
+ return "turtle"
+ except Exception:
+ pass # Not TTL, continue checking
+
+ return "Unknown"
+
+def convert_to_nt(input_string):
+ """
+ Convert an RDF string from JSON-LD or Turtle (TTL) format to N-Triples (NT) format.
+
+ This function takes an RDF string in either JSON-LD or Turtle format, parses its contents,
+ and converts it into the N-Triples format, a line-based RDF serialization.
+
+ Args:
+ input_string (str): The RDF data as a string in JSON-LD or Turtle format.
+
+ Returns:
+ str: The RDF data converted into N-Triples format.
+
+ Raises:
+ ValueError: If the input format is unsupported or the RDF data cannot be parsed.
+ """
+
+
+ input_type = detect_rdf_format(input_string)
+
+ if input_type == "nt":
+ return input_string
+
+ try:
+ if input_type == "json-ld" or input_type == "turtle":
+ return Graph().parse(data=input_string, format=input_type).serialize(format="nt")
+ else:
+ logging.error((f"Unsupported RDF format detected: {input_type}"))
+ raise ValueError(f"Unsupported RDF format detected: {input_type}")
+
+ except Exception as e:
+ logging.error(f"Failed to convert input data to N-Triples: {str(e)}")
+ raise ValueError(f"Failed to convert RDF data to N-Triples: {str(e)}")
+
+def get_file_extension(file_name):
+ """
+ Extract the file extension from a given file name.
+
+ This function retrieves the file extension (including the dot) from the provided file name
+ and returns it in lowercase.
+
+ Args:
+ file_name (str): The name of the file, including its extension.
+
+ Returns:
+ str: The file extension in lowercase (e.g., '.txt', '.json', '.ttl').
+ Returns an empty string if no extension is found.
+
+ Example:
+ >>> get_file_extension("example.json")
+ '.json'
+
+ >>> get_file_extension("document.TTL")
+ '.ttl'
+
+ >>> get_file_extension("no_extension")
+ ''
+ """
+
+ return os.path.splitext(file_name)[1].lower()
+
+def convert_single_file_to_nt(input_file):
+ """
+ Convert a JSON-LD or Turtle (TTL) file to N-Triples (N-Triples) format.
+
+ This function reads a given JSON-LD or TTL file, processes its RDF data, and converts it into
+ the N-Triples format, which is a line-based RDF serialization.
+
+ Args:
+ file_path (str): The path to the input file, which must be in JSON-LD or Turtle format.
+
+ Returns:
+ str: A string representation of the RDF data in N-Triples format.
+ """
+ valid_extensions = {".jsonld", ".ttl", ".nt"}
+ if get_file_extension(input_file) not in valid_extensions:
+ logger.error(f"Invalid file format={input_file}")
+ raise ValueError("File must have a .jsonld, .ttl, or .nt extension.")
+
+ if not os.path.isfile(input_file):
+ logging.error(f"File not found: {input_file}")
+ raise FileNotFoundError(f"File not found: {input_file}")
+
+ return input_file if get_file_extension(input_file)=="nt" else convert_to_nt(read_file(input_file))
+
+def compare_graph(source: str, destination: str) -> list[dict]:
+ """
+ Compares two RDF graphs represented in N-Triples format and identifies differences.
+
+ The function creates two named graphs (`src` and `dst`) using fixed IRIs:
+ - `http://graphcompare.org/src` for the source graph
+ - `http://graphcompare.org/dst` for the destination graph
+
+ It then runs a SPARQL query to detect differences between the graphs.
+
+ Args:
+ source (str): RDF graph in N-Triples format representing the source dataset.
+ destination (str): RDF graph in N-Triples format representing the destination dataset.
+
+ Returns:
+
+ - list[dict]: A list of dictionaries representing the differences found between the graphs.
+ Each dictionary contains:
+ - `subject`: The subject of the differing triple.
+ - `property`: The predicate/property of the differing triple.
+ - `src_value`: The object value from the source graph (if available).
+ - `dst_value`: The object value from the destination graph (if available).
+
+ Sample output
+ ```Python
+ [{'subject': 'urn:uuid:550e8400-e29b-41d4-a716-446655440000',
+ 'property': 'http://example.org/worksAt',
+ 'src_value': 'http://example.org/CompanyX1',
+ 'dst_value': 'http://example.org/CompanyY'},
+ {'subject': 'urn:uuid:550e8400-e29b-41d4-a716-446655440000',
+ 'property': 'http://example.org/location',
+ 'src_value': None,
+ 'dst_value': 'New York'},
+ {'subject': 'urn:uuid:550e8400-e29b-41d4-a716-446655440000',
+ 'property': 'http://example.org/worksAt',
+ 'src_value': 'http://example.org/CompanyX1',
+ 'dst_value': 'http://example.org/CompanyY'}]
+ ```
+ """
+ g = Dataset() #removed ConjunctiveGraph - DeprecationWarning: ConjunctiveGraph is deprecated, use Dataset instead.
+
+ # Create named graphs for comparison
+ g_src = g.get_context("http://graphcompare.org/src")
+ g_src.parse(data=source, format="nt")
+
+ g_dst = g.get_context("http://graphcompare.org/dst")
+ g_dst.parse(data=destination, format="nt")
+
+
+ results: Result = g.query(construct_diff_sparql_query())
+
+ # Convert results to a list of dictionaries
+ result_list = [
+ {
+ "subject": str(row.subject),
+ "property": str(row.property),
+ "src_value": str(row.srcGraphValue) if row.srcGraphValue else None,
+ "dst_value": str(row.dstGraphValue) if row.dstGraphValue else None,
+ }
+ for row in results
+ ]
+
+ return result_list
+
+def construct_diff_sparql_query():
+ """
+ Constructs a SPARQL query to compare RDF triples from two named graphs:
+ a source graph (`src`) and a destination graph (`dst`). The query identifies
+ triples that differ between the two graphs, including:
+
+ - Triples present in the source graph but either missing or different in the destination graph.
+ - Triples present in the destination graph but either missing or different in the source graph.
+
+ The query retrieves the following variables:
+ - `?subject`: The subject of the triple.
+ - `?property`: The predicate/property of the triple.
+ - `?srcGraphValue`: The object value from the source graph (if available).
+ - `?dstGraphValue`: The object value from the destination graph (if available).
+
+ The results are ordered by `?subject`.
+
+ Returns:
+ str: The SPARQL query as a string.
+ """
+ query = """
+ PREFIX gcp:
+ SELECT DISTINCT ?subject ?property ?srcGraphValue ?dstGraphValue
+ WHERE {
+ {
+ GRAPH { ?subject ?property ?srcGraphValue }
+ OPTIONAL { GRAPH { ?subject ?property ?dstGraphValue } }
+ FILTER (!BOUND(?dstGraphValue) || ?srcGraphValue != ?dstGraphValue)
+ }
+ UNION
+ {
+ GRAPH { ?subject ?property ?dstGraphValue }
+ OPTIONAL { GRAPH { ?subject ?property ?srcGraphValue } }
+ FILTER (!BOUND(?srcGraphValue) || ?srcGraphValue != ?dstGraphValue)
+ }
+ }
+ ORDER BY ?subject
+ """
+ return query
+
+if __name__ == "__main__":
+ input_ttl_data = """
+ @prefix ex: .
+ ex:PersonA ex:name "Alice" .
+ ex:PersonA ex:worksAt ex:CompanyY .
+ ex:CompanyY ex:location "New York" .
+ """
+ print("Converting ttl data to ntriples")
+ print(convert_to_nt(input_ttl_data))
+
+ jsonld_data = """
+ {
+ "@context": "http://example.org/",
+ "@id": "urn:uuid:550e8400-e29b-41d4-a716-446655440000",
+ "@type": "Person",
+ "name": "Alice",
+ "worksAt": {
+ "@id": "CompanyY"
+ }
+ }
+ """
+ print("Converting jsonld data to ntriples")
+ print(convert_to_nt(jsonld_data))
+
+ ntriples_data = """
+ "Alice" .
+ .
+ "New York" .
+ """
+ print("Converting ntriples_data to ntriples")
+ print(convert_to_nt(ntriples_data))
+
+ input_src_file = "../../example/test_dst.jsonld"
+ print("Converting file to ntriples")
+ print(convert_single_file_to_nt(input_src_file))
+
+ input_src_ttl_file = "../../example/test_data.ttl"
+ print("Converting ttl file to ntriples")
+ print(convert_single_file_to_nt(input_src_ttl_file))
\ No newline at end of file
diff --git a/src/tests/test_app.py b/src/tests/test_app.py
deleted file mode 100644
index 0906202..0000000
--- a/src/tests/test_app.py
+++ /dev/null
@@ -1,14 +0,0 @@
-"""Tests for the app hello_world function."""
-from _pytest.capture import CaptureFixture
-
-from ProvSense.app import hello_world
-
-
-def test_hello_world(capsys: CaptureFixture) -> None:
- """A test function for hello_world.
-
- This function uses the capsys fixture to capture stdout and stderr.
- """
- hello_world()
- captured = capsys.readouterr() # Captures the output of hello_world()
- assert "Hello World!\n" == captured.out
\ No newline at end of file
diff --git a/src/tests/test_conversion.py b/src/tests/test_conversion.py
new file mode 100644
index 0000000..52d532f
--- /dev/null
+++ b/src/tests/test_conversion.py
@@ -0,0 +1,95 @@
+import unittest
+from unittest.mock import patch, mock_open
+import os
+from ProvSense.shared import _convert_to_nt, convert_single_file_to_nt
+
+class TestNTriplesConversion(unittest.TestCase):
+ def setUp(self):
+ """Set up test data"""
+ self.test_jsonld_str = """{
+ "@context": {
+ "@vocab": "http://example.org/"
+ },
+ "@type": "Person",
+ "@id": "http://example.org/PersonA",
+ "name": "Alice",
+ "worksAt": { "@id": "http://example.org/CompanyY" }
+ }"""
+
+ self.test_ttl_str = """
+@prefix ex: .
+ex:PersonA a ex:Person ;
+ ex:name "Alice" ;
+ ex:worksAt ex:CompanyY .
+"""
+
+ self.test_ntriples_str = """ .
+ "Alice" .
+ ."""
+
+ def test_convert_jsonld_to_nt(self):
+ """Test converting JSON-LD to N-Triples"""
+ result = _convert_to_nt(self.test_jsonld_str)
+ self.assertIsInstance(result, str)
+ expected_triples = {
+ " ",
+ " \"Alice\"",
+ " "
+ }
+ result_triples = {line.strip(' .') for line in result.strip().split('\n') if line.strip()}
+ self.assertEqual(expected_triples, result_triples)
+
+ def test_convert_ttl_to_nt(self):
+ """Test converting TTL to N-Triples"""
+ result = _convert_to_nt(self.test_ttl_str)
+ self.assertIsInstance(result, str)
+ expected_triples = {
+ " ",
+ " \"Alice\"",
+ " "
+ }
+ result_triples = {line.strip(' .') for line in result.strip().split('\n') if line.strip()}
+ self.assertEqual(expected_triples, result_triples)
+
+ def test_convert_nt_to_nt(self):
+ """Test converting N-Triples to N-Triples (should return the same format)"""
+ result = _convert_to_nt(self.test_ntriples_str)
+ self.assertIsInstance(result, str)
+ expected_triples = {line.strip(' .') for line in self.test_ntriples_str.strip().split('\n') if line.strip()}
+ result_triples = {line.strip(' .') for line in result.strip().split('\n') if line.strip()}
+ self.assertEqual(expected_triples, result_triples)
+
+ def test_convert_single_file_to_nt(self):
+ """Test converting a single JSON-LD file to N-Triples"""
+ # Create a temporary test file
+ test_file = "test_temp.jsonld"
+ try:
+ with open(test_file, 'w') as f:
+ f.write(self.test_jsonld_str)
+
+ result = convert_single_file_to_nt(test_file)
+ self.assertIsInstance(result, str)
+ expected_triples = {
+ " ",
+ " \"Alice\"",
+ " "
+ }
+ result_triples = {line.strip(' .') for line in result.strip().split('\n') if line.strip()}
+ self.assertEqual(expected_triples, result_triples)
+ finally:
+ # Clean up the temporary file
+ if os.path.exists(test_file):
+ os.remove(test_file)
+
+ def test_convert_single_file_not_found(self):
+ """Test file not found error for single file conversion"""
+ with self.assertRaises(FileNotFoundError):
+ convert_single_file_to_nt("nonexistent.jsonld")
+
+ def test_convert_single_file_invalid_extension(self):
+ """Test invalid file extension error for single file conversion"""
+ with self.assertRaises(ValueError):
+ convert_single_file_to_nt("test.txt")
+
+if __name__ == '__main__':
+ unittest.main()