Exemple #1
0
def generate_meeting_reminder(meeting):
    if Reminder.objects.filter(study_group_meeting=meeting).exists():
        reminder = Reminder.objects.get(study_group_meeting=meeting)
        reminder.edited_by_facilitator = False
    else:
        reminder = Reminder()
        reminder.study_group = meeting.study_group
        reminder.study_group_meeting = meeting

    context = {
        'facilitator': meeting.study_group.facilitator,
        'study_group': meeting.study_group,
        'next_meeting': meeting,
        'reminder': reminder,
    }
    timezone.activate(pytz.timezone(meeting.study_group.timezone))
    with use_language(meeting.study_group.language):
        reminder.email_subject = render_to_string_ctx(
            'studygroups/email/meeting_reminder-subject.txt',
            context).strip('\n')
        reminder.email_body = render_to_string_ctx(
            'studygroups/email/meeting_reminder.html', context)
        reminder.sms_body = render_to_string_ctx(
            'studygroups/email/meeting_reminder-sms.txt', context)
    # TODO - handle SMS reminders that are too long
    if len(reminder.sms_body) > 160:
        logger.error('SMS body too long: ' + reminder.sms_body)
    reminder.sms_body = reminder.sms_body[:160]
    reminder.save()
    return reminder
    def send_optout_message(self):
        email = self.cleaned_data.get('email')
        mobile = self.cleaned_data.get('mobile')
        if email:
            for application in Application.objects.active().filter(email__iexact=email):
                # send opt-out email
                context = { 'application': application }
                subject = render_to_string_ctx('studygroups/email/optout_confirm-subject.txt', context).strip('\n')
                html_body = render_html_with_css('studygroups/email/optout_confirm.html', context)
                text_body = render_to_string_ctx('studygroups/email/optout_confirm.txt', context)
                notification = EmailMultiAlternatives(subject, text_body, settings.DEFAULT_FROM_EMAIL, [application.email])
                notification.attach_alternative(html_body, 'text/html')
                notification.send()


        # Find all signups with mobile with email and delete
        if mobile:
            applications = Application.objects.active().filter(mobile=mobile)
            if email:
                # don't send text to applications with a valid email in opt out form
                applications = applications.exclude(email__iexact=email)
            for application in applications:
                # This remains for old signups without email address
                context = { 'application': application }
                message = render_to_string_ctx('studygroups/email/optout_confirm_text.txt', context)
                try:
                    send_message(application.mobile, message)
                except twilio.TwilioRestException as e:
                    logger.exception("Could not send text message to %s", to, exc_info=e)
                application.delete()
def send_email_confirm_email(user):
    context = {
        "user": user,
        "profile": user.profile,
        "uid": urlsafe_base64_encode(force_bytes(user.pk)),
        "token": generate_user_token(user),
    }

    subject_template = 'custom_registration/email_confirm-subject.txt'
    email_template = 'custom_registration/email_confirm.txt'
    html_email_template = 'custom_registration/email_confirm.html'

    subject = render_to_string_ctx(subject_template, context).strip(' \n')
    text_body = render_to_string_ctx(email_template, context)
    html_body = render_html_with_css(html_email_template, context)

    to = [user.email]
    email = EmailMultiAlternatives(
        subject,
        text_body,
        settings.DEFAULT_FROM_EMAIL,
        to,
    )
    email.attach_alternative(html_body, 'text/html')
    email.send()
def send_team_invitation_email(team, email, organizer):
    """ Send email to new or existing facilitators """
    """ organizer should be a User object """
    user_qs = User.objects.filter(email__iexact=email)
    context = {
        "team": team,
        "organizer": organizer,
    }

    if user_qs.count() == 0:
        # invite user to join
        subject = render_to_string_ctx(
            'studygroups/email/new_facilitator_invite-subject.txt',
            context).strip('\n')
        html_body = render_html_with_css(
            'studygroups/email/new_facilitator_invite.html', context)
        text_body = render_to_string_ctx(
            'studygroups/email/new_facilitator_invite.txt', context)
    else:
        context['user'] = user_qs.get()
        subject = render_to_string_ctx(
            'studygroups/email/team_invite-subject.txt', context).strip('\n')
        html_body = render_html_with_css('studygroups/email/team_invite.html',
                                         context)
        text_body = render_to_string_ctx('studygroups/email/team_invite.txt',
                                         context)

    to = [email]
    from_ = organizer.email

    msg = EmailMultiAlternatives(subject, text_body, from_, to)
    msg.attach_alternative(html_body, 'text/html')
    msg.send()
