Fakultas Ilmu Komputer UI

Commit bd7e66c3 authored by Dave Nathanael's avatar Dave Nathanael
Browse files

First attempt on InvestigationCase rework, problem on filterset_fields

parent d75b2d76
Pipeline #40924 failed with stages
in 2 minutes and 15 seconds
......@@ -30,19 +30,71 @@ CASE_SUBJECT_ORDERING_FIELDS = [
"created_at",
]
class InvestigationCaseFilter(FilterSet):
class Meta:
model = InvestigationCase
fields = [
"case_subject_id",
"reference_case_id",
"case_relation",
"medical_symptoms",
"risk_factors",
"is_referral_needed",
"medical_facility_reference",
"author",
]
INVESTIGATION_CASE_FILTERSET_FIELDS = [
"case_subject__name",
"case_subject__age",
"case_subject__is_male",
"case_subject__address",
"case_subject__district",
"case_subject__sub_district",
"reference_case__case_relation",
"reference_case__medical_symptoms",
"reference_case__risk_factors",
"reference_case__is_referral_needed",
"reference_case__medical_facility_reference",
"reference_case__outcome",
"case_relation",
"medical_symptoms",
"risk_factors",
"is_referral_needed",
"medical_facility_reference",
"outcome",
]
INVESTIGATION_CASE_SEARCH_FIELDS = [
"case_subject__name",
"case_subject__age",
"case_subject__address",
"case_subject__district",
"case_subject__sub_district",
"case_subject__created_at",
"reference_case__case_relation",
"reference_case__medical_symptoms",
"reference_case__risk_factors",
"reference_case__medical_facility_reference",
"reference_case__outcome",
"reference_case__created_at",
"case_relation",
"medical_symptoms",
"risk_factors",
"medical_facility_reference",
"outcome",
"created_at",
]
INVESTIGATION_CASE_ORDERING_FIELDS = [
"case_subject__name",
"case_subject__age",
"case_subject__is_male",
"case_subject__address",
"case_subject__district",
"case_subject__sub_district",
"case_subject__created_at",
"reference_case__case_relation",
"reference_case__medical_symptoms",
"reference_case__risk_factors",
"reference_case__is_referral_needed",
"reference_case__medical_facility_reference",
"reference_case__outcome",
"reference_case__created_at",
"case_relation",
"medical_symptoms",
"risk_factors",
"is_referral_needed",
"medical_facility_reference",
"outcome",
"created_at",
]
class MonitoringCaseFilter(FilterSet):
......
# Generated by Django 3.0.1 on 2020-04-17 15:00
from django.db import migrations, models
import django.db.models.deletion
import uuid
class Migration(migrations.Migration):
dependencies = [
('accounts', '0001_initial'),
('cases', '0005_auto_20200416_2334'),
]
operations = [
migrations.RenameField(
model_name='investigationcase',
old_name='revision_id',
new_name='id',
),
migrations.RemoveField(
model_name='investigationcase',
name='case_id',
),
migrations.RemoveField(
model_name='investigationcase',
name='case_subject_id',
),
migrations.RemoveField(
model_name='investigationcase',
name='is_active',
),
migrations.RemoveField(
model_name='investigationcase',
name='reference_case_id',
),
migrations.CreateModel(
name='InvestigationCaseHistory',
fields=[
('revision_id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('object_id', models.UUIDField(default=uuid.uuid4, editable=False)),
('action_type', models.CharField(choices=[('Create', 'Create'), ('Edit', 'Edit'), ('Delete', 'Delete')], max_length=64)),
('recorded_at', models.DateTimeField(auto_now_add=True)),
('case_subject_id', models.UUIDField(blank=True, null=True)),
('reference_case_id', models.UUIDField(blank=True, null=True)),
('case_relation', models.CharField(blank=True, max_length=128)),
('medical_symptoms', models.TextField(default='{}')),
('risk_factors', models.TextField(default='{}')),
('is_referral_needed', models.BooleanField(db_index=True)),
('medical_facility_reference', models.CharField(blank=True, max_length=128)),
('outcome', models.CharField(blank=True, max_length=256, null=True)),
('author', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='investigation_case_history', to='accounts.Account')),
],
options={
'verbose_name_plural': 'investigation case histories',
'db_table': 'investigation_case_history',
'ordering': ['-recorded_at'],
},
),
]
......@@ -77,9 +77,11 @@ class CaseSubject(HistoryEnabledModel):
)
class InvestigationCase(models.Model):
class InvestigationCaseHistory(models.Model):
revision_id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
case_id = models.UUIDField(default=uuid.uuid4, editable=False)
object_id = models.UUIDField(default=uuid.uuid4, editable=False)
action_type = models.CharField(choices=ACTIVITY_TYPE_CHOICES, max_length=64)
recorded_at = models.DateTimeField(auto_now_add=True)
case_subject_id = models.UUIDField(blank=True, null=True)
reference_case_id = models.UUIDField(blank=True, null=True)
case_relation = models.CharField(max_length=128, blank=True)
......@@ -88,6 +90,56 @@ class InvestigationCase(models.Model):
is_referral_needed = models.BooleanField(db_index=True)
medical_facility_reference = models.CharField(max_length=128, blank=True)
outcome = models.CharField(max_length=256, blank=True, null=True)
author = models.ForeignKey(
Account,
blank=True,
null=True,
on_delete=models.DO_NOTHING,
related_name="investigation_case_history",
)
class Meta:
db_table = "investigation_case_history"
verbose_name_plural = "investigation case histories"
ordering = ["-recorded_at"]
def __str__(self):
return f"[Active] Rev. {self.revision_id} | by {self.author}"
@property
def reference_case(self):
return InvestigationCase.objects.filter(
deleted_at__isnull=True, id=self.reference_case_id,
).first()
@property
def case_subject(self):
return CaseSubject.objects.filter(
deleted_at__isnull=True, id=self.case_subject_id
).first()
class InvestigationCase(HistoryEnabledModel):
case_subject = models.ForeignKey(
CaseSubject,
blank=True,
null=True,
on_delete=models.DO_NOTHING,
related_name="investigation_case",
)
reference_case = models.ForeignKey(
'self',
blank=True,
null=True,
on_delete=models.DO_NOTHING,
related_name="referring_investigation_case",
)
case_relation = models.CharField(max_length=128, blank=True)
medical_symptoms = models.TextField(default="{}")
risk_factors = models.TextField(default="{}")
is_referral_needed = models.BooleanField(db_index=True)
medical_facility_reference = models.CharField(max_length=128, blank=True)
outcome = models.CharField(max_length=256, blank=True, null=True)
author = models.ForeignKey(
Account,
blank=True,
......@@ -95,11 +147,8 @@ class InvestigationCase(models.Model):
on_delete=models.DO_NOTHING,
related_name="investigation_case",
)
deleted_at = models.DateTimeField(blank=True, null=True)
created_at = models.DateTimeField(auto_now_add=True)
is_active = models.BooleanField(default=True, db_index=True)
objects = SoftObjectManager()
objects = SoftDeleteManager()
class Meta:
db_table = "investigation_case"
......@@ -107,15 +156,7 @@ class InvestigationCase(models.Model):
ordering = ["-created_at"]
def __str__(self):
if self.is_active:
return f"[Active] Rev. {self.revision_id} | by {self.author}"
return f"[Inactive] Rev. {self.revision_id} | by {self.author}"
def delete(self):
if self.deleted_at is None:
self.is_active = False
self.deleted_at = datetime.now(tz=pytz.timezone(TIMEZONE))
self.save()
return f"[Active] ID. {self.id} | by {self.author}"
@property
def reference_case(self):
......
......@@ -5,6 +5,7 @@ from apps.cases.models import (
CaseSubject,
CaseSubjectHistory,
InvestigationCase,
InvestigationCaseHistory,
MonitoringCase,
)
......@@ -43,58 +44,48 @@ class CaseSubjectHistorySerializer(serializers.ModelSerializer):
]
class InvestigationCaseSerializer(serializers.ModelSerializer):
is_active = serializers.BooleanField(read_only=True)
class InvestigationCaseSummarySerializer(serializers.ModelSerializer):
# case_subject = CaseSubjectSerializer()
# reference_case = InvestigationCaseSummarySerializer()
# author = AccountSerializer()
class Meta:
model = InvestigationCase
fields = [
"revision_id",
"case_id",
"case_subject_id",
"reference_case_id",
"id",
"case_subject",
"reference_case",
"case_relation",
"medical_symptoms",
"risk_factors",
"is_referral_needed",
"medical_facility_reference",
"is_active",
"outcome",
"author",
"created_at",
]
depth = 1
class InvestigationCaseSummarySerializer(serializers.ModelSerializer):
case_subject = serializers.SerializerMethodField()
reference_case = serializers.SerializerMethodField()
is_active = serializers.BooleanField(read_only=True)
author = AccountSerializer()
class InvestigationCaseSerializer(serializers.ModelSerializer):
class Meta:
model = InvestigationCase
fields = [
"revision_id",
"case_id",
"case_subject_id",
"id",
"case_subject",
"reference_case_id",
"reference_case",
"case_relation",
"medical_symptoms",
"risk_factors",
"is_referral_needed",
"medical_facility_reference",
"author",
"is_active",
"outcome",
]
def get_case_subject(self, instance):
case_subject = instance.case_subject
if case_subject:
return CaseSubjectSerializer(case_subject).data
def get_reference_case(self, instance):
reference_case = instance.reference_case
if reference_case:
return InvestigationCaseSerializer(reference_case).data
def to_representation(self, instance):
serializer = InvestigationCaseSummarySerializer(instance)
return serializer.data
class MonitoringCaseSerializer(serializers.ModelSerializer):
......
......@@ -46,24 +46,13 @@ class InvestigationCaseViewTest(APITestCase):
reference_case_id=self.reference_case.case_id,
deleted_at=datetime.now(tz=pytz.timezone(TIMEZONE)),
)
self.inactive_case = InvestigationCaseFactory(
author=self.author,
case_subject_id=self.case_subject.subject_id,
reference_case_id=self.reference_case.case_id,
is_active=False,
)
def setUp(self):
self.client = APIClient(HTTP_AUTHORIZATION=HEADER_PREFIX + self.token_1.key)
def test_string_representation(self):
inactive_case_str = (
f"[Inactive] Rev. {self.inactive_case.revision_id} | by {self.inactive_case.author}"
)
self.assertEqual(inactive_case_str, str(self.inactive_case))
active_case_str = (
f"[Active] Rev. {self.case.revision_id} | by {self.case.author}"
f"[Active] ID. {self.case.revision_id} | by {self.case.author}"
)
self.assertEqual(active_case_str, str(self.case))
......@@ -238,9 +227,6 @@ class InvestigationCaseViewTest(APITestCase):
InvestigationCase.objects.active_revisions().count()
)
self.assertFalse(
InvestigationCase.objects.get(revision_id=self.case.revision_id).is_active
)
self.assertEqual(
current_investigation_case_revision_count,
prev_all_investigation_case_revision_count + 1,
......@@ -260,21 +246,6 @@ class InvestigationCaseViewTest(APITestCase):
)
self.assertIn('"action_type":"{}"'.format(ACTIVITY_TYPE_EDIT), response_string)
def test_edit_inactive_investigation_case_fails(self):
url = self.BASE_URL + str(self.inactive_case.revision_id) + "/"
data = {
"case_subject_id": str(self.inactive_case.case_subject_id),
"reference_case_id": str(self.reference_case.case_id),
"case_relation": self.inactive_case.case_relation,
"medical_symptoms": self.inactive_case.medical_symptoms,
"risk_factors": self.inactive_case.risk_factors,
"is_referral_needed": not self.inactive_case.is_referral_needed,
"medical_facility_reference": self.inactive_case.medical_facility_reference,
}
response = self.client.put(path=url, data=data, format="json",)
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
def test_edit_investigation_case_fails_with_incomplete_data(self):
url = self.BASE_URL + str(self.case.revision_id) + "/"
......@@ -296,9 +267,6 @@ class InvestigationCaseViewTest(APITestCase):
current_active_investigation_case_revision_count,
prev_active_investigation_case_revision_count,
)
self.assertTrue(
InvestigationCase.objects.get(revision_id=self.case.revision_id).is_active
)
def test_soft_delete_investigation_case_success(self):
url = self.BASE_URL + str(self.case.revision_id) + "/"
......@@ -335,9 +303,3 @@ class InvestigationCaseViewTest(APITestCase):
self.assertIn(
'"action_type":"{}"'.format(ACTIVITY_TYPE_DELETE), response_string
)
def test_delete_inactive_investigation_case_fails(self):
url = self.BASE_URL + str(self.inactive_case.revision_id) + "/"
response = self.client.delete(url)
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
......@@ -5,6 +5,7 @@ from apps.cases.views import (
CaseSubjectViewSet,
MonitoringCaseViewSet,
InvestigationCaseViewSet,
InvestigationPositiveCaseAPIView,
)
......@@ -17,4 +18,5 @@ router.register(
urlpatterns = [
path("", include(router.urls)),
path("positive-cases/", InvestigationPositiveCaseAPIView.as_view()),
]
......@@ -7,6 +7,7 @@ from rest_framework import status, viewsets
from rest_framework.response import Response
from rest_framework.pagination import PageNumberPagination
from rest_framework.decorators import action
from rest_framework.generics import ListAPIView
from rest_framework.filters import SearchFilter, OrderingFilter
from django_filters import FilterSet
from django_filters.rest_framework import DjangoFilterBackend
......@@ -27,7 +28,9 @@ from apps.cases.filters import (
CASE_SUBJECT_FILTERSET_FIELDS,
CASE_SUBJECT_SEARCH_FIELDS,
CASE_SUBJECT_ORDERING_FIELDS,
InvestigationCaseFilter,
INVESTIGATION_CASE_FILTERSET_FIELDS,
INVESTIGATION_CASE_SEARCH_FIELDS,
INVESTIGATION_CASE_ORDERING_FIELDS,
MonitoringCaseFilter,
)
from apps.commons.permissions import IsAuthorOrAdministrator
......@@ -53,99 +56,37 @@ class CaseSubjectViewSet(viewsets.ModelViewSet):
ordering_fields = CASE_SUBJECT_ORDERING_FIELDS
class InvestigationCaseViewSet(viewsets.ViewSet):
queryset = InvestigationCase.objects.active_revisions()
filter_backends = (DjangoFilterBackend,)
permission_classes = [
IsAuthorOrAdministrator,
]
def list(self, request):
queryset = self.queryset.filter(Q(outcome__isnull=True) | Q(outcome__icontains="-"))
paginator = PageNumberPagination()
filtered_set = InvestigationCaseFilter(request.GET, queryset=queryset).qs
context = paginator.paginate_queryset(filtered_set, request)
serializer = InvestigationCaseSummarySerializer(context, many=True)
return paginator.get_paginated_response(serializer.data)
def retrieve(self, request, pk=None):
instance = get_object_or_404(self.queryset, pk=pk)
serializer = InvestigationCaseSummarySerializer(instance)
return Response(serializer.data, status=status.HTTP_200_OK)
def create(self, request):
serializer = InvestigationCaseSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
serializer_data = serializer.data
instance = InvestigationCase.objects.create(
**serializer_data, author=request.user.account
)
# Add investigation case creation log
Log.objects.create(
model_name=MODEL_NAME_INVESTIGATION_CASE,
object_id=instance.case_id,
revision_id=instance.revision_id,
action_type=ACTIVITY_TYPE_CREATE,
author=request.user.account,
)
return Response(
InvestigationCaseSummarySerializer(instance).data,
status=status.HTTP_201_CREATED,
)
def update(self, request, pk=None):
previous_instance = get_object_or_404(self.queryset, pk=pk)
serializer = InvestigationCaseSerializer(previous_instance, data=request.data)
serializer.is_valid(raise_exception=True)
serializer_data = serializer.validated_data
serializer_data["case_id"] = previous_instance.case_id
new_instance = InvestigationCase.objects.create(
**serializer_data, is_active=True,
)
previous_instance.is_active = False
previous_instance.save()
# Add investigation case update log
Log.objects.create(
model_name=MODEL_NAME_INVESTIGATION_CASE,
object_id=previous_instance.case_id,
revision_id=previous_instance.revision_id,
action_type=ACTIVITY_TYPE_EDIT,
author=request.user.account,
)
update_serializer = InvestigationCaseSerializer(instance=new_instance)
return Response(update_serializer.data, status=status.HTTP_200_OK)
def destroy(self, request, pk=None):
instance = get_object_or_404(self.queryset, pk=pk)
instance.delete()
serializer = InvestigationCaseSerializer(instance=instance)
class InvestigationCaseViewSet(viewsets.ModelViewSet):
serializer_class = InvestigationCaseSummarySerializer
queryset = InvestigationCase.objects.all()
filter_backends = (DjangoFilterBackend, SearchFilter, OrderingFilter)
permission_classes = (IsAuthorOrAdministrator,)
pagination_class = PageNumberPagination
filterset_fields = INVESTIGATION_CASE_FILTERSET_FIELDS
search_fields = INVESTIGATION_CASE_SEARCH_FIELDS
ordering_fields = INVESTIGATION_CASE_ORDERING_FIELDS
# Add investigation case deletion log
Log.objects.create(
model_name=MODEL_NAME_INVESTIGATION_CASE,
object_id=instance.case_id,
revision_id=instance.revision_id,
action_type=ACTIVITY_TYPE_DELETE,
author=request.user.account,
)
def get_queryset(self):
if self.action == 'list':
return self.queryset.filter(Q(outcome__isnull=True) | Q(outcome__icontains="-"))
return self.queryset
return Response(serializer.data, status=status.HTTP_200_OK)
def get_serializer_class(self):
if self.action in ['create', 'update']:
return InvestigationCaseSerializer
return InvestigationCaseSummarySerializer
@action(detail=False, methods=["get"], url_path="positive-cases")
def list_positive_cases(self, request):
queryset = self.queryset.filter(outcome__isnull=False).exclude(outcome__icontains="-")
paginator = PageNumberPagination()
filtered_set = InvestigationCaseFilter(request.GET, queryset=queryset).qs
context = paginator.paginate_queryset(filtered_set, request)
serializer = InvestigationCaseSummarySerializer(context, many=True)
return paginator.get_paginated_response(serializer.data)
class InvestigationPositiveCaseAPIView(ListAPIView):
serializer_class = InvestigationCaseSummarySerializer
queryset = InvestigationCase.objects.all().filter(
outcome__isnull=False).exclude(outcome__icontains="-")
filter_backends = (DjangoFilterBackend, SearchFilter, OrderingFilter)
permission_classes = (IsAuthorOrAdministrator,)
pagination_class = PageNumberPagination
filterset_fields = INVESTIGATION_CASE_FILTERSET_FIELDS
search_fields = INVESTIGATION_CASE_SEARCH_FIELDS
ordering_fields = INVESTIGATION_CASE_ORDERING_FIELDS
class MonitoringCaseViewSet(viewsets.ViewSet):
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment