Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/1166 fix showing monitoring #1184

Merged
merged 7 commits into from
Feb 20, 2024
19 changes: 19 additions & 0 deletions backend/api/v1/v1_data/migrations/0031_alter_formdata_parent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Generated by Django 4.0.4 on 2024-02-20 04:02

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('v1_data', '0030_alter_formdata_parent'),
]

operations = [
migrations.AlterField(
model_name='formdata',
name='parent',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='children', to='v1_data.formdata'),
),
]
8 changes: 0 additions & 8 deletions backend/api/v1/v1_data/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,6 @@ class FormData(models.Model):
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(default=None, null=True)

parent = models.ForeignKey(
'self',
on_delete=models.PROTECT,
related_name='children',
default=None,
null=True
)

def __str__(self):
return self.name

Expand Down
13 changes: 3 additions & 10 deletions backend/api/v1/v1_data/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ class ListFormDataRequestSerializer(serializers.Serializer):
required=False,
)
parent = CustomPrimaryKeyRelatedField(
queryset=FormData.objects.filter(parent=None).none(),
queryset=FormData.objects.none(),
required=False
)

Expand All @@ -245,8 +245,7 @@ def __init__(self, **kwargs):
self.fields.get("questions").child.queryset = Questions.objects.all()
form_id = self.context.get('form_id')
self.fields.get("parent").queryset = FormData.objects.filter(
form_id=form_id,
parent=None,
form_id=form_id
).all()


Expand All @@ -257,7 +256,6 @@ class ListFormDataSerializer(serializers.ModelSerializer):
updated = serializers.SerializerMethodField()
administration = serializers.ReadOnlyField(source="administration.name")
pending_data = serializers.SerializerMethodField()
children_count = serializers.SerializerMethodField()

@extend_schema_field(OpenApiTypes.STR)
def get_created_by(self, instance: FormData):
Expand Down Expand Up @@ -298,10 +296,6 @@ def get_pending_data(self, instance: FormData):
}
return None

@extend_schema_field(OpenApiTypes.NUMBER)
def get_children_count(self, instance: FormData):
return instance.children.count()

class Meta:
model = FormData
fields = [
Expand All @@ -315,8 +309,7 @@ class Meta:
"updated_by",
"created",
"updated",
"pending_data",
"children_count",
"pending_data"
]


Expand Down
1 change: 0 additions & 1 deletion backend/api/v1/v1_data/tests/tests_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ def test_list_form_data(self):
self.assertEqual(list(result['data'][0]), [
'id', 'uuid', 'name', 'form', 'administration', 'geo',
'created_by', 'updated_by', 'created', 'updated', 'pending_data',
'children_count'
])
self.assertIsNotNone(result['data'][0]['uuid'])

Expand Down
146 changes: 146 additions & 0 deletions backend/api/v1/v1_data/tests/tests_monitoring_endpoints.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
from django.test import TestCase
from django.core.management import call_command
from api.v1.v1_users.models import SystemUser
from api.v1.v1_data.models import FormData
from api.v1.v1_forms.models import Forms
from api.v1.v1_forms.constants import FormTypes
from api.v1.v1_profile.models import Administration, Access
from api.v1.v1_profile.constants import UserRoleTypes
from api.v1.v1_data.management.commands.fake_data_seeder import (
add_fake_answers
)


class MonitoringDataTestCase(TestCase):
def setUp(self):
call_command('administration_seeder', '--test')
call_command('form_seeder', '--test')
self.user = SystemUser.objects.create_user(
email='[email protected]',
password='test1234',
first_name='test',
last_name='testing',
)
self.administration = Administration.objects.filter(
parent__isnull=True
).first()
role = UserRoleTypes.admin
self.user_access = Access.objects.create(
user=self.user, role=role, administration=self.administration
)
self.uuid = '1234567890'
self.form = Forms.objects.filter(type=FormTypes.county).first()
self.data = FormData.objects.create(
parent=None,
uuid=self.uuid,
form=self.form,
administration=self.administration,
created_by=self.user,
)
add_fake_answers(self.data, FormTypes.county)

# Login as an admin
admin = {"email": self.user.email, "password": 'test1234'}
admin = self.client.post(
'/api/v1/login',
admin,
content_type='application/json'
)
admin = admin.json()
self.token = admin.get("token")

def test_parent_data(self):
data = self.client.get(
f"/api/v1/form-data/1/{self.form.id}",
content_type='application/json',
**{'HTTP_AUTHORIZATION': f'Bearer {self.token}'}
)
self.assertEqual(data.status_code, 200)
data = data.json()
self.assertEqual(data['total'], 1)
self.assertEqual(data['data'][0]['uuid'], self.uuid)

def test_update_parent_data(self):
payload = [
{
"question": 101,
"value": "Edit"
}
]
edit = self.client.put(
f'/api/v1/form-data/{self.form.id}?data_id={self.data.id}',
payload,
content_type='application/json',
**{'HTTP_AUTHORIZATION': f'Bearer {self.token}'}
)
self.assertEqual(edit.status_code, 200)

data = self.client.get(
f"/api/v1/form-data/1/{self.form.id}",
content_type='application/json',
**{'HTTP_AUTHORIZATION': f'Bearer {self.token}'}
)
self.assertEqual(data.status_code, 200)
data = data.json()
self.assertEqual(data['total'], 1)

answers = self.client.get(
f'/api/v1/data/{self.data.id}',
content_type='application/json',
**{'HTTP_AUTHORIZATION': f'Bearer {self.token}'}
)
self.assertEqual(answers.status_code, 200)
answers = answers.json()
find_answer = list(filter(lambda a: a['question'] == 101, answers))
self.assertEqual(len(find_answer), 1)
self.assertNotEqual(find_answer[0]['history'], None)
self.assertEqual(find_answer[0]['value'], 'Edit')

