Ejemplo n.º 1
0
def notify_paid_invoice_slack_admin(invoice):
    invoice = clean_instance(invoice, Invoice)

    if invoice.legacy_id or not invoice.paid:
        # ignore legacy invoices
        return

    project_url = '{}/projects/{}/'.format(TUNGA_URL, invoice.project.id)
    person_url = '{}/network/{}/'.format(TUNGA_URL, invoice.user.username)
    invoice_url = '{}/api/invoices/{}/download/?format=pdf'.format(TUNGA_URL, invoice.id)

    slack_msg = ':tada: A {} of *EUR {}* has been {} *<{}|{}>* for <{}|{}> | <{}|Download Invoice>'.format(
        invoice.type == INVOICE_TYPE_SALE and 'payment' or 'payout',
        invoice.amount,
        invoice.type == INVOICE_TYPE_SALE and 'made by' or 'sent to',
        person_url,
        invoice.user.display_name.encode('utf-8'),
        project_url,
        invoice.full_title,
        invoice_url
    )

    slack_utils.send_incoming_webhook(
        SLACK_STAFF_INCOMING_WEBHOOK,
        {
            slack_utils.KEY_TEXT: slack_msg,
            slack_utils.KEY_CHANNEL: SLACK_STAFF_PAYMENTS_CHANNEL
        }
    )
Ejemplo n.º 2
0
def notify_user_profile_updated_slack(instance, edited=None):
    instance = clean_instance(instance, UserProfile)

    if instance.user.type != USER_TYPE_DEVELOPER:
        return

    profile_url = '{}/developer/{}'.format(TUNGA_URL, instance.user.username)
    slack_msg = "{}'s profile has been updated | <{}|Review on Tunga>".format(
        instance.user.display_name, profile_url)

    attachments = [{
        slack_utils.KEY_TITLE:
        instance.user.display_name,
        slack_utils.KEY_TITLE_LINK:
        profile_url,
        slack_utils.KEY_TEXT:
        '*Name:* {}\n'
        '*Location:* {}\n'
        '*Skills*: {}\n'
        '*Verified:* {}'.format(instance.user.display_name, instance.location,
                                str(instance.skills),
                                instance.user.verified and 'True' or 'False'),
        slack_utils.KEY_MRKDWN_IN: [slack_utils.KEY_TEXT],
        slack_utils.KEY_COLOR:
        instance.user.verified and SLACK_ATTACHMENT_COLOR_GREEN
        or SLACK_ATTACHMENT_COLOR_RED
    }]

    slack_utils.send_incoming_webhook(
        SLACK_STAFF_INCOMING_WEBHOOK, {
            slack_utils.KEY_TEXT: slack_msg,
            slack_utils.KEY_ATTACHMENTS: attachments,
            slack_utils.KEY_CHANNEL: SLACK_STAFF_PROFILES_CHANNEL
        })
Ejemplo n.º 3
0
def notify_paid_invoice_email_dev(invoice):
    invoice = clean_instance(invoice, Invoice)

    if invoice.legacy_id or invoice.type != INVOICE_TYPE_PURCHASE or not invoice.paid:
        # ignore legacy invoices and only notify about developer invoices
        return

    to = [invoice.user.email]

    merge_vars = [
        mandrill_utils.create_merge_var(MANDRILL_VAR_FIRST_NAME,
                                        invoice.user.first_name),
        mandrill_utils.create_merge_var('payout_title', invoice.full_title),
    ]

    pdf_file_contents = base64.b64encode(invoice.pdf)

    attachments = [
        dict(content=pdf_file_contents,
             name='Invoice - {}.pdf'.format(invoice.full_title),
             type='application/pdf')
    ]

    mandrill_utils.send_email('87-payout-made',
                              to,
                              merge_vars=merge_vars,
                              attachments=attachments)
Ejemplo n.º 4
0
def notify_new_contact_request_email(contact_request):
    contact_request = clean_instance(contact_request, ContactRequest)

    if contact_request.body:
        merge_vars = [
            mandrill_utils.create_merge_var('full_name',
                                            contact_request.fullname),
            mandrill_utils.create_merge_var('email', contact_request.email),
            mandrill_utils.create_merge_var('message', contact_request.body),
        ]

        mandrill_utils.send_email('73_Platform-guest-emails',
                                  TUNGA_CONTACT_REQUEST_EMAIL_RECIPIENTS,
                                  merge_vars=merge_vars)
    else:
        subject = "New {} Request".format(contact_request.item and 'Offer'
                                          or 'Contact')
        msg_suffix = 'wants to know more about Tunga.'
        if contact_request.item:
            item_name = contact_request.get_item_display()
            subject = '%s (%s)' % (subject, item_name)
            msg_suffix = 'requested for "%s"' % item_name
        to = TUNGA_CONTACT_REQUEST_EMAIL_RECIPIENTS

        ctx = {
            'email': contact_request.email,
            'message': '%s %s ' % (contact_request.email, msg_suffix)
        }

        if send_mail(subject, 'tunga/email/contact_request_message', to, ctx):
            contact_request.email_sent_at = datetime.datetime.utcnow()
            contact_request.save()
Ejemplo n.º 5
0
def notify_interest_poll_email(interest_poll, reminder=False):
    interest_poll = clean_instance(interest_poll, InterestPoll)

    to = [interest_poll.user.email]

    poll_url = '{}/poll/{}/{}'.format(TUNGA_URL, interest_poll.id, interest_poll.token)

    merge_vars = [
        mandrill_utils.create_merge_var(MANDRILL_VAR_FIRST_NAME, interest_poll.user.first_name),
        mandrill_utils.create_merge_var('opportunity_title', interest_poll.project.title),
        mandrill_utils.create_merge_var('skills', str(interest_poll.project.skills)),
        mandrill_utils.create_merge_var('description', interest_poll.project.description),
        mandrill_utils.create_merge_var('scope', interest_poll.project.get_expected_duration_display()),
        mandrill_utils.create_merge_var('yes_url', '{}?status=interested'.format(poll_url)),
        mandrill_utils.create_merge_var('no_url', '{}?status=uninterested'.format(poll_url)),
    ]

    mandrill_response = mandrill_utils.send_email(
        reminder and '90-availability-for-project-reminder' or '89-availability-for-project',
        to, merge_vars=merge_vars
    )
    if mandrill_response:
        if reminder:
            interest_poll.reminded_at = datetime.datetime.utcnow()
        else:
            interest_poll.sent_at = datetime.datetime.utcnow()
        interest_poll.save()

        mandrill_utils.log_emails.delay(mandrill_response, to)
Ejemplo n.º 6
0
def notify_progress_report_deadline_missed_slack_admin(instance):
    instance = clean_instance(instance, ProgressReport)

    task_url = '{}/work/{}'.format(TUNGA_URL, instance.event.task.id)
    slack_msg = "`Alert (!):` Follow up on missed deadline for \"{}\" | <{}|View on Tunga>".format(
        instance.event.task.summary,
        task_url
    )

    attachments = [
        {
            slack_utils.KEY_TITLE: instance.event.task.summary,
            slack_utils.KEY_TITLE_LINK: task_url,
            slack_utils.KEY_TEXT: 'A deadline has been missed on the "{}" {}\n'
                                  '*Was the client informed before hand?:* {}\n'
                                  'Please contact the stakeholders.'.format(
                instance.event.task.summary,
                instance.event.task.is_task and 'task' or 'project',
                instance.deadline_miss_communicated and 'Yes' or 'No'
            ),
            slack_utils.KEY_MRKDWN_IN: [slack_utils.KEY_TEXT],
            slack_utils.KEY_COLOR: SLACK_ATTACHMENT_COLOR_TUNGA
        },
        create_task_stakeholders_attachment_slack(instance.event.task, show_title=False)
    ]

    slack_utils.send_incoming_webhook(
        SLACK_STAFF_INCOMING_WEBHOOK,
        {
            slack_utils.KEY_TEXT: slack_msg,
            slack_utils.KEY_ATTACHMENTS: attachments,
            slack_utils.KEY_CHANNEL: SLACK_STAFF_UPDATES_CHANNEL
        }
    )
Ejemplo n.º 7
0
def notify_progress_report_stuck_slack_admin(instance):
    instance = clean_instance(instance, ProgressReport)

    task_url = '{}/work/{}/event/{}'.format(TUNGA_URL, instance.event.task.id, instance.event.id)
    slack_msg = "`Alert (!):` The status for the \"{}\" {} has been classified as stuck | <{}|View on Tunga>".format(
        instance.event.task.summary,
        instance.event.task.is_task and 'task' or 'project',
        task_url
    )

    attachments = [
        {
            slack_utils.KEY_TITLE: instance.event.task.summary,
            slack_utils.KEY_TITLE_LINK: task_url,
            slack_utils.KEY_TEXT: 'Please contact all stakeholders.',
            slack_utils.KEY_MRKDWN_IN: [slack_utils.KEY_TEXT],
            slack_utils.KEY_COLOR: SLACK_ATTACHMENT_COLOR_TUNGA
        },
        create_task_stakeholders_attachment_slack(instance.event.task, show_title=False)
    ]

    slack_utils.send_incoming_webhook(
        SLACK_STAFF_INCOMING_WEBHOOK,
        {
            slack_utils.KEY_TEXT: slack_msg,
            slack_utils.KEY_ATTACHMENTS: attachments,
            slack_utils.KEY_CHANNEL: SLACK_STAFF_UPDATES_CHANNEL
        }
    )
