commit 82d8e52e0bc96a6374fb04c496f4ec075731faa2 Author: Jakub Date: Tue Jul 22 09:18:35 2025 +0200 first commit diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..0c8161d --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,17 @@ +services: + weather-web: + build: + context: ./weather-webapp + dockerfile: Dockerfile + restart: always + ports: + - "8000:8000" + networks: + - web-net +networks: + web-net: + external: true + name: shared-net + + + diff --git a/weather-webapp/Dockerfile b/weather-webapp/Dockerfile new file mode 100644 index 0000000..33a643a --- /dev/null +++ b/weather-webapp/Dockerfile @@ -0,0 +1,21 @@ +FROM python:3.11-slim + +ENV PYTHONDONTWRITEBYTECODE=1 +ENV PYTHONUNBUFFERED=1 + +WORKDIR /app +RUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/* +RUN update-ca-certificates +COPY requirements.txt . + +RUN pip install --no-cache-dir -r requirements.txt + +COPY . . +EXPOSE 8000 + + +CMD ["gunicorn", "--bind", "0.0.0.0:8000", "main:app"] + +#CMD ["flask", "run", "--host=0.0.0.0", "--port=6000"] + +#CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "6000"] diff --git a/weather-webapp/main.py b/weather-webapp/main.py new file mode 100644 index 0000000..321197d --- /dev/null +++ b/weather-webapp/main.py @@ -0,0 +1,186 @@ +from flask import Flask, render_template, request, jsonify +import requests + +app = Flask(__name__) +BASE_URL = "https://meteo.cbpio.pl" + +#app.route("/gui/weather") +#def simple_weather(): +# return render_template("weather.html") + + +@app.route("/gui/weather") +def simple_weather(): + city = request.args.get("city", "Warszawa") + + # Sprawdź, czy klient akceptuje JSON (np. curl) + if "application/json" in request.headers.get("Accept", "") or "curl" in request.headers.get("User-Agent", ""): + try: + response = requests.get(f"https://meteo.cbpio.pl/weather", params={"city": city}) + return jsonify(response.json()) + except Exception as e: + return jsonify({"error": str(e)}), 500 + + # Dla przeglądarki - HTML + return render_template("weather.html") + +@app.route("/gui/weather/geo") +def geo_weather_page(): + lat = request.args.get("lat") + lon = request.args.get("lon") + + if not lat or not lon: + msg = {"error": "Podaj współrzędne, np. ?lat=52.4&lon=16.9"} + if "application/json" in request.headers.get("Accept", "") or "curl" in request.headers.get("User-Agent", ""): + return jsonify(msg), 400 + return msg["error"], 400 + + try: + response = requests.get("https://meteo.cbpio.pl/weather/geo", params={"latitude": lat, "longitude": lon}) + data = response.json() + except Exception as e: + error_msg = {"error": str(e)} + if "application/json" in request.headers.get("Accept", "") or "curl" in request.headers.get("User-Agent", ""): + return jsonify(error_msg), 500 + return f"Błąd: {e}", 500 + + if "application/json" in request.headers.get("Accept", "") or "curl" in request.headers.get("User-Agent", ""): + return jsonify(data) + + return render_template("geo_weather.html", weather=data) + +@app.route("/gui/forecast") +def forecast(): + city = request.args.get("city", "").strip() + if not city: + return render_template("forecast.html", city="", error="Nie podano miasta") + + try: + forecast_data = requests.get(f"{BASE_URL}/forecast", params={"city": city}).json() + except Exception as e: + return render_template("forecast.html", city=city, error="Błąd pobierania danych") + + accept = request.headers.get("Accept", "") + if "application/json" in accept: + return jsonify(forecast_data) + + return render_template("forecast.html", city=forecast_data["city"], forecast=forecast_data["forecast"]) + +@app.route("/gui/forecast/hourly") +def hourly_forecast(): + city = request.args.get("city", "").lower() + hours = request.args.get("hours", 24, type=int) + + if not city: + return "Brak parametru 'city'", 400 + + try: + resp = requests.get("https://meteo.cbpio.pl/forecast/hourly", params={"city": city, "hours": hours}) + resp.raise_for_status() + data = resp.json() + except Exception as e: + error_msg = f"Błąd pobierania danych: {str(e)}" + if "text/html" in request.headers.get("Accept", ""): + return render_template("hourly_forecast.html", city=city.capitalize(), hourly_forecast=None, error=error_msg) + return jsonify({"error": error_msg}), 500 + + hourly = data.get("hourly_forecast", {}) + + if "text/html" in request.headers.get("Accept", ""): + return render_template( + "hourly_forecast.html", + city=city.capitalize(), + hourly_forecast=hourly, + error=None + ) + else: + return jsonify(data) + + +@app.route("/gui/history/range") +def history_range(): + city = request.args.get("city", "").lower() + start_date = request.args.get("start_date") + end_date = request.args.get("end_date") + accept = request.headers.get("Accept", "") + + if not city or not start_date or not end_date: + error_msg = "Brak wymaganych parametrów" + if "text/html" in accept: + return render_template("history_range.html", city=city.capitalize(), + start_date=start_date, end_date=end_date, + history=None, error=error_msg) + else: + return jsonify({"error": error_msg}), 400 + + url = ( + f"https://meteo.cbpio.pl/history-range" + f"?city={city}&start_date={start_date}&end_date={end_date}" + ) + + try: + res = requests.get(url) + res.raise_for_status() + data = res.json() + except Exception as e: + if "text/html" in accept: + return render_template("history_range.html", city=city.capitalize(), + start_date=start_date, end_date=end_date, + history=None, error=str(e)) + else: + return jsonify({"error": str(e)}), 500 + + history = data.get("history") # ✅ WYCIĄGAMY KLUCZ "history" z JSON-a + + if "text/html" in accept: + return render_template("history_range.html", city=city.capitalize(), + start_date=start_date, end_date=end_date, + history=history, error=None) # ✅ przekazujemy history, nie cały JSON + else: + return jsonify({ + "city": city.capitalize(), + "start_date": start_date, + "end_date": end_date, + "history": history + }) + + + +@app.route("/app/") +def index(): + return render_template("index.html") + +@app.route("/app/get_weather", methods=["POST"]) +def get_weather(): + data = request.get_json() + lat = data.get("lat") + lon = data.get("lon") + + if lat is None or lon is None: + return jsonify({"error": "Missing coordinates"}), 400 + + try: + current = requests.get(f"{BASE_URL}/weather/geo", params={"latitude": lat, "longitude": lon}).json() + forecast = requests.get(f"{BASE_URL}/forecast/geo", params={ + "latitude": lat, + "longitude": lon, + "days": 7 + }).json() + hourly = requests.get(f"{BASE_URL}/forecast/hourly/geo", params={ + "latitude": lat, + "longitude": lon, + "hours": 24 + }).json() + + return jsonify({ + "current": current, + "daily": forecast, + "hourly": hourly + }) + except Exception as e: + print("❌ Error fetching data:", e) + return jsonify({"error": str(e)}), 500 + +if __name__ == "__main__": + #app.run(debug=True) + app.run(debug=True,host='0.0.0.0', port=8000) diff --git a/weather-webapp/requirements.txt b/weather-webapp/requirements.txt new file mode 100644 index 0000000..0918c5f --- /dev/null +++ b/weather-webapp/requirements.txt @@ -0,0 +1,4 @@ +flask +requests +gunicorn +uvicorn diff --git a/weather-webapp/templates/forecast.html b/weather-webapp/templates/forecast.html new file mode 100644 index 0000000..30257aa --- /dev/null +++ b/weather-webapp/templates/forecast.html @@ -0,0 +1,251 @@ + + + + + Prognoza pogody - {{ city }} + + + + +

Prognoza pogody dla {{ city }}

+ + {% if error %} +

{{ error }}

+ {% elif forecast and forecast.time %} + + + + + + + + + + + {% for i in range(forecast.time|length) %} + + + + + + + {% endfor %} + +
DataTemperatura maks. (°C)Temperatura min. (°C)Opady (mm)
{{ forecast.time[i] }}{{ forecast.temperature_2m_max[i] }}{{ forecast.temperature_2m_min[i] }}{{ forecast.precipitation_sum[i] }}
+ +
+ +
+ + + + {% else %} +

Brak danych pogodowych.

+ {% endif %} + + + diff --git a/weather-webapp/templates/geo_weather.html b/weather-webapp/templates/geo_weather.html new file mode 100644 index 0000000..1f36a80 --- /dev/null +++ b/weather-webapp/templates/geo_weather.html @@ -0,0 +1,27 @@ + + + + + Pogoda z koordynatów + + + +
+

Pogoda z Open-Meteo

+

Miasto: {{ weather.city }}

+

Szerokość: {{ weather.latitude }}

+

Długość: {{ weather.longitude }}

+

Temperatura: {{ weather.temperature }} °C

+

Wiatr: {{ weather.windspeed }} m/s

+

Czas: {{ weather.time }}

+
+ + + diff --git a/weather-webapp/templates/history_range.html b/weather-webapp/templates/history_range.html new file mode 100644 index 0000000..1a95009 --- /dev/null +++ b/weather-webapp/templates/history_range.html @@ -0,0 +1,209 @@ + + + + + Historia pogody - {{ city }} + + + + +

Historia pogody dla {{ city }}
od {{ start_date }} do {{ end_date }}

+ + {% if error %} +

{{ error }}

+ {% elif history %} + + + + + + + + + + + {% for i in range(history["time"]|length) %} + + + + + + + {% endfor %} + +
DataTemp. maks. (°C)Temp. min. (°C)Opady (mm)
{{ history["time"][i] }}{{ history["temperature_2m_max"][i] }}{{ history["temperature_2m_min"][i] }}{{ history["precipitation_sum"][i] }}
+ + + + + + {% endif %} + + + diff --git a/weather-webapp/templates/hourly_forecast.html b/weather-webapp/templates/hourly_forecast.html new file mode 100644 index 0000000..dfcf175 --- /dev/null +++ b/weather-webapp/templates/hourly_forecast.html @@ -0,0 +1,221 @@ + + + + + Prognoza godzinowa - {{ city }} + + + + + +

Prognoza godzinowa dla {{ city }}

+ +
+ + + + +
+ + {% if error %} +

{{ error }}

+ {% elif hourly_forecast %} + + + + + + + + {% for i in range(hourly_forecast["time"]|length) %} + + + + + + + {% endfor %} + +
GodzinaTemp. (°C)Wiatr (km/h)Opady (mm)
{{ hourly_forecast["time"][i] }}{{ hourly_forecast["temperature_2m"][i] }}{{ hourly_forecast["windspeed_10m"][i] }}{{ hourly_forecast["precipitation"][i] }}
+ + + + + {% endif %} + + + diff --git a/weather-webapp/templates/index.html b/weather-webapp/templates/index.html new file mode 100644 index 0000000..b1e3da7 --- /dev/null +++ b/weather-webapp/templates/index.html @@ -0,0 +1,408 @@ + + + + + + + Pogoda Web + + + + + + +
+

Pogoda Web

+

Kliknij na mapie lub wyszukaj miasto, aby zobaczyć prognozę pogody

+
+
+
+
+ + +
+
+
Kliknij na mapie lub wyszukaj miasto, aby zobaczyć pogodę.
+ + +
+ + + + + + + diff --git a/weather-webapp/templates/ping.html b/weather-webapp/templates/ping.html new file mode 100644 index 0000000..4dbdc4c --- /dev/null +++ b/weather-webapp/templates/ping.html @@ -0,0 +1,55 @@ + + + + + Status serwera + + + +
+

🟡 Ładowanie...

+

Trwa sprawdzanie statusu serwera...

+
+ + + + + diff --git a/weather-webapp/templates/tmp/bootstrap_index.html b/weather-webapp/templates/tmp/bootstrap_index.html new file mode 100644 index 0000000..37bbf15 --- /dev/null +++ b/weather-webapp/templates/tmp/bootstrap_index.html @@ -0,0 +1,460 @@ + + + + + + + Pogoda Web + + + + + + + + + + + +
+

🌦️ Pogoda Web

+

Kliknij na mapie lub wyszukaj miasto, aby zobaczyć prognozę pogody

+
+
+ +
+
+ + +
+ +
+ +
Kliknij na mapie lub wyszukaj miasto, aby zobaczyć pogodę.
+ + + +
+ + + + + + + + + diff --git a/weather-webapp/templates/weather.html b/weather-webapp/templates/weather.html new file mode 100644 index 0000000..162a66e --- /dev/null +++ b/weather-webapp/templates/weather.html @@ -0,0 +1,67 @@ + + + + + + Pogoda + + + +
+

Pogoda w ...

+
Temperatura: ... °C
+
Wiatr: ... m/s
+
Czas pomiaru: ...
+
+ + + + + diff --git a/weather-webapp/templates/wzory b/weather-webapp/templates/wzory new file mode 120000 index 0000000..9e430ec --- /dev/null +++ b/weather-webapp/templates/wzory @@ -0,0 +1 @@ +/home/jakub/services/meteo/server/templates/ \ No newline at end of file