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

Release/fiscal year translate #5263

Merged
merged 17 commits into from
Sep 29, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
15 changes: 8 additions & 7 deletions bluebottle/activities/tasks.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import logging
from datetime import date
from datetime import date, datetime

from celery.schedules import crontab
from celery.task import periodic_task
from dateutil.relativedelta import relativedelta
from elasticsearch_dsl.query import Nested, Q, FunctionScore, ConstantScore, MatchAll

from bluebottle.activities.documents import activity
Expand Down Expand Up @@ -130,17 +131,17 @@ def recommend():
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
offset = settings.fiscal_month_offset
today = date.today()
q1 = (datetime(2000, 1, 1).date() + relativedelta(months=offset)).replace(today.year)
q2 = q1 + relativedelta(months=3)
q3 = q1 + relativedelta(months=6)
q4 = q1 + relativedelta(months=9)
notification = None
if settings.reminder_q1 and today == q1:
notification = DoGoodHoursReminderQ1Notification(settings)
if settings.reminder_q2 and today == q2:
Expand Down
21 changes: 21 additions & 0 deletions bluebottle/activities/tests/test_periodic_tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,12 @@ def q1(self):
datetime(now().year, 1, 1)
)

@property
def fiscal_q1(self):
return timezone.get_current_timezone().localize(
datetime(now().year, 9, 1)
)

@property
def after_q1(self):
return timezone.get_current_timezone().localize(
Expand Down Expand Up @@ -163,6 +169,21 @@ def test_reminder_q1(self):
self.run_task(self.q1)
self.assertEqual(len(mail.outbox), 0, "Reminder mail should not be send again.")

def test_reminder_fiscal_q1(self):
settings = MemberPlatformSettings.load()
settings.fiscal_month_offset = -4
settings.save()
self.run_task(self.fiscal_q1)
self.assertEqual(len(mail.outbox), 3)
recipients = [m.to[0] for m in mail.outbox]
self.assertTrue(self.moderate_user.email in recipients, "Moderate user should receive email")
self.assertTrue(self.passive_user.email in recipients, "Passive user should receive email")
self.assertTrue(self.tempted_user.email in recipients, "Tempted user should receive email")

mail.outbox = []
self.run_task(self.fiscal_q1)
self.assertEqual(len(mail.outbox), 0, "Reminder mail should not be send again.")

def test_reminder_after_q1(self):
self.run_task(self.after_q1)
self.assertEqual(len(mail.outbox), 0)
Expand Down
9 changes: 9 additions & 0 deletions bluebottle/bluebottle_dashboard/static/admin/js/dashboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,14 @@ function hideDeleteButton() {
});
}

function hideInfoBoxLabel() {
jQuery(".inline-description").each(function(index, info) {
const row = info.parentElement.parentElement;
row.after(info);
row.remove();
});
}

