fix karung view data and sync only sync with the current date

This commit is contained in:
Alberto-Audrix committed 2026-09-04 09:33:17 +07:00
1 parent b3556c6ef4
commit 5ae477a99d
8 files changed
+396 -32

No files matched your search

+130 -1
View File
@@ -8,6 +8,7 @@ from urllib.parse import urljoin
import httpx
from django.conf import settings
from django.core.exceptions import ValidationError
from django.utils import timezone
from apps.farms.models import Cycle
from apps.operations.models import FeedSacks
@@ -51,6 +52,117 @@ def _normalize_source(value: str | None) -> str:
return (value or "").strip().casefold()
def _parse_history_date(raw: Any) -> date | None:
if raw is None:
return None
try:
return date.fromisoformat(str(raw)[:10])
except ValueError:
return None
def daily_totals_from_combined_history(
combined: dict[str, Any],
*,
kandang_name: str,
) -> dict[date, dict[str, int]]:
"""Group /api/combined history into per-date in/out/use for one kandang."""
in_source = _normalize_source(source_for_kandang(kandang_name, kind="In"))
out_source = _normalize_source(source_for_kandang(kandang_name, kind="Out"))
use_source = _normalize_source(source_for_kandang(kandang_name, kind="Use"))
by_date: dict[date, dict[str, int]] = {}
def ensure(day: date) -> dict[str, int]:
return by_date.setdefault(
day, {"in_today": 0, "out_today": 0, "feed_use_today": 0}
)
masuk_history = (combined.get("masuk") or {}).get("history") or []
for row in masuk_history:
day = _parse_history_date(row.get("date"))
if day is None:
continue
source = _normalize_source(row.get("source"))
value = int(row.get("counter_value") or row.get("karung") or 0)
if source == in_source:
ensure(day)["in_today"] = value
elif source == out_source:
ensure(day)["out_today"] = value
tuang_history = (combined.get("tuang") or {}).get("history") or []
for row in tuang_history:
day = _parse_history_date(row.get("date"))
if day is None:
continue
source = _normalize_source(row.get("source"))
value = int(row.get("counter_value") or row.get("karung") or 0)
if source == use_source:
ensure(day)["feed_use_today"] = value
return by_date
def cycle_history_window(cycle: Cycle, *, today: date | None = None) -> tuple[date, date]:
"""Inclusive date window for history upserts."""
if today is None:
today = timezone.localdate()
end = min(today, cycle.end_date)
return cycle.start_date, end
def backfill_karung_from_combined(
cycle: Cycle,
combined: dict[str, Any] | None = None,
) -> list[date]:
"""Upsert FeedSacks for cycle dates present in combined history. Returns upserted dates."""
if combined is None:
combined = KarungWebClient().fetch_combined()
kandang_name = cycle.kandang.kandang_name
by_date = daily_totals_from_combined_history(combined, kandang_name=kandang_name)
start, end = cycle_history_window(cycle)
upserted: list[date] = []
for day in sorted(by_date):
if day < start or day > end:
continue
totals = by_date[day]
FeedSacks.objects.update_or_create(
cycle=cycle,
date=day,
defaults={
"in_today": totals["in_today"],
"out_today": totals["out_today"],
"feed_use_today": totals["feed_use_today"],
},
)
upserted.append(day)
return upserted
def recompute_feed_sack_totals(cycle: Cycle) -> None:
"""Recompute running in/out/use totals for all FeedSacks of a cycle in date order."""
running_in = 0
running_out = 0
running_use = 0
rows = list(FeedSacks.objects.filter(cycle=cycle).order_by("date", "pk"))
for row in rows:
running_in += row.in_today or 0
running_out += row.out_today or 0
running_use += row.feed_use_today or 0
changed = (
row.in_total != running_in
or row.out_total != running_out
or row.feed_use_total != running_use
)
if changed:
row.in_total = running_in
row.out_total = running_out
row.feed_use_total = running_use
row.save(update_fields=["in_total", "out_total", "feed_use_total", "updated_at"])
class KarungWebClient:
"""Outbound client for karung-web-admin — base URL from settings only."""
@@ -155,7 +267,7 @@ def request_karung(
saldo_awal = int(remote_cycle.get("saldo_awal") or 0)
snapshot_raw = block.get("date")
snapshot_date = date.fromisoformat(str(snapshot_raw)) if snapshot_raw else date.today()
snapshot_date = date.fromisoformat(str(snapshot_raw)) if snapshot_raw else timezone.localdate()
prior = (
FeedSacks.objects.filter(cycle=cycle)
@@ -203,3 +315,20 @@ def request_karung(
"remote_cycle": remote_cycle,
}
return feed_sack, summary
def sync_karung_for_cycle(
cycle: Cycle,
*,
section: str = "today",
) -> tuple[FeedSacks, dict[str, Any]]:
"""Sync today from sections, backfill cycle history from /api/combined, recompute totals."""
feed_sack, summary = request_karung(cycle, section=section)
history_dates = backfill_karung_from_combined(cycle)
recompute_feed_sack_totals(cycle)
feed_sack.refresh_from_db()
summary = {
**summary,
"history_dates": [d.isoformat() for d in history_dates],
}
return feed_sack, summary