Skip to main content

Hybrid SPARQL query engine for timeseries data

Project description

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

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, and relies heavily on packages from the Oxigraph project. Chrontext works with Apache Arrow, prefers time-series transport using Apache Arrow Flight and delivers results as Polars DataFrames.

Please reach out to Data Treehouse if you would like help trying Chrontext, or require support for a different database backend.

Installing

Chrontext is in pip, just use:

pip install chrontext

The API is documented HERE.

Example query in Python

The code assumes that we have a SPARQL-endpoint and BigQuery set up with time-series.

...
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
=.A1.RG1.TBB1 2018-08-24 12:00:00 UTC 39.74 184.0
=.A1.RG1.TBB1 2018-08-24 12:00:01 UTC 39.57 184.0
=.A1.RG1.TBB1 2018-08-24 12:00:02 UTC 40.1 184.0
=.A1.RG1.TBB1 2018-08-24 12:00:03 UTC 40.05 184.0
=.A1.RG1.TBB1 2018-08-24 12:00:04 UTC 40.02 184.0
=.A5.RG9.TBB1 2018-08-24 12:59:56 UTC 105.5 427.5
=.A5.RG9.TBB1 2018-08-24 12:59:57 UTC 104.9 427.6
=.A5.RG9.TBB1 2018-08-24 12:59:58 UTC 105.6 428.0
=.A5.RG9.TBB1 2018-08-24 12:59:59 UTC 105.9 428.0
=.A5.RG9.TBB1 2018-08-24 13:00:00 UTC 105.7 428.5

API

The API is documented HERE.

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.

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.

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.

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.

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.

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.

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 at OsloMet. To read more about Chrontext, read the article Chrontext: Portable Sparql Queries Over Contextualised Time Series Data in Industrial Settings.

License

All code produced since August 1st. 2023 is copyrighted to Data Treehouse AS with an Apache 2.0 license unless otherwise noted.

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

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

chrontext-0.9.11.tar.gz (197.8 kB view details)

Uploaded Source

Built Distributions

chrontext-0.9.11-cp312-cp312-manylinux_2_28_x86_64.whl (32.2 MB view details)

Uploaded CPython 3.12 manylinux: glibc 2.28+ x86-64

chrontext-0.9.11-cp311-none-win_amd64.whl (26.8 MB view details)

Uploaded CPython 3.11 Windows x86-64

chrontext-0.9.11-cp311-cp311-manylinux_2_28_x86_64.whl (32.2 MB view details)

Uploaded CPython 3.11 manylinux: glibc 2.28+ x86-64

chrontext-0.9.11-cp311-cp311-macosx_12_0_arm64.whl (27.2 MB view details)

Uploaded CPython 3.11 macOS 12.0+ ARM64

chrontext-0.9.11-cp311-cp311-macosx_11_0_arm64.whl (27.2 MB view details)

Uploaded CPython 3.11 macOS 11.0+ ARM64

chrontext-0.9.11-cp310-none-win_amd64.whl (26.8 MB view details)

Uploaded CPython 3.10 Windows x86-64

chrontext-0.9.11-cp310-cp310-manylinux_2_28_x86_64.whl (32.2 MB view details)

Uploaded CPython 3.10 manylinux: glibc 2.28+ x86-64

chrontext-0.9.11-cp310-cp310-macosx_12_0_arm64.whl (27.2 MB view details)

Uploaded CPython 3.10 macOS 12.0+ ARM64

chrontext-0.9.11-cp310-cp310-macosx_11_0_arm64.whl (27.2 MB view details)

Uploaded CPython 3.10 macOS 11.0+ ARM64

chrontext-0.9.11-cp39-none-win_amd64.whl (26.8 MB view details)

Uploaded CPython 3.9 Windows x86-64

chrontext-0.9.11-cp39-cp39-manylinux_2_28_x86_64.whl (32.2 MB view details)

Uploaded CPython 3.9 manylinux: glibc 2.28+ x86-64

chrontext-0.9.11-cp39-cp39-macosx_12_0_arm64.whl (27.2 MB view details)

Uploaded CPython 3.9 macOS 12.0+ ARM64

chrontext-0.9.11-cp39-cp39-macosx_11_0_arm64.whl (27.2 MB view details)

Uploaded CPython 3.9 macOS 11.0+ ARM64

chrontext-0.9.11-cp38-none-win_amd64.whl (26.8 MB view details)

Uploaded CPython 3.8 Windows x86-64

chrontext-0.9.11-cp38-cp38-manylinux_2_28_x86_64.whl (32.2 MB view details)

Uploaded CPython 3.8 manylinux: glibc 2.28+ x86-64

chrontext-0.9.11-cp38-cp38-macosx_12_0_arm64.whl (27.2 MB view details)

Uploaded CPython 3.8 macOS 12.0+ ARM64

