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.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/_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/DkCoreDiagnosticReport.fsh b/input/fsh/DkCoreDiagnosticReport.fsh new file mode 100644 index 000000000..93a177fb1 --- /dev/null +++ b/input/fsh/DkCoreDiagnosticReport.fsh @@ -0,0 +1,99 @@ +Profile: DkCoreDiagnosticReport +Parent: DiagnosticReport +Id: dk-core-diagnostic-report +Description: "HL7 Denmark profile for diagnostic reports." +Title: "Danish Core Diagnostic Report Profile" + +* category 0..* +* category ^slicing.discriminator.type = #value + * ^slicing.discriminator.path = "$this" + * ^slicing.rules = #open +* category contains + studyType 0..* and + specialty 0..* and + 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[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) + +* 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 +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 diff --git a/input/fsh/DkCoreObservation.fsh b/input/fsh/DkCoreObservation.fsh index 89dd6a79f..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 @@ -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/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/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/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..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" @@ -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/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" diff --git a/input/fsh/valueSets.fsh b/input/fsh/valueSets.fsh index 6f6c4f340..da93c2144 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 @@ -263,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 @@ -315,13 +355,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" @@ -859,6 +892,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 @@ -913,3 +947,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 new file mode 100644 index 000000000..28c96433c --- /dev/null +++ b/input/pagecontent/StructureDefinition-dk-core-diagnostic-report-intro.md @@ -0,0 +1,50 @@ +### Scope and Usage +The DkCoreDiagnosticReport profile is used for diagnostic reports 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. However, these use cases are typically characterized by more relaxed requirements than within laboratory medicine. + +#### Identifier +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. + +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. + +#### 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. + +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'. +* 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'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. + +#### 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 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). + +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. diff --git a/sushi-config.yaml b/sushi-config.yaml index fcee485e1..3fbb81f70 100644 --- a/sushi-config.yaml +++ b/sushi-config.yaml @@ -42,11 +42,9 @@ 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.phd: 1.1.0 + 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