Skip to content
789 changes: 789 additions & 0 deletions api/management/commands/sync_molnix_appraisals.py

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion assets
Submodule assets updated 1 files
+2,484 −1,227 openapi-schema.yaml
70 changes: 68 additions & 2 deletions deployments/drf_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from drf_spectacular.utils import extend_schema
from openpyxl import Workbook
from rest_framework import viewsets
from rest_framework.authentication import TokenAuthentication
from rest_framework.authentication import SessionAuthentication, TokenAuthentication
from rest_framework.decorators import action
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
Expand All @@ -31,7 +31,15 @@
from main.serializers import CsvListMixin
from main.utils import is_tableau

from .filters import EmergencyProjectFilter, ERUOwnerFilter, ProjectFilter
from .filters import (
EmergencyProjectFilter,
ERUOwnerFilter,
MolnixAppraisalFilter,
MolnixAppraiserFilter,
ProjectFilter,
RrmsEventParticipationFilter,
RrmsPersonSnapshotFilter,
)
from .models import (
ERU,
EmergencyProject,
Expand All @@ -41,13 +49,17 @@
ERUReadiness,
ERUReadinessType,
ERUType,
MolnixAppraisal,
MolnixAppraiser,
OperationTypes,
PartnerSocietyDeployment,
Personnel,
PersonnelDeployment,
ProgrammeTypes,
Project,
RegionalProject,
RrmsEventParticipation,
RrmsPersonSnapshot,
Sector,
Statuses,
)
Expand All @@ -65,6 +77,8 @@
GlobalProjectNSOngoingProjectsStatsSerializer,
GlobalProjectOverviewSerializer,
MiniERUReadinessTypeSerializer,
MolnixAppraisalSerializer,
MolnixAppraiserSerializer,
PartnerDeploymentSerializer,
PartnerDeploymentTableauSerializer,
PersonnelCsvSerializer,
Expand All @@ -77,6 +91,8 @@
ProjectRegionOverviewSerializer,
ProjectSerializer,
RegionalProjectSerializer,
RrmsEventParticipationSerializer,
RrmsPersonSnapshotSerializer,
)
from .utils import get_previous_months

Expand Down Expand Up @@ -410,6 +426,56 @@ def get_renderer_context(self):
return context


@extend_schema(
request=None,
responses=MolnixAppraisalSerializer(many=True),
)
class MolnixAppraisalViewset(viewsets.ReadOnlyModelViewSet):
authentication_classes = (SessionAuthentication, TokenAuthentication)
permission_classes = (IsAuthenticated, DenyGuestUserPermission)
queryset = MolnixAppraisal.objects.all()
serializer_class = MolnixAppraisalSerializer
filterset_class = MolnixAppraisalFilter
ordering_fields = ("updated_at", "created_at", "molnix_id", "appraised_person_id")


@extend_schema(
request=None,
responses=MolnixAppraiserSerializer(many=True),
)
class MolnixAppraiserViewset(viewsets.ReadOnlyModelViewSet):
authentication_classes = (SessionAuthentication, TokenAuthentication)
permission_classes = (IsAuthenticated, DenyGuestUserPermission)
queryset = MolnixAppraiser.objects.all()
serializer_class = MolnixAppraiserSerializer
filterset_class = MolnixAppraiserFilter
ordering_fields = ("updated_at", "created_at", "molnix_id", "appraisal_molnix_id")


@extend_schema(
request=None,
responses=RrmsPersonSnapshotSerializer(many=True),
)
class RrmsPersonSnapshotViewset(viewsets.ReadOnlyModelViewSet):
authentication_classes = (SessionAuthentication, TokenAuthentication)
permission_classes = (IsAuthenticated, DenyGuestUserPermission)
queryset = RrmsPersonSnapshot.objects.all()
serializer_class = RrmsPersonSnapshotSerializer
filterset_class = RrmsPersonSnapshotFilter
ordering_fields = ("source_updated_at", "person_id")


@extend_schema(
request=None,
responses=RrmsEventParticipationSerializer(many=True),
)
class RrmsEventParticipationViewset(viewsets.ReadOnlyModelViewSet):
queryset = RrmsEventParticipation.objects.all()
serializer_class = RrmsEventParticipationSerializer
filterset_class = RrmsEventParticipationFilter
ordering_fields = ("event_from", "event_to", "event_id", "person_id")


