Repository: Axelrod-Python/Axelrod

Issue:
Results set processing shouldn't be in the progress bar

Discussion:

Patch:
diff --git a/axelrod/result_set.py b/axelrod/result_set.py
index b2bc1b94..b0a67300 100644
--- a/axelrod/result_set.py
+++ b/axelrod/result_set.py
@@ -1,5 +1,6 @@
 from collections import defaultdict
 import csv
+import tqdm
 
 from numpy import mean, nanmedian, std
 
@@ -14,10 +15,25 @@ except ImportError:
     from io import StringIO
 
 
+def update_progress_bar(method):
+    """A decorator to update a progress bar if it exists"""
+    def wrapper(*args):
+        """Run the method and update the progress bar if it exists"""
+        output = method(*args)
+
+        try:
+            args[0].progress_bar.update(1)
+        except AttributeError:
+            pass
+
+        return output
+    return wrapper
+
+
 class ResultSet(object):
     """A class to hold the results of a tournament."""
 
-    def __init__(self, players, interactions, with_morality=True):
+    def __init__(self, players, interactions, progress_bar=True):
         """
         Parameters
         ----------
@@ -26,19 +42,24 @@ class ResultSet(object):
             interactions : list
                 a list of dictionaries mapping tuples of player indices to
                 interactions (1 for each repetition)
-            with_morality : bool
-                a flag to determine whether morality metrics should be
-                calculated.
+            progress_bar : bool
+                Whether or not to create a progress bar which will be updated
         """
         self.players = players
         self.nplayers = len(players)
         self.interactions = interactions
         self.nrepetitions = max([len(rep) for rep in list(interactions.values())])
 
+        if progress_bar:
+            self.progress_bar = tqdm.tqdm(total=19, desc="Analysing results")
+        else:
+            self.progress_bar = False
+
         # Calculate all attributes:
-        self.build_all(with_morality)
+        self.build_all()
+
 
-    def build_all(self, with_morality):
+    def build_all(self):
         """Build all the results. In a seperate method to make inheritance more
         straightforward"""
         self.wins = self.build_wins()
@@ -54,15 +75,19 @@ class ResultSet(object):
         self.score_diffs = self.build_score_diffs()
         self.payoff_diffs_means = self.build_payoff_diffs_means()
 
-        if with_morality:
-            self.cooperation = self.build_cooperation()
-            self.normalised_cooperation = self.build_normalised_cooperation()
-            self.vengeful_cooperation = self.build_vengeful_cooperation()
-            self.cooperating_rating = self.build_cooperating_rating()
-            self.good_partner_matrix = self.build_good_partner_matrix()
-            self.good_partner_rating = self.build_good_partner_rating()
-            self.eigenmoses_rating = self.build_eigenmoses_rating()
-            self.eigenjesus_rating = self.build_eigenjesus_rating()
+        self.cooperation = self.build_cooperation()
+        self.normalised_cooperation = self.build_normalised_cooperation()
+        self.vengeful_cooperation = self.build_vengeful_cooperation()
+        self.cooperating_rating = self.build_cooperating_rating()
+        self.good_partner_matrix = self.build_good_partner_matrix()
+        self.good_partner_rating = self.build_good_partner_rating()
+        self.eigenmoses_rating = self.build_eigenmoses_rating()
+        self.eigenjesus_rating = self.build_eigenjesus_rating()
+
+        try:
+            self.progress_bar.close()
+        except AttributeError:
+            pass
 
     @property
     def _null_results_matrix(self):
@@ -79,6 +104,7 @@ class ResultSet(object):
         replist = list(range(self.nrepetitions))
         return [[[0 for j in plist] for i in plist] for r in replist]
 
+    @update_progress_bar
     def build_match_lengths(self):
         """
         Returns:
@@ -110,6 +136,7 @@ class ResultSet(object):
 
         return match_lengths
 
+    @update_progress_bar
     def build_scores(self):
         """
         Returns:
@@ -143,6 +170,7 @@ class ResultSet(object):
 
         return scores
 
+    @update_progress_bar
     def build_ranked_names(self):
         """
         Returns:
@@ -150,8 +178,10 @@ class ResultSet(object):
             Returns the ranked names. A list of names as calculated by
             self.ranking.
         """
+
         return [str(self.players[i]) for i in self.ranking]
 
+    @update_progress_bar
     def build_wins(self):
         """
         Returns:
@@ -187,6 +217,7 @@ class ResultSet(object):
 
         return wins
 
+    @update_progress_bar
     def build_normalised_scores(self):
         """
         Returns:
