Source code for serge.blocks.scores
"""Handling high score type tables"""
import operator
import serge.serialize
[docs]class DuplicateCategory(Exception): """The category was already added"""
[docs]class BadCategory(Exception): """The category was not found"""
[docs]class BadData(Exception): """The data provided for a category was not valid"""
[docs]class InvalidSort(Exception): """The sort direction was invalid"""
[docs]class InvalidSortColumn(Exception): """The column specified for sorting was not valid"""
[docs]class HighScoreTable(serge.serialize.Serializable):
"""A high score table
The table can contain scores in a number of categories. Each
category is a table with multiple columns. The table can be
sorted by any one column and can have a limited set of
values
"""
my_properties = (
serge.serialize.D('categories', {}, 'the categories in this table'),
)
def __init__(self):
"""Initialise the HighScoreTable"""
self.categories = {}
[docs] def addCategory(self, name, number=None, sort_columns=None, directions=('ascending',)):
"""Add a new category"""
if name in self.categories:
raise DuplicateCategory('The category "%s" has already been added to this table' % name)
self.categories[name] = Category(name, number, sort_columns, directions)
[docs] def addScore(self, category_name, name, *args):
"""Add a score to a category"""
return self.getCategory(category_name).addScore(name, *args)
[docs] def getCategory(self, category_name):
"""Return a category"""
try:
return self.categories[category_name]
except KeyError:
raise BadCategory('The category "%s" was not found' % category_name)
[docs] def resetTable(self):
"""Clear the entire table"""
self.categories = {}
[docs] def resetCategory(self, category_name):
"""Reset the category name"""
try:
self.categories[category_name].resetCategory()
except KeyError:
raise BadCategory('The category "%s" was not found' % category_name)
[docs]class Category(list):
"""A category for an individual score table"""
def __init__(self, name, number=None, sort_columns=None, directions=('ascending',)):
"""Initialise the Category"""
self.name = name
self.number = number
for direction in directions:
if direction not in ('ascending', 'descending'):
raise InvalidSort('The sort direction (%s) was invalid. Should be either "ascending" or "descending"' % direction)
self.sort_columns = sort_columns
self.directions = directions
[docs] def addScore(self, name, *args):
"""Add a new score"""
if self.sort_columns and max(self.sort_columns) > len(args):
raise InvalidSortColumn('The data provided (%s) is not as long as the sort column (%s)' % (args, self.sort_columns))
#
# Add the new item
this_row = (name,) + args
self.append(this_row)
#
# Apply sorting
def sorter(a, b):
"""Sorter that can cope with multiple levels of sort"""
for column, direction in zip(self.sort_columns, self.directions):
c = cmp(a[column], b[column])
if direction == 'ascending':
c *= -1
if c:
return c
return 0
#
if self.sort_columns:
self.sort(cmp=sorter)
#
# And limit the size of the table
if self.number:
del(self[self.number:])
#
# Now find out position
for position, row in enumerate(self):
if row == this_row:
return position+1
else:
return None
[docs] def resetCategory(self):
"""Reset this category, deleting all the data but maintaining the configuration"""
del(self[:])