Update main.py and history.html; add new templates
This commit is contained in:
370
main.py
370
main.py
@ -6,6 +6,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from db import get_session, engine, Base
|
||||
from models import WeatherRecord
|
||||
from datetime import datetime
|
||||
from fastapi.responses import HTMLResponse, JSONResponse
|
||||
|
||||
app = FastAPI()
|
||||
templates = Jinja2Templates(directory="templates")
|
||||
@ -14,8 +15,74 @@ CITIES = {
|
||||
"warszawa": (52.23, 21.01),
|
||||
"krakow": (50.06, 19.94),
|
||||
"gdansk": (54.35, 18.65),
|
||||
"wroclaw": (51.11, 17.03),
|
||||
"poznan": (52.41, 16.93),
|
||||
"szczecin": (53.43, 14.55),
|
||||
"bydgoszcz": (53.12, 18.01),
|
||||
"lublin": (51.25, 22.57),
|
||||
"bialystok": (53.13, 23.15),
|
||||
"katowice": (50.26, 19.02),
|
||||
"lodz": (51.77, 19.46),
|
||||
"torun": (53.01, 18.60),
|
||||
"kielce": (50.87, 20.63),
|
||||
"rzeszow": (50.04, 22.00),
|
||||
"opole": (50.67, 17.93),
|
||||
"zielona_gora": (51.94, 15.50),
|
||||
"gorzow_wlkp": (52.73, 15.24),
|
||||
"olsztyn": (53.78, 20.48),
|
||||
"radom": (51.40, 21.15),
|
||||
"plock": (52.55, 19.70),
|
||||
"elblag": (54.16, 19.40),
|
||||
"tarnow": (50.01, 20.99),
|
||||
"chorzow": (50.30, 18.95),
|
||||
"gliwice": (50.30, 18.67),
|
||||
"zabrze": (50.30, 18.78),
|
||||
"rybnik": (50.10, 18.55),
|
||||
"walbrzych": (50.77, 16.28),
|
||||
"legnica": (51.21, 16.16),
|
||||
"pila": (53.15, 16.74),
|
||||
"suwalki": (54.10, 22.93),
|
||||
"siedlce": (52.17, 22.29),
|
||||
"piotrkow_tryb": (51.40, 19.70),
|
||||
"nowy_sacz": (49.62, 20.69),
|
||||
"przemysl": (49.78, 22.77),
|
||||
"zamosc": (50.72, 23.25),
|
||||
"chelm": (51.14, 23.47),
|
||||
"koszalin": (54.19, 16.18),
|
||||
"slupsk": (54.46, 17.03),
|
||||
"grudziadz": (53.48, 18.75),
|
||||
"jaworzno": (50.20, 19.27),
|
||||
"tarnobrzeg": (50.58, 21.68),
|
||||
"ostrow_wlkp": (51.65, 17.81),
|
||||
"konin": (52.22, 18.26),
|
||||
"leszno": (51.84, 16.57),
|
||||
"stargard": (53.34, 15.05),
|
||||
"lubin": (51.40, 16.20),
|
||||
"mielec": (50.28, 21.42),
|
||||
"pabianice": (51.66, 19.35),
|
||||
"glogow": (51.66, 16.08),
|
||||
"ostroleka": (53.09, 21.56),
|
||||
"siemianowice_sl": (50.31, 19.03),
|
||||
"swidnica": (50.84, 16.49),
|
||||
"skierniewice": (51.97, 20.15),
|
||||
"bedzin": (50.33, 19.13),
|
||||
"pulawy": (51.42, 21.97),
|
||||
"starachowice": (51.05, 21.08),
|
||||
"nowy_targ": (49.48, 20.03),
|
||||
"radomsko": (51.07, 19.45),
|
||||
"wloclawek": (52.65, 19.07),
|
||||
"lubartow": (51.46, 22.61),
|
||||
"lomza": (53.18, 22.07),
|
||||
"klodzko": (50.43, 16.65),
|
||||
"biala_podlaska": (52.03, 23.13),
|
||||
"pruszkow": (52.17, 20.80),
|
||||
"jaslo": (49.75, 21.47),
|
||||
"dzierzoniow": (50.73, 16.65),
|
||||
"bielsk_podlaski": (52.77, 23.19),
|
||||
}
|
||||
|
||||
|
||||
|
||||
@app.on_event("startup")
|
||||
async def startup():
|
||||
async with engine.begin() as conn:
|
||||
@ -34,6 +101,7 @@ async def get_weather(city: str = Query("warszawa"), session: AsyncSession = Dep
|
||||
data = res.json()
|
||||
|
||||
current = data.get("current_weather", {})
|
||||
|
||||
record = WeatherRecord(
|
||||
city=city.capitalize(),
|
||||
temperature=current.get("temperature"),
|
||||
@ -50,6 +118,7 @@ async def get_weather(city: str = Query("warszawa"), session: AsyncSession = Dep
|
||||
"time": record.time
|
||||
}
|
||||
|
||||
|
||||
@app.get("/history")
|
||||
async def get_history(city: str = Query("warszawa"), session: AsyncSession = Depends(get_session)):
|
||||
stmt = select(WeatherRecord).where(WeatherRecord.city.ilike(city)).order_by(WeatherRecord.time.desc()).limit(10)
|
||||
@ -78,9 +147,10 @@ async def view_history(request: Request, city: str = "warszawa", session: AsyncS
|
||||
|
||||
|
||||
@app.get("/ping")
|
||||
async def ping():
|
||||
return {"status": "ok"}
|
||||
|
||||
async def ping(request: Request):
|
||||
if "text/html" in request.headers.get("accept", ""):
|
||||
return templates.TemplateResponse("ping.html", {"request": request})
|
||||
return JSONResponse({"status": "ok"})
|
||||
|
||||
@app.get("/weather/geo")
|
||||
async def get_weather_by_coords(
|
||||
@ -121,3 +191,297 @@ async def get_weather_by_coords(
|
||||
"windspeed": record.windspeed,
|
||||
"time": record.time
|
||||
}
|
||||
@app.get("/forecast", response_class=HTMLResponse)
|
||||
async def get_forecast(
|
||||
request: Request,
|
||||
city: str = Query("warszawa"),
|
||||
days: int = Query(3, ge=1, le=16)
|
||||
):
|
||||
coords = CITIES.get(city.lower())
|
||||
if not coords:
|
||||
return templates.TemplateResponse(
|
||||
"forecast.html",
|
||||
{
|
||||
"request": request,
|
||||
"city": city.capitalize(),
|
||||
"forecast": None
|
||||
}
|
||||
)
|
||||
|
||||
url = (
|
||||
f"https://api.open-meteo.com/v1/forecast?"
|
||||
f"latitude={coords[0]}&longitude={coords[1]}"
|
||||
f"&daily=temperature_2m_max,temperature_2m_min,precipitation_sum"
|
||||
f"&timezone=Europe/Warsaw"
|
||||
f"&forecast_days={days}"
|
||||
)
|
||||
|
||||
async with httpx.AsyncClient() as client:
|
||||
res = await client.get(url)
|
||||
data = res.json()
|
||||
|
||||
daily = data.get("daily", {})
|
||||
|
||||
accept = request.headers.get("accept", "")
|
||||
if "text/html" in accept:
|
||||
return templates.TemplateResponse(
|
||||
"forecast.html",
|
||||
{
|
||||
"request": request,
|
||||
"city": city.capitalize(),
|
||||
"forecast": daily,
|
||||
"error": None,
|
||||
}
|
||||
)
|
||||
else:
|
||||
return JSONResponse(content={
|
||||
"city": city.capitalize(),
|
||||
"forecast": daily,
|
||||
})
|
||||
|
||||
|
||||
@app.get("/forecast/geo", response_class=HTMLResponse)
|
||||
async def get_forecast_geo(
|
||||
request: Request,
|
||||
latitude: float = Query(..., ge=-90, le=90),
|
||||
longitude: float = Query(..., ge=-180, le=180),
|
||||
days: int = Query(3, ge=1, le=16)
|
||||
):
|
||||
url = (
|
||||
f"https://api.open-meteo.com/v1/forecast?"
|
||||
f"latitude={latitude}&longitude={longitude}"
|
||||
f"&daily=temperature_2m_max,temperature_2m_min,precipitation_sum"
|
||||
f"&timezone=Europe/Warsaw"
|
||||
f"&forecast_days={days}"
|
||||
)
|
||||
|
||||
async with httpx.AsyncClient() as client:
|
||||
res = await client.get(url)
|
||||
data = res.json()
|
||||
|
||||
daily = data.get("daily", {})
|
||||
|
||||
accept = request.headers.get("accept", "")
|
||||
if "text/html" in accept:
|
||||
return templates.TemplateResponse(
|
||||
"forecast.html",
|
||||
{
|
||||
"request": request,
|
||||
"city": f"Lat: {latitude}, Lon: {longitude}",
|
||||
"forecast": daily,
|
||||
"error": None,
|
||||
}
|
||||
)
|
||||
else:
|
||||
return JSONResponse(content={
|
||||
"latitude": latitude,
|
||||
"longitude": longitude,
|
||||
"forecast": daily
|
||||
})
|
||||
|
||||
|
||||
@app.get("/forecast/hourly/geo", response_class=HTMLResponse)
|
||||
async def get_hourly_forecast_geo(
|
||||
request: Request,
|
||||
latitude: float = Query(..., ge=-90, le=90),
|
||||
longitude: float = Query(..., ge=-180, le=180),
|
||||
hours: int = Query(12, ge=1, le=48)
|
||||
):
|
||||
url = (
|
||||
f"https://api.open-meteo.com/v1/forecast?"
|
||||
f"latitude={latitude}&longitude={longitude}"
|
||||
f"&hourly=temperature_2m,windspeed_10m,precipitation"
|
||||
f"&timezone=Europe/Warsaw"
|
||||
)
|
||||
|
||||
async with httpx.AsyncClient() as client:
|
||||
res = await client.get(url)
|
||||
data = res.json()
|
||||
|
||||
hourly_data = {}
|
||||
for key, values in data.get("hourly", {}).items():
|
||||
hourly_data[key] = values[:hours]
|
||||
|
||||
accept = request.headers.get("accept", "")
|
||||
if "text/html" in accept:
|
||||
return templates.TemplateResponse(
|
||||
"hourly_forecast.html",
|
||||
{
|
||||
"request": request,
|
||||
"city": f"Lat: {latitude}, Lon: {longitude}",
|
||||
"hourly_forecast": hourly_data,
|
||||
"error": None,
|
||||
}
|
||||
)
|
||||
else:
|
||||
return JSONResponse(content={
|
||||
"latitude": latitude,
|
||||
"longitude": longitude,
|
||||
"hourly_forecast": hourly_data
|
||||
})
|
||||
|
||||
@app.get("/forecast/hourly")
|
||||
async def get_hourly_forecast_city(
|
||||
request: Request,
|
||||
city: str = Query(...),
|
||||
hours: int = Query(12, ge=1, le=48)
|
||||
):
|
||||
coords = CITIES.get(city.lower())
|
||||
if not coords:
|
||||
error_data = {"error": "Miasto nieobsługiwane"}
|
||||
accept = request.headers.get("accept", "")
|
||||
if "text/html" in accept:
|
||||
return templates.TemplateResponse(
|
||||
"hourly_forecast.html",
|
||||
{
|
||||
"request": request,
|
||||
"city": city.capitalize(),
|
||||
"hourly_forecast": None,
|
||||
"error": error_data["error"],
|
||||
}
|
||||
)
|
||||
else:
|
||||
raise HTTPException(status_code=404, detail=error_data["error"])
|
||||
latitude, longitude = coords
|
||||
|
||||
url = (
|
||||
f"https://api.open-meteo.com/v1/forecast?"
|
||||
f"latitude={latitude}&longitude={longitude}"
|
||||
f"&hourly=temperature_2m,windspeed_10m,precipitation"
|
||||
f"&timezone=Europe/Warsaw"
|
||||
)
|
||||
|
||||
async with httpx.AsyncClient() as client:
|
||||
res = await client.get(url)
|
||||
data = res.json()
|
||||
|
||||
hourly_data = {}
|
||||
for key, values in data.get("hourly", {}).items():
|
||||
hourly_data[key] = values[:hours]
|
||||
|
||||
accept = request.headers.get("accept", "")
|
||||
if "text/html" in accept:
|
||||
return templates.TemplateResponse(
|
||||
"hourly_forecast.html",
|
||||
{
|
||||
"request": request,
|
||||
"city": city.capitalize(),
|
||||
"hourly_forecast": hourly_data,
|
||||
"error": None,
|
||||
}
|
||||
)
|
||||
else:
|
||||
return JSONResponse(content={
|
||||
"city": city.capitalize(),
|
||||
"hourly_forecast": hourly_data
|
||||
})
|
||||
|
||||
@app.get("/history-range")
|
||||
async def get_weather_history_range(
|
||||
request: Request,
|
||||
city: str = Query(..., description="Nazwa miasta, np. warszawa"),
|
||||
start_date: str = Query(..., description="Początkowa data w formacie YYYY-MM-DD"),
|
||||
end_date: str = Query(..., description="Końcowa data w formacie YYYY-MM-DD")
|
||||
):
|
||||
coords = CITIES.get(city.lower())
|
||||
if not coords:
|
||||
error_data = {"error": "Miasto nieobsługiwane"}
|
||||
accept = request.headers.get("accept", "")
|
||||
if "text/html" in accept:
|
||||
return templates.TemplateResponse(
|
||||
"history_range.html",
|
||||
{
|
||||
"request": request,
|
||||
"city": city.capitalize(),
|
||||
"start_date": start_date,
|
||||
"end_date": end_date,
|
||||
"history": None,
|
||||
"error": error_data["error"]
|
||||
}
|
||||
)
|
||||
else:
|
||||
raise HTTPException(status_code=404, detail=error_data["error"])
|
||||
|
||||
latitude, longitude = coords
|
||||
|
||||
url = (
|
||||
f"https://archive-api.open-meteo.com/v1/archive?"
|
||||
f"latitude={latitude}&longitude={longitude}"
|
||||
f"&daily=temperature_2m_max,temperature_2m_min,precipitation_sum"
|
||||
f"&start_date={start_date}&end_date={end_date}"
|
||||
f"&timezone=Europe/Warsaw"
|
||||
)
|
||||
|
||||
async with httpx.AsyncClient() as client:
|
||||
res = await client.get(url)
|
||||
data = res.json()
|
||||
|
||||
daily = data.get("daily", {})
|
||||
|
||||
accept = request.headers.get("accept", "")
|
||||
if "text/html" in accept:
|
||||
return templates.TemplateResponse(
|
||||
"history_range.html",
|
||||
{
|
||||
"request": request,
|
||||
"city": city.capitalize(),
|
||||
"start_date": start_date,
|
||||
"end_date": end_date,
|
||||
"history": daily,
|
||||
"error": None
|
||||
}
|
||||
)
|
||||
else:
|
||||
return JSONResponse(content={
|
||||
"city": city.capitalize(),
|
||||
"start_date": start_date,
|
||||
"end_date": end_date,
|
||||
"history": daily
|
||||
})
|
||||
|
||||
@app.get("/history-range/geo")
|
||||
async def get_weather_history_range_geo(
|
||||
request: Request,
|
||||
latitude: float = Query(..., ge=-90, le=90, description="Szerokość geograficzna"),
|
||||
longitude: float = Query(..., ge=-180, le=180, description="Długość geograficzna"),
|
||||
start_date: str = Query(..., description="Początkowa data w formacie YYYY-MM-DD"),
|
||||
end_date: str = Query(..., description="Końcowa data w formacie YYYY-MM-DD")
|
||||
):
|
||||
url = (
|
||||
f"https://archive-api.open-meteo.com/v1/archive?"
|
||||
f"latitude={latitude}&longitude={longitude}"
|
||||
f"&daily=temperature_2m_max,temperature_2m_min,precipitation_sum"
|
||||
f"&start_date={start_date}&end_date={end_date}"
|
||||
f"&timezone=Europe/Warsaw"
|
||||
)
|
||||
|
||||
async with httpx.AsyncClient() as client:
|
||||
res = await client.get(url)
|
||||
data = res.json()
|
||||
|
||||
daily = data.get("daily", {})
|
||||
|
||||
accept = request.headers.get("accept", "")
|
||||
if "text/html" in accept:
|
||||
city_name = f"({latitude}, {longitude})"
|
||||
|
||||
return templates.TemplateResponse(
|
||||
"history_range.html",
|
||||
{
|
||||
"request": request,
|
||||
"city": city_name,
|
||||
"start_date": start_date,
|
||||
"end_date": end_date,
|
||||
"history": daily,
|
||||
"error": None
|
||||
}
|
||||
)
|
||||
else:
|
||||
return JSONResponse(content={
|
||||
"latitude": latitude,
|
||||
"longitude": longitude,
|
||||
"start_date": start_date,
|
||||
"end_date": end_date,
|
||||
"history": daily
|
||||
})
|
||||
|
||||
|
Reference in New Issue
Block a user