Example #1
0
    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'))
Example #2
0
    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)
Example #3
0
 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)
Example #4
0
    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)
Example #5
0
    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)
Example #6
0
    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)
Example #7
0
    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)
Example #8
0
 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)
Example #9
0
    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))
Example #10
0
    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)
Example #11
0
    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)
Example #12
0
 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)
Example #13
0
    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')
Example #14
0
 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)
Example #15
0
    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()
Example #16
0
    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)
Example #17
0
    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')
Example #18
0
    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]'
        )
Example #19
0
    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)
Example #20
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))
Example #21
0
    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',
        ))
Example #22
0
    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',
        ))
Example #23
0
    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)
Example #24
0
    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)
Example #25
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()
Example #26
0
 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)
Example #27
0
 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)
Example #28
0
 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)