Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

POLIO-1803: adapt supplychain to bopv/nopv new vaccine #1886

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 11 additions & 8 deletions plugins/polio/api/campaigns/campaigns.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,17 @@ class CampaignSerializer(serializers.ModelSerializer):
account: Field = serializers.PrimaryKeyRelatedField(default=CurrentAccountDefault(), read_only=True)
has_data_in_budget_tool = serializers.SerializerMethodField(read_only=True)
campaign_types = serializers.PrimaryKeyRelatedField(many=True, queryset=CampaignType.objects.all(), required=False)
# Vaccines with real scope
vaccines = serializers.SerializerMethodField(read_only=True)
single_vaccines = serializers.SerializerMethodField(read_only=True)

def get_vaccines(self, obj):
if obj.vaccines:
return ",".join([vaccine.strip() for vaccine in obj.vaccines.split(",")])
return ""

def get_single_vaccines(self, obj):
return obj.vaccines_extended

def get_top_level_org_unit_name(self, campaign):
if campaign.country:
Expand Down Expand Up @@ -410,14 +421,6 @@ def update(self, instance: Campaign, validated_data):
log_campaign_modification(campaign, old_campaign_dump, self.context["request"].user)
return campaign

# Vaccines with real scope
vaccines = serializers.SerializerMethodField(read_only=True)

def get_vaccines(self, obj):
if obj.vaccines:
return ",".join([vaccine.strip() for vaccine in obj.vaccines.split(",")])
return ""

class Meta:
model = Campaign
# TODO in the future specify the fields that need to be returned so we can remove the deprecated fields
Expand Down
4 changes: 4 additions & 0 deletions plugins/polio/api/rounds/round.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ class Meta:

# Vaccines from real scopes, from property, separated by ,
vaccine_names = serializers.CharField(read_only=True)
vaccine_names_extended = serializers.SerializerMethodField(read_only=True)

def get_vaccine_names_extended(self, obj):
return obj.vaccine_names_extended

