PACS System 0.1.0
PACS DICOM system library
Loading...
Searching...
No Matches
kcenon::pacs::storage::ups_repository Class Reference

#include <ups_repository.h>

Collaboration diagram for kcenon::pacs::storage::ups_repository:
Collaboration graph

Public Member Functions

 ups_repository (sqlite3 *db)
 
 ~ups_repository ()
 
 ups_repository (const ups_repository &)=delete
 
auto operator= (const ups_repository &) -> ups_repository &=delete
 
 ups_repository (ups_repository &&) noexcept
 
auto operator= (ups_repository &&) noexcept -> ups_repository &
 
auto create_ups_workitem (const ups_workitem &workitem) -> Result< int64_t >
 
auto update_ups_workitem (const ups_workitem &workitem) -> VoidResult
 
auto change_ups_state (std::string_view workitem_uid, std::string_view new_state, std::string_view transaction_uid="") -> VoidResult
 
auto find_ups_workitem (std::string_view workitem_uid) const -> std::optional< ups_workitem >
 
auto search_ups_workitems (const ups_workitem_query &query) const -> Result< std::vector< ups_workitem > >
 
auto delete_ups_workitem (std::string_view workitem_uid) -> VoidResult
 
auto ups_workitem_count () const -> Result< size_t >
 
auto ups_workitem_count (std::string_view state) const -> Result< size_t >
 
auto subscribe_ups (const ups_subscription &subscription) -> Result< int64_t >
 
auto unsubscribe_ups (std::string_view subscriber_ae, std::string_view workitem_uid="") -> VoidResult
 
auto get_ups_subscriptions (std::string_view subscriber_ae) const -> Result< std::vector< ups_subscription > >
 
auto get_ups_subscribers (std::string_view workitem_uid) const -> Result< std::vector< std::string > >
 

Private Member Functions

auto parse_ups_workitem_row (void *stmt) const -> ups_workitem
 

Static Private Member Functions

static auto to_like_pattern (std::string_view pattern) -> std::string
 
static auto parse_timestamp (const std::string &str) -> std::chrono::system_clock::time_point
 

Private Attributes

sqlite3 * db_ {nullptr}
 

Detailed Description

Definition at line 100 of file ups_repository.h.

Constructor & Destructor Documentation

◆ ups_repository() [1/3]

kcenon::pacs::storage::ups_repository::ups_repository ( sqlite3 * db)
explicit

Definition at line 793 of file ups_repository.cpp.

◆ ~ups_repository()

kcenon::pacs::storage::ups_repository::~ups_repository ( )
default

◆ ups_repository() [2/3]

kcenon::pacs::storage::ups_repository::ups_repository ( const ups_repository & )
delete

◆ ups_repository() [3/3]

kcenon::pacs::storage::ups_repository::ups_repository ( ups_repository && other)
noexcept

Definition at line 797 of file ups_repository.cpp.

797 : db_(other.db_) {
798 other.db_ = nullptr;
799}

Member Function Documentation

◆ change_ups_state()

auto kcenon::pacs::storage::ups_repository::change_ups_state ( std::string_view workitem_uid,
std::string_view new_state,
std::string_view transaction_uid = "" ) -> VoidResult
nodiscard

Definition at line 1049 of file ups_repository.cpp.

