Skip to content

Commit

Permalink
Merge pull request #115 from OpenUpSA/fix/subindicator-sorting
Browse files Browse the repository at this point in the history
Fix/Subindicator sorting
  • Loading branch information
milafrerichs authored Sep 29, 2020
2 parents 585961d + 2559cf3 commit 848f3e4
Show file tree
Hide file tree
Showing 7 changed files with 571 additions and 68 deletions.
16 changes: 16 additions & 0 deletions tests/datasets/factoryboy.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,19 @@ class Meta:

dataset = factory.SubFactory(DatasetFactory)
universe = factory.SubFactory(UniverseFactory)


class IndicatorDataFactory(factory.django.DjangoModelFactory):
class Meta:
model = models.IndicatorData

indicator = factory.SubFactory(IndicatorFactory)
geography = factory.SubFactory(GeographyFactory)


class GroupFactory(factory.django.DjangoModelFactory):
class Meta:
model = models.Group

dataset = factory.SubFactory(DatasetFactory)

189 changes: 189 additions & 0 deletions tests/profile/serializers/test_profile_indicator_sorter.py
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]
156 changes: 156 additions & 0 deletions tests/profile/serializers/test_subindicator_sorter.py
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
Loading

0 comments on commit 848f3e4

Please sign in to comment.