This commit is contained in:
@@ -26,7 +26,7 @@ SNAPSHOT_VERSION = "1.0.0"
|
||||
|
||||
|
||||
def _coerce_json_value(value: Any) -> Any:
|
||||
if isinstance(value, (date,)):
|
||||
if isinstance(value, date):
|
||||
return value.isoformat()
|
||||
if isinstance(value, Decimal):
|
||||
return float(value)
|
||||
@@ -85,7 +85,17 @@ def _latest_score_snapshot(job: JobPosting, user) -> dict[str, Any] | None:
|
||||
run = (
|
||||
ScoreRun.objects.filter(job=job, profile=profile)
|
||||
.order_by("-created_at")
|
||||
.only("score", "recommendation", "confidence", "components", "positives", "concerns", "hard_exclusions", "evidence", "profile_version")
|
||||
.only(
|
||||
"score",
|
||||
"recommendation",
|
||||
"confidence",
|
||||
"components",
|
||||
"positives",
|
||||
"concerns",
|
||||
"hard_exclusions",
|
||||
"evidence",
|
||||
"profile_version",
|
||||
)
|
||||
.first()
|
||||
)
|
||||
if not run:
|
||||
@@ -121,7 +131,9 @@ def _build_application_snapshot_payload(application: Application) -> dict[str, A
|
||||
}
|
||||
|
||||
|
||||
def _record_timeline_event(*, application: Application, user, event_type: str, metadata: dict[str, Any] | None = None) -> ApplicationTimelineEvent:
|
||||
def _record_timeline_event(
|
||||
*, application: Application, user, event_type: str, metadata: dict[str, Any] | None = None
|
||||
) -> ApplicationTimelineEvent:
|
||||
payload: dict[str, Any] = {}
|
||||
if metadata:
|
||||
payload.update({key: value for key, value in metadata.items() if value is not None})
|
||||
@@ -243,8 +255,11 @@ def track_application_changes(
|
||||
)
|
||||
events += 1
|
||||
|
||||
if (_normalize_str(previous.get("contact_name")) != _normalize_str(current.get("contact_name"))) or (
|
||||
_normalize_str(previous.get("contact_email")) != _normalize_str(current.get("contact_email"))
|
||||
if (
|
||||
_normalize_str(previous.get("contact_name")) != _normalize_str(current.get("contact_name"))
|
||||
) or (
|
||||
_normalize_str(previous.get("contact_email"))
|
||||
!= _normalize_str(current.get("contact_email"))
|
||||
):
|
||||
_record_timeline_event(
|
||||
application=application,
|
||||
@@ -266,16 +281,14 @@ def build_print_html(application: Application) -> str:
|
||||
safe_rows = []
|
||||
for row in timeline_rows:
|
||||
safe_rows.append(
|
||||
(
|
||||
f"<tr>"
|
||||
f"<td>{escape(row['timestamp'])}</td>"
|
||||
f"<td>{escape(row['type'])}</td>"
|
||||
f"<td>{escape(row['actor'])}</td>"
|
||||
f"<td>{escape(row['from'])}</td>"
|
||||
f"<td>{escape(row['to'])}</td>"
|
||||
f"<td>{escape(row['note'])}</td>"
|
||||
"</tr>"
|
||||
)
|
||||
f"<tr>"
|
||||
f"<td>{escape(row['timestamp'])}</td>"
|
||||
f"<td>{escape(row['type'])}</td>"
|
||||
f"<td>{escape(row['actor'])}</td>"
|
||||
f"<td>{escape(row['from'])}</td>"
|
||||
f"<td>{escape(row['to'])}</td>"
|
||||
f"<td>{escape(row['note'])}</td>"
|
||||
"</tr>"
|
||||
)
|
||||
|
||||
source_rows = []
|
||||
@@ -284,20 +297,26 @@ def build_print_html(application: Application) -> str:
|
||||
source_url = escape(str(source.get("url", "")))
|
||||
source_rows.append(f"<li>{source_name}: {source_url}</li>")
|
||||
source_section = "".join(source_rows) if source_rows else "<li>Niet beschikbaar</li>"
|
||||
frozen_at = (
|
||||
application.snapshot.get("frozen_at", "") if isinstance(application.snapshot, dict) else ""
|
||||
)
|
||||
snapshot_json = json.dumps(snapshot, indent=2, ensure_ascii=False, sort_keys=True)
|
||||
|
||||
return (
|
||||
"<!doctype html><html><head><meta charset='utf-8'>"
|
||||
"<title>Sollicitatiedossier</title><style>body{font-family:Arial,sans-serif;margin:24px}"
|
||||
"table{border-collapse:collapse;width:100%}th,td{border:1px solid #ddd;padding:8px;text-align:left}"
|
||||
"table{border-collapse:collapse;width:100%}"
|
||||
"th,td{border:1px solid #ddd;padding:8px;text-align:left}"
|
||||
"th{background:#f2f2f2} </style></head><body>"
|
||||
f"<h1>{escape(job.original_title)}</h1>"
|
||||
f"<p>Vacature: {escape(job.canonical_url)}</p>"
|
||||
f"<p>Status: {escape(application.get_status_display())}</p>"
|
||||
f"<p>Geslaagd op: {escape(str(application.snapshot.get('frozen_at') if isinstance(application.snapshot, dict) else ''))}</p>"
|
||||
f"<p>Geslaagd op: {escape(str(frozen_at))}</p>"
|
||||
f"<h2>Bronnen</h2><ul>{source_section}</ul>"
|
||||
f"<h2>Timeline</h2><table><thead><tr><th>Tijd</th><th>Type</th><th>Actor</th><th>Van</th><th>Naar</th><th>Notitie</th></tr></thead><tbody>"
|
||||
"<h2>Timeline</h2><table><thead><tr><th>Tijd</th><th>Type</th>"
|
||||
"<th>Actor</th><th>Van</th><th>Naar</th><th>Notitie</th></tr></thead><tbody>"
|
||||
f"{''.join(safe_rows)}</tbody></table>"
|
||||
f"<h2>Snapshot</h2><pre>{escape(json.dumps(snapshot, indent=2, ensure_ascii=False, sort_keys=True))}</pre>"
|
||||
f"<h2>Snapshot</h2><pre>{escape(snapshot_json)}</pre>"
|
||||
"</body></html>"
|
||||
)
|
||||
|
||||
@@ -315,7 +334,9 @@ def build_application_export(application: Application) -> tuple[bytes, str]:
|
||||
"snapshot_version": SNAPSHOT_VERSION,
|
||||
"exported_at": timezone.now().isoformat(),
|
||||
"applied_at": application.applied_at.isoformat() if application.applied_at else None,
|
||||
"follow_up_date": application.follow_up_date.isoformat() if application.follow_up_date else None,
|
||||
"follow_up_date": application.follow_up_date.isoformat()
|
||||
if application.follow_up_date
|
||||
else None,
|
||||
},
|
||||
"snapshot": _coerce_json_value(application.snapshot),
|
||||
"timeline": _timeline_dict_rows(application),
|
||||
@@ -332,7 +353,10 @@ def build_application_export(application: Application) -> tuple[bytes, str]:
|
||||
|
||||
content_buffer = io.BytesIO()
|
||||
with zipfile.ZipFile(content_buffer, "w", compression=zipfile.ZIP_DEFLATED) as zf:
|
||||
zf.writestr("application.json", json.dumps(_coerce_json_value(payload), indent=2, ensure_ascii=False, sort_keys=True))
|
||||
zf.writestr(
|
||||
"application.json",
|
||||
json.dumps(_coerce_json_value(payload), indent=2, ensure_ascii=False, sort_keys=True),
|
||||
)
|
||||
zf.writestr("timeline.csv", timeline_buffer.getvalue())
|
||||
zf.writestr("application_print.html", build_print_html(application))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user