Skip to content

Commit

Permalink
CH EMED EPR / WIP support for emediplan export
Browse files Browse the repository at this point in the history
  • Loading branch information
dvribeira committed Oct 2, 2024
1 parent 00aa35d commit 75b5457
Show file tree
Hide file tree
Showing 12 changed files with 254 additions and 180 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ public Identifier addIdentifier(final String system,

/**
* Fetches the list of email address values, optionally checking if the period is valid at the moment.
* @param activeOnly If true, the method will filter out email address for which the current timestamp is not within
* @param activeOnly If true, the method will filter out phone numbers for which the current timestamp is not within
* the period boundaries, if defined.
* @return The list of matching email addresses, as strings.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,6 @@

import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.time.Instant;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;

Expand All @@ -34,21 +31,13 @@ public byte[] generate(final ChEmedEprDocumentPmlc pmlcDocument, final Narrative
bookmarks.put("Traitements actifs", "#active-treatments");
bookmarks.put("Traitements en réserve", "#asneeded-treatments");

final var generationDate =
DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm", lang.getLocale()).withZone(ZoneId.systemDefault()).format(Instant.now());
final var subjectDateOfBirth =
DateTimeFormatter.ofPattern("dd/MM/yyyy", lang.getLocale()).withZone(ZoneId.systemDefault()).format(pmlcDocument.resolvePatient().resolveBirthDate());

// TODO change template to remove this substitution
final var template = this.template.replace("#generationDate", generationDate)
.replace("#subjectDateOfBirth", subjectDateOfBirth);

final Map<String, Object> contextVariables = Map.of(
"subject", formatHumanName(pmlcDocument.resolvePatient().resolveName()),
"author", getPdfAuthor(),
"description", "Une carte de médication pour le sujet, générée à la demande",
"title", "Carte de médication",
"bookmarks", bookmarks,
"resource", pmlcDocument,
"content", body
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public byte[] generate(final ChEmedEprDocumentPmlc pmlcDocument, final Narrative

final Map<String, Object> contextVariables = Map.of(
"subject", formatHumanName(pmlcDocument.resolvePatient().resolveName()),
"author", getPdfAuthor(), // TODO implement
"author", getPdfAuthor(),
"description", "Une carte de médication pour le sujet, générée à la demande",
"title", "Carte de médication",
"bookmarks", bookmarks,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ protected String getPdfAuthor() {
}

static InputStream getResourceAsStream(String resource) throws FileNotFoundException {
InputStream is = PdfMedicationCardGenerator.class.getResourceAsStream("/emed/pdf/fonts/" + resource);
InputStream is = PdfMedicationCardGenerator.class.getClassLoader().getResourceAsStream("narrative/default/font/" + resource);
if (is == null) {
throw new FileNotFoundException("The resource '" + resource + "' is not found");
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@
<span th:if="${lastStatement.resolveInformationSource.practitionerRole != null}">
<th:block th:replace="~{datatypes :: human-name(${lastStatement.resolveInformationSource.practitionerRole.resolvePractitioner.name[0]}, ${lang})}"/>
</span>
<span>(<th:block th:replace="~{datatypes :: instant(${lastStatement.resolveInformationSource.time}, ${lang})}"/>)</span>
<span>(<th:block th:replace="~{datatypes :: instant(${lastStatement.resolveMedicalAuthorshipTimestamp()}, ${lang})}"/>)</span>
</th:block>
Original file line number Diff line number Diff line change
Expand Up @@ -97,12 +97,6 @@
<th:block th:if="${statement.resolveMedicalAuthor().getPractitionerRole() != null}">
<th:block th:insert="~{datatypes :: human-name(${statement.resolveMedicalAuthor().getPractitionerRole().resolvePractitioner().resolveName()}, ${lang})}"/>
</th:block>
<!-- if self-medication we will just write that instead of displaying the name of the medical author
<th:block th:if="${statement.resolveMedicalAuthor().getPatient() != null}"
th:insert="~{datatypes :: human-name(${statement.resolveMedicalAuthor().getPatient().resolveName()}, ${lang})}"/>
<th:block th:if="${statement.resolveMedicalAuthor().getRelatedPerson() != null}"
th:insert="~{datatypes :: human-name(${statement.resolveMedicalAuthor().getRelatedPerson().getNameFirstRep()}, ${lang})}"/>
-->
<th:block th:unless="${statement.resolveMedicalAuthor().getPractitionerRole() != null}"
th:text="Automédication"/>
</td>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,19 @@
~ on the basis of the eHealth Connector opensource project from June 28, 2021,
~ whereas medshare GmbH is the initial and main contributor/author of the eHealth Connector.
*/-->
<th:block th:fragment="patient-data(patient, lang)">
<th:block th:fragment="patient-data(patient, weightObservation, lang)">
<span class="emediplan-patient-data-name" th:insert="~{datatypes :: human-name(${patient.name[0]}, ${lang})}"/>
<span class="emediplan-patient-data-birthdate" th:insert="~{emediplan/emediplan_datatypes :: fhirDate(${patient.getBirthDateElement()}, ${lang})}"/>
<span class="emediplan-patient-data-admin-gender" th:insert="~{emediplan/emediplan_datatypes :: admin-gender(${patient.resolveGender()}, ${lang})}"/>
<span class="emediplan-patient-data-address" th:if="${patient.hasAddress()}">
<th:block th:insert="~{emediplan/emediplan_datatypes :: address(${patient.resolveAddress()}, ${lang})}"/>
</span>
<!-- TODO add phone number -->

<!-- TODO add medical and risk parameters -->
<th:block th:with="phoneNumbers=${patient.resolvePhoneNumbersAsStrings(true)}">
<span class="emediplan-patient-data-address" th:if="${patient.hasAddress()}">
<th:block th:insert="~{emediplan/emediplan_datatypes :: address(${patient.resolveAddress()}, ${lang})}"/>
</span>
<span class="emediplan-patient-phone-number" th:each="phoneNumber : ${phoneNumbers}"> / [[${phoneNumber}]]</span>
</th:block>
<th:block th:if="${weightObservation != null}">
<span class="emediplan-patient-data-weight">Poids: <th:block th:replace="~{datatypes :: amount-quantity}">80 Kg</th:block></span>
</th:block>
</th:block>

<th:block th:fragment="short-patient-data(patient, lang)">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@
<span class="emediplan-header-title">Le plan suisse de médication</span>
</td>
<td class="emediplan-header-patient" id="emediplan-header-patient-info"
th:insert="~{emediplan/emediplan_patient :: patient-data(${resource.resolvePatient()}, ${lang})}">
th:insert="~{emediplan/emediplan_patient :: patient-data(${resource.resolvePatient()}, ${resource.resolveComposition.resolvePatientWeightObservation()}, ${lang})}">
</td>
<th:block th:with="lastStatement = ${resource.resolveLastStatement()}">
<td class="emediplan-header-service-provider" id="emediplan-header-service-provider" th:if="${lastStatement != null}">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@
<th:block th:if="${lastStatement.resolveInformationSource.practitionerRole != null}">
<th:block th:insert="~{datatypes :: human-name(${lastStatement.resolveInformationSource.practitionerRole.resolvePractitioner.name[0]}, ${lang})}"/>
</th:block>
(<th:block th:insert="~{datatypes :: instant(${lastStatement.resolveInformationSource.time}, ${lang})}"/>)
(<th:block th:insert="~{datatypes :: instant(${lastStatement.resolveMedicalAuthorshipTimestamp()}, ${lang})}"/>)
</th:block>
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package org.projecthusky.fhir.emed.ch.epr.narrative.html;

import ca.uhn.fhir.context.FhirContext;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.projecthusky.fhir.emed.ch.common.enums.EmedDocumentType;
import org.projecthusky.fhir.emed.ch.epr.narrative.enums.NarrativeLanguage;
import org.projecthusky.fhir.emed.ch.epr.narrative.pdf.EMediplanPdfMedicationCardGenerator;
import org.projecthusky.fhir.emed.ch.epr.resource.pmlc.ChEmedEprCompositionPmlc;
import org.projecthusky.fhir.emed.ch.epr.resource.pmlc.ChEmedEprDocumentPmlc;
import org.projecthusky.fhir.emed.ch.epr.service.ChEmedEprParser;
import org.projecthusky.validation.service.pdf.PdfA12Validator;
import org.verapdf.core.ValidationException;

import javax.xml.parsers.ParserConfigurationException;
import java.io.FileOutputStream;
import java.io.IOException;

import static org.junit.jupiter.api.Assertions.*;

public class EMediplanPdfGeneratorTest {
private static final SoftwareProviderMetadata softwareProviderMetadata = new SoftwareProviderMetadata(
"Husky",
"test",
"Husky"
);

@Test
void testGeneratePdf() throws IOException, ParserConfigurationException, ValidationException {
final var xml = new String(getClass().getResourceAsStream("/2-7-MedicationCard.xml").readAllBytes());
final var parser = new ChEmedEprParser(FhirContext.forR4Cached());
final var documents = parser.parse(xml, EmedDocumentType.PMLC);
assertInstanceOf(ChEmedEprDocumentPmlc.class, documents);
final var pmlcDocument = (ChEmedEprDocumentPmlc) documents;
assertNotNull(pmlcDocument.resolveComposition());
assertInstanceOf(ChEmedEprCompositionPmlc.class, pmlcDocument.resolveComposition());

final var pdfGenerator = new EMediplanPdfMedicationCardGenerator(() -> softwareProviderMetadata);
final var generatedPdf = pdfGenerator.generate(pmlcDocument, NarrativeLanguage.FRENCH);
assertNotNull(generatedPdf);

final var pdfValidator = new PdfA12Validator();
final var validationResult = pdfValidator.validate(generatedPdf);
assertTrue(validationResult.isCompliant());
}

@Test @Disabled
void testGenerateAndWritePdf() throws IOException, ParserConfigurationException, ValidationException {
final var xml = new String(getClass().getResourceAsStream("/2-7-MedicationCard.xml").readAllBytes());
final var parser = new ChEmedEprParser(FhirContext.forR4Cached());
final var documents = parser.parse(xml, EmedDocumentType.PMLC);
assertInstanceOf(ChEmedEprDocumentPmlc.class, documents);
final var pmlcDocument = (ChEmedEprDocumentPmlc) documents;
assertNotNull(pmlcDocument.resolveComposition());
assertInstanceOf(ChEmedEprCompositionPmlc.class, pmlcDocument.resolveComposition());

final var pdfGenerator = new EMediplanPdfMedicationCardGenerator(() -> softwareProviderMetadata);
final var generatedPdf = pdfGenerator.generate(pmlcDocument, NarrativeLanguage.FRENCH);
assertNotNull(generatedPdf);

final var pdfValidator = new PdfA12Validator();
final var validationResult = pdfValidator.validate(generatedPdf);
assertTrue(validationResult.isCompliant());

final var pdfOut = new FileOutputStream("pdfOut.pdf");
pdfOut.write(generatedPdf);
pdfOut.close();
}
}
Original file line number Diff line number Diff line change
@@ -1,21 +1,34 @@
package org.projecthusky.fhir.emed.ch.epr.narrative.html;

import ca.uhn.fhir.context.FhirContext;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.projecthusky.fhir.emed.ch.common.enums.EmedDocumentType;
import org.projecthusky.fhir.emed.ch.epr.narrative.enums.NarrativeLanguage;
import org.projecthusky.fhir.emed.ch.epr.narrative.pdf.ChEmedEprPdfMedicationCardGenerator;
import org.projecthusky.fhir.emed.ch.epr.resource.pmlc.ChEmedEprCompositionPmlc;
import org.projecthusky.fhir.emed.ch.epr.resource.pmlc.ChEmedEprDocumentPmlc;
import org.projecthusky.fhir.emed.ch.epr.service.ChEmedEprParser;
import org.projecthusky.validation.service.pdf.PdfA12Validator;
import org.verapdf.core.ValidationException;

import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import javax.xml.parsers.ParserConfigurationException;
import java.io.FileOutputStream;
import java.io.IOException;

import static org.junit.jupiter.api.Assertions.*;

/**
* Tests of the {@link EmedPdfGenerator} class.
* Tests of the {@link ChEmedEprPdfMedicationCardGenerator} class.
*
* @author Ronaldo Loureiro
*/
class EmedPdfGeneratorTest {
private static final SoftwareProviderMetadata softwareProviderMetadata = new SoftwareProviderMetadata(
"Husky",
"test",
"Husky"
);

@Test
void testGeneratePdf() throws Exception {
Expand All @@ -27,25 +40,35 @@ void testGeneratePdf() throws Exception {
assertNotNull(pmlcDocument.resolveComposition());
assertInstanceOf(ChEmedEprCompositionPmlc.class, pmlcDocument.resolveComposition());

// final var doc = NarrativeTreatmentDocument.builder(NarrativeLanguage.FRENCH)
// .emedDocumentDigest(pmlcDocument, EmedDocumentType.PMLC)
// .build();

// final var indexDbAugmentationService = new IndexDbAugmentationService("jdbc:postgresql://localhost:5432/pharmINDEX", "postgres", "root");
// for (var i : doc.getActiveTreatments()) {
// indexDbAugmentationService.augment(i, doc.getNarrativeLanguage());
// }

// final var templateHeader = new String(Objects.requireNonNull(PdfOriginalRepresentationGenerator.class.getResourceAsStream("/narrative/default/template.header.html")).readAllBytes(), StandardCharsets.UTF_8);
//
// final var pdfGenerator = new PdfOriginalRepresentationGenerator();
// final var pdf = pdfGenerator.generate(doc, templateHeader, "</body></html>");
//
// NarrativeUtils.setPdfOriginalRepresentation(pmlcDocument, pdf);
// assertArrayEquals(pdf, pmlcDocument.resolveComposition().getOriginalRepresentationPdf());
//
// final var pdfOut = new FileOutputStream("pdtOut.pdf");
// pdfOut.write(pdf);
// pdfOut.close();
final var pdfGenerator = new ChEmedEprPdfMedicationCardGenerator(() -> softwareProviderMetadata);
final var generatedPdf = pdfGenerator.generate(pmlcDocument, NarrativeLanguage.FRENCH);
assertNotNull(generatedPdf);

final var pdfValidator = new PdfA12Validator();
final var validationResult = pdfValidator.validate(generatedPdf);
assertTrue(validationResult.isCompliant());
}

@Test @Disabled
void testGenerateAndWritePdf() throws IOException, ParserConfigurationException, ValidationException {
final var xml = new String(getClass().getResourceAsStream("/2-7-MedicationCard.xml").readAllBytes());
final var parser = new ChEmedEprParser(FhirContext.forR4Cached());
final var documents = parser.parse(xml, EmedDocumentType.PMLC);
assertInstanceOf(ChEmedEprDocumentPmlc.class, documents);
final var pmlcDocument = (ChEmedEprDocumentPmlc) documents;
assertNotNull(pmlcDocument.resolveComposition());
assertInstanceOf(ChEmedEprCompositionPmlc.class, pmlcDocument.resolveComposition());

final var pdfGenerator = new ChEmedEprPdfMedicationCardGenerator(() -> softwareProviderMetadata);
final var generatedPdf = pdfGenerator.generate(pmlcDocument, NarrativeLanguage.FRENCH);
assertNotNull(generatedPdf);

final var pdfValidator = new PdfA12Validator();
final var validationResult = pdfValidator.validate(generatedPdf);
assertTrue(validationResult.isCompliant());

final var pdfOut = new FileOutputStream("pdfOut.pdf");
pdfOut.write(generatedPdf);
pdfOut.close();
}
}
Loading

0 comments on commit 75b5457

Please sign in to comment.