Skip to content
Merged
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 57 additions & 79 deletions bfd9000_web/archive/templates/archive/scan.html
Original file line number Diff line number Diff line change
Expand Up @@ -138,22 +138,12 @@ <h2 class="card-title text-lg">Record Metadata</h2>
min-width: 8rem;
}
</style>
<!-- Record Type -->
<!-- Image Type -->
<div class="form-control">
<label class="label">
<span class="label-text">Record Type <span class="text-error">*</span></span>
<span class="label-text">Image Type<span class="text-error">*</span></span>
Comment thread
aspiringLich marked this conversation as resolved.
Outdated
</label>
<select id="record-type" class="select select-bordered select-sm">
<option value="">Loading...</option>
</select>
</div>

<!-- Modality -->
<div class="form-control">
<label class="label">
<span class="label-text">Modality <span class="text-error">*</span></span>
</label>
<select id="modality" class="select select-bordered select-sm">
<select id="image-type" class="select select-bordered select-sm">
<option value="">Loading...</option>
</select>
</div>
Expand Down Expand Up @@ -247,9 +237,8 @@ <h3 class="font-bold text-lg mb-2">Scanner Device Info</h3>
aiFile: null,
requiresServerTransform: false,
transformOps: [],
aiImageTypeCode: null,
aiPatientOrientation: null,
valuesets: {
image_types: [],
record_types: [],
modalities: []
}
Expand Down Expand Up @@ -306,7 +295,7 @@ <h3 class="font-bold text-lg mb-2">Scanner Device Info</h3>

