This commit is contained in:
Jens
2026-07-24 22:46:37 +02:00
parent 0101660bd7
commit bdf44eec04
26 changed files with 1770 additions and 24 deletions
+269
View File
@@ -0,0 +1,269 @@
"""Vult een fictieve demo-omgeving voor het demo-account.
Wordt aangeroepen bij 'Bekijk demo' zodat de gebruiker meteen een geloofwaardige,
gevulde cockpit ziet (profiel, vacatures, matchscores en een sollicitatiepijplijn)
in plaats van een leeg dashboard. Volledig idempotent: eenmaal geseed, wordt er
niet opnieuw geseed.
"""
from __future__ import annotations
import hashlib
from datetime import timedelta
from decimal import Decimal
from django.utils import timezone
from apps.profiles.models import SearchProfile
_DEMO_PROFILE_NAME = "Demo · IT-infrastructuur Limburg"
# (titel, werkgever, domein, gemeente, regio, werkplek, skills_required,
# skills_preferred, score, recommendation, positives, concerns)
_DEMO_JOBS = [
(
"Infrastructure Engineer",
"CloudNexus",
"cloudnexus.example",
"Hasselt",
"Limburg",
"hybrid",
["Microsoft 365", "VMware", "Azure"],
["Terraform"],
Decimal("92.4"),
"strong",
["Dichtbij Hasselt", "Sterke match op Microsoft 365 en VMware", "Hybride werk"],
[],
),
(
"Cloud Architect",
"Skylink Solutions",
"skylink.example",
"Remote",
"Vlaanderen",
"remote",
["Azure", "Kubernetes"],
["Bicep", "Landing Zones"],
Decimal("88.1"),
"strong",
["Volledig remote", "Azure-zwaartepunt sluit aan"],
["Iets seniorder dan gevraagd"],
),
(
"Systeembeheerder Microsoft 365",
"Gemeente Genk",
"genk.example",
"Genk",
"Limburg",
"on_site",
["Microsoft 365", "Active Directory"],
["Intune"],
Decimal("83.6"),
"strong",
["Publieke sector met stabiliteit", "M365-focus"],
["Volledig op locatie"],
),
(
"DevOps Engineer",
"Datastroom BV",
"datastroom.example",
"Leuven",
"Vlaams-Brabant",
"hybrid",
["CI/CD", "Docker", "Azure"],
["GitLab", "Observability"],
Decimal("77.9"),
"possible",
["Moderne toolchain", "Hybride"],
["Reistijd naar Leuven"],
),
(
"Network Engineer",
"Noorder Telecom",
"noordertelecom.example",
"Antwerpen",
"Antwerpen",
"on_site",
["Cisco", "Netwerkbeheer"],
["Fortinet"],
Decimal("70.4"),
"possible",
["Stevige netwerkrol"],
["Weinig cloudcomponent", "Op locatie in Antwerpen"],
),
(
"IT Support Specialist",
"MediCare Groep",
"medicare.example",
"Diepenbeek",
"Limburg",
"on_site",
["Servicedesk", "Windows"],
["ITIL"],
Decimal("62.3"),
"weak",
["Dichtbij"],
["Sterk supportgericht", "Weinig infrastructuurverdieping"],
),
]
def _digest(*parts: str) -> str:
return hashlib.sha256("::".join(parts).encode("utf-8")).hexdigest()
def demo_environment_seeded(user) -> bool:
profile = SearchProfile.objects.filter(user=user, name=_DEMO_PROFILE_NAME).first()
if profile is None:
return False
from apps.jobs.models import ScoreRun
return ScoreRun.objects.filter(profile=profile).exists()
def seed_demo_environment(user) -> None:
"""Maak (idempotent) een fictieve demo-omgeving voor ``user``."""
if demo_environment_seeded(user):
return
from apps.jobs.models import Application, Employer, JobPosting, ScoreRun
profile, _ = SearchProfile.objects.get_or_create(
user=user,
name=_DEMO_PROFILE_NAME,
defaults={
"is_active": True,
"home_municipality": "Hasselt",
"home_latitude": Decimal("50.930700"),
"home_longitude": Decimal("5.332500"),
"max_distance_km": 60,
"experience_years": 5,
"learning_enabled": True,
"desired_titles": ["infrastructure engineer", "systeembeheerder", "cloud engineer"],
"excluded_titles": ["sales", "recruiter"],
"desired_skills": ["Microsoft 365", "VMware", "Azure"],
"allowed_employment_types": ["full_time", "permanent"],
"preferred_workplace": ["hybrid", "remote", "on_site"],
"preferred_regions": ["Limburg", "Vlaams-Brabant"],
},
)
SearchProfile.objects.filter(user=user).exclude(pk=profile.pk).update(is_active=False)
if not profile.is_active:
profile.is_active = True
profile.save(update_fields=["is_active"])
now = timezone.now()
workplace_map = {
"hybrid": JobPosting.Workplace.HYBRID,
"remote": JobPosting.Workplace.REMOTE,
"on_site": JobPosting.Workplace.ON_SITE,
}
created_jobs: list = []
for index, (
title,
employer_name,
domain,
municipality,
region,
workplace,
req_skills,
pref_skills,
score,
recommendation,
positives,
concerns,
) in enumerate(_DEMO_JOBS):
employer, _ = Employer.objects.get_or_create(
normalized_name=employer_name.lower(),
defaults={
"name": employer_name,
"domain": domain,
"is_direct_employer": True,
"is_recruiter": False,
"confidence": 0.92,
},
)
slug = _digest("demo-job", str(index), title, employer_name)
job, job_created = JobPosting.objects.get_or_create(
canonical_key=slug,
defaults={
"employer": employer,
"original_title": title,
"normalized_title": title.lower(),
"job_family": "infrastructure",
"canonical_url": f"https://{domain}/vacatures/{slug[:12]}",
"content_hash": _digest("demo-content", slug),
"description_text": (
f"{title} bij {employer_name} in {municipality}. "
"Fictieve demovacature voor de VacatureRadar-demo-omgeving."
),
"raw_location": f"{municipality}, {region}",
"region": region,
"municipality": municipality,
"workplace_type": workplace_map[workplace],
"employment_types": ["full_time", "permanent"],
"skills_required": req_skills,
"skills_preferred": pref_skills,
"direct_employer": True,
"recruiter": False,
"extraction_confidence": 0.9,
"analysis_features": {
"support_ratio": 0.1,
"public_sector_signal": 1.0 if "Gemeente" in employer_name else 0.0,
"experience_years_max": 6,
},
"status": JobPosting.Status.ACTIVE,
"first_seen": now - timedelta(days=index + 1),
"last_seen": now,
"date_posted": now - timedelta(days=index + 1),
},
)
created_jobs.append(job)
ScoreRun.objects.create(
job=job,
profile=profile,
profile_version=profile.version,
score=score,
confidence=Decimal("0.78"),
recommendation=recommendation,
components={
"content": float(score) * 0.45,
"skills": float(score) * 0.30,
"location": float(score) * 0.25,
},
positives=positives,
concerns=concerns,
hard_exclusions=[],
evidence={"demo": True},
model_version="demo-seed",
prompt_version="1.0.0",
created_at=now - timedelta(hours=index),
)
# Een kleine sollicitatiepijplijn voor de demo.
if created_jobs:
Application.objects.get_or_create(
user=user,
job=created_jobs[0],
defaults={
"status": Application.Status.INTERVIEW,
"applied_at": now - timedelta(days=6),
"follow_up_date": (now + timedelta(days=2)).date(),
"contact_name": "Sofie Vandael",
"contact_email": "sofie.vandael@cloudnexus.example",
"notes": "Eerste gesprek gepland. Demo-data.",
},
)
if len(created_jobs) > 3:
Application.objects.get_or_create(
user=user,
job=created_jobs[3],
defaults={
"status": Application.Status.APPLIED,
"applied_at": now - timedelta(days=2),
"follow_up_date": (now + timedelta(days=5)).date(),
"notes": "Sollicitatie verstuurd. Demo-data.",
},
)