update sync chicken counting realtime mirrored from weight sync, remove panel iot cron
This commit is contained in:
1 parent
83a41fbbcb
commit
a6d4ddac65
9 files changed
+40
-17
No files matched your search
@@ -166,10 +166,10 @@ Adds `127.0.0.1:5432:5432` while keeping the server’s `15432` mapping.
|
|||||||
|
|
||||||
The `cron` service runs:
|
The `cron` service runs:
|
||||||
|
|
||||||
- **Every 10 min** — `sync_iot_from_api` (IoT panel, real-time — not gated by publish cutoff)
|
- **Every 1 min** — `sync_chicken_counting_from_edge` (chicken counting + mortality + weight edge sync)
|
||||||
- **At `DASHBOARD_PUBLISH_HOUR`:`DASHBOARD_PUBLISH_MINUTE` daily** — `sync_karung_from_web_admin` then `recompute_kpi_rollups` (entrypoint renders cron from these env vars; default 17:00)
|
- **At `DASHBOARD_PUBLISH_HOUR`:`DASHBOARD_PUBLISH_MINUTE` daily** — `sync_karung_from_web_admin` then `recompute_kpi_rollups` (entrypoint renders cron from these env vars; default 17:00)
|
||||||
|
|
||||||
Dashboard reads hide today's KPI/counting/weight/karung data until the configured publish time WIB. **IoT panel is excluded** and updates live every 10 minutes.
|
Dashboard reads hide today's KPI/counting/weight/karung data until the configured publish time WIB. **IoT panel is excluded** (IoT devices push data directly via webhook) and updates live.
|
||||||
|
|
||||||
Logs: `docker compose exec cron tail -f /var/log/cron.log`
|
Logs: `docker compose exec cron tail -f /var/log/cron.log`
|
||||||
|
|
||||||
|
|||||||
@@ -74,6 +74,10 @@ const Dashboard: React.FC<Props> = ({ setView }) => {
|
|||||||
}
|
}
|
||||||
setError(null);
|
setError(null);
|
||||||
try {
|
try {
|
||||||
|
await Promise.all([
|
||||||
|
api.countings.sync({ cycle_id: cycleId }).catch(() => null),
|
||||||
|
api.weights.sync({ cycle_id: cycleId }).catch(() => null),
|
||||||
|
]);
|
||||||
const [kpiRows, countingRows, manualRows, weightRows, freshCycle] = await Promise.all([
|
const [kpiRows, countingRows, manualRows, weightRows, freshCycle] = await Promise.all([
|
||||||
api.kpis.list({ cycle_id: cycleId }),
|
api.kpis.list({ cycle_id: cycleId }),
|
||||||
api.countings.list({ cycle_id: cycleId }),
|
api.countings.list({ cycle_id: cycleId }),
|
||||||
|
|||||||
@@ -36,6 +36,12 @@ const WeightMonitoringPage: React.FC<{ onSettings: () => void }> = ({ onSettings
|
|||||||
}
|
}
|
||||||
setError(null);
|
setError(null);
|
||||||
try {
|
try {
|
||||||
|
try {
|
||||||
|
await api.weights.sync({ cycle_id: cycleId });
|
||||||
|
} catch {
|
||||||
|
// Best-effort edge sync: don't fail page load if edge vision API is temporarily unreachable
|
||||||
|
}
|
||||||
|
|
||||||
const [list, freshCycle] = await Promise.all([
|
const [list, freshCycle] = await Promise.all([
|
||||||
api.weights.list({ cycle_id: cycleId }),
|
api.weights.list({ cycle_id: cycleId }),
|
||||||
api.cycles?.retrieve?.(cycleId)?.catch(() => null) ?? Promise.resolve(null),
|
api.cycles?.retrieve?.(cycleId)?.catch(() => null) ?? Promise.resolve(null),
|
||||||
|
|||||||
@@ -68,6 +68,12 @@ const DataManagementPage: React.FC<{ onSettings: () => void }> = ({ onSettings }
|
|||||||
}
|
}
|
||||||
setError(null);
|
setError(null);
|
||||||
try {
|
try {
|
||||||
|
try {
|
||||||
|
await api.countings.sync({ cycle_id: cycleId });
|
||||||
|
} catch {
|
||||||
|
// Best-effort edge sync: don't fail page load if edge vision API is temporarily unreachable
|
||||||
|
}
|
||||||
|
|
||||||
const [k, c, m, freshCycle] = await Promise.all([
|
const [k, c, m, freshCycle] = await Promise.all([
|
||||||
api.kpis.list({ cycle_id: cycleId }),
|
api.kpis.list({ cycle_id: cycleId }),
|
||||||
api.countings.list({ cycle_id: cycleId }),
|
api.countings.list({ cycle_id: cycleId }),
|
||||||
|
|||||||
@@ -116,6 +116,15 @@ describe('DataManagementPage', () => {
|
|||||||
expect(screen.getAllByText('0.0%').length).toBeGreaterThan(0);
|
expect(screen.getAllByText('0.0%').length).toBeGreaterThan(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('automatically calls chicken counting sync on load', async () => {
|
||||||
|
render(<DataManagementPage onSettings={() => undefined} />);
|
||||||
|
|
||||||
|
await screen.findByRole('heading', { name: 'Hitung Ayam' });
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(api.countings.sync).toHaveBeenCalledWith({ cycle_id: 1 });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('calls chicken counting sync when Sync is clicked', async () => {
|
it('calls chicken counting sync when Sync is clicked', async () => {
|
||||||
render(<DataManagementPage onSettings={() => undefined} />);
|
render(<DataManagementPage onSettings={() => undefined} />);
|
||||||
|
|
||||||
|
|||||||
@@ -19,10 +19,10 @@ COPY backend/requirements.txt .
|
|||||||
RUN pip install -r requirements.txt
|
RUN pip install -r requirements.txt
|
||||||
|
|
||||||
COPY backend/ .
|
COPY backend/ .
|
||||||
COPY docker/entrypoint-api.sh docker/entrypoint-cron.sh docker/karung_sync_loop.sh /app/
|
COPY docker/entrypoint-api.sh docker/entrypoint-cron.sh docker/karung_sync_loop.sh docker/edge_sync_loop.sh /app/
|
||||||
COPY docker/crontab /etc/cron.d/newdashboard
|
COPY docker/crontab /etc/cron.d/newdashboard
|
||||||
|
|
||||||
RUN chmod +x /app/entrypoint-api.sh /app/entrypoint-cron.sh /app/karung_sync_loop.sh \
|
RUN chmod +x /app/entrypoint-api.sh /app/entrypoint-cron.sh /app/karung_sync_loop.sh /app/edge_sync_loop.sh \
|
||||||
&& mkdir -p /app/data /app/media /app/staticfiles \
|
&& mkdir -p /app/data /app/media /app/staticfiles \
|
||||||
&& chmod 0644 /etc/cron.d/newdashboard \
|
&& chmod 0644 /etc/cron.d/newdashboard \
|
||||||
&& crontab /etc/cron.d/newdashboard
|
&& crontab /etc/cron.d/newdashboard
|
||||||
|
|||||||
+1
-7
@@ -4,12 +4,6 @@ PATH=/usr/local/bin:/usr/bin:/bin
|
|||||||
# NOTE: docker/entrypoint-cron.sh regenerates this file from DASHBOARD_PUBLISH_HOUR/MINUTE
|
# NOTE: docker/entrypoint-cron.sh regenerates this file from DASHBOARD_PUBLISH_HOUR/MINUTE
|
||||||
# at container start. Keep this checked-in copy as documentation / fallback defaults (17:00).
|
# at container start. Keep this checked-in copy as documentation / fallback defaults (17:00).
|
||||||
|
|
||||||
# IoT panel: real-time sync every 10 minutes (not gated by publish cutoff).
|
# Karung-web-admin & Edge Chicken Counting: every 30s via sync loops (started by entrypoint-cron.sh).
|
||||||
*/10 * * * * root cd /app && /usr/local/bin/python manage.py sync_iot_from_api >> /var/log/cron.log 2>&1
|
|
||||||
|
|
||||||
# Chicken counting + mortality + weight edge sync every 10 minutes (not gated by publish cutoff).
|
|
||||||
*/10 * * * * root cd /app && /usr/local/bin/python manage.py sync_chicken_counting_from_edge >> /var/log/cron.log 2>&1
|
|
||||||
|
|
||||||
# Karung-web-admin: every 30s via karung_sync_loop.sh (started by entrypoint-cron.sh).
|
|
||||||
# Daily publish — recompute KPI rollups (hour/minute come from DASHBOARD_PUBLISH_* at runtime).
|
# Daily publish — recompute KPI rollups (hour/minute come from DASHBOARD_PUBLISH_* at runtime).
|
||||||
0 17 * * * root cd /app && /usr/local/bin/python manage.py sync_karung_from_web_admin >> /var/log/cron.log 2>&1 && /usr/local/bin/python manage.py recompute_kpi_rollups >> /var/log/cron.log 2>&1
|
0 17 * * * root cd /app && /usr/local/bin/python manage.py sync_karung_from_web_admin >> /var/log/cron.log 2>&1 && /usr/local/bin/python manage.py recompute_kpi_rollups >> /var/log/cron.log 2>&1
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Sub-minute chicken counting + mortality + weight edge sync.
|
||||||
|
set -eu
|
||||||
|
cd /app
|
||||||
|
while true; do
|
||||||
|
/usr/local/bin/python manage.py sync_chicken_counting_from_edge >> /var/log/cron.log 2>&1 || true
|
||||||
|
sleep 30
|
||||||
|
done
|
||||||
@@ -13,13 +13,8 @@ CRON_FILE=/etc/cron.d/newdashboard
|
|||||||
echo "SHELL=/bin/sh"
|
echo "SHELL=/bin/sh"
|
||||||
echo "PATH=/usr/local/bin:/usr/bin:/bin"
|
echo "PATH=/usr/local/bin:/usr/bin:/bin"
|
||||||
echo ""
|
echo ""
|
||||||
echo "# IoT panel: real-time sync every 10 minutes (not gated by publish cutoff)."
|
|
||||||
echo "*/10 * * * * root cd /app && /usr/local/bin/python manage.py sync_iot_from_api >> /var/log/cron.log 2>&1"
|
|
||||||
echo ""
|
|
||||||
echo "# Chicken counting + mortality + weight edge sync every 10 minutes."
|
|
||||||
echo "*/10 * * * * root cd /app && /usr/local/bin/python manage.py sync_chicken_counting_from_edge >> /var/log/cron.log 2>&1"
|
|
||||||
echo ""
|
|
||||||
echo "# Karung-web-admin: every 30s via karung_sync_loop.sh (started below)."
|
echo "# Karung-web-admin: every 30s via karung_sync_loop.sh (started below)."
|
||||||
|
echo "# Chicken counting + weight edge: every 30s via edge_sync_loop.sh (started below)."
|
||||||
echo "# Daily publish — karung sync then KPI rollups at configured cutoff."
|
echo "# Daily publish — karung sync then KPI rollups at configured cutoff."
|
||||||
echo "${PUBLISH_MINUTE} ${PUBLISH_HOUR} * * * root cd /app && /usr/local/bin/python manage.py sync_karung_from_web_admin >> /var/log/cron.log 2>&1 && /usr/local/bin/python manage.py recompute_kpi_rollups >> /var/log/cron.log 2>&1"
|
echo "${PUBLISH_MINUTE} ${PUBLISH_HOUR} * * * root cd /app && /usr/local/bin/python manage.py sync_karung_from_web_admin >> /var/log/cron.log 2>&1 && /usr/local/bin/python manage.py recompute_kpi_rollups >> /var/log/cron.log 2>&1"
|
||||||
} > "$CRON_FILE"
|
} > "$CRON_FILE"
|
||||||
@@ -27,4 +22,5 @@ chmod 0644 "$CRON_FILE"
|
|||||||
crontab "$CRON_FILE"
|
crontab "$CRON_FILE"
|
||||||
|
|
||||||
/app/karung_sync_loop.sh &
|
/app/karung_sync_loop.sh &
|
||||||
|
/app/edge_sync_loop.sh &
|
||||||
exec cron -f
|
exec cron -f
|
||||||
Reference in new issue
Block a user