async function createRecord(encounterId, formData) {
const csrftoken = getCsrfToken();

const response = await fetch((window.script_name || '') + `/api/encounters/${encounterId}/records/`, {
method: 'POST',
headers: {
Expand Down Expand Up @@ -583,38 +572,10 @@ <h3 class="font-bold text-lg mb-2">Scanner Device Info</h3>
state.aiFile = file;
state.requiresServerTransform = false;
state.transformOps = [];
state.aiImageTypeCode = null;
state.aiPatientOrientation = null;
showFilePreview(file, file);
updateSubmitButton();
}

function mapAIType(typePrediction) {
const mapping = {
'Lateral': {
recordType: '201456002',
imageType: 'L',
patientOrientation: ['A', 'F'],
},
'Hip & Pelvis': {
recordType: '268425006',
imageType: 'P',
patientOrientation: null,
},
'Foot & Ankle': {
recordType: '1597004',
imageType: 'FA',
patientOrientation: null,
},
'Hand & Wrist': {
recordType: '39714003',
imageType: 'H',
patientOrientation: null,
},
};
return mapping[typePrediction] || null;
}

// ========================================
// UI UPDATE FUNCTIONS
// ========================================
Expand Down Expand Up @@ -698,14 +659,13 @@ <h3 class="font-bold text-lg mb-2">Scanner Device Info</h3>
options.forEach(opt => {
const option = document.createElement('option');
option.value = opt.id;
option.textContent = opt.display;
option.textContent = `${opt.display} (${opt.id})`;
Comment thread
aspiringLich marked this conversation as resolved.
Outdated
select.appendChild(option);
});
}

function clearFormFields() {
document.getElementById('record-type').value = '';
document.getElementById('modality').value = '';
document.getElementById('image-type').value = '';
document.getElementById('operator').value = defaultOperatorDisplay;
// Reset acquisition date to today
document.getElementById('acquisition-date').valueAsDate = new Date();
Expand All @@ -717,10 +677,9 @@ <h3 class="font-bold text-lg mb-2">Scanner Device Info</h3>
const hasSubject = state.subject !== null;
const hasEncounter = state.encounter !== null;
const hasFile = state.file !== null;
const hasRecordType = document.getElementById('record-type').value !== '';
const hasModality = document.getElementById('modality').value !== '';
const hasImageType = document.getElementById('image-type').value !== '';

const canSubmit = hasSubject && hasEncounter && hasFile && hasRecordType && hasModality;
const canSubmit = hasSubject && hasEncounter && hasFile && hasImageType;
document.getElementById('submit-button').disabled = !canSubmit;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[claude-sonnet-4.6 / @worker] result.type_probability may be undefined here. After this PR queryAI spreads the original result but stores the probability as class_probability. If the AI service does not return a type_probability field, calling .toFixed(2) throws TypeError, which is caught by the outer catch and shown as an error to the user even though classification succeeded. Fix: (result.class_probability ?? result.type_probability)?.toFixed(2) ?? 'N/A'.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, apply.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[github-copilot/claude-sonnet-4.6] Applied: probability display now uses (result.class_probability ?? result.type_probability)?.toFixed(2) ?? 'N/A' to safely handle the case where type_probability is absent.

// Update AI and image manipulation buttons (need file and must be PNG)
Expand Down Expand Up @@ -792,7 +751,7 @@ <h3 class="font-bold text-lg mb-2">Scanner Device Info</h3>
// ========================================
// IMAGE MANIPULATION FUNCTIONS
// ========================================

// modify image by rotating / flipping
async function modifyImage(degrees, flip = false) {
if (!state.aiFile) return;
Expand Down Expand Up @@ -934,8 +893,6 @@ <h3 class="font-bold text-lg mb-2">Scanner Device Info</h3>
state.aiFile = null;
state.requiresServerTransform = false;
state.transformOps = [];
state.aiImageTypeCode = null;
state.aiPatientOrientation = null;
document.getElementById('file-preview').classList.add('hidden');
clearFormFields();
updateSubmitButton();
Expand Down Expand Up @@ -965,8 +922,6 @@ <h3 class="font-bold text-lg mb-2">Scanner Device Info</h3>

state.file = file;
state.transformOps = [];
Comment thread
zgypa marked this conversation as resolved.
state.aiImageTypeCode = null;
state.aiPatientOrientation = null;
if (ext === 'png') {
state.requiresServerTransform = false;
state.previewFile = file;
Expand Down Expand Up @@ -1046,17 +1001,17 @@ <h3 class="font-bold text-lg mb-2">Scanner Device Info</h3>
try {
const result = await queryAI(state.aiFile);

// Map AI type_prediction to record_type select value
// Map AI type_prediction to image_type select value
if (result.type_prediction) {
const mapped = mapAIType(result.type_prediction);
const recordTypeSelect = document.getElementById('record-type');
if (mapped && mapped.recordType) {
recordTypeSelect.value = mapped.recordType;
state.aiImageTypeCode = mapped.imageType;
state.aiPatientOrientation = mapped.patientOrientation;
let id = state.valuesets.image_types.find(x => x.display === result.type_prediction).id;
console.log(id);
Comment thread
aspiringLich marked this conversation as resolved.
Outdated

const imageTypeSelect = document.getElementById('image-type');
if (id) {
imageTypeSelect.value = id;
Comment thread
aspiringLich marked this conversation as resolved.
Outdated
}
}

if (result.flip || result.rotation) {
try {
await modifyImage(result.rotation || 0, result.flip || false);
Expand Down Expand Up @@ -1089,16 +1044,40 @@ <h3 class="font-bold text-lg mb-2">Scanner Device Info</h3>
if (state.previewFile) {
formData.append('thumbnail_preview', state.previewFile, 'thumbnail_preview.png');
}
formData.append('record_type', document.getElementById('record-type').value);
formData.append('modality', document.getElementById('modality').value);

if (state.aiImageTypeCode) {
formData.append('image_type', state.aiImageTypeCode);
}
if (state.aiPatientOrientation && state.aiPatientOrientation.length === 2) {
formData.append('patient_orientation', state.aiPatientOrientation[0]);
formData.append('patient_orientation', state.aiPatientOrientation[1]);
// FIXME: currently this is sorta hardcoded because we only support dental scans rn but this should
// be updated with more logic if we support more types of scans
// Find any modality where x.id == 'RG'

const set_record_type_and_modality = (record_type, modality) => {
console.assert(state.valuesets.record_types.find(x => x.id === record_type), `Expected record type ${record_type} to exist is valueset record_types`);
Comment thread
aspiringLich marked this conversation as resolved.
Outdated
formData.append('record_type', record_type);
console.assert(state.valuesets.modalities.find(x => x.id === modality), `Expected modality ${modality} to exist in valueset modalities`);
formData.append('modality', modality);
console.log(`Record type: ${record_type}, Modality: ${modality}`);
Comment thread
aspiringLich marked this conversation as resolved.
Outdated
};

let image_type = document.getElementById('image-type').value;
switch (image_type) {
// Skeletal X-ray of ankle and foot, Radiographic imaging
case 'FA': set_record_type_and_modality('1597004', 'RG'); break;
// Skeletal X-ray of wrist and hand, Radiographic imaging
case 'H': set_record_type_and_modality('39714003', 'RG'); break;
// Cephalogram, Radiographic imaging
case 'L': set_record_type_and_modality('201456002', 'RG'); break;
// Pelvis X-ray, Radiographic imaging
case 'P': set_record_type_and_modality('268425006', 'RG'); break;
// Dental model, 3D Manufacturing Modeling System
case 'SM': set_record_type_and_modality('302189007', 'M3D'); break;
case 'FM':
case 'F':
throw new Error('Image type has no match in record type codings');
Comment thread
zgypa marked this conversation as resolved.
Outdated
default:
throw new Error(`Unexpected image type ${image_type}. This is a bug.`);

}
formData.append('image_type', image_type);

if (state.requiresServerTransform && state.transformOps.length > 0) {
formData.append('image_transform_ops', JSON.stringify(state.transformOps));
}
Expand Down Expand Up @@ -1154,8 +1133,6 @@ <h3 class="font-bold text-lg mb-2">Scanner Device Info</h3>
state.aiFile = null;
state.requiresServerTransform = false;
state.transformOps = [];
state.aiImageTypeCode = null;
state.aiPatientOrientation = null;
document.getElementById('file-upload').value = '';
document.getElementById('file-preview').classList.add('hidden');
document.getElementById('scan-status').textContent = '';
Expand All @@ -1179,17 +1156,18 @@ <h3 class="font-bold text-lg mb-2">Scanner Device Info</h3>

// Load valuesets
try {
const [recordTypes, modalities] = await Promise.all([
const [image_types, record_types, modalities] = await Promise.all([
fetchValueset('image_types'),
fetchValueset('record_types'),
fetchValueset('modalities')
fetchValueset('modalities'),
]);

state.valuesets.record_types = recordTypes;
state.valuesets.image_types = image_types;
state.valuesets.record_types = record_types;
state.valuesets.modalities = modalities;
console.log(state.valuesets);

populateSelect('record-type', recordTypes);
populateSelect('modality', modalities);
populateSelect('image-type', image_types);
} catch (error) {
console.error('Failed to load valuesets:', error);
alert('Failed to load form options. Please refresh the page.');
Expand All @@ -1215,7 +1193,7 @@ <h3 class="font-bold text-lg mb-2">Scanner Device Info</h3>
});

// Watch form fields for changes
['record-type', 'modality'].forEach(id => {
['image-type'].forEach(id => {
document.getElementById(id).addEventListener('change', updateSubmitButton);
});

Expand Down