@@ -229,6 +260,7 @@ class ResultSet(object):
 
         return normalised_scores
 
+    @update_progress_bar
     def build_ranking(self):
         """
         Returns:
@@ -244,6 +276,7 @@ class ResultSet(object):
         return sorted(range(self.nplayers),
                       key=lambda i: -nanmedian(self.normalised_scores[i]))
 
+    @update_progress_bar
     def build_payoffs(self):
         """
         Returns:
@@ -281,8 +314,10 @@ class ResultSet(object):
                             utilities.append(iu.compute_final_score_per_turn(interaction)[1])
 
                     payoffs[player][opponent] = utilities
+
         return payoffs
 
+    @update_progress_bar
     def build_payoff_matrix(self):
         """
         Returns:
@@ -317,6 +352,7 @@ class ResultSet(object):
 
         return payoff_matrix
 
+    @update_progress_bar
     def build_payoff_stddevs(self):
         """
         Returns:
@@ -353,6 +389,7 @@ class ResultSet(object):
 
         return payoff_stddevs
 
+    @update_progress_bar
     def build_score_diffs(self):
         """
         Returns:
@@ -391,8 +428,10 @@ class ResultSet(object):
                         scores = iu.compute_final_score_per_turn(interaction)
                         diff = (scores[1] - scores[0])
                         score_diffs[player][opponent][repetition] = diff
+
         return score_diffs
 
+    @update_progress_bar
     def build_payoff_diffs_means(self):
         """
         Returns:
@@ -429,8 +468,10 @@ class ResultSet(object):
                     payoff_diffs_means[player][opponent] = mean(diffs)
                 else:
                     payoff_diffs_means[player][opponent] = 0
+
         return payoff_diffs_means
 
+    @update_progress_bar
     def build_cooperation(self):
         """
         Returns:
@@ -465,8 +506,10 @@ class ResultSet(object):
                                 coop_count += iu.compute_cooperations(interaction)[1]
 
                         cooperations[player][opponent] += coop_count
+
         return cooperations
 
+    @update_progress_bar
     def build_normalised_cooperation(self):
         """
         Returns:
@@ -507,8 +550,10 @@ class ResultSet(object):
 
                 # Mean over all reps:
                 normalised_cooperations[player][opponent] = mean(coop_counts)
+
         return normalised_cooperations
 
+    @update_progress_bar
     def build_vengeful_cooperation(self):
         """
         Returns:
@@ -522,6 +567,7 @@ class ResultSet(object):
         return [[2 * (element - 0.5) for element in row]
                 for row in self.normalised_cooperation]
 
+    @update_progress_bar
     def build_cooperating_rating(self):
         """
         Returns:
@@ -552,6 +598,7 @@ class ResultSet(object):
         return [sum(cs) / max(1, float(sum(ls))) for cs, ls
                 in zip(self.cooperation, lengths)]
 
+    @update_progress_bar
     def build_good_partner_matrix(self):
         """
         Returns:
@@ -586,6 +633,7 @@ class ResultSet(object):
 
         return good_partner_matrix
 
+    @update_progress_bar
     def build_good_partner_rating(self):
         """
         Returns:
@@ -607,6 +655,7 @@ class ResultSet(object):
 
         return good_partner_rating
 
+    @update_progress_bar
     def build_eigenjesus_rating(self):
         """
         Returns:
@@ -617,8 +666,10 @@ class ResultSet(object):
         """
         eigenvector, eigenvalue = eigen.principal_eigenvector(
                 self.normalised_cooperation)
+
         return eigenvector.tolist()
 
+    @update_progress_bar
     def build_eigenmoses_rating(self):
         """
         Returns:
@@ -629,6 +680,7 @@ class ResultSet(object):
         """
         eigenvector, eigenvalue = eigen.principal_eigenvector(
                 self.vengeful_cooperation)
+
         return eigenvector.tolist()
 
     def csv(self):
@@ -655,22 +707,26 @@ class ResultSetFromFile(ResultSet):
     by the tournament class.
     """
 
-    def __init__(self, filename, with_morality=True):
+    def __init__(self, filename, progress_bar=True):
         """
         Parameters
         ----------
             filename : string
                 name of a file of the correct file.