Ejemplo n.º 8
0
def notify_new_task_community_email(instance):
    instance = clean_instance(instance, Task)

    # Notify Devs or PMs
    community_receivers = None
    if instance.is_developer_ready:
        # Notify developers
        if instance.approved and instance.visibility in [VISIBILITY_DEVELOPER, VISIBILITY_MY_TEAM]:
            community_receivers = get_suggested_community_receivers(instance, user_type=USER_TYPE_DEVELOPER)
    elif instance.is_project and not instance.pm:
        community_receivers = get_suggested_community_receivers(instance, user_type=USER_TYPE_PROJECT_MANAGER)

    if instance.is_project and instance.pm:
        community_receivers = [instance.pm]

    subject = "New {} created by {}".format(
        instance.scope == TASK_SCOPE_TASK and 'task' or 'project',
        instance.user.first_name
    )

    if community_receivers:
        to = [community_receivers[0].email]
        bcc = None
        if len(community_receivers) > 1:
            bcc = [user.email for user in community_receivers[1:]] if community_receivers[1:] else None
        ctx = {
            'owner': instance.owner or instance.user,
            'task': instance,
            'task_url': '{}/work/{}/'.format(TUNGA_URL, instance.id)
        }
        send_mail(subject, 'tunga/email/new_task', to, ctx, bcc=bcc, **dict(deal_ids=[instance.hubspot_deal_id]))
Ejemplo n.º 9
0
def notify_progress_report_stuck_email_pm(instance):
    instance = clean_instance(instance, ProgressReport)

    subject = "You guys are stuck, but help is underway."

    pm = instance.event.task.pm
    if not pm:
        return

    to = [pm.email]

    ctx = {
        'owner':
        instance.event.task.owner or instance.event.task.user,
        'reporter':
        instance.user,
        'pm':
        pm,
        'event':
        instance.event,
        'report':
        instance,
        'update_url':
        '{}/work/{}/event/{}/'.format(TUNGA_URL, instance.event.task.id,
                                      instance.event.id)
    }

    send_mail(subject, 'tunga/email/report_stuck_pm', to, ctx,
              **dict(deal_ids=[instance.event.task.hubspot_deal_id]))
Ejemplo n.º 10
0
def notify_payment_link_client_email(instance):
    instance = clean_instance(instance, Task)

    to = [instance.user.email]
    if instance.owner and instance.owner.email != instance.user.email:
        to.append(instance.owner.email)

    task_url = '{}/task/{}/'.format(TUNGA_URL, instance.id)
    payment_link = '{}pay/'.format(task_url)

    owner = instance.owner or instance.user

    merge_vars = [
        mandrill_utils.create_merge_var(MANDRILL_VAR_FIRST_NAME, owner.first_name),
        mandrill_utils.create_merge_var('payment_title', instance.summary),
        mandrill_utils.create_merge_var('payment_link', payment_link),
    ]

    mandrill_response = mandrill_utils.send_email('70-payment-link-ready', to, merge_vars=merge_vars)
    if mandrill_response:
        instance.payment_link_sent = True
        instance.payment_link_sent_at = datetime.datetime.utcnow()
        instance.save()

        mandrill_utils.log_emails.delay(mandrill_response, to, deal_ids=[instance.hubspot_deal_id])
Ejemplo n.º 11
0
def notify_new_task_admin_email(instance, new_user=False, completed=False, call_scheduled=False):
    instance = clean_instance(instance, Task)

    completed_phrase_subject = ''
    completed_phrase_body = ''
    if call_scheduled:
        completed_phrase_subject = 'availability window shared'
        completed_phrase_body = 'shared an availability window'
    elif completed:
        completed_phrase_subject = 'details completed'
        completed_phrase_body = 'completed the details'

    subject = "New{} {} {} by {}{}".format(
        (completed or call_scheduled) and ' wizard' or '',
        instance.scope == TASK_SCOPE_TASK and 'task' or 'project',
        completed_phrase_subject or 'created',
        instance.user.first_name, new_user and ' (New user)' or ''
    )

    to = TUNGA_STAFF_LOW_LEVEL_UPDATE_EMAIL_RECIPIENTS  # Notified via Slack so limit receiving admins

    ctx = {
        'owner': instance.owner or instance.user,
        'task': instance,
        'task_url': '{}/task/{}/'.format(TUNGA_URL, instance.id),
        'completed_phrase': completed_phrase_body,
    }
    send_mail(subject, 'tunga/email/new_task', to, ctx, **dict(deal_ids=[instance.hubspot_deal_id]))
Ejemplo n.º 12
0
def notify_parties_of_low_rating_email(instance):
    instance = clean_instance(instance, ProgressReport)
    is_client_report = instance.event.type in [LEGACY_PROGRESS_EVENT_TYPE_CLIENT, LEGACY_PROGRESS_EVENT_TYPE_CLIENT_MID_SPRINT]

    if is_client_report:
        subject = "Work Rating For {}".format(instance.event.task.summary)
        ctx = {
            'owner': instance.event.task.owner or instance.event.task.user,
            'event': instance,
            'update_url': '{}/work/{}/event/{}/'.format(TUNGA_URL, instance.event.task.id, instance.event.id)
        }
        # send to client
        if instance.task.owner:
            to = [instance.event.task.owner.email]
            email_template = 'low_rating_client'
            send_mail(
                subject, 'tunga/email/{}'.format(email_template), to, ctx,
                **dict(deal_ids=[instance.event.task.hubspot_deal_id])
            )
        # send to pm
        if instance.event.task.pm:
            to = [instance.event.task.pm.email]
            email_template = 'low_rating_pm'
            send_mail(
                subject, 'tunga/email/{}'.format(email_template), to, ctx,
                **dict(deal_ids=[instance.event.task.hubspot_deal_id])
            )
        # send to user
        if instance.event.task.user:
            to = [instance.event.task.user.email]
            email_template = 'low_rating_user'
            send_mail(
                subject, 'tunga/email/{}'.format(email_template), to, ctx,
                **dict(deal_ids=[instance.event.task.hubspot_deal_id])
            )
Ejemplo n.º 13
0
def trigger_schedule_call_automation(user):
    user = clean_instance(user, get_user_model())
    mailchimp_utils.add_email_to_automation_queue(
        email_address=user.email,
        workflow_id=MAILCHIMP_NEW_USER_AUTOMATION_WORKFLOW_ID,
        email_id=MAILCHIMP_NEW_USER_AUTOMATION_EMAIL_ID
    )
Ejemplo n.º 14
0
def notify_progress_report_wont_meet_deadline_slack_admin(instance):
    instance = clean_instance(instance, ProgressReport)

    task_url = '{}/work/{}/event/{}'.format(TUNGA_URL, instance.event.task.id, instance.event.id)
    slack_msg = "`Alert (!):` {} doesn't expect to meet the deadline | <{}|View on Tunga>".format(
        instance.event.type in [LEGACY_PROGRESS_EVENT_TYPE_PM, LEGACY_PROGRESS_EVENT_TYPE_MILESTONE_INTERNAL] and 'PM' or 'Developer',
        task_url
    )

    attachments = [
        {
            slack_utils.KEY_TITLE: instance.event.task.summary,
            slack_utils.KEY_TITLE_LINK: task_url,
            slack_utils.KEY_TEXT: 'The {} on the \"{}\" {} has indicated that they might not meet the coming deadline.\n'
                                  'Please contact all stakeholders.'.format(
                instance.event.type in [LEGACY_PROGRESS_EVENT_TYPE_PM,
                                        LEGACY_PROGRESS_EVENT_TYPE_MILESTONE_INTERNAL] and 'PM' or 'Developer',
                instance.event.task.summary,
                instance.event.task.is_task and 'task' or 'project'
            ),
            slack_utils.KEY_MRKDWN_IN: [slack_utils.KEY_TEXT],
            slack_utils.KEY_COLOR: SLACK_ATTACHMENT_COLOR_TUNGA
        },
        create_task_stakeholders_attachment_slack(instance.event.task, show_title=False)
    ]

    slack_utils.send_incoming_webhook(
        SLACK_STAFF_INCOMING_WEBHOOK,
        {
            slack_utils.KEY_TEXT: slack_msg,
            slack_utils.KEY_ATTACHMENTS: attachments,
            slack_utils.KEY_CHANNEL: SLACK_STAFF_UPDATES_CHANNEL
        }
    )
