Automatische DWD Abfrage
Crontab hinzugefügt und automatische DWD Abfrage jeden Monat eingerichtet.
This commit is contained in:
3
.crontab
Normal file
3
.crontab
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Jeden Monat müssen wir die neuen Klimafaktoren vom DWD abholen, der Cronjob läuft immer am 28. für die höchste Wahrscheinlichkeit
|
||||||
|
# dass die Daten schon da sind, falls der DWD mal später dran ist...
|
||||||
|
0 12 28 * * bun ./src/cronjobs/update-dwd-klimafaktoren.ts
|
||||||
4
build.sh
4
build.sh
@@ -52,3 +52,7 @@ docker run -d --name $APP_NAME --link $DB_CONTAINER_NAME \
|
|||||||
-e DB_PORT=${DB_PORT} \
|
-e DB_PORT=${DB_PORT} \
|
||||||
--env-file ~/apps/${APP_NAME}/.env \
|
--env-file ~/apps/${APP_NAME}/.env \
|
||||||
$APP_NAME;
|
$APP_NAME;
|
||||||
|
|
||||||
|
# Crontab Updaten
|
||||||
|
cd ~/$APP_NAME
|
||||||
|
crontab .crontab
|
||||||
@@ -9,24 +9,14 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
PORT: 3000
|
PORT: 3000
|
||||||
NODE_ENV: "development"
|
NODE_ENV: "development"
|
||||||
ports:
|
network_mode: host
|
||||||
- 3000:3000
|
|
||||||
volumes:
|
volumes:
|
||||||
- ./:/online-energieausweis
|
- ./:/online-energieausweis
|
||||||
- ./node_modules/@ibcornelsen/ui:/online-energieausweis/node_modules/@ibcornelsen/ui
|
- ./node_modules/@ibcornelsen/ui:/online-energieausweis/node_modules/@ibcornelsen/ui
|
||||||
- ./node_modules/@ibcornelsen/database:/online-energieausweis/node_modules/@ibcornelsen/database
|
- ./node_modules/@ibcornelsen/database:/online-energieausweis/node_modules/@ibcornelsen/database
|
||||||
- ./persistent:/persistent
|
- ./persistent:/persistent
|
||||||
networks:
|
|
||||||
- postgres
|
|
||||||
database:
|
database:
|
||||||
|
network_mode: host
|
||||||
build: ../database
|
build: ../database
|
||||||
env_file:
|
env_file:
|
||||||
- ../database/.env
|
- ../database/.env
|
||||||
ports:
|
|
||||||
- "5432:5432"
|
|
||||||
networks:
|
|
||||||
- postgres
|
|
||||||
|
|
||||||
networks:
|
|
||||||
postgres:
|
|
||||||
driver: bridge
|
|
||||||
@@ -28,6 +28,7 @@
|
|||||||
"astro-i18next": "1.0.0-beta.21",
|
"astro-i18next": "1.0.0-beta.21",
|
||||||
"bun": "^1.0.2",
|
"bun": "^1.0.2",
|
||||||
"cookiejs": "^2.1.2",
|
"cookiejs": "^2.1.2",
|
||||||
|
"csvtojson": "^2.0.10",
|
||||||
"esbuild": "^0.18.17",
|
"esbuild": "^0.18.17",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
"flag-icons": "^6.9.2",
|
"flag-icons": "^6.9.2",
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
import DaemmungImage from "./DaemmungImage.svelte";
|
import DaemmungImage from "./DaemmungImage.svelte";
|
||||||
import FensterImage from "./FensterImage.svelte";
|
import FensterImage from "./FensterImage.svelte";
|
||||||
import Label from "../Label.svelte";
|
import Label from "../Label.svelte";
|
||||||
import { Verbrauchsausweis } from "src/lib/Ausweis/Verbrauchsausweis";
|
|
||||||
import { GebaeudeStammdaten, VerbrauchsausweisWohnen } from "@ibcornelsen/database";
|
import { GebaeudeStammdaten, VerbrauchsausweisWohnen } from "@ibcornelsen/database";
|
||||||
|
|
||||||
export let gebaeude: GebaeudeStammdaten;
|
export let gebaeude: GebaeudeStammdaten;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import HelpLabel from "../HelpLabel.svelte";
|
import HelpLabel from "../HelpLabel.svelte";
|
||||||
import moment, { Moment } from "moment";
|
import moment from "moment";
|
||||||
import Label from "../Label.svelte";
|
import Label from "../Label.svelte";
|
||||||
import fuelList from "./fuelList";
|
import fuelList from "./fuelList";
|
||||||
import { auditVerbrauchAbweichung } from "../Verbrauchsausweis/audits/VerbrauchAbweichung";
|
import { auditVerbrauchAbweichung } from "../Verbrauchsausweis/audits/VerbrauchAbweichung";
|
||||||
|
|||||||
73
src/cronjobs/update-dwd-klimafaktoren.ts
Normal file
73
src/cronjobs/update-dwd-klimafaktoren.ts
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
import { prisma } from "@ibcornelsen/database";
|
||||||
|
import moment from "moment";
|
||||||
|
import csv from "csvtojson"
|
||||||
|
|
||||||
|
// Als erstes schauen wir, welches das letzte Jahr ist, für das wir einen Verbrauchsausweis haben.
|
||||||
|
// Das machen wir, indem wir die Ausweise nach Jahr und Monat sortieren und dann den letzten Eintrag nehmen.
|
||||||
|
const newestDate = await prisma.klimafaktoren.findFirst({
|
||||||
|
orderBy: [
|
||||||
|
{
|
||||||
|
year: "desc",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
month: "desc",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!newestDate) {
|
||||||
|
console.error("No last year found!")
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Jetzt müssen wir abfragen, welche neuen Dateien es beim Deutschen Wetterdienst gibt.
|
||||||
|
// https://opendata.dwd.de/climate_environment/CDC/derived_germany/techn/monthly/climate_correction_factor/recent/
|
||||||
|
// Weil die Antwort in HTML ist und wir keinen Bock haben das alles zu parsen, versuchen wir einfach die neueste Datei abzufragen.
|
||||||
|
// Das format des DWD ist wie folgt: KF_20221101_20231031.csv
|
||||||
|
// Der Monat bei moment und in der Datenbank ist nullbasiert, also 0 = Januar, 11 = Dezember.
|
||||||
|
const currentDate = moment().set("month", newestDate.month).set("year", newestDate.year).set("date", 1);
|
||||||
|
|
||||||
|
|
||||||
|
const lastYearString = currentDate.clone().add(1, "month").startOf("month").format("YYYYMMDD");
|
||||||
|
const nextYearString = currentDate.clone().add(1, "year").endOf("month").format("YYYYMMDD");
|
||||||
|
|
||||||
|
const filename = `KF_${lastYearString}_${nextYearString}.csv`
|
||||||
|
|
||||||
|
const url = "https://opendata.dwd.de/climate_environment/CDC/derived_germany/techn/monthly/climate_correction_factor/recent/"
|
||||||
|
|
||||||
|
const response = await fetch(`${url}${filename}`)
|
||||||
|
|
||||||
|
if (response.status !== 200) {
|
||||||
|
console.error(`Could not fetch ${url}${filename}`)
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
const parser = csv({
|
||||||
|
noheader: false,
|
||||||
|
output: "json",
|
||||||
|
delimiter: ";",
|
||||||
|
})
|
||||||
|
|
||||||
|
const data: {
|
||||||
|
DatAnf: string,
|
||||||
|
DatEnd: string,
|
||||||
|
PLZ: string,
|
||||||
|
KF: string
|
||||||
|
}[] = await parser.fromString(await response.text())
|
||||||
|
|
||||||
|
const year = currentDate.clone().add(1, "month").year();
|
||||||
|
const month = currentDate.clone().add(1, "month").month();
|
||||||
|
|
||||||
|
const result = await prisma.klimafaktoren.createMany({
|
||||||
|
data: data.map((row) => {
|
||||||
|
return {
|
||||||
|
year,
|
||||||
|
month,
|
||||||
|
klimafaktor: parseFloat(row.KF),
|
||||||
|
plz: row.PLZ,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
console.log(`Updated ${result.count} rows!`)
|
||||||
@@ -1,40 +1,33 @@
|
|||||||
import moment, { Moment } from "moment";
|
import moment, { Moment } from "moment";
|
||||||
import { prisma } from "@ibcornelsen/database";
|
import { Klimafaktoren, prisma } from "@ibcornelsen/database";
|
||||||
|
|
||||||
export async function getClimateFactor(dates: Moment[], zip: string): Promise<({ zip: string } & Record<string, number>) | null> {
|
|
||||||
const formattedDates = dates.map(date => {
|
|
||||||
let d = moment(date).format("MM_YYYY");
|
|
||||||
return `d_${d}`
|
|
||||||
});
|
|
||||||
|
|
||||||
|
export async function getClimateFactor(
|
||||||
|
dates: Moment[],
|
||||||
|
plz: string
|
||||||
|
): Promise<Klimafaktoren[] | null> {
|
||||||
if (dates.length == 0) {
|
if (dates.length == 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (zip.length !== 5 && zip.length !== 4) {
|
|
||||||
return null;
|
if (plz.length !== 5 && plz.length !== 4) {
|
||||||
}
|
|
||||||
|
|
||||||
let result: ({ zip: string } & Record<string, number>) | null = null;
|
|
||||||
try {
|
|
||||||
result = await prisma.klimafaktoren.findUnique({
|
|
||||||
select: {
|
|
||||||
zip: true,
|
|
||||||
...formattedDates.reduce((acc, cur) => {
|
|
||||||
acc[cur] = true;
|
|
||||||
return acc;
|
|
||||||
}, {} as Record<string, boolean>)
|
|
||||||
},
|
|
||||||
where: {
|
|
||||||
zip
|
|
||||||
}
|
|
||||||
}) as ({ zip: string } & Record<string, number>) | null;
|
|
||||||
} catch(e) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let result = await prisma.klimafaktoren.findMany({
|
||||||
|
where: {
|
||||||
|
plz: plz,
|
||||||
|
month: dates[0].month(),
|
||||||
|
OR: dates.map((date) => {
|
||||||
|
return {
|
||||||
|
year: date.year(),
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { APIRoute } from "astro";
|
import type { APIRoute } from "astro";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import { ActionFailedError, InvalidDataError, MissingPropertyError, error, success } from "src/lib/APIResponse";
|
import { ActionFailedError, MissingPropertyError, error, success } from "src/lib/APIResponse";
|
||||||
import { getClimateFactor } from "src/lib/Klimafaktoren/getClimateFactor";
|
import { getClimateFactor } from "src/lib/Klimafaktoren/getClimateFactor";
|
||||||
|
|
||||||
export const get: APIRoute = async function({ url }) {
|
export const get: APIRoute = async function({ url }) {
|
||||||
@@ -45,11 +45,19 @@ export const get: APIRoute = async function({ url }) {
|
|||||||
currentDate.add(1, accuracy);
|
currentDate.add(1, accuracy);
|
||||||
}
|
}
|
||||||
|
|
||||||
const climateFactors = await getClimateFactor(intervals, zip);
|
const klimafaktoren = await getClimateFactor(intervals, zip);
|
||||||
|
|
||||||
if (!climateFactors) {
|
if (!klimafaktoren) {
|
||||||
return ActionFailedError();
|
return ActionFailedError();
|
||||||
}
|
}
|
||||||
|
|
||||||
return success(climateFactors);
|
if (klimafaktoren.length !== intervals.length) {
|
||||||
|
return error(["Not all dates could be found."]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return success(klimafaktoren.map(klimafaktor => ({
|
||||||
|
month: klimafaktor.month,
|
||||||
|
year: klimafaktor.year,
|
||||||
|
klimafaktor: klimafaktor.klimafaktor,
|
||||||
|
})));
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user