diff --git a/apps/commons/permissions.py b/apps/commons/permissions.py index fca11099cd95fc2dde168120281ff6aed8389a51..2de969639074f89e3504ffd37cc95e88b6b073c4 100644 --- a/apps/commons/permissions.py +++ b/apps/commons/permissions.py @@ -6,6 +6,10 @@ from django.core.exceptions import ObjectDoesNotExist class IsAuthenticated(BasePermission): def has_permission(self, request, view): user = request.user + + if not hasattr(user, "account"): + return False + return ( user.is_authenticated and user.account.is_active diff --git a/apps/commons/tests/test_permissions.py b/apps/commons/tests/test_permissions.py index bdf73a73d0bab3683e4ecfdd999ae4dcfc8f3370..ac933d876bf8e7bfe6c6599aff363672877cd183 100644 --- a/apps/commons/tests/test_permissions.py +++ b/apps/commons/tests/test_permissions.py @@ -19,6 +19,7 @@ class IsAuthenticatedPermissionTest(APITestCase): cls.user_1 = UserFactory(username="user_1", password="justpass") cls.user_2 = UserFactory(username="user_2", password="justpass") cls.user_3 = UserFactory(username="user_3", password="justpass") + cls.user_4 = UserFactory(username="user_4", password="justpass") cls.account_1 = AccountFactory( admin=True, @@ -32,7 +33,7 @@ class IsAuthenticatedPermissionTest(APITestCase): is_active=False, is_verified=True ) - cls.account_2 = AccountFactory( + cls.account_3 = AccountFactory( admin=False, user=cls.user_3, is_active=True, @@ -62,6 +63,12 @@ class IsAuthenticatedPermissionTest(APITestCase): request.user = self.user_3 self.assertFalse(self.permission.has_permission(request, None)) + def test_has_permission_false_for_anonymous_user(self): + self.client = APIClient() + request = self.client.get("/").wsgi_request + request.user = self.user_4 + self.assertFalse(self.permission.has_permission(request, None)) + class CreateOnlyPermissionTest(APITestCase): @classmethod diff --git a/apps/exportables/constants.py b/apps/exportables/constants.py index b33f72722e639efda649ecdb3cf05f692bb2dcea..9e7dd2be5240fcca545075bd4048cf661f9a510d 100644 --- a/apps/exportables/constants.py +++ b/apps/exportables/constants.py @@ -34,3 +34,39 @@ UNDETERMINED = "undetermined" # Total TOTAL = "total_count" + +# CSV Fields + +INVESTIGATION_CASE_RENDERER_FIELDS = [ + "case_subject__name", + "case_subject__address", + "case_subject__age", + "case_subject__is_male", + "case_subject__district", + "case_subject__sub_district", + "case_relation", + "reference_case__case_subject__name", + "outcome", + "medical_symptoms", + "risk_factors", + "medical_facility_reference", + "created_at", + "author__name", +] + +INVESTIGATION_CASE_HEADER_FIELDS = [ + "nama", + "alamat", + "usia", + "jenis_kelamin", + "kabupaten", + "kecamatan", + "jenis_kontak", + "subyek_kasus_acuan", + "hasil_pemeriksaan", + "gejala_medis", + "faktor_risiko", + "rujukan_fasilitas_kesehatan", + "tanggal_pencatatan", + "kader_pencatat", +] diff --git a/apps/exportables/renderers.py b/apps/exportables/renderers.py index e7c1cc3c1d11ce13e8afa6892d8c316680b50a2a..f9780f5f1aa9e22c2ffbe5ca833a801122e3a92c 100644 --- a/apps/exportables/renderers.py +++ b/apps/exportables/renderers.py @@ -1,21 +1,7 @@ from rest_framework_csv.renderers import CSVRenderer -INVESTIGATION_CASE_RENDERER_FIELDS = [ - "id", - "case_subject__age", - "case_subject__is_male", - "case_subject__district", - "outcome", - "is_positive", - "case_relation", - "medical_symptoms", - "risk_factors", - "is_referral_needed", - "medical_facility_reference", - "created_at", - "author__name", -] +from apps.exportables.constants import INVESTIGATION_CASE_HEADER_FIELDS class InvestigationCaseCSVRenderer(CSVRenderer): - header = INVESTIGATION_CASE_RENDERER_FIELDS + header = INVESTIGATION_CASE_HEADER_FIELDS diff --git a/apps/exportables/tests/test_units/test_exportables.py b/apps/exportables/tests/test_units/test_exportables.py index e3dd2995c8cc3f6899d710d1bfa7336551e9adcc..46fba1b14064711aa06c932d8c57168c315da9e9 100644 --- a/apps/exportables/tests/test_units/test_exportables.py +++ b/apps/exportables/tests/test_units/test_exportables.py @@ -2,13 +2,22 @@ import json import csv import io from rest_framework import status -from rest_framework.test import APITestCase +from rest_framework.authtoken.models import Token +from rest_framework.test import APITestCase, APIClient + +from apps.constants import HEADER_PREFIX + +from apps.accounts.tests.factories.accounts import AccountFactory, UserFactory from apps.cases.models import CaseSubject from apps.cases.tests.factories.case_subjects import CaseSubjectFactory from apps.cases.tests.factories.cases import InvestigationCaseFactory -from apps.exportables.renderers import INVESTIGATION_CASE_RENDERER_FIELDS +from apps.exportables.constants import ( + INVESTIGATION_CASE_HEADER_FIELDS, + INVESTIGATION_CASE_RENDERER_FIELDS, +) + def init_data(): InvestigationCaseFactory( @@ -56,8 +65,15 @@ def init_data(): class ExportableViewTest(APITestCase): @classmethod def setUpTestData(cls): + cls.user = UserFactory(username="user", password="justpass") + cls.admin = AccountFactory(admin=True, user=cls.user) + cls.token, _ = Token.objects.get_or_create(user=cls.user) + init_data() + def setUp(self): + self.client = APIClient(HTTP_AUTHORIZATION=HEADER_PREFIX + self.token.key) + def test_exportable_data_return_values(self): url = "/exportables/" response = self.client.get(url) @@ -188,9 +204,16 @@ class ExportableViewTest(APITestCase): class ExportInvestigationCaseViewTest(APITestCase): @classmethod def setUpTestData(cls): + cls.user = UserFactory(username="user", password="justpass") + cls.admin = AccountFactory(admin=True, user=cls.user) + cls.token, _ = Token.objects.get_or_create(user=cls.user) cls.BASE_URL = "/exportables/investigation-cases-csv/" + init_data() + def setUp(self): + self.client = APIClient(HTTP_AUTHORIZATION=HEADER_PREFIX + self.token.key) + def export_csv_test_util(self, filter): url = self.BASE_URL + filter response = self.client.get(url) @@ -206,7 +229,7 @@ class ExportInvestigationCaseViewTest(APITestCase): body = list(reader) headers = body.pop(0) - self.assertEqual(headers, INVESTIGATION_CASE_RENDERER_FIELDS) + self.assertEqual(headers, INVESTIGATION_CASE_HEADER_FIELDS) return body def wrong_query_param_test_util(self, filter): diff --git a/apps/exportables/utils.py b/apps/exportables/utils.py index 24f32f796414f18754c693b2ef3e64a51c60a887..410fed7a598eba5fce861b5224c8a801a592850e 100644 --- a/apps/exportables/utils.py +++ b/apps/exportables/utils.py @@ -1,3 +1,7 @@ +import json +import time +from django.utils import timezone + from apps.exportables.constants import ( DISTRICT, DISTRICTS, @@ -6,10 +10,15 @@ from apps.exportables.constants import ( NEGATIVE, POSITIVE, TOTAL, - UNDETERMINED + UNDETERMINED, + INVESTIGATION_CASE_HEADER_FIELDS, + INVESTIGATION_CASE_RENDERER_FIELDS, ) +# CASE STATISTIC COUNTS + + def generate_initial_counts(): return { POSITIVE: 0, @@ -43,3 +52,40 @@ def map_sex_value(sex): if sex: return MALE return FEMALE + + +# CSV EXPORTS + + +def format_custom_csv_rows(cases_csv_rows): + + formatted_rows = [] + FIELDS_LENGTH = len(INVESTIGATION_CASE_RENDERER_FIELDS) + + for row in cases_csv_rows: + + formatted_row = {} + + for field_name_index in range(FIELDS_LENGTH): + original_field_name = INVESTIGATION_CASE_RENDERER_FIELDS[field_name_index] + target_field_name = INVESTIGATION_CASE_HEADER_FIELDS[field_name_index] + + formatted_row[target_field_name] = row[original_field_name] + + value = formatted_row["jenis_kelamin"] + formatted_row["jenis_kelamin"] = "Laki-laki" if value else "Perempuan" + + value = json.loads(formatted_row["gejala_medis"]) + selected_values = [key for key in value.keys() if value[key]] + formatted_row["gejala_medis"] = "; ".join(selected_values) + + value = json.loads(formatted_row["faktor_risiko"]) + selected_values = [key for key in value.keys() if value[key]] + formatted_row["faktor_risiko"] = "; ".join(selected_values) + + value = formatted_row["tanggal_pencatatan"] + formatted_row["tanggal_pencatatan"] = timezone.localtime(value) + + formatted_rows.append(formatted_row) + + return formatted_rows diff --git a/apps/exportables/views.py b/apps/exportables/views.py index 60c9878fc44e9808030b5b3957a19ae7f8e74b67..911ce7c8d3db3c8eefa872f25082ba183bbcaaf5 100644 --- a/apps/exportables/views.py +++ b/apps/exportables/views.py @@ -9,6 +9,7 @@ from apps.cases.models import ( CaseSubject, InvestigationCase, ) +from apps.commons.permissions import IsAuthenticated from apps.exportables.constants import ( AGE, DISTRICT, @@ -19,31 +20,32 @@ from apps.exportables.constants import ( POSITIVE, SEX, TOTAL, - UNDETERMINED + UNDETERMINED, + INVESTIGATION_CASE_RENDERER_FIELDS, ) from apps.exportables.utils import ( + format_custom_csv_rows, generate_initial_counts, generate_initial_group_counts, map_outcome_value, map_sex_value, ) -from apps.exportables.renderers import ( - InvestigationCaseCSVRenderer, - INVESTIGATION_CASE_RENDERER_FIELDS, -) +from apps.exportables.renderers import InvestigationCaseCSVRenderer class CaseCountsView(APIView): + permission_classes = (IsAuthenticated,) def get(self, request, format=None): case_subjects = CaseSubject.objects.all() - investigation_cases = (InvestigationCase.objects - .values("case_subject") - .annotate(latest_investigation_case=Max("created_at")) - .order_by() - .values("case_subject__id", "is_positive") - ) + investigation_cases = ( + InvestigationCase.objects + .values("case_subject") + .annotate(latest_investigation_case=Max("created_at")) + .order_by() + .values("case_subject__id", "is_positive") + ) outcomes = {} @@ -91,6 +93,7 @@ class CaseCountsView(APIView): class ExportInvestigationCaseView(APIView): + permission_classes = (IsAuthenticated,) renderer_classes = (InvestigationCaseCSVRenderer,) def get(self, request, format=None): @@ -127,9 +130,14 @@ class ExportInvestigationCaseView(APIView): cases.prefetch_related( "case_subject", "reference_case", - "author").values( - *INVESTIGATION_CASE_RENDERER_FIELDS)) + "author" + ).values( + *INVESTIGATION_CASE_RENDERER_FIELDS + ) + ) + formatted_data = format_custom_csv_rows(data) + filename = "cases-{}.csv".format(timezone.localtime(timezone.now())) headers = {"Content-Disposition": 'attachment; filename="{}"'.format(filename)} - return Response(data, headers=headers) + return Response(formatted_data, headers=headers)