#pragma once #include #include #include #include #include #include #include #include #include #include "chicken_counter/types.hpp" namespace cc { inline std::string now_iso() { auto now = std::chrono::system_clock::now(); auto t = std::chrono::system_clock::to_time_t(now); std::ostringstream oss; oss << std::put_time(std::gmtime(&t), "%FT%TZ"); return oss.str(); } inline std::string relative_output_path(const std::string& path, const std::string& base_dir) { if (path.empty()) return ""; if (base_dir.empty()) return std::filesystem::path(path).filename().string(); try { auto rel = std::filesystem::relative(path, base_dir); return rel.string(); } catch (...) { return std::filesystem::path(path).filename().string(); } } inline nlohmann::json build_camera_report_entry( const CameraBatchResult& item, const std::string& output_dir = "") { if (item.skipped) { return {{"skipped", true}, {"skip_reason", item.skip_reason}, {"total_entered", 0}}; } return { {"total_entered", item.pipeline.total_entered_count}, {"source_video", std::filesystem::path(item.pipeline.source_video).filename().string()}, {"vis_video", relative_output_path(item.pipeline.vis_video_path, output_dir)}, {"compressed_video", relative_output_path(item.compressed_video_path, output_dir)}, {"compressed_size_mb", item.compressed_size_mb}, {"frames_processed", item.pipeline.frames_processed}, {"stopped_reason", item.pipeline.stopped_reason}, {"elapsed_seconds", std::round(item.pipeline.elapsed_seconds * 10.0) / 10.0} }; } struct BatchReport { std::string date; std::string generated_at; nlohmann::json cameras; int total_entered_sum; }; inline BatchReport build_batch_report( const std::string& date, const std::vector& results, const std::string& output_dir = "") { BatchReport report; report.date = date; report.generated_at = now_iso(); report.total_entered_sum = 0; for (const auto& item : results) { auto entry = build_camera_report_entry(item, output_dir); if (entry.empty()) continue; report.cameras[item.camera_id] = entry; if (!item.skipped) report.total_entered_sum += entry.value("total_entered", 0); } return report; } inline std::string write_json_file(const std::string& path, const nlohmann::json& j) { namespace fs = std::filesystem; fs::create_directories(fs::path(path).parent_path()); std::ofstream f(path); f << j.dump(2); std::cerr << "[report] wrote " << path << "\n"; return path; } inline std::string write_batch_report(const BatchReport& report, const std::string& output_path) { nlohmann::json j = { {"date", report.date}, {"generated_at", report.generated_at}, {"cameras", report.cameras}, {"total_entered_sum", report.total_entered_sum} }; return write_json_file(output_path, j); } inline std::string write_camera_report( const std::string& date, const CameraBatchResult& item, const std::string& output_dir) { namespace fs = std::filesystem; fs::create_directories(output_dir); auto entry = build_camera_report_entry(item, output_dir); nlohmann::json payload = { {"date", date}, {"camera_id", item.camera_id}, {"generated_at", now_iso()} }; for (auto& [k, v] : entry.items()) payload[k] = v; std::string path = output_dir + "/" + item.camera_id + "_counts_" + date + ".json"; return write_json_file(path, payload); } inline std::string persist_batch_reports( const std::string& date, const std::vector& results, const std::string& output_dir) { const auto& latest = results.back(); write_camera_report(date, latest, output_dir); std::string aggregate_path = output_dir + "/counts_" + date + ".json"; auto report = build_batch_report(date, results, output_dir); write_batch_report(report, aggregate_path); return aggregate_path; } } // namespace cc