Ejemplo n.º 15
0
def notify_new_progress_report_slack(progress_report, updated=False):
    progress_report = clean_instance(progress_report, ProgressReport)

    is_pm_report = progress_report.event.type in [PROGRESS_EVENT_PM, PROGRESS_EVENT_INTERNAL] or \
                   (progress_report.event.type == PROGRESS_EVENT_MILESTONE and progress_report.user.is_project_manager)
    is_client_report = progress_report.event.type == PROGRESS_EVENT_CLIENT or \
                       (
                           progress_report.event.type == PROGRESS_EVENT_MILESTONE and progress_report.user.is_project_owner)
    is_pm_or_client_report = is_pm_report or is_client_report
    is_dev_report = not is_pm_or_client_report

    # All reports go to Tunga #updates Slack
    slack_msg, attachments = create_progress_report_slack_message(
        progress_report, updated=updated)
    slack_utils.send_incoming_webhook(
        SLACK_STAFF_INCOMING_WEBHOOK, {
            slack_utils.KEY_TEXT: slack_msg,
            slack_utils.KEY_CHANNEL: SLACK_STAFF_UPDATES_CHANNEL,
            slack_utils.KEY_ATTACHMENTS: attachments
        })

    if is_dev_report:
        # Re-create report for clients
        # TODO: Respect client's settings
        slack_msg, attachments = create_progress_report_slack_message(
            progress_report, updated=updated, to_client=True)
        slack_utils.send_project_message(progress_report.event.project,
                                         message=slack_msg,
                                         attachments=attachments)
Ejemplo n.º 16
0
def notify_new_message_developers(instance):
    instance = clean_instance(instance, Message)

    if instance.channel.type == CHANNEL_TYPE_DEVELOPER and (instance.user.is_staff or instance.user.is_superuser) and \
            not instance.channel.messages.filter(user__is_staff=False, user__is_superuser=False).count():
        recipients = get_user_model().objects.filter(type=USER_TYPE_DEVELOPER)
        if recipients:
            to = [recipients[0]]
            bcc = recipients[1:] if len(recipients) > 1 else None

            if to and isinstance(to, (list, tuple)):
                subject = "Developer Notification: {}".format(
                    instance.channel.subject or instance.sender.short_name)
                ctx = {
                    'sender':
                    instance.sender.short_name,
                    'subject':
                    instance.channel.subject,
                    'channel':
                    instance.channel,
                    'message':
                    instance,
                    'message_url':
                    '%s/conversation/%s/' % (TUNGA_URL, instance.channel_id)
                }
                send_mail(subject, 'tunga/email/new_message', to, ctx, bcc=bcc)
Ejemplo n.º 17
0
def notify_missed_progress_event_slack(progress_event):
    progress_event = clean_instance(progress_event, ProgressEvent)

    if progress_event.project.archived or progress_event.status != "missed" or not progress_event.last_reminder_at or progress_event.missed_notification_at:
        return

    participants = progress_event.participants
    if not participants:
        # No one to report or project is now closed
        return

    target_user = None
    if participants and len(participants) == 1:
        target_user = participants[0]

    project_url = '{}/projects/{}'.format(TUNGA_URL, progress_event.project.id)
    slack_msg = "`Alert (!):` {} {} for \"{}\" | <{}|View on Tunga>".format(
        target_user and '{} missed a'.format(target_user.short_name) or 'Missed',
        (progress_event.type == PROGRESS_EVENT_CLIENT and 'progress survey') or
        (progress_event.type == PROGRESS_EVENT_MILESTONE and 'milestone report') or
        'progress report',
        progress_event.project.title,
        project_url
    )

    attachments = [
        {
            slack_utils.KEY_TITLE: progress_event.project.title,
            slack_utils.KEY_TITLE_LINK: project_url,
            slack_utils.KEY_TEXT: '*Due Date:* {}\n\n{}'.format(
                progress_event.due_at.strftime("%d %b, %Y"),
                '\n\n'.join(
                    [
                        '*Name:* {}\n'
                        '*Email:* {}{}'.format(
                            user.display_name.encode('utf-8'),
                            user.email,
                            not user.is_project_owner and user.profile and user.profile.phone_number and
                            '\n*Phone Number:* {}'.format(user.profile.phone_number) or '')
                        for user in participants
                    ]
                )
            ),
            slack_utils.KEY_MRKDWN_IN: [slack_utils.KEY_TEXT],
            slack_utils.KEY_COLOR: SLACK_ATTACHMENT_COLOR_TUNGA
        }
    ]

    slack_utils.send_incoming_webhook(
        SLACK_STAFF_INCOMING_WEBHOOK,
        {
            slack_utils.KEY_TEXT: slack_msg,
            slack_utils.KEY_ATTACHMENTS: attachments,
            slack_utils.KEY_CHANNEL: SLACK_STAFF_MISSED_UPDATES_CHANNEL
        }
    )

    # Save notification time
    progress_event.missed_notification_at = datetime.datetime.now()
    progress_event.save()
Ejemplo n.º 18
0
def notify_new_invite_request_slack(invite_request):
    invite_request = clean_instance(invite_request, InviteRequest)

    slack_msg = "<!channel> {} wants to join Tunga".format(invite_request.name)

    attachments = [{
        slack_utils.KEY_TITLE:
        invite_request.name,
        slack_utils.KEY_TITLE_LINK:
        invite_request.cv_url,
        slack_utils.KEY_TEXT:
        '*Name:* {}\n*Email:* {}\n*Country*: {}\n<{}|Download CV>'.format(
            invite_request.name, invite_request.email,
            invite_request.country.name, invite_request.cv_url),
        slack_utils.KEY_MRKDWN_IN: [slack_utils.KEY_TEXT],
        slack_utils.KEY_COLOR:
        SLACK_ATTACHMENT_COLOR_GREEN,
    }, {
        slack_utils.KEY_TITLE: 'Motivation',
        slack_utils.KEY_TEXT: invite_request.motivation,
        slack_utils.KEY_MRKDWN_IN: [slack_utils.KEY_TEXT],
        slack_utils.KEY_COLOR: SLACK_ATTACHMENT_COLOR_BLUE,
    }]

    slack_utils.send_incoming_webhook(
        SLACK_STAFF_INCOMING_WEBHOOK, {
            slack_utils.KEY_TEXT: slack_msg,
            slack_utils.KEY_ATTACHMENTS: attachments,
            slack_utils.KEY_CHANNEL: SLACK_STAFF_PROFILES_CHANNEL
        })
Ejemplo n.º 19
0
def notify_new_task_application_slack(instance):
    instance = clean_instance(instance, Application)

    if not slack_utils.is_task_notification_enabled(instance.task,
                                                    slugs.EVENT_APPLICATION):
        return

    application_url = '%s/work/%s/applications/' % (TUNGA_URL,
                                                    instance.task_id)
    slack_msg = "New application from %s" % instance.user.short_name
    attachments = [{
        slack_utils.KEY_TITLE:
        instance.task.summary,
        slack_utils.KEY_TITLE_LINK:
        application_url,
        slack_utils.KEY_TEXT:
        '%s%s%s%s\n\n<%s|View details on Tunga>' %
        (truncatewords(convert_to_text(instance.pitch),
                       100), instance.hours_needed
         and '\n*Workload:* {} hrs'.format(instance.hours_needed)
         or '', instance.deliver_at and '\n*Delivery Date:* {}'.format(
             instance.deliver_at.strftime("%d %b, %Y at %H:%M GMT"))
         or '', instance.remarks and '\n*Remarks:* {}'.format(
             truncatewords(convert_to_text(instance.remarks), 100))
         or '', application_url),
        slack_utils.KEY_MRKDWN_IN: [slack_utils.KEY_TEXT],
        slack_utils.KEY_COLOR:
        SLACK_ATTACHMENT_COLOR_TUNGA
    }]
    slack_utils.send_integration_message(instance.task,
                                         message=slack_msg,
                                         attachments=attachments)
Ejemplo n.º 20
0
def update_task_submit_milestone(task):
    task = clean_instance(task, Task)
    if task.deadline:
        task_period = (task.deadline - task.created_at).days
        if task.parent:
            # task is part of a bigger project
            draft_submission_date = task.deadline
        else:
            # standalone task needs a milestone before the deadline
            days_before = (task.pay > 150 and task_period >= 7) and 2 or 1
            draft_submission_date = task.deadline - datetime.timedelta(
                days=days_before)

        draft_defaults = {
            'due_at': draft_submission_date,
            'title': 'Final draft'
        }
        ProgressEvent.objects.update_or_create(task=task,
                                               type=PROGRESS_EVENT_TYPE_SUBMIT,
                                               defaults=draft_defaults)

        submit_defaults = {'due_at': task.deadline, 'title': 'Submit work'}
        ProgressEvent.objects.update_or_create(
            task=task,
            type=PROGRESS_EVENT_TYPE_COMPLETE,
            defaults=submit_defaults)
