chrontext


Namechrontext JSON
Version 0.9.11 PyPI version JSON
download
home_pageNone
SummaryHybrid SPARQL query engine for timeseries data
upload_time2024-10-26 07:09:10
maintainerNone
docs_urlNone
authorNone
requires_python>=3.9
licenseNone
keywords rdf graph arrow sparql timeseries
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # chrontext: High-performance hybrid query engine for knowledge graphs and analytical data (e.g. time-series)
Chrontext allows you to use your knowledge graph to access large amounts of time-series or other analytical data. It uses a commodity SPARQL Triplestore and your existing data storage infrastructure.
It currently supports time-series stored in a PostgreSQL-compatible Database such as DuckDB, Google Cloud BigQuery (SQL) and OPC UA HA, but can easily be extended to other APIs and databases.
![Chrontext Architecture](doc/chrontext_arch.png)

Chrontext forms a semantic layer that allows self-service data access, abstracting away technical infrastructure. 
Users can create query-based inputs for data products, that maintains these data products as the knowledge graph is maintained, and that can be deployed across heterogeneous on-premise and cloud infrastructures with the same API. 

Chrontext is a high-performance Python library built in Rust using [Polars](https://www.pola.rs/), and relies heavily on packages from the [Oxigraph](https://github.com/oxigraph/oxigraph) project. 
Chrontext works with [Apache Arrow](https://arrow.apache.org/), prefers time-series transport using [Apache Arrow Flight](https://arrow.apache.org/docs/format/Flight.html) and delivers results as [Polars](https://www.pola.rs/) DataFrames.

Please reach out to [Data Treehouse](https://www.data-treehouse.com/contact-8) if you would like help trying Chrontext, or require support for a different database backend. 

## Installing
Chrontext is in pip, just use:
```shell
pip install chrontext
```
The API is documented [HERE](https://datatreehouse.github.io/chrontext/chrontext/chrontext.html). 

## Example query in Python
The code assumes that we have a SPARQL-endpoint and BigQuery set up with time-series. 

```python
...
q = """
PREFIX xsd:<http://www.w3.org/2001/XMLSchema#>
PREFIX ct:<https://github.com/DataTreehouse/chrontext#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> 
PREFIX rds: <https://github.com/DataTreehouse/solar_demo/rds_power#> 
SELECT ?path ?t ?ts_pow_value ?ts_irr_value
WHERE {
    ?site a rds:Site;
    rdfs:label "Jonathanland";
    rds:functionalAspect ?block.
    # At the Block level there is an irradiation measurement:
    ?block a rds:A;
    ct:hasTimeseries ?ts_irr.
    ?ts_irr rdfs:label "RefCell1_Wm2".
    
    # At the Inverter level, there is a Power measurement
    ?block rds:functionalAspect+ ?inv.
    ?inv a rds:TBB;
    rds:path ?path;
    ct:hasTimeseries ?ts_pow.
    ?ts_pow rdfs:label "InvPDC_kW".
    
    ?ts_pow ct:hasDataPoint ?ts_pow_datapoint.
    ?ts_pow_datapoint ct:hasValue ?ts_pow_value;
        ct:hasTimestamp ?t.
    ?ts_irr ct:hasDataPoint ?ts_irr_datapoint.
    ?ts_irr_datapoint ct:hasValue ?ts_irr_value;
        ct:hasTimestamp ?t.
    FILTER(
        ?t >= "2018-08-24T12:00:00+00:00"^^xsd:dateTime && 
        ?t <= "2018-08-24T13:00:00+00:00"^^xsd:dateTime)
} ORDER BY ?path ?t 
"""
df = engine.query(q)
```

This produces the following DataFrame:

| path                        | t                       | ts_pow_value | ts_irr_value |
| ---                         | ---                     | ---          | ---          |
| str                         | datetime[ns, UTC]       | f64          | f64          |
| =<Jonathanland>.A1.RG1.TBB1 | 2018-08-24 12:00:00 UTC | 39.74        | 184.0        |
| =<Jonathanland>.A1.RG1.TBB1 | 2018-08-24 12:00:01 UTC | 39.57        | 184.0        |
| =<Jonathanland>.A1.RG1.TBB1 | 2018-08-24 12:00:02 UTC | 40.1         | 184.0        |
| =<Jonathanland>.A1.RG1.TBB1 | 2018-08-24 12:00:03 UTC | 40.05        | 184.0        |
| =<Jonathanland>.A1.RG1.TBB1 | 2018-08-24 12:00:04 UTC | 40.02        | 184.0        |
| …                           | …                       | …            | …            |
| =<Jonathanland>.A5.RG9.TBB1 | 2018-08-24 12:59:56 UTC | 105.5        | 427.5        |
| =<Jonathanland>.A5.RG9.TBB1 | 2018-08-24 12:59:57 UTC | 104.9        | 427.6        |
| =<Jonathanland>.A5.RG9.TBB1 | 2018-08-24 12:59:58 UTC | 105.6        | 428.0        |
| =<Jonathanland>.A5.RG9.TBB1 | 2018-08-24 12:59:59 UTC | 105.9        | 428.0        |
| =<Jonathanland>.A5.RG9.TBB1 | 2018-08-24 13:00:00 UTC | 105.7        | 428.5        |

## API
The API is documented [HERE](https://datatreehouse.github.io/chrontext/chrontext/chrontext.html).

## Tutorial using DuckDB
In the following tutorial, we assume that you have a couple of CSV-files on disk that you want to query.
We assume that you have DuckDB and chrontext installed, if not, do `pip install chrontext duckdb`. 
Installing chrontext will also install sqlalchemy, which we rely on to define the virtualized DuckDB tables.

### CSV files
Our csv files look like this.

ts1.csv :

```
timestamp,value
2022-06-01T08:46:52,1
2022-06-01T08:46:53,10
..
2022-06-01T08:46:59,105
```

ts2.csv:
```
timestamp,value
2022-06-01T08:46:52,2
2022-06-01T08:46:53,20
...
2022-06-01T08:46:59,206
```

### DuckDB setup:
We need to create a class with a method `query` that takes a SQL string its argument, returning a Polars DataFrame. 
In this class, we just hard code the DuckDB setup in the constructor. 
```python
import duckdb
import polars as pl

class MyDuckDB():
    def __init__(self):
        con = duckdb.connect()
        con.execute("SET TIME ZONE 'UTC';")
        con.execute("""CREATE TABLE ts1 ("timestamp" TIMESTAMPTZ, "value" INTEGER)""")
        ts_1 = pl.read_csv("ts1.csv", try_parse_dates=True).with_columns(pl.col("timestamp").dt.replace_time_zone("UTC"))
        con.append("ts1", df=ts_1.to_pandas())
        con.execute("""CREATE TABLE ts2 ("timestamp" TIMESTAMPTZ, "value" INTEGER)""")
        ts_2 = pl.read_csv("ts2.csv", try_parse_dates=True).with_columns(pl.col("timestamp").dt.replace_time_zone("UTC"))
        con.append("ts2", df=ts_2.to_pandas())
        self.con = con


    def query(self, sql:str) -> pl.DataFrame:
        # We execute the query and return it as a Polars DataFrame.
        # Chrontext expects this method to exist in the provided class.
        df = self.con.execute(sql).pl()
        return df

my_db = MyDuckDB()
```

### Defining a virtualized SQL
We first define a sqlalchemy select query involving the two tables. __It is crucial that we have a column labelled "id" here__. Chrontext will modify this query when executing hybrid queries.
```python
from sqlalchemy import MetaData, Table, Column, bindparam
metadata = MetaData()
ts1_table = Table(
    "ts1",
    metadata,
    Column("timestamp"),
    Column("value")
)
ts2_table = Table(
    "ts2",
    metadata,
    Column("timestamp"),
    Column("value")
)
ts1 = ts1_table.select().add_columns(
    bindparam("id1", "ts1").label("id"),
)
ts2 = ts2_table.select().add_columns(
    bindparam("id2", "ts2").label("id"),
)
sql = ts1.union(ts2)
```

Now, we are ready to define the virtualized backend. We will annotate nodes of the graph with a resource data property. 
These data properties will be linked to virtualized RDF triples in the DuckDB backend. The `resource_sql_map` decides which SQL is used for each resource property. 
```python
from chrontext import VirtualizedPythonDatabase

vdb = VirtualizedPythonDatabase(
    database=my_db,
    resource_sql_map={"my_resource": sql},
    sql_dialect="postgres"
)
```

The triple below will link the `ex:myWidget1` to triples defined by the above sql. 
```
ex:myWidget1 ct:hasResource "my_resource" . 
```
However, it will only be linked to those triples corresponding to rows where the identifier column equals the identifier associated with `ex:myWidget1`.
Below, we define that ex:instanceA is only linked to those rows where the `id` column is `ts1`.
```
ex:myWidget1 ct:hasIdentifier "ts1" . 
```
In any such resource sql, the `id` column is mandatory. 
### Relating the Database to RDF Triples
Next, we want to relate the rows in this sql, each containing id, timestamp, value to RDF triples, using a template. __It is crucial to have the column id__.
```python
from chrontext import Prefix, Variable, Template, Parameter, RDFType, Triple, XSD
ct = Prefix("ct", "https://github.com/DataTreehouse/chrontext#")
xsd = XSD()
id = Variable("id")
timestamp = Variable("timestamp")
value = Variable("value")
dp = Variable("dp")
resources = {
    "my_resource": Template(
        iri=ct.suf("my_resource"),
        parameters=[
            Parameter(id, rdf_type=RDFType.Literal(xsd.string)),
            Parameter(timestamp, rdf_type=RDFType.Literal(xsd.dateTime)),
            Parameter(value, rdf_type=RDFType.Literal(xsd.double)),
        ],
        instances=[
            Triple(id, ct.suf("hasDataPoint"), dp),
            Triple(dp, ct.suf("hasValue"), value),
            Triple(dp, ct.suf("hasTimestamp"), timestamp)
        ]
)}
```
This means that our instance `ex:myWidget1`, will be associated with a value and a timestamp (and a blank data point) for each row in `ts1.csv`.
For instance, the first row means we have: 
```
ex:widget1 ct:hasDataPoint _:b1 .
_:b1 ct:hasTimestamp "2022-06-01T08:46:52Z"^^xsd:dateTime .
_:b1 ct:hasValue 1 .
```
Chrontext is created for those cases when this is infeasibly many triples, so we do not want to materialize them, but query them.
### Creating the engine and querying:
The context for our analytical data (e.g. a model of an industrial asset) has to be stored in a SPARQL endpoint.
In this case, we use an embedded Oxigraph engine that comes with chrontext. Now we assemble the pieces and create the engine.
```python
from chrontext import Engine, SparqlEmbeddedOxigraph
oxigraph_store = SparqlEmbeddedOxigraph(rdf_file="my_graph.ttl", path="oxigraph_db_tutorial")
engine = Engine(
    resources,
    virtualized_python_database=vdb,
    sparql_embedded_oxigraph=oxigraph_store)
engine.init()
```
Now we can use our context to query the dataset. The aggregation below are pushed into DuckDB.
The example below is a bit simple, but complex conditions can identify the `?w` and `?s`.
```python
q = """
    PREFIX xsd:<http://www.w3.org/2001/XMLSchema#>
    PREFIX chrontext:<https://github.com/DataTreehouse/chrontext#>
    PREFIX types:<http://example.org/types#>
    SELECT ?w (SUM(?v) as ?sum_v) WHERE {
        ?w types:hasSensor ?s .
        ?s a types:ThingCounter .
        ?s chrontext:hasTimeseries ?ts .
        ?ts chrontext:hasDataPoint ?dp .
        ?dp chrontext:hasTimestamp ?t .
        ?dp chrontext:hasValue ?v .
        FILTER(?t > "2022-06-01T08:46:53Z"^^xsd:dateTime) .
    } GROUP BY ?w
    """
df = engine.query(q)
print(df)
```
This produces the following result:

| w                                   | sum_v         |
|-------------------------------------|---------------|
| str                                 | decimal[38,0] |
| <http://example.org/case#myWidget1> | 1215          |
| <http://example.org/case#myWidget2> | 1216          |

## Roadmap in brief
Let us know if you have suggestions!
### Stabilization
Chrontext will be put into use in the energy industry during the period, and will be stabilized as part of this process. We are very interested in your bug reports!

### Support for Azure Data Explorer / KustoQL
We are likely adding support for ADX/KustoQL. Let us know if this is something that would be useful for you. 

### Support for Databricks SQL
We are likely adding support for Databricks SQL as the virtualization backend. 

### Generalization to analytical data (not just time series!)
While chrontext is currently focused on time series data, we are incrementally adding support for contextualization of arbitrary analytical data. 

### Support for multiple databases
Currently, we only support one database backend at a given time. We plan to support hybrid queries across multiple virtualized databases. 

## References
Chrontext is joint work by Magnus Bakken and Professor [Ahmet Soylu](https://www.oslomet.no/om/ansatt/ahmetsoy/) at OsloMet.
To read more about Chrontext, read the article [Chrontext: Portable Sparql Queries Over Contextualised Time Series Data in Industrial Settings](https://www.sciencedirect.com/science/article/pii/S0957417423006516).

## License
All code produced since August 1st. 2023 is copyrighted to [Data Treehouse AS](https://www.data-treehouse.com/) with an Apache 2.0 license unless otherwise noted.

All code which was produced before August 1st. 2023 copyrighted to [Prediktor AS](https://www.prediktor.com/) with an Apache 2.0 license unless otherwise noted, and has been financed by [The Research Council of Norway](https://www.forskningsradet.no/en/) (grant no. 316656) and [Prediktor AS](https://www.prediktor.com/) as part of a PhD Degree. The code at this state is archived in the repository at [https://github.com/DataTreehouse/chrontext](https://github.com/DataTreehouse/chrontext).


            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "chrontext",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.9",
    "maintainer_email": null,
    "keywords": "rdf, graph, arrow, sparql, timeseries",
    "author": null,
    "author_email": "Magnus Bakken <magnus@data-treehouse.com>",
    "download_url": "https://files.pythonhosted.org/packages/fb/61/5e4bafd53698e0902e0e51f1891d66af4275ced2e55d4445475430d63278/chrontext-0.9.11.tar.gz",
    "platform": null,
    "description": "# chrontext: High-performance hybrid query engine for knowledge graphs and analytical data (e.g. time-series)\nChrontext allows you to use your knowledge graph to access large amounts of time-series or other analytical data. It uses a commodity SPARQL Triplestore and your existing data storage infrastructure.\nIt currently supports time-series stored in a PostgreSQL-compatible Database such as DuckDB, Google Cloud BigQuery (SQL) and OPC UA HA, but can easily be extended to other APIs and databases.\n![Chrontext Architecture](doc/chrontext_arch.png)\n\nChrontext forms a semantic layer that allows self-service data access, abstracting away technical infrastructure. \nUsers can create query-based inputs for data products, that maintains these data products as the knowledge graph is maintained, and that can be deployed across heterogeneous on-premise and cloud infrastructures with the same API. \n\nChrontext is a high-performance Python library built in Rust using [Polars](https://www.pola.rs/), and relies heavily on packages from the [Oxigraph](https://github.com/oxigraph/oxigraph) project. \nChrontext works with [Apache Arrow](https://arrow.apache.org/), prefers time-series transport using [Apache Arrow Flight](https://arrow.apache.org/docs/format/Flight.html) and delivers results as [Polars](https://www.pola.rs/) DataFrames.\n\nPlease reach out to [Data Treehouse](https://www.data-treehouse.com/contact-8) if you would like help trying Chrontext, or require support for a different database backend. \n\n## Installing\nChrontext is in pip, just use:\n```shell\npip install chrontext\n```\nThe API is documented [HERE](https://datatreehouse.github.io/chrontext/chrontext/chrontext.html). \n\n## Example query in Python\nThe code assumes that we have a SPARQL-endpoint and BigQuery set up with time-series. \n\n```python\n...\nq = \"\"\"\nPREFIX xsd:<http://www.w3.org/2001/XMLSchema#>\nPREFIX ct:<https://github.com/DataTreehouse/chrontext#>\nPREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> \nPREFIX rds: <https://github.com/DataTreehouse/solar_demo/rds_power#> \nSELECT ?path ?t ?ts_pow_value ?ts_irr_value\nWHERE {\n    ?site a rds:Site;\n    rdfs:label \"Jonathanland\";\n    rds:functionalAspect ?block.\n    # At the Block level there is an irradiation measurement:\n    ?block a rds:A;\n    ct:hasTimeseries ?ts_irr.\n    ?ts_irr rdfs:label \"RefCell1_Wm2\".\n    \n    # At the Inverter level, there is a Power measurement\n    ?block rds:functionalAspect+ ?inv.\n    ?inv a rds:TBB;\n    rds:path ?path;\n    ct:hasTimeseries ?ts_pow.\n    ?ts_pow rdfs:label \"InvPDC_kW\".\n    \n    ?ts_pow ct:hasDataPoint ?ts_pow_datapoint.\n    ?ts_pow_datapoint ct:hasValue ?ts_pow_value;\n        ct:hasTimestamp ?t.\n    ?ts_irr ct:hasDataPoint ?ts_irr_datapoint.\n    ?ts_irr_datapoint ct:hasValue ?ts_irr_value;\n        ct:hasTimestamp ?t.\n    FILTER(\n        ?t >= \"2018-08-24T12:00:00+00:00\"^^xsd:dateTime && \n        ?t <= \"2018-08-24T13:00:00+00:00\"^^xsd:dateTime)\n} ORDER BY ?path ?t \n\"\"\"\ndf = engine.query(q)\n```\n\nThis produces the following DataFrame:\n\n| path                        | t                       | ts_pow_value | ts_irr_value |\n| ---                         | ---                     | ---          | ---          |\n| str                         | datetime[ns, UTC]       | f64          | f64          |\n| =<Jonathanland>.A1.RG1.TBB1 | 2018-08-24 12:00:00 UTC | 39.74        | 184.0        |\n| =<Jonathanland>.A1.RG1.TBB1 | 2018-08-24 12:00:01 UTC | 39.57        | 184.0        |\n| =<Jonathanland>.A1.RG1.TBB1 | 2018-08-24 12:00:02 UTC | 40.1         | 184.0        |\n| =<Jonathanland>.A1.RG1.TBB1 | 2018-08-24 12:00:03 UTC | 40.05        | 184.0        |\n| =<Jonathanland>.A1.RG1.TBB1 | 2018-08-24 12:00:04 UTC | 40.02        | 184.0        |\n| \u2026                           | \u2026                       | \u2026            | \u2026            |\n| =<Jonathanland>.A5.RG9.TBB1 | 2018-08-24 12:59:56 UTC | 105.5        | 427.5        |\n| =<Jonathanland>.A5.RG9.TBB1 | 2018-08-24 12:59:57 UTC | 104.9        | 427.6        |\n| =<Jonathanland>.A5.RG9.TBB1 | 2018-08-24 12:59:58 UTC | 105.6        | 428.0        |\n| =<Jonathanland>.A5.RG9.TBB1 | 2018-08-24 12:59:59 UTC | 105.9        | 428.0        |\n| =<Jonathanland>.A5.RG9.TBB1 | 2018-08-24 13:00:00 UTC | 105.7        | 428.5        |\n\n## API\nThe API is documented [HERE](https://datatreehouse.github.io/chrontext/chrontext/chrontext.html).\n\n## Tutorial using DuckDB\nIn the following tutorial, we assume that you have a couple of CSV-files on disk that you want to query.\nWe assume that you have DuckDB and chrontext installed, if not, do `pip install chrontext duckdb`. \nInstalling chrontext will also install sqlalchemy, which we rely on to define the virtualized DuckDB tables.\n\n### CSV files\nOur csv files look like this.\n\nts1.csv :\n\n```\ntimestamp,value\n2022-06-01T08:46:52,1\n2022-06-01T08:46:53,10\n..\n2022-06-01T08:46:59,105\n```\n\nts2.csv:\n```\ntimestamp,value\n2022-06-01T08:46:52,2\n2022-06-01T08:46:53,20\n...\n2022-06-01T08:46:59,206\n```\n\n### DuckDB setup:\nWe need to create a class with a method `query` that takes a SQL string its argument, returning a Polars DataFrame. \nIn this class, we just hard code the DuckDB setup in the constructor. \n```python\nimport duckdb\nimport polars as pl\n\nclass MyDuckDB():\n    def __init__(self):\n        con = duckdb.connect()\n        con.execute(\"SET TIME ZONE 'UTC';\")\n        con.execute(\"\"\"CREATE TABLE ts1 (\"timestamp\" TIMESTAMPTZ, \"value\" INTEGER)\"\"\")\n        ts_1 = pl.read_csv(\"ts1.csv\", try_parse_dates=True).with_columns(pl.col(\"timestamp\").dt.replace_time_zone(\"UTC\"))\n        con.append(\"ts1\", df=ts_1.to_pandas())\n        con.execute(\"\"\"CREATE TABLE ts2 (\"timestamp\" TIMESTAMPTZ, \"value\" INTEGER)\"\"\")\n        ts_2 = pl.read_csv(\"ts2.csv\", try_parse_dates=True).with_columns(pl.col(\"timestamp\").dt.replace_time_zone(\"UTC\"))\n        con.append(\"ts2\", df=ts_2.to_pandas())\n        self.con = con\n\n\n    def query(self, sql:str) -> pl.DataFrame:\n        # We execute the query and return it as a Polars DataFrame.\n        # Chrontext expects this method to exist in the provided class.\n        df = self.con.execute(sql).pl()\n        return df\n\nmy_db = MyDuckDB()\n```\n\n### Defining a virtualized SQL\nWe first define a sqlalchemy select query involving the two tables. __It is crucial that we have a column labelled \"id\" here__. Chrontext will modify this query when executing hybrid queries.\n```python\nfrom sqlalchemy import MetaData, Table, Column, bindparam\nmetadata = MetaData()\nts1_table = Table(\n    \"ts1\",\n    metadata,\n    Column(\"timestamp\"),\n    Column(\"value\")\n)\nts2_table = Table(\n    \"ts2\",\n    metadata,\n    Column(\"timestamp\"),\n    Column(\"value\")\n)\nts1 = ts1_table.select().add_columns(\n    bindparam(\"id1\", \"ts1\").label(\"id\"),\n)\nts2 = ts2_table.select().add_columns(\n    bindparam(\"id2\", \"ts2\").label(\"id\"),\n)\nsql = ts1.union(ts2)\n```\n\nNow, we are ready to define the virtualized backend. We will annotate nodes of the graph with a resource data property. \nThese data properties will be linked to virtualized RDF triples in the DuckDB backend. The `resource_sql_map` decides which SQL is used for each resource property. \n```python\nfrom chrontext import VirtualizedPythonDatabase\n\nvdb = VirtualizedPythonDatabase(\n    database=my_db,\n    resource_sql_map={\"my_resource\": sql},\n    sql_dialect=\"postgres\"\n)\n```\n\nThe triple below will link the `ex:myWidget1` to triples defined by the above sql. \n```\nex:myWidget1 ct:hasResource \"my_resource\" . \n```\nHowever, it will only be linked to those triples corresponding to rows where the identifier column equals the identifier associated with `ex:myWidget1`.\nBelow, we define that ex:instanceA is only linked to those rows where the `id` column is `ts1`.\n```\nex:myWidget1 ct:hasIdentifier \"ts1\" . \n```\nIn any such resource sql, the `id` column is mandatory. \n### Relating the Database to RDF Triples\nNext, we want to relate the rows in this sql, each containing id, timestamp, value to RDF triples, using a template. __It is crucial to have the column id__.\n```python\nfrom chrontext import Prefix, Variable, Template, Parameter, RDFType, Triple, XSD\nct = Prefix(\"ct\", \"https://github.com/DataTreehouse/chrontext#\")\nxsd = XSD()\nid = Variable(\"id\")\ntimestamp = Variable(\"timestamp\")\nvalue = Variable(\"value\")\ndp = Variable(\"dp\")\nresources = {\n    \"my_resource\": Template(\n        iri=ct.suf(\"my_resource\"),\n        parameters=[\n            Parameter(id, rdf_type=RDFType.Literal(xsd.string)),\n            Parameter(timestamp, rdf_type=RDFType.Literal(xsd.dateTime)),\n            Parameter(value, rdf_type=RDFType.Literal(xsd.double)),\n        ],\n        instances=[\n            Triple(id, ct.suf(\"hasDataPoint\"), dp),\n            Triple(dp, ct.suf(\"hasValue\"), value),\n            Triple(dp, ct.suf(\"hasTimestamp\"), timestamp)\n        ]\n)}\n```\nThis means that our instance `ex:myWidget1`, will be associated with a value and a timestamp (and a blank data point) for each row in `ts1.csv`.\nFor instance, the first row means we have: \n```\nex:widget1 ct:hasDataPoint _:b1 .\n_:b1 ct:hasTimestamp \"2022-06-01T08:46:52Z\"^^xsd:dateTime .\n_:b1 ct:hasValue 1 .\n```\nChrontext is created for those cases when this is infeasibly many triples, so we do not want to materialize them, but query them.\n### Creating the engine and querying:\nThe context for our analytical data (e.g. a model of an industrial asset) has to be stored in a SPARQL endpoint.\nIn this case, we use an embedded Oxigraph engine that comes with chrontext. Now we assemble the pieces and create the engine.\n```python\nfrom chrontext import Engine, SparqlEmbeddedOxigraph\noxigraph_store = SparqlEmbeddedOxigraph(rdf_file=\"my_graph.ttl\", path=\"oxigraph_db_tutorial\")\nengine = Engine(\n    resources,\n    virtualized_python_database=vdb,\n    sparql_embedded_oxigraph=oxigraph_store)\nengine.init()\n```\nNow we can use our context to query the dataset. The aggregation below are pushed into DuckDB.\nThe example below is a bit simple, but complex conditions can identify the `?w` and `?s`.\n```python\nq = \"\"\"\n    PREFIX xsd:<http://www.w3.org/2001/XMLSchema#>\n    PREFIX chrontext:<https://github.com/DataTreehouse/chrontext#>\n    PREFIX types:<http://example.org/types#>\n    SELECT ?w (SUM(?v) as ?sum_v) WHERE {\n        ?w types:hasSensor ?s .\n        ?s a types:ThingCounter .\n        ?s chrontext:hasTimeseries ?ts .\n        ?ts chrontext:hasDataPoint ?dp .\n        ?dp chrontext:hasTimestamp ?t .\n        ?dp chrontext:hasValue ?v .\n        FILTER(?t > \"2022-06-01T08:46:53Z\"^^xsd:dateTime) .\n    } GROUP BY ?w\n    \"\"\"\ndf = engine.query(q)\nprint(df)\n```\nThis produces the following result:\n\n| w                                   | sum_v         |\n|-------------------------------------|---------------|\n| str                                 | decimal[38,0] |\n| <http://example.org/case#myWidget1> | 1215          |\n| <http://example.org/case#myWidget2> | 1216          |\n\n## Roadmap in brief\nLet us know if you have suggestions!\n### Stabilization\nChrontext will be put into use in the energy industry during the period, and will be stabilized as part of this process. We are very interested in your bug reports!\n\n### Support for Azure Data Explorer / KustoQL\nWe are likely adding support for ADX/KustoQL. Let us know if this is something that would be useful for you. \n\n### Support for Databricks SQL\nWe are likely adding support for Databricks SQL as the virtualization backend. \n\n### Generalization to analytical data (not just time series!)\nWhile chrontext is currently focused on time series data, we are incrementally adding support for contextualization of arbitrary analytical data. \n\n### Support for multiple databases\nCurrently, we only support one database backend at a given time. We plan to support hybrid queries across multiple virtualized databases. \n\n## References\nChrontext is joint work by Magnus Bakken and Professor [Ahmet Soylu](https://www.oslomet.no/om/ansatt/ahmetsoy/) at OsloMet.\nTo read more about Chrontext, read the article [Chrontext: Portable Sparql Queries Over Contextualised Time Series Data in Industrial Settings](https://www.sciencedirect.com/science/article/pii/S0957417423006516).\n\n## License\nAll code produced since August 1st. 2023 is copyrighted to [Data Treehouse AS](https://www.data-treehouse.com/) with an Apache 2.0 license unless otherwise noted.\n\nAll code which was produced before August 1st. 2023 copyrighted to [Prediktor AS](https://www.prediktor.com/) with an Apache 2.0 license unless otherwise noted, and has been financed by [The Research Council of Norway](https://www.forskningsradet.no/en/) (grant no. 316656) and [Prediktor AS](https://www.prediktor.com/) as part of a PhD Degree. The code at this state is archived in the repository at [https://github.com/DataTreehouse/chrontext](https://github.com/DataTreehouse/chrontext).\n\n",
    "bugtrack_url": null,
    "license": null,
    "summary": "Hybrid SPARQL query engine for timeseries data",
    "version": "0.9.11",
    "project_urls": {
        "Changelog": "https://github.com/DataTreehouse/chrontext/releases",
        "Documentation": "https://datatreehouse.github.io/chrontext/chrontext/chrontext.html",
        "Homepage": "https://github.com/DataTreehouse/chrontext",
        "Repository": "https://github.com/DataTreehouse/chrontext"
    },
    "split_keywords": [
        "rdf",
        " graph",
        " arrow",
        " sparql",
        " timeseries"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "a760e21c3c6229e0a5f99fb41a3a5f227bd8df03505e4913fc28759f2a5d7180",
                "md5": "8b488a047b9ba56ee74cc422135cce82",
                "sha256": "6304dd64523a0871de7498e5f682c9007c83ad269f43ebc066105eb31b179adf"
            },
            "downloads": -1,
            "filename": "chrontext-0.9.11-cp310-cp310-macosx_11_0_arm64.whl",
            "has_sig": false,
            "md5_digest": "8b488a047b9ba56ee74cc422135cce82",
            "packagetype": "bdist_wheel",
            "python_version": "cp310",
            "requires_python": ">=3.9",
            "size": 27216396,
            "upload_time": "2024-10-26T07:13:26",
            "upload_time_iso_8601": "2024-10-26T07:13:26.263192Z",
            "url": "https://files.pythonhosted.org/packages/a7/60/e21c3c6229e0a5f99fb41a3a5f227bd8df03505e4913fc28759f2a5d7180/chrontext-0.9.11-cp310-cp310-macosx_11_0_arm64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "39d221ce166d56c58384ebf10edb3662b0f25eed9ad6b9e01b5b12073e4ae63d",
                "md5": "eeb7a95503d7ec59f5b2bb2c46e66656",
                "sha256": "39f9a575cc6ddc9c7b2077259e6e05e6fd117c42b8b58a0857848684eebdd0c4"
            },
            "downloads": -1,
            "filename": "chrontext-0.9.11-cp310-cp310-macosx_12_0_arm64.whl",
            "has_sig": false,
            "md5_digest": "eeb7a95503d7ec59f5b2bb2c46e66656",
            "packagetype": "bdist_wheel",
            "python_version": "cp310",
            "requires_python": ">=3.9",
            "size": 27233065,
            "upload_time": "2024-10-26T06:59:15",
            "upload_time_iso_8601": "2024-10-26T06:59:15.539594Z",
            "url": "https://files.pythonhosted.org/packages/39/d2/21ce166d56c58384ebf10edb3662b0f25eed9ad6b9e01b5b12073e4ae63d/chrontext-0.9.11-cp310-cp310-macosx_12_0_arm64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "d474eb85a58b932cdada5094027ef326a593a0db8de78f484a6caf39f8343aff",
                "md5": "9e05d070dd4c151041d06812819d271d",
                "sha256": "140d81c20a3763272b86c4256d6b30fc9d456a26abf5d87a3a89de3426168202"
            },
            "downloads": -1,
            "filename": "chrontext-0.9.11-cp310-cp310-manylinux_2_28_x86_64.whl",
            "has_sig": false,
            "md5_digest": "9e05d070dd4c151041d06812819d271d",
            "packagetype": "bdist_wheel",
            "python_version": "cp310",
            "requires_python": ">=3.9",
            "size": 32232229,
            "upload_time": "2024-10-26T07:09:19",
            "upload_time_iso_8601": "2024-10-26T07:09:19.983945Z",
            "url": "https://files.pythonhosted.org/packages/d4/74/eb85a58b932cdada5094027ef326a593a0db8de78f484a6caf39f8343aff/chrontext-0.9.11-cp310-cp310-manylinux_2_28_x86_64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "89b29d571983b893ec9cf9b93eff6aacd21d2ce07f16b94dbb4ebdaed87af35d",
                "md5": "90fa60ec8103a8506528d11c7dcc3229",
                "sha256": "cf31ac8032b5401ccf8ec89d1f993de50d04d4a6db437fde8de458d9f1f2260c"
            },
            "downloads": -1,
            "filename": "chrontext-0.9.11-cp310-none-win_amd64.whl",
            "has_sig": false,
            "md5_digest": "90fa60ec8103a8506528d11c7dcc3229",
            "packagetype": "bdist_wheel",
            "python_version": "cp310",
            "requires_python": ">=3.9",
            "size": 26756645,
            "upload_time": "2024-10-26T07:25:08",
            "upload_time_iso_8601": "2024-10-26T07:25:08.456527Z",
            "url": "https://files.pythonhosted.org/packages/89/b2/9d571983b893ec9cf9b93eff6aacd21d2ce07f16b94dbb4ebdaed87af35d/chrontext-0.9.11-cp310-none-win_amd64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "d4e8e2fcbb6f3cc7ddeaf4b2b3b81ae9b903481e8c0a7b6a04ab8ae1cd4a2182",
                "md5": "b74aa5b7793b553d36e8495bb33e47b7",
                "sha256": "d4b85f0dd1c5a3236ac10d2d7465b9413d3a267158ed4cd6fc61c745a8e153fd"
            },
            "downloads": -1,
            "filename": "chrontext-0.9.11-cp311-cp311-macosx_11_0_arm64.whl",
            "has_sig": false,
            "md5_digest": "b74aa5b7793b553d36e8495bb33e47b7",
            "packagetype": "bdist_wheel",
            "python_version": "cp311",
            "requires_python": ">=3.9",
            "size": 27214274,
            "upload_time": "2024-10-26T07:00:03",
            "upload_time_iso_8601": "2024-10-26T07:00:03.233601Z",
            "url": "https://files.pythonhosted.org/packages/d4/e8/e2fcbb6f3cc7ddeaf4b2b3b81ae9b903481e8c0a7b6a04ab8ae1cd4a2182/chrontext-0.9.11-cp311-cp311-macosx_11_0_arm64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "da7b0729fb2fddf8a333fe4c501e2213c09a8f7fd23d1aa216c99c069ed99c43",
                "md5": "c62c87ed9c2aa63228ad952bdb4dfe5d",
                "sha256": "c62bd44b6f0693df68d54f8a208251e56b8df9c62ea549a83c3d3179b91a71d3"
            },
            "downloads": -1,
            "filename": "chrontext-0.9.11-cp311-cp311-macosx_12_0_arm64.whl",
            "has_sig": false,
            "md5_digest": "c62c87ed9c2aa63228ad952bdb4dfe5d",
            "packagetype": "bdist_wheel",
            "python_version": "cp311",
            "requires_python": ">=3.9",
            "size": 27231658,
            "upload_time": "2024-10-26T07:00:36",
            "upload_time_iso_8601": "2024-10-26T07:00:36.692923Z",
            "url": "https://files.pythonhosted.org/packages/da/7b/0729fb2fddf8a333fe4c501e2213c09a8f7fd23d1aa216c99c069ed99c43/chrontext-0.9.11-cp311-cp311-macosx_12_0_arm64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "afca3a52f5767d6b296dfa77531fc5e349c8ee4b6cc7432a9f9cf145a6c909d5",
                "md5": "023fbcfafb22a625a7b5f81df364fb6b",
                "sha256": "99635d9b4ed08ce376d0e7a8af6f6af2d25b32a6e2271357ab83a87f2cbba892"
            },
            "downloads": -1,
            "filename": "chrontext-0.9.11-cp311-cp311-manylinux_2_28_x86_64.whl",
            "has_sig": false,
            "md5_digest": "023fbcfafb22a625a7b5f81df364fb6b",
            "packagetype": "bdist_wheel",
            "python_version": "cp311",
            "requires_python": ">=3.9",
            "size": 32228727,
            "upload_time": "2024-10-26T07:09:37",
            "upload_time_iso_8601": "2024-10-26T07:09:37.808493Z",
            "url": "https://files.pythonhosted.org/packages/af/ca/3a52f5767d6b296dfa77531fc5e349c8ee4b6cc7432a9f9cf145a6c909d5/chrontext-0.9.11-cp311-cp311-manylinux_2_28_x86_64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "7dab724ccf98c5505d86c62b7140fa4c38bf9ea707b5f8367dcec371b5102e0b",
                "md5": "c75c91fdd9cd5c7e64dd9ffd684e0c05",
                "sha256": "9a811e84946ebb523d1d0ff0e2f2c74e4e8a9610cbb1b35250d9ab83fa8bc682"
            },
            "downloads": -1,
            "filename": "chrontext-0.9.11-cp311-none-win_amd64.whl",
            "has_sig": false,
            "md5_digest": "c75c91fdd9cd5c7e64dd9ffd684e0c05",
            "packagetype": "bdist_wheel",
            "python_version": "cp311",
            "requires_python": ">=3.9",
            "size": 26755695,
            "upload_time": "2024-10-26T07:22:42",
            "upload_time_iso_8601": "2024-10-26T07:22:42.791763Z",
            "url": "https://files.pythonhosted.org/packages/7d/ab/724ccf98c5505d86c62b7140fa4c38bf9ea707b5f8367dcec371b5102e0b/chrontext-0.9.11-cp311-none-win_amd64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "f00288b4d27506cd478adeaa1bdd2f4c684e5ba7c9b58ecb2510c876b0620d58",
                "md5": "3c9af852070a54515cab615a862646f1",
                "sha256": "7ba636e40686ae1de43593be9e8272b49c11cb03cda7e8851b667b46a8fc9ac1"
            },
            "downloads": -1,
            "filename": "chrontext-0.9.11-cp312-cp312-manylinux_2_28_x86_64.whl",
            "has_sig": false,
            "md5_digest": "3c9af852070a54515cab615a862646f1",
            "packagetype": "bdist_wheel",
            "python_version": "cp312",
            "requires_python": ">=3.9",
            "size": 32236459,
            "upload_time": "2024-10-26T07:09:08",
            "upload_time_iso_8601": "2024-10-26T07:09:08.228470Z",
            "url": "https://files.pythonhosted.org/packages/f0/02/88b4d27506cd478adeaa1bdd2f4c684e5ba7c9b58ecb2510c876b0620d58/chrontext-0.9.11-cp312-cp312-manylinux_2_28_x86_64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "99a757636c8f7a7f108ab265420d9bcd670c0d87640b5b3303a910c2dd7abf72",
                "md5": "5c425c2f555dd8f46ee5ce03b560c0d7",
                "sha256": "06d85ce1426bba650776c245054987a5baba6c813c7be4eb258c9f8f8f35658d"
            },
            "downloads": -1,
            "filename": "chrontext-0.9.11-cp38-cp38-macosx_11_0_arm64.whl",
            "has_sig": false,
            "md5_digest": "5c425c2f555dd8f46ee5ce03b560c0d7",
            "packagetype": "bdist_wheel",
            "python_version": "cp38",
            "requires_python": ">=3.9",
            "size": 27220003,
            "upload_time": "2024-10-26T07:13:35",
            "upload_time_iso_8601": "2024-10-26T07:13:35.241855Z",
            "url": "https://files.pythonhosted.org/packages/99/a7/57636c8f7a7f108ab265420d9bcd670c0d87640b5b3303a910c2dd7abf72/chrontext-0.9.11-cp38-cp38-macosx_11_0_arm64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "7dd7ef10eb0e05c0817fcca7f059132446fdbdc60a4084df811569dfd71c579c",
                "md5": "8329ae3a50cdf50404f225d90c4068b7",
                "sha256": "45dbebe8d41897e0948a0e59b9542258925d1c0f0b3d2c6c81154f2080ea7a91"
            },
            "downloads": -1,
            "filename": "chrontext-0.9.11-cp38-cp38-macosx_12_0_arm64.whl",
            "has_sig": false,
            "md5_digest": "8329ae3a50cdf50404f225d90c4068b7",
            "packagetype": "bdist_wheel",
            "python_version": "cp38",
            "requires_python": ">=3.9",
            "size": 27232868,
            "upload_time": "2024-10-26T07:00:12",
            "upload_time_iso_8601": "2024-10-26T07:00:12.176215Z",
            "url": "https://files.pythonhosted.org/packages/7d/d7/ef10eb0e05c0817fcca7f059132446fdbdc60a4084df811569dfd71c579c/chrontext-0.9.11-cp38-cp38-macosx_12_0_arm64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "39efb9b3cf8f251d46654f4bfc464466b7ef1cf95487a627458dfe7bbe6e4ee2",
                "md5": "5c3b9123237906ec8da250e42afe252f",
                "sha256": "d8c3eedd0d470c5e1a71bf3971bb71cfa66685aacbef6076a77c04ac85d13ba5"
            },
            "downloads": -1,
            "filename": "chrontext-0.9.11-cp38-cp38-manylinux_2_28_x86_64.whl",
            "has_sig": false,
            "md5_digest": "5c3b9123237906ec8da250e42afe252f",
            "packagetype": "bdist_wheel",
            "python_version": "cp38",
            "requires_python": ">=3.9",
            "size": 32234266,
            "upload_time": "2024-10-26T07:09:15",
            "upload_time_iso_8601": "2024-10-26T07:09:15.945406Z",
            "url": "https://files.pythonhosted.org/packages/39/ef/b9b3cf8f251d46654f4bfc464466b7ef1cf95487a627458dfe7bbe6e4ee2/chrontext-0.9.11-cp38-cp38-manylinux_2_28_x86_64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "57d79b8fe5e9332df34aa9b574c372c79e8d5f5072da89bdc04d68b05c412fe3",
                "md5": "4bb6358489d30f6c73432d16c3c2b4f3",
                "sha256": "321f149f9409ec6a1702081fa638bff8d7b26d62162d590064af246df49285fb"
            },
            "downloads": -1,
            "filename": "chrontext-0.9.11-cp38-none-win_amd64.whl",
            "has_sig": false,
            "md5_digest": "4bb6358489d30f6c73432d16c3c2b4f3",
            "packagetype": "bdist_wheel",
            "python_version": "cp38",
            "requires_python": ">=3.9",
            "size": 26757754,
            "upload_time": "2024-10-26T07:24:18",
            "upload_time_iso_8601": "2024-10-26T07:24:18.067432Z",
            "url": "https://files.pythonhosted.org/packages/57/d7/9b8fe5e9332df34aa9b574c372c79e8d5f5072da89bdc04d68b05c412fe3/chrontext-0.9.11-cp38-none-win_amd64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "a94e8e1b2d4b7979434dd18664e61f5258eae13a2e8e62ac7ee816e4683c3e7b",
                "md5": "9a1cc3d106582a197c8cce0b8f6a7a1d",
                "sha256": "e07e69ae1b3d0771d1c41e89ba435daadef653bb96a5d477f9126b0a1cdfd1ce"
            },
            "downloads": -1,
            "filename": "chrontext-0.9.11-cp39-cp39-macosx_11_0_arm64.whl",
            "has_sig": false,
            "md5_digest": "9a1cc3d106582a197c8cce0b8f6a7a1d",
            "packagetype": "bdist_wheel",
            "python_version": "cp39",
            "requires_python": ">=3.9",
            "size": 27218349,
            "upload_time": "2024-10-26T07:13:29",
            "upload_time_iso_8601": "2024-10-26T07:13:29.890158Z",
            "url": "https://files.pythonhosted.org/packages/a9/4e/8e1b2d4b7979434dd18664e61f5258eae13a2e8e62ac7ee816e4683c3e7b/chrontext-0.9.11-cp39-cp39-macosx_11_0_arm64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "34f3a2d51afd80f9135ae8151841cad8d7bd19a2db6da685dfb804245cfd91a4",
                "md5": "b051f61f467c3b53de5898795d87d144",
                "sha256": "8145827db4da0a65bf2c93d09ef471f4b78575d343e1038da8b2cf85d919aef3"
            },
            "downloads": -1,
            "filename": "chrontext-0.9.11-cp39-cp39-macosx_12_0_arm64.whl",
            "has_sig": false,
            "md5_digest": "b051f61f467c3b53de5898795d87d144",
            "packagetype": "bdist_wheel",
            "python_version": "cp39",
            "requires_python": ">=3.9",
            "size": 27233878,
            "upload_time": "2024-10-26T06:59:52",
            "upload_time_iso_8601": "2024-10-26T06:59:52.495543Z",
            "url": "https://files.pythonhosted.org/packages/34/f3/a2d51afd80f9135ae8151841cad8d7bd19a2db6da685dfb804245cfd91a4/chrontext-0.9.11-cp39-cp39-macosx_12_0_arm64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "8c4c92f880a879427928b41d7a991d8f1f1375859b39d1bf0a89b821b3d62e8e",
                "md5": "a59f43f76de068c931eb406693f6f845",
                "sha256": "cca797243b2ef2a6b838700df5f1ee568613c7bdcb0e47ae0b573786c525fcf7"
            },
            "downloads": -1,
            "filename": "chrontext-0.9.11-cp39-cp39-manylinux_2_28_x86_64.whl",
            "has_sig": false,
            "md5_digest": "a59f43f76de068c931eb406693f6f845",
            "packagetype": "bdist_wheel",
            "python_version": "cp39",
            "requires_python": ">=3.9",
            "size": 32232169,
            "upload_time": "2024-10-26T07:09:47",
            "upload_time_iso_8601": "2024-10-26T07:09:47.001118Z",
            "url": "https://files.pythonhosted.org/packages/8c/4c/92f880a879427928b41d7a991d8f1f1375859b39d1bf0a89b821b3d62e8e/chrontext-0.9.11-cp39-cp39-manylinux_2_28_x86_64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "83cc70592362fb011bacf0e5e5c8f27b991ef41e9c29f4a521244de2623c0aaf",
                "md5": "5f45e3a2334e522ab86a40d29c3648a5",
                "sha256": "c0654cf493705c028f14e956be42689001b7168922d1bfb12afb8a81ac653bb0"
            },
            "downloads": -1,
            "filename": "chrontext-0.9.11-cp39-none-win_amd64.whl",
            "has_sig": false,
            "md5_digest": "5f45e3a2334e522ab86a40d29c3648a5",
            "packagetype": "bdist_wheel",
            "python_version": "cp39",
            "requires_python": ">=3.9",
            "size": 26756328,
            "upload_time": "2024-10-26T07:21:45",
            "upload_time_iso_8601": "2024-10-26T07:21:45.042363Z",
            "url": "https://files.pythonhosted.org/packages/83/cc/70592362fb011bacf0e5e5c8f27b991ef41e9c29f4a521244de2623c0aaf/chrontext-0.9.11-cp39-none-win_amd64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "fb615e4bafd53698e0902e0e51f1891d66af4275ced2e55d4445475430d63278",
                "md5": "b04d9fe10a87395857eec74d82ae817f",
                "sha256": "1887e3c1d7b916bd4863410509623c188c69ddc6ab913d3f91545c18a0cbf27d"
            },
            "downloads": -1,
            "filename": "chrontext-0.9.11.tar.gz",
            "has_sig": false,
            "md5_digest": "b04d9fe10a87395857eec74d82ae817f",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.9",
            "size": 197826,
            "upload_time": "2024-10-26T07:09:10",
            "upload_time_iso_8601": "2024-10-26T07:09:10.965019Z",
            "url": "https://files.pythonhosted.org/packages/fb/61/5e4bafd53698e0902e0e51f1891d66af4275ced2e55d4445475430d63278/chrontext-0.9.11.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-10-26 07:09:10",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "DataTreehouse",
    "github_project": "chrontext",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "chrontext"
}
        
Elapsed time: 9.57288s