1052 {
1053 if (!parse_ups_state(new_state).has_value()) {
1054 return make_error<std::monostate>(
1055 -1, kcenon::pacs::compat::format("Invalid UPS state: {}", new_state),
1056 "storage");
1057 }
1058
1059 auto existing = find_ups_workitem(workitem_uid);
1060 if (!existing.has_value()) {
1061 return make_error<std::monostate>(
1062 -1,
1063 kcenon::pacs::compat::format("UPS workitem not found: {}", workitem_uid),
1064 "storage");
1065 }
1066
1067 auto current = existing->get_state();
1068 auto target = parse_ups_state(new_state);
1069
1070 if (existing->is_final()) {
1071 return make_error<std::monostate>(
1072 -1,
1073 kcenon::pacs::compat::format("Cannot change state from final state: {}",
1074 existing->state),
1075 "storage");
1076 }
1077
1078 if (current == ups_state::scheduled) {
1079 if (target != ups_state::in_progress &&
1080 target != ups_state::canceled) {
1081 return make_error<std::monostate>(
1082 -1,
1083 kcenon::pacs::compat::format(
1084 "Invalid transition from SCHEDULED to {}", new_state),
1085 "storage");
1086 }
1087 } else if (current == ups_state::in_progress) {
1088 if (target != ups_state::completed &&
1089 target != ups_state::canceled) {
1090 return make_error<std::monostate>(
1091 -1,
1092 kcenon::pacs::compat::format(
1093 "Invalid transition from IN PROGRESS to {}", new_state),
1094 "storage");
1095 }
1096 }
1097
1098 if (target == ups_state::in_progress && transaction_uid.empty()) {
1099 return make_error<std::monostate>(
1100 -1, "Transaction UID is required for IN PROGRESS transition",
1101 "storage");
1102 }
1103
1104 const char* sql = R"(
1105 UPDATE ups_workitems SET
1106 state = ?,
1107 transaction_uid = ?,
1108 updated_at = datetime('now')
1109 WHERE workitem_uid = ?;
1110 )";
1111
1112 sqlite3_stmt* stmt = nullptr;
1113 auto rc = sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr);
1114 if (rc != SQLITE_OK) {
1115 return make_error<std::monostate>(
1116 rc,
1117 kcenon::pacs::compat::format("Failed to prepare state change: {}",
1118 sqlite3_errmsg(db_)),
1119 "storage");
1120 }
1121
1122 sqlite3_bind_text(stmt, 1, new_state.data(), static_cast<int>(new_state.size()),
1123 SQLITE_TRANSIENT);
1124 sqlite3_bind_text(stmt, 2, transaction_uid.data(),
1125 static_cast<int>(transaction_uid.size()),
1126 SQLITE_TRANSIENT);
1127 sqlite3_bind_text(stmt, 3, workitem_uid.data(),
1128 static_cast<int>(workitem_uid.size()), SQLITE_TRANSIENT);
1129
1130 rc = sqlite3_step(stmt);
1131 sqlite3_finalize(stmt);
1132
1133 if (rc != SQLITE_DONE) {
1134 return make_error<std::monostate>(
1135 rc,
1136 kcenon::pacs::compat::format("Failed to change UPS state: {}",
1137 sqlite3_errmsg(db_)),
1138 "storage");
1139 }
1140
1141 return ok(std::monostate{});
1142}
auto find_ups_workitem(std::string_view workitem_uid) const -> std::optional< ups_workitem >
constexpr dicom_tag transaction_uid
Transaction UID — identifies a Storage Commitment transaction (PS3.4 J.3)
@ scheduled
Workitem is scheduled (initial state)
@ completed
Workitem completed successfully (final)
@ canceled
Workitem was canceled (final)
@ in_progress
Workitem is being performed.
auto parse_ups_state(std::string_view str) -> std::optional< ups_state >
Parse string to ups_state enum.

References kcenon::pacs::storage::canceled, kcenon::pacs::storage::completed, kcenon::pacs::storage::in_progress, kcenon::pacs::storage::parse_ups_state(), and kcenon::pacs::storage::scheduled.

Here is the call graph for this function:

◆ create_ups_workitem()

auto kcenon::pacs::storage::ups_repository::create_ups_workitem ( const ups_workitem & workitem) -> Result<int64_t>
nodiscard

Definition at line 865 of file ups_repository.cpp.