Ejemplo n.º 21
0
def notify_estimate_approved_client_email(instance, estimate_type='estimate'):
    instance = clean_instance(instance, estimate_type == 'quote' and Quote or Estimate)
    if instance.status != STATUS_APPROVED:
        return
    subject = "{} submitted {}".format(
        instance.user.first_name,
        estimate_type == 'estimate' and 'an estimate' or 'a quote'
    )
    to = [instance.task.user.email]
    if instance.task.owner:
        to.append(instance.task.owner.email)
    ctx = {
        'owner': instance.user,
        'estimate': instance,
        'task': instance.task,
        'estimate_url': '{}/work/{}/{}/{}'.format(TUNGA_URL, instance.task.id, estimate_type, instance.id),
        'actor': instance.user,
        'target': instance.task.owner or instance.task.user,
        'verb': 'submitted',
        'noun': estimate_type
    }

    if instance.task.source == TASK_SOURCE_NEW_USER and not instance.task.user.is_confirmed:
        url_prefix = '{}/reset-password/confirm/{}/{}?new_user=true&next='.format(
            TUNGA_URL, instance.user.uid, instance.user.generate_reset_token()
        )
        ctx['estimate_url'] = '{}{}'.format(url_prefix, ctx['estimate_url'])

    if send_mail(
            subject, 'tunga/email/estimate_status', to, ctx, **dict(deal_ids=[instance.task.hubspot_deal_id])
    ):
        instance.reviewer_email_at = datetime.datetime.utcnow()
        instance.save()
Ejemplo n.º 22
0
def notify_missed_progress_event_slack(progress_event):
    progress_event = clean_instance(progress_event, ProgressEvent)

    if progress_event.project.archived or progress_event.status != "missed" or not progress_event.last_reminder_at or progress_event.missed_notification_at:
        return

    participants = progress_event.participants
    if not participants:
        # No one to report or project is now closed
        return

    target_user = None
    if participants and len(participants) == 1:
        target_user = participants[0]

    project_url = '{}/projects/{}'.format(TUNGA_URL, progress_event.project.id)
    slack_msg = "`Alert (!):` {} {} for \"{}\" | <{}|View on Tunga>".format(
        target_user and '{} missed a'.format(target_user.short_name) or 'Missed',
        (progress_event.type == PROGRESS_EVENT_CLIENT and 'progress survey') or
        (progress_event.type == PROGRESS_EVENT_MILESTONE and 'milestone report') or
        'progress report',
        progress_event.project.title,
        project_url
    )

    attachments = [
        {
            slack_utils.KEY_TITLE: progress_event.project.title,
            slack_utils.KEY_TITLE_LINK: project_url,
            slack_utils.KEY_TEXT: '*Due Date:* {}\n\n{}'.format(
                progress_event.due_at.strftime("%d %b, %Y"),
                '\n\n'.join(
                    [
                        '*Name:* {}\n'
                        '*Email:* {}{}'.format(
                            user.display_name.encode('utf-8'),
                            user.email,
                            not user.is_project_owner and user.profile and user.profile.phone_number and
                            '\n*Phone Number:* {}'.format(user.profile.phone_number) or '')
                        for user in participants
                    ]
                )
            ),
            slack_utils.KEY_MRKDWN_IN: [slack_utils.KEY_TEXT],
            slack_utils.KEY_COLOR: SLACK_ATTACHMENT_COLOR_TUNGA
        }
    ]

    slack_utils.send_incoming_webhook(
        SLACK_STAFF_INCOMING_WEBHOOK,
        {
            slack_utils.KEY_TEXT: slack_msg,
            slack_utils.KEY_ATTACHMENTS: attachments,
            slack_utils.KEY_CHANNEL: SLACK_STAFF_MISSED_UPDATES_CHANNEL
        }
    )

    # Save notification time
    progress_event.missed_notification_at = datetime.datetime.now()
    progress_event.save()
Ejemplo n.º 23
0
def notify_progress_report_stuck_email_admin(instance):
    instance = clean_instance(instance, ProgressReport)

    subject = "{} has been classified as stuck ".format(
        instance.event.task.is_task and 'Task' or 'Project')

    to = TUNGA_STAFF_LOW_LEVEL_UPDATE_EMAIL_RECIPIENTS

    ctx = {
        'owner':
        instance.event.task.owner or instance.event.task.user,
        'reporter':
        instance.user,
        'pm':
        instance.event.task.pm,
        'event':
        instance.event,
        'report':
        instance,
        'developers':
        instance.event.task.active_participants,
        'update_url':
        '{}/work/{}/event/{}/'.format(TUNGA_URL, instance.event.task.id,
                                      instance.event.id)
    }

    send_mail(subject, 'tunga/email/report_stuck_admin', to, ctx,
              **dict(deal_ids=[instance.event.task.hubspot_deal_id]))
Ejemplo n.º 24
0
def notify_progress_report_wont_meet_deadline_email_pm(instance):
    instance = clean_instance(instance, ProgressReport)

    subject = "`Alert (!):` {} doesn't expect to meet the deadline".format(
        instance.event.type in [LEGACY_PROGRESS_EVENT_TYPE_PM, LEGACY_PROGRESS_EVENT_TYPE_MILESTONE_INTERNAL] and 'PM' or 'Developer'
    )

    pm = instance.event.task.pm
    if not pm:
        return

    to = [pm.email]

    ctx = {
        'owner': instance.event.task.owner or instance.event.task.user,
        'reporter': instance.user,
        'pm': pm,
        'event': instance.event,
        'report': instance,
        'update_url': '{}/work/{}/event/{}/'.format(TUNGA_URL, instance.event.task.id, instance.event.id)
    }

    send_mail(
        subject, 'tunga/email/wont_meet_deadline_pm', to, ctx,
        **dict(deal_ids=[instance.event.task.hubspot_deal_id])
    )
Ejemplo n.º 25
0
def notify_progress_report_client_not_satisfied_email_pm(instance):
    instance = clean_instance(instance, ProgressReport)

    subject = "Alert (!): Client dissatisfied"

    pm = instance.event.task.pm
    if not pm:
        return

    to = [pm.email]

    ctx = {
        'owner':
        instance.event.task.owner or instance.event.task.user,
        'reporter':
        instance.user,
        'pm':
        pm,
        'event':
        instance.event,
        'report':
        instance,
        'update_url':
        '{}/work/{}/event/{}/'.format(TUNGA_URL, instance.event.task.id,
                                      instance.event.id)
    }

    send_mail(subject, 'tunga/email/client_not_satisfied_pm', to, ctx,
              **dict(deal_ids=[instance.event.task.hubspot_deal_id]))
Ejemplo n.º 26
0
def notify_progress_report_wont_meet_deadline_email_pm(instance):
    instance = clean_instance(instance, ProgressReport)

    subject = "`Alert (!):` {} doesn't expect to meet the deadline".format(
        instance.event.type == PROGRESS_EVENT_TYPE_PM and 'PM' or 'Developer')

    pm = instance.event.task.pm
    if not pm:
        return

    to = [pm.email]

    ctx = {
        'owner':
        instance.event.task.owner or instance.event.task.user,
        'reporter':
        instance.user,
        'pm':
        pm,
        'event':
        instance.event,
        'report':
        instance,
        'update_url':
        '{}/work/{}/event/{}/'.format(TUNGA_URL, instance.event.task.id,
                                      instance.event.id)
    }

    send_mail(subject, 'tunga/email/wont_meet_deadline_pm', to, ctx,
              **dict(deal_ids=[instance.event.task.hubspot_deal_id]))
Ejemplo n.º 27
0
def notify_progress_report_client_not_satisfied_email_admin(instance):
    instance = clean_instance(instance, ProgressReport)

    subject = "Alert (!): Client dissatisfied"

    to = TUNGA_STAFF_LOW_LEVEL_UPDATE_EMAIL_RECIPIENTS

    ctx = {
        'owner':
        instance.event.task.owner or instance.event.task.user,
        'reporter':
        instance.user,
        'pm':
        instance.event.task.pm,
        'event':
        instance.event,
        'report':
        instance,
        'developers':
        instance.event.task.active_participants,
        'update_url':
        '{}/work/{}/event/{}/'.format(TUNGA_URL, instance.event.task.id,
                                      instance.event.id)
    }

    send_mail(subject, 'tunga/email/client_not_satisfied_admin', to, ctx,
              **dict(deal_ids=[instance.event.task.hubspot_deal_id]))
Ejemplo n.º 28
0
def notify_progress_report_client_not_satisfied_email_client(instance):
    instance = clean_instance(instance, ProgressReport)

    subject = "Following up on {} quality".format(
        instance.event.task.is_task and 'task' or 'project')

    to = [instance.event.task.user.email]
    if instance.event.task.owner:
        to.append(instance.event.task.owner.email)

    ctx = {
        'owner':
        instance.event.task.owner or instance.event.task.user,
        'reporter':
        instance.user,
        'event':
        instance.event,
        'report':
        instance,
        'update_url':
        '{}/work/{}/event/{}/'.format(TUNGA_URL, instance.event.task.id,
                                      instance.event.id)
    }

    send_mail(subject, 'tunga/email/client_not_satisfied_client', to, ctx,
              **dict(deal_ids=[instance.event.task.hubspot_deal_id]))
