@@ -0,0 +1,238 @@
|
||||
# Generated by Django 5.2.16 on 2026-07-20 23:57
|
||||
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
import uuid
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('profiles', '0001_initial'),
|
||||
('sources', '0001_initial'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Employer',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('updated_at', models.DateTimeField(auto_now=True)),
|
||||
('name', models.CharField(max_length=300)),
|
||||
('normalized_name', models.CharField(db_index=True, max_length=300)),
|
||||
('domain', models.CharField(blank=True, db_index=True, max_length=255)),
|
||||
('is_direct_employer', models.BooleanField(default=True)),
|
||||
('is_recruiter', models.BooleanField(default=False)),
|
||||
('employer_type', models.CharField(blank=True, max_length=80)),
|
||||
('confidence', models.DecimalField(decimal_places=3, default=0.5, max_digits=4)),
|
||||
('aliases', models.JSONField(blank=True, default=list)),
|
||||
('metadata', models.JSONField(blank=True, default=dict)),
|
||||
],
|
||||
options={
|
||||
'ordering': ['name'],
|
||||
'constraints': [models.UniqueConstraint(fields=('normalized_name', 'domain'), name='unique_employer_name_domain')],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='JobPosting',
|
||||
fields=[
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('updated_at', models.DateTimeField(auto_now=True)),
|
||||
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
|
||||
('original_title', models.CharField(max_length=500)),
|
||||
('normalized_title', models.CharField(db_index=True, max_length=500)),
|
||||
('job_family', models.CharField(blank=True, db_index=True, max_length=160)),
|
||||
('seniority', models.CharField(blank=True, max_length=80)),
|
||||
('language', models.CharField(blank=True, max_length=16)),
|
||||
('canonical_url', models.URLField(blank=True, db_index=True, max_length=2000)),
|
||||
('canonical_key', models.CharField(max_length=64, unique=True)),
|
||||
('content_hash', models.CharField(db_index=True, max_length=64)),
|
||||
('description_html_sanitized', models.TextField(blank=True)),
|
||||
('description_text', models.TextField(blank=True)),
|
||||
('requirements', models.JSONField(blank=True, default=list)),
|
||||
('benefits', models.JSONField(blank=True, default=list)),
|
||||
('raw_location', models.CharField(blank=True, max_length=500)),
|
||||
('country', models.CharField(blank=True, max_length=120)),
|
||||
('region', models.CharField(blank=True, db_index=True, max_length=160)),
|
||||
('municipality', models.CharField(blank=True, db_index=True, max_length=160)),
|
||||
('postal_code', models.CharField(blank=True, max_length=24)),
|
||||
('latitude', models.DecimalField(blank=True, decimal_places=6, max_digits=9, null=True)),
|
||||
('longitude', models.DecimalField(blank=True, decimal_places=6, max_digits=9, null=True)),
|
||||
('workplace_type', models.CharField(choices=[('on_site', 'Op locatie'), ('hybrid', 'Hybride'), ('remote', 'Remote'), ('unknown', 'Onbekend')], default='unknown', max_length=20)),
|
||||
('employment_types', models.JSONField(blank=True, default=list)),
|
||||
('hours_text', models.CharField(blank=True, max_length=200)),
|
||||
('compensation', models.JSONField(blank=True, default=dict)),
|
||||
('skills_required', models.JSONField(blank=True, default=list)),
|
||||
('skills_preferred', models.JSONField(blank=True, default=list)),
|
||||
('analysis_features', models.JSONField(blank=True, default=dict)),
|
||||
('date_posted', models.DateTimeField(blank=True, null=True)),
|
||||
('valid_through', models.DateTimeField(blank=True, db_index=True, null=True)),
|
||||
('first_seen', models.DateTimeField(default=django.utils.timezone.now)),
|
||||
('last_seen', models.DateTimeField(db_index=True, default=django.utils.timezone.now)),
|
||||
('last_changed', models.DateTimeField(default=django.utils.timezone.now)),
|
||||
('status', models.CharField(choices=[('new', 'Nieuw'), ('active', 'Actief'), ('uncertain', 'Onzeker'), ('expired', 'Verlopen'), ('removed', 'Verwijderd'), ('duplicate', 'Duplicaat'), ('quarantined', 'Quarantaine')], db_index=True, default='new', max_length=20)),
|
||||
('direct_employer', models.BooleanField(default=True)),
|
||||
('recruiter', models.BooleanField(default=False)),
|
||||
('extraction_confidence', models.DecimalField(decimal_places=3, default=0.5, max_digits=4)),
|
||||
('duplicate_of', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='duplicates', to='jobs.jobposting')),
|
||||
('employer', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='jobs', to='jobs.employer')),
|
||||
],
|
||||
options={
|
||||
'ordering': ['-first_seen'],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Feedback',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('updated_at', models.DateTimeField(auto_now=True)),
|
||||
('action', models.CharField(choices=[('interesting', 'Interessant'), ('save', 'Bewaren'), ('hide', 'Verbergen'), ('applied', 'Gesolliciteerd'), ('undo', 'Ongedaan maken')], max_length=20)),
|
||||
('reason', models.CharField(blank=True, max_length=200)),
|
||||
('metadata', models.JSONField(blank=True, default=dict)),
|
||||
('profile', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='profiles.searchprofile')),
|
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||
('job', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='feedback', to='jobs.jobposting')),
|
||||
],
|
||||
options={
|
||||
'ordering': ['-created_at'],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Application',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('updated_at', models.DateTimeField(auto_now=True)),
|
||||
('status', models.CharField(choices=[('preparing', 'Voorbereiden'), ('applied', 'Verzonden'), ('interview', 'Gesprek'), ('offer', 'Aanbod'), ('rejected', 'Afgewezen'), ('withdrawn', 'Ingetrokken')], default='preparing', max_length=20)),
|
||||
('applied_at', models.DateTimeField(blank=True, null=True)),
|
||||
('follow_up_date', models.DateField(blank=True, null=True)),
|
||||
('contact_name', models.CharField(blank=True, max_length=200)),
|
||||
('contact_email', models.EmailField(blank=True, max_length=254)),
|
||||
('notes', models.TextField(blank=True)),
|
||||
('document_manifest', models.JSONField(blank=True, default=list)),
|
||||
('snapshot', models.JSONField(blank=True, default=dict)),
|
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||
('job', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='applications', to='jobs.jobposting')),
|
||||
],
|
||||
options={
|
||||
'ordering': ['-updated_at'],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='JobSourceAlias',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('updated_at', models.DateTimeField(auto_now=True)),
|
||||
('url', models.URLField(max_length=2000)),
|
||||
('canonical_url', models.URLField(db_index=True, max_length=2000)),
|
||||
('external_id', models.CharField(blank=True, db_index=True, max_length=500)),
|
||||
('source_title', models.CharField(blank=True, max_length=500)),
|
||||
('source_employer', models.CharField(blank=True, max_length=300)),
|
||||
('extraction_method', models.CharField(blank=True, max_length=120)),
|
||||
('extraction_confidence', models.DecimalField(decimal_places=3, default=0.5, max_digits=4)),
|
||||
('first_seen', models.DateTimeField(default=django.utils.timezone.now)),
|
||||
('last_seen', models.DateTimeField(default=django.utils.timezone.now)),
|
||||
('is_canonical', models.BooleanField(default=False)),
|
||||
('payload', models.JSONField(blank=True, default=dict)),
|
||||
('job', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='source_aliases', to='jobs.jobposting')),
|
||||
('raw_document', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='job_aliases', to='sources.rawdocument')),
|
||||
('source', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='job_aliases', to='sources.source')),
|
||||
],
|
||||
options={
|
||||
'ordering': ['-is_canonical', '-last_seen'],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='FieldProvenance',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('updated_at', models.DateTimeField(auto_now=True)),
|
||||
('field_name', models.CharField(max_length=120)),
|
||||
('extraction_method', models.CharField(max_length=120)),
|
||||
('confidence', models.DecimalField(decimal_places=3, default=0.5, max_digits=4)),
|
||||
('evidence_excerpt', models.CharField(blank=True, max_length=1000)),
|
||||
('parser_version', models.CharField(blank=True, max_length=80)),
|
||||
('job', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='provenance', to='jobs.jobposting')),
|
||||
('source_alias', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='field_evidence', to='jobs.jobsourcealias')),
|
||||
],
|
||||
options={
|
||||
'ordering': ['field_name', '-confidence'],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='JobVersion',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('updated_at', models.DateTimeField(auto_now=True)),
|
||||
('content_hash', models.CharField(max_length=64)),
|
||||
('snapshot', models.JSONField(default=dict)),
|
||||
('changed_fields', models.JSONField(blank=True, default=list)),
|
||||
('job', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='versions', to='jobs.jobposting')),
|
||||
],
|
||||
options={
|
||||
'ordering': ['-created_at'],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='ScoreRun',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('updated_at', models.DateTimeField(auto_now=True)),
|
||||
('profile_version', models.PositiveIntegerField()),
|
||||
('score', models.DecimalField(decimal_places=2, max_digits=5)),
|
||||
('confidence', models.DecimalField(decimal_places=3, max_digits=4)),
|
||||
('recommendation', models.CharField(choices=[('strong', 'Sterke match'), ('possible', 'Mogelijke match'), ('weak', 'Lage match'), ('hidden', 'Verborgen')], max_length=16)),
|
||||
('components', models.JSONField(default=dict)),
|
||||
('positives', models.JSONField(default=list)),
|
||||
('concerns', models.JSONField(default=list)),
|
||||
('hard_exclusions', models.JSONField(default=list)),
|
||||
('evidence', models.JSONField(blank=True, default=dict)),
|
||||
('model_version', models.CharField(blank=True, max_length=120)),
|
||||
('prompt_version', models.CharField(blank=True, max_length=120)),
|
||||
('job', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='scores', to='jobs.jobposting')),
|
||||
('profile', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='scores', to='profiles.searchprofile')),
|
||||
],
|
||||
options={
|
||||
'ordering': ['-created_at'],
|
||||
},
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='jobposting',
|
||||
index=models.Index(fields=['normalized_title', 'region'], name='jobs_jobpos_normali_f8851c_idx'),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='jobposting',
|
||||
index=models.Index(fields=['status', 'valid_through'], name='jobs_jobpos_status_4c49ee_idx'),
|
||||
),
|
||||
migrations.AddConstraint(
|
||||
model_name='application',
|
||||
constraint=models.UniqueConstraint(fields=('user', 'job'), name='unique_application_per_user_job'),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='jobsourcealias',
|
||||
index=models.Index(fields=['source', 'external_id'], name='jobs_jobsou_source__66ce6b_idx'),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='jobsourcealias',
|
||||
index=models.Index(fields=['canonical_url'], name='jobs_jobsou_canonic_9fd8cb_idx'),
|
||||
),
|
||||
migrations.AddConstraint(
|
||||
model_name='jobversion',
|
||||
constraint=models.UniqueConstraint(fields=('job', 'content_hash'), name='unique_job_content_version'),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='scorerun',
|
||||
index=models.Index(fields=['profile', '-score', '-created_at'], name='jobs_scorer_profile_5fca7f_idx'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,67 @@
|
||||
# Generated by Django 5.2.16 on 2026-07-21 for VR-104
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
from decimal import Decimal
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("jobs", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="GeocodeLocationLookup",
|
||||
fields=[
|
||||
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
|
||||
("created_at", models.DateTimeField(auto_now_add=True)),
|
||||
("updated_at", models.DateTimeField(auto_now=True)),
|
||||
("source_name", models.CharField(max_length=120)),
|
||||
("source_version", models.CharField(max_length=80)),
|
||||
("source_license_name", models.CharField(blank=True, max_length=160)),
|
||||
("source_license_url", models.URLField(blank=True, max_length=500)),
|
||||
("source_metadata", models.JSONField(blank=True, default=dict)),
|
||||
("query_kind", models.CharField(choices=[("postal", "Postcode"), ("municipality", "Gemeente")], max_length=12)),
|
||||
("query_value", models.CharField(max_length=200, db_index=True)),
|
||||
("postal_code", models.CharField(db_index=True, max_length=12)),
|
||||
("municipality", models.CharField(db_index=True, max_length=200)),
|
||||
("region", models.CharField(blank=True, max_length=160)),
|
||||
(
|
||||
"latitude",
|
||||
models.DecimalField(decimal_places=6, max_digits=9),
|
||||
),
|
||||
(
|
||||
"longitude",
|
||||
models.DecimalField(decimal_places=6, max_digits=9),
|
||||
),
|
||||
("confidence", models.DecimalField(decimal_places=2, default=Decimal("0.85"), max_digits=3)),
|
||||
],
|
||||
options={
|
||||
"ordering": ["query_kind", "query_value", "municipality"],
|
||||
},
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name="geocodelocationlookup",
|
||||
index=models.Index(fields=["query_kind", "query_value"], name="geocode_lookup_kind_query_idx"),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name="geocodelocationlookup",
|
||||
index=models.Index(fields=["source_name", "source_version"], name="geocode_lookup_source_idx"),
|
||||
),
|
||||
migrations.AddConstraint(
|
||||
model_name="geocodelocationlookup",
|
||||
constraint=models.UniqueConstraint(
|
||||
fields=[
|
||||
"source_name",
|
||||
"source_version",
|
||||
"query_kind",
|
||||
"query_value",
|
||||
"postal_code",
|
||||
"municipality",
|
||||
],
|
||||
name="unique_geocode_lookup_row",
|
||||
),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,60 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("jobs", "0002_geocode_location_lookup"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="AiAnalysisCache",
|
||||
fields=[
|
||||
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
|
||||
("created_at", models.DateTimeField(auto_now_add=True)),
|
||||
("updated_at", models.DateTimeField(auto_now=True)),
|
||||
("content_hash", models.CharField(max_length=64, db_index=True)),
|
||||
("model_name", models.CharField(max_length=120)),
|
||||
("prompt_version", models.CharField(max_length=120)),
|
||||
("schema_version", models.CharField(default="1.0.0", max_length=24)),
|
||||
(
|
||||
"status",
|
||||
models.CharField(
|
||||
choices=[
|
||||
("ok", "Succes"),
|
||||
("disabled", "Uitgeschakeld"),
|
||||
("error", "Fout"),
|
||||
("timeout", "Timeout"),
|
||||
("invalid", "Ongeldige output"),
|
||||
],
|
||||
max_length=16,
|
||||
),
|
||||
),
|
||||
("error_category", models.CharField(blank=True, max_length=120)),
|
||||
("summary_nl", models.TextField(blank=True)),
|
||||
("features", models.JSONField(default=dict)),
|
||||
("warnings", models.JSONField(default=list, blank=True)),
|
||||
],
|
||||
options={
|
||||
"ordering": ["-updated_at"],
|
||||
},
|
||||
),
|
||||
migrations.AddConstraint(
|
||||
model_name="aianalysiscache",
|
||||
constraint=models.UniqueConstraint(
|
||||
fields=["content_hash", "model_name", "prompt_version", "schema_version"],
|
||||
name="unique_ai_analysis_cache",
|
||||
),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name="aianalysiscache",
|
||||
index=models.Index(fields=["content_hash"], name="jobs_aianal_cache_content_idx"),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name="aianalysiscache",
|
||||
index=models.Index(fields=["model_name", "prompt_version", "schema_version"], name="jobs_aianal_cache_model_idx"),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,72 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import django.conf
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("jobs", "0003_ai_analysis_cache"),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="application",
|
||||
name="contact_metadata",
|
||||
field=models.JSONField(blank=True, default=dict),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="ApplicationTimelineEvent",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
|
||||
),
|
||||
),
|
||||
("created_at", models.DateTimeField(auto_now_add=True)),
|
||||
("updated_at", models.DateTimeField(auto_now=True)),
|
||||
(
|
||||
"event_type",
|
||||
models.CharField(
|
||||
choices=[
|
||||
("created", "Aangemaakt"),
|
||||
("status_changed", "Status gewijzigd"),
|
||||
("notes_updated", "Notitie bijgewerkt"),
|
||||
("contact_updated", "Contact bijgewerkt"),
|
||||
("snapshot_captured", "Snapshot vastgelegd"),
|
||||
],
|
||||
max_length=20,
|
||||
),
|
||||
),
|
||||
("metadata", models.JSONField(blank=True, default=dict)),
|
||||
(
|
||||
"application",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="timeline_events",
|
||||
to="jobs.application",
|
||||
),
|
||||
),
|
||||
(
|
||||
"user",
|
||||
models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=django.conf.settings.AUTH_USER_MODEL),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"ordering": ["-created_at"],
|
||||
},
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name="applicationtimelineevent",
|
||||
index=models.Index(fields=["application", "created_at"], name="jobs_app_timeline_app_idx"),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name="applicationtimelineevent",
|
||||
index=models.Index(fields=["user", "created_at"], name="jobs_app_timeline_user_idx"),
|
||||
),
|
||||
]
|
||||
Reference in New Issue
Block a user