11#ifndef QUERY_SCU_RESULT_FORMATTER_HPP
12#define QUERY_SCU_RESULT_FORMATTER_HPP
70 const std::vector<kcenon::pacs::core::dicom_dataset>& results)
const {
87 const std::vector<kcenon::pacs::core::dicom_dataset>& results)
const {
89 std::ostringstream oss;
91 if (results.empty()) {
92 oss <<
"No results found.\n";
100 std::vector<size_t> widths;
101 widths.reserve(columns.size());
102 for (
const auto& col : columns) {
103 widths.push_back(col.header.length());
107 for (
const auto& result : results) {
108 for (
size_t i = 0; i < columns.size(); ++i) {
110 widths[i] = std::max(widths[i], value.length());
115 for (
auto& w : widths) {
116 w = std::min(w,
size_t(40));
120 oss <<
"\n=== Query Results (" << results.size() <<
" "
124 for (
size_t i = 0; i < columns.size(); ++i) {
125 oss << std::left << std::setw(static_cast<int>(widths[i] + 2))
126 << columns[i].header;
131 for (
size_t i = 0; i < columns.size(); ++i) {
132 oss << std::string(widths[i],
'-') <<
" ";
137 for (
const auto& result : results) {
138 for (
size_t i = 0; i < columns.size(); ++i) {
140 if (value.length() > widths[i]) {
141 value = value.substr(0, widths[i] - 3) +
"...";
143 oss << std::left << std::setw(static_cast<int>(widths[i] + 2))
156 const std::vector<kcenon::pacs::core::dicom_dataset>& results)
const {
158 std::ostringstream oss;
162 oss <<
" \"resultCount\": " << results.size() <<
",\n";
163 oss <<
" \"results\": [\n";
167 for (
size_t i = 0; i < results.size(); ++i) {
168 const auto& result = results[i];
171 for (
size_t j = 0; j < columns.size(); ++j) {
173 oss <<
" \"" << columns[j].json_key <<
"\": \""
175 if (j < columns.size() - 1) {
182 if (i < results.size() - 1) {
198 const std::vector<kcenon::pacs::core::dicom_dataset>& results)
const {
200 std::ostringstream oss;
205 for (
size_t i = 0; i < columns.size(); ++i) {
206 oss << columns[i].header;
207 if (i < columns.size() - 1) {
214 for (
const auto& result : results) {
215 for (
size_t i = 0; i < columns.size(); ++i) {
218 if (i < columns.size() - 1) {
242 std::vector<column_def> columns;
245 columns.push_back({
"Patient Name", tags::patient_name,
"patientName"});
246 columns.push_back({
"Patient ID", tags::patient_id,
"patientId"});
248 if (
level_ == query_level::patient) {
249 columns.push_back({
"Birth Date", tags::patient_birth_date,
"birthDate"});
250 columns.push_back({
"Sex", tags::patient_sex,
"sex"});
255 columns.push_back({
"Study Date", tags::study_date,
"studyDate"});
256 columns.push_back({
"Accession #", tags::accession_number,
"accessionNumber"});
257 columns.push_back({
"Description", tags::study_description,
"studyDescription"});
259 if (
level_ == query_level::study) {
260 columns.push_back({
"Modalities", tags::modalities_in_study,
"modalities"});
261 columns.push_back({
"Study UID", tags::study_instance_uid,
"studyInstanceUid"});
266 columns.push_back({
"Modality", tags::modality,
"modality"});
267 columns.push_back({
"Series #", tags::series_number,
"seriesNumber"});
268 columns.push_back({
"Series Desc", tags::series_description,
"seriesDescription"});
270 if (
level_ == query_level::series) {
271 columns.push_back({
"Series UID", tags::series_instance_uid,
"seriesInstanceUid"});
276 columns.push_back({
"Instance #", tags::instance_number,
"instanceNumber"});
277 columns.push_back({
"SOP Class", tags::sop_class_uid,
"sopClassUid"});
278 columns.push_back({
"SOP Instance UID", tags::sop_instance_uid,
"sopInstanceUid"});
295 [[nodiscard]]
static std::string
escape_json(
const std::string& s) {
297 result.reserve(s.length());
300 case '"': result +=
"\\\"";
break;
301 case '\\': result +=
"\\\\";
break;
302 case '\b': result +=
"\\b";
break;
303 case '\f': result +=
"\\f";
break;
304 case '\n': result +=
"\\n";
break;
305 case '\r': result +=
"\\r";
break;
306 case '\t': result +=
"\\t";
break;
308 if (
static_cast<unsigned char>(c) < 0x20) {
311 std::snprintf(buf,
sizeof(buf),
"%04X",
312 static_cast<unsigned char>(c));
325 [[nodiscard]]
static std::string
escape_csv(
const std::string& s) {
326 if (s.find_first_of(
",\"\n\r") == std::string::npos) {
330 std::string result =
"\"";
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.
auto to_string(mpps_status status) -> std::string_view
Convert mpps_status to DICOM string representation.
query_level
DICOM Query/Retrieve level enumeration.
output_format
Output format enumeration.
@ json
JSON format for integration.
@ csv
CSV format for export.
@ table
Human-readable table format.
output_format parse_output_format(std::string_view format_str)
Parse output format from string.
DICOM Query SCP service (C-FIND handler)