def test_add_new_monitoring(self):
monitoring = FormData.objects.create(
uuid=self.uuid,
form=self.form,
administration=self.administration,
created_by=self.user,
)
add_fake_answers(monitoring, form_type=FormTypes.county)

data = self.client.get(
f"/api/v1/form-data/1/{self.form.id}",
content_type='application/json',
**{'HTTP_AUTHORIZATION': f'Bearer {self.token}'}
)
self.assertEqual(data.status_code, 200)
data = data.json()
self.assertEqual(data['total'], 1)

data_parent = self.client.get(
f"/api/v1/form-data/1/{self.form.id}?parent={monitoring.id}",
content_type='application/json',
**{'HTTP_AUTHORIZATION': f'Bearer {self.token}'}
)
self.assertEqual(data_parent.status_code, 200)
data_parent = data_parent.json()
self.assertEqual(data_parent['total'], 2)

self.assertEqual(data_parent['data'][0]['name'], monitoring.name)

def test_get_latest_data(self):
for m in range(2):
monitoring = FormData.objects.create(
uuid=self.uuid,
form=self.form,
administration=self.administration,
created_by=self.user,
)
add_fake_answers(monitoring, form_type=FormTypes.county)
lastest = FormData.objects.order_by('-created').first()
data = self.client.get(
f"/api/v1/form-data/1/{self.form.id}",
content_type='application/json',
**{'HTTP_AUTHORIZATION': f'Bearer {self.token}'}
)
self.assertEqual(data.status_code, 200)
data = data.json()
self.assertEqual(data['total'], 1)
self.assertEqual(data['data'][0]['name'], lastest.name)
70 changes: 33 additions & 37 deletions backend/api/v1/v1_data/views.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
# Create your views here.
from math import ceil
from collections import defaultdict
from datetime import datetime, date, timedelta
from datetime import datetime, date
from wsgiref.util import FileWrapper
from django.utils import timezone
# from operator import or_
# from functools import reduce

from django.contrib.postgres.aggregates import StringAgg
from django.db.models import Count, TextField, Value, F, Sum, Avg, Max, Q
from django.db.models import Count, TextField, F, Sum, Avg, Max
from django.db.models.functions import Cast, Coalesce
from django.http import HttpResponse
from django_q.tasks import async_task
Expand Down Expand Up @@ -114,22 +114,41 @@ def get(self, request, form_id, version):
serializer.errors)},
status=status.HTTP_400_BAD_REQUEST
)
page_size = REST_FRAMEWORK.get('PAGE_SIZE')

paginator = PageNumberPagination()

parent = serializer.validated_data.get('parent')
filter_data = {}
if parent:
latest_created_per_uuid = FormData.objects.filter(
Q(form_id=form_id, parent=parent) |
Q(pk=parent.id)
).values('uuid').annotate(latest_created=Max('created'))
filter_data['uuid__in'] = latest_created_per_uuid.values('uuid')
else:
filter_data['parent'] = None
queryset = form.form_form_data.filter(uuid=parent.uuid).order_by(
'-created'
)
instance = paginator.paginate_queryset(queryset, request)
data = {
"current": int(request.GET.get('page', '1')),
"total": queryset.count(),
"total_page": ceil(queryset.count() / page_size),
"data": ListFormDataSerializer(
instance=instance, context={
'questions': serializer.validated_data.get(
'questions')},
many=True).data,
}
return Response(data, status=status.HTTP_200_OK)

filter_data = {}
latest_ids_per_uuid = form.form_form_data.values('uuid').annotate(
latest_id=Max('id')
).values_list('latest_id', flat=True)
filter_data["pk__in"] = latest_ids_per_uuid
if serializer.validated_data.get('administration'):
filter_administration = serializer.validated_data.get(
'administration')
if filter_administration.path:
filter_path = '{0}{1}.'.format(filter_administration.path,
filter_administration.id)
filter_path = '{0}{1}.'.format(
filter_administration.path,
filter_administration.id
)
else:
filter_path = f"{filter_administration.id}."
filter_descendants = list(Administration.objects.filter(
Expand All @@ -145,34 +164,11 @@ def get(self, request, form_id, version):
administration_id=request.GET.get("administration"),
options=request.GET.getlist('options'))
filter_data["pk__in"] = data_ids

page_size = REST_FRAMEWORK.get('PAGE_SIZE')

the_past = datetime.now() - timedelta(days=10 * 365)
queryset = form.form_form_data.filter(**filter_data)
if parent:
queryset = queryset.annotate(
last_updated=Coalesce('updated', Value(the_past)),
latest_created=Coalesce('updated', 'created') \
# Use updated time if available, otherwise use created time
).filter(
created=F('latest_created') \
# Filter by objects where created equals latest_created
).order_by(
'-last_updated',
'-created'
)
else:
queryset = queryset.annotate(
last_updated=Coalesce('updated', Value(the_past)),
children_count=Count('children')
).order_by(
'-children_count',
'-last_updated',
queryset = form.form_form_data.filter(**filter_data) \
.order_by(
'-created'
)

paginator = PageNumberPagination()
instance = paginator.paginate_queryset(queryset, request)
data = {
"current": int(request.GET.get('page', '1')),
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ const RouteList = () => {
element={<Private element={ManageData} alias="data" />}
/>
<Route
path="data/monitoring/:parentId"
path="data/:form/monitoring/:parentId"
element={<Private element={MonitoringDetail} alias="data" />}
/>
<Route
Expand Down
Loading