Skip to content

Commit

Permalink
fix: Vaccine stock improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
quang-le authored Dec 18, 2024
2 parents 8896049 + 963de45 commit 8e14c0e
Show file tree
Hide file tree
Showing 6 changed files with 219 additions and 57 deletions.
52 changes: 27 additions & 25 deletions plugins/polio/api/vaccines/repository_reports.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
"""API endpoints and serializers for vaccine repository reports."""

from datetime import datetime, timedelta
from django.db.models import OuterRef, Subquery, Q, Value, Case, When, CharField, Exists
from drf_yasg import openapi
from drf_yasg.utils import swagger_auto_schema
from rest_framework import filters, permissions, serializers
from rest_framework.exceptions import ValidationError
from rest_framework.filters import OrderingFilter, SearchFilter
from rest_framework.mixins import ListModelMixin
from rest_framework.viewsets import GenericViewSet
from django.db.models import Prefetch

from iaso.api.common import Paginator
from plugins.polio.models import VaccineStock, DestructionReport, IncidentReport
Expand Down Expand Up @@ -47,10 +43,20 @@ def filter_queryset(self, request, queryset, view):
if file_type:
try:
filetypes = [tp.strip().upper() for tp in file_type.split(",")]
if "INCIDENT" in filetypes:

has_incident = "INCIDENT" in filetypes
has_destruction = "DESTRUCTION" in filetypes

if has_incident and has_destruction:
# Both types specified - must have both
queryset = queryset.filter(incidentreport__isnull=False, destructionreport__isnull=False)
elif has_incident:
# Only incident reports
queryset = queryset.filter(incidentreport__isnull=False)
if "DESTRUCTION" in filetypes:
elif has_destruction:
# Only destruction reports
queryset = queryset.filter(destructionreport__isnull=False)
# If no types specified, show all (no filtering needed)
except ValueError:
raise ValidationError("file_type must be a comma-separated list of strings")

Expand All @@ -65,22 +71,26 @@ class VaccineRepositoryReportSerializer(serializers.Serializer):
destruction_report_data = serializers.SerializerMethodField()

