gw_data 0.3.0__tar.gz → 0.3.2__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.
- {gw_data-0.3.0 → gw_data-0.3.2}/PKG-INFO +2 -39
- {gw_data-0.3.0 → gw_data-0.3.2}/README.md +1 -38
- {gw_data-0.3.0 → gw_data-0.3.2}/alembic/env.py +2 -1
- gw_data-0.3.2/alembic/versions/a7f151e8163f_visualizer_functions_and_views.py +74 -0
- {gw_data-0.3.0 → gw_data-0.3.2}/pyproject.toml +1 -1
- {gw_data-0.3.0 → gw_data-0.3.2}/src/gw_data/db/models/__init__.py +2 -0
- {gw_data-0.3.0 → gw_data-0.3.2}/src/gw_data/db/models/reading.py +1 -1
- {gw_data-0.3.0 → gw_data-0.3.2}/src/gw_data/db/models/user.py +1 -1
- {gw_data-0.3.0 → gw_data-0.3.2}/src/gw_data/db/models/user_installation_role.py +0 -1
- gw_data-0.3.2/src/gw_data/db/scripts/misc/adjust_report_message_timestamps.sql +59 -0
- gw_data-0.3.2/src/gw_data/db/scripts/misc/create_policy_view_readings_1hr.sql +4 -0
- gw_data-0.3.2/src/gw_data/db/scripts/misc/create_proc_refresh_cached_hourly_data.sql +20 -0
- gw_data-0.3.2/src/gw_data/db/scripts/misc/create_table_cached_hourly_data.sql +41 -0
- gw_data-0.3.2/src/gw_data/db/scripts/misc/create_udf_calc_energy_1hr.sql +56 -0
- gw_data-0.3.2/src/gw_data/db/scripts/misc/create_udf_calc_hourly_data.sql +162 -0
- gw_data-0.3.2/src/gw_data/db/scripts/misc/create_udf_retrieve_readings_1s.sql +40 -0
- gw_data-0.3.2/src/gw_data/db/scripts/misc/create_view_readings_1hr.sql +15 -0
- gw_data-0.3.2/src/gw_data/db/scripts/misc/refresh_all_cached_hourly_data.py +46 -0
- {gw_data-0.3.0 → gw_data-0.3.2}/src/gw_data/db/scripts/seed_data/homes.csv +1 -0
- gw_data-0.3.2/useful-scripts.sql +70 -0
- {gw_data-0.3.0 → gw_data-0.3.2}/uv.lock +1 -1
- gw_data-0.3.0/postgres_views.md +0 -44
- {gw_data-0.3.0 → gw_data-0.3.2}/.github/workflows/release.yml +0 -0
- {gw_data-0.3.0 → gw_data-0.3.2}/.gitignore +0 -0
- {gw_data-0.3.0 → gw_data-0.3.2}/alembic/README +0 -0
- {gw_data-0.3.0 → gw_data-0.3.2}/alembic/script.py.mako +0 -0
- {gw_data-0.3.0 → gw_data-0.3.2}/alembic/versions/1220f2f941dd_initial_schema.py +0 -0
- {gw_data-0.3.0 → gw_data-0.3.2}/alembic.ini +0 -0
- {gw_data-0.3.0 → gw_data-0.3.2}/sema_seed_request.yaml +0 -0
- {gw_data-0.3.0 → gw_data-0.3.2}/src/gw_data/__init__.py +0 -0
- {gw_data-0.3.0 → gw_data-0.3.2}/src/gw_data/config.py +0 -0
- {gw_data-0.3.0 → gw_data-0.3.2}/src/gw_data/db/__init__.py +0 -0
- {gw_data-0.3.0 → gw_data-0.3.2}/src/gw_data/db/models/_base.py +0 -0
- {gw_data-0.3.0 → gw_data-0.3.2}/src/gw_data/db/models/connectivity_edge.py +0 -0
- {gw_data-0.3.0 → gw_data-0.3.2}/src/gw_data/db/models/customer.py +0 -0
- {gw_data-0.3.0 → gw_data-0.3.2}/src/gw_data/db/models/g_node.py +0 -0
- {gw_data-0.3.0 → gw_data-0.3.2}/src/gw_data/db/models/installation.py +0 -0
- {gw_data-0.3.0 → gw_data-0.3.2}/src/gw_data/db/models/installer.py +0 -0
- {gw_data-0.3.0 → gw_data-0.3.2}/src/gw_data/db/models/message.py +0 -0
- {gw_data-0.3.0 → gw_data-0.3.2}/src/gw_data/db/models/position_point.py +0 -0
- {gw_data-0.3.0 → gw_data-0.3.2}/src/gw_data/db/models/reading_channel.py +0 -0
- {gw_data-0.3.0 → gw_data-0.3.2}/src/gw_data/db/scripts/0_db_create.psql +0 -0
- {gw_data-0.3.0 → gw_data-0.3.2}/src/gw_data/db/scripts/1_db_user_setup.psql +0 -0
- {gw_data-0.3.0 → gw_data-0.3.2}/src/gw_data/db/scripts/2_db_schema_setup.sql +0 -0
- {gw_data-0.3.0 → gw_data-0.3.2}/src/gw_data/db/scripts/3_db_alembic_upgrade.sh +0 -0
- {gw_data-0.3.0 → gw_data-0.3.2}/src/gw_data/db/scripts/4_db_seed.py +0 -0
- {gw_data-0.3.0 → gw_data-0.3.2}/src/gw_data/db/scripts/_XX_drop_all.sql +0 -0
- {gw_data-0.3.0 → gw_data-0.3.2}/src/gw_data/db/session.py +0 -0
- {gw_data-0.3.0 → gw_data-0.3.2}/src/gw_data/sema/__init__.py +0 -0
- {gw_data-0.3.0 → gw_data-0.3.2}/src/gw_data/sema/base.py +0 -0
- {gw_data-0.3.0 → gw_data-0.3.2}/src/gw_data/sema/codec.py +0 -0
- {gw_data-0.3.0 → gw_data-0.3.2}/src/gw_data/sema/definitions/enums/base.g.node.class/000.yaml +0 -0
- {gw_data-0.3.0 → gw_data-0.3.2}/src/gw_data/sema/definitions/enums/g.node.status/000.yaml +0 -0
- {gw_data-0.3.0 → gw_data-0.3.2}/src/gw_data/sema/definitions/formats/left.right.dot.yaml +0 -0
- {gw_data-0.3.0 → gw_data-0.3.2}/src/gw_data/sema/definitions/formats/uuid4.str.yaml +0 -0
- {gw_data-0.3.0 → gw_data-0.3.2}/src/gw_data/sema/definitions/registry.yaml +0 -0
- {gw_data-0.3.0 → gw_data-0.3.2}/src/gw_data/sema/definitions/types/g.node.gt/004.yaml +0 -0
- {gw_data-0.3.0 → gw_data-0.3.2}/src/gw_data/sema/definitions/types/position.point.gt/000.yaml +0 -0
- {gw_data-0.3.0 → gw_data-0.3.2}/src/gw_data/sema/enums/__init__.py +0 -0
- {gw_data-0.3.0 → gw_data-0.3.2}/src/gw_data/sema/enums/base_g_node_class.py +0 -0
- {gw_data-0.3.0 → gw_data-0.3.2}/src/gw_data/sema/enums/g_node_status.py +0 -0
- {gw_data-0.3.0 → gw_data-0.3.2}/src/gw_data/sema/enums/gw_str_enum.py +0 -0
- {gw_data-0.3.0 → gw_data-0.3.2}/src/gw_data/sema/enums/old_versions/__init__.py +0 -0
- {gw_data-0.3.0 → gw_data-0.3.2}/src/gw_data/sema/indexes/dependency_closure.yaml +0 -0
- {gw_data-0.3.0 → gw_data-0.3.2}/src/gw_data/sema/indexes/local_names.yaml +0 -0
- {gw_data-0.3.0 → gw_data-0.3.2}/src/gw_data/sema/indexes/lookup.yaml +0 -0
- {gw_data-0.3.0 → gw_data-0.3.2}/src/gw_data/sema/indexes/reverse_dependencies.yaml +0 -0
- {gw_data-0.3.0 → gw_data-0.3.2}/src/gw_data/sema/indexes/seed_expanded.yaml +0 -0
- {gw_data-0.3.0 → gw_data-0.3.2}/src/gw_data/sema/indexes/versions.yaml +0 -0
- {gw_data-0.3.0 → gw_data-0.3.2}/src/gw_data/sema/property_format.py +0 -0
- {gw_data-0.3.0 → gw_data-0.3.2}/src/gw_data/sema/tests/test_property_format.py +0 -0
- {gw_data-0.3.0 → gw_data-0.3.2}/src/gw_data/sema/types/__init__.py +0 -0
- {gw_data-0.3.0 → gw_data-0.3.2}/src/gw_data/sema/types/g_node_gt.py +0 -0
- {gw_data-0.3.0 → gw_data-0.3.2}/src/gw_data/sema/types/old_versions/__init__.py +0 -0
- {gw_data-0.3.0 → gw_data-0.3.2}/src/gw_data/sema/types/position_point_gt.py +0 -0
- {gw_data-0.3.0 → gw_data-0.3.2}/template.env +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gw_data
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: Gridworks Data Infrastructure
|
|
5
5
|
Author-email: Jessica Millar <jessica.lynn.millar@gmail.com>
|
|
6
6
|
Requires-Python: <3.13.0,>=3.12
|
|
@@ -149,41 +149,4 @@ We have column-store compression enabled on the readings table for data older th
|
|
|
149
149
|
|
|
150
150
|
### Useful Queries
|
|
151
151
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
```
|
|
155
|
-
-- Display the size in MB of our two main tables
|
|
156
|
-
SELECT pg_size_pretty(hypertable_size('readings')) as "Readings Table Size", pg_size_pretty(hypertable_size('messages')) as "Messages Table Size";
|
|
157
|
-
```
|
|
158
|
-
|
|
159
|
-
```
|
|
160
|
-
-- Display the size of all database tables in order.
|
|
161
|
-
-- This will show each TimescaleDB chunk as a separate table.
|
|
162
|
-
SELECT
|
|
163
|
-
table_schema || '.' || table_name AS table_full_name,
|
|
164
|
-
pg_size_pretty(pg_total_relation_size('"' || table_schema || '"."' || table_name || '"')) AS size
|
|
165
|
-
FROM information_schema.tables
|
|
166
|
-
ORDER BY
|
|
167
|
-
pg_total_relation_size('"' || table_schema || '"."' || table_name || '"') DESC;
|
|
168
|
-
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
```
|
|
172
|
-
-- Display compression stats for each chunk in the readings table
|
|
173
|
-
SELECT
|
|
174
|
-
chunk_name,
|
|
175
|
-
pg_size_pretty(before_compression_total_bytes) AS size_before,
|
|
176
|
-
pg_size_pretty(after_compression_total_bytes) AS size_after,
|
|
177
|
-
100 - (after_compression_total_bytes::float / before_compression_total_bytes * 100) AS compression_ratio_pct
|
|
178
|
-
FROM chunk_compression_stats('readings');
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
```
|
|
182
|
-
-- Get the ID of the policy_compression job so you can manually run `CALL run_job` with it (e.g. after a bulk import).
|
|
183
|
-
SELECT job_id, proc_name, hypertable_name, config
|
|
184
|
-
FROM timescaledb_information.jobs;
|
|
185
|
-
```
|
|
186
|
-
```
|
|
187
|
-
-- Get info (table, time range, etc.) about the TimescaleDB chunks.
|
|
188
|
-
SELECT * FROM timescaledb_information.chunks
|
|
189
|
-
```
|
|
152
|
+
There are some useful queries listed in `useful-queries.sql` in the project root.
|
|
@@ -134,41 +134,4 @@ We have column-store compression enabled on the readings table for data older th
|
|
|
134
134
|
|
|
135
135
|
### Useful Queries
|
|
136
136
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
```
|
|
140
|
-
-- Display the size in MB of our two main tables
|
|
141
|
-
SELECT pg_size_pretty(hypertable_size('readings')) as "Readings Table Size", pg_size_pretty(hypertable_size('messages')) as "Messages Table Size";
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
```
|
|
145
|
-
-- Display the size of all database tables in order.
|
|
146
|
-
-- This will show each TimescaleDB chunk as a separate table.
|
|
147
|
-
SELECT
|
|
148
|
-
table_schema || '.' || table_name AS table_full_name,
|
|
149
|
-
pg_size_pretty(pg_total_relation_size('"' || table_schema || '"."' || table_name || '"')) AS size
|
|
150
|
-
FROM information_schema.tables
|
|
151
|
-
ORDER BY
|
|
152
|
-
pg_total_relation_size('"' || table_schema || '"."' || table_name || '"') DESC;
|
|
153
|
-
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
```
|
|
157
|
-
-- Display compression stats for each chunk in the readings table
|
|
158
|
-
SELECT
|
|
159
|
-
chunk_name,
|
|
160
|
-
pg_size_pretty(before_compression_total_bytes) AS size_before,
|
|
161
|
-
pg_size_pretty(after_compression_total_bytes) AS size_after,
|
|
162
|
-
100 - (after_compression_total_bytes::float / before_compression_total_bytes * 100) AS compression_ratio_pct
|
|
163
|
-
FROM chunk_compression_stats('readings');
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
```
|
|
167
|
-
-- Get the ID of the policy_compression job so you can manually run `CALL run_job` with it (e.g. after a bulk import).
|
|
168
|
-
SELECT job_id, proc_name, hypertable_name, config
|
|
169
|
-
FROM timescaledb_information.jobs;
|
|
170
|
-
```
|
|
171
|
-
```
|
|
172
|
-
-- Get info (table, time range, etc.) about the TimescaleDB chunks.
|
|
173
|
-
SELECT * FROM timescaledb_information.chunks
|
|
174
|
-
```
|
|
137
|
+
There are some useful queries listed in `useful-queries.sql` in the project root.
|
|
@@ -10,8 +10,9 @@ from gw_data.config import Settings
|
|
|
10
10
|
GW_SCHEMA = "gridworks"
|
|
11
11
|
def include_name(name, type_, parent_names):
|
|
12
12
|
if type_ == "schema":
|
|
13
|
-
# Only track migrations in your chosen schema
|
|
14
13
|
return name == GW_SCHEMA
|
|
14
|
+
elif type_ == "table":
|
|
15
|
+
return name not in ['cached_hourly_data']
|
|
15
16
|
return True
|
|
16
17
|
|
|
17
18
|
# -----------------------------------------------------------------------------
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"""visualizer functions and views
|
|
2
|
+
|
|
3
|
+
Revision ID: a7f151e8163f
|
|
4
|
+
Revises: 1220f2f941dd
|
|
5
|
+
Create Date: 2026-06-18 10:34:33.737411
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
import os
|
|
9
|
+
from typing import Sequence, Union
|
|
10
|
+
|
|
11
|
+
from alembic import op
|
|
12
|
+
import sqlalchemy as sa
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# revision identifiers, used by Alembic.
|
|
16
|
+
revision: str = 'a7f151e8163f'
|
|
17
|
+
down_revision: Union[str, Sequence[str], None] = '1220f2f941dd'
|
|
18
|
+
branch_labels: Union[str, Sequence[str], None] = None
|
|
19
|
+
depends_on: Union[str, Sequence[str], None] = None
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def upgrade() -> None:
|
|
23
|
+
"""Upgrade schema."""
|
|
24
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
|
25
|
+
# ### end Alembic commands ###
|
|
26
|
+
|
|
27
|
+
op.execute("GRANT UPDATE (last_login, updated_at) ON gridworks.users TO gw_visualizer;")
|
|
28
|
+
|
|
29
|
+
op.execute("CREATE FUNCTION gridworks.c2f(temp_c FLOAT) RETURNS FLOAT IMMUTABLE LANGUAGE 'sql' RETURN temp_c * 9.0 / 5.0 + 32;")
|
|
30
|
+
op.execute("CREATE FUNCTION gridworks.f2c(temp_f FLOAT) RETURNS FLOAT IMMUTABLE LANGUAGE 'sql' RETURN (temp_f - 32) * 5.0 / 9.0;")
|
|
31
|
+
|
|
32
|
+
misc_scripts_folder = os.path.join(
|
|
33
|
+
os.path.dirname(__file__),
|
|
34
|
+
'../../src/gw_data/db/scripts/misc/',
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
for script in [
|
|
38
|
+
'create_view_readings_1hr.sql',
|
|
39
|
+
'create_policy_view_readings_1hr.sql',
|
|
40
|
+
'create_udf_retrieve_readings_1s.sql',
|
|
41
|
+
'create_udf_calc_energy_1hr.sql',
|
|
42
|
+
'create_udf_calc_hourly_data.sql',
|
|
43
|
+
'create_table_cached_hourly_data.sql',
|
|
44
|
+
'create_proc_refresh_cached_hourly_data.sql'
|
|
45
|
+
]:
|
|
46
|
+
with open(os.path.join(misc_scripts_folder, script), 'r') as f:
|
|
47
|
+
sql = f.read().strip()
|
|
48
|
+
op.execute(sql)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
op.execute("SELECT add_job('gridworks.refresh_cached_hourly_data', INTERVAL '30 minutes');")
|
|
52
|
+
|
|
53
|
+
print("""
|
|
54
|
+
IMPORTANT: to populate existing data into the `gridworks.readings_1hr` continuous aggregate, you must manually run `refresh_continuous_aggregate`.
|
|
55
|
+
IMPORTANT: to populate existing data into the `gridworks.cached_hourly_data` table, you will need to run `refresh_all_cached_hourly_data.py`.
|
|
56
|
+
""")
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def downgrade() -> None:
|
|
61
|
+
"""Downgrade schema."""
|
|
62
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
|
63
|
+
|
|
64
|
+
# ### end Alembic commands ###
|
|
65
|
+
|
|
66
|
+
op.execute("SELECT delete_job((SELECT job_id FROM timescaledb_information.jobs WHERE proc_schema = 'gridworks' AND proc_name = 'refresh_cached_hourly_data'));")
|
|
67
|
+
op.execute('DROP PROCEDURE gridworks.refresh_cached_hourly_data;')
|
|
68
|
+
op.execute('DROP TABLE gridworks.cached_hourly_data;')
|
|
69
|
+
op.execute('DROP FUNCTION gridworks.calc_hourly_data;')
|
|
70
|
+
op.execute('DROP MATERIALIZED VIEW gridworks.readings_1hr;')
|
|
71
|
+
op.execute('DROP FUNCTION gridworks.retrieve_readings_1s(timestamp with time zone, timestamp with time zone, text[]);')
|
|
72
|
+
op.execute('DROP FUNCTION gridworks.calc_energy_1hr(timestamp with time zone, timestamp with time zone);')
|
|
73
|
+
op.execute('DROP FUNCTION gridworks.c2f(float)')
|
|
74
|
+
op.execute('DROP FUNCTION gridworks.f2c(float)')
|
|
@@ -8,6 +8,7 @@ from gw_data.db.models.customer import CustomerSql
|
|
|
8
8
|
from gw_data.db.models.installer import InstallerSql
|
|
9
9
|
from gw_data.db.models.user import UserSql
|
|
10
10
|
from gw_data.db.models.installation import InstallationSql
|
|
11
|
+
from gw_data.db.models.user_installation_role import UserInstallationRoleSql
|
|
11
12
|
|
|
12
13
|
from gw_data.db.models.reading_channel import ReadingChannelSql
|
|
13
14
|
from gw_data.db.models.message import MessageSql
|
|
@@ -31,6 +32,7 @@ __all__ = [
|
|
|
31
32
|
"InstallerSql",
|
|
32
33
|
"UserSql",
|
|
33
34
|
"InstallationSql",
|
|
35
|
+
"UserInstallationRoleSql",
|
|
34
36
|
"ReadingChannelSql",
|
|
35
37
|
"MessageSql",
|
|
36
38
|
"ReadingSql"
|
|
@@ -39,7 +39,7 @@ class ReadingSql(Base):
|
|
|
39
39
|
index=True
|
|
40
40
|
)
|
|
41
41
|
timestamp: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, index=True)
|
|
42
|
-
value: Mapped[
|
|
42
|
+
value: Mapped[int] = mapped_column(BigInteger, nullable=False)
|
|
43
43
|
|
|
44
44
|
__table_args__ = (
|
|
45
45
|
# TimescaleDB automatically creates this one; we need to include it so Alembic doesn't get confused
|
|
@@ -29,7 +29,7 @@ class UserSql(Base):
|
|
|
29
29
|
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
|
30
30
|
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
|
|
31
31
|
|
|
32
|
-
installation_roles: Mapped[list[UserInstallationRoleSql]] = relationship(uselist=True
|
|
32
|
+
installation_roles: Mapped[list[UserInstallationRoleSql]] = relationship(uselist=True)
|
|
33
33
|
|
|
34
34
|
def __repr__(self) -> str:
|
|
35
35
|
return f"User(id={self.id!r}, username={self.username!r}, is_active={self.is_active!r})"
|
|
@@ -23,7 +23,6 @@ class UserInstallationRoleSql(Base):
|
|
|
23
23
|
installations: Mapped[list[InstallationSql]] = relationship(
|
|
24
24
|
"InstallationSql",
|
|
25
25
|
uselist=True,
|
|
26
|
-
lazy="joined",
|
|
27
26
|
primaryjoin="or_(InstallationSql.id==UserInstallationRoleSql.installation_id, UserInstallationRoleSql.installation_id == None)"
|
|
28
27
|
)
|
|
29
28
|
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
-- report.event message parsing had a bug that caused the timestamps to get set in local time rather than UTC.
|
|
2
|
+
-- This script fixes them.
|
|
3
|
+
-- (Unfortunately we can't just UPDATE a timestamp column with TimescaleDB)
|
|
4
|
+
-- This never needed to be run in prod, since the server local time was UTC.
|
|
5
|
+
|
|
6
|
+
-- SET temp_buffers = '2000MB';
|
|
7
|
+
|
|
8
|
+
BEGIN;
|
|
9
|
+
|
|
10
|
+
CREATE TEMP TABLE temp_report_messages AS
|
|
11
|
+
SELECT
|
|
12
|
+
id,
|
|
13
|
+
"timestamp" + INTERVAL '6 hours' AS "timestamp",
|
|
14
|
+
from_alias,
|
|
15
|
+
created_at + INTERVAL '6 hours' AS created_at,
|
|
16
|
+
persisted_at,
|
|
17
|
+
message_type_name,
|
|
18
|
+
payload
|
|
19
|
+
FROM gridworks.messages
|
|
20
|
+
-- where id='85c153a2-ebf6-4a24-83ba-5dba12967d87';
|
|
21
|
+
WHERE message_type_name='report.event';
|
|
22
|
+
|
|
23
|
+
DELETE FROM gridworks.messages
|
|
24
|
+
-- where id='85c153a2-ebf6-4a24-83ba-5dba12967d87';
|
|
25
|
+
WHERE message_type_name='report.event';
|
|
26
|
+
|
|
27
|
+
INSERT INTO gridworks.messages SELECT * FROM temp_report_messages;
|
|
28
|
+
|
|
29
|
+
WITH
|
|
30
|
+
q1 AS (
|
|
31
|
+
SELECT COUNT(*) as cnt, ROW_NUMBER() OVER () AS rn FROM gridworks.messages
|
|
32
|
+
),
|
|
33
|
+
q2 AS (
|
|
34
|
+
SELECT *, ROW_NUMBER() OVER () AS rn
|
|
35
|
+
FROM gridworks.messages
|
|
36
|
+
where id='85c153a2-ebf6-4a24-83ba-5dba12967d87'
|
|
37
|
+
)
|
|
38
|
+
SELECT
|
|
39
|
+
q1.cnt,
|
|
40
|
+
q2.*
|
|
41
|
+
FROM q1
|
|
42
|
+
FULL OUTER JOIN q2 ON q1.rn = q2.rn;
|
|
43
|
+
|
|
44
|
+
-- Fix the OAT and WS readings
|
|
45
|
+
select id from gridworks.reading_channels where name = 'forecast-ws';
|
|
46
|
+
update gridworks.readings
|
|
47
|
+
set value=value * 10
|
|
48
|
+
where channel_id in (
|
|
49
|
+
'792a01bc-1b95-4434-97e5-30d64c000024',
|
|
50
|
+
'3310111e-33ab-4c07-bab1-7d4c7d464201',
|
|
51
|
+
'60bb6806-e5a5-4a06-a60e-c26b0bf77e59',
|
|
52
|
+
'a349f893-61a7-4c65-8e33-998517d06fb0',
|
|
53
|
+
'f0ae219e-73d1-4c61-9c02-a13e940b8737',
|
|
54
|
+
'97a42794-bb6b-4a4d-be6f-1c5c9f955af7'
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
COMMIT
|
|
59
|
+
-- ROLLBACK;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
CREATE OR REPLACE PROCEDURE gridworks.refresh_cached_hourly_data(job_id int, config jsonb)
|
|
2
|
+
LANGUAGE plpgsql
|
|
3
|
+
AS $$
|
|
4
|
+
DECLARE
|
|
5
|
+
t_start timestamp with time zone := now() - INTERVAL '2 days';
|
|
6
|
+
t_start_valid timestamp with time zone := t_start + INTERVAL '1 hour';
|
|
7
|
+
t_end timestamp with time zone := now();
|
|
8
|
+
BEGIN
|
|
9
|
+
DELETE FROM gridworks.cached_hourly_data
|
|
10
|
+
WHERE time_bucket >= t_start_valid AND time_bucket <= t_end;
|
|
11
|
+
|
|
12
|
+
INSERT INTO gridworks.cached_hourly_data
|
|
13
|
+
WITH hourly_data AS MATERIALIZED (
|
|
14
|
+
SELECT * FROM gridworks.calc_hourly_data(t_start => t_start, t_end => t_end)
|
|
15
|
+
)
|
|
16
|
+
SELECT * FROM hourly_data
|
|
17
|
+
WHERE time_bucket >= t_start_valid;
|
|
18
|
+
END;
|
|
19
|
+
$$;
|
|
20
|
+
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
CREATE TABLE gridworks.cached_hourly_data (
|
|
2
|
+
terminal_asset_alias text,
|
|
3
|
+
time_bucket timestamp with time zone,
|
|
4
|
+
hp_kwh_el float,
|
|
5
|
+
hp_kwh_th float,
|
|
6
|
+
dist_kwh float,
|
|
7
|
+
store_change_kwh float,
|
|
8
|
+
hp_avg_lwt float,
|
|
9
|
+
hp_avg_ewt float,
|
|
10
|
+
dist_avg_swt float,
|
|
11
|
+
dist_avg_rwt float,
|
|
12
|
+
buffer_depth1_start float,
|
|
13
|
+
buffer_depth2_start float,
|
|
14
|
+
buffer_depth3_start float,
|
|
15
|
+
buffer_depth4_start float,
|
|
16
|
+
tank1_depth1_start float,
|
|
17
|
+
tank1_depth2_start float,
|
|
18
|
+
tank1_depth3_start float,
|
|
19
|
+
tank1_depth4_start float,
|
|
20
|
+
tank2_depth1_start float,
|
|
21
|
+
tank2_depth2_start float,
|
|
22
|
+
tank2_depth3_start float,
|
|
23
|
+
tank2_depth4_start float,
|
|
24
|
+
tank3_depth1_start float,
|
|
25
|
+
tank3_depth2_start float,
|
|
26
|
+
tank3_depth3_start float,
|
|
27
|
+
tank3_depth4_start float,
|
|
28
|
+
relay_3_pulled_fraction float,
|
|
29
|
+
relay_5_pulled_fraction float,
|
|
30
|
+
relay_6_pulled_fraction float,
|
|
31
|
+
relay_9_pulled_fraction float,
|
|
32
|
+
zone1_heatcall_fraction float,
|
|
33
|
+
zone2_heatcall_fraction float,
|
|
34
|
+
zone3_heatcall_fraction float,
|
|
35
|
+
zone4_heatcall_fraction float,
|
|
36
|
+
ws_mph float,
|
|
37
|
+
oat_f float,
|
|
38
|
+
total_usd_per_mwh float,
|
|
39
|
+
buffer_available_kwh float,
|
|
40
|
+
lmp_usd_per_mwh float
|
|
41
|
+
)
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
CREATE FUNCTION gridworks.calc_energy_1hr(
|
|
2
|
+
t_start timestamp with time zone,
|
|
3
|
+
t_end timestamp with time zone)
|
|
4
|
+
RETURNS TABLE(
|
|
5
|
+
terminal_asset_alias text,
|
|
6
|
+
time_bucket timestamp with time zone,
|
|
7
|
+
hp_kwh float,
|
|
8
|
+
dist_kwh float,
|
|
9
|
+
store_change_kwh float
|
|
10
|
+
)
|
|
11
|
+
STABLE
|
|
12
|
+
LANGUAGE 'sql'
|
|
13
|
+
|
|
14
|
+
AS $BODY$
|
|
15
|
+
|
|
16
|
+
SELECT
|
|
17
|
+
terminal_asset_alias,
|
|
18
|
+
time_bucket(INTERVAL '1 hour', time_bucket_1s) AS time_bucket_1h,
|
|
19
|
+
avg(hp_kw) AS hp_kwh,
|
|
20
|
+
avg(dist_kw) AS dist_kwh,
|
|
21
|
+
avg(store_change_kw) AS store_change_kwh
|
|
22
|
+
FROM (
|
|
23
|
+
SELECT
|
|
24
|
+
*,
|
|
25
|
+
500 * primary_flow_gpm * (hp_lwt_c - hp_ewt_c) * 9 / 5 / 3410 AS hp_kw,
|
|
26
|
+
500 * dist_flow_gpm * (dist_swt_c - dist_rwt_c) * 9 / 5 / 3410 AS dist_kw,
|
|
27
|
+
500 * (
|
|
28
|
+
(1 - store_charge_discharge_0_1) * store_flow_gpm +
|
|
29
|
+
store_charge_discharge_0_1 * primary_flow_gpm
|
|
30
|
+
) * (store_charge_discharge_0_1 * 2 - 1) * (store_hot_pipe_c - store_cold_pipe_c) * 9 / 5 / 3410 AS store_change_kw
|
|
31
|
+
FROM (
|
|
32
|
+
SELECT
|
|
33
|
+
terminal_asset_alias,
|
|
34
|
+
time_bucket AS time_bucket_1s,
|
|
35
|
+
(AVG (value) FILTER (WHERE channel_name='primary-flow' AND unit='GpmTimes100') / 100) AS primary_flow_gpm,
|
|
36
|
+
(AVG (value) FILTER (WHERE channel_name='hp-lwt' AND unit='WaterTempCTimes1000') / 1000) AS hp_lwt_c,
|
|
37
|
+
(AVG (value) FILTER (WHERE channel_name='hp-ewt' AND unit='WaterTempCTimes1000') / 1000) AS hp_ewt_c,
|
|
38
|
+
(AVG (value) FILTER (WHERE channel_name='dist-flow' AND unit='GpmTimes100') / 100) AS dist_flow_gpm,
|
|
39
|
+
(AVG (value) FILTER (WHERE channel_name='dist-swt' AND unit='WaterTempCTimes1000') / 1000) AS dist_swt_c,
|
|
40
|
+
(AVG (value) FILTER (WHERE channel_name='dist-rwt' AND unit='WaterTempCTimes1000') / 1000) AS dist_rwt_c,
|
|
41
|
+
(AVG (value) FILTER (WHERE channel_name='store-flow' AND unit='GpmTimes100') / 100) AS store_flow_gpm,
|
|
42
|
+
(AVG (value) FILTER (WHERE channel_name='store-hot-pipe' AND unit='WaterTempCTimes1000') / 1000) AS store_hot_pipe_c,
|
|
43
|
+
(AVG (value) FILTER (WHERE channel_name='store-cold-pipe' AND unit='WaterTempCTimes1000') / 1000) AS store_cold_pipe_c,
|
|
44
|
+
(AVG (value) FILTER (WHERE channel_name='charge-discharge-relay3')) AS store_charge_discharge_0_1
|
|
45
|
+
FROM gridworks.retrieve_readings_1s(
|
|
46
|
+
t_start => t_start,
|
|
47
|
+
t_end => t_end,
|
|
48
|
+
channels => ARRAY['hp-ewt','hp-lwt', 'primary-flow', 'dist-swt', 'dist-rwt', 'dist-flow', 'charge-discharge-relay3', 'store-hot-pipe', 'store-cold-pipe', 'store-flow']
|
|
49
|
+
)
|
|
50
|
+
GROUP BY terminal_asset_alias,time_bucket
|
|
51
|
+
)
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
GROUP BY terminal_asset_alias,time_bucket_1h
|
|
55
|
+
ORDER BY terminal_asset_alias,time_bucket_1h
|
|
56
|
+
$BODY$;
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
CREATE FUNCTION gridworks.calc_hourly_data(
|
|
2
|
+
t_start timestamp with time zone,
|
|
3
|
+
t_end timestamp with time zone)
|
|
4
|
+
RETURNS TABLE(
|
|
5
|
+
-- NOTE that this table must match the cached_hourly_data table!
|
|
6
|
+
terminal_asset_alias text,
|
|
7
|
+
time_bucket timestamp with time zone,
|
|
8
|
+
hp_kwh_el float,
|
|
9
|
+
hp_kwh_th float,
|
|
10
|
+
dist_kwh float,
|
|
11
|
+
store_change_kwh float,
|
|
12
|
+
hp_avg_lwt float,
|
|
13
|
+
hp_avg_ewt float,
|
|
14
|
+
dist_avg_swt float,
|
|
15
|
+
dist_avg_rwt float,
|
|
16
|
+
buffer_depth1_start float,
|
|
17
|
+
buffer_depth2_start float,
|
|
18
|
+
buffer_depth3_start float,
|
|
19
|
+
buffer_depth4_start float,
|
|
20
|
+
tank1_depth1_start float,
|
|
21
|
+
tank1_depth2_start float,
|
|
22
|
+
tank1_depth3_start float,
|
|
23
|
+
tank1_depth4_start float,
|
|
24
|
+
tank2_depth1_start float,
|
|
25
|
+
tank2_depth2_start float,
|
|
26
|
+
tank2_depth3_start float,
|
|
27
|
+
tank2_depth4_start float,
|
|
28
|
+
tank3_depth1_start float,
|
|
29
|
+
tank3_depth2_start float,
|
|
30
|
+
tank3_depth3_start float,
|
|
31
|
+
tank3_depth4_start float,
|
|
32
|
+
relay_3_pulled_fraction float,
|
|
33
|
+
relay_5_pulled_fraction float,
|
|
34
|
+
relay_6_pulled_fraction float,
|
|
35
|
+
relay_9_pulled_fraction float,
|
|
36
|
+
zone1_heatcall_fraction float,
|
|
37
|
+
zone2_heatcall_fraction float,
|
|
38
|
+
zone3_heatcall_fraction float,
|
|
39
|
+
zone4_heatcall_fraction float,
|
|
40
|
+
ws_mph float,
|
|
41
|
+
oat_f float,
|
|
42
|
+
total_usd_per_mwh float,
|
|
43
|
+
buffer_available_kwh float,
|
|
44
|
+
lmp_usd_per_mwh float
|
|
45
|
+
)
|
|
46
|
+
STABLE
|
|
47
|
+
LANGUAGE 'sql'
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
AS $BODY$
|
|
51
|
+
SELECT terminal_asset_alias, time_bucket,
|
|
52
|
+
(hp_idu_w + hp_odu_w) / 1000 AS hp_kwh_el,
|
|
53
|
+
hp_kwh AS hp_kwh_th,
|
|
54
|
+
dist_kwh,
|
|
55
|
+
store_change_kwh,
|
|
56
|
+
hp_avg_lwt,hp_avg_ewt,
|
|
57
|
+
dist_avg_swt, dist_avg_rwt,
|
|
58
|
+
buffer_depth1_start, buffer_depth2_start, buffer_depth3_start, buffer_depth4_start,
|
|
59
|
+
tank1_depth1_start, tank1_depth2_start, tank1_depth3_start, tank1_depth4_start,
|
|
60
|
+
tank2_depth1_start, tank2_depth2_start, tank2_depth3_start, tank2_depth4_start,
|
|
61
|
+
tank3_depth1_start, tank3_depth2_start, tank3_depth3_start, tank3_depth4_start,
|
|
62
|
+
relay_3_pulled_fraction, relay_5_pulled_fraction, relay_6_pulled_fraction, relay_9_pulled_fraction,
|
|
63
|
+
zone1_heatcall_fraction, zone2_heatcall_fraction, zone3_heatcall_fraction, zone4_heatcall_fraction,
|
|
64
|
+
ws_mph, oat_f,
|
|
65
|
+
total_usd_per_mwh,
|
|
66
|
+
buffer_available_kwh,
|
|
67
|
+
lmp_usd_per_mwh
|
|
68
|
+
FROM
|
|
69
|
+
(
|
|
70
|
+
SELECT * FROM (
|
|
71
|
+
SELECT terminal_asset_alias, time_bucket, hp_kwh, dist_kwh, store_change_kwh
|
|
72
|
+
FROM gridworks.calc_energy_1hr(t_start => t_start, t_end => t_end)
|
|
73
|
+
) AS kwh_values
|
|
74
|
+
JOIN (
|
|
75
|
+
SELECT
|
|
76
|
+
terminal_asset_alias AS avg_value_ta,
|
|
77
|
+
time_bucket AS avg_value_time_bucket,
|
|
78
|
+
AVG (avg_value) FILTER (WHERE channel_name='hp-idu-pwr' AND unit='PowerW') AS hp_idu_w,
|
|
79
|
+
AVG (avg_value) FILTER (WHERE channel_name='hp-odu-pwr' AND unit='PowerW') AS hp_odu_w,
|
|
80
|
+
gridworks.C2F(AVG (avg_value) FILTER (WHERE channel_name='hp-lwt' AND unit='WaterTempCTimes1000') / 1000) AS hp_avg_lwt,
|
|
81
|
+
gridworks.C2F(AVG (avg_value) FILTER (WHERE channel_name='hp-ewt' AND unit='WaterTempCTimes1000') / 1000) AS hp_avg_ewt,
|
|
82
|
+
gridworks.C2F(AVG (avg_value) FILTER (WHERE channel_name='dist-swt' AND unit='WaterTempCTimes1000') / 1000) AS dist_avg_swt,
|
|
83
|
+
gridworks.C2F(AVG (avg_value) FILTER (WHERE channel_name='dist-rwt' AND unit='WaterTempCTimes1000') / 1000) AS dist_avg_rwt,
|
|
84
|
+
AVG (avg_value) FILTER (where channel_name like '%-relay3') AS relay_3_pulled_fraction,
|
|
85
|
+
AVG (avg_value) FILTER (where channel_name like '%-relay5') AS relay_5_pulled_fraction,
|
|
86
|
+
AVG (avg_value) FILTER (where channel_name like '%-relay6') AS relay_6_pulled_fraction,
|
|
87
|
+
AVG (avg_value) FILTER (where channel_name like '%-relay9') AS relay_9_pulled_fraction,
|
|
88
|
+
AVG (avg_value) FILTER (where channel_name like 'zone1-%-heat-call') AS zone1_heatcall_fraction,
|
|
89
|
+
AVG (avg_value) FILTER (where channel_name like 'zone2-%-heat-call') AS zone2_heatcall_fraction,
|
|
90
|
+
AVG (avg_value) FILTER (where channel_name like 'zone3-%-heat-call') AS zone3_heatcall_fraction,
|
|
91
|
+
AVG (avg_value) FILTER (where channel_name like 'zone4-%-heat-call') AS zone4_heatcall_fraction
|
|
92
|
+
FROM (
|
|
93
|
+
SELECT
|
|
94
|
+
terminal_asset_alias,
|
|
95
|
+
channel_name,
|
|
96
|
+
unit,
|
|
97
|
+
time_bucket,
|
|
98
|
+
interpolated_average(
|
|
99
|
+
time_weight,
|
|
100
|
+
time_bucket,
|
|
101
|
+
'1 hour',
|
|
102
|
+
LAG(time_weight) OVER (PARTITION BY terminal_asset_alias,channel_name,unit ORDER BY time_bucket),
|
|
103
|
+
LEAD(time_weight) OVER (PARTITION BY terminal_asset_alias,channel_name,unit ORDER BY time_bucket)
|
|
104
|
+
) AS avg_value
|
|
105
|
+
FROM (
|
|
106
|
+
SELECT * FROM gridworks.readings_1hr
|
|
107
|
+
WHERE 1=1
|
|
108
|
+
AND time_bucket >= t_start
|
|
109
|
+
AND time_bucket <= t_end
|
|
110
|
+
)
|
|
111
|
+
)
|
|
112
|
+
GROUP BY time_bucket,terminal_asset_alias
|
|
113
|
+
ORDER BY time_bucket
|
|
114
|
+
) AS avg_values ON kwh_values.time_bucket = avg_value_time_bucket AND kwh_values.terminal_asset_alias = avg_value_ta
|
|
115
|
+
JOIN (
|
|
116
|
+
SELECT
|
|
117
|
+
terminal_asset_alias AS first_value_ta,
|
|
118
|
+
time_bucket AS first_value_time_bucket,
|
|
119
|
+
AVG (first_val) FILTER (WHERE channel_name='buffer-depth1' AND unit='FahrenheitX100') / 100 AS buffer_depth1_start,
|
|
120
|
+
AVG (first_val) FILTER (WHERE channel_name='buffer-depth2' AND unit='FahrenheitX100') / 100 AS buffer_depth2_start,
|
|
121
|
+
AVG (first_val) FILTER (WHERE channel_name='buffer-depth3' AND unit='FahrenheitX100') / 100 AS buffer_depth3_start,
|
|
122
|
+
AVG (first_val) FILTER (WHERE channel_name='buffer-depth4' AND unit='FahrenheitX100') / 100 AS buffer_depth4_start,
|
|
123
|
+
AVG (first_val) FILTER (WHERE channel_name='tank1-depth1' AND unit='FahrenheitX100') / 100 AS tank1_depth1_start,
|
|
124
|
+
AVG (first_val) FILTER (WHERE channel_name='tank1-depth2' AND unit='FahrenheitX100') / 100 AS tank1_depth2_start,
|
|
125
|
+
AVG (first_val) FILTER (WHERE channel_name='tank1-depth3' AND unit='FahrenheitX100') / 100 AS tank1_depth3_start,
|
|
126
|
+
AVG (first_val) FILTER (WHERE channel_name='tank1-depth4' AND unit='FahrenheitX100') / 100 AS tank1_depth4_start,
|
|
127
|
+
AVG (first_val) FILTER (WHERE channel_name='tank2-depth1' AND unit='FahrenheitX100') / 100 AS tank2_depth1_start,
|
|
128
|
+
AVG (first_val) FILTER (WHERE channel_name='tank2-depth2' AND unit='FahrenheitX100') / 100 AS tank2_depth2_start,
|
|
129
|
+
AVG (first_val) FILTER (WHERE channel_name='tank2-depth3' AND unit='FahrenheitX100') / 100 AS tank2_depth3_start,
|
|
130
|
+
AVG (first_val) FILTER (WHERE channel_name='tank2-depth4' AND unit='FahrenheitX100') / 100 AS tank2_depth4_start,
|
|
131
|
+
AVG (first_val) FILTER (WHERE channel_name='tank3-depth1' AND unit='FahrenheitX100') / 100 AS tank3_depth1_start,
|
|
132
|
+
AVG (first_val) FILTER (WHERE channel_name='tank3-depth2' AND unit='FahrenheitX100') / 100 AS tank3_depth2_start,
|
|
133
|
+
AVG (first_val) FILTER (WHERE channel_name='tank3-depth3' AND unit='FahrenheitX100') / 100 AS tank3_depth3_start,
|
|
134
|
+
AVG (first_val) FILTER (WHERE channel_name='tank3-depth4' AND unit='FahrenheitX100') / 100 AS tank3_depth4_start,
|
|
135
|
+
AVG (first_val) FILTER (where channel_name = 'forecast-oat' AND unit='FahrenheitX100') / 100 AS oat_f,
|
|
136
|
+
AVG (first_val) FILTER (where channel_name = 'forecast-ws' AND unit='MilesPerHourX1000') / 1000 AS ws_mph,
|
|
137
|
+
AVG (first_val) FILTER (where channel_name = 'total-usd-per-mwh' AND unit='DollarsX1000') / 1000 AS total_usd_per_mwh,
|
|
138
|
+
AVG (first_val) FILTER (where channel_name = 'lmp-usd-per-mwh' AND unit='DollarsX1000') / 1000 AS lmp_usd_per_mwh,
|
|
139
|
+
AVG (first_val) FILTER (where channel_name = 'buffer-available-kwh' AND unit='KilowattHoursX1000') / 1000 AS buffer_available_kwh
|
|
140
|
+
|
|
141
|
+
FROM (
|
|
142
|
+
SELECT
|
|
143
|
+
terminal_asset_alias,
|
|
144
|
+
channel_name,
|
|
145
|
+
unit,
|
|
146
|
+
time_bucket,
|
|
147
|
+
first_val(time_weight) AS first_val
|
|
148
|
+
FROM (
|
|
149
|
+
SELECT * FROM gridworks.readings_1hr
|
|
150
|
+
WHERE 1=1
|
|
151
|
+
AND time_bucket >= t_start
|
|
152
|
+
AND time_bucket <= t_end
|
|
153
|
+
ORDER BY time_bucket
|
|
154
|
+
)
|
|
155
|
+
)
|
|
156
|
+
GROUP BY time_bucket,terminal_asset_alias
|
|
157
|
+
ORDER BY time_bucket
|
|
158
|
+
) AS first_values ON kwh_values.time_bucket = first_value_time_bucket AND kwh_values.terminal_asset_alias = first_value_ta
|
|
159
|
+
OFFSET 0 -- Optimization
|
|
160
|
+
)
|
|
161
|
+
ORDER BY time_bucket
|
|
162
|
+
$BODY$;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
CREATE FUNCTION gridworks.retrieve_readings_1s(
|
|
2
|
+
t_start timestamp with time zone,
|
|
3
|
+
t_end timestamp with time zone,
|
|
4
|
+
channels text[])
|
|
5
|
+
RETURNS TABLE(terminal_asset_alias text, channel_name text, unit text, unit_type text, time_bucket timestamp with time zone, value numeric)
|
|
6
|
+
STABLE
|
|
7
|
+
LANGUAGE 'sql'
|
|
8
|
+
|
|
9
|
+
AS $BODY$
|
|
10
|
+
|
|
11
|
+
SELECT
|
|
12
|
+
time_values.terminal_asset_alias,
|
|
13
|
+
time_values.channel_name,
|
|
14
|
+
time_values.unit,
|
|
15
|
+
time_values.unit_type,
|
|
16
|
+
time_bucket_gapfill(INTERVAL '1 seconds', time_values.time_bucket) AS time_bucket_gapfilled,
|
|
17
|
+
locf(max(time_values.avg_value)) AS last_reading_value
|
|
18
|
+
FROM (
|
|
19
|
+
SELECT
|
|
20
|
+
rc.terminal_asset_alias,
|
|
21
|
+
rc.name AS channel_name,
|
|
22
|
+
rc.unit,
|
|
23
|
+
rc.unit_type,
|
|
24
|
+
time_bucket(INTERVAL '1 seconds', r.timestamp) AS time_bucket,
|
|
25
|
+
avg(r.value) AS avg_value
|
|
26
|
+
-- time_weight('LOCF', r.timestamp, r.value) AS time_weight
|
|
27
|
+
FROM gridworks.readings r
|
|
28
|
+
JOIN gridworks.reading_channels rc ON rc.id = r.channel_id
|
|
29
|
+
WHERE
|
|
30
|
+
r.timestamp >= t_start --'2026-04-20T04:00:00Z'
|
|
31
|
+
AND r.timestamp <= t_end --'2026-04-20T07:00:00Z'
|
|
32
|
+
AND rc.name = ANY(channels)
|
|
33
|
+
GROUP BY time_bucket, rc.name, rc.unit, rc.unit_type, rc.terminal_asset_alias
|
|
34
|
+
ORDER BY rc.name, time_bucket
|
|
35
|
+
) AS time_values
|
|
36
|
+
WHERE 1=1
|
|
37
|
+
AND time_values.time_bucket >= t_start
|
|
38
|
+
AND time_values.time_bucket <= t_end
|
|
39
|
+
GROUP BY time_values.terminal_asset_alias, time_values.channel_name, time_values.unit, time_values.unit_type, time_bucket_gapfilled
|
|
40
|
+
$BODY$;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
CREATE MATERIALIZED VIEW gridworks.readings_1hr
|
|
2
|
+
WITH (timescaledb.continuous)
|
|
3
|
+
AS
|
|
4
|
+
SELECT
|
|
5
|
+
reading_channels.terminal_asset_alias AS terminal_asset_alias,
|
|
6
|
+
reading_channels.name AS channel_name,
|
|
7
|
+
reading_channels.unit AS unit,
|
|
8
|
+
reading_channels.unit_type AS unit_type,
|
|
9
|
+
time_bucket(INTERVAL '1 hour', readings.timestamp) AS time_bucket,
|
|
10
|
+
time_weight('LOCF', readings.timestamp, readings.value) AS time_weight
|
|
11
|
+
FROM gridworks.readings
|
|
12
|
+
JOIN gridworks.reading_channels ON reading_channels.id = readings.channel_id
|
|
13
|
+
GROUP BY time_bucket, reading_channels.terminal_asset_alias, reading_channels.name, reading_channels.unit, reading_channels.unit_type
|
|
14
|
+
ORDER BY reading_channels.terminal_asset_alias, reading_channels.name, time_bucket
|
|
15
|
+
WITH NO DATA;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from datetime import datetime, timedelta, timezone
|
|
3
|
+
import dotenv
|
|
4
|
+
|
|
5
|
+
from sqlalchemy import create_engine, text
|
|
6
|
+
from sqlalchemy.orm import sessionmaker
|
|
7
|
+
|
|
8
|
+
from gw_data.config import Settings
|
|
9
|
+
|
|
10
|
+
logging.basicConfig(level=logging.INFO, format="%(asctime)s | %(levelname)s | %(message)s")
|
|
11
|
+
logger = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
dotenv.load_dotenv()
|
|
14
|
+
settings = Settings()
|
|
15
|
+
engine = create_engine(settings.db_url.get_secret_value(), echo=False)
|
|
16
|
+
|
|
17
|
+
db_sessionmaker = sessionmaker(bind=engine)
|
|
18
|
+
db_session = db_sessionmaker()
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# t_start = datetime.now(timezone.utc).replace(minute=0, second=0, microsecond=0)
|
|
22
|
+
t_start = datetime(2026, 6, 1, tzinfo=timezone.utc)
|
|
23
|
+
t_final = datetime(2026, 1, 31, tzinfo=timezone.utc)
|
|
24
|
+
|
|
25
|
+
# t_start = datetime(2026, 4, 22, tzinfo=timezone.utc)
|
|
26
|
+
# t_final = datetime(2026, 4, 18, tzinfo=timezone.utc)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
while t_start > t_final:
|
|
30
|
+
logger.info(f'Refreshing from {t_start.isoformat()}...')
|
|
31
|
+
t_end = t_start
|
|
32
|
+
t_start = t_end - timedelta(days=1)
|
|
33
|
+
|
|
34
|
+
stmt = text(f"""
|
|
35
|
+
INSERT INTO gridworks.cached_hourly_data
|
|
36
|
+
WITH hourly_data AS MATERIALIZED (
|
|
37
|
+
SELECT * FROM gridworks.calc_hourly_data(t_start => '{(t_start - timedelta(hours=1)).isoformat()}', t_end => '{t_end.isoformat()}')
|
|
38
|
+
)
|
|
39
|
+
SELECT * FROM hourly_data
|
|
40
|
+
WHERE time_bucket >= '{t_start.isoformat()}' AND time_bucket < '{t_end.isoformat()}';
|
|
41
|
+
""")
|
|
42
|
+
|
|
43
|
+
db_result = db_session.execute(stmt)
|
|
44
|
+
db_session.commit()
|
|
45
|
+
logger.info(f'Inserted {db_result.rowcount} rows')
|
|
46
|
+
|
|
@@ -4,3 +4,4 @@
|
|
|
4
4
|
"fir","{""street"": ""7890 Fir Road"", ""city"": ""Millinocket"", ""state"": ""ME"", ""zip"": ""00130"", ""country"": ""USA"", ""latitude"": 45.64500, ""longitude"": -68.72}","{""first_name"": ""Renee"", ""last_name"": ""Fir"", ""phone"": ""207-754-3256"", ""email"": ""reneefir@example.com""}","{""first_name"": ""Kim"", ""last_name"": ""FirFir"", ""phone"": ""207-210-0452"", ""email"": null}","null",3,"hw1.isone.me.versant.keene.fir","{""status"": ""ok"", ""message"": null, ""acked"": null, ""acked_by"": null, ""acked_at"": null}","""NotListeningToAtn""","127.0.0.1","unknown","{""alpha"": 7.5, ""beta"": -0.13, ""gamma"": 0.0022, ""intermediate_power_kw"": 1.5, ""intermediate_rswt"": 94.0, ""dd_power_kw"": 7.5, ""dd_rswt"": 150.0, ""dd_delta_t"": 20.0}","2026-01-23 07:04:32.477627-06"
|
|
5
5
|
"maple","{""street"": ""101 Maple Road"", ""city"": ""Millinocket"", ""state"": ""ME"", ""zip"": ""04462"", ""country"": ""USA"", ""latitude"": 45.64400, ""longitude"": -68.73}","{""first_name"": ""Herman"", ""last_name"": ""Maple"", ""phone"": ""555-444-3333"", ""email"": null}","null","null",4,"hw1.isone.me.versant.keene.maple","{""status"": ""ok"", ""message"": null, ""acked"": null, ""acked_by"": null, ""acked_at"": null}","""NotListeningToAtn""","127.0.0.1","unknown","{""alpha"": 6.3, ""beta"": -0.09, ""gamma"": 0.0007, ""intermediate_power_kw"": 1.5, ""intermediate_rswt"": 118.0, ""dd_power_kw"": 6.3, ""dd_rswt"": 160.0, ""dd_delta_t"": 20.0}","2026-01-23 07:04:32.477627-06"
|
|
6
6
|
"elm","{""street"": ""202 Elm Lane"", ""city"": ""Millinocket"", ""state"": ""ME"", ""zip"": ""04462"", ""country"": ""USA"", ""latitude"": 45.6800, ""longitude"": -68.71}","{""first_name"": ""Mike"", ""last_name"": ""Elm"", ""phone"": ""555-666-7777"", ""email"": null}","{""first_name"": ""Michelle"", ""last_name"": ""Elm"", ""phone"": null, ""email"": null}","null",5,"hw1.isone.me.versant.keene.elm","{""status"": ""ok"", ""message"": null, ""acked"": null, ""acked_by"": null, ""acked_at"": null}","""NotListeningToAtn""","127.0.0.1","unknown","{""alpha"": 7.0, ""beta"": -0.13, ""gamma"": 0.001, ""intermediate_power_kw"": 1.5, ""intermediate_rswt"": 120.0, ""dd_power_kw"": 7.0, ""dd_rswt"": 150.0, ""dd_delta_t"": 20.0}","2026-01-23 07:04:32.477627-06"
|
|
7
|
+
"spruce","{""street"": ""303 Spruce Blvd"", ""city"": ""Millinocket"", ""state"": ""ME"", ""zip"": ""04462"", ""country"": ""USA"", ""latitude"": 45.6200, ""longitude"": -68.70}","{""first_name"": ""Gerald"", ""last_name"": ""Spruce"", ""phone"": ""555-666-8888"", ""email"": null}","{""first_name"": ""Maryann"", ""last_name"": ""Spruce"", ""phone"": null, ""email"": null}","null",5,"hw1.isone.me.versant.keene.spruce","{""status"": ""ok"", ""message"": null, ""acked"": null, ""acked_by"": null, ""acked_at"": null}","""NotListeningToAtn""","127.0.0.1","unknown","{""alpha"": 7.0, ""beta"": -0.13, ""gamma"": 0.001, ""intermediate_power_kw"": 1.5, ""intermediate_rswt"": 120.0, ""dd_power_kw"": 7.0, ""dd_rswt"": 150.0, ""dd_delta_t"": 20.0}","2026-01-23 07:04:32.477627-06"
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
-- Find readings for specific channels, installations, timeframe
|
|
2
|
+
select rc.terminal_asset_alias,rc.name,r.timestamp,r.value
|
|
3
|
+
from gridworks.readings r
|
|
4
|
+
join gridworks.reading_channels rc on r.channel_id = rc.id
|
|
5
|
+
where 1=1
|
|
6
|
+
and (rc.name like '%whitewire%' or rc.name like '%heat-call%')
|
|
7
|
+
and rc.terminal_asset_alias like '%beech%'
|
|
8
|
+
and timestamp >= '2026-04-20T04:00:00Z'
|
|
9
|
+
and timestamp <= '2026-04-20T05:00:00Z'
|
|
10
|
+
order by r.timestamp,rc.terminal_asset_alias
|
|
11
|
+
limit 100;
|
|
12
|
+
|
|
13
|
+
-- Find channels for an installation
|
|
14
|
+
select * from gridworks.reading_channels
|
|
15
|
+
where terminal_asset_alias like '%beech%'
|
|
16
|
+
order by name,terminal_asset_alias;
|
|
17
|
+
|
|
18
|
+
-- Find messages for an installation & timeframe
|
|
19
|
+
select * from gridworks.messages
|
|
20
|
+
where 1=1
|
|
21
|
+
and from_alias like '%beech%'
|
|
22
|
+
and timestamp >= '2026-04-20T04:00:00Z'
|
|
23
|
+
and timestamp <= '2026-04-20T05:00:00Z'
|
|
24
|
+
limit 100;
|
|
25
|
+
|
|
26
|
+
-- Total database size
|
|
27
|
+
SELECT pg_size_pretty(pg_database_size('tsdb')) AS size;
|
|
28
|
+
|
|
29
|
+
-- Display the size of all database tables in order.
|
|
30
|
+
-- This will show each TimescaleDB chunk as a separate table.
|
|
31
|
+
SELECT
|
|
32
|
+
table_schema || '.' || table_name AS table_full_name,
|
|
33
|
+
pg_size_pretty(pg_total_relation_size('"' || table_schema || '"."' || table_name || '"')) AS size
|
|
34
|
+
FROM information_schema.tables
|
|
35
|
+
ORDER BY
|
|
36
|
+
pg_total_relation_size('"' || table_schema || '"."' || table_name || '"') DESC;
|
|
37
|
+
|
|
38
|
+
-- Display compression stats for each chunk in the readings table
|
|
39
|
+
SELECT
|
|
40
|
+
chunk_name,
|
|
41
|
+
pg_size_pretty(before_compression_total_bytes) AS size_before,
|
|
42
|
+
pg_size_pretty(after_compression_total_bytes) AS size_after,
|
|
43
|
+
100 - (after_compression_total_bytes::float / before_compression_total_bytes * 100) AS compression_ratio_pct
|
|
44
|
+
FROM chunk_compression_stats('gridworks.readings');
|
|
45
|
+
|
|
46
|
+
-- Get info (table, time range, etc.) about the TimescaleDB chunks.
|
|
47
|
+
SELECT * FROM timescaledb_information.chunks
|
|
48
|
+
|
|
49
|
+
-- Display the size in MB of our two main tables across all its chunks
|
|
50
|
+
SELECT
|
|
51
|
+
pg_size_pretty(hypertable_size('gridworks.readings')) as "Readings Table Size",
|
|
52
|
+
pg_size_pretty(hypertable_size('gridworks.messages')) as "Messages Table Size";
|
|
53
|
+
|
|
54
|
+
-- Detailed size for a full hypertable across all its chunks
|
|
55
|
+
SELECT
|
|
56
|
+
pg_size_pretty(table_bytes) AS data_size,
|
|
57
|
+
pg_size_pretty(index_bytes) AS index_size,
|
|
58
|
+
pg_size_pretty(toast_bytes) AS toast_size,
|
|
59
|
+
pg_size_pretty(total_bytes) AS total_size
|
|
60
|
+
FROM hypertable_detailed_size('gridworks.readings');
|
|
61
|
+
|
|
62
|
+
-- Info about timescaledb jobs
|
|
63
|
+
select * from timescaledb_information.jobs;
|
|
64
|
+
|
|
65
|
+
-- Manually run a job (e.g. run compression after an import).
|
|
66
|
+
-- Fill in job_id based on the query above
|
|
67
|
+
CALL run_job(job_id);
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
|
gw_data-0.3.0/postgres_views.md
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
CREATE VIEW readings_pretty AS
|
|
2
|
-
SELECT
|
|
3
|
-
dc.name AS name,
|
|
4
|
-
r.value AS value,
|
|
5
|
-
dc.telemetry_name AS telemetry_name,
|
|
6
|
-
timestamp AS time,
|
|
7
|
-
r.message_id AS message_id
|
|
8
|
-
FROM
|
|
9
|
-
readings r
|
|
10
|
-
JOIN
|
|
11
|
-
data_channels dc
|
|
12
|
-
ON
|
|
13
|
-
r.data_channel_id = dc.id;
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
SELECT * FROM preadings WHERE name LIKE '%buffer%' LIMIT 5;
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
CREATE OR REPLACE VIEW msg_pretty AS
|
|
21
|
-
SELECT
|
|
22
|
-
payload, from_alias, message_type_name, persisted_at AS message_persisted, created_at AS message_created
|
|
23
|
-
FROM messages;
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
SELECT COUNT(*) from messages;
|
|
29
|
-
|
|
30
|
-
SELECT COUNT(*) FROM messages WHERE message_type_name = 'power.watts';
|
|
31
|
-
|
|
32
|
-
SELECT from_alias, message_type_name, message_created FROM msg_pretty WHERE message_type_name LIKE '%status%' LIMIT 5;
|
|
33
|
-
|
|
34
|
-
SELECT pg_size_pretty(pg_database_size('gridworks')) AS size;
|
|
35
|
-
SELECT * FROM msg_pretty WHERE message_type_name LIKE '%param%';
|
|
36
|
-
|
|
37
|
-
SELECT id, from_alias, message_type_name, persisted_at FROM messages WHERE message_type_name LIKE '%param%';
|
|
38
|
-
|
|
39
|
-
DELETE FROM messages WHERE id = 'bf06df07-36a5-4e54-ae64-37dea6041ea8';
|
|
40
|
-
|
|
41
|
-
SELECT MIN(time) AS earliest, MAX(time) AS latest FROM readings_pretty;
|
|
42
|
-
SELECT MIN(message_created) AS earliest, MAX(message_created) AS latest FROM msg_pretty;
|
|
43
|
-
|
|
44
|
-
SELECT from_alias, message_type_name, message_created FROM msg_pretty WHERE message_type_name LIKE '%batched%' LIMIT 5;
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{gw_data-0.3.0 → gw_data-0.3.2}/src/gw_data/sema/definitions/enums/base.g.node.class/000.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{gw_data-0.3.0 → gw_data-0.3.2}/src/gw_data/sema/definitions/types/position.point.gt/000.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|