[Fix] Collection of minor Bugfixes (#417)

User feedback from on-prem installation

Co-authored-by: Tim Lorsbach <tim@lorsba.ch>
Reviewed-on: enviPath/enviPy#417
This commit is contained in:
2026-06-17 07:56:32 +12:00
parent 6ab9180291
commit 2502c020f7
18 changed files with 138 additions and 82 deletions

View File

@ -254,7 +254,14 @@ class Classifier(Plugin):
def parse_config(cls, data: dict | None = None) -> EnviPyModel | None: def parse_config(cls, data: dict | None = None) -> EnviPyModel | None:
if cls.Config is None: if cls.Config is None:
return None return None
return cls.Config(**(data or {}))
# remove empty strings a.k.a unset params to not overwrite defaults
cpy = {}
if data is not None:
for k, v in data.items():
if v != "":
cpy[k] = v
return cls.Config(**cpy)
@classmethod @classmethod
def create(cls, data: dict | None = None): def create(cls, data: dict | None = None):

View File

@ -2335,8 +2335,8 @@ class Node(EnviPathModel, AliasMixin, ScenarioMixin, AdditionalInformationMixin)
def get_timeseries_data(self): def get_timeseries_data(self):
for ai in self.additional_information.all(): for ai in self.additional_information.all():
if ai.__class__.__name__ == "OECD301FTimeSeries": if ai.type == "OECD301FTimeSeries":
return ai.model_dump(mode="json") return ai.get().model_dump(mode="json")
return None return None

File diff suppressed because one or more lines are too long

View File

@ -65,6 +65,24 @@ def run_both_engines(SMILES, SMIRKS):
def migration(request): def migration(request):
accepted_diffs = [
"bt0055-3469.1",
"bt0056-2685",
"bt0193-4263",
"bt0231-1871.1",
"bt0242-3803",
"bt0254-4224.1",
"bt0254-4224.2",
"bt0291-1129",
"bt0337-3543",
"bt0391-4285",
"bt0402-3576",
"bt0404-3928",
"bt0416-4269",
"bt0432-4254",
"bt0337-4117",
]
if request.method == "GET": if request.method == "GET":
context = get_base_context(request) context = get_base_context(request)
@ -109,11 +127,11 @@ def migration(request):
), ),
"id": str(r.uuid), "id": str(r.uuid),
"url": r.url, "url": r.url,
"status": res, "status": res or r.name in accepted_diffs,
} }
) )
if res: if res or r.name in accepted_diffs:
success += 1 success += 1
else: else:
error += 1 error += 1
@ -135,7 +153,16 @@ def migration(request):
for r in migration_status["results"]: for r in migration_status["results"]:
r["detail_url"] = r["detail_url"].replace("http://localhost:8000", s.SERVER_URL) r["detail_url"] = r["detail_url"].replace("http://localhost:8000", s.SERVER_URL)
if r["name"] in accepted_diffs:
r["status"] = True
migration_status["results"] = sorted(
migration_status["results"], key=lambda x: (x["status"], x["name"])
)
num_success = sum([int(x["status"]) for x in migration_status["results"]])
migration_status["success"] = num_success
migration_status["error"] = migration_status["total"] - num_success
context.update(**migration_status) context.update(**migration_status)
return render(request, "migration.html", context) return render(request, "migration.html", context)

View File

@ -59,6 +59,9 @@ document.addEventListener("alpine:init", () => {
get isEditMode() { get isEditMode() {
return this.mode === "edit"; return this.mode === "edit";
}, },
get isRequired() {
return (this.schema.required || []).indexOf(this.fieldName) > -1
}
}); });
// Text widget // Text widget

View File

