19#ifdef PACS_WITH_DATABASE_SYSTEM
26[[nodiscard]] std::string to_timestamp_string(
27 std::chrono::system_clock::time_point tp) {
28 if (tp == std::chrono::system_clock::time_point{}) {
31 auto time = std::chrono::system_clock::to_time_t(tp);
39 std::strftime(buf,
sizeof(buf),
"%Y-%m-%d %H:%M:%S", &tm);
44[[nodiscard]] std::chrono::system_clock::time_point from_timestamp_string(
45 const std::string& str) {
50 if (std::sscanf(str.c_str(),
63 auto time = _mkgmtime(&tm);
65 auto time = timegm(&tm);
67 return std::chrono::system_clock::from_time_t(time);
76sync_config_repository::sync_config_repository(
77 std::shared_ptr<pacs_database_adapter> db)
78 : base_repository(std::
move(db),
"sync_configs",
"config_id") {}
84auto sync_config_repository::find_by_config_id(std::string_view config_id)
86 return find_by_id(std::string(config_id));
89auto sync_config_repository::find_enabled() -> list_result_type {
90 return find_where(
"enabled",
"=",
static_cast<int64_t
>(1));
93auto sync_config_repository::find_by_source_node(std::string_view node_id)
95 return find_where(
"source_node_id",
"=", std::string(node_id));
98auto sync_config_repository::update_stats(
99 std::string_view config_id,
101 size_t studies_synced) -> VoidResult {
102 if (!db() || !db()->is_connected()) {
103 return VoidResult(kcenon::common::error_info{
104 -1,
"Database not connected",
"sync_config_repository"});
107 std::ostringstream sql;
110 UPDATE sync_configs SET
111 total_syncs = total_syncs + 1,
112 studies_synced = studies_synced + )"
113 << studies_synced << R"(,
114 last_sync = datetime('now'),
115 last_successful_sync = datetime('now'),
116 updated_at = datetime('now')
117 WHERE config_id = ')"
121 UPDATE sync_configs SET
122 total_syncs = total_syncs + 1,
123 last_sync = datetime('now'),
124 updated_at = datetime('now')
125 WHERE config_id = ')"
129 auto result = storage_session().update(sql.str());
130 if (result.is_err()) {
131 return VoidResult(result.error());
134 return kcenon::common::ok();
141auto sync_config_repository::map_row_to_entity(
const database_row& row)
const
142 -> client::sync_config {
143 client::sync_config config;
145 config.pk = std::stoll(row.at(
"pk"));
146 config.config_id = row.at(
"config_id");
147 config.source_node_id = row.at(
"source_node_id");
148 config.name = row.at(
"name");
149 config.enabled = (row.at(
"enabled") ==
"1");
150 config.lookback = std::chrono::hours(std::stoi(row.at(
"lookback_hours")));
151 config.modalities = deserialize_vector(row.at(
"modalities_json"));
152 config.patient_id_patterns =
153 deserialize_vector(row.at(
"patient_patterns_json"));
155 client::sync_direction_from_string(row.at(
"sync_direction"));
156 config.delete_missing = (row.at(
"delete_missing") ==
"1");
157 config.overwrite_existing = (row.at(
"overwrite_existing") ==
"1");
158 config.sync_metadata_only = (row.at(
"sync_metadata_only") ==
"1");
159 config.schedule_cron = row.at(
"schedule_cron");
160 config.last_sync = parse_timestamp(row.at(
"last_sync"));
161 config.last_successful_sync =
162 parse_timestamp(row.at(
"last_successful_sync"));
163 config.total_syncs = std::stoull(row.at(
"total_syncs"));
164 config.studies_synced = std::stoull(row.at(
"studies_synced"));
169auto sync_config_repository::entity_to_row(
const client::sync_config& entity)
170 const -> std::map<std::string, database_value> {
171 return {{
"config_id", entity.config_id},
172 {
"source_node_id", entity.source_node_id},
173 {
"name", entity.name},
174 {
"enabled",
static_cast<int64_t
>(entity.enabled ? 1 : 0)},
175 {
"lookback_hours",
static_cast<int64_t
>(entity.lookback.count())},
176 {
"modalities_json", serialize_vector(entity.modalities)},
177 {
"patient_patterns_json",
178 serialize_vector(entity.patient_id_patterns)},
179 {
"sync_direction", std::string(client::to_string(entity.direction))},
180 {
"delete_missing",
static_cast<int64_t
>(entity.delete_missing ? 1 : 0)},
181 {
"overwrite_existing",
182 static_cast<int64_t
>(entity.overwrite_existing ? 1 : 0)},
183 {
"sync_metadata_only",
184 static_cast<int64_t
>(entity.sync_metadata_only ? 1 : 0)},
185 {
"schedule_cron", entity.schedule_cron},
186 {
"last_sync", format_timestamp(entity.last_sync)},
187 {
"last_successful_sync",
188 format_timestamp(entity.last_successful_sync)},
189 {
"total_syncs",
static_cast<int64_t
>(entity.total_syncs)},
190 {
"studies_synced",
static_cast<int64_t
>(entity.studies_synced)}};
193auto sync_config_repository::get_pk(
const client::sync_config& entity)
const
195 return entity.config_id;
198auto sync_config_repository::has_pk(
const client::sync_config& entity)
const
200 return !entity.config_id.empty();
203auto sync_config_repository::select_columns() const
204 -> std::vector<std::
string> {
212 "patient_patterns_json",
215 "overwrite_existing",
216 "sync_metadata_only",
219 "last_successful_sync",
228auto sync_config_repository::parse_timestamp(
const std::string& str)
const
229 -> std::chrono::system_clock::time_point {
230 return from_timestamp_string(str);
233auto sync_config_repository::format_timestamp(
234 std::chrono::system_clock::time_point tp)
const -> std::string {
235 return to_timestamp_string(tp);
238std::string sync_config_repository::serialize_vector(
239 const std::vector<std::string>& vec) {
240 if (vec.empty())
return "[]";
242 std::ostringstream oss;
244 for (
size_t i = 0; i < vec.size(); ++i) {
245 if (i > 0) oss <<
",";
247 for (
char c : vec[i]) {
261std::vector<std::string> sync_config_repository::deserialize_vector(
262 std::string_view json) {
263 std::vector<std::string> result;
264 if (json.empty() || json ==
"[]")
return result;
267 while (pos < json.size()) {
268 auto start = json.find(
'"', pos);
269 if (start == std::string_view::npos)
break;
271 size_t end = start + 1;
272 while (end < json.size()) {
273 if (json[end] ==
'\\' && end + 1 < json.size()) {
275 }
else if (json[end] ==
'"') {
282 if (end < json.size()) {
283 std::string value{json.substr(start + 1, end - start - 1)};
284 std::string unescaped;
285 for (
size_t i = 0; i < value.size(); ++i) {
286 if (value[i] ==
'\\' && i + 1 < value.size()) {
287 unescaped += value[++i];
289 unescaped += value[i];
292 result.push_back(std::move(unescaped));
@ move
C-MOVE move request/response.
Repository for sync config records using base_repository pattern.