866 {
867 if (workitem.workitem_uid.empty()) {
868 return make_error<int64_t>(-1, "UPS workitem UID is required",
869 "storage");
870 }
871
872 const char* sql = R"(
873 INSERT INTO ups_workitems (
874 workitem_uid, state, procedure_step_label, worklist_label,
875 priority, scheduled_start_datetime, expected_completion_datetime,
876 scheduled_station_name, scheduled_station_class,
877 scheduled_station_geographic, scheduled_human_performers,
878 input_information, performing_ae, progress_description,
879 progress_percent, output_information, transaction_uid,
880 updated_at
881 ) VALUES (?, 'SCHEDULED', ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, datetime('now'))
882 RETURNING workitem_pk;
883 )";
884
885 sqlite3_stmt* stmt = nullptr;
886 auto rc = sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr);
887 if (rc != SQLITE_OK) {
888 return make_error<int64_t>(
889 rc,
890 kcenon::pacs::compat::format("Failed to prepare UPS insert: {}",
891 sqlite3_errmsg(db_)),
892 "storage");
893 }
894
895 sqlite3_bind_text(stmt, 1, workitem.workitem_uid.c_str(), -1,
896 SQLITE_TRANSIENT);
897 sqlite3_bind_text(stmt, 2, workitem.procedure_step_label.c_str(), -1,
898 SQLITE_TRANSIENT);
899 sqlite3_bind_text(stmt, 3, workitem.worklist_label.c_str(), -1,
900 SQLITE_TRANSIENT);
901 sqlite3_bind_text(
902 stmt, 4,
903 workitem.priority.empty() ? "MEDIUM" : workitem.priority.c_str(), -1,
904 SQLITE_TRANSIENT);
905 sqlite3_bind_text(stmt, 5, workitem.scheduled_start_datetime.c_str(), -1,
906 SQLITE_TRANSIENT);
907 sqlite3_bind_text(stmt, 6,
908 workitem.expected_completion_datetime.c_str(), -1,
909 SQLITE_TRANSIENT);
910 sqlite3_bind_text(stmt, 7, workitem.scheduled_station_name.c_str(), -1,
911 SQLITE_TRANSIENT);
912 sqlite3_bind_text(stmt, 8, workitem.scheduled_station_class.c_str(), -1,
913 SQLITE_TRANSIENT);
914 sqlite3_bind_text(stmt, 9,
915 workitem.scheduled_station_geographic.c_str(), -1,
916 SQLITE_TRANSIENT);
917 sqlite3_bind_text(stmt, 10,
918 workitem.scheduled_human_performers.c_str(), -1,
919 SQLITE_TRANSIENT);
920 sqlite3_bind_text(stmt, 11, workitem.input_information.c_str(), -1,
921 SQLITE_TRANSIENT);
922 sqlite3_bind_text(stmt, 12, workitem.performing_ae.c_str(), -1,
923 SQLITE_TRANSIENT);
924 sqlite3_bind_text(stmt, 13, workitem.progress_description.c_str(), -1,
925 SQLITE_TRANSIENT);
926 sqlite3_bind_int(stmt, 14, workitem.progress_percent);
927 sqlite3_bind_text(stmt, 15, workitem.output_information.c_str(), -1,
928 SQLITE_TRANSIENT);
929 sqlite3_bind_text(stmt, 16, workitem.transaction_uid.c_str(), -1,
930 SQLITE_TRANSIENT);
931
932 rc = sqlite3_step(stmt);
933 if (rc != SQLITE_ROW) {
934 auto error_msg = sqlite3_errmsg(db_);
935 sqlite3_finalize(stmt);
936 return make_error<int64_t>(
937 rc,
938 kcenon::pacs::compat::format("Failed to create UPS workitem: {}",
939 error_msg),
940 "storage");
941 }
942
943 auto pk = sqlite3_column_int64(stmt, 0);
944 sqlite3_finalize(stmt);
945 return pk;
946}

◆ delete_ups_workitem()

auto kcenon::pacs::storage::ups_repository::delete_ups_workitem ( std::string_view workitem_uid) -> VoidResult
nodiscard

Definition at line 1239 of file ups_repository.cpp.

1240 {
1241 const char* sql = "DELETE FROM ups_workitems WHERE workitem_uid = ?;";
1242
1243 sqlite3_stmt* stmt = nullptr;
1244 auto rc = sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr);
1245 if (rc != SQLITE_OK) {
1246 return make_error<std::monostate>(
1247 rc,
1248 kcenon::pacs::compat::format("Failed to prepare UPS delete: {}",
1249 sqlite3_errmsg(db_)),
1250 "storage");
1251 }
1252
1253 sqlite3_bind_text(stmt, 1, workitem_uid.data(),
1254 static_cast<int>(workitem_uid.size()), SQLITE_TRANSIENT);
1255 rc = sqlite3_step(stmt);
1256 sqlite3_finalize(stmt);
1257
1258 if (rc != SQLITE_DONE) {
1259 return make_error<std::monostate>(
1260 rc,
1261 kcenon::pacs::compat::format("Failed to delete UPS workitem: {}",
1262 sqlite3_errmsg(db_)),
1263 "storage");
1264 }
1265
1266 return ok(std::monostate{});
1267}

◆ find_ups_workitem()

auto kcenon::pacs::storage::ups_repository::find_ups_workitem ( std::string_view workitem_uid) const -> std::optional<ups_workitem>
nodiscard

