regluit/core/signals.py

352 lines
19 KiB
Python

"""
external library imports
"""
import datetime
import itertools
import logging
from tastypie.models import create_api_key
"""
django imports
"""
import django.dispatch
from django.apps import apps
from django.conf import settings
from django.contrib.auth.models import User
from django.contrib.sites.models import Site
from django.db.models import signals
from django.db.models.signals import post_save
from django.db.utils import DatabaseError
from django.dispatch import Signal
from django.utils.translation import ugettext_noop as _
from django.template.loader import render_to_string
from notification import models as notification
"""
regluit imports
"""
from regluit.payment.signals import transaction_charged, transaction_failed, pledge_modified, pledge_created
from regluit.utils.localdatetime import now, date_today
from regluit.core.parameters import REWARDS, BUY2UNGLUE, THANKS, LIBRARY, RESERVE, THANKED
from regluit.libraryauth.models import Library, LibraryUser
logger = logging.getLogger(__name__)
# create Wishlist and UserProfile to associate with User
def create_user_objects(sender, created, instance, **kwargs):
# use get_model to avoid circular import problem with models
# don't create Wishlist or UserProfile if we are loading fixtures https://stackoverflow.com/a/3500009/7782
if not kwargs.get('raw', False):
try:
Wishlist = apps.get_model('core', 'Wishlist')
UserProfile = apps.get_model('core', 'UserProfile')
if created:
Wishlist.objects.create(user=instance)
profile = UserProfile.objects.create(user=instance)
profile.ml_subscribe()
except DatabaseError:
# this can happen when creating superuser during syncdb since the
# core_wishlist table doesn't exist yet
return
post_save.connect(create_user_objects, sender=User)
# create API key for new User
post_save.connect(create_api_key, sender=User)
# create notification types (using django-notification) -- tie to syncdb
def create_notice_types( **kwargs):
notification.create_notice_type("comment_on_commented", _("Comment on Commented Work"), _("A comment has been received on a book that you've commented on."))
notification.create_notice_type("wishlist_comment", _("Book List Comment"), _("A comment has been received on one of your books."), default = 1)
notification.create_notice_type("wishlist_official_comment", _("Book List Comment"), _("The author or publisher, or and Unglue.it staffer, has commented on one of your faves."))
notification.create_notice_type("wishlist_work_claimed", _("Rights Holder is Active"), _("A rights holder has shown up for a book that you've faved."), default = 1)
notification.create_notice_type("wishlist_active", _("New Campaign"), _("A book you've favorited has a newly launched campaign."))
notification.create_notice_type("wishlist_near_target", _("Campaign Near Target"), _("A book you want is near its ungluing target."))
notification.create_notice_type("wishlist_near_deadline", _("Campaign Near Deadline"), _("A book you want is almost out of time."))
notification.create_notice_type("wishlist_premium_limited_supply", _("Only a Few Premiums Left"), _("A limited edition premium is running out on a book you've faved."))
notification.create_notice_type("wishlist_successful", _("Successful Campaign"), _("An ungluing campaign that you have supported or faved has succeeded."))
notification.create_notice_type("wishlist_unsuccessful", _("Unsuccessful Campaign"), _("An ungluing campaign that you supported didn't succeed this time."))
notification.create_notice_type("wishlist_updated", _("Campaign Updated"), _("An ungluing campaign you support has been updated."), default = 1)
notification.create_notice_type("wishlist_message", _("Campaign Communication"), _("You have a private message from unglue.it staff or the rights holder about a book you've faved."))
notification.create_notice_type("wishlist_price_drop", _("Campaign Price Drop"), _("An ungluing campaign you've faved has a reduced target."), default = 1)
notification.create_notice_type("wishlist_unglued_book_released", _("Unglued Book!"), _("A book you've faved is now available to be downloaded."))
notification.create_notice_type("pledge_you_have_pledged", _("Thanks For Your Pledge!"), _("Your ungluing pledge has been entered."))
notification.create_notice_type("pledge_status_change", _("Your Pledge Has Been Modified"), _("Your ungluing pledge has been modified."))
notification.create_notice_type("pledge_charged", _("Your Pledge has been Executed"), _("You have contributed to a successful ungluing campaign."))
notification.create_notice_type("pledge_failed", _("Unable to charge your credit card"), _("A charge to your credit card did not go through."))
notification.create_notice_type("rights_holder_created", _("Agreement Accepted"), _("You have applied to become an Unglue.it rights holder."))
notification.create_notice_type("rights_holder_accepted", _("Agreement Accepted"), _("You have become a verified Unglue.it rights holder."))
notification.create_notice_type("rights_holder_claim", _("Claim Entered"), _("A claim has been entered."))
notification.create_notice_type("wishlist_unsuccessful_amazon", _("Campaign shut down"), _("An ungluing campaign that you supported had to be shut down due to an Amazon Payments policy change."))
notification.create_notice_type("pledge_gift_credit", _("Credit Balance"), _("You have a credit balance"))
notification.create_notice_type("new_wisher", _("New wisher"), _("Someone new has faved a book that you're the rightsholder for"))
notification.create_notice_type("account_expiring", _("Credit Card Expiring Soon"), _("Your credit card is about to expire."))
notification.create_notice_type("account_expired", _("Credit Card Has Expired"), _("Your credit card has expired."))
notification.create_notice_type("account_active", _("Credit Card Number Updated"), _("Payment method updated."), default = 1)
notification.create_notice_type("purchase_complete", _("Your Purchase is Complete"), _("Your Unglue.it Purchase is Complete."))
notification.create_notice_type("library_borrow", _("Library eBook Borrowed."), _("You've borrowed an ebook through a Library participating in Unglue.it"))
notification.create_notice_type("library_reserve", _("Library eBook Reserved."), _("An ebook you've reserved is available."))
notification.create_notice_type("library_join", _("New Library User."), _("A library participating in Unglue.it has added a user"))
notification.create_notice_type("purchase_gift", _("You have a gift."), _("An ungluer has given you an ebook."))
notification.create_notice_type("purchase_got_gift", _("Your gift was received."), _("The ebook you sent as a gift has been redeemed."))
notification.create_notice_type("purchase_gift_waiting", _("Your gift is waiting."), _("Please claim your ebook."))
notification.create_notice_type("purchase_notgot_gift", _("Your gift wasn't received."), _("The ebook you sent as a gift has not yet been redeemed."))
notification.create_notice_type("donation", _("Your donation was processed."), _("Thank you, your generous donation has been processed."))
signals.post_syncdb.connect(create_notice_types, sender=notification)
# define the notifications and tie them to corresponding signals
from django_comments.signals import comment_was_posted
def notify_comment(comment, request, **kwargs):
logger.info('comment %s notifying' % comment.pk)
other_commenters = User.objects.filter(comment_comments__content_type=comment.content_type, comment_comments__object_pk=comment.object_pk).distinct().exclude(id=comment.user_id)
all_wishers = comment.content_object.wished_by().exclude(id=comment.user_id)
other_wishers = all_wishers.exclude(id__in=other_commenters)
domain = Site.objects.get_current().domain
if comment.content_object.last_campaign() and comment.user in comment.content_object.last_campaign().managers.all():
#official
notification.queue(all_wishers, "wishlist_official_comment", {'comment':comment, 'domain':domain}, True)
else:
notification.send(other_commenters, "comment_on_commented", {'comment':comment}, True, sender=comment.user)
notification.send(other_wishers, "wishlist_comment", {'comment':comment}, True, sender=comment.user)
from regluit.core.tasks import emit_notifications
emit_notifications.delay()
comment_was_posted.connect(notify_comment)
# Successful campaign signal
# https://code.djangoproject.com/browser/django/tags/releases/1.3.1/django/db/models/signals.py
successful_campaign = Signal(providing_args=["campaign"])
def notify_successful_campaign(campaign, **kwargs):
"""send notification in response to successful campaign"""
logger.info('received successful_campaign signal for {0}'.format(campaign))
# supporters and staff -- though it might be annoying for staff to be getting all these notices!
staff = User.objects.filter(is_staff=True)
supporters = (User.objects.get(id=k) for k in campaign.supporters())
notification.send(itertools.chain(staff, supporters), "wishlist_successful", {'campaign':campaign}, True)
from regluit.core.tasks import emit_notifications
emit_notifications.delay()
# successful_campaign -> send notices
successful_campaign.connect(notify_successful_campaign)
unsuccessful_campaign = Signal(providing_args=["campaign"])
def notify_unsuccessful_campaign(campaign, **kwargs):
"""send notification in response to unsuccessful campaign"""
logger.info('received unsuccessful_campaign signal for {0}'.format(campaign))
# supporters and staff -- though it might be annoying for staff to be getting all these notices!
staff = User.objects.filter(is_staff=True)
supporters = (User.objects.get(id=k) for k in campaign.supporters())
notification.send(itertools.chain(staff, supporters), "wishlist_unsuccessful", {'campaign':campaign}, True)
from regluit.core.tasks import emit_notifications
emit_notifications.delay()
# unsuccessful_campaign -> send notices
unsuccessful_campaign.connect(notify_unsuccessful_campaign)
def handle_transaction_charged(sender,transaction=None, **kwargs):
if transaction==None:
return
transaction._current_total = None
context = {'transaction':transaction,'current_site':Site.objects.get_current()}
if not transaction.campaign:
if transaction.user:
notification.send([transaction.user], "donation", context, True)
elif transaction.receipt:
from regluit.core.tasks import send_mail_task
message = render_to_string("notification/donation/full.txt", context )
send_mail_task.delay('unglue.it donation confirmation', message, 'notices@gluejar.com', [transaction.receipt])
elif transaction.campaign.type is REWARDS:
notification.send([transaction.user], "pledge_charged", context, True)
elif transaction.campaign.type is BUY2UNGLUE:
# provision the book
Acq = apps.get_model('core', 'Acq')
if transaction.offer.license == LIBRARY:
library = Library.objects.get(id=transaction.extra['library_id'])
new_acq = Acq.objects.create(user=library.user,work=transaction.campaign.work,license= LIBRARY)
if transaction.user_id != library.user_id: # don't put it on reserve if purchased by the library
reserve_acq = Acq.objects.create(user=transaction.user,work=transaction.campaign.work,license= RESERVE, lib_acq = new_acq)
reserve_acq.expire_in(datetime.timedelta(hours=2))
copies = int(transaction.extra.get('copies',1))
while copies > 1:
Acq.objects.create(user=library.user,work=transaction.campaign.work,license= LIBRARY)
copies -= 1
else:
if transaction.extra.get('give_to', False):
# it's a gift!
Gift = apps.get_model('core', 'Gift')
giftee = Gift.giftee(transaction.extra['give_to'], str(transaction.id))
new_acq = Acq.objects.create(user=giftee, work=transaction.campaign.work, license= transaction.offer.license)
gift = Gift.objects.create(acq=new_acq, message=transaction.extra.get('give_message',''), giver=transaction.user , to = transaction.extra['give_to'])
context['gift'] = gift
notification.send([giftee], "purchase_gift", context, True)
else:
new_acq = Acq.objects.create(user=transaction.user,work=transaction.campaign.work,license= transaction.offer.license)
transaction.campaign.update_left()
notification.send([transaction.user], "purchase_complete", context, True)
from regluit.core.tasks import watermark_acq
watermark_acq.delay(new_acq)
if transaction.campaign.cc_date < date_today() :
transaction.campaign.update_status(send_notice=True)
elif transaction.campaign.type is THANKS:
if transaction.user:
Acq = apps.get_model('core', 'Acq')
new_acq = Acq.objects.create(user=transaction.user, work=transaction.campaign.work, license=THANKED)
notification.send([transaction.user], "purchase_complete", context, True)
elif transaction.receipt:
from regluit.core.tasks import send_mail_task
message = render_to_string("notification/purchase_complete/full.txt", context )
send_mail_task.delay('unglue.it transaction confirmation', message, 'notices@gluejar.com', [transaction.receipt])
if transaction.user:
from regluit.core.tasks import emit_notifications
emit_notifications.delay()
transaction_charged.connect(handle_transaction_charged)
# dealing with failed transactions
def handle_transaction_failed(sender,transaction=None, **kwargs):
if transaction is None or transaction.campaign == None or transaction.campaign.type == THANKS:
# no need to nag a failed THANKS or DONATION transaction
return
# window for recharging
recharge_deadline = transaction.deadline_or_now + datetime.timedelta(settings.RECHARGE_WINDOW)
notification.send([transaction.user], "pledge_failed", {
'transaction':transaction,
'recharge_deadline': recharge_deadline
}, True)
from regluit.core.tasks import emit_notifications
emit_notifications.delay()
transaction_failed.connect(handle_transaction_failed)
def handle_pledge_modified(sender, transaction=None, up_or_down=None, **kwargs):
# we need to know if pledges were modified up or down because Amazon handles the
# transactions in different ways, resulting in different user-visible behavior;
# we need to set expectations appropriately
# up_or_down is 'increased', 'decreased', or 'canceled'
if transaction==None or up_or_down==None:
return
if up_or_down == 'canceled':
transaction.user.profile.reset_pledge_badge()
notification.send([transaction.user], "pledge_status_change", {
'transaction': transaction,
'up_or_down': up_or_down
}, True)
from regluit.core.tasks import emit_notifications
emit_notifications.delay()
pledge_modified.connect(handle_pledge_modified)
def handle_you_have_pledged(sender, transaction=None, **kwargs):
if transaction==None:
return
#give user a badge
if not transaction.anonymous:
transaction.user.profile.reset_pledge_badge()
notification.send([transaction.user], "pledge_you_have_pledged", {
'transaction': transaction
}, True)
from regluit.core.tasks import emit_notifications
emit_notifications.delay()
pledge_created.connect(handle_you_have_pledged)
amazon_suspension = Signal(providing_args=["campaign"])
def handle_wishlist_unsuccessful_amazon(campaign, **kwargs):
"""send notification in response to campaign shutdown following Amazon suspension"""
logger.info('received amazon_suspension signal for {0}'.format(campaign))
# supporters and staff -- though it might be annoying for staff to be getting all these notices!
staff = User.objects.filter(is_staff=True)
supporters = (User.objects.get(id=k) for k in campaign.supporters())
notification.send(itertools.chain(staff, supporters), "wishlist_unsuccessful_amazon", {'campaign':campaign}, True)
from regluit.core.tasks import emit_notifications
emit_notifications.delay()
amazon_suspension.connect(handle_wishlist_unsuccessful_amazon)
wishlist_added = Signal(providing_args=["supporter", "work"])
def handle_wishlist_added(supporter, work, **kwargs):
"""send notification to confirmed rights holder when someone wishes for their work"""
claim = work.claim.filter(status="active")
if claim:
notification.send([claim[0].user], "new_wisher", {
'supporter': supporter,
'work': work,
'base_url': settings.BASE_URL_SECURE,
}, True)
from regluit.core.tasks import emit_notifications
emit_notifications.delay()
wishlist_added.connect(handle_wishlist_added)
deadline_impending = Signal(providing_args=["campaign"])
def handle_wishlist_near_deadline(campaign, **kwargs):
"""
send two groups - one the nonpledgers, one the pledgers
set the pledged flag differently in the context
"""
pledgers = campaign.ungluers()['all']
nonpledgers = campaign.work.wished_by().exclude(id__in=[p.id for p in pledgers])
notification.send(pledgers, "wishlist_near_deadline", {
'campaign': campaign,
'domain': settings.BASE_URL_SECURE,
'pledged': True,
}, True)
notification.send(nonpledgers, "wishlist_near_deadline", {
'campaign': campaign,
'domain': settings.BASE_URL_SECURE,
'pledged': False,
}, True)
from regluit.core.tasks import emit_notifications
emit_notifications.delay()
deadline_impending.connect(handle_wishlist_near_deadline)
supporter_message = Signal(providing_args=["supporter", "work", "msg"])
def notify_supporter_message(sender, work, supporter, msg, **kwargs):
"""send notification in of supporter message"""
logger.info('received supporter_message signal for {0}'.format(supporter))
notification.send( [supporter], "wishlist_message", {'work':work, 'msg':msg}, True, sender)
from regluit.core.tasks import emit_notifications
emit_notifications.delay()
supporter_message.connect(notify_supporter_message)
def notify_join_library(sender, created, instance, **kwargs):
if created:
notification.send((instance.user, instance.library.user), "library_join", {
'library': instance.library,
'user': instance.user,
})
post_save.connect(notify_join_library, sender=LibraryUser)