mirror of
https://github.com/Sudo-JHare/FHIRFLARE-IG-Toolkit.git
synced 2025-07-30 06:55:33 +00:00
HUGE RELEASE
Added _ hapi server. Added goFSH added FHIR operations UI and FHIR based resful Client Logfiles for seperate applications for persistance and log rollover to do: advanced FSH commands Error reporting hooks
This commit is contained in:
parent
8a39603554
commit
4129d7ad7b
43
Dockerfile
43
Dockerfile
@ -1,13 +1,24 @@
|
||||
FROM python:3.9-slim
|
||||
|
||||
WORKDIR /app
|
||||
# Base image with Python and Java
|
||||
FROM tomcat:10.1-jdk17
|
||||
|
||||
# Install build dependencies and Node.js 18
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
python3 python3-pip python3-venv curl \
|
||||
&& 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 GoFSH globally
|
||||
RUN npm install -g gofsh
|
||||
|
||||
# Set up Python environment
|
||||
WORKDIR /app
|
||||
RUN python3 -m venv /app/venv
|
||||
ENV PATH="/app/venv/bin:$PATH"
|
||||
|
||||
# Copy Flask files
|
||||
COPY requirements.txt .
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
COPY app.py .
|
||||
COPY services.py .
|
||||
COPY forms.py .
|
||||
@ -15,10 +26,22 @@ COPY templates/ templates/
|
||||
COPY static/ static/
|
||||
COPY tests/ tests/
|
||||
|
||||
# Ensure /tmp is writable as a fallback
|
||||
RUN mkdir -p /tmp && chmod 777 /tmp
|
||||
# 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
|
||||
|
||||
EXPOSE 5000
|
||||
ENV FLASK_APP=app.py
|
||||
ENV FLASK_ENV=development
|
||||
CMD ["flask", "run", "--host=0.0.0.0"]
|
||||
# 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/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"]
|
131
app.py
131
app.py
@ -2,7 +2,7 @@ import sys
|
||||
import os
|
||||
sys.path.append(os.path.abspath(os.path.dirname(__file__)))
|
||||
import datetime
|
||||
from flask import Flask, render_template, request, redirect, url_for, flash, jsonify, Response, current_app
|
||||
from flask import Flask, render_template, request, redirect, url_for, flash, jsonify, Response, current_app, session, send_file
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
from flask_wtf import FlaskForm
|
||||
from flask_wtf.csrf import CSRFProtect
|
||||
@ -13,7 +13,9 @@ import requests
|
||||
import re
|
||||
import services # Restore full module import
|
||||
from services import services_bp # Keep Blueprint import
|
||||
from forms import IgImportForm, ValidationForm
|
||||
from forms import IgImportForm, ValidationForm, FSHConverterForm
|
||||
from wtforms import SubmitField
|
||||
import tempfile
|
||||
|
||||
# Set up logging
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
@ -27,6 +29,7 @@ app.config['FHIR_PACKAGES_DIR'] = '/app/instance/fhir_packages'
|
||||
app.config['API_KEY'] = os.environ.get('API_KEY', 'your-fallback-api-key-here')
|
||||
app.config['VALIDATE_IMPOSED_PROFILES'] = True
|
||||
app.config['DISPLAY_PROFILE_RELATIONSHIPS'] = True
|
||||
app.config['UPLOAD_FOLDER'] = '/app/static/uploads' # For GoFSH output
|
||||
|
||||
# Ensure directories exist and are writable
|
||||
instance_path = '/app/instance'
|
||||
@ -40,6 +43,7 @@ try:
|
||||
logger.debug(f"Flask instance folder path: {instance_folder_path}")
|
||||
os.makedirs(instance_folder_path, exist_ok=True)
|
||||
os.makedirs(packages_path, exist_ok=True)
|
||||
os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)
|
||||
logger.debug(f"Directories created/verified: Instance: {instance_folder_path}, Packages: {packages_path}")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to create/verify directories: {e}", exc_info=True)
|
||||
@ -891,5 +895,128 @@ def create_db():
|
||||
with app.app_context():
|
||||
create_db()
|
||||
|
||||
|
||||
class FhirRequestForm(FlaskForm):
|
||||
submit = SubmitField('Send Request')
|
||||
|
||||
@app.route('/fhir')
|
||||
def fhir_ui():
|
||||
form = FhirRequestForm()
|
||||
return render_template('fhir_ui.html', form=form, site_name='FHIRFLARE IG Toolkit', now=datetime.datetime.now())
|
||||
|
||||
@app.route('/fhir-ui-operations')
|
||||
def fhir_ui_operations():
|
||||
form = FhirRequestForm()
|
||||
return render_template('fhir_ui_operations.html', form=form, site_name='FHIRFLARE IG Toolkit', now=datetime.datetime.now())
|
||||
|
||||
@app.route('/fhir/<path:subpath>', methods=['GET', 'POST', 'PUT', 'DELETE'])
|
||||
def proxy_hapi(subpath):
|
||||
# Clean subpath to remove r4/, fhir/, leading/trailing slashes
|
||||
clean_subpath = subpath.replace('r4/', '').replace('fhir/', '').strip('/')
|
||||
hapi_url = f"http://localhost:8080/fhir/{clean_subpath}" if clean_subpath else "http://localhost:8080/fhir"
|
||||
headers = {k: v for k, v in request.headers.items() if k != 'Host'}
|
||||
logger.debug(f"Proxying request: {request.method} {hapi_url}")
|
||||
try:
|
||||
response = requests.request(
|
||||
method=request.method,
|
||||
url=hapi_url,
|
||||
headers=headers,
|
||||
data=request.get_data(),
|
||||
cookies=request.cookies,
|
||||
allow_redirects=False
|
||||
)
|
||||
response.raise_for_status()
|
||||
# Strip hop-by-hop headers to avoid chunked encoding issues
|
||||
response_headers = {
|
||||
k: v for k, v in response.headers.items()
|
||||
if k.lower() not in (
|
||||
'transfer-encoding', 'connection', 'content-encoding',
|
||||
'content-length', 'keep-alive', 'proxy-authenticate',
|
||||
'proxy-authorization', 'te', 'trailers', 'upgrade'
|
||||
)
|
||||
}
|
||||
# Ensure Content-Length matches the actual body
|
||||
response_headers['Content-Length'] = str(len(response.content))
|
||||
logger.debug(f"Response: {response.status_code} {response.reason}")
|
||||
return response.content, response.status_code, response_headers.items()
|
||||
except requests.RequestException as e:
|
||||
logger.error(f"Proxy error: {str(e)}")
|
||||
return jsonify({'error': str(e)}), response.status_code if 'response' in locals() else 500
|
||||
|
||||
@app.route('/fsh-converter', methods=['GET', 'POST'])
|
||||
def fsh_converter():
|
||||
form = FSHConverterForm()
|
||||
error = None
|
||||
fsh_output = None
|
||||
|
||||
# Populate package choices
|
||||
packages = []
|
||||
packages_dir = app.config['FHIR_PACKAGES_DIR']
|
||||
if os.path.exists(packages_dir):
|
||||
for filename in os.listdir(packages_dir):
|
||||
if filename.endswith('.tgz'):
|
||||
try:
|
||||
with tarfile.open(os.path.join(packages_dir, filename), 'r:gz') as tar:
|
||||
package_json = tar.extractfile('package/package.json')
|
||||
if package_json:
|
||||
pkg_info = json.load(package_json)
|
||||
name = pkg_info.get('name')
|
||||
version = pkg_info.get('version')
|
||||
if name and version:
|
||||
packages.append((f"{name}#{version}", f"{name}#{version}"))
|
||||
except Exception as e:
|
||||
logger.warning(f"Error reading package {filename}: {e}")
|
||||
continue
|
||||
form.package.choices = [('', 'None')] + sorted(packages, key=lambda x: x[0])
|
||||
|
||||
if form.validate_on_submit():
|
||||
input_mode = form.input_mode.data
|
||||
fhir_file = form.fhir_file.data
|
||||
fhir_text = form.fhir_text.data
|
||||
output_style = form.output_style.data
|
||||
log_level = form.log_level.data
|
||||
fhir_version = form.fhir_version.data or None
|
||||
|
||||
# Process input
|
||||
input_path, temp_dir, error = services.process_fhir_input(input_mode, fhir_file, fhir_text)
|
||||
if error:
|
||||
flash(error, 'error')
|
||||
return render_template('fsh_converter.html', form=form, error=error, site_name='FHIRFLARE IG Toolkit', now=datetime.datetime.now())
|
||||
|
||||
# Run GoFSH
|
||||
output_dir = os.path.join(app.config['UPLOAD_FOLDER'], 'fsh_output')
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
fsh_output, error = services.run_gofsh(input_path, output_dir, output_style, log_level, fhir_version)
|
||||
|
||||
# Clean up temporary files
|
||||
if temp_dir and os.path.exists(temp_dir):
|
||||
import shutil
|
||||
shutil.rmtree(temp_dir)
|
||||
|
||||
if error:
|
||||
flash(error, 'error')
|
||||
return render_template('fsh_converter.html', form=form, error=error, site_name='FHIRFLARE IG Toolkit', now=datetime.datetime.now())
|
||||
|
||||
# Store output for download
|
||||
session['fsh_output'] = fsh_output
|
||||
flash('Conversion successful!', 'success')
|
||||
return render_template('fsh_converter.html', form=form, fsh_output=fsh_output, site_name='FHIRFLARE IG Toolkit', now=datetime.datetime.now())
|
||||
|
||||
return render_template('fsh_converter.html', form=form, error=error, site_name='FHIRFLARE IG Toolkit', now=datetime.datetime.now())
|
||||
|
||||
@app.route('/download-fsh')
|
||||
def download_fsh():
|
||||
fsh_output = session.get('fsh_output', '')
|
||||
if not fsh_output:
|
||||
flash('No FSH output available for download.', 'error')
|
||||
return redirect(url_for('fsh_converter'))
|
||||
|
||||
temp_file = os.path.join(app.config['UPLOAD_FOLDER'], 'output.fsh')
|
||||
with open(temp_file, 'w', encoding='utf-8') as f:
|
||||
f.write(fsh_output)
|
||||
|
||||
return send_file(temp_file, as_attachment=True, download_name='output.fsh')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(host='0.0.0.0', port=5000, debug=False)
|
19
docker-compose.yml
Normal file
19
docker-compose.yml
Normal file
@ -0,0 +1,19 @@
|
||||
version: '3.8'
|
||||
services:
|
||||
fhirflare:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
ports:
|
||||
- "5000:5000"
|
||||
- "8080:8080"
|
||||
volumes:
|
||||
- ./instance:/app/instance
|
||||
- ./static/uploads:/app/static/uploads
|
||||
- ./hapi-fhir-jpaserver/target/h2-data:/app/h2-data
|
||||
- ./logs:/app/logs
|
||||
environment:
|
||||
- FLASK_APP=app.py
|
||||
- FLASK_ENV=development
|
||||
- NODE_PATH=/usr/lib/node_modules
|
||||
command: supervisord -c /etc/supervisord.conf
|
60
forms.py
60
forms.py
@ -1,9 +1,10 @@
|
||||
# forms.py
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms import StringField, SelectField, TextAreaField, BooleanField, SubmitField
|
||||
from wtforms.validators import DataRequired, Regexp, ValidationError
|
||||
from wtforms import StringField, SelectField, TextAreaField, BooleanField, SubmitField, FileField
|
||||
from wtforms.validators import DataRequired, Regexp, ValidationError, Optional
|
||||
import json
|
||||
|
||||
# Existing forms (IgImportForm, ValidationForm) remain unchanged
|
||||
class IgImportForm(FlaskForm):
|
||||
package_name = StringField('Package Name', validators=[
|
||||
DataRequired(),
|
||||
@ -45,4 +46,57 @@ def validate_json(data, mode):
|
||||
except json.JSONDecodeError:
|
||||
raise ValidationError("Invalid JSON format.")
|
||||
except ValueError as e:
|
||||
raise ValidationError(str(e))
|
||||
raise ValidationError(str(e))
|
||||
|
||||
class FSHConverterForm(FlaskForm):
|
||||
package = SelectField('FHIR Package (Optional)', choices=[('', 'None')], validators=[Optional()])
|
||||
input_mode = SelectField('Input Mode', choices=[
|
||||
('file', 'Upload File'),
|
||||
('text', 'Paste Text')
|
||||
], validators=[DataRequired()])
|
||||
fhir_file = FileField('FHIR Resource File (JSON/XML)', validators=[Optional()])
|
||||
fhir_text = TextAreaField('FHIR Resource Text (JSON/XML)', validators=[Optional()])
|
||||
output_style = SelectField('Output Style', choices=[
|
||||
('file-per-definition', 'File per Definition'),
|
||||
('group-by-fsh-type', 'Group by FSH Type'),
|
||||
('group-by-profile', 'Group by Profile'),
|
||||
('single-file', 'Single File')
|
||||
], validators=[DataRequired()])
|
||||
log_level = SelectField('Log Level', choices=[
|
||||
('error', 'Error'),
|
||||
('warn', 'Warn'),
|
||||
('info', 'Info'),
|
||||
('debug', 'Debug')
|
||||
], validators=[DataRequired()])
|
||||
fhir_version = SelectField('FHIR Version', choices=[
|
||||
('', 'Auto-detect'),
|
||||
('4.0.1', 'R4'),
|
||||
('4.3.0', 'R4B'),
|
||||
('5.0.0', 'R5')
|
||||
], validators=[Optional()])
|
||||
submit = SubmitField('Convert to FSH')
|
||||
|
||||
def validate(self, extra_validators=None):
|
||||
if not super().validate(extra_validators):
|
||||
return False
|
||||
if self.input_mode.data == 'file' and not self.fhir_file.data:
|
||||
self.fhir_file.errors.append('File is required when input mode is Upload File.')
|
||||
return False
|
||||
if self.input_mode.data == 'text' and not self.fhir_text.data:
|
||||
self.fhir_text.errors.append('Text input is required when input mode is Paste Text.')
|
||||
return False
|
||||
if self.input_mode.data == 'text' and self.fhir_text.data:
|
||||
try:
|
||||
content = self.fhir_text.data.strip()
|
||||
if content.startswith('{'):
|
||||
json.loads(content)
|
||||
elif content.startswith('<'):
|
||||
import xml.etree.ElementTree as ET
|
||||
ET.fromstring(content)
|
||||
else:
|
||||
self.fhir_text.errors.append('Text input must be valid JSON or XML.')
|
||||
return False
|
||||
except (json.JSONDecodeError, ET.ParseError):
|
||||
self.fhir_text.errors.append('Invalid JSON or XML format.')
|
||||
return False
|
||||
return True
|
Binary file not shown.
@ -0,0 +1,22 @@
|
||||
{
|
||||
"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-04-17T04:04:45.070781+00:00"
|
||||
}
|
BIN
instance/fhir_packages/hl7.fhir.au.base-5.1.0-preview.tgz
Normal file
BIN
instance/fhir_packages/hl7.fhir.au.base-5.1.0-preview.tgz
Normal file
Binary file not shown.
@ -1,7 +1,7 @@
|
||||
{
|
||||
"package_name": "hl7.fhir.au.core",
|
||||
"version": "1.1.0-preview",
|
||||
"dependency_mode": "tree-shaking",
|
||||
"dependency_mode": "recursive",
|
||||
"imported_dependencies": [
|
||||
{
|
||||
"name": "hl7.fhir.r4.core",
|
||||
@ -30,5 +30,5 @@
|
||||
],
|
||||
"complies_with_profiles": [],
|
||||
"imposed_profiles": [],
|
||||
"timestamp": "2025-04-15T00:27:41.653977+00:00"
|
||||
"timestamp": "2025-04-17T04:04:20.523471+00:00"
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
{
|
||||
"package_name": "hl7.fhir.r4.core",
|
||||
"version": "4.0.1",
|
||||
"dependency_mode": "tree-shaking",
|
||||
"dependency_mode": "recursive",
|
||||
"imported_dependencies": [],
|
||||
"complies_with_profiles": [],
|
||||
"imposed_profiles": [],
|
||||
"timestamp": "2025-04-15T00:27:55.537600+00:00"
|
||||
"timestamp": "2025-04-17T04:04:29.230227+00:00"
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
{
|
||||
"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-04-17T04:04:41.588025+00:00"
|
||||
}
|
BIN
instance/fhir_packages/hl7.fhir.uv.extensions.r4-5.2.0.tgz
Normal file
BIN
instance/fhir_packages/hl7.fhir.uv.extensions.r4-5.2.0.tgz
Normal file
Binary file not shown.
22
instance/fhir_packages/hl7.fhir.uv.ipa-1.0.0.metadata.json
Normal file
22
instance/fhir_packages/hl7.fhir.uv.ipa-1.0.0.metadata.json
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"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-04-17T04:04:49.395594+00:00"
|
||||
}
|
BIN
instance/fhir_packages/hl7.fhir.uv.ipa-1.0.0.tgz
Normal file
BIN
instance/fhir_packages/hl7.fhir.uv.ipa-1.0.0.tgz
Normal file
Binary file not shown.
@ -0,0 +1,14 @@
|
||||
{
|
||||
"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-04-17T04:04:56.492512+00:00"
|
||||
}
|
BIN
instance/fhir_packages/hl7.fhir.uv.smart-app-launch-2.0.0.tgz
Normal file
BIN
instance/fhir_packages/hl7.fhir.uv.smart-app-launch-2.0.0.tgz
Normal file
Binary file not shown.
@ -0,0 +1,18 @@
|
||||
{
|
||||
"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-04-17T04:04:46.943079+00:00"
|
||||
}
|
BIN
instance/fhir_packages/hl7.fhir.uv.smart-app-launch-2.1.0.tgz
Normal file
BIN
instance/fhir_packages/hl7.fhir.uv.smart-app-launch-2.1.0.tgz
Normal file
Binary file not shown.
@ -0,0 +1,14 @@
|
||||
{
|
||||
"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-04-17T04:04:54.857273+00:00"
|
||||
}
|
BIN
instance/fhir_packages/hl7.terminology.r4-5.0.0.tgz
Normal file
BIN
instance/fhir_packages/hl7.terminology.r4-5.0.0.tgz
Normal file
Binary file not shown.
@ -0,0 +1,14 @@
|
||||
{
|
||||
"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-04-17T04:04:37.703082+00:00"
|
||||
}
|
BIN
instance/fhir_packages/hl7.terminology.r4-6.2.0.tgz
Normal file
BIN
instance/fhir_packages/hl7.terminology.r4-6.2.0.tgz
Normal file
Binary file not shown.
4
logs/flask.log
Normal file
4
logs/flask.log
Normal file
@ -0,0 +1,4 @@
|
||||
* Serving Flask app 'app'
|
||||
* Debug mode: off
|
||||
* Serving Flask app 'app'
|
||||
* Debug mode: off
|
258
logs/flask_err.log
Normal file
258
logs/flask_err.log
Normal file
@ -0,0 +1,258 @@
|
||||
DEBUG:__main__:Instance path configuration: /app/instance
|
||||
DEBUG:__main__:Database URI: sqlite:////app/instance/fhir_ig.db
|
||||
DEBUG:__main__:Packages path: /app/instance/fhir_packages
|
||||
DEBUG:__main__:Flask instance folder path: /app/instance
|
||||
DEBUG:__main__:Directories created/verified: Instance: /app/instance, Packages: /app/instance/fhir_packages
|
||||
DEBUG:__main__:Attempting to create database tables for URI: sqlite:////app/instance/fhir_ig.db
|
||||
INFO:__main__:Database tables created successfully (if they didn't exist).
|
||||
INFO:werkzeug:[31m[1mWARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.[0m
|
||||
* Running on all addresses (0.0.0.0)
|
||||
* Running on http://127.0.0.1:5000
|
||||
* Running on http://172.19.0.2:5000
|
||||
INFO:werkzeug:[33mPress CTRL+C to quit[0m
|
||||
INFO:werkzeug:172.19.0.1 - - [17/Apr/2025 06:25:30] "GET /fhir-ui-operations HTTP/1.1" 200 -
|
||||
INFO:werkzeug:172.19.0.1 - - [17/Apr/2025 06:25:30] "GET /static/FHIRFLARE.png HTTP/1.1" 200 -
|
||||
INFO:werkzeug:172.19.0.1 - - [17/Apr/2025 06:25:31] "GET /static/favicon.ico HTTP/1.1" 200 -
|
||||
INFO:werkzeug:172.19.0.1 - - [17/Apr/2025 06:25:36] "GET /fsh-converter HTTP/1.1" 200 -
|
||||
INFO:werkzeug:172.19.0.1 - - [17/Apr/2025 06:25:36] "[36mGET /static/FHIRFLARE.png HTTP/1.1[0m" 304 -
|
||||
DEBUG:__main__:Scanning packages directory: /app/instance/fhir_packages
|
||||
DEBUG:services:Parsed 'hl7.fhir.au.base-5.1.0-preview.tgz' -> name='hl7.fhir.au.base-5.1.0', version='preview'
|
||||
DEBUG:services:Parsed 'hl7.fhir.au.core-1.1.0-preview.tgz' -> name='hl7.fhir.au.core-1.1.0', version='preview'
|
||||
DEBUG:services:Parsed 'hl7.fhir.r4.core-4.0.1.tgz' -> name='hl7.fhir.r4.core', version='4.0.1'
|
||||
DEBUG:services:Parsed 'hl7.fhir.uv.extensions.r4-5.2.0.tgz' -> name='hl7.fhir.uv.extensions.r4', version='5.2.0'
|
||||
DEBUG:services:Parsed 'hl7.fhir.uv.ipa-1.0.0.tgz' -> name='hl7.fhir.uv.ipa', version='1.0.0'
|
||||
DEBUG:services:Parsed 'hl7.fhir.uv.smart-app-launch-2.0.0.tgz' -> name='hl7.fhir.uv.smart-app-launch', version='2.0.0'
|
||||
DEBUG:services:Parsed 'hl7.fhir.uv.smart-app-launch-2.1.0.tgz' -> name='hl7.fhir.uv.smart-app-launch', version='2.1.0'
|
||||
DEBUG:services:Parsed 'hl7.terminology.r4-5.0.0.tgz' -> name='hl7.terminology.r4', version='5.0.0'
|
||||
DEBUG:services:Parsed 'hl7.terminology.r4-6.2.0.tgz' -> name='hl7.terminology.r4', version='6.2.0'
|
||||
DEBUG:__main__:Found packages: [{'name': 'hl7.fhir.au.base', 'version': '5.1.0-preview', 'filename': 'hl7.fhir.au.base-5.1.0-preview.tgz'}, {'name': 'hl7.fhir.au.core', 'version': '1.1.0-preview', 'filename': 'hl7.fhir.au.core-1.1.0-preview.tgz'}, {'name': 'hl7.fhir.r4.core', 'version': '4.0.1', 'filename': 'hl7.fhir.r4.core-4.0.1.tgz'}, {'name': 'hl7.fhir.uv.extensions.r4', 'version': '5.2.0', 'filename': 'hl7.fhir.uv.extensions.r4-5.2.0.tgz'}, {'name': 'hl7.fhir.uv.ipa', 'version': '1.0.0', 'filename': 'hl7.fhir.uv.ipa-1.0.0.tgz'}, {'name': 'hl7.fhir.uv.smart-app-launch', 'version': '2.0.0', 'filename': 'hl7.fhir.uv.smart-app-launch-2.0.0.tgz'}, {'name': 'hl7.fhir.uv.smart-app-launch', 'version': '2.1.0', 'filename': 'hl7.fhir.uv.smart-app-launch-2.1.0.tgz'}, {'name': 'hl7.terminology.r4', 'version': '5.0.0', 'filename': 'hl7.terminology.r4-5.0.0.tgz'}, {'name': 'hl7.terminology.r4', 'version': '6.2.0', 'filename': 'hl7.terminology.r4-6.2.0.tgz'}]
|
||||
DEBUG:__main__:Errors during package listing: []
|
||||
DEBUG:__main__:Duplicate groups: {'hl7.fhir.uv.smart-app-launch': ['2.0.0', '2.1.0'], 'hl7.terminology.r4': ['5.0.0', '6.2.0']}
|
||||
INFO:werkzeug:172.19.0.1 - - [17/Apr/2025 06:25:56] "GET /view-igs HTTP/1.1" 200 -
|
||||
INFO:werkzeug:172.19.0.1 - - [17/Apr/2025 06:25:56] "[36mGET /static/FHIRFLARE.png HTTP/1.1[0m" 304 -
|
||||
DEBUG:__main__:Viewing IG hl7.fhir.au.core-1.1.0#preview: 25 profiles, 17 base resources, 1 optional elements
|
||||
INFO:werkzeug:172.19.0.1 - - [17/Apr/2025 06:25:59] "GET /view-ig/1 HTTP/1.1" 200 -
|
||||
INFO:werkzeug:172.19.0.1 - - [17/Apr/2025 06:25:59] "[36mGET /static/FHIRFLARE.png HTTP/1.1[0m" 304 -
|
||||
DEBUG:__main__:Attempting to find SD for 'Practitioner' in hl7.fhir.au.core-1.1.0-preview.tgz
|
||||
DEBUG:services:Searching for SD matching 'Practitioner' with profile 'None' in hl7.fhir.au.core-1.1.0-preview.tgz
|
||||
DEBUG:services:Found SD: id=au-core-allergyintolerance, name=AUCoreAllergyIntolerance, type=AllergyIntolerance, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-allergyintolerance, path=package/StructureDefinition-au-core-allergyintolerance.json
|
||||
DEBUG:services:Found SD: id=au-core-bloodpressure, name=AUCoreBloodPressure, type=Observation, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-bloodpressure, path=package/StructureDefinition-au-core-bloodpressure.json
|
||||
DEBUG:services:Found SD: id=au-core-bodyheight, name=AUCoreBodyHeight, type=Observation, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-bodyheight, path=package/StructureDefinition-au-core-bodyheight.json
|
||||
DEBUG:services:Found SD: id=au-core-bodytemp, name=AUCoreBodyTemperature, type=Observation, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-bodytemp, path=package/StructureDefinition-au-core-bodytemp.json
|
||||
DEBUG:services:Found SD: id=au-core-bodyweight, name=AUCoreBodyWeight, type=Observation, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-bodyweight, path=package/StructureDefinition-au-core-bodyweight.json
|
||||
DEBUG:services:Found SD: id=au-core-condition, name=AUCoreCondition, type=Condition, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-condition, path=package/StructureDefinition-au-core-condition.json
|
||||
DEBUG:services:Found SD: id=au-core-diagnosticresult-path, name=AUCorePathologyResult, type=Observation, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-diagnosticresult-path, path=package/StructureDefinition-au-core-diagnosticresult-path.json
|
||||
DEBUG:services:Found SD: id=au-core-diagnosticresult, name=AUCoreDiagnosticResult, type=Observation, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-diagnosticresult, path=package/StructureDefinition-au-core-diagnosticresult.json
|
||||
DEBUG:services:Found SD: id=au-core-encounter, name=AUCoreEncounter, type=Encounter, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-encounter, path=package/StructureDefinition-au-core-encounter.json
|
||||
DEBUG:services:Found SD: id=au-core-heartrate, name=AUCoreHeartRate, type=Observation, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-heartrate, path=package/StructureDefinition-au-core-heartrate.json
|
||||
DEBUG:services:Found SD: id=au-core-immunization, name=AUCoreImmunization, type=Immunization, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-immunization, path=package/StructureDefinition-au-core-immunization.json
|
||||
DEBUG:services:Found SD: id=au-core-location, name=AUCoreLocation, type=Location, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-location, path=package/StructureDefinition-au-core-location.json
|
||||
DEBUG:services:Found SD: id=au-core-medication, name=AUCoreMedication, type=Medication, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-medication, path=package/StructureDefinition-au-core-medication.json
|
||||
DEBUG:services:Found SD: id=au-core-medicationrequest, name=AUCoreMedicationRequest, type=MedicationRequest, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-medicationrequest, path=package/StructureDefinition-au-core-medicationrequest.json
|
||||
DEBUG:services:Found SD: id=au-core-medicationstatement, name=AUCoreMedicationStatement, type=MedicationStatement, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-medicationstatement, path=package/StructureDefinition-au-core-medicationstatement.json
|
||||
DEBUG:services:Found SD: id=au-core-organization, name=AUCoreOrganization, type=Organization, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-organization, path=package/StructureDefinition-au-core-organization.json
|
||||
DEBUG:services:Found SD: id=au-core-patient, name=AUCorePatient, type=Patient, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-patient, path=package/StructureDefinition-au-core-patient.json
|
||||
DEBUG:services:Found SD: id=au-core-practitioner, name=AUCorePractitioner, type=Practitioner, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-practitioner, path=package/StructureDefinition-au-core-practitioner.json
|
||||
INFO:services:Found matching SD for 'Practitioner' at path: package/StructureDefinition-au-core-practitioner.json
|
||||
DEBUG:services:Found SD: id=au-core-practitionerrole, name=AUCorePractitionerRole, type=PractitionerRole, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-practitionerrole, path=package/StructureDefinition-au-core-practitionerrole.json
|
||||
INFO:services:Found matching SD for 'Practitioner' at path: package/StructureDefinition-au-core-practitionerrole.json
|
||||
DEBUG:services:Found SD: id=au-core-procedure, name=AUCoreProcedure, type=Procedure, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-procedure, path=package/StructureDefinition-au-core-procedure.json
|
||||
DEBUG:services:Found SD: id=au-core-relatedperson, name=AUCoreRelatedPerson, type=RelatedPerson, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-relatedperson, path=package/StructureDefinition-au-core-relatedperson.json
|
||||
DEBUG:services:Found SD: id=au-core-resprate, name=AUCoreRespirationRate, type=Observation, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-resprate, path=package/StructureDefinition-au-core-resprate.json
|
||||
DEBUG:services:Found SD: id=au-core-rsg-sexassignedab, name=AUCoreSexAssignedAtBirth, type=Extension, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-rsg-sexassignedab, path=package/StructureDefinition-au-core-rsg-sexassignedab.json
|
||||
DEBUG:services:Found SD: id=au-core-smokingstatus, name=AUCoreSmokingStatus, type=Observation, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-smokingstatus, path=package/StructureDefinition-au-core-smokingstatus.json
|
||||
DEBUG:services:Found SD: id=au-core-waistcircum, name=AUCoreWaistCircumference, type=Observation, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-waistcircum, path=package/StructureDefinition-au-core-waistcircum.json
|
||||
DEBUG:__main__:Retrieved 0 Must Support paths for 'Practitioner' from processed IG hl7.fhir.au.core-1.1.0#preview
|
||||
INFO:werkzeug:172.19.0.1 - - [17/Apr/2025 06:26:04] "GET /get-structure?package_name=hl7.fhir.au.core-1.1.0&package_version=preview&resource_type=Practitioner HTTP/1.1" 200 -
|
||||
DEBUG:__main__:Attempting to find SD for 'au-core-practitioner' in hl7.fhir.au.core-1.1.0-preview.tgz
|
||||
DEBUG:services:Searching for SD matching 'au-core-practitioner' with profile 'None' in hl7.fhir.au.core-1.1.0-preview.tgz
|
||||
DEBUG:services:Found SD: id=au-core-allergyintolerance, name=AUCoreAllergyIntolerance, type=AllergyIntolerance, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-allergyintolerance, path=package/StructureDefinition-au-core-allergyintolerance.json
|
||||
DEBUG:services:Found SD: id=au-core-bloodpressure, name=AUCoreBloodPressure, type=Observation, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-bloodpressure, path=package/StructureDefinition-au-core-bloodpressure.json
|
||||
DEBUG:services:Found SD: id=au-core-bodyheight, name=AUCoreBodyHeight, type=Observation, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-bodyheight, path=package/StructureDefinition-au-core-bodyheight.json
|
||||
DEBUG:services:Found SD: id=au-core-bodytemp, name=AUCoreBodyTemperature, type=Observation, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-bodytemp, path=package/StructureDefinition-au-core-bodytemp.json
|
||||
DEBUG:services:Found SD: id=au-core-bodyweight, name=AUCoreBodyWeight, type=Observation, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-bodyweight, path=package/StructureDefinition-au-core-bodyweight.json
|
||||
DEBUG:services:Found SD: id=au-core-condition, name=AUCoreCondition, type=Condition, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-condition, path=package/StructureDefinition-au-core-condition.json
|
||||
DEBUG:services:Found SD: id=au-core-diagnosticresult-path, name=AUCorePathologyResult, type=Observation, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-diagnosticresult-path, path=package/StructureDefinition-au-core-diagnosticresult-path.json
|
||||
DEBUG:services:Found SD: id=au-core-diagnosticresult, name=AUCoreDiagnosticResult, type=Observation, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-diagnosticresult, path=package/StructureDefinition-au-core-diagnosticresult.json
|
||||
DEBUG:services:Found SD: id=au-core-encounter, name=AUCoreEncounter, type=Encounter, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-encounter, path=package/StructureDefinition-au-core-encounter.json
|
||||
DEBUG:services:Found SD: id=au-core-heartrate, name=AUCoreHeartRate, type=Observation, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-heartrate, path=package/StructureDefinition-au-core-heartrate.json
|
||||
DEBUG:services:Found SD: id=au-core-immunization, name=AUCoreImmunization, type=Immunization, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-immunization, path=package/StructureDefinition-au-core-immunization.json
|
||||
DEBUG:services:Found SD: id=au-core-location, name=AUCoreLocation, type=Location, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-location, path=package/StructureDefinition-au-core-location.json
|
||||
DEBUG:services:Found SD: id=au-core-medication, name=AUCoreMedication, type=Medication, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-medication, path=package/StructureDefinition-au-core-medication.json
|
||||
DEBUG:services:Found SD: id=au-core-medicationrequest, name=AUCoreMedicationRequest, type=MedicationRequest, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-medicationrequest, path=package/StructureDefinition-au-core-medicationrequest.json
|
||||
DEBUG:services:Found SD: id=au-core-medicationstatement, name=AUCoreMedicationStatement, type=MedicationStatement, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-medicationstatement, path=package/StructureDefinition-au-core-medicationstatement.json
|
||||
DEBUG:services:Found SD: id=au-core-organization, name=AUCoreOrganization, type=Organization, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-organization, path=package/StructureDefinition-au-core-organization.json
|
||||
DEBUG:services:Found SD: id=au-core-patient, name=AUCorePatient, type=Patient, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-patient, path=package/StructureDefinition-au-core-patient.json
|
||||
DEBUG:services:Found SD: id=au-core-practitioner, name=AUCorePractitioner, type=Practitioner, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-practitioner, path=package/StructureDefinition-au-core-practitioner.json
|
||||
INFO:services:Found matching SD for 'au-core-practitioner' at path: package/StructureDefinition-au-core-practitioner.json
|
||||
DEBUG:services:Found SD: id=au-core-practitionerrole, name=AUCorePractitionerRole, type=PractitionerRole, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-practitionerrole, path=package/StructureDefinition-au-core-practitionerrole.json
|
||||
INFO:services:Found matching SD for 'au-core-practitioner' at path: package/StructureDefinition-au-core-practitionerrole.json
|
||||
DEBUG:services:Found SD: id=au-core-procedure, name=AUCoreProcedure, type=Procedure, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-procedure, path=package/StructureDefinition-au-core-procedure.json
|
||||
DEBUG:services:Found SD: id=au-core-relatedperson, name=AUCoreRelatedPerson, type=RelatedPerson, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-relatedperson, path=package/StructureDefinition-au-core-relatedperson.json
|
||||
DEBUG:services:Found SD: id=au-core-resprate, name=AUCoreRespirationRate, type=Observation, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-resprate, path=package/StructureDefinition-au-core-resprate.json
|
||||
DEBUG:services:Found SD: id=au-core-rsg-sexassignedab, name=AUCoreSexAssignedAtBirth, type=Extension, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-rsg-sexassignedab, path=package/StructureDefinition-au-core-rsg-sexassignedab.json
|
||||
DEBUG:services:Found SD: id=au-core-smokingstatus, name=AUCoreSmokingStatus, type=Observation, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-smokingstatus, path=package/StructureDefinition-au-core-smokingstatus.json
|
||||
DEBUG:services:Found SD: id=au-core-waistcircum, name=AUCoreWaistCircumference, type=Observation, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-waistcircum, path=package/StructureDefinition-au-core-waistcircum.json
|
||||
DEBUG:__main__:Retrieved 5 Must Support paths for 'au-core-practitioner' from processed IG hl7.fhir.au.core-1.1.0#preview
|
||||
INFO:werkzeug:172.19.0.1 - - [17/Apr/2025 06:26:15] "GET /get-structure?package_name=hl7.fhir.au.core-1.1.0&package_version=preview&resource_type=au-core-practitioner HTTP/1.1" 200 -
|
||||
DEBUG:__main__:Attempting to find SD for 'au-core-patient' in hl7.fhir.au.core-1.1.0-preview.tgz
|
||||
DEBUG:services:Searching for SD matching 'au-core-patient' with profile 'None' in hl7.fhir.au.core-1.1.0-preview.tgz
|
||||
DEBUG:services:Found SD: id=au-core-allergyintolerance, name=AUCoreAllergyIntolerance, type=AllergyIntolerance, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-allergyintolerance, path=package/StructureDefinition-au-core-allergyintolerance.json
|
||||
DEBUG:services:Found SD: id=au-core-bloodpressure, name=AUCoreBloodPressure, type=Observation, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-bloodpressure, path=package/StructureDefinition-au-core-bloodpressure.json
|
||||
DEBUG:services:Found SD: id=au-core-bodyheight, name=AUCoreBodyHeight, type=Observation, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-bodyheight, path=package/StructureDefinition-au-core-bodyheight.json
|
||||
DEBUG:services:Found SD: id=au-core-bodytemp, name=AUCoreBodyTemperature, type=Observation, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-bodytemp, path=package/StructureDefinition-au-core-bodytemp.json
|
||||
DEBUG:services:Found SD: id=au-core-bodyweight, name=AUCoreBodyWeight, type=Observation, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-bodyweight, path=package/StructureDefinition-au-core-bodyweight.json
|
||||
DEBUG:services:Found SD: id=au-core-condition, name=AUCoreCondition, type=Condition, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-condition, path=package/StructureDefinition-au-core-condition.json
|
||||
DEBUG:services:Found SD: id=au-core-diagnosticresult-path, name=AUCorePathologyResult, type=Observation, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-diagnosticresult-path, path=package/StructureDefinition-au-core-diagnosticresult-path.json
|
||||
DEBUG:services:Found SD: id=au-core-diagnosticresult, name=AUCoreDiagnosticResult, type=Observation, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-diagnosticresult, path=package/StructureDefinition-au-core-diagnosticresult.json
|
||||
DEBUG:services:Found SD: id=au-core-encounter, name=AUCoreEncounter, type=Encounter, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-encounter, path=package/StructureDefinition-au-core-encounter.json
|
||||
DEBUG:services:Found SD: id=au-core-heartrate, name=AUCoreHeartRate, type=Observation, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-heartrate, path=package/StructureDefinition-au-core-heartrate.json
|
||||
DEBUG:services:Found SD: id=au-core-immunization, name=AUCoreImmunization, type=Immunization, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-immunization, path=package/StructureDefinition-au-core-immunization.json
|
||||
DEBUG:services:Found SD: id=au-core-location, name=AUCoreLocation, type=Location, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-location, path=package/StructureDefinition-au-core-location.json
|
||||
DEBUG:services:Found SD: id=au-core-medication, name=AUCoreMedication, type=Medication, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-medication, path=package/StructureDefinition-au-core-medication.json
|
||||
DEBUG:services:Found SD: id=au-core-medicationrequest, name=AUCoreMedicationRequest, type=MedicationRequest, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-medicationrequest, path=package/StructureDefinition-au-core-medicationrequest.json
|
||||
DEBUG:services:Found SD: id=au-core-medicationstatement, name=AUCoreMedicationStatement, type=MedicationStatement, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-medicationstatement, path=package/StructureDefinition-au-core-medicationstatement.json
|
||||
DEBUG:services:Found SD: id=au-core-organization, name=AUCoreOrganization, type=Organization, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-organization, path=package/StructureDefinition-au-core-organization.json
|
||||
DEBUG:services:Found SD: id=au-core-patient, name=AUCorePatient, type=Patient, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-patient, path=package/StructureDefinition-au-core-patient.json
|
||||
INFO:services:Found matching SD for 'au-core-patient' at path: package/StructureDefinition-au-core-patient.json
|
||||
DEBUG:services:Found SD: id=au-core-practitioner, name=AUCorePractitioner, type=Practitioner, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-practitioner, path=package/StructureDefinition-au-core-practitioner.json
|
||||
DEBUG:services:Found SD: id=au-core-practitionerrole, name=AUCorePractitionerRole, type=PractitionerRole, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-practitionerrole, path=package/StructureDefinition-au-core-practitionerrole.json
|
||||
DEBUG:services:Found SD: id=au-core-procedure, name=AUCoreProcedure, type=Procedure, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-procedure, path=package/StructureDefinition-au-core-procedure.json
|
||||
DEBUG:services:Found SD: id=au-core-relatedperson, name=AUCoreRelatedPerson, type=RelatedPerson, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-relatedperson, path=package/StructureDefinition-au-core-relatedperson.json
|
||||
DEBUG:services:Found SD: id=au-core-resprate, name=AUCoreRespirationRate, type=Observation, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-resprate, path=package/StructureDefinition-au-core-resprate.json
|
||||
DEBUG:services:Found SD: id=au-core-rsg-sexassignedab, name=AUCoreSexAssignedAtBirth, type=Extension, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-rsg-sexassignedab, path=package/StructureDefinition-au-core-rsg-sexassignedab.json
|
||||
DEBUG:services:Found SD: id=au-core-smokingstatus, name=AUCoreSmokingStatus, type=Observation, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-smokingstatus, path=package/StructureDefinition-au-core-smokingstatus.json
|
||||
DEBUG:services:Found SD: id=au-core-waistcircum, name=AUCoreWaistCircumference, type=Observation, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-waistcircum, path=package/StructureDefinition-au-core-waistcircum.json
|
||||
DEBUG:__main__:Retrieved 19 Must Support paths for 'au-core-patient' from processed IG hl7.fhir.au.core-1.1.0#preview
|
||||
INFO:werkzeug:172.19.0.1 - - [17/Apr/2025 06:26:16] "GET /get-structure?package_name=hl7.fhir.au.core-1.1.0&package_version=preview&resource_type=au-core-patient HTTP/1.1" 200 -
|
||||
INFO:werkzeug:172.19.0.1 - - [17/Apr/2025 06:26:19] "GET /get-example?package_name=hl7.fhir.au.core-1.1.0&package_version=preview&filename=package/example/Patient-banks-mia-leanne.json HTTP/1.1" 200 -
|
||||
DEBUG:services:Processed input: /tmp/tmph9efztgj/input.json
|
||||
ERROR:app:Exception on /fsh-converter [POST]
|
||||
Traceback (most recent call last):
|
||||
File "/app/venv/lib/python3.12/site-packages/flask/app.py", line 2190, in wsgi_app
|
||||
response = self.full_dispatch_request()
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "/app/venv/lib/python3.12/site-packages/flask/app.py", line 1486, in full_dispatch_request
|
||||
rv = self.handle_user_exception(e)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "/app/venv/lib/python3.12/site-packages/flask/app.py", line 1484, in full_dispatch_request
|
||||
rv = self.dispatch_request()
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "/app/venv/lib/python3.12/site-packages/flask/app.py", line 1469, in dispatch_request
|
||||
return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "/app/app.py", line 985, in fsh_converter
|
||||
output_dir = os.path.join(app.config['UPLOAD_FOLDER'], 'fsh_output')
|
||||
~~~~~~~~~~^^^^^^^^^^^^^^^^^
|
||||
KeyError: 'UPLOAD_FOLDER'
|
||||
INFO:werkzeug:172.19.0.1 - - [17/Apr/2025 06:28:22] "[35m[1mPOST /fsh-converter HTTP/1.1[0m" 500 -
|
||||
INFO:werkzeug:172.19.0.1 - - [17/Apr/2025 06:28:22] "[33mGET /favicon.ico HTTP/1.1[0m" 404 -
|
||||
INFO:werkzeug:172.19.0.1 - - [17/Apr/2025 06:45:46] "GET /fsh-converter HTTP/1.1" 200 -
|
||||
INFO:werkzeug:172.19.0.1 - - [17/Apr/2025 06:45:46] "[36mGET /static/FHIRFLARE.png HTTP/1.1[0m" 304 -
|
||||
INFO:werkzeug:172.19.0.1 - - [17/Apr/2025 06:45:46] "[36mGET /static/favicon.ico HTTP/1.1[0m" 304 -
|
||||
INFO:werkzeug:172.19.0.1 - - [17/Apr/2025 06:45:48] "GET / HTTP/1.1" 200 -
|
||||
INFO:werkzeug:172.19.0.1 - - [17/Apr/2025 06:45:48] "[36mGET /static/FHIRFLARE.png HTTP/1.1[0m" 304 -
|
||||
DEBUG:__main__:Scanning packages directory: /app/instance/fhir_packages
|
||||
DEBUG:services:Parsed 'hl7.fhir.au.base-5.1.0-preview.tgz' -> name='hl7.fhir.au.base-5.1.0', version='preview'
|
||||
DEBUG:services:Parsed 'hl7.fhir.au.core-1.1.0-preview.tgz' -> name='hl7.fhir.au.core-1.1.0', version='preview'
|
||||
DEBUG:services:Parsed 'hl7.fhir.r4.core-4.0.1.tgz' -> name='hl7.fhir.r4.core', version='4.0.1'
|
||||
DEBUG:services:Parsed 'hl7.fhir.uv.extensions.r4-5.2.0.tgz' -> name='hl7.fhir.uv.extensions.r4', version='5.2.0'
|
||||
DEBUG:services:Parsed 'hl7.fhir.uv.ipa-1.0.0.tgz' -> name='hl7.fhir.uv.ipa', version='1.0.0'
|
||||
DEBUG:services:Parsed 'hl7.fhir.uv.smart-app-launch-2.0.0.tgz' -> name='hl7.fhir.uv.smart-app-launch', version='2.0.0'
|
||||
DEBUG:services:Parsed 'hl7.fhir.uv.smart-app-launch-2.1.0.tgz' -> name='hl7.fhir.uv.smart-app-launch', version='2.1.0'
|
||||
DEBUG:services:Parsed 'hl7.terminology.r4-5.0.0.tgz' -> name='hl7.terminology.r4', version='5.0.0'
|
||||
DEBUG:services:Parsed 'hl7.terminology.r4-6.2.0.tgz' -> name='hl7.terminology.r4', version='6.2.0'
|
||||
DEBUG:__main__:Found packages: [{'name': 'hl7.fhir.au.base', 'version': '5.1.0-preview', 'filename': 'hl7.fhir.au.base-5.1.0-preview.tgz'}, {'name': 'hl7.fhir.au.core', 'version': '1.1.0-preview', 'filename': 'hl7.fhir.au.core-1.1.0-preview.tgz'}, {'name': 'hl7.fhir.r4.core', 'version': '4.0.1', 'filename': 'hl7.fhir.r4.core-4.0.1.tgz'}, {'name': 'hl7.fhir.uv.extensions.r4', 'version': '5.2.0', 'filename': 'hl7.fhir.uv.extensions.r4-5.2.0.tgz'}, {'name': 'hl7.fhir.uv.ipa', 'version': '1.0.0', 'filename': 'hl7.fhir.uv.ipa-1.0.0.tgz'}, {'name': 'hl7.fhir.uv.smart-app-launch', 'version': '2.0.0', 'filename': 'hl7.fhir.uv.smart-app-launch-2.0.0.tgz'}, {'name': 'hl7.fhir.uv.smart-app-launch', 'version': '2.1.0', 'filename': 'hl7.fhir.uv.smart-app-launch-2.1.0.tgz'}, {'name': 'hl7.terminology.r4', 'version': '5.0.0', 'filename': 'hl7.terminology.r4-5.0.0.tgz'}, {'name': 'hl7.terminology.r4', 'version': '6.2.0', 'filename': 'hl7.terminology.r4-6.2.0.tgz'}]
|
||||
DEBUG:__main__:Errors during package listing: []
|
||||
DEBUG:__main__:Duplicate groups: {'hl7.fhir.uv.smart-app-launch': ['2.0.0', '2.1.0'], 'hl7.terminology.r4': ['5.0.0', '6.2.0']}
|
||||
INFO:werkzeug:172.19.0.1 - - [17/Apr/2025 06:45:52] "GET /view-igs HTTP/1.1" 200 -
|
||||
INFO:werkzeug:172.19.0.1 - - [17/Apr/2025 06:45:52] "[36mGET /static/FHIRFLARE.png HTTP/1.1[0m" 304 -
|
||||
INFO:werkzeug:172.19.0.1 - - [17/Apr/2025 06:45:57] "GET /fsh-converter HTTP/1.1" 200 -
|
||||
INFO:werkzeug:172.19.0.1 - - [17/Apr/2025 06:45:57] "[36mGET /static/FHIRFLARE.png HTTP/1.1[0m" 304 -
|
||||
DEBUG:__main__:Instance path configuration: /app/instance
|
||||
DEBUG:__main__:Database URI: sqlite:////app/instance/fhir_ig.db
|
||||
DEBUG:__main__:Packages path: /app/instance/fhir_packages
|
||||
DEBUG:__main__:Flask instance folder path: /app/instance
|
||||
DEBUG:__main__:Directories created/verified: Instance: /app/instance, Packages: /app/instance/fhir_packages
|
||||
DEBUG:__main__:Attempting to create database tables for URI: sqlite:////app/instance/fhir_ig.db
|
||||
INFO:__main__:Database tables created successfully (if they didn't exist).
|
||||
INFO:werkzeug:[31m[1mWARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.[0m
|
||||
* Running on all addresses (0.0.0.0)
|
||||
* Running on http://127.0.0.1:5000
|
||||
* Running on http://172.19.0.2:5000
|
||||
INFO:werkzeug:[33mPress CTRL+C to quit[0m
|
||||
INFO:werkzeug:172.19.0.1 - - [17/Apr/2025 07:09:09] "GET /fsh-converter HTTP/1.1" 200 -
|
||||
INFO:werkzeug:172.19.0.1 - - [17/Apr/2025 07:09:09] "GET /static/FHIRFLARE.png HTTP/1.1" 200 -
|
||||
INFO:werkzeug:172.19.0.1 - - [17/Apr/2025 07:09:09] "GET /static/favicon.ico HTTP/1.1" 200 -
|
||||
INFO:werkzeug:172.19.0.1 - - [17/Apr/2025 07:09:13] "GET /fsh-converter HTTP/1.1" 200 -
|
||||
INFO:werkzeug:172.19.0.1 - - [17/Apr/2025 07:09:13] "[36mGET /static/FHIRFLARE.png HTTP/1.1[0m" 304 -
|
||||
DEBUG:__main__:Attempting to find SD for 'au-core-allergyintolerance' in hl7.fhir.au.core-1.1.0-preview.tgz
|
||||
DEBUG:services:Searching for SD matching 'au-core-allergyintolerance' with profile 'None' in hl7.fhir.au.core-1.1.0-preview.tgz
|
||||
DEBUG:services:Found SD: id=au-core-allergyintolerance, name=AUCoreAllergyIntolerance, type=AllergyIntolerance, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-allergyintolerance, path=package/StructureDefinition-au-core-allergyintolerance.json
|
||||
INFO:services:Found matching SD for 'au-core-allergyintolerance' at path: package/StructureDefinition-au-core-allergyintolerance.json
|
||||
DEBUG:services:Found SD: id=au-core-bloodpressure, name=AUCoreBloodPressure, type=Observation, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-bloodpressure, path=package/StructureDefinition-au-core-bloodpressure.json
|
||||
DEBUG:services:Found SD: id=au-core-bodyheight, name=AUCoreBodyHeight, type=Observation, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-bodyheight, path=package/StructureDefinition-au-core-bodyheight.json
|
||||
DEBUG:services:Found SD: id=au-core-bodytemp, name=AUCoreBodyTemperature, type=Observation, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-bodytemp, path=package/StructureDefinition-au-core-bodytemp.json
|
||||
DEBUG:services:Found SD: id=au-core-bodyweight, name=AUCoreBodyWeight, type=Observation, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-bodyweight, path=package/StructureDefinition-au-core-bodyweight.json
|
||||
DEBUG:services:Found SD: id=au-core-condition, name=AUCoreCondition, type=Condition, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-condition, path=package/StructureDefinition-au-core-condition.json
|
||||
DEBUG:services:Found SD: id=au-core-diagnosticresult-path, name=AUCorePathologyResult, type=Observation, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-diagnosticresult-path, path=package/StructureDefinition-au-core-diagnosticresult-path.json
|
||||
DEBUG:services:Found SD: id=au-core-diagnosticresult, name=AUCoreDiagnosticResult, type=Observation, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-diagnosticresult, path=package/StructureDefinition-au-core-diagnosticresult.json
|
||||
DEBUG:services:Found SD: id=au-core-encounter, name=AUCoreEncounter, type=Encounter, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-encounter, path=package/StructureDefinition-au-core-encounter.json
|
||||
DEBUG:services:Found SD: id=au-core-heartrate, name=AUCoreHeartRate, type=Observation, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-heartrate, path=package/StructureDefinition-au-core-heartrate.json
|
||||
DEBUG:services:Found SD: id=au-core-immunization, name=AUCoreImmunization, type=Immunization, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-immunization, path=package/StructureDefinition-au-core-immunization.json
|
||||
DEBUG:services:Found SD: id=au-core-location, name=AUCoreLocation, type=Location, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-location, path=package/StructureDefinition-au-core-location.json
|
||||
DEBUG:services:Found SD: id=au-core-medication, name=AUCoreMedication, type=Medication, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-medication, path=package/StructureDefinition-au-core-medication.json
|
||||
DEBUG:services:Found SD: id=au-core-medicationrequest, name=AUCoreMedicationRequest, type=MedicationRequest, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-medicationrequest, path=package/StructureDefinition-au-core-medicationrequest.json
|
||||
DEBUG:services:Found SD: id=au-core-medicationstatement, name=AUCoreMedicationStatement, type=MedicationStatement, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-medicationstatement, path=package/StructureDefinition-au-core-medicationstatement.json
|
||||
DEBUG:services:Found SD: id=au-core-organization, name=AUCoreOrganization, type=Organization, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-organization, path=package/StructureDefinition-au-core-organization.json
|
||||
DEBUG:services:Found SD: id=au-core-patient, name=AUCorePatient, type=Patient, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-patient, path=package/StructureDefinition-au-core-patient.json
|
||||
DEBUG:services:Found SD: id=au-core-practitioner, name=AUCorePractitioner, type=Practitioner, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-practitioner, path=package/StructureDefinition-au-core-practitioner.json
|
||||
DEBUG:services:Found SD: id=au-core-practitionerrole, name=AUCorePractitionerRole, type=PractitionerRole, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-practitionerrole, path=package/StructureDefinition-au-core-practitionerrole.json
|
||||
DEBUG:services:Found SD: id=au-core-procedure, name=AUCoreProcedure, type=Procedure, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-procedure, path=package/StructureDefinition-au-core-procedure.json
|
||||
DEBUG:services:Found SD: id=au-core-relatedperson, name=AUCoreRelatedPerson, type=RelatedPerson, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-relatedperson, path=package/StructureDefinition-au-core-relatedperson.json
|
||||
DEBUG:services:Found SD: id=au-core-resprate, name=AUCoreRespirationRate, type=Observation, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-resprate, path=package/StructureDefinition-au-core-resprate.json
|
||||
DEBUG:services:Found SD: id=au-core-rsg-sexassignedab, name=AUCoreSexAssignedAtBirth, type=Extension, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-rsg-sexassignedab, path=package/StructureDefinition-au-core-rsg-sexassignedab.json
|
||||
DEBUG:services:Found SD: id=au-core-smokingstatus, name=AUCoreSmokingStatus, type=Observation, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-smokingstatus, path=package/StructureDefinition-au-core-smokingstatus.json
|
||||
DEBUG:services:Found SD: id=au-core-waistcircum, name=AUCoreWaistCircumference, type=Observation, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-waistcircum, path=package/StructureDefinition-au-core-waistcircum.json
|
||||
DEBUG:__main__:Retrieved 8 Must Support paths for 'au-core-allergyintolerance' from processed IG hl7.fhir.au.core-1.1.0#preview
|
||||
INFO:werkzeug:172.19.0.1 - - [17/Apr/2025 07:09:27] "GET /get-structure?package_name=hl7.fhir.au.core-1.1.0&package_version=preview&resource_type=au-core-allergyintolerance HTTP/1.1" 200 -
|
||||
DEBUG:__main__:Attempting to find SD for 'au-core-patient' in hl7.fhir.au.core-1.1.0-preview.tgz
|
||||
DEBUG:services:Searching for SD matching 'au-core-patient' with profile 'None' in hl7.fhir.au.core-1.1.0-preview.tgz
|
||||
DEBUG:services:Found SD: id=au-core-allergyintolerance, name=AUCoreAllergyIntolerance, type=AllergyIntolerance, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-allergyintolerance, path=package/StructureDefinition-au-core-allergyintolerance.json
|
||||
DEBUG:services:Found SD: id=au-core-bloodpressure, name=AUCoreBloodPressure, type=Observation, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-bloodpressure, path=package/StructureDefinition-au-core-bloodpressure.json
|
||||
DEBUG:services:Found SD: id=au-core-bodyheight, name=AUCoreBodyHeight, type=Observation, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-bodyheight, path=package/StructureDefinition-au-core-bodyheight.json
|
||||
DEBUG:services:Found SD: id=au-core-bodytemp, name=AUCoreBodyTemperature, type=Observation, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-bodytemp, path=package/StructureDefinition-au-core-bodytemp.json
|
||||
DEBUG:services:Found SD: id=au-core-bodyweight, name=AUCoreBodyWeight, type=Observation, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-bodyweight, path=package/StructureDefinition-au-core-bodyweight.json
|
||||
DEBUG:services:Found SD: id=au-core-condition, name=AUCoreCondition, type=Condition, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-condition, path=package/StructureDefinition-au-core-condition.json
|
||||
DEBUG:services:Found SD: id=au-core-diagnosticresult-path, name=AUCorePathologyResult, type=Observation, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-diagnosticresult-path, path=package/StructureDefinition-au-core-diagnosticresult-path.json
|
||||
DEBUG:services:Found SD: id=au-core-diagnosticresult, name=AUCoreDiagnosticResult, type=Observation, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-diagnosticresult, path=package/StructureDefinition-au-core-diagnosticresult.json
|
||||
DEBUG:services:Found SD: id=au-core-encounter, name=AUCoreEncounter, type=Encounter, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-encounter, path=package/StructureDefinition-au-core-encounter.json
|
||||
DEBUG:services:Found SD: id=au-core-heartrate, name=AUCoreHeartRate, type=Observation, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-heartrate, path=package/StructureDefinition-au-core-heartrate.json
|
||||
DEBUG:services:Found SD: id=au-core-immunization, name=AUCoreImmunization, type=Immunization, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-immunization, path=package/StructureDefinition-au-core-immunization.json
|
||||
DEBUG:services:Found SD: id=au-core-location, name=AUCoreLocation, type=Location, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-location, path=package/StructureDefinition-au-core-location.json
|
||||
DEBUG:services:Found SD: id=au-core-medication, name=AUCoreMedication, type=Medication, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-medication, path=package/StructureDefinition-au-core-medication.json
|
||||
DEBUG:services:Found SD: id=au-core-medicationrequest, name=AUCoreMedicationRequest, type=MedicationRequest, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-medicationrequest, path=package/StructureDefinition-au-core-medicationrequest.json
|
||||
DEBUG:services:Found SD: id=au-core-medicationstatement, name=AUCoreMedicationStatement, type=MedicationStatement, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-medicationstatement, path=package/StructureDefinition-au-core-medicationstatement.json
|
||||
DEBUG:services:Found SD: id=au-core-organization, name=AUCoreOrganization, type=Organization, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-organization, path=package/StructureDefinition-au-core-organization.json
|
||||
DEBUG:services:Found SD: id=au-core-patient, name=AUCorePatient, type=Patient, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-patient, path=package/StructureDefinition-au-core-patient.json
|
||||
INFO:services:Found matching SD for 'au-core-patient' at path: package/StructureDefinition-au-core-patient.json
|
||||
DEBUG:services:Found SD: id=au-core-practitioner, name=AUCorePractitioner, type=Practitioner, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-practitioner, path=package/StructureDefinition-au-core-practitioner.json
|
||||
DEBUG:services:Found SD: id=au-core-practitionerrole, name=AUCorePractitionerRole, type=PractitionerRole, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-practitionerrole, path=package/StructureDefinition-au-core-practitionerrole.json
|
||||
DEBUG:services:Found SD: id=au-core-procedure, name=AUCoreProcedure, type=Procedure, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-procedure, path=package/StructureDefinition-au-core-procedure.json
|
||||
DEBUG:services:Found SD: id=au-core-relatedperson, name=AUCoreRelatedPerson, type=RelatedPerson, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-relatedperson, path=package/StructureDefinition-au-core-relatedperson.json
|
||||
DEBUG:services:Found SD: id=au-core-resprate, name=AUCoreRespirationRate, type=Observation, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-resprate, path=package/StructureDefinition-au-core-resprate.json
|
||||
DEBUG:services:Found SD: id=au-core-rsg-sexassignedab, name=AUCoreSexAssignedAtBirth, type=Extension, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-rsg-sexassignedab, path=package/StructureDefinition-au-core-rsg-sexassignedab.json
|
||||
DEBUG:services:Found SD: id=au-core-smokingstatus, name=AUCoreSmokingStatus, type=Observation, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-smokingstatus, path=package/StructureDefinition-au-core-smokingstatus.json
|
||||
DEBUG:services:Found SD: id=au-core-waistcircum, name=AUCoreWaistCircumference, type=Observation, url=http://hl7.org.au/fhir/core/StructureDefinition/au-core-waistcircum, path=package/StructureDefinition-au-core-waistcircum.json
|
||||
DEBUG:__main__:Retrieved 19 Must Support paths for 'au-core-patient' from processed IG hl7.fhir.au.core-1.1.0#preview
|
||||
INFO:werkzeug:172.19.0.1 - - [17/Apr/2025 07:09:28] "GET /get-structure?package_name=hl7.fhir.au.core-1.1.0&package_version=preview&resource_type=au-core-patient HTTP/1.1" 200 -
|
||||
INFO:werkzeug:172.19.0.1 - - [17/Apr/2025 07:09:32] "GET /get-example?package_name=hl7.fhir.au.core-1.1.0&package_version=preview&filename=package/example/Patient-banks-mia-leanne.json HTTP/1.1" 200 -
|
||||
DEBUG:services:Processed input: /tmp/tmpgwv6g5vl/input.json
|
||||
INFO:services:GoFSH executed successfully for /tmp/tmpgwv6g5vl/input.json
|
||||
INFO:werkzeug:172.19.0.1 - - [17/Apr/2025 07:10:23] "POST /fsh-converter HTTP/1.1" 200 -
|
||||
INFO:werkzeug:172.19.0.1 - - [17/Apr/2025 07:10:23] "[36mGET /static/FHIRFLARE.png HTTP/1.1[0m" 304 -
|
||||
INFO:werkzeug:172.19.0.1 - - [17/Apr/2025 07:11:09] "GET /download-fsh HTTP/1.1" 200 -
|
||||
INFO:werkzeug:172.19.0.1 - - [17/Apr/2025 07:16:19] "GET /fhir HTTP/1.1" 200 -
|
||||
INFO:werkzeug:172.19.0.1 - - [17/Apr/2025 07:16:19] "[36mGET /static/FHIRFLARE.png HTTP/1.1[0m" 304 -
|
||||
INFO:werkzeug:172.19.0.1 - - [17/Apr/2025 07:16:56] "GET /fsh-converter HTTP/1.1" 200 -
|
||||
INFO:werkzeug:172.19.0.1 - - [17/Apr/2025 07:16:56] "[36mGET /static/FHIRFLARE.png HTTP/1.1[0m" 304 -
|
17
logs/supervisord.log
Normal file
17
logs/supervisord.log
Normal file
@ -0,0 +1,17 @@
|
||||
2025-04-17 06:25:17,514 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-04-17 06:25:17,521 INFO supervisord started with pid 1
|
||||
2025-04-17 06:25:18,537 INFO spawned: 'flask' with pid 7
|
||||
2025-04-17 06:25:18,542 INFO spawned: 'tomcat' with pid 8
|
||||
2025-04-17 06:25:29,060 INFO success: flask entered RUNNING state, process has stayed up for > than 10 seconds (startsecs)
|
||||
2025-04-17 06:25:48,549 INFO success: tomcat entered RUNNING state, process has stayed up for > than 30 seconds (startsecs)
|
||||
2025-04-17 07:05:48,346 WARN received SIGTERM indicating exit request
|
||||
2025-04-17 07:05:48,354 INFO waiting for flask, tomcat to die
|
||||
2025-04-17 07:05:51,475 WARN stopped: tomcat (exit status 143)
|
||||
2025-04-17 07:05:51,483 INFO waiting for flask to die
|
||||
2025-04-17 07:05:52,495 WARN stopped: flask (terminated by SIGTERM)
|
||||
2025-04-17 07:08:06,100 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-04-17 07:08:06,109 INFO supervisord started with pid 1
|
||||
2025-04-17 07:08:07,117 INFO spawned: 'flask' with pid 7
|
||||
2025-04-17 07:08:07,122 INFO spawned: 'tomcat' with pid 8
|
||||
2025-04-17 07:08:17,381 INFO success: flask entered RUNNING state, process has stayed up for > than 10 seconds (startsecs)
|
||||
2025-04-17 07:08:37,990 INFO success: tomcat entered RUNNING state, process has stayed up for > than 30 seconds (startsecs)
|
1
logs/supervisord.pid
Normal file
1
logs/supervisord.pid
Normal file
@ -0,0 +1 @@
|
||||
1
|
368
logs/tomcat.log
Normal file
368
logs/tomcat.log
Normal file
@ -0,0 +1,368 @@
|
||||
|
||||
. ____ _ __ _ _
|
||||
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
|
||||
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
|
||||
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
|
||||
' |____| .__|_| |_|_| |_\__, | / / / /
|
||||
=========|_|==============|___/=/_/_/_/
|
||||
|
||||
:: Spring Boot :: (v3.3.5)
|
||||
|
||||
2025-04-17T06:25:41.168Z INFO 8 --- [ main] ca.uhn.fhir.jpa.starter.Application : Starting Application using Java 17.0.14 with PID 8 (/usr/local/tomcat/webapps/ROOT/WEB-INF/classes started by root in /usr/local/tomcat)
|
||||
2025-04-17T06:25:41.173Z INFO 8 --- [ main] ca.uhn.fhir.jpa.starter.Application : No active profile set, falling back to 1 default profile: "default"
|
||||
2025-04-17T06:25:43.114Z INFO 8 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
|
||||
2025-04-17T06:25:43.465Z INFO 8 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 338 ms. Found 52 JPA repository interfaces.
|
||||
2025-04-17T06:25:47.008Z WARN 8 --- [ 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-04-17T06:25:47.312Z INFO 8 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 6089 ms
|
||||
2025-04-17T06:25:47.688Z INFO 8 --- [ main] ca.uhn.fhir.util.VersionUtil : HAPI FHIR version 8.0.0 - Rev 091beb6d18
|
||||
2025-04-17T06:25:47.701Z INFO 8 --- [ main] ca.uhn.fhir.context.FhirContext : Creating new FHIR context for FHIR version [R4]
|
||||
2025-04-17T06:25:47.820Z INFO 8 --- [ main] c.u.f.j.s.common.FhirServerConfigCommon : Server configured to allow contains searches
|
||||
2025-04-17T06:25:47.821Z INFO 8 --- [ main] c.u.f.j.s.common.FhirServerConfigCommon : Server configured to deny multiple deletes
|
||||
2025-04-17T06:25:47.822Z INFO 8 --- [ main] c.u.f.j.s.common.FhirServerConfigCommon : Server configured to deny external references
|
||||
2025-04-17T06:25:47.823Z INFO 8 --- [ main] c.u.f.j.s.common.FhirServerConfigCommon : Server configured to enable DAO scheduling
|
||||
2025-04-17T06:25:47.823Z INFO 8 --- [ main] c.u.f.j.s.common.FhirServerConfigCommon : Server configured to disable delete expunges
|
||||
2025-04-17T06:25:47.824Z INFO 8 --- [ main] c.u.f.j.s.common.FhirServerConfigCommon : Server configured to enable expunges
|
||||
2025-04-17T06:25:47.824Z INFO 8 --- [ main] c.u.f.j.s.common.FhirServerConfigCommon : Server configured to allow overriding default search params
|
||||
2025-04-17T06:25:47.825Z INFO 8 --- [ main] c.u.f.j.s.common.FhirServerConfigCommon : Server configured to disable auto-creating placeholder references
|
||||
2025-04-17T06:25:47.825Z INFO 8 --- [ main] c.u.f.j.s.common.FhirServerConfigCommon : Server configured to auto-version references at paths []
|
||||
2025-04-17T06:25:47.826Z INFO 8 --- [ main] c.u.f.j.s.common.FhirServerConfigCommon : Server configured to enable value set pre-expansion
|
||||
2025-04-17T06:25:47.826Z INFO 8 --- [ main] c.u.f.j.s.common.FhirServerConfigCommon : Server configured to enable value set pre-expansion task
|
||||
2025-04-17T06:25:47.826Z INFO 8 --- [ main] c.u.f.j.s.common.FhirServerConfigCommon : Server configured for pre-expand value set default count of 1000
|
||||
2025-04-17T06:25:47.826Z INFO 8 --- [ main] c.u.f.j.s.common.FhirServerConfigCommon : Server configured for pre-expand value set max count of 1000
|
||||
2025-04-17T06:25:47.826Z INFO 8 --- [ main] c.u.f.j.s.common.FhirServerConfigCommon : Server configured for maximum expansion size of 1000
|
||||
2025-04-17T06:25:47.878Z INFO 8 --- [ main] c.u.f.j.s.common.FhirServerConfigCommon : Server configured to have a maximum fetch size of 'unlimited'
|
||||
2025-04-17T06:25:47.879Z INFO 8 --- [ main] c.u.f.j.s.common.FhirServerConfigCommon : Server configured to cache search results for 60000 milliseconds
|
||||
2025-04-17T06:25:47.879Z INFO 8 --- [ main] c.u.f.j.s.common.FhirServerConfigCommon : Server configured to use 'ALPHANUMERIC' Client ID Strategy
|
||||
2025-04-17T06:25:48.139Z INFO 8 --- [ main] o.hibernate.jpa.internal.util.LogHelper : HHH000204: Processing PersistenceUnitInfo [name: HAPI_PU]
|
||||
2025-04-17T06:25:48.494Z INFO 8 --- [ main] org.hibernate.Version : HHH000412: Hibernate ORM core version 6.6.4.Final
|
||||
2025-04-17T06:25:48.547Z INFO 8 --- [ main] .f.j.l.FilteringSqlLoggerImplContributor : Adding service: SqlStatementFilteringLogger
|
||||
2025-04-17T06:25:48.743Z INFO 8 --- [ main] o.h.c.internal.RegionFactoryInitiator : HHH000026: Second-level cache disabled
|
||||
2025-04-17T06:25:49.196Z INFO 8 --- [ main] o.h.e.boot.internal.EnversServiceImpl : Envers integration enabled? : true
|
||||
2025-04-17T06:25:49.780Z INFO 8 --- [ main] o.s.o.j.p.SpringPersistenceUnitInfo : No LoadTimeWeaver setup: ignoring JPA class transformer
|
||||
2025-04-17T06:25:49.878Z INFO 8 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
|
||||
2025-04-17T06:25:51.263Z INFO 8 --- [ main] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Added connection conn1: url=jdbc:h2:file:/app/h2-data/fhir user=SA
|
||||
2025-04-17T06:25:51.269Z INFO 8 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
|
||||
2025-04-17T06:25:51.433Z INFO 8 --- [ 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-04-17T06:25:53.533Z INFO 8 --- [ main] b.i.HibernateSearchPreIntegrationService : HSEARCH000034: Hibernate Search version 7.2.1.Final
|
||||
2025-04-17T06:25:53.763Z INFO 8 --- [ main] o.h.e.c.i.m.AuditMetadataGenerator : Adding properties for entity: ca.uhn.fhir.jpa.entity.MdmLink
|
||||
2025-04-17T06:25:58.698Z INFO 8 --- [ 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-04-17T06:25:59.508Z INFO 8 --- [ main] irLocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'HAPI_PU'
|
||||
2025-04-17T06:26:01.166Z INFO 8 --- [ main] .u.f.c.s.DefaultProfileValidationSupport : Loading structure definitions from classpath: /org/hl7/fhir/r4/model/profile/profiles-resources.xml
|
||||
2025-04-17T06:26:01.191Z INFO 8 --- [ main] ca.uhn.fhir.util.XmlUtil : FHIR XML procesing will use StAX implementation 'Woodstox' version '6.4.0'
|
||||
2025-04-17T06:26:02.561Z INFO 8 --- [ main] .u.f.c.s.DefaultProfileValidationSupport : Loading structure definitions from classpath: /org/hl7/fhir/r4/model/profile/profiles-types.xml
|
||||
2025-04-17T06:26:02.707Z INFO 8 --- [ main] .u.f.c.s.DefaultProfileValidationSupport : Loading structure definitions from classpath: /org/hl7/fhir/r4/model/profile/profiles-others.xml
|
||||
2025-04-17T06:26:03.137Z INFO 8 --- [ main] .u.f.c.s.DefaultProfileValidationSupport : Loading structure definitions from classpath: /org/hl7/fhir/r4/model/extension/extension-definitions.xml
|
||||
2025-04-17T06:26:04.442Z INFO 8 --- [ main] o.s.d.j.r.query.QueryEnhancerFactory : Hibernate is in classpath; If applicable, HQL parser will be used.
|
||||
2025-04-17T06:26:10.654Z INFO 8 --- [ main] c.u.f.j.sched.BaseSchedulerServiceImpl : Creating Local Scheduler
|
||||
2025-04-17T06:26:10.739Z INFO 8 --- [ main] org.quartz.impl.StdSchedulerFactory : Using default implementation for ThreadExecutor
|
||||
2025-04-17T06:26:10.772Z INFO 8 --- [ main] org.quartz.core.SchedulerSignalerImpl : Initialized Scheduler Signaller of type: class org.quartz.core.SchedulerSignalerImpl
|
||||
2025-04-17T06:26:10.772Z INFO 8 --- [ main] org.quartz.core.QuartzScheduler : Quartz Scheduler v.2.3.2 created.
|
||||
2025-04-17T06:26:10.774Z INFO 8 --- [ main] org.quartz.simpl.RAMJobStore : RAMJobStore initialized.
|
||||
2025-04-17T06:26:10.776Z INFO 8 --- [ 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-04-17T06:26:10.776Z INFO 8 --- [ main] org.quartz.impl.StdSchedulerFactory : Quartz scheduler 'hapi-fhir-jpa-scheduler' initialized from an externally provided properties instance.
|
||||
2025-04-17T06:26:10.776Z INFO 8 --- [ main] org.quartz.impl.StdSchedulerFactory : Quartz scheduler version: 2.3.2
|
||||
2025-04-17T06:26:10.777Z INFO 8 --- [ main] org.quartz.core.QuartzScheduler : JobFactory set to: ca.uhn.fhir.jpa.sched.AutowiringSpringBeanJobFactory@493c19fd
|
||||
2025-04-17T06:26:10.777Z INFO 8 --- [ main] org.quartz.core.QuartzScheduler : Scheduler hapi-fhir-jpa-scheduler_$_NON_CLUSTERED paused.
|
||||
2025-04-17T06:26:10.778Z INFO 8 --- [ main] c.u.f.j.sched.BaseSchedulerServiceImpl : Creating Clustered Scheduler
|
||||
2025-04-17T06:26:10.780Z INFO 8 --- [ main] org.quartz.impl.StdSchedulerFactory : Using default implementation for ThreadExecutor
|
||||
2025-04-17T06:26:10.790Z INFO 8 --- [ main] org.quartz.core.SchedulerSignalerImpl : Initialized Scheduler Signaller of type: class org.quartz.core.SchedulerSignalerImpl
|
||||
2025-04-17T06:26:10.790Z INFO 8 --- [ main] org.quartz.core.QuartzScheduler : Quartz Scheduler v.2.3.2 created.
|
||||
2025-04-17T06:26:10.791Z INFO 8 --- [ main] org.quartz.simpl.RAMJobStore : RAMJobStore initialized.
|
||||
2025-04-17T06:26:10.791Z INFO 8 --- [ 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-04-17T06:26:10.791Z INFO 8 --- [ main] org.quartz.impl.StdSchedulerFactory : Quartz scheduler 'hapi-fhir-jpa-scheduler' initialized from an externally provided properties instance.
|
||||
2025-04-17T06:26:10.791Z INFO 8 --- [ main] org.quartz.impl.StdSchedulerFactory : Quartz scheduler version: 2.3.2
|
||||
2025-04-17T06:26:10.792Z INFO 8 --- [ main] org.quartz.core.QuartzScheduler : JobFactory set to: ca.uhn.fhir.jpa.sched.AutowiringSpringBeanJobFactory@493c19fd
|
||||
2025-04-17T06:26:10.792Z INFO 8 --- [ main] org.quartz.core.QuartzScheduler : Scheduler hapi-fhir-jpa-scheduler_$_NON_CLUSTERED paused.
|
||||
2025-04-17T06:26:11.644Z INFO 8 --- [ main] c.u.f.log.terminology_troubleshooting : Performing initial retrieval for non-expiring cache: org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain$FetchAllKey@2604b187
|
||||
2025-04-17T06:26:11.645Z INFO 8 --- [ main] c.u.f.log.terminology_troubleshooting : Initial retrieval for non-expiring cache org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain$FetchAllKey@2604b187 succeeded in 1ms
|
||||
2025-04-17T06:26:11.658Z INFO 8 --- [ main] c.u.f.log.terminology_troubleshooting : Performing initial retrieval for non-expiring cache: org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain$FetchAllKey@2604b187
|
||||
2025-04-17T06:26:11.661Z INFO 8 --- [ main] c.u.f.log.terminology_troubleshooting : Initial retrieval for non-expiring cache org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain$FetchAllKey@2604b187 succeeded in 3ms
|
||||
2025-04-17T06:26:11.663Z INFO 8 --- [ main] c.u.f.log.terminology_troubleshooting : Performing initial retrieval for non-expiring cache: org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain$FetchAllKey@2604b187
|
||||
2025-04-17T06:26:11.664Z INFO 8 --- [ main] c.u.f.log.terminology_troubleshooting : Initial retrieval for non-expiring cache org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain$FetchAllKey@2604b187 succeeded in 1ms
|
||||
2025-04-17T06:26:11.665Z INFO 8 --- [ main] c.u.f.log.terminology_troubleshooting : Performing initial retrieval for non-expiring cache: org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain$FetchAllKey@2604b187
|
||||
2025-04-17T06:26:11.666Z INFO 8 --- [ main] c.u.f.log.terminology_troubleshooting : Initial retrieval for non-expiring cache org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain$FetchAllKey@2604b187 succeeded in 1ms
|
||||
2025-04-17T06:26:11.668Z INFO 8 --- [ main] c.u.f.log.terminology_troubleshooting : Performing initial retrieval for non-expiring cache: org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain$FetchAllKey@2604b187
|
||||
2025-04-17T06:26:11.669Z INFO 8 --- [ main] c.u.f.log.terminology_troubleshooting : Initial retrieval for non-expiring cache org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain$FetchAllKey@2604b187 succeeded in 1ms
|
||||
2025-04-17T06:26:11.671Z INFO 8 --- [ main] c.u.f.log.terminology_troubleshooting : Performing initial retrieval for non-expiring cache: org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain$FetchAllKey@2604b187
|
||||
2025-04-17T06:26:11.672Z INFO 8 --- [ main] c.u.f.log.terminology_troubleshooting : Initial retrieval for non-expiring cache org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain$FetchAllKey@2604b187 succeeded in 1ms
|
||||
2025-04-17T06:26:11.820Z INFO 8 --- [ main] ca.uhn.fhir.context.FhirContext : Creating new FHIR context for FHIR version [R4]
|
||||
2025-04-17T06:26:12.275Z INFO 8 --- [ main] c.u.f.j.starter.common.StarterJpaConfig : CORS is enabled on this server
|
||||
2025-04-17T06:26:12.280Z INFO 8 --- [ main] c.u.f.j.starter.common.StarterJpaConfig : CORS allows the following origins: *
|
||||
2025-04-17T06:26:12.330Z INFO 8 --- [ main] ca.uhn.fhir.context.FhirContext : Creating new FHIR context for FHIR version [R5]
|
||||
2025-04-17T06:26:20.902Z INFO 8 --- [ main] ca.uhn.fhir.context.FhirContext : Creating new FHIR context for FHIR version [R4]
|
||||
2025-04-17T06:26:22.510Z WARN 8 --- [ 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-04-17T06:26:23.228Z INFO 8 --- [ 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-04-17T06:26:24.173Z INFO 8 --- [ main] org.quartz.impl.StdSchedulerFactory : Using default implementation for ThreadExecutor
|
||||
2025-04-17T06:26:24.190Z INFO 8 --- [ main] org.quartz.core.SchedulerSignalerImpl : Initialized Scheduler Signaller of type: class org.quartz.core.SchedulerSignalerImpl
|
||||
2025-04-17T06:26:24.190Z INFO 8 --- [ main] org.quartz.core.QuartzScheduler : Quartz Scheduler v.2.3.2 created.
|
||||
2025-04-17T06:26:24.190Z INFO 8 --- [ main] org.quartz.simpl.RAMJobStore : RAMJobStore initialized.
|
||||
2025-04-17T06:26:24.190Z INFO 8 --- [ 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-04-17T06:26:24.190Z INFO 8 --- [ main] org.quartz.impl.StdSchedulerFactory : Quartz scheduler 'quartzScheduler' initialized from an externally provided properties instance.
|
||||
2025-04-17T06:26:24.190Z INFO 8 --- [ main] org.quartz.impl.StdSchedulerFactory : Quartz scheduler version: 2.3.2
|
||||
2025-04-17T06:26:24.190Z INFO 8 --- [ main] org.quartz.core.QuartzScheduler : JobFactory set to: org.springframework.scheduling.quartz.SpringBeanJobFactory@3d31c83e
|
||||
2025-04-17T06:26:24.251Z INFO 8 --- [ main] o.s.b.a.e.web.EndpointLinksResolver : Exposing 1 endpoint beneath base path '/actuator'
|
||||
2025-04-17T06:26:24.502Z INFO 8 --- [ main] o.s.s.quartz.SchedulerFactoryBean : Starting Quartz Scheduler now
|
||||
2025-04-17T06:26:24.503Z INFO 8 --- [ main] org.quartz.core.QuartzScheduler : Scheduler quartzScheduler_$_NON_CLUSTERED started.
|
||||
2025-04-17T06:26:24.509Z INFO 8 --- [ main] ca.uhn.fhir.log.batch_troubleshooting : Registering Batch2 Job Definition: BULK_IMPORT_PULL / 1
|
||||
2025-04-17T06:26:24.510Z INFO 8 --- [ main] ca.uhn.fhir.log.batch_troubleshooting : Registering Batch2 Job Definition: REINDEX / 1
|
||||
2025-04-17T06:26:24.510Z INFO 8 --- [ main] ca.uhn.fhir.log.batch_troubleshooting : Registering Batch2 Job Definition: REINDEX / 2
|
||||
2025-04-17T06:26:24.510Z INFO 8 --- [ main] ca.uhn.fhir.log.batch_troubleshooting : Registering Batch2 Job Definition: DELETE_EXPUNGE / 1
|
||||
2025-04-17T06:26:24.510Z INFO 8 --- [ main] ca.uhn.fhir.log.batch_troubleshooting : Registering Batch2 Job Definition: BULK_EXPORT / 1
|
||||
2025-04-17T06:26:24.510Z INFO 8 --- [ main] ca.uhn.fhir.log.batch_troubleshooting : Registering Batch2 Job Definition: BULK_EXPORT / 2
|
||||
2025-04-17T06:26:24.510Z INFO 8 --- [ main] ca.uhn.fhir.log.batch_troubleshooting : Registering Batch2 Job Definition: termCodeSystemVersionDeleteJob / 1
|
||||
2025-04-17T06:26:24.510Z INFO 8 --- [ main] ca.uhn.fhir.log.batch_troubleshooting : Registering Batch2 Job Definition: termCodeSystemDeleteJob / 1
|
||||
2025-04-17T06:26:24.510Z INFO 8 --- [ main] ca.uhn.fhir.log.batch_troubleshooting : Registering Batch2 Job Definition: bulkImportJob / 1
|
||||
2025-04-17T06:26:24.512Z INFO 8 --- [ 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-04-17T06:26:25.538Z INFO 8 --- [ main] c.u.f.j.s.registry.JpaSearchParamCache : Have 0 unique search params
|
||||
2025-04-17T06:26:25.611Z INFO 8 --- [ main] c.u.f.j.sched.BaseSchedulerServiceImpl : Scheduling 15 jobs in application
|
||||
2025-04-17T06:26:25.614Z INFO 8 --- [ main] c.u.f.j.sched.BaseSchedulerServiceImpl : Scheduling local job ca.uhn.fhir.jpa.term.TermDeferredStorageSvcImpl$Job with interval 5000ms
|
||||
2025-04-17T06:26:25.623Z INFO 8 --- [ main] c.u.f.j.sched.BaseSchedulerServiceImpl : Scheduling local job ca.uhn.fhir.jpa.cache.ResourceChangeListenerCacheRefresherImpl with interval 00:00:10.000
|
||||
2025-04-17T06:26:25.625Z INFO 8 --- [ main] c.u.f.j.sched.BaseSchedulerServiceImpl : Scheduling clustered job ca.uhn.fhir.jpa.search.reindex.ResourceReindexingSvcImpl with interval 00:00:10.000
|
||||
2025-04-17T06:26:25.627Z INFO 8 --- [ 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-04-17T06:26:25.630Z INFO 8 --- [ 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-04-17T06:26:25.632Z INFO 8 --- [ main] c.u.f.j.sched.BaseSchedulerServiceImpl : Scheduling clustered job ca.uhn.fhir.jpa.search.warm.CacheWarmingSvcImpl with interval 00:00:10.000
|
||||
2025-04-17T06:26:25.633Z INFO 8 --- [ main] c.u.f.j.sched.BaseSchedulerServiceImpl : Scheduling clustered job ca.uhn.fhir.jpa.term.TermReadSvcImpl with interval 00:10:00
|
||||
2025-04-17T06:26:25.634Z INFO 8 --- [ main] c.u.f.j.sched.BaseSchedulerServiceImpl : Scheduling local job ca.uhn.fhir.jpa.term.TermReindexingSvcImpl with interval 00:01:00.000
|
||||
2025-04-17T06:26:25.636Z INFO 8 --- [ main] c.u.f.j.sched.BaseSchedulerServiceImpl : Scheduling clustered job ca.uhn.fhir.jpa.search.SearchUrlJobMaintenanceSvcImpl$SearchUrlMaintenanceJob with interval 00:10:00
|
||||
2025-04-17T06:26:25.638Z INFO 8 --- [ main] c.u.f.j.sched.BaseSchedulerServiceImpl : Scheduling clustered job ca.uhn.fhir.jpa.search.StaleSearchDeletingSvcImpl with interval 00:01:00.000
|
||||
2025-04-17T06:26:25.639Z INFO 8 --- [ main] c.u.f.j.sched.BaseSchedulerServiceImpl : Scheduling local job ca.uhn.fhir.jpa.util.ResourceCountCache with interval 00:10:00
|
||||
2025-04-17T06:26:25.640Z INFO 8 --- [ main] c.u.f.j.sched.BaseSchedulerServiceImpl : Scheduling local job ca.uhn.fhir.jpa.subscription.triggering.SubscriptionTriggeringSvcImpl with interval 5000ms
|
||||
2025-04-17T06:26:25.642Z INFO 8 --- [ main] c.u.f.j.sched.BaseSchedulerServiceImpl : Scheduling clustered job ca.uhn.fhir.jpa.subscription.async.AsyncResourceModifiedProcessingSchedulerSvc with interval 5000ms
|
||||
2025-04-17T06:26:25.642Z INFO 8 --- [ main] c.u.f.j.sched.BaseSchedulerServiceImpl : Scheduling clustered job ca.uhn.fhir.batch2.coordinator.ReductionStepExecutorServiceImpl$ReductionStepExecutorScheduledJob with interval 00:00:10.000
|
||||
2025-04-17T06:26:25.642Z INFO 8 --- [ main] c.u.f.j.sched.BaseSchedulerServiceImpl : Scheduling clustered job ca.uhn.fhir.batch2.maintenance.JobMaintenanceServiceImpl$JobMaintenanceScheduledJob with interval 00:01:00.000
|
||||
2025-04-17T06:26:25.643Z INFO 8 --- [ main] c.u.f.j.sched.BaseSchedulerServiceImpl : Starting task schedulers for context application
|
||||
2025-04-17T06:26:25.643Z INFO 8 --- [ main] c.uhn.fhir.jpa.sched.BaseHapiScheduler : Starting scheduler hapi-fhir-jpa-scheduler-local
|
||||
2025-04-17T06:26:25.643Z INFO 8 --- [ main] org.quartz.core.QuartzScheduler : Scheduler hapi-fhir-jpa-scheduler_$_NON_CLUSTERED started.
|
||||
2025-04-17T06:26:25.643Z INFO 8 --- [ main] c.uhn.fhir.jpa.sched.BaseHapiScheduler : Starting scheduler hapi-fhir-jpa-scheduler-clustered
|
||||
2025-04-17T06:26:25.644Z INFO 8 --- [ main] org.quartz.core.QuartzScheduler : Scheduler hapi-fhir-jpa-scheduler_$_NON_CLUSTERED started.
|
||||
2025-04-17T06:26:25.644Z WARN 8 --- [ main] u.f.j.u.PartitionedIdModeVerificationSvc : Dialect is not a HAPI FHIR dialect: org.hibernate.dialect.H2Dialect, version: 2.3.232
|
||||
2025-04-17T06:26:25.648Z INFO 8 --- [ main] ca.uhn.fhir.jpa.starter.Application : Started Application in 45.501 seconds (process running for 67.034)
|
||||
2025-04-17T06:26:25.684Z INFO 8 --- [ler-clustered-2] .s.BulkDataExportJobSchedulingHelperImpl : Finished bulk export job deletion with nothing to do
|
||||
2025-04-17T06:26:25.767Z INFO 8 --- [ main] ca.uhn.fhir.rest.server.RestfulServer : Initializing HAPI FHIR restful server running in R4 mode
|
||||
2025-04-17T06:26:25.769Z INFO 8 --- [ main] ca.uhn.fhir.rest.server.RestfulServer : Added 147 resource provider(s). Total 147
|
||||
2025-04-17T06:26:26.644Z INFO 8 --- [ main] ca.uhn.fhir.rest.server.RestfulServer : Added 5 plain provider(s). Total 5
|
||||
2025-04-17T06:26:26.655Z INFO 8 --- [ main] ca.uhn.fhir.rest.server.RestfulServer : Removing RESTful methods for: class ca.uhn.fhir.jpa.provider.JpaCapabilityStatementProvider
|
||||
2025-04-17T06:26:26.656Z INFO 8 --- [ main] ca.uhn.fhir.rest.server.RestfulServer : OperationDefinition binding of ca.uhn.fhir.rest.server.method.ReadMethodBinding@7c3ae39a was removed
|
||||
2025-04-17T06:26:26.656Z INFO 8 --- [ main] ca.uhn.fhir.rest.server.RestfulServer : OperationDefinition binding of ca.uhn.fhir.rest.server.method.ReadMethodBinding@7594beb2 was removed
|
||||
2025-04-17T06:26:26.721Z INFO 8 --- [ main] ca.uhn.fhir.rest.server.RestfulServer : A FHIR has been lit on this server
|
||||
2025-04-17T07:05:48.691Z INFO 8 --- [ Thread-5] org.quartz.core.QuartzScheduler : Scheduler quartzScheduler_$_NON_CLUSTERED paused.
|
||||
2025-04-17T07:05:48.694Z INFO 8 --- [ Thread-5] o.s.s.quartz.SchedulerFactoryBean : Shutting down Quartz Scheduler
|
||||
2025-04-17T07:05:48.694Z INFO 8 --- [ Thread-5] org.quartz.core.QuartzScheduler : Scheduler quartzScheduler_$_NON_CLUSTERED shutting down.
|
||||
2025-04-17T07:05:48.694Z INFO 8 --- [ Thread-5] org.quartz.core.QuartzScheduler : Scheduler quartzScheduler_$_NON_CLUSTERED paused.
|
||||
2025-04-17T07:05:48.694Z INFO 8 --- [ Thread-5] org.quartz.core.QuartzScheduler : Scheduler quartzScheduler_$_NON_CLUSTERED shutdown complete.
|
||||
2025-04-17T07:05:48.712Z INFO 8 --- [ Thread-5] .j.s.m.m.s.MatchingQueueSubscriberLoader : Destroying matching Channel ca.uhn.fhir.jpa.subscription.channel.subscription.BroadcastingSubscribableChannelWrapper with name subscription-matching
|
||||
2025-04-17T07:05:48.735Z INFO 8 --- [ Thread-5] c.u.f.log.terminology_troubleshooting : Performing initial retrieval for non-expiring cache: org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain$FetchAllKey@2604b187
|
||||
2025-04-17T07:05:48.840Z INFO 8 --- [ Thread-5] c.u.f.log.terminology_troubleshooting : Initial retrieval for non-expiring cache org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain$FetchAllKey@2604b187 succeeded in 105ms
|
||||
2025-04-17T07:05:48.920Z INFO 8 --- [ Thread-5] c.u.f.j.sched.BaseSchedulerServiceImpl : Shutting down task scheduler...
|
||||
2025-04-17T07:05:48.920Z INFO 8 --- [ Thread-5] org.quartz.core.QuartzScheduler : Scheduler hapi-fhir-jpa-scheduler_$_NON_CLUSTERED shutting down.
|
||||
2025-04-17T07:05:48.920Z INFO 8 --- [ Thread-5] org.quartz.core.QuartzScheduler : Scheduler hapi-fhir-jpa-scheduler_$_NON_CLUSTERED paused.
|
||||
2025-04-17T07:05:49.143Z INFO 8 --- [ Thread-5] org.quartz.core.QuartzScheduler : Scheduler hapi-fhir-jpa-scheduler_$_NON_CLUSTERED shutdown complete.
|
||||
2025-04-17T07:05:49.143Z INFO 8 --- [ Thread-5] org.quartz.core.QuartzScheduler : Scheduler hapi-fhir-jpa-scheduler_$_NON_CLUSTERED shutting down.
|
||||
2025-04-17T07:05:49.143Z INFO 8 --- [ Thread-5] org.quartz.core.QuartzScheduler : Scheduler hapi-fhir-jpa-scheduler_$_NON_CLUSTERED paused.
|
||||
2025-04-17T07:05:49.643Z INFO 8 --- [ Thread-5] org.quartz.core.QuartzScheduler : Scheduler hapi-fhir-jpa-scheduler_$_NON_CLUSTERED shutdown complete.
|
||||
2025-04-17T07:05:49.664Z INFO 8 --- [ Thread-5] irLocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'HAPI_PU'
|
||||
2025-04-17T07:05:49.679Z INFO 8 --- [ Thread-5] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
|
||||
2025-04-17T07:05:49.694Z INFO 8 --- [ Thread-5] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
|
||||
|
||||
. ____ _ __ _ _
|
||||
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
|
||||
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
|
||||
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
|
||||
' |____| .__|_| |_|_| |_\__, | / / / /
|
||||
=========|_|==============|___/=/_/_/_/
|
||||
|
||||
:: Spring Boot :: (v3.3.5)
|
||||
|
||||
2025-04-17T07:08:24.261Z INFO 8 --- [ main] ca.uhn.fhir.jpa.starter.Application : Starting Application using Java 17.0.14 with PID 8 (/usr/local/tomcat/webapps/ROOT/WEB-INF/classes started by root in /usr/local/tomcat)
|
||||
2025-04-17T07:08:24.266Z INFO 8 --- [ main] ca.uhn.fhir.jpa.starter.Application : No active profile set, falling back to 1 default profile: "default"
|
||||
2025-04-17T07:08:26.438Z INFO 8 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
|
||||
2025-04-17T07:08:26.907Z INFO 8 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 446 ms. Found 52 JPA repository interfaces.
|
||||
2025-04-17T07:08:30.475Z WARN 8 --- [ 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-04-17T07:08:30.845Z INFO 8 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 6539 ms
|
||||
2025-04-17T07:08:31.211Z INFO 8 --- [ main] ca.uhn.fhir.util.VersionUtil : HAPI FHIR version 8.0.0 - Rev 091beb6d18
|
||||
2025-04-17T07:08:31.228Z INFO 8 --- [ main] ca.uhn.fhir.context.FhirContext : Creating new FHIR context for FHIR version [R4]
|
||||
2025-04-17T07:08:31.384Z INFO 8 --- [ main] c.u.f.j.s.common.FhirServerConfigCommon : Server configured to allow contains searches
|
||||
2025-04-17T07:08:31.384Z INFO 8 --- [ main] c.u.f.j.s.common.FhirServerConfigCommon : Server configured to deny multiple deletes
|
||||
2025-04-17T07:08:31.384Z INFO 8 --- [ main] c.u.f.j.s.common.FhirServerConfigCommon : Server configured to deny external references
|
||||
2025-04-17T07:08:31.384Z INFO 8 --- [ main] c.u.f.j.s.common.FhirServerConfigCommon : Server configured to enable DAO scheduling
|
||||
2025-04-17T07:08:31.384Z INFO 8 --- [ main] c.u.f.j.s.common.FhirServerConfigCommon : Server configured to disable delete expunges
|
||||
2025-04-17T07:08:31.384Z INFO 8 --- [ main] c.u.f.j.s.common.FhirServerConfigCommon : Server configured to enable expunges
|
||||
2025-04-17T07:08:31.384Z INFO 8 --- [ main] c.u.f.j.s.common.FhirServerConfigCommon : Server configured to allow overriding default search params
|
||||
2025-04-17T07:08:31.385Z INFO 8 --- [ main] c.u.f.j.s.common.FhirServerConfigCommon : Server configured to disable auto-creating placeholder references
|
||||
2025-04-17T07:08:31.385Z INFO 8 --- [ main] c.u.f.j.s.common.FhirServerConfigCommon : Server configured to auto-version references at paths []
|
||||
2025-04-17T07:08:31.385Z INFO 8 --- [ main] c.u.f.j.s.common.FhirServerConfigCommon : Server configured to enable value set pre-expansion
|
||||
2025-04-17T07:08:31.385Z INFO 8 --- [ main] c.u.f.j.s.common.FhirServerConfigCommon : Server configured to enable value set pre-expansion task
|
||||
2025-04-17T07:08:31.386Z INFO 8 --- [ main] c.u.f.j.s.common.FhirServerConfigCommon : Server configured for pre-expand value set default count of 1000
|
||||
2025-04-17T07:08:31.386Z INFO 8 --- [ main] c.u.f.j.s.common.FhirServerConfigCommon : Server configured for pre-expand value set max count of 1000
|
||||
2025-04-17T07:08:31.387Z INFO 8 --- [ main] c.u.f.j.s.common.FhirServerConfigCommon : Server configured for maximum expansion size of 1000
|
||||
2025-04-17T07:08:31.440Z INFO 8 --- [ main] c.u.f.j.s.common.FhirServerConfigCommon : Server configured to have a maximum fetch size of 'unlimited'
|
||||
2025-04-17T07:08:31.440Z INFO 8 --- [ main] c.u.f.j.s.common.FhirServerConfigCommon : Server configured to cache search results for 60000 milliseconds
|
||||
2025-04-17T07:08:31.441Z INFO 8 --- [ main] c.u.f.j.s.common.FhirServerConfigCommon : Server configured to use 'ALPHANUMERIC' Client ID Strategy
|
||||
2025-04-17T07:08:31.669Z INFO 8 --- [ main] o.hibernate.jpa.internal.util.LogHelper : HHH000204: Processing PersistenceUnitInfo [name: HAPI_PU]
|
||||
2025-04-17T07:08:31.967Z INFO 8 --- [ main] org.hibernate.Version : HHH000412: Hibernate ORM core version 6.6.4.Final
|
||||
2025-04-17T07:08:32.007Z INFO 8 --- [ main] .f.j.l.FilteringSqlLoggerImplContributor : Adding service: SqlStatementFilteringLogger
|
||||
2025-04-17T07:08:32.136Z INFO 8 --- [ main] o.h.c.internal.RegionFactoryInitiator : HHH000026: Second-level cache disabled
|
||||
2025-04-17T07:08:32.571Z INFO 8 --- [ main] o.h.e.boot.internal.EnversServiceImpl : Envers integration enabled? : true
|
||||
2025-04-17T07:08:33.153Z INFO 8 --- [ main] o.s.o.j.p.SpringPersistenceUnitInfo : No LoadTimeWeaver setup: ignoring JPA class transformer
|
||||
2025-04-17T07:08:33.239Z INFO 8 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
|
||||
2025-04-17T07:08:34.098Z INFO 8 --- [ main] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Added connection conn1: url=jdbc:h2:file:/app/h2-data/fhir user=SA
|
||||
2025-04-17T07:08:34.102Z INFO 8 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
|
||||
2025-04-17T07:08:34.192Z INFO 8 --- [ 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-04-17T07:08:35.793Z INFO 8 --- [ main] b.i.HibernateSearchPreIntegrationService : HSEARCH000034: Hibernate Search version 7.2.1.Final
|
||||
2025-04-17T07:08:35.986Z INFO 8 --- [ main] o.h.e.c.i.m.AuditMetadataGenerator : Adding properties for entity: ca.uhn.fhir.jpa.entity.MdmLink
|
||||
2025-04-17T07:08:40.053Z INFO 8 --- [ 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-04-17T07:08:40.874Z INFO 8 --- [ main] irLocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'HAPI_PU'
|
||||
2025-04-17T07:08:42.403Z INFO 8 --- [ main] .u.f.c.s.DefaultProfileValidationSupport : Loading structure definitions from classpath: /org/hl7/fhir/r4/model/profile/profiles-resources.xml
|
||||
2025-04-17T07:08:42.428Z INFO 8 --- [ main] ca.uhn.fhir.util.XmlUtil : FHIR XML procesing will use StAX implementation 'Woodstox' version '6.4.0'
|
||||
2025-04-17T07:08:43.725Z INFO 8 --- [ main] .u.f.c.s.DefaultProfileValidationSupport : Loading structure definitions from classpath: /org/hl7/fhir/r4/model/profile/profiles-types.xml
|
||||
2025-04-17T07:08:43.859Z INFO 8 --- [ main] .u.f.c.s.DefaultProfileValidationSupport : Loading structure definitions from classpath: /org/hl7/fhir/r4/model/profile/profiles-others.xml
|
||||
2025-04-17T07:08:44.102Z INFO 8 --- [ main] .u.f.c.s.DefaultProfileValidationSupport : Loading structure definitions from classpath: /org/hl7/fhir/r4/model/extension/extension-definitions.xml
|
||||
2025-04-17T07:08:45.368Z INFO 8 --- [ main] o.s.d.j.r.query.QueryEnhancerFactory : Hibernate is in classpath; If applicable, HQL parser will be used.
|
||||
2025-04-17T07:08:51.064Z INFO 8 --- [ main] c.u.f.j.sched.BaseSchedulerServiceImpl : Creating Local Scheduler
|
||||
2025-04-17T07:08:51.147Z INFO 8 --- [ main] org.quartz.impl.StdSchedulerFactory : Using default implementation for ThreadExecutor
|
||||
2025-04-17T07:08:51.181Z INFO 8 --- [ main] org.quartz.core.SchedulerSignalerImpl : Initialized Scheduler Signaller of type: class org.quartz.core.SchedulerSignalerImpl
|
||||
2025-04-17T07:08:51.182Z INFO 8 --- [ main] org.quartz.core.QuartzScheduler : Quartz Scheduler v.2.3.2 created.
|
||||
2025-04-17T07:08:51.183Z INFO 8 --- [ main] org.quartz.simpl.RAMJobStore : RAMJobStore initialized.
|
||||
2025-04-17T07:08:51.186Z INFO 8 --- [ 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-04-17T07:08:51.187Z INFO 8 --- [ main] org.quartz.impl.StdSchedulerFactory : Quartz scheduler 'hapi-fhir-jpa-scheduler' initialized from an externally provided properties instance.
|
||||
2025-04-17T07:08:51.187Z INFO 8 --- [ main] org.quartz.impl.StdSchedulerFactory : Quartz scheduler version: 2.3.2
|
||||
2025-04-17T07:08:51.187Z INFO 8 --- [ main] org.quartz.core.QuartzScheduler : JobFactory set to: ca.uhn.fhir.jpa.sched.AutowiringSpringBeanJobFactory@28e7a4df
|
||||
2025-04-17T07:08:51.187Z INFO 8 --- [ main] org.quartz.core.QuartzScheduler : Scheduler hapi-fhir-jpa-scheduler_$_NON_CLUSTERED paused.
|
||||
2025-04-17T07:08:51.188Z INFO 8 --- [ main] c.u.f.j.sched.BaseSchedulerServiceImpl : Creating Clustered Scheduler
|
||||
2025-04-17T07:08:51.190Z INFO 8 --- [ main] org.quartz.impl.StdSchedulerFactory : Using default implementation for ThreadExecutor
|
||||
2025-04-17T07:08:51.198Z INFO 8 --- [ main] org.quartz.core.SchedulerSignalerImpl : Initialized Scheduler Signaller of type: class org.quartz.core.SchedulerSignalerImpl
|
||||
2025-04-17T07:08:51.198Z INFO 8 --- [ main] org.quartz.core.QuartzScheduler : Quartz Scheduler v.2.3.2 created.
|
||||
2025-04-17T07:08:51.199Z INFO 8 --- [ main] org.quartz.simpl.RAMJobStore : RAMJobStore initialized.
|
||||
2025-04-17T07:08:51.199Z INFO 8 --- [ 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-04-17T07:08:51.199Z INFO 8 --- [ main] org.quartz.impl.StdSchedulerFactory : Quartz scheduler 'hapi-fhir-jpa-scheduler' initialized from an externally provided properties instance.
|
||||
2025-04-17T07:08:51.199Z INFO 8 --- [ main] org.quartz.impl.StdSchedulerFactory : Quartz scheduler version: 2.3.2
|
||||
2025-04-17T07:08:51.199Z INFO 8 --- [ main] org.quartz.core.QuartzScheduler : JobFactory set to: ca.uhn.fhir.jpa.sched.AutowiringSpringBeanJobFactory@28e7a4df
|
||||
2025-04-17T07:08:51.199Z INFO 8 --- [ main] org.quartz.core.QuartzScheduler : Scheduler hapi-fhir-jpa-scheduler_$_NON_CLUSTERED paused.
|
||||
2025-04-17T07:08:51.912Z INFO 8 --- [ main] c.u.f.log.terminology_troubleshooting : Performing initial retrieval for non-expiring cache: org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain$FetchAllKey@823fb59
|
||||
2025-04-17T07:08:51.914Z INFO 8 --- [ main] c.u.f.log.terminology_troubleshooting : Initial retrieval for non-expiring cache org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain$FetchAllKey@823fb59 succeeded in 2ms
|
||||
2025-04-17T07:08:51.927Z INFO 8 --- [ main] c.u.f.log.terminology_troubleshooting : Performing initial retrieval for non-expiring cache: org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain$FetchAllKey@823fb59
|
||||
2025-04-17T07:08:51.928Z INFO 8 --- [ main] c.u.f.log.terminology_troubleshooting : Initial retrieval for non-expiring cache org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain$FetchAllKey@823fb59 succeeded in 1ms
|
||||
2025-04-17T07:08:51.929Z INFO 8 --- [ main] c.u.f.log.terminology_troubleshooting : Performing initial retrieval for non-expiring cache: org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain$FetchAllKey@823fb59
|
||||
2025-04-17T07:08:51.930Z INFO 8 --- [ main] c.u.f.log.terminology_troubleshooting : Initial retrieval for non-expiring cache org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain$FetchAllKey@823fb59 succeeded in 1ms
|
||||
2025-04-17T07:08:51.932Z INFO 8 --- [ main] c.u.f.log.terminology_troubleshooting : Performing initial retrieval for non-expiring cache: org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain$FetchAllKey@823fb59
|
||||
2025-04-17T07:08:51.932Z INFO 8 --- [ main] c.u.f.log.terminology_troubleshooting : Initial retrieval for non-expiring cache org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain$FetchAllKey@823fb59 succeeded in 0ms
|
||||
2025-04-17T07:08:51.933Z INFO 8 --- [ main] c.u.f.log.terminology_troubleshooting : Performing initial retrieval for non-expiring cache: org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain$FetchAllKey@823fb59
|
||||
2025-04-17T07:08:51.933Z INFO 8 --- [ main] c.u.f.log.terminology_troubleshooting : Initial retrieval for non-expiring cache org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain$FetchAllKey@823fb59 succeeded in 0ms
|
||||
2025-04-17T07:08:51.934Z INFO 8 --- [ main] c.u.f.log.terminology_troubleshooting : Performing initial retrieval for non-expiring cache: org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain$FetchAllKey@823fb59
|
||||
2025-04-17T07:08:51.935Z INFO 8 --- [ main] c.u.f.log.terminology_troubleshooting : Initial retrieval for non-expiring cache org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain$FetchAllKey@823fb59 succeeded in 1ms
|
||||
2025-04-17T07:08:52.063Z INFO 8 --- [ main] ca.uhn.fhir.context.FhirContext : Creating new FHIR context for FHIR version [R4]
|
||||
2025-04-17T07:08:52.472Z INFO 8 --- [ main] c.u.f.j.starter.common.StarterJpaConfig : CORS is enabled on this server
|
||||
2025-04-17T07:08:52.477Z INFO 8 --- [ main] c.u.f.j.starter.common.StarterJpaConfig : CORS allows the following origins: *
|
||||
2025-04-17T07:08:52.528Z INFO 8 --- [ main] ca.uhn.fhir.context.FhirContext : Creating new FHIR context for FHIR version [R5]
|
||||
2025-04-17T07:09:00.375Z INFO 8 --- [ main] ca.uhn.fhir.context.FhirContext : Creating new FHIR context for FHIR version [R4]
|
||||
2025-04-17T07:09:03.002Z WARN 8 --- [ 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-04-17T07:09:03.862Z INFO 8 --- [ 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-04-17T07:09:04.932Z INFO 8 --- [ main] org.quartz.impl.StdSchedulerFactory : Using default implementation for ThreadExecutor
|
||||
2025-04-17T07:09:04.955Z INFO 8 --- [ main] org.quartz.core.SchedulerSignalerImpl : Initialized Scheduler Signaller of type: class org.quartz.core.SchedulerSignalerImpl
|
||||
2025-04-17T07:09:04.956Z INFO 8 --- [ main] org.quartz.core.QuartzScheduler : Quartz Scheduler v.2.3.2 created.
|
||||
2025-04-17T07:09:04.956Z INFO 8 --- [ main] org.quartz.simpl.RAMJobStore : RAMJobStore initialized.
|
||||
2025-04-17T07:09:04.956Z INFO 8 --- [ 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-04-17T07:09:04.956Z INFO 8 --- [ main] org.quartz.impl.StdSchedulerFactory : Quartz scheduler 'quartzScheduler' initialized from an externally provided properties instance.
|
||||
2025-04-17T07:09:04.956Z INFO 8 --- [ main] org.quartz.impl.StdSchedulerFactory : Quartz scheduler version: 2.3.2
|
||||
2025-04-17T07:09:04.956Z INFO 8 --- [ main] org.quartz.core.QuartzScheduler : JobFactory set to: org.springframework.scheduling.quartz.SpringBeanJobFactory@4af6d469
|
||||
2025-04-17T07:09:05.031Z INFO 8 --- [ main] o.s.b.a.e.web.EndpointLinksResolver : Exposing 1 endpoint beneath base path '/actuator'
|
||||
2025-04-17T07:09:05.376Z INFO 8 --- [ main] o.s.s.quartz.SchedulerFactoryBean : Starting Quartz Scheduler now
|
||||
2025-04-17T07:09:05.377Z INFO 8 --- [ main] org.quartz.core.QuartzScheduler : Scheduler quartzScheduler_$_NON_CLUSTERED started.
|
||||
2025-04-17T07:09:05.385Z INFO 8 --- [ main] ca.uhn.fhir.log.batch_troubleshooting : Registering Batch2 Job Definition: BULK_IMPORT_PULL / 1
|
||||
2025-04-17T07:09:05.386Z INFO 8 --- [ main] ca.uhn.fhir.log.batch_troubleshooting : Registering Batch2 Job Definition: REINDEX / 1
|
||||
2025-04-17T07:09:05.387Z INFO 8 --- [ main] ca.uhn.fhir.log.batch_troubleshooting : Registering Batch2 Job Definition: REINDEX / 2
|
||||
2025-04-17T07:09:05.387Z INFO 8 --- [ main] ca.uhn.fhir.log.batch_troubleshooting : Registering Batch2 Job Definition: DELETE_EXPUNGE / 1
|
||||
2025-04-17T07:09:05.387Z INFO 8 --- [ main] ca.uhn.fhir.log.batch_troubleshooting : Registering Batch2 Job Definition: BULK_EXPORT / 1
|
||||
2025-04-17T07:09:05.387Z INFO 8 --- [ main] ca.uhn.fhir.log.batch_troubleshooting : Registering Batch2 Job Definition: BULK_EXPORT / 2
|
||||
2025-04-17T07:09:05.387Z INFO 8 --- [ main] ca.uhn.fhir.log.batch_troubleshooting : Registering Batch2 Job Definition: termCodeSystemVersionDeleteJob / 1
|
||||
2025-04-17T07:09:05.387Z INFO 8 --- [ main] ca.uhn.fhir.log.batch_troubleshooting : Registering Batch2 Job Definition: termCodeSystemDeleteJob / 1
|
||||
2025-04-17T07:09:05.387Z INFO 8 --- [ main] ca.uhn.fhir.log.batch_troubleshooting : Registering Batch2 Job Definition: bulkImportJob / 1
|
||||
2025-04-17T07:09:05.390Z INFO 8 --- [ 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-04-17T07:09:07.149Z INFO 8 --- [ main] c.u.f.j.s.registry.JpaSearchParamCache : Have 0 unique search params
|
||||
2025-04-17T07:09:07.246Z INFO 8 --- [ main] c.u.f.j.sched.BaseSchedulerServiceImpl : Scheduling 15 jobs in application
|
||||
2025-04-17T07:09:07.252Z INFO 8 --- [ main] c.u.f.j.sched.BaseSchedulerServiceImpl : Scheduling local job ca.uhn.fhir.jpa.term.TermDeferredStorageSvcImpl$Job with interval 5000ms
|
||||
2025-04-17T07:09:07.264Z INFO 8 --- [ main] c.u.f.j.sched.BaseSchedulerServiceImpl : Scheduling local job ca.uhn.fhir.jpa.cache.ResourceChangeListenerCacheRefresherImpl with interval 00:00:10.000
|
||||
2025-04-17T07:09:07.266Z INFO 8 --- [ main] c.u.f.j.sched.BaseSchedulerServiceImpl : Scheduling clustered job ca.uhn.fhir.jpa.search.reindex.ResourceReindexingSvcImpl with interval 00:00:10.000
|
||||
2025-04-17T07:09:07.268Z INFO 8 --- [ 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-04-17T07:09:07.271Z INFO 8 --- [ 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-04-17T07:09:07.273Z INFO 8 --- [ main] c.u.f.j.sched.BaseSchedulerServiceImpl : Scheduling clustered job ca.uhn.fhir.jpa.search.warm.CacheWarmingSvcImpl with interval 00:00:10.000
|
||||
2025-04-17T07:09:07.275Z INFO 8 --- [ main] c.u.f.j.sched.BaseSchedulerServiceImpl : Scheduling clustered job ca.uhn.fhir.jpa.term.TermReadSvcImpl with interval 00:10:00
|
||||
2025-04-17T07:09:07.276Z INFO 8 --- [ main] c.u.f.j.sched.BaseSchedulerServiceImpl : Scheduling local job ca.uhn.fhir.jpa.term.TermReindexingSvcImpl with interval 00:01:00.000
|
||||
2025-04-17T07:09:07.278Z INFO 8 --- [ main] c.u.f.j.sched.BaseSchedulerServiceImpl : Scheduling clustered job ca.uhn.fhir.jpa.search.SearchUrlJobMaintenanceSvcImpl$SearchUrlMaintenanceJob with interval 00:10:00
|
||||
2025-04-17T07:09:07.280Z INFO 8 --- [ main] c.u.f.j.sched.BaseSchedulerServiceImpl : Scheduling clustered job ca.uhn.fhir.jpa.search.StaleSearchDeletingSvcImpl with interval 00:01:00.000
|
||||
2025-04-17T07:09:07.282Z INFO 8 --- [ main] c.u.f.j.sched.BaseSchedulerServiceImpl : Scheduling local job ca.uhn.fhir.jpa.util.ResourceCountCache with interval 00:10:00
|
||||
2025-04-17T07:09:07.285Z INFO 8 --- [ main] c.u.f.j.sched.BaseSchedulerServiceImpl : Scheduling local job ca.uhn.fhir.jpa.subscription.triggering.SubscriptionTriggeringSvcImpl with interval 5000ms
|
||||
2025-04-17T07:09:07.288Z INFO 8 --- [ main] c.u.f.j.sched.BaseSchedulerServiceImpl : Scheduling clustered job ca.uhn.fhir.jpa.subscription.async.AsyncResourceModifiedProcessingSchedulerSvc with interval 5000ms
|
||||
2025-04-17T07:09:07.289Z INFO 8 --- [ main] c.u.f.j.sched.BaseSchedulerServiceImpl : Scheduling clustered job ca.uhn.fhir.batch2.coordinator.ReductionStepExecutorServiceImpl$ReductionStepExecutorScheduledJob with interval 00:00:10.000
|
||||
2025-04-17T07:09:07.289Z INFO 8 --- [ main] c.u.f.j.sched.BaseSchedulerServiceImpl : Scheduling clustered job ca.uhn.fhir.batch2.maintenance.JobMaintenanceServiceImpl$JobMaintenanceScheduledJob with interval 00:01:00.000
|
||||
2025-04-17T07:09:07.289Z INFO 8 --- [ main] c.u.f.j.sched.BaseSchedulerServiceImpl : Starting task schedulers for context application
|
||||
2025-04-17T07:09:07.289Z INFO 8 --- [ main] c.uhn.fhir.jpa.sched.BaseHapiScheduler : Starting scheduler hapi-fhir-jpa-scheduler-local
|
||||
2025-04-17T07:09:07.290Z INFO 8 --- [ main] org.quartz.core.QuartzScheduler : Scheduler hapi-fhir-jpa-scheduler_$_NON_CLUSTERED started.
|
||||
2025-04-17T07:09:07.290Z INFO 8 --- [ main] c.uhn.fhir.jpa.sched.BaseHapiScheduler : Starting scheduler hapi-fhir-jpa-scheduler-clustered
|
||||
2025-04-17T07:09:07.290Z INFO 8 --- [ main] org.quartz.core.QuartzScheduler : Scheduler hapi-fhir-jpa-scheduler_$_NON_CLUSTERED started.
|
||||
2025-04-17T07:09:07.290Z WARN 8 --- [ main] u.f.j.u.PartitionedIdModeVerificationSvc : Dialect is not a HAPI FHIR dialect: org.hibernate.dialect.H2Dialect, version: 2.3.232
|
||||
2025-04-17T07:09:07.295Z INFO 8 --- [ main] ca.uhn.fhir.jpa.starter.Application : Started Application in 44.05 seconds (process running for 60.148)
|
||||
2025-04-17T07:09:07.384Z INFO 8 --- [ler-clustered-2] .s.BulkDataExportJobSchedulingHelperImpl : Finished bulk export job deletion with nothing to do
|
||||
2025-04-17T07:09:07.446Z INFO 8 --- [ main] ca.uhn.fhir.rest.server.RestfulServer : Initializing HAPI FHIR restful server running in R4 mode
|
||||
2025-04-17T07:09:07.448Z INFO 8 --- [ main] ca.uhn.fhir.rest.server.RestfulServer : Added 147 resource provider(s). Total 147
|
||||
2025-04-17T07:09:08.404Z INFO 8 --- [ main] ca.uhn.fhir.rest.server.RestfulServer : Added 5 plain provider(s). Total 5
|
||||
2025-04-17T07:09:08.422Z INFO 8 --- [ main] ca.uhn.fhir.rest.server.RestfulServer : Removing RESTful methods for: class ca.uhn.fhir.jpa.provider.JpaCapabilityStatementProvider
|
||||
2025-04-17T07:09:08.423Z INFO 8 --- [ main] ca.uhn.fhir.rest.server.RestfulServer : OperationDefinition binding of ca.uhn.fhir.rest.server.method.ReadMethodBinding@711dda46 was removed
|
||||
2025-04-17T07:09:08.423Z INFO 8 --- [ main] ca.uhn.fhir.rest.server.RestfulServer : OperationDefinition binding of ca.uhn.fhir.rest.server.method.ReadMethodBinding@231712f was removed
|
||||
2025-04-17T07:09:08.489Z INFO 8 --- [ main] ca.uhn.fhir.rest.server.RestfulServer : A FHIR has been lit on this server
|
83
logs/tomcat_err.log
Normal file
83
logs/tomcat_err.log
Normal file
@ -0,0 +1,83 @@
|
||||
17-Apr-2025 06:25:19.367 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Server version name: Apache Tomcat/10.1.40
|
||||
17-Apr-2025 06:25:19.375 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Server built: Apr 1 2025 17:20:53 UTC
|
||||
17-Apr-2025 06:25:19.375 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Server version number: 10.1.40.0
|
||||
17-Apr-2025 06:25:19.375 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log OS Name: Linux
|
||||
17-Apr-2025 06:25:19.375 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log OS Version: 5.15.167.4-microsoft-standard-WSL2
|
||||
17-Apr-2025 06:25:19.375 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Architecture: amd64
|
||||
17-Apr-2025 06:25:19.376 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Java Home: /opt/java/openjdk
|
||||
17-Apr-2025 06:25:19.376 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log JVM Version: 17.0.14+7
|
||||
17-Apr-2025 06:25:19.376 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log JVM Vendor: Eclipse Adoptium
|
||||
17-Apr-2025 06:25:19.376 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log CATALINA_BASE: /usr/local/tomcat
|
||||
17-Apr-2025 06:25:19.376 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log CATALINA_HOME: /usr/local/tomcat
|
||||
17-Apr-2025 06:25:19.400 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Djava.util.logging.config.file=/usr/local/tomcat/conf/logging.properties
|
||||
17-Apr-2025 06:25:19.401 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager
|
||||
17-Apr-2025 06:25:19.401 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Djdk.tls.ephemeralDHKeySize=2048
|
||||
17-Apr-2025 06:25:19.401 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Djava.protocol.handler.pkgs=org.apache.catalina.webresources
|
||||
17-Apr-2025 06:25:19.401 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Dsun.io.useCanonCaches=false
|
||||
17-Apr-2025 06:25:19.401 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Dorg.apache.catalina.security.SecurityListener.UMASK=0027
|
||||
17-Apr-2025 06:25:19.401 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: --add-opens=java.base/java.lang=ALL-UNNAMED
|
||||
17-Apr-2025 06:25:19.401 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: --add-opens=java.base/java.lang.reflect=ALL-UNNAMED
|
||||
17-Apr-2025 06:25:19.401 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: --add-opens=java.base/java.io=ALL-UNNAMED
|
||||
17-Apr-2025 06:25:19.401 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: --add-opens=java.base/java.util=ALL-UNNAMED
|
||||
17-Apr-2025 06:25:19.401 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: --add-opens=java.base/java.util.concurrent=ALL-UNNAMED
|
||||
17-Apr-2025 06:25:19.402 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: --add-opens=java.rmi/sun.rmi.transport=ALL-UNNAMED
|
||||
17-Apr-2025 06:25:19.402 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Dcatalina.base=/usr/local/tomcat
|
||||
17-Apr-2025 06:25:19.402 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Dcatalina.home=/usr/local/tomcat
|
||||
17-Apr-2025 06:25:19.402 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Djava.io.tmpdir=/usr/local/tomcat/temp
|
||||
17-Apr-2025 06:25:19.411 INFO [main] org.apache.catalina.core.AprLifecycleListener.lifecycleEvent Loaded Apache Tomcat Native library [2.0.8] using APR version [1.7.2].
|
||||
17-Apr-2025 06:25:19.416 INFO [main] org.apache.catalina.core.AprLifecycleListener.initializeSSL OpenSSL successfully initialized [OpenSSL 3.0.13 30 Jan 2024]
|
||||
17-Apr-2025 06:25:19.821 INFO [main] org.apache.coyote.AbstractProtocol.init Initializing ProtocolHandler ["http-nio-8080"]
|
||||
17-Apr-2025 06:25:19.882 INFO [main] org.apache.catalina.startup.Catalina.load Server initialization in [794] milliseconds
|
||||
17-Apr-2025 06:25:20.017 INFO [main] org.apache.catalina.core.StandardService.startInternal Starting service [Catalina]
|
||||
17-Apr-2025 06:25:20.017 INFO [main] org.apache.catalina.core.StandardEngine.startInternal Starting Servlet engine: [Apache Tomcat/10.1.40]
|
||||
17-Apr-2025 06:25:20.048 INFO [main] org.apache.catalina.startup.HostConfig.deployWAR Deploying web application archive [/usr/local/tomcat/webapps/ROOT.war]
|
||||
17-Apr-2025 06:25:38.970 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.
|
||||
17-Apr-2025 06:26:26.803 INFO [main] org.apache.catalina.startup.HostConfig.deployWAR Deployment of web application archive [/usr/local/tomcat/webapps/ROOT.war] has finished in [66,753] ms
|
||||
17-Apr-2025 06:26:26.804 INFO [main] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory [/usr/local/tomcat/webapps/custom]
|
||||
17-Apr-2025 06:26:26.847 INFO [main] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory [/usr/local/tomcat/webapps/custom] has finished in [42] ms
|
||||
17-Apr-2025 06:26:26.854 INFO [main] org.apache.coyote.AbstractProtocol.start Starting ProtocolHandler ["http-nio-8080"]
|
||||
17-Apr-2025 06:26:26.893 INFO [main] org.apache.catalina.startup.Catalina.start Server startup in [67014] milliseconds
|
||||
17-Apr-2025 07:05:48.360 INFO [Thread-5] org.apache.coyote.AbstractProtocol.pause Pausing ProtocolHandler ["http-nio-8080"]
|
||||
17-Apr-2025 07:05:48.381 INFO [Thread-5] org.apache.catalina.core.StandardService.stopInternal Stopping service [Catalina]
|
||||
17-Apr-2025 07:05:49.774 SEVERE [Thread-5] org.apache.catalina.loader.WebappClassLoaderBase.checkThreadLocalMapForLeaks The web application [ROOT] created a ThreadLocal with key of type [org.springframework.boot.SpringBootExceptionHandler.LoggedExceptionHandlerThreadLocal] (value [org.springframework.boot.SpringBootExceptionHandler$LoggedExceptionHandlerThreadLocal@60f94e9]) and a value of type [org.springframework.boot.SpringBootExceptionHandler] (value [org.springframework.boot.SpringBootExceptionHandler@46b9f0e9]) but failed to remove it when the web application was stopped. Threads are going to be renewed over time to try and avoid a probable memory leak.
|
||||
17-Apr-2025 07:05:49.827 INFO [Thread-5] org.apache.coyote.AbstractProtocol.stop Stopping ProtocolHandler ["http-nio-8080"]
|
||||
17-Apr-2025 07:05:49.841 INFO [Thread-5] org.apache.coyote.AbstractProtocol.destroy Destroying ProtocolHandler ["http-nio-8080"]
|
||||
17-Apr-2025 07:08:07.777 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Server version name: Apache Tomcat/10.1.40
|
||||
17-Apr-2025 07:08:07.785 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Server built: Apr 1 2025 17:20:53 UTC
|
||||
17-Apr-2025 07:08:07.786 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Server version number: 10.1.40.0
|
||||
17-Apr-2025 07:08:07.786 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log OS Name: Linux
|
||||
17-Apr-2025 07:08:07.786 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log OS Version: 5.15.167.4-microsoft-standard-WSL2
|
||||
17-Apr-2025 07:08:07.786 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Architecture: amd64
|
||||
17-Apr-2025 07:08:07.787 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Java Home: /opt/java/openjdk
|
||||
17-Apr-2025 07:08:07.787 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log JVM Version: 17.0.14+7
|
||||
17-Apr-2025 07:08:07.787 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log JVM Vendor: Eclipse Adoptium
|
||||
17-Apr-2025 07:08:07.787 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log CATALINA_BASE: /usr/local/tomcat
|
||||
17-Apr-2025 07:08:07.787 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log CATALINA_HOME: /usr/local/tomcat
|
||||
17-Apr-2025 07:08:07.813 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Djava.util.logging.config.file=/usr/local/tomcat/conf/logging.properties
|
||||
17-Apr-2025 07:08:07.813 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager
|
||||
17-Apr-2025 07:08:07.814 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Djdk.tls.ephemeralDHKeySize=2048
|
||||
17-Apr-2025 07:08:07.814 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Djava.protocol.handler.pkgs=org.apache.catalina.webresources
|
||||
17-Apr-2025 07:08:07.814 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Dsun.io.useCanonCaches=false
|
||||
17-Apr-2025 07:08:07.814 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Dorg.apache.catalina.security.SecurityListener.UMASK=0027
|
||||
17-Apr-2025 07:08:07.814 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: --add-opens=java.base/java.lang=ALL-UNNAMED
|
||||
17-Apr-2025 07:08:07.815 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: --add-opens=java.base/java.lang.reflect=ALL-UNNAMED
|
||||
17-Apr-2025 07:08:07.815 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: --add-opens=java.base/java.io=ALL-UNNAMED
|
||||
17-Apr-2025 07:08:07.815 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: --add-opens=java.base/java.util=ALL-UNNAMED
|
||||
17-Apr-2025 07:08:07.815 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: --add-opens=java.base/java.util.concurrent=ALL-UNNAMED
|
||||
17-Apr-2025 07:08:07.815 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: --add-opens=java.rmi/sun.rmi.transport=ALL-UNNAMED
|
||||
17-Apr-2025 07:08:07.815 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Dcatalina.base=/usr/local/tomcat
|
||||
17-Apr-2025 07:08:07.815 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Dcatalina.home=/usr/local/tomcat
|
||||
17-Apr-2025 07:08:07.815 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Djava.io.tmpdir=/usr/local/tomcat/temp
|
||||
17-Apr-2025 07:08:07.826 INFO [main] org.apache.catalina.core.AprLifecycleListener.lifecycleEvent Loaded Apache Tomcat Native library [2.0.8] using APR version [1.7.2].
|
||||
17-Apr-2025 07:08:07.831 INFO [main] org.apache.catalina.core.AprLifecycleListener.initializeSSL OpenSSL successfully initialized [OpenSSL 3.0.13 30 Jan 2024]
|
||||
17-Apr-2025 07:08:08.209 INFO [main] org.apache.coyote.AbstractProtocol.init Initializing ProtocolHandler ["http-nio-8080"]
|
||||
17-Apr-2025 07:08:08.251 INFO [main] org.apache.catalina.startup.Catalina.load Server initialization in [759] milliseconds
|
||||
17-Apr-2025 07:08:08.340 INFO [main] org.apache.catalina.core.StandardService.startInternal Starting service [Catalina]
|
||||
17-Apr-2025 07:08:08.340 INFO [main] org.apache.catalina.core.StandardEngine.startInternal Starting Servlet engine: [Apache Tomcat/10.1.40]
|
||||
17-Apr-2025 07:08:08.368 INFO [main] org.apache.catalina.startup.HostConfig.deployWAR Deploying web application archive [/usr/local/tomcat/webapps/ROOT.war]
|
||||
17-Apr-2025 07:08:22.061 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.
|
||||
17-Apr-2025 07:09:08.525 INFO [main] org.apache.catalina.startup.HostConfig.deployWAR Deployment of web application archive [/usr/local/tomcat/webapps/ROOT.war] has finished in [60,155] ms
|
||||
17-Apr-2025 07:09:08.528 INFO [main] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory [/usr/local/tomcat/webapps/custom]
|
||||
17-Apr-2025 07:09:08.565 INFO [main] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory [/usr/local/tomcat/webapps/custom] has finished in [37] ms
|
||||
17-Apr-2025 07:09:08.575 INFO [main] org.apache.coyote.AbstractProtocol.start Starting ProtocolHandler ["http-nio-8080"]
|
||||
17-Apr-2025 07:09:08.662 INFO [main] org.apache.catalina.startup.Catalina.start Server startup in [60427] milliseconds
|
72
services.py
72
services.py
@ -8,6 +8,9 @@ from flask import current_app, Blueprint, request, jsonify
|
||||
from collections import defaultdict
|
||||
from pathlib import Path
|
||||
import datetime
|
||||
import subprocess
|
||||
import tempfile
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
# Define Blueprint
|
||||
services_bp = Blueprint('services', __name__)
|
||||
@ -1438,3 +1441,72 @@ if __name__ == '__main__':
|
||||
else:
|
||||
print(f"\n--- Skipping Processing Test (Import failed for {pkg_name_to_test}#{pkg_version_to_test}) ---")
|
||||
|
||||
# Add new functions for GoFSH integration
|
||||
def run_gofsh(input_path, output_dir, output_style, log_level, fhir_version=None):
|
||||
"""Run GoFSH on the input FHIR resource and return the FSH output."""
|
||||
cmd = ["gofsh", input_path, "-o", output_dir, "-s", output_style, "-l", log_level]
|
||||
if fhir_version:
|
||||
cmd.extend(["-u", fhir_version])
|
||||
|
||||
try:
|
||||
result = subprocess.run(
|
||||
cmd,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=True
|
||||
)
|
||||
# Read all FSH files from the output directory
|
||||
fsh_content = []
|
||||
for root, _, files in os.walk(output_dir):
|
||||
for file in files:
|
||||
if file.endswith(".fsh"):
|
||||
with open(os.path.join(root, file), 'r', encoding='utf-8') as f:
|
||||
fsh_content.append(f.read())
|
||||
logger.info(f"GoFSH executed successfully for {input_path}")
|
||||
return "\n\n".join(fsh_content), None
|
||||
except subprocess.CalledProcessError as e:
|
||||
logger.error(f"GoFSH failed: {e.stderr}")
|
||||
return None, f"GoFSH failed: {e.stderr}"
|
||||
except Exception as e:
|
||||
logger.error(f"Error running GoFSH: {str(e)}", exc_info=True)
|
||||
return None, f"Error running GoFSH: {str(e)}"
|
||||
|
||||
def process_fhir_input(input_mode, fhir_file, fhir_text):
|
||||
"""Process user input (file or text) and save to a temporary file."""
|
||||
temp_dir = tempfile.mkdtemp()
|
||||
temp_file = None
|
||||
|
||||
try:
|
||||
if input_mode == 'file' and fhir_file:
|
||||
content = fhir_file.read().decode('utf-8')
|
||||
file_type = 'json' if content.strip().startswith('{') else 'xml'
|
||||
temp_file = os.path.join(temp_dir, f"input.{file_type}")
|
||||
with open(temp_file, 'w', encoding='utf-8') as f:
|
||||
f.write(content)
|
||||
elif input_mode == 'text' and fhir_text:
|
||||
content = fhir_text.strip()
|
||||
file_type = 'json' if content.startswith('{') else 'xml'
|
||||
temp_file = os.path.join(temp_dir, f"input.{file_type}")
|
||||
with open(temp_file, 'w', encoding='utf-8') as f:
|
||||
f.write(content)
|
||||
else:
|
||||
return None, None, "No input provided"
|
||||
|
||||
# Basic validation
|
||||
if file_type == 'json':
|
||||
try:
|
||||
json.loads(content)
|
||||
except json.JSONDecodeError:
|
||||
return None, None, "Invalid JSON format"
|
||||
elif file_type == 'xml':
|
||||
try:
|
||||
ET.fromstring(content)
|
||||
except ET.ParseError:
|
||||
return None, None, "Invalid XML format"
|
||||
|
||||
logger.debug(f"Processed input: {temp_file}")
|
||||
return temp_file, temp_dir, None
|
||||
except Exception as e:
|
||||
logger.error(f"Error processing input: {str(e)}", exc_info=True)
|
||||
return None, None, f"Error processing input: {str(e)}"
|
||||
|
||||
|
5
static/uploads/fsh_output/input/fsh/aliases.fsh
Normal file
5
static/uploads/fsh_output/input/fsh/aliases.fsh
Normal file
@ -0,0 +1,5 @@
|
||||
Alias: $sct = http://snomed.info/sct
|
||||
Alias: $loinc = http://loinc.org
|
||||
Alias: $ihi-status-1 = https://healthterminologies.gov.au/fhir/CodeSystem/ihi-status-1
|
||||
Alias: $ihi-record-status-1 = https://healthterminologies.gov.au/fhir/CodeSystem/ihi-record-status-1
|
||||
Alias: $v2-0203 = http://terminology.hl7.org/CodeSystem/v2-0203
|
2
static/uploads/fsh_output/input/fsh/index.txt
Normal file
2
static/uploads/fsh_output/input/fsh/index.txt
Normal file
@ -0,0 +1,2 @@
|
||||
Name Type File
|
||||
banks-mia-leanne Instance instances/banks-mia-leanne.fsh
|
@ -0,0 +1,49 @@
|
||||
Instance: banks-mia-leanne
|
||||
InstanceOf: Patient
|
||||
Usage: #example
|
||||
* meta.profile = "http://hl7.org.au/fhir/core/StructureDefinition/au-core-patient"
|
||||
* extension[0].url = "http://hl7.org/fhir/StructureDefinition/individual-genderIdentity"
|
||||
* extension[=].extension.url = "value"
|
||||
* extension[=].extension.valueCodeableConcept = $sct#446141000124107 "Identifies as female gender"
|
||||
* extension[+].url = "http://hl7.org/fhir/StructureDefinition/individual-pronouns"
|
||||
* extension[=].extension.url = "value"
|
||||
* extension[=].extension.valueCodeableConcept = $loinc#LA29519-8 "she/her/her/hers/herself"
|
||||
* extension[+].url = "http://hl7.org/fhir/StructureDefinition/individual-recordedSexOrGender"
|
||||
* extension[=].extension[0].url = "value"
|
||||
* extension[=].extension[=].valueCodeableConcept = $sct#248152002
|
||||
* extension[=].extension[=].valueCodeableConcept.text = "Female"
|
||||
* extension[=].extension[+].url = "type"
|
||||
* extension[=].extension[=].valueCodeableConcept = $sct#1515311000168102 "Biological sex at birth"
|
||||
* identifier.extension[0].url = "http://hl7.org.au/fhir/StructureDefinition/ihi-status"
|
||||
* identifier.extension[=].valueCoding = $ihi-status-1#active
|
||||
* identifier.extension[+].url = "http://hl7.org.au/fhir/StructureDefinition/ihi-record-status"
|
||||
* identifier.extension[=].valueCoding = $ihi-record-status-1#verified "verified"
|
||||
* identifier.type = $v2-0203#NI
|
||||
* identifier.type.text = "IHI"
|
||||
* identifier.system = "http://ns.electronichealth.net.au/id/hi/ihi/1.0"
|
||||
* identifier.value = "8003608333647261"
|
||||
* name.use = #usual
|
||||
* name.family = "Banks"
|
||||
* name.given[0] = "Mia"
|
||||
* name.given[+] = "Leanne"
|
||||
* telecom[0].system = #phone
|
||||
* telecom[=].value = "0270102724"
|
||||
* telecom[=].use = #work
|
||||
* telecom[+].system = #phone
|
||||
* telecom[=].value = "0491574632"
|
||||
* telecom[=].use = #mobile
|
||||
* telecom[+].system = #phone
|
||||
* telecom[=].value = "0270107520"
|
||||
* telecom[=].use = #home
|
||||
* telecom[+].system = #email
|
||||
* telecom[=].value = "mia.banks@myownpersonaldomain.com"
|
||||
* telecom[+].system = #phone
|
||||
* telecom[=].value = "270107520"
|
||||
* telecom[=].use = #home
|
||||
* gender = #female
|
||||
* birthDate = "1983-08-25"
|
||||
* address.line = "50 Sebastien St"
|
||||
* address.city = "Minjary"
|
||||
* address.state = "NSW"
|
||||
* address.postalCode = "2720"
|
||||
* address.country = "AU"
|
6
static/uploads/fsh_output/sushi-config.yaml
Normal file
6
static/uploads/fsh_output/sushi-config.yaml
Normal file
@ -0,0 +1,6 @@
|
||||
canonical: http://example.org
|
||||
fhirVersion: 4.0.1
|
||||
FSHOnly: true
|
||||
applyExtensionMetadataToRoot: false
|
||||
id: example
|
||||
name: Example
|
55
static/uploads/output.fsh
Normal file
55
static/uploads/output.fsh
Normal file
@ -0,0 +1,55 @@
|
||||
Alias: $sct = http://snomed.info/sct
|
||||
Alias: $loinc = http://loinc.org
|
||||
Alias: $ihi-status-1 = https://healthterminologies.gov.au/fhir/CodeSystem/ihi-status-1
|
||||
Alias: $ihi-record-status-1 = https://healthterminologies.gov.au/fhir/CodeSystem/ihi-record-status-1
|
||||
Alias: $v2-0203 = http://terminology.hl7.org/CodeSystem/v2-0203
|
||||
|
||||
Instance: banks-mia-leanne
|
||||
InstanceOf: Patient
|
||||
Usage: #example
|
||||
* meta.profile = "http://hl7.org.au/fhir/core/StructureDefinition/au-core-patient"
|
||||
* extension[0].url = "http://hl7.org/fhir/StructureDefinition/individual-genderIdentity"
|
||||
* extension[=].extension.url = "value"
|
||||
* extension[=].extension.valueCodeableConcept = $sct#446141000124107 "Identifies as female gender"
|
||||
* extension[+].url = "http://hl7.org/fhir/StructureDefinition/individual-pronouns"
|
||||
* extension[=].extension.url = "value"
|
||||
* extension[=].extension.valueCodeableConcept = $loinc#LA29519-8 "she/her/her/hers/herself"
|
||||
* extension[+].url = "http://hl7.org/fhir/StructureDefinition/individual-recordedSexOrGender"
|
||||
* extension[=].extension[0].url = "value"
|
||||
* extension[=].extension[=].valueCodeableConcept = $sct#248152002
|
||||
* extension[=].extension[=].valueCodeableConcept.text = "Female"
|
||||
* extension[=].extension[+].url = "type"
|
||||
* extension[=].extension[=].valueCodeableConcept = $sct#1515311000168102 "Biological sex at birth"
|
||||
* identifier.extension[0].url = "http://hl7.org.au/fhir/StructureDefinition/ihi-status"
|
||||
* identifier.extension[=].valueCoding = $ihi-status-1#active
|
||||
* identifier.extension[+].url = "http://hl7.org.au/fhir/StructureDefinition/ihi-record-status"
|
||||
* identifier.extension[=].valueCoding = $ihi-record-status-1#verified "verified"
|
||||
* identifier.type = $v2-0203#NI
|
||||
* identifier.type.text = "IHI"
|
||||
* identifier.system = "http://ns.electronichealth.net.au/id/hi/ihi/1.0"
|
||||
* identifier.value = "8003608333647261"
|
||||
* name.use = #usual
|
||||
* name.family = "Banks"
|
||||
* name.given[0] = "Mia"
|
||||
* name.given[+] = "Leanne"
|
||||
* telecom[0].system = #phone
|
||||
* telecom[=].value = "0270102724"
|
||||
* telecom[=].use = #work
|
||||
* telecom[+].system = #phone
|
||||
* telecom[=].value = "0491574632"
|
||||
* telecom[=].use = #mobile
|
||||
* telecom[+].system = #phone
|
||||
* telecom[=].value = "0270107520"
|
||||
* telecom[=].use = #home
|
||||
* telecom[+].system = #email
|
||||
* telecom[=].value = "mia.banks@myownpersonaldomain.com"
|
||||
* telecom[+].system = #phone
|
||||
* telecom[=].value = "270107520"
|
||||
* telecom[=].use = #home
|
||||
* gender = #female
|
||||
* birthDate = "1983-08-25"
|
||||
* address.line = "50 Sebastien St"
|
||||
* address.city = "Minjary"
|
||||
* address.state = "NSW"
|
||||
* address.postalCode = "2720"
|
||||
* address.country = "AU"
|
36
supervisord.conf
Normal file
36
supervisord.conf
Normal file
@ -0,0 +1,36 @@
|
||||
[supervisord]
|
||||
nodaemon=true
|
||||
logfile=/app/logs/supervisord.log
|
||||
logfile_maxbytes=50MB
|
||||
logfile_backups=10
|
||||
pidfile=/app/logs/supervisord.pid
|
||||
|
||||
[program:flask]
|
||||
command=/app/venv/bin/python /app/app.py
|
||||
directory=/app
|
||||
environment=FLASK_APP="app.py",FLASK_ENV="development",NODE_PATH="/usr/lib/node_modules"
|
||||
autostart=true
|
||||
autorestart=true
|
||||
startsecs=10
|
||||
stopwaitsecs=10
|
||||
stdout_logfile=/app/logs/flask.log
|
||||
stdout_logfile_maxbytes=10MB
|
||||
stdout_logfile_backups=5
|
||||
stderr_logfile=/app/logs/flask_err.log
|
||||
stderr_logfile_maxbytes=10MB
|
||||
stderr_logfile_backups=5
|
||||
|
||||
[program:tomcat]
|
||||
command=/usr/local/tomcat/bin/catalina.sh run
|
||||
directory=/usr/local/tomcat
|
||||
environment=SPRING_CONFIG_LOCATION="file:/usr/local/tomcat/conf/application.yaml",NODE_PATH="/usr/lib/node_modules"
|
||||
autostart=true
|
||||
autorestart=true
|
||||
startsecs=30
|
||||
stopwaitsecs=30
|
||||
stdout_logfile=/app/logs/tomcat.log
|
||||
stdout_logfile_maxbytes=10MB
|
||||
stdout_logfile_backups=5
|
||||
stderr_logfile=/app/logs/tomcat_err.log
|
||||
stderr_logfile_maxbytes=10MB
|
||||
stderr_logfile_backups=5
|
@ -32,6 +32,15 @@
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {{ 'active' if request.endpoint == 'validate_sample' else '' }}" href="{{ url_for('validate_sample') }}"><i class="bi bi-check-circle me-1"></i> Validate FHIR Sample</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {{ 'active' if request.endpoint == 'fhir_ui' else '' }}" href="{{ url_for('fhir_ui') }}"><i class="bi bi-cloud-arrow-down me-1"></i> FHIR API Explorer</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {{ 'active' if request.endpoint == 'fhir_ui_operations' else '' }}" href="{{ url_for('fhir_ui_operations') }}"><i class="bi bi-gear me-1"></i> FHIR UI Operations</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {{ 'active' if request.endpoint == 'fsh_converter' else '' }}" href="{{ url_for('fsh_converter') }}"><i class="bi bi-file-code me-1"></i> FSH Converter</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
303
templates/fhir_ui.html
Normal file
303
templates/fhir_ui.html
Normal file
@ -0,0 +1,303 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<div class="px-4 py-5 my-5 text-center">
|
||||
<img class="d-block mx-auto mb-4" src="{{ url_for('static', filename='FHIRFLARE.png') }}" alt="FHIRFLARE IG Toolkit" width="192" height="192">
|
||||
<h1 class="display-5 fw-bold text-body-emphasis">FHIR API Explorer</h1>
|
||||
<div class="col-lg-6 mx-auto">
|
||||
<p class="lead mb-4">
|
||||
Interact with FHIR servers using GET, POST, PUT, or DELETE requests. Toggle between local HAPI or a custom server to explore resources or perform searches.
|
||||
</p>
|
||||
<div class="d-grid gap-2 d-sm-flex justify-content-sm-center">
|
||||
<a href="{{ url_for('index') }}" class="btn btn-primary btn-lg px-4 gap-3">Back to Home</a>
|
||||
<a href="{{ url_for('validate_sample') }}" class="btn btn-outline-secondary btn-lg px-4">Validate FHIR Sample</a>
|
||||
<a href="{{ url_for('fhir_ui_operations') }}" class="btn btn-outline-secondary btn-lg px-4">FHIR UI Operations</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container mt-4">
|
||||
<div class="card">
|
||||
<div class="card-header"><i class="bi bi-cloud-arrow-down me-2"></i>Send FHIR Request</div>
|
||||
<div class="card-body">
|
||||
<form id="fhirRequestForm" onsubmit="return false;">
|
||||
{{ form.hidden_tag() }}
|
||||
<div class="mb-3">
|
||||
<label class="form-label">FHIR Server</label>
|
||||
<div class="input-group">
|
||||
<button type="button" class="btn btn-outline-primary" id="toggleServer">
|
||||
<span id="toggleLabel">Use Local HAPI</span>
|
||||
</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">
|
||||
</div>
|
||||
<small id="fhirServerHelp" class="form-text text-muted">Toggle to use local HAPI (http://localhost:8080/fhir) or enter a custom FHIR server URL.</small>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="fhirPath" class="form-label">FHIR Path</label>
|
||||
<input type="text" class="form-control" id="fhirPath" name="fhir_path" placeholder="e.g., Patient/wang-li" required aria-describedby="fhirPathHelp">
|
||||
<small id="fhirPathHelp" class="form-text text-muted">Enter a resource path (e.g., Patient, Observation/example) or '_search' for search queries.</small>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Request Type</label>
|
||||
<div class="d-flex gap-2 flex-wrap">
|
||||
<input type="radio" class="btn-check" name="method" id="get" value="GET" checked>
|
||||
<label class="btn btn-outline-success" for="get"><span class="badge bg-success">GET</span></label>
|
||||
|
||||
<input type="radio" class="btn-check" name="method" id="post" value="POST">
|
||||
<label class="btn btn-outline-primary" for="post"><span class="badge bg-primary">POST</span></label>
|
||||
|
||||
<input type="radio" class="btn-check" name="method" id="put" value="PUT">
|
||||
<label class="btn btn-outline-warning" for="put"><span class="badge bg-warning text-dark">PUT</span></label>
|
||||
|
||||
<input type="radio" class="btn-check" name="method" id="delete" value="DELETE">
|
||||
<label class="btn btn-outline-danger" for="delete"><span class="badge bg-danger">DELETE</span></label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3" id="requestBodyGroup" style="display: none;">
|
||||
<div class="d-flex align-items-center mb-2">
|
||||
<label for="requestBody" class="form-label me-2 mb-0">Request Body</label>
|
||||
<button type="button" class="btn btn-outline-secondary btn-sm" id="copyRequestBody" title="Copy to Clipboard"><i class="bi bi-clipboard"></i></button>
|
||||
</div>
|
||||
<textarea class="form-control" id="requestBody" name="request_body" rows="6" placeholder="For POST: JSON resource (e.g., {'resourceType': 'Patient', ...}) or search params (e.g., name=John&birthdate=gt2000)"></textarea>
|
||||
<div id="jsonError" class="text-danger mt-1" style="display: none;"></div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-primary" id="sendRequest">Send Request</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mt-4" id="responseCard" style="display: none;">
|
||||
<div class="card-header">Response</div>
|
||||
<div class="card-body">
|
||||
<h5>Status: <span id="responseStatus" class="badge"></span></h5>
|
||||
<div class="d-flex align-items-center mb-2">
|
||||
<h6 class="me-2 mb-0">Headers</h6>
|
||||
<button type="button" class="btn btn-outline-secondary btn-sm" id="copyResponseHeaders" title="Copy to Clipboard"><i class="bi bi-clipboard"></i></button>
|
||||
</div>
|
||||
<pre id="responseHeaders" class="border p-2 bg-light mb-3" style="max-height: 200px; overflow-y: auto;"></pre>
|
||||
<div class="d-flex align-items-center mb-2">
|
||||
<h6 class="me-2 mb-0">Body</h6>
|
||||
<button type="button" class="btn btn-outline-secondary btn-sm" id="copyResponseBody" title="Copy to Clipboard"><i class="bi bi-clipboard"></i></button>
|
||||
</div>
|
||||
<pre id="responseBody" class="border p-2 bg-light" style="max-height: 400px; overflow-y: auto;"></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const form = document.getElementById('fhirRequestForm');
|
||||
const sendButton = document.getElementById('sendRequest');
|
||||
const fhirPath = document.getElementById('fhirPath');
|
||||
const requestBody = document.getElementById('requestBody');
|
||||
const requestBodyGroup = document.getElementById('requestBodyGroup');
|
||||
const jsonError = document.getElementById('jsonError');
|
||||
const responseCard = document.getElementById('responseCard');
|
||||
const responseStatus = document.getElementById('responseStatus');
|
||||
const responseHeaders = document.getElementById('responseHeaders');
|
||||
const responseBody = document.getElementById('responseBody');
|
||||
const methodRadios = document.getElementsByName('method');
|
||||
const toggleServerButton = document.getElementById('toggleServer');
|
||||
const toggleLabel = document.getElementById('toggleLabel');
|
||||
const fhirServerUrl = document.getElementById('fhirServerUrl');
|
||||
const copyRequestBodyButton = document.getElementById('copyRequestBody');
|
||||
const copyResponseHeadersButton = document.getElementById('copyResponseHeaders');
|
||||
const copyResponseBodyButton = document.getElementById('copyResponseBody');
|
||||
let useLocalHapi = true;
|
||||
|
||||
// Show/hide request body
|
||||
function updateRequestBodyVisibility() {
|
||||
const selectedMethod = document.querySelector('input[name="method"]:checked').value;
|
||||
requestBodyGroup.style.display = (selectedMethod === 'POST' || selectedMethod === 'PUT') ? 'block' : 'none';
|
||||
if (selectedMethod !== 'POST' && selectedMethod !== 'PUT') {
|
||||
requestBody.value = '';
|
||||
jsonError.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
// Validate request body (JSON or form-encoded)
|
||||
function validateRequestBody(method, path) {
|
||||
if (!requestBody.value.trim()) {
|
||||
requestBody.classList.remove('is-invalid');
|
||||
jsonError.style.display = 'none';
|
||||
return method === 'GET' ? null : '';
|
||||
}
|
||||
const isSearch = path.endsWith('_search');
|
||||
if (method === 'POST' && isSearch) {
|
||||
return requestBody.value; // Allow form-encoded for _search
|
||||
}
|
||||
try {
|
||||
JSON.parse(requestBody.value);
|
||||
requestBody.classList.remove('is-invalid');
|
||||
jsonError.style.display = 'none';
|
||||
return requestBody.value;
|
||||
} catch (e) {
|
||||
requestBody.classList.add('is-invalid');
|
||||
jsonError.textContent = `Invalid JSON: ${e.message}`;
|
||||
jsonError.style.display = 'block';
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Clean FHIR path
|
||||
function cleanFhirPath(path) {
|
||||
const cleaned = path.replace(/^(r4\/|fhir\/)*/i, '').replace(/^\/+|\/+$/g, '');
|
||||
console.log(`Cleaned path: ${path} -> ${cleaned}`);
|
||||
return cleaned;
|
||||
}
|
||||
|
||||
// Toggle server
|
||||
function toggleServer() {
|
||||
useLocalHapi = !useLocalHapi;
|
||||
toggleLabel.textContent = useLocalHapi ? 'Use Local HAPI' : 'Use Custom URL';
|
||||
fhirServerUrl.style.display = useLocalHapi ? 'none' : 'block';
|
||||
fhirServerUrl.value = '';
|
||||
console.log(`Server toggled: ${useLocalHapi ? 'Local HAPI' : 'Custom URL'}`);
|
||||
}
|
||||
|
||||
// Copy to clipboard
|
||||
async function copyToClipboard(text, button) {
|
||||
try {
|
||||
await navigator.clipboard.writeText(text);
|
||||
button.innerHTML = '<i class="bi bi-check"></i>';
|
||||
setTimeout(() => {
|
||||
button.innerHTML = '<i class="bi bi-clipboard"></i>';
|
||||
}, 2000);
|
||||
} catch (err) {
|
||||
console.error('Copy failed:', err);
|
||||
alert('Failed to copy to clipboard');
|
||||
}
|
||||
}
|
||||
|
||||
// Bind toggle event
|
||||
toggleServerButton.addEventListener('click', toggleServer);
|
||||
|
||||
// Bind copy events
|
||||
copyRequestBodyButton.addEventListener('click', () => {
|
||||
copyToClipboard(requestBody.value, copyRequestBodyButton);
|
||||
});
|
||||
|
||||
copyResponseHeadersButton.addEventListener('click', () => {
|
||||
copyToClipboard(responseHeaders.textContent, copyResponseHeadersButton);
|
||||
});
|
||||
|
||||
copyResponseBodyButton.addEventListener('click', () => {
|
||||
copyToClipboard(responseBody.textContent, copyResponseBodyButton);
|
||||
});
|
||||
|
||||
// Update visibility on method change
|
||||
methodRadios.forEach(radio => {
|
||||
radio.addEventListener('change', updateRequestBodyVisibility);
|
||||
});
|
||||
|
||||
// Validate body on input
|
||||
requestBody.addEventListener('input', () => validateRequestBody(
|
||||
document.querySelector('input[name="method"]:checked').value,
|
||||
fhirPath.value
|
||||
));
|
||||
|
||||
// Send request
|
||||
sendButton.addEventListener('click', async function() {
|
||||
if (!fhirPath.value.trim()) {
|
||||
fhirPath.classList.add('is-invalid');
|
||||
console.log('Path validation failed: empty');
|
||||
return;
|
||||
}
|
||||
fhirPath.classList.remove('is-invalid');
|
||||
|
||||
const method = document.querySelector('input[name="method"]:checked').value;
|
||||
console.log(`Sending request: Method=${method}, Input Path=${fhirPath.value}, Server=${useLocalHapi ? 'Local HAPI' : fhirServerUrl.value}`);
|
||||
|
||||
const data = validateRequestBody(method, fhirPath.value);
|
||||
if ((method === 'POST' || method === 'PUT') && data === null) {
|
||||
console.log('Body validation failed');
|
||||
sendButton.disabled = false;
|
||||
sendButton.textContent = 'Send Request';
|
||||
return;
|
||||
}
|
||||
|
||||
sendButton.disabled = true;
|
||||
sendButton.textContent = 'Sending...';
|
||||
responseCard.style.display = 'none';
|
||||
|
||||
const cleanPath = cleanFhirPath(fhirPath.value);
|
||||
let fetchUrl, headers = {
|
||||
'Accept': 'application/fhir+json'
|
||||
};
|
||||
|
||||
if (useLocalHapi) {
|
||||
fetchUrl = `/fhir/${cleanPath}`;
|
||||
if (method === 'POST' || method === 'PUT') {
|
||||
headers['Content-Type'] = cleanPath.endsWith('_search') ? 'application/x-www-form-urlencoded' : 'application/fhir+json';
|
||||
headers['X-CSRFToken'] = '{{ form.csrf_token._value() }}';
|
||||
}
|
||||
} else {
|
||||
if (!fhirServerUrl.value.trim()) {
|
||||
fhirServerUrl.classList.add('is-invalid');
|
||||
console.log('Server URL validation failed: empty');
|
||||
sendButton.disabled = false;
|
||||
sendButton.textContent = 'Send Request';
|
||||
return;
|
||||
}
|
||||
fhirServerUrl.classList.remove('is-invalid');
|
||||
fetchUrl = `${fhirServerUrl.value.replace(/\/+$/, '')}/${cleanPath}`;
|
||||
if (method === 'POST' || method === 'PUT') {
|
||||
headers['Content-Type'] = cleanPath.endsWith('_search') ? 'application/x-www-form-urlencoded' : 'application/fhir+json';
|
||||
}
|
||||
}
|
||||
|
||||
console.log('Fetch details:', {
|
||||
url: fetchUrl,
|
||||
method,
|
||||
headers,
|
||||
body: data ? data.substring(0, 100) + '...' : null
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await fetch(fetchUrl, {
|
||||
method: method,
|
||||
headers: headers,
|
||||
body: method === 'GET' ? undefined : data
|
||||
});
|
||||
console.log('Response status:', response.status, response.statusText);
|
||||
|
||||
const responseHeadersObj = {};
|
||||
response.headers.forEach((value, key) => responseHeadersObj[key] = value);
|
||||
const contentType = responseHeadersObj['content-type'] || '';
|
||||
const text = await response.text();
|
||||
console.log('Response headers:', responseHeadersObj);
|
||||
console.log('Response body (first 200 chars):', text.substring(0, 200));
|
||||
|
||||
responseCard.style.display = 'block';
|
||||
responseStatus.textContent = `${response.status} ${response.statusText}`;
|
||||
responseStatus.className = `badge ${response.status < 300 ? 'bg-success' : response.status < 400 ? 'bg-info' : 'bg-danger'}`;
|
||||
responseHeaders.textContent = JSON.stringify(responseHeadersObj, null, 2);
|
||||
|
||||
if (contentType.includes('application/fhir+json') || contentType.includes('application/json')) {
|
||||
try {
|
||||
responseBody.textContent = JSON.stringify(JSON.parse(text), null, 2);
|
||||
} catch (e) {
|
||||
console.warn('Failed to parse JSON:', e.message);
|
||||
responseBody.textContent = text;
|
||||
}
|
||||
} else {
|
||||
responseBody.textContent = text;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Fetch error:', error.name, error.message, error.stack);
|
||||
responseCard.style.display = 'block';
|
||||
responseStatus.textContent = 'Error';
|
||||
responseStatus.className = 'badge bg-danger';
|
||||
responseHeaders.textContent = '';
|
||||
responseBody.textContent = `Error: ${error.message}`;
|
||||
} finally {
|
||||
sendButton.disabled = false;
|
||||
sendButton.textContent = 'Send Request';
|
||||
}
|
||||
});
|
||||
|
||||
// Initialize visibility
|
||||
updateRequestBodyVisibility();
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
908
templates/fhir_ui_operations.html
Normal file
908
templates/fhir_ui_operations.html
Normal file
@ -0,0 +1,908 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<style>
|
||||
/* General Button Styles */
|
||||
.btn-outline-dark-blue { border-color: #0a3d62; color: #0a3d62; }
|
||||
.btn-outline-dark-blue:hover { background-color: #0a3d62; color: white; }
|
||||
.btn-dark-blue { background-color: #0a3d62; border-color: #0a3d62; color: white; }
|
||||
.btn-dark-blue.active { background-color: #083152; border-color: #083152; } /* Keep active style */
|
||||
.btn-outline-success { border-color: #28a745; color: #28a745; }
|
||||
.btn-outline-success:hover { background-color: #28a745; color: white; }
|
||||
.btn-success { background-color: #28a745; border-color: #28a745; color: white; }
|
||||
.btn-success.active { background-color: #1e7e34; border-color: #1e7e34; } /* Keep active style */
|
||||
|
||||
/* --- Rest of the styles remain unchanged --- */
|
||||
/* Operation Block */
|
||||
.opblock { margin-bottom: 10px; border: 1px solid #dee2e6; border-radius: 4px; background: #fff; }
|
||||
.opblock-summary { display: flex; align-items: center; padding: 10px; cursor: pointer; background: #f8f9fa; border-bottom: 1px solid #dee2e6; }
|
||||
.opblock-summary:hover { background: #e9ecef; }
|
||||
.opblock-summary-method { width: 80px; text-align: center; color: white; padding: 5px; border-radius: 12px; font-size: 0.9em; margin-right: 10px; }
|
||||
.opblock-summary-method.get { background: #61affe; }
|
||||
.opblock-summary-method.post { background: #49cc90; }
|
||||
.opblock-summary-method.put { background: #fca130; }
|
||||
.opblock-summary-method.delete { background: #f93e3e; }
|
||||
.opblock-summary-method.patch { background: #50e3c2; }
|
||||
.opblock-summary-path { flex-grow: 1; font-family: monospace; font-size: 1em; margin-right: 10px; word-break: break-all; }
|
||||
.opblock-summary-description { flex-grow: 2; font-size: 0.9em; color: #333; margin-right: 10px; }
|
||||
.opblock-body { padding: 15px; display: none; border-top: 1px solid #dee2e6; }
|
||||
.opblock.is-open .opblock-body { display: block; }
|
||||
.opblock-section { margin-bottom: 20px; }
|
||||
.opblock-section:last-child { margin-bottom: 0; }
|
||||
.opblock-section-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; border-bottom: 1px solid #eee; padding-bottom: 5px; }
|
||||
.opblock-title { font-size: 1.1em; font-weight: bold; margin: 0; }
|
||||
.arrow { width: 20px; height: 20px; transition: transform 0.2s; margin-left: auto; }
|
||||
.opblock.is-open .arrow { transform: rotate(180deg); }
|
||||
|
||||
/* Try it out / Execute */
|
||||
.try-out__btn { background-color: #007bff; color: white; border: none; padding: 5px 10px; border-radius: 4px; cursor: pointer; }
|
||||
.try-out__btn.cancel { background-color: #dc3545; }
|
||||
.execute__btn { background-color: #28a745; color: white; border: none; padding: 8px 15px; border-radius: 4px; cursor: pointer; margin-top: 15px; display: none; font-weight: bold; }
|
||||
.execute__btn:disabled { background-color: #6c757d; cursor: not-allowed; }
|
||||
|
||||
/* Parameters */
|
||||
.parameters-container { margin-bottom: 15px; }
|
||||
.parameters-table { width: 100%; border-collapse: collapse; }
|
||||
.parameters-table th, .parameters-table td { border: 1px solid #dee2e6; padding: 8px; text-align: left; vertical-align: top; }
|
||||
.parameters-col_name { width: 20%; font-weight: bold; }
|
||||
.parameters-col_description { width: 80%; }
|
||||
.parameters-col_description input[type="text"],
|
||||
.parameters-col_description input[type="number"] {
|
||||
width: 100%; padding: 5px; border: 1px solid #ced4da; border-radius: 4px; box-sizing: border-box;
|
||||
}
|
||||
.parameters-col_description input.is-invalid { border-color: #dc3545; }
|
||||
.parameter__name.required span { color: red; margin-left: 2px; font-weight: bold; }
|
||||
.parameter__type { font-size: 0.9em; color: #6c757d; }
|
||||
.parameter__in { font-size: 0.8em; color: #6c757d; }
|
||||
.renderedMarkdown p { margin-bottom: 0.5rem; }
|
||||
.renderedMarkdown p:last-child { margin-bottom: 0; }
|
||||
|
||||
/* Request Body */
|
||||
.request-body-textarea { width: 100%; min-height: 100px; padding: 10px; border: 1px solid #ced4da; border-radius: 4px; font-family: monospace; box-sizing: border-box; margin-bottom: 10px; }
|
||||
.content-type-wrapper label.visually-hidden { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }
|
||||
.content-type-wrapper select { padding: 0.3rem 0.6rem; font-size: 0.9em; border-radius: 4px; border: 1px solid #ced4da; }
|
||||
|
||||
/* Responses */
|
||||
.responses-table { width: 100%; border-collapse: collapse; margin-top: 10px; }
|
||||
.responses-table th, .responses-table td { border: 1px solid #dee2e6; padding: 8px; text-align: left; }
|
||||
.response-col_status { width: 100px; font-weight: bold; }
|
||||
.response-control-media-type { margin: 10px 0; padding: 5px; background-color: #f8f9fa; border-radius: 4px; }
|
||||
.response-control-media-type__title { font-weight: bold; margin-right: 5px; }
|
||||
.response-control-media-type__accept-message { font-size: 0.85em; color: #6c757d; margin-left: 10px; }
|
||||
.model-example { margin-top: 10px; }
|
||||
.tab { display: flex; gap: 5px; margin-bottom: 10px; }
|
||||
.tablinks.badge { background: #dee2e6; color: #333; padding: 5px 10px; border-radius: 12px; cursor: pointer; font-size: 0.9em; border: none; }
|
||||
.tablinks.badge.active { background: #0a3d62; color: white; }
|
||||
.example-panel, .schema-panel { border: 1px solid #eee; border-radius: 4px; margin-top: -1px; }
|
||||
|
||||
/* Code Highlighting */
|
||||
.highlight-code { background: #333; color: #f8f8f2; padding: 10px; border-radius: 4px; overflow-x: auto; font-family: monospace; margin: 0; }
|
||||
.highlight-code pre { margin: 0; padding: 0; background: transparent; border: none; font-family: inherit; font-size: inherit; color: inherit; }
|
||||
|
||||
/* Execution Response */
|
||||
.execute-wrapper { margin-top: 20px; padding-top: 15px; border-top: 1px dashed #ccc; }
|
||||
.execute-wrapper .opblock-section-header { border-bottom: none; margin-bottom: 5px; padding-bottom: 0; }
|
||||
.execute-wrapper .response-format-select { padding: 0.3rem 0.6rem; font-size: 0.9em; border-radius: 4px; border: 1px solid #ced4da; }
|
||||
.execute-wrapper pre.response-output-content { background: #f8f9fa; color: #212529; border: 1px solid #dee2e6; padding: 10px; border-radius: 4px; max-height: 400px; overflow: auto; font-family: monospace; white-space: pre-wrap; word-break: break-all; margin-top: 5px; }
|
||||
.execute-wrapper div.response-output-narrative { display: none; border: 1px solid #dee2e6; padding: 15px; border-radius: 4px; margin-top: 5px; max-height: 400px; overflow: auto; background: #ffffff; color: #212529; line-height: 1.5; }
|
||||
.execute-wrapper .response-status { margin-top: 8px; font-weight: bold; }
|
||||
|
||||
/* Request URL, Curl, Controls */
|
||||
.request-url-section, .curl-section { margin-bottom: 15px; padding-bottom: 10px; border-bottom: 1px solid #eee; }
|
||||
.request-url-section:last-of-type, .curl-section:last-of-type { border-bottom: none; }
|
||||
.request-url-output, .curl-output { background: #e9ecef; color: #212529; padding: 10px 15px; border: 1px solid #dee2e6; border-radius: 4px; font-family: monospace; font-size: 0.9em; white-space: pre-wrap; word-break: break-all; margin-top: 5px; }
|
||||
.request-url-output code, .curl-output code { font-family: inherit; }
|
||||
.curl-section .opblock-section-header { display: flex; justify-content: space-between; align-items: center; border-bottom: none; padding-bottom: 0; margin-bottom: 5px; }
|
||||
.copy-curl-btn, .copy-response-btn, .download-response-btn { padding: 0.25rem 0.5rem; font-size: 0.8em; line-height: 1.5; border-radius: 0.2rem; }
|
||||
.copy-curl-btn i, .copy-response-btn i, .download-response-btn i { margin-right: 4px; vertical-align: text-bottom; }
|
||||
.execute-wrapper .response-controls { display: flex; align-items: center; gap: 5px; margin-left: auto; }
|
||||
.execute-wrapper .opblock-section-header h4 { margin-right: 10px; }
|
||||
.opblock-section h5.opblock-title { font-weight: bold; margin: 0; font-size: 1rem; }
|
||||
</style>
|
||||
|
||||
<div class="px-4 py-5 my-5 text-center">
|
||||
<img class="d-block mx-auto mb-4" src="{{ url_for('static', filename='FHIRFLARE.png') }}" alt="FHIRFLARE IG Toolkit" width="192" height="192">
|
||||
<h1 class="display-5 fw-bold text-body-emphasis">FHIR UI Operations</h1>
|
||||
<div class="col-lg-6 mx-auto">
|
||||
<p class="lead mb-4">
|
||||
Explore FHIR server operations by selecting resource types or system operations. Toggle between local HAPI or a custom server to interact with FHIR metadata, resources, and server-wide operations.
|
||||
</p>
|
||||
<div class="d-grid gap-2 d-sm-flex justify-content-sm-center">
|
||||
<a href="{{ url_for('index') }}" class="btn btn-primary btn-lg px-4 gap-3">Back to Home</a>
|
||||
<a href="{{ url_for('fhir_ui') }}" class="btn btn-outline-secondary btn-lg px-4">FHIR API Explorer</a>
|
||||
<a href="{{ url_for('validate_sample') }}" class="btn btn-outline-secondary btn-lg px-4">Validate FHIR Sample</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container mt-4">
|
||||
<div class="card">
|
||||
<div class="card-header"><i class="bi bi-gear me-2"></i>FHIR Operations Configuration</div>
|
||||
<div class="card-body">
|
||||
<form id="fhirOperationsForm">
|
||||
{{ form.hidden_tag() }}
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-bold">FHIR Server</label>
|
||||
<div class="input-group">
|
||||
<button type="button" class="btn btn-outline-primary" id="toggleServer">
|
||||
<span id="toggleLabel">Use Local HAPI</span> </button>
|
||||
<input type="text" class="form-control" id="fhirServerUrl" name="fhir_server_url" placeholder="Enter FHIR Base URL e.g., https://hapi.fhir.org/baseR4" style="display: none;" aria-describedby="fhirServerHelp">
|
||||
</div>
|
||||
<small id="fhirServerHelp" class="form-text text-muted">Toggle to use local HAPI (/fhir proxy) or enter a custom FHIR server URL.</small>
|
||||
</div>
|
||||
<button type="button" class="btn btn-primary mb-3" id="fetchMetadata">Fetch Metadata</button>
|
||||
</form>
|
||||
|
||||
<div class="banner3 mt-3" id="resourceTypes" style="display: none;">
|
||||
<h5>Resource Types and Operations</h5>
|
||||
<div class="d-flex flex-wrap gap-2" id="resourceButtons"></div>
|
||||
</div>
|
||||
|
||||
<div id="swagger-ui" class="mt-4" style="display: none;">
|
||||
<h5>Queries for <span id="selectedResource" class="fw-bold">Selected Resource</span></h5>
|
||||
<div id="queryList"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// --- Utility Functions (Unchanged) ---
|
||||
function escapeXml(unsafe) {
|
||||
if (typeof unsafe !== 'string') return String(unsafe);
|
||||
// Corrected escape map
|
||||
return unsafe.replace(/[<>&'"]/g, c => ({ '<': '<', '>': '>', '&': '&', "'": ''', '"': '"' })[c] || c);
|
||||
}
|
||||
|
||||
function jsonToFhirXml(json, indentLevel = 0) {
|
||||
const PADDING = ' ';
|
||||
const currentIndent = PADDING.repeat(indentLevel);
|
||||
const nextIndent = PADDING.repeat(indentLevel + 1);
|
||||
|
||||
try {
|
||||
const obj = typeof json === 'string' ? JSON.parse(json) : json;
|
||||
if (!obj || typeof obj !== 'object') return `${currentIndent}<error>Invalid input: Not an object</error>`;
|
||||
if (!Object.keys(obj).length) return `${currentIndent}<OperationOutcome xmlns="http://hl7.org/fhir">\n${nextIndent}<issue>\n${PADDING}${nextIndent}<severity value="information"/>\n${PADDING}${nextIndent}<code value="informational"/>\n${PADDING}${nextIndent}<diagnostics value="Successful operation with no content"/>\n${nextIndent}</issue>\n${currentIndent}</OperationOutcome>`;
|
||||
if (!obj.resourceType) {
|
||||
obj.resourceType = (obj.type && obj.entry) ? 'Bundle' : null;
|
||||
if (!obj.resourceType) return `${currentIndent}<error>Invalid FHIR resource: Missing resourceType</error>`;
|
||||
}
|
||||
|
||||
let xml = `${currentIndent}<${obj.resourceType} xmlns="http://hl7.org/fhir">\n`;
|
||||
function buildXmlRecursive(currentObj, level) {
|
||||
let innerXml = '';
|
||||
const itemIndent = PADDING.repeat(level);
|
||||
const childLevel = level + 1;
|
||||
for (const key in currentObj) {
|
||||
if (key === 'xmlns' || key === 'resourceType') continue;
|
||||
const value = currentObj[key];
|
||||
if (value === null || value === undefined) continue;
|
||||
if (Array.isArray(value)) {
|
||||
value.forEach(item => {
|
||||
if (typeof item === 'object' && item !== null) {
|
||||
innerXml += `${itemIndent}<${key}>\n${buildXmlRecursive(item, childLevel)}${itemIndent}</${key}>\n`;
|
||||
} else {
|
||||
innerXml += `${itemIndent}<${key} value="${escapeXml(item)}"/>\n`;
|
||||
}
|
||||
});
|
||||
} else if (typeof value === 'object') {
|
||||
innerXml += `${itemIndent}<${key}>\n${buildXmlRecursive(value, childLevel)}${itemIndent}</${key}>\n`;
|
||||
} else {
|
||||
if (key === 'div' && typeof value === 'string' && value.trim().startsWith('<div')) {
|
||||
innerXml += `${itemIndent}<${key} xmlns="http://www.w3.org/1999/xhtml">${value}</${key}>\n`;
|
||||
} else {
|
||||
innerXml += `${itemIndent}<${key} value="${escapeXml(value)}"/>\n`;
|
||||
}
|
||||
}
|
||||
}
|
||||
return innerXml;
|
||||
}
|
||||
xml += buildXmlRecursive(obj, indentLevel + 1);
|
||||
xml += `${currentIndent}</${obj.resourceType}>`;
|
||||
return xml;
|
||||
} catch (e) {
|
||||
console.error("JSON to XML conversion error:", e);
|
||||
return `${currentIndent}<error xmlns="http://local/error">\n${nextIndent}<message>Failed to convert to XML</message>\n${nextIndent}<detail>${escapeXml(e.message)}</detail>\n${currentIndent}</error>`;
|
||||
}
|
||||
}
|
||||
|
||||
function xmlToFhirJson(xmlString) {
|
||||
try {
|
||||
const parser = new DOMParser();
|
||||
const xmlDoc = parser.parseFromString(xmlString, "application/xml");
|
||||
const parseError = xmlDoc.querySelector("parsererror");
|
||||
if (parseError) {
|
||||
console.error("XML Parsing Error:", parseError.textContent);
|
||||
return JSON.stringify({ error: "Failed to parse XML", details: parseError.textContent }, null, 2);
|
||||
}
|
||||
const root = xmlDoc.documentElement;
|
||||
if (!root) return JSON.stringify({ error: "No root element found in XML" }, null, 2);
|
||||
|
||||
const jsonResult = { resourceType: root.tagName };
|
||||
function buildJsonFromNode(node) {
|
||||
const nodeObj = {};
|
||||
let textContent = "";
|
||||
for (const child of node.childNodes) {
|
||||
if (child.nodeType === Node.ELEMENT_NODE) {
|
||||
const tag = child.tagName;
|
||||
const childValue = buildJsonFromNode(child);
|
||||
if (nodeObj.hasOwnProperty(tag)) {
|
||||
if (!Array.isArray(nodeObj[tag])) nodeObj[tag] = [nodeObj[tag]];
|
||||
nodeObj[tag].push(childValue);
|
||||
} else {
|
||||
nodeObj[tag] = childValue;
|
||||
}
|
||||
} else if (child.nodeType === Node.TEXT_NODE) {
|
||||
textContent += child.nodeValue.trim();
|
||||
}
|
||||
}
|
||||
if (Object.keys(nodeObj).length) return nodeObj;
|
||||
if (node.hasAttribute('value')) return node.getAttribute('value');
|
||||
if (textContent) return textContent;
|
||||
if (node.nodeType === Node.ELEMENT_NODE && !node.childNodes.length && !node.attributes.length) return null;
|
||||
return {};
|
||||
}
|
||||
|
||||
const rootContent = buildJsonFromNode(root);
|
||||
if (typeof rootContent === 'object' && rootContent !== null) Object.assign(jsonResult, rootContent);
|
||||
else jsonResult._value = rootContent;
|
||||
|
||||
const idNode = root.querySelector(':scope > id[value]');
|
||||
if (idNode) jsonResult.id = idNode.getAttribute('value');
|
||||
const textNode = root.querySelector(':scope > text');
|
||||
if (textNode) {
|
||||
jsonResult.text = {};
|
||||
const statusNode = textNode.querySelector(':scope > status[value]');
|
||||
if (statusNode) jsonResult.text.status = statusNode.getAttribute('value');
|
||||
const divNode = textNode.querySelector(':scope > div[xmlns="http://www.w3.org/1999/xhtml"]');
|
||||
if (divNode) jsonResult.text.div = divNode.innerHTML;
|
||||
}
|
||||
return JSON.stringify(jsonResult, null, 2);
|
||||
} catch (e) {
|
||||
console.error("XML to JSON conversion error:", e);
|
||||
return JSON.stringify({ error: `Failed to convert XML to JSON: ${e.message}` }, null, 2);
|
||||
}
|
||||
}
|
||||
|
||||
async function copyToClipboard(text, buttonElement) {
|
||||
if (!navigator.clipboard) {
|
||||
alert('Clipboard API not available.');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await navigator.clipboard.writeText(text);
|
||||
const originalHtml = buttonElement.innerHTML;
|
||||
buttonElement.innerHTML = `<i class="bi bi-check-lg"></i> Copied!`;
|
||||
buttonElement.disabled = true;
|
||||
setTimeout(() => {
|
||||
buttonElement.innerHTML = originalHtml;
|
||||
buttonElement.disabled = false;
|
||||
}, 1500);
|
||||
} catch (err) {
|
||||
alert(`Failed to copy text: ${err}`);
|
||||
}
|
||||
}
|
||||
|
||||
function downloadFile(content, filename, mimeType = 'application/octet-stream') {
|
||||
try {
|
||||
const blob = new Blob([content], { type: mimeType });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const anchor = document.createElement('a');
|
||||
anchor.href = url;
|
||||
anchor.download = filename;
|
||||
document.body.appendChild(anchor);
|
||||
anchor.click();
|
||||
document.body.removeChild(anchor);
|
||||
URL.revokeObjectURL(url);
|
||||
} catch (error) {
|
||||
alert(`Failed to initiate download: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
function generateCurlCommand(method, url, headers, body) {
|
||||
let curl = `curl -X ${method.toUpperCase()} '${url}'`;
|
||||
for (const key in headers) {
|
||||
const lowerKey = key.toLowerCase();
|
||||
if (['user-agent', 'connection', 'host', 'origin', 'referer', 'x-csrftoken'].includes(lowerKey)) continue;
|
||||
const value = (headers[key] || '').replace(/'/g, "'\\''");
|
||||
curl += ` \\\n -H '${key}: ${value}'`;
|
||||
}
|
||||
if (body !== undefined && body !== null && body !== '') {
|
||||
const escapedBody = body.replace(/'/g, "'\\''");
|
||||
const contentType = headers['Content-Type'] || '';
|
||||
const dataFlag = contentType.includes('application/x-www-form-urlencoded') ? '-d' : '--data-binary';
|
||||
curl += ` \\\n ${dataFlag} '${escapedBody}'`;
|
||||
}
|
||||
return curl;
|
||||
}
|
||||
|
||||
// --- Main Application Logic ---
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// --- DOM Element References ---
|
||||
const fhirOperationsForm = document.getElementById('fhirOperationsForm');
|
||||
const fetchMetadataButton = document.getElementById('fetchMetadata');
|
||||
const toggleServerButton = document.getElementById('toggleServer');
|
||||
const toggleLabel = document.getElementById('toggleLabel');
|
||||
const fhirServerUrlInput = document.getElementById('fhirServerUrl');
|
||||
const resourceTypesDisplayDiv = document.getElementById('resourceTypes');
|
||||
const resourceButtonsContainer = document.getElementById('resourceButtons');
|
||||
const swaggerUiContainer = document.getElementById('swagger-ui');
|
||||
const selectedResourceSpan = document.getElementById('selectedResource');
|
||||
const queryListContainer = document.getElementById('queryList');
|
||||
|
||||
// --- State Variables ---
|
||||
let isUsingLocalHapi = true; // Default state
|
||||
let currentSelectedResource = '';
|
||||
let availableSystemOperations = [];
|
||||
let fetchedMetadataCache = null;
|
||||
|
||||
// --- Helper Function to Update Toggle Button/Input UI ---
|
||||
function updateServerToggleUI() {
|
||||
if (!toggleLabel || !fhirServerUrlInput) {
|
||||
console.error("Toggle UI elements not found!");
|
||||
return;
|
||||
}
|
||||
toggleLabel.textContent = isUsingLocalHapi ? 'Use Local HAPI' : 'Use Custom URL';
|
||||
fhirServerUrlInput.style.display = isUsingLocalHapi ? 'none' : 'block';
|
||||
// Ensure input is cleared *only* when switching TO local
|
||||
// No clearing needed here, handled in toggleServerSelection if needed
|
||||
fhirServerUrlInput.classList.remove('is-invalid');
|
||||
}
|
||||
|
||||
// --- Server Toggle Functionality ---
|
||||
function toggleServerSelection() {
|
||||
isUsingLocalHapi = !isUsingLocalHapi; // Flip the state first
|
||||
updateServerToggleUI(); // Update the UI based on the new state
|
||||
|
||||
// Clear custom URL input value if we just switched TO local HAPI
|
||||
if (isUsingLocalHapi) {
|
||||
fhirServerUrlInput.value = '';
|
||||
}
|
||||
|
||||
// Reset application state dependent on the server
|
||||
if (resourceTypesDisplayDiv) resourceTypesDisplayDiv.style.display = 'none';
|
||||
if (swaggerUiContainer) swaggerUiContainer.style.display = 'none';
|
||||
fetchedMetadataCache = null; // Invalidate metadata cache
|
||||
availableSystemOperations = []; // Clear parsed operations
|
||||
console.log(`Server toggled: ${isUsingLocalHapi ? 'Local HAPI' : 'Custom URL'}`);
|
||||
}
|
||||
|
||||
// Add event listener to the toggle button
|
||||
if (toggleServerButton) {
|
||||
toggleServerButton.addEventListener('click', toggleServerSelection);
|
||||
} else {
|
||||
console.error("Toggle server button (#toggleServer) not found!");
|
||||
}
|
||||
|
||||
// --- Generate Query Examples (Unchanged) ---
|
||||
function getQueryExamplesForResourceType(resourceType) {
|
||||
if (!fetchedMetadataCache?.rest?.[0]) return [];
|
||||
const restSection = fetchedMetadataCache.rest[0];
|
||||
const resourceMetadata = restSection.resource?.find(r => r.type === resourceType);
|
||||
const supportedInteractions = resourceMetadata?.interaction?.map(i => i.code) || [];
|
||||
const resourceSpecificOperations = resourceMetadata?.operation || [];
|
||||
const allSearchParameters = resourceMetadata?.searchParam || [];
|
||||
const queries = [];
|
||||
const formatParam = { name: '_format', in: 'query', type: 'string', required: false, description: 'Response format (e.g., json, xml)', example: 'json' };
|
||||
const idPathParam = { name: 'id', in: 'path', type: 'string', required: true, description: 'Resource logical ID', example: 'example' };
|
||||
const versionIdPathParam = { name: 'version_id', in: 'path', type: 'string', required: true, description: 'Version ID', example: '1' };
|
||||
const countQueryParam = { name: '_count', in: 'query', type: 'integer', required: false, description: 'Results per page', example: '10' };
|
||||
const idQueryParam = { name: '_id', in: 'query', type: 'string', required: false, description: 'Search by ID', example: 'example' };
|
||||
const lastUpdatedQueryParam = { name: '_lastUpdated', in: 'query', type: 'date', required: false, description: 'Last updated date', example: 'ge2024-01-01' };
|
||||
const commonSearchParams = [idQueryParam, lastUpdatedQueryParam];
|
||||
const specificSearchParams = allSearchParameters.filter(sp => !['_id', '_lastUpdated'].includes(sp.name)).slice(0, 3).map(sp => ({ name: sp.name, in: 'query', type: sp.type, required: false, description: sp.documentation || `Search by ${sp.name} (${sp.type})`, example: `${sp.type}-example` }));
|
||||
const searchGetParams = [countQueryParam, formatParam, ...commonSearchParams, ...specificSearchParams];
|
||||
const searchPostParams = [countQueryParam, formatParam];
|
||||
const baseExample = { resourceType, id: "example", text: { status: "generated", div: `<div xmlns="http://www.w3.org/1999/xhtml">Example ${resourceType}</div>` } };
|
||||
const baseExampleString = JSON.stringify(baseExample, null, 2);
|
||||
const createExampleString = JSON.stringify({ ...baseExample, id: undefined }, null, 2);
|
||||
const baseSchema = { resourceType, id: "string", meta: { versionId: "string", lastUpdated: "instant" }, text: { status: "code", div: "xhtml" } };
|
||||
const bundleSchema = { resourceType: "Bundle", type: "code", total: "unsignedInt", link: [{ relation: "string", url: "uri" }], entry: [{ resource: baseSchema }] };
|
||||
const paramsSchema = { resourceType: "Parameters", parameter: [{ name: "string", valueString: "string" }] };
|
||||
const outcomeSchema = { resourceType: "OperationOutcome", issue: [{ severity: "code", code: "code", diagnostics: "string" }] };
|
||||
const bundleSearchExample = JSON.stringify({ resourceType: "Bundle", type: "searchset", total: 1, entry: [{ resource: baseExample }] }, null, 2);
|
||||
const bundleHistoryExample = JSON.stringify({ resourceType: "Bundle", type: "history", total: 1, entry: [{ resource: baseExample }] }, null, 2);
|
||||
const patchExample = JSON.stringify({ resourceType: "Parameters", parameter: [{ name: "operation", part: [{ name: 'type', valueCode: 'replace' }, { name: 'path', valueString: `${resourceType}.active` }, { name: 'value', valueBoolean: false }] }] }, null, 2);
|
||||
const deleteExample = JSON.stringify({ resourceType: "OperationOutcome", issue: [{ severity: "information", code: "informational", diagnostics: "Successful deletion" }] }, null, 2);
|
||||
const postSearchExample = searchGetParams.filter(p => p.in === 'query' && p.example).map(p => `${p.name}=${encodeURIComponent(p.example)}`).join('&');
|
||||
const interactionMap = {
|
||||
'read': { label: `Read ${resourceType}`, method: 'GET', path: `${resourceType}/:id`, description: `Read a ${resourceType} by ID`, parameters: [idPathParam, formatParam], schema: baseSchema, example: baseExampleString },
|
||||
'vread': { label: `VRead ${resourceType}`, method: 'GET', path: `${resourceType}/:id/_history/:version_id`, description: `Read specific version`, parameters: [idPathParam, versionIdPathParam, formatParam], schema: baseSchema, example: JSON.stringify({ ...baseExample, meta: { versionId: "1" } }, null, 2) },
|
||||
'update': { label: `Update ${resourceType}`, method: 'PUT', path: `${resourceType}/:id`, description: `Update ${resourceType} with ID`, parameters: [idPathParam, formatParam], requestBody: true, schema: baseSchema, example: baseExampleString },
|
||||
'patch': { label: `Patch ${resourceType}`, method: 'PATCH', path: `${resourceType}/:id`, description: `Patch ${resourceType} by ID`, parameters: [idPathParam, formatParam], requestBody: true, schema: paramsSchema, example: patchExample },
|
||||
'delete': { label: `Delete ${resourceType}`, method: 'DELETE', path: `${resourceType}/:id`, description: `Delete ${resourceType}`, parameters: [idPathParam, formatParam], schema: outcomeSchema, example: deleteExample },
|
||||
'create': { label: `Create ${resourceType}`, method: 'POST', path: `${resourceType}`, description: `Create new ${resourceType}`, parameters: [formatParam], requestBody: true, schema: baseSchema, example: createExampleString },
|
||||
'search-type': { label: `Search ${resourceType} (GET)`, method: 'GET', path: `${resourceType}`, description: `Search ${resourceType} using GET`, parameters: searchGetParams, schema: bundleSchema, example: bundleSearchExample },
|
||||
'history-type': { label: `Type History ${resourceType}`, method: 'GET', path: `${resourceType}/_history`, description: `History for all ${resourceType}`, parameters: [countQueryParam, formatParam, lastUpdatedQueryParam], schema: bundleSchema, example: bundleHistoryExample },
|
||||
'history-instance': { label: `Instance History ${resourceType}`, method: 'GET', path: `${resourceType}/:id/_history`, description: `History for specific ${resourceType}`, parameters: [idPathParam, countQueryParam, formatParam, lastUpdatedQueryParam], schema: bundleSchema, example: bundleHistoryExample },
|
||||
};
|
||||
supportedInteractions.forEach(code => { if (interactionMap[code]) queries.push(interactionMap[code]); });
|
||||
if (supportedInteractions.includes('search-type')) { queries.push({ label: `Search ${resourceType} (POST)`, method: 'POST', path: `${resourceType}/_search`, description: `Search ${resourceType} using POST`, parameters: searchPostParams, requestBody: true, schema: bundleSchema, example: postSearchExample }); }
|
||||
resourceSpecificOperations.forEach(opDef => { const opName = opDef.name.startsWith('$') ? opDef.name : `$${opDef.name}`; const opDoc = opDef.documentation || `Execute ${opName} on ${resourceType}`; const opExample = JSON.stringify({ resourceType: "Parameters", parameter: [{ name: "example-param", valueString: "example-value" }] }, null, 2); const opMethod = opDef.definition?.toLowerCase().includes('get') ? 'GET' : 'POST'; const isInstanceLevel = opDef.definition?.toLowerCase().includes('/{id}/$'); queries.push({ label: `${opName} (${isInstanceLevel ? 'Instance' : 'Type'})`, method: opMethod, path: isInstanceLevel ? `${resourceType}/:id/${opName}` : `${resourceType}/${opName}`, description: opDoc, parameters: isInstanceLevel ? [idPathParam, formatParam] : [formatParam], requestBody: opMethod !== 'GET', schema: paramsSchema, example: opExample }); });
|
||||
availableSystemOperations.forEach(op => { const opName = op.name.startsWith('$') ? op.name : `$${op.name}`; const opParams = [formatParam]; const opExample = JSON.stringify({ resourceType: "Parameters", parameter: [{ name: "info", valueString: `Input for ${opName}` }] }, null, 2); if (op.levels.includes('type')) { op.methods.forEach(method => { queries.push({ label: `${opName} (Type)`, method, path: `${resourceType}/${opName}`, description: op.documentation || `Type-level ${opName} on ${resourceType}`, parameters: opParams, requestBody: method !== 'GET', schema: paramsSchema, example: opExample }); }); } if (op.levels.includes('instance')) { op.methods.forEach(method => { queries.push({ label: `${opName} (Instance)`, method, path: `${resourceType}/:id/${opName}`, description: op.documentation || `Instance-level ${opName} on ${resourceType}`, parameters: [idPathParam, ...opParams], requestBody: method !== 'GET', schema: paramsSchema, example: opExample }); }); } });
|
||||
return queries.filter((q, i, arr) => i === arr.findIndex(x => x.path === q.path && x.method === q.method));
|
||||
}
|
||||
|
||||
// --- Generate System-Level Queries (Revised based on metadata example) ---
|
||||
function getSystemLevelQueries() {
|
||||
if (!fetchedMetadataCache?.rest?.[0]) return [];
|
||||
|
||||
const restSection = fetchedMetadataCache.rest[0];
|
||||
const supportedInteractions = restSection.interaction?.map(i => i.code) || [];
|
||||
const queries = [];
|
||||
const addedPaths = new Set(); // Track METHOD + PATH to avoid duplicates
|
||||
|
||||
const formatParam = { name: '_format', in: 'query', type: 'string', required: false, description: 'Response format', example: 'json' };
|
||||
const countParam = { name: '_count', in: 'query', type: 'integer', required: false, description: 'Results per page', example: '10' };
|
||||
const sinceParam = { name: '_since', in: 'query', type: 'instant', required: false, description: 'Changes after this time', example: new Date().toISOString() };
|
||||
const contentParam = { name: '_content', in: 'query', type: 'string', required: false, description: 'Search content', example: 'example' };
|
||||
const paramsSchema = { resourceType: "Parameters" };
|
||||
const paramsExample = JSON.stringify({ resourceType: "Parameters", parameter: [] }, null, 2);
|
||||
|
||||
// --- Add Metadata (Capabilities) Operation ---
|
||||
// Always include GET /metadata as it's a standard FHIR endpoint
|
||||
const metadataPath = 'metadata';
|
||||
const metadataKey = `GET/${metadataPath}`;
|
||||
if (!addedPaths.has(metadataKey)) {
|
||||
queries.push({
|
||||
name: 'metadata',
|
||||
label: 'GET /metadata',
|
||||
method: 'GET',
|
||||
path: metadataPath,
|
||||
description: 'server-capabilities: Fetch the server FHIR CapabilityStatement',
|
||||
parameters: [formatParam],
|
||||
schema: { resourceType: "CapabilityStatement" },
|
||||
example: JSON.stringify(fetchedMetadataCache || { resourceType: "CapabilityStatement" }, null, 2)
|
||||
});
|
||||
addedPaths.add(metadataKey);
|
||||
}
|
||||
|
||||
// --- Add Other Standard System Interactions ---
|
||||
// Transaction/Batch
|
||||
if (supportedInteractions.includes('transaction') || supportedInteractions.includes('batch')) {
|
||||
const path = '';
|
||||
const key = `POST/${path}`;
|
||||
if (!addedPaths.has(key)) {
|
||||
queries.push({
|
||||
name: 'server-transaction',
|
||||
label: 'POST /',
|
||||
method: 'POST',
|
||||
path: path,
|
||||
description: 'server-transaction: Execute a FHIR Transaction (or FHIR Batch) Bundle',
|
||||
parameters: [formatParam],
|
||||
requestBody: true,
|
||||
schema: { resourceType: "Bundle" },
|
||||
example: JSON.stringify({ resourceType: "Bundle", type: "transaction", entry: [] }, null, 2)
|
||||
});
|
||||
addedPaths.add(key);
|
||||
}
|
||||
}
|
||||
|
||||
// System History
|
||||
if (supportedInteractions.includes('history-system')) {
|
||||
const path = '_history';
|
||||
const key = `GET/${path}`;
|
||||
if (!addedPaths.has(key)) {
|
||||
queries.push({
|
||||
name: 'server-history',
|
||||
label: 'GET /_history',
|
||||
method: 'GET',
|
||||
path: path,
|
||||
description: 'server-history: Fetch the resource change history across all resource types on the server',
|
||||
parameters: [formatParam, countParam, sinceParam],
|
||||
schema: { resourceType: "Bundle" },
|
||||
example: JSON.stringify({ resourceType: "Bundle", type: "history" }, null, 2)
|
||||
});
|
||||
addedPaths.add(key);
|
||||
}
|
||||
}
|
||||
|
||||
// System Search (if supported, less common)
|
||||
if (supportedInteractions.includes('search-system')) {
|
||||
const path = '';
|
||||
const key = `GET/${path}`;
|
||||
if (!addedPaths.has(key)) {
|
||||
queries.push({
|
||||
name: 'search-system',
|
||||
label: 'GET /',
|
||||
method: 'GET',
|
||||
path: path,
|
||||
description: 'Search across all resource types (limited support usually)',
|
||||
parameters: [formatParam, countParam, contentParam],
|
||||
schema: { resourceType: "Bundle" },
|
||||
example: JSON.stringify({ resourceType: "Bundle", type: "searchset" }, null, 2)
|
||||
});
|
||||
addedPaths.add(key);
|
||||
}
|
||||
}
|
||||
|
||||
// --- Add System-Level Named Operations ---
|
||||
(restSection.operation || []).forEach(op => {
|
||||
const opName = op.name;
|
||||
const opPath = opName.startsWith('$') ? opName : `$${opName}`;
|
||||
const defUrl = op.definition || '';
|
||||
const doc = op.documentation || `Execute ${opName} at system level`;
|
||||
|
||||
// Skip metadata if already added
|
||||
if (opName === 'metadata' || opName === 'capabilities') return;
|
||||
|
||||
// Determine methods based on operation name and definition
|
||||
let methods = [];
|
||||
if (['diff', 'meta', 'get-resource-counts'].includes(opName)) {
|
||||
methods = ['GET', 'POST'];
|
||||
} else if (['reindex', 'perform-reindexing-pass', 'mark-all-resources-for-reindexing', 'expunge', 'reindex-terminology', 'hapi.fhir.replace-references'].includes(opName)) {
|
||||
methods = ['POST'];
|
||||
} else {
|
||||
methods = ['POST']; // Default to POST for unknown operations
|
||||
if (defUrl.toLowerCase().includes('get')) methods.push('GET');
|
||||
}
|
||||
|
||||
methods.forEach(method => {
|
||||
const key = `${method}/${opPath}`;
|
||||
if (!addedPaths.has(key)) {
|
||||
let label = `${method} /${opPath}`;
|
||||
let description = doc;
|
||||
|
||||
// Customize labels and descriptions to match expected output
|
||||
if (opName === 'diff') {
|
||||
description = 'Compare two resources or two versions of a single resource';
|
||||
} else if (opName === 'get-resource-counts') {
|
||||
description = 'Provides the number of resources currently stored on the server, broken down by resource type';
|
||||
} else if (opName === 'hapi.fhir.replace-references') {
|
||||
description = 'Repoints referencing resources to another resource instance';
|
||||
} else {
|
||||
description = `${method}: /${opPath}`; // Fallback for operations like reindex, expunge, etc.
|
||||
}
|
||||
|
||||
queries.push({
|
||||
name: opName,
|
||||
label: label,
|
||||
method: method,
|
||||
path: opPath,
|
||||
description: description,
|
||||
parameters: [formatParam],
|
||||
requestBody: method !== 'GET',
|
||||
schema: paramsSchema,
|
||||
example: paramsExample
|
||||
});
|
||||
addedPaths.add(key);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return queries;
|
||||
}
|
||||
|
||||
|
||||
// --- Update Query List UI (FIXED Variable Scope) ---
|
||||
function updateQueryListUI(resourceType) {
|
||||
currentSelectedResource = resourceType;
|
||||
selectedResourceSpan.textContent = resourceType === 'System' ? 'System Operations' : resourceType;
|
||||
queryListContainer.innerHTML = '';
|
||||
|
||||
const queries = resourceType === 'System' ? getSystemLevelQueries() : getQueryExamplesForResourceType(resourceType);
|
||||
if (!queries || !queries.length) {
|
||||
queryListContainer.innerHTML = '<p>No operations defined or supported for this type.</p>';
|
||||
swaggerUiContainer.style.display = 'block';
|
||||
return;
|
||||
}
|
||||
|
||||
queries.sort((a, b) => { const pathA = a.path || ''; const pathB = b.path || ''; if (pathA < pathB) return -1; if (pathA > pathB) return 1; return a.method < b.method ? -1 : a.method > b.method ? 1 : 0; })
|
||||
.forEach((query, i) => {
|
||||
const blockId = `opblock-${i}`;
|
||||
const block = document.createElement('div');
|
||||
block.id = blockId;
|
||||
block.className = `opblock opblock-${query.method.toLowerCase()}`;
|
||||
block.dataset.queryIndex = i;
|
||||
block.dataset.queryData = JSON.stringify(query);
|
||||
|
||||
// Generate HTML (No changes needed here)
|
||||
block.innerHTML = `
|
||||
<div class="opblock-summary"><span class="opblock-summary-method ${query.method.toLowerCase()}">${query.method}</span><span class="opblock-summary-path">${query.path}</span><span class="opblock-summary-description">${query.description}</span><svg class="arrow" viewBox="0 0 20 20"><path fill="currentColor" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"/></svg></div>
|
||||
<div class="opblock-body">
|
||||
<div class="opblock-section parameters-section"><div class="opblock-section-header"><h4 class="opblock-title">Parameters</h4><div class="try-out"><button class="btn btn-sm try-out__btn">Try it out</button></div></div><div class="parameters-container"><table class="parameters-table"><thead><tr><th class="parameters-col_name">Name</th><th class="parameters-col_description">Description</th></tr></thead><tbody>${query.parameters && query.parameters.length ? query.parameters.map(p => `<tr data-param-name="${p.name}" data-param-in="${p.in}"><td class="parameters-col_name"><div class="parameter__name ${p.required ? 'required' : ''}">${p.name}${p.required ? '<span>*</span>' : ''}</div><div class="parameter__type">${p.type}</div><div class="parameter__in">(${p.in})</div></td><td class="parameters-col_description"><div class="renderedMarkdown"><p>${p.description || 'No description'}</p></div>${p.example ? `<div class="renderedMarkdown"><p><i>Example:</i> ${p.example}</p></div>` : ''}<input type="${p.type === 'integer' ? 'number' : 'text'}" placeholder="${p.example || p.name}" data-example="${p.example || ''}" value="" aria-label="${p.name} parameter value" disabled></td></tr>`).join('') : `<tr><td colspan="2"><div class="renderedMarkdown"><p>No parameters defined.</p></div></td></tr>`}</tbody></table></div></div>
|
||||
${query.requestBody ? `<div class="opblock-section request-body-section"><div class="opblock-section-header"><h4 class="opblock-title">Request Body</h4> <div class="content-type-wrapper"><label for="${blockId}-request-content-type" class="visually-hidden">Request Content Type</label><select id="${blockId}-request-content-type" class="content-type request-content-type" aria-label="Request body content type" disabled><option value="application/fhir+json" selected>application/fhir+json</option><option value="application/fhir+xml">application/fhir+xml</option>${query.method === 'POST' && query.path.endsWith('_search') ? '<option value="application/x-www-form-urlencoded">application/x-www-form-urlencoded</option>' : ''}</select></div></div><textarea class="request-body-textarea" placeholder="Enter request body..." aria-label="Request body input" disabled>${(query.method === 'POST' && query.path.endsWith('_search')) ? query.example : ''}</textarea><div class="model-example request-body-example" style="margin-top: 10px; ${(query.method === 'POST' && query.path.endsWith('_search')) ? 'display: none;' : ''}"><ul class="tab" role="tablist"><li class="tabitem active"><button class="tablinks badge active" data-name="example">Example Body</button></li></ul><div class="example-panel" style="display: block;"><div class="highlight-code"><pre class="request-example-code"><code class="language-json">${query.example && typeof query.example === 'string' && query.example.startsWith('{') ? query.example : '{}'}</code></pre></div></div></div></div> ` : ''}
|
||||
<div class="opblock-section execute-section"><button class="btn execute__btn" disabled>Execute</button></div>
|
||||
<div class="execute-wrapper" style="display: none;"><div class="opblock-section request-url-section"><h5 class="opblock-title">Request URL</h5><pre class="request-url-output"><code></code></pre></div><div class="opblock-section curl-section"><div class="opblock-section-header"><h5 class="opblock-title">Curl</h5><button type="button" class="btn btn-sm btn-secondary copy-curl-btn" title="Copy Curl Command"><i class="bi bi-clipboard"></i> Copy</button></div><pre class="curl-output"><code></code></pre></div><div class="opblock-section-header"><h4 class="opblock-title">Server Response</h4> <div class="response-controls"> <select class="response-format-select" aria-label="Response display format" style="display: none;"><option value="json">JSON</option><option value="xml">XML</option><option value="narrative">Narrative</option></select><button type="button" class="btn btn-sm btn-secondary copy-response-btn" title="Copy Response Body" style="display: none;"><i class="bi bi-clipboard"></i> Copy</button> <button type="button" class="btn btn-sm btn-secondary download-response-btn" title="Download Response Body" style="display: none;"><i class="bi bi-download"></i> Download</button></div></div><pre class="response-output-content"><code></code></pre><div class="response-output-narrative" style="display:none;"></div><div class="response-status" style="margin-top: 5px; font-weight: bold;"></div></div>
|
||||
<div class="opblock-section responses-section"><div class="opblock-section-header"><h4>Example Response / Schema</h4></div><table class="responses-table"><thead><tr class="responses-header"><td class="response-col_status">Code</td><td class="response-col_description">Description</td></tr></thead><tbody><tr class="response" data-code="200"><td class="response-col_status">200</td><td class="response-col_description"><div class="response-col_description__inner"><div class="renderedMarkdown"><p>Success</p></div></div><section class="response-controls"><div class="response-control-media-type"><label for="${blockId}-example-media-type" class="response-control-media-type__title">Example Format:</label><div class="content-type-wrapper d-inline-block"><select id="${blockId}-example-media-type" aria-label="Example Media Type" class="content-type example-media-type-select"><option value="application/fhir+json">JSON</option><option value="application/fhir+xml">XML</option></select></div><small class="response-control-media-type__accept-message">Controls example/schema format.</small></div></section><div class="model-example"><ul class="tab" role="tablist"><li class="tabitem active"><button class="tablinks badge active" data-name="example">Example Value</button></li><li class="tabitem"><button class="tablinks badge" data-name="schema">Schema</button></li></ul><div class="example-panel" style="display: block;"><div class="highlight-code"><pre class="example-code-display"><code class="language-json">${query.example && typeof query.example === 'string' ? query.example : '{}'}</code></pre></div></div><div class="schema-panel" style="display: none;"><div class="highlight-code"><pre class="schema-code-display"><code class="language-json">${JSON.stringify(query.schema || {}, null, 2)}</code></pre></div></div></div></td></tr><tr class="response" data-code="4xx/5xx"><td class="response-col_status">4xx/5xx</td><td class="response-col_description"><p>Error (e.g., Not Found, Server Error)</p></td></tr></tbody></table></div>
|
||||
</div>`;
|
||||
queryListContainer.appendChild(block);
|
||||
|
||||
// --- Get Element References ONCE per block ---
|
||||
const opblockSummary = block.querySelector('.opblock-summary');
|
||||
const opblockBody = block.querySelector('.opblock-body');
|
||||
const tryButton = block.querySelector('.try-out__btn');
|
||||
const executeButton = block.querySelector('.execute__btn');
|
||||
const paramInputs = block.querySelectorAll('.parameters-section input');
|
||||
const reqBodyTextarea = block.querySelector('.request-body-textarea');
|
||||
const reqContentTypeSelect = block.querySelector('.request-content-type');
|
||||
const executeWrapper = block.querySelector('.execute-wrapper');
|
||||
// Ensure executeWrapper exists before querying within it
|
||||
const respOutputPre = executeWrapper?.querySelector('.response-output-content');
|
||||
const respOutputCode = respOutputPre?.querySelector('code');
|
||||
const respNarrativeDiv = executeWrapper?.querySelector('.response-output-narrative');
|
||||
const respStatusDiv = executeWrapper?.querySelector('.response-status');
|
||||
const respFormatSelect = executeWrapper?.querySelector('.response-format-select');
|
||||
const reqUrlOutput = executeWrapper?.querySelector('.request-url-output code');
|
||||
const curlOutput = executeWrapper?.querySelector('.curl-output code');
|
||||
const copyCurlButton = executeWrapper?.querySelector('.copy-curl-btn');
|
||||
const copyRespButton = executeWrapper?.querySelector('.copy-response-btn');
|
||||
const downloadRespButton = executeWrapper?.querySelector('.download-response-btn');
|
||||
const exampleMediaTypeSelect = block.querySelector('.example-media-type-select');
|
||||
const exampleTabs = block.querySelectorAll('.responses-section .tablinks.badge');
|
||||
const examplePanel = block.querySelector('.responses-section .example-panel');
|
||||
const schemaPanel = block.querySelector('.responses-section .schema-panel');
|
||||
|
||||
// --- Attach Event Listeners (using references above, with null checks) ---
|
||||
if (opblockSummary) {
|
||||
opblockSummary.addEventListener('click', (e) => {
|
||||
if (e.target.closest('button, a, input, select')) return;
|
||||
const wasOpen = block.classList.contains('is-open');
|
||||
document.querySelectorAll('.opblock.is-open').forEach(openBlock => { if (openBlock !== block) { openBlock.classList.remove('is-open'); if(openBlock.querySelector('.opblock-body')) openBlock.querySelector('.opblock-body').style.display = 'none'; }});
|
||||
block.classList.toggle('is-open', !wasOpen);
|
||||
if(opblockBody) opblockBody.style.display = wasOpen ? 'none' : 'block';
|
||||
});
|
||||
}
|
||||
|
||||
if (tryButton && executeButton) {
|
||||
tryButton.addEventListener('click', () => {
|
||||
const isEditing = tryButton.classList.contains('cancel'); const enable = !isEditing;
|
||||
paramInputs.forEach(input => { input.disabled = !enable; if (!enable) { input.value = input.dataset.example || ''; input.classList.remove('is-invalid'); } });
|
||||
if (reqBodyTextarea) reqBodyTextarea.disabled = !enable; if (reqContentTypeSelect) reqContentTypeSelect.disabled = !enable; if (!enable && reqBodyTextarea) { reqBodyTextarea.value = (query.method === 'POST' && query.path.endsWith('_search')) ? query.example : ''; }
|
||||
tryButton.textContent = enable ? 'Cancel' : 'Try it out'; tryButton.classList.toggle('cancel', enable); tryButton.classList.toggle('btn-danger', enable); tryButton.classList.toggle('btn-primary', !enable);
|
||||
executeButton.style.display = enable ? 'inline-block' : 'none'; executeButton.disabled = !enable;
|
||||
if (!enable && executeWrapper) {
|
||||
executeWrapper.style.display = 'none';
|
||||
if (reqUrlOutput) reqUrlOutput.textContent = ''; if (curlOutput) curlOutput.textContent = '';
|
||||
if (respOutputCode) respOutputCode.textContent = ''; if (respNarrativeDiv) respNarrativeDiv.innerHTML = '';
|
||||
if (respStatusDiv) respStatusDiv.textContent = '';
|
||||
if (respFormatSelect) respFormatSelect.style.display = 'none';
|
||||
if (copyRespButton) copyRespButton.style.display = 'none'; if (downloadRespButton) downloadRespButton.style.display = 'none';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (reqContentTypeSelect && reqBodyTextarea && tryButton) {
|
||||
const reqExampleCode = block.querySelector('.request-example-code code');
|
||||
const reqExampleContainer = block.querySelector('.request-body-example');
|
||||
reqContentTypeSelect.addEventListener('change', () => {
|
||||
const type = reqContentTypeSelect.value; const isFormUrlEncoded = type === 'application/x-www-form-urlencoded'; const isEditing = tryButton.classList.contains('cancel');
|
||||
if (reqExampleContainer) reqExampleContainer.style.display = isFormUrlEncoded ? 'none' : 'block';
|
||||
if (reqBodyTextarea && !isEditing) { reqBodyTextarea.value = isFormUrlEncoded ? query.example : ''; reqBodyTextarea.placeholder = isFormUrlEncoded ? 'e.g., param1=value1¶m2=value2' : 'Enter FHIR resource JSON or XML'; }
|
||||
if (reqExampleCode && !isFormUrlEncoded) { const jsonExample = query.example && typeof query.example === 'string' && query.example.startsWith('{') ? query.example : '{}'; if (type === 'application/fhir+json') { reqExampleCode.className = 'language-json'; reqExampleCode.textContent = jsonExample; } else if (type === 'application/fhir+xml') { reqExampleCode.className = 'language-xml'; reqExampleCode.textContent = jsonToFhirXml(jsonExample); } }
|
||||
});
|
||||
reqContentTypeSelect.dispatchEvent(new Event('change'));
|
||||
}
|
||||
|
||||
if (executeButton && executeWrapper && respStatusDiv && reqUrlOutput && curlOutput && respFormatSelect && copyRespButton && downloadRespButton && respOutputCode && respNarrativeDiv && respOutputPre) {
|
||||
executeButton.addEventListener('click', async () => {
|
||||
executeButton.disabled = true; executeButton.textContent = 'Executing...';
|
||||
executeWrapper.style.display = 'block'; reqUrlOutput.textContent = 'Building request...'; curlOutput.textContent = 'Building request...'; respOutputCode.textContent = ''; respNarrativeDiv.innerHTML = ''; respNarrativeDiv.style.display = 'none'; respOutputPre.style.display = 'block'; respStatusDiv.textContent = 'Executing request...'; respStatusDiv.style.color = '#6c757d'; respFormatSelect.style.display = 'none'; copyRespButton.style.display = 'none'; downloadRespButton.style.display = 'none'; respFormatSelect.value = 'json';
|
||||
const queryDef = JSON.parse(block.dataset.queryData); const method = queryDef.method; const headers = { 'Accept': 'application/fhir+json, application/fhir+xml;q=0.9, */*;q=0.8' }; let body; let path = queryDef.path;
|
||||
// **FIX for Local URL:** Explicitly use '/fhir' if local, otherwise use input value
|
||||
const baseUrl = isUsingLocalHapi ? '/fhir' : (fhirServerUrlInput.value.trim().replace(/\/+$/, '') || '/fhir'); // Fallback to /fhir if custom URL is empty
|
||||
let url = `${baseUrl}`; let validParams = true; const missingParams = [];
|
||||
block.querySelectorAll('.parameters-section tr[data-param-in="path"]').forEach(row => { const paramName = row.dataset.paramName; const input = row.querySelector('input'); const required = queryDef.parameters.find(p => p.name === paramName)?.required; const value = input.value.trim(); input.classList.remove('is-invalid'); if (!value && required) { validParams = false; missingParams.push(`${paramName} (path)`); input.classList.add('is-invalid'); } else if (value) { path = path.replace(`:${paramName}`, encodeURIComponent(value)); } else { path = path.replace(`:${paramName}`, ''); } }); if (path.includes(':')) { const remaining = path.match(/:(\w+)/g) || []; if (remaining.length) { validParams = false; missingParams.push(...remaining); }} url += path.startsWith('/') ? path : `/${path}`; const searchParams = new URLSearchParams(); block.querySelectorAll('.parameters-section tr[data-param-in="query"]').forEach(row => { const paramName = row.dataset.paramName; const input = row.querySelector('input'); const required = queryDef.parameters.find(p => p.name === paramName)?.required; const value = input.value.trim(); input.classList.remove('is-invalid'); if (value) searchParams.set(paramName, value); else if (required) { validParams = false; missingParams.push(`${paramName} (query)`); input.classList.add('is-invalid'); } });
|
||||
if (!validParams) { const errorMsg = `Error: Missing required parameter(s): ${[...new Set(missingParams)].join(', ')}`; respStatusDiv.textContent = errorMsg; respStatusDiv.style.color = 'red'; reqUrlOutput.textContent = 'Error: Invalid parameters'; curlOutput.textContent = 'Error: Invalid parameters'; executeButton.disabled = false; executeButton.textContent = 'Execute'; return; } const queryString = searchParams.toString(); if (queryString) url += (url.includes('?') ? '&' : '?') + queryString;
|
||||
if (queryDef.requestBody) { const contentType = reqContentTypeSelect ? reqContentTypeSelect.value : 'application/fhir+json'; headers['Content-Type'] = contentType; body = reqBodyTextarea ? reqBodyTextarea.value : ''; if (contentType === 'application/fhir+xml' && body.trim().startsWith('{')) { try { body = jsonToFhirXml(body); } catch (e) { console.warn("JSON->XML conversion failed", e); }} else if (contentType === 'application/fhir+json' && body.trim().startsWith('<')) { try { body = xmlToFhirJson(body); } catch (e) { console.warn("XML->JSON conversion failed", e); }} if (isUsingLocalHapi && ['POST', 'PUT', 'PATCH', 'DELETE'].includes(method) && fhirOperationsForm.querySelector('input[name="csrf_token"]')) { headers['X-CSRFToken'] = fhirOperationsForm.querySelector('input[name="csrf_token"]').value; } }
|
||||
reqUrlOutput.textContent = url; curlOutput.textContent = generateCurlCommand(method, url, headers, body); console.log(`Executing: ${method} ${url}`); console.log("Headers:", headers); if (body !== undefined) console.log("Body:", (body || '').substring(0, 300) + (body.length > 300 ? "..." : ""));
|
||||
let respData = { json: null, xml: null, narrative: null, text: null, status: 0, statusText: '', contentType: '' };
|
||||
try {
|
||||
const resp = await fetch(url, { method, headers, body }); respData.status = resp.status; respData.statusText = resp.statusText; respData.contentType = resp.headers.get('Content-Type') || ''; respData.text = await resp.text(); respStatusDiv.textContent = `Status: ${resp.status} ${resp.statusText}`; respStatusDiv.style.color = resp.ok ? 'green' : 'red';
|
||||
if (respData.text) { if (respData.contentType.includes('json')) { try { respData.json = JSON.parse(respData.text); respData.xml = jsonToFhirXml(respData.json); if (respData.json.text?.div) respData.narrative = respData.json.text.div; } catch (e) {} } else if (respData.contentType.includes('xml')) { respData.xml = respData.text; respData.json = JSON.parse(xmlToFhirJson(respData.xml)); try { const p = new DOMParser(); const xd = p.parseFromString(respData.xml, "application/xml"); const nn = xd.querySelector("div[xmlns='http://www.w3.org/1999/xhtml']"); if (nn) respData.narrative = nn.outerHTML; } catch(e) {} } else { respData.json = { contentType: respData.contentType, content: respData.text }; respData.xml = `<data contentType="${escapeXml(respData.contentType)}">${escapeXml(respData.text)}</data>`; } } else if (resp.ok) { respData.json = { message: "Operation successful, no content returned." }; respData.xml = jsonToFhirXml({}); } else { respData.json = { error: `Request failed with status ${resp.status}`, detail: resp.statusText }; respData.xml = `<error>Request failed with status ${resp.status} ${escapeXml(resp.statusText)}</error>`; }
|
||||
block.dataset.responseData = JSON.stringify(respData); respFormatSelect.style.display = 'inline-block'; copyRespButton.style.display = 'inline-block'; downloadRespButton.style.display = 'inline-block'; respFormatSelect.disabled = false; const narrativeOption = respFormatSelect.querySelector('option[value="narrative"]'); if (narrativeOption) narrativeOption.disabled = !respData.narrative; respFormatSelect.dispatchEvent(new Event('change'));
|
||||
} catch (e) { console.error('Fetch Error:', e); respStatusDiv.textContent = `Network Error: ${e.message}`; respStatusDiv.style.color = 'red'; respOutputCode.textContent = `Request failed: ${e.message}\nURL: ${url}`; respOutputCode.className = 'language-text'; respFormatSelect.style.display = 'none'; copyRespButton.style.display = 'none'; downloadRespButton.style.display = 'none'; } finally { executeButton.disabled = false; executeButton.textContent = 'Execute'; }
|
||||
});
|
||||
}
|
||||
|
||||
if (respFormatSelect && respNarrativeDiv && respOutputPre && respOutputCode) {
|
||||
respFormatSelect.addEventListener('change', () => { const format = respFormatSelect.value; try { const data = JSON.parse(block.dataset.responseData || '{}'); respNarrativeDiv.style.display = 'none'; respOutputPre.style.display = 'block'; switch (format) { case 'xml': respOutputCode.textContent = data.xml || data.text || '<No XML>'; respOutputCode.className = 'language-xml'; respOutputPre.style.whiteSpace = 'pre'; break; case 'narrative': if (data.narrative) { respNarrativeDiv.innerHTML = data.narrative; respNarrativeDiv.style.display = 'block'; respOutputPre.style.display = 'none'; } else { respOutputCode.textContent = '<Narrative unavailable>'; respOutputCode.className = 'language-text'; respOutputPre.style.whiteSpace = 'pre-wrap'; } break; case 'json': default: respOutputCode.textContent = data.json ? JSON.stringify(data.json, null, 2) : (data.text || '<No JSON>'); respOutputCode.className = 'language-json'; respOutputPre.style.whiteSpace = 'pre-wrap'; break; } } catch (e) {} });
|
||||
}
|
||||
|
||||
if (exampleTabs.length && examplePanel && schemaPanel) {
|
||||
exampleTabs.forEach(tab => { tab.addEventListener('click', () => { exampleTabs.forEach(t => t.classList.remove('active')); tab.classList.add('active'); const target = tab.dataset.name; examplePanel.style.display = target === 'example' ? 'block' : 'none'; schemaPanel.style.display = target === 'schema' ? 'block' : 'none'; updateStaticExampleFormat(block); }); });
|
||||
}
|
||||
|
||||
if (exampleMediaTypeSelect) {
|
||||
exampleMediaTypeSelect.addEventListener('change', () => updateStaticExampleFormat(block));
|
||||
}
|
||||
|
||||
if (copyCurlButton && curlOutput) {
|
||||
copyCurlButton.addEventListener('click', (e) => { if (curlOutput.textContent) copyToClipboard(curlOutput.textContent, e.currentTarget); });
|
||||
}
|
||||
|
||||
if (copyRespButton && respFormatSelect) {
|
||||
copyRespButton.addEventListener('click', (e) => { const format = respFormatSelect.value; let content = ''; if (format === 'narrative') { content = respNarrativeDiv ? respNarrativeDiv.innerHTML : ''; } else { content = respOutputCode ? respOutputCode.textContent : ''; } if (content) copyToClipboard(content, e.currentTarget); });
|
||||
}
|
||||
|
||||
if (downloadRespButton && respFormatSelect) {
|
||||
downloadRespButton.addEventListener('click', () => { const format = respFormatSelect.value; let content = ''; let ext = 'txt'; let mime = 'text/plain'; const data = JSON.parse(block.dataset.responseData || '{}'); const dateStamp = new Date().toISOString().replace(/[:.]/g, '-'); const filename = `response-${currentSelectedResource || 'system'}-${query.method}-${dateStamp}`; switch (format) { case 'json': content = data.json ? JSON.stringify(data.json, null, 2) : (data.text || ''); ext = 'json'; mime = 'application/json'; break; case 'xml': content = data.xml || data.text || ''; ext = 'xml'; mime = 'application/xml'; break; case 'narrative': content = respNarrativeDiv ? respNarrativeDiv.innerHTML : ''; ext = 'html'; mime = 'text/html'; break; } if (content) downloadFile(content, `${filename}.${ext}`, mime); });
|
||||
}
|
||||
|
||||
updateStaticExampleFormat(block); // Initialize example format
|
||||
}); // End forEach query
|
||||
|
||||
swaggerUiContainer.style.display = 'block';
|
||||
} // End updateQueryListUI
|
||||
|
||||
// --- Update Static Example/Schema Format Display (Unchanged) ---
|
||||
function updateStaticExampleFormat(block) {
|
||||
const data = JSON.parse(block.dataset.queryData || '{}');
|
||||
const mediaTypeSelect = block.querySelector('.example-media-type-select');
|
||||
const examplePre = block.querySelector('.responses-section .example-code-display');
|
||||
const exampleCode = examplePre?.querySelector('code');
|
||||
const schemaPre = block.querySelector('.responses-section .schema-code-display');
|
||||
const schemaCode = schemaPre?.querySelector('code');
|
||||
if (!mediaTypeSelect || !exampleCode || !schemaCode || !examplePre || !schemaPre) return;
|
||||
const mediaType = mediaTypeSelect.value; const example = data.example || ''; const isJsonLike = typeof example === 'string' && example.trim().startsWith('{'); const exampleJson = isJsonLike ? example : '{}'; const schemaJson = JSON.stringify(data.schema || {}, null, 2);
|
||||
if (mediaType === 'application/fhir+json') { exampleCode.textContent = exampleJson; exampleCode.className = 'language-json'; schemaCode.textContent = schemaJson; schemaCode.className = 'language-json'; examplePre.style.whiteSpace = 'pre-wrap'; schemaPre.style.whiteSpace = 'pre-wrap'; }
|
||||
else if (mediaType === 'application/fhir+xml') { exampleCode.textContent = isJsonLike ? jsonToFhirXml(exampleJson) : `<value>${escapeXml(example)}</value>`; exampleCode.className = 'language-xml'; schemaCode.textContent = jsonToFhirXml(data.schema || {}); schemaCode.className = 'language-xml'; examplePre.style.whiteSpace = 'pre'; schemaPre.style.whiteSpace = 'pre'; }
|
||||
}
|
||||
|
||||
// --- Fetch Server Metadata (FIXED Local URL Handling) ---
|
||||
if (fetchMetadataButton) {
|
||||
fetchMetadataButton.addEventListener('click', async () => {
|
||||
// Clear previous results immediately
|
||||
if (resourceButtonsContainer) resourceButtonsContainer.innerHTML = '<span class="text-muted">Fetching...</span>';
|
||||
if (resourceTypesDisplayDiv) resourceTypesDisplayDiv.style.display = 'block';
|
||||
if (swaggerUiContainer) swaggerUiContainer.style.display = 'none'; // Hide old query list
|
||||
fetchedMetadataCache = null; // Clear cache before fetch attempt
|
||||
availableSystemOperations = [];
|
||||
|
||||
// Determine Base URL - FIXED
|
||||
const customUrl = fhirServerUrlInput.value.trim().replace(/\/+$/, '');
|
||||
const baseUrl = isUsingLocalHapi ? '/fhir' : customUrl; // Use '/fhir' proxy path if local
|
||||
|
||||
// Validate custom URL only if not using local HAPI
|
||||
if (!isUsingLocalHapi && !baseUrl) { // Should only happen if customUrl is empty
|
||||
fhirServerUrlInput.classList.add('is-invalid');
|
||||
alert('Please enter a valid FHIR server URL.');
|
||||
if (resourceButtonsContainer) resourceButtonsContainer.innerHTML = `<span class="text-danger">Error: Custom URL required.</span>`;
|
||||
return;
|
||||
}
|
||||
// Basic format check for custom URL
|
||||
if (!isUsingLocalHapi) {
|
||||
try {
|
||||
new URL(baseUrl); // Check if it's a parseable URL format
|
||||
} catch (_) {
|
||||
fhirServerUrlInput.classList.add('is-invalid');
|
||||
alert('Invalid custom URL format. Please enter a valid URL (e.g., https://example.com/fhir).');
|
||||
if (resourceButtonsContainer) resourceButtonsContainer.innerHTML = `<span class="text-danger">Error: Invalid custom URL format.</span>`;
|
||||
return;
|
||||
}
|
||||
}
|
||||
fhirServerUrlInput.classList.remove('is-invalid');
|
||||
|
||||
// Construct metadata URL (always add /metadata)
|
||||
const url = `${baseUrl}/metadata`;
|
||||
|
||||
console.log(`Fetching metadata from: ${url}`);
|
||||
fetchMetadataButton.disabled = true; fetchMetadataButton.textContent = 'Fetching...';
|
||||
|
||||
try {
|
||||
const resp = await fetch(url, { method: 'GET', headers: { 'Accept': 'application/fhir+json' } });
|
||||
if (!resp.ok) { const errText = await resp.text(); throw new Error(`HTTP ${resp.status} ${resp.statusText}: ${errText.substring(0, 500)}`); }
|
||||
const data = await resp.json();
|
||||
console.log('Metadata received:', data);
|
||||
fetchedMetadataCache = data; // Cache successful fetch
|
||||
displayMetadataAndResourceButtons(data); // Parse and display
|
||||
} catch (e) {
|
||||
console.error('Metadata fetch error:', e);
|
||||
if (resourceButtonsContainer) resourceButtonsContainer.innerHTML = `<span class="text-danger">Error fetching metadata: ${e.message}</span>`;
|
||||
if (resourceTypesDisplayDiv) resourceTypesDisplayDiv.style.display = 'block'; // Keep container visible to show error
|
||||
if (swaggerUiContainer) swaggerUiContainer.style.display = 'none';
|
||||
alert(`Error fetching metadata: ${e.message}`);
|
||||
fetchedMetadataCache = null; // Clear cache on error
|
||||
availableSystemOperations = [];
|
||||
} finally {
|
||||
fetchMetadataButton.disabled = false; fetchMetadataButton.textContent = 'Fetch Metadata';
|
||||
}
|
||||
});
|
||||
} else {
|
||||
console.error("Fetch Metadata button (#fetchMetadata) not found!");
|
||||
}
|
||||
|
||||
|
||||
// --- Display Metadata & Parse Operations (FIXED Method Parsing Logic) ---
|
||||
function displayMetadataAndResourceButtons(data) {
|
||||
const rest = data?.rest?.[0];
|
||||
if (!rest) { resourceButtonsContainer.innerHTML = `<span class="text-danger">Error: Invalid CapabilityStatement (missing 'rest').</span>`; resourceTypesDisplayDiv.style.display = 'block'; swaggerUiContainer.style.display = 'none'; availableSystemOperations = []; return; }
|
||||
const resourceTypes = rest.resource?.map(r => r.type).filter(Boolean) || [];
|
||||
const interactions = rest.interaction?.map(i => i.code) || [];
|
||||
|
||||
// --- PARSE OPERATIONS (Revised Method/Level Detection) ---
|
||||
availableSystemOperations = (rest.operation || []).map(op => {
|
||||
const name = op.name || '';
|
||||
const defUrl = op.definition || '';
|
||||
let levels = new Set();
|
||||
let methods = new Set(); // Start with empty set
|
||||
|
||||
// Determine Levels (same logic as before, seems reasonable)
|
||||
if (defUrl.includes('System') || name.includes('system') || ['transaction', 'batch', 'metadata', 'history-system'].includes(name)) levels.add('system');
|
||||
if (defUrl.includes('Type') || name.includes('type') || name.startsWith('$')) levels.add('type');
|
||||
if (defUrl.includes('Instance') || name.includes('instance') || (name.startsWith('$') && !name.endsWith('-system'))) levels.add('instance');
|
||||
if (!levels.size) { if (name.startsWith('$') || ['transaction', 'batch', 'metadata', 'history-system', 'capabilities'].includes(name)) levels.add('system'); else { levels.add('type'); levels.add('instance'); } }
|
||||
|
||||
// Determine Methods (Improved based on metadata example)
|
||||
if (['metadata', 'history-system', 'history-type', 'history-instance', 'capabilities'].includes(name)) {
|
||||
methods.add('GET');
|
||||
} else if (name === 'transaction' || name === 'batch' || name === 'server-transaction') {
|
||||
methods.add('POST');
|
||||
levels = new Set(['system']); // Force system level for transaction/batch
|
||||
} else if (['diff', 'meta', 'get-resource-counts'].includes(name)) {
|
||||
// These support both GET and POST according to the example metadata
|
||||
methods.add('GET');
|
||||
methods.add('POST');
|
||||
} else if (['reindex', 'perform-reindexing-pass', 'mark-all-resources-for-reindexing', 'expunge', 'reindex-terminology', 'hapi.fhir.replace-references'].includes(name)) {
|
||||
// These are typically POST only
|
||||
methods.add('POST');
|
||||
} else {
|
||||
// Default guess: If documentation or def URL mentions GET, add it, otherwise default to POST
|
||||
methods.add('POST'); // Assume POST by default for unknown operations
|
||||
if (op.documentation?.toLowerCase().includes(' http get') || defUrl.toLowerCase().includes('get')) {
|
||||
methods.add('GET');
|
||||
}
|
||||
}
|
||||
|
||||
return { name, methods: Array.from(methods), documentation: op.documentation || '', definition: defUrl, levels: Array.from(levels) };
|
||||
}).filter(op => op.name); // Filter out operations without a name
|
||||
|
||||
// Add standard interactions as fallback if not present as named operations (Optional, handled in generator)
|
||||
// Example: if (!availableSystemOperations.some(...) && interactions.includes(...)) { ... }
|
||||
|
||||
console.log("Final Parsed Operations List:", availableSystemOperations);
|
||||
|
||||
resourceButtonsContainer.innerHTML = ''; // Clear previous buttons
|
||||
const supportsSystemLevel = interactions.some(code => ['transaction', 'batch', 'history-system', 'search-system', 'capabilities'].includes(code)) || availableSystemOperations.some(op => op.levels.includes('system'));
|
||||
|
||||
if (supportsSystemLevel) {
|
||||
const systemButton = document.createElement('button');
|
||||
systemButton.type = 'button'; systemButton.className = 'btn btn-outline-success btn-sm me-2 mb-2'; systemButton.textContent = 'System';
|
||||
systemButton.addEventListener('click', (event) => handleResourceOrSystemButtonClick(event.target, 'System'));
|
||||
resourceButtonsContainer.appendChild(systemButton);
|
||||
}
|
||||
resourceTypes.sort().forEach(resType => {
|
||||
const resourceButton = document.createElement('button');
|
||||
resourceButton.type = 'button'; resourceButton.className = 'btn btn-outline-dark-blue btn-sm me-2 mb-2'; resourceButton.textContent = resType;
|
||||
resourceButton.addEventListener('click', (event) => handleResourceOrSystemButtonClick(event.target, resType));
|
||||
resourceButtonsContainer.appendChild(resourceButton);
|
||||
});
|
||||
|
||||
resourceTypesDisplayDiv.style.display = 'block'; // Show the buttons container
|
||||
|
||||
const firstBtn = resourceButtonsContainer.querySelector('button');
|
||||
if (firstBtn) { firstBtn.click(); }
|
||||
else { resourceButtonsContainer.innerHTML = '<span class="text-warning">No resources or system operations found in metadata.</span>'; swaggerUiContainer.style.display = 'none'; }
|
||||
}
|
||||
|
||||
// --- Handle Resource/System Button Clicks (FIXED color classes) ---
|
||||
function handleResourceOrSystemButtonClick(clickedButton, resourceOrSystemName) {
|
||||
resourceButtonsContainer.querySelectorAll('.btn').forEach(button => {
|
||||
button.classList.remove('active', 'btn-success', 'btn-dark-blue', 'btn-outline-success', 'btn-outline-dark-blue'); // Remove all relevant classes
|
||||
// Re-apply the correct OUTLINE class
|
||||
if (button.textContent === 'System') {
|
||||
button.classList.add('btn-outline-success');
|
||||
} else {
|
||||
button.classList.add('btn-outline-dark-blue');
|
||||
}
|
||||
});
|
||||
// Activate the clicked button
|
||||
clickedButton.classList.add('active');
|
||||
if (resourceOrSystemName === 'System') {
|
||||
clickedButton.classList.remove('btn-outline-success'); // Remove outline
|
||||
clickedButton.classList.add('btn-success'); // Add solid
|
||||
} else {
|
||||
clickedButton.classList.remove('btn-outline-dark-blue'); // Remove outline
|
||||
clickedButton.classList.add('btn-dark-blue'); // Add solid
|
||||
}
|
||||
updateQueryListUI(resourceOrSystemName); // Update the operation list
|
||||
}
|
||||
|
||||
// --- Initial Page State Setup ---
|
||||
resourceTypesDisplayDiv.style.display = 'none';
|
||||
swaggerUiContainer.style.display = 'none';
|
||||
updateServerToggleUI(); // Set the initial UI state correctly based on default isUsingLocalHapi = true
|
||||
|
||||
}); // End DOMContentLoaded Listener
|
||||
</script>
|
||||
{% endblock %}
|
75
templates/fsh_converter.html
Normal file
75
templates/fsh_converter.html
Normal file
@ -0,0 +1,75 @@
|
||||
{% extends "base.html" %}
|
||||
{% from "_form_helpers.html" import render_field %}
|
||||
|
||||
{% block content %}
|
||||
<div class="px-4 py-5 my-5 text-center">
|
||||
<img class="d-block mx-auto mb-4" src="{{ url_for('static', filename='FHIRFLARE.png') }}" alt="FHIRFLARE IG Toolkit" width="192" height="192">
|
||||
<h1 class="display-5 fw-bold text-body-emphasis">FSH Converter</h1>
|
||||
<div class="col-lg-6 mx-auto">
|
||||
<p class="lead mb-4">
|
||||
Convert FHIR JSON or XML resources to FHIR Shorthand (FSH) using GoFSH.
|
||||
</p>
|
||||
<div class="d-grid gap-2 d-sm-flex justify-content-sm-center">
|
||||
<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">View Downloaded IGs</a>
|
||||
<a href="{{ url_for('validate_sample') }}" class="btn btn-outline-secondary btn-lg px-4">Validate Sample</a>
|
||||
<a href="{{ url_for('fhir_ui_operations') }}" class="btn btn-outline-secondary btn-lg px-4">FHIR Operations</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container mt-4">
|
||||
<h2><i class="bi bi-file-code me-2"></i>Convert FHIR to FSH</h2>
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-8">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<form method="POST" enctype="multipart/form-data" class="form">
|
||||
{{ form.hidden_tag() }}
|
||||
{{ render_field(form.package) }}
|
||||
{{ render_field(form.input_mode) }}
|
||||
<div id="file-upload" style="display: none;">
|
||||
{{ render_field(form.fhir_file) }}
|
||||
</div>
|
||||
<div id="text-input" style="display: none;">
|
||||
{{ render_field(form.fhir_text) }}
|
||||
</div>
|
||||
{{ render_field(form.output_style) }}
|
||||
{{ render_field(form.log_level) }}
|
||||
{{ render_field(form.fhir_version) }}
|
||||
<div class="d-grid gap-2 d-sm-flex">
|
||||
{{ form.submit(class="btn btn-success") }}
|
||||
<a href="{{ url_for('index') }}" class="btn btn-secondary">Back</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% if error %}
|
||||
<div class="alert alert-danger mt-4">{{ error }}</div>
|
||||
{% endif %}
|
||||
{% if fsh_output %}
|
||||
<div class="alert alert-success mt-4">Conversion successful!</div>
|
||||
<h3 class="mt-4">FSH Output</h3>
|
||||
<pre class="bg-light p-3">{{ fsh_output }}</pre>
|
||||
<a href="{{ url_for('download_fsh') }}" class="btn btn-primary">Download FSH</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.getElementById('input_mode').addEventListener('change', function() {
|
||||
const fileUpload = document.getElementById('file-upload');
|
||||
const textInput = document.getElementById('text-input');
|
||||
if (this.value === 'file') {
|
||||
fileUpload.style.display = 'block';
|
||||
textInput.style.display = 'none';
|
||||
} else {
|
||||
fileUpload.style.display = 'none';
|
||||
textInput.style.display = 'block';
|
||||
}
|
||||
});
|
||||
// Trigger change on page load to set initial state
|
||||
document.getElementById('input_mode').dispatchEvent(new Event('change'));
|
||||
</script>
|
||||
{% endblock %}
|
155
templates/woooo
155
templates/woooo
@ -1,155 +0,0 @@
|
||||
// Function to build the hierarchical data structure for the tree
|
||||
function buildTreeData(elements) {
|
||||
const treeRoot = { children: {}, element: null, name: 'Root' };
|
||||
const nodeMap = { 'Root': treeRoot };
|
||||
|
||||
console.log("Building tree with elements:", elements.length);
|
||||
elements.forEach((el, index) => {
|
||||
const path = el.path;
|
||||
const id = el.id || null;
|
||||
const sliceName = el.sliceName || null;
|
||||
|
||||
console.log(`Element ${index}: path=${path}, id=${id}, sliceName=${sliceName}`);
|
||||
|
||||
if (!path) {
|
||||
console.warn(`Skipping element ${index} with no path`);
|
||||
return;
|
||||
}
|
||||
|
||||
const parts = path.split('.');
|
||||
let currentPath = '';
|
||||
let parentNode = treeRoot;
|
||||
|
||||
for (let i = 0; i < parts.length; i++) {
|
||||
const part = parts[i];
|
||||
currentPath = i === 0 ? part : `${currentPath}.${part}`;
|
||||
|
||||
// For extensions, append sliceName to path if present
|
||||
let nodeKey = part;
|
||||
if (part === 'extension' && i === parts.length - 1 && sliceName) {
|
||||
nodeKey = `${part}:${sliceName}`;
|
||||
currentPath = id || currentPath; // Use id for precise matching in extensions
|
||||
}
|
||||
|
||||
if (!nodeMap[currentPath]) {
|
||||
const newNode = {
|
||||
children: {},
|
||||
element: null,
|
||||
name: nodeKey,
|
||||
path: currentPath
|
||||
};
|
||||
let parentPath = i === 0 ? 'Root' : parts.slice(0, i).join('.');
|
||||
parentNode = nodeMap[parentPath] || treeRoot;
|
||||
parentNode.children[nodeKey] = newNode;
|
||||
nodeMap[currentPath] = newNode;
|
||||
console.log(`Created node: path=${currentPath}, name=${nodeKey}`);
|
||||
}
|
||||
|
||||
if (i === parts.length - 1) {
|
||||
const targetNode = nodeMap[currentPath];
|
||||
targetNode.element = el;
|
||||
console.log(`Assigned element to node: path=${currentPath}, id=${id}, sliceName=${sliceName}`);
|
||||
}
|
||||
|
||||
parentNode = nodeMap[currentPath];
|
||||
}
|
||||
});
|
||||
|
||||
const treeData = Object.values(treeRoot.children);
|
||||
console.log("Tree data constructed:", treeData);
|
||||
return treeData;
|
||||
}
|
||||
|
||||
// Function to render a single node (and its children recursively) as an <li>
|
||||
function renderNodeAsLi(node, mustSupportPathsSet, level = 0) {
|
||||
if (!node || !node.element) {
|
||||
console.warn("Skipping render for invalid node:", node);
|
||||
return '';
|
||||
}
|
||||
const el = node.element;
|
||||
const path = el.path || 'N/A';
|
||||
const id = el.id || null;
|
||||
const sliceName = el.sliceName || null;
|
||||
const min = el.min !== undefined ? el.min : '';
|
||||
const max = el.max || '';
|
||||
const short = el.short || '';
|
||||
const definition = el.definition || '';
|
||||
|
||||
console.log(`Rendering node: path=${path}, id=${id}, sliceName=${sliceName}`);
|
||||
console.log(` MustSupportPathsSet contains path: ${mustSupportPathsSet.has(path)}`);
|
||||
if (id) {
|
||||
console.log(` MustSupportPathsSet contains id: ${mustSupportPathsSet.has(id)}`);
|
||||
}
|
||||
|
||||
// Check MS for path, id, or normalized extension path
|
||||
let isMustSupport = mustSupportPathsSet.has(path) || (id && mustSupportPathsSet.has(id));
|
||||
if (!isMustSupport && path.startsWith('Extension.extension')) {
|
||||
const basePath = path.split(':')[0];
|
||||
const baseId = id ? id.split(':')[0] : null;
|
||||
isMustSupport = mustSupportPathsSet.has(basePath) || (baseId && mustSupportPathsSet.has(baseId));
|
||||
console.log(` Extension check: basePath=${basePath}, baseId=${baseId}, isMustSupport=${isMustSupport}`);
|
||||
}
|
||||
|
||||
console.log(` Final isMustSupport for ${path}: ${isMustSupport}`);
|
||||
|
||||
const liClass = isMustSupport ? 'list-group-item py-1 px-2 list-group-item-warning' : 'list-group-item py-1 px-2';
|
||||
const mustSupportDisplay = isMustSupport ? '<i class="bi bi-check-circle-fill text-warning ms-1" title="Must Support"></i>' : '';
|
||||
const hasChildren = Object.keys(node.children).length > 0;
|
||||
const collapseId = `collapse-${path.replace(/[\.\:\/\[\]\(\)]/g, '-')}`;
|
||||
const padding = level * 20;
|
||||
const pathStyle = `padding-left: ${padding}px; white-space: nowrap;`;
|
||||
|
||||
let typeString = 'N/A';
|
||||
if (el.type && el.type.length > 0) {
|
||||
typeString = el.type.map(t => {
|
||||
let s = t.code || '';
|
||||
let profiles = t.targetProfile || t.profile || [];
|
||||
if (profiles.length > 0) {
|
||||
const targetTypes = profiles.map(p => (p || '').split('/').pop()).filter(Boolean).join(', ');
|
||||
if (targetTypes) {
|
||||
s += `(<span class="text-muted fst-italic" title="${profiles.join(', ')}">${targetTypes}</span>)`;
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}).join(' | ');
|
||||
}
|
||||
|
||||
let childrenHtml = '';
|
||||
if (hasChildren) {
|
||||
childrenHtml += `<ul class="collapse list-group list-group-flush structure-subtree" id="${collapseId}">`;
|
||||
Object.values(node.children).sort((a, b) => (a.element?.path ?? a.name).localeCompare(b.element?.path ?? b.name)).forEach(childNode => {
|
||||
childrenHtml += renderNodeAsLi(childNode, mustSupportPathsSet, level + 1);
|
||||
});
|
||||
childrenHtml += `</ul>`;
|
||||
}
|
||||
|
||||
let itemHtml = `<li class="${liClass}">`;
|
||||
itemHtml += `<div class="row gx-2 align-items-center ${hasChildren ? 'collapse-toggle' : ''}" ${hasChildren ? `data-bs-toggle="collapse" data-bs-target="#${collapseId}" role="button" aria-expanded="false" aria-controls="${collapseId}"` : ''}>`;
|
||||
itemHtml += `<div class="col-lg-4 col-md-3 text-truncate" style="${pathStyle}">`;
|
||||
itemHtml += `<span style="display: inline-block; width: 1.2em; text-align: center;">`;
|
||||
if (hasChildren) {
|
||||
itemHtml += `<i class="bi bi-chevron-right small toggle-icon"></i>`;
|
||||
}
|
||||
itemHtml += `</span>`;
|
||||
itemHtml += `<code class="fw-bold ms-1" title="${path}">${node.name}</code>`;
|
||||
itemHtml += `</div>`;
|
||||
itemHtml += `<div class="col-lg-1 col-md-1 text-center text-muted small"><code>${min}..${max}</code></div>`;
|
||||
itemHtml += `<div class="col-lg-3 col-md-3 text-truncate small">${typeString}</div>`;
|
||||
itemHtml += `<div class="col-lg-1 col-md-1 text-center">${mustSupportDisplay}</div>`;
|
||||
let descriptionTooltipAttrs = '';
|
||||
if (definition) {
|
||||
const escapedDefinition = definition
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''')
|
||||
.replace(/\n/g, ' ');
|
||||
descriptionTooltipAttrs = `data-bs-toggle="tooltip" data-bs-placement="top" title="${escapedDefinition}"`;
|
||||
}
|
||||
itemHtml += `<div class="col-lg-3 col-md-4 text-muted text-truncate small" ${descriptionTooltipAttrs}>${short || (definition ? '(definition only)' : '')}</div>`;
|
||||
itemHtml += `</div>`;
|
||||
itemHtml += childrenHtml;
|
||||
itemHtml += `</li>`;
|
||||
return itemHtml;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user