chrontext-0.9.11-cp38-cp38-macosx_11_0_arm64.whl (27.2 MB view details)

Uploaded CPython 3.8 macOS 11.0+ ARM64

File details

Details for the file chrontext-0.9.11.tar.gz.

File metadata

  • Download URL: chrontext-0.9.11.tar.gz
  • Upload date:
  • Size: 197.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: maturin/1.7.1

File hashes

Hashes for chrontext-0.9.11.tar.gz
Algorithm Hash digest
SHA256 1887e3c1d7b916bd4863410509623c188c69ddc6ab913d3f91545c18a0cbf27d
MD5 b04d9fe10a87395857eec74d82ae817f
BLAKE2b-256 fb615e4bafd53698e0902e0e51f1891d66af4275ced2e55d4445475430d63278

See more details on using hashes here.

File details

Details for the file chrontext-0.9.11-cp312-cp312-manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for chrontext-0.9.11-cp312-cp312-manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 7ba636e40686ae1de43593be9e8272b49c11cb03cda7e8851b667b46a8fc9ac1
MD5 3c9af852070a54515cab615a862646f1
BLAKE2b-256 f00288b4d27506cd478adeaa1bdd2f4c684e5ba7c9b58ecb2510c876b0620d58

See more details on using hashes here.

File details

Details for the file chrontext-0.9.11-cp311-none-win_amd64.whl.

File metadata

File hashes

Hashes for chrontext-0.9.11-cp311-none-win_amd64.whl
Algorithm Hash digest
SHA256 9a811e84946ebb523d1d0ff0e2f2c74e4e8a9610cbb1b35250d9ab83fa8bc682
MD5 c75c91fdd9cd5c7e64dd9ffd684e0c05
BLAKE2b-256 7dab724ccf98c5505d86c62b7140fa4c38bf9ea707b5f8367dcec371b5102e0b

See more details on using hashes here.

File details

Details for the file chrontext-0.9.11-cp311-cp311-manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for chrontext-0.9.11-cp311-cp311-manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 99635d9b4ed08ce376d0e7a8af6f6af2d25b32a6e2271357ab83a87f2cbba892
MD5 023fbcfafb22a625a7b5f81df364fb6b
BLAKE2b-256 afca3a52f5767d6b296dfa77531fc5e349c8ee4b6cc7432a9f9cf145a6c909d5

See more details on using hashes here.

File details

Details for the file chrontext-0.9.11-cp311-cp311-macosx_12_0_arm64.whl.

File metadata

File hashes

Hashes for chrontext-0.9.11-cp311-cp311-macosx_12_0_arm64.whl
Algorithm Hash digest
SHA256 c62bd44b6f0693df68d54f8a208251e56b8df9c62ea549a83c3d3179b91a71d3
MD5 c62c87ed9c2aa63228ad952bdb4dfe5d
BLAKE2b-256 da7b0729fb2fddf8a333fe4c501e2213c09a8f7fd23d1aa216c99c069ed99c43

See more details on using hashes here.

File details

Details for the file chrontext-0.9.11-cp311-cp311-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for chrontext-0.9.11-cp311-cp311-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 d4b85f0dd1c5a3236ac10d2d7465b9413d3a267158ed4cd6fc61c745a8e153fd
MD5 b74aa5b7793b553d36e8495bb33e47b7
BLAKE2b-256 d4e8e2fcbb6f3cc7ddeaf4b2b3b81ae9b903481e8c0a7b6a04ab8ae1cd4a2182

See more details on using hashes here.

File details

Details for the file chrontext-0.9.11-cp310-none-win_amd64.whl.

File metadata

File hashes

Hashes for chrontext-0.9.11-cp310-none-win_amd64.whl
Algorithm Hash digest
SHA256 cf31ac8032b5401ccf8ec89d1f993de50d04d4a6db437fde8de458d9f1f2260c
MD5 90fa60ec8103a8506528d11c7dcc3229
BLAKE2b-256 89b29d571983b893ec9cf9b93eff6aacd21d2ce07f16b94dbb4ebdaed87af35d

See more details on using hashes here.

File details

Details for the file chrontext-0.9.11-cp310-cp310-manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for chrontext-0.9.11-cp310-cp310-manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 140d81c20a3763272b86c4256d6b30fc9d456a26abf5d87a3a89de3426168202
MD5 9e05d070dd4c151041d06812819d271d
BLAKE2b-256 d474eb85a58b932cdada5094027ef326a593a0db8de78f484a6caf39f8343aff

See more details on using hashes here.

File details

Details for the file chrontext-0.9.11-cp310-cp310-macosx_12_0_arm64.whl.

File metadata

File hashes

Hashes for chrontext-0.9.11-cp310-cp310-macosx_12_0_arm64.whl
Algorithm Hash digest
SHA256 39f9a575cc6ddc9c7b2077259e6e05e6fd117c42b8b58a0857848684eebdd0c4
MD5 eeb7a95503d7ec59f5b2bb2c46e66656
BLAKE2b-256 39d221ce166d56c58384ebf10edb3662b0f25eed9ad6b9e01b5b12073e4ae63d

