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);
38 auto since_epoch = tp.time_since_epoch();
39 auto secs = std::chrono::duration_cast<std::chrono::seconds>(since_epoch);
40 auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
44 std::strftime(buf,
sizeof(buf),
"%Y-%m-%d %H:%M:%S", &tm);
47 result,
sizeof(result),
"%s.%03d", buf,
static_cast<int>(ms.count()));
52[[nodiscard]] std::chrono::system_clock::time_point from_timestamp_string(
53 const std::string& str) {
59 if (std::sscanf(str.c_str(),
60 "%d-%d-%d %d:%d:%d.%d",
73 auto time = _mkgmtime(&tm);
75 auto time = timegm(&tm);
77 auto tp = std::chrono::system_clock::from_time_t(time);
78 return tp + std::chrono::milliseconds(ms);
87viewer_state_record_repository::viewer_state_record_repository(
88 std::shared_ptr<pacs_database_adapter> db)
89 : base_repository(std::
move(db),
"viewer_states",
"state_id") {}
95auto viewer_state_record_repository::find_by_study(std::string_view study_uid)
97 return find_where(
"study_uid",
"=", std::string(study_uid));
100auto viewer_state_record_repository::find_by_user(std::string_view user_id)
101 -> list_result_type {
102 return find_where(
"user_id",
"=", std::string(user_id));
105auto viewer_state_record_repository::find_by_study_and_user(
106 std::string_view study_uid,
107 std::string_view user_id) -> list_result_type {
108 if (!db() || !db()->is_connected()) {
109 return list_result_type(kcenon::common::error_info{
110 -1,
"Database not connected",
"viewer_state_record_repository"});
113 auto study_cond = database::query_condition(
114 "study_uid",
"=", std::string(study_uid));
115 auto user_cond = database::query_condition(
116 "user_id",
"=", std::string(user_id));
118 auto builder = storage_session().create_query_builder();
119 builder.select(select_columns())
121 .where(study_cond && user_cond)
122 .order_by(
"updated_at", database::sort_order::desc);
124 auto query = builder.build();
125 auto result = storage_session().select(query);
127 if (result.is_err()) {
128 return list_result_type(result.error());
131 std::vector<viewer_state_record> entities;
132 entities.reserve(result.value().size());
135 for (
const auto& row : result.value()) {
136 entities.push_back(map_row_to_entity(row));
138 return list_result_type(std::move(entities));
139 }
catch (
const std::exception& e) {
140 return list_result_type(kcenon::common::error_info{
142 std::string(
"Failed to map rows to entities: ") + e.what(),
143 "viewer_state_record_repository"});
147auto viewer_state_record_repository::search(
const viewer_state_query& query)
148 -> list_result_type {
149 if (!db() || !db()->is_connected()) {
150 return list_result_type(kcenon::common::error_info{
151 -1,
"Database not connected",
"viewer_state_record_repository"});
154 auto builder = storage_session().create_query_builder();
155 builder.select(select_columns()).from(table_name());
158 std::optional<database::query_condition> condition;
160 if (
query.study_uid.has_value()) {
161 condition = database::query_condition(
162 "study_uid",
"=",
query.study_uid.value());
165 if (
query.user_id.has_value()) {
166 auto user_cond = database::query_condition(
167 "user_id",
"=",
query.user_id.value());
168 if (condition.has_value()) {
169 condition = condition.value() && user_cond;
171 condition = user_cond;
175 if (condition.has_value()) {
176 builder.where(condition.value());
179 builder.order_by(
"updated_at", database::sort_order::desc);
181 if (
query.limit > 0) {
182 builder.limit(
query.limit);
183 if (
query.offset > 0) {
184 builder.offset(
query.offset);
188 auto sql = builder.build();
189 auto result = storage_session().select(sql);
191 if (result.is_err()) {
192 return list_result_type(result.error());
195 std::vector<viewer_state_record> entities;
196 entities.reserve(result.value().size());
199 for (
const auto& row : result.value()) {
200 entities.push_back(map_row_to_entity(row));
202 return list_result_type(std::move(entities));
203 }
catch (
const std::exception& e) {
204 return list_result_type(kcenon::common::error_info{
206 std::string(
"Failed to map rows to entities: ") + e.what(),
207 "viewer_state_record_repository"});
215auto viewer_state_record_repository::map_row_to_entity(
216 const database_row& row)
const -> viewer_state_record {
217 viewer_state_record
record;
219 record.pk = std::stoll(row.at(
"pk"));
220 record.state_id = row.at(
"state_id");
221 record.study_uid = row.at(
"study_uid");
222 record.user_id = row.at(
"user_id");
223 record.state_json = row.at(
"state_json");
224 record.created_at = parse_timestamp(row.at(
"created_at"));
225 record.updated_at = parse_timestamp(row.at(
"updated_at"));
230auto viewer_state_record_repository::entity_to_row(
231 const viewer_state_record& entity)
const
232 -> std::map<std::string, database_value> {
233 auto now_str = format_timestamp(std::chrono::system_clock::now());
235 return {{
"state_id", entity.state_id},
236 {
"study_uid", entity.study_uid},
237 {
"user_id", entity.user_id},
238 {
"state_json", entity.state_json},
239 {
"created_at", now_str},
240 {
"updated_at", now_str}};
243auto viewer_state_record_repository::get_pk(
244 const viewer_state_record& entity)
const -> std::string {
245 return entity.state_id;
248auto viewer_state_record_repository::has_pk(
249 const viewer_state_record& entity)
const ->
bool {
250 return !entity.state_id.empty();
253auto viewer_state_record_repository::select_columns() const
254 -> std::vector<std::
string> {
268auto viewer_state_record_repository::parse_timestamp(
const std::string& str)
269 const -> std::chrono::system_clock::time_point {
270 return from_timestamp_string(str);
273auto viewer_state_record_repository::format_timestamp(
274 std::chrono::system_clock::time_point tp)
const -> std::string {
275 return to_timestamp_string(tp);
@ move
C-MOVE move request/response.
const atna_coded_value query
Query (110112)
@ record
RECORD - Treatment record dose.
Repository for viewer state records using base_repository pattern.