From d5a0958fa3b17f6449e8ccff38a1fb847bd8e6b0 Mon Sep 17 00:00:00 2001 From: ariqbasyar Date: Mon, 15 Mar 2021 15:40:30 +0700 Subject: [PATCH 01/97] [REFACTOR] fix incompatible code for django 2.2 and python 3 removed .method() method call because now urlsafe_base64_encode returns string not bytes files: - new_rest_api/tests.py - new_rest_api/views.py --- new_rest_api/tests.py | 2 +- new_rest_api/views.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/new_rest_api/tests.py b/new_rest_api/tests.py index abba0d7..4f566fb 100644 --- a/new_rest_api/tests.py +++ b/new_rest_api/tests.py @@ -122,7 +122,7 @@ class UserTests(APITestCase): def test_activation_function(self): user = BisaGoUser.objects.get(phone_number='089892218567').user - uid = urlsafe_base64_encode(force_bytes(user.pk)).decode() + uid = urlsafe_base64_encode(force_bytes(user.pk)) token = account_activation_token.make_token(user) url = reverse('activate', kwargs={'uidb64' : uid, 'token' : token}) response = self.client.get(url) diff --git a/new_rest_api/views.py b/new_rest_api/views.py index d6f5687..3a19e78 100644 --- a/new_rest_api/views.py +++ b/new_rest_api/views.py @@ -84,7 +84,7 @@ def register_user(request): message = render_to_string('acc_active_email.html', { 'user' : user, 'domain' : request.get_host, - 'uid' : urlsafe_base64_encode(force_bytes(user.pk)).decode(), + 'uid' : urlsafe_base64_encode(force_bytes(user.pk)), 'token' : account_activation_token.make_token(user), }) mail = EmailMessage(mail_subject, message, to=[email]) -- GitLab From 7fbe7018f7a4ace4db0ae13317c7bdfa237cd8ab Mon Sep 17 00:00:00 2001 From: ariqbasyar Date: Tue, 16 Mar 2021 14:13:02 +0700 Subject: [PATCH 02/97] change header from "multipart/form-data" to "application/x-www-form-urlencoded" at tests sometimes django cant receive data using request.data with multipart/form-data as the content-type header --- informasi_fasilitas/test_views_fasilitas.py | 2 +- informasi_fasilitas/test_views_lokasi.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/informasi_fasilitas/test_views_fasilitas.py b/informasi_fasilitas/test_views_fasilitas.py index 6089b32..194192e 100644 --- a/informasi_fasilitas/test_views_fasilitas.py +++ b/informasi_fasilitas/test_views_fasilitas.py @@ -133,5 +133,5 @@ class FasilitasRelatedViewTest(InformasiFasilitasViewTest): 'rating': 3, 'tag': 'KR'} response = client.put(urls_put, data=send_data, - content_type="multipart/form-data") + content_type="application/x-www-form-urlencoded") self.assertEqual(response.status_code, HTTPStatus.ACCEPTED) diff --git a/informasi_fasilitas/test_views_lokasi.py b/informasi_fasilitas/test_views_lokasi.py index d9ffa05..d719a93 100644 --- a/informasi_fasilitas/test_views_lokasi.py +++ b/informasi_fasilitas/test_views_lokasi.py @@ -112,5 +112,5 @@ class LokasiRelatedViewTest(InformasiFasilitasViewTest): urls = reverse('update-lokasi', kwargs={'nama_lokasi': self.lokasi_test_1["name"]}) response = client.put(urls, data={"latitude": 100}, - content_type="multipart/form-data") + content_type="application/x-www-form-urlencoded") self.assertEqual(response.status_code, HTTPStatus.BAD_REQUEST) -- GitLab From b072b73e379ac9d1e1a0c3a3811ba0dfc6a5b0bd Mon Sep 17 00:00:00 2001 From: ariqbasyar Date: Wed, 17 Mar 2021 00:14:53 +0700 Subject: [PATCH 03/97] [REFACTOR] removed hardcoded variables, added python-dotenv - modified hardcoded variables DEBUG and ALLOWED_HOSTS, now using env vars - using python-dotenv for managing .env variables - change some variables from using os.environ.get to os.getenv (as the wrapper) --- pplbackend/settings.py | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/pplbackend/settings.py b/pplbackend/settings.py index 5dbd07e..f0d2d90 100644 --- a/pplbackend/settings.py +++ b/pplbackend/settings.py @@ -11,12 +11,13 @@ https://docs.djangoproject.com/en/3.0/ref/settings/ """ import os + from datetime import timedelta -import dj_database_url +from dotenv import load_dotenv # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) - +load_dotenv(os.path.join(BASE_DIR, '.env')) # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/3.0/howto/deployment/checklist/ @@ -26,11 +27,10 @@ SECRET_KEY = os.getenv("SECRET_KEY", 'akua') # SECURITY WARNING: don't run with debug turned on in production! -DEBUG = FALSE +DEBUG = os.getenv('DEBUG') == 'True' -ALLOWED_HOSTS = ['localhost', '127.0.0.1', '10.119.105.26', 'bisago.cs.ui.ac.id'] +ALLOWED_HOSTS = os.getenv('ALLOWED_HOSTS', 'localhost').strip(';').split(';') -#ALLOWED_HOSTS = ['*'] # Application definition INSTALLED_APPS = [ @@ -96,19 +96,21 @@ WSGI_APPLICATION = 'pplbackend.wsgi.application' DATABASES = { 'default': { - 'ENGINE': 'django.db.backends.postgresql_psycopg2', - 'NAME': os.environ.get('DB_NAME', ''), - 'USER': os.environ.get('DB_USER', ''), - 'PASSWORD': os.environ.get('DB_PASSWORD', ''), - 'HOST': os.environ.get('DB_HOST', 'localhost'), - 'PORT': os.environ.get('DB_PORT', '5432'), + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), } } -#if os.environ.get("DATABASE_URL"): -# DATABASES['default'] = dj_database_url.config() - - +# custom database host, overriding the default +if os.getenv('DB_HOST') is not None: + DATABASES['default'] = { + 'ENGINE': 'django.db.backends.postgresql_psycopg2', + 'NAME': os.getenv('DB_NAME'), + 'USER': os.getenv('DB_USER'), + 'PASSWORD': os.getenv('DB_PASSWORD'), + 'HOST': os.getenv('DB_HOST'), + 'PORT': os.getenv('DB_PORT'), + } # Password validation # https://docs.djangoproject.com/en/3.0/ref/settings/#auth-password-validators -- GitLab From 6e6c5ec61ee8b765c6e54eede3a583ca21ae2f0c Mon Sep 17 00:00:00 2001 From: ariqbasyar Date: Thu, 18 Mar 2021 22:34:11 +0700 Subject: [PATCH 04/97] [REFACTOR] change default databases to sqlite3 when running test using sys.argv to check if doing test or not and change databases to default --- pplbackend/settings.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/pplbackend/settings.py b/pplbackend/settings.py index f0d2d90..dc61e98 100644 --- a/pplbackend/settings.py +++ b/pplbackend/settings.py @@ -11,6 +11,7 @@ https://docs.djangoproject.com/en/3.0/ref/settings/ """ import os +import sys from datetime import timedelta from dotenv import load_dotenv @@ -94,11 +95,13 @@ WSGI_APPLICATION = 'pplbackend.wsgi.application' # Database # https://docs.djangoproject.com/en/3.0/ref/settings/#databases +DEFAULT_DATABASE = { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), +} + DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), - } + 'default': DEFAULT_DATABASE } # custom database host, overriding the default @@ -112,6 +115,10 @@ if os.getenv('DB_HOST') is not None: 'PORT': os.getenv('DB_PORT'), } +# use default database when running test +if 'test' in sys.argv: + DATABASES['default'] = DEFAULT_DATABASE + # Password validation # https://docs.djangoproject.com/en/3.0/ref/settings/#auth-password-validators -- GitLab From e659f0f0edb0b48b48e5bd19360fe7a6005195e3 Mon Sep 17 00:00:00 2001 From: ariqbasyar Date: Thu, 18 Mar 2021 23:38:42 +0700 Subject: [PATCH 05/97] [REFACTOR] remove unnecessary test, modified wrong tests --- informasi_fasilitas/test_base.py | 4 ++-- informasi_fasilitas/test_models.py | 2 +- informasi_fasilitas/test_views_lokasi.py | 20 +++++++------------- layanan_khusus/tests.py | 7 +++---- registrasi/tests.py | 2 +- 5 files changed, 14 insertions(+), 21 deletions(-) diff --git a/informasi_fasilitas/test_base.py b/informasi_fasilitas/test_base.py index e85ce9f..0f7e055 100644 --- a/informasi_fasilitas/test_base.py +++ b/informasi_fasilitas/test_base.py @@ -6,7 +6,7 @@ from django.urls import reverse, path, include from .models import Lokasi, Fasilitas, Komentar, KURSI_RODA, Likes, Dislikes -NOT_NULL_CONSTRAINT_FAILED_MESSAGE = 'null' +NOT_NULL_CONSTRAINT_FAILED_MESSAGE = 'NOT NULL constraint failed' REGISTER = '/api/register/' TOKEN_AUTH = '/api-token-auth/' @@ -37,7 +37,7 @@ class InformasiFasilitasTest(TestCase): 'phone_number': 000000000, 'password': "hahagotim"} - lokasi_test_1 = {'id': '1', + lokasi_test_1 = {'id': 1, 'name': 'Ma Homie', 'latitude': 0.1, 'longitude': 0.1, diff --git a/informasi_fasilitas/test_models.py b/informasi_fasilitas/test_models.py index f5c6603..7445c93 100644 --- a/informasi_fasilitas/test_models.py +++ b/informasi_fasilitas/test_models.py @@ -3,7 +3,7 @@ from django.db.utils import IntegrityError from .test_base import InformasiFasilitasTest from .models import Lokasi, Fasilitas, Komentar, Likes, Dislikes -NOT_NULL_CONSTRAINT_FAILED_MESSAGE = 'null' +NOT_NULL_CONSTRAINT_FAILED_MESSAGE = 'NOT NULL constraint failed' class InformasiFasilitasModelTest(InformasiFasilitasTest): diff --git a/informasi_fasilitas/test_views_lokasi.py b/informasi_fasilitas/test_views_lokasi.py index d719a93..cf525dd 100644 --- a/informasi_fasilitas/test_views_lokasi.py +++ b/informasi_fasilitas/test_views_lokasi.py @@ -28,7 +28,6 @@ class LokasiRelatedViewTest(InformasiFasilitasViewTest): response = Client().get(reverse('lokasi-list')) content = json.loads(response.content.decode('utf-8')) expected_first_entry = copy.deepcopy(self.lokasi_test_1) - expected_first_entry["id"] = 45 expected_first_entry["image"] = None expected_json = [expected_first_entry] self.assertEqual(content, expected_json) @@ -48,7 +47,7 @@ class LokasiRelatedViewTest(InformasiFasilitasViewTest): content = json.loads(response.content.decode('utf-8')) expected_json = copy.deepcopy(self.lokasi_test_1) expected_json["image"] = None - expected_json["id"] = 35 + # expected_json["id"] = 35 self.assertEqual(content, expected_json) def test_cannot_post_lokasi_details(self): @@ -68,7 +67,13 @@ class LokasiRelatedViewTest(InformasiFasilitasViewTest): def test_can_post_add_lokasi(self): client = self.client_user_token() + Lokasi.objects.all().delete() response = client.post(reverse('add-lokasi'), self.lokasi_test_1) + response_json = json.loads(response.content.decode("utf-8")) + expected_json = copy.deepcopy(self.lokasi_test_1) + expected_json['id'] = 2 + expected_json['image'] = None + self.assertEqual(response_json, expected_json) self.assertEqual(response.status_code, HTTPStatus.CREATED) def test_post_add_lokasi_missing_key(self): @@ -87,17 +92,6 @@ class LokasiRelatedViewTest(InformasiFasilitasViewTest): expected_json = {"name": ["This field is required."]} self.assertEqual(response_json, expected_json) - def test_can_post_add_lokasi_json(self): - client = self.client_user_token() - response = client.post(reverse('add-lokasi'), - self.lokasi_test_1) - response_json = json.loads(response.content.decode("utf-8")) - - expected_json = copy.deepcopy(self.lokasi_test_1) - expected_json["id"] = 40 - expected_json["image"] = None - self.assertEqual(response_json, expected_json) - def test_put_update_detail_lokasi_success(self): client = self.client_user_token() urls = reverse('update-lokasi', diff --git a/layanan_khusus/tests.py b/layanan_khusus/tests.py index 8a6038c..54d527a 100644 --- a/layanan_khusus/tests.py +++ b/layanan_khusus/tests.py @@ -7,7 +7,7 @@ from django.urls import path, include, reverse from .models import Sekolah, Penyandang, Komunitas from .serializers import SekolahSerializer, KomunitasSerializer -NOT_NULL_CONSTRAINT_FAILED_MESSAGE = 'null' +NOT_NULL_CONSTRAINT_FAILED_MESSAGE = 'NOT NULL constraint failed' ID = 'id' NAME = 'name' @@ -247,7 +247,6 @@ class LayananKhususViewsTest(TestCase): MOCK_PENYANDANG) content = json.loads(response.content.decode("utf-8")) expected_json = MOCK_PENYANDANG - expected_json[ID] = 3 self.assertEqual(content, expected_json) def test_cannot_get_register_penyandang(self): @@ -285,12 +284,12 @@ class LayananKhususViewsTest(TestCase): content = json.loads(response.content.decode('utf-8')) expected_json = MOCK_KOMUNITAS self.assertEqual(content, expected_json) - + def test_cannot_post_detail_komunitas(self): response = Client().post( reverse('detail-komunitas', kwargs={'id_komunitas': 1})) self.assertEqual(response.status_code, HTTPStatus.METHOD_NOT_ALLOWED) - + def test_get_details_komuniras_not_found(self): response = Client().get( reverse('detail-komunitas', kwargs={'id_komunitas': 1})) diff --git a/registrasi/tests.py b/registrasi/tests.py index 472b6a7..0bef2cb 100644 --- a/registrasi/tests.py +++ b/registrasi/tests.py @@ -17,4 +17,4 @@ class RegistrationTest(TestCase): with self.assertRaises(IntegrityError) as error: obj = BisaGoUser(user=None) obj.save() - self.assertFalse(str(error.exception).startswith('NOT NULL constraint failed')) + self.assertTrue(str(error.exception).startswith('NOT NULL constraint failed')) -- GitLab From 038c9eeca73a4f9ff092f240c813fc9204e3e5fc Mon Sep 17 00:00:00 2001 From: ariqbasyar Date: Sat, 20 Mar 2021 18:04:52 +0700 Subject: [PATCH 06/97] [REFACTOR] custom deployment to private server - removed variables and unnecessary postgres service (relates to commit 6e6c5ec) --- .gitlab-ci.yml | 59 ++++++++++---------------------------------------- 1 file changed, 12 insertions(+), 47 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 9968488..6ffa0f4 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,21 +1,8 @@ -variables: - POSTGRES_DB: $DB_NAME - POSTGRES_USER: $DB_USER - POSTGRES_PASSWORD: $DB_PASSWORD - POSTGRES_HOST_AUTH_METHOD: $DB_HOST_AUTH_METHOD - AWS_ACCESS_KEY_ID: $AWS_ACCESS_KEY_ID - AWS_SECRET_ACCESS_KEY: $AWS_SECRET_ACCESS_KEY - AWS_STORAGE_BUCKET_NAME: $AWS_STORAGE_BUCKET_NAME - -services: - - postgres:12.2-alpine - stages: - test - linter - sonarqube - - staging - - production + - deployment UnitTest: image: python:3.7 @@ -43,10 +30,11 @@ Pylint: - pip install -r requirements.txt when: on_success script: - - pylint --load-plugins pylint_django --rcfile=./.pylintrc informasi_fasilitas layanan_khusus new_rest_api oauth registrasi + - pylint --load-plugins pylint_django --rcfile=./.pylintrc \ + informasi_fasilitas layanan_khusus new_rest_api oauth registrasi SonarScanner: - image: + image: name: sonarsource/sonar-scanner-cli:latest entrypoint: [""] stage: sonarqube @@ -57,30 +45,13 @@ SonarScanner: -Dsonar.branch.name=$CI_COMMIT_REF_NAME -Dsonar.projectKey=$SONARQUBE_PROJECT_KEY -Staging: - image: ruby:2.4 - stage: staging - only: - refs: - - staging - before_script: - - gem install dpl - - wget -qO- https://cli-assets.heroku.com/install-ubuntu.sh | sh - script: - - dpl --provider=heroku --app=$HEROKU_APPNAME_STAGING --api-key=$HEROKU_APIKEY - - export HEROKU_API_KEY=$HEROKU_APIKEY - - heroku run --app $HEROKU_APPNAME_STAGING migrate - environment: - name: staging - url: $HEROKU_APP_HOST_STAGING - - Deployment: image: ruby:2.4 - stage: production + stage: deployment only: refs: - - master + - staging + - development before_script: - apt-get update -qq - apt-get install -qq git @@ -88,16 +59,10 @@ Deployment: - eval $(ssh-agent -s) - ssh-add <(echo "$SSH_PRIVATE_KEY" | base64 -d) - mkdir -p ~/.ssh - - '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config' + - echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config script: - - > - ssh $TARGET_USER_AT_HOST - "cd bisago-be && - git checkout master && - git pull origin master && - bash stop.sh && - bash start.sh && - exit" + - result=$(ssh $TARGET "bash deployment/reload.sh $CI_COMMIT_REF_NAME") + - echo "$result" + - [ '$(echo "$result" | tail -n 1)' != "success" ] && exit 1 environment: - name: production - url: $HEROKU_APP_HOST \ No newline at end of file + name: deployment -- GitLab From aac729dd21cdadfcceb6a37b3394cf99ad2254ae Mon Sep 17 00:00:00 2001 From: ariqbasyar Date: Sat, 20 Mar 2021 18:26:48 +0700 Subject: [PATCH 07/97] gitlab ci cant detect character '[' or '&' --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 6ffa0f4..533cd0e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -63,6 +63,6 @@ Deployment: script: - result=$(ssh $TARGET "bash deployment/reload.sh $CI_COMMIT_REF_NAME") - echo "$result" - - [ '$(echo "$result" | tail -n 1)' != "success" ] && exit 1 + - if [ '$(echo "$result" | tail -n 1)' != "success" ]; then exit 1; fi environment: name: deployment -- GitLab From d2f371f689f2de5287f3efcd0371cfc17e847b3d Mon Sep 17 00:00:00 2001 From: ariqbasyar Date: Sat, 20 Mar 2021 20:41:42 +0700 Subject: [PATCH 08/97] added allow failure to sonarqube script temporarily --- .gitlab-ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 533cd0e..e4b2bfb 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -48,6 +48,7 @@ SonarScanner: Deployment: image: ruby:2.4 stage: deployment + allow_failure: true only: refs: - staging -- GitLab From 8997a58791f780e45d8ec5906ffae7dcdb1710ae Mon Sep 17 00:00:00 2001 From: ariqbasyar Date: Sat, 20 Mar 2021 20:46:02 +0700 Subject: [PATCH 09/97] Revert "added allow failure to sonarqube script temporarily" This reverts commit d2f371f689f2de5287f3efcd0371cfc17e847b3d. --- .gitlab-ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e4b2bfb..533cd0e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -48,7 +48,6 @@ SonarScanner: Deployment: image: ruby:2.4 stage: deployment - allow_failure: true only: refs: - staging -- GitLab From 2298e07cfa337f0c1e28d67a0b92e85695ef8cee Mon Sep 17 00:00:00 2001 From: ariqbasyar Date: Sat, 20 Mar 2021 20:46:51 +0700 Subject: [PATCH 10/97] added allow failure to sonarqube script temporarily --- .gitlab-ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 533cd0e..27273ac 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -38,6 +38,7 @@ SonarScanner: name: sonarsource/sonar-scanner-cli:latest entrypoint: [""] stage: sonarqube + allow_failure: true script: - sonar-scanner -Dsonar.host.url=https://pmpl.cs.ui.ac.id/sonarqube-advprog -- GitLab From 4b5272865adaa14436116567dccf3fbffca9a8c9 Mon Sep 17 00:00:00 2001 From: ariqbasyar Date: Sat, 20 Mar 2021 20:59:20 +0700 Subject: [PATCH 11/97] remove repeated code at base test, make more general method --- informasi_fasilitas/test_base.py | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/informasi_fasilitas/test_base.py b/informasi_fasilitas/test_base.py index 0f7e055..cff9b58 100644 --- a/informasi_fasilitas/test_base.py +++ b/informasi_fasilitas/test_base.py @@ -141,29 +141,26 @@ class InformasiFasilitasTest(TestCase): dislike_test.save() return dislike_test - def client_user_token(self): - Client().post(REGISTER, self.user_post) - test_user = User.objects.get(username=self.user_post["email"]) + def get_client_from_user(self, user_dict): + Client().post(REGISTER, user_dict) + test_user = User.objects.get(username=user_dict['email']) test_user.is_active = True test_user.save() - token_response = Client().post(TOKEN_AUTH, {'username': self.user_post["email"], - 'password': self.user_post["password"]}) + token_response = Client().post(TOKEN_AUTH, {'username': user_dict["email"], + 'password': user_dict["password"]}) content = json.loads(token_response.content.decode('utf-8')) token = content['token'] client = Client(HTTP_AUTHORIZATION=TOKEN_SUFFIX+token) return client + def client_user_token(self): + return self.get_client_from_user(self.user_post) + def client_user_token2(self): - Client().post(REGISTER, self.user_post) - test_user = User.objects.get(username=self.default_username_email) - test_user.is_active = True - test_user.save() - token_response = Client().post(TOKEN_AUTH, {'username': self.default_username_email, - 'password': self.default_password}) - content = json.loads(token_response.content.decode('utf-8')) - token = content['token'] - client = Client(HTTP_AUTHORIZATION=TOKEN_SUFFIX+token) - return client + return self.get_client_from_user({'name': self.default_username_email, + 'email': self.default_username_email, + 'phone_number': self.default_no_telp, + 'password': self.default_password}) class InformasiFasilitasViewTest(InformasiFasilitasTest): -- GitLab From 8b913cf41b245f70d3b202f00e35669852792281 Mon Sep 17 00:00:00 2001 From: ariqbasyar Date: Sun, 21 Mar 2021 17:21:13 +0700 Subject: [PATCH 12/97] fixing bug gitlab-ci script - remove single quote from if condition --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 27273ac..de9bf4b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -64,6 +64,6 @@ Deployment: script: - result=$(ssh $TARGET "bash deployment/reload.sh $CI_COMMIT_REF_NAME") - echo "$result" - - if [ '$(echo "$result" | tail -n 1)' != "success" ]; then exit 1; fi + - if [ $(echo "$result" | tail -n 1) != "success" ]; then exit 1; fi environment: name: deployment -- GitLab From ac04ad26a229350e0202fe39c2019f6ae9a0c319 Mon Sep 17 00:00:00 2001 From: ariqbasyar Date: Sun, 21 Mar 2021 17:36:22 +0700 Subject: [PATCH 13/97] now can safely remove allow_failure option from gitlab ci yml file --- .gitlab-ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index de9bf4b..723c6ad 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -38,7 +38,6 @@ SonarScanner: name: sonarsource/sonar-scanner-cli:latest entrypoint: [""] stage: sonarqube - allow_failure: true script: - sonar-scanner -Dsonar.host.url=https://pmpl.cs.ui.ac.id/sonarqube-advprog -- GitLab From 0f2e5ea3e11df1754c036275090f34a29463eeda Mon Sep 17 00:00:00 2001 From: ariqbasyar Date: Sun, 21 Mar 2021 23:22:07 +0700 Subject: [PATCH 14/97] remove unused if condition on views now using @api_view decorator, no need to check the request method --- informasi_fasilitas/views.py | 353 +++++++++++++++++------------------ 1 file changed, 171 insertions(+), 182 deletions(-) diff --git a/informasi_fasilitas/views.py b/informasi_fasilitas/views.py index 30d7ad8..d799fce 100644 --- a/informasi_fasilitas/views.py +++ b/informasi_fasilitas/views.py @@ -29,10 +29,9 @@ def missing_key_message(key): @authentication_classes([]) @permission_classes([]) def lokasi_list(request): - if request.method == 'GET': - lokasi_list = Lokasi.objects.all() - serializer = LokasiSerializer(lokasi_list, many=True) - return JsonResponse(serializer.data, safe=False, status=HTTPStatus.OK) + lokasi_list = Lokasi.objects.all() + serializer = LokasiSerializer(lokasi_list, many=True) + return JsonResponse(serializer.data, safe=False, status=HTTPStatus.OK) @api_view(['GET']) @@ -40,10 +39,9 @@ def lokasi_list(request): @permission_classes([]) def lokasi_details(request, nama_lokasi): try: - if request.method == 'GET': - lokasi = Lokasi.objects.get(name=nama_lokasi) - serializer = LokasiSerializer(lokasi) - return JsonResponse(serializer.data, safe=False, status=HTTPStatus.OK) + lokasi = Lokasi.objects.get(name=nama_lokasi) + serializer = LokasiSerializer(lokasi) + return JsonResponse(serializer.data, safe=False, status=HTTPStatus.OK) except ObjectDoesNotExist as err: return JsonResponse({'response': str(err)}, status=HTTPStatus.NOT_FOUND) @@ -53,14 +51,13 @@ def lokasi_details(request, nama_lokasi): @permission_classes([IsAuthenticated]) def add_lokasi(request): try: - if request.method == 'POST': - data = request.data - serializer = LokasiSerializer(data=data) - if serializer.is_valid(): - serializer.save() - return JsonResponse(serializer.data, status=HTTPStatus.CREATED) - else: - return JsonResponse(serializer.errors, status=HTTPStatus.BAD_REQUEST) + data = request.data + serializer = LokasiSerializer(data=data) + if serializer.is_valid(): + serializer.save() + return JsonResponse(serializer.data, status=HTTPStatus.CREATED) + else: + return JsonResponse(serializer.errors, status=HTTPStatus.BAD_REQUEST) except Exception as err: return JsonResponse({'response': str(err)}, status=HTTPStatus.NOT_FOUND) @@ -69,13 +66,12 @@ def add_lokasi(request): @permission_classes([IsAuthenticated]) def update_lokasi_details(request, nama_lokasi): try: - if request.method == 'PUT': - lokasi = Lokasi.objects.get(name=nama_lokasi) - lokasi.no_telp = request.data['no_telp'] - lokasi.counter = F('counter') + 1 - lokasi.save() - return JsonResponse({'response': "phone changed to {}".format(lokasi.no_telp)}, - status=HTTPStatus.ACCEPTED) + lokasi = Lokasi.objects.get(name=nama_lokasi) + lokasi.no_telp = request.data['no_telp'] + lokasi.counter = F('counter') + 1 + lokasi.save() + return JsonResponse({'response': "phone changed to {}".format(lokasi.no_telp)}, + status=HTTPStatus.ACCEPTED) except KeyError as missing_key: return JsonResponse({'response': missing_key_message(str(missing_key))}, status=HTTPStatus.BAD_REQUEST) @@ -86,32 +82,31 @@ def update_lokasi_details(request, nama_lokasi): @permission_classes([]) def list_fasilitas(request, nama_lokasi): try: - if request.method == 'GET': - lokasi = Lokasi.objects.get(name=nama_lokasi) - list_fasilitas = Fasilitas.objects.filter(lokasi=lokasi) - return_json = {} - for fasilitas in list_fasilitas: - fasilitas.like = Likes.objects.filter( - fasilitas=fasilitas).count() - fasilitas.dislike = Dislikes.objects.filter( - fasilitas=fasilitas).count() - return_json[fasilitas.id] = {} - fasilitas_details = return_json[fasilitas.id] - fasilitas_details["id"] = fasilitas.id - fasilitas_details["nama_lokasi"] = fasilitas.lokasi.name - fasilitas_details["deskripsi"] = fasilitas.deskripsi - fasilitas_details["creator"] = fasilitas.user.last_name - fasilitas_details["date_time"] = fasilitas.date_time.strftime( - TIME_FORMAT) - fasilitas_details["like"] = fasilitas.like - fasilitas_details["dislike"] = fasilitas.dislike - fasilitas_details["rating"] = fasilitas.rating - fasilitas_details["tag"] = fasilitas.tag - fasilitas_details["disabilitas"] = fasilitas.disabilitas - fasilitas_details["jumlah"] = fasilitas.jumlah - fasilitas_details["image"] = '/media/'+str(fasilitas.image) - fasilitas_details["is_verified"] = fasilitas.is_verified - return JsonResponse(return_json, status=HTTPStatus.OK) + lokasi = Lokasi.objects.get(name=nama_lokasi) + list_fasilitas = Fasilitas.objects.filter(lokasi=lokasi) + return_json = {} + for fasilitas in list_fasilitas: + fasilitas.like = Likes.objects.filter( + fasilitas=fasilitas).count() + fasilitas.dislike = Dislikes.objects.filter( + fasilitas=fasilitas).count() + return_json[fasilitas.id] = {} + fasilitas_details = return_json[fasilitas.id] + fasilitas_details["id"] = fasilitas.id + fasilitas_details["nama_lokasi"] = fasilitas.lokasi.name + fasilitas_details["deskripsi"] = fasilitas.deskripsi + fasilitas_details["creator"] = fasilitas.user.last_name + fasilitas_details["date_time"] = fasilitas.date_time.strftime( + TIME_FORMAT) + fasilitas_details["like"] = fasilitas.like + fasilitas_details["dislike"] = fasilitas.dislike + fasilitas_details["rating"] = fasilitas.rating + fasilitas_details["tag"] = fasilitas.tag + fasilitas_details["disabilitas"] = fasilitas.disabilitas + fasilitas_details["jumlah"] = fasilitas.jumlah + fasilitas_details["image"] = '/media/'+str(fasilitas.image) + fasilitas_details["is_verified"] = fasilitas.is_verified + return JsonResponse(return_json, status=HTTPStatus.OK) except Exception as error: return JsonResponse({'response': str(error)}, status=HTTPStatus.NOT_FOUND) @@ -121,32 +116,31 @@ def list_fasilitas(request, nama_lokasi): @permission_classes([IsAuthenticated]) def add_fasilitas(request, nama_lokasi): try: - if request.method == 'POST': - lokasi = Lokasi.objects.get(name=nama_lokasi) - user = User.objects.get(email=str(request.user)) - deskripsi = request.POST['deskripsi'] - rating = request.POST['rating'] - tag = request.POST['tag'] - disabilitas = request.POST['disabilitas'] - jumlah = request.POST['jumlah'] - image = "" - if Fasilitas.objects.filter(lokasi=lokasi.pk, tag=tag).count() > 0: - return JsonResponse({'response': 'fasilitas already exist'}, - status=HTTPStatus.OK) - if tag == "": - return JsonResponse({'response': 'tag cannot be null'}, - status=HTTPStatus.BAD_REQUEST) - if 'image' in request.FILES.keys(): - image = request.FILES['image'] - fasilitas = Fasilitas.objects.create(lokasi=lokasi, - user=user, - deskripsi=deskripsi, - rating=rating, - tag=tag, - disabilitas=disabilitas, - jumlah=jumlah, - image=image) - return JsonResponse({'response': 'fasilitas added', 'id': fasilitas.id}, + lokasi = Lokasi.objects.get(name=nama_lokasi) + user = User.objects.get(email=str(request.user)) + deskripsi = request.POST['deskripsi'] + rating = request.POST['rating'] + tag = request.POST['tag'] + disabilitas = request.POST['disabilitas'] + jumlah = request.POST['jumlah'] + image = "" + if Fasilitas.objects.filter(lokasi=lokasi.pk, tag=tag).count() > 0: + return JsonResponse({'response': 'fasilitas already exist'}, + status=HTTPStatus.OK) + if tag == "": + return JsonResponse({'response': 'tag cannot be null'}, + status=HTTPStatus.BAD_REQUEST) + if 'image' in request.FILES.keys(): + image = request.FILES['image'] + fasilitas = Fasilitas.objects.create(lokasi=lokasi, + user=user, + deskripsi=deskripsi, + rating=rating, + tag=tag, + disabilitas=disabilitas, + jumlah=jumlah, + image=image) + return JsonResponse({'response': 'fasilitas added', 'id': fasilitas.id}, status=HTTPStatus.CREATED) except KeyError as missing_key: return JsonResponse({'response': missing_key_message(str(missing_key))}, @@ -160,28 +154,27 @@ def add_fasilitas(request, nama_lokasi): @permission_classes([]) def detail_fasilitas(request, nama_lokasi, id): try: - if request.method == 'GET': - lokasi = Lokasi.objects.get(name=nama_lokasi) - fasilitas = Fasilitas.objects.get(lokasi=lokasi, id=id) - user = fasilitas.user - fasilitas.like = Likes.objects.filter(fasilitas=fasilitas).count() - fasilitas.dislike = Dislikes.objects.filter( - fasilitas=fasilitas).count() - return_json = {"id": fasilitas.id, - "nama_lokasi": lokasi.name, - "deskripsi": fasilitas.deskripsi, - "creator": user.last_name, - "creator_email": user.email, - "date_time": fasilitas.date_time, - "like": fasilitas.like, - "dislike": fasilitas.dislike, - "rating": fasilitas.rating, - "tag": fasilitas.tag, - "disabilitas": fasilitas.disabilitas, - "jumlah": fasilitas.jumlah, - "image": '/media/' + str(fasilitas.image), - "is_verified": fasilitas.is_verified} - return JsonResponse(return_json, status=HTTPStatus.OK) + lokasi = Lokasi.objects.get(name=nama_lokasi) + fasilitas = Fasilitas.objects.get(lokasi=lokasi, id=id) + user = fasilitas.user + fasilitas.like = Likes.objects.filter(fasilitas=fasilitas).count() + fasilitas.dislike = Dislikes.objects.filter( + fasilitas=fasilitas).count() + return_json = {"id": fasilitas.id, + "nama_lokasi": lokasi.name, + "deskripsi": fasilitas.deskripsi, + "creator": user.last_name, + "creator_email": user.email, + "date_time": fasilitas.date_time, + "like": fasilitas.like, + "dislike": fasilitas.dislike, + "rating": fasilitas.rating, + "tag": fasilitas.tag, + "disabilitas": fasilitas.disabilitas, + "jumlah": fasilitas.jumlah, + "image": '/media/' + str(fasilitas.image), + "is_verified": fasilitas.is_verified} + return JsonResponse(return_json, status=HTTPStatus.OK) except KeyError as missing_key: return JsonResponse({'response': missing_key_message(str(missing_key))}, status=HTTPStatus.BAD_REQUEST) @@ -194,36 +187,35 @@ def detail_fasilitas(request, nama_lokasi, id): @permission_classes([IsAuthenticated]) def update_fasilitas(request, nama_lokasi, id): try: - if request.method == "PUT": - lokasi = Lokasi.objects.get(name=nama_lokasi) - fasilitas = Fasilitas.objects.get(lokasi=lokasi, id=id) - user_creator = fasilitas.user - desc = fasilitas.deskripsi - tag = fasilitas.tag - jumlah = fasilitas.jumlah - image = fasilitas.image - disabilitas = fasilitas.disabilitas - if user_creator == request.user: - if 'deskripsi' in request.data.keys(): - desc = request.data['deskripsi'] - if 'tag' in request.data.keys(): - tag = request.data['tag'].split() - if 'jumlah' in request.data.keys(): - jumlah = request.data['jumlah'] - if 'image' in request.data.keys(): - image = request.data['image'] - if 'disabilitas' in request.data.keys(): - disabilitas = request.data['disabilitas'] - fasilitas.deskripsi = desc - fasilitas.tag = tag - fasilitas.jumlah = jumlah - fasilitas.image = image - fasilitas.disabilitas = disabilitas - fasilitas.save() - return JsonResponse({'response': '{} in fasilitas edited'.format(str(request.data.keys())), }, - status=HTTPStatus.ACCEPTED) - return JsonResponse({'response': 'Authentication failed'}, - status=HTTPStatus.UNAUTHORIZED) + lokasi = Lokasi.objects.get(name=nama_lokasi) + fasilitas = Fasilitas.objects.get(lokasi=lokasi, id=id) + user_creator = fasilitas.user + desc = fasilitas.deskripsi + tag = fasilitas.tag + jumlah = fasilitas.jumlah + image = fasilitas.image + disabilitas = fasilitas.disabilitas + if user_creator == request.user: + if 'deskripsi' in request.data.keys(): + desc = request.data['deskripsi'] + if 'tag' in request.data.keys(): + tag = request.data['tag'].split() + if 'jumlah' in request.data.keys(): + jumlah = request.data['jumlah'] + if 'image' in request.data.keys(): + image = request.data['image'] + if 'disabilitas' in request.data.keys(): + disabilitas = request.data['disabilitas'] + fasilitas.deskripsi = desc + fasilitas.tag = tag + fasilitas.jumlah = jumlah + fasilitas.image = image + fasilitas.disabilitas = disabilitas + fasilitas.save() + return JsonResponse({'response': '{} in fasilitas edited'.format(str(request.data.keys())), }, + status=HTTPStatus.ACCEPTED) + return JsonResponse({'response': 'Authentication failed'}, + status=HTTPStatus.UNAUTHORIZED) except KeyError as missing_key: return JsonResponse({'response': missing_key_message(str(missing_key))}, status=HTTPStatus.BAD_REQUEST) @@ -236,37 +228,36 @@ def update_fasilitas(request, nama_lokasi, id): @permission_classes([IsAuthenticated]) def update_like_fasilitas(request, nama_lokasi, id, operation): try: - if request.method == "PUT": - lokasi = Lokasi.objects.get(name=nama_lokasi) - fasilitas = Fasilitas.objects.get(lokasi=lokasi, id=id) - user = request.user + lokasi = Lokasi.objects.get(name=nama_lokasi) + fasilitas = Fasilitas.objects.get(lokasi=lokasi, id=id) + user = request.user - try: - like = Likes.objects.get(fasilitas=fasilitas, user=user) - except Likes.DoesNotExist: - like = None - try: - dislike = Dislikes.objects.get(fasilitas=fasilitas, user=user) - except Dislikes.DoesNotExist: - dislike = None - if operation == "like": - if like != None: - return JsonResponse({'response': "You have already liked this facility"}, status=HTTPStatus.ACCEPTED) - else: - Likes.objects.create(fasilitas=fasilitas, user=user) - if dislike != None: - dislike.delete() - elif operation == "dislike": - if dislike != None: - return JsonResponse({'response': "You have already disliked this facility"}, status=HTTPStatus.ACCEPTED) - else: - Dislikes.objects.create(fasilitas=fasilitas, user=user) - if like != None: - like.delete() - fasilitas.like = Likes.objects.filter(fasilitas=fasilitas).count() - fasilitas.dislike = Dislikes.objects.filter( - fasilitas=fasilitas).count() - return JsonResponse({'response': "You have successfuly {}d this facility".format(operation)}, status=HTTPStatus.CREATED) + try: + like = Likes.objects.get(fasilitas=fasilitas, user=user) + except Likes.DoesNotExist: + like = None + try: + dislike = Dislikes.objects.get(fasilitas=fasilitas, user=user) + except Dislikes.DoesNotExist: + dislike = None + if operation == "like": + if like != None: + return JsonResponse({'response': "You have already liked this facility"}, status=HTTPStatus.ACCEPTED) + else: + Likes.objects.create(fasilitas=fasilitas, user=user) + if dislike != None: + dislike.delete() + elif operation == "dislike": + if dislike != None: + return JsonResponse({'response': "You have already disliked this facility"}, status=HTTPStatus.ACCEPTED) + else: + Dislikes.objects.create(fasilitas=fasilitas, user=user) + if like != None: + like.delete() + fasilitas.like = Likes.objects.filter(fasilitas=fasilitas).count() + fasilitas.dislike = Dislikes.objects.filter( + fasilitas=fasilitas).count() + return JsonResponse({'response': "You have successfuly {}d this facility".format(operation)}, status=HTTPStatus.CREATED) except KeyError as missing_key: return JsonResponse({'response': missing_key_message(str(missing_key))}, status=HTTPStatus.BAD_REQUEST) except Exception as error: @@ -278,17 +269,16 @@ def update_like_fasilitas(request, nama_lokasi, id, operation): @permission_classes([IsAuthenticated]) def add_komentar(request, nama_lokasi, id): try: - if request.method == 'POST': - lokasi = Lokasi.objects.get(name=nama_lokasi) - fasilitas = Fasilitas.objects.get(lokasi=lokasi, id=id) - user = User.objects.get(email=str(request.user)) - deskripsi = request.POST['deskripsi'] - komentar = Komentar.objects.create(fasilitas=fasilitas, - user=user, - deskripsi=deskripsi) - return JsonResponse({'response': 'komentar added', - 'id': komentar.id, - "created_date": komentar.date_time.strftime(TIME_FORMAT)}, status=HTTPStatus.CREATED) + lokasi = Lokasi.objects.get(name=nama_lokasi) + fasilitas = Fasilitas.objects.get(lokasi=lokasi, id=id) + user = User.objects.get(email=str(request.user)) + deskripsi = request.POST['deskripsi'] + komentar = Komentar.objects.create(fasilitas=fasilitas, + user=user, + deskripsi=deskripsi) + return JsonResponse({'response': 'komentar added', + 'id': komentar.id, + "created_date": komentar.date_time.strftime(TIME_FORMAT)}, status=HTTPStatus.CREATED) except KeyError as e: return JsonResponse({'response': missing_key_message(str(e))}, status=HTTPStatus.BAD_REQUEST) except Exception as e: @@ -300,20 +290,19 @@ def add_komentar(request, nama_lokasi, id): @permission_classes([]) def list_komentar(request, nama_lokasi, id): try: - if request.method == 'GET': - lokasi = Lokasi.objects.get(name=nama_lokasi) - fasilitas = Fasilitas.objects.get(lokasi=lokasi, id=id) - list_komentar = Komentar.objects.filter(fasilitas=fasilitas) - return_json = {} - for komentar in list_komentar: - return_json[komentar.id] = {} - komentar_details = return_json[komentar.id] - komentar_details["id"] = komentar.id - komentar_details["deskripsi"] = komentar.deskripsi - komentar_details["creator"] = komentar.user.last_name - komentar_details["date_time"] = komentar.date_time.strftime( - TIME_FORMAT) - return JsonResponse(return_json, status=HTTPStatus.OK) + lokasi = Lokasi.objects.get(name=nama_lokasi) + fasilitas = Fasilitas.objects.get(lokasi=lokasi, id=id) + list_komentar = Komentar.objects.filter(fasilitas=fasilitas) + return_json = {} + for komentar in list_komentar: + return_json[komentar.id] = {} + komentar_details = return_json[komentar.id] + komentar_details["id"] = komentar.id + komentar_details["deskripsi"] = komentar.deskripsi + komentar_details["creator"] = komentar.user.last_name + komentar_details["date_time"] = komentar.date_time.strftime( + TIME_FORMAT) + return JsonResponse(return_json, status=HTTPStatus.OK) except KeyError as key: return JsonResponse({'response': missing_key_message(str(key))}, status=HTTPStatus.BAD_REQUEST) -- GitLab From 616a8b7db86f736cc516bac333054e6e2b374956 Mon Sep 17 00:00:00 2001 From: "alvin.hariman" Date: Tue, 23 Mar 2021 11:14:35 +0700 Subject: [PATCH 15/97] [RED] Add tests for search feature --- layanan_khusus/tests.py | 91 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/layanan_khusus/tests.py b/layanan_khusus/tests.py index 8a6038c..d98719b 100644 --- a/layanan_khusus/tests.py +++ b/layanan_khusus/tests.py @@ -295,3 +295,94 @@ class LayananKhususViewsTest(TestCase): response = Client().get( reverse('detail-komunitas', kwargs={'id_komunitas': 1})) self.assertEqual(response.status_code, HTTPStatus.NOT_FOUND) + +class LayananKhususSearchTest(TestCase): + urlpatterns = [ + path('layanan-khusus/', include('layanan_khusus.urls')), + ] + + def test_can_search_komunitas(self): + komunitas_setup() + response = Client().get("%s?search=Disabilitas" % reverse('pencarian')) + self.assertEqual(response.status_code, HTTPStatus.OK) + + def test_can_search_sekolah(self): + sekolah_setup() + response = Client().get("%s?search=Sekolah" % reverse('pencarian')) + self.assertEqual(response.status_code, HTTPStatus.OK) + + def test_can_search_komunitas_json(self): + komunitas_setup() + response = Client().get("%s?search=Disabilitas" % reverse('pencarian')) + + content = json.loads(response.content.decode('utf-8')) + expected_json = {'0': { + 'name': MOCK_KOMUNITAS[NAME], + 'alamat': MOCK_KOMUNITAS[ALAMAT], + 'no_telp': MOCK_KOMUNITAS[NO_TELP], + 'website': MOCK_KOMUNITAS[WEBSITE], + 'jenis_komunitas': MOCK_KOMUNITAS[JENIS_KOMUNITAS] + }} + + self.assertEqual(content, expected_json) + + def test_can_search_sekolah_json(self): + sekolah_setup() + response = Client().get("%s?search=Sekolah" % reverse('pencarian')) + + content = json.loads(response.content.decode('utf-8')) + expected_json = {'0': { + 'name': MOCK_SEKOLAH[NAME], + 'alamat': MOCK_SEKOLAH[ALAMAT], + 'no_telp': MOCK_SEKOLAH[NO_TELP], + 'website': MOCK_SEKOLAH[WEBSITE], + 'jumlah_siswa': MOCK_SEKOLAH[JUMLAH_SISWA], + 'status': MOCK_SEKOLAH[STATUS], + 'jenis_sekolah': MOCK_SEKOLAH[JENIS_SEKOLAH], + 'akreditasi': MOCK_SEKOLAH[AKREDITASI] + }} + + self.assertEqual(content, expected_json) + + def test_cannot_post_search_sekolah(self): + sekolah_setup() + response = Client().post("%s?search=Sekolah" % reverse('pencarian')) + + self.assertEqual(response.status_code, HTTPStatus.METHOD_NOT_ALLOWED) + + def test_cannot_post_search_komunitas(self): + komunitas_setup() + response = Client().post("%s?search=Disabilitas" % reverse('pencarian')) + + self.assertEqual(response.status_code, HTTPStatus.METHOD_NOT_ALLOWED) + + def test_search_komunitas_and_sekolah(self): + sekolah_setup() + komunitas_setup() + response = Client().get("%s?search=Jakarta" % reverse('pencarian')) + self.assertEqual(response.status_code, HTTPStatus.OK) + + def test_search_komunitas_and_sekolah_json(self): + sekolah_setup() + komunitas_setup() + response = Client().get("%s?search=Jakarta" % reverse('pencarian')) + + content = json.loads(response.content.decode('utf-8')) + expected_json = {'0': { + 'name': MOCK_SEKOLAH[NAME], + 'alamat': MOCK_SEKOLAH[ALAMAT], + 'no_telp': MOCK_SEKOLAH[NO_TELP], + 'website': MOCK_SEKOLAH[WEBSITE], + 'jumlah_siswa': MOCK_SEKOLAH[JUMLAH_SISWA], + 'status': MOCK_SEKOLAH[STATUS], + 'jenis_sekolah': MOCK_SEKOLAH[JENIS_SEKOLAH], + 'akreditasi': MOCK_SEKOLAH[AKREDITASI] + }, '1': { + 'name': MOCK_KOMUNITAS[NAME], + 'alamat': MOCK_KOMUNITAS[ALAMAT], + 'no_telp': MOCK_KOMUNITAS[NO_TELP], + 'website': MOCK_KOMUNITAS[WEBSITE], + 'jenis_komunitas': MOCK_KOMUNITAS[JENIS_KOMUNITAS] + }} + + self.assertEqual(content, expected_json) -- GitLab From eb8e2b313dab9f78b69f6837e73b9961c0f39ff2 Mon Sep 17 00:00:00 2001 From: "alvin.hariman" Date: Tue, 23 Mar 2021 11:15:16 +0700 Subject: [PATCH 16/97] [GREEN] Implemented search feature for layanan disabilitas --- layanan_khusus/urls.py | 13 ++++++------ layanan_khusus/views.py | 47 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 53 insertions(+), 7 deletions(-) diff --git a/layanan_khusus/urls.py b/layanan_khusus/urls.py index 393f9af..b98d2e4 100644 --- a/layanan_khusus/urls.py +++ b/layanan_khusus/urls.py @@ -2,12 +2,13 @@ from django.urls import path from . import views urlpatterns = [ - path('sekolah/list/', views.list_sekolah, name='list-sekolah'), - path('sekolah/detail//', views.detail_sekolah, + path('sekolah/list/', views.list_sekolah, name='list-sekolah'), + path('sekolah/detail//', views.detail_sekolah, name='detail-sekolah'), - path('komunitas/list/', views.list_komunitas, name='list-komunitas'), - path('komunitas/detail//', views.detail_komunitas, + path('komunitas/list/', views.list_komunitas, name='list-komunitas'), + path('komunitas/detail//', views.detail_komunitas, name='detail-komunitas'), - path('penyandang/register/', views.register_penyandang, - name='register-penyandang-disabilitas') + path('penyandang/register/', views.register_penyandang, + name='register-penyandang-disabilitas'), + path('pencarian/', views.pencarian, name='pencarian') ] diff --git a/layanan_khusus/views.py b/layanan_khusus/views.py index 2547ad2..139fc7d 100644 --- a/layanan_khusus/views.py +++ b/layanan_khusus/views.py @@ -7,7 +7,7 @@ from rest_framework.authentication import TokenAuthentication from rest_framework.permissions import IsAuthenticated from .models import Sekolah, Komunitas from .serializers import SekolahSerializer, PenyandangSerializer, KomunitasSerializer - +from django.contrib.postgres.search import SearchVector, SearchQuery @api_view(['GET']) @authentication_classes([]) @@ -67,3 +67,48 @@ def register_penyandang(request): return JsonResponse(serializer.data, status=HTTPStatus.CREATED) else: return JsonResponse(serializer.errors, status=HTTPStatus.BAD_REQUEST) + +@api_view(['GET']) +@authentication_classes([]) +@permission_classes([]) +def pencarian(request): + try: + if request.method == 'GET': + keyword = request.GET.get("search") + + list_sekolah = Sekolah.objects.annotate(search=SearchVector( + 'name', 'alamat', 'no_telp', 'website', 'status', 'jenis_sekolah'),).filter(search=SearchQuery(keyword)) + + list_komunitas = Komunitas.objects.annotate(search=SearchVector( + 'name', 'alamat', 'no_telp', 'website', 'jenis_komunitas'),).filter(search=SearchQuery(keyword)) + + indeks = 0 + return_json = {} + + for sekolah in list_sekolah: + return_json[indeks] = {} + sekolah_details = return_json[indeks] + sekolah_details["name"] = sekolah.name + sekolah_details["alamat"] = sekolah.alamat + sekolah_details["no_telp"] = sekolah.no_telp + sekolah_details["website"] = sekolah.website + sekolah_details["jumlah_siswa"] = sekolah.jumlah_siswa + sekolah_details["status"] = sekolah.status + sekolah_details["jenis_sekolah"] = sekolah.jenis_sekolah + sekolah_details["akreditasi"] = sekolah.akreditasi + + indeks += 1 + + for komunitas in list_komunitas: + return_json[indeks] = {} + komunitas_details = return_json[indeks] + komunitas_details["name"] = komunitas.name + komunitas_details["alamat"] = komunitas.alamat + komunitas_details["no_telp"] = komunitas.no_telp + komunitas_details["website"] = komunitas.website + komunitas_details["jenis_komunitas"] = komunitas.jenis_komunitas + + return JsonResponse(return_json, status=HTTPStatus.OK) + + except Exception as err: + return JsonResponse({'response': str(err)}, status=HTTPStatus.NOT_FOUND) \ No newline at end of file -- GitLab From f9c4d24f7f4bac29ea1773be7bbdc8d889258ef5 Mon Sep 17 00:00:00 2001 From: "alvin.hariman" Date: Tue, 23 Mar 2021 11:16:12 +0700 Subject: [PATCH 17/97] [CHORES] Fix error about email registration issue --- new_rest_api/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/new_rest_api/views.py b/new_rest_api/views.py index d6f5687..3a19e78 100644 --- a/new_rest_api/views.py +++ b/new_rest_api/views.py @@ -84,7 +84,7 @@ def register_user(request): message = render_to_string('acc_active_email.html', { 'user' : user, 'domain' : request.get_host, - 'uid' : urlsafe_base64_encode(force_bytes(user.pk)).decode(), + 'uid' : urlsafe_base64_encode(force_bytes(user.pk)), 'token' : account_activation_token.make_token(user), }) mail = EmailMessage(mail_subject, message, to=[email]) -- GitLab From 058c1f3c76fa3c97dd44ffb84b4a6372c4e28f65 Mon Sep 17 00:00:00 2001 From: "alvin.hariman" Date: Tue, 23 Mar 2021 11:25:31 +0700 Subject: [PATCH 18/97] [REFACTOR] Fix search for komunitas cannot more than 1 result --- layanan_khusus/views.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/layanan_khusus/views.py b/layanan_khusus/views.py index 139fc7d..251c0c2 100644 --- a/layanan_khusus/views.py +++ b/layanan_khusus/views.py @@ -108,6 +108,8 @@ def pencarian(request): komunitas_details["website"] = komunitas.website komunitas_details["jenis_komunitas"] = komunitas.jenis_komunitas + indeks += 1 + return JsonResponse(return_json, status=HTTPStatus.OK) except Exception as err: -- GitLab From 4f910dee3001f3d2e99775c97541b1e54a730fd5 Mon Sep 17 00:00:00 2001 From: ariqbasyar Date: Sat, 27 Mar 2021 11:00:14 +0700 Subject: [PATCH 19/97] Setup sonarqube with sonar-project.properties --- .gitlab-ci.yml | 8 +++----- sonar-project.properties | 17 ++++++++--------- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 723c6ad..5bb47b6 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -34,16 +34,14 @@ Pylint: informasi_fasilitas layanan_khusus new_rest_api oauth registrasi SonarScanner: + depenencies: + - UnitTest image: name: sonarsource/sonar-scanner-cli:latest entrypoint: [""] stage: sonarqube script: - - sonar-scanner - -Dsonar.host.url=https://pmpl.cs.ui.ac.id/sonarqube-advprog - -Dsonar.login=$SONARQUBE_TOKEN - -Dsonar.branch.name=$CI_COMMIT_REF_NAME - -Dsonar.projectKey=$SONARQUBE_PROJECT_KEY + - sonar-scanner -Dsonar.sources=. Deployment: image: ruby:2.4 diff --git a/sonar-project.properties b/sonar-project.properties index 34071ac..a1910a8 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -1,27 +1,26 @@ # SonarScanner properties file ## Server -sonar.host.url=https://pmpl.cs.ui.ac.id/sonarqube -sonar.login=$SONARQUBE_TOKEN +sonar.host.url=https://pmpl.cs.ui.ac.id/sonarqube-advprog +sonar.login=${env.SONARQUBE_PROJECT_TOKEN} ## Project configuration -sonar.projectKey=$SONARQUBE_PROJECT_KEY +sonar.projectKey=${env.SONARQUBE_PROJECT_KEY} ## Path to sources sonar.sources=. -sonar.exclusions=**/migrations/**, **/__init__.py, pplbackend/**, **/test.py, manage.py -#sonar.inclusions= +sonar.exclusions=**/migrations/**, **/__init__.py, pplbackend/**, manage.py, coverage.xml ## Path to tests sonar.tests=. -#sonar.test.exclusions= -sonar.test.inclusions=**/test.py +sonar.test.inclusions=**/test*.py ## Source encoding sonar.sourceEncoding=UTF-8 ## Branch analysis -sonar.branch.name=$CI_COMMIT_REF_NAME +sonar.branch.name=${env.CI_COMMIT_REF_NAME} ## Coverage Report -sonar.python.coverage.reportPaths=coverage.xml \ No newline at end of file +sonar.coverage.exclusions=**/apps.py +sonar.python.coverage.reportPaths=coverage.xml -- GitLab From 086389b2e5adcbecff94d35e936463bfdf46221d Mon Sep 17 00:00:00 2001 From: ariqbasyar Date: Sat, 27 Mar 2021 11:23:44 +0700 Subject: [PATCH 20/97] change depenencies to dependencies at gitlab ci Setup sonarqube with sonar-project.properties --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 5bb47b6..a5172b2 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -34,7 +34,7 @@ Pylint: informasi_fasilitas layanan_khusus new_rest_api oauth registrasi SonarScanner: - depenencies: + dependencies: - UnitTest image: name: sonarsource/sonar-scanner-cli:latest -- GitLab From 181d2c381f8349533ba0e39c3368b5ae96b12bb8 Mon Sep 17 00:00:00 2001 From: ariqbasyar Date: Sun, 28 Mar 2021 18:33:56 +0700 Subject: [PATCH 21/97] [RED] added tests for new model and restructure current tests --- informasi_fasilitas/test_base.py | 411 ++++++++++-------- informasi_fasilitas/test_models.py | 32 +- informasi_fasilitas/test_views_fasilitas.py | 191 ++++---- informasi_fasilitas/test_views_komentar.py | 88 ++-- .../test_views_like_dislike.py | 186 +++----- informasi_fasilitas/test_views_lokasi.py | 104 +++-- 6 files changed, 502 insertions(+), 510 deletions(-) diff --git a/informasi_fasilitas/test_base.py b/informasi_fasilitas/test_base.py index cff9b58..6ba0967 100644 --- a/informasi_fasilitas/test_base.py +++ b/informasi_fasilitas/test_base.py @@ -1,190 +1,251 @@ import json import tempfile + from django.test import TestCase, Client from django.contrib.auth.models import User from django.urls import reverse, path, include +from django.core.files.uploadedfile import SimpleUploadedFile +from django.core.files.base import ContentFile -from .models import Lokasi, Fasilitas, Komentar, KURSI_RODA, Likes, Dislikes - -NOT_NULL_CONSTRAINT_FAILED_MESSAGE = 'NOT NULL constraint failed' +from base64 import b64decode -REGISTER = '/api/register/' -TOKEN_AUTH = '/api-token-auth/' -TOKEN_SUFFIX = 'token ' +from .models import ( + Lokasi, + Fasilitas, + Komentar, + KURSI_RODA, + Likes, + Dislikes +) +from pplbackend.utils import get_client_login_with_user class InformasiFasilitasTest(TestCase): - default_username_email = "user_test@gmail.com" - default_last_name = "name" - default_password = "hahagotim" - default_lokasi_name = "Mall" - default_latitude = 0.0 - default_longitude = 0.0 - default_alamat = "Jl. Raya Bogor no.1 Jakarta" - default_no_telp = "08123456789" - default_image = tempfile.NamedTemporaryFile(suffix=".jpg").name - default_like = 0 - default_dislike = 0 - default_tag = KURSI_RODA - default_rating = 3 - default_deskripsi = "penjelasan" - default_disabilitas = "DF" - default_jumlah = 1 - default_counter = 0 - - user_post = {'name': 'name', - 'email': "self.user_post@gmail.com", - 'phone_number': 000000000, - 'password': "hahagotim"} - - lokasi_test_1 = {'id': 1, - 'name': 'Ma Homie', - 'latitude': 0.1, - 'longitude': 0.1, - 'alamat': 'Jl. Raya Bogor no.2, Jakarta', - 'no_telp': '081212123132', - 'counter': 0 - } - - def create_user_test(self, username=default_username_email, - last_name=default_last_name, - password=default_password): - user_test = User(username=username, - last_name=last_name, - email=username, - password=password) - user_test.save() - return user_test - - def create_lokasi_test(self, name=default_lokasi_name, - latitude=default_latitude, - longitude=default_longitude, - alamat=default_alamat, - no_telp=default_no_telp, - image=default_image, - counter=default_counter): - - lokasi_test = Lokasi(pk=1, - name=name, - latitude=latitude, - longitude=longitude, - alamat=alamat, - no_telp=no_telp, - image=image, - counter=counter) - lokasi_test.save() - return lokasi_test - - def create_fasilitas_test(self, deskripsi=default_deskripsi, - like=default_like, - dislike=default_dislike, - rating=default_rating, - tag=default_tag, - disabilitas=default_disabilitas, - jumlah=default_jumlah, - image=default_image, - lokasi=None, - user=None): - if (user == None): - user = self.create_user_test() - if (lokasi == None): - lokasi = self.create_lokasi_test() - fasilitas_test = Fasilitas(pk=1, - lokasi=lokasi, - user=user, - deskripsi=deskripsi, - like=like, - dislike=dislike, - rating=rating, - tag=tag, - disabilitas=disabilitas, - jumlah=jumlah, - image=image) - fasilitas_test.save() - return fasilitas_test - - def create_komentar_test(self, deskripsi=default_deskripsi, - user=None, - fasilitas=None): - if (user == None): - user = self.create_user_test() - if (fasilitas == None): - lokasi = self.create_lokasi_test() - fasilitas = self.create_fasilitas_test(user=user, lokasi=lokasi) - komentar_test = Komentar(deskripsi=deskripsi, - user=user, - fasilitas=fasilitas) - komentar_test.save() - return komentar_test - - def create_likes_test(self, user=None, - fasilitas=None): - if (user == None): - user = self.create_user_test() - if (fasilitas == None): - lokasi = self.create_lokasi_test(user.pk) - fasilitas = self.create_fasilitas_test(user=user, lokasi=lokasi) - like_test = Likes(user=user, - fasilitas=fasilitas) - like_test.save() - return like_test - - def create_dislikes_test(self, user=None, - fasilitas=None): - if (user == None): - user = self.create_user_test() - if (fasilitas == None): - lokasi = self.create_lokasi_test(user.pk) - fasilitas = self.create_fasilitas_test(user=user, lokasi=lokasi) - dislike_test = Dislikes(user=user, - fasilitas=fasilitas) - dislike_test.save() - return dislike_test - - def get_client_from_user(self, user_dict): - Client().post(REGISTER, user_dict) - test_user = User.objects.get(username=user_dict['email']) - test_user.is_active = True - test_user.save() - token_response = Client().post(TOKEN_AUTH, {'username': user_dict["email"], - 'password': user_dict["password"]}) - content = json.loads(token_response.content.decode('utf-8')) - token = content['token'] - client = Client(HTTP_AUTHORIZATION=TOKEN_SUFFIX+token) - return client - - def client_user_token(self): - return self.get_client_from_user(self.user_post) - - def client_user_token2(self): - return self.get_client_from_user({'name': self.default_username_email, - 'email': self.default_username_email, - 'phone_number': self.default_no_telp, - 'password': self.default_password}) + not_null_constraint_failed_message = 'NOT NULL constraint failed' + unique_constraint_failed_message = 'UNIQUE constraint failed' + put_content_type = "application/x-www-form-urlencoded" + + mock_user_test = { + 'username': 'mock username', + 'email': 'self.mock_user@test.com', + 'last_name': 'mock last_name', + } + + mock_lokasi_test = { + 'place_id': 'mock_place_id', + 'name': 'Ma Homie', + 'alamat': 'Jl. Raya Bogor no.2, Jakarta', + 'no_telp': '081212123132', + 'counter': 0, + } + + mock_fasilitas_test = { + 'deskripsi': 'kursi roda lipat', + 'rating': 3, + 'tag': 'KR', + 'disabilitas': 'DF', + 'jumlah': 1, + } + + mock_komentar_test = { + 'deskripsi': 'sangat membantu', + } + + def create_user_test(self, user_dict=mock_user_test): + return User.objects.create_user(**user_dict) + + def get_or_create_user_test(self, user=None, user_dict=mock_user_test): + if user is not None: + return user + try: + return User.objects.get(**user_dict) + except User.DoesNotExist as user_does_not_exist: + return self.create_user_test(user_dict=user_dict) + + def create_lokasi_test(self, lokasi_dict=mock_lokasi_test): + return Lokasi.objects.create(**lokasi_dict) + + def get_or_create_lokasi_test( + self, + lokasi=None, + lokasi_dict=mock_lokasi_test, + ): + if lokasi is not None: + return lokasi + try: + return Lokasi.objects.get(**lokasi_dict) + except Lokasi.DoesNotExist as _: + return self.create_lokasi_test(lokasi_dict=lokasi_dict) + + def create_fasilitas_test( + self, + lokasi=None, + lokasi_dict=mock_lokasi_test, + user=None, + user_dict=mock_user_test, + fasilitas_dict=mock_fasilitas_test + ): + user = self.get_or_create_user_test(user=user, user_dict=user_dict) + + lokasi = self.get_or_create_lokasi_test( + lokasi=lokasi, + lokasi_dict=lokasi_dict + ) + + return Fasilitas.objects.create( + **fasilitas_dict, + user=user, + lokasi=lokasi + ) + + def get_or_create_fasilitas_test( + self, + user=None, + user_dict=mock_user_test, + lokasi=None, + lokasi_dict=mock_lokasi_test, + fasilitas=None, + fasilitas_dict=mock_fasilitas_test, + ): + if fasilitas is not None: + return fasilitas + + user = self.get_or_create_user_test( + user=user, + user_dict=user_dict, + ) + + lokasi = self.get_or_create_lokasi_test( + lokasi=lokasi, + lokasi_dict=lokasi_dict, + ) + + try: + fasilitas = fasilitas if fasilitas is not None \ + else Fasilitas.objects.get( + **fasilitas_dict, + user=user, + lokasi=lokasi + ) + except Fasilitas.DoesNotExist as _: + return self.create_fasilitas_test( + fasilitas_dict=fasilitas_dict, + user=user, + lokasi=lokasi, + ) + + def create_komentar_test( + self, + user=None, + user_dict=mock_user_test, + lokasi=None, + lokasi_dict=mock_lokasi_test, + fasilitas=None, + fasilitas_dict=mock_fasilitas_test, + komentar_dict=mock_komentar_test, + ): + user = self.get_or_create_user_test( + user=user, + user_dict=user_dict, + ) + + lokasi = self.get_or_create_lokasi_test( + lokasi=lokasi, + lokasi_dict=lokasi_dict, + ) + + fasilitas = self.get_or_create_fasilitas_test( + user=user, + lokasi=lokasi, + fasilitas=fasilitas, + fasilitas_dict=fasilitas_dict, + ) + + return Komentar.objects.create( + **komentar_dict, + user=user, + fasilitas=fasilitas, + ) + + def create_likes_test( + self, + user=None, + user_dict=mock_user_test, + lokasi=None, + lokasi_dict=mock_lokasi_test, + fasilitas=None, + fasilitas_dict=mock_fasilitas_test, + ): + return self.create_likes_or_dislikes_test( + Likes, + user=user, + user_dict=user_dict, + lokasi=lokasi, + lokasi_dict=lokasi_dict, + fasilitas=fasilitas, + fasilitas_dict=fasilitas_dict, + ) + + def create_dislikes_test( + self, + user=None, + user_dict=mock_user_test, + lokasi=None, + lokasi_dict=mock_lokasi_test, + fasilitas=None, + fasilitas_dict=mock_fasilitas_test, + ): + return self.create_likes_or_dislikes_test( + Dislikes, + user=user, + user_dict=user_dict, + lokasi=lokasi, + lokasi_dict=lokasi_dict, + fasilitas=fasilitas, + fasilitas_dict=fasilitas_dict, + ) + + def create_likes_or_dislikes_test( + self, + like_or_dislike, + user=None, + user_dict=mock_user_test, + lokasi=None, + lokasi_dict=mock_lokasi_test, + fasilitas=None, + fasilitas_dict=mock_fasilitas_test, + ): + user = self.get_or_create_user_test( + user=user, + user_dict=user_dict, + ) + + lokasi = self.get_or_create_lokasi_test( + lokasi=lokasi, + lokasi_dict=lokasi_dict, + ) + + fasilitas = self.get_or_create_fasilitas_test( + user=user, + lokasi=lokasi, + fasilitas=fasilitas, + fasilitas_dict=fasilitas_dict, + ) + + return like_or_dislike.objects.create( + user=user, + fasilitas=fasilitas, + ) class InformasiFasilitasViewTest(InformasiFasilitasTest): - urlpatterns = [ - path('informasi-fasilitas/', include('informasi_fasilitas.urls')), - ] def setUp(self): - email = 'usersetup@gmail.com' - password = 'hahagotim' - Client().post(REGISTER, - {'name': 'name', - 'email': email, - 'phone_number': 1000000, - 'password': password}) - test_user = User.objects.get(username=email) - test_user.is_active = True - test_user.save() - token_response = Client().post(TOKEN_AUTH, - {'username': email, - 'password': password}) - content = json.loads(token_response.content.decode('utf-8')) - token = content['token'] - client = Client(HTTP_AUTHORIZATION=TOKEN_SUFFIX+token) - - client.post(reverse('add-lokasi'), - self.lokasi_test_1) + super().setUp() + self.user = self.create_user_test() + self.lokasi = self.create_lokasi_test() + self.fasilitas = self.create_fasilitas_test( + user=self.user, + lokasi=self.lokasi + ) + self.client = get_client_login_with_user(self.user) diff --git a/informasi_fasilitas/test_models.py b/informasi_fasilitas/test_models.py index 7445c93..c4798b3 100644 --- a/informasi_fasilitas/test_models.py +++ b/informasi_fasilitas/test_models.py @@ -3,17 +3,29 @@ from django.db.utils import IntegrityError from .test_base import InformasiFasilitasTest from .models import Lokasi, Fasilitas, Komentar, Likes, Dislikes -NOT_NULL_CONSTRAINT_FAILED_MESSAGE = 'NOT NULL constraint failed' - class InformasiFasilitasModelTest(InformasiFasilitasTest): - def test_models_lokasi_not_created(self): + def test_models_lokasi_not_created_attrs_should_not_null(self): with self.assertRaises(IntegrityError) as err_message: - obj = Lokasi(name=None) - obj.save() + Lokasi.objects.create( + name=None, + alamat=None, + no_telp=None, + ) + self.assertTrue(str(err_message.exception).startswith( + self.not_null_constraint_failed_message)) + + def test_models_lokasi_not_created_place_id_should_unique(self): + with self.assertRaises(IntegrityError) as err_message: + Lokasi.objects.create( + place_id=self.mock_lokasi_test['place_id'], + ) + Lokasi.objects.create( + place_id=self.mock_lokasi_test['place_id'], + ) self.assertTrue(str(err_message.exception).startswith( - NOT_NULL_CONSTRAINT_FAILED_MESSAGE)) + self.unique_constraint_failed_message)) def test_models_create_new_lokasi(self): self.create_lokasi_test() @@ -29,7 +41,7 @@ class InformasiFasilitasModelTest(InformasiFasilitasTest): obj = Fasilitas(lokasi=None) obj.save() self.assertTrue(str(err_message.exception).startswith( - NOT_NULL_CONSTRAINT_FAILED_MESSAGE)) + self.not_null_constraint_failed_message)) def test_models_create_new_fasilitas(self): self.create_fasilitas_test() @@ -41,7 +53,7 @@ class InformasiFasilitasModelTest(InformasiFasilitasTest): obj = Komentar(fasilitas=None) obj.save() self.assertTrue(str(err_message.exception).startswith( - NOT_NULL_CONSTRAINT_FAILED_MESSAGE)) + self.not_null_constraint_failed_message)) def test_models_create_new_komentar(self): self.create_komentar_test() @@ -53,7 +65,7 @@ class InformasiFasilitasModelTest(InformasiFasilitasTest): obj = Dislikes(fasilitas=None) obj.save() self.assertTrue(str(err_message.exception).startswith( - NOT_NULL_CONSTRAINT_FAILED_MESSAGE)) + self.not_null_constraint_failed_message)) def test_models_create_new_dislikes(self): self.create_dislikes_test() @@ -65,7 +77,7 @@ class InformasiFasilitasModelTest(InformasiFasilitasTest): obj = Likes(fasilitas=None) obj.save() self.assertTrue(str(err_message.exception).startswith( - NOT_NULL_CONSTRAINT_FAILED_MESSAGE)) + self.not_null_constraint_failed_message)) def test_models_create_new_likes(self): self.create_likes_test() diff --git a/informasi_fasilitas/test_views_fasilitas.py b/informasi_fasilitas/test_views_fasilitas.py index 194192e..7e9fc41 100644 --- a/informasi_fasilitas/test_views_fasilitas.py +++ b/informasi_fasilitas/test_views_fasilitas.py @@ -4,134 +4,149 @@ from django.test import Client from django.urls import reverse from .test_base import InformasiFasilitasViewTest - -PENJELASAN = 'penjelasan fasilitas' +from .models import Fasilitas, Lokasi +from pplbackend.utils import response_decode class FasilitasRelatedViewTest(InformasiFasilitasViewTest): + def setUp(self): + super().setUp() + + self.kwargs_place_id = {'place_id': self.lokasi.place_id} + self.get_list_fasilitas_url = \ + reverse('list-fasilitas', kwargs=self.kwargs_place_id) + self.get_detail_fasilitas_url = \ + reverse('detail-fasilitas', kwargs={ + 'place_id': self.lokasi.place_id, + 'id': self.fasilitas.id, + }) + self.add_fasilitas_url = \ + reverse('add-fasilitas', kwargs=self.kwargs_place_id) + + self.update_fasilitas_url = reverse('update-fasilitas', + kwargs={ + 'place_id': self.lokasi.place_id, + 'id': self.fasilitas.id, + } + ) + def test_can_get_list_fasilitas(self): - self.create_fasilitas_test() - response = Client().get( - reverse('list-fasilitas', kwargs={'nama_lokasi': self.default_lokasi_name})) + response = Client().get(self.get_list_fasilitas_url) self.assertEqual(response.status_code, HTTPStatus.OK) def test_can_get_list_fasilitas_json(self): - fasilitas = self.create_fasilitas_test() - response = Client().get( - reverse('list-fasilitas', kwargs={'nama_lokasi': self.default_lokasi_name})) + response = Client().get(self.get_list_fasilitas_url) content = json.loads(response.content.decode('utf-8')) - expected_json = {'1': {'id': 1, - 'nama_lokasi': self.default_lokasi_name, - 'deskripsi': self.default_deskripsi, - 'creator': self.default_last_name, - 'like': self.default_like, - 'dislike': self.default_dislike, - 'rating': self.default_rating, - 'tag': 'KR', - 'disabilitas': ['DF'], - 'jumlah': 1, - 'image': '/media/' + str(fasilitas.image), - 'is_verified': False} - } - - time = fasilitas.date_time.strftime("%d-%m-%Y %H:%M:%S") + expected_json = { + '1': { + 'id': self.fasilitas.id, + 'nama_lokasi': self.fasilitas.lokasi.name, + 'deskripsi': self.fasilitas.deskripsi, + 'creator': self.fasilitas.user.last_name, + 'like': self.fasilitas.like, + 'dislike': self.fasilitas.dislike, + 'rating': self.fasilitas.rating, + 'tag': 'KR', + 'disabilitas': ['DF'], + 'jumlah': 1, + 'image': '/media/' + str(self.fasilitas.image), + 'is_verified': False, + }, + } + + time = self.fasilitas.date_time.strftime("%d-%m-%Y %H:%M:%S") expected_json["1"]['date_time'] = time self.assertEqual(content, expected_json) def test_cannot_post_list_fasilitas(self): - response = Client().post( - reverse('list-fasilitas', kwargs={'nama_lokasi': self.lokasi_test_1["name"]})) + response = Client().post(self.get_list_fasilitas_url) self.assertEqual(response.status_code, HTTPStatus.METHOD_NOT_ALLOWED) def test_get_list_fasilitas_not_found(self): - response = Client().get( - reverse('list-fasilitas', kwargs={'nama_lokasi': 'Mall'})) + Lokasi.objects.all().delete() + response = Client().get(self.get_list_fasilitas_url) self.assertEqual(response.status_code, HTTPStatus.NOT_FOUND) def test_can_get_detail_fasilitas(self): - self.create_fasilitas_test() - response = Client().get(reverse('detail-fasilitas', - kwargs={'nama_lokasi': self.default_lokasi_name, - 'id': 1})) + response = Client().get(self.get_detail_fasilitas_url) self.assertEqual(response.status_code, HTTPStatus.OK) def test_can_get_detail_fasilitas_json(self): - # TODO - self.create_fasilitas_test() - response = Client().get(reverse('detail-fasilitas', - kwargs={'nama_lokasi': self.default_lokasi_name, - 'id': 1})) + response = Client().get(self.get_detail_fasilitas_url) self.assertEqual(response.status_code, HTTPStatus.OK) def test_cannot_post_detail_fasilitas(self): - fasilitas = self.create_fasilitas_test() - response = Client().post(reverse('detail-fasilitas', - kwargs={'nama_lokasi': self.default_lokasi_name, - 'id': fasilitas.pk})) + response = Client().post(self.get_detail_fasilitas_url) self.assertEqual(response.status_code, HTTPStatus.METHOD_NOT_ALLOWED) def test_get_detail_fasilitas_not_found(self): - response = Client().get(reverse('detail-fasilitas', - kwargs={'nama_lokasi': 'Mall', 'id': 0})) + Fasilitas.objects.all().delete() + response = Client().get(self.get_detail_fasilitas_url) self.assertEqual(response.status_code, HTTPStatus.NOT_FOUND) def test_can_post_add_fasilitas(self): - client = self.client_user_token() - self.create_lokasi_test() - urls = reverse('add-fasilitas', - kwargs={'nama_lokasi': self.default_lokasi_name}) - response = client.post(urls, {'deskripsi': PENJELASAN, - 'rating': 2, - 'tag': 'KR', - 'disabilitas': 'DM', - 'jumlah': 1, - 'image': 'test.jpg'}) + Fasilitas.objects.all().delete() + response = \ + self.client.post(self.add_fasilitas_url, self.mock_fasilitas_test) self.assertEqual(response.status_code, HTTPStatus.CREATED) + def test_add_fasilitas_error_tag_is_blank(self): + Fasilitas.objects.all().delete() + custom_mock_delete_tag = self.mock_fasilitas_test.copy() + custom_mock_delete_tag['tag'] = "" + + response = \ + self.client.post(self.add_fasilitas_url, custom_mock_delete_tag) + response_json = response_decode(response) + expected_json = {'response': 'tag cannot be null'} + + self.assertEqual(response.status_code, HTTPStatus.BAD_REQUEST) + self.assertEqual(response_json, expected_json) + + def test_add_fasilitas_error_tag_is_null(self): + Fasilitas.objects.all().delete() + custom_mock_delete_tag = self.mock_fasilitas_test.copy() + del custom_mock_delete_tag['tag'] + + response = \ + self.client.post(self.add_fasilitas_url, custom_mock_delete_tag) + response_json = response_decode(response) + expected_json = {'response': "Bad Request. 'tag' key is needed"} + + self.assertEqual(response.status_code, HTTPStatus.BAD_REQUEST) + self.assertEqual(response_json, expected_json) + + def test_post_fasilitas_already_exist(self): + response = \ + self.client.post(self.add_fasilitas_url, self.mock_fasilitas_test) + response_json = response_decode(response) + expected_json = {'response': 'fasilitas already exist'} + + self.assertEqual(response.status_code, HTTPStatus.OK) + self.assertEqual(response_json, expected_json) + def test_cannot_get_add_fasilitas(self): - client = self.client_user_token() - self.create_lokasi_test() - response = client.get( - reverse('add-fasilitas', kwargs={'nama_lokasi': self.default_lokasi_name})) + response = self.client.get(self.add_fasilitas_url) self.assertEqual(response.status_code, HTTPStatus.METHOD_NOT_ALLOWED) - def test_post_add_fasilitas_not_found(self): - client = self.client_user_token() - urls = reverse('add-fasilitas', kwargs={'nama_lokasi': 'Mall'}) - response = client.post(urls, {'deskripsi': PENJELASAN, - 'rating': 2}) + def test_post_add_fasilitas_but_lokasi_not_found(self): + Lokasi.objects.all().delete() + response = \ + self.client.post(self.add_fasilitas_url, self.mock_fasilitas_test) self.assertEqual(response.status_code, HTTPStatus.NOT_FOUND) def test_post_add_fasilitas_cannot_authorized(self): - client = Client() - self.create_lokasi_test() - urls = reverse('add-fasilitas', - kwargs={'nama_lokasi': self.default_lokasi_name}) - response = client.post(urls, {'deskripsi': PENJELASAN, - 'rating': 2, - 'tag': 'KR'}) + response = \ + Client().post(self.add_fasilitas_url, self.mock_fasilitas_test) self.assertEqual(response.status_code, HTTPStatus.UNAUTHORIZED) def test_put_update_detail_fasilitas_success(self): - client = self.client_user_token() - self.create_lokasi_test() - urls_post = reverse( - 'add-fasilitas', kwargs={'nama_lokasi': self.default_lokasi_name}) - data_post = {'deskripsi': PENJELASAN, - 'rating': 2, - 'tag': 'KR', - 'disabilitas': 'DI', - 'jumlah': 1} - response = client.post(urls_post, data=data_post) - - response_json = json.loads(response.content.decode("utf-8")) - fasilitas_id = response_json["id"] - urls_put = reverse('update-fasilitas', - kwargs={'nama_lokasi': 'Mall', "id": fasilitas_id}) - send_data = {'deskripsi': 'penjelasan fasilitases', - 'rating': 3, - 'tag': 'KR'} - response = client.put(urls_put, data=send_data, - content_type="application/x-www-form-urlencoded") + send_data = { + 'deskripsi': 'penjelasan fasilitases', + 'rating': 3, + 'tag': 'KR', + } + response = self.client.put(self.update_fasilitas_url, data=send_data, + content_type='application/x-www-form-urlencoded') self.assertEqual(response.status_code, HTTPStatus.ACCEPTED) diff --git a/informasi_fasilitas/test_views_komentar.py b/informasi_fasilitas/test_views_komentar.py index 6c50d89..8a90fd8 100644 --- a/informasi_fasilitas/test_views_komentar.py +++ b/informasi_fasilitas/test_views_komentar.py @@ -4,71 +4,51 @@ from django.urls import reverse from .test_base import InformasiFasilitasViewTest from .models import Komentar - -COMMENT = "halo saya komen" +from pplbackend.utils import response_decode +from .views import TIME_FORMAT class KomentarRelatedViewTest(InformasiFasilitasViewTest): + def setUp(self): + super().setUp() + self.kwargs_add_or_list_komentar = { + 'place_id': self.lokasi.place_id, + 'id': self.fasilitas.id, + } + self.add_komentar_url = \ + reverse('add-komentar', kwargs=self.kwargs_add_or_list_komentar) + + self.list_komentar_url = \ + reverse('list-komentar', kwargs=self.kwargs_add_or_list_komentar) def test_can_comment_facility(self): - client = self.client_user_token() - fasilitas = self.create_fasilitas_test() - urls = reverse('add-komentar', - kwargs={'nama_lokasi': self.default_lokasi_name, - 'id': 1, - }) - comment_data = {"deskripsi": COMMENT} - client.post(urls, comment_data) - count = Komentar.objects.filter(fasilitas=fasilitas).count() - self.assertEqual(count, 1) - - def test_can_comment_facility_status_code(self): - client = self.client_user_token() - self.create_fasilitas_test() - urls = reverse('add-komentar', - kwargs={'nama_lokasi': self.default_lokasi_name, - 'id': 1, - }) - comment_data = {'deskripsi': COMMENT} - response = client.post(urls, comment_data) + response = \ + self.client.post(self.add_komentar_url, self.mock_komentar_test) self.assertEqual(response.status_code, HTTPStatus.CREATED) - def test_not_comment_facility_empty(self): - client = self.client_user_token() - self.create_fasilitas_test() - urls = reverse('add-komentar', - kwargs={'nama_lokasi': self.default_lokasi_name, - 'id': 1, - }) - comment_data = {} - response = client.post(urls, comment_data) + count = Komentar.objects.filter(fasilitas=self.fasilitas).count() + self.assertEqual(count, 1) + + def test_cannot_comment_facility_if_deskripsi_empty(self): + response = self.client.post(self.add_komentar_url, {}) self.assertEqual(response.status_code, HTTPStatus.BAD_REQUEST) - def test_not_comment_facility_empty_response(self): - client = self.client_user_token() - self.create_fasilitas_test() - urls = reverse('add-komentar', - kwargs={'nama_lokasi': self.default_lokasi_name, - 'id': 1, - }) - comment_data = {} - response = client.post(urls, comment_data) - response_json = json.loads(response.content.decode('utf-8')) + response_json = response_decode(response) expected_json = {"response": "Bad Request. 'deskripsi' key is needed"} self.assertEqual(expected_json, response_json) def test_get_list_komentar(self): - client = self.client_user_token() - self.create_fasilitas_test() - urls = reverse('add-komentar', - kwargs={'nama_lokasi': self.default_lokasi_name, - 'id': 1, - }) - comment_data = {"deskripsi": COMMENT} - client.post(urls, comment_data) - urls_get = reverse('list-komentar', - kwargs={'nama_lokasi': self.default_lokasi_name, - 'id': 1, - }) - response = client.get(urls_get) + komentar = self.create_komentar_test(fasilitas=self.fasilitas) + response = self.client.get(self.list_komentar_url) self.assertEqual(response.status_code, HTTPStatus.OK) + + response_json = response_decode(response) + expected_json = { + '1': { + 'id': komentar.id, + 'deskripsi': komentar.deskripsi, + 'creator': komentar.user.last_name, + 'date_time': komentar.date_time.strftime(TIME_FORMAT), + } + } + self.assertEqual(response_json, expected_json) diff --git a/informasi_fasilitas/test_views_like_dislike.py b/informasi_fasilitas/test_views_like_dislike.py index adf8d90..305d8d0 100644 --- a/informasi_fasilitas/test_views_like_dislike.py +++ b/informasi_fasilitas/test_views_like_dislike.py @@ -5,168 +5,82 @@ from django.urls import reverse from .test_base import InformasiFasilitasViewTest from .models import Likes, Dislikes +from pplbackend.utils import response_decode class LikeDislikeRelatedViewTest(InformasiFasilitasViewTest): + def setUp(self): + super().setUp() + self.kwargs_like_or_dislike = { + 'place_id': self.lokasi.place_id, + 'id': self.fasilitas.id, + } + self.kwargs_like = { + 'operation': 'like', + } + self.kwargs_like.update(self.kwargs_like_or_dislike) + + self.kwargs_dislike = { + 'operation': 'dislike', + } + self.kwargs_dislike.update(self.kwargs_like_or_dislike) + + self.like_url = \ + reverse('update-like-fasilitas', kwargs=self.kwargs_like) + + self.dislike_url = \ + reverse('update-like-fasilitas', kwargs=self.kwargs_dislike) + def test_can_like_facility(self): - client = self.client_user_token() - fasilitas = self.create_fasilitas_test() - urls = reverse('update-like-fasilitas', - kwargs={'nama_lokasi': self.default_lokasi_name, - 'id': 1, - 'operation': "like" - }) - client.put(urls) - count = Likes.objects.filter(fasilitas=fasilitas).count() - self.assertEqual(count, 1) + response = self.client.put(self.like_url) + count = Likes.objects.filter(fasilitas=self.fasilitas).count() - def test_can_like_facility_status_code(self): - client = self.client_user_token() - self.create_fasilitas_test() - urls = reverse('update-like-fasilitas', - kwargs={'nama_lokasi': self.default_lokasi_name, - 'id': 1, - 'operation': "like" - }) - response = client.put(urls) + self.assertEqual(count, 1) self.assertEqual(response.status_code, HTTPStatus.CREATED) - def test_like_like_facility_status_code(self): - client = self.client_user_token() - self.create_fasilitas_test() - urls = reverse('update-like-fasilitas', - kwargs={'nama_lokasi': self.default_lokasi_name, - 'id': 1, - 'operation': "like" - }) - response = client.put(urls) - response = client.put(urls) # like and like - self.assertEqual(response.status_code, HTTPStatus.ACCEPTED) - def test_like_like_facility_response(self): - client = self.client_user_token() - self.create_fasilitas_test() - urls = reverse('update-like-fasilitas', - kwargs={'nama_lokasi': self.default_lokasi_name, - 'id': 1, - 'operation': "like" - }) - response = client.put(urls) - response = client.put(urls) # like and like - json_response = json.loads(response.content.decode("utf-8")) + self.client.put(self.like_url) + response = self.client.put(self.like_url) # like and like + + json_response = response_decode(response) expected_json = {"response": "You have already liked this facility"} - self.assertEqual(json_response, expected_json) + count = Likes.objects.filter(fasilitas=self.fasilitas).count() - def test_like_like_facility_only_count_one(self): - client = self.client_user_token() - fasilitas = self.create_fasilitas_test() - urls = reverse('update-like-fasilitas', - kwargs={'nama_lokasi': self.default_lokasi_name, - 'id': 1, - 'operation': "like" - }) - client.put(urls) - client.put(urls) # like and like - count = Likes.objects.filter(fasilitas=fasilitas).count() + self.assertEqual(json_response, expected_json) self.assertEqual(count, 1) def test_like_dislike_facility_count_like(self): - client = self.client_user_token() - fasilitas = self.create_fasilitas_test() - urls_like = reverse('update-like-fasilitas', - kwargs={'nama_lokasi': self.default_lokasi_name, - 'id': 1, - 'operation': "like" - }) - urls_dislike = reverse('update-like-fasilitas', - kwargs={'nama_lokasi': self.default_lokasi_name, - 'id': 1, - 'operation': "dislike" - }) - client.put(urls_like) - - client.put(urls_dislike) # like and dislike - count = Likes.objects.filter(fasilitas=fasilitas).count() + self.client.put(self.like_url) + self.client.put(self.dislike_url) # like and dislike + count = Likes.objects.filter(fasilitas=self.fasilitas).count() self.assertEqual(count, 0) def test_can_dislike_facility(self): - client = self.client_user_token() - fasilitas = self.create_fasilitas_test() - urls = reverse('update-like-fasilitas', - kwargs={'nama_lokasi': self.default_lokasi_name, - 'id': 1, - 'operation': "dislike" - }) - client.put(urls) - count = Dislikes.objects.filter(fasilitas=fasilitas).count() - self.assertEqual(count, 1) + response = self.client.put(self.dislike_url) + count = Dislikes.objects.filter(fasilitas=self.fasilitas).count() - def test_can_dislike_facility_status_code(self): - client = self.client_user_token() - self.create_fasilitas_test() - urls = reverse('update-like-fasilitas', - kwargs={'nama_lokasi': self.default_lokasi_name, - 'id': 1, - 'operation': "dislike" - }) - response = client.put(urls) + self.assertEqual(count, 1) self.assertEqual(response.status_code, HTTPStatus.CREATED) def test_dislike_dislike_facility_status_code(self): - client = self.client_user_token() - self.create_fasilitas_test() - urls = reverse('update-like-fasilitas', - kwargs={'nama_lokasi': self.default_lokasi_name, - 'id': 1, - 'operation': "dislike" - }) - response = client.put(urls) - response = client.put(urls) # Dislike and dislike - self.assertEqual(response.status_code, HTTPStatus.ACCEPTED) + self.client.put(self.dislike_url) - def test_dislike_dislike_facility_response(self): - client = self.client_user_token() - self.create_fasilitas_test() - urls = reverse('update-like-fasilitas', - kwargs={'nama_lokasi': self.default_lokasi_name, - 'id': 1, - 'operation': "dislike" - }) - response = client.put(urls) - response = client.put(urls) # Dislike and dislike - json_response = json.loads(response.content.decode("utf-8")) + response = self.client.put(self.dislike_url) # Dislike and dislike + json_response = response_decode(response) expected_json = {"response": "You have already disliked this facility"} + + self.assertEqual(response.status_code, HTTPStatus.ACCEPTED) self.assertEqual(json_response, expected_json) def test_dislike_dislike_facility_only_count_one(self): - client = self.client_user_token() - fasilitas = self.create_fasilitas_test() - urls = reverse('update-like-fasilitas', - kwargs={'nama_lokasi': self.default_lokasi_name, - 'id': 1, - 'operation': "dislike" - }) - client.put(urls) - client.put(urls) # Dislike and dislike - count = Dislikes.objects.filter(fasilitas=fasilitas).count() + self.client.put(self.dislike_url) + self.client.put(self.dislike_url) # Dislike and dislike + count = Dislikes.objects.filter(fasilitas=self.fasilitas).count() self.assertEqual(count, 1) def test_dislike_like_facility_count_dislike(self): - client = self.client_user_token() - fasilitas = self.create_fasilitas_test() - urls_like = reverse('update-like-fasilitas', - kwargs={'nama_lokasi': self.default_lokasi_name, - 'id': 1, - 'operation': "like" - }) - urls_dislike = reverse('update-like-fasilitas', - kwargs={'nama_lokasi': self.default_lokasi_name, - 'id': 1, - 'operation': "dislike" - }) - - client.put(urls_dislike) - client.put(urls_like) - - count = Dislikes.objects.filter(fasilitas=fasilitas).count() + self.client.put(self.dislike_url) + self.client.put(self.like_url) + + count = Dislikes.objects.filter(fasilitas=self.fasilitas).count() self.assertEqual(count, 0) diff --git a/informasi_fasilitas/test_views_lokasi.py b/informasi_fasilitas/test_views_lokasi.py index cf525dd..aa9b8bb 100644 --- a/informasi_fasilitas/test_views_lokasi.py +++ b/informasi_fasilitas/test_views_lokasi.py @@ -9,11 +9,23 @@ from .test_base import InformasiFasilitasViewTest from .models import Lokasi from .serializers import LokasiSerializer +from pplbackend.utils import response_decode + class LokasiRelatedViewTest(InformasiFasilitasViewTest): + def setUp(self): + super().setUp() + self.kwargs_detail_or_update = {'place_id': self.lokasi.place_id} + self.list_lokasi_url = reverse('lokasi-list') + self.detail_lokasi_url = reverse('lokasi-details', + kwargs=self.kwargs_detail_or_update) + self.add_lokasi_url = reverse('add-lokasi') + self.update_lokasi_url = reverse('update-lokasi', + kwargs=self.kwargs_detail_or_update) + def test_LokasiSerializer_valid(self): - serializer = LokasiSerializer(data=self.lokasi_test_1) + serializer = LokasiSerializer(data=self.mock_lokasi_test) self.assertTrue(serializer.is_valid) def test_amount_lokasi(self): @@ -21,90 +33,88 @@ class LokasiRelatedViewTest(InformasiFasilitasViewTest): self.assertEqual(count, 1) def test_can_get_lokasi_list_url(self): - response = Client().get(reverse('lokasi-list')) + response = Client().get(self.list_lokasi_url) self.assertEqual(response.status_code, HTTPStatus.OK) def test_get_lokasi_list_json(self): - response = Client().get(reverse('lokasi-list')) - content = json.loads(response.content.decode('utf-8')) - expected_first_entry = copy.deepcopy(self.lokasi_test_1) + response = Client().get(self.list_lokasi_url) + content = response_decode(response) + + expected_first_entry = self.mock_lokasi_test.copy() expected_first_entry["image"] = None expected_json = [expected_first_entry] self.assertEqual(content, expected_json) def test_cannot_post_lokasi_list_url(self): - response = Client().post(reverse('lokasi-list')) + response = Client().post(self.list_lokasi_url) self.assertEqual(response.status_code, HTTPStatus.METHOD_NOT_ALLOWED) def test_can_get_lokasi_details(self): - response = Client().get( - reverse('lokasi-details', kwargs={'nama_lokasi': self.lokasi_test_1["name"]})) - self.assertEqual(response.status_code, HTTPStatus.OK) + response = Client().get(self.detail_lokasi_url) + content = response_decode(response) - def test_can_get_lokasi_details_json(self): - response = Client().get( - reverse('lokasi-details', kwargs={'nama_lokasi': self.lokasi_test_1["name"]})) - content = json.loads(response.content.decode('utf-8')) - expected_json = copy.deepcopy(self.lokasi_test_1) + expected_json = self.mock_lokasi_test.copy() expected_json["image"] = None - # expected_json["id"] = 35 + + self.assertEqual(response.status_code, HTTPStatus.OK) self.assertEqual(content, expected_json) def test_cannot_post_lokasi_details(self): - response = Client().post( - reverse('lokasi-details', kwargs={'nama_lokasi': self.lokasi_test_1["name"]})) + response = Client().post(self.detail_lokasi_url) self.assertEqual(response.status_code, HTTPStatus.METHOD_NOT_ALLOWED) def test_get_lokasi_details_not_found(self): - response = Client().get( - reverse('lokasi-details', kwargs={'nama_lokasi': 'Mall'})) + Lokasi.objects.all().delete() + response = Client().get(self.detail_lokasi_url) self.assertEqual(response.status_code, HTTPStatus.NOT_FOUND) def test_cannot_get_add_lokasi(self): - client = self.client_user_token() - response = client.get(reverse('add-lokasi')) + response = self.client.get(self.add_lokasi_url) self.assertEqual(response.status_code, HTTPStatus.METHOD_NOT_ALLOWED) def test_can_post_add_lokasi(self): - client = self.client_user_token() Lokasi.objects.all().delete() - response = client.post(reverse('add-lokasi'), self.lokasi_test_1) - response_json = json.loads(response.content.decode("utf-8")) - expected_json = copy.deepcopy(self.lokasi_test_1) - expected_json['id'] = 2 + response = \ + self.client.post(self.add_lokasi_url, self.mock_lokasi_test) + response_json = response_decode(response) + expected_json = self.mock_lokasi_test.copy() expected_json['image'] = None + self.assertEqual(response_json, expected_json) self.assertEqual(response.status_code, HTTPStatus.CREATED) - def test_post_add_lokasi_missing_key(self): - test_lokasi = copy.deepcopy(self.lokasi_test_1) - del test_lokasi["name"] - client = self.client_user_token() - response = client.post(reverse('add-lokasi'), test_lokasi) + def test_cannot_post_lokasi_place_id_should_unique(self): + response = \ + self.client.post(self.add_lokasi_url, self.mock_lokasi_test) + response_json = response_decode(response) + expected_json = \ + {'place_id': ['lokasi with this place id already exists.']} + + self.assertEqual(response_json, expected_json) self.assertEqual(response.status_code, HTTPStatus.BAD_REQUEST) - def test_post_add_lokasi_missing_key_return_message(self): - test_lokasi = copy.deepcopy(self.lokasi_test_1) + def test_post_add_lokasi_missing_key(self): + Lokasi.objects.all().delete() + test_lokasi = self.mock_lokasi_test.copy() del test_lokasi["name"] - client = self.client_user_token() - response = client.post(reverse('add-lokasi'), test_lokasi) - response_json = json.loads(response.content.decode('utf-8')) + response = self.client.post(self.add_lokasi_url, test_lokasi) + + response_json = response_decode(response) expected_json = {"name": ["This field is required."]} + + self.assertEqual(response.status_code, HTTPStatus.BAD_REQUEST) self.assertEqual(response_json, expected_json) def test_put_update_detail_lokasi_success(self): - client = self.client_user_token() - urls = reverse('update-lokasi', - kwargs={'nama_lokasi': self.lokasi_test_1["name"]}) - response = client.put(urls, data=urlencode({'no_telp': '0000000121', }), - content_type="application/x-www-form-urlencoded") + response = self.client.put(self.update_lokasi_url, + data=urlencode({'no_telp': '0000000121'}), + content_type=self.put_content_type) self.assertEqual(response.status_code, HTTPStatus.ACCEPTED) def test_put_update_detail_lokasi_missing_key(self): - client = self.client_user_token() - - urls = reverse('update-lokasi', - kwargs={'nama_lokasi': self.lokasi_test_1["name"]}) - response = client.put(urls, data={"latitude": 100}, - content_type="application/x-www-form-urlencoded") + response = self.client.put( + self.update_lokasi_url, + data={"latitude": 100}, + content_type=self.put_content_type + ) self.assertEqual(response.status_code, HTTPStatus.BAD_REQUEST) -- GitLab From b4daf098e80adc44dde22e25eb549cbd5a721957 Mon Sep 17 00:00:00 2001 From: ariqbasyar Date: Sun, 28 Mar 2021 18:39:20 +0700 Subject: [PATCH 22/97] [RED] utils for unittest, get client from user --- pplbackend/utils.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 pplbackend/utils.py diff --git a/pplbackend/utils.py b/pplbackend/utils.py new file mode 100644 index 0000000..dac4c0a --- /dev/null +++ b/pplbackend/utils.py @@ -0,0 +1,24 @@ +from rest_framework.test import APIClient +from rest_framework_simplejwt.tokens import RefreshToken +from rest_framework.authtoken.models import Token + +from django.urls import reverse +from django.test import Client + +import json + +def get_client_login_with_user(user): + token, _ = Token.objects.get_or_create(user=user) + client = Client(HTTP_AUTHORIZATION=f'token {token.key}') + + return client + + +def response_decode( + response, +): + try: + content = json.loads(response.content.decode('utf-8')) + except json.decoder.JSONDecodeError as json_error: + return None + return content -- GitLab From ce60339e59f6b1aadcd7fef0a43410a7bf3c6b8b Mon Sep 17 00:00:00 2001 From: ariqbasyar Date: Sun, 28 Mar 2021 18:46:57 +0700 Subject: [PATCH 23/97] [RED] remove allow failure keyword on UnitTest stage gitlab ci] --- .gitlab-ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index a5172b2..f7cad09 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -7,7 +7,6 @@ stages: UnitTest: image: python:3.7 stage: test - allow_failure: true coverage: '/TOTAL\s+\d+\s+\d+\s+(\d+)%/' before_script: - pip install -r requirements.txt -- GitLab From cd6eccadac5c0f9b24215cc777ac7b77175e229e Mon Sep 17 00:00:00 2001 From: ariqbasyar Date: Sun, 28 Mar 2021 19:06:00 +0700 Subject: [PATCH 24/97] [GREEN] implemented models for PBI add lokasi - restructured models, added place_id and removed latitude and longitude attrs, default place_id is randomized with 40 chars - remove latitude and longitude from serializer and change id to place_id - change url dispatcher from string nama_lokasi to place_id and modified the views as well - added migration --- .../migrations/0009_auto_20210327_1436.py | 32 ++++++++++ informasi_fasilitas/models.py | 13 ++-- informasi_fasilitas/serializers.py | 2 +- informasi_fasilitas/urls.py | 44 ++++++------- informasi_fasilitas/views.py | 62 +++++++++---------- 5 files changed, 92 insertions(+), 61 deletions(-) create mode 100644 informasi_fasilitas/migrations/0009_auto_20210327_1436.py diff --git a/informasi_fasilitas/migrations/0009_auto_20210327_1436.py b/informasi_fasilitas/migrations/0009_auto_20210327_1436.py new file mode 100644 index 0000000..6fc27b9 --- /dev/null +++ b/informasi_fasilitas/migrations/0009_auto_20210327_1436.py @@ -0,0 +1,32 @@ +# Generated by Django 3.1.7 on 2021-03-27 14:36 + +from django.db import migrations, models +import informasi_fasilitas.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('informasi_fasilitas', '0008_lokasi_counter'), + ] + + operations = [ + migrations.RemoveField( + model_name='lokasi', + name='latitude', + ), + migrations.RemoveField( + model_name='lokasi', + name='longitude', + ), + migrations.AddField( + model_name='lokasi', + name='place_id', + field=models.TextField(default=informasi_fasilitas.models._default_lokasi_place_id, unique=True), + ), + migrations.AlterField( + model_name='lokasi', + name='image', + field=models.TextField(null=True), + ), + ] diff --git a/informasi_fasilitas/models.py b/informasi_fasilitas/models.py index 8a6aa2e..439ece4 100644 --- a/informasi_fasilitas/models.py +++ b/informasi_fasilitas/models.py @@ -2,6 +2,9 @@ from django.db import models from django.contrib.auth.models import User from multiselectfield import MultiSelectField +import string +import random + # Create your models here. KURSI_RODA = 'KR' LIFT_DISABILITAS = 'LF' @@ -40,17 +43,17 @@ JENIS_DISABILITAS = ( ('DS', 'Disabilitas Sensorik'), ) +def _default_lokasi_place_id(): + return \ + ''.join([random.choice(string.ascii_letters) for _ in range(40)]) class Lokasi(models.Model): + place_id = models.TextField(unique=True, default=_default_lokasi_place_id) name = models.CharField(max_length=50) - latitude = models.FloatField() - longitude = models.FloatField() alamat = models.CharField(max_length=100) no_telp = models.CharField(max_length=16) - image = models.ImageField(upload_to='lokasi/') + image = models.TextField(null=True) counter = models.PositiveIntegerField(default=0) - objects = models.Manager() - POINT_FIELD = ""+str(latitude)+","+str(longitude) def __str__(self): return self.name diff --git a/informasi_fasilitas/serializers.py b/informasi_fasilitas/serializers.py index ecfa9c2..8ae0076 100644 --- a/informasi_fasilitas/serializers.py +++ b/informasi_fasilitas/serializers.py @@ -7,4 +7,4 @@ class LokasiSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = Lokasi - fields = ('id', 'name', 'latitude', 'longitude', 'alamat', 'no_telp', 'image', 'counter') \ No newline at end of file + fields = ('place_id', 'name', 'alamat', 'no_telp', 'image', 'counter') diff --git a/informasi_fasilitas/urls.py b/informasi_fasilitas/urls.py index f7c755e..e2e2d57 100644 --- a/informasi_fasilitas/urls.py +++ b/informasi_fasilitas/urls.py @@ -3,34 +3,34 @@ from . import views from .views import * urlpatterns = [ - path('lokasi/list/', views.lokasi_list, name='lokasi-list'), + path('lokasi/list/', views.lokasi_list, name='lokasi-list'), - path('lokasi/detail//', - views.lokasi_details, name='lokasi-details'), + path('lokasi/detail//', + views.lokasi_details, name='lokasi-details'), - path('lokasi/update-detail//', - views.update_lokasi_details, name='update-lokasi'), + path('lokasi/update-detail//', + views.update_lokasi_details, name='update-lokasi'), - path('lokasi/add/', views.add_lokasi, name='add-lokasi'), + path('lokasi/add/', views.add_lokasi, name='add-lokasi'), - path('lokasi/add-fasilitas//', - views.add_fasilitas, name='add-fasilitas'), + path('lokasi/add-fasilitas//', + views.add_fasilitas, name='add-fasilitas'), - path('lokasi/list-fasilitas//', - views.list_fasilitas, name='list-fasilitas'), + path('lokasi/list-fasilitas//', + views.list_fasilitas, name='list-fasilitas'), - path('lokasi/detail-fasilitas///', - views.detail_fasilitas, name='detail-fasilitas'), + path('lokasi/detail-fasilitas///', + views.detail_fasilitas, name='detail-fasilitas'), - path('lokasi/add-komentar///', - views.add_komentar, name='add-komentar'), + path('lokasi/add-komentar///', + views.add_komentar, name='add-komentar'), - path('lokasi/list-komentar///', - views.list_komentar, name='list-komentar'), - - path('lokasi/update-fasilitas///', - views.update_fasilitas, name='update-fasilitas'), - - path('lokasi/like-fasilitas////', - views.update_like_fasilitas, name='update-like-fasilitas'), + path('lokasi/list-komentar///', + views.list_komentar, name='list-komentar'), + + path('lokasi/update-fasilitas///', + views.update_fasilitas, name='update-fasilitas'), + + path('lokasi/like-fasilitas////', + views.update_like_fasilitas, name='update-like-fasilitas'), ] diff --git a/informasi_fasilitas/views.py b/informasi_fasilitas/views.py index d799fce..d0e41c0 100644 --- a/informasi_fasilitas/views.py +++ b/informasi_fasilitas/views.py @@ -37,9 +37,9 @@ def lokasi_list(request): @api_view(['GET']) @authentication_classes([]) @permission_classes([]) -def lokasi_details(request, nama_lokasi): +def lokasi_details(request, place_id): try: - lokasi = Lokasi.objects.get(name=nama_lokasi) + lokasi = Lokasi.objects.get(place_id=place_id) serializer = LokasiSerializer(lokasi) return JsonResponse(serializer.data, safe=False, status=HTTPStatus.OK) except ObjectDoesNotExist as err: @@ -50,23 +50,21 @@ def lokasi_details(request, nama_lokasi): @authentication_classes([TokenAuthentication]) @permission_classes([IsAuthenticated]) def add_lokasi(request): - try: - data = request.data - serializer = LokasiSerializer(data=data) - if serializer.is_valid(): - serializer.save() - return JsonResponse(serializer.data, status=HTTPStatus.CREATED) - else: - return JsonResponse(serializer.errors, status=HTTPStatus.BAD_REQUEST) - except Exception as err: - return JsonResponse({'response': str(err)}, status=HTTPStatus.NOT_FOUND) + data = request.data + serializer = LokasiSerializer(data=data) + if serializer.is_valid(): + serializer.save() + return JsonResponse(serializer.data, status=HTTPStatus.CREATED) + else: + return JsonResponse(serializer.errors, + status=HTTPStatus.BAD_REQUEST) @api_view(['PUT']) @authentication_classes([TokenAuthentication]) @permission_classes([IsAuthenticated]) -def update_lokasi_details(request, nama_lokasi): +def update_lokasi_details(request, place_id): try: - lokasi = Lokasi.objects.get(name=nama_lokasi) + lokasi = Lokasi.objects.get(place_id=place_id) lokasi.no_telp = request.data['no_telp'] lokasi.counter = F('counter') + 1 lokasi.save() @@ -80,9 +78,9 @@ def update_lokasi_details(request, nama_lokasi): @api_view(['GET']) @authentication_classes([]) @permission_classes([]) -def list_fasilitas(request, nama_lokasi): +def list_fasilitas(request, place_id): try: - lokasi = Lokasi.objects.get(name=nama_lokasi) + lokasi = Lokasi.objects.get(place_id=place_id) list_fasilitas = Fasilitas.objects.filter(lokasi=lokasi) return_json = {} for fasilitas in list_fasilitas: @@ -114,10 +112,9 @@ def list_fasilitas(request, nama_lokasi): @api_view(['POST']) @authentication_classes([TokenAuthentication]) @permission_classes([IsAuthenticated]) -def add_fasilitas(request, nama_lokasi): +def add_fasilitas(request, place_id): try: - lokasi = Lokasi.objects.get(name=nama_lokasi) - user = User.objects.get(email=str(request.user)) + lokasi = Lokasi.objects.get(place_id=place_id) deskripsi = request.POST['deskripsi'] rating = request.POST['rating'] tag = request.POST['tag'] @@ -133,7 +130,7 @@ def add_fasilitas(request, nama_lokasi): if 'image' in request.FILES.keys(): image = request.FILES['image'] fasilitas = Fasilitas.objects.create(lokasi=lokasi, - user=user, + user=request.user, deskripsi=deskripsi, rating=rating, tag=tag, @@ -152,16 +149,16 @@ def add_fasilitas(request, nama_lokasi): @api_view(['GET']) @authentication_classes([]) @permission_classes([]) -def detail_fasilitas(request, nama_lokasi, id): +def detail_fasilitas(request, place_id, id): try: - lokasi = Lokasi.objects.get(name=nama_lokasi) + lokasi = Lokasi.objects.get(place_id=place_id) fasilitas = Fasilitas.objects.get(lokasi=lokasi, id=id) user = fasilitas.user fasilitas.like = Likes.objects.filter(fasilitas=fasilitas).count() fasilitas.dislike = Dislikes.objects.filter( fasilitas=fasilitas).count() return_json = {"id": fasilitas.id, - "nama_lokasi": lokasi.name, + "place_id": lokasi.name, "deskripsi": fasilitas.deskripsi, "creator": user.last_name, "creator_email": user.email, @@ -185,9 +182,9 @@ def detail_fasilitas(request, nama_lokasi, id): @api_view(['PUT']) @authentication_classes([TokenAuthentication]) @permission_classes([IsAuthenticated]) -def update_fasilitas(request, nama_lokasi, id): +def update_fasilitas(request, place_id, id): try: - lokasi = Lokasi.objects.get(name=nama_lokasi) + lokasi = Lokasi.objects.get(place_id=place_id) fasilitas = Fasilitas.objects.get(lokasi=lokasi, id=id) user_creator = fasilitas.user desc = fasilitas.deskripsi @@ -226,9 +223,9 @@ def update_fasilitas(request, nama_lokasi, id): @api_view(['PUT']) @authentication_classes([TokenAuthentication]) @permission_classes([IsAuthenticated]) -def update_like_fasilitas(request, nama_lokasi, id, operation): +def update_like_fasilitas(request, place_id, id, operation): try: - lokasi = Lokasi.objects.get(name=nama_lokasi) + lokasi = Lokasi.objects.get(place_id=place_id) fasilitas = Fasilitas.objects.get(lokasi=lokasi, id=id) user = request.user @@ -267,14 +264,13 @@ def update_like_fasilitas(request, nama_lokasi, id, operation): @api_view(['POST']) @authentication_classes([TokenAuthentication]) @permission_classes([IsAuthenticated]) -def add_komentar(request, nama_lokasi, id): +def add_komentar(request, place_id, id): try: - lokasi = Lokasi.objects.get(name=nama_lokasi) + lokasi = Lokasi.objects.get(place_id=place_id) fasilitas = Fasilitas.objects.get(lokasi=lokasi, id=id) - user = User.objects.get(email=str(request.user)) deskripsi = request.POST['deskripsi'] komentar = Komentar.objects.create(fasilitas=fasilitas, - user=user, + user=request.user, deskripsi=deskripsi) return JsonResponse({'response': 'komentar added', 'id': komentar.id, @@ -288,9 +284,9 @@ def add_komentar(request, nama_lokasi, id): @api_view(['GET']) @authentication_classes([]) @permission_classes([]) -def list_komentar(request, nama_lokasi, id): +def list_komentar(request, place_id, id): try: - lokasi = Lokasi.objects.get(name=nama_lokasi) + lokasi = Lokasi.objects.get(place_id=place_id) fasilitas = Fasilitas.objects.get(lokasi=lokasi, id=id) list_komentar = Komentar.objects.filter(fasilitas=fasilitas) return_json = {} -- GitLab From 3fe8cef45515b950abfcdb77c25353a5cfe8da24 Mon Sep 17 00:00:00 2001 From: ariqbasyar Date: Mon, 29 Mar 2021 15:09:57 +0700 Subject: [PATCH 25/97] [RED] tests for new Lokasi models - test remove alamat, name, no_telp - test timestamp with auto_now=True - test place_id is required on LokasiSerializer --- informasi_fasilitas/test_base.py | 5 ----- informasi_fasilitas/test_models.py | 22 ++++++++++----------- informasi_fasilitas/test_views_fasilitas.py | 2 +- informasi_fasilitas/test_views_lokasi.py | 4 ++-- 4 files changed, 14 insertions(+), 19 deletions(-) diff --git a/informasi_fasilitas/test_base.py b/informasi_fasilitas/test_base.py index 6ba0967..a759bf4 100644 --- a/informasi_fasilitas/test_base.py +++ b/informasi_fasilitas/test_base.py @@ -7,8 +7,6 @@ from django.urls import reverse, path, include from django.core.files.uploadedfile import SimpleUploadedFile from django.core.files.base import ContentFile -from base64 import b64decode - from .models import ( Lokasi, Fasilitas, @@ -33,9 +31,6 @@ class InformasiFasilitasTest(TestCase): mock_lokasi_test = { 'place_id': 'mock_place_id', - 'name': 'Ma Homie', - 'alamat': 'Jl. Raya Bogor no.2, Jakarta', - 'no_telp': '081212123132', 'counter': 0, } diff --git a/informasi_fasilitas/test_models.py b/informasi_fasilitas/test_models.py index c4798b3..9cbc5cf 100644 --- a/informasi_fasilitas/test_models.py +++ b/informasi_fasilitas/test_models.py @@ -6,16 +6,6 @@ from .models import Lokasi, Fasilitas, Komentar, Likes, Dislikes class InformasiFasilitasModelTest(InformasiFasilitasTest): - def test_models_lokasi_not_created_attrs_should_not_null(self): - with self.assertRaises(IntegrityError) as err_message: - Lokasi.objects.create( - name=None, - alamat=None, - no_telp=None, - ) - self.assertTrue(str(err_message.exception).startswith( - self.not_null_constraint_failed_message)) - def test_models_lokasi_not_created_place_id_should_unique(self): with self.assertRaises(IntegrityError) as err_message: Lokasi.objects.create( @@ -27,6 +17,15 @@ class InformasiFasilitasModelTest(InformasiFasilitasTest): self.assertTrue(str(err_message.exception).startswith( self.unique_constraint_failed_message)) + def test_models_timestamp_auto_now_is_working(self): + lokasi = self.create_lokasi_test() + timestamp_before_save = lokasi.timestamp + + lokasi.save() + timestamp_after_save = lokasi.timestamp + + self.assertGreater(timestamp_after_save, timestamp_before_save) + def test_models_create_new_lokasi(self): self.create_lokasi_test() count = Lokasi.objects.all().count() @@ -34,7 +33,8 @@ class InformasiFasilitasModelTest(InformasiFasilitasTest): def test_models_lokasi_string(self): lokasi = self.create_lokasi_test() - self.assertEqual(str(lokasi), lokasi.name) + self.assertEqual(str(lokasi), "%s | %s" % \ + (lokasi.timestamp.strftime("%d %b %Y %X"), lokasi.place_id)) def test_models_fasilitas_not_created(self): with self.assertRaises(IntegrityError) as err_message: diff --git a/informasi_fasilitas/test_views_fasilitas.py b/informasi_fasilitas/test_views_fasilitas.py index 7e9fc41..8328504 100644 --- a/informasi_fasilitas/test_views_fasilitas.py +++ b/informasi_fasilitas/test_views_fasilitas.py @@ -41,7 +41,7 @@ class FasilitasRelatedViewTest(InformasiFasilitasViewTest): expected_json = { '1': { 'id': self.fasilitas.id, - 'nama_lokasi': self.fasilitas.lokasi.name, + 'place_id': self.fasilitas.lokasi.place_id, 'deskripsi': self.fasilitas.deskripsi, 'creator': self.fasilitas.user.last_name, 'like': self.fasilitas.like, diff --git a/informasi_fasilitas/test_views_lokasi.py b/informasi_fasilitas/test_views_lokasi.py index aa9b8bb..91f5c5c 100644 --- a/informasi_fasilitas/test_views_lokasi.py +++ b/informasi_fasilitas/test_views_lokasi.py @@ -96,11 +96,11 @@ class LokasiRelatedViewTest(InformasiFasilitasViewTest): def test_post_add_lokasi_missing_key(self): Lokasi.objects.all().delete() test_lokasi = self.mock_lokasi_test.copy() - del test_lokasi["name"] + del test_lokasi['place_id'] response = self.client.post(self.add_lokasi_url, test_lokasi) response_json = response_decode(response) - expected_json = {"name": ["This field is required."]} + expected_json = {"place_id": ["This field is required."]} self.assertEqual(response.status_code, HTTPStatus.BAD_REQUEST) self.assertEqual(response_json, expected_json) -- GitLab From 3c9f835a671885316606a03b4f221a7f740b2b26 Mon Sep 17 00:00:00 2001 From: ariqbasyar Date: Mon, 29 Mar 2021 15:15:43 +0700 Subject: [PATCH 26/97] [GREEN] implemented new Lokasi models - now image has param null and blank True - new attr timestamp with auto_now True - place_id is required on LokasiSerializer --- .../migrations/0010_auto_20210329_0714.py | 35 +++++++++++++++++++ informasi_fasilitas/models.py | 9 +++-- informasi_fasilitas/serializers.py | 3 +- informasi_fasilitas/views.py | 8 ++--- 4 files changed, 44 insertions(+), 11 deletions(-) create mode 100644 informasi_fasilitas/migrations/0010_auto_20210329_0714.py diff --git a/informasi_fasilitas/migrations/0010_auto_20210329_0714.py b/informasi_fasilitas/migrations/0010_auto_20210329_0714.py new file mode 100644 index 0000000..918b686 --- /dev/null +++ b/informasi_fasilitas/migrations/0010_auto_20210329_0714.py @@ -0,0 +1,35 @@ +# Generated by Django 3.1.7 on 2021-03-29 07:14 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('informasi_fasilitas', '0009_auto_20210327_1436'), + ] + + operations = [ + migrations.RemoveField( + model_name='lokasi', + name='alamat', + ), + migrations.RemoveField( + model_name='lokasi', + name='name', + ), + migrations.RemoveField( + model_name='lokasi', + name='no_telp', + ), + migrations.AddField( + model_name='lokasi', + name='timestamp', + field=models.DateTimeField(auto_now=True), + ), + migrations.AlterField( + model_name='lokasi', + name='image', + field=models.ImageField(blank=True, null=True, upload_to='lokasi/'), + ), + ] diff --git a/informasi_fasilitas/models.py b/informasi_fasilitas/models.py index 439ece4..8c84327 100644 --- a/informasi_fasilitas/models.py +++ b/informasi_fasilitas/models.py @@ -49,14 +49,13 @@ def _default_lokasi_place_id(): class Lokasi(models.Model): place_id = models.TextField(unique=True, default=_default_lokasi_place_id) - name = models.CharField(max_length=50) - alamat = models.CharField(max_length=100) - no_telp = models.CharField(max_length=16) - image = models.TextField(null=True) + image = models.ImageField(upload_to='lokasi/', null=True, blank=True) counter = models.PositiveIntegerField(default=0) + timestamp = models.DateTimeField(auto_now=True) def __str__(self): - return self.name + return "%s | %s" % \ + (self.timestamp.strftime("%d %b %Y %X"), self.place_id) class Fasilitas(models.Model): objects = models.Manager() diff --git a/informasi_fasilitas/serializers.py b/informasi_fasilitas/serializers.py index 8ae0076..5d37f54 100644 --- a/informasi_fasilitas/serializers.py +++ b/informasi_fasilitas/serializers.py @@ -7,4 +7,5 @@ class LokasiSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = Lokasi - fields = ('place_id', 'name', 'alamat', 'no_telp', 'image', 'counter') + fields = ('place_id', 'image', 'counter') + extra_kwargs = {'place_id': {'required': True}} diff --git a/informasi_fasilitas/views.py b/informasi_fasilitas/views.py index d0e41c0..60d18cc 100644 --- a/informasi_fasilitas/views.py +++ b/informasi_fasilitas/views.py @@ -24,7 +24,6 @@ def missing_key_message(key): return "Bad Request. {} key is needed".format(key) -@csrf_exempt @api_view(['GET']) @authentication_classes([]) @permission_classes([]) @@ -91,7 +90,7 @@ def list_fasilitas(request, place_id): return_json[fasilitas.id] = {} fasilitas_details = return_json[fasilitas.id] fasilitas_details["id"] = fasilitas.id - fasilitas_details["nama_lokasi"] = fasilitas.lokasi.name + fasilitas_details["place_id"] = fasilitas.lokasi.place_id fasilitas_details["deskripsi"] = fasilitas.deskripsi fasilitas_details["creator"] = fasilitas.user.last_name fasilitas_details["date_time"] = fasilitas.date_time.strftime( @@ -151,14 +150,13 @@ def add_fasilitas(request, place_id): @permission_classes([]) def detail_fasilitas(request, place_id, id): try: - lokasi = Lokasi.objects.get(place_id=place_id) - fasilitas = Fasilitas.objects.get(lokasi=lokasi, id=id) + fasilitas = Fasilitas.objects.get(lokasi__place_id=place_id, id=id) user = fasilitas.user fasilitas.like = Likes.objects.filter(fasilitas=fasilitas).count() fasilitas.dislike = Dislikes.objects.filter( fasilitas=fasilitas).count() return_json = {"id": fasilitas.id, - "place_id": lokasi.name, + "place_id": fasilitas.lokasi.place_id, "deskripsi": fasilitas.deskripsi, "creator": user.last_name, "creator_email": user.email, -- GitLab From cbf2067abe7982fc55e769103b386e40e1423cb4 Mon Sep 17 00:00:00 2001 From: ariqbasyar Date: Mon, 29 Mar 2021 16:49:12 +0700 Subject: [PATCH 27/97] [RED] test remove attribute image from Lokasi models --- informasi_fasilitas/test_views_lokasi.py | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/informasi_fasilitas/test_views_lokasi.py b/informasi_fasilitas/test_views_lokasi.py index 91f5c5c..e76981c 100644 --- a/informasi_fasilitas/test_views_lokasi.py +++ b/informasi_fasilitas/test_views_lokasi.py @@ -40,10 +40,7 @@ class LokasiRelatedViewTest(InformasiFasilitasViewTest): response = Client().get(self.list_lokasi_url) content = response_decode(response) - expected_first_entry = self.mock_lokasi_test.copy() - expected_first_entry["image"] = None - expected_json = [expected_first_entry] - self.assertEqual(content, expected_json) + self.assertEqual(content, [self.mock_lokasi_test]) def test_cannot_post_lokasi_list_url(self): response = Client().post(self.list_lokasi_url) @@ -53,11 +50,8 @@ class LokasiRelatedViewTest(InformasiFasilitasViewTest): response = Client().get(self.detail_lokasi_url) content = response_decode(response) - expected_json = self.mock_lokasi_test.copy() - expected_json["image"] = None - self.assertEqual(response.status_code, HTTPStatus.OK) - self.assertEqual(content, expected_json) + self.assertEqual(content, self.mock_lokasi_test) def test_cannot_post_lokasi_details(self): response = Client().post(self.detail_lokasi_url) @@ -77,10 +71,8 @@ class LokasiRelatedViewTest(InformasiFasilitasViewTest): response = \ self.client.post(self.add_lokasi_url, self.mock_lokasi_test) response_json = response_decode(response) - expected_json = self.mock_lokasi_test.copy() - expected_json['image'] = None - self.assertEqual(response_json, expected_json) + self.assertEqual(response_json, self.mock_lokasi_test) self.assertEqual(response.status_code, HTTPStatus.CREATED) def test_cannot_post_lokasi_place_id_should_unique(self): -- GitLab From c04687bf44499a2e1c9e03259119c073ce0840f5 Mon Sep 17 00:00:00 2001 From: ariqbasyar Date: Mon, 29 Mar 2021 16:54:31 +0700 Subject: [PATCH 28/97] [GREEN] removed image from Lokasi models now image can be retrieved from photo_reference --- .../migrations/0011_remove_lokasi_image.py | 17 +++++++++++++++++ informasi_fasilitas/models.py | 1 - informasi_fasilitas/serializers.py | 3 +-- 3 files changed, 18 insertions(+), 3 deletions(-) create mode 100644 informasi_fasilitas/migrations/0011_remove_lokasi_image.py diff --git a/informasi_fasilitas/migrations/0011_remove_lokasi_image.py b/informasi_fasilitas/migrations/0011_remove_lokasi_image.py new file mode 100644 index 0000000..1d686d9 --- /dev/null +++ b/informasi_fasilitas/migrations/0011_remove_lokasi_image.py @@ -0,0 +1,17 @@ +# Generated by Django 3.1.7 on 2021-03-29 09:48 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('informasi_fasilitas', '0010_auto_20210329_0714'), + ] + + operations = [ + migrations.RemoveField( + model_name='lokasi', + name='image', + ), + ] diff --git a/informasi_fasilitas/models.py b/informasi_fasilitas/models.py index 8c84327..9558415 100644 --- a/informasi_fasilitas/models.py +++ b/informasi_fasilitas/models.py @@ -49,7 +49,6 @@ def _default_lokasi_place_id(): class Lokasi(models.Model): place_id = models.TextField(unique=True, default=_default_lokasi_place_id) - image = models.ImageField(upload_to='lokasi/', null=True, blank=True) counter = models.PositiveIntegerField(default=0) timestamp = models.DateTimeField(auto_now=True) diff --git a/informasi_fasilitas/serializers.py b/informasi_fasilitas/serializers.py index 5d37f54..a51c0e6 100644 --- a/informasi_fasilitas/serializers.py +++ b/informasi_fasilitas/serializers.py @@ -3,9 +3,8 @@ from rest_framework import serializers from .models import Lokasi class LokasiSerializer(serializers.HyperlinkedModelSerializer): - image = serializers.ImageField(required=False) class Meta: model = Lokasi - fields = ('place_id', 'image', 'counter') + fields = ('place_id', 'counter') extra_kwargs = {'place_id': {'required': True}} -- GitLab From a83d609af1088af23956b77c67280ecbe520dc9d Mon Sep 17 00:00:00 2001 From: ariqbasyar Date: Tue, 30 Mar 2021 20:05:51 +0700 Subject: [PATCH 29/97] [RED] tests for counter and timestamp field for Lokasi models --- informasi_fasilitas/test_models.py | 4 +- informasi_fasilitas/test_views_lokasi.py | 88 +++++++++++++----------- 2 files changed, 48 insertions(+), 44 deletions(-) diff --git a/informasi_fasilitas/test_models.py b/informasi_fasilitas/test_models.py index 9cbc5cf..d6b73aa 100644 --- a/informasi_fasilitas/test_models.py +++ b/informasi_fasilitas/test_models.py @@ -17,14 +17,14 @@ class InformasiFasilitasModelTest(InformasiFasilitasTest): self.assertTrue(str(err_message.exception).startswith( self.unique_constraint_failed_message)) - def test_models_timestamp_auto_now_is_working(self): + def test_models_timestamp_not_changing_after_save(self): lokasi = self.create_lokasi_test() timestamp_before_save = lokasi.timestamp lokasi.save() timestamp_after_save = lokasi.timestamp - self.assertGreater(timestamp_after_save, timestamp_before_save) + self.assertEqual(timestamp_after_save, timestamp_before_save) def test_models_create_new_lokasi(self): self.create_lokasi_test() diff --git a/informasi_fasilitas/test_views_lokasi.py b/informasi_fasilitas/test_views_lokasi.py index e76981c..ab304c6 100644 --- a/informasi_fasilitas/test_views_lokasi.py +++ b/informasi_fasilitas/test_views_lokasi.py @@ -16,13 +16,8 @@ class LokasiRelatedViewTest(InformasiFasilitasViewTest): def setUp(self): super().setUp() - self.kwargs_detail_or_update = {'place_id': self.lokasi.place_id} - self.list_lokasi_url = reverse('lokasi-list') - self.detail_lokasi_url = reverse('lokasi-details', - kwargs=self.kwargs_detail_or_update) + self.list_lokasi_url = reverse('list-lokasi') self.add_lokasi_url = reverse('add-lokasi') - self.update_lokasi_url = reverse('update-lokasi', - kwargs=self.kwargs_detail_or_update) def test_LokasiSerializer_valid(self): serializer = LokasiSerializer(data=self.mock_lokasi_test) @@ -42,29 +37,52 @@ class LokasiRelatedViewTest(InformasiFasilitasViewTest): self.assertEqual(content, [self.mock_lokasi_test]) - def test_cannot_post_lokasi_list_url(self): - response = Client().post(self.list_lokasi_url) - self.assertEqual(response.status_code, HTTPStatus.METHOD_NOT_ALLOWED) + def test_list_lokasi_should_return_sorted_descending_and_best_3(self): + data_lokasi1 = self.mock_lokasi_test.copy() + data_lokasi1['counter'] = 2 - def test_can_get_lokasi_details(self): - response = Client().get(self.detail_lokasi_url) + data_lokasi2 = {'counter': 7, 'place_id': 'a'} + data_lokasi3 = {'counter': 1, 'place_id': 'b'} + data_lokasi4 = {'counter': 0, 'place_id': 'c'} + + lokasi1 = self.lokasi + lokasi2 = self.create_lokasi_test(lokasi_dict=data_lokasi2) + lokasi3 = self.create_lokasi_test(lokasi_dict=data_lokasi3) + self.create_lokasi_test(lokasi_dict=data_lokasi4) + + lokasi1.counter = 2 + lokasi1.save() + + lokasi2.counter = 7 + lokasi2.save() + + lokasi3.counter = 1 + lokasi3.save() + + response = Client().get(self.list_lokasi_url) content = response_decode(response) - self.assertEqual(response.status_code, HTTPStatus.OK) - self.assertEqual(content, self.mock_lokasi_test) + expected = [ + data_lokasi2, + data_lokasi1, + data_lokasi3, + ] - def test_cannot_post_lokasi_details(self): - response = Client().post(self.detail_lokasi_url) - self.assertEqual(response.status_code, HTTPStatus.METHOD_NOT_ALLOWED) + self.assertEqual(len(content), 3) + self.assertEqual(content, expected) - def test_get_lokasi_details_not_found(self): - Lokasi.objects.all().delete() - response = Client().get(self.detail_lokasi_url) - self.assertEqual(response.status_code, HTTPStatus.NOT_FOUND) + def test_attribute_counter_is_readonly_from_views(self): + new_lokasi = {'counter': 10, 'place_id': 'a'} + response = \ + self.client.post(self.add_lokasi_url, new_lokasi) - def test_cannot_get_add_lokasi(self): - response = self.client.get(self.add_lokasi_url) - self.assertEqual(response.status_code, HTTPStatus.METHOD_NOT_ALLOWED) + lokasi = Lokasi.objects.get(place_id=new_lokasi['place_id']) + + self.assertEqual(lokasi.counter, 0) + + def test_cannot_delete_lokasi(self): + response = self.client.delete(self.list_lokasi_url) + self.assertEqual(response.status_code, HTTPStatus.FORBIDDEN) def test_can_post_add_lokasi(self): Lokasi.objects.all().delete() @@ -73,17 +91,17 @@ class LokasiRelatedViewTest(InformasiFasilitasViewTest): response_json = response_decode(response) self.assertEqual(response_json, self.mock_lokasi_test) - self.assertEqual(response.status_code, HTTPStatus.CREATED) + self.assertEqual(response.status_code, HTTPStatus.ACCEPTED) - def test_cannot_post_lokasi_place_id_should_unique(self): + def test_post_place_id_increase_counter(self): response = \ self.client.post(self.add_lokasi_url, self.mock_lokasi_test) response_json = response_decode(response) - expected_json = \ - {'place_id': ['lokasi with this place id already exists.']} + expected_json = self.mock_lokasi_test.copy() + expected_json['counter'] += 1 self.assertEqual(response_json, expected_json) - self.assertEqual(response.status_code, HTTPStatus.BAD_REQUEST) + self.assertEqual(response.status_code, HTTPStatus.ACCEPTED) def test_post_add_lokasi_missing_key(self): Lokasi.objects.all().delete() @@ -96,17 +114,3 @@ class LokasiRelatedViewTest(InformasiFasilitasViewTest): self.assertEqual(response.status_code, HTTPStatus.BAD_REQUEST) self.assertEqual(response_json, expected_json) - - def test_put_update_detail_lokasi_success(self): - response = self.client.put(self.update_lokasi_url, - data=urlencode({'no_telp': '0000000121'}), - content_type=self.put_content_type) - self.assertEqual(response.status_code, HTTPStatus.ACCEPTED) - - def test_put_update_detail_lokasi_missing_key(self): - response = self.client.put( - self.update_lokasi_url, - data={"latitude": 100}, - content_type=self.put_content_type - ) - self.assertEqual(response.status_code, HTTPStatus.BAD_REQUEST) -- GitLab From b72b72627ce9d11f632c8035dd32bc0b351bb542 Mon Sep 17 00:00:00 2001 From: ariqbasyar Date: Tue, 30 Mar 2021 20:31:41 +0700 Subject: [PATCH 30/97] [GREEN] now Lokasi use default timezone.now instead of auto_now=True - also refactor some views for lokasi - added UserPermission in informasi_fasilitas.permissions --- .../migrations/0012_auto_20210329_1332.py | 19 +++++ informasi_fasilitas/models.py | 4 +- informasi_fasilitas/permissions.py | 9 ++ informasi_fasilitas/serializers.py | 11 ++- informasi_fasilitas/urls.py | 14 ++- informasi_fasilitas/views.py | 85 +++++++------------ 6 files changed, 78 insertions(+), 64 deletions(-) create mode 100644 informasi_fasilitas/migrations/0012_auto_20210329_1332.py create mode 100644 informasi_fasilitas/permissions.py diff --git a/informasi_fasilitas/migrations/0012_auto_20210329_1332.py b/informasi_fasilitas/migrations/0012_auto_20210329_1332.py new file mode 100644 index 0000000..ebbb285 --- /dev/null +++ b/informasi_fasilitas/migrations/0012_auto_20210329_1332.py @@ -0,0 +1,19 @@ +# Generated by Django 3.1.7 on 2021-03-29 13:32 + +from django.db import migrations, models +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('informasi_fasilitas', '0011_remove_lokasi_image'), + ] + + operations = [ + migrations.AlterField( + model_name='lokasi', + name='timestamp', + field=models.DateTimeField(blank=True, default=django.utils.timezone.now), + ), + ] diff --git a/informasi_fasilitas/models.py b/informasi_fasilitas/models.py index 9558415..1e90a63 100644 --- a/informasi_fasilitas/models.py +++ b/informasi_fasilitas/models.py @@ -2,6 +2,8 @@ from django.db import models from django.contrib.auth.models import User from multiselectfield import MultiSelectField +from django.utils import timezone + import string import random @@ -50,7 +52,7 @@ def _default_lokasi_place_id(): class Lokasi(models.Model): place_id = models.TextField(unique=True, default=_default_lokasi_place_id) counter = models.PositiveIntegerField(default=0) - timestamp = models.DateTimeField(auto_now=True) + timestamp = models.DateTimeField(default=timezone.now, blank=True) def __str__(self): return "%s | %s" % \ diff --git a/informasi_fasilitas/permissions.py b/informasi_fasilitas/permissions.py new file mode 100644 index 0000000..20dedb0 --- /dev/null +++ b/informasi_fasilitas/permissions.py @@ -0,0 +1,9 @@ +from rest_framework import permissions + +class UserPermission(permissions.BasePermission): + def has_permission(self, request, view): + if view.action == 'list': + return True + elif view.action == 'create': + return request.user.is_authenticated + return False diff --git a/informasi_fasilitas/serializers.py b/informasi_fasilitas/serializers.py index a51c0e6..e191b99 100644 --- a/informasi_fasilitas/serializers.py +++ b/informasi_fasilitas/serializers.py @@ -2,9 +2,16 @@ from rest_framework import serializers from .models import Lokasi -class LokasiSerializer(serializers.HyperlinkedModelSerializer): +class LokasiSerializer(serializers.ModelSerializer): class Meta: model = Lokasi fields = ('place_id', 'counter') - extra_kwargs = {'place_id': {'required': True}} + extra_kwargs = { + 'place_id': { + 'required': True, + }, + 'counter': { + 'read_only': True, + }, + } diff --git a/informasi_fasilitas/urls.py b/informasi_fasilitas/urls.py index e2e2d57..eacb246 100644 --- a/informasi_fasilitas/urls.py +++ b/informasi_fasilitas/urls.py @@ -1,17 +1,13 @@ from django.urls import path from . import views -from .views import * -urlpatterns = [ - path('lokasi/list/', views.lokasi_list, name='lokasi-list'), - - path('lokasi/detail//', - views.lokasi_details, name='lokasi-details'), +list_lokasi_views = views.LokasiListCreateView.as_view({'get':'list'}) +add_lokasi_views = views.LokasiListCreateView.as_view({'post':'create'}) - path('lokasi/update-detail//', - views.update_lokasi_details, name='update-lokasi'), +urlpatterns = [ + path('lokasi/list/', list_lokasi_views, name='list-lokasi'), - path('lokasi/add/', views.add_lokasi, name='add-lokasi'), + path('lokasi/add/', add_lokasi_views, name='add-lokasi'), path('lokasi/add-fasilitas//', views.add_fasilitas, name='add-fasilitas'), diff --git a/informasi_fasilitas/views.py b/informasi_fasilitas/views.py index 60d18cc..02ab23d 100644 --- a/informasi_fasilitas/views.py +++ b/informasi_fasilitas/views.py @@ -8,14 +8,15 @@ from django.db.models import F from rest_framework.decorators import api_view, permission_classes, authentication_classes from rest_framework.authentication import TokenAuthentication from rest_framework.permissions import IsAuthenticated +from rest_framework.response import Response +from rest_framework.parsers import FileUploadParser +from rest_framework.views import APIView +from rest_framework.generics import ListCreateAPIView +from rest_framework import viewsets from .serializers import LokasiSerializer from .models import Lokasi, Fasilitas, Komentar, Likes, Dislikes - - - -from rest_framework.parsers import FileUploadParser -from rest_framework.views import APIView +from .permissions import UserPermission TIME_FORMAT = "%d-%m-%Y %H:%M:%S" @@ -23,56 +24,36 @@ TIME_FORMAT = "%d-%m-%Y %H:%M:%S" def missing_key_message(key): return "Bad Request. {} key is needed".format(key) +class LokasiListCreateView(viewsets.ModelViewSet): + queryset = Lokasi.objects.all() + serializer_class = LokasiSerializer + authentication_classes = [TokenAuthentication] + permission_classes = (UserPermission,) -@api_view(['GET']) -@authentication_classes([]) -@permission_classes([]) -def lokasi_list(request): - lokasi_list = Lokasi.objects.all() - serializer = LokasiSerializer(lokasi_list, many=True) - return JsonResponse(serializer.data, safe=False, status=HTTPStatus.OK) - - -@api_view(['GET']) -@authentication_classes([]) -@permission_classes([]) -def lokasi_details(request, place_id): - try: - lokasi = Lokasi.objects.get(place_id=place_id) - serializer = LokasiSerializer(lokasi) - return JsonResponse(serializer.data, safe=False, status=HTTPStatus.OK) - except ObjectDoesNotExist as err: - return JsonResponse({'response': str(err)}, status=HTTPStatus.NOT_FOUND) - - -@api_view(['POST']) -@authentication_classes([TokenAuthentication]) -@permission_classes([IsAuthenticated]) -def add_lokasi(request): - data = request.data - serializer = LokasiSerializer(data=data) - if serializer.is_valid(): - serializer.save() - return JsonResponse(serializer.data, status=HTTPStatus.CREATED) - else: - return JsonResponse(serializer.errors, - status=HTTPStatus.BAD_REQUEST) + def get_queryset(self): + return self.queryset -@api_view(['PUT']) -@authentication_classes([TokenAuthentication]) -@permission_classes([IsAuthenticated]) -def update_lokasi_details(request, place_id): - try: - lokasi = Lokasi.objects.get(place_id=place_id) - lokasi.no_telp = request.data['no_telp'] - lokasi.counter = F('counter') + 1 - lokasi.save() - return JsonResponse({'response': "phone changed to {}".format(lokasi.no_telp)}, - status=HTTPStatus.ACCEPTED) - except KeyError as missing_key: - return JsonResponse({'response': missing_key_message(str(missing_key))}, - status=HTTPStatus.BAD_REQUEST) + def list(self, request): + queryset = self.get_queryset().order_by('-counter')[:3] + serializer = self.get_serializer(queryset, many=True) + return Response(serializer.data, status=HTTPStatus.OK) + def create(self, request): + data = request.data + serializer = self.get_serializer(data=data) + if serializer.is_valid(): + serializer.save() + return Response(serializer.data, status=HTTPStatus.ACCEPTED) + try: + place_id = data.get("place_id") + lokasi = Lokasi.objects.get(place_id=place_id) + lokasi.counter += 1 + lokasi.save() + serializer = self.get_serializer(lokasi) + return Response(serializer.data, status=HTTPStatus.ACCEPTED) + except Lokasi.DoesNotExist as _: + pass + return Response(serializer.errors, status=HTTPStatus.BAD_REQUEST) @api_view(['GET']) @authentication_classes([]) -- GitLab From 4fab90475c26ce963c5acb486c9f13be9a155870 Mon Sep 17 00:00:00 2001 From: "alvin.hariman" Date: Tue, 30 Mar 2021 22:27:23 +0700 Subject: [PATCH 31/97] [GREEN] Fix unittest error in app layanan-khusus --- layanan_khusus/tests.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/layanan_khusus/tests.py b/layanan_khusus/tests.py index 4a9c476..d45706c 100644 --- a/layanan_khusus/tests.py +++ b/layanan_khusus/tests.py @@ -6,6 +6,7 @@ from django.contrib.auth.models import User from django.urls import path, include, reverse from .models import Sekolah, Penyandang, Komunitas from .serializers import SekolahSerializer, KomunitasSerializer +import django NOT_NULL_CONSTRAINT_FAILED_MESSAGE = 'NOT NULL constraint failed' @@ -141,8 +142,7 @@ class LayananKhususModelTest(TestCase): with self.assertRaises(IntegrityError) as ex: obj = Sekolah(name=None) obj.save() - self.assertTrue(str(ex.exception).startswith( - NOT_NULL_CONSTRAINT_FAILED_MESSAGE)) + self.assertTrue(ex.expected == django.db.utils.IntegrityError) def test_models_create_new_sekolah(self): sekolah_setup() @@ -153,8 +153,7 @@ class LayananKhususModelTest(TestCase): with self.assertRaises(IntegrityError) as ex: obj = Penyandang(name=None) obj.save() - self.assertTrue(str(ex.exception).startswith( - NOT_NULL_CONSTRAINT_FAILED_MESSAGE)) + self.assertTrue(ex.expected == django.db.utils.IntegrityError) def test_models_create_new_penyandang(self): penyandang_setup() @@ -165,8 +164,7 @@ class LayananKhususModelTest(TestCase): with self.assertRaises(IntegrityError) as ex: obj = Komunitas(name=None) obj.save() - self.assertTrue(str(ex.exception).startswith( - NOT_NULL_CONSTRAINT_FAILED_MESSAGE)) + self.assertTrue(ex.expected == django.db.utils.IntegrityError) def test_models_create_new_komunitas(self): komunitas_setup() @@ -247,6 +245,7 @@ class LayananKhususViewsTest(TestCase): MOCK_PENYANDANG) content = json.loads(response.content.decode("utf-8")) expected_json = MOCK_PENYANDANG + expected_json["id"] = 3 self.assertEqual(content, expected_json) def test_cannot_get_register_penyandang(self): -- GitLab From 48a56b537de6f64081c52a697e28fcdc8ac4b40d Mon Sep 17 00:00:00 2001 From: ariqbasyar Date: Wed, 31 Mar 2021 10:38:49 +0700 Subject: [PATCH 32/97] fix database issue with postgres's vector search from PBI-4 --- layanan_khusus/tests.py | 18 +++++++++++++----- pplbackend/settings.py | 29 ++++++++++++++++++++--------- 2 files changed, 33 insertions(+), 14 deletions(-) diff --git a/layanan_khusus/tests.py b/layanan_khusus/tests.py index d45706c..835bd7c 100644 --- a/layanan_khusus/tests.py +++ b/layanan_khusus/tests.py @@ -4,6 +4,7 @@ from django.test import TestCase, Client from django.db.utils import IntegrityError from django.contrib.auth.models import User from django.urls import path, include, reverse +from dynamic_db_router import in_database from .models import Sekolah, Penyandang, Komunitas from .serializers import SekolahSerializer, KomunitasSerializer import django @@ -172,7 +173,10 @@ class LayananKhususModelTest(TestCase): self.assertNotEqual(count, 0) +@in_database('postgres') class LayananKhususViewsTest(TestCase): + databases = 'postgres' + urlpatterns = [ path('layanan-khusus/', include('layanan_khusus.urls')), ] @@ -294,7 +298,11 @@ class LayananKhususViewsTest(TestCase): reverse('detail-komunitas', kwargs={'id_komunitas': 1})) self.assertEqual(response.status_code, HTTPStatus.NOT_FOUND) + +@in_database('postgres') class LayananKhususSearchTest(TestCase): + databases = 'postgres' + urlpatterns = [ path('layanan-khusus/', include('layanan_khusus.urls')), ] @@ -312,7 +320,7 @@ class LayananKhususSearchTest(TestCase): def test_can_search_komunitas_json(self): komunitas_setup() response = Client().get("%s?search=Disabilitas" % reverse('pencarian')) - + content = json.loads(response.content.decode('utf-8')) expected_json = {'0': { 'name': MOCK_KOMUNITAS[NAME], @@ -327,7 +335,7 @@ class LayananKhususSearchTest(TestCase): def test_can_search_sekolah_json(self): sekolah_setup() response = Client().get("%s?search=Sekolah" % reverse('pencarian')) - + content = json.loads(response.content.decode('utf-8')) expected_json = {'0': { 'name': MOCK_SEKOLAH[NAME], @@ -345,13 +353,13 @@ class LayananKhususSearchTest(TestCase): def test_cannot_post_search_sekolah(self): sekolah_setup() response = Client().post("%s?search=Sekolah" % reverse('pencarian')) - + self.assertEqual(response.status_code, HTTPStatus.METHOD_NOT_ALLOWED) def test_cannot_post_search_komunitas(self): komunitas_setup() response = Client().post("%s?search=Disabilitas" % reverse('pencarian')) - + self.assertEqual(response.status_code, HTTPStatus.METHOD_NOT_ALLOWED) def test_search_komunitas_and_sekolah(self): @@ -364,7 +372,7 @@ class LayananKhususSearchTest(TestCase): sekolah_setup() komunitas_setup() response = Client().get("%s?search=Jakarta" % reverse('pencarian')) - + content = json.loads(response.content.decode('utf-8')) expected_json = {'0': { 'name': MOCK_SEKOLAH[NAME], diff --git a/pplbackend/settings.py b/pplbackend/settings.py index b8e8c2b..48109fe 100644 --- a/pplbackend/settings.py +++ b/pplbackend/settings.py @@ -95,30 +95,41 @@ WSGI_APPLICATION = 'pplbackend.wsgi.application' # Database # https://docs.djangoproject.com/en/3.0/ref/settings/#databases +DATABASE_ROUTERS = [ + 'dynamic_db_router.DynamicDbRouter', +] + DEFAULT_DATABASE = { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), } +POSTGRES_DATABASE = { + 'ENGINE': 'django.db.backends.postgresql_psycopg2', + 'NAME': os.getenv('DB_NAME'), + 'USER': os.getenv('DB_USER'), + 'PASSWORD': os.getenv('DB_PASSWORD'), + 'HOST': os.getenv('DB_HOST'), + 'PORT': os.getenv('DB_PORT'), +} + DATABASES = { - 'default': DEFAULT_DATABASE + 'default': DEFAULT_DATABASE, } # custom database host, overriding the default if os.getenv('DB_HOST') is not None: - DATABASES['default'] = { - 'ENGINE': 'django.db.backends.postgresql_psycopg2', - 'NAME': os.getenv('DB_NAME'), - 'USER': os.getenv('DB_USER'), - 'PASSWORD': os.getenv('DB_PASSWORD'), - 'HOST': os.getenv('DB_HOST'), - 'PORT': os.getenv('DB_PORT'), - } + DATABASES['default'] = POSTGRES_DATABASE # use default database when running test if 'test' in sys.argv: DATABASES['default'] = DEFAULT_DATABASE + # database postgres context used in + # - layanan_khusus.tests.LayananKhususSearchTest + # - layanan_khusus.tests.LayananKhususViewsTest + DATABASES['postgres'] = POSTGRES_DATABASE + # Password validation # https://docs.djangoproject.com/en/3.0/ref/settings/#auth-password-validators -- GitLab From f1d80dcd38b6ffb11aab9d22515dd8766b185931 Mon Sep 17 00:00:00 2001 From: ariqbasyar Date: Wed, 31 Mar 2021 17:44:26 +0700 Subject: [PATCH 33/97] [RED] revert database engine set back to postgresql - PBI 4 fungsi pencarian needs search vector which only support postgresql - use relative to object's id instead of hardcoded to prevent error because of postgresql's strange behavior - new assertion with expected IntegrityError when testing models - removed some variable like NOT_NULL_constraint_failed_message and UNIQUE_constraint_failed_message --- informasi_fasilitas/test_base.py | 2 -- informasi_fasilitas/test_models.py | 25 +++++++++------------ informasi_fasilitas/test_views_fasilitas.py | 8 ++++--- informasi_fasilitas/test_views_komentar.py | 2 +- layanan_khusus/tests.py | 21 +++++++---------- registrasi/tests.py | 4 ++-- 6 files changed, 26 insertions(+), 36 deletions(-) diff --git a/informasi_fasilitas/test_base.py b/informasi_fasilitas/test_base.py index a759bf4..5db110f 100644 --- a/informasi_fasilitas/test_base.py +++ b/informasi_fasilitas/test_base.py @@ -19,8 +19,6 @@ from pplbackend.utils import get_client_login_with_user class InformasiFasilitasTest(TestCase): - not_null_constraint_failed_message = 'NOT NULL constraint failed' - unique_constraint_failed_message = 'UNIQUE constraint failed' put_content_type = "application/x-www-form-urlencoded" mock_user_test = { diff --git a/informasi_fasilitas/test_models.py b/informasi_fasilitas/test_models.py index d6b73aa..b155d23 100644 --- a/informasi_fasilitas/test_models.py +++ b/informasi_fasilitas/test_models.py @@ -7,15 +7,14 @@ from .models import Lokasi, Fasilitas, Komentar, Likes, Dislikes class InformasiFasilitasModelTest(InformasiFasilitasTest): def test_models_lokasi_not_created_place_id_should_unique(self): - with self.assertRaises(IntegrityError) as err_message: + with self.assertRaises(IntegrityError) as ex: Lokasi.objects.create( place_id=self.mock_lokasi_test['place_id'], ) Lokasi.objects.create( place_id=self.mock_lokasi_test['place_id'], ) - self.assertTrue(str(err_message.exception).startswith( - self.unique_constraint_failed_message)) + self.assertEqual(ex.expected, IntegrityError) def test_models_timestamp_not_changing_after_save(self): lokasi = self.create_lokasi_test() @@ -37,11 +36,10 @@ class InformasiFasilitasModelTest(InformasiFasilitasTest): (lokasi.timestamp.strftime("%d %b %Y %X"), lokasi.place_id)) def test_models_fasilitas_not_created(self): - with self.assertRaises(IntegrityError) as err_message: + with self.assertRaises(IntegrityError) as ex: obj = Fasilitas(lokasi=None) obj.save() - self.assertTrue(str(err_message.exception).startswith( - self.not_null_constraint_failed_message)) + self.assertEqual(ex.expected, IntegrityError) def test_models_create_new_fasilitas(self): self.create_fasilitas_test() @@ -49,11 +47,10 @@ class InformasiFasilitasModelTest(InformasiFasilitasTest): self.assertNotEqual(count, 0) def test_models_komentar_not_created(self): - with self.assertRaises(IntegrityError) as err_message: + with self.assertRaises(IntegrityError) as ex: obj = Komentar(fasilitas=None) obj.save() - self.assertTrue(str(err_message.exception).startswith( - self.not_null_constraint_failed_message)) + self.assertEqual(ex.expected, IntegrityError) def test_models_create_new_komentar(self): self.create_komentar_test() @@ -61,11 +58,10 @@ class InformasiFasilitasModelTest(InformasiFasilitasTest): self.assertNotEqual(count, 0) def test_models_dislikes_not_created(self): - with self.assertRaises(IntegrityError) as err_message: + with self.assertRaises(IntegrityError) as ex: obj = Dislikes(fasilitas=None) obj.save() - self.assertTrue(str(err_message.exception).startswith( - self.not_null_constraint_failed_message)) + self.assertEqual(ex.expected, IntegrityError) def test_models_create_new_dislikes(self): self.create_dislikes_test() @@ -73,11 +69,10 @@ class InformasiFasilitasModelTest(InformasiFasilitasTest): self.assertNotEqual(count, 0) def test_models_likes_not_created(self): - with self.assertRaises(IntegrityError) as err_message: + with self.assertRaises(IntegrityError) as ex: obj = Likes(fasilitas=None) obj.save() - self.assertTrue(str(err_message.exception).startswith( - self.not_null_constraint_failed_message)) + self.assertEqual(ex.expected, IntegrityError) def test_models_create_new_likes(self): self.create_likes_test() diff --git a/informasi_fasilitas/test_views_fasilitas.py b/informasi_fasilitas/test_views_fasilitas.py index 8328504..9fa8193 100644 --- a/informasi_fasilitas/test_views_fasilitas.py +++ b/informasi_fasilitas/test_views_fasilitas.py @@ -31,6 +31,8 @@ class FasilitasRelatedViewTest(InformasiFasilitasViewTest): } ) + self.maxDiff = 1e5 + def test_can_get_list_fasilitas(self): response = Client().get(self.get_list_fasilitas_url) self.assertEqual(response.status_code, HTTPStatus.OK) @@ -39,7 +41,7 @@ class FasilitasRelatedViewTest(InformasiFasilitasViewTest): response = Client().get(self.get_list_fasilitas_url) content = json.loads(response.content.decode('utf-8')) expected_json = { - '1': { + str(self.fasilitas.id): { 'id': self.fasilitas.id, 'place_id': self.fasilitas.lokasi.place_id, 'deskripsi': self.fasilitas.deskripsi, @@ -56,7 +58,7 @@ class FasilitasRelatedViewTest(InformasiFasilitasViewTest): } time = self.fasilitas.date_time.strftime("%d-%m-%Y %H:%M:%S") - expected_json["1"]['date_time'] = time + expected_json[str(self.fasilitas.id)]['date_time'] = time self.assertEqual(content, expected_json) def test_cannot_post_list_fasilitas(self): @@ -148,5 +150,5 @@ class FasilitasRelatedViewTest(InformasiFasilitasViewTest): 'tag': 'KR', } response = self.client.put(self.update_fasilitas_url, data=send_data, - content_type='application/x-www-form-urlencoded') + content_type=self.put_content_type) self.assertEqual(response.status_code, HTTPStatus.ACCEPTED) diff --git a/informasi_fasilitas/test_views_komentar.py b/informasi_fasilitas/test_views_komentar.py index 8a90fd8..b81ba1f 100644 --- a/informasi_fasilitas/test_views_komentar.py +++ b/informasi_fasilitas/test_views_komentar.py @@ -44,7 +44,7 @@ class KomentarRelatedViewTest(InformasiFasilitasViewTest): response_json = response_decode(response) expected_json = { - '1': { + str(komentar.id): { 'id': komentar.id, 'deskripsi': komentar.deskripsi, 'creator': komentar.user.last_name, diff --git a/layanan_khusus/tests.py b/layanan_khusus/tests.py index 17424e7..2acae84 100644 --- a/layanan_khusus/tests.py +++ b/layanan_khusus/tests.py @@ -1,15 +1,12 @@ import json from http import HTTPStatus -from django.test import TestCase, Client +from django.test import TestCase, Client, override_settings +from django.conf import settings from django.db.utils import IntegrityError from django.contrib.auth.models import User from django.urls import path, include, reverse -from dynamic_db_router import in_database from .models import Sekolah, Penyandang, Komunitas from .serializers import SekolahSerializer, KomunitasSerializer -import django - -NOT_NULL_CONSTRAINT_FAILED_MESSAGE = 'NOT NULL constraint failed' ID = 'id' NAME = 'name' @@ -143,7 +140,7 @@ class LayananKhususModelTest(TestCase): with self.assertRaises(IntegrityError) as ex: obj = Sekolah(name=None) obj.save() - self.assertTrue(ex.expected == django.db.utils.IntegrityError) + self.assertEqual(ex.expected, IntegrityError) def test_models_create_new_sekolah(self): sekolah_setup() @@ -154,7 +151,7 @@ class LayananKhususModelTest(TestCase): with self.assertRaises(IntegrityError) as ex: obj = Penyandang(name=None) obj.save() - self.assertTrue(ex.expected == django.db.utils.IntegrityError) + self.assertEqual(ex.expected, IntegrityError) def test_models_create_new_penyandang(self): penyandang_setup() @@ -165,7 +162,7 @@ class LayananKhususModelTest(TestCase): with self.assertRaises(IntegrityError) as ex: obj = Komunitas(name=None) obj.save() - self.assertTrue(ex.expected == django.db.utils.IntegrityError) + self.assertEqual(ex.expected, IntegrityError) def test_models_create_new_komunitas(self): komunitas_setup() @@ -173,9 +170,7 @@ class LayananKhususModelTest(TestCase): self.assertNotEqual(count, 0) -@in_database('postgres') class LayananKhususViewsTest(TestCase): - databases = 'postgres' urlpatterns = [ path('layanan-khusus/', include('layanan_khusus.urls')), @@ -248,7 +243,9 @@ class LayananKhususViewsTest(TestCase): response = client.post(reverse('register-penyandang-disabilitas'), MOCK_PENYANDANG) content = json.loads(response.content.decode("utf-8")) - expected_json = MOCK_PENYANDANG + expected_json = MOCK_PENYANDANG.copy() + del content['id'] + del expected_json['id'] self.assertEqual(content, expected_json) def test_cannot_get_register_penyandang(self): @@ -298,9 +295,7 @@ class LayananKhususViewsTest(TestCase): self.assertEqual(response.status_code, HTTPStatus.NOT_FOUND) -@in_database('postgres') class LayananKhususSearchTest(TestCase): - databases = 'postgres' urlpatterns = [ path('layanan-khusus/', include('layanan_khusus.urls')), diff --git a/registrasi/tests.py b/registrasi/tests.py index 0bef2cb..32f7ca8 100644 --- a/registrasi/tests.py +++ b/registrasi/tests.py @@ -14,7 +14,7 @@ class RegistrationTest(TestCase): self.assertNotEqual(count, 0) def test_user_model_not_created(self): - with self.assertRaises(IntegrityError) as error: + with self.assertRaises(IntegrityError) as ex: obj = BisaGoUser(user=None) obj.save() - self.assertTrue(str(error.exception).startswith('NOT NULL constraint failed')) + self.assertEqual(ex.expected, IntegrityError) -- GitLab From 03b471aba808bb05fe4edd3fe92e23d7c2742318 Mon Sep 17 00:00:00 2001 From: ariqbasyar Date: Wed, 31 Mar 2021 17:46:45 +0700 Subject: [PATCH 34/97] [GREEN] implemented postgresql config for test (see f1d80dc) - added new .gitignore from gitignore.io - bring back postgresql service to gitlab ci --- .gitignore | 283 +++++++++++++++++++++++++++++++++++++++-- .gitlab-ci.yml | 9 ++ pplbackend/settings.py | 22 ++-- 3 files changed, 296 insertions(+), 18 deletions(-) diff --git a/.gitignore b/.gitignore index 3293b85..fe7c927 100644 --- a/.gitignore +++ b/.gitignore @@ -1,14 +1,281 @@ -# Environments -env/ -#misc +# Created by https://www.toptal.com/developers/gitignore/api/django,vscode,python,virtualenv,dotenv,sonarqube,vim +# Edit at https://www.toptal.com/developers/gitignore?templates=django,vscode,python,virtualenv,dotenv,sonarqube,vim + +### Django ### +*.log +*.pot +*.pyc __pycache__/ +local_settings.py db.sqlite3 -.coverage +db.sqlite3-journal +media + +# If your build process includes running collectstatic, then you probably don't need or want to include staticfiles/ +# in your Git repository. Update and uncomment the following line accordingly. +# /staticfiles/ + +### Django.Python Stack ### +# Byte-compiled / optimized / DLL files +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports htmlcov/ -static/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +pytestdebug.log + +# Translations +*.mo + +# Django stuff: + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ +doc/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +#poetry.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +# .env +.env/ +.venv/ +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ +pythonenv* +.env.export + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy .mypy_cache/ -#vscode +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# operating system-related files +*.DS_Store #file properties cache/storage on macOS +Thumbs.db #thumbnail cache on Windows + +# profiling data +.prof + + +### dotenv ### +.env + +### Python ### +# Byte-compiled / optimized / DLL files + +# C extensions + +# Distribution / packaging + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. + +# Installer logs + +# Unit test / coverage reports + +# Translations + +# Django stuff: + +# Flask stuff: + +# Scrapy stuff: + +# Sphinx documentation + +# PyBuilder + +# Jupyter Notebook + +# IPython + +# pyenv + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. + +# poetry + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow + +# Celery stuff + +# SageMath parsed files + +# Environments +# .env + +# Spyder project settings + +# Rope project settings + +# mkdocs documentation + +# mypy + +# Pyre type checker + +# pytype static type analyzer + +# operating system-related files + +# profiling data + + +### SonarQube ### +# SonarQube ignore files. +# +# https://docs.sonarqube.org/display/SCAN/Analyzing+with+SonarQube+Scanner +# Sonar Scanner working directories +.sonar/ +.scannerwork/ + +# http://www.sonarlint.org/commandline/ +# SonarLint working directories, configuration files (including credentials) +.sonarlint/ + +### Vim ### +# Swap +[._]*.s[a-v][a-z] +!*.svg # comment out if you don't need vector files +[._]*.sw[a-p] +[._]s[a-rt-v][a-z] +[._]ss[a-gi-z] +[._]sw[a-p] + +# Session +Session.vim +Sessionx.vim + +# Temporary +.netrwhist +*~ +# Auto-generated tag files +tags +# Persistent undo +[._]*.un~ + +### VirtualEnv ### +# Virtualenv +# http://iamzed.com/2009/05/07/a-primer-on-virtualenv/ +[Bb]in +[Ii]nclude +[Ll]ib +[Ll]ib64 +[Ll]ocal +[Ss]cripts +pyvenv.cfg +.venv +pip-selfcheck.json + +### vscode ### .vscode/ -#intellij -.idea/ +*.code-workspace + +# End of https://www.toptal.com/developers/gitignore/api/django,vscode,python,virtualenv,dotenv,sonarqube,vim diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f7cad09..a43b2e1 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,3 +1,12 @@ +variables: + POSTGRES_DB: $DB_NAME + POSTGRES_USER: $DB_USER + POSTGRES_PASSWORD: $DB_PASSWORD + POSTGRES_HOST_AUTH_METHOD: trust + +services: + - postgres:12.2-alpine + stages: - test - linter diff --git a/pplbackend/settings.py b/pplbackend/settings.py index b8e8c2b..2f9ee04 100644 --- a/pplbackend/settings.py +++ b/pplbackend/settings.py @@ -100,24 +100,26 @@ DEFAULT_DATABASE = { 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), } +POSTGRES_DATABASE = { + 'ENGINE': 'django.db.backends.postgresql_psycopg2', + 'NAME': os.getenv('DB_NAME'), + 'USER': os.getenv('DB_USER'), + 'PASSWORD': os.getenv('DB_PASSWORD'), + 'HOST': os.getenv('DB_HOST'), + 'PORT': os.getenv('DB_PORT'), +} + DATABASES = { 'default': DEFAULT_DATABASE } # custom database host, overriding the default if os.getenv('DB_HOST') is not None: - DATABASES['default'] = { - 'ENGINE': 'django.db.backends.postgresql_psycopg2', - 'NAME': os.getenv('DB_NAME'), - 'USER': os.getenv('DB_USER'), - 'PASSWORD': os.getenv('DB_PASSWORD'), - 'HOST': os.getenv('DB_HOST'), - 'PORT': os.getenv('DB_PORT'), - } + DATABASES['default'] = POSTGRES_DATABASE -# use default database when running test +# use postgresql database when running test if 'test' in sys.argv: - DATABASES['default'] = DEFAULT_DATABASE + DATABASES['default'] = POSTGRES_DATABASE # Password validation # https://docs.djangoproject.com/en/3.0/ref/settings/#auth-password-validators -- GitLab From 1fd1f3954e1fcc5bedd8397db62f2dcf16f8c11e Mon Sep 17 00:00:00 2001 From: Muhammad Rafif Elfazri Date: Thu, 1 Apr 2021 13:51:25 +0700 Subject: [PATCH 35/97] [CHORES] update MAP_API_KEY variable in settings.py --- pplbackend/settings.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pplbackend/settings.py b/pplbackend/settings.py index 2f9ee04..0c7eeda 100644 --- a/pplbackend/settings.py +++ b/pplbackend/settings.py @@ -241,3 +241,4 @@ PROXIES = { "http": http_proxy, "https": https_proxy } +MAP_API_KEY = os.environ.get('MAP_API_KEY', '') -- GitLab From 19b3c1d3a4a8168de319b4ec5160935bfa9fd76a Mon Sep 17 00:00:00 2001 From: Muhammad Rafif Elfazri Date: Thu, 1 Apr 2021 15:11:42 +0700 Subject: [PATCH 36/97] [RED] Test for update_place_id CronJob --- informasi_fasilitas/test_update_place_id.py | 93 +++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 informasi_fasilitas/test_update_place_id.py diff --git a/informasi_fasilitas/test_update_place_id.py b/informasi_fasilitas/test_update_place_id.py new file mode 100644 index 0000000..b8330b0 --- /dev/null +++ b/informasi_fasilitas/test_update_place_id.py @@ -0,0 +1,93 @@ +from django.test import TestCase +from informasi_fasilitas.test_base import InformasiFasilitasTest +from informasi_fasilitas.management.commands.update_place_id import Command +from informasi_fasilitas.models import Lokasi +from unittest.mock import patch +from django.utils import timezone +from datetime import timedelta + +factory = InformasiFasilitasTest() +command = Command() +time = timezone.now() + +class UpdatePlaceIdTest(TestCase): + def setUp(self) -> None: + user = factory.create_user_test() + lokasi1 = factory.create_lokasi_test({ + "place_id": "SLASSONALDDIONW", + "timestamp": time - timedelta(days=120) + }) + lokasi2 = factory.create_lokasi_test({ + "place_id": "IEUBJWLNDSOHIUJL", + "timestamp": time - timedelta(days=120) + }) + fasilitas1 = factory.create_fasilitas_test(user=user, lokasi=lokasi2) + fasilitas2 = factory.create_fasilitas_test(user=user, lokasi=lokasi2) + + @patch('informasi_fasilitas.management.commands.update_place_id.requests.get') + def test_lokasi_expired(self, mock_get): + mock_get.return_value.json.return_value = {"status": "INVALID_REQUEST"} + command.handle() + lokasi1 = Lokasi.objects.filter(place_id="SLASSONALDDIONW").first() + lokasi2 = Lokasi.objects.filter(place_id="IEUBJWLNDSOHIUJL").first() + self.assertIsNone(lokasi1) + self.assertIsNone(lokasi2) + + @patch('informasi_fasilitas.management.commands.update_place_id.requests.get') + @patch('informasi_fasilitas.management.commands.update_place_id.timezone') + def test_lokasi_new_id(self, mock_timezone ,mock_get): + result_json = [{"status": "OK", + "result": {"place_id": "JSAKDJBSJDSLND"}}, + {"status": "OK", + "result": {"place_id": "JDNSLKNDNKLDSL"}} + ] + mock_get.return_value.json.side_effect = result_json + mock_timezone.now.return_value = time + command.handle() + lokasi1 = Lokasi.objects.filter(place_id="JSAKDJBSJDSLND").first() + lokasi2 = Lokasi.objects.filter(place_id="JDNSLKNDNKLDSL").first() + + self.assertEqual("JSAKDJBSJDSLND", lokasi1.place_id) + self.assertEqual(time, lokasi1.timestamp) + self.assertEqual("JDNSLKNDNKLDSL", lokasi2.place_id) + self.assertEqual(time, lokasi2.timestamp) + + @patch('informasi_fasilitas.management.commands.update_place_id.requests.get') + @patch('informasi_fasilitas.management.commands.update_place_id.timezone') + def test_lokasi_same_id(self, mock_timezone, mock_get): + result_json = [{"status": "OK", + "result": {"place_id": "SLASSONALDDIONW"}}, + {"status": "OK", + "result": {"place_id": "IEUBJWLNDSOHIUJL"}} + ] + mock_get.return_value.json.side_effect = result_json + mock_timezone.now.return_value = time + command.handle() + lokasi1 = Lokasi.objects.filter(place_id="SLASSONALDDIONW").first() + lokasi2 = Lokasi.objects.filter(place_id="IEUBJWLNDSOHIUJL").first() + + self.assertEqual("SLASSONALDDIONW", lokasi1.place_id) + self.assertEqual(time, lokasi1.timestamp) + self.assertEqual("IEUBJWLNDSOHIUJL", lokasi2.place_id) + self.assertEqual(time, lokasi2.timestamp) + + @patch('informasi_fasilitas.management.commands.update_place_id.requests.get') + @patch('informasi_fasilitas.management.commands.update_place_id.timezone') + def test_lokasi_new_id_but_exists(self, mock_timezone, mock_get): + result_json = [{"status": "OK", + "result": {"place_id": "SLASSONALDDIONW"}}, + {"status": "OK", + "result": {"place_id": "SLASSONALDDIONW"}} + ] + mock_get.return_value.json.side_effect = result_json + mock_timezone.now.return_value = time + command.handle() + lokasi1 = Lokasi.objects.filter(place_id="SLASSONALDDIONW").first() + lokasi2 = Lokasi.objects.filter(place_id="IEUBJWLNDSOHIUJL").first() + self.assertEqual("SLASSONALDDIONW", lokasi1.place_id) + self.assertEqual(time, lokasi1.timestamp) + self.assertEqual(2, len(lokasi1.fasilitas_set.all())) + self.assertIsNone(lokasi2) + + + -- GitLab From 5249d440d533dbf59c8b68adece7315f3fca5351 Mon Sep 17 00:00:00 2001 From: Muhammad Rafif Elfazri Date: Thu, 1 Apr 2021 15:12:58 +0700 Subject: [PATCH 37/97] [GREEN] Implemented update_place_id CronJob --- informasi_fasilitas/management/__init__.py | 0 .../management/commands/__init__.py | 0 .../management/commands/update_place_id.py | 53 +++++++++++++++++++ 3 files changed, 53 insertions(+) create mode 100644 informasi_fasilitas/management/__init__.py create mode 100644 informasi_fasilitas/management/commands/__init__.py create mode 100644 informasi_fasilitas/management/commands/update_place_id.py diff --git a/informasi_fasilitas/management/__init__.py b/informasi_fasilitas/management/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/informasi_fasilitas/management/commands/__init__.py b/informasi_fasilitas/management/commands/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/informasi_fasilitas/management/commands/update_place_id.py b/informasi_fasilitas/management/commands/update_place_id.py new file mode 100644 index 0000000..329184d --- /dev/null +++ b/informasi_fasilitas/management/commands/update_place_id.py @@ -0,0 +1,53 @@ +from django.core.management.base import BaseCommand +from django.utils import timezone +from datetime import timedelta +from informasi_fasilitas.models import Lokasi +from django.conf import settings +import requests + +class Command(BaseCommand): + help = 'Update Lokasi place_id from google server' + + def update_place_id(self, lokasi, place_id, time): + if lokasi.place_id != place_id: + message = f"Lokasi place_id {lokasi.place_id} changed to {place_id}" + lokasi_in_db = Lokasi.objects.filter(place_id=place_id).first() + + if lokasi_in_db is not None: + for fasilitas in lokasi.fasilitas_set.all(): + fasilitas.lokasi = lokasi_in_db + fasilitas.save() + lokasi.delete() + lokasi = lokasi_in_db + self.stdout.write(message) + lokasi.place_id = place_id + + lokasi.timestamp = time + lokasi.save() + + def request_place_id(self, place_id): + self.stdout.write(f"Request Refresh Place_id {place_id}") + payload = { + "key": settings.MAP_API_KEY, + "place_id": place_id, + "fields": "place_id" + } + url = "https://maps.googleapis.com/maps/api/place/details/json" + req = requests.get(url, params=payload, proxies=settings.PROXIES) + data = req.json() + return data + + def handle(self, *args, **kwargs): + time = timezone.now() + time_delta = timedelta(days=100) + for lokasi in Lokasi.objects.all(): + lokasi_update_time = lokasi.timestamp + time_delta + if time >= lokasi_update_time: + data = self.request_place_id(lokasi.place_id) + if data.get("status") == "OK": + place_id = data.get("result").get("place_id") + self.update_place_id(lokasi, place_id, time) + else: + self.stdout.write(f"Lokasi with expired place_id {lokasi.place_id} deleted") + lokasi.delete() + -- GitLab From 5da183e4b224058bbd00871f32879188be46c51c Mon Sep 17 00:00:00 2001 From: Muhammad Rafif Elfazri Date: Tue, 6 Apr 2021 08:46:41 +0700 Subject: [PATCH 38/97] [REFACTOR] Reduce Complexity and code smells --- oauth/views.py | 74 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 45 insertions(+), 29 deletions(-) diff --git a/oauth/views.py b/oauth/views.py index 1ea9c01..a69ca2a 100644 --- a/oauth/views.py +++ b/oauth/views.py @@ -17,40 +17,55 @@ def request_token(request): email = request.POST["username"] password = request.POST["password"] google = request.POST.get("google", False) - response = {} if google: access_token = request.POST["access_token"] name = request.POST["name"] - try: - result_code, result_email = validate_google_token(access_token) - if result_code: - user = User.objects.get(email=result_email) - email = result_email - else: - return result_email - except User.DoesNotExist: - user = _create_google_user(email, name) + result = _google_check(access_token, name) else: - try: - user = authenticate(request, username=email, password=password) - if user is None: - User.objects.get(email=email) - except User.DoesNotExist: - response["response"] = "User not exist" - return JsonResponse(response, status=404) - - if user is not None: - if user.is_active: - token, create = Token.objects.get_or_create(user=user) - response = {'username': user.username, 'token': token.key, 'token_type': "token"} - return JsonResponse(response, status=200) - else: - response["response"] = "Please activate your account" - return JsonResponse(response, status=400) + result = _check_normal_auth(request, email, password) + + if isinstance(result, JsonResponse): + return result + user = result + return _check_user(user) + + +def _google_check(access_token, name): + try: + result_code, result_email = validate_google_token(access_token) + if result_code: + user = User.objects.get(email=result_email) else: - response["response"] = "Wrong password" + return result_email + except User.DoesNotExist: + user = _create_google_user(result_email, name) + return user + + +def _check_normal_auth(request, email, password): + try: + user = authenticate(request, username=email, password=password) + if user is None: + User.objects.get(email=email) + except User.DoesNotExist: + response = {"response" : "User not exist"} + return JsonResponse(response, status=404) + return user + + +def _check_user(user): + if user is not None: + if user.is_active: + token, create = Token.objects.get_or_create(user=user) + response = {'username': user.username, 'token': token.key, 'token_type': "token"} + return JsonResponse(response, status=200) + else: + response = {"response": "Please activate your account"} return JsonResponse(response, status=400) + else: + response = {"response": "Wrong password"} + return JsonResponse(response, status=400) def _create_google_user(email, name): @@ -70,15 +85,16 @@ def _create_google_user(email, name): BisaGoUser.objects.create(user=user, phone_number=random_generated_phone_number) return user + def _create_random_phone_number(): - phone_number = 'x'.join([str(random.randint(0, 9)) for i in range(8)]) + phone_number = 'x'.join([str(random.randint(0, 9)) for _ in range(8)]) try: BisaGoUser.objects.get(phone_number=phone_number) return _create_random_phone_number() except BisaGoUser.DoesNotExist: return phone_number -@csrf_exempt + def validate_google_token(access_token): payload = {'access_token': access_token} # validate the token req = requests.get('https://www.googleapis.com/oauth2/v2/userinfo', -- GitLab From efd532208b2d23b5b03dc3c9009ce56c81ca6ac8 Mon Sep 17 00:00:00 2001 From: Muhammad Rafif Elfazri Date: Tue, 6 Apr 2021 15:26:47 +0700 Subject: [PATCH 39/97] [RED] Update new test for validate_access_token function --- oauth/tests.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/oauth/tests.py b/oauth/tests.py index 85b3da6..6d6fb24 100644 --- a/oauth/tests.py +++ b/oauth/tests.py @@ -141,9 +141,8 @@ class TestOauth(TestCase): def test_validate_access_token_valid(self, mock_json_loads, mock_get): mock_get.return_value.text = "DKSJNDKDSKN" mock_json_loads.return_value = {"email": 'mock_user@email.com'} - result_flag, result_email = validate_google_token("DLSLDSMDSBAS^&**") + result_email = validate_google_token("DLSLDSMDSBAS^&**") - self.assertTrue(result_flag) self.assertEqual('mock_user@email.com', result_email) @patch('oauth.views.requests.get') @@ -151,11 +150,10 @@ class TestOauth(TestCase): def test_validate_access_token_invalid(self, mock_json_loads, mock_get): mock_get.return_value.text = "DKSJNDKDSKN" mock_json_loads.return_value = {"error": 'error description'} - result_flag, result_email = validate_google_token("DLSLDSMDSBAS^&**") + result_email = validate_google_token("DLSLDSMDSBAS^&**") json_response = json.loads(result_email.content) self.assertTrue("message" in json_response) self.assertEqual(404, result_email.status_code) - self.assertFalse(result_flag) @patch('oauth.views.random.randint') def test_create_phone_number_exist(self, mock_randint): -- GitLab From e38854de6d8982316e330292937373303b8bf417 Mon Sep 17 00:00:00 2001 From: Muhammad Rafif Elfazri Date: Tue, 6 Apr 2021 15:28:23 +0700 Subject: [PATCH 40/97] [GREEN] Update validate_access_token function and _check_normal_auth --- oauth/views.py | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/oauth/views.py b/oauth/views.py index a69ca2a..335c6e3 100644 --- a/oauth/views.py +++ b/oauth/views.py @@ -17,7 +17,6 @@ def request_token(request): email = request.POST["username"] password = request.POST["password"] google = request.POST.get("google", False) - if google: access_token = request.POST["access_token"] name = request.POST["name"] @@ -33,13 +32,13 @@ def request_token(request): def _google_check(access_token, name): try: - result_code, result_email = validate_google_token(access_token) - if result_code: - user = User.objects.get(email=result_email) + result = validate_google_token(access_token) + if isinstance(result, JsonResponse): + return result else: - return result_email + user = User.objects.get(email=result) except User.DoesNotExist: - user = _create_google_user(result_email, name) + user = _create_google_user(email=result, name=name) return user @@ -48,23 +47,21 @@ def _check_normal_auth(request, email, password): user = authenticate(request, username=email, password=password) if user is None: User.objects.get(email=email) + response = {"response": "Wrong password"} + return JsonResponse(response, status=400) except User.DoesNotExist: - response = {"response" : "User not exist"} + response = {"response": "User not exist"} return JsonResponse(response, status=404) return user def _check_user(user): - if user is not None: - if user.is_active: - token, create = Token.objects.get_or_create(user=user) - response = {'username': user.username, 'token': token.key, 'token_type': "token"} - return JsonResponse(response, status=200) - else: - response = {"response": "Please activate your account"} - return JsonResponse(response, status=400) + if user.is_active: + token, create = Token.objects.get_or_create(user=user) + response = {'username': user.username, 'token': token.key, 'token_type': "token"} + return JsonResponse(response, status=200) else: - response = {"response": "Wrong password"} + response = {"response": "Please activate your account"} return JsonResponse(response, status=400) @@ -102,8 +99,8 @@ def validate_google_token(access_token): data = json.loads(req.text) if 'error' in data or 'email' not in data: content = {'message': 'wrong google token / this google token is already expired.'} - return False, JsonResponse(content, status=404) - return True, data.get("email") + return JsonResponse(content, status=404) + return data.get("email") -- GitLab From 3685131fb9332fe76f67f2aa7424b351f91a73c9 Mon Sep 17 00:00:00 2001 From: Muhammad Rafif Elfazri Date: Mon, 19 Apr 2021 09:28:33 +0700 Subject: [PATCH 41/97] [REFACTOR] Decorator usage to filter response on Oauth request --- oauth/views.py | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/oauth/views.py b/oauth/views.py index 335c6e3..f4699ef 100644 --- a/oauth/views.py +++ b/oauth/views.py @@ -1,5 +1,7 @@ import requests import random +from rest_framework.exceptions import APIException +from rest_framework.decorators import api_view, permission_classes from rest_framework.utils import json from rest_framework.authtoken.models import Token from django.views.decorators.csrf import csrf_exempt @@ -11,23 +13,26 @@ from django.contrib.auth.models import User from django.conf import settings from registrasi.models import BisaGoUser + + @csrf_exempt +@api_view(http_method_names=['POST']) +@permission_classes([]) def request_token(request): - if request.method == "POST": - email = request.POST["username"] - password = request.POST["password"] - google = request.POST.get("google", False) - if google: - access_token = request.POST["access_token"] - name = request.POST["name"] - result = _google_check(access_token, name) - else: - result = _check_normal_auth(request, email, password) + email = request.POST["username"] + password = request.POST["password"] + google = request.POST.get("google", False) + if google: + access_token = request.POST["access_token"] + name = request.POST["name"] + result = _google_check(access_token, name) + else: + result = _check_normal_auth(request, email, password) - if isinstance(result, JsonResponse): - return result - user = result - return _check_user(user) + if isinstance(result, JsonResponse): + return result + user = result + return _check_user(user) def _google_check(access_token, name): -- GitLab From ecdbec6a7802c41938153a6c0aba75dd651458d2 Mon Sep 17 00:00:00 2001 From: Muhammad Rafif Elfazri Date: Mon, 19 Apr 2021 09:41:20 +0700 Subject: [PATCH 42/97] [RED] Change expectation status code 400 to 401 for failed authentication --- oauth/tests.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/oauth/tests.py b/oauth/tests.py index 6d6fb24..1eb9f07 100644 --- a/oauth/tests.py +++ b/oauth/tests.py @@ -68,7 +68,7 @@ class TestOauth(TestCase): 'password': password }) json_response = json.loads(response.content) - self.assertEqual(400, response.status_code) + self.assertEqual(401, response.status_code) self.assertEqual(json_response['response'], "Please activate your account") def test_user_wrong_password(self): @@ -81,7 +81,7 @@ class TestOauth(TestCase): 'password': password }) json_response = json.loads(response.content) - self.assertEqual(400, response.status_code) + self.assertEqual(401, response.status_code) self.assertEqual(json_response['response'], "Wrong password") @patch('oauth.views.json.loads') -- GitLab From a45a003c4dc23211f599e9021939932b42d5e65e Mon Sep 17 00:00:00 2001 From: Muhammad Rafif Elfazri Date: Mon, 19 Apr 2021 10:08:56 +0700 Subject: [PATCH 43/97] [RED] Test for implementing API Exception --- oauth/tests.py | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/oauth/tests.py b/oauth/tests.py index 1eb9f07..18fa166 100644 --- a/oauth/tests.py +++ b/oauth/tests.py @@ -5,7 +5,7 @@ from unittest.mock import patch from django.test import Client from registrasi.models import BisaGoUser from django.contrib.auth.models import User -import time +from rest_framework.exceptions import AuthenticationFailed class TestOauth(TestCase): @@ -56,7 +56,7 @@ class TestOauth(TestCase): }) json_response = json.loads(response.content) self.assertEqual(404, response.status_code) - self.assertEqual(json_response['response'], "User not exist") + self.assertEqual(json_response['detail'], "User doesn't exist") def test_request_token_not_active(self): email = 'mock_user12@email.com' @@ -69,7 +69,7 @@ class TestOauth(TestCase): }) json_response = json.loads(response.content) self.assertEqual(401, response.status_code) - self.assertEqual(json_response['response'], "Please activate your account") + self.assertEqual(json_response['detail'], "Please activate your account") def test_user_wrong_password(self): email = 'mock_user12@email.com' @@ -82,7 +82,7 @@ class TestOauth(TestCase): }) json_response = json.loads(response.content) self.assertEqual(401, response.status_code) - self.assertEqual(json_response['response'], "Wrong password") + self.assertEqual(json_response['detail'], "Wrong password") @patch('oauth.views.json.loads') def test_google_login_exist(self, mock_json_loads): @@ -99,7 +99,6 @@ class TestOauth(TestCase): json_response = json.loads(response.content) self.assertEqual(200, response.status_code) self.assertEqual('mock_user@email.com', json_response.get("username")) - time.sleep(1) @patch('oauth.views.json.loads') @@ -117,7 +116,6 @@ class TestOauth(TestCase): json_response = json.loads(response.content) self.assertEqual(200, response.status_code) self.assertEqual('mock_user4545@email.com', json_response.get("username")) - time.sleep(1) @patch('oauth.views.json.loads') def test_google_login_error(self, mock_json_loads): @@ -132,9 +130,8 @@ class TestOauth(TestCase): 'google': True, }) json_response = json.loads(response.content) - self.assertEqual(404, response.status_code) - self.assertTrue("message" in json_response) - time.sleep(1) + self.assertEqual(401, response.status_code) + self.assertEqual(json_response['detail'], 'Wrong google token / this google token is already expired.') @patch('oauth.views.requests.get') @patch('oauth.views.json.loads') @@ -150,10 +147,8 @@ class TestOauth(TestCase): def test_validate_access_token_invalid(self, mock_json_loads, mock_get): mock_get.return_value.text = "DKSJNDKDSKN" mock_json_loads.return_value = {"error": 'error description'} - result_email = validate_google_token("DLSLDSMDSBAS^&**") - json_response = json.loads(result_email.content) - self.assertTrue("message" in json_response) - self.assertEqual(404, result_email.status_code) + self.assertRaises(AuthenticationFailed, validate_google_token, + "DLSLDSMDSBAS^&**") @patch('oauth.views.random.randint') def test_create_phone_number_exist(self, mock_randint): -- GitLab From fdcfc3d5ca50439a82c9c0017aef8c4dfbd08efb Mon Sep 17 00:00:00 2001 From: Muhammad Rafif Elfazri Date: Mon, 19 Apr 2021 10:09:57 +0700 Subject: [PATCH 44/97] [GREEN] Change Error raising using Raise API Exception --- oauth/views.py | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/oauth/views.py b/oauth/views.py index f4699ef..d516f86 100644 --- a/oauth/views.py +++ b/oauth/views.py @@ -1,6 +1,6 @@ import requests import random -from rest_framework.exceptions import APIException +from rest_framework.exceptions import AuthenticationFailed, NotFound from rest_framework.decorators import api_view, permission_classes from rest_framework.utils import json from rest_framework.authtoken.models import Token @@ -29,8 +29,6 @@ def request_token(request): else: result = _check_normal_auth(request, email, password) - if isinstance(result, JsonResponse): - return result user = result return _check_user(user) @@ -52,11 +50,9 @@ def _check_normal_auth(request, email, password): user = authenticate(request, username=email, password=password) if user is None: User.objects.get(email=email) - response = {"response": "Wrong password"} - return JsonResponse(response, status=400) + raise AuthenticationFailed(detail="Wrong password") except User.DoesNotExist: - response = {"response": "User not exist"} - return JsonResponse(response, status=404) + raise NotFound(detail="User doesn't exist") return user @@ -66,8 +62,7 @@ def _check_user(user): response = {'username': user.username, 'token': token.key, 'token_type': "token"} return JsonResponse(response, status=200) else: - response = {"response": "Please activate your account"} - return JsonResponse(response, status=400) + raise AuthenticationFailed(detail="Please activate your account") def _create_google_user(email, name): @@ -103,8 +98,7 @@ def validate_google_token(access_token): params=payload, proxies=settings.PROXIES) data = json.loads(req.text) if 'error' in data or 'email' not in data: - content = {'message': 'wrong google token / this google token is already expired.'} - return JsonResponse(content, status=404) + raise AuthenticationFailed(detail='Wrong google token / this google token is already expired.') return data.get("email") -- GitLab From dc6ab3f70d7cd7d50fb94b19223a144a84092ccd Mon Sep 17 00:00:00 2001 From: Muhammad Rafif Elfazri Date: Mon, 19 Apr 2021 10:16:55 +0700 Subject: [PATCH 45/97] [GREEN] Change test expectation on new_rest_api module related to oauth --- new_rest_api/tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/new_rest_api/tests.py b/new_rest_api/tests.py index 4f566fb..ef33fb2 100644 --- a/new_rest_api/tests.py +++ b/new_rest_api/tests.py @@ -118,7 +118,7 @@ class UserTests(APITestCase): data = {'username': 'astraykai@gmail.com', 'password':'chingchenghanji'} response = self.client.post(url, data) - self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) def test_activation_function(self): user = BisaGoUser.objects.get(phone_number='089892218567').user -- GitLab From bfd1b6674a5b80d7bbee1f2d66b021bcfa86c027 Mon Sep 17 00:00:00 2001 From: Muhammad Rafif Elfazri Date: Sat, 24 Apr 2021 10:02:37 +0700 Subject: [PATCH 46/97] [REFACTOR] Change Loop All Lokasi to Loop Lokasi that need update --- .../management/commands/update_place_id.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/informasi_fasilitas/management/commands/update_place_id.py b/informasi_fasilitas/management/commands/update_place_id.py index 329184d..47009a2 100644 --- a/informasi_fasilitas/management/commands/update_place_id.py +++ b/informasi_fasilitas/management/commands/update_place_id.py @@ -40,14 +40,13 @@ class Command(BaseCommand): def handle(self, *args, **kwargs): time = timezone.now() time_delta = timedelta(days=100) - for lokasi in Lokasi.objects.all(): - lokasi_update_time = lokasi.timestamp + time_delta - if time >= lokasi_update_time: - data = self.request_place_id(lokasi.place_id) - if data.get("status") == "OK": - place_id = data.get("result").get("place_id") - self.update_place_id(lokasi, place_id, time) - else: - self.stdout.write(f"Lokasi with expired place_id {lokasi.place_id} deleted") - lokasi.delete() + time_check = time-time_delta + for lokasi in Lokasi.objects.filter(timestamp__lte=time_check): + data = self.request_place_id(lokasi.place_id) + if data.get("status") == "OK": + place_id = data.get("result").get("place_id") + self.update_place_id(lokasi, place_id, time) + else: + self.stdout.write(f"Lokasi with expired place_id {lokasi.place_id} deleted") + lokasi.delete() -- GitLab From aa89e18fe902197f021ef39001b69a1df6a4f917 Mon Sep 17 00:00:00 2001 From: Muhammad Rafif Elfazri Date: Sat, 24 Apr 2021 11:19:26 +0700 Subject: [PATCH 47/97] [CHORES] Update Readme.md for CronJob Guide --- README.md | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/README.md b/README.md index a14e5f7..d1b5b29 100644 --- a/README.md +++ b/README.md @@ -31,8 +31,48 @@ PoiPoLeGan ## Table of Content * Install +* Environment * API Testing + + +# Environment + +To run this application, it is recommended to use python virtual environment. To create virtual environment use venv module. + +```python3 +python3 -m venv env +``` + +This command line will create new virtual environment in ```env``` folder. + +For further information on how to use virtual environment check this [link](https://packaging.python.org/guides/installing-using-pip-and-virtual-environments/) + +This app also needs Environment Variables. put the ```.env``` file containing Environment Variables in root project. Below are the template of the environment variables needed for the application, change according to your needs. + +```bash +DEBUG=True +ALLOWED_HOSTS=localhost;127.0.0.1;10.119.105.26 +DB_NAME=bisago_test +DB_USER=test +DB_PASSWORD=test123 +DB_HOST=localhost +DB_PORT=5432 +# SONARQUBE_PROJECT_KEY=829qrwuoghfdsydhq8wipds #Use it for Sonarqube +# SONARQUBE_PROJECT_TOKEN=829whoidsnjuf9qwiyvhkbdjlnka +# SECRET_KEY=dat7923yequwohdsgy86r2186feyisdaf21 # Secret key for .ssh in CI/CD +EMAIL_HOST=localhost +EMAIL_HOST_USER= +EMAIL_HOST_PASSWORD= +GOOGLE_OAUTH2_CLIENT_ID=0826798309-9yuobjwqdt82731yhdsaqewihdsan.apps.googleusercontent.com +GOOGLE_OAUTH2_CLIENT_SECRET=*^78dysuabjkoKNJTE75se%&ADTUCGVJ798d +MAP_API_KEY=*T78vIY75e^*&(Y*OHUr75dtuV) # Google Map API KEY +http_proxy=http://test.ac.id:8080 # Http proxy server +https_proxy=http://test.ac.id:8080 # Http proxy server +``` + + + ## Install The back end side uses django, for installing please use this command: @@ -45,7 +85,39 @@ To check the dependencies that have been installed within file requirements, use pip list ``` + + +# Google Map Integration + +This app storing Google place_id into database to integrate google map places with our app features. Google reccomend to update the stored place id in database every 12 Months according to this [documentation](https://developers.google.com/maps/documentation/places/web-service/place-id ). + +Therefore, The app needs to run a CronJob to check stored Google Map place_id and updated it. Below are the custom django command to update the place_id. + +```bash +python manage.py update_place_id +``` + +Before adding a custom command to a Cronjob, first you need to locate the file path python3 module in virtual environment or global environment and locate the root project file path. Below the path file example. + +```bash +PYTHON_ENV=/opt/bisago/back-end/development/bisago-be/env/bin/python3 +PROJECT_ROOT_ENV=/opt/bisago/back-end/development/bisago-be +``` + +To add the custom commands to CronJob, type command below. + +```bash +crontab -e +``` + +Select your desired text editor to add the cronjob to the crontab file. Below are example lines to run the custom django command for updating stored place_id every 01:00. + +```bash +0 1 * * * /opt/bisago/back-end/development/bisago-be/env/bin/python3 /opt/bisago/back-end/development/bisago-be/manage.py update_place_id +``` + ## API + ### 1. To register your account: Make `POST` request to API endpoint `/api/register` with `name`, `phone_number`, `email`, `password` key. -- GitLab From 93ad1ae5e3507292c3879b3c4c52ba3e24f3229d Mon Sep 17 00:00:00 2001 From: Muhammad Rafif Elfazri Date: Sat, 24 Apr 2021 12:07:35 +0700 Subject: [PATCH 48/97] [REFACTOR] Reduce Complexity and code smells --- README.md | 72 ++++++++++++++ .../management/commands/update_place_id.py | 19 ++-- new_rest_api/tests.py | 2 +- oauth/tests.py | 29 +++--- oauth/views.py | 96 +++++++++++-------- 5 files changed, 147 insertions(+), 71 deletions(-) diff --git a/README.md b/README.md index a14e5f7..d1b5b29 100644 --- a/README.md +++ b/README.md @@ -31,8 +31,48 @@ PoiPoLeGan ## Table of Content * Install +* Environment * API Testing + + +# Environment + +To run this application, it is recommended to use python virtual environment. To create virtual environment use venv module. + +```python3 +python3 -m venv env +``` + +This command line will create new virtual environment in ```env``` folder. + +For further information on how to use virtual environment check this [link](https://packaging.python.org/guides/installing-using-pip-and-virtual-environments/) + +This app also needs Environment Variables. put the ```.env``` file containing Environment Variables in root project. Below are the template of the environment variables needed for the application, change according to your needs. + +```bash +DEBUG=True +ALLOWED_HOSTS=localhost;127.0.0.1;10.119.105.26 +DB_NAME=bisago_test +DB_USER=test +DB_PASSWORD=test123 +DB_HOST=localhost +DB_PORT=5432 +# SONARQUBE_PROJECT_KEY=829qrwuoghfdsydhq8wipds #Use it for Sonarqube +# SONARQUBE_PROJECT_TOKEN=829whoidsnjuf9qwiyvhkbdjlnka +# SECRET_KEY=dat7923yequwohdsgy86r2186feyisdaf21 # Secret key for .ssh in CI/CD +EMAIL_HOST=localhost +EMAIL_HOST_USER= +EMAIL_HOST_PASSWORD= +GOOGLE_OAUTH2_CLIENT_ID=0826798309-9yuobjwqdt82731yhdsaqewihdsan.apps.googleusercontent.com +GOOGLE_OAUTH2_CLIENT_SECRET=*^78dysuabjkoKNJTE75se%&ADTUCGVJ798d +MAP_API_KEY=*T78vIY75e^*&(Y*OHUr75dtuV) # Google Map API KEY +http_proxy=http://test.ac.id:8080 # Http proxy server +https_proxy=http://test.ac.id:8080 # Http proxy server +``` + + + ## Install The back end side uses django, for installing please use this command: @@ -45,7 +85,39 @@ To check the dependencies that have been installed within file requirements, use pip list ``` + + +# Google Map Integration + +This app storing Google place_id into database to integrate google map places with our app features. Google reccomend to update the stored place id in database every 12 Months according to this [documentation](https://developers.google.com/maps/documentation/places/web-service/place-id ). + +Therefore, The app needs to run a CronJob to check stored Google Map place_id and updated it. Below are the custom django command to update the place_id. + +```bash +python manage.py update_place_id +``` + +Before adding a custom command to a Cronjob, first you need to locate the file path python3 module in virtual environment or global environment and locate the root project file path. Below the path file example. + +```bash +PYTHON_ENV=/opt/bisago/back-end/development/bisago-be/env/bin/python3 +PROJECT_ROOT_ENV=/opt/bisago/back-end/development/bisago-be +``` + +To add the custom commands to CronJob, type command below. + +```bash +crontab -e +``` + +Select your desired text editor to add the cronjob to the crontab file. Below are example lines to run the custom django command for updating stored place_id every 01:00. + +```bash +0 1 * * * /opt/bisago/back-end/development/bisago-be/env/bin/python3 /opt/bisago/back-end/development/bisago-be/manage.py update_place_id +``` + ## API + ### 1. To register your account: Make `POST` request to API endpoint `/api/register` with `name`, `phone_number`, `email`, `password` key. diff --git a/informasi_fasilitas/management/commands/update_place_id.py b/informasi_fasilitas/management/commands/update_place_id.py index 329184d..47009a2 100644 --- a/informasi_fasilitas/management/commands/update_place_id.py +++ b/informasi_fasilitas/management/commands/update_place_id.py @@ -40,14 +40,13 @@ class Command(BaseCommand): def handle(self, *args, **kwargs): time = timezone.now() time_delta = timedelta(days=100) - for lokasi in Lokasi.objects.all(): - lokasi_update_time = lokasi.timestamp + time_delta - if time >= lokasi_update_time: - data = self.request_place_id(lokasi.place_id) - if data.get("status") == "OK": - place_id = data.get("result").get("place_id") - self.update_place_id(lokasi, place_id, time) - else: - self.stdout.write(f"Lokasi with expired place_id {lokasi.place_id} deleted") - lokasi.delete() + time_check = time-time_delta + for lokasi in Lokasi.objects.filter(timestamp__lte=time_check): + data = self.request_place_id(lokasi.place_id) + if data.get("status") == "OK": + place_id = data.get("result").get("place_id") + self.update_place_id(lokasi, place_id, time) + else: + self.stdout.write(f"Lokasi with expired place_id {lokasi.place_id} deleted") + lokasi.delete() diff --git a/new_rest_api/tests.py b/new_rest_api/tests.py index 4f566fb..ef33fb2 100644 --- a/new_rest_api/tests.py +++ b/new_rest_api/tests.py @@ -118,7 +118,7 @@ class UserTests(APITestCase): data = {'username': 'astraykai@gmail.com', 'password':'chingchenghanji'} response = self.client.post(url, data) - self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) def test_activation_function(self): user = BisaGoUser.objects.get(phone_number='089892218567').user diff --git a/oauth/tests.py b/oauth/tests.py index 85b3da6..18fa166 100644 --- a/oauth/tests.py +++ b/oauth/tests.py @@ -5,7 +5,7 @@ from unittest.mock import patch from django.test import Client from registrasi.models import BisaGoUser from django.contrib.auth.models import User -import time +from rest_framework.exceptions import AuthenticationFailed class TestOauth(TestCase): @@ -56,7 +56,7 @@ class TestOauth(TestCase): }) json_response = json.loads(response.content) self.assertEqual(404, response.status_code) - self.assertEqual(json_response['response'], "User not exist") + self.assertEqual(json_response['detail'], "User doesn't exist") def test_request_token_not_active(self): email = 'mock_user12@email.com' @@ -68,8 +68,8 @@ class TestOauth(TestCase): 'password': password }) json_response = json.loads(response.content) - self.assertEqual(400, response.status_code) - self.assertEqual(json_response['response'], "Please activate your account") + self.assertEqual(401, response.status_code) + self.assertEqual(json_response['detail'], "Please activate your account") def test_user_wrong_password(self): email = 'mock_user12@email.com' @@ -81,8 +81,8 @@ class TestOauth(TestCase): 'password': password }) json_response = json.loads(response.content) - self.assertEqual(400, response.status_code) - self.assertEqual(json_response['response'], "Wrong password") + self.assertEqual(401, response.status_code) + self.assertEqual(json_response['detail'], "Wrong password") @patch('oauth.views.json.loads') def test_google_login_exist(self, mock_json_loads): @@ -99,7 +99,6 @@ class TestOauth(TestCase): json_response = json.loads(response.content) self.assertEqual(200, response.status_code) self.assertEqual('mock_user@email.com', json_response.get("username")) - time.sleep(1) @patch('oauth.views.json.loads') @@ -117,7 +116,6 @@ class TestOauth(TestCase): json_response = json.loads(response.content) self.assertEqual(200, response.status_code) self.assertEqual('mock_user4545@email.com', json_response.get("username")) - time.sleep(1) @patch('oauth.views.json.loads') def test_google_login_error(self, mock_json_loads): @@ -132,18 +130,16 @@ class TestOauth(TestCase): 'google': True, }) json_response = json.loads(response.content) - self.assertEqual(404, response.status_code) - self.assertTrue("message" in json_response) - time.sleep(1) + self.assertEqual(401, response.status_code) + self.assertEqual(json_response['detail'], 'Wrong google token / this google token is already expired.') @patch('oauth.views.requests.get') @patch('oauth.views.json.loads') def test_validate_access_token_valid(self, mock_json_loads, mock_get): mock_get.return_value.text = "DKSJNDKDSKN" mock_json_loads.return_value = {"email": 'mock_user@email.com'} - result_flag, result_email = validate_google_token("DLSLDSMDSBAS^&**") + result_email = validate_google_token("DLSLDSMDSBAS^&**") - self.assertTrue(result_flag) self.assertEqual('mock_user@email.com', result_email) @patch('oauth.views.requests.get') @@ -151,11 +147,8 @@ class TestOauth(TestCase): def test_validate_access_token_invalid(self, mock_json_loads, mock_get): mock_get.return_value.text = "DKSJNDKDSKN" mock_json_loads.return_value = {"error": 'error description'} - result_flag, result_email = validate_google_token("DLSLDSMDSBAS^&**") - json_response = json.loads(result_email.content) - self.assertTrue("message" in json_response) - self.assertEqual(404, result_email.status_code) - self.assertFalse(result_flag) + self.assertRaises(AuthenticationFailed, validate_google_token, + "DLSLDSMDSBAS^&**") @patch('oauth.views.random.randint') def test_create_phone_number_exist(self, mock_randint): diff --git a/oauth/views.py b/oauth/views.py index 1ea9c01..d516f86 100644 --- a/oauth/views.py +++ b/oauth/views.py @@ -1,5 +1,7 @@ import requests import random +from rest_framework.exceptions import AuthenticationFailed, NotFound +from rest_framework.decorators import api_view, permission_classes from rest_framework.utils import json from rest_framework.authtoken.models import Token from django.views.decorators.csrf import csrf_exempt @@ -11,46 +13,56 @@ from django.contrib.auth.models import User from django.conf import settings from registrasi.models import BisaGoUser + + @csrf_exempt +@api_view(http_method_names=['POST']) +@permission_classes([]) def request_token(request): - if request.method == "POST": - email = request.POST["username"] - password = request.POST["password"] - google = request.POST.get("google", False) - response = {} - - if google: - access_token = request.POST["access_token"] - name = request.POST["name"] - try: - result_code, result_email = validate_google_token(access_token) - if result_code: - user = User.objects.get(email=result_email) - email = result_email - else: - return result_email - except User.DoesNotExist: - user = _create_google_user(email, name) - else: - try: - user = authenticate(request, username=email, password=password) - if user is None: - User.objects.get(email=email) - except User.DoesNotExist: - response["response"] = "User not exist" - return JsonResponse(response, status=404) - - if user is not None: - if user.is_active: - token, create = Token.objects.get_or_create(user=user) - response = {'username': user.username, 'token': token.key, 'token_type': "token"} - return JsonResponse(response, status=200) - else: - response["response"] = "Please activate your account" - return JsonResponse(response, status=400) + email = request.POST["username"] + password = request.POST["password"] + google = request.POST.get("google", False) + if google: + access_token = request.POST["access_token"] + name = request.POST["name"] + result = _google_check(access_token, name) + else: + result = _check_normal_auth(request, email, password) + + user = result + return _check_user(user) + + +def _google_check(access_token, name): + try: + result = validate_google_token(access_token) + if isinstance(result, JsonResponse): + return result else: - response["response"] = "Wrong password" - return JsonResponse(response, status=400) + user = User.objects.get(email=result) + except User.DoesNotExist: + user = _create_google_user(email=result, name=name) + return user + + +def _check_normal_auth(request, email, password): + try: + user = authenticate(request, username=email, password=password) + if user is None: + User.objects.get(email=email) + raise AuthenticationFailed(detail="Wrong password") + except User.DoesNotExist: + raise NotFound(detail="User doesn't exist") + return user + + +def _check_user(user): + if user.is_active: + token, create = Token.objects.get_or_create(user=user) + response = {'username': user.username, 'token': token.key, 'token_type': "token"} + return JsonResponse(response, status=200) + else: + raise AuthenticationFailed(detail="Please activate your account") def _create_google_user(email, name): @@ -70,24 +82,24 @@ def _create_google_user(email, name): BisaGoUser.objects.create(user=user, phone_number=random_generated_phone_number) return user + def _create_random_phone_number(): - phone_number = 'x'.join([str(random.randint(0, 9)) for i in range(8)]) + phone_number = 'x'.join([str(random.randint(0, 9)) for _ in range(8)]) try: BisaGoUser.objects.get(phone_number=phone_number) return _create_random_phone_number() except BisaGoUser.DoesNotExist: return phone_number -@csrf_exempt + def validate_google_token(access_token): payload = {'access_token': access_token} # validate the token req = requests.get('https://www.googleapis.com/oauth2/v2/userinfo', params=payload, proxies=settings.PROXIES) data = json.loads(req.text) if 'error' in data or 'email' not in data: - content = {'message': 'wrong google token / this google token is already expired.'} - return False, JsonResponse(content, status=404) - return True, data.get("email") + raise AuthenticationFailed(detail='Wrong google token / this google token is already expired.') + return data.get("email") -- GitLab From 30f39139efd2f8643c71187d7aa1abb8970f79ab Mon Sep 17 00:00:00 2001 From: Christopher Samuel Date: Sat, 1 May 2021 10:55:21 +0700 Subject: [PATCH 49/97] [RED] Create initial tests for Kegiatan model --- informasi_fasilitas/test_base.py | 15 ++++++++++++++- informasi_fasilitas/test_models.py | 7 ++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/informasi_fasilitas/test_base.py b/informasi_fasilitas/test_base.py index 5db110f..f768810 100644 --- a/informasi_fasilitas/test_base.py +++ b/informasi_fasilitas/test_base.py @@ -1,6 +1,7 @@ import json import tempfile +from datetime import datetime from django.test import TestCase, Client from django.contrib.auth.models import User from django.urls import reverse, path, include @@ -13,7 +14,8 @@ from .models import ( Komentar, KURSI_RODA, Likes, - Dislikes + Dislikes, + Kegiatan ) from pplbackend.utils import get_client_login_with_user @@ -44,6 +46,14 @@ class InformasiFasilitasTest(TestCase): 'deskripsi': 'sangat membantu', } + # Waktu mungkin perlu disesuaikan lagi test dan implementasinya + mock_kegiatan_test = { + 'nama' : 'mock kegiatan', + 'penyelenggara' : 'mock penyelenggara', + 'waktu' : datetime.now(), + 'deskripsi' : 'sebuah deskripsi' + } + def create_user_test(self, user_dict=mock_user_test): return User.objects.create_user(**user_dict) @@ -229,6 +239,9 @@ class InformasiFasilitasTest(TestCase): user=user, fasilitas=fasilitas, ) + + def create_kegiatan_test(self, kegiatan_dict=mock_kegiatan_test): + return Kegiatan.objects.create_kegiatan(**kegiatan_dict) class InformasiFasilitasViewTest(InformasiFasilitasTest): diff --git a/informasi_fasilitas/test_models.py b/informasi_fasilitas/test_models.py index b155d23..7ea2d1c 100644 --- a/informasi_fasilitas/test_models.py +++ b/informasi_fasilitas/test_models.py @@ -1,7 +1,7 @@ from django.db.utils import IntegrityError from .test_base import InformasiFasilitasTest -from .models import Lokasi, Fasilitas, Komentar, Likes, Dislikes +from .models import Lokasi, Fasilitas, Komentar, Likes, Dislikes, Kegiatan class InformasiFasilitasModelTest(InformasiFasilitasTest): @@ -78,3 +78,8 @@ class InformasiFasilitasModelTest(InformasiFasilitasTest): self.create_likes_test() count = Likes.objects.all().count() self.assertNotEqual(count, 0) + + def test_models_create_new_kegiatan(self): + self.create_kegiatan_test() + count = Kegiatan.objects.all().count() + self.assertNotEqual(count, 0) -- GitLab From f060ae094ee60470c863496de616285af326ec84 Mon Sep 17 00:00:00 2001 From: ariqbasyar Date: Mon, 3 May 2021 00:37:19 +0700 Subject: [PATCH 50/97] [RED] test for date_time at detail fasilitas --- informasi_fasilitas/test_views_fasilitas.py | 32 +++++++++++++++++---- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/informasi_fasilitas/test_views_fasilitas.py b/informasi_fasilitas/test_views_fasilitas.py index 9fa8193..0f6bad8 100644 --- a/informasi_fasilitas/test_views_fasilitas.py +++ b/informasi_fasilitas/test_views_fasilitas.py @@ -5,6 +5,7 @@ from django.urls import reverse from .test_base import InformasiFasilitasViewTest from .models import Fasilitas, Lokasi +from .views import TIME_FORMAT from pplbackend.utils import response_decode @@ -48,6 +49,7 @@ class FasilitasRelatedViewTest(InformasiFasilitasViewTest): 'creator': self.fasilitas.user.last_name, 'like': self.fasilitas.like, 'dislike': self.fasilitas.dislike, + 'date_time': self.fasilitas.date_time.strftime(TIME_FORMAT), 'rating': self.fasilitas.rating, 'tag': 'KR', 'disabilitas': ['DF'], @@ -57,8 +59,30 @@ class FasilitasRelatedViewTest(InformasiFasilitasViewTest): }, } - time = self.fasilitas.date_time.strftime("%d-%m-%Y %H:%M:%S") - expected_json[str(self.fasilitas.id)]['date_time'] = time + self.assertEqual(content, expected_json) + + def test_can_get_detail_fasilitas_json(self): + response = Client().get(self.get_detail_fasilitas_url) + content = json.loads(response.content.decode('utf-8')) + fasilitas = Fasilitas.objects.get(id=self.fasilitas.id) + expected_json = { + "id": fasilitas.id, + "place_id": fasilitas.lokasi.place_id, + "deskripsi": fasilitas.deskripsi, + "creator": self.user.last_name, + "creator_email": self.user.email, + "date_time": fasilitas.date_time.strftime(TIME_FORMAT), + "like": fasilitas.like, + "dislike": fasilitas.dislike, + "rating": fasilitas.rating, + "tag": fasilitas.tag, + "disabilitas": fasilitas.disabilitas, + "jumlah": fasilitas.jumlah, + "image": '/media/' + str(fasilitas.image), + "is_verified": fasilitas.is_verified + } + + self.assertEqual(response.status_code, HTTPStatus.OK) self.assertEqual(content, expected_json) def test_cannot_post_list_fasilitas(self): @@ -74,10 +98,6 @@ class FasilitasRelatedViewTest(InformasiFasilitasViewTest): response = Client().get(self.get_detail_fasilitas_url) self.assertEqual(response.status_code, HTTPStatus.OK) - def test_can_get_detail_fasilitas_json(self): - response = Client().get(self.get_detail_fasilitas_url) - self.assertEqual(response.status_code, HTTPStatus.OK) - def test_cannot_post_detail_fasilitas(self): response = Client().post(self.get_detail_fasilitas_url) self.assertEqual(response.status_code, HTTPStatus.METHOD_NOT_ALLOWED) -- GitLab From 88929c09db48e61712ac4497e79f16187e8b3c81 Mon Sep 17 00:00:00 2001 From: ariqbasyar Date: Mon, 3 May 2021 00:41:20 +0700 Subject: [PATCH 51/97] [CHORES] fix date_time attribute incorrect format --- informasi_fasilitas/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/informasi_fasilitas/views.py b/informasi_fasilitas/views.py index de5e357..e5904ef 100644 --- a/informasi_fasilitas/views.py +++ b/informasi_fasilitas/views.py @@ -141,7 +141,7 @@ def detail_fasilitas(request, place_id, id): "deskripsi": fasilitas.deskripsi, "creator": user.last_name, "creator_email": user.email, - "date_time": fasilitas.date_time, + "date_time": fasilitas.date_time.strftime(TIME_FORMAT), "like": fasilitas.like, "dislike": fasilitas.dislike, "rating": fasilitas.rating, -- GitLab From 34f186b1f3c6a31761c88334ebf434b46324ce21 Mon Sep 17 00:00:00 2001 From: Muhammad Rafif Elfazri Date: Wed, 5 May 2021 14:16:53 +0700 Subject: [PATCH 52/97] [RED] Create Kegiatan models for PBI-9 --- informasi_fasilitas/models.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/informasi_fasilitas/models.py b/informasi_fasilitas/models.py index 1e90a63..4caf61f 100644 --- a/informasi_fasilitas/models.py +++ b/informasi_fasilitas/models.py @@ -73,6 +73,18 @@ class Fasilitas(models.Model): image = models.ImageField(upload_to="fasilitas/", null=True, default=None) is_verified = models.BooleanField(default=False) + +class Kegiatan(models.Model): + objects = models.Manager() + lokasi = models.ForeignKey(Lokasi, on_delete=models.CASCADE) + user = models.ForeignKey(User, on_delete=models.CASCADE) + nama_kegiatan = models.CharField(max_length=50) + penyelenggara = models.CharField(max_length=50) + deskripsi = models.TextField(null=False) + time_start = models.DateTimeField(blank=False, null=False, default=timezone.now) + time_end = models.DateTimeField(blank=True, null=True) + + class Komentar(models.Model): objects = models.Manager() fasilitas = models.ForeignKey(Fasilitas, on_delete=models.CASCADE) -- GitLab From d19dcc90c27d471fd7db2b8005ca8523620e5abb Mon Sep 17 00:00:00 2001 From: Christopher Samuel Date: Thu, 6 May 2021 17:34:45 +0700 Subject: [PATCH 53/97] [RED] Update tests + create base models for FotoKegiatan and Komentar Kegiatan, waiting for Kegiatan model --- ..._fotokegiatan_kegiatan_komentarkegiatan.py | 48 ++++++++++++++ informasi_fasilitas/models.py | 14 ++++- informasi_fasilitas/test_base.py | 63 +++++++++++++++++-- informasi_fasilitas/test_models.py | 12 +++- 4 files changed, 130 insertions(+), 7 deletions(-) create mode 100644 informasi_fasilitas/migrations/0013_fotokegiatan_kegiatan_komentarkegiatan.py diff --git a/informasi_fasilitas/migrations/0013_fotokegiatan_kegiatan_komentarkegiatan.py b/informasi_fasilitas/migrations/0013_fotokegiatan_kegiatan_komentarkegiatan.py new file mode 100644 index 0000000..b6e7a89 --- /dev/null +++ b/informasi_fasilitas/migrations/0013_fotokegiatan_kegiatan_komentarkegiatan.py @@ -0,0 +1,48 @@ +# Generated by Django 3.1.7 on 2021-05-06 10:09 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('informasi_fasilitas', '0012_auto_20210329_1332'), + ] + + operations = [ + migrations.CreateModel( + name='Kegiatan', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('nama_kegiatan', models.CharField(max_length=50)), + ('penyelenggara', models.CharField(max_length=50)), + ('deskripsi', models.TextField()), + ('time_start', models.DateTimeField(default=django.utils.timezone.now)), + ('time_end', models.DateTimeField(blank=True, null=True)), + ('lokasi', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='informasi_fasilitas.lokasi')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='KomentarKegiatan', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('deskripsi', models.TextField()), + ('created', models.DateTimeField(auto_now_add=True)), + ('kegiatan', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='informasi_fasilitas.kegiatan')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='FotoKegiatan', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('foto', models.ImageField(upload_to='')), + ('kegiatan', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='informasi_fasilitas.kegiatan')), + ], + ), + ] diff --git a/informasi_fasilitas/models.py b/informasi_fasilitas/models.py index 4caf61f..21a166d 100644 --- a/informasi_fasilitas/models.py +++ b/informasi_fasilitas/models.py @@ -102,4 +102,16 @@ class Dislikes(models.Model): objects = models.Manager() user = models.ForeignKey(User, on_delete=models.CASCADE) fasilitas = models.ForeignKey(Fasilitas, on_delete=models.CASCADE) - created = models.DateTimeField(auto_now_add=True) \ No newline at end of file + created = models.DateTimeField(auto_now_add=True) + +class KomentarKegiatan(models.Model): + objects = models.Manager() + user = models.ForeignKey(User, on_delete=models.CASCADE) + kegiatan = models.ForeignKey(Kegiatan, on_delete=models.CASCADE) + deskripsi = models.TextField() + created = models.DateTimeField(auto_now_add=True) + +class FotoKegiatan(models.Model): + objects = models.Manager() + kegiatan = models.ForeignKey(Kegiatan, on_delete=models.CASCADE) + foto = models.ImageField() diff --git a/informasi_fasilitas/test_base.py b/informasi_fasilitas/test_base.py index f768810..4180515 100644 --- a/informasi_fasilitas/test_base.py +++ b/informasi_fasilitas/test_base.py @@ -15,7 +15,9 @@ from .models import ( KURSI_RODA, Likes, Dislikes, - Kegiatan + Kegiatan, + FotoKegiatan, + KomentarKegiatan ) from pplbackend.utils import get_client_login_with_user @@ -48,12 +50,21 @@ class InformasiFasilitasTest(TestCase): # Waktu mungkin perlu disesuaikan lagi test dan implementasinya mock_kegiatan_test = { - 'nama' : 'mock kegiatan', + 'nama_kegiatan' : 'mock kegiatan', 'penyelenggara' : 'mock penyelenggara', - 'waktu' : datetime.now(), + 'time_start' : datetime.now(), + 'time_end' : datetime.now(), 'deskripsi' : 'sebuah deskripsi' } + mock_komentar_kegiatan_test = { + 'deskripsi': 'sangat membantu' + } + + mock_foto_kegiatan_test = { + 'foto' : '/media/lokasi/Screen_Shot_2020-12-29_at_01.48.32.png', + } + def create_user_test(self, user_dict=mock_user_test): return User.objects.create_user(**user_dict) @@ -240,8 +251,50 @@ class InformasiFasilitasTest(TestCase): fasilitas=fasilitas, ) - def create_kegiatan_test(self, kegiatan_dict=mock_kegiatan_test): - return Kegiatan.objects.create_kegiatan(**kegiatan_dict) + def create_kegiatan_test( + self, + user=None, + user_dict=mock_user_test, + lokasi=None, + lokasi_dict=mock_lokasi_test, + kegiatan_dict=mock_kegiatan_test + ): + user = self.get_or_create_user_test( + user=user, + user_dict=user_dict, + ) + + lokasi = self.get_or_create_lokasi_test( + lokasi=lokasi, + lokasi_dict=lokasi_dict, + ) + return Kegiatan.objects.create(**kegiatan_dict, user=user, lokasi=lokasi) + + def create_foto_kegiatan_test( + self, + foto_kegiatan_dict=mock_foto_kegiatan_test + ): + kegiatan = self.create_kegiatan_test() + return FotoKegiatan.objects.create(**foto_kegiatan_dict, kegiatan=kegiatan) + + def create_komentar_kegiatan_test( + self, + user=None, + komentar_kegiatan_dict=mock_komentar_kegiatan_test, + user_dict=mock_user_test + ): + user = self.get_or_create_user_test( + user=user, + user_dict=user_dict, + ) + + kegiatan = self.create_kegiatan_test() + + return KomentarKegiatan.objects.create( + **komentar_kegiatan_dict, + kegiatan=kegiatan, + user=user + ) class InformasiFasilitasViewTest(InformasiFasilitasTest): diff --git a/informasi_fasilitas/test_models.py b/informasi_fasilitas/test_models.py index 7ea2d1c..fae0d4c 100644 --- a/informasi_fasilitas/test_models.py +++ b/informasi_fasilitas/test_models.py @@ -1,7 +1,7 @@ from django.db.utils import IntegrityError from .test_base import InformasiFasilitasTest -from .models import Lokasi, Fasilitas, Komentar, Likes, Dislikes, Kegiatan +from .models import Lokasi, Fasilitas, Komentar, Likes, Dislikes, Kegiatan, FotoKegiatan, KomentarKegiatan class InformasiFasilitasModelTest(InformasiFasilitasTest): @@ -83,3 +83,13 @@ class InformasiFasilitasModelTest(InformasiFasilitasTest): self.create_kegiatan_test() count = Kegiatan.objects.all().count() self.assertNotEqual(count, 0) + + def test_models_create_new_foto_kegiatan(self): + self.create_foto_kegiatan_test() + count = FotoKegiatan.objects.all().count() + self.assertNotEqual(count, 0) + + def test_models_create_new_komentar_kegiatan(self): + self.create_komentar_kegiatan_test() + count = KomentarKegiatan.objects.all().count() + self.assertNotEqual(count, 0) -- GitLab From 0b9143dd38961ed6e43c1793dd75d81cea484330 Mon Sep 17 00:00:00 2001 From: Christopher Samuel Date: Sat, 8 May 2021 21:28:24 +0700 Subject: [PATCH 54/97] [RED] Update tests + create base models for FotoKegiatan and Komentar Kegiatan, waiting for Kegiatan model --- informasi_fasilitas/test_views_kegiatan.py | 118 +++++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 informasi_fasilitas/test_views_kegiatan.py diff --git a/informasi_fasilitas/test_views_kegiatan.py b/informasi_fasilitas/test_views_kegiatan.py new file mode 100644 index 0000000..3fc98e5 --- /dev/null +++ b/informasi_fasilitas/test_views_kegiatan.py @@ -0,0 +1,118 @@ +import json +from http import HTTPStatus +from django.test import Client +from django.urls import reverse + +from .test_base import InformasiFasilitasViewTest +from .models import Lokasi, Kegiatan, FotoKegiatan, KomentarKegiatan +from pplbackend.utils import response_decode + +class KegiatanRelatedViewTest(InformasiFasilitasViewTest): + + def setUp(self): + super().setUp() + + self.kegiatan = self.create_kegiatan_test(self.user, self.lokasi) + self.kwargs_place_id = {'place_id': self.lokasi.place_id} + self.kwargs_add_or_update_kegiatan = { + 'place_id': self.lokasi.place_id, + 'id': self.kegiatan.id, + } + self.kwargs_kegiatan_id = {'kegiatan_id' : self.kegiatan.place_id} + + self.get_list_kegiatan_url = \ + reverse('list-kegiatan', kwargs=self.kwargs_place_id) + self.get_detail_kegiatan_url = \ + reverse('detail-kegiatan', kwargs=self.kwargs_add_or_update_kegiatan) + 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) + + self.add_komentar_kegiatan_url = \ + reverse('add-komentar-kegiatan', kwargs=self.kwargs_kegiatan_id) + self.list_komentar_kegiatan_url = \ + reverse('list-komentar-kegiatan', kwargs=self.kwargs_kegiatan_id) + + self.add_foto_kegiatan_url = \ + reverse('add-foto-kegiatan', kwargs=self.kwargs_kegiatan_id) + self.list_foto_kegiatan_url = \ + reverse('list-foto-kegiatan', kwargs=self.kwargs_kegiatan_id) + + def test_can_add_kegiatan(self): + Kegiatan.objects.all().delete() + response = \ + self.client.post(self.add_kegiatan_url, self.mock_kegiatan_test) + self.assertEqual(response.status_code, HTTPStatus.CREATED) + count = Kegiatan.objects.all().count() + self.assertEqual(count, 1) + + def test_can_get_list_kegiatan(self): + response = Client().get(self.get_list_kegiatan_url) + self.assertEqual(response.status_code, HTTPStatus.OK) + content = json.loads(response.content.decode('utf-8')) + expected_json = { + str(self.kegiatan.id): { + 'id': self.kegiatan.id, + 'place_id': self.kegiatan.lokasi.place_id, + 'user': self.kegiatan.user.last_name, + 'nama_kegiatan' : self.kegiatan.nama_kegiatan, + 'penyelenggara': self.kegiatan.penyelenggara, + 'deskripsi': self.kegiatan.deskripsi, + 'time_start': self.kegiatan.time_start, + 'time_end': self.kegiatan.time_end, + }, + } + self.assertEqual(content, expected_json) + + def test_can_put_update_kegiatan(self): + send_data = { + 'deskripsi': 'deskripsi kegiatan baru untuk test', + 'nama_kegiatan': 'nama kegiatan baru untuk test', + } + response = self.client.put(self.update_kegiatan_url, data=send_data, + content_type=self.put_content_type) + self.assertEqual(response.status_code, HTTPStatus.ACCEPTED) + + def test_can_add_komentar_kegiatan(self): + KomentarKegiatan.objects.all().delete() + response = \ + self.client.post(self.add_komentar_kegiatan_url, self.mock_komentar_kegiatan_test) + self.assertEqual(response.status_code, HTTPStatus.CREATED) + + count = KomentarKegiatan.objects.filter(kegiatan=self.kegiatan).count() + self.assertEqual(count, 1) + + def test_can_get_list_komentar_kegiatan(self): + komentar = self.create_komentar_kegiatan_test(user=self.user) + response = self.client.get(self.list_komentar_kegiatan_url) + self.assertEqual(response.status_code, HTTPStatus.OK) + + response_json = response_decode(response) + expected_json = { + str(komentar.id): { + 'id': komentar.id, + 'user': komentar.user.last_name, + 'kegiatan': komentar.kegiatan.id, + 'deskripsi': komentar.deskripsi, + } + } + self.assertEqual(response_json, expected_json) + + def test_can_add_foto_kegiatan(self): + FotoKegiatan.objects.all().delete() + response = \ + self.client.post(self.add_foto_kegiatan_url, self.mock_foto_kegiatan_test) + self.assertEqual(response.status_code, HTTPStatus.CREATED) + + count = FotoKegiatan.objects.filter(kegiatan=self.kegiatan).count() + self.assertEqual(count, 1) + + def test_can_get_list_foto_kegiatan(self): + foto = self.create_foto_kegiatan_test() + response = self.client.get(self.list_foto_kegiatan_url) + self.assertEqual(response.status_code, HTTPStatus.OK) + + response_json = response_decode(response) + self.assertEqual(response_json['id'], foto.id) + self.assertIsNotNone(response_json['foto']) -- GitLab From 7cf95d3d3212a6a9a439836885d40d1f59cab663 Mon Sep 17 00:00:00 2001 From: Muhammad Rafif Elfazri Date: Sun, 9 May 2021 10:11:30 +0700 Subject: [PATCH 55/97] [CHORES] Create serializer for models kegiatan --- informasi_fasilitas/serializers.py | 33 +++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/informasi_fasilitas/serializers.py b/informasi_fasilitas/serializers.py index e191b99..178dd12 100644 --- a/informasi_fasilitas/serializers.py +++ b/informasi_fasilitas/serializers.py @@ -1,6 +1,6 @@ from rest_framework import serializers -from .models import Lokasi +from .models import Lokasi, Kegiatan class LokasiSerializer(serializers.ModelSerializer): @@ -15,3 +15,34 @@ class LokasiSerializer(serializers.ModelSerializer): 'read_only': True, }, } + + +class KegiatanSerializer(serializers.ModelSerializer): + class Meta: + model = Kegiatan + fields = ('nama_kegiatan', 'penyelenggara', 'deskripsi', + 'time_start', 'time_end') + extra_kwargs = { + 'nama_kegiatan': {'required': True}, + 'penyelenggara': {'required': True}, + 'nama_kegiatan': {'required': True}, + 'deskripsi': {'required': True}, + 'time_start': {'required': True} + } + + +class KegiatanFullSerializer(serializers.ModelSerializer): + place_id = serializers.CharField(source='lokasi.place_id', read_only=True) + creator = serializers.CharField(source='user.last_name', read_only=True) + class Meta: + model = Kegiatan + fields = ('id', 'place_id', 'creator', + 'nama_kegiatan', 'penyelenggara', 'deskripsi', + 'time_start', 'time_end') + extra_kwargs = { + 'nama_kegiatan': {'required': True}, + 'penyelenggara': {'required': True}, + 'nama_kegiatan': {'required': True}, + 'deskripsi': {'required': True}, + 'time_start': {'required': True} + } -- GitLab From 355ae218c6857c5f3936142c68ded0ce088a5013 Mon Sep 17 00:00:00 2001 From: Muhammad Rafif Elfazri Date: Sun, 9 May 2021 10:41:25 +0700 Subject: [PATCH 56/97] [RED] Change DateTime data in self.mock_kegiatan_test to string --- informasi_fasilitas/test_base.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/informasi_fasilitas/test_base.py b/informasi_fasilitas/test_base.py index 4180515..6ef56a4 100644 --- a/informasi_fasilitas/test_base.py +++ b/informasi_fasilitas/test_base.py @@ -50,11 +50,11 @@ class InformasiFasilitasTest(TestCase): # Waktu mungkin perlu disesuaikan lagi test dan implementasinya mock_kegiatan_test = { - 'nama_kegiatan' : 'mock kegiatan', - 'penyelenggara' : 'mock penyelenggara', - 'time_start' : datetime.now(), - 'time_end' : datetime.now(), - 'deskripsi' : 'sebuah deskripsi' + 'nama_kegiatan': 'mock kegiatan', + 'penyelenggara': 'mock penyelenggara', + 'time_start': datetime.now().strftime("%Y-%m-%d %H:%M"), + 'time_end': datetime.now().strftime("%Y-%m-%d %H:%M"), + 'deskripsi': 'sebuah deskripsi' } mock_komentar_kegiatan_test = { -- GitLab From 077fb43d2b03bb10723cdfc806085ebacd21ec1a Mon Sep 17 00:00:00 2001 From: Muhammad Rafif Elfazri Date: Sun, 9 May 2021 13:27:05 +0700 Subject: [PATCH 57/97] [CHORES] Add default DateTimeFromat for Django Rest Framework --- pplbackend/settings.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pplbackend/settings.py b/pplbackend/settings.py index 0c7eeda..1804a87 100644 --- a/pplbackend/settings.py +++ b/pplbackend/settings.py @@ -200,6 +200,7 @@ SOCIALACCOUNT_PROVIDERS = { LOGIN_REDIRECT_URL = '/' REST_FRAMEWORK = { + 'DATETIME_FORMAT': "%Y-%m-%d %H:%M", 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', 'PAGE_SIZE': 10, 'DEFAULT_AUTHENTICATION_CLASSES': [ -- GitLab From 897a099b274a4108b2fd4a373b40fb893eae7bb4 Mon Sep 17 00:00:00 2001 From: Muhammad Rafif Elfazri Date: Sun, 9 May 2021 13:28:09 +0700 Subject: [PATCH 58/97] [CHORES] Change Client() function from django.test to rest_framework.test modules --- pplbackend/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pplbackend/utils.py b/pplbackend/utils.py index dac4c0a..1273caf 100644 --- a/pplbackend/utils.py +++ b/pplbackend/utils.py @@ -1,7 +1,7 @@ from rest_framework.test import APIClient from rest_framework_simplejwt.tokens import RefreshToken from rest_framework.authtoken.models import Token - +from rest_framework.test import APIClient from django.urls import reverse from django.test import Client @@ -9,7 +9,7 @@ import json def get_client_login_with_user(user): token, _ = Token.objects.get_or_create(user=user) - client = Client(HTTP_AUTHORIZATION=f'token {token.key}') + client = APIClient(HTTP_AUTHORIZATION=f'token {token.key}') return client -- GitLab From ae33afa4514f08f81a3bb3e18e710cf0a96299b0 Mon Sep 17 00:00:00 2001 From: Muhammad Rafif Elfazri Date: Sun, 9 May 2021 13:30:35 +0700 Subject: [PATCH 59/97] [RED] Change some test for kegiatan models and add test if Lokasi or kegiatan models not found in update and add --- informasi_fasilitas/test_views_kegiatan.py | 77 +++++++++++++++++----- 1 file changed, 61 insertions(+), 16 deletions(-) diff --git a/informasi_fasilitas/test_views_kegiatan.py b/informasi_fasilitas/test_views_kegiatan.py index 3fc98e5..3209f7f 100644 --- a/informasi_fasilitas/test_views_kegiatan.py +++ b/informasi_fasilitas/test_views_kegiatan.py @@ -18,7 +18,7 @@ class KegiatanRelatedViewTest(InformasiFasilitasViewTest): 'place_id': self.lokasi.place_id, 'id': self.kegiatan.id, } - self.kwargs_kegiatan_id = {'kegiatan_id' : self.kegiatan.place_id} + self.kwargs_kegiatan_id = {'kegiatan_id' : self.kegiatan.lokasi.place_id} self.get_list_kegiatan_url = \ reverse('list-kegiatan', kwargs=self.kwargs_place_id) @@ -29,24 +29,49 @@ class KegiatanRelatedViewTest(InformasiFasilitasViewTest): self.update_kegiatan_url = reverse('update-kegiatan', kwargs=self.kwargs_add_or_update_kegiatan) - self.add_komentar_kegiatan_url = \ - reverse('add-komentar-kegiatan', kwargs=self.kwargs_kegiatan_id) - self.list_komentar_kegiatan_url = \ - reverse('list-komentar-kegiatan', kwargs=self.kwargs_kegiatan_id) - - self.add_foto_kegiatan_url = \ - reverse('add-foto-kegiatan', kwargs=self.kwargs_kegiatan_id) - self.list_foto_kegiatan_url = \ - reverse('list-foto-kegiatan', kwargs=self.kwargs_kegiatan_id) + # self.add_komentar_kegiatan_url = \ + # reverse('add-komentar-kegiatan', kwargs=self.kwargs_kegiatan_id) + # self.list_komentar_kegiatan_url = \ + # reverse('list-komentar-kegiatan', kwargs=self.kwargs_kegiatan_id) + # + # self.add_foto_kegiatan_url = \ + # reverse('add-foto-kegiatan', kwargs=self.kwargs_kegiatan_id) + # self.list_foto_kegiatan_url = \ + # reverse('list-foto-kegiatan', kwargs=self.kwargs_kegiatan_id) def test_can_add_kegiatan(self): Kegiatan.objects.all().delete() response = \ self.client.post(self.add_kegiatan_url, self.mock_kegiatan_test) + data = response.json() + data.pop("id", None) + expected_json = self.mock_kegiatan_test.copy() + expected_json.update({'creator': 'mock last_name', + 'place_id': 'mock_place_id' + }) self.assertEqual(response.status_code, HTTPStatus.CREATED) + self.assertDictEqual(data, expected_json) + count = Kegiatan.objects.all().count() self.assertEqual(count, 1) + def test_add_kegiatan_with_missing_params(self): + Kegiatan.objects.all().delete() + response_params = self.mock_kegiatan_test.copy() + response_params.pop("time_start", None) + response = \ + self.client.post(self.add_kegiatan_url, response_params) + self.assertEqual(response.status_code, HTTPStatus.BAD_REQUEST) + + def test_add_kegiatan_lokasi_not_exist(self): + Kegiatan.objects.all().delete() + url = reverse('add-kegiatan', kwargs={ + 'place_id': "IDSNKCM", + }) + response_params = self.mock_kegiatan_test.copy() + response = self.client.post(url, response_params) + self.assertEqual(response.status_code, HTTPStatus.NOT_FOUND) + def test_can_get_list_kegiatan(self): response = Client().get(self.get_list_kegiatan_url) self.assertEqual(response.status_code, HTTPStatus.OK) @@ -55,7 +80,7 @@ class KegiatanRelatedViewTest(InformasiFasilitasViewTest): str(self.kegiatan.id): { 'id': self.kegiatan.id, 'place_id': self.kegiatan.lokasi.place_id, - 'user': self.kegiatan.user.last_name, + 'creator': self.kegiatan.user.last_name, 'nama_kegiatan' : self.kegiatan.nama_kegiatan, 'penyelenggara': self.kegiatan.penyelenggara, 'deskripsi': self.kegiatan.deskripsi, @@ -70,10 +95,30 @@ class KegiatanRelatedViewTest(InformasiFasilitasViewTest): 'deskripsi': 'deskripsi kegiatan baru untuk test', 'nama_kegiatan': 'nama kegiatan baru untuk test', } - response = self.client.put(self.update_kegiatan_url, data=send_data, - content_type=self.put_content_type) + response = self.client.put(self.update_kegiatan_url, data=send_data) + data = response.json() + data.pop("id", None) + expected_json = self.mock_kegiatan_test.copy() + expected_json.update({'creator': 'mock last_name', + 'place_id': 'mock_place_id' + }) + expected_json.update(send_data) self.assertEqual(response.status_code, HTTPStatus.ACCEPTED) - + self.assertDictEqual(data, expected_json) + + def test_update_kegiatan_lokasi_not_exist(self): + Kegiatan.objects.all().delete() + url = reverse('update-kegiatan', kwargs={ + 'place_id': "IDSNKCM", + 'id': 200, + }) + send_data = { + 'deskripsi': 'deskripsi kegiatan baru untuk test', + 'nama_kegiatan': 'nama kegiatan baru untuk test', + } + response = self.client.put(self.update_kegiatan_url, data=send_data) + self.assertEqual(response.status_code, HTTPStatus.NOT_FOUND) + def test_can_add_komentar_kegiatan(self): KomentarKegiatan.objects.all().delete() response = \ @@ -98,7 +143,7 @@ class KegiatanRelatedViewTest(InformasiFasilitasViewTest): } } self.assertEqual(response_json, expected_json) - + def test_can_add_foto_kegiatan(self): FotoKegiatan.objects.all().delete() response = \ @@ -107,7 +152,7 @@ class KegiatanRelatedViewTest(InformasiFasilitasViewTest): count = FotoKegiatan.objects.filter(kegiatan=self.kegiatan).count() self.assertEqual(count, 1) - + def test_can_get_list_foto_kegiatan(self): foto = self.create_foto_kegiatan_test() response = self.client.get(self.list_foto_kegiatan_url) -- GitLab From 89329118889810e6ba4199219ab60f82d3b946df Mon Sep 17 00:00:00 2001 From: Muhammad Rafif Elfazri Date: Sun, 9 May 2021 13:31:55 +0700 Subject: [PATCH 60/97] [GREEN] Implement views only for Kegiatan models GET, POST, and PUT request --- informasi_fasilitas/urls.py | 14 ++++- informasi_fasilitas/views_kegiatan.py | 77 +++++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 informasi_fasilitas/views_kegiatan.py diff --git a/informasi_fasilitas/urls.py b/informasi_fasilitas/urls.py index eacb246..2308642 100644 --- a/informasi_fasilitas/urls.py +++ b/informasi_fasilitas/urls.py @@ -1,5 +1,5 @@ from django.urls import path -from . import views +from . import views, views_kegiatan list_lokasi_views = views.LokasiListCreateView.as_view({'get':'list'}) add_lokasi_views = views.LokasiListCreateView.as_view({'post':'create'}) @@ -29,4 +29,16 @@ urlpatterns = [ path('lokasi/like-fasilitas////', views.update_like_fasilitas, name='update-like-fasilitas'), + + path('lokasi/list-kegiatan//', + views_kegiatan.list_kegiatan, name='list-kegiatan'), + + path('lokasi/detail-kegiatan///', + views_kegiatan.detail_kegiatan, name='detail-kegiatan'), + + path('lokasi/add-kegiatan//', + views_kegiatan.add_kegiatan, name='add-kegiatan'), + + path('lokasi/update-kegiatan///', + views_kegiatan.update_kegiatan, name='update-kegiatan'), ] diff --git a/informasi_fasilitas/views_kegiatan.py b/informasi_fasilitas/views_kegiatan.py new file mode 100644 index 0000000..037b43c --- /dev/null +++ b/informasi_fasilitas/views_kegiatan.py @@ -0,0 +1,77 @@ +from http import HTTPStatus +from django.http import JsonResponse +from rest_framework.decorators import api_view, permission_classes, authentication_classes +from rest_framework.authentication import TokenAuthentication +from rest_framework.permissions import IsAuthenticated +from rest_framework.response import Response +from rest_framework.views import APIView +from rest_framework.exceptions import AuthenticationFailed, NotFound +from .models import Kegiatan, Lokasi +from django.contrib.auth.models import User +from .serializers import KegiatanFullSerializer, KegiatanSerializer + + +@api_view(['GET']) +@authentication_classes([]) +@permission_classes([]) +def list_kegiatan(request, place_id): + queryset = Kegiatan.objects.filter(lokasi__place_id=place_id) + serializer = KegiatanFullSerializer(queryset, many=True) + data_response = serializer.data + new_dict = {item['id']: dict(item) for item in data_response} + return JsonResponse(new_dict, status=HTTPStatus.OK) + + +@api_view(['GET']) +@authentication_classes([]) +@permission_classes([]) +def detail_kegiatan(request, place_id, id): + try: + queryset = Kegiatan.objects.get(lokasi__place_id=place_id, id=id) + serializer = KegiatanFullSerializer(queryset, many=False) + return JsonResponse(serializer.data, status=HTTPStatus.OK) + except Kegiatan.DoesNotExist: + raise NotFound(detail="Kegiatan doesn't exist") + + +@api_view(['POST']) +@authentication_classes([TokenAuthentication]) +@permission_classes([IsAuthenticated]) +def add_kegiatan(request, place_id): + try: + lokasi = Lokasi.objects.get(place_id=place_id) + user = request.user + data = KegiatanSerializer(data=request.data) + data.is_valid(raise_exception=True) + kegiatan_params = data.data + kegiatan_params.update({"lokasi": lokasi, "user": user}) + kegiatan = Kegiatan.objects.create(**kegiatan_params) + serializer = KegiatanFullSerializer(kegiatan, many=False) + return JsonResponse(serializer.data, status=HTTPStatus.CREATED) + except Lokasi.DoesNotExist: + raise NotFound(detail="Lokasi doesn't exist") + + +@api_view(['PUT']) +@authentication_classes([TokenAuthentication]) +@permission_classes([IsAuthenticated]) +def update_kegiatan(request, place_id, id): + kegiatan_attr_update = ['nama_kegiatan', 'penyelenggara', + 'deskripsi', 'time_start', 'time_end'] + try: + kegiatan = Kegiatan.objects.get(lokasi__place_id=place_id, id=id) + updated_params = {} + for attr in kegiatan_attr_update: + if attr in request.POST: + updated_params[attr] = request.data[attr] + Kegiatan.objects.filter(lokasi__place_id=place_id, id=id).update(**updated_params) + kegiatan.refresh_from_db() + serializer = KegiatanFullSerializer(kegiatan, many=False) + return JsonResponse(serializer.data, status=HTTPStatus.ACCEPTED) + except Kegiatan.DoesNotExist: + raise NotFound(detail="Kegiatan doesn't exist") + + + + + -- GitLab From 7178a140c31bd423cce0d2d354bcf9bf6521c6cb Mon Sep 17 00:00:00 2001 From: "alvin.hariman" Date: Mon, 10 May 2021 14:58:53 +0700 Subject: [PATCH 61/97] [RED] Add model foto --- new_rest_api/tests.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/new_rest_api/tests.py b/new_rest_api/tests.py index ef33fb2..cc7a52c 100644 --- a/new_rest_api/tests.py +++ b/new_rest_api/tests.py @@ -51,14 +51,14 @@ class UserTests(APITestCase): response = self.client.get(url, format='json') self.assertEqual(response.status_code, status.HTTP_200_OK) json_test = json.loads(response.content) - self.assertEqual(len(json_test), 10) #JSON Attribute + self.assertEqual(len(json_test), 11) #JSON Attribute def test_account_details(self): url = reverse('user-details', kwargs={'email':'astraykai@gmail.com'}) response = self.client.get(url, format='json') self.assertEqual(response.status_code, status.HTTP_200_OK) json_test = json.loads(response.content) - self.assertEqual(len(json_test), 10) #JSON Attribute + self.assertEqual(len(json_test), 11) #JSON Attribute def test_account_list(self): url = reverse('user-list') -- GitLab From c411844c2bc4a49234daa8a047f3c46d91f5f5b4 Mon Sep 17 00:00:00 2001 From: "alvin.hariman" Date: Mon, 10 May 2021 15:02:08 +0700 Subject: [PATCH 62/97] [GREEN] Add model foto --- registrasi/migrations/0005_bisagouser_foto.py | 18 ++++++++++++++++++ registrasi/models.py | 3 ++- registrasi/serializers.py | 8 ++++++-- 3 files changed, 26 insertions(+), 3 deletions(-) create mode 100644 registrasi/migrations/0005_bisagouser_foto.py diff --git a/registrasi/migrations/0005_bisagouser_foto.py b/registrasi/migrations/0005_bisagouser_foto.py new file mode 100644 index 0000000..a67db68 --- /dev/null +++ b/registrasi/migrations/0005_bisagouser_foto.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.2 on 2021-05-10 07:28 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('registrasi', '0004_auto_20201210_1459'), + ] + + operations = [ + migrations.AddField( + model_name='bisagouser', + name='foto', + field=models.ImageField(blank=True, default='-', null=True, upload_to='', verbose_name='Foto'), + ), + ] diff --git a/registrasi/models.py b/registrasi/models.py index 65bd41a..67a54c8 100644 --- a/registrasi/models.py +++ b/registrasi/models.py @@ -13,5 +13,6 @@ class BisaGoUser(models.Model): disabilitas = models.CharField('Disabilitas', max_length=64, null=True, default='Belum memilih') pekerjaan = models.CharField('Pekerjaan', max_length=64, default='-') alamat = models.CharField('Alamat', max_length=255, default='-') + foto = models.ImageField('Foto', blank=True, default='-', null=True) def __str__(self): - return self.user.username + return self.user.username \ No newline at end of file diff --git a/registrasi/serializers.py b/registrasi/serializers.py index bfa70cc..01a62c4 100644 --- a/registrasi/serializers.py +++ b/registrasi/serializers.py @@ -12,5 +12,9 @@ class BisaGoUserSerializers(serializers.ModelSerializer): 'jenis_kelamin', 'disabilitas', 'pekerjaan', - 'alamat' - ) \ No newline at end of file + 'alamat', + 'foto', + ) + extra_kwargs = { + 'foto': {'required': False}, + } \ No newline at end of file -- GitLab From a6eb9bfc1c66ddced02d494a15788654858351b7 Mon Sep 17 00:00:00 2001 From: Christopher Samuel Date: Mon, 10 May 2021 17:58:59 +0700 Subject: [PATCH 63/97] [RED] Update tests + create base models for FotoKegiatan and Komentar Kegiatan, waiting for Kegiatan model --- informasi_fasilitas/serializers.py | 13 ++- informasi_fasilitas/test_views_kegiatan.py | 40 +------- .../test_views_komentar_kegiatan.py | 98 +++++++++++++++++++ informasi_fasilitas/urls.py | 14 ++- .../views_komentar_kegiatan.py | 73 ++++++++++++++ 5 files changed, 201 insertions(+), 37 deletions(-) create mode 100644 informasi_fasilitas/test_views_komentar_kegiatan.py create mode 100644 informasi_fasilitas/views_komentar_kegiatan.py diff --git a/informasi_fasilitas/serializers.py b/informasi_fasilitas/serializers.py index 178dd12..683404b 100644 --- a/informasi_fasilitas/serializers.py +++ b/informasi_fasilitas/serializers.py @@ -1,6 +1,6 @@ from rest_framework import serializers -from .models import Lokasi, Kegiatan +from .models import Lokasi, Kegiatan, KomentarKegiatan class LokasiSerializer(serializers.ModelSerializer): @@ -46,3 +46,14 @@ class KegiatanFullSerializer(serializers.ModelSerializer): 'deskripsi': {'required': True}, 'time_start': {'required': True} } + +class KomentarKegiatanSerializer(serializers.ModelSerializer): + kegiatan = serializers.CharField(source='kegiatan.id', read_only=True) + creator = serializers.CharField(source='user.last_name', read_only=True) + class Meta: + model = KomentarKegiatan + fields = {'id', 'creator', 'kegiatan', 'deskripsi', 'created'} + extra_kwargs = { + 'deskripsi': {'required': True}, + 'created': {'required' : True} + } diff --git a/informasi_fasilitas/test_views_kegiatan.py b/informasi_fasilitas/test_views_kegiatan.py index 3209f7f..2d122c8 100644 --- a/informasi_fasilitas/test_views_kegiatan.py +++ b/informasi_fasilitas/test_views_kegiatan.py @@ -28,16 +28,11 @@ class KegiatanRelatedViewTest(InformasiFasilitasViewTest): reverse('add-kegiatan', kwargs=self.kwargs_place_id) self.update_kegiatan_url = reverse('update-kegiatan', kwargs=self.kwargs_add_or_update_kegiatan) - - # self.add_komentar_kegiatan_url = \ - # reverse('add-komentar-kegiatan', kwargs=self.kwargs_kegiatan_id) - # self.list_komentar_kegiatan_url = \ - # reverse('list-komentar-kegiatan', kwargs=self.kwargs_kegiatan_id) - # - # self.add_foto_kegiatan_url = \ - # reverse('add-foto-kegiatan', kwargs=self.kwargs_kegiatan_id) - # self.list_foto_kegiatan_url = \ - # reverse('list-foto-kegiatan', kwargs=self.kwargs_kegiatan_id) + + self.add_foto_kegiatan_url = \ + reverse('add-foto-kegiatan', kwargs=self.kwargs_kegiatan_id) + self.list_foto_kegiatan_url = \ + reverse('list-foto-kegiatan', kwargs=self.kwargs_kegiatan_id) def test_can_add_kegiatan(self): Kegiatan.objects.all().delete() @@ -119,31 +114,6 @@ class KegiatanRelatedViewTest(InformasiFasilitasViewTest): response = self.client.put(self.update_kegiatan_url, data=send_data) self.assertEqual(response.status_code, HTTPStatus.NOT_FOUND) - def test_can_add_komentar_kegiatan(self): - KomentarKegiatan.objects.all().delete() - response = \ - self.client.post(self.add_komentar_kegiatan_url, self.mock_komentar_kegiatan_test) - self.assertEqual(response.status_code, HTTPStatus.CREATED) - - count = KomentarKegiatan.objects.filter(kegiatan=self.kegiatan).count() - self.assertEqual(count, 1) - - def test_can_get_list_komentar_kegiatan(self): - komentar = self.create_komentar_kegiatan_test(user=self.user) - response = self.client.get(self.list_komentar_kegiatan_url) - self.assertEqual(response.status_code, HTTPStatus.OK) - - response_json = response_decode(response) - expected_json = { - str(komentar.id): { - 'id': komentar.id, - 'user': komentar.user.last_name, - 'kegiatan': komentar.kegiatan.id, - 'deskripsi': komentar.deskripsi, - } - } - self.assertEqual(response_json, expected_json) - def test_can_add_foto_kegiatan(self): FotoKegiatan.objects.all().delete() response = \ diff --git a/informasi_fasilitas/test_views_komentar_kegiatan.py b/informasi_fasilitas/test_views_komentar_kegiatan.py new file mode 100644 index 0000000..bc6de61 --- /dev/null +++ b/informasi_fasilitas/test_views_komentar_kegiatan.py @@ -0,0 +1,98 @@ +import json +from http import HTTPStatus +from django.test import Client +from django.urls import reverse + +from .test_base import InformasiFasilitasViewTest +from .models import Lokasi, Kegiatan, KomentarKegiatan +from pplbackend.utils import response_decode + +class KomentarKegiatanRelatedViewTest(InformasiFasilitasViewTest): + + def setUp(self): + super().setUp() + self.kegiatan = self.create_kegiatan_test(self.user, self.lokasi) + self.kwargs_place_id = {'place_id': self.lokasi.place_id} + self.kwargs_get_or_delete_komentar_kegiatan = { + 'kegiatan_id': self.kegiatan.id, + 'komentar_id': self.kegiatan.id, + } + self.kwargs_kegiatan_id = { + 'place_id': self.lokasi.place_id, + 'kegiatan_id' : self.kegiatan.place_id, + } + + self.add_komentar_kegiatan_url = \ + reverse('add-komentar-kegiatan', kwargs=self.kwargs_kegiatan_id) + self.list_komentar_kegiatan_url = \ + reverse('list-komentar-kegiatan', kwargs=self.kwargs_kegiatan_id) + + def test_can_add_komentar_kegiatan(self): + KomentarKegiatan.objects.all().delete() + response = \ + self.client.post(self.add_komentar_kegiatan_url, self.mock_komentar_kegiatan_test) + self.assertEqual(response.status_code, HTTPStatus.CREATED) + + count = KomentarKegiatan.objects.filter(kegiatan=self.kegiatan).count() + self.assertEqual(count, 1) + + def test_can_get_list_komentar_kegiatan(self): + komentar = self.create_komentar_kegiatan_test(user=self.user) + response = self.client.get(self.list_komentar_kegiatan_url) + self.assertEqual(response.status_code, HTTPStatus.OK) + + response_json = response_decode(response) + expected_id = komentar.id + expected_creator: komentar.user.last_name + expected_kegiatan: komentar.kegiatan.id + expected_deskripsi: komentar.deskripsi + + self.assertEqual(response_json['id']['id'], expected_id) + self.assertEqual(response_json['id']['creator'], expected_creator) + self.assertEqual(response_json['id']['kegiatan'], expected_kegiatan) + self.assertEqual(response_json['id']['deskripsi'], expected_deskripsi) + self.assertEqual(True, ('created' in response_json['id'].keys())) + + def test_can_get_komentar_kegiatan(self): + komentar = self.create_komentar_kegiatan_test(user=self.user) + kwargs_get_komentar_kegiatan = { + 'place_id': self.lokasi.place_id, + 'kegiatan_id': self.kegiatan.id, + 'komentar_id': komentar.id, + } + get_komentar_kegiatan_url = \ + reverse('get-komentar-kegiatan', kwargs=kwargs_get_komentar_kegiatan) + response = self.client.get(get_komentar_kegiatan_url) + self.assertEqual(response.status_code, HTTPStatus.OK) + + response_json = response_decode(response) + expected_id = komentar.id + expected_creator: komentar.user.last_name + expected_kegiatan: komentar.kegiatan.id + expected_deskripsi: komentar.deskripsi + + self.assertEqual(response_json['id']['id'], expected_id) + self.assertEqual(response_json['id']['creator'], expected_creator) + self.assertEqual(response_json['id']['kegiatan'], expected_kegiatan) + self.assertEqual(response_json['id']['deskripsi'], expected_deskripsi) + self.assertEqual(True, ('created' in response_json['id'].keys())) + + def test_can_delete_komentar_kegiatan(self): + KomentarKegiatan.objects.all().delete() + komentar = self.create_komentar_kegiatan_test(user=self.user) + + kwargs_delete_komentar_kegiatan = { + 'place_id': self.lokasi.place_id, + 'kegiatan_id': self.kegiatan.id, + 'komentar_id': komentar.id, + } + + delete_komentar_kegiatan_url = \ + reverse('delete-komentar-kegiatan', kwargs=kwargs_delete_komentar_kegiatan) + + response = self.client.delete(delete_komentar_kegiatan_url) + self.assertEqual(response.status_code, HTTPStatus.OK) + + count = KomentarKegiatan.objects.filter(kegiatan=self.kegiatan).count() + self.assertEqual(count, 0) + diff --git a/informasi_fasilitas/urls.py b/informasi_fasilitas/urls.py index 2308642..41ef7e3 100644 --- a/informasi_fasilitas/urls.py +++ b/informasi_fasilitas/urls.py @@ -1,5 +1,5 @@ from django.urls import path -from . import views, views_kegiatan +from . import views, views_kegiatan, views_komentar_kegiatan list_lokasi_views = views.LokasiListCreateView.as_view({'get':'list'}) add_lokasi_views = views.LokasiListCreateView.as_view({'post':'create'}) @@ -41,4 +41,16 @@ urlpatterns = [ path('lokasi/update-kegiatan///', views_kegiatan.update_kegiatan, name='update-kegiatan'), + + path('lokasi/get-komentar-kegiatan///', + views_komentar_kegiatan.get_komentar_kegiatan, name='get-komentar-kegiatan'), + + path('lokasi/list-komentar-kegiatan//', + views_komentar_kegiatan.list_komentar_kegiatan, name='list-komentar-kegiatan'), + + path('lokasi/add-komentar-kegiatan//', + views_komentar_kegiatan.add_komentar_kegiatan, name='add-komentar-kegiatan'), + + path('lokasi/delete-komentar-kegiatan///', + views_komentar_kegiatan.delete_komentar_kegiatan, name='delete-komentar-kegiatan'), ] diff --git a/informasi_fasilitas/views_komentar_kegiatan.py b/informasi_fasilitas/views_komentar_kegiatan.py new file mode 100644 index 0000000..c8e2f5d --- /dev/null +++ b/informasi_fasilitas/views_komentar_kegiatan.py @@ -0,0 +1,73 @@ +from http import HTTPStatus +from django.http import JsonResponse +from django.views.decorators.csrf import csrf_exempt +from django.contrib.auth.models import User +from django.core.exceptions import ObjectDoesNotExist +from django.db.models import F + +from rest_framework.decorators import api_view, permission_classes, authentication_classes +from rest_framework.authentication import TokenAuthentication +from rest_framework.permissions import IsAuthenticated +from rest_framework.response import Response +from rest_framework.parsers import FileUploadParser +from rest_framework.views import APIView +from rest_framework.generics import ListCreateAPIView +from rest_framework import viewsets + +from .serializers import KomentarKegiatanSerializer +from .models import Lokasi, Kegiatan, KomentarKegiatan +from .permissions import UserPermission + +@api_view(['GET']) +@authentication_classes([]) +@permission_classes([]) +def list_komentar_kegiatan(request, place_id, kegiatan_id): + try: + queryset = KomentarKegiatan.objects.filter(kegiatan__lokasi__place_id=place_id, kegiatan__id=kegiatan_id) + serializer = KomentarKegiatanSerializer(queryset, many=True) + data_response = serializer.data + new_dict = {item['id']: dict(item) for item in data_response} + return JsonResponse(new_dict, status=HTTPStatus.OK) + except Exception as error: + return JsonResponse({'response': str(error)}, status=HTTPStatus.NOT_FOUND) + +@api_view(['GET']) +@authentication_classes([]) +@permission_classes([]) +def get_komentar_kegiatan(request, place_id, kegiatan_id, komentar_id): + try: + queryset = KomentarKegiatan.objects.filter(kegiatan__lokasi__place_id=place_id, kegiatan__id=kegiatan_id) + serializer = KomentarKegiatanSerializer(queryset, many=False) + return JsonResponse(serializer.data, status=HTTPStatus.OK) + except ObjectDoesNotExist: + return JsonResponse({'response': 'Komentar tidak ditemukan'}, status=HTTPStatus.NOT_FOUND) + +@api_view(['POST']) +@authentication_classes([TokenAuthentication]) +@permission_classes([IsAuthenticated]) +def add_komentar_kegiatan(request, place_id, kegiatan_id): + try: + kegiatan = Kegiatan.objects.get(lokasi__place_id=place_id, id=kegiatan_id) + deskripsi = request.POST['deskripsi'] + komentar = KomentarKegiatan.objects \ + .create( + kegiatan=kegiatan, + user=request.user, + deskripsi=deskripsi + ) + return JsonResponse({'response': 'komentar kegiatan added', 'id': komentar.id}, + status=HTTPStatus.CREATED) + except Exception as error: + return JsonResponse({'response': str(error)}, status=HTTPStatus.NOT_FOUND) + +@api_view(['DELETE']) +@authentication_classes([TokenAuthentication]) +@permission_classes([IsAuthenticated]) +def delete_komentar_kegiatan(request, place_id, kegiatan_id, komentar_id): + try: + komentar = KomentarKegiatan.objects.get(kegiatan__lokasi__place_id=place_id, kegiatan__id=kegiatan_id, id=komentar_id) + komentar.delete() + return JsonResponse({'response': 'komentar kegiatan deleted', 'id': komentar_id}, + status=HTTPStatus.OK) + except Exception as error: + return JsonResponse({'response': str(error)}, status=HTTPStatus.NOT_FOUND) -- GitLab From ea4bb208cb24018b2a7e0ec650a8a50bd447e11b Mon Sep 17 00:00:00 2001 From: Muhammad Rafif Elfazri Date: Mon, 10 May 2021 18:48:36 +0700 Subject: [PATCH 64/97] [CHORES] add narahubung field in Kegiatan --- .../migrations/0014_kegiatan_narahubung.py | 18 ++++++++++++++++++ informasi_fasilitas/models.py | 1 + 2 files changed, 19 insertions(+) create mode 100644 informasi_fasilitas/migrations/0014_kegiatan_narahubung.py diff --git a/informasi_fasilitas/migrations/0014_kegiatan_narahubung.py b/informasi_fasilitas/migrations/0014_kegiatan_narahubung.py new file mode 100644 index 0000000..d94fea3 --- /dev/null +++ b/informasi_fasilitas/migrations/0014_kegiatan_narahubung.py @@ -0,0 +1,18 @@ +# Generated by Django 3.1.7 on 2021-05-10 11:47 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('informasi_fasilitas', '0013_fotokegiatan_kegiatan_komentarkegiatan'), + ] + + operations = [ + migrations.AddField( + model_name='kegiatan', + name='narahubung', + field=models.TextField(null=True), + ), + ] diff --git a/informasi_fasilitas/models.py b/informasi_fasilitas/models.py index 21a166d..66e123f 100644 --- a/informasi_fasilitas/models.py +++ b/informasi_fasilitas/models.py @@ -80,6 +80,7 @@ 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) deskripsi = models.TextField(null=False) time_start = models.DateTimeField(blank=False, null=False, default=timezone.now) time_end = models.DateTimeField(blank=True, null=True) -- GitLab From e69774c11766b278af42fbaf9c1bc025f551232b Mon Sep 17 00:00:00 2001 From: "alvin.hariman" Date: Mon, 10 May 2021 21:51:21 +0700 Subject: [PATCH 65/97] [RED] Add test for update profile picture --- new_rest_api/tests.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/new_rest_api/tests.py b/new_rest_api/tests.py index cc7a52c..1d715b4 100644 --- a/new_rest_api/tests.py +++ b/new_rest_api/tests.py @@ -7,6 +7,8 @@ from django.utils.http import urlsafe_base64_encode from django.utils.encoding import force_bytes from registrasi.models import BisaGoUser from .tokens import account_activation_token +import io +import requests class UserTests(APITestCase): urlpatterns = [ @@ -185,7 +187,24 @@ class UserTests(APITestCase): json_response = json.loads(response.content) self.assertEqual(json_response['response'], 'Internal server error') - + def test_upload_foto_with_invalid_image(self): + ''' + Ensure that profile picture will not be updated if invalid image is sent + ''' + + url = reverse('update-user') + data = {'name': 'Astraykai', + 'email':'astraykai@gmail.com', + 'phone_number':'089892218567', + 'tanggal_lahir':'1990-05-05', + 'jenis_kelamin':'Laki-laki', + 'disabilitas':'', + 'pekerjaan':'Mahasiswa', + 'alamat':'Alamat Palsu', + 'foto':(io.BytesIO(b"this is a test"), 'test.pdf')} + response = self.client.post(url, data, headers={'Content-Type': "multipart/form-data"}) + json_response = json.loads(response.content) + self.assertEqual(json_response['response'], 'File is not image type') class InfoTests(APITestCase, URLPatternsTestCase): pass -- GitLab From 6ea3372f2eecb98e58f4a4f425f0bcbe5da9e406 Mon Sep 17 00:00:00 2001 From: "alvin.hariman" Date: Mon, 10 May 2021 22:15:53 +0700 Subject: [PATCH 66/97] [GREEN] Add profile update for profile picture --- new_rest_api/views.py | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/new_rest_api/views.py b/new_rest_api/views.py index 3a19e78..e7a6263 100644 --- a/new_rest_api/views.py +++ b/new_rest_api/views.py @@ -18,6 +18,10 @@ from registrasi.models import BisaGoUser from registrasi.serializers import BisaGoUserSerializers from .tokens import account_activation_token +from django.core.files.storage import default_storage +from django.core.files.base import ContentFile +from django.conf import settings + def request_error_message(request_kind): return "get {} request instead".format(request_kind) @@ -68,6 +72,8 @@ def user_details(request, email): @authentication_classes([]) @permission_classes([]) def register_user(request): + print(request.POST) + print(request.FILES) try: if request.method == 'POST': name = request.POST['name'] @@ -80,6 +86,10 @@ def register_user(request): user.is_active = False user.save() data['user'] = user.pk + if request.FILES : + data['foto'] = request.FILES['foto'] + else : + data['foto'] = None mail_subject = "Activate your account" message = render_to_string('acc_active_email.html', { 'user' : user, @@ -131,22 +141,45 @@ def activate(request, uidb64, token): @authentication_classes([]) @permission_classes([]) def update_user(request): + with default_storage.open('tmp/test.png', 'wb+') as destination: + for chunk in request.FILES['foto'].chunks(): + destination.write(chunk) + print('-----------------') + print(request.POST) + print('-----------------') + print(request.FILES) + print('-----------------') try: if request.method == 'POST': name = request.POST['name'] email = request.POST['email'] data = dict(list(request.POST.dict().items())) + if request.FILES : + type = request.FILES['foto'].content_type.split("/")[0] + if type == "image" : + data['foto'] = request.FILES['foto'] + else : + return JsonResponse({'response': 'File is not image type'}, safe=False, + status=status.INTERNAL_SERVER_ERROR) user = User.objects.get(username=email) bisa_go_user = BisaGoUser.objects.get(user=user) user.last_name = name data['user'] = user.pk data_query_dict = QueryDict('', mutable=True) + print(1) data_query_dict.update(data) + print(2) serializer = BisaGoUserSerializers(bisa_go_user, data=data_query_dict) + print(3) if serializer.is_valid(): + print(4) user.save() + print(5) serializer.save() - else: + print(6) + else: + print(serializer.errors) + print(7) return JsonResponse({'response': 'Internal server error'}, safe=False, status=status.INTERNAL_SERVER_ERROR) return JsonResponse({'response': 'User updated'}, safe=False, status=status.OK) -- GitLab From dd982b3cf7c7228f53793584214624ea0f64d632 Mon Sep 17 00:00:00 2001 From: "alvin.hariman" Date: Mon, 10 May 2021 22:26:18 +0700 Subject: [PATCH 67/97] [CHORE] Delete debug code --- new_rest_api/views.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/new_rest_api/views.py b/new_rest_api/views.py index e7a6263..0a54cc7 100644 --- a/new_rest_api/views.py +++ b/new_rest_api/views.py @@ -141,14 +141,6 @@ def activate(request, uidb64, token): @authentication_classes([]) @permission_classes([]) def update_user(request): - with default_storage.open('tmp/test.png', 'wb+') as destination: - for chunk in request.FILES['foto'].chunks(): - destination.write(chunk) - print('-----------------') - print(request.POST) - print('-----------------') - print(request.FILES) - print('-----------------') try: if request.method == 'POST': name = request.POST['name'] -- GitLab From aa590c0e0f09af7ef8bca57bee5e8850ea9ab4f3 Mon Sep 17 00:00:00 2001 From: Muhammad Rafif Elfazri Date: Mon, 10 May 2021 22:33:27 +0700 Subject: [PATCH 68/97] [CHORES] add related name to reference to kegiatan in FotoKegiatan --- informasi_fasilitas/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/informasi_fasilitas/models.py b/informasi_fasilitas/models.py index 66e123f..0e5172e 100644 --- a/informasi_fasilitas/models.py +++ b/informasi_fasilitas/models.py @@ -114,5 +114,5 @@ class KomentarKegiatan(models.Model): class FotoKegiatan(models.Model): objects = models.Manager() - kegiatan = models.ForeignKey(Kegiatan, on_delete=models.CASCADE) + kegiatan = models.ForeignKey(Kegiatan, related_name="images", on_delete=models.CASCADE) foto = models.ImageField() -- GitLab From ca89acb438c7cc9ce5455c13b29d0d7894939812 Mon Sep 17 00:00:00 2001 From: Muhammad Rafif Elfazri Date: Mon, 10 May 2021 22:34:31 +0700 Subject: [PATCH 69/97] [CHORES] reduce redundant serializer --- informasi_fasilitas/serializers.py | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/informasi_fasilitas/serializers.py b/informasi_fasilitas/serializers.py index 683404b..4f53484 100644 --- a/informasi_fasilitas/serializers.py +++ b/informasi_fasilitas/serializers.py @@ -18,35 +18,23 @@ class LokasiSerializer(serializers.ModelSerializer): class KegiatanSerializer(serializers.ModelSerializer): - class Meta: - model = Kegiatan - fields = ('nama_kegiatan', 'penyelenggara', 'deskripsi', - 'time_start', 'time_end') - extra_kwargs = { - 'nama_kegiatan': {'required': True}, - 'penyelenggara': {'required': True}, - 'nama_kegiatan': {'required': True}, - 'deskripsi': {'required': True}, - 'time_start': {'required': True} - } - - -class KegiatanFullSerializer(serializers.ModelSerializer): place_id = serializers.CharField(source='lokasi.place_id', read_only=True) creator = serializers.CharField(source='user.last_name', read_only=True) + class Meta: model = Kegiatan - fields = ('id', 'place_id', 'creator', + fields = ('id', 'place_id', 'creator', 'lokasi', 'user', 'nama_kegiatan', 'penyelenggara', 'deskripsi', - 'time_start', 'time_end') + "narahubung", 'time_start', 'time_end') extra_kwargs = { 'nama_kegiatan': {'required': True}, 'penyelenggara': {'required': True}, - 'nama_kegiatan': {'required': True}, 'deskripsi': {'required': True}, - 'time_start': {'required': True} + 'narahubung': {"required": True}, + 'time_start': {'required': True}, } + class KomentarKegiatanSerializer(serializers.ModelSerializer): kegiatan = serializers.CharField(source='kegiatan.id', read_only=True) creator = serializers.CharField(source='user.last_name', read_only=True) -- GitLab From 5cc66cbd567377642406934b5ec5591c7f09f75a Mon Sep 17 00:00:00 2001 From: Muhammad Rafif Elfazri Date: Mon, 10 May 2021 22:36:13 +0700 Subject: [PATCH 70/97] [RED] Change test for adding narahubung field in Kegiatan --- informasi_fasilitas/test_base.py | 3 +- informasi_fasilitas/test_views_kegiatan.py | 49 ++++++++++++---------- 2 files changed, 28 insertions(+), 24 deletions(-) diff --git a/informasi_fasilitas/test_base.py b/informasi_fasilitas/test_base.py index 6ef56a4..c9ec351 100644 --- a/informasi_fasilitas/test_base.py +++ b/informasi_fasilitas/test_base.py @@ -54,7 +54,8 @@ class InformasiFasilitasTest(TestCase): 'penyelenggara': 'mock penyelenggara', 'time_start': datetime.now().strftime("%Y-%m-%d %H:%M"), 'time_end': datetime.now().strftime("%Y-%m-%d %H:%M"), - 'deskripsi': 'sebuah deskripsi' + 'narahubung': 'sebuah narahubung', + 'deskripsi': 'sebuah deskripsi', } mock_komentar_kegiatan_test = { diff --git a/informasi_fasilitas/test_views_kegiatan.py b/informasi_fasilitas/test_views_kegiatan.py index 2d122c8..1ed98b9 100644 --- a/informasi_fasilitas/test_views_kegiatan.py +++ b/informasi_fasilitas/test_views_kegiatan.py @@ -28,14 +28,15 @@ class KegiatanRelatedViewTest(InformasiFasilitasViewTest): reverse('add-kegiatan', kwargs=self.kwargs_place_id) self.update_kegiatan_url = reverse('update-kegiatan', kwargs=self.kwargs_add_or_update_kegiatan) - - self.add_foto_kegiatan_url = \ - reverse('add-foto-kegiatan', kwargs=self.kwargs_kegiatan_id) - self.list_foto_kegiatan_url = \ - reverse('list-foto-kegiatan', kwargs=self.kwargs_kegiatan_id) + + # self.add_foto_kegiatan_url = \ + # reverse('add-foto-kegiatan', kwargs=self.kwargs_kegiatan_id) + # self.list_foto_kegiatan_url = \ + # reverse('list-foto-kegiatan', kwargs=self.kwargs_kegiatan_id) def test_can_add_kegiatan(self): Kegiatan.objects.all().delete() + print(Lokasi.objects.get(place_id=self.lokasi.place_id)) response = \ self.client.post(self.add_kegiatan_url, self.mock_kegiatan_test) data = response.json() @@ -79,6 +80,7 @@ class KegiatanRelatedViewTest(InformasiFasilitasViewTest): 'nama_kegiatan' : self.kegiatan.nama_kegiatan, 'penyelenggara': self.kegiatan.penyelenggara, 'deskripsi': self.kegiatan.deskripsi, + 'narahubung': self.kegiatan.narahubung, 'time_start': self.kegiatan.time_start, 'time_end': self.kegiatan.time_end, }, @@ -110,24 +112,25 @@ class KegiatanRelatedViewTest(InformasiFasilitasViewTest): send_data = { 'deskripsi': 'deskripsi kegiatan baru untuk test', 'nama_kegiatan': 'nama kegiatan baru untuk test', + 'narahubung': 'narahubung baru' } - response = self.client.put(self.update_kegiatan_url, data=send_data) + response = self.client.put(url, data=send_data) self.assertEqual(response.status_code, HTTPStatus.NOT_FOUND) - def test_can_add_foto_kegiatan(self): - FotoKegiatan.objects.all().delete() - response = \ - self.client.post(self.add_foto_kegiatan_url, self.mock_foto_kegiatan_test) - self.assertEqual(response.status_code, HTTPStatus.CREATED) - - count = FotoKegiatan.objects.filter(kegiatan=self.kegiatan).count() - self.assertEqual(count, 1) - - def test_can_get_list_foto_kegiatan(self): - foto = self.create_foto_kegiatan_test() - response = self.client.get(self.list_foto_kegiatan_url) - self.assertEqual(response.status_code, HTTPStatus.OK) - - response_json = response_decode(response) - self.assertEqual(response_json['id'], foto.id) - self.assertIsNotNone(response_json['foto']) + # def test_can_add_foto_kegiatan(self): + # FotoKegiatan.objects.all().delete() + # response = \ + # self.client.post(self.add_foto_kegiatan_url, self.mock_foto_kegiatan_test) + # self.assertEqual(response.status_code, HTTPStatus.CREATED) + # + # count = FotoKegiatan.objects.filter(kegiatan=self.kegiatan).count() + # self.assertEqual(count, 1) + # + # def test_can_get_list_foto_kegiatan(self): + # foto = self.create_foto_kegiatan_test() + # response = self.client.get(self.list_foto_kegiatan_url) + # self.assertEqual(response.status_code, HTTPStatus.OK) + # + # response_json = response_decode(response) + # self.assertEqual(response_json['id'], foto.id) + # self.assertIsNotNone(response_json['foto']) -- GitLab From 96970a20589e3dfb4f351b44c96262c759db28ab Mon Sep 17 00:00:00 2001 From: Muhammad Rafif Elfazri Date: Mon, 10 May 2021 22:37:00 +0700 Subject: [PATCH 71/97] [GREEN] Implement changes on adding field in kegiatan and refactor --- informasi_fasilitas/views_kegiatan.py | 40 +++++++++++++-------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/informasi_fasilitas/views_kegiatan.py b/informasi_fasilitas/views_kegiatan.py index 037b43c..4664dd3 100644 --- a/informasi_fasilitas/views_kegiatan.py +++ b/informasi_fasilitas/views_kegiatan.py @@ -8,17 +8,22 @@ from rest_framework.views import APIView from rest_framework.exceptions import AuthenticationFailed, NotFound from .models import Kegiatan, Lokasi from django.contrib.auth.models import User -from .serializers import KegiatanFullSerializer, KegiatanSerializer +from .serializers import KegiatanSerializer +def clean_json_kegiatan(kegiatan): + kegiatan.pop("user") + kegiatan.pop("lokasi") + return kegiatan + @api_view(['GET']) @authentication_classes([]) @permission_classes([]) def list_kegiatan(request, place_id): queryset = Kegiatan.objects.filter(lokasi__place_id=place_id) - serializer = KegiatanFullSerializer(queryset, many=True) + serializer = KegiatanSerializer(queryset, many=True) data_response = serializer.data - new_dict = {item['id']: dict(item) for item in data_response} + new_dict = {item['id']: clean_json_kegiatan(dict(item)) for item in data_response} return JsonResponse(new_dict, status=HTTPStatus.OK) @@ -28,8 +33,8 @@ 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 = KegiatanFullSerializer(queryset, many=False) - return JsonResponse(serializer.data, status=HTTPStatus.OK) + serializer = KegiatanSerializer(queryset, many=False) + return JsonResponse(clean_json_kegiatan(serializer.data), status=HTTPStatus.OK) except Kegiatan.DoesNotExist: raise NotFound(detail="Kegiatan doesn't exist") @@ -41,13 +46,12 @@ def add_kegiatan(request, place_id): try: lokasi = Lokasi.objects.get(place_id=place_id) user = request.user - data = KegiatanSerializer(data=request.data) + data = request.data.dict() + data.update({"user": user.id, "lokasi": lokasi.id}) + data = KegiatanSerializer(data=data) data.is_valid(raise_exception=True) - kegiatan_params = data.data - kegiatan_params.update({"lokasi": lokasi, "user": user}) - kegiatan = Kegiatan.objects.create(**kegiatan_params) - serializer = KegiatanFullSerializer(kegiatan, many=False) - return JsonResponse(serializer.data, status=HTTPStatus.CREATED) + data.save() + return JsonResponse(clean_json_kegiatan(data.data), status=HTTPStatus.CREATED) except Lokasi.DoesNotExist: raise NotFound(detail="Lokasi doesn't exist") @@ -56,18 +60,12 @@ def add_kegiatan(request, place_id): @authentication_classes([TokenAuthentication]) @permission_classes([IsAuthenticated]) def update_kegiatan(request, place_id, id): - kegiatan_attr_update = ['nama_kegiatan', 'penyelenggara', - 'deskripsi', 'time_start', 'time_end'] try: kegiatan = Kegiatan.objects.get(lokasi__place_id=place_id, id=id) - updated_params = {} - for attr in kegiatan_attr_update: - if attr in request.POST: - updated_params[attr] = request.data[attr] - Kegiatan.objects.filter(lokasi__place_id=place_id, id=id).update(**updated_params) - kegiatan.refresh_from_db() - serializer = KegiatanFullSerializer(kegiatan, many=False) - return JsonResponse(serializer.data, status=HTTPStatus.ACCEPTED) + serializer = KegiatanSerializer(kegiatan, data=request.data, partial=True) + serializer.is_valid(raise_exception=True) + serializer.save() + return JsonResponse(clean_json_kegiatan(serializer.data), status=HTTPStatus.ACCEPTED) except Kegiatan.DoesNotExist: raise NotFound(detail="Kegiatan doesn't exist") -- GitLab From e9e6cc1faa7e99295b02f23c07f0ebee886cacf6 Mon Sep 17 00:00:00 2001 From: Muhammad Rafif Elfazri Date: Tue, 11 May 2021 09:51:01 +0700 Subject: [PATCH 72/97] Add related name to FotoKegiatan and Kegiatan --- .../migrations/0015_auto_20210511_0249.py | 24 +++++++++++++++++++ informasi_fasilitas/models.py | 2 +- 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 informasi_fasilitas/migrations/0015_auto_20210511_0249.py diff --git a/informasi_fasilitas/migrations/0015_auto_20210511_0249.py b/informasi_fasilitas/migrations/0015_auto_20210511_0249.py new file mode 100644 index 0000000..7aca39c --- /dev/null +++ b/informasi_fasilitas/migrations/0015_auto_20210511_0249.py @@ -0,0 +1,24 @@ +# Generated by Django 3.1.7 on 2021-05-11 02:49 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('informasi_fasilitas', '0014_kegiatan_narahubung'), + ] + + operations = [ + migrations.AlterField( + model_name='fotokegiatan', + name='foto', + field=models.ImageField(default=None, null=True, upload_to='kegiatan/'), + ), + migrations.AlterField( + model_name='fotokegiatan', + name='kegiatan', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='images', to='informasi_fasilitas.kegiatan'), + ), + ] diff --git a/informasi_fasilitas/models.py b/informasi_fasilitas/models.py index 0e5172e..2f2178b 100644 --- a/informasi_fasilitas/models.py +++ b/informasi_fasilitas/models.py @@ -115,4 +115,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() + foto = models.ImageField(upload_to="kegiatan/", null=True, default=None) -- GitLab From 29cb28d93980855aaa44627744e3e3aafdaaba61 Mon Sep 17 00:00:00 2001 From: Muhammad Rafif Elfazri Date: Tue, 11 May 2021 09:51:44 +0700 Subject: [PATCH 73/97] [CHORES] add serializer for FotoKegiatan --- informasi_fasilitas/serializers.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/informasi_fasilitas/serializers.py b/informasi_fasilitas/serializers.py index 4f53484..60ae60d 100644 --- a/informasi_fasilitas/serializers.py +++ b/informasi_fasilitas/serializers.py @@ -1,6 +1,6 @@ from rest_framework import serializers -from .models import Lokasi, Kegiatan, KomentarKegiatan +from .models import Lokasi, Kegiatan, KomentarKegiatan, FotoKegiatan class LokasiSerializer(serializers.ModelSerializer): @@ -17,6 +17,17 @@ class LokasiSerializer(serializers.ModelSerializer): } +class FotoKegiatanSerializer(serializers.ModelSerializer): + place_id = serializers.CharField(source='lokasi.place_id', read_only=True) + class Meta: + model = FotoKegiatan + fields = '__all__' + extra_kwargs = { + 'foto': {'required': True}, + 'kegiatan': {'required': True}, + } + + class KegiatanSerializer(serializers.ModelSerializer): place_id = serializers.CharField(source='lokasi.place_id', read_only=True) creator = serializers.CharField(source='user.last_name', read_only=True) -- GitLab From 67ac5ca117b36dd58c8c704aacbf496e4fc58c28 Mon Sep 17 00:00:00 2001 From: Muhammad Rafif Elfazri Date: Tue, 11 May 2021 09:52:39 +0700 Subject: [PATCH 74/97] [RED] test for adding image in create/update kegiatan --- informasi_fasilitas/test_views_kegiatan.py | 48 ++++++++++++++++++---- 1 file changed, 40 insertions(+), 8 deletions(-) diff --git a/informasi_fasilitas/test_views_kegiatan.py b/informasi_fasilitas/test_views_kegiatan.py index 1ed98b9..ac29995 100644 --- a/informasi_fasilitas/test_views_kegiatan.py +++ b/informasi_fasilitas/test_views_kegiatan.py @@ -1,17 +1,21 @@ import json +import shutil +import os from http import HTTPStatus from django.test import Client from django.urls import reverse - +from django.test import override_settings +from django.core.files.uploadedfile import SimpleUploadedFile +from django.conf import settings from .test_base import InformasiFasilitasViewTest -from .models import Lokasi, Kegiatan, FotoKegiatan, KomentarKegiatan +from .models import Lokasi, Kegiatan, FotoKegiatan from pplbackend.utils import response_decode class KegiatanRelatedViewTest(InformasiFasilitasViewTest): def setUp(self): super().setUp() - + self.media_root = os.path.join(settings.BASE_DIR, 'test_file/media') self.kegiatan = self.create_kegiatan_test(self.user, self.lokasi) self.kwargs_place_id = {'place_id': self.lokasi.place_id} self.kwargs_add_or_update_kegiatan = { @@ -20,6 +24,14 @@ class KegiatanRelatedViewTest(InformasiFasilitasViewTest): } self.kwargs_kegiatan_id = {'kegiatan_id' : self.kegiatan.lokasi.place_id} + image_path1, image_path2 = ("test_file/test1.jpg", "test_file/test2.jpg") + image1 = SimpleUploadedFile("test1.jpg", content=open(image_path1, 'rb').read(), + content_type='image/jpeg') + image2 = SimpleUploadedFile("test2.jpg", content=open(image_path2, 'rb').read(), + content_type='image/jpeg') + + self.kegiatan_images = {'images': [image1, image2]} + self.get_list_kegiatan_url = \ reverse('list-kegiatan', kwargs=self.kwargs_place_id) self.get_detail_kegiatan_url = \ @@ -33,12 +45,20 @@ class KegiatanRelatedViewTest(InformasiFasilitasViewTest): # reverse('add-foto-kegiatan', kwargs=self.kwargs_kegiatan_id) # self.list_foto_kegiatan_url = \ # reverse('list-foto-kegiatan', kwargs=self.kwargs_kegiatan_id) - + + def tearDown(self): + try: + shutil.rmtree(self.media_root) + except OSError: + pass + + @override_settings(MEDIA_ROOT=("test_file" + '/media')) def test_can_add_kegiatan(self): Kegiatan.objects.all().delete() - print(Lokasi.objects.get(place_id=self.lokasi.place_id)) + response_params = self.mock_kegiatan_test.copy() + response_params.update(self.kegiatan_images) response = \ - self.client.post(self.add_kegiatan_url, self.mock_kegiatan_test) + self.client.post(self.add_kegiatan_url, response_params) data = response.json() data.pop("id", None) expected_json = self.mock_kegiatan_test.copy() @@ -47,9 +67,10 @@ class KegiatanRelatedViewTest(InformasiFasilitasViewTest): }) self.assertEqual(response.status_code, HTTPStatus.CREATED) self.assertDictEqual(data, expected_json) - count = Kegiatan.objects.all().count() + count_foto = FotoKegiatan.objects.all().count() self.assertEqual(count, 1) + self.assertEqual(count_foto, 2) def test_add_kegiatan_with_missing_params(self): Kegiatan.objects.all().delete() @@ -86,12 +107,16 @@ class KegiatanRelatedViewTest(InformasiFasilitasViewTest): }, } self.assertEqual(content, expected_json) - + + + + @override_settings(MEDIA_ROOT=("test_file" + '/media')) def test_can_put_update_kegiatan(self): send_data = { 'deskripsi': 'deskripsi kegiatan baru untuk test', 'nama_kegiatan': 'nama kegiatan baru untuk test', } + send_data.update(self.kegiatan_images) response = self.client.put(self.update_kegiatan_url, data=send_data) data = response.json() data.pop("id", None) @@ -99,10 +124,17 @@ class KegiatanRelatedViewTest(InformasiFasilitasViewTest): expected_json.update({'creator': 'mock last_name', 'place_id': 'mock_place_id' }) + send_data.pop("images") expected_json.update(send_data) self.assertEqual(response.status_code, HTTPStatus.ACCEPTED) self.assertDictEqual(data, expected_json) + count = Kegiatan.objects.all().count() + count_foto = FotoKegiatan.objects.all().count() + self.assertEqual(count, 1) + self.assertEqual(count_foto, 2) + + def test_update_kegiatan_lokasi_not_exist(self): Kegiatan.objects.all().delete() url = reverse('update-kegiatan', kwargs={ -- GitLab From 55153a780fda4e79edd2d5c8dc54667a78a030c2 Mon Sep 17 00:00:00 2001 From: Muhammad Rafif Elfazri Date: Tue, 11 May 2021 09:53:45 +0700 Subject: [PATCH 75/97] [GREEN] Implement saving images when update/create Kegiatan --- informasi_fasilitas/views_kegiatan.py | 37 ++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/informasi_fasilitas/views_kegiatan.py b/informasi_fasilitas/views_kegiatan.py index 4664dd3..b761e12 100644 --- a/informasi_fasilitas/views_kegiatan.py +++ b/informasi_fasilitas/views_kegiatan.py @@ -6,7 +6,7 @@ from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response from rest_framework.views import APIView from rest_framework.exceptions import AuthenticationFailed, NotFound -from .models import Kegiatan, Lokasi +from .models import Kegiatan, Lokasi, FotoKegiatan from django.contrib.auth.models import User from .serializers import KegiatanSerializer @@ -48,10 +48,14 @@ def add_kegiatan(request, place_id): user = request.user data = request.data.dict() data.update({"user": user.id, "lokasi": lokasi.id}) - data = KegiatanSerializer(data=data) - data.is_valid(raise_exception=True) - data.save() - return JsonResponse(clean_json_kegiatan(data.data), status=HTTPStatus.CREATED) + data.pop("images", []) + + serializer = KegiatanSerializer(data=data) + serializer.is_valid(raise_exception=True) + kegiatan = serializer.save() + serializer = add_foto_kegiatan(kegiatan, request.data.getlist("images")) + + return JsonResponse(clean_json_kegiatan(serializer.data), status=HTTPStatus.CREATED) except Lokasi.DoesNotExist: raise NotFound(detail="Lokasi doesn't exist") @@ -62,14 +66,33 @@ def add_kegiatan(request, place_id): def update_kegiatan(request, place_id, id): try: kegiatan = Kegiatan.objects.get(lokasi__place_id=place_id, id=id) - serializer = KegiatanSerializer(kegiatan, data=request.data, partial=True) + data = request.data.dict() + data.pop("images", []) + serializer = KegiatanSerializer(kegiatan, data=data, partial=True) serializer.is_valid(raise_exception=True) - serializer.save() + kegiatan = serializer.save() + serializer = add_foto_kegiatan(kegiatan, request.data.getlist("images")) + return JsonResponse(clean_json_kegiatan(serializer.data), status=HTTPStatus.ACCEPTED) except Kegiatan.DoesNotExist: raise NotFound(detail="Kegiatan doesn't exist") +def add_foto_kegiatan(kegiatan, list_image): + _create_list_kegiatan_foto(kegiatan, list_image) + kegiatan.refresh_from_db() + return KegiatanSerializer(kegiatan) + + +def _create_list_kegiatan_foto(kegiatan, list_image): + list_kegiatan_foto = [] + for image in list_image: + foto = FotoKegiatan.objects.create(kegiatan=kegiatan, foto=image) + list_kegiatan_foto.append(foto) + return list_kegiatan_foto + + + -- GitLab From 680673b1c89d3c1e7f6315c80eb75988bac0591a Mon Sep 17 00:00:00 2001 From: Muhammad Rafif Elfazri Date: Tue, 11 May 2021 09:54:26 +0700 Subject: [PATCH 76/97] [CHORES] add Test file for image --- test_file/test1.jpg | Bin 0 -> 182455 bytes test_file/test2.jpg | Bin 0 -> 249224 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 test_file/test1.jpg create mode 100644 test_file/test2.jpg diff --git a/test_file/test1.jpg b/test_file/test1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5521673831b4e2d02eed407942bbeda242fd6e2d GIT binary patch literal 182455 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%0A9vIwzOa_=S$gX(G#M09zMVysj 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

ljD(WELFbv>;npyEjlBzKHl^gXQZ0 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 literal 0 HcmV?d00001 -- GitLab From 4d939f69129c4f9b2f15fc45ab8bcdc7b8711b5d Mon Sep 17 00:00:00 2001 From: Muhammad Rafif Elfazri Date: Tue, 11 May 2021 09:57:18 +0700 Subject: [PATCH 77/97] [REFACTOR] Change name add_foto_kegiatan to _add_foto_kegiatan for marking it as private function --- informasi_fasilitas/views_kegiatan.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/informasi_fasilitas/views_kegiatan.py b/informasi_fasilitas/views_kegiatan.py index b761e12..bcd83b9 100644 --- a/informasi_fasilitas/views_kegiatan.py +++ b/informasi_fasilitas/views_kegiatan.py @@ -53,7 +53,7 @@ def add_kegiatan(request, place_id): serializer = KegiatanSerializer(data=data) serializer.is_valid(raise_exception=True) kegiatan = serializer.save() - serializer = add_foto_kegiatan(kegiatan, request.data.getlist("images")) + serializer = _add_foto_kegiatan(kegiatan, request.data.getlist("images")) return JsonResponse(clean_json_kegiatan(serializer.data), status=HTTPStatus.CREATED) except Lokasi.DoesNotExist: @@ -71,14 +71,16 @@ def update_kegiatan(request, place_id, id): serializer = KegiatanSerializer(kegiatan, data=data, partial=True) serializer.is_valid(raise_exception=True) kegiatan = serializer.save() - serializer = add_foto_kegiatan(kegiatan, request.data.getlist("images")) + serializer = _add_foto_kegiatan(kegiatan, request.data.getlist("images")) return JsonResponse(clean_json_kegiatan(serializer.data), status=HTTPStatus.ACCEPTED) except Kegiatan.DoesNotExist: raise NotFound(detail="Kegiatan doesn't exist") -def add_foto_kegiatan(kegiatan, list_image): + + +def _add_foto_kegiatan(kegiatan, list_image): _create_list_kegiatan_foto(kegiatan, list_image) kegiatan.refresh_from_db() return KegiatanSerializer(kegiatan) -- GitLab From b6b5c62e0f6844697580b99bdae6c0a130bc30cd Mon Sep 17 00:00:00 2001 From: Christopher Samuel Date: Tue, 11 May 2021 10:03:09 +0700 Subject: [PATCH 78/97] [GREEN] Fixed implementation + tests for KomentarKegiatan, temporary commented unused tests --- informasi_fasilitas/serializers.py | 4 +- informasi_fasilitas/test_base.py | 6 ++- informasi_fasilitas/test_views_kegiatan.py | 36 ++++++++-------- .../test_views_komentar_kegiatan.py | 42 +++++++++---------- .../views_komentar_kegiatan.py | 5 ++- 5 files changed, 48 insertions(+), 45 deletions(-) diff --git a/informasi_fasilitas/serializers.py b/informasi_fasilitas/serializers.py index 683404b..f3f3f7c 100644 --- a/informasi_fasilitas/serializers.py +++ b/informasi_fasilitas/serializers.py @@ -48,11 +48,11 @@ class KegiatanFullSerializer(serializers.ModelSerializer): } class KomentarKegiatanSerializer(serializers.ModelSerializer): - kegiatan = serializers.CharField(source='kegiatan.id', read_only=True) + kegiatan = serializers.IntegerField(source='kegiatan.id', read_only=True) creator = serializers.CharField(source='user.last_name', read_only=True) class Meta: model = KomentarKegiatan - fields = {'id', 'creator', 'kegiatan', 'deskripsi', 'created'} + fields = ('id', 'creator', 'kegiatan', 'deskripsi', 'created') extra_kwargs = { 'deskripsi': {'required': True}, 'created': {'required' : True} diff --git a/informasi_fasilitas/test_base.py b/informasi_fasilitas/test_base.py index 6ef56a4..d67f4aa 100644 --- a/informasi_fasilitas/test_base.py +++ b/informasi_fasilitas/test_base.py @@ -281,14 +281,16 @@ class InformasiFasilitasTest(TestCase): self, user=None, komentar_kegiatan_dict=mock_komentar_kegiatan_test, - user_dict=mock_user_test + user_dict=mock_user_test, + kegiatan=None ): user = self.get_or_create_user_test( user=user, user_dict=user_dict, ) - kegiatan = self.create_kegiatan_test() + if kegiatan is None: + kegiatan = self.create_kegiatan_test() return KomentarKegiatan.objects.create( **komentar_kegiatan_dict, diff --git a/informasi_fasilitas/test_views_kegiatan.py b/informasi_fasilitas/test_views_kegiatan.py index 2d122c8..d513c1a 100644 --- a/informasi_fasilitas/test_views_kegiatan.py +++ b/informasi_fasilitas/test_views_kegiatan.py @@ -29,10 +29,10 @@ class KegiatanRelatedViewTest(InformasiFasilitasViewTest): self.update_kegiatan_url = reverse('update-kegiatan', kwargs=self.kwargs_add_or_update_kegiatan) - self.add_foto_kegiatan_url = \ - reverse('add-foto-kegiatan', kwargs=self.kwargs_kegiatan_id) - self.list_foto_kegiatan_url = \ - reverse('list-foto-kegiatan', kwargs=self.kwargs_kegiatan_id) + # self.add_foto_kegiatan_url = \ + # reverse('add-foto-kegiatan', kwargs=self.kwargs_kegiatan_id) + # self.list_foto_kegiatan_url = \ + # reverse('list-foto-kegiatan', kwargs=self.kwargs_kegiatan_id) def test_can_add_kegiatan(self): Kegiatan.objects.all().delete() @@ -114,20 +114,20 @@ class KegiatanRelatedViewTest(InformasiFasilitasViewTest): response = self.client.put(self.update_kegiatan_url, data=send_data) self.assertEqual(response.status_code, HTTPStatus.NOT_FOUND) - def test_can_add_foto_kegiatan(self): - FotoKegiatan.objects.all().delete() - response = \ - self.client.post(self.add_foto_kegiatan_url, self.mock_foto_kegiatan_test) - self.assertEqual(response.status_code, HTTPStatus.CREATED) + # def test_can_add_foto_kegiatan(self): + # FotoKegiatan.objects.all().delete() + # response = \ + # self.client.post(self.add_foto_kegiatan_url, self.mock_foto_kegiatan_test) + # self.assertEqual(response.status_code, HTTPStatus.CREATED) - count = FotoKegiatan.objects.filter(kegiatan=self.kegiatan).count() - self.assertEqual(count, 1) + # count = FotoKegiatan.objects.filter(kegiatan=self.kegiatan).count() + # self.assertEqual(count, 1) - def test_can_get_list_foto_kegiatan(self): - foto = self.create_foto_kegiatan_test() - response = self.client.get(self.list_foto_kegiatan_url) - self.assertEqual(response.status_code, HTTPStatus.OK) + # def test_can_get_list_foto_kegiatan(self): + # foto = self.create_foto_kegiatan_test() + # response = self.client.get(self.list_foto_kegiatan_url) + # self.assertEqual(response.status_code, HTTPStatus.OK) - response_json = response_decode(response) - self.assertEqual(response_json['id'], foto.id) - self.assertIsNotNone(response_json['foto']) + # response_json = response_decode(response) + # self.assertEqual(response_json['id'], foto.id) + # self.assertIsNotNone(response_json['foto']) diff --git a/informasi_fasilitas/test_views_komentar_kegiatan.py b/informasi_fasilitas/test_views_komentar_kegiatan.py index bc6de61..cd8ca12 100644 --- a/informasi_fasilitas/test_views_komentar_kegiatan.py +++ b/informasi_fasilitas/test_views_komentar_kegiatan.py @@ -19,7 +19,7 @@ class KomentarKegiatanRelatedViewTest(InformasiFasilitasViewTest): } self.kwargs_kegiatan_id = { 'place_id': self.lokasi.place_id, - 'kegiatan_id' : self.kegiatan.place_id, + 'kegiatan_id' : self.kegiatan.id, } self.add_komentar_kegiatan_url = \ @@ -37,24 +37,24 @@ class KomentarKegiatanRelatedViewTest(InformasiFasilitasViewTest): self.assertEqual(count, 1) def test_can_get_list_komentar_kegiatan(self): - komentar = self.create_komentar_kegiatan_test(user=self.user) + komentar = self.create_komentar_kegiatan_test(user=self.user, kegiatan=self.kegiatan) response = self.client.get(self.list_komentar_kegiatan_url) self.assertEqual(response.status_code, HTTPStatus.OK) response_json = response_decode(response) expected_id = komentar.id - expected_creator: komentar.user.last_name - expected_kegiatan: komentar.kegiatan.id - expected_deskripsi: komentar.deskripsi + expected_creator = komentar.user.last_name + expected_kegiatan = komentar.kegiatan.id + expected_deskripsi = komentar.deskripsi + + self.assertEqual(response_json[str(komentar.id)]['id'], expected_id) + self.assertEqual(response_json[str(komentar.id)]['creator'], expected_creator) + self.assertEqual(response_json[str(komentar.id)]['kegiatan'], expected_kegiatan) + self.assertEqual(response_json[str(komentar.id)]['deskripsi'], expected_deskripsi) + self.assertEqual(True, ('created' in response_json[str(komentar.id)].keys())) - self.assertEqual(response_json['id']['id'], expected_id) - self.assertEqual(response_json['id']['creator'], expected_creator) - self.assertEqual(response_json['id']['kegiatan'], expected_kegiatan) - self.assertEqual(response_json['id']['deskripsi'], expected_deskripsi) - self.assertEqual(True, ('created' in response_json['id'].keys())) - def test_can_get_komentar_kegiatan(self): - komentar = self.create_komentar_kegiatan_test(user=self.user) + komentar = self.create_komentar_kegiatan_test(user=self.user, kegiatan=self.kegiatan) kwargs_get_komentar_kegiatan = { 'place_id': self.lokasi.place_id, 'kegiatan_id': self.kegiatan.id, @@ -67,19 +67,19 @@ class KomentarKegiatanRelatedViewTest(InformasiFasilitasViewTest): response_json = response_decode(response) expected_id = komentar.id - expected_creator: komentar.user.last_name - expected_kegiatan: komentar.kegiatan.id - expected_deskripsi: komentar.deskripsi + expected_creator = komentar.user.last_name + expected_kegiatan = komentar.kegiatan.id + expected_deskripsi = komentar.deskripsi - self.assertEqual(response_json['id']['id'], expected_id) - self.assertEqual(response_json['id']['creator'], expected_creator) - self.assertEqual(response_json['id']['kegiatan'], expected_kegiatan) - self.assertEqual(response_json['id']['deskripsi'], expected_deskripsi) - self.assertEqual(True, ('created' in response_json['id'].keys())) + self.assertEqual(response_json['id'], expected_id) + self.assertEqual(response_json['creator'], expected_creator) + self.assertEqual(response_json['kegiatan'], expected_kegiatan) + self.assertEqual(response_json['deskripsi'], expected_deskripsi) + self.assertEqual(True, ('created' in response_json.keys())) def test_can_delete_komentar_kegiatan(self): KomentarKegiatan.objects.all().delete() - komentar = self.create_komentar_kegiatan_test(user=self.user) + komentar = self.create_komentar_kegiatan_test(user=self.user, kegiatan=self.kegiatan) kwargs_delete_komentar_kegiatan = { 'place_id': self.lokasi.place_id, diff --git a/informasi_fasilitas/views_komentar_kegiatan.py b/informasi_fasilitas/views_komentar_kegiatan.py index c8e2f5d..eea2872 100644 --- a/informasi_fasilitas/views_komentar_kegiatan.py +++ b/informasi_fasilitas/views_komentar_kegiatan.py @@ -36,8 +36,9 @@ def list_komentar_kegiatan(request, place_id, kegiatan_id): @permission_classes([]) def get_komentar_kegiatan(request, place_id, kegiatan_id, komentar_id): try: - queryset = KomentarKegiatan.objects.filter(kegiatan__lokasi__place_id=place_id, kegiatan__id=kegiatan_id) - serializer = KomentarKegiatanSerializer(queryset, many=False) + komentar = KomentarKegiatan.objects.get(kegiatan__lokasi__place_id=place_id, + kegiatan__id=kegiatan_id, id=komentar_id) + serializer = KomentarKegiatanSerializer(komentar, many=False) return JsonResponse(serializer.data, status=HTTPStatus.OK) except ObjectDoesNotExist: return JsonResponse({'response': 'Komentar tidak ditemukan'}, status=HTTPStatus.NOT_FOUND) -- GitLab From 4cb15b26de0bc1e5ef6b0c8e957386c3e317f9c3 Mon Sep 17 00:00:00 2001 From: Muhammad Rafif Elfazri Date: Tue, 11 May 2021 10:10:16 +0700 Subject: [PATCH 79/97] [REFACTOR] Move clean_json_kegiatan to KegiatanSerializer class --- informasi_fasilitas/serializers.py | 17 +++++++++++++++++ informasi_fasilitas/views_kegiatan.py | 17 +++++------------ 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/informasi_fasilitas/serializers.py b/informasi_fasilitas/serializers.py index 60ae60d..6d0a752 100644 --- a/informasi_fasilitas/serializers.py +++ b/informasi_fasilitas/serializers.py @@ -45,6 +45,23 @@ class KegiatanSerializer(serializers.ModelSerializer): 'time_start': {'required': True}, } + def json_no_relation(self): + data = self.data + dict_data = None + + def clean_json_kegiatan(kegiatan): + kegiatan.pop("user") + kegiatan.pop("lokasi") + return kegiatan + + if isinstance(data, list): + dict_data = {item['id']: clean_json_kegiatan(dict(item)) for item in data} + else: + dict_data = clean_json_kegiatan(data) + return data + + + class KomentarKegiatanSerializer(serializers.ModelSerializer): kegiatan = serializers.CharField(source='kegiatan.id', read_only=True) diff --git a/informasi_fasilitas/views_kegiatan.py b/informasi_fasilitas/views_kegiatan.py index bcd83b9..c261d77 100644 --- a/informasi_fasilitas/views_kegiatan.py +++ b/informasi_fasilitas/views_kegiatan.py @@ -11,11 +11,6 @@ from django.contrib.auth.models import User from .serializers import KegiatanSerializer -def clean_json_kegiatan(kegiatan): - kegiatan.pop("user") - kegiatan.pop("lokasi") - return kegiatan - @api_view(['GET']) @authentication_classes([]) @permission_classes([]) @@ -23,8 +18,8 @@ def list_kegiatan(request, place_id): queryset = Kegiatan.objects.filter(lokasi__place_id=place_id) serializer = KegiatanSerializer(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) + # new_dict = {item['id']: clean_json_kegiatan(dict(item)) for item in data_response} + return JsonResponse(serializer.json_no_relation(), status=HTTPStatus.OK) @api_view(['GET']) @@ -34,7 +29,7 @@ def detail_kegiatan(request, place_id, id): try: queryset = Kegiatan.objects.get(lokasi__place_id=place_id, id=id) serializer = KegiatanSerializer(queryset, many=False) - return JsonResponse(clean_json_kegiatan(serializer.data), status=HTTPStatus.OK) + return JsonResponse(serializer.json_no_relation(), status=HTTPStatus.OK) except Kegiatan.DoesNotExist: raise NotFound(detail="Kegiatan doesn't exist") @@ -55,7 +50,7 @@ def add_kegiatan(request, place_id): kegiatan = serializer.save() serializer = _add_foto_kegiatan(kegiatan, request.data.getlist("images")) - return JsonResponse(clean_json_kegiatan(serializer.data), status=HTTPStatus.CREATED) + return JsonResponse(serializer.json_no_relation(), status=HTTPStatus.CREATED) except Lokasi.DoesNotExist: raise NotFound(detail="Lokasi doesn't exist") @@ -73,13 +68,11 @@ def update_kegiatan(request, place_id, id): kegiatan = serializer.save() serializer = _add_foto_kegiatan(kegiatan, request.data.getlist("images")) - return JsonResponse(clean_json_kegiatan(serializer.data), status=HTTPStatus.ACCEPTED) + return JsonResponse(serializer.json_no_relation(), status=HTTPStatus.ACCEPTED) except Kegiatan.DoesNotExist: raise NotFound(detail="Kegiatan doesn't exist") - - def _add_foto_kegiatan(kegiatan, list_image): _create_list_kegiatan_foto(kegiatan, list_image) kegiatan.refresh_from_db() -- GitLab From 296a6e371869fb5062e8dde29df810cd861cbc49 Mon Sep 17 00:00:00 2001 From: Muhammad Rafif Elfazri Date: Tue, 11 May 2021 10:59:01 +0700 Subject: [PATCH 80/97] [RED] Create test for get list of FotoKegiatan --- informasi_fasilitas/test_views_kegiatan.py | 43 +++++++++++----------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/informasi_fasilitas/test_views_kegiatan.py b/informasi_fasilitas/test_views_kegiatan.py index ac29995..5a359bb 100644 --- a/informasi_fasilitas/test_views_kegiatan.py +++ b/informasi_fasilitas/test_views_kegiatan.py @@ -22,6 +22,11 @@ class KegiatanRelatedViewTest(InformasiFasilitasViewTest): 'place_id': self.lokasi.place_id, 'id': self.kegiatan.id, } + self.kwargs_get_foto_kegiatan = { + 'place_id': self.lokasi.place_id, + 'kegiatan_id': self.kegiatan.id, + } + self.kwargs_kegiatan_id = {'kegiatan_id' : self.kegiatan.lokasi.place_id} image_path1, image_path2 = ("test_file/test1.jpg", "test_file/test2.jpg") @@ -31,6 +36,8 @@ class KegiatanRelatedViewTest(InformasiFasilitasViewTest): content_type='image/jpeg') self.kegiatan_images = {'images': [image1, image2]} + for image in self.kegiatan_images["images"]: + FotoKegiatan.objects.create(kegiatan=self.kegiatan, foto=image) self.get_list_kegiatan_url = \ reverse('list-kegiatan', kwargs=self.kwargs_place_id) @@ -41,10 +48,8 @@ class KegiatanRelatedViewTest(InformasiFasilitasViewTest): self.update_kegiatan_url = reverse('update-kegiatan', kwargs=self.kwargs_add_or_update_kegiatan) - # self.add_foto_kegiatan_url = \ - # reverse('add-foto-kegiatan', kwargs=self.kwargs_kegiatan_id) - # self.list_foto_kegiatan_url = \ - # reverse('list-foto-kegiatan', kwargs=self.kwargs_kegiatan_id) + self.list_foto_kegiatan_url = \ + reverse('list-foto-kegiatan', kwargs=self.kwargs_get_foto_kegiatan) def tearDown(self): try: @@ -132,7 +137,7 @@ class KegiatanRelatedViewTest(InformasiFasilitasViewTest): count = Kegiatan.objects.all().count() count_foto = FotoKegiatan.objects.all().count() self.assertEqual(count, 1) - self.assertEqual(count_foto, 2) + self.assertEqual(count_foto, 4) def test_update_kegiatan_lokasi_not_exist(self): @@ -149,20 +154,14 @@ class KegiatanRelatedViewTest(InformasiFasilitasViewTest): response = self.client.put(url, data=send_data) self.assertEqual(response.status_code, HTTPStatus.NOT_FOUND) - # def test_can_add_foto_kegiatan(self): - # FotoKegiatan.objects.all().delete() - # response = \ - # self.client.post(self.add_foto_kegiatan_url, self.mock_foto_kegiatan_test) - # self.assertEqual(response.status_code, HTTPStatus.CREATED) - # - # count = FotoKegiatan.objects.filter(kegiatan=self.kegiatan).count() - # self.assertEqual(count, 1) - # - # def test_can_get_list_foto_kegiatan(self): - # foto = self.create_foto_kegiatan_test() - # response = self.client.get(self.list_foto_kegiatan_url) - # self.assertEqual(response.status_code, HTTPStatus.OK) - # - # response_json = response_decode(response) - # self.assertEqual(response_json['id'], foto.id) - # self.assertIsNotNone(response_json['foto']) + 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.assertEqual(item["kegiatan"], self.kegiatan.id) + counter += 1 + + -- GitLab From 0b95657e9f2edf45f9f315062f60b543e3990e13 Mon Sep 17 00:00:00 2001 From: Muhammad Rafif Elfazri Date: Tue, 11 May 2021 11:00:32 +0700 Subject: [PATCH 81/97] [GREEN] Implement API to get list of FotoKegiatan --- informasi_fasilitas/urls.py | 5 +++++ informasi_fasilitas/views_kegiatan.py | 13 ++++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/informasi_fasilitas/urls.py b/informasi_fasilitas/urls.py index 41ef7e3..18018ce 100644 --- a/informasi_fasilitas/urls.py +++ b/informasi_fasilitas/urls.py @@ -42,6 +42,9 @@ urlpatterns = [ path('lokasi/update-kegiatan///', views_kegiatan.update_kegiatan, name='update-kegiatan'), + path('lokasi/list-foto-kegiatan//', + views_kegiatan.list_foto_kegiatan, name='list-foto-kegiatan'), + path('lokasi/get-komentar-kegiatan///', views_komentar_kegiatan.get_komentar_kegiatan, name='get-komentar-kegiatan'), @@ -53,4 +56,6 @@ 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 c261d77..1c3bd29 100644 --- a/informasi_fasilitas/views_kegiatan.py +++ b/informasi_fasilitas/views_kegiatan.py @@ -8,7 +8,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 +from .serializers import KegiatanSerializer, FotoKegiatanSerializer @api_view(['GET']) @@ -72,6 +72,17 @@ def update_kegiatan(request, place_id, id): except Kegiatan.DoesNotExist: raise NotFound(detail="Kegiatan doesn't exist") +@api_view(['GET']) +@authentication_classes([]) +@permission_classes([]) +def list_foto_kegiatan(request, place_id, kegiatan_id): + queryset = FotoKegiatan.objects.filter(kegiatan__lokasi__place_id=place_id, + kegiatan_id=kegiatan_id) + serializer = FotoKegiatanSerializer(queryset, many=True) + data_response = serializer.data + new_dict = {item['id']: dict(item) for item in data_response} + return JsonResponse(new_dict, status=HTTPStatus.OK) + def _add_foto_kegiatan(kegiatan, list_image): _create_list_kegiatan_foto(kegiatan, list_image) -- GitLab From 9e62a37bf6e82919a9c21f5149d4cd1f7a76fea0 Mon Sep 17 00:00:00 2001 From: Muhammad Rafif Elfazri Date: Tue, 11 May 2021 11:03:58 +0700 Subject: [PATCH 82/97] [REFACTOR] Change back clean_json_kegiatan to function in views_kegiatan.py --- informasi_fasilitas/serializers.py | 18 +----------------- informasi_fasilitas/views_kegiatan.py | 16 +++++++++++----- 2 files changed, 12 insertions(+), 22 deletions(-) diff --git a/informasi_fasilitas/serializers.py b/informasi_fasilitas/serializers.py index 6d0a752..c031af0 100644 --- a/informasi_fasilitas/serializers.py +++ b/informasi_fasilitas/serializers.py @@ -2,6 +2,7 @@ from rest_framework import serializers from .models import Lokasi, Kegiatan, KomentarKegiatan, FotoKegiatan + class LokasiSerializer(serializers.ModelSerializer): class Meta: @@ -45,23 +46,6 @@ class KegiatanSerializer(serializers.ModelSerializer): 'time_start': {'required': True}, } - def json_no_relation(self): - data = self.data - dict_data = None - - def clean_json_kegiatan(kegiatan): - kegiatan.pop("user") - kegiatan.pop("lokasi") - return kegiatan - - if isinstance(data, list): - dict_data = {item['id']: clean_json_kegiatan(dict(item)) for item in data} - else: - dict_data = clean_json_kegiatan(data) - return data - - - class KomentarKegiatanSerializer(serializers.ModelSerializer): kegiatan = serializers.CharField(source='kegiatan.id', read_only=True) diff --git a/informasi_fasilitas/views_kegiatan.py b/informasi_fasilitas/views_kegiatan.py index 1c3bd29..86c819e 100644 --- a/informasi_fasilitas/views_kegiatan.py +++ b/informasi_fasilitas/views_kegiatan.py @@ -18,8 +18,8 @@ def list_kegiatan(request, place_id): queryset = Kegiatan.objects.filter(lokasi__place_id=place_id) serializer = KegiatanSerializer(queryset, many=True) data_response = serializer.data - # new_dict = {item['id']: clean_json_kegiatan(dict(item)) for item in data_response} - return JsonResponse(serializer.json_no_relation(), status=HTTPStatus.OK) + new_dict = {item['id']: _clean_json_kegiatan(dict(item)) for item in data_response} + return JsonResponse(new_dict, status=HTTPStatus.OK) @api_view(['GET']) @@ -29,7 +29,7 @@ def detail_kegiatan(request, place_id, id): try: queryset = Kegiatan.objects.get(lokasi__place_id=place_id, id=id) serializer = KegiatanSerializer(queryset, many=False) - return JsonResponse(serializer.json_no_relation(), status=HTTPStatus.OK) + return JsonResponse(_clean_json_kegiatan(serializer.data), status=HTTPStatus.OK) except Kegiatan.DoesNotExist: raise NotFound(detail="Kegiatan doesn't exist") @@ -50,7 +50,7 @@ def add_kegiatan(request, place_id): kegiatan = serializer.save() serializer = _add_foto_kegiatan(kegiatan, request.data.getlist("images")) - return JsonResponse(serializer.json_no_relation(), status=HTTPStatus.CREATED) + return JsonResponse(_clean_json_kegiatan(serializer.data), status=HTTPStatus.CREATED) except Lokasi.DoesNotExist: raise NotFound(detail="Lokasi doesn't exist") @@ -68,7 +68,7 @@ def update_kegiatan(request, place_id, id): kegiatan = serializer.save() serializer = _add_foto_kegiatan(kegiatan, request.data.getlist("images")) - return JsonResponse(serializer.json_no_relation(), status=HTTPStatus.ACCEPTED) + return JsonResponse(_clean_json_kegiatan(serializer.data), status=HTTPStatus.ACCEPTED) except Kegiatan.DoesNotExist: raise NotFound(detail="Kegiatan doesn't exist") @@ -84,6 +84,12 @@ def list_foto_kegiatan(request, place_id, kegiatan_id): return JsonResponse(new_dict, status=HTTPStatus.OK) +def _clean_json_kegiatan(kegiatan): + kegiatan.pop("user") + kegiatan.pop("lokasi") + return kegiatan + + def _add_foto_kegiatan(kegiatan, list_image): _create_list_kegiatan_foto(kegiatan, list_image) kegiatan.refresh_from_db() -- GitLab From 2b8d8397389b33523cf335082f769cc601dbca8d Mon Sep 17 00:00:00 2001 From: "alvin.hariman" Date: Tue, 11 May 2021 14:26:01 +0700 Subject: [PATCH 83/97] [CHORES] Delete debug code --- new_rest_api/views.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/new_rest_api/views.py b/new_rest_api/views.py index 0a54cc7..aa8e35d 100644 --- a/new_rest_api/views.py +++ b/new_rest_api/views.py @@ -72,8 +72,6 @@ def user_details(request, email): @authentication_classes([]) @permission_classes([]) def register_user(request): - print(request.POST) - print(request.FILES) try: if request.method == 'POST': name = request.POST['name'] @@ -158,20 +156,12 @@ def update_user(request): user.last_name = name data['user'] = user.pk data_query_dict = QueryDict('', mutable=True) - print(1) data_query_dict.update(data) - print(2) serializer = BisaGoUserSerializers(bisa_go_user, data=data_query_dict) - print(3) if serializer.is_valid(): - print(4) user.save() - print(5) serializer.save() - print(6) else: - print(serializer.errors) - print(7) return JsonResponse({'response': 'Internal server error'}, safe=False, status=status.INTERNAL_SERVER_ERROR) return JsonResponse({'response': 'User updated'}, safe=False, status=status.OK) -- GitLab From 2d2b1190f4161eb45f722e6a829449e04c659f26 Mon Sep 17 00:00:00 2001 From: Muhammad Rafif Elfazri Date: Tue, 11 May 2021 15:05:30 +0700 Subject: [PATCH 84/97] [CHORES] register Kegiatan related models to admin --- informasi_fasilitas/admin.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/informasi_fasilitas/admin.py b/informasi_fasilitas/admin.py index 3f66350..16f4a84 100644 --- a/informasi_fasilitas/admin.py +++ b/informasi_fasilitas/admin.py @@ -1,7 +1,10 @@ from django.contrib import admin -from .models import Lokasi, Fasilitas, Komentar +from .models import Lokasi, Fasilitas, Komentar, Kegiatan, KomentarKegiatan, FotoKegiatan # Register your models here. admin.site.register(Lokasi) admin.site.register(Fasilitas) admin.site.register(Komentar) +admin.site.register(Kegiatan) +admin.site.register(KomentarKegiatan) +admin.site.register(FotoKegiatan) -- GitLab From e71d0e0e5dc013e74d610af5f6ebafa6e24803aa Mon Sep 17 00:00:00 2001 From: Muhammad Rafif Elfazri Date: Sat, 15 May 2021 15:19:48 +0700 Subject: [PATCH 85/97] [RED] Create test for detail kegiatan and nearest kegiatan --- informasi_fasilitas/test_base.py | 6 +-- informasi_fasilitas/test_views_kegiatan.py | 44 ++++++++++++++++++++++ 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/informasi_fasilitas/test_base.py b/informasi_fasilitas/test_base.py index a9ec6f3..10ec4c2 100644 --- a/informasi_fasilitas/test_base.py +++ b/informasi_fasilitas/test_base.py @@ -1,7 +1,7 @@ import json import tempfile -from datetime import datetime +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 @@ -52,8 +52,8 @@ class InformasiFasilitasTest(TestCase): mock_kegiatan_test = { 'nama_kegiatan': 'mock kegiatan', 'penyelenggara': 'mock penyelenggara', - 'time_start': datetime.now().strftime("%Y-%m-%d %H:%M"), - 'time_end': datetime.now().strftime("%Y-%m-%d %H:%M"), + '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', } diff --git a/informasi_fasilitas/test_views_kegiatan.py b/informasi_fasilitas/test_views_kegiatan.py index d7546fa..2f57b9d 100644 --- a/informasi_fasilitas/test_views_kegiatan.py +++ b/informasi_fasilitas/test_views_kegiatan.py @@ -1,12 +1,15 @@ import json import shutil import os +from datetime import timedelta from http import HTTPStatus from django.test import Client from django.urls import reverse from django.test import override_settings +from django.utils import timezone from django.core.files.uploadedfile import SimpleUploadedFile from django.conf import settings +from unittest.mock import patch from .test_base import InformasiFasilitasViewTest from .models import Lokasi, Kegiatan, FotoKegiatan from pplbackend.utils import response_decode @@ -43,6 +46,8 @@ class KegiatanRelatedViewTest(InformasiFasilitasViewTest): reverse('list-kegiatan', kwargs=self.kwargs_place_id) self.get_detail_kegiatan_url = \ reverse('detail-kegiatan', kwargs=self.kwargs_add_or_update_kegiatan) + self.get_nearest_kegiatan_url = \ + reverse('nearest-kegiatan') self.add_kegiatan_url = \ reverse('add-kegiatan', kwargs=self.kwargs_place_id) self.update_kegiatan_url = reverse('update-kegiatan', @@ -115,6 +120,45 @@ class KegiatanRelatedViewTest(InformasiFasilitasViewTest): } self.assertEqual(content, expected_json) + def test_can_get_detail_kegiatan(self): + response = Client().get(self.get_detail_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, + 'narahubung': self.kegiatan.narahubung, + 'time_start': self.kegiatan.time_start, + 'time_end': self.kegiatan.time_end, + } + self.assertEqual(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, + 'narahubung': self.kegiatan.narahubung, + 'time_start': self.kegiatan.time_start, + 'time_end': self.kegiatan.time_end, + } + self.assertEqual(content, expected_json) + + @patch('informasi_fasilitas.views_kegiatan.timezone') + def test_empty_get_nearest_kegiatan(self, mock_timezone): + mock_timezone.now.return_value = timezone.now() + timedelta(days=100) + response = Client().get(self.get_nearest_kegiatan_url) + self.assertEqual(response.status_code, HTTPStatus.NOT_FOUND) @override_settings(MEDIA_ROOT=("test_file" + '/media')) def test_can_put_update_kegiatan(self): -- GitLab From 08d6aad95f0e8452c73d2b307caa42e4701cdfb1 Mon Sep 17 00:00:00 2001 From: Muhammad Rafif Elfazri Date: Sat, 15 May 2021 15:20:56 +0700 Subject: [PATCH 86/97] [GREEN] Implement nearest kegiatan --- informasi_fasilitas/urls.py | 3 +++ informasi_fasilitas/views_kegiatan.py | 12 ++++++++++++ 2 files changed, 15 insertions(+) diff --git a/informasi_fasilitas/urls.py b/informasi_fasilitas/urls.py index 18018ce..9529f3e 100644 --- a/informasi_fasilitas/urls.py +++ b/informasi_fasilitas/urls.py @@ -36,6 +36,9 @@ urlpatterns = [ path('lokasi/detail-kegiatan///', views_kegiatan.detail_kegiatan, name='detail-kegiatan'), + path('lokasi/kegiatan-terdekat', + views_kegiatan.nearest_kegiatan, name='nearest-kegiatan'), + path('lokasi/add-kegiatan//', views_kegiatan.add_kegiatan, name='add-kegiatan'), diff --git a/informasi_fasilitas/views_kegiatan.py b/informasi_fasilitas/views_kegiatan.py index 86c819e..7bf9c2c 100644 --- a/informasi_fasilitas/views_kegiatan.py +++ b/informasi_fasilitas/views_kegiatan.py @@ -1,5 +1,6 @@ from http import HTTPStatus from django.http import JsonResponse +from django.utils import timezone from rest_framework.decorators import api_view, permission_classes, authentication_classes from rest_framework.authentication import TokenAuthentication from rest_framework.permissions import IsAuthenticated @@ -34,6 +35,17 @@ def detail_kegiatan(request, place_id, id): raise NotFound(detail="Kegiatan doesn't exist") +@api_view(['GET']) +@authentication_classes([]) +@permission_classes([]) +def nearest_kegiatan(request): + time_now = timezone.now() + 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) + return JsonResponse(_clean_json_kegiatan(serializer.data), status=HTTPStatus.OK) + @api_view(['POST']) @authentication_classes([TokenAuthentication]) @permission_classes([IsAuthenticated]) -- GitLab From b4fd766ae3415d161c97ec8a845df7ff083129e7 Mon Sep 17 00:00:00 2001 From: Christopher Samuel Date: Sun, 16 May 2021 13:55:15 +0700 Subject: [PATCH 87/97] [RED] Update tests + create base models for FotoKegiatan and Komentar Kegiatan, waiting for Kegiatan model --- informasi_fasilitas/test_views_fasilitas.py | 32 +++++++++++++++---- informasi_fasilitas/test_views_kegiatan.py | 1 - .../test_views_komentar_kegiatan.py | 21 +++++++++--- informasi_fasilitas/views.py | 2 +- 4 files changed, 44 insertions(+), 12 deletions(-) diff --git a/informasi_fasilitas/test_views_fasilitas.py b/informasi_fasilitas/test_views_fasilitas.py index 9fa8193..0f6bad8 100644 --- a/informasi_fasilitas/test_views_fasilitas.py +++ b/informasi_fasilitas/test_views_fasilitas.py @@ -5,6 +5,7 @@ from django.urls import reverse from .test_base import InformasiFasilitasViewTest from .models import Fasilitas, Lokasi +from .views import TIME_FORMAT from pplbackend.utils import response_decode @@ -48,6 +49,7 @@ class FasilitasRelatedViewTest(InformasiFasilitasViewTest): 'creator': self.fasilitas.user.last_name, 'like': self.fasilitas.like, 'dislike': self.fasilitas.dislike, + 'date_time': self.fasilitas.date_time.strftime(TIME_FORMAT), 'rating': self.fasilitas.rating, 'tag': 'KR', 'disabilitas': ['DF'], @@ -57,8 +59,30 @@ class FasilitasRelatedViewTest(InformasiFasilitasViewTest): }, } - time = self.fasilitas.date_time.strftime("%d-%m-%Y %H:%M:%S") - expected_json[str(self.fasilitas.id)]['date_time'] = time + self.assertEqual(content, expected_json) + + def test_can_get_detail_fasilitas_json(self): + response = Client().get(self.get_detail_fasilitas_url) + content = json.loads(response.content.decode('utf-8')) + fasilitas = Fasilitas.objects.get(id=self.fasilitas.id) + expected_json = { + "id": fasilitas.id, + "place_id": fasilitas.lokasi.place_id, + "deskripsi": fasilitas.deskripsi, + "creator": self.user.last_name, + "creator_email": self.user.email, + "date_time": fasilitas.date_time.strftime(TIME_FORMAT), + "like": fasilitas.like, + "dislike": fasilitas.dislike, + "rating": fasilitas.rating, + "tag": fasilitas.tag, + "disabilitas": fasilitas.disabilitas, + "jumlah": fasilitas.jumlah, + "image": '/media/' + str(fasilitas.image), + "is_verified": fasilitas.is_verified + } + + self.assertEqual(response.status_code, HTTPStatus.OK) self.assertEqual(content, expected_json) def test_cannot_post_list_fasilitas(self): @@ -74,10 +98,6 @@ class FasilitasRelatedViewTest(InformasiFasilitasViewTest): response = Client().get(self.get_detail_fasilitas_url) self.assertEqual(response.status_code, HTTPStatus.OK) - def test_can_get_detail_fasilitas_json(self): - response = Client().get(self.get_detail_fasilitas_url) - self.assertEqual(response.status_code, HTTPStatus.OK) - def test_cannot_post_detail_fasilitas(self): response = Client().post(self.get_detail_fasilitas_url) self.assertEqual(response.status_code, HTTPStatus.METHOD_NOT_ALLOWED) diff --git a/informasi_fasilitas/test_views_kegiatan.py b/informasi_fasilitas/test_views_kegiatan.py index 2f57b9d..0b6eb1f 100644 --- a/informasi_fasilitas/test_views_kegiatan.py +++ b/informasi_fasilitas/test_views_kegiatan.py @@ -209,4 +209,3 @@ class KegiatanRelatedViewTest(InformasiFasilitasViewTest): self.assertIn("test"+str(counter), item["foto"]) self.assertEqual(item["kegiatan"], self.kegiatan.id) counter += 1 - diff --git a/informasi_fasilitas/test_views_komentar_kegiatan.py b/informasi_fasilitas/test_views_komentar_kegiatan.py index cd8ca12..c712e22 100644 --- a/informasi_fasilitas/test_views_komentar_kegiatan.py +++ b/informasi_fasilitas/test_views_komentar_kegiatan.py @@ -13,10 +13,6 @@ class KomentarKegiatanRelatedViewTest(InformasiFasilitasViewTest): super().setUp() self.kegiatan = self.create_kegiatan_test(self.user, self.lokasi) self.kwargs_place_id = {'place_id': self.lokasi.place_id} - self.kwargs_get_or_delete_komentar_kegiatan = { - 'kegiatan_id': self.kegiatan.id, - 'komentar_id': self.kegiatan.id, - } self.kwargs_kegiatan_id = { 'place_id': self.lokasi.place_id, 'kegiatan_id' : self.kegiatan.id, @@ -36,6 +32,15 @@ class KomentarKegiatanRelatedViewTest(InformasiFasilitasViewTest): count = KomentarKegiatan.objects.filter(kegiatan=self.kegiatan).count() self.assertEqual(count, 1) + def test_fail_add_komentar_kegiatan(self): + KomentarKegiatan.objects.all().delete() + response = \ + self.client.post(self.add_komentar_kegiatan_url, None) + self.assertEqual(response.status_code, HTTPStatus.NOT_FOUND) + + count = KomentarKegiatan.objects.filter(kegiatan=self.kegiatan).count() + self.assertEqual(count, 0) + def test_can_get_list_komentar_kegiatan(self): komentar = self.create_komentar_kegiatan_test(user=self.user, kegiatan=self.kegiatan) response = self.client.get(self.list_komentar_kegiatan_url) @@ -77,6 +82,10 @@ class KomentarKegiatanRelatedViewTest(InformasiFasilitasViewTest): self.assertEqual(response_json['deskripsi'], expected_deskripsi) self.assertEqual(True, ('created' in response_json.keys())) + def test_fail_get_komentar_kegiatan(self): + response = self.client.get('/informasi-fasilitas/lokasi/get-komentar-kegiatan/harusnyaTidakAda/101/1011') + self.assertEqual(response.status_code, HTTPStatus.NOT_FOUND) + def test_can_delete_komentar_kegiatan(self): KomentarKegiatan.objects.all().delete() komentar = self.create_komentar_kegiatan_test(user=self.user, kegiatan=self.kegiatan) @@ -95,4 +104,8 @@ class KomentarKegiatanRelatedViewTest(InformasiFasilitasViewTest): count = KomentarKegiatan.objects.filter(kegiatan=self.kegiatan).count() self.assertEqual(count, 0) + + def test_fail_delete_komentar_kegiatan(self): + response = self.client.delete('/informasi-fasilitas/lokasi/delete-komentar-kegiatan/harusnyaTidakAda/10/10101') + self.assertEqual(response.status_code, HTTPStatus.NOT_FOUND) diff --git a/informasi_fasilitas/views.py b/informasi_fasilitas/views.py index de5e357..e5904ef 100644 --- a/informasi_fasilitas/views.py +++ b/informasi_fasilitas/views.py @@ -141,7 +141,7 @@ def detail_fasilitas(request, place_id, id): "deskripsi": fasilitas.deskripsi, "creator": user.last_name, "creator_email": user.email, - "date_time": fasilitas.date_time, + "date_time": fasilitas.date_time.strftime(TIME_FORMAT), "like": fasilitas.like, "dislike": fasilitas.dislike, "rating": fasilitas.rating, -- GitLab From 2d17d6ab7fccc49dc0e6d6bb4f81348827c3f0a6 Mon Sep 17 00:00:00 2001 From: Christopher Samuel Date: Sun, 16 May 2021 14:32:19 +0700 Subject: [PATCH 88/97] [RED] Update tests + create base models for FotoKegiatan and Komentar Kegiatan, waiting for Kegiatan model --- informasi_fasilitas/test_views_kegiatan.py | 1 - .../test_views_komentar_kegiatan.py | 21 +++++++++++++++---- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/informasi_fasilitas/test_views_kegiatan.py b/informasi_fasilitas/test_views_kegiatan.py index 2f57b9d..0b6eb1f 100644 --- a/informasi_fasilitas/test_views_kegiatan.py +++ b/informasi_fasilitas/test_views_kegiatan.py @@ -209,4 +209,3 @@ class KegiatanRelatedViewTest(InformasiFasilitasViewTest): self.assertIn("test"+str(counter), item["foto"]) self.assertEqual(item["kegiatan"], self.kegiatan.id) counter += 1 - diff --git a/informasi_fasilitas/test_views_komentar_kegiatan.py b/informasi_fasilitas/test_views_komentar_kegiatan.py index cd8ca12..c712e22 100644 --- a/informasi_fasilitas/test_views_komentar_kegiatan.py +++ b/informasi_fasilitas/test_views_komentar_kegiatan.py @@ -13,10 +13,6 @@ class KomentarKegiatanRelatedViewTest(InformasiFasilitasViewTest): super().setUp() self.kegiatan = self.create_kegiatan_test(self.user, self.lokasi) self.kwargs_place_id = {'place_id': self.lokasi.place_id} - self.kwargs_get_or_delete_komentar_kegiatan = { - 'kegiatan_id': self.kegiatan.id, - 'komentar_id': self.kegiatan.id, - } self.kwargs_kegiatan_id = { 'place_id': self.lokasi.place_id, 'kegiatan_id' : self.kegiatan.id, @@ -36,6 +32,15 @@ class KomentarKegiatanRelatedViewTest(InformasiFasilitasViewTest): count = KomentarKegiatan.objects.filter(kegiatan=self.kegiatan).count() self.assertEqual(count, 1) + def test_fail_add_komentar_kegiatan(self): + KomentarKegiatan.objects.all().delete() + response = \ + self.client.post(self.add_komentar_kegiatan_url, None) + self.assertEqual(response.status_code, HTTPStatus.NOT_FOUND) + + count = KomentarKegiatan.objects.filter(kegiatan=self.kegiatan).count() + self.assertEqual(count, 0) + def test_can_get_list_komentar_kegiatan(self): komentar = self.create_komentar_kegiatan_test(user=self.user, kegiatan=self.kegiatan) response = self.client.get(self.list_komentar_kegiatan_url) @@ -77,6 +82,10 @@ class KomentarKegiatanRelatedViewTest(InformasiFasilitasViewTest): self.assertEqual(response_json['deskripsi'], expected_deskripsi) self.assertEqual(True, ('created' in response_json.keys())) + def test_fail_get_komentar_kegiatan(self): + response = self.client.get('/informasi-fasilitas/lokasi/get-komentar-kegiatan/harusnyaTidakAda/101/1011') + self.assertEqual(response.status_code, HTTPStatus.NOT_FOUND) + def test_can_delete_komentar_kegiatan(self): KomentarKegiatan.objects.all().delete() komentar = self.create_komentar_kegiatan_test(user=self.user, kegiatan=self.kegiatan) @@ -95,4 +104,8 @@ class KomentarKegiatanRelatedViewTest(InformasiFasilitasViewTest): count = KomentarKegiatan.objects.filter(kegiatan=self.kegiatan).count() self.assertEqual(count, 0) + + def test_fail_delete_komentar_kegiatan(self): + response = self.client.delete('/informasi-fasilitas/lokasi/delete-komentar-kegiatan/harusnyaTidakAda/10/10101') + self.assertEqual(response.status_code, HTTPStatus.NOT_FOUND) -- GitLab From f07566da5a9a0836934d6809cab5d146302de5e5 Mon Sep 17 00:00:00 2001 From: Muhammad Rafif Elfazri Date: Mon, 17 May 2021 00:04:53 +0700 Subject: [PATCH 89/97] [RED] Test for adding links field in kegiatan --- informasi_fasilitas/test_base.py | 1 + informasi_fasilitas/test_views_kegiatan.py | 3 +++ 2 files changed, 4 insertions(+) diff --git a/informasi_fasilitas/test_base.py b/informasi_fasilitas/test_base.py index 10ec4c2..e481d3d 100644 --- a/informasi_fasilitas/test_base.py +++ b/informasi_fasilitas/test_base.py @@ -56,6 +56,7 @@ class InformasiFasilitasTest(TestCase): '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_komentar_kegiatan_test = { diff --git a/informasi_fasilitas/test_views_kegiatan.py b/informasi_fasilitas/test_views_kegiatan.py index 0b6eb1f..cd84115 100644 --- a/informasi_fasilitas/test_views_kegiatan.py +++ b/informasi_fasilitas/test_views_kegiatan.py @@ -113,6 +113,7 @@ class KegiatanRelatedViewTest(InformasiFasilitasViewTest): '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, @@ -131,6 +132,7 @@ class KegiatanRelatedViewTest(InformasiFasilitasViewTest): '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, @@ -148,6 +150,7 @@ class KegiatanRelatedViewTest(InformasiFasilitasViewTest): '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, -- GitLab From 2deb7d9294031c5e844997d49b3251c987b9eb52 Mon Sep 17 00:00:00 2001 From: Muhammad Rafif Elfazri Date: Mon, 17 May 2021 00:05:51 +0700 Subject: [PATCH 90/97] [GREEN] implement adding field links in Kegiatan Model --- .../migrations/0016_kegiatan_links.py | 18 ++++++++++++++++++ informasi_fasilitas/models.py | 1 + informasi_fasilitas/serializers.py | 2 +- 3 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 informasi_fasilitas/migrations/0016_kegiatan_links.py diff --git a/informasi_fasilitas/migrations/0016_kegiatan_links.py b/informasi_fasilitas/migrations/0016_kegiatan_links.py new file mode 100644 index 0000000..c49399f --- /dev/null +++ b/informasi_fasilitas/migrations/0016_kegiatan_links.py @@ -0,0 +1,18 @@ +# Generated by Django 3.1.7 on 2021-05-16 16:56 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('informasi_fasilitas', '0015_auto_20210511_0249'), + ] + + operations = [ + migrations.AddField( + model_name='kegiatan', + name='links', + field=models.TextField(blank=True, null=True), + ), + ] diff --git a/informasi_fasilitas/models.py b/informasi_fasilitas/models.py index 2f2178b..7745493 100644 --- a/informasi_fasilitas/models.py +++ b/informasi_fasilitas/models.py @@ -82,6 +82,7 @@ class Kegiatan(models.Model): penyelenggara = models.CharField(max_length=50) narahubung = models.TextField(null=True) deskripsi = models.TextField(null=False) + links = models.TextField(null=True, blank=True) time_start = models.DateTimeField(blank=False, null=False, default=timezone.now) time_end = models.DateTimeField(blank=True, null=True) diff --git a/informasi_fasilitas/serializers.py b/informasi_fasilitas/serializers.py index 038e386..8b034ae 100644 --- a/informasi_fasilitas/serializers.py +++ b/informasi_fasilitas/serializers.py @@ -37,7 +37,7 @@ class KegiatanSerializer(serializers.ModelSerializer): model = Kegiatan fields = ('id', 'place_id', 'creator', 'lokasi', 'user', 'nama_kegiatan', 'penyelenggara', 'deskripsi', - "narahubung", 'time_start', 'time_end') + "links","narahubung", 'time_start', 'time_end') extra_kwargs = { 'nama_kegiatan': {'required': True}, 'penyelenggara': {'required': True}, -- GitLab From 4e5c0689401f1930cd921ffec91335eee75c4cc1 Mon Sep 17 00:00:00 2001 From: Muhammad Rafif Elfazri Date: Mon, 17 May 2021 00:36:04 +0700 Subject: [PATCH 91/97] [CHORES] add default tanggal lahir value --- .../migrations/0005_auto_20210516_1734.py | 19 +++++++++++++++++++ registrasi/models.py | 3 ++- 2 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 registrasi/migrations/0005_auto_20210516_1734.py diff --git a/registrasi/migrations/0005_auto_20210516_1734.py b/registrasi/migrations/0005_auto_20210516_1734.py new file mode 100644 index 0000000..a627fce --- /dev/null +++ b/registrasi/migrations/0005_auto_20210516_1734.py @@ -0,0 +1,19 @@ +# Generated by Django 3.1.7 on 2021-05-16 17:34 + +import datetime +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('registrasi', '0004_auto_20201210_1459'), + ] + + operations = [ + migrations.AlterField( + model_name='bisagouser', + name='tanggal_lahir', + field=models.DateField(default=datetime.date.today, max_length=15, verbose_name='Tanggal Lahir'), + ), + ] diff --git a/registrasi/models.py b/registrasi/models.py index 65bd41a..da66e63 100644 --- a/registrasi/models.py +++ b/registrasi/models.py @@ -1,6 +1,7 @@ from django.db import models from django.core.mail import send_mail from django.contrib.auth.models import User +from datetime import date class BisaGoUser(models.Model): @@ -8,7 +9,7 @@ class BisaGoUser(models.Model): db_table = "BisaGoUser" user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="phone_number") phone_number = models.CharField('Nomor Telepon', max_length=15, unique=True) - tanggal_lahir = models.DateField('Tanggal Lahir', max_length=15, null=True) + tanggal_lahir = models.DateField('Tanggal Lahir', max_length=15, null=False, default=date.today) jenis_kelamin = models.CharField('Jenis Kelamin', max_length=20, default='-') disabilitas = models.CharField('Disabilitas', max_length=64, null=True, default='Belum memilih') pekerjaan = models.CharField('Pekerjaan', max_length=64, default='-') -- GitLab From ab02083c58cef844ba4b29d3cc3d635a8f35a403 Mon Sep 17 00:00:00 2001 From: Muhammad Rafif Elfazri Date: Mon, 17 May 2021 01:21:07 +0700 Subject: [PATCH 92/97] [CHORES] Fix migrations --- registrasi/migrations/0006_merge_20210516_1820.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 registrasi/migrations/0006_merge_20210516_1820.py diff --git a/registrasi/migrations/0006_merge_20210516_1820.py b/registrasi/migrations/0006_merge_20210516_1820.py new file mode 100644 index 0000000..d6db443 --- /dev/null +++ b/registrasi/migrations/0006_merge_20210516_1820.py @@ -0,0 +1,14 @@ +# Generated by Django 3.1.7 on 2021-05-16 18:20 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('registrasi', '0005_bisagouser_foto'), + ('registrasi', '0005_auto_20210516_1734'), + ] + + operations = [ + ] -- GitLab From aea95c308eba46fcfcc34dd9b6a12fd4b3b5ecc6 Mon Sep 17 00:00:00 2001 From: "alvin.hariman" Date: Mon, 17 May 2021 01:54:25 +0700 Subject: [PATCH 93/97] [RED] Add creator email in list fasilitas --- informasi_fasilitas/test_views_fasilitas.py | 1 + 1 file changed, 1 insertion(+) diff --git a/informasi_fasilitas/test_views_fasilitas.py b/informasi_fasilitas/test_views_fasilitas.py index 0f6bad8..cbd8f6f 100644 --- a/informasi_fasilitas/test_views_fasilitas.py +++ b/informasi_fasilitas/test_views_fasilitas.py @@ -44,6 +44,7 @@ class FasilitasRelatedViewTest(InformasiFasilitasViewTest): expected_json = { str(self.fasilitas.id): { 'id': self.fasilitas.id, + 'creator_email': self.fasilitas.user.email, 'place_id': self.fasilitas.lokasi.place_id, 'deskripsi': self.fasilitas.deskripsi, 'creator': self.fasilitas.user.last_name, -- GitLab From 97d023ac433c2a0b45cd1ec3a710a16188675ac8 Mon Sep 17 00:00:00 2001 From: "alvin.hariman" Date: Mon, 17 May 2021 01:56:10 +0700 Subject: [PATCH 94/97] [GREEN] Add creator email in list fasilitas --- informasi_fasilitas/views.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/informasi_fasilitas/views.py b/informasi_fasilitas/views.py index e5904ef..bebbff7 100644 --- a/informasi_fasilitas/views.py +++ b/informasi_fasilitas/views.py @@ -64,6 +64,7 @@ def list_fasilitas(request, place_id): list_fasilitas = Fasilitas.objects.filter(lokasi=lokasi) return_json = {} for fasilitas in list_fasilitas: + user = fasilitas.user fasilitas.like = Likes.objects.filter( fasilitas=fasilitas).count() fasilitas.dislike = Dislikes.objects.filter( @@ -71,6 +72,7 @@ def list_fasilitas(request, place_id): return_json[fasilitas.id] = {} fasilitas_details = return_json[fasilitas.id] fasilitas_details["id"] = fasilitas.id + fasilitas_details["creator_email"] = user.email fasilitas_details["place_id"] = fasilitas.lokasi.place_id fasilitas_details["deskripsi"] = fasilitas.deskripsi fasilitas_details["creator"] = fasilitas.user.last_name -- GitLab From be1182c7302bfbf9e9e964fc283f766fede4e52f Mon Sep 17 00:00:00 2001 From: "alvin.hariman" Date: Mon, 17 May 2021 02:23:14 +0700 Subject: [PATCH 95/97] [RED] Add seen attribute in edit profile --- new_rest_api/tests.py | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/new_rest_api/tests.py b/new_rest_api/tests.py index 1d715b4..81b9256 100644 --- a/new_rest_api/tests.py +++ b/new_rest_api/tests.py @@ -53,14 +53,14 @@ class UserTests(APITestCase): response = self.client.get(url, format='json') self.assertEqual(response.status_code, status.HTTP_200_OK) json_test = json.loads(response.content) - self.assertEqual(len(json_test), 11) #JSON Attribute + self.assertEqual(len(json_test), 12) #JSON Attribute def test_account_details(self): url = reverse('user-details', kwargs={'email':'astraykai@gmail.com'}) response = self.client.get(url, format='json') self.assertEqual(response.status_code, status.HTTP_200_OK) json_test = json.loads(response.content) - self.assertEqual(len(json_test), 11) #JSON Attribute + self.assertEqual(len(json_test), 12) #JSON Attribute def test_account_list(self): url = reverse('user-list') @@ -206,5 +206,29 @@ class UserTests(APITestCase): json_response = json.loads(response.content) self.assertEqual(json_response['response'], 'File is not image type') + def test_all_user_information_can_be_seen_after_register(self): + ''' + Ensure that user information can be seen by others after registration + ''' + + url = reverse('create-user') + data = {'name': 'Astray', + 'email':'astrayyahoo@gmail.com', + 'password':'chingchenghanji', + 'phone_number':'08989221856', + 'tanggal_lahir':'1990-05-05', + 'jenis_kelamin':'Laki-laki', + 'disabilitas':'Yuhu', + 'pekerjaan':'Mahasiswa', + 'alamat':'Alamat Palsu', + 'is_active': True} + response = self.client.post(url, data) + + url = reverse('user-details', kwargs={'email':'astrayyahoo@gmail.com'}) + response = self.client.get(url, format='json') + json_test = json.loads(response.content) + print(json_test) + self.assertEqual(json_test['seen'], True) + class InfoTests(APITestCase, URLPatternsTestCase): pass -- GitLab From b603ef70a7d143dfa3e7a6ec8a49628e66a711a7 Mon Sep 17 00:00:00 2001 From: "alvin.hariman" Date: Mon, 17 May 2021 02:34:18 +0700 Subject: [PATCH 96/97] [GREEN] Add seen attribute in edit profile --- new_rest_api/views.py | 8 ++------ registrasi/migrations/0006_bisagouser_seen.py | 18 ++++++++++++++++++ registrasi/models.py | 1 + registrasi/serializers.py | 1 + 4 files changed, 22 insertions(+), 6 deletions(-) create mode 100644 registrasi/migrations/0006_bisagouser_seen.py diff --git a/new_rest_api/views.py b/new_rest_api/views.py index aa8e35d..4e6ec62 100644 --- a/new_rest_api/views.py +++ b/new_rest_api/views.py @@ -84,10 +84,7 @@ def register_user(request): user.is_active = False user.save() data['user'] = user.pk - if request.FILES : - data['foto'] = request.FILES['foto'] - else : - data['foto'] = None + data['seen'] = True mail_subject = "Activate your account" message = render_to_string('acc_active_email.html', { 'user' : user, @@ -152,9 +149,8 @@ def update_user(request): return JsonResponse({'response': 'File is not image type'}, safe=False, status=status.INTERNAL_SERVER_ERROR) user = User.objects.get(username=email) + data["user"] = user.pk bisa_go_user = BisaGoUser.objects.get(user=user) - user.last_name = name - data['user'] = user.pk data_query_dict = QueryDict('', mutable=True) data_query_dict.update(data) serializer = BisaGoUserSerializers(bisa_go_user, data=data_query_dict) diff --git a/registrasi/migrations/0006_bisagouser_seen.py b/registrasi/migrations/0006_bisagouser_seen.py new file mode 100644 index 0000000..1884243 --- /dev/null +++ b/registrasi/migrations/0006_bisagouser_seen.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.2 on 2021-05-10 20:25 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('registrasi', '0005_bisagouser_foto'), + ] + + operations = [ + migrations.AddField( + model_name='bisagouser', + name='seen', + field=models.BooleanField(blank=True, default=True, verbose_name='Seen'), + ), + ] diff --git a/registrasi/models.py b/registrasi/models.py index 67a54c8..cadf158 100644 --- a/registrasi/models.py +++ b/registrasi/models.py @@ -14,5 +14,6 @@ class BisaGoUser(models.Model): pekerjaan = models.CharField('Pekerjaan', max_length=64, default='-') alamat = models.CharField('Alamat', max_length=255, default='-') foto = models.ImageField('Foto', blank=True, default='-', null=True) + seen = models.BooleanField('Seen', blank=True, default=True) def __str__(self): return self.user.username \ No newline at end of file diff --git a/registrasi/serializers.py b/registrasi/serializers.py index 01a62c4..addcd1d 100644 --- a/registrasi/serializers.py +++ b/registrasi/serializers.py @@ -14,6 +14,7 @@ class BisaGoUserSerializers(serializers.ModelSerializer): 'pekerjaan', 'alamat', 'foto', + 'seen', ) extra_kwargs = { 'foto': {'required': False}, -- GitLab From 8702134cf6fd117324dfeb713987010237432a9f Mon Sep 17 00:00:00 2001 From: ariqbasyar Date: Mon, 17 May 2021 11:47:35 +0700 Subject: [PATCH 97/97] [CHORE] fix migrations error --- registrasi/migrations/0007_merge_20210517_0447.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 registrasi/migrations/0007_merge_20210517_0447.py diff --git a/registrasi/migrations/0007_merge_20210517_0447.py b/registrasi/migrations/0007_merge_20210517_0447.py new file mode 100644 index 0000000..5760767 --- /dev/null +++ b/registrasi/migrations/0007_merge_20210517_0447.py @@ -0,0 +1,14 @@ +# Generated by Django 3.1.7 on 2021-05-17 04:47 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('registrasi', '0006_bisagouser_seen'), + ('registrasi', '0006_merge_20210516_1820'), + ] + + operations = [ + ] -- GitLab