o2o-site-AEO/backend/services/external/open_meteo.py
Mina Choi 6784e59ca5 최초 커밋 — 기존 코드 전체 + 문서 체계 신설
git 저장소가 없어 히스토리·협업 기반이 아예 없던 상태를 연다.
함께 문서를 재편했다. 그동안 문서가 있어도 "이 제품이 뭘 푸는가"와
"어떻게 도는가"를 담은 문서가 없어서, 목표 문장이 backend/frontend
README 두 곳에 복붙돼 있었다 — 상위 문서가 없어 아래로 샌 것이다.

신설
  README.md               레포 진입점 + 문서 지도 + 문서 규칙 4가지
  AGENTS.md               에이전트·신규 합류자용 함정 목록과 규약
                          (CLAUDE.md 는 여기로 걸린 심볼릭 링크)
  docs/PRODUCT.md         제품 정의 — 문제·사용자·원칙·**non-goals**·성공 기준
  docs/ARCHITECTURE.md    payload 경계·발행 파이프라인·서빙 결정·앱 분리 설계

이동
  backend/docs/DECISIONS.md → docs/DECISIONS.md
    백엔드만의 결정이 아니다. 게다가 코드 주석 ~25곳이 이미
    `docs/DECISIONS.md` 로 적고 있어 레포 루트 기준으로는 그게 맞다.

갱신
  docs/DEPLOY.md          서빙 결정 반영 — nginx 정적 서빙이 지금 경로(3절),
                          Azure 는 나중에 켤 때(4절)로 분리
  docs/ARCHITECTURE.md    사이트 = 한 장(2026-08-31) 구조 반영
  docs/COLLECTION_SEO_AEO_FLOW.md
                          robots.txt·sitemap.xml 은 오리진 루트에만 굽는다는 점 명시
  frontend/site/scripts/prerender.ts
                          헤더 주석의 렌더 보고서 경로가 실제(422줄)와 달라 수정

.gitignore
  ★ CLAUDE.md 를 더 이상 무시하지 않는다. 에이전트 지침은 팀과 모든
    에이전트가 공유하는 규약이라 커밋해야 한다 — 무시하면 클론한 사람이
    "배포 후 republish_all.py 필수" 같은 함정을 전달받지 못한다.
    개인용 오버라이드는 ~/.claude/CLAUDE.md 에 둔다.
2026-08-31 13:57:59 +09:00

44 lines
1.4 KiB
Python

"""Open-Meteo current-weather adapter.
The adapter only knows the upstream protocol. Cache policy belongs to
LocalContentService so callers cannot accidentally bypass it.
"""
from datetime import datetime
from typing import Any
import httpx
OPEN_METEO_URL = "https://api.open-meteo.com/v1/forecast"
class OpenMeteoRequestFailed(RuntimeError):
pass
async def fetch_current_weather(latitude: float, longitude: float) -> dict[str, Any]:
params = {
"latitude": latitude,
"longitude": longitude,
"current": "temperature_2m,weather_code,wind_speed_10m",
"timezone": "auto",
}
try:
async with httpx.AsyncClient(timeout=httpx.Timeout(10.0, connect=5.0)) as client:
response = await client.get(OPEN_METEO_URL, params=params)
response.raise_for_status()
payload = response.json()
current = payload["current"]
return {
"temperature": current["temperature_2m"],
"weather_code": current["weather_code"],
"wind_speed": current.get("wind_speed_10m"),
"observed_at": current["time"],
"timezone": payload.get("timezone"),
"latitude": payload.get("latitude", latitude),
"longitude": payload.get("longitude", longitude),
}
except (httpx.HTTPError, KeyError, TypeError, ValueError) as ex:
raise OpenMeteoRequestFailed("Open-Meteo 날씨 조회에 실패했습니다.") from ex