Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

NXRG - Design- og arkitekturbeskrivelse

TODO: skrive hvad slet er først, og anvend så det istedet for deprecated.

Indledning

Formål

Dokumentdeling gør det muligt for aktører sundhedsvæsnet at dele relevante data om borgere, og dermed skabe et overblik over den enkelte borgers situation for de aktører som har brug for dette. Det kan være dem, som planlægger og gennemfører behandlingsforløb eller for borgerens selv og omsorgspersoner. Samtidig skal det være muligt for borgeren at bestemme, hvem han/hun vil dele disse data med gennem samtykke samt se, hvem der har læst data via MinLog. Infrastrukturen der muliggør deling af dokumenter på tværs af sundhedsaktører er baseret på IHE XDS standarden

...

Code Block
languagexml
linenumberstrue
<ns2:Classification classificationScheme="urn:uuid:f4f85eac-e6cb-4883-b524-f2705394840f" classifiedObject="urn:uuid:69d3b9f3-7919-40e0-8731-32fb339216c2" nodeRepresentation="N" id="urn:uuid:9068bc2b-1717-4337-abfb-027f303b20c6">
  <ns2:Slot name="codingScheme">
    <ns2:ValueList>
      <ns2:Value>2.16.840.1.113883.5.25</ns2:Value>
    </ns2:ValueList>
  </ns2:Slot>
  <ns2:Name>
    <ns2:LocalizedString xml:lang="en-US" charset="UTF-8" value="N"/>
  </ns2:Name>
</ns2:Classification>

<ns2:ExternalIdentifier registryObject="urn:uuid:69d3b9f3-7919-40e0-8731-32fb339216c2" identificationScheme="urn:uuid:58a6f841-87b3-4a3e-92fd-a8ffeff98427" value="2512489996^^^&1.2.208.176.1.2&ISO" id="urn:uuid:6dc240d8-d871-4df1-b8c6-3414f1415022">
  <ns2:Name>
    <ns2:LocalizedString value="XDSDocumentEntry.patientId"/>
  </ns2:Name>
</ns2:ExternalIdentifier> 

Det følgende er eksempelkode til at illustrere et ITI-41 kald til oprettelse af et dokument

Vigtige Objekter og attributer

TODO: bedre overskrift

Når man arbejder med OpeneHealth core model er der nogle centrale klasser/objekter man arbejder med. 3 af dem er SubmissionSet, DocumentEntry og Relation:

Gliffy Diagram
macroIde68cb528-2a28-4579-ab6a-7e85a921815a
displayNameobjekter
nameobjekter
pagePin4

(Simplificert figur efter figur 4.1-1 i ITI TF-3. Da man på NSP ikke arbejder med folders er disse elementer udeladt)

Man anvender submissionSet når man sender data ind (dvs ikke til søgninger). Et submissionSet pakker et kald ind. Indholdet af et submissionSet er documententries og associations. Opretter man nye dokumenter, vil der i et submissionset være en eller flere documentEntries (et for hvert dokument) og tilsvarende antal "SE-DE HasMember" associationer.  Tilsvarende gælder for ret dokument. Men her vil der yderligere være en "Relationship" association af typen "replace". Deprecater man et dokument, har man ikke en docuemntEntry, men en "Relationship" association af typen "update availability status".

Når man opretter et documentEntry skal der sættes et entryUuid på. Dette id er vigtigt, da det anvendes i forbindelse med senere ret og deprecate af dokumentet. Det skal have prefix "urn:uuid:" for at være et entryUuid, ellers vil det blive opfattet som et symbolsk id, og den kaldte komponent vil selv tildele et gyldigt entryUuid.

Når man udfylder documentEntry med metadata skal man huske de førnævnte CDA standarder.

I forbindelse med, at man opretter associationer, skal der angives en source og target entryuuid, udover associationens egen entryUuid. Her gælder

  • For opret af dokumenter: source er submissionSet entryUuid og target er documentEntry's entryUuid
  • For ret, som for opret. Men den ekstra "replace" association er source  nye dokuments entryUuid og target det gamle dokument
  • For slet:  source er submissionSet entryUuid og target er entryUuid for det dokument som skal deprecates


TODO: hvilke id'er er vigtige ud over de nævne?

Når man laver en fremsøgning med ITI-18 kan man få to typer af objekter tilbage basert på den return type man sætter i kaldet. 

  • LEAF_CLASS: her returnes en liste af matchende documentEntry 
  • OBJECT_REF: her returnes en liste af objekt referencer 

TODO: debug nxrg og se faktisk lister



Eksempler på kald

