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

#include <aira_assessment.h>

Collaboration diagram for kcenon::pacs::ai::assessment_creator:
Collaboration graph

Public Member Functions

 assessment_creator ()=default
 
assessment_creation_result create_assessment (const ai_assessment &assessment) const
 Create an assessment SR document for an AI result.
 

Static Public Member Functions

static std::string assessment_type_to_code (assessment_type type)
 Convert assessment_type to DICOM coded value.
 
static std::string assessment_type_to_meaning (assessment_type type)
 Convert assessment_type to human-readable meaning.
 
static std::string status_to_completion_flag (assessment_status status)
 Convert assessment_status to DICOM completion flag value.
 

Private Member Functions

void build_patient_module (core::dicom_dataset &sr, const ai_assessment &assessment) const
 
void build_sr_content (core::dicom_dataset &sr, const ai_assessment &assessment) const
 
void build_referenced_sop_sequence (core::dicom_dataset &sr, const ai_assessment &assessment) const
 
std::string generate_uid () const
 

Detailed Description

Definition at line 210 of file aira_assessment.h.

Constructor & Destructor Documentation

◆ assessment_creator()

kcenon::pacs::ai::assessment_creator::assessment_creator ( )
default

Member Function Documentation

◆ assessment_type_to_code()

std::string kcenon::pacs::ai::assessment_creator::assessment_type_to_code ( assessment_type type)
staticnodiscard

Convert assessment_type to DICOM coded value.

Parameters
typeAssessment type
Returns
Coded string representation
Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/ai/aira_assessment.h.

Definition at line 330 of file aira_assessment.cpp.

330 {
331 switch (type) {
332 case assessment_type::accept: return "AIRA-ACCEPT";
333 case assessment_type::modify: return "AIRA-MODIFY";
334 case assessment_type::reject: return "AIRA-REJECT";
335 }
336 return "AIRA-ACCEPT";
337}
@ accept
Clinician accepts AI result as-is.
@ reject
Clinician rejects AI result with reason.
@ modify
Clinician modifies AI result (e.g., edits segmentation)

References kcenon::pacs::ai::accept, kcenon::pacs::ai::modify, and kcenon::pacs::ai::reject.

Referenced by build_sr_content().

Here is the caller graph for this function:

◆ assessment_type_to_meaning()

std::string kcenon::pacs::ai::assessment_creator::assessment_type_to_meaning ( assessment_type type)
staticnodiscard

Convert assessment_type to human-readable meaning.

Parameters
typeAssessment type
Returns
Display string
Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/ai/aira_assessment.h.

Definition at line 339 of file aira_assessment.cpp.

339 {
340 switch (type) {
341 case assessment_type::accept: return "AI Result Accepted";
342 case assessment_type::modify: return "AI Result Modified";
343 case assessment_type::reject: return "AI Result Rejected";
344 }
345 return "AI Result Accepted";
346}

References kcenon::pacs::ai::accept, kcenon::pacs::ai::modify, and kcenon::pacs::ai::reject.

Referenced by build_sr_content().

Here is the caller graph for this function:

◆ build_patient_module()

void kcenon::pacs::ai::assessment_creator::build_patient_module ( core::dicom_dataset & sr,
const ai_assessment & assessment ) const
private
Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/ai/aira_assessment.h.

Definition at line 162 of file aira_assessment.cpp.

164 {
165
166 sr.set_string(tags::patient_name, vr_type::PN, "");
167 sr.set_string(tags::patient_id, vr_type::LO, "");
168 sr.set_string(tags::patient_birth_date, vr_type::DA, "");
169 sr.set_string(tags::patient_sex, vr_type::CS, "");
170}
constexpr dicom_tag patient_id
Patient ID.
constexpr dicom_tag patient_birth_date
Patient's Birth Date.
constexpr dicom_tag patient_sex
Patient's Sex.
constexpr dicom_tag patient_name
Patient's Name.

References kcenon::pacs::core::tags::patient_birth_date, kcenon::pacs::core::tags::patient_id, kcenon::pacs::core::tags::patient_name, and kcenon::pacs::core::tags::patient_sex.

Referenced by create_assessment().

Here is the caller graph for this function:

