18#ifdef PACS_WITH_DATABASE_SYSTEM
24[[nodiscard]] std::string to_timestamp_string(
25 std::chrono::system_clock::time_point tp) {
26 if (tp == std::chrono::system_clock::time_point{}) {
29 auto time = std::chrono::system_clock::to_time_t(tp);
37 std::strftime(buf,
sizeof(buf),
"%Y-%m-%d %H:%M:%S", &tm);
41[[nodiscard]] std::chrono::system_clock::time_point from_timestamp_string(
42 const std::string& str) {
47 if (std::sscanf(str.c_str(),
60 auto time = _mkgmtime(&tm);
62 auto time = timegm(&tm);
64 return std::chrono::system_clock::from_time_t(time);
69sync_conflict_repository::sync_conflict_repository(
70 std::shared_ptr<pacs_database_adapter> db)
71 : base_repository(std::
move(db),
"sync_conflicts",
"study_uid") {}
73auto sync_conflict_repository::find_by_study_uid(std::string_view study_uid)
75 return find_by_id(std::string(study_uid));
78auto sync_conflict_repository::find_by_config(std::string_view config_id)
80 return find_where(
"config_id",
"=", std::string(config_id));
83auto sync_conflict_repository::find_unresolved() -> list_result_type {
84 return find_where(
"resolved",
"=",
static_cast<int64_t
>(0));
87auto sync_conflict_repository::resolve(
88 std::string_view study_uid,
89 client::conflict_resolution resolution) -> VoidResult {
90 if (!db() || !db()->is_connected()) {
91 return VoidResult(kcenon::common::error_info{
92 -1,
"Database not connected",
"sync_conflict_repository"});
95 std::ostringstream sql;
97 UPDATE sync_conflicts SET
100 << client::to_string(resolution) << R"(',
101 resolved_at = datetime('now')
102 WHERE study_uid = ')"
105 auto result = storage_session().update(sql.str());
106 if (result.is_err()) {
107 return VoidResult(result.error());
110 return kcenon::common::ok();
113auto sync_conflict_repository::cleanup_old(std::chrono::hours max_age)
115 if (!db() || !db()->is_connected()) {
116 return Result<size_t>(kcenon::common::error_info{
117 -1,
"Database not connected",
"sync_conflict_repository"});
120 std::ostringstream sql;
122 DELETE FROM sync_conflicts
123 WHERE resolved = 1 AND resolved_at < datetime('now', '-)"
124 << max_age.count() << R"( hours'))";
126 auto result = storage_session().remove(sql.str());
127 if (result.is_err()) {
128 return Result<size_t>(result.error());
131 return Result<size_t>(
static_cast<size_t>(result.value()));
134auto sync_conflict_repository::map_row_to_entity(
const database_row& row)
const
135 -> client::sync_conflict {
138 conflict.pk = std::stoll(row.at(
"pk"));
139 conflict.config_id = row.at(
"config_id");
140 conflict.study_uid = row.at(
"study_uid");
141 conflict.patient_id = row.at(
"patient_id");
143 client::sync_conflict_type_from_string(row.at(
"conflict_type"));
144 conflict.local_modified = parse_timestamp(row.at(
"local_modified"));
145 conflict.remote_modified = parse_timestamp(row.at(
"remote_modified"));
147 std::stoull(row.at(
"local_instance_count"));
149 std::stoull(row.at(
"remote_instance_count"));
150 conflict.resolved = (row.at(
"resolved") ==
"1");
152 client::conflict_resolution_from_string(row.at(
"resolution"));
153 conflict.detected_at = parse_timestamp(row.at(
"detected_at"));
155 auto resolved_at_str = row.at(
"resolved_at");
156 if (!resolved_at_str.empty()) {
157 conflict.resolved_at = parse_timestamp(resolved_at_str);
163auto sync_conflict_repository::entity_to_row(
164 const client::sync_conflict& entity)
const
165 -> std::map<std::string, database_value> {
166 std::map<std::string, database_value> row = {
167 {
"config_id", entity.config_id},
168 {
"study_uid", entity.study_uid},
169 {
"patient_id", entity.patient_id},
170 {
"conflict_type", std::string(client::to_string(entity.conflict_type))},
171 {
"local_modified", format_timestamp(entity.local_modified)},
172 {
"remote_modified", format_timestamp(entity.remote_modified)},
173 {
"local_instance_count",
174 static_cast<int64_t
>(entity.local_instance_count)},
175 {
"remote_instance_count",
176 static_cast<int64_t
>(entity.remote_instance_count)},
177 {
"resolved",
static_cast<int64_t
>(entity.resolved ? 1 : 0)},
179 entity.resolved ? std::string(client::to_string(entity.resolution_used))
181 {
"detected_at", format_timestamp(entity.detected_at)}};
183 if (entity.resolved_at.has_value()) {
184 row[
"resolved_at"] = format_timestamp(entity.resolved_at.value());
190auto sync_conflict_repository::get_pk(
const client::sync_conflict& entity)
const
192 return entity.study_uid;
195auto sync_conflict_repository::has_pk(
const client::sync_conflict& entity)
const
197 return !entity.study_uid.empty();
200auto sync_conflict_repository::select_columns() const
201 -> std::vector<std::
string> {
209 "local_instance_count",
210 "remote_instance_count",
217auto sync_conflict_repository::parse_timestamp(
const std::string& str)
const
218 -> std::chrono::system_clock::time_point {
219 return from_timestamp_string(str);
222auto sync_conflict_repository::format_timestamp(
223 std::chrono::system_clock::time_point tp)
const -> std::string {
224 return to_timestamp_string(tp);
@ move
C-MOVE move request/response.
Repository for sync conflict records using base_repository pattern.