11#ifndef WORKLIST_SCU_WORKLIST_RESULT_FORMATTER_HPP
12#define WORKLIST_SCU_WORKLIST_RESULT_FORMATTER_HPP
70 const std::vector<kcenon::pacs::core::dicom_dataset>& results)
const {
123 item.patient_name = ds.
get_string(tags::patient_name);
124 item.patient_id = ds.
get_string(tags::patient_id);
125 item.patient_birth_date = ds.
get_string(tags::patient_birth_date);
126 item.patient_sex = ds.
get_string(tags::patient_sex);
129 item.accession_number = ds.
get_string(tags::accession_number);
130 item.study_uid = ds.
get_string(tags::study_instance_uid);
131 item.requested_procedure_id = ds.
get_string(tags::requested_procedure_id);
134 item.scheduled_date = ds.
get_string(tags::scheduled_procedure_step_start_date);
135 item.scheduled_time = ds.
get_string(tags::scheduled_procedure_step_start_time);
136 item.modality = ds.
get_string(tags::modality);
137 item.station_ae = ds.
get_string(tags::scheduled_station_ae_title);
138 item.step_id = ds.
get_string(tags::scheduled_procedure_step_id);
139 item.step_description = ds.
get_string(tags::scheduled_procedure_step_description);
148 const std::vector<kcenon::pacs::core::dicom_dataset>& results)
const {
149 std::ostringstream oss;
151 if (results.empty()) {
152 oss <<
"No worklist items found.\n";
157 std::vector<worklist_item> items;
158 items.reserve(results.size());
159 for (
const auto& r : results) {
164 size_t w_name = 20, w_id = 12, w_date = 10, w_time = 8;
165 size_t w_mod = 6, w_station = 16, w_accession = 12, w_step = 12;
168 for (
const auto& item : items) {
169 w_name = std::min(
size_t(30), std::max(w_name, item.patient_name.length()));
170 w_id = std::min(
size_t(20), std::max(w_id, item.patient_id.length()));
171 w_station = std::min(
size_t(20), std::max(w_station, item.station_ae.length()));
172 w_accession = std::min(
size_t(20), std::max(w_accession, item.accession_number.length()));
173 w_step = std::min(
size_t(20), std::max(w_step, item.step_id.length()));
177 oss <<
"\n=== Worklist Results (" << results.size() <<
" scheduled procedure(s)) ===\n\n";
181 << std::setw(
static_cast<int>(w_name + 2)) <<
"Patient Name"
182 << std::setw(
static_cast<int>(w_id + 2)) <<
"Patient ID"
183 << std::setw(
static_cast<int>(w_date + 2)) <<
"Sched Date"
184 << std::setw(
static_cast<int>(w_time + 2)) <<
"Time"
185 << std::setw(
static_cast<int>(w_mod + 2)) <<
"Mod"
186 << std::setw(
static_cast<int>(w_station + 2)) <<
"Station AE"
187 << std::setw(
static_cast<int>(w_accession + 2)) <<
"Accession#"
188 << std::setw(
static_cast<int>(w_step + 2)) <<
"Step ID"
192 oss << std::string(w_name,
'-') <<
" "
193 << std::string(w_id,
'-') <<
" "
194 << std::string(w_date,
'-') <<
" "
195 << std::string(w_time,
'-') <<
" "
196 << std::string(w_mod,
'-') <<
" "
197 << std::string(w_station,
'-') <<
" "
198 << std::string(w_accession,
'-') <<
" "
199 << std::string(w_step,
'-') <<
" "
203 for (
const auto& item : items) {
205 << std::setw(
static_cast<int>(w_name + 2)) <<
truncate(item.patient_name, w_name)
206 << std::setw(
static_cast<int>(w_id + 2)) <<
truncate(item.patient_id, w_id)
207 << std::setw(
static_cast<int>(w_date + 2)) <<
format_date(item.scheduled_date)
208 << std::setw(
static_cast<int>(w_time + 2)) <<
format_time(item.scheduled_time)
209 << std::setw(
static_cast<int>(w_mod + 2)) << item.modality
210 << std::setw(
static_cast<int>(w_station + 2)) <<
truncate(item.station_ae, w_station)
211 << std::setw(
static_cast<int>(w_accession + 2)) <<
truncate(item.accession_number, w_accession)
212 << std::setw(
static_cast<int>(w_step + 2)) <<
truncate(item.step_id, w_step)
223 const std::vector<kcenon::pacs::core::dicom_dataset>& results)
const {
224 std::ostringstream oss;
227 oss <<
" \"resultCount\": " << results.size() <<
",\n";
228 oss <<
" \"worklistItems\": [\n";
230 for (
size_t i = 0; i < results.size(); ++i) {
234 oss <<
" \"patient\": {\n";
235 oss <<
" \"name\": \"" <<
escape_json(item.patient_name) <<
"\",\n";
236 oss <<
" \"id\": \"" <<
escape_json(item.patient_id) <<
"\",\n";
237 oss <<
" \"birthDate\": \"" << item.patient_birth_date <<
"\",\n";
238 oss <<
" \"sex\": \"" << item.patient_sex <<
"\"\n";
240 oss <<
" \"scheduledProcedureStep\": {\n";
241 oss <<
" \"startDate\": \"" << item.scheduled_date <<
"\",\n";
242 oss <<
" \"startTime\": \"" << item.scheduled_time <<
"\",\n";
243 oss <<
" \"modality\": \"" << item.modality <<
"\",\n";
244 oss <<
" \"stationAETitle\": \"" <<
escape_json(item.station_ae) <<
"\",\n";
245 oss <<
" \"stepId\": \"" <<
escape_json(item.step_id) <<
"\",\n";
246 oss <<
" \"description\": \"" <<
escape_json(item.step_description) <<
"\"\n";
248 oss <<
" \"accessionNumber\": \"" <<
escape_json(item.accession_number) <<
"\",\n";
249 oss <<
" \"studyInstanceUid\": \"" << item.study_uid <<
"\",\n";
250 oss <<
" \"requestedProcedureId\": \"" <<
escape_json(item.requested_procedure_id) <<
"\"\n";
253 if (i < results.size() - 1) {
269 const std::vector<kcenon::pacs::core::dicom_dataset>& results)
const {
270 std::ostringstream oss;
273 oss <<
"PatientName,PatientID,BirthDate,Sex,"
274 <<
"ScheduledDate,ScheduledTime,Modality,StationAE,"
275 <<
"StepID,StepDescription,AccessionNumber,StudyUID,RequestedProcedureID\n";
278 for (
const auto& r : results) {
283 << item.patient_birth_date <<
","
284 << item.patient_sex <<
","
285 << item.scheduled_date <<
","
286 << item.scheduled_time <<
","
287 << item.modality <<
","
292 << item.study_uid <<
","
293 <<
escape_csv(item.requested_procedure_id) <<
"\n";
303 const std::vector<kcenon::pacs::core::dicom_dataset>& results)
const {
304 std::ostringstream oss;
306 oss <<
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
307 oss <<
"<WorklistQueryResult>\n";
308 oss <<
" <ResultCount>" << results.size() <<
"</ResultCount>\n";
309 oss <<
" <WorklistItems>\n";
311 for (
size_t i = 0; i < results.size(); ++i) {
314 oss <<
" <WorklistItem index=\"" << (i + 1) <<
"\">\n";
317 oss <<
" <Patient>\n";
318 oss <<
" <Name>" <<
escape_xml(item.patient_name) <<
"</Name>\n";
319 oss <<
" <ID>" <<
escape_xml(item.patient_id) <<
"</ID>\n";
320 oss <<
" <BirthDate>" << item.patient_birth_date <<
"</BirthDate>\n";
321 oss <<
" <Sex>" << item.patient_sex <<
"</Sex>\n";
322 oss <<
" </Patient>\n";
325 oss <<
" <ScheduledProcedureStep>\n";
326 oss <<
" <StartDate>" << item.scheduled_date <<
"</StartDate>\n";
327 oss <<
" <StartTime>" << item.scheduled_time <<
"</StartTime>\n";
328 oss <<
" <Modality>" << item.modality <<
"</Modality>\n";
329 oss <<
" <StationAETitle>" <<
escape_xml(item.station_ae) <<
"</StationAETitle>\n";
330 oss <<
" <StepID>" <<
escape_xml(item.step_id) <<
"</StepID>\n";
331 oss <<
" <Description>" <<
escape_xml(item.step_description) <<
"</Description>\n";
332 oss <<
" </ScheduledProcedureStep>\n";
335 oss <<
" <AccessionNumber>" <<
escape_xml(item.accession_number) <<
"</AccessionNumber>\n";
336 oss <<
" <StudyInstanceUID>" << item.study_uid <<
"</StudyInstanceUID>\n";
337 oss <<
" <RequestedProcedureID>" <<
escape_xml(item.requested_procedure_id) <<
"</RequestedProcedureID>\n";
339 oss <<
" </WorklistItem>\n";
342 oss <<
" </WorklistItems>\n";
343 oss <<
"</WorklistQueryResult>\n";
351 [[nodiscard]]
static std::string
truncate(
const std::string& s,
size_t max_len) {
352 if (s.length() <= max_len) {
355 return s.substr(0, max_len - 3) +
"...";
361 [[nodiscard]]
static std::string
format_date(
const std::string& date) {
362 if (date.length() == 8) {
363 return date.substr(0, 4) +
"-" + date.substr(4, 2) +
"-" + date.substr(6, 2);
371 [[nodiscard]]
static std::string
format_time(
const std::string& time) {
372 if (time.length() >= 4) {
373 return time.substr(0, 2) +
":" + time.substr(2, 2);
381 [[nodiscard]]
static std::string
escape_json(
const std::string& s) {
383 result.reserve(s.length());
386 case '"': result +=
"\\\"";
break;
387 case '\\': result +=
"\\\\";
break;
388 case '\b': result +=
"\\b";
break;
389 case '\f': result +=
"\\f";
break;
390 case '\n': result +=
"\\n";
break;
391 case '\r': result +=
"\\r";
break;
392 case '\t': result +=
"\\t";
break;
394 if (
static_cast<unsigned char>(c) < 0x20) {
397 std::snprintf(buf,
sizeof(buf),
"%04X",
398 static_cast<unsigned char>(c));
411 [[nodiscard]]
static std::string
escape_csv(
const std::string& s) {
412 if (s.find_first_of(
",\"\n\r") == std::string::npos) {
416 std::string result =
"\"";
431 [[nodiscard]]
static std::string
escape_xml(
const std::string& s) {
433 result.reserve(s.length());
436 case '&': result +=
"&";
break;
437 case '<': result +=
"<";
break;
438 case '>': result +=
">";
break;
439 case '"': result +=
""";
break;
440 case '\'': result +=
"'";
break;
441 default: result += c;
auto get_string(dicom_tag tag, std::string_view default_value="") const -> std::string
Get the string value of an element.
DICOM Dataset - ordered collection of Data Elements.
Compile-time constants for commonly used DICOM tags.
output_format
Output format enumeration.
@ xml
XML format for integration.
@ json
JSON format for integration.
@ csv
CSV format for export.
@ table
Human-readable table format (alias: text)
output_format parse_output_format(std::string_view format_str)
Parse output format from string.