◆ build_referenced_sop_sequence()

void kcenon::pacs::ai::assessment_creator::build_referenced_sop_sequence ( core::dicom_dataset & sr,
const ai_assessment & assessment ) const
private
Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/ai/aira_assessment.h.

Definition at line 282 of file aira_assessment.cpp.

284 {
285
286 std::vector<dicom_dataset> ref_items;
287
288 // Reference to the assessed AI result
289 dicom_dataset ref_item;
290 ref_item.set_string(aira_tags::referenced_sop_class_uid, vr_type::UI,
291 assessment.ai_result.sop_class_uid);
292 ref_item.set_string(aira_tags::referenced_sop_instance_uid, vr_type::UI,
293 assessment.ai_result.sop_instance_uid);
294 ref_items.push_back(std::move(ref_item));
295
296 // Reference to modified result (if applicable)
297 if (assessment.type == assessment_type::modify &&
298 assessment.modification &&
299 assessment.modification->modified_result_uid) {
300 dicom_dataset mod_ref;
301 mod_ref.set_string(aira_tags::referenced_sop_class_uid, vr_type::UI,
302 assessment.modification->modified_result_class_uid
303 .value_or(assessment.ai_result.sop_class_uid));
304 mod_ref.set_string(aira_tags::referenced_sop_instance_uid, vr_type::UI,
305 assessment.modification->modified_result_uid.value());
306 ref_items.push_back(std::move(mod_ref));
307 }
308
309 insert_sequence(sr, aira_tags::referenced_sop_sequence,
310 std::move(ref_items));
311}
constexpr dicom_tag referenced_sop_class_uid
constexpr dicom_tag referenced_sop_sequence
constexpr dicom_tag referenced_sop_instance_uid

References kcenon::pacs::ai::ai_assessment::ai_result, kcenon::pacs::ai::ai_assessment::modification, kcenon::pacs::ai::modify, kcenon::pacs::ai::aira_tags::referenced_sop_class_uid, kcenon::pacs::ai::aira_tags::referenced_sop_instance_uid, kcenon::pacs::ai::aira_tags::referenced_sop_sequence, kcenon::pacs::core::dicom_dataset::set_string(), kcenon::pacs::ai::assessed_result_reference::sop_class_uid, kcenon::pacs::ai::assessed_result_reference::sop_instance_uid, and kcenon::pacs::ai::ai_assessment::type.

Referenced by create_assessment().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ build_sr_content()

void kcenon::pacs::ai::assessment_creator::build_sr_content ( core::dicom_dataset & sr,
const ai_assessment & assessment ) const
private
Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/ai/aira_assessment.h.

Definition at line 172 of file aira_assessment.cpp.

