def test_get_rrule_end(self): """ Test that get_rrule_end returns the rrule end correctly """ # when until is provided # pylint: disable=line-too-long rule1 = "DTSTART:20180501T210000Z RRULE:FREQ=YEARLY;BYDAY=SU;BYSETPOS=1;BYMONTH=1;UNTIL=20480521T210000Z" # noqa end1 = get_rrule_end(rrulestr(rule1)) # must be timezone aware self.assertTrue(timezone.is_aware(end1)) # must be 21 may 2018 self.assertEqual(2048, end1.year) self.assertEqual(5, end1.month) self.assertEqual(21, end1.day) # when count is provided instead rule2 = "RRULE:FREQ=DAILY;COUNT=5" end2 = get_rrule_end(rrulestr(rule2)) # must be timezone aware self.assertTrue(timezone.is_aware(end2)) # must be 4 days from now (5 occurrences with today as the first) now = timezone.now().astimezone(pytz.timezone("Africa/Nairobi")) then = now + timedelta(days=4) self.assertEqual(then.year, end2.year) self.assertEqual(then.month, end2.month) self.assertEqual(then.day, end2.day)
def test_get_start_end_from_timing_rules(self): """ Test get_start_end_from_timing_rules """ zero = ['invalid'] one = [None] two = [] three = [None, 'RRULE:FREQ=DAILY;INTERVAL=10;COUNT=5'] four = [ 'RRULE:FREQ=DAILY;INTERVAL=10;COUNT=5', 'RRULE:FREQ=DAILY;INTERVAL=10;COUNT=17', 'DTSTART:20170521T210000Z RRULE:FREQ=DAILY;INTERVAL=10;COUNT=5' ] self.assertEqual((None, None), get_start_end_from_timing_rules(zero)) self.assertEqual((None, None), get_start_end_from_timing_rules(one)) self.assertEqual((None, None), get_start_end_from_timing_rules(two)) self.assertEqual( (get_rrule_start(rrulestr('RRULE:FREQ=DAILY;INTERVAL=10;COUNT=5')), get_rrule_end(rrulestr('RRULE:FREQ=DAILY;INTERVAL=10;COUNT=5'))), get_start_end_from_timing_rules(three)) self.assertEqual( (get_rrule_start( rrulestr('DTSTART:20170521T210000Z RRULE:FREQ=DAILY;COUNT=5')), get_rrule_end(rrulestr('RRULE:FREQ=DAILY;INTERVAL=10;COUNT=17'))), get_start_end_from_timing_rules(four))
def get_start_end_from_timing_rules(timing_rules: list): """ Get the start and end times from timing rules Will return the very very first start and the very very last end from the provided timing_rules """ start, end = None, None start_list = [] end_list = [] # remove obviously invalid timing_rules = [x for x in timing_rules if x] for timing_rule in timing_rules: try: start_list.append(get_rrule_start(rrulestr(timing_rule))) except ValueError: pass try: end_list.append(get_rrule_end(rrulestr(timing_rule))) except ValueError: pass if start_list: start = min(start_list) if end_list: end = max(end_list) return (start, end)
def test_create_task(self): """ Test that the serializer can create Task objects """ mocked_target_object = mommy.make( 'ona.XForm', title='Coconut', id_string='coconut828', version='v828', json=dict( owner="mosh", owner_url="http://example.com/mosh" ), ) rule1 = mommy.make('main.SegmentRule') rule2 = mommy.make('main.SegmentRule') rrule = 'DTSTART:20180521T210000Z RRULE:FREQ=DAILY;INTERVAL=10;COUNT=5' data = { "type": "Task", 'name': 'Cow price', 'description': 'Some description', 'total_submission_target': 10, 'timing_rule': rrule, 'target_content_type': self.xform_type.id, 'target_id': mocked_target_object.id, 'estimated_time': 'P4DT1H15M20S', } data_with_segment_rules = data.copy() segment_rules = [ { "type": "SegmentRule", "id": rule1.id }, { "type": "SegmentRule", "id": rule2.id } ] data_with_segment_rules['segment_rules'] = segment_rules serializer_instance = KaznetTaskSerializer( data=data_with_segment_rules) self.assertTrue(serializer_instance.is_valid()) task = serializer_instance.save() # the start and end fields are going to be from the timing rule start = get_rrule_start(rrulestr(rrule)) end = get_rrule_end(rrulestr(rrule)) # Change estimated time to DD HH:MM:SS format since Serializer # Changes it to such data['estimated_time'] = '4 01:15:20' # 'type' is not returns data.pop('type') # the order of segment_rules may have changed so a dict comparison # may fail, we use `data` that does not include segment rules self.assertDictContainsSubset(data, serializer_instance.data) # we test that we do have our segment rules self.assertEqual(2, len(serializer_instance.data['segment_rules'])) # we test that submissions are equal to 0 self.assertEqual(serializer_instance.data['submission_count'], 0) self.assertEqual(task.submissions, 0) # we test that rejectedsubmissions are equal to 0 self.assertEqual( serializer_instance.data['rejected_submissions_count'], 0) self.assertEqual(task.rejected_submissions_count, 0) # we test that pending submissions are equal to 0 self.assertEqual( serializer_instance.data['pending_submissions_count'], 0) self.assertEqual(task.pending_submissions_count, 0) # we test that approved submissions are equal to 0 self.assertEqual( serializer_instance.data['approved_submissions_count'], 0) self.assertEqual(task.approved_submissions_count, 0) # we test that total_bounty_payout is 0 self.assertEqual( serializer_instance.data['total_bounty_payout'], '0 KES') self.assertEqual(task.total_bounty_payout, Money(0, 'KES')) # Add a submission to task and assert it changes. mocked_submission = mommy.make('main.Submission', task=task) self.assertTrue(mocked_submission.task, task) self.assertEqual(task.submissions, 1) self.assertEqual('Cow price', task.name) self.assertEqual('Some description', task.description) self.assertEqual(start, task.start) self.assertEqual(end, task.end) self.assertEqual(10, task.total_submission_target) # assert that the ISO 8601 String was converted to accurately self.assertEqual(task.estimated_time, timedelta(4, 4520)) # test that the segment rules for the task are as we expect self.assertEqual(rule1, task.segment_rules.get(id=rule1.id)) self.assertEqual(rule2, task.segment_rules.get(id=rule2.id)) # check that you get XForm stuff self.assertEqual( serializer_instance.data['xform_title'], mocked_target_object.title) self.assertEqual( serializer_instance.data['xform_id_string'], mocked_target_object.id_string) self.assertEqual( serializer_instance.data['xform_version'], mocked_target_object.version) self.assertEqual( serializer_instance.data['xform_owner'], mocked_target_object.json.get('owner')) self.assertEqual( serializer_instance.data['xform_owner_url'], mocked_target_object.json.get('owner_url')) # test no bounty was created since amount wasn't passed # pylint: disable=no-member self.assertEqual(Bounty.objects.all().count(), 0) expected_fields = [ 'id', 'created', 'created_by', 'created_by_name', 'modified', 'name', 'client_name', 'parent', 'description', 'xform_title', 'xform_id_string', 'approved_submissions_count', 'pending_submissions_count', 'required_expertise_display', 'rejected_submissions_count', 'total_bounty_payout', 'current_bounty_amount', 'bounty', 'start', 'required_expertise', 'client', 'end', 'status_display', 'timing_rule', 'estimated_time', 'total_submission_target', 'user_submission_target', 'status', 'submission_count', 'target_content_type', 'target_id', 'segment_rules', 'locations', 'task_locations', 'xform_ona_id', 'xform_project_id', 'xform_version', 'xform_owner', 'xform_owner_url', ] self.assertEqual(set(expected_fields), set(list(serializer_instance.data.keys())))
def validate(self, attrs): """ Object level validation method for TaskSerializer """ if not self.instance: # this is a new object # get start from input start_from_input = attrs.get('start') # get timing rules from task locations tasklocation_timing_rules = [] for location_input in attrs.get('locations_input', []): tasklocation_timing_rules.append(location_input['timing_rule']) # get start and end from timing rules timing_rule = attrs.get('timing_rule') if timing_rule is not None: timing_rule_start = get_rrule_start(rrulestr(timing_rule)) timing_rule_end = get_rrule_end(rrulestr(timing_rule)) else: timing_rule_start = None timing_rule_end = None if not start_from_input: # start was not input by user, we try and generate it from # timing rules if not timing_rule_start: # we cannot determine a start time raise serializers.ValidationError({ 'timing_rule': MISSING_START_DATE, 'start': MISSING_START_DATE }) attrs['start'] = timing_rule_start # get end attrs['end'] = attrs.get('end', timing_rule_end) # If end date is present we validate that it is greater than start_date if attrs.get('end') is not None: # If end date is lesser than the start date raise an error the_start = attrs.get('start') if the_start is None and self.instance is not None: the_start = self.instance.start if not the_start: raise serializers.ValidationError( {'start': INVALID_START_DATE}) if attrs['end'] < the_start: raise serializers.ValidationError({'end': INVALID_END_DATE}) # If start date is present and this is an existing object, we validate # that the start date is not greater than the existing end date if attrs.get('start') is not None and self.instance is not None\ and self.instance.end is not None and attrs.get('start') >\ self.instance.end: raise serializers.ValidationError({'start': INVALID_START_DATE}) return super(TaskSerializer, self).validate(attrs)
def validate(self, attrs): """ Object level validation method for TaskSerializer """ if not self.instance: # this is a new object # get start from input start_from_input = attrs.get("start") # get timing rules from task locations tasklocation_timing_rules = [] for location_input in attrs.get("locations_input", []): tasklocation_timing_rules.append(location_input["timing_rule"]) # get start and end from timing rules timing_rule = attrs.get("timing_rule") if timing_rule is not None: timing_rule_start = get_rrule_start(rrulestr(timing_rule)) timing_rule_end = get_rrule_end(rrulestr(timing_rule)) else: timing_rule_start = None timing_rule_end = None if not start_from_input: # start was not input by user, we try and generate it from # timing rules if not timing_rule_start: # we cannot determine a start time raise serializers.ValidationError( {"timing_rule": MISSING_START_DATE, "start": MISSING_START_DATE} ) attrs["start"] = timing_rule_start # get end attrs["end"] = attrs.get("end", timing_rule_end) # If end date is present we validate that it is greater than start_date if attrs.get("end") is not None: # If end date is lesser than the start date raise an error the_start = attrs.get("start") if the_start is None and self.instance is not None: the_start = self.instance.start if not the_start: raise serializers.ValidationError({"start": INVALID_START_DATE}) if attrs["end"] < the_start: raise serializers.ValidationError({"end": INVALID_END_DATE}) # If start date is present and this is an existing object, we validate # that the start date is not greater than the existing end date if ( # pylint: disable=bad-continuation attrs.get("start") is not None and self.instance is not None and self.instance.end is not None and attrs.get("start") > self.instance.end ): raise serializers.ValidationError({"start": INVALID_START_DATE}) return super(TaskSerializer, self).validate(attrs)
def test_create_task(self): """ Test that the serializer can create Task objects """ mocked_target_object = mommy.make('tasking.Task') rule1 = mommy.make('tasking.SegmentRule') rule2 = mommy.make('tasking.SegmentRule') rrule = 'DTSTART:20180521T210000Z RRULE:FREQ=DAILY;INTERVAL=10;COUNT=5' data = { 'name': 'Cow price', 'description': 'Some description', 'total_submission_target': 10, 'timing_rule': rrule, 'target_content_type': self.task_type.id, 'target_id': mocked_target_object.id, 'estimated_time': 'P4DT1H15M20S', } data_with_segment_rules = data.copy() data_with_segment_rules['segment_rules'] = [rule1.id, rule2.id] serializer_instance = TaskSerializer(data=data_with_segment_rules) self.assertTrue(serializer_instance.is_valid()) task = serializer_instance.save() # the start and end fields are going to be from the timing rule start = get_rrule_start(rrulestr(rrule)) end = get_rrule_end(rrulestr(rrule)) # Change estimated time to DD HH:MM:SS format since Serializer # Changes it to such data['estimated_time'] = '4 01:15:20' # the order of segment_rules may have changed so a dict comparison # may fail, we use `data` that does not include segment rules self.assertDictContainsSubset(data, serializer_instance.data) # we test that we do have our segment rules self.assertEqual(set([rule1.id, rule2.id]), set(serializer_instance.data['segment_rules'])) # we test that submissions are equal to 0 self.assertEqual(serializer_instance.data['submission_count'], 0) self.assertEqual(task.submissions, 0) # Add a submission to task and assert it changes. mocked_submission = mommy.make('tasking.Submission', task=task) self.assertTrue(mocked_submission.task, task) self.assertEqual(task.submissions, 1) self.assertEqual('Cow price', task.name) self.assertEqual('Some description', task.description) self.assertEqual(start, task.start) self.assertEqual(end, task.end) self.assertEqual(10, task.total_submission_target) # assert that the ISO 8601 String was converted to accurately self.assertEqual(task.estimated_time, timedelta(4, 4520)) # test that the segment rules for the task are as we expect self.assertEqual(rule1, task.segment_rules.get(id=rule1.id)) self.assertEqual(rule2, task.segment_rules.get(id=rule2.id)) expected_fields = [ 'id', 'created', 'modified', 'name', 'parent', 'description', 'start', 'end', 'timing_rule', 'estimated_time', 'total_submission_target', 'user_submission_target', 'status', 'submission_count', 'target_content_type', 'target_id', 'segment_rules', 'locations', 'task_locations' ] self.assertEqual(set(expected_fields), set(list(serializer_instance.data.keys())))
def test_create_task(self): """ Test that the serializer can create Task objects """ mocked_target_object = mommy.make("tasking.Task") rule1 = mommy.make("tasking.SegmentRule") rule2 = mommy.make("tasking.SegmentRule") rrule = "DTSTART:20180521T210000Z RRULE:FREQ=DAILY;INTERVAL=10;COUNT=5" data = { "name": "Cow price", "description": "Some description", "total_submission_target": 10, "timing_rule": rrule, "target_content_type": self.task_type.id, "target_id": mocked_target_object.id, "estimated_time": "P4DT1H15M20S", } data_with_segment_rules = data.copy() data_with_segment_rules["segment_rules"] = [rule1.id, rule2.id] serializer_instance = TaskSerializer(data=data_with_segment_rules) self.assertTrue(serializer_instance.is_valid()) task = serializer_instance.save() # the start and end fields are going to be from the timing rule start = get_rrule_start(rrulestr(rrule)) end = get_rrule_end(rrulestr(rrule)) # Change estimated time to DD HH:MM:SS format since Serializer # Changes it to such data["estimated_time"] = "4 01:15:20" # the order of segment_rules may have changed so a dict comparison # may fail, we use `data` that does not include segment rules self.assertDictContainsSubset(data, serializer_instance.data) # we test that we do have our segment rules self.assertEqual(set([rule1.id, rule2.id]), set(serializer_instance.data["segment_rules"])) # we test that submissions are equal to 0 self.assertEqual(serializer_instance.data["submission_count"], 0) self.assertEqual(task.submissions, 0) # Add a submission to task and assert it changes. mocked_submission = mommy.make("tasking.Submission", task=task) self.assertTrue(mocked_submission.task, task) self.assertEqual(task.submissions, 1) self.assertEqual("Cow price", task.name) self.assertEqual("Some description", task.description) self.assertEqual(start, task.start) self.assertEqual(end, task.end) self.assertEqual(10, task.total_submission_target) # assert that the ISO 8601 String was converted to accurately self.assertEqual(task.estimated_time, timedelta(4, 4520)) # test that the segment rules for the task are as we expect self.assertEqual(rule1, task.segment_rules.get(id=rule1.id)) self.assertEqual(rule2, task.segment_rules.get(id=rule2.id)) expected_fields = [ "id", "created", "modified", "name", "parent", "description", "start", "end", "timing_rule", "estimated_time", "total_submission_target", "user_submission_target", "status", "submission_count", "target_content_type", "target_id", "segment_rules", "locations", "task_locations", ] self.assertEqual(set(expected_fields), set(list(serializer_instance.data.keys())))