The operator's daily clicks become durable assets:
Label export (GET /api/export/labels?days=N, JSONL download):
- Every 확인/오탐 click is a human verdict on a detection, and each verdict
carries a blurred 3-frame evidence timeline. The export turns that into a
dataset: one JSON line per event with type/severity/camera/zone/metrics/
spatial, label (confirmed | false_positive | unreviewed), labeled_by, and
snapshot uuids (images fetched via /snap, which re-checks session + site).
ergo_report rows are excluded - statistics are not labelable detections.
- Admin-only ('manage_compliance'): bulk export of detection history is a
compliance decision, not an operator convenience. Site scope applies.
- This is where the deferred P1-C evaluation set will come from for free.
Improvement actions (move_actions + GET/POST /api/actions):
- Record a site fix against a camera ("미끄럼 방지 매트 설치"); the report's
new card compares detection density over EQUAL windows before/after the
fix (capped 30d; under 3 days shows "측정 중" - two days of data is noise,
not a verdict). Improvement shows accent, worsening shows red - the card
reports honestly in both directions, which is exactly what makes the
improvement numbers credible.
- Future timestamps and >1y-old entries are rejected (the comparison could
not be fair). Recording needs edit_zones + CSRF.
Verified: PHPUnit 145 pass (5 new: export perm gate + line shape/labels/
snapshot join, action validation, equal-window effect math, pending state)
+ Chrome real-render (action card with better/worse/pending rows, export
link, no console errors).
66 lines
2.6 KiB
PHP
66 lines
2.6 KiB
PHP
<?php
|
||
require __DIR__ . '/layout.php';
|
||
render_header('안전 리포트');
|
||
?>
|
||
<div class="report">
|
||
<header class="report-head">
|
||
<div>
|
||
<h1>안전 운영 리포트</h1>
|
||
<p id="report-range" class="report-range">기간 집계 중…</p>
|
||
</div>
|
||
<div class="report-controls">
|
||
<label class="field">기간
|
||
<select id="report-days" aria-label="리포트 기간">
|
||
<option value="7">최근 7일</option>
|
||
<option value="30">최근 30일</option>
|
||
<option value="90">최근 90일</option>
|
||
</select>
|
||
</label>
|
||
<button type="button" id="report-print">인쇄</button>
|
||
</div>
|
||
</header>
|
||
|
||
<div id="report-alert" class="report-spike" hidden role="alert"></div>
|
||
<div id="report-kpis" class="report-kpis" aria-label="핵심 지표"></div>
|
||
|
||
<div class="report-grid">
|
||
<section class="report-card">
|
||
<h2>유형별 감지</h2>
|
||
<div id="report-types" class="bar-list"></div>
|
||
</section>
|
||
<section class="report-card">
|
||
<h2>카메라별 위험 순위</h2>
|
||
<div id="report-cameras" class="bar-list"></div>
|
||
</section>
|
||
<section class="report-card">
|
||
<h2>작업자세 부담 노출</h2>
|
||
<div id="report-ergo" class="bar-list"></div>
|
||
</section>
|
||
<section class="report-card">
|
||
<h2>존 규칙 위반 · 처리 현황</h2>
|
||
<div id="report-rules"></div>
|
||
</section>
|
||
<section class="report-card report-card-wide">
|
||
<h2>시간 패턴 (요일 × 시간)</h2>
|
||
<div id="report-pattern"></div>
|
||
</section>
|
||
<section class="report-card report-card-wide">
|
||
<h2>개선 조치 효과</h2>
|
||
<!-- 조치를 기록하면 시스템이 전후 동일 길이 창의 감지 밀도를 비교한다 —
|
||
안전 투자의 효과가 숫자로 나오는 자리. -->
|
||
<form id="action-form" class="action-form">
|
||
<input type="text" id="action-camera" placeholder="카메라 ID" maxlength="64" aria-label="카메라 ID">
|
||
<input type="text" id="action-note" placeholder="조치 내용 (예: 미끄럼 방지 매트 설치)" maxlength="500" aria-label="조치 내용">
|
||
<button type="submit">조치 기록</button>
|
||
</form>
|
||
<div id="report-actions"></div>
|
||
</section>
|
||
</div>
|
||
|
||
<p id="report-foot" class="report-foot"></p>
|
||
<p class="report-foot"><a id="export-labels" href="#">라벨 데이터셋 내려받기 (JSONL, 관리자)</a>
|
||
— 확인/오탐 판정이 붙은 감지 이력. 평가셋·임계 튜닝의 원료입니다.</p>
|
||
</div>
|
||
<script src="<?= view_url('/assets/report.js') ?>"></script>
|
||
<?php
|
||
render_footer();
|