class AggregateDeployments(APIView):
"""
Get aggregated data for personnel deployments
Expand Down
57 changes: 57 additions & 0 deletions deployments/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,13 @@
EmergencyProjectActivitySector,
ERUOwner,
ERUType,
MolnixAppraisal,
MolnixAppraiser,
OperationTypes,
ProgrammeTypes,
Project,
RrmsEventParticipation,
RrmsPersonSnapshot,
Sector,
SectorTag,
Statuses,
Expand Down Expand Up @@ -161,3 +165,56 @@ def qs(self):
if available is not None:
eru_qs = eru_qs.filter(available=available)
return qs.filter(eru__in=eru_qs).distinct()


class MolnixAppraisalFilter(filters.FilterSet):
appraised_person_id = filters.NumberFilter(field_name="appraised_person_id", lookup_expr="exact")
molnix_id = filters.NumberFilter(field_name="molnix_id", lookup_expr="exact")
deployment_molnix_id = filters.NumberFilter(field_name="deployment_molnix_id", lookup_expr="exact")
stage = filters.CharFilter(field_name="stage", lookup_expr="exact")

class Meta:
model = MolnixAppraisal
fields = {
"updated_at": ("exact", "gt", "gte", "lt", "lte"),
"created_at": ("exact", "gt", "gte", "lt", "lte"),
}


class MolnixAppraiserFilter(filters.FilterSet):
appraisal_molnix_id = filters.NumberFilter(field_name="appraisal_molnix_id", lookup_expr="exact")
person_id = filters.NumberFilter(field_name="person_id", lookup_expr="exact")
appraiser_type = filters.CharFilter(field_name="appraiser_type", lookup_expr="exact")

class Meta:
model = MolnixAppraiser
fields = {
"updated_at": ("exact", "gt", "gte", "lt", "lte"),
"created_at": ("exact", "gt", "gte", "lt", "lte"),
}


class RrmsPersonSnapshotFilter(filters.FilterSet):
person_id = filters.NumberFilter(field_name="person_id", lookup_expr="exact")
organization_id = filters.NumberFilter(field_name="organization_id", lookup_expr="exact")
person_status = filters.CharFilter(field_name="person_status", lookup_expr="exact")

class Meta:
model = RrmsPersonSnapshot
fields = {
"source_updated_at": ("exact", "gt", "gte", "lt", "lte"),
}


class RrmsEventParticipationFilter(filters.FilterSet):
event_id = filters.NumberFilter(field_name="event_id", lookup_expr="exact")
person_id = filters.NumberFilter(field_name="person_id", lookup_expr="exact")
event_person_role = filters.CharFilter(field_name="event_person_role", lookup_expr="exact")
event_type = filters.CharFilter(field_name="event_type", lookup_expr="exact")

class Meta:
model = RrmsEventParticipation
fields = {
"event_from": ("exact", "gt", "gte", "lt", "lte"),
"event_to": ("exact", "gt", "gte", "lt", "lte"),
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
# Generated by Django 4.2.29 on 2026-04-10 11:39

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('deployments', '0093_sector_title_ar_sector_title_en_sector_title_es_and_more'),
]

operations = [
migrations.CreateModel(
name='MolnixAppraisal',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('molnix_id', models.BigIntegerField(unique=True)),
('target_id', models.BigIntegerField()),
('deployment_molnix_id', models.BigIntegerField(blank=True, null=True)),
('stage', models.CharField(blank=True, max_length=64, null=True)),
('appraisers_count', models.IntegerField(blank=True, null=True)),
('score', models.DecimalField(blank=True, decimal_places=3, max_digits=7, null=True)),
('deployment_country_id', models.IntegerField(blank=True, null=True)),
('deployment_start', models.DateTimeField(blank=True, null=True)),
('deployment_end', models.DateTimeField(blank=True, null=True)),
('deployment_title', models.CharField(blank=True, max_length=255, null=True)),
('sending_organization_id', models.BigIntegerField(blank=True, null=True)),
('receiving_organization_id', models.BigIntegerField(blank=True, null=True)),
('deployment_tags_json', models.JSONField(blank=True, null=True)),
('competencies_json', models.JSONField(blank=True, null=True)),
('created_at', models.DateTimeField(blank=True, null=True)),
('updated_at', models.DateTimeField(blank=True, null=True)),
],
options={
'verbose_name': 'Molnix Appraisal',
'verbose_name_plural': 'Molnix Appraisals',
},
),
migrations.CreateModel(
name='MolnixAppraiser',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('molnix_id', models.BigIntegerField(unique=True)),
('appraisal_molnix_id', models.BigIntegerField()),
('appraiser_type', models.CharField(blank=True, max_length=32, null=True)),
('person_id', models.BigIntegerField(blank=True, null=True)),
('required', models.BooleanField(blank=True, null=True)),
('notified_at', models.DateTimeField(blank=True, null=True)),
('completed_at', models.DateTimeField(blank=True, null=True)),
('created_at', models.DateTimeField(blank=True, null=True)),
('updated_at', models.DateTimeField(blank=True, null=True)),
],
options={
'verbose_name': 'Molnix Appraiser',
'verbose_name_plural': 'Molnix Appraisers',
},
),
migrations.CreateModel(
name='RrmsPersonSnapshot',
fields=[
('person_id', models.BigIntegerField(primary_key=True, serialize=False)),
('person_status', models.CharField(blank=True, max_length=32, null=True)),
('sex', models.CharField(blank=True, max_length=32, null=True)),
('current_availability', models.CharField(blank=True, max_length=64, null=True)),
('outofscope', models.BooleanField(blank=True, null=True)),
('organization_id', models.BigIntegerField(blank=True, null=True)),
('organization_name', models.CharField(blank=True, max_length=255, null=True)),
('roles_json', models.JSONField(blank=True, null=True)),
('languages_json', models.JSONField(blank=True, null=True)),
('tags_json', models.JSONField(blank=True, null=True)),
('source_updated_at', models.DateTimeField(blank=True, null=True)),
('personnel', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='rrms_person_snapshots', to='deployments.personnel')),
],
options={
'verbose_name': 'RRMS Person Snapshot',
'verbose_name_plural': 'RRMS Person Snapshots',
'db_table': 'rrms_person_snapshot',
},
),
migrations.CreateModel(
name='RrmsEventParticipation',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('event_id', models.BigIntegerField()),
('event_name', models.CharField(blank=True, max_length=255, null=True)),
('person_id', models.BigIntegerField()),
('event_person_role', models.CharField(blank=True, max_length=128, null=True)),
('event_type', models.CharField(blank=True, max_length=128, null=True)),
('event_scale_type', models.CharField(blank=True, max_length=128, null=True)),
('event_from', models.DateTimeField(blank=True, null=True)),
('event_to', models.DateTimeField(blank=True, null=True)),
('participant_start', models.DateTimeField(blank=True, null=True)),
('participant_end', models.DateTimeField(blank=True, null=True)),
('requested', models.BooleanField(blank=True, null=True)),
('event_organization_id', models.BigIntegerField(blank=True, null=True)),
('event_organization_name', models.CharField(blank=True, max_length=255, null=True)),
('venue', models.CharField(blank=True, max_length=255, null=True)),
('tags_json', models.JSONField(blank=True, null=True)),
],
options={
'verbose_name': 'RRMS Event Participation',
'verbose_name_plural': 'RRMS Event Participation',
'db_table': 'rrms_event_participation',
'indexes': [models.Index(fields=['event_id'], name='rrms_event_id_idx'), models.Index(fields=['person_id'], name='rrms_event_person_idx')],
},
),
migrations.AddConstraint(
model_name='rrmseventparticipation',
constraint=models.UniqueConstraint(fields=('event_id', 'person_id', 'event_person_role'), name='rrms_event_person_role_uniq'),
),
migrations.AddField(
model_name='molnixappraiser',
name='appraisal',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='appraisers', to='deployments.molnixappraisal'),
),
migrations.AddField(
model_name='molnixappraiser',
name='personnel',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='molnix_appraisers', to='deployments.personnel'),
),
migrations.AddField(
model_name='molnixappraisal',
name='personnel',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='molnix_appraisals', to='deployments.personnel'),
),
migrations.AddIndex(
model_name='rrmspersonsnapshot',
index=models.Index(fields=['organization_id'], name='rrms_person_org_idx'),
),
migrations.AddIndex(
model_name='rrmspersonsnapshot',
index=models.Index(fields=['personnel'], name='rrms_personnel_idx'),
),
migrations.AddIndex(
model_name='molnixappraiser',
index=models.Index(fields=['appraisal_molnix_id'], name='molnix_appr_mol_idx'),
),
migrations.AddIndex(
model_name='molnixappraiser',
index=models.Index(fields=['appraisal'], name='molnix_appr_idx'),
),
migrations.AddIndex(
model_name='molnixappraiser',
index=models.Index(fields=['person_id'], name='molnix_appr_person_idx'),
),
migrations.AddIndex(
model_name='molnixappraiser',
index=models.Index(fields=['personnel'], name='molnix_appr_personnel_idx'),
),
migrations.AddIndex(
model_name='molnixappraisal',
index=models.Index(fields=['target_id'], name='molnix_app_target_idx'),
),
migrations.AddIndex(
model_name='molnixappraisal',
index=models.Index(fields=['personnel'], name='molnix_app_personnel_idx'),
),
migrations.AddIndex(
model_name='molnixappraisal',
index=models.Index(fields=['updated_at'], name='molnix_app_updated_idx'),
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Generated by Django 4.2.29 on 2026-05-11 00:00

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("deployments", "0094_erureadinesstype_ns_contribution"),
("deployments", "0094_molnixappraisal_molnixappraiser_rrmspersonsnapshot_and_more"),
]

operations = [
migrations.AddField(
model_name="molnixappraisal",
name="appraised_person_id",
field=models.BigIntegerField(blank=True, null=True),
),
migrations.AddIndex(
model_name="molnixappraisal",
index=models.Index(fields=["appraised_person_id"], name="molnix_appraised_person_idx"),
),
migrations.RemoveConstraint(
model_name="rrmseventparticipation",
name="rrms_event_person_role_uniq",
),
]
Loading
Loading