Source code for pychievements.trackers

from .achievements import Achievement
from .backends import AchievementBackend
from .signals import goal_achieved, level_increased, highest_level_achieved
from inspect import isclass as _isclass


class AlreadyRegistered(Exception):
        pass


class NotRegistered(Exception):
    pass


[docs]class AchievementTracker(object): """ AchievementTracker tracks achievements and current levels for ``tracked_id`` using a configured achievement backend. A default instance of Achievement tracker is created as a singleton when pycheivements is imported as ``pychievements.tracker``. Most often, this is what you will want to use. Arguments: backend: The backend to use for storing/retrieving achievement data. If ``None``, the default :py:class:`AchievementBackend` will be used, which stores all data in memory. .. note:: The backend the tracker is using can be updated at any time using the :py:func:`set_backend` function. """ def __init__(self, backend=None): self._registry = [] self._backend = AchievementBackend() if backend is None else backend
[docs] def set_backend(self, backend): """ Configures a new backend for storing achievement data. """ if not isinstance(backend, AchievementBackend): raise ValueError('Backend must be an instance of an AchievementBackend') self._backend = backend
[docs] def register(self, achievement_or_iterable, **options): """ Registers the given achievement(s) to be tracked. """ if _isclass(achievement_or_iterable) and issubclass(achievement_or_iterable, Achievement): achievement_or_iterable = [achievement_or_iterable] for achievement in achievement_or_iterable: if not achievement.category: raise ValueError('Achievements must specify a category, could not register ' '%s' % achievement.__name__) if achievement in self._registry: raise AlreadyRegistered('The achievement %s is already ' 'registered' % achievement.__name__) if achievement is not Achievement: self._registry.append(achievement)
[docs] def unregister(self, achievement_or_iterable): """ Un-registers the given achievement(s). If an achievement isn't already registered, this will raise NotRegistered. """ if _isclass(achievement_or_iterable) and issubclass(achievement_or_iterable, Achievement): achievement_or_iterable = [achievement_or_iterable] for achievement in achievement_or_iterable: if achievement not in self._registry: raise NotRegistered('The achievement %s is not registered' % achievement.__name__) self._registry.remove(achievement)
[docs] def is_registered(self, achievement): """ Check if an achievement is registered with this `AchievementTracker` """ return achievement in self._registry
[docs] def achievements(self, category=None, keywords=[]): """ Returns all registered achievements. Arguments: category Filters returned achievements by category. This is a strict string match. keywords Filters returned achievements by keywords. Returned achievements will match all given keywords """ achievements = [] for achievement in self._registry: if category is None or achievement.category == category: if not keywords or all([_ in achievement.keywords for _ in keywords]): achievements.append(achievement) return achievements
[docs] def achievement_for_id(self, tracked_id, achievement): """ Returns ``Achievement`` for a given ``tracked_id``. Achievement can be an ``Achievement`` class or a string of the name of an achievement class that has been registered with this tracker. Raises NotRegistered if the given achievement is not registered with the tracker. If ``tracked_id`` has not been tracked yet by this tracker, it will be created. """ if isinstance(achievement, Achievement): achievement = achievement.__class__.__name__ elif _isclass(achievement) and issubclass(achievement, Achievement): achievement = achievement.__name__ a = [_ for _ in self._registry if _.__name__ == achievement] if a: return self._backend.achievement_for_id(tracked_id, a[0]) raise NotRegistered('The achievement %s is not registered with this tracker' % achievement)
[docs] def achievements_for_id(self, tracked_id, category=None, keywords=[]): """ Returns all of the achievements for tracked_id that match the given category and keywords """ return self._backend.achievements_for_id(tracked_id, self.achievements(category, keywords))
def _check_signals(self, tracked_id, achievement, old_level, old_achieved): cur_level = achievement.current[0] if old_level < cur_level: level_increased.send_robust(self, tracked_id=tracked_id, achievement=achievement) if old_achieved != achievement.achieved: new_goals = [_ for _ in achievement.achieved if _ not in old_achieved] goal_achieved.send_robust(self, tracked_id=tracked_id, achievement=achievement, goals=new_goals) if not achievement.unachieved: highest_level_achieved.send_robust(self, tracked_id=tracked_id, achievement=achievement) return new_goals return False
[docs] def increment(self, tracked_id, achievement, amount=1, *args, **kwargs): """ Increments an achievement for a given ``tracked_id``. Achievement can be an ``Achievement`` class or a string of the name of an achievement class that has been registered with this tracker. Raises NotRegistered if the given achievement is not registered with the tracker. If ``tracked_id`` has not been tracked yet by this tracker, it will be created before incrementing. Returns an list of achieved goals if a new goal was reached, or False """ achievement = self.achievement_for_id(tracked_id, achievement) cur_level = achievement.current[0] achieved = achievement.achieved[:] achievement.increment(amount, *args, **kwargs) self._backend.set_level_for_id(tracked_id, achievement.__class__, achievement.current[0]) return self._check_signals(tracked_id, achievement, cur_level, achieved)
[docs] def evaluate(self, tracked_id, achievement, *args, **kwargs): """ Evaluates an achievement for a given ``tracked_id``. Achievement can be an ``Achievement`` class or a string of the name of an achievement class that has been registered with this tracker. Raises NotRegistered if the given achievement is not registered with the tracker. If ``tracked_id`` has not been tracked yet by this tracker, it will be created before evaluating. Returns list of achieved goals for the given achievement after evaluation """ achievement = self.achievement_for_id(tracked_id, achievement) cur_level = achievement.current[0] achieved = achievement.achieved[:] result = achievement.evaluate(*args, **kwargs) self._backend.set_level_for_id(tracked_id, achievement.__class__, achievement.current[0]) self._check_signals(tracked_id, achievement, cur_level, achieved) return result
[docs] def current(self, tracked_id, achievement): """ Returns ``current`` for a given tracked_id. See :ref:``Achievement`` """ achievement = self.achievement_for_id(tracked_id, achievement) return achievement.current
[docs] def achieved(self, tracked_id, achievement): """ Returns ``achieved`` for a given tracked_id. See :ref:``Achievement`` """ achievement = self.achievement_for_id(tracked_id, achievement) return achievement.achieved
[docs] def unachieved(self, tracked_id, achievement): """ Returns ``unachieved`` for a given tracked_id. See :ref:``Achievement`` """ achievement = self.achievement_for_id(tracked_id, achievement) return achievement.unachieved
[docs] def set_level(self, tracked_id, achievement, level): """ Returns ``set_level`` for a given tracked_id. See :ref:``Achievement`` """ achievement = self.achievement_for_id(tracked_id, achievement) cur_level = achievement.current[0] achieved = achievement.achieved[:] achievement.set_level(level) self._backend.set_level_for_id(tracked_id, achievement.__class__, achievement.current[0]) self._check_signals(tracked_id, achievement, cur_level, achieved)
[docs] def get_tracked_ids(self): """ Returns all tracked ids """ return self._backend.get_tracked_ids()
[docs] def remove_id(self, tracked_id): """ Remove all tracked information for tracked_id """ self._backend.remove_id(tracked_id)