@atomic
def create(self, validated_data):
Expand Down
2 changes: 2 additions & 0 deletions plugins/polio/js/src/constants/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,7 @@ export type RoundDateHistoryEntry = {

export type Round = {
id: number;
vaccine_names_extended:string;
started_at: Nullable<string>;
ended_at: Nullable<string>;
mop_up_started_at: Nullable<string>; // date
Expand Down Expand Up @@ -308,6 +309,7 @@ export type Campaign = {
created_at: string;
updated_at: string;
deleted_at: Nullable<string>;
single_vaccines?: string;
rounds: Round[];
org_unit: {
id: number;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import MESSAGES from '../../../../constants/messages';
import { useGetCountries } from '../../../../hooks/useGetCountries';

import { appId } from '../../../../constants/app';
import { defaultVaccineOptions } from '../../SupplyChain/constants';
import { singleVaccinesList } from '../../SupplyChain/constants';
import { useGetFileTypes } from '../hooks/useGetFileTypes';
import { VaccineRepositoryParams } from '../types';

Expand Down Expand Up @@ -101,7 +101,7 @@ export const Filters: FunctionComponent<Props> = ({ params, redirectUrl }) => {
}}
value={vaccineName}
type="select"
options={defaultVaccineOptions}
options={singleVaccinesList}
label={MESSAGES.vaccine}
/>
</Grid>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import MESSAGES from '../../../../constants/messages';
import { useGetCountries } from '../../../../hooks/useGetCountries';

import { appId } from '../../../../constants/app';
import { defaultVaccineOptions } from '../../SupplyChain/constants';
import { singleVaccinesList } from '../../SupplyChain/constants';
import { useGetReportFileTypes } from '../hooks/useGetFileTypes';
import { VaccineRepositoryParams } from '../types';

Expand All @@ -30,7 +30,9 @@ export const Filters: FunctionComponent<Props> = ({ params, redirectUrl }) => {

const [filtersUpdated, setFiltersUpdated] = useState(false);
const [countries, setCountries] = useState(params.reportCountries);
const [fileType, setFileType] = useState(params.reportFileType || 'INCIDENT,DESTRUCTION');
const [fileType, setFileType] = useState(
params.reportFileType || 'INCIDENT,DESTRUCTION',
);
const [vaccineName, setVaccineName] = useState(params.reportVaccineName);
const [countryBlocks, setCountryBlocks] = useState(
params.reportCountryBlock,
Expand Down Expand Up @@ -133,7 +135,7 @@ export const Filters: FunctionComponent<Props> = ({ params, redirectUrl }) => {
}}
value={vaccineName}
type="select"
options={defaultVaccineOptions}
options={singleVaccinesList}
label={MESSAGES.vaccine}
/>
</Grid>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { SingleSelect } from '../../../../components/Inputs/SingleSelect';
import MESSAGES from '../messages';
import { useSaveVaccineStock } from '../hooks/api';
import { useGetCountriesOptions } from '../../SupplyChain/hooks/api/vrf';
import { defaultVaccineOptions } from '../../SupplyChain/constants';
import { singleVaccinesList } from '../../SupplyChain/constants';

type Props = {
isOpen: boolean;
Expand Down Expand Up @@ -71,7 +71,7 @@ const CreateVaccineStock: FunctionComponent<Props> = ({
name="vaccine"
component={SingleSelect}
required
options={defaultVaccineOptions}
options={singleVaccinesList}
withMarginTop
// isLoading={isFetchingCountries}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ import { FilterButton } from '../../../../../../../../hat/assets/js/apps/Iaso/co
import { useFilterState } from '../../../../../../../../hat/assets/js/apps/Iaso/hooks/useFilterState';
import InputComponent from '../../../../../../../../hat/assets/js/apps/Iaso/components/forms/InputComponent';
import MESSAGES from '../messages';
import { polioVaccines } from '../../../../constants/virus';
import { useGetCountriesOptions } from '../../SupplyChain/hooks/api/vrf';
import { StockManagementListParams } from '../types';
import { baseUrls } from '../../../../constants/urls';
import { singleVaccinesList } from '../../SupplyChain/constants';

const baseUrl = baseUrls.stockManagement;
type Props = { params: StockManagementListParams };
Expand Down Expand Up @@ -46,10 +46,7 @@ export const VaccineStockManagementFilters: FunctionComponent<Props> = ({
keyValue="vaccine_type"
value={filters.vaccine_type}
onChange={handleChange}
options={polioVaccines.map(vaccine => ({
label: vaccine.label,
value: vaccine.value,
}))}
options={singleVaccinesList}
labelString={formatMessage(MESSAGES.vaccine)}
/>
</Grid>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ import { FilterButton } from '../../../../../../../../hat/assets/js/apps/Iaso/co
import { useFilterState } from '../../../../../../../../hat/assets/js/apps/Iaso/hooks/useFilterState';
import InputComponent from '../../../../../../../../hat/assets/js/apps/Iaso/components/forms/InputComponent';
import MESSAGES from '../messages';
import { polioVaccines } from '../../../../constants/virus';
import { apiDateFormat } from '../../../../../../../../hat/assets/js/apps/Iaso/utils/dates';
import { useGetCountriesOptions } from '../hooks/api/vrf';
import { useGetGroupDropdown } from '../../../../../../../../hat/assets/js/apps/Iaso/domains/orgUnits/hooks/requests/useGetGroups';
import { singleVaccinesList } from '../constants';

type Props = { params: any };

Expand Down Expand Up @@ -83,10 +83,7 @@ export const VaccineSupplyChainFilters: FunctionComponent<Props> = ({
keyValue="vaccine_type"
value={filters.vaccine_type}
onChange={handleChange}
options={polioVaccines.map(vaccine => ({
label: vaccine.label,
value: vaccine.value,
}))}
options={singleVaccinesList}
labelString={formatMessage(MESSAGES.vaccine)}
/>
<InputComponent
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,18 @@ export const defaultVaccineOptions = [
value: 'nOPV2 & bOPV',
},
];

export const singleVaccinesList = [
{
label: 'nOPV2',
value: 'nOPV2',
},
{
label: 'mOPV2',
value: 'mOPV2',
},
{
label: 'bOPV',
value: 'bOPV',
},
]
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ import {
CampaignCategory,
useGetCampaigns,
} from '../../../../Campaigns/hooks/api/useGetCampaigns';
import { apiUrl, defaultVaccineOptions } from '../../constants';
import { apiUrl, defaultVaccineOptions, singleVaccinesList } from '../../constants';
import MESSAGES from '../../messages';
import {
CampaignDropdowns,
Expand Down Expand Up @@ -133,15 +133,15 @@ export const useCampaignDropDowns = (
label: c.obr_name,
value: c.obr_name,
}));
const vaccines = selectedCampaign?.vaccines
? selectedCampaign.vaccines.split(',').map(vaccineName => ({
label: vaccineName,
value: vaccineName,
const vaccines = selectedCampaign?.single_vaccines
? selectedCampaign.single_vaccines.split(',').map(vaccineName => ({
label: vaccineName.trim(),
value: vaccineName.trim(),
}))
: defaultVaccineOptions;
: singleVaccinesList;
const rounds = vaccine
? (selectedCampaign?.rounds ?? [])
.filter(round => round.vaccine_names.includes(vaccine))
.filter(round => round.vaccine_names_extended.includes(vaccine))
.map(round => ({
label: `Round ${round.number}`,
value: `${round.number}`,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Generated by Django 4.2.17 on 2024-12-19 13:58

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("polio", "0212_alter_vaccinerequestform_vaccine_type"),
]

operations = [
migrations.AlterField(
model_name="vaccinerequestform",
name="vaccine_type",
field=models.CharField(choices=[("mOPV2", "mOPV2"), ("nOPV2", "nOPV2"), ("bOPV", "bOPV")], max_length=30),
),
]
60 changes: 59 additions & 1 deletion plugins/polio/models/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@
("nOPV2 & bOPV", _("nOPV2 & bOPV")),
]

INDIVIDUAL_VACCINES = [
("mOPV2", _("mOPV2")),
("nOPV2", _("nOPV2")),
("bOPV", _("bOPV")),
]

DOSES_PER_VIAL = {
"mOPV2": 20,
"nOPV2": 50,
Expand Down Expand Up @@ -380,6 +386,32 @@ def vaccine_names(self):
)
return ",".join(scope.vaccine for scope in scopes_with_orgunits)

@property
def vaccine_names_extended(self):
vaccines = set()
subactivity_vaccines = [
subactivity["scopes__vaccine"]
for subactivity in list(self.sub_activities.filter(scopes__isnull=False).values("scopes__vaccine"))
]
for subactivity_vaccine in subactivity_vaccines:
vaccines.add(subactivity_vaccine)
if self.campaign.separate_scopes_per_round:
scopes_with_orgunits = filter(
lambda s: len(s.group.org_units.all()) > 0 and s.vaccine is not None, self.scopes.all()
)
else:
scopes_with_orgunits = filter(
lambda s: len(s.group.org_units.all()) > 0 and s.vaccine is not None, self.campaign.scopes.all()
)
for scope in scopes_with_orgunits:
vaccines.add(scope.vaccine)

if VACCINES[3][0] in vaccines:
vaccines.remove(VACCINES[3][0])
vaccines.add(VACCINES[1][0])
vaccines.add(VACCINES[2][0])
return ", ".join(sorted(vaccines))

@property
def districts_count_calculated(self):
return len(self.campaign.get_districts_for_round(self))
Expand Down Expand Up @@ -781,6 +813,32 @@ def vaccine_names(self):
vaccine_names = sorted({scope.vaccine for scope in scopes_with_orgunits_and_vaccine})
return ", ".join(vaccine_names)

@property
def vaccines_extended(self):
vaccines = set()
for round in self.rounds.all():
subactivity_vaccines = [
subactivity["scopes__vaccine"]
for subactivity in list(round.sub_activities.filter(scopes__isnull=False).values("scopes__vaccine"))
]
for subactivity_vaccine in subactivity_vaccines:
vaccines.add(subactivity_vaccine)
if self.separate_scopes_per_round:
scopes_with_orgunits = filter(
lambda s: len(s.group.org_units.all()) > 0 and s.vaccine is not None, round.scopes.all()
)
else:
scopes_with_orgunits = filter(
lambda s: len(s.group.org_units.all()) > 0 and s.vaccine is not None, self.scopes.all()
)
for scope in scopes_with_orgunits:
vaccines.add(scope.vaccine)
if VACCINES[3][0] in vaccines:
vaccines.remove(VACCINES[3][0])
vaccines.add(VACCINES[1][0])
vaccines.add(VACCINES[2][0])
return ", ".join(sorted(vaccines))

def update_geojson_field(self):
"Update the geojson field on the campaign DO NOT TRIGGER the save() you have to do it manually"
campaign = self
Expand Down Expand Up @@ -1047,7 +1105,7 @@ class Meta:
]

campaign = models.ForeignKey(Campaign, on_delete=models.CASCADE, db_index=True)
vaccine_type = models.CharField(max_length=30, choices=VACCINES)
vaccine_type = models.CharField(max_length=30, choices=INDIVIDUAL_VACCINES)
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)
Expand Down
Loading