Created
August 19, 2011 16:46
-
-
Save graingert/1157299 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# -*- coding: utf-8 -*- | |
"horfunk.radiopirate models and events handlers" | |
import datetime | |
import os | |
import logging | |
from django.db import models | |
from django.contrib.auth.models import User | |
from django.utils.translation import ugettext as _ | |
from product.modules.subscription.models import SubscriptionProduct \ | |
as SatchmoSubscriptionProduct | |
from satchmo_store.shop import signals as satchmo_signals | |
from horfunk.shows.models.podcasts import Podcast | |
from horfunk.streams.models import StreamContentGroup | |
import horfunk.radiopirate.cache | |
from horfunk.utils import user_profile | |
from sg.django_tools.cache import CachedInvalidator | |
logger = logging.getLogger(__name__) | |
class PodcastSubscription(models.Model): | |
"Subscribed user to a podcast" | |
user = models.ForeignKey( | |
User, | |
verbose_name=_("user"), | |
help_text=_("Subscribed user")) | |
podcast = models.ForeignKey( | |
Podcast, | |
verbose_name=_("podcast"), | |
help_text=_("Subscribed podcast")) | |
# regular subscription | |
expiration = models.DateField( | |
_("Expiration"), | |
help_text=_("Date when user subscription expire")) | |
# podcast on demand | |
tokens_expiration = models.DateField( | |
_("Tokens expiration"), | |
null=True, blank=True, | |
help_text=_("Date when tokens expires")) | |
tokens = models.PositiveIntegerField( | |
_("Remaining tokens"), | |
default=0, | |
help_text=_("Number of tokens user can spend on podcast download")) | |
@property | |
def is_expired(self): | |
"return True if subscription is expired" | |
return self.expiration <= datetime.datetime.now().date() | |
def __unicode__(self): | |
"return string representation of this PodcastSubscription instance" | |
return '%s for %s' % (self.user, self.podcast.slug) | |
class Meta: | |
"Metadata of PodcastSubscription" | |
verbose_name = _('podcast subscription') | |
verbose_name_plural = _('podcast subscriptions') | |
ordering = ('expiration', 'podcast', 'user',) | |
unique_together = ('user', 'podcast',) | |
class StreamSubscription(models.Model): | |
"Subscribed user to a stream content group" | |
user = models.ForeignKey( | |
User, | |
verbose_name=_("user"), | |
help_text=_("Subscribed user")) | |
stream = models.ForeignKey( | |
StreamContentGroup, | |
verbose_name=_("stream content group"), | |
help_text=_("Subscribed stream content group")) | |
# regular subscription | |
expiration = models.DateField( | |
_("Expiration"), | |
help_text=_("Date when user subscription expire")) | |
@property | |
def is_expired(self): | |
"return True if subscription is expired" | |
return self.expiration <= datetime.datetime.now().date() | |
def __unicode__(self): | |
"return string representation of this StreamSubscription instance" | |
return '%s | %s' % (self.user, self.stream) | |
class Meta: | |
"Metadata of StreamSubscription" | |
verbose_name = _('stream content group subscription') | |
verbose_name_plural = _('stream content group subscriptions') | |
ordering = ('expiration', 'stream', 'user',) | |
unique_together = ('user', 'stream',) | |
def extend_subscription(subscription, expire_date, log_prefix): | |
"""Pick an existing subscription and update or extend expiration date | |
:type: subscription: :class:`PodcastSubscription` or | |
:class:`StreamContentGroupSubscription` | |
:param: subscription: subscription instance to check | |
:type: expire_date: :class:`datetime.date` | |
:param: expire_date: new expiration date | |
:type: log_prefix: string | |
:param: log_prefix: message that will be in front of each log message | |
""" | |
today = datetime.datetime.now().date() | |
if subscription.expiration < today: | |
logger.debug("%s renew subscription that expired %s", log_prefix, | |
subscription.expiration) | |
subscription.expiration = expire_date | |
subscription.save() | |
else: | |
remaining = subscription.expiration - today | |
logger.info("%s renew subscription that was expiring in %d days", | |
log_prefix, remaining.days) | |
subscription.expiration = expire_date + remaining | |
logger.info("%s Order expires date %s is shifted to %s", log_prefix, | |
expire_date, subscription.expiration) | |
subscription.save() | |
class SubscriptionProduct(models.Model): | |
"Satchmo Product Subscription" | |
product = models.ForeignKey( | |
SatchmoSubscriptionProduct, | |
unique=True, | |
verbose_name=_("Associated Store Product"), | |
help_text=_("Select the Subscription Product in Store " | |
"associated to this entry")) | |
podcasts = models.ManyToManyField( | |
Podcast, | |
null=True, | |
blank=True, | |
help_text=_("List of one or more podcast the customer will" | |
"be subscribed to"), | |
verbose_name=_("subscribing podcast")) | |
streams = models.ManyToManyField( | |
StreamContentGroup, | |
null=True, | |
blank=True, | |
help_text=_("List of one or more audio stream content the customer will" | |
"be subscribed to"), | |
verbose_name=_("subscribing audio stream")) | |
description = models.TextField( | |
_("description"), | |
blank=True, | |
help_text=_("Optional description of this Subscription Product"), | |
max_length=2048) | |
def __unicode__(self): | |
"return real product slug" | |
return self.product.product.slug | |
def new_order(self, expire_date, user): | |
"""New order for this subscription product | |
type: expire_date: :class:`datetime.date` | |
param: expire_date: date when the subscription is suposed to expires | |
type: user: :class:`django.contrib.auth.models.User` | |
param: user: user who complete the order | |
""" | |
# a profile is required past this point | |
if not user_profile(user): | |
logger.info("user %s did not have a profile yet, create one with" | |
"default values", user) | |
new_profile = Profile(user=user) | |
new_profile.save() | |
# start with podcast | |
for podcast in self.podcasts.all(): | |
log_prefix = "new_order Podcast[%s] User[%s]:" % (podcast, user) | |
try: | |
existing = PodcastSubscription.objects.get(user=user, | |
podcast=podcast) | |
extend_subscription(existing, expire_date, log_prefix) | |
except PodcastSubscription.DoesNotExist: | |
logger.debug("%s is a new subscription", log_prefix) | |
new_subscription = PodcastSubscription(user=user, | |
podcast=podcast, | |
expiration=expire_date) | |
new_subscription.save() | |
# then stream content group | |
for content_group in self.streams.all(): | |
log_prefix = "StreamContentGroup[%s] User[%s]:" % (content_group, | |
user) | |
try: | |
existing = StreamSubscription.objects.get(user=user, | |
stream=content_group) | |
extend_subscription(existing, expire_date, log_prefix) | |
except StreamSubscription.DoesNotExist: | |
logger.debug("%s is a new subscription", log_prefix) | |
new_subscription = StreamSubscription(user=user, | |
stream=content_group, | |
expiration=expire_date) | |
new_subscription.save() | |
class Meta: | |
"Metadata of RPSubscriptionProduct" | |
verbose_name = _('RadioPirate.com Subscription product') | |
verbose_name_plural = _('RadioPirate.com Subscription products') | |
class Profile(models.Model): | |
""" | |
Profile of a single User in RadioPirate.com website | |
""" | |
user = models.OneToOneField( | |
User, | |
unique=True) | |
public_profile_field = models.BooleanField( | |
_("Is public"), | |
help_text=_("Is your profile public to other members"), | |
default=False) | |
suggesting_user = models.ForeignKey(User, | |
null=True, | |
related_name = "suggestions") | |
# subscriptions | |
@property | |
def podcast_subscriptions(self): | |
"return QuerySet of podcast subscription of this user profile" | |
subscriptions = PodcastSubscription.objects.filter(user=self.user) | |
logger.debug("User[%s] got %d podcast subscriptions", | |
self.user, len(subscriptions)) | |
return subscriptions | |
@property | |
def stream_subscriptions(self): | |
"return QuerySet of subscription of this user profile" | |
subscriptions = StreamSubscription.objects.filter(user=self.user) | |
logger.debug("User[%s] got %d stream subscriptions", | |
self.user, len(subscriptions)) | |
return subscriptions | |
@models.permalink | |
def get_absolute_url(self): | |
"return this profile instance URL" | |
return 'profiles_profile_detail', [self.user.username] | |
class Meta: | |
"Profile metadata" | |
verbose_name = _('profile') | |
verbose_name_plural = _('profiles') | |
ordering = ('user',) | |
class RadioGroup(models.Model): | |
"Group of radio station" | |
title = models.CharField( | |
_("name of this group of radio"), | |
max_length=140, | |
unique=True, | |
help_text=_("One-liner naming the group of radio")) | |
description = models.TextField( | |
_("radio group description"), | |
blank=True, | |
help_text=_("Radio group description in HTML"), | |
max_length=2048) | |
slug = models.SlugField( | |
_("URL compatible name"), | |
unique=True, | |
help_text=_("URL compatible name, used for template directory too")) | |
content_group = models.ManyToManyField( | |
StreamContentGroup, | |
verbose_name=_("List of stream content group"), | |
help_text=_("List of StreamContentGroup in this group of radio")) | |
def __unicode__(self): | |
return '%s (%s)' % (self.title, self.slug) | |
@models.permalink | |
def get_absolute_url(self): | |
"return absolute URL of this instance" | |
return 'radiogroup_index', [self.slug] | |
def template_file(self, prefix, extension='html', root='radios'): | |
""" | |
return template file for specified prefix filename | |
the template file name is create from: | |
%(root)s/%(radiogroup.slug)%/%(prefix).%(extension) | |
so, for radiogroup with slug 'rock' will be, by default: | |
"radios/rock/%(prefix).html" | |
""" | |
# append .html | |
filename = os.extsep.join((prefix, extension)) | |
return os.sep.join((root, self.slug, filename)) | |
class Meta: | |
"Metadata for RadioGroup" | |
verbose_name = _("group of radio station") | |
verbose_name_plural = _("groups of radio station") | |
ordering = ('slug',) | |
class ContactCategory(models.Model): | |
""" | |
Contact category recipients | |
""" | |
name = models.CharField( | |
_('name'), | |
max_length=256, | |
unique=True, | |
help_text=_('User displayed text associatedto this category')) | |
recipient = models.EmailField( | |
_('recipient email'), | |
help_text=_('mail is sent to this address when a' | |
'new contact message is submited')) | |
def __unicode__(self): | |
return self.name | |
class Meta: | |
"ContactCategory metadata" | |
verbose_name = _('contact category') | |
verbose_name_plural = _('contact categories') | |
ordering = ('name', 'recipient') | |
class AffiliationRequest(models.Model): | |
submitter = models.ForeignKey(User) | |
referrals = | |
def AffilateJunction(models.Model): | |
request = models.ForeignKey(AffiliationRequest) | |
referral = models.ForeignKey(User) | |
approved = BooleanField(default=False) | |
def _meta(): | |
unique_together = ("request", "referral") | |
def subscription_sale_listener(order=None, **kwargs): | |
""" | |
Signal receiver function that listen to all complted Satchmo | |
orders and look if a purchased product is a RadioPirate subscription | |
""" | |
logger.debug("New sale!") | |
if order: | |
log_prefix = "order #%i: " % order.id | |
for item in order.orderitem_set.all(): | |
item_log = log_prefix + "product '%s': " % item.product.slug | |
if item.product.is_subscription: | |
logger.debug("%sis a subscription", item_log) | |
try: | |
subscription_product = SubscriptionProduct.objects.get( | |
product=item.product) | |
logger.debug("%sis a RP Subscription", item_log) | |
subscription_product.new_order(item.expire_date, | |
order.contact.user) | |
except SubscriptionProduct.DoesNotExist: | |
logger.debug("%sis not RP subscription", item_log) | |
else: | |
logger.debug("%snot a subscription, ignore it", item_log) | |
else: | |
logger.error("No order?") | |
INVALIDATOR = CachedInvalidator({ | |
StreamSubscription: ('subscriptions', 'subscriptions-episodes'), | |
PodcastSubscription: ('subscriptions', 'subscriptions-episodes')}) | |
horfunk.radiopirate.cache.connect( | |
{Profile: horfunk.radiopirate.cache.invalidate_profile}) | |
satchmo_signals.order_success.connect(subscription_sale_listener) | |
# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment