def setUp(self):

        self.url = '/api/activities/'

        # activity for group with one member and one place
        self.member = UserFactory()
        self.group = GroupFactory(members=[self.member])
        self.place = PlaceFactory(group=self.group)
        self.activity_type = ActivityTypeFactory(group=self.group)
        self.activity = ActivityFactory(activity_type=self.activity_type,
                                        place=self.place)

        # and another place + group + activity
        self.group2 = GroupFactory(members=[self.member])
        self.place2 = PlaceFactory(group=self.group2)
        self.activity_type2 = ActivityTypeFactory(group=self.group2)
        self.activity2 = ActivityFactory(activity_type=self.activity_type2,
                                         place=self.place2)

        # an activity series
        self.series = ActivitySeriesFactory(activity_type=self.activity_type,
                                            place=self.place)

        # another activity series
        self.series2 = ActivitySeriesFactory(activity_type=self.activity_type,
                                             place=self.place)
Exemplo n.º 2
0
 def setUp(self):
     self.now = timezone.now()
     self.member = UserFactory()
     self.group = GroupFactory(members=[self.member])
     self.place = PlaceFactory(group=self.group)
     self.series = ActivitySeriesFactory(max_participants=3,
                                         place=self.place)
Exemplo n.º 3
0
    def setUp(self):
        super().setUp()
        self.member = UserFactory()
        self.group = GroupFactory(members=[self.member])
        self.place = PlaceFactory(group=self.group)

        # Create far in the future to generate no activities
        # They would lead to interfering websocket messages
        self.series = ActivitySeriesFactory(place=self.place,
                                            start_date=timezone.now() +
                                            relativedelta(months=2))
Exemplo n.º 4
0
    def test_delete(self):
        now = timezone.now()
        two_weeks_ago = now - relativedelta(weeks=2)
        with freeze_time(two_weeks_ago, tick=True):
            series = ActivitySeriesFactory(place=self.place, start_date=two_weeks_ago)

        activities = series.activities.all()
        past_date_count = activities.filter(date__startswith__lt=now).count()
        self.assertGreater(activities.count(), 2)
        series.delete()
        upcoming_activities = Activity.objects.filter(date__startswith__gte=now, is_disabled=False)
        self.assertEqual(upcoming_activities.count(), 0, upcoming_activities)
        self.assertEqual(Activity.objects.filter(date__startswith__lt=now).count(), past_date_count)
Exemplo n.º 5
0
 def setUp(self):
     self.member = UserFactory()
     self.group = GroupFactory(members=[self.member])
     self.place = PlaceFactory(group=self.group)
     self.activity = ActivityFactory(place=self.place)
     self.activity_url = '/api/activities/{}/'.format(self.activity.id)
     self.series = ActivitySeriesFactory(place=self.place)
     self.series_url = '/api/activity-series/{}/'.format(self.series.id)
Exemplo n.º 6
0
    def setUp(self):

        self.now = timezone.now()
        self.url = '/api/places/'
        self.member = UserFactory()
        self.group = GroupFactory(members=[self.member])
        self.place = PlaceFactory(group=self.group)
        self.place_url = self.url + str(self.place.id) + '/'
        self.series = ActivitySeriesFactory(max_participants=3,
                                            place=self.place)
Exemplo n.º 7
0
 def setUp(self):
     self.url = '/api/activity-series/'
     self.series = ActivitySeriesFactory()
     self.series_url = '/api/activity-series/{}/'.format(self.series.id)
     self.non_member = UserFactory()
     self.series_data = {
         'place': self.series.place.id,
         'rule': 'FREQ=WEEKLY',
         'start_date': timezone.now()
     }
Exemplo n.º 8
0
    def test_create_all_activities_inactive_places(self):
        self.place.status = PlaceStatus.ARCHIVED.value
        self.place.save()

        start_date = self.place.group.timezone.localize(datetime.now().replace(2017, 3, 18, 15, 0, 0, 0))

        ActivitySeriesFactory(place=self.place, start_date=start_date)

        Activity.objects.all().delete()
        ActivitySeries.objects.update_activities()
        self.assertEqual(Activity.objects.count(), 0)
