async-httpd-data-collector 0.2__tar.gz → 0.2.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (24) hide show
  1. {async_httpd_data_collector-0.2 → async_httpd_data_collector-0.2.1}/.github/workflows/publish.yml +7 -0
  2. {async_httpd_data_collector-0.2 → async_httpd_data_collector-0.2.1}/PKG-INFO +1 -1
  3. async_httpd_data_collector-0.2.1/ahttpdc/__init__.py +1 -0
  4. {async_httpd_data_collector-0.2 → async_httpd_data_collector-0.2.1}/ahttpdc/reads/fetch/async_fetch.py +0 -1
  5. {async_httpd_data_collector-0.2 → async_httpd_data_collector-0.2.1}/ahttpdc/reads/interface.py +1 -4
  6. {async_httpd_data_collector-0.2 → async_httpd_data_collector-0.2.1}/ahttpdc/reads/query/async_query.py +53 -26
  7. async_httpd_data_collector-0.2.1/examples/query.py +42 -0
  8. {async_httpd_data_collector-0.2 → async_httpd_data_collector-0.2.1}/tests/reads/test_interface.py +3 -6
  9. async_httpd_data_collector-0.2/ahttpdc/__init__.py +0 -1
  10. {async_httpd_data_collector-0.2 → async_httpd_data_collector-0.2.1}/.gitignore +0 -0
  11. {async_httpd_data_collector-0.2 → async_httpd_data_collector-0.2.1}/LICENSE +0 -0
  12. {async_httpd_data_collector-0.2 → async_httpd_data_collector-0.2.1}/README.md +0 -0
  13. {async_httpd_data_collector-0.2 → async_httpd_data_collector-0.2.1}/ahttpdc/reads/__init__.py +0 -0
  14. {async_httpd_data_collector-0.2 → async_httpd_data_collector-0.2.1}/ahttpdc/reads/fetch/__init__.py +0 -0
  15. {async_httpd_data_collector-0.2 → async_httpd_data_collector-0.2.1}/ahttpdc/reads/query/__init__.py +0 -0
  16. /async_httpd_data_collector-0.2/examples/minimal-example.py → /async_httpd_data_collector-0.2.1/examples/fetch.py +0 -0
  17. /async_httpd_data_collector-0.2/examples/dev_dashboard.py → /async_httpd_data_collector-0.2.1/examples/minimal_dashboard.py +0 -0
  18. {async_httpd_data_collector-0.2 → async_httpd_data_collector-0.2.1}/influx +0 -0
  19. {async_httpd_data_collector-0.2 → async_httpd_data_collector-0.2.1}/influxdb2-client-2.7.5-linux-amd64.tar.gz +0 -0
  20. {async_httpd_data_collector-0.2 → async_httpd_data_collector-0.2.1}/pyproject.toml +0 -0
  21. {async_httpd_data_collector-0.2 → async_httpd_data_collector-0.2.1}/requirements.txt +0 -0
  22. {async_httpd_data_collector-0.2 → async_httpd_data_collector-0.2.1}/tests/dev_server.py +0 -0
  23. {async_httpd_data_collector-0.2 → async_httpd_data_collector-0.2.1}/tests/reads/fetch/test_fetcher.py +0 -0
  24. {async_httpd_data_collector-0.2 → async_httpd_data_collector-0.2.1}/tests/reads/query/test_query.py +0 -0
@@ -114,3 +114,10 @@ jobs:
114
114
  TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }}
115
115
  run: |
