feat: personalize scoring and add skills radar

This commit is contained in:
Jens
2026-07-22 19:33:29 +02:00
parent 029df89265
commit a30ecbc553
33 changed files with 1529 additions and 114 deletions
+24 -56
View File
@@ -5,55 +5,12 @@ import re
from django import forms
from .models import SearchProfile
TITLE_CHOICES = (
("system engineer", "System engineer"),
("infrastructure engineer", "Infrastructure engineer"),
("cloud engineer", "Cloud engineer"),
("devops engineer", "DevOps engineer"),
("network engineer", "Network engineer"),
("security engineer", "Security engineer"),
("workplace engineer", "Workplace engineer"),
("support engineer", "Support engineer"),
("software engineer", "Software engineer"),
("data engineer", "Data engineer"),
("solution architect", "Solution architect"),
("project manager", "Projectmanager"),
)
EXCLUDED_TITLE_CHOICES = (
("sales", "Sales"),
("recruiter", "Recruiter"),
("account manager", "Accountmanager"),
("callcenter", "Callcenter"),
("stage", "Stage"),
("student", "Studentenjob"),
)
SKILL_CHOICES = (
("azure", "Microsoft Azure"),
("aws", "AWS"),
("microsoft 365", "Microsoft 365"),
("active directory", "Active Directory"),
("entra id", "Entra ID"),
("intune", "Microsoft Intune"),
("linux", "Linux"),
("windows server", "Windows Server"),
("networking", "Netwerken"),
("security", "Security"),
("python", "Python"),
("powershell", "PowerShell"),
("terraform", "Terraform"),
("docker", "Docker"),
("kubernetes", "Kubernetes"),
("ci/cd", "CI/CD"),
)
EXCLUDED_SKILL_CHOICES = (
("cold calling", "Cold calling"),
("door to door", "Deur-aan-deurverkoop"),
("commission only", "Alleen commissieloon"),
("night shift", "Nachtwerk"),
from .taxonomy import (
EXCLUDED_SKILL_CHOICES,
EXCLUDED_TITLE_CHOICES,
SKILL_CHOICES,
TITLE_CHOICES,
iter_choices,
)
EMPLOYMENT_CHOICES = (
@@ -91,27 +48,31 @@ class SearchProfileForm(forms.ModelForm):
label="Gewenste functietitels",
required=False,
choices=TITLE_CHOICES,
widget=forms.CheckboxSelectMultiple,
help_text="Selecteer alle rollen die bij je zoekrichting passen.",
widget=forms.CheckboxSelectMultiple(attrs={"class": "grouped-choices"}),
help_text="Selecteer alle rollen die bij je zoekrichting passen; de beste titelmatch telt.",
)
excluded_titles = forms.MultipleChoiceField(
label="Uitgesloten functietitels",
required=False,
choices=EXCLUDED_TITLE_CHOICES,
widget=forms.CheckboxSelectMultiple,
widget=forms.CheckboxSelectMultiple(attrs={"class": "grouped-choices"}),
help_text="Vacatures met deze titelwoorden worden hard uitgesloten.",
)
desired_skills = forms.MultipleChoiceField(
label="Gewenste skills",
required=False,
choices=SKILL_CHOICES,
widget=forms.CheckboxSelectMultiple,
widget=forms.CheckboxSelectMultiple(attrs={"class": "grouped-choices"}),
help_text=(
"Kies je herkenbare cv-skills ruim. Maximaal vier aangetroffen skills volstaan voor "
"de volledige skillcomponent; extra keuzes verwateren je score niet."
),
)
excluded_skills = forms.MultipleChoiceField(
label="Uitgesloten kenmerken",
required=False,
choices=EXCLUDED_SKILL_CHOICES,
widget=forms.CheckboxSelectMultiple,
widget=forms.CheckboxSelectMultiple(attrs={"class": "grouped-choices"}),
)
allowed_employment_types = forms.MultipleChoiceField(
label="Toegestane contractvormen",
@@ -145,6 +106,7 @@ class SearchProfileForm(forms.ModelForm):
"is_active",
"home_postal_code",
"max_distance_km",
"experience_years",
"desired_titles",
"excluded_titles",
"desired_skills",
@@ -163,6 +125,7 @@ class SearchProfileForm(forms.ModelForm):
"is_active": "Actief profiel",
"home_postal_code": "Thuispostcode",
"max_distance_km": "Maximale afstand (km)",
"experience_years": "Relevante IT-ervaring",
"recommendation_threshold": "Aanbevelingsdrempel",
"top_match_threshold": "Topmatchdrempel",
"digest_time": "Tijdstip dagelijkse samenvatting",
@@ -173,6 +136,10 @@ class SearchProfileForm(forms.ModelForm):
"Vier cijfers volstaan. Gemeente en coördinaten worden veilig afgeleid wanneer "
"lokale geodata beschikbaar is."
),
"experience_years": (
"Optionele profielcontext. Deze waarde telt nooit mee voor je matchscore en "
"kan een vacature niet uitsluiten."
),
"learning_enabled": "Past alleen zachte voorkeuren aan; harde regels blijven vast.",
}
widgets = {
@@ -203,14 +170,15 @@ class SearchProfileForm(forms.ModelForm):
field = self.fields[field_name]
stored = getattr(self.instance, field_name, [])
existing_by_casefold = {
str(value).casefold(): str(value) for value, _label in field.choices
str(value).casefold(): str(value) for value, _label in iter_choices(field.choices)
}
extra = [
(str(value), str(value))
for value in stored
if str(value).casefold() not in existing_by_casefold
]
field.choices = [*field.choices, *extra]
if extra:
field.choices = [*field.choices, ("Eerder opgeslagen", tuple(extra))]
field.initial = [
existing_by_casefold.get(str(value).casefold(), str(value)) for value in stored
]