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);
87recent_study_repository::recent_study_repository(
88 std::shared_ptr<pacs_database_adapter> db)
89 : base_repository(std::
move(db),
"recent_studies",
"pk") {}
95auto recent_study_repository::record_access(
96 std::string_view user_id,
97 std::string_view study_uid) -> VoidResult {
98 if (!db() || !db()->is_connected()) {
99 return VoidResult(kcenon::common::error_info{
100 -1,
"Database not connected",
"recent_study_repository"});
103 auto now_str = format_timestamp(std::chrono::system_clock::now());
106 std::ostringstream sql;
108 INSERT INTO recent_studies (user_id, study_uid, accessed_at)
110 << user_id << "', '" << study_uid <<
"', '" << now_str
112 ON CONFLICT(user_id, study_uid) DO UPDATE SET
113 accessed_at = excluded.accessed_at
116 auto result = storage_session().insert(sql.str());
117 if (result.is_err()) {
118 return VoidResult(result.error());
121 return kcenon::common::ok();
124auto recent_study_repository::find_by_user(
125 std::string_view user_id,
126 size_t limit) -> list_result_type {
127 if (!db() || !db()->is_connected()) {
128 return list_result_type(kcenon::common::error_info{
129 -1,
"Database not connected",
"recent_study_repository"});
132 auto builder = storage_session().create_query_builder();
133 builder.select(select_columns())
135 .where(
"user_id",
"=", std::string(user_id))
136 .order_by(
"accessed_at", database::sort_order::desc)
137 .order_by(
"pk", database::sort_order::desc)
140 auto query = builder.build();
141 auto result = storage_session().select(query);
143 if (result.is_err()) {
144 return list_result_type(result.error());
147 std::vector<recent_study_record> entities;
148 entities.reserve(result.value().size());
151 for (
const auto& row : result.value()) {
152 entities.push_back(map_row_to_entity(row));
154 return list_result_type(std::move(entities));
155 }
catch (
const std::exception& e) {
156 return list_result_type(kcenon::common::error_info{
158 std::string(
"Failed to map rows to entities: ") + e.what(),
159 "recent_study_repository"});
163auto recent_study_repository::clear_for_user(std::string_view user_id)
165 auto result = remove_where(
"user_id",
"=", std::string(user_id));
166 if (result.is_err()) {
167 return VoidResult(result.error());
169 return kcenon::common::ok();
172auto recent_study_repository::count_for_user(std::string_view user_id)
174 if (!db() || !db()->is_connected()) {
175 return Result<size_t>(kcenon::common::error_info{
176 -1,
"Database not connected",
"recent_study_repository"});
179 auto builder = storage_session().create_query_builder();
180 builder.select({
"pk"})
182 .where(
"user_id",
"=", std::string(user_id));
184 auto query = builder.build();
185 auto result = storage_session().select(query);
187 if (result.is_err()) {
188 return Result<size_t>(result.error());
191 return Result<size_t>(result.value().size());
194auto recent_study_repository::was_recently_accessed(
195 std::string_view user_id,
196 std::string_view study_uid) -> Result<bool> {
197 if (!db() || !db()->is_connected()) {
198 return Result<bool>(kcenon::common::error_info{
199 -1,
"Database not connected",
"recent_study_repository"});
202 auto user_cond = database::query_condition(
203 "user_id",
"=", std::string(user_id));
204 auto study_cond = database::query_condition(
205 "study_uid",
"=", std::string(study_uid));
207 auto builder = storage_session().create_query_builder();
208 builder.select({
"pk"})
210 .where(user_cond && study_cond);
212 auto query = builder.build();
213 auto result = storage_session().select(query);
215 if (result.is_err()) {
216 return Result<bool>(result.error());
219 return Result<bool>(!result.value().empty());
226auto recent_study_repository::map_row_to_entity(
const database_row& row)
const
227 -> recent_study_record {
228 recent_study_record
record;
230 record.pk = std::stoll(row.at(
"pk"));
231 record.user_id = row.at(
"user_id");
232 record.study_uid = row.at(
"study_uid");
233 record.accessed_at = parse_timestamp(row.at(
"accessed_at"));
238auto recent_study_repository::entity_to_row(
239 const recent_study_record& entity)
const
240 -> std::map<std::string, database_value> {
241 return {{
"user_id", entity.user_id},
242 {
"study_uid", entity.study_uid},
243 {
"accessed_at", format_timestamp(entity.accessed_at)}};
246auto recent_study_repository::get_pk(
const recent_study_record& entity)
const
251auto recent_study_repository::has_pk(
const recent_study_record& entity)
const
253 return entity.pk > 0;
256auto recent_study_repository::select_columns() const
257 -> std::vector<std::
string> {
258 return {
"pk",
"user_id",
"study_uid",
"accessed_at"};
265auto recent_study_repository::parse_timestamp(
const std::string& str)
const
266 -> std::chrono::system_clock::time_point {
267 return from_timestamp_string(str);
270auto recent_study_repository::format_timestamp(
271 std::chrono::system_clock::time_point tp)
const -> std::string {
272 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 recent study records using base_repository pattern.