Add Doris Kitchen household recipe app
This commit is contained in:
78
home/doris-kitchen/app/services/kitchenowl.py
Normal file
78
home/doris-kitchen/app/services/kitchenowl.py
Normal file
@@ -0,0 +1,78 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from typing import Any
|
||||
from urllib import error, parse, request
|
||||
|
||||
from app.config import settings
|
||||
|
||||
|
||||
class KitchenOwlService:
|
||||
def __init__(self) -> None:
|
||||
self.base_url = settings.kitchenowl_base_url.rstrip("/")
|
||||
self.token = settings.kitchenowl_api_token
|
||||
self.household_id = settings.kitchenowl_household_id
|
||||
|
||||
def configured(self) -> bool:
|
||||
return bool(self.base_url and self.token and self.household_id)
|
||||
|
||||
def _request(self, method: str, path: str, *, params: dict[str, Any] | None = None, body: dict[str, Any] | None = None, timeout: int = 30):
|
||||
url = f"{self.base_url}{path}"
|
||||
if params:
|
||||
url += ("&" if "?" in url else "?") + parse.urlencode(params, doseq=True)
|
||||
data = None
|
||||
headers = {
|
||||
"Authorization": f"Bearer {self.token}",
|
||||
"Accept": "application/json",
|
||||
"User-Agent": "doris-kitchen/1.0",
|
||||
}
|
||||
if body is not None:
|
||||
data = json.dumps(body).encode("utf-8")
|
||||
headers["Content-Type"] = "application/json"
|
||||
req = request.Request(url, data=data, headers=headers, method=method.upper())
|
||||
try:
|
||||
with request.urlopen(req, timeout=timeout) as resp:
|
||||
raw = resp.read().decode("utf-8", "replace")
|
||||
content_type = resp.headers.get("Content-Type", "")
|
||||
parsed = json.loads(raw) if "json" in content_type and raw else raw
|
||||
return resp.status, parsed
|
||||
except error.HTTPError as exc:
|
||||
raw = exc.read().decode("utf-8", "replace")
|
||||
try:
|
||||
parsed = json.loads(raw)
|
||||
except Exception:
|
||||
parsed = raw
|
||||
return exc.code, parsed
|
||||
|
||||
def list_recipes(self) -> list[dict[str, Any]]:
|
||||
if not self.configured():
|
||||
return []
|
||||
status, payload = self._request("GET", f"/api/household/{self.household_id}/recipe")
|
||||
if status == 200 and isinstance(payload, list):
|
||||
return payload
|
||||
return []
|
||||
|
||||
def find_existing(self, payload: dict[str, Any]) -> dict[str, Any] | None:
|
||||
want_source = str(payload.get("source") or "").strip().lower()
|
||||
want_name = str(payload.get("name") or "").strip().lower()
|
||||
for recipe in self.list_recipes():
|
||||
source = str(recipe.get("source") or "").strip().lower()
|
||||
name = str(recipe.get("name") or "").strip().lower()
|
||||
if want_source and source and source == want_source:
|
||||
return recipe
|
||||
if want_name and name == want_name:
|
||||
return recipe
|
||||
return None
|
||||
|
||||
def create_recipe(self, payload: dict[str, Any]) -> dict[str, Any]:
|
||||
if not self.configured():
|
||||
raise RuntimeError("KitchenOwl credentials are not configured.")
|
||||
existing = self.find_existing(payload)
|
||||
if existing:
|
||||
return {"status": "duplicate", "recipe": existing}
|
||||
status, created = self._request("POST", f"/api/household/{self.household_id}/recipe", body=payload, timeout=45)
|
||||
return {"status": "created" if status == 200 else "error", "http_status": status, "recipe": created}
|
||||
|
||||
|
||||
kitchenowl = KitchenOwlService()
|
||||
|
||||
Reference in New Issue
Block a user