.
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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():
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user