[Fix] UI bugs, Registrations Mail, BTRules Popup, Legacy API fixes (#309)

Co-authored-by: Tim Lorsbach <tim@lorsba.ch>
Reviewed-on: enviPath/enviPy#309
This commit is contained in:
2026-01-29 11:13:34 +13:00
parent ab0b5a5186
commit 5565b9cb9e
14 changed files with 391 additions and 154 deletions

View File

@ -1,7 +1,7 @@
import json
import logging
from typing import Any, Dict, List
from datetime import datetime
from typing import Any, Dict, List
import nh3
from django.conf import settings as s
@ -13,6 +13,7 @@ from django.urls import reverse
from django.views.decorators.csrf import csrf_exempt
from envipy_additional_information import NAME_MAPPING
from oauth2_provider.decorators import protected_resource
from sentry_sdk import capture_exception
from utilities.chem import FormatConverter, IndigoUtils
from utilities.decorators import package_permission_required
@ -34,6 +35,7 @@ from .models import (
EnviFormer,
EnzymeLink,
EPModel,
ExpansionSchemeChoice,
ExternalDatabase,
ExternalIdentifier,
Group,
@ -51,7 +53,6 @@ from .models import (
SimpleAmbitRule,
User,
UserPackagePermission,
ExpansionSchemeChoice,
)
logger = logging.getLogger(__name__)
@ -238,6 +239,15 @@ def register(request):
try:
u = UserManager.create_user(username, email, password)
logger.info(f"Created user {u.username} ({u.pk})")
try:
from .tasks import send_registration_mail
send_registration_mail.delay(u.pk)
except Exception as e:
logger.error(f"Failed to send registration mail to {u.email}: {e}")
capture_exception(e)
except Exception:
context["message"] = "Registration failed! Couldn't create User Account."
return render(request, "static/login.html", context)
@ -339,7 +349,7 @@ def breadcrumbs(
{"Package": s.SERVER_URL + "/package"},
]
if first_level_object is not None:
bread.append({first_level_object.name: first_level_object.url})
bread.append({first_level_object.get_name(): first_level_object.url})
if second_level_namespace is not None:
bread.append(
@ -350,7 +360,7 @@ def breadcrumbs(
)
if second_level_object is not None:
bread.append({second_level_object.name: second_level_object.url})
bread.append({second_level_object.get_name(): second_level_object.url})
if third_level_namespace is not None:
bread.append(
@ -361,7 +371,7 @@ def breadcrumbs(
)
if third_level_object is not None:
bread.append({third_level_object.name: third_level_object.url})
bread.append({third_level_object.get_name(): third_level_object.url})
return bread
@ -462,7 +472,7 @@ def package_predict_pathway(request, package_uuid):
current_package = PackageManager.get_package_by_id(current_user, package_uuid)
context = get_base_context(request)
context["title"] = f"enviPath - {current_package.name} - Predict Pathway"
context["title"] = f"enviPath - {current_package.get_name()} - Predict Pathway"
context["meta"]["current_package"] = current_package
return render(request, "predict_pathway.html", context)
@ -475,6 +485,10 @@ def packages(request):
context = get_base_context(request)
context["title"] = "enviPath - Packages"
context["meta"]["current_package"] = context["meta"]["user"].default_package
context["breadcrumbs"] = [
{"Home": s.SERVER_URL},
{"Package": s.SERVER_URL + "/package"},
]
# Context for paginated template
context["entity_type"] = "package"
@ -529,6 +543,10 @@ def compounds(request):
context = get_base_context(request)
context["title"] = "enviPath - Compounds"
context["meta"]["current_package"] = context["meta"]["user"].default_package
context["breadcrumbs"] = [
{"Home": s.SERVER_URL},
{"Compound": s.SERVER_URL + "/compound"},
]
# Context for paginated template
context["entity_type"] = "compound"
@ -759,7 +777,7 @@ def package_models(request, package_uuid):
if request.method == "GET":
context = get_base_context(request)
context["title"] = f"enviPath - {current_package.name} - Models"
context["title"] = f"enviPath - {current_package.get_name()} - Models"
context["meta"]["current_package"] = current_package
context["object_type"] = "model"
@ -781,7 +799,7 @@ def package_models(request, package_uuid):
return JsonResponse(
{
"objects": [
{"name": pw.name, "url": pw.url, "reviewed": current_package.reviewed}
{"name": pw.get_name(), "url": pw.url, "reviewed": current_package.reviewed}
for pw in (
reviewed_model_qs if current_package.reviewed else unreviewed_model_qs
)
@ -931,7 +949,7 @@ def package_model(request, package_uuid, model_uuid):
return JsonResponse(app_domain_assessment, safe=False)
context = get_base_context(request)
context["title"] = f"enviPath - {current_package.name} - {current_model.name}"
context["title"] = f"enviPath - {current_package.get_name()} - {current_model.get_name()}"
context["meta"]["current_package"] = current_package
context["object_type"] = "model"
@ -1009,7 +1027,7 @@ def package(request, package_uuid):
if request.method == "GET":
if request.GET.get("export", False) == "true":
filename = f"{current_package.name.replace(' ', '_')}_{current_package.uuid}.json"
filename = f"{current_package.get_name().replace(' ', '_')}_{current_package.uuid}.json"
pack_json = PackageManager.export_package(
current_package, include_models=False, include_external_identifiers=False
)
@ -1019,7 +1037,7 @@ def package(request, package_uuid):
return response
context = get_base_context(request)
context["title"] = f"enviPath - {current_package.name}"
context["title"] = f"enviPath - {current_package.get_name()}"
context["meta"]["current_package"] = current_package
context["object_type"] = "package"
@ -1056,7 +1074,7 @@ def package(request, package_uuid):
if current_user.default_package == current_package:
return error(
request,
f'Package "{current_package.name}" is the default and cannot be deleted!',
f'Package "{current_package.get_name()}" is the default and cannot be deleted!',
"You cannot delete the default package. If you want to delete this package you have to set another default package first.",
)
@ -1154,7 +1172,7 @@ def package_compounds(request, package_uuid):
if request.method == "GET":
context = get_base_context(request)
context["title"] = f"enviPath - {current_package.name} - Compounds"
context["title"] = f"enviPath - {current_package.get_name()} - Compounds"
context["meta"]["current_package"] = current_package
context["object_type"] = "compound"
@ -1179,7 +1197,7 @@ def package_compounds(request, package_uuid):
return JsonResponse(
{
"objects": [
{"name": pw.name, "url": pw.url, "reviewed": current_package.reviewed}
{"name": pw.get_name(), "url": pw.url, "reviewed": current_package.reviewed}
for pw in (
reviewed_compound_qs
if current_package.reviewed
@ -1216,7 +1234,9 @@ def package_compound(request, package_uuid, compound_uuid):
if request.method == "GET":
context = get_base_context(request)
context["title"] = f"enviPath - {current_package.name} - {current_compound.name}"
context["title"] = (
f"enviPath - {current_package.get_name()} - {current_compound.get_name()}"
)
context["meta"]["current_package"] = current_package
context["object_type"] = "compound"
@ -1300,7 +1320,7 @@ def package_compound_structures(request, package_uuid, compound_uuid):
if request.method == "GET":
context = get_base_context(request)
context["title"] = (
f"enviPath - {current_package.name} - {current_compound.name} - Structures"
f"enviPath - {current_package.get_name()} - {current_compound.get_name()} - Structures"
)
context["meta"]["current_package"] = current_package
@ -1309,7 +1329,7 @@ def package_compound_structures(request, package_uuid, compound_uuid):
current_package, "compound", current_compound, "structure"
)
context["entity_type"] = "structure"
context["page_title"] = f"{current_compound.name} - Structures"
context["page_title"] = f"{current_compound.get_name()} - Structures"
context["api_endpoint"] = (
f"/api/v1/package/{current_package.uuid}/compound/{current_compound.uuid}/structure/"
)
@ -1362,7 +1382,7 @@ def package_compound_structure(request, package_uuid, compound_uuid, structure_u
context = get_base_context(request)
context["title"] = (
f"enviPath - {current_package.name} - {current_compound.name} - {current_structure.name}"
f"enviPath - {current_package.get_name()} - {current_compound.get_name()} - {current_structure.get_name()}"
)
context["meta"]["current_package"] = current_package
@ -1468,7 +1488,7 @@ def package_rules(request, package_uuid):
if request.method == "GET":
context = get_base_context(request)
context["title"] = f"enviPath - {current_package.name} - Rules"
context["title"] = f"enviPath - {current_package.get_name()} - Rules"
context["meta"]["current_package"] = current_package
context["object_type"] = "rule"
@ -1490,7 +1510,7 @@ def package_rules(request, package_uuid):
return JsonResponse(
{
"objects": [
{"name": pw.name, "url": pw.url, "reviewed": current_package.reviewed}
{"name": pw.get_name(), "url": pw.url, "reviewed": current_package.reviewed}
for pw in (
reviewed_rule_qs if current_package.reviewed else unreviewed_rule_qs
)
@ -1580,7 +1600,7 @@ def package_rule(request, package_uuid, rule_uuid):
content_type="image/svg+xml",
)
context["title"] = f"enviPath - {current_package.name} - {current_rule.name}"
context["title"] = f"enviPath - {current_package.get_name()} - {current_rule.get_name()}"
context["meta"]["current_package"] = current_package
context["object_type"] = "rule"
@ -1653,7 +1673,7 @@ def package_rule_enzymelink(request, package_uuid, rule_uuid, enzymelink_uuid):
if request.method == "GET":
context = get_base_context(request)
context["title"] = f"enviPath - {current_package.name} - {current_rule.name}"
context["title"] = f"enviPath - {current_package.get_name()} - {current_rule.get_name()}"
context["meta"]["current_package"] = current_package
context["object_type"] = "enzyme"
@ -1676,7 +1696,7 @@ def package_reactions(request, package_uuid):
if request.method == "GET":
context = get_base_context(request)
context["title"] = f"enviPath - {current_package.name} - Reactions"
context["title"] = f"enviPath - {current_package.get_name()} - Reactions"
context["meta"]["current_package"] = current_package
context["object_type"] = "reaction"
@ -1700,7 +1720,7 @@ def package_reactions(request, package_uuid):
return JsonResponse(
{
"objects": [
{"name": pw.name, "url": pw.url, "reviewed": current_package.reviewed}
{"name": pw.get_name(), "url": pw.url, "reviewed": current_package.reviewed}
for pw in (
reviewed_reaction_qs
if current_package.reviewed
@ -1741,7 +1761,9 @@ def package_reaction(request, package_uuid, reaction_uuid):
if request.method == "GET":
context = get_base_context(request)
context["title"] = f"enviPath - {current_package.name} - {current_reaction.name}"
context["title"] = (
f"enviPath - {current_package.get_name()} - {current_reaction.get_name()}"
)
context["meta"]["current_package"] = current_package
context["object_type"] = "reaction"
@ -1824,7 +1846,7 @@ def package_pathways(request, package_uuid):
if request.method == "GET":
context = get_base_context(request)
context["title"] = f"enviPath - {current_package.name} - Pathways"
context["title"] = f"enviPath - {current_package.get_name()} - Pathways"
context["meta"]["current_package"] = current_package
context["object_type"] = "pathway"
@ -1846,7 +1868,7 @@ def package_pathways(request, package_uuid):
return JsonResponse(
{
"objects": [
{"name": pw.name, "url": pw.url, "reviewed": current_package.reviewed}
{"name": pw.get_name(), "url": pw.url, "reviewed": current_package.reviewed}
for pw in (
reviewed_pathway_qs
if current_package.reviewed
@ -1953,7 +1975,7 @@ def package_pathway(request, package_uuid, pathway_uuid):
)
if request.GET.get("download", False) == "true":
filename = f"{current_pathway.name.replace(' ', '_')}_{current_pathway.uuid}.csv"
filename = f"{current_pathway.get_name().replace(' ', '_')}_{current_pathway.uuid}.csv"
csv_pw = current_pathway.to_csv()
response = HttpResponse(csv_pw, content_type="text/csv")
response["Content-Disposition"] = f'attachment; filename="{filename}"'
@ -1973,7 +1995,7 @@ def package_pathway(request, package_uuid, pathway_uuid):
current_user, identify_missing_rules, [current_pathway.pk], rule_package.pk
)
filename = f"{current_pathway.name.replace(' ', '_')}_{current_pathway.uuid}.csv"
filename = f"{current_pathway.get_name().replace(' ', '_')}_{current_pathway.uuid}.csv"
response = HttpResponse(res, content_type="text/csv")
response["Content-Disposition"] = f'attachment; filename="{filename}"'
@ -1996,7 +2018,7 @@ def package_pathway(request, package_uuid, pathway_uuid):
).get(uuid=pathway_uuid)
context = get_base_context(request)
context["title"] = f"enviPath - {current_package.name} - {current_pathway.name}"
context["title"] = f"enviPath - {current_package.get_name()} - {current_pathway.get_name()}"
context["meta"]["current_package"] = current_package
context["object_type"] = "pathway"
@ -2008,9 +2030,9 @@ def package_pathway(request, package_uuid, pathway_uuid):
context["breadcrumbs"] = [
{"Home": s.SERVER_URL},
{"Package": s.SERVER_URL + "/package"},
{current_package.name: current_package.url},
{current_package.get_name(): current_package.url},
{"Pathway": current_package.url + "/pathway"},
{current_pathway.name: current_pathway.url},
{current_pathway.get_name(): current_pathway.url},
]
return render(request, "objects/pathway.html", context)
@ -2097,16 +2119,18 @@ def package_pathway_nodes(request, package_uuid, pathway_uuid):
if request.method == "GET":
context = get_base_context(request)
context["title"] = f"enviPath - {current_package.name} - {current_pathway.name} - Nodes"
context["title"] = (
f"enviPath - {current_package.get_name()} - {current_pathway.get_name()} - Nodes"
)
context["meta"]["current_package"] = current_package
context["object_type"] = "node"
context["breadcrumbs"] = [
{"Home": s.SERVER_URL},
{"Package": s.SERVER_URL + "/package"},
{current_package.name: current_package.url},
{current_package.get_name(): current_package.url},
{"Pathway": current_package.url + "/pathway"},
{current_pathway.name: current_pathway.url},
{current_pathway.get_name(): current_pathway.url},
{"Node": current_pathway.url + "/node"},
]
@ -2122,7 +2146,7 @@ def package_pathway_nodes(request, package_uuid, pathway_uuid):
return JsonResponse(
{
"objects": [
{"name": pw.name, "url": pw.url, "reviewed": current_package.reviewed}
{"name": pw.get_name(), "url": pw.url, "reviewed": current_package.reviewed}
for pw in (
reviewed_node_qs if current_package.reviewed else unreviewed_node_qs
)
@ -2196,7 +2220,7 @@ def package_pathway_node(request, package_uuid, pathway_uuid, node_uuid):
return HttpResponse(svg_data, content_type="image/svg+xml")
context = get_base_context(request)
context["title"] = f"enviPath - {current_package.name} - {current_pathway.name}"
context["title"] = f"enviPath - {current_package.get_name()} - {current_pathway.get_name()}"
context["meta"]["current_package"] = current_package
context["object_type"] = "pathway"
@ -2204,11 +2228,11 @@ def package_pathway_node(request, package_uuid, pathway_uuid, node_uuid):
context["breadcrumbs"] = [
{"Home": s.SERVER_URL},
{"Package": s.SERVER_URL + "/package"},
{current_package.name: current_package.url},
{current_package.get_name(): current_package.url},
{"Pathway": current_package.url + "/pathway"},
{current_pathway.name: current_pathway.url},
{current_pathway.get_name(): current_pathway.url},
{"Node": current_pathway.url + "/node"},
{current_node.name: current_node.url},
{current_node.get_name(): current_node.url},
]
context["node"] = current_node
@ -2261,16 +2285,18 @@ def package_pathway_edges(request, package_uuid, pathway_uuid):
if request.method == "GET":
context = get_base_context(request)
context["title"] = f"enviPath - {current_package.name} - {current_pathway.name} - Edges"
context["title"] = (
f"enviPath - {current_package.get_name()} - {current_pathway.get_name()} - Edges"
)
context["meta"]["current_package"] = current_package
context["object_type"] = "edge"
context["breadcrumbs"] = [
{"Home": s.SERVER_URL},
{"Package": s.SERVER_URL + "/package"},
{current_package.name: current_package.url},
{current_package.get_name(): current_package.url},
{"Pathway": current_package.url + "/pathway"},
{current_pathway.name: current_pathway.url},
{current_pathway.get_name(): current_pathway.url},
{"Edge": current_pathway.url + "/edge"},
]
@ -2286,7 +2312,7 @@ def package_pathway_edges(request, package_uuid, pathway_uuid):
return JsonResponse(
{
"objects": [
{"name": pw.name, "url": pw.url, "reviewed": current_package.reviewed}
{"name": pw.get_name(), "url": pw.url, "reviewed": current_package.reviewed}
for pw in (
reviewed_edge_qs if current_package.reviewed else unreviewed_edge_qs
)
@ -2338,7 +2364,7 @@ def package_pathway_edge(request, package_uuid, pathway_uuid, edge_uuid):
context = get_base_context(request)
context["title"] = (
f"enviPath - {current_package.name} - {current_pathway.name} - {current_edge.edge_label.name}"
f"enviPath - {current_package.get_name()} - {current_pathway.get_name()} - {current_edge.edge_label.get_name()}"
)
context["meta"]["current_package"] = current_package
@ -2391,11 +2417,11 @@ def package_scenarios(request, package_uuid):
"all", False
):
scens = Scenario.objects.filter(package=current_package).order_by("name")
res = [{"name": s_.name, "url": s_.url, "uuid": s_.uuid} for s_ in scens]
res = [{"name": s_.get_name(), "url": s_.url, "uuid": s_.uuid} for s_ in scens]
return JsonResponse(res, safe=False)
context = get_base_context(request)
context["title"] = f"enviPath - {current_package.name} - Scenarios"
context["title"] = f"enviPath - {current_package.get_name()} - Scenarios"
context["meta"]["current_package"] = current_package
context["object_type"] = "scenario"
@ -2419,7 +2445,7 @@ def package_scenarios(request, package_uuid):
return JsonResponse(
{
"objects": [
{"name": pw.name, "url": pw.url, "reviewed": current_package.reviewed}
{"name": pw.get_name(), "url": pw.url, "reviewed": current_package.reviewed}
for pw in (
reviewed_scenario_qs
if current_package.reviewed
@ -2511,7 +2537,9 @@ def package_scenario(request, package_uuid, scenario_uuid):
if request.method == "GET":
context = get_base_context(request)
context["title"] = f"enviPath - {current_package.name} - {current_scenario.name}"
context["title"] = (
f"enviPath - {current_package.get_name()} - {current_scenario.get_name()}"
)
context["meta"]["current_package"] = current_package
context["object_type"] = "scenario"
@ -2748,13 +2776,13 @@ def group(request, group_uuid):
if request.method == "GET":
context = get_base_context(request)
context["title"] = f"enviPath - {current_group.name}"
context["title"] = f"enviPath - {current_group.get_name()}"
context["object_type"] = "group"
context["breadcrumbs"] = [
{"Home": s.SERVER_URL},
{"Group": s.SERVER_URL + "/group"},
{current_group.name: current_group.url},
{current_group.get_name(): current_group.url},
]
context["group"] = current_group
@ -2909,13 +2937,13 @@ def setting(request, setting_uuid):
if request.method == "GET":
context = get_base_context(request)
context["title"] = f"enviPath - {current_setting.name}"
context["title"] = f"enviPath - {current_setting.get_name()}"
context["object_type"] = "setting"
context["breadcrumbs"] = [
{"Home": s.SERVER_URL},
{"Setting": s.SERVER_URL + "/setting"},
{f"{current_setting.name}": current_setting.url},
{f"{current_setting.get_name()}": current_setting.url},
]
context["setting"] = current_setting
@ -2964,8 +2992,8 @@ def jobs(request):
target_package = PackageManager.create_package(
current_user,
f"Autogenerated Package for Pathway Engineering of {pathway_to_engineer.name}",
f"This Package was generated automatically for the engineering Task of {pathway_to_engineer.name}.",
f"Autogenerated Package for Pathway Engineering of {pathway_to_engineer.get_name()}",
f"This Package was generated automatically for the engineering Task of {pathway_to_engineer.get_name()}.",
)
from .tasks import dispatch, engineer_pathways
@ -3019,7 +3047,7 @@ def jobs(request):
"This Package was generated automatically for the batch prediction task.",
)
from .tasks import dispatch, batch_predict
from .tasks import batch_predict, dispatch
res = dispatch(
current_user,
@ -3057,6 +3085,8 @@ def job(request, job_uuid):
if job.job_name == "batch_predict":
filename = f"{job.job_name.replace(' ', '_')}_{job.task_id}.csv"
elif job.job_name == "identify_missing_rules":
filename = f"{job.job_name.replace(' ', '_')}_{job.task_id}.csv"
else:
raise BadRequest("Result is not downloadable!")