From 20ba25546762e26af9150e931e5e035c39b6f243 Mon Sep 17 00:00:00 2001 From: karel rehor Date: Fri, 17 Apr 2026 17:03:59 +0200 Subject: [PATCH 01/36] chore: checkpoint 1 refactoring examples --- Examples/README.md | 20 ++ Examples/config.py | 2 +- Examples/core/__init__.py | 0 .../{pokemon-trainer => core}/basic-query.py | 9 +- .../{pokemon-trainer => core}/basic-write.py | 13 +- Examples/file-import/feather_write.py | 39 --- Examples/file-import/json_write.py | 65 ----- Examples/file-import/orc_write.py | 39 --- Examples/file-import/parquet_write.py | 40 ---- .../file-import/write_file_parse_options.py | 42 ---- Examples/jupyter/basic-write-query.ipynb | 222 ++++++++++++++++++ .../csv_write.py => write/fileimport.py} | 42 +++- .../source_data}/out.csv | 0 .../source_data}/out.feather | Bin .../source_data}/out.json | 0 .../source_data}/out.orc | Bin .../source_data}/out.parquet | Bin .../source_data}/out_orig.csv | 0 .../source_data}/out_orig.json | 0 Examples/write/source_data/updater.py | 150 ++++++++++++ 20 files changed, 441 insertions(+), 242 deletions(-) create mode 100644 Examples/README.md create mode 100644 Examples/core/__init__.py rename Examples/{pokemon-trainer => core}/basic-query.py (76%) rename Examples/{pokemon-trainer => core}/basic-write.py (83%) delete mode 100644 Examples/file-import/feather_write.py delete mode 100644 Examples/file-import/json_write.py delete mode 100644 Examples/file-import/orc_write.py delete mode 100644 Examples/file-import/parquet_write.py delete mode 100644 Examples/file-import/write_file_parse_options.py create mode 100644 Examples/jupyter/basic-write-query.ipynb rename Examples/{file-import/csv_write.py => write/fileimport.py} (61%) rename Examples/{file-import => write/source_data}/out.csv (100%) rename Examples/{file-import => write/source_data}/out.feather (100%) rename Examples/{file-import => write/source_data}/out.json (100%) rename Examples/{file-import => write/source_data}/out.orc (100%) rename Examples/{file-import => write/source_data}/out.parquet (100%) rename Examples/{file-import => write/source_data}/out_orig.csv (100%) rename Examples/{file-import => write/source_data}/out_orig.json (100%) create mode 100644 Examples/write/source_data/updater.py diff --git a/Examples/README.md b/Examples/README.md new file mode 100644 index 0000000..654f739 --- /dev/null +++ b/Examples/README.md @@ -0,0 +1,20 @@ +## Infludb3 Python Examples + +First time users will likely want to study the examples in the `./core` directory. Users who work with __Jupyter__ notebooks may want to take a look at `basic-write-query.ipynb` in the `./jupyter` directory. + +### Writing data + +Basic examples can be found in the `write` directory. + +* `fileimport.py` shows how to import data to an Influx database directly from a number of other standard database formats. +TODO - others + +## Refactoring Notes +TODO - delete this section as examples take shape and before creating PR. + +1. Want to remove `pokemon-trainer` and refactor examples to `core`, `write` and `query` +2. Keep `file-import` as single file, with source data updatable - see `updater.py` + 1. Note `feather` type writes data using measurement `machine_data` and not `machine_data_feather` + 2. Note `orc` type writes data using measurement `machine_data` and not `machine_data_orc` + 3. Note `parquet` type writes data using measurement `machine_data` and not `machine_data_parquet` +3. Keep one simple example of jupiter notebook \ No newline at end of file diff --git a/Examples/config.py b/Examples/config.py index 5ab1b49..ab4546e 100644 --- a/Examples/config.py +++ b/Examples/config.py @@ -4,7 +4,7 @@ class Config: def __init__(self): - self.host = os.getenv('INFLUXDB_HOST') or 'https://us-east-1-1.aws.cloud2.influxdata.com/' + self.host = os.getenv('INFLUXDB_HOST') or 'http://localhost:8181' self.token = os.getenv('INFLUXDB_TOKEN') or 'my-token' self.database = os.getenv('INFLUXDB_DATABASE') or 'my-db' diff --git a/Examples/core/__init__.py b/Examples/core/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Examples/pokemon-trainer/basic-query.py b/Examples/core/basic-query.py similarity index 76% rename from Examples/pokemon-trainer/basic-query.py rename to Examples/core/basic-query.py index 08b50ba..fa72366 100644 --- a/Examples/pokemon-trainer/basic-query.py +++ b/Examples/core/basic-query.py @@ -1,9 +1,12 @@ +from Examples.config import Config from influxdb_client_3 import InfluxDBClient3 +config = Config() + client = InfluxDBClient3( - token="", - host="eu-central-1-1.aws.cloud2.influxdata.com", - database="pokemon-codex") + token=config.token, + host=config.host, + database=config.database,) sql = '''SELECT * FROM caught WHERE trainer = 'ash' AND time >= now() - interval '1 hour' LIMIT 5''' table = client.query(query=sql, language='sql', mode='all') diff --git a/Examples/pokemon-trainer/basic-write.py b/Examples/core/basic-write.py similarity index 83% rename from Examples/pokemon-trainer/basic-write.py rename to Examples/core/basic-write.py index ee91797..14f2f96 100644 --- a/Examples/pokemon-trainer/basic-write.py +++ b/Examples/core/basic-write.py @@ -1,11 +1,18 @@ import datetime +from Examples.config import Config from influxdb_client_3 import InfluxDBClient3, Point +#client = InfluxDBClient3( +# token="mGbL-OJ2kxYqvbIL9jQOOg2VJLhf16hh-xn-XJe3RUKrI5cewOAy80L5cVIzG0vh7dLLckZkpYfvExgoMBXLFA==", +# host="eu-central-1-1.aws.cloud2.influxdata.com", +# database="pokemon-codex") + +config = Config() client = InfluxDBClient3( - token="mGbL-OJ2kxYqvbIL9jQOOg2VJLhf16hh-xn-XJe3RUKrI5cewOAy80L5cVIzG0vh7dLLckZkpYfvExgoMBXLFA==", - host="eu-central-1-1.aws.cloud2.influxdata.com", - database="pokemon-codex") + token=config.token, + host=config.host, + database=config.database,) now = datetime.datetime.now(datetime.timezone.utc) diff --git a/Examples/file-import/feather_write.py b/Examples/file-import/feather_write.py deleted file mode 100644 index d931ca9..0000000 --- a/Examples/file-import/feather_write.py +++ /dev/null @@ -1,39 +0,0 @@ -import influxdb_client_3 as InfluxDBClient3 -from influxdb_client_3 import write_client_options, WriteOptions, InfluxDBError - - -class BatchingCallback(object): - - def success(self, conf, data: str): - print(f"Written batch: {conf}, data: {data}") - - def error(self, conf, data: str, exception: InfluxDBError): - print(f"Cannot write batch: {conf}, data: {data} due: {exception}") - - def retry(self, conf, data: str, exception: InfluxDBError): - print(f"Retryable error occurs for batch: {conf}, data: {data} retry: {exception}") - - -callback = BatchingCallback() - -write_options = WriteOptions(batch_size=500, - flush_interval=10_000, - jitter_interval=2_000, - retry_interval=5_000, - max_retries=5, - max_retry_delay=30_000, - exponential_base=2) - -wco = write_client_options(success_callback=callback.success, - error_callback=callback.error, - retry_callback=callback.retry, - write_options=write_options - ) - -with InfluxDBClient3.InfluxDBClient3( - token="INSERT_TOKEN", - host="eu-central-1-1.aws.cloud2.influxdata.com", - database="python", write_client_options=wco) as client: - client.write_file( - file='./out.feather', - timestamp_column='time', tag_columns=["provider", "machineID"]) diff --git a/Examples/file-import/json_write.py b/Examples/file-import/json_write.py deleted file mode 100644 index c259bd3..0000000 --- a/Examples/file-import/json_write.py +++ /dev/null @@ -1,65 +0,0 @@ -import logging -import influxdb_client_3 as InfluxDBClient3 -from influxdb_client_3 import write_client_options, WriteOptions, InfluxDBError - - -class BatchingCallback(object): - - def __init__(self): - self.write_count = 0 - - def success(self, conf, data: str): - self.write_count += 1 - print(f"Written batch: {conf}, data: {data}") - - def error(self, conf, data: str, exception: InfluxDBError): - print(f"Cannot write batch: {conf}, data: {data} due: {exception}") - - def retry(self, conf, data: str, exception: InfluxDBError): - print(f"Retryable error occurs for batch: {conf}, data: {data} retry: {exception}") - - -def main() -> None: - - # allow detailed inspection - logging.basicConfig(level=logging.DEBUG) - - callback = BatchingCallback() - - write_options = WriteOptions(batch_size=100, - flush_interval=10_000, - jitter_interval=2_000, - retry_interval=5_000, - max_retries=5, - max_retry_delay=30_000, - exponential_base=2) - - wco = write_client_options(success_callback=callback.success, - error_callback=callback.error, - retry_callback=callback.retry, - write_options=write_options - ) - """ - token: access token generated in cloud - host: ATTN could be another AWS region or even another cloud provider - database: should have retention policy 'forever' to handle older sample data timestamps - write_client_options: see above - debug: allows low-level inspection of communications and context-manager termination - """ - with InfluxDBClient3.InfluxDBClient3( - token="INSERT_TOKEN", - host="https://us-east-1-1.aws.cloud2.influxdata.com/", - database="example_data_forever", - write_client_options=wco, - debug=True) as client: - client.write_file( - file='./out.json', - timestamp_column='time', - tag_columns=["provider", "machineID"], - date_unit='ns') - - print(f"DONE writing from json in {callback.write_count} batch(es)") - - -if __name__ == "__main__": - main() diff --git a/Examples/file-import/orc_write.py b/Examples/file-import/orc_write.py deleted file mode 100644 index 67581c5..0000000 --- a/Examples/file-import/orc_write.py +++ /dev/null @@ -1,39 +0,0 @@ -import influxdb_client_3 as InfluxDBClient3 -from influxdb_client_3 import write_client_options, WriteOptions, InfluxDBError - - -class BatchingCallback(object): - - def success(self, conf, data: str): - print(f"Written batch: {conf}, data: {data}") - - def error(self, conf, data: str, exception: InfluxDBError): - print(f"Cannot write batch: {conf}, data: {data} due: {exception}") - - def retry(self, conf, data: str, exception: InfluxDBError): - print(f"Retryable error occurs for batch: {conf}, data: {data} retry: {exception}") - - -callback = BatchingCallback() - -write_options = WriteOptions(batch_size=500, - flush_interval=10_000, - jitter_interval=2_000, - retry_interval=5_000, - max_retries=5, - max_retry_delay=30_000, - exponential_base=2) - -wco = write_client_options(success_callback=callback.success, - error_callback=callback.error, - retry_callback=callback.retry, - write_options=write_options - ) - -with InfluxDBClient3.InfluxDBClient3( - token="INSERT_TOKEN", - host="eu-central-1-1.aws.cloud2.influxdata.com", - database="python") as client: - client.write_file( - file='./out.orc', - timestamp_column='time', tag_columns=["provider", "machineID"]) diff --git a/Examples/file-import/parquet_write.py b/Examples/file-import/parquet_write.py deleted file mode 100644 index b7a3c70..0000000 --- a/Examples/file-import/parquet_write.py +++ /dev/null @@ -1,40 +0,0 @@ -import influxdb_client_3 as InfluxDBClient3 -from influxdb_client_3 import write_client_options, WriteOptions, InfluxDBError - - -class BatchingCallback(object): - - def success(self, conf, data: str): - print(f"Written batch: {conf}, data: {data}") - - def error(self, conf, data: str, exception: InfluxDBError): - print(f"Cannot write batch: {conf}, data: {data} due: {exception}") - - def retry(self, conf, data: str, exception: InfluxDBError): - print(f"Retryable error occurs for batch: {conf}, data: {data} retry: {exception}") - - -callback = BatchingCallback() - -write_options = WriteOptions(batch_size=500, - flush_interval=10_000, - jitter_interval=2_000, - retry_interval=5_000, - max_retries=5, - max_retry_delay=30_000, - exponential_base=2) - -wco = write_client_options(success_callback=callback.success, - error_callback=callback.error, - retry_callback=callback.retry, - write_options=write_options - ) - -with InfluxDBClient3.InfluxDBClient3( - token="INSERT_TOKEN", - host="eu-central-1-1.aws.cloud2.influxdata.com", - database="python", - write_client_options=wco) as client: - client.write_file( - file='./out.parquet', - timestamp_column='time', tag_columns=["provider", "machineID"]) diff --git a/Examples/file-import/write_file_parse_options.py b/Examples/file-import/write_file_parse_options.py deleted file mode 100644 index 282a9cc..0000000 --- a/Examples/file-import/write_file_parse_options.py +++ /dev/null @@ -1,42 +0,0 @@ -import influxdb_client_3 as InfluxDBClient3 -from influxdb_client_3 import write_client_options, WriteOptions, InfluxDBError, file_parser_options - - -class BatchingCallback(object): - - def success(self, conf, data: str): - print(f"Written batch: {conf}, data: {data}") - - def error(self, conf, data: str, exception: InfluxDBError): - print(f"Cannot write batch: {conf}, data: {data} due: {exception}") - - def retry(self, conf, data: str, exception: InfluxDBError): - print(f"Retryable error occurs for batch: {conf}, data: {data} retry: {exception}") - - -callback = BatchingCallback() - -write_options = WriteOptions(batch_size=500, - flush_interval=10_000, - jitter_interval=2_000, - retry_interval=5_000, - max_retries=5, - max_retry_delay=30_000, - exponential_base=2) - -wco = write_client_options(success_callback=callback.success, - error_callback=callback.error, - retry_callback=callback.retry, - write_options=write_options - ) - -with InfluxDBClient3.InfluxDBClient3( - token="", - host="eu-central-1-1.aws.cloud2.influxdata.com", - database="python", write_client_options=wco) as client: - fpo = file_parser_options(columns=["time", "machineID", "vibration"]) - - client.write_file( - file='./out.parquet', - timestamp_column='time', tag_columns=["provider", "machineID"], measurement_name='machine_data', - file_parser_options=fpo) diff --git a/Examples/jupyter/basic-write-query.ipynb b/Examples/jupyter/basic-write-query.ipynb new file mode 100644 index 0000000..1994e78 --- /dev/null +++ b/Examples/jupyter/basic-write-query.ipynb @@ -0,0 +1,222 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "ba2e56cae6ed4007", + "metadata": {}, + "source": [ + "## Influxdb3 Python basic usage\n", + "\n", + "This jupyter notebook illustrates the basics of writing and querying data from an Influxdb3 database using the Influxdb3 python client. Code cells need to be run step by step in the order in which they are presented.\n", + "\n", + "1. Start by setting up the basic connection values that match your server or account." + ] + }, + { + "cell_type": "code", + "id": "7df36b5af8ac161e", + "metadata": {}, + "source": [ + "%env INFLUXDB_HOST=http://localhost:8181\n", + "%env INFLUXDB_TOKEN=apiv3_j0S3Nhx2DW1Lo2pyM7wHKlCUz315z4cEGOBORrPMF8GKXFq8sVfBf9AyQu1EYB4IVSu9jCjwmKHhpB-vsr_HUw\n", + "%env INFLUXDB_DATABASE=my_db\n", + "from influxdb_client_3 import InfluxDBClient3, Point, WritePrecision, InfluxDBError, WriteOptions, write_client_options" + ], + "outputs": [], + "execution_count": null + }, + { + "cell_type": "markdown", + "id": "95bc0c8ac612b91a", + "metadata": {}, + "source": "2. Next, setup a basic sensor class for generating test data. This simple class will generate readings using the built-in Influxdb3 Python `Point` class. Using the `Point` class for writes is recommended in basic applications even though the client `write()` method handles other types of data." + }, + { + "cell_type": "code", + "id": "532b67ce851fe031", + "metadata": {}, + "source": [ + "class Sensor:\n", + "\n", + " def __init__(self, location, model, id):\n", + " self._location = location\n", + " self._model = model\n", + " self._id = id\n", + "\n", + " def point_reading(self, _measurement, temperature, humidity, pressure, timestamp) -> Point:\n", + " return (Point(_measurement)\n", + " .tag(\"location\", self._location)\n", + " .tag(\"model\", self._model)\n", + " .tag(\"id\", self._id)\n", + " .field(\"temperature\", temperature)\n", + " .field(\"humidity\", humidity)\n", + " .field(\"pressure\", pressure)\n", + " .time(timestamp)\n", + " )\n" + ], + "outputs": [], + "execution_count": null + }, + { + "cell_type": "markdown", + "id": "40ac0ffee0bcaefa", + "metadata": {}, + "source": [ + "3. Create a client instance. Please note that this client is instantiated using default values only. WriteOptions and QueryOptions can also be added at this step. For simplicity of illustration they have been omitted." + ] + }, + { + "cell_type": "code", + "id": "fefbdd206fedf89f", + "metadata": {}, + "source": [ + "import logging\n", + "import os\n", + "\n", + "logging.basicConfig(level=logging.INFO)\n", + "\n", + "logging.info(\"Using\\n\"\n", + " \"INFLUXDB_HOST={}\\n\"\n", + " \"INFLUXDB_DATABASE={}\".format(os.environ[\"INFLUXDB_HOST\"], os.environ[\"INFLUXDB_DATABASE\"]))\n", + "\n", + "client = InfluxDBClient3(\n", + " host= os.environ[\"INFLUXDB_HOST\"],\n", + " token=os.environ[\"INFLUXDB_TOKEN\"],\n", + " database=os.environ[\"INFLUXDB_DATABASE\"]\n", + ")\n", + "\n", + "logging.info(\"Have client {}\".format(client))\n" + ], + "outputs": [], + "execution_count": null + }, + { + "cell_type": "markdown", + "id": "64228a6a65db2dfc", + "metadata": {}, + "source": [ + "4. Generate data points and write them to the database." + ] + }, + { + "cell_type": "code", + "id": "1613427c088c1a56", + "metadata": {}, + "source": [ + "import random\n", + "from datetime import datetime, timezone, timedelta\n", + "\n", + "sensors = [Sensor(\"entry_hall\", \"R2D2\", \"a2026001\"),\n", + " Sensor(\"conf01\", \"C3PO\", \"a2026002\"),\n", + " Sensor(\"conf02\", \"R2D2\", \"a2026004\"),\n", + " Sensor(\"hall_west\", \"C3PO\", \"a2026005\"),\n", + " Sensor(\"hall_east\", \"ROBBIE\", \"b2025255\"),\n", + " ]\n", + "\n", + "measurement = \"sensor_test\"\n", + "samplesize = 1000\n", + "interval = 10 # seconds\n", + "\n", + "now = datetime.now(timezone.utc)\n", + "ts = now - timedelta(seconds=(interval * samplesize))\n", + "\n", + "data = []\n", + "\n", + "while ts < now:\n", + " for sensor in sensors:\n", + " data.append(\n", + " sensor.point_reading(\n", + " _measurement=measurement,\n", + " temperature=random.uniform(10.0,35.0),\n", + " humidity=random.uniform(50.0,100.0),\n", + " pressure=random.uniform(26.0,32.0),\n", + " timestamp=ts\n", + " )\n", + " )\n", + " ts = ts + timedelta(seconds=interval)\n", + "try:\n", + " client.write(data)\n", + " logging.info(f\"Write successful!\")\n", + "except InfluxDBError as e:\n", + " logging.error(\"InfluxDB error: {}\".format(e))\n" + ], + "outputs": [], + "execution_count": null + }, + { + "cell_type": "markdown", + "id": "3422e2abc73a174", + "metadata": {}, + "source": [ + "Now query" + ] + }, + { + "cell_type": "code", + "id": "8a2fb254614ccc27", + "metadata": {}, + "source": [ + "sql = f\"SELECT time, location, model, temperature FROM {measurement} WHERE location = 'hall_east' ORDER BY time DESC\"\n", + "\n", + "table = []\n", + "\n", + "try:\n", + " table = client.query(query=sql, language=\"sql\", mode=\"pandas\")\n", + " print(table)\n", + "except InfluxDBError as e:\n", + " print(\"InfluxDB error: {}\".format(e))" + ], + "outputs": [], + "execution_count": null + }, + { + "cell_type": "markdown", + "id": "1788e61d03b7f8c5", + "metadata": {}, + "source": [ + "Sample results plot below." + ] + }, + { + "cell_type": "code", + "id": "b56ff2f6e600ed01", + "metadata": {}, + "source": [ + "%matplotlib inline\n", + "import matplotlib.pyplot as plt\n", + "\n", + "temps = table.get(\"temperature\")\n", + "times = table.get(\"time\")\n", + "\n", + "plt.plot(times, temps)\n", + "plt.title(f\"Temperature in hall_east\")\n", + "plt.xlabel(\"Time\")\n", + "plt.ylabel(\"Temperature\")\n", + "plt.show()" + ], + "outputs": [], + "execution_count": null + } + ], + "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.12.0" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Examples/file-import/csv_write.py b/Examples/write/fileimport.py similarity index 61% rename from Examples/file-import/csv_write.py rename to Examples/write/fileimport.py index 7928d30..eb51656 100644 --- a/Examples/file-import/csv_write.py +++ b/Examples/write/fileimport.py @@ -1,7 +1,12 @@ import logging +import os + import influxdb_client_3 as InfluxDBClient3 -from influxdb_client_3 import write_client_options, WriteOptions, InfluxDBError +from influxdb_client_3 import write_client_options, WriteOptions, InfluxDBError, file_parser_options + +from Examples.config import Config +data_types = ["csv", "json", "feather", "orc", "parquet"] class BatchingCallback(object): @@ -19,9 +24,11 @@ def retry(self, conf, data: str, exception: InfluxDBError): print(f"Retryable error occurs for batch: {conf}, data: {data} retry: {exception}") -def main() -> None: +def main(file_types=("csv",)) -> None: # allow detailed inspection + if file_types is None: + file_types = ["csv"] logging.basicConfig(level=logging.DEBUG) callback = BatchingCallback() @@ -47,18 +54,33 @@ def main() -> None: write_client_options: see above debug: allows low-level inspection of communications and context-manager termination """ + config = Config() + with InfluxDBClient3.InfluxDBClient3( - token="INSERT_TOKEN", - host="https://us-east-1-1.aws.cloud2.influxdata.com/", - database="example_data_forever", + token=config.token, + host=config.host, + database=config.database, write_client_options=wco, debug=True) as client: - client.write_file( - file='./out.csv', - timestamp_column='time', tag_columns=["provider", "machineID"]) - print(f'DONE writing from csv in {callback.write_count} batch(es)') + for type in file_types: + if not type in data_types: + logging.error(f"File type {type} not supported.") + continue + + logging.info(f"Writing from file of type: {type}") + source_file = f"./source_data/out_update.{type}" + if not (os.path.exists(source_file) and os.path.isfile(source_file)): + logging.error(f"Source file {source_file} not found.") + logging.error(" TIP!: Perhaps source_data/updater.py needs to be run.") + continue + # write data from file + client.write_file( + file=source_file, + timestamp_column='time', tag_columns=["provider", "machineID"]) + + print(f'DONE writing from {file_types} in {callback.write_count} batch(es)') if __name__ == "__main__": - main() + main(("feather","parquet","orc","csv","json")) diff --git a/Examples/file-import/out.csv b/Examples/write/source_data/out.csv similarity index 100% rename from Examples/file-import/out.csv rename to Examples/write/source_data/out.csv diff --git a/Examples/file-import/out.feather b/Examples/write/source_data/out.feather similarity index 100% rename from Examples/file-import/out.feather rename to Examples/write/source_data/out.feather diff --git a/Examples/file-import/out.json b/Examples/write/source_data/out.json similarity index 100% rename from Examples/file-import/out.json rename to Examples/write/source_data/out.json diff --git a/Examples/file-import/out.orc b/Examples/write/source_data/out.orc similarity index 100% rename from Examples/file-import/out.orc rename to Examples/write/source_data/out.orc diff --git a/Examples/file-import/out.parquet b/Examples/write/source_data/out.parquet similarity index 100% rename from Examples/file-import/out.parquet rename to Examples/write/source_data/out.parquet diff --git a/Examples/file-import/out_orig.csv b/Examples/write/source_data/out_orig.csv similarity index 100% rename from Examples/file-import/out_orig.csv rename to Examples/write/source_data/out_orig.csv diff --git a/Examples/file-import/out_orig.json b/Examples/write/source_data/out_orig.json similarity index 100% rename from Examples/file-import/out_orig.json rename to Examples/write/source_data/out_orig.json diff --git a/Examples/write/source_data/updater.py b/Examples/write/source_data/updater.py new file mode 100644 index 0000000..c936597 --- /dev/null +++ b/Examples/write/source_data/updater.py @@ -0,0 +1,150 @@ +import pandas as pd +import pyarrow.feather as feather +import time +from datetime import datetime, timedelta +import logging +import random +import numpy as np +import pyarrow as pa +import pyarrow.compute as pac + +# TODO output as orc, json, csv etc. + +def update_measurement_name(source: pd.DataFrame, measurment_name: str): + count = 0 + for _ in source["iox::measurement"]: + source.loc[count, "iox::measurement"] = measurment_name + count += 1 + +def update_timestamps(source: pd.DataFrame): + + # now = datetime.now() + now = time.time_ns(); + interval = 333_000_000 # ms + grit = random.randrange(0, 1_000_000) + interval = interval + grit + # current = now - (timedelta(milliseconds=interval) * len(source["time"])) + current = np.int64(now - interval * len(source["time"])) + print(f"DEBUG source['time'] {source['time'][0]} {type(source['time'][0]).__name__}") + print(f"DEBUG source[\"time\"].dtype {source["time"].dtype}") + ts_type = type(source['time'][0]).__name__ + + count = 0 + for _ in source['time']: + if ts_type in ["int", "int32", "int64"]: + #print(f"current {current} as type {type(current).__name__}") + source.loc[count, "time"] = current + # source.at[count, "time"] = current + # source.at[count, "time"].astype('int64') + #print(f"DEBUG source.loc[count, \"time\"] {source.loc[count, "time"]} {type(source.loc[count, "time"]).__name__}") + # count += 1 + else: + # ts = pd.Timestamp(current) + # print(f"{data} {type(data)} ts {ts}") + source.loc[count, "time"] = pd.Timestamp(current) + count += 1 + current = current + interval + + if ts_type in ["int", "int32", "int64"]: + source["time"] = source["time"].astype("int64") + +def feather_update(): + logging.info("Updating feather") + f_df = pd.read_feather('./out.feather') + update_timestamps(f_df) + update_measurement_name(f_df, "machine_data_feather") + f_df.to_feather('./out_update.feather') + +def orc_update(): + logging.info("Updating orc") + o_df = pd.read_orc('./out.orc') + update_timestamps(o_df) + update_measurement_name(o_df, "machine_data_orc") + o_df.to_orc('./out_update.orc', index=False) + +def parquet_update(): + logging.info("Updating parquet") + p_df = pd.read_parquet('./out.parquet') + update_timestamps(p_df) + update_measurement_name(p_df, "machine_data_parquet") + p_df.to_parquet('./out_update.parquet', index=False) + +def csv_update(): + logging.info("Updating csv") + csv_df = pd.read_csv('./out.csv') + update_timestamps(csv_df) + csv_df.to_csv('./out_update.csv', index=False) + +def json_update(): + logging.info("Updating json") + json_df = pd.read_json('./out.json') + update_timestamps(json_df) + print(f"DEBUG json_df[\"time\"][0] {json_df['time'][0]} as type {type(json_df['time'][0])}") + json_df.to_json('./out_update.json', orient='records', index=False) + +def explore(): + logging.info("Explore") + read_frame = feather.read_feather('./out.feather') + # print(f"DEBUG read_table:\n{read_table}") + c = len(read_frame["time"]) + print(f"DEBUG c: {c} type: {type(c)} \n") + print(f"DEBUG read_frame length: {read_frame.count()} type: {type(read_frame.count())}") + print(f"DEBUG read_frame:\n{read_frame}") + + now = datetime.now() + interval = 334 # ms + # current = now - (timedelta(milliseconds=interval) * read_frame.count()) + current = now - (timedelta(milliseconds=interval) * len(read_frame["time"])) + + count = 0 + # new_ts = [] + for data in read_frame['time']: + print(f"{data} {type(data)} current {current}") + read_frame.loc[count, "time"] = current + # read_frame['time'][count] = data + # new_ts.append(current) + count += 1 + current = current + timedelta(milliseconds=interval) + + # for data in read_frame['time']: + # print(f"{data}" ) + + print(f"DEBUG read_frame:\n{read_frame}") + + # as feather + # feather.write_feather(read_frame, './out_test.feather') + read_frame.to_feather('./out_test.feather') + + # as csv + count = 0 + for _ in read_frame['iox::measurement']: + read_frame.loc[count, "iox::measurement"] = "machine_data_csv" + count += 1 + + read_frame.to_csv('./out_test.csv', columns=[ + "iox::measurement","time","host","load","machineID","power","provider","temperature","topic","vibration" + ], index=False) + + # as json + read_frame.to_json('./out_test.json', orient='records',) + # test_frame = read_frame.pivot(index='iox::measurement', columns='time', values=[ + # 'iox::measurement','host','load','machineID','power','provider','temperature','topic','vibration' + # ]) + # test_frame = pd.pivot_table(read_frame,values="iox::measurement", index="time", aggfunc=None) + + # print(f"DEBUG test_frame:\n{test_frame}") + # test_frame.to_json('./out_test3.json') + + # as orc + read_frame.to_orc('./out_test.orc') + + test_frame = pd.read_csv('./out.csv') + print(f"DEBUG test_frame:\n{test_frame}") + +if __name__ == "__main__": + logging.basicConfig(level=logging.INFO) + feather_update() + orc_update() + parquet_update() + csv_update() + json_update() From b110dba6fcc1d51ac23eb617ed45f7a94c0a8bb1 Mon Sep 17 00:00:00 2001 From: karel rehor Date: Fri, 17 Apr 2026 17:18:13 +0200 Subject: [PATCH 02/36] chore: remove unneeded dry-run data files. --- Examples/write/source_data/out_orig.csv | 163 ----------------------- Examples/write/source_data/out_orig.json | 1 - 2 files changed, 164 deletions(-) delete mode 100644 Examples/write/source_data/out_orig.csv delete mode 100644 Examples/write/source_data/out_orig.json diff --git a/Examples/write/source_data/out_orig.csv b/Examples/write/source_data/out_orig.csv deleted file mode 100644 index cb508b1..0000000 --- a/Examples/write/source_data/out_orig.csv +++ /dev/null @@ -1,163 +0,0 @@ -iox::measurement,time,host,load,machineID,power,provider,temperature,topic,vibration -machine_data,2023-06-02 16:42:42.317550714,491cc20bbe13,120.0,machine3,301.0,Smith Group,80.0,machine/machine3,310.0 -machine_data,2023-06-02 16:42:42.317757560,491cc20bbe13,23.0,machine1,183.0,"Morales, Robinson and Newman",31.0,machine/machine1,78.0 -machine_data,2023-06-02 16:42:42.317787898,491cc20bbe13,13.0,machine2,198.0,Thomas and Sons,34.0,machine/machine2,73.0 -machine_data,2023-06-02 16:42:43.318452979,491cc20bbe13,120.0,machine3,307.0,Smith Group,81.0,machine/machine3,332.0 -machine_data,2023-06-02 16:42:43.318733897,491cc20bbe13,23.0,machine1,191.0,"Morales, Robinson and Newman",30.0,machine/machine1,53.0 -machine_data,2023-06-02 16:42:43.318895072,491cc20bbe13,13.0,machine2,197.0,Thomas and Sons,31.0,machine/machine2,56.0 -machine_data,2023-06-02 16:42:44.319014820,491cc20bbe13,120.0,machine3,302.0,Smith Group,87.0,machine/machine3,406.0 -machine_data,2023-06-02 16:42:44.319289702,491cc20bbe13,23.0,machine1,194.0,"Morales, Robinson and Newman",31.0,machine/machine1,65.0 -machine_data,2023-06-02 16:42:44.319743319,491cc20bbe13,13.0,machine2,192.0,Thomas and Sons,34.0,machine/machine2,64.0 -machine_data,2023-06-02 16:42:45.319492285,491cc20bbe13,120.0,machine3,312.0,Smith Group,83.0,machine/machine3,460.0 -machine_data,2023-06-02 16:42:45.319953921,491cc20bbe13,23.0,machine1,199.0,"Morales, Robinson and Newman",29.0,machine/machine1,69.0 -machine_data,2023-06-02 16:42:45.319985284,491cc20bbe13,13.0,machine2,188.0,Thomas and Sons,29.0,machine/machine2,72.0 -machine_data,2023-06-02 16:42:46.319917712,491cc20bbe13,120.0,machine3,317.0,Smith Group,90.0,machine/machine3,363.0 -machine_data,2023-06-02 16:42:46.320373453,491cc20bbe13,23.0,machine1,183.0,"Morales, Robinson and Newman",34.0,machine/machine1,69.0 -machine_data,2023-06-02 16:42:46.320760737,491cc20bbe13,13.0,machine2,184.0,Thomas and Sons,31.0,machine/machine2,78.0 -machine_data,2023-06-02 16:42:47.320548979,491cc20bbe13,120.0,machine3,303.0,Smith Group,80.0,machine/machine3,419.0 -machine_data,2023-06-02 16:42:47.320966397,491cc20bbe13,23.0,machine1,181.0,"Morales, Robinson and Newman",30.0,machine/machine1,71.0 -machine_data,2023-06-02 16:42:47.321006298,491cc20bbe13,13.0,machine2,186.0,Thomas and Sons,31.0,machine/machine2,60.0 -machine_data,2023-06-02 16:42:48.321265346,491cc20bbe13,120.0,machine3,305.0,Smith Group,89.0,machine/machine3,367.0 -machine_data,2023-06-02 16:42:48.321382477,491cc20bbe13,23.0,machine1,197.0,"Morales, Robinson and Newman",33.0,machine/machine1,54.0 -machine_data,2023-06-02 16:42:48.321535645,491cc20bbe13,13.0,machine2,195.0,Thomas and Sons,30.0,machine/machine2,68.0 -machine_data,2023-06-02 16:42:49.321847629,491cc20bbe13,120.0,machine3,317.0,Smith Group,86.0,machine/machine3,334.0 -machine_data,2023-06-02 16:42:49.322119417,491cc20bbe13,13.0,machine2,188.0,Thomas and Sons,29.0,machine/machine2,71.0 -machine_data,2023-06-02 16:42:49.322147172,491cc20bbe13,23.0,machine1,184.0,"Morales, Robinson and Newman",34.0,machine/machine1,71.0 -machine_data,2023-06-02 16:42:50.322698923,491cc20bbe13,120.0,machine3,302.0,Smith Group,87.0,machine/machine3,407.0 -machine_data,2023-06-02 16:42:50.323173071,491cc20bbe13,23.0,machine1,193.0,"Morales, Robinson and Newman",32.0,machine/machine1,59.0 -machine_data,2023-06-02 16:42:50.323237285,491cc20bbe13,13.0,machine2,182.0,Thomas and Sons,30.0,machine/machine2,72.0 -machine_data,2023-06-02 16:42:51.323193568,491cc20bbe13,120.0,machine3,303.0,Smith Group,90.0,machine/machine3,343.0 -machine_data,2023-06-02 16:42:51.323662012,491cc20bbe13,13.0,machine2,188.0,Thomas and Sons,31.0,machine/machine2,67.0 -machine_data,2023-06-02 16:42:51.323725850,491cc20bbe13,23.0,machine1,187.0,"Morales, Robinson and Newman",30.0,machine/machine1,69.0 -machine_data,2023-06-02 16:42:52.323837298,491cc20bbe13,120.0,machine3,319.0,Smith Group,89.0,machine/machine3,406.0 -machine_data,2023-06-02 16:42:52.324179952,491cc20bbe13,23.0,machine1,192.0,"Morales, Robinson and Newman",31.0,machine/machine1,65.0 -machine_data,2023-06-02 16:42:52.324204211,491cc20bbe13,13.0,machine2,193.0,Thomas and Sons,34.0,machine/machine2,61.0 -machine_data,2023-06-02 16:42:53.324457761,491cc20bbe13,120.0,machine3,306.0,Smith Group,81.0,machine/machine3,432.0 -machine_data,2023-06-02 16:42:53.324791324,491cc20bbe13,23.0,machine1,196.0,"Morales, Robinson and Newman",30.0,machine/machine1,59.0 -machine_data,2023-06-02 16:42:53.324822237,491cc20bbe13,13.0,machine2,184.0,Thomas and Sons,30.0,machine/machine2,62.0 -machine_data,2023-06-02 16:42:54.324940162,491cc20bbe13,120.0,machine3,319.0,Smith Group,87.0,machine/machine3,473.0 -machine_data,2023-06-02 16:42:54.325470257,491cc20bbe13,23.0,machine1,183.0,"Morales, Robinson and Newman",29.0,machine/machine1,69.0 -machine_data,2023-06-02 16:42:54.325535111,491cc20bbe13,13.0,machine2,184.0,Thomas and Sons,34.0,machine/machine2,72.0 -machine_data,2023-06-02 16:42:55.325452588,491cc20bbe13,120.0,machine3,308.0,Smith Group,88.0,machine/machine3,478.0 -machine_data,2023-06-02 16:42:55.326241988,491cc20bbe13,23.0,machine1,180.0,"Morales, Robinson and Newman",32.0,machine/machine1,67.0 -machine_data,2023-06-02 16:42:55.326458954,491cc20bbe13,13.0,machine2,181.0,Thomas and Sons,34.0,machine/machine2,53.0 -machine_data,2023-06-02 16:42:56.325962575,491cc20bbe13,120.0,machine3,301.0,Smith Group,89.0,machine/machine3,366.0 -machine_data,2023-06-02 16:42:56.326547488,491cc20bbe13,23.0,machine1,194.0,"Morales, Robinson and Newman",33.0,machine/machine1,77.0 -machine_data,2023-06-02 16:42:56.326817078,491cc20bbe13,13.0,machine2,182.0,Thomas and Sons,33.0,machine/machine2,54.0 -machine_data,2023-06-02 16:42:57.326651847,491cc20bbe13,120.0,machine3,316.0,Smith Group,82.0,machine/machine3,446.0 -machine_data,2023-06-02 16:42:57.326903277,491cc20bbe13,23.0,machine1,186.0,"Morales, Robinson and Newman",32.0,machine/machine1,57.0 -machine_data,2023-06-02 16:42:57.327169455,491cc20bbe13,13.0,machine2,183.0,Thomas and Sons,33.0,machine/machine2,50.0 -machine_data,2023-06-02 16:42:58.327381372,491cc20bbe13,120.0,machine3,303.0,Smith Group,83.0,machine/machine3,329.0 -machine_data,2023-06-02 16:42:58.327785814,491cc20bbe13,13.0,machine2,189.0,Thomas and Sons,31.0,machine/machine2,54.0 -machine_data,2023-06-02 16:42:58.328075202,491cc20bbe13,23.0,machine1,184.0,"Morales, Robinson and Newman",34.0,machine/machine1,67.0 -machine_data,2023-06-02 16:42:59.328107312,491cc20bbe13,120.0,machine3,300.0,Smith Group,90.0,machine/machine3,446.0 -machine_data,2023-06-02 16:42:59.328697178,491cc20bbe13,13.0,machine2,196.0,Thomas and Sons,32.0,machine/machine2,51.0 -machine_data,2023-06-02 16:42:59.328842456,491cc20bbe13,23.0,machine1,183.0,"Morales, Robinson and Newman",30.0,machine/machine1,56.0 -machine_data,2023-06-02 16:43:00.328689987,491cc20bbe13,120.0,machine3,300.0,Smith Group,81.0,machine/machine3,460.0 -machine_data,2023-06-02 16:43:00.328730602,491cc20bbe13,13.0,machine2,180.0,Thomas and Sons,34.0,machine/machine2,57.0 -machine_data,2023-06-02 16:43:00.329187607,491cc20bbe13,23.0,machine1,187.0,"Morales, Robinson and Newman",33.0,machine/machine1,70.0 -machine_data,2023-06-02 16:43:01.329623051,491cc20bbe13,13.0,machine2,194.0,Thomas and Sons,33.0,machine/machine2,79.0 -machine_data,2023-06-02 16:43:01.329737661,491cc20bbe13,120.0,machine3,314.0,Smith Group,85.0,machine/machine3,441.0 -machine_data,2023-06-02 16:43:01.329994500,491cc20bbe13,23.0,machine1,196.0,"Morales, Robinson and Newman",34.0,machine/machine1,70.0 -machine_data,2023-06-02 16:43:02.330561375,491cc20bbe13,13.0,machine2,182.0,Thomas and Sons,31.0,machine/machine2,66.0 -machine_data,2023-06-02 16:43:02.330611344,491cc20bbe13,120.0,machine3,304.0,Smith Group,89.0,machine/machine3,455.0 -machine_data,2023-06-02 16:43:02.330943808,491cc20bbe13,23.0,machine1,186.0,"Morales, Robinson and Newman",29.0,machine/machine1,77.0 -machine_data,2023-06-02 16:43:03.331222016,491cc20bbe13,120.0,machine3,319.0,Smith Group,90.0,machine/machine3,459.0 -machine_data,2023-06-02 16:43:03.331373671,491cc20bbe13,23.0,machine1,196.0,"Morales, Robinson and Newman",29.0,machine/machine1,60.0 -machine_data,2023-06-02 16:43:03.331578703,491cc20bbe13,13.0,machine2,195.0,Thomas and Sons,34.0,machine/machine2,60.0 -machine_data,2023-06-02 16:43:04.331723999,491cc20bbe13,120.0,machine3,311.0,Smith Group,80.0,machine/machine3,493.0 -machine_data,2023-06-02 16:43:04.332158179,491cc20bbe13,23.0,machine1,198.0,"Morales, Robinson and Newman",31.0,machine/machine1,71.0 -machine_data,2023-06-02 16:43:04.332222502,491cc20bbe13,13.0,machine2,188.0,Thomas and Sons,34.0,machine/machine2,72.0 -machine_data,2023-06-02 16:43:05.332522723,491cc20bbe13,120.0,machine3,305.0,Smith Group,89.0,machine/machine3,442.0 -machine_data,2023-06-02 16:43:05.332606103,491cc20bbe13,23.0,machine1,189.0,"Morales, Robinson and Newman",33.0,machine/machine1,71.0 -machine_data,2023-06-02 16:43:05.332956871,491cc20bbe13,13.0,machine2,191.0,Thomas and Sons,34.0,machine/machine2,76.0 -machine_data,2023-06-02 16:43:06.332934234,491cc20bbe13,120.0,machine3,313.0,Smith Group,87.0,machine/machine3,470.0 -machine_data,2023-06-02 16:43:06.333179737,491cc20bbe13,13.0,machine2,193.0,Thomas and Sons,29.0,machine/machine2,74.0 -machine_data,2023-06-02 16:43:06.333464546,491cc20bbe13,23.0,machine1,193.0,"Morales, Robinson and Newman",31.0,machine/machine1,61.0 -machine_data,2023-06-02 16:43:07.333538813,491cc20bbe13,120.0,machine3,303.0,Smith Group,86.0,machine/machine3,443.0 -machine_data,2023-06-02 16:43:07.333592979,491cc20bbe13,13.0,machine2,197.0,Thomas and Sons,33.0,machine/machine2,62.0 -machine_data,2023-06-02 16:43:07.333964542,491cc20bbe13,23.0,machine1,197.0,"Morales, Robinson and Newman",29.0,machine/machine1,70.0 -machine_data,2023-06-02 16:43:08.333966919,491cc20bbe13,120.0,machine3,308.0,Smith Group,84.0,machine/machine3,330.0 -machine_data,2023-06-02 16:43:08.334168052,491cc20bbe13,13.0,machine2,195.0,Thomas and Sons,31.0,machine/machine2,73.0 -machine_data,2023-06-02 16:43:08.334236775,491cc20bbe13,23.0,machine1,184.0,"Morales, Robinson and Newman",30.0,machine/machine1,73.0 -machine_data,2023-06-02 16:43:09.334998961,491cc20bbe13,120.0,machine3,303.0,Smith Group,88.0,machine/machine3,395.0 -machine_data,2023-06-02 16:43:09.335091622,491cc20bbe13,21.0,machine2,193.0,Thomas and Sons,34.0,machine/machine2,72.0 -machine_data,2023-06-02 16:43:09.335117396,491cc20bbe13,51.0,machine1,201.0,"Morales, Robinson and Newman",37.0,machine/machine1,90.0 -machine_data,2023-06-02 16:43:10.335359448,491cc20bbe13,53.0,machine3,209.0,Smith Group,36.0,machine/machine3,89.0 -machine_data,2023-06-02 16:43:10.335917983,491cc20bbe13,21.0,machine2,189.0,Thomas and Sons,32.0,machine/machine2,58.0 -machine_data,2023-06-02 16:43:10.335961039,491cc20bbe13,51.0,machine1,203.0,"Morales, Robinson and Newman",38.0,machine/machine1,85.0 -machine_data,2023-06-02 16:43:11.335865097,491cc20bbe13,53.0,machine3,211.0,Smith Group,39.0,machine/machine3,83.0 -machine_data,2023-06-02 16:43:11.336300844,491cc20bbe13,21.0,machine2,194.0,Thomas and Sons,29.0,machine/machine2,58.0 -machine_data,2023-06-02 16:43:11.336354602,491cc20bbe13,51.0,machine1,205.0,"Morales, Robinson and Newman",38.0,machine/machine1,85.0 -machine_data,2023-06-02 16:43:12.336856584,491cc20bbe13,53.0,machine3,213.0,Smith Group,37.0,machine/machine3,88.0 -machine_data,2023-06-02 16:43:12.337596930,491cc20bbe13,21.0,machine2,193.0,Thomas and Sons,32.0,machine/machine2,55.0 -machine_data,2023-06-02 16:43:12.337627620,491cc20bbe13,51.0,machine1,203.0,"Morales, Robinson and Newman",36.0,machine/machine1,81.0 -machine_data,2023-06-02 16:43:13.337427240,491cc20bbe13,53.0,machine3,206.0,Smith Group,38.0,machine/machine3,80.0 -machine_data,2023-06-02 16:43:13.337811242,491cc20bbe13,21.0,machine2,184.0,Thomas and Sons,32.0,machine/machine2,73.0 -machine_data,2023-06-02 16:43:13.337874112,491cc20bbe13,51.0,machine1,201.0,"Morales, Robinson and Newman",40.0,machine/machine1,81.0 -machine_data,2023-06-02 16:43:14.337992849,491cc20bbe13,53.0,machine3,217.0,Smith Group,39.0,machine/machine3,89.0 -machine_data,2023-06-02 16:43:14.338326166,491cc20bbe13,21.0,machine2,198.0,Thomas and Sons,34.0,machine/machine2,63.0 -machine_data,2023-06-02 16:43:14.338634931,491cc20bbe13,51.0,machine1,205.0,"Morales, Robinson and Newman",37.0,machine/machine1,81.0 -machine_data,2023-06-02 16:43:15.339009274,491cc20bbe13,53.0,machine3,210.0,Smith Group,40.0,machine/machine3,80.0 -machine_data,2023-06-02 16:43:15.339100441,491cc20bbe13,51.0,machine1,206.0,"Morales, Robinson and Newman",38.0,machine/machine1,90.0 -machine_data,2023-06-02 16:43:15.339762188,491cc20bbe13,21.0,machine2,188.0,Thomas and Sons,30.0,machine/machine2,55.0 -machine_data,2023-06-02 16:43:16.340230361,491cc20bbe13,51.0,machine1,201.0,"Morales, Robinson and Newman",38.0,machine/machine1,84.0 -machine_data,2023-06-02 16:43:16.340650516,491cc20bbe13,21.0,machine2,184.0,Thomas and Sons,33.0,machine/machine2,64.0 -machine_data,2023-06-02 16:43:16.340991256,491cc20bbe13,53.0,machine3,203.0,Smith Group,37.0,machine/machine3,84.0 -machine_data,2023-06-02 16:43:17.341908770,491cc20bbe13,53.0,machine3,211.0,Smith Group,40.0,machine/machine3,84.0 -machine_data,2023-06-02 16:43:17.342360815,491cc20bbe13,51.0,machine1,215.0,"Morales, Robinson and Newman",38.0,machine/machine1,86.0 -machine_data,2023-06-02 16:43:17.342426363,491cc20bbe13,21.0,machine2,194.0,Thomas and Sons,33.0,machine/machine2,62.0 -machine_data,2023-06-02 16:43:18.342445484,491cc20bbe13,53.0,machine3,201.0,Smith Group,36.0,machine/machine3,90.0 -machine_data,2023-06-02 16:43:18.342768157,491cc20bbe13,21.0,machine2,197.0,Thomas and Sons,30.0,machine/machine2,53.0 -machine_data,2023-06-02 16:43:18.342799655,491cc20bbe13,51.0,machine1,211.0,"Morales, Robinson and Newman",36.0,machine/machine1,87.0 -machine_data,2023-06-02 16:43:19.342914561,491cc20bbe13,53.0,machine3,206.0,Smith Group,40.0,machine/machine3,80.0 -machine_data,2023-06-02 16:43:19.343415645,491cc20bbe13,21.0,machine2,196.0,Thomas and Sons,33.0,machine/machine2,53.0 -machine_data,2023-06-02 16:43:19.343478737,491cc20bbe13,51.0,machine1,220.0,"Morales, Robinson and Newman",36.0,machine/machine1,89.0 -machine_data,2023-06-02 16:43:20.343479811,491cc20bbe13,53.0,machine3,216.0,Smith Group,36.0,machine/machine3,84.0 -machine_data,2023-06-02 16:43:20.344124763,491cc20bbe13,21.0,machine2,193.0,Thomas and Sons,32.0,machine/machine2,77.0 -machine_data,2023-06-02 16:43:20.344153596,491cc20bbe13,51.0,machine1,200.0,"Morales, Robinson and Newman",35.0,machine/machine1,89.0 -machine_data,2023-06-02 16:43:21.343964197,491cc20bbe13,53.0,machine3,212.0,Smith Group,35.0,machine/machine3,86.0 -machine_data,2023-06-02 16:43:21.344617756,491cc20bbe13,21.0,machine2,183.0,Thomas and Sons,33.0,machine/machine2,64.0 -machine_data,2023-06-02 16:43:21.344685120,491cc20bbe13,51.0,machine1,209.0,"Morales, Robinson and Newman",35.0,machine/machine1,86.0 -machine_data,2023-06-02 16:43:22.344825564,491cc20bbe13,53.0,machine3,205.0,Smith Group,39.0,machine/machine3,87.0 -machine_data,2023-06-02 16:43:22.345135145,491cc20bbe13,21.0,machine2,184.0,Thomas and Sons,31.0,machine/machine2,60.0 -machine_data,2023-06-02 16:43:22.345167055,491cc20bbe13,51.0,machine1,220.0,"Morales, Robinson and Newman",38.0,machine/machine1,81.0 -machine_data,2023-06-02 16:43:23.345437181,491cc20bbe13,53.0,machine3,200.0,Smith Group,37.0,machine/machine3,86.0 -machine_data,2023-06-02 16:43:23.345506574,491cc20bbe13,51.0,machine1,214.0,"Morales, Robinson and Newman",37.0,machine/machine1,86.0 -machine_data,2023-06-02 16:43:23.345564703,491cc20bbe13,21.0,machine2,193.0,Thomas and Sons,33.0,machine/machine2,62.0 -machine_data,2023-06-02 16:43:24.346349521,491cc20bbe13,53.0,machine3,210.0,Smith Group,37.0,machine/machine3,85.0 -machine_data,2023-06-02 16:43:24.346383833,491cc20bbe13,51.0,machine1,211.0,"Morales, Robinson and Newman",37.0,machine/machine1,87.0 -machine_data,2023-06-02 16:43:24.346401291,491cc20bbe13,21.0,machine2,193.0,Thomas and Sons,32.0,machine/machine2,54.0 -machine_data,2023-06-02 16:43:25.346821289,491cc20bbe13,53.0,machine3,220.0,Smith Group,37.0,machine/machine3,89.0 -machine_data,2023-06-02 16:43:25.346921797,491cc20bbe13,21.0,machine2,190.0,Thomas and Sons,34.0,machine/machine2,75.0 -machine_data,2023-06-02 16:43:25.347491870,491cc20bbe13,51.0,machine1,214.0,"Morales, Robinson and Newman",39.0,machine/machine1,83.0 -machine_data,2023-06-02 16:43:26.347838431,491cc20bbe13,21.0,machine2,186.0,Thomas and Sons,29.0,machine/machine2,79.0 -machine_data,2023-06-02 16:43:26.348427616,491cc20bbe13,51.0,machine1,200.0,"Morales, Robinson and Newman",40.0,machine/machine1,85.0 -machine_data,2023-06-02 16:43:26.348455741,491cc20bbe13,53.0,machine3,205.0,Smith Group,40.0,machine/machine3,80.0 -machine_data,2023-06-02 16:43:27.348535696,491cc20bbe13,21.0,machine2,189.0,Thomas and Sons,34.0,machine/machine2,79.0 -machine_data,2023-06-02 16:43:27.349173287,491cc20bbe13,51.0,machine1,215.0,"Morales, Robinson and Newman",36.0,machine/machine1,86.0 -machine_data,2023-06-02 16:43:27.349217288,491cc20bbe13,53.0,machine3,213.0,Smith Group,39.0,machine/machine3,83.0 -machine_data,2023-06-02 16:43:28.348964054,491cc20bbe13,21.0,machine2,191.0,Thomas and Sons,31.0,machine/machine2,63.0 -machine_data,2023-06-02 16:43:28.349958403,491cc20bbe13,51.0,machine1,211.0,"Morales, Robinson and Newman",35.0,machine/machine1,84.0 -machine_data,2023-06-02 16:43:28.350213700,491cc20bbe13,53.0,machine3,201.0,Smith Group,38.0,machine/machine3,81.0 -machine_data,2023-06-02 16:43:29.349501656,491cc20bbe13,21.0,machine2,190.0,Thomas and Sons,30.0,machine/machine2,70.0 -machine_data,2023-06-02 16:43:29.350320992,491cc20bbe13,51.0,machine1,209.0,"Morales, Robinson and Newman",35.0,machine/machine1,88.0 -machine_data,2023-06-02 16:43:29.350858026,491cc20bbe13,53.0,machine3,204.0,Smith Group,36.0,machine/machine3,82.0 -machine_data,2023-06-02 16:43:30.350640276,491cc20bbe13,21.0,machine2,197.0,Thomas and Sons,30.0,machine/machine2,55.0 -machine_data,2023-06-02 16:43:30.351158057,491cc20bbe13,51.0,machine1,214.0,"Morales, Robinson and Newman",36.0,machine/machine1,90.0 -machine_data,2023-06-02 16:43:30.351331667,491cc20bbe13,53.0,machine3,210.0,Smith Group,39.0,machine/machine3,85.0 -machine_data,2023-06-02 16:43:31.351859842,491cc20bbe13,21.0,machine2,188.0,Thomas and Sons,29.0,machine/machine2,65.0 -machine_data,2023-06-02 16:43:31.352098083,491cc20bbe13,51.0,machine1,214.0,"Morales, Robinson and Newman",35.0,machine/machine1,86.0 -machine_data,2023-06-02 16:43:31.352141060,491cc20bbe13,53.0,machine3,213.0,Smith Group,37.0,machine/machine3,84.0 -machine_data,2023-06-02 16:43:32.352201239,491cc20bbe13,21.0,machine2,195.0,Thomas and Sons,32.0,machine/machine2,56.0 -machine_data,2023-06-02 16:43:32.352517261,491cc20bbe13,51.0,machine1,205.0,"Morales, Robinson and Newman",37.0,machine/machine1,84.0 -machine_data,2023-06-02 16:43:32.352589820,491cc20bbe13,53.0,machine3,215.0,Smith Group,36.0,machine/machine3,85.0 -machine_data,2023-06-02 16:43:33.352642145,491cc20bbe13,21.0,machine2,187.0,Thomas and Sons,34.0,machine/machine2,68.0 -machine_data,2023-06-02 16:43:33.353162708,491cc20bbe13,51.0,machine1,218.0,"Morales, Robinson and Newman",35.0,machine/machine1,82.0 -machine_data,2023-06-02 16:43:33.353192522,491cc20bbe13,53.0,machine3,205.0,Smith Group,36.0,machine/machine3,89.0 -machine_data,2023-06-02 16:43:34.353255548,491cc20bbe13,21.0,machine2,197.0,Thomas and Sons,31.0,machine/machine2,50.0 -machine_data,2023-06-02 16:43:34.353521359,491cc20bbe13,51.0,machine1,205.0,"Morales, Robinson and Newman",38.0,machine/machine1,80.0 -machine_data,2023-06-02 16:43:34.353592745,491cc20bbe13,53.0,machine3,202.0,Smith Group,38.0,machine/machine3,83.0 -machine_data,2023-06-02 16:43:35.353829588,491cc20bbe13,21.0,machine2,186.0,Thomas and Sons,30.0,machine/machine2,68.0 -machine_data,2023-06-02 16:43:35.354477346,491cc20bbe13,53.0,machine3,215.0,Smith Group,36.0,machine/machine3,84.0 -machine_data,2023-06-02 16:43:35.354644118,491cc20bbe13,51.0,machine1,206.0,"Morales, Robinson and Newman",38.0,machine/machine1,85.0 diff --git a/Examples/write/source_data/out_orig.json b/Examples/write/source_data/out_orig.json deleted file mode 100644 index 91c31d8..0000000 --- a/Examples/write/source_data/out_orig.json +++ /dev/null @@ -1 +0,0 @@ -[{"iox::measurement":"machine_data","time":1685724162317550714,"host":"491cc20bbe13","load":120.0,"machineID":"machine3","power":301.0,"provider":"Smith Group","temperature":80.0,"topic":"machine\/machine3","vibration":310.0},{"iox::measurement":"machine_data","time":1685724162317757560,"host":"491cc20bbe13","load":23.0,"machineID":"machine1","power":183.0,"provider":"Morales, Robinson and Newman","temperature":31.0,"topic":"machine\/machine1","vibration":78.0},{"iox::measurement":"machine_data","time":1685724162317787898,"host":"491cc20bbe13","load":13.0,"machineID":"machine2","power":198.0,"provider":"Thomas and Sons","temperature":34.0,"topic":"machine\/machine2","vibration":73.0},{"iox::measurement":"machine_data","time":1685724163318452979,"host":"491cc20bbe13","load":120.0,"machineID":"machine3","power":307.0,"provider":"Smith Group","temperature":81.0,"topic":"machine\/machine3","vibration":332.0},{"iox::measurement":"machine_data","time":1685724163318733897,"host":"491cc20bbe13","load":23.0,"machineID":"machine1","power":191.0,"provider":"Morales, Robinson and Newman","temperature":30.0,"topic":"machine\/machine1","vibration":53.0},{"iox::measurement":"machine_data","time":1685724163318895072,"host":"491cc20bbe13","load":13.0,"machineID":"machine2","power":197.0,"provider":"Thomas and Sons","temperature":31.0,"topic":"machine\/machine2","vibration":56.0},{"iox::measurement":"machine_data","time":1685724164319014820,"host":"491cc20bbe13","load":120.0,"machineID":"machine3","power":302.0,"provider":"Smith Group","temperature":87.0,"topic":"machine\/machine3","vibration":406.0},{"iox::measurement":"machine_data","time":1685724164319289702,"host":"491cc20bbe13","load":23.0,"machineID":"machine1","power":194.0,"provider":"Morales, Robinson and Newman","temperature":31.0,"topic":"machine\/machine1","vibration":65.0},{"iox::measurement":"machine_data","time":1685724164319743319,"host":"491cc20bbe13","load":13.0,"machineID":"machine2","power":192.0,"provider":"Thomas and Sons","temperature":34.0,"topic":"machine\/machine2","vibration":64.0},{"iox::measurement":"machine_data","time":1685724165319492285,"host":"491cc20bbe13","load":120.0,"machineID":"machine3","power":312.0,"provider":"Smith Group","temperature":83.0,"topic":"machine\/machine3","vibration":460.0},{"iox::measurement":"machine_data","time":1685724165319953921,"host":"491cc20bbe13","load":23.0,"machineID":"machine1","power":199.0,"provider":"Morales, Robinson and Newman","temperature":29.0,"topic":"machine\/machine1","vibration":69.0},{"iox::measurement":"machine_data","time":1685724165319985284,"host":"491cc20bbe13","load":13.0,"machineID":"machine2","power":188.0,"provider":"Thomas and Sons","temperature":29.0,"topic":"machine\/machine2","vibration":72.0},{"iox::measurement":"machine_data","time":1685724166319917712,"host":"491cc20bbe13","load":120.0,"machineID":"machine3","power":317.0,"provider":"Smith Group","temperature":90.0,"topic":"machine\/machine3","vibration":363.0},{"iox::measurement":"machine_data","time":1685724166320373453,"host":"491cc20bbe13","load":23.0,"machineID":"machine1","power":183.0,"provider":"Morales, Robinson and Newman","temperature":34.0,"topic":"machine\/machine1","vibration":69.0},{"iox::measurement":"machine_data","time":1685724166320760737,"host":"491cc20bbe13","load":13.0,"machineID":"machine2","power":184.0,"provider":"Thomas and Sons","temperature":31.0,"topic":"machine\/machine2","vibration":78.0},{"iox::measurement":"machine_data","time":1685724167320548979,"host":"491cc20bbe13","load":120.0,"machineID":"machine3","power":303.0,"provider":"Smith Group","temperature":80.0,"topic":"machine\/machine3","vibration":419.0},{"iox::measurement":"machine_data","time":1685724167320966397,"host":"491cc20bbe13","load":23.0,"machineID":"machine1","power":181.0,"provider":"Morales, Robinson and Newman","temperature":30.0,"topic":"machine\/machine1","vibration":71.0},{"iox::measurement":"machine_data","time":1685724167321006298,"host":"491cc20bbe13","load":13.0,"machineID":"machine2","power":186.0,"provider":"Thomas and Sons","temperature":31.0,"topic":"machine\/machine2","vibration":60.0},{"iox::measurement":"machine_data","time":1685724168321265346,"host":"491cc20bbe13","load":120.0,"machineID":"machine3","power":305.0,"provider":"Smith Group","temperature":89.0,"topic":"machine\/machine3","vibration":367.0},{"iox::measurement":"machine_data","time":1685724168321382477,"host":"491cc20bbe13","load":23.0,"machineID":"machine1","power":197.0,"provider":"Morales, Robinson and Newman","temperature":33.0,"topic":"machine\/machine1","vibration":54.0},{"iox::measurement":"machine_data","time":1685724168321535645,"host":"491cc20bbe13","load":13.0,"machineID":"machine2","power":195.0,"provider":"Thomas and Sons","temperature":30.0,"topic":"machine\/machine2","vibration":68.0},{"iox::measurement":"machine_data","time":1685724169321847629,"host":"491cc20bbe13","load":120.0,"machineID":"machine3","power":317.0,"provider":"Smith Group","temperature":86.0,"topic":"machine\/machine3","vibration":334.0},{"iox::measurement":"machine_data","time":1685724169322119417,"host":"491cc20bbe13","load":13.0,"machineID":"machine2","power":188.0,"provider":"Thomas and Sons","temperature":29.0,"topic":"machine\/machine2","vibration":71.0},{"iox::measurement":"machine_data","time":1685724169322147172,"host":"491cc20bbe13","load":23.0,"machineID":"machine1","power":184.0,"provider":"Morales, Robinson and Newman","temperature":34.0,"topic":"machine\/machine1","vibration":71.0},{"iox::measurement":"machine_data","time":1685724170322698923,"host":"491cc20bbe13","load":120.0,"machineID":"machine3","power":302.0,"provider":"Smith Group","temperature":87.0,"topic":"machine\/machine3","vibration":407.0},{"iox::measurement":"machine_data","time":1685724170323173071,"host":"491cc20bbe13","load":23.0,"machineID":"machine1","power":193.0,"provider":"Morales, Robinson and Newman","temperature":32.0,"topic":"machine\/machine1","vibration":59.0},{"iox::measurement":"machine_data","time":1685724170323237285,"host":"491cc20bbe13","load":13.0,"machineID":"machine2","power":182.0,"provider":"Thomas and Sons","temperature":30.0,"topic":"machine\/machine2","vibration":72.0},{"iox::measurement":"machine_data","time":1685724171323193568,"host":"491cc20bbe13","load":120.0,"machineID":"machine3","power":303.0,"provider":"Smith Group","temperature":90.0,"topic":"machine\/machine3","vibration":343.0},{"iox::measurement":"machine_data","time":1685724171323662012,"host":"491cc20bbe13","load":13.0,"machineID":"machine2","power":188.0,"provider":"Thomas and Sons","temperature":31.0,"topic":"machine\/machine2","vibration":67.0},{"iox::measurement":"machine_data","time":1685724171323725850,"host":"491cc20bbe13","load":23.0,"machineID":"machine1","power":187.0,"provider":"Morales, Robinson and Newman","temperature":30.0,"topic":"machine\/machine1","vibration":69.0},{"iox::measurement":"machine_data","time":1685724172323837298,"host":"491cc20bbe13","load":120.0,"machineID":"machine3","power":319.0,"provider":"Smith Group","temperature":89.0,"topic":"machine\/machine3","vibration":406.0},{"iox::measurement":"machine_data","time":1685724172324179952,"host":"491cc20bbe13","load":23.0,"machineID":"machine1","power":192.0,"provider":"Morales, Robinson and Newman","temperature":31.0,"topic":"machine\/machine1","vibration":65.0},{"iox::measurement":"machine_data","time":1685724172324204211,"host":"491cc20bbe13","load":13.0,"machineID":"machine2","power":193.0,"provider":"Thomas and Sons","temperature":34.0,"topic":"machine\/machine2","vibration":61.0},{"iox::measurement":"machine_data","time":1685724173324457761,"host":"491cc20bbe13","load":120.0,"machineID":"machine3","power":306.0,"provider":"Smith Group","temperature":81.0,"topic":"machine\/machine3","vibration":432.0},{"iox::measurement":"machine_data","time":1685724173324791324,"host":"491cc20bbe13","load":23.0,"machineID":"machine1","power":196.0,"provider":"Morales, Robinson and Newman","temperature":30.0,"topic":"machine\/machine1","vibration":59.0},{"iox::measurement":"machine_data","time":1685724173324822237,"host":"491cc20bbe13","load":13.0,"machineID":"machine2","power":184.0,"provider":"Thomas and Sons","temperature":30.0,"topic":"machine\/machine2","vibration":62.0},{"iox::measurement":"machine_data","time":1685724174324940162,"host":"491cc20bbe13","load":120.0,"machineID":"machine3","power":319.0,"provider":"Smith Group","temperature":87.0,"topic":"machine\/machine3","vibration":473.0},{"iox::measurement":"machine_data","time":1685724174325470257,"host":"491cc20bbe13","load":23.0,"machineID":"machine1","power":183.0,"provider":"Morales, Robinson and Newman","temperature":29.0,"topic":"machine\/machine1","vibration":69.0},{"iox::measurement":"machine_data","time":1685724174325535111,"host":"491cc20bbe13","load":13.0,"machineID":"machine2","power":184.0,"provider":"Thomas and Sons","temperature":34.0,"topic":"machine\/machine2","vibration":72.0},{"iox::measurement":"machine_data","time":1685724175325452588,"host":"491cc20bbe13","load":120.0,"machineID":"machine3","power":308.0,"provider":"Smith Group","temperature":88.0,"topic":"machine\/machine3","vibration":478.0},{"iox::measurement":"machine_data","time":1685724175326241988,"host":"491cc20bbe13","load":23.0,"machineID":"machine1","power":180.0,"provider":"Morales, Robinson and Newman","temperature":32.0,"topic":"machine\/machine1","vibration":67.0},{"iox::measurement":"machine_data","time":1685724175326458954,"host":"491cc20bbe13","load":13.0,"machineID":"machine2","power":181.0,"provider":"Thomas and Sons","temperature":34.0,"topic":"machine\/machine2","vibration":53.0},{"iox::measurement":"machine_data","time":1685724176325962575,"host":"491cc20bbe13","load":120.0,"machineID":"machine3","power":301.0,"provider":"Smith Group","temperature":89.0,"topic":"machine\/machine3","vibration":366.0},{"iox::measurement":"machine_data","time":1685724176326547488,"host":"491cc20bbe13","load":23.0,"machineID":"machine1","power":194.0,"provider":"Morales, Robinson and Newman","temperature":33.0,"topic":"machine\/machine1","vibration":77.0},{"iox::measurement":"machine_data","time":1685724176326817078,"host":"491cc20bbe13","load":13.0,"machineID":"machine2","power":182.0,"provider":"Thomas and Sons","temperature":33.0,"topic":"machine\/machine2","vibration":54.0},{"iox::measurement":"machine_data","time":1685724177326651847,"host":"491cc20bbe13","load":120.0,"machineID":"machine3","power":316.0,"provider":"Smith Group","temperature":82.0,"topic":"machine\/machine3","vibration":446.0},{"iox::measurement":"machine_data","time":1685724177326903277,"host":"491cc20bbe13","load":23.0,"machineID":"machine1","power":186.0,"provider":"Morales, Robinson and Newman","temperature":32.0,"topic":"machine\/machine1","vibration":57.0},{"iox::measurement":"machine_data","time":1685724177327169455,"host":"491cc20bbe13","load":13.0,"machineID":"machine2","power":183.0,"provider":"Thomas and Sons","temperature":33.0,"topic":"machine\/machine2","vibration":50.0},{"iox::measurement":"machine_data","time":1685724178327381372,"host":"491cc20bbe13","load":120.0,"machineID":"machine3","power":303.0,"provider":"Smith Group","temperature":83.0,"topic":"machine\/machine3","vibration":329.0},{"iox::measurement":"machine_data","time":1685724178327785814,"host":"491cc20bbe13","load":13.0,"machineID":"machine2","power":189.0,"provider":"Thomas and Sons","temperature":31.0,"topic":"machine\/machine2","vibration":54.0},{"iox::measurement":"machine_data","time":1685724178328075202,"host":"491cc20bbe13","load":23.0,"machineID":"machine1","power":184.0,"provider":"Morales, Robinson and Newman","temperature":34.0,"topic":"machine\/machine1","vibration":67.0},{"iox::measurement":"machine_data","time":1685724179328107312,"host":"491cc20bbe13","load":120.0,"machineID":"machine3","power":300.0,"provider":"Smith Group","temperature":90.0,"topic":"machine\/machine3","vibration":446.0},{"iox::measurement":"machine_data","time":1685724179328697178,"host":"491cc20bbe13","load":13.0,"machineID":"machine2","power":196.0,"provider":"Thomas and Sons","temperature":32.0,"topic":"machine\/machine2","vibration":51.0},{"iox::measurement":"machine_data","time":1685724179328842456,"host":"491cc20bbe13","load":23.0,"machineID":"machine1","power":183.0,"provider":"Morales, Robinson and Newman","temperature":30.0,"topic":"machine\/machine1","vibration":56.0},{"iox::measurement":"machine_data","time":1685724180328689987,"host":"491cc20bbe13","load":120.0,"machineID":"machine3","power":300.0,"provider":"Smith Group","temperature":81.0,"topic":"machine\/machine3","vibration":460.0},{"iox::measurement":"machine_data","time":1685724180328730602,"host":"491cc20bbe13","load":13.0,"machineID":"machine2","power":180.0,"provider":"Thomas and Sons","temperature":34.0,"topic":"machine\/machine2","vibration":57.0},{"iox::measurement":"machine_data","time":1685724180329187607,"host":"491cc20bbe13","load":23.0,"machineID":"machine1","power":187.0,"provider":"Morales, Robinson and Newman","temperature":33.0,"topic":"machine\/machine1","vibration":70.0},{"iox::measurement":"machine_data","time":1685724181329623051,"host":"491cc20bbe13","load":13.0,"machineID":"machine2","power":194.0,"provider":"Thomas and Sons","temperature":33.0,"topic":"machine\/machine2","vibration":79.0},{"iox::measurement":"machine_data","time":1685724181329737661,"host":"491cc20bbe13","load":120.0,"machineID":"machine3","power":314.0,"provider":"Smith Group","temperature":85.0,"topic":"machine\/machine3","vibration":441.0},{"iox::measurement":"machine_data","time":1685724181329994500,"host":"491cc20bbe13","load":23.0,"machineID":"machine1","power":196.0,"provider":"Morales, Robinson and Newman","temperature":34.0,"topic":"machine\/machine1","vibration":70.0},{"iox::measurement":"machine_data","time":1685724182330561375,"host":"491cc20bbe13","load":13.0,"machineID":"machine2","power":182.0,"provider":"Thomas and Sons","temperature":31.0,"topic":"machine\/machine2","vibration":66.0},{"iox::measurement":"machine_data","time":1685724182330611344,"host":"491cc20bbe13","load":120.0,"machineID":"machine3","power":304.0,"provider":"Smith Group","temperature":89.0,"topic":"machine\/machine3","vibration":455.0},{"iox::measurement":"machine_data","time":1685724182330943808,"host":"491cc20bbe13","load":23.0,"machineID":"machine1","power":186.0,"provider":"Morales, Robinson and Newman","temperature":29.0,"topic":"machine\/machine1","vibration":77.0},{"iox::measurement":"machine_data","time":1685724183331222016,"host":"491cc20bbe13","load":120.0,"machineID":"machine3","power":319.0,"provider":"Smith Group","temperature":90.0,"topic":"machine\/machine3","vibration":459.0},{"iox::measurement":"machine_data","time":1685724183331373671,"host":"491cc20bbe13","load":23.0,"machineID":"machine1","power":196.0,"provider":"Morales, Robinson and Newman","temperature":29.0,"topic":"machine\/machine1","vibration":60.0},{"iox::measurement":"machine_data","time":1685724183331578703,"host":"491cc20bbe13","load":13.0,"machineID":"machine2","power":195.0,"provider":"Thomas and Sons","temperature":34.0,"topic":"machine\/machine2","vibration":60.0},{"iox::measurement":"machine_data","time":1685724184331723999,"host":"491cc20bbe13","load":120.0,"machineID":"machine3","power":311.0,"provider":"Smith Group","temperature":80.0,"topic":"machine\/machine3","vibration":493.0},{"iox::measurement":"machine_data","time":1685724184332158179,"host":"491cc20bbe13","load":23.0,"machineID":"machine1","power":198.0,"provider":"Morales, Robinson and Newman","temperature":31.0,"topic":"machine\/machine1","vibration":71.0},{"iox::measurement":"machine_data","time":1685724184332222502,"host":"491cc20bbe13","load":13.0,"machineID":"machine2","power":188.0,"provider":"Thomas and Sons","temperature":34.0,"topic":"machine\/machine2","vibration":72.0},{"iox::measurement":"machine_data","time":1685724185332522723,"host":"491cc20bbe13","load":120.0,"machineID":"machine3","power":305.0,"provider":"Smith Group","temperature":89.0,"topic":"machine\/machine3","vibration":442.0},{"iox::measurement":"machine_data","time":1685724185332606103,"host":"491cc20bbe13","load":23.0,"machineID":"machine1","power":189.0,"provider":"Morales, Robinson and Newman","temperature":33.0,"topic":"machine\/machine1","vibration":71.0},{"iox::measurement":"machine_data","time":1685724185332956871,"host":"491cc20bbe13","load":13.0,"machineID":"machine2","power":191.0,"provider":"Thomas and Sons","temperature":34.0,"topic":"machine\/machine2","vibration":76.0},{"iox::measurement":"machine_data","time":1685724186332934234,"host":"491cc20bbe13","load":120.0,"machineID":"machine3","power":313.0,"provider":"Smith Group","temperature":87.0,"topic":"machine\/machine3","vibration":470.0},{"iox::measurement":"machine_data","time":1685724186333179737,"host":"491cc20bbe13","load":13.0,"machineID":"machine2","power":193.0,"provider":"Thomas and Sons","temperature":29.0,"topic":"machine\/machine2","vibration":74.0},{"iox::measurement":"machine_data","time":1685724186333464546,"host":"491cc20bbe13","load":23.0,"machineID":"machine1","power":193.0,"provider":"Morales, Robinson and Newman","temperature":31.0,"topic":"machine\/machine1","vibration":61.0},{"iox::measurement":"machine_data","time":1685724187333538813,"host":"491cc20bbe13","load":120.0,"machineID":"machine3","power":303.0,"provider":"Smith Group","temperature":86.0,"topic":"machine\/machine3","vibration":443.0},{"iox::measurement":"machine_data","time":1685724187333592979,"host":"491cc20bbe13","load":13.0,"machineID":"machine2","power":197.0,"provider":"Thomas and Sons","temperature":33.0,"topic":"machine\/machine2","vibration":62.0},{"iox::measurement":"machine_data","time":1685724187333964542,"host":"491cc20bbe13","load":23.0,"machineID":"machine1","power":197.0,"provider":"Morales, Robinson and Newman","temperature":29.0,"topic":"machine\/machine1","vibration":70.0},{"iox::measurement":"machine_data","time":1685724188333966919,"host":"491cc20bbe13","load":120.0,"machineID":"machine3","power":308.0,"provider":"Smith Group","temperature":84.0,"topic":"machine\/machine3","vibration":330.0},{"iox::measurement":"machine_data","time":1685724188334168052,"host":"491cc20bbe13","load":13.0,"machineID":"machine2","power":195.0,"provider":"Thomas and Sons","temperature":31.0,"topic":"machine\/machine2","vibration":73.0},{"iox::measurement":"machine_data","time":1685724188334236775,"host":"491cc20bbe13","load":23.0,"machineID":"machine1","power":184.0,"provider":"Morales, Robinson and Newman","temperature":30.0,"topic":"machine\/machine1","vibration":73.0},{"iox::measurement":"machine_data","time":1685724189334998961,"host":"491cc20bbe13","load":120.0,"machineID":"machine3","power":303.0,"provider":"Smith Group","temperature":88.0,"topic":"machine\/machine3","vibration":395.0},{"iox::measurement":"machine_data","time":1685724189335091622,"host":"491cc20bbe13","load":21.0,"machineID":"machine2","power":193.0,"provider":"Thomas and Sons","temperature":34.0,"topic":"machine\/machine2","vibration":72.0},{"iox::measurement":"machine_data","time":1685724189335117396,"host":"491cc20bbe13","load":51.0,"machineID":"machine1","power":201.0,"provider":"Morales, Robinson and Newman","temperature":37.0,"topic":"machine\/machine1","vibration":90.0},{"iox::measurement":"machine_data","time":1685724190335359448,"host":"491cc20bbe13","load":53.0,"machineID":"machine3","power":209.0,"provider":"Smith Group","temperature":36.0,"topic":"machine\/machine3","vibration":89.0},{"iox::measurement":"machine_data","time":1685724190335917983,"host":"491cc20bbe13","load":21.0,"machineID":"machine2","power":189.0,"provider":"Thomas and Sons","temperature":32.0,"topic":"machine\/machine2","vibration":58.0},{"iox::measurement":"machine_data","time":1685724190335961039,"host":"491cc20bbe13","load":51.0,"machineID":"machine1","power":203.0,"provider":"Morales, Robinson and Newman","temperature":38.0,"topic":"machine\/machine1","vibration":85.0},{"iox::measurement":"machine_data","time":1685724191335865097,"host":"491cc20bbe13","load":53.0,"machineID":"machine3","power":211.0,"provider":"Smith Group","temperature":39.0,"topic":"machine\/machine3","vibration":83.0},{"iox::measurement":"machine_data","time":1685724191336300844,"host":"491cc20bbe13","load":21.0,"machineID":"machine2","power":194.0,"provider":"Thomas and Sons","temperature":29.0,"topic":"machine\/machine2","vibration":58.0},{"iox::measurement":"machine_data","time":1685724191336354602,"host":"491cc20bbe13","load":51.0,"machineID":"machine1","power":205.0,"provider":"Morales, Robinson and Newman","temperature":38.0,"topic":"machine\/machine1","vibration":85.0},{"iox::measurement":"machine_data","time":1685724192336856584,"host":"491cc20bbe13","load":53.0,"machineID":"machine3","power":213.0,"provider":"Smith Group","temperature":37.0,"topic":"machine\/machine3","vibration":88.0},{"iox::measurement":"machine_data","time":1685724192337596930,"host":"491cc20bbe13","load":21.0,"machineID":"machine2","power":193.0,"provider":"Thomas and Sons","temperature":32.0,"topic":"machine\/machine2","vibration":55.0},{"iox::measurement":"machine_data","time":1685724192337627620,"host":"491cc20bbe13","load":51.0,"machineID":"machine1","power":203.0,"provider":"Morales, Robinson and Newman","temperature":36.0,"topic":"machine\/machine1","vibration":81.0},{"iox::measurement":"machine_data","time":1685724193337427240,"host":"491cc20bbe13","load":53.0,"machineID":"machine3","power":206.0,"provider":"Smith Group","temperature":38.0,"topic":"machine\/machine3","vibration":80.0},{"iox::measurement":"machine_data","time":1685724193337811242,"host":"491cc20bbe13","load":21.0,"machineID":"machine2","power":184.0,"provider":"Thomas and Sons","temperature":32.0,"topic":"machine\/machine2","vibration":73.0},{"iox::measurement":"machine_data","time":1685724193337874112,"host":"491cc20bbe13","load":51.0,"machineID":"machine1","power":201.0,"provider":"Morales, Robinson and Newman","temperature":40.0,"topic":"machine\/machine1","vibration":81.0},{"iox::measurement":"machine_data","time":1685724194337992849,"host":"491cc20bbe13","load":53.0,"machineID":"machine3","power":217.0,"provider":"Smith Group","temperature":39.0,"topic":"machine\/machine3","vibration":89.0},{"iox::measurement":"machine_data","time":1685724194338326166,"host":"491cc20bbe13","load":21.0,"machineID":"machine2","power":198.0,"provider":"Thomas and Sons","temperature":34.0,"topic":"machine\/machine2","vibration":63.0},{"iox::measurement":"machine_data","time":1685724194338634931,"host":"491cc20bbe13","load":51.0,"machineID":"machine1","power":205.0,"provider":"Morales, Robinson and Newman","temperature":37.0,"topic":"machine\/machine1","vibration":81.0},{"iox::measurement":"machine_data","time":1685724195339009274,"host":"491cc20bbe13","load":53.0,"machineID":"machine3","power":210.0,"provider":"Smith Group","temperature":40.0,"topic":"machine\/machine3","vibration":80.0},{"iox::measurement":"machine_data","time":1685724195339100441,"host":"491cc20bbe13","load":51.0,"machineID":"machine1","power":206.0,"provider":"Morales, Robinson and Newman","temperature":38.0,"topic":"machine\/machine1","vibration":90.0},{"iox::measurement":"machine_data","time":1685724195339762188,"host":"491cc20bbe13","load":21.0,"machineID":"machine2","power":188.0,"provider":"Thomas and Sons","temperature":30.0,"topic":"machine\/machine2","vibration":55.0},{"iox::measurement":"machine_data","time":1685724196340230361,"host":"491cc20bbe13","load":51.0,"machineID":"machine1","power":201.0,"provider":"Morales, Robinson and Newman","temperature":38.0,"topic":"machine\/machine1","vibration":84.0},{"iox::measurement":"machine_data","time":1685724196340650516,"host":"491cc20bbe13","load":21.0,"machineID":"machine2","power":184.0,"provider":"Thomas and Sons","temperature":33.0,"topic":"machine\/machine2","vibration":64.0},{"iox::measurement":"machine_data","time":1685724196340991256,"host":"491cc20bbe13","load":53.0,"machineID":"machine3","power":203.0,"provider":"Smith Group","temperature":37.0,"topic":"machine\/machine3","vibration":84.0},{"iox::measurement":"machine_data","time":1685724197341908770,"host":"491cc20bbe13","load":53.0,"machineID":"machine3","power":211.0,"provider":"Smith Group","temperature":40.0,"topic":"machine\/machine3","vibration":84.0},{"iox::measurement":"machine_data","time":1685724197342360815,"host":"491cc20bbe13","load":51.0,"machineID":"machine1","power":215.0,"provider":"Morales, Robinson and Newman","temperature":38.0,"topic":"machine\/machine1","vibration":86.0},{"iox::measurement":"machine_data","time":1685724197342426363,"host":"491cc20bbe13","load":21.0,"machineID":"machine2","power":194.0,"provider":"Thomas and Sons","temperature":33.0,"topic":"machine\/machine2","vibration":62.0},{"iox::measurement":"machine_data","time":1685724198342445484,"host":"491cc20bbe13","load":53.0,"machineID":"machine3","power":201.0,"provider":"Smith Group","temperature":36.0,"topic":"machine\/machine3","vibration":90.0},{"iox::measurement":"machine_data","time":1685724198342768157,"host":"491cc20bbe13","load":21.0,"machineID":"machine2","power":197.0,"provider":"Thomas and Sons","temperature":30.0,"topic":"machine\/machine2","vibration":53.0},{"iox::measurement":"machine_data","time":1685724198342799655,"host":"491cc20bbe13","load":51.0,"machineID":"machine1","power":211.0,"provider":"Morales, Robinson and Newman","temperature":36.0,"topic":"machine\/machine1","vibration":87.0},{"iox::measurement":"machine_data","time":1685724199342914561,"host":"491cc20bbe13","load":53.0,"machineID":"machine3","power":206.0,"provider":"Smith Group","temperature":40.0,"topic":"machine\/machine3","vibration":80.0},{"iox::measurement":"machine_data","time":1685724199343415645,"host":"491cc20bbe13","load":21.0,"machineID":"machine2","power":196.0,"provider":"Thomas and Sons","temperature":33.0,"topic":"machine\/machine2","vibration":53.0},{"iox::measurement":"machine_data","time":1685724199343478737,"host":"491cc20bbe13","load":51.0,"machineID":"machine1","power":220.0,"provider":"Morales, Robinson and Newman","temperature":36.0,"topic":"machine\/machine1","vibration":89.0},{"iox::measurement":"machine_data","time":1685724200343479811,"host":"491cc20bbe13","load":53.0,"machineID":"machine3","power":216.0,"provider":"Smith Group","temperature":36.0,"topic":"machine\/machine3","vibration":84.0},{"iox::measurement":"machine_data","time":1685724200344124763,"host":"491cc20bbe13","load":21.0,"machineID":"machine2","power":193.0,"provider":"Thomas and Sons","temperature":32.0,"topic":"machine\/machine2","vibration":77.0},{"iox::measurement":"machine_data","time":1685724200344153596,"host":"491cc20bbe13","load":51.0,"machineID":"machine1","power":200.0,"provider":"Morales, Robinson and Newman","temperature":35.0,"topic":"machine\/machine1","vibration":89.0},{"iox::measurement":"machine_data","time":1685724201343964197,"host":"491cc20bbe13","load":53.0,"machineID":"machine3","power":212.0,"provider":"Smith Group","temperature":35.0,"topic":"machine\/machine3","vibration":86.0},{"iox::measurement":"machine_data","time":1685724201344617756,"host":"491cc20bbe13","load":21.0,"machineID":"machine2","power":183.0,"provider":"Thomas and Sons","temperature":33.0,"topic":"machine\/machine2","vibration":64.0},{"iox::measurement":"machine_data","time":1685724201344685120,"host":"491cc20bbe13","load":51.0,"machineID":"machine1","power":209.0,"provider":"Morales, Robinson and Newman","temperature":35.0,"topic":"machine\/machine1","vibration":86.0},{"iox::measurement":"machine_data","time":1685724202344825564,"host":"491cc20bbe13","load":53.0,"machineID":"machine3","power":205.0,"provider":"Smith Group","temperature":39.0,"topic":"machine\/machine3","vibration":87.0},{"iox::measurement":"machine_data","time":1685724202345135145,"host":"491cc20bbe13","load":21.0,"machineID":"machine2","power":184.0,"provider":"Thomas and Sons","temperature":31.0,"topic":"machine\/machine2","vibration":60.0},{"iox::measurement":"machine_data","time":1685724202345167055,"host":"491cc20bbe13","load":51.0,"machineID":"machine1","power":220.0,"provider":"Morales, Robinson and Newman","temperature":38.0,"topic":"machine\/machine1","vibration":81.0},{"iox::measurement":"machine_data","time":1685724203345437181,"host":"491cc20bbe13","load":53.0,"machineID":"machine3","power":200.0,"provider":"Smith Group","temperature":37.0,"topic":"machine\/machine3","vibration":86.0},{"iox::measurement":"machine_data","time":1685724203345506574,"host":"491cc20bbe13","load":51.0,"machineID":"machine1","power":214.0,"provider":"Morales, Robinson and Newman","temperature":37.0,"topic":"machine\/machine1","vibration":86.0},{"iox::measurement":"machine_data","time":1685724203345564703,"host":"491cc20bbe13","load":21.0,"machineID":"machine2","power":193.0,"provider":"Thomas and Sons","temperature":33.0,"topic":"machine\/machine2","vibration":62.0},{"iox::measurement":"machine_data","time":1685724204346349521,"host":"491cc20bbe13","load":53.0,"machineID":"machine3","power":210.0,"provider":"Smith Group","temperature":37.0,"topic":"machine\/machine3","vibration":85.0},{"iox::measurement":"machine_data","time":1685724204346383833,"host":"491cc20bbe13","load":51.0,"machineID":"machine1","power":211.0,"provider":"Morales, Robinson and Newman","temperature":37.0,"topic":"machine\/machine1","vibration":87.0},{"iox::measurement":"machine_data","time":1685724204346401291,"host":"491cc20bbe13","load":21.0,"machineID":"machine2","power":193.0,"provider":"Thomas and Sons","temperature":32.0,"topic":"machine\/machine2","vibration":54.0},{"iox::measurement":"machine_data","time":1685724205346821289,"host":"491cc20bbe13","load":53.0,"machineID":"machine3","power":220.0,"provider":"Smith Group","temperature":37.0,"topic":"machine\/machine3","vibration":89.0},{"iox::measurement":"machine_data","time":1685724205346921797,"host":"491cc20bbe13","load":21.0,"machineID":"machine2","power":190.0,"provider":"Thomas and Sons","temperature":34.0,"topic":"machine\/machine2","vibration":75.0},{"iox::measurement":"machine_data","time":1685724205347491870,"host":"491cc20bbe13","load":51.0,"machineID":"machine1","power":214.0,"provider":"Morales, Robinson and Newman","temperature":39.0,"topic":"machine\/machine1","vibration":83.0},{"iox::measurement":"machine_data","time":1685724206347838431,"host":"491cc20bbe13","load":21.0,"machineID":"machine2","power":186.0,"provider":"Thomas and Sons","temperature":29.0,"topic":"machine\/machine2","vibration":79.0},{"iox::measurement":"machine_data","time":1685724206348427616,"host":"491cc20bbe13","load":51.0,"machineID":"machine1","power":200.0,"provider":"Morales, Robinson and Newman","temperature":40.0,"topic":"machine\/machine1","vibration":85.0},{"iox::measurement":"machine_data","time":1685724206348455741,"host":"491cc20bbe13","load":53.0,"machineID":"machine3","power":205.0,"provider":"Smith Group","temperature":40.0,"topic":"machine\/machine3","vibration":80.0},{"iox::measurement":"machine_data","time":1685724207348535696,"host":"491cc20bbe13","load":21.0,"machineID":"machine2","power":189.0,"provider":"Thomas and Sons","temperature":34.0,"topic":"machine\/machine2","vibration":79.0},{"iox::measurement":"machine_data","time":1685724207349173287,"host":"491cc20bbe13","load":51.0,"machineID":"machine1","power":215.0,"provider":"Morales, Robinson and Newman","temperature":36.0,"topic":"machine\/machine1","vibration":86.0},{"iox::measurement":"machine_data","time":1685724207349217288,"host":"491cc20bbe13","load":53.0,"machineID":"machine3","power":213.0,"provider":"Smith Group","temperature":39.0,"topic":"machine\/machine3","vibration":83.0},{"iox::measurement":"machine_data","time":1685724208348964054,"host":"491cc20bbe13","load":21.0,"machineID":"machine2","power":191.0,"provider":"Thomas and Sons","temperature":31.0,"topic":"machine\/machine2","vibration":63.0},{"iox::measurement":"machine_data","time":1685724208349958403,"host":"491cc20bbe13","load":51.0,"machineID":"machine1","power":211.0,"provider":"Morales, Robinson and Newman","temperature":35.0,"topic":"machine\/machine1","vibration":84.0},{"iox::measurement":"machine_data","time":1685724208350213700,"host":"491cc20bbe13","load":53.0,"machineID":"machine3","power":201.0,"provider":"Smith Group","temperature":38.0,"topic":"machine\/machine3","vibration":81.0},{"iox::measurement":"machine_data","time":1685724209349501656,"host":"491cc20bbe13","load":21.0,"machineID":"machine2","power":190.0,"provider":"Thomas and Sons","temperature":30.0,"topic":"machine\/machine2","vibration":70.0},{"iox::measurement":"machine_data","time":1685724209350320992,"host":"491cc20bbe13","load":51.0,"machineID":"machine1","power":209.0,"provider":"Morales, Robinson and Newman","temperature":35.0,"topic":"machine\/machine1","vibration":88.0},{"iox::measurement":"machine_data","time":1685724209350858026,"host":"491cc20bbe13","load":53.0,"machineID":"machine3","power":204.0,"provider":"Smith Group","temperature":36.0,"topic":"machine\/machine3","vibration":82.0},{"iox::measurement":"machine_data","time":1685724210350640276,"host":"491cc20bbe13","load":21.0,"machineID":"machine2","power":197.0,"provider":"Thomas and Sons","temperature":30.0,"topic":"machine\/machine2","vibration":55.0},{"iox::measurement":"machine_data","time":1685724210351158057,"host":"491cc20bbe13","load":51.0,"machineID":"machine1","power":214.0,"provider":"Morales, Robinson and Newman","temperature":36.0,"topic":"machine\/machine1","vibration":90.0},{"iox::measurement":"machine_data","time":1685724210351331667,"host":"491cc20bbe13","load":53.0,"machineID":"machine3","power":210.0,"provider":"Smith Group","temperature":39.0,"topic":"machine\/machine3","vibration":85.0},{"iox::measurement":"machine_data","time":1685724211351859842,"host":"491cc20bbe13","load":21.0,"machineID":"machine2","power":188.0,"provider":"Thomas and Sons","temperature":29.0,"topic":"machine\/machine2","vibration":65.0},{"iox::measurement":"machine_data","time":1685724211352098083,"host":"491cc20bbe13","load":51.0,"machineID":"machine1","power":214.0,"provider":"Morales, Robinson and Newman","temperature":35.0,"topic":"machine\/machine1","vibration":86.0},{"iox::measurement":"machine_data","time":1685724211352141060,"host":"491cc20bbe13","load":53.0,"machineID":"machine3","power":213.0,"provider":"Smith Group","temperature":37.0,"topic":"machine\/machine3","vibration":84.0},{"iox::measurement":"machine_data","time":1685724212352201239,"host":"491cc20bbe13","load":21.0,"machineID":"machine2","power":195.0,"provider":"Thomas and Sons","temperature":32.0,"topic":"machine\/machine2","vibration":56.0},{"iox::measurement":"machine_data","time":1685724212352517261,"host":"491cc20bbe13","load":51.0,"machineID":"machine1","power":205.0,"provider":"Morales, Robinson and Newman","temperature":37.0,"topic":"machine\/machine1","vibration":84.0},{"iox::measurement":"machine_data","time":1685724212352589820,"host":"491cc20bbe13","load":53.0,"machineID":"machine3","power":215.0,"provider":"Smith Group","temperature":36.0,"topic":"machine\/machine3","vibration":85.0},{"iox::measurement":"machine_data","time":1685724213352642145,"host":"491cc20bbe13","load":21.0,"machineID":"machine2","power":187.0,"provider":"Thomas and Sons","temperature":34.0,"topic":"machine\/machine2","vibration":68.0},{"iox::measurement":"machine_data","time":1685724213353162708,"host":"491cc20bbe13","load":51.0,"machineID":"machine1","power":218.0,"provider":"Morales, Robinson and Newman","temperature":35.0,"topic":"machine\/machine1","vibration":82.0},{"iox::measurement":"machine_data","time":1685724213353192522,"host":"491cc20bbe13","load":53.0,"machineID":"machine3","power":205.0,"provider":"Smith Group","temperature":36.0,"topic":"machine\/machine3","vibration":89.0},{"iox::measurement":"machine_data","time":1685724214353255548,"host":"491cc20bbe13","load":21.0,"machineID":"machine2","power":197.0,"provider":"Thomas and Sons","temperature":31.0,"topic":"machine\/machine2","vibration":50.0},{"iox::measurement":"machine_data","time":1685724214353521359,"host":"491cc20bbe13","load":51.0,"machineID":"machine1","power":205.0,"provider":"Morales, Robinson and Newman","temperature":38.0,"topic":"machine\/machine1","vibration":80.0},{"iox::measurement":"machine_data","time":1685724214353592745,"host":"491cc20bbe13","load":53.0,"machineID":"machine3","power":202.0,"provider":"Smith Group","temperature":38.0,"topic":"machine\/machine3","vibration":83.0},{"iox::measurement":"machine_data","time":1685724215353829588,"host":"491cc20bbe13","load":21.0,"machineID":"machine2","power":186.0,"provider":"Thomas and Sons","temperature":30.0,"topic":"machine\/machine2","vibration":68.0},{"iox::measurement":"machine_data","time":1685724215354477346,"host":"491cc20bbe13","load":53.0,"machineID":"machine3","power":215.0,"provider":"Smith Group","temperature":36.0,"topic":"machine\/machine3","vibration":84.0},{"iox::measurement":"machine_data","time":1685724215354644118,"host":"491cc20bbe13","load":51.0,"machineID":"machine1","power":206.0,"provider":"Morales, Robinson and Newman","temperature":38.0,"topic":"machine\/machine1","vibration":85.0}] \ No newline at end of file From d197f2357c99a106e272ec80ca49efe415f79bf3 Mon Sep 17 00:00:00 2001 From: karel rehor Date: Fri, 17 Apr 2026 17:24:15 +0200 Subject: [PATCH 03/36] chore: clean-up data updater.py --- Examples/write/source_data/updater.py | 77 --------------------------- 1 file changed, 77 deletions(-) diff --git a/Examples/write/source_data/updater.py b/Examples/write/source_data/updater.py index c936597..df3c76d 100644 --- a/Examples/write/source_data/updater.py +++ b/Examples/write/source_data/updater.py @@ -1,14 +1,8 @@ import pandas as pd -import pyarrow.feather as feather import time -from datetime import datetime, timedelta import logging import random import numpy as np -import pyarrow as pa -import pyarrow.compute as pac - -# TODO output as orc, json, csv etc. def update_measurement_name(source: pd.DataFrame, measurment_name: str): count = 0 @@ -18,29 +12,18 @@ def update_measurement_name(source: pd.DataFrame, measurment_name: str): def update_timestamps(source: pd.DataFrame): - # now = datetime.now() now = time.time_ns(); interval = 333_000_000 # ms grit = random.randrange(0, 1_000_000) interval = interval + grit - # current = now - (timedelta(milliseconds=interval) * len(source["time"])) current = np.int64(now - interval * len(source["time"])) - print(f"DEBUG source['time'] {source['time'][0]} {type(source['time'][0]).__name__}") - print(f"DEBUG source[\"time\"].dtype {source["time"].dtype}") ts_type = type(source['time'][0]).__name__ count = 0 for _ in source['time']: if ts_type in ["int", "int32", "int64"]: - #print(f"current {current} as type {type(current).__name__}") source.loc[count, "time"] = current - # source.at[count, "time"] = current - # source.at[count, "time"].astype('int64') - #print(f"DEBUG source.loc[count, \"time\"] {source.loc[count, "time"]} {type(source.loc[count, "time"]).__name__}") - # count += 1 else: - # ts = pd.Timestamp(current) - # print(f"{data} {type(data)} ts {ts}") source.loc[count, "time"] = pd.Timestamp(current) count += 1 current = current + interval @@ -79,68 +62,8 @@ def json_update(): logging.info("Updating json") json_df = pd.read_json('./out.json') update_timestamps(json_df) - print(f"DEBUG json_df[\"time\"][0] {json_df['time'][0]} as type {type(json_df['time'][0])}") json_df.to_json('./out_update.json', orient='records', index=False) -def explore(): - logging.info("Explore") - read_frame = feather.read_feather('./out.feather') - # print(f"DEBUG read_table:\n{read_table}") - c = len(read_frame["time"]) - print(f"DEBUG c: {c} type: {type(c)} \n") - print(f"DEBUG read_frame length: {read_frame.count()} type: {type(read_frame.count())}") - print(f"DEBUG read_frame:\n{read_frame}") - - now = datetime.now() - interval = 334 # ms - # current = now - (timedelta(milliseconds=interval) * read_frame.count()) - current = now - (timedelta(milliseconds=interval) * len(read_frame["time"])) - - count = 0 - # new_ts = [] - for data in read_frame['time']: - print(f"{data} {type(data)} current {current}") - read_frame.loc[count, "time"] = current - # read_frame['time'][count] = data - # new_ts.append(current) - count += 1 - current = current + timedelta(milliseconds=interval) - - # for data in read_frame['time']: - # print(f"{data}" ) - - print(f"DEBUG read_frame:\n{read_frame}") - - # as feather - # feather.write_feather(read_frame, './out_test.feather') - read_frame.to_feather('./out_test.feather') - - # as csv - count = 0 - for _ in read_frame['iox::measurement']: - read_frame.loc[count, "iox::measurement"] = "machine_data_csv" - count += 1 - - read_frame.to_csv('./out_test.csv', columns=[ - "iox::measurement","time","host","load","machineID","power","provider","temperature","topic","vibration" - ], index=False) - - # as json - read_frame.to_json('./out_test.json', orient='records',) - # test_frame = read_frame.pivot(index='iox::measurement', columns='time', values=[ - # 'iox::measurement','host','load','machineID','power','provider','temperature','topic','vibration' - # ]) - # test_frame = pd.pivot_table(read_frame,values="iox::measurement", index="time", aggfunc=None) - - # print(f"DEBUG test_frame:\n{test_frame}") - # test_frame.to_json('./out_test3.json') - - # as orc - read_frame.to_orc('./out_test.orc') - - test_frame = pd.read_csv('./out.csv') - print(f"DEBUG test_frame:\n{test_frame}") - if __name__ == "__main__": logging.basicConfig(level=logging.INFO) feather_update() From 256a93b84f33d43b37662a5d975b5fa16db953aa Mon Sep 17 00:00:00 2001 From: karel rehor Date: Fri, 17 Apr 2026 17:38:25 +0200 Subject: [PATCH 04/36] chore: tidy flake in recently added code. --- Examples/core/basic-write.py | 5 ----- Examples/write/fileimport.py | 7 ++++--- Examples/write/source_data/updater.py | 12 ++++++++++-- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/Examples/core/basic-write.py b/Examples/core/basic-write.py index 14f2f96..91c2803 100644 --- a/Examples/core/basic-write.py +++ b/Examples/core/basic-write.py @@ -3,11 +3,6 @@ from Examples.config import Config from influxdb_client_3 import InfluxDBClient3, Point -#client = InfluxDBClient3( -# token="mGbL-OJ2kxYqvbIL9jQOOg2VJLhf16hh-xn-XJe3RUKrI5cewOAy80L5cVIzG0vh7dLLckZkpYfvExgoMBXLFA==", -# host="eu-central-1-1.aws.cloud2.influxdata.com", -# database="pokemon-codex") - config = Config() client = InfluxDBClient3( token=config.token, diff --git a/Examples/write/fileimport.py b/Examples/write/fileimport.py index eb51656..52fb8a5 100644 --- a/Examples/write/fileimport.py +++ b/Examples/write/fileimport.py @@ -2,12 +2,13 @@ import os import influxdb_client_3 as InfluxDBClient3 -from influxdb_client_3 import write_client_options, WriteOptions, InfluxDBError, file_parser_options +from influxdb_client_3 import write_client_options, WriteOptions, InfluxDBError from Examples.config import Config data_types = ["csv", "json", "feather", "orc", "parquet"] + class BatchingCallback(object): def __init__(self): @@ -64,7 +65,7 @@ def main(file_types=("csv",)) -> None: debug=True) as client: for type in file_types: - if not type in data_types: + if type not in data_types: logging.error(f"File type {type} not supported.") continue @@ -83,4 +84,4 @@ def main(file_types=("csv",)) -> None: if __name__ == "__main__": - main(("feather","parquet","orc","csv","json")) + main(("feather", "parquet", "orc", "csv", "json")) diff --git a/Examples/write/source_data/updater.py b/Examples/write/source_data/updater.py index df3c76d..d6c5526 100644 --- a/Examples/write/source_data/updater.py +++ b/Examples/write/source_data/updater.py @@ -4,16 +4,18 @@ import random import numpy as np + def update_measurement_name(source: pd.DataFrame, measurment_name: str): count = 0 for _ in source["iox::measurement"]: source.loc[count, "iox::measurement"] = measurment_name count += 1 + def update_timestamps(source: pd.DataFrame): - now = time.time_ns(); - interval = 333_000_000 # ms + now = time.time_ns() + interval = 333_000_000 # ms grit = random.randrange(0, 1_000_000) interval = interval + grit current = np.int64(now - interval * len(source["time"])) @@ -31,6 +33,7 @@ def update_timestamps(source: pd.DataFrame): if ts_type in ["int", "int32", "int64"]: source["time"] = source["time"].astype("int64") + def feather_update(): logging.info("Updating feather") f_df = pd.read_feather('./out.feather') @@ -38,6 +41,7 @@ def feather_update(): update_measurement_name(f_df, "machine_data_feather") f_df.to_feather('./out_update.feather') + def orc_update(): logging.info("Updating orc") o_df = pd.read_orc('./out.orc') @@ -45,6 +49,7 @@ def orc_update(): update_measurement_name(o_df, "machine_data_orc") o_df.to_orc('./out_update.orc', index=False) + def parquet_update(): logging.info("Updating parquet") p_df = pd.read_parquet('./out.parquet') @@ -52,18 +57,21 @@ def parquet_update(): update_measurement_name(p_df, "machine_data_parquet") p_df.to_parquet('./out_update.parquet', index=False) + def csv_update(): logging.info("Updating csv") csv_df = pd.read_csv('./out.csv') update_timestamps(csv_df) csv_df.to_csv('./out_update.csv', index=False) + def json_update(): logging.info("Updating json") json_df = pd.read_json('./out.json') update_timestamps(json_df) json_df.to_json('./out_update.json', orient='records', index=False) + if __name__ == "__main__": logging.basicConfig(level=logging.INFO) feather_update() From eabad522431f50d02ca7078d6883039888f922f2 Mon Sep 17 00:00:00 2001 From: karel rehor Date: Mon, 20 Apr 2026 14:20:57 +0200 Subject: [PATCH 05/36] chore: refactor/remove pokemon-trainer Examples --- Examples/README.md | 41 +- Examples/core/basic-write.py | 90 +- .../basic-write-errorhandling.py | 73 - .../basic-write-writeoptions.py | 83 - Examples/pokemon-trainer/cookbook.ipynb | 6077 ----------------- Examples/pokemon-trainer/kanto.parquet | Bin 79453 -> 0 bytes Examples/pokemon-trainer/pandas-write.py | 73 - .../write-batching-flight-calloptions.py | 108 - Examples/pokemon-trainer/write-batching.py | 101 - .../source_data}/pokemon.csv | 0 Examples/write/writeoptions.py | 84 + 11 files changed, 160 insertions(+), 6570 deletions(-) delete mode 100644 Examples/pokemon-trainer/basic-write-errorhandling.py delete mode 100644 Examples/pokemon-trainer/basic-write-writeoptions.py delete mode 100644 Examples/pokemon-trainer/cookbook.ipynb delete mode 100644 Examples/pokemon-trainer/kanto.parquet delete mode 100644 Examples/pokemon-trainer/pandas-write.py delete mode 100644 Examples/pokemon-trainer/write-batching-flight-calloptions.py delete mode 100644 Examples/pokemon-trainer/write-batching.py rename Examples/{pokemon-trainer => write/source_data}/pokemon.csv (100%) create mode 100644 Examples/write/writeoptions.py diff --git a/Examples/README.md b/Examples/README.md index 654f739..c43e3bd 100644 --- a/Examples/README.md +++ b/Examples/README.md @@ -1,6 +1,10 @@ ## Infludb3 Python Examples -First time users will likely want to study the examples in the `./core` directory. Users who work with __Jupyter__ notebooks may want to take a look at `basic-write-query.ipynb` in the `./jupyter` directory. +First time users will likely want to study the examples in the `./core` directory. Users who work with __Jupyter__ notebooks may want to take a look at `basic-write-query.ipynb` in the `./jupyter` directory. + +### Underlying principles + +Influxdb3 uses two transports: one for writing data and another for querying. ### Writing data @@ -13,8 +17,33 @@ TODO - others TODO - delete this section as examples take shape and before creating PR. 1. Want to remove `pokemon-trainer` and refactor examples to `core`, `write` and `query` -2. Keep `file-import` as single file, with source data updatable - see `updater.py` - 1. Note `feather` type writes data using measurement `machine_data` and not `machine_data_feather` - 2. Note `orc` type writes data using measurement `machine_data` and not `machine_data_orc` - 3. Note `parquet` type writes data using measurement `machine_data` and not `machine_data_parquet` -3. Keep one simple example of jupiter notebook \ No newline at end of file + 1. `basic-write-errorhandling.py` can be removed. __DONE__ + - actually shows error handling for query + - duplicates examples `handle_http_error.py` and `handle_query_error.py` + 2. `basic-write-writeoptions.py` to `./write` refactored __DONE__ + 3. `pandas-write.py` can be removed - duplicates `./pandas_write.py` __DONE__ + 4. `write-batching.py` can be removed - duplicates `./batching_example.py` __DONE__ + 5. `write-batching-flight-call-options.py` to be removed __DONE__ + - This is an odd example. Sets write options then performs only a query. Also _flight_ applies only to query API. + - apparently depends on other examples + - write and query options are illustrated in other examples + 6. `cookbook.ipynb` - most of this is migrated to `./jupyter/basic-write-query.ipynb` - can be removed __DONE__ + - what to do about step _write table to parquet file_? - not necessary. +2. Keep `file-import` as single file, with source data updatable - see `updater.py` __DONE__ +3. Keep one simple example of jupiter notebook. __DONE__ +4. Decide what to do with `./community` examples. +5. Root examples + 1. `basic_ssl_examle.py` to `./core` + 2. `batching_example.py` to `./write` + 3. `cloud_dedicated_query.py` - is it necessary to have specific _cloud_dedicated_ examples? Can this simply be documented in `README.md` or in code comments? + 4. `cloud_dedicated_write.py` - is it necessary to have specific _cloud_dedicated_ examples? Can this simply be documented in `README.md` or in code comments? + 5. `config.py` - universal configuration file. Keep as is. + 6. `example.csv` - where is this used? It doesn't seem to be used in any example... ??? + 7. `flight_options_example.py` - to `./query` + 8. `handle_http_error.py` - to `./write` + 9. `handle_query_error.py` - to `./query` + 10. `pandas_write.py` - to `./write` + 11. `query_async.py` - to `./query` + 12. `query_type.py` - possible rename. `_type` token in name is unclear. Study closer. Move to `./query` + 13. `query_with_middleware.py` - to `./query` + 14. `timeouts.py` - to `./core` diff --git a/Examples/core/basic-write.py b/Examples/core/basic-write.py index 91c2803..475dac7 100644 --- a/Examples/core/basic-write.py +++ b/Examples/core/basic-write.py @@ -24,59 +24,51 @@ except Exception as e: print(f"Error writing point: {e}") -data = [] -# Adding first point -data.append( - Point("caught") - .tag("trainer", "ash") - .tag("id", "0006") - .tag("num", "1") - .field("caught", "charizard") - .field("level", 10) - .field("attack", 30) - .field("defense", 40) - .field("hp", 200) - .field("speed", 10) - .field("type1", "fire") - .field("type2", "flying") - .time(now) -) +data = [Point("caught") # first point + .tag("trainer", "ash") + .tag("id", "0006") + .tag("num", "1") + .field("caught", "charizard") + .field("level", 10) + .field("attack", 30) + .field("defense", 40) + .field("hp", 200) + .field("speed", 10) + .field("type1", "fire") + .field("type2", "flying") + .time(now), -# Adding second point -data.append( - Point("caught") - .tag("trainer", "ash") - .tag("id", "0007") - .tag("num", "2") - .field("caught", "bulbasaur") - .field("level", 12) - .field("attack", 31) - .field("defense", 31) - .field("hp", 190) - .field("speed", 11) - .field("type1", "grass") - .field("type2", "poison") - .time(now) -) + Point("caught") # second point + .tag("trainer", "ash") + .tag("id", "0007") + .tag("num", "2") + .field("caught", "bulbasaur") + .field("level", 12) + .field("attack", 31) + .field("defense", 31) + .field("hp", 190) + .field("speed", 11) + .field("type1", "grass") + .field("type2", "poison") + .time(now), -# Adding third point -data.append( - Point("caught") - .tag("trainer", "ash") - .tag("id", "0008") - .tag("num", "3") - .field("caught", "squirtle") - .field("level", 13) - .field("attack", 29) - .field("defense", 40) - .field("hp", 180) - .field("speed", 13) - .field("type1", "water") - .field("type2", None) - .time(now) -) + Point("caught") # third point + .tag("trainer", "ash") + .tag("id", "0008") + .tag("num", "3") + .field("caught", "squirtle") + .field("level", 13) + .field("attack", 29) + .field("defense", 40) + .field("hp", 180) + .field("speed", 13) + .field("type1", "water") + .field("type2", None) + .time(now) + ] try: client.write(data) + print(f"Write success: {len(data)} points!") except Exception as e: print(f"Error writing point: {e}") diff --git a/Examples/pokemon-trainer/basic-write-errorhandling.py b/Examples/pokemon-trainer/basic-write-errorhandling.py deleted file mode 100644 index 14be2c9..0000000 --- a/Examples/pokemon-trainer/basic-write-errorhandling.py +++ /dev/null @@ -1,73 +0,0 @@ -import datetime - -from influxdb_client_3 import InfluxDBClient3, Point, SYNCHRONOUS, write_client_options - -wco = write_client_options(write_options=SYNCHRONOUS) - -with InfluxDBClient3( - token="", - host="eu-central-1-1.aws.cloud2.influxdata.com", - database="pokemon-codex", write_client_options=wco) as client: - now = datetime.datetime.now(datetime.timezone.utc) - - data = Point("caught").tag("trainer", "ash").tag("id", "0006").tag("num", "1") \ - .field("caught", "charizard") \ - .field("level", 10).field("attack", 30) \ - .field("defense", 40).field("hp", 200) \ - .field("speed", 10) \ - .field("type1", "fire").field("type2", "flying") \ - .time(now) - - data = [] - # Adding first point - data.append( - Point("caught") - .tag("trainer", "ash") - .tag("id", "0006") - .tag("num", "1") - .field("caught", "charizard") - .field("level", 10) - .field("attack", 30) - .field("defense", 40) - .field("hp", 200) - .field("speed", 10) - .field("type1", "fire") - .field("type2", "flying") - .time(now) - ) - - # Bad point - data.append( - Point("caught") - .tag("trainer", "ash") - .tag("id", "0008") - .tag("num", "3") - .field("caught", "squirtle") - .field("level", 13) - .field("attack", 29) - .field("defense", 40) - .field("hp", 180) - .field("speed", 13) - .field("type1", "water") - .field("type2", None) - .time(now) - ) - - try: - client.write(data) - except Exception as e: - print(f"Error writing point: {e}") - - # Good Query - try: - table = client.query(query='''SELECT * FROM "caught" WHERE time > now() - 5m''', language='influxql') - print(table) - except Exception as e: - print(f"Error querying data: {e}") - - # Bad Query - not a sql query - try: - table = client.query(query='''SELECT * FROM "caught" WHERE time > now() - 5m''', language='sql') - print(table) - except Exception as e: - print(f"Error querying data: {e}") diff --git a/Examples/pokemon-trainer/basic-write-writeoptions.py b/Examples/pokemon-trainer/basic-write-writeoptions.py deleted file mode 100644 index 97bc399..0000000 --- a/Examples/pokemon-trainer/basic-write-writeoptions.py +++ /dev/null @@ -1,83 +0,0 @@ -import datetime - -from influxdb_client_3 import InfluxDBClient3, Point, SYNCHRONOUS, write_client_options - -wco = write_client_options(write_options=SYNCHRONOUS) - -with InfluxDBClient3( - token="", - host="eu-central-1-1.aws.cloud2.influxdata.com", - database="pokemon-codex", - write_client_options=wco, - debug=True) as client: - now = datetime.datetime.now(datetime.timezone.utc) - - data = Point("caught").tag("trainer", "ash").tag("id", "0006").tag("num", "1") \ - .field("caught", "charizard") \ - .field("level", 10).field("attack", 30) \ - .field("defense", 40).field("hp", 200) \ - .field("speed", 10) \ - .field("type1", "fire").field("type2", "flying") \ - .time(now) - - try: - client.write(data) - except Exception as e: - print(f"Error writing point: {e}") - - data = [] - # Adding first point - data.append( - Point("caught") - .tag("trainer", "ash") - .tag("id", "0006") - .tag("num", "1") - .field("caught", "charizard") - .field("level", 10) - .field("attack", 30) - .field("defense", 40) - .field("hp", 200) - .field("speed", 10) - .field("type1", "fire") - .field("type2", "flying") - .time(now) - ) - - # Adding second point - data.append( - Point("caught") - .tag("trainer", "ash") - .tag("id", "0007") - .tag("num", "2") - .field("caught", "bulbasaur") - .field("level", 12) - .field("attack", 31) - .field("defense", 31) - .field("hp", 190) - .field("speed", 11) - .field("type1", "grass") - .field("type2", "poison") - .time(now) - ) - - # Adding third point - data.append( - Point("caught") - .tag("trainer", "ash") - .tag("id", "0008") - .tag("num", "3") - .field("caught", "squirtle") - .field("level", 13) - .field("attack", 29) - .field("defense", 40) - .field("hp", 180) - .field("speed", 13) - .field("type1", "water") - .field("type2", None) - .time(now) - ) - - try: - client.write(data) - except Exception as e: - print(f"Error writing point: {e}") diff --git a/Examples/pokemon-trainer/cookbook.ipynb b/Examples/pokemon-trainer/cookbook.ipynb deleted file mode 100644 index caa956a..0000000 --- a/Examples/pokemon-trainer/cookbook.ipynb +++ /dev/null @@ -1,6077 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# The Pokemon Cookbook\n", - "This cookbook teaches you the concepts of the InfluxDB 3.0 Python Client library using a novel example of Pokemon data. The scenerio is to keep track of each trainer and the number of different pokemon they have caught.\n", - "\n", - "

\n", - "\n", - "

" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "\n", - "# Here we include all the imports required from influxdb_client_3\n", - "from influxdb_client_3 import InfluxDBClient3, InfluxDBError, WriteOptions, write_client_options\n", - "import pandas as pd\n", - "import random" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Writing Data\n", - "The first step is to write data into InfluxDB. We will use the `write_api` to write data into InfluxDB. In this example we are going to utilise `batching mode` to write data in batches. This is the most efficient way to write data into InfluxDB. To do this we are going to first setup some paramters for our client." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "# This class handles the callbacks for the batching\n", - "class BatchingCallback(object):\n", - "\n", - " def success(self, conf, data: str):\n", - " print(f\"Written batch: {conf}\")\n", - "\n", - " def error(self, conf, data: str, exception: InfluxDBError):\n", - " print(f\"Cannot write batch: {conf}, data: {data} due: {exception}\")\n", - "\n", - " def retry(self, conf, data: str, exception: InfluxDBError):\n", - " print(f\"Retryable error occurs for batch: {conf}, data: {data} retry: {exception}\")\n", - "\n", - "callback = BatchingCallback()\n", - "\n", - "# This is the configuration for the batching. This is wrapped in a WriteOptions object. Within this example you\n", - "# can see the different options that can be set for the batching.\n", - "# Batch size is the number of points to write before the batch is written to the server.\n", - "# Flush interval is the time in milliseconds to wait before the batch is written to the server.\n", - "# Jitter interval is the time in milliseconds to wait before the batch is written to the server.\n", - "# Retry interval is the time in milliseconds to wait before retrying a failed batch.\n", - "# Max retries is the maximum number of times to retry a failed batch.\n", - "# exponential base is the base for the exponential retry delay.\n", - "write_options = WriteOptions(batch_size=100,\n", - " flush_interval=10_000,\n", - " jitter_interval=2_000,\n", - " retry_interval=5_000,\n", - " max_retries=5,\n", - " max_retry_delay=30_000,\n", - " exponential_base=2)\n", - "\n", - "\n", - "# This is the configuration for the write client. This is wrapped in a WriteClientOptions object.\n", - "# As you can see we incldue the BatchingCallback object we created earlier, plus the write_options.\n", - "wco = write_client_options(success_callback=callback.success,\n", - " error_callback=callback.error,\n", - " retry_callback=callback.retry,\n", - " write_options=write_options\n", - " )" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Client Setup\n", - "Now that we have done the inital configurations of our write paramters its time to include these within our client initalization. The InfluxDB 3.0 Client can both write and query data. For now we will use it to write data based upon our configuration." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "# In this example we are using the InfluxDBClient3 object to connect to the InfluxDB Cloud instance.\n", - "# We are also passing in the write_client_options we created earlier.\n", - "# The token and host are required to connect to the InfluxDB Serverless instance.\n", - "# Note: that Org is optional with Dedicated instances.\n", - "client = InfluxDBClient3(\n", - " token=\"phVXVXPBZ12vyNvVGJJX6vngFa4zyCYIzgDTYRJsPyIUSi3r78agctL2ksCH1GlUxtIWdGFWL5ScxQxUnbQOAQ==\",\n", - " host=\"eu-central-1-1.aws.cloud2.influxdata.com\",\n", - " database=\"pokemon-codex\", enable_gzip=True, write_client_options=wco)\n", - "\n", - "now = pd.Timestamp.now(tz='UTC').floor('ms')\n", - "\n", - "# Lists of possible trainers\n", - "trainers = [\"ash\", \"brock\", \"misty\", \"gary\", \"jessie\", \"james\"]\n", - "\n", - "# Read the CSV into a DataFrame. (Credit to @ritchie46 for the dataset)\n", - "pokemon_df = pd.read_csv(\"./pokemon.csv\")\n", - "\n", - "# Creating an empty list to store the data\n", - "data = []\n", - "\n", - "# Dictionary to keep track of the number of times each trainer has caught each Pokémon\n", - "trainer_pokemon_counts = {}\n", - "\n", - "# Number of entries we want to create\n", - "num_entries = 1000" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Generate some data\n", - "Now that we have our client setup lets start generating some data we can write to InfluxDB. Following the Pokemon example we will create a list of trainers and the number of pokemon they have caught. Trainers will catch pokemon randomly selected from our list stored within the Pandas DataFrame `pokemon_df`." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
traineridnumnamelevelattackdefensehpspeedtype1type2
timestamp
2023-07-27 10:44:44.001000+00:00ash01041Cubone950955035GroundNaN
2023-07-27 10:44:44.001000+00:00brock01461Moltres19100909090FireFlying
2023-07-27 10:44:44.001000+00:00jessie00711Victreebel6105658070GrassPoison
2023-07-27 10:44:44.001000+00:00jessie00701Weepinbell1990506555GrassPoison
2023-07-27 10:44:44.001000+00:00brock01021Exeggcute1340806040GrassPsychic
....................................
2023-07-27 10:44:44.001000+00:00james01385Omanyte20401003535RockWater
2023-07-27 10:44:44.001000+00:00misty00563Mankey2080354070FightingNaN
2023-07-27 10:44:44.001000+00:00gary01342Vaporeon7656013065WaterNaN
2023-07-27 10:44:44.001000+00:00jessie00012Bulbasaur749494545GrassPoison
2023-07-27 10:44:44.001000+00:00james01113Rhyhorn985958025GroundRock
\n", - "

1000 rows × 11 columns

\n", - "
" - ], - "text/plain": [ - " trainer id num name level attack \\\n", - "timestamp \n", - "2023-07-27 10:44:44.001000+00:00 ash 0104 1 Cubone 9 50 \n", - "2023-07-27 10:44:44.001000+00:00 brock 0146 1 Moltres 19 100 \n", - "2023-07-27 10:44:44.001000+00:00 jessie 0071 1 Victreebel 6 105 \n", - "2023-07-27 10:44:44.001000+00:00 jessie 0070 1 Weepinbell 19 90 \n", - "2023-07-27 10:44:44.001000+00:00 brock 0102 1 Exeggcute 13 40 \n", - "... ... ... .. ... ... ... \n", - "2023-07-27 10:44:44.001000+00:00 james 0138 5 Omanyte 20 40 \n", - "2023-07-27 10:44:44.001000+00:00 misty 0056 3 Mankey 20 80 \n", - "2023-07-27 10:44:44.001000+00:00 gary 0134 2 Vaporeon 7 65 \n", - "2023-07-27 10:44:44.001000+00:00 jessie 0001 2 Bulbasaur 7 49 \n", - "2023-07-27 10:44:44.001000+00:00 james 0111 3 Rhyhorn 9 85 \n", - "\n", - " defense hp speed type1 type2 \n", - "timestamp \n", - "2023-07-27 10:44:44.001000+00:00 95 50 35 Ground NaN \n", - "2023-07-27 10:44:44.001000+00:00 90 90 90 Fire Flying \n", - "2023-07-27 10:44:44.001000+00:00 65 80 70 Grass Poison \n", - "2023-07-27 10:44:44.001000+00:00 50 65 55 Grass Poison \n", - "2023-07-27 10:44:44.001000+00:00 80 60 40 Grass Psychic \n", - "... ... ... ... ... ... \n", - "2023-07-27 10:44:44.001000+00:00 100 35 35 Rock Water \n", - "2023-07-27 10:44:44.001000+00:00 35 40 70 Fighting NaN \n", - "2023-07-27 10:44:44.001000+00:00 60 130 65 Water NaN \n", - "2023-07-27 10:44:44.001000+00:00 49 45 45 Grass Poison \n", - "2023-07-27 10:44:44.001000+00:00 95 80 25 Ground Rock \n", - "\n", - "[1000 rows x 11 columns]" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "from IPython.display import display, HTML\n", - "\n", - "# Generating random data\n", - "for i in range(num_entries):\n", - " trainer = random.choice(trainers)\n", - " \n", - " # Randomly select a row from pokemon_df\n", - " random_pokemon = pokemon_df.sample().iloc[0]\n", - " caught = random_pokemon['Name']\n", - " \n", - " # Count the number of times this trainer has caught this Pokémon\n", - " if (trainer, caught) in trainer_pokemon_counts:\n", - " trainer_pokemon_counts[(trainer, caught)] += 1\n", - " else:\n", - " trainer_pokemon_counts[(trainer, caught)] = 1\n", - " \n", - " # Get the number for this combination of trainer and Pokémon\n", - " num = trainer_pokemon_counts[(trainer, caught)]\n", - "\n", - " entry = {\n", - " \"trainer\": trainer,\n", - " \"id\": f\"{0000 + random_pokemon['#']:04d}\",\n", - " \"num\": str(num),\n", - " \"name\": caught,\n", - " \"level\": random.randint(5, 20),\n", - " \"attack\": random_pokemon['Attack'],\n", - " \"defense\": random_pokemon['Defense'],\n", - " \"hp\": random_pokemon['HP'],\n", - " \"speed\": random_pokemon['Speed'],\n", - " \"type1\": random_pokemon['Type 1'],\n", - " \"type2\": random_pokemon['Type 2'],\n", - " \"timestamp\": now\n", - " }\n", - " data.append(entry)\n", - "\n", - "# Convert the list of dictionaries to a DataFrame\n", - "caught_pokemon_df = pd.DataFrame(data).set_index('timestamp')\n", - "\n", - "# Print the DataFrame\n", - "display(caught_pokemon_df)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Write Data to InfluxDB\n", - "We will now write our newley created trainer data to InfluxDB. To do this we simply call client.write and pass in our dataframe. We then provide a static measurement name of `caught`. We also provide a series of tags to help identify our data. In this case we use the columns `['trainer', 'id', 'num']`. Note that we didn't provide our time column this is due to the fact we set this column as our `dataframe_index`. This means that the index column will be used as the time column." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Written batch: ('pokemon-codex', '6a841c0c08328fb1', 'ns')\n" - ] - } - ], - "source": [ - "import time\n", - "\n", - "try:\n", - " client.write(caught_pokemon_df, data_frame_measurement_name='caught',\n", - " data_frame_tag_columns=['trainer', 'id', 'num'])\n", - "except Exception as e:\n", - " print(f\"Error writing point: {e}\")\n", - "\n", - "# Wait for the batch to be written\n", - "time.sleep(2)\n", - " " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Querying Data\n", - "We have now stored 1000 registered pokemon catches within InfluxDB. We can now query this data using the InfluxDB 3.0 Python Client to gain some insights into our data. We are going to use Plotly to visualise our data." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
table_catalogtable_schematable_namecolumn_namedata_typeis_nullable
0publicioxcaughtattackInt64YES
1publicioxcaughtdefenseInt64YES
2publicioxcaughthpInt64YES
3publicioxcaughtidDictionary(Int32, Utf8)YES
4publicioxcaughtlevelInt64YES
5publicioxcaughtnameUtf8YES
6publicioxcaughtnumDictionary(Int32, Utf8)YES
7publicioxcaughtspeedInt64YES
8publicioxcaughttimeTimestamp(Nanosecond, None)NO
9publicioxcaughttrainerDictionary(Int32, Utf8)YES
10publicioxcaughttype1Utf8YES
11publicioxcaughttype2Utf8YES
\n", - "
" - ], - "text/plain": [ - " table_catalog table_schema table_name column_name \\\n", - "0 public iox caught attack \n", - "1 public iox caught defense \n", - "2 public iox caught hp \n", - "3 public iox caught id \n", - "4 public iox caught level \n", - "5 public iox caught name \n", - "6 public iox caught num \n", - "7 public iox caught speed \n", - "8 public iox caught time \n", - "9 public iox caught trainer \n", - "10 public iox caught type1 \n", - "11 public iox caught type2 \n", - "\n", - " data_type is_nullable \n", - "0 Int64 YES \n", - "1 Int64 YES \n", - "2 Int64 YES \n", - "3 Dictionary(Int32, Utf8) YES \n", - "4 Int64 YES \n", - "5 Utf8 YES \n", - "6 Dictionary(Int32, Utf8) YES \n", - "7 Int64 YES \n", - "8 Timestamp(Nanosecond, None) NO \n", - "9 Dictionary(Int32, Utf8) YES \n", - "10 Utf8 YES \n", - "11 Utf8 YES " - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# These are just some library imports for Plotly so we can make use of the interactive graphs.\n", - "import plotly.express as px\n", - "import plotly.io as pio\n", - "pio.renderers.default = \"vscode\"\n", - "\n", - "# Lets start with a simple query to understand our schema.\n", - "query = '''SHOW COLUMNS FROM caught'''\n", - "\n", - "# We can use the query method to run a query against the database.\n", - "# Under the hood this creates a flight ticket and uses the FlightClient to run the query.\n", - "# For this example we are using the pandas mode, which will return a pandas DataFrame.\n", - "# Language also allows us to specify the query language, in this case we are using SQL.\n", - "table = client.query(query=query, language='sql', mode='pandas')\n", - "\n", - "display(table)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Simple InfluxQL Query\n", - "The first query we will run is a simple InfluxQL query to get the number of pokemon caught by each trainer. We will then use Plotly to visualise this data." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.plotly.v1+json": { - "config": { - "plotlyServerURL": "https://plot.ly" - }, - "data": [ - { - "alignmentgroup": "True", - "hovertemplate": "trainer=%{x}
count=%{y}", - "legendgroup": "ash", - "marker": { - "color": "#636efa", - "pattern": { - "shape": "" - } - }, - "name": "ash", - "offsetgroup": "ash", - "orientation": "v", - "showlegend": true, - "textposition": "auto", - "type": "bar", - "x": [ - "ash" - ], - "xaxis": "x", - "y": [ - 146 - ], - "yaxis": "y" - }, - { - "alignmentgroup": "True", - "hovertemplate": "trainer=%{x}
count=%{y}", - "legendgroup": "brock", - "marker": { - "color": "#EF553B", - "pattern": { - "shape": "" - } - }, - "name": "brock", - "offsetgroup": "brock", - "orientation": "v", - "showlegend": true, - "textposition": "auto", - "type": "bar", - "x": [ - "brock" - ], - "xaxis": "x", - "y": [ - 172 - ], - "yaxis": "y" - }, - { - "alignmentgroup": "True", - "hovertemplate": "trainer=%{x}
count=%{y}", - "legendgroup": "gary", - "marker": { - "color": "#00cc96", - "pattern": { - "shape": "" - } - }, - "name": "gary", - "offsetgroup": "gary", - "orientation": "v", - "showlegend": true, - "textposition": "auto", - "type": "bar", - "x": [ - "gary" - ], - "xaxis": "x", - "y": [ - 162 - ], - "yaxis": "y" - }, - { - "alignmentgroup": "True", - "hovertemplate": "trainer=%{x}
count=%{y}", - "legendgroup": "james", - "marker": { - "color": "#ab63fa", - "pattern": { - "shape": "" - } - }, - "name": "james", - "offsetgroup": "james", - "orientation": "v", - "showlegend": true, - "textposition": "auto", - "type": "bar", - "x": [ - "james" - ], - "xaxis": "x", - "y": [ - 162 - ], - "yaxis": "y" - }, - { - "alignmentgroup": "True", - "hovertemplate": "trainer=%{x}
count=%{y}", - "legendgroup": "jessie", - "marker": { - "color": "#FFA15A", - "pattern": { - "shape": "" - } - }, - "name": "jessie", - "offsetgroup": "jessie", - "orientation": "v", - "showlegend": true, - "textposition": "auto", - "type": "bar", - "x": [ - "jessie" - ], - "xaxis": "x", - "y": [ - 155 - ], - "yaxis": "y" - }, - { - "alignmentgroup": "True", - "hovertemplate": "trainer=%{x}
count=%{y}", - "legendgroup": "misty", - "marker": { - "color": "#19d3f3", - "pattern": { - "shape": "" - } - }, - "name": "misty", - "offsetgroup": "misty", - "orientation": "v", - "showlegend": true, - "textposition": "auto", - "type": "bar", - "x": [ - "misty" - ], - "xaxis": "x", - "y": [ - 166 - ], - "yaxis": "y" - } - ], - "layout": { - "barmode": "relative", - "legend": { - "title": { - "text": "trainer" - }, - "tracegroupgap": 0 - }, - "template": { - "data": { - "bar": [ - { - "error_x": { - "color": "#2a3f5f" - }, - "error_y": { - "color": "#2a3f5f" - }, - "marker": { - "line": { - "color": "#E5ECF6", - "width": 0.5 - }, - "pattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - } - }, - "type": "bar" - } - ], - "barpolar": [ - { - "marker": { - "line": { - "color": "#E5ECF6", - "width": 0.5 - }, - "pattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - } - }, - "type": "barpolar" - } - ], - "carpet": [ - { - "aaxis": { - "endlinecolor": "#2a3f5f", - "gridcolor": "white", - "linecolor": "white", - "minorgridcolor": "white", - "startlinecolor": "#2a3f5f" - }, - "baxis": { - "endlinecolor": "#2a3f5f", - "gridcolor": "white", - "linecolor": "white", - "minorgridcolor": "white", - "startlinecolor": "#2a3f5f" - }, - "type": "carpet" - } - ], - "choropleth": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "type": "choropleth" - } - ], - "contour": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "contour" - } - ], - "contourcarpet": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "type": "contourcarpet" - } - ], - "heatmap": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "heatmap" - } - ], - "heatmapgl": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "heatmapgl" - } - ], - "histogram": [ - { - "marker": { - "pattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - } - }, - "type": "histogram" - } - ], - "histogram2d": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "histogram2d" - } - ], - "histogram2dcontour": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "histogram2dcontour" - } - ], - "mesh3d": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "type": "mesh3d" - } - ], - "parcoords": [ - { - "line": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "parcoords" - } - ], - "pie": [ - { - "automargin": true, - "type": "pie" - } - ], - "scatter": [ - { - "fillpattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - }, - "type": "scatter" - } - ], - "scatter3d": [ - { - "line": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatter3d" - } - ], - "scattercarpet": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattercarpet" - } - ], - "scattergeo": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattergeo" - } - ], - "scattergl": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattergl" - } - ], - "scattermapbox": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattermapbox" - } - ], - "scatterpolar": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatterpolar" - } - ], - "scatterpolargl": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatterpolargl" - } - ], - "scatterternary": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatterternary" - } - ], - "surface": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "surface" - } - ], - "table": [ - { - "cells": { - "fill": { - "color": "#EBF0F8" - }, - "line": { - "color": "white" - } - }, - "header": { - "fill": { - "color": "#C8D4E3" - }, - "line": { - "color": "white" - } - }, - "type": "table" - } - ] - }, - "layout": { - "annotationdefaults": { - "arrowcolor": "#2a3f5f", - "arrowhead": 0, - "arrowwidth": 1 - }, - "autotypenumbers": "strict", - "coloraxis": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "colorscale": { - "diverging": [ - [ - 0, - "#8e0152" - ], - [ - 0.1, - "#c51b7d" - ], - [ - 0.2, - "#de77ae" - ], - [ - 0.3, - "#f1b6da" - ], - [ - 0.4, - "#fde0ef" - ], - [ - 0.5, - "#f7f7f7" - ], - [ - 0.6, - "#e6f5d0" - ], - [ - 0.7, - "#b8e186" - ], - [ - 0.8, - "#7fbc41" - ], - [ - 0.9, - "#4d9221" - ], - [ - 1, - "#276419" - ] - ], - "sequential": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "sequentialminus": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ] - }, - "colorway": [ - "#636efa", - "#EF553B", - "#00cc96", - "#ab63fa", - "#FFA15A", - "#19d3f3", - "#FF6692", - "#B6E880", - "#FF97FF", - "#FECB52" - ], - "font": { - "color": "#2a3f5f" - }, - "geo": { - "bgcolor": "white", - "lakecolor": "white", - "landcolor": "#E5ECF6", - "showlakes": true, - "showland": true, - "subunitcolor": "white" - }, - "hoverlabel": { - "align": "left" - }, - "hovermode": "closest", - "mapbox": { - "style": "light" - }, - "paper_bgcolor": "white", - "plot_bgcolor": "#E5ECF6", - "polar": { - "angularaxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - }, - "bgcolor": "#E5ECF6", - "radialaxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - } - }, - "scene": { - "xaxis": { - "backgroundcolor": "#E5ECF6", - "gridcolor": "white", - "gridwidth": 2, - "linecolor": "white", - "showbackground": true, - "ticks": "", - "zerolinecolor": "white" - }, - "yaxis": { - "backgroundcolor": "#E5ECF6", - "gridcolor": "white", - "gridwidth": 2, - "linecolor": "white", - "showbackground": true, - "ticks": "", - "zerolinecolor": "white" - }, - "zaxis": { - "backgroundcolor": "#E5ECF6", - "gridcolor": "white", - "gridwidth": 2, - "linecolor": "white", - "showbackground": true, - "ticks": "", - "zerolinecolor": "white" - } - }, - "shapedefaults": { - "line": { - "color": "#2a3f5f" - } - }, - "ternary": { - "aaxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - }, - "baxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - }, - "bgcolor": "#E5ECF6", - "caxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - } - }, - "title": { - "x": 0.05 - }, - "xaxis": { - "automargin": true, - "gridcolor": "white", - "linecolor": "white", - "ticks": "", - "title": { - "standoff": 15 - }, - "zerolinecolor": "white", - "zerolinewidth": 2 - }, - "yaxis": { - "automargin": true, - "gridcolor": "white", - "linecolor": "white", - "ticks": "", - "title": { - "standoff": 15 - }, - "zerolinecolor": "white", - "zerolinewidth": 2 - } - } - }, - "title": { - "text": "Number of Pokémon caught in the last hour" - }, - "xaxis": { - "anchor": "y", - "categoryarray": [ - "ash", - "brock", - "gary", - "james", - "jessie", - "misty" - ], - "categoryorder": "array", - "domain": [ - 0, - 1 - ], - "title": { - "text": "trainer" - } - }, - "yaxis": { - "anchor": "x", - "domain": [ - 0, - 1 - ], - "title": { - "text": "count" - } - } - } - } - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Lets start with a simple query to understand our schema.\n", - "query = '''SELECT count(\"name\") FROM caught WHERE time > now() - 1h GROUP BY trainer'''\n", - "\n", - "# We can use the query method to run a query against the database.\n", - "# Under the hood this creates a flight ticket and uses the FlightClient to run the query.\n", - "# For this example we are using the pandas mode, which will return a pandas DataFrame.\n", - "# Language also allows us to specify the query language, in this case we are using SQL.\n", - "table = client.query(query=query, language='influxql', mode='pandas')\n", - "\n", - "fig1 = px.bar(table, x=\"trainer\", y=\"count\",color='trainer' ,title='Number of Pokémon caught in the last hour')\n", - "fig1.show()" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.plotly.v1+json": { - "config": { - "plotlyServerURL": "https://plot.ly" - }, - "data": [ - { - "alignmentgroup": "True", - "hovertemplate": "type1=Bug
trainer=%{x}
count=%{y}", - "legendgroup": "Bug", - "marker": { - "color": "#636efa", - "pattern": { - "shape": "" - } - }, - "name": "Bug", - "offsetgroup": "Bug", - "orientation": "v", - "showlegend": true, - "textposition": "auto", - "type": "bar", - "x": [ - "ash", - "brock", - "gary", - "james", - "jessie", - "misty" - ], - "xaxis": "x", - "y": [ - 13, - 19, - 12, - 13, - 14, - 13 - ], - "yaxis": "y" - }, - { - "alignmentgroup": "True", - "hovertemplate": "type1=Electric
trainer=%{x}
count=%{y}", - "legendgroup": "Electric", - "marker": { - "color": "#EF553B", - "pattern": { - "shape": "" - } - }, - "name": "Electric", - "offsetgroup": "Electric", - "orientation": "v", - "showlegend": true, - "textposition": "auto", - "type": "bar", - "x": [ - "ash", - "brock", - "gary", - "james", - "jessie", - "misty" - ], - "xaxis": "x", - "y": [ - 7, - 13, - 11, - 5, - 8, - 6 - ], - "yaxis": "y" - }, - { - "alignmentgroup": "True", - "hovertemplate": "type1=Fairy
trainer=%{x}
count=%{y}", - "legendgroup": "Fairy", - "marker": { - "color": "#00cc96", - "pattern": { - "shape": "" - } - }, - "name": "Fairy", - "offsetgroup": "Fairy", - "orientation": "v", - "showlegend": true, - "textposition": "auto", - "type": "bar", - "x": [ - "ash", - "brock", - "gary", - "james", - "jessie", - "misty" - ], - "xaxis": "x", - "y": [ - 3, - 5, - 2, - 3, - 2, - 4 - ], - "yaxis": "y" - }, - { - "alignmentgroup": "True", - "hovertemplate": "type1=Fighting
trainer=%{x}
count=%{y}", - "legendgroup": "Fighting", - "marker": { - "color": "#ab63fa", - "pattern": { - "shape": "" - } - }, - "name": "Fighting", - "offsetgroup": "Fighting", - "orientation": "v", - "showlegend": true, - "textposition": "auto", - "type": "bar", - "x": [ - "ash", - "brock", - "gary", - "james", - "jessie", - "misty" - ], - "xaxis": "x", - "y": [ - 4, - 7, - 9, - 10, - 9, - 12 - ], - "yaxis": "y" - }, - { - "alignmentgroup": "True", - "hovertemplate": "type1=Fire
trainer=%{x}
count=%{y}", - "legendgroup": "Fire", - "marker": { - "color": "#FFA15A", - "pattern": { - "shape": "" - } - }, - "name": "Fire", - "offsetgroup": "Fire", - "orientation": "v", - "showlegend": true, - "textposition": "auto", - "type": "bar", - "x": [ - "ash", - "brock", - "gary", - "james", - "jessie", - "misty" - ], - "xaxis": "x", - "y": [ - 20, - 16, - 12, - 13, - 13, - 4 - ], - "yaxis": "y" - }, - { - "alignmentgroup": "True", - "hovertemplate": "type1=Ghost
trainer=%{x}
count=%{y}", - "legendgroup": "Ghost", - "marker": { - "color": "#19d3f3", - "pattern": { - "shape": "" - } - }, - "name": "Ghost", - "offsetgroup": "Ghost", - "orientation": "v", - "showlegend": true, - "textposition": "auto", - "type": "bar", - "x": [ - "ash", - "brock", - "gary", - "james", - "jessie", - "misty" - ], - "xaxis": "x", - "y": [ - 3, - 4, - 1, - 6, - 3, - 2 - ], - "yaxis": "y" - }, - { - "alignmentgroup": "True", - "hovertemplate": "type1=Grass
trainer=%{x}
count=%{y}", - "legendgroup": "Grass", - "marker": { - "color": "#FF6692", - "pattern": { - "shape": "" - } - }, - "name": "Grass", - "offsetgroup": "Grass", - "orientation": "v", - "showlegend": true, - "textposition": "auto", - "type": "bar", - "x": [ - "ash", - "brock", - "gary", - "james", - "jessie", - "misty" - ], - "xaxis": "x", - "y": [ - 12, - 13, - 12, - 11, - 11, - 14 - ], - "yaxis": "y" - }, - { - "alignmentgroup": "True", - "hovertemplate": "type1=Ground
trainer=%{x}
count=%{y}", - "legendgroup": "Ground", - "marker": { - "color": "#B6E880", - "pattern": { - "shape": "" - } - }, - "name": "Ground", - "offsetgroup": "Ground", - "orientation": "v", - "showlegend": true, - "textposition": "auto", - "type": "bar", - "x": [ - "ash", - "brock", - "gary", - "james", - "jessie", - "misty" - ], - "xaxis": "x", - "y": [ - 4, - 7, - 10, - 12, - 7, - 9 - ], - "yaxis": "y" - }, - { - "alignmentgroup": "True", - "hovertemplate": "type1=Ice
trainer=%{x}
count=%{y}", - "legendgroup": "Ice", - "marker": { - "color": "#FF97FF", - "pattern": { - "shape": "" - } - }, - "name": "Ice", - "offsetgroup": "Ice", - "orientation": "v", - "showlegend": true, - "textposition": "auto", - "type": "bar", - "x": [ - "ash", - "brock", - "gary", - "james", - "jessie", - "misty" - ], - "xaxis": "x", - "y": [ - 3, - 2, - 1, - 1, - 4, - 1 - ], - "yaxis": "y" - }, - { - "alignmentgroup": "True", - "hovertemplate": "type1=Normal
trainer=%{x}
count=%{y}", - "legendgroup": "Normal", - "marker": { - "color": "#FECB52", - "pattern": { - "shape": "" - } - }, - "name": "Normal", - "offsetgroup": "Normal", - "orientation": "v", - "showlegend": true, - "textposition": "auto", - "type": "bar", - "x": [ - "ash", - "brock", - "gary", - "james", - "jessie", - "misty" - ], - "xaxis": "x", - "y": [ - 19, - 24, - 21, - 14, - 22, - 29 - ], - "yaxis": "y" - }, - { - "alignmentgroup": "True", - "hovertemplate": "type1=Poison
trainer=%{x}
count=%{y}", - "legendgroup": "Poison", - "marker": { - "color": "#636efa", - "pattern": { - "shape": "" - } - }, - "name": "Poison", - "offsetgroup": "Poison", - "orientation": "v", - "showlegend": true, - "textposition": "auto", - "type": "bar", - "x": [ - "ash", - "brock", - "gary", - "james", - "jessie", - "misty" - ], - "xaxis": "x", - "y": [ - 12, - 13, - 12, - 17, - 11, - 15 - ], - "yaxis": "y" - }, - { - "alignmentgroup": "True", - "hovertemplate": "type1=Psychic
trainer=%{x}
count=%{y}", - "legendgroup": "Psychic", - "marker": { - "color": "#EF553B", - "pattern": { - "shape": "" - } - }, - "name": "Psychic", - "offsetgroup": "Psychic", - "orientation": "v", - "showlegend": true, - "textposition": "auto", - "type": "bar", - "x": [ - "ash", - "brock", - "gary", - "james", - "jessie", - "misty" - ], - "xaxis": "x", - "y": [ - 7, - 6, - 8, - 10, - 6, - 7 - ], - "yaxis": "y" - }, - { - "alignmentgroup": "True", - "hovertemplate": "type1=Rock
trainer=%{x}
count=%{y}", - "legendgroup": "Rock", - "marker": { - "color": "#00cc96", - "pattern": { - "shape": "" - } - }, - "name": "Rock", - "offsetgroup": "Rock", - "orientation": "v", - "showlegend": true, - "textposition": "auto", - "type": "bar", - "x": [ - "ash", - "brock", - "gary", - "james", - "jessie", - "misty" - ], - "xaxis": "x", - "y": [ - 11, - 9, - 11, - 15, - 16, - 12 - ], - "yaxis": "y" - }, - { - "alignmentgroup": "True", - "hovertemplate": "type1=Water
trainer=%{x}
count=%{y}", - "legendgroup": "Water", - "marker": { - "color": "#ab63fa", - "pattern": { - "shape": "" - } - }, - "name": "Water", - "offsetgroup": "Water", - "orientation": "v", - "showlegend": true, - "textposition": "auto", - "type": "bar", - "x": [ - "ash", - "brock", - "gary", - "james", - "jessie", - "misty" - ], - "xaxis": "x", - "y": [ - 28, - 32, - 39, - 32, - 25, - 36 - ], - "yaxis": "y" - }, - { - "alignmentgroup": "True", - "hovertemplate": "type1=Dragon
trainer=%{x}
count=%{y}", - "legendgroup": "Dragon", - "marker": { - "color": "#FFA15A", - "pattern": { - "shape": "" - } - }, - "name": "Dragon", - "offsetgroup": "Dragon", - "orientation": "v", - "showlegend": true, - "textposition": "auto", - "type": "bar", - "x": [ - "brock", - "gary", - "jessie", - "misty" - ], - "xaxis": "x", - "y": [ - 2, - 1, - 4, - 2 - ], - "yaxis": "y" - } - ], - "layout": { - "barmode": "group", - "legend": { - "title": { - "text": "type1" - }, - "tracegroupgap": 0 - }, - "template": { - "data": { - "bar": [ - { - "error_x": { - "color": "#2a3f5f" - }, - "error_y": { - "color": "#2a3f5f" - }, - "marker": { - "line": { - "color": "#E5ECF6", - "width": 0.5 - }, - "pattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - } - }, - "type": "bar" - } - ], - "barpolar": [ - { - "marker": { - "line": { - "color": "#E5ECF6", - "width": 0.5 - }, - "pattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - } - }, - "type": "barpolar" - } - ], - "carpet": [ - { - "aaxis": { - "endlinecolor": "#2a3f5f", - "gridcolor": "white", - "linecolor": "white", - "minorgridcolor": "white", - "startlinecolor": "#2a3f5f" - }, - "baxis": { - "endlinecolor": "#2a3f5f", - "gridcolor": "white", - "linecolor": "white", - "minorgridcolor": "white", - "startlinecolor": "#2a3f5f" - }, - "type": "carpet" - } - ], - "choropleth": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "type": "choropleth" - } - ], - "contour": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "contour" - } - ], - "contourcarpet": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "type": "contourcarpet" - } - ], - "heatmap": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "heatmap" - } - ], - "heatmapgl": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "heatmapgl" - } - ], - "histogram": [ - { - "marker": { - "pattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - } - }, - "type": "histogram" - } - ], - "histogram2d": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "histogram2d" - } - ], - "histogram2dcontour": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "histogram2dcontour" - } - ], - "mesh3d": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "type": "mesh3d" - } - ], - "parcoords": [ - { - "line": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "parcoords" - } - ], - "pie": [ - { - "automargin": true, - "type": "pie" - } - ], - "scatter": [ - { - "fillpattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - }, - "type": "scatter" - } - ], - "scatter3d": [ - { - "line": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatter3d" - } - ], - "scattercarpet": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattercarpet" - } - ], - "scattergeo": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattergeo" - } - ], - "scattergl": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattergl" - } - ], - "scattermapbox": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattermapbox" - } - ], - "scatterpolar": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatterpolar" - } - ], - "scatterpolargl": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatterpolargl" - } - ], - "scatterternary": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatterternary" - } - ], - "surface": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "surface" - } - ], - "table": [ - { - "cells": { - "fill": { - "color": "#EBF0F8" - }, - "line": { - "color": "white" - } - }, - "header": { - "fill": { - "color": "#C8D4E3" - }, - "line": { - "color": "white" - } - }, - "type": "table" - } - ] - }, - "layout": { - "annotationdefaults": { - "arrowcolor": "#2a3f5f", - "arrowhead": 0, - "arrowwidth": 1 - }, - "autotypenumbers": "strict", - "coloraxis": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "colorscale": { - "diverging": [ - [ - 0, - "#8e0152" - ], - [ - 0.1, - "#c51b7d" - ], - [ - 0.2, - "#de77ae" - ], - [ - 0.3, - "#f1b6da" - ], - [ - 0.4, - "#fde0ef" - ], - [ - 0.5, - "#f7f7f7" - ], - [ - 0.6, - "#e6f5d0" - ], - [ - 0.7, - "#b8e186" - ], - [ - 0.8, - "#7fbc41" - ], - [ - 0.9, - "#4d9221" - ], - [ - 1, - "#276419" - ] - ], - "sequential": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "sequentialminus": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ] - }, - "colorway": [ - "#636efa", - "#EF553B", - "#00cc96", - "#ab63fa", - "#FFA15A", - "#19d3f3", - "#FF6692", - "#B6E880", - "#FF97FF", - "#FECB52" - ], - "font": { - "color": "#2a3f5f" - }, - "geo": { - "bgcolor": "white", - "lakecolor": "white", - "landcolor": "#E5ECF6", - "showlakes": true, - "showland": true, - "subunitcolor": "white" - }, - "hoverlabel": { - "align": "left" - }, - "hovermode": "closest", - "mapbox": { - "style": "light" - }, - "paper_bgcolor": "white", - "plot_bgcolor": "#E5ECF6", - "polar": { - "angularaxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - }, - "bgcolor": "#E5ECF6", - "radialaxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - } - }, - "scene": { - "xaxis": { - "backgroundcolor": "#E5ECF6", - "gridcolor": "white", - "gridwidth": 2, - "linecolor": "white", - "showbackground": true, - "ticks": "", - "zerolinecolor": "white" - }, - "yaxis": { - "backgroundcolor": "#E5ECF6", - "gridcolor": "white", - "gridwidth": 2, - "linecolor": "white", - "showbackground": true, - "ticks": "", - "zerolinecolor": "white" - }, - "zaxis": { - "backgroundcolor": "#E5ECF6", - "gridcolor": "white", - "gridwidth": 2, - "linecolor": "white", - "showbackground": true, - "ticks": "", - "zerolinecolor": "white" - } - }, - "shapedefaults": { - "line": { - "color": "#2a3f5f" - } - }, - "ternary": { - "aaxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - }, - "baxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - }, - "bgcolor": "#E5ECF6", - "caxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - } - }, - "title": { - "x": 0.05 - }, - "xaxis": { - "automargin": true, - "gridcolor": "white", - "linecolor": "white", - "ticks": "", - "title": { - "standoff": 15 - }, - "zerolinecolor": "white", - "zerolinewidth": 2 - }, - "yaxis": { - "automargin": true, - "gridcolor": "white", - "linecolor": "white", - "ticks": "", - "title": { - "standoff": 15 - }, - "zerolinecolor": "white", - "zerolinewidth": 2 - } - } - }, - "title": { - "text": "Number of Pokémon caught in the last hour grouped by type" - }, - "xaxis": { - "anchor": "y", - "domain": [ - 0, - 1 - ], - "title": { - "text": "trainer" - } - }, - "yaxis": { - "anchor": "x", - "domain": [ - 0, - 1 - ], - "title": { - "text": "count" - } - } - } - } - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Lets start with a simple query to understand our schema.\n", - "query = '''SELECT count(\"name\") FROM caught WHERE time > now() - 1h GROUP BY trainer,type1'''\n", - "\n", - "# We can use the query method to run a query against the database.\n", - "# Under the hood this creates a flight ticket and uses the FlightClient to run the query.\n", - "# For this example we are using the pandas mode, which will return a pandas DataFrame.\n", - "# Language also allows us to specify the query language, in this case we are using SQL.\n", - "table = client.query(query=query, language='influxql' , mode='pandas')\n", - "\n", - "fig2 = px.bar(table, x=\"trainer\", y=\"count\", color='type1', barmode= 'group', title='Number of Pokémon caught in the last hour grouped by type')\n", - "fig2.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Working with Arrow tables\n", - "So within the last section we discussed converting returned queries directly to Pandas Dataframes. However, we can also utilise their raw format, Arrow tables. Arrow tables are a columnar format that is more efficient for working with data.\n", - "\n", - "Lets first start by adding more data with a random timestamp between now and 1 hour ago" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
traineridnumnamelevelattackdefensehpspeedtype1type2
timestamp
2023-07-27 10:13:38.242000+00:00james00151Beedrill890406575BugPoison
2023-07-27 10:24:38.242000+00:00misty01013Electrode17507060140ElectricNaN
2023-07-27 10:10:38.242000+00:00brock00253Pikachu1055403590ElectricNaN
2023-07-27 10:47:38.242000+00:00misty01352Jolteon20656065130ElectricNaN
2023-07-27 10:14:38.242000+00:00ash00222Fearow13906565100NormalFlying
....................................
2023-07-27 10:40:38.242000+00:00james013310Eevee1355505555NormalNaN
2023-07-27 10:11:38.242000+00:00james011012Weezing10901206560PoisonNaN
2023-07-27 10:19:38.242000+00:00james00728Tentacool540354070WaterPoison
2023-07-27 10:18:38.242000+00:00brock011017Weezing12901206560PoisonNaN
2023-07-27 10:30:38.242000+00:00misty009416Gengar11656060110GhostPoison
\n", - "

10000 rows × 11 columns

\n", - "
" - ], - "text/plain": [ - " trainer id num name level attack \\\n", - "timestamp \n", - "2023-07-27 10:13:38.242000+00:00 james 0015 1 Beedrill 8 90 \n", - "2023-07-27 10:24:38.242000+00:00 misty 0101 3 Electrode 17 50 \n", - "2023-07-27 10:10:38.242000+00:00 brock 0025 3 Pikachu 10 55 \n", - "2023-07-27 10:47:38.242000+00:00 misty 0135 2 Jolteon 20 65 \n", - "2023-07-27 10:14:38.242000+00:00 ash 0022 2 Fearow 13 90 \n", - "... ... ... .. ... ... ... \n", - "2023-07-27 10:40:38.242000+00:00 james 0133 10 Eevee 13 55 \n", - "2023-07-27 10:11:38.242000+00:00 james 0110 12 Weezing 10 90 \n", - "2023-07-27 10:19:38.242000+00:00 james 0072 8 Tentacool 5 40 \n", - "2023-07-27 10:18:38.242000+00:00 brock 0110 17 Weezing 12 90 \n", - "2023-07-27 10:30:38.242000+00:00 misty 0094 16 Gengar 11 65 \n", - "\n", - " defense hp speed type1 type2 \n", - "timestamp \n", - "2023-07-27 10:13:38.242000+00:00 40 65 75 Bug Poison \n", - "2023-07-27 10:24:38.242000+00:00 70 60 140 Electric NaN \n", - "2023-07-27 10:10:38.242000+00:00 40 35 90 Electric NaN \n", - "2023-07-27 10:47:38.242000+00:00 60 65 130 Electric NaN \n", - "2023-07-27 10:14:38.242000+00:00 65 65 100 Normal Flying \n", - "... ... .. ... ... ... \n", - "2023-07-27 10:40:38.242000+00:00 50 55 55 Normal NaN \n", - "2023-07-27 10:11:38.242000+00:00 120 65 60 Poison NaN \n", - "2023-07-27 10:19:38.242000+00:00 35 40 70 Water Poison \n", - "2023-07-27 10:18:38.242000+00:00 120 65 60 Poison NaN \n", - "2023-07-27 10:30:38.242000+00:00 60 60 110 Ghost Poison \n", - "\n", - "[10000 rows x 11 columns]" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Written batch: ('pokemon-codex', '6a841c0c08328fb1', 'ns')\n", - "Written batch: ('pokemon-codex', '6a841c0c08328fb1', 'ns')\n", - "Written batch: ('pokemon-codex', '6a841c0c08328fb1', 'ns')\n", - "Written batch: ('pokemon-codex', '6a841c0c08328fb1', 'ns')\n", - "Written batch: ('pokemon-codex', '6a841c0c08328fb1', 'ns')\n", - "Written batch: ('pokemon-codex', '6a841c0c08328fb1', 'ns')\n", - "Written batch: ('pokemon-codex', '6a841c0c08328fb1', 'ns')\n", - "Written batch: ('pokemon-codex', '6a841c0c08328fb1', 'ns')\n", - "Written batch: ('pokemon-codex', '6a841c0c08328fb1', 'ns')\n", - "Written batch: ('pokemon-codex', '6a841c0c08328fb1', 'ns')\n" - ] - } - ], - "source": [ - "num_entries = 10000 # You can reduce this if required. The more data the more interesting the results.\n", - "now = pd.Timestamp.now(tz='UTC').floor('ms')\n", - "data = []\n", - "\n", - "\n", - "# Generating random data\n", - "for i in range(num_entries):\n", - " # Randomise the timestamp\n", - " timestamp = now - pd.Timedelta(minutes=random.randint(0, 60))\n", - " trainer = random.choice(trainers)\n", - " \n", - " # Randomly select a row from pokemon_df\n", - " random_pokemon = pokemon_df.sample().iloc[0]\n", - " caught = random_pokemon['Name']\n", - " \n", - " # Count the number of times this trainer has caught this Pokémon\n", - " if (trainer, caught) in trainer_pokemon_counts:\n", - " trainer_pokemon_counts[(trainer, caught)] += 1\n", - " else:\n", - " trainer_pokemon_counts[(trainer, caught)] = 1\n", - " \n", - " # Get the number for this combination of trainer and Pokémon\n", - " num = trainer_pokemon_counts[(trainer, caught)]\n", - "\n", - " entry = {\n", - " \"trainer\": trainer,\n", - " \"id\": f\"{0000 + random_pokemon['#']:04d}\",\n", - " \"num\": str(num),\n", - " \"name\": caught,\n", - " \"level\": random.randint(5, 20),\n", - " \"attack\": random_pokemon['Attack'],\n", - " \"defense\": random_pokemon['Defense'],\n", - " \"hp\": random_pokemon['HP'],\n", - " \"speed\": random_pokemon['Speed'],\n", - " \"type1\": random_pokemon['Type 1'],\n", - " \"type2\": random_pokemon['Type 2'],\n", - " \"timestamp\": timestamp\n", - " }\n", - " data.append(entry)\n", - "\n", - "# Convert the list of dictionaries to a DataFrame\n", - "caught_pokemon_df = pd.DataFrame(data).set_index('timestamp')\n", - "\n", - "# Print the DataFrame\n", - "display(caught_pokemon_df)\n", - "\n", - "try:\n", - " client.write(caught_pokemon_df, data_frame_measurement_name='kanto',\n", - " data_frame_tag_columns=['trainer', 'id', 'num'])\n", - "except Exception as e:\n", - " print(f\"Error writing point: {e}\")\n", - "\n", - "# Wait for the batch to be written\n", - "time.sleep(5)\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now lets return the result as an Arrow table. We can recreate he same aggregation we did on server side with the pyarrow library. the `mode='all'` parameter tells InfluxDB to return all data within the query result as a Arrow table. We can then use the `to_pandas()` method to convert the Arrow table to a Pandas DataFrame." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "pyarrow.Table\n", - "iox::measurement: string not null\n", - "time: timestamp[ns] not null\n", - "attack: int64\n", - "defense: int64\n", - "hp: int64\n", - "id: string\n", - "level: int64\n", - "name: string\n", - "num: string\n", - "speed: int64\n", - "trainer: string\n", - "type1: string\n", - "type2: string\n", - "----\n", - "iox::measurement: [[\"kanto\",\"kanto\",\"kanto\",\"kanto\",\"kanto\",...,\"kanto\",\"kanto\",\"kanto\",\"kanto\",\"kanto\"],[\"kanto\",\"kanto\",\"kanto\",\"kanto\",\"kanto\",...,\"kanto\",\"kanto\",\"kanto\",\"kanto\",\"kanto\"]]\n", - "time: [[2023-07-27 09:48:38.242000000,2023-07-27 09:48:38.242000000,2023-07-27 09:48:38.242000000,2023-07-27 09:48:38.242000000,2023-07-27 09:48:38.242000000,...,2023-07-27 10:38:38.242000000,2023-07-27 10:38:38.242000000,2023-07-27 10:38:38.242000000,2023-07-27 10:38:38.242000000,2023-07-27 10:38:38.242000000],[2023-07-27 10:38:38.242000000,2023-07-27 10:38:38.242000000,2023-07-27 10:38:38.242000000,2023-07-27 10:38:38.242000000,2023-07-27 10:38:38.242000000,...,2023-07-27 10:48:38.242000000,2023-07-27 10:48:38.242000000,2023-07-27 10:48:38.242000000,2023-07-27 10:48:38.242000000,2023-07-27 10:48:38.242000000]]\n", - "attack: [[82,82,100,64,64,...,60,85,85,100,100],[47,72,45,41,41,...,64,64,134,134,110]]\n", - "defense: [[83,83,123,58,58,...,44,69,69,110,110],[52,57,48,40,40,...,45,45,95,95,90]]\n", - "hp: [[80,80,80,58,58,...,35,60,60,75,75],[55,61,70,38,38,...,41,41,91,91,106]]\n", - "id: [[\"0003\",\"0003\",\"0003\",\"0005\",\"0005\",...,\"0023\",\"0024\",\"0024\",\"0028\",\"0028\"],[\"0029\",\"0033\",\"0035\",\"0037\",\"0037\",...,\"0147\",\"0147\",\"0149\",\"0149\",\"0150\"]]\n", - "level: [[9,9,18,18,12,...,15,5,8,15,5],[11,15,10,16,19,...,10,11,16,6,17]]\n", - "name: [[\"Venusaur\",\"Venusaur\",\"VenusaurMega Venusaur\",\"Charmeleon\",\"Charmeleon\",...,\"Ekans\",\"Arbok\",\"Arbok\",\"Sandslash\",\"Sandslash\"],[\"Nidoran♀\",\"Nidorino\",\"Clefairy\",\"Vulpix\",\"Vulpix\",...,\"Dratini\",\"Dratini\",\"Dragonite\",\"Dragonite\",\"Mewtwo\"]]\n", - "num: [[\"4\",\"6\",\"8\",\"11\",\"3\",...,\"12\",\"6\",\"7\",\"7\",\"7\"],[\"8\",\"6\",\"16\",\"1\",\"10\",...,\"1\",\"3\",\"3\",\"5\",\"11\"]]\n", - "speed: [[80,80,80,80,80,...,55,80,80,65,65],[41,65,35,65,65,...,50,50,80,80,130]]\n", - "...\n" - ] - }, - { - "data": { - "application/vnd.plotly.v1+json": { - "config": { - "plotlyServerURL": "https://plot.ly" - }, - "data": [ - { - "alignmentgroup": "True", - "hovertemplate": "type1=Grass
trainer=%{x}
name_count=%{y}", - "legendgroup": "Grass", - "marker": { - "color": "#636efa", - "pattern": { - "shape": "" - } - }, - "name": "Grass", - "offsetgroup": "Grass", - "orientation": "v", - "showlegend": true, - "textposition": "auto", - "type": "bar", - "x": [ - "ash", - "misty", - "gary", - "james", - "brock", - "jessie" - ], - "xaxis": "x", - "y": [ - 121, - 137, - 134, - 129, - 130, - 138 - ], - "yaxis": "y" - }, - { - "alignmentgroup": "True", - "hovertemplate": "type1=Fire
trainer=%{x}
name_count=%{y}", - "legendgroup": "Fire", - "marker": { - "color": "#EF553B", - "pattern": { - "shape": "" - } - }, - "name": "Fire", - "offsetgroup": "Fire", - "orientation": "v", - "showlegend": true, - "textposition": "auto", - "type": "bar", - "x": [ - "james", - "brock", - "ash", - "jessie", - "misty", - "gary" - ], - "xaxis": "x", - "y": [ - 153, - 151, - 128, - 142, - 132, - 161 - ], - "yaxis": "y" - }, - { - "alignmentgroup": "True", - "hovertemplate": "type1=Water
trainer=%{x}
name_count=%{y}", - "legendgroup": "Water", - "marker": { - "color": "#00cc96", - "pattern": { - "shape": "" - } - }, - "name": "Water", - "offsetgroup": "Water", - "orientation": "v", - "showlegend": true, - "textposition": "auto", - "type": "bar", - "x": [ - "misty", - "james", - "brock", - "gary", - "ash", - "jessie" - ], - "xaxis": "x", - "y": [ - 333, - 323, - 324, - 283, - 315, - 345 - ], - "yaxis": "y" - }, - { - "alignmentgroup": "True", - "hovertemplate": "type1=Bug
trainer=%{x}
name_count=%{y}", - "legendgroup": "Bug", - "marker": { - "color": "#ab63fa", - "pattern": { - "shape": "" - } - }, - "name": "Bug", - "offsetgroup": "Bug", - "orientation": "v", - "showlegend": true, - "textposition": "auto", - "type": "bar", - "x": [ - "james", - "misty", - "ash", - "brock", - "gary", - "jessie" - ], - "xaxis": "x", - "y": [ - 160, - 147, - 157, - 142, - 140, - 154 - ], - "yaxis": "y" - }, - { - "alignmentgroup": "True", - "hovertemplate": "type1=Normal
trainer=%{x}
name_count=%{y}", - "legendgroup": "Normal", - "marker": { - "color": "#FFA15A", - "pattern": { - "shape": "" - } - }, - "name": "Normal", - "offsetgroup": "Normal", - "orientation": "v", - "showlegend": true, - "textposition": "auto", - "type": "bar", - "x": [ - "jessie", - "gary", - "ash", - "brock", - "james", - "misty" - ], - "xaxis": "x", - "y": [ - 240, - 261, - 231, - 239, - 237, - 230 - ], - "yaxis": "y" - }, - { - "alignmentgroup": "True", - "hovertemplate": "type1=Electric
trainer=%{x}
name_count=%{y}", - "legendgroup": "Electric", - "marker": { - "color": "#19d3f3", - "pattern": { - "shape": "" - } - }, - "name": "Electric", - "offsetgroup": "Electric", - "orientation": "v", - "showlegend": true, - "textposition": "auto", - "type": "bar", - "x": [ - "jessie", - "gary", - "misty", - "brock", - "james", - "ash" - ], - "xaxis": "x", - "y": [ - 90, - 107, - 85, - 88, - 102, - 97 - ], - "yaxis": "y" - }, - { - "alignmentgroup": "True", - "hovertemplate": "type1=Poison
trainer=%{x}
name_count=%{y}", - "legendgroup": "Poison", - "marker": { - "color": "#FF6692", - "pattern": { - "shape": "" - } - }, - "name": "Poison", - "offsetgroup": "Poison", - "orientation": "v", - "showlegend": true, - "textposition": "auto", - "type": "bar", - "x": [ - "misty", - "jessie", - "james", - "brock", - "ash", - "gary" - ], - "xaxis": "x", - "y": [ - 147, - 138, - 154, - 147, - 154, - 137 - ], - "yaxis": "y" - }, - { - "alignmentgroup": "True", - "hovertemplate": "type1=Fairy
trainer=%{x}
name_count=%{y}", - "legendgroup": "Fairy", - "marker": { - "color": "#B6E880", - "pattern": { - "shape": "" - } - }, - "name": "Fairy", - "offsetgroup": "Fairy", - "orientation": "v", - "showlegend": true, - "textposition": "auto", - "type": "bar", - "x": [ - "jessie", - "brock", - "gary", - "james", - "misty", - "ash" - ], - "xaxis": "x", - "y": [ - 20, - 12, - 25, - 20, - 21, - 20 - ], - "yaxis": "y" - }, - { - "alignmentgroup": "True", - "hovertemplate": "type1=Ground
trainer=%{x}
name_count=%{y}", - "legendgroup": "Ground", - "marker": { - "color": "#FF97FF", - "pattern": { - "shape": "" - } - }, - "name": "Ground", - "offsetgroup": "Ground", - "orientation": "v", - "showlegend": true, - "textposition": "auto", - "type": "bar", - "x": [ - "ash", - "jessie", - "misty", - "gary", - "james", - "brock" - ], - "xaxis": "x", - "y": [ - 70, - 87, - 85, - 63, - 87, - 64 - ], - "yaxis": "y" - }, - { - "alignmentgroup": "True", - "hovertemplate": "type1=Fighting
trainer=%{x}
name_count=%{y}", - "legendgroup": "Fighting", - "marker": { - "color": "#FECB52", - "pattern": { - "shape": "" - } - }, - "name": "Fighting", - "offsetgroup": "Fighting", - "orientation": "v", - "showlegend": true, - "textposition": "auto", - "type": "bar", - "x": [ - "gary", - "ash", - "brock", - "james", - "jessie", - "misty" - ], - "xaxis": "x", - "y": [ - 63, - 65, - 69, - 75, - 65, - 90 - ], - "yaxis": "y" - }, - { - "alignmentgroup": "True", - "hovertemplate": "type1=Psychic
trainer=%{x}
name_count=%{y}", - "legendgroup": "Psychic", - "marker": { - "color": "#636efa", - "pattern": { - "shape": "" - } - }, - "name": "Psychic", - "offsetgroup": "Psychic", - "orientation": "v", - "showlegend": true, - "textposition": "auto", - "type": "bar", - "x": [ - "gary", - "brock", - "jessie", - "james", - "ash", - "misty" - ], - "xaxis": "x", - "y": [ - 85, - 92, - 66, - 83, - 70, - 72 - ], - "yaxis": "y" - }, - { - "alignmentgroup": "True", - "hovertemplate": "type1=Rock
trainer=%{x}
name_count=%{y}", - "legendgroup": "Rock", - "marker": { - "color": "#EF553B", - "pattern": { - "shape": "" - } - }, - "name": "Rock", - "offsetgroup": "Rock", - "orientation": "v", - "showlegend": true, - "textposition": "auto", - "type": "bar", - "x": [ - "jessie", - "ash", - "brock", - "gary", - "misty", - "james" - ], - "xaxis": "x", - "y": [ - 113, - 97, - 82, - 102, - 97, - 90 - ], - "yaxis": "y" - }, - { - "alignmentgroup": "True", - "hovertemplate": "type1=Ghost
trainer=%{x}
name_count=%{y}", - "legendgroup": "Ghost", - "marker": { - "color": "#00cc96", - "pattern": { - "shape": "" - } - }, - "name": "Ghost", - "offsetgroup": "Ghost", - "orientation": "v", - "showlegend": true, - "textposition": "auto", - "type": "bar", - "x": [ - "gary", - "jessie", - "ash", - "brock", - "misty", - "james" - ], - "xaxis": "x", - "y": [ - 45, - 44, - 40, - 44, - 38, - 44 - ], - "yaxis": "y" - }, - { - "alignmentgroup": "True", - "hovertemplate": "type1=Dragon
trainer=%{x}
name_count=%{y}", - "legendgroup": "Dragon", - "marker": { - "color": "#ab63fa", - "pattern": { - "shape": "" - } - }, - "name": "Dragon", - "offsetgroup": "Dragon", - "orientation": "v", - "showlegend": true, - "textposition": "auto", - "type": "bar", - "x": [ - "james", - "jessie", - "gary", - "misty", - "ash", - "brock" - ], - "xaxis": "x", - "y": [ - 33, - 39, - 34, - 31, - 47, - 27 - ], - "yaxis": "y" - }, - { - "alignmentgroup": "True", - "hovertemplate": "type1=Ice
trainer=%{x}
name_count=%{y}", - "legendgroup": "Ice", - "marker": { - "color": "#FFA15A", - "pattern": { - "shape": "" - } - }, - "name": "Ice", - "offsetgroup": "Ice", - "orientation": "v", - "showlegend": true, - "textposition": "auto", - "type": "bar", - "x": [ - "jessie", - "misty", - "ash", - "james", - "gary", - "brock" - ], - "xaxis": "x", - "y": [ - 22, - 14, - 28, - 15, - 14, - 22 - ], - "yaxis": "y" - } - ], - "layout": { - "barmode": "group", - "legend": { - "title": { - "text": "type1" - }, - "tracegroupgap": 0 - }, - "template": { - "data": { - "bar": [ - { - "error_x": { - "color": "#2a3f5f" - }, - "error_y": { - "color": "#2a3f5f" - }, - "marker": { - "line": { - "color": "#E5ECF6", - "width": 0.5 - }, - "pattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - } - }, - "type": "bar" - } - ], - "barpolar": [ - { - "marker": { - "line": { - "color": "#E5ECF6", - "width": 0.5 - }, - "pattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - } - }, - "type": "barpolar" - } - ], - "carpet": [ - { - "aaxis": { - "endlinecolor": "#2a3f5f", - "gridcolor": "white", - "linecolor": "white", - "minorgridcolor": "white", - "startlinecolor": "#2a3f5f" - }, - "baxis": { - "endlinecolor": "#2a3f5f", - "gridcolor": "white", - "linecolor": "white", - "minorgridcolor": "white", - "startlinecolor": "#2a3f5f" - }, - "type": "carpet" - } - ], - "choropleth": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "type": "choropleth" - } - ], - "contour": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "contour" - } - ], - "contourcarpet": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "type": "contourcarpet" - } - ], - "heatmap": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "heatmap" - } - ], - "heatmapgl": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "heatmapgl" - } - ], - "histogram": [ - { - "marker": { - "pattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - } - }, - "type": "histogram" - } - ], - "histogram2d": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "histogram2d" - } - ], - "histogram2dcontour": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "histogram2dcontour" - } - ], - "mesh3d": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "type": "mesh3d" - } - ], - "parcoords": [ - { - "line": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "parcoords" - } - ], - "pie": [ - { - "automargin": true, - "type": "pie" - } - ], - "scatter": [ - { - "fillpattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - }, - "type": "scatter" - } - ], - "scatter3d": [ - { - "line": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatter3d" - } - ], - "scattercarpet": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattercarpet" - } - ], - "scattergeo": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattergeo" - } - ], - "scattergl": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattergl" - } - ], - "scattermapbox": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattermapbox" - } - ], - "scatterpolar": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatterpolar" - } - ], - "scatterpolargl": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatterpolargl" - } - ], - "scatterternary": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatterternary" - } - ], - "surface": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "surface" - } - ], - "table": [ - { - "cells": { - "fill": { - "color": "#EBF0F8" - }, - "line": { - "color": "white" - } - }, - "header": { - "fill": { - "color": "#C8D4E3" - }, - "line": { - "color": "white" - } - }, - "type": "table" - } - ] - }, - "layout": { - "annotationdefaults": { - "arrowcolor": "#2a3f5f", - "arrowhead": 0, - "arrowwidth": 1 - }, - "autotypenumbers": "strict", - "coloraxis": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "colorscale": { - "diverging": [ - [ - 0, - "#8e0152" - ], - [ - 0.1, - "#c51b7d" - ], - [ - 0.2, - "#de77ae" - ], - [ - 0.3, - "#f1b6da" - ], - [ - 0.4, - "#fde0ef" - ], - [ - 0.5, - "#f7f7f7" - ], - [ - 0.6, - "#e6f5d0" - ], - [ - 0.7, - "#b8e186" - ], - [ - 0.8, - "#7fbc41" - ], - [ - 0.9, - "#4d9221" - ], - [ - 1, - "#276419" - ] - ], - "sequential": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "sequentialminus": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ] - }, - "colorway": [ - "#636efa", - "#EF553B", - "#00cc96", - "#ab63fa", - "#FFA15A", - "#19d3f3", - "#FF6692", - "#B6E880", - "#FF97FF", - "#FECB52" - ], - "font": { - "color": "#2a3f5f" - }, - "geo": { - "bgcolor": "white", - "lakecolor": "white", - "landcolor": "#E5ECF6", - "showlakes": true, - "showland": true, - "subunitcolor": "white" - }, - "hoverlabel": { - "align": "left" - }, - "hovermode": "closest", - "mapbox": { - "style": "light" - }, - "paper_bgcolor": "white", - "plot_bgcolor": "#E5ECF6", - "polar": { - "angularaxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - }, - "bgcolor": "#E5ECF6", - "radialaxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - } - }, - "scene": { - "xaxis": { - "backgroundcolor": "#E5ECF6", - "gridcolor": "white", - "gridwidth": 2, - "linecolor": "white", - "showbackground": true, - "ticks": "", - "zerolinecolor": "white" - }, - "yaxis": { - "backgroundcolor": "#E5ECF6", - "gridcolor": "white", - "gridwidth": 2, - "linecolor": "white", - "showbackground": true, - "ticks": "", - "zerolinecolor": "white" - }, - "zaxis": { - "backgroundcolor": "#E5ECF6", - "gridcolor": "white", - "gridwidth": 2, - "linecolor": "white", - "showbackground": true, - "ticks": "", - "zerolinecolor": "white" - } - }, - "shapedefaults": { - "line": { - "color": "#2a3f5f" - } - }, - "ternary": { - "aaxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - }, - "baxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - }, - "bgcolor": "#E5ECF6", - "caxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - } - }, - "title": { - "x": 0.05 - }, - "xaxis": { - "automargin": true, - "gridcolor": "white", - "linecolor": "white", - "ticks": "", - "title": { - "standoff": 15 - }, - "zerolinecolor": "white", - "zerolinewidth": 2 - }, - "yaxis": { - "automargin": true, - "gridcolor": "white", - "linecolor": "white", - "ticks": "", - "title": { - "standoff": 15 - }, - "zerolinecolor": "white", - "zerolinewidth": 2 - } - } - }, - "title": { - "text": "Number of Pokémon caught in the last hour grouped by type" - }, - "xaxis": { - "anchor": "y", - "domain": [ - 0, - 1 - ], - "title": { - "text": "trainer" - } - }, - "yaxis": { - "anchor": "x", - "domain": [ - 0, - 1 - ], - "title": { - "text": "name_count" - } - } - } - } - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "table = client.query(query='''SELECT * FROM kanto ORDER BY time''', language='influxql', mode='all')\n", - "print(table)\n", - "\n", - "# PyArrow Aggregation\n", - "aggregation = table.group_by([\"trainer\", \"type1\"]).aggregate([(\"name\", \"count\")]).to_pandas()\n", - "fig3 = px.bar(aggregation, x=\"trainer\", y=\"name_count\", color='type1', barmode= 'group', title='Number of Pokémon caught in the last hour grouped by type')\n", - "fig3.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Saving to file\n", - "We can also save our query results to file. This is useful if we want to save our data for later use. We can save our data in a number of formats including CSV, JSON, Parquet and Apache Arrow. Lets save our data as a parquet file." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [], - "source": [ - "import pyarrow.parquet as pq\n", - "\n", - "# Write the table to a parquet file\n", - "pq.write_table(table, 'kanto.parquet')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Uploading a file to InfluxDB\n", - "This allows to show show off another feature of the InfluxDB 3.0 Python Client. We can parse our file directly to InfluxDB. This is useful if we want to upload data from a local file. Lets upload our parquet file to InfluxDB." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Written batch: ('pokemon-codex', '6a841c0c08328fb1', 'ns')\n", - "Written batch: ('pokemon-codex', '6a841c0c08328fb1', 'ns')\n", - "Written batch: ('pokemon-codex', '6a841c0c08328fb1', 'ns')\n", - "Written batch: ('pokemon-codex', '6a841c0c08328fb1', 'ns')\n", - "Written batch: ('pokemon-codex', '6a841c0c08328fb1', 'ns')\n", - "Written batch: ('pokemon-codex', '6a841c0c08328fb1', 'ns')\n", - "Written batch: ('pokemon-codex', '6a841c0c08328fb1', 'ns')\n", - "Written batch: ('pokemon-codex', '6a841c0c08328fb1', 'ns')\n", - "Written batch: ('pokemon-codex', '6a841c0c08328fb1', 'ns')\n", - "Written batch: ('pokemon-codex', '6a841c0c08328fb1', 'ns')\n" - ] - } - ], - "source": [ - "client.write_file('kanto.parquet', measurement_name='kanto2', database='pokemon-codex', timestamp_column='time', tag_columns=['trainer', 'id', 'num'])\n", - "time.sleep(2)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Group by time query\n", - "Finally lets run a group by time query to get the number of pokemon caught by each trainer over the last hour grouped into 10 minute intervals. We will then use Plotly to visualise this data." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.plotly.v1+json": { - "config": { - "plotlyServerURL": "https://plot.ly" - }, - "data": [ - { - "hovertemplate": "trainer=ash
time=%{x}
count=%{y}", - "legendgroup": "ash", - "line": { - "color": "#636efa", - "dash": "solid" - }, - "marker": { - "symbol": "circle" - }, - "mode": "lines", - "name": "ash", - "orientation": "v", - "showlegend": true, - "type": "scatter", - "x": [ - "2023-07-27T09:50:00", - "2023-07-27T10:00:00", - "2023-07-27T10:10:00", - "2023-07-27T10:20:00", - "2023-07-27T10:30:00", - "2023-07-27T10:40:00", - "2023-07-27T10:50:00" - ], - "xaxis": "x", - "y": [ - 204, - 252, - 256, - 265, - 265, - 270, - 0 - ], - "yaxis": "y" - }, - { - "hovertemplate": "trainer=brock
time=%{x}
count=%{y}", - "legendgroup": "brock", - "line": { - "color": "#EF553B", - "dash": "solid" - }, - "marker": { - "symbol": "circle" - }, - "mode": "lines", - "name": "brock", - "orientation": "v", - "showlegend": true, - "type": "scatter", - "x": [ - "2023-07-27T09:50:00", - "2023-07-27T10:00:00", - "2023-07-27T10:10:00", - "2023-07-27T10:20:00", - "2023-07-27T10:30:00", - "2023-07-27T10:40:00", - "2023-07-27T10:50:00" - ], - "xaxis": "x", - "y": [ - 211, - 286, - 280, - 260, - 261, - 235, - 0 - ], - "yaxis": "y" - }, - { - "hovertemplate": "trainer=gary
time=%{x}
count=%{y}", - "legendgroup": "gary", - "line": { - "color": "#00cc96", - "dash": "solid" - }, - "marker": { - "symbol": "circle" - }, - "mode": "lines", - "name": "gary", - "orientation": "v", - "showlegend": true, - "type": "scatter", - "x": [ - "2023-07-27T09:50:00", - "2023-07-27T10:00:00", - "2023-07-27T10:10:00", - "2023-07-27T10:20:00", - "2023-07-27T10:30:00", - "2023-07-27T10:40:00", - "2023-07-27T10:50:00" - ], - "xaxis": "x", - "y": [ - 223, - 302, - 275, - 276, - 255, - 231, - 0 - ], - "yaxis": "y" - }, - { - "hovertemplate": "trainer=james
time=%{x}
count=%{y}", - "legendgroup": "james", - "line": { - "color": "#ab63fa", - "dash": "solid" - }, - "marker": { - "symbol": "circle" - }, - "mode": "lines", - "name": "james", - "orientation": "v", - "showlegend": true, - "type": "scatter", - "x": [ - "2023-07-27T09:50:00", - "2023-07-27T10:00:00", - "2023-07-27T10:10:00", - "2023-07-27T10:20:00", - "2023-07-27T10:30:00", - "2023-07-27T10:40:00", - "2023-07-27T10:50:00" - ], - "xaxis": "x", - "y": [ - 236, - 275, - 286, - 277, - 280, - 256, - 0 - ], - "yaxis": "y" - }, - { - "hovertemplate": "trainer=jessie
time=%{x}
count=%{y}", - "legendgroup": "jessie", - "line": { - "color": "#FFA15A", - "dash": "solid" - }, - "marker": { - "symbol": "circle" - }, - "mode": "lines", - "name": "jessie", - "orientation": "v", - "showlegend": true, - "type": "scatter", - "x": [ - "2023-07-27T09:50:00", - "2023-07-27T10:00:00", - "2023-07-27T10:10:00", - "2023-07-27T10:20:00", - "2023-07-27T10:30:00", - "2023-07-27T10:40:00", - "2023-07-27T10:50:00" - ], - "xaxis": "x", - "y": [ - 204, - 261, - 265, - 290, - 293, - 265, - 0 - ], - "yaxis": "y" - }, - { - "hovertemplate": "trainer=misty
time=%{x}
count=%{y}", - "legendgroup": "misty", - "line": { - "color": "#19d3f3", - "dash": "solid" - }, - "marker": { - "symbol": "circle" - }, - "mode": "lines", - "name": "misty", - "orientation": "v", - "showlegend": true, - "type": "scatter", - "x": [ - "2023-07-27T09:50:00", - "2023-07-27T10:00:00", - "2023-07-27T10:10:00", - "2023-07-27T10:20:00", - "2023-07-27T10:30:00", - "2023-07-27T10:40:00", - "2023-07-27T10:50:00" - ], - "xaxis": "x", - "y": [ - 215, - 276, - 266, - 268, - 270, - 265, - 0 - ], - "yaxis": "y" - } - ], - "layout": { - "legend": { - "title": { - "text": "trainer" - }, - "tracegroupgap": 0 - }, - "template": { - "data": { - "bar": [ - { - "error_x": { - "color": "#2a3f5f" - }, - "error_y": { - "color": "#2a3f5f" - }, - "marker": { - "line": { - "color": "#E5ECF6", - "width": 0.5 - }, - "pattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - } - }, - "type": "bar" - } - ], - "barpolar": [ - { - "marker": { - "line": { - "color": "#E5ECF6", - "width": 0.5 - }, - "pattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - } - }, - "type": "barpolar" - } - ], - "carpet": [ - { - "aaxis": { - "endlinecolor": "#2a3f5f", - "gridcolor": "white", - "linecolor": "white", - "minorgridcolor": "white", - "startlinecolor": "#2a3f5f" - }, - "baxis": { - "endlinecolor": "#2a3f5f", - "gridcolor": "white", - "linecolor": "white", - "minorgridcolor": "white", - "startlinecolor": "#2a3f5f" - }, - "type": "carpet" - } - ], - "choropleth": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "type": "choropleth" - } - ], - "contour": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "contour" - } - ], - "contourcarpet": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "type": "contourcarpet" - } - ], - "heatmap": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "heatmap" - } - ], - "heatmapgl": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "heatmapgl" - } - ], - "histogram": [ - { - "marker": { - "pattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - } - }, - "type": "histogram" - } - ], - "histogram2d": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "histogram2d" - } - ], - "histogram2dcontour": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "histogram2dcontour" - } - ], - "mesh3d": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "type": "mesh3d" - } - ], - "parcoords": [ - { - "line": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "parcoords" - } - ], - "pie": [ - { - "automargin": true, - "type": "pie" - } - ], - "scatter": [ - { - "fillpattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - }, - "type": "scatter" - } - ], - "scatter3d": [ - { - "line": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatter3d" - } - ], - "scattercarpet": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattercarpet" - } - ], - "scattergeo": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattergeo" - } - ], - "scattergl": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattergl" - } - ], - "scattermapbox": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattermapbox" - } - ], - "scatterpolar": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatterpolar" - } - ], - "scatterpolargl": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatterpolargl" - } - ], - "scatterternary": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatterternary" - } - ], - "surface": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "surface" - } - ], - "table": [ - { - "cells": { - "fill": { - "color": "#EBF0F8" - }, - "line": { - "color": "white" - } - }, - "header": { - "fill": { - "color": "#C8D4E3" - }, - "line": { - "color": "white" - } - }, - "type": "table" - } - ] - }, - "layout": { - "annotationdefaults": { - "arrowcolor": "#2a3f5f", - "arrowhead": 0, - "arrowwidth": 1 - }, - "autotypenumbers": "strict", - "coloraxis": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "colorscale": { - "diverging": [ - [ - 0, - "#8e0152" - ], - [ - 0.1, - "#c51b7d" - ], - [ - 0.2, - "#de77ae" - ], - [ - 0.3, - "#f1b6da" - ], - [ - 0.4, - "#fde0ef" - ], - [ - 0.5, - "#f7f7f7" - ], - [ - 0.6, - "#e6f5d0" - ], - [ - 0.7, - "#b8e186" - ], - [ - 0.8, - "#7fbc41" - ], - [ - 0.9, - "#4d9221" - ], - [ - 1, - "#276419" - ] - ], - "sequential": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "sequentialminus": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ] - }, - "colorway": [ - "#636efa", - "#EF553B", - "#00cc96", - "#ab63fa", - "#FFA15A", - "#19d3f3", - "#FF6692", - "#B6E880", - "#FF97FF", - "#FECB52" - ], - "font": { - "color": "#2a3f5f" - }, - "geo": { - "bgcolor": "white", - "lakecolor": "white", - "landcolor": "#E5ECF6", - "showlakes": true, - "showland": true, - "subunitcolor": "white" - }, - "hoverlabel": { - "align": "left" - }, - "hovermode": "closest", - "mapbox": { - "style": "light" - }, - "paper_bgcolor": "white", - "plot_bgcolor": "#E5ECF6", - "polar": { - "angularaxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - }, - "bgcolor": "#E5ECF6", - "radialaxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - } - }, - "scene": { - "xaxis": { - "backgroundcolor": "#E5ECF6", - "gridcolor": "white", - "gridwidth": 2, - "linecolor": "white", - "showbackground": true, - "ticks": "", - "zerolinecolor": "white" - }, - "yaxis": { - "backgroundcolor": "#E5ECF6", - "gridcolor": "white", - "gridwidth": 2, - "linecolor": "white", - "showbackground": true, - "ticks": "", - "zerolinecolor": "white" - }, - "zaxis": { - "backgroundcolor": "#E5ECF6", - "gridcolor": "white", - "gridwidth": 2, - "linecolor": "white", - "showbackground": true, - "ticks": "", - "zerolinecolor": "white" - } - }, - "shapedefaults": { - "line": { - "color": "#2a3f5f" - } - }, - "ternary": { - "aaxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - }, - "baxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - }, - "bgcolor": "#E5ECF6", - "caxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - } - }, - "title": { - "x": 0.05 - }, - "xaxis": { - "automargin": true, - "gridcolor": "white", - "linecolor": "white", - "ticks": "", - "title": { - "standoff": 15 - }, - "zerolinecolor": "white", - "zerolinewidth": 2 - }, - "yaxis": { - "automargin": true, - "gridcolor": "white", - "linecolor": "white", - "ticks": "", - "title": { - "standoff": 15 - }, - "zerolinecolor": "white", - "zerolinewidth": 2 - } - } - }, - "title": { - "text": "Number of Pokémon caught in the last hour grouped by trainer and time" - }, - "xaxis": { - "anchor": "y", - "domain": [ - 0, - 1 - ], - "title": { - "text": "time" - } - }, - "yaxis": { - "anchor": "x", - "domain": [ - 0, - 1 - ], - "title": { - "text": "count" - } - } - } - } - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "query='''SELECT count(\"name\") FROM kanto2 WHERE time > now() - 1h GROUP BY time(10m),trainer'''\n", - "table = client.query(query=query, language='influxql', mode='all')\n", - "\n", - "fig4 = px.line(table, x=\"time\", y=\"count\", color='trainer', title='Number of Pokémon caught in the last hour grouped by trainer and time')\n", - "fig4.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Conclusion\n", - "We have now covered the basics of the InfluxDB 3.0 Python Client. I hope you found this novel cook book informative and fun. If you have any questions, bugs or feature requests please raise an issue on the [GitHub repo](https://github.com/InfluxCommunity/influxdb3-python/issues)\n", - "\n", - "\n", - "

\n", - "\n", - "

" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": ".venv", - "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.4" - }, - "orig_nbformat": 4 - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/Examples/pokemon-trainer/kanto.parquet b/Examples/pokemon-trainer/kanto.parquet deleted file mode 100644 index 599571419b5fdcef72f6057f9aabc411249bfff7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 79453 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ⅈT6>v2bYFElaO#tomJ5#pJ?}cL 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

+YZ| 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 now() - 30m''', database='pokemon-codex', - timeout=90.0, language='sql', mode='pandas') - print(table) -except Exception as e: - print(f"Error querying points: {e}") diff --git a/Examples/pokemon-trainer/write-batching.py b/Examples/pokemon-trainer/write-batching.py deleted file mode 100644 index f6fa180..0000000 --- a/Examples/pokemon-trainer/write-batching.py +++ /dev/null @@ -1,101 +0,0 @@ -import random - -import pandas as pd - -from influxdb_client_3 import InfluxDBClient3, InfluxDBError, WriteOptions, write_client_options - - -class BatchingCallback(object): - - def success(self, conf, data: str): - print(f"Written batch: {conf}, data: {data}") - - def error(self, conf, data: str, exception: InfluxDBError): - print(f"Cannot write batch: {conf}, data: {data} due: {exception}") - - def retry(self, conf, data: str, exception: InfluxDBError): - print(f"Retryable error occurs for batch: {conf}, data: {data} retry: {exception}") - - -callback = BatchingCallback() - -write_options = WriteOptions(batch_size=100, - flush_interval=10_000, - jitter_interval=2_000, - retry_interval=5_000, - max_retries=5, - max_retry_delay=30_000, - exponential_base=2) - -wco = write_client_options(success_callback=callback.success, - error_callback=callback.error, - retry_callback=callback.retry, - write_options=write_options - ) - -client = InfluxDBClient3( - token="", - host="eu-central-1-1.aws.cloud2.influxdata.com", - database="pokemon-codex", enable_gzip=True, write_client_options=wco) - -now = pd.Timestamp.now(tz='UTC').floor('ms') - -# Lists of possible trainers -trainers = ["ash", "brock", "misty", "gary", "jessie", "james"] - -# Read the CSV into a DataFrame -pokemon_df = pd.read_csv("https://gist.githubusercontent.com/ritchie46/cac6b337ea52281aa23c049250a4ff03/raw/89a957ff3919d90e6ef2d34235e6bf22304f3366/pokemon.csv") # noqa: E501 - -# Creating an empty list to store the data -data = [] - -# Dictionary to keep track of the number of times each trainer has caught each Pokémon -trainer_pokemon_counts = {} - -# Number of entries we want to create -num_entries = 1000 - -# Generating random data -for i in range(num_entries): - trainer = random.choice(trainers) - - # Randomly select a row from pokemon_df - random_pokemon = pokemon_df.sample().iloc[0] - caught = random_pokemon['Name'] - - # Count the number of times this trainer has caught this Pokémon - if (trainer, caught) in trainer_pokemon_counts: - trainer_pokemon_counts[(trainer, caught)] += 1 - else: - trainer_pokemon_counts[(trainer, caught)] = 1 - - # Get the number for this combination of trainer and Pokémon - num = trainer_pokemon_counts[(trainer, caught)] - - entry = { - "trainer": trainer, - "id": f"{0000 + random_pokemon['#']:04d}", - "num": str(num), - "caught": caught, - "level": random.randint(5, 20), - "attack": random_pokemon['Attack'], - "defense": random_pokemon['Defense'], - "hp": random_pokemon['HP'], - "speed": random_pokemon['Speed'], - "type1": random_pokemon['Type 1'], - "type2": random_pokemon['Type 2'], - "timestamp": now - } - data.append(entry) - -# Convert the list of dictionaries to a DataFrame -caught_pokemon_df = pd.DataFrame(data).set_index('timestamp') - -# Print the DataFrame -print(caught_pokemon_df) - -try: - client.write(caught_pokemon_df, data_frame_measurement_name='caught', - data_frame_tag_columns=['trainer', 'id', 'num']) -except Exception as e: - print(f"Error writing point: {e}") diff --git a/Examples/pokemon-trainer/pokemon.csv b/Examples/write/source_data/pokemon.csv similarity index 100% rename from Examples/pokemon-trainer/pokemon.csv rename to Examples/write/source_data/pokemon.csv diff --git a/Examples/write/writeoptions.py b/Examples/write/writeoptions.py new file mode 100644 index 0000000..c895fd1 --- /dev/null +++ b/Examples/write/writeoptions.py @@ -0,0 +1,84 @@ +import datetime + +from Examples.config import Config +from influxdb_client_3 import InfluxDBClient3, Point, SYNCHRONOUS, write_client_options + +wco = write_client_options(write_options=SYNCHRONOUS) + + +def main(config: Config): + + with InfluxDBClient3( + token=config.token, + host=config.host, + database="pokemon-codex", + write_client_options=wco, + debug=True) as client: + + now = datetime.datetime.now(datetime.timezone.utc) + + data = Point("caught").tag("trainer", "ash").tag("id", "0006").tag("num", "1") \ + .field("caught", "charizard") \ + .field("level", 10).field("attack", 30) \ + .field("defense", 40).field("hp", 200) \ + .field("speed", 10) \ + .field("type1", "fire").field("type2", "flying") \ + .time(now) + + try: + client.write(data) + except Exception as e: + print(f"Error writing point: {e}") + + data = [Point("caught") # point 1 + .tag("trainer", "ash") + .tag("id", "0006") + .tag("num", "1") + .field("caught", "charizard") + .field("level", 10) + .field("attack", 30) + .field("defense", 40) + .field("hp", 200) + .field("speed", 10) + .field("type1", "fire") + .field("type2", "flying") + .time(now), + + Point("caught") # point 2 + .tag("trainer", "ash") + .tag("id", "0007") + .tag("num", "2") + .field("caught", "bulbasaur") + .field("level", 12) + .field("attack", 31) + .field("defense", 31) + .field("hp", 190) + .field("speed", 11) + .field("type1", "grass") + .field("type2", "poison") + .time(now), + + Point("caught") # point 3 + .tag("trainer", "ash") + .tag("id", "0008") + .tag("num", "3") + .field("caught", "squirtle") + .field("level", 13) + .field("attack", 29) + .field("defense", 40) + .field("hp", 180) + .field("speed", 13) + .field("type1", "water") + .field("type2", None) + .time(now) + ] + + try: + client.write(data) + print(f"Write success: {len(data)} points!") + except Exception as e: + print(f"Error writing point: {e}") + + +if __name__ == "__main__": + main(Config()) From d22b13262d3567ee4fbfba8e0718f31ab44338bb Mon Sep 17 00:00:00 2001 From: karel rehor Date: Mon, 20 Apr 2026 15:20:54 +0200 Subject: [PATCH 06/36] chore: checkpoint 2 - refactoring - old examples moved or removed. --- Examples/README.md | 30 +- Examples/cloud_dedicated_query.py | 15 - Examples/cloud_dedicated_write.py | 48 -- Examples/{ => core}/basic_ssl_example.py | 2 +- Examples/{ => core}/timeouts.py | 0 Examples/example.csv | 489 ------------------ .../{ => query}/flight_options_example.py | 0 Examples/{ => query}/handle_query_error.py | 0 Examples/{ => query}/query_async.py | 0 .../{query_type.py => query/query_modes.py} | 0 Examples/{ => query}/query_with_middleware.py | 0 Examples/{ => write}/batching_example.py | 0 Examples/{ => write}/handle_http_error.py | 0 Examples/{ => write}/pandas_write.py | 0 14 files changed, 18 insertions(+), 566 deletions(-) delete mode 100644 Examples/cloud_dedicated_query.py delete mode 100644 Examples/cloud_dedicated_write.py rename Examples/{ => core}/basic_ssl_example.py (99%) rename Examples/{ => core}/timeouts.py (100%) delete mode 100644 Examples/example.csv rename Examples/{ => query}/flight_options_example.py (100%) rename Examples/{ => query}/handle_query_error.py (100%) rename Examples/{ => query}/query_async.py (100%) rename Examples/{query_type.py => query/query_modes.py} (100%) rename Examples/{ => query}/query_with_middleware.py (100%) rename Examples/{ => write}/batching_example.py (100%) rename Examples/{ => write}/handle_http_error.py (100%) rename Examples/{ => write}/pandas_write.py (100%) diff --git a/Examples/README.md b/Examples/README.md index c43e3bd..7c2c32a 100644 --- a/Examples/README.md +++ b/Examples/README.md @@ -33,17 +33,21 @@ TODO - delete this section as examples take shape and before creating PR. 3. Keep one simple example of jupiter notebook. __DONE__ 4. Decide what to do with `./community` examples. 5. Root examples - 1. `basic_ssl_examle.py` to `./core` - 2. `batching_example.py` to `./write` - 3. `cloud_dedicated_query.py` - is it necessary to have specific _cloud_dedicated_ examples? Can this simply be documented in `README.md` or in code comments? - 4. `cloud_dedicated_write.py` - is it necessary to have specific _cloud_dedicated_ examples? Can this simply be documented in `README.md` or in code comments? + 1. `basic_ssl_examle.py` to `./core` __DONE__ + 2. `batching_example.py` to `./write` __DONE__ + 3. `cloud_dedicated_query.py` - is it necessary to have specific _cloud_dedicated_ examples? Can this simply be documented in `README.md` or in code comments? (Removed __DONE__) + 4. `cloud_dedicated_write.py` - is it necessary to have specific _cloud_dedicated_ examples? Can this simply be documented in `README.md` or in code comments? (Removed __DONE__) 5. `config.py` - universal configuration file. Keep as is. - 6. `example.csv` - where is this used? It doesn't seem to be used in any example... ??? - 7. `flight_options_example.py` - to `./query` - 8. `handle_http_error.py` - to `./write` - 9. `handle_query_error.py` - to `./query` - 10. `pandas_write.py` - to `./write` - 11. `query_async.py` - to `./query` - 12. `query_type.py` - possible rename. `_type` token in name is unclear. Study closer. Move to `./query` - 13. `query_with_middleware.py` - to `./query` - 14. `timeouts.py` - to `./core` + 6. `example.csv` - where is this used? It doesn't seem to be used in any example... ??? (Removed __DONE__) + 7. `flight_options_example.py` - to `./query` __DONE__ + - Note only option illustrated is tls certificate. + - TODO after move - either enrich/refactor or verify this isn't covered elsewhere + 8. `handle_http_error.py` - to `./write` __DONE__ + 9. `handle_query_error.py` - to `./query` __DONE__ + 10. `pandas_write.py` - to `./write` __DONE__ + 11. `query_async.py` - to `./query` __DONE__ + 12. `query_type.py` - rename to `query_modes.py`. Move to `./query` __DONE__ + 13. `query_with_middleware.py` - to `./query` __DONE__ + 14. `timeouts.py` - to `./core` __DONE__ +6. Leverage `config.py` in all examples +7. Verify all refactored examples are working diff --git a/Examples/cloud_dedicated_query.py b/Examples/cloud_dedicated_query.py deleted file mode 100644 index 38e5ae7..0000000 --- a/Examples/cloud_dedicated_query.py +++ /dev/null @@ -1,15 +0,0 @@ -from config import Config -import influxdb_client_3 as InfluxDBClient3 - -config = Config() - -client = InfluxDBClient3.InfluxDBClient3( - token=config.token, - host=config.host, - database=config.database) - -table = client.query( - query="SELECT * FROM flight WHERE time > now() - 4h", - language="influxql") - -print(table.to_pandas()) diff --git a/Examples/cloud_dedicated_write.py b/Examples/cloud_dedicated_write.py deleted file mode 100644 index f51422e..0000000 --- a/Examples/cloud_dedicated_write.py +++ /dev/null @@ -1,48 +0,0 @@ -from config import Config -import influxdb_client_3 as InfluxDBClient3 -from influxdb_client_3 import WriteOptions -import pandas as pd -import numpy as np - -config = Config() - -client = InfluxDBClient3.InfluxDBClient3( - token=config.token, - host=config.host, - database=config.database, - write_options=WriteOptions( - batch_size=500, - flush_interval=10_000, - jitter_interval=2_000, - retry_interval=5_000, - max_retries=5, - max_retry_delay=30_000, - max_close_wait=300_000, - exponential_base=2, - write_type='batching')) - - -# Create a dataframe -df = pd.DataFrame({'a': [1, 2, 3], 'b': [4, 5, 6]}) - - -# Create a range of datetime values -dates = pd.date_range(start='2024-09-08', end='2024-09-09', freq='5min') - -# Create a DataFrame with random data and datetime index -df = pd.DataFrame( - np.random.randn( - len(dates), - 3), - index=dates, - columns=[ - 'Column 1', - 'Column 2', - 'Column 3']) -df['tagkey'] = 'Hello World' - -print(df) - -# Write the DataFrame to InfluxDB -client.write(df, data_frame_measurement_name='table', - data_frame_tag_columns=['tagkey']) diff --git a/Examples/basic_ssl_example.py b/Examples/core/basic_ssl_example.py similarity index 99% rename from Examples/basic_ssl_example.py rename to Examples/core/basic_ssl_example.py index 1667f78..a1f764d 100644 --- a/Examples/basic_ssl_example.py +++ b/Examples/core/basic_ssl_example.py @@ -3,7 +3,7 @@ import pyarrow -from config import Config +from Examples.config import Config from influxdb_client_3 import InfluxDBClient3 bad_cert = """-----BEGIN CERTIFICATE----- diff --git a/Examples/timeouts.py b/Examples/core/timeouts.py similarity index 100% rename from Examples/timeouts.py rename to Examples/core/timeouts.py diff --git a/Examples/example.csv b/Examples/example.csv deleted file mode 100644 index 0ab1db8..0000000 --- a/Examples/example.csv +++ /dev/null @@ -1,489 +0,0 @@ -Date,VIX Open,VIX High,VIX Low,VIX Close,host,region -2021-05-05,13.48,15.82,13.43,13.98,412df,US -2021-05-06,13.29,14.06,13.11,14.05,412df,US -2021-05-09,14.11,14.56,13.53,13.75,412df,US -2021-05-10,14.05,15.11,14.03,14.91,412df,US -2021-05-11,14.69,15.8,14.31,14.45,412df,US -2021-05-12,14.43,16.23,14.16,16.12,412df,US -2021-05-13,15.88,17.7,15.4,16.32,412df,US -2021-05-16,16.87,16.87,15.67,15.68,412df,US -2021-05-17,15.92,16.21,14.45,14.57,412df,US -2021-05-18,14.11,14.13,13.3,13.63,412df,US -2021-05-19,13.35,14.02,13.25,13.32,412df,US -2021-05-20,13.57,13.73,13.07,13.14,412df,US -2021-05-23,13.38,13.45,12.75,12.95,412df,US -2021-05-24,13.12,13.3,12.53,12.69,412df,US -2021-05-25,12.83,13.11,12.58,12.58,412df,US -2021-05-26,12.23,12.38,11.65,12.24,412df,US -2021-05-27,12.06,12.43,12.06,12.15,412df,US -2021-05-31,12.69,13.45,12.67,13.29,412df,US -2021-06-01,13.34,13.34,12.17,12.36,412df,US -2021-06-02,12.35,12.43,11.8,11.84,412df,US -2021-06-03,11.78,12.49,11.52,12.15,412df,US -2021-06-06,12.68,13,12.21,12.28,412df,US -2021-06-07,12.22,12.43,11.47,12.39,412df,US -2021-06-08,12.05,12.83,12.05,12.7,412df,US -2021-06-09,12.69,13.07,12.06,12.08,412df,US -2021-06-10,12.16,12.4,11.9,11.96,412df,US -2021-06-13,12.12,12.12,11.31,11.65,412df,US -2021-06-14,11.65,12.02,11.52,11.79,412df,US -2021-06-15,11.22,11.96,11.05,11.46,412df,US -2021-06-16,11.24,11.36,11.04,11.15,412df,US -2021-06-17,10.83,11.64,10.78,11.48,412df,US -2021-06-20,12.51,12.53,11.31,11.47,412df,US -2021-06-21,11.52,11.67,11.01,11.08,412df,US -2021-06-22,10.96,11.31,10.81,11.05,412df,US -2021-06-23,11.19,12.13,10.96,12.13,412df,US -2021-06-24,12.27,12.32,11.95,12.18,412df,US -2021-06-27,12.73,12.78,12.38,12.52,412df,US -2021-06-28,12.31,12.31,11.55,11.58,412df,US -2021-06-29,11.54,11.78,11.44,11.77,412df,US -2021-06-30,11.72,12.1,11.5,12.04,412df,US -2021-07-01,11.75,11.92,11.38,11.4,412df,US -2021-07-05,12.41,12.41,11.53,11.68,412df,US -2021-07-06,11.67,12.33,11.54,12.27,412df,US -2021-07-07,13.91,13.92,12.48,12.49,412df,US -2021-07-08,12.24,12.29,11.09,11.45,412df,US -2021-07-11,10.96,11.41,10.53,11.28,412df,US -2021-07-12,11.03,11.37,10.85,10.95,412df,US -2021-07-13,11.09,11.44,10.8,10.84,412df,US -2021-07-14,10.87,11.13,10.5,10.81,412df,US -2021-07-15,10.61,10.9,10.13,10.33,412df,US -2021-07-18,11.09,11.09,10.77,10.77,412df,US -2021-07-19,10.63,10.79,10.36,10.45,412df,US -2021-07-20,10.8,10.94,9.88,10.23,412df,US -2021-07-21,10.23,11.21,10.23,10.97,412df,US -2021-07-22,11.01,11.05,10.51,10.52,412df,US -2021-07-25,11.12,11.28,10.7,11.1,412df,US -2021-07-26,11.11,11.25,10.92,10.99,412df,US -2021-07-27,11.03,11.03,10.34,10.36,412df,US -2021-07-28,10.4,10.58,10.27,10.52,412df,US -2021-07-29,10.24,11.73,10.24,11.57,412df,US -2021-08-01,11.75,12.23,11.36,12.08,412df,US -2021-08-02,12,12.03,11.69,11.75,412df,US -2021-08-03,11.9,12.01,11.62,11.83,412df,US -2021-08-04,12.12,12.57,11.99,12.52,412df,US -2021-08-05,11.82,12.92,11.82,12.48,412df,US -2021-08-08,12.56,13.29,12.56,13.21,412df,US -2021-08-09,12.25,12.78,12.24,12.4,412df,US -2021-08-10,12.03,12.56,11.51,12.38,412df,US -2021-08-11,12.34,12.94,11.88,12.42,412df,US -2021-08-12,12.36,12.98,12.18,12.74,412df,US -2021-08-15,12.87,12.99,12.08,12.26,412df,US -2021-08-16,12.37,13.89,12.28,13.52,412df,US -2021-08-17,13.35,13.76,12.78,13.3,412df,US -2021-08-18,13.47,13.88,13.01,13.42,412df,US -2021-08-19,12.84,13.85,12.61,13.42,412df,US -2021-08-22,13.67,13.89,13.2,13.42,412df,US -2021-08-23,13.63,14.05,13.3,13.34,412df,US -2021-08-24,13.56,14.18,13,14.17,412df,US -2021-08-25,13.92,14.12,13.66,13.73,412df,US -2021-08-26,13.79,14.2,13.65,13.72,412df,US -2021-08-29,14.34,14.41,13.38,13.52,412df,US -2021-08-30,13.49,14.18,13.47,13.65,412df,US -2021-08-31,13.37,13.63,12.56,12.6,412df,US -2021-09-01,12.86,13.37,12.31,13.15,412df,US -2021-09-02,13.21,13.72,13.03,13.57,412df,US -2021-09-06,14.17,14.18,12.9,12.93,412df,US -2021-09-07,13.06,13.2,12.44,12.52,412df,US -2021-09-08,12.72,12.94,12.37,12.93,412df,US -2021-09-09,12.46,12.65,11.96,11.98,412df,US -2021-09-12,11.66,11.89,11.45,11.65,412df,US -2021-09-13,11.76,12.48,11.75,12.39,412df,US -2021-09-14,12.79,13.26,12.67,12.91,412df,US -2021-09-15,12.72,13.17,12.28,12.49,412df,US -2021-09-16,11.89,11.93,11.07,11.22,412df,US -2021-09-19,12.17,12.46,12.04,12.14,412df,US -2021-09-20,12.23,12.96,11.83,12.64,412df,US -2021-09-21,13.07,13.91,12.96,13.79,412df,US -2021-09-22,13.85,14.39,13.31,13.33,412df,US -2021-09-23,13.64,13.88,12.75,12.96,412df,US -2021-09-26,13.27,13.47,12.64,13.04,412df,US -2021-09-27,13.06,13.46,12.51,12.76,412df,US -2021-09-28,12.6,12.98,12.37,12.63,412df,US -2021-09-29,12.92,13.23,12.15,12.24,412df,US -2021-09-30,12.42,12.44,11.92,11.92,412df,US -2021-10-03,12.37,12.62,11.98,12.46,412df,US -2021-10-04,12.5,13.21,12.11,13.2,412df,US -2021-10-05,13.15,14.58,12.97,14.55,412df,US -2021-10-06,14.53,15.63,14.04,14.96,412df,US -2021-10-07,14.56,14.82,14.3,14.59,412df,US -2021-10-10,14.93,15.61,14.87,15.55,412df,US -2021-10-11,15.35,15.7,15.06,15.63,412df,US -2021-10-12,15.56,16.27,15.04,16.22,412df,US -2021-10-13,16.32,17.19,15.98,16.47,412df,US -2021-10-14,15.99,16.03,14.83,14.87,412df,US -2021-10-17,15.3,15.3,14.58,14.67,412df,US -2021-10-18,14.92,15.4,14.79,15.33,412df,US -2021-10-19,15.63,15.86,13.47,13.5,412df,US -2021-10-20,14.18,16.19,13.98,16.11,412df,US -2021-10-21,15.53,16.27,15.26,16.13,412df,US -2021-10-24,16.18,16.44,14.61,14.74,412df,US -2021-10-25,14.96,15.3,14.2,14.53,412df,US -2021-10-26,14.8,14.88,13.72,14.59,412df,US -2021-10-27,14.73,16.3,14.57,16.02,412df,US -2021-10-28,15.45,15.61,14.11,14.25,412df,US -2021-10-31,14.04,15.39,13.93,15.32,412df,US -2021-11-01,14.85,15.66,14.65,14.85,412df,US -2021-11-02,14.99,14.99,13.38,13.48,412df,US -2021-11-03,13.12,13.23,12.5,13,412df,US -2021-11-04,12.88,13.62,12.69,13.17,412df,US -2021-11-07,13.11,13.65,12.76,13.1,412df,US -2021-11-08,13.61,13.61,13.04,13.08,412df,US -2021-11-09,13.14,13.36,12.42,12.8,412df,US -2021-11-10,12.68,13.17,11.73,11.9,412df,US -2021-11-11,11.82,11.96,11.63,11.63,412df,US -2021-11-14,11.99,12.34,11.77,12.18,412df,US -2021-11-15,12.16,12.69,11.7,12.23,412df,US -2021-11-16,12.22,12.68,12.17,12.26,412df,US -2021-11-17,11.92,12.2,11.25,11.25,412df,US -2021-11-18,11.19,11.67,10.96,11.12,412df,US -2021-11-21,11.18,11.38,10.71,10.82,412df,US -2021-11-22,11.01,11.1,10.5,10.6,412df,US -2021-11-23,10.96,11.03,10.67,10.96,412df,US -2021-11-25,11.04,11.24,10.88,10.88,412df,US -2021-11-28,11.34,11.99,11.33,11.84,412df,US -2021-11-29,11.77,11.96,11.61,11.89,412df,US -2021-11-30,11.84,12.13,11.55,12.06,412df,US -2021-12-01,12.09,12.23,11.14,11.24,412df,US -2021-12-02,11.1,11.23,10.94,11.01,412df,US -2021-12-05,11.45,11.68,11.4,11.6,412df,US -2021-12-06,11.16,11.59,10.96,11.52,412df,US -2021-12-07,11.49,12.44,11.45,12.18,412df,US -2021-12-08,11.88,12.41,11.73,12.21,412df,US -2021-12-09,11.91,12.2,11.56,11.69,412df,US -2021-12-12,10.86,11.81,10.83,11.47,412df,US -2021-12-13,10.57,11.42,10.57,11.11,412df,US -2021-12-14,10.75,10.79,10.39,10.48,412df,US -2021-12-15,10.66,11.11,10.37,10.73,412df,US -2021-12-16,10.7,10.75,10.15,10.68,412df,US -2021-12-19,11.11,11.38,10.65,11.38,412df,US -2021-12-20,11.32,11.45,11,11.19,412df,US -2021-12-21,10.71,11.07,10.34,10.81,412df,US -2021-12-22,10.82,10.89,10.28,10.29,412df,US -2021-12-23,10.37,10.48,10.24,10.27,412df,US -2021-12-27,10.97,11.65,10.83,11.57,412df,US -2021-12-28,11.55,11.64,11.26,11.35,412df,US -2021-12-29,11.36,11.67,11.14,11.61,412df,US -2021-12-30,11.96,12.07,11.55,12.07,412df,US -2022-01-03,12.25,12.51,10.99,11.14,412df,US -2022-01-04,11.22,11.71,10.97,11.37,412df,US -2022-01-05,11.43,11.84,11.31,11.31,412df,US -2022-01-06,11.23,11.5,10.81,11,412df,US -2022-01-09,11.35,11.35,10.98,11.13,412df,US -2022-01-10,11.39,11.39,10.84,10.86,412df,US -2022-01-11,11.03,11.16,10.83,10.94,412df,US -2022-01-12,10.98,11.38,10.94,11.2,412df,US -2022-01-13,11.4,11.61,11.11,11.23,412df,US -2022-01-17,12.13,12.46,11.87,11.91,412df,US -2022-01-18,12.62,12.7,12.2,12.25,412df,US -2022-01-19,12.12,12.34,11.65,11.98,412df,US -2022-01-20,12.1,14.56,11.89,14.56,412df,US -2022-01-23,14.44,14.48,13.58,13.93,412df,US -2022-01-24,13.79,13.83,13.14,13.31,412df,US -2022-01-25,12.95,13.38,12.62,12.87,412df,US -2022-01-26,12.89,12.91,12.19,12.42,412df,US -2022-01-27,12.27,12.33,11.72,11.97,412df,US -2022-01-30,12.22,12.46,12.07,12.39,412df,US -2022-01-31,12.45,13.06,12.45,12.95,412df,US -2022-02-01,13.03,13.03,12.34,12.36,412df,US -2022-02-02,12.54,13.47,12.48,13.23,412df,US -2022-02-03,13.37,13.7,12.64,12.96,412df,US -2022-02-06,13.43,13.43,12.99,13.04,412df,US -2022-02-07,13.2,13.67,12.97,13.59,412df,US -2022-02-08,13.41,13.61,12.76,12.83,412df,US -2022-02-09,12.56,13.15,12.27,13.12,412df,US -2022-02-10,13.35,13.73,12.69,12.87,412df,US -2022-02-13,13.34,13.7,13.05,13.35,412df,US -2022-02-14,13.09,13.15,12.01,12.25,412df,US -2022-02-15,12.43,12.95,12.21,12.31,412df,US -2022-02-16,12.19,12.29,11.13,11.48,412df,US -2022-02-17,11.64,12.21,11.52,12.01,412df,US -2022-02-21,12.36,12.64,12.28,12.41,412df,US -2022-02-22,12.27,12.3,11.68,11.88,412df,US -2022-02-23,11.86,11.97,11.58,11.87,412df,US -2022-02-24,11.94,12.06,11.42,11.46,412df,US -2022-02-27,11.79,11.83,11.4,11.59,412df,US -2022-02-28,11.74,12.36,11.71,12.34,412df,US -2022-03-01,12.05,12.07,11.52,11.54,412df,US -2022-03-02,11.79,11.95,11.56,11.72,412df,US -2022-03-03,12.03,12.1,11.36,11.96,412df,US -2022-03-06,12.22,12.91,12.22,12.74,412df,US -2022-03-07,12.92,13.34,12.64,12.66,412df,US -2022-03-08,12.83,13.34,12.26,12.32,412df,US -2022-03-09,12.21,12.69,12.01,12.68,412df,US -2022-03-10,12.52,12.6,11.79,11.85,412df,US -2022-03-13,11.72,11.75,10.89,11.37,412df,US -2022-03-14,11.61,11.72,10.53,10.74,412df,US -2022-03-15,11.17,11.54,10.98,11.35,413df,US -2022-03-16,10.59,12.05,10.57,11.98,413df,US -2022-03-17,11.56,12.2,11.53,12.12,413df,US -2022-03-20,12.11,12.21,10.79,11.79,413df,US -2022-03-21,11.71,11.89,11.17,11.62,413df,US -2022-03-22,11.71,11.79,11.11,11.21,413df,US -2022-03-23,11.19,11.57,11.11,11.17,413df,US -2022-03-24,11.23,11.48,11.09,11.19,413df,US -2022-03-27,11.46,11.72,11.41,11.46,413df,US -2022-03-28,11.69,11.9,11.38,11.58,413df,US -2022-03-29,11.46,11.57,10.79,10.95,413df,US -2022-03-30,11.03,11.73,10.7,11.57,413df,US -2022-03-31,11.42,11.65,11.27,11.39,413df,US -2022-04-03,11.47,11.62,11.03,11.57,413df,US -2022-04-04,11.66,11.8,11.09,11.14,413df,US -2022-04-05,11.24,11.36,11.06,11.13,413df,US -2022-04-06,11.34,11.74,11.25,11.45,413df,US -2022-04-07,11.44,12.31,11.2,12.26,413df,US -2022-04-10,12.44,12.49,12.04,12.19,413df,US -2022-04-11,12.09,13.06,12.06,13,413df,US -2022-04-12,13.08,13.09,12.66,12.76,413df,US -2022-04-13,12.93,13,12.28,12.38,413df,US -2022-04-17,12.8,13.02,12.27,12.58,413df,US -2022-04-18,12.55,12.55,11.31,11.4,413df,US -2022-04-19,11.52,11.8,11.23,11.32,413df,US -2022-04-20,11.3,11.67,11.02,11.64,413df,US -2022-04-21,11.24,11.98,11.19,11.59,413df,US -2022-04-24,12.26,12.42,11.67,11.75,413df,US -2022-04-25,11.71,12.19,11.59,11.75,413df,US -2022-04-26,11.76,11.79,11.34,11.76,413df,US -2022-04-27,12.27,12.52,11.55,11.84,413df,US -2022-04-28,12.13,12.13,11.42,11.59,413df,US -2022-05-01,11.83,12.59,11.75,12.54,413df,US -2022-05-02,12.2,12.27,11.93,11.99,413df,US -2022-05-03,12.21,12.45,11.95,11.99,413df,US -2022-05-04,11.92,12.16,11.71,11.86,413df,US -2022-05-05,11.39,11.62,11.18,11.62,413df,US -2022-05-08,11.98,12.06,11.81,12,413df,US -2022-05-09,12.1,12.15,11.88,11.99,413df,US -2022-05-10,12.22,12.32,11.78,11.78,413df,US -2022-05-11,12.33,12.92,11.99,12.49,413df,US -2022-05-12,12.71,14.26,12.71,14.19,413df,US -2022-05-15,15.12,15.13,13.44,13.57,413df,US -2022-05-16,13.49,13.61,12.98,13.35,413df,US -2022-05-17,13.83,16.26,13.39,16.26,413df,US -2022-05-18,15.54,17.09,14.96,16.99,413df,US -2022-05-19,16.16,18.01,15.9,17.18,413df,US -2022-05-22,18.55,19.62,16.73,17.72,413df,US -2022-05-23,16.7,18.26,15.36,18.26,413df,US -2022-05-24,17.87,19.87,17.13,17.36,413df,US -2022-05-25,16.67,16.67,15.31,15.5,413df,US -2022-05-26,14.92,15.04,14.26,14.26,413df,US -2022-05-30,15.39,18.72,15.39,18.66,413df,US -2022-05-31,18.09,18.09,16.41,16.44,413df,US -2022-06-01,16.47,16.56,14.48,14.52,413df,US -2022-06-02,14.17,14.97,13.92,14.32,413df,US -2022-06-05,14.97,17.14,14.97,16.65,413df,US -2022-06-06,16.72,18.56,16.65,17.34,413df,US -2022-06-07,17.21,17.86,16.07,17.8,413df,US -2022-06-08,17.92,20.75,17.88,18.35,413df,US -2022-06-09,17.9,18.45,17.07,18.12,413df,US -2022-06-12,18.13,21.25,17.89,20.96,413df,US -2022-06-13,20.95,23.81,20.27,23.81,413df,US -2022-06-14,23.45,23.49,21.45,21.46,413df,US -2022-06-15,21.05,21.14,15.65,15.9,413df,US -2022-06-16,16.56,18.13,16.52,17.25,413df,US -2022-06-19,16.06,18.1,15.73,17.83,413df,US -2022-06-20,17.52,17.65,16.39,16.69,413df,US -2022-06-21,16.67,16.72,14.88,15.52,413df,US -2022-06-22,15.95,16.66,15.56,15.88,413df,US -2022-06-23,16.41,16.58,14.94,15.89,413df,US -2022-06-26,16.55,16.61,15.49,15.62,413df,US -2022-06-27,15.58,16.55,15.27,16.4,413df,US -2022-06-28,16.02,17,15.78,15.79,413df,US -2022-06-29,15.28,15.3,12.93,13.03,413df,US -2022-06-30,12.9,13.47,12.74,13.08,413df,US -2022-07-03,13.29,13.51,12.77,13.05,413df,US -2022-07-05,13.92,14.77,13.86,14.15,413df,US -2022-07-06,14.04,14.04,13.25,13.65,413df,US -2022-07-07,13.9,14.45,13.43,13.97,413df,US -2022-07-10,14.17,14.5,13.67,14.02,413df,US -2022-07-11,14.31,14.68,13.1,13.14,413df,US -2022-07-12,13.39,14.85,13.26,14.49,413df,US -2022-07-13,15.17,17.83,15.17,17.79,413df,US -2022-07-14,17.57,18.79,17.28,18.05,413df,US -2022-07-17,18.73,18.76,17.75,18.64,413df,US -2022-07-18,18.2,19.58,17.66,17.74,413df,US -2022-07-19,17.62,17.62,14.47,15.55,413df,US -2022-07-20,15.1,16.37,14.87,16.21,413df,US -2022-07-21,16.23,17.56,16.23,17.4,413df,US -2022-07-24,17.08,17.08,14.89,14.98,413df,US -2022-07-25,15.44,15.68,14.31,14.85,413df,US -2022-07-26,15.04,15.21,14.09,14.62,413df,US -2022-07-27,14.26,15.39,14.07,14.94,413df,US -2022-07-28,14.73,14.75,14.06,14.33,413df,US -2022-07-31,15.01,15.13,14.86,14.95,413df,US -2022-08-01,15.49,16.15,15.03,15.05,413df,US -2022-08-02,14.93,14.93,13.92,14.34,413df,US -2022-08-03,15.1,15.19,14.2,14.46,413df,US -2022-08-04,14.03,14.91,13.65,14.34,413df,US -2022-08-07,14.98,15.53,14.97,15.23,413df,US -2022-08-08,15.2,15.55,14.86,15.23,413df,US -2022-08-09,14.76,15.41,14.07,15.2,413df,US -2022-08-10,15.56,15.73,14.45,14.46,413df,US -2022-08-11,14.72,14.85,14.3,14.3,413df,US -2022-08-14,14.15,14.43,13.41,14.26,413df,US -2022-08-15,13.57,13.72,13.15,13.42,413df,US -2022-08-16,12.69,12.95,12.11,12.41,413df,US -2022-08-17,12.69,12.72,12.21,12.24,413df,US -2022-08-18,12.11,12.52,11.57,11.64,413df,US -2022-08-21,12.4,12.62,12.21,12.22,413df,US -2022-08-22,12.42,12.42,11.93,12.19,413df,US -2022-08-23,12.26,12.73,12.16,12.4,413df,US -2022-08-24,12.24,12.58,12.21,12.4,413df,US -2022-08-25,12.41,12.47,12.08,12.31,413df,US -2022-08-28,12.92,12.92,12.07,12.18,413df,US -2022-08-29,12.27,12.83,12.21,12.28,413df,US -2022-08-30,12.14,12.35,12.1,12.22,413df,US -2022-08-31,12.23,12.33,12.07,12.31,413df,US -2022-09-01,12.14,12.33,11.91,11.96,413df,US -2022-09-05,12.8,12.86,12.43,12.63,413df,US -2022-09-06,13.06,13.88,13.06,13.74,413df,US -2022-09-07,14.07,14.49,13.67,13.88,413df,US -2022-09-08,13.79,13.8,13.05,13.16,413df,US -2022-09-11,13.95,13.95,12.75,12.99,413df,US -2022-09-12,13.04,13.14,11.55,11.92,413df,US -2022-09-13,11.4,11.56,10.99,11.18,413df,US -2022-09-14,11.36,12.02,10.74,11.55,413df,US -2022-09-15,11.49,12.54,11.4,11.76,413df,US -2022-09-18,12.28,12.41,11.58,11.78,413df,US -2022-09-19,11.96,12.69,11.86,11.98,413df,US -2022-09-20,11.75,11.75,11.34,11.39,413df,US -2022-09-21,11.43,12.6,11.28,12.25,413df,US -2022-09-22,12.47,13.28,12.47,12.59,413df,US -2022-09-25,12.97,13.41,11.93,12.12,413df,US -2022-09-26,12.23,12.23,11.51,11.53,413df,US -2022-09-27,11.65,11.9,11.42,11.58,413df,US -2022-09-28,11.64,12.06,11.59,11.72,413df,US -2022-09-29,11.75,12.1,11.72,11.98,413df,US -2022-10-02,12.45,12.72,12.11,12.57,413df,US -2022-10-03,12.68,12.91,11.97,12.24,413df,US -2022-10-04,12.64,12.64,11.62,11.86,413df,US -2022-10-05,11.94,12.06,11.65,11.98,413df,US -2022-10-06,11.99,12.17,11.55,11.56,413df,US -2022-10-09,12.07,12.09,11.58,11.68,413df,US -2022-10-10,11.77,11.89,11.47,11.52,413df,US -2022-10-11,11.75,12.03,11.31,11.62,413df,US -2022-10-12,11.5,11.51,11.09,11.09,413df,US -2022-10-13,11.17,11.35,10.75,10.75,413df,US -2022-10-16,11.07,11.2,10.91,11.09,413df,US -2022-10-17,11.36,12.03,11.35,11.73,413df,US -2022-10-18,11.44,11.83,11.33,11.34,413df,US -2022-10-19,11.51,11.57,10.78,10.9,413df,US -2022-10-20,11.06,11.23,10.44,10.63,413df,US -2022-10-23,11.15,11.25,10.62,11.08,413df,US -2022-10-24,11.25,11.27,10.78,10.78,413df,US -2022-10-25,10.88,10.88,10.6,10.66,413df,US -2022-10-26,10.56,10.96,10.47,10.56,413df,US -2022-10-27,10.66,10.99,10.53,10.8,413df,US -2022-10-30,11.36,11.43,10.92,11.2,413df,US -2022-10-31,11.1,11.39,10.99,11.1,413df,US -2022-11-01,10.93,11.68,10.89,11.51,413df,US -2022-11-02,11.61,11.76,11.41,11.42,413df,US -2022-11-03,11.16,11.43,10.34,11.16,413df,US -2022-11-06,11.39,11.41,10.99,11.16,413df,US -2022-11-07,11.06,11.19,10.87,11.09,413df,US -2022-11-08,11.41,11.49,10.7,10.75,413df,US -2022-11-09,10.65,11.07,10.57,11.01,413df,US -2022-11-10,11.01,11.12,10.77,10.79,413df,US -2022-11-13,11.19,11.26,10.69,10.86,413df,US -2022-11-14,10.89,11.31,10.14,10.5,413df,US -2022-11-15,10.47,10.61,10.13,10.31,413df,US -2022-11-16,10.13,10.35,10.04,10.16,413df,US -2022-11-17,10.37,10.48,10.05,10.05,413df,US -2022-11-20,10.42,10.48,9.91,9.97,413df,US -2022-11-21,10.05,10.06,9.84,9.9,413df,US -2022-11-22,10.02,10.15,9.81,10.14,413df,US -2022-11-24,10.81,10.86,10.56,10.73,413df,US -2022-11-27,11.26,12.33,11.14,12.3,413df,US -2022-11-28,12.48,12.55,11.56,11.62,413df,US -2022-11-29,11.41,11.43,10.67,10.83,413df,US -2022-11-30,10.88,11.3,10.58,10.91,413df,US -2022-12-01,11.07,12.28,10.96,11.66,413df,US -2022-12-04,12.01,12.01,10.98,11.23,413df,US -2022-12-05,11.13,11.59,10.85,11.27,413df,US -2022-12-06,11.39,11.55,11.19,11.33,413df,US -2022-12-07,11.35,12.68,11.17,12.67,413df,US -2022-12-08,12.58,12.67,11.91,12.07,413df,US -2022-12-11,11.88,11.93,10.71,10.71,413df,US -2022-12-12,11.2,11.39,10.35,10.65,413df,US -2022-12-13,10.45,10.58,10.15,10.18,413df,US -2022-12-14,10.74,10.75,9.64,9.97,413df,US -2022-12-15,9.68,10.07,9.39,10.05,413df,US -2022-12-18,10.64,10.91,10.46,10.6,413df,US -2022-12-19,11.06,11.31,10.27,10.3,413df,US -2022-12-20,10.3,10.33,10.03,10.26,413df,US -2022-12-21,10.36,10.89,10.13,10.53,413df,US -2022-12-22,10.59,11.46,10.59,11.36,413df,US -2022-12-26,12.03,12.03,11.24,11.26,413df,US -2022-12-27,11.12,11.12,10.59,10.64,413df,US -2022-12-28,10.84,11.06,10.73,10.99,413df,US -2022-12-29,10.95,11.65,10.71,11.56,413df,US -2023-01-03,12.16,12.75,11.53,12.04,413df,US -2023-01-04,12.4,12.42,11.28,11.51,413df,US -2023-01-05,11.84,12.25,11.68,12.14,413df,US -2023-01-08,12.48,12.83,11.78,12,413df,US -2023-01-09,11.86,12.47,11.69,11.91,413df,US -2023-01-10,12.34,12.5,11.43,11.47,413df,US -2023-01-11,11.42,11.48,10.5,10.87,413df,US -2023-01-12,10.93,10.93,10.14,10.15,413df,US -2023-01-16,10.64,10.89,10.4,10.74,413df,US -2023-01-17,10.9,10.9,10.35,10.59,413df,US -2023-01-18,10.65,11.04,10.45,10.85,413df,US -2023-01-19,10.8,11.03,10.24,10.4,413df,US -2023-01-22,10.77,11.08,10.62,10.77,413df,US -2023-01-23,10.77,10.94,10.22,10.34,413df,US -2023-01-24,10.41,10.41,9.87,9.89,413df,US -2023-01-25,9.99,11.38,9.95,11.22,413df,US -2023-01-26,10.95,11.6,10.92,11.13,413df,US -2023-01-29,11.5,11.6,10.92,11.45,413df,US -2023-01-30,11.28,11.49,10.95,10.96,413df,US -2023-01-31,11.09,11.26,10.27,10.42,413df,US -2023-02-01,10.32,10.43,10.14,10.31,413df,US -2023-02-02,10.3,10.36,9.96,10.08,413df,US -2023-02-05,10.53,10.7,10.44,10.55,413df,US -2023-02-06,10.55,10.88,10.44,10.65,413df,US -2023-02-07,10.31,10.56,10.24,10.32,413df,US -2023-02-08,10.49,10.91,10.4,10.44,413df,US -2023-02-09,10.42,11.56,10.25,11.1,413df,US -2023-02-12,11.33,11.88,11.3,11.61,413df,US -2023-02-13,11.42,11.42,10.33,10.34,413df,US -2023-02-14,10.19,10.26,9.7,10.23,413df,US -2023-02-15,10.28,10.32,10.05,10.22,413df,US -2023-02-16,10.42,10.44,9.98,10.02,413df,US -2023-02-20,10.62,10.72,10.14,10.24,413df,US -2023-02-21,10.48,10.53,10.17,10.2,413df,US -2023-02-22,10.23,10.52,10.01,10.18,413df,US -2023-02-23,10.41,10.7,10.36,10.58,413df,US -2023-02-26,10.59,11.44,10.52,11.15,413df,US -2023-02-27,12.12,19.01,12.1,18.31,413df,US -2023-02-28,17.21,17.29,14.5,15.42,413df,US -2023-03-01,17.76,19.4,15.36,15.82,413df,US -2023-03-02,16.7,18.63,16.04,18.61,413df,US -2023-03-05,20.4,20.41,18.13,19.63,413df,US -2023-03-06,18.31,18.34,15.76,15.96,413df,US -2023-03-07,16.25,16.27,14.52,15.24,413df,US -2023-03-08,14.34,14.7,13.48,14.29,413df,US -2023-03-09,13.7,14.62,13.67,14.09,413df,US -2023-03-12,14.96,14.96,13.76,13.99,413df,US -2023-03-13,14.87,18.42,14.6,18.13,413df,US -2023-03-14,17.63,21.25,16.75,17.27,413df,US -2023-03-15,16.91,16.91,15.27,16.43,413df,US -2023-03-16,15.38,17.71,15.29,16.79,413df,US -2023-03-19,15.82,15.82,14.58,14.59,413df,US -2023-03-20,15.01,15.16,13.25,13.27,413df,US -2023-03-21,13.27,13.67,11.21,12.19,413df,US -2023-03-22,12.29,12.95,12.04,12.93,413df,US -2023-03-23,13.13,13.18,12.46,12.95,413df,US -2023-03-26,13.39,14.7,12.91,13.16,413df,US -2023-03-27,13.35,13.9,13.35,13.48,413df,US -2023-03-28,14.28,15.51,14.22,14.98,413df,US -2023-03-29,14.45,16.05,14.34,15.14,413df,US -2023-03-30,14.94,15.82,14.14,14.64,413df,US -2023-04-02,14.91,15.46,14.4,14.53,413df,US -2023-04-03,14.02,14.06,12.81,13.46,413df,US -2023-04-04,13.82,13.91,13.2,13.24,413df,US -2023-04-05,13.57,13.66,12.69,13.23,413df,US -2023-04-09,13.26,13.56,12.89,13.14,413df,US -2023-04-10,13.33,13.33,12.56,12.68,413df,US -2023-04-11,12.72,14.08,12.72,13.49,413df,US -2023-04-12,13.65,14.15,12.58,12.71,413df,US -2023-04-13,12.67,13,12.13,12.2,413df,US \ No newline at end of file diff --git a/Examples/flight_options_example.py b/Examples/query/flight_options_example.py similarity index 100% rename from Examples/flight_options_example.py rename to Examples/query/flight_options_example.py diff --git a/Examples/handle_query_error.py b/Examples/query/handle_query_error.py similarity index 100% rename from Examples/handle_query_error.py rename to Examples/query/handle_query_error.py diff --git a/Examples/query_async.py b/Examples/query/query_async.py similarity index 100% rename from Examples/query_async.py rename to Examples/query/query_async.py diff --git a/Examples/query_type.py b/Examples/query/query_modes.py similarity index 100% rename from Examples/query_type.py rename to Examples/query/query_modes.py diff --git a/Examples/query_with_middleware.py b/Examples/query/query_with_middleware.py similarity index 100% rename from Examples/query_with_middleware.py rename to Examples/query/query_with_middleware.py diff --git a/Examples/batching_example.py b/Examples/write/batching_example.py similarity index 100% rename from Examples/batching_example.py rename to Examples/write/batching_example.py diff --git a/Examples/handle_http_error.py b/Examples/write/handle_http_error.py similarity index 100% rename from Examples/handle_http_error.py rename to Examples/write/handle_http_error.py diff --git a/Examples/pandas_write.py b/Examples/write/pandas_write.py similarity index 100% rename from Examples/pandas_write.py rename to Examples/write/pandas_write.py From 6cca7af53a3b7ddcbd98ed98bfdb1c86fd0c05ae Mon Sep 17 00:00:00 2001 From: karel rehor Date: Mon, 20 Apr 2026 17:13:34 +0200 Subject: [PATCH 07/36] chore: refactor - remove _example_ from file names and enforce use of underscores. More review notes. --- Examples/README.md | 35 ++++++++++++++++--- .../core/{basic-query.py => basic_query.py} | 0 .../{basic_ssl_example.py => basic_ssl.py} | 0 .../core/{basic-write.py => basic_write.py} | 1 + ...t_options_example.py => flight_options.py} | 0 .../{batching_example.py => batching.py} | 2 +- 6 files changed, 32 insertions(+), 6 deletions(-) rename Examples/core/{basic-query.py => basic_query.py} (100%) rename Examples/core/{basic_ssl_example.py => basic_ssl.py} (100%) rename Examples/core/{basic-write.py => basic_write.py} (97%) rename Examples/query/{flight_options_example.py => flight_options.py} (100%) rename Examples/write/{batching_example.py => batching.py} (99%) diff --git a/Examples/README.md b/Examples/README.md index 7c2c32a..5316d53 100644 --- a/Examples/README.md +++ b/Examples/README.md @@ -8,10 +8,27 @@ Influxdb3 uses two transports: one for writing data and another for querying. ### Writing data -Basic examples can be found in the `write` directory. +Basic examples can be found in the `Examples/core` directory. -* `fileimport.py` shows how to import data to an Influx database directly from a number of other standard database formats. -TODO - others + * `basic_write.py` - shows the essentials of using the `Point` class and making simple writes. + * `basic_ssl.py` - shows how to handle SSL and TLS certificates. (TODO see point 7.2 below.) + * `timeouts.py` - shows how to set and leverage timeout values. + +Richer examples can be found in the `Examples/write` directory. + + * `batching.py` - shows how to make use of the _batching_ API for writing long-running data sets. + * `fileimport.py` - shows how to import data to an Influx database directly from a number of other standard database formats. + * `handle_http_error.py` - shows error handling on writes. + * `pandas_write.py` - shows how to write pandas dataframes directly to an Influx database. + * `writeoptions.py` - shows the core options API for writes. (TODO more specific? See point 7.1 below) + +### Querying data + +Basic examples can be found in the `Examples/core` directory. + + * `basic_query.py` - shows the essentials of querying and Influxdb database. + * `basic_ssl.py` - shows how to handle SSL and TLS certificates. (TODO see point 7.2 below.) + * `timeouts.py` - shows how to set and leverage timeout values. ## Refactoring Notes TODO - delete this section as examples take shape and before creating PR. @@ -49,5 +66,13 @@ TODO - delete this section as examples take shape and before creating PR. 12. `query_type.py` - rename to `query_modes.py`. Move to `./query` __DONE__ 13. `query_with_middleware.py` - to `./query` __DONE__ 14. `timeouts.py` - to `./core` __DONE__ -6. Leverage `config.py` in all examples -7. Verify all refactored examples are working +6. Standardization + 1. Some examples show leveraging internal **kwargs like `data_frame_measurement_name` or `data_frame_tag_columns` + - Makes sense to expose these in _advanced_ examples. (e.g. pandas examples... ) + - Perhaps though should encourage the use of a simpler standard API that hides them + 2. Leverage `config.py` in all examples + 3. Prefer using Influxdb3 Core by default. But also document possibility of using other products. +7. Enhancements + 1. `writeoptions.py` - does not show much in the way of setting options. + 2. `basic_ssl.py` - review. Seems to only show handling SSL handshake failures. +8. Verify all refactored examples are working diff --git a/Examples/core/basic-query.py b/Examples/core/basic_query.py similarity index 100% rename from Examples/core/basic-query.py rename to Examples/core/basic_query.py diff --git a/Examples/core/basic_ssl_example.py b/Examples/core/basic_ssl.py similarity index 100% rename from Examples/core/basic_ssl_example.py rename to Examples/core/basic_ssl.py diff --git a/Examples/core/basic-write.py b/Examples/core/basic_write.py similarity index 97% rename from Examples/core/basic-write.py rename to Examples/core/basic_write.py index 475dac7..b9f304c 100644 --- a/Examples/core/basic-write.py +++ b/Examples/core/basic_write.py @@ -21,6 +21,7 @@ try: client.write(data) + print("First point written to InfluxDB!") except Exception as e: print(f"Error writing point: {e}") diff --git a/Examples/query/flight_options_example.py b/Examples/query/flight_options.py similarity index 100% rename from Examples/query/flight_options_example.py rename to Examples/query/flight_options.py diff --git a/Examples/write/batching_example.py b/Examples/write/batching.py similarity index 99% rename from Examples/write/batching_example.py rename to Examples/write/batching.py index 87f907e..7806886 100644 --- a/Examples/write/batching_example.py +++ b/Examples/write/batching.py @@ -7,7 +7,7 @@ import influxdb_client_3 as InfluxDBClient3 from influxdb_client_3 import write_client_options, WritePrecision, WriteOptions, InfluxDBError -from config import Config +from Examples.config import Config class BatchingCallback(object): From 2f66b9baca3d35ac946ca7c4110714bd9f7efb84 Mon Sep 17 00:00:00 2001 From: karel rehor Date: Tue, 21 Apr 2026 17:22:49 +0200 Subject: [PATCH 08/36] chore: change community examples to advanced and refactor custom_url.py as downsample.py --- Examples/README.md | 32 ++++ .../database_transfer.py | 0 Examples/advanced/downsample.py | 151 ++++++++++++++++++ Examples/community/custom_url.py | 103 ------------ Examples/query/query_async.py | 2 +- Examples/query/query_with_middleware.py | 2 +- 6 files changed, 185 insertions(+), 105 deletions(-) rename Examples/{community => advanced}/database_transfer.py (100%) create mode 100644 Examples/advanced/downsample.py delete mode 100644 Examples/community/custom_url.py diff --git a/Examples/README.md b/Examples/README.md index 5316d53..557fc63 100644 --- a/Examples/README.md +++ b/Examples/README.md @@ -18,6 +18,7 @@ Richer examples can be found in the `Examples/write` directory. * `batching.py` - shows how to make use of the _batching_ API for writing long-running data sets. * `fileimport.py` - shows how to import data to an Influx database directly from a number of other standard database formats. + * To refresh the source data use `Examples/write/source_data/updater.py` * `handle_http_error.py` - shows error handling on writes. * `pandas_write.py` - shows how to write pandas dataframes directly to an Influx database. * `writeoptions.py` - shows the core options API for writes. (TODO more specific? See point 7.1 below) @@ -30,6 +31,16 @@ Basic examples can be found in the `Examples/core` directory. * `basic_ssl.py` - shows how to handle SSL and TLS certificates. (TODO see point 7.2 below.) * `timeouts.py` - shows how to set and leverage timeout values. +Richer examples can be found in the `Examples/query` directory. + + * `flight_options.py` - shows how to set options on the query transport. + * `handle_query_error.py` - shows basic error handling. + * `query_async.py` - shows basic usage of the asynchronous query API. + * `query_modes.py` - when making a query, different modes return data in different types of structures. This example shows which modes return which structured formats. + * `query_with_middleware.py` - when making a query it is possible to insert special headers into the HTTP layer using middleware. This example shows how to do this. + +### Advanced examples + ## Refactoring Notes TODO - delete this section as examples take shape and before creating PR. @@ -49,6 +60,24 @@ TODO - delete this section as examples take shape and before creating PR. 2. Keep `file-import` as single file, with source data updatable - see `updater.py` __DONE__ 3. Keep one simple example of jupiter notebook. __DONE__ 4. Decide what to do with `./community` examples. + * `custom_url.py` + * Doesn't seem to do what is on the tin. + * Seems more like a _down sampling_ example + * Doesn't query Influxdb directly but down samples from remote CSV + * TODO + * rename to _down sampling_ or similar + * use initial query + * move to `./advanced` + * remove dependency on `githubusercontent` + * `database_transfer.py` + * Simply copies data from one bucket to another + * Useful base example + * Current illustrative only - doesn't look functional without some undocumented setup + * To work requires `dbfrom` and measurement `airSensors` to exist. + * Note - are camel caps not sometimes problematic in Influx? (seem to recall encountering problem with them in the past) + * TODO + * move to `./advanced` + * make functional 5. Root examples 1. `basic_ssl_examle.py` to `./core` __DONE__ 2. `batching_example.py` to `./write` __DONE__ @@ -70,9 +99,12 @@ TODO - delete this section as examples take shape and before creating PR. 1. Some examples show leveraging internal **kwargs like `data_frame_measurement_name` or `data_frame_tag_columns` - Makes sense to expose these in _advanced_ examples. (e.g. pandas examples... ) - Perhaps though should encourage the use of a simpler standard API that hides them + 2. Remove dependencies on remote `../githubusercontent/../*.csv` 2. Leverage `config.py` in all examples 3. Prefer using Influxdb3 Core by default. But also document possibility of using other products. 7. Enhancements 1. `writeoptions.py` - does not show much in the way of setting options. 2. `basic_ssl.py` - review. Seems to only show handling SSL handshake failures. 8. Verify all refactored examples are working + *. __NOTE__ - If making an _illustrative_ example functional _out-of-the-box_ leads to too much distractive information being added, leave the example as _illustrative only_ and add a comment that it is for purposes of illustration. However, make sure the illustrative example is still working in a concrete implementation. (e.g. `query_with_middleware.py`) + *. TODO In comments, mark examples as either _illustrative_ or _functional_ diff --git a/Examples/community/database_transfer.py b/Examples/advanced/database_transfer.py similarity index 100% rename from Examples/community/database_transfer.py rename to Examples/advanced/database_transfer.py diff --git a/Examples/advanced/downsample.py b/Examples/advanced/downsample.py new file mode 100644 index 0000000..65e9c17 --- /dev/null +++ b/Examples/advanced/downsample.py @@ -0,0 +1,151 @@ +import datetime +import random + +import pandas as pd + +from influxdb_client_3 import InfluxDBClient3, InfluxDBError, WriteOptions, write_client_options + +from Examples.config import Config + +config = Config() + + +class BatchingCallback(object): + + def success(self, conf, data: str): + print(f"Written batch: {conf}, data: {data}") + + def error(self, conf, data: str, exception: InfluxDBError): + print(f"Cannot write batch: {conf}, data: {data} due: {exception}") + + def retry(self, conf, data: str, exception: InfluxDBError): + print(f"Retryable error occurs for batch: {conf}, data: {data} retry: {exception}") + + +callback = BatchingCallback() + +write_options = WriteOptions(batch_size=100, + flush_interval=10_000, + jitter_interval=2_000, + retry_interval=5_000, + max_retries=5, + max_retry_delay=30_000, + exponential_base=2) + +wco = write_client_options(success_callback=callback.success, + error_callback=callback.error, + retry_callback=callback.retry, + write_options=write_options + ) + +now = pd.Timestamp.now(tz='UTC').floor('ms') + +current = now - datetime.timedelta(days=1) + +print(f"DEBUG now: {now} type is {type(now)}") +print(f"DEBUG current: {current} type is {type(current)}") + +# Lists of possible trainers +trainers = ["ash", "brock", "misty", "gary", "jessie", "james"] + +# Read the CSV into a DataFrame +pokemon_df = pd.read_csv( + "../write/source_data/pokemon.csv" +) + +# Creating an empty list to store the original data +data = [] + +# Dictionary to keep track of the number of times each trainer has caught each Pokémon +trainer_pokemon_counts = {} + +# Number of entries we want to create +num_entries = 1000 + +# use a first client to write the prep data +with InfluxDBClient3( + token=config.token, + host=config.host, + database=config.database, + enable_gzip=True, + write_client_options=wco) as prep_client: + + # Generating random data + for i in range(num_entries): + trainer = random.choice(trainers) + + # Randomly select a row from pokemon_df + random_pokemon = pokemon_df.sample().iloc[0] + caught = random_pokemon['Name'] + + # Count the number of times this trainer has caught this Pokémon + if (trainer, caught) in trainer_pokemon_counts: + trainer_pokemon_counts[(trainer, caught)] += 1 + else: + trainer_pokemon_counts[(trainer, caught)] = 1 + + # Get the number for this combination of trainer and Pokémon + num = trainer_pokemon_counts[(trainer, caught)] + + entry = { + "trainer": trainer, + "id": f"{0000 + random_pokemon['#']:04d}", + "num": num, + "caught": caught, + "level": random.randint(5, 20), + "attack": random_pokemon['Attack'], + "defense": random_pokemon['Defense'], + "hp": random_pokemon['HP'], + "speed": random_pokemon['Speed'], + "type1": random_pokemon['Type 1'], + "type2": random_pokemon['Type 2'], + "legendary": random_pokemon['Legendary'], + "timestamp": current + } + data.append(entry) + current = current + datetime.timedelta(seconds=int((24 * 60 * 60) / num_entries)) + + # Convert the list of dictionaries to a DataFrame + caught_pokemon_df = pd.DataFrame(data).set_index('timestamp') + + # Print the DataFrame + print(caught_pokemon_df) + + # Write the data directly from the DataFrame + # taking care to specify the measurement name and tags + try: + prep_client.write(caught_pokemon_df, data_frame_measurement_name='monsters_caught', + data_frame_tag_columns=['trainer', 'id', 'type1', 'type2', "legendary", "caught"]) + except Exception as e: + print(f"Error writing point: {e}") + +# Now query just written data and downsample +with InfluxDBClient3( + token=config.token, + host=config.host, + database=config.database, enable_gzip=True, write_client_options=wco) as ds_client: + + # downsample data to average number of catches per quarter-hour + sql = ("SELECT date_bin('15 minutes', \"time\") as window_start, \n" + "AVG(\"num\") as avg\n" + "FROM monsters_caught\n" + "WHERE \"time\" >= now() - interval '1 day'\n" + "GROUP BY window_start\n" + "ORDER BY window_start ASC" + ) + + # Query directly to a pandas DataFrame + interim_df = ds_client.query(sql, language="sql", mode="pandas") + + # Modify the DataFrame to make the columns Influx friendly + interim_df.rename(columns={'window_start': 'timestamp'}, inplace=True) + interim_df["context"] = pd.Series('demo', index=interim_df.index) + + interim_df.set_index('timestamp', inplace=True) + + # Write the downsampled DataFrame to a new measurement in the database + try: + ds_client.write(interim_df, data_frame_measurement_name='caught_avg', + data_frame_tag_columns=['context']) + except Exception as e: + print(f"Error writing down sampled point: {e}") diff --git a/Examples/community/custom_url.py b/Examples/community/custom_url.py deleted file mode 100644 index e356334..0000000 --- a/Examples/community/custom_url.py +++ /dev/null @@ -1,103 +0,0 @@ -import random - -import pandas as pd - -from influxdb_client_3 import InfluxDBClient3, InfluxDBError, WriteOptions, write_client_options - - -class BatchingCallback(object): - - def success(self, conf, data: str): - print(f"Written batch: {conf}, data: {data}") - - def error(self, conf, data: str, exception: InfluxDBError): - print(f"Cannot write batch: {conf}, data: {data} due: {exception}") - - def retry(self, conf, data: str, exception: InfluxDBError): - print(f"Retryable error occurs for batch: {conf}, data: {data} retry: {exception}") - - -callback = BatchingCallback() - -write_options = WriteOptions(batch_size=100, - flush_interval=10_000, - jitter_interval=2_000, - retry_interval=5_000, - max_retries=5, - max_retry_delay=30_000, - exponential_base=2) - -wco = write_client_options(success_callback=callback.success, - error_callback=callback.error, - retry_callback=callback.retry, - write_options=write_options - ) - -client = InfluxDBClient3( - token="", - host="https://eu-central-1-1.aws.cloud2.influxdata.com:442", - database="pokemon-codex", enable_gzip=True, write_client_options=wco, write_port_overwrite=443, - query_port_overwrite=443) - -now = pd.Timestamp.now(tz='UTC').floor('ms') - -# Lists of possible trainers -trainers = ["ash", "brock", "misty", "gary", "jessie", "james"] - -# Read the CSV into a DataFrame -pokemon_df = pd.read_csv( - "https://gist.githubusercontent.com/ritchie46/cac6b337ea52281aa23c049250a4ff03/raw/89a957ff3919d90e6ef2d34235e6bf22304f3366/pokemon.csv") # noqa: E501 - -# Creating an empty list to store the data -data = [] - -# Dictionary to keep track of the number of times each trainer has caught each Pokémon -trainer_pokemon_counts = {} - -# Number of entries we want to create -num_entries = 1000 - -# Generating random data -for i in range(num_entries): - trainer = random.choice(trainers) - - # Randomly select a row from pokemon_df - random_pokemon = pokemon_df.sample().iloc[0] - caught = random_pokemon['Name'] - - # Count the number of times this trainer has caught this Pokémon - if (trainer, caught) in trainer_pokemon_counts: - trainer_pokemon_counts[(trainer, caught)] += 1 - else: - trainer_pokemon_counts[(trainer, caught)] = 1 - - # Get the number for this combination of trainer and Pokémon - num = trainer_pokemon_counts[(trainer, caught)] - - entry = { - "trainer": trainer, - "id": f"{0000 + random_pokemon['#']:04d}", - "num": str(num), - "caught": caught, - "level": random.randint(5, 20), - "attack": random_pokemon['Attack'], - "defense": random_pokemon['Defense'], - "hp": random_pokemon['HP'], - "speed": random_pokemon['Speed'], - "type1": random_pokemon['Type 1'], - "type2": random_pokemon['Type 2'], - "timestamp": now - } - data.append(entry) - -# Convert the list of dictionaries to a DataFrame -caught_pokemon_df = pd.DataFrame(data).set_index('timestamp') - -# Print the DataFrame -print(caught_pokemon_df) - -try: - client.write(caught_pokemon_df, data_frame_measurement_name='caught', - data_frame_tag_columns=['trainer', 'id', 'num']) -except Exception as e: - print(f"Error writing point: {e}") diff --git a/Examples/query/query_async.py b/Examples/query/query_async.py index 77bff4b..99d7291 100644 --- a/Examples/query/query_async.py +++ b/Examples/query/query_async.py @@ -6,7 +6,7 @@ from influxdb_client_3 import InfluxDBClient3 -from config import Config +from Examples.config import Config async def fibio(iterations, grit=0.5): diff --git a/Examples/query/query_with_middleware.py b/Examples/query/query_with_middleware.py index e9f3d7f..ec64a75 100644 --- a/Examples/query/query_with_middleware.py +++ b/Examples/query/query_with_middleware.py @@ -1,6 +1,6 @@ from pyarrow import flight -from config import Config +from Examples.config import Config from influxdb_client_3 import InfluxDBClient3, flight_client_options From c24ffd074b26f4e488a5674cc7004b190b44bb0a Mon Sep 17 00:00:00 2001 From: karel rehor Date: Tue, 21 Apr 2026 17:35:04 +0200 Subject: [PATCH 09/36] docs: update status in README.md --- Examples/README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Examples/README.md b/Examples/README.md index 557fc63..12dd46d 100644 --- a/Examples/README.md +++ b/Examples/README.md @@ -60,15 +60,15 @@ TODO - delete this section as examples take shape and before creating PR. 2. Keep `file-import` as single file, with source data updatable - see `updater.py` __DONE__ 3. Keep one simple example of jupiter notebook. __DONE__ 4. Decide what to do with `./community` examples. - * `custom_url.py` + * `custom_url.py` __DONE__ * Doesn't seem to do what is on the tin. * Seems more like a _down sampling_ example * Doesn't query Influxdb directly but down samples from remote CSV - * TODO - * rename to _down sampling_ or similar - * use initial query - * move to `./advanced` - * remove dependency on `githubusercontent` + * TODO __DONE__ + * rename to _down sampling_ or similar __DONE__ + * use initial query __DONE__ + * move to `./advanced` __DONE__ + * remove dependency on `githubusercontent` __DONE__ * `database_transfer.py` * Simply copies data from one bucket to another * Useful base example @@ -76,7 +76,7 @@ TODO - delete this section as examples take shape and before creating PR. * To work requires `dbfrom` and measurement `airSensors` to exist. * Note - are camel caps not sometimes problematic in Influx? (seem to recall encountering problem with them in the past) * TODO - * move to `./advanced` + * move to `./advanced` __DONE__ * make functional 5. Root examples 1. `basic_ssl_examle.py` to `./core` __DONE__ From 2c81bf0f555bd7539e12e3cb696ed45d6bfdaacc Mon Sep 17 00:00:00 2001 From: karel rehor Date: Wed, 22 Apr 2026 14:31:26 +0200 Subject: [PATCH 10/36] chore: refactor/update Examples/write/writeoptions.py --- Examples/README.md | 10 +++-- Examples/write/writeoptions.py | 68 ++++++++++++++++++++++++++++++---- 2 files changed, 67 insertions(+), 11 deletions(-) diff --git a/Examples/README.md b/Examples/README.md index 12dd46d..d81ace1 100644 --- a/Examples/README.md +++ b/Examples/README.md @@ -4,7 +4,11 @@ First time users will likely want to study the examples in the `./core` director ### Underlying principles -Influxdb3 uses two transports: one for writing data and another for querying. +Influxdb3 uses two transports: one for writing data and another for querying. For writes a standard HTTP REST style client is used. Queries on the other hand make use of an HTTP/2.0 and gRPC compliant client. + +Most of the examples found here are functional and should be runnable against an Influxdb3 database, whether in the cloud or locally using for example Influxdb3 Core. They have been revised and tested against the Influxdb3 Core product. + +Some more advanced examples contain only illustrative code that can be reused in your license compliant applications. Whether an example is intended to be simply _illustrative_ or _functional_ is noted in comments at the start of each example file. ### Writing data @@ -21,7 +25,7 @@ Richer examples can be found in the `Examples/write` directory. * To refresh the source data use `Examples/write/source_data/updater.py` * `handle_http_error.py` - shows error handling on writes. * `pandas_write.py` - shows how to write pandas dataframes directly to an Influx database. - * `writeoptions.py` - shows the core options API for writes. (TODO more specific? See point 7.1 below) + * `writeoptions.py` - shows the core options API for writes. ### Querying data @@ -103,7 +107,7 @@ TODO - delete this section as examples take shape and before creating PR. 2. Leverage `config.py` in all examples 3. Prefer using Influxdb3 Core by default. But also document possibility of using other products. 7. Enhancements - 1. `writeoptions.py` - does not show much in the way of setting options. + 1. `writeoptions.py` - does not show much in the way of setting options. (Update and revision - __DONE__) 2. `basic_ssl.py` - review. Seems to only show handling SSL handshake failures. 8. Verify all refactored examples are working *. __NOTE__ - If making an _illustrative_ example functional _out-of-the-box_ leads to too much distractive information being added, leave the example as _illustrative only_ and add a comment that it is for purposes of illustration. However, make sure the illustrative example is still working in a concrete implementation. (e.g. `query_with_middleware.py`) diff --git a/Examples/write/writeoptions.py b/Examples/write/writeoptions.py index c895fd1..1424c1d 100644 --- a/Examples/write/writeoptions.py +++ b/Examples/write/writeoptions.py @@ -1,9 +1,61 @@ +""" +`writeoptions.py` is a functional example, except for certain illustrative callbacks, +that shows the basic principles of setting up configuration properties for the standard +HTTP write client. +""" import datetime +import logging from Examples.config import Config -from influxdb_client_3 import InfluxDBClient3, Point, SYNCHRONOUS, write_client_options +from influxdb_client_3 import (exceptions, InfluxDBClient3, Point, + WriteOptions, WritePrecision, WriteType, write_client_options) -wco = write_client_options(write_options=SYNCHRONOUS) +logger = logging.getLogger("writeoptions") + + +# An illustrative callback - see below +def error_callback(conf, data: str, exception: exceptions.InfluxDBError): + now = datetime.datetime.now() + logger.warning(f"[{now}] an error occurred on latest write: {exception}") + logger.warning(f" conf: {conf}") + logger.warning(f" data: {data}") + + +# An illustrative callback - see below +def success_callback(conf, data: str): + now = datetime.datetime.now() + logger.info(f"[{now}] data written: {len(bytes(data, 'utf-8'))} bytes") + logger.debug(f" conf: {conf}") + + +wo = WriteOptions( + write_type=WriteType.synchronous, # Type of write api to use + no_sync=False, # Whether to wait for synchronizing writes with server acknowledgements + timeout=30_000, # Time in milliseconds to wait for a post write response + write_precision=WritePrecision.MS, # Timestamp precision used when writing data points +) +""" +The WriteOptions class encapsulates basic configuration properties. + +Applicable properties will depend upon the value of the `write_type` property. This can be... + * WriteType.asynchronous + * WriteType.synchronous (Default) + * WriteType.batching - see the example `write/batching.py` for more details. +""" + +wco = write_client_options(write_options=wo, # The core WriteOptions object to use + success_callback=success_callback, # N.B. currently only used in with batching type + error_callback=error_callback, # N.B. currently only used with batching type + ) +""" +The dictionary created by the call to `write_client_options()` can add other write client properties +such as callback functions. Note that the `write_options` property is not always required, +in which case a default WriteOptions object is used internally. + +The InfluxDBClient3 constructor will leverage this dictionary when configuring the standard HTTP based write client. +""" + +measurement = 'wo_caught' def main(config: Config): @@ -11,13 +63,13 @@ def main(config: Config): with InfluxDBClient3( token=config.token, host=config.host, - database="pokemon-codex", - write_client_options=wco, + database=config.database, + write_client_options=wco, # write client options get passed to the client instance here. debug=True) as client: now = datetime.datetime.now(datetime.timezone.utc) - data = Point("caught").tag("trainer", "ash").tag("id", "0006").tag("num", "1") \ + data = Point(measurement).tag("trainer", "ash").tag("id", "0006").tag("num", "1") \ .field("caught", "charizard") \ .field("level", 10).field("attack", 30) \ .field("defense", 40).field("hp", 200) \ @@ -30,7 +82,7 @@ def main(config: Config): except Exception as e: print(f"Error writing point: {e}") - data = [Point("caught") # point 1 + data = [Point(measurement) # point 1 .tag("trainer", "ash") .tag("id", "0006") .tag("num", "1") @@ -44,7 +96,7 @@ def main(config: Config): .field("type2", "flying") .time(now), - Point("caught") # point 2 + Point(measurement) # point 2 .tag("trainer", "ash") .tag("id", "0007") .tag("num", "2") @@ -58,7 +110,7 @@ def main(config: Config): .field("type2", "poison") .time(now), - Point("caught") # point 3 + Point(measurement) # point 3 .tag("trainer", "ash") .tag("id", "0008") .tag("num", "3") From baa226fc35eec90b8943941fc7d3a99db779c995 Mon Sep 17 00:00:00 2001 From: karel rehor Date: Wed, 22 Apr 2026 15:32:31 +0200 Subject: [PATCH 11/36] docs: improve comments for Examples/core/basic_ssl.py --- Examples/README.md | 12 ++++++++++-- Examples/core/basic_ssl.py | 10 ++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/Examples/README.md b/Examples/README.md index d81ace1..dfd7a37 100644 --- a/Examples/README.md +++ b/Examples/README.md @@ -8,7 +8,15 @@ Influxdb3 uses two transports: one for writing data and another for querying. F Most of the examples found here are functional and should be runnable against an Influxdb3 database, whether in the cloud or locally using for example Influxdb3 Core. They have been revised and tested against the Influxdb3 Core product. -Some more advanced examples contain only illustrative code that can be reused in your license compliant applications. Whether an example is intended to be simply _illustrative_ or _functional_ is noted in comments at the start of each example file. +Some more advanced examples contain only illustrative code that can be reused in your license compliant applications. Whether an example is intended to be simply _illustrative_ or _functional_ is noted in comments at the start of each example file. + +__Configuration__ + +Functional examples make use of the base `Config` class in `config.py`. User values can be set either directly in this file, or through the following environment variables. + +* `INFLUXDB_HOST` - host URL to connect to an Influxdb3 database. +* `INFLUXDB_DATABASE` - default database to be used with the examples. +* `INFLUXDB_TOKEN` - a token associated with read and write permissions to the default database and any additional databases that might be used with the examples. ### Writing data @@ -108,7 +116,7 @@ TODO - delete this section as examples take shape and before creating PR. 3. Prefer using Influxdb3 Core by default. But also document possibility of using other products. 7. Enhancements 1. `writeoptions.py` - does not show much in the way of setting options. (Update and revision - __DONE__) - 2. `basic_ssl.py` - review. Seems to only show handling SSL handshake failures. + 2. `basic_ssl.py` - review. Seems to only show handling SSL handshake failures. (Reviewed and updated - __DONE__) 8. Verify all refactored examples are working *. __NOTE__ - If making an _illustrative_ example functional _out-of-the-box_ leads to too much distractive information being added, leave the example as _illustrative only_ and add a comment that it is for purposes of illustration. However, make sure the illustrative example is still working in a concrete implementation. (e.g. `query_with_middleware.py`) *. TODO In comments, mark examples as either _illustrative_ or _functional_ diff --git a/Examples/core/basic_ssl.py b/Examples/core/basic_ssl.py index a1f764d..e5a1df9 100644 --- a/Examples/core/basic_ssl.py +++ b/Examples/core/basic_ssl.py @@ -1,3 +1,13 @@ +""" +basic_ssl.py is a functional example that shows how to work with non-standard SSL configurations. + +Please NOTE, as this is an SSL example it needs to be run over an HTTPS transport. Running with a local +Influxdb3 Core deployment over HTTP is of little use. When running this example, please either... + + * deploy Influxdb3 Core / Enterprise locally over HTTPS. + (see https://docs.influxdata.com/influxdb3/core/reference/config-options/#security) + * use an Influxdb3 Cloud account. +""" import os import time From fa81571cacb4a1fd486b233913ce99b0887c2540 Mon Sep 17 00:00:00 2001 From: karel rehor Date: Wed, 22 Apr 2026 17:50:33 +0200 Subject: [PATCH 12/36] chore: standardize core examples --- Examples/README.md | 4 ++++ Examples/core/basic_query.py | 19 +++++++++++++++++-- Examples/core/basic_ssl.py | 1 + Examples/core/basic_write.py | 18 ++++++++++++++---- Examples/core/timeouts.py | 29 +++++++++++++++-------------- Examples/query/__init__.py | 0 Examples/write/__init__.py | 0 7 files changed, 51 insertions(+), 20 deletions(-) create mode 100644 Examples/query/__init__.py create mode 100644 Examples/write/__init__.py diff --git a/Examples/README.md b/Examples/README.md index dfd7a37..9c3f56f 100644 --- a/Examples/README.md +++ b/Examples/README.md @@ -53,6 +53,9 @@ Richer examples can be found in the `Examples/query` directory. ### Advanced examples + * `database_transfer.py` - illustrates writing datapoints from one database to another. + * `downsample.py` - shows how to read data from one measurement, reduce it to a smaller data set, and then write the new data set as a new measurement. + ## Refactoring Notes TODO - delete this section as examples take shape and before creating PR. @@ -114,6 +117,7 @@ TODO - delete this section as examples take shape and before creating PR. 2. Remove dependencies on remote `../githubusercontent/../*.csv` 2. Leverage `config.py` in all examples 3. Prefer using Influxdb3 Core by default. But also document possibility of using other products. + 4. Add shebangs to functional examples 7. Enhancements 1. `writeoptions.py` - does not show much in the way of setting options. (Update and revision - __DONE__) 2. `basic_ssl.py` - review. Seems to only show handling SSL handshake failures. (Reviewed and updated - __DONE__) diff --git a/Examples/core/basic_query.py b/Examples/core/basic_query.py index fa72366..c20368c 100644 --- a/Examples/core/basic_query.py +++ b/Examples/core/basic_query.py @@ -1,3 +1,12 @@ +#!/usr/bin/env python3 +""" +basic_query.py - shows the simplest ways in which to query data from an influxdb3 database. + +As a functional example it should be run after running basic_write.py, which prepares the measurements +queried here. + +For more information on working with query modes see the `query/query_modes.py` example. +""" from Examples.config import Config from influxdb_client_3 import InfluxDBClient3 @@ -8,10 +17,16 @@ host=config.host, database=config.database,) -sql = '''SELECT * FROM caught WHERE trainer = 'ash' AND time >= now() - interval '1 hour' LIMIT 5''' +measurement = "basic_caught" + +print("Quering with sql to Arrow Table") +sql = f'''SELECT * FROM {measurement} WHERE trainer = 'ash' AND time >= now() - interval '1 hour' LIMIT 5''' table = client.query(query=sql, language='sql', mode='all') print(table) -influxql = '''SELECT * FROM caught WHERE trainer = 'ash' AND time > now() - 1h LIMIT 5''' +print("Querying with influxql to pandas DataFrame") +influxql = f'''SELECT * FROM {measurement} WHERE trainer = 'ash' AND time > now() - 1h LIMIT 5''' table = client.query(query=influxql, language='influxql', mode='pandas') print(table) + +client.close() diff --git a/Examples/core/basic_ssl.py b/Examples/core/basic_ssl.py index e5a1df9..a565a4c 100644 --- a/Examples/core/basic_ssl.py +++ b/Examples/core/basic_ssl.py @@ -1,3 +1,4 @@ +#!/usr/bin/env python3 """ basic_ssl.py is a functional example that shows how to work with non-standard SSL configurations. diff --git a/Examples/core/basic_write.py b/Examples/core/basic_write.py index b9f304c..b4a0097 100644 --- a/Examples/core/basic_write.py +++ b/Examples/core/basic_write.py @@ -1,3 +1,9 @@ +#!/usr/bin/env python3 +""" +basic_write.py - shows the simplest ways in which to write data to an influxdb3 database using Point objects. + +After successfully running this example try basic_query.py to verify the results. +""" import datetime from Examples.config import Config @@ -11,7 +17,9 @@ now = datetime.datetime.now(datetime.timezone.utc) -data = Point("caught").tag("trainer", "ash").tag("id", "0006").tag("num", "1") \ +measurement = "basic_caught" + +data = Point(measurement).tag("trainer", "ash").tag("id", "0006").tag("num", "1") \ .field("caught", "charizard") \ .field("level", 10).field("attack", 30) \ .field("defense", 40).field("hp", 200) \ @@ -25,7 +33,7 @@ except Exception as e: print(f"Error writing point: {e}") -data = [Point("caught") # first point +data = [Point(measurement) # first point .tag("trainer", "ash") .tag("id", "0006") .tag("num", "1") @@ -39,7 +47,7 @@ .field("type2", "flying") .time(now), - Point("caught") # second point + Point(measurement) # second point .tag("trainer", "ash") .tag("id", "0007") .tag("num", "2") @@ -53,7 +61,7 @@ .field("type2", "poison") .time(now), - Point("caught") # third point + Point(measurement) # third point .tag("trainer", "ash") .tag("id", "0008") .tag("num", "3") @@ -73,3 +81,5 @@ print(f"Write success: {len(data)} points!") except Exception as e: print(f"Error writing point: {e}") +finally: + client.close() diff --git a/Examples/core/timeouts.py b/Examples/core/timeouts.py index 70b8ff1..a505bc5 100644 --- a/Examples/core/timeouts.py +++ b/Examples/core/timeouts.py @@ -1,38 +1,39 @@ #!/usr/bin/env python3 -import sys -import time - -from influxdb_client_3 import InfluxDBClient3, write_client_options - """ -This example shows how to set query and write timeouts. +timeouts.py - shows how to set query and write timeouts. + They can be set directly using arguments (write_timeout, query_timeout) in the client constructor. They can also be overridden in write and query calls. To trigger timeout and deadline expired exceptions, reset the timeout values in the examples or supply new values as command line parameters. -Be sure to update the host, token a database values below, before running this example. +Be sure to update INFLUXDB_HOST, INFLUXDB_DATABASE and INFLUXDB_TOKEN environment variables, +before running this example. """ +import sys +import time + +from influxdb_client_3 import InfluxDBClient3, write_client_options +from Examples.config import Config DEFAULT_WRITE_TIMEOUT = 30_000 # in milliseconds DEFAULT_QUERY_TIMEOUT = 120_000 # in milliseconds -DEFAULT_HOST = 'http://localhost:8181' -DEFAULT_TOKEN = 'my-token' -DEFAULT_DATABASE = 'test-data' +config = Config() + +print(f"DEBUG config: {config}") def handle_write_error_cb(rd, rt, rx): print(f"Got a write error: {rd}, {rt}, {rx}") - def main(w_to: int, q_to: int) -> None: print(f"main {w_to}, {q_to}") lp_data = "timeout_example,location=terra fVal=3.14,iVal=42i" with InfluxDBClient3( - host=DEFAULT_HOST, - token=DEFAULT_TOKEN, - database=DEFAULT_DATABASE, + host=config.host, + token=config.token, + database=config.database, write_timeout=w_to, query_timeout=q_to, write_client_options=write_client_options( diff --git a/Examples/query/__init__.py b/Examples/query/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Examples/write/__init__.py b/Examples/write/__init__.py new file mode 100644 index 0000000..e69de29 From b23fd822c610f6b58bb03b13c5988c3ce978ba3d Mon Sep 17 00:00:00 2001 From: karel rehor Date: Wed, 22 Apr 2026 17:55:24 +0200 Subject: [PATCH 13/36] chore: fix flake problems --- Examples/core/timeouts.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Examples/core/timeouts.py b/Examples/core/timeouts.py index a505bc5..6efee1c 100644 --- a/Examples/core/timeouts.py +++ b/Examples/core/timeouts.py @@ -24,9 +24,11 @@ print(f"DEBUG config: {config}") + def handle_write_error_cb(rd, rt, rx): print(f"Got a write error: {rd}, {rt}, {rx}") + def main(w_to: int, q_to: int) -> None: print(f"main {w_to}, {q_to}") lp_data = "timeout_example,location=terra fVal=3.14,iVal=42i" From 0b91189c7cb06728f77532f84fbcd7c6e3a0e55b Mon Sep 17 00:00:00 2001 From: karel rehor Date: Thu, 23 Apr 2026 13:23:04 +0200 Subject: [PATCH 14/36] chore: standardize write examples. --- Examples/README.md | 9 ++-- Examples/core/timeouts.py | 2 - Examples/jupyter/basic-write-query.ipynb | 4 +- Examples/query/handle_query_error.py | 5 +- Examples/write/batching.py | 9 +++- Examples/write/fileimport.py | 31 ++++++----- Examples/write/handle_http_error.py | 5 +- Examples/write/pandas_write.py | 69 ++++++++++++++++-------- Examples/write/writeoptions.py | 5 +- 9 files changed, 89 insertions(+), 50 deletions(-) diff --git a/Examples/README.md b/Examples/README.md index 9c3f56f..d7ef8c0 100644 --- a/Examples/README.md +++ b/Examples/README.md @@ -23,7 +23,7 @@ Functional examples make use of the base `Config` class in `config.py`. User va Basic examples can be found in the `Examples/core` directory. * `basic_write.py` - shows the essentials of using the `Point` class and making simple writes. - * `basic_ssl.py` - shows how to handle SSL and TLS certificates. (TODO see point 7.2 below.) + * `basic_ssl.py` - shows how to handle SSL and TLS certificates. * `timeouts.py` - shows how to set and leverage timeout values. Richer examples can be found in the `Examples/write` directory. @@ -40,12 +40,12 @@ Richer examples can be found in the `Examples/write` directory. Basic examples can be found in the `Examples/core` directory. * `basic_query.py` - shows the essentials of querying and Influxdb database. - * `basic_ssl.py` - shows how to handle SSL and TLS certificates. (TODO see point 7.2 below.) + * `basic_ssl.py` - shows how to handle SSL and TLS certificates. * `timeouts.py` - shows how to set and leverage timeout values. Richer examples can be found in the `Examples/query` directory. - * `flight_options.py` - shows how to set options on the query transport. + * `flight_options.py` - shows how to set options on the query transport. (TODO see point 7.3 below) * `handle_query_error.py` - shows basic error handling. * `query_async.py` - shows basic usage of the asynchronous query API. * `query_modes.py` - when making a query, different modes return data in different types of structures. This example shows which modes return which structured formats. @@ -118,9 +118,12 @@ TODO - delete this section as examples take shape and before creating PR. 2. Leverage `config.py` in all examples 3. Prefer using Influxdb3 Core by default. But also document possibility of using other products. 4. Add shebangs to functional examples + 5. Ensure timestamps are current and not fixed for example to 2023 7. Enhancements 1. `writeoptions.py` - does not show much in the way of setting options. (Update and revision - __DONE__) 2. `basic_ssl.py` - review. Seems to only show handling SSL handshake failures. (Reviewed and updated - __DONE__) + 3. `write_pandas.py` - has fixed dates from 2023, make dynamic with current. + 3. `flight_options.py` - review. This example is nearly three years old, and flight/query options has been greatly enhanced since then. 8. Verify all refactored examples are working *. __NOTE__ - If making an _illustrative_ example functional _out-of-the-box_ leads to too much distractive information being added, leave the example as _illustrative only_ and add a comment that it is for purposes of illustration. However, make sure the illustrative example is still working in a concrete implementation. (e.g. `query_with_middleware.py`) *. TODO In comments, mark examples as either _illustrative_ or _functional_ diff --git a/Examples/core/timeouts.py b/Examples/core/timeouts.py index 6efee1c..507631b 100644 --- a/Examples/core/timeouts.py +++ b/Examples/core/timeouts.py @@ -22,8 +22,6 @@ config = Config() -print(f"DEBUG config: {config}") - def handle_write_error_cb(rd, rt, rx): print(f"Got a write error: {rd}, {rt}, {rx}") diff --git a/Examples/jupyter/basic-write-query.ipynb b/Examples/jupyter/basic-write-query.ipynb index 1994e78..87c184a 100644 --- a/Examples/jupyter/basic-write-query.ipynb +++ b/Examples/jupyter/basic-write-query.ipynb @@ -7,7 +7,7 @@ "source": [ "## Influxdb3 Python basic usage\n", "\n", - "This jupyter notebook illustrates the basics of writing and querying data from an Influxdb3 database using the Influxdb3 python client. Code cells need to be run step by step in the order in which they are presented.\n", + "This jupyter notebook illustrates the basics of writing and querying data with an Influxdb3 database using the Influxdb3 python client. Code cells need to be run step by step in the order in which they are presented.\n", "\n", "1. Start by setting up the basic connection values that match your server or account." ] @@ -18,7 +18,7 @@ "metadata": {}, "source": [ "%env INFLUXDB_HOST=http://localhost:8181\n", - "%env INFLUXDB_TOKEN=apiv3_j0S3Nhx2DW1Lo2pyM7wHKlCUz315z4cEGOBORrPMF8GKXFq8sVfBf9AyQu1EYB4IVSu9jCjwmKHhpB-vsr_HUw\n", + "%env INFLUXDB_TOKEN=\n", "%env INFLUXDB_DATABASE=my_db\n", "from influxdb_client_3 import InfluxDBClient3, Point, WritePrecision, InfluxDBError, WriteOptions, write_client_options" ], diff --git a/Examples/query/handle_query_error.py b/Examples/query/handle_query_error.py index f0086d0..940adf6 100644 --- a/Examples/query/handle_query_error.py +++ b/Examples/query/handle_query_error.py @@ -1,8 +1,9 @@ +#!/usr/bin/env python3 """ -Demonstrates handling error when querying InfluxDB. +handle_query_error.py - Is a functional example that demonstrates handling error when querying InfluxDB. """ import logging -from config import Config +from Examples.config import Config from influxdb_client_3.exceptions import InfluxDB3ClientQueryError import influxdb_client_3 as InfluxDBClient3 diff --git a/Examples/write/batching.py b/Examples/write/batching.py index 7806886..ed38d33 100644 --- a/Examples/write/batching.py +++ b/Examples/write/batching.py @@ -1,3 +1,8 @@ +#!/usr/bin/env python3 +""" +batching.py - is a functional example that shows how to set up and use WriteType.batching, +which is the default WriteType when instantiating a WriteOptions object. +""" import datetime import random import time @@ -11,7 +16,9 @@ class BatchingCallback(object): - + """ + Prepare callbacks to be used to handle batching states. + """ def __init__(self): self.write_status_msg = None self.write_count = 0 diff --git a/Examples/write/fileimport.py b/Examples/write/fileimport.py index 52fb8a5..08f2fb4 100644 --- a/Examples/write/fileimport.py +++ b/Examples/write/fileimport.py @@ -1,3 +1,12 @@ +#!/usr/bin/env python3 +""" +fileimport.py - is a functional example that shows how to import data directly to Influxdb3 +from other common database types. + +The template databases used for this example can be found in `Examples/write/source_data`. To create +fresh databases with current timestamps please run the helper file `Examples/write/source_data/updater.py` +before running fileimport.py. +""" import logging import os @@ -48,15 +57,11 @@ def main(file_types=("csv",)) -> None: write_options=write_options ) + config = Config() """ - token: access token generated in cloud - host: ATTN could be another AWS region or even another cloud provider - database: should have retention policy 'forever' to handle older sample data timestamps - write_client_options: see above - debug: allows low-level inspection of communications and context-manager termination + Note: + debug: allows low-level inspection of communications and of context-manager termination """ - config = Config() - with InfluxDBClient3.InfluxDBClient3( token=config.token, host=config.host, @@ -64,15 +69,15 @@ def main(file_types=("csv",)) -> None: write_client_options=wco, debug=True) as client: - for type in file_types: - if type not in data_types: - logging.error(f"File type {type} not supported.") + for _ftype in file_types: + if _ftype not in data_types: + logging.error(f"File type {_ftype} not supported.") continue - logging.info(f"Writing from file of type: {type}") - source_file = f"./source_data/out_update.{type}" + logging.info(f"Writing from DB file of type: {_ftype}") + source_file = f"./source_data/out_update.{_ftype}" if not (os.path.exists(source_file) and os.path.isfile(source_file)): - logging.error(f"Source file {source_file} not found.") + logging.error(f"Source DB file {source_file} not found.") logging.error(" TIP!: Perhaps source_data/updater.py needs to be run.") continue # write data from file diff --git a/Examples/write/handle_http_error.py b/Examples/write/handle_http_error.py index fd0571f..45cdcc7 100644 --- a/Examples/write/handle_http_error.py +++ b/Examples/write/handle_http_error.py @@ -1,8 +1,9 @@ +#!/usr/bin/env python3 """ -Demonstrates handling response error headers on error. +handle_http_error.py - is a functional example that demonstrates handling response error headers on error. """ import logging -from config import Config +from Examples.config import Config import influxdb_client_3 as InfluxDBClient3 diff --git a/Examples/write/pandas_write.py b/Examples/write/pandas_write.py index ead33cc..4f1b51a 100644 --- a/Examples/write/pandas_write.py +++ b/Examples/write/pandas_write.py @@ -1,34 +1,57 @@ +#!/usr/bin/env python3 +""" +pandas_write.py - is a functional example that demonstrates how to write data to Influxdb3 +directly from a pandas DataFrame. +""" import influxdb_client_3 as InfluxDBClient3 import pandas as pd import numpy as np -client = InfluxDBClient3.InfluxDBClient3( - token="", - host="eu-central-1-1.aws.cloud2.influxdata.com", - database="") +from Examples.config import Config -# Create a dataframe -df = pd.DataFrame({'a': [1, 2, 3], 'b': [4, 5, 6]}) +def main(config: Config): + print(f"DEBUG config: {config}") -# Create a range of datetime values -dates = pd.date_range(start='2023-03-01', end='2023-03-29', freq='5min') + with InfluxDBClient3.InfluxDBClient3( + token=config.token, + host=config.host, + database=config.database) as client: -# Create a DataFrame with random data and datetime index -df = pd.DataFrame( - np.random.randn( - len(dates), - 3), - index=dates, - columns=[ - 'Column 1', - 'Column 2', - 'Column 3']) -df['tagkey'] = 'Hello World' + # Create a dataframe + df = pd.DataFrame({'a': [1, 2, 3], 'b': [4, 5, 6]}) -print(df) + # Create a range of datetime values + now = pd.Timestamp.now(tz="utc").floor(freq="min") + start = now - pd.Timedelta(days=30) -# Write the DataFrame to InfluxDB -client.write(df, data_frame_measurement_name='table', - data_frame_tag_columns=['tagkey']) + dates = pd.date_range(start=start, end=now, freq='5min') + + # Create a DataFrame with random data and datetime index + df = pd.DataFrame( + np.random.randn( + len(dates), + 3), + index=dates, + columns=[ + 'Column 1', + 'Column 2', + 'Column 3']) + df['tagkey'] = 'Hello World' + + print(df) + + # Write the DataFrame to InfluxDB + # Please note the keyword values used to declare + # the measurement name and which DataFrame columns + # will be written as tags. + try: + client.write(df, data_frame_measurement_name='pd_table', + data_frame_tag_columns=['tagkey']) + print(f"Write Success!") + except Exception as e: + print(f"Write failure: {e}") + +if __name__ == '__main__': + main(Config()) diff --git a/Examples/write/writeoptions.py b/Examples/write/writeoptions.py index 1424c1d..b8759d1 100644 --- a/Examples/write/writeoptions.py +++ b/Examples/write/writeoptions.py @@ -1,3 +1,4 @@ +#!/usr/bin/env python3 """ `writeoptions.py` is a functional example, except for certain illustrative callbacks, that shows the basic principles of setting up configuration properties for the standard @@ -39,8 +40,8 @@ def success_callback(conf, data: str): Applicable properties will depend upon the value of the `write_type` property. This can be... * WriteType.asynchronous - * WriteType.synchronous (Default) - * WriteType.batching - see the example `write/batching.py` for more details. + * WriteType.synchronous + * WriteType.batching (Constructor Default) - see the example `write/batching.py` for more details. """ wco = write_client_options(write_options=wo, # The core WriteOptions object to use From 1d689651ce7486327eafaaee023b9b20c6b4ab28 Mon Sep 17 00:00:00 2001 From: karel rehor Date: Thu, 23 Apr 2026 13:26:13 +0200 Subject: [PATCH 15/36] chore: tidy flake --- Examples/write/pandas_write.py | 11 ++++++----- Examples/write/writeoptions.py | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Examples/write/pandas_write.py b/Examples/write/pandas_write.py index 4f1b51a..4d03c45 100644 --- a/Examples/write/pandas_write.py +++ b/Examples/write/pandas_write.py @@ -15,9 +15,9 @@ def main(config: Config): print(f"DEBUG config: {config}") with InfluxDBClient3.InfluxDBClient3( - token=config.token, - host=config.host, - database=config.database) as client: + token=config.token, + host=config.host, + database=config.database) as client: # Create a dataframe df = pd.DataFrame({'a': [1, 2, 3], 'b': [4, 5, 6]}) @@ -48,10 +48,11 @@ def main(config: Config): # will be written as tags. try: client.write(df, data_frame_measurement_name='pd_table', - data_frame_tag_columns=['tagkey']) - print(f"Write Success!") + data_frame_tag_columns=['tagkey']) + print("Write Success!") except Exception as e: print(f"Write failure: {e}") + if __name__ == '__main__': main(Config()) diff --git a/Examples/write/writeoptions.py b/Examples/write/writeoptions.py index b8759d1..7b461ed 100644 --- a/Examples/write/writeoptions.py +++ b/Examples/write/writeoptions.py @@ -40,7 +40,7 @@ def success_callback(conf, data: str): Applicable properties will depend upon the value of the `write_type` property. This can be... * WriteType.asynchronous - * WriteType.synchronous + * WriteType.synchronous * WriteType.batching (Constructor Default) - see the example `write/batching.py` for more details. """ From 2ae6ecbf38e764d3dba103b296e09152904e7404 Mon Sep 17 00:00:00 2001 From: karel rehor Date: Thu, 23 Apr 2026 15:30:27 +0200 Subject: [PATCH 16/36] chore: standardize/enhance query_async.py and query_modes.py examples --- Examples/README.md | 6 +- Examples/query/query_async.py | 5 ++ Examples/query/query_modes.py | 137 ++++++++++++++++++++++++---------- 3 files changed, 104 insertions(+), 44 deletions(-) diff --git a/Examples/README.md b/Examples/README.md index d7ef8c0..e5139f9 100644 --- a/Examples/README.md +++ b/Examples/README.md @@ -4,7 +4,7 @@ First time users will likely want to study the examples in the `./core` director ### Underlying principles -Influxdb3 uses two transports: one for writing data and another for querying. For writes a standard HTTP REST style client is used. Queries on the other hand make use of an HTTP/2.0 and gRPC compliant client. +Influxdb3 uses two transports: one for writing data and another for querying. For writes a standard HTTP REST style client is used. Queries on the other hand make use of an HTTP/2.0 and [gRPC](https://grpc.io/docs/what-is-grpc/introduction/) compliant client under [Apache Arrow Flight](https://arrow.apache.org/cookbook/py/flight.html). Most of the examples found here are functional and should be runnable against an Influxdb3 database, whether in the cloud or locally using for example Influxdb3 Core. They have been revised and tested against the Influxdb3 Core product. @@ -23,7 +23,7 @@ Functional examples make use of the base `Config` class in `config.py`. User va Basic examples can be found in the `Examples/core` directory. * `basic_write.py` - shows the essentials of using the `Point` class and making simple writes. - * `basic_ssl.py` - shows how to handle SSL and TLS certificates. + * `basic_ssl.py` - shows how to handle special SSL/TLS situations. * `timeouts.py` - shows how to set and leverage timeout values. Richer examples can be found in the `Examples/write` directory. @@ -40,7 +40,7 @@ Richer examples can be found in the `Examples/write` directory. Basic examples can be found in the `Examples/core` directory. * `basic_query.py` - shows the essentials of querying and Influxdb database. - * `basic_ssl.py` - shows how to handle SSL and TLS certificates. + * `basic_ssl.py` - shows how to handle special SSL/TLS situations. * `timeouts.py` - shows how to set and leverage timeout values. Richer examples can be found in the `Examples/query` directory. diff --git a/Examples/query/query_async.py b/Examples/query/query_async.py index 99d7291..1e199ab 100644 --- a/Examples/query/query_async.py +++ b/Examples/query/query_async.py @@ -1,3 +1,8 @@ +#!/usr/bin/env python3 +""" +query_async.py - is a functional example that shows how to run a query and process results in one coroutine +while calculating a Fibonacci series in a parallel coroutine. +""" import asyncio import random import time diff --git a/Examples/query/query_modes.py b/Examples/query/query_modes.py index 4a37b7a..0b9fb3f 100644 --- a/Examples/query/query_modes.py +++ b/Examples/query/query_modes.py @@ -1,54 +1,109 @@ -import influxdb_client_3 as InfluxDBClient3 +#!/usr/bin/env python3 +""" +query_modes.py - is a functional example that shows how to use different modes when executing queries. +""" +import pandas as pd +import numpy as np +from influxdb_client_3 import InfluxDBClient3 +from Examples.config import Config -client = InfluxDBClient3.InfluxDBClient3( - token="", - host="eu-central-1-1.aws.cloud2.influxdata.com", - database="factory") +def prep_data(client: InfluxDBClient3, measurement: str): + now = pd.Timestamp.now(tz="utc").floor(freq='s') + start = now - pd.Timedelta(minutes=5) + timestamps = pd.date_range(start=start, end=now, freq='10s') -# Chunk mode provides a FlightReader object that can be used to read chunks of data. -reader = client.query( - query="SELECT * FROM machine_data WHERE time > now() - 2h", - language="influxql", mode="chunk") + df = pd.DataFrame( + np.random.randn( + len(timestamps), + 3), + index=timestamps, + columns=[ + 'volts', + 'amps', + 'dBm']) + df['model'] = np.random.choice(['R2D2', 'C3PO', 'ROBBIE', 'HAL'], len(df)) -try: - while True: - batch, buff = reader.read_chunk() - print("batch:") - print(batch.to_pandas()) -except StopIteration: - print("No more chunks to read") + client.write(df, data_frame_measurement_name=measurement, + data_frame_tag_columns=['model']) + + +def query_chunk(client: InfluxDBClient3, influxql_query: str): + # Chunk mode provides a FlightReader object that can be used to read chunks of data. + reader = client.query( + query=influxql_query, + language="influxql", mode="chunk") + + try: + while True: + batch, buff = reader.read_chunk() + print("batch:") + print(batch.to_pandas()) + except StopIteration: + print("No more chunks to read") + + +def query_pandas(client: InfluxDBClient3, influxql_query: str): + # Pandas mode provides a Pandas DataFrame object. + df = client.query( + query=influxql_query, + language="influxql", mode="pandas") + print("pandas:") + print(df) -# Pandas mode provides a Pandas DataFrame object. -df = client.query( - query="SELECT * FROM machine_data WHERE time > now() - 2h", - language="influxql", mode="pandas") -print("pandas:") -print(df) +def query_all(client: InfluxDBClient3, influxql_query: str): + # All mode provides an Arrow Table object. + table = client.query( + query=influxql_query, + language="influxql", mode="all") -# All mode provides an Arrow Table object. -table = client.query( - query="SELECT * FROM machine_data WHERE time > now() - 2h", - language="influxql", mode="all") + print("table:") + print(table) -print("table:") -print(table) -# Print the schema of the table -table = client.query( - query="SELECT * FROM machine_data WHERE time > now() - 2h", - language="influxql", mode="schema") +def query_schema(client: InfluxDBClient3, influxql_query: str): + # Print the schema of the table + table = client.query( + query=influxql_query, + language="influxql", mode="schema") + + print("schema:") + print(table) + + +def query_reader(client: InfluxDBClient3, influxqul_query: str): + # Convert this reader into a regular RecordBatchReader + reader = client.query( + query=influxqul_query, + language="influxql", mode="reader") + + print("reader:") + for batch in reader: + print(batch.to_pandas()) + -print("schema:") -print(table) +def main(config: Config): + measurement = "machine_data" + influxql = f"SELECT * FROM {measurement} WHERE time > now() - 5m" + with InfluxDBClient3( + host=config.host, + database=config.database, + token=config.token + ) as client: + prep_data(client, measurement) + print("\n=== Querying Chunks ===\n") + query_chunk(client, influxql) + print("\n=== Querying Pandas ===\n") + query_pandas(client, influxql) + print("\n=== Querying All ===\n") + query_all(client, influxql) + print("\n=== Querying Schema ===\n") + query_schema(client, influxql) + print("\n=== Querying Reader ===\n") + query_reader(client, influxql) -# Convert this reader into a regular RecordBatchReader -reader = client.query( - query="SELECT * FROM machine_data WHERE time > now() - 2h", - language="influxql", mode="reader") -print("reader:") -for batch in reader: - print(batch.to_pandas()) +if __name__ == "__main__": + main(Config()) From 6f8bd6fec513793aa6d05badb037881d585b5ee9 Mon Sep 17 00:00:00 2001 From: karel rehor Date: Thu, 23 Apr 2026 17:44:36 +0200 Subject: [PATCH 17/36] chore: standardize flight_options.py and query_with_middleware.py examples --- Examples/query/flight_options.py | 34 ++++++++++++++++++------- Examples/query/query_with_middleware.py | 4 +++ 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/Examples/query/flight_options.py b/Examples/query/flight_options.py index d30470d..b3d687b 100644 --- a/Examples/query/flight_options.py +++ b/Examples/query/flight_options.py @@ -1,19 +1,35 @@ -import influxdb_client_3 as InfluxDBClient3 -from influxdb_client_3 import flight_client_options +""" +flight_options.py - is an illustrative example of how to set low level options for the Arrow Flight client +and even for its internal gRPC client. + +Note that the query transport of Influxdb3 is built on top of Arrow Flight, so `flight_client_options` in +a broad sense is synonymous with setting low level "Query API Options". + +TODO review this one more time +""" +from influxdb_client_3 import InfluxDBClient3, flight_client_options +from Examples.config import Config with open("./cert.pem", 'rb') as f: cert = f.read() print(cert) +config = Config() -client = InfluxDBClient3.InfluxDBClient3( - token="", - host="b0c7cce5-8dbc-428e-98c6-7f996fb96467.a.influxdb.io", - database="flightdemo", - flight_client_options=flight_client_options( - tls_root_certs=cert)) - +client = InfluxDBClient3( + token=config.token, + host=config.host, + database=config.database, + flight_client_options=flight_client_options( # Options passed directly to the underlying Arrow flight client + tls_root_certs=cert, # Use a non-standard root certificate + disable_server_verification=True, # N.B. unsafe - for illustration here + generic_options=[ # options to be passed to the gRPC client used by Arrow flight + ("grpc.keepalive_time_ms", 300000), + ("grpc.keepalive_timeout_ms", 20000), + ] + ) +) table = client.query( query="SELECT * FROM flight WHERE time > now() - 4h", diff --git a/Examples/query/query_with_middleware.py b/Examples/query/query_with_middleware.py index ec64a75..bb01c3d 100644 --- a/Examples/query/query_with_middleware.py +++ b/Examples/query/query_with_middleware.py @@ -1,3 +1,7 @@ +""" +query_with_middleware.py - is an illustrative example of how to add Arrow Flight middleware +when initializing a client. +""" from pyarrow import flight from Examples.config import Config From 670613faf966732f64f06b2382a45784f3c7332d Mon Sep 17 00:00:00 2001 From: karel rehor Date: Fri, 24 Apr 2026 11:10:13 +0200 Subject: [PATCH 18/36] docs: pre PR inventory and final TODOs added to README.md --- Examples/README.md | 24 +++++++++++++++++++++++- Examples/advanced/__init__.py | 0 Examples/core/basic_query.py | 5 ++--- Examples/core/basic_write.py | 3 ++- Examples/core/timeouts.py | 2 +- 5 files changed, 28 insertions(+), 6 deletions(-) create mode 100644 Examples/advanced/__init__.py diff --git a/Examples/README.md b/Examples/README.md index e5139f9..436f064 100644 --- a/Examples/README.md +++ b/Examples/README.md @@ -114,7 +114,7 @@ TODO - delete this section as examples take shape and before creating PR. 1. Some examples show leveraging internal **kwargs like `data_frame_measurement_name` or `data_frame_tag_columns` - Makes sense to expose these in _advanced_ examples. (e.g. pandas examples... ) - Perhaps though should encourage the use of a simpler standard API that hides them - 2. Remove dependencies on remote `../githubusercontent/../*.csv` + 2. Remove dependencies on remote `../githubusercontent/../*.csv` __DONE__ 2. Leverage `config.py` in all examples 3. Prefer using Influxdb3 Core by default. But also document possibility of using other products. 4. Add shebangs to functional examples @@ -127,3 +127,25 @@ TODO - delete this section as examples take shape and before creating PR. 8. Verify all refactored examples are working *. __NOTE__ - If making an _illustrative_ example functional _out-of-the-box_ leads to too much distractive information being added, leave the example as _illustrative only_ and add a comment that it is for purposes of illustration. However, make sure the illustrative example is still working in a concrete implementation. (e.g. `query_with_middleware.py`) *. TODO In comments, mark examples as either _illustrative_ or _functional_ + +#### Standardization summary (F - functional, I - illustrative) + +| example | F/I | Head Comment | shebang | Config() | **kwargs (adv) | github user link | Timestamps | Notes | +|---------------------------------|-----|--------------|----------|----------|----------------|--------------------|------------|--------------------------------------------------| +| advanced/database_transfer.py | TODO | None | None | No | Yes (pd) | None | ??? | TODO revise | +| advanced/downsample.py | F | None | None | Yes | Yes (pd) | None | Dynamic / Current | TODO shebang + header | +| core/basic_write.py | F | Yes | Yes | Yes | None | None | Dynamic / Now | Ready | +| core/basic_query.py | F | Yes | Yes | Yes | None | None | Read only | Ready | +| core/basic_ssl.py | F | Yes | Yes | Yes | None | None | Dynamic / Now | Ready | +| core/timeouts.py | F | Yes | Yes | Yes | None | None | Now (implicit) | Ready | +| jupyter/basic-write-query.ipynb | F | N/A | N/A | N/A | None | None | Dynamic | Ready | +| query/flight_options.py | I | Yes | None | Yes | None | None | Read only | Ready | +| query/handle_query_error.py | F | Yes | Yes | Yes | None | None | Read only | Ready | +| query/query_async.py | F | Yes | Yes | Yes | None | None | Dynamic | Ready | +| query/query_modes.py | F | Yes | Yes | Yes | Yes (pd) | None | Dynamic | TODO - review kwargs use in non-advanced example | +| query/query_with_middleware.py | I | Yes | None | Yes | None | None | Read only | Ready | +| write/batching.py | F | Yes | Yes | Yes | None | None | Dynamic | Ready | +| write/fileimport.py | F | Yes | Yes | Yes | Yes (`file_write()`) | None | Dynamic | Ready - kwargs here are part of example | +| write/handle_http_error.py | F | Yes | Yes | Yes | None | None | N.A. | Ready - shows write error | +| write/pandas_write.py | F | Yes | Yes | Yes | Yes (pd) | None | Dynamic | Ready - kwargs here are part of example | +| write/writeoptions | F | Yes | Yes | Yes | None | None | Dynamic / now | Ready | diff --git a/Examples/advanced/__init__.py b/Examples/advanced/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Examples/core/basic_query.py b/Examples/core/basic_query.py index c20368c..a3dab6d 100644 --- a/Examples/core/basic_query.py +++ b/Examples/core/basic_query.py @@ -1,9 +1,8 @@ #!/usr/bin/env python3 """ -basic_query.py - shows the simplest ways in which to query data from an influxdb3 database. +basic_query.py - is a functional example that shows the simplest ways in which to query data from an influxdb3 database. -As a functional example it should be run after running basic_write.py, which prepares the measurements -queried here. +It should be run after running basic_write.py, which prepares the measurements queried here. For more information on working with query modes see the `query/query_modes.py` example. """ diff --git a/Examples/core/basic_write.py b/Examples/core/basic_write.py index b4a0097..ab8c933 100644 --- a/Examples/core/basic_write.py +++ b/Examples/core/basic_write.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 """ -basic_write.py - shows the simplest ways in which to write data to an influxdb3 database using Point objects. +basic_write.py - is a functional example showing the simplest ways +in which to write data to an influxdb3 database using Point objects. After successfully running this example try basic_query.py to verify the results. """ diff --git a/Examples/core/timeouts.py b/Examples/core/timeouts.py index 507631b..f0138f1 100644 --- a/Examples/core/timeouts.py +++ b/Examples/core/timeouts.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 """ -timeouts.py - shows how to set query and write timeouts. +timeouts.py - is a functional example that shows how to set query and write timeouts. They can be set directly using arguments (write_timeout, query_timeout) in the client constructor. They can also be overridden in write and query calls. From fdc4117ae3a2eb875d774eb6aa1759e165faa7c5 Mon Sep 17 00:00:00 2001 From: karel rehor Date: Fri, 24 Apr 2026 14:37:05 +0200 Subject: [PATCH 19/36] chore: checkpoint 3 - ensure header commentsand remove kwargs from examples. --- Examples/README.md | 69 +++++++++++++------------- Examples/advanced/database_transfer.py | 20 +++++--- Examples/advanced/downsample.py | 8 +-- Examples/query/query_modes.py | 39 ++++++++------- Examples/write/pandas_write.py | 2 - 5 files changed, 71 insertions(+), 67 deletions(-) diff --git a/Examples/README.md b/Examples/README.md index 436f064..fcf52a5 100644 --- a/Examples/README.md +++ b/Examples/README.md @@ -45,7 +45,7 @@ Basic examples can be found in the `Examples/core` directory. Richer examples can be found in the `Examples/query` directory. - * `flight_options.py` - shows how to set options on the query transport. (TODO see point 7.3 below) + * `flight_options.py` - shows how to set options on the query transport. * `handle_query_error.py` - shows basic error handling. * `query_async.py` - shows basic usage of the asynchronous query API. * `query_modes.py` - when making a query, different modes return data in different types of structures. This example shows which modes return which structured formats. @@ -71,15 +71,15 @@ TODO - delete this section as examples take shape and before creating PR. - apparently depends on other examples - write and query options are illustrated in other examples 6. `cookbook.ipynb` - most of this is migrated to `./jupyter/basic-write-query.ipynb` - can be removed __DONE__ - - what to do about step _write table to parquet file_? - not necessary. + - what to do about step _write table to parquet file_? - not necessary __DONE__. 2. Keep `file-import` as single file, with source data updatable - see `updater.py` __DONE__ 3. Keep one simple example of jupiter notebook. __DONE__ 4. Decide what to do with `./community` examples. * `custom_url.py` __DONE__ * Doesn't seem to do what is on the tin. * Seems more like a _down sampling_ example - * Doesn't query Influxdb directly but down samples from remote CSV - * TODO __DONE__ + * ~~Doesn't query Influxdb directly but down samples from remote CSV~~ + * review/enhance __DONE__ * rename to _down sampling_ or similar __DONE__ * use initial query __DONE__ * move to `./advanced` __DONE__ @@ -89,10 +89,9 @@ TODO - delete this section as examples take shape and before creating PR. * Useful base example * Current illustrative only - doesn't look functional without some undocumented setup * To work requires `dbfrom` and measurement `airSensors` to exist. - * Note - are camel caps not sometimes problematic in Influx? (seem to recall encountering problem with them in the past) - * TODO + * review __DONE__ * move to `./advanced` __DONE__ - * make functional + * ~~make functional~~ kept as _illustrative_ - `advanced/downsample.py` is similar _and_ functional 5. Root examples 1. `basic_ssl_examle.py` to `./core` __DONE__ 2. `batching_example.py` to `./write` __DONE__ @@ -101,8 +100,8 @@ TODO - delete this section as examples take shape and before creating PR. 5. `config.py` - universal configuration file. Keep as is. 6. `example.csv` - where is this used? It doesn't seem to be used in any example... ??? (Removed __DONE__) 7. `flight_options_example.py` - to `./query` __DONE__ - - Note only option illustrated is tls certificate. - - TODO after move - either enrich/refactor or verify this isn't covered elsewhere + - ~~Note only option illustrated is tls certificate~~. + - after move - either enrich/refactor or verify this isn't covered elsewhere __DONE__ 8. `handle_http_error.py` - to `./write` __DONE__ 9. `handle_query_error.py` - to `./query` __DONE__ 10. `pandas_write.py` - to `./write` __DONE__ @@ -111,41 +110,41 @@ TODO - delete this section as examples take shape and before creating PR. 13. `query_with_middleware.py` - to `./query` __DONE__ 14. `timeouts.py` - to `./core` __DONE__ 6. Standardization - 1. Some examples show leveraging internal **kwargs like `data_frame_measurement_name` or `data_frame_tag_columns` + 1. Some examples show leveraging internal **kwargs like `data_frame_measurement_name` or `data_frame_tag_columns` __DONE__ - Makes sense to expose these in _advanced_ examples. (e.g. pandas examples... ) - Perhaps though should encourage the use of a simpler standard API that hides them 2. Remove dependencies on remote `../githubusercontent/../*.csv` __DONE__ - 2. Leverage `config.py` in all examples + 2. Leverage `config.py` in all examples __DONE__ 3. Prefer using Influxdb3 Core by default. But also document possibility of using other products. - 4. Add shebangs to functional examples - 5. Ensure timestamps are current and not fixed for example to 2023 + 4. Add shebangs to functional examples __DONE__ + 5. Ensure timestamps are current and not fixed for example to 2023 __DONE__ 7. Enhancements 1. `writeoptions.py` - does not show much in the way of setting options. (Update and revision - __DONE__) 2. `basic_ssl.py` - review. Seems to only show handling SSL handshake failures. (Reviewed and updated - __DONE__) - 3. `write_pandas.py` - has fixed dates from 2023, make dynamic with current. - 3. `flight_options.py` - review. This example is nearly three years old, and flight/query options has been greatly enhanced since then. + 3. `write_pandas.py` - has fixed dates from 2023, make dynamic with current. __DONE__ + 3. `flight_options.py` - review. This example is nearly three years old, and flight/query options has been greatly enhanced since then. __DONE__ 8. Verify all refactored examples are working *. __NOTE__ - If making an _illustrative_ example functional _out-of-the-box_ leads to too much distractive information being added, leave the example as _illustrative only_ and add a comment that it is for purposes of illustration. However, make sure the illustrative example is still working in a concrete implementation. (e.g. `query_with_middleware.py`) - *. TODO In comments, mark examples as either _illustrative_ or _functional_ + *. In comments, mark examples as either _illustrative_ or _functional_ __DONE__ #### Standardization summary (F - functional, I - illustrative) -| example | F/I | Head Comment | shebang | Config() | **kwargs (adv) | github user link | Timestamps | Notes | -|---------------------------------|-----|--------------|----------|----------|----------------|--------------------|------------|--------------------------------------------------| -| advanced/database_transfer.py | TODO | None | None | No | Yes (pd) | None | ??? | TODO revise | -| advanced/downsample.py | F | None | None | Yes | Yes (pd) | None | Dynamic / Current | TODO shebang + header | -| core/basic_write.py | F | Yes | Yes | Yes | None | None | Dynamic / Now | Ready | -| core/basic_query.py | F | Yes | Yes | Yes | None | None | Read only | Ready | -| core/basic_ssl.py | F | Yes | Yes | Yes | None | None | Dynamic / Now | Ready | -| core/timeouts.py | F | Yes | Yes | Yes | None | None | Now (implicit) | Ready | -| jupyter/basic-write-query.ipynb | F | N/A | N/A | N/A | None | None | Dynamic | Ready | -| query/flight_options.py | I | Yes | None | Yes | None | None | Read only | Ready | -| query/handle_query_error.py | F | Yes | Yes | Yes | None | None | Read only | Ready | -| query/query_async.py | F | Yes | Yes | Yes | None | None | Dynamic | Ready | -| query/query_modes.py | F | Yes | Yes | Yes | Yes (pd) | None | Dynamic | TODO - review kwargs use in non-advanced example | -| query/query_with_middleware.py | I | Yes | None | Yes | None | None | Read only | Ready | -| write/batching.py | F | Yes | Yes | Yes | None | None | Dynamic | Ready | -| write/fileimport.py | F | Yes | Yes | Yes | Yes (`file_write()`) | None | Dynamic | Ready - kwargs here are part of example | -| write/handle_http_error.py | F | Yes | Yes | Yes | None | None | N.A. | Ready - shows write error | -| write/pandas_write.py | F | Yes | Yes | Yes | Yes (pd) | None | Dynamic | Ready - kwargs here are part of example | -| write/writeoptions | F | Yes | Yes | Yes | None | None | Dynamic / now | Ready | +| example | F/I | Head Comment | shebang | Config() | **kwargs (adv) | github user link | Timestamps | Notes | +|---------------------------------|-----|--------------|---------|----------|----------------------|------------------|------------------------------|-----------------------------------------| +| advanced/database_transfer.py | I | Yes | None | Yes | Yes (pd) | None | N.A. - copied without change | Ready | +| advanced/downsample.py | F | Yes | Yes | Yes | Yes (pd) | None | Dynamic / Current | Ready | +| core/basic_write.py | F | Yes | Yes | Yes | None | None | Dynamic / Now | Ready | +| core/basic_query.py | F | Yes | Yes | Yes | None | None | Read only | Ready | +| core/basic_ssl.py | F | Yes | Yes | Yes | None | None | Dynamic / Now | Ready | +| core/timeouts.py | F | Yes | Yes | Yes | None | None | Now (implicit) | Ready | +| jupyter/basic-write-query.ipynb | F | N/A | N/A | N/A | None | None | Dynamic | Ready | +| query/flight_options.py | I | Yes | None | Yes | None | None | Read only | Ready | +| query/handle_query_error.py | F | Yes | Yes | Yes | None | None | Read only | Ready | +| query/query_async.py | F | Yes | Yes | Yes | None | None | Dynamic | Ready | +| query/query_modes.py | F | Yes | Yes | Yes | None | None | Dynamic | Ready | +| query/query_with_middleware.py | I | Yes | None | Yes | None | None | Read only | Ready | +| write/batching.py | F | Yes | Yes | Yes | None | None | Dynamic | Ready | +| write/fileimport.py | F | Yes | Yes | Yes | Yes (`file_write()`) | None | Dynamic | Ready - kwargs here are part of example | +| write/handle_http_error.py | F | Yes | Yes | Yes | None | None | N.A. | Ready - shows write error | +| write/pandas_write.py | F | Yes | Yes | Yes | Yes (pd) | None | Dynamic | Ready - kwargs here are part of example | +| write/writeoptions | F | Yes | Yes | Yes | None | None | Dynamic / now | Ready | diff --git a/Examples/advanced/database_transfer.py b/Examples/advanced/database_transfer.py index 478532b..44f7e63 100644 --- a/Examples/advanced/database_transfer.py +++ b/Examples/advanced/database_transfer.py @@ -1,7 +1,12 @@ +""" +database_transfer.py - is an illustrative examples showing how to copy data from one Influxdb 3 database to another. +""" import time -import influxdb_client_3 as InfluxDBClient3 -from influxdb_client_3 import write_client_options, WriteOptions, InfluxDBError +from influxdb_client_3 import InfluxDBClient3, write_client_options, WriteOptions, InfluxDBError +from Examples.config import Config + +config = Config() class BatchingCallback(object): @@ -16,11 +21,9 @@ def retry(self, conf, data: str, exception: InfluxDBError): print(f"Retryable error occurs for batch: {conf}, data: {data} retry: {exception}") -# InfluxDB connection details -token = "" +# Data management details dbfrom = "a" dbto = "b" -url = "eu-central-1-1.aws.cloud2.influxdata.com" measurement = "airSensors" taglist = [] @@ -41,9 +44,10 @@ def retry(self, conf, data: str, exception: InfluxDBError): ) # Opening InfluxDB client with a batch size of 5k points or flush interval # of 10k ms and gzip compression -with InfluxDBClient3.InfluxDBClient3(token=token, - host=url, - enable_gzip=True, write_client_options=wco) as _client: +with InfluxDBClient3(token=config.token, + host=config.host, + enable_gzip=True, + write_client_options=wco) as _client: query = f"SHOW TAG KEYS FROM {measurement}" tags = _client.query(query=query, language="influxql", database=dbfrom) tags = tags.to_pydict() diff --git a/Examples/advanced/downsample.py b/Examples/advanced/downsample.py index 65e9c17..9301b76 100644 --- a/Examples/advanced/downsample.py +++ b/Examples/advanced/downsample.py @@ -1,3 +1,8 @@ +#!/usr/bin/env python3 +""" +downsample.py - is a functional example showing how to reduce a larger measurement set to a smaller one +by averaging values across a given time window. +""" import datetime import random @@ -42,9 +47,6 @@ def retry(self, conf, data: str, exception: InfluxDBError): current = now - datetime.timedelta(days=1) -print(f"DEBUG now: {now} type is {type(now)}") -print(f"DEBUG current: {current} type is {type(current)}") - # Lists of possible trainers trainers = ["ash", "brock", "misty", "gary", "jessie", "james"] diff --git a/Examples/query/query_modes.py b/Examples/query/query_modes.py index 0b9fb3f..a35e8be 100644 --- a/Examples/query/query_modes.py +++ b/Examples/query/query_modes.py @@ -2,30 +2,31 @@ """ query_modes.py - is a functional example that shows how to use different modes when executing queries. """ -import pandas as pd +import pytz import numpy as np -from influxdb_client_3 import InfluxDBClient3 + +from datetime import datetime, timedelta + +from influxdb_client_3 import InfluxDBClient3, Point from Examples.config import Config def prep_data(client: InfluxDBClient3, measurement: str): - now = pd.Timestamp.now(tz="utc").floor(freq='s') - start = now - pd.Timedelta(minutes=5) - timestamps = pd.date_range(start=start, end=now, freq='10s') - - df = pd.DataFrame( - np.random.randn( - len(timestamps), - 3), - index=timestamps, - columns=[ - 'volts', - 'amps', - 'dBm']) - df['model'] = np.random.choice(['R2D2', 'C3PO', 'ROBBIE', 'HAL'], len(df)) - - client.write(df, data_frame_measurement_name=measurement, - data_frame_tag_columns=['model']) + now = datetime.now(tz=pytz.utc) + current = now - timedelta(minutes=5) + + points = [] + + while current < now: + points.append(Point(measurement) + .tag('model', np.random.choice(['R2D2', 'C3PO', 'ROBBIE', 'HAL'])) + .field('mV', np.random.uniform(low=0, high=1000)) + .field('mA', np.random.uniform(low=0, high=1000)) + .field('dBm', np.random.uniform(low=-10, high=10)) + .time(current)) + current += timedelta(seconds=10) + + client.write(points) def query_chunk(client: InfluxDBClient3, influxql_query: str): diff --git a/Examples/write/pandas_write.py b/Examples/write/pandas_write.py index 4d03c45..07ec4af 100644 --- a/Examples/write/pandas_write.py +++ b/Examples/write/pandas_write.py @@ -12,8 +12,6 @@ def main(config: Config): - print(f"DEBUG config: {config}") - with InfluxDBClient3.InfluxDBClient3( token=config.token, host=config.host, From be7efc8b992ec5b4d3d4d5fa381581262fb2d632 Mon Sep 17 00:00:00 2001 From: karel rehor Date: Fri, 24 Apr 2026 17:56:06 +0200 Subject: [PATCH 20/36] chore: additional steps in Example/README.md and use absolute paths in fileimport.py --- Examples/README.md | 27 +++++++++++++++++++++++++-- Examples/write/fileimport.py | 4 +++- Examples/write/source_data/updater.py | 26 +++++++++++++++----------- 3 files changed, 43 insertions(+), 14 deletions(-) diff --git a/Examples/README.md b/Examples/README.md index fcf52a5..0a96086 100644 --- a/Examples/README.md +++ b/Examples/README.md @@ -4,7 +4,7 @@ First time users will likely want to study the examples in the `./core` director ### Underlying principles -Influxdb3 uses two transports: one for writing data and another for querying. For writes a standard HTTP REST style client is used. Queries on the other hand make use of an HTTP/2.0 and [gRPC](https://grpc.io/docs/what-is-grpc/introduction/) compliant client under [Apache Arrow Flight](https://arrow.apache.org/cookbook/py/flight.html). +Influxdb3 uses two transports: one for writing data and another for querying. For __writes__ a standard _HTTP REST_ style client is used internally. __Queries__ on the other hand make use of an HTTP/2.0 and a _[gRPC](https://grpc.io/docs/what-is-grpc/introduction/)_ compliant client under _[Apache Arrow Flight](https://arrow.apache.org/cookbook/py/flight.html)_. Most of the examples found here are functional and should be runnable against an Influxdb3 database, whether in the cloud or locally using for example Influxdb3 Core. They have been revised and tested against the Influxdb3 Core product. @@ -18,6 +18,29 @@ Functional examples make use of the base `Config` class in `config.py`. User va * `INFLUXDB_DATABASE` - default database to be used with the examples. * `INFLUXDB_TOKEN` - a token associated with read and write permissions to the default database and any additional databases that might be used with the examples. +It is recommended to run examples using a python virtual environment. + +For example... + +```bash +$ python -m venv venv +$ source ./venv/bin/activate +``` + +Before running any functional examples, ensure that the Influxdb3-python project is installed. From the `influxdb3-python` project root run... + +```bash +$ pip install . +``` + +A few of the examples depend on libraries not included in Influxdb3 python. Be sure to install them as well. + +```bash +$ Examples/install_extra_deps.sh +``` + +Functional examples come with a shebang header and should run from the commandline once exec permissions are set. + ### Writing data Basic examples can be found in the `Examples/core` directory. @@ -30,7 +53,7 @@ Richer examples can be found in the `Examples/write` directory. * `batching.py` - shows how to make use of the _batching_ API for writing long-running data sets. * `fileimport.py` - shows how to import data to an Influx database directly from a number of other standard database formats. - * To refresh the source data use `Examples/write/source_data/updater.py` + * To refresh the source data used in the example, please run `Examples/write/source_data/updater.py` beforehand. * `handle_http_error.py` - shows error handling on writes. * `pandas_write.py` - shows how to write pandas dataframes directly to an Influx database. * `writeoptions.py` - shows the core options API for writes. diff --git a/Examples/write/fileimport.py b/Examples/write/fileimport.py index 08f2fb4..48d859a 100644 --- a/Examples/write/fileimport.py +++ b/Examples/write/fileimport.py @@ -15,6 +15,8 @@ from Examples.config import Config +dir_path = os.path.dirname(os.path.realpath(__file__)) + data_types = ["csv", "json", "feather", "orc", "parquet"] @@ -75,7 +77,7 @@ def main(file_types=("csv",)) -> None: continue logging.info(f"Writing from DB file of type: {_ftype}") - source_file = f"./source_data/out_update.{_ftype}" + source_file = f"{dir_path}/source_data/out_update.{_ftype}" if not (os.path.exists(source_file) and os.path.isfile(source_file)): logging.error(f"Source DB file {source_file} not found.") logging.error(" TIP!: Perhaps source_data/updater.py needs to be run.") diff --git a/Examples/write/source_data/updater.py b/Examples/write/source_data/updater.py index d6c5526..b49f7a6 100644 --- a/Examples/write/source_data/updater.py +++ b/Examples/write/source_data/updater.py @@ -1,8 +1,12 @@ +#!/usr/bin/env python3 import pandas as pd import time import logging import random import numpy as np +import os + +dir_path = os.path.dirname(os.path.realpath(__file__)) def update_measurement_name(source: pd.DataFrame, measurment_name: str): @@ -26,7 +30,7 @@ def update_timestamps(source: pd.DataFrame): if ts_type in ["int", "int32", "int64"]: source.loc[count, "time"] = current else: - source.loc[count, "time"] = pd.Timestamp(current) + source.loc[count, "time"] = pd.Timestamp(current).__str__() count += 1 current = current + interval @@ -36,40 +40,40 @@ def update_timestamps(source: pd.DataFrame): def feather_update(): logging.info("Updating feather") - f_df = pd.read_feather('./out.feather') + f_df = pd.read_feather(f"{dir_path}/out.feather") update_timestamps(f_df) update_measurement_name(f_df, "machine_data_feather") - f_df.to_feather('./out_update.feather') + f_df.to_feather(f"{dir_path}/out_update.feather") def orc_update(): logging.info("Updating orc") - o_df = pd.read_orc('./out.orc') + o_df = pd.read_orc(f"{dir_path}/out.orc") update_timestamps(o_df) update_measurement_name(o_df, "machine_data_orc") - o_df.to_orc('./out_update.orc', index=False) + o_df.to_orc(f"{dir_path}/out_update.orc", index=False) def parquet_update(): logging.info("Updating parquet") - p_df = pd.read_parquet('./out.parquet') + p_df = pd.read_parquet(f"{dir_path}/out.parquet") update_timestamps(p_df) update_measurement_name(p_df, "machine_data_parquet") - p_df.to_parquet('./out_update.parquet', index=False) + p_df.to_parquet(f"{dir_path}/out_update.parquet", index=False) def csv_update(): logging.info("Updating csv") - csv_df = pd.read_csv('./out.csv') + csv_df = pd.read_csv(f"{dir_path}/out.csv") update_timestamps(csv_df) - csv_df.to_csv('./out_update.csv', index=False) + csv_df.to_csv(f"{dir_path}/out_update.csv", index=False) def json_update(): logging.info("Updating json") - json_df = pd.read_json('./out.json') + json_df = pd.read_json(f"{dir_path}/out.json") update_timestamps(json_df) - json_df.to_json('./out_update.json', orient='records', index=False) + json_df.to_json(f"{dir_path}/out_update.json", orient='records', index=False) if __name__ == "__main__": From 236040e2fce710ffff5e53011a2f93b0b8fe0ebb Mon Sep 17 00:00:00 2001 From: karel rehor Date: Fri, 24 Apr 2026 18:04:01 +0200 Subject: [PATCH 21/36] chore: add extra dependencies script for Examples --- Examples/install_extra_deps.sh | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100755 Examples/install_extra_deps.sh diff --git a/Examples/install_extra_deps.sh b/Examples/install_extra_deps.sh new file mode 100755 index 0000000..4ccdc69 --- /dev/null +++ b/Examples/install_extra_deps.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +# installs packages that are not part of Influxdb3 python +# but required to run some examples + +pip install numpy pandas bson matplotlib From 2e6508d6bc2de96cba8f40cd3973502d841441bd Mon Sep 17 00:00:00 2001 From: karel rehor Date: Mon, 27 Apr 2026 10:15:21 +0200 Subject: [PATCH 22/36] chore: replace Config() with ENVARS in core examples. --- Examples/README.md | 41 ++++++++++++++++++------------------ Examples/core/basic_query.py | 12 ++++++----- Examples/core/basic_ssl.py | 26 ++++++++++++----------- Examples/core/basic_write.py | 13 +++++++----- Examples/core/timeouts.py | 12 ++++++++--- 5 files changed, 59 insertions(+), 45 deletions(-) diff --git a/Examples/README.md b/Examples/README.md index 0a96086..72a042f 100644 --- a/Examples/README.md +++ b/Examples/README.md @@ -137,7 +137,8 @@ TODO - delete this section as examples take shape and before creating PR. - Makes sense to expose these in _advanced_ examples. (e.g. pandas examples... ) - Perhaps though should encourage the use of a simpler standard API that hides them 2. Remove dependencies on remote `../githubusercontent/../*.csv` __DONE__ - 2. Leverage `config.py` in all examples __DONE__ + 2. ~~Leverage `config.py` in all examples~~ __DONE__ + 1. TODO undo and use standard ENVARS instead 3. Prefer using Influxdb3 Core by default. But also document possibility of using other products. 4. Add shebangs to functional examples __DONE__ 5. Ensure timestamps are current and not fixed for example to 2023 __DONE__ @@ -152,22 +153,22 @@ TODO - delete this section as examples take shape and before creating PR. #### Standardization summary (F - functional, I - illustrative) -| example | F/I | Head Comment | shebang | Config() | **kwargs (adv) | github user link | Timestamps | Notes | -|---------------------------------|-----|--------------|---------|----------|----------------------|------------------|------------------------------|-----------------------------------------| -| advanced/database_transfer.py | I | Yes | None | Yes | Yes (pd) | None | N.A. - copied without change | Ready | -| advanced/downsample.py | F | Yes | Yes | Yes | Yes (pd) | None | Dynamic / Current | Ready | -| core/basic_write.py | F | Yes | Yes | Yes | None | None | Dynamic / Now | Ready | -| core/basic_query.py | F | Yes | Yes | Yes | None | None | Read only | Ready | -| core/basic_ssl.py | F | Yes | Yes | Yes | None | None | Dynamic / Now | Ready | -| core/timeouts.py | F | Yes | Yes | Yes | None | None | Now (implicit) | Ready | -| jupyter/basic-write-query.ipynb | F | N/A | N/A | N/A | None | None | Dynamic | Ready | -| query/flight_options.py | I | Yes | None | Yes | None | None | Read only | Ready | -| query/handle_query_error.py | F | Yes | Yes | Yes | None | None | Read only | Ready | -| query/query_async.py | F | Yes | Yes | Yes | None | None | Dynamic | Ready | -| query/query_modes.py | F | Yes | Yes | Yes | None | None | Dynamic | Ready | -| query/query_with_middleware.py | I | Yes | None | Yes | None | None | Read only | Ready | -| write/batching.py | F | Yes | Yes | Yes | None | None | Dynamic | Ready | -| write/fileimport.py | F | Yes | Yes | Yes | Yes (`file_write()`) | None | Dynamic | Ready - kwargs here are part of example | -| write/handle_http_error.py | F | Yes | Yes | Yes | None | None | N.A. | Ready - shows write error | -| write/pandas_write.py | F | Yes | Yes | Yes | Yes (pd) | None | Dynamic | Ready - kwargs here are part of example | -| write/writeoptions | F | Yes | Yes | Yes | None | None | Dynamic / now | Ready | +| example | F/I | Head Comment | shebang | ~~Config()~~ | ENVARS | **kwargs (adv) | github user link | Timestamps | Notes | +|---------------------------------|-----|--------------|---------|--------------|--------|----------------------|------------------|------------------------------|-----------------------------------------| +| advanced/database_transfer.py | I | Yes | None | Yes | TODO | Yes (pd) | None | N.A. - copied without change | Ready | +| advanced/downsample.py | F | Yes | Yes | Yes | TODO | Yes (pd) | None | Dynamic / Current | Ready | +| core/basic_write.py | F | Yes | Yes | None | Yes | None | None | Dynamic / Now | Ready | +| core/basic_query.py | F | Yes | Yes | None | Yes | None | None | Read only | Ready | +| core/basic_ssl.py | F | Yes | Yes | None | Yes | None | None | Dynamic / Now | Ready | +| core/timeouts.py | F | Yes | Yes | None | Yes | None | None | Now (implicit) | Ready | +| jupyter/basic-write-query.ipynb | F | N/A | N/A | N/A | TODO | None | None | Dynamic | Ready | +| query/flight_options.py | I | Yes | None | Yes | TODO | None | None | Read only | Ready | +| query/handle_query_error.py | F | Yes | Yes | Yes | TODO | None | None | Read only | Ready | +| query/query_async.py | F | Yes | Yes | Yes | TODO | None | None | Dynamic | Ready | +| query/query_modes.py | F | Yes | Yes | Yes | TODO | None | None | Dynamic | Ready | +| query/query_with_middleware.py | I | Yes | None | Yes | TODO | None | None | Read only | Ready | +| write/batching.py | F | Yes | Yes | Yes | TODO | None | None | Dynamic | Ready | +| write/fileimport.py | F | Yes | Yes | Yes | TODO | Yes (`file_write()`) | None | Dynamic | Ready - kwargs here are part of example | +| write/handle_http_error.py | F | Yes | Yes | Yes | TODO | None | None | N.A. | Ready - shows write error | +| write/pandas_write.py | F | Yes | Yes | Yes | TODO | Yes (pd) | None | Dynamic | Ready - kwargs here are part of example | +| write/writeoptions | F | Yes | Yes | Yes | TODO | None | None | Dynamic / now | Ready | diff --git a/Examples/core/basic_query.py b/Examples/core/basic_query.py index a3dab6d..ddbdb02 100644 --- a/Examples/core/basic_query.py +++ b/Examples/core/basic_query.py @@ -6,15 +6,17 @@ For more information on working with query modes see the `query/query_modes.py` example. """ -from Examples.config import Config +import os from influxdb_client_3 import InfluxDBClient3 -config = Config() +HOST = os.getenv('INFLUXDB_HOST') or 'http://localhost:8181' +TOKEN = os.getenv('INFLUXDB_TOKEN') or 'my-token' +DATABASE = os.getenv('INFLUXDB_DATABASE') or 'my-db' client = InfluxDBClient3( - token=config.token, - host=config.host, - database=config.database,) + token=TOKEN, + host=HOST, + database=DATABASE,) measurement = "basic_caught" diff --git a/Examples/core/basic_ssl.py b/Examples/core/basic_ssl.py index a565a4c..3ef86b6 100644 --- a/Examples/core/basic_ssl.py +++ b/Examples/core/basic_ssl.py @@ -14,9 +14,12 @@ import pyarrow -from Examples.config import Config from influxdb_client_3 import InfluxDBClient3 +HOST = os.getenv('INFLUXDB_HOST') or 'http://localhost:8181' +TOKEN = os.getenv('INFLUXDB_TOKEN') or 'my-token' +DATABASE = os.getenv('INFLUXDB_DATABASE') or 'my-db' + bad_cert = """-----BEGIN CERTIFICATE----- MIIFDTCCAvWgAwIBAgIUYzpfisy9xLrhiZd+D9vOdzC3+iswDQYJKoZIhvcNAQEL BQAwFjEUMBIGA1UEAwwLdGVzdGhvc3QuaW8wHhcNMjUwMjI4MTM1NTMyWhcNMzUw @@ -68,20 +71,19 @@ def print_results(results: list): def main() -> None: print("Main") temp_cert_file = "temp_cert.pem" - conf = Config() - write_and_query_with_explicit_sys_cert(conf) + write_and_query_with_explicit_sys_cert() write_cert(bad_cert, temp_cert_file) - query_with_verify_ssl_off(conf, temp_cert_file) + query_with_verify_ssl_off(temp_cert_file) remove_cert(temp_cert_file) -def write_and_query_with_explicit_sys_cert(conf): +def write_and_query_with_explicit_sys_cert(): print("\nwrite and query with typical linux system cert\n") - with InfluxDBClient3(token=conf.token, - host=conf.host, - database=conf.database, + with InfluxDBClient3(token=TOKEN, + host=HOST, + database=DATABASE, ssl_ca_cert="/etc/ssl/certs/ca-certificates.crt", verify_ssl=True) as _client: now = time.time_ns() @@ -93,14 +95,14 @@ def write_and_query_with_explicit_sys_cert(conf): print_results(reader.to_pylist()) -def query_with_verify_ssl_off(conf, cert): +def query_with_verify_ssl_off(cert): print("\nquerying with verify_ssl off\n") # Note that the passed root cert above is bad # Switch verify_ssl to True to throw SSL_ERROR_SSL - with InfluxDBClient3(token=conf.token, - host=conf.host, - database=conf.database, + with InfluxDBClient3(token=TOKEN, + host=HOST, + database=DATABASE, ssl_ca_cert=cert, verify_ssl=False) as _client: diff --git a/Examples/core/basic_write.py b/Examples/core/basic_write.py index ab8c933..c399a74 100644 --- a/Examples/core/basic_write.py +++ b/Examples/core/basic_write.py @@ -6,15 +6,18 @@ After successfully running this example try basic_query.py to verify the results. """ import datetime +import os -from Examples.config import Config from influxdb_client_3 import InfluxDBClient3, Point -config = Config() +HOST = os.getenv('INFLUXDB_HOST') or 'http://localhost:8181' +TOKEN = os.getenv('INFLUXDB_TOKEN') or 'my-token' +DATABASE = os.getenv('INFLUXDB_DATABASE') or 'my-db' + client = InfluxDBClient3( - token=config.token, - host=config.host, - database=config.database,) + token=TOKEN, + host=HOST, + database=DATABASE,) now = datetime.datetime.now(datetime.timezone.utc) diff --git a/Examples/core/timeouts.py b/Examples/core/timeouts.py index f0138f1..32b2735 100644 --- a/Examples/core/timeouts.py +++ b/Examples/core/timeouts.py @@ -11,6 +11,7 @@ Be sure to update INFLUXDB_HOST, INFLUXDB_DATABASE and INFLUXDB_TOKEN environment variables, before running this example. """ +import os import sys import time @@ -28,12 +29,17 @@ def handle_write_error_cb(rd, rt, rx): def main(w_to: int, q_to: int) -> None: + + host = os.getenv('INFLUXDB_HOST') or 'http://localhost:8181' + token = os.getenv('INFLUXDB_TOKEN') or 'my-token' + database = os.getenv('INFLUXDB_DATABASE') or 'my-db' + print(f"main {w_to}, {q_to}") lp_data = "timeout_example,location=terra fVal=3.14,iVal=42i" with InfluxDBClient3( - host=config.host, - token=config.token, - database=config.database, + host=host, + token=token, + database=database, write_timeout=w_to, query_timeout=q_to, write_client_options=write_client_options( From 74f311dddf9d95fd6b96a3343debed081a70453e Mon Sep 17 00:00:00 2001 From: karel rehor Date: Mon, 27 Apr 2026 11:09:18 +0200 Subject: [PATCH 23/36] chore: remove Config() from examples. --- Examples/README.md | 40 +++++++++++++------------ Examples/__init__.py | 1 - Examples/advanced/database_transfer.py | 10 ++++--- Examples/advanced/downsample.py | 19 ++++++------ Examples/config.py | 12 -------- Examples/core/timeouts.py | 3 -- Examples/query/flight_options.py | 13 ++++---- Examples/query/handle_query_error.py | 19 +++++++----- Examples/query/query_async.py | 21 +++++++------ Examples/query/query_modes.py | 19 ++++++++---- Examples/query/query_with_middleware.py | 14 +++++---- Examples/write/batching.py | 14 +++++---- Examples/write/fileimport.py | 12 ++++---- Examples/write/handle_http_error.py | 13 ++++---- Examples/write/pandas_write.py | 18 ++++++----- Examples/write/writeoptions.py | 18 ++++++----- 16 files changed, 135 insertions(+), 111 deletions(-) delete mode 100644 Examples/config.py diff --git a/Examples/README.md b/Examples/README.md index 72a042f..6c1d289 100644 --- a/Examples/README.md +++ b/Examples/README.md @@ -118,6 +118,8 @@ TODO - delete this section as examples take shape and before creating PR. 5. Root examples 1. `basic_ssl_examle.py` to `./core` __DONE__ 2. `batching_example.py` to `./write` __DONE__ + 1. Issue - getting interpreter shutdown before example ends. TODO - fix + 2. Issue - runs very slow - TODO speed up. 3. `cloud_dedicated_query.py` - is it necessary to have specific _cloud_dedicated_ examples? Can this simply be documented in `README.md` or in code comments? (Removed __DONE__) 4. `cloud_dedicated_write.py` - is it necessary to have specific _cloud_dedicated_ examples? Can this simply be documented in `README.md` or in code comments? (Removed __DONE__) 5. `config.py` - universal configuration file. Keep as is. @@ -153,22 +155,22 @@ TODO - delete this section as examples take shape and before creating PR. #### Standardization summary (F - functional, I - illustrative) -| example | F/I | Head Comment | shebang | ~~Config()~~ | ENVARS | **kwargs (adv) | github user link | Timestamps | Notes | -|---------------------------------|-----|--------------|---------|--------------|--------|----------------------|------------------|------------------------------|-----------------------------------------| -| advanced/database_transfer.py | I | Yes | None | Yes | TODO | Yes (pd) | None | N.A. - copied without change | Ready | -| advanced/downsample.py | F | Yes | Yes | Yes | TODO | Yes (pd) | None | Dynamic / Current | Ready | -| core/basic_write.py | F | Yes | Yes | None | Yes | None | None | Dynamic / Now | Ready | -| core/basic_query.py | F | Yes | Yes | None | Yes | None | None | Read only | Ready | -| core/basic_ssl.py | F | Yes | Yes | None | Yes | None | None | Dynamic / Now | Ready | -| core/timeouts.py | F | Yes | Yes | None | Yes | None | None | Now (implicit) | Ready | -| jupyter/basic-write-query.ipynb | F | N/A | N/A | N/A | TODO | None | None | Dynamic | Ready | -| query/flight_options.py | I | Yes | None | Yes | TODO | None | None | Read only | Ready | -| query/handle_query_error.py | F | Yes | Yes | Yes | TODO | None | None | Read only | Ready | -| query/query_async.py | F | Yes | Yes | Yes | TODO | None | None | Dynamic | Ready | -| query/query_modes.py | F | Yes | Yes | Yes | TODO | None | None | Dynamic | Ready | -| query/query_with_middleware.py | I | Yes | None | Yes | TODO | None | None | Read only | Ready | -| write/batching.py | F | Yes | Yes | Yes | TODO | None | None | Dynamic | Ready | -| write/fileimport.py | F | Yes | Yes | Yes | TODO | Yes (`file_write()`) | None | Dynamic | Ready - kwargs here are part of example | -| write/handle_http_error.py | F | Yes | Yes | Yes | TODO | None | None | N.A. | Ready - shows write error | -| write/pandas_write.py | F | Yes | Yes | Yes | TODO | Yes (pd) | None | Dynamic | Ready - kwargs here are part of example | -| write/writeoptions | F | Yes | Yes | Yes | TODO | None | None | Dynamic / now | Ready | +| example | F/I | Head Comment | shebang | ~~Config()~~ | ENVARS | **kwargs (adv) | github user link | Timestamps | Notes | +|---------------------------------|-----|--------------|---------|--------------|--------|----------------------|------------------|------------------------------|--| +| advanced/database_transfer.py | I | Yes | None | None | Yes | Yes (pd) | None | N.A. - copied without change | Review | +| advanced/downsample.py | F | Yes | Yes | None | Yes | Yes (pd) | None | Dynamic / Current | Review | +| core/basic_write.py | F | Yes | Yes | None | Yes | None | None | Dynamic / Now | Review | +| core/basic_query.py | F | Yes | Yes | None | Yes | None | None | Read only | Review | +| core/basic_ssl.py | F | Yes | Yes | None | Yes | None | None | Dynamic / Now | Review | +| core/timeouts.py | F | Yes | Yes | None | Yes | None | None | Now (implicit) | Review | +| jupyter/basic-write-query.ipynb | F | N/A | N/A | N/A | Yes | None | None | Dynamic | Review | +| query/flight_options.py | I | Yes | None | None | Yes | None | None | Read only | Review | +| query/handle_query_error.py | F | Yes | Yes | None | Yes | None | None | Read only | Review | +| query/query_async.py | F | Yes | Yes | None | Yes | None | None | Dynamic | Review | +| query/query_modes.py | F | Yes | Yes | None | Yes | None | None | Dynamic | Review | +| query/query_with_middleware.py | I | Yes | None | None | Yes | None | None | Read only | Review | +| write/batching.py | F | Yes | Yes | None | Yes | None | None | Dynamic | Review | +| write/fileimport.py | F | Yes | Yes | None | Yes | Yes (`file_write()`) | None | Dynamic | Review - kwargs here are part of example | +| write/handle_http_error.py | F | Yes | Yes | None | Yes | None | None | N.A. | Review - shows write error | +| write/pandas_write.py | F | Yes | Yes | None | Yes | Yes (pd) | None | Dynamic | Review - kwargs here are part of example | +| write/writeoptions | F | Yes | Yes | None | Yes | None | None | Dynamic / now | Review | diff --git a/Examples/__init__.py b/Examples/__init__.py index af2bb72..e69de29 100644 --- a/Examples/__init__.py +++ b/Examples/__init__.py @@ -1 +0,0 @@ -# used mainly to resolve local utility helpers like config.py diff --git a/Examples/advanced/database_transfer.py b/Examples/advanced/database_transfer.py index 44f7e63..19d497c 100644 --- a/Examples/advanced/database_transfer.py +++ b/Examples/advanced/database_transfer.py @@ -1,12 +1,14 @@ """ database_transfer.py - is an illustrative examples showing how to copy data from one Influxdb 3 database to another. """ +import os import time from influxdb_client_3 import InfluxDBClient3, write_client_options, WriteOptions, InfluxDBError -from Examples.config import Config -config = Config() +HOST = os.getenv('INFLUXDB_HOST') or 'http://localhost:8181' +TOKEN = os.getenv('INFLUXDB_TOKEN') or 'my-token' +DATABASE = os.getenv('INFLUXDB_DATABASE') or 'my-db' class BatchingCallback(object): @@ -44,8 +46,8 @@ def retry(self, conf, data: str, exception: InfluxDBError): ) # Opening InfluxDB client with a batch size of 5k points or flush interval # of 10k ms and gzip compression -with InfluxDBClient3(token=config.token, - host=config.host, +with InfluxDBClient3(token=TOKEN, + host=HOST, enable_gzip=True, write_client_options=wco) as _client: query = f"SHOW TAG KEYS FROM {measurement}" diff --git a/Examples/advanced/downsample.py b/Examples/advanced/downsample.py index 9301b76..a5b9d8f 100644 --- a/Examples/advanced/downsample.py +++ b/Examples/advanced/downsample.py @@ -4,15 +4,16 @@ by averaging values across a given time window. """ import datetime +import os import random import pandas as pd from influxdb_client_3 import InfluxDBClient3, InfluxDBError, WriteOptions, write_client_options -from Examples.config import Config - -config = Config() +HOST = os.getenv('INFLUXDB_HOST') or 'http://localhost:8181' +TOKEN = os.getenv('INFLUXDB_TOKEN') or 'my-token' +DATABASE = os.getenv('INFLUXDB_DATABASE') or 'my-db' class BatchingCallback(object): @@ -66,9 +67,9 @@ def retry(self, conf, data: str, exception: InfluxDBError): # use a first client to write the prep data with InfluxDBClient3( - token=config.token, - host=config.host, - database=config.database, + token=TOKEN, + host=HOST, + database=DATABASE, enable_gzip=True, write_client_options=wco) as prep_client: @@ -123,9 +124,9 @@ def retry(self, conf, data: str, exception: InfluxDBError): # Now query just written data and downsample with InfluxDBClient3( - token=config.token, - host=config.host, - database=config.database, enable_gzip=True, write_client_options=wco) as ds_client: + token=TOKEN, + host=HOST, + database=DATABASE, enable_gzip=True, write_client_options=wco) as ds_client: # downsample data to average number of catches per quarter-hour sql = ("SELECT date_bin('15 minutes', \"time\") as window_start, \n" diff --git a/Examples/config.py b/Examples/config.py deleted file mode 100644 index ab4546e..0000000 --- a/Examples/config.py +++ /dev/null @@ -1,12 +0,0 @@ -import os -import json - - -class Config: - def __init__(self): - self.host = os.getenv('INFLUXDB_HOST') or 'http://localhost:8181' - self.token = os.getenv('INFLUXDB_TOKEN') or 'my-token' - self.database = os.getenv('INFLUXDB_DATABASE') or 'my-db' - - def __str__(self): - return json.dumps(self.__dict__) diff --git a/Examples/core/timeouts.py b/Examples/core/timeouts.py index 32b2735..ff10ef4 100644 --- a/Examples/core/timeouts.py +++ b/Examples/core/timeouts.py @@ -16,13 +16,10 @@ import time from influxdb_client_3 import InfluxDBClient3, write_client_options -from Examples.config import Config DEFAULT_WRITE_TIMEOUT = 30_000 # in milliseconds DEFAULT_QUERY_TIMEOUT = 120_000 # in milliseconds -config = Config() - def handle_write_error_cb(rd, rt, rx): print(f"Got a write error: {rd}, {rt}, {rx}") diff --git a/Examples/query/flight_options.py b/Examples/query/flight_options.py index b3d687b..97f709a 100644 --- a/Examples/query/flight_options.py +++ b/Examples/query/flight_options.py @@ -7,20 +7,23 @@ TODO review this one more time """ +import os + from influxdb_client_3 import InfluxDBClient3, flight_client_options -from Examples.config import Config with open("./cert.pem", 'rb') as f: cert = f.read() print(cert) -config = Config() +HOST = os.getenv('INFLUXDB_HOST') or 'http://localhost:8181' +TOKEN = os.getenv('INFLUXDB_TOKEN') or 'my-token' +DATABASE = os.getenv('INFLUXDB_DATABASE') or 'my-db' client = InfluxDBClient3( - token=config.token, - host=config.host, - database=config.database, + token=TOKEN, + host=HOST, + database=DATABASE, flight_client_options=flight_client_options( # Options passed directly to the underlying Arrow flight client tls_root_certs=cert, # Use a non-standard root certificate disable_server_verification=True, # N.B. unsafe - for illustration here diff --git a/Examples/query/handle_query_error.py b/Examples/query/handle_query_error.py index 940adf6..2b1bd1b 100644 --- a/Examples/query/handle_query_error.py +++ b/Examples/query/handle_query_error.py @@ -3,10 +3,10 @@ handle_query_error.py - Is a functional example that demonstrates handling error when querying InfluxDB. """ import logging -from Examples.config import Config -from influxdb_client_3.exceptions import InfluxDB3ClientQueryError +import os -import influxdb_client_3 as InfluxDBClient3 +from influxdb_client_3 import InfluxDBClient3 +from influxdb_client_3.exceptions import InfluxDB3ClientQueryError def main() -> None: @@ -14,13 +14,16 @@ def main() -> None: Main function :return: """ - config = Config() + host = os.getenv('INFLUXDB_HOST') or 'http://localhost:8181' + token = os.getenv('INFLUXDB_TOKEN') or 'my-token' + database = os.getenv('INFLUXDB_DATABASE') or 'my-db' + logging.basicConfig(format='%(asctime)s %(message)s', level=logging.INFO) - client = InfluxDBClient3.InfluxDBClient3( - host=config.host, - token=config.token, - database=config.database + client = InfluxDBClient3( + host=host, + token=token, + database=database ) try: diff --git a/Examples/query/query_async.py b/Examples/query/query_async.py index 1e199ab..8917e90 100644 --- a/Examples/query/query_async.py +++ b/Examples/query/query_async.py @@ -4,6 +4,7 @@ while calculating a Fibonacci series in a parallel coroutine. """ import asyncio +import os import random import time @@ -11,10 +12,8 @@ from influxdb_client_3 import InfluxDBClient3 -from Examples.config import Config - -async def fibio(iterations, grit=0.5): +async def fibo(iterations, grit=0.5): """ example coroutine to run parallel with query_async :param iterations: @@ -72,18 +71,22 @@ async def query_data(client: InfluxDBClient3, measurement): async def main(): - config = Config() + + host = os.getenv('INFLUXDB_HOST') or 'http://localhost:8181' + token = os.getenv('INFLUXDB_TOKEN') or 'my-token' + database = os.getenv('INFLUXDB_DATABASE') or 'my-db' + client = InfluxDBClient3( - host=config.host, - token=config.token, - database=config.database, + host=host, + token=token, + database=database, ) measurement = 'example_uav' write_data(client, measurement) # run both coroutines simultaneously - result = await asyncio.gather(fibio(10, 0.2), query_data(client, measurement)) - print(f"fibio sequence = {result[0]}") + result = await asyncio.gather(fibo(10, 0.2), query_data(client, measurement)) + print(f"fibonacci sequence = {result[0]}") print(f"data set =\n{result[1]}") diff --git a/Examples/query/query_modes.py b/Examples/query/query_modes.py index a35e8be..6cf2385 100644 --- a/Examples/query/query_modes.py +++ b/Examples/query/query_modes.py @@ -2,13 +2,14 @@ """ query_modes.py - is a functional example that shows how to use different modes when executing queries. """ +import os + import pytz import numpy as np from datetime import datetime, timedelta from influxdb_client_3 import InfluxDBClient3, Point -from Examples.config import Config def prep_data(client: InfluxDBClient3, measurement: str): @@ -85,13 +86,19 @@ def query_reader(client: InfluxDBClient3, influxqul_query: str): print(batch.to_pandas()) -def main(config: Config): +def main(): + + host = os.getenv('INFLUXDB_HOST') or 'http://localhost:8181' + token = os.getenv('INFLUXDB_TOKEN') or 'my-token' + database = os.getenv('INFLUXDB_DATABASE') or 'my-db' + measurement = "machine_data" + influxql = f"SELECT * FROM {measurement} WHERE time > now() - 5m" with InfluxDBClient3( - host=config.host, - database=config.database, - token=config.token + host=host, + database=database, + token=token ) as client: prep_data(client, measurement) print("\n=== Querying Chunks ===\n") @@ -107,4 +114,4 @@ def main(config: Config): if __name__ == "__main__": - main(Config()) + main() diff --git a/Examples/query/query_with_middleware.py b/Examples/query/query_with_middleware.py index bb01c3d..0fe8c30 100644 --- a/Examples/query/query_with_middleware.py +++ b/Examples/query/query_with_middleware.py @@ -2,9 +2,10 @@ query_with_middleware.py - is an illustrative example of how to add Arrow Flight middleware when initializing a client. """ +import os + from pyarrow import flight -from Examples.config import Config from influxdb_client_3 import InfluxDBClient3, flight_client_options @@ -24,12 +25,15 @@ def start_call(self, info): return ModifyHeaderClientMiddleware() -config = Config() +HOST = os.getenv('INFLUXDB_HOST') or 'http://localhost:8181' +TOKEN = os.getenv('INFLUXDB_TOKEN') or 'my-token' +DATABASE = os.getenv('INFLUXDB_DATABASE') or 'my-db' + middleware = [ModifyHeaderClientMiddlewareFactory()] client = InfluxDBClient3( - host=config.host, - token=config.token, - database=config.database, + host=HOST, + token=TOKEN, + database=DATABASE, flight_client_options=flight_client_options(middleware=middleware) ) diff --git a/Examples/write/batching.py b/Examples/write/batching.py index ed38d33..5fd9d62 100644 --- a/Examples/write/batching.py +++ b/Examples/write/batching.py @@ -4,6 +4,7 @@ which is the default WriteType when instantiating a WriteOptions object. """ import datetime +import os import random import time @@ -12,8 +13,6 @@ import influxdb_client_3 as InfluxDBClient3 from influxdb_client_3 import write_client_options, WritePrecision, WriteOptions, InfluxDBError -from Examples.config import Config - class BatchingCallback(object): """ @@ -43,7 +42,10 @@ def elapsed(self) -> int: def main() -> None: - conf = Config() + + host = os.getenv('INFLUXDB_HOST') or 'http://localhost:8181' + token = os.getenv('INFLUXDB_TOKEN') or 'my-token' + database = os.getenv('INFLUXDB_DATABASE') or 'my-db' # Creating 5.000 gatewayId values as MongoDB ObjectIDs gatewayIds = [ObjectId() for x in range(0, 100)] @@ -75,9 +77,9 @@ def main() -> None: # Opening InfluxDB client with a batch size of 5k points or flush interval # of 10k ms and gzip compression - with InfluxDBClient3.InfluxDBClient3(token=conf.token, - host=conf.host, - database=conf.database, + with InfluxDBClient3.InfluxDBClient3(token=token, + host=host, + database=database, enable_gzip=True, write_client_options=wco) as _client: # Creating iterator for one hour worth of data (6 sensor readings per diff --git a/Examples/write/fileimport.py b/Examples/write/fileimport.py index 48d859a..3feaddc 100644 --- a/Examples/write/fileimport.py +++ b/Examples/write/fileimport.py @@ -13,7 +13,6 @@ import influxdb_client_3 as InfluxDBClient3 from influxdb_client_3 import write_client_options, WriteOptions, InfluxDBError -from Examples.config import Config dir_path = os.path.dirname(os.path.realpath(__file__)) @@ -38,6 +37,10 @@ def retry(self, conf, data: str, exception: InfluxDBError): def main(file_types=("csv",)) -> None: + host = os.getenv('INFLUXDB_HOST') or 'http://localhost:8181' + token = os.getenv('INFLUXDB_TOKEN') or 'my-token' + database = os.getenv('INFLUXDB_DATABASE') or 'my-db' + # allow detailed inspection if file_types is None: file_types = ["csv"] @@ -59,15 +62,14 @@ def main(file_types=("csv",)) -> None: write_options=write_options ) - config = Config() """ Note: debug: allows low-level inspection of communications and of context-manager termination """ with InfluxDBClient3.InfluxDBClient3( - token=config.token, - host=config.host, - database=config.database, + token=token, + host=host, + database=database, write_client_options=wco, debug=True) as client: diff --git a/Examples/write/handle_http_error.py b/Examples/write/handle_http_error.py index 45cdcc7..2ef28a6 100644 --- a/Examples/write/handle_http_error.py +++ b/Examples/write/handle_http_error.py @@ -3,7 +3,7 @@ handle_http_error.py - is a functional example that demonstrates handling response error headers on error. """ import logging -from Examples.config import Config +import os import influxdb_client_3 as InfluxDBClient3 @@ -13,13 +13,16 @@ def main() -> None: Main function :return: """ - config = Config() + host = os.getenv('INFLUXDB_HOST') or 'http://localhost:8181' + token = os.getenv('INFLUXDB_TOKEN') or 'my-token' + database = os.getenv('INFLUXDB_DATABASE') or 'my-db' + logging.basicConfig(format='%(asctime)s %(message)s', level=logging.INFO) client = InfluxDBClient3.InfluxDBClient3( - host=config.host, - token=config.token, - database=config.database + host=host, + token=token, + database=database ) # write with empty field results in HTTP 400 error diff --git a/Examples/write/pandas_write.py b/Examples/write/pandas_write.py index 07ec4af..5da5a19 100644 --- a/Examples/write/pandas_write.py +++ b/Examples/write/pandas_write.py @@ -3,19 +3,23 @@ pandas_write.py - is a functional example that demonstrates how to write data to Influxdb3 directly from a pandas DataFrame. """ -import influxdb_client_3 as InfluxDBClient3 +import os import pandas as pd import numpy as np -from Examples.config import Config +import influxdb_client_3 as InfluxDBClient3 + +def main(): -def main(config: Config): + host = os.getenv('INFLUXDB_HOST') or 'http://localhost:8181' + token = os.getenv('INFLUXDB_TOKEN') or 'my-token' + database = os.getenv('INFLUXDB_DATABASE') or 'my-db' with InfluxDBClient3.InfluxDBClient3( - token=config.token, - host=config.host, - database=config.database) as client: + token=token, + host=host, + database=database) as client: # Create a dataframe df = pd.DataFrame({'a': [1, 2, 3], 'b': [4, 5, 6]}) @@ -53,4 +57,4 @@ def main(config: Config): if __name__ == '__main__': - main(Config()) + main() diff --git a/Examples/write/writeoptions.py b/Examples/write/writeoptions.py index 7b461ed..7506a68 100644 --- a/Examples/write/writeoptions.py +++ b/Examples/write/writeoptions.py @@ -1,13 +1,13 @@ #!/usr/bin/env python3 """ -`writeoptions.py` is a functional example, except for certain illustrative callbacks, +`writeoptions.py` - is a functional example, except for certain illustrative callbacks, that shows the basic principles of setting up configuration properties for the standard HTTP write client. """ import datetime import logging +import os -from Examples.config import Config from influxdb_client_3 import (exceptions, InfluxDBClient3, Point, WriteOptions, WritePrecision, WriteType, write_client_options) @@ -59,12 +59,16 @@ def success_callback(conf, data: str): measurement = 'wo_caught' -def main(config: Config): +def main(): + + host = os.getenv('INFLUXDB_HOST') or 'http://localhost:8181' + token = os.getenv('INFLUXDB_TOKEN') or 'my-token' + database = os.getenv('INFLUXDB_DATABASE') or 'my-db' with InfluxDBClient3( - token=config.token, - host=config.host, - database=config.database, + token=token, + host=host, + database=database, write_client_options=wco, # write client options get passed to the client instance here. debug=True) as client: @@ -134,4 +138,4 @@ def main(config: Config): if __name__ == "__main__": - main(Config()) + main() From 592efd0e77133a673d6c52088d788f4872f4cd04 Mon Sep 17 00:00:00 2001 From: karel rehor Date: Mon, 27 Apr 2026 13:05:04 +0200 Subject: [PATCH 24/36] chore: refactor ./Examples directory to ./examples and remove __init__.py (decouple from project). --- Examples/write/__init__.py | 0 {Examples => examples}/README.md | 0 {Examples => examples/advanced}/__init__.py | 0 .../advanced/database_transfer.py | 0 {Examples => examples}/advanced/downsample.py | 0 {Examples/advanced => examples/core}/__init__.py | 0 {Examples => examples}/core/basic_query.py | 0 {Examples => examples}/core/basic_ssl.py | 0 {Examples => examples}/core/basic_write.py | 0 {Examples => examples}/core/timeouts.py | 0 {Examples => examples}/install_extra_deps.sh | 0 .../jupyter/basic-write-query.ipynb | 0 {Examples/core => examples/query}/__init__.py | 0 {Examples => examples}/query/flight_options.py | 0 {Examples => examples}/query/handle_query_error.py | 0 {Examples => examples}/query/query_async.py | 0 {Examples => examples}/query/query_modes.py | 0 .../query/query_with_middleware.py | 0 {Examples/query => examples/write}/__init__.py | 0 {Examples => examples}/write/batching.py | 0 {Examples => examples}/write/fileimport.py | 0 {Examples => examples}/write/handle_http_error.py | 0 {Examples => examples}/write/pandas_write.py | 0 {Examples => examples}/write/source_data/out.csv | 0 .../write/source_data/out.feather | Bin {Examples => examples}/write/source_data/out.json | 0 {Examples => examples}/write/source_data/out.orc | Bin .../write/source_data/out.parquet | Bin .../write/source_data/pokemon.csv | 0 {Examples => examples}/write/source_data/updater.py | 0 {Examples => examples}/write/writeoptions.py | 0 31 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 Examples/write/__init__.py rename {Examples => examples}/README.md (100%) rename {Examples => examples/advanced}/__init__.py (100%) rename {Examples => examples}/advanced/database_transfer.py (100%) rename {Examples => examples}/advanced/downsample.py (100%) rename {Examples/advanced => examples/core}/__init__.py (100%) rename {Examples => examples}/core/basic_query.py (100%) rename {Examples => examples}/core/basic_ssl.py (100%) rename {Examples => examples}/core/basic_write.py (100%) rename {Examples => examples}/core/timeouts.py (100%) rename {Examples => examples}/install_extra_deps.sh (100%) rename {Examples => examples}/jupyter/basic-write-query.ipynb (100%) rename {Examples/core => examples/query}/__init__.py (100%) rename {Examples => examples}/query/flight_options.py (100%) rename {Examples => examples}/query/handle_query_error.py (100%) rename {Examples => examples}/query/query_async.py (100%) rename {Examples => examples}/query/query_modes.py (100%) rename {Examples => examples}/query/query_with_middleware.py (100%) rename {Examples/query => examples/write}/__init__.py (100%) rename {Examples => examples}/write/batching.py (100%) rename {Examples => examples}/write/fileimport.py (100%) rename {Examples => examples}/write/handle_http_error.py (100%) rename {Examples => examples}/write/pandas_write.py (100%) rename {Examples => examples}/write/source_data/out.csv (100%) rename {Examples => examples}/write/source_data/out.feather (100%) rename {Examples => examples}/write/source_data/out.json (100%) rename {Examples => examples}/write/source_data/out.orc (100%) rename {Examples => examples}/write/source_data/out.parquet (100%) rename {Examples => examples}/write/source_data/pokemon.csv (100%) rename {Examples => examples}/write/source_data/updater.py (100%) rename {Examples => examples}/write/writeoptions.py (100%) diff --git a/Examples/write/__init__.py b/Examples/write/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/Examples/README.md b/examples/README.md similarity index 100% rename from Examples/README.md rename to examples/README.md diff --git a/Examples/__init__.py b/examples/advanced/__init__.py similarity index 100% rename from Examples/__init__.py rename to examples/advanced/__init__.py diff --git a/Examples/advanced/database_transfer.py b/examples/advanced/database_transfer.py similarity index 100% rename from Examples/advanced/database_transfer.py rename to examples/advanced/database_transfer.py diff --git a/Examples/advanced/downsample.py b/examples/advanced/downsample.py similarity index 100% rename from Examples/advanced/downsample.py rename to examples/advanced/downsample.py diff --git a/Examples/advanced/__init__.py b/examples/core/__init__.py similarity index 100% rename from Examples/advanced/__init__.py rename to examples/core/__init__.py diff --git a/Examples/core/basic_query.py b/examples/core/basic_query.py similarity index 100% rename from Examples/core/basic_query.py rename to examples/core/basic_query.py diff --git a/Examples/core/basic_ssl.py b/examples/core/basic_ssl.py similarity index 100% rename from Examples/core/basic_ssl.py rename to examples/core/basic_ssl.py diff --git a/Examples/core/basic_write.py b/examples/core/basic_write.py similarity index 100% rename from Examples/core/basic_write.py rename to examples/core/basic_write.py diff --git a/Examples/core/timeouts.py b/examples/core/timeouts.py similarity index 100% rename from Examples/core/timeouts.py rename to examples/core/timeouts.py diff --git a/Examples/install_extra_deps.sh b/examples/install_extra_deps.sh similarity index 100% rename from Examples/install_extra_deps.sh rename to examples/install_extra_deps.sh diff --git a/Examples/jupyter/basic-write-query.ipynb b/examples/jupyter/basic-write-query.ipynb similarity index 100% rename from Examples/jupyter/basic-write-query.ipynb rename to examples/jupyter/basic-write-query.ipynb diff --git a/Examples/core/__init__.py b/examples/query/__init__.py similarity index 100% rename from Examples/core/__init__.py rename to examples/query/__init__.py diff --git a/Examples/query/flight_options.py b/examples/query/flight_options.py similarity index 100% rename from Examples/query/flight_options.py rename to examples/query/flight_options.py diff --git a/Examples/query/handle_query_error.py b/examples/query/handle_query_error.py similarity index 100% rename from Examples/query/handle_query_error.py rename to examples/query/handle_query_error.py diff --git a/Examples/query/query_async.py b/examples/query/query_async.py similarity index 100% rename from Examples/query/query_async.py rename to examples/query/query_async.py diff --git a/Examples/query/query_modes.py b/examples/query/query_modes.py similarity index 100% rename from Examples/query/query_modes.py rename to examples/query/query_modes.py diff --git a/Examples/query/query_with_middleware.py b/examples/query/query_with_middleware.py similarity index 100% rename from Examples/query/query_with_middleware.py rename to examples/query/query_with_middleware.py diff --git a/Examples/query/__init__.py b/examples/write/__init__.py similarity index 100% rename from Examples/query/__init__.py rename to examples/write/__init__.py diff --git a/Examples/write/batching.py b/examples/write/batching.py similarity index 100% rename from Examples/write/batching.py rename to examples/write/batching.py diff --git a/Examples/write/fileimport.py b/examples/write/fileimport.py similarity index 100% rename from Examples/write/fileimport.py rename to examples/write/fileimport.py diff --git a/Examples/write/handle_http_error.py b/examples/write/handle_http_error.py similarity index 100% rename from Examples/write/handle_http_error.py rename to examples/write/handle_http_error.py diff --git a/Examples/write/pandas_write.py b/examples/write/pandas_write.py similarity index 100% rename from Examples/write/pandas_write.py rename to examples/write/pandas_write.py diff --git a/Examples/write/source_data/out.csv b/examples/write/source_data/out.csv similarity index 100% rename from Examples/write/source_data/out.csv rename to examples/write/source_data/out.csv diff --git a/Examples/write/source_data/out.feather b/examples/write/source_data/out.feather similarity index 100% rename from Examples/write/source_data/out.feather rename to examples/write/source_data/out.feather diff --git a/Examples/write/source_data/out.json b/examples/write/source_data/out.json similarity index 100% rename from Examples/write/source_data/out.json rename to examples/write/source_data/out.json diff --git a/Examples/write/source_data/out.orc b/examples/write/source_data/out.orc similarity index 100% rename from Examples/write/source_data/out.orc rename to examples/write/source_data/out.orc diff --git a/Examples/write/source_data/out.parquet b/examples/write/source_data/out.parquet similarity index 100% rename from Examples/write/source_data/out.parquet rename to examples/write/source_data/out.parquet diff --git a/Examples/write/source_data/pokemon.csv b/examples/write/source_data/pokemon.csv similarity index 100% rename from Examples/write/source_data/pokemon.csv rename to examples/write/source_data/pokemon.csv diff --git a/Examples/write/source_data/updater.py b/examples/write/source_data/updater.py similarity index 100% rename from Examples/write/source_data/updater.py rename to examples/write/source_data/updater.py diff --git a/Examples/write/writeoptions.py b/examples/write/writeoptions.py similarity index 100% rename from Examples/write/writeoptions.py rename to examples/write/writeoptions.py From a96f5545297cf1036ea8815810d31e03a9a1fbc9 Mon Sep 17 00:00:00 2001 From: karel rehor Date: Mon, 27 Apr 2026 13:12:16 +0200 Subject: [PATCH 25/36] chore: update circleCI flake check to match new examples directory name. --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 406373f..a1a7b58 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -154,7 +154,7 @@ jobs: name: Checks style consistency across examples. command: | pip install flake8 --user - flake8 Examples/ + flake8 examples/ check-twine: docker: - image: << pipeline.parameters.default-python-image >> From 61ea751666790b4057177f620c741ad75297ba70 Mon Sep 17 00:00:00 2001 From: karel rehor Date: Mon, 27 Apr 2026 14:21:15 +0200 Subject: [PATCH 26/36] chore: add examples/prep.py script. --- examples/README.md | 6 ++-- examples/install_extra_deps.sh | 6 ---- examples/prep.py | 53 ++++++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 10 deletions(-) delete mode 100755 examples/install_extra_deps.sh create mode 100644 examples/prep.py diff --git a/examples/README.md b/examples/README.md index 6c1d289..0a2eb9a 100644 --- a/examples/README.md +++ b/examples/README.md @@ -33,14 +33,12 @@ Before running any functional examples, ensure that the Influxdb3-python project $ pip install . ``` -A few of the examples depend on libraries not included in Influxdb3 python. Be sure to install them as well. +A few of the examples depend on libraries not included in Influxdb3 python. The `examples/prep.py` script will install any missing example dependencies and set functional examples as executable. ```bash -$ Examples/install_extra_deps.sh +$ python examples/prep.py ``` -Functional examples come with a shebang header and should run from the commandline once exec permissions are set. - ### Writing data Basic examples can be found in the `Examples/core` directory. diff --git a/examples/install_extra_deps.sh b/examples/install_extra_deps.sh deleted file mode 100755 index 4ccdc69..0000000 --- a/examples/install_extra_deps.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env bash - -# installs packages that are not part of Influxdb3 python -# but required to run some examples - -pip install numpy pandas bson matplotlib diff --git a/examples/prep.py b/examples/prep.py new file mode 100644 index 0000000..1537c45 --- /dev/null +++ b/examples/prep.py @@ -0,0 +1,53 @@ +import os +import subprocess +import sys + +""" +Installs extra packages needed by some examples. + +Sets functional examples with shebang headers as executable. +""" + +extra_packages = [ + 'numpy', + 'pandas', + 'bson', + 'matplotlib', +] + +functional_examples = [ + './core/basic_write.py', + './core/basic_query.py', + './core/basic_ssl.py', + './core/timeouts.py', + './write/batching.py', + './write/fileimport.py', + './write/source_data/updater.py', + './write/handle_http_error.py', + './write/pandas_write.py', + './write/writeoptions.py', + './query/handle_query_error.py', + './query/query_async.py', + './query/query_modes.py', + './advanced/downsample.py' +] + +def set_functional_examples_executable(): + global functional_examples + dir_path = os.path.dirname(os.path.realpath(__file__)) + for example in functional_examples: + if os.path.exists(f"{dir_path}/{example}"): + print(f"Functional example found at {example}") + os.chmod(f"{dir_path}/{example}", 0o775) + + +def install_extra_packages(): + global extra_packages + for package in extra_packages: + subprocess.check_call([sys.executable, "-m", "pip", "install", f"{package}"]) + +print(f"Installing extra packages {extra_packages}") +install_extra_packages() + +print("Setting functional examples executable") +set_functional_examples_executable() From d3b6ba268fd8810f55d863a8004102bec05cce2d Mon Sep 17 00:00:00 2001 From: karel rehor Date: Mon, 27 Apr 2026 14:25:49 +0200 Subject: [PATCH 27/36] chore: tidy examples/prep.py --- examples/prep.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/prep.py b/examples/prep.py index 1537c45..1643e1a 100644 --- a/examples/prep.py +++ b/examples/prep.py @@ -32,20 +32,20 @@ './advanced/downsample.py' ] + def set_functional_examples_executable(): - global functional_examples dir_path = os.path.dirname(os.path.realpath(__file__)) for example in functional_examples: if os.path.exists(f"{dir_path}/{example}"): - print(f"Functional example found at {example}") + print(f"Setting chmod of {example} to 775") os.chmod(f"{dir_path}/{example}", 0o775) def install_extra_packages(): - global extra_packages for package in extra_packages: subprocess.check_call([sys.executable, "-m", "pip", "install", f"{package}"]) + print(f"Installing extra packages {extra_packages}") install_extra_packages() From 4926b9f377f042c0225c305c77c7f08413c85e5e Mon Sep 17 00:00:00 2001 From: karel rehor Date: Mon, 27 Apr 2026 14:57:35 +0200 Subject: [PATCH 28/36] docs: update example/README.md to reflect latest changes. --- examples/README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/examples/README.md b/examples/README.md index 0a2eb9a..4809368 100644 --- a/examples/README.md +++ b/examples/README.md @@ -12,12 +12,14 @@ Some more advanced examples contain only illustrative code that can be reused in __Configuration__ -Functional examples make use of the base `Config` class in `config.py`. User values can be set either directly in this file, or through the following environment variables. +Functional examples make use of the three basic environment variables. * `INFLUXDB_HOST` - host URL to connect to an Influxdb3 database. * `INFLUXDB_DATABASE` - default database to be used with the examples. * `INFLUXDB_TOKEN` - a token associated with read and write permissions to the default database and any additional databases that might be used with the examples. +These need to be set before running any example. + It is recommended to run examples using a python virtual environment. For example... @@ -138,7 +140,7 @@ TODO - delete this section as examples take shape and before creating PR. - Perhaps though should encourage the use of a simpler standard API that hides them 2. Remove dependencies on remote `../githubusercontent/../*.csv` __DONE__ 2. ~~Leverage `config.py` in all examples~~ __DONE__ - 1. TODO undo and use standard ENVARS instead + 1. Undo and use standard ENVARS instead __DONE__ 3. Prefer using Influxdb3 Core by default. But also document possibility of using other products. 4. Add shebangs to functional examples __DONE__ 5. Ensure timestamps are current and not fixed for example to 2023 __DONE__ @@ -172,3 +174,5 @@ TODO - delete this section as examples take shape and before creating PR. | write/handle_http_error.py | F | Yes | Yes | None | Yes | None | None | N.A. | Review - shows write error | | write/pandas_write.py | F | Yes | Yes | None | Yes | Yes (pd) | None | Dynamic | Review - kwargs here are part of example | | write/writeoptions | F | Yes | Yes | None | Yes | None | None | Dynamic / now | Review | + +TODO Review - run any example marked as "Review" in a fresh venv after a fresh project checkout and ensure it executes without issue. \ No newline at end of file From 42d683ee66e0a222c8037e92f8f9f6fc1cbfe247 Mon Sep 17 00:00:00 2001 From: karel rehor Date: Tue, 28 Apr 2026 14:30:41 +0200 Subject: [PATCH 29/36] chore: stifle noise in batching.py example and minor example tweaks --- examples/README.md | 48 ++++++++++++++++++++------------- examples/advanced/downsample.py | 4 ++- examples/prep.py | 1 + examples/write/batching.py | 11 ++++---- 4 files changed, 39 insertions(+), 25 deletions(-) diff --git a/examples/README.md b/examples/README.md index 4809368..2e198d3 100644 --- a/examples/README.md +++ b/examples/README.md @@ -41,6 +41,16 @@ A few of the examples depend on libraries not included in Influxdb3 python. The $ python examples/prep.py ``` +Functional examples can now be executed from the command line. + +e.g. + +```bash +$ examples/core/basic_write.py +First point written to InfluxDB! +Write success: 3 points! +``` + ### Writing data Basic examples can be found in the `Examples/core` directory. @@ -155,24 +165,24 @@ TODO - delete this section as examples take shape and before creating PR. #### Standardization summary (F - functional, I - illustrative) -| example | F/I | Head Comment | shebang | ~~Config()~~ | ENVARS | **kwargs (adv) | github user link | Timestamps | Notes | -|---------------------------------|-----|--------------|---------|--------------|--------|----------------------|------------------|------------------------------|--| -| advanced/database_transfer.py | I | Yes | None | None | Yes | Yes (pd) | None | N.A. - copied without change | Review | -| advanced/downsample.py | F | Yes | Yes | None | Yes | Yes (pd) | None | Dynamic / Current | Review | -| core/basic_write.py | F | Yes | Yes | None | Yes | None | None | Dynamic / Now | Review | -| core/basic_query.py | F | Yes | Yes | None | Yes | None | None | Read only | Review | -| core/basic_ssl.py | F | Yes | Yes | None | Yes | None | None | Dynamic / Now | Review | -| core/timeouts.py | F | Yes | Yes | None | Yes | None | None | Now (implicit) | Review | -| jupyter/basic-write-query.ipynb | F | N/A | N/A | N/A | Yes | None | None | Dynamic | Review | -| query/flight_options.py | I | Yes | None | None | Yes | None | None | Read only | Review | -| query/handle_query_error.py | F | Yes | Yes | None | Yes | None | None | Read only | Review | -| query/query_async.py | F | Yes | Yes | None | Yes | None | None | Dynamic | Review | -| query/query_modes.py | F | Yes | Yes | None | Yes | None | None | Dynamic | Review | -| query/query_with_middleware.py | I | Yes | None | None | Yes | None | None | Read only | Review | -| write/batching.py | F | Yes | Yes | None | Yes | None | None | Dynamic | Review | -| write/fileimport.py | F | Yes | Yes | None | Yes | Yes (`file_write()`) | None | Dynamic | Review - kwargs here are part of example | -| write/handle_http_error.py | F | Yes | Yes | None | Yes | None | None | N.A. | Review - shows write error | -| write/pandas_write.py | F | Yes | Yes | None | Yes | Yes (pd) | None | Dynamic | Review - kwargs here are part of example | -| write/writeoptions | F | Yes | Yes | None | Yes | None | None | Dynamic / now | Review | +| example | F/I | Head Comment | shebang | ~~Config()~~ | ENVARS | **kwargs (adv) | github user link | Timestamps | Notes | +|---------------------------------|-----|--------------|---------|--------------|--------|----------------------|------------------|------------------------------|-----------------------------------------| +| advanced/database_transfer.py | I | Yes | None | None | Yes | Yes (pd) | None | N.A. - copied without change | Ready | +| advanced/downsample.py | F | Yes | Yes | None | Yes | Yes (pd) | None | Dynamic / Current | Ready | +| core/basic_write.py | F | Yes | Yes | None | Yes | None | None | Dynamic / Now | Ready | +| core/basic_query.py | F | Yes | Yes | None | Yes | None | None | Read only | Ready | +| core/basic_ssl.py | F | Yes | Yes | None | Yes | None | None | Dynamic / Now | Ready | +| core/timeouts.py | F | Yes | Yes | None | Yes | None | None | Now (implicit) | Ready | +| jupyter/basic-write-query.ipynb | F | N/A | N/A | N/A | Yes | None | None | Dynamic | Ready | +| query/flight_options.py | I | Yes | None | None | Yes | None | None | Read only | Ready | +| query/handle_query_error.py | F | Yes | Yes | None | Yes | None | None | Read only | Ready | +| query/query_async.py | F | Yes | Yes | None | Yes | None | None | Dynamic | Ready | +| query/query_modes.py | F | Yes | Yes | None | Yes | None | None | Dynamic | Ready | +| query/query_with_middleware.py | I | Yes | None | None | Yes | None | None | Read only | Ready | +| write/batching.py | F | Yes | Yes | None | Yes | None | None | Dynamic | Review | +| write/fileimport.py | F | Yes | Yes | None | Yes | Yes (`file_write()`) | None | Dynamic | Ready - kwargs here are part of example | +| write/handle_http_error.py | F | Yes | Yes | None | Yes | None | None | N.A. | Ready - shows write error | +| write/pandas_write.py | F | Yes | Yes | None | Yes | Yes (pd) | None | Dynamic | Ready - kwargs here are part of example | +| write/writeoptions | F | Yes | Yes | None | Yes | None | None | Dynamic / now | Ready | TODO Review - run any example marked as "Review" in a fresh venv after a fresh project checkout and ensure it executes without issue. \ No newline at end of file diff --git a/examples/advanced/downsample.py b/examples/advanced/downsample.py index a5b9d8f..283888f 100644 --- a/examples/advanced/downsample.py +++ b/examples/advanced/downsample.py @@ -11,6 +11,8 @@ from influxdb_client_3 import InfluxDBClient3, InfluxDBError, WriteOptions, write_client_options +dir_path = os.path.dirname(os.path.realpath(__file__)) + HOST = os.getenv('INFLUXDB_HOST') or 'http://localhost:8181' TOKEN = os.getenv('INFLUXDB_TOKEN') or 'my-token' DATABASE = os.getenv('INFLUXDB_DATABASE') or 'my-db' @@ -53,7 +55,7 @@ def retry(self, conf, data: str, exception: InfluxDBError): # Read the CSV into a DataFrame pokemon_df = pd.read_csv( - "../write/source_data/pokemon.csv" + f"{dir_path}/../write/source_data/pokemon.csv" ) # Creating an empty list to store the original data diff --git a/examples/prep.py b/examples/prep.py index 1643e1a..f815d5c 100644 --- a/examples/prep.py +++ b/examples/prep.py @@ -13,6 +13,7 @@ 'pandas', 'bson', 'matplotlib', + 'pytz' ] functional_examples = [ diff --git a/examples/write/batching.py b/examples/write/batching.py index 5fd9d62..cf769d7 100644 --- a/examples/write/batching.py +++ b/examples/write/batching.py @@ -25,16 +25,16 @@ def __init__(self): self.start = time.time_ns() def success(self, conf, data: str): - print(f"Written batch: {conf}, data: {data}") + print(f"Written batch: {conf}, data: {len(data)} bytes") self.write_count += 1 self.write_status_msg = f"SUCCESS: {self.write_count} writes" def error(self, conf, data: str, exception: InfluxDBError): - print(f"Cannot write batch: {conf}, data: {data} due: {exception}") + print(f"Cannot write batch: {conf}, data: {len(data)} bytes, due_to: {exception}") self.write_status_msg = f"FAILURE - cause: {exception}" def retry(self, conf, data: str, exception: InfluxDBError): - print(f"Retryable error occurs for batch: {conf}, data: {data} retry: {exception}") + print(f"Retryable error occurs for batch: {conf}, data: {len(data)} bytes, retry: {exception}") self.retry_count += 1 def elapsed(self) -> int: @@ -54,10 +54,11 @@ def main() -> None: precision = 2 # Setting timestamp for first sensor reading - sample_window_days = 7 + # sample_window_days = 7 + sample_window_days = 0.25 now = datetime.datetime.now() now = now - datetime.timedelta(days=sample_window_days) - target_sample_count = sample_window_days * 24 * 60 * 6 + target_sample_count = int(sample_window_days * 24 * 60 * 6) callback = BatchingCallback() From e20222ff159cd6fcbacb039c3c0c870daf7e0545 Mon Sep 17 00:00:00 2001 From: karel rehor Date: Tue, 28 Apr 2026 15:14:39 +0200 Subject: [PATCH 30/36] chore: additional minor changes to batching.py example --- examples/README.md | 8 +++----- examples/write/batching.py | 3 +-- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/examples/README.md b/examples/README.md index 2e198d3..19f8663 100644 --- a/examples/README.md +++ b/examples/README.md @@ -128,8 +128,8 @@ TODO - delete this section as examples take shape and before creating PR. 5. Root examples 1. `basic_ssl_examle.py` to `./core` __DONE__ 2. `batching_example.py` to `./write` __DONE__ - 1. Issue - getting interpreter shutdown before example ends. TODO - fix - 2. Issue - runs very slow - TODO speed up. + 1. Issue - getting interpreter shutdown before example ends. - fix, reduce data set size __DONE__ + 2. Issue - runs very slow - fix, stop printing full data set with every write. __DONE__ 3. `cloud_dedicated_query.py` - is it necessary to have specific _cloud_dedicated_ examples? Can this simply be documented in `README.md` or in code comments? (Removed __DONE__) 4. `cloud_dedicated_write.py` - is it necessary to have specific _cloud_dedicated_ examples? Can this simply be documented in `README.md` or in code comments? (Removed __DONE__) 5. `config.py` - universal configuration file. Keep as is. @@ -179,10 +179,8 @@ TODO - delete this section as examples take shape and before creating PR. | query/query_async.py | F | Yes | Yes | None | Yes | None | None | Dynamic | Ready | | query/query_modes.py | F | Yes | Yes | None | Yes | None | None | Dynamic | Ready | | query/query_with_middleware.py | I | Yes | None | None | Yes | None | None | Read only | Ready | -| write/batching.py | F | Yes | Yes | None | Yes | None | None | Dynamic | Review | +| write/batching.py | F | Yes | Yes | None | Yes | None | None | Dynamic | Ready | | write/fileimport.py | F | Yes | Yes | None | Yes | Yes (`file_write()`) | None | Dynamic | Ready - kwargs here are part of example | | write/handle_http_error.py | F | Yes | Yes | None | Yes | None | None | N.A. | Ready - shows write error | | write/pandas_write.py | F | Yes | Yes | None | Yes | Yes (pd) | None | Dynamic | Ready - kwargs here are part of example | | write/writeoptions | F | Yes | Yes | None | Yes | None | None | Dynamic / now | Ready | - -TODO Review - run any example marked as "Review" in a fresh venv after a fresh project checkout and ensure it executes without issue. \ No newline at end of file diff --git a/examples/write/batching.py b/examples/write/batching.py index cf769d7..d74de82 100644 --- a/examples/write/batching.py +++ b/examples/write/batching.py @@ -54,9 +54,8 @@ def main() -> None: precision = 2 # Setting timestamp for first sensor reading - # sample_window_days = 7 sample_window_days = 0.25 - now = datetime.datetime.now() + now = datetime.datetime.now(datetime.timezone.utc) now = now - datetime.timedelta(days=sample_window_days) target_sample_count = int(sample_window_days * 24 * 60 * 6) From 93a6e85fbac5a5c1e5941e1e7ea4313c7ef66775 Mon Sep 17 00:00:00 2001 From: karel rehor Date: Tue, 28 Apr 2026 16:15:21 +0200 Subject: [PATCH 31/36] docs: remove refactoring notes section from README.md --- examples/README.md | 96 ---------------------------------------------- 1 file changed, 96 deletions(-) diff --git a/examples/README.md b/examples/README.md index 19f8663..1ffb855 100644 --- a/examples/README.md +++ b/examples/README.md @@ -88,99 +88,3 @@ Richer examples can be found in the `Examples/query` directory. * `database_transfer.py` - illustrates writing datapoints from one database to another. * `downsample.py` - shows how to read data from one measurement, reduce it to a smaller data set, and then write the new data set as a new measurement. - -## Refactoring Notes -TODO - delete this section as examples take shape and before creating PR. - -1. Want to remove `pokemon-trainer` and refactor examples to `core`, `write` and `query` - 1. `basic-write-errorhandling.py` can be removed. __DONE__ - - actually shows error handling for query - - duplicates examples `handle_http_error.py` and `handle_query_error.py` - 2. `basic-write-writeoptions.py` to `./write` refactored __DONE__ - 3. `pandas-write.py` can be removed - duplicates `./pandas_write.py` __DONE__ - 4. `write-batching.py` can be removed - duplicates `./batching_example.py` __DONE__ - 5. `write-batching-flight-call-options.py` to be removed __DONE__ - - This is an odd example. Sets write options then performs only a query. Also _flight_ applies only to query API. - - apparently depends on other examples - - write and query options are illustrated in other examples - 6. `cookbook.ipynb` - most of this is migrated to `./jupyter/basic-write-query.ipynb` - can be removed __DONE__ - - what to do about step _write table to parquet file_? - not necessary __DONE__. -2. Keep `file-import` as single file, with source data updatable - see `updater.py` __DONE__ -3. Keep one simple example of jupiter notebook. __DONE__ -4. Decide what to do with `./community` examples. - * `custom_url.py` __DONE__ - * Doesn't seem to do what is on the tin. - * Seems more like a _down sampling_ example - * ~~Doesn't query Influxdb directly but down samples from remote CSV~~ - * review/enhance __DONE__ - * rename to _down sampling_ or similar __DONE__ - * use initial query __DONE__ - * move to `./advanced` __DONE__ - * remove dependency on `githubusercontent` __DONE__ - * `database_transfer.py` - * Simply copies data from one bucket to another - * Useful base example - * Current illustrative only - doesn't look functional without some undocumented setup - * To work requires `dbfrom` and measurement `airSensors` to exist. - * review __DONE__ - * move to `./advanced` __DONE__ - * ~~make functional~~ kept as _illustrative_ - `advanced/downsample.py` is similar _and_ functional -5. Root examples - 1. `basic_ssl_examle.py` to `./core` __DONE__ - 2. `batching_example.py` to `./write` __DONE__ - 1. Issue - getting interpreter shutdown before example ends. - fix, reduce data set size __DONE__ - 2. Issue - runs very slow - fix, stop printing full data set with every write. __DONE__ - 3. `cloud_dedicated_query.py` - is it necessary to have specific _cloud_dedicated_ examples? Can this simply be documented in `README.md` or in code comments? (Removed __DONE__) - 4. `cloud_dedicated_write.py` - is it necessary to have specific _cloud_dedicated_ examples? Can this simply be documented in `README.md` or in code comments? (Removed __DONE__) - 5. `config.py` - universal configuration file. Keep as is. - 6. `example.csv` - where is this used? It doesn't seem to be used in any example... ??? (Removed __DONE__) - 7. `flight_options_example.py` - to `./query` __DONE__ - - ~~Note only option illustrated is tls certificate~~. - - after move - either enrich/refactor or verify this isn't covered elsewhere __DONE__ - 8. `handle_http_error.py` - to `./write` __DONE__ - 9. `handle_query_error.py` - to `./query` __DONE__ - 10. `pandas_write.py` - to `./write` __DONE__ - 11. `query_async.py` - to `./query` __DONE__ - 12. `query_type.py` - rename to `query_modes.py`. Move to `./query` __DONE__ - 13. `query_with_middleware.py` - to `./query` __DONE__ - 14. `timeouts.py` - to `./core` __DONE__ -6. Standardization - 1. Some examples show leveraging internal **kwargs like `data_frame_measurement_name` or `data_frame_tag_columns` __DONE__ - - Makes sense to expose these in _advanced_ examples. (e.g. pandas examples... ) - - Perhaps though should encourage the use of a simpler standard API that hides them - 2. Remove dependencies on remote `../githubusercontent/../*.csv` __DONE__ - 2. ~~Leverage `config.py` in all examples~~ __DONE__ - 1. Undo and use standard ENVARS instead __DONE__ - 3. Prefer using Influxdb3 Core by default. But also document possibility of using other products. - 4. Add shebangs to functional examples __DONE__ - 5. Ensure timestamps are current and not fixed for example to 2023 __DONE__ -7. Enhancements - 1. `writeoptions.py` - does not show much in the way of setting options. (Update and revision - __DONE__) - 2. `basic_ssl.py` - review. Seems to only show handling SSL handshake failures. (Reviewed and updated - __DONE__) - 3. `write_pandas.py` - has fixed dates from 2023, make dynamic with current. __DONE__ - 3. `flight_options.py` - review. This example is nearly three years old, and flight/query options has been greatly enhanced since then. __DONE__ -8. Verify all refactored examples are working - *. __NOTE__ - If making an _illustrative_ example functional _out-of-the-box_ leads to too much distractive information being added, leave the example as _illustrative only_ and add a comment that it is for purposes of illustration. However, make sure the illustrative example is still working in a concrete implementation. (e.g. `query_with_middleware.py`) - *. In comments, mark examples as either _illustrative_ or _functional_ __DONE__ - -#### Standardization summary (F - functional, I - illustrative) - -| example | F/I | Head Comment | shebang | ~~Config()~~ | ENVARS | **kwargs (adv) | github user link | Timestamps | Notes | -|---------------------------------|-----|--------------|---------|--------------|--------|----------------------|------------------|------------------------------|-----------------------------------------| -| advanced/database_transfer.py | I | Yes | None | None | Yes | Yes (pd) | None | N.A. - copied without change | Ready | -| advanced/downsample.py | F | Yes | Yes | None | Yes | Yes (pd) | None | Dynamic / Current | Ready | -| core/basic_write.py | F | Yes | Yes | None | Yes | None | None | Dynamic / Now | Ready | -| core/basic_query.py | F | Yes | Yes | None | Yes | None | None | Read only | Ready | -| core/basic_ssl.py | F | Yes | Yes | None | Yes | None | None | Dynamic / Now | Ready | -| core/timeouts.py | F | Yes | Yes | None | Yes | None | None | Now (implicit) | Ready | -| jupyter/basic-write-query.ipynb | F | N/A | N/A | N/A | Yes | None | None | Dynamic | Ready | -| query/flight_options.py | I | Yes | None | None | Yes | None | None | Read only | Ready | -| query/handle_query_error.py | F | Yes | Yes | None | Yes | None | None | Read only | Ready | -| query/query_async.py | F | Yes | Yes | None | Yes | None | None | Dynamic | Ready | -| query/query_modes.py | F | Yes | Yes | None | Yes | None | None | Dynamic | Ready | -| query/query_with_middleware.py | I | Yes | None | None | Yes | None | None | Read only | Ready | -| write/batching.py | F | Yes | Yes | None | Yes | None | None | Dynamic | Ready | -| write/fileimport.py | F | Yes | Yes | None | Yes | Yes (`file_write()`) | None | Dynamic | Ready - kwargs here are part of example | -| write/handle_http_error.py | F | Yes | Yes | None | Yes | None | None | N.A. | Ready - shows write error | -| write/pandas_write.py | F | Yes | Yes | None | Yes | Yes (pd) | None | Dynamic | Ready - kwargs here are part of example | -| write/writeoptions | F | Yes | Yes | None | Yes | None | None | Dynamic / now | Ready | From 8ef93c774ba209aa2b009d214064906d3f37bd61 Mon Sep 17 00:00:00 2001 From: karel rehor Date: Tue, 28 Apr 2026 16:30:01 +0200 Subject: [PATCH 32/36] docs: proofread examples/README.md --- examples/README.md | 30 ++++++++++++++++-------------- examples/advanced/__init__.py | 0 examples/core/__init__.py | 0 examples/query/__init__.py | 0 examples/write/__init__.py | 0 5 files changed, 16 insertions(+), 14 deletions(-) delete mode 100644 examples/advanced/__init__.py delete mode 100644 examples/core/__init__.py delete mode 100644 examples/query/__init__.py delete mode 100644 examples/write/__init__.py diff --git a/examples/README.md b/examples/README.md index 1ffb855..a7b7d82 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,12 +1,12 @@ -## Infludb3 Python Examples +## InfluxDB 3 Python Examples First time users will likely want to study the examples in the `./core` directory. Users who work with __Jupyter__ notebooks may want to take a look at `basic-write-query.ipynb` in the `./jupyter` directory. ### Underlying principles -Influxdb3 uses two transports: one for writing data and another for querying. For __writes__ a standard _HTTP REST_ style client is used internally. __Queries__ on the other hand make use of an HTTP/2.0 and a _[gRPC](https://grpc.io/docs/what-is-grpc/introduction/)_ compliant client under _[Apache Arrow Flight](https://arrow.apache.org/cookbook/py/flight.html)_. +InfluxDB 3 uses two transports: one for writing data and another for querying. For __writes__ a standard _HTTP REST_ style client is used internally. __Queries__ on the other hand make use of an HTTP/2.0 and a _[gRPC](https://grpc.io/docs/what-is-grpc/introduction/)_ compliant client under _[Apache Arrow Flight](https://arrow.apache.org/cookbook/py/flight.html)_. -Most of the examples found here are functional and should be runnable against an Influxdb3 database, whether in the cloud or locally using for example Influxdb3 Core. They have been revised and tested against the Influxdb3 Core product. +Most of the examples found here are functional and should be runnable against an InfluxDB 3 database whether in the cloud or locally using for example InfluxDB 3 Core. They have been revised and tested against the InfluxDB 3 Core product. Some more advanced examples contain only illustrative code that can be reused in your license compliant applications. Whether an example is intended to be simply _illustrative_ or _functional_ is noted in comments at the start of each example file. @@ -14,7 +14,7 @@ __Configuration__ Functional examples make use of the three basic environment variables. -* `INFLUXDB_HOST` - host URL to connect to an Influxdb3 database. +* `INFLUXDB_HOST` - host URL to connect to an InfluxDB 3 database. * `INFLUXDB_DATABASE` - default database to be used with the examples. * `INFLUXDB_TOKEN` - a token associated with read and write permissions to the default database and any additional databases that might be used with the examples. @@ -29,13 +29,13 @@ $ python -m venv venv $ source ./venv/bin/activate ``` -Before running any functional examples, ensure that the Influxdb3-python project is installed. From the `influxdb3-python` project root run... +Before running any functional examples, ensure that the influxdb3-python project is installed. From the `influxdb3-python` project root run... ```bash $ pip install . ``` -A few of the examples depend on libraries not included in Influxdb3 python. The `examples/prep.py` script will install any missing example dependencies and set functional examples as executable. +A few of the examples depend on libraries not included in influxdb3-python. The `examples/prep.py` script will install any missing example dependencies and set functional examples as executable. ```bash $ python examples/prep.py @@ -53,30 +53,30 @@ Write success: 3 points! ### Writing data -Basic examples can be found in the `Examples/core` directory. +Basic examples can be found in the `./examples/core` directory. * `basic_write.py` - shows the essentials of using the `Point` class and making simple writes. * `basic_ssl.py` - shows how to handle special SSL/TLS situations. * `timeouts.py` - shows how to set and leverage timeout values. -Richer examples can be found in the `Examples/write` directory. +Richer examples can be found in the `./examples/write` directory. * `batching.py` - shows how to make use of the _batching_ API for writing long-running data sets. - * `fileimport.py` - shows how to import data to an Influx database directly from a number of other standard database formats. - * To refresh the source data used in the example, please run `Examples/write/source_data/updater.py` beforehand. + * `fileimport.py` - shows how to import data to an InfluxDB 3 database directly from a number of other standard database formats. + * _Note_ - To refresh the source data used in the example, please run `./examples/write/source_data/updater.py` beforehand. * `handle_http_error.py` - shows error handling on writes. - * `pandas_write.py` - shows how to write pandas dataframes directly to an Influx database. + * `pandas_write.py` - shows how to write pandas dataframes directly to an InfluxDB 3 database. * `writeoptions.py` - shows the core options API for writes. ### Querying data -Basic examples can be found in the `Examples/core` directory. +Basic examples can be found in the `./examples/core` directory. - * `basic_query.py` - shows the essentials of querying and Influxdb database. + * `basic_query.py` - shows the essentials of querying an InfluxDB 3 database. * `basic_ssl.py` - shows how to handle special SSL/TLS situations. * `timeouts.py` - shows how to set and leverage timeout values. -Richer examples can be found in the `Examples/query` directory. +Richer examples can be found in the `./examples/query` directory. * `flight_options.py` - shows how to set options on the query transport. * `handle_query_error.py` - shows basic error handling. @@ -86,5 +86,7 @@ Richer examples can be found in the `Examples/query` directory. ### Advanced examples +These can be found in `./examples/advanced` + * `database_transfer.py` - illustrates writing datapoints from one database to another. * `downsample.py` - shows how to read data from one measurement, reduce it to a smaller data set, and then write the new data set as a new measurement. diff --git a/examples/advanced/__init__.py b/examples/advanced/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/examples/core/__init__.py b/examples/core/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/examples/query/__init__.py b/examples/query/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/examples/write/__init__.py b/examples/write/__init__.py deleted file mode 100644 index e69de29..0000000 From 9a7e6643ea206fee6bfb2947cadb5a85ea053f72 Mon Sep 17 00:00:00 2001 From: karel rehor Date: Tue, 28 Apr 2026 17:25:03 +0200 Subject: [PATCH 33/36] chore: walk subdirs in examples/prep.py method. --- examples/prep.py | 31 ++++++++++--------------------- 1 file changed, 10 insertions(+), 21 deletions(-) diff --git a/examples/prep.py b/examples/prep.py index f815d5c..b717dfb 100644 --- a/examples/prep.py +++ b/examples/prep.py @@ -16,30 +16,19 @@ 'pytz' ] -functional_examples = [ - './core/basic_write.py', - './core/basic_query.py', - './core/basic_ssl.py', - './core/timeouts.py', - './write/batching.py', - './write/fileimport.py', - './write/source_data/updater.py', - './write/handle_http_error.py', - './write/pandas_write.py', - './write/writeoptions.py', - './query/handle_query_error.py', - './query/query_async.py', - './query/query_modes.py', - './advanced/downsample.py' -] - def set_functional_examples_executable(): dir_path = os.path.dirname(os.path.realpath(__file__)) - for example in functional_examples: - if os.path.exists(f"{dir_path}/{example}"): - print(f"Setting chmod of {example} to 775") - os.chmod(f"{dir_path}/{example}", 0o775) + for root, dirs, files in os.walk(dir_path): + root.split(os.sep) + for file in files: + with (open(os.path.join(root, file), "r")) as input_file: + try: + head = [next(input_file) for _ in range(1)] + if head[0].startswith("#!/"): + os.chmod(input_file.name, 0o775) + except UnicodeDecodeError: + continue def install_extra_packages(): From cb9600daf21da98a585f484eb4b5015aaf5558d4 Mon Sep 17 00:00:00 2001 From: karel rehor Date: Wed, 29 Apr 2026 13:38:52 +0200 Subject: [PATCH 34/36] chore: fix typos, misnamed vars, and similar --- examples/core/basic_query.py | 2 +- examples/query/flight_options.py | 5 ++--- examples/query/query_modes.py | 4 ++-- examples/write/fileimport.py | 12 ++++++------ examples/write/source_data/updater.py | 6 +++--- examples/write/writeoptions.py | 4 ++-- 6 files changed, 16 insertions(+), 17 deletions(-) diff --git a/examples/core/basic_query.py b/examples/core/basic_query.py index ddbdb02..a91a8b4 100644 --- a/examples/core/basic_query.py +++ b/examples/core/basic_query.py @@ -20,7 +20,7 @@ measurement = "basic_caught" -print("Quering with sql to Arrow Table") +print("Querying with sql to Arrow Table") sql = f'''SELECT * FROM {measurement} WHERE trainer = 'ash' AND time >= now() - interval '1 hour' LIMIT 5''' table = client.query(query=sql, language='sql', mode='all') print(table) diff --git a/examples/query/flight_options.py b/examples/query/flight_options.py index 97f709a..cc6b3ee 100644 --- a/examples/query/flight_options.py +++ b/examples/query/flight_options.py @@ -4,15 +4,14 @@ Note that the query transport of Influxdb3 is built on top of Arrow Flight, so `flight_client_options` in a broad sense is synonymous with setting low level "Query API Options". - -TODO review this one more time """ import os from influxdb_client_3 import InfluxDBClient3, flight_client_options +dir_path = os.path.dirname(os.path.realpath(__file__)) -with open("./cert.pem", 'rb') as f: +with open(f"${dir_path}/cert.pem", 'rb') as f: cert = f.read() print(cert) diff --git a/examples/query/query_modes.py b/examples/query/query_modes.py index 6cf2385..4b8674a 100644 --- a/examples/query/query_modes.py +++ b/examples/query/query_modes.py @@ -75,10 +75,10 @@ def query_schema(client: InfluxDBClient3, influxql_query: str): print(table) -def query_reader(client: InfluxDBClient3, influxqul_query: str): +def query_reader(client: InfluxDBClient3, influxql_query: str): # Convert this reader into a regular RecordBatchReader reader = client.query( - query=influxqul_query, + query=influxql_query, language="influxql", mode="reader") print("reader:") diff --git a/examples/write/fileimport.py b/examples/write/fileimport.py index 3feaddc..69ed0be 100644 --- a/examples/write/fileimport.py +++ b/examples/write/fileimport.py @@ -3,7 +3,7 @@ fileimport.py - is a functional example that shows how to import data directly to Influxdb3 from other common database types. -The template databases used for this example can be found in `Examples/write/source_data`. To create +The template databases used for this example can be found in `examples/write/source_data`. To create fresh databases with current timestamps please run the helper file `Examples/write/source_data/updater.py` before running fileimport.py. """ @@ -24,15 +24,15 @@ class BatchingCallback(object): def __init__(self): self.write_count = 0 - def success(self, conf, data: str): + def success(self, conf, data: bytes): self.write_count += 1 - print(f"Written batch: {conf}, data: {data}") + print(f"Written batch: {conf}, data: {bytes(data)} bytes") - def error(self, conf, data: str, exception: InfluxDBError): + def error(self, conf, data: bytes, exception: InfluxDBError): print(f"Cannot write batch: {conf}, data: {data} due: {exception}") - def retry(self, conf, data: str, exception: InfluxDBError): - print(f"Retryable error occurs for batch: {conf}, data: {data} retry: {exception}") + def retry(self, conf, data: bytes, exception: InfluxDBError): + print(f"Retryable error occurred for batch: {conf}, data: {bytes(data)} bytes, retry: {exception}") def main(file_types=("csv",)) -> None: diff --git a/examples/write/source_data/updater.py b/examples/write/source_data/updater.py index b49f7a6..d033b63 100644 --- a/examples/write/source_data/updater.py +++ b/examples/write/source_data/updater.py @@ -9,17 +9,17 @@ dir_path = os.path.dirname(os.path.realpath(__file__)) -def update_measurement_name(source: pd.DataFrame, measurment_name: str): +def update_measurement_name(source: pd.DataFrame, measurement_name: str): count = 0 for _ in source["iox::measurement"]: - source.loc[count, "iox::measurement"] = measurment_name + source.loc[count, "iox::measurement"] = measurement_name count += 1 def update_timestamps(source: pd.DataFrame): now = time.time_ns() - interval = 333_000_000 # ms + interval = 333_000_000 # ns grit = random.randrange(0, 1_000_000) interval = interval + grit current = np.int64(now - interval * len(source["time"])) diff --git a/examples/write/writeoptions.py b/examples/write/writeoptions.py index 7506a68..4b37fae 100644 --- a/examples/write/writeoptions.py +++ b/examples/write/writeoptions.py @@ -15,7 +15,7 @@ # An illustrative callback - see below -def error_callback(conf, data: str, exception: exceptions.InfluxDBError): +def error_callback(conf, data: bytes, exception: exceptions.InfluxDBError): now = datetime.datetime.now() logger.warning(f"[{now}] an error occurred on latest write: {exception}") logger.warning(f" conf: {conf}") @@ -23,7 +23,7 @@ def error_callback(conf, data: str, exception: exceptions.InfluxDBError): # An illustrative callback - see below -def success_callback(conf, data: str): +def success_callback(conf, data: bytes): now = datetime.datetime.now() logger.info(f"[{now}] data written: {len(bytes(data, 'utf-8'))} bytes") logger.debug(f" conf: {conf}") From 96dc60cdb518f0b5e344312ca80acab495e698e6 Mon Sep 17 00:00:00 2001 From: karel rehor Date: Wed, 29 Apr 2026 13:56:35 +0200 Subject: [PATCH 35/36] chore: fix f_string escape. --- examples/query/flight_options.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/query/flight_options.py b/examples/query/flight_options.py index cc6b3ee..a12a75c 100644 --- a/examples/query/flight_options.py +++ b/examples/query/flight_options.py @@ -11,7 +11,7 @@ dir_path = os.path.dirname(os.path.realpath(__file__)) -with open(f"${dir_path}/cert.pem", 'rb') as f: +with open(f"{dir_path}/cert.pem", 'rb') as f: cert = f.read() print(cert) From 270d786781374d9ca4def2def22a849d7985418a Mon Sep 17 00:00:00 2001 From: karel rehor Date: Wed, 29 Apr 2026 14:15:32 +0200 Subject: [PATCH 36/36] chore: protect prep.py with main() and minor fixes --- examples/prep.py | 13 +++++++++---- examples/write/fileimport.py | 2 +- examples/write/writeoptions.py | 2 +- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/examples/prep.py b/examples/prep.py index b717dfb..6dbbac6 100644 --- a/examples/prep.py +++ b/examples/prep.py @@ -36,8 +36,13 @@ def install_extra_packages(): subprocess.check_call([sys.executable, "-m", "pip", "install", f"{package}"]) -print(f"Installing extra packages {extra_packages}") -install_extra_packages() +def main(): + print(f"Installing extra packages {extra_packages}") + install_extra_packages() -print("Setting functional examples executable") -set_functional_examples_executable() + print("Setting functional examples executable") + set_functional_examples_executable() + + +if __name__ == "__main__": + main() diff --git a/examples/write/fileimport.py b/examples/write/fileimport.py index 69ed0be..bb7b405 100644 --- a/examples/write/fileimport.py +++ b/examples/write/fileimport.py @@ -4,7 +4,7 @@ from other common database types. The template databases used for this example can be found in `examples/write/source_data`. To create -fresh databases with current timestamps please run the helper file `Examples/write/source_data/updater.py` +fresh databases with current timestamps please run the helper file `examples/write/source_data/updater.py` before running fileimport.py. """ import logging diff --git a/examples/write/writeoptions.py b/examples/write/writeoptions.py index 4b37fae..77cb911 100644 --- a/examples/write/writeoptions.py +++ b/examples/write/writeoptions.py @@ -25,7 +25,7 @@ def error_callback(conf, data: bytes, exception: exceptions.InfluxDBError): # An illustrative callback - see below def success_callback(conf, data: bytes): now = datetime.datetime.now() - logger.info(f"[{now}] data written: {len(bytes(data, 'utf-8'))} bytes") + logger.info(f"[{now}] data written: {len(bytes(data))} bytes") logger.debug(f" conf: {conf}")