Definition at line 1144 of file ups_repository.cpp.

1145 {
1146 const char* sql = "SELECT * FROM ups_workitems WHERE workitem_uid = ?;";
1147
1148 sqlite3_stmt* stmt = nullptr;
1149 auto rc = sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr);
1150 if (rc != SQLITE_OK) {
1151 return std::nullopt;
1152 }
1153
1154 sqlite3_bind_text(stmt, 1, workitem_uid.data(),
1155 static_cast<int>(workitem_uid.size()), SQLITE_TRANSIENT);
1156
1157 std::optional<ups_workitem> result;
1158 if (sqlite3_step(stmt) == SQLITE_ROW) {
1159 result = parse_ups_workitem_row(stmt);
1160 }
1161
1162 sqlite3_finalize(stmt);
1163 return result;
1164}
auto parse_ups_workitem_row(void *stmt) const -> ups_workitem

◆ get_ups_subscribers()

auto kcenon::pacs::storage::ups_repository::get_ups_subscribers ( std::string_view workitem_uid) const -> Result<std::vector<std::string>>
nodiscard

Definition at line 1439 of file ups_repository.cpp.

1440 {
1441 const char* sql = R"(
1442 SELECT DISTINCT subscriber_ae FROM ups_subscriptions
1443 WHERE workitem_uid = ? OR workitem_uid IS NULL;
1444 )";
1445
1446 sqlite3_stmt* stmt = nullptr;
1447 auto rc = sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr);
1448 if (rc != SQLITE_OK) {
1449 return make_error<std::vector<std::string>>(
1450 rc,
1451 kcenon::pacs::compat::format("Failed to prepare subscriber query: {}",
1452 sqlite3_errmsg(db_)),
1453 "storage");
1454 }
1455
1456 sqlite3_bind_text(stmt, 1, workitem_uid.data(),
1457 static_cast<int>(workitem_uid.size()), SQLITE_TRANSIENT);
1458
1459 std::vector<std::string> results;
1460 while (sqlite3_step(stmt) == SQLITE_ROW) {
1461 results.push_back(get_text(stmt, 0));
1462 }
1463
1464 sqlite3_finalize(stmt);
1465 return ok(std::move(results));
1466}

◆ get_ups_subscriptions()

auto kcenon::pacs::storage::ups_repository::get_ups_subscriptions ( std::string_view subscriber_ae) const -> Result<std::vector<ups_subscription>>
nodiscard

Definition at line 1405 of file ups_repository.cpp.

1406 {
1407 const char* sql =
1408 "SELECT * FROM ups_subscriptions WHERE subscriber_ae = ?;";
1409
1410 sqlite3_stmt* stmt = nullptr;
1411 auto rc = sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr);
1412 if (rc != SQLITE_OK) {
1413 return make_error<std::vector<ups_subscription>>(
1414 rc,
1415 kcenon::pacs::compat::format("Failed to prepare subscription query: {}",
1416 sqlite3_errmsg(db_)),
1417 "storage");
1418 }
1419
1420 sqlite3_bind_text(stmt, 1, subscriber_ae.data(),
1421 static_cast<int>(subscriber_ae.size()), SQLITE_TRANSIENT);
1422
1423 std::vector<ups_subscription> results;
1424 while (sqlite3_step(stmt) == SQLITE_ROW) {
1425 ups_subscription sub;
1426 sub.pk = sqlite3_column_int64(stmt, 0);
1427 sub.subscriber_ae = get_text(stmt, 1);
1428 sub.workitem_uid = get_text(stmt, 2);
1429 sub.deletion_lock = sqlite3_column_int(stmt, 3) != 0;
1430 sub.filter_criteria = get_text(stmt, 4);
1431 sub.created_at = parse_datetime(get_text(stmt, 5).c_str());
1432 results.push_back(std::move(sub));
1433 }
1434
1435 sqlite3_finalize(stmt);
1436 return ok(std::move(results));
1437}

References kcenon::pacs::storage::ups_subscription::created_at, kcenon::pacs::storage::ups_subscription::deletion_lock, kcenon::pacs::storage::ups_subscription::filter_criteria, kcenon::pacs::storage::ups_subscription::pk, kcenon::pacs::storage::ups_subscription::subscriber_ae, and kcenon::pacs::storage::ups_subscription::workitem_uid.

◆ operator=() [1/2]