@ -23,6 +23,7 @@ function predictFromNode(url) {
// elem = 'vizdiv' // elem = 'vizdiv'
function draw(pathway, elem) { function draw(pathway, elem) {
const initialzoom = 2.5
const nodeRadius = 20; const nodeRadius = 20;
const linkDistance = 100; const linkDistance = 100;
const chargeStrength = -200; const chargeStrength = -200;
@ -63,7 +64,7 @@ function draw(pathway, elem) {
node.fx = width / 2 + depthMap.get(node.depth) * horizontalSpacing - ((nodesInLevel - 1) * horizontalSpacing) / 2; node.fx = width / 2 + depthMap.get(node.depth) * horizontalSpacing - ((nodesInLevel - 1) * horizontalSpacing) / 2;
} }
node.fy = node.depth * levelSpacing + 50; node.fy = (node.depth + initialzoom + 0.5) * levelSpacing + 50;
depthMap.set(node.depth, depthMap.get(node.depth) + 1); depthMap.set(node.depth, depthMap.get(node.depth) + 1);
}); });
} }
@ -577,6 +578,8 @@ function draw(pathway, elem) {
// Apply zoom to the SVG element - this enables wheel zoom // Apply zoom to the SVG element - this enables wheel zoom
svg.call(zoom); svg.call(zoom);
svg.call(zoom.scaleBy, initialzoom);
// Also apply zoom to container to catch events that might not reach SVG // Also apply zoom to container to catch events that might not reach SVG
// This ensures drag-to-pan works even when clicking on empty space // This ensures drag-to-pan works even when clicking on empty space
container.call(zoom); container.call(zoom);

View File

@ -5,7 +5,7 @@
{% block action_button %} {% block action_button %}
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
{% if meta.can_edit %} {% if meta.can_edit or not meta.url_contains_package %}
<button <button
type="button" type="button"
class="btn btn-primary btn-sm" class="btn btn-primary btn-sm"

View File

@ -3,14 +3,16 @@
{% block page_title %}Models{% endblock %} {% block page_title %}Models{% endblock %}
{% block action_button %} {% block action_button %}
{% if meta.can_edit and meta.enabled_features.MODEL_BUILDING %} {% if meta.can_edit or not meta.url_contains_package %}
<button {% if meta.enabled_features.MODEL_BUILDING %}
type="button" <button
class="btn btn-primary btn-sm" type="button"
onclick="document.getElementById('new_model_modal').showModal(); return false;" class="btn btn-primary btn-sm"
> onclick="document.getElementById('new_model_modal').showModal(); return false;"
New Model >
</button> New Model
</button>
{% endif %}
{% endif %} {% endif %}
{% endblock action_button %} {% endblock action_button %}

View File

@ -3,75 +3,73 @@
{% block page_title %}Packages{% endblock %} {% block page_title %}Packages{% endblock %}
{% block action_button %} {% block action_button %}
{% if meta.can_edit %} <div class="flex items-center gap-2">
<div class="flex items-center gap-2"> <button
<button type="button"
type="button" class="btn btn-primary btn-sm"
class="btn btn-primary btn-sm" id="new-package-button"
id="new-package-button" onclick="document.getElementById('new_package_modal').showModal(); return false;"
onclick="document.getElementById('new_package_modal').showModal(); return false;" >
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-folder-plus-icon lucide-folder-plus"
> >
<path d="M12 10v6" />
<path d="M9 13h6" />
<path
d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z"
/>
</svg>
</button>
<div class="dropdown dropdown-end">
<div tabindex="0" role="button" class="btn btn-sm">
Import
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
width="24" width="16"
height="24" height="16"
viewBox="0 0 24 24" viewBox="0 0 24 24"
fill="none" fill="none"
stroke="currentColor" stroke="currentColor"
stroke-width="2" stroke-width="2"
stroke-linecap="round" stroke-linecap="round"
stroke-linejoin="round" stroke-linejoin="round"
class="lucide lucide-folder-plus-icon lucide-folder-plus" class="lucide lucide-chevron-down ml-1"
> >
<path d="M12 10v6" /> <path d="m6 9 6 6 6-6" />
<path d="M9 13h6" />
<path
d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z"
/>
</svg> </svg>
</button>
<div class="dropdown dropdown-end">
<div tabindex="0" role="button" class="btn btn-sm">
Import
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-chevron-down ml-1"
>
<path d="m6 9 6 6 6-6" />
</svg>
</div>
<ul
tabindex="-1"
class="dropdown-content menu bg-base-100 rounded-box z-50 w-56 p-2"
>
<li>
<a
role="button"
onclick="document.getElementById('import_package_modal').showModal(); return false;"
>
Import Package from JSON
</a>
</li>
<li>
<a
role="button"
onclick="document.getElementById('import_legacy_package_modal').showModal(); return false;"
>
Import Package from legacy JSON
</a>
</li>
</ul>
</div> </div>
<ul
tabindex="-1"
class="dropdown-content menu bg-base-100 rounded-box z-50 w-56 p-2"
>
<li>
<a
role="button"
onclick="document.getElementById('import_package_modal').showModal(); return false;"
>
Import Package from JSON
</a>
</li>
<li>
<a
role="button"
onclick="document.getElementById('import_legacy_package_modal').showModal(); return false;"
>
Import Package from legacy JSON
</a>
</li>
</ul>
</div> </div>
{% endif %} </div>
{% endblock action_button %} {% endblock action_button %}
{% block action_modals %} {% block action_modals %}

View File

@ -3,7 +3,7 @@
{% block page_title %}Pathways{% endblock %} {% block page_title %}Pathways{% endblock %}
{% block action_button %} {% block action_button %}
{% if meta.can_edit %} {% if meta.can_edit or not meta.url_contains_package %}
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<a <a
class="btn btn-primary btn-sm" class="btn btn-primary btn-sm"

View File

@ -3,7 +3,7 @@
{% block page_title %}Reactions{% endblock %} {% block page_title %}Reactions{% endblock %}
{% block action_button %} {% block action_button %}
{% if meta.can_edit %} {% if meta.can_edit or not meta.url_contains_package %}
<button <button
type="button" type="button"
class="btn btn-primary btn-sm" class="btn btn-primary btn-sm"

View File

@ -3,7 +3,7 @@
{% block page_title %}Rules{% endblock %} {% block page_title %}Rules{% endblock %}
{% block action_button %} {% block action_button %}
{% if meta.can_edit %} {% if meta.can_edit or not meta.url_contains_package %}
<button <button
type="button" type="button"
class="btn btn-primary btn-sm" class="btn btn-primary btn-sm"

View File

@ -11,7 +11,7 @@
{% endblock action_modals %} {% endblock action_modals %}
{% block action_button %} {% block action_button %}
{% if meta.can_edit %} {% if meta.can_edit or not meta.url_contains_package %}
<button <button
type="button" type="button"
class="btn btn-primary btn-sm" class="btn btn-primary btn-sm"

View File

@ -3,7 +3,7 @@
{% block page_title %}{{ page_title|default:"Structures" }}{% endblock %} {% block page_title %}{{ page_title|default:"Structures" }}{% endblock %}
{% block action_button %} {% block action_button %}
{% if meta.can_edit %} {% if meta.can_edit or not meta.url_contains_package %}
<button <button
type="button" type="button"
class="btn btn-primary btn-sm" class="btn btn-primary btn-sm"

View File

@ -44,6 +44,7 @@
:class="{ 'select-error': $store.validationErrors.hasError(fieldName, context) }" :class="{ 'select-error': $store.validationErrors.hasError(fieldName, context) }"
x-model="value" x-model="value"
:multiple="multiple" :multiple="multiple"
:required="isRequired"
> >
<option value="" :selected="!value">Select...</option> <option value="" :selected="!value">Select...</option>

View File

@ -65,11 +65,11 @@
}, },
get showMlrr() { get showMlrr() {
return this.selectedType === 'mlrr'; return this.selectedType === 'ml-relative-reasoning';
}, },
get showRbrr() { get showRbrr() {
return this.selectedType === 'rbrr'; return this.selectedType === 'rule-based-relative-reasoning';
}, },
get showEnviformer() { get showEnviformer() {

View File

@ -203,11 +203,11 @@
id="model-based-prediction-setting-threshold" id="model-based-prediction-setting-threshold"
name="model-based-prediction-setting-threshold" name="model-based-prediction-setting-threshold"
class="input input-bordered w-full" class="input input-bordered w-full"
placeholder="0.25" value="0.25"
type="number" type="number"
min="0" min="0"
max="1" max="1"
step="0.05" step="any"
/> />
</div> </div>

View File

@ -105,7 +105,7 @@
></iframe> ></iframe>
</div> </div>
<label class="select mb-8 w-full"> <label class="select mb-8 w-full" id="prediction-setting-label">
<span class="label">Predictor</span> <span class="label">Predictor</span>
<select id="prediction-setting" name="prediction-setting"> <select id="prediction-setting" name="prediction-setting">
<option disabled>Select a Setting</option> <option disabled>Select a Setting</option>
@ -148,6 +148,21 @@
</div> </div>
{# prettier-ignore-start #} {# prettier-ignore-start #}
<script> <script>
// Hide predictor selection and update button text if mode is "build"
function radioChange(event) {
if (event.target.value === "build") {
document.getElementById("prediction-setting-label").hidden = true;
document.getElementById("predict-submit-button").innerText = "Build";
} else {
document.getElementById("prediction-setting-label").hidden = false;
document.getElementById("predict-submit-button").innerText = "Predict";
}
}
const radioButtons = document.querySelectorAll('input[name="predict"]');
radioButtons.forEach(radio => {
radio.addEventListener('change', radioChange);
});
// Helper function to safely get Ketcher instance from iframe // Helper function to safely get Ketcher instance from iframe
function getKetcherInstance(iframeId) { function getKetcherInstance(iframeId) {
const ketcherFrame = document.getElementById(iframeId); const ketcherFrame = document.getElementById(iframeId);