def render_email_templates(template_base, context):
    """ use template_base to render subject, text and html for email """
    # TODO make text template optional - use html to text when not specified
    subject = render_to_string_ctx('{}-subject.txt'.format(template_base),
                                   context).strip()
    txt = render_to_string_ctx('{}.txt'.format(template_base), context)
    html = render_html_with_css('{}.html'.format(template_base), context)
    return subject, txt, html
def generate_reminder(study_group):
    now = timezone.now()
    next_meeting = study_group.next_meeting()
    # TODO reminder generation code should be moved to models, only sending facilitator notification should be here
    if next_meeting and next_meeting.meeting_datetime(
    ) - now < datetime.timedelta(days=4):
        # check if a notifcation already exists for this meeting
        if not Reminder.objects.filter(
                study_group=study_group,
                study_group_meeting=next_meeting).exists():
            reminder = Reminder()
            reminder.study_group = study_group
            reminder.study_group_meeting = next_meeting
            context = {
                'facilitator': study_group.facilitator,
                'study_group': study_group,
                'next_meeting': next_meeting,
                'reminder': reminder,
            }
            previous_meeting = study_group.meeting_set.filter(
                meeting_date__lt=next_meeting.meeting_date).order_by(
                    'meeting_date').last()
            if previous_meeting and previous_meeting.feedback_set.first():
                context['feedback'] = previous_meeting.feedback_set.first()
            timezone.activate(pytz.timezone(study_group.timezone))
            with use_language(study_group.language):
                reminder.email_subject = render_to_string_ctx(
                    'studygroups/email/reminder-subject.txt',
                    context).strip('\n')
                reminder.email_body = render_to_string_ctx(
                    'studygroups/email/reminder.txt', context)
                reminder.sms_body = render_to_string_ctx(
                    'studygroups/email/sms.txt', context)
            # TODO - handle SMS reminders that are too long
            if len(reminder.sms_body) > 160:
                logger.error('SMS body too long: ' + reminder.sms_body)
            reminder.sms_body = reminder.sms_body[:160]
            reminder.save()

            with use_language(reminder.study_group.language):
                facilitator_notification_subject = _(
                    'A reminder for %(studygroup_name)s was generated' %
                    {"studygroup_name": study_group.name})
                facilitator_notification_html = render_html_with_css(
                    'studygroups/email/reminder_notification.html', context)
                facilitator_notification_txt = render_to_string_ctx(
                    'studygroups/email/reminder_notification.txt', context)

            timezone.deactivate()
            to = [study_group.facilitator.email]
            notification = EmailMultiAlternatives(
                facilitator_notification_subject, facilitator_notification_txt,
                settings.DEFAULT_FROM_EMAIL, to)
            notification.attach_alternative(facilitator_notification_html,
                                            'text/html')
            notification.send()
def render_html_with_css(template, context):
    html = render_to_string_ctx(template, context)
    base_url = f"{settings.PROTOCOL}://{settings.DOMAIN}"
    html_with_inlined_css = Premailer(html,
                                      base_url=base_url,
                                      disable_validation=True).transform()
    return html_with_inlined_css
def send_community_digest(start_date, end_date):

    context = community_digest_data(start_date, end_date)

    chart_data = {
        "meetings_chart":
        charts.LearningCircleMeetingsChart(
            end_date.date()).generate(output="png"),
        "countries_chart":
        charts.LearningCircleCountriesChart(
            start_date.date(), end_date.date()).generate(output="png"),
        "top_topics_chart":
        charts.TopTopicsChart(
            end_date.date(),
            context['studygroups_that_met']).generate(output="png"),
    }

    context.update(chart_data)

    subject = render_to_string_ctx(
        'studygroups/email/community_digest-subject.txt', context)
    html_body = render_html_with_css('studygroups/email/community_digest.html',
                                     context)
    text_body = html_body_to_text(html_body)
    to = [settings.COMMUNITY_DIGEST_EMAIL]

    msg = EmailMultiAlternatives(subject, text_body,
                                 settings.DEFAULT_FROM_EMAIL, to)
    msg.attach_alternative(html_body, 'text/html')
    msg.send()
