Repository: hugovk/pypistats

Issue:
Add total row to tables
| category |  downloads  |
|----------|------------:|
|      2.7 |   1,594,594 |
|      2.6 |       1,968 |
|      3.3 |       1,824 |
|      3.2 |         244 |
|      3.1 |           3 |
| Total    |   1,598,633 |

Discussion:

Patch:
diff --git a/README.md b/README.md
index 46cbe21..850c1d8 100644
--- a/README.md
+++ b/README.md
@@ -19,35 +19,32 @@ pip install .
 
 ## Example use
 
-Return values are the JSON responses documented in the API: 
+Return values are from the JSON responses documented in the API: 
 https://pypistats.org/api/
 
 ```python
 import pypistats
 from pprint import pprint
 
-# Initialise the API
-api = pypistats.PyPiStats()
-
 # Call the API
-print(api.recent("pillow"))
-print(api.recent("pillow", "day", output="table"))
-pprint(api.recent("pillow", "week", output="json"))
-print(api.recent("pillow", "month"))
-
-print(api.overall("pillow"))
-print(api.overall("pillow", mirrors=True, output="table"))
-pprint(api.overall("pillow", mirrors=False, output="json"))
-
-print(api.python_major("pillow"))
-print(api.python_major("pillow", version=2, output="table"))
-pprint(api.python_major("pillow", version="3", output="json"))
-
-print(api.python_minor("pillow"))
-print(api.python_minor("pillow", version=2.7, output="table"))
-pprint(api.python_minor("pillow", version="3.7", output="json"))
-
-print(api.system("pillow"))
-print(api.system("pillow", os="darwin", output="table"))
-pprint(api.system("pillow", os="linux", output="json"))
+print(pypistats.recent("pillow"))
+print(pypistats.recent("pillow", "day", output="table"))
+pprint(pypistats.recent("pillow", "week", output="json"))
+print(pypistats.recent("pillow", "month"))
+
+print(pypistats.overall("pillow"))
+print(pypistats.overall("pillow", mirrors=True, output="table"))
+pprint(pypistats.overall("pillow", mirrors=False, output="json"))
+
+print(pypistats.python_major("pillow"))
+print(pypistats.python_major("pillow", version=2, output="table"))
+pprint(pypistats.python_major("pillow", version="3", output="json"))
+
+print(pypistats.python_minor("pillow"))
+print(pypistats.python_minor("pillow", version=2.7, output="table"))
+pprint(pypistats.python_minor("pillow", version="3.7", output="json"))
+
+print(pypistats.system("pillow"))
+print(pypistats.system("pillow", os="darwin", output="table"))
+pprint(pypistats.system("pillow", os="linux", output="json"))
 ```
diff --git a/pypistats/__init__.py b/pypistats/__init__.py
index 719f31a..9ebc7e6 100644
--- a/pypistats/__init__.py
+++ b/pypistats/__init__.py
@@ -46,10 +46,12 @@ def pypi_stats_api(
     if output == "json":
         return res
 
-    # Only sort for table
+    # These only for table
     if sort:
         res["data"] = _sort(res["data"])
 
+    res["data"] = _grand_total(res["data"])
+
     data = res["data"]
     return _tabulate(data)
 
@@ -76,7 +78,7 @@ def _filter(data, start_date=None, end_date=None):
 def _sort(data):
     """Sort by downloads"""
 
-    # Only sum lists of dicts, not a single dict
+    # Only for lists of dicts, not a single dict
     if isinstance(data, dict):
         return data
 
@@ -87,7 +89,7 @@ def _sort(data):
 def _total(data):
     """Sum all downloads per category, regardless of date"""
 
-    # Only sum lists of dicts, not a single dict
+    # Only for lists of dicts, not a single dict
     if isinstance(data, dict):
         return data
 
@@ -105,6 +107,20 @@ def _total(data):
     return data
 
 
+def _grand_total(data):
+    """Add a grand total row"""
+
+    # Only for lists of dicts, not a single dict
+    if isinstance(data, dict):
+        return data
+
+    grand_total = sum(row["downloads"] for row in data)
+    new_row = {"category": "Total", "downloads": grand_total}
+    data.append(new_row)
+
+    return data
+
+
 def _tabulate(data):
     """Return data in table"""
 


Test patch:
diff --git a/tests/test_pypistats.py b/tests/test_pypistats.py
index cad9a90..1e3f8fe 100644
--- a/tests/test_pypistats.py
+++ b/tests/test_pypistats.py
@@ -3,9 +3,11 @@
 """
 Unit tests for pypistats
 """
+import copy
 import unittest
 
 import pypistats
+from data.python_minor import data as PYTHON_MINOR_DATA
 
 SAMPLE_DATA = [
     {"category": "2.6", "date": "2018-08-15", "downloads": 51},
@@ -24,7 +26,7 @@ SAMPLE_DATA = [
 class TestPypiStats(unittest.TestCase):
     def test__filter_no_filters_no_change(self):
         # Arrange
-        from data.python_minor import data
+        data = copy.deepcopy(PYTHON_MINOR_DATA)
 
         # Act
         output = pypistats._filter(data)
@@ -34,8 +36,7 @@ class TestPypiStats(unittest.TestCase):
 
     def test__filter_start_date(self):
         # Arrange
-        from data.python_minor import data
-
+        data = copy.deepcopy(PYTHON_MINOR_DATA)
         start_date = "2018-09-22"
 
         # Act
@@ -48,8 +49,7 @@ class TestPypiStats(unittest.TestCase):
 
     def test__filter_end_date(self):
         # Arrange
-        from data.python_minor import data
-
+        data = copy.deepcopy(PYTHON_MINOR_DATA)
         end_date = "2018-04-22"
 
         # Act
@@ -62,8 +62,7 @@ class TestPypiStats(unittest.TestCase):
 
     def test__filter_start_and_end_date(self):
         # Arrange
-        from data.python_minor import data
-
+        data = copy.deepcopy(PYTHON_MINOR_DATA)
         start_date = "2018-09-01"
         end_date = "2018-09-11"
 
@@ -184,7 +183,7 @@ class TestPypiStats(unittest.TestCase):
 
     def test__total(self):
         # Arrange
-        from data.python_minor import data
+        data = copy.deepcopy(PYTHON_MINOR_DATA)
 
         # Act
         output = pypistats._total(data)
@@ -203,3 +202,26 @@ class TestPypiStats(unittest.TestCase):
 
         # Assert
         self.assertEqual(output, data)
+
+    def test__grand_total(self):
+        # Arrange
+        data = copy.deepcopy(PYTHON_MINOR_DATA)
+        original_len = len(data)
+
+        # Act
+        output = pypistats._grand_total(data)
+
+        # Assert
+        self.assertEqual(len(output), original_len + 1)
+        self.assertEqual(output[-1]["category"], "Total")
+        self.assertEqual(output[-1]["downloads"], 9355317)
+
+    def test__grand_total_recent(self):
+        # Arrange
+        data = {"last_day": 123002, "last_month": 3254221, "last_week": 761649}
+
+        # Act
+        output = pypistats._grand_total(data)
+
+        # Assert
+        self.assertEqual(output, data)