Compare commits

...

83 Commits

Author SHA1 Message Date
47b746c4b1
Merge pull request #24 from Sudo-JHare/V4-Alpha
V4.1 - Merge release
2025-09-08 19:48:13 +10:00
92fe759b0f V4.3 - Advanced Validation
V4.3  Advanced Validation

added full gamut of options and configuration for fully fleshed out validation
2025-08-29 00:11:24 +10:00
356914f306 ui hotfix 2025-08-28 10:59:58 +10:00
9f3748cc49 UI Change
Hotfix UI change for displaying larger and wider IG package names.
2025-08-28 07:22:38 +10:00
f7063484d9 docker builds 2025-08-27 21:28:30 +10:00
b8014de7c3 Hotfix
added download validation links and fixed CORS issues
2025-08-27 21:08:01 +10:00
f4c457043a hotfix 2025-08-27 19:21:53 +10:00
dffe43beee rollback 2025-08-27 16:42:52 +10:00
3f1ac10290 hotfix 2025-08-27 16:19:52 +10:00
85f980be0a Update app.py 2025-08-27 14:16:52 +10:00
d37af01b3a Update README.md 2025-08-27 13:07:48 +10:00
6ae0e56118 V4.1 Prep
Build Path options

added Candle
Added Custom
2025-08-27 12:47:16 +10:00
f1478561c1 Remove Hapi references
remove Hapi references
Disabled Legacy Hapi controls
2025-08-26 22:00:44 +10:00
236390e57b V4-Alpha
Remove Hapi validaotr
replace with Hl7 Validator
restructure validation report
provide Json and Html validation reports
remove hardcoded local references and couple to ENV allowing custom env Fhir Server URl to be set for local ""instance:
2025-08-26 21:15:24 +10:00
5725f8a22f update 2025-08-13 08:12:47 +10:00
57d05eab38 hotfix 2025-08-13 07:31:32 +10:00
62ff8e8a28 fix 2025-08-12 23:26:05 +10:00
91fdaa89f9 fix 2025-08-12 23:14:09 +10:00
27f9a397b2 update 2025-08-12 23:10:52 +10:00
a76180fd48 hotfix 2025-08-12 23:00:44 +10:00
ffeef91166 Hotfix 2025-08-12 22:33:05 +10:00
9cf99ec78d updates 2025-08-12 21:55:28 +10:00
5f4e1b7207 Update services.py 2025-08-12 21:29:13 +10:00
9729004982 Hotfix - proxy pathing 2025-08-12 20:48:52 +10:00
5a6f2072d7 Hotifx
fix context relative path.
2025-08-12 19:28:47 +10:00
189ba1d18c fix. 2025-08-12 16:39:29 +10:00
Jörn Guy Süß
3474d4e7e5 set up the base url 2025-08-04 23:05:52 +10:00
Jörn Guy Süß
f1f69ad32f only add to existing files 2025-08-04 22:46:38 +10:00
Jörn Guy Süß
b10d9a8cec fix: typo - underscore should be dash 2025-08-04 22:29:05 +10:00
Jörn Guy Süß
5277cd16f4 use gh pages 2025-08-04 22:18:58 +10:00
Jörn Guy Süß
850257f977 update ruby 2025-08-04 22:03:46 +10:00
Jörn Guy Süß
6826b7ba59 use v3 2025-08-04 21:59:56 +10:00
Jörn Guy Süß
dac129d16c update upload action to v4 due to deprecation 2025-08-04 21:58:31 +10:00
Jörn Guy Süß
2af2fd9b11 Add website and website build 2025-08-04 21:52:16 +10:00
Jörn Guy Süß
777dddbcaf ignore build output folder 2025-08-04 20:19:29 +10:00
Jörn Guy Süß
3a34212899 Add website build 2025-08-04 20:17:57 +10:00
Jörn Guy Süß
6447047b86 more bump. 2025-08-04 15:52:37 +10:00
Jörn Guy Süß
cebed936ae Bump helm version 2025-08-04 15:46:14 +10:00
3744123361 updates 2025-08-04 15:15:46 +10:00
Jörn Guy Süß
7eaa94a705 fix: Removed as sub-chart for HAPI FHIR has been removed. 2025-08-04 14:34:03 +10:00
399249faa3 adding in build files 2025-08-04 14:19:12 +10:00
26f095cdd2 Adding back in build scripts
auto user run build scripts
2025-08-04 14:13:56 +10:00
Jörn Guy Süß
ff366fa6ba
Merge pull request #21 from Sudo-JHare/Validation_PH_2Pass
2 Pass Validation and Helm Charts
2025-08-04 13:48:51 +10:00
Jörn Guy Süß
d547ca12a1 feat: use ephemeral volumes and variable image
* use ephemeral volumes to avoid root mounts in user space
* use image definition using environment variable with default.
2025-08-04 12:30:39 +10:00
Jörn Guy Süß
8df84579a8 Build multi-arch 2025-08-04 11:26:33 +10:00
Jörn Guy Süß
35221a7495 Update the dockerfile
* Allow build on click
* expect the dockerfile in subdirectory
2025-08-04 11:21:20 +10:00
Jörn Guy Süß
a6a8427eed Add some upload samples
These are versions PH core
2025-08-04 11:17:33 +10:00
Jörn Guy Süß
ea9ae2abf3 remove the lods and instance directories
They are dynamically created and should not be checked in.
2025-08-04 11:16:50 +10:00
Jörn Guy Süß
505e78404e move the Dockerfile into a subdirectory to clean up. 2025-08-04 11:16:05 +10:00
Jörn Guy Süß
4c451962ae Merge remote-tracking branch 'upstream/Validation_PH_2Pass' into
1-basic-helm-chart-for-light-deployment
2025-08-04 11:15:12 +10:00
Jörn Guy Süß
d740ac8b9e Initial commit 2025-08-04 10:30:38 +10:00
Jörn Guy Süß
8931e921be update chart version 2025-07-18 14:09:44 +10:00
Jörn Guy Süß
81d4f775e9 Add architecture affinity 2025-07-18 13:37:54 +10:00
Jörn Guy Süß
9a379e74f2 Bump chart to 0.2.0 2025-07-16 16:59:29 +10:00
Jörn Guy Süß
a5b442cd2a Merge branch 'main' of git@github.com:jgsuess/FHIRFLARE-IG-Toolkit.git into main 2025-07-16 16:54:55 +10:00
Jörn Guy Süß
81919bface ignore rendered files 2025-07-16 16:54:42 +10:00
Jörn Guy Süß
a78d33fd5f simplify the ingress for test purposes 2025-07-16 16:54:28 +10:00
Jörn Guy Süß
26685633ce simplify the ingress for test purposes 2025-07-16 16:54:20 +10:00
Jörn Guy Süß
3b75177a4c remove rendered content 2025-07-16 14:49:09 +10:00
Jörn Guy Süß
9a0d419e18 trigger page push 2025-07-16 14:41:51 +10:00
Jörn Guy Süß
c4f5f2c1fd explicity add repo 2025-07-16 14:01:42 +10:00
Jörn Guy Süß
3e4982425d Revert chart lock 2025-07-16 14:00:28 +10:00
Jörn Guy Süß
992ab6f168 Remove chart lock for workflow test 2025-07-16 13:59:27 +10:00
Jörn Guy Süß
1d9a263d23 trigger build 2025-07-16 13:50:20 +10:00
Jörn Guy Süß
ea402ceaa9 move gitignore from cache directory 2025-07-16 13:45:21 +10:00
Jörn Guy Süß
55856a35dc update safe directory 2025-07-16 13:42:35 +10:00
Jörn Guy Süß
3413b9ba71 add keyword 2025-07-16 13:40:01 +10:00
Jörn Guy Süß
ebb8f31974 Trigger on branch main 2025-07-16 13:38:50 +10:00
Jörn Guy Süß
43921790fa Add chart and chart workflow 2025-07-16 13:35:34 +10:00
Jörn Guy Süß
83ec579214 Update supervisor configuration not to autostart catalina 2025-07-16 13:08:04 +10:00
Jörn Guy Süß
0bb3ba7e82 fix: updated label match, added test 2025-07-16 11:06:00 +10:00
Jörn Guy Süß
410dd003f7 Initial commit for chart 2025-07-16 10:01:45 +10:00
Jörn Guy Süß
7d69ca27ae refactor: Move docker compose to its own directory 2025-07-15 14:02:24 +10:00
Jörn Guy Süß
d662e8509a remove related build artifacts 2025-07-15 12:09:27 +10:00
Jörn Guy Süß
928a331a40 fix: remove webapp volume mount, it is not required. 2025-07-15 12:09:05 +10:00
Jörn Guy Süß
0674128568 feat: Add helper scripts for startup and shutdown 2025-07-15 12:08:34 +10:00
Jörn Guy Süß
ea5752f59b feat: Add compose using off-the-shelf HAPI 2025-07-15 11:28:50 +10:00
Jörn Guy Süß
8d078e672b feat: reduce and optimize for python 2025-07-15 11:28:20 +10:00
Jörn Guy Süß
75639c176c refactoring: factor out full image build components. 2025-07-15 11:08:38 +10:00
Jörn Guy Süß
3fe12a8030 feat: Add a shell script for Linux users 2025-07-15 11:01:27 +10:00
Jörn Guy Süß
e2a6b19a2e fix: two tag extensions
Run docker build -t
ghcr.io/ghcr.io/jgsuess/fhirflare-ig-toolkit:main:latest .
ERROR: failed to build: invalid tag
"ghcr.io/ghcr.io/jgsuess/fhirflare-ig-toolkit:main:latest": invalid
reference format
2025-07-15 10:21:32 +10:00
Jörn Guy Süß
6ede139084 fix: work around naming convention issue
container registry name must be lowercase, repo can be anything
2025-07-15 10:18:20 +10:00
Jörn Guy Süß
366917c768 Enable container build 2025-07-15 10:06:56 +10:00
125 changed files with 56076 additions and 217232 deletions

23
.github/ct/chart-schema.yaml vendored Normal file
View File

@ -0,0 +1,23 @@
name: str()
home: str()
version: str()
apiVersion: str()
appVersion: any(str(), num(), required=False)
type: str()
dependencies: any(required=False)
description: str()
keywords: list(str(), required=False)
sources: list(str(), required=False)
maintainers: list(include('maintainer'), required=False)
icon: str(required=False)
engine: str(required=False)
condition: str(required=False)
tags: str(required=False)
deprecated: bool(required=False)
kubeVersion: str(required=False)
annotations: map(str(), str(), required=False)
---
maintainer:
name: str()
email: str(required=False)
url: str(required=False)

15
.github/ct/config.yaml vendored Normal file
View File

@ -0,0 +1,15 @@
debug: true
remote: origin
chart-yaml-schema: .github/ct/chart-schema.yaml
validate-maintainers: false
validate-chart-schema: true
validate-yaml: true
check-version-increment: true
chart-dirs:
- charts
helm-extra-args: --timeout 300s
upgrade: true
skip-missing-values: true
release-label: release
release-name-template: "helm-v{{ .Version }}"
target-branch: master

84
.github/workflows/build-images.yaml vendored Normal file
View File

@ -0,0 +1,84 @@
name: Build Container Images
on:
push:
tags:
- "image/v*"
paths-ignore:
- "charts/**"
pull_request:
branches: [master]
paths-ignore:
- "charts/**"
env:
IMAGES: docker.io/hapiproject/hapi
PLATFORMS: linux/amd64,linux/arm64/v8
jobs:
build:
name: Build
runs-on: ubuntu-22.04
steps:
- name: Container meta for default (distroless) image
id: docker_meta
uses: docker/metadata-action@v5
with:
images: ${{ env.IMAGES }}
tags: |
type=match,pattern=image/(.*),group=1,enable=${{github.event_name != 'pull_request'}}
- name: Container meta for tomcat image
id: docker_tomcat_meta
uses: docker/metadata-action@v5
with:
images: ${{ env.IMAGES }}
tags: |
type=match,pattern=image/(.*),group=1,enable=${{github.event_name != 'pull_request'}}
flavor: |
suffix=-tomcat,onlatest=true
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to DockerHub
uses: docker/login-action@v3
if: github.event_name != 'pull_request'
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Cache Docker layers
uses: actions/cache@v3
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-${{ github.sha }}
restore-keys: |
${{ runner.os }}-buildx-
- name: Build and push default (distroless) image
id: docker_build
uses: docker/build-push-action@v5
with:
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.docker_meta.outputs.tags }}
labels: ${{ steps.docker_meta.outputs.labels }}
platforms: ${{ env.PLATFORMS }}
target: default
- name: Build and push tomcat image
id: docker_build_tomcat
uses: docker/build-push-action@v5
with:
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.docker_tomcat_meta.outputs.tags }}
labels: ${{ steps.docker_tomcat_meta.outputs.labels }}
platforms: ${{ env.PLATFORMS }}
target: tomcat

41
.github/workflows/chart-release.yaml vendored Normal file
View File

@ -0,0 +1,41 @@
name: Release Charts
on:
push:
branches:
- main
paths:
- "charts/**"
jobs:
release:
runs-on: ubuntu-22.04
steps:
- name: Add workspace as safe directory
run: |
git config --global --add safe.directory /__w/FHIRFLARE-IG-Toolkit/FHIRFLARE-IG-Toolkit
- name: Checkout
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
with:
fetch-depth: 0
- name: Configure Git
run: |
git config user.name "$GITHUB_ACTOR"
git config user.email "$GITHUB_ACTOR@users.noreply.github.com"
- name: Update dependencies
run: find charts/ ! -path charts/ -maxdepth 1 -type d -exec helm dependency update {} \;
- name: Add Helm Repositories
run: |
helm repo add hapifhir https://hapifhir.github.io/hapi-fhir-jpaserver-starter/
helm repo update
- name: Run chart-releaser
uses: helm/chart-releaser-action@be16258da8010256c6e82849661221415f031968 # v1.5.0
with:
config: .github/ct/config.yaml
env:
CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}"

73
.github/workflows/chart-test.yaml vendored Normal file
View File

@ -0,0 +1,73 @@
name: Lint and Test Charts
on:
pull_request:
branches:
- master
paths:
- "charts/**"
jobs:
lint:
runs-on: ubuntu-22.04
container: quay.io/helmpack/chart-testing:v3.11.0@sha256:f2fd21d30b64411105c7eafb1862783236a219d29f2292219a09fe94ca78ad2a
steps:
- name: Install helm-docs
working-directory: /tmp
env:
HELM_DOCS_URL: https://github.com/norwoodj/helm-docs/releases/download/v1.14.2/helm-docs_1.14.2_Linux_x86_64.tar.gz
run: |
curl -LSs $HELM_DOCS_URL | tar xz && \
mv ./helm-docs /usr/local/bin/helm-docs && \
chmod +x /usr/local/bin/helm-docs && \
helm-docs --version
- name: Add workspace as safe directory
run: |
git config --global --add safe.directory /__w/hapi-fhir-jpaserver-starter/hapi-fhir-jpaserver-starter
- name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
fetch-depth: 0
- name: Check if documentation is up-to-date
run: helm-docs && git diff --exit-code HEAD
- name: Run chart-testing (lint)
run: ct lint --config .github/ct/config.yaml
test:
runs-on: ubuntu-22.04
strategy:
matrix:
k8s-version: [1.30.8, 1.31.4, 1.32.0]
needs:
- lint
steps:
- name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
fetch-depth: 0
- name: Set up chart-testing
uses: helm/chart-testing-action@e6669bcd63d7cb57cb4380c33043eebe5d111992 # v2.6.1
- name: Run chart-testing (list-changed)
id: list-changed
run: |
changed=$(ct list-changed --config .github/ct/config.yaml)
if [[ -n "$changed" ]]; then
echo "::set-output name=changed::true"
fi
- name: Create k8s Kind Cluster
uses: helm/kind-action@dda0770415bac9fc20092cacbc54aa298604d140 # v1.8.0
if: ${{ steps.list-changed.outputs.changed == 'true' }}
with:
cluster_name: kind-cluster-k8s-${{ matrix.k8s-version }}
node_image: kindest/node:v${{ matrix.k8s-version }}
- name: Run chart-testing (install)
run: ct install --config .github/ct/config.yaml
if: ${{ steps.list-changed.outputs.changed == 'true' }}

58
.github/workflows/docker-publish.yml vendored Normal file
View File

@ -0,0 +1,58 @@
# This workflow builds and pushes a multi-architecture Docker image to GitHub Container Registry (ghcr.io).
#
# The Docker meta step is required because GitHub repository names can contain uppercase letters, but Docker image tags must be lowercase.
# The docker/metadata-action@v5 normalizes the repository name to lowercase, ensuring the build and push steps use a valid image tag.
#
# This workflow builds for both AMD64 and ARM64 architectures using Docker Buildx and QEMU emulation.
name: Build and Push Docker image
on:
push:
branches:
- main
- '*' # This will run the workflow on any branch
workflow_dispatch: # This enables manual triggering
jobs:
build-and-push:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository }}
- name: Set normalized image name
run: |
if [[ "${{ github.ref_name }}" == "main" ]]; then
echo "IMAGE_NAME=$(echo ${{ steps.meta.outputs.tags }} | sed 's/:main/:latest/')" >> $GITHUB_ENV
else
echo "IMAGE_NAME=${{ steps.meta.outputs.tags }}" >> $GITHUB_ENV
fi
- name: Build and push multi-architecture Docker image
uses: docker/build-push-action@v5
with:
context: .
file: ./docker/Dockerfile
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ env.IMAGE_NAME }}

40
.github/workflows/jekyll.yml vendored Normal file
View File

@ -0,0 +1,40 @@
name: Build and Deploy Jekyll site
on:
push:
branches: [ main, master ]
pull_request:
branches: [ main, master ]
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
defaults:
run:
working-directory: website
steps:
- uses: actions/checkout@v3
- name: Setup Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: '3.2'
bundler-cache: true
working-directory: website
- name: Install dependencies
run: |
bundle install
- name: Build site
run: bundle exec jekyll build
- name: Deploy to gh_pages branch
uses: peaceiris/actions-gh-pages@v4
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./website/_site
publish_branch: gh-pages
keep_files: true

7
.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
/logs/
/.pydevproject
/__pycache__/
/myenv/
/tmp/
/Gemfile.lock
/_site/

23
.project Normal file
View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>FHIRFLARE-IG-Toolkit</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.python.pydev.PyDevBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.wst.validation.validationbuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.wst.jsdt.core.jsNature</nature>
<nature>org.python.pydev.pythonNature</nature>
</natures>
</projectDescription>

7
.settings/.jsdtscope Normal file
View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.wst.jsdt.launching.JRE_CONTAINER"/>
<classpathentry kind="con" path="org.eclipse.wst.jsdt.launching.baseBrowserLibrary"/>
<classpathentry kind="src" path=""/>
<classpathentry kind="output" path=""/>
</classpath>

View File

@ -0,0 +1 @@
org.eclipse.wst.jsdt.launching.JRE_CONTAINER

View File

@ -0,0 +1 @@
Global

View File

@ -2,57 +2,130 @@
setlocal enabledelayedexpansion setlocal enabledelayedexpansion
REM --- Configuration --- REM --- Configuration ---
set REPO_URL=https://github.com/hapifhir/hapi-fhir-jpaserver-starter.git set REPO_URL_HAPI=https://github.com/hapifhir/hapi-fhir-jpaserver-starter.git
set CLONE_DIR=hapi-fhir-jpaserver set REPO_URL_CANDLE=https://github.com/FHIR/fhir-candle.git
set CLONE_DIR_HAPI=hapi-fhir-jpaserver
set CLONE_DIR_CANDLE=fhir-candle
set SOURCE_CONFIG_DIR=hapi-fhir-setup set SOURCE_CONFIG_DIR=hapi-fhir-setup
set CONFIG_FILE=application.yaml set CONFIG_FILE=application.yaml
REM --- Define Paths --- REM --- Define Paths ---
set SOURCE_CONFIG_PATH=..\%SOURCE_CONFIG_DIR%\target\classes\%CONFIG_FILE% set SOURCE_CONFIG_PATH=..\%SOURCE_CONFIG_DIR%\target\classes\%CONFIG_FILE%
set DEST_CONFIG_PATH=%CLONE_DIR%\target\classes\%CONFIG_FILE% set DEST_CONFIG_PATH=%CLONE_DIR_HAPI%\target\classes\%CONFIG_FILE%
REM === CORRECTED: Prompt for Version === REM --- NEW: Define a variable for the custom FHIR URL and server type ---
set "CUSTOM_FHIR_URL_VAL="
set "SERVER_TYPE="
set "CANDLE_FHIR_VERSION="
REM === MODIFIED: Prompt for Installation Mode ===
:GetModeChoice :GetModeChoice
SET "APP_MODE=" REM Clear the variable first SET "APP_MODE=" REM Clear the variable first
echo.
echo Select Installation Mode: echo Select Installation Mode:
echo 1. Standalone (Includes local HAPI FHIR Server - Requires Git & Maven) echo 1. Lite (Excludes local HAPI FHIR Server - No Git/Maven/Dotnet needed)
echo 2. Lite (Excludes local HAPI FHIR Server - No Git/Maven needed) echo 2. Custom URL (Uses a custom FHIR Server - No Git/Maven/Dotnet needed)
CHOICE /C 12 /N /M "Enter your choice (1 or 2):" echo 3. Hapi (Includes local HAPI FHIR Server - Requires Git & Maven)
echo 4. Candle (Includes local FHIR Candle Server - Requires Git & Dotnet)
CHOICE /C 1234 /N /M "Enter your choice (1, 2, 3, or 4):"
IF ERRORLEVEL 4 (
SET APP_MODE=standalone
SET SERVER_TYPE=candle
goto :GetCandleFhirVersion
)
IF ERRORLEVEL 3 (
SET APP_MODE=standalone
SET SERVER_TYPE=hapi
goto :ModeSet
)
IF ERRORLEVEL 2 ( IF ERRORLEVEL 2 (
SET APP_MODE=standalone
goto :GetCustomUrl
)
IF ERRORLEVEL 1 (
SET APP_MODE=lite SET APP_MODE=lite
goto :ModeSet goto :ModeSet
) )
IF ERRORLEVEL 1 (
SET APP_MODE=standalone
goto :ModeSet
)
REM If somehow neither was chosen (e.g., Ctrl+C), loop back REM If somehow neither was chosen (e.g., Ctrl+C), loop back
echo Invalid input. Please try again. echo Invalid input. Please try again.
goto :GetModeChoice goto :GetModeChoice
:GetCustomUrl
set "CONFIRMED_URL="
:PromptUrlLoop
echo.
set /p "CUSTOM_URL_INPUT=Please enter the custom FHIR server URL: "
echo.
echo You entered: !CUSTOM_URL_INPUT!
set /p "CONFIRM_URL=Is this URL correct? (Y/N): "
if /i "!CONFIRM_URL!" EQU "Y" (
set "CONFIRMED_URL=!CUSTOM_URL_INPUT!"
goto :ConfirmUrlLoop
) else (
goto :PromptUrlLoop
)
:ConfirmUrlLoop
echo.
echo Please re-enter the URL to confirm it is correct:
set /p "CUSTOM_URL_INPUT=Re-enter URL: "
if /i "!CUSTOM_URL_INPUT!" EQU "!CONFIRMED_URL!" (
set "CUSTOM_FHIR_URL_VAL=!CUSTOM_URL_INPUT!"
echo.
echo Custom URL confirmed: !CUSTOM_FHIR_URL_VAL!
goto :ModeSet
) else (
echo.
echo URLs do not match. Please try again.
goto :PromptUrlLoop
)
:GetCandleFhirVersion
echo.
echo Select the FHIR version for the Candle server:
echo 1. R4 (4.0)
echo 2. R4B (4.3)
echo 3. R5 (5.0)
CHOICE /C 123 /N /M "Enter your choice (1, 2, or 3):"
IF ERRORLEVEL 3 (
SET CANDLE_FHIR_VERSION=r5
goto :ModeSet
)
IF ERRORLEVEL 2 (
SET CANDLE_FHIR_VERSION=r4b
goto :ModeSet
)
IF ERRORLEVEL 1 (
SET CANDLE_FHIR_VERSION=r4
goto :ModeSet
)
echo Invalid input. Please try again.
goto :GetCandleFhirVersion
:ModeSet :ModeSet
IF "%APP_MODE%"=="" ( IF "%APP_MODE%"=="" (
echo Invalid choice detected after checks. Exiting. echo Invalid choice detected after checks. Exiting.
goto :eof goto :eof
) )
echo Selected Mode: %APP_MODE% echo Selected Mode: %APP_MODE%
echo Server Type: %SERVER_TYPE%
echo. echo.
REM === END CORRECTION === REM === END MODIFICATION ===
REM === Conditionally Execute HAPI Setup === REM === Conditionally Execute Server Setup ===
IF "%APP_MODE%"=="standalone" ( IF "%SERVER_TYPE%"=="hapi" (
echo Running Standalone setup including HAPI FHIR... echo Running Hapi server setup...
echo. echo.
REM --- Step 0: Clean up previous clone (optional) --- REM --- Step 0: Clean up previous clone (optional) ---
echo Checking for existing directory: %CLONE_DIR% echo Checking for existing directory: %CLONE_DIR_HAPI%
if exist "%CLONE_DIR%" ( if exist "%CLONE_DIR_HAPI%" (
echo Found existing directory, removing it... echo Found existing directory, removing it...
rmdir /s /q "%CLONE_DIR%" rmdir /s /q "%CLONE_DIR_HAPI%"
if errorlevel 1 ( if errorlevel 1 (
echo ERROR: Failed to remove existing directory: %CLONE_DIR% echo ERROR: Failed to remove existing directory: %CLONE_DIR_HAPI%
goto :error goto :error
) )
echo Existing directory removed. echo Existing directory removed.
@ -62,8 +135,8 @@ IF "%APP_MODE%"=="standalone" (
echo. echo.
REM --- Step 1: Clone the HAPI FHIR server repository --- REM --- Step 1: Clone the HAPI FHIR server repository ---
echo Cloning repository: %REPO_URL% into %CLONE_DIR%... echo Cloning repository: %REPO_URL_HAPI% into %CLONE_DIR_HAPI%...
git clone "%REPO_URL%" "%CLONE_DIR%" git clone "%REPO_URL_HAPI%" "%CLONE_DIR_HAPI%"
if errorlevel 1 ( if errorlevel 1 (
echo ERROR: Failed to clone repository. Check Git installation and network connection. echo ERROR: Failed to clone repository. Check Git installation and network connection.
goto :error goto :error
@ -72,10 +145,10 @@ IF "%APP_MODE%"=="standalone" (
echo. echo.
REM --- Step 2: Navigate into the cloned directory --- REM --- Step 2: Navigate into the cloned directory ---
echo Changing directory to %CLONE_DIR%... echo Changing directory to %CLONE_DIR_HAPI%...
cd "%CLONE_DIR%" cd "%CLONE_DIR_HAPI%"
if errorlevel 1 ( if errorlevel 1 (
echo ERROR: Failed to change directory to %CLONE_DIR%. echo ERROR: Failed to change directory to %CLONE_DIR_HAPI%.
goto :error goto :error
) )
echo Current directory: %CD% echo Current directory: %CD%
@ -92,7 +165,7 @@ IF "%APP_MODE%"=="standalone" (
) )
echo Maven build completed successfully. ErrorLevel: %errorlevel% echo Maven build completed successfully. ErrorLevel: %errorlevel%
echo. echo.
REM --- Step 4: Copy the configuration file --- REM --- Step 4: Copy the configuration file ---
echo ===> "Starting file copy (Step 4)..." echo ===> "Starting file copy (Step 4)..."
echo Copying configuration file... echo Copying configuration file...
@ -118,47 +191,141 @@ IF "%APP_MODE%"=="standalone" (
echo Current directory: %CD% echo Current directory: %CD%
echo. echo.
) ELSE ( ) ELSE IF "%SERVER_TYPE%"=="candle" (
echo Running Lite setup, skipping HAPI FHIR build... echo Running FHIR Candle server setup...
REM Ensure the hapi-fhir-jpaserver directory doesn't exist or is empty if Lite mode is chosen after a standalone attempt echo.
if exist "%CLONE_DIR%" (
echo Found existing HAPI directory in Lite mode. Removing it to avoid build issues... REM --- Step 0: Clean up previous clone (optional) ---
rmdir /s /q "%CLONE_DIR%" echo Checking for existing directory: %CLONE_DIR_CANDLE%
if exist "%CLONE_DIR_CANDLE%" (
echo Found existing directory, removing it...
rmdir /s /q "%CLONE_DIR_CANDLE%"
if errorlevel 1 (
echo ERROR: Failed to remove existing directory: %CLONE_DIR_CANDLE%
goto :error
)
echo Existing directory removed.
) else (
echo Directory does not exist, proceeding with clone.
) )
REM Create empty target directories expected by Dockerfile COPY, even if not used echo.
mkdir "%CLONE_DIR%\target\classes" 2> nul
mkdir "%CLONE_DIR%\custom" 2> nul REM --- Step 1: Clone the FHIR Candle server repository ---
REM Create a placeholder empty WAR file to satisfy Dockerfile COPY echo Cloning repository: %REPO_URL_CANDLE% into %CLONE_DIR_CANDLE%...
echo. > "%CLONE_DIR%\target\ROOT.war" git clone "%REPO_URL_CANDLE%" "%CLONE_DIR_CANDLE%"
echo. > "%CLONE_DIR%\target\classes\application.yaml" if errorlevel 1 (
echo ERROR: Failed to clone repository. Check Git and Dotnet installation and network connection.
goto :error
)
echo Repository cloned successfully.
echo.
REM --- Step 2: Navigate into the cloned directory ---
echo Changing directory to %CLONE_DIR_CANDLE%...
cd "%CLONE_DIR_CANDLE%"
if errorlevel 1 (
echo ERROR: Failed to change directory to %CLONE_DIR_CANDLE%.
goto :error
)
echo Current directory: %CD%
echo.
REM --- Step 3: Build the FHIR Candle server using Dotnet ---
echo ===> "Starting Dotnet build (Step 3)...""
dotnet publish -c Release -f net9.0 -o publish
echo ===> Dotnet command finished. Checking error level...
if errorlevel 1 (
echo ERROR: Dotnet build failed. Check Dotnet SDK installation.
cd ..
goto :error
)
echo Dotnet build completed successfully. ErrorLevel: %errorlevel%
echo.
REM --- Step 4: Navigate back to the parent directory ---
echo ===> "Changing directory back (Step 4)..."
cd ..
if errorlevel 1 (
echo ERROR: Failed to change back to the parent directory. ErrorLevel: %errorlevel%
goto :error
)
echo Current directory: %CD%
echo.
) ELSE (
echo Running Lite setup, skipping server build...
REM Ensure the server directories don't exist in Lite mode
if exist "%CLONE_DIR_HAPI%" (
echo Found existing HAPI directory in Lite mode. Removing it to avoid build issues...
rmdir /s /q "%CLONE_DIR_HAPI%"
)
if exist "%CLONE_DIR_CANDLE%" (
echo Found existing Candle directory in Lite mode. Removing it to avoid build issues...
rmdir /s /q "%CLONE_DIR_CANDLE%"
)
REM Create empty placeholder files to satisfy Dockerfile COPY commands in Lite mode.
mkdir "%CLONE_DIR_HAPI%\target\classes" 2> nul
mkdir "%CLONE_DIR_HAPI%\custom" 2> nul
echo. > "%CLONE_DIR_HAPI%\target\ROOT.war"
echo. > "%CLONE_DIR_HAPI%\target\classes\application.yaml"
mkdir "%CLONE_DIR_CANDLE%\publish" 2> nul
echo. > "%CLONE_DIR_CANDLE%\publish\fhir-candle.dll"
echo Placeholder files created for Lite mode build. echo Placeholder files created for Lite mode build.
echo. echo.
) )
REM === Modify docker-compose.yml to set APP_MODE === REM === MODIFIED: Update docker-compose.yml to set APP_MODE and HAPI_FHIR_URL ===
echo Updating docker-compose.yml with APP_MODE=%APP_MODE%... echo Updating docker-compose.yml with APP_MODE=%APP_MODE% and HAPI_FHIR_URL...
( (
echo version: '3.8' echo version: '3.8'
echo services: echo services:
echo fhirflare: echo fhirflare:
echo build: echo build:
echo context: . echo context: .
echo dockerfile: Dockerfile IF "%SERVER_TYPE%"=="hapi" (
echo dockerfile: Dockerfile.hapi
) ELSE IF "%SERVER_TYPE%"=="candle" (
echo dockerfile: Dockerfile.candle
) ELSE (
echo dockerfile: Dockerfile.lite
)
echo ports: echo ports:
echo - "5000:5000" IF "%SERVER_TYPE%"=="candle" (
echo - "8080:8080" # Keep port exposed, even if Tomcat isn't running useful stuff in Lite echo - "5000:5000"
echo - "5001:5826"
) ELSE (
echo - "5000:5000"
echo - "8080:8080"
)
echo volumes: echo volumes:
echo - ./instance:/app/instance echo - ./instance:/app/instance
echo - ./static/uploads:/app/static/uploads echo - ./static/uploads:/app/static/uploads
echo - ./instance/hapi-h2-data/:/app/h2-data # Keep volume mounts consistent IF "%SERVER_TYPE%"=="hapi" (
echo - ./instance/hapi-h2-data/:/app/h2-data # Keep volume mounts consistent
)
echo - ./logs:/app/logs echo - ./logs:/app/logs
IF "%SERVER_TYPE%"=="hapi" (
echo - ./hapi-fhir-jpaserver/target/ROOT.war:/usr/local/tomcat/webapps/ROOT.war
echo - ./hapi-fhir-jpaserver/target/classes/application.yaml:/usr/local/tomcat/conf/application.yaml
) ELSE IF "%SERVER_TYPE%"=="candle" (
echo - ./fhir-candle/publish/:/app/fhir-candle-publish/
)
echo environment: echo environment:
echo - FLASK_APP=app.py echo - FLASK_APP=app.py
echo - FLASK_ENV=development echo - FLASK_ENV=development
echo - NODE_PATH=/usr/lib/node_modules echo - NODE_PATH=/usr/lib/node_modules
echo - APP_MODE=%APP_MODE% echo - APP_MODE=%APP_MODE%
echo - APP_BASE_URL=http://localhost:5000 echo - APP_BASE_URL=http://localhost:5000
echo - HAPI_FHIR_URL=http://localhost:8080/fhir IF DEFINED CUSTOM_FHIR_URL_VAL (
echo - HAPI_FHIR_URL=!CUSTOM_FHIR_URL_VAL!
) ELSE (
IF "%SERVER_TYPE%"=="candle" (
echo - HAPI_FHIR_URL=http://localhost:5826/fhir/%CANDLE_FHIR_VERSION%
echo - ASPNETCORE_URLS=http://0.0.0.0:5826
) ELSE (
echo - HAPI_FHIR_URL=http://localhost:8080/fhir
)
)
echo command: supervisord -c /etc/supervisord.conf echo command: supervisord -c /etc/supervisord.conf
) > docker-compose.yml.tmp ) > docker-compose.yml.tmp
@ -208,4 +375,4 @@ exit /b 1
:eof :eof
echo Script execution finished. echo Script execution finished.
pause pause

View File

@ -1,57 +1,73 @@
# Base image with Python and Java # Base image with Python and Java
FROM tomcat:10.1-jdk17 FROM tomcat:10.1-jdk17
# Install build dependencies, Node.js 18, and coreutils (for stdbuf) # Install build dependencies, Node.js 18, and coreutils (for stdbuf)
RUN apt-get update && apt-get install -y --no-install-recommends \ RUN apt-get update && apt-get install -y --no-install-recommends \
python3 python3-pip python3-venv curl coreutils \ python3 python3-pip python3-venv curl coreutils git \
&& curl -fsSL https://deb.nodesource.com/setup_18.x | bash - \ && curl -fsSL https://deb.nodesource.com/setup_18.x | bash - \
&& apt-get install -y --no-install-recommends nodejs \ && apt-get install -y --no-install-recommends nodejs \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
# Install specific versions of GoFSH and SUSHI # ADDED: Install the Dotnet SDK for the FHIR Candle server
# REMOVED pip install fhirpath from this line # This makes the image a universal base for either server type
RUN npm install -g gofsh fsh-sushi RUN apt-get update && apt-get install -y dotnet-sdk-6.0
# Set up Python environment # Install specific versions of GoFSH and SUSHI
WORKDIR /app RUN npm install -g gofsh fsh-sushi
RUN python3 -m venv /app/venv
ENV PATH="/app/venv/bin:$PATH" # ADDED: Download the latest HL7 FHIR Validator CLI
RUN mkdir -p /app/validator_cli
# ADDED: Uninstall old fhirpath just in case it's in requirements.txt WORKDIR /app/validator_cli
RUN pip uninstall -y fhirpath || true # Download the validator JAR and a separate checksum file for verification
# ADDED: Install the new fhirpathpy library RUN curl -L -o validator_cli.jar "https://github.com/hapifhir/org.hl7.fhir.core/releases/latest/download/validator_cli.jar"
RUN pip install --no-cache-dir fhirpathpy
# Set permissions for the downloaded file
# Copy Flask files RUN chmod 755 validator_cli.jar
COPY requirements.txt .
# Install requirements (including Pydantic - check version compatibility if needed) # Change back to the main app directory for the next steps
RUN pip install --no-cache-dir -r requirements.txt WORKDIR /app
COPY app.py . # Set up Python environment
COPY services.py . RUN python3 -m venv /app/venv
COPY forms.py . ENV PATH="/app/venv/bin:$PATH"
COPY package.py .
COPY templates/ templates/ # ADDED: Uninstall old fhirpath just in case it's in requirements.txt
COPY static/ static/ RUN pip uninstall -y fhirpath || true
COPY tests/ tests/ # ADDED: Install the new fhirpathpy library
RUN pip install --no-cache-dir fhirpathpy
# Ensure /tmp, /app/h2-data, /app/static/uploads, and /app/logs are writable
RUN mkdir -p /tmp /app/h2-data /app/static/uploads /app/logs && chmod 777 /tmp /app/h2-data /app/static/uploads /app/logs # Copy Flask files
COPY requirements.txt .
# Copy pre-built HAPI WAR and configuration # Install requirements (including Pydantic - check version compatibility if needed)
COPY hapi-fhir-jpaserver/target/ROOT.war /usr/local/tomcat/webapps/ RUN pip install --no-cache-dir -r requirements.txt
COPY hapi-fhir-jpaserver/target/classes/application.yaml /usr/local/tomcat/conf/ COPY app.py .
COPY hapi-fhir-jpaserver/target/classes/application.yaml /app/config/application.yaml COPY services.py .
COPY hapi-fhir-jpaserver/target/classes/application.yaml /usr/local/tomcat/webapps/app/config/application.yaml COPY forms.py .
COPY hapi-fhir-jpaserver/custom/ /usr/local/tomcat/webapps/custom/ COPY package.py .
COPY templates/ templates/
# Install supervisord COPY static/ static/
RUN pip install supervisor COPY tests/ tests/
# Configure supervisord # Ensure /tmp, /app/h2-data, /app/static/uploads, and /app/logs are writable
COPY supervisord.conf /etc/supervisord.conf RUN mkdir -p /tmp /app/h2-data /app/static/uploads /app/logs && chmod 777 /tmp /app/h2-data /app/static/uploads /app/logs
# Expose ports # Copy pre-built HAPI WAR and configuration
EXPOSE 5000 8080 COPY hapi-fhir-jpaserver/target/ROOT.war /usr/local/tomcat/webapps/
COPY hapi-fhir-jpaserver/target/classes/application.yaml /usr/local/tomcat/conf/
# Start supervisord COPY hapi-fhir-jpaserver/target/classes/application.yaml /app/config/application.yaml
CMD ["supervisord", "-c", "/etc/supervisord.conf"] COPY hapi-fhir-jpaserver/target/classes/application.yaml /usr/local/tomcat/webapps/app/config/application.yaml
COPY hapi-fhir-jpaserver/custom/ /usr/local/tomcat/webapps/custom/
# ADDED: Copy pre-built Candle DLL files
COPY fhir-candle/publish/ /app/fhir-candle-publish/
# Install supervisord
RUN pip install supervisor
# Configure supervisord
COPY supervisord.conf /etc/supervisord.conf
# Expose ports
EXPOSE 5000 8080 5001
# Start supervisord
CMD ["supervisord", "-c", "/etc/supervisord.conf"]

64
Dockerfile.candle Normal file
View File

@ -0,0 +1,64 @@
# Base image with Python and Dotnet
FROM mcr.microsoft.com/dotnet/sdk:9.0
# Install build dependencies, Node.js 18, and coreutils (for stdbuf)
RUN apt-get update && apt-get install -y --no-install-recommends \
python3 python3-pip python3-venv \
default-jre-headless \
curl coreutils git \
&& curl -fsSL https://deb.nodesource.com/setup_18.x | bash - \
&& apt-get install -y --no-install-recommends nodejs \
&& rm -rf /var/lib/apt/lists/*
# Install specific versions of GoFSH and SUSHI
RUN npm install -g gofsh fsh-sushi
# ADDED: Download the latest HL7 FHIR Validator CLI
RUN mkdir -p /app/validator_cli
WORKDIR /app/validator_cli
# Download the validator JAR and a separate checksum file for verification
RUN curl -L -o validator_cli.jar "https://github.com/hapifhir/org.hl7.fhir.core/releases/latest/download/validator_cli.jar"
# Set permissions for the downloaded file
RUN chmod 755 validator_cli.jar
# Change back to the main app directory for the next steps
WORKDIR /app
# Set up Python environment
RUN python3 -m venv /app/venv
ENV PATH="/app/venv/bin:$PATH"
# ADDED: Uninstall old fhirpath just in case it's in requirements.txt
RUN pip uninstall -y fhirpath || true
# ADDED: Install the new fhirpathpy library
RUN pip install --no-cache-dir fhirpathpy
# Copy Flask files
COPY requirements.txt .
# Install requirements (including Pydantic - check version compatibility if needed)
RUN pip install --no-cache-dir -r requirements.txt
COPY app.py .
COPY services.py .
COPY forms.py .
COPY package.py .
COPY templates/ templates/
COPY static/ static/
COPY tests/ tests/
# Ensure /tmp, /app/h2-data, /app/static/uploads, and /app/logs are writable
RUN mkdir -p /tmp /app/h2-data /app/static/uploads /app/logs && chmod 777 /tmp /app/h2-data /app/static/uploads /app/logs
# Copy pre-built Candle DLL files
COPY fhir-candle/publish/ /app/fhir-candle-publish/
# Install supervisord
RUN pip install supervisor
# Configure supervisord
COPY supervisord.conf /etc/supervisord.conf
# Expose ports
EXPOSE 5000 5001
# Start supervisord
CMD ["supervisord", "-c", "/etc/supervisord.conf"]

66
Dockerfile.hapi Normal file
View File

@ -0,0 +1,66 @@
# Base image with Python, Java, and Maven
FROM tomcat:10.1-jdk17
# Install build dependencies, Node.js 18, and coreutils (for stdbuf)
RUN apt-get update && apt-get install -y --no-install-recommends \
python3 python3-pip python3-venv curl coreutils git maven \
&& curl -fsSL https://deb.nodesource.com/setup_18.x | bash - \
&& apt-get install -y --no-install-recommends nodejs \
&& rm -rf /var/lib/apt/lists/*
# Install specific versions of GoFSH and SUSHI
RUN npm install -g gofsh fsh-sushi
# ADDED: Download the latest HL7 FHIR Validator CLI
RUN mkdir -p /app/validator_cli
WORKDIR /app/validator_cli
# Download the validator JAR and a separate checksum file for verification
RUN curl -L -o validator_cli.jar "https://github.com/hapifhir/org.hl7.fhir.core/releases/latest/download/validator_cli.jar"
# Set permissions for the downloaded file
RUN chmod 755 validator_cli.jar
# Change back to the main app directory for the next steps
WORKDIR /app
# Set up Python environment
RUN python3 -m venv /app/venv
ENV PATH="/app/venv/bin:$PATH"
# ADDED: Uninstall old fhirpath just in case it's in requirements.txt
RUN pip uninstall -y fhirpath || true
# ADDED: Install the new fhirpathpy library
RUN pip install --no-cache-dir fhirpathpy
# Copy Flask files
COPY requirements.txt .
# Install requirements (including Pydantic - check version compatibility if needed)
RUN pip install --no-cache-dir -r requirements.txt
COPY app.py .
COPY services.py .
COPY forms.py .
COPY package.py .
COPY templates/ templates/
COPY static/ static/
COPY tests/ tests/
# Ensure /tmp, /app/h2-data, /app/static/uploads, and /app/logs are writable
RUN mkdir -p /tmp /app/h2-data /app/static/uploads /app/logs && chmod 777 /tmp /app/h2-data /app/static/uploads /app/logs
# Copy pre-built HAPI WAR and configuration
COPY hapi-fhir-jpaserver/target/ROOT.war /usr/local/tomcat/webapps/
COPY hapi-fhir-jpaserver/target/classes/application.yaml /usr/local/tomcat/conf/
COPY hapi-fhir-jpaserver/target/classes/application.yaml /app/config/application.yaml
COPY hapi-fhir-jpaserver/target/classes/application.yaml /usr/local/tomcat/webapps/app/config/application.yaml
COPY hapi-fhir-jpaserver/custom/ /usr/local/tomcat/webapps/custom/
# Install supervisord
RUN pip install supervisor
# Configure supervisord
COPY supervisord.conf /etc/supervisord.conf
# Expose ports
EXPOSE 5000 8080
# Start supervisord
CMD ["supervisord", "-c", "/etc/supervisord.conf"]

60
Dockerfile.lite Normal file
View File

@ -0,0 +1,60 @@
# Base image with Python and Node.js
FROM python:3.9-slim
# Install JRE and other dependencies for the validator
# We need to install `default-jre-headless` to get a minimal Java Runtime Environment
RUN apt-get update && apt-get install -y --no-install-recommends \
default-jre-headless \
curl coreutils git \
&& curl -fsSL https://deb.nodesource.com/setup_18.x | bash - \
&& apt-get install -y --no-install-recommends nodejs \
&& rm -rf /var/lib/apt/lists/*
# Install specific versions of GoFSH and SUSHI
RUN npm install -g gofsh fsh-sushi
# ADDED: Download the latest HL7 FHIR Validator CLI
RUN mkdir -p /app/validator_cli
WORKDIR /app/validator_cli
# Download the validator JAR and a separate checksum file for verification
RUN curl -L -o validator_cli.jar "https://github.com/hapifhir/org.hl7.fhir.core/releases/latest/download/validator_cli.jar"
# Set permissions for the downloaded file
RUN chmod 755 validator_cli.jar
# Change back to the main app directory for the next steps
WORKDIR /app
# Set up Python environment
RUN python3 -m venv /app/venv
ENV PATH="/app/venv/bin:$PATH"
# ADDED: Uninstall old fhirpath just in case it's in requirements.txt
RUN pip uninstall -y fhirpath || true
# ADDED: Install the new fhirpathpy library
RUN pip install --no-cache-dir fhirpathpy
# Copy Flask files
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app.py .
COPY services.py .
COPY forms.py .
COPY package.py .
COPY templates/ templates/
COPY static/ static/
COPY tests/ tests/
# Ensure necessary directories are writable
RUN mkdir -p /app/static/uploads /app/logs && chmod 777 /app/static/uploads /app/logs
# Install supervisord
RUN pip install supervisor
# Configure supervisord
COPY supervisord.conf /etc/supervisord.conf
# Expose ports
EXPOSE 5000
# Start supervisord
CMD ["supervisord", "-c", "/etc/supervisord.conf"]

View File

@ -5,22 +5,65 @@
The FHIRFLARE IG Toolkit is a Flask-based web application designed to streamline the management, processing, validation, and deployment of FHIR Implementation Guides (IGs) and test data. It offers a user-friendly interface for importing IG packages, extracting metadata, validating FHIR resources or bundles, pushing IGs to FHIR servers, converting FHIR resources to FHIR Shorthand (FSH), uploading complex test data sets with dependency management, and retrieving/splitting FHIR bundles. The toolkit includes live consoles for real-time feedback, making it an essential tool for FHIR developers and implementers. The FHIRFLARE IG Toolkit is a Flask-based web application designed to streamline the management, processing, validation, and deployment of FHIR Implementation Guides (IGs) and test data. It offers a user-friendly interface for importing IG packages, extracting metadata, validating FHIR resources or bundles, pushing IGs to FHIR servers, converting FHIR resources to FHIR Shorthand (FSH), uploading complex test data sets with dependency management, and retrieving/splitting FHIR bundles. The toolkit includes live consoles for real-time feedback, making it an essential tool for FHIR developers and implementers.
The application can run in two modes: The application can run in four modes:
* **Standalone:** Includes a Dockerized Flask frontend, SQLite database, and an embedded HAPI FHIR server for local validation and interaction.
* **Lite:** Includes only the Dockerized Flask frontend and SQLite database, excluding the local HAPI FHIR server. Requires connection to external FHIR servers for certain features. * Installation Modes (Lite, Custom URL, Hapi, and Candle)
This toolkit now offers four primary installation modes, configured via a simple command-line prompt during setup:
Lite Mode
Includes the Flask frontend, SQLite database, and core tooling (GoFSH, SUSHI) without an embedded FHIR server.
Requires you to provide a URL for an external FHIR server when using features like the FHIR API Explorer and validation.
Does not require Git, Maven, or .NET for setup.
Ideal for users who will always connect to an existing external FHIR server.
Custom URL Mode
Similar to Lite Mode, but prompts you for a specific external FHIR server URL to use as the default for the toolkit.
The application will be pre-configured to use your custom URL for all FHIR-related operations.
Does not require Git, Maven, or .NET for setup.
Hapi Mode
Includes the full FHIRFLARE toolkit and an embedded HAPI FHIR server.
The docker-compose configuration will proxy requests to the internal HAPI server, which is accessible via http://localhost:8080/fhir.
Requires Git and Maven during the initial build process to compile the HAPI FHIR server.
Ideal for users who want a self-contained, offline development and testing environment.
Candle Mode
Includes the full FHIRFLARE toolkit and an embedded FHIR Candle server.
The docker-compose configuration will proxy requests to the internal Candle server, which is accessible via http://localhost:5001/fhir/<version>.
Requires Git and the .NET SDK during the initial build process.
Ideal for users who want to use the .NET-based FHIR server for development and testing.
## Installation Modes (Lite vs. Standalone) ## Installation Modes (Lite vs. Standalone)
This toolkit offers two primary installation modes to suit different needs: This toolkit offers two primary installation modes to suit different needs:
* **Standalone Version:** * **Standalone Version - Hapi / Candle:**
* Includes the full FHIRFLARE Toolkit application **and** an embedded HAPI FHIR server running locally within the Docker environment. * Includes the full FHIRFLARE Toolkit application **and** an embedded HAPI FHIR server running locally within the Docker environment.
* Allows for local FHIR resource validation using HAPI FHIR's capabilities. * Allows for local FHIR resource validation using HAPI FHIR's capabilities.
* Enables the "Use Local HAPI" option in the FHIR API Explorer and FHIR UI Operations pages, proxying requests to the internal HAPI server (`http://localhost:8080/fhir`). * Enables the "Use Local HAPI" option in the FHIR API Explorer and FHIR UI Operations pages, proxying requests to the internal HAPI server (`http://localhost:8080/fhir`).
* Requires Git and Maven during the initial build process (via the `.bat` script or manual steps) to prepare the HAPI FHIR server. * Requires Git and Maven during the initial build process (via the `.bat` script or manual steps) to prepare the HAPI FHIR server.
* Ideal for users who want a self-contained environment for development and testing or who don't have readily available external FHIR servers. * Ideal for users who want a self-contained environment for development and testing or who don't have readily available external FHIR servers.
* **Standalone Version - Custom Mode:**
* Includes the full FHIRFLARE Toolkit application **and** point the Environmet variable at your chosen custom fhir url endpoint allowing for Custom setups
* **Lite Version:** * **Lite Version:**
* Includes the FHIRFLARE Toolkit application **without** the embedded HAPI FHIR server. * Includes the FHIRFLARE Toolkit application **without** the embedded HAPI FHIR server.
* Requires users to provide URLs for external FHIR servers when using features like the FHIR API Explorer and FHIR UI Operations pages. The "Use Local HAPI" option will be disabled in the UI. * Requires users to provide URLs for external FHIR servers when using features like the FHIR API Explorer and FHIR UI Operations pages. The "Use Local HAPI" option will be disabled in the UI.

412
app.py
View File

@ -1,6 +1,11 @@
import sys import sys
import os import os
sys.path.append(os.path.abspath(os.path.dirname(__file__))) # Make paths relative to the current directory instead of absolute '/app' paths
CURRENT_DIR = os.path.abspath(os.path.dirname(__file__))
# Introduce app_dir variable that can be overridden by environment
app_dir = os.environ.get('APP_DIR', CURRENT_DIR)
sys.path.append(CURRENT_DIR)
#sys.path.append(os.path.abspath(os.path.dirname(__file__)))
import datetime import datetime
import shutil import shutil
import queue import queue
@ -24,6 +29,7 @@ import re
import yaml import yaml
import threading import threading
import time # Add time import import time # Add time import
import io
import services import services
from services import ( from services import (
services_bp, services_bp,
@ -39,9 +45,14 @@ from services import (
pkg_version, pkg_version,
get_package_description, get_package_description,
safe_parse_version, safe_parse_version,
import_manual_package_and_dependencies import_manual_package_and_dependencies,
parse_and_list_igs,
generate_ig_yaml,
apply_and_validate,
run_validator_cli,
uuid
) )
from forms import IgImportForm, ManualIgImportForm, ValidationForm, FSHConverterForm, TestDataUploadForm, RetrieveSplitDataForm from forms import IgImportForm, ManualIgImportForm, ValidationForm, FSHConverterForm, TestDataUploadForm, RetrieveSplitDataForm, IgYamlForm
from wtforms import SubmitField from wtforms import SubmitField
from package import package_bp from package import package_bp
from flasgger import Swagger, swag_from # Import Flasgger from flasgger import Swagger, swag_from # Import Flasgger
@ -52,16 +63,24 @@ from logging.handlers import RotatingFileHandler
#app setup #app setup
app = Flask(__name__) app = Flask(__name__)
app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY', 'your-fallback-secret-key-here') app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY', 'your-fallback-secret-key-here')
app.config['SQLALCHEMY_DATABASE_URI'] = os.environ.get('DATABASE_URL', 'sqlite:////app/instance/fhir_ig.db')
# Update paths to be relative to current directory
instance_path = os.path.join(CURRENT_DIR, 'instance')
app.config['SQLALCHEMY_DATABASE_URI'] = os.environ.get('DATABASE_URL', f'sqlite:///{os.path.join(instance_path, "fhir_ig.db")}')
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['FHIR_PACKAGES_DIR'] = '/app/instance/fhir_packages' app.config['FHIR_PACKAGES_DIR'] = os.path.join(instance_path, 'fhir_packages')
app.config['API_KEY'] = os.environ.get('API_KEY', 'your-fallback-api-key-here') app.config['API_KEY'] = os.environ.get('API_KEY', 'your-fallback-api-key-here')
app.config['VALIDATE_IMPOSED_PROFILES'] = True app.config['VALIDATE_IMPOSED_PROFILES'] = True
app.config['DISPLAY_PROFILE_RELATIONSHIPS'] = True app.config['DISPLAY_PROFILE_RELATIONSHIPS'] = True
app.config['UPLOAD_FOLDER'] = '/app/static/uploads' # For GoFSH output app.config['LATEST_BUILD_URL'] = "http://build.fhir.org/ig/qas.json"
app.config['VALIDATION_LOG_DIR'] = os.path.join(app.instance_path, 'validation_logs')
os.makedirs(app.config['VALIDATION_LOG_DIR'], exist_ok=True)
app.config['UPLOAD_FOLDER'] = os.path.join(CURRENT_DIR, 'static', 'uploads') # For GoFSH output
app.config['APP_BASE_URL'] = os.environ.get('APP_BASE_URL', 'http://localhost:5000') app.config['APP_BASE_URL'] = os.environ.get('APP_BASE_URL', 'http://localhost:5000')
app.config['HAPI_FHIR_URL'] = os.environ.get('HAPI_FHIR_URL', 'http://localhost:8080/fhir') app.config['HAPI_FHIR_URL'] = os.environ.get('HAPI_FHIR_URL', 'http://localhost:8080/fhir')
CONFIG_PATH = '/usr/local/tomcat/conf/application.yaml' CONFIG_PATH = os.environ.get('CONFIG_PATH', '/usr/local/tomcat/conf/application.yaml')
# Basic Swagger configuration # Basic Swagger configuration
app.config['SWAGGER'] = { app.config['SWAGGER'] = {
@ -228,6 +247,11 @@ db = SQLAlchemy(app)
csrf = CSRFProtect(app) csrf = CSRFProtect(app)
migrate = Migrate(app, db) migrate = Migrate(app, db)
# Add a global application state dictionary for sharing state between threads
app_state = {
'fetch_failed': False
}
# @app.route('/clear-cache') # @app.route('/clear-cache')
# def clear_cache(): # def clear_cache():
# """Clears the in-memory package cache, the DB timestamp, and the CachedPackage table.""" # """Clears the in-memory package cache, the DB timestamp, and the CachedPackage table."""
@ -519,6 +543,29 @@ def restart_tomcat():
def config_hapi(): def config_hapi():
return render_template('config_hapi.html', site_name='FHIRFLARE IG Toolkit', now=datetime.datetime.now()) return render_template('config_hapi.html', site_name='FHIRFLARE IG Toolkit', now=datetime.datetime.now())
@app.route('/api/get-local-server-url', methods=['GET'])
@swag_from({
'tags': ['FHIR Server Configuration'],
'summary': 'Get the local FHIR server URL.',
'description': 'Retrieves the base URL of the configured local FHIR server (HAPI). This is used by frontend components to make direct requests, bypassing the proxy.',
'responses': {
'200': {
'description': 'The URL of the local FHIR server.',
'schema': {
'type': 'object',
'properties': {
'url': {'type': 'string', 'example': 'http://localhost:8080/fhir'}
}
}
}
}
})
def get_local_server_url():
"""
Expose the local HAPI FHIR server URL to the frontend.
"""
return jsonify({'url': app.config.get('HAPI_FHIR_URL', 'http://localhost:8080/fhir')})
@app.route('/manual-import-ig', methods=['GET', 'POST']) @app.route('/manual-import-ig', methods=['GET', 'POST'])
def manual_import_ig(): def manual_import_ig():
""" """
@ -695,7 +742,7 @@ def perform_cache_refresh_and_log():
now_ts = datetime.datetime.now(datetime.timezone.utc) now_ts = datetime.datetime.now(datetime.timezone.utc)
app.config['MANUAL_PACKAGE_CACHE'] = normalized_packages app.config['MANUAL_PACKAGE_CACHE'] = normalized_packages
app.config['MANUAL_CACHE_TIMESTAMP'] = now_ts app.config['MANUAL_CACHE_TIMESTAMP'] = now_ts
session['fetch_failed'] = fetch_failed # Update session flag reflecting fetch outcome app_state['fetch_failed'] = fetch_failed # Update app_state instead of session
logger.info(f"Updated in-memory cache with {len(normalized_packages)} packages. Fetch failed: {fetch_failed}") logger.info(f"Updated in-memory cache with {len(normalized_packages)} packages. Fetch failed: {fetch_failed}")
# 6. Cache in Database (if successful fetch) # 6. Cache in Database (if successful fetch)
@ -1725,6 +1772,157 @@ csrf.exempt(api_push_ig) # Add this line
# Exempt the entire API blueprint (for routes defined IN services.py, like /api/validate-sample) # Exempt the entire API blueprint (for routes defined IN services.py, like /api/validate-sample)
csrf.exempt(services_bp) # Keep this line for routes defined in the blueprint csrf.exempt(services_bp) # Keep this line for routes defined in the blueprint
@app.route('/validate-sample', methods=['GET'])
def validate_resource_page():
"""Renders the validation form page."""
form = ValidationForm()
# Fetch list of packages to populate the dropdown
packages = current_app.config.get('MANUAL_PACKAGE_CACHE', [])
return render_template('validate_sample.html', form=form, packages=packages)
@app.route('/api/validate', methods=['POST'])
@swag_from({
'tags': ['Validation'],
'summary': 'Starts a validation process for a FHIR resource or bundle.',
'description': 'Starts a validation task and immediately returns a task ID for polling. The actual validation is run in a separate thread to prevent timeouts.',
'security': [{'ApiKeyAuth': []}],
'consumes': ['application/json'],
'parameters': [
{
'name': 'validation_payload',
'in': 'body',
'required': True,
'schema': {
'type': 'object',
'required': ['package_name', 'version', 'fhir_resource'],
'properties': {
'package_name': {'type': 'string', 'example': 'hl7.fhir.us.core'},
'version': {'type': 'string', 'example': '6.1.0'},
'fhir_resource': {'type': 'string', 'description': 'A JSON string of the FHIR resource or Bundle to validate.'},
'fhir_version': {'type': 'string', 'example': '4.0.1'},
'do_native': {'type': 'boolean', 'default': False},
'hint_about_must_support': {'type': 'boolean', 'default': False},
'assume_valid_rest_references': {'type': 'boolean', 'default': False},
'no_extensible_binding_warnings': {'type': 'boolean', 'default': False},
'show_times': {'type': 'boolean', 'default': False},
'allow_example_urls': {'type': 'boolean', 'default': False},
'check_ips_codes': {'type': 'boolean', 'default': False},
'allow_any_extensions': {'type': 'boolean', 'default': False},
'tx_routing': {'type': 'boolean', 'default': False},
'snomed_ct_version': {'type': 'string', 'default': ''},
'profiles': {'type': 'string', 'description': 'Comma-separated list of profile URLs.'},
'extensions': {'type': 'string', 'description': 'Comma-separated list of extension URLs.'},
'terminology_server': {'type': 'string', 'description': 'URL of an alternate terminology server.'}
}
}
}
],
'responses': {
'202': {'description': 'Validation process started successfully, returns a task_id.'},
'400': {'description': 'Invalid request (e.g., missing fields, invalid JSON).'}
}
})
def validate_resource():
"""API endpoint to run validation on a FHIR resource."""
try:
# Get data from the JSON payload
data = request.get_json()
if not data:
return jsonify({'status': 'error', 'message': 'Invalid JSON: No data received.'}), 400
fhir_resource_text = data.get('fhir_resource')
fhir_version = data.get('fhir_version')
package_name = data.get('package_name')
version = data.get('version')
profiles = data.get('profiles')
extensions = data.get('extensions')
terminology_server = data.get('terminology_server')
snomed_ct_version = data.get('snomed_ct_version')
# Build the list of IGs from the package name and version
igs = [f"{package_name}#{version}"] if package_name and version else []
flags = {
'doNative': data.get('do_native'),
'hintAboutMustSupport': data.get('hint_about_must_support'),
'assumeValidRestReferences': data.get('assume_valid_rest_references'),
'noExtensibleBindingWarnings': data.get('no_extensible_binding_warnings'),
'showTimes': data.get('show_times'),
'allowExampleUrls': data.get('allow_example_urls'),
'checkIpsCodes': data.get('check_ips_codes'),
'allowAnyExtensions': data.get('allow_any_extensions'),
'txRouting': data.get('tx_routing'),
}
# Check for empty resource
if not fhir_resource_text:
return jsonify({'status': 'error', 'message': 'No FHIR resource provided.'}), 400
try:
# Generate a unique task ID
task_id = str(uuid.uuid4())
# Store a pending status in the global cache
with services.cache_lock:
services.validation_results_cache[task_id] = {"status": "pending"}
# Start the validation process in a new thread
app_context = app.app_context()
thread = threading.Thread(
target=services.validate_in_thread,
args=(app_context, task_id, fhir_resource_text, fhir_version, igs, profiles, extensions, terminology_server, flags, snomed_ct_version),
daemon=True
)
thread.start()
logger.info(f"Validation task {task_id} started in background thread.")
return jsonify({"status": "accepted", "message": "Validation started", "task_id": task_id}), 202
except Exception as e:
logger.error(f"Error starting validation thread: {e}", exc_info=True)
return jsonify({'status': 'error', 'message': f'An internal server error occurred while starting the validation: {e}'}), 500
except Exception as e:
logger.error(f"Validation API error: {e}")
return jsonify({'status': 'error', 'message': f'An internal server error occurred: {e}'}), 500
@app.route('/api/check-validation-status/<task_id>', methods=['GET'])
@swag_from({
'tags': ['Validation'],
'summary': 'Check the status of a validation task.',
'description': 'Polls for the result of a validation task by its ID. Returns the full validation report when the task is complete.',
'security': [{'ApiKeyAuth': []}],
'parameters': [
{'name': 'task_id', 'in': 'path', 'type': 'string', 'required': True, 'description': 'The unique ID of the validation task.'}
],
'responses': {
'200': {'description': 'Returns the full validation report if the task is complete.'},
'202': {'description': 'The validation task is still in progress.'},
'404': {'description': 'The specified task ID was not found.'}
}
})
def check_validation_status(task_id):
"""
This GET endpoint is polled by the frontend to check the status of a validation task.
"""
with services.cache_lock:
task_result = services.validation_results_cache.get(task_id)
if task_result is None:
return jsonify({"status": "error", "message": "Task not found."}), 404
if task_result["status"] == "pending":
return jsonify({"status": "pending", "message": "Validation is still in progress."}), 202
# Remove the result from the cache after it has been retrieved to prevent a memory leak
with services.cache_lock:
del services.validation_results_cache[task_id]
return jsonify({"status": "complete", "result": task_result["result"]}), 200
def create_db(): def create_db():
logger.debug(f"Attempting to create database tables for URI: {app.config['SQLALCHEMY_DATABASE_URI']}") logger.debug(f"Attempting to create database tables for URI: {app.config['SQLALCHEMY_DATABASE_URI']}")
try: try:
@ -1778,25 +1976,39 @@ def proxy_hapi(subpath):
logger.debug(f"Proxy received request for path: '/fhir/{subpath}', cleaned subpath: '{clean_subpath}'") logger.debug(f"Proxy received request for path: '/fhir/{subpath}', cleaned subpath: '{clean_subpath}'")
# Determine the target FHIR server base URL # Determine the target FHIR server base URL
# NEW: Check for proxy-target query parameter first
target_server_query = request.args.get('proxy-target')
target_server_header = request.headers.get('X-Target-FHIR-Server') target_server_header = request.headers.get('X-Target-FHIR-Server')
final_base_url = None final_base_url = None
is_custom_target = False is_custom_target = False
if target_server_header: if target_server_query:
try: try:
parsed_url = urlparse(target_server_header) parsed_url = urlparse(target_server_query)
if not parsed_url.scheme or not parsed_url.netloc: if not parsed_url.scheme or not parsed_url.netloc:
raise ValueError("Invalid URL format in X-Target-FHIR-Server header") raise ValueError("Invalid URL format in proxy-target query parameter")
final_base_url = target_server_header.rstrip('/') final_base_url = target_server_query.rstrip('/')
is_custom_target = True is_custom_target = True
logger.info(f"Proxy target identified from header: {final_base_url}") logger.info(f"Proxy target identified from query parameter: {final_base_url}")
except ValueError as e: except ValueError as e:
logger.warning(f"Invalid URL in X-Target-FHIR-Server header: '{target_server_header}'. Falling back. Error: {e}") logger.warning(f"Invalid URL in proxy-target query parameter: '{target_server_query}'. Falling back. Error: {e}")
final_base_url = current_app.config['HAPI_FHIR_URL'].rstrip('/') final_base_url = current_app.config['HAPI_FHIR_URL'].rstrip('/')
logger.debug(f"Falling back to default local HAPI due to invalid header: {final_base_url}") logger.debug(f"Falling back to default local HAPI due to invalid query: {final_base_url}")
elif target_server_header:
try:
parsed_url = urlparse(target_server_header)
if not parsed_url.scheme or not parsed_url.netloc:
raise ValueError("Invalid URL format in X-Target-FHIR-Server header")
final_base_url = target_server_header.rstrip('/')
is_custom_target = True
logger.info(f"Proxy target identified from header: {final_base_url}")
except ValueError as e:
logger.warning(f"Invalid URL in X-Target-FHIR-Server header: '{target_server_header}'. Falling back. Error: {e}")
final_base_url = current_app.config['HAPI_FHIR_URL'].rstrip('/')
logger.debug(f"Falling back to default local HAPI due to invalid header: {final_base_url}")
else: else:
final_base_url = current_app.config['HAPI_FHIR_URL'].rstrip('/') final_base_url = current_app.config['HAPI_FHIR_URL'].rstrip('/')
logger.debug(f"No target header found, proxying to default local HAPI: {final_base_url}") logger.debug(f"No target info found, proxying to default local HAPI: {final_base_url}")
# Construct the final URL for the target server request # Construct the final URL for the target server request
# Append the cleaned subpath only if it's not empty # Append the cleaned subpath only if it's not empty
@ -1879,7 +2091,47 @@ def proxy_hapi(subpath):
logger.error(f"Unexpected proxy error for {final_url}: {str(e)}", exc_info=True) logger.error(f"Unexpected proxy error for {final_url}: {str(e)}", exc_info=True)
return jsonify({'resourceType': 'OperationOutcome', 'issue': [{'severity': 'error', 'code': 'exception', 'diagnostics': 'An unexpected error occurred within the FHIR proxy.', 'details': {'text': str(e)}}]}), 500 return jsonify({'resourceType': 'OperationOutcome', 'issue': [{'severity': 'error', 'code': 'exception', 'diagnostics': 'An unexpected error occurred within the FHIR proxy.', 'details': {'text': str(e)}}]}), 500
# --- End of corrected proxy_hapi function ---
@app.route('/api/stream-retrieve', methods=['GET'])
def stream_retrieve_bundles():
"""
Handles streaming FHIR bundle retrieval. This endpoint directly calls the service function,
bypassing the proxy to avoid conflicts. It receives the target URL,
resources, and other parameters from the front-end via URL query parameters.
"""
def generate_stream():
with app.app_context():
# Extract parameters from query string
fhir_server_url = request.args.get('fhir_server_url')
resources = request.args.getlist('resources')
validate_references = request.args.get('validate_references', 'false').lower() == 'true'
fetch_reference_bundles = request.args.get('fetch_reference_bundles', 'false').lower() == 'true'
# Extract authentication headers
auth_token = request.headers.get('Authorization')
auth_type = 'bearer' if auth_token and auth_token.lower().startswith('bearer') else 'basic' if auth_token and auth_token.lower().startswith('basic') else 'none'
temp_dir = tempfile.gettempdir()
zip_filename = f"retrieved_bundles_{datetime.datetime.now().strftime('%Y%m%d%H%M%S')}.zip"
output_zip = os.path.join(temp_dir, zip_filename)
try:
yield from services.retrieve_bundles(
fhir_server_url=fhir_server_url,
resources=resources,
output_zip=output_zip,
validate_references=validate_references,
fetch_reference_bundles=fetch_reference_bundles,
auth_type=auth_type,
auth_token=auth_token
)
except Exception as e:
logger.error(f"Error in retrieve_bundles: {e}", exc_info=True)
yield json.dumps({"type": "error", "message": f"Unexpected error: {str(e)}"}) + "\n"
response = Response(generate_stream(), mimetype='application/x-ndjson')
response.headers['X-Zip-Path'] = os.path.join('/tmp', zip_filename)
return response
@app.route('/api/load-ig-to-hapi', methods=['POST']) @app.route('/api/load-ig-to-hapi', methods=['POST'])
@ -2194,7 +2446,7 @@ def api_upload_test_data():
if auth_type not in ['none', 'bearerToken', 'basic']: if auth_type not in ['none', 'bearerToken', 'basic']:
return jsonify({"status": "error", "message": "Invalid Authentication Type."}), 400 return jsonify({"status": "error", "message": "Invalid Authentication Type."}), 400
if auth_type == 'bearerToken' and not auth_token: if auth_type == 'bearerToken' and not auth_token:
return jsonify({"status": "error", "message": "Bearer Token required."}), 400 return jsonify({"status": "error", "message": "auth_token required for bearerToken."}), 400
if auth_type == 'basic' and (not username or not password): if auth_type == 'basic' and (not username or not password):
return jsonify({"status": "error", "message": "Username and Password required for Basic Authentication."}), 400 return jsonify({"status": "error", "message": "Username and Password required for Basic Authentication."}), 400
if upload_mode not in ['individual', 'transaction']: if upload_mode not in ['individual', 'transaction']:
@ -2238,7 +2490,7 @@ def api_upload_test_data():
# --- Prepare Server Info and Options --- # --- Prepare Server Info and Options ---
server_info = {'url': fhir_server_url, 'auth_type': auth_type} server_info = {'url': fhir_server_url, 'auth_type': auth_type}
if auth_type == 'bearerToken': if auth_type == 'bearer':
server_info['auth_token'] = auth_token server_info['auth_token'] = auth_token
elif auth_type == 'basic': elif auth_type == 'basic':
credentials = f"{username}:{password}" credentials = f"{username}:{password}"
@ -2390,19 +2642,21 @@ def api_retrieve_bundles():
output_zip = os.path.join(temp_dir, zip_filename) output_zip = os.path.join(temp_dir, zip_filename)
def generate(): def generate():
try: # Push the application context manually for the generator's lifetime
yield from services.retrieve_bundles( with app.app_context():
fhir_server_url=fhir_server_url, try:
resources=resources, yield from services.retrieve_bundles(
output_zip=output_zip, fhir_server_url=fhir_server_url,
validate_references=validate_references, resources=resources,
fetch_reference_bundles=fetch_reference_bundles, output_zip=output_zip,
auth_type=auth_type, validate_references=validate_references,
auth_token=auth_token fetch_reference_bundles=fetch_reference_bundles,
) auth_type=auth_type,
except Exception as e: auth_token=auth_token
logger.error(f"Error in retrieve_bundles: {e}", exc_info=True) )
yield json.dumps({"type": "error", "message": f"Unexpected error: {str(e)}"}) + "\n" except Exception as e:
logger.error(f"Error in retrieve_bundles: {e}", exc_info=True)
yield json.dumps({"type": "error", "message": f"Unexpected error: {str(e)}"}) + "\n"
response = Response(generate(), mimetype='application/x-ndjson') response = Response(generate(), mimetype='application/x-ndjson')
response.headers['X-Zip-Path'] = os.path.join('/tmp', zip_filename) response.headers['X-Zip-Path'] = os.path.join('/tmp', zip_filename)
@ -2658,7 +2912,7 @@ def search_and_import():
raw_packages = fetch_packages_from_registries(search_term='') raw_packages = fetch_packages_from_registries(search_term='')
logger.debug(f"fetch_packages_from_registries returned {len(raw_packages)} raw packages.") logger.debug(f"fetch_packages_from_registries returned {len(raw_packages)} raw packages.")
if not raw_packages: if not raw_packages:
logger.warning("fetch_packages_from_registries returned no packages. Handling fallback or empty list.") logger.warning("No packages returned from registries during refresh.")
normalized_packages = [] normalized_packages = []
fetch_failed_flag = True fetch_failed_flag = True
session['fetch_failed'] = True session['fetch_failed'] = True
@ -2672,6 +2926,7 @@ def search_and_import():
now_ts = datetime.datetime.now(datetime.timezone.utc) now_ts = datetime.datetime.now(datetime.timezone.utc)
app.config['MANUAL_PACKAGE_CACHE'] = normalized_packages app.config['MANUAL_PACKAGE_CACHE'] = normalized_packages
app.config['MANUAL_CACHE_TIMESTAMP'] = now_ts app.config['MANUAL_CACHE_TIMESTAMP'] = now_ts
app_state['fetch_failed'] = False
logger.info(f"Stored {len(normalized_packages)} packages in manual cache (memory).") logger.info(f"Stored {len(normalized_packages)} packages in manual cache (memory).")
# Save to CachedPackage table # Save to CachedPackage table
@ -2881,10 +3136,18 @@ def safe_parse_version_local(v_str): # Use different name
elif suffix in ['draft', 'ballot', 'preview']: return pkg_version_local.parse(f"{base_part}b0") elif suffix in ['draft', 'ballot', 'preview']: return pkg_version_local.parse(f"{base_part}b0")
elif suffix and suffix.startswith('rc'): return pkg_version_local.parse(f"{base_part}rc{ ''.join(filter(str.isdigit, suffix)) or '0'}") elif suffix and suffix.startswith('rc'): return pkg_version_local.parse(f"{base_part}rc{ ''.join(filter(str.isdigit, suffix)) or '0'}")
return pkg_version_local.parse(base_part) return pkg_version_local.parse(base_part)
except pkg_version_local.InvalidVersion: logger_details.warning(f"[DetailsView] Invalid base version '{base_part}' after splitting '{original_v_str}'. Treating as alpha."); return pkg_version_local.parse("0.0.0a0") except pkg_version_local.InvalidVersion:
except Exception as e: logger_details.error(f"[DetailsView] Unexpected error parsing FHIR-suffixed version '{original_v_str}': {e}"); return pkg_version_local.parse("0.0.0a0") logger_details.warning(f"[DetailsView] Invalid base version '{base_part}' after splitting '{original_v_str}'. Treating as alpha.")
else: logger_details.warning(f"[DetailsView] Unparseable version '{original_v_str}' (base '{base_part}' not standard). Treating as alpha."); return pkg_version_local.parse("0.0.0a0") return pkg_version_local.parse("0.0.0a0")
except Exception as e: logger_details.error(f"[DetailsView] Unexpected error in safe_parse_version_local for '{v_str}': {e}"); return pkg_version_local.parse("0.0.0a0") except Exception as e:
logger_details.error(f"[DetailsView] Unexpected error parsing FHIR-suffixed version '{original_v_str}': {e}")
return pkg_version_local.parse("0.0.0a0")
else:
logger_details.warning(f"[DetailsView] Unparseable version '{original_v_str}' (base '{base_part}' not standard). Treating as alpha.")
return pkg_version_local.parse("0.0.0a0")
except Exception as e:
logger_details.error(f"[DetailsView] Unexpected error in safe_parse_version_local for '{v_str}': {e}")
return pkg_version_local.parse("0.0.0a0")
# --- End Local Helper Definition --- # --- End Local Helper Definition ---
@app.route('/package-details/<name>') @app.route('/package-details/<name>')
@ -3055,7 +3318,74 @@ def package_details_view(name):
package_name=actual_package_name, package_name=actual_package_name,
latest_official_version=latest_official_version_str) latest_official_version=latest_official_version_str)
@app.route('/ig-configurator', methods=['GET', 'POST'])
def ig_configurator():
form = IgYamlForm()
packages_dir = current_app.config['FHIR_PACKAGES_DIR']
loaded_igs = services.parse_and_list_igs(packages_dir)
form.igs.choices = [(name, f"{name} ({info['version']})") for name, info in loaded_igs.items()]
yaml_output = None
if form.validate_on_submit():
selected_ig_names = form.igs.data
try:
yaml_output = generate_ig_yaml(selected_ig_names, loaded_igs)
flash("YAML configuration generated successfully!", "success")
except Exception as e:
flash(f"Error generating YAML: {str(e)}", "error")
logger.error(f"Error generating YAML: {str(e)}", exc_info=True)
return render_template('ig_configurator.html', form=form, yaml_output=yaml_output)
@app.route('/download/<path:filename>')
def download_file(filename):
"""
Serves a file from the temporary directory created during validation.
The filename includes the unique temporary directory name to prevent
unauthorized file access and to correctly locate the file.
"""
# Use the stored temporary directory path from app.config
temp_dir = current_app.config.get('VALIDATION_TEMP_DIR')
# If no temporary directory is set, or it's a security risk, return a 404.
if not temp_dir or not os.path.exists(temp_dir):
logger.error(f"Download request failed: Temporary directory not found or expired. Path: {temp_dir}")
return jsonify({"error": "File not found or validation session expired."}), 404
# Construct the full file path.
file_path = os.path.join(temp_dir, filename)
if not os.path.exists(file_path):
logger.error(f"File not found for download: {file_path}")
return jsonify({"error": "File not found."}), 404
try:
# Use send_file with as_attachment=True to trigger a download
return send_file(file_path, as_attachment=True, download_name=filename)
except Exception as e:
logger.error(f"Error serving file {file_path}: {str(e)}")
return jsonify({"error": "Error serving file.", "details": str(e)}), 500
@app.route('/api/clear-validation-results', methods=['POST'])
@csrf.exempt
def clear_validation_results():
"""
Clears the temporary directory containing the last validation run's results.
"""
temp_dir = current_app.config.get('VALIDATION_TEMP_DIR')
if temp_dir and os.path.exists(temp_dir):
try:
shutil.rmtree(temp_dir)
current_app.config['VALIDATION_TEMP_DIR'] = None
return jsonify({"status": "success", "message": f"Results cleared successfully."})
except Exception as e:
logger.error(f"Failed to clear validation results at {temp_dir}: {e}")
return jsonify({"status": "error", "message": f"Failed to clear results: {e}"}), 500
else:
return jsonify({"status": "success", "message": "No results to clear."})
@app.route('/favicon.ico') @app.route('/favicon.ico')
def favicon(): def favicon():
@ -3072,4 +3402,4 @@ if __name__ == '__main__':
logger.debug(f"Attempting to create database tables for URI: {app.config['SQLALCHEMY_DATABASE_URI']}") logger.debug(f"Attempting to create database tables for URI: {app.config['SQLALCHEMY_DATABASE_URI']}")
db.create_all() db.create_all()
logger.info("Database tables created successfully (if they didn't exist).") logger.info("Database tables created successfully (if they didn't exist).")
app.run(host='0.0.0.0', port=5000, debug=False) app.run(host='0.0.0.0', port=5000, debug=False)

1
charts/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/hapi-fhir-jpaserver-0.20.0.tgz

View File

@ -0,0 +1 @@
/rendered/

View File

@ -0,0 +1,16 @@
apiVersion: v2
name: fhirflare-ig-toolkit
version: 0.5.0
description: Helm chart for deploying the fhirflare-ig-toolkit application
type: application
appVersion: "latest"
icon: https://github.com/jgsuess/FHIRFLARE-IG-Toolkit/raw/main/static/FHIRFLARE.png
keywords:
- fhir
- healthcare
- ig-toolkit
- implementation-guide
home: https://github.com/jgsuess/FHIRFLARE-IG-Toolkit
maintainers:
- name: Jörn Guy Süß
email: jgsuess@gmail.com

View File

@ -0,0 +1,152 @@
{{/*
Expand the name of the chart.
*/}}
{{- define "fhirflare-ig-toolkit.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "fhirflare-ig-toolkit.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "fhirflare-ig-toolkit.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Common labels
*/}}
{{- define "fhirflare-ig-toolkit.labels" -}}
helm.sh/chart: {{ include "fhirflare-ig-toolkit.chart" . }}
{{ include "fhirflare-ig-toolkit.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{/*
Selector labels
*/}}
{{- define "fhirflare-ig-toolkit.selectorLabels" -}}
app.kubernetes.io/name: {{ include "fhirflare-ig-toolkit.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
{{/*
Create the name of the service account to use
*/}}
{{- define "hapi-fhir-jpaserver.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "hapi-fhir-jpaserver.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}
{{/*
Create a default fully qualified postgresql name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
*/}}
{{- define "hapi-fhir-jpaserver.postgresql.fullname" -}}
{{- $name := default "postgresql" .Values.postgresql.nameOverride -}}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Get the Postgresql credentials secret name.
*/}}
{{- define "hapi-fhir-jpaserver.postgresql.secretName" -}}
{{- if .Values.postgresql.enabled -}}
{{- if .Values.postgresql.auth.existingSecret -}}
{{- printf "%s" .Values.postgresql.auth.existingSecret -}}
{{- else -}}
{{- printf "%s" (include "hapi-fhir-jpaserver.postgresql.fullname" .) -}}
{{- end -}}
{{- else }}
{{- if .Values.externalDatabase.existingSecret -}}
{{- printf "%s" .Values.externalDatabase.existingSecret -}}
{{- else -}}
{{ printf "%s-%s" (include "hapi-fhir-jpaserver.fullname" .) "external-db" }}
{{- end -}}
{{- end -}}
{{- end -}}
{{/*
Get the Postgresql credentials secret key.
*/}}
{{- define "hapi-fhir-jpaserver.postgresql.secretKey" -}}
{{- if .Values.postgresql.enabled -}}
{{- if .Values.postgresql.auth.username -}}
{{- printf "%s" .Values.postgresql.auth.secretKeys.userPasswordKey -}}
{{- else -}}
{{- printf "%s" .Values.postgresql.auth.secretKeys.adminPasswordKey -}}
{{- end -}}
{{- else }}
{{- if .Values.externalDatabase.existingSecret -}}
{{- printf "%s" .Values.externalDatabase.existingSecretKey -}}
{{- else -}}
{{- printf "postgres-password" -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{/*
Add environment variables to configure database values
*/}}
{{- define "hapi-fhir-jpaserver.database.host" -}}
{{- ternary (include "hapi-fhir-jpaserver.postgresql.fullname" .) .Values.externalDatabase.host .Values.postgresql.enabled -}}
{{- end -}}
{{/*
Add environment variables to configure database values
*/}}
{{- define "hapi-fhir-jpaserver.database.user" -}}
{{- if .Values.postgresql.enabled -}}
{{- printf "%s" .Values.postgresql.auth.username | default "postgres" -}}
{{- else -}}
{{- printf "%s" .Values.externalDatabase.user -}}
{{- end -}}
{{- end -}}
{{/*
Add environment variables to configure database values
*/}}
{{- define "hapi-fhir-jpaserver.database.name" -}}
{{- ternary .Values.postgresql.auth.database .Values.externalDatabase.database .Values.postgresql.enabled -}}
{{- end -}}
{{/*
Add environment variables to configure database values
*/}}
{{- define "hapi-fhir-jpaserver.database.port" -}}
{{- ternary "5432" .Values.externalDatabase.port .Values.postgresql.enabled -}}
{{- end -}}
{{/*
Create the JDBC URL from the host, port and database name.
*/}}
{{- define "hapi-fhir-jpaserver.database.jdbcUrl" -}}
{{- $host := (include "hapi-fhir-jpaserver.database.host" .) -}}
{{- $port := (include "hapi-fhir-jpaserver.database.port" .) -}}
{{- $name := (include "hapi-fhir-jpaserver.database.name" .) -}}
{{- $appName := .Release.Name -}}
{{ printf "jdbc:postgresql://%s:%d/%s?ApplicationName=%s" $host (int $port) $name $appName }}
{{- end -}}

View File

@ -0,0 +1,91 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "fhirflare-ig-toolkit.fullname" . }}
labels:
{{- include "fhirflare-ig-toolkit.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount | default 1 }}
selector:
matchLabels:
{{- include "fhirflare-ig-toolkit.selectorLabels" . | nindent 6 }}
strategy:
type: Recreate
template:
metadata:
labels:
{{- include "fhirflare-ig-toolkit.selectorLabels" . | nindent 8 }}
{{- with .Values.podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
securityContext:
{{- toYaml .Values.podSecurityContext | nindent 8 }}
containers:
- name: {{ .Chart.Name }}
securityContext:
{{- toYaml .Values.securityContext | nindent 12 }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
args: ["supervisord", "-c", "/etc/supervisord.conf"]
env:
- name: APP_BASE_URL
value: {{ .Values.config.appBaseUrl | default "http://localhost:5000" | quote }}
- name: APP_MODE
value: {{ .Values.config.appMode | default "lite" | quote }}
- name: FLASK_APP
value: {{ .Values.config.flaskApp | default "app.py" | quote }}
- name: FLASK_ENV
value: {{ .Values.config.flaskEnv | default "development" | quote }}
- name: HAPI_FHIR_URL
value: {{ .Values.config.externalHapiServerUrl | default "http://external-hapi-fhir:8080/fhir" | quote }}
- name: NODE_PATH
value: {{ .Values.config.nodePath | default "/usr/lib/node_modules" | quote }}
- name: TMPDIR
value: "/tmp-dir"
ports:
- name: http
containerPort: {{ .Values.service.port | default 5000 }}
protocol: TCP
volumeMounts:
- name: logs
mountPath: /app/logs
- name: tmp-dir
mountPath: /tmp-dir
{{- with .Values.resources }}
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.livenessProbe }}
livenessProbe:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.readinessProbe }}
readinessProbe:
{{- toYaml . | nindent 12 }}
{{- end }}
volumes:
- name: logs
emptyDir: {}
- name: tmp-dir
emptyDir: {}
# Always require Intel 64-bit architecture nodes
nodeSelector:
kubernetes.io/arch: amd64
{{- with .Values.nodeSelector }}
# Merge with user-defined nodeSelectors if any
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}

View File

@ -0,0 +1,36 @@
{{- if .Values.ingress.enabled -}}
{{- $fullName := include "fhirflare-ig-toolkit.fullname" . -}}
{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion }}
apiVersion: networking.k8s.io/v1
{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion }}
apiVersion: networking.k8s.io/v1beta1
{{- else }}
apiVersion: extensions/v1beta1
{{- end }}
kind: Ingress
metadata:
name: {{ $fullName }}
labels:
{{- include "fhirflare-ig-toolkit.labels" . | nindent 4 }}
{{- with .Values.ingress.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
rules:
- http:
paths:
- path: /
{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion }}
pathType: Prefix
backend:
service:
name: {{ $fullName }}
port:
number: {{ .Values.service.port | default 5000 }}
{{- else }}
backend:
serviceName: {{ $fullName }}
servicePort: {{ .Values.service.port | default 5000 }}
{{- end }}
{{- end }}

View File

@ -0,0 +1,18 @@
apiVersion: v1
kind: Service
metadata:
name: {{ include "fhirflare-ig-toolkit.fullname" . }}
labels:
{{- include "fhirflare-ig-toolkit.labels" . | nindent 4 }}
spec:
type: {{ .Values.service.type | default "ClusterIP" }}
ports:
- name: http
port: {{ .Values.service.port | default 5000 }}
targetPort: {{ .Values.service.port | default 5000 }}
protocol: TCP
{{- if and (eq .Values.service.type "NodePort") .Values.service.nodePort }}
nodePort: {{ .Values.service.nodePort }}
{{- end }}
selector:
{{- include "fhirflare-ig-toolkit.selectorLabels" . | nindent 4 }}

View File

@ -0,0 +1,41 @@
apiVersion: v1
kind: Pod
metadata:
name: "{{ .Release.Name }}-fhirflare-test-endpoint"
labels:
helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
app.kubernetes.io/name: {{ .Chart.Name }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
app.kubernetes.io/component: tests
annotations:
"helm.sh/hook": test
spec:
restartPolicy: Never
containers:
- name: test-fhirflare-endpoint
image: curlimages/curl:8.12.1
command: ["curl", "--fail-with-body", "--retry", "5", "--retry-delay", "10"]
args: ["http://fhirflare:5000"]
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
privileged: false
readOnlyRootFilesystem: true
runAsGroup: 65534
runAsNonRoot: true
runAsUser: 65534
seccompProfile:
type: RuntimeDefault
resources:
limits:
cpu: 150m
ephemeral-storage: 2Gi
memory: 192Mi
requests:
cpu: 100m
ephemeral-storage: 50Mi
memory: 128Mi

View File

@ -0,0 +1,89 @@
# Default values for fhirflare-ig-toolkit
replicaCount: 1
image:
repository: ghcr.io/jgsuess/fhirflare-ig-toolkit
pullPolicy: Always
tag: "latest"
imagePullSecrets: []
nameOverride: ""
fullnameOverride: ""
# FHIRflare specific configuration
config:
# Application mode: "lite" means using external HAPI server, "standalone" means running with embedded HAPI server
appMode: "lite"
# URL for the external HAPI FHIR server when in lite mode
externalHapiServerUrl: "http://external-hapi-fhir:8080/fhir"
appBaseUrl: "http://localhost:5000"
flaskApp: "app.py"
flaskEnv: "development"
nodePath: "/usr/lib/node_modules"
service:
type: ClusterIP
port: 5000
nodePort: null
podAnnotations: {}
# podSecurityContext:
# fsGroup: 65532
# fsGroupChangePolicy: OnRootMismatch
# runAsNonRoot: true
# runAsGroup: 65532
# runAsUser: 65532
# seccompProfile:
# type: RuntimeDefault
# securityContext:
# allowPrivilegeEscalation: false
# capabilities:
# drop:
# - ALL
# privileged: false
# readOnlyRootFilesystem: true
# runAsGroup: 65532
# runAsNonRoot: true
# runAsUser: 65532
# seccompProfile:
# type: RuntimeDefault
resources:
limits:
cpu: 500m
memory: 512Mi
ephemeral-storage: 1Gi
requests:
cpu: 100m
memory: 128Mi
ephemeral-storage: 100Mi
livenessProbe:
httpGet:
path: /
port: http
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 6
successThreshold: 1
readinessProbe:
httpGet:
path: /
port: http
initialDelaySeconds: 5
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 6
successThreshold: 1
nodeSelector: {}
tolerations: []
affinity: {}
ingress:
# -- whether to create a primitive Ingress to expose the FHIR server HTTP endpoint
enabled: false

23
charts/install.sh Executable file
View File

@ -0,0 +1,23 @@
#!/bin/bash
#
# FHIRFLARE-IG-Toolkit Installation Script
#
# Description:
# This script installs the FHIRFLARE-IG-Toolkit Helm chart into a Kubernetes cluster.
# It adds the FHIRFLARE-IG-Toolkit Helm repository and then installs the chart
# in the 'flare' namespace, creating the namespace if it doesn't exist.
#
# Usage:
# ./install.sh
#
# Requirements:
# - Helm (v3+)
# - kubectl configured with access to your Kubernetes cluster
#
# Add the FHIRFLARE-IG-Toolkit Helm repository
helm repo add flare https://jgsuess.github.io/FHIRFLARE-IG-Toolkit/
# Install the FHIRFLARE-IG-Toolkit chart in the 'flare' namespace
helm install flare/fhirflare-ig-toolkit --namespace flare --create-namespace --generate-name --set hapi-fhir-jpaserver.postgresql.primary.persistence.storageClass=gp2 --atomic

View File

@ -3,14 +3,13 @@ services:
fhirflare: fhirflare:
build: build:
context: . context: .
dockerfile: Dockerfile dockerfile: Dockerfile.lite
ports: ports:
- "5000:5000" - "5000:5000"
- "8080:8080" # Keep port exposed, even if Tomcat isn't running useful stuff in Lite - "8080:8080"
volumes: volumes:
- ./instance:/app/instance - ./instance:/app/instance
- ./static/uploads:/app/static/uploads - ./static/uploads:/app/static/uploads
- ./instance/hapi-h2-data/:/app/h2-data # Keep volume mounts consistent
- ./logs:/app/logs - ./logs:/app/logs
environment: environment:
- FLASK_APP=app.py - FLASK_APP=app.py
@ -18,5 +17,5 @@ services:
- NODE_PATH=/usr/lib/node_modules - NODE_PATH=/usr/lib/node_modules
- APP_MODE=standalone - APP_MODE=standalone
- APP_BASE_URL=http://localhost:5000 - APP_BASE_URL=http://localhost:5000
- HAPI_FHIR_URL=http://localhost:8080/fhir - HAPI_FHIR_URL=https://smile.sparked-fhir.com/aucore/fhir/DEFAULT/
command: supervisord -c /etc/supervisord.conf command: supervisord -c /etc/supervisord.conf

View File

@ -0,0 +1,22 @@
# This docker-compose file uses ephemeral Docker named volumes for all data storage.
# These volumes persist only as long as the Docker volumes exist and are deleted if you run `docker-compose down -v`.
# No data is stored on the host filesystem. If you want persistent storage, replace these with host mounts.
services:
fhirflare-standalone:
image: ${FHIRFLARE_IMAGE:-ghcr.io/sudo-jhare/fhirflare-ig-toolkit-standalone:latest}
container_name: fhirflare-standalone
ports:
- "5000:5000"
- "8080:8080"
volumes:
- fhirflare-instance:/app/instance
- fhirflare-uploads:/app/static/uploads
- fhirflare-h2-data:/app/h2-data
- fhirflare-logs:/app/logs
restart: unless-stopped
volumes:
fhirflare-instance:
fhirflare-uploads:
fhirflare-h2-data:
fhirflare-logs:

View File

@ -0,0 +1,5 @@
#!/bin/bash
# Stop and remove all containers defined in the Docker Compose file,
# along with any anonymous volumes attached to them.
docker compose down --volumes

View File

@ -0,0 +1,5 @@
#!/bin/bash
# Run Docker Compose
docker compose up --detach --force-recreate --renew-anon-volumes --always-recreate-deps

View File

@ -0,0 +1,18 @@
hapi.fhir:
ig_runtime_upload_enabled: false
narrative_enabled: true
logical_urls:
- http://terminology.hl7.org/*
- https://terminology.hl7.org/*
- http://snomed.info/*
- https://snomed.info/*
- http://unitsofmeasure.org/*
- https://unitsofmeasure.org/*
- http://loinc.org/*
- https://loinc.org/*
cors:
allow_Credentials: true
allowed_origin:
- '*'
tester.home.name: FHIRFLARE Tester
inline_resource_storage_below_size: 4000

View File

@ -0,0 +1,50 @@
services:
fhirflare:
image: ${FHIRFLARE_IMAGE:-ghcr.io/sudo-jhare/fhirflare-ig-toolkit-lite:latest}
ports:
- "5000:5000"
# Ephemeral Docker named volumes for all data storage. No data is stored on the host filesystem.
volumes:
- fhirflare-instance:/app/instance
- fhirflare-uploads:/app/static/uploads
- fhirflare-h2-data:/app/h2-data
- fhirflare-logs:/app/logs
environment:
- FLASK_APP=app.py
- FLASK_ENV=development
- NODE_PATH=/usr/lib/node_modules
- APP_MODE=lite
- APP_BASE_URL=http://localhost:5000
- HAPI_FHIR_URL=http://fhir:8080/fhir
command: supervisord -c /etc/supervisord.conf
fhir:
container_name: hapi
image: "hapiproject/hapi:v8.2.0-1"
ports:
- "8080:8080"
configs:
- source: hapi
target: /app/config/application.yaml
depends_on:
- db
db:
image: "postgres:17.2-bookworm"
restart: always
environment:
POSTGRES_PASSWORD: admin
POSTGRES_USER: admin
POSTGRES_DB: hapi
volumes:
- ./hapi.postgress.data:/var/lib/postgresql/data
configs:
hapi:
file: ./application.yaml
volumes:
fhirflare-instance:
fhirflare-uploads:
fhirflare-h2-data:
fhirflare-logs:

View File

@ -0,0 +1,5 @@
#!/bin/bash
# Stop and remove all containers defined in the Docker Compose file,
# along with any anonymous volumes attached to them.
docker compose down --volumes

View File

@ -0,0 +1,19 @@
# FHIRFLARE IG Toolkit
This directory provides scripts and configuration to start and stop a FHIRFLARE instance with an attached HAPI FHIR server using Docker Compose.
## Usage
- To start the FHIRFLARE toolkit and HAPI server:
```sh
./docker-compose/up.sh
```
- To stop and remove the containers and volumes:
```sh
./docker-compose/down.sh
```
The web interface will be available at [http://localhost:5000](http://localhost:5000) and the HAPI FHIR server at [http://localhost:8080/fhir](http://localhost:8080/fhir).
For more details, see the configuration files in this directory.

View File

@ -0,0 +1,5 @@
#!/bin/bash
# Run Docker Compose
docker compose up --detach --force-recreate --renew-anon-volumes --always-recreate-deps

View File

@ -0,0 +1,25 @@
services:
fhirflare:
image: ${FHIRFLARE_IMAGE:-ghcr.io/sudo-jhare/fhirflare-ig-toolkit-lite:latest}
ports:
- "5000:5000"
# Ephemeral Docker named volumes for all data storage. No data is stored on the host filesystem.
volumes:
- fhirflare-instance:/app/instance
- fhirflare-uploads:/app/static/uploads
- fhirflare-h2-data:/app/h2-data
- fhirflare-logs:/app/logs
environment:
- FLASK_APP=app.py
- FLASK_ENV=development
- NODE_PATH=/usr/lib/node_modules
- APP_MODE=lite
- APP_BASE_URL=http://localhost:5000
- HAPI_FHIR_URL=https://cdr.fhirlab.net/fhir
command: supervisord -c /etc/supervisord.conf
volumes:
fhirflare-instance:
fhirflare-uploads:
fhirflare-h2-data:
fhirflare-logs:

View File

@ -0,0 +1,5 @@
#!/bin/bash
# Stop and remove all containers defined in the Docker Compose file,
# along with any anonymous volumes attached to them.
docker compose down --volumes

View File

@ -0,0 +1,19 @@
# FHIRFLARE IG Toolkit
This directory provides scripts and configuration to start and stop a FHIRFLARE instance with an attached HAPI FHIR server using Docker Compose.
## Usage
- To start the FHIRFLARE toolkit and HAPI server:
```sh
./docker-compose/up.sh
```
- To stop and remove the containers and volumes:
```sh
./docker-compose/down.sh
```
The web interface will be available at [http://localhost:5000](http://localhost:5000) and the HAPI FHIR server at [http://localhost:8080/fhir](http://localhost:8080/fhir).
For more details, see the configuration files in this directory.

View File

@ -0,0 +1,5 @@
#!/bin/bash
# Run Docker Compose
docker compose up --detach --force-recreate --renew-anon-volumes --always-recreate-deps

66
docker/Dockerfile Normal file
View File

@ -0,0 +1,66 @@
# ------------------------------------------------------------------------------
# Dockerfile for FHIRFLARE-IG-Toolkit (Optimized for Python/Flask)
#
# This Dockerfile builds a container for the FHIRFLARE-IG-Toolkit application.
#
# Key Features:
# - Uses python:3.11-slim as the base image for a minimal, secure Python runtime.
# - Installs Node.js and global NPM packages (gofsh, fsh-sushi) for FHIR IG tooling.
# - Sets up a Python virtual environment and installs all Python dependencies.
# - Installs and configures Supervisor to manage the Flask app and related processes.
# - Copies all necessary application code, templates, static files, and configuration.
# - Exposes ports 5000 (Flask) and 8080 (optional, for compatibility).
# - Entrypoint runs Supervisor for process management.
#
# Notes:
# - The Dockerfile is optimized for Python. Tomcat/Java is not included.
# - Node.js is only installed if needed for FHIR IG tooling.
# - The image is suitable for development and production with minimal changes.
# ------------------------------------------------------------------------------
# Optimized Dockerfile for Python (Flask)
FROM python:3.11-slim AS base
# Install system dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \
curl \
coreutils \
&& rm -rf /var/lib/apt/lists/*
# Optional: Install Node.js if needed for GoFSH/SUSHI
RUN curl -fsSL https://deb.nodesource.com/setup_18.x | bash - \
&& apt-get install -y --no-install-recommends nodejs \
&& npm install -g gofsh fsh-sushi \
&& rm -rf /var/lib/apt/lists/*
# Set workdir
WORKDIR /app
# Copy requirements and install Python dependencies
COPY requirements.txt .
RUN python -m venv /app/venv \
&& . /app/venv/bin/activate \
&& pip install --upgrade pip \
&& pip install --no-cache-dir -r requirements.txt \
&& pip uninstall -y fhirpath || true \
&& pip install --no-cache-dir fhirpathpy \
&& pip install supervisor
# Copy application files
COPY app.py .
COPY services.py .
COPY forms.py .
COPY package.py .
COPY templates/ templates/
COPY static/ static/
COPY tests/ tests/
COPY supervisord.conf /etc/supervisord.conf
# Expose ports
EXPOSE 5000 8080
# Set environment
ENV PATH="/app/venv/bin:$PATH"
# Start supervisord
CMD ["supervisord", "-c", "/etc/supervisord.conf"]

7
docker/build-docker.sh Executable file
View File

@ -0,0 +1,7 @@
#!/bin/bash
# Build FHIRFLARE-IG-Toolkit Docker image
# Build the image using the Dockerfile in the docker directory
docker build -f Dockerfile -t fhirflare-ig-toolkit:latest ..
echo "Docker image built successfully"

View File

@ -1,6 +1,6 @@
# forms.py # forms.py
from flask_wtf import FlaskForm from flask_wtf import FlaskForm
from wtforms import StringField, SelectField, TextAreaField, BooleanField, SubmitField, FileField, PasswordField from wtforms import StringField, SelectField, TextAreaField, BooleanField, SubmitField, FileField, PasswordField, SelectMultipleField
from wtforms.validators import DataRequired, Regexp, ValidationError, URL, Optional, InputRequired from wtforms.validators import DataRequired, Regexp, ValidationError, URL, Optional, InputRequired
from flask import request from flask import request
import json import json
@ -306,4 +306,64 @@ class FhirRequestForm(FlaskForm):
if not self.basic_auth_password.data: if not self.basic_auth_password.data:
self.basic_auth_password.errors.append('Password is required for Basic Authentication with a custom URL.') self.basic_auth_password.errors.append('Password is required for Basic Authentication with a custom URL.')
return False return False
return True return True
class IgYamlForm(FlaskForm):
"""Form to select IGs for YAML generation."""
igs = SelectMultipleField('Select IGs to include', validators=[DataRequired()])
generate_yaml = SubmitField('Generate YAML')
# --- New ValidationForm class for the new page ---
class ValidationForm(FlaskForm):
"""Form for validating a single FHIR resource with various options."""
# Added fields to match the HTML template
package_name = StringField('Package Name', validators=[Optional()])
version = StringField('Package Version', validators=[Optional()])
# Main content fields
fhir_resource = TextAreaField('FHIR Resource (JSON or XML)', validators=[InputRequired()],
render_kw={'placeholder': 'Paste your FHIR JSON here...'})
fhir_version = SelectField('FHIR Version', choices=[
('4.0.1', 'R4 (4.0.1)'),
('3.0.2', 'STU3 (3.0.2)'),
('5.0.0', 'R5 (5.0.0)')
], default='4.0.1', validators=[DataRequired()])
# Flags and options from validator settings.pdf
do_native = BooleanField('Native Validation (doNative)')
hint_about_must_support = BooleanField('Must Support (hintAboutMustSupport)')
assume_valid_rest_references = BooleanField('Assume Valid Rest References (assumeValidRestReferences)')
no_extensible_binding_warnings = BooleanField('Extensible Binding Warnings (noExtensibleBindingWarnings)')
show_times = BooleanField('Show Times (-show-times)')
allow_example_urls = BooleanField('Allow Example URLs (-allow-example-urls)')
check_ips_codes = BooleanField('Check IPS Codes (-check-ips-codes)')
allow_any_extensions = BooleanField('Allow Any Extensions (-allow-any-extensions)')
tx_routing = BooleanField('Show Terminology Routing (-tx-routing)')
# SNOMED CT options
snomed_ct_version = SelectField('Select SNOMED Version', choices=[
('', '-- No selection --'),
('intl', 'International edition (900000000000207008)'),
('us', 'US edition (731000124108)'),
('uk', 'United Kingdom Edition (999000041000000102)'),
('es', 'Spanish Language Edition (449081005)'),
('nl', 'Netherlands Edition (11000146104)'),
('ca', 'Canadian Edition (20611000087101)'),
('dk', 'Danish Edition (554471000005108)'),
('se', 'Swedish Edition (45991000052106)'),
('au', 'Australian Edition (32506021000036107)'),
('be', 'Belgium Edition (11000172109)')
], validators=[Optional()])
# Text input fields
profiles = StringField('Profiles',
validators=[Optional()],
render_kw={'placeholder': 'e.g. http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient'})
extensions = StringField('Extensions',
validators=[Optional()],
render_kw={'placeholder': 'e.g. http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace'})
terminology_server = StringField('Terminology Server',
validators=[Optional()],
render_kw={'placeholder': 'e.g. http://tx.fhir.org'})
submit = SubmitField('Validate')

Binary file not shown.

Binary file not shown.

View File

@ -1,42 +0,0 @@
{
"package_name": "example.fhir.ph.core.r4",
"version": "0.1.0",
"dependency_mode": "recursive",
"imported_dependencies": [
{
"name": "hl7.fhir.r4.core",
"version": "4.0.1"
},
{
"name": "hl7.terminology.r4",
"version": "6.4.0"
},
{
"name": "hl7.fhir.uv.extensions.r4",
"version": "5.2.0"
},
{
"name": "hl7.fhir.r4.core",
"version": "4.0.1"
},
{
"name": "hl7.fhir.uv.extensions.r4",
"version": "5.2.0"
},
{
"name": "hl7.fhir.r4.core",
"version": "4.0.1"
},
{
"name": "hl7.fhir.r4.core",
"version": "4.0.1"
},
{
"name": "hl7.fhir.r4.core",
"version": "4.0.1"
}
],
"complies_with_profiles": [],
"imposed_profiles": [],
"timestamp": "2025-08-01T00:48:13.000931+00:00"
}

View File

@ -1,22 +0,0 @@
{
"package_name": "hl7.fhir.au.base",
"version": "5.1.0-preview",
"dependency_mode": "recursive",
"imported_dependencies": [
{
"name": "hl7.fhir.r4.core",
"version": "4.0.1"
},
{
"name": "hl7.terminology.r4",
"version": "6.2.0"
},
{
"name": "hl7.fhir.uv.extensions.r4",
"version": "5.2.0"
}
],
"complies_with_profiles": [],
"imposed_profiles": [],
"timestamp": "2025-05-04T12:29:17.475734+00:00"
}

View File

@ -1,34 +0,0 @@
{
"package_name": "hl7.fhir.au.core",
"version": "1.1.0-preview",
"dependency_mode": "recursive",
"imported_dependencies": [
{
"name": "hl7.fhir.r4.core",
"version": "4.0.1"
},
{
"name": "hl7.terminology.r4",
"version": "6.2.0"
},
{
"name": "hl7.fhir.uv.extensions.r4",
"version": "5.2.0"
},
{
"name": "hl7.fhir.au.base",
"version": "5.1.0-preview"
},
{
"name": "hl7.fhir.uv.smart-app-launch",
"version": "2.1.0"
},
{
"name": "hl7.fhir.uv.ipa",
"version": "1.0.0"
}
],
"complies_with_profiles": [],
"imposed_profiles": [],
"timestamp": "2025-05-04T12:29:15.067826+00:00"
}

View File

@ -1,9 +0,0 @@
{
"package_name": "hl7.fhir.r4.core",
"version": "4.0.1",
"dependency_mode": "recursive",
"imported_dependencies": [],
"complies_with_profiles": [],
"imposed_profiles": [],
"timestamp": "2025-08-01T00:48:12.995792+00:00"
}

View File

@ -1,14 +0,0 @@
{
"package_name": "hl7.fhir.uv.extensions.r4",
"version": "5.2.0",
"dependency_mode": "recursive",
"imported_dependencies": [
{
"name": "hl7.fhir.r4.core",
"version": "4.0.1"
}
],
"complies_with_profiles": [],
"imposed_profiles": [],
"timestamp": "2025-08-01T00:48:12.196313+00:00"
}

View File

@ -1,22 +0,0 @@
{
"package_name": "hl7.fhir.uv.ipa",
"version": "1.0.0",
"dependency_mode": "recursive",
"imported_dependencies": [
{
"name": "hl7.fhir.r4.core",
"version": "4.0.1"
},
{
"name": "hl7.terminology.r4",
"version": "5.0.0"
},
{
"name": "hl7.fhir.uv.smart-app-launch",
"version": "2.0.0"
}
],
"complies_with_profiles": [],
"imposed_profiles": [],
"timestamp": "2025-05-04T12:29:17.590266+00:00"
}

View File

@ -1,14 +0,0 @@
{
"package_name": "hl7.fhir.uv.smart-app-launch",
"version": "2.0.0",
"dependency_mode": "recursive",
"imported_dependencies": [
{
"name": "hl7.fhir.r4.core",
"version": "4.0.1"
}
],
"complies_with_profiles": [],
"imposed_profiles": [],
"timestamp": "2025-05-04T12:29:18.256800+00:00"
}

View File

@ -1,18 +0,0 @@
{
"package_name": "hl7.fhir.uv.smart-app-launch",
"version": "2.1.0",
"dependency_mode": "recursive",
"imported_dependencies": [
{
"name": "hl7.fhir.r4.core",
"version": "4.0.1"
},
{
"name": "hl7.terminology.r4",
"version": "5.0.0"
}
],
"complies_with_profiles": [],
"imposed_profiles": [],
"timestamp": "2025-05-04T12:29:17.529611+00:00"
}

View File

@ -1,14 +0,0 @@
{
"package_name": "hl7.terminology.r4",
"version": "5.0.0",
"dependency_mode": "recursive",
"imported_dependencies": [
{
"name": "hl7.fhir.r4.core",
"version": "4.0.1"
}
],
"complies_with_profiles": [],
"imposed_profiles": [],
"timestamp": "2025-05-04T12:29:18.216757+00:00"
}

View File

@ -1,14 +0,0 @@
{
"package_name": "hl7.terminology.r4",
"version": "6.2.0",
"dependency_mode": "recursive",
"imported_dependencies": [
{
"name": "hl7.fhir.r4.core",
"version": "4.0.1"
}
],
"complies_with_profiles": [],
"imposed_profiles": [],
"timestamp": "2025-05-04T12:29:17.148041+00:00"
}

View File

@ -1,22 +0,0 @@
{
"package_name": "hl7.terminology.r4",
"version": "6.4.0",
"dependency_mode": "recursive",
"imported_dependencies": [
{
"name": "hl7.fhir.r4.core",
"version": "4.0.1"
},
{
"name": "hl7.fhir.uv.extensions.r4",
"version": "5.2.0"
},
{
"name": "hl7.fhir.r4.core",
"version": "4.0.1"
}
],
"complies_with_profiles": [],
"imposed_profiles": [],
"timestamp": "2025-08-01T00:48:11.281867+00:00"
}

View File

@ -1,740 +0,0 @@
2025-08-01 00:44:39,706 - __main__ - INFO - --- File logging initialized to /app/instance/fhirflare_debug.log (Level: DEBUG) ---
2025-08-01 00:44:39,707 - __main__ - INFO - Application running in mode: standalone
2025-08-01 00:44:39,708 - __main__ - DEBUG - Instance path configuration: /app/instance
2025-08-01 00:44:39,709 - __main__ - DEBUG - Database URI: sqlite:////app/instance/fhir_ig.db
2025-08-01 00:44:39,710 - __main__ - DEBUG - Packages path: /app/instance/fhir_packages
2025-08-01 00:44:39,724 - __main__ - DEBUG - Flask instance folder path: /app/instance
2025-08-01 00:44:39,731 - __main__ - DEBUG - Directories created/verified: Instance: /app/instance, Packages: /app/instance/fhir_packages
2025-08-01 00:44:39,759 - __main__ - DEBUG - Attempting to create database tables for URI: sqlite:////app/instance/fhir_ig.db
2025-08-01 00:44:39,797 - __main__ - INFO - Database tables created/verified successfully.
2025-08-01 00:44:39,804 - __main__ - DEBUG - Instance path configuration: /app/instance
2025-08-01 00:44:39,806 - __main__ - DEBUG - Database URI: sqlite:////app/instance/fhir_ig.db
2025-08-01 00:44:39,807 - __main__ - DEBUG - Packages path: /app/instance/fhir_packages
2025-08-01 00:44:39,808 - __main__ - DEBUG - Flask instance folder path: /app/instance
2025-08-01 00:44:39,811 - __main__ - DEBUG - Directories created/verified: Instance: /app/instance, Packages: /app/instance/fhir_packages
2025-08-01 00:44:39,812 - __main__ - DEBUG - Attempting to create database tables for URI: sqlite:////app/instance/fhir_ig.db
2025-08-01 00:44:39,822 - __main__ - INFO - Database tables created successfully (if they didn't exist).
2025-08-01 00:44:39,825 - werkzeug - INFO - WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on all addresses (0.0.0.0)
* Running on http://127.0.0.1:5000
* Running on http://172.18.0.2:5000
2025-08-01 00:44:39,826 - werkzeug - INFO - Press CTRL+C to quit
2025-08-01 00:45:00,413 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:45:00] "GET / HTTP/1.1" 200 -
2025-08-01 00:45:00,466 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:45:00] "GET /static/FHIRFLARE.png HTTP/1.1" 200 -
2025-08-01 00:45:00,481 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:45:00] "GET /static/css/fire-animation.css HTTP/1.1" 304 -
2025-08-01 00:45:00,493 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:45:00] "GET /static/js/htmx.min.js HTTP/1.1" 304 -
2025-08-01 00:45:00,539 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:45:00] "GET /static/css/animation.css HTTP/1.1" 304 -
2025-08-01 00:45:01,469 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:45:01] "GET /static/favicon.ico HTTP/1.1" 200 -
2025-08-01 00:45:06,325 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:45:06] "GET /manual-import-ig HTTP/1.1" 200 -
2025-08-01 00:45:06,344 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:45:06] "GET /static/css/fire-animation.css HTTP/1.1" 304 -
2025-08-01 00:45:06,348 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:45:06] "GET /static/css/animation.css HTTP/1.1" 304 -
2025-08-01 00:45:06,349 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:45:06] "GET /static/js/htmx.min.js HTTP/1.1" 304 -
2025-08-01 00:47:18,005 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:47:18] "GET /validate-sample HTTP/1.1" 200 -
2025-08-01 00:47:18,023 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:47:18] "GET /static/css/fire-animation.css HTTP/1.1" 304 -
2025-08-01 00:47:18,026 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:47:18] "GET /static/css/animation.css HTTP/1.1" 304 -
2025-08-01 00:47:18,030 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:47:18] "GET /static/js/htmx.min.js HTTP/1.1" 304 -
2025-08-01 00:47:18,034 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:47:18] "GET /static/FHIRFLARE.png HTTP/1.1" 304 -
2025-08-01 00:47:27,948 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:47:27] "GET /manual-import-ig HTTP/1.1" 200 -
2025-08-01 00:47:27,969 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:47:27] "GET /static/css/fire-animation.css HTTP/1.1" 304 -
2025-08-01 00:47:27,970 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:47:27] "GET /static/css/animation.css HTTP/1.1" 304 -
2025-08-01 00:47:27,972 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:47:27] "GET /static/js/htmx.min.js HTTP/1.1" 304 -
2025-08-01 00:47:43,826 - services - INFO - Starting manual import for /tmp/tmpo2f3s9g5/example.fhir.ph.core.r4-0.1.0.tgz (mode=recursive, resolve_deps=True)
2025-08-01 00:47:43,827 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:47:43,831 - services - DEBUG - Parsed 'example.fhir.ph.core.r4-0.1.0.tgz' -> name='example.fhir.ph.core.r4', version='0.1.0'
2025-08-01 00:47:43,841 - services - DEBUG - Parsed '/tmp/tmpo2f3s9g5/example.fhir.ph.core.r4-0.1.0.tgz' -> name='/tmp/tmpo2f3s9g5/example.fhir.ph.core.r4', version='0.1.0'
2025-08-01 00:47:43,842 - services - INFO - Manually processing package: example.fhir.ph.core.r4-0.1.0.tgz (/tmp/tmpo2f3s9g5/example.fhir.ph.core.r4#0.1.0)
2025-08-01 00:47:43,843 - services - INFO - Processing dependency hl7.fhir.r4.core#4.0.1
2025-08-01 00:47:43,844 - services - INFO - Starting manual import for hl7.fhir.r4.core (mode=recursive, resolve_deps=True)
2025-08-01 00:47:43,845 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:47:43,848 - services - INFO - Attempting manual download of hl7.fhir.r4.core#4.0.1
2025-08-01 00:47:43,850 - services - INFO - Manual package hl7.fhir.r4.core#4.0.1 already exists at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz
2025-08-01 00:47:43,852 - services - DEBUG - Parsed '/app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz' -> name='/app/instance/fhir.packages/hl7.fhir.r4.core', version='4.0.1'
2025-08-01 00:47:43,853 - services - INFO - Manually processing package: hl7.fhir.r4.core-4.0.1.tgz (/app/instance/fhir.packages/hl7.fhir.r4.core#4.0.1)
2025-08-01 00:47:44,379 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:47:44,388 - services - INFO - Saved metadata for hl7.fhir.r4.core#4.0.1 at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.metadata.json
2025-08-01 00:47:44,389 - services - INFO - Processing dependency hl7.terminology.r4#6.4.0
2025-08-01 00:47:44,390 - services - INFO - Starting manual import for hl7.terminology.r4 (mode=recursive, resolve_deps=True)
2025-08-01 00:47:44,391 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:47:44,393 - services - INFO - Attempting manual download of hl7.terminology.r4#6.4.0
2025-08-01 00:47:44,398 - urllib3.connectionpool - DEBUG - Starting new HTTPS connection (1): packages.fhir.org:443
2025-08-01 00:47:46,170 - urllib3.connectionpool - DEBUG - https://packages.fhir.org:443 "GET /hl7.terminology.r4/6.4.0 HTTP/1.1" 200 5053901
2025-08-01 00:47:49,116 - services - INFO - Manually downloaded hl7.terminology.r4#6.4.0 to /app/instance/fhir_packages/hl7.terminology.r4-6.4.0.tgz
2025-08-01 00:47:49,118 - services - DEBUG - Parsed '/app/instance/fhir_packages/hl7.terminology.r4-6.4.0.tgz' -> name='/app/instance/fhir.packages/hl7.terminology.r4', version='6.4.0'
2025-08-01 00:47:49,119 - services - INFO - Manually processing package: hl7.terminology.r4-6.4.0.tgz (/app/instance/fhir.packages/hl7.terminology.r4#6.4.0)
2025-08-01 00:47:49,132 - services - INFO - Processing dependency hl7.fhir.r4.core#4.0.1
2025-08-01 00:47:49,133 - services - INFO - Starting manual import for hl7.fhir.r4.core (mode=recursive, resolve_deps=True)
2025-08-01 00:47:49,133 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:47:49,135 - services - INFO - Attempting manual download of hl7.fhir.r4.core#4.0.1
2025-08-01 00:47:49,136 - services - INFO - Manual package hl7.fhir.r4.core#4.0.1 already exists at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz
2025-08-01 00:47:49,138 - services - DEBUG - Parsed '/app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz' -> name='/app/instance/fhir.packages/hl7.fhir.r4.core', version='4.0.1'
2025-08-01 00:47:49,138 - services - INFO - Manually processing package: hl7.fhir.r4.core-4.0.1.tgz (/app/instance/fhir.packages/hl7.fhir.r4.core#4.0.1)
2025-08-01 00:47:49,396 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:47:49,401 - services - INFO - Saved metadata for hl7.fhir.r4.core#4.0.1 at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.metadata.json
2025-08-01 00:47:49,401 - services - INFO - Processing dependency hl7.fhir.uv.extensions.r4#5.2.0
2025-08-01 00:47:49,402 - services - INFO - Starting manual import for hl7.fhir.uv.extensions.r4 (mode=recursive, resolve_deps=True)
2025-08-01 00:47:49,402 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:47:49,403 - services - INFO - Attempting manual download of hl7.fhir.uv.extensions.r4#5.2.0
2025-08-01 00:47:49,404 - services - INFO - Manual package hl7.fhir.uv.extensions.r4#5.2.0 already exists at /app/instance/fhir_packages/hl7.fhir.uv.extensions.r4-5.2.0.tgz
2025-08-01 00:47:49,406 - services - DEBUG - Parsed '/app/instance/fhir_packages/hl7.fhir.uv.extensions.r4-5.2.0.tgz' -> name='/app/instance/fhir.packages/hl7.fhir.uv.extensions.r4', version='5.2.0'
2025-08-01 00:47:49,406 - services - INFO - Manually processing package: hl7.fhir.uv.extensions.r4-5.2.0.tgz (/app/instance/fhir.packages/hl7.fhir.uv.extensions.r4#5.2.0)
2025-08-01 00:47:49,461 - services - INFO - Processing dependency hl7.fhir.r4.core#4.0.1
2025-08-01 00:47:49,462 - services - INFO - Starting manual import for hl7.fhir.r4.core (mode=recursive, resolve_deps=True)
2025-08-01 00:47:49,462 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:47:49,464 - services - INFO - Attempting manual download of hl7.fhir.r4.core#4.0.1
2025-08-01 00:47:49,465 - services - INFO - Manual package hl7.fhir.r4.core#4.0.1 already exists at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz
2025-08-01 00:47:49,466 - services - DEBUG - Parsed '/app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz' -> name='/app/instance/fhir.packages/hl7.fhir.r4.core', version='4.0.1'
2025-08-01 00:47:49,466 - services - INFO - Manually processing package: hl7.fhir.r4.core-4.0.1.tgz (/app/instance/fhir.packages/hl7.fhir.r4.core#4.0.1)
2025-08-01 00:47:49,731 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:47:49,735 - services - INFO - Saved metadata for hl7.fhir.r4.core#4.0.1 at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.metadata.json
2025-08-01 00:47:49,736 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:47:49,740 - services - INFO - Saved metadata for hl7.fhir.uv.extensions.r4#5.2.0 at /app/instance/fhir_packages/hl7.fhir.uv.extensions.r4-5.2.0.metadata.json
2025-08-01 00:47:49,740 - services - INFO - Processing dependency hl7.fhir.r4.core#4.0.1
2025-08-01 00:47:49,741 - services - INFO - Starting manual import for hl7.fhir.r4.core (mode=recursive, resolve_deps=True)
2025-08-01 00:47:49,741 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:47:49,743 - services - INFO - Attempting manual download of hl7.fhir.r4.core#4.0.1
2025-08-01 00:47:49,745 - services - INFO - Manual package hl7.fhir.r4.core#4.0.1 already exists at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz
2025-08-01 00:47:49,746 - services - DEBUG - Parsed '/app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz' -> name='/app/instance/fhir.packages/hl7.fhir.r4.core', version='4.0.1'
2025-08-01 00:47:49,747 - services - INFO - Manually processing package: hl7.fhir.r4.core-4.0.1.tgz (/app/instance/fhir.packages/hl7.fhir.r4.core#4.0.1)
2025-08-01 00:47:50,011 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:47:50,014 - services - INFO - Saved metadata for hl7.fhir.r4.core#4.0.1 at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.metadata.json
2025-08-01 00:47:50,015 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:47:50,018 - services - INFO - Saved metadata for hl7.terminology.r4#6.4.0 at /app/instance/fhir_packages/hl7.terminology.r4-6.4.0.metadata.json
2025-08-01 00:47:50,019 - services - INFO - Processing dependency hl7.fhir.uv.extensions.r4#5.2.0
2025-08-01 00:47:50,019 - services - INFO - Starting manual import for hl7.fhir.uv.extensions.r4 (mode=recursive, resolve_deps=True)
2025-08-01 00:47:50,019 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:47:50,020 - services - INFO - Attempting manual download of hl7.fhir.uv.extensions.r4#5.2.0
2025-08-01 00:47:50,021 - services - INFO - Manual package hl7.fhir.uv.extensions.r4#5.2.0 already exists at /app/instance/fhir_packages/hl7.fhir.uv.extensions.r4-5.2.0.tgz
2025-08-01 00:47:50,022 - services - DEBUG - Parsed '/app/instance/fhir_packages/hl7.fhir.uv.extensions.r4-5.2.0.tgz' -> name='/app/instance/fhir.packages/hl7.fhir.uv.extensions.r4', version='5.2.0'
2025-08-01 00:47:50,023 - services - INFO - Manually processing package: hl7.fhir.uv.extensions.r4-5.2.0.tgz (/app/instance/fhir.packages/hl7.fhir.uv.extensions.r4#5.2.0)
2025-08-01 00:47:50,079 - services - INFO - Processing dependency hl7.fhir.r4.core#4.0.1
2025-08-01 00:47:50,080 - services - INFO - Starting manual import for hl7.fhir.r4.core (mode=recursive, resolve_deps=True)
2025-08-01 00:47:50,081 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:47:50,082 - services - INFO - Attempting manual download of hl7.fhir.r4.core#4.0.1
2025-08-01 00:47:50,083 - services - INFO - Manual package hl7.fhir.r4.core#4.0.1 already exists at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz
2025-08-01 00:47:50,083 - services - DEBUG - Parsed '/app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz' -> name='/app/instance/fhir.packages/hl7.fhir.r4.core', version='4.0.1'
2025-08-01 00:47:50,084 - services - INFO - Manually processing package: hl7.fhir.r4.core-4.0.1.tgz (/app/instance/fhir.packages/hl7.fhir.r4.core#4.0.1)
2025-08-01 00:47:50,353 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:47:50,357 - services - INFO - Saved metadata for hl7.fhir.r4.core#4.0.1 at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.metadata.json
2025-08-01 00:47:50,358 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:47:50,364 - services - INFO - Saved metadata for hl7.fhir.uv.extensions.r4#5.2.0 at /app/instance/fhir_packages/hl7.fhir.uv.extensions.r4-5.2.0.metadata.json
2025-08-01 00:47:50,364 - services - INFO - Processing dependency hl7.fhir.r4.core#4.0.1
2025-08-01 00:47:50,365 - services - INFO - Starting manual import for hl7.fhir.r4.core (mode=recursive, resolve_deps=True)
2025-08-01 00:47:50,365 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:47:50,366 - services - INFO - Attempting manual download of hl7.fhir.r4.core#4.0.1
2025-08-01 00:47:50,367 - services - INFO - Manual package hl7.fhir.r4.core#4.0.1 already exists at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz
2025-08-01 00:47:50,368 - services - DEBUG - Parsed '/app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz' -> name='/app/instance/fhir.packages/hl7.fhir.r4.core', version='4.0.1'
2025-08-01 00:47:50,369 - services - INFO - Manually processing package: hl7.fhir.r4.core-4.0.1.tgz (/app/instance/fhir.packages/hl7.fhir.r4.core#4.0.1)
2025-08-01 00:47:50,631 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:47:50,634 - services - INFO - Saved metadata for hl7.fhir.r4.core#4.0.1 at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.metadata.json
2025-08-01 00:47:50,634 - services - INFO - Processing dependency hl7.fhir.uv.extensions.r4#5.2.0
2025-08-01 00:47:50,635 - services - INFO - Starting manual import for hl7.fhir.uv.extensions.r4 (mode=recursive, resolve_deps=True)
2025-08-01 00:47:50,636 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:47:50,637 - services - INFO - Attempting manual download of hl7.fhir.uv.extensions.r4#5.2.0
2025-08-01 00:47:50,638 - services - INFO - Manual package hl7.fhir.uv.extensions.r4#5.2.0 already exists at /app/instance/fhir_packages/hl7.fhir.uv.extensions.r4-5.2.0.tgz
2025-08-01 00:47:50,639 - services - DEBUG - Parsed '/app/instance/fhir_packages/hl7.fhir.uv.extensions.r4-5.2.0.tgz' -> name='/app/instance/fhir.packages/hl7.fhir.uv.extensions.r4', version='5.2.0'
2025-08-01 00:47:50,639 - services - INFO - Manually processing package: hl7.fhir.uv.extensions.r4-5.2.0.tgz (/app/instance/fhir.packages/hl7.fhir.uv.extensions.r4#5.2.0)
2025-08-01 00:47:50,694 - services - INFO - Processing dependency hl7.fhir.r4.core#4.0.1
2025-08-01 00:47:50,695 - services - INFO - Starting manual import for hl7.fhir.r4.core (mode=recursive, resolve_deps=True)
2025-08-01 00:47:50,695 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:47:50,697 - services - INFO - Attempting manual download of hl7.fhir.r4.core#4.0.1
2025-08-01 00:47:50,697 - services - INFO - Manual package hl7.fhir.r4.core#4.0.1 already exists at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz
2025-08-01 00:47:50,698 - services - DEBUG - Parsed '/app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz' -> name='/app/instance/fhir.packages/hl7.fhir.r4.core', version='4.0.1'
2025-08-01 00:47:50,699 - services - INFO - Manually processing package: hl7.fhir.r4.core-4.0.1.tgz (/app/instance/fhir.packages/hl7.fhir.r4.core#4.0.1)
2025-08-01 00:47:50,986 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:47:50,989 - services - INFO - Saved metadata for hl7.fhir.r4.core#4.0.1 at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.metadata.json
2025-08-01 00:47:50,990 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:47:50,993 - services - INFO - Saved metadata for hl7.fhir.uv.extensions.r4#5.2.0 at /app/instance/fhir_packages/hl7.fhir.uv.extensions.r4-5.2.0.metadata.json
2025-08-01 00:47:50,994 - services - INFO - Processing dependency hl7.fhir.r4.core#4.0.1
2025-08-01 00:47:50,994 - services - INFO - Starting manual import for hl7.fhir.r4.core (mode=recursive, resolve_deps=True)
2025-08-01 00:47:50,995 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:47:50,997 - services - INFO - Attempting manual download of hl7.fhir.r4.core#4.0.1
2025-08-01 00:47:50,998 - services - INFO - Manual package hl7.fhir.r4.core#4.0.1 already exists at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz
2025-08-01 00:47:50,999 - services - DEBUG - Parsed '/app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz' -> name='/app/instance/fhir.packages/hl7.fhir.r4.core', version='4.0.1'
2025-08-01 00:47:51,000 - services - INFO - Manually processing package: hl7.fhir.r4.core-4.0.1.tgz (/app/instance/fhir.packages/hl7.fhir.r4.core#4.0.1)
2025-08-01 00:47:51,247 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:47:51,250 - services - INFO - Saved metadata for hl7.fhir.r4.core#4.0.1 at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.metadata.json
2025-08-01 00:47:51,251 - services - INFO - Processing dependency hl7.fhir.r4.core#4.0.1
2025-08-01 00:47:51,252 - services - INFO - Starting manual import for hl7.fhir.r4.core (mode=recursive, resolve_deps=True)
2025-08-01 00:47:51,252 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:47:51,253 - services - INFO - Attempting manual download of hl7.fhir.r4.core#4.0.1
2025-08-01 00:47:51,254 - services - INFO - Manual package hl7.fhir.r4.core#4.0.1 already exists at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz
2025-08-01 00:47:51,255 - services - DEBUG - Parsed '/app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz' -> name='/app/instance/fhir.packages/hl7.fhir.r4.core', version='4.0.1'
2025-08-01 00:47:51,255 - services - INFO - Manually processing package: hl7.fhir.r4.core-4.0.1.tgz (/app/instance/fhir.packages/hl7.fhir.r4.core#4.0.1)
2025-08-01 00:47:51,509 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:47:51,512 - services - INFO - Saved metadata for hl7.fhir.r4.core#4.0.1 at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.metadata.json
2025-08-01 00:47:51,513 - services - INFO - Processing dependency hl7.fhir.r4.core#4.0.1
2025-08-01 00:47:51,513 - services - INFO - Starting manual import for hl7.fhir.r4.core (mode=recursive, resolve_deps=True)
2025-08-01 00:47:51,514 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:47:51,515 - services - INFO - Attempting manual download of hl7.fhir.r4.core#4.0.1
2025-08-01 00:47:51,516 - services - INFO - Manual package hl7.fhir.r4.core#4.0.1 already exists at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz
2025-08-01 00:47:51,518 - services - DEBUG - Parsed '/app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz' -> name='/app/instance/fhir.packages/hl7.fhir.r4.core', version='4.0.1'
2025-08-01 00:47:51,518 - services - INFO - Manually processing package: hl7.fhir.r4.core-4.0.1.tgz (/app/instance/fhir.packages/hl7.fhir.r4.core#4.0.1)
2025-08-01 00:47:51,779 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:47:51,782 - services - INFO - Saved metadata for hl7.fhir.r4.core#4.0.1 at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.metadata.json
2025-08-01 00:47:51,783 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:47:51,790 - services - INFO - Saved metadata for example.fhir.ph.core.r4#0.1.0 at /app/instance/fhir_packages/example.fhir.ph.core.r4-0.1.0.metadata.json
2025-08-01 00:47:51,792 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:47:51] "POST /manual-import-ig HTTP/1.1" 302 -
2025-08-01 00:47:51,808 - __main__ - DEBUG - Scanning packages directory: /app/instance/fhir_packages
2025-08-01 00:47:51,811 - services - DEBUG - Parsed 'example.fhir.ph.core.r4-0.1.0.tgz' -> name='example.fhir.ph.core.r4', version='0.1.0'
2025-08-01 00:47:51,819 - services - DEBUG - Parsed 'hl7.fhir.au.base-5.1.0-preview.tgz' -> name='hl7.fhir.au.base', version='5.1.0-preview'
2025-08-01 00:47:51,846 - services - DEBUG - Parsed 'hl7.fhir.au.core-1.1.0-preview.tgz' -> name='hl7.fhir.au.core', version='1.1.0-preview'
2025-08-01 00:47:51,863 - services - DEBUG - Parsed 'hl7.fhir.r4.core-4.0.1.tgz' -> name='hl7.fhir.r4.core', version='4.0.1'
2025-08-01 00:47:52,313 - services - DEBUG - Parsed 'hl7.fhir.uv.extensions.r4-5.2.0.tgz' -> name='hl7.fhir.uv.extensions.r4', version='5.2.0'
2025-08-01 00:47:52,375 - services - DEBUG - Parsed 'hl7.fhir.uv.ipa-1.0.0.tgz' -> name='hl7.fhir.uv.ipa', version='1.0.0'
2025-08-01 00:47:52,384 - services - DEBUG - Parsed 'hl7.fhir.uv.smart-app-launch-2.0.0.tgz' -> name='hl7.fhir.uv.smart-app-launch', version='2.0.0'
2025-08-01 00:47:52,387 - services - DEBUG - Parsed 'hl7.fhir.uv.smart-app-launch-2.1.0.tgz' -> name='hl7.fhir.uv.smart-app-launch', version='2.1.0'
2025-08-01 00:47:52,391 - services - DEBUG - Parsed 'hl7.terminology.r4-5.0.0.tgz' -> name='hl7.terminology.r4', version='5.0.0'
2025-08-01 00:47:52,627 - services - DEBUG - Parsed 'hl7.terminology.r4-6.2.0.tgz' -> name='hl7.terminology.r4', version='6.2.0'
2025-08-01 00:47:52,920 - services - DEBUG - Parsed 'hl7.terminology.r4-6.4.0.tgz' -> name='hl7.terminology.r4', version='6.4.0'
2025-08-01 00:47:53,250 - __main__ - DEBUG - Found packages: 11
2025-08-01 00:47:53,250 - __main__ - DEBUG - Errors during package listing: []
2025-08-01 00:47:53,251 - __main__ - DEBUG - Duplicate groups: {'hl7.fhir.uv.smart-app-launch': ['2.0.0', '2.1.0'], 'hl7.terminology.r4': ['5.0.0', '6.2.0', '6.4.0']}
2025-08-01 00:47:53,269 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:47:53] "GET /view-igs HTTP/1.1" 200 -
2025-08-01 00:47:53,299 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:47:53] "GET /static/css/fire-animation.css HTTP/1.1" 304 -
2025-08-01 00:47:53,301 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:47:53] "GET /static/css/animation.css HTTP/1.1" 304 -
2025-08-01 00:47:53,304 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:47:53] "GET /static/FHIRFLARE.png HTTP/1.1" 304 -
2025-08-01 00:47:53,307 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:47:53] "GET /static/js/htmx.min.js HTTP/1.1" 304 -
2025-08-01 00:47:55,256 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:47:55] "GET /manual-import-ig HTTP/1.1" 200 -
2025-08-01 00:47:55,270 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:47:55] "GET /static/css/fire-animation.css HTTP/1.1" 304 -
2025-08-01 00:47:55,272 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:47:55] "GET /static/css/animation.css HTTP/1.1" 304 -
2025-08-01 00:47:55,275 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:47:55] "GET /static/js/htmx.min.js HTTP/1.1" 304 -
2025-08-01 00:48:09,986 - services - INFO - Starting manual import for /tmp/tmpa5kg6gav/example.fhir.ph.core.r4-0.1.0.tgz (mode=recursive, resolve_deps=True)
2025-08-01 00:48:09,987 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:48:09,989 - services - DEBUG - Parsed 'example.fhir.ph.core.r4-0.1.0.tgz' -> name='example.fhir.ph.core.r4', version='0.1.0'
2025-08-01 00:48:09,995 - services - DEBUG - Parsed '/tmp/tmpa5kg6gav/example.fhir.ph.core.r4-0.1.0.tgz' -> name='/tmp/tmpa5kg6gav/example.fhir.ph.core.r4', version='0.1.0'
2025-08-01 00:48:09,996 - services - INFO - Manually processing package: example.fhir.ph.core.r4-0.1.0.tgz (/tmp/tmpa5kg6gav/example.fhir.ph.core.r4#0.1.0)
2025-08-01 00:48:09,997 - services - INFO - Processing dependency hl7.fhir.r4.core#4.0.1
2025-08-01 00:48:09,997 - services - INFO - Starting manual import for hl7.fhir.r4.core (mode=recursive, resolve_deps=True)
2025-08-01 00:48:09,997 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:48:09,998 - services - INFO - Attempting manual download of hl7.fhir.r4.core#4.0.1
2025-08-01 00:48:10,000 - services - INFO - Manual package hl7.fhir.r4.core#4.0.1 already exists at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz
2025-08-01 00:48:10,002 - services - DEBUG - Parsed '/app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz' -> name='/app/instance/fhir.packages/hl7.fhir.r4.core', version='4.0.1'
2025-08-01 00:48:10,003 - services - INFO - Manually processing package: hl7.fhir.r4.core-4.0.1.tgz (/app/instance/fhir.packages/hl7.fhir.r4.core#4.0.1)
2025-08-01 00:48:10,319 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:48:10,323 - services - INFO - Saved metadata for hl7.fhir.r4.core#4.0.1 at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.metadata.json
2025-08-01 00:48:10,324 - services - INFO - Processing dependency hl7.terminology.r4#6.4.0
2025-08-01 00:48:10,324 - services - INFO - Starting manual import for hl7.terminology.r4 (mode=recursive, resolve_deps=True)
2025-08-01 00:48:10,324 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:48:10,325 - services - INFO - Attempting manual download of hl7.terminology.r4#6.4.0
2025-08-01 00:48:10,326 - services - INFO - Manual package hl7.terminology.r4#6.4.0 already exists at /app/instance/fhir_packages/hl7.terminology.r4-6.4.0.tgz
2025-08-01 00:48:10,327 - services - DEBUG - Parsed '/app/instance/fhir_packages/hl7.terminology.r4-6.4.0.tgz' -> name='/app/instance/fhir.packages/hl7.terminology.r4', version='6.4.0'
2025-08-01 00:48:10,328 - services - INFO - Manually processing package: hl7.terminology.r4-6.4.0.tgz (/app/instance/fhir.packages/hl7.terminology.r4#6.4.0)
2025-08-01 00:48:10,330 - services - INFO - Processing dependency hl7.fhir.r4.core#4.0.1
2025-08-01 00:48:10,330 - services - INFO - Starting manual import for hl7.fhir.r4.core (mode=recursive, resolve_deps=True)
2025-08-01 00:48:10,331 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:48:10,332 - services - INFO - Attempting manual download of hl7.fhir.r4.core#4.0.1
2025-08-01 00:48:10,334 - services - INFO - Manual package hl7.fhir.r4.core#4.0.1 already exists at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz
2025-08-01 00:48:10,336 - services - DEBUG - Parsed '/app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz' -> name='/app/instance/fhir.packages/hl7.fhir.r4.core', version='4.0.1'
2025-08-01 00:48:10,336 - services - INFO - Manually processing package: hl7.fhir.r4.core-4.0.1.tgz (/app/instance/fhir.packages/hl7.fhir.r4.core#4.0.1)
2025-08-01 00:48:10,586 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:48:10,590 - services - INFO - Saved metadata for hl7.fhir.r4.core#4.0.1 at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.metadata.json
2025-08-01 00:48:10,590 - services - INFO - Processing dependency hl7.fhir.uv.extensions.r4#5.2.0
2025-08-01 00:48:10,591 - services - INFO - Starting manual import for hl7.fhir.uv.extensions.r4 (mode=recursive, resolve_deps=True)
2025-08-01 00:48:10,591 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:48:10,593 - services - INFO - Attempting manual download of hl7.fhir.uv.extensions.r4#5.2.0
2025-08-01 00:48:10,594 - services - INFO - Manual package hl7.fhir.uv.extensions.r4#5.2.0 already exists at /app/instance/fhir_packages/hl7.fhir.uv.extensions.r4-5.2.0.tgz
2025-08-01 00:48:10,595 - services - DEBUG - Parsed '/app/instance/fhir_packages/hl7.fhir.uv.extensions.r4-5.2.0.tgz' -> name='/app/instance/fhir.packages/hl7.fhir.uv.extensions.r4', version='5.2.0'
2025-08-01 00:48:10,596 - services - INFO - Manually processing package: hl7.fhir.uv.extensions.r4-5.2.0.tgz (/app/instance/fhir.packages/hl7.fhir.uv.extensions.r4#5.2.0)
2025-08-01 00:48:10,655 - services - INFO - Processing dependency hl7.fhir.r4.core#4.0.1
2025-08-01 00:48:10,655 - services - INFO - Starting manual import for hl7.fhir.r4.core (mode=recursive, resolve_deps=True)
2025-08-01 00:48:10,656 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:48:10,657 - services - INFO - Attempting manual download of hl7.fhir.r4.core#4.0.1
2025-08-01 00:48:10,659 - services - INFO - Manual package hl7.fhir.r4.core#4.0.1 already exists at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz
2025-08-01 00:48:10,660 - services - DEBUG - Parsed '/app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz' -> name='/app/instance/fhir.packages/hl7.fhir.r4.core', version='4.0.1'
2025-08-01 00:48:10,661 - services - INFO - Manually processing package: hl7.fhir.r4.core-4.0.1.tgz (/app/instance/fhir.packages/hl7.fhir.r4.core#4.0.1)
2025-08-01 00:48:10,997 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:48:11,004 - services - INFO - Saved metadata for hl7.fhir.r4.core#4.0.1 at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.metadata.json
2025-08-01 00:48:11,005 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:48:11,008 - services - INFO - Saved metadata for hl7.fhir.uv.extensions.r4#5.2.0 at /app/instance/fhir_packages/hl7.fhir.uv.extensions.r4-5.2.0.metadata.json
2025-08-01 00:48:11,009 - services - INFO - Processing dependency hl7.fhir.r4.core#4.0.1
2025-08-01 00:48:11,010 - services - INFO - Starting manual import for hl7.fhir.r4.core (mode=recursive, resolve_deps=True)
2025-08-01 00:48:11,011 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:48:11,012 - services - INFO - Attempting manual download of hl7.fhir.r4.core#4.0.1
2025-08-01 00:48:11,014 - services - INFO - Manual package hl7.fhir.r4.core#4.0.1 already exists at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz
2025-08-01 00:48:11,016 - services - DEBUG - Parsed '/app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz' -> name='/app/instance/fhir.packages/hl7.fhir.r4.core', version='4.0.1'
2025-08-01 00:48:11,016 - services - INFO - Manually processing package: hl7.fhir.r4.core-4.0.1.tgz (/app/instance/fhir.packages/hl7.fhir.r4.core#4.0.1)
2025-08-01 00:48:11,275 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:48:11,279 - services - INFO - Saved metadata for hl7.fhir.r4.core#4.0.1 at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.metadata.json
2025-08-01 00:48:11,280 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:48:11,283 - services - INFO - Saved metadata for hl7.terminology.r4#6.4.0 at /app/instance/fhir_packages/hl7.terminology.r4-6.4.0.metadata.json
2025-08-01 00:48:11,284 - services - INFO - Processing dependency hl7.fhir.uv.extensions.r4#5.2.0
2025-08-01 00:48:11,285 - services - INFO - Starting manual import for hl7.fhir.uv.extensions.r4 (mode=recursive, resolve_deps=True)
2025-08-01 00:48:11,285 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:48:11,287 - services - INFO - Attempting manual download of hl7.fhir.uv.extensions.r4#5.2.0
2025-08-01 00:48:11,288 - services - INFO - Manual package hl7.fhir.uv.extensions.r4#5.2.0 already exists at /app/instance/fhir_packages/hl7.fhir.uv.extensions.r4-5.2.0.tgz
2025-08-01 00:48:11,289 - services - DEBUG - Parsed '/app/instance/fhir_packages/hl7.fhir.uv.extensions.r4-5.2.0.tgz' -> name='/app/instance/fhir.packages/hl7.fhir.uv.extensions.r4', version='5.2.0'
2025-08-01 00:48:11,289 - services - INFO - Manually processing package: hl7.fhir.uv.extensions.r4-5.2.0.tgz (/app/instance/fhir.packages/hl7.fhir.uv.extensions.r4#5.2.0)
2025-08-01 00:48:11,344 - services - INFO - Processing dependency hl7.fhir.r4.core#4.0.1
2025-08-01 00:48:11,345 - services - INFO - Starting manual import for hl7.fhir.r4.core (mode=recursive, resolve_deps=True)
2025-08-01 00:48:11,345 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:48:11,346 - services - INFO - Attempting manual download of hl7.fhir.r4.core#4.0.1
2025-08-01 00:48:11,347 - services - INFO - Manual package hl7.fhir.r4.core#4.0.1 already exists at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz
2025-08-01 00:48:11,348 - services - DEBUG - Parsed '/app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz' -> name='/app/instance/fhir.packages/hl7.fhir.r4.core', version='4.0.1'
2025-08-01 00:48:11,349 - services - INFO - Manually processing package: hl7.fhir.r4.core-4.0.1.tgz (/app/instance/fhir.packages/hl7.fhir.r4.core#4.0.1)
2025-08-01 00:48:11,601 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:48:11,605 - services - INFO - Saved metadata for hl7.fhir.r4.core#4.0.1 at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.metadata.json
2025-08-01 00:48:11,605 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:48:11,609 - services - INFO - Saved metadata for hl7.fhir.uv.extensions.r4#5.2.0 at /app/instance/fhir_packages/hl7.fhir.uv.extensions.r4-5.2.0.metadata.json
2025-08-01 00:48:11,610 - services - INFO - Processing dependency hl7.fhir.r4.core#4.0.1
2025-08-01 00:48:11,610 - services - INFO - Starting manual import for hl7.fhir.r4.core (mode=recursive, resolve_deps=True)
2025-08-01 00:48:11,611 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:48:11,613 - services - INFO - Attempting manual download of hl7.fhir.r4.core#4.0.1
2025-08-01 00:48:11,614 - services - INFO - Manual package hl7.fhir.r4.core#4.0.1 already exists at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz
2025-08-01 00:48:11,615 - services - DEBUG - Parsed '/app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz' -> name='/app/instance/fhir.packages/hl7.fhir.r4.core', version='4.0.1'
2025-08-01 00:48:11,616 - services - INFO - Manually processing package: hl7.fhir.r4.core-4.0.1.tgz (/app/instance/fhir.packages/hl7.fhir.r4.core#4.0.1)
2025-08-01 00:48:11,867 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:48:11,871 - services - INFO - Saved metadata for hl7.fhir.r4.core#4.0.1 at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.metadata.json
2025-08-01 00:48:11,871 - services - INFO - Processing dependency hl7.fhir.uv.extensions.r4#5.2.0
2025-08-01 00:48:11,872 - services - INFO - Starting manual import for hl7.fhir.uv.extensions.r4 (mode=recursive, resolve_deps=True)
2025-08-01 00:48:11,872 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:48:11,874 - services - INFO - Attempting manual download of hl7.fhir.uv.extensions.r4#5.2.0
2025-08-01 00:48:11,875 - services - INFO - Manual package hl7.fhir.uv.extensions.r4#5.2.0 already exists at /app/instance/fhir_packages/hl7.fhir.uv.extensions.r4-5.2.0.tgz
2025-08-01 00:48:11,876 - services - DEBUG - Parsed '/app/instance/fhir_packages/hl7.fhir.uv.extensions.r4-5.2.0.tgz' -> name='/app/instance/fhir.packages/hl7.fhir.uv.extensions.r4', version='5.2.0'
2025-08-01 00:48:11,877 - services - INFO - Manually processing package: hl7.fhir.uv.extensions.r4-5.2.0.tgz (/app/instance/fhir.packages/hl7.fhir.uv.extensions.r4#5.2.0)
2025-08-01 00:48:11,932 - services - INFO - Processing dependency hl7.fhir.r4.core#4.0.1
2025-08-01 00:48:11,932 - services - INFO - Starting manual import for hl7.fhir.r4.core (mode=recursive, resolve_deps=True)
2025-08-01 00:48:11,933 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:48:11,935 - services - INFO - Attempting manual download of hl7.fhir.r4.core#4.0.1
2025-08-01 00:48:11,936 - services - INFO - Manual package hl7.fhir.r4.core#4.0.1 already exists at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz
2025-08-01 00:48:11,937 - services - DEBUG - Parsed '/app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz' -> name='/app/instance/fhir.packages/hl7.fhir.r4.core', version='4.0.1'
2025-08-01 00:48:11,937 - services - INFO - Manually processing package: hl7.fhir.r4.core-4.0.1.tgz (/app/instance/fhir.packages/hl7.fhir.r4.core#4.0.1)
2025-08-01 00:48:12,191 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:48:12,194 - services - INFO - Saved metadata for hl7.fhir.r4.core#4.0.1 at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.metadata.json
2025-08-01 00:48:12,194 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:48:12,198 - services - INFO - Saved metadata for hl7.fhir.uv.extensions.r4#5.2.0 at /app/instance/fhir_packages/hl7.fhir.uv.extensions.r4-5.2.0.metadata.json
2025-08-01 00:48:12,199 - services - INFO - Processing dependency hl7.fhir.r4.core#4.0.1
2025-08-01 00:48:12,200 - services - INFO - Starting manual import for hl7.fhir.r4.core (mode=recursive, resolve_deps=True)
2025-08-01 00:48:12,201 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:48:12,202 - services - INFO - Attempting manual download of hl7.fhir.r4.core#4.0.1
2025-08-01 00:48:12,203 - services - INFO - Manual package hl7.fhir.r4.core#4.0.1 already exists at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz
2025-08-01 00:48:12,205 - services - DEBUG - Parsed '/app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz' -> name='/app/instance/fhir.packages/hl7.fhir.r4.core', version='4.0.1'
2025-08-01 00:48:12,205 - services - INFO - Manually processing package: hl7.fhir.r4.core-4.0.1.tgz (/app/instance/fhir.packages/hl7.fhir.r4.core#4.0.1)
2025-08-01 00:48:12,451 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:48:12,456 - services - INFO - Saved metadata for hl7.fhir.r4.core#4.0.1 at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.metadata.json
2025-08-01 00:48:12,456 - services - INFO - Processing dependency hl7.fhir.r4.core#4.0.1
2025-08-01 00:48:12,457 - services - INFO - Starting manual import for hl7.fhir.r4.core (mode=recursive, resolve_deps=True)
2025-08-01 00:48:12,457 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:48:12,458 - services - INFO - Attempting manual download of hl7.fhir.r4.core#4.0.1
2025-08-01 00:48:12,459 - services - INFO - Manual package hl7.fhir.r4.core#4.0.1 already exists at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz
2025-08-01 00:48:12,460 - services - DEBUG - Parsed '/app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz' -> name='/app/instance/fhir.packages/hl7.fhir.r4.core', version='4.0.1'
2025-08-01 00:48:12,461 - services - INFO - Manually processing package: hl7.fhir.r4.core-4.0.1.tgz (/app/instance/fhir.packages/hl7.fhir.r4.core#4.0.1)
2025-08-01 00:48:12,720 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:48:12,723 - services - INFO - Saved metadata for hl7.fhir.r4.core#4.0.1 at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.metadata.json
2025-08-01 00:48:12,724 - services - INFO - Processing dependency hl7.fhir.r4.core#4.0.1
2025-08-01 00:48:12,724 - services - INFO - Starting manual import for hl7.fhir.r4.core (mode=recursive, resolve_deps=True)
2025-08-01 00:48:12,725 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:48:12,726 - services - INFO - Attempting manual download of hl7.fhir.r4.core#4.0.1
2025-08-01 00:48:12,727 - services - INFO - Manual package hl7.fhir.r4.core#4.0.1 already exists at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz
2025-08-01 00:48:12,728 - services - DEBUG - Parsed '/app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz' -> name='/app/instance/fhir.packages/hl7.fhir.r4.core', version='4.0.1'
2025-08-01 00:48:12,728 - services - INFO - Manually processing package: hl7.fhir.r4.core-4.0.1.tgz (/app/instance/fhir.packages/hl7.fhir.r4.core#4.0.1)
2025-08-01 00:48:12,994 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:48:12,997 - services - INFO - Saved metadata for hl7.fhir.r4.core#4.0.1 at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.metadata.json
2025-08-01 00:48:12,998 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:48:13,003 - services - INFO - Saved metadata for example.fhir.ph.core.r4#0.1.0 at /app/instance/fhir_packages/example.fhir.ph.core.r4-0.1.0.metadata.json
2025-08-01 00:48:13,004 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:48:13] "POST /manual-import-ig HTTP/1.1" 302 -
2025-08-01 00:48:13,014 - __main__ - DEBUG - Scanning packages directory: /app/instance/fhir_packages
2025-08-01 00:48:13,016 - services - DEBUG - Parsed 'example.fhir.ph.core.r4-0.1.0.tgz' -> name='example.fhir.ph.core.r4', version='0.1.0'
2025-08-01 00:48:13,023 - services - DEBUG - Parsed 'hl7.fhir.au.base-5.1.0-preview.tgz' -> name='hl7.fhir.au.base', version='5.1.0-preview'
2025-08-01 00:48:13,047 - services - DEBUG - Parsed 'hl7.fhir.au.core-1.1.0-preview.tgz' -> name='hl7.fhir.au.core', version='1.1.0-preview'
2025-08-01 00:48:13,064 - services - DEBUG - Parsed 'hl7.fhir.r4.core-4.0.1.tgz' -> name='hl7.fhir.r4.core', version='4.0.1'
2025-08-01 00:48:13,486 - services - DEBUG - Parsed 'hl7.fhir.uv.extensions.r4-5.2.0.tgz' -> name='hl7.fhir.uv.extensions.r4', version='5.2.0'
2025-08-01 00:48:13,543 - services - DEBUG - Parsed 'hl7.fhir.uv.ipa-1.0.0.tgz' -> name='hl7.fhir.uv.ipa', version='1.0.0'
2025-08-01 00:48:13,551 - services - DEBUG - Parsed 'hl7.fhir.uv.smart-app-launch-2.0.0.tgz' -> name='hl7.fhir.uv.smart-app-launch', version='2.0.0'
2025-08-01 00:48:13,554 - services - DEBUG - Parsed 'hl7.fhir.uv.smart-app-launch-2.1.0.tgz' -> name='hl7.fhir.uv.smart-app-launch', version='2.1.0'
2025-08-01 00:48:13,558 - services - DEBUG - Parsed 'hl7.terminology.r4-5.0.0.tgz' -> name='hl7.terminology.r4', version='5.0.0'
2025-08-01 00:48:13,743 - services - DEBUG - Parsed 'hl7.terminology.r4-6.2.0.tgz' -> name='hl7.terminology.r4', version='6.2.0'
2025-08-01 00:48:13,974 - services - DEBUG - Parsed 'hl7.terminology.r4-6.4.0.tgz' -> name='hl7.terminology.r4', version='6.4.0'
2025-08-01 00:48:14,164 - __main__ - DEBUG - Found packages: 11
2025-08-01 00:48:14,165 - __main__ - DEBUG - Errors during package listing: []
2025-08-01 00:48:14,166 - __main__ - DEBUG - Duplicate groups: {'hl7.fhir.uv.smart-app-launch': ['2.0.0', '2.1.0'], 'hl7.terminology.r4': ['5.0.0', '6.2.0', '6.4.0']}
2025-08-01 00:48:14,169 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:48:14] "GET /view-igs HTTP/1.1" 200 -
2025-08-01 00:48:14,185 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:48:14] "GET /static/css/fire-animation.css HTTP/1.1" 304 -
2025-08-01 00:48:14,187 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:48:14] "GET /static/js/htmx.min.js HTTP/1.1" 304 -
2025-08-01 00:48:14,188 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:48:14] "GET /static/css/animation.css HTTP/1.1" 304 -
2025-08-01 00:48:14,189 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:48:14] "GET /static/FHIRFLARE.png HTTP/1.1" 304 -
2025-08-01 00:48:19,052 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:48:19] "GET /validate-sample HTTP/1.1" 200 -
2025-08-01 00:48:19,067 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:48:19] "GET /static/css/animation.css HTTP/1.1" 304 -
2025-08-01 00:48:19,068 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:48:19] "GET /static/css/fire-animation.css HTTP/1.1" 304 -
2025-08-01 00:48:19,069 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:48:19] "GET /static/js/htmx.min.js HTTP/1.1" 304 -
2025-08-01 00:48:19,071 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:48:19] "GET /static/FHIRFLARE.png HTTP/1.1" 304 -
2025-08-01 00:50:33,336 - __main__ - DEBUG - Proxy received request for path: '/fhir/metadata', cleaned subpath: 'metadata'
2025-08-01 00:50:33,337 - __main__ - DEBUG - No target header found, proxying to default local HAPI: http://localhost:8080/fhir
2025-08-01 00:50:33,337 - __main__ - INFO - Proxying request: GET http://localhost:8080/fhir/metadata
2025-08-01 00:50:33,339 - urllib3.connectionpool - DEBUG - Starting new HTTP connection (1): localhost:8080
2025-08-01 00:50:34,754 - urllib3.connectionpool - DEBUG - http://localhost:8080 "GET /fhir/metadata HTTP/1.1" 200 None
2025-08-01 00:50:34,995 - __main__ - INFO - Target server 'http://localhost:8080/fhir' responded with status: 200
2025-08-01 00:50:34,997 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:50:34] "GET /fhir/metadata HTTP/1.1" 200 -
2025-08-01 00:50:35,010 - services - DEBUG - Received validate-sample request
2025-08-01 00:50:35,011 - services - DEBUG - Request params: package_name=example.fhir.ph.core.r4, version=0.1.0, sample_data_length=13970
2025-08-01 00:50:35,012 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:50:35,014 - services - DEBUG - Checking package file: /app/instance/fhir_packages/example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:50:35,017 - services - DEBUG - Validating Bundle against example.fhir.ph.core.r4#0.1.0
2025-08-01 00:50:35,017 - services - DEBUG - Validating bundle against example.fhir.ph.core.r4#0.1.0, include_dependencies=True
2025-08-01 00:50:35,018 - services - DEBUG - Found 14 local references in the bundle.
2025-08-01 00:50:35,019 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:50:35,021 - services - DEBUG - Using provided profile: urn://example.com/ph-core/fhir/StructureDefinition/ph-core-patient. Cleaned to: urn://example.com/ph-core/fhir/StructureDefinition/ph-core-patient
2025-08-01 00:50:35,022 - services - DEBUG - Checking for package file: /app/instance/fhir_packages/example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:50:35,027 - services - DEBUG - Searching for SD matching 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-patient' with profile 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-patient' in example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:50:35,028 - services - DEBUG - Cleaned profile URL for search: 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-patient'
2025-08-01 00:50:35,039 - services - DEBUG - Removing narrative text from resource: StructureDefinition
2025-08-01 00:50:35,040 - services - INFO - Found definitive SD matching profile 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-patient' at path: package/StructureDefinition-ph-core-patient.json
2025-08-01 00:50:35,042 - services - DEBUG - Found SD at package/StructureDefinition-ph-core-patient.json
2025-08-01 00:50:35,043 - services - DEBUG - Navigating FHIR path: Patient, extension_url=None
2025-08-01 00:50:35,046 - services - DEBUG - Navigating FHIR path: Patient, extension_url=None
2025-08-01 00:50:35,048 - services - DEBUG - Navigating FHIR path: Patient.extension, extension_url=None
2025-08-01 00:50:35,050 - services - DEBUG - Navigating FHIR path: Patient.extension, extension_url=None
2025-08-01 00:50:35,052 - services - DEBUG - Navigating FHIR path: Patient.identifier, extension_url=None
2025-08-01 00:50:35,054 - services - DEBUG - Navigating FHIR path: Patient.identifier, extension_url=None
2025-08-01 00:50:35,055 - services - DEBUG - Validation result: valid=True, errors=0, warnings=0
2025-08-01 00:50:35,057 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:50:35,059 - services - DEBUG - Using provided profile: urn://example.com/ph-core/fhir/StructureDefinition/ph-core-encounter. Cleaned to: urn://example.com/ph-core/fhir/StructureDefinition/ph-core-encounter
2025-08-01 00:50:35,060 - services - DEBUG - Checking for package file: /app/instance/fhir_packages/example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:50:35,065 - services - DEBUG - Searching for SD matching 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-encounter' with profile 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-encounter' in example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:50:35,066 - services - DEBUG - Cleaned profile URL for search: 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-encounter'
2025-08-01 00:50:35,071 - services - DEBUG - Removing narrative text from resource: StructureDefinition
2025-08-01 00:50:35,073 - services - INFO - Found definitive SD matching profile 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-encounter' at path: package/StructureDefinition-ph-core-encounter.json
2025-08-01 00:50:35,074 - services - DEBUG - Found SD at package/StructureDefinition-ph-core-encounter.json
2025-08-01 00:50:35,075 - services - DEBUG - Navigating FHIR path: Encounter, extension_url=None
2025-08-01 00:50:35,077 - services - DEBUG - Navigating FHIR path: Encounter, extension_url=None
2025-08-01 00:50:35,078 - services - DEBUG - Navigating FHIR path: Encounter.status, extension_url=None
2025-08-01 00:50:35,079 - services - DEBUG - Navigating FHIR path: Encounter.class, extension_url=None
2025-08-01 00:50:35,081 - services - DEBUG - Validation result: valid=True, errors=0, warnings=0
2025-08-01 00:50:35,082 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:50:35,083 - services - DEBUG - No profile in resource, using base type as identifier: Condition
2025-08-01 00:50:35,084 - services - DEBUG - Checking for package file: /app/instance/fhir_packages/example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:50:35,088 - services - DEBUG - Searching for SD matching 'Condition' with profile 'None' in example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:50:35,090 - services - DEBUG - Cleaned profile URL for search: 'None'
2025-08-01 00:50:35,106 - services - INFO - SD matching identifier 'Condition' or profile 'None' not found within archive example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:50:35,108 - services - DEBUG - SD not found in example.fhir.ph.core.r4#0.1.0. Checking dependencies.
2025-08-01 00:50:35,112 - services - DEBUG - Found dependencies: {'hl7.fhir.r4.core': '4.0.1', 'hl7.terminology.r4': '6.4.0', 'hl7.fhir.uv.extensions.r4': '5.2.0'}
2025-08-01 00:50:35,113 - services - DEBUG - Searching SD in dependency hl7.fhir.r4.core#4.0.1
2025-08-01 00:50:35,116 - services - DEBUG - Searching for SD matching 'Condition' with profile 'None' in hl7.fhir.r4.core-4.0.1.tgz
2025-08-01 00:50:35,117 - services - DEBUG - Cleaned profile URL for search: 'None'
2025-08-01 00:50:35,182 - services - INFO - Found SD in dependency hl7.fhir.r4.core#4.0.1 at package/StructureDefinition-Condition.json
2025-08-01 00:50:35,184 - services - DEBUG - Found SD at package/StructureDefinition-Condition.json
2025-08-01 00:50:35,185 - services - DEBUG - Navigating FHIR path: Condition, extension_url=None
2025-08-01 00:50:35,186 - services - DEBUG - Navigating FHIR path: Condition, extension_url=None
2025-08-01 00:50:35,187 - services - DEBUG - Navigating FHIR path: Condition.subject, extension_url=None
2025-08-01 00:50:35,188 - services - DEBUG - Validation result: valid=True, errors=0, warnings=0
2025-08-01 00:50:35,190 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:50:35,192 - services - DEBUG - Using provided profile: urn://example.com/ph-core/fhir/StructureDefinition/ph-core-medication. Cleaned to: urn://example.com/ph-core/fhir/StructureDefinition/ph-core-medication
2025-08-01 00:50:35,192 - services - DEBUG - Checking for package file: /app/instance/fhir_packages/example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:50:35,196 - services - DEBUG - Searching for SD matching 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-medication' with profile 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-medication' in example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:50:35,197 - services - DEBUG - Cleaned profile URL for search: 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-medication'
2025-08-01 00:50:35,204 - services - DEBUG - Removing narrative text from resource: StructureDefinition
2025-08-01 00:50:35,206 - services - INFO - Found definitive SD matching profile 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-medication' at path: package/StructureDefinition-ph-core-medication.json
2025-08-01 00:50:35,207 - services - DEBUG - Found SD at package/StructureDefinition-ph-core-medication.json
2025-08-01 00:50:35,207 - services - DEBUG - Navigating FHIR path: Medication, extension_url=None
2025-08-01 00:50:35,209 - services - DEBUG - Navigating FHIR path: Medication, extension_url=None
2025-08-01 00:50:35,210 - services - DEBUG - Validation result: valid=True, errors=0, warnings=0
2025-08-01 00:50:35,211 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:50:35,213 - services - DEBUG - Using provided profile: urn://example.com/ph-core/fhir/StructureDefinition/ph-core-observation. Cleaned to: urn://example.com/ph-core/fhir/StructureDefinition/ph-core-observation
2025-08-01 00:50:35,214 - services - DEBUG - Checking for package file: /app/instance/fhir_packages/example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:50:35,217 - services - DEBUG - Searching for SD matching 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-observation' with profile 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-observation' in example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:50:35,218 - services - DEBUG - Cleaned profile URL for search: 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-observation'
2025-08-01 00:50:35,224 - services - DEBUG - Removing narrative text from resource: StructureDefinition
2025-08-01 00:50:35,225 - services - INFO - Found definitive SD matching profile 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-observation' at path: package/StructureDefinition-ph-core-observation.json
2025-08-01 00:50:35,226 - services - DEBUG - Found SD at package/StructureDefinition-ph-core-observation.json
2025-08-01 00:50:35,227 - services - DEBUG - Navigating FHIR path: Observation, extension_url=None
2025-08-01 00:50:35,228 - services - DEBUG - Navigating FHIR path: Observation, extension_url=None
2025-08-01 00:50:35,229 - services - DEBUG - Navigating FHIR path: Observation.status, extension_url=None
2025-08-01 00:50:35,230 - services - DEBUG - Navigating FHIR path: Observation.code, extension_url=None
2025-08-01 00:50:35,231 - services - DEBUG - Validation result: valid=True, errors=0, warnings=0
2025-08-01 00:50:35,232 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:50:35,233 - services - DEBUG - No profile in resource, using base type as identifier: AllergyIntolerance
2025-08-01 00:50:35,234 - services - DEBUG - Checking for package file: /app/instance/fhir_packages/example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:50:35,237 - services - DEBUG - Searching for SD matching 'AllergyIntolerance' with profile 'None' in example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:50:35,238 - services - DEBUG - Cleaned profile URL for search: 'None'
2025-08-01 00:50:35,250 - services - INFO - SD matching identifier 'AllergyIntolerance' or profile 'None' not found within archive example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:50:35,252 - services - DEBUG - SD not found in example.fhir.ph.core.r4#0.1.0. Checking dependencies.
2025-08-01 00:50:35,255 - services - DEBUG - Found dependencies: {'hl7.fhir.r4.core': '4.0.1', 'hl7.terminology.r4': '6.4.0', 'hl7.fhir.uv.extensions.r4': '5.2.0'}
2025-08-01 00:50:35,256 - services - DEBUG - Searching SD in dependency hl7.fhir.r4.core#4.0.1
2025-08-01 00:50:35,259 - services - DEBUG - Searching for SD matching 'AllergyIntolerance' with profile 'None' in hl7.fhir.r4.core-4.0.1.tgz
2025-08-01 00:50:35,259 - services - DEBUG - Cleaned profile URL for search: 'None'
2025-08-01 00:50:35,710 - services - INFO - Found SD in dependency hl7.fhir.r4.core#4.0.1 at package/StructureDefinition-AllergyIntolerance.json
2025-08-01 00:50:35,711 - services - DEBUG - Found SD at package/StructureDefinition-AllergyIntolerance.json
2025-08-01 00:50:35,712 - services - DEBUG - Navigating FHIR path: AllergyIntolerance, extension_url=None
2025-08-01 00:50:35,713 - services - DEBUG - Navigating FHIR path: AllergyIntolerance, extension_url=None
2025-08-01 00:50:35,713 - services - DEBUG - Navigating FHIR path: AllergyIntolerance.patient, extension_url=None
2025-08-01 00:50:35,714 - services - DEBUG - Validation result: valid=True, errors=0, warnings=0
2025-08-01 00:50:35,715 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:50:35,716 - services - DEBUG - Using provided profile: urn://example.com/ph-core/fhir/StructureDefinition/ph-core-practitioner. Cleaned to: urn://example.com/ph-core/fhir/StructureDefinition/ph-core-practitioner
2025-08-01 00:50:35,716 - services - DEBUG - Checking for package file: /app/instance/fhir_packages/example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:50:35,719 - services - DEBUG - Searching for SD matching 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-practitioner' with profile 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-practitioner' in example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:50:35,719 - services - DEBUG - Cleaned profile URL for search: 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-practitioner'
2025-08-01 00:50:35,727 - services - DEBUG - Removing narrative text from resource: StructureDefinition
2025-08-01 00:50:35,728 - services - INFO - Found definitive SD matching profile 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-practitioner' at path: package/StructureDefinition-ph-core-practitioner.json
2025-08-01 00:50:35,729 - services - DEBUG - Found SD at package/StructureDefinition-ph-core-practitioner.json
2025-08-01 00:50:35,729 - services - DEBUG - Navigating FHIR path: Practitioner, extension_url=None
2025-08-01 00:50:35,730 - services - DEBUG - Navigating FHIR path: Practitioner, extension_url=None
2025-08-01 00:50:35,731 - services - DEBUG - Validation result: valid=True, errors=0, warnings=0
2025-08-01 00:50:35,731 - services - DEBUG - Bundle validation result: valid=True, errors=0, warnings=0, resources=7
2025-08-01 00:50:35,732 - services - INFO - Validation result for Bundle against example.fhir.ph.core.r4#0.1.0: valid=True, errors=0, warnings=0
2025-08-01 00:50:35,732 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:50:35] "POST /api/validate-sample HTTP/1.1" 200 -
2025-08-01 00:51:28,784 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:51:28] "GET /manual-import-ig HTTP/1.1" 200 -
2025-08-01 00:51:28,825 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:51:28] "GET /static/css/animation.css HTTP/1.1" 304 -
2025-08-01 00:51:28,828 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:51:28] "GET /static/css/fire-animation.css HTTP/1.1" 304 -
2025-08-01 00:51:28,839 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:51:28] "GET /static/js/htmx.min.js HTTP/1.1" 304 -
2025-08-01 00:51:39,832 - __main__ - DEBUG - Scanning packages directory: /app/instance/fhir_packages
2025-08-01 00:51:39,838 - services - DEBUG - Parsed 'example.fhir.ph.core.r4-0.1.0.tgz' -> name='example.fhir.ph.core.r4', version='0.1.0'
2025-08-01 00:51:39,851 - services - DEBUG - Parsed 'hl7.fhir.au.base-5.1.0-preview.tgz' -> name='hl7.fhir.au.base', version='5.1.0-preview'
2025-08-01 00:51:39,892 - services - DEBUG - Parsed 'hl7.fhir.au.core-1.1.0-preview.tgz' -> name='hl7.fhir.au.core', version='1.1.0-preview'
2025-08-01 00:51:39,918 - services - DEBUG - Parsed 'hl7.fhir.r4.core-4.0.1.tgz' -> name='hl7.fhir.r4.core', version='4.0.1'
2025-08-01 00:51:40,732 - services - DEBUG - Parsed 'hl7.fhir.uv.extensions.r4-5.2.0.tgz' -> name='hl7.fhir.uv.extensions.r4', version='5.2.0'
2025-08-01 00:51:40,837 - services - DEBUG - Parsed 'hl7.fhir.uv.ipa-1.0.0.tgz' -> name='hl7.fhir.uv.ipa', version='1.0.0'
2025-08-01 00:51:40,855 - services - DEBUG - Parsed 'hl7.fhir.uv.smart-app-launch-2.0.0.tgz' -> name='hl7.fhir.uv.smart-app-launch', version='2.0.0'
2025-08-01 00:51:40,860 - services - DEBUG - Parsed 'hl7.fhir.uv.smart-app-launch-2.1.0.tgz' -> name='hl7.fhir.uv.smart-app-launch', version='2.1.0'
2025-08-01 00:51:40,868 - services - DEBUG - Parsed 'hl7.terminology.r4-5.0.0.tgz' -> name='hl7.terminology.r4', version='5.0.0'
2025-08-01 00:51:41,223 - services - DEBUG - Parsed 'hl7.terminology.r4-6.2.0.tgz' -> name='hl7.terminology.r4', version='6.2.0'
2025-08-01 00:51:41,669 - services - DEBUG - Parsed 'hl7.terminology.r4-6.4.0.tgz' -> name='hl7.terminology.r4', version='6.4.0'
2025-08-01 00:51:42,069 - __main__ - DEBUG - Found packages: 11
2025-08-01 00:51:42,070 - __main__ - DEBUG - Errors during package listing: []
2025-08-01 00:51:42,071 - __main__ - DEBUG - Duplicate groups: {'hl7.fhir.uv.smart-app-launch': ['2.0.0', '2.1.0'], 'hl7.terminology.r4': ['5.0.0', '6.2.0', '6.4.0']}
2025-08-01 00:51:42,075 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:51:42] "GET /view-igs HTTP/1.1" 200 -
2025-08-01 00:51:42,100 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:51:42] "GET /static/css/fire-animation.css HTTP/1.1" 304 -
2025-08-01 00:51:42,103 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:51:42] "GET /static/css/animation.css HTTP/1.1" 304 -
2025-08-01 00:51:42,108 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:51:42] "GET /static/FHIRFLARE.png HTTP/1.1" 304 -
2025-08-01 00:51:42,108 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:51:42] "GET /static/js/htmx.min.js HTTP/1.1" 304 -
2025-08-01 00:54:20,189 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:54:20] "GET /validate-sample HTTP/1.1" 200 -
2025-08-01 00:54:20,209 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:54:20] "GET /static/css/fire-animation.css HTTP/1.1" 304 -
2025-08-01 00:54:20,212 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:54:20] "GET /static/css/animation.css HTTP/1.1" 304 -
2025-08-01 00:54:20,216 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:54:20] "GET /static/js/htmx.min.js HTTP/1.1" 304 -
2025-08-01 00:54:20,218 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:54:20] "GET /static/FHIRFLARE.png HTTP/1.1" 304 -
2025-08-01 00:54:39,350 - __main__ - DEBUG - Proxy received request for path: '/fhir/metadata', cleaned subpath: 'metadata'
2025-08-01 00:54:39,351 - __main__ - DEBUG - No target header found, proxying to default local HAPI: http://localhost:8080/fhir
2025-08-01 00:54:39,353 - __main__ - INFO - Proxying request: GET http://localhost:8080/fhir/metadata
2025-08-01 00:54:39,356 - urllib3.connectionpool - DEBUG - Starting new HTTP connection (1): localhost:8080
2025-08-01 00:54:39,413 - urllib3.connectionpool - DEBUG - http://localhost:8080 "GET /fhir/metadata HTTP/1.1" 200 None
2025-08-01 00:54:39,611 - __main__ - INFO - Target server 'http://localhost:8080/fhir' responded with status: 200
2025-08-01 00:54:39,613 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:54:39] "GET /fhir/metadata HTTP/1.1" 200 -
2025-08-01 00:54:39,639 - services - DEBUG - Received validate-sample request
2025-08-01 00:54:39,641 - services - DEBUG - Request params: package_name=example.fhir.ph.core.r4, version=0.1.0, sample_data_length=13970
2025-08-01 00:54:39,643 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:54:39,647 - services - DEBUG - Checking package file: /app/instance/fhir_packages/example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:54:39,650 - services - DEBUG - Validating Bundle against example.fhir.ph.core.r4#0.1.0
2025-08-01 00:54:39,652 - services - DEBUG - Validating bundle against example.fhir.ph.core.r4#0.1.0, include_dependencies=True
2025-08-01 00:54:39,653 - services - DEBUG - Found 14 local references in the bundle.
2025-08-01 00:54:39,655 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:54:39,659 - services - DEBUG - Using provided profile: urn://example.com/ph-core/fhir/StructureDefinition/ph-core-patient. Cleaned to: urn://example.com/ph-core/fhir/StructureDefinition/ph-core-patient
2025-08-01 00:54:39,660 - services - DEBUG - Checking for package file: /app/instance/fhir_packages/example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:54:39,668 - services - DEBUG - Searching for SD matching 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-patient' with profile 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-patient' in example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:54:39,670 - services - DEBUG - Cleaned profile URL for search: 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-patient'
2025-08-01 00:54:39,688 - services - DEBUG - Removing narrative text from resource: StructureDefinition
2025-08-01 00:54:39,689 - services - INFO - Found definitive SD matching profile 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-patient' at path: package/StructureDefinition-ph-core-patient.json
2025-08-01 00:54:39,692 - services - DEBUG - Found SD at package/StructureDefinition-ph-core-patient.json
2025-08-01 00:54:39,693 - services - DEBUG - Navigating FHIR path: Patient, extension_url=None
2025-08-01 00:54:39,695 - services - DEBUG - Navigating FHIR path: Patient, extension_url=None
2025-08-01 00:54:39,697 - services - DEBUG - Navigating FHIR path: Patient.extension, extension_url=None
2025-08-01 00:54:39,700 - services - DEBUG - Navigating FHIR path: Patient.extension, extension_url=None
2025-08-01 00:54:39,702 - services - DEBUG - Navigating FHIR path: Patient.identifier, extension_url=None
2025-08-01 00:54:39,705 - services - DEBUG - Navigating FHIR path: Patient.identifier, extension_url=None
2025-08-01 00:54:39,708 - services - DEBUG - Validation result: valid=True, errors=0, warnings=0
2025-08-01 00:54:39,710 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:54:39,714 - services - DEBUG - Using provided profile: urn://example.com/ph-core/fhir/StructureDefinition/ph-core-encounter. Cleaned to: urn://example.com/ph-core/fhir/StructureDefinition/ph-core-encounter
2025-08-01 00:54:39,715 - services - DEBUG - Checking for package file: /app/instance/fhir_packages/example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:54:39,724 - services - DEBUG - Searching for SD matching 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-encounter' with profile 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-encounter' in example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:54:39,725 - services - DEBUG - Cleaned profile URL for search: 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-encounter'
2025-08-01 00:54:39,734 - services - DEBUG - Removing narrative text from resource: StructureDefinition
2025-08-01 00:54:39,736 - services - INFO - Found definitive SD matching profile 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-encounter' at path: package/StructureDefinition-ph-core-encounter.json
2025-08-01 00:54:39,739 - services - DEBUG - Found SD at package/StructureDefinition-ph-core-encounter.json
2025-08-01 00:54:39,740 - services - DEBUG - Navigating FHIR path: Encounter, extension_url=None
2025-08-01 00:54:39,742 - services - DEBUG - Navigating FHIR path: Encounter, extension_url=None
2025-08-01 00:54:39,744 - services - DEBUG - Navigating FHIR path: Encounter.status, extension_url=None
2025-08-01 00:54:39,746 - services - DEBUG - Navigating FHIR path: Encounter.class, extension_url=None
2025-08-01 00:54:39,747 - services - DEBUG - Validation result: valid=True, errors=0, warnings=0
2025-08-01 00:54:39,749 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:54:39,753 - services - DEBUG - No profile in resource, using base type as identifier: Condition
2025-08-01 00:54:39,755 - services - DEBUG - Checking for package file: /app/instance/fhir_packages/example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:54:39,764 - services - DEBUG - Searching for SD matching 'Condition' with profile 'None' in example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:54:39,766 - services - DEBUG - Cleaned profile URL for search: 'None'
2025-08-01 00:54:39,791 - services - INFO - SD matching identifier 'Condition' or profile 'None' not found within archive example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:54:39,794 - services - DEBUG - SD not found in example.fhir.ph.core.r4#0.1.0. Checking dependencies.
2025-08-01 00:54:39,799 - services - DEBUG - Found dependencies: {'hl7.fhir.r4.core': '4.0.1', 'hl7.terminology.r4': '6.4.0', 'hl7.fhir.uv.extensions.r4': '5.2.0'}
2025-08-01 00:54:39,803 - services - DEBUG - Searching SD in dependency hl7.fhir.r4.core#4.0.1
2025-08-01 00:54:39,808 - services - DEBUG - Searching for SD matching 'Condition' with profile 'None' in hl7.fhir.r4.core-4.0.1.tgz
2025-08-01 00:54:39,810 - services - DEBUG - Cleaned profile URL for search: 'None'
2025-08-01 00:54:39,902 - services - INFO - Found SD in dependency hl7.fhir.r4.core#4.0.1 at package/StructureDefinition-Condition.json
2025-08-01 00:54:39,904 - services - DEBUG - Found SD at package/StructureDefinition-Condition.json
2025-08-01 00:54:39,905 - services - DEBUG - Navigating FHIR path: Condition, extension_url=None
2025-08-01 00:54:39,907 - services - DEBUG - Navigating FHIR path: Condition, extension_url=None
2025-08-01 00:54:39,908 - services - DEBUG - Navigating FHIR path: Condition.subject, extension_url=None
2025-08-01 00:54:39,910 - services - DEBUG - Validation result: valid=True, errors=0, warnings=0
2025-08-01 00:54:39,911 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:54:39,914 - services - DEBUG - Using provided profile: urn://example.com/ph-core/fhir/StructureDefinition/ph-core-medication. Cleaned to: urn://example.com/ph-core/fhir/StructureDefinition/ph-core-medication
2025-08-01 00:54:39,915 - services - DEBUG - Checking for package file: /app/instance/fhir_packages/example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:54:39,923 - services - DEBUG - Searching for SD matching 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-medication' with profile 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-medication' in example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:54:39,924 - services - DEBUG - Cleaned profile URL for search: 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-medication'
2025-08-01 00:54:39,934 - services - DEBUG - Removing narrative text from resource: StructureDefinition
2025-08-01 00:54:39,936 - services - INFO - Found definitive SD matching profile 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-medication' at path: package/StructureDefinition-ph-core-medication.json
2025-08-01 00:54:39,938 - services - DEBUG - Found SD at package/StructureDefinition-ph-core-medication.json
2025-08-01 00:54:39,939 - services - DEBUG - Navigating FHIR path: Medication, extension_url=None
2025-08-01 00:54:39,940 - services - DEBUG - Navigating FHIR path: Medication, extension_url=None
2025-08-01 00:54:39,942 - services - DEBUG - Validation result: valid=True, errors=0, warnings=0
2025-08-01 00:54:39,943 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:54:39,945 - services - DEBUG - Using provided profile: urn://example.com/ph-core/fhir/StructureDefinition/ph-core-observation. Cleaned to: urn://example.com/ph-core/fhir/StructureDefinition/ph-core-observation
2025-08-01 00:54:39,946 - services - DEBUG - Checking for package file: /app/instance/fhir_packages/example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:54:39,952 - services - DEBUG - Searching for SD matching 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-observation' with profile 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-observation' in example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:54:39,953 - services - DEBUG - Cleaned profile URL for search: 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-observation'
2025-08-01 00:54:39,964 - services - DEBUG - Removing narrative text from resource: StructureDefinition
2025-08-01 00:54:39,966 - services - INFO - Found definitive SD matching profile 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-observation' at path: package/StructureDefinition-ph-core-observation.json
2025-08-01 00:54:39,968 - services - DEBUG - Found SD at package/StructureDefinition-ph-core-observation.json
2025-08-01 00:54:39,969 - services - DEBUG - Navigating FHIR path: Observation, extension_url=None
2025-08-01 00:54:39,970 - services - DEBUG - Navigating FHIR path: Observation, extension_url=None
2025-08-01 00:54:39,971 - services - DEBUG - Navigating FHIR path: Observation.status, extension_url=None
2025-08-01 00:54:39,973 - services - DEBUG - Navigating FHIR path: Observation.code, extension_url=None
2025-08-01 00:54:39,975 - services - DEBUG - Validation result: valid=True, errors=0, warnings=0
2025-08-01 00:54:39,976 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:54:39,980 - services - DEBUG - No profile in resource, using base type as identifier: AllergyIntolerance
2025-08-01 00:54:39,981 - services - DEBUG - Checking for package file: /app/instance/fhir_packages/example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:54:39,988 - services - DEBUG - Searching for SD matching 'AllergyIntolerance' with profile 'None' in example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:54:39,990 - services - DEBUG - Cleaned profile URL for search: 'None'
2025-08-01 00:54:40,015 - services - INFO - SD matching identifier 'AllergyIntolerance' or profile 'None' not found within archive example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:54:40,017 - services - DEBUG - SD not found in example.fhir.ph.core.r4#0.1.0. Checking dependencies.
2025-08-01 00:54:40,022 - services - DEBUG - Found dependencies: {'hl7.fhir.r4.core': '4.0.1', 'hl7.terminology.r4': '6.4.0', 'hl7.fhir.uv.extensions.r4': '5.2.0'}
2025-08-01 00:54:40,024 - services - DEBUG - Searching SD in dependency hl7.fhir.r4.core#4.0.1
2025-08-01 00:54:40,028 - services - DEBUG - Searching for SD matching 'AllergyIntolerance' with profile 'None' in hl7.fhir.r4.core-4.0.1.tgz
2025-08-01 00:54:40,029 - services - DEBUG - Cleaned profile URL for search: 'None'
2025-08-01 00:54:41,057 - services - INFO - Found SD in dependency hl7.fhir.r4.core#4.0.1 at package/StructureDefinition-AllergyIntolerance.json
2025-08-01 00:54:41,059 - services - DEBUG - Found SD at package/StructureDefinition-AllergyIntolerance.json
2025-08-01 00:54:41,060 - services - DEBUG - Navigating FHIR path: AllergyIntolerance, extension_url=None
2025-08-01 00:54:41,061 - services - DEBUG - Navigating FHIR path: AllergyIntolerance, extension_url=None
2025-08-01 00:54:41,063 - services - DEBUG - Navigating FHIR path: AllergyIntolerance.patient, extension_url=None
2025-08-01 00:54:41,065 - services - DEBUG - Validation result: valid=True, errors=0, warnings=0
2025-08-01 00:54:41,066 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:54:41,070 - services - DEBUG - Using provided profile: urn://example.com/ph-core/fhir/StructureDefinition/ph-core-practitioner. Cleaned to: urn://example.com/ph-core/fhir/StructureDefinition/ph-core-practitioner
2025-08-01 00:54:41,071 - services - DEBUG - Checking for package file: /app/instance/fhir_packages/example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:54:41,077 - services - DEBUG - Searching for SD matching 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-practitioner' with profile 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-practitioner' in example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:54:41,078 - services - DEBUG - Cleaned profile URL for search: 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-practitioner'
2025-08-01 00:54:41,096 - services - DEBUG - Removing narrative text from resource: StructureDefinition
2025-08-01 00:54:41,098 - services - INFO - Found definitive SD matching profile 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-practitioner' at path: package/StructureDefinition-ph-core-practitioner.json
2025-08-01 00:54:41,100 - services - DEBUG - Found SD at package/StructureDefinition-ph-core-practitioner.json
2025-08-01 00:54:41,101 - services - DEBUG - Navigating FHIR path: Practitioner, extension_url=None
2025-08-01 00:54:41,103 - services - DEBUG - Navigating FHIR path: Practitioner, extension_url=None
2025-08-01 00:54:41,105 - services - DEBUG - Validation result: valid=True, errors=0, warnings=0
2025-08-01 00:54:41,106 - services - DEBUG - Bundle validation result: valid=True, errors=0, warnings=0, resources=7
2025-08-01 00:54:41,107 - services - INFO - Validation result for Bundle against example.fhir.ph.core.r4#0.1.0: valid=True, errors=0, warnings=0
2025-08-01 00:54:41,109 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:54:41] "POST /api/validate-sample HTTP/1.1" 200 -
2025-08-01 00:55:46,274 - __main__ - DEBUG - Proxy received request for path: '/fhir/metadata', cleaned subpath: 'metadata'
2025-08-01 00:55:46,275 - __main__ - DEBUG - No target header found, proxying to default local HAPI: http://localhost:8080/fhir
2025-08-01 00:55:46,276 - __main__ - INFO - Proxying request: GET http://localhost:8080/fhir/metadata
2025-08-01 00:55:46,278 - urllib3.connectionpool - DEBUG - Starting new HTTP connection (1): localhost:8080
2025-08-01 00:55:46,303 - urllib3.connectionpool - DEBUG - http://localhost:8080 "GET /fhir/metadata HTTP/1.1" 200 None
2025-08-01 00:55:46,409 - __main__ - INFO - Target server 'http://localhost:8080/fhir' responded with status: 200
2025-08-01 00:55:46,411 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:55:46] "GET /fhir/metadata HTTP/1.1" 200 -
2025-08-01 00:55:46,425 - services - DEBUG - Received validate-sample request
2025-08-01 00:55:46,427 - services - DEBUG - Request params: package_name=example.fhir.ph.core.r4, version=0.1.0, sample_data_length=713
2025-08-01 00:55:46,428 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:55:46,431 - services - DEBUG - Checking package file: /app/instance/fhir_packages/example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:55:46,434 - services - DEBUG - Validating AllergyIntolerance against example.fhir.ph.core.r4#0.1.0
2025-08-01 00:55:46,435 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:55:46,438 - services - DEBUG - No profile in resource, using base type as identifier: AllergyIntolerance
2025-08-01 00:55:46,439 - services - DEBUG - Checking for package file: /app/instance/fhir_packages/example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:55:46,445 - services - DEBUG - Searching for SD matching 'AllergyIntolerance' with profile 'None' in example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:55:46,446 - services - DEBUG - Cleaned profile URL for search: 'None'
2025-08-01 00:55:46,468 - services - INFO - SD matching identifier 'AllergyIntolerance' or profile 'None' not found within archive example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:55:46,470 - services - DEBUG - SD not found in example.fhir.ph.core.r4#0.1.0. Checking dependencies.
2025-08-01 00:55:46,475 - services - DEBUG - Found dependencies: {'hl7.fhir.r4.core': '4.0.1', 'hl7.terminology.r4': '6.4.0', 'hl7.fhir.uv.extensions.r4': '5.2.0'}
2025-08-01 00:55:46,477 - services - DEBUG - Searching SD in dependency hl7.fhir.r4.core#4.0.1
2025-08-01 00:55:46,481 - services - DEBUG - Searching for SD matching 'AllergyIntolerance' with profile 'None' in hl7.fhir.r4.core-4.0.1.tgz
2025-08-01 00:55:46,482 - services - DEBUG - Cleaned profile URL for search: 'None'
2025-08-01 00:55:47,472 - services - INFO - Found SD in dependency hl7.fhir.r4.core#4.0.1 at package/StructureDefinition-AllergyIntolerance.json
2025-08-01 00:55:47,474 - services - DEBUG - Found SD at package/StructureDefinition-AllergyIntolerance.json
2025-08-01 00:55:47,474 - services - DEBUG - Navigating FHIR path: AllergyIntolerance, extension_url=None
2025-08-01 00:55:47,476 - services - DEBUG - Navigating FHIR path: AllergyIntolerance, extension_url=None
2025-08-01 00:55:47,477 - services - DEBUG - Navigating FHIR path: AllergyIntolerance.patient, extension_url=None
2025-08-01 00:55:47,479 - services - DEBUG - Validation result: valid=True, errors=0, warnings=0
2025-08-01 00:55:47,480 - services - INFO - Validation result for AllergyIntolerance against example.fhir.ph.core.r4#0.1.0: valid=True, errors=0, warnings=0
2025-08-01 00:55:47,481 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:55:47] "POST /api/validate-sample HTTP/1.1" 200 -
2025-08-01 00:56:01,610 - __main__ - DEBUG - Proxy received request for path: '/fhir/metadata', cleaned subpath: 'metadata'
2025-08-01 00:56:01,611 - __main__ - DEBUG - No target header found, proxying to default local HAPI: http://localhost:8080/fhir
2025-08-01 00:56:01,612 - __main__ - INFO - Proxying request: GET http://localhost:8080/fhir/metadata
2025-08-01 00:56:01,614 - urllib3.connectionpool - DEBUG - Starting new HTTP connection (1): localhost:8080
2025-08-01 00:56:01,634 - urllib3.connectionpool - DEBUG - http://localhost:8080 "GET /fhir/metadata HTTP/1.1" 200 None
2025-08-01 00:56:01,712 - __main__ - INFO - Target server 'http://localhost:8080/fhir' responded with status: 200
2025-08-01 00:56:01,714 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:56:01] "GET /fhir/metadata HTTP/1.1" 200 -
2025-08-01 00:56:01,726 - services - DEBUG - Received validate-sample request
2025-08-01 00:56:01,727 - services - DEBUG - Request params: package_name=example.fhir.ph.core.r4, version=0.1.0, sample_data_length=713
2025-08-01 00:56:01,728 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:56:01,731 - services - DEBUG - Checking package file: /app/instance/fhir_packages/example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:56:01,733 - services - DEBUG - Validating AllergyIntolerance against example.fhir.ph.core.r4#0.1.0
2025-08-01 00:56:01,735 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:56:01,738 - services - DEBUG - No profile in resource, using base type as identifier: AllergyIntolerance
2025-08-01 00:56:01,739 - services - DEBUG - Checking for package file: /app/instance/fhir_packages/example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:56:01,743 - services - DEBUG - Searching for SD matching 'AllergyIntolerance' with profile 'None' in example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:56:01,744 - services - DEBUG - Cleaned profile URL for search: 'None'
2025-08-01 00:56:01,759 - services - INFO - SD matching identifier 'AllergyIntolerance' or profile 'None' not found within archive example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:56:01,761 - services - DEBUG - SD not found in example.fhir.ph.core.r4#0.1.0. Checking dependencies.
2025-08-01 00:56:01,764 - services - DEBUG - Found dependencies: {'hl7.fhir.r4.core': '4.0.1', 'hl7.terminology.r4': '6.4.0', 'hl7.fhir.uv.extensions.r4': '5.2.0'}
2025-08-01 00:56:01,766 - services - DEBUG - Searching SD in dependency hl7.fhir.r4.core#4.0.1
2025-08-01 00:56:01,770 - services - DEBUG - Searching for SD matching 'AllergyIntolerance' with profile 'None' in hl7.fhir.r4.core-4.0.1.tgz
2025-08-01 00:56:01,771 - services - DEBUG - Cleaned profile URL for search: 'None'
2025-08-01 00:56:02,425 - services - INFO - Found SD in dependency hl7.fhir.r4.core#4.0.1 at package/StructureDefinition-AllergyIntolerance.json
2025-08-01 00:56:02,427 - services - DEBUG - Found SD at package/StructureDefinition-AllergyIntolerance.json
2025-08-01 00:56:02,428 - services - DEBUG - Navigating FHIR path: AllergyIntolerance, extension_url=None
2025-08-01 00:56:02,430 - services - DEBUG - Navigating FHIR path: AllergyIntolerance, extension_url=None
2025-08-01 00:56:02,431 - services - DEBUG - Navigating FHIR path: AllergyIntolerance.patient, extension_url=None
2025-08-01 00:56:02,433 - services - DEBUG - Validation result: valid=True, errors=0, warnings=0
2025-08-01 00:56:02,434 - services - INFO - Validation result for AllergyIntolerance against example.fhir.ph.core.r4#0.1.0: valid=True, errors=0, warnings=0
2025-08-01 00:56:02,435 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:56:02] "POST /api/validate-sample HTTP/1.1" 200 -
2025-08-01 00:56:14,054 - __main__ - DEBUG - Proxy received request for path: '/fhir/metadata', cleaned subpath: 'metadata'
2025-08-01 00:56:14,055 - __main__ - DEBUG - No target header found, proxying to default local HAPI: http://localhost:8080/fhir
2025-08-01 00:56:14,056 - __main__ - INFO - Proxying request: GET http://localhost:8080/fhir/metadata
2025-08-01 00:56:14,057 - urllib3.connectionpool - DEBUG - Starting new HTTP connection (1): localhost:8080
2025-08-01 00:56:14,070 - urllib3.connectionpool - DEBUG - http://localhost:8080 "GET /fhir/metadata HTTP/1.1" 200 None
2025-08-01 00:56:14,147 - __main__ - INFO - Target server 'http://localhost:8080/fhir' responded with status: 200
2025-08-01 00:56:14,149 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:56:14] "GET /fhir/metadata HTTP/1.1" 200 -
2025-08-01 00:56:14,157 - services - DEBUG - Received validate-sample request
2025-08-01 00:56:14,158 - services - DEBUG - Request params: package_name=example.fhir.ph.core.r4, version=0.1.0, sample_data_length=713
2025-08-01 00:56:14,160 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:56:14,163 - services - DEBUG - Checking package file: /app/instance/fhir_packages/example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:56:14,166 - services - DEBUG - Validating AllergyIntolerance against example.fhir.ph.core.r4#0.1.0
2025-08-01 00:56:14,166 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:56:14,170 - services - DEBUG - No profile in resource, using base type as identifier: AllergyIntolerance
2025-08-01 00:56:14,172 - services - DEBUG - Checking for package file: /app/instance/fhir_packages/example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:56:14,177 - services - DEBUG - Searching for SD matching 'AllergyIntolerance' with profile 'None' in example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:56:14,178 - services - DEBUG - Cleaned profile URL for search: 'None'
2025-08-01 00:56:14,193 - services - INFO - SD matching identifier 'AllergyIntolerance' or profile 'None' not found within archive example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:56:14,195 - services - DEBUG - SD not found in example.fhir.ph.core.r4#0.1.0. Checking dependencies.
2025-08-01 00:56:14,197 - services - DEBUG - Found dependencies: {'hl7.fhir.r4.core': '4.0.1', 'hl7.terminology.r4': '6.4.0', 'hl7.fhir.uv.extensions.r4': '5.2.0'}
2025-08-01 00:56:14,198 - services - DEBUG - Searching SD in dependency hl7.fhir.r4.core#4.0.1
2025-08-01 00:56:14,201 - services - DEBUG - Searching for SD matching 'AllergyIntolerance' with profile 'None' in hl7.fhir.r4.core-4.0.1.tgz
2025-08-01 00:56:14,202 - services - DEBUG - Cleaned profile URL for search: 'None'
2025-08-01 00:56:14,875 - services - INFO - Found SD in dependency hl7.fhir.r4.core#4.0.1 at package/StructureDefinition-AllergyIntolerance.json
2025-08-01 00:56:14,877 - services - DEBUG - Found SD at package/StructureDefinition-AllergyIntolerance.json
2025-08-01 00:56:14,878 - services - DEBUG - Navigating FHIR path: AllergyIntolerance, extension_url=None
2025-08-01 00:56:14,879 - services - DEBUG - Navigating FHIR path: AllergyIntolerance, extension_url=None
2025-08-01 00:56:14,881 - services - DEBUG - Navigating FHIR path: AllergyIntolerance.patient, extension_url=None
2025-08-01 00:56:14,883 - services - DEBUG - Validation result: valid=True, errors=0, warnings=0
2025-08-01 00:56:14,884 - services - INFO - Validation result for AllergyIntolerance against example.fhir.ph.core.r4#0.1.0: valid=True, errors=0, warnings=0
2025-08-01 00:56:14,885 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:56:14] "POST /api/validate-sample HTTP/1.1" 200 -
2025-08-01 00:56:18,236 - __main__ - DEBUG - Proxy received request for path: '/fhir/metadata', cleaned subpath: 'metadata'
2025-08-01 00:56:18,237 - __main__ - DEBUG - No target header found, proxying to default local HAPI: http://localhost:8080/fhir
2025-08-01 00:56:18,238 - __main__ - INFO - Proxying request: GET http://localhost:8080/fhir/metadata
2025-08-01 00:56:18,239 - urllib3.connectionpool - DEBUG - Starting new HTTP connection (1): localhost:8080
2025-08-01 00:56:18,257 - urllib3.connectionpool - DEBUG - http://localhost:8080 "GET /fhir/metadata HTTP/1.1" 200 None
2025-08-01 00:56:18,348 - __main__ - INFO - Target server 'http://localhost:8080/fhir' responded with status: 200
2025-08-01 00:56:18,350 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:56:18] "GET /fhir/metadata HTTP/1.1" 200 -
2025-08-01 00:56:18,364 - services - DEBUG - Received validate-sample request
2025-08-01 00:56:18,366 - services - DEBUG - Request params: package_name=example.fhir.ph.core.r4, version=0.1.0, sample_data_length=713
2025-08-01 00:56:18,366 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:56:18,369 - services - DEBUG - Checking package file: /app/instance/fhir_packages/example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:56:18,373 - services - DEBUG - Validating AllergyIntolerance against example.fhir.ph.core.r4#0.1.0
2025-08-01 00:56:18,374 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:56:18,377 - services - DEBUG - No profile in resource, using base type as identifier: AllergyIntolerance
2025-08-01 00:56:18,378 - services - DEBUG - Checking for package file: /app/instance/fhir_packages/example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:56:18,383 - services - DEBUG - Searching for SD matching 'AllergyIntolerance' with profile 'None' in example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:56:18,384 - services - DEBUG - Cleaned profile URL for search: 'None'
2025-08-01 00:56:18,401 - services - INFO - SD matching identifier 'AllergyIntolerance' or profile 'None' not found within archive example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:56:18,403 - services - DEBUG - SD not found in example.fhir.ph.core.r4#0.1.0. Checking dependencies.
2025-08-01 00:56:18,406 - services - DEBUG - Found dependencies: {'hl7.fhir.r4.core': '4.0.1', 'hl7.terminology.r4': '6.4.0', 'hl7.fhir.uv.extensions.r4': '5.2.0'}
2025-08-01 00:56:18,408 - services - DEBUG - Searching SD in dependency hl7.fhir.r4.core#4.0.1
2025-08-01 00:56:18,412 - services - DEBUG - Searching for SD matching 'AllergyIntolerance' with profile 'None' in hl7.fhir.r4.core-4.0.1.tgz
2025-08-01 00:56:18,413 - services - DEBUG - Cleaned profile URL for search: 'None'
2025-08-01 00:56:19,183 - services - INFO - Found SD in dependency hl7.fhir.r4.core#4.0.1 at package/StructureDefinition-AllergyIntolerance.json
2025-08-01 00:56:19,184 - services - DEBUG - Found SD at package/StructureDefinition-AllergyIntolerance.json
2025-08-01 00:56:19,185 - services - DEBUG - Navigating FHIR path: AllergyIntolerance, extension_url=None
2025-08-01 00:56:19,186 - services - DEBUG - Navigating FHIR path: AllergyIntolerance, extension_url=None
2025-08-01 00:56:19,187 - services - DEBUG - Navigating FHIR path: AllergyIntolerance.patient, extension_url=None
2025-08-01 00:56:19,189 - services - DEBUG - Validation result: valid=True, errors=0, warnings=0
2025-08-01 00:56:19,190 - services - INFO - Validation result for AllergyIntolerance against example.fhir.ph.core.r4#0.1.0: valid=True, errors=0, warnings=0
2025-08-01 00:56:19,191 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:56:19] "POST /api/validate-sample HTTP/1.1" 200 -

View File

@ -1,6 +0,0 @@
#FileLock
#Fri Aug 01 00:44:59 UTC 2025
server=172.18.0.2\:44371
hostName=fd26f32c4551
method=file
id=1986316d3912ca2c5021ee56e639ff5ae831929a5f0

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -1,2 +0,0 @@
* Serving Flask app 'app'
* Debug mode: off

View File

@ -1,741 +0,0 @@
fhir.resources library failed to import. XML parsing will be basic and dependency analysis for XML may be incomplete. Error: No module named 'fhir.resources.fhirtypesvalidators'
2025-08-01 00:44:39,706 - __main__ - INFO - --- File logging initialized to /app/instance/fhirflare_debug.log (Level: DEBUG) ---
2025-08-01 00:44:39,707 - __main__ - INFO - Application running in mode: standalone
2025-08-01 00:44:39,708 - __main__ - DEBUG - Instance path configuration: /app/instance
2025-08-01 00:44:39,709 - __main__ - DEBUG - Database URI: sqlite:////app/instance/fhir_ig.db
2025-08-01 00:44:39,710 - __main__ - DEBUG - Packages path: /app/instance/fhir_packages
2025-08-01 00:44:39,724 - __main__ - DEBUG - Flask instance folder path: /app/instance
2025-08-01 00:44:39,731 - __main__ - DEBUG - Directories created/verified: Instance: /app/instance, Packages: /app/instance/fhir_packages
2025-08-01 00:44:39,759 - __main__ - DEBUG - Attempting to create database tables for URI: sqlite:////app/instance/fhir_ig.db
2025-08-01 00:44:39,797 - __main__ - INFO - Database tables created/verified successfully.
2025-08-01 00:44:39,804 - __main__ - DEBUG - Instance path configuration: /app/instance
2025-08-01 00:44:39,806 - __main__ - DEBUG - Database URI: sqlite:////app/instance/fhir_ig.db
2025-08-01 00:44:39,807 - __main__ - DEBUG - Packages path: /app/instance/fhir_packages
2025-08-01 00:44:39,808 - __main__ - DEBUG - Flask instance folder path: /app/instance
2025-08-01 00:44:39,811 - __main__ - DEBUG - Directories created/verified: Instance: /app/instance, Packages: /app/instance/fhir_packages
2025-08-01 00:44:39,812 - __main__ - DEBUG - Attempting to create database tables for URI: sqlite:////app/instance/fhir_ig.db
2025-08-01 00:44:39,822 - __main__ - INFO - Database tables created successfully (if they didn't exist).
2025-08-01 00:44:39,825 - werkzeug - INFO - WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on all addresses (0.0.0.0)
* Running on http://127.0.0.1:5000
* Running on http://172.18.0.2:5000
2025-08-01 00:44:39,826 - werkzeug - INFO - Press CTRL+C to quit
2025-08-01 00:45:00,413 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:45:00] "GET / HTTP/1.1" 200 -
2025-08-01 00:45:00,466 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:45:00] "GET /static/FHIRFLARE.png HTTP/1.1" 200 -
2025-08-01 00:45:00,481 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:45:00] "GET /static/css/fire-animation.css HTTP/1.1" 304 -
2025-08-01 00:45:00,493 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:45:00] "GET /static/js/htmx.min.js HTTP/1.1" 304 -
2025-08-01 00:45:00,539 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:45:00] "GET /static/css/animation.css HTTP/1.1" 304 -
2025-08-01 00:45:01,469 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:45:01] "GET /static/favicon.ico HTTP/1.1" 200 -
2025-08-01 00:45:06,325 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:45:06] "GET /manual-import-ig HTTP/1.1" 200 -
2025-08-01 00:45:06,344 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:45:06] "GET /static/css/fire-animation.css HTTP/1.1" 304 -
2025-08-01 00:45:06,348 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:45:06] "GET /static/css/animation.css HTTP/1.1" 304 -
2025-08-01 00:45:06,349 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:45:06] "GET /static/js/htmx.min.js HTTP/1.1" 304 -
2025-08-01 00:47:18,005 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:47:18] "GET /validate-sample HTTP/1.1" 200 -
2025-08-01 00:47:18,023 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:47:18] "GET /static/css/fire-animation.css HTTP/1.1" 304 -
2025-08-01 00:47:18,026 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:47:18] "GET /static/css/animation.css HTTP/1.1" 304 -
2025-08-01 00:47:18,030 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:47:18] "GET /static/js/htmx.min.js HTTP/1.1" 304 -
2025-08-01 00:47:18,034 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:47:18] "GET /static/FHIRFLARE.png HTTP/1.1" 304 -
2025-08-01 00:47:27,948 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:47:27] "GET /manual-import-ig HTTP/1.1" 200 -
2025-08-01 00:47:27,969 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:47:27] "GET /static/css/fire-animation.css HTTP/1.1" 304 -
2025-08-01 00:47:27,970 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:47:27] "GET /static/css/animation.css HTTP/1.1" 304 -
2025-08-01 00:47:27,972 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:47:27] "GET /static/js/htmx.min.js HTTP/1.1" 304 -
2025-08-01 00:47:43,826 - services - INFO - Starting manual import for /tmp/tmpo2f3s9g5/example.fhir.ph.core.r4-0.1.0.tgz (mode=recursive, resolve_deps=True)
2025-08-01 00:47:43,827 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:47:43,831 - services - DEBUG - Parsed 'example.fhir.ph.core.r4-0.1.0.tgz' -> name='example.fhir.ph.core.r4', version='0.1.0'
2025-08-01 00:47:43,841 - services - DEBUG - Parsed '/tmp/tmpo2f3s9g5/example.fhir.ph.core.r4-0.1.0.tgz' -> name='/tmp/tmpo2f3s9g5/example.fhir.ph.core.r4', version='0.1.0'
2025-08-01 00:47:43,842 - services - INFO - Manually processing package: example.fhir.ph.core.r4-0.1.0.tgz (/tmp/tmpo2f3s9g5/example.fhir.ph.core.r4#0.1.0)
2025-08-01 00:47:43,843 - services - INFO - Processing dependency hl7.fhir.r4.core#4.0.1
2025-08-01 00:47:43,844 - services - INFO - Starting manual import for hl7.fhir.r4.core (mode=recursive, resolve_deps=True)
2025-08-01 00:47:43,845 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:47:43,848 - services - INFO - Attempting manual download of hl7.fhir.r4.core#4.0.1
2025-08-01 00:47:43,850 - services - INFO - Manual package hl7.fhir.r4.core#4.0.1 already exists at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz
2025-08-01 00:47:43,852 - services - DEBUG - Parsed '/app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz' -> name='/app/instance/fhir.packages/hl7.fhir.r4.core', version='4.0.1'
2025-08-01 00:47:43,853 - services - INFO - Manually processing package: hl7.fhir.r4.core-4.0.1.tgz (/app/instance/fhir.packages/hl7.fhir.r4.core#4.0.1)
2025-08-01 00:47:44,379 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:47:44,388 - services - INFO - Saved metadata for hl7.fhir.r4.core#4.0.1 at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.metadata.json
2025-08-01 00:47:44,389 - services - INFO - Processing dependency hl7.terminology.r4#6.4.0
2025-08-01 00:47:44,390 - services - INFO - Starting manual import for hl7.terminology.r4 (mode=recursive, resolve_deps=True)
2025-08-01 00:47:44,391 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:47:44,393 - services - INFO - Attempting manual download of hl7.terminology.r4#6.4.0
2025-08-01 00:47:44,398 - urllib3.connectionpool - DEBUG - Starting new HTTPS connection (1): packages.fhir.org:443
2025-08-01 00:47:46,170 - urllib3.connectionpool - DEBUG - https://packages.fhir.org:443 "GET /hl7.terminology.r4/6.4.0 HTTP/1.1" 200 5053901
2025-08-01 00:47:49,116 - services - INFO - Manually downloaded hl7.terminology.r4#6.4.0 to /app/instance/fhir_packages/hl7.terminology.r4-6.4.0.tgz
2025-08-01 00:47:49,118 - services - DEBUG - Parsed '/app/instance/fhir_packages/hl7.terminology.r4-6.4.0.tgz' -> name='/app/instance/fhir.packages/hl7.terminology.r4', version='6.4.0'
2025-08-01 00:47:49,119 - services - INFO - Manually processing package: hl7.terminology.r4-6.4.0.tgz (/app/instance/fhir.packages/hl7.terminology.r4#6.4.0)
2025-08-01 00:47:49,132 - services - INFO - Processing dependency hl7.fhir.r4.core#4.0.1
2025-08-01 00:47:49,133 - services - INFO - Starting manual import for hl7.fhir.r4.core (mode=recursive, resolve_deps=True)
2025-08-01 00:47:49,133 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:47:49,135 - services - INFO - Attempting manual download of hl7.fhir.r4.core#4.0.1
2025-08-01 00:47:49,136 - services - INFO - Manual package hl7.fhir.r4.core#4.0.1 already exists at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz
2025-08-01 00:47:49,138 - services - DEBUG - Parsed '/app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz' -> name='/app/instance/fhir.packages/hl7.fhir.r4.core', version='4.0.1'
2025-08-01 00:47:49,138 - services - INFO - Manually processing package: hl7.fhir.r4.core-4.0.1.tgz (/app/instance/fhir.packages/hl7.fhir.r4.core#4.0.1)
2025-08-01 00:47:49,396 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:47:49,401 - services - INFO - Saved metadata for hl7.fhir.r4.core#4.0.1 at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.metadata.json
2025-08-01 00:47:49,401 - services - INFO - Processing dependency hl7.fhir.uv.extensions.r4#5.2.0
2025-08-01 00:47:49,402 - services - INFO - Starting manual import for hl7.fhir.uv.extensions.r4 (mode=recursive, resolve_deps=True)
2025-08-01 00:47:49,402 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:47:49,403 - services - INFO - Attempting manual download of hl7.fhir.uv.extensions.r4#5.2.0
2025-08-01 00:47:49,404 - services - INFO - Manual package hl7.fhir.uv.extensions.r4#5.2.0 already exists at /app/instance/fhir_packages/hl7.fhir.uv.extensions.r4-5.2.0.tgz
2025-08-01 00:47:49,406 - services - DEBUG - Parsed '/app/instance/fhir_packages/hl7.fhir.uv.extensions.r4-5.2.0.tgz' -> name='/app/instance/fhir.packages/hl7.fhir.uv.extensions.r4', version='5.2.0'
2025-08-01 00:47:49,406 - services - INFO - Manually processing package: hl7.fhir.uv.extensions.r4-5.2.0.tgz (/app/instance/fhir.packages/hl7.fhir.uv.extensions.r4#5.2.0)
2025-08-01 00:47:49,461 - services - INFO - Processing dependency hl7.fhir.r4.core#4.0.1
2025-08-01 00:47:49,462 - services - INFO - Starting manual import for hl7.fhir.r4.core (mode=recursive, resolve_deps=True)
2025-08-01 00:47:49,462 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:47:49,464 - services - INFO - Attempting manual download of hl7.fhir.r4.core#4.0.1
2025-08-01 00:47:49,465 - services - INFO - Manual package hl7.fhir.r4.core#4.0.1 already exists at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz
2025-08-01 00:47:49,466 - services - DEBUG - Parsed '/app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz' -> name='/app/instance/fhir.packages/hl7.fhir.r4.core', version='4.0.1'
2025-08-01 00:47:49,466 - services - INFO - Manually processing package: hl7.fhir.r4.core-4.0.1.tgz (/app/instance/fhir.packages/hl7.fhir.r4.core#4.0.1)
2025-08-01 00:47:49,731 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:47:49,735 - services - INFO - Saved metadata for hl7.fhir.r4.core#4.0.1 at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.metadata.json
2025-08-01 00:47:49,736 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:47:49,740 - services - INFO - Saved metadata for hl7.fhir.uv.extensions.r4#5.2.0 at /app/instance/fhir_packages/hl7.fhir.uv.extensions.r4-5.2.0.metadata.json
2025-08-01 00:47:49,740 - services - INFO - Processing dependency hl7.fhir.r4.core#4.0.1
2025-08-01 00:47:49,741 - services - INFO - Starting manual import for hl7.fhir.r4.core (mode=recursive, resolve_deps=True)
2025-08-01 00:47:49,741 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:47:49,743 - services - INFO - Attempting manual download of hl7.fhir.r4.core#4.0.1
2025-08-01 00:47:49,745 - services - INFO - Manual package hl7.fhir.r4.core#4.0.1 already exists at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz
2025-08-01 00:47:49,746 - services - DEBUG - Parsed '/app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz' -> name='/app/instance/fhir.packages/hl7.fhir.r4.core', version='4.0.1'
2025-08-01 00:47:49,747 - services - INFO - Manually processing package: hl7.fhir.r4.core-4.0.1.tgz (/app/instance/fhir.packages/hl7.fhir.r4.core#4.0.1)
2025-08-01 00:47:50,011 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:47:50,014 - services - INFO - Saved metadata for hl7.fhir.r4.core#4.0.1 at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.metadata.json
2025-08-01 00:47:50,015 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:47:50,018 - services - INFO - Saved metadata for hl7.terminology.r4#6.4.0 at /app/instance/fhir_packages/hl7.terminology.r4-6.4.0.metadata.json
2025-08-01 00:47:50,019 - services - INFO - Processing dependency hl7.fhir.uv.extensions.r4#5.2.0
2025-08-01 00:47:50,019 - services - INFO - Starting manual import for hl7.fhir.uv.extensions.r4 (mode=recursive, resolve_deps=True)
2025-08-01 00:47:50,019 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:47:50,020 - services - INFO - Attempting manual download of hl7.fhir.uv.extensions.r4#5.2.0
2025-08-01 00:47:50,021 - services - INFO - Manual package hl7.fhir.uv.extensions.r4#5.2.0 already exists at /app/instance/fhir_packages/hl7.fhir.uv.extensions.r4-5.2.0.tgz
2025-08-01 00:47:50,022 - services - DEBUG - Parsed '/app/instance/fhir_packages/hl7.fhir.uv.extensions.r4-5.2.0.tgz' -> name='/app/instance/fhir.packages/hl7.fhir.uv.extensions.r4', version='5.2.0'
2025-08-01 00:47:50,023 - services - INFO - Manually processing package: hl7.fhir.uv.extensions.r4-5.2.0.tgz (/app/instance/fhir.packages/hl7.fhir.uv.extensions.r4#5.2.0)
2025-08-01 00:47:50,079 - services - INFO - Processing dependency hl7.fhir.r4.core#4.0.1
2025-08-01 00:47:50,080 - services - INFO - Starting manual import for hl7.fhir.r4.core (mode=recursive, resolve_deps=True)
2025-08-01 00:47:50,081 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:47:50,082 - services - INFO - Attempting manual download of hl7.fhir.r4.core#4.0.1
2025-08-01 00:47:50,083 - services - INFO - Manual package hl7.fhir.r4.core#4.0.1 already exists at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz
2025-08-01 00:47:50,083 - services - DEBUG - Parsed '/app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz' -> name='/app/instance/fhir.packages/hl7.fhir.r4.core', version='4.0.1'
2025-08-01 00:47:50,084 - services - INFO - Manually processing package: hl7.fhir.r4.core-4.0.1.tgz (/app/instance/fhir.packages/hl7.fhir.r4.core#4.0.1)
2025-08-01 00:47:50,353 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:47:50,357 - services - INFO - Saved metadata for hl7.fhir.r4.core#4.0.1 at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.metadata.json
2025-08-01 00:47:50,358 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:47:50,364 - services - INFO - Saved metadata for hl7.fhir.uv.extensions.r4#5.2.0 at /app/instance/fhir_packages/hl7.fhir.uv.extensions.r4-5.2.0.metadata.json
2025-08-01 00:47:50,364 - services - INFO - Processing dependency hl7.fhir.r4.core#4.0.1
2025-08-01 00:47:50,365 - services - INFO - Starting manual import for hl7.fhir.r4.core (mode=recursive, resolve_deps=True)
2025-08-01 00:47:50,365 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:47:50,366 - services - INFO - Attempting manual download of hl7.fhir.r4.core#4.0.1
2025-08-01 00:47:50,367 - services - INFO - Manual package hl7.fhir.r4.core#4.0.1 already exists at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz
2025-08-01 00:47:50,368 - services - DEBUG - Parsed '/app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz' -> name='/app/instance/fhir.packages/hl7.fhir.r4.core', version='4.0.1'
2025-08-01 00:47:50,369 - services - INFO - Manually processing package: hl7.fhir.r4.core-4.0.1.tgz (/app/instance/fhir.packages/hl7.fhir.r4.core#4.0.1)
2025-08-01 00:47:50,631 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:47:50,634 - services - INFO - Saved metadata for hl7.fhir.r4.core#4.0.1 at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.metadata.json
2025-08-01 00:47:50,634 - services - INFO - Processing dependency hl7.fhir.uv.extensions.r4#5.2.0
2025-08-01 00:47:50,635 - services - INFO - Starting manual import for hl7.fhir.uv.extensions.r4 (mode=recursive, resolve_deps=True)
2025-08-01 00:47:50,636 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:47:50,637 - services - INFO - Attempting manual download of hl7.fhir.uv.extensions.r4#5.2.0
2025-08-01 00:47:50,638 - services - INFO - Manual package hl7.fhir.uv.extensions.r4#5.2.0 already exists at /app/instance/fhir_packages/hl7.fhir.uv.extensions.r4-5.2.0.tgz
2025-08-01 00:47:50,639 - services - DEBUG - Parsed '/app/instance/fhir_packages/hl7.fhir.uv.extensions.r4-5.2.0.tgz' -> name='/app/instance/fhir.packages/hl7.fhir.uv.extensions.r4', version='5.2.0'
2025-08-01 00:47:50,639 - services - INFO - Manually processing package: hl7.fhir.uv.extensions.r4-5.2.0.tgz (/app/instance/fhir.packages/hl7.fhir.uv.extensions.r4#5.2.0)
2025-08-01 00:47:50,694 - services - INFO - Processing dependency hl7.fhir.r4.core#4.0.1
2025-08-01 00:47:50,695 - services - INFO - Starting manual import for hl7.fhir.r4.core (mode=recursive, resolve_deps=True)
2025-08-01 00:47:50,695 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:47:50,697 - services - INFO - Attempting manual download of hl7.fhir.r4.core#4.0.1
2025-08-01 00:47:50,697 - services - INFO - Manual package hl7.fhir.r4.core#4.0.1 already exists at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz
2025-08-01 00:47:50,698 - services - DEBUG - Parsed '/app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz' -> name='/app/instance/fhir.packages/hl7.fhir.r4.core', version='4.0.1'
2025-08-01 00:47:50,699 - services - INFO - Manually processing package: hl7.fhir.r4.core-4.0.1.tgz (/app/instance/fhir.packages/hl7.fhir.r4.core#4.0.1)
2025-08-01 00:47:50,986 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:47:50,989 - services - INFO - Saved metadata for hl7.fhir.r4.core#4.0.1 at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.metadata.json
2025-08-01 00:47:50,990 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:47:50,993 - services - INFO - Saved metadata for hl7.fhir.uv.extensions.r4#5.2.0 at /app/instance/fhir_packages/hl7.fhir.uv.extensions.r4-5.2.0.metadata.json
2025-08-01 00:47:50,994 - services - INFO - Processing dependency hl7.fhir.r4.core#4.0.1
2025-08-01 00:47:50,994 - services - INFO - Starting manual import for hl7.fhir.r4.core (mode=recursive, resolve_deps=True)
2025-08-01 00:47:50,995 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:47:50,997 - services - INFO - Attempting manual download of hl7.fhir.r4.core#4.0.1
2025-08-01 00:47:50,998 - services - INFO - Manual package hl7.fhir.r4.core#4.0.1 already exists at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz
2025-08-01 00:47:50,999 - services - DEBUG - Parsed '/app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz' -> name='/app/instance/fhir.packages/hl7.fhir.r4.core', version='4.0.1'
2025-08-01 00:47:51,000 - services - INFO - Manually processing package: hl7.fhir.r4.core-4.0.1.tgz (/app/instance/fhir.packages/hl7.fhir.r4.core#4.0.1)
2025-08-01 00:47:51,247 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:47:51,250 - services - INFO - Saved metadata for hl7.fhir.r4.core#4.0.1 at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.metadata.json
2025-08-01 00:47:51,251 - services - INFO - Processing dependency hl7.fhir.r4.core#4.0.1
2025-08-01 00:47:51,252 - services - INFO - Starting manual import for hl7.fhir.r4.core (mode=recursive, resolve_deps=True)
2025-08-01 00:47:51,252 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:47:51,253 - services - INFO - Attempting manual download of hl7.fhir.r4.core#4.0.1
2025-08-01 00:47:51,254 - services - INFO - Manual package hl7.fhir.r4.core#4.0.1 already exists at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz
2025-08-01 00:47:51,255 - services - DEBUG - Parsed '/app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz' -> name='/app/instance/fhir.packages/hl7.fhir.r4.core', version='4.0.1'
2025-08-01 00:47:51,255 - services - INFO - Manually processing package: hl7.fhir.r4.core-4.0.1.tgz (/app/instance/fhir.packages/hl7.fhir.r4.core#4.0.1)
2025-08-01 00:47:51,509 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:47:51,512 - services - INFO - Saved metadata for hl7.fhir.r4.core#4.0.1 at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.metadata.json
2025-08-01 00:47:51,513 - services - INFO - Processing dependency hl7.fhir.r4.core#4.0.1
2025-08-01 00:47:51,513 - services - INFO - Starting manual import for hl7.fhir.r4.core (mode=recursive, resolve_deps=True)
2025-08-01 00:47:51,514 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:47:51,515 - services - INFO - Attempting manual download of hl7.fhir.r4.core#4.0.1
2025-08-01 00:47:51,516 - services - INFO - Manual package hl7.fhir.r4.core#4.0.1 already exists at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz
2025-08-01 00:47:51,518 - services - DEBUG - Parsed '/app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz' -> name='/app/instance/fhir.packages/hl7.fhir.r4.core', version='4.0.1'
2025-08-01 00:47:51,518 - services - INFO - Manually processing package: hl7.fhir.r4.core-4.0.1.tgz (/app/instance/fhir.packages/hl7.fhir.r4.core#4.0.1)
2025-08-01 00:47:51,779 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:47:51,782 - services - INFO - Saved metadata for hl7.fhir.r4.core#4.0.1 at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.metadata.json
2025-08-01 00:47:51,783 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:47:51,790 - services - INFO - Saved metadata for example.fhir.ph.core.r4#0.1.0 at /app/instance/fhir_packages/example.fhir.ph.core.r4-0.1.0.metadata.json
2025-08-01 00:47:51,792 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:47:51] "POST /manual-import-ig HTTP/1.1" 302 -
2025-08-01 00:47:51,808 - __main__ - DEBUG - Scanning packages directory: /app/instance/fhir_packages
2025-08-01 00:47:51,811 - services - DEBUG - Parsed 'example.fhir.ph.core.r4-0.1.0.tgz' -> name='example.fhir.ph.core.r4', version='0.1.0'
2025-08-01 00:47:51,819 - services - DEBUG - Parsed 'hl7.fhir.au.base-5.1.0-preview.tgz' -> name='hl7.fhir.au.base', version='5.1.0-preview'
2025-08-01 00:47:51,846 - services - DEBUG - Parsed 'hl7.fhir.au.core-1.1.0-preview.tgz' -> name='hl7.fhir.au.core', version='1.1.0-preview'
2025-08-01 00:47:51,863 - services - DEBUG - Parsed 'hl7.fhir.r4.core-4.0.1.tgz' -> name='hl7.fhir.r4.core', version='4.0.1'
2025-08-01 00:47:52,313 - services - DEBUG - Parsed 'hl7.fhir.uv.extensions.r4-5.2.0.tgz' -> name='hl7.fhir.uv.extensions.r4', version='5.2.0'
2025-08-01 00:47:52,375 - services - DEBUG - Parsed 'hl7.fhir.uv.ipa-1.0.0.tgz' -> name='hl7.fhir.uv.ipa', version='1.0.0'
2025-08-01 00:47:52,384 - services - DEBUG - Parsed 'hl7.fhir.uv.smart-app-launch-2.0.0.tgz' -> name='hl7.fhir.uv.smart-app-launch', version='2.0.0'
2025-08-01 00:47:52,387 - services - DEBUG - Parsed 'hl7.fhir.uv.smart-app-launch-2.1.0.tgz' -> name='hl7.fhir.uv.smart-app-launch', version='2.1.0'
2025-08-01 00:47:52,391 - services - DEBUG - Parsed 'hl7.terminology.r4-5.0.0.tgz' -> name='hl7.terminology.r4', version='5.0.0'
2025-08-01 00:47:52,627 - services - DEBUG - Parsed 'hl7.terminology.r4-6.2.0.tgz' -> name='hl7.terminology.r4', version='6.2.0'
2025-08-01 00:47:52,920 - services - DEBUG - Parsed 'hl7.terminology.r4-6.4.0.tgz' -> name='hl7.terminology.r4', version='6.4.0'
2025-08-01 00:47:53,250 - __main__ - DEBUG - Found packages: 11
2025-08-01 00:47:53,250 - __main__ - DEBUG - Errors during package listing: []
2025-08-01 00:47:53,251 - __main__ - DEBUG - Duplicate groups: {'hl7.fhir.uv.smart-app-launch': ['2.0.0', '2.1.0'], 'hl7.terminology.r4': ['5.0.0', '6.2.0', '6.4.0']}
2025-08-01 00:47:53,269 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:47:53] "GET /view-igs HTTP/1.1" 200 -
2025-08-01 00:47:53,299 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:47:53] "GET /static/css/fire-animation.css HTTP/1.1" 304 -
2025-08-01 00:47:53,301 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:47:53] "GET /static/css/animation.css HTTP/1.1" 304 -
2025-08-01 00:47:53,304 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:47:53] "GET /static/FHIRFLARE.png HTTP/1.1" 304 -
2025-08-01 00:47:53,307 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:47:53] "GET /static/js/htmx.min.js HTTP/1.1" 304 -
2025-08-01 00:47:55,256 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:47:55] "GET /manual-import-ig HTTP/1.1" 200 -
2025-08-01 00:47:55,270 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:47:55] "GET /static/css/fire-animation.css HTTP/1.1" 304 -
2025-08-01 00:47:55,272 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:47:55] "GET /static/css/animation.css HTTP/1.1" 304 -
2025-08-01 00:47:55,275 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:47:55] "GET /static/js/htmx.min.js HTTP/1.1" 304 -
2025-08-01 00:48:09,986 - services - INFO - Starting manual import for /tmp/tmpa5kg6gav/example.fhir.ph.core.r4-0.1.0.tgz (mode=recursive, resolve_deps=True)
2025-08-01 00:48:09,987 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:48:09,989 - services - DEBUG - Parsed 'example.fhir.ph.core.r4-0.1.0.tgz' -> name='example.fhir.ph.core.r4', version='0.1.0'
2025-08-01 00:48:09,995 - services - DEBUG - Parsed '/tmp/tmpa5kg6gav/example.fhir.ph.core.r4-0.1.0.tgz' -> name='/tmp/tmpa5kg6gav/example.fhir.ph.core.r4', version='0.1.0'
2025-08-01 00:48:09,996 - services - INFO - Manually processing package: example.fhir.ph.core.r4-0.1.0.tgz (/tmp/tmpa5kg6gav/example.fhir.ph.core.r4#0.1.0)
2025-08-01 00:48:09,997 - services - INFO - Processing dependency hl7.fhir.r4.core#4.0.1
2025-08-01 00:48:09,997 - services - INFO - Starting manual import for hl7.fhir.r4.core (mode=recursive, resolve_deps=True)
2025-08-01 00:48:09,997 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:48:09,998 - services - INFO - Attempting manual download of hl7.fhir.r4.core#4.0.1
2025-08-01 00:48:10,000 - services - INFO - Manual package hl7.fhir.r4.core#4.0.1 already exists at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz
2025-08-01 00:48:10,002 - services - DEBUG - Parsed '/app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz' -> name='/app/instance/fhir.packages/hl7.fhir.r4.core', version='4.0.1'
2025-08-01 00:48:10,003 - services - INFO - Manually processing package: hl7.fhir.r4.core-4.0.1.tgz (/app/instance/fhir.packages/hl7.fhir.r4.core#4.0.1)
2025-08-01 00:48:10,319 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:48:10,323 - services - INFO - Saved metadata for hl7.fhir.r4.core#4.0.1 at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.metadata.json
2025-08-01 00:48:10,324 - services - INFO - Processing dependency hl7.terminology.r4#6.4.0
2025-08-01 00:48:10,324 - services - INFO - Starting manual import for hl7.terminology.r4 (mode=recursive, resolve_deps=True)
2025-08-01 00:48:10,324 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:48:10,325 - services - INFO - Attempting manual download of hl7.terminology.r4#6.4.0
2025-08-01 00:48:10,326 - services - INFO - Manual package hl7.terminology.r4#6.4.0 already exists at /app/instance/fhir_packages/hl7.terminology.r4-6.4.0.tgz
2025-08-01 00:48:10,327 - services - DEBUG - Parsed '/app/instance/fhir_packages/hl7.terminology.r4-6.4.0.tgz' -> name='/app/instance/fhir.packages/hl7.terminology.r4', version='6.4.0'
2025-08-01 00:48:10,328 - services - INFO - Manually processing package: hl7.terminology.r4-6.4.0.tgz (/app/instance/fhir.packages/hl7.terminology.r4#6.4.0)
2025-08-01 00:48:10,330 - services - INFO - Processing dependency hl7.fhir.r4.core#4.0.1
2025-08-01 00:48:10,330 - services - INFO - Starting manual import for hl7.fhir.r4.core (mode=recursive, resolve_deps=True)
2025-08-01 00:48:10,331 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:48:10,332 - services - INFO - Attempting manual download of hl7.fhir.r4.core#4.0.1
2025-08-01 00:48:10,334 - services - INFO - Manual package hl7.fhir.r4.core#4.0.1 already exists at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz
2025-08-01 00:48:10,336 - services - DEBUG - Parsed '/app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz' -> name='/app/instance/fhir.packages/hl7.fhir.r4.core', version='4.0.1'
2025-08-01 00:48:10,336 - services - INFO - Manually processing package: hl7.fhir.r4.core-4.0.1.tgz (/app/instance/fhir.packages/hl7.fhir.r4.core#4.0.1)
2025-08-01 00:48:10,586 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:48:10,590 - services - INFO - Saved metadata for hl7.fhir.r4.core#4.0.1 at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.metadata.json
2025-08-01 00:48:10,590 - services - INFO - Processing dependency hl7.fhir.uv.extensions.r4#5.2.0
2025-08-01 00:48:10,591 - services - INFO - Starting manual import for hl7.fhir.uv.extensions.r4 (mode=recursive, resolve_deps=True)
2025-08-01 00:48:10,591 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:48:10,593 - services - INFO - Attempting manual download of hl7.fhir.uv.extensions.r4#5.2.0
2025-08-01 00:48:10,594 - services - INFO - Manual package hl7.fhir.uv.extensions.r4#5.2.0 already exists at /app/instance/fhir_packages/hl7.fhir.uv.extensions.r4-5.2.0.tgz
2025-08-01 00:48:10,595 - services - DEBUG - Parsed '/app/instance/fhir_packages/hl7.fhir.uv.extensions.r4-5.2.0.tgz' -> name='/app/instance/fhir.packages/hl7.fhir.uv.extensions.r4', version='5.2.0'
2025-08-01 00:48:10,596 - services - INFO - Manually processing package: hl7.fhir.uv.extensions.r4-5.2.0.tgz (/app/instance/fhir.packages/hl7.fhir.uv.extensions.r4#5.2.0)
2025-08-01 00:48:10,655 - services - INFO - Processing dependency hl7.fhir.r4.core#4.0.1
2025-08-01 00:48:10,655 - services - INFO - Starting manual import for hl7.fhir.r4.core (mode=recursive, resolve_deps=True)
2025-08-01 00:48:10,656 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:48:10,657 - services - INFO - Attempting manual download of hl7.fhir.r4.core#4.0.1
2025-08-01 00:48:10,659 - services - INFO - Manual package hl7.fhir.r4.core#4.0.1 already exists at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz
2025-08-01 00:48:10,660 - services - DEBUG - Parsed '/app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz' -> name='/app/instance/fhir.packages/hl7.fhir.r4.core', version='4.0.1'
2025-08-01 00:48:10,661 - services - INFO - Manually processing package: hl7.fhir.r4.core-4.0.1.tgz (/app/instance/fhir.packages/hl7.fhir.r4.core#4.0.1)
2025-08-01 00:48:10,997 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:48:11,004 - services - INFO - Saved metadata for hl7.fhir.r4.core#4.0.1 at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.metadata.json
2025-08-01 00:48:11,005 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:48:11,008 - services - INFO - Saved metadata for hl7.fhir.uv.extensions.r4#5.2.0 at /app/instance/fhir_packages/hl7.fhir.uv.extensions.r4-5.2.0.metadata.json
2025-08-01 00:48:11,009 - services - INFO - Processing dependency hl7.fhir.r4.core#4.0.1
2025-08-01 00:48:11,010 - services - INFO - Starting manual import for hl7.fhir.r4.core (mode=recursive, resolve_deps=True)
2025-08-01 00:48:11,011 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:48:11,012 - services - INFO - Attempting manual download of hl7.fhir.r4.core#4.0.1
2025-08-01 00:48:11,014 - services - INFO - Manual package hl7.fhir.r4.core#4.0.1 already exists at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz
2025-08-01 00:48:11,016 - services - DEBUG - Parsed '/app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz' -> name='/app/instance/fhir.packages/hl7.fhir.r4.core', version='4.0.1'
2025-08-01 00:48:11,016 - services - INFO - Manually processing package: hl7.fhir.r4.core-4.0.1.tgz (/app/instance/fhir.packages/hl7.fhir.r4.core#4.0.1)
2025-08-01 00:48:11,275 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:48:11,279 - services - INFO - Saved metadata for hl7.fhir.r4.core#4.0.1 at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.metadata.json
2025-08-01 00:48:11,280 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:48:11,283 - services - INFO - Saved metadata for hl7.terminology.r4#6.4.0 at /app/instance/fhir_packages/hl7.terminology.r4-6.4.0.metadata.json
2025-08-01 00:48:11,284 - services - INFO - Processing dependency hl7.fhir.uv.extensions.r4#5.2.0
2025-08-01 00:48:11,285 - services - INFO - Starting manual import for hl7.fhir.uv.extensions.r4 (mode=recursive, resolve_deps=True)
2025-08-01 00:48:11,285 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:48:11,287 - services - INFO - Attempting manual download of hl7.fhir.uv.extensions.r4#5.2.0
2025-08-01 00:48:11,288 - services - INFO - Manual package hl7.fhir.uv.extensions.r4#5.2.0 already exists at /app/instance/fhir_packages/hl7.fhir.uv.extensions.r4-5.2.0.tgz
2025-08-01 00:48:11,289 - services - DEBUG - Parsed '/app/instance/fhir_packages/hl7.fhir.uv.extensions.r4-5.2.0.tgz' -> name='/app/instance/fhir.packages/hl7.fhir.uv.extensions.r4', version='5.2.0'
2025-08-01 00:48:11,289 - services - INFO - Manually processing package: hl7.fhir.uv.extensions.r4-5.2.0.tgz (/app/instance/fhir.packages/hl7.fhir.uv.extensions.r4#5.2.0)
2025-08-01 00:48:11,344 - services - INFO - Processing dependency hl7.fhir.r4.core#4.0.1
2025-08-01 00:48:11,345 - services - INFO - Starting manual import for hl7.fhir.r4.core (mode=recursive, resolve_deps=True)
2025-08-01 00:48:11,345 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:48:11,346 - services - INFO - Attempting manual download of hl7.fhir.r4.core#4.0.1
2025-08-01 00:48:11,347 - services - INFO - Manual package hl7.fhir.r4.core#4.0.1 already exists at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz
2025-08-01 00:48:11,348 - services - DEBUG - Parsed '/app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz' -> name='/app/instance/fhir.packages/hl7.fhir.r4.core', version='4.0.1'
2025-08-01 00:48:11,349 - services - INFO - Manually processing package: hl7.fhir.r4.core-4.0.1.tgz (/app/instance/fhir.packages/hl7.fhir.r4.core#4.0.1)
2025-08-01 00:48:11,601 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:48:11,605 - services - INFO - Saved metadata for hl7.fhir.r4.core#4.0.1 at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.metadata.json
2025-08-01 00:48:11,605 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:48:11,609 - services - INFO - Saved metadata for hl7.fhir.uv.extensions.r4#5.2.0 at /app/instance/fhir_packages/hl7.fhir.uv.extensions.r4-5.2.0.metadata.json
2025-08-01 00:48:11,610 - services - INFO - Processing dependency hl7.fhir.r4.core#4.0.1
2025-08-01 00:48:11,610 - services - INFO - Starting manual import for hl7.fhir.r4.core (mode=recursive, resolve_deps=True)
2025-08-01 00:48:11,611 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:48:11,613 - services - INFO - Attempting manual download of hl7.fhir.r4.core#4.0.1
2025-08-01 00:48:11,614 - services - INFO - Manual package hl7.fhir.r4.core#4.0.1 already exists at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz
2025-08-01 00:48:11,615 - services - DEBUG - Parsed '/app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz' -> name='/app/instance/fhir.packages/hl7.fhir.r4.core', version='4.0.1'
2025-08-01 00:48:11,616 - services - INFO - Manually processing package: hl7.fhir.r4.core-4.0.1.tgz (/app/instance/fhir.packages/hl7.fhir.r4.core#4.0.1)
2025-08-01 00:48:11,867 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:48:11,871 - services - INFO - Saved metadata for hl7.fhir.r4.core#4.0.1 at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.metadata.json
2025-08-01 00:48:11,871 - services - INFO - Processing dependency hl7.fhir.uv.extensions.r4#5.2.0
2025-08-01 00:48:11,872 - services - INFO - Starting manual import for hl7.fhir.uv.extensions.r4 (mode=recursive, resolve_deps=True)
2025-08-01 00:48:11,872 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:48:11,874 - services - INFO - Attempting manual download of hl7.fhir.uv.extensions.r4#5.2.0
2025-08-01 00:48:11,875 - services - INFO - Manual package hl7.fhir.uv.extensions.r4#5.2.0 already exists at /app/instance/fhir_packages/hl7.fhir.uv.extensions.r4-5.2.0.tgz
2025-08-01 00:48:11,876 - services - DEBUG - Parsed '/app/instance/fhir_packages/hl7.fhir.uv.extensions.r4-5.2.0.tgz' -> name='/app/instance/fhir.packages/hl7.fhir.uv.extensions.r4', version='5.2.0'
2025-08-01 00:48:11,877 - services - INFO - Manually processing package: hl7.fhir.uv.extensions.r4-5.2.0.tgz (/app/instance/fhir.packages/hl7.fhir.uv.extensions.r4#5.2.0)
2025-08-01 00:48:11,932 - services - INFO - Processing dependency hl7.fhir.r4.core#4.0.1
2025-08-01 00:48:11,932 - services - INFO - Starting manual import for hl7.fhir.r4.core (mode=recursive, resolve_deps=True)
2025-08-01 00:48:11,933 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:48:11,935 - services - INFO - Attempting manual download of hl7.fhir.r4.core#4.0.1
2025-08-01 00:48:11,936 - services - INFO - Manual package hl7.fhir.r4.core#4.0.1 already exists at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz
2025-08-01 00:48:11,937 - services - DEBUG - Parsed '/app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz' -> name='/app/instance/fhir.packages/hl7.fhir.r4.core', version='4.0.1'
2025-08-01 00:48:11,937 - services - INFO - Manually processing package: hl7.fhir.r4.core-4.0.1.tgz (/app/instance/fhir.packages/hl7.fhir.r4.core#4.0.1)
2025-08-01 00:48:12,191 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:48:12,194 - services - INFO - Saved metadata for hl7.fhir.r4.core#4.0.1 at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.metadata.json
2025-08-01 00:48:12,194 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:48:12,198 - services - INFO - Saved metadata for hl7.fhir.uv.extensions.r4#5.2.0 at /app/instance/fhir_packages/hl7.fhir.uv.extensions.r4-5.2.0.metadata.json
2025-08-01 00:48:12,199 - services - INFO - Processing dependency hl7.fhir.r4.core#4.0.1
2025-08-01 00:48:12,200 - services - INFO - Starting manual import for hl7.fhir.r4.core (mode=recursive, resolve_deps=True)
2025-08-01 00:48:12,201 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:48:12,202 - services - INFO - Attempting manual download of hl7.fhir.r4.core#4.0.1
2025-08-01 00:48:12,203 - services - INFO - Manual package hl7.fhir.r4.core#4.0.1 already exists at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz
2025-08-01 00:48:12,205 - services - DEBUG - Parsed '/app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz' -> name='/app/instance/fhir.packages/hl7.fhir.r4.core', version='4.0.1'
2025-08-01 00:48:12,205 - services - INFO - Manually processing package: hl7.fhir.r4.core-4.0.1.tgz (/app/instance/fhir.packages/hl7.fhir.r4.core#4.0.1)
2025-08-01 00:48:12,451 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:48:12,456 - services - INFO - Saved metadata for hl7.fhir.r4.core#4.0.1 at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.metadata.json
2025-08-01 00:48:12,456 - services - INFO - Processing dependency hl7.fhir.r4.core#4.0.1
2025-08-01 00:48:12,457 - services - INFO - Starting manual import for hl7.fhir.r4.core (mode=recursive, resolve_deps=True)
2025-08-01 00:48:12,457 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:48:12,458 - services - INFO - Attempting manual download of hl7.fhir.r4.core#4.0.1
2025-08-01 00:48:12,459 - services - INFO - Manual package hl7.fhir.r4.core#4.0.1 already exists at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz
2025-08-01 00:48:12,460 - services - DEBUG - Parsed '/app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz' -> name='/app/instance/fhir.packages/hl7.fhir.r4.core', version='4.0.1'
2025-08-01 00:48:12,461 - services - INFO - Manually processing package: hl7.fhir.r4.core-4.0.1.tgz (/app/instance/fhir.packages/hl7.fhir.r4.core#4.0.1)
2025-08-01 00:48:12,720 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:48:12,723 - services - INFO - Saved metadata for hl7.fhir.r4.core#4.0.1 at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.metadata.json
2025-08-01 00:48:12,724 - services - INFO - Processing dependency hl7.fhir.r4.core#4.0.1
2025-08-01 00:48:12,724 - services - INFO - Starting manual import for hl7.fhir.r4.core (mode=recursive, resolve_deps=True)
2025-08-01 00:48:12,725 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:48:12,726 - services - INFO - Attempting manual download of hl7.fhir.r4.core#4.0.1
2025-08-01 00:48:12,727 - services - INFO - Manual package hl7.fhir.r4.core#4.0.1 already exists at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz
2025-08-01 00:48:12,728 - services - DEBUG - Parsed '/app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.tgz' -> name='/app/instance/fhir.packages/hl7.fhir.r4.core', version='4.0.1'
2025-08-01 00:48:12,728 - services - INFO - Manually processing package: hl7.fhir.r4.core-4.0.1.tgz (/app/instance/fhir.packages/hl7.fhir.r4.core#4.0.1)
2025-08-01 00:48:12,994 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:48:12,997 - services - INFO - Saved metadata for hl7.fhir.r4.core#4.0.1 at /app/instance/fhir_packages/hl7.fhir.r4.core-4.0.1.metadata.json
2025-08-01 00:48:12,998 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:48:13,003 - services - INFO - Saved metadata for example.fhir.ph.core.r4#0.1.0 at /app/instance/fhir_packages/example.fhir.ph.core.r4-0.1.0.metadata.json
2025-08-01 00:48:13,004 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:48:13] "POST /manual-import-ig HTTP/1.1" 302 -
2025-08-01 00:48:13,014 - __main__ - DEBUG - Scanning packages directory: /app/instance/fhir_packages
2025-08-01 00:48:13,016 - services - DEBUG - Parsed 'example.fhir.ph.core.r4-0.1.0.tgz' -> name='example.fhir.ph.core.r4', version='0.1.0'
2025-08-01 00:48:13,023 - services - DEBUG - Parsed 'hl7.fhir.au.base-5.1.0-preview.tgz' -> name='hl7.fhir.au.base', version='5.1.0-preview'
2025-08-01 00:48:13,047 - services - DEBUG - Parsed 'hl7.fhir.au.core-1.1.0-preview.tgz' -> name='hl7.fhir.au.core', version='1.1.0-preview'
2025-08-01 00:48:13,064 - services - DEBUG - Parsed 'hl7.fhir.r4.core-4.0.1.tgz' -> name='hl7.fhir.r4.core', version='4.0.1'
2025-08-01 00:48:13,486 - services - DEBUG - Parsed 'hl7.fhir.uv.extensions.r4-5.2.0.tgz' -> name='hl7.fhir.uv.extensions.r4', version='5.2.0'
2025-08-01 00:48:13,543 - services - DEBUG - Parsed 'hl7.fhir.uv.ipa-1.0.0.tgz' -> name='hl7.fhir.uv.ipa', version='1.0.0'
2025-08-01 00:48:13,551 - services - DEBUG - Parsed 'hl7.fhir.uv.smart-app-launch-2.0.0.tgz' -> name='hl7.fhir.uv.smart-app-launch', version='2.0.0'
2025-08-01 00:48:13,554 - services - DEBUG - Parsed 'hl7.fhir.uv.smart-app-launch-2.1.0.tgz' -> name='hl7.fhir.uv.smart-app-launch', version='2.1.0'
2025-08-01 00:48:13,558 - services - DEBUG - Parsed 'hl7.terminology.r4-5.0.0.tgz' -> name='hl7.terminology.r4', version='5.0.0'
2025-08-01 00:48:13,743 - services - DEBUG - Parsed 'hl7.terminology.r4-6.2.0.tgz' -> name='hl7.terminology.r4', version='6.2.0'
2025-08-01 00:48:13,974 - services - DEBUG - Parsed 'hl7.terminology.r4-6.4.0.tgz' -> name='hl7.terminology.r4', version='6.4.0'
2025-08-01 00:48:14,164 - __main__ - DEBUG - Found packages: 11
2025-08-01 00:48:14,165 - __main__ - DEBUG - Errors during package listing: []
2025-08-01 00:48:14,166 - __main__ - DEBUG - Duplicate groups: {'hl7.fhir.uv.smart-app-launch': ['2.0.0', '2.1.0'], 'hl7.terminology.r4': ['5.0.0', '6.2.0', '6.4.0']}
2025-08-01 00:48:14,169 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:48:14] "GET /view-igs HTTP/1.1" 200 -
2025-08-01 00:48:14,185 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:48:14] "GET /static/css/fire-animation.css HTTP/1.1" 304 -
2025-08-01 00:48:14,187 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:48:14] "GET /static/js/htmx.min.js HTTP/1.1" 304 -
2025-08-01 00:48:14,188 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:48:14] "GET /static/css/animation.css HTTP/1.1" 304 -
2025-08-01 00:48:14,189 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:48:14] "GET /static/FHIRFLARE.png HTTP/1.1" 304 -
2025-08-01 00:48:19,052 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:48:19] "GET /validate-sample HTTP/1.1" 200 -
2025-08-01 00:48:19,067 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:48:19] "GET /static/css/animation.css HTTP/1.1" 304 -
2025-08-01 00:48:19,068 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:48:19] "GET /static/css/fire-animation.css HTTP/1.1" 304 -
2025-08-01 00:48:19,069 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:48:19] "GET /static/js/htmx.min.js HTTP/1.1" 304 -
2025-08-01 00:48:19,071 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:48:19] "GET /static/FHIRFLARE.png HTTP/1.1" 304 -
2025-08-01 00:50:33,336 - __main__ - DEBUG - Proxy received request for path: '/fhir/metadata', cleaned subpath: 'metadata'
2025-08-01 00:50:33,337 - __main__ - DEBUG - No target header found, proxying to default local HAPI: http://localhost:8080/fhir
2025-08-01 00:50:33,337 - __main__ - INFO - Proxying request: GET http://localhost:8080/fhir/metadata
2025-08-01 00:50:33,339 - urllib3.connectionpool - DEBUG - Starting new HTTP connection (1): localhost:8080
2025-08-01 00:50:34,754 - urllib3.connectionpool - DEBUG - http://localhost:8080 "GET /fhir/metadata HTTP/1.1" 200 None
2025-08-01 00:50:34,995 - __main__ - INFO - Target server 'http://localhost:8080/fhir' responded with status: 200
2025-08-01 00:50:34,997 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:50:34] "GET /fhir/metadata HTTP/1.1" 200 -
2025-08-01 00:50:35,010 - services - DEBUG - Received validate-sample request
2025-08-01 00:50:35,011 - services - DEBUG - Request params: package_name=example.fhir.ph.core.r4, version=0.1.0, sample_data_length=13970
2025-08-01 00:50:35,012 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:50:35,014 - services - DEBUG - Checking package file: /app/instance/fhir_packages/example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:50:35,017 - services - DEBUG - Validating Bundle against example.fhir.ph.core.r4#0.1.0
2025-08-01 00:50:35,017 - services - DEBUG - Validating bundle against example.fhir.ph.core.r4#0.1.0, include_dependencies=True
2025-08-01 00:50:35,018 - services - DEBUG - Found 14 local references in the bundle.
2025-08-01 00:50:35,019 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:50:35,021 - services - DEBUG - Using provided profile: urn://example.com/ph-core/fhir/StructureDefinition/ph-core-patient. Cleaned to: urn://example.com/ph-core/fhir/StructureDefinition/ph-core-patient
2025-08-01 00:50:35,022 - services - DEBUG - Checking for package file: /app/instance/fhir_packages/example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:50:35,027 - services - DEBUG - Searching for SD matching 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-patient' with profile 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-patient' in example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:50:35,028 - services - DEBUG - Cleaned profile URL for search: 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-patient'
2025-08-01 00:50:35,039 - services - DEBUG - Removing narrative text from resource: StructureDefinition
2025-08-01 00:50:35,040 - services - INFO - Found definitive SD matching profile 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-patient' at path: package/StructureDefinition-ph-core-patient.json
2025-08-01 00:50:35,042 - services - DEBUG - Found SD at package/StructureDefinition-ph-core-patient.json
2025-08-01 00:50:35,043 - services - DEBUG - Navigating FHIR path: Patient, extension_url=None
2025-08-01 00:50:35,046 - services - DEBUG - Navigating FHIR path: Patient, extension_url=None
2025-08-01 00:50:35,048 - services - DEBUG - Navigating FHIR path: Patient.extension, extension_url=None
2025-08-01 00:50:35,050 - services - DEBUG - Navigating FHIR path: Patient.extension, extension_url=None
2025-08-01 00:50:35,052 - services - DEBUG - Navigating FHIR path: Patient.identifier, extension_url=None
2025-08-01 00:50:35,054 - services - DEBUG - Navigating FHIR path: Patient.identifier, extension_url=None
2025-08-01 00:50:35,055 - services - DEBUG - Validation result: valid=True, errors=0, warnings=0
2025-08-01 00:50:35,057 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:50:35,059 - services - DEBUG - Using provided profile: urn://example.com/ph-core/fhir/StructureDefinition/ph-core-encounter. Cleaned to: urn://example.com/ph-core/fhir/StructureDefinition/ph-core-encounter
2025-08-01 00:50:35,060 - services - DEBUG - Checking for package file: /app/instance/fhir_packages/example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:50:35,065 - services - DEBUG - Searching for SD matching 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-encounter' with profile 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-encounter' in example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:50:35,066 - services - DEBUG - Cleaned profile URL for search: 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-encounter'
2025-08-01 00:50:35,071 - services - DEBUG - Removing narrative text from resource: StructureDefinition
2025-08-01 00:50:35,073 - services - INFO - Found definitive SD matching profile 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-encounter' at path: package/StructureDefinition-ph-core-encounter.json
2025-08-01 00:50:35,074 - services - DEBUG - Found SD at package/StructureDefinition-ph-core-encounter.json
2025-08-01 00:50:35,075 - services - DEBUG - Navigating FHIR path: Encounter, extension_url=None
2025-08-01 00:50:35,077 - services - DEBUG - Navigating FHIR path: Encounter, extension_url=None
2025-08-01 00:50:35,078 - services - DEBUG - Navigating FHIR path: Encounter.status, extension_url=None
2025-08-01 00:50:35,079 - services - DEBUG - Navigating FHIR path: Encounter.class, extension_url=None
2025-08-01 00:50:35,081 - services - DEBUG - Validation result: valid=True, errors=0, warnings=0
2025-08-01 00:50:35,082 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:50:35,083 - services - DEBUG - No profile in resource, using base type as identifier: Condition
2025-08-01 00:50:35,084 - services - DEBUG - Checking for package file: /app/instance/fhir_packages/example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:50:35,088 - services - DEBUG - Searching for SD matching 'Condition' with profile 'None' in example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:50:35,090 - services - DEBUG - Cleaned profile URL for search: 'None'
2025-08-01 00:50:35,106 - services - INFO - SD matching identifier 'Condition' or profile 'None' not found within archive example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:50:35,108 - services - DEBUG - SD not found in example.fhir.ph.core.r4#0.1.0. Checking dependencies.
2025-08-01 00:50:35,112 - services - DEBUG - Found dependencies: {'hl7.fhir.r4.core': '4.0.1', 'hl7.terminology.r4': '6.4.0', 'hl7.fhir.uv.extensions.r4': '5.2.0'}
2025-08-01 00:50:35,113 - services - DEBUG - Searching SD in dependency hl7.fhir.r4.core#4.0.1
2025-08-01 00:50:35,116 - services - DEBUG - Searching for SD matching 'Condition' with profile 'None' in hl7.fhir.r4.core-4.0.1.tgz
2025-08-01 00:50:35,117 - services - DEBUG - Cleaned profile URL for search: 'None'
2025-08-01 00:50:35,182 - services - INFO - Found SD in dependency hl7.fhir.r4.core#4.0.1 at package/StructureDefinition-Condition.json
2025-08-01 00:50:35,184 - services - DEBUG - Found SD at package/StructureDefinition-Condition.json
2025-08-01 00:50:35,185 - services - DEBUG - Navigating FHIR path: Condition, extension_url=None
2025-08-01 00:50:35,186 - services - DEBUG - Navigating FHIR path: Condition, extension_url=None
2025-08-01 00:50:35,187 - services - DEBUG - Navigating FHIR path: Condition.subject, extension_url=None
2025-08-01 00:50:35,188 - services - DEBUG - Validation result: valid=True, errors=0, warnings=0
2025-08-01 00:50:35,190 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:50:35,192 - services - DEBUG - Using provided profile: urn://example.com/ph-core/fhir/StructureDefinition/ph-core-medication. Cleaned to: urn://example.com/ph-core/fhir/StructureDefinition/ph-core-medication
2025-08-01 00:50:35,192 - services - DEBUG - Checking for package file: /app/instance/fhir_packages/example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:50:35,196 - services - DEBUG - Searching for SD matching 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-medication' with profile 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-medication' in example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:50:35,197 - services - DEBUG - Cleaned profile URL for search: 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-medication'
2025-08-01 00:50:35,204 - services - DEBUG - Removing narrative text from resource: StructureDefinition
2025-08-01 00:50:35,206 - services - INFO - Found definitive SD matching profile 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-medication' at path: package/StructureDefinition-ph-core-medication.json
2025-08-01 00:50:35,207 - services - DEBUG - Found SD at package/StructureDefinition-ph-core-medication.json
2025-08-01 00:50:35,207 - services - DEBUG - Navigating FHIR path: Medication, extension_url=None
2025-08-01 00:50:35,209 - services - DEBUG - Navigating FHIR path: Medication, extension_url=None
2025-08-01 00:50:35,210 - services - DEBUG - Validation result: valid=True, errors=0, warnings=0
2025-08-01 00:50:35,211 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:50:35,213 - services - DEBUG - Using provided profile: urn://example.com/ph-core/fhir/StructureDefinition/ph-core-observation. Cleaned to: urn://example.com/ph-core/fhir/StructureDefinition/ph-core-observation
2025-08-01 00:50:35,214 - services - DEBUG - Checking for package file: /app/instance/fhir_packages/example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:50:35,217 - services - DEBUG - Searching for SD matching 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-observation' with profile 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-observation' in example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:50:35,218 - services - DEBUG - Cleaned profile URL for search: 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-observation'
2025-08-01 00:50:35,224 - services - DEBUG - Removing narrative text from resource: StructureDefinition
2025-08-01 00:50:35,225 - services - INFO - Found definitive SD matching profile 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-observation' at path: package/StructureDefinition-ph-core-observation.json
2025-08-01 00:50:35,226 - services - DEBUG - Found SD at package/StructureDefinition-ph-core-observation.json
2025-08-01 00:50:35,227 - services - DEBUG - Navigating FHIR path: Observation, extension_url=None
2025-08-01 00:50:35,228 - services - DEBUG - Navigating FHIR path: Observation, extension_url=None
2025-08-01 00:50:35,229 - services - DEBUG - Navigating FHIR path: Observation.status, extension_url=None
2025-08-01 00:50:35,230 - services - DEBUG - Navigating FHIR path: Observation.code, extension_url=None
2025-08-01 00:50:35,231 - services - DEBUG - Validation result: valid=True, errors=0, warnings=0
2025-08-01 00:50:35,232 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:50:35,233 - services - DEBUG - No profile in resource, using base type as identifier: AllergyIntolerance
2025-08-01 00:50:35,234 - services - DEBUG - Checking for package file: /app/instance/fhir_packages/example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:50:35,237 - services - DEBUG - Searching for SD matching 'AllergyIntolerance' with profile 'None' in example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:50:35,238 - services - DEBUG - Cleaned profile URL for search: 'None'
2025-08-01 00:50:35,250 - services - INFO - SD matching identifier 'AllergyIntolerance' or profile 'None' not found within archive example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:50:35,252 - services - DEBUG - SD not found in example.fhir.ph.core.r4#0.1.0. Checking dependencies.
2025-08-01 00:50:35,255 - services - DEBUG - Found dependencies: {'hl7.fhir.r4.core': '4.0.1', 'hl7.terminology.r4': '6.4.0', 'hl7.fhir.uv.extensions.r4': '5.2.0'}
2025-08-01 00:50:35,256 - services - DEBUG - Searching SD in dependency hl7.fhir.r4.core#4.0.1
2025-08-01 00:50:35,259 - services - DEBUG - Searching for SD matching 'AllergyIntolerance' with profile 'None' in hl7.fhir.r4.core-4.0.1.tgz
2025-08-01 00:50:35,259 - services - DEBUG - Cleaned profile URL for search: 'None'
2025-08-01 00:50:35,710 - services - INFO - Found SD in dependency hl7.fhir.r4.core#4.0.1 at package/StructureDefinition-AllergyIntolerance.json
2025-08-01 00:50:35,711 - services - DEBUG - Found SD at package/StructureDefinition-AllergyIntolerance.json
2025-08-01 00:50:35,712 - services - DEBUG - Navigating FHIR path: AllergyIntolerance, extension_url=None
2025-08-01 00:50:35,713 - services - DEBUG - Navigating FHIR path: AllergyIntolerance, extension_url=None
2025-08-01 00:50:35,713 - services - DEBUG - Navigating FHIR path: AllergyIntolerance.patient, extension_url=None
2025-08-01 00:50:35,714 - services - DEBUG - Validation result: valid=True, errors=0, warnings=0
2025-08-01 00:50:35,715 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:50:35,716 - services - DEBUG - Using provided profile: urn://example.com/ph-core/fhir/StructureDefinition/ph-core-practitioner. Cleaned to: urn://example.com/ph-core/fhir/StructureDefinition/ph-core-practitioner
2025-08-01 00:50:35,716 - services - DEBUG - Checking for package file: /app/instance/fhir_packages/example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:50:35,719 - services - DEBUG - Searching for SD matching 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-practitioner' with profile 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-practitioner' in example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:50:35,719 - services - DEBUG - Cleaned profile URL for search: 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-practitioner'
2025-08-01 00:50:35,727 - services - DEBUG - Removing narrative text from resource: StructureDefinition
2025-08-01 00:50:35,728 - services - INFO - Found definitive SD matching profile 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-practitioner' at path: package/StructureDefinition-ph-core-practitioner.json
2025-08-01 00:50:35,729 - services - DEBUG - Found SD at package/StructureDefinition-ph-core-practitioner.json
2025-08-01 00:50:35,729 - services - DEBUG - Navigating FHIR path: Practitioner, extension_url=None
2025-08-01 00:50:35,730 - services - DEBUG - Navigating FHIR path: Practitioner, extension_url=None
2025-08-01 00:50:35,731 - services - DEBUG - Validation result: valid=True, errors=0, warnings=0
2025-08-01 00:50:35,731 - services - DEBUG - Bundle validation result: valid=True, errors=0, warnings=0, resources=7
2025-08-01 00:50:35,732 - services - INFO - Validation result for Bundle against example.fhir.ph.core.r4#0.1.0: valid=True, errors=0, warnings=0
2025-08-01 00:50:35,732 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:50:35] "POST /api/validate-sample HTTP/1.1" 200 -
2025-08-01 00:51:28,784 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:51:28] "GET /manual-import-ig HTTP/1.1" 200 -
2025-08-01 00:51:28,825 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:51:28] "GET /static/css/animation.css HTTP/1.1" 304 -
2025-08-01 00:51:28,828 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:51:28] "GET /static/css/fire-animation.css HTTP/1.1" 304 -
2025-08-01 00:51:28,839 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:51:28] "GET /static/js/htmx.min.js HTTP/1.1" 304 -
2025-08-01 00:51:39,832 - __main__ - DEBUG - Scanning packages directory: /app/instance/fhir_packages
2025-08-01 00:51:39,838 - services - DEBUG - Parsed 'example.fhir.ph.core.r4-0.1.0.tgz' -> name='example.fhir.ph.core.r4', version='0.1.0'
2025-08-01 00:51:39,851 - services - DEBUG - Parsed 'hl7.fhir.au.base-5.1.0-preview.tgz' -> name='hl7.fhir.au.base', version='5.1.0-preview'
2025-08-01 00:51:39,892 - services - DEBUG - Parsed 'hl7.fhir.au.core-1.1.0-preview.tgz' -> name='hl7.fhir.au.core', version='1.1.0-preview'
2025-08-01 00:51:39,918 - services - DEBUG - Parsed 'hl7.fhir.r4.core-4.0.1.tgz' -> name='hl7.fhir.r4.core', version='4.0.1'
2025-08-01 00:51:40,732 - services - DEBUG - Parsed 'hl7.fhir.uv.extensions.r4-5.2.0.tgz' -> name='hl7.fhir.uv.extensions.r4', version='5.2.0'
2025-08-01 00:51:40,837 - services - DEBUG - Parsed 'hl7.fhir.uv.ipa-1.0.0.tgz' -> name='hl7.fhir.uv.ipa', version='1.0.0'
2025-08-01 00:51:40,855 - services - DEBUG - Parsed 'hl7.fhir.uv.smart-app-launch-2.0.0.tgz' -> name='hl7.fhir.uv.smart-app-launch', version='2.0.0'
2025-08-01 00:51:40,860 - services - DEBUG - Parsed 'hl7.fhir.uv.smart-app-launch-2.1.0.tgz' -> name='hl7.fhir.uv.smart-app-launch', version='2.1.0'
2025-08-01 00:51:40,868 - services - DEBUG - Parsed 'hl7.terminology.r4-5.0.0.tgz' -> name='hl7.terminology.r4', version='5.0.0'
2025-08-01 00:51:41,223 - services - DEBUG - Parsed 'hl7.terminology.r4-6.2.0.tgz' -> name='hl7.terminology.r4', version='6.2.0'
2025-08-01 00:51:41,669 - services - DEBUG - Parsed 'hl7.terminology.r4-6.4.0.tgz' -> name='hl7.terminology.r4', version='6.4.0'
2025-08-01 00:51:42,069 - __main__ - DEBUG - Found packages: 11
2025-08-01 00:51:42,070 - __main__ - DEBUG - Errors during package listing: []
2025-08-01 00:51:42,071 - __main__ - DEBUG - Duplicate groups: {'hl7.fhir.uv.smart-app-launch': ['2.0.0', '2.1.0'], 'hl7.terminology.r4': ['5.0.0', '6.2.0', '6.4.0']}
2025-08-01 00:51:42,075 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:51:42] "GET /view-igs HTTP/1.1" 200 -
2025-08-01 00:51:42,100 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:51:42] "GET /static/css/fire-animation.css HTTP/1.1" 304 -
2025-08-01 00:51:42,103 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:51:42] "GET /static/css/animation.css HTTP/1.1" 304 -
2025-08-01 00:51:42,108 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:51:42] "GET /static/FHIRFLARE.png HTTP/1.1" 304 -
2025-08-01 00:51:42,108 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:51:42] "GET /static/js/htmx.min.js HTTP/1.1" 304 -
2025-08-01 00:54:20,189 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:54:20] "GET /validate-sample HTTP/1.1" 200 -
2025-08-01 00:54:20,209 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:54:20] "GET /static/css/fire-animation.css HTTP/1.1" 304 -
2025-08-01 00:54:20,212 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:54:20] "GET /static/css/animation.css HTTP/1.1" 304 -
2025-08-01 00:54:20,216 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:54:20] "GET /static/js/htmx.min.js HTTP/1.1" 304 -
2025-08-01 00:54:20,218 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:54:20] "GET /static/FHIRFLARE.png HTTP/1.1" 304 -
2025-08-01 00:54:39,350 - __main__ - DEBUG - Proxy received request for path: '/fhir/metadata', cleaned subpath: 'metadata'
2025-08-01 00:54:39,351 - __main__ - DEBUG - No target header found, proxying to default local HAPI: http://localhost:8080/fhir
2025-08-01 00:54:39,353 - __main__ - INFO - Proxying request: GET http://localhost:8080/fhir/metadata
2025-08-01 00:54:39,356 - urllib3.connectionpool - DEBUG - Starting new HTTP connection (1): localhost:8080
2025-08-01 00:54:39,413 - urllib3.connectionpool - DEBUG - http://localhost:8080 "GET /fhir/metadata HTTP/1.1" 200 None
2025-08-01 00:54:39,611 - __main__ - INFO - Target server 'http://localhost:8080/fhir' responded with status: 200
2025-08-01 00:54:39,613 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:54:39] "GET /fhir/metadata HTTP/1.1" 200 -
2025-08-01 00:54:39,639 - services - DEBUG - Received validate-sample request
2025-08-01 00:54:39,641 - services - DEBUG - Request params: package_name=example.fhir.ph.core.r4, version=0.1.0, sample_data_length=13970
2025-08-01 00:54:39,643 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:54:39,647 - services - DEBUG - Checking package file: /app/instance/fhir_packages/example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:54:39,650 - services - DEBUG - Validating Bundle against example.fhir.ph.core.r4#0.1.0
2025-08-01 00:54:39,652 - services - DEBUG - Validating bundle against example.fhir.ph.core.r4#0.1.0, include_dependencies=True
2025-08-01 00:54:39,653 - services - DEBUG - Found 14 local references in the bundle.
2025-08-01 00:54:39,655 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:54:39,659 - services - DEBUG - Using provided profile: urn://example.com/ph-core/fhir/StructureDefinition/ph-core-patient. Cleaned to: urn://example.com/ph-core/fhir/StructureDefinition/ph-core-patient
2025-08-01 00:54:39,660 - services - DEBUG - Checking for package file: /app/instance/fhir_packages/example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:54:39,668 - services - DEBUG - Searching for SD matching 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-patient' with profile 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-patient' in example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:54:39,670 - services - DEBUG - Cleaned profile URL for search: 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-patient'
2025-08-01 00:54:39,688 - services - DEBUG - Removing narrative text from resource: StructureDefinition
2025-08-01 00:54:39,689 - services - INFO - Found definitive SD matching profile 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-patient' at path: package/StructureDefinition-ph-core-patient.json
2025-08-01 00:54:39,692 - services - DEBUG - Found SD at package/StructureDefinition-ph-core-patient.json
2025-08-01 00:54:39,693 - services - DEBUG - Navigating FHIR path: Patient, extension_url=None
2025-08-01 00:54:39,695 - services - DEBUG - Navigating FHIR path: Patient, extension_url=None
2025-08-01 00:54:39,697 - services - DEBUG - Navigating FHIR path: Patient.extension, extension_url=None
2025-08-01 00:54:39,700 - services - DEBUG - Navigating FHIR path: Patient.extension, extension_url=None
2025-08-01 00:54:39,702 - services - DEBUG - Navigating FHIR path: Patient.identifier, extension_url=None
2025-08-01 00:54:39,705 - services - DEBUG - Navigating FHIR path: Patient.identifier, extension_url=None
2025-08-01 00:54:39,708 - services - DEBUG - Validation result: valid=True, errors=0, warnings=0
2025-08-01 00:54:39,710 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:54:39,714 - services - DEBUG - Using provided profile: urn://example.com/ph-core/fhir/StructureDefinition/ph-core-encounter. Cleaned to: urn://example.com/ph-core/fhir/StructureDefinition/ph-core-encounter
2025-08-01 00:54:39,715 - services - DEBUG - Checking for package file: /app/instance/fhir_packages/example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:54:39,724 - services - DEBUG - Searching for SD matching 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-encounter' with profile 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-encounter' in example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:54:39,725 - services - DEBUG - Cleaned profile URL for search: 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-encounter'
2025-08-01 00:54:39,734 - services - DEBUG - Removing narrative text from resource: StructureDefinition
2025-08-01 00:54:39,736 - services - INFO - Found definitive SD matching profile 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-encounter' at path: package/StructureDefinition-ph-core-encounter.json
2025-08-01 00:54:39,739 - services - DEBUG - Found SD at package/StructureDefinition-ph-core-encounter.json
2025-08-01 00:54:39,740 - services - DEBUG - Navigating FHIR path: Encounter, extension_url=None
2025-08-01 00:54:39,742 - services - DEBUG - Navigating FHIR path: Encounter, extension_url=None
2025-08-01 00:54:39,744 - services - DEBUG - Navigating FHIR path: Encounter.status, extension_url=None
2025-08-01 00:54:39,746 - services - DEBUG - Navigating FHIR path: Encounter.class, extension_url=None
2025-08-01 00:54:39,747 - services - DEBUG - Validation result: valid=True, errors=0, warnings=0
2025-08-01 00:54:39,749 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:54:39,753 - services - DEBUG - No profile in resource, using base type as identifier: Condition
2025-08-01 00:54:39,755 - services - DEBUG - Checking for package file: /app/instance/fhir_packages/example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:54:39,764 - services - DEBUG - Searching for SD matching 'Condition' with profile 'None' in example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:54:39,766 - services - DEBUG - Cleaned profile URL for search: 'None'
2025-08-01 00:54:39,791 - services - INFO - SD matching identifier 'Condition' or profile 'None' not found within archive example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:54:39,794 - services - DEBUG - SD not found in example.fhir.ph.core.r4#0.1.0. Checking dependencies.
2025-08-01 00:54:39,799 - services - DEBUG - Found dependencies: {'hl7.fhir.r4.core': '4.0.1', 'hl7.terminology.r4': '6.4.0', 'hl7.fhir.uv.extensions.r4': '5.2.0'}
2025-08-01 00:54:39,803 - services - DEBUG - Searching SD in dependency hl7.fhir.r4.core#4.0.1
2025-08-01 00:54:39,808 - services - DEBUG - Searching for SD matching 'Condition' with profile 'None' in hl7.fhir.r4.core-4.0.1.tgz
2025-08-01 00:54:39,810 - services - DEBUG - Cleaned profile URL for search: 'None'
2025-08-01 00:54:39,902 - services - INFO - Found SD in dependency hl7.fhir.r4.core#4.0.1 at package/StructureDefinition-Condition.json
2025-08-01 00:54:39,904 - services - DEBUG - Found SD at package/StructureDefinition-Condition.json
2025-08-01 00:54:39,905 - services - DEBUG - Navigating FHIR path: Condition, extension_url=None
2025-08-01 00:54:39,907 - services - DEBUG - Navigating FHIR path: Condition, extension_url=None
2025-08-01 00:54:39,908 - services - DEBUG - Navigating FHIR path: Condition.subject, extension_url=None
2025-08-01 00:54:39,910 - services - DEBUG - Validation result: valid=True, errors=0, warnings=0
2025-08-01 00:54:39,911 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:54:39,914 - services - DEBUG - Using provided profile: urn://example.com/ph-core/fhir/StructureDefinition/ph-core-medication. Cleaned to: urn://example.com/ph-core/fhir/StructureDefinition/ph-core-medication
2025-08-01 00:54:39,915 - services - DEBUG - Checking for package file: /app/instance/fhir_packages/example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:54:39,923 - services - DEBUG - Searching for SD matching 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-medication' with profile 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-medication' in example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:54:39,924 - services - DEBUG - Cleaned profile URL for search: 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-medication'
2025-08-01 00:54:39,934 - services - DEBUG - Removing narrative text from resource: StructureDefinition
2025-08-01 00:54:39,936 - services - INFO - Found definitive SD matching profile 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-medication' at path: package/StructureDefinition-ph-core-medication.json
2025-08-01 00:54:39,938 - services - DEBUG - Found SD at package/StructureDefinition-ph-core-medication.json
2025-08-01 00:54:39,939 - services - DEBUG - Navigating FHIR path: Medication, extension_url=None
2025-08-01 00:54:39,940 - services - DEBUG - Navigating FHIR path: Medication, extension_url=None
2025-08-01 00:54:39,942 - services - DEBUG - Validation result: valid=True, errors=0, warnings=0
2025-08-01 00:54:39,943 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:54:39,945 - services - DEBUG - Using provided profile: urn://example.com/ph-core/fhir/StructureDefinition/ph-core-observation. Cleaned to: urn://example.com/ph-core/fhir/StructureDefinition/ph-core-observation
2025-08-01 00:54:39,946 - services - DEBUG - Checking for package file: /app/instance/fhir_packages/example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:54:39,952 - services - DEBUG - Searching for SD matching 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-observation' with profile 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-observation' in example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:54:39,953 - services - DEBUG - Cleaned profile URL for search: 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-observation'
2025-08-01 00:54:39,964 - services - DEBUG - Removing narrative text from resource: StructureDefinition
2025-08-01 00:54:39,966 - services - INFO - Found definitive SD matching profile 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-observation' at path: package/StructureDefinition-ph-core-observation.json
2025-08-01 00:54:39,968 - services - DEBUG - Found SD at package/StructureDefinition-ph-core-observation.json
2025-08-01 00:54:39,969 - services - DEBUG - Navigating FHIR path: Observation, extension_url=None
2025-08-01 00:54:39,970 - services - DEBUG - Navigating FHIR path: Observation, extension_url=None
2025-08-01 00:54:39,971 - services - DEBUG - Navigating FHIR path: Observation.status, extension_url=None
2025-08-01 00:54:39,973 - services - DEBUG - Navigating FHIR path: Observation.code, extension_url=None
2025-08-01 00:54:39,975 - services - DEBUG - Validation result: valid=True, errors=0, warnings=0
2025-08-01 00:54:39,976 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:54:39,980 - services - DEBUG - No profile in resource, using base type as identifier: AllergyIntolerance
2025-08-01 00:54:39,981 - services - DEBUG - Checking for package file: /app/instance/fhir_packages/example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:54:39,988 - services - DEBUG - Searching for SD matching 'AllergyIntolerance' with profile 'None' in example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:54:39,990 - services - DEBUG - Cleaned profile URL for search: 'None'
2025-08-01 00:54:40,015 - services - INFO - SD matching identifier 'AllergyIntolerance' or profile 'None' not found within archive example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:54:40,017 - services - DEBUG - SD not found in example.fhir.ph.core.r4#0.1.0. Checking dependencies.
2025-08-01 00:54:40,022 - services - DEBUG - Found dependencies: {'hl7.fhir.r4.core': '4.0.1', 'hl7.terminology.r4': '6.4.0', 'hl7.fhir.uv.extensions.r4': '5.2.0'}
2025-08-01 00:54:40,024 - services - DEBUG - Searching SD in dependency hl7.fhir.r4.core#4.0.1
2025-08-01 00:54:40,028 - services - DEBUG - Searching for SD matching 'AllergyIntolerance' with profile 'None' in hl7.fhir.r4.core-4.0.1.tgz
2025-08-01 00:54:40,029 - services - DEBUG - Cleaned profile URL for search: 'None'
2025-08-01 00:54:41,057 - services - INFO - Found SD in dependency hl7.fhir.r4.core#4.0.1 at package/StructureDefinition-AllergyIntolerance.json
2025-08-01 00:54:41,059 - services - DEBUG - Found SD at package/StructureDefinition-AllergyIntolerance.json
2025-08-01 00:54:41,060 - services - DEBUG - Navigating FHIR path: AllergyIntolerance, extension_url=None
2025-08-01 00:54:41,061 - services - DEBUG - Navigating FHIR path: AllergyIntolerance, extension_url=None
2025-08-01 00:54:41,063 - services - DEBUG - Navigating FHIR path: AllergyIntolerance.patient, extension_url=None
2025-08-01 00:54:41,065 - services - DEBUG - Validation result: valid=True, errors=0, warnings=0
2025-08-01 00:54:41,066 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:54:41,070 - services - DEBUG - Using provided profile: urn://example.com/ph-core/fhir/StructureDefinition/ph-core-practitioner. Cleaned to: urn://example.com/ph-core/fhir/StructureDefinition/ph-core-practitioner
2025-08-01 00:54:41,071 - services - DEBUG - Checking for package file: /app/instance/fhir_packages/example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:54:41,077 - services - DEBUG - Searching for SD matching 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-practitioner' with profile 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-practitioner' in example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:54:41,078 - services - DEBUG - Cleaned profile URL for search: 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-practitioner'
2025-08-01 00:54:41,096 - services - DEBUG - Removing narrative text from resource: StructureDefinition
2025-08-01 00:54:41,098 - services - INFO - Found definitive SD matching profile 'urn://example.com/ph-core/fhir/StructureDefinition/ph-core-practitioner' at path: package/StructureDefinition-ph-core-practitioner.json
2025-08-01 00:54:41,100 - services - DEBUG - Found SD at package/StructureDefinition-ph-core-practitioner.json
2025-08-01 00:54:41,101 - services - DEBUG - Navigating FHIR path: Practitioner, extension_url=None
2025-08-01 00:54:41,103 - services - DEBUG - Navigating FHIR path: Practitioner, extension_url=None
2025-08-01 00:54:41,105 - services - DEBUG - Validation result: valid=True, errors=0, warnings=0
2025-08-01 00:54:41,106 - services - DEBUG - Bundle validation result: valid=True, errors=0, warnings=0, resources=7
2025-08-01 00:54:41,107 - services - INFO - Validation result for Bundle against example.fhir.ph.core.r4#0.1.0: valid=True, errors=0, warnings=0
2025-08-01 00:54:41,109 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:54:41] "POST /api/validate-sample HTTP/1.1" 200 -
2025-08-01 00:55:46,274 - __main__ - DEBUG - Proxy received request for path: '/fhir/metadata', cleaned subpath: 'metadata'
2025-08-01 00:55:46,275 - __main__ - DEBUG - No target header found, proxying to default local HAPI: http://localhost:8080/fhir
2025-08-01 00:55:46,276 - __main__ - INFO - Proxying request: GET http://localhost:8080/fhir/metadata
2025-08-01 00:55:46,278 - urllib3.connectionpool - DEBUG - Starting new HTTP connection (1): localhost:8080
2025-08-01 00:55:46,303 - urllib3.connectionpool - DEBUG - http://localhost:8080 "GET /fhir/metadata HTTP/1.1" 200 None
2025-08-01 00:55:46,409 - __main__ - INFO - Target server 'http://localhost:8080/fhir' responded with status: 200
2025-08-01 00:55:46,411 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:55:46] "GET /fhir/metadata HTTP/1.1" 200 -
2025-08-01 00:55:46,425 - services - DEBUG - Received validate-sample request
2025-08-01 00:55:46,427 - services - DEBUG - Request params: package_name=example.fhir.ph.core.r4, version=0.1.0, sample_data_length=713
2025-08-01 00:55:46,428 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:55:46,431 - services - DEBUG - Checking package file: /app/instance/fhir_packages/example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:55:46,434 - services - DEBUG - Validating AllergyIntolerance against example.fhir.ph.core.r4#0.1.0
2025-08-01 00:55:46,435 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:55:46,438 - services - DEBUG - No profile in resource, using base type as identifier: AllergyIntolerance
2025-08-01 00:55:46,439 - services - DEBUG - Checking for package file: /app/instance/fhir_packages/example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:55:46,445 - services - DEBUG - Searching for SD matching 'AllergyIntolerance' with profile 'None' in example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:55:46,446 - services - DEBUG - Cleaned profile URL for search: 'None'
2025-08-01 00:55:46,468 - services - INFO - SD matching identifier 'AllergyIntolerance' or profile 'None' not found within archive example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:55:46,470 - services - DEBUG - SD not found in example.fhir.ph.core.r4#0.1.0. Checking dependencies.
2025-08-01 00:55:46,475 - services - DEBUG - Found dependencies: {'hl7.fhir.r4.core': '4.0.1', 'hl7.terminology.r4': '6.4.0', 'hl7.fhir.uv.extensions.r4': '5.2.0'}
2025-08-01 00:55:46,477 - services - DEBUG - Searching SD in dependency hl7.fhir.r4.core#4.0.1
2025-08-01 00:55:46,481 - services - DEBUG - Searching for SD matching 'AllergyIntolerance' with profile 'None' in hl7.fhir.r4.core-4.0.1.tgz
2025-08-01 00:55:46,482 - services - DEBUG - Cleaned profile URL for search: 'None'
2025-08-01 00:55:47,472 - services - INFO - Found SD in dependency hl7.fhir.r4.core#4.0.1 at package/StructureDefinition-AllergyIntolerance.json
2025-08-01 00:55:47,474 - services - DEBUG - Found SD at package/StructureDefinition-AllergyIntolerance.json
2025-08-01 00:55:47,474 - services - DEBUG - Navigating FHIR path: AllergyIntolerance, extension_url=None
2025-08-01 00:55:47,476 - services - DEBUG - Navigating FHIR path: AllergyIntolerance, extension_url=None
2025-08-01 00:55:47,477 - services - DEBUG - Navigating FHIR path: AllergyIntolerance.patient, extension_url=None
2025-08-01 00:55:47,479 - services - DEBUG - Validation result: valid=True, errors=0, warnings=0
2025-08-01 00:55:47,480 - services - INFO - Validation result for AllergyIntolerance against example.fhir.ph.core.r4#0.1.0: valid=True, errors=0, warnings=0
2025-08-01 00:55:47,481 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:55:47] "POST /api/validate-sample HTTP/1.1" 200 -
2025-08-01 00:56:01,610 - __main__ - DEBUG - Proxy received request for path: '/fhir/metadata', cleaned subpath: 'metadata'
2025-08-01 00:56:01,611 - __main__ - DEBUG - No target header found, proxying to default local HAPI: http://localhost:8080/fhir
2025-08-01 00:56:01,612 - __main__ - INFO - Proxying request: GET http://localhost:8080/fhir/metadata
2025-08-01 00:56:01,614 - urllib3.connectionpool - DEBUG - Starting new HTTP connection (1): localhost:8080
2025-08-01 00:56:01,634 - urllib3.connectionpool - DEBUG - http://localhost:8080 "GET /fhir/metadata HTTP/1.1" 200 None
2025-08-01 00:56:01,712 - __main__ - INFO - Target server 'http://localhost:8080/fhir' responded with status: 200
2025-08-01 00:56:01,714 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:56:01] "GET /fhir/metadata HTTP/1.1" 200 -
2025-08-01 00:56:01,726 - services - DEBUG - Received validate-sample request
2025-08-01 00:56:01,727 - services - DEBUG - Request params: package_name=example.fhir.ph.core.r4, version=0.1.0, sample_data_length=713
2025-08-01 00:56:01,728 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:56:01,731 - services - DEBUG - Checking package file: /app/instance/fhir_packages/example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:56:01,733 - services - DEBUG - Validating AllergyIntolerance against example.fhir.ph.core.r4#0.1.0
2025-08-01 00:56:01,735 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:56:01,738 - services - DEBUG - No profile in resource, using base type as identifier: AllergyIntolerance
2025-08-01 00:56:01,739 - services - DEBUG - Checking for package file: /app/instance/fhir_packages/example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:56:01,743 - services - DEBUG - Searching for SD matching 'AllergyIntolerance' with profile 'None' in example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:56:01,744 - services - DEBUG - Cleaned profile URL for search: 'None'
2025-08-01 00:56:01,759 - services - INFO - SD matching identifier 'AllergyIntolerance' or profile 'None' not found within archive example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:56:01,761 - services - DEBUG - SD not found in example.fhir.ph.core.r4#0.1.0. Checking dependencies.
2025-08-01 00:56:01,764 - services - DEBUG - Found dependencies: {'hl7.fhir.r4.core': '4.0.1', 'hl7.terminology.r4': '6.4.0', 'hl7.fhir.uv.extensions.r4': '5.2.0'}
2025-08-01 00:56:01,766 - services - DEBUG - Searching SD in dependency hl7.fhir.r4.core#4.0.1
2025-08-01 00:56:01,770 - services - DEBUG - Searching for SD matching 'AllergyIntolerance' with profile 'None' in hl7.fhir.r4.core-4.0.1.tgz
2025-08-01 00:56:01,771 - services - DEBUG - Cleaned profile URL for search: 'None'
2025-08-01 00:56:02,425 - services - INFO - Found SD in dependency hl7.fhir.r4.core#4.0.1 at package/StructureDefinition-AllergyIntolerance.json
2025-08-01 00:56:02,427 - services - DEBUG - Found SD at package/StructureDefinition-AllergyIntolerance.json
2025-08-01 00:56:02,428 - services - DEBUG - Navigating FHIR path: AllergyIntolerance, extension_url=None
2025-08-01 00:56:02,430 - services - DEBUG - Navigating FHIR path: AllergyIntolerance, extension_url=None
2025-08-01 00:56:02,431 - services - DEBUG - Navigating FHIR path: AllergyIntolerance.patient, extension_url=None
2025-08-01 00:56:02,433 - services - DEBUG - Validation result: valid=True, errors=0, warnings=0
2025-08-01 00:56:02,434 - services - INFO - Validation result for AllergyIntolerance against example.fhir.ph.core.r4#0.1.0: valid=True, errors=0, warnings=0
2025-08-01 00:56:02,435 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:56:02] "POST /api/validate-sample HTTP/1.1" 200 -
2025-08-01 00:56:14,054 - __main__ - DEBUG - Proxy received request for path: '/fhir/metadata', cleaned subpath: 'metadata'
2025-08-01 00:56:14,055 - __main__ - DEBUG - No target header found, proxying to default local HAPI: http://localhost:8080/fhir
2025-08-01 00:56:14,056 - __main__ - INFO - Proxying request: GET http://localhost:8080/fhir/metadata
2025-08-01 00:56:14,057 - urllib3.connectionpool - DEBUG - Starting new HTTP connection (1): localhost:8080
2025-08-01 00:56:14,070 - urllib3.connectionpool - DEBUG - http://localhost:8080 "GET /fhir/metadata HTTP/1.1" 200 None
2025-08-01 00:56:14,147 - __main__ - INFO - Target server 'http://localhost:8080/fhir' responded with status: 200
2025-08-01 00:56:14,149 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:56:14] "GET /fhir/metadata HTTP/1.1" 200 -
2025-08-01 00:56:14,157 - services - DEBUG - Received validate-sample request
2025-08-01 00:56:14,158 - services - DEBUG - Request params: package_name=example.fhir.ph.core.r4, version=0.1.0, sample_data_length=713
2025-08-01 00:56:14,160 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:56:14,163 - services - DEBUG - Checking package file: /app/instance/fhir_packages/example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:56:14,166 - services - DEBUG - Validating AllergyIntolerance against example.fhir.ph.core.r4#0.1.0
2025-08-01 00:56:14,166 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:56:14,170 - services - DEBUG - No profile in resource, using base type as identifier: AllergyIntolerance
2025-08-01 00:56:14,172 - services - DEBUG - Checking for package file: /app/instance/fhir_packages/example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:56:14,177 - services - DEBUG - Searching for SD matching 'AllergyIntolerance' with profile 'None' in example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:56:14,178 - services - DEBUG - Cleaned profile URL for search: 'None'
2025-08-01 00:56:14,193 - services - INFO - SD matching identifier 'AllergyIntolerance' or profile 'None' not found within archive example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:56:14,195 - services - DEBUG - SD not found in example.fhir.ph.core.r4#0.1.0. Checking dependencies.
2025-08-01 00:56:14,197 - services - DEBUG - Found dependencies: {'hl7.fhir.r4.core': '4.0.1', 'hl7.terminology.r4': '6.4.0', 'hl7.fhir.uv.extensions.r4': '5.2.0'}
2025-08-01 00:56:14,198 - services - DEBUG - Searching SD in dependency hl7.fhir.r4.core#4.0.1
2025-08-01 00:56:14,201 - services - DEBUG - Searching for SD matching 'AllergyIntolerance' with profile 'None' in hl7.fhir.r4.core-4.0.1.tgz
2025-08-01 00:56:14,202 - services - DEBUG - Cleaned profile URL for search: 'None'
2025-08-01 00:56:14,875 - services - INFO - Found SD in dependency hl7.fhir.r4.core#4.0.1 at package/StructureDefinition-AllergyIntolerance.json
2025-08-01 00:56:14,877 - services - DEBUG - Found SD at package/StructureDefinition-AllergyIntolerance.json
2025-08-01 00:56:14,878 - services - DEBUG - Navigating FHIR path: AllergyIntolerance, extension_url=None
2025-08-01 00:56:14,879 - services - DEBUG - Navigating FHIR path: AllergyIntolerance, extension_url=None
2025-08-01 00:56:14,881 - services - DEBUG - Navigating FHIR path: AllergyIntolerance.patient, extension_url=None
2025-08-01 00:56:14,883 - services - DEBUG - Validation result: valid=True, errors=0, warnings=0
2025-08-01 00:56:14,884 - services - INFO - Validation result for AllergyIntolerance against example.fhir.ph.core.r4#0.1.0: valid=True, errors=0, warnings=0
2025-08-01 00:56:14,885 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:56:14] "POST /api/validate-sample HTTP/1.1" 200 -
2025-08-01 00:56:18,236 - __main__ - DEBUG - Proxy received request for path: '/fhir/metadata', cleaned subpath: 'metadata'
2025-08-01 00:56:18,237 - __main__ - DEBUG - No target header found, proxying to default local HAPI: http://localhost:8080/fhir
2025-08-01 00:56:18,238 - __main__ - INFO - Proxying request: GET http://localhost:8080/fhir/metadata
2025-08-01 00:56:18,239 - urllib3.connectionpool - DEBUG - Starting new HTTP connection (1): localhost:8080
2025-08-01 00:56:18,257 - urllib3.connectionpool - DEBUG - http://localhost:8080 "GET /fhir/metadata HTTP/1.1" 200 None
2025-08-01 00:56:18,348 - __main__ - INFO - Target server 'http://localhost:8080/fhir' responded with status: 200
2025-08-01 00:56:18,350 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:56:18] "GET /fhir/metadata HTTP/1.1" 200 -
2025-08-01 00:56:18,364 - services - DEBUG - Received validate-sample request
2025-08-01 00:56:18,366 - services - DEBUG - Request params: package_name=example.fhir.ph.core.r4, version=0.1.0, sample_data_length=713
2025-08-01 00:56:18,366 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:56:18,369 - services - DEBUG - Checking package file: /app/instance/fhir_packages/example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:56:18,373 - services - DEBUG - Validating AllergyIntolerance against example.fhir.ph.core.r4#0.1.0
2025-08-01 00:56:18,374 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-08-01 00:56:18,377 - services - DEBUG - No profile in resource, using base type as identifier: AllergyIntolerance
2025-08-01 00:56:18,378 - services - DEBUG - Checking for package file: /app/instance/fhir_packages/example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:56:18,383 - services - DEBUG - Searching for SD matching 'AllergyIntolerance' with profile 'None' in example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:56:18,384 - services - DEBUG - Cleaned profile URL for search: 'None'
2025-08-01 00:56:18,401 - services - INFO - SD matching identifier 'AllergyIntolerance' or profile 'None' not found within archive example.fhir.ph.core.r4-0.1.0.tgz
2025-08-01 00:56:18,403 - services - DEBUG - SD not found in example.fhir.ph.core.r4#0.1.0. Checking dependencies.
2025-08-01 00:56:18,406 - services - DEBUG - Found dependencies: {'hl7.fhir.r4.core': '4.0.1', 'hl7.terminology.r4': '6.4.0', 'hl7.fhir.uv.extensions.r4': '5.2.0'}
2025-08-01 00:56:18,408 - services - DEBUG - Searching SD in dependency hl7.fhir.r4.core#4.0.1
2025-08-01 00:56:18,412 - services - DEBUG - Searching for SD matching 'AllergyIntolerance' with profile 'None' in hl7.fhir.r4.core-4.0.1.tgz
2025-08-01 00:56:18,413 - services - DEBUG - Cleaned profile URL for search: 'None'
2025-08-01 00:56:19,183 - services - INFO - Found SD in dependency hl7.fhir.r4.core#4.0.1 at package/StructureDefinition-AllergyIntolerance.json
2025-08-01 00:56:19,184 - services - DEBUG - Found SD at package/StructureDefinition-AllergyIntolerance.json
2025-08-01 00:56:19,185 - services - DEBUG - Navigating FHIR path: AllergyIntolerance, extension_url=None
2025-08-01 00:56:19,186 - services - DEBUG - Navigating FHIR path: AllergyIntolerance, extension_url=None
2025-08-01 00:56:19,187 - services - DEBUG - Navigating FHIR path: AllergyIntolerance.patient, extension_url=None
2025-08-01 00:56:19,189 - services - DEBUG - Validation result: valid=True, errors=0, warnings=0
2025-08-01 00:56:19,190 - services - INFO - Validation result for AllergyIntolerance against example.fhir.ph.core.r4#0.1.0: valid=True, errors=0, warnings=0
2025-08-01 00:56:19,191 - werkzeug - INFO - 172.18.0.1 - - [01/Aug/2025 00:56:19] "POST /api/validate-sample HTTP/1.1" 200 -

View File

@ -1,6 +0,0 @@
2025-08-01 00:44:37,751 CRIT Supervisor is running as root. Privileges were not dropped because no user is specified in the config file. If you intend to run as root, you can set user=root in the config file to avoid this message.
2025-08-01 00:44:37,755 INFO supervisord started with pid 1
2025-08-01 00:44:38,762 INFO spawned: 'flask' with pid 8
2025-08-01 00:44:38,767 INFO spawned: 'tomcat' with pid 9
2025-08-01 00:44:49,750 INFO success: flask entered RUNNING state, process has stayed up for > than 10 seconds (startsecs)
2025-08-01 00:45:08,829 INFO success: tomcat entered RUNNING state, process has stayed up for > than 30 seconds (startsecs)

View File

@ -1 +0,0 @@
1

View File

@ -1,184 +0,0 @@
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v3.3.11)
2025-08-01T00:44:50.113Z INFO 9 --- [ main] ca.uhn.fhir.jpa.starter.Application : Starting Application using Java 17.0.15 with PID 9 (/usr/local/tomcat/webapps/ROOT/WEB-INF/classes started by root in /usr/local/tomcat)
2025-08-01T00:44:50.117Z INFO 9 --- [ main] ca.uhn.fhir.jpa.starter.Application : No active profile set, falling back to 1 default profile: "default"
2025-08-01T00:44:51.730Z INFO 9 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
2025-08-01T00:44:52.051Z INFO 9 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 305 ms. Found 53 JPA repository interfaces.
2025-08-01T00:44:54.276Z WARN 9 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'ca.uhn.fhir.jpa.config.BeanPostProcessorConfig' of type [ca.uhn.fhir.jpa.config.BeanPostProcessorConfig$$SpringCGLIB$$0] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). The currently created BeanPostProcessor [persistenceExceptionTranslationPostProcessor] is declared through a non-static factory method on that class; consider declaring it as static instead.
2025-08-01T00:44:54.434Z INFO 9 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 4281 ms
2025-08-01T00:44:54.614Z INFO 9 --- [ main] ca.uhn.fhir.util.VersionUtil : HAPI FHIR version 8.2.0 - Rev 8a44e1058f
2025-08-01T00:44:54.623Z INFO 9 --- [ main] ca.uhn.fhir.context.FhirContext : Creating new FHIR context for FHIR version [R4]
2025-08-01T00:44:54.682Z INFO 9 --- [ main] c.u.f.j.s.common.FhirServerConfigCommon : Server configured to allow contains searches
2025-08-01T00:44:54.682Z INFO 9 --- [ main] c.u.f.j.s.common.FhirServerConfigCommon : Server configured to deny multiple deletes
2025-08-01T00:44:54.682Z INFO 9 --- [ main] c.u.f.j.s.common.FhirServerConfigCommon : Server configured to deny external references
2025-08-01T00:44:54.683Z INFO 9 --- [ main] c.u.f.j.s.common.FhirServerConfigCommon : Server configured to enable DAO scheduling
2025-08-01T00:44:54.683Z INFO 9 --- [ main] c.u.f.j.s.common.FhirServerConfigCommon : Server configured to disable delete expunges
2025-08-01T00:44:54.683Z INFO 9 --- [ main] c.u.f.j.s.common.FhirServerConfigCommon : Server configured to enable expunges
2025-08-01T00:44:54.683Z INFO 9 --- [ main] c.u.f.j.s.common.FhirServerConfigCommon : Server configured to allow overriding default search params
2025-08-01T00:44:54.684Z INFO 9 --- [ main] c.u.f.j.s.common.FhirServerConfigCommon : Server configured to disable auto-creating placeholder references
2025-08-01T00:44:54.684Z INFO 9 --- [ main] c.u.f.j.s.common.FhirServerConfigCommon : Server configured to auto-version references at paths []
2025-08-01T00:44:54.684Z INFO 9 --- [ main] c.u.f.j.s.common.FhirServerConfigCommon : Server configured to enable value set pre-expansion
2025-08-01T00:44:54.685Z INFO 9 --- [ main] c.u.f.j.s.common.FhirServerConfigCommon : Server configured to enable value set pre-expansion task
2025-08-01T00:44:54.685Z INFO 9 --- [ main] c.u.f.j.s.common.FhirServerConfigCommon : Server configured for pre-expand value set default count of 1000
2025-08-01T00:44:54.685Z INFO 9 --- [ main] c.u.f.j.s.common.FhirServerConfigCommon : Server configured for pre-expand value set max count of 1000
2025-08-01T00:44:54.685Z INFO 9 --- [ main] c.u.f.j.s.common.FhirServerConfigCommon : Server configured for maximum expansion size of 1000
2025-08-01T00:44:54.717Z INFO 9 --- [ main] c.u.f.j.s.common.FhirServerConfigCommon : Server configured to have a maximum fetch size of 'unlimited'
2025-08-01T00:44:54.717Z INFO 9 --- [ main] c.u.f.j.s.common.FhirServerConfigCommon : Server configured to cache search results for 60000 milliseconds
2025-08-01T00:44:54.717Z INFO 9 --- [ main] c.u.f.j.s.common.FhirServerConfigCommon : Server configured to use 'ALPHANUMERIC' Client ID Strategy
2025-08-01T00:44:54.758Z INFO 9 --- [ main] c.u.f.j.s.common.FhirServerConfigCommon : Partitioning is not enabled on this server
2025-08-01T00:44:54.852Z INFO 9 --- [ main] o.hibernate.jpa.internal.util.LogHelper : HHH000204: Processing PersistenceUnitInfo [name: HAPI_PU]
2025-08-01T00:44:54.986Z INFO 9 --- [ main] org.hibernate.Version : HHH000412: Hibernate ORM core version 6.6.4.Final
2025-08-01T00:44:55.001Z INFO 9 --- [ main] .f.j.l.FilteringSqlLoggerImplContributor : Adding service: SqlStatementFilteringLogger
2025-08-01T00:44:55.060Z INFO 9 --- [ main] o.h.c.internal.RegionFactoryInitiator : HHH000026: Second-level cache disabled
2025-08-01T00:44:55.247Z INFO 9 --- [ main] o.h.e.boot.internal.EnversServiceImpl : Envers integration enabled? : true
2025-08-01T00:44:55.586Z INFO 9 --- [ main] o.s.o.j.p.SpringPersistenceUnitInfo : No LoadTimeWeaver setup: ignoring JPA class transformer
2025-08-01T00:44:55.628Z INFO 9 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2025-08-01T00:44:59.687Z INFO 9 --- [ main] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Added connection conn1: url=jdbc:h2:file:/app/h2-data/fhir user=SA
2025-08-01T00:44:59.691Z INFO 9 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2025-08-01T00:44:59.756Z INFO 9 --- [ main] org.hibernate.orm.connections.pooling : HHH10001005: Database info:
Database JDBC URL [Connecting through datasource 'HikariDataSource (HikariPool-1)']
Database driver: undefined/unknown
Database version: 2.3.232
Autocommit mode: undefined/unknown
Isolation level: undefined/unknown
Minimum pool size: undefined/unknown
Maximum pool size: undefined/unknown
2025-08-01T00:45:01.900Z INFO 9 --- [ main] b.i.HibernateSearchPreIntegrationService : HSEARCH000034: Hibernate Search version 7.2.1.Final
2025-08-01T00:45:02.062Z INFO 9 --- [ main] o.h.e.c.i.m.AuditMetadataGenerator : Adding properties for entity: ca.uhn.fhir.jpa.entity.MdmLink
2025-08-01T00:45:04.735Z INFO 9 --- [ main] o.h.e.t.j.p.i.JtaPlatformInitiator : HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration)
2025-08-01T00:45:06.640Z INFO 9 --- [ main] irLocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'HAPI_PU'
2025-08-01T00:45:07.684Z INFO 9 --- [ main] .u.f.c.s.DefaultProfileValidationSupport : Loading structure definitions from classpath: /org/hl7/fhir/r4/model/profile/profiles-resources.xml
2025-08-01T00:45:07.705Z INFO 9 --- [ main] ca.uhn.fhir.util.XmlUtil : FHIR XML procesing will use StAX implementation 'Woodstox' version '6.4.0'
2025-08-01T00:45:08.736Z INFO 9 --- [ main] .u.f.c.s.DefaultProfileValidationSupport : Loading structure definitions from classpath: /org/hl7/fhir/r4/model/profile/profiles-types.xml
2025-08-01T00:45:08.828Z INFO 9 --- [ main] .u.f.c.s.DefaultProfileValidationSupport : Loading structure definitions from classpath: /org/hl7/fhir/r4/model/profile/profiles-others.xml
2025-08-01T00:45:09.260Z INFO 9 --- [ main] .u.f.c.s.DefaultProfileValidationSupport : Loading structure definitions from classpath: /org/hl7/fhir/r4/model/extension/extension-definitions.xml
2025-08-01T00:45:10.281Z INFO 9 --- [ main] o.s.d.j.r.query.QueryEnhancerFactory : Hibernate is in classpath; If applicable, HQL parser will be used.
2025-08-01T00:45:14.625Z INFO 9 --- [ main] c.u.f.j.sched.BaseSchedulerServiceImpl : Creating Local Scheduler
2025-08-01T00:45:14.674Z INFO 9 --- [ main] org.quartz.impl.StdSchedulerFactory : Using default implementation for ThreadExecutor
2025-08-01T00:45:14.693Z INFO 9 --- [ main] org.quartz.core.SchedulerSignalerImpl : Initialized Scheduler Signaller of type: class org.quartz.core.SchedulerSignalerImpl
2025-08-01T00:45:14.693Z INFO 9 --- [ main] org.quartz.core.QuartzScheduler : Quartz Scheduler v.2.3.2 created.
2025-08-01T00:45:14.694Z INFO 9 --- [ main] org.quartz.simpl.RAMJobStore : RAMJobStore initialized.
2025-08-01T00:45:14.695Z INFO 9 --- [ main] org.quartz.core.QuartzScheduler : Scheduler meta-data: Quartz Scheduler (v2.3.2) 'hapi-fhir-jpa-scheduler' with instanceId 'NON_CLUSTERED'
Scheduler class: 'org.quartz.core.QuartzScheduler' - running locally.
NOT STARTED.
Currently in standby mode.
Number of jobs executed: 0
Using thread pool 'org.quartz.simpl.SimpleThreadPool' - with 4 threads.
Using job-store 'org.quartz.simpl.RAMJobStore' - which does not support persistence. and is not clustered.
2025-08-01T00:45:14.695Z INFO 9 --- [ main] org.quartz.impl.StdSchedulerFactory : Quartz scheduler 'hapi-fhir-jpa-scheduler' initialized from an externally provided properties instance.
2025-08-01T00:45:14.695Z INFO 9 --- [ main] org.quartz.impl.StdSchedulerFactory : Quartz scheduler version: 2.3.2
2025-08-01T00:45:14.696Z INFO 9 --- [ main] org.quartz.core.QuartzScheduler : JobFactory set to: ca.uhn.fhir.jpa.sched.AutowiringSpringBeanJobFactory@f7fb17c
2025-08-01T00:45:14.696Z INFO 9 --- [ main] org.quartz.core.QuartzScheduler : Scheduler hapi-fhir-jpa-scheduler_$_NON_CLUSTERED paused.
2025-08-01T00:45:14.696Z INFO 9 --- [ main] c.u.f.j.sched.BaseSchedulerServiceImpl : Creating Clustered Scheduler
2025-08-01T00:45:14.697Z INFO 9 --- [ main] org.quartz.impl.StdSchedulerFactory : Using default implementation for ThreadExecutor
2025-08-01T00:45:14.699Z INFO 9 --- [ main] org.quartz.core.SchedulerSignalerImpl : Initialized Scheduler Signaller of type: class org.quartz.core.SchedulerSignalerImpl
2025-08-01T00:45:14.699Z INFO 9 --- [ main] org.quartz.core.QuartzScheduler : Quartz Scheduler v.2.3.2 created.
2025-08-01T00:45:14.699Z INFO 9 --- [ main] org.quartz.simpl.RAMJobStore : RAMJobStore initialized.
2025-08-01T00:45:14.699Z INFO 9 --- [ main] org.quartz.core.QuartzScheduler : Scheduler meta-data: Quartz Scheduler (v2.3.2) 'hapi-fhir-jpa-scheduler' with instanceId 'NON_CLUSTERED'
Scheduler class: 'org.quartz.core.QuartzScheduler' - running locally.
NOT STARTED.
Currently in standby mode.
Number of jobs executed: 0
Using thread pool 'org.quartz.simpl.SimpleThreadPool' - with 4 threads.
Using job-store 'org.quartz.simpl.RAMJobStore' - which does not support persistence. and is not clustered.
2025-08-01T00:45:14.699Z INFO 9 --- [ main] org.quartz.impl.StdSchedulerFactory : Quartz scheduler 'hapi-fhir-jpa-scheduler' initialized from an externally provided properties instance.
2025-08-01T00:45:14.699Z INFO 9 --- [ main] org.quartz.impl.StdSchedulerFactory : Quartz scheduler version: 2.3.2
2025-08-01T00:45:14.699Z INFO 9 --- [ main] org.quartz.core.QuartzScheduler : JobFactory set to: ca.uhn.fhir.jpa.sched.AutowiringSpringBeanJobFactory@f7fb17c
2025-08-01T00:45:14.699Z INFO 9 --- [ main] org.quartz.core.QuartzScheduler : Scheduler hapi-fhir-jpa-scheduler_$_NON_CLUSTERED paused.
2025-08-01T00:45:15.168Z INFO 9 --- [ main] c.u.f.log.terminology_troubleshooting : Performing initial retrieval for non-expiring cache: org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain$FetchAllKey@4d737618
2025-08-01T00:45:15.169Z INFO 9 --- [ main] c.u.f.log.terminology_troubleshooting : Initial retrieval for non-expiring cache org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain$FetchAllKey@4d737618 succeeded in 1ms
2025-08-01T00:45:15.177Z INFO 9 --- [ main] c.u.f.log.terminology_troubleshooting : Performing initial retrieval for non-expiring cache: org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain$FetchAllKey@4d737618
2025-08-01T00:45:15.178Z INFO 9 --- [ main] c.u.f.log.terminology_troubleshooting : Initial retrieval for non-expiring cache org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain$FetchAllKey@4d737618 succeeded in 1ms
2025-08-01T00:45:15.179Z INFO 9 --- [ main] c.u.f.log.terminology_troubleshooting : Performing initial retrieval for non-expiring cache: org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain$FetchAllKey@4d737618
2025-08-01T00:45:15.179Z INFO 9 --- [ main] c.u.f.log.terminology_troubleshooting : Initial retrieval for non-expiring cache org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain$FetchAllKey@4d737618 succeeded in 0ms
2025-08-01T00:45:15.180Z INFO 9 --- [ main] c.u.f.log.terminology_troubleshooting : Performing initial retrieval for non-expiring cache: org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain$FetchAllKey@4d737618
2025-08-01T00:45:15.180Z INFO 9 --- [ main] c.u.f.log.terminology_troubleshooting : Initial retrieval for non-expiring cache org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain$FetchAllKey@4d737618 succeeded in 0ms
2025-08-01T00:45:15.181Z INFO 9 --- [ main] c.u.f.log.terminology_troubleshooting : Performing initial retrieval for non-expiring cache: org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain$FetchAllKey@4d737618
2025-08-01T00:45:15.181Z INFO 9 --- [ main] c.u.f.log.terminology_troubleshooting : Initial retrieval for non-expiring cache org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain$FetchAllKey@4d737618 succeeded in 0ms
2025-08-01T00:45:15.182Z INFO 9 --- [ main] c.u.f.log.terminology_troubleshooting : Performing initial retrieval for non-expiring cache: org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain$FetchAllKey@4d737618
2025-08-01T00:45:15.182Z INFO 9 --- [ main] c.u.f.log.terminology_troubleshooting : Initial retrieval for non-expiring cache org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain$FetchAllKey@4d737618 succeeded in 0ms
2025-08-01T00:45:15.255Z INFO 9 --- [ main] ca.uhn.fhir.context.FhirContext : Creating new FHIR context for FHIR version [R4]
2025-08-01T00:45:15.531Z INFO 9 --- [ main] c.u.f.j.starter.common.StarterJpaConfig : CORS is enabled on this server
2025-08-01T00:45:15.533Z INFO 9 --- [ main] c.u.f.j.starter.common.StarterJpaConfig : CORS allows the following origins: *
2025-08-01T00:45:15.562Z INFO 9 --- [ main] ca.uhn.fhir.context.FhirContext : Creating new FHIR context for FHIR version [R5]
2025-08-01T00:45:20.832Z INFO 9 --- [ main] ca.uhn.fhir.context.FhirContext : Creating new FHIR context for FHIR version [R4]
2025-08-01T00:45:21.725Z WARN 9 --- [ main] JpaBaseConfiguration$JpaWebConfiguration : spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
2025-08-01T00:45:22.254Z INFO 9 --- [ main] .s.i.SubscriptionSubmitInterceptorLoader : Subscriptions are disabled on this server. Subscriptions will not be activated and incoming resources will not be matched against subscriptions.
2025-08-01T00:45:22.925Z INFO 9 --- [ main] org.quartz.impl.StdSchedulerFactory : Using default implementation for ThreadExecutor
2025-08-01T00:45:22.930Z INFO 9 --- [ main] org.quartz.core.SchedulerSignalerImpl : Initialized Scheduler Signaller of type: class org.quartz.core.SchedulerSignalerImpl
2025-08-01T00:45:22.931Z INFO 9 --- [ main] org.quartz.core.QuartzScheduler : Quartz Scheduler v.2.3.2 created.
2025-08-01T00:45:22.931Z INFO 9 --- [ main] org.quartz.simpl.RAMJobStore : RAMJobStore initialized.
2025-08-01T00:45:22.931Z INFO 9 --- [ main] org.quartz.core.QuartzScheduler : Scheduler meta-data: Quartz Scheduler (v2.3.2) 'quartzScheduler' with instanceId 'NON_CLUSTERED'
Scheduler class: 'org.quartz.core.QuartzScheduler' - running locally.
NOT STARTED.
Currently in standby mode.
Number of jobs executed: 0
Using thread pool 'org.quartz.simpl.SimpleThreadPool' - with 10 threads.
Using job-store 'org.quartz.simpl.RAMJobStore' - which does not support persistence. and is not clustered.
2025-08-01T00:45:22.931Z INFO 9 --- [ main] org.quartz.impl.StdSchedulerFactory : Quartz scheduler 'quartzScheduler' initialized from an externally provided properties instance.
2025-08-01T00:45:22.931Z INFO 9 --- [ main] org.quartz.impl.StdSchedulerFactory : Quartz scheduler version: 2.3.2
2025-08-01T00:45:22.931Z INFO 9 --- [ main] org.quartz.core.QuartzScheduler : JobFactory set to: org.springframework.scheduling.quartz.SpringBeanJobFactory@48e64e95
2025-08-01T00:45:22.968Z INFO 9 --- [ main] o.s.b.a.e.web.EndpointLinksResolver : Exposing 1 endpoint beneath base path '/actuator'
2025-08-01T00:45:23.183Z INFO 9 --- [ main] o.s.s.quartz.SchedulerFactoryBean : Starting Quartz Scheduler now
2025-08-01T00:45:23.183Z INFO 9 --- [ main] org.quartz.core.QuartzScheduler : Scheduler quartzScheduler_$_NON_CLUSTERED started.
2025-08-01T00:45:23.189Z INFO 9 --- [ main] ca.uhn.fhir.log.batch_troubleshooting : Registering Batch2 Job Definition: BULK_IMPORT_PULL / 1
2025-08-01T00:45:23.191Z INFO 9 --- [ main] ca.uhn.fhir.log.batch_troubleshooting : Registering Batch2 Job Definition: BULK_IMPORT_PULL / 2
2025-08-01T00:45:23.191Z INFO 9 --- [ main] ca.uhn.fhir.log.batch_troubleshooting : Registering Batch2 Job Definition: REINDEX / 1
2025-08-01T00:45:23.191Z INFO 9 --- [ main] ca.uhn.fhir.log.batch_troubleshooting : Registering Batch2 Job Definition: REINDEX / 2
2025-08-01T00:45:23.191Z INFO 9 --- [ main] ca.uhn.fhir.log.batch_troubleshooting : Registering Batch2 Job Definition: DELETE_EXPUNGE / 1
2025-08-01T00:45:23.191Z INFO 9 --- [ main] ca.uhn.fhir.log.batch_troubleshooting : Registering Batch2 Job Definition: BULK_EXPORT / 1
2025-08-01T00:45:23.191Z INFO 9 --- [ main] ca.uhn.fhir.log.batch_troubleshooting : Registering Batch2 Job Definition: BULK_EXPORT / 2
2025-08-01T00:45:23.191Z INFO 9 --- [ main] ca.uhn.fhir.log.batch_troubleshooting : Registering Batch2 Job Definition: termCodeSystemVersionDeleteJob / 1
2025-08-01T00:45:23.192Z INFO 9 --- [ main] ca.uhn.fhir.log.batch_troubleshooting : Registering Batch2 Job Definition: termCodeSystemDeleteJob / 1
2025-08-01T00:45:23.192Z INFO 9 --- [ main] ca.uhn.fhir.log.batch_troubleshooting : Registering Batch2 Job Definition: bulkImportJob / 1
2025-08-01T00:45:23.194Z INFO 9 --- [ main] .j.s.m.m.s.MatchingQueueSubscriberLoader : Subscription Matching Subscriber subscribed to Matching Channel ca.uhn.fhir.jpa.subscription.channel.subscription.BroadcastingSubscribableChannelWrapper with name subscription-matching
2025-08-01T00:45:24.054Z INFO 9 --- [ main] c.u.f.j.s.registry.JpaSearchParamCache : Have 0 unique search params
2025-08-01T00:45:24.148Z INFO 9 --- [ main] c.u.f.j.sched.BaseSchedulerServiceImpl : Scheduling 15 jobs in application
2025-08-01T00:45:24.150Z INFO 9 --- [ main] c.u.f.j.sched.BaseSchedulerServiceImpl : Scheduling local job ca.uhn.fhir.jpa.term.TermDeferredStorageSvcImpl$Job with interval 5000ms
2025-08-01T00:45:24.156Z INFO 9 --- [ main] c.u.f.j.sched.BaseSchedulerServiceImpl : Scheduling local job ca.uhn.fhir.jpa.cache.ResourceChangeListenerCacheRefresherImpl with interval 00:00:10.000
2025-08-01T00:45:24.158Z INFO 9 --- [ main] c.u.f.j.sched.BaseSchedulerServiceImpl : Scheduling clustered job ca.uhn.fhir.jpa.search.reindex.ResourceReindexingSvcImpl with interval 00:00:10.000
2025-08-01T00:45:24.159Z INFO 9 --- [ main] c.u.f.j.sched.BaseSchedulerServiceImpl : Scheduling clustered job ca.uhn.fhir.jpa.bulk.export.svc.BulkDataExportJobSchedulingHelperImpl$PurgeExpiredFilesJob with interval 01:00:00
2025-08-01T00:45:24.161Z INFO 9 --- [ main] c.u.f.j.sched.BaseSchedulerServiceImpl : Scheduling local job ca.uhn.fhir.jpa.bulk.imprt.svc.BulkDataImportSvcImpl$ActivationJob with interval 00:00:10.000
2025-08-01T00:45:24.162Z INFO 9 --- [ main] c.u.f.j.sched.BaseSchedulerServiceImpl : Scheduling clustered job ca.uhn.fhir.jpa.search.warm.CacheWarmingSvcImpl with interval 00:00:10.000
2025-08-01T00:45:24.163Z INFO 9 --- [ main] c.u.f.j.sched.BaseSchedulerServiceImpl : Scheduling clustered job ca.uhn.fhir.jpa.term.TermReadSvcImpl with interval 00:10:00
2025-08-01T00:45:24.165Z INFO 9 --- [ main] c.u.f.j.sched.BaseSchedulerServiceImpl : Scheduling local job ca.uhn.fhir.jpa.term.TermReindexingSvcImpl with interval 00:01:00.000
2025-08-01T00:45:24.166Z INFO 9 --- [ main] c.u.f.j.sched.BaseSchedulerServiceImpl : Scheduling clustered job ca.uhn.fhir.jpa.search.SearchUrlJobMaintenanceSvcImpl$SearchUrlMaintenanceJob with interval 00:10:00
2025-08-01T00:45:24.166Z INFO 9 --- [ main] c.u.f.j.sched.BaseSchedulerServiceImpl : Scheduling clustered job ca.uhn.fhir.jpa.search.StaleSearchDeletingSvcImpl with interval 00:01:00.000
2025-08-01T00:45:24.167Z INFO 9 --- [ main] c.u.f.j.sched.BaseSchedulerServiceImpl : Scheduling local job ca.uhn.fhir.jpa.util.ResourceCountCache with interval 00:10:00
2025-08-01T00:45:24.168Z INFO 9 --- [ main] c.u.f.j.sched.BaseSchedulerServiceImpl : Scheduling local job ca.uhn.fhir.jpa.subscription.triggering.SubscriptionTriggeringSvcImpl with interval 5000ms
2025-08-01T00:45:24.168Z INFO 9 --- [ main] c.u.f.j.sched.BaseSchedulerServiceImpl : Scheduling clustered job ca.uhn.fhir.jpa.subscription.async.AsyncResourceModifiedProcessingSchedulerSvc with interval 5000ms
2025-08-01T00:45:24.168Z INFO 9 --- [ main] c.u.f.j.sched.BaseSchedulerServiceImpl : Scheduling clustered job ca.uhn.fhir.batch2.coordinator.ReductionStepExecutorServiceImpl$ReductionStepExecutorScheduledJob with interval 00:00:10.000
2025-08-01T00:45:24.169Z INFO 9 --- [ main] c.u.f.j.sched.BaseSchedulerServiceImpl : Scheduling clustered job ca.uhn.fhir.batch2.maintenance.JobMaintenanceServiceImpl$JobMaintenanceScheduledJob with interval 00:01:00.000
2025-08-01T00:45:24.169Z INFO 9 --- [ main] c.u.f.j.sched.BaseSchedulerServiceImpl : Starting task schedulers for context application
2025-08-01T00:45:24.169Z INFO 9 --- [ main] c.uhn.fhir.jpa.sched.BaseHapiScheduler : Starting scheduler hapi-fhir-jpa-scheduler-local
2025-08-01T00:45:24.169Z INFO 9 --- [ main] org.quartz.core.QuartzScheduler : Scheduler hapi-fhir-jpa-scheduler_$_NON_CLUSTERED started.
2025-08-01T00:45:24.169Z INFO 9 --- [ main] c.uhn.fhir.jpa.sched.BaseHapiScheduler : Starting scheduler hapi-fhir-jpa-scheduler-clustered
2025-08-01T00:45:24.170Z INFO 9 --- [ main] org.quartz.core.QuartzScheduler : Scheduler hapi-fhir-jpa-scheduler_$_NON_CLUSTERED started.
2025-08-01T00:45:24.194Z INFO 9 --- [ main] ca.uhn.fhir.jpa.starter.Application : Started Application in 34.72 seconds (process running for 45.415)
2025-08-01T00:45:24.208Z INFO 9 --- [ler-clustered-2] .s.BulkDataExportJobSchedulingHelperImpl : Finished bulk export job deletion with nothing to do
2025-08-01T00:45:24.260Z INFO 9 --- [ main] ca.uhn.fhir.rest.server.RestfulServer : Initializing HAPI FHIR restful server running in R4 mode
2025-08-01T00:45:24.261Z INFO 9 --- [ main] ca.uhn.fhir.rest.server.RestfulServer : Added 147 resource provider(s). Total 147
2025-08-01T00:45:24.710Z INFO 9 --- [ main] ca.uhn.fhir.rest.server.RestfulServer : Added 5 plain provider(s). Total 5
2025-08-01T00:45:24.717Z INFO 9 --- [ main] ca.uhn.fhir.rest.server.RestfulServer : Removing RESTful methods for: class ca.uhn.fhir.jpa.provider.JpaCapabilityStatementProvider
2025-08-01T00:45:24.718Z INFO 9 --- [ main] ca.uhn.fhir.rest.server.RestfulServer : OperationDefinition binding of ca.uhn.fhir.rest.server.method.ReadMethodBinding@2091d0e3 was removed
2025-08-01T00:45:24.719Z INFO 9 --- [ main] ca.uhn.fhir.rest.server.RestfulServer : OperationDefinition binding of ca.uhn.fhir.rest.server.method.ReadMethodBinding@5221dfd5 was removed
2025-08-01T00:45:24.762Z INFO 9 --- [ main] ca.uhn.fhir.rest.server.RestfulServer : A FHIR has been lit on this server
2025-08-01T00:50:33.549Z INFO 9 --- [nio-8080-exec-1] c.u.f.log.terminology_troubleshooting : Performing initial retrieval for non-expiring cache: org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain$FetchAllKey@3e87800e
2025-08-01T00:50:33.963Z INFO 9 --- [nio-8080-exec-1] c.u.f.log.terminology_troubleshooting : Initial retrieval for non-expiring cache org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain$FetchAllKey@3e87800e succeeded in 414ms
2025-08-01T00:50:35.020Z INFO 9 --- [nio-8080-exec-1] fhirtest.access : Path[/fhir] Source[] Operation[metadata ] UA[Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36 Edg/138.0.0.0] Params[] ResponseEncoding[] Operation[metadata ] UA[Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36 Edg/138.0.0.0] Params[] ResponseEncoding[]
2025-08-01T00:54:39.611Z INFO 9 --- [nio-8080-exec-2] fhirtest.access : Path[/fhir] Source[] Operation[metadata ] UA[Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36 Edg/138.0.0.0] Params[] ResponseEncoding[] Operation[metadata ] UA[Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36 Edg/138.0.0.0] Params[] ResponseEncoding[]
2025-08-01T00:55:46.409Z INFO 9 --- [nio-8080-exec-4] fhirtest.access : Path[/fhir] Source[] Operation[metadata ] UA[Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36 Edg/138.0.0.0] Params[] ResponseEncoding[] Operation[metadata ] UA[Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36 Edg/138.0.0.0] Params[] ResponseEncoding[]
2025-08-01T00:56:01.712Z INFO 9 --- [nio-8080-exec-6] fhirtest.access : Path[/fhir] Source[] Operation[metadata ] UA[Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36 Edg/138.0.0.0] Params[] ResponseEncoding[] Operation[metadata ] UA[Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36 Edg/138.0.0.0] Params[] ResponseEncoding[]
2025-08-01T00:56:14.147Z INFO 9 --- [nio-8080-exec-8] fhirtest.access : Path[/fhir] Source[] Operation[metadata ] UA[Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36 Edg/138.0.0.0] Params[] ResponseEncoding[] Operation[metadata ] UA[Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36 Edg/138.0.0.0] Params[] ResponseEncoding[]
2025-08-01T00:56:18.348Z INFO 9 --- [io-8080-exec-10] fhirtest.access : Path[/fhir] Source[] Operation[metadata ] UA[Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36 Edg/138.0.0.0] Params[] ResponseEncoding[] Operation[metadata ] UA[Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36 Edg/138.0.0.0] Params[] ResponseEncoding[]

View File

@ -1,41 +0,0 @@
01-Aug-2025 00:44:39.175 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Server version name: Apache Tomcat/10.1.43
01-Aug-2025 00:44:39.178 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Server built: Jul 1 2025 21:30:20 UTC
01-Aug-2025 00:44:39.178 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Server version number: 10.1.43.0
01-Aug-2025 00:44:39.178 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log OS Name: Linux
01-Aug-2025 00:44:39.178 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log OS Version: 5.15.167.4-microsoft-standard-WSL2
01-Aug-2025 00:44:39.178 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Architecture: amd64
01-Aug-2025 00:44:39.178 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Java Home: /opt/java/openjdk
01-Aug-2025 00:44:39.178 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log JVM Version: 17.0.15+6
01-Aug-2025 00:44:39.178 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log JVM Vendor: Eclipse Adoptium
01-Aug-2025 00:44:39.178 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log CATALINA_BASE: /usr/local/tomcat
01-Aug-2025 00:44:39.178 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log CATALINA_HOME: /usr/local/tomcat
01-Aug-2025 00:44:39.188 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Djava.util.logging.config.file=/usr/local/tomcat/conf/logging.properties
01-Aug-2025 00:44:39.188 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager
01-Aug-2025 00:44:39.188 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Djdk.tls.ephemeralDHKeySize=2048
01-Aug-2025 00:44:39.188 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Djava.protocol.handler.pkgs=org.apache.catalina.webresources
01-Aug-2025 00:44:39.188 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Dsun.io.useCanonCaches=false
01-Aug-2025 00:44:39.188 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Dorg.apache.catalina.security.SecurityListener.UMASK=0027
01-Aug-2025 00:44:39.188 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: --add-opens=java.base/java.lang=ALL-UNNAMED
01-Aug-2025 00:44:39.188 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: --add-opens=java.base/java.lang.reflect=ALL-UNNAMED
01-Aug-2025 00:44:39.188 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: --add-opens=java.base/java.io=ALL-UNNAMED
01-Aug-2025 00:44:39.188 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: --add-opens=java.base/java.util=ALL-UNNAMED
01-Aug-2025 00:44:39.189 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: --add-opens=java.base/java.util.concurrent=ALL-UNNAMED
01-Aug-2025 00:44:39.189 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: --add-opens=java.rmi/sun.rmi.transport=ALL-UNNAMED
01-Aug-2025 00:44:39.189 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Dcatalina.base=/usr/local/tomcat
01-Aug-2025 00:44:39.189 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Dcatalina.home=/usr/local/tomcat
01-Aug-2025 00:44:39.189 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Djava.io.tmpdir=/usr/local/tomcat/temp
01-Aug-2025 00:44:39.192 INFO [main] org.apache.catalina.core.AprLifecycleListener.lifecycleEvent Loaded Apache Tomcat Native library [2.0.9] using APR version [1.7.2].
01-Aug-2025 00:44:39.194 INFO [main] org.apache.catalina.core.AprLifecycleListener.initializeSSL OpenSSL successfully initialized [OpenSSL 3.0.13 30 Jan 2024]
01-Aug-2025 00:44:39.445 INFO [main] org.apache.coyote.AbstractProtocol.init Initializing ProtocolHandler ["http-nio-8080"]
01-Aug-2025 00:44:39.465 INFO [main] org.apache.catalina.startup.Catalina.load Server initialization in [475] milliseconds
01-Aug-2025 00:44:39.519 INFO [main] org.apache.catalina.core.StandardService.startInternal Starting service [Catalina]
01-Aug-2025 00:44:39.519 INFO [main] org.apache.catalina.core.StandardEngine.startInternal Starting Servlet engine: [Apache Tomcat/10.1.43]
01-Aug-2025 00:44:39.544 INFO [main] org.apache.catalina.startup.HostConfig.deployWAR Deploying web application archive [/usr/local/tomcat/webapps/ROOT.war]
01-Aug-2025 00:44:48.747 INFO [main] org.apache.jasper.servlet.TldScanner.scanJars At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
01-Aug-2025 00:45:24.787 INFO [main] org.apache.catalina.startup.HostConfig.deployWAR Deployment of web application archive [/usr/local/tomcat/webapps/ROOT.war] has finished in [45,243] ms
01-Aug-2025 00:45:24.788 INFO [main] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory [/usr/local/tomcat/webapps/custom]
01-Aug-2025 00:45:24.812 INFO [main] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory [/usr/local/tomcat/webapps/custom] has finished in [24] ms
01-Aug-2025 00:45:24.813 INFO [main] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory [/usr/local/tomcat/webapps/app]
01-Aug-2025 00:45:24.829 INFO [main] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory [/usr/local/tomcat/webapps/app] has finished in [15] ms
01-Aug-2025 00:45:24.834 INFO [main] org.apache.coyote.AbstractProtocol.start Starting ProtocolHandler ["http-nio-8080"]
01-Aug-2025 00:45:24.865 INFO [main] org.apache.catalina.startup.Catalina.start Server startup in [45402] milliseconds

View File

@ -11,4 +11,4 @@ Flask-Migrate==4.1.0
cachetools cachetools
beautifulsoup4 beautifulsoup4
feedparser==6.0.11 feedparser==6.0.11
flasgger flasgger

File diff suppressed because it is too large Load Diff

View File

@ -1,18 +1,21 @@
#!/bin/bash #!/bin/bash
# --- Configuration --- # --- Configuration ---
REPO_URL="https://github.com/hapifhir/hapi-fhir-jpaserver-starter.git" REPO_URL_HAPI="https://github.com/hapifhir/hapi-fhir-jpaserver-starter.git"
CLONE_DIR="hapi-fhir-jpaserver" REPO_URL_CANDLE="https://github.com/FHIR/fhir-candle.git"
SOURCE_CONFIG_DIR="hapi-fhir-Setup" # Assuming this is relative to the script's parent CLONE_DIR_HAPI="hapi-fhir-jpaserver"
CLONE_DIR_CANDLE="fhir-candle"
SOURCE_CONFIG_DIR="hapi-fhir-Setup"
CONFIG_FILE="application.yaml" CONFIG_FILE="application.yaml"
# --- Define Paths --- # --- Define Paths ---
# Note: Adjust SOURCE_CONFIG_PATH if SOURCE_CONFIG_DIR is not a sibling directory
# This assumes the script is run from a directory, and hapi-fhir-setup is at the same level
SOURCE_CONFIG_PATH="../${SOURCE_CONFIG_DIR}/target/classes/${CONFIG_FILE}" SOURCE_CONFIG_PATH="../${SOURCE_CONFIG_DIR}/target/classes/${CONFIG_FILE}"
DEST_CONFIG_PATH="${CLONE_DIR}/target/classes/${CONFIG_FILE}" DEST_CONFIG_PATH="${CLONE_DIR_HAPI}/target/classes/${CONFIG_FILE}"
APP_MODE="" APP_MODE=""
CUSTOM_FHIR_URL_VAL=""
SERVER_TYPE=""
CANDLE_FHIR_VERSION=""
# --- Error Handling Function --- # --- Error Handling Function ---
handle_error() { handle_error() {
@ -20,25 +23,39 @@ handle_error() {
echo "An error occurred: $1" echo "An error occurred: $1"
echo "Script aborted." echo "Script aborted."
echo "------------------------------------" echo "------------------------------------"
# Removed 'read -p "Press Enter to exit..."' as it's not typical for non-interactive CI/CD
exit 1 exit 1
} }
# === Prompt for Installation Mode === # === MODIFIED: Prompt for Installation Mode ===
get_mode_choice() { get_mode_choice() {
echo ""
echo "Select Installation Mode:" echo "Select Installation Mode:"
echo "1. Standalone (Includes local HAPI FHIR Server - Requires Git & Maven)" echo "1. Lite (Excludes local HAPI FHIR Server - No Git/Maven/Dotnet needed)"
echo "2. Lite (Excludes local HAPI FHIR Server - No Git/Maven needed)" echo "2. Custom URL (Uses a custom FHIR Server - No Git/Maven/Dotnet needed)"
echo "3. Hapi (Includes local HAPI FHIR Server - Requires Git & Maven)"
echo "4. Candle (Includes local FHIR Candle Server - Requires Git & Dotnet)"
while true; do while true; do
read -r -p "Enter your choice (1 or 2): " choice read -r -p "Enter your choice (1, 2, 3, or 4): " choice
case "$choice" in case "$choice" in
1) 1)
APP_MODE="standalone" APP_MODE="lite"
break break
;; ;;
2) 2)
APP_MODE="lite" APP_MODE="standalone"
get_custom_url_prompt
break
;;
3)
APP_MODE="standalone"
SERVER_TYPE="hapi"
break
;;
4)
APP_MODE="standalone"
SERVER_TYPE="candle"
get_candle_fhir_version
break break
;; ;;
*) *)
@ -47,156 +64,301 @@ get_mode_choice() {
esac esac
done done
echo "Selected Mode: $APP_MODE" echo "Selected Mode: $APP_MODE"
echo "Server Type: $SERVER_TYPE"
echo echo
} }
# === NEW: Prompt for Custom URL ===
get_custom_url_prompt() {
local confirmed_url=""
while true; do
echo
read -r -p "Please enter the custom FHIR server URL: " custom_url_input
echo
echo "You entered: $custom_url_input"
read -r -p "Is this URL correct? (Y/N): " confirm_url
if [[ "$confirm_url" =~ ^[Yy]$ ]]; then
confirmed_url="$custom_url_input"
break
else
echo "URL not confirmed. Please re-enter."
fi
done
while true; do
echo
read -r -p "Please re-enter the URL to confirm it is correct: " custom_url_input
if [ "$custom_url_input" = "$confirmed_url" ]; then
CUSTOM_FHIR_URL_VAL="$custom_url_input"
echo
echo "Custom URL confirmed: $CUSTOM_FHIR_URL_VAL"
break
else
echo
echo "URLs do not match. Please try again."
confirmed_url="$custom_url_input"
fi
done
}
# === NEW: Prompt for Candle FHIR version ===
get_candle_fhir_version() {
echo ""
echo "Select the FHIR version for the Candle server:"
echo "1. R4 (4.0)"
echo "2. R4B (4.3)"
echo "3. R5 (5.0)"
while true; do
read -r -p "Enter your choice (1, 2, or 3): " choice
case "$choice" in
1)
CANDLE_FHIR_VERSION=r4
break
;;
2)
CANDLE_FHIR_VERSION=r4b
break
;;
3)
CANDLE_FHIR_VERSION=r5
break
;;
*)
echo "Invalid input. Please try again."
;;
esac
done
}
# Call the function to get mode choice # Call the function to get mode choice
get_mode_choice get_mode_choice
# === Conditionally Execute HAPI Setup === # === Conditionally Execute Server Setup ===
if [ "$APP_MODE" = "standalone" ]; then case "$SERVER_TYPE" in
echo "Running Standalone setup including HAPI FHIR..." "hapi")
echo echo "Running Hapi server setup..."
echo
# --- Step 0: Clean up previous clone (optional) --- # --- Step 0: Clean up previous clone (optional) ---
echo "Checking for existing directory: $CLONE_DIR" echo "Checking for existing directory: $CLONE_DIR_HAPI"
if [ -d "$CLONE_DIR" ]; then if [ -d "$CLONE_DIR_HAPI" ]; then
echo "Found existing directory, removing it..." echo "Found existing directory, removing it..."
rm -rf "$CLONE_DIR" rm -rf "$CLONE_DIR_HAPI"
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
handle_error "Failed to remove existing directory: $CLONE_DIR" handle_error "Failed to remove existing directory: $CLONE_DIR_HAPI"
fi
echo "Existing directory removed."
else
echo "Directory does not exist, proceeding with clone."
fi fi
echo "Existing directory removed." echo
else
echo "Directory does not exist, proceeding with clone."
fi
echo
# --- Step 1: Clone the HAPI FHIR server repository --- # --- Step 1: Clone the HAPI FHIR server repository ---
echo "Cloning repository: $REPO_URL into $CLONE_DIR..." echo "Cloning repository: $REPO_URL_HAPI into $CLONE_DIR_HAPI..."
git clone "$REPO_URL" "$CLONE_DIR" git clone "$REPO_URL_HAPI" "$CLONE_DIR_HAPI"
if [ $? -ne 0 ]; then
handle_error "Failed to clone repository. Check Git installation and network connection."
fi
echo "Repository cloned successfully."
echo
# --- Step 2: Navigate into the cloned directory ---
echo "Changing directory to $CLONE_DIR..."
cd "$CLONE_DIR" || handle_error "Failed to change directory to $CLONE_DIR."
echo "Current directory: $(pwd)"
echo
# --- Step 3: Build the HAPI server using Maven ---
echo "===> Starting Maven build (Step 3)..."
mvn clean package -DskipTests=true -Pboot
if [ $? -ne 0 ]; then
echo "ERROR: Maven build failed."
cd ..
handle_error "Maven build process resulted in an error."
fi
echo "Maven build completed successfully."
echo
# --- Step 4: Copy the configuration file ---
echo "===> Starting file copy (Step 4)..."
echo "Copying configuration file..."
# Corrected SOURCE_CONFIG_PATH to be relative to the new current directory ($CLONE_DIR)
# This assumes the original script's SOURCE_CONFIG_PATH was relative to its execution location
# If SOURCE_CONFIG_DIR is ../hapi-fhir-setup relative to script's original location:
# Then from within CLONE_DIR, it becomes ../../hapi-fhir-setup
# We defined SOURCE_CONFIG_PATH earlier relative to the script start.
# So, when inside CLONE_DIR, the path from original script location should be used.
# The original script had: set SOURCE_CONFIG_PATH=..\%SOURCE_CONFIG_DIR%\target\classes\%CONFIG_FILE%
# And then: xcopy "%SOURCE_CONFIG_PATH%" "target\classes\"
# This implies SOURCE_CONFIG_PATH is relative to the original script's location, not the $CLONE_DIR
# Therefore, we need to construct the correct relative path from *within* $CLONE_DIR back to the source.
# Assuming the script is in dir X, and SOURCE_CONFIG_DIR is ../hapi-fhir-setup from X.
# So, hapi-fhir-setup is a sibling of X's parent.
# If CLONE_DIR is also in X, then from within CLONE_DIR, the path is ../ + original SOURCE_CONFIG_PATH
# For simplicity and robustness, let's use an absolute path or a more clearly defined relative path from the start.
# The original `SOURCE_CONFIG_PATH=..\%SOURCE_CONFIG_DIR%\target\classes\%CONFIG_FILE%` implies
# that `hapi-fhir-setup` is a sibling of the directory where the script *is being run from*.
# Let's assume the script is run from the root of FHIRFLARE-IG-Toolkit.
# And hapi-fhir-setup is also in the root, next to this script.
# Then SOURCE_CONFIG_PATH would be ./hapi-fhir-setup/target/classes/application.yaml
# And from within ./hapi-fhir-jpaserver/, the path would be ../hapi-fhir-setup/target/classes/application.yaml
# The original batch file sets SOURCE_CONFIG_PATH as "..\%SOURCE_CONFIG_DIR%\target\classes\%CONFIG_FILE%"
# And COPIES it to "target\classes\" *while inside CLONE_DIR*.
# This means the source path is relative to where the *cd %CLONE_DIR%* happened from.
# Let's make it relative to the script's initial execution directory.
INITIAL_SCRIPT_DIR=$(pwd)
ABSOLUTE_SOURCE_CONFIG_PATH="${INITIAL_SCRIPT_DIR}/../${SOURCE_CONFIG_DIR}/target/classes/${CONFIG_FILE}" # This matches the ..\ logic
echo "Source: $ABSOLUTE_SOURCE_CONFIG_PATH"
echo "Destination: target/classes/$CONFIG_FILE"
if [ ! -f "$ABSOLUTE_SOURCE_CONFIG_PATH" ]; then
echo "WARNING: Source configuration file not found at $ABSOLUTE_SOURCE_CONFIG_PATH."
echo "The script will continue, but the server might use default configuration."
else
cp "$ABSOLUTE_SOURCE_CONFIG_PATH" "target/classes/"
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
echo "WARNING: Failed to copy configuration file. Check if the source file exists and permissions." handle_error "Failed to clone repository. Check Git installation and network connection."
fi
echo "Repository cloned successfully."
echo
# --- Step 2: Navigate into the cloned directory ---
echo "Changing directory to $CLONE_DIR_HAPI..."
cd "$CLONE_DIR_HAPI" || handle_error "Failed to change directory to $CLONE_DIR_HAPI."
echo "Current directory: $(pwd)"
echo
# --- Step 3: Build the HAPI server using Maven ---
echo "===> Starting Maven build (Step 3)..."
mvn clean package -DskipTests=true -Pboot
if [ $? -ne 0 ]; then
echo "ERROR: Maven build failed."
cd ..
handle_error "Maven build process resulted in an error."
fi
echo "Maven build completed successfully."
echo
# --- Step 4: Copy the configuration file ---
echo "===> Starting file copy (Step 4)..."
echo "Copying configuration file..."
INITIAL_SCRIPT_DIR=$(pwd)
ABSOLUTE_SOURCE_CONFIG_PATH="${INITIAL_SCRIPT_DIR}/../${SOURCE_CONFIG_DIR}/target/classes/${CONFIG_FILE}"
echo "Source: $ABSOLUTE_SOURCE_CONFIG_PATH"
echo "Destination: target/classes/$CONFIG_FILE"
if [ ! -f "$ABSOLUTE_SOURCE_CONFIG_PATH" ]; then
echo "WARNING: Source configuration file not found at $ABSOLUTE_SOURCE_CONFIG_PATH."
echo "The script will continue, but the server might use default configuration." echo "The script will continue, but the server might use default configuration."
else else
echo "Configuration file copied successfully." cp "$ABSOLUTE_SOURCE_CONFIG_PATH" "target/classes/"
if [ $? -ne 0 ]; then
echo "WARNING: Failed to copy configuration file. Check if the source file exists and permissions."
echo "The script will continue, but the server might use default configuration."
else
echo "Configuration file copied successfully."
fi
fi fi
fi echo
echo
# --- Step 5: Navigate back to the parent directory --- # --- Step 5: Navigate back to the parent directory ---
echo "===> Changing directory back (Step 5)..." echo "===> Changing directory back (Step 5)..."
cd .. || handle_error "Failed to change back to the parent directory." cd .. || handle_error "Failed to change back to the parent directory."
echo "Current directory: $(pwd)" echo "Current directory: $(pwd)"
echo echo
;;
else # APP_MODE is "lite" "candle")
echo "Running Lite setup, skipping HAPI FHIR build..." echo "Running FHIR Candle server setup..."
# Ensure the hapi-fhir-jpaserver directory doesn't exist or is empty if Lite mode is chosen echo
if [ -d "$CLONE_DIR" ]; then
echo "Found existing HAPI directory ($CLONE_DIR) in Lite mode. Removing it..." # --- Step 0: Clean up previous clone (optional) ---
rm -rf "$CLONE_DIR" echo "Checking for existing directory: $CLONE_DIR_CANDLE"
fi if [ -d "$CLONE_DIR_CANDLE" ]; then
# Create empty target directories expected by Dockerfile COPY, even if not used echo "Found existing directory, removing it..."
mkdir -p "${CLONE_DIR}/target/classes" rm -rf "$CLONE_DIR_CANDLE"
mkdir -p "${CLONE_DIR}/custom" # This was in the original batch, ensure it's here if [ $? -ne 0 ]; then
# Create a placeholder empty WAR file and application.yaml to satisfy Dockerfile COPY handle_error "Failed to remove existing directory: $CLONE_DIR_CANDLE"
touch "${CLONE_DIR}/target/ROOT.war" fi
touch "${CLONE_DIR}/target/classes/application.yaml" echo "Existing directory removed."
echo "Placeholder files and directories created for Lite mode build in $CLONE_DIR." else
echo echo "Directory does not exist, proceeding with clone."
fi fi
echo
# === Modify docker-compose.yml to set APP_MODE === # --- Step 1: Clone the FHIR Candle server repository ---
echo "Updating docker-compose.yml with APP_MODE=$APP_MODE..." echo "Cloning repository: $REPO_URL_CANDLE into $CLONE_DIR_CANDLE..."
git clone "$REPO_URL_CANDLE" "$CLONE_DIR_CANDLE"
if [ $? -ne 0 ]; then
handle_error "Failed to clone repository. Check Git and Dotnet SDK installation and network connection."
fi
echo "Repository cloned successfully."
echo
# --- Step 2: Navigate into the cloned directory ---
echo "Changing directory to $CLONE_DIR_CANDLE..."
cd "$CLONE_DIR_CANDLE" || handle_error "Failed to change directory to $CLONE_DIR_CANDLE."
echo "Current directory: $(pwd)"
echo
# --- Step 3: Build the FHIR Candle server using Dotnet ---
echo "===> Starting Dotnet build (Step 3)..."
dotnet publish -c Release -f net9.0 -o publish
if [ $? -ne 0 ]; then
handle_error "Dotnet build failed. Check Dotnet SDK installation."
fi
echo "Dotnet build completed successfully."
echo
# --- Step 4: Navigate back to the parent directory ---
echo "===> Changing directory back (Step 4)..."
cd .. || handle_error "Failed to change back to the parent directory."
echo "Current directory: $(pwd)"
echo
;;
*) # APP_MODE is Lite, no SERVER_TYPE
echo "Running Lite setup, skipping server build..."
if [ -d "$CLONE_DIR_HAPI" ]; then
echo "Found existing HAPI directory in Lite mode. Removing it to avoid build issues..."
rm -rf "$CLONE_DIR_HAPI"
fi
if [ -d "$CLONE_DIR_CANDLE" ]; then
echo "Found existing Candle directory in Lite mode. Removing it to avoid build issues..."
rm -rf "$CLONE_DIR_CANDLE"
fi
mkdir -p "${CLONE_DIR_HAPI}/target/classes"
mkdir -p "${CLONE_DIR_HAPI}/custom"
touch "${CLONE_DIR_HAPI}/target/ROOT.war"
touch "${CLONE_DIR_HAPI}/target/classes/application.yaml"
mkdir -p "${CLONE_DIR_CANDLE}/publish"
touch "${CLONE_DIR_CANDLE}/publish/fhir-candle.dll"
echo "Placeholder files and directories created for Lite mode build."
echo
;;
esac
# === MODIFIED: Update docker-compose.yml to set APP_MODE and HAPI_FHIR_URL and DOCKERFILE ===
echo "Updating docker-compose.yml with APP_MODE=$APP_MODE and HAPI_FHIR_URL..."
DOCKER_COMPOSE_TMP="docker-compose.yml.tmp" DOCKER_COMPOSE_TMP="docker-compose.yml.tmp"
DOCKER_COMPOSE_ORIG="docker-compose.yml" DOCKER_COMPOSE_ORIG="docker-compose.yml"
HAPI_URL_TO_USE="https://fhir.hl7.org.au/aucore/fhir/DEFAULT/"
if [ -n "$CUSTOM_FHIR_URL_VAL" ]; then
HAPI_URL_TO_USE="$CUSTOM_FHIR_URL_VAL"
elif [ "$SERVER_TYPE" = "candle" ]; then
HAPI_URL_TO_USE="http://localhost:5826/fhir/${CANDLE_FHIR_VERSION}"
else
HAPI_URL_TO_USE="http://localhost:8080/fhir"
fi
DOCKERFILE_TO_USE="Dockerfile.lite"
if [ "$SERVER_TYPE" = "hapi" ]; then
DOCKERFILE_TO_USE="Dockerfile.hapi"
elif [ "$SERVER_TYPE" = "candle" ]; then
DOCKERFILE_TO_USE="Dockerfile.candle"
fi
cat << EOF > "$DOCKER_COMPOSE_TMP" cat << EOF > "$DOCKER_COMPOSE_TMP"
version: '3.8' version: '3.8'
services: services:
fhirflare: fhirflare:
build: build:
context: . context: .
dockerfile: Dockerfile dockerfile: ${DOCKERFILE_TO_USE}
ports: ports:
EOF
if [ "$SERVER_TYPE" = "candle" ]; then
cat << EOF >> "$DOCKER_COMPOSE_TMP"
- "5000:5000" - "5000:5000"
- "8080:8080" # Keep port exposed, even if Tomcat isn't running useful stuff in Lite - "5001:5826"
EOF
else
cat << EOF >> "$DOCKER_COMPOSE_TMP"
- "5000:5000"
- "8080:8080"
EOF
fi
cat << EOF >> "$DOCKER_COMPOSE_TMP"
volumes: volumes:
- ./instance:/app/instance - ./instance:/app/instance
- ./static/uploads:/app/static/uploads - ./static/uploads:/app/static/uploads
- ./instance/hapi-h2-data/:/app/h2-data # Keep volume mounts consistent
- ./logs:/app/logs - ./logs:/app/logs
EOF
if [ "$SERVER_TYPE" = "hapi" ]; then
cat << EOF >> "$DOCKER_COMPOSE_TMP"
- ./instance/hapi-h2-data/:/app/h2-data # Keep volume mounts consistent
- ./hapi-fhir-jpaserver/target/ROOT.war:/usr/local/tomcat/webapps/ROOT.war
- ./hapi-fhir-jpaserver/target/classes/application.yaml:/usr/local/tomcat/conf/application.yaml
EOF
elif [ "$SERVER_TYPE" = "candle" ]; then
cat << EOF >> "$DOCKER_COMPOSE_TMP"
- ./fhir-candle/publish/:/app/fhir-candle-publish/
EOF
fi
cat << EOF >> "$DOCKER_COMPOSE_TMP"
environment: environment:
- FLASK_APP=app.py - FLASK_APP=app.py
- FLASK_ENV=development - FLASK_ENV=development
- NODE_PATH=/usr/lib/node_modules - NODE_PATH=/usr/lib/node_modules
- APP_MODE=${APP_MODE} - APP_MODE=${APP_MODE}
- APP_BASE_URL=http://localhost:5000 - APP_BASE_URL=http://localhost:5000
- HAPI_FHIR_URL=http://localhost:8080/fhir - HAPI_FHIR_URL=${HAPI_URL_TO_USE}
EOF
if [ "$SERVER_TYPE" = "candle" ]; then
cat << EOF >> "$DOCKER_COMPOSE_TMP"
- ASPNETCORE_URLS=http://0.0.0.0:5826
EOF
fi
cat << EOF >> "$DOCKER_COMPOSE_TMP"
command: supervisord -c /etc/supervisord.conf command: supervisord -c /etc/supervisord.conf
EOF EOF

File diff suppressed because one or more lines are too long

View File

@ -33,4 +33,18 @@ stdout_logfile_maxbytes=10MB
stdout_logfile_backups=5 stdout_logfile_backups=5
stderr_logfile=/app/logs/tomcat_err.log stderr_logfile=/app/logs/tomcat_err.log
stderr_logfile_maxbytes=10MB stderr_logfile_maxbytes=10MB
stderr_logfile_backups=5 stderr_logfile_backups=5
[program:candle]
command=dotnet /app/fhir-candle-publish/fhir-candle.dll
directory=/app/fhir-candle-publish
autostart=true
autorestart=true
startsecs=10
stopwaitsecs=10
stdout_logfile=/app/logs/candle.log
stdout_logfile_maxbytes=10MB
stdout_logfile_backups=5
stderr_logfile=/app/logs/candle_err.log
stderr_logfile_maxbytes=10MB
stderr_logfile_backups=5

View File

@ -19,11 +19,11 @@
<p>Whether you're downloading the latest IG versions, checking compliance, converting resources to FHIR Shorthand (FSH), or pushing guides to a test server, this toolkit aims to be an essential companion.</p> <p>Whether you're downloading the latest IG versions, checking compliance, converting resources to FHIR Shorthand (FSH), or pushing guides to a test server, this toolkit aims to be an essential companion.</p>
{% if app_mode == 'lite' %} {% if app_mode == 'lite' %}
<div class="alert alert-info" role="alert"> <div class="alert alert-info" role="alert">
<i class="bi bi-info-circle-fill me-2"></i>You are currently running the <strong>Lite Version</strong> of the toolkit. This version excludes the built-in HAPI FHIR server and relies on external FHIR servers for functionalities like the API Explorer and Operations UI. Validation uses local StructureDefinition checks. <i class="bi bi-info-circle-fill me-2"></i>You are currently running the <strong>Lite Version</strong> of the toolkit. This version excludes the built-in FHIR server depending and relies on external FHIR servers for functionalities like the API Explorer and Operations UI. Validation uses local StructureDefinition checks.
</div> </div>
{% else %} {% else %}
<div class="alert alert-success" role="alert"> <div class="alert alert-success" role="alert">
<i class="bi bi-check-circle-fill me-2"></i>You are currently running the <strong>Standalone Version</strong> of the toolkit, which includes a built-in HAPI FHIR server (accessible via the `/fhir` proxy) for local validation and exploration. <i class="bi bi-check-circle-fill me-2"></i>You are currently running the <strong>Standalone Version</strong> of the toolkit, which includes a built-in FHIR server based on your build choice (accessible via the `/fhir` proxy) for local validation and exploration, if you configured the Server ENV variable this could also be whatever customer server you have pointed the local ENV variable at allowing you to use any fhir server of your choice.
</div> </div>
{% endif %} {% endif %}
@ -36,7 +36,7 @@
<li><strong>FHIR Server Interaction:</strong> <li><strong>FHIR Server Interaction:</strong>
<ul> <ul>
<li>Push processed IGs (including dependencies) to a target FHIR server with real-time console feedback.</li> <li>Push processed IGs (including dependencies) to a target FHIR server with real-time console feedback.</li>
<li>Explore FHIR server capabilities and interact with resources using the "FHIR API Explorer" (GET/POST/PUT/DELETE) and "FHIR UI Operations" pages, supporting both the local HAPI server (in Standalone mode) and external servers.</li> <li>Explore FHIR server capabilities and interact with resources using the "FHIR API Explorer" (GET/POST/PUT/DELETE) and "FHIR UI Operations" pages, supporting both the local server (in Standalone mode) and external servers.</li>
</ul> </ul>
</li> </li>
<li><strong>FHIR Shorthand (FSH) Conversion:</strong> Convert FHIR JSON or XML resources to FSH using the integrated GoFSH tool. Offers advanced options like context package selection, various output styles, FHIR version selection, dependency loading, alias file usage, and round-trip validation ("Fishing Trip") with SUSHI. Includes a loading indicator during conversion.</li> <li><strong>FHIR Shorthand (FSH) Conversion:</strong> Convert FHIR JSON or XML resources to FSH using the integrated GoFSH tool. Offers advanced options like context package selection, various output styles, FHIR version selection, dependency loading, alias file usage, and round-trip validation ("Fishing Trip") with SUSHI. Includes a loading indicator during conversion.</li>
@ -48,7 +48,7 @@
<ul> <ul>
<li><strong>Backend:</strong> Python with the Flask web framework and SQLAlchemy for database interaction (SQLite).</li> <li><strong>Backend:</strong> Python with the Flask web framework and SQLAlchemy for database interaction (SQLite).</li>
<li><strong>Frontend:</strong> HTML, Bootstrap 5 for styling, and JavaScript for interactivity and dynamic content loading. Uses Lottie-Web for animations.</li> <li><strong>Frontend:</strong> HTML, Bootstrap 5 for styling, and JavaScript for interactivity and dynamic content loading. Uses Lottie-Web for animations.</li>
<li><strong>FHIR Tooling:</strong> Integrates GoFSH and SUSHI (via Node.js) for FSH conversion and validation. Utilizes the HAPI FHIR server (in Standalone mode) for robust FHIR validation and operations.</li> <li><strong>FHIR Tooling:</strong> Integrates GoFSH and SUSHI (via Node.js) for FSH conversion and validation. Utilizes the Local FHIR server (in Standalone mode) for robust FHIR validation and operations.</li>
<li><strong>Deployment:</strong> Runs within a Docker container managed by Docker Compose and Supervisor, ensuring a consistent environment.</li> <li><strong>Deployment:</strong> Runs within a Docker container managed by Docker Compose and Supervisor, ensuring a consistent environment.</li>
</ul> </ul>

View File

@ -796,11 +796,14 @@
<li class="nav-item"> <li class="nav-item">
<a class="nav-link {{ 'active' if request.endpoint == 'fsh_converter' else '' }}" href="{{ url_for('fsh_converter') }}"><i class="fas fa-file-code me-1"></i> FSH Converter</a> <a class="nav-link {{ 'active' if request.endpoint == 'fsh_converter' else '' }}" href="{{ url_for('fsh_converter') }}"><i class="fas fa-file-code me-1"></i> FSH Converter</a>
</li> </li>
{% if app_mode != 'lite' %} <!-- <li class="nav-item">
<a class="nav-link {{ 'active' if request.endpoint == 'ig_configurator' else '' }}" href="{{ url_for('ig_configurator') }}"><i class="fas fa-wrench me-1"></i> IG Valid-Conf Gen</a>
</li> -->
<!-- {% if app_mode != 'lite' %}
<li class="nav-item"> <li class="nav-item">
<a class="nav-link {{ 'active' if request.endpoint == 'config_hapi' else '' }}" href="{{ url_for('config_hapi') }}"><i class="fas fa-cog me-1"></i> Configure HAPI</a> <a class="nav-link {{ 'active' if request.endpoint == 'config_hapi' else '' }}" href="{{ url_for('config_hapi') }}"><i class="fas fa-cog me-1"></i> Configure HAPI</a>
</li> </li>
{% endif %} {% endif %} -->
</ul> </ul>
<div class="navbar-controls d-flex align-items-center"> <div class="navbar-controls d-flex align-items-center">
<div class="form-check form-switch"> <div class="form-check form-switch">

View File

@ -40,7 +40,7 @@
<div class="row g-4"> <div class="row g-4">
<!-- LEFT: Downloaded Packages --> <!-- LEFT: Downloaded Packages -->
<div class="col-md-6"> <div class="col-md-5">
<div class="card h-100"> <div class="card h-100">
<div class="card-header"><i class="bi bi-folder-symlink me-1"></i> Downloaded Packages ({{ packages|length }})</div> <div class="card-header"><i class="bi bi-folder-symlink me-1"></i> Downloaded Packages ({{ packages|length }})</div>
<div class="card-body"> <div class="card-body">
@ -65,20 +65,20 @@
</td> </td>
<td>{{ pkg.version }}</td> <td>{{ pkg.version }}</td>
<td> <td>
<div class="btn-group btn-group-sm"> <div class="btn-group-vertical btn-group-sm w-80">
{% if is_processed %} {% if is_processed %}
<span class="btn btn-success disabled"><i class="bi bi-check-lg"></i> Processed</span> <span class="btn btn-success disabled"><i class="bi bi-check-lg"></i> Processed</span>
{% else %} {% else %}
<form method="POST" action="{{ url_for('process_ig') }}" style="display:inline;"> <form method="POST" action="{{ url_for('process_ig') }}" style="display:inline;">
{{ form.csrf_token }} {{ form.csrf_token }}
<input type="hidden" name="filename" value="{{ pkg.filename }}"> <input type="hidden" name="filename" value="{{ pkg.filename }}">
<button type="submit" class="btn btn-outline-primary"><i class="bi bi-gear"></i> Process</button> <button type="submit" class="btn btn-outline-primary w-80"><i class="bi bi-gear"></i> Process</button>
</form> </form>
{% endif %} {% endif %}
<form method="POST" action="{{ url_for('delete_ig') }}" style="display:inline;"> <form method="POST" action="{{ url_for('delete_ig') }}" style="display:inline;">
{{ form.csrf_token }} {{ form.csrf_token }}
<input type="hidden" name="filename" value="{{ pkg.filename }}"> <input type="hidden" name="filename" value="{{ pkg.filename }}">
<button type="submit" class="btn btn-outline-danger" onclick="return confirm('Are you sure you want to delete {{ pkg.name }}#{{ pkg.version }}?');"><i class="bi bi-trash"></i> Delete</button> <button type="submit" class="btn btn-outline-danger w-80" onclick="return confirm('Are you sure you want to delete {{ pkg.name }}#{{ pkg.version }}?');"><i class="bi bi-trash"></i> Delete</button>
</form> </form>
</div> </div>
</td> </td>
@ -105,7 +105,7 @@
</div> </div>
<!-- RIGHT: Processed Packages --> <!-- RIGHT: Processed Packages -->
<div class="col-md-6"> <div class="col-md-7">
<div class="card h-100"> <div class="card h-100">
<div class="card-header"><i class="bi bi-check-circle me-1"></i> Processed Packages ({{ processed_list|length }})</div> <div class="card-header"><i class="bi bi-check-circle me-1"></i> Processed Packages ({{ processed_list|length }})</div>
<div class="card-body"> <div class="card-body">
@ -139,12 +139,12 @@
{% endif %} {% endif %}
</td> </td>
<td> <td>
<div class="btn-group-vertical btn-group-sm w-100"> <div class="btn-group-vertical btn-group-sm w-80">
<a href="{{ url_for('view_ig', processed_ig_id=processed_ig.id) }}" class="btn btn-outline-info w-100" title="View Details"><i class="bi bi-search"></i> View</a> <a href="{{ url_for('view_ig', processed_ig_id=processed_ig.id) }}" class="btn btn-outline-info w-80" title="View Details"><i class="bi bi-search"></i> View</a>
<form method="POST" action="{{ url_for('unload_ig') }}" style="display:inline;"> <form method="POST" action="{{ url_for('unload_ig') }}" style="display:inline;">
{{ form.csrf_token }} {{ form.csrf_token }}
<input type="hidden" name="ig_id" value="{{ processed_ig.id }}"> <input type="hidden" name="ig_id" value="{{ processed_ig.id }}">
<button type="submit" class="btn btn-outline-warning w-100 mt-1" onclick="return confirm('Are you sure you want to unload {{ processed_ig.package_name }}#{{ processed_ig.version }}?');"><i class="bi bi-x-lg"></i> Unload</button> <button type="submit" class="btn btn-outline-warning w-80 mt-1" onclick="return confirm('Are you sure you want to unload {{ processed_ig.package_name }}#{{ processed_ig.version }}?');"><i class="bi bi-x-lg"></i> Unload</button>
</form> </form>
</div> </div>
</td> </td>

View File

@ -21,7 +21,7 @@
<label class="form-label">FHIR Server</label> <label class="form-label">FHIR Server</label>
<div class="input-group"> <div class="input-group">
<button type="button" class="btn btn-outline-primary" id="toggleServer"> <button type="button" class="btn btn-outline-primary" id="toggleServer">
<span id="toggleLabel">Use Local HAPI</span> <span id="toggleLabel">Use Local Server</span>
</button> </button>
<input type="text" class="form-control" id="fhirServerUrl" name="fhir_server_url" placeholder="e.g., https://hapi.fhir.org/baseR4" style="display: none;" aria-describedby="fhirServerHelp"> <input type="text" class="form-control" id="fhirServerUrl" name="fhir_server_url" placeholder="e.g., https://hapi.fhir.org/baseR4" style="display: none;" aria-describedby="fhirServerHelp">
</div> </div>
@ -126,7 +126,7 @@ document.addEventListener('DOMContentLoaded', function() {
const authSection = document.getElementById('authSection'); const authSection = document.getElementById('authSection');
const authTypeSelect = document.getElementById('authType'); const authTypeSelect = document.getElementById('authType');
const authInputsGroup = document.getElementById('authInputsGroup'); const authInputsGroup = document.getElementById('authInputsGroup');
const bearerTokenInput = document.getElementById('bearerToken'); const bearerTokenInput = document.getElementById('bearerTokenInput');
const basicAuthInputs = document.getElementById('basicAuthInputs'); const basicAuthInputs = document.getElementById('basicAuthInputs');
const usernameInput = document.getElementById('username'); const usernameInput = document.getElementById('username');
const passwordInput = document.getElementById('password'); const passwordInput = document.getElementById('password');
@ -212,7 +212,7 @@ document.addEventListener('DOMContentLoaded', function() {
toggleServerButton.style.pointerEvents = 'auto'; toggleServerButton.style.pointerEvents = 'auto';
toggleServerButton.removeAttribute('aria-disabled'); toggleServerButton.removeAttribute('aria-disabled');
toggleServerButton.title = "Toggle between Local HAPI and Custom URL"; toggleServerButton.title = "Toggle between Local HAPI and Custom URL";
toggleLabel.textContent = useLocalHapi ? 'Using Local HAPI' : 'Using Custom URL'; toggleLabel.textContent = useLocalHapi ? 'Using Local Server' : 'Using Custom URL';
fhirServerUrlInput.style.display = useLocalHapi ? 'none' : 'block'; fhirServerUrlInput.style.display = useLocalHapi ? 'none' : 'block';
fhirServerUrlInput.placeholder = "e.g., https://hapi.fhir.org/baseR4"; fhirServerUrlInput.placeholder = "e.g., https://hapi.fhir.org/baseR4";
fhirServerUrlInput.required = !useLocalHapi; fhirServerUrlInput.required = !useLocalHapi;
@ -244,7 +244,8 @@ document.addEventListener('DOMContentLoaded', function() {
fhirServerUrlInput.value = ''; fhirServerUrlInput.value = '';
} }
updateServerToggleUI(); updateServerToggleUI();
console.log(`Server toggled: Now using ${useLocalHapi ? 'Local HAPI' : 'Custom URL'}`); updateAuthInputsUI();
console.log(`Server toggled: Now using ${useLocalHapi ? 'Local Server' : 'Custom URL'}`);
} }
function updateRequestBodyVisibility() { function updateRequestBodyVisibility() {
@ -295,7 +296,7 @@ document.addEventListener('DOMContentLoaded', function() {
const customUrl = fhirServerUrlInput.value.trim(); const customUrl = fhirServerUrlInput.value.trim();
let body = undefined; let body = undefined;
const authType = authTypeSelect ? authTypeSelect.value : 'none'; const authType = authTypeSelect ? authTypeSelect.value : 'none';
const bearerToken = bearerTokenInput ? bearerTokenInput.value.trim() : ''; const bearerToken = document.getElementById('bearerToken').value.trim();
const username = usernameInput ? usernameInput.value.trim() : ''; const username = usernameInput ? usernameInput.value.trim() : '';
const password = passwordInput ? passwordInput.value : ''; const password = passwordInput ? passwordInput.value : '';
@ -334,7 +335,7 @@ document.addEventListener('DOMContentLoaded', function() {
if (!useLocalHapi) { if (!useLocalHapi) {
if (authType === 'bearer' && !bearerToken) { if (authType === 'bearer' && !bearerToken) {
alert('Please enter a Bearer Token.'); alert('Please enter a Bearer Token.');
bearerTokenInput.classList.add('is-invalid'); document.getElementById('bearerToken').classList.add('is-invalid');
sendButton.disabled = false; sendButton.disabled = false;
sendButton.textContent = 'Send Request'; sendButton.textContent = 'Send Request';
return; return;
@ -453,7 +454,7 @@ document.addEventListener('DOMContentLoaded', function() {
} catch (error) { } catch (error) {
console.error('Fetch error:', error); console.error('Fetch error:', error);
responseCard.style.display = 'block'; responseCard.style.display = 'block';
responseStatus.textContent = `Network Error`; responseStatus.textContent = 'Network Error';
responseStatus.className = 'badge bg-danger'; responseStatus.className = 'badge bg-danger';
responseHeaders.textContent = 'N/A'; responseHeaders.textContent = 'N/A';
let errorDetail = `Error: ${error.message}\n\n`; let errorDetail = `Error: ${error.message}\n\n`;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,40 @@
{% extends "base.html" %}
{% block content %}
<div class="container mt-4">
<h2>IG Configuration Generator</h2>
<p>Select the Implementation Guides you want to load into Local FHIR for validation. This will generate the YAML configuration required for the <code>application.yaml</code> file.</p>
<div class="card mb-4">
<div class="card-header">Select IGs</div>
<div class="card-body">
<form method="POST">
{{ form.hidden_tag() }}
<div class="mb-3">
<label class="form-label">IGs and their dependencies</label>
{% for choice in form.igs.choices %}
<div class="form-check">
<input class="form-check-input" type="checkbox" name="igs" value="{{ choice[0] }}" id="ig-{{ choice[0] }}">
<label class="form-check-label" for="ig-{{ choice[0] }}">{{ choice[1] }}</label>
</div>
{% endfor %}
</div>
{{ form.generate_yaml(class="btn btn-primary") }}
</form>
</div>
</div>
{% if yaml_output %}
<div class="card mt-4">
<div class="card-header">Generated <code>application.yaml</code> Excerpt</div>
<div class="card-body">
<p>Copy the following configuration block and paste it into the `hapi.fhir` section of your HAPI server's <code>application.yaml</code> file.</p>
<pre><code class="language-yaml">{{ yaml_output }}</code></pre>
<div class="alert alert-warning mt-3">
<strong>Important:</strong> After updating the <code>application.yaml</code> file, you must **restart your HAPI server** for the changes to take effect.
</div>
</div>
</div>
{% endif %}
</div>
{% endblock %}

View File

@ -10,7 +10,6 @@
</p> </p>
</div> </div>
</div> </div>
<div class="container mt-4"> <div class="container mt-4">
<div class="card shadow-sm mb-4"> <div class="card shadow-sm mb-4">
<div class="card-header"> <div class="card-header">
@ -27,19 +26,18 @@
</ul> </ul>
</div> </div>
{% endif %} {% endif %}
<form id="retrieveForm" method="POST" enctype="multipart/form-data"> <form id="retrieveForm" method="POST">
{{ form.hidden_tag() }} {{ form.csrf_token(id='retrieve_csrf_token') }}
<div class="mb-3"> <div class="mb-3">
<label class="form-label fw-bold">FHIR Server</label> <label class="form-label fw-bold">FHIR Server</label>
<div class="input-group"> <div class="input-group">
<button type="button" class="btn btn-outline-primary" id="toggleServer"> <button type="button" class="btn btn-outline-primary" id="toggleServer">
<span id="toggleLabel">Use Local HAPI</span> <span id="toggleLabel">Use Local Server</span>
</button> </button>
{{ form.fhir_server_url(class="form-control", id="fhirServerUrl", style="display: none;", placeholder="e.g., https://fhir.hl7.org.au/aucore/fhir/DEFAULT", **{'aria-describedby': 'fhirServerHelp'}) }} {{ form.fhir_server_url(class="form-control", id="fhirServerUrl", style="display: none;", placeholder="e.g., https://fhir.hl7.org.au/aucore/fhir/DEFAULT", **{'aria-describedby': 'fhirServerHelp'}) }}
</div> </div>
<small id="fhirServerHelp" class="form-text text-muted">Toggle to use local HAPI (/fhir proxy) or enter a custom FHIR server URL.</small> <small id="fhirServerHelp" class="form-text text-muted">Toggle to use local Fhir Server(/fhir proxy) or enter a custom FHIR server URL.</small>
</div> </div>
{# Authentication Section (Shown for Custom URL) #} {# Authentication Section (Shown for Custom URL) #}
<div class="mb-3" id="authSection" style="display: none;"> <div class="mb-3" id="authSection" style="display: none;">
<label class="form-label fw-bold">Authentication</label> <label class="form-label fw-bold">Authentication</label>
@ -68,7 +66,6 @@
</div> </div>
<small class="form-text text-muted">Select authentication method for the custom FHIR server.</small> <small class="form-text text-muted">Select authentication method for the custom FHIR server.</small>
</div> </div>
{# Checkbox Row #} {# Checkbox Row #}
<div class="row g-3 mb-3 align-items-center"> <div class="row g-3 mb-3 align-items-center">
<div class="col-md-6"> <div class="col-md-6">
@ -78,7 +75,6 @@
{{ render_field(form.fetch_reference_bundles, id='fetch_reference_bundles_checkbox') }} {{ render_field(form.fetch_reference_bundles, id='fetch_reference_bundles_checkbox') }}
</div> </div>
</div> </div>
<button type="button" class="btn btn-primary mb-3" id="fetchMetadata">Fetch Metadata</button> <button type="button" class="btn btn-primary mb-3" id="fetchMetadata">Fetch Metadata</button>
<div class="banner3 mt-3" id="resourceTypes" style="display: none;"> <div class="banner3 mt-3" id="resourceTypes" style="display: none;">
<h5>Resource Types</h5> <h5>Resource Types</h5>
@ -102,14 +98,13 @@
</div> </div>
</div> </div>
</div> </div>
<div class="card shadow-sm"> <div class="card shadow-sm">
<div class="card-header"> <div class="card-header">
<h4 class="my-0 fw-normal"><i class="bi bi-scissors me-2"></i>Split Bundles</h4> <h4 class="my-0 fw-normal"><i class="bi bi-scissors me-2"></i>Split Bundles</h4>
</div> </div>
<div class="card-body"> <div class="card-body">
<form id="splitForm" method="POST" enctype="multipart/form-data"> <form id="splitForm" method="POST" enctype="multipart/form-data">
{{ form.hidden_tag() }} {{ form.csrf_token(id='split_csrf_token') }}
<div class="mb-3"> <div class="mb-3">
<label class="form-label fw-bold">Bundle Source</label> <label class="form-label fw-bold">Bundle Source</label>
<div class="form-check"> <div class="form-check">
@ -141,7 +136,6 @@
</div> </div>
</div> </div>
</div> </div>
<script> <script>
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
// --- DOM Element References --- // --- DOM Element References ---
@ -168,11 +162,10 @@ document.addEventListener('DOMContentLoaded', () => {
const authSection = document.getElementById('authSection'); const authSection = document.getElementById('authSection');
const authTypeSelect = document.getElementById('authType'); const authTypeSelect = document.getElementById('authType');
const authInputsGroup = document.getElementById('authInputsGroup'); const authInputsGroup = document.getElementById('authInputsGroup');
const bearerTokenInput = document.getElementById('bearerToken'); const bearerTokenInput = document.getElementById('bearerTokenInput');
const basicAuthInputs = document.getElementById('basicAuthInputs'); const basicAuthInputs = document.getElementById('basicAuthInputs');
const usernameInput = document.getElementById('username'); const usernameInput = document.getElementById('username');
const passwordInput = document.getElementById('password'); const passwordInput = document.getElementById('password');
// --- State & Config Variables --- // --- State & Config Variables ---
const appMode = '{{ app_mode | default("standalone") | lower }}'; const appMode = '{{ app_mode | default("standalone") | lower }}';
const apiKey = '{{ api_key }}'; const apiKey = '{{ api_key }}';
@ -181,10 +174,9 @@ document.addEventListener('DOMContentLoaded', () => {
let retrieveZipPath = null; let retrieveZipPath = null;
let splitZipPath = null; let splitZipPath = null;
let fetchedMetadataCache = null; let fetchedMetadataCache = null;
let localHapiBaseUrl = ''; // To store the fetched URL
// --- Helper Functions --- // --- Helper Functions ---
const sanitizeText = (str) => str ? String(str).replace(/</g, "&lt;").replace(/>/g, "&gt;") : ""; const sanitizeText = (str) => str ? String(str).replace(/</g, "&lt;").replace(/>/g, "&gt;") : "";
// --- UI Update Functions --- // --- UI Update Functions ---
function updateBundleSourceUI() { function updateBundleSourceUI() {
const selectedSource = Array.from(bundleSourceRadios).find(radio => radio.checked)?.value; const selectedSource = Array.from(bundleSourceRadios).find(radio => radio.checked)?.value;
@ -195,10 +187,23 @@ document.addEventListener('DOMContentLoaded', () => {
splitBundleZipInput.required = selectedSource === 'upload'; splitBundleZipInput.required = selectedSource === 'upload';
} }
} }
async function fetchLocalUrlAndSetUI() {
try {
const response = await fetch('/api/get-local-server-url');
if (!response.ok) throw new Error('Failed to fetch local server URL.');
const data = await response.json();
localHapiBaseUrl = data.url.replace(/\/+$/, ''); // Normalize by removing trailing slashes
console.log(`Local HAPI URL fetched: ${localHapiBaseUrl}`);
} catch (error) {
console.error(error);
localHapiBaseUrl = '/fhir'; // Fallback to proxy
alert('Could not fetch local HAPI server URL. Falling back to proxy.');
} finally {
updateServerToggleUI();
}
}
function updateServerToggleUI() { function updateServerToggleUI() {
if (!toggleLabel || !fhirServerUrlInput || !toggleServerButton || !authSection) return; if (!toggleLabel || !fhirServerUrlInput || !toggleServerButton || !authSection) return;
if (appMode === 'lite') { if (appMode === 'lite') {
useLocalHapi = false; useLocalHapi = false;
toggleServerButton.disabled = true; toggleServerButton.disabled = true;
@ -216,8 +221,7 @@ document.addEventListener('DOMContentLoaded', () => {
toggleServerButton.classList.remove('disabled'); toggleServerButton.classList.remove('disabled');
toggleServerButton.style.pointerEvents = 'auto'; toggleServerButton.style.pointerEvents = 'auto';
toggleServerButton.removeAttribute('aria-disabled'); toggleServerButton.removeAttribute('aria-disabled');
toggleServerButton.title = "Toggle between Local HAPI and Custom URL"; toggleLabel.textContent = useLocalHapi ? 'Use Local Server' : 'Use Custom URL';
toggleLabel.textContent = useLocalHapi ? 'Using Local HAPI' : 'Using Custom URL';
fhirServerUrlInput.style.display = useLocalHapi ? 'none' : 'block'; fhirServerUrlInput.style.display = useLocalHapi ? 'none' : 'block';
fhirServerUrlInput.placeholder = "e.g., https://hapi.fhir.org/baseR4"; fhirServerUrlInput.placeholder = "e.g., https://hapi.fhir.org/baseR4";
fhirServerUrlInput.required = !useLocalHapi; fhirServerUrlInput.required = !useLocalHapi;
@ -227,18 +231,22 @@ document.addEventListener('DOMContentLoaded', () => {
updateAuthInputsUI(); updateAuthInputsUI();
console.log(`Server toggle UI updated: useLocalHapi=${useLocalHapi}, customUrl=${fhirServerUrlInput.value}`); console.log(`Server toggle UI updated: useLocalHapi=${useLocalHapi}, customUrl=${fhirServerUrlInput.value}`);
} }
function updateAuthInputsUI() { function updateAuthInputsUI() {
if (!authTypeSelect || !authInputsGroup || !bearerTokenInput || !basicAuthInputs) return; if (!authTypeSelect || !authInputsGroup || !bearerTokenInput || !basicAuthInputs) return;
const authType = authTypeSelect.value; const authType = authTypeSelect.value;
authInputsGroup.style.display = (authType === 'bearer' || authType === 'basic') ? 'block' : 'none'; authInputsGroup.style.display = (authType === 'bearer' || authType === 'basic') ? 'block' : 'none';
bearerTokenInput.style.display = authType === 'bearer' ? 'block' : 'none'; bearerTokenInput.style.display = authType === 'bearer' ? 'block' : 'none';
basicAuthInputs.style.display = authType === 'basic' ? 'block' : 'none'; basicAuthInputs.style.display = authType === 'basic' ? 'block' : 'none';
if (authType !== 'bearer' && bearerTokenInput) bearerTokenInput.value = ''; if (authType !== 'bearer') {
if (authType !== 'basic' && usernameInput) usernameInput.value = ''; // Correctly reference the input element by its ID
if (authType !== 'basic' && passwordInput) passwordInput.value = ''; const tokenInput = document.getElementById('bearerToken');
if (tokenInput) tokenInput.value = '';
}
if (authType !== 'basic') {
if (usernameInput) usernameInput.value = '';
if (passwordInput) passwordInput.value = '';
}
} }
function toggleFetchReferenceBundles() { function toggleFetchReferenceBundles() {
if (validateReferencesCheckbox && fetchReferenceBundlesGroup) { if (validateReferencesCheckbox && fetchReferenceBundlesGroup) {
fetchReferenceBundlesGroup.style.display = validateReferencesCheckbox.checked ? 'block' : 'none'; fetchReferenceBundlesGroup.style.display = validateReferencesCheckbox.checked ? 'block' : 'none';
@ -249,44 +257,40 @@ document.addEventListener('DOMContentLoaded', () => {
console.warn("Could not find checkbox elements needed for toggling."); console.warn("Could not find checkbox elements needed for toggling.");
} }
} }
// --- Event Listeners --- // --- Event Listeners ---
bundleSourceRadios.forEach(radio => { bundleSourceRadios.forEach(radio => {
radio.addEventListener('change', updateBundleSourceUI); radio.addEventListener('change', updateBundleSourceUI);
}); });
toggleServerButton.addEventListener('click', () => { toggleServerButton.addEventListener('click', () => {
if (appMode === 'lite') return; if (appMode === 'lite') return;
useLocalHapi = !useLocalHapi; useLocalHapi = !useLocalHapi;
if (useLocalHapi) fhirServerUrlInput.value = ''; if (useLocalHapi) fhirServerUrlInput.value = '';
updateServerToggleUI(); updateServerToggleUI();
updateAuthInputsUI();
resourceTypesDiv.style.display = 'none'; resourceTypesDiv.style.display = 'none';
fetchedMetadataCache = null; fetchedMetadataCache = null;
console.log(`Server toggled: useLocalHapi=${useLocalHapi}`); console.log(`Server toggled: useLocalHapi=${useLocalHapi}`);
}); });
if (authTypeSelect) { if (authTypeSelect) {
authTypeSelect.addEventListener('change', updateAuthInputsUI); authTypeSelect.addEventListener('change', updateAuthInputsUI);
} }
if (validateReferencesCheckbox) { if (validateReferencesCheckbox) {
validateReferencesCheckbox.addEventListener('change', toggleFetchReferenceBundles); validateReferencesCheckbox.addEventListener('change', toggleFetchReferenceBundles);
} else { } else {
console.warn("Validate references checkbox not found."); console.warn("Validate references checkbox not found.");
} }
fetchMetadataButton.addEventListener('click', async () => { fetchMetadataButton.addEventListener('click', async () => {
resourceButtonsContainer.innerHTML = '<span class="text-muted">Fetching...</span>'; resourceButtonsContainer.innerHTML = '<span class="text-muted">Fetching...</span>';
resourceTypesDiv.style.display = 'block'; resourceTypesDiv.style.display = 'block';
const customUrl = useLocalHapi ? null : fhirServerUrlInput.value.trim().replace(/\/+$/, ''); let customUrl = null;
if (!useLocalHapi && !customUrl) {
fhirServerUrlInput.classList.add('is-invalid');
alert('Please enter a valid FHIR server URL.');
resourceButtonsContainer.innerHTML = '<span class="text-danger">Error: Custom URL required.</span>';
return;
}
if (!useLocalHapi) { if (!useLocalHapi) {
customUrl = fhirServerUrlInput.value.trim().replace(/\/+$/, ''); // Normalize custom URL
if (!customUrl) {
fhirServerUrlInput.classList.add('is-invalid');
alert('Please enter a valid FHIR server URL.');
resourceButtonsContainer.innerHTML = '<span class="text-danger">Error: Custom URL required.</span>';
return;
}
try { new URL(customUrl); } catch (_) { try { new URL(customUrl); } catch (_) {
fhirServerUrlInput.classList.add('is-invalid'); fhirServerUrlInput.classList.add('is-invalid');
alert('Invalid custom URL format.'); alert('Invalid custom URL format.');
@ -295,33 +299,32 @@ document.addEventListener('DOMContentLoaded', () => {
} }
} }
fhirServerUrlInput.classList.remove('is-invalid'); fhirServerUrlInput.classList.remove('is-invalid');
fetchMetadataButton.disabled = true; fetchMetadataButton.disabled = true;
fetchMetadataButton.textContent = 'Fetching...'; fetchMetadataButton.textContent = 'Fetching...';
try { try {
const fetchUrl = '/fhir/metadata'; // Build URL and headers based on the toggle state
let fetchUrl;
const headers = { 'Accept': 'application/fhir+json' }; const headers = { 'Accept': 'application/fhir+json' };
if (!useLocalHapi && customUrl) { if (useLocalHapi) {
headers['X-Target-FHIR-Server'] = customUrl; if (!localHapiBaseUrl) {
if (authTypeSelect && authTypeSelect.value !== 'none') { throw new Error("Local HAPI URL not available. Please refresh.");
const authType = authTypeSelect.value;
if (authType === 'bearer' && bearerTokenInput && bearerTokenInput.value) {
headers['Authorization'] = `Bearer ${bearerTokenInput.value}`;
} else if (authType === 'basic' && usernameInput && passwordInput && usernameInput.value && passwordInput.value) {
const credentials = btoa(`${usernameInput.value}:${passwordInput.value}`);
headers['Authorization'] = `Basic ${credentials}`;
}
} }
console.log(`Fetching metadata via proxy with X-Target-FHIR-Server: ${customUrl}`); fetchUrl = `${localHapiBaseUrl}/metadata`; // localHapiBaseUrl is already normalized
console.log(`Fetching metadata directly from local URL: ${fetchUrl}`);
} else { } else {
console.log("Fetching metadata via proxy for local HAPI server"); fetchUrl = `${customUrl}/metadata`;
// Add authentication headers for the direct request to the custom server
const authType = authTypeSelect.value;
if (authType === 'bearer' && document.getElementById('bearerToken') && document.getElementById('bearerToken').value) {
headers['Authorization'] = `Bearer ${document.getElementById('bearerToken').value}`;
} else if (authType === 'basic' && usernameInput && passwordInput && usernameInput.value && passwordInput.value) {
const credentials = btoa(`${usernameInput.value}:${passwordInput.value}`);
headers['Authorization'] = `Basic ${credentials}`;
}
console.log(`Fetching metadata directly from custom URL: ${fetchUrl}`);
} }
console.log(`Proxy Fetch URL: ${fetchUrl}`); console.log(`Request Headers sent: ${JSON.stringify(headers)}`);
console.log(`Request Headers sent TO PROXY: ${JSON.stringify(headers)}`);
const response = await fetch(fetchUrl, { method: 'GET', headers: headers }); const response = await fetch(fetchUrl, { method: 'GET', headers: headers });
if (!response.ok) { if (!response.ok) {
let errorText = `Request failed with status ${response.status} ${response.statusText}`; let errorText = `Request failed with status ${response.status} ${response.statusText}`;
try { const errorBody = await response.text(); errorText += ` - Body: ${errorBody.substring(0, 200)}...`; } catch (e) {} try { const errorBody = await response.text(); errorText += ` - Body: ${errorBody.substring(0, 200)}...`; } catch (e) {}
@ -331,7 +334,6 @@ document.addEventListener('DOMContentLoaded', () => {
const data = await response.json(); const data = await response.json();
console.log("Metadata received:", JSON.stringify(data, null, 2).substring(0, 500)); console.log("Metadata received:", JSON.stringify(data, null, 2).substring(0, 500));
fetchedMetadataCache = data; fetchedMetadataCache = data;
const resourceTypes = data.rest?.[0]?.resource?.map(r => r.type)?.filter(Boolean) || []; const resourceTypes = data.rest?.[0]?.resource?.map(r => r.type)?.filter(Boolean) || [];
resourceButtonsContainer.innerHTML = ''; resourceButtonsContainer.innerHTML = '';
if (!resourceTypes.length) { if (!resourceTypes.length) {
@ -362,7 +364,6 @@ document.addEventListener('DOMContentLoaded', () => {
fetchMetadataButton.textContent = 'Fetch Metadata'; fetchMetadataButton.textContent = 'Fetch Metadata';
} }
}); });
retrieveForm.addEventListener('submit', async (e) => { retrieveForm.addEventListener('submit', async (e) => {
e.preventDefault(); e.preventDefault();
const spinner = retrieveButton.querySelector('.spinner-border'); const spinner = retrieveButton.querySelector('.spinner-border');
@ -373,7 +374,6 @@ document.addEventListener('DOMContentLoaded', () => {
downloadRetrieveButton.style.display = 'none'; downloadRetrieveButton.style.display = 'none';
retrieveZipPath = null; retrieveZipPath = null;
retrieveConsole.innerHTML = `<div>${new Date().toLocaleTimeString()} [INFO] Starting bundle retrieval...</div>`; retrieveConsole.innerHTML = `<div>${new Date().toLocaleTimeString()} [INFO] Starting bundle retrieval...</div>`;
const selectedResources = Array.from(resourceButtonsContainer.querySelectorAll('.btn.active')).map(btn => btn.dataset.resource); const selectedResources = Array.from(resourceButtonsContainer.querySelectorAll('.btn.active')).map(btn => btn.dataset.resource);
if (!selectedResources.length) { if (!selectedResources.length) {
alert('Please select at least one resource type.'); alert('Please select at least one resource type.');
@ -382,12 +382,20 @@ document.addEventListener('DOMContentLoaded', () => {
if (icon) icon.style.display = 'inline-block'; if (icon) icon.style.display = 'inline-block';
return; return;
} }
const currentFhirServerUrl = useLocalHapi ? localHapiBaseUrl : fhirServerUrlInput.value.trim().replace(/\/+$/, '');
if (!currentFhirServerUrl) {
alert('FHIR Server URL is required.');
fhirServerUrlInput.classList.add('is-invalid');
retrieveButton.disabled = false;
if (spinner) spinner.style.display = 'none';
if (icon) icon.style.display = 'inline-block';
return;
}
const formData = new FormData(); const formData = new FormData();
const csrfTokenInput = retrieveForm.querySelector('input[name="csrf_token"]'); const csrfTokenInput = retrieveForm.querySelector('input[name="csrf_token"]');
if (csrfTokenInput) formData.append('csrf_token', csrfTokenInput.value); if (csrfTokenInput) formData.append('csrf_token', csrfTokenInput.value);
selectedResources.forEach(res => formData.append('resources', res)); selectedResources.forEach(res => formData.append('resources', res));
formData.append('fhir_server_url', currentFhirServerUrl);
if (validateReferencesCheckbox) { if (validateReferencesCheckbox) {
formData.append('validate_references', validateReferencesCheckbox.checked ? 'true' : 'false'); formData.append('validate_references', validateReferencesCheckbox.checked ? 'true' : 'false');
} }
@ -400,31 +408,19 @@ document.addEventListener('DOMContentLoaded', () => {
} else { } else {
formData.append('fetch_reference_bundles', 'false'); formData.append('fetch_reference_bundles', 'false');
} }
const currentFhirServerUrl = useLocalHapi ? '/fhir' : fhirServerUrlInput.value.trim();
if (!useLocalHapi && !currentFhirServerUrl) {
alert('Custom FHIR Server URL is required.');
fhirServerUrlInput.classList.add('is-invalid');
retrieveButton.disabled = false;
if (spinner) spinner.style.display = 'none';
if (icon) icon.style.display = 'inline-block';
return;
}
formData.append('fhir_server_url', currentFhirServerUrl);
// Add authentication fields for custom URL
if (!useLocalHapi && authTypeSelect) { if (!useLocalHapi && authTypeSelect) {
const authType = authTypeSelect.value; const authType = authTypeSelect.value;
formData.append('auth_type', authType); formData.append('auth_type', authType);
if (authType === 'bearer' && bearerTokenInput) { if (authType === 'bearer') {
if (!bearerTokenInput.value) { const bearerToken = document.getElementById('bearerToken').value.trim();
if (!bearerToken) {
alert('Please enter a Bearer Token.'); alert('Please enter a Bearer Token.');
retrieveButton.disabled = false; retrieveButton.disabled = false;
if (spinner) spinner.style.display = 'none'; if (spinner) spinner.style.display = 'none';
if (icon) icon.style.display = 'inline-block'; if (icon) icon.style.display = 'inline-block';
return; return;
} }
formData.append('bearer_token', bearerTokenInput.value); formData.append('bearer_token', bearerToken);
} else if (authType === 'basic' && usernameInput && passwordInput) { } else if (authType === 'basic' && usernameInput && passwordInput) {
if (!usernameInput.value || !passwordInput.value) { if (!usernameInput.value || !passwordInput.value) {
alert('Please enter both Username and Password for Basic Authentication.'); alert('Please enter both Username and Password for Basic Authentication.');
@ -437,24 +433,20 @@ document.addEventListener('DOMContentLoaded', () => {
formData.append('password', passwordInput.value); formData.append('password', passwordInput.value);
} }
} }
const headers = { const headers = {
'Accept': 'application/x-ndjson', 'Accept': 'application/x-ndjson',
'X-CSRFToken': csrfTokenInput ? csrfTokenInput.value : '', 'X-CSRFToken': csrfTokenInput ? csrfTokenInput.value : '',
'X-API-Key': apiKey 'X-API-Key': apiKey
}; };
console.log(`Submitting retrieve request. Server: ${currentFhirServerUrl}, ValidateRefs: ${formData.get('validate_references')}, FetchRefBundles: ${formData.get('fetch_reference_bundles')}, AuthType: ${formData.get('auth_type')}`); console.log(`Submitting retrieve request. Server: ${currentFhirServerUrl}, ValidateRefs: ${formData.get('validate_references')}, FetchRefBundles: ${formData.get('fetch_reference_bundles')}, AuthType: ${formData.get('auth_type')}`);
try { try {
const response = await fetch('/api/retrieve-bundles', { method: 'POST', headers: headers, body: formData }); const response = await fetch('/api/retrieve-bundles', { method: 'POST', headers: headers, body: formData });
if (!response.ok) { if (!response.ok) {
const errorData = await response.json().catch(() => ({ message: 'Failed to parse error response.' })); const errorData = await response.json().catch(() => ({ message: 'Failed to parse error response.' }));
throw new Error(`HTTP ${response.status}: ${errorData.message || 'Unknown error during retrieval'}`); throw new Error(`HTTP ${response.status}: ${errorData.message || 'Unknown error during retrieval'}`);
} }
retrieveZipPath = response.headers.get('X-Zip-Path'); retrieveZipPath = response.headers.get('X-Zip-Path');
console.log(`Received X-Zip-Path: ${retrieveZipPath}`); console.log(`Received X-Zip-Path: ${retrieveZipPath}`);
const reader = response.body.getReader(); const reader = response.body.getReader();
const decoder = new TextDecoder(); const decoder = new TextDecoder();
let buffer = ''; let buffer = '';
@ -476,13 +468,11 @@ document.addEventListener('DOMContentLoaded', () => {
else if (data.type === 'success') { prefix = '[SUCCESS]'; messageClass = 'text-success'; } else if (data.type === 'success') { prefix = '[SUCCESS]'; messageClass = 'text-success'; }
else if (data.type === 'progress') { prefix = '[PROGRESS]'; messageClass = 'text-light'; } else if (data.type === 'progress') { prefix = '[PROGRESS]'; messageClass = 'text-light'; }
else if (data.type === 'complete') { prefix = '[COMPLETE]'; messageClass = 'text-primary'; } else if (data.type === 'complete') { prefix = '[COMPLETE]'; messageClass = 'text-primary'; }
const messageDiv = document.createElement('div'); const messageDiv = document.createElement('div');
messageDiv.className = messageClass; messageDiv.className = messageClass;
messageDiv.innerHTML = `${timestamp} ${prefix} ${sanitizeText(data.message)}`; messageDiv.innerHTML = `${timestamp} ${prefix} ${sanitizeText(data.message)}`;
retrieveConsole.appendChild(messageDiv); retrieveConsole.appendChild(messageDiv);
retrieveConsole.scrollTop = retrieveConsole.scrollHeight; retrieveConsole.scrollTop = retrieveConsole.scrollHeight;
if (data.type === 'complete' && retrieveZipPath) { if (data.type === 'complete' && retrieveZipPath) {
const completeData = data.data || {}; const completeData = data.data || {};
const bundlesFound = (completeData.total_initial_bundles > 0) || (completeData.fetched_individual_references > 0) || (completeData.fetched_type_bundles > 0); const bundlesFound = (completeData.total_initial_bundles > 0) || (completeData.fetched_individual_references > 0) || (completeData.fetched_type_bundles > 0);
@ -500,7 +490,6 @@ document.addEventListener('DOMContentLoaded', () => {
} }
} }
if (buffer.trim()) { /* Handle final buffer */ } if (buffer.trim()) { /* Handle final buffer */ }
} catch (e) { } catch (e) {
console.error('Retrieval error:', e); console.error('Retrieval error:', e);
const ts = new Date().toLocaleTimeString(); const ts = new Date().toLocaleTimeString();
@ -512,20 +501,17 @@ document.addEventListener('DOMContentLoaded', () => {
if (icon) icon.style.display = 'inline-block'; if (icon) icon.style.display = 'inline-block';
} }
}); });
downloadRetrieveButton.addEventListener('click', () => { downloadRetrieveButton.addEventListener('click', () => {
if (retrieveZipPath) { if (retrieveZipPath) {
const filename = retrieveZipPath.split('/').pop() || 'retrieved_bundles.zip'; const filename = retrieveZipPath.split('/').pop() || 'retrieved_bundles.zip';
const downloadUrl = `/tmp/${filename}`; const downloadUrl = `/tmp/${filename}`;
console.log(`Attempting to download ZIP from Flask endpoint: ${downloadUrl}`); console.log(`Attempting to download ZIP from Flask endpoint: ${downloadUrl}`);
const link = document.createElement('a'); const link = document.createElement('a');
link.href = downloadUrl; link.href = downloadUrl;
link.download = 'retrieved_bundles.zip'; link.download = 'retrieved_bundles.zip';
document.body.appendChild(link); document.body.appendChild(link);
link.click(); link.click();
document.body.removeChild(link); document.body.removeChild(link);
setTimeout(() => { setTimeout(() => {
const csrfToken = retrieveForm.querySelector('input[name="csrf_token"]')?.value || ''; const csrfToken = retrieveForm.querySelector('input[name="csrf_token"]')?.value || '';
fetch('/clear-session', { method: 'POST', headers: { 'X-CSRFToken': csrfToken }}) fetch('/clear-session', { method: 'POST', headers: { 'X-CSRFToken': csrfToken }})
@ -543,7 +529,6 @@ document.addEventListener('DOMContentLoaded', () => {
alert("Download error: No file path available."); alert("Download error: No file path available.");
} }
}); });
splitForm.addEventListener('submit', async (e) => { splitForm.addEventListener('submit', async (e) => {
e.preventDefault(); e.preventDefault();
const spinner = splitButton.querySelector('.spinner-border'); const spinner = splitButton.querySelector('.spinner-border');
@ -554,13 +539,10 @@ document.addEventListener('DOMContentLoaded', () => {
downloadSplitButton.style.display = 'none'; downloadSplitButton.style.display = 'none';
splitZipPath = null; splitZipPath = null;
splitConsole.innerHTML = `<div>${new Date().toLocaleTimeString()} [INFO] Starting bundle splitting...</div>`; splitConsole.innerHTML = `<div>${new Date().toLocaleTimeString()} [INFO] Starting bundle splitting...</div>`;
const formData = new FormData(); const formData = new FormData();
const csrfTokenInput = splitForm.querySelector('input[name="csrf_token"]'); const csrfTokenInput = splitForm.querySelector('input[name="csrf_token"]');
if (csrfTokenInput) formData.append('csrf_token', csrfTokenInput.value); if (csrfTokenInput) formData.append('csrf_token', csrfTokenInput.value);
const bundleSource = Array.from(bundleSourceRadios).find(radio => radio.checked)?.value; const bundleSource = Array.from(bundleSourceRadios).find(radio => radio.checked)?.value;
if (bundleSource === 'upload') { if (bundleSource === 'upload') {
if (!splitBundleZipInput || splitBundleZipInput.files.length === 0) { if (!splitBundleZipInput || splitBundleZipInput.files.length === 0) {
alert('Please select a ZIP file to upload for splitting.'); alert('Please select a ZIP file to upload for splitting.');
@ -592,16 +574,13 @@ document.addEventListener('DOMContentLoaded', () => {
if (icon) icon.style.display = 'inline-block'; if (icon) icon.style.display = 'inline-block';
return; return;
} }
const headers = { const headers = {
'Accept': 'application/x-ndjson', 'Accept': 'application/x-ndjson',
'X-CSRFToken': csrfTokenInput ? csrfTokenInput.value : '', 'X-CSRFToken': csrfTokenInput ? csrfTokenInput.value : '',
'X-API-Key': apiKey 'X-API-Key': apiKey
}; };
try { try {
const response = await fetch('/api/split-bundles', { method: 'POST', headers: headers, body: formData }); const response = await fetch('/api/split-bundles', { method: 'POST', headers: headers, body: formData });
if (!response.ok) { if (!response.ok) {
const errorMsg = await response.text(); const errorMsg = await response.text();
let detail = errorMsg; let detail = errorMsg;
@ -610,7 +589,6 @@ document.addEventListener('DOMContentLoaded', () => {
} }
splitZipPath = response.headers.get('X-Zip-Path'); splitZipPath = response.headers.get('X-Zip-Path');
console.log(`Received X-Zip-Path for split: ${splitZipPath}`); console.log(`Received X-Zip-Path for split: ${splitZipPath}`);
const reader = response.body.getReader(); const reader = response.body.getReader();
const decoder = new TextDecoder(); const decoder = new TextDecoder();
let buffer = ''; let buffer = '';
@ -643,7 +621,6 @@ document.addEventListener('DOMContentLoaded', () => {
} }
} }
if (buffer.trim()) { /* Handle final buffer */ } if (buffer.trim()) { /* Handle final buffer */ }
} catch (e) { } catch (e) {
console.error('Splitting error:', e); console.error('Splitting error:', e);
const ts = new Date().toLocaleTimeString(); const ts = new Date().toLocaleTimeString();
@ -655,7 +632,6 @@ document.addEventListener('DOMContentLoaded', () => {
if (icon) icon.style.display = 'inline-block'; if (icon) icon.style.display = 'inline-block';
} }
}); });
downloadSplitButton.addEventListener('click', () => { downloadSplitButton.addEventListener('click', () => {
if (splitZipPath) { if (splitZipPath) {
const filename = splitZipPath.split('/').pop() || 'split_resources.zip'; const filename = splitZipPath.split('/').pop() || 'split_resources.zip';
@ -680,10 +656,9 @@ document.addEventListener('DOMContentLoaded', () => {
alert("Download error: No file path available."); alert("Download error: No file path available.");
} }
}); });
// --- Initial Setup Calls --- // --- Initial Setup Calls ---
updateBundleSourceUI(); updateBundleSourceUI();
updateServerToggleUI(); fetchLocalUrlAndSetUI();
toggleFetchReferenceBundles(); toggleFetchReferenceBundles();
}); });
</script> </script>

View File

@ -1,4 +1,3 @@
<!-- templates/validate_sample.html -->
{% extends "base.html" %} {% extends "base.html" %}
{% block content %} {% block content %}
@ -7,14 +6,15 @@
<h1 class="display-5 fw-bold text-body-emphasis">Validate FHIR Sample</h1> <h1 class="display-5 fw-bold text-body-emphasis">Validate FHIR Sample</h1>
<div class="col-lg-6 mx-auto"> <div class="col-lg-6 mx-auto">
<p class="lead mb-4"> <p class="lead mb-4">
Validate a FHIR resource or bundle against a selected Implementation Guide. (ALPHA - TEST Fhir pathing is complex and the logic is WIP, please report anomalies you find in Github issues alongside your sample json REMOVE PHI) Validate a FHIR resource or bundle against a selected Implementation Guide.
</p> </p>
<!-----------------------------------------------------------------remove the buttons----------------------------------------------------- </div>
<div class="d-grid gap-2 d-sm-flex justify-content-sm-center"> </div>
<a href="{{ url_for('index') }}" class="btn btn-primary btn-lg px-4 gap-3">Back to Home</a>
<a href="{{ url_for('view_igs') }}" class="btn btn-outline-secondary btn-lg px-4">Manage FHIR Packages</a> <div id="spinner-overlay" class="d-none position-fixed top-0 start-0 w-100 h-100 bg-dark bg-opacity-50 d-flex align-items-center justify-content-center" style="z-index: 1050;">
</div> <div class="text-center text-white">
----------------------------------------------------------------remove the buttons-----------------------------------------------------> <div id="spinner-animation" style="width: 200px; height: 200px;"></div>
<p class="mt-3" id="spinner-message">Waiting, don't leave this page...</p>
</div> </div>
</div> </div>
@ -22,8 +22,9 @@
<div class="card"> <div class="card">
<div class="card-header">Validation Form</div> <div class="card-header">Validation Form</div>
<div class="card-body"> <div class="card-body">
<form id="validationForm"> <form id="validationForm" method="POST" action="/api/validate">
{{ form.hidden_tag() }} {{ form.csrf_token }}
<div class="mb-3"> <div class="mb-3">
<small class="form-text text-muted"> <small class="form-text text-muted">
Select a package from the list below or enter a new one (e.g., <code>hl7.fhir.us.core</code>). Select a package from the list below or enter a new one (e.g., <code>hl7.fhir.us.core</code>).
@ -46,6 +47,7 @@
<div class="text-danger">{{ error }}</div> <div class="text-danger">{{ error }}</div>
{% endfor %} {% endfor %}
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label for="{{ form.version.id }}" class="form-label">Package Version</label> <label for="{{ form.version.id }}" class="form-label">Package Version</label>
{{ form.version(class="form-control", id=form.version.id) }} {{ form.version(class="form-control", id=form.version.id) }}
@ -53,145 +55,404 @@
<div class="text-danger">{{ error }}</div> <div class="text-danger">{{ error }}</div>
{% endfor %} {% endfor %}
</div> </div>
<hr class="my-4">
<!-- New Fields from ValidationForm -->
<div class="mb-3"> <div class="mb-3">
<label for="{{ form.include_dependencies.id }}" class="form-label">Include Dependencies</label> <label for="fhir_resource" class="form-label">FHIR Resource (JSON or XML)</label>
<div class="form-check"> {{ form.fhir_resource(class="form-control", rows=10, placeholder="Paste your FHIR JSON here...") }}
{{ form.include_dependencies(class="form-check-input") }} </div>
{{ form.include_dependencies.label(class="form-check-label") }}
<div class="row g-3">
<div class="col-md-6">
<label for="fhir_version" class="form-label">FHIR Version</label>
{{ form.fhir_version(class="form-select") }}
</div> </div>
</div> </div>
<div class="mb-3">
<label for="{{ form.mode.id }}" class="form-label">Validation Mode</label> <hr class="my-4">
{{ form.mode(class="form-select") }}
<h5>Validation Flags</h5>
<div class="row g-3">
<div class="col-md-6">
<div class="form-check">
{{ form.do_native(class="form-check-input") }}
<label class="form-check-label" for="do_native">{{ form.do_native.label }}</label>
</div>
<div class="form-check">
{{ form.hint_about_must_support(class="form-check-input") }}
<label class="form-check-label" for="hint_about_must_support">{{ form.hint_about_must_support.label }}</label>
</div>
<div class="form-check">
{{ form.assume_valid_rest_references(class="form-check-input") }}
<label class="form-check-label" for="assume_valid_rest_references">{{ form.assume_valid_rest_references.label }}</label>
</div>
<div class="form-check">
{{ form.no_extensible_binding_warnings(class="form-check-input") }}
<label class="form-check-label" for="no_extensible_binding_warnings">{{ form.no_extensible_binding_warnings.label }}</label>
</div>
</div>
<div class="col-md-6">
<div class="form-check">
{{ form.show_times(class="form-check-input") }}
<label class="form-check-label" for="show_times">{{ form.show_times.label }}</label>
</div>
<div class="form-check">
{{ form.allow_example_urls(class="form-check-input") }}
<label class="form-check-label" for="allow_example_urls">{{ form.allow_example_urls.label }}</label>
</div>
<div class="form-check">
{{ form.check_ips_codes(class="form-check-input") }}
<label class="form-check-label" for="check_ips_codes">{{ form.check_ips_codes.label }}</label>
</div>
<div class="form-check">
{{ form.allow_any_extensions(class="form-check-input") }}
<label class="form-check-label" for="allow_any_extensions">{{ form.allow_any_extensions.label }}</label>
</div>
<div class="form-check">
{{ form.tx_routing(class="form-check-input") }}
<label class="form-check-label" for="tx_routing">{{ form.tx_routing.label }}</label>
</div>
</div>
</div> </div>
<div class="mb-3">
<label for="{{ form.sample_input.id }}" class="form-label">Sample FHIR Resource/Bundle (JSON)</label> <hr class="my-4">
{{ form.sample_input(class="form-control", rows=10, placeholder="Paste your FHIR JSON here...") }}
{% for error in form.sample_input.errors %} <h5>Other Settings</h5>
<div class="text-danger">{{ error }}</div> <div class="row g-3">
{% endfor %} <div class="col-md-6">
<label for="profiles" class="form-label">{{ form.profiles.label }}</label>
{{ form.profiles(class="form-control") }}
<small class="form-text text-muted">Comma-separated URLs, e.g., url1,url2</small>
</div>
<div class="col-md-6">
<label for="extensions" class="form-label">{{ form.extensions.label }}</label>
{{ form.extensions(class="form-control") }}
<small class="form-text text-muted">Comma-separated URLs, e.g., url1,url2</small>
</div>
<div class="col-md-6">
<label for="terminology_server" class="form-label">{{ form.terminology_server.label }}</label>
{{ form.terminology_server(class="form-control") }}
<small class="form-text text-muted">e.g., http://tx.fhir.org</small>
</div>
<div class="col-md-6">
<label for="snomed_ct_version" class="form-label">{{ form.snomed_ct_version.label }}</label>
{{ form.snomed_ct_version(class="form-select") }}
<small class="form-text text-muted">Select a SNOMED CT edition.</small>
</div>
</div>
<div class="mt-4 text-center">
<button type="submit" id="validateButton" class="btn btn-primary btn-lg">
Validate Resource
</button>
<button type="button" id="clearResultsButton" class="btn btn-secondary btn-lg d-none">
Clear Results
</button>
</div> </div>
<button type="button" class="btn btn-primary" id="validateButton">Validate</button>
</form> </form>
</div> </div>
</div> </div>
<div class="card mt-4" id="validationResult" style="display: none;"> <div id="validationResult" class="card mt-4" style="display: none;">
<div class="card-header">Validation Report</div> <div class="card-header">Validation Results</div>
<div class="card-body" id="validationContent"> <div class="card-body" id="validationContent">
<!-- Results will be populated dynamically --> <!-- Results will be displayed here -->
</div> </div>
</div> </div>
</div> </div>
<script src="{{ url_for('static', filename='js/lottie.min.js') }}"></script>
<script> <script>
document.addEventListener('DOMContentLoaded', function() { // --- Global Variables and Helper Functions ---
const packageSelect = document.getElementById('packageSelect'); let currentLottieAnimation = null;
const packageNameInput = document.getElementById('{{ form.package_name.id }}'); let pollInterval = null;
const versionInput = document.getElementById('{{ form.version.id }}'); const spinnerOverlay = document.getElementById('spinner-overlay');
const validateButton = document.getElementById('validateButton'); const spinnerMessage = document.getElementById('spinner-message');
const sampleInput = document.getElementById('{{ form.sample_input.id }}'); const spinnerContainer = document.getElementById('spinner-animation');
const validationResult = document.getElementById('validationResult'); const perPage = 10;
const validationContent = document.getElementById('validationContent');
// Function to load the Lottie animation based on theme
// Debug DOM elements function loadLottieAnimation(theme) {
console.log('Package Select:', packageSelect ? 'Found' : 'Missing'); if (currentLottieAnimation) {
console.log('Package Name Input:', packageNameInput ? 'Found' : 'Missing'); currentLottieAnimation.destroy();
console.log('Version Input:', versionInput ? 'Found' : 'Missing'); currentLottieAnimation = null;
// Update package_name and version fields from dropdown
function updatePackageFields(select) {
const value = select.value;
console.log('Selected package:', value);
if (!packageNameInput || !versionInput) {
console.error('Input fields not found');
return;
} }
if (value) { const animationPath = (theme === 'dark')
const [name, version] = value.split('#'); ? '{{ url_for('static', filename='animations/Validation abstract.json') }}'
if (name && version) { : '{{ url_for('static', filename='animations/Validation abstract.json') }}';
packageNameInput.value = name;
versionInput.value = version; try {
console.log('Updated fields:', { packageName: name, version }); currentLottieAnimation = lottie.loadAnimation({
container: spinnerContainer,
renderer: 'svg',
loop: true,
autoplay: true,
path: animationPath
});
} catch(lottieError) {
console.error("Error loading Lottie animation:", lottieError);
}
}
// Function to show/hide the spinner with a custom message
function toggleSpinner(show, message = "Running validator...") {
if (show) {
spinnerMessage.textContent = message;
spinnerOverlay.classList.remove('d-none');
const currentTheme = document.documentElement.getAttribute('data-theme') === 'dark' ? 'dark' : 'light';
loadLottieAnimation(currentTheme);
} else {
spinnerOverlay.classList.add('d-none');
if (currentLottieAnimation) {
currentLottieAnimation.destroy();
currentLottieAnimation = null;
}
}
}
// --- Frontend logic starts here
document.addEventListener('DOMContentLoaded', function() {
const form = document.getElementById('validationForm');
const validateButton = document.getElementById('validateButton');
const clearResultsButton = document.getElementById('clearResultsButton');
const validationResult = document.getElementById('validationResult');
const validationContent = document.getElementById('validationContent');
const csrfToken = document.querySelector('input[name="csrf_token"]').value;
const packageSelect = document.getElementById('packageSelect');
const packageNameInput = document.getElementById('{{ form.package_name.id }}');
const versionInput = document.getElementById('{{ form.version.id }}');
const fhirResourceInput = document.getElementById('fhir_resource');
// Function to update package fields when a package is selected from the dropdown
function updatePackageFields(select) {
const value = select.value;
if (!packageNameInput || !versionInput) return;
if (value) {
const [name, version] = value.split('#');
if (name && version) {
packageNameInput.value = name;
versionInput.value = version;
} else {
packageNameInput.value = '';
versionInput.value = '';
}
} else { } else {
console.warn('Invalid package format:', value);
packageNameInput.value = ''; packageNameInput.value = '';
versionInput.value = ''; versionInput.value = '';
} }
} else {
packageNameInput.value = '';
versionInput.value = '';
console.log('Cleared fields: no package selected');
} }
}
// Sanitize text to prevent XSS
// Initialize dropdown based on form values function sanitizeText(text) {
if (packageNameInput && versionInput && packageNameInput.value && versionInput.value) { const div = document.createElement('div');
const currentValue = `${packageNameInput.value}#${versionInput.value}`; div.textContent = text;
if (packageSelect.querySelector(`option[value="${currentValue}"]`)) { return div.innerHTML;
packageSelect.value = currentValue;
console.log('Initialized dropdown with:', currentValue);
} }
}
// Debug dropdown options // Tidy up the display by paginating results
console.log('Dropdown options:', Array.from(packageSelect.options).map(opt => opt.value)); function renderResultList(container, items, title, isWarnings = false) {
container.innerHTML = '';
// Attach event listener for package selection if (items.length === 0) {
if (packageSelect) { return;
packageSelect.addEventListener('change', function() {
updatePackageFields(this);
});
}
// Client-side JSON validation preview
if (sampleInput) {
sampleInput.addEventListener('input', function() {
try {
if (this.value.trim()) {
JSON.parse(this.value);
this.classList.remove('is-invalid');
this.classList.add('is-valid');
} else {
this.classList.remove('is-valid', 'is-invalid');
}
} catch (e) {
this.classList.remove('is-valid');
this.classList.add('is-invalid');
} }
});
}
// Check HAPI server status const wrapper = document.createElement('div');
function checkHapiStatus() { const listId = `list-${title.toLowerCase()}`;
return fetch('/fhir/metadata', { method: 'GET' })
.then(response => { let initialItems = items.slice(0, perPage);
if (!response.ok) { let remainingItems = items.slice(perPage);
throw new Error('HAPI server unavailable');
} const titleElement = document.createElement('h5');
return true; titleElement.textContent = title;
}) wrapper.appendChild(titleElement);
.catch(error => {
console.warn('HAPI status check failed:', error.message); const ul = document.createElement('ul');
validationContent.innerHTML = '<div class="alert alert-warning">HAPI server unavailable; using local validation.</div>'; ul.className = 'list-unstyled';
return false; initialItems.forEach(item => {
const li = document.createElement('li');
li.innerHTML = `
<div class="alert alert-${isWarnings ? 'warning' : 'danger'}">
${sanitizeText(typeof item === 'string' ? item : item.message || item.issue)}
${isWarnings && (item.issue && item.issue.includes('Must Support')) ? ' <i class="bi bi-info-circle" title="This element is marked as Must Support in the profile."></i>' : ''}
</div>
`;
ul.appendChild(li);
}); });
} wrapper.appendChild(ul);
// Sanitize text to prevent XSS if (remainingItems.length > 0) {
function sanitizeText(text) { const toggleButton = document.createElement('a');
const div = document.createElement('div'); toggleButton.href = '#';
div.textContent = text; toggleButton.className = 'btn btn-sm btn-link text-decoration-none';
return div.innerHTML; toggleButton.textContent = `Show More (${remainingItems.length} more)`;
}
const remainingUl = document.createElement('ul');
remainingUl.className = 'list-unstyled d-none';
remainingItems.forEach(item => {
const li = document.createElement('li');
li.innerHTML = `
<div class="alert alert-${isWarnings ? 'warning' : 'danger'}">
${sanitizeText(typeof item === 'string' ? item : item.message || item.issue)}
${isWarnings && (item.issue && item.issue.includes('Must Support')) ? ' <i class="bi bi-info-circle" title="This element is marked as Must Support in the profile."></i>' : ''}
</div>
`;
remainingUl.appendChild(li);
});
// Validate button handler toggleButton.addEventListener('click', (e) => {
if (validateButton) { e.preventDefault();
validateButton.addEventListener('click', function() { if (remainingUl.classList.contains('d-none')) {
const packageName = packageNameInput.value; remainingUl.classList.remove('d-none');
const version = versionInput.value; toggleButton.textContent = 'Show Less';
const sampleData = sampleInput.value.trim(); } else {
const includeDependencies = document.getElementById('{{ form.include_dependencies.id }}').checked; remainingUl.classList.add('d-none');
toggleButton.textContent = `Show More (${remainingItems.length} more)`;
}
});
wrapper.appendChild(toggleButton);
wrapper.appendChild(remainingUl);
}
container.appendChild(wrapper);
}
function displayFinalReport(data) {
validationContent.innerHTML = '';
let hasContent = false;
clearResultsButton.classList.remove('d-none');
if (data.file_paths) {
// Use the paths returned from the server to create download links
const jsonFileName = data.file_paths.json.split('/').pop();
const htmlFileName = data.file_paths.html.split('/').pop();
const downloadDiv = document.createElement('div');
downloadDiv.className = 'alert alert-info d-flex justify-content-between align-items-center';
downloadDiv.innerHTML = `
<span>Download Raw Reports:</span>
<div>
<a href="/download/${jsonFileName}" class="btn btn-sm btn-secondary me-2" download>Download JSON</a>
<a href="/download/${htmlFileName}" class="btn btn-sm btn-secondary" download>Download HTML</a>
</div>
`;
validationContent.appendChild(downloadDiv);
hasContent = true;
}
if (data.errors && data.errors.length > 0) {
hasContent = true;
const errorContainer = document.createElement('div');
renderResultList(errorContainer, data.errors, 'Errors');
validationContent.appendChild(errorContainer);
}
if (data.warnings && data.warnings.length > 0) {
hasContent = true;
const warningContainer = document.createElement('div');
renderResultList(warningContainer, data.warnings, 'Warnings', true);
validationContent.appendChild(warningContainer);
}
if (data.results) {
hasContent = true;
const resultsDiv = document.createElement('div');
resultsDiv.innerHTML = '<h5>Detailed Results</h5>';
let index = 0;
for (const [resourceId, result] of Object.entries(data.results)) {
const collapseId = `result-collapse-${index++}`;
resultsDiv.innerHTML += `
<div class="card mb-2">
<div class="card-header" role="button" data-bs-toggle="collapse" data-bs-target="#${collapseId}" aria-expanded="false" aria-controls="${collapseId}">
<h6 class="mb-0">${sanitizeText(resourceId)}</h6>
<p class="mb-0"><strong>Valid:</strong> ${result.valid ? 'Yes' : 'No'}</p>
</div>
<div id="${collapseId}" class="collapse">
<div class="card-body">
${result.details && result.details.length > 0 ? `
<ul>
${result.details.map(d => `
<li class="${d.severity === 'error' ? 'text-danger' : d.severity === 'warning' ? 'text-warning' : 'text-info'}">
${sanitizeText(d.issue)}
${d.description && d.description !== d.issue ? `<br><small class="text-muted">${sanitizeText(d.description)}</small>` : ''}
</li>
`).join('')}
</ul>
` : '<p>No additional details.</p>'}
</div>
</div>
</div>
`;
}
validationContent.appendChild(resultsDiv);
}
if (data.summary) {
const summaryDiv = document.createElement('div');
summaryDiv.className = 'alert alert-info';
summaryDiv.innerHTML = `
<h5>Summary</h5>
<p>Valid: ${data.valid ? 'Yes' : 'No'}</p>
<p>Errors: ${data.summary.error_count || 0}, Warnings: ${data.summary.warning_count || 0}</p>
`;
validationContent.appendChild(summaryDiv);
hasContent = true;
}
if (!hasContent && data.valid) {
validationContent.innerHTML = '<div class="alert alert-success">Validation passed with no errors or warnings.</div>';
}
document.querySelectorAll('[data-bs-toggle="collapse"]').forEach(el => {
el.addEventListener('click', () => {
const target = document.querySelector(el.getAttribute('data-bs-target'));
target.classList.toggle('show');
});
});
document.querySelectorAll('[data-bs-toggle="tooltip"]').forEach(el => {
new bootstrap.Tooltip(el);
});
}
function checkValidationStatus(taskId) {
fetch(`/api/check-validation-status/${taskId}`)
.then(response => {
if (response.status === 202) {
console.log('Validation still in progress...');
return null;
} else if (response.status === 200) {
clearInterval(pollInterval);
toggleSpinner(false);
return response.json();
} else {
clearInterval(pollInterval);
toggleSpinner(false);
return response.json().then(errorData => {
throw new Error(errorData.message);
});
}
})
.then(data => {
if (data && data.status === 'complete') {
validationResult.style.display = 'block';
displayFinalReport(data.result);
}
})
.catch(error => {
clearInterval(pollInterval);
toggleSpinner(false);
console.error('Validation failed during polling:', error);
validationResult.style.display = 'block';
validationContent.innerHTML = `<div class="alert alert-danger">An unexpected error occurred during validation: ${sanitizeText(error.message)}</div>`;
});
}
function runBlockingValidation(e) {
e.preventDefault(); // This is the crucial line to prevent default form submission
const packageName = document.getElementById('{{ form.package_name.id }}').value;
const version = document.getElementById('{{ form.version.id }}').value;
const sampleData = document.getElementById('fhir_resource').value.trim();
const csrfToken = document.querySelector('input[name="csrf_token"]').value;
if (!packageName || !version) { if (!packageName || !version) {
validationResult.style.display = 'block'; validationResult.style.display = 'block';
@ -201,176 +462,126 @@ document.addEventListener('DOMContentLoaded', function() {
if (!sampleData) { if (!sampleData) {
validationResult.style.display = 'block'; validationResult.style.display = 'block';
validationContent.innerHTML = '<div class="alert alert-danger">Please provide FHIR sample JSON.</div>'; validationContent.innerHTML = '<div class="alert alert-danger">Please provide FHIR sample JSON or XML.</div>';
return; return;
} }
toggleSpinner(true, "Running FHIR Validator (This may take up to 5 minutes on the first run).");
// Collect all form data including the new fields
const data = {
package_name: packageName,
version: version,
fhir_resource: sampleData,
fhir_version: document.getElementById('fhir_version').value,
do_native: document.getElementById('do_native').checked,
hint_about_must_support: document.getElementById('hint_about_must_support').checked,
assume_valid_rest_references: document.getElementById('assume_valid_rest_references').checked,
no_extensible_binding_warnings: document.getElementById('no_extensible_binding_warnings').checked,
show_times: document.getElementById('show_times').checked,
allow_example_urls: document.getElementById('allow_example_urls').checked,
check_ips_codes: document.getElementById('check_ips_codes').checked,
allow_any_extensions: document.getElementById('allow_any_extensions').checked,
tx_routing: document.getElementById('tx_routing').checked,
snomed_ct_version: document.getElementById('snomed_ct_version').value,
profiles: document.getElementById('profiles').value,
extensions: document.getElementById('extensions').value,
terminology_server: document.getElementById('terminology_server').value
};
try { fetch('/api/validate', {
JSON.parse(sampleData); method: 'POST',
} catch (e) { headers: {
validationResult.style.display = 'block'; 'Content-Type': 'application/json',
validationContent.innerHTML = '<div class="alert alert-danger">Invalid JSON: ' + sanitizeText(e.message) + '</div>'; 'X-CSRFToken': csrfToken
return; },
} body: JSON.stringify(data)
})
validationResult.style.display = 'block'; .then(response => {
validationContent.innerHTML = '<div class="text-center py-3"><div class="spinner-border text-primary" role="status"><span class="visually-hidden">Validating...</span></div></div>'; if (response.status === 202) {
// Check HAPI status before validation
checkHapiStatus().then(() => {
console.log('Sending request to /api/validate-sample with:', {
package_name: packageName,
version: version,
include_dependencies: includeDependencies,
sample_data_length: sampleData.length
});
fetch('/api/validate-sample', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': '{{ form.csrf_token._value() }}'
},
body: JSON.stringify({
package_name: packageName,
version: version,
include_dependencies: includeDependencies,
sample_data: sampleData
})
})
.then(response => {
console.log('Server response status:', response.status, response.statusText);
if (!response.ok) {
return response.text().then(text => {
console.error('Server response text:', text.substring(0, 200) + (text.length > 200 ? '...' : ''));
throw new Error(`HTTP error ${response.status}: ${text.substring(0, 100)}...`);
});
}
return response.json(); return response.json();
}) } else {
.then(data => { toggleSpinner(false);
console.log('Validation response:', data); return response.json().then(errorData => {
validationContent.innerHTML = ''; throw new Error(errorData.message);
let hasContent = false;
// Display profile information
if (data.results) {
const profileDiv = document.createElement('div');
profileDiv.className = 'mb-3';
const profiles = new Set();
Object.values(data.results).forEach(result => {
if (result.profile) profiles.add(result.profile);
});
if (profiles.size > 0) {
profileDiv.innerHTML = `
<h5>Validated Against Profile${profiles.size > 1 ? 's' : ''}</h5>
<ul>
${Array.from(profiles).map(p => `<li><a href="${sanitizeText(p)}" target="_blank">${sanitizeText(p.split('/').pop())}</a></li>`).join('')}
</ul>
`;
validationContent.appendChild(profileDiv);
hasContent = true;
}
}
// Display errors
if (data.errors && data.errors.length > 0) {
hasContent = true;
const errorDiv = document.createElement('div');
errorDiv.className = 'alert alert-danger';
errorDiv.innerHTML = `
<h5>Errors</h5>
<ul>
${data.errors.map(e => `<li>${sanitizeText(typeof e === 'string' ? e : e.message)}</li>`).join('')}
</ul>
`;
validationContent.appendChild(errorDiv);
}
// Display warnings
if (data.warnings && data.warnings.length > 0) {
hasContent = true;
const warningDiv = document.createElement('div');
warningDiv.className = 'alert alert-warning';
warningDiv.innerHTML = `
<h5>Warnings</h5>
<ul>
${data.warnings.map(w => {
const isMustSupport = w.includes('Must Support');
return `<li>${sanitizeText(typeof w === 'string' ? w : w.message)}${isMustSupport ? ' <i class="bi bi-info-circle" title="This element is marked as Must Support in the profile."></i>' : ''}</li>`;
}).join('')}
</ul>
`;
validationContent.appendChild(warningDiv);
}
// Detailed results with collapsible sections
if (data.results) {
hasContent = true;
const resultsDiv = document.createElement('div');
resultsDiv.innerHTML = '<h5>Detailed Results</h5>';
let index = 0;
for (const [resourceId, result] of Object.entries(data.results)) {
const collapseId = `result-collapse-${index++}`;
resultsDiv.innerHTML += `
<div class="card mb-2">
<div class="card-header" role="button" data-bs-toggle="collapse" data-bs-target="#${collapseId}" aria-expanded="false" aria-controls="${collapseId}">
<h6 class="mb-0">${sanitizeText(resourceId)}</h6>
<p class="mb-0"><strong>Valid:</strong> ${result.valid ? 'Yes' : 'No'}</p>
</div>
<div id="${collapseId}" class="collapse">
<div class="card-body">
${result.details && result.details.length > 0 ? `
<ul>
${result.details.map(d => `
<li class="${d.severity === 'error' ? 'text-danger' : d.severity === 'warning' ? 'text-warning' : 'text-info'}">
${sanitizeText(d.issue)}
${d.description && d.description !== d.issue ? `<br><small class="text-muted">${sanitizeText(d.description)}</small>` : ''}
</li>
`).join('')}
</ul>
` : '<p>No additional details.</p>'}
</div>
</div>
</div>
`;
}
validationContent.appendChild(resultsDiv);
}
// Summary
const summaryDiv = document.createElement('div');
summaryDiv.className = 'alert alert-info';
summaryDiv.innerHTML = `
<h5>Summary</h5>
<p>Valid: ${data.valid ? 'Yes' : 'No'}</p>
<p>Errors: ${data.summary.error_count || 0}, Warnings: ${data.summary.warning_count || 0}</p>
`;
validationContent.appendChild(summaryDiv);
hasContent = true;
if (!hasContent && data.valid) {
validationContent.innerHTML = '<div class="alert alert-success">Validation passed with no errors or warnings.</div>';
}
// Initialize Bootstrap collapse and tooltips
document.querySelectorAll('[data-bs-toggle="collapse"]').forEach(el => {
el.addEventListener('click', () => {
const target = document.querySelector(el.getAttribute('data-bs-target'));
target.classList.toggle('show');
});
}); });
document.querySelectorAll('[data-bs-toggle="tooltip"]').forEach(el => { }
new bootstrap.Tooltip(el); })
}); .then(data => {
}) if (data.status === 'accepted' && data.task_id) {
.catch(error => { pollInterval = setInterval(() => checkValidationStatus(data.task_id), 3000);
console.error('Validation error:', error); } else {
validationContent.innerHTML = `<div class="alert alert-danger">Error during validation: ${sanitizeText(error.message)}</div>`; toggleSpinner(false);
}); validationResult.style.display = 'block';
validationContent.innerHTML = `<div class="alert alert-danger">Server response format unexpected.</div>`;
}
})
.catch(error => {
clearInterval(pollInterval);
toggleSpinner(false);
console.error('Validation failed:', error);
validationResult.style.display = 'block';
validationContent.innerHTML = `<div class="alert alert-danger">An unexpected error occurred during validation: ${sanitizeText(error.message)}</div>`;
});
};
clearResultsButton.addEventListener('click', function() {
fetch('/api/clear-validation-results', {
method: 'POST',
headers: {
'X-CSRFToken': csrfToken
}
})
.then(response => response.json())
.then(data => {
if (data.status === 'success') {
validationResult.style.display = 'none';
clearResultsButton.classList.add('d-none');
} else {
console.error('Failed to clear results:', data.message);
}
})
.catch(error => {
console.error('Network error while clearing results:', error);
}); });
}); });
}
}); if (packageSelect) {
packageSelect.addEventListener('change', function() {
updatePackageFields(this);
});
}
if (validateButton) {
validateButton.addEventListener('click', function(e) {
runBlockingValidation(e);
});
}
if (packageNameInput.value && versionInput.value) {
const currentValue = `${packageNameInput.value}#${versionInput.value}`;
const optionExists = Array.from(packageSelect.options).some(opt => opt.value === currentValue);
if (optionExists) {
packageSelect.value = currentValue;
}
}
if (fhirResourceInput) {
fhirResourceInput.addEventListener('input', function() {
try {
if (this.value.trim()) {
JSON.parse(this.value);
this.classList.remove('is-invalid');
this.classList.add('is-valid');
} else {
this.classList.remove('is-valid', 'is-invalid');
}
} catch (e) {
this.classList.remove('is-valid');
this.classList.add('is-invalid');
}
});
}
});
</script> </script>
{% endblock %} {% endblock %}

View File

@ -6,6 +6,8 @@ import tarfile
import shutil import shutil
import io import io
import requests import requests
import time
import subprocess
from unittest.mock import patch, MagicMock, mock_open, call from unittest.mock import patch, MagicMock, mock_open, call
from flask import Flask, session from flask import Flask, session
from flask.testing import FlaskClient from flask.testing import FlaskClient
@ -27,9 +29,252 @@ def parse_ndjson(byte_stream):
lines = decoded_stream.split('\n') lines = decoded_stream.split('\n')
return [json.loads(line) for line in lines if line.strip()] return [json.loads(line) for line in lines if line.strip()]
class DockerComposeContainer:
"""
A class that follows the Testcontainers pattern for managing Docker Compose environments.
This implementation uses subprocess to call docker-compose directly since we're not
installing the testcontainers-python package.
"""
def __init__(self, compose_file_path):
"""
Initialize with the path to the docker-compose.yml file
Args:
compose_file_path: Path to the docker-compose.yml file
"""
self.compose_file = compose_file_path
self.compose_dir = os.path.dirname(os.path.abspath(compose_file_path))
self.containers_up = False
self.service_ports = {}
self._container_ids = {}
def __enter__(self):
"""Start containers when entering context"""
self.start()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
"""Stop containers when exiting context"""
self.stop()
def with_service_port(self, service_name, port):
"""
Map a service port (following the testcontainers builder pattern)
Args:
service_name: Name of the service in docker-compose.yml
port: Port number to expose
Returns:
self for chaining
"""
self.service_ports[service_name] = port
return self
def start(self):
"""Start the Docker Compose environment"""
if self.containers_up:
return self
print("Starting Docker Compose environment...")
result = subprocess.run(
['docker-compose', '-f', self.compose_file, 'up', '-d'],
cwd=self.compose_dir,
capture_output=True,
text=True
)
if result.returncode != 0:
error_msg = f"Failed to start Docker Compose environment: {result.stderr}"
print(error_msg)
raise RuntimeError(error_msg)
# Store container IDs for later use
self._get_container_ids()
self.containers_up = True
self._wait_for_services()
return self
def _get_container_ids(self):
"""Get the container IDs for all services"""
result = subprocess.run(
['docker-compose', '-f', self.compose_file, 'ps', '-q'],
cwd=self.compose_dir,
capture_output=True,
text=True
)
if result.returncode != 0:
return
container_ids = result.stdout.strip().split('\n')
if not container_ids:
return
# Get service names for each container
for container_id in container_ids:
if not container_id:
continue
inspect_result = subprocess.run(
['docker', 'inspect', '--format', '{{index .Config.Labels "com.docker.compose.service"}}', container_id],
capture_output=True,
text=True
)
if inspect_result.returncode == 0:
service_name = inspect_result.stdout.strip()
self._container_ids[service_name] = container_id
def get_container_id(self, service_name):
"""
Get the container ID for a specific service
Args:
service_name: Name of the service in docker-compose.yml
Returns:
Container ID as string or None if not found
"""
return self._container_ids.get(service_name)
def get_service_host(self, service_name):
"""
Get the host for a specific service - for Docker Compose we just use localhost
Args:
service_name: Name of the service in docker-compose.yml
Returns:
Host as string (usually localhost)
"""
return "localhost"
def get_service_url(self, service_name, path=""):
"""
Get the URL for a specific service
Args:
service_name: Name of the service in docker-compose.yml
path: Optional path to append to the URL
Returns:
URL as string
"""
port = self.service_ports.get(service_name)
if not port:
raise ValueError(f"No port mapping defined for service {service_name}")
url = f"http://{self.get_service_host(service_name)}:{port}"
if path:
# Ensure path starts with /
if not path.startswith('/'):
path = f"/{path}"
url = f"{url}{path}"
return url
def get_logs(self, service_name):
"""
Get logs for a specific service
Args:
service_name: Name of the service in docker-compose.yml
Returns:
Logs as string
"""
container_id = self.get_container_id(service_name)
if not container_id:
return f"No container found for service {service_name}"
result = subprocess.run(
['docker', 'logs', container_id],
capture_output=True,
text=True
)
return result.stdout
def stop(self):
"""Stop the Docker Compose environment"""
if not self.containers_up:
return
print("Stopping Docker Compose environment...")
result = subprocess.run(
['docker-compose', '-f', self.compose_file, 'down'],
cwd=self.compose_dir,
capture_output=True,
text=True
)
if result.returncode != 0:
print(f"Warning: Error stopping Docker Compose: {result.stderr}")
self.containers_up = False
def _wait_for_services(self):
"""Wait for all services to be ready"""
print("Waiting for services to be ready...")
# Wait for HAPI FHIR server
if 'fhir' in self.service_ports:
self._wait_for_http_service(
self.get_service_url('fhir', 'fhir/metadata'),
"HAPI FHIR server"
)
# Wait for FHIRFLARE application
if 'fhirflare' in self.service_ports:
self._wait_for_http_service(
self.get_service_url('fhirflare'),
"FHIRFLARE application"
)
# Give additional time for services to stabilize
time.sleep(5)
def _wait_for_http_service(self, url, service_name, max_retries=30, retry_interval=2):
"""
Wait for an HTTP service to be ready
Args:
url: URL to check
service_name: Name of the service for logging
max_retries: Maximum number of retries
retry_interval: Interval between retries in seconds
"""
for attempt in range(max_retries):
try:
response = requests.get(url, timeout=5)
if response.status_code == 200:
print(f"{service_name} is ready after {attempt + 1} attempts")
return True
except requests.RequestException:
pass
print(f"Waiting for {service_name} (attempt {attempt + 1}/{max_retries})...")
time.sleep(retry_interval)
print(f"Warning: {service_name} did not become ready in time")
return False
class TestFHIRFlareIGToolkit(unittest.TestCase): class TestFHIRFlareIGToolkit(unittest.TestCase):
@classmethod @classmethod
def setUpClass(cls): def setUpClass(cls):
# Define the Docker Compose container
compose_file_path = os.path.join(os.path.dirname(__file__), 'docker-compose.yml')
cls.container = DockerComposeContainer(compose_file_path) \
.with_service_port('fhir', 8080) \
.with_service_port('fhirflare', 5000)
# Start the containers
cls.container.start()
# Configure app for testing
app.config['TESTING'] = True app.config['TESTING'] = True
app.config['WTF_CSRF_ENABLED'] = False app.config['WTF_CSRF_ENABLED'] = False
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///:memory:' app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///:memory:'
@ -39,6 +284,7 @@ class TestFHIRFlareIGToolkit(unittest.TestCase):
app.config['API_KEY'] = 'test-api-key' app.config['API_KEY'] = 'test-api-key'
app.config['VALIDATE_IMPOSED_PROFILES'] = True app.config['VALIDATE_IMPOSED_PROFILES'] = True
app.config['DISPLAY_PROFILE_RELATIONSHIPS'] = True app.config['DISPLAY_PROFILE_RELATIONSHIPS'] = True
app.config['HAPI_FHIR_URL'] = cls.container.get_service_url('fhir', 'fhir') # Point to containerized HAPI FHIR
cls.app_context = app.app_context() cls.app_context = app.app_context()
cls.app_context.push() cls.app_context.push()
@ -50,6 +296,9 @@ class TestFHIRFlareIGToolkit(unittest.TestCase):
cls.app_context.pop() cls.app_context.pop()
if os.path.exists(cls.test_packages_dir): if os.path.exists(cls.test_packages_dir):
shutil.rmtree(cls.test_packages_dir) shutil.rmtree(cls.test_packages_dir)
# Stop Docker Compose environment
cls.container.stop()
def setUp(self): def setUp(self):
if os.path.exists(self.test_packages_dir): if os.path.exists(self.test_packages_dir):
@ -96,321 +345,63 @@ class TestFHIRFlareIGToolkit(unittest.TestCase):
with patch('fhirpath.evaluate', side_effect=Exception("fhirpath error")): with patch('fhirpath.evaluate', side_effect=Exception("fhirpath error")):
self.assertEqual(services.navigate_fhir_path(resource, "Patient.name[0].given"), ["John"]) self.assertEqual(services.navigate_fhir_path(resource, "Patient.name[0].given"), ["John"])
def test_02_render_node_as_li(self):
node = {
"element": {"path": "Patient.identifier", "id": "Patient.identifier", "sliceName": "us-ssn", "min": 0, "max": "*", "type": [{"code": "Identifier"}]},
"name": "identifier",
"children": {}
}
must_support_paths = {"Patient.identifier:us-ssn"}
with app.app_context:
html = render_template('cp_view_processed_ig.html', processed_ig=MagicMock(must_support_elements={"USCorePatientProfile": ["Patient.identifier:us-ssn"]}), profile_list=[{"name": "USCorePatientProfile"}], base_list=[])
self.assertIn("identifier:us-ssn", html)
self.assertIn("list-group-item-warning", html)
self.assertIn("Must Support (Slice: us-ssn)", html)
# --- Basic Page Rendering Tests --- # --- Basic Page Rendering Tests ---
def test_03_homepage(self): def test_03_homepage(self):
response = self.client.get('/') # Connect to the containerized application
response = requests.get(self.container.get_service_url('fhirflare'))
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertIn(b'FHIRFLARE IG Toolkit', response.data) self.assertIn('FHIRFLARE IG Toolkit', response.text)
def test_04_import_ig_page(self): def test_04_import_ig_page(self):
response = self.client.get('/import-ig') response = requests.get(self.container.get_service_url('fhirflare', 'import-ig'))
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertIn(b'Import IG', response.data) self.assertIn('Import IG', response.text)
self.assertIn(b'Package Name', response.data) self.assertIn('Package Name', response.text)
self.assertIn(b'Package Version', response.data) self.assertIn('Package Version', response.text)
self.assertIn(b'name="dependency_mode"', response.data) self.assertIn('name="dependency_mode"', response.text)
@patch('app.list_downloaded_packages', return_value=([], [], {})) # --- API Integration Tests ---
def test_05_view_igs_no_packages(self, mock_list_pkgs):
response = self.client.get('/view-igs')
self.assertEqual(response.status_code, 200)
self.assertNotIn(b'<th>Package Name</th>', response.data)
self.assertIn(b'No packages downloaded yet.', response.data)
mock_list_pkgs.assert_called_once()
def test_06_view_igs_with_packages(self): def test_30_load_ig_to_hapi_integration(self):
self.create_mock_tgz('hl7.fhir.us.core-6.1.0.tgz', {'package/package.json': {'name': 'hl7.fhir.us.core', 'version': '6.1.0'}}) """Test loading an IG to the containerized HAPI FHIR server"""
response = self.client.get('/view-igs')
self.assertEqual(response.status_code, 200)
self.assertIn(b'hl7.fhir.us.core', response.data)
self.assertIn(b'6.1.0', response.data)
self.assertIn(b'<th>Package Name</th>', response.data)
@patch('app.render_template')
def test_07_push_igs_page(self, mock_render_template):
mock_render_template.return_value = "Mock Render"
response = self.client.get('/push-igs')
self.assertEqual(response.status_code, 200)
mock_render_template.assert_called()
call_args, call_kwargs = mock_render_template.call_args
self.assertEqual(call_args[0], 'cp_push_igs.html')
# --- UI Form Tests ---
@patch('app.services.import_package_and_dependencies')
def test_10_import_ig_form_success(self, mock_import):
mock_import.return_value = {'requested': ('hl7.fhir.us.core', '6.1.0'), 'processed': {('hl7.fhir.us.core', '6.1.0')}, 'downloaded': {('hl7.fhir.us.core', '6.1.0'): 'path/pkg.tgz'}, 'all_dependencies': {}, 'dependencies': [], 'errors': []}
response = self.client.post('/import-ig', data={'package_name': 'hl7.fhir.us.core', 'package_version': '6.1.0', 'dependency_mode': 'recursive'}, follow_redirects=True)
self.assertEqual(response.status_code, 200)
self.assertIn(b'Successfully downloaded hl7.fhir.us.core#6.1.0 and dependencies! Mode: recursive', response.data)
mock_import.assert_called_once_with('hl7.fhir.us.core', '6.1.0', dependency_mode='recursive')
@patch('app.services.import_package_and_dependencies')
def test_11_import_ig_form_failure_404(self, mock_import):
mock_import.return_value = {'requested': ('invalid.package', '1.0.0'), 'processed': set(), 'downloaded': {}, 'all_dependencies': {}, 'dependencies': [], 'errors': ['HTTP error fetching package: 404 Client Error: Not Found for url: ...']}
response = self.client.post('/import-ig', data={'package_name': 'invalid.package', 'package_version': '1.0.0', 'dependency_mode': 'recursive'}, follow_redirects=False)
self.assertEqual(response.status_code, 200)
self.assertIn(b'Package not found on registry (404)', response.data)
@patch('app.services.import_package_and_dependencies')
def test_12_import_ig_form_failure_conn_error(self, mock_import):
mock_import.return_value = {'requested': ('conn.error.pkg', '1.0.0'), 'processed': set(), 'downloaded': {}, 'all_dependencies': {}, 'dependencies': [], 'errors': ['Connection error: Cannot connect to registry...']}
response = self.client.post('/import-ig', data={'package_name': 'conn.error.pkg', 'package_version': '1.0.0', 'dependency_mode': 'recursive'}, follow_redirects=False)
self.assertEqual(response.status_code, 200)
self.assertIn(b'Could not connect to the FHIR package registry', response.data)
def test_13_import_ig_form_invalid_input(self):
response = self.client.post('/import-ig', data={'package_name': 'invalid@package', 'package_version': '1.0.0', 'dependency_mode': 'recursive'}, follow_redirects=True)
self.assertEqual(response.status_code, 200)
self.assertIn(b'Error in Package Name: Invalid package name format.', response.data)
@patch('app.services.process_package_file')
@patch('app.services.parse_package_filename')
def test_20_process_ig_success(self, mock_parse, mock_process):
pkg_name = 'hl7.fhir.us.core'
pkg_version = '6.1.0'
filename = f'{pkg_name}-{pkg_version}.tgz'
mock_parse.return_value = (pkg_name, pkg_version)
mock_process.return_value = {
'resource_types_info': [{'name': 'Patient', 'type': 'Patient', 'is_profile': False, 'must_support': True, 'optional_usage': False}],
'must_support_elements': {'Patient': ['Patient.name', 'Patient.identifier:us-ssn']},
'examples': {'Patient': ['package/Patient-example.json']},
'complies_with_profiles': [],
'imposed_profiles': ['http://hl7.org/fhir/StructureDefinition/Patient'],
'errors': []
}
self.create_mock_tgz(filename, {'package/package.json': {'name': pkg_name, 'version': pkg_version}})
response = self.client.post('/process-igs', data={'filename': filename}, follow_redirects=False)
self.assertEqual(response.status_code, 302)
self.assertTrue(response.location.endswith('/view-igs'))
with self.client.session_transaction() as sess:
self.assertIn(('success', f'Successfully processed {pkg_name}#{pkg_version}!'), sess.get('_flashes', []))
mock_parse.assert_called_once_with(filename)
mock_process.assert_called_once_with(os.path.join(app.config['FHIR_PACKAGES_DIR'], filename))
processed_ig = db.session.query(ProcessedIg).filter_by(package_name=pkg_name, version=pkg_version).first()
self.assertIsNotNone(processed_ig)
self.assertEqual(processed_ig.package_name, pkg_name)
self.assertIn('Patient.name', processed_ig.must_support_elements.get('Patient', []))
def test_21_process_ig_file_not_found(self):
response = self.client.post('/process-igs', data={'filename': 'nonexistent.tgz'}, follow_redirects=True)
self.assertEqual(response.status_code, 200)
self.assertIn(b'Package file not found: nonexistent.tgz', response.data)
def test_22_delete_ig_success(self):
filename = 'hl7.fhir.us.core-6.1.0.tgz'
metadata_filename = 'hl7.fhir.us.core-6.1.0.metadata.json'
self.create_mock_tgz(filename, {'package/package.json': {'name': 'hl7.fhir.us.core', 'version': '6.1.0'}})
metadata_path = os.path.join(app.config['FHIR_PACKAGES_DIR'], metadata_filename)
open(metadata_path, 'w').write(json.dumps({'name': 'hl7.fhir.us.core'}))
self.assertTrue(os.path.exists(os.path.join(app.config['FHIR_PACKAGES_DIR'], filename)))
self.assertTrue(os.path.exists(metadata_path))
response = self.client.post('/delete-ig', data={'filename': filename}, follow_redirects=True)
self.assertEqual(response.status_code, 200)
self.assertIn(f'Deleted: {filename}, {metadata_filename}'.encode('utf-8'), response.data)
self.assertFalse(os.path.exists(os.path.join(app.config['FHIR_PACKAGES_DIR'], filename)))
self.assertFalse(os.path.exists(metadata_path))
def test_23_unload_ig_success(self):
processed_ig = ProcessedIg(package_name='test.pkg', version='1.0', processed_date=datetime.now(timezone.utc), resource_types_info=[], must_support_elements={}, examples={})
db.session.add(processed_ig)
db.session.commit()
ig_id = processed_ig.id
self.assertIsNotNone(db.session.get(ProcessedIg, ig_id))
response = self.client.post('/unload-ig', data={'ig_id': str(ig_id)}, follow_redirects=True)
self.assertEqual(response.status_code, 200)
self.assertIn(b'Unloaded processed data for test.pkg#1.0', response.data)
self.assertIsNone(db.session.get(ProcessedIg, ig_id))
# --- Phase 2 Tests ---
@patch('os.path.exists', return_value=True)
@patch('tarfile.open')
@patch('requests.put')
def test_30_load_ig_to_hapi_success(self, mock_requests_put, mock_tarfile_open, mock_os_exists):
pkg_name = 'hl7.fhir.us.core' pkg_name = 'hl7.fhir.us.core'
pkg_version = '6.1.0' pkg_version = '6.1.0'
filename = f'{pkg_name}-{pkg_version}.tgz' filename = f'{pkg_name}-{pkg_version}.tgz'
self.create_mock_tgz(filename, { self.create_mock_tgz(filename, {
'package/package.json': {'name': pkg_name, 'version': pkg_version}, 'package/package.json': {'name': pkg_name, 'version': pkg_version},
'package/Patient-profile.json': {'resourceType': 'StructureDefinition', 'id': 'us-core-patient'} 'package/StructureDefinition-us-core-patient.json': {
'resourceType': 'StructureDefinition',
'id': 'us-core-patient',
'url': 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient',
'name': 'USCorePatientProfile',
'type': 'Patient',
'status': 'active'
}
}) })
mock_tar = MagicMock()
profile_member = MagicMock(spec=tarfile.TarInfo) # Load IG to HAPI
profile_member.name = 'package/Patient-profile.json'
profile_member.isfile.return_value = True
mock_tar.getmembers.return_value = [profile_member]
mock_tar.extractfile.return_value = io.BytesIO(json.dumps({'resourceType': 'StructureDefinition', 'id': 'us-core-patient'}).encode('utf-8'))
mock_tarfile_open.return_value.__enter__.return_value = mock_tar
mock_requests_put.return_value = MagicMock(status_code=200)
response = self.client.post( response = self.client.post(
'/api/load-ig-to-hapi', '/api/load-ig-to-hapi',
data=json.dumps({'package_name': pkg_name, 'version': pkg_version}), data=json.dumps({'package_name': pkg_name, 'version': pkg_version}),
content_type='application/json' content_type='application/json',
headers={'X-API-Key': 'test-api-key'}
) )
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
data = json.loads(response.data) data = json.loads(response.data)
self.assertEqual(data['status'], 'success') self.assertEqual(data['status'], 'success')
mock_requests_put.assert_called_once_with(
'http://localhost:8080/fhir/StructureDefinition/us-core-patient', # Verify the resource was loaded by querying the HAPI FHIR server directly
json={'resourceType': 'StructureDefinition', 'id': 'us-core-patient'}, hapi_response = requests.get(self.container.get_service_url('fhir', 'fhir/StructureDefinition/us-core-patient'))
headers={'Content-Type': 'application/fhir+json'} self.assertEqual(hapi_response.status_code, 200)
) resource = hapi_response.json()
self.assertEqual(resource['resourceType'], 'StructureDefinition')
self.assertEqual(resource['id'], 'us-core-patient')
def test_31_load_ig_to_hapi_not_found(self): def test_31_validate_sample_with_hapi_integration(self):
response = self.client.post( """Test validating a sample against the containerized HAPI FHIR server"""
'/api/load-ig-to-hapi', # First, load the necessary StructureDefinition
data=json.dumps({'package_name': 'nonexistent', 'version': '1.0'}),
content_type='application/json'
)
self.assertEqual(response.status_code, 404)
data = json.loads(response.data)
self.assertEqual(data['error'], 'Package not found')
@patch('os.path.exists', return_value=True)
@patch('requests.post')
def test_32_api_validate_sample_hapi_success(self, mock_requests_post, mock_os_exists):
pkg_name = 'hl7.fhir.us.core'
pkg_version = '6.1.0'
sample_resource = {
'resourceType': 'Patient',
'id': 'valid1',
'meta': {'profile': ['http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient']},
'name': [{'given': ['John'], 'family': 'Doe'}]
}
mock_requests_post.return_value = MagicMock(
status_code=200,
json=lambda: {
'resourceType': 'OperationOutcome',
'issue': [{'severity': 'warning', 'diagnostics': 'Must Support element Patient.identifier missing'}]
}
)
response = self.client.post(
'/api/validate-sample',
data=json.dumps({
'package_name': pkg_name,
'version': pkg_version,
'sample_data': json.dumps(sample_resource),
'mode': 'single',
'include_dependencies': True
}),
content_type='application/json',
headers={'X-API-Key': 'test-api-key'}
)
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertTrue(data['valid'])
self.assertEqual(data['warnings'], ['Must Support element Patient.identifier missing'])
mock_requests_post.assert_called_once_with(
'http://localhost:8080/fhir/Patient/$validate?profile=http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient',
json=sample_resource,
headers={'Content-Type': 'application/fhir+json', 'Accept': 'application/fhir+json'},
timeout=10
)
@patch('os.path.exists', return_value=True)
@patch('requests.post', side_effect=requests.ConnectionError("HAPI down"))
@patch('services.navigate_fhir_path')
def test_33_api_validate_sample_hapi_fallback(self, mock_navigate_fhir_path, mock_requests_post, mock_os_exists):
pkg_name = 'hl7.fhir.us.core'
pkg_version = '6.1.0'
sample_resource = {
'resourceType': 'Patient',
'id': 'valid1',
'meta': {'profile': ['http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient']}
}
mock_navigate_fhir_path.return_value = None
self.create_mock_tgz(f'{pkg_name}-{pkg_version}.tgz', {
'package/package.json': {'name': pkg_name, 'version': pkg_version},
'package/StructureDefinition-us-core-patient.json': {
'resourceType': 'StructureDefinition',
'snapshot': {'element': [{'path': 'Patient.name', 'min': 1}, {'path': 'Patient.identifier', 'mustSupport': True}]}
}
})
response = self.client.post(
'/api/validate-sample',
data=json.dumps({
'package_name': pkg_name,
'version': pkg_version,
'sample_data': json.dumps(sample_resource),
'mode': 'single',
'include_dependencies': True
}),
content_type='application/json',
headers={'X-API-Key': 'test-api-key'}
)
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertFalse(data['valid'])
self.assertIn('Required element Patient.name missing', data['errors'])
self.assertIn('HAPI validation failed', [d['issue'] for d in data['details']])
# --- Phase 3 Tests ---
@patch('requests.get')
def test_34_hapi_status_check(self, mock_requests_get):
mock_requests_get.return_value = MagicMock(status_code=200, json=lambda: {'resourceType': 'CapabilityStatement'})
response = self.client.get('/fhir/metadata')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertEqual(data['resourceType'], 'CapabilityStatement')
mock_requests_get.side_effect = requests.ConnectionError("HAPI down")
response = self.client.get('/fhir/metadata')
self.assertEqual(response.status_code, 503)
data = json.loads(response.data)
self.assertIn('Unable to connect to HAPI FHIR server', data['error'])
def test_35_validate_sample_ui_rendering(self):
pkg_name = 'hl7.fhir.us.core'
pkg_version = '6.1.0'
sample_resource = {
'resourceType': 'Patient',
'id': 'test',
'meta': {'profile': ['http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient']}
}
self.create_mock_tgz(f'{pkg_name}-{pkg_version}.tgz', {
'package/package.json': {'name': pkg_name, 'version': pkg_version},
'package/StructureDefinition-us-core-patient.json': {
'resourceType': 'StructureDefinition',
'snapshot': {'element': [{'path': 'Patient.name', 'min': 1}, {'path': 'Patient.identifier', 'mustSupport': True}]}
}
})
response = self.client.post(
'/api/validate-sample',
data=json.dumps({
'package_name': pkg_name,
'version': pkg_version,
'sample_data': json.dumps(sample_resource),
'mode': 'single',
'include_dependencies': True
}),
content_type='application/json',
headers={'X-API-Key': 'test-api-key'}
)
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertFalse(data['valid'])
self.assertIn('Required element Patient.name missing', data['errors'])
self.assertIn('Must Support element Patient.identifier missing', data['warnings'])
response = self.client.get('/validate-sample')
self.assertEqual(response.status_code, 200)
self.assertIn(b'us-core-patient', response.data)
def test_36_must_support_consistency(self):
pkg_name = 'hl7.fhir.us.core' pkg_name = 'hl7.fhir.us.core'
pkg_version = '6.1.0' pkg_version = '6.1.0'
filename = f'{pkg_name}-{pkg_version}.tgz' filename = f'{pkg_name}-{pkg_version}.tgz'
@ -418,15 +409,37 @@ class TestFHIRFlareIGToolkit(unittest.TestCase):
'package/package.json': {'name': pkg_name, 'version': pkg_version}, 'package/package.json': {'name': pkg_name, 'version': pkg_version},
'package/StructureDefinition-us-core-patient.json': { 'package/StructureDefinition-us-core-patient.json': {
'resourceType': 'StructureDefinition', 'resourceType': 'StructureDefinition',
'snapshot': {'element': [{'path': 'Patient.name', 'min': 1}, {'path': 'Patient.identifier', 'mustSupport': True, 'sliceName': 'us-ssn'}]} 'id': 'us-core-patient',
'url': 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient',
'name': 'USCorePatientProfile',
'type': 'Patient',
'status': 'active',
'snapshot': {
'element': [
{'path': 'Patient', 'min': 1, 'max': '1'},
{'path': 'Patient.name', 'min': 1, 'max': '*'},
{'path': 'Patient.identifier', 'min': 0, 'max': '*', 'mustSupport': True}
]
}
} }
}) })
services.process_package_file(os.path.join(app.config['FHIR_PACKAGES_DIR'], filename))
# Load IG to HAPI
self.client.post(
'/api/load-ig-to-hapi',
data=json.dumps({'package_name': pkg_name, 'version': pkg_version}),
content_type='application/json',
headers={'X-API-Key': 'test-api-key'}
)
# Validate a sample that's missing a required element
sample_resource = { sample_resource = {
'resourceType': 'Patient', 'resourceType': 'Patient',
'id': 'test', 'id': 'test-patient',
'meta': {'profile': ['http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient']} 'meta': {'profile': ['http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient']}
# Missing required 'name' element
} }
response = self.client.post( response = self.client.post(
'/api/validate-sample', '/api/validate-sample',
data=json.dumps({ data=json.dumps({
@ -439,18 +452,68 @@ class TestFHIRFlareIGToolkit(unittest.TestCase):
content_type='application/json', content_type='application/json',
headers={'X-API-Key': 'test-api-key'} headers={'X-API-Key': 'test-api-key'}
) )
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
data = json.loads(response.data) data = json.loads(response.data)
self.assertIn('Must Support element Patient.identifier missing', data['warnings']) self.assertFalse(data['valid'])
with self.app_context: # Check for validation error related to missing name
ig = ProcessedIg.query.filter_by(package_name=pkg_name, version=pkg_version).first() found_name_error = any('name' in error for error in data['errors'])
self.assertIsNotNone(ig) self.assertTrue(found_name_error, f"Expected error about missing name element, got: {data['errors']}")
must_support_paths = ig.must_support_elements.get('Patient', [])
self.assertIn('Patient.identifier:us-ssn', must_support_paths) def test_32_push_ig_to_hapi_integration(self):
response = self.client.get(f'/view-ig/{ig.id}') """Test pushing multiple resources from an IG to the containerized HAPI FHIR server"""
pkg_name = 'test.push.pkg'
pkg_version = '1.0.0'
filename = f'{pkg_name}-{pkg_version}.tgz'
# Create a test package with multiple resources
self.create_mock_tgz(filename, {
'package/package.json': {'name': pkg_name, 'version': pkg_version},
'package/Patient-test1.json': {
'resourceType': 'Patient',
'id': 'test1',
'name': [{'family': 'Test', 'given': ['Patient']}]
},
'package/Observation-test1.json': {
'resourceType': 'Observation',
'id': 'test1',
'status': 'final',
'code': {'coding': [{'system': 'http://loinc.org', 'code': '12345-6'}]}
}
})
# Push the IG to HAPI
response = self.client.post(
'/api/push-ig',
data=json.dumps({
'package_name': pkg_name,
'version': pkg_version,
'fhir_server_url': self.container.get_service_url('fhir', 'fhir'),
'include_dependencies': False
}),
content_type='application/json',
headers={'X-API-Key': 'test-api-key', 'Accept': 'application/x-ndjson'}
)
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertIn(b'Patient.identifier:us-ssn', response.data) streamed_data = parse_ndjson(response.data)
self.assertIn(b'list-group-item-warning', response.data) complete_msg = next((item for item in streamed_data if item.get('type') == 'complete'), None)
self.assertIsNotNone(complete_msg, "Complete message not found in streamed response")
summary = complete_msg.get('data', {})
self.assertTrue(summary.get('success_count') >= 2, f"Expected at least 2 successful resources, got {summary.get('success_count')}")
# Verify resources were loaded by querying the HAPI FHIR server directly
patient_response = requests.get(self.container.get_service_url('fhir', 'fhir/Patient/test1'))
self.assertEqual(patient_response.status_code, 200)
patient = patient_response.json()
self.assertEqual(patient['resourceType'], 'Patient')
self.assertEqual(patient['id'], 'test1')
observation_response = requests.get(self.container.get_service_url('fhir', 'fhir/Observation/test1'))
self.assertEqual(observation_response.status_code, 200)
observation = observation_response.json()
self.assertEqual(observation['resourceType'], 'Observation')
self.assertEqual(observation['id'], 'test1')
# --- Existing API Tests --- # --- Existing API Tests ---
@ -515,7 +578,7 @@ class TestFHIRFlareIGToolkit(unittest.TestCase):
pkg_name = 'push.test.pkg' pkg_name = 'push.test.pkg'
pkg_version = '1.0.0' pkg_version = '1.0.0'
filename = f'{pkg_name}-{pkg_version}.tgz' filename = f'{pkg_name}-{pkg_version}.tgz'
fhir_server_url = 'http://fake-fhir.com/baseR4' fhir_server_url = self.container.get_service_url('fhir', 'fhir')
mock_get_metadata.return_value = {'imported_dependencies': []} mock_get_metadata.return_value = {'imported_dependencies': []}
mock_tar = MagicMock() mock_tar = MagicMock()
mock_patient = {'resourceType': 'Patient', 'id': 'pat1'} mock_patient = {'resourceType': 'Patient', 'id': 'pat1'}
@ -564,225 +627,22 @@ class TestFHIRFlareIGToolkit(unittest.TestCase):
self.assertEqual(len(summary.get('failed_details')), 0) self.assertEqual(len(summary.get('failed_details')), 0)
mock_os_exists.assert_called_with(os.path.join(self.test_packages_dir, filename)) mock_os_exists.assert_called_with(os.path.join(self.test_packages_dir, filename))
@patch('os.path.exists', return_value=True) # --- Helper method to debug container issues ---
@patch('app.services.get_package_metadata')
@patch('tarfile.open') def test_99_print_container_logs_on_failure(self):
@patch('requests.Session') """Helper test that prints container logs in case of failures"""
def test_51_api_push_ig_with_failures(self, mock_session, mock_tarfile_open, mock_get_metadata, mock_os_exists): # This test should always pass but will print logs if other tests fail
pkg_name = 'push.fail.pkg' try:
pkg_version = '1.0.0' if hasattr(self, 'container') and self.container.containers_up:
filename = f'{pkg_name}-{pkg_version}.tgz' for service_name in ['fhir', 'db', 'fhirflare']:
fhir_server_url = 'http://fail-fhir.com/baseR4' if service_name in self.container._container_ids:
mock_get_metadata.return_value = {'imported_dependencies': []} print(f"\n=== Logs for {service_name} ===")
mock_tar = MagicMock() print(self.container.get_logs(service_name))
mock_ok_res = {'resourceType': 'Patient', 'id': 'ok1'} except Exception as e:
mock_fail_res = {'resourceType': 'Observation', 'id': 'fail1'} print(f"Error getting container logs: {e}")
ok_member = MagicMock(spec=tarfile.TarInfo)
ok_member.name = 'package/Patient-ok1.json' # This assertion always passes - this test is just for debug info
ok_member.isfile.return_value = True self.assertTrue(True)
fail_member = MagicMock(spec=tarfile.TarInfo)
fail_member.name = 'package/Observation-fail1.json'
fail_member.isfile.return_value = True
mock_tar.getmembers.return_value = [ok_member, fail_member]
def mock_extractfile(member):
if member.name == 'package/Patient-ok1.json':
return io.BytesIO(json.dumps(mock_ok_res).encode('utf-8'))
if member.name == 'package/Observation-fail1.json':
return io.BytesIO(json.dumps(mock_fail_res).encode('utf-8'))
return None
mock_tar.extractfile.side_effect = mock_extractfile
mock_tarfile_open.return_value.__enter__.return_value = mock_tar
mock_session_instance = MagicMock()
mock_ok_response = MagicMock(status_code=200)
mock_ok_response.raise_for_status.return_value = None
mock_fail_http_response = MagicMock(status_code=400)
mock_fail_http_response.json.return_value = {'resourceType': 'OperationOutcome', 'issue': [{'severity': 'error', 'diagnostics': 'Validation failed'}]}
mock_fail_exception = requests.exceptions.HTTPError(response=mock_fail_http_response)
mock_fail_http_response.raise_for_status.side_effect = mock_fail_exception
mock_session_instance.put.side_effect = [mock_ok_response, mock_fail_http_response]
mock_session.return_value = mock_session_instance
self.create_mock_tgz(filename, {'package/dummy.txt': 'content'})
response = self.client.post(
'/api/push-ig',
data=json.dumps({
'package_name': pkg_name,
'version': pkg_version,
'fhir_server_url': fhir_server_url,
'include_dependencies': False,
'api_key': 'test-api-key'
}),
content_type='application/json',
headers={'X-API-Key': 'test-api-key', 'Accept': 'application/x-ndjson'}
)
self.assertEqual(response.status_code, 200)
streamed_data = parse_ndjson(response.data)
complete_msg = next((item for item in streamed_data if item.get('type') == 'complete'), None)
self.assertIsNotNone(complete_msg)
summary = complete_msg.get('data', {})
self.assertEqual(summary.get('status'), 'partial')
self.assertEqual(summary.get('success_count'), 1)
self.assertEqual(summary.get('failure_count'), 1)
self.assertEqual(len(summary.get('failed_details')), 1)
self.assertEqual(summary['failed_details'][0].get('resource'), 'Observation/fail1')
self.assertIn('Validation failed', summary['failed_details'][0].get('error', ''))
mock_os_exists.assert_called_with(os.path.join(self.test_packages_dir, filename))
@patch('os.path.exists', return_value=True)
@patch('app.services.get_package_metadata')
@patch('tarfile.open')
@patch('requests.Session')
def test_52_api_push_ig_with_dependency(self, mock_session, mock_tarfile_open, mock_get_metadata, mock_os_exists):
main_pkg_name = 'main.dep.pkg'
main_pkg_ver = '1.0'
main_filename = f'{main_pkg_name}-{main_pkg_ver}.tgz'
dep_pkg_name = 'dep.pkg'
dep_pkg_ver = '1.0'
dep_filename = f'{dep_pkg_name}-{dep_pkg_ver}.tgz'
fhir_server_url = 'http://dep-fhir.com/baseR4'
self.create_mock_tgz(main_filename, {'package/Patient-main.json': {'resourceType': 'Patient', 'id': 'main'}})
self.create_mock_tgz(dep_filename, {'package/Observation-dep.json': {'resourceType': 'Observation', 'id': 'dep'}})
mock_get_metadata.return_value = {'imported_dependencies': [{'name': dep_pkg_name, 'version': dep_pkg_ver}]}
mock_main_tar = MagicMock()
main_member = MagicMock(spec=tarfile.TarInfo)
main_member.name = 'package/Patient-main.json'
main_member.isfile.return_value = True
mock_main_tar.getmembers.return_value = [main_member]
mock_main_tar.extractfile.return_value = io.BytesIO(json.dumps({'resourceType': 'Patient', 'id': 'main'}).encode('utf-8'))
mock_dep_tar = MagicMock()
dep_member = MagicMock(spec=tarfile.TarInfo)
dep_member.name = 'package/Observation-dep.json'
dep_member.isfile.return_value = True
mock_dep_tar.getmembers.return_value = [dep_member]
mock_dep_tar.extractfile.return_value = io.BytesIO(json.dumps({'resourceType': 'Observation', 'id': 'dep'}).encode('utf-8'))
def tar_opener(path, mode):
mock_tar_ctx = MagicMock()
if main_filename in path:
mock_tar_ctx.__enter__.return_value = mock_main_tar
elif dep_filename in path:
mock_tar_ctx.__enter__.return_value = mock_dep_tar
else:
empty_mock_tar = MagicMock()
empty_mock_tar.getmembers.return_value = []
mock_tar_ctx.__enter__.return_value = empty_mock_tar
return mock_tar_ctx
mock_tarfile_open.side_effect = tar_opener
mock_session_instance = MagicMock()
mock_put_response = MagicMock(status_code=200)
mock_put_response.raise_for_status.return_value = None
mock_session_instance.put.return_value = mock_put_response
mock_session.return_value = mock_session_instance
response = self.client.post(
'/api/push-ig',
data=json.dumps({
'package_name': main_pkg_name,
'version': main_pkg_ver,
'fhir_server_url': fhir_server_url,
'include_dependencies': True,
'api_key': 'test-api-key'
}),
content_type='application/json',
headers={'X-API-Key': 'test-api-key', 'Accept': 'application/x-ndjson'}
)
self.assertEqual(response.status_code, 200)
streamed_data = parse_ndjson(response.data)
complete_msg = next((item for item in streamed_data if item.get('type') == 'complete'), None)
self.assertIsNotNone(complete_msg)
summary = complete_msg.get('data', {})
self.assertEqual(summary.get('status'), 'success')
self.assertEqual(summary.get('success_count'), 2)
self.assertEqual(len(summary.get('pushed_packages_summary')), 2)
mock_os_exists.assert_any_call(os.path.join(self.test_packages_dir, main_filename))
mock_os_exists.assert_any_call(os.path.join(self.test_packages_dir, dep_filename))
# --- Helper Route Tests ---
@patch('app.ProcessedIg.query')
@patch('app.services.find_and_extract_sd')
@patch('os.path.exists')
def test_60_get_structure_definition_success(self, mock_exists, mock_find_sd, mock_query):
pkg_name = 'struct.test'
pkg_version = '1.0'
resource_type = 'Patient'
mock_exists.return_value = True
mock_sd_data = {'resourceType': 'StructureDefinition', 'snapshot': {'element': [{'id': 'Patient.name', 'min': 1}, {'id': 'Patient.birthDate', 'mustSupport': True}]}}
mock_find_sd.return_value = (mock_sd_data, 'path/to/sd.json')
mock_processed_ig = MagicMock()
mock_processed_ig.must_support_elements = {resource_type: ['Patient.birthDate']}
mock_query.filter_by.return_value.first.return_value = mock_processed_ig
response = self.client.get(f'/get-structure?package_name={pkg_name}&package_version={pkg_version}&resource_type={resource_type}')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertEqual(data['must_support_paths'], ['Patient.birthDate'])
@patch('app.services.import_package_and_dependencies')
@patch('app.services.find_and_extract_sd')
@patch('os.path.exists')
def test_61_get_structure_definition_fallback(self, mock_exists, mock_find_sd, mock_import):
pkg_name = 'struct.test'
pkg_version = '1.0'
core_pkg_name, core_pkg_version = services.CANONICAL_PACKAGE
resource_type = 'Observation'
def exists_side_effect(path):
return True
mock_exists.side_effect = exists_side_effect
mock_core_sd_data = {'resourceType': 'StructureDefinition', 'snapshot': {'element': [{'id': 'Observation.status'}]}}
def find_sd_side_effect(path, identifier, profile_url=None):
if f"{pkg_name}-{pkg_version}.tgz" in path:
return (None, None)
if f"{core_pkg_name}-{core_pkg_version}.tgz" in path:
return (mock_core_sd_data, 'path/obs.json')
return (None, None)
mock_find_sd.side_effect = find_sd_side_effect
with patch('app.ProcessedIg.query') as mock_query:
mock_query.filter_by.return_value.first.return_value = None
response = self.client.get(f'/get-structure?package_name={pkg_name}&package_version={pkg_version}&resource_type={resource_type}')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertTrue(data['fallback_used'])
@patch('app.services.find_and_extract_sd', return_value=(None, None))
@patch('app.services.import_package_and_dependencies')
@patch('os.path.exists')
def test_62_get_structure_definition_not_found_anywhere(self, mock_exists, mock_import, mock_find_sd):
pkg_name = 'no.sd.pkg'
pkg_version = '1.0'
core_pkg_name, core_pkg_version = services.CANONICAL_PACKAGE
def exists_side_effect(path):
if f"{pkg_name}-{pkg_version}.tgz" in path:
return True
if f"{core_pkg_name}-{core_pkg_version}.tgz" in path:
return False
return False
mock_exists.side_effect = exists_side_effect
mock_import.return_value = {'errors': ['Download failed'], 'downloaded': False}
response = self.client.get(f'/get-structure?package_name={pkg_name}&package_version={pkg_version}&resource_type=Whatever')
self.assertEqual(response.status_code, 500)
data = json.loads(response.data)
self.assertIn('failed to download core package', data['error'])
def test_63_get_example_content_success(self):
pkg_name = 'example.test'
pkg_version = '1.0'
filename = f"{pkg_name}-{pkg_version}.tgz"
example_path = 'package/Patient-example.json'
example_content = {'resourceType': 'Patient', 'id': 'example'}
self.create_mock_tgz(filename, {example_path: example_content})
response = self.client.get(f'/get-example?package_name={pkg_name}&package_version={pkg_version}&filename={example_path}')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertEqual(data, example_content)
def test_64_get_package_metadata_success(self):
pkg_name = 'metadata.test'
pkg_version = '1.0'
metadata_filename = f"{pkg_name}-{pkg_version}.metadata.json"
metadata_content = {'package_name': pkg_name, 'version': pkg_version, 'dependency_mode': 'tree-shaking'}
metadata_path = os.path.join(app.config['FHIR_PACKAGES_DIR'], metadata_filename)
open(metadata_path, 'w').write(json.dumps(metadata_content))
response = self.client.get(f'/get-package-metadata?package_name={pkg_name}&version={pkg_version}')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertEqual(data.get('dependency_mode'), 'tree-shaking')
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@ -0,0 +1,431 @@
{
"resourceType" : "Bundle",
"id" : "transaction-ex",
"type" : "transaction",
"entry" : [{
"fullUrl" : "urn:uuid:64eb2d39-8da6-4c1d-b4c7-a6d3e916cd5b",
"resource" : {
"resourceType" : "Patient",
"id" : "example-patient",
"meta" : {
"profile" : ["urn://example.com/ph-core/fhir/StructureDefinition/ph-core-patient"]
},
"text" : {
"status" : "generated",
"div" : "<div xmlns=\"http://www.w3.org/1999/xhtml\"><a name=\"Patient_example-patient\"> </a>Juan Dela Cruz is a male patient born on 1 January 1980, residing in Manila, NCR, Philippines.</div>"
},
"extension" : [{
"extension" : [{
"url" : "code",
"valueCodeableConcept" : {
"coding" : [{
"system" : "urn:iso:std:iso:3166",
"code" : "PH",
"display" : "Philippines"
}]
}
},
{
"url" : "period",
"valuePeriod" : {
"start" : "2020-01-01",
"end" : "2023-01-01"
}
}],
"url" : "http://hl7.org/fhir/StructureDefinition/patient-nationality"
},
{
"url" : "http://hl7.org/fhir/StructureDefinition/patient-religion",
"valueCodeableConcept" : {
"coding" : [{
"system" : "http://terminology.hl7.org/CodeSystem/v3-ReligiousAffiliation",
"code" : "1007",
"display" : "Atheism"
}]
}
},
{
"url" : "urn://example.com/ph-core/fhir/StructureDefinition/indigenous-people",
"valueBoolean" : true
},
{
"url" : "urn://example.com/ph-core/fhir/StructureDefinition/indigenous-group",
"valueCodeableConcept" : {
"coding" : [{
"system" : "urn://example.com/ph-core/fhir/CodeSystem/indigenous-groups",
"code" : "Ilongots",
"display" : "Ilongots"
}]
}
},
{
"url" : "urn://example.com/ph-core/fhir/StructureDefinition/race",
"valueCodeableConcept" : {
"coding" : [{
"system" : "http://terminology.hl7.org/CodeSystem/v3-Race",
"code" : "2036-2",
"display" : "Filipino"
}]
}
}],
"identifier" : [{
"system" : "http://philhealth.gov.ph/fhir/Identifier/philhealth-id",
"value" : "63-584789845-5"
}],
"active" : true,
"name" : [{
"family" : "Dela Cruz",
"given" : ["Juan Jane",
"Dela Fuente"]
}],
"gender" : "male",
"birthDate" : "1985-06-15",
"address" : [{
"extension" : [{
"url" : "urn://example.com/ph-core/fhir/StructureDefinition/city-municipality",
"valueCoding" : {
"system" : "urn://example.com/ph-core/fhir/CodeSystem/PSGC",
"code" : "1380200000",
"display" : "City of Las Piñas"
}
},
{
"url" : "urn://example.com/ph-core/fhir/StructureDefinition/city-municipality",
"valueCoding" : {
"system" : "urn://example.com/ph-core/fhir/CodeSystem/PSGC",
"code" : "1380100000",
"display" : "City of Caloocan"
}
},
{
"url" : "urn://example.com/ph-core/fhir/StructureDefinition/province",
"valueCoding" : {
"system" : "urn://example.com/ph-core/fhir/CodeSystem/PSGC",
"code" : "0402100000",
"display" : "Cavite"
}
},
{
"url" : "urn://example.com/ph-core/fhir/StructureDefinition/province",
"valueCoding" : {
"system" : "urn://example.com/ph-core/fhir/CodeSystem/PSGC",
"code" : "0403400000",
"display" : "Laguna"
}
},
{
"url" : "urn://example.com/ph-core/fhir/StructureDefinition/province",
"valueCoding" : {
"system" : "urn://example.com/ph-core/fhir/CodeSystem/PSGC",
"code" : "0405800000",
"display" : "Rizal"
}
},
{
"url" : "urn://example.com/ph-core/fhir/StructureDefinition/province",
"valueCoding" : {
"system" : "urn://example.com/ph-core/fhir/CodeSystem/PSGC",
"code" : "1704000000",
"display" : "Marinduque"
}
},
{
"url" : "urn://example.com/ph-core/fhir/StructureDefinition/province",
"valueCoding" : {
"system" : "urn://example.com/ph-core/fhir/CodeSystem/PSGC",
"code" : "0402100000",
"display" : "Cavite"
}
},
{
"url" : "urn://example.com/ph-core/fhir/StructureDefinition/province",
"valueCoding" : {
"system" : "urn://example.com/ph-core/fhir/CodeSystem/PSGC",
"code" : "1705100000",
"display" : "Occidental Mindoro"
}
}],
"line" : ["123 Mabini Street",
"Barangay Malinis"],
"city" : "Quezon City",
"district" : "NCR",
"postalCode" : "1100",
"country" : "PH"
}]
},
"request" : {
"method" : "POST",
"url" : "Patient"
}
},
{
"fullUrl" : "urn:uuid:60b7132e-7cfd-44bc-83c2-de140dc8aaae",
"resource" : {
"resourceType" : "Encounter",
"id" : "example-encounter",
"meta" : {
"profile" : ["urn://example.com/ph-core/fhir/StructureDefinition/ph-core-encounter"]
},
"text" : {
"status" : "generated",
"div" : "<div xmlns=\"http://www.w3.org/1999/xhtml\"><a name=\"Encounter_example-encounter\"> </a>An ambulatory encounter for Juan Dela Cruz that has been completed.</div>"
},
"status" : "finished",
"class" : {
"system" : "http://terminology.hl7.org/CodeSystem/v3-ActCode",
"code" : "AMB",
"display" : "ambulatory"
},
"subject" : {
"reference" : "urn:uuid:64eb2d39-8da6-4c1d-b4c7-a6d3e916cd5b"
}
},
"request" : {
"method" : "POST",
"url" : "Encounter"
}
},
{
"fullUrl" : "urn:uuid:1a391d1e-a068-479a-88e3-e3d52c3a6f64",
"resource" : {
"resourceType" : "Condition",
"id" : "example-condition",
"text" : {
"status" : "generated",
"div" : "<div xmlns=\"http://www.w3.org/1999/xhtml\"><a name=\"Condition_example-condition\"> </a>Juan Dela Cruz has an active diagnosis of Type 2 Diabetes Mellitus.</div>"
},
"clinicalStatus" : {
"coding" : [{
"system" : "http://terminology.hl7.org/CodeSystem/condition-clinical",
"code" : "active",
"display" : "Active"
}]
},
"code" : {
"coding" : [{
"system" : "http://snomed.info/sct",
"code" : "44054006",
"display" : "Diabetes mellitus type 2"
}]
},
"subject" : {
"reference" : "urn:uuid:64eb2d39-8da6-4c1d-b4c7-a6d3e916cd5b"
},
"encounter" : {
"reference" : "urn:uuid:60b7132e-7cfd-44bc-83c2-de140dc8aaae"
}
},
"request" : {
"method" : "POST",
"url" : "Condition"
}
},
{
"fullUrl" : "urn:uuid:024dcb47-cc23-407a-839b-b4634e95abae",
"resource" : {
"resourceType" : "Medication",
"id" : "example-medication",
"meta" : {
"profile" : ["urn://example.com/ph-core/fhir/StructureDefinition/ph-core-medication"]
},
"text" : {
"status" : "generated",
"div" : "<div xmlns=\"http://www.w3.org/1999/xhtml\"><a name=\"Medication_example-medication\"> </a>A medication resource has been created, but no specific details are provided.</div>"
}
},
"request" : {
"method" : "POST",
"url" : "Medication"
}
},
{
"fullUrl" : "urn:uuid:013f46df-f245-4a2f-beaf-9eb2c47fb1a3",
"resource" : {
"resourceType" : "Observation",
"id" : "blood-pressure",
"meta" : {
"profile" : ["urn://example.com/ph-core/fhir/StructureDefinition/ph-core-observation",
"http://hl7.org/fhir/StructureDefinition/vitalsigns",
"http://hl7.org/fhir/StructureDefinition/bp"]
},
"text" : {
"status" : "generated",
"div" : "<div xmlns=\"http://www.w3.org/1999/xhtml\"><a name=\"Observation_blood-pressure\"> </a>On 17 September 2012, a blood pressure observation was recorded for Juan Dela Cruz. The systolic pressure was 107 mmHg (Normal), and the diastolic pressure was 60 mmHg (Below low normal). The measurement was taken from the right arm and performed by a practitioner.</div>"
},
"identifier" : [{
"system" : "urn:ietf:rfc:3986",
"value" : "urn:uuid:187e0c12-8dd2-67e2-99b2-bf273c878281"
}],
"basedOn" : [{
"identifier" : {
"system" : "https://acme.org/identifiers",
"value" : "1234"
}
}],
"status" : "final",
"category" : [{
"coding" : [{
"system" : "http://terminology.hl7.org/CodeSystem/observation-category",
"code" : "vital-signs",
"display" : "Vital Signs"
}]
}],
"code" : {
"coding" : [{
"system" : "http://loinc.org",
"code" : "85354-9",
"display" : "Blood pressure panel with all children optional"
}],
"text" : "Blood pressure systolic & diastolic"
},
"subject" : {
"reference" : "urn:uuid:64eb2d39-8da6-4c1d-b4c7-a6d3e916cd5b"
},
"effectiveDateTime" : "2012-09-17",
"performer" : [{
"reference" : "urn:uuid:a036fd4c-c950-497b-8905-0d2c5ec6f1d4"
}],
"interpretation" : [{
"coding" : [{
"system" : "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation",
"code" : "L",
"display" : "Low"
}],
"text" : "Below low normal"
}],
"bodySite" : {
"coding" : [{
"system" : "http://snomed.info/sct",
"code" : "85050009",
"display" : "Bone structure of humerus"
}]
},
"component" : [{
"code" : {
"coding" : [{
"system" : "http://loinc.org",
"code" : "8480-6",
"display" : "Systolic blood pressure"
}]
},
"valueQuantity" : {
"value" : 107,
"unit" : "mmHg",
"system" : "http://unitsofmeasure.org",
"code" : "mm[Hg]"
},
"interpretation" : [{
"coding" : [{
"system" : "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation",
"code" : "N",
"display" : "Normal"
}],
"text" : "Normal"
}]
},
{
"code" : {
"coding" : [{
"system" : "http://loinc.org",
"code" : "8462-4",
"display" : "Diastolic blood pressure"
}]
},
"valueQuantity" : {
"value" : 60,
"unit" : "mmHg",
"system" : "http://unitsofmeasure.org",
"code" : "mm[Hg]"
},
"interpretation" : [{
"coding" : [{
"system" : "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation",
"code" : "L",
"display" : "Low"
}],
"text" : "Below low normal"
}]
}]
},
"request" : {
"method" : "POST",
"url" : "Observation"
}
},
{
"fullUrl" : "urn:uuid:b43c67e7-d9c4-48bb-a1b4-55769eeb9066",
"resource" : {
"resourceType" : "AllergyIntolerance",
"id" : "example-allergy",
"text" : {
"status" : "generated",
"div" : "<div xmlns=\"http://www.w3.org/1999/xhtml\"><a name=\"AllergyIntolerance_example-allergy\"> </a>Juan Dela Cruz has a high criticality, active allergy to Benethamine penicillin.</div>"
},
"clinicalStatus" : {
"coding" : [{
"system" : "http://terminology.hl7.org/CodeSystem/allergyintolerance-clinical",
"code" : "active",
"display" : "Active"
}]
},
"criticality" : "high",
"code" : {
"coding" : [{
"system" : "http://snomed.info/sct",
"code" : "294494002",
"display" : "Benethamine penicillin allergy"
}]
},
"patient" : {
"reference" : "urn:uuid:64eb2d39-8da6-4c1d-b4c7-a6d3e916cd5b"
}
},
"request" : {
"method" : "POST",
"url" : "AllergyIntolerance"
}
},
{
"fullUrl" : "urn:uuid:a036fd4c-c950-497b-8905-0d2c5ec6f1d4",
"resource" : {
"resourceType" : "Practitioner",
"id" : "example-practitioner",
"meta" : {
"profile" : ["urn://example.com/ph-core/fhir/StructureDefinition/ph-core-practitioner"]
},
"text" : {
"status" : "generated",
"div" : "<div xmlns=\"http://www.w3.org/1999/xhtml\"><a name=\"Practitioner_example-practitioner\"> </a>Dr. Maria Clara Santos is a female practitioner born on May 15, 1985. She resides at 1234 Mabini Street, Manila, NCR, 1000, Philippines. She can be contacted via mobile at +63-912-345-6789 or by email at maria.santos@example.ph.</div>"
},
"name" : [{
"family" : "Santos",
"given" : ["Maria",
"Clara"]
}],
"telecom" : [{
"system" : "phone",
"value" : "+63-912-345-6789",
"use" : "mobile"
},
{
"system" : "email",
"value" : "maria.santos@example.ph",
"use" : "work"
}],
"address" : [{
"use" : "home",
"line" : ["1234 Mabini Street"],
"city" : "Manila",
"state" : "NCR",
"postalCode" : "1000",
"country" : "PH"
}],
"gender" : "female",
"birthDate" : "1985-05-15"
},
"request" : {
"method" : "POST",
"url" : "Practitioner"
}
}]
}

Binary file not shown.

View File

@ -0,0 +1,25 @@
2025-07-31 12:34:21,943 - services - DEBUG - Received validate-sample request
2025-07-31 12:34:21,944 - services - DEBUG - Request params: package_name=example.fhir.ph.core.r4, version=0.1.0, sample_data_length=713
2025-07-31 12:34:21,944 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-07-31 12:34:21,944 - services - DEBUG - Checking package file: /app/instance/fhir_packages/example.fhir.ph.core.r4-0.1.0.tgz
2025-07-31 12:34:21,944 - services - DEBUG - Validating AllergyIntolerance against example.fhir.ph.core.r4#0.1.0
2025-07-31 12:34:21,944 - services - DEBUG - Using FHIR_PACKAGES_DIR from current_app config: /app/instance/fhir_packages
2025-07-31 12:34:21,945 - services - DEBUG - Searching for SD matching 'AllergyIntolerance' with profile 'None' in example.fhir.ph.core.r4-0.1.0.tgz
2025-07-31 12:34:21,956 - services - INFO - SD matching identifier 'AllergyIntolerance' or profile 'None' not found within archive example.fhir.ph.core.r4-0.1.0.tgz
2025-07-31 12:34:21,956 - services - INFO - Validation result for AllergyIntolerance against example.fhir.ph.core.r4#0.1.0: valid=False, errors=1, warnings=0
2025-07-31 12:34:21,957 - werkzeug - INFO - 10.0.0.102 - - [31/Jul/2025 12:34:21] "POST /api/validate-sample HTTP/1.1" 200 -
2025-07-31 12:34:24,510 - werkzeug - INFO - 10.0.2.245 - - [31/Jul/2025 12:34:24] "GET / HTTP/1.1" 200 -
2025-07-31 12:34:27,378 - werkzeug - INFO - 10.0.2.245 - - [31/Jul/2025 12:34:27] "GET / HTTP/1.1" 200 -
2025-07-31 12:34:34,510 - werkzeug - INFO - 10.0.2.245 - - [31/Jul/2025 12:34:34] "GET / HTTP/1.1" 200 -
2025-07-31 12:34:36,799 - __main__ - DEBUG - Scanning packages directory: /app/instance/fhir_packages
2025-07-31 12:34:36,800 - __main__ - DEBUG - Found 8 .tgz files: ['PHCDI.r4-0.1.0.tgz', 'hl7.fhir.uv.ips-1.1.0.tgz', 'hl7.fhir.r4.core-4.0.1.tgz', 'fhir.dicom-2022.4.20221006.tgz', 'hl7.terminology.r4-5.0.0.tgz', 'example.fhir.ph.core.r4-0.1.0.tgz', 'hl7.terminology.r4-6.4.0.tgz', 'hl7.fhir.uv.extensions.r4-5.2.0.tgz']
2025-07-31 12:34:36,813 - __main__ - DEBUG - Added package: PHCDI.r4#0.1.0
2025-07-31 12:34:36,837 - __main__ - DEBUG - Added package: hl7.fhir.uv.ips#1.1.0
2025-07-31 12:34:37,378 - werkzeug - INFO - 10.0.2.245 - - [31/Jul/2025 12:34:37] "GET / HTTP/1.1" 200 -
2025-07-31 12:34:37,514 - __main__ - DEBUG - Added package: hl7.fhir.r4.core#4.0.1
2025-07-31 12:34:37,622 - __main__ - DEBUG - Added package: fhir.dicom#2022.4.20221006
2025-07-31 12:34:38,008 - __main__ - DEBUG - Added package: hl7.terminology.r4#5.0.0
2025-07-31 12:34:38,015 - __main__ - DEBUG - Added package: example.fhir.ph.core.r4#0.1.0
2025-07-31 12:34:38,413 - __main__ - DEBUG - Added package: hl7.terminology.r4#6.4.0
2025-07-31 12:34:38,524 - __main__ - DEBUG - Added package: hl7.fhir.uv.extensions.r4#5.2.0
2025-07-31 12:34:38,525 - __main__ - DEBUG - Set package choices: [('', 'None'), ('PHCDI.r4#0.1.0', 'PHCDI.r4#0.1.0'), ('example.fhir.ph.core.r4#0.1.0', 'example.fhir.ph.core.r4#0.1.0'), ('fhir.dicom#2022.4.20221006', 'fhir.dicom#2022.4.20221006'), ('hl7.fhir.r4.core#4.0.1', 'hl7.fhir.r4.core#4.0.1'), ('hl7.fhir.uv.extensions.r4#5.2.0', 'hl7.fhir.uv.extensions.r4#5.2.0'), ('hl7.fhir.uv.ips#1.1.0', 'hl7.fhir.uv.ips#1.1.0'), ('hl7.terminology.r4#5.0.0', 'hl7.terminology.r4#5.0.0'), ('hl7.terminology.r4#6.4.0', 'hl7.terminology.r4#6.4.0')]

1
website/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/_site/

7
website/Gemfile Normal file
View File

@ -0,0 +1,7 @@
source "https://rubygems.org"
gem "jekyll", "~> 3.9.3"
gem "github-pages", group: :jekyll_plugins
gem "jekyll-remote-theme"
gem "jekyll-seo-tag"
gem "webrick", "~> 1.7"

309
website/Gemfile.lock Normal file
View File

@ -0,0 +1,309 @@
GEM
remote: https://rubygems.org/
specs:
activesupport (8.0.2)
base64
benchmark (>= 0.3)
bigdecimal
concurrent-ruby (~> 1.0, >= 1.3.1)
connection_pool (>= 2.2.5)
drb
i18n (>= 1.6, < 2)
logger (>= 1.4.2)
minitest (>= 5.1)
securerandom (>= 0.3)
tzinfo (~> 2.0, >= 2.0.5)
uri (>= 0.13.1)
addressable (2.8.7)
public_suffix (>= 2.0.2, < 7.0)
base64 (0.2.0)
benchmark (0.4.1)
bigdecimal (3.2.2)
coffee-script (2.4.1)
coffee-script-source
execjs
coffee-script-source (1.12.2)
colorator (1.1.0)
commonmarker (0.23.11)
concurrent-ruby (1.3.5)
connection_pool (2.5.3)
dnsruby (1.72.4)
base64 (~> 0.2.0)
logger (~> 1.6.5)
simpleidn (~> 0.2.1)
drb (2.2.3)
em-websocket (0.5.3)
eventmachine (>= 0.12.9)
http_parser.rb (~> 0)
ethon (0.16.0)
ffi (>= 1.15.0)
eventmachine (1.2.7)
execjs (2.10.0)
faraday (2.13.4)
faraday-net_http (>= 2.0, < 3.5)
json
logger
faraday-net_http (3.4.1)
net-http (>= 0.5.0)
ffi (1.17.2-aarch64-linux-gnu)
ffi (1.17.2-aarch64-linux-musl)
ffi (1.17.2-arm-linux-gnu)
ffi (1.17.2-arm-linux-musl)
ffi (1.17.2-arm64-darwin)
ffi (1.17.2-x86_64-darwin)
ffi (1.17.2-x86_64-linux-gnu)
ffi (1.17.2-x86_64-linux-musl)
forwardable-extended (2.6.0)
gemoji (4.1.0)
github-pages (231)
github-pages-health-check (= 1.18.2)
jekyll (= 3.9.5)
jekyll-avatar (= 0.8.0)
jekyll-coffeescript (= 1.2.2)
jekyll-commonmark-ghpages (= 0.4.0)
jekyll-default-layout (= 0.1.5)
jekyll-feed (= 0.17.0)
jekyll-gist (= 1.5.0)
jekyll-github-metadata (= 2.16.1)
jekyll-include-cache (= 0.2.1)
jekyll-mentions (= 1.6.0)
jekyll-optional-front-matter (= 0.3.2)
jekyll-paginate (= 1.1.0)
jekyll-readme-index (= 0.3.0)
jekyll-redirect-from (= 0.16.0)
jekyll-relative-links (= 0.6.1)
jekyll-remote-theme (= 0.4.3)
jekyll-sass-converter (= 1.5.2)
jekyll-seo-tag (= 2.8.0)
jekyll-sitemap (= 1.4.0)
jekyll-swiss (= 1.0.0)
jekyll-theme-architect (= 0.2.0)
jekyll-theme-cayman (= 0.2.0)
jekyll-theme-dinky (= 0.2.0)
jekyll-theme-hacker (= 0.2.0)
jekyll-theme-leap-day (= 0.2.0)
jekyll-theme-merlot (= 0.2.0)
jekyll-theme-midnight (= 0.2.0)
jekyll-theme-minimal (= 0.2.0)
jekyll-theme-modernist (= 0.2.0)
jekyll-theme-primer (= 0.6.0)
jekyll-theme-slate (= 0.2.0)
jekyll-theme-tactile (= 0.2.0)
jekyll-theme-time-machine (= 0.2.0)
jekyll-titles-from-headings (= 0.5.3)
jemoji (= 0.13.0)
kramdown (= 2.4.0)
kramdown-parser-gfm (= 1.1.0)
liquid (= 4.0.4)
mercenary (~> 0.3)
minima (= 2.5.1)
nokogiri (>= 1.13.6, < 2.0)
rouge (= 3.30.0)
terminal-table (~> 1.4)
github-pages-health-check (1.18.2)
addressable (~> 2.3)
dnsruby (~> 1.60)
octokit (>= 4, < 8)
public_suffix (>= 3.0, < 6.0)
typhoeus (~> 1.3)
html-pipeline (2.14.3)
activesupport (>= 2)
nokogiri (>= 1.4)
http_parser.rb (0.8.0)
i18n (1.14.7)
concurrent-ruby (~> 1.0)
jekyll (3.9.5)
addressable (~> 2.4)
colorator (~> 1.0)
em-websocket (~> 0.5)
i18n (>= 0.7, < 2)
jekyll-sass-converter (~> 1.0)
jekyll-watch (~> 2.0)
kramdown (>= 1.17, < 3)
liquid (~> 4.0)
mercenary (~> 0.3.3)
pathutil (~> 0.9)
rouge (>= 1.7, < 4)
safe_yaml (~> 1.0)
jekyll-avatar (0.8.0)
jekyll (>= 3.0, < 5.0)
jekyll-coffeescript (1.2.2)
coffee-script (~> 2.2)
coffee-script-source (~> 1.12)
jekyll-commonmark (1.4.0)
commonmarker (~> 0.22)
jekyll-commonmark-ghpages (0.4.0)
commonmarker (~> 0.23.7)
jekyll (~> 3.9.0)
jekyll-commonmark (~> 1.4.0)
rouge (>= 2.0, < 5.0)
jekyll-default-layout (0.1.5)
jekyll (>= 3.0, < 5.0)
jekyll-feed (0.17.0)
jekyll (>= 3.7, < 5.0)
jekyll-gist (1.5.0)
octokit (~> 4.2)
jekyll-github-metadata (2.16.1)
jekyll (>= 3.4, < 5.0)
octokit (>= 4, < 7, != 4.4.0)
jekyll-include-cache (0.2.1)
jekyll (>= 3.7, < 5.0)
jekyll-mentions (1.6.0)
html-pipeline (~> 2.3)
jekyll (>= 3.7, < 5.0)
jekyll-optional-front-matter (0.3.2)
jekyll (>= 3.0, < 5.0)
jekyll-paginate (1.1.0)
jekyll-readme-index (0.3.0)
jekyll (>= 3.0, < 5.0)
jekyll-redirect-from (0.16.0)
jekyll (>= 3.3, < 5.0)
jekyll-relative-links (0.6.1)
jekyll (>= 3.3, < 5.0)
jekyll-remote-theme (0.4.3)
addressable (~> 2.0)
jekyll (>= 3.5, < 5.0)
jekyll-sass-converter (>= 1.0, <= 3.0.0, != 2.0.0)
rubyzip (>= 1.3.0, < 3.0)
jekyll-sass-converter (1.5.2)
sass (~> 3.4)
jekyll-seo-tag (2.8.0)
jekyll (>= 3.8, < 5.0)
jekyll-sitemap (1.4.0)
jekyll (>= 3.7, < 5.0)
jekyll-swiss (1.0.0)
jekyll-theme-architect (0.2.0)
jekyll (> 3.5, < 5.0)
jekyll-seo-tag (~> 2.0)
jekyll-theme-cayman (0.2.0)
jekyll (> 3.5, < 5.0)
jekyll-seo-tag (~> 2.0)
jekyll-theme-dinky (0.2.0)
jekyll (> 3.5, < 5.0)
jekyll-seo-tag (~> 2.0)
jekyll-theme-hacker (0.2.0)
jekyll (> 3.5, < 5.0)
jekyll-seo-tag (~> 2.0)
jekyll-theme-leap-day (0.2.0)
jekyll (> 3.5, < 5.0)
jekyll-seo-tag (~> 2.0)
jekyll-theme-merlot (0.2.0)
jekyll (> 3.5, < 5.0)
jekyll-seo-tag (~> 2.0)
jekyll-theme-midnight (0.2.0)
jekyll (> 3.5, < 5.0)
jekyll-seo-tag (~> 2.0)
jekyll-theme-minimal (0.2.0)
jekyll (> 3.5, < 5.0)
jekyll-seo-tag (~> 2.0)
jekyll-theme-modernist (0.2.0)
jekyll (> 3.5, < 5.0)
jekyll-seo-tag (~> 2.0)
jekyll-theme-primer (0.6.0)
jekyll (> 3.5, < 5.0)
jekyll-github-metadata (~> 2.9)
jekyll-seo-tag (~> 2.0)
jekyll-theme-slate (0.2.0)
jekyll (> 3.5, < 5.0)
jekyll-seo-tag (~> 2.0)
jekyll-theme-tactile (0.2.0)
jekyll (> 3.5, < 5.0)
jekyll-seo-tag (~> 2.0)
jekyll-theme-time-machine (0.2.0)
jekyll (> 3.5, < 5.0)
jekyll-seo-tag (~> 2.0)
jekyll-titles-from-headings (0.5.3)
jekyll (>= 3.3, < 5.0)
jekyll-watch (2.2.1)
listen (~> 3.0)
jemoji (0.13.0)
gemoji (>= 3, < 5)
html-pipeline (~> 2.2)
jekyll (>= 3.0, < 5.0)
json (2.13.2)
kramdown (2.4.0)
rexml
kramdown-parser-gfm (1.1.0)
kramdown (~> 2.0)
liquid (4.0.4)
listen (3.9.0)
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
logger (1.6.6)
mercenary (0.3.6)
minima (2.5.1)
jekyll (>= 3.5, < 5.0)
jekyll-feed (~> 0.9)
jekyll-seo-tag (~> 2.1)
minitest (5.25.5)
net-http (0.6.0)
uri
nokogiri (1.18.9-aarch64-linux-gnu)
racc (~> 1.4)
nokogiri (1.18.9-aarch64-linux-musl)
racc (~> 1.4)
nokogiri (1.18.9-arm-linux-gnu)
racc (~> 1.4)
nokogiri (1.18.9-arm-linux-musl)
racc (~> 1.4)
nokogiri (1.18.9-arm64-darwin)
racc (~> 1.4)
nokogiri (1.18.9-x86_64-darwin)
racc (~> 1.4)
nokogiri (1.18.9-x86_64-linux-gnu)
racc (~> 1.4)
nokogiri (1.18.9-x86_64-linux-musl)
racc (~> 1.4)
octokit (4.25.1)
faraday (>= 1, < 3)
sawyer (~> 0.9)
pathutil (0.16.2)
forwardable-extended (~> 2.6)
public_suffix (5.1.1)
racc (1.8.1)
rb-fsevent (0.11.2)
rb-inotify (0.11.1)
ffi (~> 1.0)
rexml (3.4.1)
rouge (3.30.0)
rubyzip (2.4.1)
safe_yaml (1.0.5)
sass (3.7.4)
sass-listen (~> 4.0.0)
sass-listen (4.0.0)
rb-fsevent (~> 0.9, >= 0.9.4)
rb-inotify (~> 0.9, >= 0.9.7)
sawyer (0.9.2)
addressable (>= 2.3.5)
faraday (>= 0.17.3, < 3)
securerandom (0.4.1)
simpleidn (0.2.3)
terminal-table (1.8.0)
unicode-display_width (~> 1.1, >= 1.1.1)
typhoeus (1.4.1)
ethon (>= 0.9.0)
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
unicode-display_width (1.8.0)
uri (1.0.3)
webrick (1.9.1)
PLATFORMS
aarch64-linux-gnu
aarch64-linux-musl
arm-linux-gnu
arm-linux-musl
arm64-darwin
x86_64-darwin
x86_64-linux-gnu
x86_64-linux-musl
DEPENDENCIES
github-pages
jekyll (~> 3.9.3)
jekyll-remote-theme
jekyll-seo-tag
webrick (~> 1.7)
BUNDLED WITH
2.7.1

726
website/README.md Normal file
View File

@ -0,0 +1,726 @@
---
layout: default
---
# FHIRFLARE IG Toolkit
![FHIRFLARE Logo](static/FHIRFLARE.png)
## Overview
The FHIRFLARE IG Toolkit is a Flask-based web application designed to streamline the management, processing, validation, and deployment of FHIR Implementation Guides (IGs) and test data. It offers a user-friendly interface for importing IG packages, extracting metadata, validating FHIR resources or bundles, pushing IGs to FHIR servers, converting FHIR resources to FHIR Shorthand (FSH), uploading complex test data sets with dependency management, and retrieving/splitting FHIR bundles. The toolkit includes live consoles for real-time feedback, making it an essential tool for FHIR developers and implementers.
The application can run in two modes:
* **Standalone:** Includes a Dockerized Flask frontend, SQLite database, and an embedded HAPI FHIR server for local validation and interaction.
* **Lite:** Includes only the Dockerized Flask frontend and SQLite database, excluding the local HAPI FHIR server. Requires connection to external FHIR servers for certain features.
## Installation Modes (Lite vs. Standalone)
This toolkit offers two primary installation modes to suit different needs:
* **Standalone Version:**
* Includes the full FHIRFLARE Toolkit application **and** an embedded HAPI FHIR server running locally within the Docker environment.
* Allows for local FHIR resource validation using HAPI FHIR's capabilities.
* Enables the "Use Local HAPI" option in the FHIR API Explorer and FHIR UI Operations pages, proxying requests to the internal HAPI server (`http://localhost:8080/fhir`).
* Requires Git and Maven during the initial build process (via the `.bat` script or manual steps) to prepare the HAPI FHIR server.
* Ideal for users who want a self-contained environment for development and testing or who don't have readily available external FHIR servers.
* **Lite Version:**
* Includes the FHIRFLARE Toolkit application **without** the embedded HAPI FHIR server.
* Requires users to provide URLs for external FHIR servers when using features like the FHIR API Explorer and FHIR UI Operations pages. The "Use Local HAPI" option will be disabled in the UI.
* Resource validation relies solely on local checks against downloaded StructureDefinitions, which may be less comprehensive than HAPI FHIR's validation (e.g., for terminology bindings or complex invariants).
* **Does not require Git or Maven** for setup if using the `.bat` script or running the pre-built Docker image.
* Ideal for users who primarily want to use the IG management, processing, and FSH conversion features, or who will always connect to existing external FHIR servers.
## Features
* **Import IGs:** Download FHIR IG packages and dependencies from a package registry, supporting flexible version formats (e.g., `1.2.3`, `1.1.0-preview`, `current`) and dependency pulling modes (Recursive, Patch Canonical, Tree Shaking).
* **Enhanced Package Search and Import:**
* Interactive page (`/search-and-import`) to search for FHIR IG packages from configured registries.
* Displays package details, version history, dependencies, and dependents.
* Utilizes a local database cache (`CachedPackage`) for faster subsequent searches.
* Background task to refresh the package cache from registries (`/api/refresh-cache-task`).
* Direct import from search results.
* **Manage IGs:** View, process, unload, or delete downloaded IGs, with duplicate detection and resolution.
* **Process IGs:** Extract resource types, profiles, must-support elements, examples, and profile relationships (`structuredefinition-compliesWithProfile` and `structuredefinition-imposeProfile`).
* **Validate FHIR Resources/Bundles:** Validate single FHIR resources or bundles against selected IGs, with detailed error and warning reports (alpha feature). *Note: Lite version uses local SD checks only.*
* **Push IGs:** Upload IG resources (and optionally dependencies) to a target FHIR server. Features include:
* Real-time console output.
* Authentication support (Bearer Token).
* Filtering by resource type or specific files to skip.
* Semantic comparison to skip uploading identical resources (override with **Force Upload** option).
* Correct handling of canonical resources (searching by URL/version before deciding POST/PUT).
* Dry run mode for simulation.
* Verbose logging option.
* **Upload Test Data:** Upload complex sets of test data (individual JSON/XML files or ZIP archives) to a target FHIR server. Features include:
* Robust parsing of JSON and XML (using `fhir.resources` library when available).
* Automatic dependency analysis based on resource references within the uploaded set.
* Topological sorting to ensure resources are uploaded in the correct order.
* Cycle detection in dependencies.
* Choice of individual resource uploads or a single transaction bundle.
* **Optional Pre-Upload Validation:** Validate resources against a selected profile package before uploading.
* **Optional Conditional Uploads (Individual Mode):** Check resource existence (GET) and use conditional `If-Match` headers for updates (PUT) or create resources (PUT/POST). Falls back to simple PUT if unchecked.
* Configurable error handling (stop on first error or continue).
* Authentication support (Bearer Token).
* Streaming progress log via the UI.
* Handles large numbers of files using a custom form parser.
* **Profile Relationships:** Display and validate `compliesWithProfile` and `imposeProfile` extensions in the UI (configurable).
* **FSH Converter:** Convert FHIR JSON/XML resources to FHIR Shorthand (FSH) using GoFSH, with advanced options (Package context, Output styles, Log levels, FHIR versions, Fishing Trip, Dependencies, Indentation, Meta Profile handling, Alias File, No Alias). Includes a waiting spinner.
* **Retrieve and Split Bundles:**
* Retrieve specified resource types as bundles from a FHIR server.
* Optionally fetch referenced resources, either individually or as full bundles for each referenced type.
* Split uploaded ZIP files containing bundles into individual resource JSON files.
* Download retrieved/split resources as a ZIP archive.
* Streaming progress log via the UI for retrieval operations.
* **FHIR Interaction UIs:** Explore FHIR server capabilities and interact with resources using the "FHIR API Explorer" (simple GET/POST/PUT/DELETE) and "FHIR UI Operations" (Swagger-like interface based on CapabilityStatement). *Note: Lite version requires custom server URLs.*
* **HAPI FHIR Configuration (Standalone Mode):**
* A dedicated page (`/config-hapi`) to view and edit the `application.yaml` configuration for the embedded HAPI FHIR server.
* Allows modification of HAPI FHIR properties directly from the UI.
* Option to restart the HAPI FHIR server (Tomcat) to apply changes.
* **API Support:** RESTful API endpoints for importing, pushing, retrieving metadata, validating, uploading test data, and retrieving/splitting bundles.
* **Live Console:** Real-time logs for push, validation, upload test data, FSH conversion, and bundle retrieval operations.
* **Configurable Behavior:** Control validation modes, display options via `app.config`.
* **Theming:** Supports light and dark modes.
## Technology Stack
* Python 3.12+, Flask 2.3.3, Flask-SQLAlchemy 3.0.5, Flask-WTF 1.2.1
* Jinja2, Bootstrap 5.3.3, JavaScript (ES6), Lottie-Web 5.12.2
* SQLite
* Docker, Docker Compose, Supervisor
* Node.js 18+ (for GoFSH/SUSHI), GoFSH, SUSHI
* HAPI FHIR (Standalone version only)
* Requests 2.31.0, Tarfile, Logging, Werkzeug
* fhir.resources (optional, for robust XML parsing)
## Prerequisites
* **Docker:** Required for containerized deployment (both versions).
* **Git & Maven:** Required **only** for building the **Standalone** version from source using the `.bat` script or manual steps. Not required for the Lite version build or for running pre-built Docker Hub images.
* **Windows:** Required if using the `.bat` scripts.
## Setup Instructions
### Running Pre-built Images (General Users)
This is the easiest way to get started without needing Git or Maven. Choose the version you need:
**Lite Version (No local HAPI FHIR):**
```bash
# Pull the latest Lite image
docker pull ghcr.io/sudo-jhare/fhirflare-ig-toolkit-lite:latest
# Run the Lite version (maps port 5000 for the UI)
# You'll need to create local directories for persistent data first:
# mkdir instance logs static static/uploads instance/hapi-h2-data
docker run -d \
-p 5000:5000 \
-v ./instance:/app/instance \
-v ./static/uploads:/app/static/uploads \
-v ./instance/hapi-h2-data:/app/h2-data \
-v ./logs:/app/logs \
--name fhirflare-lite \
ghcr.io/sudo-jhare/fhirflare-ig-toolkit-lite:latest
Standalone Version (Includes local HAPI FHIR):
```
Bash
```bash
# Pull the latest Standalone image
docker pull ghcr.io/sudo-jhare/fhirflare-ig-toolkit-standalone:latest
# Run the Standalone version (maps ports 5000 and 8080)
# You'll need to create local directories for persistent data first:
# mkdir instance logs static static/uploads instance/hapi-h2-data
docker run -d \
-p 5000:5000 \
-p 8080:8080 \
-v ./instance:/app/instance \
-v ./static/uploads:/app/static/uploads \
-v ./instance/hapi-h2-data:/app/h2-data \
-v ./logs:/app/logs \
--name fhirflare-standalone \
ghcr.io/sudo-jhare/fhirflare-ig-toolkit-standalone:latest
```
Building from Source (Developers)
Using Windows .bat Scripts (Standalone Version Only):
First Time Setup:
Run Build and Run for first time.bat:
Code snippet
```bash
cd "<project folder>"
git clone [https://github.com/hapifhir/hapi-fhir-jpaserver-starter.git](https://github.com/hapifhir/hapi-fhir-jpaserver-starter.git) hapi-fhir-jpaserver
copy .\\hapi-fhir-Setup\\target\\classes\\application.yaml .\\hapi-fhir-jpaserver\\target\\classes\\application.yaml
mvn clean package -DskipTests=true -Pboot
docker-compose build --no-cache
docker-compose up -d
```
This clones the HAPI FHIR server, copies configuration, builds the project, and starts the containers.
Subsequent Runs:
Run Run.bat:
Code snippet
```bash
cd "<project folder>"
docker-compose up -d
```
This starts the Flask app (port 5000) and HAPI FHIR server (port 8080).
Access the Application:
* Flask UI: http://localhost:5000
* HAPI FHIR server: http://localhost:8080
* Manual Setup (Linux/MacOS/Windows):
Preparation (Standalone Version Only):
```bash
cd <project folder>
git clone [https://github.com/hapifhir/hapi-fhir-jpaserver-starter.git](https://github.com/hapifhir/hapi-fhir-jpaserver-starter.git) hapi-fhir-jpaserver
cp ./hapi-fhir-Setup/target/classes/application.yaml ./hapi-fhir-jpaserver/target/classes/application.yaml
```
Build:
```bash
# Build HAPI FHIR (Standalone Version Only)
mvn clean package -DskipTests=true -Pboot
# Build Docker Image (Specify APP_MODE=lite in docker-compose.yml for Lite version)
docker-compose build --no-cache
```
Run:
```bash
docker-compose up -d
Access the Application:
```
* Flask UI: http://localhost:5000
* HAPI FHIR server (Standalone only): http://localhost:8080
* Local Development (Without Docker):
Clone the Repository:
```bash
git clone [https://github.com/Sudo-JHare/FHIRFLARE-IG-Toolkit.git](https://github.com/Sudo-JHare/FHIRFLARE-IG-Toolkit.git)
cd FHIRFLARE-IG-Toolkit
```
Install Dependencies:
```bash
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
pip install -r requirements.txt
```
Install Node.js, GoFSH, and SUSHI (for FSH Converter):
```bash
# Example for Debian/Ubuntu
curl -fsSL [https://deb.nodesource.com/setup_18.x](https://deb.nodesource.com/setup_18.x) | sudo bash -
sudo apt-get install -y nodejs
# Install globally
npm install -g gofsh fsh-sushi
Set Environment Variables:
```
```bash
export FLASK_SECRET_KEY='your-secure-secret-key'
export API_KEY='your-api-key'
# Optional: Set APP_MODE to 'lite' if desired
# export APP_MODE='lite'
```
Initialize Directories:
```bash
mkdir -p instance static/uploads logs
# Ensure write permissions if needed
# chmod -R 777 instance static/uploads logs
```
Run the Application:
```bash
export FLASK_APP=app.py
flask run
```
Access at http://localhost:5000.
Usage
Import an IG
### Search, View Details, and Import Packages
Navigate to **Search and Import Packages** (`/search-and-import`).
1. The page will load a list of available FHIR Implementation Guide packages from a local cache or by fetching from configured registries.
* A loading animation and progress messages are shown if fetching from registries.
* The timestamp of the last cache update is displayed.
2. Use the search bar to filter packages by name or author.
3. Packages are paginated for easier Browse.
4. For each package, you can:
* View its latest official and absolute versions.
* Click on the package name to navigate to a **detailed view** (`/package-details/<name>`) showing:
* Comprehensive metadata (author, FHIR version, canonical URL, description).
* A full list of available versions with publication dates.
* Declared dependencies.
* Other packages that depend on it (dependents).
* Version history (logs).
* Directly import a specific version using the "Import" button on the search page or the details page.
5. **Cache Management:**
* A "Clear & Refresh Cache" button is available to trigger a background task (`/api/refresh-cache-task`) that clears the local database and in-memory cache and fetches the latest package information from all configured registries. Progress is shown via a live log.
* Enter a package name (e.g., hl7.fhir.au.core) and version (e.g., 1.1.0-preview).
* Choose a dependency mode:
* Current Recursive: Import all dependencies listed in package.json recursively.
* Patch Canonical Versions: Import only canonical FHIR packages (e.g., hl7.fhir.r4.core).
* Tree Shaking: Import only dependencies containing resources actually used by the main package.
* Click Import to download the package and dependencies.
Manage IGs
Go to Manage FHIR Packages (/view-igs) to view downloaded and processed IGs.
Actions:
* Process: Extract metadata (resource types, profiles, must-support elements, examples).
* Unload: Remove processed IG data from the database.
* Delete: Remove package files from the filesystem.
Duplicates are highlighted for resolution.
View Processed IGs
After processing, view IG details (/view-ig/<id>), including:
* Resource types and profiles.
* Must-support elements and examples.
* Profile relationships (compliesWithProfile, imposeProfile) if enabled (DISPLAY_PROFILE_RELATIONSHIPS).
Interactive StructureDefinition viewer (Differential, Snapshot, Must Support, Key Elements, Constraints, Terminology, Search Params).
* Validate FHIR Resources/Bundles
* Navigate to Validate FHIR Sample (/validate-sample).
Select a package (e.g., hl7.fhir.au.core#1.1.0-preview).
* Choose Single Resource or Bundle mode.
* Paste or upload FHIR JSON/XML (e.g., a Patient resource).
* Submit to view validation errors/warnings. Note: Alpha feature; report issues to GitHub (remove PHI).
* Push IGs to a FHIR Server
* Go to Push IGs (/push-igs).
Select a downloaded package.
* Enter the Target FHIR Server URL.
* Configure Authentication (None, Bearer Token).
* Choose options: Include Dependencies, Force Upload (skips comparison check), Dry Run, Verbose Log.
* Optionally filter by Resource Types (comma-separated) or Skip Specific Files (paths within package, comma/newline separated).
* Click Push to FHIR Server to upload resources. Canonical resources are checked before upload. Identical resources are skipped unless Force Upload is checked.
* Monitor progress in the live console.
* Upload Test Data
* Navigate to Upload Test Data (/upload-test-data).
* Enter the Target FHIR Server URL.
* Configure Authentication (None, Bearer Token).
* Select one or more .json, .xml files, or a single .zip file containing test resources.
* Optionally check Validate Resources Before Upload? and select a Validation Profile Package.
* Choose Upload Mode:
* Individual Resources: Uploads each resource one by one in dependency order.
* Transaction Bundle: Uploads all resources in a single transaction.
* Optionally check Use Conditional Upload (Individual Mode Only)? to use If-Match headers for updates.
* Choose Error Handling:
* Stop on First Error: Halts the process if any validation or upload fails.
* Continue on Error: Reports errors but attempts to process/upload remaining resources.
* Click Upload and Process. The tool parses files, optionally validates, analyzes dependencies, topologically sorts resources, and uploads them according to selected options.
* Monitor progress in the streaming log output.
Convert FHIR to FSH
* Navigate to FSH Converter (/fsh-converter).
Optionally select a package for context (e.g., hl7.fhir.au.core#1.1.0-preview).
Choose input mode:
Upload File: Upload a FHIR JSON/XML file.
Paste Text: Paste FHIR JSON/XML content.
Configure options:
Output Style: file-per-definition, group-by-fsh-type, group-by-profile, single-file.
Log Level: error, warn, info, debug.
FHIR Version: R4, R4B, R5, or auto-detect.
Fishing Trip: Enable round-trip validation with SUSHI, generating a comparison report.
Dependencies: Specify additional packages (e.g., hl7.fhir.us.core@6.1.0, one per line).
Indent Rules: Enable context path indentation for readable FSH.
Meta Profile: Choose only-one, first, or none for meta.profile handling.
Alias File: Upload an FSH file with aliases (e.g., $MyAlias = http://example.org).
No Alias: Disable automatic alias generation.
Click Convert to FSH to generate and display FSH output, with a waiting spinner (light/dark theme) during processing.
If Fishing Trip is enabled, view the comparison report via the "Click here for SUSHI Validation" badge button.
Download the result as a .fsh file.
Retrieve and Split Bundles
Navigate to Retrieve/Split Data (/retrieve-split-data).
Retrieve Bundles from Server:
* Enter the FHIR Server URL (defaults to the proxy if empty).
* Select one or more Resource Types to retrieve (e.g., Patient, Observation).
* Optionally check Fetch Referenced Resources.
* If checked, further optionally check Fetch Full Reference Bundles to retrieve entire bundles for each referenced type (e.g., all Patients if a Patient is referenced) instead of individual resources by ID.
* Click Retrieve Bundles.
* Monitor progress in the streaming log. A ZIP file containing the retrieved bundles/resources will be prepared for download.
Split Uploaded Bundles:
* Upload a ZIP file containing FHIR bundles (JSON format).
* Click Split Bundles.
* A ZIP file containing individual resources extracted from the bundles will be prepared for download.
* Explore FHIR Operations
* Navigate to FHIR UI Operations (/fhir-ui-operations).
Toggle between local HAPI (/fhir) or a custom FHIR server.
* Click Fetch Metadata to load the servers CapabilityStatement.
* Select a resource type (e.g., Patient, Observation) or System to view operations:
* System operations: GET /metadata, POST /, GET /_history, GET/POST /$diff, POST /$reindex, POST /$expunge, etc.
* Resource operations: GET Patient/:id, POST Observation/_search, etc.
* Use Try it out to input parameters or request bodies, then Execute to view results in JSON, XML, or narrative formats.
### Configure Embedded HAPI FHIR Server (Standalone Mode)
For users running the **Standalone version**, which includes an embedded HAPI FHIR server.
1. Navigate to **Configure HAPI FHIR** (`/config-hapi`).
2. The page displays the content of the HAPI FHIR server's `application.yaml` file.
3. You can edit the configuration directly in the text area.
* *Caution: Incorrect modifications can break the HAPI FHIR server.*
4. Click **Save Configuration** to apply your changes to the `application.yaml` file.
5. Click **Restart Tomcat** to restart the HAPI FHIR server and load the new configuration. The restart process may take a few moments.
API Usage
Import IG
Bash
curl -X POST http://localhost:5000/api/import-ig \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key" \
-d '{"package_name": "hl7.fhir.au.core", "version": "1.1.0-preview", "dependency_mode": "recursive"}'
Returns complies_with_profiles, imposed_profiles, and duplicate_packages_present info.
### Refresh Package Cache (Background Task)
```bash
curl -X POST http://localhost:5000/api/refresh-cache-task \
-H "X-API-Key: your-api-key"
```
Push IG
Bash
```bash
curl -X POST http://localhost:5000/api/push-ig \
-H "Content-Type: application/json" \
-H "Accept: application/x-ndjson" \
-H "X-API-Key: your-api-key" \
-d '{
"package_name": "hl7.fhir.au.core",
"version": "1.1.0-preview",
"fhir_server_url": "http://localhost:8080/fhir",
"include_dependencies": true,
"force_upload": false,
"dry_run": false,
"verbose": false,
"auth_type": "none"
}'
```
Returns a streaming NDJSON response with progress and final summary.
Upload Test Data
Bash
```bash
curl -X POST http://localhost:5000/api/upload-test-data \
-H "X-API-Key: your-api-key" \
-H "Accept: application/x-ndjson" \
-F "fhir_server_url=http://your-fhir-server/fhir" \
-F "auth_type=bearerToken" \
-F "auth_token=YOUR_TOKEN" \
-F "upload_mode=individual" \
-F "error_handling=continue" \
-F "validate_before_upload=true" \
-F "validation_package_id=hl7.fhir.r4.core#4.0.1" \
-F "use_conditional_uploads=true" \
-F "test_data_files=@/path/to/your/patient.json" \
-F "test_data_files=@/path/to/your/observations.zip"
```
Returns a streaming NDJSON response with progress and final summary. Uses multipart/form-data for file uploads.
Retrieve Bundles
Bash
```bash
curl -X POST http://localhost:5000/api/retrieve-bundles \
-H "X-API-Key: your-api-key" \
-H "Accept: application/x-ndjson" \
-F "fhir_server_url=http://your-fhir-server/fhir" \
-F "resources=Patient" \
-F "resources=Observation" \
-F "validate_references=true" \
-F "fetch_reference_bundles=false"
```
Returns a streaming NDJSON response with progress. The X-Zip-Path header in the final response part will contain the path to download the ZIP archive (e.g., /tmp/retrieved_bundles_datetime.zip).
Split Bundles
Bash
```bash
curl -X POST http://localhost:5000/api/split-bundles \
-H "X-API-Key: your-api-key" \
-H "Accept: application/x-ndjson" \
-F "split_bundle_zip_path=@/path/to/your/bundles.zip"
```
Returns a streaming NDJSON response. The X-Zip-Path header in the final response part will contain the path to download the ZIP archive of split resources.
Validate Resource/Bundle
Not yet exposed via API; use the UI at /validate-sample.
Configuration Options
Located in app.py:
* VALIDATE_IMPOSED_PROFILES: (Default: True) Validates resources against imposed profiles during push.
* DISPLAY_PROFILE_RELATIONSHIPS: (Default: True) Shows compliesWithProfile and imposeProfile in the UI.
* FHIR_PACKAGES_DIR: (Default: /app/instance/fhir_packages) Stores .tgz packages and metadata.
* UPLOAD_FOLDER: (Default: /app/static/uploads) Stores GoFSH output files and FSH comparison reports.
* SECRET_KEY: Required for CSRF protection and sessions. Set via environment variable or directly.
* API_KEY: Required for API authentication. Set via environment variable or directly.
* MAX_CONTENT_LENGTH: (Default: Flask default) Max size for HTTP request body (e.g., 16 * 1024 * 1024 for 16MB). Important for large uploads.
* MAX_FORM_PARTS: (Default: Werkzeug default, often 1000) Default max number of form parts. Overridden for /api/upload-test-data by CustomFormDataParser.
### Get HAPI FHIR Configuration (Standalone Mode)
```bash
curl -X GET http://localhost:5000/api/config \
-H "X-API-Key: your-api-key"
```
Save HAPI FHIR Configuration:
```bash
curl -X POST http://localhost:5000/api/config \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key" \
-d '{"your_yaml_key": "your_value", ...}' # Send the full YAML content as JSON
```
Restart HAPI FHIR Server:
```bash
curl -X POST http://localhost:5000/api/restart-tomcat \
-H "X-API-Key: your-api-key"
```
Testing
The project includes a test suite covering UI, API, database, file operations, and security.
Test Prerequisites:
pytest: For running tests.
pytest-mock: For mocking dependencies. Install: pip install pytest pytest-mock
Running Tests:
Bash
```bash
cd <project folder>
pytest tests/test_app.py -v
```
Test Coverage:
* UI Pages: Homepage, Import IG, Manage IGs, Push IGs, Validate Sample, View Processed IG, FSH Converter, Upload Test Data, Retrieve/Split Data.
* API Endpoints: POST /api/import-ig, POST /api/push-ig, GET /get-structure, GET /get-example, POST /api/upload-test-data, POST /api/retrieve-bundles, POST /api/split-bundles.
* Database: IG processing, unloading, viewing.
* File Operations: Package processing, deletion, FSH output, ZIP handling.
* Security: CSRF protection, flash messages, secret key.
* FSH Converter: Form submission, file/text input, GoFSH execution, Fishing Trip comparison.
* Upload Test Data: Parsing, dependency graph, sorting, upload modes, validation, conditional uploads.
Development Notes
Background
The toolkit addresses the need for a comprehensive FHIR IG management tool, with recent enhancements for resource validation, FSH conversion with advanced GoFSH features, flexible versioning, improved IG pushing, dependency-aware test data uploading, and bundle retrieval/splitting, making it a versatile platform for FHIR developers.
Technical Decisions
* Flask: Lightweight and flexible for web development.
* SQLite: Simple for development; consider PostgreSQL for production.
* Bootstrap 5.3.3: Responsive UI with custom styling.
* Lottie-Web: Renders themed animations for FSH conversion waiting spinner.
* GoFSH/SUSHI: Integrated via Node.js for advanced FSH conversion and round-trip validation.
* Docker: Ensures consistent deployment with Flask and HAPI FHIR.
* Flexible Versioning: Supports non-standard IG versions (e.g., -preview, -ballot).
* Live Console/Streaming: Real-time feedback for complex operations (Push, Upload Test Data, FSH, Retrieve Bundles).
* Validation: Alpha feature with ongoing FHIRPath improvements.
* Dependency Management: Uses topological sort for Upload Test Data feature.
* Form Parsing: Uses custom Werkzeug parser for Upload Test Data to handle large numbers of files.
Recent Updates
* Enhanced package search page with caching, detailed views (dependencies, dependents, version history), and background cache refresh.
* Upload Test Data Enhancements (April 2025):
* Added optional Pre-Upload Validation against selected IG profiles.
* Added optional Conditional Uploads (GET + POST/PUT w/ If-Match) for individual mode.
* Implemented robust XML parsing using fhir.resources library (when available).
* Fixed 413 Request Entity Too Large errors for large file counts using a custom Werkzeug FormDataParser.
* Path: templates/upload_test_data.html, app.py, services.py, forms.py.
* Push IG Enhancements (April 2025):
* Added semantic comparison to skip uploading identical resources.
* Added "Force Upload" option to bypass comparison.
* Improved handling of canonical resources (search before PUT/POST).
* Added filtering by specific files to skip during push.
* More detailed summary report in stream response.
* Path: templates/cp_push_igs.html, app.py, services.py.
* Waiting Spinner for FSH Converter (April 2025):
* Added a themed (light/dark) Lottie animation spinner during FSH execution.
* Path: templates/fsh_converter.html, static/animations/, static/js/lottie-web.min.js.
* Advanced FSH Converter (April 2025):
* Added support for GoFSH advanced options: --fshing-trip, --dependency, --indent, --meta-profile, --alias-file, --no-alias.
* Displays Fishing Trip comparison reports.
* Path: templates/fsh_converter.html, app.py, services.py, forms.py.
* (New) Retrieve and Split Data (May 2025):
* Added UI and API for retrieving bundles from a FHIR server by resource type.
* Added options to fetch referenced resources (individually or as full type bundles).
* Added functionality to split uploaded ZIP files of bundles into individual resources.
* Streaming log for retrieval and ZIP download for results.
* Paths: templates/retrieve_split_data.html, app.py, services.py, forms.py.
* Known Issues and Workarounds
* Favicon 404: Clear browser cache or verify /app/static/favicon.ico.
* CSRF Errors: Set FLASK_SECRET_KEY and ensure {{ form.hidden_tag() }} in forms.
* Import Fails: Check package name/version and connectivity.
* Validation Accuracy: Alpha feature; report issues to GitHub (remove PHI).
* Package Parsing: Non-standard .tgz filenames may parse incorrectly. Fallback uses name-only parsing.
* Permissions: Ensure instance/ and static/uploads/ are writable.
* GoFSH/SUSHI Errors: Check ./logs/flask_err.log for ERROR:services:GoFSH failed. Ensure valid FHIR inputs and SUSHI installation.
* Upload Test Data XML Parsing: Relies on fhir.resources library for full validation; basic parsing used as fallback. Complex XML structures might not be fully analyzed for dependencies with basic parsing. Prefer JSON for reliable dependency analysis.
* 413 Request Entity Too Large: Primarily handled by CustomFormDataParser for /api/upload-test-data. Check the parser's max_form_parts limit if still occurring. MAX_CONTENT_LENGTH in app.py controls overall size. Reverse proxy limits (client_max_body_size in Nginx) might also apply.
Future Improvements
* Upload Test Data: Improve XML parsing further (direct XML->fhir.resource object if possible), add visual progress bar, add upload order preview, implement transaction bundle size splitting, add 'Clear Target Server' option (with confirmation).
* Validation: Enhance FHIRPath for complex constraints; add API endpoint.
* Sorting: Sort IG versions in /view-igs (e.g., ascending).
* Duplicate Resolution: Options to keep latest version or merge resources.
* Production Database: Support PostgreSQL.
* Error Reporting: Detailed validation error paths in the UI.
* FSH Enhancements: Add API endpoint for FSH conversion; support inline instance construction.
* FHIR Operations: Add complex parameter support (e.g., /$diff with left/right).
* Retrieve/Split Data: Add option to filter resources during retrieval (e.g., by date, specific IDs).
Completed Items
* Testing suite with basic coverage.
* API endpoints for POST /api/import-ig and POST /api/push-ig.
* Flexible versioning (-preview, -ballot).
* CSRF fixes for forms.
* Resource validation UI (alpha).
* FSH Converter with advanced GoFSH features and waiting spinner.
* Push IG enhancements (force upload, semantic comparison, canonical handling, skip files).
* Upload Test Data feature with dependency sorting, multiple upload modes, pre-upload validation, conditional uploads, robust XML parsing, and fix for large file counts.
* Retrieve and Split Data functionality with reference fetching and ZIP download.
* Far-Distant Improvements
* Cache Service: Use Redis for IG metadata caching.
* Database Optimization: Composite index on ProcessedIg.package_name and ProcessedIg.version.
Directory Structure
```bash
FHIRFLARE-IG-Toolkit/
├── app.py # Main Flask application
├── Build and Run for first time.bat # Windows script for first-time Docker setup
├── docker-compose.yml # Docker Compose configuration
├── Dockerfile # Docker configuration
├── forms.py # Form definitions
├── LICENSE.md # Apache 2.0 License
├── README.md # Project documentation
├── requirements.txt # Python dependencies
├── Run.bat # Windows script for running Docker
├── services.py # Logic for IG import, processing, validation, pushing, FSH conversion, test data upload, retrieve/split
├── supervisord.conf # Supervisor configuration
├── hapi-fhir-Setup/
│ ├── README.md # HAPI FHIR setup instructions
│ └── target/
│ └── classes/
│ └── application.yaml # HAPI FHIR configuration
├── instance/
│ ├── fhir_ig.db # SQLite database
│ ├── fhir_ig.db.old # Database backup
│ └── fhir_packages/ # Stored IG packages and metadata
│ ├── ... (example packages) ...
├── logs/
│ ├── flask.log # Flask application logs
│ ├── flask_err.log # Flask error logs
│ ├── supervisord.log # Supervisor logs
│ ├── supervisord.pid # Supervisor PID file
│ ├── tomcat.log # Tomcat logs for HAPI FHIR
│ └── tomcat_err.log # Tomcat error logs
├── static/
│ ├── animations/
│ │ ├── loading-dark.json # Dark theme spinner animation
│ │ └── loading-light.json # Light theme spinner animation
│ ├── favicon.ico # Application favicon
│ ├── FHIRFLARE.png # Application logo
│ ├── js/
│ │ └── lottie-web.min.js # Lottie library for spinner
│ └── uploads/
│ ├── output.fsh # Generated FSH output (temp location)
│ └── fsh_output/ # GoFSH output directory
│ ├── ... (example GoFSH output) ...
├── templates/
│ ├── base.html # Base template
│ ├── cp_downloaded_igs.html # UI for managing IGs
│ ├── cp_push_igs.html # UI for pushing IGs
│ ├── cp_view_processed_ig.html # UI for viewing processed IGs
│ ├── fhir_ui.html # UI for FHIR API explorer
│ ├── fhir_ui_operations.html # UI for FHIR server operations
│ ├── fsh_converter.html # UI for FSH conversion
│ ├── import_ig.html # UI for importing IGs
│ ├── index.html # Homepage
│ ├── retrieve_split_data.html # UI for Retrieve and Split Data
│ ├── upload_test_data.html # UI for Uploading Test Data
│ ├── validate_sample.html # UI for validating resources/bundles
│ ├── config_hapi.html # UI for HAPI FHIR Configuration
│ └── _form_helpers.html # Form helper macros
├── tests/
│ └── test_app.py # Test suite
└── hapi-fhir-jpaserver/ # HAPI FHIR server resources (if Standalone)
```
Contributing
1. Fork the repository.
1. Create a feature branch (git checkout -b feature/your-feature).
1. Commit changes (git commit -m "Add your feature").
1. Push to your branch (git push origin feature/your-feature).
1. Open a Pull Request.
1. Ensure code follows PEP 8 and includes tests in tests/test_app.py.
Troubleshooting
* Favicon 404: Clear browser cache or verify /app/static/favicon.ico: docker exec -it <container_name> curl http://localhost:5000/static/favicon.ico
* CSRF Errors: Set FLASK_SECRET_KEY and ensure {{ form.hidden_tag() }} in forms.
* Import Fails: Check package name/version and connectivity.
* Validation Accuracy: Alpha feature; report issues to GitHub (remove PHI).
* Package Parsing: Non-standard .tgz filenames may parse incorrectly. Fallback uses name-only parsing.
* Permissions: Ensure instance/ and static/uploads/ are writable: chmod -R 777 instance static/uploads logs
* GoFSH/SUSHI Errors: Check ./logs/flask_err.log for ERROR:services:GoFSH failed. Ensure valid FHIR inputs and SUSHI installation: docker exec -it <container_name> sushi --version
* 413 Request Entity Too Large: Increase MAX_CONTENT_LENGTH and MAX_FORM_PARTS in app.py. If using a reverse proxy (e.g., Nginx), increase its client_max_body_size setting as well. Ensure the application/container is fully restarted/rebuilt.
License
Licensed under the Apache 2.0 License. See LICENSE.md for details.

Some files were not shown because too many files have changed in this diff Show More