window.onload = function() {
if (!django.jQuery && jQuery) {
django.jQuery = jQuery;
Expand All @@ -59,5 +67,6 @@ window.onload = function() {
removeRedundantTabs();
addHashToInlinePaginator();
hideDeleteButton();
hideInfoBoxLabel();
window.onhashchange = addHashToInlinePaginator;
};
31 changes: 26 additions & 5 deletions bluebottle/members/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
from bluebottle.segments.admin import SegmentAdminFormMetaClass
from bluebottle.segments.models import SegmentType
from bluebottle.time_based.models import DateParticipant, PeriodParticipant
from bluebottle.utils.admin import export_as_csv_action, BasePlatformSettingsAdmin
from bluebottle.utils.admin import export_as_csv_action, BasePlatformSettingsAdmin, admin_info_box
from bluebottle.utils.email_backend import send_mail
from bluebottle.utils.widgets import SecureAdminURLFieldWidget
from .models import Member, UserSegment
Expand All @@ -61,7 +61,7 @@ def __init__(self, data=None, files=None, current_user=None, *args, **kwargs):
super(MemberForm, self).__init__(data, files, *args, **kwargs)

if self.current_user.is_superuser:
# Super users can assign every group to a user
# Superusers can assign every group to a user
group_queryset = Group.objects.all()
else:
# Normal staff users can only choose groups that they belong to.
Expand Down Expand Up @@ -112,6 +112,19 @@ def save(self, commit=True):

class MemberPlatformSettingsAdmin(BasePlatformSettingsAdmin, NonSortableParentAdmin):

def reminder_info(self, obj):
return admin_info_box(
_('Quarterly emails will only be sent at the beginning of each '
'quarter if the impact hours are set. Users will only receive '
'the emails if they have not spent all the set hours.')
)

def impact_hours_info(self, obj):
return admin_info_box(
_('The impact hours feature will show the amount of hours '
'users are encouraged to spend making an impact each year.')
)

fieldsets = (
(
_('Login'),
Expand Down Expand Up @@ -142,16 +155,24 @@ class MemberPlatformSettingsAdmin(BasePlatformSettingsAdmin, NonSortableParentAd
}
),
(
_('Engagement'),
_('Impact hours'),
{
'description': _('Quarterly emails will only be sent to users who have not spent any hours.'),
'fields': (
'impact_hours_info',
'do_good_hours',
'fiscal_month_offset',
'reminder_info',
'reminder_q1',
'reminder_q2',
'reminder_q3',
'reminder_q4',
),
}
),
(
_('Initiatives'),
{
'fields': (
'create_initiatives',
),
}
Expand Down Expand Up @@ -193,7 +214,7 @@ def get_fieldsets(self, request, obj=None):

return fieldsets

readonly_fields = ('segment_types', )
readonly_fields = ('segment_types', 'reminder_info', 'impact_hours_info')

def segment_types(self, obj):
template = loader.get_template('segments/admin/required_segment_types.html')
Expand Down
20 changes: 20 additions & 0 deletions bluebottle/members/migrations/0067_auto_20220920_1139.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Generated by Django 2.2.24 on 2022-09-20 09:39

import bluebottle.bb_accounts.models
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('members', '0066_auto_20220920_0913'),
]

operations = [
migrations.AlterField(
model_name='memberplatformsettings',
name='fiscal_month_offset',
field=models.IntegerField(default=0, help_text='Use this if the fiscal years starts later or earlier then January. If the year starts September (so earlier) then this value should be 4.', verbose_name='Fiscal year offset'),
),
]
45 changes: 45 additions & 0 deletions bluebottle/members/migrations/0067_auto_20220923_1212.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Generated by Django 2.2.24 on 2022-09-23 10:12

import bluebottle.bb_accounts.models
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('members', '0066_auto_20220920_0913'),
]

operations = [
migrations.AlterField(
model_name='memberplatformsettings',
name='do_good_hours',
field=models.IntegerField(blank=True, help_text="Leave empty if this feature won't be used.", null=True, verbose_name='Impact hours'),
),
migrations.AlterField(
model_name='memberplatformsettings',
name='fiscal_month_offset',
field=models.PositiveIntegerField(blank=True, default=0, help_text='Set the number of months your fiscal year will be offset by. This will also take into account how the impact metrics are shown on the homepage. e.g. If the year starts from September (so earlier) then this value should be -4.', null=True, verbose_name='Fiscal year offset'),
),
migrations.AlterField(
model_name='memberplatformsettings',
name='reminder_q1',
field=models.BooleanField(default=False, verbose_name='Reminder Q1'),
),
migrations.AlterField(
model_name='memberplatformsettings',
name='reminder_q2',
field=models.BooleanField(default=False, verbose_name='Reminder Q2'),
),
migrations.AlterField(
model_name='memberplatformsettings',
name='reminder_q3',
field=models.BooleanField(default=False, verbose_name='Reminder Q3'),
),
migrations.AlterField(
model_name='memberplatformsettings',
name='reminder_q4',
field=models.BooleanField(default=False, verbose_name='Reminder Q4'),
),
]
25 changes: 25 additions & 0 deletions bluebottle/members/migrations/0068_auto_20220923_1420.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Generated by Django 2.2.24 on 2022-09-23 12:20

import bluebottle.bb_accounts.models
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('members', '0067_auto_20220923_1212'),
]

operations = [
migrations.AlterField(
model_name='memberplatformsettings',
name='do_good_hours',
field=models.PositiveIntegerField(blank=True, help_text="Leave empty if this feature won't be used.", null=True, verbose_name='Impact hours'),
),
migrations.AlterField(
model_name='memberplatformsettings',
name='fiscal_month_offset',
field=models.IntegerField(blank=True, default=0, help_text='Set the number of months your fiscal year will be offset by. This will also take into account how the impact metrics are shown on the homepage. e.g. If the year starts from September (so earlier) then this value should be -4.', null=True, verbose_name='Fiscal year offset'),
),
]
14 changes: 14 additions & 0 deletions bluebottle/members/migrations/0069_merge_20220928_1545.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Generated by Django 2.2.24 on 2022-09-28 13:45

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
('members', '0067_auto_20220920_1139'),
('members', '0068_auto_20220923_1420'),
]