174 {
175
176 // Root content: CONTAINER
177 sr.set_string(aira_tags::value_type, vr_type::CS, "CONTAINER");
178
179 // Concept Name: AI Result Assessment (custom code)
180 auto concept_name = make_code_item(
181 "AIRA-001", "99PACS", "AI Result Assessment");
182 insert_sequence(sr, aira_tags::concept_name_code_sequence, {concept_name});
183
184 // Content Template Sequence (IHE AIRA assessment template)
185 dicom_dataset template_item;
186 template_item.set_string(aira_tags::template_identifier, vr_type::CS,
187 "AIRA");
188 template_item.set_string(aira_tags::mapping_resource, vr_type::CS, "99PACS");
189 insert_sequence(sr, aira_tags::content_template_sequence, {template_item});
190
191 // Content Sequence items
192 std::vector<dicom_dataset> content_items;
193
194 // Item 1: Assessment Type (CODE)
195 {
196 dicom_dataset item;
197 item.set_string(aira_tags::relationship_type, vr_type::CS, "CONTAINS");
198 item.set_string(aira_tags::value_type, vr_type::CS, "CODE");
199
200 auto name = make_code_item("AIRA-010", "99PACS", "Assessment Decision");
201 insert_sequence(item, aira_tags::concept_name_code_sequence, {name});
202
203 auto value = make_code_item(
204 assessment_type_to_code(assessment.type),
205 "99PACS",
206 assessment_type_to_meaning(assessment.type));
207 // Concept Code Sequence (0040,A168) for CODE value type
208 constexpr dicom_tag concept_code_sequence{0x0040, 0xA168};
209 insert_sequence(item, concept_code_sequence, {value});
210
211 content_items.push_back(std::move(item));
212 }
213
214 // Item 2: Assessor (PNAME)
215 {
216 dicom_dataset item;
217 item.set_string(aira_tags::relationship_type, vr_type::CS, "HAS OBS CONTEXT");
218 item.set_string(aira_tags::value_type, vr_type::CS, "PNAME");
219
220 auto name = make_code_item("AIRA-020", "99PACS", "Assessor");
221 insert_sequence(item, aira_tags::concept_name_code_sequence, {name});
222
223 constexpr dicom_tag person_name{0x0040, 0xA123};
224 item.set_string(person_name, vr_type::PN, assessment.assessor_name);
225
226 content_items.push_back(std::move(item));
227 }
228
229 // Item 3: Comment (TEXT, optional)
230 if (assessment.comment && !assessment.comment->empty()) {
231 dicom_dataset item;
232 item.set_string(aira_tags::relationship_type, vr_type::CS, "CONTAINS");
233 item.set_string(aira_tags::value_type, vr_type::CS, "TEXT");
234
235 auto name = make_code_item("AIRA-030", "99PACS", "Assessment Comment");
236 insert_sequence(item, aira_tags::concept_name_code_sequence, {name});
237
238 item.set_string(aira_tags::text_value, vr_type::UT,
239 assessment.comment.value());
240
241 content_items.push_back(std::move(item));
242 }
243
244 // Item 4: Modification description (TEXT, only for modify type)
245 if (assessment.type == assessment_type::modify && assessment.modification) {
246 dicom_dataset item;
247 item.set_string(aira_tags::relationship_type, vr_type::CS, "CONTAINS");
248 item.set_string(aira_tags::value_type, vr_type::CS, "TEXT");
249
250 auto name = make_code_item("AIRA-040", "99PACS",
251 "Modification Description");
252 insert_sequence(item, aira_tags::concept_name_code_sequence, {name});
253
254 item.set_string(aira_tags::text_value, vr_type::UT,
255 assessment.modification->description);
256
257 content_items.push_back(std::move(item));
258 }
259
260 // Item 5: Rejection reason (CODE, only for reject type)
261 if (assessment.type == assessment_type::reject && assessment.rejection) {
262 dicom_dataset item;
263 item.set_string(aira_tags::relationship_type, vr_type::CS, "CONTAINS");
264 item.set_string(aira_tags::value_type, vr_type::CS, "CODE");
265
266 auto name = make_code_item("AIRA-050", "99PACS", "Rejection Reason");
267 insert_sequence(item, aira_tags::concept_name_code_sequence, {name});
268
269 auto value = make_code_item(
270 assessment.rejection->reason_code,
271 assessment.rejection->reason_scheme,
272 assessment.rejection->reason_description);
273 constexpr dicom_tag concept_code_sequence{0x0040, 0xA168};
274 insert_sequence(item, concept_code_sequence, {value});
275
276 content_items.push_back(std::move(item));
277 }
278
279 insert_sequence(sr, aira_tags::content_sequence, std::move(content_items));
280}
static std::string assessment_type_to_meaning(assessment_type type)
Convert assessment_type to human-readable meaning.
static std::string assessment_type_to_code(assessment_type type)
Convert assessment_type to DICOM coded value.
constexpr dicom_tag template_identifier
constexpr dicom_tag content_sequence
constexpr dicom_tag concept_name_code_sequence
constexpr dicom_tag mapping_resource
constexpr dicom_tag value_type
constexpr dicom_tag content_template_sequence
constexpr dicom_tag text_value
constexpr dicom_tag relationship_type
constexpr dicom_tag item
Item.
std::string_view name