Exemplo n.º 9
0
    def test_daylight_saving_time_to_winter(self):
        start_date = self.place.group.timezone.localize(datetime.now().replace(2016, 10, 22, 15, 0, 0, 0))

        before_dst_switch = timezone.now().replace(2016, 10, 22, 4, 40, 13)
        with freeze_time(before_dst_switch, tick=True):
            series = ActivitySeriesFactory(place=self.place, start_date=start_date)

        expected_dates = []
        for month, day in [(10, 22), (10, 29), (11, 5), (11, 12)]:
            expected_dates.append(self.place.group.timezone.localize(datetime(2016, month, day, 15, 0)))
        for actual_date, expected_date in zip(Activity.objects.filter(series=series), expected_dates):
            self.assertEqual(actual_date.date.start, expected_date)
Exemplo n.º 10
0
class ActivitySeriesReceiverTests(WSTestCase):
    def setUp(self):
        super().setUp()
        self.member = UserFactory()
        self.group = GroupFactory(members=[self.member])
        self.place = PlaceFactory(group=self.group)

        # Create far in the future to generate no activities
        # They would lead to interfering websocket messages
        self.series = ActivitySeriesFactory(place=self.place,
                                            start_date=timezone.now() +
                                            relativedelta(months=2))

    def test_receive_series_changes(self):
        client = self.connect_as(self.member)

        date = faker.future_datetime(
            end_date='+30d', tzinfo=timezone.utc) + relativedelta(months=2)
        self.series.start_date = date
        self.series.save()

        response = client.messages_by_topic.get('activities:series')[0]
        self.assertEqual(parse(response['payload']['start_date']), date)

        self.assertEqual(len(client.messages), 1)

    def test_receive_series_delete(self):
        client = self.connect_as(self.member)

        id = self.series.id
        self.series.delete()

        response = client.messages_by_topic.get('activities:series_deleted')[0]
        self.assertEqual(response['payload']['id'], id)

        self.assertEqual(len(client.messages), 1)