116
116
  python3 -m twine upload dist/*
117
+
118
+ - name: Release
119
+ uses: softprops/action-gh-release@v2
120
+ if: startsWith(github.ref, 'refs/tags/')
121
+ with:
122
+ files: |
123
+ dist/*
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: async-httpd-data-collector
3
- Version: 0.2
3
+ Version: 0.2.1
4
4
  Summary: Gateway facilitating asyncronous communication between sensory data-emitting devices, InfluxDB and the user.
5
5
  Project-URL: Repository, https://github.com/straightchlorine/async-httpd-data-collector
6
6
  Project-URL: Issues, https://github.com/straightchlorine/async-httpd-data-collector/issues
@@ -0,0 +1 @@
1
+ __version__ = '0.2.1'
@@ -135,7 +135,6 @@ class AsyncReadFetcher:
135
135
  records (dict): The sensor readings as records for InfluxDB.
136
136
  """
137
137
 
138
- print('<.> writing new read into database...')
139
138
  write_api = client.write_api()
140
139
  point = Point.from_dict(record, write_precision='ns')
141
140
  await write_api.write(
@@ -133,13 +133,12 @@ class DatabaseInterface:
133
133
  pd.DataFrame: The latest measurement.
134
134
  """
135
135
 
136
- print('<.> querying latest measurement...')
137
136
  query_task = asyncio.create_task(self.query_interface.latest())
138
137
  await query_task
139
138
  result = query_task.result()
140
139
  return result
141
140
 
142
- async def query_historical(self, start: str, end: str) -> pd.DataFrame:
141
+ async def query_historical(self, start: str, end: str = '') -> pd.DataFrame:
143
142
  """
144
143
  Query historical data from the database.
145
144
 
@@ -151,7 +150,6 @@ class DatabaseInterface:
151
150
  pd.DataFrame: Historical data within the specified time range.
152
151
  """
153
152
 
154
- print('<.> querying historical data...')
155
153
  query_task = asyncio.create_task(
156
154
  self.query_interface.historical_data(start, end)
157
155
  )
@@ -170,7 +168,6 @@ class DatabaseInterface:
170
168
  pd.DataFrame: The result of the custom query.
171
169
  """
172
170
 
173
- print('<.> custom query...')
174
171
  query_task = asyncio.create_task(self.query_interface.query(query))
175
172
  await query_task
176
173
  result = query_task.result()
@@ -8,6 +8,7 @@ from datetime import datetime, timedelta
8
8
 
9
9
  from influxdb_client.client.exceptions import InfluxDBError
10
10
  from influxdb_client.client.influxdb_client_async import InfluxDBClientAsync
11
+ from influxdb_client import InfluxDBClient
11
12
  import pandas as pd
12
13
 
13
14
  __all__ = ['AsyncQuery']
@@ -96,13 +97,14 @@ class AsyncQuery:
96
97
 
97
98
  def _into_dataframe(self, tables) -> pd.DataFrame:
98
99
  """
99
- Turns the tables into a pandas DataFrame.
100
+ Turns the tables into a pandas DataFrame with time of measurement as
101
+ as index.
102
+
100
103
  Args:
101
104
  tables (list): The tables to turn into a DataFrame.
102
105
  Returns:
103
- pd.DataFrame: procured measurements as a DataFrame.
106
+ pd.DataFrame: procured measurements as a DataFrame sorted by time.
104
107
  """
105
- print('into dataframe')
106
108
  read: dict = {}
107
109
  timestamps = set()
108
110
 
@@ -130,8 +132,11 @@ class AsyncQuery:
130
132
  for timestamp in local_timestamps:
131
133
  read['time'].append(pd.to_datetime(timestamp))
132
134
 
133
- # return the data as a DataFrame
134
- return pd.DataFrame(read)
135
+ df = pd.DataFrame(read)
136
+ df.set_index('time', inplace=True)
137
+ df.sort_index(inplace=True)
138
+
139
+ return df
135
140
 
136
141
  async def latest(self) -> pd.DataFrame:
137
142
  """
@@ -163,35 +168,57 @@ class AsyncQuery:
163
168
  else:
164
169
  return pd.DataFrame()
165
170
 
166
- async def historical_data(self, start: str, end: str) -> pd.DataFrame:
167
- """
168
- Query historical data from the database.
171
+ def historical_query(self, query: str) -> pd.DataFrame:
172
+ tables = None
173
+ try:
174
+ with InfluxDBClient(
175
+ url=self._db_url,
176
+ token=self._influxdb_token,
177
+ org=self._influxdb_organization,
178
+ ) as client:
179
+ query_api = client.query_api()
180
+ tables = query_api.query(query)
181
+ except InfluxDBError as e:
182
+ print(f'Exception caught while querying the database:\n\n {e.message}')
169
183
 
170
- Args:
171
- start (str): Start time of the query (e.g., '2024-01-01T00:00:00Z').
172
- end (str): End time of the query (e.g., '2024-01-02T00:00:00Z').
184
+ if tables is not None:
185
+ return self._into_dataframe(tables)
186
+ else:
187
+ return pd.DataFrame()
173
188
 
174
- Returns:
175
- pd.DataFrame: Historical data within the specified time range.
189
+ async def historical_data(self, start: str, end: str = '') -> pd.DataFrame:
176
190
  """
191
+ Query the database synchronously for historical data within the specified
192
+ range.
193
+ Query the database for historical data within the specified time range.
194
+ Use when dealing with large time intervals. Uses regular client, not the
195
+ async one.
177
196
 
178
- # get the connection to the database via query api
179
- client = await self._get_InfluxDB_client()
180
- query_api = client.query_api()
197
+ Args:
198
+ start(str): start time (e.g., '2024-01-02T00:00:00Z') of the query
199
+ or relative time string (e.g., '-30d') for end unspecified
200
+ end (str): End time of the query (e.g., '2024-01-02T00:00:00Z')
181
201
 
182
- # query the data from specified start and finish
183
- query = f'from(bucket:"{self._influxdb_bucket}") |> range(start: {start}, stop: {end})'
202
+ Example:
203
+ interface.historical_data('-30d')
184
204
 
185
- tables = None
186
- try:
187
- tables = await query_api.query(query)
188
- except InfluxDBError as e:
189
- print(f'Exception caught while querying the database:\n\n {e.message}')
205
+ finish = datetime.now()
206
+ start = finish - timedelta(minuts=30)
207
+ start = start.strftime('%Y-%m-%dT%H:%M:%SZ')
208
+ finish = finish.strftime('%Y-%m-%dT%H:%M:%SZ')
190
209
 
191
- await client.close()
210
+ interface.historical_data(start, finish)
192
211
 
193
- if tables is not None:
194
- return self._into_dataframe(tables)
212
+ Returns:
213
+ pd.DataFrame: Historical data within the specified time range.
214
+ """
215
+
216
+ if start is not None and end == '':
217
+ query = f'from(bucket:"{self._influxdb_bucket}") |> range(start: {start})'
218
+ return self.historical_query(query)
219
+ elif start is not None and end != '':
220
+ query = f'from(bucket:"{self._influxdb_bucket}") |> range(start: {start}, stop: {end})'
221
+ return self.historical_query(query)
195
222
  else:
196
223
  return pd.DataFrame()
197
224
 
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env python
2
+ import json
3
+ import asyncio
4
+ import pandas as pd
5
+ from ahttpdc.reads.interface import DatabaseInterface
6
+
7
+ # load the secrets
8
+ with open('secrets/rpi-secrets.json', 'r') as f:
9
+ secrets = json.load(f)
10
+
11
+ sensors = {
12
+ 'bmp180': ['altitude', 'pressure', 'seaLevelPressure'],
13
+ 'mq135': ['aceton', 'alcohol', 'co', 'co2', 'nh4', 'toulen'],
14
+ 'ds18b20': ['temperature'],
15
+ 'dht22': ['humidity'],
16
+ }
17
+
18
+ dev_ip = '192.168.10.101'
19
+ dev_port = '80'
20
+
21
+ # create the DatabaseInterface object
22
+ interface = DatabaseInterface(
23
+ secrets['host'],
24
+ secrets['port'],
25
+ secrets['token'],
26
+ secrets['organization'],
27
+ secrets['bucket'],
28
+ sensors,
29
+ dev_ip,
30
+ dev_port,
31
+ secrets['handle'],
32
+ )
33
+
34
+
35
+ async def query():
36
+ result = await interface.query_historical('-30d')
37
+ return result
38
+
39
+
40
+ if __name__ == '__main__':
41
+ dataframe: pd.DataFrame = asyncio.run(query())
42
+ dataframe.to_csv('sensor-data.csv')
@@ -10,7 +10,7 @@ import os
10
10
  import pytest
11
11
 
12
12
  from ahttpdc.reads.interface import DatabaseInterface
13
- from datetime import datetime, timedelta
13
+ # from datetime import datetime, timedelta
14
14
 
15
15
  from tests.reads.query.test_query import TestQuery
16
16
 
@@ -104,11 +104,8 @@ class TestInterface:
104
104
  self.set_up()
105
105
  await asyncio.sleep(5)
106
106
 
107
- finish = datetime.now()
108
- start = finish - timedelta(seconds=4)
109
- result = await self.interface.query_historical(
110
- start.strftime('%Y-%m-%dT%H:%M:%SZ'), finish.strftime('%Y-%m-%dT%H:%M:%SZ')
111
- )
107
+ # the date variant tested in test_query.py
108
+ result = await self.interface.query_historical('-3s')
112
109
  self.interface.disable_fetching()
113
110
 
114
111
  assert not result.empty
@@ -1 +0,0 @@
1
- __version__ = '0.2'