def rerun_complete_revivals(debug=False): """ Re-run Revivals with status = COMPLETE to keep users engaged """ t0 = datetime.now() total_revival, total_mail = 0, 0 three_days_ago = timezone.now() - timedelta(days=3) for revival in Revival.objects.select_related('service').filter( status=COMPLETE, is_active=True): try: refreshed = Revival.objects.get(pk=revival.id) if refreshed.is_running: continue refreshed.is_running = True refreshed.save() total_revival += 1 except Revival.DoesNotExist: continue try: mail_renderer = import_by_path(revival.mail_renderer) kwargs = {} if revival.get_kwargs: get_kwargs = import_by_path(revival.get_kwargs) kwargs = get_kwargs(revival) except: revival.is_running = False revival.save() logger.error("Error when starting revival %s for %s" % (revival.mail_renderer, revival.service), exc_info=True) continue service = revival.service db = revival.service.database add_database(db) balance = Balance.objects.using(WALLETS_DB_ALIAS).get( service_id=service.id) if balance.mail_count == 0: revival.is_running = False revival.save() try: notify_for_empty_messaging_credit(service, balance) except: logger.error( "Failed to notify %s for empty messaging credit." % service, exc_info=True) continue if 0 < balance.mail_count < LOW_MAIL_LIMIT: try: notify_for_low_messaging_credit(service, balance) except: logger.error("Failed to notify %s for low messaging credit." % service, exc_info=True) tk = revival.model_name.split('.') model = get_model(tk[0], tk[1]) try: obj = model._default_manager.using(db).get(pk=revival.object_id) except ObjectDoesNotExist: revival.is_running = False revival.save() continue try: profile_tag = ProfileTag.objects.using(db).get( pk=revival.profile_tag_id) except: revival.is_running = False revival.save() continue set_counters_many(profile_tag) revival_local = Revival.objects.using(db).get(pk=revival.id) target_queryset = revival_local.target_set.select_related( 'member').filter(revived_on__lte=three_days_ago, revival_count__lt=MAX_AUTO_REWARDS) if target_queryset.count() == 0: revival.is_running = False revival.save() continue revival.run_on = timezone.now() revival.status = STARTED revival.save() connection = mail.get_connection() try: connection.open() except: revival.is_running = False revival.save() logger.error(u"Connexion error", exc_info=True) continue logger.debug("Running rerun_complete_revivals() %s for %s" % (revival.mail_renderer, revival.service)) for target in target_queryset.order_by('updated_on')[:MAX_BATCH_SEND]: if not debug and balance.mail_count == 0: revival.is_running = False revival.save() try: notify_for_empty_messaging_credit(service, balance) except: logger.error( "Failed to notify %s for empty messaging credit." % service, exc_info=True) break member = target.member if debug and not member.is_superuser: continue if member.language: activate(member.language) else: activate('en') if getattr(settings, 'UNIT_TESTING', False): sender, subject, html_content = mail_renderer( target, obj, revival, **kwargs) else: try: sender, subject, html_content = mail_renderer( target, obj, revival, **kwargs) except: logger.error( "Could not render mail for member %s, Revival %s, Obj: %s" % (member.email, revival.mail_renderer, str(obj)), exc_info=True) continue if not html_content: continue if debug: subject = 'Test remind - ' + subject msg = XEmailMessage(subject, html_content, sender, [member.email]) msg.content_subtype = "html" msg.type = XEmailObject.REVIVAL try: with transaction.atomic(using=WALLETS_DB_ALIAS): if not debug: balance.mail_count -= 1 balance.save() if msg.send(): target.revival_count += 1 target.revived_on = t0 target.save() total_mail += 1 increment_history_field(profile_tag, 'smart_revival_history') else: logger.error("Member %s not notified for Content %s" % (member.email, str(obj)), exc_info=True) except: logger.error("Member %s not notified for Content %s" % (member.email, str(obj)), exc_info=True) revival.is_running = False revival.progress += 1 revival.save() try: connection.close() except: revival.is_running = False revival.save() diff = datetime.now() - t0 logger.debug( "rerun_complete_revivals() run %d revivals. %d mails sent in %s" % (total_revival, total_mail, diff))
def invite_member(service, member): sender = '%s <no-reply@%s>' % (service.project_name, service.domain) config = service.config try: invitation_message = config.__getattribute__('invitation_message') except AttributeError: return template_name = 'revival/mails/suggest_create_account.html' kwargs = get_join_reward_pack_list(service=service) join_reward_pack_list = kwargs['reward_pack_list'] if join_reward_pack_list: subject = _("Join us on ikwen and earn free coupons." % service.project_name) email_type = XEmailObject.REWARDING else: subject = _("Join our community on ikwen.") email_type = XEmailObject.REVIVAL if invitation_message or join_reward_pack_list: with transaction.atomic(using=WALLETS_DB_ALIAS): from echo.models import Balance from echo.utils import LOW_MAIL_LIMIT, notify_for_low_messaging_credit, notify_for_empty_messaging_credit balance, update = Balance.objects.using( WALLETS_DB_ALIAS).get_or_create(service_id=service.id) if 0 < balance.mail_count < LOW_MAIL_LIMIT: try: notify_for_low_messaging_credit(service, balance) except: logger.error( "Failed to notify %s for low messaging credit." % service, exc_info=True) if balance.mail_count == 0 and not getattr(settings, 'UNIT_TESTING', False): try: notify_for_empty_messaging_credit(service, balance) except: logger.error( "Failed to notify %s for empty messaging credit." % service, exc_info=True) return invitation_message = invitation_message.replace( '$client', member.first_name) extra_context = { 'member_name': member.first_name, 'join_reward_pack_list': join_reward_pack_list, 'invitation_message': invitation_message } try: html_content = get_mail_content(subject, service=service, template_name=template_name, extra_context=extra_context) msg = XEmailMessage(subject, html_content, sender, [member.email]) msg.content_subtype = "html" msg.type = email_type if not getattr(settings, 'UNIT_TESTING', False): balance.mail_count -= 1 balance.save() Thread(target=lambda m: m.send(), args=(msg, )).start() notice = "%s: Invitation sent message to member after ghost registration attempt" % service.project_name_slug logger.error(notice, exc_info=True) except: notice = "%s: Failed to send invite message to member after ghost registration attempt" % service.project_name_slug logger.error(notice, exc_info=True)
def notify_profiles(debug=False): """ Cron job that revive users by mail. Must be configured to run with a settings file having 'umbrella' as default database. :return: """ t0 = datetime.now() seven_hours_ago = t0 - timedelta(hours=7) total_revival, total_mail = 0, 0 for revival in Revival.objects.select_related('service').exclude( status=COMPLETE, is_active=False): try: refreshed = Revival.objects.get(pk=revival.id) if refreshed.is_running: continue refreshed.is_running = True refreshed.save() total_revival += 1 except Revival.DoesNotExist: continue try: mail_renderer = import_by_path(revival.mail_renderer) kwargs = {} if revival.get_kwargs: get_kwargs = import_by_path(revival.get_kwargs) kwargs = get_kwargs(revival) except: revival.is_running = False revival.save() logger.error("Error when starting revival %s for %s" % (revival.mail_renderer, revival.service), exc_info=True) continue service = revival.service db = service.database add_database(db) balance = Balance.objects.using(WALLETS_DB_ALIAS).get( service_id=service.id) if balance.mail_count == 0: revival.is_running = False revival.save() try: notify_for_empty_messaging_credit(service, balance) except: logger.error( "Failed to notify %s for empty messaging credit." % service, exc_info=True) continue if 0 < balance.mail_count < LOW_MAIL_LIMIT: try: notify_for_low_messaging_credit(service, balance) except: logger.error("Failed to notify %s for low messaging credit." % service, exc_info=True) tk = revival.model_name.split('.') model = get_model(tk[0], tk[1]) try: obj = model._default_manager.using(db).get(pk=revival.object_id) except ObjectDoesNotExist: revival.is_running = False revival.save() continue try: profile_tag = ProfileTag.objects.using(db).get( pk=revival.profile_tag_id) except: revival.is_running = False revival.save() continue if revival.status != PENDING: revival.is_running = False revival.save() continue set_counters(profile_tag) revival_local = Revival.objects.using(db).get(pk=revival.id) if debug: member_queryset = Member.objects.using(db).filter( is_superuser=True) else: member_queryset = Member.objects.using(db).filter( date_joined__lte=seven_hours_ago) total = member_queryset.count() chunks = total / 500 + 1 target_count = 0 for i in range(chunks): start = i * 500 finish = (i + 1) * 500 for member in member_queryset.order_by( 'date_joined')[start:finish]: try: profile = MemberProfile.objects.using(db).get( member=member) except MemberProfile.DoesNotExist: ref_tag = ProfileTag.objects.using(db).get(slug=REFERRAL) profile = MemberProfile.objects.using(db).create( member=member, tag_fk_list=[ref_tag.id]) if revival.profile_tag_id in profile.tag_fk_list: if debug: tag = ProfileTag.objects.using(db).get( pk=revival.profile_tag_id) print "Profiles matching on %s for member %s" % ( tag, member) if member.email: Target.objects.using(db).get_or_create( revival=revival_local, member=member) target_count += 1 if target_count == 0: revival.is_running = False revival.save() continue revival.run_on = datetime.now() revival.status = STARTED revival.total = revival_local.target_set.all().count() revival.save() connection = mail.get_connection() try: connection.open() except: revival.is_running = False revival.save() logger.error(u"Connexion error", exc_info=True) continue logger.debug("Running notify_profiles() %s for %s" % (revival.mail_renderer, revival.service)) for target in revival_local.target_set.select_related('member').filter( notified=False)[:MAX_BATCH_SEND]: if not debug and balance.mail_count == 0: revival.is_running = False revival.save() try: notify_for_empty_messaging_credit(service, balance) except: logger.error( "Failed to notify %s for empty messaging credit." % service, exc_info=True) break member = target.member if member.language: activate(member.language) else: activate('en') if getattr(settings, 'UNIT_TESTING', False): sender, subject, html_content = mail_renderer( target, obj, revival, **kwargs) else: try: sender, subject, html_content = mail_renderer( target, obj, revival, **kwargs) except: logger.error( "Could not render mail for member %s, Revival %s, Obj: %s" % (member.email, revival.mail_renderer, str(obj)), exc_info=True) continue if not html_content: continue if debug: subject = 'Test - ' + subject msg = XEmailMessage(subject, html_content, sender, [member.email]) msg.content_subtype = "html" msg.type = XEmailObject.REVIVAL try: with transaction.atomic(using=WALLETS_DB_ALIAS): if not debug: balance.mail_count -= 1 balance.save() if msg.send(): target.revival_count += 1 target.notified = True target.revived_on = t0 target.save() total_mail += 1 increment_history_field(profile_tag, 'smart_revival_history') else: logger.error("Member %s not notified for Content %s" % (member.email, str(obj)), exc_info=True) except: logger.error("Member %s not notified for Content %s" % (member.email, str(obj)), exc_info=True) revival.progress += 1 revival.save() revival.is_running = False if revival.progress > 0 and revival.progress >= revival.total: revival.status = COMPLETE revival.save() try: connection.close() except: revival.is_running = False revival.save() diff = datetime.now() - t0 logger.debug("notify_profiles() run %d revivals. %d mails sent in %s" % (total_revival, total_mail, diff))
def notify_profiles(debug=False): t0 = datetime.now() total_revival, total_mail, total_sms = 0, 0, 0 logger.debug("Starting cyclic revival") today = t0.date() queryset = CyclicRevival.objects.select_related('service')\ .filter(next_run_on=today, hour_of_sending=t0.hour, end_on__gt=today, is_active=True) for revival in queryset: try: refreshed = CyclicRevival.objects.get(pk=revival.id) if refreshed.is_running: continue refreshed.is_running = True refreshed.save() total_revival += 1 except CyclicRevival.DoesNotExist: continue service = revival.service db = service.database add_database(db) balance = Balance.objects.using(WALLETS_DB_ALIAS).get( service_id=service.id) if balance.mail_count == 0 and balance.sms_count == 0: try: notify_for_empty_messaging_credit(service, balance) except: revival.is_running = False revival.save() logger.error( "Failed to notify %s for empty messaging credit." % service, exc_info=True) continue if 0 < balance.mail_count < LOW_MAIL_LIMIT or 0 < balance.sms_count < LOW_SMS_LIMIT: try: notify_for_low_messaging_credit(service, balance) except: revival.is_running = False revival.save() logger.error("Failed to notify %s for low messaging credit." % service, exc_info=True) label = get_sms_label(service.config) notified_empty_mail_credit = False notified_empty_sms_credit = False if debug: member_queryset = Member.objects.using(db).filter( is_superuser=True) else: member_queryset = Member.objects.using(db).all() total = member_queryset.count() try: profile_tag = ProfileTag.objects.using(db).get( pk=revival.profile_tag_id) except ProfileTag.DoesNotExist: revival.delete() continue set_counters(profile_tag) revival_local = CyclicRevival.objects.using(db).get(pk=revival.id) chunks = total / 500 + 1 for i in range(chunks): start = i * 500 finish = (i + 1) * 500 for member in member_queryset.order_by( 'date_joined')[start:finish]: try: profile = MemberProfile.objects.using(db).get( member=member) except MemberProfile.DoesNotExist: continue match = set(profile.tag_fk_list) & {profile_tag.id} if len(match) > 0: if member.email: CyclicTarget.objects.using(db).get_or_create( revival=revival_local, member=member) revival.set_next_run_date() connection = mail.get_connection() try: connection.open() except: logger.error(u"Connexion error", exc_info=True) break logger.debug("Running revival %s for %s" % (revival.mail_subject, revival.service)) for target in revival_local.cyclictarget_set.select_related('member'): member = target.member if member.language: activate(member.language) else: activate('en') subject = revival.mail_subject message = revival.mail_content.replace('$client', member.first_name) sender = '%s <no-reply@%s>' % (service.project_name, service.domain) try: currency = Currency.objects.using(using=db).get(is_base=True) except Currency.DoesNotExist: currency = None product_list = [] if service.app.slug == 'kakocase': product_list = Product.objects.using(db).filter( pk__in=revival.items_fk_list) extra_context = { 'revival': revival, 'currency': currency, 'media_url': getattr(settings, 'CLUSTER_MEDIA_URL') + service.project_name_slug + '/', 'product_list': product_list } try: html_content = get_mail_content( subject, message, template_name='revival/mails/default.html', service=service, extra_context=extra_context) except: logger.error( "Could not render mail for member %s, Cyclic revival on %s" % (member.username, profile_tag), exc_info=True) break msg = XEmailMessage(subject, html_content, sender, [member.email]) msg.content_subtype = "html" msg.type = XEmailObject.REVIVAL if balance.mail_count == 0 and not notified_empty_mail_credit: notify_for_empty_messaging_credit(service, balance) notified_empty_mail_credit = True else: try: with transaction.atomic(using=WALLETS_DB_ALIAS): if not debug: balance.mail_count -= 1 balance.save() if msg.send(): target.revival_count += 1 target.save() total_mail += 1 increment_history_field( profile_tag, 'cyclic_revival_mail_history') else: logger.error( "Cyclic revival with subject %s not sent for member %s" % (subject, member.email), exc_info=True) except: logger.error( "Critical error in revival %s when processing Mail sending for member %s" % (revival.id, member.email), exc_info=True) if revival.sms_text: if balance.sms_count == 0 and not notified_empty_sms_credit: notify_for_empty_messaging_credit(service, balance) notified_empty_sms_credit = True else: sms_text = revival.sms_text.replace( '$client', member.first_name) page_count = count_pages(sms_text) try: with transaction.atomic(using=WALLETS_DB_ALIAS): balance.sms_count -= page_count balance.save() send_sms(recipient=member.phone, text=sms_text, fail_silently=False) total_sms += 1 increment_history_field( profile_tag, 'cyclic_revival_sms_history') SMSObject.objects.create(recipient=member.phone, text=sms_text, label=label) except: logger.error( "Critical error in revival %s when processing SMS sending for member %s" % (revival.id, member.email), exc_info=True) if balance.mail_count == 0 and balance.sms_count == 0: break revival.is_running = False revival.save() try: connection.close() except: pass diff = datetime.now() - t0 logger.debug("%d revivals run. %d mails and %d SMS sent in %s" % (total_revival, total_mail, total_sms, diff))