auto kcenon::pacs::storage::ups_repository::operator= ( const ups_repository & ) -> ups_repository &=delete
delete

◆ operator=() [2/2]

auto kcenon::pacs::storage::ups_repository::operator= ( ups_repository && other) -> ups_repository&
noexcept

Definition at line 801 of file ups_repository.cpp.

802 {
803 if (this != &other) {
804 db_ = other.db_;
805 other.db_ = nullptr;
806 }
807 return *this;
808}

◆ parse_timestamp()

auto kcenon::pacs::storage::ups_repository::parse_timestamp ( const std::string & str) -> std::chrono::system_clock::time_point
staticnodiscardprivate

Definition at line 831 of file ups_repository.cpp.

832 {
833 return parse_datetime(str.c_str());
834}

◆ parse_ups_workitem_row()

auto kcenon::pacs::storage::ups_repository::parse_ups_workitem_row ( void * stmt) const -> ups_workitem
nodiscardprivate

Definition at line 836 of file ups_repository.cpp.

837 {
838 auto* stmt = static_cast<sqlite3_stmt*>(stmt_ptr);
839 ups_workitem item;
840
841 item.pk = sqlite3_column_int64(stmt, 0);
842 item.workitem_uid = get_text(stmt, 1);
843 item.state = get_text(stmt, 2);
844 item.procedure_step_label = get_text(stmt, 3);
845 item.worklist_label = get_text(stmt, 4);
846 item.priority = get_text(stmt, 5);
847 item.scheduled_start_datetime = get_text(stmt, 6);
848 item.expected_completion_datetime = get_text(stmt, 7);
849 item.scheduled_station_name = get_text(stmt, 8);
850 item.scheduled_station_class = get_text(stmt, 9);
851 item.scheduled_station_geographic = get_text(stmt, 10);
852 item.scheduled_human_performers = get_text(stmt, 11);
853 item.input_information = get_text(stmt, 12);
854 item.performing_ae = get_text(stmt, 13);
855 item.progress_description = get_text(stmt, 14);
856 item.progress_percent = sqlite3_column_int(stmt, 15);
857 item.output_information = get_text(stmt, 16);
858 item.transaction_uid = get_text(stmt, 17);
859 item.created_at = parse_datetime(get_text(stmt, 18).c_str());
860 item.updated_at = parse_datetime(get_text(stmt, 19).c_str());
861
862 return item;
863}
constexpr dicom_tag item
Item.

◆ search_ups_workitems()

auto kcenon::pacs::storage::ups_repository::search_ups_workitems ( const ups_workitem_query & query) const -> Result<std::vector<ups_workitem>>
nodiscard

Definition at line 1166 of file ups_repository.cpp.

1167 {
1168 std::string sql = "SELECT * FROM ups_workitems WHERE 1=1";
1169 std::vector<std::string> params;
1170
1171 if (query.workitem_uid.has_value()) {
1172 sql += " AND workitem_uid = ?";
1173 params.push_back(query.workitem_uid.value());
1174 }
1175 if (query.state.has_value()) {
1176 sql += " AND state = ?";
1177 params.push_back(query.state.value());
1178 }
1179 if (query.priority.has_value()) {
1180 sql += " AND priority = ?";
1181 params.push_back(query.priority.value());
1182 }
1183 if (query.procedure_step_label.has_value()) {
1184 sql += " AND procedure_step_label LIKE ?";
1185 params.push_back(to_like_pattern(*query.procedure_step_label));
1186 }
1187 if (query.worklist_label.has_value()) {
1188 sql += " AND worklist_label LIKE ?";
1189 params.push_back(to_like_pattern(*query.worklist_label));
1190 }
1191 if (query.performing_ae.has_value()) {
1192 sql += " AND performing_ae = ?";
1193 params.push_back(query.performing_ae.value());
1194 }
1195 if (query.scheduled_date_from.has_value()) {
1196 sql += " AND scheduled_start_datetime >= ?";
1197 params.push_back(query.scheduled_date_from.value());
1198 }
1199 if (query.scheduled_date_to.has_value()) {
1200 sql += " AND scheduled_start_datetime <= ?";
1201 params.push_back(query.scheduled_date_to.value() + "235959");
1202 }
1203
1204 sql += " ORDER BY scheduled_start_datetime ASC";
1205
1206 if (query.limit > 0) {
1207 sql += " LIMIT " + std::to_string(query.limit);
1208 }
1209 if (query.offset > 0) {
1210 sql += " OFFSET " + std::to_string(query.offset);
1211 }
1212
1213 sql += ";";
1214
1215 sqlite3_stmt* stmt = nullptr;
1216 auto rc = sqlite3_prepare_v2(db_, sql.c_str(), -1, &stmt, nullptr);
1217 if (rc != SQLITE_OK) {
1218 return make_error<std::vector<ups_workitem>>(
1219 rc,
1220 kcenon::pacs::compat::format("Failed to prepare UPS search: {}",
1221 sqlite3_errmsg(db_)),
1222 "storage");
1223 }
1224
1225 for (size_t i = 0; i < params.size(); ++i) {
1226 sqlite3_bind_text(stmt, static_cast<int>(i + 1), params[i].c_str(), -1,
1227 SQLITE_TRANSIENT);
1228 }
1229
1230 std::vector<ups_workitem> results;
1231 while (sqlite3_step(stmt) == SQLITE_ROW) {
1232 results.push_back(parse_ups_workitem_row(stmt));
1233 }
1234
1235 sqlite3_finalize(stmt);
1236 return ok(std::move(results));
1237}
static auto to_like_pattern(std::string_view pattern) -> std::string
const atna_coded_value query
Query (110112)