Exemplo n.º 11
0
class TestActivitySeriesChangeAPI(APITestCase, ExtractPaginationMixin):
    """
    This is an integration test for the activity-series API with pre-created series
    """
    def setUp(self):
        self.now = timezone.now()
        self.member = UserFactory()
        self.group = GroupFactory(members=[self.member])
        self.place = PlaceFactory(group=self.group)
        self.series = ActivitySeriesFactory(max_participants=3, place=self.place)

    def test_change_max_participants_for_series(self):
        "should change all future instances (except for individually changed ones), but not past ones"
        url = '/api/activity-series/{}/'.format(self.series.id)
        self.client.force_login(user=self.member)
        response = self.client.patch(url, {'max_participants': 99})
        self.assertEqual(response.status_code, status.HTTP_200_OK, response.data)
        self.assertEqual(response.data['max_participants'], 99)

        url = '/api/activities/'
        response = self.get_results(url, {'series': self.series.id, 'date_min': self.now})
        self.assertEqual(response.status_code, status.HTTP_200_OK, response.data)
        for _ in response.data:
            self.assertEqual(_['max_participants'], 99)

    def test_change_series_activates_group(self):
        self.group.status = GroupStatus.INACTIVE.value
        self.group.save()
        url = '/api/activity-series/{}/'.format(self.series.id)
        self.client.force_login(user=self.member)
        self.client.patch(url, {'max_participants': 99})
        self.group.refresh_from_db()
        self.assertEqual(self.group.status, GroupStatus.ACTIVE.value)

    def test_change_start_time(self):
        self.client.force_login(user=self.member)
        # get original times
        url = '/api/activities/'
        response = self.get_results(url, {'series': self.series.id, 'date_min': self.now})
        self.assertEqual(response.status_code, status.HTTP_200_OK, response.data)
        original_dates = [parse(_['date'][0]) for _ in response.data]

        # change times
        url = '/api/activity-series/{}/'.format(self.series.id)
        new_startdate = shift_date_in_local_time(
            self.series.start_date, relativedelta(hours=2, minutes=20), self.group.timezone
        )
        response = self.client.patch(url, {'start_date': new_startdate.isoformat()})
        self.assertEqual(response.status_code, status.HTTP_200_OK, response.data)
        self.assertEqual(parse(response.data['start_date']), new_startdate)

        # compare resulting activities
        url = '/api/activities/'
        response = self.get_results(url, {'series': self.series.id, 'date_min': self.now})
        self.assertEqual(response.status_code, status.HTTP_200_OK, response.data)
        for response_activity, old_date in zip(response.data, original_dates):
            self.assertEqual(
                parse(response_activity['date'][0]),
                shift_date_in_local_time(old_date, relativedelta(hours=2, minutes=20), self.group.timezone)
            )

    def test_change_start_date_to_future(self):
        self.client.force_login(user=self.member)
        # get original dates
        url = '/api/activities/'
        response = self.get_results(url, {'series': self.series.id, 'date_min': self.now})
        self.assertEqual(response.status_code, status.HTTP_200_OK, response.data)
        original_dates = [parse(_['date'][0]) for _ in response.data]

        # change dates
        url = '/api/activity-series/{}/'.format(self.series.id)
        new_startdate = shift_date_in_local_time(self.series.start_date, relativedelta(days=5), self.group.timezone)
        response = self.client.patch(url, {'start_date': new_startdate.isoformat()})
        self.assertEqual(response.status_code, status.HTTP_200_OK, response.data)
        self.assertEqual(parse(response.data['start_date']), new_startdate)

        # compare resulting activities
        url = '/api/activities/'
        response = self.get_results(url, {'series': self.series.id, 'date_min': self.now})
        self.assertEqual(response.status_code, status.HTTP_200_OK, response.data)
        for response_activity, old_date in zip_longest(response.data, original_dates):
            self.assertEqual(
                parse(response_activity['date'][0]),
                shift_date_in_local_time(old_date, relativedelta(days=5), self.group.timezone)
            )

    def test_change_start_date_to_past(self):
        self.client.force_login(user=self.member)
        # get original dates
        url = '/api/activities/'
        response = self.get_results(url, {'series': self.series.id, 'date_min': self.now})
        self.assertEqual(response.status_code, status.HTTP_200_OK, response.data)
        original_dates = [parse(_['date'][0]) for _ in response.data]

        # change dates
        url = '/api/activity-series/{}/'.format(self.series.id)
        new_startdate = shift_date_in_local_time(self.series.start_date, relativedelta(days=-5), self.group.timezone)
        response = self.client.patch(url, {'start_date': new_startdate.isoformat()})
        self.assertEqual(response.status_code, status.HTTP_200_OK, response.data)
        self.assertEqual(parse(response.data['start_date']), new_startdate)

        # compare resulting activities
        url = '/api/activities/'
        response = self.get_results(url, {'series': self.series.id, 'date_min': self.now})
        self.assertEqual(response.status_code, status.HTTP_200_OK, response.data)

        # shifting 5 days to the past is similar to shifting 2 days to the future
        for response_activity, old_date in zip_longest(response.data, original_dates):
            new_date = shift_date_in_local_time(old_date, relativedelta(days=2), self.group.timezone)
            if new_date > self.now + relativedelta(weeks=self.place.weeks_in_advance):
                # date too far in future
                self.assertIsNone(response_activity)
            else:
                self.assertEqual(parse(response_activity['date'][0]), new_date)

    def test_set_end_date(self):
        self.client.force_login(user=self.member)
        # change rule
        url = '/api/activity-series/{}/'.format(self.series.id)
        rule = 'FREQ=WEEKLY;UNTIL={}'.format((self.now + relativedelta(days=8)).strftime('%Y%m%dT%H%M%S'))
        response = self.client.patch(url, {'rule': rule})
        self.assertEqual(response.status_code, status.HTTP_200_OK, response.data)
        self.assertEqual(response.data['rule'], rule)

        # compare resulting activities
        url = '/api/activities/'
        response = self.get_results(url, {'series': self.series.id, 'date_min': self.now})
        self.assertEqual(response.status_code, status.HTTP_200_OK, response.data)
        self.assertEqual(len(response.data), 2, response.data)

    def test_set_end_date_with_timezone(self):
        self.client.force_login(user=self.member)
        url = '/api/activity-series/{}/'.format(self.series.id)
        rule = 'FREQ=WEEKLY;UNTIL={}+0100'.format((self.now + relativedelta(days=8)).strftime('%Y%m%dT%H%M%S'))
        response = self.client.patch(url, {'rule': rule})
        self.assertEqual(response.status_code, status.HTTP_200_OK, response.data)

    def test_set_end_date_with_users_have_joined_activity(self):
        self.client.force_login(user=self.member)
        self.series.activities.last().add_participant(self.member)
        # change rule
        url = '/api/activity-series/{}/'.format(self.series.id)
        rule = rrulestr(self.series.rule, dtstart=self.now) \
            .replace(until=self.now)
        response = self.client.patch(url, {
            'rule': str(rule),
        })
        self.assertEqual(response.status_code, status.HTTP_200_OK, response.data)
        self.assertEqual(response.data['rule'], str(rule))

        # compare resulting activities
        url = '/api/activities/'
        response = self.get_results(url, {'series': self.series.id, 'date_min': self.now})
        self.assertEqual(response.status_code, status.HTTP_200_OK, response.data)
        self.assertEqual(len(response.data), 1, response.data)

    def test_disable_activity_series(self):
        "the series should get removed, empty upcoming activities disabled, non-empty activities kept"
        self.client.force_login(user=self.member)
        joined_activity = self.series.activities.last()
        joined_activity.add_participant(self.member)

        url = '/api/activity-series/{}/'.format(self.series.id)
        response = self.client.delete(url)
        self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT, response.data)

        url = '/api/activities/'
        response = self.get_results(url, {'date_min': self.now})
        self.assertEqual(response.status_code, status.HTTP_200_OK, response.data)
        empty_activities = [p for p in response.data if len(p['participants']) == 0]
        self.assertEqual(empty_activities, [])

        url = '/api/activities/{}/'.format(joined_activity.id)
        response = self.client.get(url)
        self.assertEqual(response.status_code, status.HTTP_200_OK, response.data)
        self.assertEqual(response.data['participants'], [self.member.id])
        self.assertFalse(response.data['is_disabled'])

    def test_change_max_participants_to_invalid_number_fails(self):
        self.client.force_login(user=self.member)
        url = '/api/activity-series/{}/'.format(self.series.id)
        response = self.client.patch(url, {'max_participants': -1})
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST, response.data)

    def test_set_invalid_place_fails(self):
        original_place = self.series.place
        unrelated_place = PlaceFactory()

        self.client.force_login(user=self.member)
        url = '/api/activity-series/{}/'.format(self.series.id)
        response = self.client.patch(url, {'place': unrelated_place.id})
        self.assertEqual(response.status_code, status.HTTP_200_OK, response.data)
        self.assertEqual(response.data['place'], original_place.id)
        self.series.refresh_from_db()
        self.assertEqual(self.series.place.id, original_place.id)

    def test_set_multiple_rules_fails(self):
        self.client.force_login(user=self.member)
        url = '/api/activity-series/{}/'.format(self.series.id)
        response = self.client.patch(url, {'rule': 'RRULE:FREQ=WEEKLY;BYDAY=MO\nRRULE:FREQ=MONTHLY'})
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST, response.data)
        self.assertEqual(response.data, {'rule': ['Only single recurrence rules are allowed.']})

    def test_keep_changes_to_max_participants(self):
        self.client.force_login(user=self.member)
        activity_under_test = self.series.activities.first()
        url = '/api/activities/{}/'.format(activity_under_test.id)

        # change setting of activity
        response = self.client.patch(url, {'max_participants': 666})
        self.assertEqual(response.status_code, status.HTTP_200_OK, response.data)
        self.assertEqual(response.data['max_participants'], 666)

        # run regular update command of series
        self.series.update_activities()

        # check if changes persist
        url = '/api/activities/{}/'.format(activity_under_test.id)
        response = self.client.get(url)
        self.assertEqual(response.status_code, status.HTTP_200_OK, response.data)
        self.assertEqual(response.data['max_participants'], 666)

        # modify series max_participants
        series_url = '/api/activity-series/{}/'.format(self.series.id)
        response = self.client.patch(series_url, {'max_participants': 20})
        self.assertEqual(response.status_code, status.HTTP_200_OK, response.data)

        # check if changes persist
        url = '/api/activities/{}/'.format(activity_under_test.id)
        response = self.client.get(url)
        self.assertEqual(response.status_code, status.HTTP_200_OK, response.data)
        self.assertEqual(response.data['max_participants'], 666)

    def test_keep_changes_to_description(self):
        self.client.force_login(user=self.member)
        activity_under_test = self.series.activities.first()
        url = '/api/activities/{}/'.format(activity_under_test.id)

        # change setting of activity
        response = self.client.patch(url, {'description': 'asdf'})
        self.assertEqual(response.status_code, status.HTTP_200_OK, response.data)
        self.assertEqual(response.data['description'], 'asdf')

        # run regular update command of series
        self.series.update_activities()

        # check if changes persist
        url = '/api/activities/{}/'.format(activity_under_test.id)
        response = self.client.get(url)
        self.assertEqual(response.status_code, status.HTTP_200_OK, response.data)
        self.assertEqual(response.data['description'], 'asdf')

        # modify series description
        series_url = '/api/activity-series/{}/'.format(self.series.id)
        response = self.client.patch(series_url, {'description': 'new series description'})
        self.assertEqual(response.status_code, status.HTTP_200_OK, response.data)

        # check if changes persist
        url = '/api/activities/{}/'.format(activity_under_test.id)
        response = self.client.get(url)
        self.assertEqual(response.status_code, status.HTTP_200_OK, response.data)
        self.assertEqual(response.data['description'], 'asdf')

    def test_invalid_rule_fails(self):
        url = '/api/activity-series/{}/'.format(self.series.id)
        self.client.force_login(user=self.member)
        response = self.client.patch(url, {'rule': 'FREQ=WEEKLY;BYDAY='})
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST, response.data)

    def test_keeps_joined_activities(self):
        # join activities
        [p.add_participant(self.member) for p in self.series.activities.all()]

        # change series rule to add another day
        today = self.now.astimezone(self.group.timezone).weekday()
        tomorrow = shift_date_in_local_time(self.now, relativedelta(days=1),
                                            self.group.timezone).astimezone(self.group.timezone).weekday()
        recurrence = rrule.rrule(
            freq=rrule.WEEKLY,
            byweekday=[
                today,
                tomorrow,
            ],
        )
        series_url = '/api/activity-series/{}/'.format(self.series.id)
        self.client.force_login(user=self.member)
        response = self.client.patch(series_url, {
            'rule': str(recurrence),
        })
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.series.refresh_from_db()

        response = self.client.get('/api/activities/?series={}'.format(self.series.id))
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        # self.assertEqual([parse(p['date'][0]) for p in response.data['results']], [
        #     shift_date_in_local_time(self.series.start_date, delta, self.group.timezone) for delta in (
        #         relativedelta(days=0),
        #         relativedelta(days=1),
        #         relativedelta(days=7),
        #         relativedelta(days=8),
        #         relativedelta(days=14),
        #         relativedelta(days=15),
        #         relativedelta(days=21),
        #         relativedelta(days=22),
        #     )
        # ])
        self.assertEqual(
            [p['participants'] for p in response.data['results']],
            list(interleave(
                [[self.member.id] for _ in range(4)],
                [[] for _ in range(4)],
            )),
        )

    def test_removes_empty_leftover_activities_when_reducing_weeks_in_advance(self):
        # join one activity
        joined_activity = self.series.activities.first()
        joined_activity.add_participant(self.member)

        # change weeks_in_advance
        place_url = '/api/places/{}/'.format(self.place.id)
        self.client.force_login(user=self.member)
        response = self.client.patch(place_url, {
            'weeks_in_advance': 1,
        })
        self.assertEqual(response.status_code, status.HTTP_200_OK)

        response = self.get_results('/api/activities/?series={}'.format(self.series.id))
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(len(response.data), 1)
        self.assertEqual(response.data[0]['id'], joined_activity.id)
        self.assertEqual(response.data[0]['participants'], [self.member.id])

    def test_cannot_move_activities_in_a_series(self):
        self.client.force_login(user=self.member)
        activity = self.series.activities.last()

        response = self.client.patch(
            '/api/activities/{}/'.format(activity.id),
            {
                'date': (activity.date + relativedelta(weeks=7)).as_list(),
            },
            format='json',
        )

        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
        self.assertIn('You can\'t move activities', response.data['date'][0])

    def test_cannot_change_activity_has_duration_in_a_series(self):
        self.client.force_login(user=self.member)
        activity = self.series.activities.last()

        response = self.client.patch(
            '/api/activities/{}/'.format(activity.id),
            {'has_duration': True},
            format='json',
        )

        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
        self.assertEqual(
            'You cannot modify the duration of activities that are part of a series', response.data['has_duration'][0]
        )
Exemplo n.º 12
0
 def setUp(self):
     self.series = ActivitySeriesFactory()