Ejemplo n.º 29
0
def notify_progress_report_deadline_missed_email_client(instance):
    instance = clean_instance(instance, ProgressReport)

    subject = "Following up on missed deadline"

    to = [instance.event.task.user.email]
    if instance.event.task.owner:
        to.append(instance.event.task.owner.email)

    ctx = {
        'owner':
        instance.event.task.owner or instance.event.task.user,
        'reporter':
        instance.user,
        'event':
        instance.event,
        'report':
        instance,
        'update_url':
        '{}/work/{}/event/{}/'.format(TUNGA_URL, instance.event.task.id,
                                      instance.event.id)
    }

    send_mail(subject, 'tunga/email/deadline_missed_client', to, ctx,
              **dict(deal_ids=[instance.event.task.hubspot_deal_id]))
Ejemplo n.º 30
0
def notify_progress_report_behind_schedule_by_algo_email_pm(instance):
    instance = clean_instance(instance, ProgressReport)

    subject = "Alert (!): it appears you're behind schedule"

    pm = instance.event.task.pm
    if not pm:
        return

    to = [pm.email]

    ctx = {
        'owner':
        instance.event.task.owner or instance.event.task.user,
        'reporter':
        instance.user,
        'pm':
        pm,
        'event':
        instance.event,
        'report':
        instance,
        'update_url':
        '{}/work/{}/event/{}/'.format(TUNGA_URL, instance.event.task.id,
                                      instance.event.id)
    }

    send_mail(subject, 'tunga/email/behind_schedule_by_algo_pm', to, ctx,
              **dict(deal_ids=[instance.event.task.hubspot_deal_id]))
Ejemplo n.º 31
0
def notify_new_task_admin_email(instance,
                                new_user=False,
                                completed=False,
                                call_scheduled=False):
    instance = clean_instance(instance, Task)

    completed_phrase_subject = ''
    completed_phrase_body = ''
    if call_scheduled:
        completed_phrase_subject = 'availability window shared'
        completed_phrase_body = 'shared an availability window'
    elif completed:
        completed_phrase_subject = 'details completed'
        completed_phrase_body = 'completed the details'

    subject = "New{} {} {} by {}{}".format(
        (completed or call_scheduled) and ' wizard' or '',
        instance.scope == TASK_SCOPE_TASK and 'task' or 'project',
        completed_phrase_subject or 'created', instance.user.first_name,
        new_user and ' (New user)' or '')

    to = TUNGA_STAFF_LOW_LEVEL_UPDATE_EMAIL_RECIPIENTS  # Notified via Slack so limit receiving admins

    ctx = {
        'owner': instance.owner or instance.user,
        'task': instance,
        'task_url': '{}/task/{}/'.format(TUNGA_URL, instance.id),
        'completed_phrase': completed_phrase_body,
    }
    send_mail(subject, 'tunga/email/new_task', to, ctx,
              **dict(deal_ids=[instance.hubspot_deal_id]))
Ejemplo n.º 32
0
def notify_new_task_client_drip_one(instance, template='welcome'):
    instance = clean_instance(instance, Task)

    if instance.source != TASK_SOURCE_NEW_USER:
        # Only target wizard users
        return False

    to = [instance.user.email]
    if instance.owner:
        to.append(instance.owner.email)

    task_url = '{}/task/{}/'.format(TUNGA_URL, instance.id)
    task_edit_url = '{}/task/{}/edit/complete-task/'.format(
        TUNGA_URL, instance.id)
    task_call_url = '{}/task/{}/edit/call/'.format(TUNGA_URL, instance.id)
    browse_url = '{}/people/filter/developers'.format(TUNGA_URL)

    if not instance.user.is_confirmed:
        url_prefix = '{}/reset-password/confirm/{}/{}?new_user=true&next='.format(
            TUNGA_URL, instance.user.uid, instance.user.generate_reset_token())
        task_url = '{}{}'.format(url_prefix, task_url)
        task_edit_url = '{}{}'.format(url_prefix, task_edit_url)
        task_call_url = '{}{}'.format(url_prefix, task_call_url)
        browse_url = '{}{}'.format(url_prefix, browse_url)

    merge_vars = [
        mandrill_utils.create_merge_var(MANDRILL_VAR_FIRST_NAME,
                                        instance.user.first_name),
        mandrill_utils.create_merge_var('task_url', task_edit_url),
        mandrill_utils.create_merge_var('call_url', task_call_url),
        mandrill_utils.create_merge_var('browse_url', browse_url)
    ]

    template_code = None
    if template == 'welcome':
        if instance.schedule_call_start:
            template_code = '01-b-welcome-call-scheduled'
            merge_vars.extend([
                mandrill_utils.create_merge_var(
                    'date',
                    instance.schedule_call_start.strftime("%d %b, %Y")),
                mandrill_utils.create_merge_var(
                    'time', instance.schedule_call_start.strftime("%I:%M%p")),
            ])
        else:
            template_code = '01-welcome-new'
    elif template == 'hiring':
        template_code = '02-hiring'

    if template_code:
        mandrill_response = mandrill_utils.send_email(template_code,
                                                      to,
                                                      merge_vars=merge_vars)
        if mandrill_response:
            instance.last_drip_mail = template
            instance.last_drip_mail_at = datetime.datetime.utcnow()
            instance.save()

            mandrill_utils.log_emails.delay(
                mandrill_response, to, deal_ids=[instance.hubspot_deal_id])
Ejemplo n.º 33
0
def notify_progress_report_wont_meet_deadline_email_dev(instance):
    instance = clean_instance(instance, ProgressReport)

    subject = "`Alert (!):` {} doesn't expect to meet the deadline".format(
        instance.event.type in [
            LEGACY_PROGRESS_EVENT_TYPE_PM,
            LEGACY_PROGRESS_EVENT_TYPE_MILESTONE_INTERNAL
        ] and 'PM' or 'Developer')

    to = [instance.user.email]

    ctx = {
        'owner':
        instance.event.task.owner or instance.event.task.user,
        'reporter':
        instance.user,
        'event':
        instance.event,
        'report':
        instance,
        'update_url':
        '{}/work/{}/event/{}/'.format(TUNGA_URL, instance.event.task.id,
                                      instance.event.id)
    }

    send_mail(subject, 'tunga/email/wont_meet_deadline_dev', to, ctx,
              **dict(deal_ids=[instance.event.task.hubspot_deal_id]))
Ejemplo n.º 34
0
def notify_payment_link_client_email(instance):
    instance = clean_instance(instance, Task)

    to = [instance.user.email]
    if instance.owner and instance.owner.email != instance.user.email:
        to.append(instance.owner.email)

    task_url = '{}/task/{}/'.format(TUNGA_URL, instance.id)
    payment_link = '{}pay/'.format(task_url)

    owner = instance.owner or instance.user

    merge_vars = [
        mandrill_utils.create_merge_var(MANDRILL_VAR_FIRST_NAME,
                                        owner.first_name),
        mandrill_utils.create_merge_var('payment_title', instance.summary),
        mandrill_utils.create_merge_var('payment_link', payment_link),
    ]

    mandrill_response = mandrill_utils.send_email('70-payment-link-ready',
                                                  to,
                                                  merge_vars=merge_vars)
    if mandrill_response:
        instance.payment_link_sent = True
        instance.payment_link_sent_at = datetime.datetime.utcnow()
        instance.save()

        mandrill_utils.log_emails.delay(mandrill_response,
                                        to,
                                        deal_ids=[instance.hubspot_deal_id])
Ejemplo n.º 35
0
def notify_new_comment_slack(instance):
    instance = clean_instance(instance, Comment)

    if ContentType.objects.get_for_model(
            Task) != ContentType.objects.get_for_model(
                instance.content_object):
        return

    task = instance.content_object
    if not slack_utils.is_task_notification_enabled(task,
                                                    slugs.EVENT_COMMUNICATION):
        return

    task_url = '{}/work/{}/'.format(TUNGA_URL, task.id)

    slack_msg = '{} | <{}|View on Tunga>'.format(instance.text_body, task_url)
    extras = dict(author_name=instance.user.display_name)

    try:
        if instance.user.avatar_url:
            extras['author_icon'] = instance.user.avatar_url
    except:
        pass

    slack_utils.send_integration_message(instance.content_object,
                                         message=slack_msg,
                                         **extras)
Ejemplo n.º 36
0
def notify_progress_report_wont_meet_deadline_email_admin(instance):
    instance = clean_instance(instance, ProgressReport)

    subject = "`Alert (!):` {} doesn't expect to meet the deadline".format(
        instance.event.type in [
            LEGACY_PROGRESS_EVENT_TYPE_PM,
            LEGACY_PROGRESS_EVENT_TYPE_MILESTONE_INTERNAL
        ] and 'PM' or 'Developer')

    to = TUNGA_STAFF_LOW_LEVEL_UPDATE_EMAIL_RECIPIENTS

    ctx = {
        'owner':
        instance.event.task.owner or instance.event.task.user,
        'reporter':
        instance.user,
        'pm':
        instance.event.task.pm,
        'event':
        instance.event,
        'report':
        instance,
        'developers':
        instance.event.task.active_participants,
        'update_url':
        '{}/work/{}/event/{}/'.format(TUNGA_URL, instance.event.task.id,
                                      instance.event.id)
    }

    send_mail(subject, 'tunga/email/wont_meet_deadline_admin', to, ctx,
              **dict(deal_ids=[instance.event.task.hubspot_deal_id]))
