diff --git a/src/tixlbase/cache.py b/src/tixlbase/cache.py index b7f0ffa7b1..e1f81f220a 100644 --- a/src/tixlbase/cache.py +++ b/src/tixlbase/cache.py @@ -12,11 +12,14 @@ class EventRelatedCache: you store data in this cache, it is only stored for this event. The main purpose of this is to be able to flush all cached data related to this event at once. + + The object is stateless, all state is in the cache, so you can + instantiate it as many times as you want. """ def __init__(self, event, cache='default'): self.cache = caches[cache] - self.prefix = self._build_prefix() + self.event = event self.prefixkey = 'event:%d' % self.event.pk def _prefix_key(self, original_key): @@ -42,7 +45,7 @@ class EventRelatedCache: prefix = int(time.time()) self.cache.set(self.prefixkey, prefix) - def set(self, key, value, timeout=300): + def set(self, key, value, timeout=3600): return self.cache.set(self._prefix_key(key), value, timeout) def get(self, key): @@ -51,7 +54,7 @@ class EventRelatedCache: def get_many(self, keys): return self.cache.get_many([self._prefix_key(key) for key in keys]) - def set_many(self, values, timeout=300): + def set_many(self, values, timeout=3600): newvalues = {} for i in values.items(): newvalues[self._prefix_key(i[0])] = i[1] diff --git a/src/tixlbase/models.py b/src/tixlbase/models.py index afcbd0341e..411c4cb25e 100644 --- a/src/tixlbase/models.py +++ b/src/tixlbase/models.py @@ -285,8 +285,9 @@ class Event(models.Model): return self.name def save(self, *args, **kwargs): + obj = super().save(*args, **kwargs) self.get_cache().clear() - return super().save(self, *args, **kwargs) + return obj def get_date_from_display(self): return _date( @@ -364,7 +365,7 @@ class ItemCategory(models.Model): def save(self, *args, **kwargs): if self.event: self.event.get_cache().clear() - return super().save(self, *args, **kwargs) + return super().save(*args, **kwargs) class Property(models.Model): @@ -393,7 +394,7 @@ class Property(models.Model): def save(self, *args, **kwargs): if self.event: self.event.get_cache().clear() - return super().save(self, *args, **kwargs) + return super().save(*args, **kwargs) class PropertyValue(models.Model): @@ -426,7 +427,7 @@ class PropertyValue(models.Model): def save(self, *args, **kwargs): if self.prop: self.prop.event.get_cache().clear() - return super().save(self, *args, **kwargs) + return super().save(*args, **kwargs) class Question(models.Model): @@ -472,7 +473,7 @@ class Question(models.Model): def save(self, *args, **kwargs): if self.event: self.event.get_cache().clear() - return super().save(self, *args, **kwargs) + return super().save(*args, **kwargs) class Item(models.Model): @@ -560,7 +561,7 @@ class Item(models.Model): def save(self, *args, **kwargs): if self.event: self.event.get_cache().clear() - return super().save(self, *args, **kwargs) + return super().save(*args, **kwargs) def delete(self): self.deleted = True @@ -647,7 +648,7 @@ class ItemVariation(models.Model): def save(self, *args, **kwargs): if self.item: self.item.event.get_cache().clear() - return super().save(self, *args, **kwargs) + return super().save(*args, **kwargs) class BaseRestriction(models.Model): @@ -679,4 +680,4 @@ class BaseRestriction(models.Model): def save(self, *args, **kwargs): if self.event: self.event.get_cache().clear() - return super().save(self, *args, **kwargs) + return super().save(*args, **kwargs) diff --git a/src/tixlplugins/timerestriction/signals.py b/src/tixlplugins/timerestriction/signals.py index b22d45ba5e..e0bc01b1fb 100644 --- a/src/tixlplugins/timerestriction/signals.py +++ b/src/tixlplugins/timerestriction/signals.py @@ -11,7 +11,7 @@ def availability_handler(sender, **kwargs): # Handle the signal's input arguments item = kwargs['item'] variations = kwargs['variations'] - cache = kwargs['cache'] # NOQA + cache = kwargs['cache'] context = kwargs['context'] # NOQA # Fetch all restriction objects applied to this item @@ -29,6 +29,25 @@ def availability_handler(sender, **kwargs): # interfere with other plugins. variations = [d.copy() for d in variations] + # The maximum validity of our cached values is the next date, one of our + # timeframe_from or tiemframe_to actions happens + def timediff(restrictions): + for r in restrictions: + if r.timeframe_from >= now(): + yield (r.timeframe_from - now()).total_seconds() + if r.timeframe_to >= now(): + yield (r.timeframe_to - now()).total_seconds() + + try: + cache_validity = min(timediff(restrictions)) + except ValueError: + # empty sequence + # If we get here, there are restrictions available but nothing will + # change about them any more. If it were not for the case of no + # restriction for the base item but restrictions for special + # variations, we could quit here with 'item not available'. + cache_validity = 3600 + # Walk through all variations we are asked for for v in variations: # If this point is reached, there ARE time restrictions for this item @@ -36,6 +55,23 @@ def availability_handler(sender, **kwargs): # without any timeframe available = False price = None + + # 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'])) + ) + + # Fetch from cache, if available + cached = cache.get(cachekey) + if cached is not None: + v['available'] = (cached.split(":")[0] == 'True') + try: + v['price'] = float(cached.split(":")[1]) + except ValueError: + v['price'] = None + continue + # Walk through all restriction objects applied to this item for restriction in restrictions: applied_to = list(restriction.variations.all()) @@ -57,5 +93,10 @@ def availability_handler(sender, **kwargs): v['available'] = available v['price'] = price + cache.set( + cachekey, + '%s:%s' % ('True' if available else 'False', str(price) if price else ''), + cache_validity + ) return variations diff --git a/src/tixlplugins/timerestriction/tests.py b/src/tixlplugins/timerestriction/tests.py index e18d4b1d04..046a129bb7 100644 --- a/src/tixlplugins/timerestriction/tests.py +++ b/src/tixlplugins/timerestriction/tests.py @@ -33,7 +33,7 @@ class TimeRestrictionTest(TestCase): def test_nothing(self): result = signals.availability_handler( None, item=self.item, - variations=self.event.get_all_variations(), + variations=self.item.get_all_variations(), context=None, cache=self.event.get_cache() ) self.assertEqual(len(result), 1) @@ -43,12 +43,13 @@ class TimeRestrictionTest(TestCase): r = TimeRestriction.objects.create( timeframe_from=now() - timedelta(days=3), timeframe_to=now() + timedelta(days=3), + event=self.event, price=12 ) r.items.add(self.item) result = signals.availability_handler( None, item=self.item, - variations=self.event.get_all_variations(), + variations=self.item.get_all_variations(), context=None, cache=self.event.get_cache() ) self.assertEqual(len(result), 1) @@ -60,12 +61,13 @@ class TimeRestrictionTest(TestCase): r = TimeRestriction.objects.create( timeframe_from=now() - timedelta(days=5), timeframe_to=now() - timedelta(days=3), + event=self.event, price=12 ) r.items.add(self.item) result = signals.availability_handler( None, item=self.item, - variations=self.event.get_all_variations(), + variations=self.item.get_all_variations(), context=None, cache=self.event.get_cache() ) self.assertEqual(len(result), 1) @@ -76,18 +78,20 @@ class TimeRestrictionTest(TestCase): r1 = TimeRestriction.objects.create( timeframe_from=now() - timedelta(days=5), timeframe_to=now() + timedelta(days=3), + event=self.event, price=12 ) r1.items.add(self.item) r2 = TimeRestriction.objects.create( timeframe_from=now() - timedelta(days=3), timeframe_to=now() + timedelta(days=5), + event=self.event, price=8 ) r2.items.add(self.item) result = signals.availability_handler( None, item=self.item, - variations=self.event.get_all_variations(), + variations=self.item.get_all_variations(), context=None, cache=self.event.get_cache() ) self.assertEqual(len(result), 1) @@ -99,18 +103,20 @@ class TimeRestrictionTest(TestCase): r1 = TimeRestriction.objects.create( timeframe_from=now() - timedelta(days=5), timeframe_to=now() + timedelta(days=5), + event=self.event, price=12 ) r1.items.add(self.item) r2 = TimeRestriction.objects.create( timeframe_from=now() + timedelta(days=1), timeframe_to=now() + timedelta(days=7), + event=self.event, price=8 ) r2.items.add(self.item) result = signals.availability_handler( None, item=self.item, - variations=self.event.get_all_variations(), + variations=self.item.get_all_variations(), context=None, cache=self.event.get_cache() ) self.assertEqual(len(result), 1) @@ -122,18 +128,20 @@ class TimeRestrictionTest(TestCase): r1 = TimeRestriction.objects.create( timeframe_from=now() - timedelta(days=5), timeframe_to=now() + timedelta(days=2), + event=self.event, price=12 ) r1.items.add(self.item) r2 = TimeRestriction.objects.create( timeframe_from=now() + timedelta(days=4), timeframe_to=now() + timedelta(days=7), + event=self.event, price=8 ) r2.items.add(self.item) result = signals.availability_handler( None, item=self.item, - variations=self.event.get_all_variations(), + variations=self.item.get_all_variations(), context=None, cache=self.event.get_cache() ) self.assertEqual(len(result), 1) @@ -145,18 +153,20 @@ class TimeRestrictionTest(TestCase): r1 = TimeRestriction.objects.create( timeframe_from=now() - timedelta(days=5), timeframe_to=now() - timedelta(days=1), + event=self.event, price=12 ) r1.items.add(self.item) r2 = TimeRestriction.objects.create( timeframe_from=now() + timedelta(days=4), timeframe_to=now() + timedelta(days=7), + event=self.event, price=8 ) r2.items.add(self.item) result = signals.availability_handler( None, item=self.item, - variations=self.event.get_all_variations(), + variations=self.item.get_all_variations(), context=None, cache=self.event.get_cache() ) self.assertEqual(len(result), 1) @@ -173,13 +183,14 @@ class TimeRestrictionTest(TestCase): r1 = TimeRestriction.objects.create( timeframe_from=now() - timedelta(days=5), timeframe_to=now() + timedelta(days=1), + event=self.event, price=12 ) r1.items.add(self.item) r1.variations.add(v1) result = signals.availability_handler( None, item=self.item, - variations=self.event.get_all_variations(), + variations=self.item.get_all_variations(), context=None, cache=self.event.get_cache() ) self.assertEqual(len(result), 3) @@ -200,12 +211,14 @@ class TimeRestrictionTest(TestCase): r1 = TimeRestriction.objects.create( timeframe_from=now() - timedelta(days=5), timeframe_to=now() + timedelta(days=1), + event=self.event, price=12 ) r1.items.add(self.item) r2 = TimeRestriction.objects.create( timeframe_from=now() - timedelta(days=5), timeframe_to=now() + timedelta(days=1), + event=self.event, price=8 ) r2.items.add(self.item) @@ -213,13 +226,14 @@ class TimeRestrictionTest(TestCase): r3 = TimeRestriction.objects.create( timeframe_from=now() - timedelta(days=5), timeframe_to=now() - timedelta(days=1), + event=self.event, price=10 ) r3.items.add(self.item) r3.variations.add(v2) result = signals.availability_handler( None, item=self.item, - variations=self.event.get_all_variations(), + variations=self.item.get_all_variations(), context=None, cache=self.event.get_cache() ) self.assertEqual(len(result), 3) @@ -241,6 +255,7 @@ class TimeRestrictionTest(TestCase): r1 = TimeRestriction.objects.create( timeframe_from=now() - timedelta(days=5), timeframe_to=now() + timedelta(days=1), + event=self.event, price=12 ) r1.items.add(self.item) @@ -248,6 +263,7 @@ class TimeRestrictionTest(TestCase): r2 = TimeRestriction.objects.create( timeframe_from=now() - timedelta(days=5), timeframe_to=now() + timedelta(days=1), + event=self.event, price=8 ) r2.items.add(self.item) @@ -255,13 +271,14 @@ class TimeRestrictionTest(TestCase): r3 = TimeRestriction.objects.create( timeframe_from=now() - timedelta(days=5), timeframe_to=now() - timedelta(days=1), + event=self.event, price=8 ) r3.items.add(self.item) r3.variations.add(v2) result = signals.availability_handler( None, item=self.item, - variations=self.event.get_all_variations(), + variations=self.item.get_all_variations(), context=None, cache=self.event.get_cache() ) self.assertEqual(len(result), 3)