Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Do Good Hours #5210

Closed
wants to merge 27 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
f26b032
Add setting for create initiatives
gannetson Aug 15, 2022
008801e
Add test
gannetson Aug 17, 2022
1238164
dd admin test too
gannetson Aug 17, 2022
e2c7f4b
Add upcoming filter to activity list api endpoint
eodolphi Aug 16, 2022
7eaa793
Fix test
eodolphi Aug 16, 2022
08e7194
Add has_initiatives to current user api
gannetson Aug 17, 2022
78184c9
First hours spent admin + api
gannetson Aug 18, 2022
16c53c1
Add tests
gannetson Aug 18, 2022
fc7b442
Add api tests too
gannetson Aug 18, 2022
4811f1d
Fix tests
gannetson Aug 19, 2022
44167df
Merge branch 'master' into demo/do-good-hours
gannetson Aug 22, 2022
e40d80b
Add mails and tests
gannetson Aug 25, 2022
3810734
Merge branch 'master' into demo/do-good-hours
gannetson Aug 26, 2022
2eb835b
Schedule quarterly emails
gannetson Aug 30, 2022
a8d0a26
add test for next years reminder emails
gannetson Aug 30, 2022
b10200d
Merge branch 'integration/BB-20411-quarterly-reminder-emails' into ti…
gannetson Aug 30, 2022
4b6749c
Merge remote-tracking branch 'origin/master' into demo/do-good-hours
gannetson Aug 30, 2022
71d866e
Check that receive_reminder_emails is in user profile
gannetson Aug 30, 2022
8057bd7
Add field + test
gannetson Aug 30, 2022
2e34b8e
Add comma
gannetson Aug 30, 2022
a0330eb
Add comma
gannetson Aug 30, 2022
300957e
Fix emssage + test
gannetson Aug 30, 2022
24ce94b
Merge pull request #5219 from onepercentclub/ticket/user-reminder-mai…
gannetson Aug 30, 2022
c9d389b
Merge branch 'integration/BB-20411-quarterly-reminder-emails' into ti…
gannetson Aug 31, 2022
5878d6b
Merge pull request #5220 from onepercentclub/integration/BB-20411-qua…
gannetson Aug 31, 2022
ad18e57
Merge pull request #5218 from onepercentclub/ticket/BB-20531-task-for…
gannetson Aug 31, 2022
e78791f
Merge remote-tracking branch 'origin/master' into demo/do-good-hours
gannetson Sep 7, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 68 additions & 5 deletions bluebottle/activities/messages.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
from pytz import timezone
from datetime import timedelta

from django.urls import reverse
from django.db.models import Sum, Q
from django.template.defaultfilters import time, date
from django.urls import reverse
from django.utils.timezone import get_current_timezone, now
from django.utils.translation import pgettext_lazy as pgettext
from pytz import timezone

from bluebottle.initiatives.models import InitiativePlatformSettings
from django.utils.timezone import get_current_timezone

from bluebottle.notifications.messages import TransitionMessage
from django.utils.translation import pgettext_lazy as pgettext
from bluebottle.notifications.models import Message
from bluebottle.utils.utils import get_current_host, get_current_language


Expand Down Expand Up @@ -445,3 +447,64 @@ def get_recipients(self):
return [self.obj.team.owner]
else:
return []


class BaseDoGoodHoursReminderNotification(TransitionMessage):

@property
def action_link(self):
return self.obj.get_absolute_url()

action_title = pgettext('email', 'Find activities')

send_once = True

def already_send(self, recipient):
return Message.objects.filter(
template=self.get_template(),
recipient=recipient,
sent__year=now().year
).count() > 0

def get_recipients(self):
"""members with do good hours"""
from bluebottle.members.models import Member
from bluebottle.members.models import MemberPlatformSettings

year = now().year
do_good_hours = timedelta(hours=MemberPlatformSettings.load().do_good_hours)

members = Member.objects.annotate(
hours=Sum(
'contributor__contributions__timecontribution__value',
filter=(
Q(contributor__contributions__start__year=year) &
Q(contributor__contributions__status__in=['new', 'succeeded'])
)
),
).filter(
Q(hours__lt=do_good_hours) | Q(hours__isnull=True),
is_active=True,
receive_reminder_emails=True
).distinct()
return members


class DoGoodHoursReminderQ1Notification(BaseDoGoodHoursReminderNotification):
subject = pgettext('email', "Are you ready to do good? Q1")
template = 'messages/do-good-hours/reminder-q1'


