2013-05-13 16:17:49 -07:00
import time
2010-06-30 13:36:51 -04:00
import datetime
2022-04-06 15:56:13 -04:00
from wsgiref . util import application_uri
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
2022-02-08 12:49:40 -05:00
import paypalrestsdk
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
2022-02-08 12:49:40 -05:00
from django . db . models import Q
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
2022-02-16 09:20:43 -05:00
from django . contrib . sites . models import Site
2011-09-19 08:56:16 -07:00
from django . core . mail import EmailMultiAlternatives
2020-06-17 00:17:32 -04:00
from django . urls 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
2020-06-15 17:53:35 -04:00
from apps . feed_import . models import 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
2020-06-24 16:59:43 -04:00
from paypal . standard . ipn . signals import valid_ipn_received , invalid_ipn_received
from paypal . standard . ipn . models import PayPalIPN
2012-02-28 17:37:01 -08:00
from zebra . signals import zebra_webhook_customer_subscription_created
2022-01-26 15:51:26 -05:00
from zebra . signals import zebra_webhook_customer_subscription_updated
2012-12-03 15:17:35 -08:00
from zebra . signals import zebra_webhook_charge_succeeded
2022-01-26 15:51:26 -05:00
from zebra . signals import zebra_webhook_charge_refunded
2022-01-24 17:23:25 -05:00
from zebra . signals import zebra_webhook_checkout_session_completed
2011-09-19 08:56:16 -07:00
2010-05-11 21:36:17 -04:00
class Profile ( models . Model ) :
2020-06-11 05:45:48 -04:00
user = models . OneToOneField ( User , unique = True , related_name = " profile " , on_delete = models . CASCADE )
2011-05-12 18:15:15 -04:00
is_premium = models . BooleanField ( default = False )
2022-01-11 10:59:45 -05:00
is_archive = models . BooleanField ( default = False , blank = True , null = True )
2022-01-10 16:07:49 -05:00
is_pro = models . BooleanField ( default = False , blank = True , null = True )
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 = " [] " )
2022-03-16 15:28:52 -04:00
feed_pane_size = models . IntegerField ( default = 282 )
2022-02-16 15:46:44 -05:00
days_of_unread = models . IntegerField ( default = settings . DAYS_OF_UNREAD , blank = True , null = True )
2012-03-20 11:15:40 -07:00
tutorial_finished = models . BooleanField ( default = False )
2020-12-09 17:20:08 -05:00
hide_getting_started = models . BooleanField ( default = False , null = True , blank = True )
has_setup_feeds = models . BooleanField ( default = False , null = True , blank = True )
has_found_friends = models . BooleanField ( default = False , null = True , blank = True )
has_trained_intelligence = models . BooleanField ( 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 )
2022-02-08 12:49:40 -05:00
paypal_sub_id = models . CharField ( max_length = 24 , blank = True , null = True )
# paypal_payer_id = models.CharField(max_length=24, blank=True, null=True)
2022-01-25 16:08:56 -05:00
premium_renewal = models . BooleanField ( default = False , blank = True , null = True )
2022-02-16 09:20:43 -05:00
active_provider = models . CharField ( max_length = 24 , blank = True , null = True )
2010-05-11 21:36:17 -04:00
2020-06-30 20:50:30 -04:00
def __str__ ( self ) :
2022-01-11 10:59:45 -05:00
return " %s < %s > %s %s %s " % (
2022-01-10 16:07:49 -05:00
self . user ,
self . user . email ,
2022-01-11 10:59:45 -05:00
" (Premium) " if self . is_premium and not self . is_archive and not self . is_pro else " " ,
" (Premium ARCHIVE) " if self . is_archive and not self . is_pro else " " ,
" (Premium PRO) " if self . is_pro else " " ,
2022-01-10 16:07:49 -05:00
)
2011-01-20 09:57:23 -05:00
2022-01-24 17:23:25 -05:00
@classmethod
def plan_to_stripe_price ( cls , plan ) :
price = None
if plan == " premium " :
price = " newsblur-premium-36 "
elif plan == " archive " :
price = " price_0KK5a7wdsmP8XBlaHfbQNnaL "
if settings . DEBUG :
price = " price_0KK5tVwdsmP8XBlaXW1vYUn9 "
elif plan == " pro " :
price = " price_0KK5cvwdsmP8XBlaZDq068bA "
if settings . DEBUG :
price = " price_0KK5twwdsmP8XBlasifbX56Z "
return price
2022-02-04 17:10:23 -05:00
@classmethod
2022-02-16 09:20:43 -05:00
def plan_to_paypal_plan_id ( cls , plan ) :
price = None
if plan == " premium " :
price = " P-48R22630SD810553FMHZONIY "
if settings . DEBUG :
price = " P-4RV31836YD8080909MHZROJY "
elif plan == " archive " :
price = " P-5JM46230U31841226MHZOMZY "
if settings . DEBUG :
price = " P-2EG40290653242115MHZROQQ "
# elif plan == "pro":
# price = "price_0KK5cvwdsmP8XBlaZDq068bA"
# if settings.DEBUG:
# price = "price_0KK5twwdsmP8XBlasifbX56Z"
return price
2022-02-04 17:10:23 -05:00
2013-09-16 16:42:49 -07:00
@property
2022-01-13 14:55:44 -05:00
def unread_cutoff ( self , force_premium = False , force_archive = False ) :
if self . is_archive or force_archive :
2022-02-16 15:46:44 -05:00
days_of_unread = self . days_of_unread or settings . DAYS_OF_UNREAD
return datetime . datetime . utcnow ( ) - datetime . timedelta ( days = days_of_unread )
2013-10-07 10:02:44 -07:00
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 )
2022-01-13 14:55:44 -05:00
@property
def days_of_story_hashes ( self ) :
if self . is_archive :
return settings . DAYS_OF_STORY_HASHES_ARCHIVE
return settings . DAYS_OF_STORY_HASHES
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 ,
2022-01-11 10:59:45 -05:00
' is_archive ' : self . is_archive ,
2022-01-10 16:07:49 -05:00
' is_pro ' : self . is_pro ,
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 )
2022-02-16 15:40:51 -05:00
except DatabaseError as e :
print ( f " ---> Profile not saved: { e } " )
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 :
2020-06-17 00:17:32 -04:00
print ( " ---> You must pass confirm=True to delete this user. " )
2012-07-28 18:33:07 -07:00
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 :
2022-06-06 11:52:16 -04:00
if not fast :
self . cancel_premium ( )
2014-04-03 14:59:30 -07:00
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 ( )
2020-01-18 14:37:44 -05:00
except ( MSocialProfile . DoesNotExist , IndexError ) :
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 ( )
2022-06-21 11:23:25 -04:00
paypal_ids = PaypalIds . objects . filter ( user = self . user )
logging . user ( self . user , " Deleting %s PayPal IDs. " % paypal_ids . count ( ) )
paypal_ids . delete ( )
stripe_ids = StripeIds . objects . filter ( user = self . user )
logging . user ( self . user , " Deleting %s Stripe IDs. " % stripe_ids . count ( ) )
stripe_ids . 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
2022-02-17 12:27:49 -05: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
2021-01-19 17:33:20 -05:00
EmailNewPremium . delay ( user_id = self . user . pk )
2011-10-26 20:09:28 -07:00
2018-07-20 15:55:15 -04:00
was_premium = self . is_premium
2010-10-23 11:20:54 -04:00
self . is_premium = True
2022-02-17 12:27:49 -05:00
self . is_archive = False
self . is_pro = False
2010-10-23 11:20:54 -04:00
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
2018-07-20 15:55:15 -04:00
# Only auto-enable every feed if a free user is moving to premium
2018-07-23 10:39:11 -04:00
subs = UserSubscription . objects . filter ( user = self . user )
2018-07-20 15:55:15 -04:00
if not was_premium :
for sub in subs :
if sub . active : continue
sub . active = True
try :
sub . save ( )
except ( IntegrityError , Feed . DoesNotExist ) :
pass
2018-07-23 10:39:11 -04:00
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 ) )
2021-01-06 14:42:24 -05:00
SchedulePremiumSetup . apply_async ( kwargs = dict ( feed_ids = scheduled_feeds ) )
2018-07-23 10:39:11 -04:00
UserSubscription . queue_new_feeds ( self . user )
2018-07-22 20:45:11 -04:00
2022-02-16 17:11:08 -05:00
# self.setup_premium_history() # Let's not call this unnecessarily
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 ( )
2022-04-08 16:08:06 -04:00
if not was_premium :
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
2022-01-11 13:43:38 -05:00
def activate_archive ( self , never_expire = False ) :
2022-04-18 13:29:13 -04:00
UserSubscription . schedule_fetch_archive_feeds_for_user ( self . user . pk )
2022-01-11 13:43:38 -05:00
was_premium = self . is_premium
was_archive = self . is_archive
was_pro = self . is_pro
self . is_premium = True
self . is_archive = True
self . save ( )
self . user . is_active = True
self . user . save ( )
# Only auto-enable every feed if a free user is moving to premium
subs = UserSubscription . objects . filter ( user = self . user )
if not was_premium :
for sub in subs :
if sub . active : continue
sub . active = True
try :
sub . save ( )
except ( IntegrityError , Feed . DoesNotExist ) :
pass
2022-04-18 13:29:13 -04:00
# Count subscribers to turn on archive_subscribers counts, then show that count to users
# on the paypal_archive_return page.
2022-01-11 13:43:38 -05:00
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 ) )
2022-04-18 13:29:13 -04:00
2022-01-11 13:43:38 -05:00
UserSubscription . queue_new_feeds ( self . user )
self . setup_premium_history ( )
if never_expire :
self . premium_expire = None
self . save ( )
2022-04-08 16:08:06 -04:00
if not was_archive :
logging . user ( self . user , " ~BY~SK~FW~SBNEW PREMIUM ~BBARCHIVE~BY ACCOUNT! WOOHOO!!! ~FR %s subscriptions~SN! " % ( subs . count ( ) ) )
2022-01-11 13:43:38 -05:00
return True
def activate_pro ( self , never_expire = False ) :
from apps . profile . tasks import EmailNewPremiumPro
EmailNewPremiumPro . delay ( user_id = self . user . pk )
was_premium = self . is_premium
was_archive = self . is_archive
was_pro = self . is_pro
self . is_premium = True
self . is_archive = True
self . is_pro = True
self . save ( )
self . user . is_active = True
self . user . save ( )
# Only auto-enable every feed if a free user is moving to premium
subs = UserSubscription . objects . filter ( user = self . user )
if not was_premium :
for sub in subs :
if sub . active : continue
sub . active = True
try :
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 ) )
UserSubscription . queue_new_feeds ( self . user )
self . setup_premium_history ( )
if never_expire :
self . premium_expire = None
self . save ( )
2022-04-08 16:08:06 -04:00
if not was_pro :
logging . user ( self . user , " ~BY~SK~FW~SBNEW PREMIUM ~BGPRO~BY ACCOUNT! WOOHOO!!! ~FR %s subscriptions~SN! " % ( subs . count ( ) ) )
2022-01-11 13:43:38 -05:00
return True
2012-12-05 13:10:11 -08:00
def deactivate_premium ( self ) :
self . is_premium = False
2022-01-11 15:56:19 -05:00
self . is_pro = False
self . is_archive = False
2012-12-05 13:10:11 -08:00
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 ( )
2022-01-25 17:26:24 -05:00
2022-02-16 09:20:43 -05:00
def paypal_change_billing_details_url ( self ) :
return " https://paypal.com "
def switch_stripe_subscription ( self , plan ) :
2022-01-25 17:26:24 -05:00
stripe_customer = self . stripe_customer ( )
if not stripe_customer :
return
stripe_subscriptions = stripe . Subscription . list ( customer = stripe_customer . id ) . data
existing_subscription = None
for subscription in stripe_subscriptions :
if subscription . plan . active :
existing_subscription = subscription
break
if not existing_subscription :
return
2022-07-01 12:20:10 -04:00
try :
stripe . Subscription . modify (
existing_subscription . id ,
cancel_at_period_end = False ,
proration_behavior = ' always_invoice ' ,
items = [ {
' id ' : existing_subscription [ ' items ' ] [ ' data ' ] [ 0 ] . id ,
' price ' : Profile . plan_to_stripe_price ( plan )
} ]
)
except stripe . error . CardError as e :
logging . user ( self . user , f " ~FRStripe switch subscription failed: ~SB { e } " )
return
2022-01-25 17:26:24 -05:00
self . setup_premium_history ( )
2022-02-16 13:43:53 -05:00
return True
2022-04-08 15:07:58 -04:00
def cancel_and_prorate_existing_paypal_subscriptions ( self , data ) :
paypal_api = self . paypal_api ( )
if not paypal_api :
return
canceled_paypal_sub_id = self . cancel_premium_paypal ( cancel_older_subscriptions_only = True )
if not canceled_paypal_sub_id :
logging . user ( self . user , f " ~FRCould not cancel and prorate older paypal premium: { data } " )
return
if isinstance ( canceled_paypal_sub_id , str ) :
self . refund_paypal_payment_from_subscription ( canceled_paypal_sub_id , prorate = True )
2022-02-16 17:11:08 -05:00
def switch_paypal_subscription_approval_url ( self , plan ) :
2022-02-16 09:20:43 -05:00
paypal_api = self . paypal_api ( )
if not paypal_api :
return
2022-04-06 15:56:13 -04:00
paypal_return = reverse ( ' paypal-return ' )
if plan == " archive " :
paypal_return = reverse ( ' paypal-archive-return ' )
2022-02-16 09:20:43 -05:00
try :
2022-04-06 15:56:13 -04:00
application_context = {
' shipping_preference ' : ' NO_SHIPPING ' ,
2022-04-08 07:20:09 -04:00
' user_action ' : ' SUBSCRIBE_NOW ' ,
2022-04-06 15:56:13 -04:00
}
if settings . DEBUG :
2022-04-08 15:07:58 -04:00
application_context [ ' return_url ' ] = f " https://a6d3-161-77-224-226.ngrok.io { paypal_return } "
2022-04-06 15:56:13 -04:00
else :
application_context [ ' return_url ' ] = f " https:// { Site . objects . get_current ( ) . domain } { paypal_return } "
2022-04-08 15:07:58 -04:00
paypal_subscription = paypal_api . post ( f ' /v1/billing/subscriptions ' , {
2022-02-16 09:20:43 -05:00
' plan_id ' : Profile . plan_to_paypal_plan_id ( plan ) ,
2022-04-08 15:07:58 -04:00
' custom_id ' : self . user . pk ,
2022-04-06 15:56:13 -04:00
' application_context ' : application_context ,
2022-02-16 09:20:43 -05:00
} )
2022-04-08 15:07:58 -04:00
except paypalrestsdk . ResourceNotFound as e :
logging . user ( self . user , f " ~FRCouldn ' t create paypal subscription: { self . paypal_sub_id } { plan } : { e } " )
2022-02-16 09:20:43 -05:00
paypal_subscription = None
if not paypal_subscription :
return
logging . user ( self . user , paypal_subscription )
2022-02-16 17:11:08 -05:00
for link in paypal_subscription . get ( ' links ' , [ ] ) :
2022-02-16 09:20:43 -05:00
if link [ ' rel ' ] == ' approve ' :
2022-02-16 17:11:08 -05:00
return link [ ' href ' ]
2022-02-16 09:20:43 -05:00
logging . user ( self . user , f " ~FRFailed to switch paypal subscription: ~FC { paypal_subscription } " )
2022-04-08 15:07:58 -04:00
def store_paypal_sub_id ( self , paypal_sub_id , skip_save_primary = False ) :
2022-02-08 20:56:23 -05:00
if not paypal_sub_id :
logging . user ( self . user , " ~FBPaypal sub id not found, ignoring " )
return
2022-04-08 15:07:58 -04:00
if not skip_save_primary or not self . paypal_sub_id :
self . paypal_sub_id = paypal_sub_id
self . save ( )
2022-02-08 20:56:23 -05:00
seen_paypal_ids = set ( p . paypal_sub_id for p in self . user . paypal_ids . all ( ) )
if paypal_sub_id in seen_paypal_ids :
logging . user ( self . user , f " ~FBPaypal sub seen before, ignoring: { paypal_sub_id } " )
return
self . user . paypal_ids . create ( paypal_sub_id = paypal_sub_id )
logging . user ( self . user , f " ~FBPaypal sub ~SBadded~SN: ~SB { paypal_sub_id } " )
2019-09-09 21:17:10 -04:00
def setup_premium_history ( self , alt_email = None , set_premium_expire = True , force_expiration = False ) :
2014-11-17 13:54:39 -08:00
stripe_payments = [ ]
2018-05-07 13:10:27 -07:00
total_stripe_payments = 0
2022-02-08 12:49:40 -05:00
total_paypal_payments = 0
2022-01-25 17:26:24 -05:00
active_plan = None
premium_renewal = False
2022-02-16 09:20:43 -05:00
active_provider = None
2012-12-03 14:35:21 -08:00
2022-02-08 12:49:40 -05:00
# Find modern Paypal payments
2022-02-08 18:40:07 -05:00
self . retrieve_paypal_ids ( )
2022-02-08 12:49:40 -05:00
if self . paypal_sub_id :
seen_payments = set ( )
2022-06-05 14:16:52 -04:00
seen_payment_history = PaymentHistory . objects . filter ( user = self . user , payment_provider = " paypal " )
2022-06-05 14:14:01 -04:00
deleted_paypal_payments = 0
for payment in list ( seen_payment_history ) :
2022-06-05 12:47:57 -04:00
if payment . payment_date . date ( ) in seen_payments :
payment . delete ( )
2022-06-05 14:14:01 -04:00
deleted_paypal_payments + = 1
2022-06-05 12:47:57 -04:00
else :
seen_payments . add ( payment . payment_date . date ( ) )
2022-06-05 13:34:58 -04:00
total_paypal_payments + = 1
2022-06-05 14:14:01 -04:00
if deleted_paypal_payments > 0 :
logging . user ( self . user , f " ~BY~SN~FRDeleting~FW duplicate paypal history: ~SB { deleted_paypal_payments } payments " )
2022-02-09 12:26:35 -05:00
paypal_api = self . paypal_api ( )
2022-02-08 20:56:23 -05:00
for paypal_id_model in self . user . paypal_ids . all ( ) :
2022-02-09 12:26:35 -05:00
paypal_id = paypal_id_model . paypal_sub_id
2022-02-08 20:56:23 -05:00
try :
paypal_subscription = paypal_api . get ( f ' /v1/billing/subscriptions/ { paypal_id } ' )
except paypalrestsdk . ResourceNotFound :
logging . user ( self . user , f " ~FRCouldn ' t find paypal payments: { paypal_id } " )
2022-02-09 12:26:35 -05:00
paypal_subscription = None
2022-02-08 12:49:40 -05:00
2022-02-08 20:56:23 -05:00
if paypal_subscription :
if paypal_subscription [ ' status ' ] in [ " APPROVAL_PENDING " , " APPROVED " , " ACTIVE " ] :
2022-07-01 17:31:30 -04:00
active_plan = paypal_subscription [ ' plan_id ' ]
2022-02-16 09:20:43 -05:00
active_provider = " paypal "
2022-02-08 20:56:23 -05:00
premium_renewal = True
2022-02-08 12:49:40 -05:00
2022-06-05 09:46:27 -04:00
start_date = datetime . datetime ( 2009 , 1 , 1 ) . strftime ( " % Y- % m- %d T % H: % M: % S.000Z " )
end_date = datetime . datetime . now ( ) . strftime ( " % Y- % m- %d T % H: % M: % S.000Z " )
2022-04-06 15:56:13 -04:00
try :
transactions = paypal_api . get ( f " /v1/billing/subscriptions/ { paypal_id } /transactions?start_time= { start_date } &end_time= { end_date } " )
except paypalrestsdk . exceptions . ResourceNotFound :
transactions = None
if not transactions or ' transactions ' not in transactions :
logging . user ( self . user , f " ~FRCouldn ' t find paypal transactions: ~SB { paypal_id } " )
2022-02-08 21:16:12 -05:00
continue
2022-02-08 20:56:23 -05:00
for transaction in transactions [ ' transactions ' ] :
2022-06-05 12:19:49 -04:00
created = dateutil . parser . parse ( transaction [ ' time ' ] ) . date ( )
2022-04-08 15:27:40 -04:00
if transaction [ ' status ' ] not in [ ' COMPLETED ' , ' PARTIALLY_REFUNDED ' , ' REFUNDED ' ] : continue
2022-02-08 20:56:23 -05:00
if created in seen_payments : continue
seen_payments . add ( created )
total_paypal_payments + = 1
2022-04-08 15:07:58 -04:00
refunded = None
if transaction [ ' status ' ] in [ ' PARTIALLY_REFUNDED ' , ' REFUNDED ' ] :
refunded = True
2022-02-08 20:56:23 -05:00
PaymentHistory . objects . get_or_create ( user = self . user ,
payment_date = created ,
payment_amount = int ( float ( transaction [ ' amount_with_breakdown ' ] [ ' gross_amount ' ] [ ' value ' ] ) ) ,
2022-04-08 15:07:58 -04:00
payment_provider = ' paypal ' ,
refunded = refunded )
2022-06-05 14:16:52 -04:00
2022-06-05 12:19:49 -04:00
ipns = PayPalIPN . objects . filter ( Q ( custom = self . user . username ) |
Q ( payer_email = self . user . email ) |
Q ( custom = self . user . pk ) ) . order_by ( ' -payment_date ' )
for transaction in ipns :
2022-06-05 12:45:17 -04:00
if transaction . txn_type != " subscr_payment " :
continue
2022-06-05 12:19:49 -04:00
created = transaction . payment_date . date ( )
2022-06-05 12:45:17 -04:00
if created in seen_payments :
continue
2022-06-05 12:19:49 -04:00
seen_payments . add ( created )
total_paypal_payments + = 1
PaymentHistory . objects . get_or_create ( user = self . user ,
payment_date = created ,
payment_amount = int ( transaction . payment_gross ) ,
payment_provider = ' paypal ' )
2022-02-08 18:40:07 -05:00
else :
logging . user ( self . user , " ~FBNo Paypal payments " )
2012-12-03 14:35:21 -08:00
# Record Stripe payments
2022-06-05 12:19:49 -04:00
existing_stripe_history = PaymentHistory . objects . filter ( user = self . user ,
payment_provider = " stripe " )
if existing_stripe_history . count ( ) :
logging . user ( self . user , " ~BY~SN~FRDeleting~FW existing stripe history: ~SB %s payments " % existing_stripe_history . count ( ) )
existing_stripe_history . delete ( )
2012-12-03 14:35:21 -08:00
if self . stripe_id :
2018-05-01 14:18:59 -07:00
self . retrieve_stripe_ids ( )
2012-12-03 14:35:21 -08:00
stripe . api_key = settings . STRIPE_SECRET
2018-07-13 12:28:20 -04:00
seen_payments = set ( )
2018-05-01 14:18:59 -07:00
for stripe_id_model in self . user . stripe_ids . all ( ) :
stripe_id = stripe_id_model . stripe_id
stripe_customer = stripe . Customer . retrieve ( stripe_id )
2021-04-20 15:24:39 -04:00
stripe_payments = stripe . Charge . list ( customer = stripe_customer . id ) . data
2022-01-25 17:26:24 -05:00
stripe_subscriptions = stripe . Subscription . list ( customer = stripe_customer . id ) . data
2018-05-01 14:26:55 -07:00
2022-01-25 17:26:24 -05:00
for subscription in stripe_subscriptions :
2022-01-26 10:53:56 -05:00
if subscription . plan . active and not subscription . cancel_at :
2022-01-25 17:26:24 -05:00
active_plan = subscription . plan . id
2022-02-16 09:20:43 -05:00
active_provider = " stripe "
2022-01-25 17:26:24 -05:00
premium_renewal = True
break
2018-05-01 14:18:59 -07:00
for payment in stripe_payments :
created = datetime . datetime . fromtimestamp ( payment . created )
if payment . status == ' failed ' : continue
2018-07-13 12:28:20 -04:00
if created in seen_payments : continue
seen_payments . add ( created )
2018-05-01 14:29:13 -07:00
total_stripe_payments + = 1
2022-04-08 15:27:40 -04:00
refunded = None
if payment . refunded :
refunded = True
2019-08-22 20:07:16 -07:00
PaymentHistory . objects . get_or_create ( user = self . user ,
payment_date = created ,
payment_amount = payment . amount / 100.0 ,
2022-04-08 15:27:40 -04:00
payment_provider = ' stripe ' ,
refunded = refunded )
2022-02-08 18:40:07 -05:00
else :
logging . user ( self . user , " ~FBNo Stripe payments " )
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 :
2021-01-03 17:48:08 -05:00
# Don't use free gift premiums in calculation for expiration
2015-06-30 16:05:58 -07:00
if payment . payment_amount == 0 :
2021-01-03 17:48:08 -05:00
logging . user ( self . user , " ~BY~SN~FWFree lifetime premium " )
2015-06-30 16:05:58 -07:00
free_lifetime_premium = True
2021-01-03 17:48:08 -05:00
continue
# Only update exiration if payment in the last year
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
2021-01-03 17:48:08 -05:00
if 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
2021-01-03 17:48:08 -05:00
( set_premium_expire and not self . premium_expire and not free_lifetime_premium ) 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
2022-02-16 09:20:43 -05:00
if self . premium_renewal != premium_renewal or self . active_provider != active_provider :
2022-04-06 15:56:13 -04:00
active_sub_id = self . stripe_id
if active_provider == " paypal " :
active_sub_id = self . paypal_sub_id
logging . user ( self . user , " ~FCTurning ~SB~ %s ~SN~FC premium renewal ( %s : %s ) " % ( " FRoff " if not premium_renewal else " FBon " , active_provider , active_sub_id ) )
2022-01-25 17:26:24 -05:00
self . premium_renewal = premium_renewal
2022-02-16 09:20:43 -05:00
self . active_provider = active_provider
2022-01-25 17:26:24 -05:00
self . save ( )
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) " % (
2022-02-08 18:40:07 -05:00
total_paypal_payments , total_stripe_payments , len ( payment_history ) , self . premium_expire ) )
2014-11-17 13:54:39 -08:00
2019-09-09 21:17:10 -04:00
if ( set_premium_expire and not self . is_premium and
2022-06-21 13:17:00 -04:00
self . premium_expire > datetime . datetime . now ( ) ) :
2015-01-05 16:06:48 -08:00
self . activate_premium ( )
2022-01-25 17:26:24 -05:00
2022-07-01 17:29:22 -04:00
logging . user ( self . user , " ~FCActive plan: %s , stripe/paypal: %s / %s , is_archive? %s " % ( active_plan , Profile . plan_to_stripe_price ( ' archive ' ) , Profile . plan_to_paypal_plan_id ( ' archive ' ) , self . is_archive ) )
2022-01-25 17:26:24 -05:00
if ( active_plan == Profile . plan_to_stripe_price ( ' pro ' ) and not self . is_pro ) :
self . activate_pro ( )
elif ( active_plan == Profile . plan_to_stripe_price ( ' archive ' ) and not self . is_archive ) :
self . activate_archive ( )
2022-02-16 09:20:43 -05:00
elif ( Profile . plan_to_paypal_plan_id ( ' pro ' ) == active_plan and not self . is_pro ) :
2022-02-08 12:49:40 -05:00
self . activate_premium ( )
2022-02-16 09:20:43 -05:00
elif ( Profile . plan_to_paypal_plan_id ( ' archive ' ) == active_plan and not self . is_archive ) :
2022-02-08 12:49:40 -05:00
self . activate_archive ( )
2022-01-25 17:26:24 -05:00
2021-02-01 14:00:04 -05:00
def preference_value ( self , key , default = None ) :
preferences = json . decode ( self . preferences )
return preferences . get ( key , default )
2022-06-05 13:04:55 -04:00
@classmethod
def resync_stripe_and_paypal_history ( cls , start_days = 365 , end_days = 0 , skip = 0 ) :
start_date = datetime . datetime . now ( ) - datetime . timedelta ( days = start_days )
end_date = datetime . datetime . now ( ) - datetime . timedelta ( days = end_days )
payments = PaymentHistory . objects . filter ( payment_date__gte = start_date ,
payment_date__lte = end_date )
last_seen_date = None
for p , payment in enumerate ( payments ) :
if p < skip :
continue
if p == skip and skip > 0 :
print ( f " ---> Skipping { skip } payments... " )
if payment . payment_date . date ( ) != last_seen_date :
last_seen_date = payment . payment_date . date ( )
print ( f " ---> Payment date: { last_seen_date } (# { p } ) " )
payment . user . profile . setup_premium_history ( )
2019-07-16 12:00:10 -07:00
@classmethod
2019-07-16 12:02:50 -07:00
def reimport_stripe_history ( cls , limit = 10 , days = 7 , starting_after = None ) :
2019-07-16 12:00:10 -07:00
stripe . api_key = settings . STRIPE_SECRET
week = ( datetime . datetime . now ( ) - datetime . timedelta ( days = days ) ) . strftime ( ' %s ' )
failed = [ ]
i = 0
while True :
logging . debug ( " ---> At %s / %s " % ( i , starting_after ) )
i + = 1
try :
2021-04-20 15:24:39 -04:00
data = stripe . Charge . list ( created = { ' gt ' : week } , count = limit , starting_after = starting_after )
2021-04-29 15:25:02 -04:00
except stripe . error . APIConnectionError :
2019-07-16 12:00:10 -07:00
time . sleep ( 10 )
continue
charges = data [ ' data ' ]
if not len ( charges ) :
logging . debug ( " At %s ( %s ), finished " % ( i , starting_after ) )
break
starting_after = charges [ - 1 ] [ " id " ]
customers = [ c [ ' customer ' ] for c in charges if ' customer ' in c ]
for customer in customers :
if not customer :
2020-06-17 00:17:32 -04:00
print ( " ***> No customer! " )
2019-07-16 12:00:10 -07:00
continue
try :
profile = Profile . objects . get ( stripe_id = customer )
user = profile . user
except Profile . DoesNotExist :
logging . debug ( " ***> Couldn ' t find stripe_id= %s " % customer )
failed . append ( customer )
continue
except Profile . MultipleObjectsReturned :
logging . debug ( " ***> Multiple stripe_id= %s " % customer )
failed . append ( customer )
continue
try :
user . profile . setup_premium_history ( )
2021-04-29 15:25:02 -04:00
except stripe . error . APIConnectionError :
2019-07-16 12:00:10 -07:00
logging . debug ( " ***> Failed: %s " % user . username )
failed . append ( user . username )
time . sleep ( 2 )
continue
return ' , ' . join ( failed )
2022-02-10 13:40:07 -05:00
def refund_premium ( self , partial = False , provider = None ) :
2013-05-10 12:05:24 -07:00
refunded = False
2022-02-10 13:40:07 -05:00
if provider == " paypal " :
2022-04-08 15:07:58 -04:00
refunded = self . refund_paypal_payment_from_subscription ( self . paypal_sub_id , prorate = partial )
self . cancel_premium_paypal ( )
2022-02-10 13:40:07 -05:00
elif provider == " stripe " :
refunded = self . refund_latest_stripe_payment ( partial = partial )
2022-04-08 15:07:58 -04:00
# self.cancel_premium_stripe()
2022-02-10 13:40:07 -05:00
else :
# Find last payment, refund that
payment_history = PaymentHistory . objects . filter ( user = self . user ,
payment_provider__in = [ ' paypal ' , ' stripe ' ] )
if payment_history . count ( ) :
provider = payment_history [ 0 ] . payment_provider
if provider == " stripe " :
refunded = self . refund_latest_stripe_payment ( partial = partial )
2022-04-08 15:07:58 -04:00
# self.cancel_premium_stripe()
2022-02-10 13:40:07 -05:00
elif provider == " paypal " :
2022-04-08 15:07:58 -04:00
refunded = self . refund_paypal_payment_from_subscription ( self . paypal_sub_id , prorate = partial )
self . cancel_premium_paypal ( )
2022-02-10 13:40:07 -05:00
return refunded
def refund_latest_stripe_payment ( self , partial = False ) :
refunded = False
if not self . stripe_id :
return
2013-05-10 12:05:24 -07:00
2022-02-10 13:40:07 -05:00
stripe . api_key = settings . STRIPE_SECRET
stripe_customer = stripe . Customer . retrieve ( self . stripe_id )
stripe_payments = stripe . Charge . list ( customer = stripe_customer . id ) . data
if partial :
stripe_payments [ 0 ] . refund ( amount = 1200 )
refunded = 12
2013-05-23 16:28:39 -07:00
else :
2022-02-10 13:40:07 -05:00
stripe_payments [ 0 ] . refund ( )
self . cancel_premium_stripe ( )
refunded = stripe_payments [ 0 ] . amount / 100
logging . user ( self . user , " ~FRRefunding stripe payment: $ %s " % refunded )
return refunded
2022-04-08 15:07:58 -04:00
def refund_paypal_payment_from_subscription ( self , paypal_sub_id , prorate = False ) :
if not paypal_sub_id :
2022-02-10 13:40:07 -05:00
return
paypal_api = self . paypal_api ( )
refunded = False
2014-09-24 17:00:09 -07:00
2022-02-10 13:40:07 -05:00
# Find transaction from subscription
2022-04-08 15:07:58 -04:00
now = datetime . datetime . now ( ) + datetime . timedelta ( days = 1 )
2022-02-10 13:40:07 -05:00
# 200 days captures Paypal's 180 day limit on refunds
start_date = ( now - datetime . timedelta ( days = 200 ) ) . strftime ( " % Y- % m- %d T % H: % M: % SZ " )
end_date = now . strftime ( " % Y- % m- %d T % H: % M: % SZ " )
try :
2022-04-08 15:07:58 -04:00
transactions = paypal_api . get ( f " /v1/billing/subscriptions/ { paypal_sub_id } /transactions?start_time= { start_date } &end_time= { end_date } " )
2022-02-10 13:40:07 -05:00
except paypalrestsdk . ResourceNotFound :
transactions = { }
if ' transactions ' not in transactions or not len ( transactions [ ' transactions ' ] ) :
2022-04-08 15:07:58 -04:00
logging . user ( self . user , f " ~FRCouldn ' t find paypal transactions for refund: { paypal_sub_id } { transactions } " )
2022-02-10 13:40:07 -05:00
return
2013-05-10 12:05:24 -07:00
2022-02-10 13:40:07 -05:00
# Refund the latest transaction
transaction = transactions [ ' transactions ' ] [ 0 ]
today = datetime . datetime . now ( ) . strftime ( ' % B %d , % Y ' )
url = f " /v2/payments/captures/ { transaction [ ' id ' ] } /refund "
2022-04-08 15:07:58 -04:00
refund_amount = float ( transaction [ ' amount_with_breakdown ' ] [ ' gross_amount ' ] [ ' value ' ] )
if prorate :
transaction_date = dateutil . parser . parse ( transaction [ ' time ' ] )
days_since = ( datetime . datetime . now ( ) - transaction_date . replace ( tzinfo = None ) ) . days
if days_since < 365 :
days_left = ( 365 - days_since )
pct_left = days_left / 365
2022-04-08 15:27:40 -04:00
refund_amount = pct_left * refund_amount
2022-04-08 15:07:58 -04:00
else :
logging . user ( self . user , f " ~FRCouldn ' t prorate paypal payment, too old: ~SB { transaction } " )
2022-02-10 13:40:07 -05:00
try :
response = paypal_api . post ( url , {
2022-04-08 15:07:58 -04:00
' reason ' : f " Refunded on { today } " ,
' amount ' : {
' currency_code ' : ' USD ' ,
' value ' : f " { refund_amount : .2f } " ,
}
2022-02-10 13:40:07 -05:00
} )
except paypalrestsdk . exceptions . ResourceInvalid as e :
response = e . response . json ( )
if len ( response . get ( ' details ' , [ ] ) ) :
response = response [ ' details ' ] [ 0 ] [ ' description ' ]
2022-04-08 15:07:58 -04:00
if settings . DEBUG :
logging . user ( self . user , f " Paypal refund response: { response } " )
2022-02-10 13:40:07 -05:00
if ' status ' in response and response [ ' status ' ] == " COMPLETED " :
refunded = int ( float ( transaction [ ' amount_with_breakdown ' ] [ ' gross_amount ' ] [ ' value ' ] ) )
2022-04-08 15:27:40 -04:00
logging . user ( self . user , " ~FRRefunding paypal payment: $ %s / %s " % ( refund_amount , refunded ) )
2022-02-10 13:40:07 -05:00
else :
logging . user ( self . user , " ~FRCouldn ' t refund paypal payment: %s " % response )
refunded = response
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 ( )
2022-04-08 16:08:06 -04:00
self . setup_premium_history ( ) # Sure, webhooks will force new history, but they take forever
2017-03-30 11:12:21 -07:00
return stripe_cancel or paypal_cancel
2013-03-13 15:46:51 -07:00
2022-04-08 15:07:58 -04:00
def cancel_premium_paypal ( self , cancel_older_subscriptions_only = False ) :
2022-02-09 12:26:35 -05:00
self . retrieve_paypal_ids ( )
if not self . paypal_sub_id :
logging . user ( self . user , " ~FRUser doesn ' t have a Paypal subscription, how did we get here? " )
2013-05-23 16:28:39 -07:00
return
2022-04-08 15:07:58 -04:00
if not self . premium_renewal and not cancel_older_subscriptions_only :
2022-02-09 12:26:35 -05:00
logging . user ( self . user , " ~FRUser ~SBalready~SN canceled Paypal subscription: %s " % self . paypal_sub_id )
return
paypal_api = self . paypal_api ( )
today = datetime . datetime . now ( ) . strftime ( ' % B %d , % Y ' )
for paypal_id_model in self . user . paypal_ids . all ( ) :
paypal_id = paypal_id_model . paypal_sub_id
2022-04-08 15:07:58 -04:00
if cancel_older_subscriptions_only and paypal_id == self . paypal_sub_id :
logging . user ( self . user , " ~FBNot canceling active Paypal subscription: %s " % self . paypal_sub_id )
continue
2022-02-09 12:26:35 -05:00
try :
paypal_subscription = paypal_api . get ( f ' /v1/billing/subscriptions/ { paypal_id } ' )
except paypalrestsdk . ResourceNotFound :
logging . user ( self . user , f " ~FRCouldn ' t find paypal payments: { paypal_id } " )
continue
if paypal_subscription [ ' status ' ] not in [ ' ACTIVE ' , ' APPROVED ' , ' APPROVAL_PENDING ' ] :
logging . user ( self . user , " ~FRUser ~SBalready~SN canceled Paypal subscription: %s " % paypal_id )
continue
url = f " /v1/billing/subscriptions/ { paypal_id } /suspend "
2022-07-01 12:35:59 -04:00
try :
response = paypal_api . post ( url , {
' reason ' : f " Cancelled on { today } "
} )
except paypalrestsdk . ResourceNotFound as e :
logging . user ( self . user , f " ~FRCouldn ' t find paypal response during ~FB~SB { paypal_id } ~SN~FR profile suspend: ~SB~FB { e } " )
2022-02-09 12:26:35 -05:00
logging . user ( self . user , " ~FRCanceling Paypal subscription: %s " % paypal_id )
2022-04-08 15:07:58 -04:00
return paypal_id
2022-02-09 12:26:35 -05:00
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
2022-02-16 13:27:56 -05:00
for stripe_id_model in self . user . stripe_ids . all ( ) :
stripe_id = stripe_id_model . stripe_id
stripe_customer = stripe . Customer . retrieve ( stripe_id )
try :
subscriptions = stripe . Subscription . list ( customer = stripe_customer )
for subscription in subscriptions . data :
stripe . Subscription . modify ( subscription [ ' id ' ] , cancel_at_period_end = True )
logging . user ( self . user , " ~FRCanceling Stripe subscription: %s " % subscription [ ' id ' ] )
except stripe . error . InvalidRequestError :
logging . user ( self . user , " ~FRFailed to cancel Stripe subscription: %s " % stripe_id )
continue
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
2018-04-27 10:26:56 -07:00
def retrieve_stripe_ids ( self ) :
if not self . stripe_id :
return
2018-05-01 14:26:55 -07:00
stripe . api_key = settings . STRIPE_SECRET
2018-04-27 10:26:56 -07:00
stripe_customer = stripe . Customer . retrieve ( self . stripe_id )
stripe_email = stripe_customer . email
2018-05-01 14:18:59 -07:00
stripe_ids = set ( )
2018-04-27 10:26:56 -07:00
for email in set ( [ stripe_email , self . user . email ] ) :
customers = stripe . Customer . list ( email = email )
2018-05-01 14:18:59 -07:00
for customer in customers :
stripe_ids . add ( customer . stripe_id )
self . user . stripe_ids . all ( ) . delete ( )
for stripe_id in stripe_ids :
self . user . stripe_ids . create ( stripe_id = stripe_id )
2022-02-08 12:49:40 -05:00
2022-06-05 09:46:27 -04:00
def retrieve_paypal_ids ( self , force = False ) :
if self . paypal_sub_id and not force :
2022-02-08 12:49:40 -05:00
return
2022-02-08 18:40:07 -05:00
ipns = PayPalIPN . objects . filter ( Q ( custom = self . user . username ) |
2022-02-08 12:49:40 -05:00
Q ( payer_email = self . user . email ) |
2022-02-08 18:40:07 -05:00
Q ( custom = self . user . pk ) ) . order_by ( ' -payment_date ' )
if not len ( ipns ) :
2022-02-08 12:49:40 -05:00
return
2022-02-08 18:40:07 -05:00
self . paypal_sub_id = ipns [ 0 ] . subscr_id
self . save ( )
2022-02-08 12:49:40 -05:00
2022-02-08 18:40:07 -05:00
paypal_ids = set ( )
for ipn in ipns :
if not ipn . subscr_id : continue
paypal_ids . add ( ipn . subscr_id )
seen_paypal_ids = set ( p . paypal_sub_id for p in self . user . paypal_ids . all ( ) )
for paypal_id in paypal_ids :
if paypal_id in seen_paypal_ids :
continue
self . user . paypal_ids . create ( paypal_sub_id = paypal_id )
2018-04-27 10:26:56 -07:00
2017-03-30 11:12:21 -07:00
@property
def latest_paypal_email ( self ) :
ipn = PayPalIPN . objects . filter ( custom = self . user . username )
2021-01-23 18:01:19 -05:00
if not len ( ipn ) :
ipn = PayPalIPN . objects . filter ( payer_email = self . user . email )
2017-03-30 11:12:21 -07:00
if not len ( ipn ) :
return
return ipn [ 0 ] . payer_email
2017-11-10 15:12:54 -08:00
2021-03-22 10:25:14 -04:00
def update_email ( self , new_email ) :
from apps . social . models import MSocialProfile
if self . user . email == new_email :
return
self . user . email = new_email
self . user . save ( )
sp = MSocialProfile . get_user ( self . user . pk )
sp . email = new_email
sp . save ( )
if self . stripe_id :
stripe_customer = self . stripe_customer ( )
stripe_customer . update ( { ' email ' : new_email } )
stripe_customer . save ( )
def stripe_customer ( self ) :
if self . stripe_id :
stripe . api_key = settings . STRIPE_SECRET
stripe_customer = stripe . Customer . retrieve ( self . stripe_id )
return stripe_customer
2022-02-09 12:26:35 -05:00
def paypal_api ( self ) :
if self . paypal_sub_id :
api = paypalrestsdk . Api ( {
" mode " : " sandbox " if settings . DEBUG else " live " ,
" client_id " : settings . PAYPAL_API_CLIENTID ,
" client_secret " : settings . PAYPAL_API_SECRET
} )
return api
2020-08-25 21:08:27 -04:00
def activate_ios_premium ( self , transaction_identifier = None , amount = 36 ) :
2017-11-15 17:26:27 -08:00
payments = PaymentHistory . objects . filter ( user = self . user ,
2018-12-16 15:14:14 -05:00
payment_identifier = transaction_identifier ,
payment_date__gte = datetime . datetime . now ( ) - datetime . timedelta ( days = 3 ) )
2017-11-15 17:26:27 -08:00
if len ( payments ) :
# Already paid
2018-01-08 13:40:26 -08:00
logging . user ( self . user , " ~FG~BBAlready paid iOS premium subscription: $ %s ~FW " % transaction_identifier )
2017-11-15 17:26:27 -08:00
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 )
2017-11-15 18:01:55 -08:00
2019-09-09 21:17:10 -04:00
self . setup_premium_history ( )
2017-11-15 17:26:27 -08:00
if not self . is_premium :
self . activate_premium ( )
2020-08-25 21:08:27 -04:00
logging . user ( self . user , " ~FG~BBNew iOS premium subscription: $ %s ~FW " % amount )
return True
def activate_android_premium ( self , order_id = None , amount = 36 ) :
payments = PaymentHistory . objects . filter ( user = self . user ,
payment_identifier = order_id ,
payment_date__gte = datetime . datetime . now ( ) - datetime . timedelta ( days = 3 ) )
if len ( payments ) :
# Already paid
logging . user ( self . user , " ~FG~BBAlready paid Android premium subscription: $ %s ~FW " % amount )
return False
PaymentHistory . objects . create ( user = self . user ,
payment_date = datetime . datetime . now ( ) ,
payment_amount = amount ,
payment_provider = ' android-subscription ' ,
payment_identifier = order_id )
self . setup_premium_history ( )
if not self . is_premium :
self . activate_premium ( )
logging . user ( self . user , " ~FG~BBNew Android premium subscription: $ %s ~FW " % amount )
2017-11-15 17:26:27 -08:00
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 )
2018-01-18 16:10:41 -08:00
try :
has_profile = user . profile . last_seen_ip
except Profile . DoesNotExist :
usernames . add ( user . username )
2020-06-17 00:17:32 -04:00
print ( " ---> Missing profile: %-20s %-30s %-6s %-6s " % ( user . username , user . email , opens , reads ) )
2018-01-18 16:10:41 -08:00
continue
2015-11-23 11:26:22 -08:00
if opens is None and not reads and has_numbers :
usernames . add ( user . username )
2020-06-17 00:17:32 -04:00
print ( " ---> Numerics: %-20s %-30s %-6s %-6s " % ( user . username , user . email , opens , reads ) )
2018-01-18 16:10:41 -08:00
elif not has_profile :
2015-11-23 11:26:22 -08:00
usernames . add ( user . username )
2020-06-17 00:17:32 -04: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 :
2019-10-03 09:05:28 -04:00
try :
u = User . objects . get ( username = username )
except User . DoesNotExist :
continue
2015-07-30 18:10:55 -07:00
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
2022-01-11 10:59:45 -05:00
archive = 0
2022-01-10 17:00:27 -05:00
pro = 0
2015-07-28 18:46:37 -07:00
key = ' s: %s ' % feed_id
premium_key = ' sp: %s ' % feed_id
2022-01-11 10:59:45 -05:00
archive_key = ' sarchive: %s ' % feed_id
2022-01-10 17:00:27 -05:00
pro_key = ' spro: %s ' % feed_id
2015-07-28 18:46:37 -07:00
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
2022-01-11 15:56:19 -05:00
user_active_feeds = dict ( [ ( user_id , active ) ] )
2015-07-28 18:46:37 -07:00
else :
2022-01-11 15:56:19 -05:00
user_active_feeds = dict ( [ ( us . user_id , us . active )
2015-07-28 19:56:06 -07:00
for us in UserSubscription . objects . filter ( feed_id = feed_id ) . only ( ' user ' , ' active ' ) ] )
2022-01-11 15:56:19 -05:00
profiles = Profile . objects . filter ( user_id__in = list ( user_active_feeds . keys ( ) ) ) . values ( ' user_id ' , ' last_seen_on ' , ' is_premium ' , ' is_archive ' , ' is_pro ' )
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 :
2022-01-11 15:56:19 -05:00
pipeline = r . pipeline ( )
pipeline . delete ( key )
pipeline . delete ( premium_key )
pipeline . delete ( archive_key )
pipeline . delete ( pro_key )
pipeline . execute ( )
2015-07-28 20:46:30 -07:00
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 ' ) )
2022-01-11 15:56:19 -05:00
muted_feed = not bool ( user_active_feeds [ profile [ ' user_id ' ] ] )
2015-07-28 20:46:30 -07:00
if muted_feed :
last_seen_on = 0
2020-06-20 00:08:18 -04:00
pipeline . zadd ( key , { profile [ ' user_id ' ] : last_seen_on } )
2015-07-28 18:46:37 -07:00
total + = 1
if profile [ ' is_premium ' ] :
2020-06-20 00:08:18 -04:00
pipeline . zadd ( premium_key , { profile [ ' user_id ' ] : last_seen_on } )
2015-07-28 18:46:37 -07:00
premium + = 1
2015-07-28 19:04:06 -07:00
else :
pipeline . zrem ( premium_key , profile [ ' user_id ' ] )
2022-01-11 10:59:45 -05:00
if profile [ ' is_archive ' ] :
pipeline . zadd ( archive_key , { profile [ ' user_id ' ] : last_seen_on } )
archive + = 1
else :
pipeline . zrem ( archive_key , profile [ ' user_id ' ] )
2022-01-10 17:00:27 -05:00
if profile [ ' is_pro ' ] :
pipeline . zadd ( pro_key , { profile [ ' user_id ' ] : last_seen_on } )
pro + = 1
else :
pipeline . zrem ( pro_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 ' ) )
2020-06-20 00:08:18 -04:00
r . zadd ( key , { - 1 : now } )
2015-08-11 13:33:49 -07:00
r . expire ( key , settings . SUBSCRIBER_EXPIRE * 24 * 60 * 60 )
2020-06-29 17:39:55 -04: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 )
2022-01-11 15:56:19 -05:00
r . zadd ( archive_key , { - 1 : now } )
r . expire ( archive_key , settings . SUBSCRIBER_EXPIRE * 24 * 60 * 60 )
r . zadd ( pro_key , { - 1 : now } )
r . expire ( pro_key , settings . SUBSCRIBER_EXPIRE * 24 * 60 * 60 )
2015-07-28 18:46:37 -07:00
2022-01-11 13:43:38 -05: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 ~SN archive:~SB %s ~SN pro:~SB %s " %
( feed . log_title [ : 30 ] , total , active , premium , active_premium , archive , pro ) )
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
2022-01-11 10:59:45 -05:00
archive_key = ' sarchive: %s ' % feed_id
2022-01-10 17:00:27 -05:00
pro_key = ' spro: %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
2020-06-20 00:08:18 -04:00
pipeline . zadd ( key , { user . pk : last_seen_on } )
2015-07-28 20:46:30 -07:00
if user . profile . is_premium :
2020-06-20 00:08:18 -04:00
pipeline . zadd ( premium_key , { user . pk : last_seen_on } )
2015-07-28 20:46:30 -07:00
else :
pipeline . zrem ( premium_key , user . pk )
2022-01-11 10:59:45 -05:00
if user . profile . is_archive :
pipeline . zadd ( archive_key , { user . pk : last_seen_on } )
else :
pipeline . zrem ( archive_key , user . pk )
2022-01-10 17:00:27 -05:00
if user . profile . is_pro :
pipeline . zadd ( pro_key , { user . pk : last_seen_on } )
else :
pipeline . zrem ( pro_key , user . pk )
2015-07-28 20:46:30 -07:00
pipeline . execute ( )
2011-09-19 08:56:16 -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 " )
2022-03-17 16:54:40 -04:00
msg . send ( )
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 ' )
2022-03-17 16:54:40 -04:00
msg . send ( )
2013-05-23 18:14:21 -07:00
2022-03-14 11:06:01 -04:00
from apps . social . models import MActivity
2022-03-14 15:52:22 -04:00
MActivity . new_opml_export ( user_id = self . user . pk , count = exporter . feed_count , automated = True )
2022-03-14 11:06:01 -04:00
2013-05-23 18:14:21 -07:00
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 " )
2022-03-17 16:54:40 -04:00
msg . send ( )
2013-04-22 15:24:38 -07:00
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
2022-03-17 16:54:40 -04:00
def send_new_premium_email ( self , force = False ) :
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 ( ) )
2022-03-17 16:54:40 -04:00
subject = " Thank you for subscribing to NewsBlur Premium! "
2011-09-19 08:56:16 -07:00
msg = EmailMultiAlternatives ( subject , text ,
from_email = ' NewsBlur < %s > ' % settings . HELLO_EMAIL ,
to = [ ' %s < %s > ' % ( user , user . email ) ] )
msg . attach_alternative ( html , " text/html " )
2022-03-17 16:54:40 -04:00
msg . send ( )
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
2022-06-22 15:53:13 -04:00
def send_new_premium_archive_email ( self , total_story_count , pre_archive_count , force = False ) :
2022-04-18 13:29:13 -04:00
if not self . user . email :
2022-01-11 15:56:19 -05:00
return
params = dict ( receiver_user_id = self . user . pk , email_type = ' new_premium_archive ' )
try :
MSentEmail . objects . get ( * * params )
if not force :
# Return if email already sent
2022-06-22 12:11:52 -04:00
logging . user ( self . user , " ~BB~FMNot ~SBSending email for new premium archive: %s ( %s to %s stories) " % ( self . user . email , pre_archive_count , total_story_count ) )
2022-01-11 15:56:19 -05:00
return
except MSentEmail . DoesNotExist :
MSentEmail . objects . create ( * * params )
2022-05-05 16:03:14 -04:00
feed_count = UserSubscription . objects . filter ( user = self . user ) . count ( )
2022-01-11 15:56:19 -05:00
user = self . user
text = render_to_string ( ' mail/email_new_premium_archive.txt ' , locals ( ) )
html = render_to_string ( ' mail/email_new_premium_archive.xhtml ' , locals ( ) )
2022-05-05 16:03:14 -04:00
if total_story_count > pre_archive_count :
subject = f " NewsBlur archive backfill is complete: from { pre_archive_count : , } to { total_story_count : , } stories "
else :
subject = f " NewsBlur archive backfill is complete: { total_story_count : , } stories "
2022-01-11 15:56:19 -05:00
msg = EmailMultiAlternatives ( subject , text ,
from_email = ' NewsBlur < %s > ' % settings . HELLO_EMAIL ,
to = [ ' %s < %s > ' % ( user , user . email ) ] )
msg . attach_alternative ( html , " text/html " )
2022-03-17 16:54:40 -04:00
msg . send ( )
2022-01-11 15:56:19 -05:00
2022-05-05 16:03:14 -04:00
logging . user ( self . user , " ~BB~FM~SBSending email for new premium archive: %s ( %s to %s stories) " % ( self . user . email , pre_archive_count , total_story_count ) )
2022-01-11 15:56:19 -05:00
def send_new_premium_pro_email ( self , force = False ) :
if not self . user . email or not self . send_emails :
return
params = dict ( receiver_user_id = self . user . pk , email_type = ' new_premium_pro ' )
try :
MSentEmail . objects . get ( * * params )
if not force :
# Return if email already sent
return
except MSentEmail . DoesNotExist :
MSentEmail . objects . create ( * * params )
user = self . user
text = render_to_string ( ' mail/email_new_premium_pro.txt ' , locals ( ) )
html = render_to_string ( ' mail/email_new_premium_pro.xhtml ' , locals ( ) )
subject = " Thanks for subscribing to NewsBlur Premium Pro! "
msg = EmailMultiAlternatives ( subject , text ,
from_email = ' NewsBlur < %s > ' % settings . HELLO_EMAIL ,
to = [ ' %s < %s > ' % ( user , user . email ) ] )
msg . attach_alternative ( html , " text/html " )
2022-03-17 16:54:40 -04:00
msg . send ( )
2022-01-11 15:56:19 -05:00
logging . user ( self . user , " ~BB~FM~SBSending email for new premium pro: %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 :
2020-06-17 00:17:32 -04:00
print ( " Please provide an email address. " )
2011-09-22 09:23:42 -07:00
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 " )
2022-03-17 16:54:40 -04:00
msg . send ( )
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 :
2020-06-17 00:17:32 -04:00
print ( " Please provide an email address. " )
2013-05-13 18:03:17 -07:00
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 " )
2022-03-17 16:54:40 -04:00
msg . send ( )
2013-05-13 18:03:17 -07:00
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 :
2020-06-17 00:17:32 -04:00
print ( " Please provide an email address. " )
2012-07-20 19:43:28 -07:00
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 :
2020-06-17 00:17:32 -04:00
print ( " Please provide an email address. " )
2013-04-02 15:41:50 -07:00
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 :
2020-06-17 00:17:32 -04:00
print ( " Please provide an email address. " )
2013-04-02 15:41:50 -07:00
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 " )
2022-03-17 16:54:40 -04:00
msg . send ( )
2012-08-09 19:45:08 -07:00
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 " )
2022-03-17 16:54:40 -04:00
msg . send ( )
2016-09-30 23:49:15 -07:00
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 " )
2022-03-17 16:54:40 -04:00
msg . send ( )
2017-03-08 18:04:57 -08:00
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 " )
2022-03-17 16:54:40 -04:00
msg . send ( )
2012-12-05 11:56:55 -08:00
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 " )
2022-03-17 16:54:40 -04:00
msg . send ( )
2012-12-05 11:56:55 -08:00
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
2019-09-09 21:17:10 -04:00
profile . setup_premium_history ( )
2015-01-05 17:06:41 -08:00
2018-05-01 14:18:59 -07:00
class StripeIds ( models . Model ) :
2020-06-20 04:31:48 -04:00
user = models . ForeignKey ( User , related_name = ' stripe_ids ' , on_delete = models . CASCADE , null = True )
2018-05-01 14:18:59 -07:00
stripe_id = models . CharField ( max_length = 24 , blank = True , null = True )
2020-06-30 20:50:30 -04:00
def __str__ ( self ) :
2018-05-01 14:18:59 -07:00
return " %s : %s " % ( self . user . username , self . stripe_id )
2022-02-08 12:49:40 -05:00
class PaypalIds ( models . Model ) :
user = models . ForeignKey ( User , related_name = ' paypal_ids ' , on_delete = models . CASCADE , null = True )
paypal_sub_id = models . CharField ( max_length = 24 , blank = True , null = True )
def __str__ ( self ) :
return " %s : %s " % ( self . user . username , self . paypal_sub_id )
2018-05-01 14:18:59 -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
2022-07-01 12:17:14 -04:00
user = None
2013-01-31 16:55:45 -08:00
try :
user = User . objects . get ( username__iexact = ipn_obj . custom )
except User . DoesNotExist :
2022-07-01 12:17:14 -04:00
pass
if not user :
2021-08-03 16:04:56 -04:00
try :
user = User . objects . get ( email__iexact = ipn_obj . payer_email )
except User . DoesNotExist :
2022-07-01 12:17:14 -04:00
pass
if not user :
try :
user = User . objects . get ( pk = ipn_obj . custom )
except User . DoesNotExist :
pass
if not user :
try :
user = PaypalIds . objects . get ( paypal_sub_id = ipn_obj . subscr_id ) . user
except PaypalIds . DoesNotExist :
pass
if not user :
logging . debug ( " ---> Paypal subscription not found during paypal_signup: %s / %s " % (
ipn_obj . payer_email ,
ipn_obj . custom ) )
return { " code " : - 1 , " message " : " User doesn ' t exist. " }
2021-01-05 14:55:53 -05:00
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 ( )
2022-02-09 12:26:35 -05:00
# user.profile.cancel_premium_paypal(second_most_recent_only=True)
2022-07-01 12:17:14 -04:00
# assert False, "Shouldn't be here anymore as the new Paypal REST API uses webhooks"
2020-06-24 16:59:43 -04:00
valid_ipn_received . connect ( paypal_signup )
2015-01-15 12:40:17 -08:00
def paypal_payment_history_sync ( sender , * * kwargs ) :
ipn_obj = sender
try :
user = User . objects . get ( username__iexact = ipn_obj . custom )
except User . DoesNotExist :
2021-08-03 16:04:56 -04:00
try :
user = User . objects . get ( email__iexact = ipn_obj . payer_email )
except User . DoesNotExist :
logging . debug ( " ---> Paypal subscription not found during flagging: %s / %s " % (
ipn_obj . payer_email ,
ipn_obj . custom ) )
return { " code " : - 1 , " message " : " User doesn ' t exist. " }
2021-01-05 14:55:53 -05:00
2015-01-15 12:40:17 -08:00
logging . user ( user , " ~BC~SB~FBPaypal subscription payment " )
try :
2019-09-09 21:17:10 -04:00
user . profile . setup_premium_history ( )
2012-12-03 15:17:35 -08:00
except :
return { " code " : - 1 , " message " : " User doesn ' t exist. " }
2020-06-24 16:59:43 -04:00
valid_ipn_received . 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 :
2021-08-03 16:04:56 -04:00
try :
2015-02-09 13:51:06 -08:00
user = User . objects . get ( email__iexact = ipn_obj . payer_email )
2021-08-03 16:04:56 -04:00
except User . DoesNotExist :
logging . debug ( " ---> Paypal subscription not found during flagging: %s / %s " % (
ipn_obj . payer_email ,
ipn_obj . custom ) )
return { " code " : - 1 , " message " : " User doesn ' t exist. " }
2021-01-05 14:55:53 -05:00
2014-12-04 15:47:19 -08:00
try :
2019-09-09 21:17:10 -04:00
user . profile . setup_premium_history ( )
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. " }
2020-06-24 16:59:43 -04:00
invalid_ipn_received . connect ( paypal_payment_was_flagged )
2015-01-15 12:40:17 -08:00
2022-01-24 17:33:11 -05:00
def stripe_checkout_session_completed ( sender , full_json , * * kwargs ) :
2022-01-24 17:23:25 -05:00
newsblur_user_id = full_json [ ' data ' ] [ ' object ' ] [ ' metadata ' ] [ ' newsblur_user_id ' ]
stripe_id = full_json [ ' data ' ] [ ' object ' ] [ ' customer ' ]
profile = None
try :
profile = Profile . objects . get ( stripe_id = stripe_id )
except Profile . DoesNotExist :
pass
if not profile :
try :
profile = User . objects . get ( pk = int ( newsblur_user_id ) ) . profile
profile . stripe_id = stripe_id
profile . save ( )
except User . DoesNotExist :
pass
if profile :
logging . user ( profile . user , " ~BC~SB~FBStripe checkout subscription signup " )
profile . retrieve_stripe_ids ( )
else :
2022-01-24 17:33:11 -05:00
logging . user ( profile . user , " ~BR~SB~FRCouldn ' t find Stripe user: ~FW %s " % full_json )
2022-01-24 17:23:25 -05:00
return { " code " : - 1 , " message " : " User doesn ' t exist. " }
2022-01-24 17:33:11 -05:00
zebra_webhook_checkout_session_completed . connect ( stripe_checkout_session_completed )
2022-01-24 17:23:25 -05: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 ' ]
2022-01-24 17:23:25 -05:00
plan_id = full_json [ ' data ' ] [ ' object ' ] [ ' plan ' ] [ ' id ' ]
2012-07-27 12:46:37 -07:00
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 " )
2022-01-24 17:23:25 -05:00
if plan_id == Profile . plan_to_stripe_price ( ' premium ' ) :
profile . activate_premium ( )
elif plan_id == Profile . plan_to_stripe_price ( ' archive ' ) :
2022-02-17 12:27:49 -05:00
profile . activate_archive ( )
2022-01-24 17:23:25 -05:00
elif plan_id == Profile . plan_to_stripe_price ( ' pro ' ) :
2022-02-17 12:27:49 -05:00
profile . activate_pro ( )
2016-03-20 17:55:44 -07:00
profile . cancel_premium_paypal ( )
2018-05-01 14:18:59 -07:00
profile . retrieve_stripe_ids ( )
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 )
2022-01-26 15:51:26 -05:00
def stripe_subscription_updated ( sender , full_json , * * kwargs ) :
stripe_id = full_json [ ' data ' ] [ ' object ' ] [ ' customer ' ]
plan_id = full_json [ ' data ' ] [ ' object ' ] [ ' plan ' ] [ ' id ' ]
try :
profile = Profile . objects . get ( stripe_id = stripe_id )
2022-02-16 13:43:53 -05:00
active = not full_json [ ' data ' ] [ ' object ' ] [ ' cancel_at ' ] and full_json [ ' data ' ] [ ' object ' ] [ ' plan ' ] [ ' active ' ]
logging . user ( profile . user , " ~BC~SB~FBStripe subscription updated: %s " % " active " if active else " cancelled " )
if active :
if plan_id == Profile . plan_to_stripe_price ( ' premium ' ) :
profile . activate_premium ( )
elif plan_id == Profile . plan_to_stripe_price ( ' archive ' ) :
2022-02-17 12:27:49 -05:00
profile . activate_archive ( )
2022-02-16 13:43:53 -05:00
elif plan_id == Profile . plan_to_stripe_price ( ' pro ' ) :
2022-02-17 12:27:49 -05:00
profile . activate_pro ( )
2022-02-16 13:43:53 -05:00
profile . cancel_premium_paypal ( )
profile . retrieve_stripe_ids ( )
else :
profile . setup_premium_history ( )
2022-01-26 15:51:26 -05:00
except Profile . DoesNotExist :
return { " code " : - 1 , " message " : " User doesn ' t exist. " }
zebra_webhook_customer_subscription_updated . connect ( stripe_subscription_updated )
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 " )
2019-09-09 21:17:10 -04:00
profile . setup_premium_history ( )
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 )
2022-01-26 15:51:26 -05:00
zebra_webhook_charge_refunded . connect ( stripe_payment_history_sync )
2012-12-03 15:17:35 -08:00
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 )
2020-06-20 00:27:01 -04:00
encoded_blank = hashlib . sha1 ( ( salt + password ) . encode ( encoding = ' utf-8 ' ) ) . hexdigest ( )
2013-04-05 19:23:42 -07:00
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 ' ] ,
2019-12-25 16:06:34 -05:00
' unique ' : True ,
2020-06-20 00:05:32 -04:00
} ] ,
2016-09-30 23:49:15 -07:00
}
2020-06-30 20:50:30 -04:00
def __str__ ( self ) :
2016-09-30 23:49:15 -07:00
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
}
2020-06-30 20:50:30 -04:00
def __str__ ( self ) :
2022-07-01 15:57:09 -04:00
sender_user = self . sending_user_id
if sender_user :
sender_user = User . objects . get ( pk = self . sending_user_id )
receiver_user = self . receiver_user_id
if receiver_user :
receiver_user = User . objects . get ( pk = self . receiver_user_id )
return " %s sent %s email to %s %s " % ( sender_user , self . email_type , receiver_user , receiver_user . profile if receiver_user else receiver_user )
2012-07-09 13:55:29 -07:00
@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 ) :
2020-06-11 05:45:48 -04:00
user = models . ForeignKey ( User , related_name = ' payments ' , on_delete = models . CASCADE )
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 )
2022-04-08 15:07:58 -04:00
refunded = models . BooleanField ( blank = True , null = True )
2012-12-03 14:35:21 -08:00
2020-06-30 20:50:30 -04:00
def __str__ ( self ) :
2022-04-08 15:07:58 -04:00
return " [ %s ] $ %s / %s %s " % ( self . payment_date . strftime ( " % Y- % m- %d " ) , self . payment_amount ,
self . payment_provider , " <REFUNDED> " if self . refunded else " " )
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 ,
2022-04-08 15:27:40 -04:00
' refunded ' : self . refunded ,
2013-02-15 13:47:45 -08:00
}
@classmethod
2016-04-18 14:58:49 -07:00
def report ( cls , months = 26 ) :
2018-04-17 19:13:44 -07:00
output = " "
def _counter ( start_date , end_date , output , payments = None ) :
2017-12-14 15:38:24 -08:00
if not payments :
payments = PaymentHistory . objects . filter ( payment_date__gte = start_date , payment_date__lte = end_date )
payments = payments . aggregate ( avg = Avg ( ' payment_amount ' ) ,
sum = Sum ( ' payment_amount ' ) ,
count = Count ( ' user ' ) )
2018-04-17 19:13:44 -07:00
output + = " %s - %02d - %02d - %s - %02d - %02d : \t $ %.2f \t $ %-6s \t %-4s \n " % (
2014-11-14 10:32:03 -08:00
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 ' ] )
2018-04-17 19:13:44 -07:00
return payments , output
2013-05-13 16:17:49 -07:00
2018-04-17 19:13:44 -07:00
output + = " \n Monthly Totals: \n "
2020-06-17 00:17:32 -04:00
for m in reversed ( list ( range ( months ) ) ) :
2015-04-16 21:15:30 -07:00
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 )
2018-04-17 19:13:44 -07:00
total , output = _counter ( start_date , end_date , output )
total = total [ ' sum ' ]
2015-04-16 22:29:36 -07:00
2018-04-17 19:13:44 -07:00
output + = " \n MTD Totals: \n "
2016-05-05 19:09:09 +08:00
years = datetime . datetime . now ( ) . year - 2009
2017-12-14 15:47:22 -08:00
this_mtd_avg = 0
last_mtd_avg = 0
2017-12-14 15:38:24 -08:00
last_mtd_sum = 0
this_mtd_sum = 0
last_mtd_count = 0
this_mtd_count = 0
2020-06-17 00:17:32 -04:00
for y in reversed ( list ( range ( years ) ) ) :
2016-05-05 19:09:09 +08:00
now = datetime . datetime . now ( )
start_date = datetime . datetime ( now . year , now . month , 1 ) - dateutil . relativedelta . relativedelta ( years = y )
2017-12-14 15:38:24 -08:00
end_date = now - dateutil . relativedelta . relativedelta ( years = y )
2016-05-05 19:09:09 +08:00
if end_date > now : end_date = now
2018-04-17 19:13:44 -07:00
count , output = _counter ( start_date , end_date , output )
2017-12-14 15:38:24 -08:00
if end_date . year != now . year :
2018-04-17 19:13:44 -07:00
last_mtd_avg = count [ ' avg ' ] or 0
last_mtd_sum = count [ ' sum ' ] or 0
2017-12-14 15:38:24 -08:00
last_mtd_count = count [ ' count ' ]
else :
2018-04-17 19:13:44 -07:00
this_mtd_avg = count [ ' avg ' ] or 0
this_mtd_sum = count [ ' sum ' ] or 0
2017-12-14 15:38:24 -08:00
this_mtd_count = count [ ' count ' ]
2016-05-05 19:09:09 +08:00
2018-04-17 19:13:44 -07:00
output + = " \n Current Month Totals: \n "
2016-05-05 19:05:06 +08:00
years = datetime . datetime . now ( ) . year - 2009
2017-12-14 15:47:22 -08:00
last_month_avg = 0
2017-12-14 15:38:24 -08:00
last_month_sum = 0
last_month_count = 0
2020-06-17 00:17:32 -04:00
for y in reversed ( list ( range ( years ) ) ) :
2016-05-05 19:05:06 +08:00
now = datetime . datetime . now ( )
start_date = datetime . datetime ( now . year , now . month , 1 ) - dateutil . relativedelta . relativedelta ( years = y )
2017-12-14 15:38:24 -08:00
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 :
2017-12-14 15:47:22 -08:00
payments = { ' avg ' : this_mtd_avg / ( max ( 1 , last_mtd_avg ) / float ( max ( 1 , last_month_avg ) ) ) ,
' sum ' : int ( round ( this_mtd_sum / ( max ( 1 , last_mtd_sum ) / float ( max ( 1 , last_month_sum ) ) ) ) ) ,
2017-12-14 15:43:26 -08:00
' count ' : int ( round ( this_mtd_count / ( max ( 1 , last_mtd_count ) / float ( max ( 1 , last_month_count ) ) ) ) ) }
2018-04-17 19:13:44 -07:00
_ , output = _counter ( start_date , end_date , output , payments = payments )
2017-12-14 15:38:24 -08:00
else :
2018-04-17 19:13:44 -07:00
count , output = _counter ( start_date , end_date , output )
2017-12-14 15:47:22 -08:00
last_month_avg = count [ ' avg ' ]
2017-12-14 15:38:24 -08:00
last_month_sum = count [ ' sum ' ]
last_month_count = count [ ' count ' ]
2016-05-05 19:05:06 +08:00
2018-04-17 19:13:44 -07:00
output + = " \n YTD Totals: \n "
2015-04-16 21:15:30 -07:00
years = datetime . datetime . now ( ) . year - 2009
2017-12-14 16:06:22 -08:00
this_ytd_avg = 0
last_ytd_avg = 0
this_ytd_sum = 0
last_ytd_sum = 0
this_ytd_count = 0
last_ytd_count = 0
2020-06-17 00:17:32 -04:00
for y in reversed ( list ( range ( years ) ) ) :
2015-04-16 21:15:30 -07:00
now = datetime . datetime . now ( )
start_date = datetime . datetime ( now . year , 1 , 1 ) - dateutil . relativedelta . relativedelta ( years = y )
2017-12-14 16:06:22 -08:00
end_date = now - dateutil . relativedelta . relativedelta ( years = y )
2018-04-17 19:13:44 -07:00
count , output = _counter ( start_date , end_date , output )
2017-12-14 16:12:45 -08:00
if end_date . year != now . year :
2018-04-17 19:13:44 -07:00
last_ytd_avg = count [ ' avg ' ] or 0
last_ytd_sum = count [ ' sum ' ] or 0
2017-12-14 16:06:22 -08:00
last_ytd_count = count [ ' count ' ]
else :
2018-04-17 19:13:44 -07:00
this_ytd_avg = count [ ' avg ' ] or 0
this_ytd_sum = count [ ' sum ' ] or 0
2017-12-14 16:07:10 -08:00
this_ytd_count = count [ ' count ' ]
2016-04-18 14:58:49 -07:00
2018-04-17 19:13:44 -07:00
output + = " \n Yearly Totals: \n "
2016-04-18 14:58:49 -07:00
years = datetime . datetime . now ( ) . year - 2009
2017-12-14 16:06:22 -08:00
last_year_avg = 0
last_year_sum = 0
last_year_count = 0
2018-04-17 19:16:55 -07:00
annual = 0
2020-06-17 00:17:32 -04:00
for y in reversed ( list ( range ( years ) ) ) :
2016-04-18 14:58:49 -07:00
now = datetime . datetime . now ( )
start_date = datetime . datetime ( now . year , 1 , 1 ) - dateutil . relativedelta . relativedelta ( years = y )
2017-12-14 16:06:22 -08:00
end_date = datetime . datetime ( now . year , 1 , 1 ) - dateutil . relativedelta . relativedelta ( years = y - 1 ) - datetime . timedelta ( seconds = 1 )
if end_date > now :
payments = { ' avg ' : this_ytd_avg / ( max ( 1 , last_ytd_avg ) / float ( max ( 1 , last_year_avg ) ) ) ,
' sum ' : int ( round ( this_ytd_sum / ( max ( 1 , last_ytd_sum ) / float ( max ( 1 , last_year_sum ) ) ) ) ) ,
' count ' : int ( round ( this_ytd_count / ( max ( 1 , last_ytd_count ) / float ( max ( 1 , last_year_count ) ) ) ) ) }
2018-04-17 19:16:55 -07:00
count , output = _counter ( start_date , end_date , output , payments = payments )
annual = count [ ' sum ' ]
2017-12-14 16:06:22 -08:00
else :
2018-04-17 19:13:44 -07:00
count , output = _counter ( start_date , end_date , output )
last_year_avg = count [ ' avg ' ] or 0
last_year_sum = count [ ' sum ' ] or 0
2017-12-14 16:07:10 -08:00
last_year_count = count [ ' count ' ]
2017-12-14 16:06:22 -08:00
2015-04-16 21:15:30 -07:00
total = cls . objects . all ( ) . aggregate ( sum = Sum ( ' payment_amount ' ) )
2018-04-17 19:13:44 -07:00
output + = " \n Total: $ %s \n " % total [ ' sum ' ]
2020-06-17 00:17:32 -04:00
print ( output )
2018-04-17 19:13:44 -07:00
2018-04-17 19:16:55 -07:00
return { ' annual ' : annual , ' output ' : output }
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 ' ] ,
}
2020-06-30 20:50:30 -04:00
def __str__ ( self ) :
2015-06-30 16:05:58 -07:00
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 ) :
2020-06-17 00:17:32 -04:00
u = str ( uuid . uuid4 ( ) )
2015-06-30 16:05:58 -07:00
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 ' ] ,
}
2020-06-30 20:50:30 -04:00
def __str__ ( self ) :
2014-11-06 14:55:27 -08:00
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 ' ] ,
}
2020-06-30 20:50:30 -04:00
def __str__ ( self ) :
2017-05-18 16:59:35 -07:00
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 ( )
2020-12-09 18:20:55 -05:00
2020-12-10 18:33:43 -05:00
class MDashboardRiver ( mongo . Document ) :
2021-04-07 15:32:11 -04:00
user_id = mongo . IntField ( unique_with = ( ) )
2020-12-10 18:33:43 -05:00
river_id = mongo . StringField ( )
river_side = mongo . StringField ( )
river_order = mongo . IntField ( )
meta = {
2021-04-07 15:32:11 -04:00
' collection ' : ' dashboard_river ' ,
' allow_inheritance ' : False ,
' indexes ' : [ ' user_id ' ,
{ ' fields ' : [ ' user_id ' , ' river_id ' , ' river_side ' , ' river_order ' ] ,
' unique ' : True ,
} ] ,
2020-12-10 18:33:43 -05:00
' ordering ' : [ ' river_order ' ]
}
2020-12-09 18:20:55 -05:00
2021-01-13 15:40:31 -05:00
def __str__ ( self ) :
2021-02-05 17:18:29 -05:00
try :
u = User . objects . get ( pk = self . user_id )
except User . DoesNotExist :
u = " <missing user> "
2021-01-13 15:40:31 -05:00
return f " { u } ( { self . river_side } / { self . river_order } ): { self . river_id } "
2020-12-09 18:20:55 -05:00
def canonical ( self ) :
return {
2020-12-10 18:33:43 -05:00
' river_id ' : self . river_id ,
' river_side ' : self . river_side ,
' river_order ' : self . river_order ,
2020-12-09 18:20:55 -05:00
}
@classmethod
2021-03-25 20:17:13 -04:00
def get_user_rivers ( cls , user_id ) :
2021-01-13 15:40:31 -05:00
return cls . objects ( user_id = user_id )
2020-12-09 18:20:55 -05:00
2022-05-17 13:45:51 -04:00
@classmethod
def remove_river ( cls , user_id , river_side , river_order ) :
try :
river = cls . objects . get ( user_id = user_id , river_side = river_side , river_order = river_order )
except cls . DoesNotExist :
return
river . delete ( )
for r , river in enumerate ( cls . objects . filter ( user_id = user_id , river_side = river_side ) ) :
if river . river_order != r :
logging . debug ( f " ---> Rebalancing { river } from { river . river_order } to { r } " )
river . river_order = r
river . save ( )
2020-12-09 18:20:55 -05:00
@classmethod
2020-12-10 18:33:43 -05:00
def save_user ( cls , user_id , river_id , river_side , river_order ) :
2021-04-07 15:32:11 -04:00
try :
river = cls . objects . get ( user_id = user_id , river_side = river_side , river_order = river_order )
except cls . DoesNotExist :
river = None
2022-05-17 13:45:51 -04:00
2020-12-10 18:33:43 -05:00
if not river :
2021-03-25 20:17:13 -04:00
river = cls . objects . create ( user_id = user_id , river_id = river_id ,
2022-05-17 13:45:51 -04:00
river_side = river_side , river_order = river_order )
2020-12-09 18:20:55 -05:00
2021-01-13 15:40:31 -05:00
river . river_id = river_id
2020-12-10 18:33:43 -05:00
river . river_side = river_side
river . river_order = river_order
river . save ( )
2020-12-09 18:20:55 -05:00
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 ( )
2020-06-20 00:08:18 -04:00
r . zadd ( cls . KEY , { user_id : now } )
2013-05-13 16:17:49 -07:00
@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 )
2021-03-25 16:28:50 -04:00
if position is None :
2021-03-25 16:29:42 -04:00
return - 1
2013-05-13 18:03:17 -07:00
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