def test_charge_admin_queryset_is_annotated_with_time_charged(self): Charge(project=self.project, start_time=timezone.now()).validate_and_save() Charge(project=self.project, start_time=timezone.now()).validate_and_save() queryset = self.model_admin.get_queryset(HttpRequest()) for charge in queryset: self.assertTrue(hasattr(charge, 'db_time_charged'))
def test_charge_time_charged_property_returns_correct_time_charged(self): start_datetime = timezone.make_aware( datetime(2019, 1, 1, hour=8, minute=0, second=0)) timedelta_zero = timedelta() added_time = timedelta(minutes=30) charge = Charge(project=self.project, start_time=start_datetime) self.assertEqual(charge.time_charged, timedelta_zero) charge.end_time = start_datetime + added_time self.assertEqual(charge.time_charged, added_time)
def test_charge_close_view(self): now = timezone.now() end = now + timedelta(minutes=1) self.performLogin() project = Project(name='Test').validate_and_save() charge = Charge(project=project, start_time=now, end_time=end).validate_and_save() response = self.client.post( reverse('project:close-charge', args=(charge.pk, ))) charge.refresh_from_db() self.assertTrue(charge.closed) self.assertEqual(response.status_code, 302)
def test_project_queryset_can_annotate_latest_charge(self): today = timezone.now().replace(hour=0, minute=0, second=0) yesterday = today - timedelta(days=1) tomorrow = today + timedelta(days=1) Charge(project=self.project, start_time=yesterday, end_time=today).validate_and_save() Charge(project=self.project, start_time=today, end_time=tomorrow).validate_and_save() annotated_project = (Project.objects.annotate_latest_charge().get( pk=self.project.pk)) self.assertEqual(annotated_project.db_latest_charge, tomorrow)
def test_charge_closed_field_is_set_to_given_value(self): today = timezone.now().replace(hour=0, minute=0, second=0) charge = Charge(project=self.project, start_time=today, end_time=today + timedelta(minutes=1), closed=False).validate_and_save() self.assertFalse(charge.closed) charge = Charge(project=self.project, start_time=today, end_time=today + timedelta(minutes=1), closed=True).validate_and_save() self.assertTrue(charge.closed)
def test_charge_closed_field_defaults_to_false(self): today = timezone.now().replace(hour=0, minute=0, second=0) charge = Charge(project=self.project, start_time=today).validate_and_save() self.assertFalse(charge.closed)
def test_charge_end_time_field_defaults_to_null(self): today = timezone.now().replace(hour=0, minute=0, second=0) charge = Charge(project=self.project, start_time=today).validate_and_save() self.assertIsNone(charge.end_time)
def test_charge_update_view_redirects_when_not_logged_in(self): now = timezone.now() project = Project(name='Test').validate_and_save() charge = Charge(project=project, start_time=now).validate_and_save() response = self.client.get( reverse('project:charge-update', args=(charge.pk, ))) self.assertEqual(response.status_code, 302)
def test_charge_queryset_can_aggregate_time_charged(self): start_of_today = timezone.now().replace(hour=0, minute=0, second=0, microsecond=0) Charge(project=self.project, start_time=start_of_today.replace(hour=8), end_time=start_of_today.replace(hour=9)).validate_and_save() Charge(project=self.project, start_time=start_of_today.replace(hour=9), end_time=start_of_today.replace(hour=17)).validate_and_save() total_time_charged = Charge.objects.aggregate_time_charged() self.assertEqual(total_time_charged, timedelta(hours=9))
def test_charges_are_sorted_by_start_time(self): start_datetime = timezone.make_aware( datetime(2019, 1, 1, hour=8, minute=0, second=0)) Charge(project=self.project, start_time=start_datetime).validate_and_save() earlier_charge = Charge(project=self.project, start_time=start_datetime - timedelta(hours=1)).validate_and_save() later_charge = Charge(project=self.project, start_time=start_datetime + timedelta(hours=1)).validate_and_save() self.assertEqual(Charge.objects.earliest(), earlier_charge) self.assertEqual(Charge.objects.latest(), later_charge)
def test_charge_end_time_field_is_set_to_given_value(self): today = timezone.now().replace(hour=0, minute=0, second=0) end_time = today + timedelta(minutes=1) charge = Charge(project=self.project, start_time=today, end_time=end_time).validate_and_save() self.assertEqual(charge.end_time, end_time)
def test_charge_update_view_is_ok(self): now = timezone.now() self.performLogin() project = Project(name='Test').validate_and_save() charge = Charge(project=project, start_time=now).validate_and_save() response = self.client.get( reverse('project:charge-update', args=(charge.pk, ))) self.assertEqual(response.status_code, 200)
def test_charge_project_must_be_active_project(self): start_datetime = timezone.make_aware( datetime(2019, 1, 1, hour=8, minute=0, second=0)) inactive_project = Project(name='Test 2', active=False).validate_and_save() charge = Charge(project=inactive_project, start_time=start_datetime, end_time=start_datetime + timedelta(hours=1)) with self.assertRaises(ValidationError) as context_manager: charge.full_clean() self.assertValidationMessagePresent( context_manager.exception.error_dict, field='project', error_code='project_must_be_active')
def test_charge_close_view_redirects_when_not_logged_in(self): now = timezone.now() end = now + timedelta(minutes=1) project = Project(name='Test').validate_and_save() charge = Charge(project=project, start_time=now, end_time=end).validate_and_save() response = self.client.get( reverse('project:close-charge', args=(charge.pk, ))) self.assertEqual(response.status_code, 302)
def test_cannot_delete_project_with_associated_charge(self): start_datetime = timezone.make_aware( datetime(2019, 1, 1, hour=8, minute=0, second=0)) Charge(project=self.project, start_time=start_datetime).validate_and_save() with self.assertRaises(ProtectedError): self.project.delete()
def test_charge_can_be_created_today(self): today = timezone.now().replace(hour=0, minute=0, second=0) timedelta_zero = timedelta() charge = Charge(project=self.project, start_time=today).validate_and_save() self.assertIsNotNone(charge.pk) self.assertEqual(charge.project, self.project) self.assertEqual(charge.start_time, today)
def test_charge_cannot_be_closed_without_end_time(self): start_datetime = timezone.make_aware( datetime(2019, 1, 1, hour=8, minute=0, second=0)) charge = Charge(project=self.project, start_time=start_datetime, closed=True) # Should raise a keyed validation error with self.assertRaises(ValidationError) as context_manager: charge.full_clean() self.assertValidationMessagePresent( context_manager.exception.error_dict, field='closed', error_code='cannot_close_without_end_time') # Should raise a generic validation error if the end time field # is excluded with self.assertRaises(ValidationError) as context_manager: charge.full_clean(exclude=('closed', )) self.assertValidationMessagePresent( context_manager.exception.error_dict, field='__all__', error_code='cannot_close_without_end_time')
def test_charge_has_descriptive_string_representation(self): start_datetime = timezone.make_aware( datetime(2019, 1, 1, hour=8, minute=0, second=0)) charge = Charge(project=self.project, start_time=start_datetime) self.assertEqual( str(charge), 'Test, Jan. 1, 2019, 8 a.m. - __:__:__ (0:00:00 minutes) [Open]') charge.end_time = start_datetime + timedelta(minutes=30) self.assertEqual( str(charge), 'Test, Jan. 1, 2019, 8 a.m. - Jan. 1, 2019, 8:30 a.m. (0:30:00 minutes) [Open]' ) charge.end_time = start_datetime + timedelta(hours=1) self.assertEqual( str(charge), 'Test, Jan. 1, 2019, 8 a.m. - Jan. 1, 2019, 9 a.m. (1:00:00 hours) [Open]' ) charge.end_time = start_datetime + timedelta(hours=1, minutes=15) self.assertEqual( str(charge), 'Test, Jan. 1, 2019, 8 a.m. - Jan. 1, 2019, 9:15 a.m. (1:15:00 hours) [Open]' ) charge.closed = True self.assertEqual( str(charge), 'Test, Jan. 1, 2019, 8 a.m. - Jan. 1, 2019, 9:15 a.m. (1:15:00 hours) [Closed]' )
def test_charge_admin_has_all_fields_editable_when_project_is_active_and_charge_is_not_closed( self): self.project.active = True self.project.validate_and_save() charge = Charge(project=self.project, start_time=timezone.now(), closed=False).validate_and_save() readonly_fields = self.model_admin.get_readonly_fields( HttpRequest(), charge) self.assertEqual(len(readonly_fields), 0)
def test_charge_queryset_can_annotate_time_charged(self): start_of_today = timezone.now().replace(hour=0, minute=0, second=0, microsecond=0) charge = Charge( project=self.project, start_time=start_of_today.replace(hour=9), end_time=start_of_today.replace(hour=17)).validate_and_save() annotated_charge = (Charge.objects.annotate_time_charged().get( pk=charge.pk)) self.assertEqual(annotated_charge.db_time_charged, timedelta(hours=8))
def test_charge_admin_has_read_only_start_time_end_time_and_closed_status_when_project_not_active( self): charge = Charge(project=self.project, start_time=timezone.now(), closed=False).validate_and_save() self.project.active = False self.project.validate_and_save() readonly_fields = self.model_admin.get_readonly_fields( HttpRequest(), charge) self.assertSequenceEqual(readonly_fields, ( 'start_time', 'end_time', 'closed', ))
def test_charge_admin_has_read_only_start_time_end_time_and_project_when_charge_is_closed( self): self.project.active = True self.project.validate_and_save() charge = Charge(project=self.project, start_time=timezone.now(), end_time=timezone.now() + timedelta(hours=1), closed=True).validate_and_save() readonly_fields = self.model_admin.get_readonly_fields( HttpRequest(), charge) self.assertSequenceEqual(readonly_fields, ( 'start_time', 'end_time', 'project', ))
def test_charge_cannot_be_modified_when_closed(self): start_datetime = timezone.make_aware( datetime(2019, 1, 1, hour=8, minute=0, second=0)) charge = Charge(project=self.project, start_time=start_datetime, end_time=start_datetime + timedelta(hours=1), closed=True).validate_and_save() # Should raise a generic validation error if attempting to # save on a closed charge with self.assertRaises(ValidationError) as context_manager: charge.validate_and_save() self.assertValidationMessagePresent( context_manager.exception.error_dict, field='__all__', error_code='cannot_modify_when_closed') # But should be able to save the change if it is # re-opening the charge charge.closed = False charge.validate_and_save() self.assertEqual(charge.closed, False)
def test_visualization_creation_whitebox(self): project_a = Project(name='Project A').validate_and_save() project_b = Project(name='Project B').validate_and_save() # Create some test charges in the current month project_a_current_charges = [ timedelta(hours=4), timedelta(hours=4), ] project_b_current_charges = [ timedelta(hours=4), timedelta(hours=4), ] for charge in project_a_current_charges: ChargeFactory.today(project=project_a, charge_time=charge).validate_and_save() for charge in project_b_current_charges: ChargeFactory.today(project=project_b, charge_time=charge).validate_and_save() # Create some unclosed charges in the current month Charge(project=project_a, start_time=get_start_of_today()).validate_and_save() Charge(project=project_b, start_time=get_start_of_today()).validate_and_save() # Create charges in a past month ChargeFactory.past_month( project=project_a, charge_time=timedelta(hours=1)).validate_and_save() ChargeFactory.past_month( project=project_b, charge_time=timedelta(hours=1)).validate_and_save() # Create charges in a future month ChargeFactory.future_month( project=project_a, charge_time=timedelta(hours=1)).validate_and_save() ChargeFactory.future_month( project=project_b, charge_time=timedelta(hours=1)).validate_and_save() # Should return data on all projects dataframe = report_helpers.get_monthly_summary_series( timezone.localtime()) self.assertIsInstance(dataframe, pd.DataFrame) self.assertEqual(len(dataframe), 2) self.assertIsInstance(dataframe['charge'], pd.Series) self.assertIsInstance(dataframe['value'], pd.Series) self.assertIsInstance(dataframe['angle'], pd.Series) self.assertIsInstance(dataframe['color'], pd.Series) project_a_dataframe = dataframe[dataframe.charge == project_a.name] project_b_dataframe = dataframe[dataframe.charge == project_b.name] self.assertFalse(project_a_dataframe.empty) self.assertFalse(project_b_dataframe.empty) self.assertEqual(project_a_dataframe.iloc[0].value, 8.0) self.assertEqual(project_b_dataframe.iloc[0].value, 8.0) self.assertIsInstance(project_a_dataframe.iloc[0].angle, float) self.assertIsInstance(project_b_dataframe.iloc[0].angle, float) self.assertIsInstance(project_a_dataframe.iloc[0].color, str) self.assertIsInstance(project_b_dataframe.iloc[0].color, str) div, chart = report_helpers.get_monthly_summary_chart_components( dataframe) self.assertGreater(len(div), 0) self.assertGreater(len(chart), 0) # Should return data on only the requested projects dataframe = report_helpers.get_monthly_summary_series( timezone.localtime(), project_ids=[project_a.pk]) self.assertIsInstance(dataframe, pd.DataFrame) self.assertEqual(len(dataframe), 1) self.assertIsInstance(dataframe['charge'], pd.Series) self.assertIsInstance(dataframe['value'], pd.Series) self.assertIsInstance(dataframe['angle'], pd.Series) self.assertIsInstance(dataframe['color'], pd.Series) self.assertEqual(dataframe.iloc[0].charge, project_a.name) self.assertEqual(dataframe.iloc[0].value, 8.0) self.assertIsInstance(dataframe.iloc[0].angle, float) self.assertIsInstance(dataframe.iloc[0].color, str) div, chart = report_helpers.get_monthly_summary_chart_components( dataframe) self.assertGreater(len(div), 0) self.assertGreater(len(chart), 0)
def test_charge_closed_field_is_not_required(self): today = timezone.now().replace(hour=0, minute=0, second=0) Charge(project=self.project, start_time=today).validate_and_save()
def today(cls, project=None, charge_time=None): start_of_today = get_start_of_today() return Charge(project=project, start_time=start_of_today, end_time=start_of_today + charge_time)
def future_month(cls, project=None, charge_time=None): future_month = get_start_of_today() + timedelta(days=31) return Charge(project=project, start_time=future_month, end_time=future_month + charge_time)
def past_month(cls, project=None, charge_time=None): past_month = get_start_of_today() - timedelta(days=31) return Charge(project=project, start_time=past_month, end_time=past_month + charge_time)