Skip to content

Commit

Permalink
add validation for individual rules, add terst coverage for TP query …
Browse files Browse the repository at this point in the history
…resolvers
  • Loading branch information
pkujawa committed Aug 23, 2024
1 parent 4b8d76b commit 4e73ceb
Show file tree
Hide file tree
Showing 5 changed files with 190 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,18 @@
'totalIndividualsCount': 2
}
},
{
'node': {
'createdBy': {
'firstName': 'Second',
'lastName': 'User'
},
'name': 'target_population_with_pdu_filter',
'status': 'LOCKED',
'totalHouseholdsCount': 1,
'totalIndividualsCount': 3
}
},
{
'node': {
'createdBy': {
Expand Down Expand Up @@ -79,6 +91,14 @@
'totalHouseholdsCount': 2,
'totalIndividualsCount': 2
}
},
{
'node': {
'name': 'target_population_with_pdu_filter',
'status': 'LOCKED',
'totalHouseholdsCount': 1,
'totalIndividualsCount': 3
}
}
]
}
Expand Down Expand Up @@ -132,6 +152,14 @@
'totalHouseholdsCount': 2,
'totalIndividualsCount': 2
}
},
{
'node': {
'name': 'target_population_with_pdu_filter',
'status': 'LOCKED',
'totalHouseholdsCount': 1,
'totalIndividualsCount': 3
}
}
]
}
Expand Down Expand Up @@ -161,6 +189,8 @@
'fieldName': 'size',
'flexFieldClassification': 'NOT_FLEX_FIELD'
}
],
'individualsFiltersBlocks': [
]
}
]
Expand Down Expand Up @@ -214,6 +244,8 @@
'fieldName': 'residence_status',
'flexFieldClassification': 'NOT_FLEX_FIELD'
}
],
'individualsFiltersBlocks': [
]
}
]
Expand Down Expand Up @@ -243,3 +275,59 @@
}
]
}

snapshots['TestTargetPopulationQuery::test_simple_target_query_pdu_0_with_permission 1'] = {
'data': {
'targetPopulation': {
'hasEmptyCriteria': False,
'hasEmptyIdsCriteria': True,
'name': 'target_population_with_pdu_filter',
'status': 'LOCKED',
'targetingCriteria': {
'rules': [
{
'filters': [
],
'individualsFiltersBlocks': [
{
'individualBlockFilters': [
{
'arguments': [
'some'
],
'comparisonMethod': 'EQUALS',
'fieldName': 'pdu_field_string',
'flexFieldClassification': 'FLEX_FIELD_PDU',
'roundNumber': 1
}
]
}
]
}
]
},
'totalHouseholdsCount': 1,
'totalIndividualsCount': 3
}
}
}

snapshots['TestTargetPopulationQuery::test_simple_target_query_pdu_1_without_permission 1'] = {
'data': {
'targetPopulation': None
},
'errors': [
{
'locations': [
{
'column': 11,
'line': 3
}
],
'message': 'Permission Denied',
'path': [
'targetPopulation'
]
}
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ def test_filter_on_pdu_flex_field_no_round_number(self) -> None:
pdu_data = PeriodicFieldDataFactory(
subtype=PeriodicFieldData.DECIMAL,
number_of_rounds=2,
rounds_names=["Round 1", "Rounds 2"],
rounds_names=["Round 1", "Round 2"],
)
FlexibleAttributeForPDUFactory(
program=self.program,
Expand Down Expand Up @@ -284,7 +284,7 @@ def test_filter_on_pdu_flex_field_incorrect_round_number(self) -> None:
pdu_data = PeriodicFieldDataFactory(
subtype=PeriodicFieldData.DECIMAL,
number_of_rounds=2,
rounds_names=["Round 1", "Rounds 2"],
rounds_names=["Round 1", "Round 2"],
)
FlexibleAttributeForPDUFactory(
program=self.program,
Expand Down Expand Up @@ -324,7 +324,7 @@ def test_filter_on_pdu_flex_field(self) -> None:
pdu_data = PeriodicFieldDataFactory(
subtype=PeriodicFieldData.DECIMAL,
number_of_rounds=2,
rounds_names=["Round 1", "Rounds 2"],
rounds_names=["Round 1", "Round 2"],
)
FlexibleAttributeForPDUFactory(
program=self.program,
Expand Down
92 changes: 90 additions & 2 deletions backend/hct_mis_api/apps/targeting/tests/test_target_query.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,23 @@
from hct_mis_api.apps.account.fixtures import PartnerFactory, UserFactory
from hct_mis_api.apps.account.permissions import Permissions
from hct_mis_api.apps.core.base_test_case import APITestCase
from hct_mis_api.apps.core.fixtures import create_afghanistan
from hct_mis_api.apps.core.models import BusinessArea
from hct_mis_api.apps.core.fixtures import (
FlexibleAttributeForPDUFactory,
PeriodicFieldDataFactory,
create_afghanistan,
)
from hct_mis_api.apps.core.models import BusinessArea, PeriodicFieldData
from hct_mis_api.apps.household.fixtures import create_household
from hct_mis_api.apps.periodic_data_update.utils import populate_pdu_with_null_values
from hct_mis_api.apps.program.fixtures import ProgramFactory
from hct_mis_api.apps.program.models import Program
from hct_mis_api.apps.targeting.choices import FlexFieldClassification
from hct_mis_api.apps.targeting.models import (
TargetingCriteria,
TargetingCriteriaRule,
TargetingCriteriaRuleFilter,
TargetingIndividualBlockRuleFilter,
TargetingIndividualRuleFilterBlock,
TargetPopulation,
)
from hct_mis_api.apps.targeting.services.targeting_stats_refresher import full_rebuild
Expand Down Expand Up @@ -75,6 +83,15 @@ class TestTargetPopulationQuery(APITestCase):
type
}
}
individualsFiltersBlocks{
individualBlockFilters{
comparisonMethod
fieldName
arguments
flexFieldClassification
roundNumber
}
}
}
}
}
Expand Down Expand Up @@ -147,6 +164,52 @@ def setUpTestData(cls) -> None:
cls.target_population_size_1_approved = full_rebuild(cls.target_population_size_1_approved)
cls.target_population_size_1_approved.save()

