From d3a98d7023c0bc28cae0fc8d6c2df9215eb05eee Mon Sep 17 00:00:00 2001 From: kirstinerosenbeck Date: Mon, 23 Mar 2026 13:57:44 +0100 Subject: [PATCH 01/11] initial commit of diagnostic report --- _build.new.bat | 386 ++++++++++++++++++ input/fsh/DkCoreDiagnosticReport.fsh | 36 ++ input/fsh/valueSets.fsh | 39 ++ ...inition-dk-core-diagnostic-report-intro.md | 48 +++ 4 files changed, 509 insertions(+) create mode 100644 _build.new.bat create mode 100644 input/fsh/DkCoreDiagnosticReport.fsh create mode 100644 input/pagecontent/StructureDefinition-dk-core-diagnostic-report-intro.md diff --git a/_build.new.bat b/_build.new.bat new file mode 100644 index 000000000..99d0e1f97 --- /dev/null +++ b/_build.new.bat @@ -0,0 +1,386 @@ +@ECHO OFF +setlocal enabledelayedexpansion + +SET "dlurl=https://github.com/HL7/fhir-ig-publisher/releases/latest/download/publisher.jar" +SET "publisher_jar=publisher.jar" +SET "input_cache_path=%CD%\input-cache\" +SET "skipPrompts=false" +SET "upper_path=..\" +SET "scriptdlroot=https://raw.githubusercontent.com/HL7/ig-publisher-scripts/main" +SET "build_bat_url=%scriptdlroot%/_build.bat" +SET "build_sh_url=%scriptdlroot%/_build.sh" + + + +:: Debugging statements to check jar file location +ECHO Checking for publisher.jar in %input_cache_path% +IF EXIST "%input_cache_path%%publisher_jar%" ( + SET "jar_location=%input_cache_path%%publisher_jar%" + ECHO Found publisher.jar in input-cache +) ELSE ( + ECHO Checking for publisher.jar in %upper_path% + IF EXIST "%upper_path%%publisher_jar%" ( + SET "jar_location=%upper_path%%publisher_jar%" + ECHO Found publisher.jar in parent folder + ) ELSE ( + SET "jar_location=not_found" + SET "default_choice=1" + ECHO publisher.jar not found in input-cache or parent folder + ) +) + + +:: Handle command-line argument to bypass the menu +IF NOT "%~1"=="" ( + IF /I "%~1"=="update" SET "userChoice=1" + IF /I "%~1"=="build" SET "userChoice=2" + IF /I "%~1"=="nosushi" SET "userChoice=3" + IF /I "%~1"=="notx" SET "userChoice=4" + IF /I "%~1"=="jekyll" SET "userChoice=5" + IF /I "%~1"=="clean" SET "userChoice=6" + IF /I "%~1"=="exit" SET "userChoice=0" + GOTO executeChoice +) + +echo --------------------------------------------------------------- +ECHO Checking internet connection... +PING tx.fhir.org -4 -n 1 -w 4000 >nul 2>&1 && SET "online_status=true" || SET "online_status=false" + +IF "%online_status%"=="true" ( + ECHO We're online and tx.fhir.org is available. + FOR /F "tokens=2 delims=:" %%a IN ('curl -s https://api.github.com/repos/HL7/fhir-ig-publisher/releases/latest ^| findstr "tag_name"') DO SET "latest_version=%%a" + SET "latest_version=!latest_version:"=!" + SET "latest_version=!latest_version: =!" + SET "latest_version=!latest_version:~0,-1!" +) ELSE ( + ECHO We're offline or tx.fhir.org is not available, can only run the publisher without TX... + SET "txoption=-tx n/a" + SET "latest_version=unknown" + SET "default_choice=4" +) + +echo --------------------------------------------------------------- + + +IF NOT "%jar_location%"=="not_found" ( + FOR /F "tokens=*" %%i IN ('java "-Dfile.encoding=UTF-8" -jar "%jar_location%" -v 2^>^&1') DO SET "publisher_version=%%i" + SET "publisher_version=!publisher_version:"=!" + ECHO Detected publisher version: !publisher_version! +) ELSE ( + SET "publisher_version=unknown" + ECHO publisher.jar location is not found +) + +ECHO Publisher version: !publisher_version!; Latest is !latest_version! + +IF NOT "%online_status%"=="true" ( + ECHO We're offline. +) ELSE ( + IF NOT "!publisher_version!"=="!latest_version!" ( + ECHO An update is recommended. + SET "default_choice=1" + ) ELSE ( + ECHO Publisher is up to date. + SET "default_choice=2" + ) +) + +echo --------------------------------------------------------------- +echo. + +echo Please select an option: +echo 1. Download or update publisher +echo 2. Build IG +echo 3. Build IG - no sushi +echo 4. Build IG - force no TX server +echo 5. Jekyll build +echo 6. Clean up temp directories +echo 0. Exit +:: echo [Press Enter for default (%default_choice%) or type an option number:] +echo. + +:: Using CHOICE to handle input with timeout +:: ECHO [Enter=Continue, 1-7=Option, 0=Exit] +choice /C 12345670 /N /CS /D %default_choice% /T 5 /M "Choose an option number or wait 5 seconds for default (%default_choice%):" +SET "userChoice=%ERRORLEVEL%" + + +:executeChoice +echo You selected: %userChoice% + +IF "%userChoice%"=="1" GOTO downloadpublisher +IF "%userChoice%"=="2" GOTO publish_once +IF "%userChoice%"=="3" GOTO publish_nosushi +IF "%userChoice%"=="4" GOTO publish_notx +IF "%userChoice%"=="5" GOTO debugjekyll +IF "%userChoice%"=="6" GOTO clean +IF "%userChoice%"=="0" EXIT /B + +:end + + + +:debugjekyll + echo Running Jekyll build... + jekyll build -s temp/pages -d output +GOTO end + + +:clean + echo Cleaning up directories... + if exist ".\input-cache\publisher.jar" ( + echo Preserving publisher.jar and removing other files in .\input-cache... + move ".\input-cache\publisher.jar" ".\" + rmdir /s /q ".\input-cache" + mkdir ".\input-cache" + move ".\publisher.jar" ".\input-cache" + ) else ( + if exist ".\input-cache\" ( + rmdir /s /q ".\input-cache" + ) + ) + if exist ".\temp\" ( + rmdir /s /q ".\temp" + echo Removed: .\temp + ) + if exist ".\output\" ( + rmdir /s /q ".\output" + echo Removed: .\output + ) + if exist ".\template\" ( + rmdir /s /q ".\template" + echo Removed: .\template + ) + +GOTO end + + + + + +:downloadpublisher +ECHO Downloading Publisher... +:processflags +SET ARG=%1 +IF DEFINED ARG ( + IF "%ARG%"=="-f" SET FORCE=true + IF "%ARG%"=="--force" SET FORCE=true + SHIFT + GOTO processflags +) + +FOR %%x IN ("%CD%") DO SET upper_path=%%~dpx + +ECHO. +IF NOT EXIST "%input_cache_path%%publisher_jar%" ( + IF NOT EXIST "%upper_path%%publisher_jar%" ( + SET jarlocation="%input_cache_path%%publisher_jar%" + SET jarlocationname=Input Cache + ECHO IG Publisher is not yet in input-cache or parent folder. + REM we don't use jarlocation below because it will be empty because we're in a bracketed if statement + GOTO create + ) ELSE ( + ECHO IG Publisher FOUND in parent folder + SET jarlocation="%upper_path%%publisher_jar%" + SET jarlocationname=Parent folder + GOTO upgrade + ) +) ELSE ( + ECHO IG Publisher FOUND in input-cache + SET jarlocation="%input_cache_path%%publisher_jar%" + SET jarlocationname=Input Cache + GOTO upgrade +) + +:create +IF DEFINED FORCE ( + MKDIR "%input_cache_path%" 2> NUL + GOTO download +) + +IF "%skipPrompts%"=="y" ( + SET create=Y +) ELSE ( + SET /p create="Download? (Y/N) " +) +IF /I "%create%"=="Y" ( + ECHO Will place publisher jar here: %input_cache_path%%publisher_jar% + MKDIR "%input_cache_path%" 2> NUL + GOTO download +) +GOTO done + +:upgrade +IF "%skipPrompts%"=="y" ( + SET overwrite=Y +) ELSE ( + SET /p overwrite="Overwrite %jarlocation%? (Y/N) " +) + +IF /I "%overwrite%"=="Y" ( + GOTO download +) +GOTO done + +:download +ECHO Downloading most recent publisher to %jarlocationname% - it's ~200 MB, so this may take a bit + +FOR /f "tokens=4-5 delims=. " %%i IN ('ver') DO SET VERSION=%%i.%%j +IF "%version%" == "10.0" GOTO win10 +IF "%version%" == "6.3" GOTO win8.1 +IF "%version%" == "6.2" GOTO win8 +IF "%version%" == "6.1" GOTO win7 +IF "%version%" == "6.0" GOTO vista + +ECHO Unrecognized version: %version% +GOTO done + +:win10 +CALL POWERSHELL -command if ('System.Net.WebClient' -as [type]) {(new-object System.Net.WebClient).DownloadFile(\"%dlurl%\",\"%jarlocation%\") } else { Invoke-WebRequest -Uri "%dlurl%" -Outfile "%jarlocation%" } + +GOTO done + +:win7 +rem this may be triggering the antivirus - bitsadmin.exe is a known threat +rem CALL bitsadmin /transfer GetPublisher /download /priority normal "%dlurl%" "%jarlocation%" + +rem this didn't work in win 10 +rem CALL Start-BitsTransfer /priority normal "%dlurl%" "%jarlocation%" + +rem this should work - untested +call (New-Object Net.WebClient).DownloadFile('%dlurl%', '%jarlocation%') +GOTO done + +:win8.1 +:win8 +:vista +GOTO done + + + +:done + + + + +ECHO. +ECHO Updating scripts +IF "%skipPrompts%"=="y" ( + SET updateScripts=Y +) ELSE ( + SET /p updateScripts="Update scripts? (Y/N) " +) +IF /I "%updateScripts%"=="Y" ( + GOTO scripts +) +GOTO end + + +:scripts + +REM Download all batch files (and this one with a new name) + +SETLOCAL DisableDelayedExpansion + + + +:dl_script_1 +ECHO Updating _build.sh +call POWERSHELL -command if ('System.Net.WebClient' -as [type]) {(new-object System.Net.WebClient).DownloadFile(\"%build_sh_url%\",\"_build.new.sh\") } else { Invoke-WebRequest -Uri "%build_sh_url%" -Outfile "_build.new.sh" } +if %ERRORLEVEL% == 0 goto upd_script_1 +echo "Errors encountered during download: %errorlevel%" +goto dl_script_2 +:upd_script_1 +start copy /y "_build.new.sh" "_build.sh" ^&^& del "_build.new.sh" ^&^& exit + + +:dl_script_2 +ECHO Updating _build.bat +call POWERSHELL -command if ('System.Net.WebClient' -as [type]) {(new-object System.Net.WebClient).DownloadFile(\"%build_bat_url%\",\"_build.new.bat\") } else { Invoke-WebRequest -Uri "%build_bat_url%" -Outfile "_build.new.bat" } +if %ERRORLEVEL% == 0 goto upd_script_2 +echo "Errors encountered during download: %errorlevel%" +goto end +:upd_script_2 +start copy /y "_build.new.bat" "_build.bat" ^&^& del "_build.new.bat" ^&^& exit + + +GOTO end + + +:publish_once + +SET JAVA_TOOL_OPTIONS=-Dfile.encoding=UTF-8 + +:: Debugging statements before running publisher +ECHO 1jar_location is: %jar_location% +IF NOT "%jar_location%"=="not_found" ( + java %JAVA_OPTS% -jar "%jar_location%" -ig . %txoption% %* +) ELSE ( + ECHO IG Publisher NOT FOUND in input-cache or parent folder. Please run _updatePublisher. Aborting... +) + +GOTO end + + + +:publish_nosushi + +SET JAVA_TOOL_OPTIONS=-Dfile.encoding=UTF-8 + +:: Debugging statements before running publisher +ECHO 3jar_location is: %jar_location% +IF NOT "%jar_location%"=="not_found" ( + java %JAVA_OPTS% -jar "%jar_location%" -ig . %txoption% -no-sushi %* +) ELSE ( + ECHO IG Publisher NOT FOUND in input-cache or parent folder. Please run _updatePublisher. Aborting... +) + +GOTO end + + +:publish_notx +SET txoption=-tx n/a + +SET JAVA_TOOL_OPTIONS=-Dfile.encoding=UTF-8 + +:: Debugging statements before running publisher +ECHO 2jar_location is: %jar_location% +IF NOT "%jar_location%"=="not_found" ( + java %JAVA_OPTS% -jar "%jar_location%" -ig . %txoption% %* +) ELSE ( + ECHO IG Publisher NOT FOUND in input-cache or parent folder. Please run _updatePublisher. Aborting... +) + +GOTO end + + + + +:publish_continuous + +SET JAVA_TOOL_OPTIONS=-Dfile.encoding=UTF-8 + +:: Debugging statements before running publisher +ECHO Checking %input_cache_path% for publisher.jar +IF EXIST "%input_cache_path%\%publisher_jar%" ( + java %JAVA_OPTS% -jar "%input_cache_path%\%publisher_jar%" -ig . %txoption% -watch %* +) ELSE ( + ECHO Checking %upper_path% for publisher.jar + IF EXIST "..\%publisher_jar%" ( + java %JAVA_OPTS% -jar "..\%publisher_jar%" -ig . %txoption% -watch %* + ) ELSE ( + ECHO IG Publisher NOT FOUND in input-cache or parent folder. Please run _updatePublisher. Aborting... + ) +) + +GOTO end + + +:end + +:: Pausing at the end + + +IF NOT "%skipPrompts%"=="true" ( + PAUSE +) diff --git a/input/fsh/DkCoreDiagnosticReport.fsh b/input/fsh/DkCoreDiagnosticReport.fsh new file mode 100644 index 000000000..6b948d1b9 --- /dev/null +++ b/input/fsh/DkCoreDiagnosticReport.fsh @@ -0,0 +1,36 @@ +Profile: DkCoreDiagnosticReport +Parent: DiagnosticReport +Id: dk-core-diagnostic-report +Description: "dk-core profile for laboratory reports." + +* identifier 0..1 + * ^short = "[DK] undersøgelsesnummer" + * system 1..1 + * system = "http://medcom/undersøgelsesnummer" (exactly) +//consider slicing + +//* basedOn 1..1 +//* basedOn only Reference(DkCoreServiceRequest) +// * ^short = "[DK] rapportBaseretPå" +* status ^short = "[DK] rapportstatus" +* category 0..* + * ^short = "[DK] rapportkategori" +* category ^slicing.discriminator.type = #value + * ^slicing.discriminator.path = "$this" + * ^slicing.rules = #open +* category contains + studyType 0..* and + specialty 0..* and + danishSpeciality 0..* +* category[studyType] from LoincLabStudyTypes (required) + * ^short = "The way of grouping of the test results into clinically meaningful domains (e.g. hematology study, microbiology study, etc.)" +* category[specialty] from dk-core-SCTLaboratorySpecialities (required) + * ^short = "The clinical domain of the laboratory performing the observation (e.g. microbiology, toxicology, chemistry)" +* category[danishSpeciality] from dk-core-practice-setting-code (required) +* code 1..1 +* code from dk-core-LoincDiagnosticDocumentTypes + * ^short = "[DK] rapportkode" + +* subject only Reference(DkCorePatient or Group or DkCoreLocation or Device) +* performer only Reference(DkCorePractitioner or DkCoreOrganization or DkCorePractitionerRole or CareTeam) +* resultsInterpreter only Reference(DkCorePractitioner or DkCoreOrganization or DkCorePractitionerRole or CareTeam) \ No newline at end of file diff --git a/input/fsh/valueSets.fsh b/input/fsh/valueSets.fsh index 6f6c4f340..c10731020 100644 --- a/input/fsh/valueSets.fsh +++ b/input/fsh/valueSets.fsh @@ -46,6 +46,31 @@ Description: "LOINC codes describing Basic Observations usable in Denmark." * $LOINC#8478-0 //"Mean blood pressure" * $LOINC#59408-5 //"Oxygen saturation in Arterial blood by Pulse oximetry" +ValueSet: LoincDiagnosticDocumentTypes +Id: dk-core-LoincDiagnosticDocumentTypes +Title: "DK Core LOINC Diagnostic Document Types" +Description: "LOINC codes describing diagnostic documents that may be used for common Danish use cases." +* ^experimental = false +* ^copyright = "This material contains content from LOINC (http://loinc.org). LOINC is copyright © 1995-2020, Regenstrief Institute, Inc. and the Logical Observation Identifiers Names and Codes (LOINC) Committee and is available at no cost under the license at http://loinc.org/license. LOINC® is a registered United States trademark of Regenstrief Institute, Inc" +* $LOINC#11502-2 //"Laboratory report" +* $LOINC#53576-5 //"Personal health monitoring report" +* $LOINC#11506-3 //"Progress note" + +ValueSet: LoincLabStudyTypes +Id: dk-core-LoincLabStudyTypes +Title: "DK Core Laboratory study types" +Description: "LOINC codes describing laboratory study types that are consistent with European use in EHDS." +* ^experimental = false +* ^copyright = "This material contains content from LOINC (http://loinc.org). LOINC is copyright © 1995-2020, Regenstrief Institute, Inc. and the Logical Observation Identifiers Names and Codes (LOINC) Committee and is available at no cost under the license at http://loinc.org/license. LOINC® is a registered United States trademark of Regenstrief Institute, Inc" +* $LOINC#18717-9 // Blood bank studies (set) +* $LOINC#18719-5 //Chemistry studies (set) +* $LOINC#18722-9 // Fertility studies (set) +* $LOINC#18723-7 // Hematology studies (set) +* $LOINC#18725-2 // Microbiology studies (set) +* $LOINC#18728-6 // Toxicology studies (set) +* $LOINC#26436-6 // Laboratory studies (set) + + ValueSet: SCTBasicObservation Id: dk-core-SCTBasicObservation Title: "DK Core SNOMED CT Basic Observation" @@ -84,6 +109,20 @@ Description: "SNOMED CT codes for Basic Observations usable in Denmark" * $sct#1149101003 //|Recumbent body height| * $sct#1162392001 //|Pre-amputation measured body height| +ValueSet: SCTLaboratorySpecialities +Id: dk-core-SCTLaboratorySpecialities +Title: "SNOMED CT Laboratory Specialities" +Description: "SNOMED CT codes for Laboratory Specialities. Note that this is the set of accepted specialities in Europe" +* ^experimental = false +* ^copyright = "This value set includes content from SNOMED CT, which is copyright © 2002+ International Health Terminology Standards Development Organisation (IHTSDO), and distributed by agreement between IHTSDO and HL7. Implementer use of SNOMED CT is not covered by this agreement" +* $sct#394596001 //Chemical pathology +* $sct#394916005 //Hematopathology +* $sct#421661004 //Blood banking and transfusion medicine +* $sct#394915009 //General pathology +* $sct#394598000 //Immunopathology +* $sct#408454008 //Clinical microbiology +* $sct#1236877003 //Genetic pathology +* $sct#1236878008 //Histocompatibility and Immunogenetics ValueSet: IEEEBasicObservation Id: dk-core-IEEEBasicObservation diff --git a/input/pagecontent/StructureDefinition-dk-core-diagnostic-report-intro.md b/input/pagecontent/StructureDefinition-dk-core-diagnostic-report-intro.md new file mode 100644 index 000000000..9b1ed0098 --- /dev/null +++ b/input/pagecontent/StructureDefinition-dk-core-diagnostic-report-intro.md @@ -0,0 +1,48 @@ +### Scope and Usage +The DkCoreDiagnosticReport profile is used for diagnostic reports and notes communicated within the Danish healthcare sector. Several use cases exist: + +* Laboratory reports +* Personal health monitoring reports +* Other reports conveying observations between healthcare providers + +The most typical is the laboratory report used to convey the laboratory’s response to a request for one or more analyses. It represents the report at a specific point in time and provides the overall context for the results produced. The report contains information about the report itself, the subject, and the specimen(s) on which the analyses are based. + +In this case, a DiagnosticReport serves as the primary structure that links the requested analyses, the underlying specimens, and the resulting observations into a coherent laboratory response. + +In other use cases, the DiagnosticReport is still a coherent report of resulting observations. These use cases are typically characterized by more relaxed requirements than within laboratory medicine. + +#### Code +DiagnosticReport.code may hold different values and LOINC document codes are recommended. In the EHDS laboratory standard, the code 'laboratory report' is always used. A few other example codes are included in the value set to make it easier to support common Danish use cases: + +* 'personal health monitoring report' for collections of data produced by the patients themselves, typically in telehealth projects. +* 'progress note' for clinical situations where a collection of observation results originates from an encounter and needs to be communicated to other actors in the health sector. Examples could be acute nursing encounters where measurements are shared with the general practitioner. It could also be a general practitioner visit or hospital encounter where a number of different observations are made and need to be shared afterwards. + +Note that if a general practitioner visit originates in a report containing only laboratory results, it is recommended to use the code 'laboratory-report'. To differentiate from laboratory reports made in laboratories, use an appropriate category instead. + +#### Identifier +The identifier may contain an analysis number equivalent to the Danish 'RekvNrLab' from MedCom's RPT01. It may correspond to the Danish requisition number (NPN), but could be re-assigned at the laboratory. For correctly referencing the requisition, see DiagnosticReport.basedOn instead. + +#### Status Codes +The DiagnosticReport resource must contain the status of the report and uses the required FHIR ValueSet as appropriate. + +All codes in the ValueSet may be used, but if mapping to and from the MedCom standard for laboratory results (RPT01/XRPT01), the following codes should be considered equivalent: + +The DiagnosticReport progresses through well-defined status values throughout its lifecycle. + +* registered: As soon as the specimen is received by the laboratory, a DiagnosticReport is created with the status 'registered'. This reflects that the order has entered the laboratory workflow and initial processing has begun. This corresponds to MedCom's 'modtaget'. +* partial: The status 'partial' is used when some, but not all, Observations within the DiagnosticReport have been finalized. At least one Observation result must still be pending for the status to remain 'partial'. This corresponds to MedCom's 'delsvar'. +* final: When all observations included in the DiagnosticReport have completed processing and all results are available, the status is set to 'final'. This represents the finalization of the entire laboratory response, corresponding to MedCom’s 'komplet_svar'. +* amended: If a previously finalized DiagnosticReport is updated because one or more referenced results have been corrected, the status becomes 'amended'. This ensures that receivers are aware that a new version replaces an earlier final report. There is no single corresponding code in MedCom. Nevertheless, to ensure FHIR compliance, the 'amended' code should be used as described here. + +In a MedCom context, there are several ways of showing that a report has corrected results—but only at the result level. In FHIR, these results are Observations that may be referred to in DiagnosticReport.result. + +#### Category +The category is used to classify the report. It is divided into three distinct categories: studyType, specialty, and danishSpecialty. The first two are derived directly from the specification of laboratory reports from the European Health Data Space (EHDS), and they should only be used for laboratory reports: + +* studyType: A classification of the type of tests or services performed, grouped into clinically meaningful categories. It represents a typology of test results rather than the laboratory itself. +* specialty: Describes the clinical or professional domain of the laboratory that produced the results. It reflects the specific area of laboratory expertise that executed the tests (e.g., microbiology, chemistry, or hematology). + +The third category is Danish Specialties as expressed in Clinical Speciality in SOR (Sygehusets Organisationsregister). These may be used for non-laboratory use cases as needed. + +#### Other documentation about attributes +Note that different timestamps are allowed. Match them to your specific use case. MedCom's laboratory report uses 'brevDannetTid'. In this case, DiagnosticReport.issued is recommended as the equivalent. For MedCom's 'svarTid', using DiagnosticReport.effectiveDateTime is recommended. From ce1d8e8ca9cb0d21453e2fd99b73065d9bd600f8 Mon Sep 17 00:00:00 2001 From: kirstinerosenbeck Date: Mon, 23 Mar 2026 19:21:56 +0100 Subject: [PATCH 02/11] Diagnostic report examples included, errors i DiagnosictReport.category ValueSets corrected --- input/fsh/DkCoreDiagnosticReport.fsh | 78 ++++++++++++++++++- input/fsh/DkCoreOrganization.fsh | 9 +++ input/fsh/valueSets.fsh | 11 +++ ...inition-dk-core-diagnostic-report-intro.md | 4 +- 4 files changed, 97 insertions(+), 5 deletions(-) diff --git a/input/fsh/DkCoreDiagnosticReport.fsh b/input/fsh/DkCoreDiagnosticReport.fsh index 6b948d1b9..7fbb11d79 100644 --- a/input/fsh/DkCoreDiagnosticReport.fsh +++ b/input/fsh/DkCoreDiagnosticReport.fsh @@ -2,6 +2,7 @@ Profile: DkCoreDiagnosticReport Parent: DiagnosticReport Id: dk-core-diagnostic-report Description: "dk-core profile for laboratory reports." +Title: "Danish Core Diagnostic Report Profile" * identifier 0..1 * ^short = "[DK] undersøgelsesnummer" @@ -21,16 +22,87 @@ Description: "dk-core profile for laboratory reports." * category contains studyType 0..* and specialty 0..* and - danishSpeciality 0..* + danishSpecialty 0..* * category[studyType] from LoincLabStudyTypes (required) * ^short = "The way of grouping of the test results into clinically meaningful domains (e.g. hematology study, microbiology study, etc.)" * category[specialty] from dk-core-SCTLaboratorySpecialities (required) * ^short = "The clinical domain of the laboratory performing the observation (e.g. microbiology, toxicology, chemistry)" -* category[danishSpeciality] from dk-core-practice-setting-code (required) +* category[danishSpecialty] from dk-core-practice-setting-exclude-lab-code (required) * code 1..1 * code from dk-core-LoincDiagnosticDocumentTypes * ^short = "[DK] rapportkode" * subject only Reference(DkCorePatient or Group or DkCoreLocation or Device) * performer only Reference(DkCorePractitioner or DkCoreOrganization or DkCorePractitionerRole or CareTeam) -* resultsInterpreter only Reference(DkCorePractitioner or DkCoreOrganization or DkCorePractitionerRole or CareTeam) \ No newline at end of file +* resultsInterpreter only Reference(DkCorePractitioner or DkCoreOrganization or DkCorePractitionerRole or CareTeam) +* basedOn only Reference(CarePlan or ImmunizationRecommendation or MedicationRequest or NutritionOrder or DkCorePersonServiceRequest) +* encounter only Reference(DkCoreEncounter) + +Instance: ElseHomeNursingMeasurements +InstanceOf: DkCoreDiagnosticReport +Title: "Else's TOBS measurements and urin stix" +Description: "An example of urin stix and TOBS (Danish: Tidlig opsporing af begyndende sygdom) organized as a DiagnosticReport." +Usage: #example +* status = http://hl7.org/fhir/diagnostic-report-status#final +* code = $LOINC#11506-3 "Progress note" +* subject = Reference(else) +* category[danishSpecialty] = $sct#658161000005107 "hjemmesygepleje" +* result[0] = Reference(ElseConsciousness) +* result[+] = Reference(ElseBloodPressure) +* result[+] = Reference(ElseBodyTemperature) +* result[+] = Reference(ElseHeartRate) +* result[+] = Reference(ElseRespirationRate) +* result[+] = Reference(ElsesTOBSscore) +* result[+] = Reference(ElseUrinStix) +* performer = Reference(SidselSygeplejerske) +* conclusion = "Elses lidt høje TOBS og leukocytter i urin gør det nødvendigt med lægetilsyn snarest." +* effectiveDateTime = "2023-09-12T17:45:00.000Z" + +Instance: ElseLaboratoryReport +InstanceOf: DkCoreDiagnosticReport +Title: "Else's Laboratory report using EHDS value sets" +Description: "An example of clinical chemistry report." +Usage: #example +* status = http://hl7.org/fhir/diagnostic-report-status#final +* code = $LOINC#11502-2 "Laboratory report" +* subject = Reference(else) +* category[specialty] = $sct#394596001 "klinisk biokemi" +* category[studyType] = $LOINC#18719-5 "Chemistry studies (set)" +* result[0] = Reference(ElseINR) +* result[+] = Reference(ElseKalium) +* result[+] = Reference(ElseNatrium) +* performer = Reference(KliniskBiokemiHBY) +* effectiveDateTime = "2023-09-15T10:00:00.000Z" + +Instance: ElseINR +InstanceOf: DkCoreObservation +Usage: #example +* status = #final +* subject = Reference(else) +* effectiveDateTime = "2023-09-15T09:15:00.000Z" +* code.coding = $NPU#NPU01685 "P—Coagulation, tissue factor-induced; rel.time(actual/norm;INR;IRP 67/40;proc.) = ?" +* valueQuantity.value = 1.00 +* valueQuantity.code = #1 +* valueQuantity.system = $ucum + +Instance: ElseKalium +InstanceOf: DkCoreObservation +Usage: #example +* status = #final +* subject = Reference(else) +* effectiveDateTime = "2023-09-15T09:15:00.000Z" +* code.coding = $NPU#NPU03230 "P—Potassium ion; subst.c. = ? mmol/L" +* valueQuantity.value = 3.0 +* valueQuantity.code = #mmol/L +* valueQuantity.system = $ucum + +Instance: ElseNatrium +InstanceOf: DkCoreObservation +Usage: #example +* status = #final +* subject = Reference(else) +* effectiveDateTime = "2023-09-15T09:15:00.000Z" +* code.coding = $NPU#NPU03429 "P—Sodium ion; subst.c. = ? mmol/L" +* valueQuantity.value = 150 +* valueQuantity.code = #mmol/L +* valueQuantity.system = $ucum \ No newline at end of file diff --git a/input/fsh/DkCoreOrganization.fsh b/input/fsh/DkCoreOrganization.fsh index 95a444d22..2d6b6b0a9 100644 --- a/input/fsh/DkCoreOrganization.fsh +++ b/input/fsh/DkCoreOrganization.fsh @@ -163,6 +163,15 @@ Usage: #example * identifier[0].system = "http://cvr.dk" * identifier[=].value = "64942212" +Instance: KliniskBiokemiHBY +InstanceOf: DkCoreOrganization +Title: "Laboratory instance of DkCoreOrganization" +Description: "Laboratory organization" +Usage: #example +* identifier[0].value = "275871000016006" +* identifier[=].system = "urn:oid:1.2.208.176.1.1" + + Invariant: dk-core-organization-mandatory-identifier Description: "Minimum one identifier shall be of type SOR-ID, KOMBIT-ORG-ID or CVR-ID" Severity: #error diff --git a/input/fsh/valueSets.fsh b/input/fsh/valueSets.fsh index c10731020..33cb7e1c2 100644 --- a/input/fsh/valueSets.fsh +++ b/input/fsh/valueSets.fsh @@ -898,6 +898,7 @@ Title: "DK SOR Practice Setting Code" Description: "Values used for the document metadata attribute practiceSettingCode, which is an attribute specifying the clinical specialty where the act that resulted in the document was performed (e.g., Family Practice, Laboratory, Radiology). The value set is based on a subset of the code list from the SOR lookup table 'SOR-Kliniske specialer' (https://sor.sum.dsdn.dk/lookupdata/#clinical_speciality, accessable on Sundhedsdatanettet (SDN)), which is based on SNOMED CT codes." * ^status = #active * ^experimental = false +* ^copyright = "This value set includes content from SNOMED CT, which is copyright © 2002+ International Health Terminology Standards Development Organisation (IHTSDO), and distributed by agreement between IHTSDO and HL7. Implementer use of SNOMED CT is not covered by this agreement" * $sct#773568002 * $sct#408443003 * $sct#394577000 @@ -952,3 +953,13 @@ Description: "Values used for the document metadata attribute practiceSettingCod * $sct#658201000005103 * $sct#658141000005108 * $sct#658181000005104 + +ValueSet: SorPracticeSettingCodeExcludingLab +Id: dk-core-practice-setting-exclude-lab-code +Title: "DK SOR Practice Setting Code excluding laboratory codes" +Description: "Values used for Practice Setting, as described in the SOR registry. This list excludes Laboratory Specialties ." +* ^status = #active +* ^experimental = false +* ^copyright = "This value set includes content from SNOMED CT, which is copyright © 2002+ International Health Terminology Standards Development Organisation (IHTSDO), and distributed by agreement between IHTSDO and HL7. Implementer use of SNOMED CT is not covered by this agreement" +* include codes from valueset SorPracticeSettingCode +* exclude codes from valueset SCTLaboratorySpecialities \ No newline at end of file diff --git a/input/pagecontent/StructureDefinition-dk-core-diagnostic-report-intro.md b/input/pagecontent/StructureDefinition-dk-core-diagnostic-report-intro.md index 9b1ed0098..5b8d29e58 100644 --- a/input/pagecontent/StructureDefinition-dk-core-diagnostic-report-intro.md +++ b/input/pagecontent/StructureDefinition-dk-core-diagnostic-report-intro.md @@ -37,12 +37,12 @@ The DiagnosticReport progresses through well-defined status values throughout it In a MedCom context, there are several ways of showing that a report has corrected results—but only at the result level. In FHIR, these results are Observations that may be referred to in DiagnosticReport.result. #### Category -The category is used to classify the report. It is divided into three distinct categories: studyType, specialty, and danishSpecialty. The first two are derived directly from the specification of laboratory reports from the European Health Data Space (EHDS), and they should only be used for laboratory reports: +The category is used to classify the report. It is divided into three distinct slices: studyType, specialty, and danishSpecialty. The first two are derived directly from the specification of laboratory reports from the European Health Data Space (EHDS), and they should only be used for laboratory reports: * studyType: A classification of the type of tests or services performed, grouped into clinically meaningful categories. It represents a typology of test results rather than the laboratory itself. * specialty: Describes the clinical or professional domain of the laboratory that produced the results. It reflects the specific area of laboratory expertise that executed the tests (e.g., microbiology, chemistry, or hematology). -The third category is Danish Specialties as expressed in Clinical Speciality in SOR (Sygehusets Organisationsregister). These may be used for non-laboratory use cases as needed. +The third category is Danish Specialties as expressed in Clinical Speciality in SOR (Sundhedsvæsenets Organisationsregister) excluding the Laboratory specialities since they are already expressed in the speciality-slice. These may be used for non-laboratory use cases as needed. #### Other documentation about attributes Note that different timestamps are allowed. Match them to your specific use case. MedCom's laboratory report uses 'brevDannetTid'. In this case, DiagnosticReport.issued is recommended as the equivalent. For MedCom's 'svarTid', using DiagnosticReport.effectiveDateTime is recommended. From a16e938f42a8cc25ff74e774899aa72e1bc389a7 Mon Sep 17 00:00:00 2001 From: kirstinerosenbeck Date: Mon, 23 Mar 2026 20:52:00 +0100 Subject: [PATCH 03/11] corrected binding strength on DiagnosticReport.code --- input/fsh/DkCoreDiagnosticReport.fsh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/input/fsh/DkCoreDiagnosticReport.fsh b/input/fsh/DkCoreDiagnosticReport.fsh index 7fbb11d79..ad959b8af 100644 --- a/input/fsh/DkCoreDiagnosticReport.fsh +++ b/input/fsh/DkCoreDiagnosticReport.fsh @@ -29,7 +29,7 @@ Title: "Danish Core Diagnostic Report Profile" * ^short = "The clinical domain of the laboratory performing the observation (e.g. microbiology, toxicology, chemistry)" * category[danishSpecialty] from dk-core-practice-setting-exclude-lab-code (required) * code 1..1 -* code from dk-core-LoincDiagnosticDocumentTypes +* code from dk-core-LoincDiagnosticDocumentTypes (extensible) * ^short = "[DK] rapportkode" * subject only Reference(DkCorePatient or Group or DkCoreLocation or Device) From cba2525dde8314ecd1ea2671d2284649d718ccf7 Mon Sep 17 00:00:00 2001 From: tmsMedcom <88831880+tmsMedcom@users.noreply.github.com> Date: Thu, 26 Mar 2026 08:09:13 +0100 Subject: [PATCH 04/11] Added instance of cvr naming system --- input/fsh/instances.fsh | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/input/fsh/instances.fsh b/input/fsh/instances.fsh index 569844e84..d73ea2767 100644 --- a/input/fsh/instances.fsh +++ b/input/fsh/instances.fsh @@ -41,6 +41,28 @@ Usage: #definition * uniqueId[+].type = #uri * uniqueId[=].value = "https://sundhedsdatastyrelsen.dk/da/registre-og-services/om-sor" +Instance: cvr +InstanceOf: NamingSystem +Title: "cvr" +Description: "The Central Business Register (CVR) is the state's master register for business information. The register contains information about all Danish businesses, including their name, address, industry code, and other relevant details. The CVR is maintained by the Danish Business Authority (Erhvervsstyrelsen) and is used for various purposes, including taxation, business registration, and statistical analysis." +Usage: #definition +* name = "DK Central Business Register (CVR)" +* status = #active +* kind = #identifier +* date = "2023-10-01" +* publisher = "Erhvervsstyrelsen" +* contact.name = "Erhvervsstyrelsen" +* contact.telecom.system = #url +* contact.telecom.value = "https://erst.dk" +* responsible = "Erhvervsstyrelsen" +* description = "Det Centrale Virksomhedsregister (CVR) er statens stamregister for virksomhedsoplysninger." +* jurisdiction = urn:iso:std:iso:3166#DK "Denmark" +* uniqueId[0].type = #uri +* uniqueId[0].value = "http://cvr.dk" +* uniqueId[+].type = #oid +* uniqueId[=].value = "urn:oid:1.3.184" + + Instance: HenvisningFraLageTilSygehus InstanceOf: ServiceRequest Title: "Henvisning vedr. modermærkekræft" From 816ca4888aecd9169243fc182851dcfa2a3bf951 Mon Sep 17 00:00:00 2001 From: tmsMedcom <88831880+tmsMedcom@users.noreply.github.com> Date: Thu, 26 Mar 2026 08:33:22 +0100 Subject: [PATCH 05/11] Update description in DkCoreDiagnosticReport profile --- input/fsh/DkCoreDiagnosticReport.fsh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/input/fsh/DkCoreDiagnosticReport.fsh b/input/fsh/DkCoreDiagnosticReport.fsh index ad959b8af..91f2b6a25 100644 --- a/input/fsh/DkCoreDiagnosticReport.fsh +++ b/input/fsh/DkCoreDiagnosticReport.fsh @@ -1,7 +1,7 @@ Profile: DkCoreDiagnosticReport Parent: DiagnosticReport Id: dk-core-diagnostic-report -Description: "dk-core profile for laboratory reports." +Description: "HL7 Denmark profile for diagnostic reports." Title: "Danish Core Diagnostic Report Profile" * identifier 0..1 @@ -105,4 +105,4 @@ Usage: #example * code.coding = $NPU#NPU03429 "P—Sodium ion; subst.c. = ? mmol/L" * valueQuantity.value = 150 * valueQuantity.code = #mmol/L -* valueQuantity.system = $ucum \ No newline at end of file +* valueQuantity.system = $ucum From 0450787ca4fd4e97b0dd8545fd2f2989135540c7 Mon Sep 17 00:00:00 2001 From: tmsMedcom <88831880+tmsMedcom@users.noreply.github.com> Date: Thu, 26 Mar 2026 14:23:51 +0100 Subject: [PATCH 06/11] Minor clarifications --- ...reDefinition-dk-core-diagnostic-report-intro.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/input/pagecontent/StructureDefinition-dk-core-diagnostic-report-intro.md b/input/pagecontent/StructureDefinition-dk-core-diagnostic-report-intro.md index 5b8d29e58..a6fc5cb23 100644 --- a/input/pagecontent/StructureDefinition-dk-core-diagnostic-report-intro.md +++ b/input/pagecontent/StructureDefinition-dk-core-diagnostic-report-intro.md @@ -12,18 +12,18 @@ In this case, a DiagnosticReport serves as the primary structure that links the In other use cases, the DiagnosticReport is still a coherent report of resulting observations. These use cases are typically characterized by more relaxed requirements than within laboratory medicine. #### Code -DiagnosticReport.code may hold different values and LOINC document codes are recommended. In the EHDS laboratory standard, the code 'laboratory report' is always used. A few other example codes are included in the value set to make it easier to support common Danish use cases: +DiagnosticReport.code may hold different values and LOINC document codes are recommended. In the [HL7 Europe laboratory standard](https://hl7.eu/fhir/laboratory/), the code 'laboratory report' is always used. A few other example codes are included in the value set for this profile to make it easier to support common Danish use cases: * 'personal health monitoring report' for collections of data produced by the patients themselves, typically in telehealth projects. * 'progress note' for clinical situations where a collection of observation results originates from an encounter and needs to be communicated to other actors in the health sector. Examples could be acute nursing encounters where measurements are shared with the general practitioner. It could also be a general practitioner visit or hospital encounter where a number of different observations are made and need to be shared afterwards. -Note that if a general practitioner visit originates in a report containing only laboratory results, it is recommended to use the code 'laboratory-report'. To differentiate from laboratory reports made in laboratories, use an appropriate category instead. +Note that if a general practitioner visit originates in a report containing only laboratory results, it is recommended to use the code 'laboratory-report'. To differentiate from laboratory reports made in laboratories, use an appropriate category instead e.g. ´408443003 almen lægepraksis´. #### Identifier -The identifier may contain an analysis number equivalent to the Danish 'RekvNrLab' from MedCom's RPT01. It may correspond to the Danish requisition number (NPN), but could be re-assigned at the laboratory. For correctly referencing the requisition, see DiagnosticReport.basedOn instead. +The identifier may contain an analysis number equivalent to the Danish 'RekvNrLab' from [MedCom's RPT01](https://svn.medcom.dk/svn/releases/Standarder/Det%20gode%20laboratoriesvar/). It may correspond to the Danish requisition number (NPN), but could be re-assigned at the laboratory. For correctly referencing the requisition, see DiagnosticReport.basedOn instead. #### Status Codes -The DiagnosticReport resource must contain the status of the report and uses the required FHIR ValueSet as appropriate. +The DiagnosticReport resource must contain the status of the report and uses the required FHIR ValueSet. All codes in the ValueSet may be used, but if mapping to and from the MedCom standard for laboratory results (RPT01/XRPT01), the following codes should be considered equivalent: @@ -32,12 +32,12 @@ The DiagnosticReport progresses through well-defined status values throughout it * registered: As soon as the specimen is received by the laboratory, a DiagnosticReport is created with the status 'registered'. This reflects that the order has entered the laboratory workflow and initial processing has begun. This corresponds to MedCom's 'modtaget'. * partial: The status 'partial' is used when some, but not all, Observations within the DiagnosticReport have been finalized. At least one Observation result must still be pending for the status to remain 'partial'. This corresponds to MedCom's 'delsvar'. * final: When all observations included in the DiagnosticReport have completed processing and all results are available, the status is set to 'final'. This represents the finalization of the entire laboratory response, corresponding to MedCom’s 'komplet_svar'. -* amended: If a previously finalized DiagnosticReport is updated because one or more referenced results have been corrected, the status becomes 'amended'. This ensures that receivers are aware that a new version replaces an earlier final report. There is no single corresponding code in MedCom. Nevertheless, to ensure FHIR compliance, the 'amended' code should be used as described here. +* amended: If a previously finalized DiagnosticReport is updated because one or more referenced results have been corrected, the status becomes 'amended'. This ensures that receivers are aware that a new version replaces an earlier final report. There is no single corresponding code in MedCom's laboratory standards on the entire report. Nevertheless, to ensure FHIR compliance, the 'amended' code should be used as described here. -In a MedCom context, there are several ways of showing that a report has corrected results—but only at the result level. In FHIR, these results are Observations that may be referred to in DiagnosticReport.result. +In a MedCom context, there are several ways of showing that a report has corrected results, but only at the result level. In FHIR, these results are Observations that may be referred to in DiagnosticReport.result. #### Category -The category is used to classify the report. It is divided into three distinct slices: studyType, specialty, and danishSpecialty. The first two are derived directly from the specification of laboratory reports from the European Health Data Space (EHDS), and they should only be used for laboratory reports: +The category is used to classify the report. It is divided into three distinct slices: studyType, specialty, and danishSpecialty. The first two are derived directly from the specification of laboratory reports from HL7 Europe, and they should only be used for laboratory reports: * studyType: A classification of the type of tests or services performed, grouped into clinically meaningful categories. It represents a typology of test results rather than the laboratory itself. * specialty: Describes the clinical or professional domain of the laboratory that produced the results. It reflects the specific area of laboratory expertise that executed the tests (e.g., microbiology, chemistry, or hematology). From f8f1588595a3cfd02e22a6fe4730e6a42a6cf08c Mon Sep 17 00:00:00 2001 From: kirstinerosenbeck Date: Mon, 13 Apr 2026 17:29:55 +0200 Subject: [PATCH 07/11] changes following review of DiagnosticReport, most noteworthy is cardinality 1..1 of subject and changes made to identifier. --- input/fsh/DkCoreDiagnosticReport.fsh | 15 ++------- ...inition-dk-core-diagnostic-report-intro.md | 32 ++++++++++--------- 2 files changed, 20 insertions(+), 27 deletions(-) diff --git a/input/fsh/DkCoreDiagnosticReport.fsh b/input/fsh/DkCoreDiagnosticReport.fsh index 91f2b6a25..93a177fb1 100644 --- a/input/fsh/DkCoreDiagnosticReport.fsh +++ b/input/fsh/DkCoreDiagnosticReport.fsh @@ -4,18 +4,7 @@ Id: dk-core-diagnostic-report Description: "HL7 Denmark profile for diagnostic reports." Title: "Danish Core Diagnostic Report Profile" -* identifier 0..1 - * ^short = "[DK] undersøgelsesnummer" - * system 1..1 - * system = "http://medcom/undersøgelsesnummer" (exactly) -//consider slicing - -//* basedOn 1..1 -//* basedOn only Reference(DkCoreServiceRequest) -// * ^short = "[DK] rapportBaseretPå" -* status ^short = "[DK] rapportstatus" * category 0..* - * ^short = "[DK] rapportkategori" * category ^slicing.discriminator.type = #value * ^slicing.discriminator.path = "$this" * ^slicing.rules = #open @@ -28,15 +17,17 @@ Title: "Danish Core Diagnostic Report Profile" * category[specialty] from dk-core-SCTLaboratorySpecialities (required) * ^short = "The clinical domain of the laboratory performing the observation (e.g. microbiology, toxicology, chemistry)" * category[danishSpecialty] from dk-core-practice-setting-exclude-lab-code (required) + * ^short = "The clinical domain of a Danish practise setting in which a DiagnosticReport is produced" * code 1..1 * code from dk-core-LoincDiagnosticDocumentTypes (extensible) - * ^short = "[DK] rapportkode" * subject only Reference(DkCorePatient or Group or DkCoreLocation or Device) +* subject 1..1 * performer only Reference(DkCorePractitioner or DkCoreOrganization or DkCorePractitionerRole or CareTeam) * resultsInterpreter only Reference(DkCorePractitioner or DkCoreOrganization or DkCorePractitionerRole or CareTeam) * basedOn only Reference(CarePlan or ImmunizationRecommendation or MedicationRequest or NutritionOrder or DkCorePersonServiceRequest) * encounter only Reference(DkCoreEncounter) +* result only Reference(DkCoreObservation) Instance: ElseHomeNursingMeasurements InstanceOf: DkCoreDiagnosticReport diff --git a/input/pagecontent/StructureDefinition-dk-core-diagnostic-report-intro.md b/input/pagecontent/StructureDefinition-dk-core-diagnostic-report-intro.md index a6fc5cb23..28c96433c 100644 --- a/input/pagecontent/StructureDefinition-dk-core-diagnostic-report-intro.md +++ b/input/pagecontent/StructureDefinition-dk-core-diagnostic-report-intro.md @@ -1,5 +1,5 @@ ### Scope and Usage -The DkCoreDiagnosticReport profile is used for diagnostic reports and notes communicated within the Danish healthcare sector. Several use cases exist: +The DkCoreDiagnosticReport profile is used for diagnostic reports communicated within the Danish healthcare sector. Several use cases exist: * Laboratory reports * Personal health monitoring reports @@ -9,25 +9,17 @@ The most typical is the laboratory report used to convey the laboratory’s resp In this case, a DiagnosticReport serves as the primary structure that links the requested analyses, the underlying specimens, and the resulting observations into a coherent laboratory response. -In other use cases, the DiagnosticReport is still a coherent report of resulting observations. These use cases are typically characterized by more relaxed requirements than within laboratory medicine. - -#### Code -DiagnosticReport.code may hold different values and LOINC document codes are recommended. In the [HL7 Europe laboratory standard](https://hl7.eu/fhir/laboratory/), the code 'laboratory report' is always used. A few other example codes are included in the value set for this profile to make it easier to support common Danish use cases: - -* 'personal health monitoring report' for collections of data produced by the patients themselves, typically in telehealth projects. -* 'progress note' for clinical situations where a collection of observation results originates from an encounter and needs to be communicated to other actors in the health sector. Examples could be acute nursing encounters where measurements are shared with the general practitioner. It could also be a general practitioner visit or hospital encounter where a number of different observations are made and need to be shared afterwards. - -Note that if a general practitioner visit originates in a report containing only laboratory results, it is recommended to use the code 'laboratory-report'. To differentiate from laboratory reports made in laboratories, use an appropriate category instead e.g. ´408443003 almen lægepraksis´. +In other use cases, the DiagnosticReport is still a coherent report of resulting observations. However, these use cases are typically characterized by more relaxed requirements than within laboratory medicine. #### Identifier -The identifier may contain an analysis number equivalent to the Danish 'RekvNrLab' from [MedCom's RPT01](https://svn.medcom.dk/svn/releases/Standarder/Det%20gode%20laboratoriesvar/). It may correspond to the Danish requisition number (NPN), but could be re-assigned at the laboratory. For correctly referencing the requisition, see DiagnosticReport.basedOn instead. +In Denmark, traditional laboratory reports such as [MedCom's RPT01](https://svn.medcom.dk/svn/releases/Standarder/Det%20gode%20laboratoriesvar/), repeat the requisition number in the laboratory report to ensure traceability. In FHIR, it is more consistent with the standard to use DiagnosticReport.basedOn to reference the actual requisition. As a result, HL7 Denmark recommends not repeating the requisition number as a DiagnosticReport.identifier. -#### Status Codes -The DiagnosticReport resource must contain the status of the report and uses the required FHIR ValueSet. +In Danish laboratories, a laboratory report is sometimes assigned a unique identifier that is not the requisition number. This practice is especially useful when more than one report is associated with a single requisition. This is an example of a business identifier associated with the report itself. As such, it could be relevant to express as a DiagnosticReport.identifier. -All codes in the ValueSet may be used, but if mapping to and from the MedCom standard for laboratory results (RPT01/XRPT01), the following codes should be considered equivalent: +#### Status Codes +The DiagnosticReport resource must contain the status of the report and use the required FHIR ValueSet. For all new use cases this is the only requirement. -The DiagnosticReport progresses through well-defined status values throughout its lifecycle. +However, if legacy data requires a mapping to and from the MedCom standard for laboratory results (RPT01/XRPT01), the following codes should be considered equivalent: * registered: As soon as the specimen is received by the laboratory, a DiagnosticReport is created with the status 'registered'. This reflects that the order has entered the laboratory workflow and initial processing has begun. This corresponds to MedCom's 'modtaget'. * partial: The status 'partial' is used when some, but not all, Observations within the DiagnosticReport have been finalized. At least one Observation result must still be pending for the status to remain 'partial'. This corresponds to MedCom's 'delsvar'. @@ -44,5 +36,15 @@ The category is used to classify the report. It is divided into three distinct s The third category is Danish Specialties as expressed in Clinical Speciality in SOR (Sundhedsvæsenets Organisationsregister) excluding the Laboratory specialities since they are already expressed in the speciality-slice. These may be used for non-laboratory use cases as needed. +#### Code +DiagnosticReport.code may hold different values. In the [HL7 Europe laboratory standard](https://hl7.eu/fhir/laboratory/), the code 'laboratory report' is always used. To support common Danish use cases, the value set includes two more valid values: + +* 'personal health monitoring report' for collections of data produced by the patients themselves, typically in telehealth projects. +* 'progress note' for clinical situations where a collection of observation results originates from an encounter and needs to be communicated to other actors in the health sector. Examples could be acute nursing encounters where measurements are shared with the general practitioner. It could also be a general practitioner visit or hospital encounter where a number of different observations are made and need to be shared afterwards. + +The value set is extensible. This means that if a specific use case can make use of either of the three codes already included in the value set, they shall be used. However, if a use case cannot meaningfully use either of the three cades, the value set may be extended. To ensure consistency, HL7 Denmark recommends using LOINC document codes for such additional codes. + +Note that if a general practitioner visit originates in a report containing only laboratory results, it is recommended to use the code 'laboratory-report'. To differentiate from laboratory reports made in laboratories, use an appropriate category instead e.g. ´408443003 almen lægepraksis´. + #### Other documentation about attributes Note that different timestamps are allowed. Match them to your specific use case. MedCom's laboratory report uses 'brevDannetTid'. In this case, DiagnosticReport.issued is recommended as the equivalent. For MedCom's 'svarTid', using DiagnosticReport.effectiveDateTime is recommended. From 3c9a10fb8db009f7e2a84bdfbe411ef6b1daee44 Mon Sep 17 00:00:00 2001 From: Jens Kristian Villadsen Date: Tue, 21 Apr 2026 20:32:06 +0200 Subject: [PATCH 08/11] removing deprecated content --- input/fsh/aliases.fsh | 1 - input/fsh/codeSystems.fsh | 23 ----------------------- input/fsh/valueSets.fsh | 7 ------- 3 files changed, 31 deletions(-) diff --git a/input/fsh/aliases.fsh b/input/fsh/aliases.fsh index 0ff32aaa1..93b051260 100644 --- a/input/fsh/aliases.fsh +++ b/input/fsh/aliases.fsh @@ -6,7 +6,6 @@ Alias: $condition-category = http://terminology.hl7.org/CodeSystem/condition-cat Alias: $condition-ver-status = http://terminology.hl7.org/CodeSystem/condition-ver-status Alias: $v2-0203 = http://terminology.hl7.org/CodeSystem/v2-0203 Alias: $dk-core-municipality-codes = http://hl7.dk/fhir/core/CodeSystem/dk-core-municipality-codes -Alias: $dk-core-regional-subdivision-codes = http://hl7.dk/fhir/core/CodeSystem/dk-core-regional-subdivision-codes Alias: $iso3166-2 = urn:iso:std:iso:3166:-2 Alias: $v3-MaritalStatus = http://terminology.hl7.org/CodeSystem/v3-MaritalStatus Alias: $dk-marital-status = http://hl7.dk/fhir/core/CodeSystem/dk-marital-status diff --git a/input/fsh/codeSystems.fsh b/input/fsh/codeSystems.fsh index 1917594fc..746a38b7c 100644 --- a/input/fsh/codeSystems.fsh +++ b/input/fsh/codeSystems.fsh @@ -428,29 +428,6 @@ Description: "Municipality codes as defined by https://danmarksadresser.dk/adres * ^designation.language = #da * ^designation.value = "Vejle" -CodeSystem: DKCoreRegionalSubdivisionCodes -Title: "DK Regional Subdivision Codes" -Id: dk-core-regional-subdivision-codes -Description: "Subdivision codes (Regional codes) used in Denmark as found on https://www.iso.org/obp/ui/#iso:code:3166:DK" -* ^experimental = false -* ^caseSensitive = false -* ^content = #complete -* #DK-84 "Capital Region of Denmark" - * ^designation.language = #da - * ^designation.value = "Hovedstaden" -* #DK-82 "Central Denmark Region" - * ^designation.language = #da - * ^designation.value = "Midtjylland" -* #DK-81 "Nord Denmark Region" - * ^designation.language = #da - * ^designation.value = "Nordjylland" -* #DK-85 "Region Zealand" - * ^designation.language = #da - * ^designation.value = "Sjælland" -* #DK-83 "Region of Southern Denmark" - * ^designation.language = #da - * ^designation.value = "Syddanmark" - CodeSystem: DK_Marital_Status_Codes Id: dk-marital-status Title: "DK Marital Statuses" diff --git a/input/fsh/valueSets.fsh b/input/fsh/valueSets.fsh index 33cb7e1c2..8d7dc3570 100644 --- a/input/fsh/valueSets.fsh +++ b/input/fsh/valueSets.fsh @@ -354,13 +354,6 @@ Description: "DK Related Person Relationship Types" // * include codes from system MunicipalityCodes // * include codes from system GreenlandMunicipalityCodes -// ValueSet: RegionalSubdivisionCodes -// Id: dk-core-RegionalSubDivisionCodes -// Title: "DK Regional Subdivision Codes" -// Description: "Subdivision codes (Regional codes) used in Denmark" -// * ^experimental = false -// * include codes from system DKCoreRegionalSubdivisionCodes - // ValueSet: LoincBasicObservation // Id: dk-core-LoincBasicObservation // Title: "DK Core LOINC Basic Observations" From 87a0ca9c5975671ff3f3adcba2b6a3b290956af3 Mon Sep 17 00:00:00 2001 From: Jacob Andersen Date: Thu, 23 Apr 2026 19:34:27 +0200 Subject: [PATCH 09/11] Upgraded PHD dependency to 2.0.0 --- input/fsh/DkCoreObservation.fsh | 59 +++++++++++++++++++++------------ sushi-config.yaml | 2 +- 2 files changed, 38 insertions(+), 23 deletions(-) diff --git a/input/fsh/DkCoreObservation.fsh b/input/fsh/DkCoreObservation.fsh index 89dd6a79f..2f45f452d 100644 --- a/input/fsh/DkCoreObservation.fsh +++ b/input/fsh/DkCoreObservation.fsh @@ -203,14 +203,15 @@ Alias: $PhdBitsEnumerationObservation = http://hl7.org/fhir/uv/phd/StructureDefi Alias: $PhdCoincidentTimeStampObservation = http://hl7.org/fhir/uv/phd/StructureDefinition/PhdCoincidentTimeStampObservation Alias: $EUI64 = urn:oid:1.2.840.10004.1.1.1.0.0.1.0.0.1.2680 Alias: $BD_ADDR = http://hl7.org/fhir/sid/eui-48/bluetooth -Alias: $ContinuaDeviceIdentifiers = http://hl7.org/fhir/uv/phd/CodeSystem/ContinuaDeviceIdentifiers -Alias: $ContinuaPHD = http://hl7.org/fhir/uv/phd/CodeSystem/ContinuaPHD +Alias: $ContinuaDeviceIdentifiers = http://terminology.hl7.org/CodeSystem/ContinuaDeviceIdentifiers +Alias: $ContinuaPHD = http://hl7.org/fhir/uv/phd/CodeSystem/ContinuaPHDInterfaceIDs Alias: $ContinuaHFS = http://hl7.org/fhir/uv/phd/CodeSystem/ContinuaHFS Alias: $PhdObservationCategory = http://hl7.org/fhir/uv/phd/CodeSystem/PhdObservationCategories Alias: $observation-gatewayDevice = http://hl7.org/fhir/StructureDefinition/observation-gatewayDevice +Alias: $CoincidentTimeStampReference = http://hl7.org/fhir/uv/phd/StructureDefinition/CoincidentTimeStampReference Alias: $v2-0136 = http://terminology.hl7.org/CodeSystem/v2-0136 Alias: $v2-0203 = http://terminology.hl7.org/CodeSystem/v2-0203 -Alias: $ASN1ToHL7 = http://hl7.org/fhir/uv/phd/CodeSystem/ASN1ToHL7 +Alias: $ASN1ToHL7 = http://terminology.hl7.org/CodeSystem/ASN1ToHL7 Instance: Poul @@ -321,7 +322,7 @@ Usage: #example * meta.profile[+] = $observation-bodyweight * status = #final * category[+].coding = $observation-category#vital-signs "Vital signs" -* category[+].coding = $PhdObservationCategory#phd-observation "PHD generated Observation" +* category[+].coding = $PhdObservationCategory#phd "PHD generated Observation" * code * coding[+] = $LOINC#29463-7 "Body weight" * coding[+] = $IEEEx73#188736 "MDC_MASS_BODY_ACTUAL" @@ -363,6 +364,11 @@ Usage: #example * type * coding = $IEEEx73#531981 "MDC_MOC_VMS_MDS_AHD" * text = "MDC_MOC_VMS_MDS_AHD: Continua compliant gateway" +* specialization + * systemType + * coding = $IEEEx73#528457 "MDC_DEV_SPEC_PROFILE_GENERIC" + * text = "MDC_DEV_SPEC_PROFILE_GENERIC: Device following the IEEE 11073-10206 standard but no specific specialization" + * version = "2" * version[+] * type * coding = $IEEEx73#532352 "MDC_REG_CERT_DATA_CONTINUA_VERSION" @@ -390,9 +396,17 @@ Usage: #example * valueCode[+] * coding = $ContinuaPHD#32783 * text = "BluetoothLE: Weighing scale" +* property[+] + * type + * coding = $IEEEx73#532353 "MDC_REG_CERT_DATA_CONTINUA_CERT_DEV_LIST" + * text = "MDC_REG_CERT_DATA_CONTINUA_CERT_DEV_LIST: Continua certified device list" * valueCode[+] * coding = $ContinuaPHD#32775 * text = "BluetoothLE: Blood pressure monitor" +* property[+] + * type + * coding = $IEEEx73#532353 "MDC_REG_CERT_DATA_CONTINUA_CERT_DEV_LIST" + * text = "MDC_REG_CERT_DATA_CONTINUA_CERT_DEV_LIST: Continua certified device list" * valueCode[+] * coding = $ContinuaPHD#32772 * text = "BluetoothLE: Pulse oximeter" @@ -508,7 +522,7 @@ InstanceOf: DkCoreObservation Usage: #inline * meta.profile[+] = $PhdNumericObservation * status = #final -* category[+].coding = $PhdObservationCategory#phd-observation "PHD generated Observation" +* category[+].coding = $PhdObservationCategory#phd "PHD generated Observation" * code * coding[+] = $IEEEx73#67996 "MDC_ATTR_VAL_BATT_CHARGE" * text = "MDC_ATTR_VAL_BATT_CHARGE: Battery level" @@ -537,14 +551,11 @@ Usage: #example * code * coding[+] = $IEEEx73#67975 "MDC_ATTR_TIME_ABS" * text = "MDC_ATTR_TIME_ABS: Uses Absolute time clock" -* subject = Reference(Poul) +* subject = Reference(BPMonitor.C4F312FFFE53F2C9) * performer = Reference(Poul) * effectiveDateTime = "2023-02-23T10:24:34.467+01:00" * valueDateTime = "2023-02-23T10:24:25+01:00" -* device = Reference(BPMonitor.C4F312FFFE53F2C9) -* extension - * url = $observation-gatewayDevice - * valueReference = Reference(Telma.FEEDDADADEADBEEF) +* device = Reference(Telma.FEEDDADADEADBEEF) Instance: BloodPressure.Poul.643992 @@ -556,7 +567,7 @@ Usage: #inline * meta.profile[+] = $observation-bp * status = #final * category[+].coding = $observation-category#vital-signs "Vital signs" -* category[+].coding = $PhdObservationCategory#phd-observation "PHD generated Observation" +* category[+].coding = $PhdObservationCategory#phd "PHD generated Observation" * code * coding[+] = $LOINC#85354-9 "Blood pressure panel with all children optional" * coding[+] = $IEEEx73#150020 "MDC_PRESS_BLD_NONINV" @@ -583,10 +594,12 @@ Usage: #inline * text = "MDC_PRESS_BLD_NONINV_MEAN: Mean Blood Pressure" * valueQuantity = 99 'mm[Hg]' "mmHg" * device = Reference(BPMonitor.C4F312FFFE53F2C9) -* extension +* extension[+] * url = $observation-gatewayDevice * valueReference = Reference(Telma.FEEDDADADEADBEEF) -* derivedFrom = Reference(CoincidentTimeStamp.0222) +* extension[+] + * url = $CoincidentTimeStampReference + * valueReference = Reference(CoincidentTimeStamp.0222) Instance: HeartRate.Poul.1974654 @@ -603,7 +616,7 @@ Usage: #example * meta.profile[+] = $observation-heartrate * status = #final * category[+].coding = $observation-category#vital-signs "Vital signs" -* category[+].coding = $PhdObservationCategory#phd-observation "PHD generated Observation" +* category[+].coding = $PhdObservationCategory#phd "PHD generated Observation" * code * coding[+] = $LOINC#8867-4 "Heart rate" * coding[+] = $IEEEx73#149546 "MDC_PULS_RATE_NON_INV" @@ -614,10 +627,12 @@ Usage: #example * effectiveDateTime = "2023-02-23T10:24:08+01:00" * valueQuantity = 93 '/min' "bpm" * device = Reference(BPMonitor.C4F312FFFE53F2C9) -* extension +* extension[+] * url = $observation-gatewayDevice * valueReference = Reference(Telma.FEEDDADADEADBEEF) -* derivedFrom = Reference(CoincidentTimeStamp.0222) +* extension[+] + * url = $CoincidentTimeStampReference + * valueReference = Reference(CoincidentTimeStamp.0222) Instance: BloodPressureStatus.Poul.133527 @@ -626,7 +641,7 @@ Usage: #inline * identifier.value = "C4F312FFFE53F2C9-3001749995-urn:oid:1.2.208.176.1.2-8410608-8192-20230223T102408.00" * meta.profile[+] = $PhdBitsEnumerationObservation * status = #final -* category[+].coding = $PhdObservationCategory#phd-observation "PHD generated Observation" +* category[+].coding = $PhdObservationCategory#phd "PHD generated Observation" * code * coding = $IEEEx73#8410608 "MDC_BLOOD_PRESSURE_MEASUREMENT_STATUS" * text = "MDC_BLOOD_PRESSURE_MEASUREMENT_STATUS: Blood Pressure measurement problem" @@ -637,14 +652,14 @@ Usage: #inline * code * coding = $ASN1ToHL7#8410608.2 * text = "irregular-pulse" - * valueCodeableConcept - * coding = $v2-0136#Y - * text = "Irregular pulse was detected" + * valueBoolean = true * device = Reference(BPMonitor.C4F312FFFE53F2C9) -* extension +* extension[+] * url = $observation-gatewayDevice * valueReference = Reference(Telma.FEEDDADADEADBEEF) -* derivedFrom[+] = Reference(CoincidentTimeStamp.0222) +* extension[+] + * url = $CoincidentTimeStampReference + * valueReference = Reference(CoincidentTimeStamp.0222) * derivedFrom[+] = Reference(BloodPressure.Poul.643992) diff --git a/sushi-config.yaml b/sushi-config.yaml index fcee485e1..b9ef17795 100644 --- a/sushi-config.yaml +++ b/sushi-config.yaml @@ -46,7 +46,7 @@ dependencies: hl7.fhir.uv.extensions.r4: version: 5.3.0-ballot-tc1 uri: http://hl7.org/fhir/extensions/ImplementationGuide/hl7.fhir.uv.extensions - hl7.fhir.uv.phd: 1.1.0 + hl7.fhir.uv.phd: 2.0.0 hl7.fhir.uv.ipa: 1.1.0 ihe.iti.mhd: 4.2.3 From 1e8e30bc5b8bb71ea75cbb3cc7da44608503727d Mon Sep 17 00:00:00 2001 From: Jens Kristian Villadsen Date: Sat, 30 May 2026 12:00:34 +0200 Subject: [PATCH 10/11] fixing translations --- _build.bat | 123 +++++++++---------- _build.sh | 204 +++++++++++++++++++------------- _genonce.bat | 3 +- _updatePublisher.bat | 3 +- input/fsh/DkCoreCondition.fsh | 2 +- input/fsh/DkCoreObservation.fsh | 2 +- 6 files changed, 192 insertions(+), 145 deletions(-) diff --git a/_build.bat b/_build.bat index 99d0e1f97..903d207a7 100644 --- a/_build.bat +++ b/_build.bat @@ -25,38 +25,55 @@ IF EXIST "%input_cache_path%%publisher_jar%" ( ) ELSE ( SET "jar_location=not_found" SET "default_choice=1" + SET "default_reason=publisher not found" ECHO publisher.jar not found in input-cache or parent folder ) ) :: Handle command-line argument to bypass the menu -IF NOT "%~1"=="" ( - IF /I "%~1"=="update" SET "userChoice=1" - IF /I "%~1"=="build" SET "userChoice=2" - IF /I "%~1"=="nosushi" SET "userChoice=3" - IF /I "%~1"=="notx" SET "userChoice=4" - IF /I "%~1"=="jekyll" SET "userChoice=5" - IF /I "%~1"=="clean" SET "userChoice=6" - IF /I "%~1"=="exit" SET "userChoice=0" - GOTO executeChoice -) +:: Known first arguments select a menu option; anything else is passed through to the publisher +SET "extraArgs=" +IF "%~1"=="" GOTO showMenu +IF /I "%~1"=="update" SET "userChoice=1" & GOTO parseExtra +IF /I "%~1"=="build" SET "userChoice=2" & GOTO parseExtra +IF /I "%~1"=="nosushi" SET "userChoice=3" & GOTO parseExtra +IF /I "%~1"=="notx" SET "userChoice=4" & GOTO parseExtra +IF /I "%~1"=="jekyll" SET "userChoice=5" & GOTO parseExtra +IF /I "%~1"=="clean" SET "userChoice=6" & GOTO parseExtra +IF /I "%~1"=="exit" SET "userChoice=0" & GOTO parseExtra +:: Unknown first arg - default to build, pass all args through +SET "userChoice=2" +GOTO collectArgs +:parseExtra +SHIFT +:collectArgs +IF "%~1"=="" GOTO executeChoice +SET "extraArgs=!extraArgs! %1" +SHIFT +GOTO collectArgs +:showMenu echo --------------------------------------------------------------- ECHO Checking internet connection... -PING tx.fhir.org -4 -n 1 -w 4000 >nul 2>&1 && SET "online_status=true" || SET "online_status=false" +powershell -Command "try { $r=[System.Net.WebRequest]::Create('https://tx.fhir.org/r4/metadata'); $r.Timeout=4000; $r.GetResponse().Close(); exit 0 } catch { exit 1 }" +IF %ERRORLEVEL% EQU 0 (SET "online_status=true") ELSE (SET "online_status=false") IF "%online_status%"=="true" ( - ECHO We're online and tx.fhir.org is available. + ECHO We are online and tx.fhir.org is available. FOR /F "tokens=2 delims=:" %%a IN ('curl -s https://api.github.com/repos/HL7/fhir-ig-publisher/releases/latest ^| findstr "tag_name"') DO SET "latest_version=%%a" SET "latest_version=!latest_version:"=!" SET "latest_version=!latest_version: =!" SET "latest_version=!latest_version:~0,-1!" ) ELSE ( - ECHO We're offline or tx.fhir.org is not available, can only run the publisher without TX... + ECHO. + ECHO *** WARNING: Working offline - this is not the normal mode. + ECHO Some features including terminology rendering will not work. + ECHO. SET "txoption=-tx n/a" SET "latest_version=unknown" SET "default_choice=4" + SET "default_reason=working offline" ) echo --------------------------------------------------------------- @@ -74,14 +91,16 @@ IF NOT "%jar_location%"=="not_found" ( ECHO Publisher version: !publisher_version!; Latest is !latest_version! IF NOT "%online_status%"=="true" ( - ECHO We're offline. + ECHO We are offline. ) ELSE ( IF NOT "!publisher_version!"=="!latest_version!" ( ECHO An update is recommended. SET "default_choice=1" + SET "default_reason=newer version available" ) ELSE ( ECHO Publisher is up to date. SET "default_choice=2" + SET "default_reason=publisher is up to date" ) ) @@ -96,12 +115,9 @@ echo 4. Build IG - force no TX server echo 5. Jekyll build echo 6. Clean up temp directories echo 0. Exit -:: echo [Press Enter for default (%default_choice%) or type an option number:] echo. -:: Using CHOICE to handle input with timeout -:: ECHO [Enter=Continue, 1-7=Option, 0=Exit] -choice /C 12345670 /N /CS /D %default_choice% /T 5 /M "Choose an option number or wait 5 seconds for default (%default_choice%):" +choice /C 12345670 /N /CS /D %default_choice% /T 5 /M "Choose an option number or wait 5 seconds for default (%default_choice% - %default_reason%):" SET "userChoice=%ERRORLEVEL%" @@ -115,15 +131,12 @@ IF "%userChoice%"=="4" GOTO publish_notx IF "%userChoice%"=="5" GOTO debugjekyll IF "%userChoice%"=="6" GOTO clean IF "%userChoice%"=="0" EXIT /B - -:end - - +GOTO endscript :debugjekyll echo Running Jekyll build... jekyll build -s temp/pages -d output -GOTO end +GOTO endscript :clean @@ -152,10 +165,7 @@ GOTO end echo Removed: .\template ) -GOTO end - - - +GOTO endscript :downloadpublisher @@ -198,7 +208,7 @@ IF DEFINED FORCE ( GOTO download ) -IF "%skipPrompts%"=="y" ( +IF "%skipPrompts%"=="true" ( SET create=Y ) ELSE ( SET /p create="Download? (Y/N) " @@ -211,7 +221,7 @@ IF /I "%create%"=="Y" ( GOTO done :upgrade -IF "%skipPrompts%"=="y" ( +IF "%skipPrompts%"=="true" ( SET overwrite=Y ) ELSE ( SET /p overwrite="Overwrite %jarlocation%? (Y/N) " @@ -265,7 +275,7 @@ GOTO done ECHO. ECHO Updating scripts -IF "%skipPrompts%"=="y" ( +IF "%skipPrompts%"=="true" ( SET updateScripts=Y ) ELSE ( SET /p updateScripts="Update scripts? (Y/N) " @@ -273,7 +283,7 @@ IF "%skipPrompts%"=="y" ( IF /I "%updateScripts%"=="Y" ( GOTO scripts ) -GOTO end +GOTO endscript :scripts @@ -299,12 +309,12 @@ ECHO Updating _build.bat call POWERSHELL -command if ('System.Net.WebClient' -as [type]) {(new-object System.Net.WebClient).DownloadFile(\"%build_bat_url%\",\"_build.new.bat\") } else { Invoke-WebRequest -Uri "%build_bat_url%" -Outfile "_build.new.bat" } if %ERRORLEVEL% == 0 goto upd_script_2 echo "Errors encountered during download: %errorlevel%" -goto end +goto endscript :upd_script_2 start copy /y "_build.new.bat" "_build.bat" ^&^& del "_build.new.bat" ^&^& exit -GOTO end +GOTO endscript :publish_once @@ -312,14 +322,15 @@ GOTO end SET JAVA_TOOL_OPTIONS=-Dfile.encoding=UTF-8 :: Debugging statements before running publisher -ECHO 1jar_location is: %jar_location% +ECHO jar_location is: %jar_location% IF NOT "%jar_location%"=="not_found" ( - java %JAVA_OPTS% -jar "%jar_location%" -ig . %txoption% %* + ECHO IG Publisher FOUND, Publishing... + java %JAVA_OPTS% -jar "%jar_location%" -ig . %txoption% %extraArgs% ) ELSE ( - ECHO IG Publisher NOT FOUND in input-cache or parent folder. Please run _updatePublisher. Aborting... + ECHO IG Publisher NOT FOUND in input-cache or parent folder. Please run the script and update the publisher. Aborting... ) -GOTO end +GOTO endscript @@ -328,14 +339,14 @@ GOTO end SET JAVA_TOOL_OPTIONS=-Dfile.encoding=UTF-8 :: Debugging statements before running publisher -ECHO 3jar_location is: %jar_location% +ECHO jar_location is: %jar_location% IF NOT "%jar_location%"=="not_found" ( - java %JAVA_OPTS% -jar "%jar_location%" -ig . %txoption% -no-sushi %* + java %JAVA_OPTS% -jar "%jar_location%" -ig . %txoption% -no-sushi %extraArgs% ) ELSE ( - ECHO IG Publisher NOT FOUND in input-cache or parent folder. Please run _updatePublisher. Aborting... + ECHO IG Publisher NOT FOUND in input-cache or parent folder. Please run the script and update the publisher. Aborting... ) -GOTO end +GOTO endscript :publish_notx @@ -344,43 +355,33 @@ SET txoption=-tx n/a SET JAVA_TOOL_OPTIONS=-Dfile.encoding=UTF-8 :: Debugging statements before running publisher -ECHO 2jar_location is: %jar_location% +ECHO jar_location is: %jar_location% IF NOT "%jar_location%"=="not_found" ( - java %JAVA_OPTS% -jar "%jar_location%" -ig . %txoption% %* + java %JAVA_OPTS% -jar "%jar_location%" -ig . %txoption% %extraArgs% ) ELSE ( - ECHO IG Publisher NOT FOUND in input-cache or parent folder. Please run _updatePublisher. Aborting... + ECHO IG Publisher NOT FOUND in input-cache or parent folder. Please run the script and update the publisher. Aborting... ) -GOTO end - - +GOTO endscript :publish_continuous SET JAVA_TOOL_OPTIONS=-Dfile.encoding=UTF-8 -:: Debugging statements before running publisher -ECHO Checking %input_cache_path% for publisher.jar -IF EXIST "%input_cache_path%\%publisher_jar%" ( - java %JAVA_OPTS% -jar "%input_cache_path%\%publisher_jar%" -ig . %txoption% -watch %* +ECHO jar_location is: %jar_location% +IF NOT "%jar_location%"=="not_found" ( + java %JAVA_OPTS% -jar "%jar_location%" -ig . %txoption% -watch %extraArgs% ) ELSE ( - ECHO Checking %upper_path% for publisher.jar - IF EXIST "..\%publisher_jar%" ( - java %JAVA_OPTS% -jar "..\%publisher_jar%" -ig . %txoption% -watch %* - ) ELSE ( - ECHO IG Publisher NOT FOUND in input-cache or parent folder. Please run _updatePublisher. Aborting... - ) + ECHO IG Publisher NOT FOUND in input-cache or parent folder. Please run the script and update the publisher. Aborting... ) -GOTO end +GOTO endscript -:end +:endscript :: Pausing at the end - - IF NOT "%skipPrompts%"=="true" ( PAUSE ) diff --git a/_build.sh b/_build.sh index f11edff67..69ac585cf 100755 --- a/_build.sh +++ b/_build.sh @@ -26,20 +26,44 @@ function check_jar_location() { } function check_internet_connection() { - if ping -c 1 tx.fhir.org &>/dev/null; then + local target="tx.fhir.org" + local reachable=false + + if command -v ping > /dev/null 2>&1; then + if ping -c 1 -W 5 "$target" > /dev/null 2>&1 \ + || ping -c 1 -w 5 "$target" > /dev/null 2>&1; then + reachable=true + fi + elif command -v curl > /dev/null 2>&1; then + if curl --silent --max-time 5 --output /dev/null "https://$target"; then + reachable=true + fi + else + echo "WARNING: Neither ping nor curl available — assuming offline." + fi + + if [ "$reachable" = "true" ]; then online=true - echo "We're online and tx.fhir.org is available." + echo "We're online and $target is available." latest_version=$(curl -s https://api.github.com/repos/HL7/fhir-ig-publisher/releases/latest | grep tag_name | cut -d'"' -f4) else online=false - echo "We're offline or tx.fhir.org is unavailable." + echo "" + echo "⚠️ WARNING: Working offline — this is not the normal mode." + echo " Some features (e.g. terminology rendering) will not work." + echo "" fi } + function update_publisher() { echo "Publisher jar location: ${input_cache_path}${publisher_jar}" - read -p "Download or update publisher.jar? (Y/N): " confirm + if [ "$skipPrompts" = "true" ]; then + confirm="Y" + else + read -p "Download or update publisher.jar? (Y/N): " confirm + fi if [[ "$confirm" =~ ^[Yy]$ ]]; then echo "Downloading latest publisher.jar (~200 MB)..." mkdir -p "$input_cache_path" @@ -53,7 +77,11 @@ function update_publisher() { function update_scripts_prompt() { - read -p "Update scripts (_build.bat and _build.sh)? (Y/N): " update_confirm + if [ "$skipPrompts" = "true" ]; then + update_confirm="Y" + else + read -p "Update scripts (_build.bat and _build.sh)? (Y/N): " update_confirm + fi if [[ "$update_confirm" =~ ^[Yy]$ ]]; then echo "Updating scripts..." curl -L "$build_bat_url" -o "_build.new.bat" && mv "_build.new.bat" "_build.bat" @@ -66,33 +94,35 @@ function update_scripts_prompt() { } -function build_ig() { +function run_publisher() { + local extra_flags=("$@") if [ "$jar_location" != "not_found" ]; then - args=() - if [ "$online" = "false" ]; then - args+=("-tx" "n/a") - fi - java -Dfile.encoding=UTF-8 -jar "$jar_location" -ig . "${args[@]}" "$@" + echo "jar_location is: $jar_location" + export JAVA_TOOL_OPTIONS="-Dfile.encoding=UTF-8" + java $JAVA_OPTS -jar "$jar_location" -ig . "${extra_flags[@]}" else - echo "publisher.jar not found. Please run update." + echo "IG Publisher NOT FOUND in input-cache or parent folder. Please run update. Aborting..." fi } +function build_ig() { + local args=() + if [ "$online" = "false" ]; then + args+=("-tx" "n/a") + fi + run_publisher "${args[@]}" "$@" +} function build_nosushi() { - if [ "$jar_location" != "not_found" ]; then - java -Dfile.encoding=UTF-8 -jar "$jar_location" -ig . -no-sushi "$@" - else - echo "publisher.jar not found. Please run update." - fi + run_publisher -no-sushi "$@" } function build_notx() { - if [ "$jar_location" != "not_found" ]; then - java -Dfile.encoding=UTF-8 -jar "$jar_location" -ig . -tx n/a "$@" - else - echo "publisher.jar not found. Please run update." - fi + run_publisher -tx n/a "$@" +} + +function build_continuous() { + run_publisher -watch "$@" } function jekyll_build() { @@ -113,64 +143,78 @@ function cleanup() { } check_jar_location -check_internet_connection -# Handle command-line argument or menu -case "$1" in - update) update_publisher ;; - build) build_ig ;; - nosushi) build_nosushi ;; - notx) build_notx ;; - jekyll) jekyll_build ;; - clean) cleanup ;; - exit) exit 0 ;; - *) - # Compute default choice - default_choice=2 # Build by default - - if [ "$jar_location" = "not_found" ]; then - default_choice=1 # Download if jar is missing - elif [ "$online" = "false" ]; then - default_choice=4 # Offline build - elif [ -n "$latest_version" ]; then - current_version=$(java -jar "$jar_location" -v 2>/dev/null | tr -d '\r') - if [ "$current_version" != "$latest_version" ]; then - default_choice=1 # Offer update if newer version exists - fi - fi - - echo "---------------------------------------------" - echo "Publisher: ${current_version:-unknown}; Latest: ${latest_version:-unknown}" - echo "Publisher location: $jar_location" - echo "Online: $online" - echo "---------------------------------------------" - echo - echo "Please select an option:" - echo "1) Download or update publisher" - echo "2) Build IG" - echo "3) Build IG without Sushi" - echo "4) Build IG without TX server" - echo "5) Jekyll build" - echo "6) Cleanup temp directories" - echo "0) Exit" - echo - - # Read with timeout, but default if nothing entered - echo -n "Choose an option [default: $default_choice]: " - read -t 5 choice || choice="$default_choice" - choice="${choice:-$default_choice}" - echo "You selected: $choice" - - case "$choice" in - 1) update_publisher ;; - 2) build_ig ;; - 3) build_nosushi ;; - 4) build_notx ;; - 5) jekyll_build ;; - 6) cleanup ;; - 0) exit 0 ;; - *) echo "Invalid option." ;; - esac - ;; +# Handle command-line arguments +# Known first arguments select a menu option; anything else is passed through to the publisher +extraArgs=() +if [ $# -gt 0 ]; then + case "$1" in + update) shift; extraArgs=("$@"); update_publisher; exit 0 ;; + build) shift; extraArgs=("$@"); check_internet_connection; build_ig "${extraArgs[@]}"; exit 0 ;; + nosushi) shift; extraArgs=("$@"); check_internet_connection; build_nosushi "${extraArgs[@]}"; exit 0 ;; + notx) shift; extraArgs=("$@"); build_notx "${extraArgs[@]}"; exit 0 ;; + jekyll) jekyll_build; exit 0 ;; + clean) cleanup; exit 0 ;; + exit) exit 0 ;; + *) + # Unknown first arg - default to build, pass all args through + extraArgs=("$@") + run_publisher "${extraArgs[@]}" + exit 0 + ;; + esac +fi + +# Interactive menu +check_internet_connection +# Compute default choice and reason +default_choice=2 +default_reason="publisher is up to date" + +if [ "$jar_location" = "not_found" ]; then + default_choice=1 + default_reason="publisher not found" +elif [ "$online" = "false" ]; then + default_choice=4 + default_reason="working offline" +elif [ -n "$latest_version" ]; then + current_version=$(java -jar "$jar_location" -v 2>/dev/null | tr -d '\r') + if [ "$current_version" != "$latest_version" ]; then + default_choice=1 + default_reason="newer version available" + fi +fi + +echo "---------------------------------------------" +echo "Publisher: ${current_version:-unknown}; Latest: ${latest_version:-unknown}" +echo "Publisher location: $jar_location" +echo "Online: $online" +echo "---------------------------------------------" +echo +echo "Please select an option:" +echo "1) Download or update publisher" +echo "2) Build IG" +echo "3) Build IG without Sushi" +echo "4) Build IG without TX server" +echo "5) Jekyll build" +echo "6) Cleanup temp directories" +echo "0) Exit" +echo + +# Read with timeout, but default if nothing entered +echo -n "Choose an option [default: $default_choice - $default_reason]: " +read -t 5 choice || choice="$default_choice" +choice="${choice:-$default_choice}" +echo "You selected: $choice" + +case "$choice" in + 1) update_publisher ;; + 2) build_ig ;; + 3) build_nosushi ;; + 4) build_notx ;; + 5) jekyll_build ;; + 6) cleanup ;; + 0) exit 0 ;; + *) echo "Invalid option." ;; esac diff --git a/_genonce.bat b/_genonce.bat index a9864ef10..c477ba8fe 100644 --- a/_genonce.bat +++ b/_genonce.bat @@ -3,7 +3,8 @@ SET publisher_jar=publisher.jar SET input_cache_path=%CD%\input-cache ECHO Checking internet connection... -PING tx.fhir.org -4 -n 1 -w 1000 | FINDSTR TTL && GOTO isonline +powershell -Command "try { $r=[System.Net.WebRequest]::Create('https://tx.fhir.org/r4/metadata'); $r.Timeout=4000; $r.GetResponse().Close(); exit 0 } catch { exit 1 }" +IF %ERRORLEVEL% EQU 0 GOTO isonline ECHO We're offline... SET txoption=-tx n/a GOTO igpublish diff --git a/_updatePublisher.bat b/_updatePublisher.bat index 10fee3815..d4e1b7d1a 100644 --- a/_updatePublisher.bat +++ b/_updatePublisher.bat @@ -22,7 +22,8 @@ IF "%~1"=="/f" SET skipPrompts=y ECHO. ECHO Checking internet connection... -PING tx.fhir.org -4 -n 1 -w 4000 | FINDSTR TTL && GOTO isonline +powershell -Command "try { $r=[System.Net.WebRequest]::Create('https://tx.fhir.org/r4/metadata'); $r.Timeout=4000; $r.GetResponse().Close(); exit 0 } catch { exit 1 }" +IF %ERRORLEVEL% EQU 0 GOTO isonline ECHO We're offline, nothing to do... GOTO end diff --git a/input/fsh/DkCoreCondition.fsh b/input/fsh/DkCoreCondition.fsh index ddbcf9e66..faeaeb3b8 100644 --- a/input/fsh/DkCoreCondition.fsh +++ b/input/fsh/DkCoreCondition.fsh @@ -119,7 +119,7 @@ InstanceOf: DkCoreCondition Title: "John pacemaker" Description: "Johns status efter pacemakeroperation, udtrykt semantisk korrekt med fund frem for operationskode" Usage: #example -* code = $sct#441509002 "Cardiac pacemaker in situ" +* code = $sct#441509002 "kardiel pacemaker in situ" * subject.reference = "Patient/john" * recorder.reference = "Practitioner/AbrahamLaege" * category = $condition-category#problem-list-item diff --git a/input/fsh/DkCoreObservation.fsh b/input/fsh/DkCoreObservation.fsh index 2f45f452d..17a25aabf 100644 --- a/input/fsh/DkCoreObservation.fsh +++ b/input/fsh/DkCoreObservation.fsh @@ -163,7 +163,7 @@ Usage: #example Title: "ElsePainVRS" Description: "Elses smerte målt med VRS" * subject = Reference(else) -* code.coding = $sct#1144798005 "Verbal Rating Scale pain intensity score" +* code.coding = $sct#1144798005 "smerteintensitetsscore på verbal smerteskala" * valueCodeableConcept.coding[0].system = "http://localSystem.dk" * valueCodeableConcept.coding[0].code = #d253770d-ce0b-4320-9806-b8fbe9e5af8a "Kraftige/svære smerter (3)" * valueCodeableConcept.coding[0].userSelected = true From 5ac5be049988d22a06f01916f4570dc419028024 Mon Sep 17 00:00:00 2001 From: Jens Kristian Villadsen Date: Sat, 30 May 2026 14:05:00 +0200 Subject: [PATCH 11/11] minor corrections --- input/fsh/DkCorePatient.fsh | 2 +- input/fsh/codeSystems.fsh | 2 +- input/fsh/valueSets.fsh | 3 ++- sushi-config.yaml | 6 ++---- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/input/fsh/DkCorePatient.fsh b/input/fsh/DkCorePatient.fsh index 6afcce6cc..93215138e 100644 --- a/input/fsh/DkCorePatient.fsh +++ b/input/fsh/DkCorePatient.fsh @@ -44,7 +44,7 @@ Description: "HL7 Denmark core profile for a patient" * identifier only SORIdentifier * managingOrganization only Reference(DkCoreOrganization) * link.other only Reference(DkCorePatient or DkCoreRelatedPerson) -* contact.relationship from extended-patient-contactrelationship (extensible) +* contact.relationship from ExtendedPatientContactRelationship (extensible) Instance: 283 InstanceOf: DkCorePatient diff --git a/input/fsh/codeSystems.fsh b/input/fsh/codeSystems.fsh index 746a38b7c..f64316296 100644 --- a/input/fsh/codeSystems.fsh +++ b/input/fsh/codeSystems.fsh @@ -51,7 +51,7 @@ Description: "CodeSystem supplement with danish translations for administrative -CodeSystem: v2-0131DkSupplement +CodeSystem: V2_0131DkSupplement Id: v2-0131-supplement Title: "DK v2 Contact Role Supplement" Description: "CodeSystem supplement with danish translations for Contact Role" diff --git a/input/fsh/valueSets.fsh b/input/fsh/valueSets.fsh index 8d7dc3570..da93c2144 100644 --- a/input/fsh/valueSets.fsh +++ b/input/fsh/valueSets.fsh @@ -302,7 +302,8 @@ Description: "DK D-eCPR OID values" * ^status = #active * include codes from system DkCoreDeCPRCodes -ValueSet: extended-patient-contactrelationship +ValueSet: ExtendedPatientContactRelationship +Id: extended-patient-contactrelationship Title: "DK Extended Patient Contact Relationship" Description: "DK Extended Patient Contact Relationship" * ^experimental = false diff --git a/sushi-config.yaml b/sushi-config.yaml index b9ef17795..3fbb81f70 100644 --- a/sushi-config.yaml +++ b/sushi-config.yaml @@ -42,10 +42,8 @@ parameters: path-expansion-params: Parameters-expParam.json dependencies: - hl7.fhir.extensions.r5: 4.0.1 - hl7.fhir.uv.extensions.r4: - version: 5.3.0-ballot-tc1 - uri: http://hl7.org/fhir/extensions/ImplementationGuide/hl7.fhir.uv.extensions + hl7.fhir.uv.xver-r5.r4: 0.1.0 + hl7.fhir.uv.extensions.r4: 5.3.0 hl7.fhir.uv.phd: 2.0.0 hl7.fhir.uv.ipa: 1.1.0 ihe.iti.mhd: 4.2.3