◆ subscribe_ups()

auto kcenon::pacs::storage::ups_repository::subscribe_ups ( const ups_subscription & subscription) -> Result<int64_t>
nodiscard

Definition at line 1313 of file ups_repository.cpp.

1314 {
1315 if (subscription.subscriber_ae.empty()) {
1316 return make_error<int64_t>(-1, "Subscriber AE Title is required",
1317 "storage");
1318 }
1319
1320 const char* sql = R"(
1321 INSERT OR REPLACE INTO ups_subscriptions (
1322 subscriber_ae, workitem_uid, deletion_lock, filter_criteria
1323 ) VALUES (?, ?, ?, ?)
1324 RETURNING subscription_pk;
1325 )";
1326
1327 sqlite3_stmt* stmt = nullptr;
1328 auto rc = sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr);
1329 if (rc != SQLITE_OK) {
1330 return make_error<int64_t>(
1331 rc,
1332 kcenon::pacs::compat::format("Failed to prepare subscription insert: {}",
1333 sqlite3_errmsg(db_)),
1334 "storage");
1335 }
1336
1337 sqlite3_bind_text(stmt, 1, subscription.subscriber_ae.c_str(), -1,
1338 SQLITE_TRANSIENT);
1339 if (subscription.workitem_uid.empty()) {
1340 sqlite3_bind_null(stmt, 2);
1341 } else {
1342 sqlite3_bind_text(stmt, 2, subscription.workitem_uid.c_str(), -1,
1343 SQLITE_TRANSIENT);
1344 }
1345 sqlite3_bind_int(stmt, 3, subscription.deletion_lock ? 1 : 0);
1346 sqlite3_bind_text(stmt, 4, subscription.filter_criteria.c_str(), -1,
1347 SQLITE_TRANSIENT);
1348
1349 rc = sqlite3_step(stmt);
1350 if (rc != SQLITE_ROW) {
1351 auto error_msg = sqlite3_errmsg(db_);
1352 sqlite3_finalize(stmt);
1353 return make_error<int64_t>(
1354 rc, kcenon::pacs::compat::format("Failed to create subscription: {}", error_msg),
1355 "storage");
1356 }
1357
1358 auto pk = sqlite3_column_int64(stmt, 0);
1359 sqlite3_finalize(stmt);
1360 return pk;
1361}

◆ to_like_pattern()

auto kcenon::pacs::storage::ups_repository::to_like_pattern ( std::string_view pattern) -> std::string
staticnodiscardprivate

Definition at line 810 of file ups_repository.cpp.

811 {
812 std::string result;
813 result.reserve(pattern.size());
814
815 for (char c : pattern) {
816 if (c == '*') {
817 result += '%';
818 } else if (c == '?') {
819 result += '_';
820 } else if (c == '%' || c == '_') {
821 result += '\\';
822 result += c;
823 } else {
824 result += c;
825 }
826 }
827
828 return result;
829}

◆ unsubscribe_ups()

auto kcenon::pacs::storage::ups_repository::unsubscribe_ups ( std::string_view subscriber_ae,
std::string_view workitem_uid = "" ) -> VoidResult
nodiscard

