feat: replace pipeline profile and source health
This commit is contained in:
@@ -274,6 +274,31 @@ def track_application_changes(
|
||||
return events
|
||||
|
||||
|
||||
def change_application_status(*, application: Application, user, status: str) -> Application:
|
||||
"""Persist an accessible pipeline status change and its audit event."""
|
||||
|
||||
valid_statuses = {value for value, _label in Application.Status.choices}
|
||||
if status not in valid_statuses:
|
||||
raise ValueError("Ongeldige sollicitatiestatus.")
|
||||
previous = application.status
|
||||
if previous == status:
|
||||
return application
|
||||
with transaction.atomic():
|
||||
application.status = status
|
||||
update_fields = ["status", "updated_at"]
|
||||
if status == Application.Status.APPLIED and application.applied_at is None:
|
||||
application.applied_at = timezone.now()
|
||||
update_fields.append("applied_at")
|
||||
application.save(update_fields=update_fields)
|
||||
_record_timeline_event(
|
||||
application=application,
|
||||
user=user,
|
||||
event_type=ApplicationTimelineEvent.EventType.STATUS_CHANGED,
|
||||
metadata={"from": previous, "to": status},
|
||||
)
|
||||
return application
|
||||
|
||||
|
||||
def build_print_html(application: Application) -> str:
|
||||
snapshot = application.snapshot if isinstance(application.snapshot, dict) else {}
|
||||
job = application.job
|
||||
|
||||
@@ -9,6 +9,7 @@ from .views import (
|
||||
application_delete,
|
||||
application_export,
|
||||
application_print,
|
||||
application_status,
|
||||
job_feedback,
|
||||
)
|
||||
|
||||
@@ -17,6 +18,7 @@ urlpatterns = [
|
||||
path("skills/", SkillInsightsView.as_view(), name="skill-insights"),
|
||||
path("applications/", ApplicationListView.as_view(), name="applications"),
|
||||
path("applications/<int:pk>/", ApplicationUpdateView.as_view(), name="application-edit"),
|
||||
path("applications/<int:pk>/status/", application_status, name="application-status"),
|
||||
path("applications/<int:pk>/export/", application_export, name="application-export"),
|
||||
path("applications/<int:pk>/print/", application_print, name="application-print"),
|
||||
path("applications/<int:pk>/delete/", application_delete, name="application-delete"),
|
||||
|
||||
+38
-3
@@ -7,6 +7,7 @@ from django.db.models import Count, F, Q
|
||||
from django.http import Http404, HttpResponse
|
||||
from django.shortcuts import get_object_or_404, redirect
|
||||
from django.urls import reverse
|
||||
from django.utils import timezone
|
||||
from django.utils.http import url_has_allowed_host_and_scheme
|
||||
from django.views.decorators.http import require_POST
|
||||
from django.views.generic import DetailView, ListView, TemplateView, UpdateView
|
||||
@@ -18,6 +19,7 @@ from .models import Application, Feedback, JobPosting, ScoreRun
|
||||
from .services.applications import (
|
||||
build_application_export,
|
||||
build_print_html,
|
||||
change_application_status,
|
||||
delete_application_dossier,
|
||||
track_application_changes,
|
||||
)
|
||||
@@ -206,8 +208,10 @@ class ApplicationListView(LoginRequiredMixin, ListView):
|
||||
context_object_name = "applications"
|
||||
|
||||
def get_queryset(self):
|
||||
return Application.objects.filter(user=self.request.user).select_related(
|
||||
"job", "job__employer"
|
||||
return (
|
||||
Application.objects.filter(user=self.request.user)
|
||||
.select_related("job", "job__employer")
|
||||
.prefetch_related("timeline_events")
|
||||
)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
@@ -222,6 +226,16 @@ class ApplicationListView(LoginRequiredMixin, ListView):
|
||||
}
|
||||
for status, label in Application.Status.choices
|
||||
]
|
||||
context["status_choices"] = Application.Status.choices
|
||||
context["open_count"] = sum(
|
||||
item.status not in {Application.Status.REJECTED, Application.Status.WITHDRAWN}
|
||||
for item in applications
|
||||
)
|
||||
context["follow_up_count"] = sum(
|
||||
bool(item.follow_up_date and item.follow_up_date <= timezone.localdate())
|
||||
for item in applications
|
||||
)
|
||||
context["today"] = timezone.localdate()
|
||||
return context
|
||||
|
||||
|
||||
@@ -231,7 +245,11 @@ class ApplicationUpdateView(LoginRequiredMixin, UpdateView):
|
||||
template_name = "applications/edit.html"
|
||||
|
||||
def get_queryset(self):
|
||||
return Application.objects.filter(user=self.request.user).select_related("job")
|
||||
return (
|
||||
Application.objects.filter(user=self.request.user)
|
||||
.select_related("job", "job__employer")
|
||||
.prefetch_related("timeline_events")
|
||||
)
|
||||
|
||||
def form_valid(self, form):
|
||||
previous = (
|
||||
@@ -269,6 +287,23 @@ def application_export(request, pk: int):
|
||||
return response
|
||||
|
||||
|
||||
@login_required
|
||||
@require_POST
|
||||
def application_status(request, pk: int):
|
||||
application = get_object_or_404(Application, pk=pk, user=request.user)
|
||||
try:
|
||||
change_application_status(
|
||||
application=application,
|
||||
user=request.user,
|
||||
status=request.POST.get("status", ""),
|
||||
)
|
||||
except ValueError as exc:
|
||||
messages.error(request, str(exc))
|
||||
else:
|
||||
messages.success(request, "Pipelinestatus bijgewerkt.")
|
||||
return redirect("jobs:applications")
|
||||
|
||||
|
||||
@login_required
|
||||
def application_print(request, pk: int):
|
||||
application = get_object_or_404(
|
||||
|
||||
Reference in New Issue
Block a user