[Feature] Biotransformer in enviPath (#364)

Co-authored-by: Tim Lorsbach <tim@lorsba.ch>
Reviewed-on: enviPath/enviPy#364
This commit is contained in:
2026-04-10 00:00:13 +12:00
parent 5029a8cda5
commit 964574c700
13 changed files with 793 additions and 56 deletions

View File

@ -1,33 +1,82 @@
{% load static %}
<dialog
id="new_model_modal"
class="modal"
x-data="{
isSubmitting: false,
modelType: '',
selectedType: '',
buildAppDomain: false,
requiresRulePackages: false,
requiresDataPackages: false,
additional_parameters: null,
schemas: {},
formRenderKey: 0, // Counter to force form re-render
formData: null, // Store reference to form data
async init() {
// Watch for selectedType changes
this.$watch('selectedType', (value) => {
// Reset formData when type changes and increment key to force re-render
this.formData = null;
this.formRenderKey++;
// Clear previous errors
this.error = null;
Alpine.store('validationErrors').clearErrors(); // No context - clears all
const select = this.$refs.typeSelect;
const selectedOption = select.options[select.selectedIndex];
this.requiresRulePackages = selectedOption.dataset.requires_rule_packages === 'True';
this.requiresDataPackages = selectedOption.dataset.requires_data_packages === 'True';
this.additional_parameters = selectedOption.dataset.additional_parameters;
console.log(this.selectedType);
console.log(this.schemas[this.additional_parameters]);
});
// Load schemas and existing items
try {
this.loadingSchemas = true;
const [schemasRes] = await Promise.all([
fetch('/api/v1/information/schema/'),
]);
if (!schemasRes.ok) throw new Error('Failed to load schemas');
this.schemas = await schemasRes.json();
} catch (err) {
this.error = err.message;
} finally {
this.loadingSchemas = false;
}
},
reset() {
this.isSubmitting = false;
this.modelType = '';
this.selectedType = '';
this.buildAppDomain = false;
this.requiresRulePackages = false;
this.requiresDataPackages = false;
this.additional_parameters = null;
},
setFormData(data) {
this.formData = data;
},
get showMlrr() {
return this.modelType === 'mlrr';
return this.selectedType === 'mlrr';
},
get showRbrr() {
return this.modelType === 'rbrr';
return this.selectedType === 'rbrr';
},
get showEnviformer() {
return this.modelType === 'enviformer';
return this.selectedType === 'enviformer';
},
get showRulePackages() {
console.log(this.requiresRulePackages);
return this.requiresRulePackages;
},
@ -35,14 +84,25 @@
return this.requiresDataPackages;
},
updateRequirements(event) {
const option = event.target.selectedOptions[0];
this.requiresRulePackages = option.dataset.requires_rule_packages === 'True';
this.requiresDataPackages = option.dataset.requires_data_packages === 'True';
},
submit(formId) {
const form = document.getElementById(formId);
// Remove previously injected inputs
form.querySelectorAll('.dynamic-param').forEach(el => el.remove());
// Add values from dynamic form into the html form
if (this.formData) {
Object.entries(this.formData).forEach(([key, value]) => {
const input = document.createElement('input');
input.type = 'hidden';
input.name = key;
input.value = value;
input.classList.add('dynamic-param');
form.appendChild(input);
});
}
if (form && form.checkValidity()) {
this.isSubmitting = true;
form.submit();
@ -52,6 +112,7 @@
}
}"
@close="reset()"
@form-data-ready="formData = $event.detail"
>
<div class="modal-box max-w-3xl">
<!-- Header -->
@ -127,8 +188,8 @@
id="model-type"
name="model-type"
class="select select-bordered w-full"
x-model="modelType"
x-on:change="updateRequirements($event)"
x-model="selectedType"
x-ref="typeSelect"
required
>
<option value="" disabled selected>Select Model Type</option>
@ -137,6 +198,7 @@
value="{{ v.type }}"
data-requires_rule_packages="{{ v.requires_rule_packages }}"
data-requires_data_packages="{{ v.requires_data_packages }}"
data-additional_parameters="{{ v.additional_parameters }}"
>
{{ k }}
</option>
@ -252,6 +314,23 @@
/>
</div>
<template x-if="!loadingSchemas">
<template x-for="renderKey in [formRenderKey]" :key="renderKey">
<div x-show="selectedType && schemas[additional_parameters]">
<div
x-data="schemaRenderer({
rjsf: schemas[additional_parameters],
mode: 'edit'
// No context - single form, backward compatible
})"
x-init="await init(); $dispatch('form-data-ready', data)"
>
{% include "components/schema_form.html" %}
</div>
</div>
</template>
</template>
<!-- Applicability Domain (MLRR) -->
{% if meta.enabled_features.APPLICABILITY_DOMAIN %}
<div x-show="showMlrr" x-cloak>
@ -338,7 +417,7 @@
type="button"
class="btn btn-primary"
@click="submit('new_model_form')"
:disabled="isSubmitting"
:disabled="isSubmitting || !selectedType || loadingSchemas"
>
<span x-show="!isSubmitting">Submit</span>
<span