adjust api for newest karung admin

This commit is contained in:
Alberto-Audrix committed 2026-08-28 10:58:13 +07:00
1 parent 5b4d9d56d4
commit d7d268ad48
4 files changed
+38 -19

No files matched your search

+3
View File
@@ -3,6 +3,9 @@ __pycache__/
*.py[codz] *.py[codz]
*$py.class *$py.class
#superpower
docs
# C extensions # C extensions
*.so *.so
+10 -2
View File
@@ -183,6 +183,7 @@ def api_snapshot_image(category, filename):
def _ensure_db(): def _ensure_db():
Path(DB_PATH).parent.mkdir(parents=True, exist_ok=True)
conn = sqlite3.connect(DB_PATH) conn = sqlite3.connect(DB_PATH)
cur = conn.cursor() cur = conn.cursor()
cur.execute( cur.execute(
@@ -362,7 +363,7 @@ def _empty_counts():
def _empty_current(): def _empty_current():
return { return {
"counting_date": get_counting_date(), "counting_date": get_counting_date(),
"count": 0, "total_count": 0,
"counts": _empty_counts(), "counts": _empty_counts(),
"zones": ZONES_META, "zones": ZONES_META,
"start_time": None, "start_time": None,
@@ -390,9 +391,16 @@ def _load_current_state():
"zone_2": int(data.get("count_right", 0) or 0), "zone_2": int(data.get("count_right", 0) or 0),
} }
counts = _normalize_counts(counts) counts = _normalize_counts(counts)
# Prefer total_count; fall back to legacy "count" then sum of zones.
if data.get("total_count") is not None:
total = int(data.get("total_count") or 0)
elif data.get("count") is not None:
total = int(data.get("count") or 0)
else:
total = sum(counts.values())
return { return {
"counting_date": data.get("counting_date") or get_counting_date(), "counting_date": data.get("counting_date") or get_counting_date(),
"count": int(data.get("count", sum(counts.values())) or 0), "total_count": total,
"counts": counts, "counts": counts,
"zones": ZONES_META, "zones": ZONES_META,
"start_time": data.get("start_time"), "start_time": data.get("start_time"),
+9 -8
View File
@@ -177,7 +177,8 @@ class CounterStore:
# Preserve migrated zone_2 even if current config is 1 zone? Spec says # Preserve migrated zone_2 even if current config is 1 zone? Spec says
# configured ids only — drop unknown keys from active counts. # configured ids only — drop unknown keys from active counts.
state['counts'] = normalized state['counts'] = normalized
state['count'] = sum(normalized.values()) state['total_count'] = sum(normalized.values())
state.pop('count', None)
state.pop('count_left', None) state.pop('count_left', None)
state.pop('count_right', None) state.pop('count_right', None)
state.setdefault('counted_event_ids', []) state.setdefault('counted_event_ids', [])
@@ -208,7 +209,7 @@ class CounterStore:
) )
state['counted_event_ids'] = [] state['counted_event_ids'] = []
self.log( self.log(
f"Resumed {current_date} with total={state['count']} " f"Resumed {current_date} with total={state['total_count']} "
f"counts={state['counts']}" f"counts={state['counts']}"
) )
with open(self.state_file, 'w', encoding='utf-8') as f: with open(self.state_file, 'w', encoding='utf-8') as f:
@@ -236,7 +237,7 @@ class CounterStore:
counts = self._empty_counts() counts = self._empty_counts()
self.current_state = { self.current_state = {
'counting_date': counting_date, 'counting_date': counting_date,
'count': 0, 'total_count': 0,
'counts': counts, 'counts': counts,
'start_time': now, 'start_time': now,
'last_detection_time': now, 'last_detection_time': now,
@@ -264,7 +265,7 @@ class CounterStore:
counted = False counted = False
event_key = f"{track_id}_{zone_id}" event_key = f"{track_id}_{zone_id}"
if event_key not in self.current_state['counted_event_ids']: if event_key not in self.current_state['counted_event_ids']:
self.current_state['count'] += 1 self.current_state['total_count'] += 1
self.current_state['counts'][zone_id] = ( self.current_state['counts'][zone_id] = (
int(self.current_state['counts'].get(zone_id, 0)) + 1 int(self.current_state['counts'].get(zone_id, 0)) + 1
) )
@@ -272,14 +273,14 @@ class CounterStore:
counted = True counted = True
self.log( self.log(
f'Counted {zone_id} (track {track_id}) | {counting_date} ' f'Counted {zone_id} (track {track_id}) | {counting_date} '
f'total: {self.current_state["count"]} ' f'total: {self.current_state["total_count"]} '
f'counts={self.current_state["counts"]}' f'counts={self.current_state["counts"]}'
) )
self._persist_day() self._persist_day()
self.current_state['last_detection_time'] = datetime.now().isoformat() self.current_state['last_detection_time'] = datetime.now().isoformat()
self.save_state() self.save_state()
return self.current_state['count'], day_started, counted return self.current_state['total_count'], day_started, counted
def _persist_day(self): def _persist_day(self):
state = self.current_state state = self.current_state
@@ -298,7 +299,7 @@ class CounterStore:
""", """,
( (
state['counting_date'], self.camera_name, self.object_label, state['counting_date'], self.camera_name, self.object_label,
state['count'], state['total_count'],
state['start_time'], datetime.now().isoformat(), state['start_time'], datetime.now().isoformat(),
), ),
) )
@@ -339,7 +340,7 @@ class CounterStore:
def current_count(self): def current_count(self):
if self.current_state is None: if self.current_state is None:
return 0 return 0
return self.current_state['count'] return self.current_state['total_count']
@property @property
def current_counts(self): def current_counts(self):
+16 -9
View File
@@ -772,8 +772,9 @@
<div class="footer">Edge Nano Counter by ZenAi</div> <div class="footer">Edge Nano Counter by ZenAi</div>
</div> </div>
<script type="application/json" id="zones-config">{{ zones | tojson }}</script>
<script> <script>
const ZONES = {{ zones | tojson }}; const ZONES = JSON.parse(document.getElementById('zones-config').textContent);
const ZONE_COLORS = [ const ZONE_COLORS = [
['rgba(0, 255, 136, 0.7)', 'rgba(0, 255, 136, 1)'], ['rgba(0, 255, 136, 0.7)', 'rgba(0, 255, 136, 1)'],
['rgba(255, 45, 120, 0.7)', 'rgba(255, 45, 120, 1)'], ['rgba(255, 45, 120, 0.7)', 'rgba(255, 45, 120, 1)'],
@@ -883,7 +884,9 @@ async function loadCurrentCounter() {
if (!res.ok) throw new Error('No active counter'); if (!res.ok) throw new Error('No active counter');
const data = await res.json(); const data = await res.json();
const counts = data.counts || {}; const counts = data.counts || {};
const total = data.count != null ? data.count : Object.values(counts).reduce((a, b) => a + (b || 0), 0); const total = data.total_count != null
? data.total_count
: Object.values(counts).reduce((a, b) => a + (b || 0), 0);
document.getElementById('today-count').textContent = total.toLocaleString(); document.getElementById('today-count').textContent = total.toLocaleString();
ZONES.forEach(z => { ZONES.forEach(z => {
const el = document.getElementById('today-' + z.id); const el = document.getElementById('today-' + z.id);
@@ -902,12 +905,16 @@ async function loadSummary() {
try { try {
const res = await fetch('/api/summary'); const res = await fetch('/api/summary');
const data = await res.json(); const data = await res.json();
if (!data || !data.today) {
console.error('Failed to load summary: unexpected payload', data);
return;
}
document.getElementById('current-date').textContent = data.today.date; document.getElementById('current-date').textContent = data.today.date || '--';
const yCounts = (data.yesterday && data.yesterday.counts) || {}; const yCounts = (data.yesterday && data.yesterday.counts) || {};
document.getElementById('yesterday-count').textContent = ( document.getElementById('yesterday-count').textContent = (
data.yesterday.total_count != null data.yesterday && data.yesterday.total_count != null
? data.yesterday.total_count ? data.yesterday.total_count
: Object.values(yCounts).reduce((a, b) => a + (b || 0), 0) : Object.values(yCounts).reduce((a, b) => a + (b || 0), 0)
).toLocaleString(); ).toLocaleString();
@@ -916,14 +923,14 @@ async function loadSummary() {
if (el) el.textContent = (yCounts[z.id] || 0).toLocaleString(); if (el) el.textContent = (yCounts[z.id] || 0).toLocaleString();
}); });
document.getElementById('avg-count').textContent = Number(data.average_per_day).toLocaleString(); document.getElementById('avg-count').textContent = Number(data.average_per_day || 0).toLocaleString();
document.getElementById('total-days').textContent = `${data.all_time.total_days} days recorded`; document.getElementById('total-days').textContent = `${(data.all_time && data.all_time.total_days) || 0} days recorded`;
document.getElementById('best-count').textContent = data.best_day.count.toLocaleString(); document.getElementById('best-count').textContent = ((data.best_day && data.best_day.count) || 0).toLocaleString();
document.getElementById('best-date').textContent = data.best_day.date || '--'; document.getElementById('best-date').textContent = (data.best_day && data.best_day.date) || '--';
const gCounts = (data.all_time && data.all_time.counts) || {}; const gCounts = (data.all_time && data.all_time.counts) || {};
document.getElementById('grand-total').textContent = (data.all_time.grand_total || 0).toLocaleString(); document.getElementById('grand-total').textContent = ((data.all_time && data.all_time.grand_total) || 0).toLocaleString();
ZONES.forEach(z => { ZONES.forEach(z => {
const el = document.getElementById('grand-' + z.id); const el = document.getElementById('grand-' + z.id);
if (el) el.textContent = (gCounts[z.id] || 0).toLocaleString(); if (el) el.textContent = (gCounts[z.id] || 0).toLocaleString();