diff --git a/fahari/common/migrations/0021_auto_20210911_1724.py b/fahari/common/migrations/0021_auto_20210911_1724.py index 5b78327b..fb7e446f 100644 --- a/fahari/common/migrations/0021_auto_20210911_1724.py +++ b/fahari/common/migrations/0021_auto_20210911_1724.py @@ -19,7 +19,7 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='system', name='pattern', - field=models.CharField(choices=[('poc', 'Point of Care'), ('rde', 'Retrospective Data Entry'), ('hybrid', 'Hybrid'), ('none', 'None')], default='none', max_length=10), + field=models.CharField(choices=[('poc', 'Point of Care'), ('rde', 'Retrospective Data Entry'), ('hybrid', 'Hybrid'), ('none', 'None')], default='none', max_length=100), ), migrations.CreateModel( name='UserFacilityAllotment', @@ -30,7 +30,7 @@ class Migration(migrations.Migration): ('created_by', models.UUIDField(blank=True, null=True)), ('updated', models.DateTimeField(default=django.utils.timezone.now)), ('updated_by', models.UUIDField(blank=True, null=True)), - ('allotment_type', models.CharField(choices=[('facility', 'By Facility'), ('region', 'By Region'), ('both', 'By Both Facility and Region')], max_length=10)), + ('allotment_type', models.CharField(choices=[('facility', 'By Facility'), ('region', 'By Region'), ('both', 'By Both Facility and Region')], max_length=100)), ('region_type', models.CharField(blank=True, choices=[('county', 'County'), ('constituency', 'Constituency'), ('sub_county', 'Sub County'), ('ward', 'Ward')], max_length=20, null=True)), ('counties', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(blank=True, choices=[('Nairobi', 'Nairobi'), ('Kajiado', 'Kajiado')], max_length=150, null=True), blank=True, help_text='All the facilities in the selected counties will be allocated to the selected user.', null=True, size=None)), ('constituencies', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(blank=True, choices=[('Dagoretti North', 'Dagoretti North'), ('Dagoretti South', 'Dagoretti South'), ('Embakasi Central', 'Embakasi Central'), ('Embakasi East', 'Embakasi East'), ('Embakasi North', 'Embakasi North'), ('Embakasi South', 'Embakasi South'), ('Embakasi West', 'Embakasi West'), ('Kajiado Central', 'Kajiado Central'), ('Kajiado East', 'Kajiado East'), ('Kajiado North', 'Kajiado North'), ('Kajiado West', 'Kajiado West'), ('Kamukunji', 'Kamukunji'), ('Kasarani', 'Kasarani'), ('Kibra', 'Kibra'), ('Langata', 'Langata'), ('Magadi', 'Magadi'), ('Makadara', 'Makadara'), ('Mathare', 'Mathare'), ('Roysambu', 'Roysambu'), ('Ruaraka', 'Ruaraka'), ('Starehe', 'Starehe'), ('Westlands', 'Westlands')], max_length=150, null=True), blank=True, help_text='All the facilities in the selected constituencies will be allocated to the selected user.', null=True, size=None)), diff --git a/fahari/common/migrations/0023_auto_20210914_1639.py b/fahari/common/migrations/0023_auto_20210914_1639.py new file mode 100644 index 00000000..dee5f263 --- /dev/null +++ b/fahari/common/migrations/0023_auto_20210914_1639.py @@ -0,0 +1,23 @@ +# Generated by Django 3.2.7 on 2021-09-14 13:39 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('common', '0022_auto_20210914_1403'), + ] + + operations = [ + migrations.AlterField( + model_name='system', + name='pattern', + field=models.CharField(choices=[('poc', 'Point of Care'), ('rde', 'Retrospective Data Entry'), ('hybrid', 'Hybrid'), ('none', 'None')], default='none', max_length=100), + ), + migrations.AlterField( + model_name='userfacilityallotment', + name='allotment_type', + field=models.CharField(choices=[('facility', 'By Facility'), ('region', 'By Region'), ('both', 'By Both Facility and Region')], max_length=100), + ), + ] diff --git a/fahari/common/models/common_models.py b/fahari/common/models/common_models.py index 41c47e12..30487b22 100644 --- a/fahari/common/models/common_models.py +++ b/fahari/common/models/common_models.py @@ -280,7 +280,7 @@ class SystemPatters(models.TextChoices): name = models.CharField(max_length=128, null=False, blank=False, unique=True) pattern = models.CharField( - max_length=10, choices=SystemPatters.choices, default=SystemPatters.NONE.value + max_length=100, choices=SystemPatters.choices, default=SystemPatters.NONE.value ) description = models.TextField() @@ -317,7 +317,7 @@ class RegionType(models.TextChoices): WARD = "ward" user = models.OneToOneField(User, on_delete=models.PROTECT) - allotment_type = models.CharField(max_length=10, choices=AllotmentType.choices) + allotment_type = models.CharField(max_length=100, choices=AllotmentType.choices) region_type = models.CharField( max_length=20, choices=RegionType.choices, null=True, blank=True ) diff --git a/fahari/common/tests/test_dashboard.py b/fahari/common/tests/test_dashboard.py index 0cc55c94..a6c0528e 100644 --- a/fahari/common/tests/test_dashboard.py +++ b/fahari/common/tests/test_dashboard.py @@ -1,8 +1,10 @@ +import json import random import pytest from django.contrib.auth import get_user_model from django.utils import timezone +from faker.proxy import Faker from model_bakery import baker from fahari.common.constants import WHITELIST_COUNTIES @@ -12,13 +14,16 @@ get_appointments_mtd, get_open_ticket_count, ) -from fahari.common.models import Facility -from fahari.ops.models import DailyUpdate, FacilitySystemTicket +from fahari.common.models import Facility, Organisation +from fahari.common.models.common_models import System +from fahari.ops.models import DailyUpdate, FacilitySystem, FacilitySystemTicket User = get_user_model() pytestmark = pytest.mark.django_db +fake = Faker() + def test_get_active_facility_count(user): baker.make( @@ -33,8 +38,21 @@ def test_get_active_facility_count(user): def test_get_open_ticket_count(user): + org = baker.make(Organisation) + facility = baker.make(Facility, organisation=org, name="Test") + system = baker.make(System, organisation=org, name="System") + vrs = "0.0.1" + facility_system = baker.make( + FacilitySystem, + facility=facility, + system=system, + version=vrs, + organisation=org, + trainees=json.dumps([fake.name(), fake.name()]), + ) baker.make( FacilitySystemTicket, + facility_system=facility_system, resolved=None, active=True, organisation=user.organisation, diff --git a/fahari/ops/filters.py b/fahari/ops/filters.py index ecb105fe..f712c98e 100644 --- a/fahari/ops/filters.py +++ b/fahari/ops/filters.py @@ -28,7 +28,10 @@ class FacilitySystemFilter(CommonFieldsFilterset): class Meta: model = FacilitySystem - fields = "__all__" + exclude = ( + "attachment", + "trainees", + ) class FacilitySystemTicketFilter(CommonFieldsFilterset): diff --git a/fahari/ops/migrations/0026_auto_20210914_1059.py b/fahari/ops/migrations/0026_auto_20210914_1059.py new file mode 100644 index 00000000..ad7bce27 --- /dev/null +++ b/fahari/ops/migrations/0026_auto_20210914_1059.py @@ -0,0 +1,29 @@ +# Generated by Django 3.2.7 on 2021-09-14 07:59 + +from django.db import migrations, models +import fahari.common.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ops', '0025_securityincidence'), + ] + + operations = [ + migrations.AddField( + model_name='facilitysystem', + name='attachment', + field=models.FileField(blank=True, null=True, upload_to=fahari.common.models.get_directory, verbose_name='Attach File or Photo'), + ), + migrations.AddField( + model_name='facilitysystem', + name='release_notes', + field=models.TextField(default='-'), + ), + migrations.AddField( + model_name='facilitysystem', + name='trainees', + field=models.TextField(default='-'), + ), + ] diff --git a/fahari/ops/migrations/0027_merge_0026_auto_20210914_1059_0026_auto_20210914_1403.py b/fahari/ops/migrations/0027_merge_0026_auto_20210914_1059_0026_auto_20210914_1403.py new file mode 100644 index 00000000..7bac499c --- /dev/null +++ b/fahari/ops/migrations/0027_merge_0026_auto_20210914_1059_0026_auto_20210914_1403.py @@ -0,0 +1,14 @@ +# Generated by Django 3.2.7 on 2021-09-14 13:03 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('ops', '0026_auto_20210914_1059'), + ('ops', '0026_auto_20210914_1403'), + ] + + operations = [ + ] diff --git a/fahari/ops/migrations/0028_alter_facilitysystem_trainees.py b/fahari/ops/migrations/0028_alter_facilitysystem_trainees.py new file mode 100644 index 00000000..8d2fc909 --- /dev/null +++ b/fahari/ops/migrations/0028_alter_facilitysystem_trainees.py @@ -0,0 +1,19 @@ +# Generated by Django 3.2.7 on 2021-09-14 13:04 + +import django.contrib.postgres.fields +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ops', '0027_merge_0026_auto_20210914_1059_0026_auto_20210914_1403'), + ] + + operations = [ + migrations.AlterField( + model_name='facilitysystem', + name='trainees', + field=django.contrib.postgres.fields.ArrayField(base_field=models.TextField(), help_text='Use commas to separate trainees names', size=None), + ), + ] diff --git a/fahari/ops/models.py b/fahari/ops/models.py index 241b7643..11d58a4d 100644 --- a/fahari/ops/models.py +++ b/fahari/ops/models.py @@ -110,6 +110,14 @@ class FacilitySystem(AbstractBase): facility = models.ForeignKey(Facility, on_delete=models.PROTECT) system = models.ForeignKey(System, on_delete=models.PROTECT) version = models.CharField(max_length=64) + release_notes = models.TextField(default="-") + trainees = ArrayField( + models.TextField(), + help_text="Use commas to separate trainees names", + ) + attachment = models.FileField( + upload_to=get_directory, verbose_name="Attach File or Photo", null=True, blank=True + ) def get_absolute_url(self): update_url = reverse_lazy("ops:version_update", kwargs={"pk": self.pk}) diff --git a/fahari/ops/serializers.py b/fahari/ops/serializers.py index 39de6e09..8938ec65 100644 --- a/fahari/ops/serializers.py +++ b/fahari/ops/serializers.py @@ -25,6 +25,7 @@ class FacilitySystemSerializer(BaseSerializer): facility_name = serializers.ReadOnlyField() system_name = serializers.ReadOnlyField() + updated = serializers.DateTimeField(format="%d/%m/%Y", required=False) class Meta(BaseSerializer.Meta): model = FacilitySystem diff --git a/fahari/ops/tests/test_api.py b/fahari/ops/tests/test_api.py index 1bb68352..bb774659 100644 --- a/fahari/ops/tests/test_api.py +++ b/fahari/ops/tests/test_api.py @@ -50,6 +50,7 @@ def test_create(self): "system": self.system.pk, "version": fake.name()[:63], "organisation": self.global_organisation.pk, + "trainees": json.dumps([fake.name(), fake.name()]), } response = self.client.post(self.url_list, data) assert response.status_code == 201, response.json() @@ -59,6 +60,7 @@ def test_retrieve(self): instance = baker.make( FacilitySystem, organisation=self.global_organisation, + trainees=json.dumps([fake.name(), fake.name()]), ) response = self.client.get(self.url_list) assert response.status_code == 200, response.json() @@ -71,6 +73,7 @@ def test_patch_system(self): instance = baker.make( FacilitySystem, organisation=self.global_organisation, + trainees=json.dumps([fake.name(), fake.name()]), ) edit = {"version": fake.name()[:63]} url = reverse("api:facilitysystem-detail", kwargs={"pk": instance.pk}) @@ -83,12 +86,14 @@ def test_put_system(self): instance = baker.make( FacilitySystem, organisation=self.global_organisation, + trainees=json.dumps([fake.name(), fake.name()]), ) data = { "facility": self.facility.pk, "system": self.system.pk, "version": fake.name()[:63], "organisation": self.global_organisation.pk, + "trainees": f"{fake.name()},{fake.name()}", } url = reverse("api:facilitysystem-detail", kwargs={"pk": instance.pk}) response = self.client.put(url, data) @@ -111,7 +116,11 @@ def setUp(self): super().setUp() def test_facilitysystem_form_init(self): - baker.make(FacilitySystem, organisation=self.global_organisation) + baker.make( + FacilitySystem, + organisation=self.global_organisation, + trainees=json.dumps([fake.name(), fake.name()]), + ) form = FacilitySystemTicketForm() queryset = form.fields["facility_system"].queryset assert FacilitySystem.objects.count() > 0 @@ -123,6 +132,9 @@ def test_create(self): "system": self.system.pk, "version": fake.name()[:63], "organisation": self.global_organisation.pk, + "release_notes": fake.text(), + "trainees": json.dumps([fake.name(), fake.name()]), + "attachment": fake.file_name(), } response = self.client.post(reverse("ops:version_create"), data=data) self.assertEqual( @@ -134,13 +146,17 @@ def test_update(self): instance = baker.make( FacilitySystem, organisation=self.global_organisation, + trainees=json.dumps([fake.name(), fake.name()]), ) data = { "pk": instance.pk, "facility": self.facility.pk, + "organisation": self.global_organisation.pk, "system": self.system.pk, "version": fake.name()[:63], - "organisation": self.global_organisation.pk, + "release_notes": fake.text(), + "trainees": f"{fake.name()},{fake.name()}", + "attachment": fake.file_name(), } response = self.client.post( reverse("ops:version_update", kwargs={"pk": instance.pk}), data=data @@ -154,6 +170,7 @@ def test_delete(self): instance = baker.make( FacilitySystem, organisation=self.global_organisation, + trainees=json.dumps([fake.name(), fake.name()]), ) response = self.client.post( reverse("ops:version_delete", kwargs={"pk": instance.pk}), @@ -174,6 +191,7 @@ def setUp(self): facility=self.facility, system=self.system, organisation=self.global_organisation, + trainees=json.dumps([fake.name(), fake.name()]), ) super().setUp() @@ -254,16 +272,20 @@ def setUp(self): facility=self.facility, system=self.system, organisation=self.global_organisation, + trainees=json.dumps([fake.name(), fake.name()]), ) super().setUp() def test_create(self): data = { "facility_system": self.facility_system.pk, + "organisation": self.global_organisation.pk, "details": fake.text(), "raised": timezone.now().isoformat(), "raised_by": fake.name(), - "organisation": self.global_organisation.pk, + "resolved": timezone.now().isoformat(), + "resolved_by": fake.name(), + "resolved_note": fake.text(), } response = self.client.post(reverse("ops:ticket_create"), data=data) self.assertEqual( diff --git a/fahari/ops/tests/test_models.py b/fahari/ops/tests/test_models.py index 4dcf3450..f3376a35 100644 --- a/fahari/ops/tests/test_models.py +++ b/fahari/ops/tests/test_models.py @@ -1,3 +1,4 @@ +import json import random from datetime import date @@ -6,9 +7,10 @@ from django.utils import timezone from faker import Faker from model_bakery import baker +from rest_framework.test import APITestCase from fahari.common.models import Facility, Organisation, System -from fahari.common.tests.test_api import global_organisation +from fahari.common.tests.test_api import LoggedInMixin, global_organisation from fahari.ops.models import ( DEFAULT_COMMODITY_PK, Activity, @@ -38,70 +40,63 @@ fake = Faker() -def test_facility_system_str(): - org = baker.make(Organisation) - facility = baker.make(Facility, organisation=org, name="Test") - system = baker.make(System, organisation=org, name="System") - vrs = "0.0.1" - facility_system = baker.make( - FacilitySystem, - facility=facility, - system=system, - version=vrs, - ) - assert str(facility_system) == "Test - System, version 0.0.1" - +class InitializeTestData(LoggedInMixin, APITestCase): + def setUp(self): + org = baker.make(Organisation) + facility = baker.make(Facility, organisation=org, name="Test") + system = baker.make(System, organisation=org, name="System") + vrs = "0.0.1" + self.facility_system = baker.make( + FacilitySystem, + facility=facility, + system=system, + version=vrs, + organisation=org, + trainees=json.dumps([fake.name(), fake.name()]), + ) + super().setUp() -def test_facility_system_ticket_str(): - org = baker.make(Organisation) - facility = baker.make(Facility, organisation=org, name="Test") - system = baker.make(System, organisation=org, name="System") - vrs = "0.0.1" - facility_system = baker.make( - FacilitySystem, - facility=facility, - system=system, - version=vrs, - ) - facility_system_details = baker.make( - FacilitySystemTicket, - facility_system=facility_system, - details="Details", - ) - assert ( - str(facility_system_details) == "Facility: Test; System: System; Version: 0.0.1 (Details)" - ) + def test_facility_system_str(self): + assert str(self.facility_system) == "Test - System, version 0.0.1" + def test_facility_system_ticket_str(self): + facility_system_details = baker.make( + FacilitySystemTicket, + facility_system=self.facility_system, + details="Details", + ) + assert ( + str(facility_system_details) + == "Facility: Test; System: System; Version: 0.0.1 (Details)" + ) -def test_facility_system_ticket_is_resolved(): - org = baker.make(Organisation) - facility = baker.make(Facility, organisation=org, name="Test") - system = baker.make(System, organisation=org, name="System") - vrs = "0.0.1" - facility_system = baker.make( - FacilitySystem, - facility=facility, - system=system, - version=vrs, - ) - facility_system_details = baker.make( - FacilitySystemTicket, - facility_system=facility_system, - details="Details", - resolved=timezone.now(), - resolved_by="User", - ) - assert facility_system_details.is_resolved is True + def test_facility_system_ticket_is_resolved(self): + facility_system_details = baker.make( + FacilitySystemTicket, + facility_system=self.facility_system, + details="Details", + resolved=timezone.now(), + resolved_by="User", + ) + assert facility_system_details.is_resolved is True + def test_facility_ticket_status(self): -def test_facility_ticket_status(staff_user): - open_ticket = baker.make(FacilitySystemTicket, resolved=None, resolved_by=None) - assert open_ticket.is_open is True + open_ticket = baker.make( + FacilitySystemTicket, + facility_system=self.facility_system, + resolved=None, + resolved_by=None, + ) + assert open_ticket.is_open is True - closed_ticket = baker.make( - FacilitySystemTicket, resolved=timezone.now(), resolved_by=staff_user - ) - assert closed_ticket.is_open is False + closed_ticket = baker.make( + FacilitySystemTicket, + facility_system=self.facility_system, + resolved=timezone.now(), + resolved_by=self.user, + ) + assert closed_ticket.is_open is False def get_random_date(): diff --git a/fahari/ops/tests/test_views.py b/fahari/ops/tests/test_views.py index f8625ecf..160837b5 100644 --- a/fahari/ops/tests/test_views.py +++ b/fahari/ops/tests/test_views.py @@ -1,11 +1,15 @@ +import json import uuid import pytest from django.urls import reverse +from faker.proxy import Faker from model_bakery import baker from rest_framework import status -from fahari.ops.models import FacilitySystemTicket, TimeSheet +from fahari.common.models.common_models import Facility, System +from fahari.common.models.organisation_models import Organisation +from fahari.ops.models import FacilitySystem, FacilitySystemTicket, TimeSheet from fahari.ops.views import ( CommoditiesListView, FacilityDeviceRequestsListView, @@ -21,6 +25,8 @@ pytestmark = pytest.mark.django_db +fake = Faker() + def test_system_versions_view(user_with_all_permissions, client): client.force_login(user_with_all_permissions) @@ -112,16 +118,42 @@ def test_timesheet_approve_view_error_case(request_with_user): def test_ticket_resolve_view_get(user_with_all_permissions, client): + org = baker.make(Organisation) + facility = baker.make(Facility, organisation=org, name="Test") + system = baker.make(System, organisation=org, name="System") + vrs = "0.0.1" + facility_system = baker.make( + FacilitySystem, + facility=facility, + system=system, + version=vrs, + trainees=json.dumps([fake.name(), fake.name()]), + ) client.force_login(user_with_all_permissions) - open_ticket = baker.make(FacilitySystemTicket, resolved=None, resolved_by=None) + open_ticket = baker.make( + FacilitySystemTicket, facility_system=facility_system, resolved=None, resolved_by=None + ) url = reverse("ops:ticket_resolve", args=[open_ticket.pk]) response = client.get(url, format="json") assert response.status_code == 200 def test_ticket_resolve_view_post(user_with_all_permissions, client): + org = baker.make(Organisation) + facility = baker.make(Facility, organisation=org, name="Test") + system = baker.make(System, organisation=org, name="System") + vrs = "0.0.1" + facility_system = baker.make( + FacilitySystem, + facility=facility, + system=system, + version=vrs, + trainees=json.dumps([fake.name(), fake.name()]), + ) client.force_login(user_with_all_permissions) - open_ticket = baker.make(FacilitySystemTicket, resolved=None, resolved_by=None) + open_ticket = baker.make( + FacilitySystemTicket, facility_system=facility_system, resolved=None, resolved_by=None + ) url = reverse("ops:ticket_resolve", args=[open_ticket.pk]) response = client.post(url, None, format="json") assert response.status_code == 302 @@ -133,8 +165,21 @@ def test_ticket_resolve_view_post(user_with_all_permissions, client): def test_ticket_resolve_view_post_with_note(user_with_all_permissions, client): + org = baker.make(Organisation) + facility = baker.make(Facility, organisation=org, name="Test") + system = baker.make(System, organisation=org, name="System") + vrs = "0.0.1" + facility_system = baker.make( + FacilitySystem, + facility=facility, + system=system, + version=vrs, + trainees=json.dumps([fake.name(), fake.name()]), + ) client.force_login(user_with_all_permissions) - open_ticket = baker.make(FacilitySystemTicket, resolved=None, resolved_by=None) + open_ticket = baker.make( + FacilitySystemTicket, facility_system=facility_system, resolved=None, resolved_by=None + ) url = reverse("ops:ticket_resolve", args=[open_ticket.pk]) data = {"resolve_note": "All issues solved ..."} response = client.post(url, data, format="json") diff --git a/fahari/templates/pages/ops/versions.html b/fahari/templates/pages/ops/versions.html index 08c21db9..e7fa2b03 100644 --- a/fahari/templates/pages/ops/versions.html +++ b/fahari/templates/pages/ops/versions.html @@ -37,6 +37,7 @@

Facility System Version + Last Updated @@ -56,6 +57,7 @@

{data: "facility_name", name: "facility__name"}, {data: "system_name", name: "system__name"}, {data: "version", name: "version"}, + {data: "updated", name: "updated"}, { data: "url", name: "id",