Files
chicken-counting-sukawarna-det/cpp/include/chicken_counter/report.hpp
T
2026-07-22 00:03:02 +07:00

140 lines
4.3 KiB
C++

#pragma once
#include <chrono>
#include <filesystem>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <nlohmann/json.hpp>
#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<CameraBatchResult>& 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<CameraBatchResult>& 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