# HG changeset patch # User Brian Neal # Date 1323830475 0 # Node ID ae89ba801e8b95dbc94db50016c2dccbef693121 # Parent 6d816aa586c1e04f014cefcb98d4da90b6da85a5 For #194, convert the POTD management command to a celery task. Refactored to put the logic for the command into a function, and the command simply calls this function. The task can also just call this function. Added some basic tests for the new function. diff -r 6d816aa586c1 -r ae89ba801e8b gpp/potd/fixtures/potd_test.json --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gpp/potd/fixtures/potd_test.json Wed Dec 14 02:41:15 2011 +0000 @@ -0,0 +1,73 @@ +[ + { + "pk": 1, + "model": "auth.user", + "fields": { + "username": "Anonymous", + "first_name": "", + "last_name": "", + "is_active": false, + "is_superuser": false, + "is_staff": false, + "last_login": "1969-12-31 18:00:00", + "groups": [], + "user_permissions": [], + "password": "!", + "email": "", + "date_joined": "2000-11-10 00:00:00" + } + }, + { + "pk": 3, + "model": "potd.photo", + "fields": { + "description": "

The Deadbeats and Daikaiju after a gig at the 924 Gilman club in Berkeley, California.

", + "photo": "potd/2011/02/28/deadbeats-daikaiju.jpg", + "potd_count": 6, + "caption": "Daikaiju VS The Deadbeats", + "user": 1, + "date_added": "2011-02-28", + "thumb": "potd/2011/02/28/thumbs/deadbeats-daikaiju.jpg" + } + }, + { + "pk": 2, + "model": "potd.photo", + "fields": { + "description": "

