Add date-parameterized sync with production and consumption tables
- Accept ?date= param on POST /eloverblik/sync (defaults to today) - Populate production table from D06 meter (571313113163368491) - Populate consumption table from D07 meter (571313113163368507) - ON CONFLICT DO NOTHING to skip existing data Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -47,3 +47,15 @@ INSERT INTO charges (metering_point_id, charge_type, name, description, owner,
|
|||||||
VALUES (:metering-point-id, :charge-type, :name, :description, :owner,
|
VALUES (:metering-point-id, :charge-type, :name, :description, :owner,
|
||||||
:valid-from-date, :valid-to-date, :period-type, :price, :quantity, :position)
|
:valid-from-date, :valid-to-date, :period-type, :price, :quantity, :position)
|
||||||
ON CONFLICT (metering_point_id, charge_type, name, owner, valid_from_date, position) DO NOTHING;
|
ON CONFLICT (metering_point_id, charge_type, name, owner, valid_from_date, position) DO NOTHING;
|
||||||
|
|
||||||
|
-- :name insert-production! :! :n
|
||||||
|
-- :doc Insert a production record, skip if already exists
|
||||||
|
INSERT INTO production (time_start, hour, kwh)
|
||||||
|
VALUES (:time-start, :hour, :kwh)
|
||||||
|
ON CONFLICT (time_start, hour) DO NOTHING;
|
||||||
|
|
||||||
|
-- :name insert-consumption! :! :n
|
||||||
|
-- :doc Insert a consumption record, skip if already exists
|
||||||
|
INSERT INTO consumption (time_start, hour, kwh)
|
||||||
|
VALUES (:time-start, :hour, :kwh)
|
||||||
|
ON CONFLICT (time_start, hour) DO NOTHING;
|
||||||
|
|||||||
@@ -145,10 +145,30 @@
|
|||||||
results
|
results
|
||||||
metering-point-ids)))
|
metering-point-ids)))
|
||||||
|
|
||||||
(defn- save-time-series! [query-fn readings]
|
(def ^:private production-mp-id "571313113163368491")
|
||||||
|
(def ^:private consumption-mp-id "571313113163368507")
|
||||||
|
|
||||||
|
(defn- utc-start-for-date
|
||||||
|
"Compute the UTC start of a Danish date (midnight DK -> UTC)."
|
||||||
|
[^LocalDate date]
|
||||||
|
(-> date
|
||||||
|
(.atStartOfDay dk-zone)
|
||||||
|
(.toInstant)
|
||||||
|
(Timestamp/from)))
|
||||||
|
|
||||||
|
(defn- save-time-series! [query-fn readings from]
|
||||||
(doseq [r readings]
|
(doseq [r readings]
|
||||||
(query-fn :insert-meter-reading! r))
|
(query-fn :insert-meter-reading! r))
|
||||||
(log/info "Saved" (count readings) "meter readings"))
|
(let [time-start (utc-start-for-date from)]
|
||||||
|
(doseq [r (filter #(= production-mp-id (:metering-point-id %)) readings)]
|
||||||
|
(let [hour (.getHour (.toLocalDateTime ^Timestamp (:time-dk r)))]
|
||||||
|
(query-fn :insert-production!
|
||||||
|
{:time-start time-start :hour hour :kwh (:quantity-kwh r)})))
|
||||||
|
(doseq [r (filter #(= consumption-mp-id (:metering-point-id %)) readings)]
|
||||||
|
(let [hour (.getHour (.toLocalDateTime ^Timestamp (:time-dk r)))]
|
||||||
|
(query-fn :insert-consumption!
|
||||||
|
{:time-start time-start :hour hour :kwh (:quantity-kwh r)}))))
|
||||||
|
(log/info "Saved" (count readings) "meter readings + production/consumption"))
|
||||||
|
|
||||||
;; --- Charges ---
|
;; --- Charges ---
|
||||||
|
|
||||||
@@ -223,9 +243,10 @@
|
|||||||
;; --- Orchestrator ---
|
;; --- Orchestrator ---
|
||||||
|
|
||||||
(defn fetch-and-save-all!
|
(defn fetch-and-save-all!
|
||||||
"Fetch all Eloverblik data and save to database."
|
"Fetch all Eloverblik data and save to database.
|
||||||
[query-fn]
|
date is a LocalDate for which to fetch time series (one day)."
|
||||||
(log/info "Starting Eloverblik fetch")
|
[query-fn ^LocalDate date]
|
||||||
|
(log/info "Starting Eloverblik fetch for" (.format date date-fmt))
|
||||||
(let [refresh-token (read-refresh-token)
|
(let [refresh-token (read-refresh-token)
|
||||||
access-token (get-access-token refresh-token)]
|
access-token (get-access-token refresh-token)]
|
||||||
(if-not access-token
|
(if-not access-token
|
||||||
@@ -239,14 +260,14 @@
|
|||||||
(:childMeteringPoints mp)))))
|
(:childMeteringPoints mp)))))
|
||||||
(distinct)
|
(distinct)
|
||||||
(vec))
|
(vec))
|
||||||
from (LocalDate/of 2026 2 1)
|
from date
|
||||||
to (LocalDate/of 2026 2 2)]
|
to (.plusDays date 1)]
|
||||||
(log/info "Found" (count all-ids) "metering points:" all-ids)
|
(log/info "Found" (count all-ids) "metering points:" all-ids)
|
||||||
(save-metering-points! query-fn mps)
|
(save-metering-points! query-fn mps)
|
||||||
;; Time series
|
;; Time series
|
||||||
(let [ts-resp (fetch-time-series access-token all-ids from to)
|
(let [ts-resp (fetch-time-series access-token all-ids from to)
|
||||||
readings (parse-time-series ts-resp all-ids)]
|
readings (parse-time-series ts-resp all-ids)]
|
||||||
(save-time-series! query-fn readings))
|
(save-time-series! query-fn readings from))
|
||||||
;; Charges
|
;; Charges
|
||||||
(let [ch-resp (fetch-charges access-token all-ids)]
|
(let [ch-resp (fetch-charges access-token all-ids)]
|
||||||
(save-charges! query-fn ch-resp))
|
(save-charges! query-fn ch-resp))
|
||||||
|
|||||||
@@ -176,7 +176,11 @@
|
|||||||
(price-content :today (prices/get-today-prices query-fn) (has-tomorrow? query-fn))))
|
(price-content :today (prices/get-today-prices query-fn) (has-tomorrow? query-fn))))
|
||||||
|
|
||||||
(defn sync-eloverblik [query-fn request]
|
(defn sync-eloverblik [query-fn request]
|
||||||
(let [result (eloverblik/fetch-and-save-all! query-fn)]
|
(let [date-str (get-in request [:params :date])
|
||||||
|
date (if date-str
|
||||||
|
(java.time.LocalDate/parse date-str)
|
||||||
|
(java.time.LocalDate/now dk-zone))
|
||||||
|
result (eloverblik/fetch-and-save-all! query-fn date)]
|
||||||
(fragment
|
(fragment
|
||||||
(if (:error result)
|
(if (:error result)
|
||||||
[:p {:class "text-red-600 font-medium"} (:error result)]
|
[:p {:class "text-red-600 font-medium"} (:error result)]
|
||||||
|
|||||||
Reference in New Issue
Block a user