mirror of
https://github.com/Sudo-JHare/FHIRFLARE-IG-Toolkit.git
synced 2025-06-15 09:00:00 +00:00
344 lines
16 KiB
HTML
344 lines
16 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block content %}
|
|
<div class="container-fluid">
|
|
<div class="row">
|
|
<!-- Left Column: List of Downloaded IGs -->
|
|
<div class="col-md-6">
|
|
<h2>Downloaded IGs</h2>
|
|
{% if packages %}
|
|
<table class="table table-striped">
|
|
<thead>
|
|
<tr>
|
|
<th>Package Name</th>
|
|
<th>Version</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for pkg in packages %}
|
|
{% set name = pkg.name %}
|
|
{% set version = pkg.version %}
|
|
{% set filename = pkg.filename %}
|
|
{% set processed = (name, version) in processed_ids %}
|
|
{% set duplicate_group = duplicate_groups.get(name) %}
|
|
<tr {% if duplicate_group %}class="{{ group_colors[name] }}"{% endif %}>
|
|
<td>{{ name }}</td>
|
|
<td>{{ version }}</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
{% if duplicate_groups %}
|
|
<div class="alert alert-warning">
|
|
<strong>Duplicate Packages Detected:</strong>
|
|
<ul>
|
|
{% for name, versions in duplicate_groups.items() %}
|
|
<li>{{ name }} (Versions: {{ versions | join(', ') }})</li>
|
|
{% endfor %}
|
|
</ul>
|
|
<p>Please resolve duplicates by deleting older versions to avoid conflicts.</p>
|
|
</div>
|
|
{% endif %}
|
|
{% else %}
|
|
<p>No packages downloaded yet. Use the "Import IG" tab to download packages.</p>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- Right Column: Push IGs Form -->
|
|
<div class="col-md-6">
|
|
<h2>Push IGs to FHIR Server</h2>
|
|
<form id="pushIgForm" method="POST">
|
|
{{ form.csrf_token }}
|
|
<div class="mb-3">
|
|
<label for="packageSelect" class="form-label">Select Package to Push</label>
|
|
<select class="form-select" id="packageSelect" name="package_id" required>
|
|
<option value="" disabled selected>Select a package...</option>
|
|
{% for pkg in packages %}
|
|
<option value="{{ pkg.name }}#{{ pkg.version }}">{{ pkg.name }}#{{ pkg.version }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="dependencyMode" class="form-label">Dependency Mode Used During Import</label>
|
|
<input type="text" class="form-control" id="dependencyMode" readonly>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="fhirServerUrl" class="form-label">FHIR Server URL</label>
|
|
<input type="url" class="form-control" id="fhirServerUrl" name="fhir_server_url" placeholder="e.g., http://hapi.fhir.org/baseR4" required>
|
|
</div>
|
|
<div class="mb-3 form-check">
|
|
<input type="checkbox" class="form-check-input" id="includeDependencies" name="include_dependencies" checked>
|
|
<label class="form-check-label" for="includeDependencies">Include Dependencies</label>
|
|
</div>
|
|
<button type="submit" class="btn btn-primary" id="pushButton">Push to FHIR Server</button>
|
|
</form>
|
|
|
|
<!-- Live Console -->
|
|
<div class="mt-3">
|
|
<h4>Live Console</h4>
|
|
<div id="liveConsole" class="border p-3 bg-dark text-light" style="height: 300px; overflow-y: auto; font-family: monospace; font-size: 14px;">
|
|
<div>Console output will appear here...</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Response Area -->
|
|
<div id="pushResponse" class="mt-3">
|
|
{% with messages = get_flashed_messages(with_categories=true) %}
|
|
{% if messages %}
|
|
{% for category, message in messages %}
|
|
<div class="alert alert-{{ category }}"> {{ message }}</div>
|
|
{% endfor %}
|
|
{% endif %}
|
|
{% endwith %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
const packageSelect = document.getElementById('packageSelect');
|
|
const dependencyModeField = document.getElementById('dependencyMode');
|
|
const csrfToken = "{{ form.csrf_token._value() }}";
|
|
|
|
// Update dependency mode when package selection changes
|
|
packageSelect.addEventListener('change', function() {
|
|
const packageId = this.value;
|
|
if (packageId) {
|
|
const [packageName, version] = packageId.split('#');
|
|
fetch(`/get-package-metadata?package_name=${packageName}&version=${version}`)
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.dependency_mode) {
|
|
dependencyModeField.value = data.dependency_mode;
|
|
} else {
|
|
dependencyModeField.value = 'Unknown';
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('Error fetching metadata:', error);
|
|
dependencyModeField.value = 'Error';
|
|
});
|
|
} else {
|
|
dependencyModeField.value = '';
|
|
}
|
|
});
|
|
|
|
// Trigger change event on page load if a package is pre-selected
|
|
if (packageSelect.value) {
|
|
packageSelect.dispatchEvent(new Event('change'));
|
|
}
|
|
|
|
document.getElementById('pushIgForm').addEventListener('submit', async function(event) {
|
|
event.preventDefault();
|
|
|
|
const form = event.target;
|
|
const pushButton = document.getElementById('pushButton');
|
|
const formData = new FormData(form);
|
|
const packageId = formData.get('package_id');
|
|
const [packageName, version] = packageId.split('#');
|
|
const fhirServerUrl = formData.get('fhir_server_url');
|
|
const includeDependencies = formData.get('include_dependencies') === 'on';
|
|
|
|
// Disable the button to prevent multiple submissions
|
|
pushButton.disabled = true;
|
|
pushButton.textContent = 'Pushing...';
|
|
|
|
// Clear the console and response area
|
|
const liveConsole = document.getElementById('liveConsole');
|
|
const responseDiv = document.getElementById('pushResponse');
|
|
liveConsole.innerHTML = '<div>Starting push operation...</div>';
|
|
responseDiv.innerHTML = '';
|
|
|
|
try {
|
|
const response = await fetch('/api/push-ig', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'Accept': 'application/x-ndjson',
|
|
'X-CSRFToken': csrfToken
|
|
},
|
|
body: JSON.stringify({
|
|
package_name: packageName,
|
|
version: version,
|
|
fhir_server_url: fhirServerUrl,
|
|
include_dependencies: includeDependencies,
|
|
api_key: '{{ api_key | safe }}'
|
|
})
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const errorData = await response.json();
|
|
throw new Error(errorData.message || 'Failed to push package');
|
|
}
|
|
|
|
// Stream the response
|
|
const reader = response.body.getReader();
|
|
const decoder = new TextDecoder();
|
|
let buffer = '';
|
|
|
|
while (true) {
|
|
const { done, value } = await reader.read();
|
|
if (done) break;
|
|
|
|
buffer += decoder.decode(value, { stream: true });
|
|
const lines = buffer.split('\n');
|
|
buffer = lines.pop();
|
|
|
|
for (const line of lines) {
|
|
if (!line.trim()) continue;
|
|
try {
|
|
const data = JSON.parse(line);
|
|
const timestamp = new Date().toLocaleTimeString();
|
|
let messageClass = '';
|
|
let prefix = '';
|
|
|
|
switch (data.type) {
|
|
case 'start':
|
|
case 'progress':
|
|
messageClass = 'text-info';
|
|
prefix = '[INFO]';
|
|
break;
|
|
case 'success':
|
|
messageClass = 'text-success';
|
|
prefix = '[SUCCESS]';
|
|
break;
|
|
case 'error':
|
|
messageClass = 'text-danger';
|
|
prefix = '[ERROR]';
|
|
break;
|
|
case 'warning':
|
|
messageClass = 'text-warning';
|
|
prefix = '[WARNING]';
|
|
break;
|
|
case 'complete':
|
|
if (data.data.status === 'success') {
|
|
responseDiv.innerHTML = `
|
|
<div class="alert alert-success">
|
|
<strong>Success:</strong> ${data.data.message}<br>
|
|
<strong>Pushed Packages:</strong> ${data.data.pushed_packages.join(', ')}<br>
|
|
<strong>Server Response:</strong> ${data.data.server_response}
|
|
</div>
|
|
`;
|
|
} else if (data.data.status === 'partial') {
|
|
responseDiv.innerHTML = `
|
|
<div class="alert alert-warning">
|
|
<strong>Partial Success:</strong> ${data.data.message}<br>
|
|
<strong>Pushed Packages:</strong> ${data.data.pushed_packages.join(', ')}<br>
|
|
<strong>Server Response:</strong> ${data.data.server_response}
|
|
</div>
|
|
`;
|
|
} else {
|
|
responseDiv.innerHTML = `
|
|
<div class="alert alert-danger">
|
|
<strong>Error:</strong> ${data.data.message}
|
|
</div>
|
|
`;
|
|
}
|
|
messageClass = data.data.status === 'success' ? 'text-success' : (data.data.status === 'partial' ? 'text-warning' : 'text-danger');
|
|
prefix = data.data.status === 'success' ? '[SUCCESS]' : (data.data.status === 'partial' ? '[PARTIAL]' : '[ERROR]');
|
|
break;
|
|
default:
|
|
messageClass = 'text-light';
|
|
prefix = '[INFO]';
|
|
}
|
|
|
|
const messageDiv = document.createElement('div');
|
|
messageDiv.className = messageClass;
|
|
messageDiv.textContent = `${timestamp} ${prefix} ${data.message || (data.data && data.data.message) || 'Unknown message'}`;
|
|
liveConsole.appendChild(messageDiv);
|
|
liveConsole.scrollTop = liveConsole.scrollHeight;
|
|
} catch (parseError) {
|
|
console.error('Error parsing streamed message:', parseError, 'Line:', line);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (buffer.trim()) {
|
|
try {
|
|
const data = JSON.parse(buffer);
|
|
const timestamp = new Date().toLocaleTimeString();
|
|
let messageClass = '';
|
|
let prefix = '';
|
|
|
|
switch (data.type) {
|
|
case 'start':
|
|
case 'progress':
|
|
messageClass = 'text-info';
|
|
prefix = '[INFO]';
|
|
break;
|
|
case 'success':
|
|
messageClass = 'text-success';
|
|
prefix = '[SUCCESS]';
|
|
break;
|
|
case 'error':
|
|
messageClass = 'text-danger';
|
|
prefix = '[ERROR]';
|
|
break;
|
|
case 'warning':
|
|
messageClass = 'text-warning';
|
|
prefix = '[WARNING]';
|
|
break;
|
|
case 'complete':
|
|
if (data.data.status === 'success') {
|
|
responseDiv.innerHTML = `
|
|
<div class="alert alert-success">
|
|
<strong>Success:</strong> ${data.data.message}<br>
|
|
<strong>Pushed Packages:</strong> ${data.data.pushed_packages.join(', ')}<br>
|
|
<strong>Server Response:</strong> ${data.data.server_response}
|
|
</div>
|
|
`;
|
|
} else if (data.data.status === 'partial') {
|
|
responseDiv.innerHTML = `
|
|
<div class="alert alert-warning">
|
|
<strong>Partial Success:</strong> ${data.data.message}<br>
|
|
<strong>Pushed Packages:</strong> ${data.data.pushed_packages.join(', ')}<br>
|
|
<strong>Server Response:</strong> ${data.data.server_response}
|
|
</div>
|
|
`;
|
|
} else {
|
|
responseDiv.innerHTML = `
|
|
<div class="alert alert-danger">
|
|
<strong>Error:</strong> ${data.data.message}
|
|
</div>
|
|
`;
|
|
}
|
|
messageClass = data.data.status === 'success' ? 'text-success' : (data.data.status === 'partial' ? 'text-warning' : 'text-danger');
|
|
prefix = data.data.status === 'success' ? '[SUCCESS]' : (data.data.status === 'partial' ? '[PARTIAL]' : '[ERROR]');
|
|
break;
|
|
default:
|
|
messageClass = 'text-light';
|
|
prefix = '[INFO]';
|
|
}
|
|
|
|
const messageDiv = document.createElement('div');
|
|
messageDiv.className = messageClass;
|
|
messageDiv.textContent = `${timestamp} ${prefix} ${data.message || (data.data && data.data.message) || 'Unknown message'}`;
|
|
liveConsole.appendChild(messageDiv);
|
|
liveConsole.scrollTop = liveConsole.scrollHeight;
|
|
} catch (parseError) {
|
|
console.error('Error parsing final streamed message:', parseError, 'Buffer:', buffer);
|
|
}
|
|
}
|
|
|
|
} catch (error) {
|
|
const timestamp = new Date().toLocaleTimeString();
|
|
const errorDiv = document.createElement('div');
|
|
errorDiv.className = 'text-danger';
|
|
errorDiv.textContent = `${timestamp} [ERROR] Failed to push package: ${error.message}`;
|
|
liveConsole.appendChild(errorDiv);
|
|
liveConsole.scrollTop = liveConsole.scrollHeight;
|
|
|
|
responseDiv.innerHTML = `
|
|
<div class="alert alert-danger">
|
|
<strong>Error:</strong> Failed to push package: ${error.message}
|
|
</div>
|
|
`;
|
|
} finally {
|
|
pushButton.disabled = false;
|
|
pushButton.textContent = 'Push to FHIR Server';
|
|
}
|
|
});
|
|
});
|
|
</script>
|
|
{% endblock %} |