Initial deploy setup
deploy / deploy (push) Canceled after 0s

This commit is contained in:
Jens
2026-07-21 14:00:00 +02:00
commit b8091e59bd
285 changed files with 27854 additions and 0 deletions
+238
View File
@@ -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"),
),
]
View File