Ejemplo n.º 37
0
def notify_progress_report_client_not_satisfied_slack_admin(instance):
    instance = clean_instance(instance, ProgressReport)

    task_url = '{}/work/{}/event/{}'.format(TUNGA_URL, instance.event.task.id, instance.event.id)
    slack_msg = "`Alert (!):` Client dissatisfied | <{}|View on Tunga>".format(task_url)

    attachments = [
        {
            slack_utils.KEY_TITLE: instance.event.task.summary,
            slack_utils.KEY_TITLE_LINK: task_url,
            slack_utils.KEY_TEXT: 'The project owner of \"{}\" {} is unsatisfied with the deliverable.\n '
                                  'Please contact all stakeholders.'.format(
                instance.event.task.summary,
                instance.event.task.is_task and 'task' or 'project'
            ),
            slack_utils.KEY_MRKDWN_IN: [slack_utils.KEY_TEXT],
            slack_utils.KEY_COLOR: SLACK_ATTACHMENT_COLOR_TUNGA
        },
        create_task_stakeholders_attachment_slack(instance.event.task, show_title=False)
    ]

    slack_utils.send_incoming_webhook(
        SLACK_STAFF_INCOMING_WEBHOOK,
        {
            slack_utils.KEY_TEXT: slack_msg,
            slack_utils.KEY_ATTACHMENTS: attachments,
            slack_utils.KEY_CHANNEL: SLACK_STAFF_UPDATES_CHANNEL
        }
    )
Ejemplo n.º 38
0
def send_developer_invited_email(instance, resend=False):
    instance = clean_instance(instance, DeveloperInvitation)
    subject = "You have been invited to become a Tunga {}".format(
        instance.get_type_display().lower())
    to = [instance.email]
    ctx = {
        'invite':
        instance,
        'invite_url':
        '%s/signup/invite/%s/' % (
            TUNGA_URL,
            instance.invitation_key,
        )
    }
    if send_mail(subject, 'tunga/email/user_invitation', to, ctx):
        if resend:
            instance.used = False
            instance.resent = True
            instance.resent_at = datetime.datetime.utcnow()
        else:
            instance.invitation_sent_at = datetime.datetime.utcnow()
        instance.save()

        if not resend:
            notify_user_has_been_invited_to_developer_slack(instance)
Ejemplo n.º 39
0
def send_developer_application_received_email(instance):
    instance = clean_instance(instance, DeveloperApplication)
    subject = "%s Your application to become a Tunga developer has been received" % EMAIL_SUBJECT_PREFIX
    to = [instance.email]
    ctx = {'application': instance}
    send_mail(subject, 'tunga/email/email_developer_application_received', to,
              ctx)
Ejemplo n.º 40
0
def notify_paid_invoice_email_dev(invoice):
    invoice = clean_instance(invoice, Invoice)

    if invoice.legacy_id or invoice.type != INVOICE_TYPE_PURCHASE or not invoice.paid:
        # ignore legacy invoices and only notify about developer invoices
        return

    to = [invoice.user.email]

    merge_vars = [
        mandrill_utils.create_merge_var(MANDRILL_VAR_FIRST_NAME, invoice.user.first_name),
        mandrill_utils.create_merge_var('payout_title', invoice.full_title),
    ]

    pdf_file_contents = base64.b64encode(invoice.pdf)

    attachments = [
        dict(
            content=pdf_file_contents,
            name='Invoice - {}.pdf'.format(invoice.full_title),
            type='application/pdf'
        )
    ]

    mandrill_utils.send_email('87-payout-made', to, merge_vars=merge_vars, attachments=attachments)
Ejemplo n.º 41
0
def notify_missed_progress_event_slack(instance):
    instance = clean_instance(instance, ProgressEvent)

    is_client_report = instance.type in [LEGACY_PROGRESS_EVENT_TYPE_CLIENT, LEGACY_PROGRESS_EVENT_TYPE_CLIENT_MID_SPRINT]

    if instance.task.archived or instance.status != "missed" or not instance.last_reminder_at:
        return

    participants = instance.participants
    if not participants or instance.task.closed:
        # No one to report or task is now closed
        return

    target_user = None
    if participants and len(participants) == 1:
        target_user = participants[0]

    task_url = '{}/work/{}'.format(TUNGA_URL, instance.task.id)
    slack_msg = "`Alert (!):` {} {} for \"{}\" | <{}|View on Tunga>".format(
        target_user and '{} missed a'.format(target_user.short_name) or 'Missed',
        is_client_report and 'weekly survey' or 'progress report',
        instance.task.summary,
        task_url
    )

    attachments = [
        {
            slack_utils.KEY_TITLE: instance.task.summary,
            slack_utils.KEY_TITLE_LINK: task_url,
            slack_utils.KEY_TEXT: '\n\n'.join(
                [
                    '*Due Date:* {}\n\n'
                    '*Name:* {}\n'
                    '*Email:* {}{}'.format(
                        instance.due_at.strftime("%d %b, %Y"),
                        user.display_name.encode('utf-8'),
                        user.email,
                        user.profile and user.profile.phone_number and '\n*Phone Number:* {}'.format(
                            user.profile.phone_number) or ''
                    ) for user in participants
                ]
            ),
            slack_utils.KEY_MRKDWN_IN: [slack_utils.KEY_TEXT],
            slack_utils.KEY_COLOR: SLACK_ATTACHMENT_COLOR_TUNGA
        }
    ]

    slack_utils.send_incoming_webhook(
        SLACK_STAFF_INCOMING_WEBHOOK,
        {
            slack_utils.KEY_TEXT: slack_msg,
            slack_utils.KEY_ATTACHMENTS: attachments,
            slack_utils.KEY_CHANNEL: SLACK_STAFF_MISSED_UPDATES_CHANNEL
        }
    )

    # Save notification time
    instance.missed_notification_at = datetime.datetime.now()
    instance.save()
Ejemplo n.º 42
0
def trigger_progress_report_actionable_events(instance):
    # Trigger actionable event notifications
    instance = clean_instance(instance, ProgressReport)
    is_pm_report = instance.event.type in [LEGACY_PROGRESS_EVENT_TYPE_PM, LEGACY_PROGRESS_EVENT_TYPE_MILESTONE_INTERNAL]
    is_client_report = instance.event.type in [LEGACY_PROGRESS_EVENT_TYPE_CLIENT, LEGACY_PROGRESS_EVENT_TYPE_CLIENT_MID_SPRINT]
    is_pm_or_client_report = is_pm_report or is_client_report
    is_dev_report = not is_pm_or_client_report

    task = instance.event.task
    has_pm = instance.event.task.pm

    # Deadline wasn't met
    if instance.last_deadline_met is not None and not instance.last_deadline_met:
        if is_pm_report or is_dev_report:
            notify_progress_report_deadline_missed_admin(instance)

            notify_progress_report_deadline_missed_client(instance)

            if has_pm:
                notify_progress_report_deadline_missed_pm(instance)

            if is_dev_report:
                notify_progress_report_deadline_missed_dev(instance)

    # More than 20% difference between time passed and accomplished
    if task.deadline:
        if is_dev_report and instance.started_at and task.deadline > instance.started_at:
            right_now = datetime.datetime.utcnow()
            spent_percentage = ((right_now - instance.started_at)/(task.deadline - right_now))*100
            if ((instance.percentage or 0) + 20) < spent_percentage:
                notify_progress_report_behind_schedule_by_algo_admin(instance)

                if has_pm:
                    notify_progress_report_behind_schedule_by_algo_pm(instance)

                notify_progress_report_behind_schedule_by_algo_dev(instance)

    # Client not satisfied with deliverable
    if instance.deliverable_satisfaction is not None and not instance.deliverable_satisfaction:
        if is_client_report:
            notify_progress_report_client_not_satisfied_admin(instance)

            notify_progress_report_client_not_satisfied_client(instance)

            if has_pm:
                notify_progress_report_client_not_satisfied_pm(instance)

            notify_progress_report_client_not_satisfied_dev(instance)

    # Stuck and/ or not progressing
    if instance.status in [LEGACY_PROGRESS_REPORT_STATUS_STUCK, LEGACY_PROGRESS_REPORT_STATUS_BEHIND_AND_STUCK]:
        if is_pm_report or is_dev_report:
            notify_progress_report_stuck_admin(instance)

            if has_pm:
                notify_progress_report_stuck_pm(instance)

            if is_dev_report:
                notify_progress_report_stuck_dev(instance)
Ejemplo n.º 43
0
def distribute_multi_task_payment(multi_task_key):
    multi_task_key = clean_instance(multi_task_key, MultiTaskPaymentKey)
    if not multi_task_key.paid:
        return

    # Distribute connected tasks
    for task in multi_task_key.tasks.all():
        distribute_task_payment_payoneer(task)