class DoGoodHoursReminderQ2Notification(BaseDoGoodHoursReminderNotification):
subject = pgettext('email', "Are you ready to do good? Q2")
template = 'messages/do-good-hours/reminder-q2'


class DoGoodHoursReminderQ3Notification(BaseDoGoodHoursReminderNotification):
subject = pgettext('email', "Are you ready to do good? Q3")
template = 'messages/do-good-hours/reminder-q3'


class DoGoodHoursReminderQ4Notification(BaseDoGoodHoursReminderNotification):
subject = pgettext('email', "Are you ready to do good? Q4")
template = 'messages/do-good-hours/reminder-q4'
48 changes: 40 additions & 8 deletions bluebottle/activities/tasks.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
import logging
from datetime import date

from celery.schedules import crontab
from celery.task import periodic_task

from bluebottle.clients.models import Client
from bluebottle.clients.utils import LocalTenant

from elasticsearch_dsl.query import Nested, Q, FunctionScore, ConstantScore, MatchAll

from bluebottle.initiatives.models import InitiativePlatformSettings
from bluebottle.activities.documents import activity
from bluebottle.activities.messages import MatchingActivitiesNotification, DoGoodHoursReminderQ1Notification, \
DoGoodHoursReminderQ4Notification, DoGoodHoursReminderQ3Notification, DoGoodHoursReminderQ2Notification
from bluebottle.activities.models import Activity
from bluebottle.activities.messages import MatchingActivitiesNotification
from bluebottle.members.models import Member

from bluebottle.clients.models import Client
from bluebottle.clients.utils import LocalTenant
from bluebottle.initiatives.models import InitiativePlatformSettings
from bluebottle.members.models import Member, MemberPlatformSettings

logger = logging.getLogger('bluebottle')

Expand Down Expand Up @@ -123,3 +122,36 @@ def recommend():
)
except Exception as e:
logger.error(e)


@periodic_task(
run_every=(crontab(minute=0, hour=10)),
name="do_good_hours_reminder",
ignore_result=True
)
def do_good_hours_reminder():

for tenant in Client.objects.all():
with LocalTenant(tenant, clear_tenant=True):
settings = MemberPlatformSettings.objects.get()
if settings.do_good_hours:
q1 = date.today().replace(month=1, day=1)
q2 = date.today().replace(month=4, day=1)
q3 = date.today().replace(month=7, day=1)
q4 = date.today().replace(month=10, day=1)
notification = None
today = date.today()
if settings.reminder_q1 and today == q1:
notification = DoGoodHoursReminderQ1Notification(settings)
if settings.reminder_q2 and today == q2:
notification = DoGoodHoursReminderQ2Notification(settings)
if settings.reminder_q3 and today == q3:
notification = DoGoodHoursReminderQ3Notification(settings)
if settings.reminder_q4 and today == q4:
notification = DoGoodHoursReminderQ4Notification(settings)

if notification:
try:
notification.compose_and_send()
except Exception as e:
logger.error(e)
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{% extends "base.mail.html" %}
{% load i18n %}

{% block content %}
<p>
{% blocktrans context 'email' %}Hi {{ recipient_name }},{% endblocktrans %}
</p>
{% block message %}{% endblock %}
{% endblock %}

