This commit is contained in:
Ross
2025-12-09 12:07:26 +00:00
parent eb95f5fa6d
commit c63d4d1585
5 changed files with 105 additions and 5 deletions
+7
View File
@@ -8,6 +8,13 @@ https://docs.djangoproject.com/en/6.0/howto/deployment/asgi/
"""
import os
import sys
import pathlib
# Ensure repo root is importable when running via ASGI
repo_root = pathlib.Path(__file__).resolve().parents[2]
if str(repo_root) not in sys.path:
sys.path.insert(0, str(repo_root))
from django.core.asgi import get_asgi_application
+7
View File
@@ -8,6 +8,13 @@ https://docs.djangoproject.com/en/6.0/howto/deployment/wsgi/
"""
import os
import sys
import pathlib
# Ensure repo root is importable when running via WSGI
repo_root = pathlib.Path(__file__).resolve().parents[2]
if str(repo_root) not in sys.path:
sys.path.insert(0, str(repo_root))
from django.core.wsgi import get_wsgi_application
+8
View File
@@ -2,6 +2,14 @@
"""Django's command-line utility for administrative tasks."""
import os
import sys
import pathlib
# Ensure the repository root is on sys.path so top-level modules (e.g.
# `rota_generator` or `rota`) are importable when running Django from
# the `djangorota/` directory.
repo_root = pathlib.Path(__file__).resolve().parents[1]
if str(repo_root) not in sys.path:
sys.path.insert(0, str(repo_root))
def main():
+20 -4
View File
@@ -32,10 +32,26 @@ class RotaSchedule(models.Model):
Returns the RotaBuilder instance.
"""
try:
from rota_generator.shifts import RotaBuilder, SingleShift
except Exception:
raise RuntimeError("Unable to import rota_generator.shifts; ensure the rota package is importable")
# Try importing the project's rota generator implementation. Some setups
# provide the implementation as `rota_generator.shifts` (script/package),
# while others provide it as `rota.shifts` (library folder). Try both
# so the Django app works in either layout.
RotaBuilder = None
SingleShift = None
import importlib
for mod_name in ("rota_generator.shifts", "rota.shifts"):
try:
mod = importlib.import_module(mod_name)
RotaBuilder = getattr(mod, "RotaBuilder")
SingleShift = getattr(mod, "SingleShift")
break
except Exception:
continue
if RotaBuilder is None or SingleShift is None:
raise RuntimeError(
"Unable to import rota generator (tried 'rota_generator.shifts' and 'rota.shifts'); "
"ensure the rota package is importable and contains RotaBuilder/SingleShift"
)
# compute weeks_to_rota (integer number of weeks)
delta = self.end_date - self.start_date
+63 -1
View File
@@ -38,14 +38,76 @@ def rota_create(request):
def worker_create(request):
# Support both full-page and HTMX modal flows. If the request comes from
# HTMX (header `HX-Request: true`) we return the modal partial so it can
# be injected into the `#modal` container. On successful HTMX POST we
# return an out-of-band swap to refresh the worker list and clear the
# modal.
is_hx = request.headers.get("HX-Request", "false").lower() == "true"
if request.method == "POST":
form = WorkerForm(request.POST)
if form.is_valid():
worker = form.save()
if is_hx:
# Return updated worker list and close modal
# If a rota context was supplied via querystring, we do not need it
# here because the partial will re-render from DB.
# Render worker list OOB and clear modal container
# We need a rota to render the list — try to use `rota_id` query param
# rota_id may be provided as a querystring or as a hidden form field.
rota_id = request.GET.get("rota_id") or request.POST.get("rota_id")
rota = None
if rota_id:
try:
rota = get_object_or_404(RotaSchedule, pk=int(rota_id))
except Exception:
rota = None
# If we created a worker and a rota_id was provided, create Assignment
if rota is not None:
from .models import Assignment
try:
Assignment.objects.create(worker=worker, rota=rota)
except Exception:
# best-effort: try using the M2M add as fallback
try:
rota.workers.add(worker)
except Exception:
pass
if rota is not None:
worker_list_html = render_to_string(
"rota/partials/worker_list.html",
{"rota": rota},
request=request,
)
resp = (
f'<div id="worker-list" hx-swap-oob="true">{worker_list_html}</div>'
+ '<div id="modal" hx-swap-oob="true"></div>'
)
response = HttpResponse(resp)
response["HX-Trigger"] = "closeModal"
return response
# If we couldn't render the worker list (no rota), just close modal
response = HttpResponse('<div id="modal" hx-swap-oob="true"></div>')
response["HX-Trigger"] = "closeModal"
return response
# Non-HTMX POST -> redirect
return redirect("/")
else:
form = WorkerForm()
# HTMX GET: return modal partial
if is_hx:
modal_html = render_to_string(
"rota/partials/worker_form_modal.html",
{"form": form, "form_action": request.get_full_path()},
request=request,
)
return HttpResponse(modal_html)
# Non-HTMX GET: full page
return render(request, "rota/worker_form.html", {"form": form})
@@ -86,7 +148,7 @@ def worker_edit(request, rota_id, worker_id):
# invalid: return form modal with errors
modal_html = render_to_string(
"rota/partials/worker_form_modal.html",
{"form": form, "form_action": request.path, "rota": rota},
{"form": form, "form_action": request.get_full_path(), "rota": rota},
request=request,
)
return HttpResponse(modal_html)