Definition at line 1363 of file ups_repository.cpp.

1365 {
1366 std::string sql;
1367 if (workitem_uid.empty()) {
1368 sql = "DELETE FROM ups_subscriptions WHERE subscriber_ae = ?;";
1369 } else {
1370 sql = "DELETE FROM ups_subscriptions WHERE subscriber_ae = ? AND workitem_uid = ?;";
1371 }
1372
1373 sqlite3_stmt* stmt = nullptr;
1374 auto rc = sqlite3_prepare_v2(db_, sql.c_str(), -1, &stmt, nullptr);
1375 if (rc != SQLITE_OK) {
1376 return make_error<std::monostate>(
1377 rc,
1378 kcenon::pacs::compat::format("Failed to prepare unsubscribe: {}",
1379 sqlite3_errmsg(db_)),
1380 "storage");
1381 }
1382
1383 sqlite3_bind_text(stmt, 1, subscriber_ae.data(),
1384 static_cast<int>(subscriber_ae.size()), SQLITE_TRANSIENT);
1385 if (!workitem_uid.empty()) {
1386 sqlite3_bind_text(stmt, 2, workitem_uid.data(),
1387 static_cast<int>(workitem_uid.size()),
1388 SQLITE_TRANSIENT);
1389 }
1390
1391 rc = sqlite3_step(stmt);
1392 sqlite3_finalize(stmt);
1393
1394 if (rc != SQLITE_DONE) {
1395 return make_error<std::monostate>(
1396 rc,
1397 kcenon::pacs::compat::format("Failed to unsubscribe: {}",
1398 sqlite3_errmsg(db_)),
1399 "storage");
1400 }
1401
1402 return ok(std::monostate{});
1403}

◆ update_ups_workitem()

auto kcenon::pacs::storage::ups_repository::update_ups_workitem ( const ups_workitem & workitem) -> VoidResult
nodiscard

Definition at line 948 of file ups_repository.cpp.

949 {
950 if (workitem.workitem_uid.empty()) {
951 return make_error<std::monostate>(
952 -1, "UPS workitem UID is required for update", "storage");
953 }
954
955 auto existing = find_ups_workitem(workitem.workitem_uid);
956 if (!existing.has_value()) {
957 return make_error<std::monostate>(
958 -1,
959 kcenon::pacs::compat::format("UPS workitem not found: {}",
960 workitem.workitem_uid),
961 "storage");
962 }
963
964 if (existing->is_final()) {
965 return make_error<std::monostate>(
966 -1,
967 kcenon::pacs::compat::format("Cannot update workitem in final state: {}",
968 existing->state),
969 "storage");
970 }
971
972 const char* sql = R"(
973 UPDATE ups_workitems SET
974 procedure_step_label = ?,
975 worklist_label = ?,
976 priority = ?,
977 scheduled_start_datetime = ?,
978 expected_completion_datetime = ?,
979 scheduled_station_name = ?,
980 scheduled_station_class = ?,
981 scheduled_station_geographic = ?,
982 scheduled_human_performers = ?,
983 input_information = ?,
984 performing_ae = ?,
985 progress_description = ?,
986 progress_percent = ?,
987 output_information = ?,
988 updated_at = datetime('now')
989 WHERE workitem_uid = ?;
990 )";
991
992 sqlite3_stmt* stmt = nullptr;
993 auto rc = sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr);
994 if (rc != SQLITE_OK) {
995 return make_error<std::monostate>(
996 rc,
997 kcenon::pacs::compat::format("Failed to prepare UPS update: {}",
998 sqlite3_errmsg(db_)),
999 "storage");
1000 }
1001
1002 sqlite3_bind_text(stmt, 1, workitem.procedure_step_label.c_str(), -1,
1003 SQLITE_TRANSIENT);
1004 sqlite3_bind_text(stmt, 2, workitem.worklist_label.c_str(), -1,
1005 SQLITE_TRANSIENT);
1006 sqlite3_bind_text(stmt, 3, workitem.priority.c_str(), -1,
1007 SQLITE_TRANSIENT);
1008 sqlite3_bind_text(stmt, 4, workitem.scheduled_start_datetime.c_str(), -1,
1009 SQLITE_TRANSIENT);
1010 sqlite3_bind_text(stmt, 5,
1011 workitem.expected_completion_datetime.c_str(), -1,
1012 SQLITE_TRANSIENT);
1013 sqlite3_bind_text(stmt, 6, workitem.scheduled_station_name.c_str(), -1,
1014 SQLITE_TRANSIENT);
1015 sqlite3_bind_text(stmt, 7, workitem.scheduled_station_class.c_str(), -1,
1016 SQLITE_TRANSIENT);
1017 sqlite3_bind_text(stmt, 8,
1018 workitem.scheduled_station_geographic.c_str(), -1,
1019 SQLITE_TRANSIENT);
1020 sqlite3_bind_text(stmt, 9,
1021 workitem.scheduled_human_performers.c_str(), -1,
1022 SQLITE_TRANSIENT);
1023 sqlite3_bind_text(stmt, 10, workitem.input_information.c_str(), -1,
1024 SQLITE_TRANSIENT);
1025 sqlite3_bind_text(stmt, 11, workitem.performing_ae.c_str(), -1,
1026 SQLITE_TRANSIENT);
1027 sqlite3_bind_text(stmt, 12, workitem.progress_description.c_str(), -1,
1028 SQLITE_TRANSIENT);
1029 sqlite3_bind_int(stmt, 13, workitem.progress_percent);
1030 sqlite3_bind_text(stmt, 14, workitem.output_information.c_str(), -1,
1031 SQLITE_TRANSIENT);
1032 sqlite3_bind_text(stmt, 15, workitem.workitem_uid.c_str(), -1,
1033 SQLITE_TRANSIENT);
1034
1035 rc = sqlite3_step(stmt);
1036 sqlite3_finalize(stmt);
1037
1038 if (rc != SQLITE_DONE) {
1039 return make_error<std::monostate>(
1040 rc,
1041 kcenon::pacs::compat::format("Failed to update UPS workitem: {}",
1042 sqlite3_errmsg(db_)),
1043 "storage");
1044 }
1045
1046 return ok(std::monostate{});
1047}