{% block action %}
<a href="{{ action_link }}" class="action-email">{{ action_title }}</a>
{% endblock %}`
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{% extends "mails/messages/do-good-hours/reminder-base.html" %}
{% load i18n %}

{% block message %}
{% blocktrans context 'email' %}
First reminder
{% endblocktrans %}
{% endblock %}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{% extends "mails/messages/do-good-hours/reminder-base.html" %}
{% load i18n %}

{% block message %}
{% blocktrans context 'email' %}
Second reminder
{% endblocktrans %}
{% endblock %}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{% extends "mails/messages/do-good-hours/reminder-base.html" %}
{% load i18n %}

{% block message %}
{% blocktrans context 'email' %}
Third reminder
{% endblocktrans %}
{% endblock %}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{% extends "mails/messages/do-good-hours/reminder-base.html" %}
{% load i18n %}

{% block message %}
{% blocktrans context 'email' %}
Fourth reminder
{% endblocktrans %}
{% endblock %}
116 changes: 114 additions & 2 deletions bluebottle/activities/tests/test_notifications.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,25 @@
from datetime import timedelta

from django.utils.timezone import now

from bluebottle.activities.messages import (
ActivityRejectedNotification, ActivityCancelledNotification,
ActivitySucceededNotification, ActivityRestoredNotification,
ActivityExpiredNotification, TeamAddedMessage,
TeamAppliedMessage, TeamCancelledMessage,
TeamCancelledTeamCaptainMessage, TeamWithdrawnActivityOwnerMessage,
TeamWithdrawnMessage, TeamMemberAddedMessage, TeamMemberWithdrewMessage,
TeamMemberRemovedMessage, TeamReappliedMessage, TeamCaptainAcceptedMessage
TeamMemberRemovedMessage, TeamReappliedMessage, TeamCaptainAcceptedMessage, DoGoodHoursReminderQ1Notification,
DoGoodHoursReminderQ3Notification, DoGoodHoursReminderQ2Notification, DoGoodHoursReminderQ4Notification
)
from bluebottle.activities.tests.factories import TeamFactory
from bluebottle.members.models import MemberPlatformSettings, Member
from bluebottle.notifications.models import NotificationPlatformSettings
from bluebottle.test.factory_models.accounts import BlueBottleUserFactory
from bluebottle.test.utils import NotificationTestCase
from bluebottle.time_based.tests.factories import (
DateActivityFactory, PeriodActivityFactory, PeriodParticipantFactory
DateActivityFactory, PeriodActivityFactory, PeriodParticipantFactory, DateActivitySlotFactory,
DateParticipantFactory, SlotParticipantFactory
)


Expand Down Expand Up @@ -260,3 +268,107 @@ def test_team_member_removed_notification(self):

self.assertActionLink(self.obj.activity.get_absolute_url())
self.assertActionTitle('View activity')


class DoGoodHoursReminderNotificationTestCase(NotificationTestCase):

def setUp(self):
self.obj = NotificationPlatformSettings.load()
self.obj = MemberPlatformSettings.load()
self.obj.do_good_hours = 8
self.obj.save()
activity = DateActivityFactory.create(
slots=[],
slot_selection='free',
)

slot1 = DateActivitySlotFactory.create(
start=now() - timedelta(days=2),
duration=timedelta(hours=4),
activity=activity
)
slot2 = DateActivitySlotFactory.create(
start=now() - timedelta(days=1),
duration=timedelta(hours=4),
activity=activity
)
old_slot = DateActivitySlotFactory.create(
start=now().replace(year=2011),
duration=timedelta(hours=8),
activity=activity
)

self.active_user = BlueBottleUserFactory.create(first_name='Active')
part1 = DateParticipantFactory.create(
user=self.active_user,
activity=activity
)
SlotParticipantFactory.create(
participant=part1,
slot=slot1
)
SlotParticipantFactory.create(
participant=part1,
slot=slot2
)
self.moderate_user = BlueBottleUserFactory.create(first_name='Moderate')
part2 = DateParticipantFactory.create(
user=self.moderate_user,
activity=activity
)
SlotParticipantFactory.create(
participant=part2,
slot=slot1
)
SlotParticipantFactory.create(
participant=part2,
slot=old_slot
)
self.passive_user = BlueBottleUserFactory.create(first_name='Passive')
part3 = DateParticipantFactory.create(
user=self.passive_user,
activity=activity
)

SlotParticipantFactory.create(
participant=part3,
slot=old_slot
)

Member.objects.exclude(id__in=[
self.active_user.id,
self.passive_user.id,
self.moderate_user.id,
]).update(receive_reminder_emails=False)

def test_reminder_q1(self):
self.message_class = DoGoodHoursReminderQ1Notification
self.create()
self.assertRecipients([self.moderate_user, self.passive_user])
self.assertSubject("Are you ready to do good? Q1")
self.assertBodyContains('First reminder')
self.assertActionTitle('Find activities')

def test_reminder_q2(self):
self.message_class = DoGoodHoursReminderQ2Notification
self.create()
self.assertRecipients([self.moderate_user, self.passive_user])
self.assertSubject("Are you ready to do good? Q2")
self.assertBodyContains('Second reminder')
self.assertActionTitle('Find activities')

def test_reminder_q3(self):
self.message_class = DoGoodHoursReminderQ3Notification
self.create()
self.assertRecipients([self.moderate_user, self.passive_user])
self.assertSubject("Are you ready to do good? Q3")
self.assertBodyContains('Third reminder')
self.assertActionTitle('Find activities')

def test_reminder_q4(self):
self.message_class = DoGoodHoursReminderQ4Notification
self.create()
self.assertRecipients([self.moderate_user, self.passive_user])
self.assertSubject("Are you ready to do good? Q4")
self.assertBodyContains('Fourth reminder')
self.assertActionTitle('Find activities')
Loading