def handle_new_study_group_creation(sender, instance, created, **kwargs):
    if not created:
        return

    study_group = instance
    context = {
        'study_group': study_group,
    }
    subject = render_to_string_ctx(
        'studygroups/email/learning_circle_created-subject.txt',
        context).strip(' \n')
    html_body = render_html_with_css(
        'studygroups/email/learning_circle_created.html', context)
    text_body = html_body_to_text(html_body)

    # on all learning circles, CC p2pu
    cc = [settings.TEAM_EMAIL]
    # if the user is part of a team, send to the organizer(s)
    cc += [o.email for o in get_study_group_organizers(study_group)]
    # if there is a question, send to the welcoming comitte
    if study_group.facilitator_concerns:
        cc += [settings.COMMUNITY_MANAGER]

    notification = EmailMultiAlternatives(
        subject,
        text_body,
        settings.DEFAULT_FROM_EMAIL, [study_group.facilitator.email],
        cc=cc,
        reply_to=[study_group.facilitator.email] + cc)
    notification.attach_alternative(html_body, 'text/html')
    notification.send()
Exemple #10
0
def send_announcement(sender, subject, body_text, body_html):
    """ Send message to all users that opted-in for the community email list """

    # Check that account settings link is present in message
    account_settings_url = settings.PROTOCOL + '://' + settings.DOMAIN + reverse(
        'account_settings')

    # check if account settings URL is in HTML body
    if not re.search(account_settings_url, body_html):
        settings_link = render_to_string_ctx(
            'announce/account_settings_email_link.html')
        # if there is a body tag, add link before the closing body tag
        if re.search('</body>', body_html):
            settings_link = settings_link + '</body>'
            body_html = re.sub(r'</body>', settings_link, body_html)
        else:
            settings_link = settings_link
            body_html = body_html + settings_link

    # check if account settings URL is in text body
    if not re.search(account_settings_url, body_text):
        settings_link = _(
            'If you would no longer like to receive announcements from P2PU, you can update your account preferences at {url}'
        ).format(url=account_settings_url)
        body_text = '\n'.join([body_text, settings_link])

    # Get list of users who opted-in to communications
    users = User.objects.filter(is_active=True,
                                profile__communication_opt_in=True)
    batch_size = 500

    # send in batches of batch_size
    url = 'https://api.mailgun.net/v3/{}/messages'.format(
        settings.MAILGUN_DOMAIN)
    auth = HTTPBasicAuth('api', settings.MAILGUN_API_KEY)

    for index in range(0, len(users), batch_size):
        to = users[index:index + 500]

        post_data = [
            ('from', sender),
            ('subject', subject),
            ('text', body_text),
            ('html', body_html),
            ('o:tracking', 'yes'),
            ('o:tracking-clicks', 'htmlonly'),
            ('o:tracking-opens', 'yes'),
            ('o:tag', 'announce'),
        ]

        post_data += [('to', u.email) for u in to]
        # Add recipient variables to ensure mailgun sends messages individually and
        # not with everyone in the to field.
        post_data += [('recipient-variables',
                       json.dumps({u.email: {}
                                   for u in to}))]
        resp = requests.post(url, auth=auth, data=post_data)
        if resp.status_code != 200:
            logger.error('Could not send mailgun batch email')
def send_meeting_change_notification(old_meeting, new_meeting):
    study_group = new_meeting.study_group
    to = [
        su.email for su in study_group.application_set.active().filter(
            accepted_at__isnull=False).exclude(email='')
    ]
    context = {
        'old_meeting': old_meeting,
        'new_meeting': new_meeting,
        'learning_circle': study_group,
    }

    with use_language(study_group.language), timezone.override(
            pytz.timezone(study_group.timezone)):
        subject = render_to_string_ctx(
            'studygroups/email/meeting_changed-subject.txt',
            context).strip('\n')
        html_body = render_to_string_ctx(
            'studygroups/email/meeting_changed.html', context)
        text_body = html_body_to_text(html_body)
        sms_body = render_to_string_ctx(
            'studygroups/email/meeting_changed-sms.txt', context).strip('\n')

    notification = EmailMultiAlternatives(subject,
                                          text_body,
                                          settings.DEFAULT_FROM_EMAIL,
                                          bcc=to)
    notification.attach_alternative(html_body, 'text/html')
    try:
        notification.send()
    except Exception as e:
        logger.exception('Could not send meeting change notification',
                         exc_info=e)

    applications = study_group.application_set.active().filter(
        accepted_at__isnull=False).exclude(mobile='')
    applications = applications.filter(mobile_opt_out_at__isnull=True)
    tos = [su.mobile for su in applications]
    for to in tos:
        try:
            send_message(to, sms_body)
        except TwilioRestException as e:
            logger.exception("Could not send text message to %s",
                             to,
                             exc_info=e)
    def form_valid(self, form):
        # send notification to organizers about feedback
        to = [] #TODO should we send this to someone if the facilitators is not part of a team? - for now, don't worry, this notification is likely to be removed.
        meeting = get_object_or_404(Meeting, pk=self.kwargs.get('study_group_meeting_id'))
        organizers = get_study_group_organizers(meeting.study_group)
        if organizers:
            to = [o.email for o in organizers]

        context = {
            'feedback': form.save(commit=False),
            'study_group_meeting': self.get_initial()['study_group_meeting']
        }
        subject = render_to_string_ctx('studygroups/email/feedback-submitted-subject.txt', context).strip('\n')
        html_body = render_to_string_ctx('studygroups/email/feedback-submitted.html', context)
        text_body = render_to_string_ctx('studygroups/email/feedback-submitted.txt', context)
        notification = EmailMultiAlternatives(subject, text_body, settings.DEFAULT_FROM_EMAIL, to)
        notification.attach_alternative(html_body, 'text/html')
        notification.send()
        return super(FeedbackCreate, self).form_valid(form)
    def send_organization_guide(self):
        email = self.cleaned_data['email']
        first_name = self.cleaned_data['first_name']
        last_name = self.cleaned_data['last_name']
        location = self.cleaned_data['location']

        context = {
            'first_name': first_name,
            'last_name': last_name,
            'location': location,
        }
        to = [email, settings.TEAM_EMAIL]
        subject = render_to_string_ctx('studygroups/email/organizer_guide-subject.txt', context).strip('\n')
        html_body = render_to_string_ctx('studygroups/email/organizer_guide.html', context)
        text_body = render_to_string_ctx('studygroups/email/organizer_guide.txt', context)
        email = EmailMultiAlternatives(subject, text_body, settings.DEFAULT_FROM_EMAIL, to)
        email.attach_alternative(html_body, 'text/html')
        email.attach_file("static/files/organizer_guide.pdf")
        email.send()
