This commit is contained in:
1 parent
e1d22a26ed
commit
9e0230731b
10 files changed
+118
-223
No files matched your search
+8
-28
@@ -20,7 +20,6 @@ import pyarrow.parquet as pq
|
||||
|
||||
from .config import retention_cutoff, utc_text
|
||||
from .coverage import daily_coverage, milliseconds, sample_coverage
|
||||
from .history import normalize_history_sample
|
||||
|
||||
NUMBERS = (
|
||||
"production_w", "production_ac_w", "consumption_w", "grid_w",
|
||||
@@ -34,7 +33,6 @@ SCHEMA = pa.schema([
|
||||
*((field, pa.float64()) for field in NUMBERS),
|
||||
("boiler_enabled", pa.bool_()), ("boiler_reason", pa.string()),
|
||||
("record_kind", pa.string()),
|
||||
("interval_energy_wh", pa.struct([(field, pa.float64()) for field in ("production", "consumption", "self_consumption", "bought", "sold")])),
|
||||
("power_ms", pa.int64()), ("power_ok", pa.bool_()),
|
||||
("meter_ms", pa.int64()), ("meter_ok", pa.bool_()),
|
||||
("boiler_ms", pa.int64()), ("boiler_ok", pa.bool_()),
|
||||
@@ -82,9 +80,13 @@ class Store:
|
||||
if not path.exists():
|
||||
return []
|
||||
parquet = pq.ParquetFile(path, page_checksum_verification=True)
|
||||
if not parquet.schema_arrow.equals(SCHEMA, check_metadata=True):
|
||||
schema = parquet.schema_arrow
|
||||
# Validate the fields this version uses; read only those columns.
|
||||
if schema.metadata != SCHEMA.metadata or any(
|
||||
schema.get_field_index(field.name) < 0 or schema.field(field.name) != field for field in SCHEMA
|
||||
):
|
||||
raise StoreError("Unsupported Parquet archive schema")
|
||||
return parquet.read(columns=columns).to_pylist()
|
||||
return parquet.read(columns=SCHEMA.names if columns is None else columns).to_pylist()
|
||||
|
||||
@staticmethod
|
||||
def _sync_directory(path):
|
||||
@@ -125,8 +127,7 @@ class Store:
|
||||
"payload": json.dumps(sample, allow_nan=False, separators=(",", ":")),
|
||||
**{name: readings.get(name) for name in NUMBERS},
|
||||
"boiler_enabled": readings.get("boiler_enabled"), "boiler_reason": readings.get("boiler_reason"),
|
||||
"record_kind": "history" if "history_interval" in sample else "live",
|
||||
"interval_energy_wh": sample.get("history_interval", {}).get("energy_wh"),
|
||||
"record_kind": "history" if "solarweb_history" in sample.get("raw", {}) else "live",
|
||||
**{name: bool(value) if name.endswith("_ok") else value
|
||||
for name, value in zip(COVERAGE_COLUMNS[1:], coverage[3:])},
|
||||
}
|
||||
@@ -137,32 +138,11 @@ class Store:
|
||||
rows = self._read(path)
|
||||
for existing in rows:
|
||||
if existing["id"] == row["id"]:
|
||||
if normalize_history_sample(json.loads(existing["payload"])) != normalize_history_sample(sample):
|
||||
if json.loads(existing["payload"]) != sample:
|
||||
raise StoreError("Sample ID already contains different data")
|
||||
return
|
||||
self._write(path, [*rows, row])
|
||||
|
||||
def normalize_history(self):
|
||||
"""Backfill common power columns and queued payloads without changing IDs or acknowledgements."""
|
||||
changed = 0
|
||||
with self._locked(write=True):
|
||||
for path in self._files():
|
||||
rows = self._read(path)
|
||||
dirty = False
|
||||
for index, row in enumerate(rows):
|
||||
if row["record_kind"] != "history":
|
||||
continue
|
||||
sample = json.loads(row["payload"])
|
||||
normalized = normalize_history_sample(sample)
|
||||
if normalized == sample:
|
||||
continue
|
||||
rows[index] = {**self._row(normalized, row["uploaded_at"]), "recorded_ns": row["recorded_ns"]}
|
||||
changed += 1
|
||||
dirty = True
|
||||
if dirty:
|
||||
self._write(path, rows)
|
||||
return changed
|
||||
|
||||
def pending(self, limit):
|
||||
with self._locked():
|
||||
rows = []
|
||||
|
||||
Reference in new issue
Block a user