adjust kpi card not showing after manual input before cutoff time
This commit is contained in:
1 parent
3f5db48b16
commit
c963bb778c
2 files changed
+44
-10
No files matched your search
@@ -15,44 +15,56 @@ from apps.operations.services.kpi_formulas import (
|
|||||||
compute_fcr,
|
compute_fcr,
|
||||||
compute_stock_akhir,
|
compute_stock_akhir,
|
||||||
)
|
)
|
||||||
from apps.operations.services.visibility import visible_through_date
|
|
||||||
|
|
||||||
|
|
||||||
def refresh_kpi_source_fields(cycle: Cycle, *, through: date | None = None) -> int:
|
def refresh_kpi_source_fields(cycle: Cycle, *, through: date | None = None) -> int:
|
||||||
"""
|
"""
|
||||||
Upsert one KPI row per day that has cc + cw + fs + manual.
|
Upsert one KPI row per day that has cc + cw + fs + manual.
|
||||||
|
|
||||||
|
If `through` is specified, rollup calculations are bounded up to that date.
|
||||||
|
Otherwise, rollups are calculated for all complete days of the cycle (up to `cycle.end_date` if set).
|
||||||
|
Dashboard cutoff visibility is enforced by the KPI API queryset layer (`VisibilityFilteredMixin`).
|
||||||
|
|
||||||
Returns the number of KPI rows written.
|
Returns the number of KPI rows written.
|
||||||
"""
|
"""
|
||||||
cutoff = through if through is not None else visible_through_date()
|
cutoff = through if through is not None else cycle.end_date
|
||||||
start = cycle.start_date
|
start = cycle.start_date
|
||||||
if cycle.end_date and cycle.end_date < cutoff:
|
if through is not None and cycle.end_date and cycle.end_date < cutoff:
|
||||||
cutoff = cycle.end_date
|
cutoff = cycle.end_date
|
||||||
|
|
||||||
|
manual_filter: dict = {"cycle": cycle, "date__gte": start}
|
||||||
|
cc_filter: dict = {"cycle": cycle, "date__gte": start}
|
||||||
|
cw_filter: dict = {"cycle": cycle, "date__gte": start}
|
||||||
|
fs_filter: dict = {"cycle": cycle, "date__gte": start}
|
||||||
|
|
||||||
|
if cutoff is not None:
|
||||||
|
manual_filter["date__lte"] = cutoff
|
||||||
|
cc_filter["date__lte"] = cutoff
|
||||||
|
cw_filter["date__lte"] = cutoff
|
||||||
|
fs_filter["date__lte"] = cutoff
|
||||||
|
|
||||||
manuals = {
|
manuals = {
|
||||||
row.date: row
|
row.date: row
|
||||||
for row in ManualInput.objects.filter(cycle=cycle, date__gte=start, date__lte=cutoff).order_by(
|
for row in ManualInput.objects.filter(**manual_filter).order_by("date", "pk")
|
||||||
"date", "pk"
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
countings = {
|
countings = {
|
||||||
row.date: row
|
row.date: row
|
||||||
for row in ChickenCounting.objects.filter(cycle=cycle, date__gte=start, date__lte=cutoff)
|
for row in ChickenCounting.objects.filter(**cc_filter)
|
||||||
}
|
}
|
||||||
weights = {
|
weights = {
|
||||||
row.date: row
|
row.date: row
|
||||||
for row in ChickenWeight.objects.filter(cycle=cycle, date__gte=start, date__lte=cutoff)
|
for row in ChickenWeight.objects.filter(**cw_filter)
|
||||||
}
|
}
|
||||||
feeds = {
|
feeds = {
|
||||||
row.date: row
|
row.date: row
|
||||||
for row in FeedSacks.objects.filter(cycle=cycle, date__gte=start, date__lte=cutoff)
|
for row in FeedSacks.objects.filter(**fs_filter)
|
||||||
}
|
}
|
||||||
|
|
||||||
written = 0
|
written = 0
|
||||||
harvest_age_sum = 0.0
|
harvest_age_sum = 0.0
|
||||||
|
|
||||||
for day in sorted(set(manuals) | set(countings) | set(weights) | set(feeds)):
|
for day in sorted(set(manuals) | set(countings) | set(weights) | set(feeds)):
|
||||||
if day < start or day > cutoff:
|
if day < start or (cutoff is not None and day > cutoff):
|
||||||
continue
|
continue
|
||||||
manual = manuals.get(day)
|
manual = manuals.get(day)
|
||||||
cc = countings.get(day)
|
cc = countings.get(day)
|
||||||
|
|||||||
@@ -121,3 +121,25 @@ class KpiRollupServiceTests(TestCase):
|
|||||||
refresh_kpi_source_fields(self.cycle, through=day)
|
refresh_kpi_source_fields(self.cycle, through=day)
|
||||||
self.assertTrue(KPI.objects.filter(cycle=self.cycle, date=day).exists())
|
self.assertTrue(KPI.objects.filter(cycle=self.cycle, date=day).exists())
|
||||||
self.assertEqual(KPI.objects.get(cycle=self.cycle, date=day).mortality_total, 21)
|
self.assertEqual(KPI.objects.get(cycle=self.cycle, date=day).mortality_total, 21)
|
||||||
|
|
||||||
|
def test_manual_save_generates_kpi_without_explicit_through(self):
|
||||||
|
day = self.start
|
||||||
|
ChickenCounting.objects.create(cycle=self.cycle, date=day, total_count=1, mortality_count=0)
|
||||||
|
ChickenWeight.objects.create(
|
||||||
|
cycle=self.cycle,
|
||||||
|
date=day,
|
||||||
|
age=0,
|
||||||
|
doc_weight=40,
|
||||||
|
average_weight=35.0,
|
||||||
|
chicken_count=1,
|
||||||
|
uniformity=80,
|
||||||
|
average_daily_gain=0,
|
||||||
|
)
|
||||||
|
FeedSacks.objects.create(cycle=self.cycle, date=day, feed_use_today=15, feed_use_total=15)
|
||||||
|
ManualInput.objects.create(cycle=self.cycle, date=day, mortality_manual=21)
|
||||||
|
recompute_manual_totals(self.cycle)
|
||||||
|
written = refresh_kpi_source_fields(self.cycle)
|
||||||
|
self.assertEqual(written, 1)
|
||||||
|
self.assertTrue(KPI.objects.filter(cycle=self.cycle, date=day).exists())
|
||||||
|
self.assertEqual(KPI.objects.get(cycle=self.cycle, date=day).mortality_total, 21)
|
||||||
|
|
||||||
Reference in new issue
Block a user