feat: ship premium IT-focused vacancy radar
deploy / deploy (push) Canceled after 0s

This commit is contained in:
Jens
2026-07-22 15:16:15 +02:00
parent 7d3d026d1d
commit 029df89265
23 changed files with 838 additions and 53 deletions
@@ -159,6 +159,15 @@ def test_job_profile_source_and_application_views(client, user, profile, job, so
assert response.status_code == 302
assert Feedback.objects.filter(user=user, job=job).exists()
unsafe_redirect = client.post(
reverse("jobs:feedback", kwargs={"pk": job.pk}),
{
"action": Feedback.Action.INTERESTING,
"next": "https://malicious.example/steal-session",
},
)
assert unsafe_redirect.url == reverse("jobs:detail", kwargs={"pk": job.pk})
assert client.get(reverse("profiles:list")).status_code == 200
assert client.get(reverse("profiles:edit", kwargs={"pk": profile.pk})).status_code == 200
assert client.get(reverse("sources:list")).status_code == 200
+135
View File
@@ -2,6 +2,9 @@ import pytest
from django.core.cache import cache
from django.test import override_settings
from django.urls import reverse
from django.utils import timezone
from apps.jobs.models import JobPosting, ScoreRun
@pytest.mark.integration
@@ -12,6 +15,14 @@ def test_today_requires_login(client):
assert reverse("login") in response.url
@pytest.mark.integration
@pytest.mark.django_db
def test_browser_favicon_fallback_uses_local_svg(client):
response = client.get("/favicon.ico")
assert response.status_code == 301
assert response.url.endswith("/static/favicon.svg")
@pytest.mark.integration
@pytest.mark.django_db
def test_today_loads_for_user(client, user, profile):
@@ -21,6 +32,130 @@ def test_today_loads_for_user(client, user, profile):
assert "Vandaag" in response.content.decode()
@pytest.mark.integration
@pytest.mark.django_db
def test_job_list_defaults_to_ranked_it_focus_but_keeps_explicit_archive(
client, user, profile, job
):
non_it = JobPosting.objects.create(
employer=job.employer,
original_title="Spontane sollicitatie - logopedist",
normalized_title="spontane sollicitatie logopedist",
canonical_url="https://jobs.example.org/vacatures/logopedist",
canonical_key="9" * 64,
content_hash="8" * 64,
description_text="Zorgfunctie met toegang tot digitale patiëntendossiers.",
status=JobPosting.Status.ACTIVE,
)
misleading_it_title = JobPosting.objects.create(
employer=job.employer,
original_title="IT Recruitment Specialist",
normalized_title="it recruitment specialist",
canonical_url="https://jobs.example.org/vacatures/it-recruitment",
canonical_key="3" * 64,
content_hash="2" * 64,
status=JobPosting.Status.ACTIVE,
)
ScoreRun.objects.create(
job=job,
profile=profile,
profile_version=profile.version,
score=60,
confidence=0.9,
recommendation=ScoreRun.Recommendation.WEAK,
positives=["IT-focus bevestigd via de functietitel."],
)
ScoreRun.objects.create(
job=non_it,
profile=profile,
profile_version=profile.version,
score=99,
confidence=0.9,
recommendation=ScoreRun.Recommendation.POSSIBLE,
)
hidden_it = JobPosting.objects.create(
employer=job.employer,
original_title="Cloud Engineer buiten de zoekradius",
normalized_title="cloud engineer buiten de zoekradius",
canonical_url="https://jobs.example.org/vacatures/cloud-ver",
canonical_key="5" * 64,
content_hash="4" * 64,
status=JobPosting.Status.ACTIVE,
)
ScoreRun.objects.create(
job=hidden_it,
profile=profile,
profile_version=profile.version,
score=98,
confidence=0.9,
recommendation=ScoreRun.Recommendation.HIDDEN,
hard_exclusions=["Buiten de zoekradius."],
)
client.force_login(user)
focused = client.get(reverse("jobs:list"))
focused_body = focused.content.decode()
assert job.original_title in focused_body
assert non_it.original_title not in focused_body
assert misleading_it_title.original_title not in focused_body
assert hidden_it.original_title not in focused_body
assert focused.context["it_job_count"] == 2
assert focused.context["filtered_non_it_count"] == 2
archive = client.get(reverse("jobs:list"), {"focus": "all", "match": "all"})
archive_body = archive.content.decode()
assert non_it.original_title in archive_body
assert misleading_it_title.original_title in archive_body
assert hidden_it.original_title in archive_body
@pytest.mark.integration
@pytest.mark.django_db
def test_today_uses_latest_user_score_and_excludes_non_it(client, user, profile, job):
non_it = JobPosting.objects.create(
employer=job.employer,
original_title="Technieker Facilitaire Diensten",
normalized_title="technieker facilitaire diensten",
canonical_url="https://jobs.example.org/vacatures/facilitair",
canonical_key="7" * 64,
content_hash="6" * 64,
status=JobPosting.Status.ACTIVE,
first_seen=timezone.now(),
)
ScoreRun.objects.create(
job=job,
profile=profile,
profile_version=profile.version,
score=70,
confidence=0.9,
recommendation=ScoreRun.Recommendation.POSSIBLE,
)
ScoreRun.objects.create(
job=job,
profile=profile,
profile_version=profile.version,
score=66,
confidence=0.9,
recommendation=ScoreRun.Recommendation.POSSIBLE,
)
ScoreRun.objects.create(
job=non_it,
profile=profile,
profile_version=profile.version,
score=95,
confidence=0.9,
recommendation=ScoreRun.Recommendation.STRONG,
)
client.force_login(user)
response = client.get(reverse("dashboard:today"))
cards = list(response.context["score_cards"])
assert [card.job_id for card in cards] == [job.id]
assert cards[0].score == 66
assert response.context["filtered_non_it_jobs"] == 1
@pytest.mark.integration
@pytest.mark.django_db
@override_settings(
+39
View File
@@ -0,0 +1,39 @@
from apps.jobs.services.relevance import assess_it_relevance, profile_requires_it_focus
def test_title_led_it_relevance_accepts_clear_it_roles():
for title in (
"Modern Workplace Engineer",
"Product owner ICT (100%)",
"Functioneel Analist",
"Senior .Net Developer",
"Senior Mobile Developer",
"Deeltijds docent informatica, networking & security",
):
assessment = assess_it_relevance(title)
assert assessment.relevant, title
assert assessment.signals
def test_title_led_it_relevance_rejects_non_it_and_ambiguous_roles():
for title in (
"Technieker Facilitaire Diensten",
"Spontane sollicitatie - logopedist",
"Doctoraatsbursaal trombo-inflammatie bij hemodialyse",
"Stafmedewerker Onderwijs faculteit Rechten",
"Project Manager",
"Business Developer",
"IT Recruitment Specialist",
):
assessment = assess_it_relevance(title)
assert not assessment.relevant, title
assert assessment.reason
def test_active_it_profile_requires_deterministic_it_focus(profile):
assert profile_requires_it_focus(profile)
profile.name = "Algemeen profiel"
profile.desired_titles = ["verpleegkundige"]
profile.desired_skills = []
assert not profile_requires_it_focus(profile)
+12
View File
@@ -63,6 +63,18 @@ def test_hard_title_exclusion_wins(matching_job, profile):
assert any("sales" in reason.lower() for reason in result.hard_exclusions)
def test_non_it_vacancy_is_hidden_for_it_profile(matching_job, profile):
matching_job.original_title = "Spontane sollicitatie - logopedist"
matching_job.normalized_title = "spontane sollicitatie logopedist"
matching_job.description_text = "Werk met het digitale patiëntendossier in ons ziekenhuis."
result = calculate_score(matching_job, profile)
assert result.recommendation == ScoreRun.Recommendation.HIDDEN
assert any("Geen aantoonbaar IT-signaal" in reason for reason in result.hard_exclusions)
assert result.evidence["it_relevance"]["relevant"] is False
def test_distance_boundary_inclusief(matching_job, profile, monkeypatch):
profile.max_distance_km = 50
profile.save(update_fields=["max_distance_km"])