Exemple #14
0
def send_new_event_notification(event):
    to = User.objects.filter(is_staff=True).values_list('email', flat=True)
    context = {"event": event}

    subject = render_to_string_ctx('community_calendar/new_event-subject.txt',
                                   context).strip('\n')
    html_body = render_to_string_ctx('community_calendar/new_event.html',
                                     context)
    text_body = html_body_to_text(html_body)

    notification = EmailMultiAlternatives(subject,
                                          text_body,
                                          settings.DEFAULT_FROM_EMAIL,
                                          bcc=to)
    notification.attach_alternative(html_body, 'text/html')
    try:
        notification.send()
    except Exception as e:
        logger.exception('Could not sent event notification', exc_info=e)
Exemple #15
0
def handle_new_application(sender, instance, created, **kwargs):
    """ Send welcome message to learner introducing them to their facilitator """
    if not created:
        return

    application = instance

    # get a random piece of advice
    # TODO remove unused advice logic
    advice = None
    if application.study_group.language == 'en':
        advice = Advice.objects.order_by('?').first()

    # activate language and timezone for message reminder
    with use_language(application.study_group.language), timezone.override(pytz.timezone(application.study_group.timezone)):
        # Send welcome message to learner
        learner_signup_subject = render_to_string_ctx(
            'studygroups/email/learner_signup-subject.txt', {
                'application': application,
                'advice': advice,
            }
        ).strip('\n')

        learner_signup_html = render_html_with_css(
            'studygroups/email/learner_signup.html', {
                'application': application,
                'advice': advice,
            }
        )
        learner_signup_body = html_body_to_text(learner_signup_html)

    to = [application.email]
    # CC facilitator and put in reply-to
    welcome_message = EmailMultiAlternatives(
        learner_signup_subject,
        learner_signup_body,
        settings.DEFAULT_FROM_EMAIL,
        to,
        cc=[application.study_group.facilitator.email],
        reply_to=[application.study_group.facilitator.email]
    )
    welcome_message.attach_alternative(learner_signup_html, 'text/html')
    welcome_message.send()
def send_new_user_email(user):
    context = {
        "user": user,
    }

    subject_template = 'custom_registration/new_user_confirmed-subject.txt'
    html_email_template = 'custom_registration/new_user_confirmed.html'

    subject = render_to_string_ctx(subject_template, context).strip(' \n')
    html_body = render_html_with_css(html_email_template, context)
    text_body = html_body_to_text(html_body)

    to = [user.email]
    email = EmailMultiAlternatives(
        subject,
        text_body,
        settings.DEFAULT_FROM_EMAIL,
        to,
    )
    email.attach_alternative(html_body, 'text/html')
    email.send()
