2013-05-13 16:17:49 -07:00
|
|
|
import time
|
2010-06-30 13:36:51 -04:00
|
|
|
import datetime
|
2014-11-14 10:32:03 -08:00
|
|
|
import dateutil
|
2012-12-03 14:35:21 -08:00
|
|
|
import stripe
|
2013-03-20 18:36:15 -07:00
|
|
|
import hashlib
|
2015-08-26 10:58:05 -07:00
|
|
|
import re
|
2013-05-13 18:03:17 -07:00
|
|
|
import redis
|
2015-06-30 16:05:58 -07:00
|
|
|
import uuid
|
2012-07-09 13:55:29 -07:00
|
|
|
import mongoengine as mongo
|
2009-06-16 03:08:55 +00:00
|
|
|
from django.db import models
|
2010-10-25 20:20:59 -04:00
|
|
|
from django.db import IntegrityError
|
2011-07-17 20:53:30 -07:00
|
|
|
from django.db.utils import DatabaseError
|
2010-05-11 21:36:17 -04:00
|
|
|
from django.db.models.signals import post_save
|
2013-02-15 13:47:45 -08:00
|
|
|
from django.db.models import Sum, Avg, Count
|
2011-09-19 08:56:16 -07:00
|
|
|
from django.conf import settings
|
2010-11-08 12:09:55 -05:00
|
|
|
from django.contrib.auth import authenticate
|
2011-09-19 08:56:16 -07:00
|
|
|
from django.contrib.auth.models import User
|
|
|
|
from django.core.mail import EmailMultiAlternatives
|
2011-09-19 09:46:36 -07:00
|
|
|
from django.core.urlresolvers import reverse
|
2011-09-19 08:56:16 -07:00
|
|
|
from django.template.loader import render_to_string
|
2013-07-11 15:55:37 -07:00
|
|
|
from apps.rss_feeds.models import Feed, MStory, MStarredStory
|
2013-02-15 09:52:11 -08:00
|
|
|
from apps.rss_feeds.tasks import SchedulePremiumSetup
|
2013-05-23 18:14:21 -07:00
|
|
|
from apps.feed_import.models import GoogleReaderImporter, OPMLExporter
|
2015-07-28 18:46:37 -07:00
|
|
|
from apps.reader.models import UserSubscription
|
2015-07-30 18:10:55 -07:00
|
|
|
from apps.reader.models import RUserStory
|
2010-10-23 12:29:18 -04:00
|
|
|
from utils import log as logging
|
2012-07-13 18:42:44 -07:00
|
|
|
from utils import json_functions as json
|
2011-01-20 09:57:23 -05:00
|
|
|
from utils.user_functions import generate_secret_token
|
2015-07-28 18:46:37 -07:00
|
|
|
from utils.feed_functions import chunks
|
2011-09-19 08:56:16 -07:00
|
|
|
from vendor.timezones.fields import TimeZoneField
|
2015-01-15 12:40:17 -08:00
|
|
|
from vendor.paypal.standard.ipn.signals import subscription_signup, payment_was_successful, recurring_payment
|
|
|
|
from vendor.paypal.standard.ipn.signals import payment_was_flagged
|
2012-12-03 14:35:21 -08:00
|
|
|
from vendor.paypal.standard.ipn.models import PayPalIPN
|
2013-05-23 16:28:39 -07:00
|
|
|
from vendor.paypalapi.interface import PayPalInterface
|
2014-02-18 17:34:46 -08:00
|
|
|
from vendor.paypalapi.exceptions import PayPalAPIResponseError
|
2012-02-28 17:37:01 -08:00
|
|
|
from zebra.signals import zebra_webhook_customer_subscription_created
|
2012-12-03 15:17:35 -08:00
|
|
|
from zebra.signals import zebra_webhook_charge_succeeded
|
2011-09-19 08:56:16 -07:00
|
|
|
|
2010-05-11 21:36:17 -04:00
|
|
|
class Profile(models.Model):
|
2011-05-12 18:15:15 -04:00
|
|
|
user = models.OneToOneField(User, unique=True, related_name="profile")
|
|
|
|
is_premium = models.BooleanField(default=False)
|
2012-12-03 14:35:21 -08:00
|
|
|
premium_expire = models.DateTimeField(blank=True, null=True)
|
2011-09-21 17:49:26 -07:00
|
|
|
send_emails = models.BooleanField(default=True)
|
2011-05-12 18:15:15 -04:00
|
|
|
preferences = models.TextField(default="{}")
|
|
|
|
view_settings = models.TextField(default="{}")
|
2010-09-05 18:08:08 -07:00
|
|
|
collapsed_folders = models.TextField(default="[]")
|
2013-07-05 09:31:49 +02:00
|
|
|
feed_pane_size = models.IntegerField(default=242)
|
2012-03-20 11:15:40 -07:00
|
|
|
tutorial_finished = models.BooleanField(default=False)
|
2012-03-21 12:28:51 -07:00
|
|
|
hide_getting_started = models.NullBooleanField(default=False, null=True, blank=True)
|
|
|
|
has_setup_feeds = models.NullBooleanField(default=False, null=True, blank=True)
|
|
|
|
has_found_friends = models.NullBooleanField(default=False, null=True, blank=True)
|
|
|
|
has_trained_intelligence = models.NullBooleanField(default=False, null=True, blank=True)
|
2011-05-12 18:15:15 -04:00
|
|
|
last_seen_on = models.DateTimeField(default=datetime.datetime.now)
|
|
|
|
last_seen_ip = models.CharField(max_length=50, blank=True, null=True)
|
2012-04-21 18:20:49 -07:00
|
|
|
dashboard_date = models.DateTimeField(default=datetime.datetime.now)
|
2011-05-12 18:15:15 -04:00
|
|
|
timezone = TimeZoneField(default="America/New_York")
|
|
|
|
secret_token = models.CharField(max_length=12, blank=True, null=True)
|
2012-02-27 21:46:34 -08:00
|
|
|
stripe_4_digits = models.CharField(max_length=4, blank=True, null=True)
|
|
|
|
stripe_id = models.CharField(max_length=24, blank=True, null=True)
|
2010-05-11 21:36:17 -04:00
|
|
|
|
2010-10-23 11:20:54 -04:00
|
|
|
def __unicode__(self):
|
2011-09-19 08:56:16 -07:00
|
|
|
return "%s <%s> (Premium: %s)" % (self.user, self.user.email, self.is_premium)
|
2011-01-20 09:57:23 -05:00
|
|
|
|
2013-09-16 16:42:49 -07:00
|
|
|
@property
|
2013-10-07 10:02:44 -07:00
|
|
|
def unread_cutoff(self, force_premium=False):
|
|
|
|
if self.is_premium or force_premium:
|
2013-09-16 16:42:49 -07:00
|
|
|
return datetime.datetime.utcnow() - datetime.timedelta(days=settings.DAYS_OF_UNREAD)
|
|
|
|
|
|
|
|
return datetime.datetime.utcnow() - datetime.timedelta(days=settings.DAYS_OF_UNREAD_FREE)
|
2013-10-07 10:02:44 -07:00
|
|
|
|
|
|
|
@property
|
|
|
|
def unread_cutoff_premium(self):
|
|
|
|
return datetime.datetime.utcnow() - datetime.timedelta(days=settings.DAYS_OF_UNREAD)
|
2013-09-16 16:42:49 -07:00
|
|
|
|
2013-06-12 13:52:43 -07:00
|
|
|
def canonical(self):
|
2012-07-13 18:42:44 -07:00
|
|
|
return {
|
|
|
|
'is_premium': self.is_premium,
|
2017-11-15 11:05:24 -08:00
|
|
|
'premium_expire': int(self.premium_expire.strftime('%s')) if self.premium_expire else 0,
|
2012-07-13 18:42:44 -07:00
|
|
|
'preferences': json.decode(self.preferences),
|
|
|
|
'tutorial_finished': self.tutorial_finished,
|
|
|
|
'hide_getting_started': self.hide_getting_started,
|
|
|
|
'has_setup_feeds': self.has_setup_feeds,
|
|
|
|
'has_found_friends': self.has_found_friends,
|
|
|
|
'has_trained_intelligence': self.has_trained_intelligence,
|
|
|
|
'dashboard_date': self.dashboard_date
|
|
|
|
}
|
|
|
|
|
2011-01-20 09:57:23 -05:00
|
|
|
def save(self, *args, **kwargs):
|
|
|
|
if not self.secret_token:
|
|
|
|
self.secret_token = generate_secret_token(self.user.username, 12)
|
2011-07-17 20:53:30 -07:00
|
|
|
try:
|
|
|
|
super(Profile, self).save(*args, **kwargs)
|
|
|
|
except DatabaseError:
|
|
|
|
print " ---> Profile not saved. Table isn't there yet."
|
2011-01-20 09:57:23 -05:00
|
|
|
|
2014-06-16 15:01:05 -07:00
|
|
|
def delete_user(self, confirm=False, fast=False):
|
2012-07-28 18:33:07 -07:00
|
|
|
if not confirm:
|
|
|
|
print " ---> You must pass confirm=True to delete this user."
|
|
|
|
return
|
|
|
|
|
2015-11-23 11:22:00 -08:00
|
|
|
logging.user(self.user, "Deleting user: %s / %s" % (self.user.email, self.user.profile.last_seen_ip))
|
2014-04-03 14:59:30 -07:00
|
|
|
try:
|
|
|
|
self.cancel_premium()
|
|
|
|
except:
|
|
|
|
logging.user(self.user, "~BR~SK~FWError cancelling premium renewal for: %s" % self.user.username)
|
|
|
|
|
2012-07-28 18:33:07 -07:00
|
|
|
from apps.social.models import MSocialProfile, MSharedStory, MSocialSubscription
|
|
|
|
from apps.social.models import MActivity, MInteraction
|
2012-07-29 23:53:02 -07:00
|
|
|
try:
|
|
|
|
social_profile = MSocialProfile.objects.get(user_id=self.user.pk)
|
2013-01-03 10:33:22 -08:00
|
|
|
logging.user(self.user, "Unfollowing %s followings and %s followers" %
|
|
|
|
(social_profile.following_count,
|
|
|
|
social_profile.follower_count))
|
2012-07-29 23:53:02 -07:00
|
|
|
for follow in social_profile.following_user_ids:
|
|
|
|
social_profile.unfollow_user(follow)
|
|
|
|
for follower in social_profile.follower_user_ids:
|
|
|
|
follower_profile = MSocialProfile.objects.get(user_id=follower)
|
|
|
|
follower_profile.unfollow_user(self.user.pk)
|
|
|
|
social_profile.delete()
|
|
|
|
except MSocialProfile.DoesNotExist:
|
2013-01-03 10:33:22 -08:00
|
|
|
logging.user(self.user, " ***> No social profile found. S'ok, moving on.")
|
2012-07-29 23:53:02 -07:00
|
|
|
pass
|
2012-07-28 18:33:07 -07:00
|
|
|
|
|
|
|
shared_stories = MSharedStory.objects.filter(user_id=self.user.pk)
|
2013-01-03 10:33:22 -08:00
|
|
|
logging.user(self.user, "Deleting %s shared stories" % shared_stories.count())
|
2012-07-28 18:33:07 -07:00
|
|
|
for story in shared_stories:
|
2012-10-25 14:18:25 -07:00
|
|
|
try:
|
2014-06-16 15:01:05 -07:00
|
|
|
if not fast:
|
|
|
|
original_story = MStory.objects.get(story_hash=story.story_hash)
|
|
|
|
original_story.sync_redis()
|
2012-10-25 14:18:25 -07:00
|
|
|
except MStory.DoesNotExist:
|
|
|
|
pass
|
2012-07-28 18:33:07 -07:00
|
|
|
story.delete()
|
|
|
|
|
|
|
|
subscriptions = MSocialSubscription.objects.filter(subscription_user_id=self.user.pk)
|
2013-01-03 10:33:22 -08:00
|
|
|
logging.user(self.user, "Deleting %s social subscriptions" % subscriptions.count())
|
2012-07-28 18:33:07 -07:00
|
|
|
subscriptions.delete()
|
|
|
|
|
|
|
|
interactions = MInteraction.objects.filter(user_id=self.user.pk)
|
2013-01-03 10:33:22 -08:00
|
|
|
logging.user(self.user, "Deleting %s interactions for user." % interactions.count())
|
2012-07-28 18:33:07 -07:00
|
|
|
interactions.delete()
|
|
|
|
|
|
|
|
interactions = MInteraction.objects.filter(with_user_id=self.user.pk)
|
2013-01-03 10:33:22 -08:00
|
|
|
logging.user(self.user, "Deleting %s interactions with user." % interactions.count())
|
2012-07-28 18:33:07 -07:00
|
|
|
interactions.delete()
|
|
|
|
|
|
|
|
activities = MActivity.objects.filter(user_id=self.user.pk)
|
2013-01-03 10:33:22 -08:00
|
|
|
logging.user(self.user, "Deleting %s activities for user." % activities.count())
|
2012-07-28 18:33:07 -07:00
|
|
|
activities.delete()
|
|
|
|
|
|
|
|
activities = MActivity.objects.filter(with_user_id=self.user.pk)
|
2013-01-03 10:33:22 -08:00
|
|
|
logging.user(self.user, "Deleting %s activities with user." % activities.count())
|
2012-07-28 18:33:07 -07:00
|
|
|
activities.delete()
|
|
|
|
|
2013-07-11 15:55:37 -07:00
|
|
|
starred_stories = MStarredStory.objects.filter(user_id=self.user.pk)
|
|
|
|
logging.user(self.user, "Deleting %s starred stories." % starred_stories.count())
|
|
|
|
starred_stories.delete()
|
|
|
|
|
2013-01-03 10:33:22 -08:00
|
|
|
logging.user(self.user, "Deleting user: %s" % self.user)
|
2012-07-28 18:33:07 -07:00
|
|
|
self.user.delete()
|
2014-06-16 15:49:49 -07:00
|
|
|
|
2015-05-18 17:27:24 -07:00
|
|
|
def activate_premium(self, never_expire=False):
|
2012-07-05 22:20:49 -07:00
|
|
|
from apps.profile.tasks import EmailNewPremium
|
2016-03-20 17:55:44 -07:00
|
|
|
|
2012-07-05 22:20:49 -07:00
|
|
|
EmailNewPremium.delay(user_id=self.user.pk)
|
2011-10-26 20:09:28 -07:00
|
|
|
|
2010-10-23 11:20:54 -04:00
|
|
|
self.is_premium = True
|
|
|
|
self.save()
|
2013-05-13 18:03:17 -07:00
|
|
|
self.user.is_active = True
|
|
|
|
self.user.save()
|
2010-10-23 11:20:54 -04:00
|
|
|
|
2015-01-15 12:40:17 -08:00
|
|
|
subs = UserSubscription.objects.filter(user=self.user)
|
|
|
|
for sub in subs:
|
|
|
|
if sub.active: continue
|
|
|
|
sub.active = True
|
2015-01-15 12:34:29 -08:00
|
|
|
try:
|
2015-01-15 12:40:17 -08:00
|
|
|
sub.save()
|
|
|
|
except (IntegrityError, Feed.DoesNotExist):
|
|
|
|
pass
|
|
|
|
|
|
|
|
try:
|
|
|
|
scheduled_feeds = [sub.feed.pk for sub in subs]
|
|
|
|
except Feed.DoesNotExist:
|
|
|
|
scheduled_feeds = []
|
|
|
|
logging.user(self.user, "~SN~FMTasking the scheduling immediate premium setup of ~SB%s~SN feeds..." %
|
|
|
|
len(scheduled_feeds))
|
|
|
|
SchedulePremiumSetup.apply_async(kwargs=dict(feed_ids=scheduled_feeds))
|
2013-02-15 09:52:11 -08:00
|
|
|
|
2015-07-28 18:46:37 -07:00
|
|
|
UserSubscription.queue_new_feeds(self.user)
|
2012-12-05 13:10:11 -08:00
|
|
|
self.setup_premium_history()
|
2010-10-30 00:27:52 -04:00
|
|
|
|
2015-05-18 17:27:24 -07:00
|
|
|
if never_expire:
|
|
|
|
self.premium_expire = None
|
|
|
|
self.save()
|
|
|
|
|
2015-01-15 12:40:17 -08:00
|
|
|
logging.user(self.user, "~BY~SK~FW~SBNEW PREMIUM ACCOUNT! WOOHOO!!! ~FR%s subscriptions~SN!" % (subs.count()))
|
2013-05-10 12:05:24 -07:00
|
|
|
|
|
|
|
return True
|
2012-12-03 14:35:21 -08:00
|
|
|
|
2012-12-05 13:10:11 -08:00
|
|
|
def deactivate_premium(self):
|
|
|
|
self.is_premium = False
|
|
|
|
self.save()
|
|
|
|
|
|
|
|
subs = UserSubscription.objects.filter(user=self.user)
|
|
|
|
for sub in subs:
|
|
|
|
sub.active = False
|
|
|
|
try:
|
|
|
|
sub.save()
|
2015-05-18 14:45:00 -07:00
|
|
|
# Don't bother recalculating feed's subs, as it will do that on next fetch
|
|
|
|
# sub.feed.setup_feed_for_premium_subscribers()
|
2013-05-10 12:05:24 -07:00
|
|
|
except (IntegrityError, Feed.DoesNotExist):
|
2012-12-05 13:10:11 -08:00
|
|
|
pass
|
|
|
|
|
|
|
|
logging.user(self.user, "~BY~FW~SBBOO! Deactivating premium account: ~FR%s subscriptions~SN!" % (subs.count()))
|
|
|
|
|
2013-05-13 18:03:17 -07:00
|
|
|
def activate_free(self):
|
|
|
|
if self.user.is_active:
|
|
|
|
return
|
|
|
|
|
|
|
|
self.user.is_active = True
|
|
|
|
self.user.save()
|
|
|
|
self.send_new_user_queue_email()
|
|
|
|
|
2015-06-15 16:58:50 -07:00
|
|
|
def setup_premium_history(self, alt_email=None, check_premium=False, force_expiration=False):
|
2014-11-17 13:54:39 -08:00
|
|
|
paypal_payments = []
|
|
|
|
stripe_payments = []
|
2014-11-07 14:52:45 -08:00
|
|
|
existing_history = PaymentHistory.objects.filter(user=self.user,
|
2014-11-07 14:47:04 -08:00
|
|
|
payment_provider__in=['paypal', 'stripe'])
|
2012-12-03 16:02:59 -08:00
|
|
|
if existing_history.count():
|
2014-11-17 14:01:36 -08:00
|
|
|
logging.user(self.user, "~BY~SN~FRDeleting~FW existing history: ~SB%s payments" % existing_history.count())
|
2012-12-03 16:02:59 -08:00
|
|
|
existing_history.delete()
|
2012-12-03 14:35:21 -08:00
|
|
|
|
|
|
|
# Record Paypal payments
|
|
|
|
paypal_payments = PayPalIPN.objects.filter(custom=self.user.username,
|
2014-11-07 14:58:18 -08:00
|
|
|
payment_status='Completed',
|
2012-12-03 14:35:21 -08:00
|
|
|
txn_type='subscr_payment')
|
2012-12-03 16:02:59 -08:00
|
|
|
if not paypal_payments.count():
|
|
|
|
paypal_payments = PayPalIPN.objects.filter(payer_email=self.user.email,
|
2014-11-07 14:58:18 -08:00
|
|
|
payment_status='Completed',
|
2012-12-03 16:02:59 -08:00
|
|
|
txn_type='subscr_payment')
|
2014-04-07 15:20:47 -07:00
|
|
|
if alt_email and not paypal_payments.count():
|
|
|
|
paypal_payments = PayPalIPN.objects.filter(payer_email=alt_email,
|
2014-11-07 14:58:18 -08:00
|
|
|
payment_status='Completed',
|
2014-04-07 15:20:47 -07:00
|
|
|
txn_type='subscr_payment')
|
|
|
|
if paypal_payments.count():
|
|
|
|
# Make sure this doesn't happen again, so let's use Paypal's email.
|
|
|
|
self.user.email = alt_email
|
|
|
|
self.user.save()
|
2015-04-16 20:26:35 -07:00
|
|
|
seen_txn_ids = set()
|
2012-12-03 14:35:21 -08:00
|
|
|
for payment in paypal_payments:
|
2015-04-16 20:26:35 -07:00
|
|
|
if payment.txn_id in seen_txn_ids: continue
|
|
|
|
seen_txn_ids.add(payment.txn_id)
|
2012-12-03 14:35:21 -08:00
|
|
|
PaymentHistory.objects.create(user=self.user,
|
|
|
|
payment_date=payment.payment_date,
|
|
|
|
payment_amount=payment.payment_gross,
|
|
|
|
payment_provider='paypal')
|
2014-11-17 13:54:39 -08:00
|
|
|
|
2012-12-03 14:35:21 -08:00
|
|
|
# Record Stripe payments
|
|
|
|
if self.stripe_id:
|
|
|
|
stripe.api_key = settings.STRIPE_SECRET
|
|
|
|
stripe_customer = stripe.Customer.retrieve(self.stripe_id)
|
|
|
|
stripe_payments = stripe.Charge.all(customer=stripe_customer.id).data
|
2013-03-29 13:39:35 -07:00
|
|
|
|
2012-12-03 14:35:21 -08:00
|
|
|
for payment in stripe_payments:
|
|
|
|
created = datetime.datetime.fromtimestamp(payment.created)
|
2015-04-16 19:09:08 -07:00
|
|
|
if payment.status == 'failed': continue
|
2012-12-03 14:35:21 -08:00
|
|
|
PaymentHistory.objects.create(user=self.user,
|
|
|
|
payment_date=created,
|
|
|
|
payment_amount=payment.amount / 100.0,
|
|
|
|
payment_provider='stripe')
|
|
|
|
|
2013-12-20 12:45:28 -08:00
|
|
|
# Calculate payments in last year, then add together
|
2012-12-03 14:35:21 -08:00
|
|
|
payment_history = PaymentHistory.objects.filter(user=self.user)
|
2013-12-20 12:45:28 -08:00
|
|
|
last_year = datetime.datetime.now() - datetime.timedelta(days=364)
|
|
|
|
recent_payments_count = 0
|
|
|
|
oldest_recent_payment_date = None
|
2015-06-30 16:05:58 -07:00
|
|
|
free_lifetime_premium = False
|
2012-12-03 14:35:21 -08:00
|
|
|
for payment in payment_history:
|
2015-06-30 16:05:58 -07:00
|
|
|
if payment.payment_amount == 0:
|
|
|
|
free_lifetime_premium = True
|
2013-12-20 12:45:28 -08:00
|
|
|
if payment.payment_date > last_year:
|
|
|
|
recent_payments_count += 1
|
2013-12-20 12:46:09 -08:00
|
|
|
if not oldest_recent_payment_date or payment.payment_date < oldest_recent_payment_date:
|
2013-12-20 12:45:28 -08:00
|
|
|
oldest_recent_payment_date = payment.payment_date
|
2012-12-03 14:35:21 -08:00
|
|
|
|
2015-06-30 16:05:58 -07:00
|
|
|
if free_lifetime_premium:
|
|
|
|
self.premium_expire = None
|
|
|
|
self.save()
|
|
|
|
elif oldest_recent_payment_date:
|
2015-05-18 14:13:48 -07:00
|
|
|
new_premium_expire = (oldest_recent_payment_date +
|
|
|
|
datetime.timedelta(days=365*recent_payments_count))
|
|
|
|
# Only move premium expire forward, never earlier. Also set expiration if not premium.
|
2015-06-15 16:58:50 -07:00
|
|
|
if (force_expiration or
|
|
|
|
(check_premium and not self.premium_expire) or
|
2015-05-19 17:05:01 -07:00
|
|
|
(self.premium_expire and new_premium_expire > self.premium_expire)):
|
2015-05-18 14:13:48 -07:00
|
|
|
self.premium_expire = new_premium_expire
|
|
|
|
self.save()
|
2013-04-05 17:54:10 -07:00
|
|
|
|
2015-04-16 20:29:35 -07:00
|
|
|
logging.user(self.user, "~BY~SN~FWFound ~SB~FB%s paypal~FW~SN and ~SB~FC%s stripe~FW~SN payments (~SB%s payments expire: ~SN~FB%s~FW)" % (
|
2014-11-17 13:57:30 -08:00
|
|
|
len(paypal_payments), len(stripe_payments), len(payment_history), self.premium_expire))
|
2014-11-17 13:54:39 -08:00
|
|
|
|
2015-01-05 17:08:45 -08:00
|
|
|
if (check_premium and not self.is_premium and
|
|
|
|
(not self.premium_expire or self.premium_expire > datetime.datetime.now())):
|
2015-01-05 16:06:48 -08:00
|
|
|
self.activate_premium()
|
|
|
|
|
2013-07-04 11:29:41 -07:00
|
|
|
def refund_premium(self, partial=False):
|
2013-05-10 12:05:24 -07:00
|
|
|
refunded = False
|
|
|
|
|
2013-04-05 17:54:10 -07:00
|
|
|
if self.stripe_id:
|
|
|
|
stripe.api_key = settings.STRIPE_SECRET
|
|
|
|
stripe_customer = stripe.Customer.retrieve(self.stripe_id)
|
|
|
|
stripe_payments = stripe.Charge.all(customer=stripe_customer.id).data
|
2013-07-04 11:29:41 -07:00
|
|
|
if partial:
|
2013-07-04 11:34:09 -07:00
|
|
|
stripe_payments[0].refund(amount=1200)
|
2013-07-04 11:29:41 -07:00
|
|
|
refunded = 12
|
|
|
|
else:
|
|
|
|
stripe_payments[0].refund()
|
|
|
|
self.cancel_premium()
|
|
|
|
refunded = stripe_payments[0].amount/100
|
2013-05-23 16:28:39 -07:00
|
|
|
logging.user(self.user, "~FRRefunding stripe payment: $%s" % refunded)
|
|
|
|
else:
|
2014-09-24 17:00:09 -07:00
|
|
|
self.cancel_premium()
|
|
|
|
|
2013-05-23 16:28:39 -07:00
|
|
|
paypal_opts = {
|
|
|
|
'API_ENVIRONMENT': 'PRODUCTION',
|
|
|
|
'API_USERNAME': settings.PAYPAL_API_USERNAME,
|
|
|
|
'API_PASSWORD': settings.PAYPAL_API_PASSWORD,
|
|
|
|
'API_SIGNATURE': settings.PAYPAL_API_SIGNATURE,
|
2015-05-26 12:22:50 -07:00
|
|
|
'API_CA_CERTS': False,
|
2013-05-23 16:28:39 -07:00
|
|
|
}
|
|
|
|
paypal = PayPalInterface(**paypal_opts)
|
2015-04-16 19:16:20 -07:00
|
|
|
transactions = PayPalIPN.objects.filter(custom=self.user.username,
|
|
|
|
txn_type='subscr_payment'
|
|
|
|
).order_by('-payment_date')
|
|
|
|
if not transactions:
|
|
|
|
transactions = PayPalIPN.objects.filter(payer_email=self.user.email,
|
|
|
|
txn_type='subscr_payment'
|
|
|
|
).order_by('-payment_date')
|
|
|
|
if transactions:
|
|
|
|
transaction = transactions[0]
|
|
|
|
refund = paypal.refund_transaction(transaction.txn_id)
|
|
|
|
try:
|
|
|
|
refunded = int(float(refund.raw['TOTALREFUNDEDAMOUNT'][0]))
|
|
|
|
except KeyError:
|
|
|
|
refunded = int(transaction.payment_gross)
|
|
|
|
logging.user(self.user, "~FRRefunding paypal payment: $%s" % refunded)
|
|
|
|
else:
|
|
|
|
logging.user(self.user, "~FRCouldn't refund paypal payment: not found by username or email")
|
|
|
|
refunded = 0
|
|
|
|
|
2013-05-10 12:05:24 -07:00
|
|
|
|
|
|
|
return refunded
|
2013-04-05 17:54:10 -07:00
|
|
|
|
2013-03-13 15:46:51 -07:00
|
|
|
def cancel_premium(self):
|
2013-05-23 16:28:39 -07:00
|
|
|
paypal_cancel = self.cancel_premium_paypal()
|
|
|
|
stripe_cancel = self.cancel_premium_stripe()
|
2017-03-30 11:12:21 -07:00
|
|
|
return stripe_cancel or paypal_cancel
|
2013-03-13 15:46:51 -07:00
|
|
|
|
2016-03-20 17:55:44 -07:00
|
|
|
def cancel_premium_paypal(self, second_most_recent_only=False):
|
2013-05-23 16:28:39 -07:00
|
|
|
transactions = PayPalIPN.objects.filter(custom=self.user.username,
|
2016-03-20 17:55:44 -07:00
|
|
|
txn_type='subscr_signup').order_by('-subscr_date')
|
|
|
|
|
2013-05-23 16:28:39 -07:00
|
|
|
if not transactions:
|
|
|
|
return
|
|
|
|
|
|
|
|
paypal_opts = {
|
|
|
|
'API_ENVIRONMENT': 'PRODUCTION',
|
|
|
|
'API_USERNAME': settings.PAYPAL_API_USERNAME,
|
|
|
|
'API_PASSWORD': settings.PAYPAL_API_PASSWORD,
|
|
|
|
'API_SIGNATURE': settings.PAYPAL_API_SIGNATURE,
|
2015-05-26 12:22:50 -07:00
|
|
|
'API_CA_CERTS': False,
|
2013-05-23 16:28:39 -07:00
|
|
|
}
|
|
|
|
paypal = PayPalInterface(**paypal_opts)
|
2016-03-20 17:55:44 -07:00
|
|
|
if second_most_recent_only:
|
|
|
|
# Check if user has an active subscription. If so, cancel it because a new one came in.
|
|
|
|
if len(transactions) > 1:
|
|
|
|
transaction = transactions[1]
|
|
|
|
else:
|
|
|
|
return False
|
|
|
|
else:
|
|
|
|
transaction = transactions[0]
|
2013-05-23 16:28:39 -07:00
|
|
|
profileid = transaction.subscr_id
|
2014-02-18 17:34:46 -08:00
|
|
|
try:
|
|
|
|
paypal.manage_recurring_payments_profile_status(profileid=profileid, action='Cancel')
|
|
|
|
except PayPalAPIResponseError:
|
2016-03-20 17:55:44 -07:00
|
|
|
logging.user(self.user, "~FRUser ~SBalready~SN canceled Paypal subscription: %s" % profileid)
|
2014-02-18 17:34:46 -08:00
|
|
|
else:
|
2016-03-20 18:03:11 -07:00
|
|
|
if second_most_recent_only:
|
2016-03-20 18:04:36 -07:00
|
|
|
logging.user(self.user, "~FRCanceling ~BR~FWsecond-oldest~SB~FR Paypal subscription: %s" % profileid)
|
|
|
|
else:
|
2016-03-20 18:03:11 -07:00
|
|
|
logging.user(self.user, "~FRCanceling Paypal subscription: %s" % profileid)
|
2013-05-23 16:28:39 -07:00
|
|
|
|
|
|
|
return True
|
2013-03-13 15:46:51 -07:00
|
|
|
|
|
|
|
def cancel_premium_stripe(self):
|
|
|
|
if not self.stripe_id:
|
|
|
|
return
|
|
|
|
|
|
|
|
stripe.api_key = settings.STRIPE_SECRET
|
|
|
|
stripe_customer = stripe.Customer.retrieve(self.stripe_id)
|
2013-08-05 18:45:43 -07:00
|
|
|
try:
|
|
|
|
stripe_customer.cancel_subscription()
|
|
|
|
except stripe.InvalidRequestError:
|
|
|
|
logging.user(self.user, "~FRFailed to cancel Stripe subscription")
|
2013-04-05 17:54:10 -07:00
|
|
|
|
|
|
|
logging.user(self.user, "~FRCanceling Stripe subscription")
|
2010-10-23 11:20:54 -04:00
|
|
|
|
2013-03-13 15:46:51 -07:00
|
|
|
return True
|
2013-05-23 18:14:21 -07:00
|
|
|
|
2017-03-30 11:12:21 -07:00
|
|
|
@property
|
|
|
|
def latest_paypal_email(self):
|
|
|
|
ipn = PayPalIPN.objects.filter(custom=self.user.username)
|
|
|
|
if not len(ipn):
|
|
|
|
return
|
|
|
|
|
|
|
|
return ipn[0].payer_email
|
2017-11-10 15:12:54 -08:00
|
|
|
|
2017-11-15 17:26:27 -08:00
|
|
|
def activate_ios_premium(self, product_identifier, transaction_identifier, amount=36):
|
|
|
|
payments = PaymentHistory.objects.filter(user=self.user,
|
|
|
|
payment_identifier=transaction_identifier)
|
|
|
|
if len(payments):
|
|
|
|
# Already paid
|
|
|
|
return False
|
|
|
|
|
2017-11-10 15:12:54 -08:00
|
|
|
PaymentHistory.objects.create(user=self.user,
|
|
|
|
payment_date=datetime.datetime.now(),
|
|
|
|
payment_amount=amount,
|
2017-11-15 17:26:27 -08:00
|
|
|
payment_provider='ios-subscription',
|
|
|
|
payment_identifier=transaction_identifier)
|
|
|
|
|
|
|
|
if not self.is_premium:
|
|
|
|
self.activate_premium()
|
|
|
|
|
|
|
|
logging.user(self.user, "~FG~BBNew iOS premium subscription: $%s~FW" % product_identifier)
|
|
|
|
return True
|
2017-03-30 11:12:21 -07:00
|
|
|
|
2015-07-30 18:10:55 -07:00
|
|
|
@classmethod
|
2015-08-03 12:23:49 -07:00
|
|
|
def clear_dead_spammers(self, days=30, confirm=False):
|
2015-07-30 18:10:55 -07:00
|
|
|
users = User.objects.filter(date_joined__gte=datetime.datetime.now()-datetime.timedelta(days=days)).order_by('-date_joined')
|
|
|
|
usernames = set()
|
2015-08-26 10:58:05 -07:00
|
|
|
numerics = re.compile(r'[0-9]+')
|
2015-07-30 18:10:55 -07:00
|
|
|
for user in users:
|
2015-11-23 11:26:22 -08:00
|
|
|
opens = UserSubscription.objects.filter(user=user).aggregate(sum=Sum('feed_opens'))['sum']
|
|
|
|
reads = RUserStory.read_story_count(user.pk)
|
|
|
|
has_numbers = numerics.search(user.username)
|
|
|
|
if opens is None and not reads and has_numbers:
|
|
|
|
usernames.add(user.username)
|
2015-11-23 11:28:45 -08:00
|
|
|
print " ---> Numerics: %-20s %-30s %-6s %-6s" % (user.username, user.email, opens, reads)
|
2015-11-23 11:26:22 -08:00
|
|
|
elif not user.profile.last_seen_ip:
|
|
|
|
usernames.add(user.username)
|
2015-11-23 11:28:45 -08:00
|
|
|
print " ---> No IP: %-20s %-30s %-6s %-6s" % (user.username, user.email, opens, reads)
|
2015-07-30 18:10:55 -07:00
|
|
|
|
2015-10-14 19:26:56 -07:00
|
|
|
if not confirm: return usernames
|
2015-07-30 18:10:55 -07:00
|
|
|
|
|
|
|
for username in usernames:
|
|
|
|
u = User.objects.get(username=username)
|
|
|
|
u.profile.delete_user(confirm=True)
|
|
|
|
|
|
|
|
RNewUserQueue.user_count()
|
|
|
|
RNewUserQueue.activate_all()
|
|
|
|
|
2015-07-28 18:46:37 -07:00
|
|
|
@classmethod
|
2015-08-05 13:59:26 -07:00
|
|
|
def count_feed_subscribers(self, feed_id=None, user_id=None, verbose=True):
|
2015-07-28 18:46:37 -07:00
|
|
|
SUBSCRIBER_EXPIRE = datetime.datetime.now() - datetime.timedelta(days=settings.SUBSCRIBER_EXPIRE)
|
|
|
|
r = redis.Redis(connection_pool=settings.REDIS_FEED_SUB_POOL)
|
|
|
|
entire_feed_counted = False
|
|
|
|
|
2015-07-29 11:57:11 -07:00
|
|
|
if verbose:
|
2015-08-24 14:26:49 -07:00
|
|
|
feed = Feed.get_by_id(feed_id)
|
2017-03-31 19:52:24 -07:00
|
|
|
logging.debug(" ---> [%-30s] ~SN~FBCounting subscribers for feed:~SB~FM%s~SN~FB user:~SB~FM%s" % (feed.log_title[:30], feed_id, user_id))
|
2015-07-28 18:46:37 -07:00
|
|
|
|
|
|
|
if feed_id:
|
|
|
|
feed_ids = [feed_id]
|
|
|
|
elif user_id:
|
2015-07-28 19:56:06 -07:00
|
|
|
feed_ids = [us['feed_id'] for us in UserSubscription.objects.filter(user=user_id, active=True).values('feed_id')]
|
2015-07-28 18:46:37 -07:00
|
|
|
else:
|
|
|
|
assert False, "feed_id or user_id required"
|
2011-01-30 23:56:51 -05:00
|
|
|
|
2015-07-28 18:46:37 -07:00
|
|
|
if feed_id and not user_id:
|
|
|
|
entire_feed_counted = True
|
|
|
|
|
|
|
|
for feed_id in feed_ids:
|
|
|
|
total = 0
|
|
|
|
premium = 0
|
|
|
|
active = 0
|
|
|
|
active_premium = 0
|
|
|
|
key = 's:%s' % feed_id
|
|
|
|
premium_key = 'sp:%s' % feed_id
|
|
|
|
|
|
|
|
if user_id:
|
2015-07-28 19:56:06 -07:00
|
|
|
active = UserSubscription.objects.get(feed_id=feed_id, user_id=user_id).only('active').active
|
|
|
|
user_ids = dict([(user_id, active)])
|
2015-07-28 18:46:37 -07:00
|
|
|
else:
|
2015-07-28 19:56:06 -07:00
|
|
|
user_ids = dict([(us.user_id, us.active)
|
|
|
|
for us in UserSubscription.objects.filter(feed_id=feed_id).only('user', 'active')])
|
|
|
|
profiles = Profile.objects.filter(user_id__in=user_ids.keys()).values('user_id', 'last_seen_on', 'is_premium')
|
2015-07-28 18:46:37 -07:00
|
|
|
feed = Feed.get_by_id(feed_id)
|
2015-07-28 20:46:30 -07:00
|
|
|
|
|
|
|
if entire_feed_counted:
|
|
|
|
r.delete(key)
|
|
|
|
r.delete(premium_key)
|
|
|
|
|
2015-07-28 18:46:37 -07:00
|
|
|
for profiles_group in chunks(profiles, 20):
|
|
|
|
pipeline = r.pipeline()
|
|
|
|
for profile in profiles_group:
|
|
|
|
last_seen_on = int(profile['last_seen_on'].strftime('%s'))
|
2015-07-28 20:46:30 -07:00
|
|
|
muted_feed = not bool(user_ids[profile['user_id']])
|
|
|
|
if muted_feed:
|
|
|
|
last_seen_on = 0
|
2015-07-28 18:46:37 -07:00
|
|
|
pipeline.zadd(key, profile['user_id'], last_seen_on)
|
|
|
|
total += 1
|
|
|
|
if profile['is_premium']:
|
|
|
|
pipeline.zadd(premium_key, profile['user_id'], last_seen_on)
|
|
|
|
premium += 1
|
2015-07-28 19:04:06 -07:00
|
|
|
else:
|
|
|
|
pipeline.zrem(premium_key, profile['user_id'])
|
2015-07-28 20:46:30 -07:00
|
|
|
if profile['last_seen_on'] > SUBSCRIBER_EXPIRE and not muted_feed:
|
2015-07-28 18:46:37 -07:00
|
|
|
active += 1
|
|
|
|
if profile['is_premium']:
|
|
|
|
active_premium += 1
|
|
|
|
|
|
|
|
pipeline.execute()
|
|
|
|
|
|
|
|
if entire_feed_counted:
|
|
|
|
now = int(datetime.datetime.now().strftime('%s'))
|
|
|
|
r.zadd(key, -1, now)
|
2015-08-11 13:33:49 -07:00
|
|
|
r.expire(key, settings.SUBSCRIBER_EXPIRE*24*60*60)
|
2015-07-28 18:46:37 -07:00
|
|
|
r.zadd(premium_key, -1, now)
|
2015-08-11 13:33:49 -07:00
|
|
|
r.expire(premium_key, settings.SUBSCRIBER_EXPIRE*24*60*60)
|
2015-07-28 18:46:37 -07:00
|
|
|
|
|
|
|
logging.info(" ---> [%-30s] ~SN~FBCounting subscribers, storing in ~SBredis~SN: ~FMt:~SB~FM%s~SN a:~SB%s~SN p:~SB%s~SN ap:~SB%s" %
|
2017-03-31 19:52:24 -07:00
|
|
|
(feed.log_title[:30], total, active, premium, active_premium))
|
2015-07-28 19:04:06 -07:00
|
|
|
|
|
|
|
@classmethod
|
2015-07-28 19:10:19 -07:00
|
|
|
def count_all_feed_subscribers_for_user(self, user):
|
2015-07-28 19:04:06 -07:00
|
|
|
r = redis.Redis(connection_pool=settings.REDIS_FEED_SUB_POOL)
|
|
|
|
if not isinstance(user, User):
|
|
|
|
user = User.objects.get(pk=user)
|
|
|
|
|
2015-07-28 20:46:30 -07:00
|
|
|
active_feed_ids = [us['feed_id'] for us in UserSubscription.objects.filter(user=user.pk, active=True).values('feed_id')]
|
|
|
|
muted_feed_ids = [us['feed_id'] for us in UserSubscription.objects.filter(user=user.pk, active=False).values('feed_id')]
|
|
|
|
logging.user(user, "~SN~FBRefreshing user last_login_on for ~SB%s~SN/~SB%s subscriptions~SN" %
|
|
|
|
(len(active_feed_ids), len(muted_feed_ids)))
|
|
|
|
for feed_ids in [active_feed_ids, muted_feed_ids]:
|
|
|
|
for feeds_group in chunks(feed_ids, 20):
|
|
|
|
pipeline = r.pipeline()
|
|
|
|
for feed_id in feeds_group:
|
|
|
|
key = 's:%s' % feed_id
|
|
|
|
premium_key = 'sp:%s' % feed_id
|
2015-07-28 19:04:06 -07:00
|
|
|
|
2015-07-28 20:46:30 -07:00
|
|
|
last_seen_on = int(user.profile.last_seen_on.strftime('%s'))
|
|
|
|
if feed_ids is muted_feed_ids:
|
|
|
|
last_seen_on = 0
|
|
|
|
pipeline.zadd(key, user.pk, last_seen_on)
|
|
|
|
if user.profile.is_premium:
|
|
|
|
pipeline.zadd(premium_key, user.pk, last_seen_on)
|
|
|
|
else:
|
|
|
|
pipeline.zrem(premium_key, user.pk)
|
|
|
|
pipeline.execute()
|
2011-09-19 08:56:16 -07:00
|
|
|
|
2012-11-19 14:28:04 -08:00
|
|
|
def import_reader_starred_items(self, count=20):
|
|
|
|
importer = GoogleReaderImporter(self.user)
|
|
|
|
importer.import_starred_items(count=count)
|
2014-04-16 11:57:49 -07:00
|
|
|
|
2011-09-21 17:49:26 -07:00
|
|
|
def send_new_user_email(self):
|
|
|
|
if not self.user.email or not self.send_emails:
|
2011-09-19 08:56:16 -07:00
|
|
|
return
|
|
|
|
|
|
|
|
user = self.user
|
|
|
|
text = render_to_string('mail/email_new_account.txt', locals())
|
|
|
|
html = render_to_string('mail/email_new_account.xhtml', locals())
|
|
|
|
subject = "Welcome to NewsBlur, %s" % (self.user.username)
|
|
|
|
msg = EmailMultiAlternatives(subject, text,
|
|
|
|
from_email='NewsBlur <%s>' % settings.HELLO_EMAIL,
|
|
|
|
to=['%s <%s>' % (user, user.email)])
|
|
|
|
msg.attach_alternative(html, "text/html")
|
2011-10-28 10:29:11 -07:00
|
|
|
msg.send(fail_silently=True)
|
2011-09-23 18:13:16 -07:00
|
|
|
|
|
|
|
logging.user(self.user, "~BB~FM~SBSending email for new user: %s" % self.user.email)
|
2013-05-23 18:14:21 -07:00
|
|
|
|
2014-10-14 15:59:46 -07:00
|
|
|
def send_opml_export_email(self, reason=None, force=False):
|
2013-05-23 18:14:21 -07:00
|
|
|
if not self.user.email:
|
|
|
|
return
|
|
|
|
|
2014-10-14 15:59:46 -07:00
|
|
|
emails_sent = MSentEmail.objects.filter(receiver_user_id=self.user.pk,
|
|
|
|
email_type='opml_export')
|
|
|
|
day_ago = datetime.datetime.now() - datetime.timedelta(days=1)
|
|
|
|
for email in emails_sent:
|
|
|
|
if email.date_sent > day_ago and not force:
|
|
|
|
logging.user(self.user, "~SN~FMNot sending opml export email, already sent today.")
|
|
|
|
return
|
|
|
|
|
|
|
|
MSentEmail.record(receiver_user_id=self.user.pk, email_type='opml_export')
|
2013-05-23 18:14:21 -07:00
|
|
|
|
|
|
|
exporter = OPMLExporter(self.user)
|
|
|
|
opml = exporter.process()
|
2013-04-22 15:24:38 -07:00
|
|
|
|
2013-05-23 18:14:21 -07:00
|
|
|
params = {
|
|
|
|
'feed_count': UserSubscription.objects.filter(user=self.user).count(),
|
2014-10-14 15:59:46 -07:00
|
|
|
'reason': reason,
|
2013-05-23 18:14:21 -07:00
|
|
|
}
|
|
|
|
user = self.user
|
|
|
|
text = render_to_string('mail/email_opml_export.txt', params)
|
|
|
|
html = render_to_string('mail/email_opml_export.xhtml', params)
|
|
|
|
subject = "Backup OPML file of your NewsBlur sites"
|
|
|
|
filename= 'NewsBlur Subscriptions - %s.xml' % datetime.datetime.now().strftime('%Y-%m-%d')
|
|
|
|
msg = EmailMultiAlternatives(subject, text,
|
|
|
|
from_email='NewsBlur <%s>' % settings.HELLO_EMAIL,
|
|
|
|
to=['%s <%s>' % (user, user.email)])
|
|
|
|
msg.attach_alternative(html, "text/html")
|
|
|
|
msg.attach(filename, opml, 'text/xml')
|
|
|
|
msg.send(fail_silently=True)
|
|
|
|
|
|
|
|
logging.user(self.user, "~BB~FM~SBSending OPML backup email to: %s" % self.user.email)
|
|
|
|
|
2013-04-22 15:24:38 -07:00
|
|
|
def send_first_share_to_blurblog_email(self, force=False):
|
|
|
|
from apps.social.models import MSocialProfile, MSharedStory
|
|
|
|
|
|
|
|
if not self.user.email:
|
|
|
|
return
|
|
|
|
|
2015-07-20 16:44:50 -07:00
|
|
|
params = dict(receiver_user_id=self.user.pk, email_type='first_share')
|
|
|
|
try:
|
2017-05-18 16:59:35 -07:00
|
|
|
MSentEmail.objects.get(**params)
|
2015-07-20 16:44:50 -07:00
|
|
|
if not force:
|
|
|
|
# Return if email already sent
|
|
|
|
return
|
|
|
|
except MSentEmail.DoesNotExist:
|
2017-05-18 16:59:35 -07:00
|
|
|
MSentEmail.objects.create(**params)
|
2015-07-20 16:44:50 -07:00
|
|
|
|
2013-04-22 15:24:38 -07:00
|
|
|
social_profile = MSocialProfile.objects.get(user_id=self.user.pk)
|
|
|
|
params = {
|
|
|
|
'shared_stories': MSharedStory.objects.filter(user_id=self.user.pk).count(),
|
|
|
|
'blurblog_url': social_profile.blurblog_url,
|
|
|
|
'blurblog_rss': social_profile.blurblog_rss
|
|
|
|
}
|
|
|
|
user = self.user
|
|
|
|
text = render_to_string('mail/email_first_share_to_blurblog.txt', params)
|
|
|
|
html = render_to_string('mail/email_first_share_to_blurblog.xhtml', params)
|
|
|
|
subject = "Your shared stories on NewsBlur are available on your Blurblog"
|
|
|
|
msg = EmailMultiAlternatives(subject, text,
|
|
|
|
from_email='NewsBlur <%s>' % settings.HELLO_EMAIL,
|
|
|
|
to=['%s <%s>' % (user, user.email)])
|
|
|
|
msg.attach_alternative(html, "text/html")
|
|
|
|
msg.send(fail_silently=True)
|
|
|
|
|
|
|
|
logging.user(self.user, "~BB~FM~SBSending first share to blurblog email to: %s" % self.user.email)
|
2011-09-19 08:56:16 -07:00
|
|
|
|
2011-10-26 20:09:28 -07:00
|
|
|
def send_new_premium_email(self, force=False):
|
2017-05-18 16:59:35 -07:00
|
|
|
# subs = UserSubscription.objects.filter(user=self.user)
|
|
|
|
# message = """Woohoo!
|
|
|
|
#
|
|
|
|
# User: %(user)s
|
|
|
|
# Feeds: %(feeds)s
|
|
|
|
#
|
|
|
|
# Sincerely,
|
|
|
|
# NewsBlur""" % {'user': self.user.username, 'feeds': subs.count()}
|
2015-04-15 16:35:01 -07:00
|
|
|
# mail_admins('New premium account', message, fail_silently=True)
|
2012-07-05 22:20:49 -07:00
|
|
|
|
2011-09-21 17:49:26 -07:00
|
|
|
if not self.user.email or not self.send_emails:
|
2011-09-19 08:56:16 -07:00
|
|
|
return
|
|
|
|
|
2015-07-20 16:44:50 -07:00
|
|
|
params = dict(receiver_user_id=self.user.pk, email_type='new_premium')
|
|
|
|
try:
|
2017-05-18 16:59:35 -07:00
|
|
|
MSentEmail.objects.get(**params)
|
2015-07-20 16:44:50 -07:00
|
|
|
if not force:
|
|
|
|
# Return if email already sent
|
|
|
|
return
|
|
|
|
except MSentEmail.DoesNotExist:
|
2017-05-18 16:59:35 -07:00
|
|
|
MSentEmail.objects.create(**params)
|
2015-07-20 16:44:50 -07:00
|
|
|
|
2011-09-19 08:56:16 -07:00
|
|
|
user = self.user
|
|
|
|
text = render_to_string('mail/email_new_premium.txt', locals())
|
|
|
|
html = render_to_string('mail/email_new_premium.xhtml', locals())
|
|
|
|
subject = "Thanks for going premium on NewsBlur!"
|
|
|
|
msg = EmailMultiAlternatives(subject, text,
|
|
|
|
from_email='NewsBlur <%s>' % settings.HELLO_EMAIL,
|
|
|
|
to=['%s <%s>' % (user, user.email)])
|
|
|
|
msg.attach_alternative(html, "text/html")
|
2011-10-28 10:29:11 -07:00
|
|
|
msg.send(fail_silently=True)
|
2011-09-23 18:13:16 -07:00
|
|
|
|
|
|
|
logging.user(self.user, "~BB~FM~SBSending email for new premium: %s" % self.user.email)
|
2011-09-22 09:23:42 -07:00
|
|
|
|
|
|
|
def send_forgot_password_email(self, email=None):
|
|
|
|
if not self.user.email and not email:
|
|
|
|
print "Please provide an email address."
|
|
|
|
return
|
|
|
|
|
|
|
|
if not self.user.email and email:
|
|
|
|
self.user.email = email
|
|
|
|
self.user.save()
|
|
|
|
|
|
|
|
user = self.user
|
|
|
|
text = render_to_string('mail/email_forgot_password.txt', locals())
|
|
|
|
html = render_to_string('mail/email_forgot_password.xhtml', locals())
|
|
|
|
subject = "Forgot your password on NewsBlur?"
|
|
|
|
msg = EmailMultiAlternatives(subject, text,
|
|
|
|
from_email='NewsBlur <%s>' % settings.HELLO_EMAIL,
|
|
|
|
to=['%s <%s>' % (user, user.email)])
|
|
|
|
msg.attach_alternative(html, "text/html")
|
2011-10-28 10:29:11 -07:00
|
|
|
msg.send(fail_silently=True)
|
2011-09-22 09:23:42 -07:00
|
|
|
|
2011-09-23 18:13:16 -07:00
|
|
|
logging.user(self.user, "~BB~FM~SBSending email for forgotten password: %s" % self.user.email)
|
2013-05-13 18:03:17 -07:00
|
|
|
|
|
|
|
def send_new_user_queue_email(self, force=False):
|
|
|
|
if not self.user.email:
|
|
|
|
print "Please provide an email address."
|
|
|
|
return
|
2011-09-23 18:13:16 -07:00
|
|
|
|
2015-07-20 16:44:50 -07:00
|
|
|
params = dict(receiver_user_id=self.user.pk, email_type='new_user_queue')
|
|
|
|
try:
|
2017-05-18 16:59:35 -07:00
|
|
|
MSentEmail.objects.get(**params)
|
2015-07-20 16:44:50 -07:00
|
|
|
if not force:
|
|
|
|
# Return if email already sent
|
|
|
|
return
|
|
|
|
except MSentEmail.DoesNotExist:
|
2017-05-18 16:59:35 -07:00
|
|
|
MSentEmail.objects.create(**params)
|
2015-07-20 16:44:50 -07:00
|
|
|
|
2013-05-13 18:03:17 -07:00
|
|
|
user = self.user
|
|
|
|
text = render_to_string('mail/email_new_user_queue.txt', locals())
|
|
|
|
html = render_to_string('mail/email_new_user_queue.xhtml', locals())
|
|
|
|
subject = "Your free account is now ready to go on NewsBlur"
|
|
|
|
msg = EmailMultiAlternatives(subject, text,
|
|
|
|
from_email='NewsBlur <%s>' % settings.HELLO_EMAIL,
|
|
|
|
to=['%s <%s>' % (user, user.email)])
|
|
|
|
msg.attach_alternative(html, "text/html")
|
|
|
|
msg.send(fail_silently=True)
|
|
|
|
|
|
|
|
logging.user(self.user, "~BB~FM~SBSending email for new user queue: %s" % self.user.email)
|
|
|
|
|
2012-07-20 19:43:28 -07:00
|
|
|
def send_upload_opml_finished_email(self, feed_count):
|
|
|
|
if not self.user.email:
|
|
|
|
print "Please provide an email address."
|
|
|
|
return
|
|
|
|
|
|
|
|
user = self.user
|
|
|
|
text = render_to_string('mail/email_upload_opml_finished.txt', locals())
|
|
|
|
html = render_to_string('mail/email_upload_opml_finished.xhtml', locals())
|
|
|
|
subject = "Your OPML upload is complete. Get going with NewsBlur!"
|
|
|
|
msg = EmailMultiAlternatives(subject, text,
|
|
|
|
from_email='NewsBlur <%s>' % settings.HELLO_EMAIL,
|
|
|
|
to=['%s <%s>' % (user, user.email)])
|
|
|
|
msg.attach_alternative(html, "text/html")
|
|
|
|
msg.send()
|
|
|
|
|
|
|
|
logging.user(self.user, "~BB~FM~SBSending email for OPML upload: %s" % self.user.email)
|
2013-04-02 15:41:50 -07:00
|
|
|
|
|
|
|
def send_import_reader_finished_email(self, feed_count):
|
|
|
|
if not self.user.email:
|
|
|
|
print "Please provide an email address."
|
|
|
|
return
|
|
|
|
|
|
|
|
user = self.user
|
|
|
|
text = render_to_string('mail/email_import_reader_finished.txt', locals())
|
|
|
|
html = render_to_string('mail/email_import_reader_finished.xhtml', locals())
|
|
|
|
subject = "Your Google Reader import is complete. Get going with NewsBlur!"
|
|
|
|
msg = EmailMultiAlternatives(subject, text,
|
|
|
|
from_email='NewsBlur <%s>' % settings.HELLO_EMAIL,
|
|
|
|
to=['%s <%s>' % (user, user.email)])
|
|
|
|
msg.attach_alternative(html, "text/html")
|
|
|
|
msg.send()
|
|
|
|
|
|
|
|
logging.user(self.user, "~BB~FM~SBSending email for Google Reader import: %s" % self.user.email)
|
|
|
|
|
|
|
|
def send_import_reader_starred_finished_email(self, feed_count, starred_count):
|
|
|
|
if not self.user.email:
|
|
|
|
print "Please provide an email address."
|
|
|
|
return
|
|
|
|
|
|
|
|
user = self.user
|
|
|
|
text = render_to_string('mail/email_import_reader_starred_finished.txt', locals())
|
|
|
|
html = render_to_string('mail/email_import_reader_starred_finished.xhtml', locals())
|
|
|
|
subject = "Your Google Reader starred stories import is complete. Get going with NewsBlur!"
|
|
|
|
msg = EmailMultiAlternatives(subject, text,
|
|
|
|
from_email='NewsBlur <%s>' % settings.HELLO_EMAIL,
|
|
|
|
to=['%s <%s>' % (user, user.email)])
|
|
|
|
msg.attach_alternative(html, "text/html")
|
|
|
|
msg.send()
|
|
|
|
|
|
|
|
logging.user(self.user, "~BB~FM~SBSending email for Google Reader starred stories import: %s" % self.user.email)
|
2012-07-20 19:43:28 -07:00
|
|
|
|
2012-08-09 19:45:08 -07:00
|
|
|
def send_launch_social_email(self, force=False):
|
|
|
|
if not self.user.email or not self.send_emails:
|
2012-08-09 20:42:04 -07:00
|
|
|
logging.user(self.user, "~FM~SB~FRNot~FM sending launch social email for user, %s: %s" % (self.user.email and 'opt-out: ' or 'blank', self.user.email))
|
2012-08-09 19:45:08 -07:00
|
|
|
return
|
|
|
|
|
2015-07-20 16:44:50 -07:00
|
|
|
params = dict(receiver_user_id=self.user.pk, email_type='launch_social')
|
|
|
|
try:
|
2017-05-18 16:59:35 -07:00
|
|
|
MSentEmail.objects.get(**params)
|
2015-07-20 16:44:50 -07:00
|
|
|
if not force:
|
|
|
|
# Return if email already sent
|
|
|
|
logging.user(self.user, "~FM~SB~FRNot~FM sending launch social email for user, sent already: %s" % self.user.email)
|
|
|
|
return
|
|
|
|
except MSentEmail.DoesNotExist:
|
2017-05-18 16:59:35 -07:00
|
|
|
MSentEmail.objects.create(**params)
|
2012-08-09 19:45:08 -07:00
|
|
|
|
|
|
|
delta = datetime.datetime.now() - self.last_seen_on
|
|
|
|
months_ago = delta.days / 30
|
|
|
|
user = self.user
|
|
|
|
data = dict(user=user, months_ago=months_ago)
|
|
|
|
text = render_to_string('mail/email_launch_social.txt', data)
|
|
|
|
html = render_to_string('mail/email_launch_social.xhtml', data)
|
|
|
|
subject = "NewsBlur is now a social news reader"
|
|
|
|
msg = EmailMultiAlternatives(subject, text,
|
|
|
|
from_email='NewsBlur <%s>' % settings.HELLO_EMAIL,
|
|
|
|
to=['%s <%s>' % (user, user.email)])
|
|
|
|
msg.attach_alternative(html, "text/html")
|
|
|
|
msg.send(fail_silently=True)
|
|
|
|
|
2012-08-09 20:42:04 -07:00
|
|
|
logging.user(self.user, "~BB~FM~SBSending launch social email for user: %s months, %s" % (months_ago, self.user.email))
|
2015-05-18 14:13:48 -07:00
|
|
|
|
2016-09-30 23:49:15 -07:00
|
|
|
def send_launch_turntouch_email(self, force=False):
|
|
|
|
if not self.user.email or not self.send_emails:
|
|
|
|
logging.user(self.user, "~FM~SB~FRNot~FM sending launch TT email for user, %s: %s" % (self.user.email and 'opt-out: ' or 'blank', self.user.email))
|
|
|
|
return
|
|
|
|
|
|
|
|
params = dict(receiver_user_id=self.user.pk, email_type='launch_turntouch')
|
|
|
|
try:
|
2017-05-18 16:59:35 -07:00
|
|
|
MSentEmail.objects.get(**params)
|
2016-09-30 23:49:15 -07:00
|
|
|
if not force:
|
|
|
|
# Return if email already sent
|
|
|
|
logging.user(self.user, "~FM~SB~FRNot~FM sending launch social email for user, sent already: %s" % self.user.email)
|
|
|
|
return
|
|
|
|
except MSentEmail.DoesNotExist:
|
2017-05-18 16:59:35 -07:00
|
|
|
MSentEmail.objects.create(**params)
|
2016-09-30 23:49:15 -07:00
|
|
|
|
|
|
|
delta = datetime.datetime.now() - self.last_seen_on
|
|
|
|
months_ago = delta.days / 30
|
|
|
|
user = self.user
|
|
|
|
data = dict(user=user, months_ago=months_ago)
|
|
|
|
text = render_to_string('mail/email_launch_turntouch.txt', data)
|
|
|
|
html = render_to_string('mail/email_launch_turntouch.xhtml', data)
|
|
|
|
subject = "Introducing Turn Touch for NewsBlur"
|
|
|
|
msg = EmailMultiAlternatives(subject, text,
|
|
|
|
from_email='NewsBlur <%s>' % settings.HELLO_EMAIL,
|
|
|
|
to=['%s <%s>' % (user, user.email)])
|
|
|
|
msg.attach_alternative(html, "text/html")
|
|
|
|
msg.send(fail_silently=True)
|
|
|
|
|
|
|
|
logging.user(self.user, "~BB~FM~SBSending launch TT email for user: %s months, %s" % (months_ago, self.user.email))
|
2017-03-08 18:04:57 -08:00
|
|
|
|
|
|
|
def send_launch_turntouch_end_email(self, force=False):
|
|
|
|
if not self.user.email or not self.send_emails:
|
|
|
|
logging.user(self.user, "~FM~SB~FRNot~FM sending launch TT end email for user, %s: %s" % (self.user.email and 'opt-out: ' or 'blank', self.user.email))
|
|
|
|
return
|
|
|
|
|
|
|
|
params = dict(receiver_user_id=self.user.pk, email_type='launch_turntouch_end')
|
|
|
|
try:
|
2017-05-18 16:59:35 -07:00
|
|
|
MSentEmail.objects.get(**params)
|
2017-03-08 18:04:57 -08:00
|
|
|
if not force:
|
|
|
|
# Return if email already sent
|
|
|
|
logging.user(self.user, "~FM~SB~FRNot~FM sending launch TT end email for user, sent already: %s" % self.user.email)
|
|
|
|
return
|
|
|
|
except MSentEmail.DoesNotExist:
|
2017-05-18 16:59:35 -07:00
|
|
|
MSentEmail.objects.create(**params)
|
2017-03-08 18:04:57 -08:00
|
|
|
|
|
|
|
delta = datetime.datetime.now() - self.last_seen_on
|
|
|
|
months_ago = delta.days / 30
|
|
|
|
user = self.user
|
|
|
|
data = dict(user=user, months_ago=months_ago)
|
|
|
|
text = render_to_string('mail/email_launch_turntouch_end.txt', data)
|
|
|
|
html = render_to_string('mail/email_launch_turntouch_end.xhtml', data)
|
|
|
|
subject = "Last day to back Turn Touch: NewsBlur's beautiful remote"
|
|
|
|
msg = EmailMultiAlternatives(subject, text,
|
|
|
|
from_email='NewsBlur <%s>' % settings.HELLO_EMAIL,
|
|
|
|
to=['%s <%s>' % (user, user.email)])
|
|
|
|
msg.attach_alternative(html, "text/html")
|
|
|
|
msg.send(fail_silently=True)
|
|
|
|
|
|
|
|
logging.user(self.user, "~BB~FM~SBSending launch TT end email for user: %s months, %s" % (months_ago, self.user.email))
|
2016-09-30 23:49:15 -07:00
|
|
|
|
2015-05-18 14:13:48 -07:00
|
|
|
def grace_period_email_sent(self, force=False):
|
2012-12-05 11:56:55 -08:00
|
|
|
emails_sent = MSentEmail.objects.filter(receiver_user_id=self.user.pk,
|
|
|
|
email_type='premium_expire_grace')
|
|
|
|
day_ago = datetime.datetime.now() - datetime.timedelta(days=360)
|
|
|
|
for email in emails_sent:
|
2014-05-29 12:19:26 -07:00
|
|
|
if email.date_sent > day_ago and not force:
|
2013-01-07 17:43:05 -08:00
|
|
|
logging.user(self.user, "~SN~FMNot sending premium expire grace email, already sent before.")
|
2015-05-18 14:13:48 -07:00
|
|
|
return True
|
2012-12-05 11:56:55 -08:00
|
|
|
|
2015-05-18 14:13:48 -07:00
|
|
|
def send_premium_expire_grace_period_email(self, force=False):
|
|
|
|
if not self.user.email:
|
|
|
|
logging.user(self.user, "~FM~SB~FRNot~FM~SN sending premium expire grace for user: %s" % (self.user))
|
|
|
|
return
|
|
|
|
|
|
|
|
if self.grace_period_email_sent(force=force):
|
|
|
|
return
|
|
|
|
|
2017-09-18 08:34:49 -07:00
|
|
|
if self.premium_expire and self.premium_expire < datetime.datetime.now():
|
2015-05-18 14:13:48 -07:00
|
|
|
self.premium_expire = datetime.datetime.now()
|
2012-12-05 11:56:55 -08:00
|
|
|
self.save()
|
|
|
|
|
|
|
|
delta = datetime.datetime.now() - self.last_seen_on
|
|
|
|
months_ago = delta.days / 30
|
|
|
|
user = self.user
|
|
|
|
data = dict(user=user, months_ago=months_ago)
|
|
|
|
text = render_to_string('mail/email_premium_expire_grace.txt', data)
|
|
|
|
html = render_to_string('mail/email_premium_expire_grace.xhtml', data)
|
|
|
|
subject = "Your premium account on NewsBlur has one more month!"
|
|
|
|
msg = EmailMultiAlternatives(subject, text,
|
|
|
|
from_email='NewsBlur <%s>' % settings.HELLO_EMAIL,
|
|
|
|
to=['%s <%s>' % (user, user.email)])
|
|
|
|
msg.attach_alternative(html, "text/html")
|
|
|
|
msg.send(fail_silently=True)
|
|
|
|
|
|
|
|
MSentEmail.record(receiver_user_id=self.user.pk, email_type='premium_expire_grace')
|
|
|
|
logging.user(self.user, "~BB~FM~SBSending premium expire grace email for user: %s months, %s" % (months_ago, self.user.email))
|
|
|
|
|
|
|
|
def send_premium_expire_email(self, force=False):
|
|
|
|
if not self.user.email:
|
|
|
|
logging.user(self.user, "~FM~SB~FRNot~FM sending premium expire for user: %s" % (self.user))
|
|
|
|
return
|
|
|
|
|
|
|
|
emails_sent = MSentEmail.objects.filter(receiver_user_id=self.user.pk,
|
|
|
|
email_type='premium_expire')
|
|
|
|
day_ago = datetime.datetime.now() - datetime.timedelta(days=360)
|
|
|
|
for email in emails_sent:
|
2014-05-29 12:19:26 -07:00
|
|
|
if email.date_sent > day_ago and not force:
|
2013-01-07 17:43:05 -08:00
|
|
|
logging.user(self.user, "~FM~SBNot sending premium expire email, already sent before.")
|
2012-12-05 11:56:55 -08:00
|
|
|
return
|
|
|
|
|
|
|
|
delta = datetime.datetime.now() - self.last_seen_on
|
|
|
|
months_ago = delta.days / 30
|
|
|
|
user = self.user
|
|
|
|
data = dict(user=user, months_ago=months_ago)
|
|
|
|
text = render_to_string('mail/email_premium_expire.txt', data)
|
|
|
|
html = render_to_string('mail/email_premium_expire.xhtml', data)
|
|
|
|
subject = "Your premium account on NewsBlur has expired"
|
|
|
|
msg = EmailMultiAlternatives(subject, text,
|
|
|
|
from_email='NewsBlur <%s>' % settings.HELLO_EMAIL,
|
|
|
|
to=['%s <%s>' % (user, user.email)])
|
|
|
|
msg.attach_alternative(html, "text/html")
|
|
|
|
msg.send(fail_silently=True)
|
|
|
|
|
|
|
|
MSentEmail.record(receiver_user_id=self.user.pk, email_type='premium_expire')
|
|
|
|
logging.user(self.user, "~BB~FM~SBSending premium expire email for user: %s months, %s" % (months_ago, self.user.email))
|
2012-08-09 19:45:08 -07:00
|
|
|
|
2011-09-19 09:46:36 -07:00
|
|
|
def autologin_url(self, next=None):
|
|
|
|
return reverse('autologin', kwargs={
|
|
|
|
'username': self.user.username,
|
|
|
|
'secret': self.secret_token
|
|
|
|
}) + ('?' + next + '=1' if next else '')
|
|
|
|
|
2015-01-05 17:06:41 -08:00
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def doublecheck_paypal_payments(cls, days=14):
|
|
|
|
payments = PayPalIPN.objects.filter(txn_type='subscr_payment',
|
|
|
|
updated_at__gte=datetime.datetime.now()
|
|
|
|
- datetime.timedelta(days)
|
|
|
|
).order_by('-created_at')
|
|
|
|
for payment in payments:
|
|
|
|
try:
|
|
|
|
profile = Profile.objects.get(user__username=payment.custom)
|
|
|
|
except Profile.DoesNotExist:
|
|
|
|
logging.debug(" ---> ~FRCouldn't find user: ~SB~FC%s" % payment.custom)
|
|
|
|
continue
|
|
|
|
profile.setup_premium_history(check_premium=True)
|
|
|
|
|
2012-04-16 11:21:52 -07:00
|
|
|
|
2010-05-11 21:36:17 -04:00
|
|
|
def create_profile(sender, instance, created, **kwargs):
|
|
|
|
if created:
|
|
|
|
Profile.objects.create(user=instance)
|
2010-06-11 20:55:38 -04:00
|
|
|
else:
|
|
|
|
Profile.objects.get_or_create(user=instance)
|
2010-10-16 18:52:52 -04:00
|
|
|
post_save.connect(create_profile, sender=User)
|
|
|
|
|
2015-01-15 12:40:17 -08:00
|
|
|
|
|
|
|
def paypal_signup(sender, **kwargs):
|
2010-10-16 18:52:52 -04:00
|
|
|
ipn_obj = sender
|
2013-01-31 16:55:45 -08:00
|
|
|
try:
|
|
|
|
user = User.objects.get(username__iexact=ipn_obj.custom)
|
|
|
|
except User.DoesNotExist:
|
|
|
|
user = User.objects.get(email__iexact=ipn_obj.payer_email)
|
2015-01-15 12:40:17 -08:00
|
|
|
logging.user(user, "~BC~SB~FBPaypal subscription signup")
|
2011-10-19 09:40:31 -07:00
|
|
|
try:
|
|
|
|
if not user.email:
|
|
|
|
user.email = ipn_obj.payer_email
|
|
|
|
user.save()
|
2015-01-15 12:40:17 -08:00
|
|
|
except:
|
|
|
|
pass
|
|
|
|
user.profile.activate_premium()
|
2016-03-20 17:55:44 -07:00
|
|
|
user.profile.cancel_premium_stripe()
|
|
|
|
user.profile.cancel_premium_paypal(second_most_recent_only=True)
|
2015-01-15 12:40:17 -08:00
|
|
|
subscription_signup.connect(paypal_signup)
|
|
|
|
|
|
|
|
def paypal_payment_history_sync(sender, **kwargs):
|
|
|
|
ipn_obj = sender
|
|
|
|
try:
|
|
|
|
user = User.objects.get(username__iexact=ipn_obj.custom)
|
|
|
|
except User.DoesNotExist:
|
|
|
|
user = User.objects.get(email__iexact=ipn_obj.payer_email)
|
|
|
|
logging.user(user, "~BC~SB~FBPaypal subscription payment")
|
|
|
|
try:
|
2015-01-05 16:06:48 -08:00
|
|
|
user.profile.setup_premium_history(check_premium=True)
|
2012-12-03 15:17:35 -08:00
|
|
|
except:
|
|
|
|
return {"code": -1, "message": "User doesn't exist."}
|
2015-01-15 12:40:17 -08:00
|
|
|
payment_was_successful.connect(paypal_payment_history_sync)
|
2014-12-04 15:47:19 -08:00
|
|
|
|
2015-01-15 12:40:17 -08:00
|
|
|
def paypal_payment_was_flagged(sender, **kwargs):
|
2014-12-04 15:47:19 -08:00
|
|
|
ipn_obj = sender
|
|
|
|
try:
|
|
|
|
user = User.objects.get(username__iexact=ipn_obj.custom)
|
|
|
|
except User.DoesNotExist:
|
2015-02-09 13:51:06 -08:00
|
|
|
if ipn_obj.payer_email:
|
|
|
|
user = User.objects.get(email__iexact=ipn_obj.payer_email)
|
2014-12-04 15:47:19 -08:00
|
|
|
try:
|
2015-01-05 16:06:48 -08:00
|
|
|
user.profile.setup_premium_history(check_premium=True)
|
2015-02-19 10:50:06 -08:00
|
|
|
logging.user(user, "~BC~SB~FBPaypal subscription payment flagged")
|
2014-12-04 15:47:19 -08:00
|
|
|
except:
|
|
|
|
return {"code": -1, "message": "User doesn't exist."}
|
2015-01-15 12:40:17 -08:00
|
|
|
payment_was_flagged.connect(paypal_payment_was_flagged)
|
|
|
|
|
|
|
|
def paypal_recurring_payment_history_sync(sender, **kwargs):
|
|
|
|
ipn_obj = sender
|
|
|
|
try:
|
|
|
|
user = User.objects.get(username__iexact=ipn_obj.custom)
|
|
|
|
except User.DoesNotExist:
|
|
|
|
user = User.objects.get(email__iexact=ipn_obj.payer_email)
|
|
|
|
logging.user(user, "~BC~SB~FBPaypal subscription recurring payment")
|
|
|
|
try:
|
|
|
|
user.profile.setup_premium_history(check_premium=True)
|
|
|
|
except:
|
|
|
|
return {"code": -1, "message": "User doesn't exist."}
|
|
|
|
recurring_payment.connect(paypal_recurring_payment_history_sync)
|
2012-12-03 15:17:35 -08:00
|
|
|
|
2012-02-28 17:39:02 -08:00
|
|
|
def stripe_signup(sender, full_json, **kwargs):
|
2012-07-27 12:46:37 -07:00
|
|
|
stripe_id = full_json['data']['object']['customer']
|
|
|
|
try:
|
|
|
|
profile = Profile.objects.get(stripe_id=stripe_id)
|
2014-12-04 15:15:15 -08:00
|
|
|
logging.user(profile.user, "~BC~SB~FBStripe subscription signup")
|
2012-07-27 12:46:37 -07:00
|
|
|
profile.activate_premium()
|
2016-03-20 17:55:44 -07:00
|
|
|
profile.cancel_premium_paypal()
|
2012-07-27 12:46:37 -07:00
|
|
|
except Profile.DoesNotExist:
|
|
|
|
return {"code": -1, "message": "User doesn't exist."}
|
2012-02-27 21:46:34 -08:00
|
|
|
zebra_webhook_customer_subscription_created.connect(stripe_signup)
|
|
|
|
|
2012-12-03 15:17:35 -08:00
|
|
|
def stripe_payment_history_sync(sender, full_json, **kwargs):
|
|
|
|
stripe_id = full_json['data']['object']['customer']
|
|
|
|
try:
|
|
|
|
profile = Profile.objects.get(stripe_id=stripe_id)
|
2014-12-04 15:15:15 -08:00
|
|
|
logging.user(profile.user, "~BC~SB~FBStripe subscription payment")
|
2015-01-05 16:06:48 -08:00
|
|
|
profile.setup_premium_history(check_premium=True)
|
2012-12-03 15:17:35 -08:00
|
|
|
except Profile.DoesNotExist:
|
|
|
|
return {"code": -1, "message": "User doesn't exist."}
|
|
|
|
zebra_webhook_charge_succeeded.connect(stripe_payment_history_sync)
|
|
|
|
|
2013-05-06 15:12:18 -07:00
|
|
|
def change_password(user, old_password, new_password, only_check=False):
|
2010-11-08 12:09:55 -05:00
|
|
|
user_db = authenticate(username=user.username, password=old_password)
|
|
|
|
if user_db is None:
|
2013-03-20 18:36:15 -07:00
|
|
|
blank = blank_authenticate(user.username)
|
2013-05-06 15:12:18 -07:00
|
|
|
if blank and not only_check:
|
|
|
|
user.set_password(new_password or user.username)
|
2013-03-20 18:36:15 -07:00
|
|
|
user.save()
|
|
|
|
if user_db is None:
|
|
|
|
user_db = authenticate(username=user.username, password=user.username)
|
|
|
|
|
|
|
|
if not user_db:
|
2010-11-08 12:09:55 -05:00
|
|
|
return -1
|
|
|
|
else:
|
2013-05-06 15:12:18 -07:00
|
|
|
if not only_check:
|
|
|
|
user_db.set_password(new_password)
|
|
|
|
user_db.save()
|
2012-07-09 13:55:29 -07:00
|
|
|
return 1
|
2013-04-05 19:23:42 -07:00
|
|
|
|
2013-03-20 18:36:15 -07:00
|
|
|
def blank_authenticate(username, password=""):
|
|
|
|
try:
|
2013-05-06 15:12:18 -07:00
|
|
|
user = User.objects.get(username__iexact=username)
|
2013-03-20 18:36:15 -07:00
|
|
|
except User.DoesNotExist:
|
|
|
|
return
|
2012-07-09 13:55:29 -07:00
|
|
|
|
2013-03-20 18:36:15 -07:00
|
|
|
if user.password == "!":
|
|
|
|
return user
|
|
|
|
|
|
|
|
algorithm, salt, hash = user.password.split('$', 2)
|
2013-04-05 19:23:42 -07:00
|
|
|
encoded_blank = hashlib.sha1(salt + password).hexdigest()
|
|
|
|
encoded_username = authenticate(username=username, password=username)
|
|
|
|
if encoded_blank == hash or encoded_username == user:
|
2013-03-20 18:36:15 -07:00
|
|
|
return user
|
2016-09-30 23:49:15 -07:00
|
|
|
|
|
|
|
# Unfinished
|
|
|
|
class MEmailUnsubscribe(mongo.Document):
|
|
|
|
user_id = mongo.IntField()
|
|
|
|
email_type = mongo.StringField()
|
|
|
|
date = mongo.DateTimeField(default=datetime.datetime.now)
|
|
|
|
|
|
|
|
EMAIL_TYPE_FOLLOWS = 'follows'
|
|
|
|
EMAIL_TYPE_REPLIES = 'replies'
|
|
|
|
EMAIL_TYOE_PRODUCT = 'product'
|
|
|
|
|
|
|
|
meta = {
|
|
|
|
'collection': 'email_unsubscribes',
|
|
|
|
'allow_inheritance': False,
|
|
|
|
'indexes': ['user_id',
|
|
|
|
{'fields': ['user_id', 'email_type'],
|
|
|
|
'unique': True,
|
|
|
|
'types': False}],
|
|
|
|
}
|
|
|
|
|
|
|
|
def __unicode__(self):
|
|
|
|
return "%s unsubscribed from %s on %s" % (self.user_id, self.email_type, self.date)
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def user(cls, user_id):
|
|
|
|
unsubs = cls.objects(user_id=user_id)
|
|
|
|
return unsubs
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def unsubscribe(cls, user_id, email_type):
|
|
|
|
cls.objects.create()
|
|
|
|
|
|
|
|
|
2012-07-09 13:55:29 -07:00
|
|
|
class MSentEmail(mongo.Document):
|
|
|
|
sending_user_id = mongo.IntField()
|
|
|
|
receiver_user_id = mongo.IntField()
|
|
|
|
email_type = mongo.StringField()
|
|
|
|
date_sent = mongo.DateTimeField(default=datetime.datetime.now)
|
|
|
|
|
|
|
|
meta = {
|
|
|
|
'collection': 'sent_emails',
|
|
|
|
'allow_inheritance': False,
|
2012-08-09 19:45:08 -07:00
|
|
|
'indexes': ['sending_user_id', 'receiver_user_id', 'email_type'],
|
2012-07-09 13:55:29 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
def __unicode__(self):
|
|
|
|
return "%s sent %s email to %s" % (self.sending_user_id, self.email_type, self.receiver_user_id)
|
|
|
|
|
|
|
|
@classmethod
|
2012-12-05 13:10:11 -08:00
|
|
|
def record(cls, email_type, receiver_user_id, sending_user_id=None):
|
2012-07-09 13:55:29 -07:00
|
|
|
cls.objects.create(email_type=email_type,
|
|
|
|
receiver_user_id=receiver_user_id,
|
|
|
|
sending_user_id=sending_user_id)
|
2012-12-03 14:35:21 -08:00
|
|
|
|
|
|
|
class PaymentHistory(models.Model):
|
|
|
|
user = models.ForeignKey(User, related_name='payments')
|
2012-12-03 16:12:13 -08:00
|
|
|
payment_date = models.DateTimeField()
|
2012-12-03 14:35:21 -08:00
|
|
|
payment_amount = models.IntegerField()
|
|
|
|
payment_provider = models.CharField(max_length=20)
|
2017-11-15 17:45:38 -08:00
|
|
|
payment_identifier = models.CharField(max_length=100, null=True)
|
2012-12-03 14:35:21 -08:00
|
|
|
|
2013-01-31 16:55:45 -08:00
|
|
|
def __unicode__(self):
|
|
|
|
return "[%s] $%s/%s" % (self.payment_date.strftime("%Y-%m-%d"), self.payment_amount,
|
|
|
|
self.payment_provider)
|
2012-12-03 14:35:21 -08:00
|
|
|
class Meta:
|
2012-12-03 15:03:47 -08:00
|
|
|
ordering = ['-payment_date']
|
|
|
|
|
2013-06-12 13:52:43 -07:00
|
|
|
def canonical(self):
|
2012-12-03 15:03:47 -08:00
|
|
|
return {
|
|
|
|
'payment_date': self.payment_date.strftime('%Y-%m-%d'),
|
|
|
|
'payment_amount': self.payment_amount,
|
|
|
|
'payment_provider': self.payment_provider,
|
2013-02-15 13:47:45 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
@classmethod
|
2016-04-18 14:58:49 -07:00
|
|
|
def report(cls, months=26):
|
2015-04-16 21:15:30 -07:00
|
|
|
def _counter(start_date, end_date):
|
2014-11-14 10:32:03 -08:00
|
|
|
payments = PaymentHistory.objects.filter(payment_date__gte=start_date, payment_date__lte=end_date)
|
2013-02-15 13:47:45 -08:00
|
|
|
payments = payments.aggregate(avg=Avg('payment_amount'),
|
|
|
|
sum=Sum('payment_amount'),
|
|
|
|
count=Count('user'))
|
2014-11-14 10:32:03 -08:00
|
|
|
print "%s-%02d-%02d - %s-%02d-%02d:\t$%.2f\t$%-6s\t%-4s" % (
|
|
|
|
start_date.year, start_date.month, start_date.day,
|
|
|
|
end_date.year, end_date.month, end_date.day,
|
2016-04-18 14:58:49 -07:00
|
|
|
round(payments['avg'] if payments['avg'] else 0, 2), payments['sum'] if payments['sum'] else 0, payments['count'])
|
2015-04-16 22:29:36 -07:00
|
|
|
return payments['sum']
|
2013-05-13 16:17:49 -07:00
|
|
|
|
2015-04-16 22:29:36 -07:00
|
|
|
print "\nMonthly Totals:"
|
|
|
|
month_totals = {}
|
2015-04-16 21:15:30 -07:00
|
|
|
for m in reversed(range(months)):
|
|
|
|
now = datetime.datetime.now()
|
|
|
|
start_date = datetime.datetime(now.year, now.month, 1) - dateutil.relativedelta.relativedelta(months=m)
|
|
|
|
end_time = start_date + datetime.timedelta(days=31)
|
|
|
|
end_date = datetime.datetime(end_time.year, end_time.month, 1) - datetime.timedelta(seconds=1)
|
2015-04-16 22:29:36 -07:00
|
|
|
total = _counter(start_date, end_date)
|
|
|
|
month_totals[start_date.strftime("%Y-%m")] = total
|
|
|
|
|
2016-05-05 19:09:09 +08:00
|
|
|
print "\nCurrent Month Totals:"
|
|
|
|
month_totals = {}
|
|
|
|
years = datetime.datetime.now().year - 2009
|
|
|
|
for y in reversed(range(years)):
|
|
|
|
now = datetime.datetime.now()
|
|
|
|
start_date = datetime.datetime(now.year, now.month, 1) - dateutil.relativedelta.relativedelta(years=y)
|
|
|
|
end_time = start_date + datetime.timedelta(days=31)
|
|
|
|
end_date = datetime.datetime(end_time.year, end_time.month, 1) - datetime.timedelta(seconds=1)
|
|
|
|
if end_date > now: end_date = now
|
|
|
|
month_totals[start_date.strftime("%Y-%m")] = _counter(start_date, end_date)
|
|
|
|
|
2016-05-05 19:05:06 +08:00
|
|
|
print "\nMTD Totals:"
|
|
|
|
month_totals = {}
|
|
|
|
years = datetime.datetime.now().year - 2009
|
|
|
|
for y in reversed(range(years)):
|
|
|
|
now = datetime.datetime.now()
|
|
|
|
start_date = datetime.datetime(now.year, now.month, 1) - dateutil.relativedelta.relativedelta(years=y)
|
|
|
|
end_date = now - dateutil.relativedelta.relativedelta(years=y)
|
|
|
|
if end_date > now: end_date = now
|
|
|
|
month_totals[start_date.strftime("%Y-%m")] = _counter(start_date, end_date)
|
|
|
|
|
2015-04-16 22:29:36 -07:00
|
|
|
print "\nYearly Totals:"
|
|
|
|
year_totals = {}
|
2015-04-16 21:15:30 -07:00
|
|
|
years = datetime.datetime.now().year - 2009
|
|
|
|
for y in reversed(range(years)):
|
|
|
|
now = datetime.datetime.now()
|
|
|
|
start_date = datetime.datetime(now.year, 1, 1) - dateutil.relativedelta.relativedelta(years=y)
|
2016-04-18 14:58:49 -07:00
|
|
|
end_date = datetime.datetime(now.year, 1, 1) - dateutil.relativedelta.relativedelta(years=y-1) - datetime.timedelta(seconds=1)
|
|
|
|
if end_date > now: end_date = now
|
|
|
|
year_totals[now.year - y] = _counter(start_date, end_date)
|
|
|
|
|
|
|
|
print "\nYTD Totals:"
|
|
|
|
year_totals = {}
|
|
|
|
years = datetime.datetime.now().year - 2009
|
|
|
|
for y in reversed(range(years)):
|
|
|
|
now = datetime.datetime.now()
|
|
|
|
start_date = datetime.datetime(now.year, 1, 1) - dateutil.relativedelta.relativedelta(years=y)
|
|
|
|
end_date = now - dateutil.relativedelta.relativedelta(years=y)
|
2015-04-16 22:29:36 -07:00
|
|
|
if end_date > now: end_date = now
|
|
|
|
year_totals[now.year - y] = _counter(start_date, end_date)
|
2015-04-16 21:15:30 -07:00
|
|
|
|
|
|
|
total = cls.objects.all().aggregate(sum=Sum('payment_amount'))
|
2015-04-16 22:29:36 -07:00
|
|
|
print "\nTotal: $%s" % total['sum']
|
2014-11-06 14:55:27 -08:00
|
|
|
|
2015-06-30 16:05:58 -07:00
|
|
|
|
|
|
|
class MGiftCode(mongo.Document):
|
|
|
|
gifting_user_id = mongo.IntField()
|
|
|
|
receiving_user_id = mongo.IntField()
|
|
|
|
gift_code = mongo.StringField(max_length=12)
|
|
|
|
duration_days = mongo.IntField()
|
|
|
|
payment_amount = mongo.IntField()
|
|
|
|
created_date = mongo.DateTimeField(default=datetime.datetime.now)
|
|
|
|
|
|
|
|
meta = {
|
|
|
|
'collection': 'gift_codes',
|
|
|
|
'allow_inheritance': False,
|
|
|
|
'indexes': ['gifting_user_id', 'receiving_user_id', 'created_date'],
|
|
|
|
}
|
|
|
|
|
|
|
|
def __unicode__(self):
|
|
|
|
return "%s gifted %s on %s: %s (redeemed %s times)" % (self.gifting_user_id, self.receiving_user_id, self.created_date, self.gift_code, self.redeemed)
|
|
|
|
|
|
|
|
@property
|
|
|
|
def redeemed(self):
|
|
|
|
redeemed_code = MRedeemedCode.objects.filter(gift_code=self.gift_code)
|
|
|
|
return len(redeemed_code)
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def create_code(gift_code=None):
|
|
|
|
u = unicode(uuid.uuid4())
|
|
|
|
code = u[:8] + u[9:13]
|
|
|
|
if gift_code:
|
|
|
|
code = gift_code + code[len(gift_code):]
|
|
|
|
return code
|
|
|
|
|
|
|
|
@classmethod
|
2015-06-30 18:17:20 -07:00
|
|
|
def add(cls, gift_code=None, duration=0, gifting_user_id=None, receiving_user_id=None, payment=0):
|
2015-06-30 16:05:58 -07:00
|
|
|
return cls.objects.create(gift_code=cls.create_code(gift_code),
|
|
|
|
gifting_user_id=gifting_user_id,
|
|
|
|
receiving_user_id=receiving_user_id,
|
|
|
|
duration_days=duration,
|
|
|
|
payment_amount=payment)
|
|
|
|
|
|
|
|
|
2014-11-06 14:55:27 -08:00
|
|
|
class MRedeemedCode(mongo.Document):
|
|
|
|
user_id = mongo.IntField()
|
|
|
|
gift_code = mongo.StringField()
|
|
|
|
redeemed_date = mongo.DateTimeField(default=datetime.datetime.now)
|
|
|
|
|
|
|
|
meta = {
|
|
|
|
'collection': 'redeemed_codes',
|
|
|
|
'allow_inheritance': False,
|
|
|
|
'indexes': ['user_id', 'gift_code', 'redeemed_date'],
|
|
|
|
}
|
|
|
|
|
|
|
|
def __unicode__(self):
|
|
|
|
return "%s redeemed %s on %s" % (self.user_id, self.gift_code, self.redeemed_date)
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def record(cls, user_id, gift_code):
|
|
|
|
cls.objects.create(user_id=user_id,
|
|
|
|
gift_code=gift_code)
|
2015-06-30 16:05:58 -07:00
|
|
|
@classmethod
|
|
|
|
def redeem(cls, user, gift_code):
|
|
|
|
newsblur_gift_code = MGiftCode.objects.filter(gift_code__iexact=gift_code)
|
|
|
|
if newsblur_gift_code:
|
|
|
|
newsblur_gift_code = newsblur_gift_code[0]
|
|
|
|
PaymentHistory.objects.create(user=user,
|
|
|
|
payment_date=datetime.datetime.now(),
|
|
|
|
payment_amount=newsblur_gift_code.payment_amount,
|
|
|
|
payment_provider='newsblur-gift')
|
|
|
|
|
|
|
|
else:
|
|
|
|
# Thinkup / Good Web Bundle
|
|
|
|
PaymentHistory.objects.create(user=user,
|
|
|
|
payment_date=datetime.datetime.now(),
|
|
|
|
payment_amount=12,
|
|
|
|
payment_provider='good-web-bundle')
|
|
|
|
cls.record(user.pk, gift_code)
|
|
|
|
user.profile.activate_premium()
|
|
|
|
logging.user(user, "~FG~BBRedeeming gift code: %s~FW" % gift_code)
|
|
|
|
|
2013-05-13 16:17:49 -07:00
|
|
|
|
2017-05-18 16:59:35 -07:00
|
|
|
class MCustomStyling(mongo.Document):
|
|
|
|
user_id = mongo.IntField(unique=True)
|
|
|
|
custom_css = mongo.StringField()
|
|
|
|
custom_js = mongo.StringField()
|
|
|
|
updated_date = mongo.DateTimeField(default=datetime.datetime.now)
|
|
|
|
|
|
|
|
meta = {
|
|
|
|
'collection': 'custom_styling',
|
|
|
|
'allow_inheritance': False,
|
|
|
|
'indexes': ['user_id'],
|
|
|
|
}
|
|
|
|
|
|
|
|
def __unicode__(self):
|
|
|
|
return "%s custom style %s/%s %s" % (self.user_id, len(self.custom_css) if self.custom_css else "-",
|
|
|
|
len(self.custom_js) if self.custom_js else "-", self.updated_date)
|
|
|
|
|
|
|
|
def canonical(self):
|
|
|
|
return {
|
|
|
|
'css': self.custom_css,
|
|
|
|
'js': self.custom_js,
|
|
|
|
}
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def get_user(cls, user_id):
|
|
|
|
try:
|
|
|
|
styling = cls.objects.get(user_id=user_id)
|
|
|
|
except cls.DoesNotExist:
|
|
|
|
return None
|
|
|
|
|
|
|
|
return styling
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def save_user(cls, user_id, css, js):
|
|
|
|
styling = cls.get_user(user_id)
|
|
|
|
if not css and not js:
|
|
|
|
if styling:
|
|
|
|
styling.delete()
|
|
|
|
return
|
|
|
|
|
|
|
|
if not styling:
|
|
|
|
styling = cls.objects.create(user_id=user_id)
|
|
|
|
|
|
|
|
styling.custom_css = css
|
|
|
|
styling.custom_js = js
|
|
|
|
styling.save()
|
|
|
|
|
2013-05-13 16:17:49 -07:00
|
|
|
class RNewUserQueue:
|
|
|
|
|
|
|
|
KEY = "new_user_queue"
|
|
|
|
|
2013-05-13 18:03:17 -07:00
|
|
|
@classmethod
|
|
|
|
def activate_next(cls):
|
|
|
|
count = cls.user_count()
|
|
|
|
if not count:
|
|
|
|
return
|
|
|
|
|
|
|
|
user_id = cls.pop_user()
|
2014-03-13 16:47:31 -07:00
|
|
|
try:
|
|
|
|
user = User.objects.get(pk=user_id)
|
2015-03-09 15:32:05 -07:00
|
|
|
except User.DoesNotExist:
|
2014-03-13 16:47:31 -07:00
|
|
|
logging.debug("~FRCan't activate free account, can't find user ~SB%s~SN. ~FB%s still in queue." % (user_id, count-1))
|
|
|
|
return
|
|
|
|
|
2015-11-23 11:20:36 -08:00
|
|
|
logging.user(user, "~FBActivating free account (%s / %s). %s still in queue." % (user.email, user.profile.last_seen_ip, (count-1)))
|
2013-05-13 18:03:17 -07:00
|
|
|
|
|
|
|
user.profile.activate_free()
|
2015-06-22 11:03:57 -07:00
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def activate_all(cls):
|
|
|
|
count = cls.user_count()
|
|
|
|
if not count:
|
|
|
|
logging.debug("~FBNo users to activate, sleeping...")
|
|
|
|
return
|
|
|
|
|
|
|
|
for i in range(count):
|
|
|
|
cls.activate_next()
|
2013-05-13 18:03:17 -07:00
|
|
|
|
2013-05-13 16:17:49 -07:00
|
|
|
@classmethod
|
|
|
|
def add_user(cls, user_id):
|
2015-07-27 18:35:25 -07:00
|
|
|
r = redis.Redis(connection_pool=settings.REDIS_FEED_UPDATE_POOL)
|
2013-05-13 16:17:49 -07:00
|
|
|
now = time.time()
|
|
|
|
|
|
|
|
r.zadd(cls.KEY, user_id, now)
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def user_count(cls):
|
2015-07-27 18:35:25 -07:00
|
|
|
r = redis.Redis(connection_pool=settings.REDIS_FEED_UPDATE_POOL)
|
2013-05-13 16:17:49 -07:00
|
|
|
count = r.zcard(cls.KEY)
|
|
|
|
|
|
|
|
return count
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def user_position(cls, user_id):
|
2015-07-27 18:35:25 -07:00
|
|
|
r = redis.Redis(connection_pool=settings.REDIS_FEED_UPDATE_POOL)
|
2013-05-13 18:03:17 -07:00
|
|
|
position = r.zrank(cls.KEY, user_id)
|
|
|
|
if position >= 0:
|
|
|
|
return position + 1
|
2013-05-13 16:17:49 -07:00
|
|
|
|
|
|
|
@classmethod
|
2013-05-13 18:03:17 -07:00
|
|
|
def pop_user(cls):
|
2015-07-27 18:35:25 -07:00
|
|
|
r = redis.Redis(connection_pool=settings.REDIS_FEED_UPDATE_POOL)
|
2013-05-13 18:03:17 -07:00
|
|
|
user = r.zrange(cls.KEY, 0, 0)[0]
|
2013-05-13 16:17:49 -07:00
|
|
|
r.zrem(cls.KEY, user)
|
2013-05-13 18:03:17 -07:00
|
|
|
|
2013-05-13 16:17:49 -07:00
|
|
|
return user
|
2013-07-05 09:31:49 +02:00
|
|
|
|