-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #115 from OpenUpSA/fix/subindicator-sorting
Fix/Subindicator sorting
- Loading branch information
Showing
7 changed files
with
571 additions
and
68 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
189 changes: 189 additions & 0 deletions
189
tests/profile/serializers/test_profile_indicator_sorter.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,189 @@ | ||
import pytest | ||
import unittest | ||
from collections import OrderedDict | ||
|
||
from tests.profile import factoryboy as profile_factoryboy | ||
from tests.datasets import factoryboy as datasets_factoryboy | ||
|
||
from wazimap_ng.profile.serializers.profile_indicator_sorter import ProfileIndicatorSorter | ||
|
||
@pytest.fixture | ||
def profile(): | ||
return profile_factoryboy.ProfileFactory() | ||
|
||
@pytest.fixture | ||
def profile_indicators(profile): | ||
profile_indicator1 = profile_factoryboy.ProfileIndicatorFactory(profile=profile) | ||
profile_indicator2 = profile_factoryboy.ProfileIndicatorFactory(profile=profile) | ||
profile_indicator3 = profile_factoryboy.ProfileIndicatorFactory() | ||
|
||
return [profile_indicator1, profile_indicator2, profile_indicator3] | ||
|
||
@pytest.fixture | ||
def datasets(profile_indicators): | ||
return [ | ||
profile_indicators[0].indicator.dataset, | ||
profile_indicators[1].indicator.dataset, | ||
profile_indicators[2].indicator.dataset, | ||
] | ||
|
||
@pytest.fixture | ||
def groups(datasets): | ||
subindicators1 = ["g1s3", "g1s2", "g1s1"] | ||
subindicators2 = ["g2s2", "g2s1", "g2s3"] | ||
subindicators3 = ["g3s1", "g3s2", "g3s3"] | ||
subindicators4 = ["a", "b"] | ||
|
||
return [ | ||
datasets_factoryboy.GroupFactory(name="group1", dataset=datasets[0], subindicators=subindicators1), | ||
datasets_factoryboy.GroupFactory(name="group2", dataset=datasets[0], subindicators=subindicators2), | ||
datasets_factoryboy.GroupFactory(name="group3", dataset=datasets[1], subindicators=subindicators3), | ||
datasets_factoryboy.GroupFactory(name="unrelated group", dataset=datasets[2], subindicators=subindicators4), | ||
] | ||
|
||
|
||
@pytest.fixture | ||
def subindicators_data(): | ||
return OrderedDict(a="x", b="y", c="z") | ||
|
||
@pytest.fixture | ||
def profile_indicator_sorter(profile, groups): | ||
""" | ||
groups passed in to make sure that appropriate objects are created | ||
""" | ||
return ProfileIndicatorSorter(profile) | ||
|
||
@pytest.fixture | ||
def test_data(datasets): | ||
data = [{ | ||
"dataset": datasets[0].id, | ||
"indicator_group": ["group1", "group2"], | ||
"jsdata": { | ||
"subindicators": {"g1s1": "ABC", "g1s2": "DEF", "g1s3": "GHI", }, | ||
"groups": { | ||
"group2": { | ||
"g2s2": {"g1s1": 4, "g1s2": 5, "g1s3": 6}, | ||
"g2s1": {"g1s1": 1, "g1s2": 2, "g1s3": 3}, | ||
"g2s3": {"g1s1": 7, "g1s2": 8, "g1s3": 9}, | ||
} | ||
} | ||
} | ||
}, { | ||
"dataset": datasets[0].id, | ||
"indicator_group": ["group1", "group2"], | ||
"jsdata": { | ||
"subindicators": {"g1s2": "123", "g1s1": "456", "g1s3": "789", }, | ||
"groups": { | ||
"group2": { | ||
"g2s2": {"g1s1": 40, "g1s2": 50, "g1s3": 60}, | ||
"g2s1": {"g1s1": 10, "g1s2": 20, "g1s3": 30}, | ||
"g2s3": {"g1s1": 70, "g1s2": 80, "g1s3": 90}, | ||
} | ||
} | ||
} | ||
}] | ||
|
||
return data | ||
|
||
@pytest.fixture | ||
def expected_subindicators(): | ||
return [ | ||
{"g1s3": "GHI", "g1s2": "DEF", "g1s1": "ABC", }, | ||
{"g1s3": "789", "g1s2": "123", "g1s1": "456", } | ||
] | ||
|
||
@pytest.fixture | ||
def expected_groups(): | ||
return [ | ||
{ | ||
"group2": OrderedDict( | ||
g2s2=OrderedDict(g1s3=6, g1s2=5, g1s1=4), | ||
g2s1=OrderedDict(g1s3=3, g1s2=2, g1s1=1), | ||
g2s3=OrderedDict(g1s3=9, g1s2=8, g1s1=7), | ||
), | ||
}, | ||
{ | ||
"group2": OrderedDict( | ||
g2s2=OrderedDict(g1s3=60, g1s2=50, g1s1=40), | ||
g2s1=OrderedDict(g1s3=30, g1s2=20, g1s1=10), | ||
g2s3=OrderedDict(g1s3=90, g1s2=80, g1s1=70), | ||
) | ||
} | ||
] | ||
return { | ||
"group2": [ | ||
OrderedDict( | ||
g2s2=OrderedDict(g1s3=6, g1s2=5, g1s1=4), | ||
g2s1=OrderedDict(g1s3=3, g1s2=2, g1s1=1), | ||
g2s3=OrderedDict(g1s3=9, g1s2=8, g1s1=7), | ||
), | ||
OrderedDict( | ||
g2s2=OrderedDict(g1s3=60, g1s2=50, g1s1=40), | ||
g2s1=OrderedDict(g1s3=30, g1s2=20, g1s1=10), | ||
g2s3=OrderedDict(g1s3=90, g1s2=80, g1s1=70), | ||
) | ||
] | ||
} | ||
|
||
|
||
@pytest.fixture | ||
def alternative_expected_groups(): | ||
return [{ | ||
"group2": { | ||
"g2s1": OrderedDict(g1s3=3, g1s2=2, g1s1=1), | ||
"g2s3": OrderedDict(g1s3=9, g1s2=8, g1s1=7), | ||
"g2s2": OrderedDict(g1s3=6, g1s2=5, g1s1=4), | ||
} | ||
}, | ||
{ | ||
"group2": { | ||
"g2s1": OrderedDict(g1s3=30, g1s2=20, g1s1=10), | ||
"g2s3": OrderedDict(g1s3=90, g1s2=80, g1s1=70), | ||
"g2s2": OrderedDict(g1s3=60, g1s2=50, g1s1=40), | ||
} | ||
}] | ||
|
||
@pytest.mark.django_db | ||
class TestProfileIndicatorSorter: | ||
def test_create_profile_indicator_sorter(self, datasets, groups, profile_indicator_sorter): | ||
|
||
sorters = profile_indicator_sorter._sorters | ||
|
||
assert len(sorters) == 2 | ||
assert list(sorters.keys()) == [datasets[0].id, datasets[1].id] | ||
|
||
subindicator_sorter1 = sorters[datasets[0].id] | ||
si_groups1 = subindicator_sorter1._group_orders | ||
assert list(si_groups1.keys()) == [groups[0].name, groups[1].name] | ||
assert si_groups1[groups[0].name] == groups[0].subindicators | ||
assert si_groups1[groups[1].name] == groups[1].subindicators | ||
|
||
subindicator_sorter2 = sorters[datasets[1].id] | ||
si_groups2 = subindicator_sorter2._group_orders | ||
assert list(si_groups2.keys()) == [groups[2].name] | ||
assert si_groups2[groups[2].name] == groups[2].subindicators | ||
|
||
@pytest.mark.django_db | ||
def test_sort_subindicators(self, profile_indicator_sorter, datasets, test_data, expected_subindicators): | ||
sorted_data = profile_indicator_sorter.sort_subindicators(test_data) | ||
sorted_data = list(sorted_data) | ||
assert sorted_data[0]["jsdata"]["subindicators"] == expected_subindicators[0] | ||
assert sorted_data[1]["jsdata"]["subindicators"] == expected_subindicators[1] | ||
|
||
@pytest.mark.django_db | ||
def test_sort_groups(self, profile_indicator_sorter, test_data, expected_groups): | ||
sorted_data = profile_indicator_sorter.sort_groups(test_data) | ||
sorted_data = list(sorted_data) | ||
assert sorted_data[0]["jsdata"]["groups"] == expected_groups[0] | ||
assert sorted_data[1]["jsdata"]["groups"] == expected_groups[1] | ||
|
||
@pytest.mark.django_db | ||
def test_sort(self, profile_indicator_sorter, test_data, expected_subindicators, expected_groups): | ||
sorted_data = profile_indicator_sorter.sort_groups(test_data) | ||
sorted_data = list(sorted_data) | ||
|
||
assert sorted_data[0]["jsdata"]["subindicators"] == expected_subindicators[0] | ||
assert sorted_data[1]["jsdata"]["subindicators"] == expected_subindicators[1] | ||
|
||
assert sorted_data[0]["jsdata"]["groups"] == expected_groups[0] | ||
assert sorted_data[1]["jsdata"]["groups"] == expected_groups[1] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
from collections import OrderedDict | ||
|
||
import pytest | ||
|
||
from wazimap_ng.profile.serializers.subindicator_sorter import SubindicatorSorter | ||
|
||
@pytest.fixture | ||
def subindicators(): | ||
return OrderedDict(a="x", b="y", c="z") | ||
|
||
@pytest.fixture | ||
def group_subindicators(): | ||
return { | ||
"group1": { | ||
"a": {"c1": 10, "c2": 20, "c3": 30}, | ||
"b": {"c1": 100, "c2": 200, "c3": 300}, | ||
"c": {"c1": 1000, "c2": 2000, "c3": 3000} | ||
}, | ||
"group2": { | ||
"x": {"c1": 40, "c2": 50, "c3": 60}, | ||
"y": {"c1": 400, "c2": 500, "c3": 600}, | ||
"z": {"c1": 4000, "c2": 5000, "c3": 6000} | ||
} | ||
} | ||
|
||
@pytest.fixture | ||
def sorter(): | ||
group_orders = { | ||
"group1": ["b", "c", "a"], | ||
"group2": ["z", "y", "x"], | ||
"group3": ["c2", "c3", "c1"], | ||
} | ||
sorter = SubindicatorSorter(group_orders) | ||
|
||
return sorter | ||
|
||
@pytest.fixture | ||
def alternative_sorter(): | ||
group_orders = { | ||
"group1": ["c", "a", "b"], | ||
"group2": ["x", "z", "y"], | ||
"group3": ["c3", "c1", "c2"], | ||
} | ||
sorter = SubindicatorSorter(group_orders) | ||
|
||
return sorter | ||
|
||
class TestSubindicatorSorter: | ||
def test_subindicator_sort(self, sorter, subindicators): | ||
sort_order = ["b", "c", "a"] | ||
|
||
sorted_subindicators = sorter.sort_subindicators(subindicators, "group1") | ||
assert sorted_subindicators == OrderedDict(b="y", c="z", a="x") | ||
|
||
sort_order = ["c", "a", "b"] | ||
sorter._group_orders["group1"] = sort_order | ||
|
||
sorted_subindicators = sorter.sort_subindicators(subindicators, "group1") | ||
assert sorted_subindicators == OrderedDict(c="z", a="x", b="y") | ||
|
||
def test_subindicator_sort_with_missing_order(self, sorter, subindicators): | ||
sorted_subindicators = sorter.sort_subindicators(subindicators, "missing group") | ||
assert sorted_subindicators == OrderedDict(a="x", b="y", c="z") | ||
|
||
def test_subindicator_sort_with_incomplete_order(self, sorter, subindicators): | ||
sorter._group_orders["group1"] = ["b", "c"] | ||
sorted_subindicators = sorter.sort_subindicators(subindicators, "group1") | ||
|
||
assert sorted_subindicators == OrderedDict(b="y", c="z", a="x") | ||
|
||
def test_subindicator_sort_with_extra_order_element(self, sorter, subindicators): | ||
sorter._group_orders["group1"] = ["b", "c", "a", "z"] | ||
sorted_subindicators = sorter.sort_subindicators(subindicators, "group1") | ||
|
||
assert sorted_subindicators == OrderedDict(b="y", c="z", a="x") | ||
|
||
def test_subindicator_duplicate_keys_are_ignored(self, sorter, subindicators): | ||
sorter._group_orders["group1"] = ["b", "c", "a", "b"] | ||
|
||
sorted_subindicators = sorter.sort_subindicators(subindicators, "group1") | ||
assert sorted_subindicators == OrderedDict(b="y", c="z", a="x") | ||
|
||
def test_subindicator_no_side_effects(self, sorter, subindicators): | ||
sort_order = ["b", "c", "a"] | ||
|
||
sorted_subindicators = sorter.sort_subindicators(subindicators, "group1") | ||
sorted_subindicators["dummy"] = "XXX" | ||
assert "dummy" not in subindicators | ||
|
||
def test_group_sort(self, sorter, group_subindicators): | ||
sorted_groups = sorter.sort_groups(group_subindicators, "group3") | ||
sorted_group1 = sorted_groups["group1"] | ||
sorted_group2 = sorted_groups["group2"] | ||
|
||
assert list(sorted_group1.keys()) == ["b", "c", "a"] | ||
assert isinstance(sorted_group1, OrderedDict) | ||
|
||
sorted_group1_group3 = list(sorted_group1.values()) | ||
assert sorted_group1_group3[0] == OrderedDict(c2=200, c3=300, c1=100) | ||
assert sorted_group1_group3[1] == OrderedDict(c2=2000, c3=3000, c1=1000) | ||
assert sorted_group1_group3[2] == OrderedDict(c2=20, c3=30, c1=10) | ||
|
||
assert list(sorted_group2.keys()) == ["z", "y", "x"] | ||
assert isinstance(sorted_group2, OrderedDict) | ||
sorted_group2_group3 = list(sorted_group2.values()) | ||
assert sorted_group2_group3[0] == OrderedDict(c2=5000, c3=6000, c1=4000) | ||
assert sorted_group2_group3[1] == OrderedDict(c2=500, c3=600, c1=400) | ||
assert sorted_group2_group3[2] == OrderedDict(c2=50, c3=60, c1=40) | ||
|
||
def test_alternative_group_sort(self, alternative_sorter, group_subindicators): | ||
sorter = alternative_sorter | ||
sorted_groups = sorter.sort_groups(group_subindicators, "group3") | ||
sorted_group1 = sorted_groups["group1"] | ||
sorted_group2 = sorted_groups["group2"] | ||
|
||
assert list(sorted_group1.keys()) == ["c", "a", "b"] | ||
assert isinstance(sorted_group1, OrderedDict) | ||
|
||
sorted_group1_group3 = list(sorted_group1.values()) | ||
assert sorted_group1_group3[0] == OrderedDict(c3=3000, c1=1000, c2=2000) | ||
assert sorted_group1_group3[1] == OrderedDict(c3=30, c1=10, c2=20) | ||
assert sorted_group1_group3[2] == OrderedDict(c3=300, c1=100, c2=200) | ||
|
||
assert list(sorted_group2.keys()) == ["x", "z", "y"] | ||
assert isinstance(sorted_group2, OrderedDict) | ||
|
||
sorted_group2_group3 = list(sorted_group2.values()) | ||
assert sorted_group2_group3[0] == OrderedDict(c3=60, c1=40, c2=50) | ||
assert sorted_group2_group3[1] == OrderedDict(c3=6000, c1=4000, c2=5000) | ||
assert sorted_group2_group3[2] == OrderedDict(c3=600, c1=400, c2=500) | ||
|
||
def test_group_subset(self, sorter, group_subindicators): | ||
del group_subindicators["group1"] | ||
|
||
sorted_groups = sorter.sort_groups(group_subindicators, "group3") | ||
assert len(sorted_groups) == 1 | ||
|
||
sorted_group1 = sorted_groups["group2"] | ||
assert list(sorted_group1.keys()) == ["z", "y", "x"] | ||
|
||
def test_group_empty(self, sorter, group_subindicators): | ||
group_subindicators["group4"] = group_subindicators["group1"] | ||
del group_subindicators["group1"] | ||
del group_subindicators["group2"] | ||
|
||
sorted_groups = sorter.sort_groups(group_subindicators, "group3") | ||
assert len(sorted_groups) == 1 | ||
|
||
sorted_group4 = sorted_groups["group4"] | ||
assert list(sorted_group4.keys()) == ["a", "b", "c"] | ||
|
||
def test_group_no_side_effects(self, sorter, group_subindicators): | ||
sorted_groups = sorter.sort_groups(group_subindicators, "group3") | ||
sorted_groups["dummy"] = "XXX" | ||
|
||
assert "dummy" not in group_subindicators |
Oops, something went wrong.