operations = [
]
54 changes: 35 additions & 19 deletions bluebottle/members/models.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
from __future__ import absolute_import

from builtins import object
from datetime import timedelta, datetime

from dateutil.relativedelta import relativedelta
from django.conf import settings
from django.core.exceptions import ObjectDoesNotExist
from django.db import models
from django.db.models import Sum, Q
from django.db.models import Sum
from django.utils.timezone import now
from django.utils.translation import gettext_lazy as _
from future.utils import python_2_unicode_compatible
Expand Down Expand Up @@ -41,35 +43,33 @@ class MemberPlatformSettings(BasePlatformSettings):
create_initiatives = models.BooleanField(
default=True, help_text=_('Members can create initiatives')
)
do_good_hours = models.IntegerField(
do_good_hours = models.PositiveIntegerField(
_('Impact hours'),
null=True, blank=True,
help_text=_('The amount of hours users can spend each year. '
'Leave empty if no restrictions apply.')
help_text=_("Leave empty if this feature won't be used.")
)
fiscal_month_offset = models.PositiveIntegerField(
fiscal_month_offset = models.IntegerField(
_('Fiscal year offset'),
help_text=_('Use this if the fiscal years starts later or earlier then January. '
'If the year starts September (so earlier) then this value should be 4.'),
help_text=_('Set the number of months your fiscal year will be offset by. '
'This will also take into account how the impact metrics are shown on the homepage. '
'e.g. If the year starts from September (so earlier) then this value should be -4.'),
default=0)

reminder_q1 = models.BooleanField(
_('Reminder Q1'),
default=False,
help_text=_("This activation mail is sent in first month.")
)
reminder_q2 = models.BooleanField(
_('Reminder Q2'),
default=False,
help_text=_("This activation mail is sent in fourth month")
)
reminder_q3 = models.BooleanField(
_('Reminder Q3'),
default=False,
help_text=_("This activation mail is sent in seventh month.")
)
reminder_q4 = models.BooleanField(
_('Reminder Q4'),
default=False,
help_text=_("This activation mail is sent in tenth month.")
)

login_methods = MultiSelectField(max_length=100, choices=LOGIN_METHODS, default=['password'])
Expand Down Expand Up @@ -185,6 +185,20 @@ class MemberPlatformSettings(BasePlatformSettings):
)
)

@property
def fiscal_year_start(self):
offset = self.fiscal_month_offset
month_start = (datetime(2000, 1, 1) + relativedelta(months=offset)).month
return (now() + relativedelta(months=offset)).replace(month=month_start, day=1, hour=0, second=0)

@property
def fiscal_year_end(self):
return self.fiscal_year_start + relativedelta(years=1) - timedelta(seconds=1)

def fiscal_year(self):
offset = self.fiscal_month_offset
return (now() - relativedelta(months=offset)).year

class Meta(object):
verbose_name_plural = _('member platform settings')
verbose_name = _('member platform settings')
Expand Down Expand Up @@ -289,19 +303,19 @@ def required(self):
).count():
required.append(f'segment_type.{segment_type.id}')

settings = MemberPlatformSettings.load()
platform_settings = MemberPlatformSettings.load()

if settings.require_office and (
if platform_settings.require_office and (
not self.location or
(settings.verify_office and not self.location_verified)
(platform_settings.verify_office and not self.location_verified)
):
required.append('location')

for attr in ['birthdate', 'phone_number']:
if getattr(settings, f'require_{attr}') and not getattr(self, attr):
if getattr(platform_settings, f'require_{attr}') and not getattr(self, attr):
required.append(attr)

if settings.require_address and not (self.place and self.place.complete):
if platform_settings.require_address and not (self.place and self.place.complete):
required.append('address')

return required
Expand All @@ -310,10 +324,12 @@ def __str__(self):
return self.full_name

def get_hours(self, status):
platform_settings = MemberPlatformSettings.load()
year_start = platform_settings.fiscal_year_start
year_end = platform_settings.fiscal_year_end
hours = TimeContribution.objects.filter(
contributor__user=self, status=status
).filter(
Q(start__year=now().year) | Q(end__year=now().year)
contributor__user=self, status=status,
start__gte=year_start, start__lte=year_end
).aggregate(hours=Sum('value'))['hours']
if hours:
return hours.seconds / 3600
Expand Down
Loading