This commit is contained in:
Ross
2025-12-09 12:36:18 +00:00
parent e6943ccfc7
commit 40135a282d
6 changed files with 92 additions and 8 deletions
@@ -10,6 +10,8 @@ class Command(BaseCommand):
def add_arguments(self, parser):
parser.add_argument("rota_id", type=int)
parser.add_argument("--external", action="store_true", help="Use external generator hook if available")
parser.add_argument("--no-export", action="store_true", help="Do not generate HTML export for this run")
parser.add_argument("--no-solve", action="store_true", help="Do not run solver when generating export")
def handle(self, *args, **options):
rota_id = options["rota_id"]
@@ -19,5 +21,12 @@ class Command(BaseCommand):
except RotaSchedule.DoesNotExist:
raise CommandError(f"RotaSchedule with id={rota_id} not found")
run = run_rota_async(rota.id, use_external_generator=use_external)
no_export = options.get("no_export", False)
no_solve = options.get("no_solve", False)
run = run_rota_async(
rota.id,
use_external_generator=use_external,
generate_builder=not no_export,
builder_solve=not no_solve,
)
self.stdout.write(self.style.SUCCESS(f"Started RotaRun id={run.id} for rota={rota.name}"))
+2 -2
View File
@@ -7,7 +7,7 @@ from django.utils import timezone
from .models import RotaRun, RotaSchedule
def _run_rota_job(run_id: int, use_external_generator: bool = False, generate_builder: bool = False, builder_solve: bool = False):
def _run_rota_job(run_id: int, use_external_generator: bool = False, generate_builder: bool = True, builder_solve: bool = True):
"""Background job that executes the scheduler and updates RotaRun.
If `use_external_generator` is True, callers should implement a project
@@ -83,7 +83,7 @@ def _run_rota_job(run_id: int, use_external_generator: bool = False, generate_bu
print(tb)
def run_rota_async(rota_id: int, use_external_generator: bool = False, generate_builder: bool = False, builder_solve: bool = False) -> RotaRun:
def run_rota_async(rota_id: int, use_external_generator: bool = False, generate_builder: bool = True, builder_solve: bool = True) -> RotaRun:
"""Create a RotaRun and start background thread to run it.
Returns the created RotaRun instance.
+1
View File
@@ -20,4 +20,5 @@ urlpatterns = [
path("run/<int:run_id>/export/download/", views.rota_run_export_download, name="rota_run_export_download"),
path("rota/<int:rota_id>/export/builder/", views.rota_export_builder, name="rota_export_builder"),
path("rota/<int:rota_id>/runs/clear/", views.rota_runs_clear, name="rota_runs_clear"),
path("run/<int:run_id>/export/regenerate/", views.rota_run_export_regenerate, name="rota_run_export_regenerate"),
]
+40 -1
View File
@@ -9,6 +9,7 @@ from django.views.decorators.http import require_http_methods
from django.template.loader import render_to_string
from django.http import HttpResponse, HttpResponseBadRequest
from django.views.decorators.http import require_POST
from django.shortcuts import HttpResponseRedirect
def index(request):
@@ -213,6 +214,11 @@ def rota_run_export_html(request, run_id):
from .models import RotaRun
run = get_object_or_404(RotaRun, pk=run_id)
# If the export HTML was persisted with the run, return it directly.
if getattr(run, "export_html", None):
return HttpResponse(run.export_html, content_type="text/html; charset=utf-8")
# Fallback: render the export template which shows run.result.
return render(request, "rota/rota_run_export.html", {"run": run})
@@ -220,13 +226,46 @@ def rota_run_export_download(request, run_id):
from .models import RotaRun
run = get_object_or_404(RotaRun, pk=run_id)
html = render_to_string("rota/rota_run_export.html", {"run": run}, request=request)
# Prefer persisted export HTML if available
if getattr(run, "export_html", None):
html = run.export_html
else:
html = render_to_string("rota/rota_run_export.html", {"run": run}, request=request)
response = HttpResponse(html, content_type="text/html; charset=utf-8")
filename = f"rota_run_{run.id}_export.html"
response["Content-Disposition"] = f'attachment; filename="{filename}"'
return response
@require_POST
def rota_run_export_regenerate(request, run_id):
"""Regenerate the HTML export for a completed run and persist it on the RotaRun.
This is a POST endpoint — regenerating may be slow if the solver is invoked.
"""
from .models import RotaRun
run = get_object_or_404(RotaRun, pk=run_id)
rota = run.rota
solve = request.POST.get("solve") == "1"
try:
rb = rota.to_rota_builder()
rb.build_and_solve(solve=solve)
html = rb.get_worker_timetable_html(include_html_tag=True)
run.export_html = html
run.save(update_fields=["export_html"])
except Exception as exc:
tb = traceback.format_exc()
run.log = (run.log or "") + "\n" + tb
run.save(update_fields=["log"])
return HttpResponse(f"Failed to regenerate export: {exc}", status=500)
# Redirect to the HTML view so user sees the generated page
return HttpResponseRedirect(reverse("rota:rota_run_export_html", args=(run.id,)))
def rota_export_builder(request, rota_id):
"""Build a `RotaBuilder` for the given `RotaSchedule` and return its HTML export.