Files
Axia4/django_app/aulatek/views.py
2026-03-16 22:51:13 +00:00

764 lines
30 KiB
Python

import re
import mimetypes
from datetime import date
from pathlib import Path
from urllib.parse import urlencode
from django.contrib import messages
from django.db.models import Q
from django.http import FileResponse, Http404, HttpRequest, HttpResponseForbidden
from django.shortcuts import redirect, render
from core.models import Aulario
from core.shell import require_axia_login, shell_context
from .models import ComedorMenu, ComedorMenuType, Cuaderno, StudentAttachment
def _safe_organization_id(value: str) -> str:
return re.sub(r"[^A-Za-z0-9._-]", "", (value or ""))
def _safe_aulario_id(value: str) -> str:
value = (value or "").split("/")[-1].split("\\")[-1]
return re.sub(r"[^A-Za-z0-9._-]", "", value)
def _safe_alumno_name(value: str) -> str:
value = (value or "").split("/")[-1].split("\\")[-1].strip()
value = re.sub(r"[\x00-\x1F\x7F]", "", value)
value = value.replace("/", "").replace("\\", "")
return value
def _valid_image(uploaded_file) -> bool:
content_type = (getattr(uploaded_file, "content_type", "") or "").lower()
if not content_type.startswith("image/"):
return False
allowed_ext = {".jpg", ".jpeg", ".png", ".gif", ".webp"}
ext = Path(uploaded_file.name or "").suffix.lower()
return ext in allowed_ext
def _alumnos_permission(request: HttpRequest) -> bool:
return (
request.axia_user.has_permission("aulatek:admin")
or request.axia_user.has_permission("aulatek:docente")
or request.axia_user.has_permission("entreaulas:docente")
)
def _cuadernos_permission(request: HttpRequest) -> bool:
return (
request.axia_user.has_permission("aulatek:docente")
or request.axia_user.has_permission("entreaulas:docente")
)
def _alumnos_access(request: HttpRequest):
gate = require_axia_login(request)
if gate:
return gate, None, None
if not _alumnos_permission(request):
return HttpResponseForbidden("No tienes permiso para gestionar alumnos."), None, None
org_id = _safe_organization_id(request.axia_user.active_org)
if not org_id:
return None, None, None
return None, request.axia_user, org_id
def _cuadernos_access(request: HttpRequest):
gate, user, org_id = _alumnos_access(request)
if gate:
return gate, None, None
if not _cuadernos_permission(request):
return HttpResponseForbidden("No tienes permiso para ver cuadernos."), None, None
return None, user, org_id
def _comedor_access(request: HttpRequest):
gate, user, org_id = _alumnos_access(request)
if gate:
return gate, None, None, None
aulario_id = _safe_aulario_id(request.GET.get("aulario", ""))
if not aulario_id or not org_id:
return None, user, org_id, None
aulario_row = Aulario.objects.filter(org_id=org_id, aulario_id=aulario_id).first()
if not aulario_row:
raise Http404("Aulario no encontrado")
return None, user, org_id, aulario_row
def _parse_menu_date(value: str):
text = (value or "").strip()
if not text:
return None
try:
return date.fromisoformat(text)
except ValueError:
return None
def _safe_titulo(value: str) -> str:
value = (value or "").strip()
value = re.sub(r"[\x00-\x1F\x7F]", "", value)
return value[:140]
def _signature_verified(request: HttpRequest) -> bool:
return (request.POST.get("_firma_signed") or "") == "1"
def aulatek_home(request: HttpRequest):
gate = require_axia_login(request)
if gate:
return gate
user = request.axia_user
aularios = []
if user.active_org:
rows = Aulario.objects.filter(org_id=user.active_org).order_by("aulario_id")
if user.aulas:
rows = rows.filter(aulario_id__in=user.aulas)
for row in rows:
payload = row.payload
if row.icon_file:
icon_url = row.icon_file.url
elif payload.get("icon"):
icon_url = payload.get("icon")
else:
icon_url = "/static/arasaac/aulario.png"
aularios.append(
{
"id": row.aulario_id,
"name": payload.get("name") or row.aulario_id,
"icon": icon_url,
}
)
context = shell_context(request, "aulatek", "AulaTek")
context["aularios"] = aularios
return render(request, "aulatek/index.html", context)
def aulatek_aulario(request: HttpRequest):
gate = require_axia_login(request)
if gate:
return gate
aulario_id = (request.GET.get("id") or "").strip()
row = Aulario.objects.filter(org_id=request.axia_user.active_org, aulario_id=aulario_id).first()
if not row:
raise Http404("Aulario no encontrado")
payload = row.payload
context = shell_context(request, "aulatek", f"Aulario: {payload.get('name') or aulario_id}")
context.update(
{
"aulario": {"id": aulario_id, "name": payload.get("name") or aulario_id},
"tiles": [
{"href": f"/aulatek/paneldiario/?aulario={aulario_id}", "label": "Panel Diario", "icon": "/static/arasaac/pdi.png", "variant": "primary"},
{
"href": f"/aulatek/alumnos/?aulario={aulario_id}",
"label": "Gestion de Alumnos",
"icon": "/static/arasaac/alumnos.png",
"variant": "info",
"visible": _alumnos_permission(request),
},
{
"href": f"/aulatek/cuadernos/?aulario={aulario_id}",
"label": "Cuadernos",
"icon": "/static/iconexperience/contract.png",
"variant": "dark",
"visible": _cuadernos_permission(request),
},
{
"href": f"/sysadmin/aularios/?aulario={aulario_id}",
"label": "Cambiar Ajustes",
"icon": "/static/iconexperience/gear_edit.png",
"variant": "secondary",
"visible": request.axia_user.has_permission("sysadmin:access"),
},
{"href": f"/aulatek/comedor/?aulario={aulario_id}", "label": "Menu del Comedor", "icon": "/static/arasaac/comedor.png", "variant": "success"},
{"href": f"/aulatek/proyectos/?aulario={aulario_id}", "label": "Proyectos", "icon": "", "variant": "warning"},
],
}
)
return render(request, "aulatek/aulario.html", context)
def _placeholder(request: HttpRequest, title: str):
gate = require_axia_login(request)
if gate:
return gate
context = shell_context(request, "aulatek", title)
context.update({"module_title": title, "module_description": "Modulo en migracion a Django.", "tiles": []})
return render(request, "core/module.html", context)
def paneldiario(request: HttpRequest):
return _placeholder(request, "Panel Diario")
def alumnos(request: HttpRequest):
gate, _, org_id = _alumnos_access(request)
if gate:
return gate
aulario_id = _safe_aulario_id(request.GET.get("aulario", ""))
if not aulario_id or not org_id:
context = shell_context(request, "aulatek", "Gestion de Alumnos")
context.update({"module_title": "Gestion de Alumnos", "aulario_id": "", "students": []})
return render(request, "aulatek/alumnos.html", context)
if not Aulario.objects.filter(org_id=org_id, aulario_id=aulario_id).exists():
raise Http404("Aulario no encontrado")
students = []
rows = StudentAttachment.objects.filter(org_id=org_id, aulario_id=aulario_id).order_by("alumno")
for row in rows:
students.append(
{
"id": row.id,
"name": row.alumno,
"has_photo": bool(row.photo),
}
)
context = shell_context(request, "aulatek", "Gestion de Alumnos")
context.update(
{
"module_title": "Gestion de Alumnos",
"aulario_id": aulario_id,
"students": students,
"can_view_cuadernos": _cuadernos_permission(request),
}
)
return render(request, "aulatek/alumnos.html", context)
def alumno_new(request: HttpRequest):
gate, _, org_id = _alumnos_access(request)
if gate:
return gate
aulario_id = _safe_aulario_id(request.GET.get("aulario", ""))
if not aulario_id or not org_id:
messages.error(request, "No se ha indicado un aulario valido.")
return redirect("/aulatek/")
if not Aulario.objects.filter(org_id=org_id, aulario_id=aulario_id).exists():
raise Http404("Aulario no encontrado")
if request.method == "POST":
nombre = _safe_alumno_name(request.POST.get("nombre", ""))
if not nombre:
messages.error(request, "El nombre no puede estar vacio.")
return redirect(f"/aulatek/alumnos/new/?aulario={aulario_id}")
if StudentAttachment.objects.filter(org_id=org_id, aulario_id=aulario_id, alumno=nombre).exists():
messages.error(request, "Ya existe un alumno con ese nombre.")
return redirect(f"/aulatek/alumnos/new/?aulario={aulario_id}")
student = StudentAttachment(org_id=org_id, aulario_id=aulario_id, alumno=nombre, data={})
photo = request.FILES.get("photo")
if photo and _valid_image(photo):
student.photo = photo
elif photo:
messages.warning(request, "La foto no se ha guardado porque no era una imagen valida.")
student.save()
messages.success(request, "Alumno anadido correctamente.")
return redirect(f"/aulatek/alumnos/?aulario={aulario_id}")
context = shell_context(request, "aulatek", "Nuevo Alumno")
context.update({"module_title": "Nuevo Alumno", "aulario_id": aulario_id})
return render(request, "aulatek/alumnos_new.html", context)
def alumno_edit(request: HttpRequest, student_id: int):
gate, _, org_id = _alumnos_access(request)
if gate:
return gate
student = StudentAttachment.objects.filter(id=student_id, org_id=org_id).first()
if not student:
raise Http404("Alumno no encontrado")
if request.method == "POST":
nombre_new = _safe_alumno_name(request.POST.get("nombre_new", ""))
if not nombre_new:
messages.error(request, "Nombre invalido.")
return redirect(f"/aulatek/alumnos/edit/{student.id}/")
if nombre_new != student.alumno and StudentAttachment.objects.filter(org_id=org_id, aulario_id=student.aulario_id, alumno=nombre_new).exists():
messages.error(request, "Ya existe un alumno con ese nombre.")
return redirect(f"/aulatek/alumnos/edit/{student.id}/")
student.alumno = nombre_new
photo = request.FILES.get("photo")
if photo and _valid_image(photo):
if student.photo:
student.photo.delete(save=False)
student.photo = photo
elif photo:
messages.warning(request, "La foto no se ha guardado porque no era una imagen valida.")
student.save()
messages.success(request, "Alumno actualizado correctamente.")
return redirect(f"/aulatek/alumnos/?aulario={student.aulario_id}")
context = shell_context(request, "aulatek", f"Editar Alumno: {student.alumno}")
context.update({"module_title": "Editar Alumno", "student": student})
return render(request, "aulatek/alumnos_edit.html", context)
def alumno_delete(request: HttpRequest, student_id: int):
gate, _, org_id = _alumnos_access(request)
if gate:
return gate
if request.method != "POST":
return HttpResponseForbidden("Metodo no permitido.")
if not _signature_verified(request):
return HttpResponseForbidden("Firma requerida para eliminar.")
student = StudentAttachment.objects.filter(id=student_id, org_id=org_id).first()
if not student:
raise Http404("Alumno no encontrado")
aulario_id = student.aulario_id
if student.photo:
student.photo.delete(save=False)
student.delete()
messages.success(request, "Alumno eliminado correctamente.")
return redirect(f"/aulatek/alumnos/?aulario={aulario_id}")
def alumno_photo(request: HttpRequest, student_id: int):
gate, _, org_id = _alumnos_access(request)
if gate:
return gate
if not org_id:
raise Http404("Foto no encontrada")
student = StudentAttachment.objects.filter(id=student_id, org_id=org_id).first()
if not student:
raise Http404("Foto no encontrada")
if not student.photo:
raise Http404("Foto no encontrada")
mime_type, _ = mimetypes.guess_type(student.photo.name)
return FileResponse(student.photo.open("rb"), content_type=mime_type or "application/octet-stream")
def cuadernos(request: HttpRequest):
gate, _, org_id = _cuadernos_access(request)
if gate:
return gate
aulario_id = _safe_aulario_id(request.GET.get("aulario", ""))
context = shell_context(request, "aulatek", "Cuadernos")
if not aulario_id or not org_id:
context.update(
{
"module_title": "Cuadernos",
"aulario_id": "",
"cuadernos": [],
"alumnos": [],
}
)
return render(request, "aulatek/cuadernos.html", context)
if not Aulario.objects.filter(org_id=org_id, aulario_id=aulario_id).exists():
raise Http404("Aulario no encontrado")
cuadernos_rows = Cuaderno.objects.filter(org_id=org_id, aulario_id=aulario_id).order_by("-updated_at", "-created_at")
context.update(
{
"module_title": "Cuadernos",
"aulario_id": aulario_id,
"cuadernos": cuadernos_rows,
}
)
return render(request, "aulatek/cuadernos.html", context)
def cuaderno_new(request: HttpRequest):
gate, user, org_id = _cuadernos_access(request)
if gate:
return gate
aulario_id = _safe_aulario_id(request.GET.get("aulario", ""))
if not aulario_id or not org_id:
messages.error(request, "No se ha indicado un aulario valido.")
return redirect("/aulatek/")
if not Aulario.objects.filter(org_id=org_id, aulario_id=aulario_id).exists():
raise Http404("Aulario no encontrado")
if request.method == "POST":
titulo = _safe_titulo(request.POST.get("titulo", ""))
alumno = _safe_alumno_name(request.POST.get("alumno", ""))
contenido = (request.POST.get("contenido") or "").strip()
if not titulo:
messages.error(request, "El titulo es obligatorio.")
return redirect(f"/aulatek/cuadernos/new/?aulario={aulario_id}")
if not contenido:
messages.error(request, "El contenido no puede estar vacio.")
return redirect(f"/aulatek/cuadernos/new/?aulario={aulario_id}")
Cuaderno.objects.create(
org_id=org_id,
aulario_id=aulario_id,
alumno=alumno,
titulo=titulo,
contenido_encriptado=contenido,
pin_hash="",
pin_salt="",
created_by=user.username if user else "",
updated_by=user.username if user else "",
)
messages.success(request, "Cuaderno creado correctamente.")
return redirect(f"/aulatek/cuadernos/?aulario={aulario_id}")
alumnos_rows = StudentAttachment.objects.filter(org_id=org_id, aulario_id=aulario_id).order_by("alumno")
context = shell_context(request, "aulatek", "Nuevo Cuaderno")
context.update(
{
"module_title": "Nuevo Cuaderno",
"aulario_id": aulario_id,
"alumnos": [row.alumno for row in alumnos_rows],
}
)
return render(request, "aulatek/cuaderno_new.html", context)
def cuaderno_edit(request: HttpRequest, cuaderno_id: int):
gate, user, org_id = _cuadernos_access(request)
if gate:
return gate
cuaderno = Cuaderno.objects.filter(id=cuaderno_id, org_id=org_id).first()
if not cuaderno:
raise Http404("Cuaderno no encontrado")
aulario_id = cuaderno.aulario_id
decrypted_content = cuaderno.contenido_encriptado or ""
if request.method == "POST":
action = (request.POST.get("action") or "unlock").strip().lower()
if action == "unlock":
return redirect(f"/aulatek/cuadernos/{cuaderno.id}/")
if action == "update":
titulo = _safe_titulo(request.POST.get("titulo", ""))
alumno = _safe_alumno_name(request.POST.get("alumno", ""))
contenido = (request.POST.get("contenido") or "").strip()
if not titulo:
messages.error(request, "El titulo es obligatorio.")
return redirect(f"/aulatek/cuadernos/{cuaderno.id}/")
if not contenido:
messages.error(request, "El contenido no puede estar vacio.")
return redirect(f"/aulatek/cuadernos/{cuaderno.id}/")
cuaderno.titulo = titulo
cuaderno.alumno = alumno
cuaderno.pin_salt = ""
cuaderno.pin_hash = ""
cuaderno.contenido_encriptado = contenido
cuaderno.updated_by = user.username if user else ""
cuaderno.save(update_fields=["titulo", "alumno", "pin_hash", "pin_salt", "contenido_encriptado", "updated_by", "updated_at"])
messages.success(request, "Cuaderno actualizado correctamente.")
return redirect(f"/aulatek/cuadernos/{cuaderno.id}/")
if action == "delete":
if not _signature_verified(request):
messages.error(request, "Firma requerida para eliminar el cuaderno.")
return redirect(f"/aulatek/cuadernos/{cuaderno.id}/")
cuaderno.delete()
messages.success(request, "Cuaderno eliminado.")
return redirect(f"/aulatek/cuadernos/?aulario={aulario_id}")
context = shell_context(request, "aulatek", f"Cuaderno: {cuaderno.titulo}")
context.update(
{
"module_title": "Editar Cuaderno",
"cuaderno": cuaderno,
"aulario_id": aulario_id,
"decrypted_content": decrypted_content,
}
)
return render(request, "aulatek/cuaderno_edit.html", context)
def comedor(request: HttpRequest):
gate, user, org_id, aulario_row = _comedor_access(request)
if gate:
return gate
context = shell_context(request, "aulatek", "Menu del Comedor")
if not org_id or not aulario_row:
context.update(
{
"module_title": "Menu del Comedor",
"aulario_id": "",
"menus": [],
"all_aularios": [],
}
)
return render(request, "aulatek/comedor.html", context)
all_aularios = list(Aulario.objects.filter(org_id=org_id).order_by("name", "aulario_id"))
selectable_shared = [row for row in all_aularios if row.id != aulario_row.id]
selected_date = _parse_menu_date(request.GET.get("date", "")) or date.today()
selected_menu_type = (request.GET.get("menu") or request.GET.get("type") or "").strip()
def comedor_url(target_date: date, target_type: str) -> str:
return "/aulatek/comedor/?" + urlencode(
{
"aulario": aulario_row.aulario_id,
"date": target_date.isoformat(),
"menu": target_type,
}
)
type_rows = list(
ComedorMenuType.objects.filter(org_id=org_id)
.filter(Q(source_aulario_id=aulario_row.aulario_id) | Q(shared_aularios=aulario_row))
.distinct()
.order_by("label")
)
if not selected_menu_type and type_rows:
selected_menu_type = type_rows[0].type_id
selected_type_row = next((row for row in type_rows if row.type_id == selected_menu_type), None)
if not selected_type_row and type_rows:
selected_type_row = type_rows[0]
selected_menu_type = selected_type_row.type_id
source_aulario_id = selected_type_row.source_aulario_id if selected_type_row else aulario_row.aulario_id
can_edit_selected_type = bool(selected_type_row and selected_type_row.source_aulario_id == aulario_row.aulario_id)
if request.method == "POST":
action = (request.POST.get("action") or "").strip()
if action == "add_type":
new_id = re.sub(r"[^a-z0-9_-]", "", (request.POST.get("new_type_id") or "").strip().lower())
new_label = (request.POST.get("new_type_label") or "").strip()
new_color = (request.POST.get("new_type_color") or "#0d6efd").strip() or "#0d6efd"
if new_id and new_label:
exists = ComedorMenuType.objects.filter(org_id=org_id, source_aulario_id=aulario_row.aulario_id, type_id=new_id).exists()
if exists:
messages.error(request, "Ese tipo ya existe.")
else:
ComedorMenuType.objects.create(
org_id=org_id,
source_aulario_id=aulario_row.aulario_id,
type_id=new_id,
label=new_label,
color=new_color,
created_by=user.username if user else "",
)
messages.success(request, "Tipo de menu creado.")
return redirect(comedor_url(selected_date, new_id))
if action == "rename_type":
rename_id = (request.POST.get("rename_type_id") or "").strip()
row = ComedorMenuType.objects.filter(org_id=org_id, source_aulario_id=aulario_row.aulario_id, type_id=rename_id).first()
if row:
new_label = (request.POST.get("rename_type_label") or "").strip()
new_color = (request.POST.get("rename_type_color") or "").strip()
if new_label:
row.label = new_label
if new_color:
row.color = new_color
row.save()
messages.success(request, "Tipo actualizado.")
return redirect(comedor_url(selected_date, row.type_id))
if action == "delete_type":
if not _signature_verified(request):
return HttpResponseForbidden("Firma requerida para eliminar tipo.")
delete_id = (request.POST.get("delete_type_id") or "").strip()
row = ComedorMenuType.objects.filter(org_id=org_id, source_aulario_id=aulario_row.aulario_id, type_id=delete_id).first()
if row:
ComedorMenu.objects.filter(org_id=org_id, aulario_id=aulario_row.aulario_id, menu_type=delete_id).delete()
row.delete()
messages.success(request, "Tipo eliminado.")
return redirect(comedor_url(selected_date, ""))
if action == "share_type":
if not selected_type_row or not can_edit_selected_type:
return HttpResponseForbidden("No tienes permiso para compartir este tipo.")
selected_ids = request.POST.getlist("shared_aularios")
rows = Aulario.objects.filter(org_id=org_id, id__in=selected_ids).exclude(id=aulario_row.id)
selected_type_row.shared_aularios.set(rows)
messages.success(request, "Comparticion de tipo actualizada.")
return redirect(comedor_url(selected_date, selected_type_row.type_id))
if action in {"save", "create", "update"}:
if not selected_type_row:
messages.error(request, "Selecciona un tipo valido.")
return redirect(comedor_url(selected_date, ""))
if not can_edit_selected_type:
return HttpResponseForbidden("No puedes editar un tipo compartido desde otro aulario.")
menu_date = _parse_menu_date(request.POST.get("menu_date", "")) or selected_date
first_name = (request.POST.get("first_name") or "").strip()
second_name = (request.POST.get("second_name") or "").strip()
dessert_name = (request.POST.get("dessert_name") or "").strip()
if not first_name or not second_name or not dessert_name:
messages.error(request, "Debes completar primero, segundo y postre.")
return redirect(comedor_url(menu_date, selected_type_row.type_id))
menu = ComedorMenu.objects.filter(
org_id=org_id,
aulario_id=selected_type_row.source_aulario_id,
menu_type=selected_type_row.type_id,
menu_date=menu_date,
).first()
if not menu:
menu = ComedorMenu(
org_id=org_id,
aulario_id=selected_type_row.source_aulario_id,
menu_name=selected_type_row.label,
menu_type=selected_type_row.type_id,
menu_date=menu_date,
created_by=user.username if user else "",
)
menu.first_name = first_name
menu.second_name = second_name
menu.dessert_name = dessert_name
first_photo = request.FILES.get(f"first_photo_{menu.id}") if menu.id else request.FILES.get("first_photo")
second_photo = request.FILES.get(f"second_photo_{menu.id}") if menu.id else request.FILES.get("second_photo")
dessert_photo = request.FILES.get(f"dessert_photo_{menu.id}") if menu.id else request.FILES.get("dessert_photo")
if first_photo and _valid_image(first_photo):
if menu.first_photo:
menu.first_photo.delete(save=False)
menu.first_photo = first_photo
if second_photo and _valid_image(second_photo):
if menu.second_photo:
menu.second_photo.delete(save=False)
menu.second_photo = second_photo
if dessert_photo and _valid_image(dessert_photo):
if menu.dessert_photo:
menu.dessert_photo.delete(save=False)
menu.dessert_photo = dessert_photo
menu.save()
messages.success(request, "Menu guardado correctamente.")
return redirect(comedor_url(menu_date, selected_type_row.type_id))
if action == "delete":
if not selected_type_row or not can_edit_selected_type:
return HttpResponseForbidden("No puedes eliminar menu de un tipo compartido.")
if not _signature_verified(request):
return HttpResponseForbidden("Firma requerida para eliminar menu.")
menu_id = int(request.POST.get("menu_id") or 0)
menu = ComedorMenu.objects.filter(
id=menu_id,
org_id=org_id,
aulario_id=selected_type_row.source_aulario_id,
menu_type=selected_type_row.type_id,
menu_date=selected_date,
).first()
if menu:
if menu.first_photo:
menu.first_photo.delete(save=False)
if menu.second_photo:
menu.second_photo.delete(save=False)
if menu.dessert_photo:
menu.dessert_photo.delete(save=False)
menu.delete()
messages.success(request, "Menu eliminado correctamente.")
return redirect(comedor_url(selected_date, selected_type_row.type_id))
# Refresca tras posibles acciones.
type_rows = list(
ComedorMenuType.objects.filter(org_id=org_id)
.filter(Q(source_aulario_id=aulario_row.aulario_id) | Q(shared_aularios=aulario_row))
.distinct()
.order_by("label")
)
if not selected_menu_type and type_rows:
selected_menu_type = type_rows[0].type_id
selected_type_row = next((row for row in type_rows if row.type_id == selected_menu_type), None)
if not selected_type_row and type_rows:
selected_type_row = type_rows[0]
selected_menu_type = selected_type_row.type_id
source_aulario_id = selected_type_row.source_aulario_id if selected_type_row else aulario_row.aulario_id
can_edit_selected_type = bool(selected_type_row and selected_type_row.source_aulario_id == aulario_row.aulario_id)
aularios_by_pk = {row.id: row for row in all_aularios}
selected_menu = None
if selected_type_row:
selected_menu = ComedorMenu.objects.filter(
org_id=org_id,
aulario_id=source_aulario_id,
menu_type=selected_type_row.type_id,
menu_date=selected_date,
).first()
selected_menu_data = None
shared_ids = []
if selected_type_row:
shared_ids = list(selected_type_row.shared_aularios.values_list("id", flat=True))
if selected_menu:
owner_row = Aulario.objects.filter(org_id=org_id, aulario_id=source_aulario_id).first()
selected_menu_data = {
"obj": selected_menu,
"is_owner": can_edit_selected_type,
"origin_label": (owner_row.name or owner_row.aulario_id) if owner_row else source_aulario_id,
"shared_ids": shared_ids,
"shared_labels": [
(aularios_by_pk[pk].name or aularios_by_pk[pk].aulario_id)
for pk in shared_ids
if pk in aularios_by_pk
],
}
menu_types = [
{
"id": row.type_id,
"label": row.label,
"color": row.color or "#0d6efd",
"active": row.type_id == selected_menu_type,
"is_owner": row.source_aulario_id == aulario_row.aulario_id,
"source_aulario_id": row.source_aulario_id,
}
for row in type_rows
]
prev_date = selected_date.fromordinal(selected_date.toordinal() - 1)
next_date = selected_date.fromordinal(selected_date.toordinal() + 1)
context.update(
{
"module_title": "Menu del Comedor",
"aulario_id": aulario_row.aulario_id,
"aulario_name": aulario_row.name or aulario_row.aulario_id,
"menu_types": menu_types,
"selected_menu_type": selected_menu_type,
"selected_date": selected_date,
"prev_date": prev_date,
"next_date": next_date,
"selected_menu": selected_menu_data,
"selected_type": selected_type_row,
"can_edit_selected_type": can_edit_selected_type,
"all_aularios": selectable_shared,
"aulario_options": all_aularios,
}
)
return render(request, "aulatek/comedor.html", context)
def proyectos(request: HttpRequest):
return _placeholder(request, "Proyectos")
def supercafe(request: HttpRequest):
return _placeholder(request, "SuperCafe")