def get_incident_report_data(self, obj):
return [
pir = obj.incidentreport_set.all()
data = [
{
"date": ir.date_of_incident_report,
"file": ir.document.url if ir.document else None,
}
for ir in obj.prefetched_incident_reports
for ir in pir
]
return data

def get_destruction_report_data(self, obj):
return [
drs = obj.destructionreport_set.all()
data = [
{
"date": dr.destruction_report_date,
"file": dr.document.url if dr.document else None,
}
for dr in obj.prefetched_destruction_reports
for dr in drs
]
return data


class VaccineRepositoryReportsViewSet(GenericViewSet, ListModelMixin):
Expand All @@ -97,26 +107,18 @@ class VaccineRepositoryReportsViewSet(GenericViewSet, ListModelMixin):

def get_queryset(self):
"""Get the queryset for VaccineStock objects."""

base_qs = VaccineStock.objects.select_related(
"country",
).filter(Q(incidentreport__isnull=False) | Q(destructionreport__isnull=False))

incident_qs = IncidentReport.objects.only(
"vaccine_stock_id",
"date_of_incident_report",
"document",
).prefetch_related(
"incidentreport_set",
"destructionreport_set",
)

destruction_qs = DestructionReport.objects.only(
"vaccine_stock_id",
"destruction_report_date",
"document",
)
if self.request.user and self.request.user.is_authenticated:
base_qs = base_qs.filter(account=self.request.user.iaso_profile.account)

return base_qs.prefetch_related(
Prefetch("incidentreport_set", queryset=incident_qs, to_attr="prefetched_incident_reports"),
Prefetch("destructionreport_set", queryset=destruction_qs, to_attr="prefetched_destruction_reports"),
).distinct()
return base_qs

@swagger_auto_schema(
manual_parameters=[
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export const VaccineRepository: FunctionComponent = () => {
...params,
tab: newTab,
};
redirectTo(baseUrl, newParams);
redirectTo(redirectUrl, newParams);
};

return (
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# Generated by Django 4.2.17 on 2024-12-16 10:55

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("polio", "0207_merge_20241204_1433"),
]

operations = [
migrations.AlterField(
model_name="vaccinerequestform",
name="rounds",
field=models.ManyToManyField(db_index=True, to="polio.round"),
),
migrations.AddIndex(
model_name="destructionreport",
index=models.Index(
fields=["vaccine_stock", "destruction_report_date"], name="polio_destr_vaccine_e5b90d_idx"
),
),
migrations.AddIndex(
model_name="destructionreport",
index=models.Index(fields=["rrt_destruction_report_reception_date"], name="polio_destr_rrt_des_449e4f_idx"),
),
migrations.AddIndex(
model_name="incidentreport",
index=models.Index(
fields=["vaccine_stock", "date_of_incident_report"], name="polio_incid_vaccine_b012dc_idx"
),
),
migrations.AddIndex(
model_name="incidentreport",
index=models.Index(fields=["incident_report_received_by_rrt"], name="polio_incid_inciden_067b16_idx"),
),
migrations.AddIndex(
model_name="outgoingstockmovement",
index=models.Index(fields=["vaccine_stock", "campaign"], name="polio_outgo_vaccine_fa2e84_idx"),
),
migrations.AddIndex(
model_name="outgoingstockmovement",
index=models.Index(fields=["form_a_reception_date"], name="polio_outgo_form_a__b64b56_idx"),
),
migrations.AddIndex(
model_name="outgoingstockmovement",
index=models.Index(fields=["report_date"], name="polio_outgo_report__44ffe2_idx"),
),
migrations.AddIndex(
model_name="vaccinearrivalreport",
index=models.Index(fields=["request_form", "arrival_report_date"], name="polio_vacci_request_48e891_idx"),
),
migrations.AddIndex(
model_name="vaccinearrivalreport",
index=models.Index(fields=["po_number"], name="polio_vacci_po_numb_bd6c9f_idx"),
),
migrations.AddIndex(
model_name="vaccinearrivalreport",
index=models.Index(fields=["doses_received"], name="polio_vacci_doses_r_d2cd9d_idx"),
),
migrations.AddIndex(
model_name="vaccineprealert",
index=models.Index(
fields=["request_form", "estimated_arrival_time"], name="polio_vacci_request_4c2b0b_idx"
),
),
migrations.AddIndex(
model_name="vaccineprealert",
index=models.Index(fields=["po_number"], name="polio_vacci_po_numb_511963_idx"),
),
migrations.AddIndex(
model_name="vaccineprealert",
index=models.Index(fields=["date_pre_alert_reception"], name="polio_vacci_date_pr_b7d59e_idx"),
),
migrations.AddIndex(
model_name="vaccinerequestform",
index=models.Index(fields=["campaign", "vaccine_type"], name="polio_vacci_campaig_f43af8_idx"),
),
migrations.AddIndex(
model_name="vaccinerequestform",
index=models.Index(fields=["vrf_type"], name="polio_vacci_vrf_typ_2acd7d_idx"),
),
migrations.AddIndex(
model_name="vaccinerequestform",
index=models.Index(fields=["created_at"], name="polio_vacci_created_8563f0_idx"),
),
migrations.AddIndex(
model_name="vaccinerequestform",
index=models.Index(fields=["updated_at"], name="polio_vacci_updated_fd171a_idx"),
),
migrations.AddIndex(
model_name="vaccinestock",
index=models.Index(fields=["country", "vaccine"], name="polio_vacci_country_91274d_idx"),
),
migrations.AddIndex(
model_name="vaccinestock",
index=models.Index(fields=["account"], name="polio_vacci_account_f1f77e_idx"),
),
]
12 changes: 12 additions & 0 deletions plugins/polio/migrations/0209_merge_20241216_1100.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Generated by Django 4.2.17 on 2024-12-16 11:00

from django.db import migrations


class Migration(migrations.Migration):
dependencies = [
("polio", "0208_alter_vaccinerequestform_rounds_and_more"),
("polio", "0208_migrate_vrf_orpg_fields"),
]

operations = []
49 changes: 47 additions & 2 deletions plugins/polio/models/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -1036,9 +1036,17 @@ class VaccineRequestFormType(models.TextChoices):


class VaccineRequestForm(SoftDeletableModel):
campaign = models.ForeignKey(Campaign, on_delete=models.CASCADE)
class Meta:
indexes = [
models.Index(fields=["campaign", "vaccine_type"]), # Frequently filtered together
models.Index(fields=["vrf_type"]), # Filtered in repository_forms.py
models.Index(fields=["created_at"]), # Used for ordering
models.Index(fields=["updated_at"]), # Used for ordering
]

campaign = models.ForeignKey(Campaign, on_delete=models.CASCADE, db_index=True)
vaccine_type = models.CharField(max_length=5, choices=VACCINES)
rounds = models.ManyToManyField(Round)
rounds = models.ManyToManyField(Round, db_index=True)
date_vrf_signature = models.DateField(null=True, blank=True)
date_vrf_reception = models.DateField(null=True, blank=True)
date_dg_approval = models.DateField(null=True, blank=True)
Expand Down Expand Up @@ -1117,6 +1125,13 @@ def save(self, *args, **kwargs):
def get_doses_per_vial(self):
return DOSES_PER_VIAL[self.request_form.vaccine_type]

class Meta:
indexes = [
models.Index(fields=["request_form", "estimated_arrival_time"]), # Used together in queries
models.Index(fields=["po_number"]), # Unique field that's queried
models.Index(fields=["date_pre_alert_reception"]), # Used for filtering/ordering
]


class VaccineArrivalReport(models.Model):
request_form = models.ForeignKey(VaccineRequestForm, on_delete=models.CASCADE)
Expand Down Expand Up @@ -1152,6 +1167,13 @@ def save(self, *args, **kwargs):

super().save(*args, **kwargs)

class Meta:
indexes = [
models.Index(fields=["request_form", "arrival_report_date"]), # Frequently queried together
models.Index(fields=["po_number"]), # Unique field that's queried
models.Index(fields=["doses_received"]), # Used in aggregations
]


class VaccineStock(models.Model):
account = models.ForeignKey("iaso.account", on_delete=models.CASCADE, related_name="vaccine_stocks")
Expand All @@ -1167,6 +1189,10 @@ class VaccineStock(models.Model):

class Meta:
unique_together = ("country", "vaccine")
indexes = [
models.Index(fields=["country", "vaccine"]), # Already unique_together, but used in many queries
models.Index(fields=["account"]), # Frequently filtered by account
]

def __str__(self):
return f"{self.country} - {self.vaccine}"
Expand Down Expand Up @@ -1204,6 +1230,13 @@ class Meta:

# Form A
class OutgoingStockMovement(models.Model):
class Meta:
indexes = [
models.Index(fields=["vaccine_stock", "campaign"]), # Frequently queried together
models.Index(fields=["form_a_reception_date"]), # Used in ordering
models.Index(fields=["report_date"]), # Used in filtering/ordering
]

campaign = models.ForeignKey(Campaign, on_delete=models.CASCADE)
round = models.ForeignKey(Round, on_delete=models.CASCADE, null=True, blank=True)
vaccine_stock = models.ForeignKey(
Expand Down Expand Up @@ -1234,6 +1267,12 @@ class DestructionReport(models.Model):
storage=CustomPublicStorage(), upload_to="public_documents/destructionreport/", null=True, blank=True
)

class Meta:
indexes = [
models.Index(fields=["vaccine_stock", "destruction_report_date"]), # Used together in queries
models.Index(fields=["rrt_destruction_report_reception_date"]), # Used in filtering
]


class IncidentReport(models.Model):
class StockCorrectionChoices(models.TextChoices):
Expand Down Expand Up @@ -1262,6 +1301,12 @@ class StockCorrectionChoices(models.TextChoices):
storage=CustomPublicStorage(), upload_to="public_documents/incidentreport/", null=True, blank=True
)

class Meta:
indexes = [
models.Index(fields=["vaccine_stock", "date_of_incident_report"]), # Frequently queried together
models.Index(fields=["incident_report_received_by_rrt"]), # Used in filtering
]


class Notification(models.Model):
"""
Expand Down
Loading

0 comments on commit 8e14c0e

Please sign in to comment.