def _send_learning_circle_insights(study_group):
    timezone.deactivate()

    goals_met_chart = charts.GoalsMetChart(study_group)
    report_path = reverse('studygroups_final_report',
                          kwargs={'study_group_id': study_group.id})
    recipients = study_group.application_set.active().values_list('email',
                                                                  flat=True)
    organizers = get_study_group_organizers(study_group)
    organizers_emails = [organizer.email for organizer in organizers]
    to = list(recipients) + organizers_emails
    to.append(study_group.facilitator.email)

    context = {
        'study_group': study_group,
        'report_path': report_path,
        'facilitator_name': study_group.facilitator.first_name,
        'registrations': study_group.application_set.active().count(),
        'survey_responses': study_group.learnersurveyresponse_set.count(),
        'goals_met_chart': goals_met_chart.generate(output="png"),
        'learner_goals_chart': charts.goals_chart(study_group),
    }

    subject = render_to_string_ctx(
        'studygroups/email/learning_circle_final_report-subject.txt',
        context).strip('\n')
    html = render_html_with_css(
        'studygroups/email/learning_circle_final_report.html', context)
    txt = html_body_to_text(html)

    notification = EmailMultiAlternatives(
        subject,
        txt,
        settings.DEFAULT_FROM_EMAIL,
        bcc=to,
        reply_to=[study_group.facilitator.email])
    notification.attach_alternative(html, 'text/html')
    notification.send()
Exemple #18
0
def send_contact_form_inquiry(email, name, content, source, organization=None):
    context = {
        "email": email,
        "name": name,
        "content": content,
        "source": source,
        "organization": organization,
    }

    subject_template = 'contact/contact_email-subject.txt'
    html_email_template = 'contact/contact_email.html'
    subject = render_to_string_ctx(subject_template, context).strip(' \n')
    html_body = render_html_with_css(html_email_template, context)
    text_body = html_body_to_text(html_body)

    to = [settings.TEAM_EMAIL]
    email = EmailMultiAlternatives(subject,
                                   text_body,
                                   settings.DEFAULT_FROM_EMAIL,
                                   to,
                                   cc=[settings.SUPPORT_EMAIL],
                                   reply_to=[email, settings.SUPPORT_EMAIL])
    email.attach_alternative(html_body, 'text/html')
    email.send()
def receive_sms(request):
    # TODO - secure this callback
    sender = request.POST.get('From')
    message = request.POST.get('Body')

    STOP_LIST = ['STOP', 'STOPALL', 'UNSUBSCRIBE', 'CANCEL', 'END', 'QUIT']
    command = message.strip(string.punctuation + string.whitespace).upper()
    opt_out = command in STOP_LIST
    if opt_out:
        application_mobile_opt_out(sender)
        return http.HttpResponse(status=200)

    START_LIST = ['START', 'YES', 'UNSTOP']
    opt_in = command in START_LIST
    if opt_in:
        application_mobile_opt_out_revert(sender)
        return http.HttpResponse(status=200)

    to = []
    bcc = None
    subject = 'New SMS reply from {0}'.format(sender)
    context = {
        'message': message,
        'sender': sender,
    }

    # Only forward message to facilitator if there is a meeting in the future-ish
    today = datetime.datetime.now().date()
    yesterday = today - datetime.timedelta(days=1)
    meetings = Meeting.objects.active().filter(meeting_date__gte=yesterday)
    signups = Application.objects.active().filter(
        Q(mobile=sender) & Q(mobile_opt_out_at__isnull=True)
        & Q(study_group__in=meetings.values('study_group')))

    # TODO handle user signed up to 2 learning circles
    if signups.count() == 1:
        signup = signups.first()
        context['signup'] = signup
        # TODO i18n
        subject = 'New SMS reply from {0} <{1}>'.format(signup.name, sender)
        to += [signup.study_group.facilitator.email]
        next_meeting = signups.first().study_group.next_meeting()
        # TODO - replace this check with a check to see if the meeting reminder has been sent
        if next_meeting and next_meeting.meeting_datetime() - timezone.now(
        ) < datetime.timedelta(days=2):
            context['next_meeting'] = next_meeting
            context['rsvp_yes'] = next_meeting.rsvp_yes_link(sender)
            context['rsvp_no'] = next_meeting.rsvp_no_link(sender)

    text_body = render_to_string_ctx('studygroups/email/incoming_sms.txt',
                                     context)
    html_body = render_to_string_ctx('studygroups/email/incoming_sms.html',
                                     context)
    if len(to) == 0:
        to = [a[1] for a in settings.ADMINS]
    else:
        bcc = [a[1] for a in settings.ADMINS]

    notification = EmailMultiAlternatives(subject, text_body,
                                          settings.DEFAULT_FROM_EMAIL, to, bcc)
    notification.attach_alternative(html_body, 'text/html')
    notification.send()
    return http.HttpResponse(status=200)