Skip to content

Commit

Permalink
Merge 71a3e61 into 257b464
Browse files Browse the repository at this point in the history
  • Loading branch information
gannetson committed Sep 30, 2022
2 parents 257b464 + 71a3e61 commit 3efcb2c
Show file tree
Hide file tree
Showing 17 changed files with 423 additions and 37 deletions.
8 changes: 6 additions & 2 deletions bluebottle/activities/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,6 @@ def save_model(self, request, obj, form, change):

office_fields = (
'office_location',
'office_restriction',
)

description_fields = (
Expand Down Expand Up @@ -430,7 +429,12 @@ def get_fieldsets(self, request, obj=None):
(_('Description'), {'fields': self.get_description_fields(request, obj)}),
(_('Status'), {'fields': self.get_status_fields(request, obj)}),
]
if settings.enable_office_regions:

if Location.objects.count():
if settings.enable_office_restrictions and 'office_restriction' not in self.office_fields:
self.office_fields += (
'office_restriction',
)
fieldsets.insert(1, (
_('Office'), {'fields': self.office_fields}
))
Expand Down
29 changes: 24 additions & 5 deletions bluebottle/activities/documents.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
from builtins import str

from django_elasticsearch_dsl import Document, fields
from elasticsearch_dsl.field import DateRange

from bluebottle.activities.models import Activity
from bluebottle.funding.models import Donor
from bluebottle.initiatives.models import Initiative, Theme
from bluebottle.members.models import Member
from bluebottle.utils.documents import MultiTenantIndex
from bluebottle.activities.models import Activity
from bluebottle.utils.search import Search
from elasticsearch_dsl.field import DateRange
from bluebottle.members.models import Member

from bluebottle.initiatives.models import Initiative, Theme


class DateRangeField(fields.DEDField, DateRange):
Expand Down Expand Up @@ -110,6 +110,16 @@ class ActivityDocument(Document):
}
)

office_restriction = fields.NestedField(
attr='office_restriction',
properties={
'restriction': fields.TextField(),
'office': fields.LongField(),
'office_subregion': fields.LongField(),
'office_region': fields.LongField(),
}
)

contributors = fields.DateField()
contributor_count = fields.IntegerField()
donation_count = fields.IntegerField()
Expand Down Expand Up @@ -233,6 +243,15 @@ def prepare_location(self, instance):
})
return locations

def prepare_office_restriction(self, instance):
office = instance.office_location
return {
'restriction': instance.office_restriction,
'office': office.id if office else None,
'subregion': office.subregion.id if office and office.subregion_id else None,
'region': office.subregion.region.id if office and office.subregion and office.subregion.region else None
}