References assessment_type_to_code(), assessment_type_to_meaning(), kcenon::pacs::ai::ai_assessment::assessor_name, kcenon::pacs::ai::ai_assessment::comment, kcenon::pacs::ai::aira_tags::concept_name_code_sequence, kcenon::pacs::ai::aira_tags::content_sequence, kcenon::pacs::ai::aira_tags::content_template_sequence, kcenon::pacs::ai::aira_tags::mapping_resource, kcenon::pacs::ai::ai_assessment::modification, kcenon::pacs::ai::modify, name, kcenon::pacs::ai::reject, kcenon::pacs::ai::ai_assessment::rejection, kcenon::pacs::ai::aira_tags::relationship_type, kcenon::pacs::core::dicom_dataset::set_string(), kcenon::pacs::ai::aira_tags::template_identifier, kcenon::pacs::ai::aira_tags::text_value, kcenon::pacs::ai::ai_assessment::type, and kcenon::pacs::ai::aira_tags::value_type.

Referenced by create_assessment().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ create_assessment()

assessment_creation_result kcenon::pacs::ai::assessment_creator::create_assessment ( const ai_assessment & assessment) const
nodiscard

Create an assessment SR document for an AI result.

Parameters
assessmentThe assessment data
Returns
Creation result with the SR dataset
Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/ai/aira_assessment.h.

Definition at line 98 of file aira_assessment.cpp.

99 {
100
101 assessment_creation_result result;
102
103 // Validate required fields
104 if (assessment.ai_result.sop_instance_uid.empty()) {
105 result.error_message = "AI result SOP Instance UID is required";
106 return result;
107 }
108
109 if (assessment.assessor_name.empty()) {
110 result.error_message = "Assessor name is required";
111 return result;
112 }
113
114 core::dicom_dataset sr;
115
116 // --- SOP Common Module ---
117 sr.set_string(tags::sop_class_uid, vr_type::UI,
118 "1.2.840.10008.5.1.4.1.1.88.22"); // Enhanced SR
119 result.assessment_uid = generate_uid();
120 sr.set_string(tags::sop_instance_uid, vr_type::UI, result.assessment_uid);
121
122 // --- Patient Module (Type 2 - empty if not provided) ---
123 build_patient_module(sr, assessment);
124
125 // --- General Study Module ---
126 sr.set_string(tags::study_instance_uid, vr_type::UI,
127 assessment.ai_result.study_instance_uid);
128 sr.set_string(tags::study_date, vr_type::DA, current_date());
129 sr.set_string(tags::study_time, vr_type::TM, current_time());
130 sr.set_string(tags::referring_physician_name, vr_type::PN, "");
131 sr.set_string(tags::study_id, vr_type::SH, "");
132 sr.set_string(tags::accession_number, vr_type::SH, "");
133
134 // --- General Series Module ---
135 sr.set_string(tags::modality, vr_type::CS, "SR");
136 sr.set_string(tags::series_instance_uid, vr_type::UI, generate_uid());
137 sr.set_string(tags::series_number, vr_type::IS, "1");
138
139 // --- General Equipment Module ---
140 sr.set_string(dicom_tag{0x0008, 0x0070}, vr_type::LO, ""); // Manufacturer
141
142 // --- SR Document General Module ---
143 sr.set_string(tags::instance_number, vr_type::IS, "1");
144 sr.set_string(tags::content_date, vr_type::DA, current_date());
145 sr.set_string(tags::content_time, vr_type::TM, current_time());
146 sr.set_string(aira_tags::completion_flag, vr_type::CS,
147 status_to_completion_flag(assessment.status));
148 sr.set_string(aira_tags::verification_flag, vr_type::CS, "UNVERIFIED");
149
150 // --- SR Document Content Module ---
151 build_sr_content(sr, assessment);
152
153 // --- Referenced SOP Sequence ---
154 build_referenced_sop_sequence(sr, assessment);
155
156 result.success = true;
157 result.sr_dataset = std::move(sr);
158
159 return result;
160}
static std::string status_to_completion_flag(assessment_status status)
Convert assessment_status to DICOM completion flag value.
void build_referenced_sop_sequence(core::dicom_dataset &sr, const ai_assessment &assessment) const
void build_patient_module(core::dicom_dataset &sr, const ai_assessment &assessment) const
void build_sr_content(core::dicom_dataset &sr, const ai_assessment &assessment) const
constexpr dicom_tag verification_flag
constexpr dicom_tag completion_flag
constexpr dicom_tag referring_physician_name
Referring Physician's Name.
constexpr dicom_tag content_time
Content Time.
constexpr dicom_tag sop_instance_uid
SOP Instance UID.
constexpr dicom_tag accession_number
Accession Number.
constexpr dicom_tag modality
Modality.
constexpr dicom_tag study_time
Study Time.
constexpr dicom_tag study_instance_uid
Study Instance UID.
constexpr dicom_tag series_number
Series Number.
constexpr dicom_tag content_date
Content Date.
constexpr dicom_tag sop_class_uid
SOP Class UID.
constexpr dicom_tag study_id
Study ID.
constexpr dicom_tag study_date
Study Date.
constexpr dicom_tag series_instance_uid
Series Instance UID.
constexpr dicom_tag instance_number
Instance Number.