-            with_morality : bool
-                a flag to determine whether morality metrics should be
-                calculated.
+            progress_bar : bool
+                Whether or not to create a progress bar which will be updated
         """
         self.players, self.interactions = self._read_csv(filename)
         self.nplayers = len(self.players)
         self.nrepetitions = len(list(self.interactions.values())[0])
 
+        if progress_bar:
+            self.progress_bar = tqdm.tqdm(total=19, desc="Analysing results")
+        else:
+            self.progress_bar = False
+
         # Calculate all attributes:
-        self.build_all(with_morality)
+        self.build_all()
 
     def _read_csv(self, filename):
         """
diff --git a/axelrod/tournament.py b/axelrod/tournament.py
index 6b638aa1..32684643 100644
--- a/axelrod/tournament.py
+++ b/axelrod/tournament.py
@@ -85,7 +85,8 @@ class Tournament(object):
         axelrod.ResultSet
         """
         if progress_bar:
-            self.progress_bar = tqdm.tqdm(total=len(self.match_generator))
+            self.progress_bar = tqdm.tqdm(total=len(self.match_generator),
+                                          desc="Playing matches")
 
         self.setup_output_file(filename)
         if not build_results and not filename:
@@ -96,13 +97,16 @@ class Tournament(object):
         else:
             self._run_parallel(processes=processes, progress_bar=progress_bar)
 
+        if progress_bar:
+            self.progress_bar.close()
+
         # Make sure that python has finished writing to disk
         self.outputfile.flush()
 
         if build_results:
-            return self._build_result_set()
+            return self._build_result_set(progress_bar=progress_bar)
 
-    def _build_result_set(self):
+    def _build_result_set(self, progress_bar=True):
         """
         Build the result set (used by the play method)
 