◆ ups_workitem_count() [1/2]

auto kcenon::pacs::storage::ups_repository::ups_workitem_count ( ) const -> Result<size_t>
nodiscard

Definition at line 1269 of file ups_repository.cpp.

1269 {
1270 const char* sql = "SELECT COUNT(*) FROM ups_workitems;";
1271 sqlite3_stmt* stmt = nullptr;
1272 auto rc = sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr);
1273 if (rc != SQLITE_OK) {
1274 return make_error<size_t>(
1275 rc,
1276 kcenon::pacs::compat::format("Failed to prepare query: {}",
1277 sqlite3_errmsg(db_)),
1278 "storage");
1279 }
1280
1281 size_t count = 0;
1282 if (sqlite3_step(stmt) == SQLITE_ROW) {
1283 count = static_cast<size_t>(sqlite3_column_int64(stmt, 0));
1284 }
1285 sqlite3_finalize(stmt);
1286 return ok(count);
1287}

References db_.

◆ ups_workitem_count() [2/2]

auto kcenon::pacs::storage::ups_repository::ups_workitem_count ( std::string_view state) const -> Result<size_t>
nodiscard

Definition at line 1289 of file ups_repository.cpp.

1290 {
1291 const char* sql = "SELECT COUNT(*) FROM ups_workitems WHERE state = ?;";
1292 sqlite3_stmt* stmt = nullptr;
1293 auto rc = sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr);
1294 if (rc != SQLITE_OK) {
1295 return make_error<size_t>(
1296 rc,
1297 kcenon::pacs::compat::format("Failed to prepare query: {}",
1298 sqlite3_errmsg(db_)),
1299 "storage");
1300 }
1301
1302 sqlite3_bind_text(stmt, 1, state.data(), static_cast<int>(state.size()),
1303 SQLITE_TRANSIENT);
1304
1305 size_t count = 0;
1306 if (sqlite3_step(stmt) == SQLITE_ROW) {
1307 count = static_cast<size_t>(sqlite3_column_int64(stmt, 0));
1308 }
1309 sqlite3_finalize(stmt);
1310 return ok(count);
1311}

Member Data Documentation

◆ db_

sqlite3* kcenon::pacs::storage::ups_repository::db_ {nullptr}
private

Definition at line 144 of file ups_repository.h.

144{nullptr};

Referenced by ups_workitem_count().


The documentation for this class was generated from the following files: