From c2a8000e1cdb1bf0e48b5eb411c4d113f8193192 Mon Sep 17 00:00:00 2001 From: Muhammad Rafif Elfazri Date: Wed, 19 May 2021 18:49:59 +0700 Subject: [PATCH 01/13] [CHORES] Install django-imagefield module for Image Compression --- requirements.txt | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index eb157e8..52944c0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,14 +4,18 @@ boto3==1.17.12 botocore==1.20.12 cachetools==4.2.1 certifi==2020.12.5 +cffi==1.14.5 chardet==4.0.0 +colorclass==2.2.0 coverage==5.4 +cryptography==3.4.6 defusedxml==0.6.0 dj-database-url==0.5.0 Django==3.1.7 django-allauth==0.44.0 django-cors-middleware==1.5.0 django-environ==0.4.5 +django-imagefield==0.13.0 django-multiselectfield==0.1.12 django-oauth-toolkit==1.4.0 django-rest-auth==0.9.5 @@ -19,9 +23,12 @@ django-storages==1.11.1 django-utils-six==2.0 djangorestframework==3.12.2 djangorestframework-simplejwt==4.6.0 +docopt==0.6.2 +google-api-core==1.26.1 google-api-python-client==1.12.8 google-auth==1.27.0 google-auth-httplib2==0.0.4 +googleapis-common-protos==1.53.0 gunicorn==20.0.4 httplib2==0.19.0 idna==2.6 @@ -30,7 +37,10 @@ jmespath==0.10.0 lazy-object-proxy==1.5.2 mccabe==0.6.1 oauthlib==3.1.0 +packaging==20.9 Pillow==8.1.0 +pip-upgrader==1.4.15 +protobuf==3.15.6 psycopg2-binary==2.8.6 pyasn1==0.4.8 pyasn1-modules==0.2.8 @@ -39,10 +49,10 @@ PyJWT==2.0.1 pylint==2.7.0 pylint-django==2.4.2 pylint-plugin-utils==0.6 +pyparsing==2.4.7 python-dateutil==2.8.1 python-dotenv==0.15.0 python3-openid==3.2.0 -pip-upgrader==1.4.15 pytz==2021.1 requests==2.25.1 requests-oauthlib==1.3.0 @@ -53,6 +63,7 @@ six==1.15.0 social-auth-app-django==4.0.0 social-auth-core==4.0.3 sqlparse==0.4.1 +terminaltables==3.1.0 toml==0.10.2 typed-ast==1.4.2 uritemplate==3.0.1 -- GitLab From e0fed9ed788ace78036e40135bcd47ec4c7dc9b6 Mon Sep 17 00:00:00 2001 From: Muhammad Rafif Elfazri Date: Wed, 19 May 2021 19:13:38 +0700 Subject: [PATCH 02/13] [CHORES] Fix Migrations file by merge --- registrasi/migrations/0006_merge_20210519_1213.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 registrasi/migrations/0006_merge_20210519_1213.py diff --git a/registrasi/migrations/0006_merge_20210519_1213.py b/registrasi/migrations/0006_merge_20210519_1213.py new file mode 100644 index 0000000..4de0e03 --- /dev/null +++ b/registrasi/migrations/0006_merge_20210519_1213.py @@ -0,0 +1,14 @@ +# Generated by Django 3.1.7 on 2021-05-19 12:13 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('registrasi', '0005_auto_20210516_1734'), + ('registrasi', '0005_bisagouser_foto'), + ] + + operations = [ + ] -- GitLab From dc0f4d38581ac59a6b8c20b32fc0ebd842e681be Mon Sep 17 00:00:00 2001 From: Muhammad Rafif Elfazri Date: Thu, 20 May 2021 09:42:54 +0700 Subject: [PATCH 03/13] [CHORES] Remove django-image-field module --- requirements.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 52944c0..71d14a6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -15,7 +15,6 @@ Django==3.1.7 django-allauth==0.44.0 django-cors-middleware==1.5.0 django-environ==0.4.5 -django-imagefield==0.13.0 django-multiselectfield==0.1.12 django-oauth-toolkit==1.4.0 django-rest-auth==0.9.5 -- GitLab From 3ed674bf09960085a12eb542994a4146ad95afea Mon Sep 17 00:00:00 2001 From: Muhammad Rafif Elfazri Date: Thu, 20 May 2021 12:07:54 +0700 Subject: [PATCH 04/13] [REFACTOR] seek(0) of InMemoryFileUpload after creating image --- informasi_fasilitas/test_views_kegiatan.py | 1 + 1 file changed, 1 insertion(+) diff --git a/informasi_fasilitas/test_views_kegiatan.py b/informasi_fasilitas/test_views_kegiatan.py index cd84115..91cb273 100644 --- a/informasi_fasilitas/test_views_kegiatan.py +++ b/informasi_fasilitas/test_views_kegiatan.py @@ -41,6 +41,7 @@ class KegiatanRelatedViewTest(InformasiFasilitasViewTest): self.kegiatan_images = {'images': [image1, image2]} for image in self.kegiatan_images["images"]: FotoKegiatan.objects.create(kegiatan=self.kegiatan, foto=image) + image.seek(0) self.get_list_kegiatan_url = \ reverse('list-kegiatan', kwargs=self.kwargs_place_id) -- GitLab From 02eb18fffe3dce1a2111ece3746af72b2af8836b Mon Sep 17 00:00:00 2001 From: Muhammad Rafif Elfazri Date: Thu, 20 May 2021 12:09:33 +0700 Subject: [PATCH 05/13] [CHORES] Create CompressedImageField (subclass ImageField) --- pplbackend/custom_model_field.py | 38 ++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 pplbackend/custom_model_field.py diff --git a/pplbackend/custom_model_field.py b/pplbackend/custom_model_field.py new file mode 100644 index 0000000..407ea34 --- /dev/null +++ b/pplbackend/custom_model_field.py @@ -0,0 +1,38 @@ +from io import BytesIO +import os +from django.core.files import File +from django.db import models +from django.db.models.fields.files import ImageFieldFile +from PIL import Image, ImageOps + + +class CompressedImageFieldFile(ImageFieldFile): + def save(self, name, content, save=True): + # Compressed Image + image = Image.open(content) + image = image.convert('RGB') + image = ImageOps.exif_transpose(image) + im_io = BytesIO() + image.save(im_io, "JPEG", optimize=True, quality=self.field.quality) + + # Change extension + filename = os.path.splitext(name)[0] + filename = f"{filename}.jpg" + + image = File(im_io, name=filename) + super().save(filename, image, save) + + +class CompressedImageField(models.ImageField): + attr_class = CompressedImageFieldFile + + def __init__(self, verbose_name=None, name=None, width_field=None, height_field=None, + quality=90, **kwargs): + self.quality = quality + super().__init__(verbose_name, name, width_field, height_field, **kwargs) + + def deconstruct(self): + name, path, args, kwargs = super().deconstruct() + if self.quality: + kwargs['quality'] = self.quality + return name, path, args, kwargs -- GitLab From d8873846bb84d4557025488b03fae6435b13f388 Mon Sep 17 00:00:00 2001 From: Muhammad Rafif Elfazri Date: Thu, 20 May 2021 12:10:39 +0700 Subject: [PATCH 06/13] [CHORES] implement CompressedImageField in FotoKegiatan models --- .../migrations/0017_auto_20210520_0336.py | 19 +++++++++++++++++++ .../migrations/0018_auto_20210520_0451.py | 19 +++++++++++++++++++ informasi_fasilitas/models.py | 4 ++-- 3 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 informasi_fasilitas/migrations/0017_auto_20210520_0336.py create mode 100644 informasi_fasilitas/migrations/0018_auto_20210520_0451.py diff --git a/informasi_fasilitas/migrations/0017_auto_20210520_0336.py b/informasi_fasilitas/migrations/0017_auto_20210520_0336.py new file mode 100644 index 0000000..c0494ed --- /dev/null +++ b/informasi_fasilitas/migrations/0017_auto_20210520_0336.py @@ -0,0 +1,19 @@ +# Generated by Django 3.1.7 on 2021-05-20 03:36 + +from django.db import migrations +import pplbackend.custom_model_field + + +class Migration(migrations.Migration): + + dependencies = [ + ('informasi_fasilitas', '0016_kegiatan_links'), + ] + + operations = [ + migrations.AlterField( + model_name='fotokegiatan', + name='foto', + field=pplbackend.custom_model_field.CompressedImageField(default=None, null=True, upload_to='kegiatan/'), + ), + ] diff --git a/informasi_fasilitas/migrations/0018_auto_20210520_0451.py b/informasi_fasilitas/migrations/0018_auto_20210520_0451.py new file mode 100644 index 0000000..a00898b --- /dev/null +++ b/informasi_fasilitas/migrations/0018_auto_20210520_0451.py @@ -0,0 +1,19 @@ +# Generated by Django 3.1.7 on 2021-05-20 04:51 + +from django.db import migrations +import pplbackend.custom_model_field + + +class Migration(migrations.Migration): + + dependencies = [ + ('informasi_fasilitas', '0017_auto_20210520_0336'), + ] + + operations = [ + migrations.AlterField( + model_name='fotokegiatan', + name='foto', + field=pplbackend.custom_model_field.CompressedImageField(default=None, null=True, quality=85, upload_to='kegiatan/'), + ), + ] diff --git a/informasi_fasilitas/models.py b/informasi_fasilitas/models.py index 7745493..788a34b 100644 --- a/informasi_fasilitas/models.py +++ b/informasi_fasilitas/models.py @@ -1,7 +1,7 @@ from django.db import models from django.contrib.auth.models import User from multiselectfield import MultiSelectField - +from pplbackend.custom_model_field import CompressedImageField from django.utils import timezone import string @@ -116,4 +116,4 @@ class KomentarKegiatan(models.Model): class FotoKegiatan(models.Model): objects = models.Manager() kegiatan = models.ForeignKey(Kegiatan, related_name="images", on_delete=models.CASCADE) - foto = models.ImageField(upload_to="kegiatan/", null=True, default=None) + foto = CompressedImageField(upload_to="kegiatan/", null=True, default=None, quality=85) -- GitLab From 1d1ec8f3d14ad30499150f10c4d2c39feeffd622 Mon Sep 17 00:00:00 2001 From: Christopher Samuel Date: Tue, 25 May 2021 17:21:28 +0700 Subject: [PATCH 07/13] [GREEN] Implemented Agenda Kegiatan functions --- informasi_fasilitas/test_base.py | 21 ++++- informasi_fasilitas/test_views_kegiatan.py | 105 +++++++++++++++++++++ informasi_fasilitas/urls.py | 20 +++- informasi_fasilitas/views_kegiatan.py | 39 ++++++++ 4 files changed, 182 insertions(+), 3 deletions(-) diff --git a/informasi_fasilitas/test_base.py b/informasi_fasilitas/test_base.py index e481d3d..1108940 100644 --- a/informasi_fasilitas/test_base.py +++ b/informasi_fasilitas/test_base.py @@ -48,7 +48,6 @@ class InformasiFasilitasTest(TestCase): 'deskripsi': 'sangat membantu', } - # Waktu mungkin perlu disesuaikan lagi test dan implementasinya mock_kegiatan_test = { 'nama_kegiatan': 'mock kegiatan', 'penyelenggara': 'mock penyelenggara', @@ -59,6 +58,26 @@ class InformasiFasilitasTest(TestCase): 'links': "www.example.com;www.example.com" } + mock_kegiatan_time_order_test = { + 'nama_kegiatan': 'mock kegiatan', + 'penyelenggara': 'mock penyelenggara', + 'time_start':(datetime.now()+timedelta(days=2)).strftime("%Y-%m-%d %H:%M"), + 'time_end': (datetime.now()+timedelta(days=3)).strftime("%Y-%m-%d %H:%M"), + 'narahubung': 'sebuah narahubung', + 'deskripsi': 'sebuah deskripsi', + 'links': "www.example.com;www.example.com" + } + + mock_kegiatan_name_order_test = { + 'nama_kegiatan': 'aaaa mock kegiatan', + 'penyelenggara': 'mock penyelenggara', + 'time_start':(datetime.now()+timedelta(days=2)).strftime("%Y-%m-%d %H:%M"), + 'time_end': (datetime.now()+timedelta(days=3)).strftime("%Y-%m-%d %H:%M"), + 'narahubung': 'sebuah narahubung', + 'deskripsi': 'sebuah deskripsi', + 'links': "www.example.com;www.example.com" + } + mock_komentar_kegiatan_test = { 'deskripsi': 'sangat membantu' } diff --git a/informasi_fasilitas/test_views_kegiatan.py b/informasi_fasilitas/test_views_kegiatan.py index 76dcf03..5802e0d 100644 --- a/informasi_fasilitas/test_views_kegiatan.py +++ b/informasi_fasilitas/test_views_kegiatan.py @@ -33,6 +33,14 @@ class KegiatanRelatedViewTest(InformasiFasilitasViewTest): self.kwargs_search_kegiatan_fail = {'query': 'this shouldnt exist',} self.kwargs_kegiatan_id = {'kegiatan_id' : self.kegiatan.lokasi.place_id} + self.kwargs_list_kegiatan_in_order = { + 'start_index': 0, + 'query_limit': 10, + } + self.kwargs_list_kegiatan_in_order_fail = { + 'start_index': 10, + 'query_limit': 20, + } image_path1, image_path2 = ("test_file/test1.jpg", "test_file/test2.jpg") image1 = SimpleUploadedFile("test1.jpg", content=open(image_path1, 'rb').read(), @@ -60,6 +68,18 @@ class KegiatanRelatedViewTest(InformasiFasilitasViewTest): kwargs=self.kwargs_search_kegiatan_fail) self.list_foto_kegiatan_url = \ reverse('list-foto-kegiatan', kwargs=self.kwargs_get_foto_kegiatan) + self.list_kegiatan_by_latest_added_url = \ + reverse('list-kegiatan-by-latest-added', kwargs=self.kwargs_list_kegiatan_in_order) + self.list_kegiatan_by_name_url = \ + reverse('list-kegiatan-by-name', kwargs=self.kwargs_list_kegiatan_in_order) + self.list_kegiatan_by_time_url = \ + reverse('list-kegiatan-by-time', kwargs=self.kwargs_list_kegiatan_in_order) + self.list_kegiatan_by_latest_added_fail_url = \ + reverse('list-kegiatan-by-latest-added', kwargs=self.kwargs_list_kegiatan_in_order_fail) + self.list_kegiatan_by_name_fail_url = \ + reverse('list-kegiatan-by-name', kwargs=self.kwargs_list_kegiatan_in_order_fail) + self.list_kegiatan_by_time_fail_url = \ + reverse('list-kegiatan-by-time', kwargs=self.kwargs_list_kegiatan_in_order_fail) def tearDown(self): try: @@ -240,3 +260,88 @@ class KegiatanRelatedViewTest(InformasiFasilitasViewTest): def test_search_kegiatan_empty_result_or_fail(self): response = Client().get(self.search_kegiatan_fail_url) self.assertEqual(response.status_code, HTTPStatus.NOT_FOUND) + + def test_can_get_list_kegiatan_by_name_order(self): + Kegiatan.objects.all().delete() + response_params = self.mock_kegiatan_test.copy() + response = self.client.post(self.add_kegiatan_url, response_params) + self.assertEqual(response.status_code, HTTPStatus.CREATED) + content = json.loads(response.content.decode('utf-8')) + base_mock_id = str(content['id']) + + response_params = self.mock_kegiatan_name_order_test.copy() + response = self.client.post(self.add_kegiatan_url, response_params) + self.assertEqual(response.status_code, HTTPStatus.CREATED) + content = json.loads(response.content.decode('utf-8')) + ordered_name_mock_id = str(content['id']) + + response = self.client.get(self.list_kegiatan_by_name_url) + self.assertEqual(response.status_code, HTTPStatus.OK) + content = json.loads(response.content.decode('utf-8')) + + response_id_in_order = list(content.keys()) + + self.assertEqual(content[base_mock_id]['nama_kegiatan'], + self.kegiatan.nama_kegiatan) + self.assertEqual(content[ordered_name_mock_id]['nama_kegiatan'], + self.mock_kegiatan_name_order_test['nama_kegiatan']) + self.assertEqual(response_id_in_order[0], ordered_name_mock_id) + self.assertEqual(response_id_in_order[1], base_mock_id) + + def test_fail_get_list_kegiatan_by_name_order(self): + response = Client().get(self.list_kegiatan_by_name_fail_url) + self.assertEqual(response.status_code, HTTPStatus.NOT_FOUND) + + def test_can_get_list_kegiatan_by_time_order(self): + Kegiatan.objects.all().delete() + response_params = self.mock_kegiatan_test.copy() + response = self.client.post(self.add_kegiatan_url, response_params) + self.assertEqual(response.status_code, HTTPStatus.CREATED) + content = json.loads(response.content.decode('utf-8')) + base_mock_id = str(content['id']) + + response_params = self.mock_kegiatan_name_order_test.copy() + response = self.client.post(self.add_kegiatan_url, response_params) + self.assertEqual(response.status_code, HTTPStatus.CREATED) + content = json.loads(response.content.decode('utf-8')) + ordered_time_mock_id = str(content['id']) + + response = self.client.get(self.list_kegiatan_by_time_url) + self.assertEqual(response.status_code, HTTPStatus.OK) + content = json.loads(response.content.decode('utf-8')) + + response_id_in_order = list(content.keys()) + + self.assertEqual(content[base_mock_id]['time_start'], + self.kegiatan.time_start) + self.assertEqual(content[ordered_time_mock_id]['time_start'], + self.mock_kegiatan_time_order_test['time_start']) + self.assertEqual(response_id_in_order[0], base_mock_id) + self.assertEqual(response_id_in_order[1], ordered_time_mock_id) + + def test_fail_get_list_kegiatan_by_time_order(self): + response = Client().get(self.list_kegiatan_by_time_fail_url) + self.assertEqual(response.status_code, HTTPStatus.NOT_FOUND) + + def test_can_get_list_kegiatan_by_latest_added(self): + Kegiatan.objects.all().delete() + response_params = self.mock_kegiatan_test.copy() + response = self.client.post(self.add_kegiatan_url, response_params) + self.assertEqual(response.status_code, HTTPStatus.CREATED) + content = json.loads(response.content.decode('utf-8')) + + response_params = self.mock_kegiatan_test.copy() + response = self.client.post(self.add_kegiatan_url, response_params) + self.assertEqual(response.status_code, HTTPStatus.CREATED) + content = json.loads(response.content.decode('utf-8')) + + response = self.client.get(self.list_kegiatan_by_latest_added_url) + self.assertEqual(response.status_code, HTTPStatus.OK) + content = json.loads(response.content.decode('utf-8')) + + response_id_in_order = list(content.keys()) + self.assertGreater(int(response_id_in_order[0]), int(response_id_in_order[1])) + + def test_fail_get_list_kegiatan_by_latest_added(self): + response = Client().get(self.list_kegiatan_by_latest_added_fail_url) + self.assertEqual(response.status_code, HTTPStatus.NOT_FOUND) diff --git a/informasi_fasilitas/urls.py b/informasi_fasilitas/urls.py index 6442517..05f598d 100644 --- a/informasi_fasilitas/urls.py +++ b/informasi_fasilitas/urls.py @@ -50,6 +50,24 @@ urlpatterns = [ path('lokasi/list-foto-kegiatan//', views_kegiatan.list_foto_kegiatan, name='list-foto-kegiatan'), + + path('lokasi/list-kegiatan-by-latest-added//', + views_kegiatan.list_kegiatan_by_latest_added, name='list-kegiatan-by-latest-added'), + + path('lokasi/list-kegiatan-by-latest-added/', + views_kegiatan.list_kegiatan_by_latest_added, name='list-kegiatan-by-latest-added'), + + path('lokasi/list-kegiatan-by-name//', + views_kegiatan.list_kegiatan_by_name, name='list-kegiatan-by-name'), + + path('lokasi/list-kegiatan-by-name/', + views_kegiatan.list_kegiatan_by_name, name='list-kegiatan-by-name'), + + path('lokasi/list-kegiatan-by-time//', + views_kegiatan.list_kegiatan_by_time, name='list-kegiatan-by-time'), + + path('lokasi/list-kegiatan-by-time/', + views_kegiatan.list_kegiatan_by_time, name='list-kegiatan-by-time'), path('lokasi/get-komentar-kegiatan///', views_komentar_kegiatan.get_komentar_kegiatan, name='get-komentar-kegiatan'), @@ -62,6 +80,4 @@ urlpatterns = [ path('lokasi/delete-komentar-kegiatan///', views_komentar_kegiatan.delete_komentar_kegiatan, name='delete-komentar-kegiatan'), - - ] diff --git a/informasi_fasilitas/views_kegiatan.py b/informasi_fasilitas/views_kegiatan.py index 3228f1a..0c8094c 100644 --- a/informasi_fasilitas/views_kegiatan.py +++ b/informasi_fasilitas/views_kegiatan.py @@ -109,6 +109,45 @@ def search_kegiatan(request, query): new_dict = {item['id']: _clean_json_kegiatan(dict(item)) for item in serializer.data} return JsonResponse(new_dict, status=HTTPStatus.OK) +@api_view(['GET']) +@authentication_classes([]) +@permission_classes([]) +def list_kegiatan_by_name(request, start_index, query_limit=9): + time_now = timezone.now() + queryset = Kegiatan.objects.filter(time_end__gte=time_now).order_by('nama_kegiatan') + queryset = queryset[start_index:start_index+query_limit] + if not queryset.exists(): + raise NotFound(detail="No Kegiatan available") + serializer = KegiatanSerializer(queryset, many=True) + new_dict = {item['id']: _clean_json_kegiatan(dict(item)) for item in serializer.data} + return JsonResponse(new_dict, status=HTTPStatus.OK) + +@api_view(['GET']) +@authentication_classes([]) +@permission_classes([]) +def list_kegiatan_by_time(request, start_index, query_limit=9): + time_now = timezone.now() + queryset = Kegiatan.objects.filter(time_end__gte=time_now).order_by('time_start') + queryset = queryset[start_index:start_index+query_limit] + if not queryset.exists(): + raise NotFound(detail="No Kegiatan available") + serializer = KegiatanSerializer(queryset, many=True) + new_dict = {item['id']: _clean_json_kegiatan(dict(item)) for item in serializer.data} + return JsonResponse(new_dict, status=HTTPStatus.OK) + +@api_view(['GET']) +@authentication_classes([]) +@permission_classes([]) +def list_kegiatan_by_latest_added(request, start_index, query_limit=9): + time_now = timezone.now() + queryset = Kegiatan.objects.filter(time_end__gte=time_now).order_by('-id') + queryset = queryset[start_index:start_index+query_limit] + if not queryset.exists(): + raise NotFound(detail="No Kegiatan available") + serializer = KegiatanSerializer(queryset, many=True) + new_dict = {item['id']: _clean_json_kegiatan(dict(item)) for item in serializer.data} + return JsonResponse(new_dict, status=HTTPStatus.OK) + def _clean_json_kegiatan(kegiatan): kegiatan.pop("user") kegiatan.pop("lokasi") -- GitLab From a6d30076218f0158039c02bc650c3870b684ec22 Mon Sep 17 00:00:00 2001 From: Muhammad Rafif Elfazri Date: Fri, 28 May 2021 10:46:40 +0700 Subject: [PATCH 08/13] [RED] Test for Implement TimeZone in kegiatan --- informasi_fasilitas/test_base.py | 87 ++++++++++-- informasi_fasilitas/test_views_kegiatan.py | 153 ++++++++++++++------- 2 files changed, 181 insertions(+), 59 deletions(-) diff --git a/informasi_fasilitas/test_base.py b/informasi_fasilitas/test_base.py index 1108940..ea50b6a 100644 --- a/informasi_fasilitas/test_base.py +++ b/informasi_fasilitas/test_base.py @@ -1,10 +1,11 @@ import json import tempfile - +import pytz from datetime import datetime, timedelta from django.test import TestCase, Client from django.contrib.auth.models import User from django.urls import reverse, path, include +from django.utils import timezone from django.core.files.uploadedfile import SimpleUploadedFile from django.core.files.base import ContentFile @@ -21,6 +22,11 @@ from .models import ( ) from pplbackend.utils import get_client_login_with_user +TIMEZONE_INDONESIA = { + "WIB": pytz.timezone("Asia/Jakarta"), + "WITA": pytz.timezone("Asia/Makassar"), + "WIT": pytz.timezone("Asia/Jayapura"), +} class InformasiFasilitasTest(TestCase): put_content_type = "application/x-www-form-urlencoded" @@ -48,34 +54,89 @@ class InformasiFasilitasTest(TestCase): 'deskripsi': 'sangat membantu', } + time_start = timezone.now() + timedelta(days=1) + time_end = timezone.now() + timedelta(days=2) + + time_start_zone = { + "WIB": time_start.astimezone(TIMEZONE_INDONESIA["WIB"]).strftime("%Y-%m-%dT%H:%M%z"), + "WITA": time_start.astimezone(TIMEZONE_INDONESIA["WITA"]).strftime("%Y-%m-%dT%H:%M%z"), + "WIT": time_start.astimezone(TIMEZONE_INDONESIA["WIT"]).strftime("%Y-%m-%dT%H:%M%z"), + } + + time_end_zone = { + "WIB": time_end.astimezone(TIMEZONE_INDONESIA["WIB"]).strftime("%Y-%m-%dT%H:%M%z"), + "WITA": time_end.astimezone(TIMEZONE_INDONESIA["WITA"]).strftime("%Y-%m-%dT%H:%M%z"), + "WIT": time_end.astimezone(TIMEZONE_INDONESIA["WIT"]).strftime("%Y-%m-%dT%H:%M%z"), + } + + # mock_kegiatan_test = { + # 'nama_kegiatan': 'mock kegiatan', + # 'penyelenggara': 'mock penyelenggara', + # 'time_start':(datetime.now()+timedelta(days=1)).strftime("%Y-%m-%d %H:%M"), + # 'time_end': (datetime.now()+timedelta(days=2)).strftime("%Y-%m-%d %H:%M"), + # 'narahubung': 'sebuah narahubung', + # 'deskripsi': 'sebuah deskripsi', + # 'links': "www.example.com;www.example.com" + # } + mock_kegiatan_test = { 'nama_kegiatan': 'mock kegiatan', 'penyelenggara': 'mock penyelenggara', - 'time_start':(datetime.now()+timedelta(days=1)).strftime("%Y-%m-%d %H:%M"), - 'time_end': (datetime.now()+timedelta(days=2)).strftime("%Y-%m-%d %H:%M"), - 'narahubung': 'sebuah narahubung', + "zona_waktu": "WIB", + 'time_start': time_start_zone["WIB"], + 'time_end': time_end_zone["WIB"], + 'nama_kontak': 'sebuah narahubung', + 'nomor_kontak': "1" * 15, + 'deskripsi': 'sebuah deskripsi', + 'links': "www.example.com" + } + + mock_kegiatan_wita_test = { + 'nama_kegiatan': 'mock kegiatan', + 'penyelenggara': 'mock penyelenggara', + "zona_waktu": "WITA", + 'time_start': time_start_zone["WITA"], + 'time_end': time_end_zone["WITA"], + 'nama_kontak': 'sebuah narahubung', + 'nomor_kontak': "1" * 15, + 'deskripsi': 'sebuah deskripsi', + 'links': "www.example.com" + } + + mock_kegiatan_wit_test = { + 'nama_kegiatan': 'mock kegiatan', + 'penyelenggara': 'mock penyelenggara', + "zona_waktu": "WIT", + 'time_start': time_start_zone["WIT"], + 'time_end': time_end_zone["WIT"], + 'nama_kontak': 'sebuah narahubung', + 'nomor_kontak': "1" * 15, 'deskripsi': 'sebuah deskripsi', - 'links': "www.example.com;www.example.com" + 'links': "www.example.com" } mock_kegiatan_time_order_test = { 'nama_kegiatan': 'mock kegiatan', 'penyelenggara': 'mock penyelenggara', - 'time_start':(datetime.now()+timedelta(days=2)).strftime("%Y-%m-%d %H:%M"), - 'time_end': (datetime.now()+timedelta(days=3)).strftime("%Y-%m-%d %H:%M"), - 'narahubung': 'sebuah narahubung', + "zona_waktu": "WIB", + 'time_start': time_start_zone["WIB"], + 'time_end': time_end_zone["WIB"], + 'nama_kontak': 'sebuah narahubung', + 'nomor_kontak': "1" * 15, 'deskripsi': 'sebuah deskripsi', - 'links': "www.example.com;www.example.com" + 'links': "www.example.com" } mock_kegiatan_name_order_test = { 'nama_kegiatan': 'aaaa mock kegiatan', 'penyelenggara': 'mock penyelenggara', - 'time_start':(datetime.now()+timedelta(days=2)).strftime("%Y-%m-%d %H:%M"), - 'time_end': (datetime.now()+timedelta(days=3)).strftime("%Y-%m-%d %H:%M"), - 'narahubung': 'sebuah narahubung', + "zona_waktu": "WIB", + 'time_start': time_start_zone["WIB"], + 'time_end': time_end_zone["WIB"], + 'nama_kontak': 'sebuah narahubung', + 'nomor_kontak': "1" * 15, 'deskripsi': 'sebuah deskripsi', - 'links': "www.example.com;www.example.com" + 'links': "www.example.com" } mock_komentar_kegiatan_test = { diff --git a/informasi_fasilitas/test_views_kegiatan.py b/informasi_fasilitas/test_views_kegiatan.py index d6f0daa..0004768 100644 --- a/informasi_fasilitas/test_views_kegiatan.py +++ b/informasi_fasilitas/test_views_kegiatan.py @@ -14,8 +14,8 @@ from .test_base import InformasiFasilitasViewTest from .models import Lokasi, Kegiatan, FotoKegiatan from pplbackend.utils import response_decode -class KegiatanRelatedViewTest(InformasiFasilitasViewTest): +class KegiatanRelatedViewTest(InformasiFasilitasViewTest): def setUp(self): super().setUp() self.media_root = os.path.join(settings.BASE_DIR, 'test_file/media') @@ -29,10 +29,10 @@ class KegiatanRelatedViewTest(InformasiFasilitasViewTest): 'place_id': self.lokasi.place_id, 'kegiatan_id': self.kegiatan.id, } - self.kwargs_search_kegiatan = {'query': 'mock',} - self.kwargs_search_kegiatan_fail = {'query': 'this shouldnt exist',} + self.kwargs_search_kegiatan = {'query': 'mock', } + self.kwargs_search_kegiatan_fail = {'query': 'this shouldnt exist', } - self.kwargs_kegiatan_id = {'kegiatan_id' : self.kegiatan.lokasi.place_id} + self.kwargs_kegiatan_id = {'kegiatan_id': self.kegiatan.lokasi.place_id} self.kwargs_list_kegiatan_in_order = { 'start_index': 0, 'query_limit': 10, @@ -62,11 +62,11 @@ class KegiatanRelatedViewTest(InformasiFasilitasViewTest): self.add_kegiatan_url = \ reverse('add-kegiatan', kwargs=self.kwargs_place_id) self.update_kegiatan_url = reverse('update-kegiatan', - kwargs=self.kwargs_add_or_update_kegiatan) + kwargs=self.kwargs_add_or_update_kegiatan) self.search_kegiatan_url = reverse('search-kegiatan', - kwargs=self.kwargs_search_kegiatan) + kwargs=self.kwargs_search_kegiatan) self.search_kegiatan_fail_url = reverse('search-kegiatan', - kwargs=self.kwargs_search_kegiatan_fail) + kwargs=self.kwargs_search_kegiatan_fail) self.list_foto_kegiatan_url = \ reverse('list-foto-kegiatan', kwargs=self.kwargs_get_foto_kegiatan) self.list_kegiatan_by_latest_added_url = \ @@ -88,7 +88,6 @@ class KegiatanRelatedViewTest(InformasiFasilitasViewTest): except OSError: pass - @override_settings(MEDIA_ROOT=("test_file" + '/media')) def test_can_add_kegiatan(self): Kegiatan.objects.all().delete() @@ -135,11 +134,13 @@ class KegiatanRelatedViewTest(InformasiFasilitasViewTest): 'id': self.kegiatan.id, 'place_id': self.kegiatan.lokasi.place_id, 'creator': self.kegiatan.user.last_name, - 'nama_kegiatan' : self.kegiatan.nama_kegiatan, + 'nama_kegiatan': self.kegiatan.nama_kegiatan, 'penyelenggara': self.kegiatan.penyelenggara, 'deskripsi': self.kegiatan.deskripsi, 'links': self.kegiatan.links, - 'narahubung': self.kegiatan.narahubung, + 'nama_kontak': self.kegiatan.nama_kontak, + 'nomor_kontak': self.kegiatan.nomor_kontak, + 'zona_waktu': self.kegiatan.zona_waktu, 'time_start': self.kegiatan.time_start, 'time_end': self.kegiatan.time_end, }, @@ -151,34 +152,94 @@ class KegiatanRelatedViewTest(InformasiFasilitasViewTest): self.assertEqual(response.status_code, HTTPStatus.OK) content = json.loads(response.content.decode('utf-8')) expected_json = { - 'id': self.kegiatan.id, - 'place_id': self.kegiatan.lokasi.place_id, - 'creator': self.kegiatan.user.last_name, - 'nama_kegiatan' : self.kegiatan.nama_kegiatan, - 'penyelenggara': self.kegiatan.penyelenggara, - 'deskripsi': self.kegiatan.deskripsi, - 'links': self.kegiatan.links, - 'narahubung': self.kegiatan.narahubung, - 'time_start': self.kegiatan.time_start, - 'time_end': self.kegiatan.time_end, + 'id': self.kegiatan.id, + 'place_id': self.kegiatan.lokasi.place_id, + 'creator': self.kegiatan.user.last_name, + 'nama_kegiatan': self.kegiatan.nama_kegiatan, + 'penyelenggara': self.kegiatan.penyelenggara, + 'deskripsi': self.kegiatan.deskripsi, + 'links': self.kegiatan.links, + 'nama_kontak': self.kegiatan.nama_kontak, + 'nomor_kontak': self.kegiatan.nomor_kontak, + 'zona_waktu': self.kegiatan.zona_waktu, + 'time_start': self.kegiatan.time_start, + 'time_end': self.kegiatan.time_end, } self.assertEqual(content, expected_json) + def test_can_get_detail_kegiatan_wita(self): + Kegiatan.objects.all().delete() + kegiatan_wita = self.create_kegiatan_test(kegiatan_dict=self.mock_kegiatan_wita_test, + user=self.user, lokasi=self.lokasi) + get_detail_kegiatan_url = \ + reverse('detail-kegiatan', kwargs={ + 'place_id': kegiatan_wita.lokasi.place_id, + 'id': kegiatan_wita.id, + }) + response = Client().get(get_detail_kegiatan_url) + self.assertEqual(response.status_code, HTTPStatus.OK) + content = json.loads(response.content.decode('utf-8')) + expected_json = { + 'id': kegiatan_wita.id, + 'place_id': kegiatan_wita.lokasi.place_id, + 'creator': kegiatan_wita.user.last_name, + 'nama_kegiatan': kegiatan_wita.nama_kegiatan, + 'penyelenggara': kegiatan_wita.penyelenggara, + 'deskripsi': kegiatan_wita.deskripsi, + 'links': kegiatan_wita.links, + 'nama_kontak': kegiatan_wita.nama_kontak, + 'nomor_kontak': kegiatan_wita.nomor_kontak, + 'zona_waktu': kegiatan_wita.zona_waktu, + 'time_start': kegiatan_wita.time_start, + 'time_end': kegiatan_wita.time_end, + } + self.assertDictEqual(content, expected_json) + + def test_can_get_detail_kegiatan_wit(self): + Kegiatan.objects.all().delete() + kegiatan_wit = self.create_kegiatan_test(kegiatan_dict=self.mock_kegiatan_wit_test, + user=self.user, lokasi=self.lokasi) + get_detail_kegiatan_url = \ + reverse('detail-kegiatan', kwargs={ + 'place_id': kegiatan_wit.lokasi.place_id, + 'id': kegiatan_wit.id, + }) + response = Client().get(get_detail_kegiatan_url) + self.assertEqual(response.status_code, HTTPStatus.OK) + content = json.loads(response.content.decode('utf-8')) + expected_json = { + 'id': kegiatan_wit.id, + 'place_id': kegiatan_wit.lokasi.place_id, + 'creator': kegiatan_wit.user.last_name, + 'nama_kegiatan': kegiatan_wit.nama_kegiatan, + 'penyelenggara': kegiatan_wit.penyelenggara, + 'deskripsi': kegiatan_wit.deskripsi, + 'links': kegiatan_wit.links, + 'nama_kontak': kegiatan_wit.nama_kontak, + 'nomor_kontak': kegiatan_wit.nomor_kontak, + 'zona_waktu': kegiatan_wit.zona_waktu, + 'time_start': kegiatan_wit.time_start, + 'time_end': kegiatan_wit.time_end, + } + self.assertDictEqual(content, expected_json) + def test_can_get_nearest_kegiatan(self): response = Client().get(self.get_nearest_kegiatan_url) self.assertEqual(response.status_code, HTTPStatus.OK) content = json.loads(response.content.decode('utf-8')) expected_json = { - 'id': self.kegiatan.id, - 'place_id': self.kegiatan.lokasi.place_id, - 'creator': self.kegiatan.user.last_name, - 'nama_kegiatan' : self.kegiatan.nama_kegiatan, - 'penyelenggara': self.kegiatan.penyelenggara, - 'deskripsi': self.kegiatan.deskripsi, - 'links': self.kegiatan.links, - 'narahubung': self.kegiatan.narahubung, - 'time_start': self.kegiatan.time_start, - 'time_end': self.kegiatan.time_end, + 'id': self.kegiatan.id, + 'place_id': self.kegiatan.lokasi.place_id, + 'creator': self.kegiatan.user.last_name, + 'nama_kegiatan': self.kegiatan.nama_kegiatan, + 'penyelenggara': self.kegiatan.penyelenggara, + 'deskripsi': self.kegiatan.deskripsi, + 'links': self.kegiatan.links, + 'nama_kontak': self.kegiatan.nama_kontak, + 'nomor_kontak': self.kegiatan.nomor_kontak, + 'zona_waktu': self.kegiatan.zona_waktu, + 'time_start': self.kegiatan.time_start, + 'time_end': self.kegiatan.time_end, } self.assertEqual(content, expected_json) @@ -212,7 +273,6 @@ class KegiatanRelatedViewTest(InformasiFasilitasViewTest): self.assertEqual(count, 1) self.assertEqual(count_foto, 4) - def test_update_kegiatan_lokasi_not_exist(self): Kegiatan.objects.all().delete() url = reverse('update-kegiatan', kwargs={ @@ -227,17 +287,16 @@ class KegiatanRelatedViewTest(InformasiFasilitasViewTest): response = self.client.put(url, data=send_data) self.assertEqual(response.status_code, HTTPStatus.NOT_FOUND) - def test_can_get_list_foto_kegiatan(self): response = Client().get(self.list_foto_kegiatan_url) data = response.json() self.assertEqual(len(data), 2) counter = 1 for _, item in data.items(): - self.assertIn("test"+str(counter), item["foto"]) + self.assertIn("test" + str(counter), item["foto"]) self.assertEqual(item["kegiatan"], self.kegiatan.id) counter += 1 - + def test_can_search_kegiatan(self): response = Client().get(self.search_kegiatan_url) self.assertEqual(response.status_code, HTTPStatus.OK) @@ -247,21 +306,23 @@ class KegiatanRelatedViewTest(InformasiFasilitasViewTest): 'id': self.kegiatan.id, 'place_id': self.kegiatan.lokasi.place_id, 'creator': self.kegiatan.user.last_name, - 'nama_kegiatan' : self.kegiatan.nama_kegiatan, + 'nama_kegiatan': self.kegiatan.nama_kegiatan, 'penyelenggara': self.kegiatan.penyelenggara, 'deskripsi': self.kegiatan.deskripsi, 'links': self.kegiatan.links, - 'narahubung': self.kegiatan.narahubung, + 'nama_kontak': self.kegiatan.nama_kontak, + 'nomor_kontak': self.kegiatan.nomor_kontak, + 'zona_waktu': self.kegiatan.zona_waktu, 'time_start': self.kegiatan.time_start, 'time_end': self.kegiatan.time_end, }, } self.assertEqual(content, expected_json) - + def test_search_kegiatan_empty_result_or_fail(self): response = Client().get(self.search_kegiatan_fail_url) self.assertEqual(response.status_code, HTTPStatus.NOT_FOUND) - + def test_can_get_list_kegiatan_by_name_order(self): Kegiatan.objects.all().delete() response_params = self.mock_kegiatan_test.copy() @@ -283,12 +344,12 @@ class KegiatanRelatedViewTest(InformasiFasilitasViewTest): response_id_in_order = list(content.keys()) self.assertEqual(content[base_mock_id]['nama_kegiatan'], - self.kegiatan.nama_kegiatan) + self.kegiatan.nama_kegiatan) self.assertEqual(content[ordered_name_mock_id]['nama_kegiatan'], - self.mock_kegiatan_name_order_test['nama_kegiatan']) + self.mock_kegiatan_name_order_test['nama_kegiatan']) self.assertEqual(response_id_in_order[0], ordered_name_mock_id) self.assertEqual(response_id_in_order[1], base_mock_id) - + def test_fail_get_list_kegiatan_by_name_order(self): response = Client().get(self.list_kegiatan_by_name_fail_url) self.assertEqual(response.status_code, HTTPStatus.NOT_FOUND) @@ -314,16 +375,16 @@ class KegiatanRelatedViewTest(InformasiFasilitasViewTest): response_id_in_order = list(content.keys()) self.assertEqual(content[base_mock_id]['time_start'], - self.kegiatan.time_start) + self.kegiatan.time_start) self.assertEqual(content[ordered_time_mock_id]['time_start'], - self.mock_kegiatan_time_order_test['time_start']) + self.mock_kegiatan_time_order_test['time_start']) self.assertEqual(response_id_in_order[0], base_mock_id) self.assertEqual(response_id_in_order[1], ordered_time_mock_id) - + def test_fail_get_list_kegiatan_by_time_order(self): response = Client().get(self.list_kegiatan_by_time_fail_url) self.assertEqual(response.status_code, HTTPStatus.NOT_FOUND) - + def test_can_get_list_kegiatan_by_latest_added(self): Kegiatan.objects.all().delete() response_params = self.mock_kegiatan_test.copy() @@ -342,7 +403,7 @@ class KegiatanRelatedViewTest(InformasiFasilitasViewTest): response_id_in_order = list(content.keys()) self.assertGreater(int(response_id_in_order[0]), int(response_id_in_order[1])) - + def test_fail_get_list_kegiatan_by_latest_added(self): response = Client().get(self.list_kegiatan_by_latest_added_fail_url) self.assertEqual(response.status_code, HTTPStatus.NOT_FOUND) -- GitLab From 30b0211233fcd0281df12c732fb97c7455d37f8d Mon Sep 17 00:00:00 2001 From: Muhammad Rafif Elfazri Date: Fri, 28 May 2021 10:48:25 +0700 Subject: [PATCH 09/13] [CHORES] Merge Registrasi migrations --- registrasi/migrations/0008_merge_20210527_2250.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 registrasi/migrations/0008_merge_20210527_2250.py diff --git a/registrasi/migrations/0008_merge_20210527_2250.py b/registrasi/migrations/0008_merge_20210527_2250.py new file mode 100644 index 0000000..9730e9c --- /dev/null +++ b/registrasi/migrations/0008_merge_20210527_2250.py @@ -0,0 +1,14 @@ +# Generated by Django 3.1.7 on 2021-05-27 22:50 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('registrasi', '0007_merge_20210517_0447'), + ('registrasi', '0006_merge_20210519_1213'), + ] + + operations = [ + ] -- GitLab From 6c41b7f0d44164851c7dce873cbac992caf4bc37 Mon Sep 17 00:00:00 2001 From: Muhammad Rafif Elfazri Date: Fri, 28 May 2021 10:50:07 +0700 Subject: [PATCH 10/13] [CHORES] delete narahubung and add fields nomor_kontak, nama_kontak, zona_waktu at Kegiatan models --- .../migrations/0019_auto_20210527_2251.py | 32 +++++++++++++++++++ informasi_fasilitas/models.py | 10 +++++- 2 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 informasi_fasilitas/migrations/0019_auto_20210527_2251.py diff --git a/informasi_fasilitas/migrations/0019_auto_20210527_2251.py b/informasi_fasilitas/migrations/0019_auto_20210527_2251.py new file mode 100644 index 0000000..b325187 --- /dev/null +++ b/informasi_fasilitas/migrations/0019_auto_20210527_2251.py @@ -0,0 +1,32 @@ +# Generated by Django 3.1.7 on 2021-05-27 22:51 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('informasi_fasilitas', '0018_auto_20210520_0451'), + ] + + operations = [ + migrations.RemoveField( + model_name='kegiatan', + name='narahubung', + ), + migrations.AddField( + model_name='kegiatan', + name='nama_kontak', + field=models.CharField(blank=True, max_length=50, null=True), + ), + migrations.AddField( + model_name='kegiatan', + name='nomor_kontak', + field=models.CharField(default='111111111111111', max_length=15), + ), + migrations.AddField( + model_name='kegiatan', + name='zona_waktu', + field=models.CharField(choices=[('WIB', 'Disabilitas Fisik'), ('WITA', 'Disabilitas Mental'), ('WIT', 'Disabilitas Intelektual')], default='WIB', max_length=4), + ), + ] diff --git a/informasi_fasilitas/models.py b/informasi_fasilitas/models.py index 9fc2c04..e313fd0 100644 --- a/informasi_fasilitas/models.py +++ b/informasi_fasilitas/models.py @@ -45,6 +45,12 @@ JENIS_DISABILITAS = ( ('DS', 'Disabilitas Sensorik'), ) +TIMEZONE_INDONESIA = ( + ('WIB', 'Disabilitas Fisik'), + ('WITA', 'Disabilitas Mental'), + ('WIT', 'Disabilitas Intelektual'), +) + def _default_lokasi_place_id(): return \ ''.join([random.choice(string.ascii_letters) for _ in range(40)]) @@ -80,9 +86,11 @@ class Kegiatan(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE) nama_kegiatan = models.CharField(max_length=50) penyelenggara = models.CharField(max_length=50) - narahubung = models.TextField(null=True) + nama_kontak = models.CharField(max_length=50, null=True, blank=True) + nomor_kontak = models.CharField(max_length=15, unique=False, null=False, default="1"*15) deskripsi = models.TextField(null=False) links = models.TextField(null=True, blank=True) + zona_waktu = models.CharField(choices=TIMEZONE_INDONESIA, null=False, max_length=4, default="WIB") time_start = models.DateTimeField(blank=False, null=False, default=timezone.now) time_end = models.DateTimeField(blank=True, null=True) -- GitLab From 54f9eecb1c12e8bcb7a04118f82671255b5bd356 Mon Sep 17 00:00:00 2001 From: Muhammad Rafif Elfazri Date: Fri, 28 May 2021 10:54:06 +0700 Subject: [PATCH 11/13] [CHORES] Change DateTime format for rest framework serializer --- pplbackend/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pplbackend/settings.py b/pplbackend/settings.py index cb98bad..47d3d2c 100644 --- a/pplbackend/settings.py +++ b/pplbackend/settings.py @@ -202,7 +202,7 @@ SOCIALACCOUNT_PROVIDERS = { LOGIN_REDIRECT_URL = '/' REST_FRAMEWORK = { - 'DATETIME_FORMAT': "%Y-%m-%d %H:%M", + 'DATETIME_FORMAT': "%Y-%m-%dT%H:%M%z", 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', 'PAGE_SIZE': 10, 'DEFAULT_AUTHENTICATION_CLASSES': [ -- GitLab From 267176eb0e122a6827634cb4c5e8decc71c42f87 Mon Sep 17 00:00:00 2001 From: Muhammad Rafif Elfazri Date: Fri, 28 May 2021 10:55:51 +0700 Subject: [PATCH 12/13] [GREEN] Implement timezone in Kegiatan model related api --- informasi_fasilitas/serializers.py | 41 +++++++++++++++++++++++++-- informasi_fasilitas/views_kegiatan.py | 18 ++++++------ 2 files changed, 48 insertions(+), 11 deletions(-) diff --git a/informasi_fasilitas/serializers.py b/informasi_fasilitas/serializers.py index 8b034ae..91e5219 100644 --- a/informasi_fasilitas/serializers.py +++ b/informasi_fasilitas/serializers.py @@ -1,6 +1,21 @@ from rest_framework import serializers from .models import Lokasi, Kegiatan, KomentarKegiatan, FotoKegiatan +import pytz + +TIMEZONE_INDONESIA = { + "WIB": pytz.timezone("Asia/Jakarta"), + "WITA": pytz.timezone("Asia/Makassar"), + "WIT": pytz.timezone("Asia/Jayapura"), +} + + +def time_converter(time, zona_waktu): + if zona_waktu in TIMEZONE_INDONESIA: + time = time.astimezone(TIMEZONE_INDONESIA[zona_waktu]) + else: + time = time.astimezone("Asia/Jakarta") + return time class LokasiSerializer(serializers.ModelSerializer): @@ -37,16 +52,38 @@ class KegiatanSerializer(serializers.ModelSerializer): model = Kegiatan fields = ('id', 'place_id', 'creator', 'lokasi', 'user', 'nama_kegiatan', 'penyelenggara', 'deskripsi', - "links","narahubung", 'time_start', 'time_end') + "links", "nomor_kontak", "nama_kontak", + "zona_waktu", 'time_start', 'time_end') + extra_kwargs = { 'nama_kegiatan': {'required': True}, 'penyelenggara': {'required': True}, 'deskripsi': {'required': True}, - 'narahubung': {"required": True}, + 'nomor_kontak': {"required": True}, + 'zona_waktu': {"required": True}, 'time_start': {'required': True}, } +class KegiatanSerializerRead(KegiatanSerializer): + time_start = serializers.SerializerMethodField() + time_end = serializers.SerializerMethodField() + + def get_time_start(self, obj): + time = obj.time_start + zona_waktu = obj.zona_waktu + time = time_converter(time, zona_waktu) + return time.strftime('%Y-%m-%dT%H:%M%z') + + def get_time_end(self, obj): + time = obj.time_end + zona_waktu = obj.zona_waktu + if time is None: + return None + time = time_converter(time, zona_waktu) + return time.strftime('%Y-%m-%dT%H:%M%z') + + class KomentarKegiatanSerializer(serializers.ModelSerializer): kegiatan = serializers.IntegerField(source='kegiatan.id', read_only=True) creator = serializers.CharField(source='user.last_name', read_only=True) diff --git a/informasi_fasilitas/views_kegiatan.py b/informasi_fasilitas/views_kegiatan.py index 0c8094c..4a165b0 100644 --- a/informasi_fasilitas/views_kegiatan.py +++ b/informasi_fasilitas/views_kegiatan.py @@ -9,7 +9,7 @@ from rest_framework.views import APIView from rest_framework.exceptions import AuthenticationFailed, NotFound from .models import Kegiatan, Lokasi, FotoKegiatan from django.contrib.auth.models import User -from .serializers import KegiatanSerializer, FotoKegiatanSerializer +from .serializers import KegiatanSerializer, KegiatanSerializerRead, FotoKegiatanSerializer @api_view(['GET']) @@ -17,7 +17,7 @@ from .serializers import KegiatanSerializer, FotoKegiatanSerializer @permission_classes([]) def list_kegiatan(request, place_id): queryset = Kegiatan.objects.filter(lokasi__place_id=place_id) - serializer = KegiatanSerializer(queryset, many=True) + serializer = KegiatanSerializerRead(queryset, many=True) data_response = serializer.data new_dict = {item['id']: _clean_json_kegiatan(dict(item)) for item in data_response} return JsonResponse(new_dict, status=HTTPStatus.OK) @@ -29,7 +29,7 @@ def list_kegiatan(request, place_id): def detail_kegiatan(request, place_id, id): try: queryset = Kegiatan.objects.get(lokasi__place_id=place_id, id=id) - serializer = KegiatanSerializer(queryset, many=False) + serializer = KegiatanSerializerRead(queryset, many=False) return JsonResponse(_clean_json_kegiatan(serializer.data), status=HTTPStatus.OK) except Kegiatan.DoesNotExist: raise NotFound(detail="Kegiatan doesn't exist") @@ -43,7 +43,7 @@ def nearest_kegiatan(request): queryset = Kegiatan.objects.filter(time_start__gte=time_now).order_by('time_start').first() if queryset is None: raise NotFound(detail="No Kegiatan available") - serializer = KegiatanSerializer(queryset, many=False) + serializer = KegiatanSerializerRead(queryset, many=False) return JsonResponse(_clean_json_kegiatan(serializer.data), status=HTTPStatus.OK) @api_view(['POST']) @@ -105,7 +105,7 @@ def search_kegiatan(request, query): queryset = query_by_nama | query_by_deskripsi | query_by_penyelenggara if not queryset.exists(): raise NotFound(detail="No Kegiatan available") - serializer = KegiatanSerializer(queryset, many=True) + serializer = KegiatanSerializerRead(queryset, many=True) new_dict = {item['id']: _clean_json_kegiatan(dict(item)) for item in serializer.data} return JsonResponse(new_dict, status=HTTPStatus.OK) @@ -118,7 +118,7 @@ def list_kegiatan_by_name(request, start_index, query_limit=9): queryset = queryset[start_index:start_index+query_limit] if not queryset.exists(): raise NotFound(detail="No Kegiatan available") - serializer = KegiatanSerializer(queryset, many=True) + serializer = KegiatanSerializerRead(queryset, many=True) new_dict = {item['id']: _clean_json_kegiatan(dict(item)) for item in serializer.data} return JsonResponse(new_dict, status=HTTPStatus.OK) @@ -131,7 +131,7 @@ def list_kegiatan_by_time(request, start_index, query_limit=9): queryset = queryset[start_index:start_index+query_limit] if not queryset.exists(): raise NotFound(detail="No Kegiatan available") - serializer = KegiatanSerializer(queryset, many=True) + serializer = KegiatanSerializerRead(queryset, many=True) new_dict = {item['id']: _clean_json_kegiatan(dict(item)) for item in serializer.data} return JsonResponse(new_dict, status=HTTPStatus.OK) @@ -144,7 +144,7 @@ def list_kegiatan_by_latest_added(request, start_index, query_limit=9): queryset = queryset[start_index:start_index+query_limit] if not queryset.exists(): raise NotFound(detail="No Kegiatan available") - serializer = KegiatanSerializer(queryset, many=True) + serializer = KegiatanSerializerRead(queryset, many=True) new_dict = {item['id']: _clean_json_kegiatan(dict(item)) for item in serializer.data} return JsonResponse(new_dict, status=HTTPStatus.OK) @@ -157,7 +157,7 @@ def _clean_json_kegiatan(kegiatan): def _add_foto_kegiatan(kegiatan, list_image): _create_list_kegiatan_foto(kegiatan, list_image) kegiatan.refresh_from_db() - return KegiatanSerializer(kegiatan) + return KegiatanSerializerRead(kegiatan) def _create_list_kegiatan_foto(kegiatan, list_image): -- GitLab From 94afe0cb64b84d18226a57dc8a450d36b589696c Mon Sep 17 00:00:00 2001 From: ariqbasyar Date: Fri, 28 May 2021 22:27:42 +0700 Subject: [PATCH 13/13] [CHORES] now profile photo is compressed with rate .55 --- informasi_fasilitas/test_views_kegiatan.py | 2 ++ .../migrations/0011_merge_20210528_1510.py | 14 ++++++++++++++ .../migrations/0012_auto_20210528_1519.py | 19 +++++++++++++++++++ registrasi/models.py | 4 +++- 4 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 registrasi/migrations/0011_merge_20210528_1510.py create mode 100644 registrasi/migrations/0012_auto_20210528_1519.py diff --git a/informasi_fasilitas/test_views_kegiatan.py b/informasi_fasilitas/test_views_kegiatan.py index 8eb9ba8..156a562 100644 --- a/informasi_fasilitas/test_views_kegiatan.py +++ b/informasi_fasilitas/test_views_kegiatan.py @@ -188,6 +188,7 @@ class KegiatanRelatedViewTest(InformasiFasilitasViewTest): 'id': kegiatan_wita.id, 'place_id': kegiatan_wita.lokasi.place_id, 'creator': kegiatan_wita.user.last_name, + 'creator_email': kegiatan_wita.user.email, 'nama_kegiatan': kegiatan_wita.nama_kegiatan, 'penyelenggara': kegiatan_wita.penyelenggara, 'deskripsi': kegiatan_wita.deskripsi, @@ -216,6 +217,7 @@ class KegiatanRelatedViewTest(InformasiFasilitasViewTest): 'id': kegiatan_wit.id, 'place_id': kegiatan_wit.lokasi.place_id, 'creator': kegiatan_wit.user.last_name, + 'creator_email': kegiatan_wit.user.email, 'nama_kegiatan': kegiatan_wit.nama_kegiatan, 'penyelenggara': kegiatan_wit.penyelenggara, 'deskripsi': kegiatan_wit.deskripsi, diff --git a/registrasi/migrations/0011_merge_20210528_1510.py b/registrasi/migrations/0011_merge_20210528_1510.py new file mode 100644 index 0000000..c5ab54b --- /dev/null +++ b/registrasi/migrations/0011_merge_20210528_1510.py @@ -0,0 +1,14 @@ +# Generated by Django 3.1.7 on 2021-05-28 15:10 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('registrasi', '0010_auto_20210527_0722'), + ('registrasi', '0008_merge_20210527_2250'), + ] + + operations = [ + ] diff --git a/registrasi/migrations/0012_auto_20210528_1519.py b/registrasi/migrations/0012_auto_20210528_1519.py new file mode 100644 index 0000000..5d27ec4 --- /dev/null +++ b/registrasi/migrations/0012_auto_20210528_1519.py @@ -0,0 +1,19 @@ +# Generated by Django 3.1.7 on 2021-05-28 15:19 + +from django.db import migrations +import pplbackend.custom_model_field + + +class Migration(migrations.Migration): + + dependencies = [ + ('registrasi', '0011_merge_20210528_1510'), + ] + + operations = [ + migrations.AlterField( + model_name='bisagouser', + name='foto', + field=pplbackend.custom_model_field.CompressedImageField(blank=True, default=None, null=True, quality=55, upload_to='user/', verbose_name='Foto'), + ), + ] diff --git a/registrasi/models.py b/registrasi/models.py index 29621bb..1be6d6e 100644 --- a/registrasi/models.py +++ b/registrasi/models.py @@ -3,6 +3,8 @@ from datetime import date from django.db import models from django.contrib.auth.models import User +from pplbackend.custom_model_field import CompressedImageField + class BisaGoUser(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="phone_number") @@ -13,7 +15,7 @@ class BisaGoUser(models.Model): organisasi_komunitas = models.CharField('Organisasi / Komunitas', max_length=64, null=True, default='-') pekerjaan = models.CharField('Pekerjaan', max_length=64, default='-') alamat = models.CharField('Alamat', max_length=255, default='-') - foto = models.ImageField('Foto', upload_to='user/', blank=True, default=None, null=True) + foto = CompressedImageField('Foto', upload_to='user/', blank=True, default=None, null=True, quality=55) seen = models.BooleanField('Seen', blank=True, default=True) class Meta: -- GitLab