Skip to content

Commit

Permalink
Merge branch 'develop' into ephemeral-1
Browse files Browse the repository at this point in the history
  • Loading branch information
patryk-dabrowski authored Jun 11, 2024
2 parents a5e60d5 + 3d66831 commit b2f4f83
Show file tree
Hide file tree
Showing 201 changed files with 8,768 additions and 1,166 deletions.
27 changes: 24 additions & 3 deletions backend/hct_mis_api/api/endpoints/rdi/push_people.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Dict, List, Optional
from typing import Any, Dict, List, Optional
from uuid import UUID

from django.db.transaction import atomic
Expand All @@ -15,6 +15,7 @@
from hct_mis_api.api.endpoints.rdi.mixin import get_photo_from_stream
from hct_mis_api.api.endpoints.rdi.upload import BirthDateValidator, DocumentSerializer
from hct_mis_api.api.models import Grant
from hct_mis_api.apps.geo.models import Area
from hct_mis_api.apps.household.models import (
BLANK,
COLLECT_TYPES,
Expand Down Expand Up @@ -52,11 +53,23 @@ class PushPeopleSerializer(serializers.ModelSerializer):
country = serializers.CharField(allow_blank=True, required=True)
collect_individual_data = serializers.ChoiceField(choices=COLLECT_TYPES)
residence_status = serializers.ChoiceField(choices=RESIDENCE_STATUS_CHOICE)
village = serializers.CharField(allow_blank=True, required=False)
village = serializers.CharField(allow_blank=True, allow_null=True, required=False)

phone_no = serializers.CharField(allow_null=True, allow_blank=True, required=False)
phone_no_alternative = serializers.CharField(allow_null=True, allow_blank=True, required=False)

admin1 = serializers.ChoiceField(allow_blank=True, allow_null=True, required=False, default="", choices=[])
admin2 = serializers.ChoiceField(allow_blank=True, allow_null=True, required=False, default="", choices=[])
admin3 = serializers.ChoiceField(allow_blank=True, allow_null=True, required=False, default="", choices=[])
admin4 = serializers.ChoiceField(allow_blank=True, allow_null=True, required=False, default="", choices=[])

def __init__(self, *args: Any, **kwargs: Any) -> None:
super().__init__(*args, **kwargs)
self.fields["admin1"].choices = Area.objects.filter(area_type__area_level=1).values_list("p_code", "name")
self.fields["admin2"].choices = Area.objects.filter(area_type__area_level=2).values_list("p_code", "name")
self.fields["admin3"].choices = Area.objects.filter(area_type__area_level=3).values_list("p_code", "name")
self.fields["admin4"].choices = Area.objects.filter(area_type__area_level=4).values_list("p_code", "name")

class Meta:
model = ImportedIndividual
exclude = [
Expand Down Expand Up @@ -93,12 +106,20 @@ def _create_household(
return None
household_fields = [field.name for field in ImportedHousehold._meta.get_fields()]
household_data = {field: value for field, value in person_data.items() if field in household_fields}
return ImportedHousehold.objects.create(
household_data["village"] = household_data.get("village") or ""
household_data["admin1"] = household_data.get("admin1") or ""
household_data["admin2"] = household_data.get("admin2") or ""
household_data["admin3"] = household_data.get("admin3") or ""
household_data["admin4"] = household_data.get("admin4") or ""
household = ImportedHousehold.objects.create(
registration_data_import=rdi,
program_id=program_id,
collect_type=ImportedHousehold.CollectType.SINGLE.value,
**household_data,
)
household.set_admin_areas()
household.save()
return household

def _create_individual(
self,
Expand Down
68 changes: 68 additions & 0 deletions backend/hct_mis_api/api/tests/test_push_people.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from hct_mis_api.apps.core.fixtures import DataCollectingTypeFactory
from hct_mis_api.apps.core.models import DataCollectingType
from hct_mis_api.apps.core.utils import IDENTIFICATION_TYPE_TO_KEY_MAPPING
from hct_mis_api.apps.geo.fixtures import AreaFactory, AreaTypeFactory, CountryFactory
from hct_mis_api.apps.household.models import (
COLLECT_TYPE_FULL,
FEMALE,
Expand Down Expand Up @@ -61,6 +62,15 @@ def setUpTestData(cls) -> None:

cls.url = reverse("api:rdi-push-people", args=[cls.business_area.slug, str(cls.rdi.id)])

country = CountryFactory()
admin_type_1 = AreaTypeFactory(country=country, area_level=1)
admin_type_2 = AreaTypeFactory(country=country, area_level=2, parent=admin_type_1)
admin_type_3 = AreaTypeFactory(country=country, area_level=3, parent=admin_type_2)

area1 = AreaFactory(parent=None, p_code="AF01", area_type=admin_type_1)
area2 = AreaFactory(parent=area1, p_code="AF0101", area_type=admin_type_2)
AreaFactory(parent=area2, p_code="AF010101", area_type=admin_type_3)

def test_upload_single_person(self) -> None:
data = [
{
Expand Down Expand Up @@ -281,3 +291,61 @@ def test_upload_single_person_with_phone_number(
self.assertIsNotNone(ind)
self.assertEqual(ind.full_name, "John Doe")
self.assertEqual(getattr(ind, f"{field_name}_valid"), expected_value)

@parameterized.expand(
[
("valid-village", "village1", "village1"),
("empty-village", "", ""),
("null-village", None, ""),
]
)
def test_push_single_person_with_village(self, _: Any, village: str, expected_value: str) -> None:
data = [
{
"residence_status": "IDP",
"village": village,
"country": "AF",
"collect_individual_data": COLLECT_TYPE_FULL,
"full_name": "John Doe",
"birth_date": "2000-01-01",
"sex": "MALE",
"type": "",
}
]
response = self.client.post(self.url, data, format="json")
self.assertEqual(response.status_code, status.HTTP_201_CREATED, str(response.json()))
response_json = response.json()

rdi_datahub = RegistrationDataImportDatahub.objects.filter(id=response_json["id"]).first()
self.assertIsNotNone(rdi_datahub)
ind = ImportedIndividual.objects.filter(registration_data_import=rdi_datahub).first()
self.assertEqual(ind.household.village, expected_value)

def test_push_single_person_with_admin_areas(self) -> None:
data = [
{
"residence_status": "IDP",
"village": "village1",
"country": "AF",
"collect_individual_data": COLLECT_TYPE_FULL,
"full_name": "John Doe",
"birth_date": "2000-01-01",
"sex": "MALE",
"type": "",
"admin1": "AF01",
"admin2": "AF0101",
"admin3": "",
"admin4": None,
}
]
response = self.client.post(self.url, data, format="json")
self.assertEqual(response.status_code, status.HTTP_201_CREATED, str(response.json()))
response_json = response.json()

rdi_datahub = RegistrationDataImportDatahub.objects.filter(id=response_json["id"]).first()
self.assertIsNotNone(rdi_datahub)
ind = ImportedIndividual.objects.filter(registration_data_import=rdi_datahub).first()
self.assertEqual(ind.household.admin1, "AF01")
self.assertEqual(ind.household.admin2, "AF0101")
self.assertEqual(ind.household.admin3, "")
self.assertEqual(ind.household.admin4, "")
2 changes: 2 additions & 0 deletions backend/hct_mis_api/apps/account/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ class AccountConfig(AppConfig):
def ready(self) -> None:
from hijack.signals import hijack_started

import hct_mis_api.apps.account.signals # noqa

hijack_started.connect(log_impersonate)


Expand Down
42 changes: 39 additions & 3 deletions backend/hct_mis_api/apps/account/signals.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
from datetime import timezone
from typing import Any

from django.contrib.auth import get_user_model
from django.db.models.signals import post_save, pre_delete, pre_save
from django.db.models.signals import m2m_changed, post_save, pre_delete, pre_save
from django.dispatch import receiver
from django.utils import timezone

from hct_mis_api.apps.account.models import Role, User, UserRole
from hct_mis_api.apps.account.models import Partner, Role, User, UserRole
from hct_mis_api.apps.core.models import BusinessArea
from hct_mis_api.apps.program.models import Program, ProgramPartnerThrough


@receiver(post_save, sender=UserRole)
Expand Down Expand Up @@ -36,3 +37,38 @@ def post_save_user(sender: Any, instance: User, created: bool, *args: Any, **kwa
role = Role.objects.filter(name="Basic User").first()
if business_area and role:
UserRole.objects.get_or_create(business_area=business_area, user=instance, role=role)


@receiver(m2m_changed, sender=Partner.allowed_business_areas.through)
def allowed_business_areas_changed(sender: Any, instance: Partner, action: str, pk_set: set, **kwargs: Any) -> None:
if action == "post_add":
added_business_areas_ids = pk_set
programs_to_add_access = Program.objects.filter(
business_area_id__in=added_business_areas_ids,
partner_access=Program.ALL_PARTNERS_ACCESS,
)
for program in programs_to_add_access:
program_partner = ProgramPartnerThrough.objects.create(program=program, partner=instance)
program_partner.full_area_access = True
program_partner.save()

elif action == "post_remove":
removed_business_areas_ids = pk_set
programs_to_remove_access = Program.objects.filter(
business_area_id__in=removed_business_areas_ids,
partner_access=Program.ALL_PARTNERS_ACCESS,
)
for program in programs_to_remove_access:
program.partners.remove(instance)

elif action == "pre_clear":
instance._removed_business_areas = list(instance.allowed_business_areas.all())

elif action == "post_clear":
removed_business_areas = getattr(instance, "_removed_business_areas", [])
programs_to_remove_access = Program.objects.filter(
business_area__in=removed_business_areas,
partner_access=Program.ALL_PARTNERS_ACCESS,
)
for program in programs_to_remove_access:
program.partners.remove(instance)
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
from django.test import TestCase

from hct_mis_api.apps.account.fixtures import PartnerFactory
from hct_mis_api.apps.core.fixtures import create_afghanistan, create_ukraine
from hct_mis_api.apps.program.fixtures import ProgramFactory
from hct_mis_api.apps.program.models import Program


class TestSignalChangeAllowedBusinessAreas(TestCase):
@classmethod
def setUpTestData(cls) -> None:
cls.business_area_afg = create_afghanistan()
cls.business_area_ukr = create_ukraine()
cls.program_afg = ProgramFactory.create(
status=Program.DRAFT, business_area=cls.business_area_afg, partner_access=Program.ALL_PARTNERS_ACCESS
)
cls.program_ukr = ProgramFactory.create(
status=Program.DRAFT, business_area=cls.business_area_ukr, partner_access=Program.ALL_PARTNERS_ACCESS
)
cls.partner = PartnerFactory(name="Partner")
cls.partner_unicef = PartnerFactory(name="UNICEF") # UNICEF partner has access to all programs

def test_signal_change_allowed_business_areas(self) -> None:
self.partner.allowed_business_areas.add(self.business_area_afg)

self.assertEqual(self.program_afg.partners.count(), 2)
self.assertEqual(self.program_ukr.partners.count(), 1)
self.assertEqual(self.partner.programs.count(), 1)
self.assertEqual(self.partner.program_partner_through.first().full_area_access, True)

self.partner.allowed_business_areas.add(self.business_area_ukr)

self.assertEqual(self.program_afg.partners.count(), 2)
self.assertEqual(self.program_ukr.partners.count(), 2)
self.assertEqual(self.partner.programs.count(), 2)
self.assertEqual(self.partner.program_partner_through.first().full_area_access, True)
self.assertEqual(self.partner.program_partner_through.last().full_area_access, True)

self.partner.allowed_business_areas.remove(self.business_area_afg)

self.assertEqual(self.program_afg.partners.count(), 1)
self.assertEqual(self.program_ukr.partners.count(), 2)
self.assertEqual(self.partner.programs.count(), 1)
self.assertEqual(self.partner.program_partner_through.first().full_area_access, True)

self.partner.allowed_business_areas.clear()

self.assertEqual(self.program_afg.partners.count(), 1)
self.assertEqual(self.program_ukr.partners.count(), 1)
self.assertEqual(self.partner.programs.count(), 0)
Original file line number Diff line number Diff line change
Expand Up @@ -1622,7 +1622,7 @@
{
"id": "df2b2588-68af-4dea-89ab-c5a53a5764be",
"type": TYPE_STRING,
"name": "unicef_id",
"name": "household_unicef_id",
"lookup": "unicef_id",
"required": False,
"label": {"English(EN)": f"{TEMPLATE_HOUSEHOLD} unicef id"},
Expand All @@ -1635,7 +1635,7 @@
{
"id": "bb9bcb76-c9e4-4e83-8b9a-6c8bb35cb84c",
"type": TYPE_STRING,
"name": "unicef_id",
"name": "individual_unicef_id",
"lookup": "unicef_id",
"required": False,
"label": {"English(EN)": f"{TEMPLATE_INDIVIDUAL} unicef id"},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@

class Command(BaseCommand):
def handle(self, *args: Any, **options: Any) -> None:
call_command("collectstatic", "--no-default-ignore", interactive=False)
call_command("migratealldb")
call_command("collectstatic", "--no-default-ignore", interactive=False)
call_command("generateroles")
from adminactions.perms import create_extra_permissions

Expand Down
2 changes: 1 addition & 1 deletion backend/hct_mis_api/apps/core/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -645,10 +645,10 @@ def resolve_f(*args: Any, **kwargs: Any) -> Any:
from hct_mis_api.apps.core.models import BusinessArea

_, resolve_info = args
program_id = get_program_id_from_headers(resolve_info.context.headers)
if resolve_info.context.user.is_authenticated:
business_area_slug = kwargs.get("business_area_slug", "global")
business_area = BusinessArea.objects.filter(slug=business_area_slug).first()
program_id = get_program_id_from_headers(resolve_info.context.headers)
if any(
resolve_info.context.user.has_permission(per.name, business_area, program_id) for per in permissions
):
Expand Down
2 changes: 2 additions & 0 deletions backend/hct_mis_api/apps/grievance/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
TypedMultipleChoiceFilter,
UUIDFilter,
)
from graphene_django.filter import GlobalIDFilter

from hct_mis_api.apps.account.permissions import Permissions
from hct_mis_api.apps.core.filters import DateTimeRangeFilter, IntegerFilter
Expand Down Expand Up @@ -119,6 +120,7 @@ class GrievanceTicketFilter(FilterSet):
program = CharFilter(method="filter_by_program")
is_active_program = BooleanFilter(method="filter_is_active_program")
is_cross_area = BooleanFilter(method="filter_is_cross_area")
admin1 = GlobalIDFilter(field_name="admin2__parent")

class Meta:
fields = {
Expand Down
6 changes: 6 additions & 0 deletions backend/hct_mis_api/apps/grievance/inputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,9 @@ class IndividualUpdateDataObjectType(graphene.InputObjectType):
preferred_language = graphene.String()
flex_fields = Arg()
payment_delivery_phone_no = graphene.String()
blockchain_name = graphene.String()
wallet_address = graphene.String()
wallet_name = graphene.String()


class AddIndividualDataObjectType(graphene.InputObjectType):
Expand Down Expand Up @@ -223,6 +226,9 @@ class AddIndividualDataObjectType(graphene.InputObjectType):
preferred_language = graphene.String()
flex_fields = Arg()
payment_delivery_phone_no = graphene.String()
blockchain_name = graphene.String()
wallet_address = graphene.String()
wallet_name = graphene.String()


class HouseholdDataUpdateIssueTypeExtras(graphene.InputObjectType):
Expand Down
2 changes: 1 addition & 1 deletion backend/hct_mis_api/apps/grievance/notifications.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ def _prepare_default_context(self, user_recipient: "User") -> Dict[str, Any]:
context = {
"first_name": user_recipient.first_name,
"last_name": user_recipient.last_name,
"ticket_url": f'{protocol}://{settings.FRONTEND_HOST}/{self.grievance_ticket.business_area.slug}/grievance/tickets/{self.grievance_ticket.grievance_type_to_string()}-generated/{encode_id_base64(self.grievance_ticket.id, "GrievanceTicket")}',
"ticket_url": f'{protocol}://{settings.FRONTEND_HOST}/{self.grievance_ticket.business_area.slug}/programs/all/grievance/tickets/{self.grievance_ticket.grievance_type_to_string()}-generated/{encode_id_base64(self.grievance_ticket.id, "GrievanceTicket")}',
"ticket_id": self.grievance_ticket.unicef_id,
"ticket_category": self.grievance_ticket.get_category_display(),
"title": "Grievance and feedback notification",
Expand Down
6 changes: 3 additions & 3 deletions backend/hct_mis_api/apps/grievance/tests/test_grievance_es.py
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ def setUpTestData(cls) -> None:
}
)

cls.es.index( # type: ignore
cls.es.index(
index="test_es_db",
doc_type="_doc",
id=cls.grievance_ticket_1.id,
Expand Down Expand Up @@ -405,7 +405,7 @@ def setUpTestData(cls) -> None:
}
)

cls.es.index( # type: ignore
cls.es.index(
index="test_es_db",
doc_type="_doc",
id=cls.grievance_ticket_2.id,
Expand Down Expand Up @@ -447,7 +447,7 @@ def setUpTestData(cls) -> None:
}
)

cls.es.index( # type: ignore
cls.es.index(
index="test_es_db",
doc_type="_doc",
id=cls.grievance_ticket_3.id,
Expand Down
Loading

0 comments on commit b2f4f83

Please sign in to comment.