Ejemplo n.º 44
0
def sync_exact_invoices(task, invoice_types=('client', 'tunga', 'developer'), developers=None):
    task = clean_instance(task, Task)
    invoice = task.invoice
    client = task.owner or task.user

    admin_emails = ['*****@*****.**', '*****@*****.**', '*****@*****.**']

    if 'client' in invoice_types and task.paid and client.type == USER_TYPE_PROJECT_OWNER \
            and client.email not in admin_emails:
        # Only sync paid invoices whose project owner is not a Tunga admin
        invoice_file_client = HTML(
            string=process_invoices(task.id, invoice_types=['client'], user_id=client.id, is_admin=False),
            encoding='utf-8'
        ).write_pdf()
        exact_utils.upload_invoice(
            task, client, 'client', invoice_file_client,
            float(invoice.amount.get('total_invoice_client', 0)),
            vat_location=invoice.vat_location_client
        )

    participation_shares = task.get_participation_shares()
    for share_info in participation_shares:
        participant = share_info['participant']
        dev = participant.user

        if participant.status != STATUS_ACCEPTED or share_info['share'] <= 0:
            continue

        if developers and dev.id not in developers:
            continue

        if ParticipantPayment.objects.filter(participant=participant):
            amount_details = invoice.get_amount_details(share=share_info['share'])

            if 'tunga' in invoice_types:
                invoice_file_tunga = HTML(
                    string=process_invoices(
                        task.id, invoice_types=['tunga'], user_id=dev.id, developer_ids=[dev.id], is_admin=False
                    ),
                    encoding='utf-8'
                ).write_pdf()
                exact_utils.upload_invoice(
                    task, dev, 'tunga', invoice_file_tunga,
                    float(amount_details.get('total_invoice_tunga', 0))
                )

            if 'developer' in invoice_types and invoice.version == 1:
                # Developer (tunga invoicing dev) invoices are only part of the old invoice scheme
                invoice_file_dev = HTML(
                    string=process_invoices(
                        task.id, invoice_types=['developer'], user_id=dev.id, developer_ids=[dev.id], is_admin=False
                    ),
                    encoding='utf-8'
                ).write_pdf()
                exact_utils.upload_invoice(
                    task, dev, 'developer', invoice_file_dev,
                    float(amount_details.get('total_invoice_developer', 0))
                )
Ejemplo n.º 45
0
def notify_estimate_status_email(instance, estimate_type='estimate', target_admins=False):
    instance = clean_instance(instance, estimate_type == 'quote' and Quote or Estimate)
    if instance.status == STATUS_INITIAL:
        return

    actor = None
    target = None
    action_verb = VERB_MAP_STATUS_CHANGE.get(instance.status, None)
    recipients = None

    if instance.status in [STATUS_SUBMITTED]:
        actor = instance.user
        recipients = TUNGA_STAFF_UPDATE_EMAIL_RECIPIENTS
    elif instance.status in [STATUS_APPROVED, STATUS_DECLINED]:
        actor = instance.moderated_by
        target = instance.user
        recipients = [instance.user.email]
    elif instance.status in [STATUS_ACCEPTED, STATUS_REJECTED]:
        actor = instance.reviewed_by
        if target_admins:
            recipients = TUNGA_STAFF_UPDATE_EMAIL_RECIPIENTS
        else:
            target = instance.user
            recipients = [instance.user.email]

            # Notify staff in a separate email
            notify_estimate_status_email.delay(instance.id, estimate_type=estimate_type, target_admins=True)

    subject = "{} {} {}".format(
        actor.first_name,
        action_verb,
        estimate_type == 'estimate' and 'an estimate' or 'a quote'
    )
    to = recipients

    ctx = {
        'owner': instance.user,
        'estimate': instance,
        'task': instance.task,
        'estimate_url': '{}/work/{}/{}/{}'.format(TUNGA_URL, instance.task.id, estimate_type, instance.id),
        'actor': actor,
        'target': target,
        'verb': action_verb,
        'noun': estimate_type
    }

    if send_mail(
            subject, 'tunga/email/estimate_status', to, ctx, **dict(deal_ids=[instance.task.hubspot_deal_id])
    ):
        if instance.status == STATUS_SUBMITTED:
            instance.moderator_email_at = datetime.datetime.utcnow()
            instance.save()
        if instance.status in [STATUS_ACCEPTED, STATUS_REJECTED]:
            instance.reviewed_email_at = datetime.datetime.utcnow()
            instance.save()

    if instance.status == STATUS_APPROVED:
        notify_estimate_approved_client_email(instance, estimate_type=estimate_type)
Ejemplo n.º 46
0
def notify_invoice_email(invoice, updated=False):
    invoice = clean_instance(invoice, Invoice)
    if invoice.type == INVOICE_TYPE_SALE:
        if updated:
            notify_updated_invoice_email_client(invoice)
        else:
            notify_new_invoice_email_client(invoice)
    elif invoice.type == INVOICE_TYPE_PURCHASE and not updated:
        notify_new_invoice_email_dev(invoice)
Ejemplo n.º 47
0
def notify_new_project_slack_admin(project):
    project = clean_instance(project, Project)

    summary, attachments = create_project_slack_message(project)
    slack_utils.send_incoming_webhook(SLACK_STAFF_INCOMING_WEBHOOK, {
        slack_utils.KEY_TEXT: summary,
        slack_utils.KEY_CHANNEL: SLACK_STAFF_LEADS_CHANNEL,
        slack_utils.KEY_ATTACHMENTS: attachments
    })
Ejemplo n.º 48
0
def send_new_user_joined_email(instance):
    instance = clean_instance(instance, get_user_model())
    subject = "{} joined Tunga".format(instance.display_name)
    to = TUNGA_STAFF_UPDATE_EMAIL_RECIPIENTS
    ctx = {
        'user': instance,
        'user_url': '%s/people/%s/' % (TUNGA_URL, instance.username)
    }
    send_mail(subject, 'tunga/email/new_user', to, ctx)
Ejemplo n.º 49
0
def notify_new_task_community_slack(instance):
    instance = clean_instance(instance, Task)

    # Notify Devs or PMs via Slack
    if (not instance.is_developer_ready) or (instance.approved and instance.visibility == VISIBILITY_DEVELOPER):
        slack_msg = create_task_slack_msg(
            instance,
            channel=instance.is_developer_ready and SLACK_DEVELOPER_UPDATES_CHANNEL or SLACK_PMS_UPDATES_CHANNEL
        )
        slack_utils.send_incoming_webhook(SLACK_DEVELOPER_INCOMING_WEBHOOK, slack_msg)
Ejemplo n.º 50
0
def notify_new_task_invoice_admin_slack(instance):
    instance = clean_instance(instance, TaskInvoice)

    task_url = '{}/work/{}/'.format(TUNGA_URL, instance.task.id)
    owner = instance.task.owner or instance.task.user
    client_url = '{}/people/{}/'.format(TUNGA_URL, owner.username)
    invoice_url = '{}/api/task/{}/download/invoice/?format=pdf'.format(TUNGA_URL, instance.task.id)
    slack_msg = '{} generated an invoice'.format(
        instance.user.display_name.encode('utf-8')
    )

    attachments = [
        {
            slack_utils.KEY_TITLE: instance.task.summary,
            slack_utils.KEY_TITLE_LINK: task_url,
            slack_utils.KEY_TEXT: 'Client: <{}|{}>\nFee: {}\nPayment Method: {}\n<{}|Download invoice>'.format(
                client_url,
                owner.display_name.encode('utf-8'),
                instance.display_fee().encode('utf-8'),
                instance.get_payment_method_display(),
                invoice_url
            ),
            slack_utils.KEY_MRKDWN_IN: [slack_utils.KEY_TEXT],
            slack_utils.KEY_COLOR: SLACK_ATTACHMENT_COLOR_BLUE
        },
    ]
    if not instance.task.payment_approved:
        task_approval_url = '{}edit/payment-approval/'.format(task_url)
        if instance.payment_method == PAYMENT_METHOD_BANK:
            attachments.append({
                slack_utils.KEY_TITLE: 'Review and approve payment.',
                slack_utils.KEY_TITLE_LINK: task_approval_url,
                slack_utils.KEY_TEXT: "Payment will be completed via bank transfer.\n "
                                      "However, developer payments won't be distributed until the payment"
                                      " is reviewed and approved.",
                slack_utils.KEY_MRKDWN_IN: [slack_utils.KEY_TEXT],
                slack_utils.KEY_COLOR: SLACK_ATTACHMENT_COLOR_GREEN
            })
        else:
            attachments.append({
                slack_utils.KEY_TITLE: 'Review and approve payment.',
                slack_utils.KEY_TITLE_LINK: task_approval_url,
                slack_utils.KEY_TEXT: "The client won't be able to pay until the payment is approved.",
                slack_utils.KEY_MRKDWN_IN: [slack_utils.KEY_TEXT],
                slack_utils.KEY_COLOR: SLACK_ATTACHMENT_COLOR_RED
            })

    slack_utils.send_incoming_webhook(
        SLACK_STAFF_INCOMING_WEBHOOK,
        {
            slack_utils.KEY_TEXT: slack_msg,
            slack_utils.KEY_ATTACHMENTS: attachments,
            slack_utils.KEY_CHANNEL: SLACK_STAFF_PAYMENTS_CHANNEL
        }
    )
