Skip to content

Commit b1a3552

Browse files
committed
add Library.content html view
1 parent 22fd6fe commit b1a3552

File tree

5 files changed

+161
-3
lines changed

5 files changed

+161
-3
lines changed

dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/adapter/ResourceLibrary.java

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,42 @@
11
package dev.dsf.fhir.adapter;
22

3+
import java.nio.charset.Charset;
34
import java.util.List;
5+
import java.util.function.Function;
46

7+
import org.hl7.fhir.r4.model.Attachment;
58
import org.hl7.fhir.r4.model.Coding;
69
import org.hl7.fhir.r4.model.Library;
10+
import org.slf4j.Logger;
11+
import org.slf4j.LoggerFactory;
12+
13+
import com.fasterxml.jackson.core.JsonProcessingException;
14+
import com.fasterxml.jackson.databind.ObjectMapper;
15+
import com.fasterxml.jackson.databind.ObjectWriter;
716

817
public class ResourceLibrary extends AbstractMetdataResource<Library>
918
{
10-
private record Element(String subtitle, String description, List<String> type)
19+
private static final Logger logger = LoggerFactory.getLogger(ResourceLibrary.class);
20+
21+
private static final String CONTENT_TYPE_CQL = "text/cql";
22+
private static final String CONTENT_TYPE_STRUCTURED_QUERY = "application/json";
23+
private static final List<String> CONTENT_TYPES_SUPPORTED = List.of(CONTENT_TYPE_CQL,
24+
CONTENT_TYPE_STRUCTURED_QUERY);
25+
26+
private record Element(String subtitle, String description, List<String> type, List<ContentElement> content)
1127
{
1228
}
1329

30+
private record ContentElement(String data, String contentType)
31+
{
32+
static ContentElement from(Attachment attachment, Function<String, String> prettyPrint)
33+
{
34+
String data = new String(attachment.getData(), Charset.defaultCharset());
35+
String pretty = prettyPrint.apply(data);
36+
return new ContentElement(pretty, attachment.getContentType());
37+
}
38+
}
39+
1440
public ResourceLibrary()
1541
{
1642
super(Library.class);
@@ -28,6 +54,34 @@ protected Element toElement(Library resource)
2854
.map(c -> c.getSystemElement().getValue() + " | " + c.getCodeElement().getValue()).toList()
2955
: null;
3056

31-
return new Element(subtitle, description, type);
57+
List<ContentElement> contents = resource.getContent().stream().filter(Attachment::hasData)
58+
.filter(a -> CONTENT_TYPES_SUPPORTED.contains(a.getContentType()))
59+
.map(a -> ContentElement.from(a, getPrettyPrintFunction(a.getContentType()))).toList();
60+
61+
return new Element(subtitle, description, type, contents);
62+
}
63+
64+
private Function<String, String> getPrettyPrintFunction(String contentType)
65+
{
66+
if (CONTENT_TYPE_STRUCTURED_QUERY.equals(contentType))
67+
return this::prettyPrintJson;
68+
69+
return (input) -> input;
70+
}
71+
72+
private String prettyPrintJson(String toFormat)
73+
{
74+
try
75+
{
76+
ObjectMapper mapper = new ObjectMapper();
77+
Object json = mapper.readValue(toFormat, Object.class);
78+
ObjectWriter writer = mapper.writerWithDefaultPrettyPrinter();
79+
return writer.writeValueAsString(json);
80+
}
81+
catch (JsonProcessingException e)
82+
{
83+
logger.warn("Could not format JSON string, returning string unformatted");
84+
return toFormat;
85+
}
3286
}
3387
}

dsf-fhir/dsf-fhir-server/src/main/resources/fhir/static/dsf.css

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1199,6 +1199,75 @@ a#download {
11991199
text-decoration: none;
12001200
}
12011201