References kcenon::pacs::core::tags::accession_number, kcenon::pacs::ai::ai_assessment::ai_result, kcenon::pacs::ai::assessment_creation_result::assessment_uid, kcenon::pacs::ai::ai_assessment::assessor_name, build_patient_module(), build_referenced_sop_sequence(), build_sr_content(), kcenon::pacs::ai::aira_tags::completion_flag, kcenon::pacs::core::tags::content_date, kcenon::pacs::core::tags::content_time, kcenon::pacs::ai::assessment_creation_result::error_message, generate_uid(), kcenon::pacs::core::tags::instance_number, kcenon::pacs::core::tags::modality, kcenon::pacs::core::tags::referring_physician_name, kcenon::pacs::core::tags::series_instance_uid, kcenon::pacs::core::tags::series_number, kcenon::pacs::core::tags::sop_class_uid, kcenon::pacs::ai::assessed_result_reference::sop_instance_uid, kcenon::pacs::core::tags::sop_instance_uid, kcenon::pacs::ai::assessment_creation_result::sr_dataset, kcenon::pacs::ai::ai_assessment::status, status_to_completion_flag(), kcenon::pacs::core::tags::study_date, kcenon::pacs::core::tags::study_id, kcenon::pacs::ai::assessed_result_reference::study_instance_uid, kcenon::pacs::core::tags::study_instance_uid, kcenon::pacs::core::tags::study_time, kcenon::pacs::ai::assessment_creation_result::success, and kcenon::pacs::ai::aira_tags::verification_flag.

Here is the call graph for this function:

◆ generate_uid()

std::string kcenon::pacs::ai::assessment_creator::generate_uid ( ) const
nodiscardprivate
Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/ai/aira_assessment.h.

Definition at line 313 of file aira_assessment.cpp.

313 {
314 static constexpr const char* uid_root = "1.2.826.0.1.3680043.2.1545.1";
315 static std::mt19937_64 gen{std::random_device{}()};
316 static std::uniform_int_distribution<uint64_t> dist;
317
318 auto now = std::chrono::system_clock::now();
319 auto timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(
320 now.time_since_epoch()).count();
321
322 return std::string(uid_root) + "." + std::to_string(timestamp) +
323 "." + std::to_string(dist(gen) % 100000);
324}

Referenced by create_assessment().

Here is the caller graph for this function:

◆ status_to_completion_flag()

std::string kcenon::pacs::ai::assessment_creator::status_to_completion_flag ( assessment_status status)
staticnodiscard

Convert assessment_status to DICOM completion flag value.

Parameters
statusAssessment status
Returns
Completion flag string (COMPLETE or PARTIAL)
Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/ai/aira_assessment.h.

Definition at line 348 of file aira_assessment.cpp.

349 {
350 switch (status) {
351 case assessment_status::draft: return "PARTIAL";
352 case assessment_status::final_: return "COMPLETE";
353 case assessment_status::amended: return "COMPLETE";
354 }
355 return "PARTIAL";
356}
@ amended
Previously finalized assessment has been amended.
@ draft
Assessment in progress, not yet finalized.
@ final_
Assessment finalized and signed off.

References kcenon::pacs::ai::amended, kcenon::pacs::ai::draft, and kcenon::pacs::ai::final_.

Referenced by create_assessment().

Here is the caller graph for this function:

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