The Kilaueas live on May the 29th, 2010 in Osnabrück, Germany. Playin` my new old 1964 sonic blue Jaguar. Tom is playing his 1972 Mustang bass. Matze is playing my Fender Custom from 1966/69. Twang cheers to everybody!

\r\n

-Ralf Kilauea.

", + "photo": "potd/2011/02/27/kilaueas.jpg", + "potd_count": 5, + "caption": "The Kilaueas Live in Germany", + "user": 1, + "date_added": "2011-02-27", + "thumb": "potd/2011/02/27/thumbs/kilaueas.jpg" + } + }, + { + "pk": 1, + "model": "potd.photo", + "fields": { + "description": "

Here is a photo of Dave Wronski of Slacktone at the Tiki Caliente show in Palm Springs, CA June 6, 2010. It was 110 degrees that day.

", + "photo": "potd/2011/02/26/wronski.jpg", + "potd_count": 6, + "caption": "Dave Wronski - 110 In The Shade", + "user": 1, + "date_added": "2011-02-26", + "thumb": "potd/2011/02/26/thumbs/wronski.jpg" + } + }, + { + "pk": 1, + "model": "potd.current", + "fields": { + "potd": 1 + } + }, + { + "pk": 1, + "model": "potd.sequence", + "fields": { + "seq": "1,2,3" + } + } +] diff -r 6d816aa586c1 -r ae89ba801e8b gpp/potd/management/commands/pick_potd.py --- a/gpp/potd/management/commands/pick_potd.py Tue Dec 13 02:37:50 2011 +0000 +++ b/gpp/potd/management/commands/pick_potd.py Wed Dec 14 02:41:15 2011 +0000 @@ -1,74 +1,15 @@ """ -pick_potd is a custom manage.py command for the POTD application. -It is intended to be called from a cron job at midnight to pick the -new POTD. +pick_potd is a custom manage.py command for the POTD application. +Calling it will pick a new POTD. + """ - -import random from django.core.management.base import NoArgsCommand -from potd.models import Current -from potd.models import Sequence -from potd.models import Photo +from potd.tools import pick_potd -def get_sequence(): - try: - s = Sequence.objects.get(pk=1) - if s.seq: - return [int(x) for x in s.seq.split(',')] - except: - pass - return [] - -def new_sequence(): - the_ids = Photo.objects.values_list('id', flat=True).order_by('id') - ids = [] - for id in the_ids.iterator(): - ids.append(int(id)) - - random.shuffle(ids) - try: - s = Sequence.objects.get(pk=1) - except Sequence.DoesNotExist: - s = Sequence() - - s.seq = ','.join([str(id) for id in ids]) - s.save() - return ids class Command(NoArgsCommand): - help = "Chooses the next POTD. Run this command at midnight to update the POTD." - #requires_model_validation = False + help = "Chooses the next POTD." def handle_noargs(self, **options): - try: - c = Current.objects.get(pk=1) - current = c.potd.pk - except Current.DoesNotExist: - c = Current() - current = None - - seq = get_sequence() - if current is None or len(seq) == 0 or current == seq[-1]: - # time to generate a new random sequence - seq = new_sequence() - # set current to the first one in the sequence - if len(seq) > 0: - try: - c.potd = Photo.objects.get(pk=seq[0]) - c.potd.potd_count += 1 - c.potd.save() - c.save() - except: - pass - else: - # find current in the sequence, pick the next one - try: - i = seq.index(current) - c.potd = Photo.objects.get(pk=seq[i + 1]) - c.potd.potd_count += 1 - c.potd.save() - c.save() - except: - pass - + pick_potd() diff -r 6d816aa586c1 -r ae89ba801e8b gpp/potd/models.py --- a/gpp/potd/models.py Tue Dec 13 02:37:50 2011 +0000 +++ b/gpp/potd/models.py Wed Dec 14 02:41:15 2011 +0000 @@ -1,5 +1,6 @@ """ Models for the Photo Of The Day (POTD) application. + """ import os from PIL import ImageFile @@ -89,7 +90,15 @@ class CurrentManager(models.Manager): + """ + Manager for the Current model. + + """ def get_current_photo(self): + """ + Retrieves the current photo object from the current record. + + """ try: c = self.get(pk=1) return c.potd @@ -97,6 +106,10 @@ return None def get_current_id(self): + """ + Returns the ID of the current POTD from the current record. + + """ potd = self.get_current_photo() if potd is not None: return potd.pk @@ -117,7 +130,16 @@ class SequenceManager(models.Manager): + """ + Manager for the Sequence model. + + """ def insert_photo(self, photo_id): + """ + Inserts the given photo_id just after the current photo so it + will appear as tomorrow's POTD. + + """ current = Current.objects.get_current_id() if current is not None: s = self.get(pk=1) @@ -129,6 +151,10 @@ s.save() def remove_photo(self, photo_id): + """ + Removes a given photo id from the sequence of photos. + + """ try: s = self.get(pk=1) except Sequence.DoesNotExist: diff -r 6d816aa586c1 -r ae89ba801e8b gpp/potd/tasks.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gpp/potd/tasks.py Wed Dec 14 02:41:15 2011 +0000 @@ -0,0 +1,12 @@ +""" +Celery tasks for the POTD app. + +""" +from celery.task import task + +import potd.tools + + +@task +def pick_potd(): + potd.tools.pick_potd() diff -r 6d816aa586c1 -r ae89ba801e8b gpp/potd/tests/__init__.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gpp/potd/tests/__init__.py Wed Dec 14 02:41:15 2011 +0000 @@ -0,0 +1,1 @@ +from tools_tests import * diff -r 6d816aa586c1 -r ae89ba801e8b gpp/potd/tests/tools_tests.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gpp/potd/tests/tools_tests.py Wed Dec 14 02:41:15 2011 +0000 @@ -0,0 +1,35 @@ +""" +Tests for the pick_potd() function. + +""" +from django.test import TestCase +from django.contrib.auth.models import User + +from potd.models import Current, Photo, Sequence +from potd.tools import pick_potd + + +class PickPotdTest(TestCase): + + fixtures = ['potd_test.json'] + + def test_pick(self): + + pick_potd() + + curr = Current.objects.get(pk=1) + self.assertEqual(curr.potd.pk, 2) + + def test_shuffle(self): + + photo = Photo.objects.get(pk=3) + curr = Current.objects.get(pk=1) + curr.potd = photo + curr.save() + + pick_potd() + + ids = Sequence.objects.get(pk=1).seq.split(',') + curr = Current.objects.get(pk=1) + self.assertEqual(len(ids), 3) + self.assertEqual(curr.potd.pk, int(ids[0])) diff -r 6d816aa586c1 -r ae89ba801e8b gpp/potd/tools.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gpp/potd/tools.py Wed Dec 14 02:41:15 2011 +0000 @@ -0,0 +1,96 @@ +""" +Tools for the Photo of the Day (potd) application. + +""" +import random +import logging + +from potd.models import Current, Sequence, Photo + + +logger = logging.getLogger(__name__) + + +def get_sequence(): + """ + Reads the photo sequence object from the database and converts it into a + list of photo IDs. If the sequence object is not found, and empty list is + returned. + + """ + try: + s = Sequence.objects.get(pk=1) + except Sequence.DoesNotExist: + return [] + + return [int(x) for x in s.seq.split(',')] + + +def new_sequence(): + """ + Generates a new random sequence of photos and saves it to the database. + The sequence is returned as a list of photo IDs. + + """ + ids = list(Photo.objects.values_list('id', flat=True)) + random.shuffle(ids) + + try: + s = Sequence.objects.get(pk=1) + except Sequence.DoesNotExist: + s = Sequence() + + s.seq = ','.join([str(n) for n in ids]) + s.save() + return ids + + +def pick_potd(): + """ + Chooses the next POTD. Run this command at midnight to update the POTD. + + """ + # Get the "current" record for the now old POTD: + try: + c = Current.objects.get(pk=1) + current = c.potd.pk + except Current.DoesNotExist: + c = Current() + current = None + + # Get the sequence of photo ID's: + seq = get_sequence() + + # If there is no current object, sequence, or if this was the last POTD in + # the sequence, generate a new random sequence: + + if current is None or not seq or current == seq[-1]: + # time to generate a new random sequence + seq = new_sequence() + # set current to the first one in the sequence + if seq: + try: + c.potd = Photo.objects.get(pk=seq[0]) + except Photo.DoesNotExist: + logger.error("POTD: missing photo %d", seq[0]) + else: + c.potd.potd_count += 1 + c.potd.save() + c.save() + else: + # find current in the sequence, pick the next one + try: + i = seq.index(current) + except ValueError: + logger.error("POTD: current photo (%d) not in sequence", current) + return + + n = i + 1 + try: + c.potd = Photo.objects.get(pk=seq[n]) + except Photo.DoesNotExist: + logger.error("POTD: missing next photo %d", n) + else: + c.potd.potd_count += 1 + c.potd.save() + c.save() diff -r 6d816aa586c1 -r ae89ba801e8b gpp/settings/base.py --- a/gpp/settings/base.py Tue Dec 13 02:37:50 2011 +0000 +++ b/gpp/settings/base.py Wed Dec 14 02:41:15 2011 +0000 @@ -6,6 +6,7 @@ import django.utils.simplejson as json from django.contrib.messages import constants as message_constants import djcelery +from celery.schedules import crontab PROJECT_PATH = os.path.abspath(os.path.join(os.path.split(__file__)[0], '..')) @@ -214,6 +215,13 @@ djcelery.setup_loader() +CELERYBEAT_SCHEDULE = { + "potd": { + "task": "potd.tasks.pick_potd", + "schedule": crontab(minute=0, hour=0), + } +} + ####################################################################### # GPP Specific Settings #######################################################################