Introduce VariationDict to reduce duplicate code

This commit is contained in:
Raphael Michel
2014-10-07 10:25:29 +02:00
parent 421b4d8eca
commit 1ec224049d
6 changed files with 90 additions and 25 deletions

View File

@@ -68,6 +68,8 @@ It is sent out with several arguments:
the item does not have any properties, the list will contain exactly one empty
dictionary. Please not: this is *not* the list of all possible variations, this is
only the list of all variations the frontend likes to determine the status for.
Technically, you won't get ``dict`` objects but ``tixlbase.types.VariationDict``
objects, which behave exactly the same but add some extra methods.
context
A yet-to-defined context object containing information about the user and the order
process. This is required to implement coupon-systems or similar restrictions.
@@ -158,9 +160,7 @@ In our example, the implementation could look like this::
# Make up some unique key for this variation
cachekey = 'timerestriction:%d:%s' % (
item.pk,
",".join(sorted(
[str(v[1].pk) for v in v.items() if v[0] != 'variation']
))
v.identify(),
)
# Fetch from cache, if available

View File

@@ -7,6 +7,8 @@ from django.utils.translation import ugettext_lazy as _
from django.template.defaultfilters import date as _date
from django.core.validators import RegexValidator
from tixlbase.types import VariationDict
class UserManager(BaseUserManager):
"""
@@ -571,10 +573,13 @@ class Item(models.Model):
def get_all_variations(self):
"""
This method returns a list containing all variations of this
item. The list contains one dictionary per variation, where
item. The list contains one VariationDict per variation, where
the Proprty IDs are keys and the PropertyValue objects are
values. If an ItemVariation object exists, it is available in
the dictionary via the special key 'variation'.
VariationDicts differ from dicts only by specifying some extra
methods.
"""
all_variations = self.variations.all().prefetch_related("values")
all_properties = self.properties.all().prefetch_related("values")
@@ -583,20 +588,20 @@ class Item(models.Model):
key = []
for v in var.values.all():
key.append((v.prop_id, v.pk))
key = hash(tuple(sorted(key)))
key = tuple(sorted(key))
variations_cache[key] = var
result = []
for comb in product(*[prop.values.all() for prop in all_properties]):
if len(comb) == 0:
result.append({})
result.append(VariationDict())
continue
key = []
var = {}
var = VariationDict()
for v in comb:
key.append((v.prop.pk, v.pk))
var[v.prop.pk] = v
key = hash(tuple(sorted(key)))
key = tuple(sorted(key))
if key in variations_cache:
var['variation'] = variations_cache[key]
result.append(var)

View File

@@ -5,6 +5,7 @@ from tixlbase.models import (
Event, Organizer, Item, ItemVariation,
Property, PropertyValue
)
from tixlbase.types import VariationDict
class ItemVariationsTest(TestCase):
@@ -44,7 +45,7 @@ class ItemVariationsTest(TestCase):
self.assertEqual(len(v), 3)
values = []
for var in v:
self.assertIs(type(var), dict)
self.assertIs(type(var), VariationDict)
self.assertIn(p.pk, var)
self.assertIs(type(var[p.pk]), PropertyValue)
values.append(var[p.pk].value)
@@ -59,7 +60,7 @@ class ItemVariationsTest(TestCase):
values = []
num_variations = 0
for var in v:
self.assertIs(type(var), dict)
self.assertIs(type(var), VariationDict)
if 'variation' in var and type(var['variation']) is ItemVariation:
self.assertEqual(iv.pk, var['variation'].pk)
values.append(var['variation'].values.all()[0].value)
@@ -80,7 +81,7 @@ class ItemVariationsTest(TestCase):
values = []
num_variations = 0
for var in v:
self.assertIs(type(var), dict)
self.assertIs(type(var), VariationDict)
if 'variation' in var:
self.assertEqual(iv.pk, var['variation'].pk)
values.append(sorted([ivv.value for ivv in iv.values.all()]))

71
src/tixlbase/types.py Normal file
View File

@@ -0,0 +1,71 @@
class VariationDict(dict):
"""
A VariationDict object behaves exactle the same as the Python built-in
``dict`` does, but adds some special methods. It is used for the dicts
returned by ``Item.get_all_variations()`` to avoid duplicate code in the
code calling this method.
"""
def relevant_items(self):
"""
Iterate over all items with numeric keys.
This is in use because the variation dictionaries use property ids
as key and have some special keys like 'variation'.
"""
for i in self.items():
if type(i[0]) is int:
yield i
def relevant_values(self):
"""
Iterate over all values with numeric keys.
This is in use because the variation dictionaries use property ids
as key and have some special keys like 'variation'.
"""
for i in self.items():
if type(i[0]) is int:
yield i[1]
def identify(self):
"""
Build an identifier for this dict. This can be any string used to
compare one VariationDict to others.
In the current implementation, it is a string containing a list of
the PropertyValue id's, sorted by the Property id's and is therefore
unique among one item.
"""
order_key = lambda i: i[0]
return ",".join([
str(v[1].pk) for v in sorted(self.relevant_items(), key=order_key)
])
def __eq__(self, other):
if type(other) is type(self):
return self.identify() == other.identify()
else:
return super().__eq__(other)
def ordered_values(self):
"""
Returns a list of values ordered by their keys
"""
return [
i[1] for i
in sorted(
[it for it in self.relevant_items()],
key=lambda i: i[0]
)
]
def copy(self):
"""
Return a one-level deep copy of this object (create a new
VariationDict but make a shallow copy of the dict inside it).
"""
new = VariationDict()
for k, v in self.items():
new[k] = v
return new

View File

@@ -561,16 +561,6 @@ class ItemVariations(TemplateView, SingleObjectMixin):
if v.prop.pk != prop2.pk
]
# Given an dictionary like the ones returned by
# Item.get_all_variation() this will return a list of PropertyValue
# objects sorted by the primary keys of the properties they belong
# to.
values = lambda variation: [
i[1] for i in sorted(
[it for it in variation.items() if it[0] != 'variation']
)
]
# Given a list of variations, this will sort them by their position
# on the x-axis
sort = lambda v: v[prop2.pk].pk
@@ -592,7 +582,7 @@ class ItemVariations(TemplateView, SingleObjectMixin):
selection = selector(gridrow + (val1,))
# We now iterate over all variations who generate the same
# selector as 'selection'.
filtered = [v for v in variations if selector(values(v)) == selection]
filtered = [v for v in variations if selector(v.relevant_values()) == selection]
for variation in sorted(filtered, key=sort):
form = self.get_form(variation, data)
formrow.append(form)

View File

@@ -59,9 +59,7 @@ def availability_handler(sender, **kwargs):
# Make up some unique key for this variation
cachekey = 'timerestriction:%d:%s' % (
item.pk,
",".join(sorted(
[str(v[1].pk) for v in v.items() if v[0] != 'variation']
))
v.identify(),
)
# Fetch from cache, if available