@@ -112,7 +116,7 @@ class Tournament(object):
         """
         result_set = ResultSetFromFile(
             filename=self.filename,
-            with_morality=self._with_morality)
+            progress_bar=progress_bar)
         self.outputfile.close()
         return result_set
 


Test patch:
diff --git a/axelrod/tests/unit/test_resultset.py b/axelrod/tests/unit/test_resultset.py
index 2df8666a..c5a084bb 100644
--- a/axelrod/tests/unit/test_resultset.py
+++ b/axelrod/tests/unit/test_resultset.py
@@ -161,7 +161,9 @@ class TestResultSet(unittest.TestCase):
             'Defector,Tit For Tat,Alternator\n2.6,1.7,1.5\n2.6,1.7,1.5\n2.6,1.7,1.5\n')
 
     def test_init(self):
-        rs = axelrod.ResultSet(self.players, self.interactions)
+        rs = axelrod.ResultSet(self.players, self.interactions,
+                               progress_bar=False)
+        self.assertFalse(rs.progress_bar)
         self.assertEqual(rs.players, self.players)
         self.assertEqual(rs.nplayers, len(self.players))
         self.assertEqual(rs.interactions, self.interactions)
@@ -176,13 +178,25 @@ class TestResultSet(unittest.TestCase):
                 self.assertIsInstance(interaction, list)
                 self.assertEqual(len(interaction), self.turns)
 
-    def test_null_results_matrix(self):
+    def test_with_progress_bar(self):
         rs = axelrod.ResultSet(self.players, self.interactions)
+        self.assertTrue(rs.progress_bar)
+        self.assertEqual(rs.progress_bar.total, 19)
+
+        rs = axelrod.ResultSet(self.players, self.interactions,
+                               progress_bar=True)
+        self.assertTrue(rs.progress_bar)
+        self.assertEqual(rs.progress_bar.total, 19)
+
+    def test_null_results_matrix(self):
+        rs = axelrod.ResultSet(self.players, self.interactions,
+                               progress_bar=False)
         self.assertEqual(
             rs._null_results_matrix, self.expected_null_results_matrix)
 
     def test_match_lengths(self):
-        rs = axelrod.ResultSet(self.players, self.interactions)
+        rs = axelrod.ResultSet(self.players, self.interactions,
+                               progress_bar=False)
         self.assertIsInstance(rs.match_lengths, list)
         self.assertEqual(len(rs.match_lengths), rs.nrepetitions)
         self.assertEqual(rs.match_lengths, self.expected_match_lengths)
@@ -202,49 +216,57 @@ class TestResultSet(unittest.TestCase):
                         self.assertEqual(length, self.turns)
 
     def test_scores(self):
-        rs = axelrod.ResultSet(self.players, self.interactions)
+        rs = axelrod.ResultSet(self.players, self.interactions,
+                               progress_bar=False)
         self.assertIsInstance(rs.scores, list)
         self.assertEqual(len(rs.scores), rs.nplayers)
         self.assertEqual(rs.scores, self.expected_scores)
 
     def test_ranking(self):
-        rs = axelrod.ResultSet(self.players, self.interactions)
+        rs = axelrod.ResultSet(self.players, self.interactions,
+                               progress_bar=False)
         self.assertIsInstance(rs.ranking, list)
         self.assertEqual(len(rs.ranking), rs.nplayers)
         self.assertEqual(rs.ranking, self.expected_ranking)
 
     def test_ranked_names(self):
-        rs = axelrod.ResultSet(self.players, self.interactions)
+        rs = axelrod.ResultSet(self.players, self.interactions,
+                               progress_bar=False)
         self.assertIsInstance(rs.ranked_names, list)
         self.assertEqual(len(rs.ranked_names), rs.nplayers)
         self.assertEqual(rs.ranked_names, self.expected_ranked_names)
 
     def test_wins(self):
-        rs = axelrod.ResultSet(self.players, self.interactions)
+        rs = axelrod.ResultSet(self.players, self.interactions,
+                               progress_bar=False)
         self.assertIsInstance(rs.wins, list)
         self.assertEqual(len(rs.wins), rs.nplayers)
         self.assertEqual(rs.wins, self.expected_wins)
 
     def test_normalised_scores(self):
-        rs = axelrod.ResultSet(self.players, self.interactions)
+        rs = axelrod.ResultSet(self.players, self.interactions,
+                               progress_bar=False)
         self.assertIsInstance(rs.normalised_scores, list)
         self.assertEqual(len(rs.normalised_scores), rs.nplayers)
         self.assertEqual(rs.normalised_scores, self.expected_normalised_scores)
 
     def test_payoffs(self):
-        rs = axelrod.ResultSet(self.players, self.interactions)
+        rs = axelrod.ResultSet(self.players, self.interactions,
+                               progress_bar=False)
         self.assertIsInstance(rs.payoffs, list)
         self.assertEqual(len(rs.payoffs), rs.nplayers)
         self.assertEqual(rs.payoffs, self.expected_payoffs)
 
     def test_payoff_matrix(self):
-        rs = axelrod.ResultSet(self.players, self.interactions)
+        rs = axelrod.ResultSet(self.players, self.interactions,
+                               progress_bar=False)
         self.assertIsInstance(rs.payoff_matrix, list)
         self.assertEqual(len(rs.payoff_matrix), rs.nplayers)
         self.assertEqual(rs.payoff_matrix, self.expected_payoff_matrix)
 
     def test_score_diffs(self):
-        rs = axelrod.ResultSet(self.players, self.interactions)
+        rs = axelrod.ResultSet(self.players, self.interactions,
+                               progress_bar=False)
         self.assertIsInstance(rs.score_diffs, list)
         self.assertEqual(len(rs.score_diffs), rs.nplayers)
         for i, row in enumerate(rs.score_diffs):
@@ -254,7 +276,8 @@ class TestResultSet(unittest.TestCase):
                                      self.expected_score_diffs[i][j][k])
 
     def test_payoff_diffs_means(self):
-        rs = axelrod.ResultSet(self.players, self.interactions)
+        rs = axelrod.ResultSet(self.players, self.interactions,
+                               progress_bar=False)
         self.assertIsInstance(rs.payoff_diffs_means, list)
         self.assertEqual(len(rs.payoff_diffs_means), rs.nplayers)
         for i, row in enumerate(rs.payoff_diffs_means):
@@ -263,68 +286,78 @@ class TestResultSet(unittest.TestCase):
                                  self.expected_payoff_diffs_means[i][j])
 
     def test_payoff_stddevs(self):
-        rs = axelrod.ResultSet(self.players, self.interactions)
+        rs = axelrod.ResultSet(self.players, self.interactions,
+                               progress_bar=False)
         self.assertIsInstance(rs.payoff_stddevs, list)
         self.assertEqual(len(rs.payoff_stddevs), rs.nplayers)
         self.assertEqual(rs.payoff_stddevs, self.expected_payoff_stddevs)
 
     def test_cooperation(self):
-        rs = axelrod.ResultSet(self.players, self.interactions)
+        rs = axelrod.ResultSet(self.players, self.interactions,
+                               progress_bar=False)
         self.assertIsInstance(rs.cooperation, list)
         self.assertEqual(len(rs.cooperation), rs.nplayers)
         self.assertEqual(rs.cooperation, self.expected_cooperation)
 
     def test_normalised_cooperation(self):
-        rs = axelrod.ResultSet(self.players, self.interactions)
+        rs = axelrod.ResultSet(self.players, self.interactions,
+                               progress_bar=False)
         self.assertIsInstance(rs.normalised_cooperation, list)
         self.assertEqual(len(rs.normalised_cooperation), rs.nplayers)
         self.assertEqual(rs.normalised_cooperation,
                          self.expected_normalised_cooperation)
 
     def test_vengeful_cooperation(self):
-        rs = axelrod.ResultSet(self.players, self.interactions)
+        rs = axelrod.ResultSet(self.players, self.interactions,
+                               progress_bar=False)
         self.assertIsInstance(rs.vengeful_cooperation, list)
         self.assertEqual(len(rs.vengeful_cooperation), rs.nplayers)
         self.assertEqual(rs.vengeful_cooperation,
                          self.expected_vengeful_cooperation)
 
     def test_cooperating_rating(self):
-        rs = axelrod.ResultSet(self.players, self.interactions)
+        rs = axelrod.ResultSet(self.players, self.interactions,
+                               progress_bar=False)
         self.assertIsInstance(rs.cooperating_rating, list)
         self.assertEqual(len(rs.cooperating_rating), rs.nplayers)
         self.assertEqual(rs.cooperating_rating,
                          self.expected_cooperating_rating)
 
     def test_good_partner_matrix(self):
-        rs = axelrod.ResultSet(self.players, self.interactions)
+        rs = axelrod.ResultSet(self.players, self.interactions,
+                               progress_bar=False)
         self.assertIsInstance(rs.good_partner_matrix, list)
         self.assertEqual(len(rs.good_partner_matrix), rs.nplayers)
         self.assertEqual(rs.good_partner_matrix,
                          self.expected_good_partner_matrix)
 
     def test_good_partner_rating(self):
-        rs = axelrod.ResultSet(self.players, self.interactions)
+        rs = axelrod.ResultSet(self.players, self.interactions,
+                               progress_bar=False)
         self.assertIsInstance(rs.good_partner_rating, list)
         self.assertEqual(len(rs.good_partner_rating), rs.nplayers)
         self.assertEqual(rs.good_partner_rating,
                          self.expected_good_partner_rating)
 
     def test_eigenjesus_rating(self):
-        rs = axelrod.ResultSet(self.players, self.interactions)
+        rs = axelrod.ResultSet(self.players, self.interactions,
+                               progress_bar=False)
         self.assertIsInstance(rs.eigenjesus_rating, list)
         self.assertEqual(len(rs.eigenjesus_rating), rs.nplayers)
         for j, rate in enumerate(rs.eigenjesus_rating):
             self.assertAlmostEqual(rate, self.expected_eigenjesus_rating[j])
 
     def test_eigenmoses_rating(self):
-        rs = axelrod.ResultSet(self.players, self.interactions)
+        rs = axelrod.ResultSet(self.players, self.interactions,
+                               progress_bar=False)
         self.assertIsInstance(rs.eigenmoses_rating, list)
         self.assertEqual(len(rs.eigenmoses_rating), rs.nplayers)
         for j, rate in enumerate(rs.eigenmoses_rating):
             self.assertAlmostEqual(rate, self.expected_eigenmoses_rating[j])
 
     def test_csv(self):
-        rs = axelrod.ResultSet(self.players, self.interactions)
+        rs = axelrod.ResultSet(self.players, self.interactions,
+                               progress_bar=False)
         self.assertEqual(rs.csv(), self.expected_csv)
 
 
@@ -341,7 +374,7 @@ class TestResultSetFromFile(unittest.TestCase):
 
 
     def test_init(self):
-        rs = axelrod.ResultSetFromFile(self.tmp_file.name)
+        rs = axelrod.ResultSetFromFile(self.tmp_file.name, progress_bar=False)
         players = ['Cooperator', 'Tit For Tat', 'Defector']
         self.assertEqual(rs.players, players)
         self.assertEqual(rs.nplayers, len(players))
@@ -354,3 +387,9 @@ class TestResultSetFromFile(unittest.TestCase):
                                  (0, 2): [[('C', 'D'), ('C', 'D')]],
                                  (1, 1): [[('C', 'C'), ('C', 'C')]]}
         self.assertEqual(rs.interactions, expected_interactions)
+
+
+class TestDecorator(unittest.TestCase):
+    def test_update_progress_bar(self):
+        method = lambda x: None
+        self.assertEqual(axelrod.result_set.update_progress_bar(method)(1), None)