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);
69prefetch_rule_repository::prefetch_rule_repository(
70 std::shared_ptr<pacs_database_adapter> db)
71 : base_repository(std::
move(db),
"prefetch_rules",
"rule_id") {}
73auto prefetch_rule_repository::find_by_rule_id(std::string_view rule_id)
75 return find_by_id(std::string(rule_id));
78auto prefetch_rule_repository::find_enabled() -> list_result_type {
79 return find_where(
"enabled",
"=",
static_cast<int64_t
>(1));
82auto prefetch_rule_repository::find_by_trigger(client::prefetch_trigger trigger)
85 "trigger_type",
"=", std::string(client::to_string(trigger)));
88auto prefetch_rule_repository::enable(std::string_view rule_id) -> VoidResult {
89 if (!db() || !db()->is_connected()) {
90 return VoidResult(kcenon::common::error_info{
91 -1,
"Database not connected",
"prefetch_rule_repository"});
94 std::ostringstream sql;
95 sql <<
"UPDATE prefetch_rules SET enabled = 1 WHERE rule_id = '"
98 auto result = storage_session().update(sql.str());
99 if (result.is_err()) {
100 return VoidResult(result.error());
103 return kcenon::common::ok();
106auto prefetch_rule_repository::disable(std::string_view rule_id) -> VoidResult {
107 if (!db() || !db()->is_connected()) {
108 return VoidResult(kcenon::common::error_info{
109 -1,
"Database not connected",
"prefetch_rule_repository"});
112 std::ostringstream sql;
113 sql <<
"UPDATE prefetch_rules SET enabled = 0 WHERE rule_id = '"
116 auto result = storage_session().update(sql.str());
117 if (result.is_err()) {
118 return VoidResult(result.error());
121 return kcenon::common::ok();
124auto prefetch_rule_repository::increment_triggered(std::string_view rule_id)
126 if (!db() || !db()->is_connected()) {
127 return VoidResult(kcenon::common::error_info{
128 -1,
"Database not connected",
"prefetch_rule_repository"});
131 std::ostringstream sql;
133 UPDATE prefetch_rules SET
134 triggered_count = triggered_count + 1,
135 last_triggered = datetime('now')
139 auto result = storage_session().update(sql.str());
140 if (result.is_err()) {
141 return VoidResult(result.error());
144 return kcenon::common::ok();
147auto prefetch_rule_repository::increment_studies_prefetched(
148 std::string_view rule_id,
149 size_t count) -> VoidResult {
150 if (!db() || !db()->is_connected()) {
151 return VoidResult(kcenon::common::error_info{
152 -1,
"Database not connected",
"prefetch_rule_repository"});
155 std::ostringstream sql;
156 sql <<
"UPDATE prefetch_rules SET studies_prefetched = studies_prefetched + "
157 << count <<
" WHERE rule_id = '" << rule_id <<
"'";
159 auto result = storage_session().update(sql.str());
160 if (result.is_err()) {
161 return VoidResult(result.error());
164 return kcenon::common::ok();
167auto prefetch_rule_repository::map_row_to_entity(
const database_row& row)
const
168 -> client::prefetch_rule {
169 client::prefetch_rule rule;
171 rule.pk = std::stoll(row.at(
"pk"));
172 rule.rule_id = row.at(
"rule_id");
173 rule.name = row.at(
"name");
174 rule.enabled = (row.at(
"enabled") ==
"1");
175 rule.trigger = client::prefetch_trigger_from_string(row.at(
"trigger_type"));
176 rule.modality_filter = row.at(
"modality_filter");
177 rule.body_part_filter = row.at(
"body_part_filter");
178 rule.station_ae_filter = row.at(
"station_ae_filter");
179 rule.prior_lookback =
180 std::chrono::hours(std::stoll(row.at(
"prior_lookback_hours")));
181 rule.max_prior_studies = std::stoull(row.at(
"max_prior_studies"));
182 rule.prior_modalities = deserialize_vector(row.at(
"prior_modalities_json"));
183 rule.source_node_ids = deserialize_vector(row.at(
"source_node_ids_json"));
184 rule.schedule_cron = row.at(
"schedule_cron");
186 std::chrono::minutes(std::stoll(row.at(
"advance_time_minutes")));
187 rule.triggered_count = std::stoull(row.at(
"triggered_count"));
188 rule.studies_prefetched = std::stoull(row.at(
"studies_prefetched"));
189 rule.last_triggered = parse_timestamp(row.at(
"last_triggered"));
194auto prefetch_rule_repository::entity_to_row(
195 const client::prefetch_rule& entity)
const
196 -> std::map<std::string, database_value> {
198 {
"rule_id", entity.rule_id},
199 {
"name", entity.name},
200 {
"enabled",
static_cast<int64_t
>(entity.enabled ? 1 : 0)},
201 {
"trigger_type", std::string(client::to_string(entity.trigger))},
202 {
"modality_filter", entity.modality_filter},
203 {
"body_part_filter", entity.body_part_filter},
204 {
"station_ae_filter", entity.station_ae_filter},
205 {
"prior_lookback_hours",
206 static_cast<int64_t
>(entity.prior_lookback.count())},
207 {
"max_prior_studies",
static_cast<int64_t
>(entity.max_prior_studies)},
208 {
"prior_modalities_json", serialize_vector(entity.prior_modalities)},
209 {
"source_node_ids_json", serialize_vector(entity.source_node_ids)},
210 {
"schedule_cron", entity.schedule_cron},
211 {
"advance_time_minutes",
212 static_cast<int64_t
>(entity.advance_time.count())},
213 {
"triggered_count",
static_cast<int64_t
>(entity.triggered_count)},
214 {
"studies_prefetched",
static_cast<int64_t
>(entity.studies_prefetched)},
215 {
"last_triggered", format_timestamp(entity.last_triggered)}};
218auto prefetch_rule_repository::get_pk(
const client::prefetch_rule& entity)
const
220 return entity.rule_id;
223auto prefetch_rule_repository::has_pk(
const client::prefetch_rule& entity)
const
225 return !entity.rule_id.empty();
228auto prefetch_rule_repository::select_columns() const
229 -> std::vector<std::
string> {
238 "prior_lookback_hours",
240 "prior_modalities_json",
241 "source_node_ids_json",
243 "advance_time_minutes",
245 "studies_prefetched",
249auto prefetch_rule_repository::parse_timestamp(
const std::string& str)
const
250 -> std::chrono::system_clock::time_point {
251 return from_timestamp_string(str);
254auto prefetch_rule_repository::format_timestamp(
255 std::chrono::system_clock::time_point tp)
const -> std::string {
256 return to_timestamp_string(tp);
259std::string prefetch_rule_repository::serialize_vector(
260 const std::vector<std::string>& vec) {
261 if (vec.empty())
return "[]";
263 std::ostringstream oss;
265 for (
size_t i = 0; i < vec.size(); ++i) {
266 if (i > 0) oss <<
",";
268 for (
char c : vec[i]) {
282std::vector<std::string> prefetch_rule_repository::deserialize_vector(
283 std::string_view json) {
284 std::vector<std::string> result;
285 if (json.empty() || json ==
"[]")
return result;
288 while (pos < json.size()) {
289 auto start = json.find(
'"', pos);
290 if (start == std::string_view::npos)
break;
292 size_t end = start + 1;
293 while (end < json.size()) {
294 if (json[end] ==
'\\' && end + 1 < json.size()) {
296 }
else if (json[end] ==
'"') {
303 if (end < json.size()) {
304 std::string value{json.substr(start + 1, end - start - 1)};
305 std::string unescaped;
306 for (
size_t i = 0; i < value.size(); ++i) {
307 if (value[i] ==
'\\' && i + 1 < value.size()) {
308 unescaped += value[++i];
310 unescaped += value[i];
313 result.push_back(std::move(unescaped));
@ move
C-MOVE move request/response.
Repository for prefetch rules using base_repository pattern.