[Feature] Dynamic additional information rendering in frontend (#282)

This implements a version of #274, relying on Pydantics built in JSON schema and JSON rendering.
Requires additional UI tagging in the ai model repo but will remove HTML tags.

Example scenario with filled information: 5882df9c-dae1-4d80-a40e-db4724271456/scenario/3a4d395a-6a6d-4154-8ce3-ced667fceec0

Reviewed-on: enviPath/enviPy#282
Co-authored-by: Tobias O <tobias.olenyi@envipath.com>
Co-committed-by: Tobias O <tobias.olenyi@envipath.com>
This commit is contained in:
2026-01-31 00:44:03 +13:00
committed by jebus
parent 9f63a9d4de
commit d80dfb5ee3
42 changed files with 3732 additions and 609 deletions

View File

@ -1,12 +1,12 @@
from django.db.models import Model
from epdb.logic import PackageManager
from epdb.models import CompoundStructure, User, Package, Compound
from epdb.models import CompoundStructure, User, Package, Compound, Scenario
from uuid import UUID
from .errors import EPAPINotFoundError, EPAPIPermissionDeniedError
def get_compound_or_error(user, compound_uuid: UUID):
def get_compound_for_read(user, compound_uuid: UUID):
"""
Get compound by UUID with permission check.
"""
@ -23,7 +23,7 @@ def get_compound_or_error(user, compound_uuid: UUID):
return compound
def get_package_or_error(user, package_uuid: UUID):
def get_package_for_read(user, package_uuid: UUID):
"""
Get package by UUID with permission check.
"""
@ -41,14 +41,40 @@ def get_package_or_error(user, package_uuid: UUID):
return package
def get_user_packages_qs(user: User | None):
def get_scenario_for_read(user, scenario_uuid: UUID):
"""Get scenario by UUID with read permission check."""
try:
scenario = Scenario.objects.select_related("package").get(uuid=scenario_uuid)
except Scenario.DoesNotExist:
raise EPAPINotFoundError(f"Scenario with UUID {scenario_uuid} not found")
if not user or user.is_anonymous or not PackageManager.readable(user, scenario.package):
raise EPAPIPermissionDeniedError("Insufficient permissions to access this scenario.")
return scenario
def get_scenario_for_write(user, scenario_uuid: UUID):
"""Get scenario by UUID with write permission check."""
try:
scenario = Scenario.objects.select_related("package").get(uuid=scenario_uuid)
except Scenario.DoesNotExist:
raise EPAPINotFoundError(f"Scenario with UUID {scenario_uuid} not found")
if not user or user.is_anonymous or not PackageManager.writable(user, scenario.package):
raise EPAPIPermissionDeniedError("Insufficient permissions to modify this scenario.")
return scenario
def get_user_packages_for_read(user: User | None):
"""Get all packages readable by the user."""
if not user or user.is_anonymous:
return PackageManager.get_reviewed_packages()
return PackageManager.get_all_readable_packages(user, include_reviewed=True)
def get_user_entities_qs(model_class: Model, user: User | None):
def get_user_entities_for_read(model_class: Model, user: User | None):
"""Build queryset for reviewed package entities."""
if not user or user.is_anonymous:
@ -60,16 +86,14 @@ def get_user_entities_qs(model_class: Model, user: User | None):
return qs
def get_package_scoped_entities_qs(
model_class: Model, package_uuid: UUID, user: User | None = None
):
def get_package_entities_for_read(model_class: Model, package_uuid: UUID, user: User | None = None):
"""Build queryset for specific package entities."""
package = get_package_or_error(user, package_uuid)
package = get_package_for_read(user, package_uuid)
qs = model_class.objects.filter(package=package).select_related("package")
return qs
def get_user_structures_qs(user: User | None):
def get_user_structure_for_read(user: User | None):
"""Build queryset for structures accessible to the user (via compound->package)."""
if not user or user.is_anonymous:
@ -83,13 +107,13 @@ def get_user_structures_qs(user: User | None):
return qs
def get_package_compound_scoped_structure_qs(
def get_package_compound_structure_for_read(
package_uuid: UUID, compound_uuid: UUID, user: User | None = None
):
"""Build queryset for specific package compound structures."""
get_package_or_error(user, package_uuid)
compound = get_compound_or_error(user, compound_uuid)
get_package_for_read(user, package_uuid)
compound = get_compound_for_read(user, compound_uuid)
qs = CompoundStructure.objects.filter(compound=compound).select_related("compound__package")
return qs