Automatische DWD Abfrage

Crontab hinzugefügt und automatische DWD Abfrage jeden Monat eingerichtet.
This commit is contained in:
Moritz Utcke
2024-01-06 15:42:59 +07:00
parent 30cc5fab63
commit dfd7cce6c8
9 changed files with 117 additions and 46 deletions

3
.crontab Normal file
View 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

View File

@@ -52,3 +52,7 @@ docker run -d --name $APP_NAME --link $DB_CONTAINER_NAME \
-e DB_PORT=${DB_PORT} \
--env-file ~/apps/${APP_NAME}/.env \
$APP_NAME;
# Crontab Updaten
cd ~/$APP_NAME
crontab .crontab

View File

@@ -9,24 +9,14 @@ services:
environment:
PORT: 3000
NODE_ENV: "development"
ports:
- 3000:3000
network_mode: host
volumes:
- ./:/online-energieausweis
- ./node_modules/@ibcornelsen/ui:/online-energieausweis/node_modules/@ibcornelsen/ui
- ./node_modules/@ibcornelsen/database:/online-energieausweis/node_modules/@ibcornelsen/database
- ./persistent:/persistent
networks:
- postgres
database:
network_mode: host
build: ../database
env_file:
- ../database/.env
ports:
- "5432:5432"
networks:
- postgres
networks:
postgres:
driver: bridge
- ../database/.env

View File

@@ -28,6 +28,7 @@
"astro-i18next": "1.0.0-beta.21",
"bun": "^1.0.2",
"cookiejs": "^2.1.2",
"csvtojson": "^2.0.10",
"esbuild": "^0.18.17",
"express": "^4.18.2",
"flag-icons": "^6.9.2",

View File

@@ -5,7 +5,6 @@
import DaemmungImage from "./DaemmungImage.svelte";
import FensterImage from "./FensterImage.svelte";
import Label from "../Label.svelte";
import { Verbrauchsausweis } from "src/lib/Ausweis/Verbrauchsausweis";
import { GebaeudeStammdaten, VerbrauchsausweisWohnen } from "@ibcornelsen/database";
export let gebaeude: GebaeudeStammdaten;

View File

@@ -1,6 +1,6 @@
<script lang="ts">
import HelpLabel from "../HelpLabel.svelte";
import moment, { Moment } from "moment";
import moment from "moment";
import Label from "../Label.svelte";
import fuelList from "./fuelList";
import { auditVerbrauchAbweichung } from "../Verbrauchsausweis/audits/VerbrauchAbweichung";

View 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!`)

View File

@@ -1,40 +1,33 @@
import moment, { Moment } from "moment";
import { 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}`
});
import { Klimafaktoren, prisma } from "@ibcornelsen/database";
export async function getClimateFactor(
dates: Moment[],
plz: string
): Promise<Klimafaktoren[] | null> {
if (dates.length == 0) {
return null;
}
if (zip.length !== 5 && zip.length !== 4) {
return null;
}
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) {
if (plz.length !== 5 && plz.length !== 4) {
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) {
return null;
}
return result;
}
}

View File

@@ -1,6 +1,6 @@
import type { APIRoute } from "astro";
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";
export const get: APIRoute = async function({ url }) {
@@ -45,11 +45,19 @@ export const get: APIRoute = async function({ url }) {
currentDate.add(1, accuracy);
}
const climateFactors = await getClimateFactor(intervals, zip);
const klimafaktoren = await getClimateFactor(intervals, zip);
if (!climateFactors) {
if (!klimafaktoren) {
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,
})));
}