Skip to content

Commit

Permalink
Merge 0f69e02 into 8b8b21a
Browse files Browse the repository at this point in the history
  • Loading branch information
Zwork101 committed Oct 3, 2018
2 parents 8b8b21a + 0f69e02 commit e888752
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 5 deletions.
2 changes: 2 additions & 0 deletions AUTHORS
Expand Up @@ -14,3 +14,5 @@ Maintainers

Patches and Suggestions
```````````````````````

- Nathan Zilora <Zwork101>
56 changes: 52 additions & 4 deletions pychievements/trackers.py
Expand Up @@ -12,6 +12,16 @@ class NotRegistered(Exception):
pass


def median(lst):
n = len(lst)
if n < 1:
return None
if n % 2 == 1:
return sorted(lst)[n // 2]
else:
return sum(sorted(lst)[n // 2-1:n // 2 + 1])/ 2.0


class AchievementTracker(object):
"""
AchievementTracker tracks achievements and current levels for ``tracked_id`` using a configured
Expand Down Expand Up @@ -77,7 +87,7 @@ def is_registered(self, achievement):
"""
return achievement in self._registry

def achievements(self, category=None, keywords=[]):
def achievements(self, category=None, keywords=None):
"""
Returns all registered achievements.
Expand All @@ -88,9 +98,10 @@ def achievements(self, category=None, keywords=[]):
keywords
Filters returned achievements by keywords. Returned achievements will match all
given keywords
given keywords. This will be [] if not provided.
"""
achievements = []
keywords = keywords if keywords is not None else []
for achievement in self._registry:
if category is None or achievement.category == category:
if not keywords or all([_ in achievement.keywords for _ in keywords]):
Expand All @@ -117,9 +128,10 @@ class or a string of the name of an achievement class that has been registered w
return self._backend.achievement_for_id(tracked_id, a[0])
raise NotRegistered('The achievement %s is not registered with this tracker' % achievement)

def achievements_for_id(self, tracked_id, category=None, keywords=[]):
def achievements_for_id(self, tracked_id, category=None, keywords=None):
""" Returns all of the achievements for tracked_id that match the given category and
keywords """
keywords. keywords will be [] if not provided."""
keywords = keywords if keywords is not None else []
return self._backend.achievements_for_id(tracked_id, self.achievements(category, keywords))

def _check_signals(self, tracked_id, achievement, old_level, old_achieved):
Expand Down Expand Up @@ -216,3 +228,39 @@ def get_tracked_ids(self):
def remove_id(self, tracked_id):
""" Remove all tracked information for tracked_id """
self._backend.remove_id(tracked_id)

def compare_stats(self, tracked_ids, achievement):
"""
Fetches an achievement's statistics for each ID provided, and compares them.
This will return a dictionary that looks like the following:
{
"mean": Number with the average current level. This will be rounded into a goal index.
"median": The median of all the current levels.
"max": The highest level among the IDs.
"min": The lowest level among the IDs.
}
"""
stats = {
"mean": 0,
"median": [],
"max": [],
"min": []
}

for tid in tracked_ids:
personal_achievement = self._backend.achievement_for_id(tid, achievement)
stats["mean"] += personal_achievement.current[0]
for stat in ["median", "max", "min"]:
stats[stat].append(personal_achievement.current[0])

stats["mean"] /= len(tracked_ids)
stats["median"] = median(stats["median"])
stats["max"] = max(stats["max"])
stats["min"] = min(stats["min"])

return stats

def compare_global_stats(self, achievement):
""" Shorthand for tracker.compare_stats(tracker.get_tracked_ids(), ``achievement``) """
return self.compare_stats(self.get_tracked_ids(), achievement)
34 changes: 33 additions & 1 deletion tests/__init__.py
Expand Up @@ -30,6 +30,24 @@ def setUp(self):
self.tracker = AchievementTracker()
self.tracker.register(ACHIEVEMENTS)

self.fixed_tracker = AchievementTracker()

class FixedAchievement(Achievement):
name = "Fixed"
category = "test"
keywords = "unittest"
goals = (
{'level': 10, 'name': 'Level 1', 'icon': icons.star, 'description': 'Level One'},
{'level': 20, 'name': 'Level 2', 'icon': icons.star, 'description': 'Level Two'},
{'level': 30, 'name': 'Level 3', 'icon': icons.star, 'description': 'Level Three'},
)

self.fixed_achievement = FixedAchievement
self.fixed_tracker.register(FixedAchievement)
people = {"Greg": 10, "Cindy": 20, "Megan": 30}
for name, value in people.items():
self.fixed_tracker.increment(name, FixedAchievement, value)

def test_bad_backend(self):
self.assertRaises(ValueError, self.tracker.set_backend, AchievementTracker)

Expand Down Expand Up @@ -107,6 +125,20 @@ def test_remove_id(self):
print(self.tracker.get_tracked_ids())
self.assertEqual(len(self.tracker.get_tracked_ids()), len(TRACKED_IDS)-1)

def test_compare_stats(self):
results = self.fixed_tracker.compare_stats(["Greg", "Megan"], self.fixed_achievement)
self.assertEqual(results["mean"], 20)
self.assertEqual(results["median"], 20)
self.assertEqual(results["max"], 30)
self.assertEqual(results["min"], 10)

def test_compare_global_stats(self):
results = self.fixed_tracker.compare_global_stats(self.fixed_achievement)
self.assertEqual(results["mean"], 20)
self.assertEqual(results["median"], 20)
self.assertEqual(results["max"], 30)
self.assertEqual(results["min"], 10)


class AchievementBackenedTests(unittest.TestCase):
# only tests things that haven't been hit in TrackerTests
Expand All @@ -128,7 +160,7 @@ def setUp(self):
self.tracker.register(ACHIEVEMENTS)

def tearDown(self):
os.remove(self.dbfile.name)
os.remove(self.dbfile.name) # Fails on windows. This should be looked into later.

def test_increment(self):
tid = random.choice(TRACKED_IDS)
Expand Down

0 comments on commit e888752

Please sign in to comment.