Det følgende er eksempelkode til at illustrere et ITI-41 kald til oprettelse af et dokument

Code Block
languagejava
titleEksempel på ITI-41 kald
linenumberstrue
collapsetrue
// Nyt kald/request 
ProvideAndRegisterDocumentSet provideAndRegisterDocumentSet = new ProvideAndRegisterDocumentSet();

// Opret documentEntry
DocumentEntry documentEntry = new DocumentEntry();
AssigningAuthority patientIdAssigningAuthority = new AssigningAuthority("1.2.208.176.1.2"); // OID for CPR registret
Identifiable patientIdentifiable = patientIdentifiable = new Identifiable("2512489996", patientIdAssigningAuthority);
documentEntry.setPatientId(patientIdentifiable);
... mere metadata, se CDA profilen

// Tilføj dokumentet til request
provideAndRegisterDocumentSet.getDocuments().add(new Document(documentEntry, new DataHandler(new ByteArrayDataSource(documentPayload.getBytes(), documentEntry.
Code Block
languagejava
titleEksempel på ITI-41 kald
linenumberstrue
collapsetrue
// Nyt kald/request 
ProvideAndRegisterDocumentSet provideAndRegisterDocumentSet = new ProvideAndRegisterDocumentSet();

// Opret documentEntry
DocumentEntry documentEntry = new DocumentEntry();
AssigningAuthority patientIdAssigningAuthority = new AssigningAuthority("1.2.208.176.1.2"); // OID for CPR registret
Identifiable patientIdentifiable = patientIdentifiable = new Identifiable("2512489996", patientIdAssigningAuthority);
documentEntry.setPatientId(patientIdentifiable);
... mere metadata, se CDA profilen

// Tilføj dokumentet til request
provideAndRegisterDocumentSet.getDocuments().add(new Document(documentEntry, new DataHandler(new ByteArrayDataSource(documentPayload.getBytes(), documentEntry.getMimeType()))));

// Opret SubmissionSet
provideAndRegisterDocumentSet.setSubmissionSet(createSubmissionSet(documentEntry.getPatientId(), contentTypeCode, submissionTime));

// Opret association mellem SubmissionSet og DocumentEntry
provideAndRegisterDocumentSet.getAssociations().add(createAssociation(submissionSet, documentEntry));

// Transformer request - dette laver core model om til RIM format
ProvideAndRegisterDocumentSetTransformer registerDocumentSetTransformer = new ProvideAndRegisterDocumentSetTransformer(ebXMLFactory);
EbXMLProvideAndRegisterDocumentSetRequest30 ebxmlRequest = (EbXMLProvideAndRegisterDocumentSetRequest30) registerDocumentSetTransformer.toEbXML(provideAndRegisterDocumentSet);
ProvideAndRegisterDocumentSetRequestType provideAndRegisterDocumentSetRequestType = ebxmlRequest.getInternal();

// Udfør kald
RegistryResponseType registryResponse = iti41PortType.documentRepositoryProvideAndRegisterDocumentSetB(provideAndRegisterDocumentSetRequestType);

// Transformer response - dette laver RIM format til core model
ResponseTransformer responseTransformer = new ResponseTransformer(ebXMLFactory);
Response response = responseTransformer.fromEbXML(new EbXMLRegistryResponse30(registryResponse));

// Aflæs kaldets svar
if (response.getStatus().equals(Status.SUCCESS)) {
	// Kaldet gik godt
} else
if (response.getStatus().equals(Status.PARTIAL_SUCCESS) || response.getStatus().equals(Status.FAILURE)) {
	// Der er warning eller fejl i kaldet, håndter dem
	for (ErrorInfo error : response.getErrors()) {
	}
}

(Det er flere detaljer omkring ITI-41 eksempelkode i anvender guiden til DROS.)TODO map til tegning)


Et tilsvarende eksempel for ITI-18 fremsøgning kunne se således ud:

Code Block
languagejava
titleEksempel på ITI-18 kald
linenumberstrue
collapsetrue
// Nyt kald/request 
FindDocumentsQuery findDocumentsQuery = new FindDocumentsQuery();

// Opret søge kriterier
AssigningAuthority patientIdAssigningAuthority = new AssigningAuthority("1.2.208.176.1.2"); // OID for CPR registret
Identifiable patientIdentifiable = patientIdentifiable = new Identifiable("2512489996", patientIdAssigningAuthority);
findDocumentsQuery.setPatientId(patientIdentifiable); // angiv patienten dokumenterne vedrører

List<AvailabilityStatus> searchStatusList = new LinkedList<AvailabilityStatus>();
searchStatusList.add(AvailabilityStatus.APPROVED);
findDocumentsQuery.setStatus(searchStatusList); // søg efter dokumenter, som har status approved

findDocumentsQuery.getServiceStartTime().setFrom(serviceStartTimeFrom); // søg efter specifik dato interval som start tidspunkt
findDocumentsQuery.getServiceStartTime().setTo(serviceStartTimeTo);     // ved at angive fra og til serviceStartTime

QueryRegistry queryRegistry = new QueryRegistry(findDocumentsQuery);
queryRegistry.setReturnType(QueryReturnType.LEAF_CLASS); //returner fuld metadata for de fremsøgte dokumenter

// Transformer request - dette laver core model om til RIM format
QueryRegistryTransformer requestTransformer = new QueryRegistryTransformer();
EbXMLAdhocQueryRequest30 ebxmlRequest = (EbXMLAdhocQueryRequest30) requestTransformer.toEbXML(queryRegistry);
AdhocQueryRequest adhocQueryRequest = ebxmlRequest.getInternal();

// Udfør kald
AdhocQueryResponse adhocQueryResponse = iti18PortType.documentRegistryRegistryStoredQuery(adhocQueryRequest
AdhocQueryResponse adhocQueryResponse = iti18PortType.documentRegistryRegistryStoredQuery(adhocQueryRequest);

// Transformer response - dette laver RIM format til core model
QueryResponseTransformer responseTransformer = new QueryResponseTransformer(ebXMLFactory);
EbXMLQueryResponse ebXML = new EbXMLQueryResponse30(adhocQueryResponse);
QueryResponse queryResponse = responseTransformer.fromEbXML(ebXML);

// TransformerAflæs responsekaldets - dette laver RIM format til core model
QueryResponseTransformer responseTransformer = new QueryResponseTransformer(ebXMLFactory);
EbXMLQueryResponse ebXML = new EbXMLQueryResponse30(adhocQueryResponse);
QueryResponse queryResponse = responseTransformer.fromEbXML(ebXML);

// Aflæs kaldets svarsvar
if (queryResponse.getStatus().equals(Status.SUCCESS)) {
 	// Kaldet gik godt, håndter de fremsøgte dokumenter metadata
	for (DocumentEntry documentEntry : queryResponse.getDocumentEntries()) {
	} 
} else
if (queryResponse.getStatus().equals(Status.PARTIAL_SUCCESS) || queryResponse.getStatus().equals(Status.FAILURE)) {
 	// Kaldet gik godt Der er warning eller fejl i kaldet, håndter de fremsøgte dokumenter metadatadem
	for (DocumentEntryErrorInfo documentEntryerror : queryResponse.getDocumentEntriesgetErrors()) {
	} 
} else
if (queryResponse.getStatus().equals(Status.PARTIAL_SUCCESS) || queryResponse.getStatus().equals(Status.FAILURE)) {
	// Der er warning eller fejl i kaldet, håndter dem
	for (ErrorInfo error : queryResponse.getErrors()) {
	}
}

evt. request resspone 

eksempler på brug af librariet (hvilke objekter og hvilke id'er skal man tage stillinger)

ise det resulterende "RIM XML" så man forstår sammenhængen.

noget af det kan tages dros anvender guide

evt nævn logiske id'er og hvad der sker hvis man tror man sender et uuuid men ikke gør.

gode steder at kigge: integrationstestene  og anvender guides for komponenterne

husk at nævne overholdes af medcom standard for metadata i documententry

...




Sammenholder man ovenståede kodeekspempler kan man genkende ovenstående transformeringsfigurs kasser i kodelinierne. Eksempelvis for ITI-41 kaldet, hvor  line 1-18 svarer til boks 2 (core model), linie 20-23 til boks 2 og 3  (core model og RIM format), linie 26 til kaldet mellem boks 3 (RIM format) og NSP komponenten, linie 28-30 til boks 3 og 2 (RIM format og core model) samt linie 32-40 til boks 2 (core model)

Savner man inspiration til kodeeksempler er også integrationstestene til f.eks. DROS og NXRG et godt sted at kigge.

TODO: indsæt requst og response på iti41 og 18

Biblioteker til .Net

TODO: find noget

https://www.iheusa.org/sites/iheusa/files/IHE%20Profiles%20for%20Health%20Information%20Exchange.pdf

https://groups.google.com/g/ihe-north-america-connectathon-2007/c/dLNk8mWoMVY

De følgende links kan alle bidrage til at lette arbejdet med at implementere dokumentdeling.

...