See more details on using hashes here.

File details

Details for the file chrontext-0.9.11-cp310-cp310-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for chrontext-0.9.11-cp310-cp310-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 6304dd64523a0871de7498e5f682c9007c83ad269f43ebc066105eb31b179adf
MD5 8b488a047b9ba56ee74cc422135cce82
BLAKE2b-256 a760e21c3c6229e0a5f99fb41a3a5f227bd8df03505e4913fc28759f2a5d7180

See more details on using hashes here.

File details

Details for the file chrontext-0.9.11-cp39-none-win_amd64.whl.

File metadata

File hashes

Hashes for chrontext-0.9.11-cp39-none-win_amd64.whl
Algorithm Hash digest
SHA256 c0654cf493705c028f14e956be42689001b7168922d1bfb12afb8a81ac653bb0
MD5 5f45e3a2334e522ab86a40d29c3648a5
BLAKE2b-256 83cc70592362fb011bacf0e5e5c8f27b991ef41e9c29f4a521244de2623c0aaf

See more details on using hashes here.

File details

Details for the file chrontext-0.9.11-cp39-cp39-manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for chrontext-0.9.11-cp39-cp39-manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 cca797243b2ef2a6b838700df5f1ee568613c7bdcb0e47ae0b573786c525fcf7
MD5 a59f43f76de068c931eb406693f6f845
BLAKE2b-256 8c4c92f880a879427928b41d7a991d8f1f1375859b39d1bf0a89b821b3d62e8e

See more details on using hashes here.

File details

Details for the file chrontext-0.9.11-cp39-cp39-macosx_12_0_arm64.whl.

File metadata

File hashes

Hashes for chrontext-0.9.11-cp39-cp39-macosx_12_0_arm64.whl
Algorithm Hash digest
SHA256 8145827db4da0a65bf2c93d09ef471f4b78575d343e1038da8b2cf85d919aef3
MD5 b051f61f467c3b53de5898795d87d144
BLAKE2b-256 34f3a2d51afd80f9135ae8151841cad8d7bd19a2db6da685dfb804245cfd91a4

See more details on using hashes here.

File details

Details for the file chrontext-0.9.11-cp39-cp39-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for chrontext-0.9.11-cp39-cp39-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 e07e69ae1b3d0771d1c41e89ba435daadef653bb96a5d477f9126b0a1cdfd1ce
MD5 9a1cc3d106582a197c8cce0b8f6a7a1d
BLAKE2b-256 a94e8e1b2d4b7979434dd18664e61f5258eae13a2e8e62ac7ee816e4683c3e7b

See more details on using hashes here.

File details

Details for the file chrontext-0.9.11-cp38-none-win_amd64.whl.

File metadata

File hashes

Hashes for chrontext-0.9.11-cp38-none-win_amd64.whl
Algorithm Hash digest
SHA256 321f149f9409ec6a1702081fa638bff8d7b26d62162d590064af246df49285fb
MD5 4bb6358489d30f6c73432d16c3c2b4f3
BLAKE2b-256 57d79b8fe5e9332df34aa9b574c372c79e8d5f5072da89bdc04d68b05c412fe3

See more details on using hashes here.

File details

Details for the file chrontext-0.9.11-cp38-cp38-manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for chrontext-0.9.11-cp38-cp38-manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 d8c3eedd0d470c5e1a71bf3971bb71cfa66685aacbef6076a77c04ac85d13ba5
MD5 5c3b9123237906ec8da250e42afe252f
BLAKE2b-256 39efb9b3cf8f251d46654f4bfc464466b7ef1cf95487a627458dfe7bbe6e4ee2

See more details on using hashes here.

File details

Details for the file chrontext-0.9.11-cp38-cp38-macosx_12_0_arm64.whl.

File metadata

File hashes

Hashes for chrontext-0.9.11-cp38-cp38-macosx_12_0_arm64.whl
Algorithm Hash digest
SHA256 45dbebe8d41897e0948a0e59b9542258925d1c0f0b3d2c6c81154f2080ea7a91
MD5 8329ae3a50cdf50404f225d90c4068b7
BLAKE2b-256 7dd7ef10eb0e05c0817fcca7f059132446fdbdc60a4084df811569dfd71c579c

See more details on using hashes here.

File details

Details for the file chrontext-0.9.11-cp38-cp38-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for chrontext-0.9.11-cp38-cp38-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 06d85ce1426bba650776c245054987a5baba6c813c7be4eb258c9f8f8f35658d
MD5 5c425c2f555dd8f46ee5ce03b560c0d7
BLAKE2b-256 99a757636c8f7a7f108ab265420d9bcd670c0d87640b5b3303a910c2dd7abf72

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page