pdu_data_string = PeriodicFieldDataFactory(
subtype=PeriodicFieldData.STRING,
number_of_rounds=2,
rounds_names=["Round 1", "Round 2"],
)
cls.pdu_field_string = FlexibleAttributeForPDUFactory(
program=cls.program,
label="PDU Field STRING",
pdu_data=pdu_data_string,
)
(household, individuals) = create_household(
{"size": 3, "residence_status": "HOST", "business_area": cls.business_area, "program": cls.program},
)
individual_with_pdu_value = individuals[0]
populate_pdu_with_null_values(cls.program, individual_with_pdu_value.flex_fields)
individual_with_pdu_value.flex_fields[cls.pdu_field_string.name]["1"]["value"] = "some"
individual_with_pdu_value.save()
targeting_criteria = TargetingCriteria()
targeting_criteria.save()
rule = TargetingCriteriaRule(targeting_criteria=targeting_criteria)
rule.save()
individuals_filters_block = TargetingIndividualRuleFilterBlock(
targeting_criteria_rule=rule, target_only_hoh=False
)
individuals_filters_block.save()
rule_filter = TargetingIndividualBlockRuleFilter(
individuals_filters_block=individuals_filters_block,
comparison_method="EQUALS",
field_name=cls.pdu_field_string.name,
arguments=["some"],
round_number=1,
flex_field_classification=FlexFieldClassification.FLEX_FIELD_PDU,
)
rule_filter.save()
cls.target_population_with_pdu_filter = TargetPopulation(
name="target_population_with_pdu_filter",
created_by=user_second,
targeting_criteria=targeting_criteria,
status=TargetPopulation.STATUS_LOCKED,
business_area=cls.business_area,
program=cls.program,
)
cls.target_population_with_pdu_filter.save()
cls.target_population_with_pdu_filter = full_rebuild(cls.target_population_with_pdu_filter)
cls.target_population_with_pdu_filter.save()

@staticmethod
def get_targeting_criteria_for_rule(rule_filter: Dict) -> TargetingCriteria:
targeting_criteria = TargetingCriteria()
Expand Down Expand Up @@ -242,3 +305,28 @@ def test_simple_target_query_next(self, _: Any, permissions: List[Permissions])
)
},
)

@parameterized.expand(
[
(
"with_permission",
[Permissions.TARGETING_VIEW_DETAILS],
),
(
"without_permission",
[],
),
]
)
def test_simple_target_query_pdu(self, _: Any, permissions: List[Permissions]) -> None:
self.create_user_role_with_permissions(self.user, permissions, self.business_area, self.program)
self.snapshot_graphql_request(
request_string=TestTargetPopulationQuery.TARGET_POPULATION_QUERY,
context={"user": self.user},
variables={
"id": self.id_to_base64(
self.target_population_with_pdu_filter.id,
"TargetPopulationNode",
)
},
)
Original file line number Diff line number Diff line change
Expand Up @@ -401,7 +401,7 @@ def setUpTestData(cls) -> None:
pdu_data_string = PeriodicFieldDataFactory(
subtype=PeriodicFieldData.STRING,
number_of_rounds=2,
rounds_names=["Round 1", "Rounds 2"],
rounds_names=["Round 1", "Round 2"],
)
cls.pdu_field_string = FlexibleAttributeForPDUFactory(
program=cls.program,
Expand Down
9 changes: 8 additions & 1 deletion backend/hct_mis_api/apps/targeting/validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,10 @@ def validate(rule_filter: Any, program: Program) -> None:
f"Comparison method '{rule_filter.comparison_method}' "
f"expected {args_count} arguments, {given_args_count} given"
)
if get_attr_value("type", attribute) not in comparison_attribute.get("supported_types"):
type = get_attr_value("type", attribute, None)
if type == FlexibleAttribute.PDU:
type = attribute.pdu_data.subtype
if type not in comparison_attribute.get("supported_types"):
raise ValidationError(
f"{rule_filter.field_name} is '{get_attr_value('type', attribute)}' type filter "
f"and does not accept '{rule_filter.comparison_method}' comparison method"
Expand All @@ -143,6 +146,10 @@ def validate(rule: "Rule", program: "Program") -> None:
raise ValidationError("There should be at least 1 filter or block in rules")
for rule_filter in filters:
TargetingCriteriaRuleFilterInputValidator.validate(rule_filter=rule_filter, program=program)
for individuals_filters_block in individuals_filters_blocks:
individual_block_filters = individuals_filters_block.get("individual_block_filters")
for individual_block_filter in individual_block_filters:
TargetingCriteriaRuleFilterInputValidator.validate(rule_filter=individual_block_filter, program=program)


class TargetingCriteriaInputValidator:
Expand Down

0 comments on commit 4e73ceb

Please sign in to comment.