.
This commit is contained in:
@@ -8,6 +8,13 @@ https://docs.djangoproject.com/en/6.0/howto/deployment/asgi/
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
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
|
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 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
|
from django.core.wsgi import get_wsgi_application
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,14 @@
|
|||||||
"""Django's command-line utility for administrative tasks."""
|
"""Django's command-line utility for administrative tasks."""
|
||||||
import os
|
import os
|
||||||
import sys
|
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():
|
def main():
|
||||||
|
|||||||
@@ -32,10 +32,26 @@ class RotaSchedule(models.Model):
|
|||||||
|
|
||||||
Returns the RotaBuilder instance.
|
Returns the RotaBuilder instance.
|
||||||
"""
|
"""
|
||||||
try:
|
# Try importing the project's rota generator implementation. Some setups
|
||||||
from rota_generator.shifts import RotaBuilder, SingleShift
|
# provide the implementation as `rota_generator.shifts` (script/package),
|
||||||
except Exception:
|
# while others provide it as `rota.shifts` (library folder). Try both
|
||||||
raise RuntimeError("Unable to import rota_generator.shifts; ensure the rota package is importable")
|
# 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)
|
# compute weeks_to_rota (integer number of weeks)
|
||||||
delta = self.end_date - self.start_date
|
delta = self.end_date - self.start_date
|
||||||
|
|||||||
@@ -38,14 +38,76 @@ def rota_create(request):
|
|||||||
|
|
||||||
|
|
||||||
def worker_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":
|
if request.method == "POST":
|
||||||
form = WorkerForm(request.POST)
|
form = WorkerForm(request.POST)
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
worker = form.save()
|
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("/")
|
return redirect("/")
|
||||||
else:
|
else:
|
||||||
form = WorkerForm()
|
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})
|
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
|
# invalid: return form modal with errors
|
||||||
modal_html = render_to_string(
|
modal_html = render_to_string(
|
||||||
"rota/partials/worker_form_modal.html",
|
"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,
|
request=request,
|
||||||
)
|
)
|
||||||
return HttpResponse(modal_html)
|
return HttpResponse(modal_html)
|
||||||
|
|||||||
Reference in New Issue
Block a user