Files
dashboard-cpsp/backend/apps/jobs/sukawarna_preview.py
T

105 lines
3.5 KiB
Python

"""Load Sukawarna / Kandang 2 preview sheets into ORM-ready row dicts."""
from __future__ import annotations
from datetime import date, datetime
from pathlib import Path
from typing import Any
from openpyxl import load_workbook
REPO_ROOT = Path(__file__).resolve().parents[3]
PREVIEW_XLSX = REPO_ROOT / "backup" / "kandang5_sukawarna_preview.xlsx"
IOT_EXTRACT_JSON = REPO_ROOT / "scripts" / "iot_panel_cycle_extract.json"
# Final-cycle report (reference for manual feed use on Kandang 2).
FEED_REFERENCE_XLSX = (
REPO_ROOT
/ "backup"
/ "Laporan_Akhir-Siklus_Sukawarna_Kandang-Atas_Akhir-Siklus_2026-05-22_sd_2026-07-09.xlsx"
)
def as_date(value: Any) -> date | None:
if value is None:
return None
if isinstance(value, datetime):
return value.date()
if isinstance(value, date):
return value
text = str(value).strip()
if len(text) >= 10 and text[0].isdigit() and text[4] == "-":
return date.fromisoformat(text[:10])
return None
def as_number(value: Any, default: float | int = 0):
if value is None or value == "":
return default
if isinstance(value, (int, float)) and not isinstance(value, bool):
return value
try:
if isinstance(value, str) and "." not in value:
return int(value)
return float(value)
except (TypeError, ValueError):
return default
def dated_rows(ws) -> list[dict[str, Any]]:
headers = [cell.value for cell in next(ws.iter_rows(min_row=1, max_row=1))]
rows: list[dict[str, Any]] = []
for raw in ws.iter_rows(min_row=2, values_only=True):
mapped = {str(headers[i]): raw[i] for i in range(len(headers))}
parsed = as_date(mapped.get("date"))
if parsed is None:
continue
mapped["date"] = parsed
rows.append(mapped)
return rows
def load_preview(path: Path | None = None) -> dict[str, list[dict[str, Any]]]:
workbook = load_workbook(path or PREVIEW_XLSX, data_only=True)
return {
"chicken_counting": dated_rows(workbook["chicken_counting"]),
"chicken_weight": dated_rows(workbook["chicken_weight"]),
"feed_sacks": dated_rows(workbook["feed_sacks"]),
"manual_input": dated_rows(workbook["manual_input"]),
"kpi_preview": dated_rows(workbook["kpi_preview"]),
}
def load_feed_reference(path: Path | None = None) -> dict[str, dict[str, int]]:
"""Load the final-cycle report's Feed In / Feed Use sheets.
Returns ``{date: {manual_in, iot_in, manual_use, iot_use}}``. These values are
the authoritative reference for the karung "Manual" / "IOT" columns.
"""
workbook = load_workbook(path or FEED_REFERENCE_XLSX, data_only=True)
feed_in: dict[str, dict[str, int]] = {}
for raw in workbook["Feed In"].iter_rows(min_row=3, values_only=True):
parsed = as_date(raw[0])
if parsed is None:
continue
feed_in[parsed.isoformat()] = {
"manual_in": int(as_number(raw[1])),
"iot_in": int(as_number(raw[2])),
}
feed_use: dict[str, dict[str, int]] = {}
for raw in workbook["Feed Use"].iter_rows(min_row=3, values_only=True):
parsed = as_date(raw[0])
if parsed is None:
continue
feed_use[parsed.isoformat()] = {
"manual_use": int(as_number(raw[2])),
"iot_use": int(as_number(raw[3])),
}
merged: dict[str, dict[str, int]] = {}
for key in feed_in:
if key in feed_use:
merged[key] = {**feed_in[key], **feed_use[key]}
return merged