diff --git a/stats/analytics.py b/stats/analytics.py
index fa278566265..4005d539fb7 100644
--- a/stats/analytics.py
+++ b/stats/analytics.py
@@ -7,6 +7,7 @@
import copy
import csv
+import glob
import json
import os
import re
@@ -14,6 +15,7 @@
from datetime import date, datetime, timedelta
from decimal import Decimal, InvalidOperation
+from bdd_tester import BDDTester
import iatirulesets
from dateutil.relativedelta import relativedelta
from helpers.currency_conversion import get_USD_value
@@ -39,6 +41,38 @@
)
+INDEX_INDICATOR_DEFINTIONS_PATH = './2024-Index-indicator-definitions'
+
+
+def load_ati_tests():
+ """Load the index tests."""
+ base_path = os.path.join(INDEX_INDICATOR_DEFINTIONS_PATH, "test_definitions")
+ step_definitions = os.path.join(base_path, "step_definitions.py")
+ feature_filepaths = glob.glob(os.path.join(base_path, "*", "*.feature"))
+ tester = BDDTester(step_definitions)
+ all_tests = [t for feature_filepath in feature_filepaths for t in tester.load_feature(feature_filepath).tests]
+
+ # Remove the current data condition from tests.
+ for test in all_tests:
+ test.steps = [x for x in test.steps if not (x.step_type == "given" and x.text == "the activity is current")]
+
+ return all_tests
+
+
+ati_tests = load_ati_tests()
+
+
+def load_ati_current_data_test():
+ """Load the current data test."""
+ base_path = os.path.join(INDEX_INDICATOR_DEFINTIONS_PATH, "test_definitions")
+ step_definitions = os.path.join(base_path, "step_definitions.py")
+ tester = BDDTester(step_definitions)
+ return tester.load_feature(os.path.join(base_path, "current_data.feature")).tests[0]
+
+
+ati_current_data_test = load_ati_current_data_test()
+
+
def add_years(d, years):
"""Return a date that's `years` years before/after the date (or datetime)
object `d`. Return the same calendar date (month and day) in the
@@ -2034,6 +2068,28 @@ def transaction_total(self):
out += 1
return out
+ @returns_numberdict
+ @memoize
+ def ati_tests(self):
+ out = {}
+ for test in ati_tests:
+ result = test(self.element)
+ result = int(bool(result))
+ out[f"{test.feature.name}: {test.name}"] = result
+ return out
+
+ @returns_number
+ @memoize
+ def ati_current(self):
+ return int(bool(ati_current_data_test(self.element)))
+
+ @returns_numberdict
+ def ati_tests_current(self):
+ if self.ati_current():
+ return self.ati_tests()
+ else:
+ return {}
+
ckan = json.load(open("helpers/ckan.json"))
publisher_re = re.compile(r"(.*)\-[^\-]")
diff --git a/stats/tests/test_ati.py b/stats/tests/test_ati.py
new file mode 100644
index 00000000000..6d4406702dc
--- /dev/null
+++ b/stats/tests/test_ati.py
@@ -0,0 +1,48 @@
+# coding=utf-8
+import pytest
+from lxml import etree
+
+from stats.analytics import ActivityStats
+
+
+class MockActivityStats(ActivityStats):
+ def __init__(self, major_version):
+ self.major_version = major_version
+ return super(MockActivityStats, self).__init__()
+
+ def _major_version(self):
+ return self.major_version
+
+
+@pytest.mark.parametrize("major_version", ["1", "2"])
+def test_comprehensiveness_is_current(major_version):
+ activity_stats = MockActivityStats(major_version)
+
+ activity_stats.element = etree.fromstring(
+ """
+
+
+ Title
+
+
+ """
+ )
+ ati_dict = activity_stats.ati_tests()
+ assert type(ati_dict) is dict
+ ati_dict["Title: Title is present"] == 1
+ ati_dict["Title: Title has at least 10 characters"] == 0
+
+ activity_stats.element = etree.fromstring(
+ """
+
+
+ Title with at least 10 characters
+
+
+ """
+ )
+
+ ati_dict = activity_stats.ati_tests()
+ assert type(ati_dict) is dict
+ ati_dict["Title: Title is present"] == 1
+ ati_dict["Title: Title has at least 10 characters"] == 0