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_history_repository::prefetch_history_repository(
70 std::shared_ptr<pacs_database_adapter> db)
71 : base_repository(std::
move(db),
"prefetch_history",
"pk") {}
73auto prefetch_history_repository::find_by_patient(
74 std::string_view patient_id,
75 size_t limit) -> list_result_type {
76 if (!db() || !db()->is_connected()) {
77 return list_result_type(kcenon::common::error_info{
78 -1,
"Database not connected",
"prefetch_history_repository"});
81 auto builder = storage_session().create_query_builder();
82 builder.select(select_columns())
84 .where(
"patient_id",
"=", std::string(patient_id))
85 .order_by(
"prefetched_at", database::sort_order::desc)
88 auto result = storage_session().select(builder.build());
89 if (result.is_err()) {
90 return list_result_type(result.error());
93 std::vector<client::prefetch_history> entities;
94 entities.reserve(result.value().size());
95 for (
const auto& row : result.value()) {
96 entities.push_back(map_row_to_entity(row));
99 return list_result_type(std::move(entities));
102auto prefetch_history_repository::find_by_study(std::string_view study_uid)
103 -> list_result_type {
104 return find_where(
"study_uid",
"=", std::string(study_uid));
107auto prefetch_history_repository::find_by_rule(
108 std::string_view rule_id,
109 size_t limit) -> list_result_type {
110 if (!db() || !db()->is_connected()) {
111 return list_result_type(kcenon::common::error_info{
112 -1,
"Database not connected",
"prefetch_history_repository"});
115 auto builder = storage_session().create_query_builder();
116 builder.select(select_columns())
118 .where(
"rule_id",
"=", std::string(rule_id))
119 .order_by(
"prefetched_at", database::sort_order::desc)
122 auto result = storage_session().select(builder.build());
123 if (result.is_err()) {
124 return list_result_type(result.error());
127 std::vector<client::prefetch_history> entities;
128 entities.reserve(result.value().size());
129 for (
const auto& row : result.value()) {
130 entities.push_back(map_row_to_entity(row));
133 return list_result_type(std::move(entities));
136auto prefetch_history_repository::find_by_status(
137 std::string_view status,
138 size_t limit) -> list_result_type {
139 if (!db() || !db()->is_connected()) {
140 return list_result_type(kcenon::common::error_info{
141 -1,
"Database not connected",
"prefetch_history_repository"});
144 auto builder = storage_session().create_query_builder();
145 builder.select(select_columns())
147 .where(
"status",
"=", std::string(status))
148 .order_by(
"prefetched_at", database::sort_order::desc)
151 auto result = storage_session().select(builder.build());
152 if (result.is_err()) {
153 return list_result_type(result.error());
156 std::vector<client::prefetch_history> entities;
157 entities.reserve(result.value().size());
158 for (
const auto& row : result.value()) {
159 entities.push_back(map_row_to_entity(row));
162 return list_result_type(std::move(entities));
165auto prefetch_history_repository::find_recent(
size_t limit) -> list_result_type {
166 if (!db() || !db()->is_connected()) {
167 return list_result_type(kcenon::common::error_info{
168 -1,
"Database not connected",
"prefetch_history_repository"});
171 auto builder = storage_session().create_query_builder();
172 builder.select(select_columns())
174 .order_by(
"prefetched_at", database::sort_order::desc)
177 auto result = storage_session().select(builder.build());
178 if (result.is_err()) {
179 return list_result_type(result.error());
182 std::vector<client::prefetch_history> entities;
183 entities.reserve(result.value().size());
184 for (
const auto& row : result.value()) {
185 entities.push_back(map_row_to_entity(row));
188 return list_result_type(std::move(entities));
191auto prefetch_history_repository::is_study_prefetched(
192 std::string_view study_uid) -> Result<bool> {
193 if (!db() || !db()->is_connected()) {
194 return Result<bool>(kcenon::common::error_info{
195 -1,
"Database not connected",
"prefetch_history_repository"});
198 std::ostringstream sql;
199 sql <<
"SELECT COUNT(*) as count FROM prefetch_history "
200 <<
"WHERE study_uid = '" << study_uid <<
"' "
201 <<
"AND status IN ('completed', 'pending')";
203 auto result = storage_session().select(sql.str());
204 if (result.is_err()) {
205 return Result<bool>(result.error());
208 if (result.value().empty()) {
209 return Result<bool>(
false);
212 return Result<bool>(std::stoull(result.value()[0].at(
"count")) > 0);
215auto prefetch_history_repository::count_by_status_on_current_date(
216 std::string_view status) -> Result<size_t> {
217 if (!db() || !db()->is_connected()) {
218 return Result<size_t>(kcenon::common::error_info{
219 -1,
"Database not connected",
"prefetch_history_repository"});
222 std::ostringstream sql;
223 sql <<
"SELECT COUNT(*) as count FROM prefetch_history "
224 <<
"WHERE status = '" <<
status <<
"' "
225 <<
"AND date(prefetched_at) = date('now')";
227 auto result = storage_session().select(sql.str());
228 if (result.is_err()) {
229 return Result<size_t>(result.error());
232 if (result.value().empty()) {
233 return Result<size_t>(
static_cast<size_t>(0));
236 return Result<size_t>(std::stoull(result.value()[0].at(
"count")));
239auto prefetch_history_repository::update_status(
241 std::string_view status) -> VoidResult {
242 if (!db() || !db()->is_connected()) {
243 return VoidResult(kcenon::common::error_info{
244 -1,
"Database not connected",
"prefetch_history_repository"});
247 std::ostringstream sql;
248 sql <<
"UPDATE prefetch_history SET status = '" <<
status
249 <<
"' WHERE pk = " << pk;
251 auto result = storage_session().update(sql.str());
252 if (result.is_err()) {
253 return VoidResult(result.error());
256 return kcenon::common::ok();
259auto prefetch_history_repository::cleanup_old(std::chrono::hours max_age)
261 if (!db() || !db()->is_connected()) {
262 return Result<size_t>(kcenon::common::error_info{
263 -1,
"Database not connected",
"prefetch_history_repository"});
266 std::ostringstream sql;
268 DELETE FROM prefetch_history
269 WHERE prefetched_at < datetime('now', '-)"
270 << max_age.count() << R"( hours'))";
272 auto result = storage_session().remove(sql.str());
273 if (result.is_err()) {
274 return Result<size_t>(result.error());
277 return Result<size_t>(
static_cast<size_t>(result.value()));
280auto prefetch_history_repository::map_row_to_entity(
281 const database_row& row)
const -> client::prefetch_history {
282 client::prefetch_history history;
284 history.pk = std::stoll(row.at(
"pk"));
285 history.patient_id = row.at(
"patient_id");
286 history.study_uid = row.at(
"study_uid");
287 history.rule_id = row.at(
"rule_id");
288 history.source_node_id = row.at(
"source_node_id");
289 history.job_id = row.at(
"job_id");
290 history.status = row.at(
"status");
291 history.prefetched_at = parse_timestamp(row.at(
"prefetched_at"));
296auto prefetch_history_repository::entity_to_row(
297 const client::prefetch_history& entity)
const
298 -> std::map<std::string, database_value> {
299 return {{
"patient_id", entity.patient_id},
300 {
"study_uid", entity.study_uid},
301 {
"rule_id", entity.rule_id},
302 {
"source_node_id", entity.source_node_id},
303 {
"job_id", entity.job_id},
304 {
"status", entity.status},
305 {
"prefetched_at", format_timestamp(entity.prefetched_at)}};
308auto prefetch_history_repository::get_pk(
309 const client::prefetch_history& entity)
const -> int64_t {
313auto prefetch_history_repository::has_pk(
314 const client::prefetch_history& entity)
const ->
bool {
315 return entity.pk > 0;
318auto prefetch_history_repository::select_columns() const
319 -> std::vector<std::
string> {
330auto prefetch_history_repository::parse_timestamp(
const std::string& str)
const
331 -> std::chrono::system_clock::time_point {
332 return from_timestamp_string(str);
335auto prefetch_history_repository::format_timestamp(
336 std::chrono::system_clock::time_point tp)
const -> std::string {
337 return to_timestamp_string(tp);
@ move
C-MOVE move request/response.
Repository for prefetch history records using base_repository pattern.