1202+
.content-header {
1203+
display: flex;
1204+
justify-content: space-between;
1205+
}
1206+
1207+
.content-pre {
1208+
margin-bottom: 0;
1209+
}
1210+
1211+
.collapsible {
1212+
transition: max-height 0.3s ease;
1213+
}
1214+
1215+
.collapse-button {
1216+
margin-top: -0.5em;
1217+
margin-right: -0.25em;
1218+
}
1219+
1220+
.collapsed {
1221+
overflow: hidden;
1222+
max-height: 15em;
1223+
position: relative;
1224+
}
1225+
1226+
.collapsed::after {
1227+
content: "";
1228+
position: absolute;
1229+
bottom: 0;
1230+
left: 0;
1231+
height: 10em;
1232+
width: 100%;
1233+
background: linear-gradient(to bottom, rgba(255, 255, 255, 0), var(--color-row-background));
1234+
pointer-events: none;
1235+
}
1236+
1237+
.collapsable.overflow::after {
1238+
opacity: 1;
1239+
}
1240+
1241+
.collapsable.overflow .collapse-button {
1242+
opacity: 1;
1243+
cursor: pointer;
1244+
}
1245+
1246+
.collapsable.no-overflow::after {
1247+
opacity: 0;
1248+
}
1249+
1250+
.collapsable.no-overflow .collapse-button {
1251+
opacity: 0;
1252+
cursor: default;
1253+
}
1254+
1255+
.expanded {
1256+
max-height: none;
1257+
}
1258+
1259+
.collapse-button {
1260+
fill: none;
1261+
stroke: #aaa;
1262+
stroke-width: 2;
1263+
stroke-linecap: round;
1264+
stroke-linejoin: round;
1265+
}
1266+
1267+
.collapse-button-rotated {
1268+
transform: rotate(180deg);
1269+
}
1270+
12021271
@media print {
12031272
body {
12041273
margin: 0;

dsf-fhir/dsf-fhir-server/src/main/resources/fhir/static/main.js

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,6 @@ window.addEventListener('DOMContentLoaded', () => {
9393
}
9494

9595
if (resourceType != null && resourceType[1] === 'Task' && resourceType[2] && (resourceType[3] === undefined || resourceType[4])) {
96-
9796
adaptTaskFormInputs()
9897

9998
// input placeholder insert buttons
@@ -128,6 +127,30 @@ window.addEventListener('DOMContentLoaded', () => {
128127
event.preventDefault()
129128
})
130129
}
130+
131+
document.querySelectorAll(".collapse-button").forEach(button => {
132+
button.addEventListener("click", () => {
133+
button.classList.toggle("collapse-button-rotated")
134+
135+
const parent = button.closest(".collapsable");
136+
parent.classList.toggle("collapsed");
137+
parent.classList.toggle("expanded");
138+
})
139+
});
140+
141+
document.querySelectorAll(".collapsable").forEach(element => {
142+
content = element.querySelector(".content-pre");
143+
144+
function checkOverflow() {
145+
if (content.scrollHeight > element.clientHeight) {
146+
element.classList.add("overflow");
147+
} else {
148+
element.classList.add("no-overflow");
149+
}
150+
}
151+
152+
checkOverflow();
153+
});
131154
})
132155

133156
window.addEventListener("popstate", (event) => {

dsf-fhir/dsf-fhir-server/src/main/resources/fhir/template/resourceElements.html

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,17 @@
9090
</ul>
9191
</div>
9292

93+
<div th:class="${class} ? ${class} : 'element element-100 collapsable collapsed'" th:fragment="contents" th:each="c : ${contents}">
94+
<div class="content-header">
95+
<label th:text="'Content ' + ${c.contentType}">contentType</label>
96+
<svg width="24" height="24" viewBox="0 0 24 24" class="collapse-button">
97+
<path d="M6 9l6 6 6-6"></path>
98+
</svg>
99+
</div>
100+
101+
<pre class="content-pre" th:text="${c.data}">data</pre>
102+
</div>
103+
93104
<i th:fragment="additionalValuesComment">See json or xml for additional values.</i>
94105
</div>
95106
</body>

dsf-fhir/dsf-fhir-server/src/main/resources/fhir/template/resourceLibrary.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
<th:block th:insert="~{resourceElements::string}" th:with="label='Date',string=${metadataResource.date}" th:if="${metadataResource.date}"></th:block>
1313
<th:block th:insert="~{resourceElements::string}" th:with="label='Description',string=${library.description}" th:if="${library.description}"></th:block>
1414
<th:block th:insert="~{resourceElements::strings}" th:with="label='Type',strings=${library.type}"></th:block>
15+
<th:block th:insert="~{resourceElements::contents}" th:with="contents=${library.content}" th:unless="${#lists.isEmpty(library.content)}"></th:block>
1516
<th:block th:insert="~{resourceElements::additionalValuesComment}"></th:block>
1617
</div>
1718
</div>

0 commit comments

Comments
 (0)