def prepare_expertise(self, instance):
if hasattr(instance, 'expertise') and instance.expertise:
return [
Expand Down
30 changes: 27 additions & 3 deletions bluebottle/activities/filters.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import re
import dateutil
from datetime import datetime, time

import dateutil
from django.conf import settings

from elasticsearch_dsl.function import ScriptScore
from elasticsearch_dsl.query import (
FunctionScore, SF, Terms, Term, Nested, Q, Range, ConstantScore
)
from elasticsearch_dsl.function import ScriptScore

from bluebottle.activities.documents import activity
from bluebottle.geo.models import Location
from bluebottle.initiatives.models import InitiativePlatformSettings
from bluebottle.utils.filters import ElasticSearchFilter


Expand All @@ -34,6 +36,7 @@ class ActivitySearchFilter(ElasticSearchFilter):
'status',
'upcoming',
'location.id',
'office',
'segment',
'team_activity',
'initiative.id',
Expand Down Expand Up @@ -162,6 +165,27 @@ def get_upcoming_filter(self, value, request):
if value == 'false':
return Terms(status=['succeeded', 'partially_funded'])

def get_office_filter(self, value, request):
office = Location.objects.filter(id=value).first()
initiative_settings = InitiativePlatformSettings.load()
if initiative_settings.enable_office_restrictions:
return Nested(
path='office_restriction',
query=Term(
office_restriction__restriction='all'
) | (
Term(office_restriction__office=office.id) &
Term(office_restriction__restriction='office')
) | (
Term(office_restriction__subregion=office.subregion.id) &
Term(office_restriction__restriction='office_subregion')
) | (
Term(office_restriction__region=office.subregion.region.id) &
Term(office_restriction__restriction='office_region')
)
)
return []

def get_duration_filter(self, value, request):
start = request.GET.get('filter[start]')
end = request.GET.get('filter[end]')
Expand Down
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
92 changes: 92 additions & 0 deletions bluebottle/activities/tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from bluebottle.activities.utils import TeamSerializer, InviteSerializer
from bluebottle.activities.serializers import TeamTransitionSerializer
from bluebottle.funding.tests.factories import FundingFactory, DonorFactory
from bluebottle.offices.tests.factories import OfficeRegionFactory, OfficeSubRegionFactory
from bluebottle.time_based.serializers import PeriodParticipantSerializer
from bluebottle.time_based.tests.factories import (
DateActivityFactory, PeriodActivityFactory, DateParticipantFactory, PeriodParticipantFactory,
Expand Down Expand Up @@ -1031,6 +1032,97 @@ def test_filter_segment_mismatch(self):

self.assertEqual(data['meta']['pagination']['count'], 0)

def test_filter_by_office(self):

europe = OfficeRegionFactory.create(name='Europe')
africa = OfficeRegionFactory.create(name='Africa')
netherlands = OfficeSubRegionFactory.create(
region=europe,
name='The Netherlands'
)
bulgaria = OfficeSubRegionFactory.create(
region=europe,
name='Bulgaria'
)
namibia = OfficeSubRegionFactory.create(
region=africa,
name='Nambibia'
)
amsterdam = LocationFactory.create(name='Amsterdam', subregion=netherlands)
leiden = LocationFactory.create(name='Leiden', subregion=netherlands)
lyutidol = LocationFactory.create(name='Lyutidol', subregion=bulgaria)
windhoek = LocationFactory.create(name='Windhoek', subregion=namibia)

DateActivityFactory.create(
office_location=leiden,
status='open',
office_restriction='office'
)
DateActivityFactory.create(
office_location=amsterdam,
status='open',
office_restriction='office_subregion'
)
DateActivityFactory.create(
office_location=amsterdam,
status='open',
office_restriction='office_subregion'
)
DateActivityFactory.create(
office_location=lyutidol,
status='open',
office_restriction='office_region'
)
DateActivityFactory.create(
office_location=windhoek,
status='open',
office_restriction='all'
)
DateActivityFactory.create(
office_location=windhoek,
status='open',
office_restriction='office_region'
)
platform = InitiativePlatformSettings.load()
platform.enable_office_restrictions = False
platform.save()

user = BlueBottleUserFactory.create()
response = self.client.get(self.url, user=user)
data = json.loads(response.content)
self.assertEqual(data['meta']['pagination']['count'], 6)

user = BlueBottleUserFactory.create()
response = self.client.get(f'{self.url}?filter[office]={windhoek.id}', user=user)
data = json.loads(response.content)
self.assertEqual(data['meta']['pagination']['count'], 6)

platform = InitiativePlatformSettings.load()
platform.enable_office_restrictions = True
platform.save()

user = BlueBottleUserFactory.create()
response = self.client.get(self.url, user=user)
data = json.loads(response.content)
self.assertEqual(data['meta']['pagination']['count'], 6)

user = BlueBottleUserFactory.create()
response = self.client.get(f'{self.url}?filter[office]={windhoek.id}', user=user)
data = json.loads(response.content)
self.assertEqual(data['meta']['pagination']['count'], 2)

response = self.client.get(f'{self.url}?filter[office]={leiden.id}', user=user)
data = json.loads(response.content)
self.assertEqual(data['meta']['pagination']['count'], 5)

response = self.client.get(f'{self.url}?filter[office]={lyutidol.id}', user=user)
data = json.loads(response.content)
self.assertEqual(data['meta']['pagination']['count'], 2)

response = self.client.get(f'{self.url}?filter[office]={amsterdam.id}', user=user)
data = json.loads(response.content)
self.assertEqual(data['meta']['pagination']['count'], 4)

def test_search(self):
first = DateActivityFactory.create(
title='Lorem ipsum dolor sit amet',
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
3 changes: 2 additions & 1 deletion bluebottle/initiatives/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,8 @@ class InitiativePlatformSettingsAdmin(BasePlatformSettingsAdmin):
(_('Options'), {
'fields': (
'contact_method', 'require_organization',
'enable_impact', 'enable_office_regions', 'enable_multiple_dates',
'enable_impact', 'enable_office_regions', 'enable_office_restrictions',
'enable_multiple_dates',
'enable_open_initiatives', 'enable_participant_exports',
'enable_matching_emails',
)
Expand Down
22 changes: 22 additions & 0 deletions bluebottle/initiatives/migrations/0042_auto_20220928_1600.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Generated by Django 2.2.24 on 2022-09-28 14:00

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import multiselectfield.db.fields
import parler.fields


class Migration(migrations.Migration):

dependencies = [
('initiatives', '0041_merge_20220906_0704'),
]

operations = [
migrations.AddField(
model_name='initiativeplatformsettings',
name='enable_office_restrictions',
field=models.BooleanField(default=False, help_text='Allow activity managers to specify office restrictions on activities.'),
),
]
4 changes: 4 additions & 0 deletions bluebottle/initiatives/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,10 @@ class InitiativePlatformSettings(BasePlatformSettings):
default=False,
help_text=_("Allow admins to add (sub)regions to their offices.")
)
enable_office_restrictions = models.BooleanField(
default=False,
help_text=_("Allow activity managers to specify office restrictions on activities.")
)
enable_multiple_dates = models.BooleanField(
default=False,
help_text=_("Enable date activities to have multiple slots.")
Expand Down
1 change: 1 addition & 0 deletions bluebottle/initiatives/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,7 @@ class Meta(object):
'contact_method',
'enable_impact',
'enable_office_regions',
'enable_office_restrictions',
'enable_multiple_dates',
'enable_participant_exports',
'enable_open_initiatives',
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'),
),
]
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 = [
]
Loading

0 comments on commit 3efcb2c

Please sign in to comment.