From ff1629a68f55859a0c229fcaa96cbf88629d99b7 Mon Sep 17 00:00:00 2001 From: SiemensHalske <140380689+SiemensHalske@users.noreply.github.com> Date: Mon, 17 Jun 2024 20:35:31 +0200 Subject: [PATCH 1/3] chore: Update CSS styles for responsive design on mobile devices --- .../src/app/dashboard/radar/Radar.module.css | 34 +++++ frontend/src/app/dashboard/radar/WMSLayer.tsx | 8 +- frontend/src/app/dashboard/radar/page.tsx | 61 ++++++++- .../account-settings.module.css | 16 +++ .../src/app/profile/account-settings/page.tsx | 84 +++++++++++++ .../edit-profile/edit-profile.module.css | 16 +++ .../src/app/profile/edit-profile/page.tsx | 101 +++++++++++++++ .../notification-settings.module.css | 14 +++ .../profile/notification-settings/page.tsx | 104 ++++++++++++++++ frontend/src/app/profile/page.tsx | 117 ++++++++++++++++++ frontend/src/app/profile/profile.module.css | 13 ++ .../app/profile/security-settings/page.tsx | 102 +++++++++++++++ .../security-settings.module.css | 18 +++ frontend/src/components/Navbar/Navbar.tsx | 16 +++ 14 files changed, 698 insertions(+), 6 deletions(-) create mode 100644 frontend/src/app/profile/account-settings/account-settings.module.css create mode 100644 frontend/src/app/profile/account-settings/page.tsx create mode 100644 frontend/src/app/profile/edit-profile/edit-profile.module.css create mode 100644 frontend/src/app/profile/edit-profile/page.tsx create mode 100644 frontend/src/app/profile/notification-settings/notification-settings.module.css create mode 100644 frontend/src/app/profile/notification-settings/page.tsx create mode 100644 frontend/src/app/profile/page.tsx create mode 100644 frontend/src/app/profile/profile.module.css create mode 100644 frontend/src/app/profile/security-settings/page.tsx create mode 100644 frontend/src/app/profile/security-settings/security-settings.module.css diff --git a/frontend/src/app/dashboard/radar/Radar.module.css b/frontend/src/app/dashboard/radar/Radar.module.css index fb85339..f6138ab 100644 --- a/frontend/src/app/dashboard/radar/Radar.module.css +++ b/frontend/src/app/dashboard/radar/Radar.module.css @@ -65,3 +65,37 @@ height: 600px; background: transparent; /* Ensure the background is transparent */ } + +.legendBox { + display: flex; + flex-direction: column; + align-items: center; + width: 90%; + max-width: 1200px; + margin: auto; + margin-top: 20px; + padding: 1rem; + border: 1px solid #ddd; + border-radius: 8px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + background-color: 0 4px 8px rgba(0, 0, 0, 0.8); + color: black; + font-size: 1rem; + line-height: 1.5; + text-align: center; + box-sizing: border-box; + overflow: hidden; + transition: max-height 0.5s; +} + +.legendTitle { + font-size: 1.5rem; + font-weight: bold; + margin-bottom: 0.5rem; +} + +.legendContent { + display: flex; + flex-wrap: wrap; + justify-content: center; +} diff --git a/frontend/src/app/dashboard/radar/WMSLayer.tsx b/frontend/src/app/dashboard/radar/WMSLayer.tsx index 2a04654..805b262 100644 --- a/frontend/src/app/dashboard/radar/WMSLayer.tsx +++ b/frontend/src/app/dashboard/radar/WMSLayer.tsx @@ -2,12 +2,12 @@ import { useEffect } from "react"; import { useMap } from "react-leaflet"; import L from "leaflet"; -const WMSTileLayer = ({ url, layers }: { url: string; layers: string }) => { +const WMSLayer = ({ url, layerName }: { url: string; layerName: string }) => { const map = useMap(); useEffect(() => { const wmsLayer = L.tileLayer.wms(url, { - layers, + layers: layerName, format: "image/png", transparent: true, attribution: "© DWD", @@ -18,9 +18,9 @@ const WMSTileLayer = ({ url, layers }: { url: string; layers: string }) => { return () => { map.removeLayer(wmsLayer); }; - }, [url, layers, map]); + }, [url, layerName, map]); return null; }; -export default WMSTileLayer; +export default WMSLayer; diff --git a/frontend/src/app/dashboard/radar/page.tsx b/frontend/src/app/dashboard/radar/page.tsx index d6e5813..e17c0af 100644 --- a/frontend/src/app/dashboard/radar/page.tsx +++ b/frontend/src/app/dashboard/radar/page.tsx @@ -4,7 +4,14 @@ import React, { useEffect, useState } from "react"; import { MapContainer, TileLayer, useMap } from "react-leaflet"; import L from "leaflet"; import "leaflet/dist/leaflet.css"; -import { FormControl, InputLabel, Select, MenuItem } from "@mui/material"; +import { + FormControl, + InputLabel, + Select, + MenuItem, + Typography, +} from "@mui/material"; +import Image from "next/image"; import styles from "./Radar.module.css"; // Custom component to add the WMS layer @@ -32,13 +39,53 @@ const WMSLayer = ({ url, layerName }: { url: string; layerName: string }) => { const RadarPage = () => { const [layerName, setLayerName] = useState("dwd:Niederschlagsradar"); + const legendContent = () => { + switch (layerName) { + case "dwd:Niederschlagsradar": + return ( + + {" "} + {/*-- Regenradar --*/} + Zeigt die Niederschlagsintensität in der Region.
+
+ Farbskala:
+ Blau: Leichter Niederschlag
+ Grün: Mäßiger Niederschlag
+ Gelb: Starker Niederschlag
+ Rot: Sehr starker Niederschlag
+ Violett: Extrem starker Niederschlag{" "} +
+ ); + case "dwd:Blitzdichte": + return ( + + {" "} + {/*-- Blitzdichte --*/} + Blitzradar: Zeigt die Blitzdichte in der Region. + + ); + case "dwd:Warnungen_Gemeinden": + return ( + + {" "} + {/*-- Warnungen Gemeinden --*/} + Warnungen Gemeinden: Zeigt aktuelle Wetterwarnungen für + Gemeinden. + + ); + default: + return null; + } + }; + return (
- Background Image

Radar

@@ -102,6 +149,16 @@ const RadarPage = () => { />
+
+
+ + Legende + +
+
+ {legendContent()} +
+
); diff --git a/frontend/src/app/profile/account-settings/account-settings.module.css b/frontend/src/app/profile/account-settings/account-settings.module.css new file mode 100644 index 0000000..ea10ec0 --- /dev/null +++ b/frontend/src/app/profile/account-settings/account-settings.module.css @@ -0,0 +1,16 @@ +.pageTitle { + margin-bottom: 16px; + text-align: center; + font-weight: bold; +} + +.form { + margin-top: 24px; +} + +.textField { + margin-bottom: 16px; + background-color: #f5f5f5; + padding: none; + border-radius: 4px; +} diff --git a/frontend/src/app/profile/account-settings/page.tsx b/frontend/src/app/profile/account-settings/page.tsx new file mode 100644 index 0000000..9a2ff11 --- /dev/null +++ b/frontend/src/app/profile/account-settings/page.tsx @@ -0,0 +1,84 @@ +"use client"; + +import * as React from "react"; +import { + Container, + Typography, + TextField, + Button, + Grid, + Box, +} from "@mui/material"; +import { styled } from "@mui/system"; +import styles from "./account-settings.module.css"; + +const AccountSettingsContainer = styled(Container)(({ theme }) => ({ + marginTop: theme.spacing(4), +})); + +const AccountSettingsPage: React.FC = () => { + const handleSubmit = (event: React.FormEvent) => { + event.preventDefault(); + // Handle form submission logic here + }; + + return ( + + + Account Settings + +
+ + + + + + + + + + + + + + + + + +
+
+ ); +}; + +export default AccountSettingsPage; diff --git a/frontend/src/app/profile/edit-profile/edit-profile.module.css b/frontend/src/app/profile/edit-profile/edit-profile.module.css new file mode 100644 index 0000000..ea10ec0 --- /dev/null +++ b/frontend/src/app/profile/edit-profile/edit-profile.module.css @@ -0,0 +1,16 @@ +.pageTitle { + margin-bottom: 16px; + text-align: center; + font-weight: bold; +} + +.form { + margin-top: 24px; +} + +.textField { + margin-bottom: 16px; + background-color: #f5f5f5; + padding: none; + border-radius: 4px; +} diff --git a/frontend/src/app/profile/edit-profile/page.tsx b/frontend/src/app/profile/edit-profile/page.tsx new file mode 100644 index 0000000..e0b37bb --- /dev/null +++ b/frontend/src/app/profile/edit-profile/page.tsx @@ -0,0 +1,101 @@ +"use client"; + +import * as React from "react"; +import { + Container, + Typography, + TextField, + Button, + Grid, + Box, + Avatar, +} from "@mui/material"; +import { styled } from "@mui/system"; +import styles from "./edit-profile.module.css"; + +const EditProfileContainer = styled(Container)(({ theme }) => ({ + marginTop: theme.spacing(4), +})); + +const ProfileAvatar = styled(Avatar)(({ theme }) => ({ + width: theme.spacing(15), + height: theme.spacing(15), + margin: "auto", + marginBottom: theme.spacing(2), +})); + +const EditProfilePage: React.FC = () => { + const handleSubmit = (event: React.FormEvent) => { + event.preventDefault(); + // Handle form submission logic here + }; + + return ( + + + Edit Profile + + + + + +
+ + + + + + + + + + + + + + + + + +
+
+ ); +}; + +export default EditProfilePage; diff --git a/frontend/src/app/profile/notification-settings/notification-settings.module.css b/frontend/src/app/profile/notification-settings/notification-settings.module.css new file mode 100644 index 0000000..712cb58 --- /dev/null +++ b/frontend/src/app/profile/notification-settings/notification-settings.module.css @@ -0,0 +1,14 @@ +.pageTitle { + margin-bottom: 16px; + text-align: center; + font-weight: bold; +} + +.form { + margin-top: 24px; +} + +.checkboxLabel { + margin-bottom: 16px; + display: block; +} diff --git a/frontend/src/app/profile/notification-settings/page.tsx b/frontend/src/app/profile/notification-settings/page.tsx new file mode 100644 index 0000000..85d206a --- /dev/null +++ b/frontend/src/app/profile/notification-settings/page.tsx @@ -0,0 +1,104 @@ +"use client"; + +import * as React from "react"; +import { + Container, + Typography, + FormControlLabel, + Checkbox, + Button, + Grid, + Box, +} from "@mui/material"; +import { styled } from "@mui/system"; +import styles from "./notification-settings.module.css"; + +const NotificationSettingsContainer = styled(Container)(({ theme }) => ({ + marginTop: theme.spacing(4), +})); + +const NotificationSettingsPage: React.FC = () => { + const [settings, setSettings] = React.useState({ + emailNotifications: true, + smsNotifications: false, + pushNotifications: true, + }); + + const handleChange = (event: React.ChangeEvent) => { + setSettings({ + ...settings, + [event.target.name]: event.target.checked, + }); + }; + + const handleSubmit = (event: React.FormEvent) => { + event.preventDefault(); + // Handle form submission logic here + }; + + return ( + + + Notification Settings + +
+ + + + } + label="Email Notifications" + className={styles.checkboxLabel} + /> + + + + } + label="SMS Notifications" + className={styles.checkboxLabel} + /> + + + + } + label="Push Notifications" + className={styles.checkboxLabel} + /> + + + + + +
+
+ ); +}; + +export default NotificationSettingsPage; diff --git a/frontend/src/app/profile/page.tsx b/frontend/src/app/profile/page.tsx new file mode 100644 index 0000000..0ddea4a --- /dev/null +++ b/frontend/src/app/profile/page.tsx @@ -0,0 +1,117 @@ +"use client"; + +import * as React from "react"; +import { + Container, + Typography, + Avatar, + Box, + Grid, + Paper, + Button, +} from "@mui/material"; +import { styled } from "@mui/system"; +import Link from "next/link"; +import styles from "./profile.module.css"; + +const ProfileContainer = styled(Container)(({ theme }) => ({ + marginTop: theme.spacing(4), + textAlign: "center", +})); + +const ProfileAvatar = styled(Avatar)(({ theme }) => ({ + width: theme.spacing(15), + height: theme.spacing(15), + margin: "auto", +})); + +const ProfileCard = styled(Paper)(({ theme }) => ({ + padding: theme.spacing(3), + textAlign: "center", + cursor: "pointer", + transition: "transform 0.2s", + "&:hover": { + transform: "scale(1.05)", + }, +})); + +const ProfilePage: React.FC = () => { + return ( + + + + John Doe + + + Software Engineer + + + + + + + + Edit Profile + + + Update your personal information and profile + picture. + + + + + + + + + Account Settings + + + Manage your account settings and password. + + + + + + + + + Notification Settings + + + Customize your notification preferences. + + + + + + + + + Security Settings + + + Enhance the security of your account. + + + + + + + + ); +}; + +export default ProfilePage; diff --git a/frontend/src/app/profile/profile.module.css b/frontend/src/app/profile/profile.module.css new file mode 100644 index 0000000..d1bb7d8 --- /dev/null +++ b/frontend/src/app/profile/profile.module.css @@ -0,0 +1,13 @@ +.profileName { + margin-top: 16px; + font-weight: bold; +} + +.profileTitle { + color: grey; +} + +.cardDescription { + margin-top: 8px; + color: grey; +} diff --git a/frontend/src/app/profile/security-settings/page.tsx b/frontend/src/app/profile/security-settings/page.tsx new file mode 100644 index 0000000..fdb8dfa --- /dev/null +++ b/frontend/src/app/profile/security-settings/page.tsx @@ -0,0 +1,102 @@ +"use client"; + +import * as React from "react"; +import { + Container, + Typography, + TextField, + Button, + Grid, + Box, + FormControlLabel, + Checkbox, +} from "@mui/material"; +import { styled } from "@mui/system"; +import styles from "./security-settings.module.css"; + +const SecuritySettingsContainer = styled(Container)(({ theme }) => ({ + marginTop: theme.spacing(4), +})); + +const SecuritySettingsPage: React.FC = () => { + const [settings, setSettings] = React.useState({ + twoFactorAuth: false, + }); + + const handleChange = (event: React.ChangeEvent) => { + setSettings({ + ...settings, + [event.target.name]: event.target.checked, + }); + }; + + const handleSubmit = (event: React.FormEvent) => { + event.preventDefault(); + // Handle form submission logic here + }; + + return ( + + + Security Settings + +
+ + + + } + label="Enable Two-Factor Authentication" + className={styles.checkboxLabel} + /> + + + + + + + + + + + + + + +
+
+ ); +}; + +export default SecuritySettingsPage; diff --git a/frontend/src/app/profile/security-settings/security-settings.module.css b/frontend/src/app/profile/security-settings/security-settings.module.css new file mode 100644 index 0000000..1eed541 --- /dev/null +++ b/frontend/src/app/profile/security-settings/security-settings.module.css @@ -0,0 +1,18 @@ +.pageTitle { + margin-bottom: 16px; + text-align: center; + font-weight: bold; +} + +.form { + margin-top: 24px; +} + +.textField { + margin-bottom: 16px; +} + +.checkboxLabel { + margin-bottom: 16px; + display: block; +} diff --git a/frontend/src/components/Navbar/Navbar.tsx b/frontend/src/components/Navbar/Navbar.tsx index 8b1388e..8e254c4 100755 --- a/frontend/src/components/Navbar/Navbar.tsx +++ b/frontend/src/components/Navbar/Navbar.tsx @@ -17,6 +17,10 @@ import { Brightness7, Menu, AccountCircle, + Home as HomeIcon, + Info as InfoIcon, + ContactMail as ContactIcon, + Dashboard as DashboardIcon, } from "@mui/icons-material"; import Link from "next/link"; import clsx from "clsx"; @@ -70,15 +74,27 @@ const Navbar: React.FC = ({ themeMode, toggleTheme }) => { + + + + + + + + + + + + From 50581be4d6cffdc0660569c0fa818923749205bc Mon Sep 17 00:00:00 2001 From: SiemensHalske Date: Tue, 18 Jun 2024 05:52:42 +0000 Subject: [PATCH 2/3] chore: Add API routes for sensors and handle GET and POST requests --- Images/sensors/sensor_mesh.drawio | 347 ++++++++++++++++++ backend/Scripts/calc_achteck.py | 48 +++ .../app/__pycache__/models.cpython-312.pyc | Bin 7961 -> 9061 bytes .../__pycache__/api.cpython-312.pyc | Bin 419 -> 3144 bytes backend/app/blueprints/api.py | 67 +++- backend/app/models.py | 36 ++ 6 files changed, 496 insertions(+), 2 deletions(-) create mode 100644 Images/sensors/sensor_mesh.drawio create mode 100644 backend/Scripts/calc_achteck.py diff --git a/Images/sensors/sensor_mesh.drawio b/Images/sensors/sensor_mesh.drawio new file mode 100644 index 0000000..e5953b8 --- /dev/null +++ b/Images/sensors/sensor_mesh.drawio @@ -0,0 +1,347 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/backend/Scripts/calc_achteck.py b/backend/Scripts/calc_achteck.py new file mode 100644 index 0000000..852f2ec --- /dev/null +++ b/backend/Scripts/calc_achteck.py @@ -0,0 +1,48 @@ +""" +This script calculates the vertices of a regular octagon given its center and radius. +""" + +import math + + +def octagon_vertices(center_x, center_y, radius): + """Calculates the vertices of a regular octagon given its center and radius.""" + + # 360 degrees / 8 vertices = 45 degrees per vertex + angle_increment = 2 * math.pi / 8 + vertices = [] + for i in range(8): + angle = i * angle_increment + x = center_x + radius * math.cos(angle) + y = center_y + radius * math.sin(angle) + vertices.append((x, y)) + return vertices + + +def main(): + """Prompts the user for center coordinates and radius, then prints the vertices.""" + + while True: + try: + center_input = input("Enter the center coordinates (x,y): ") + center_x, center_y = map(float, center_input.split(",")) + + radius = float(input("Enter the radius of the octagon: ")) + + if radius <= 0: + raise ValueError("Radius must be positive.") + + break # Exit the loop if the input is valid + except ValueError: + print( + "Invalid input. Please enter coordinates in the format x,y and a positive radius.") + + vertices = octagon_vertices(center_x, center_y, radius) + + print("\nThe vertices of the octagon are:") + for vertex in vertices: + print(f"({vertex[0]:.2f}, {vertex[1]:.2f})") + + +if __name__ == "__main__": + main() diff --git a/backend/app/__pycache__/models.cpython-312.pyc b/backend/app/__pycache__/models.cpython-312.pyc index 919ea2650ffd65c7151aea789240aa23578cd0ce..11e8a82d144651726530dc9ac3fa14a2de7be5c0 100644 GIT binary patch delta 1474 zcmZ`(PiP!f7=LefXJ>Zm>?WIJvwu3P2%9kGblhECw_^w4zTU}1hRoCH`{8Co1ziGh^_i#xQ| zXeHK`-)@?=snyMP$FLOpV?&uYnpTS`jh1fItFu2O{gf4WR8#jQ%V3bKHdh;q2J??a zk*3x#e4(z{roE~geyZMTeiSZ+){UmYptQpK3CpN2vne>9y@v2Q!ZgAg2%R?pE#yE6 z2g}3S7gW`RE2?Tc6W*+P=y(hBs{rfdeqo}U*i3u!*=}M_D0-#SIOn~*0&dBRqG)|7 zv2iGQf@=XBanb>@zlYT=VjodUf$BI=$~nm&>D?rRVcg{H0o^MDa>E$n%Hu*tZil}c z?MlhBtAnNq%2VzgzSyC};pYiRRGv3%OVN~&Y{hH_GU=MFEozogWs^`;;n*uMF%;hC zEyJ?RR@0Aby6z{JT8)NjR|IwmGya!D_k}`F zDC`NP`#PmR>ffBGYvWoxjU=skI1X+Rk=>gt>Hb*h=K0_ymeEncF4>4?jr-nn^ z-I39bPQOc{2(tmpA=NY>(!#4n)bMso)y*ZlosM{k>_teiE&^bO%D2ngWlui2FQ4zp z=e^R2{nEK!>7188ep}jx9u4E6^{>Pai4O=bro^CKXDj1wPdHNx)ffsAapvUbrGuG8dO$_D zIP%N$Qo(vGfZQTekQggiZ1OrWImQE%&x##nYINN^Up$^E$N{9p5kxqF2#_te*b`Gy zic*VNFAKB*KY Z#Kq{x*b&qp(HZfD0mS|yHQ7Mf6ac}vZtVa7 diff --git a/backend/app/blueprints/__pycache__/api.cpython-312.pyc b/backend/app/blueprints/__pycache__/api.cpython-312.pyc index 0ef3be8380df1dac2aed6e337e5b9f8023a7a98d..d329227c595ddb7217aaa5dd63d6c53e0705df6e 100644 GIT binary patch literal 3144 zcmai0Uu+b|8K2$z|IcUN;h11!tuab{Qn+h^94FKzfnqxl2Pig9d%`-cZ|06Y_I9r` zd$I3yj+_c9q>6;|(BepuVzqf7f~r)i)IRpHRbTcvTBH?KRfS6B%|N6!JoTI1+w<8C zVWgdz-S6*w-#5Q+{}PLZ5sb5+R|;PS5&Dw9xFgWsJnj(?x`r^yA}nB070O~(EPJvZ zfxmlHZ`qgimHkP$nle6)s$V7I@M7v~qpCS{(LEKXmyDY_tNJ7K|PhUZN9&EZZ?!D~o z_>ider4sT*2uk?a*^|<7wMr^f(Twn+?h&59s4!{CB}(v8WduuxE@4toG$I)niNqC9 zy=o996?AHUk}DNe$;*bKYiUs8@a)h-Nl{@2p`cCCTp6h#=MA0Kx;|xcIYm>9TrORy zNd-kE>2UZ5RZ59wNat*$N5Y4sj9gK|5)9b7EBIrCZR2g{WJ=m@V@c+|s;a)sc8SkS z%^#7@5u-{qX1m1+d+BP$`5y$XTCFjAB1K|fe{VCD%o!1~LmT$gVQAx^U zg*vgf0wtzvN}*;2DEUbhq*8(vJV#hX*BF7le~xHOrLrplSZ4ms zGv_i(a{dz0FoeF6=_p`46Oby)r*K;|0g^-TFn$r&(3T%X2WcpnYn&-7X1(fc-+ zEidP={d6X-o%X?Gd3kndKWq=c)8#_KaUqytc0ifsJ$q(u-ttpB?>l_*futSBQ*h<4 zAp>5WQEEeg`_5M^Tp9!8(*_T^JxYFex4o7^+k1ECUd$8dD$0vRcordatEhoW5tnCs zb-6zlKicC(tHLTWVlK5I+<;rn2NnzB+_OCP*dBl0b*>&qx_f55+xoE=0|b65cB}-r zl_J8yUkaDtmS8LK zLX_Iq9{@xbeGaC2VmHnv|C{O*f<^B9U^%sBwfNlmbct zEHNe*iLws$nDSd8$E}=#tr#Ozk=2}5EiVyjMcNciE)y%LLfbH^m{=iI*NV7+u)3IFAu}|_zjreLIT{+23iD1SA~YqRTJNEQ{P3u5UvRV`e?5g)zGg!%R)*x zof0XBDa*@o7!D$)ziasl3Q;lRDkRBioAk1v?X3ug4fBd;)c zMz|8cA5TC&Zs~Ut(|6+2ZRRu0!LzO856!s+Gr3?!7w$*;u1hv6$KCG%qg~0}yPOm6uilq#uJgFu($3d=3CIrZc24q%h_%Y%)F9Y z%!vh=leaOebNk(52}vz3S;^SCPqe~&&&)=;$RloNB|%WKD__{ diff --git a/backend/app/blueprints/api.py b/backend/app/blueprints/api.py index 8d400de..387a742 100755 --- a/backend/app/blueprints/api.py +++ b/backend/app/blueprints/api.py @@ -1,8 +1,71 @@ -from flask import Blueprint +""" +API Blueprint +------------- +This blueprint is used to define the API routes for the application. +The API blueprint is registered in the app factory in the app/__init__.py file. + +Current Routes: +- /api + - / + - /test + +The routes are defined as follows: +- /api + - GET: Returns the string "API Blueprint" +- /api/test + - GET: Returns the string "Test" +""" + +from typing import Union, Tuple +from flask import Blueprint, jsonify, request +from werkzeug.wrappers import Response + +from app.models import Sensor api_bp = Blueprint('api', __name__) -@api_bp.route('/test') +@api_bp.route('/', methods=['GET']) +def index(): + """Index route for the API blueprint""" + return "API Blueprint" + + +@api_bp.route('/test', methods=['GET']) def test(): + """Test route for the API blueprint""" return "Test" + + +@api_bp.route('/sensors', methods=['GET', 'POST']) +def sensors() -> Union[Response, Tuple[Response, int]]: + """ + Handles GET and POST requests to the /sensors route. + GET: Returns a list of all sensors in the database. + POST: Adds a new sensor to the database. + """ + if request.method == 'GET': + sensors_list = [sensor.to_dict() for sensor in Sensor.query.all()] + return jsonify(sensors_list), 200 + + elif request.method == 'POST': + if not request.json: + return jsonify({"error": "Bad Request", "message": "Body cannot be empty"}), 400 + + required_fields = ['sensor_id', 'serial_number', + 'sensor_name', 'latitude', 'longitude', 'owner_id'] + if not all(field in request.json for field in required_fields): + return jsonify({"error": "Bad Request", "message": "Missing values"}), 400 + + if Sensor.query.filter_by(sensor_id=request.json['sensor_id']).first(): + return jsonify({"error": "Bad Request", "message": "Sensor already exists"}), 400 + + # Hier die Logik zum Hinzufügen des Sensors zur Datenbank einfügen. + # Beispiel: new_sensor = Sensor(...) + # db.session.add(new_sensor) + # db.session.commit() + + return jsonify({"message": "Sensor successfully added"}), 201 + + # Fügt einen expliziten Rückgabewert für den Fall hinzu, dass keine der Bedingungen erfüllt ist + return jsonify({"error": "Method Not Allowed"}), 405 diff --git a/backend/app/models.py b/backend/app/models.py index 76362b6..12d7bf3 100755 --- a/backend/app/models.py +++ b/backend/app/models.py @@ -69,6 +69,42 @@ class Sensor(db.Model): owner = db.relationship('User', backref=db.backref('sensors', lazy=True)) + def __init__(self, sensor_id, serial_number, sensor_name, latitude, longitude, owner_id): + """Initializes the Sensor model.""" + self.sensor_id = sensor_id + self.serial_number = serial_number + self.sensor_name = sensor_name + self.latitude = latitude + self.longitude = longitude + self.owner_id = owner_id + + def set_sensor(self, sensor_id, serial_number, sensor_name, latitude, longitude, owner_id): + """Sets a sensor in the database.""" + + sensor = Sensor( + sensor_id=sensor_id, + serial_number=serial_number, + sensor_name=sensor_name, + latitude=latitude, + longitude=longitude, + owner_id=owner_id + ) + + db.session.add(sensor) + db.session.commit() + + return sensor + + def to_dict(self): + return { + 'sensor_id': self.sensor_id, + 'serial_number': self.serial_number, + 'sensor_name': self.sensor_name, + 'latitude': self.latitude, + 'longitude': self.longitude, + 'owner_id': self.owner_id + } + class LocalizationData(db.Model): __tablename__ = 'localizationdata' From 81de34cc6ab486bf269d717914c7592700e01ff4 Mon Sep 17 00:00:00 2001 From: SiemensHalske <140380689+SiemensHalske@users.noreply.github.com> Date: Tue, 18 Jun 2024 10:14:41 +0200 Subject: [PATCH 3/3] chore: Update npm dependencies and add OpenMeteo SDK library --- .cache.sqlite | Bin 0 -> 24576 bytes backend/Scripts/rain_test.py | 52 ++++++++++++ backend/Scripts/test.py | 15 ++++ backend/app/blueprints/api.py | 4 +- frontend/package-lock.json | 29 +++++++ frontend/package.json | 1 + frontend/src/app/rain/RadarPage.module.css | 39 +++++++++ frontend/src/app/rain/page.tsx | 87 +++++++++++++++++++++ 8 files changed, 226 insertions(+), 1 deletion(-) create mode 100644 .cache.sqlite create mode 100644 backend/Scripts/rain_test.py create mode 100644 backend/Scripts/test.py create mode 100644 frontend/src/app/rain/RadarPage.module.css create mode 100644 frontend/src/app/rain/page.tsx diff --git a/.cache.sqlite b/.cache.sqlite new file mode 100644 index 0000000000000000000000000000000000000000..11fedafc4d9bae3353ff52efc0ee2fc02b49d8c8 GIT binary patch literal 24576 zcmeI&Jx{_w7{Ku>ZyFO`HbyRSfy9^)2UkHahKQiGAuu&4XHrE#S`3bUI5!7Bj+^#K zY1Bj)C!7BzEzk9?cYS`nltW4CJ0D3Cj`(QVJo`;gJiEbusnKx)T0c zpW>gwwAj6Q%@^N9w$KyBZt=PBUg#BGe(KJ31Q0*~0R#|0009ILKmdXNB@kpb%9V=s zv>yyF$9k?u-dtY=ejH>EZPjp;bQ;aJlJR9(m0`KnkJ3?nN1oZO(}q2eCu*=`8Xkt@ zg_g~Bx4EiHy_|*P`m)t=)UmQ_^=z)JDwgmjBfa$R$Kl4-9WH|Ds_w?Kxb-;9b<(-= zcE$2mU83<^Z?p`DolPgc_A}9wwmL_uzn;#G_eE;!$Y!@|$K&<;|&NP80+XKmY**5I_I{1Q0*~0R#|8s=$^>jQxL7UoUwf zfB*srAb Union[Response, Tuple[Response, int]]: GET: Returns a list of all sensors in the database. POST: Adds a new sensor to the database. """ + + print(f"Request method: {request.method}") + if request.method == 'GET': sensors_list = [sensor.to_dict() for sensor in Sensor.query.all()] return jsonify(sensors_list), 200 @@ -67,5 +70,4 @@ def sensors() -> Union[Response, Tuple[Response, int]]: return jsonify({"message": "Sensor successfully added"}), 201 - # Fügt einen expliziten Rückgabewert für den Fall hinzu, dass keine der Bedingungen erfüllt ist return jsonify({"error": "Method Not Allowed"}), 405 diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 40d5f69..eb6e91e 100755 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -30,6 +30,7 @@ "next-redux-wrapper": "^8.1.0", "next-transpile-modules": "^10.0.1", "ol": "^9.2.4", + "openmeteo": "^1.1.4", "react": "^18.3.1", "react-chartjs-2": "^5.2.0", "react-dnd": "^16.0.1", @@ -1922,6 +1923,17 @@ "node": ">= 8" } }, + "node_modules/@openmeteo/sdk": { + "version": "1.11.7", + "resolved": "https://registry.npmjs.org/@openmeteo/sdk/-/sdk-1.11.7.tgz", + "integrity": "sha512-qV790gksvJ+l/umb1iKt+ZRUKE5RzgmPkwTeUSmtUcnoRaAQZX9/BQLDpmEZrkcuv4g1trzcsNRwxBrBLWUnWA==", + "dependencies": { + "flatbuffers": "^24.3.25" + }, + "engines": { + "node": ">=12.0" + } + }, "node_modules/@petamoriken/float16": { "version": "3.8.7", "resolved": "https://registry.npmjs.org/@petamoriken/float16/-/float16-3.8.7.tgz", @@ -6323,6 +6335,11 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/flatbuffers": { + "version": "24.3.25", + "resolved": "https://registry.npmjs.org/flatbuffers/-/flatbuffers-24.3.25.tgz", + "integrity": "sha512-3HDgPbgiwWMI9zVB7VYBHaMrbOO7Gm0v+yD2FV/sCKj+9NDeVL7BOBYUuhWAQGKWOzBo8S9WdMvV0eixO233XQ==" + }, "node_modules/flatted": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", @@ -8842,6 +8859,18 @@ "node": ">=12.20.0" } }, + "node_modules/openmeteo": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/openmeteo/-/openmeteo-1.1.4.tgz", + "integrity": "sha512-TalTDl0M7JJoeRTf+rWiFZ9SLvoxm7KkFLOQqcSjCiYs+bVMhax1qtryJqeZ1RF4W4Xfsgcl9x+VC1z39ULCxA==", + "dependencies": { + "@openmeteo/sdk": "^1.11.4", + "flatbuffers": "^24.3.25" + }, + "engines": { + "node": ">=12.0" + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", diff --git a/frontend/package.json b/frontend/package.json index 493a165..1655f4b 100755 --- a/frontend/package.json +++ b/frontend/package.json @@ -33,6 +33,7 @@ "next-redux-wrapper": "^8.1.0", "next-transpile-modules": "^10.0.1", "ol": "^9.2.4", + "openmeteo": "^1.1.4", "react": "^18.3.1", "react-chartjs-2": "^5.2.0", "react-dnd": "^16.0.1", diff --git a/frontend/src/app/rain/RadarPage.module.css b/frontend/src/app/rain/RadarPage.module.css new file mode 100644 index 0000000..42acbcc --- /dev/null +++ b/frontend/src/app/rain/RadarPage.module.css @@ -0,0 +1,39 @@ +/* RadarPage.module.css */ +.container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 2rem; +} + +.title { + font-size: 2.5rem; + margin-bottom: 2rem; + text-align: center; +} + +.radarGrid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 1rem; + width: 100%; + max-width: 1200px; +} + +.radarFrame { + border: 1px solid #ddd; + background-color: #f9f9f9; + color: #333; + padding: 1rem; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); + display: flex; + flex-direction: column; + align-items: center; +} + +.loading, +.error { + font-size: 1.5rem; + color: #888; +} diff --git a/frontend/src/app/rain/page.tsx b/frontend/src/app/rain/page.tsx new file mode 100644 index 0000000..b852134 --- /dev/null +++ b/frontend/src/app/rain/page.tsx @@ -0,0 +1,87 @@ +// pages/RadarPage.tsx +"use client"; + +import React, { useState, useEffect } from "react"; +import styles from "./RadarPage.module.css"; + +const fetchWeatherApi = async (url: string, params: any) => { + const response = await fetch(`${url}?${new URLSearchParams(params)}`); + return response.json(); +}; + +const RadarPage: React.FC = () => { + const [weatherData, setWeatherData] = useState(null); + const [error, setError] = useState(null); + + useEffect(() => { + const fetchRadarData = async () => { + try { + const params = { + latitude: 51.73929, + longitude: 8.2509, + hourly: ["rain", "windspeed_10m", "winddirection_10m"], + }; + const url = "https://api.open-meteo.com/v1/forecast"; + const response = await fetchWeatherApi(url, params); + + console.log("API Response:", response); // Log the API response + + if (!response || !response.hourly) { + throw new Error("Ungültige API-Antwort"); + } + + const hourly = response.hourly; + + const weatherData = { + hourly: { + time: hourly.time.map((t: string) => new Date(t)), + rain: hourly.rain, + windSpeed10m: hourly.windspeed_10m, + windDirection10m: hourly.winddirection_10m, + }, + }; + + console.log("Processed Weather Data:", weatherData); // Log the processed weather data + + setWeatherData(weatherData); + } catch (error: any) { + setError(error.message); + } + }; + + fetchRadarData(); + }, []); + + return ( +
+

Niederschlagsradar

+ {error ? ( +

{error}

+ ) : weatherData ? ( +
+ {weatherData.hourly.time.map( + (time: Date, index: number) => ( +
+

{time.toLocaleString()}

+

Regen: {weatherData.hourly.rain[index]}

+

+ Windgeschwindigkeit:{" "} + {weatherData.hourly.windSpeed10m[index]} +

+

+ Windrichtung:{" "} + {weatherData.hourly.windDirection10m[index]} +

+ {/* Hier könnten Sie das Radarbild für den jeweiligen Zeitpunkt einfügen */} +
+ ) + )} +
+ ) : ( +

Daten werden geladen...

+ )} +
+ ); +}; + +export default RadarPage;