Ejemplo n.º 51
0
def notify_new_task_client_drip_one(instance, template='welcome'):
    instance = clean_instance(instance, Task)

    if instance.source != TASK_SOURCE_NEW_USER:
        # Only target wizard users
        return False

    to = [instance.user.email]
    if instance.owner:
        to.append(instance.owner.email)

    task_url = '{}/task/{}/'.format(TUNGA_URL, instance.id)
    task_edit_url = '{}/task/{}/edit/complete-task/'.format(TUNGA_URL, instance.id)
    task_call_url = '{}/task/{}/edit/call/'.format(TUNGA_URL, instance.id)
    browse_url = '{}/people/filter/developers'.format(TUNGA_URL)

    if not instance.user.is_confirmed:
        url_prefix = '{}/reset-password/confirm/{}/{}?new_user=true&next='.format(
            TUNGA_URL, instance.user.uid, instance.user.generate_reset_token()
        )
        task_url = '{}{}'.format(url_prefix, task_url)
        task_edit_url = '{}{}'.format(url_prefix, task_edit_url)
        task_call_url = '{}{}'.format(url_prefix, task_call_url)
        browse_url = '{}{}'.format(url_prefix, browse_url)

    merge_vars = [
        mandrill_utils.create_merge_var(MANDRILL_VAR_FIRST_NAME, instance.user.first_name),
        mandrill_utils.create_merge_var('task_url', task_edit_url),
        mandrill_utils.create_merge_var('call_url', task_call_url),
        mandrill_utils.create_merge_var('browse_url', browse_url)
    ]

    template_code = None
    if template == 'welcome':
        if instance.schedule_call_start:
            template_code = '01-b-welcome-call-scheduled'
            merge_vars.extend(
                [
                    mandrill_utils.create_merge_var('date', instance.schedule_call_start.strftime("%d %b, %Y")),
                    mandrill_utils.create_merge_var('time', instance.schedule_call_start.strftime("%I:%M%p")),
                ]
            )
        else:
            template_code = '01-welcome-new'
    elif template == 'hiring':
        template_code = '02-hiring'

    if template_code:
        mandrill_response = mandrill_utils.send_email(template_code, to, merge_vars=merge_vars)
        if mandrill_response:
            instance.last_drip_mail = template
            instance.last_drip_mail_at = datetime.datetime.utcnow()
            instance.save()

            mandrill_utils.log_emails.delay(mandrill_response, to, deal_ids=[instance.hubspot_deal_id])
Ejemplo n.º 52
0
def update_task_pm_updates(task):
    task = clean_instance(task, Task)

    target_task = task
    if task.parent:
        # for sub-tasks, create all pm updates on the project
        target_task = task.parent

    if target_task.archived or target_task.closed or target_task.is_task or not target_task.pm:
        # only request pm updates for project which are approved and not closed
        return

    if target_task.update_interval and target_task.update_interval_units:
        periodic_start_date = ProgressEvent.objects.filter(
            Q(task=target_task) | Q(task__parent=target_task), type=LEGACY_PROGRESS_EVENT_TYPE_PM
        ).aggregate(latest_date=Max('due_at'))['latest_date']

        now = datetime.datetime.utcnow()
        if periodic_start_date and periodic_start_date > now:
            return

        if not periodic_start_date:
            periodic_start_date = datetime.datetime.utcnow()

        if periodic_start_date:
            last_update_at = clean_update_datetime(periodic_start_date, target_task)
            while True:
                last_update_day = last_update_at.weekday()
                next_update_at = last_update_at
                if last_update_day < 3:
                    # Last was before Thursday so schedule for Thursday
                    next_update_at += relativedelta(days=3 - last_update_day)
                else:
                    # Last was on after Thursday so schedule for Monday
                    next_update_at += relativedelta(days=7 - last_update_day)

                if next_update_at >= now:
                    future_by_18_hours = now + relativedelta(hours=18)
                    if next_update_at <= future_by_18_hours and (
                        not target_task.deadline or next_update_at < target_task.deadline):
                        num_updates_within_on_same_day = ProgressEvent.objects.filter(
                            task=target_task, type=LEGACY_PROGRESS_EVENT_TYPE_PM,
                            due_at__contains=next_update_at.date()
                        ).count()

                        if num_updates_within_on_same_day == 0:
                            # Schedule at most one pm update for any day
                            ProgressEvent.objects.update_or_create(
                                task=target_task, type=LEGACY_PROGRESS_EVENT_TYPE_PM,
                                due_at=next_update_at, defaults={'title': 'PM Report'}
                            )
                    break
                else:
                    last_update_at = next_update_at
Ejemplo n.º 53
0
def send_new_user_password_email(instance):
    instance = clean_instance(instance, get_user_model())
    subject = "You have been invited to become a Tunga {}".format(
        instance.get_type_display().lower()
    )
    to = [instance.email]
    ctx = {
        'invite': instance,
        'invite_url': '{}/password/{}/{}'.format(TUNGA_URL, instance.uid, instance.generate_reset_token())
    }
    send_mail(subject, 'tunga/email/user_invitation_password', to, ctx)
Ejemplo n.º 54
0
def update_multi_tasks(multi_task_key, distribute=False):
    multi_task_key = clean_instance(multi_task_key, MultiTaskPaymentKey)

    if multi_task_key.distribute_only:
        connected_tasks = multi_task_key.distribute_tasks
        connected_tasks.filter(paid=True).update(
            btc_price=multi_task_key.btc_price,
            withhold_tunga_fee_distribute=multi_task_key.withhold_tunga_fee,
            btc_paid=multi_task_key.paid,
            btc_paid_at=multi_task_key.paid_at
        )
    else:
        connected_tasks = multi_task_key.tasks
        connected_tasks.filter(paid=False).update(
            payment_method=multi_task_key.payment_method,
            btc_price=multi_task_key.btc_price,
            withhold_tunga_fee=multi_task_key.withhold_tunga_fee,
            paid=multi_task_key.paid,
            paid_at=multi_task_key.paid_at,
            processing=multi_task_key.processing,
            processing_at=multi_task_key.processing_at
        )

    # Generate invoices for all connected tasks
    for task in connected_tasks.all():
        if multi_task_key.distribute_only:
            if task.paid and multi_task_key.paid:
                # Coinbase waits for 6 confirmations, so not safe to distribute yet
                # distribute_task_payment.delay(task.id)
                pass
            return

        # Save Invoice
        if not task.btc_address or not bitcoin_utils.is_valid_btc_address(task.btc_address):
            address = coinbase_utils.get_new_address(coinbase_utils.get_api_client())
            task.btc_address = address
            task.save()

        TaskInvoice.objects.create(
            task=task,
            user=multi_task_key.user,
            title=task.title,
            fee=task.pay,
            client=task.owner or task.user,
            # developer=developer,
            payment_method=multi_task_key.payment_method,
            btc_price=multi_task_key.btc_price,
            btc_address=task.btc_address,
            withhold_tunga_fee=multi_task_key.withhold_tunga_fee
        )

        if distribute and multi_task_key.paid:
            distribute_task_payment_payoneer.delay(task.id)
Ejemplo n.º 55
0
def make_payout(invoice):
    invoice = clean_instance(invoice, Invoice)

    if invoice.legacy_id or invoice.type != INVOICE_TYPE_PURCHASE or invoice.status != STATUS_APPROVED or invoice.paid:
        # Only payout non-legacy non-paid approved purchase invoices
        return

    payoneer_client = payoneer_utils.get_client(
        PAYONEER_USERNAME, PAYONEER_PASSWORD, PAYONEER_PARTNER_ID
    )

    balance = payoneer_client.get_balance()
    if Decimal(20) <= invoice.amount <= balance.get('accountbalance', 0):
        # Payments must be more than EUR 20 and less than the balance

        payment, created = Payment.objects.get_or_create(
            invoice=invoice, defaults=dict(
                amount=invoice.amount, payment_method=PAYMENT_METHOD_PAYONEER
            )
        )

        if created or (payment and payment.status == STATUS_RETRY):
            if payment.status == STATUS_RETRY:
                payment.status = STATUS_INITIATED
                payment.save()

            transaction = payoneer_client.make_payment(
                PAYONEER_PARTNER_ID, 'invoice{}'.format(invoice.id), invoice.user.id, invoice.amount,
                invoice.full_title
            )

            if transaction.get('status', None) == '000':
                paid_at = datetime.datetime.utcnow()

                # Update invoice
                invoice.paid = True
                invoice.paid_at = paid_at
                invoice.save()

                payment.invoice = invoice
                payment.amount = invoice.amount
                payment.payment_method = PAYMENT_METHOD_PAYONEER
                payment.currency = invoice.currency or CURRENCY_EUR
                payment.ref = transaction.get('paymentid', None)
                payment.paid_at = paid_at
                payment.created_by = invoice.created_by
                payment.save()

                notify_paid_invoice.delay(invoice)
            else:
                payment.status = STATUS_FAILED
                payment.save()