From b67061f5f8cab09fd38077813de15c04ec0d55ba Mon Sep 17 00:00:00 2001
From: addffa <adli.daffa5@gmail.com>
Date: Thu, 16 Jul 2020 10:48:30 +0700
Subject: [PATCH 01/41] membuat class mustahik

---
 sizakat/apps/mustahik/models.py | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/sizakat/apps/mustahik/models.py b/sizakat/apps/mustahik/models.py
index fd18c6e..a85d999 100644
--- a/sizakat/apps/mustahik/models.py
+++ b/sizakat/apps/mustahik/models.py
@@ -1,3 +1,7 @@
 from django.db import models
 
 # Create your models here.
+
+
+class Mustahik(models.Model):
+    pass
-- 
GitLab


From 265a05bfdfd94e2570f9f68f4ede2e7b29dca42f Mon Sep 17 00:00:00 2001
From: addffa <adli.daffa5@gmail.com>
Date: Thu, 16 Jul 2020 10:54:16 +0700
Subject: [PATCH 02/41] [RED] membuat test_mustahik_model

---
 sizakat/apps/mustahik/tests.py | 25 ++++++++++++++++++++++++-
 1 file changed, 24 insertions(+), 1 deletion(-)

diff --git a/sizakat/apps/mustahik/tests.py b/sizakat/apps/mustahik/tests.py
index de8bdc0..4997f5a 100644
--- a/sizakat/apps/mustahik/tests.py
+++ b/sizakat/apps/mustahik/tests.py
@@ -1,3 +1,26 @@
+from datetime import date
 from django.test import TestCase
 
-# Create your tests here.
+from .models import Mustahik
+
+
+class MustahikModelTestCase(TestCase):
+    def setUp(self):
+        Mustahik.objects.create(
+            name='mustahik',
+            no_ktp='31751234567890',
+            phone='081234567890',
+            address='Jalan raya depok',
+            province='Jawa Barat',
+            regency='Depok',
+            rt='003',
+            rw='002',
+            birthdate=date(1987, 6, 5),
+            status=Mustahik.Status.MISKIN,
+            family_size=4,
+            description='desc'
+        )
+
+    def test_mustahik_creation(self):
+        mustahik = Mustahik.objects.get(no_ktp='31751234567890')
+        self.assertTrue(isinstance(mustahik, Mustahik))
-- 
GitLab


From acdd7f6dd78a82497b4247c070ad2b0d85c2d97d Mon Sep 17 00:00:00 2001
From: addffa <adli.daffa5@gmail.com>
Date: Thu, 16 Jul 2020 11:18:34 +0700
Subject: [PATCH 03/41] [GREEN] implementasi model mustahik

---
 sizakat/apps/mustahik/models.py | 23 ++++++++++++++++++++++-
 1 file changed, 22 insertions(+), 1 deletion(-)

diff --git a/sizakat/apps/mustahik/models.py b/sizakat/apps/mustahik/models.py
index a85d999..81316b7 100644
--- a/sizakat/apps/mustahik/models.py
+++ b/sizakat/apps/mustahik/models.py
@@ -4,4 +4,25 @@ from django.db import models
 
 
 class Mustahik(models.Model):
-    pass
+
+    class Status(models.TextChoices):
+        JANDA = ('JANDA', 'Janda')
+        MISKIN = ('MISKIN', 'Miskin')
+        YATIM = ('YATIM', 'Yatim')
+
+    name = models.CharField(max_length=32)
+    no_ktp = models.CharField(max_length=32)
+    phone = models.CharField(max_length=32, blank=True)
+    address = models.TextField()
+    province = models.CharField(max_length=32)
+    regency = models.CharField(max_length=32)
+    rt = models.CharField(max_length=4)
+    rw = models.CharField(max_length=4)
+    birthdate = models.DateField()
+    status = models.CharField(
+        max_length=32,
+        choices=Status.choices,
+        default=Status.MISKIN,
+    )
+    family_size = models.PositiveSmallIntegerField()
+    description = models.TextField()
-- 
GitLab


From c6fa6cb0fa624a98e12991ce3cea832f37bf4166 Mon Sep 17 00:00:00 2001
From: addffa <adli.daffa5@gmail.com>
Date: Thu, 16 Jul 2020 11:19:31 +0700
Subject: [PATCH 04/41] [CHORES] penambahan app mustahik

---
 sizakat/settings.py | 1 +
 1 file changed, 1 insertion(+)

diff --git a/sizakat/settings.py b/sizakat/settings.py
index f2caf3f..0d410ec 100644
--- a/sizakat/settings.py
+++ b/sizakat/settings.py
@@ -38,6 +38,7 @@ INSTALLED_APPS = [
     'django.contrib.messages',
     'django.contrib.staticfiles',
     'graphene_django',
+    'sizakat.apps.mustahik'
 ]
 
 GRAPHENE = {
-- 
GitLab


From 1c941264e8d14219da069a3fd9d4b38363d94646 Mon Sep 17 00:00:00 2001
From: addffa <adli.daffa5@gmail.com>
Date: Thu, 16 Jul 2020 13:59:34 +0700
Subject: [PATCH 05/41] [RED] membuat test_no_ktp_mustahik_is_unique

---
 sizakat/apps/mustahik/tests.py | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/sizakat/apps/mustahik/tests.py b/sizakat/apps/mustahik/tests.py
index 4997f5a..1dffa8b 100644
--- a/sizakat/apps/mustahik/tests.py
+++ b/sizakat/apps/mustahik/tests.py
@@ -1,4 +1,5 @@
 from datetime import date
+from django.db.utils import IntegrityError
 from django.test import TestCase
 
 from .models import Mustahik
@@ -24,3 +25,20 @@ class MustahikModelTestCase(TestCase):
     def test_mustahik_creation(self):
         mustahik = Mustahik.objects.get(no_ktp='31751234567890')
         self.assertTrue(isinstance(mustahik, Mustahik))
+
+    def test_no_ktp_mustahik_is_unique(self):
+        with self.assertRaises(IntegrityError):
+            Mustahik.objects.create(
+                name='kihatsum',
+                no_ktp='31751234567890',
+                phone='08987654321',
+                address='Jalan raya bogor',
+                province='Jawa Barat',
+                regency='Bogor',
+                rt='002',
+                rw='003',
+                birthdate=date(1987, 4, 3),
+                status=Mustahik.Status.MISKIN,
+                family_size=1,
+                description='no_ktp is unique'
+            )
-- 
GitLab


From bcfc1ad825fdab651588cd264170ff73ba06e2bd Mon Sep 17 00:00:00 2001
From: addffa <adli.daffa5@gmail.com>
Date: Thu, 16 Jul 2020 14:00:19 +0700
Subject: [PATCH 06/41] [GREEN] menambahkan constraint unique pada attribute
 no_ktp

---
 sizakat/apps/mustahik/models.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/sizakat/apps/mustahik/models.py b/sizakat/apps/mustahik/models.py
index 81316b7..0468613 100644
--- a/sizakat/apps/mustahik/models.py
+++ b/sizakat/apps/mustahik/models.py
@@ -11,7 +11,7 @@ class Mustahik(models.Model):
         YATIM = ('YATIM', 'Yatim')
 
     name = models.CharField(max_length=32)
-    no_ktp = models.CharField(max_length=32)
+    no_ktp = models.CharField(max_length=32, unique=True)
     phone = models.CharField(max_length=32, blank=True)
     address = models.TextField()
     province = models.CharField(max_length=32)
-- 
GitLab


From 083aa62d6b4b42b56275eebcdf18c3f629a13959 Mon Sep 17 00:00:00 2001
From: addffa <adli.daffa5@gmail.com>
Date: Fri, 17 Jul 2020 09:41:06 +0700
Subject: [PATCH 07/41] [RED] membuat test mutasi tambah dan ubah mustahik

---
 sizakat/apps/mustahik/tests.py | 117 +++++++++++++++++++++++++++++++++
 1 file changed, 117 insertions(+)

diff --git a/sizakat/apps/mustahik/tests.py b/sizakat/apps/mustahik/tests.py
index 1dffa8b..6b0c259 100644
--- a/sizakat/apps/mustahik/tests.py
+++ b/sizakat/apps/mustahik/tests.py
@@ -1,6 +1,10 @@
+import json
+
 from datetime import date
 from django.db.utils import IntegrityError
 from django.test import TestCase
+from graphene_django.utils.testing import GraphQLTestCase
+from sizakat.schema import schema
 
 from .models import Mustahik
 
@@ -42,3 +46,116 @@ class MustahikModelTestCase(TestCase):
                 family_size=1,
                 description='no_ktp is unique'
             )
+
+
+class MustahikGraphQLTestCase(GraphQLTestCase):
+    GRAPHQL_SCHEMA = schema
+
+    def setUp(self):
+        Mustahik.objects.create(
+            name='mustahik',
+            no_ktp='31751234567890',
+            phone='081234567890',
+            address='Jalan raya depok',
+            province='Jawa Barat',
+            regency='Depok',
+            rt='003',
+            rw='002',
+            birthdate=date(1987, 6, 5),
+            status=Mustahik.Status.MISKIN,
+            family_size=4,
+            description='desc'
+        )
+
+    def test_mustahik_mutation_can_add_new_mustahik(self):
+        no_ktp = '123891210121'
+        response = self.query(
+            '''
+            mutation mustahikMutation($input: MustahikMutationInput!) {
+                mustahikMutation(input: $input) {
+                    mustahik {
+                        id
+                        name
+                        noKtp
+                        status
+                        description
+                    }
+                    errors {
+                        field
+                        messages
+                    }
+                }
+            }
+            ''',
+            op_name='mustahikMutation',
+            input_data={
+                "name": "jumat",
+                "noKtp": no_ktp,
+                "phone": "02132132180",
+                "address": "jalan swadaya",
+                "province": "jakarta",
+                "regency": "manggarai",
+                "rt": "001",
+                "rw": "001",
+                "birthdate": "1998-03-12",
+                "status": "YATIM",
+                "familySize": 3,
+                "description": "anak yatim"
+            }
+        )
+
+        # This validates the status code and if you get errors
+        self.assertResponseNoErrors(response)
+
+        # Validate content
+        content = json.loads(response.content)
+        self.assertEqual(content['data']['mustahikMutation']
+                         ['mustahik']['status'], 'YATIM')
+
+        # Validate success save to db
+        self.assertNotEqual(Mustahik.objects.count(), 0)
+        mustahik = Mustahik.objects.get(no_ktp=no_ktp)
+        self.assertEqual(mustahik.name, 'jumat')
+
+    def test_mustahik_mutation_can_update_mustahik(self):
+        mustahik = Mustahik.objects.get(no_ktp='31751234567890')
+        mustahik_id = mustahik.pk
+        old_desc = mustahik.description
+        new_desc = 'keluarga tidak mampu'
+        response = self.query(
+            '''
+            mutation mustahikMutation($input: MustahikMutationInput!) {
+                mustahikMutation(input: $input) {
+                    mustahik {
+                        id
+                        description
+                    }
+                    errors {
+                        field
+                        messages
+                    }
+                }
+            }
+            ''',
+            op_name='mustahikMutation',
+            input_data={
+                "name": "mustahik",
+                "noKtp": "31751234567890",
+                "phone": "081234567890",
+                "address": "Jalan raya depok",
+                "province": "Jawa Barat",
+                "regency": "Depok",
+                "rt": "003",
+                "rw": "002",
+                "birthdate": "1987-06-05",
+                "status": "MISKIN",
+                "familySize": 4,
+                "description": new_desc,
+                "id": mustahik.pk
+            }
+        )
+
+        # Validate success update desc mustahik
+        mustahik = Mustahik.objects.get(no_ktp='31751234567890')
+        self.assertNotEqual(mustahik.description, old_desc)
+        self.assertEqual(mustahik.description, new_desc)
-- 
GitLab


From 493026c6c0f2d84fc8bff353a13ebd125a5159b9 Mon Sep 17 00:00:00 2001
From: addffa <adli.daffa5@gmail.com>
Date: Fri, 17 Jul 2020 09:41:53 +0700
Subject: [PATCH 08/41] [GREEN] membuat mutasi tambah dan ubah mustahik

---
 sizakat/apps/mustahik/models.py    |  4 ++--
 sizakat/apps/mustahik/mutations.py | 35 ++++++++++++++++++++++++++++++
 sizakat/apps/mustahik/types.py     | 10 +++++++++
 sizakat/schema.py                  | 12 ++++++++--
 4 files changed, 57 insertions(+), 4 deletions(-)
 create mode 100644 sizakat/apps/mustahik/mutations.py
 create mode 100644 sizakat/apps/mustahik/types.py

diff --git a/sizakat/apps/mustahik/models.py b/sizakat/apps/mustahik/models.py
index 0468613..267fdaa 100644
--- a/sizakat/apps/mustahik/models.py
+++ b/sizakat/apps/mustahik/models.py
@@ -12,7 +12,7 @@ class Mustahik(models.Model):
 
     name = models.CharField(max_length=32)
     no_ktp = models.CharField(max_length=32, unique=True)
-    phone = models.CharField(max_length=32, blank=True)
+    phone = models.CharField(max_length=32, blank=True, null=True)
     address = models.TextField()
     province = models.CharField(max_length=32)
     regency = models.CharField(max_length=32)
@@ -25,4 +25,4 @@ class Mustahik(models.Model):
         default=Status.MISKIN,
     )
     family_size = models.PositiveSmallIntegerField()
-    description = models.TextField()
+    description = models.TextField(blank=True, null=True)
diff --git a/sizakat/apps/mustahik/mutations.py b/sizakat/apps/mustahik/mutations.py
new file mode 100644
index 0000000..ee12875
--- /dev/null
+++ b/sizakat/apps/mustahik/mutations.py
@@ -0,0 +1,35 @@
+import graphene
+
+from django import forms
+from graphene_django.forms.mutation import DjangoModelFormMutation
+
+from .models import Mustahik
+from .types import MustahikType
+
+
+class MustahikForm(forms.ModelForm):
+
+    class Meta:
+        model = Mustahik
+        fields = [
+            'name',
+            'no_ktp',
+            'phone',
+            'address',
+            'province',
+            'regency',
+            'rt',
+            'rw',
+            'birthdate',
+            'status',
+            'family_size',
+            'description',
+        ]
+
+
+class MustahikMutation(DjangoModelFormMutation):
+
+    mustahik = graphene.Field(MustahikType)
+
+    class Meta:
+        form_class = MustahikForm
diff --git a/sizakat/apps/mustahik/types.py b/sizakat/apps/mustahik/types.py
new file mode 100644
index 0000000..cf9dba9
--- /dev/null
+++ b/sizakat/apps/mustahik/types.py
@@ -0,0 +1,10 @@
+import graphene
+
+from graphene_django.types import DjangoObjectType
+from .models import Mustahik
+
+
+class MustahikType(DjangoObjectType):
+
+    class Meta:
+        model = Mustahik
diff --git a/sizakat/schema.py b/sizakat/schema.py
index 642be04..3737a29 100644
--- a/sizakat/schema.py
+++ b/sizakat/schema.py
@@ -1,14 +1,22 @@
-from graphene_django import DjangoObjectType
 import graphene
 
+from graphene_django import DjangoObjectType
+from .apps.mustahik.mutations import MustahikMutation
+
 ABOUT = 'Si Zakat merupakan sistem informasi untuk membantu masjid dalam \
 mengelola transaksi zakat. Sistem ini dibuat oleh tim lab 1231, \
 yang dipimpin oleh Prof. Dr. Wisnu Jatmiko.'
 
+
 class Query(graphene.ObjectType):
     about = graphene.String()
 
     def resolve_about(self, info):
         return ABOUT
 
-schema = graphene.Schema(query=Query)
+
+class Mutation(graphene.ObjectType):
+    mustahik_mutation = MustahikMutation.Field()
+
+
+schema = graphene.Schema(query=Query, mutation=Mutation)
-- 
GitLab


From c5737f806f0f98dacd2ebb2165279bf2d720414f Mon Sep 17 00:00:00 2001
From: Naufal Alauddin Hilmi <naufalauddin@gmail.com>
Date: Fri, 17 Jul 2020 17:25:21 +0700
Subject: [PATCH 09/41] [RED] test for mustahik filter query

---
 sizakat/apps/mustahik/tests.py | 74 ++++++++++++++++++++++++++++++++++
 1 file changed, 74 insertions(+)

diff --git a/sizakat/apps/mustahik/tests.py b/sizakat/apps/mustahik/tests.py
index 6b0c259..7bfe64c 100644
--- a/sizakat/apps/mustahik/tests.py
+++ b/sizakat/apps/mustahik/tests.py
@@ -159,3 +159,77 @@ class MustahikGraphQLTestCase(GraphQLTestCase):
         mustahik = Mustahik.objects.get(no_ktp='31751234567890')
         self.assertNotEqual(mustahik.description, old_desc)
         self.assertEqual(mustahik.description, new_desc)
+
+    def test_query_mustahik_should_return_list_of_mustahiks(self):
+        response = self.query(
+        '''
+            query mustahiksQuery{
+                mustahiks {
+                    id,
+                    name
+                }
+            }
+        ''',
+        op_name='mustahiksQuery'
+        )
+
+        self.assertResponseNoErrors(response)
+
+        content = json.loads(response.content)
+        self.assertEqual(len(content['data']['mustahiks']), 1)
+        self.assertEqual(content['data']['mustahiks'][0]['name'], 'mustahik')
+
+    def test_query_mustahiks_if_statuses_is_set_should_return_list_of_mustahiks_with_coresponding_status(self):
+        Mustahik.objects.create(
+            name='test',
+            no_ktp='11751234567890',
+            phone='081234567890',
+            address='Jalan raya depok',
+            province='Jawa Barat',
+            regency='Depok',
+            rt='003',
+            rw='002',
+            birthdate=date(1987, 6, 5),
+            status=Mustahik.Status.YATIM,
+            family_size=4,
+            description='desc'
+        )
+
+        response = self.query(
+            '''
+                query mustahiksQuery($statuses: [String]) {
+                    mustahiks(statuses: $statuses) {
+                        id,
+                        name,
+                        status
+                    }
+                }
+            ''',
+            op_name='mustahiksQuery',
+            variables={'statuses': [Mustahik.Status.MISKIN]}
+        )
+
+        content = json.loads(response.content)
+        self.assertEqual(len(content['data']['mustahiks']), 1)
+
+        for mustahik in content['data']['mustahiks']:
+            self.assertEqual(mustahik['status'], Mustahik.Status.MISKIN)
+
+    def test_query_mustahiks_if_statuses_is_provided_and_no_mustahiks_are_qualified_it_should_return_empty_list(self):
+        response = self.query(
+            '''
+                query mustahiksQuery($statuses: [String]) {
+                    mustahiks(statuses: $statuses) {
+                        id,
+                        name,
+                        status
+                    }
+                }
+            ''',
+            op_name='mustahiksQuery',
+            variables={'statuses': [Mustahik.Status.JANDA]}
+        )
+
+        content = json.loads(response.content)
+        self.assertEqual(len(content['data']['mustahiks']), 0)
+
-- 
GitLab


From fd19d39d3d7125c1b7232db07e94b6f58f477769 Mon Sep 17 00:00:00 2001
From: Naufal Alauddin Hilmi <naufalauddin@gmail.com>
Date: Fri, 17 Jul 2020 17:27:13 +0700
Subject: [PATCH 10/41] [GREEN] create query for filter and get mustahik

---
 sizakat/apps/mustahik/query.py | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)
 create mode 100644 sizakat/apps/mustahik/query.py

diff --git a/sizakat/apps/mustahik/query.py b/sizakat/apps/mustahik/query.py
new file mode 100644
index 0000000..70df3e5
--- /dev/null
+++ b/sizakat/apps/mustahik/query.py
@@ -0,0 +1,17 @@
+import graphene
+from django.db.models import Q
+
+from .models import Mustahik
+from .types import MustahikType
+
+class MustahikQuery(graphene.ObjectType):
+    mustahiks = graphene.List(MustahikType, statuses=graphene.List(graphene.String))
+
+    def resolve_mustahiks(self, info, statuses=[], **kwargs):
+        if statuses and len(statuses) > 0:
+            filter = Q(status=statuses[0])
+            for status in statuses:
+                filter = filter | Q(status=status)
+            return Mustahik.objects.filter(filter)
+
+        return Mustahik.objects.all()
-- 
GitLab


From a3a0f3f9fc40e71c1f526b3d680ef815ba77d9a3 Mon Sep 17 00:00:00 2001
From: Naufal Alauddin Hilmi <naufalauddin@gmail.com>
Date: Fri, 17 Jul 2020 17:28:23 +0700
Subject: [PATCH 11/41] [GREEN] add Mustahik query to the schema

---
 sizakat/schema.py | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/sizakat/schema.py b/sizakat/schema.py
index 3737a29..93b39bb 100644
--- a/sizakat/schema.py
+++ b/sizakat/schema.py
@@ -2,13 +2,14 @@ import graphene
 
 from graphene_django import DjangoObjectType
 from .apps.mustahik.mutations import MustahikMutation
+from .apps.mustahik.query import MustahikQuery
 
 ABOUT = 'Si Zakat merupakan sistem informasi untuk membantu masjid dalam \
 mengelola transaksi zakat. Sistem ini dibuat oleh tim lab 1231, \
 yang dipimpin oleh Prof. Dr. Wisnu Jatmiko.'
 
 
-class Query(graphene.ObjectType):
+class Query(MustahikQuery, graphene.ObjectType):
     about = graphene.String()
 
     def resolve_about(self, info):
-- 
GitLab


From 3c759810210a041bccc5bb5963437ffc1be504a1 Mon Sep 17 00:00:00 2001
From: addffa <adli.daffa5@gmail.com>
Date: Fri, 17 Jul 2020 20:16:40 +0700
Subject: [PATCH 12/41] [CHORES] update cors in settings and fix path
 migrations in .gitignore

---
 .gitignore          | 2 +-
 sizakat/settings.py | 4 ++++
 2 files changed, 5 insertions(+), 1 deletion(-)

diff --git a/.gitignore b/.gitignore
index db16b24..31d9ad9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -67,7 +67,7 @@ coverage.xml
 staticfiles/
 
 # migrations file
-*/migrations/
+/**/migrations/
 
 # Jupyter Notebook 
 .ipynb_checkpoints 
diff --git a/sizakat/settings.py b/sizakat/settings.py
index 0d410ec..105329d 100644
--- a/sizakat/settings.py
+++ b/sizakat/settings.py
@@ -38,6 +38,7 @@ INSTALLED_APPS = [
     'django.contrib.messages',
     'django.contrib.staticfiles',
     'graphene_django',
+    'corsheaders',
     'sizakat.apps.mustahik'
 ]
 
@@ -48,6 +49,7 @@ GRAPHENE = {
 MIDDLEWARE = [
     'django.middleware.security.SecurityMiddleware',
     'django.contrib.sessions.middleware.SessionMiddleware',
+    'corsheaders.middleware.CorsMiddleware',
     'django.middleware.common.CommonMiddleware',
     'django.middleware.csrf.CsrfViewMiddleware',
     'django.contrib.auth.middleware.AuthenticationMiddleware',
@@ -55,6 +57,8 @@ MIDDLEWARE = [
     'django.middleware.clickjacking.XFrameOptionsMiddleware',
 ]
 
+CORS_ORIGIN_WHITELIST = os.environ.get('CORS_ORIGIN_WHITELIST').split()
+
 ROOT_URLCONF = 'sizakat.urls'
 
 TEMPLATES = [
-- 
GitLab


From d8009e5fbd49b5f3f5338e2be26d73c62091f4a9 Mon Sep 17 00:00:00 2001
From: addffa <adli.daffa5@gmail.com>
Date: Fri, 17 Jul 2020 20:21:08 +0700
Subject: [PATCH 13/41] [REFACTOR] change django apps structure

---
 sizakat/apps/mustahik/migrations/__init__.py | 0
 sizakat/{apps => }/mustahik/__init__.py      | 0
 sizakat/{apps => }/mustahik/admin.py         | 0
 sizakat/{apps => }/mustahik/apps.py          | 0
 sizakat/{apps => }/mustahik/models.py        | 0
 sizakat/{apps => }/mustahik/mutations.py     | 0
 sizakat/{apps => }/mustahik/query.py         | 0
 sizakat/{apps => }/mustahik/tests.py         | 0
 sizakat/{apps => }/mustahik/types.py         | 0
 sizakat/schema.py                            | 5 +++--
 sizakat/settings.py                          | 2 +-
 11 files changed, 4 insertions(+), 3 deletions(-)
 delete mode 100644 sizakat/apps/mustahik/migrations/__init__.py
 rename sizakat/{apps => }/mustahik/__init__.py (100%)
 rename sizakat/{apps => }/mustahik/admin.py (100%)
 rename sizakat/{apps => }/mustahik/apps.py (100%)
 rename sizakat/{apps => }/mustahik/models.py (100%)
 rename sizakat/{apps => }/mustahik/mutations.py (100%)
 rename sizakat/{apps => }/mustahik/query.py (100%)
 rename sizakat/{apps => }/mustahik/tests.py (100%)
 rename sizakat/{apps => }/mustahik/types.py (100%)

diff --git a/sizakat/apps/mustahik/migrations/__init__.py b/sizakat/apps/mustahik/migrations/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/sizakat/apps/mustahik/__init__.py b/sizakat/mustahik/__init__.py
similarity index 100%
rename from sizakat/apps/mustahik/__init__.py
rename to sizakat/mustahik/__init__.py
diff --git a/sizakat/apps/mustahik/admin.py b/sizakat/mustahik/admin.py
similarity index 100%
rename from sizakat/apps/mustahik/admin.py
rename to sizakat/mustahik/admin.py
diff --git a/sizakat/apps/mustahik/apps.py b/sizakat/mustahik/apps.py
similarity index 100%
rename from sizakat/apps/mustahik/apps.py
rename to sizakat/mustahik/apps.py
diff --git a/sizakat/apps/mustahik/models.py b/sizakat/mustahik/models.py
similarity index 100%
rename from sizakat/apps/mustahik/models.py
rename to sizakat/mustahik/models.py
diff --git a/sizakat/apps/mustahik/mutations.py b/sizakat/mustahik/mutations.py
similarity index 100%
rename from sizakat/apps/mustahik/mutations.py
rename to sizakat/mustahik/mutations.py
diff --git a/sizakat/apps/mustahik/query.py b/sizakat/mustahik/query.py
similarity index 100%
rename from sizakat/apps/mustahik/query.py
rename to sizakat/mustahik/query.py
diff --git a/sizakat/apps/mustahik/tests.py b/sizakat/mustahik/tests.py
similarity index 100%
rename from sizakat/apps/mustahik/tests.py
rename to sizakat/mustahik/tests.py
diff --git a/sizakat/apps/mustahik/types.py b/sizakat/mustahik/types.py
similarity index 100%
rename from sizakat/apps/mustahik/types.py
rename to sizakat/mustahik/types.py
diff --git a/sizakat/schema.py b/sizakat/schema.py
index 93b39bb..f287b17 100644
--- a/sizakat/schema.py
+++ b/sizakat/schema.py
@@ -1,8 +1,9 @@
 import graphene
 
 from graphene_django import DjangoObjectType
-from .apps.mustahik.mutations import MustahikMutation
-from .apps.mustahik.query import MustahikQuery
+
+from .mustahik.mutations import MustahikMutation
+from .mustahik.query import MustahikQuery
 
 ABOUT = 'Si Zakat merupakan sistem informasi untuk membantu masjid dalam \
 mengelola transaksi zakat. Sistem ini dibuat oleh tim lab 1231, \
diff --git a/sizakat/settings.py b/sizakat/settings.py
index 105329d..1658e6d 100644
--- a/sizakat/settings.py
+++ b/sizakat/settings.py
@@ -39,7 +39,7 @@ INSTALLED_APPS = [
     'django.contrib.staticfiles',
     'graphene_django',
     'corsheaders',
-    'sizakat.apps.mustahik'
+    'sizakat.mustahik'
 ]
 
 GRAPHENE = {
-- 
GitLab


From 549622a50ab2289d5c86ff48081581bf18d88395 Mon Sep 17 00:00:00 2001
From: Ilma Ainur Rohma <ilma13rohma@gmail.com>
Date: Sat, 18 Jul 2020 14:42:00 +0700
Subject: [PATCH 14/41] [RED] add test for read detail and delete

---
 sizakat/mustahik/mutations.py |  2 +-
 sizakat/mustahik/query.py     |  1 +
 sizakat/mustahik/tests.py     | 52 +++++++++++++++++++++++++++++++++++
 3 files changed, 54 insertions(+), 1 deletion(-)

diff --git a/sizakat/mustahik/mutations.py b/sizakat/mustahik/mutations.py
index ee12875..62c6266 100644
--- a/sizakat/mustahik/mutations.py
+++ b/sizakat/mustahik/mutations.py
@@ -32,4 +32,4 @@ class MustahikMutation(DjangoModelFormMutation):
     mustahik = graphene.Field(MustahikType)
 
     class Meta:
-        form_class = MustahikForm
+        form_class = MustahikForm
\ No newline at end of file
diff --git a/sizakat/mustahik/query.py b/sizakat/mustahik/query.py
index 70df3e5..57a1583 100644
--- a/sizakat/mustahik/query.py
+++ b/sizakat/mustahik/query.py
@@ -15,3 +15,4 @@ class MustahikQuery(graphene.ObjectType):
             return Mustahik.objects.filter(filter)
 
         return Mustahik.objects.all()
+
diff --git a/sizakat/mustahik/tests.py b/sizakat/mustahik/tests.py
index 7bfe64c..54d63c6 100644
--- a/sizakat/mustahik/tests.py
+++ b/sizakat/mustahik/tests.py
@@ -233,3 +233,55 @@ class MustahikGraphQLTestCase(GraphQLTestCase):
         content = json.loads(response.content)
         self.assertEqual(len(content['data']['mustahiks']), 0)
 
+    def test_mustahik_mutation_can_delete_mustahik(self):
+        count = Mustahik.objects.count()
+        response = self.query(
+            '''
+                mutation{
+                    deleteMustahik(id: 3) {
+                        mustahik {
+                            id
+                            name
+                        }
+                    }
+                }
+            ''',
+        )
+        self.assertResponseNoErrors(response)
+
+        content = json.loads(response.content)
+        self.assertIsNone(content['data']['deleteMustahik'])
+        self.assertEquals(Mustahik.objects.count(), count-1)
+
+    def test_mustahik_query_can_read_detail_mustahik(self):
+        mustahik = Mustahik.objects.get(no_ktp='31751234567890')
+        mustahik_id = mustahik.pk
+        response = self.query(
+            '''
+                query detailMustahikQuery($id:ID){
+                    mustahik(id:$id){
+                        id
+                        name
+                        noKtp
+                        phone
+                        address
+                        province
+                        regency
+                        rt
+                        rw
+                        birthdate
+                        status
+                        familySize
+                        description
+                    }
+                }
+            ''',
+            op_name='detailMustahikQuery',
+            variables={'id':mustahik_id}
+        )
+        content = json.loads(response.content)
+        self.assertEqual(len(content['data']), 1)
+        self.assertEqual(content['data']['mustahik']['name'], 'mustahik')
+        self.assertEqual(content['data']['mustahik']['noKtp'], '31751234567890')
+        self.assertEqual(content['data']['mustahik']['address'], 'Jalan raya depok')
+        
\ No newline at end of file
-- 
GitLab


From db50eef5e695eab538d731fc11cd1762ca015fd1 Mon Sep 17 00:00:00 2001
From: Ilma Ainur Rohma <ilma13rohma@gmail.com>
Date: Sat, 18 Jul 2020 15:02:56 +0700
Subject: [PATCH 15/41] [GREEN] add read detail query and delete mutation

---
 sizakat/mustahik/mutations.py | 13 ++++++++++++-
 sizakat/mustahik/query.py     |  6 ++++++
 sizakat/mustahik/tests.py     |  2 ++
 sizakat/schema.py             |  3 ++-
 4 files changed, 22 insertions(+), 2 deletions(-)

diff --git a/sizakat/mustahik/mutations.py b/sizakat/mustahik/mutations.py
index 62c6266..4cf800b 100644
--- a/sizakat/mustahik/mutations.py
+++ b/sizakat/mustahik/mutations.py
@@ -32,4 +32,15 @@ class MustahikMutation(DjangoModelFormMutation):
     mustahik = graphene.Field(MustahikType)
 
     class Meta:
-        form_class = MustahikForm
\ No newline at end of file
+        form_class = MustahikForm
+
+class DeleteMustahik(graphene.Mutation): 
+    class Arguments:
+        id = graphene.ID()
+    
+    mustahik = graphene.Field(MustahikType)
+
+    def mutate(self, info, id):
+        mustahik = Mustahik.objects.get(pk=id)
+        if mustahik is not None:
+            mustahik.delete()
diff --git a/sizakat/mustahik/query.py b/sizakat/mustahik/query.py
index 57a1583..be7accf 100644
--- a/sizakat/mustahik/query.py
+++ b/sizakat/mustahik/query.py
@@ -6,6 +6,7 @@ from .types import MustahikType
 
 class MustahikQuery(graphene.ObjectType):
     mustahiks = graphene.List(MustahikType, statuses=graphene.List(graphene.String))
+    mustahik = graphene.Field(MustahikType, id=graphene.ID())
 
     def resolve_mustahiks(self, info, statuses=[], **kwargs):
         if statuses and len(statuses) > 0:
@@ -16,3 +17,8 @@ class MustahikQuery(graphene.ObjectType):
 
         return Mustahik.objects.all()
 
+    def resolve_mustahik(self, info, id):
+        mustahik = Mustahik.objects.get(pk=id)
+        if mustahik is not None:
+            return mustahik
+
diff --git a/sizakat/mustahik/tests.py b/sizakat/mustahik/tests.py
index 54d63c6..372a8d1 100644
--- a/sizakat/mustahik/tests.py
+++ b/sizakat/mustahik/tests.py
@@ -279,6 +279,8 @@ class MustahikGraphQLTestCase(GraphQLTestCase):
             op_name='detailMustahikQuery',
             variables={'id':mustahik_id}
         )
+        self.assertResponseNoErrors(response)
+        
         content = json.loads(response.content)
         self.assertEqual(len(content['data']), 1)
         self.assertEqual(content['data']['mustahik']['name'], 'mustahik')
diff --git a/sizakat/schema.py b/sizakat/schema.py
index f287b17..f7d47ba 100644
--- a/sizakat/schema.py
+++ b/sizakat/schema.py
@@ -2,7 +2,7 @@ import graphene
 
 from graphene_django import DjangoObjectType
 
-from .mustahik.mutations import MustahikMutation
+from .mustahik.mutations import MustahikMutation, DeleteMustahik
 from .mustahik.query import MustahikQuery
 
 ABOUT = 'Si Zakat merupakan sistem informasi untuk membantu masjid dalam \
@@ -19,6 +19,7 @@ class Query(MustahikQuery, graphene.ObjectType):
 
 class Mutation(graphene.ObjectType):
     mustahik_mutation = MustahikMutation.Field()
+    delete_mustahik = DeleteMustahik.Field()
 
 
 schema = graphene.Schema(query=Query, mutation=Mutation)
-- 
GitLab


From 847daff12f48b1c55edfcd41f4a7395085bb25d7 Mon Sep 17 00:00:00 2001
From: Ilma Ainur Rohma <ilma13rohma@gmail.com>
Date: Thu, 23 Jul 2020 15:40:41 +0700
Subject: [PATCH 16/41] [REFACTOR] add message in delete mutation

---
 sizakat/mustahik/mutations.py | 10 ++++++++--
 sizakat/mustahik/tests.py     |  7 ++++++-
 2 files changed, 14 insertions(+), 3 deletions(-)

diff --git a/sizakat/mustahik/mutations.py b/sizakat/mustahik/mutations.py
index 4cf800b..7d352ca 100644
--- a/sizakat/mustahik/mutations.py
+++ b/sizakat/mustahik/mutations.py
@@ -38,9 +38,15 @@ class DeleteMustahik(graphene.Mutation):
     class Arguments:
         id = graphene.ID()
     
+    message = graphene.String()
+    idMustahik = graphene.ID()
+    nama = graphene.String()
+    noKtp = graphene.String()
     mustahik = graphene.Field(MustahikType)
 
     def mutate(self, info, id):
         mustahik = Mustahik.objects.get(pk=id)
-        if mustahik is not None:
-            mustahik.delete()
+        _nama = mustahik.name
+        _no_ktp = mustahik.no_ktp
+        mustahik.delete()
+        return DeleteMustahik(message = "Success", idMustahik=id, nama=_nama, noKtp=_no_ktp)
\ No newline at end of file
diff --git a/sizakat/mustahik/tests.py b/sizakat/mustahik/tests.py
index 372a8d1..e87f6eb 100644
--- a/sizakat/mustahik/tests.py
+++ b/sizakat/mustahik/tests.py
@@ -243,6 +243,10 @@ class MustahikGraphQLTestCase(GraphQLTestCase):
                             id
                             name
                         }
+                        message
+                        idMustahik
+                        nama
+                        noKtp
                     }
                 }
             ''',
@@ -250,7 +254,8 @@ class MustahikGraphQLTestCase(GraphQLTestCase):
         self.assertResponseNoErrors(response)
 
         content = json.loads(response.content)
-        self.assertIsNone(content['data']['deleteMustahik'])
+        self.assertIsNone(content['data']['deleteMustahik']['mustahik'])
+        self.assertEquals(content['data']['deleteMustahik']['message'], "Success")
         self.assertEquals(Mustahik.objects.count(), count-1)
 
     def test_mustahik_query_can_read_detail_mustahik(self):
-- 
GitLab


From d85ecd8be1d1a0f7b137179a3ab5d8b528792951 Mon Sep 17 00:00:00 2001
From: abdurrafiarief <abdurrafiarief@gmail.com>
Date: Fri, 24 Jul 2020 16:45:40 +0700
Subject: [PATCH 17/41] [RED] test for search mustahik with name

---
 sizakat/mustahik/mutations.py |  11 +++-
 sizakat/mustahik/query.py     |   7 +++
 sizakat/mustahik/tests.py     | 109 ++++++++++++++++++++++++++++++++--
 sizakat/settings.py           |   2 +-
 4 files changed, 122 insertions(+), 7 deletions(-)

diff --git a/sizakat/mustahik/mutations.py b/sizakat/mustahik/mutations.py
index 4cf800b..55fd8ef 100644
--- a/sizakat/mustahik/mutations.py
+++ b/sizakat/mustahik/mutations.py
@@ -38,9 +38,16 @@ class DeleteMustahik(graphene.Mutation):
     class Arguments:
         id = graphene.ID()
     
+    message = graphene.String()
+    idMustahik = graphene.ID()
+    nama = graphene.String()
+    noKtp = graphene.String()
     mustahik = graphene.Field(MustahikType)
 
     def mutate(self, info, id):
         mustahik = Mustahik.objects.get(pk=id)
-        if mustahik is not None:
-            mustahik.delete()
+        _nama = mustahik.name
+        _no_ktp = mustahik.no_ktp
+        mustahik.delete()
+        return DeleteMustahik(message = "Success", idMustahik=id, nama=_nama, noKtp=_no_ktp)
+
diff --git a/sizakat/mustahik/query.py b/sizakat/mustahik/query.py
index be7accf..be4165c 100644
--- a/sizakat/mustahik/query.py
+++ b/sizakat/mustahik/query.py
@@ -4,9 +4,11 @@ from django.db.models import Q
 from .models import Mustahik
 from .types import MustahikType
 
+
 class MustahikQuery(graphene.ObjectType):
     mustahiks = graphene.List(MustahikType, statuses=graphene.List(graphene.String))
     mustahik = graphene.Field(MustahikType, id=graphene.ID())
+    mustahikWithName = graphene.Field(lambda: graphene.List(MustahikType), name=graphene.String())
 
     def resolve_mustahiks(self, info, statuses=[], **kwargs):
         if statuses and len(statuses) > 0:
@@ -22,3 +24,8 @@ class MustahikQuery(graphene.ObjectType):
         if mustahik is not None:
             return mustahik
 
+    def resolve_mustahikWithName(self, info, name=""):
+        return
+        
+        
+ 
diff --git a/sizakat/mustahik/tests.py b/sizakat/mustahik/tests.py
index 372a8d1..ea9a47e 100644
--- a/sizakat/mustahik/tests.py
+++ b/sizakat/mustahik/tests.py
@@ -235,24 +235,35 @@ class MustahikGraphQLTestCase(GraphQLTestCase):
 
     def test_mustahik_mutation_can_delete_mustahik(self):
         count = Mustahik.objects.count()
+        mustahik = Mustahik.objects.get(no_ktp='31751234567890')
+        mustahik_id = mustahik.pk
         response = self.query(
             '''
-                mutation{
-                    deleteMustahik(id: 3) {
+                mutation deleteMustahik($id: ID) {
+                    deleteMustahik(id: $id) {
                         mustahik {
                             id
                             name
                         }
+                        message
+                        idMustahik
+                        nama
+                        noKtp
                     }
                 }
             ''',
+            op_name='deleteMustahik',
+            variables={'id':mustahik_id}
         )
+        
         self.assertResponseNoErrors(response)
 
         content = json.loads(response.content)
-        self.assertIsNone(content['data']['deleteMustahik'])
+        self.assertIsNone(content['data']['deleteMustahik']['mustahik'])
+        self.assertEquals(content['data']['deleteMustahik']['message'], "Success")
         self.assertEquals(Mustahik.objects.count(), count-1)
 
+
     def test_mustahik_query_can_read_detail_mustahik(self):
         mustahik = Mustahik.objects.get(no_ktp='31751234567890')
         mustahik_id = mustahik.pk
@@ -286,4 +297,94 @@ class MustahikGraphQLTestCase(GraphQLTestCase):
         self.assertEqual(content['data']['mustahik']['name'], 'mustahik')
         self.assertEqual(content['data']['mustahik']['noKtp'], '31751234567890')
         self.assertEqual(content['data']['mustahik']['address'], 'Jalan raya depok')
-        
\ No newline at end of file
+
+    def test_mustahikWithName_should_return_list_of_mustahik(self):
+        response = self.query(
+            '''
+            query mustahikWithName($name:String){
+                mustahikWithName(name: $name){
+                    id,
+                    name
+                }
+                
+            }
+            ''',
+            op_name='mustahikWithName',
+        )
+
+        content = json.loads(response.content)
+        self.assertEqual(len(content['data']['mustahikWithName']),1)
+        self.assertEqual(content['data']['mustahikWithName'][0]['name'], 'mustahik')
+
+    def test_mustahikWithName_if_name_is_set_should_return_list_of_mustahiks_with_contain_the_name(self):
+        Mustahik.objects.create(
+            name='test',
+            no_ktp='11751234567890',
+            phone='081234567890',
+            address='Jalan raya depok',
+            province='Jawa Barat',
+            regency='Depok',
+            rt='003',
+            rw='002',
+            birthdate=date(1987, 6, 5),
+            status=Mustahik.Status.YATIM,
+            family_size=4,
+            description='desc'
+        )
+
+        Mustahik.objects.create(
+            name='eslu',
+            no_ktp='22751234337899',
+            phone='081234567890',
+            address='Jalan depok',
+            province='Jawa Timur',
+            regency='Bondo',
+            rt='003',
+            rw='002',
+            birthdate=date(1987, 6, 5),
+            status=Mustahik.Status.YATIM,
+            family_size=4,
+            description='desc'
+        )
+
+        response = self.query(
+            '''
+            query mustahikWithName($name:String){
+                mustahikWithName(name: $name){
+                    id,
+                    name
+                }
+                
+            }
+            ''',
+            op_name='mustahikWithName',
+            variables={'name':'es'}
+        )
+
+        content = json.loads(response.content)
+        self.assertEqual(len(content['data']['mustahikWithName']), 2)
+        self.assertEqual(content['data']['mustahikWithName'][0]['name'], 'test')
+        self.assertEqual(content['data']['mustahikWithName'][1]['name'], 'eslu')
+
+    def test_mustahikWithName_if_name_is_not_available_should_return_empty_list(self):
+        response = self.query(
+            '''
+            query mustahikWithName($name:String){
+                mustahikWithName(name: $name){
+                    id,
+                    name
+                }
+                
+            }
+            ''',
+            op_name='mustahikWithName',
+            variables={'name':'#'}
+        )
+
+        content = json.loads(response.content)
+        self.assertEqual(len(content['data']['mustahikWithName']), 0)
+        
+
+
+
+
diff --git a/sizakat/settings.py b/sizakat/settings.py
index 1658e6d..6ede7a0 100644
--- a/sizakat/settings.py
+++ b/sizakat/settings.py
@@ -39,7 +39,7 @@ INSTALLED_APPS = [
     'django.contrib.staticfiles',
     'graphene_django',
     'corsheaders',
-    'sizakat.mustahik'
+    'sizakat.mustahik',
 ]
 
 GRAPHENE = {
-- 
GitLab


From 97b8548393dc655cc38008c2aedab9c187fcabec Mon Sep 17 00:00:00 2001
From: abdurrafiarief <abdurrafiarief@gmail.com>
Date: Fri, 24 Jul 2020 16:47:51 +0700
Subject: [PATCH 18/41] [GREEN] complete task for search mustahik with name

---
 sizakat/mustahik/query.py | 4 +++-
 sizakat/mustahik/tests.py | 2 +-
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/sizakat/mustahik/query.py b/sizakat/mustahik/query.py
index be4165c..f78221c 100644
--- a/sizakat/mustahik/query.py
+++ b/sizakat/mustahik/query.py
@@ -25,7 +25,9 @@ class MustahikQuery(graphene.ObjectType):
             return mustahik
 
     def resolve_mustahikWithName(self, info, name=""):
-        return
+        if name != "":
+            return Mustahik.objects.filter(name__icontains=name)
+        return Mustahik.objects.all()
         
         
  
diff --git a/sizakat/mustahik/tests.py b/sizakat/mustahik/tests.py
index ea9a47e..ceeba11 100644
--- a/sizakat/mustahik/tests.py
+++ b/sizakat/mustahik/tests.py
@@ -316,7 +316,7 @@ class MustahikGraphQLTestCase(GraphQLTestCase):
         self.assertEqual(len(content['data']['mustahikWithName']),1)
         self.assertEqual(content['data']['mustahikWithName'][0]['name'], 'mustahik')
 
-    def test_mustahikWithName_if_name_is_set_should_return_list_of_mustahiks_with_contain_the_name(self):
+    def test_mustahikWithName_if_name_is_set_should_return_list_of_mustahiks_that_contain_the_name(self):
         Mustahik.objects.create(
             name='test',
             no_ktp='11751234567890',
-- 
GitLab


From dc10a586c8424a5c5947bb1a23f5d489242f6238 Mon Sep 17 00:00:00 2001
From: addffa <adli.daffa5@gmail.com>
Date: Sun, 26 Jul 2020 14:06:32 +0700
Subject: [PATCH 19/41] [REFACTOR] refactor fungsi-fungsi sesuai dengan panduan
 dan kesepakatan

---
 sizakat/mustahik/forms.py     | 22 +++++++++
 sizakat/mustahik/models.py    | 23 +++++----
 sizakat/mustahik/mutations.py | 46 +++++-------------
 sizakat/mustahik/query.py     | 42 +++++++++--------
 sizakat/mustahik/tests.py     | 87 +++++++++++++----------------------
 sizakat/mustahik/types.py     |  1 -
 6 files changed, 102 insertions(+), 119 deletions(-)
 create mode 100644 sizakat/mustahik/forms.py

diff --git a/sizakat/mustahik/forms.py b/sizakat/mustahik/forms.py
new file mode 100644
index 0000000..4a6ddae
--- /dev/null
+++ b/sizakat/mustahik/forms.py
@@ -0,0 +1,22 @@
+from django import forms
+
+from .models import Mustahik
+
+
+class MustahikForm(forms.ModelForm):
+    class Meta:
+        model = Mustahik
+        fields = [
+            'name',
+            'no_ktp',
+            'phone',
+            'address',
+            'province',
+            'regency',
+            'rt',
+            'rw',
+            'birthdate',
+            'status',
+            'family_size',
+            'description',
+        ]
diff --git a/sizakat/mustahik/models.py b/sizakat/mustahik/models.py
index 267fdaa..5083d9b 100644
--- a/sizakat/mustahik/models.py
+++ b/sizakat/mustahik/models.py
@@ -1,23 +1,30 @@
 from django.db import models
 
-# Create your models here.
+from django.core.validators import RegexValidator
 
+numeric_only = RegexValidator(r'^[0-9]*$', 'Numeric only.')
 
-class Mustahik(models.Model):
 
+class Mustahik(models.Model):
     class Status(models.TextChoices):
         JANDA = ('JANDA', 'Janda')
         MISKIN = ('MISKIN', 'Miskin')
         YATIM = ('YATIM', 'Yatim')
 
-    name = models.CharField(max_length=32)
-    no_ktp = models.CharField(max_length=32, unique=True)
-    phone = models.CharField(max_length=32, blank=True, null=True)
+    name = models.CharField(max_length=150)
+    no_ktp = models.CharField(
+        max_length=32, unique=True,
+        validators=[numeric_only]
+    )
+    phone = models.CharField(
+        max_length=32, blank=True, null=True,
+        validators=[numeric_only]
+    )
     address = models.TextField()
     province = models.CharField(max_length=32)
-    regency = models.CharField(max_length=32)
-    rt = models.CharField(max_length=4)
-    rw = models.CharField(max_length=4)
+    regency = models.CharField(max_length=50)
+    rt = models.CharField(max_length=4, validators=[numeric_only])
+    rw = models.CharField(max_length=4, validators=[numeric_only])
     birthdate = models.DateField()
     status = models.CharField(
         max_length=32,
diff --git a/sizakat/mustahik/mutations.py b/sizakat/mustahik/mutations.py
index 93db912..48f21d9 100644
--- a/sizakat/mustahik/mutations.py
+++ b/sizakat/mustahik/mutations.py
@@ -1,54 +1,32 @@
 import graphene
 
-from django import forms
 from graphene_django.forms.mutation import DjangoModelFormMutation
 
+from .forms import MustahikForm
 from .models import Mustahik
 from .types import MustahikType
 
 
-class MustahikForm(forms.ModelForm):
-
-    class Meta:
-        model = Mustahik
-        fields = [
-            'name',
-            'no_ktp',
-            'phone',
-            'address',
-            'province',
-            'regency',
-            'rt',
-            'rw',
-            'birthdate',
-            'status',
-            'family_size',
-            'description',
-        ]
-
-
 class MustahikMutation(DjangoModelFormMutation):
-
     mustahik = graphene.Field(MustahikType)
 
     class Meta:
         form_class = MustahikForm
 
-class DeleteMustahik(graphene.Mutation): 
+
+class DeleteMustahik(graphene.Mutation):
     class Arguments:
         id = graphene.ID()
-    
-    message = graphene.String()
-    idMustahik = graphene.ID()
-    nama = graphene.String()
-    noKtp = graphene.String()
-    mustahik = graphene.Field(MustahikType)
+
+    deleted = graphene.Boolean()
+    id_mustahik = graphene.ID()
+    name = graphene.String()
+    no_ktp = graphene.String()
 
     def mutate(self, info, id):
         mustahik = Mustahik.objects.get(pk=id)
-        _nama = mustahik.name
-        _no_ktp = mustahik.no_ktp
+        name = mustahik.name
+        no_ktp = mustahik.no_ktp
         mustahik.delete()
-        return DeleteMustahik(message = "Success", idMustahik=id, nama=_nama, noKtp=_no_ktp)
-
-
+        deleted = True
+        return DeleteMustahik(deleted=deleted, id_mustahik=id, name=name, no_ktp=no_ktp)
diff --git a/sizakat/mustahik/query.py b/sizakat/mustahik/query.py
index f78221c..38d987e 100644
--- a/sizakat/mustahik/query.py
+++ b/sizakat/mustahik/query.py
@@ -1,33 +1,35 @@
 import graphene
+
 from django.db.models import Q
+from functools import reduce
 
 from .models import Mustahik
 from .types import MustahikType
 
 
 class MustahikQuery(graphene.ObjectType):
-    mustahiks = graphene.List(MustahikType, statuses=graphene.List(graphene.String))
-    mustahik = graphene.Field(MustahikType, id=graphene.ID())
-    mustahikWithName = graphene.Field(lambda: graphene.List(MustahikType), name=graphene.String())
+    mustahiks = graphene.List(
+        MustahikType,
+        statuses=graphene.List(graphene.String),
+        name_contains=graphene.String()
+    )
+    mustahik = graphene.Field(MustahikType, id=graphene.ID(required=True))
+
+    def resolve_mustahiks(self, info, **kwargs):
+        statuses = kwargs.get('statuses', None)
+        name_contains = kwargs.get('name_contains', None)
+        mustahiks = Mustahik.objects.all()
 
-    def resolve_mustahiks(self, info, statuses=[], **kwargs):
         if statuses and len(statuses) > 0:
-            filter = Q(status=statuses[0])
-            for status in statuses:
-                filter = filter | Q(status=status)
-            return Mustahik.objects.filter(filter)
+            mustahiks = mustahiks.filter(reduce(
+                lambda a, b: a | b,
+                list(map(lambda s: Q(status=s), statuses))
+            ))
+
+        if name_contains:
+            mustahiks = mustahiks.filter(name__icontains=name_contains)
 
-        return Mustahik.objects.all()
+        return mustahiks
 
     def resolve_mustahik(self, info, id):
-        mustahik = Mustahik.objects.get(pk=id)
-        if mustahik is not None:
-            return mustahik
-
-    def resolve_mustahikWithName(self, info, name=""):
-        if name != "":
-            return Mustahik.objects.filter(name__icontains=name)
-        return Mustahik.objects.all()
-        
-        
- 
+        return Mustahik.objects.get(pk=id)
diff --git a/sizakat/mustahik/tests.py b/sizakat/mustahik/tests.py
index ceeba11..577bb1f 100644
--- a/sizakat/mustahik/tests.py
+++ b/sizakat/mustahik/tests.py
@@ -162,15 +162,15 @@ class MustahikGraphQLTestCase(GraphQLTestCase):
 
     def test_query_mustahik_should_return_list_of_mustahiks(self):
         response = self.query(
-        '''
+            '''
             query mustahiksQuery{
                 mustahiks {
                     id,
                     name
                 }
             }
-        ''',
-        op_name='mustahiksQuery'
+            ''',
+            op_name='mustahiksQuery'
         )
 
         self.assertResponseNoErrors(response)
@@ -241,35 +241,29 @@ class MustahikGraphQLTestCase(GraphQLTestCase):
             '''
                 mutation deleteMustahik($id: ID) {
                     deleteMustahik(id: $id) {
-                        mustahik {
-                            id
-                            name
-                        }
-                        message
+                        deleted
                         idMustahik
-                        nama
+                        name
                         noKtp
                     }
                 }
             ''',
             op_name='deleteMustahik',
-            variables={'id':mustahik_id}
+            variables={'id': mustahik_id}
         )
-        
+
         self.assertResponseNoErrors(response)
 
         content = json.loads(response.content)
-        self.assertIsNone(content['data']['deleteMustahik']['mustahik'])
-        self.assertEquals(content['data']['deleteMustahik']['message'], "Success")
+        self.assertTrue(content['data']['deleteMustahik']['deleted'])
         self.assertEquals(Mustahik.objects.count(), count-1)
 
-
     def test_mustahik_query_can_read_detail_mustahik(self):
         mustahik = Mustahik.objects.get(no_ktp='31751234567890')
         mustahik_id = mustahik.pk
         response = self.query(
             '''
-                query detailMustahikQuery($id:ID){
+                query detailMustahikQuery($id:ID!){
                     mustahik(id:$id){
                         id
                         name
@@ -288,35 +282,19 @@ class MustahikGraphQLTestCase(GraphQLTestCase):
                 }
             ''',
             op_name='detailMustahikQuery',
-            variables={'id':mustahik_id}
+            variables={'id': mustahik_id}
         )
         self.assertResponseNoErrors(response)
-        
+
         content = json.loads(response.content)
         self.assertEqual(len(content['data']), 1)
         self.assertEqual(content['data']['mustahik']['name'], 'mustahik')
-        self.assertEqual(content['data']['mustahik']['noKtp'], '31751234567890')
-        self.assertEqual(content['data']['mustahik']['address'], 'Jalan raya depok')
+        self.assertEqual(content['data']['mustahik']
+                         ['noKtp'], '31751234567890')
+        self.assertEqual(content['data']['mustahik']
+                         ['address'], 'Jalan raya depok')
 
-    def test_mustahikWithName_should_return_list_of_mustahik(self):
-        response = self.query(
-            '''
-            query mustahikWithName($name:String){
-                mustahikWithName(name: $name){
-                    id,
-                    name
-                }
-                
-            }
-            ''',
-            op_name='mustahikWithName',
-        )
-
-        content = json.loads(response.content)
-        self.assertEqual(len(content['data']['mustahikWithName']),1)
-        self.assertEqual(content['data']['mustahikWithName'][0]['name'], 'mustahik')
-
-    def test_mustahikWithName_if_name_is_set_should_return_list_of_mustahiks_that_contain_the_name(self):
+    def test_mustahiks_if_name_is_set_should_return_list_of_mustahiks_that_contain_the_name(self):
         Mustahik.objects.create(
             name='test',
             no_ktp='11751234567890',
@@ -349,42 +327,39 @@ class MustahikGraphQLTestCase(GraphQLTestCase):
 
         response = self.query(
             '''
-            query mustahikWithName($name:String){
-                mustahikWithName(name: $name){
+            query mustahiks($nameContains:String){
+                mustahiks(nameContains: $nameContains){
                     id,
                     name
                 }
                 
             }
             ''',
-            op_name='mustahikWithName',
-            variables={'name':'es'}
+            op_name='mustahiks',
+            variables={'nameContains': 'es'}
         )
 
         content = json.loads(response.content)
-        self.assertEqual(len(content['data']['mustahikWithName']), 2)
-        self.assertEqual(content['data']['mustahikWithName'][0]['name'], 'test')
-        self.assertEqual(content['data']['mustahikWithName'][1]['name'], 'eslu')
+        self.assertEqual(len(content['data']['mustahiks']), 2)
+        self.assertEqual(
+            content['data']['mustahiks'][0]['name'], 'test')
+        self.assertEqual(
+            content['data']['mustahiks'][1]['name'], 'eslu')
 
-    def test_mustahikWithName_if_name_is_not_available_should_return_empty_list(self):
+    def test_mustahiks_if_name_is_not_available_should_return_empty_list(self):
         response = self.query(
             '''
-            query mustahikWithName($name:String){
-                mustahikWithName(name: $name){
+            query mustahiks($nameContains:String){
+                mustahiks(nameContains: $nameContains){
                     id,
                     name
                 }
                 
             }
             ''',
-            op_name='mustahikWithName',
-            variables={'name':'#'}
+            op_name='mustahiks',
+            variables={'nameContains': '#'}
         )
 
         content = json.loads(response.content)
-        self.assertEqual(len(content['data']['mustahikWithName']), 0)
-        
-
-
-
-
+        self.assertEqual(len(content['data']['mustahiks']), 0)
diff --git a/sizakat/mustahik/types.py b/sizakat/mustahik/types.py
index cf9dba9..da1ff91 100644
--- a/sizakat/mustahik/types.py
+++ b/sizakat/mustahik/types.py
@@ -5,6 +5,5 @@ from .models import Mustahik
 
 
 class MustahikType(DjangoObjectType):
-
     class Meta:
         model = Mustahik
-- 
GitLab


From 9a667dff6b34533b12acfc5b4382b9b7bdbfbc49 Mon Sep 17 00:00:00 2001
From: addffa <adli.daffa5@gmail.com>
Date: Sun, 26 Jul 2020 16:57:29 +0700
Subject: [PATCH 20/41] change multiple line of string styling

---
 sizakat/schema.py | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/sizakat/schema.py b/sizakat/schema.py
index f7d47ba..683f54f 100644
--- a/sizakat/schema.py
+++ b/sizakat/schema.py
@@ -5,9 +5,9 @@ from graphene_django import DjangoObjectType
 from .mustahik.mutations import MustahikMutation, DeleteMustahik
 from .mustahik.query import MustahikQuery
 
-ABOUT = 'Si Zakat merupakan sistem informasi untuk membantu masjid dalam \
-mengelola transaksi zakat. Sistem ini dibuat oleh tim lab 1231, \
-yang dipimpin oleh Prof. Dr. Wisnu Jatmiko.'
+ABOUT = ('Si Zakat merupakan sistem informasi untuk membantu masjid dalam '
+         'mengelola transaksi zakat. Sistem ini dibuat oleh tim lab 1231, '
+         'yang dipimpin oleh Prof. Dr. Wisnu Jatmiko.')
 
 
 class Query(MustahikQuery, graphene.ObjectType):
-- 
GitLab


From 0785106fd946331906c0ede59c35e5ae882d348f Mon Sep 17 00:00:00 2001
From: addffa <adli.daffa5@gmail.com>
Date: Sun, 26 Jul 2020 21:09:47 +0700
Subject: [PATCH 21/41] [REFACTOR] refactor query dan validator

---
 sizakat/mustahik/models.py | 16 +++++++++-------
 sizakat/mustahik/query.py  | 12 ++++++------
 sizakat/settings.py        |  3 ++-
 sizakat/validators.py      |  5 +++++
 4 files changed, 22 insertions(+), 14 deletions(-)
 create mode 100644 sizakat/validators.py

diff --git a/sizakat/mustahik/models.py b/sizakat/mustahik/models.py
index 5083d9b..f66a5fb 100644
--- a/sizakat/mustahik/models.py
+++ b/sizakat/mustahik/models.py
@@ -1,8 +1,6 @@
 from django.db import models
 
-from django.core.validators import RegexValidator
-
-numeric_only = RegexValidator(r'^[0-9]*$', 'Numeric only.')
+from sizakat.validators import validate_numeric_character
 
 
 class Mustahik(models.Model):
@@ -14,17 +12,21 @@ class Mustahik(models.Model):
     name = models.CharField(max_length=150)
     no_ktp = models.CharField(
         max_length=32, unique=True,
-        validators=[numeric_only]
+        validators=[validate_numeric_character]
     )
     phone = models.CharField(
         max_length=32, blank=True, null=True,
-        validators=[numeric_only]
+        validators=[validate_numeric_character]
     )
     address = models.TextField()
     province = models.CharField(max_length=32)
     regency = models.CharField(max_length=50)
-    rt = models.CharField(max_length=4, validators=[numeric_only])
-    rw = models.CharField(max_length=4, validators=[numeric_only])
+    rt = models.CharField(
+        max_length=4, validators=[validate_numeric_character]
+    )
+    rw = models.CharField(
+        max_length=4, validators=[validate_numeric_character]
+    )
     birthdate = models.DateField()
     status = models.CharField(
         max_length=32,
diff --git a/sizakat/mustahik/query.py b/sizakat/mustahik/query.py
index 38d987e..e7e8036 100644
--- a/sizakat/mustahik/query.py
+++ b/sizakat/mustahik/query.py
@@ -18,18 +18,18 @@ class MustahikQuery(graphene.ObjectType):
     def resolve_mustahiks(self, info, **kwargs):
         statuses = kwargs.get('statuses', None)
         name_contains = kwargs.get('name_contains', None)
-        mustahiks = Mustahik.objects.all()
+        filter_query = Q()
 
         if statuses and len(statuses) > 0:
-            mustahiks = mustahiks.filter(reduce(
+            filter_query |= reduce(
                 lambda a, b: a | b,
-                list(map(lambda s: Q(status=s), statuses))
-            ))
+                [Q(status=status) for status in statuses]
+            )
 
         if name_contains:
-            mustahiks = mustahiks.filter(name__icontains=name_contains)
+            filter_query &= Q(name__icontains=name_contains)
 
-        return mustahiks
+        return Mustahik.objects.filter(filter_query)
 
     def resolve_mustahik(self, info, id):
         return Mustahik.objects.get(pk=id)
diff --git a/sizakat/settings.py b/sizakat/settings.py
index 6ede7a0..0a90bcb 100644
--- a/sizakat/settings.py
+++ b/sizakat/settings.py
@@ -57,7 +57,8 @@ MIDDLEWARE = [
     'django.middleware.clickjacking.XFrameOptionsMiddleware',
 ]
 
-CORS_ORIGIN_WHITELIST = os.environ.get('CORS_ORIGIN_WHITELIST').split()
+CORS_ORIGIN_WHITELIST = os.environ.get(
+    'CORS_ORIGIN_WHITELIST', 'http://localhost:3000').split()
 
 ROOT_URLCONF = 'sizakat.urls'
 
diff --git a/sizakat/validators.py b/sizakat/validators.py
new file mode 100644
index 0000000..7b55e88
--- /dev/null
+++ b/sizakat/validators.py
@@ -0,0 +1,5 @@
+from django.core.validators import RegexValidator
+
+validate_numeric_character = RegexValidator(
+    r'^[0-9]*$', 'Numeric character only.'
+)
-- 
GitLab


From f12debca43b147f6dade86e0a15785edf5f49b8a Mon Sep 17 00:00:00 2001
From: addffa <adli.daffa5@gmail.com>
Date: Mon, 27 Jul 2020 08:13:53 +0700
Subject: [PATCH 22/41] [RED] membuat test gender pada model mustahik

---
 sizakat/mustahik/tests.py | 23 ++++++++++++++++-------
 1 file changed, 16 insertions(+), 7 deletions(-)

diff --git a/sizakat/mustahik/tests.py b/sizakat/mustahik/tests.py
index 577bb1f..1b39071 100644
--- a/sizakat/mustahik/tests.py
+++ b/sizakat/mustahik/tests.py
@@ -23,7 +23,8 @@ class MustahikModelTestCase(TestCase):
             birthdate=date(1987, 6, 5),
             status=Mustahik.Status.MISKIN,
             family_size=4,
-            description='desc'
+            description='desc',
+            gender=Mustahik.Gender.LAKILAKI
         )
 
     def test_mustahik_creation(self):
@@ -44,7 +45,8 @@ class MustahikModelTestCase(TestCase):
                 birthdate=date(1987, 4, 3),
                 status=Mustahik.Status.MISKIN,
                 family_size=1,
-                description='no_ktp is unique'
+                description='no_ktp is unique',
+                gender=Mustahik.Gender.PEREMPUAN
             )
 
 
@@ -64,7 +66,8 @@ class MustahikGraphQLTestCase(GraphQLTestCase):
             birthdate=date(1987, 6, 5),
             status=Mustahik.Status.MISKIN,
             family_size=4,
-            description='desc'
+            description='desc',
+            gender=Mustahik.Gender.LAKILAKI
         )
 
     def test_mustahik_mutation_can_add_new_mustahik(self):
@@ -100,7 +103,8 @@ class MustahikGraphQLTestCase(GraphQLTestCase):
                 "birthdate": "1998-03-12",
                 "status": "YATIM",
                 "familySize": 3,
-                "description": "anak yatim"
+                "description": "anak yatim",
+                "gender": "L"
             }
         )
 
@@ -151,6 +155,7 @@ class MustahikGraphQLTestCase(GraphQLTestCase):
                 "status": "MISKIN",
                 "familySize": 4,
                 "description": new_desc,
+                "gender": "L",
                 "id": mustahik.pk
             }
         )
@@ -192,7 +197,8 @@ class MustahikGraphQLTestCase(GraphQLTestCase):
             birthdate=date(1987, 6, 5),
             status=Mustahik.Status.YATIM,
             family_size=4,
-            description='desc'
+            description='desc',
+            gender=Mustahik.Gender.LAKILAKI
         )
 
         response = self.query(
@@ -278,6 +284,7 @@ class MustahikGraphQLTestCase(GraphQLTestCase):
                         status
                         familySize
                         description
+                        gender
                     }
                 }
             ''',
@@ -307,7 +314,8 @@ class MustahikGraphQLTestCase(GraphQLTestCase):
             birthdate=date(1987, 6, 5),
             status=Mustahik.Status.YATIM,
             family_size=4,
-            description='desc'
+            description='desc',
+            gender=Mustahik.Gender.LAKILAKI
         )
 
         Mustahik.objects.create(
@@ -322,7 +330,8 @@ class MustahikGraphQLTestCase(GraphQLTestCase):
             birthdate=date(1987, 6, 5),
             status=Mustahik.Status.YATIM,
             family_size=4,
-            description='desc'
+            description='desc',
+            gender=Mustahik.Gender.LAKILAKI
         )
 
         response = self.query(
-- 
GitLab


From de78763f079d8b349c8fa69d368eda10da2f6c82 Mon Sep 17 00:00:00 2001
From: addffa <adli.daffa5@gmail.com>
Date: Mon, 27 Jul 2020 08:14:20 +0700
Subject: [PATCH 23/41] [GREEN] menambahkan gender ke model mustahik

---
 sizakat/mustahik/forms.py  | 1 +
 sizakat/mustahik/models.py | 5 +++++
 2 files changed, 6 insertions(+)

diff --git a/sizakat/mustahik/forms.py b/sizakat/mustahik/forms.py
index 4a6ddae..c0862ef 100644
--- a/sizakat/mustahik/forms.py
+++ b/sizakat/mustahik/forms.py
@@ -19,4 +19,5 @@ class MustahikForm(forms.ModelForm):
             'status',
             'family_size',
             'description',
+            'gender',
         ]
diff --git a/sizakat/mustahik/models.py b/sizakat/mustahik/models.py
index f66a5fb..081f064 100644
--- a/sizakat/mustahik/models.py
+++ b/sizakat/mustahik/models.py
@@ -9,6 +9,10 @@ class Mustahik(models.Model):
         MISKIN = ('MISKIN', 'Miskin')
         YATIM = ('YATIM', 'Yatim')
 
+    class Gender(models.TextChoices):
+        LAKILAKI = ('L', 'Laki-Laki')
+        PEREMPUAN = ('P', 'Perempuan')
+
     name = models.CharField(max_length=150)
     no_ktp = models.CharField(
         max_length=32, unique=True,
@@ -35,3 +39,4 @@ class Mustahik(models.Model):
     )
     family_size = models.PositiveSmallIntegerField()
     description = models.TextField(blank=True, null=True)
+    gender = models.CharField(max_length=1, choices=Gender.choices)
-- 
GitLab


From f846900670a0584ee5b305963a4e011fef9fdec8 Mon Sep 17 00:00:00 2001
From: addffa <adli.daffa5@gmail.com>
Date: Mon, 27 Jul 2020 09:07:30 +0700
Subject: [PATCH 24/41] [RED] membuat test hitung umur mustahik

---
 sizakat/mustahik/tests.py | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/sizakat/mustahik/tests.py b/sizakat/mustahik/tests.py
index 1b39071..52ccc6b 100644
--- a/sizakat/mustahik/tests.py
+++ b/sizakat/mustahik/tests.py
@@ -3,6 +3,7 @@ import json
 from datetime import date
 from django.db.utils import IntegrityError
 from django.test import TestCase
+from django.utils import timezone
 from graphene_django.utils.testing import GraphQLTestCase
 from sizakat.schema import schema
 
@@ -49,6 +50,13 @@ class MustahikModelTestCase(TestCase):
                 gender=Mustahik.Gender.PEREMPUAN
             )
 
+    def test_calculate_mustahik_age(self):
+        mustahik = Mustahik.objects.get(no_ktp='31751234567890')
+        self.assertEqual(
+            mustahik.calculate_age(),
+            timezone.now().year - mustahik.birthdate.year
+        )
+
 
 class MustahikGraphQLTestCase(GraphQLTestCase):
     GRAPHQL_SCHEMA = schema
-- 
GitLab


From 41e23466436575c0eabec3bc1765e4e8d18d90c0 Mon Sep 17 00:00:00 2001
From: addffa <adli.daffa5@gmail.com>
Date: Mon, 27 Jul 2020 09:17:13 +0700
Subject: [PATCH 25/41] [GREEN] menambahkan fungsi hitung umur mustahik

---
 sizakat/mustahik/models.py |  4 ++++
 sizakat/mustahik/tests.py  | 11 ++++++-----
 sizakat/mustahik/types.py  |  2 ++
 3 files changed, 12 insertions(+), 5 deletions(-)

diff --git a/sizakat/mustahik/models.py b/sizakat/mustahik/models.py
index 081f064..f9e8978 100644
--- a/sizakat/mustahik/models.py
+++ b/sizakat/mustahik/models.py
@@ -1,4 +1,5 @@
 from django.db import models
+from django.utils import timezone
 
 from sizakat.validators import validate_numeric_character
 
@@ -40,3 +41,6 @@ class Mustahik(models.Model):
     family_size = models.PositiveSmallIntegerField()
     description = models.TextField(blank=True, null=True)
     gender = models.CharField(max_length=1, choices=Gender.choices)
+
+    def calculate_age(self):
+        return timezone.now().year - self.birthdate.year
diff --git a/sizakat/mustahik/tests.py b/sizakat/mustahik/tests.py
index 52ccc6b..2c8d705 100644
--- a/sizakat/mustahik/tests.py
+++ b/sizakat/mustahik/tests.py
@@ -293,6 +293,7 @@ class MustahikGraphQLTestCase(GraphQLTestCase):
                         familySize
                         description
                         gender
+                        age
                     }
                 }
             ''',
@@ -302,12 +303,12 @@ class MustahikGraphQLTestCase(GraphQLTestCase):
         self.assertResponseNoErrors(response)
 
         content = json.loads(response.content)
+        data_mustahik = content['data']['mustahik']
         self.assertEqual(len(content['data']), 1)
-        self.assertEqual(content['data']['mustahik']['name'], 'mustahik')
-        self.assertEqual(content['data']['mustahik']
-                         ['noKtp'], '31751234567890')
-        self.assertEqual(content['data']['mustahik']
-                         ['address'], 'Jalan raya depok')
+        self.assertEqual(data_mustahik['name'], 'mustahik')
+        self.assertEqual(data_mustahik['noKtp'], '31751234567890')
+        self.assertEqual(data_mustahik['address'], 'Jalan raya depok')
+        self.assertEqual(data_mustahik['age'], mustahik.calculate_age())
 
     def test_mustahiks_if_name_is_set_should_return_list_of_mustahiks_that_contain_the_name(self):
         Mustahik.objects.create(
diff --git a/sizakat/mustahik/types.py b/sizakat/mustahik/types.py
index da1ff91..d1d87cb 100644
--- a/sizakat/mustahik/types.py
+++ b/sizakat/mustahik/types.py
@@ -7,3 +7,5 @@ from .models import Mustahik
 class MustahikType(DjangoObjectType):
     class Meta:
         model = Mustahik
+
+    age = graphene.Int(source='calculate_age')
-- 
GitLab


From 3376c4674a97ff003137f27a1b1e627b3537e81f Mon Sep 17 00:00:00 2001
From: addffa <adli.daffa5@gmail.com>
Date: Mon, 27 Jul 2020 11:25:24 +0700
Subject: [PATCH 26/41] [CHORES] menambahkan migrations ke commit

---
 .gitignore                                  |  3 --
 sizakat/mustahik/migrations/0001_initial.py | 34 +++++++++++++++++++++
 sizakat/mustahik/migrations/__init__.py     |  0
 3 files changed, 34 insertions(+), 3 deletions(-)
 create mode 100644 sizakat/mustahik/migrations/0001_initial.py
 create mode 100644 sizakat/mustahik/migrations/__init__.py

diff --git a/.gitignore b/.gitignore
index 31d9ad9..e6dcff9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -66,9 +66,6 @@ coverage.xml
 # staticfiles
 staticfiles/
 
-# migrations file
-/**/migrations/
-
 # Jupyter Notebook 
 .ipynb_checkpoints 
 
diff --git a/sizakat/mustahik/migrations/0001_initial.py b/sizakat/mustahik/migrations/0001_initial.py
new file mode 100644
index 0000000..c098adf
--- /dev/null
+++ b/sizakat/mustahik/migrations/0001_initial.py
@@ -0,0 +1,34 @@
+# Generated by Django 3.0.7 on 2020-07-27 04:24
+
+import django.core.validators
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    initial = True
+
+    dependencies = [
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='Mustahik',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('name', models.CharField(max_length=150)),
+                ('no_ktp', models.CharField(max_length=32, unique=True, validators=[django.core.validators.RegexValidator('^[0-9]*$', 'Numeric character only.')])),
+                ('phone', models.CharField(blank=True, max_length=32, null=True, validators=[django.core.validators.RegexValidator('^[0-9]*$', 'Numeric character only.')])),
+                ('address', models.TextField()),
+                ('province', models.CharField(max_length=32)),
+                ('regency', models.CharField(max_length=50)),
+                ('rt', models.CharField(max_length=4, validators=[django.core.validators.RegexValidator('^[0-9]*$', 'Numeric character only.')])),
+                ('rw', models.CharField(max_length=4, validators=[django.core.validators.RegexValidator('^[0-9]*$', 'Numeric character only.')])),
+                ('birthdate', models.DateField()),
+                ('status', models.CharField(choices=[('JANDA', 'Janda'), ('MISKIN', 'Miskin'), ('YATIM', 'Yatim')], default='MISKIN', max_length=32)),
+                ('family_size', models.PositiveSmallIntegerField()),
+                ('description', models.TextField(blank=True, null=True)),
+                ('gender', models.CharField(choices=[('L', 'Laki-Laki'), ('P', 'Perempuan')], max_length=1)),
+            ],
+        ),
+    ]
diff --git a/sizakat/mustahik/migrations/__init__.py b/sizakat/mustahik/migrations/__init__.py
new file mode 100644
index 0000000..e69de29
-- 
GitLab


From c48bbb3c317c58d7f69db18ad17fa90977522de4 Mon Sep 17 00:00:00 2001
From: addffa <adli.daffa5@gmail.com>
Date: Tue, 28 Jul 2020 17:48:17 +0700
Subject: [PATCH 27/41] [RED] membuat test model mustahik terbaru

---
 sizakat/mustahik/tests.py | 234 +++++++++++++++++---------------------
 1 file changed, 103 insertions(+), 131 deletions(-)

diff --git a/sizakat/mustahik/tests.py b/sizakat/mustahik/tests.py
index 2c8d705..543c738 100644
--- a/sizakat/mustahik/tests.py
+++ b/sizakat/mustahik/tests.py
@@ -7,48 +7,85 @@ from django.utils import timezone
 from graphene_django.utils.testing import GraphQLTestCase
 from sizakat.schema import schema
 
-from .models import Mustahik
+from .models import DataSource, DataSourceInstitusi, DataSourcePekerja, DataSourceWarga, Mustahik
 
 
 class MustahikModelTestCase(TestCase):
     def setUp(self):
-        Mustahik.objects.create(
+        data_source_institusi = DataSource.objects.create(
+            pic_name='pic test',
+            pic_ktp='1234567890',
+            pic_phone='0812389120',
+            pic_position='test',
+            category=DataSource.Category.INSTITUSI
+        )
+        institusi_detail = DataSourceInstitusi.objects.create(
+            name='lembaga test',
+            address='jl test',
+            data_source=data_source_institusi,
+            province='jakarta',
+            regency='jakarta timur',
+            sub_district='makasar',
+            village='pinangranti',
+            rt='001',
+            rw='001'
+        )
+        data_source_pekerja = DataSource.objects.create(
+            pic_name='pic test',
+            pic_ktp='1234567891',
+            pic_phone='0812389121',
+            pic_position='test',
+            category=DataSource.Category.PEKERJA
+        )
+        pekerja_detail = DataSourcePekerja.objects.create(
+            data_source=data_source_pekerja,
+            profession='tester',
+            location='jl tester'
+        )
+        data_source_warga = DataSource.objects.create(
+            pic_name='pic test',
+            pic_ktp='1234567892',
+            pic_phone='0812389122',
+            pic_position='test',
+            category=DataSource.Category.WARGA
+        )
+        institusi_detail = DataSourceWarga.objects.create(
+            data_source=data_source_warga,
+            province='jakarta',
+            regency='jakarta timur',
+            sub_district='makasar',
+            village='pinangranti',
+            rt='001',
+            rw='001'
+        )
+        mustahik = Mustahik.objects.create(
             name='mustahik',
             no_ktp='31751234567890',
             phone='081234567890',
             address='Jalan raya depok',
-            province='Jawa Barat',
-            regency='Depok',
-            rt='003',
-            rw='002',
             birthdate=date(1987, 6, 5),
             status=Mustahik.Status.MISKIN,
-            family_size=4,
-            description='desc',
-            gender=Mustahik.Gender.LAKILAKI
+            gender=Mustahik.Gender.LAKILAKI,
+            data_source=data_source_warga
         )
 
-    def test_mustahik_creation(self):
+    def test_mustahik_creation_from_datasource_institusi(self):
         mustahik = Mustahik.objects.get(no_ktp='31751234567890')
         self.assertTrue(isinstance(mustahik, Mustahik))
 
-    def test_no_ktp_mustahik_is_unique(self):
-        with self.assertRaises(IntegrityError):
-            Mustahik.objects.create(
-                name='kihatsum',
-                no_ktp='31751234567890',
-                phone='08987654321',
-                address='Jalan raya bogor',
-                province='Jawa Barat',
-                regency='Bogor',
-                rt='002',
-                rw='003',
-                birthdate=date(1987, 4, 3),
-                status=Mustahik.Status.MISKIN,
-                family_size=1,
-                description='no_ktp is unique',
-                gender=Mustahik.Gender.PEREMPUAN
-            )
+    def test_mustahik_change_datasource_to_datasource_pekerja(self):
+        data_source_pekerja = DataSource.objects.get(pic_ktp='1234567891')
+        mustahik = Mustahik.objects.get(no_ktp='31751234567890')
+        mustahik.data_source = data_source_pekerja
+        mustahik.save()
+        self.assertEqual(mustahik.data_source.category, DataSource.Category.PEKERJA)
+
+    def test_mustahik_change_datasource_to_datasource_warga(self):
+        data_source_warga = DataSource.objects.get(pic_ktp='1234567892')
+        mustahik = Mustahik.objects.get(no_ktp='31751234567890')
+        mustahik.data_source = data_source_warga
+        mustahik.save()
+        self.assertEqual(mustahik.data_source.category, DataSource.Category.WARGA)
 
     def test_calculate_mustahik_age(self):
         mustahik = Mustahik.objects.get(no_ktp='31751234567890')
@@ -62,38 +99,44 @@ class MustahikGraphQLTestCase(GraphQLTestCase):
     GRAPHQL_SCHEMA = schema
 
     def setUp(self):
-        Mustahik.objects.create(
+        data_source_warga = DataSource.objects.create(
+            pic_name='pic test',
+            pic_ktp='1234567892',
+            pic_phone='0812389122',
+            pic_position='test',
+            category=DataSource.Category.WARGA
+        )
+        institusi_detail = DataSourceWarga.objects.create(
+            data_source=data_source_warga,
+            province='jakarta',
+            regency='jakarta timur',
+            sub_district='makasar',
+            village='pinangranti',
+            rt='001',
+            rw='001'
+        )
+        mustahik = Mustahik.objects.create(
             name='mustahik',
             no_ktp='31751234567890',
             phone='081234567890',
             address='Jalan raya depok',
-            province='Jawa Barat',
-            regency='Depok',
-            rt='003',
-            rw='002',
             birthdate=date(1987, 6, 5),
             status=Mustahik.Status.MISKIN,
-            family_size=4,
-            description='desc',
-            gender=Mustahik.Gender.LAKILAKI
+            gender=Mustahik.Gender.LAKILAKI,
+            data_source=data_source_warga
         )
 
     def test_mustahik_mutation_can_add_new_mustahik(self):
         no_ktp = '123891210121'
+        data_source = DataSource.objects.get(pic_ktp='1234567892')
         response = self.query(
             '''
             mutation mustahikMutation($input: MustahikMutationInput!) {
                 mustahikMutation(input: $input) {
                     mustahik {
-                        id
                         name
                         noKtp
                         status
-                        description
-                    }
-                    errors {
-                        field
-                        messages
                     }
                 }
             }
@@ -104,15 +147,10 @@ class MustahikGraphQLTestCase(GraphQLTestCase):
                 "noKtp": no_ktp,
                 "phone": "02132132180",
                 "address": "jalan swadaya",
-                "province": "jakarta",
-                "regency": "manggarai",
-                "rt": "001",
-                "rw": "001",
                 "birthdate": "1998-03-12",
-                "status": "YATIM",
-                "familySize": 3,
-                "description": "anak yatim",
-                "gender": "L"
+                "status": "MISKIN",
+                "gender": "L",
+                "dataSource": data_source.pk
             }
         )
 
@@ -122,25 +160,26 @@ class MustahikGraphQLTestCase(GraphQLTestCase):
         # Validate content
         content = json.loads(response.content)
         self.assertEqual(content['data']['mustahikMutation']
-                         ['mustahik']['status'], 'YATIM')
+                         ['mustahik']['status'], 'MISKIN')
 
         # Validate success save to db
-        self.assertNotEqual(Mustahik.objects.count(), 0)
+        self.assertEqual(Mustahik.objects.count(), 2)
         mustahik = Mustahik.objects.get(no_ktp=no_ktp)
         self.assertEqual(mustahik.name, 'jumat')
 
     def test_mustahik_mutation_can_update_mustahik(self):
         mustahik = Mustahik.objects.get(no_ktp='31751234567890')
+        data_source = mustahik.data_source
         mustahik_id = mustahik.pk
-        old_desc = mustahik.description
-        new_desc = 'keluarga tidak mampu'
+        old_status = mustahik.status
+        new_status = 'MUSAFIR'
         response = self.query(
             '''
             mutation mustahikMutation($input: MustahikMutationInput!) {
                 mustahikMutation(input: $input) {
                     mustahik {
                         id
-                        description
+                        status
                     }
                     errors {
                         field
@@ -155,23 +194,17 @@ class MustahikGraphQLTestCase(GraphQLTestCase):
                 "noKtp": "31751234567890",
                 "phone": "081234567890",
                 "address": "Jalan raya depok",
-                "province": "Jawa Barat",
-                "regency": "Depok",
-                "rt": "003",
-                "rw": "002",
                 "birthdate": "1987-06-05",
-                "status": "MISKIN",
-                "familySize": 4,
-                "description": new_desc,
+                "status": "MUSAFIR",
                 "gender": "L",
-                "id": mustahik.pk
+                "id": mustahik.pk,
+                "dataSource": data_source.pk
             }
         )
 
         # Validate success update desc mustahik
         mustahik = Mustahik.objects.get(no_ktp='31751234567890')
-        self.assertNotEqual(mustahik.description, old_desc)
-        self.assertEqual(mustahik.description, new_desc)
+        self.assertEqual(mustahik.status, new_status)
 
     def test_query_mustahik_should_return_list_of_mustahiks(self):
         response = self.query(
@@ -193,22 +226,6 @@ class MustahikGraphQLTestCase(GraphQLTestCase):
         self.assertEqual(content['data']['mustahiks'][0]['name'], 'mustahik')
 
     def test_query_mustahiks_if_statuses_is_set_should_return_list_of_mustahiks_with_coresponding_status(self):
-        Mustahik.objects.create(
-            name='test',
-            no_ktp='11751234567890',
-            phone='081234567890',
-            address='Jalan raya depok',
-            province='Jawa Barat',
-            regency='Depok',
-            rt='003',
-            rw='002',
-            birthdate=date(1987, 6, 5),
-            status=Mustahik.Status.YATIM,
-            family_size=4,
-            description='desc',
-            gender=Mustahik.Gender.LAKILAKI
-        )
-
         response = self.query(
             '''
                 query mustahiksQuery($statuses: [String]) {
@@ -241,7 +258,7 @@ class MustahikGraphQLTestCase(GraphQLTestCase):
                 }
             ''',
             op_name='mustahiksQuery',
-            variables={'statuses': [Mustahik.Status.JANDA]}
+            variables={'statuses': [Mustahik.Status.GHARIM]}
         )
 
         content = json.loads(response.content)
@@ -279,20 +296,9 @@ class MustahikGraphQLTestCase(GraphQLTestCase):
             '''
                 query detailMustahikQuery($id:ID!){
                     mustahik(id:$id){
-                        id
                         name
                         noKtp
-                        phone
                         address
-                        province
-                        regency
-                        rt
-                        rw
-                        birthdate
-                        status
-                        familySize
-                        description
-                        gender
                         age
                     }
                 }
@@ -311,38 +317,6 @@ class MustahikGraphQLTestCase(GraphQLTestCase):
         self.assertEqual(data_mustahik['age'], mustahik.calculate_age())
 
     def test_mustahiks_if_name_is_set_should_return_list_of_mustahiks_that_contain_the_name(self):
-        Mustahik.objects.create(
-            name='test',
-            no_ktp='11751234567890',
-            phone='081234567890',
-            address='Jalan raya depok',
-            province='Jawa Barat',
-            regency='Depok',
-            rt='003',
-            rw='002',
-            birthdate=date(1987, 6, 5),
-            status=Mustahik.Status.YATIM,
-            family_size=4,
-            description='desc',
-            gender=Mustahik.Gender.LAKILAKI
-        )
-
-        Mustahik.objects.create(
-            name='eslu',
-            no_ktp='22751234337899',
-            phone='081234567890',
-            address='Jalan depok',
-            province='Jawa Timur',
-            regency='Bondo',
-            rt='003',
-            rw='002',
-            birthdate=date(1987, 6, 5),
-            status=Mustahik.Status.YATIM,
-            family_size=4,
-            description='desc',
-            gender=Mustahik.Gender.LAKILAKI
-        )
-
         response = self.query(
             '''
             query mustahiks($nameContains:String){
@@ -350,19 +324,17 @@ class MustahikGraphQLTestCase(GraphQLTestCase):
                     id,
                     name
                 }
-                
+
             }
             ''',
             op_name='mustahiks',
-            variables={'nameContains': 'es'}
+            variables={'nameContains': 'hik'}
         )
 
         content = json.loads(response.content)
-        self.assertEqual(len(content['data']['mustahiks']), 2)
-        self.assertEqual(
-            content['data']['mustahiks'][0]['name'], 'test')
+        self.assertEqual(len(content['data']['mustahiks']), 1)
         self.assertEqual(
-            content['data']['mustahiks'][1]['name'], 'eslu')
+            content['data']['mustahiks'][0]['name'], 'mustahik')
 
     def test_mustahiks_if_name_is_not_available_should_return_empty_list(self):
         response = self.query(
@@ -372,7 +344,7 @@ class MustahikGraphQLTestCase(GraphQLTestCase):
                     id,
                     name
                 }
-                
+
             }
             ''',
             op_name='mustahiks',
-- 
GitLab


From 6fad5cec4b3b48702481064d77e262c3c1d5d8b5 Mon Sep 17 00:00:00 2001
From: addffa <adli.daffa5@gmail.com>
Date: Tue, 28 Jul 2020 17:49:34 +0700
Subject: [PATCH 28/41] [GREEN] implementasi model mustahik baru

---
 sizakat/mustahik/forms.py                   |  7 +-
 sizakat/mustahik/migrations/0001_initial.py | 66 ++++++++++++--
 sizakat/mustahik/models.py                  | 96 +++++++++++++++++----
 3 files changed, 138 insertions(+), 31 deletions(-)

diff --git a/sizakat/mustahik/forms.py b/sizakat/mustahik/forms.py
index c0862ef..485c70e 100644
--- a/sizakat/mustahik/forms.py
+++ b/sizakat/mustahik/forms.py
@@ -11,13 +11,8 @@ class MustahikForm(forms.ModelForm):
             'no_ktp',
             'phone',
             'address',
-            'province',
-            'regency',
-            'rt',
-            'rw',
             'birthdate',
             'status',
-            'family_size',
-            'description',
             'gender',
+            'data_source',
         ]
diff --git a/sizakat/mustahik/migrations/0001_initial.py b/sizakat/mustahik/migrations/0001_initial.py
index c098adf..b14b749 100644
--- a/sizakat/mustahik/migrations/0001_initial.py
+++ b/sizakat/mustahik/migrations/0001_initial.py
@@ -1,7 +1,8 @@
-# Generated by Django 3.0.7 on 2020-07-27 04:24
+# Generated by Django 3.0.7 on 2020-07-28 10:47
 
 import django.core.validators
 from django.db import migrations, models
+import django.db.models.deletion
 
 
 class Migration(migrations.Migration):
@@ -12,6 +13,20 @@ class Migration(migrations.Migration):
     ]
 
     operations = [
+        migrations.CreateModel(
+            name='DataSource',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('pic_name', models.CharField(max_length=150)),
+                ('pic_ktp', models.CharField(max_length=32, validators=[django.core.validators.RegexValidator('^[0-9]*$', 'Numeric character only.')])),
+                ('pic_phone', models.CharField(max_length=32, validators=[django.core.validators.RegexValidator('^[0-9]*$', 'Numeric character only.')])),
+                ('pic_position', models.CharField(max_length=50)),
+                ('category', models.CharField(choices=[('WARGA', 'Warga'), ('INSTITUSI', 'Institusi'), ('PEKERJA', 'Pekerja')], max_length=32)),
+            ],
+            options={
+                'unique_together': {('pic_ktp', 'category')},
+            },
+        ),
         migrations.CreateModel(
             name='Mustahik',
             fields=[
@@ -19,16 +34,49 @@ class Migration(migrations.Migration):
                 ('name', models.CharField(max_length=150)),
                 ('no_ktp', models.CharField(max_length=32, unique=True, validators=[django.core.validators.RegexValidator('^[0-9]*$', 'Numeric character only.')])),
                 ('phone', models.CharField(blank=True, max_length=32, null=True, validators=[django.core.validators.RegexValidator('^[0-9]*$', 'Numeric character only.')])),
-                ('address', models.TextField()),
-                ('province', models.CharField(max_length=32)),
-                ('regency', models.CharField(max_length=50)),
-                ('rt', models.CharField(max_length=4, validators=[django.core.validators.RegexValidator('^[0-9]*$', 'Numeric character only.')])),
-                ('rw', models.CharField(max_length=4, validators=[django.core.validators.RegexValidator('^[0-9]*$', 'Numeric character only.')])),
+                ('address', models.CharField(max_length=255)),
                 ('birthdate', models.DateField()),
-                ('status', models.CharField(choices=[('JANDA', 'Janda'), ('MISKIN', 'Miskin'), ('YATIM', 'Yatim')], default='MISKIN', max_length=32)),
-                ('family_size', models.PositiveSmallIntegerField()),
-                ('description', models.TextField(blank=True, null=True)),
+                ('status', models.CharField(choices=[('FAKIR', 'Fakir'), ('MISKIN', 'Miskin'), ('AMIL', 'Amil'), ('MUALAF', 'Mualaf'), ('GHARIM', 'Gharim'), ('FISABILILLAH', 'Fisabilillah'), ('MUSAFIR', 'Musafir')], max_length=32)),
                 ('gender', models.CharField(choices=[('L', 'Laki-Laki'), ('P', 'Perempuan')], max_length=1)),
+                ('photo', models.FileField(default='images/default_photo.jpg', upload_to='images/mustahik')),
+                ('data_source', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mustahik.DataSource')),
+            ],
+        ),
+        migrations.CreateModel(
+            name='DataSourceWarga',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('province', models.CharField(max_length=50)),
+                ('regency', models.CharField(max_length=50)),
+                ('sub_district', models.CharField(max_length=50)),
+                ('village', models.CharField(max_length=50)),
+                ('rt', models.CharField(max_length=3, validators=[django.core.validators.RegexValidator('^[0-9]*$', 'Numeric character only.')])),
+                ('rw', models.CharField(max_length=3, validators=[django.core.validators.RegexValidator('^[0-9]*$', 'Numeric character only.')])),
+                ('data_source', models.OneToOneField(limit_choices_to={'category': 'WARGA'}, on_delete=django.db.models.deletion.CASCADE, to='mustahik.DataSource')),
+            ],
+        ),
+        migrations.CreateModel(
+            name='DataSourcePekerja',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('profession', models.CharField(max_length=50)),
+                ('location', models.CharField(max_length=50)),
+                ('data_source', models.OneToOneField(limit_choices_to={'category': 'PEKERJA'}, on_delete=django.db.models.deletion.CASCADE, to='mustahik.DataSource')),
+            ],
+        ),
+        migrations.CreateModel(
+            name='DataSourceInstitusi',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('name', models.CharField(max_length=150)),
+                ('province', models.CharField(max_length=50)),
+                ('regency', models.CharField(max_length=50)),
+                ('sub_district', models.CharField(max_length=50)),
+                ('village', models.CharField(max_length=50)),
+                ('rt', models.CharField(max_length=3, validators=[django.core.validators.RegexValidator('^[0-9]*$', 'Numeric character only.')])),
+                ('rw', models.CharField(max_length=3, validators=[django.core.validators.RegexValidator('^[0-9]*$', 'Numeric character only.')])),
+                ('address', models.CharField(max_length=255)),
+                ('data_source', models.OneToOneField(limit_choices_to={'category': 'INSTITUSI'}, on_delete=django.db.models.deletion.CASCADE, to='mustahik.DataSource')),
             ],
         ),
     ]
diff --git a/sizakat/mustahik/models.py b/sizakat/mustahik/models.py
index f9e8978..18067f0 100644
--- a/sizakat/mustahik/models.py
+++ b/sizakat/mustahik/models.py
@@ -1,3 +1,5 @@
+import os
+
 from django.db import models
 from django.utils import timezone
 
@@ -6,9 +8,13 @@ from sizakat.validators import validate_numeric_character
 
 class Mustahik(models.Model):
     class Status(models.TextChoices):
-        JANDA = ('JANDA', 'Janda')
+        FAKIR = ('FAKIR', 'Fakir')
         MISKIN = ('MISKIN', 'Miskin')
-        YATIM = ('YATIM', 'Yatim')
+        AMIL = ('AMIL', 'Amil')
+        MUALAF = ('MUALAF', 'Mualaf')
+        GHARIM = ('GHARIM', 'Gharim')
+        FISABILILLAH = ('FISABILILLAH', 'Fisabilillah')
+        MUSAFIR = ('MUSAFIR', 'Musafir')
 
     class Gender(models.TextChoices):
         LAKILAKI = ('L', 'Laki-Laki')
@@ -23,24 +29,82 @@ class Mustahik(models.Model):
         max_length=32, blank=True, null=True,
         validators=[validate_numeric_character]
     )
-    address = models.TextField()
-    province = models.CharField(max_length=32)
+    address = models.CharField(max_length=255)
+    birthdate = models.DateField()
+    status = models.CharField(max_length=32, choices=Status.choices)
+    gender = models.CharField(max_length=1, choices=Gender.choices)
+    photo = models.FileField(
+        upload_to=os.path.join('images', 'mustahik'),
+        default=os.path.join('images', 'default_photo.jpg')
+    )
+    data_source = models.ForeignKey('DataSource', on_delete=models.CASCADE)
+
+    def calculate_age(self):
+        return timezone.now().year - self.birthdate.year
+
+
+class DataSource(models.Model):
+    class Category(models.TextChoices):
+        WARGA = ('WARGA', 'Warga')
+        INSTITUSI = ('INSTITUSI', 'Institusi')
+        PEKERJA = ('PEKERJA', 'Pekerja')
+
+    pic_name = models.CharField(max_length=150)
+    pic_ktp = models.CharField(
+        max_length=32,
+        validators=[validate_numeric_character]
+    )
+    pic_phone = models.CharField(
+        max_length=32,
+        validators=[validate_numeric_character]
+    )
+    pic_position = models.CharField(max_length=50)
+    category = models.CharField(max_length=32, choices=Category.choices)
+
+    class Meta:
+        unique_together = ('pic_ktp', 'category',)
+
+
+class DataSourceWarga(models.Model):
+    province = models.CharField(max_length=50)
     regency = models.CharField(max_length=50)
+    sub_district = models.CharField(max_length=50)
+    village = models.CharField(max_length=50)
     rt = models.CharField(
-        max_length=4, validators=[validate_numeric_character]
+        max_length=3, validators=[validate_numeric_character]
     )
     rw = models.CharField(
-        max_length=4, validators=[validate_numeric_character]
+        max_length=3, validators=[validate_numeric_character]
     )
-    birthdate = models.DateField()
-    status = models.CharField(
-        max_length=32,
-        choices=Status.choices,
-        default=Status.MISKIN,
+    data_source = models.OneToOneField(
+        'DataSource', on_delete=models.CASCADE,
+        limit_choices_to={'category': DataSource.Category.WARGA}
     )
-    family_size = models.PositiveSmallIntegerField()
-    description = models.TextField(blank=True, null=True)
-    gender = models.CharField(max_length=1, choices=Gender.choices)
 
-    def calculate_age(self):
-        return timezone.now().year - self.birthdate.year
+
+class DataSourceInstitusi(models.Model):
+    name = models.CharField(max_length=150)
+    province = models.CharField(max_length=50)
+    regency = models.CharField(max_length=50)
+    sub_district = models.CharField(max_length=50)
+    village = models.CharField(max_length=50)
+    rt = models.CharField(
+        max_length=3, validators=[validate_numeric_character]
+    )
+    rw = models.CharField(
+        max_length=3, validators=[validate_numeric_character]
+    )
+    address = models.CharField(max_length=255)
+    data_source = models.OneToOneField(
+        'DataSource', on_delete=models.CASCADE,
+        limit_choices_to={'category': DataSource.Category.INSTITUSI}
+    )
+
+
+class DataSourcePekerja(models.Model):
+    profession = models.CharField(max_length=50)
+    location = models.CharField(max_length=50)
+    data_source = models.OneToOneField(
+        'DataSource', on_delete=models.CASCADE,
+        limit_choices_to={'category': DataSource.Category.PEKERJA}
+    )
-- 
GitLab


From 550096b988193b0932f596caf977b540f863288d Mon Sep 17 00:00:00 2001
From: addffa <adli.daffa5@gmail.com>
Date: Wed, 29 Jul 2020 11:14:04 +0700
Subject: [PATCH 29/41] [RED] membuat test query data source

---
 sizakat/mustahik/tests.py | 72 ++++++++++++++++++++++++++++++++++++++-
 1 file changed, 71 insertions(+), 1 deletion(-)

diff --git a/sizakat/mustahik/tests.py b/sizakat/mustahik/tests.py
index 543c738..fabe5d0 100644
--- a/sizakat/mustahik/tests.py
+++ b/sizakat/mustahik/tests.py
@@ -106,7 +106,7 @@ class MustahikGraphQLTestCase(GraphQLTestCase):
             pic_position='test',
             category=DataSource.Category.WARGA
         )
-        institusi_detail = DataSourceWarga.objects.create(
+        data_source_detail = DataSourceWarga.objects.create(
             data_source=data_source_warga,
             province='jakarta',
             regency='jakarta timur',
@@ -353,3 +353,73 @@ class MustahikGraphQLTestCase(GraphQLTestCase):
 
         content = json.loads(response.content)
         self.assertEqual(len(content['data']['mustahiks']), 0)
+
+    def test_data_sources_query_should_return_list_data_sources(self):
+        response = self.query(
+            '''
+            query dataSourcesQuery{
+                dataSources {
+                    id
+                    category
+                    dataSourceDetail {
+                        __typename
+                        ... on DataSourceWargaType {
+                            rt
+                        }
+                        ... on DataSourcePekerjaType {
+                            profession
+                        }
+                        ... on DataSourceInstitusiType {
+                            name
+                        }
+                    }
+                }
+            }
+            ''',
+            op_name='dataSourcesQuery'
+        )
+
+        self.assertResponseNoErrors(response)
+
+        content = json.loads(response.content)
+        self.assertEqual(len(content['data']['dataSources']), 1)
+        self.assertEqual(content['data']['dataSources'][0]['category'], 'WARGA')
+
+        first_data_source = content['data']['dataSources'][0]['dataSourceDetail']
+        self.assertEqual(first_data_source['__typename'], 'DataSourceWargaType')
+        self.assertTrue('rt' in first_data_source.keys())
+
+    def test_data_sources_query_with_category_selection(self):
+        response = self.query(
+            '''
+            query dataSourcesQuery($category: String) {
+                dataSources(category: $category) {
+                    id
+                    category
+                }
+            }
+            ''',
+            op_name='dataSourcesQuery',
+            variables={'category': DataSource.Category.WARGA}
+        )
+
+        content = json.loads(response.content)
+        categories = [source['category'] for source in content['data']['dataSources']]
+        self.assertTrue(all([category == DataSource.Category.WARGA for category in categories]))
+
+    def test_data_source_query_detail_with_given_id(self):
+        response = self.query(
+            '''
+            query dataSourceQuery($id: ID!) {
+                dataSource(id: $id) {
+                    id
+                    picName
+                }
+            }
+            ''',
+            op_name='dataSourceQuery',
+            variables={'id': 1}
+        )
+        content = json.loads(response.content)
+        self.assertEqual(content['data']['dataSource']['id'], '1')
+        self.assertEqual(content['data']['dataSource']['picName'], 'pic test')
-- 
GitLab


From c0e8839725a9f8989017adf307cbafa2925ffd87 Mon Sep 17 00:00:00 2001
From: addffa <adli.daffa5@gmail.com>
Date: Wed, 29 Jul 2020 11:33:35 +0700
Subject: [PATCH 30/41] [GREEN] implementasi query data source model

---
 sizakat/mustahik/query.py | 17 ++++++++++++--
 sizakat/mustahik/types.py | 47 ++++++++++++++++++++++++++++++++++++++-
 2 files changed, 61 insertions(+), 3 deletions(-)

diff --git a/sizakat/mustahik/query.py b/sizakat/mustahik/query.py
index e7e8036..95f8c37 100644
--- a/sizakat/mustahik/query.py
+++ b/sizakat/mustahik/query.py
@@ -3,8 +3,8 @@ import graphene
 from django.db.models import Q
 from functools import reduce
 
-from .models import Mustahik
-from .types import MustahikType
+from .models import Mustahik, DataSource
+from .types import MustahikType, DataSourceType
 
 
 class MustahikQuery(graphene.ObjectType):
@@ -14,6 +14,8 @@ class MustahikQuery(graphene.ObjectType):
         name_contains=graphene.String()
     )
     mustahik = graphene.Field(MustahikType, id=graphene.ID(required=True))
+    data_sources = graphene.List(DataSourceType, category=graphene.String())
+    data_source = graphene.Field(DataSourceType, id=graphene.ID(required=True))
 
     def resolve_mustahiks(self, info, **kwargs):
         statuses = kwargs.get('statuses', None)
@@ -33,3 +35,14 @@ class MustahikQuery(graphene.ObjectType):
 
     def resolve_mustahik(self, info, id):
         return Mustahik.objects.get(pk=id)
+
+    def resolve_data_sources(self, info, **kwargs):
+        category = kwargs.get('category')
+        filter_query = Q()
+        if category:
+            filter_query &= Q(category=category)
+
+        return DataSource.objects.filter(filter_query)
+
+    def resolve_data_source(self, info, id):
+        return DataSource.objects.get(pk=id)
diff --git a/sizakat/mustahik/types.py b/sizakat/mustahik/types.py
index d1d87cb..fdb73dc 100644
--- a/sizakat/mustahik/types.py
+++ b/sizakat/mustahik/types.py
@@ -1,7 +1,10 @@
 import graphene
 
 from graphene_django.types import DjangoObjectType
-from .models import Mustahik
+from .models import (
+    Mustahik, DataSource, DataSourceInstitusi,
+    DataSourcePekerja, DataSourceWarga
+)
 
 
 class MustahikType(DjangoObjectType):
@@ -9,3 +12,45 @@ class MustahikType(DjangoObjectType):
         model = Mustahik
 
     age = graphene.Int(source='calculate_age')
+
+
+class DataSourceInstitusiType(DjangoObjectType):
+    class Meta:
+        model = DataSourceInstitusi
+
+
+class DataSourcePekerjaType(DjangoObjectType):
+    class Meta:
+        model = DataSourcePekerja
+
+
+class DataSourceWargaType(DjangoObjectType):
+    class Meta:
+        model = DataSourceWarga
+
+
+class DataSourceDetailType(graphene.Union):
+    class Meta:
+        types = (
+            DataSourceInstitusiType, DataSourcePekerjaType,
+            DataSourceWargaType
+        )
+        my_attr = True
+
+
+class DataSourceType(DjangoObjectType):
+    class Meta:
+        model = DataSource
+        exclude = (
+            'datasourceinstitusi', 'datasourcepekerja', 'datasourcewarga'
+        )
+
+    data_source_detail = graphene.Field(DataSourceDetailType)
+
+    def resolve_data_source_detail(self, info):
+        if self.category == DataSource.Category.INSTITUSI:
+            return DataSourceInstitusi.objects.get(data_source__pk=self.pk)
+        if self.category == DataSource.Category.PEKERJA:
+            return DataSourcePekerja.objects.get(data_source__pk=self.pk)
+        if self.category == DataSource.Category.WARGA:
+            return DataSourceWarga.objects.get(data_source__pk=self.pk)
-- 
GitLab


From d022550de6376e563410183e959fc360b27dd1ae Mon Sep 17 00:00:00 2001
From: addffa <adli.daffa5@gmail.com>
Date: Wed, 29 Jul 2020 16:45:40 +0700
Subject: [PATCH 31/41] [REFACTOR] memindahkan atribut model data source ke
 model detail data source

---
 sizakat/mustahik/models.py |  17 ++--
 sizakat/mustahik/tests.py  | 155 +++++++++++++++++++------------------
 2 files changed, 90 insertions(+), 82 deletions(-)

diff --git a/sizakat/mustahik/models.py b/sizakat/mustahik/models.py
index 18067f0..be83d10 100644
--- a/sizakat/mustahik/models.py
+++ b/sizakat/mustahik/models.py
@@ -49,6 +49,13 @@ class DataSource(models.Model):
         INSTITUSI = ('INSTITUSI', 'Institusi')
         PEKERJA = ('PEKERJA', 'Pekerja')
 
+    category = models.CharField(max_length=32, choices=Category.choices)
+
+
+class DataSourceDetail(models.Model):
+    class Meta:
+        abstract = True
+
     pic_name = models.CharField(max_length=150)
     pic_ktp = models.CharField(
         max_length=32,
@@ -59,13 +66,9 @@ class DataSource(models.Model):
         validators=[validate_numeric_character]
     )
     pic_position = models.CharField(max_length=50)
-    category = models.CharField(max_length=32, choices=Category.choices)
-
-    class Meta:
-        unique_together = ('pic_ktp', 'category',)
 
 
-class DataSourceWarga(models.Model):
+class DataSourceWarga(DataSourceDetail):
     province = models.CharField(max_length=50)
     regency = models.CharField(max_length=50)
     sub_district = models.CharField(max_length=50)
@@ -82,7 +85,7 @@ class DataSourceWarga(models.Model):
     )
 
 
-class DataSourceInstitusi(models.Model):
+class DataSourceInstitusi(DataSourceDetail):
     name = models.CharField(max_length=150)
     province = models.CharField(max_length=50)
     regency = models.CharField(max_length=50)
@@ -101,7 +104,7 @@ class DataSourceInstitusi(models.Model):
     )
 
 
-class DataSourcePekerja(models.Model):
+class DataSourcePekerja(DataSourceDetail):
     profession = models.CharField(max_length=50)
     location = models.CharField(max_length=50)
     data_source = models.OneToOneField(
diff --git a/sizakat/mustahik/tests.py b/sizakat/mustahik/tests.py
index fabe5d0..d85d0f8 100644
--- a/sizakat/mustahik/tests.py
+++ b/sizakat/mustahik/tests.py
@@ -12,29 +12,13 @@ from .models import DataSource, DataSourceInstitusi, DataSourcePekerja, DataSour
 
 class MustahikModelTestCase(TestCase):
     def setUp(self):
-        data_source_institusi = DataSource.objects.create(
-            pic_name='pic test',
-            pic_ktp='1234567890',
-            pic_phone='0812389120',
-            pic_position='test',
-            category=DataSource.Category.INSTITUSI
-        )
-        institusi_detail = DataSourceInstitusi.objects.create(
-            name='lembaga test',
-            address='jl test',
-            data_source=data_source_institusi,
-            province='jakarta',
-            regency='jakarta timur',
-            sub_district='makasar',
-            village='pinangranti',
-            rt='001',
-            rw='001'
-        )
+        data_source_detail = {
+            'pic_name': 'pic test',
+            'pic_ktp': '1234567890',
+            'pic_phone': '0812389120',
+            'pic_position': 'test',
+        }
         data_source_pekerja = DataSource.objects.create(
-            pic_name='pic test',
-            pic_ktp='1234567891',
-            pic_phone='0812389121',
-            pic_position='test',
             category=DataSource.Category.PEKERJA
         )
         pekerja_detail = DataSourcePekerja.objects.create(
@@ -42,53 +26,26 @@ class MustahikModelTestCase(TestCase):
             profession='tester',
             location='jl tester'
         )
-        data_source_warga = DataSource.objects.create(
-            pic_name='pic test',
-            pic_ktp='1234567892',
-            pic_phone='0812389122',
-            pic_position='test',
-            category=DataSource.Category.WARGA
-        )
-        institusi_detail = DataSourceWarga.objects.create(
-            data_source=data_source_warga,
-            province='jakarta',
-            regency='jakarta timur',
-            sub_district='makasar',
-            village='pinangranti',
-            rt='001',
-            rw='001'
-        )
+        mustahik_base = {
+            'phone': '081234567890',
+            'address': 'Jalan raya depok',
+            'birthdate': date(1987, 6, 5),
+            'status': Mustahik.Status.MISKIN,
+            'gender': Mustahik.Gender.LAKILAKI,
+        }
         mustahik = Mustahik.objects.create(
-            name='mustahik',
-            no_ktp='31751234567890',
-            phone='081234567890',
-            address='Jalan raya depok',
-            birthdate=date(1987, 6, 5),
-            status=Mustahik.Status.MISKIN,
-            gender=Mustahik.Gender.LAKILAKI,
-            data_source=data_source_warga
+            name='mustahik1',
+            no_ktp='31751234567891',
+            data_source=data_source_pekerja,
+            **mustahik_base
         )
 
-    def test_mustahik_creation_from_datasource_institusi(self):
-        mustahik = Mustahik.objects.get(no_ktp='31751234567890')
+    def test_mustahik_creation(self):
+        mustahik = Mustahik.objects.get(no_ktp='31751234567891')
         self.assertTrue(isinstance(mustahik, Mustahik))
 
-    def test_mustahik_change_datasource_to_datasource_pekerja(self):
-        data_source_pekerja = DataSource.objects.get(pic_ktp='1234567891')
-        mustahik = Mustahik.objects.get(no_ktp='31751234567890')
-        mustahik.data_source = data_source_pekerja
-        mustahik.save()
-        self.assertEqual(mustahik.data_source.category, DataSource.Category.PEKERJA)
-
-    def test_mustahik_change_datasource_to_datasource_warga(self):
-        data_source_warga = DataSource.objects.get(pic_ktp='1234567892')
-        mustahik = Mustahik.objects.get(no_ktp='31751234567890')
-        mustahik.data_source = data_source_warga
-        mustahik.save()
-        self.assertEqual(mustahik.data_source.category, DataSource.Category.WARGA)
-
     def test_calculate_mustahik_age(self):
-        mustahik = Mustahik.objects.get(no_ktp='31751234567890')
+        mustahik = Mustahik.objects.get(no_ktp='31751234567891')
         self.assertEqual(
             mustahik.calculate_age(),
             timezone.now().year - mustahik.birthdate.year
@@ -100,14 +57,14 @@ class MustahikGraphQLTestCase(GraphQLTestCase):
 
     def setUp(self):
         data_source_warga = DataSource.objects.create(
-            pic_name='pic test',
-            pic_ktp='1234567892',
-            pic_phone='0812389122',
-            pic_position='test',
             category=DataSource.Category.WARGA
         )
         data_source_detail = DataSourceWarga.objects.create(
             data_source=data_source_warga,
+            pic_name='pic test',
+            pic_ktp='1234567892',
+            pic_phone='0812389122',
+            pic_position='test',
             province='jakarta',
             regency='jakarta timur',
             sub_district='makasar',
@@ -126,9 +83,14 @@ class MustahikGraphQLTestCase(GraphQLTestCase):
             data_source=data_source_warga
         )
 
+    def test_about_query(self):
+        response = self.query('{ about }')
+        self.assertResponseNoErrors(response)
+
     def test_mustahik_mutation_can_add_new_mustahik(self):
         no_ktp = '123891210121'
-        data_source = DataSource.objects.get(pic_ktp='1234567892')
+        data_source_warga = DataSourceWarga.objects.get(pic_ktp='1234567892')
+        data_source = data_source_warga.data_source
         response = self.query(
             '''
             mutation mustahikMutation($input: MustahikMutationInput!) {
@@ -169,7 +131,8 @@ class MustahikGraphQLTestCase(GraphQLTestCase):
 
     def test_mustahik_mutation_can_update_mustahik(self):
         mustahik = Mustahik.objects.get(no_ktp='31751234567890')
-        data_source = mustahik.data_source
+        data_source_warga = DataSourceWarga.objects.get(pic_ktp='1234567892')
+        data_source = data_source_warga.data_source
         mustahik_id = mustahik.pk
         old_status = mustahik.status
         new_status = 'MUSAFIR'
@@ -408,18 +371,60 @@ class MustahikGraphQLTestCase(GraphQLTestCase):
         self.assertTrue(all([category == DataSource.Category.WARGA for category in categories]))
 
     def test_data_source_query_detail_with_given_id(self):
+        data_source_detail = {
+            'pic_name': 'pic test',
+            'pic_ktp': '1234567890',
+            'pic_phone': '0812389120',
+            'pic_position': 'test',
+        }
+        data_source_pekerja = DataSource.objects.create(
+            category=DataSource.Category.PEKERJA
+        )
+        pekerja_detail = DataSourcePekerja.objects.create(
+            data_source=data_source_pekerja,
+            profession='tester',
+            location='jl tester',
+            **data_source_detail
+        )
+        data_source_institusi = DataSource.objects.create(
+            category=DataSource.Category.INSTITUSI
+        )
+        pekerja_detail = DataSourceInstitusi.objects.create(
+            data_source=data_source_institusi,
+            name='test',
+            province='test',
+            regency='test',
+            sub_district='test',
+            village='test',
+            rw='01',
+            rt='01',
+            address='test',
+            **data_source_detail
+        )
         response = self.query(
             '''
-            query dataSourceQuery($id: ID!) {
-                dataSource(id: $id) {
+            query dataSourceQuery($id1: ID!, $id2: ID!, $id3: ID!) {
+                q1: dataSource(id: $id1) {
+                    id
+                    detail: dataSourceDetail { __typename }
+                }
+                q2: dataSource(id: $id2) {
+                    id
+                    detail: dataSourceDetail { __typename }
+                }
+                q3: dataSource(id: $id3) {
                     id
-                    picName
+                    detail: dataSourceDetail { __typename }
                 }
             }
             ''',
             op_name='dataSourceQuery',
-            variables={'id': 1}
+            variables={'id1': 2, 'id2': 3, 'id3': 4}
         )
         content = json.loads(response.content)
-        self.assertEqual(content['data']['dataSource']['id'], '1')
-        self.assertEqual(content['data']['dataSource']['picName'], 'pic test')
+        self.assertEqual(content['data']['q1']['id'], '2')
+        self.assertEqual(content['data']['q1']['detail']['__typename'], 'DataSourceWargaType')
+        self.assertEqual(content['data']['q2']['id'], '3')
+        self.assertEqual(content['data']['q2']['detail']['__typename'], 'DataSourcePekerjaType')
+        self.assertEqual(content['data']['q3']['id'], '4')
+        self.assertEqual(content['data']['q3']['detail']['__typename'], 'DataSourceInstitusiType')
-- 
GitLab


From daef951e435926ce25bdba0c037fcd3fa8579fac Mon Sep 17 00:00:00 2001
From: addffa <adli.daffa5@gmail.com>
Date: Wed, 29 Jul 2020 16:56:34 +0700
Subject: [PATCH 32/41] [GREEN] update migrations file

---
 sizakat/mustahik/migrations/0001_initial.py | 30 +++++++++++++++------
 1 file changed, 22 insertions(+), 8 deletions(-)

diff --git a/sizakat/mustahik/migrations/0001_initial.py b/sizakat/mustahik/migrations/0001_initial.py
index b14b749..c0e1595 100644
--- a/sizakat/mustahik/migrations/0001_initial.py
+++ b/sizakat/mustahik/migrations/0001_initial.py
@@ -1,4 +1,4 @@
-# Generated by Django 3.0.7 on 2020-07-28 10:47
+# Generated by Django 3.0.7 on 2020-07-29 08:26
 
 import django.core.validators
 from django.db import migrations, models
@@ -17,15 +17,8 @@ class Migration(migrations.Migration):
             name='DataSource',
             fields=[
                 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('pic_name', models.CharField(max_length=150)),
-                ('pic_ktp', models.CharField(max_length=32, validators=[django.core.validators.RegexValidator('^[0-9]*$', 'Numeric character only.')])),
-                ('pic_phone', models.CharField(max_length=32, validators=[django.core.validators.RegexValidator('^[0-9]*$', 'Numeric character only.')])),
-                ('pic_position', models.CharField(max_length=50)),
                 ('category', models.CharField(choices=[('WARGA', 'Warga'), ('INSTITUSI', 'Institusi'), ('PEKERJA', 'Pekerja')], max_length=32)),
             ],
-            options={
-                'unique_together': {('pic_ktp', 'category')},
-            },
         ),
         migrations.CreateModel(
             name='Mustahik',
@@ -46,6 +39,10 @@ class Migration(migrations.Migration):
             name='DataSourceWarga',
             fields=[
                 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('pic_name', models.CharField(max_length=150)),
+                ('pic_ktp', models.CharField(max_length=32, validators=[django.core.validators.RegexValidator('^[0-9]*$', 'Numeric character only.')])),
+                ('pic_phone', models.CharField(max_length=32, validators=[django.core.validators.RegexValidator('^[0-9]*$', 'Numeric character only.')])),
+                ('pic_position', models.CharField(max_length=50)),
                 ('province', models.CharField(max_length=50)),
                 ('regency', models.CharField(max_length=50)),
                 ('sub_district', models.CharField(max_length=50)),
@@ -54,20 +51,34 @@ class Migration(migrations.Migration):
                 ('rw', models.CharField(max_length=3, validators=[django.core.validators.RegexValidator('^[0-9]*$', 'Numeric character only.')])),
                 ('data_source', models.OneToOneField(limit_choices_to={'category': 'WARGA'}, on_delete=django.db.models.deletion.CASCADE, to='mustahik.DataSource')),
             ],
+            options={
+                'abstract': False,
+            },
         ),
         migrations.CreateModel(
             name='DataSourcePekerja',
             fields=[
                 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('pic_name', models.CharField(max_length=150)),
+                ('pic_ktp', models.CharField(max_length=32, validators=[django.core.validators.RegexValidator('^[0-9]*$', 'Numeric character only.')])),
+                ('pic_phone', models.CharField(max_length=32, validators=[django.core.validators.RegexValidator('^[0-9]*$', 'Numeric character only.')])),
+                ('pic_position', models.CharField(max_length=50)),
                 ('profession', models.CharField(max_length=50)),
                 ('location', models.CharField(max_length=50)),
                 ('data_source', models.OneToOneField(limit_choices_to={'category': 'PEKERJA'}, on_delete=django.db.models.deletion.CASCADE, to='mustahik.DataSource')),
             ],
+            options={
+                'abstract': False,
+            },
         ),
         migrations.CreateModel(
             name='DataSourceInstitusi',
             fields=[
                 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('pic_name', models.CharField(max_length=150)),
+                ('pic_ktp', models.CharField(max_length=32, validators=[django.core.validators.RegexValidator('^[0-9]*$', 'Numeric character only.')])),
+                ('pic_phone', models.CharField(max_length=32, validators=[django.core.validators.RegexValidator('^[0-9]*$', 'Numeric character only.')])),
+                ('pic_position', models.CharField(max_length=50)),
                 ('name', models.CharField(max_length=150)),
                 ('province', models.CharField(max_length=50)),
                 ('regency', models.CharField(max_length=50)),
@@ -78,5 +89,8 @@ class Migration(migrations.Migration):
                 ('address', models.CharField(max_length=255)),
                 ('data_source', models.OneToOneField(limit_choices_to={'category': 'INSTITUSI'}, on_delete=django.db.models.deletion.CASCADE, to='mustahik.DataSource')),
             ],
+            options={
+                'abstract': False,
+            },
         ),
     ]
-- 
GitLab


From 7d4254e8b4b5f0d8c9c80ebfc41faf52fef35bb2 Mon Sep 17 00:00:00 2001
From: abdurrafiarief <abdurrafiarief@gmail.com>
Date: Thu, 30 Jul 2020 20:10:20 +0700
Subject: [PATCH 33/41] [RED] data source mutation tests

---
 sizakat/mustahik/forms.py     |  57 ++++-
 sizakat/mustahik/mutations.py |  28 ++-
 sizakat/mustahik/tests.py     | 428 ++++++++++++++++++++++++++++++++--
 sizakat/schema.py             |   4 +-
 4 files changed, 496 insertions(+), 21 deletions(-)

diff --git a/sizakat/mustahik/forms.py b/sizakat/mustahik/forms.py
index 485c70e..11351c7 100644
--- a/sizakat/mustahik/forms.py
+++ b/sizakat/mustahik/forms.py
@@ -1,6 +1,6 @@
 from django import forms
 
-from .models import Mustahik
+from .models import Mustahik, DataSource, DataSourceWarga, DataSourceInstitusi, DataSourcePekerja 
 
 
 class MustahikForm(forms.ModelForm):
@@ -16,3 +16,58 @@ class MustahikForm(forms.ModelForm):
             'gender',
             'data_source',
         ]
+
+class DataSourceForm(forms.ModelForm):
+    class Meta:
+        model = DataSource
+        fields = [
+            'category',
+        ]
+
+class DataSourceWargaForm(forms.ModelForm):
+    class Meta:
+        model = DataSourceWarga
+        fields = [
+            'pic_name',
+            'pic_ktp',
+            'pic_phone',
+            'pic_position',
+            'province',
+            'regency',
+            'sub_district',
+            'village',
+            'rt',
+            'rw',
+            'data_source',
+        ]
+
+class DataSourceInstitusiForm(forms.ModelForm):
+    class Meta:
+        model = DataSourceInstitusi
+        fields = [
+            'pic_name',
+            'pic_ktp',
+            'pic_phone',
+            'pic_position',
+            'name',
+            'province',
+            'sub_district',
+            'village',
+            'rt',
+            'rw',
+            'address',
+            'data_source',
+        ]
+
+class DataSourcePekerjaForm(forms.ModelForm):
+    class Meta:
+        model = DataSourcePekerja
+        fields = [
+            'pic_name',
+            'pic_ktp',
+            'pic_phone',
+            'pic_position',
+            'profession',
+            'location',
+            'data_source',
+        ]
\ No newline at end of file
diff --git a/sizakat/mustahik/mutations.py b/sizakat/mustahik/mutations.py
index 48f21d9..439afef 100644
--- a/sizakat/mustahik/mutations.py
+++ b/sizakat/mustahik/mutations.py
@@ -2,9 +2,9 @@ import graphene
 
 from graphene_django.forms.mutation import DjangoModelFormMutation
 
-from .forms import MustahikForm
+from .forms import MustahikForm, DataSourceForm, DataSourceWargaForm, DataSourceInstitusiForm, DataSourcePekerjaForm
 from .models import Mustahik
-from .types import MustahikType
+from .types import MustahikType, DataSourceInstitusiType,  DataSourcePekerjaType, DataSourceWargaType, DataSourceType
 
 
 class MustahikMutation(DjangoModelFormMutation):
@@ -30,3 +30,27 @@ class DeleteMustahik(graphene.Mutation):
         mustahik.delete()
         deleted = True
         return DeleteMustahik(deleted=deleted, id_mustahik=id, name=name, no_ktp=no_ktp)
+
+class DataSourceMutation(DjangoModelFormMutation):
+    dataSource = graphene.Field(DataSourceType)
+
+    class Meta:
+        form_class = DataSourceForm
+
+class DataSourceWargaMutation(DjangoModelFormMutation):
+    dataSourceWarga = graphene.Field(DataSourceWargaType)
+
+    class Meta:
+        form_class = DataSourceWargaForm
+
+class DataSourceInstitusiMutation(DjangoModelFormMutation):
+    dataSourceInstitusi = graphene.Field(DataSourceInstitusiType)
+
+    class Meta:
+        form_class = DataSourceInstitusiForm
+
+class DataSourcePekerjaMutation(DjangoModelFormMutation):
+    dataSourcePekerja = graphene.Field(DataSourcePekerjaType)
+
+    class Meta:
+        form_class = DataSourcePekerjaForm
diff --git a/sizakat/mustahik/tests.py b/sizakat/mustahik/tests.py
index d85d0f8..b41b715 100644
--- a/sizakat/mustahik/tests.py
+++ b/sizakat/mustahik/tests.py
@@ -40,6 +40,43 @@ class MustahikModelTestCase(TestCase):
             **mustahik_base
         )
 
+        data_source_institusi = DataSource.objects.create(
+            category=DataSource.Category.INSTITUSI
+        )
+
+        institusi_detail = DataSourceInstitusi.objects.create(
+            pic_ktp = '12345678901234',
+            pic_name = 'instisusi',
+            pic_phone = '123456789012',
+            pic_position = 'Head',
+            name = 'Institusi Bandung',
+            province = 'Jawa Barat',
+            sub_district = 'Bogor',
+            village = 'Desa',
+            rt = '001',
+            rw = '001',
+            address = 'Jalan suatu desa no 1',
+            data_source = data_source_institusi,
+        )
+
+        data_source_warga = DataSource.objects.create(
+            category=DataSource.Category.WARGA
+        )
+
+        warga_detail = DataSourceWarga.objects.create(
+            pic_ktp = '12345678901111',
+            pic_name = 'wargai',
+            pic_phone = '123456789012',
+            pic_position = 'Ketua RT',
+            province = 'Test Barat',
+            regency = 'Kabupaten test',
+            sub_district = 'Testmatan',
+            village = 'Desa tes',
+            rt = '001',
+            rw = '002',
+            data_source = data_source_warga
+        )
+
     def test_mustahik_creation(self):
         mustahik = Mustahik.objects.get(no_ktp='31751234567891')
         self.assertTrue(isinstance(mustahik, Mustahik))
@@ -51,6 +88,14 @@ class MustahikModelTestCase(TestCase):
             timezone.now().year - mustahik.birthdate.year
         )
 
+    def test_data_source_warga_creation(self):
+        data_source_warga = DataSourceWarga.objects.get(pic_ktp='12345678901111')
+        self.assertTrue(isinstance(data_source_warga, DataSourceWarga))
+    
+    def test_data_source_institusi_creation(self):
+        data_source_institusi = DataSourceInstitusi.objects.get(pic_ktp='12345678901234')
+        self.assertTrue(isinstance(data_source_institusi, DataSourceInstitusi))
+
 
 class MustahikGraphQLTestCase(GraphQLTestCase):
     GRAPHQL_SCHEMA = schema
@@ -83,6 +128,7 @@ class MustahikGraphQLTestCase(GraphQLTestCase):
             data_source=data_source_warga
         )
 
+
     def test_about_query(self):
         response = self.query('{ about }')
         self.assertResponseNoErrors(response)
@@ -371,35 +417,36 @@ class MustahikGraphQLTestCase(GraphQLTestCase):
         self.assertTrue(all([category == DataSource.Category.WARGA for category in categories]))
 
     def test_data_source_query_detail_with_given_id(self):
-        data_source_detail = {
-            'pic_name': 'pic test',
-            'pic_ktp': '1234567890',
-            'pic_phone': '0812389120',
-            'pic_position': 'test',
-        }
+        warga_detail = DataSourceWarga.objects.get(pic_ktp='1234567892')
+
         data_source_pekerja = DataSource.objects.create(
             category=DataSource.Category.PEKERJA
         )
         pekerja_detail = DataSourcePekerja.objects.create(
-            data_source=data_source_pekerja,
+            pic_name='pic test',
+            pic_ktp='1234567890',
+            pic_phone='0812389120',
+            pic_position='test',
             profession='tester',
             location='jl tester',
-            **data_source_detail
+            data_source=data_source_pekerja,
         )
         data_source_institusi = DataSource.objects.create(
             category=DataSource.Category.INSTITUSI
         )
-        pekerja_detail = DataSourceInstitusi.objects.create(
-            data_source=data_source_institusi,
+        institusi_detail = DataSourceInstitusi.objects.create(
+            pic_name='pic test',
+            pic_ktp='1234567891',
+            pic_phone='0812389120',
+            pic_position='test',
             name='test',
             province='test',
-            regency='test',
             sub_district='test',
             village='test',
             rw='01',
             rt='01',
             address='test',
-            **data_source_detail
+            data_source=data_source_institusi,
         )
         response = self.query(
             '''
@@ -419,12 +466,361 @@ class MustahikGraphQLTestCase(GraphQLTestCase):
             }
             ''',
             op_name='dataSourceQuery',
-            variables={'id1': 2, 'id2': 3, 'id3': 4}
+            variables={'id1': warga_detail.data_source.pk, 
+                        'id2': pekerja_detail.data_source.pk, 
+                        'id3': institusi_detail.data_source.pk}
         )
+
+        self.assertResponseNoErrors(response)
+
         content = json.loads(response.content)
-        self.assertEqual(content['data']['q1']['id'], '2')
+        self.assertEqual(content['data']['q1']['id'], str(warga_detail.data_source.pk))
         self.assertEqual(content['data']['q1']['detail']['__typename'], 'DataSourceWargaType')
-        self.assertEqual(content['data']['q2']['id'], '3')
+        self.assertEqual(content['data']['q2']['id'], str(pekerja_detail.data_source.pk))
         self.assertEqual(content['data']['q2']['detail']['__typename'], 'DataSourcePekerjaType')
-        self.assertEqual(content['data']['q3']['id'], '4')
+        self.assertEqual(content['data']['q3']['id'], str(institusi_detail.data_source.pk))
         self.assertEqual(content['data']['q3']['detail']['__typename'], 'DataSourceInstitusiType')
+
+
+    def test_data_source_mutation_can_add_new_data_source(self):
+        existing_data_source_ammount = DataSource.objects.count()
+        response = self.query(
+            '''
+            mutation dataSourceMutation($input: DataSourceMutationInput!){
+                dataSourceMutation(input: $input){
+                    dataSource{
+                        category
+                    }
+                }
+            }
+            ''',
+            op_name='dataSourceMutation',
+            input_data={
+                'category':'WARGA',
+            }
+        )
+
+        #This validates the status code and if you get errors
+        self.assertResponseNoErrors(response)
+
+        #Validate content
+        content = json.loads(response.content)
+        self.assertEqual(content['data']['dataSourceMutation']['dataSource']['category'], "WARGA")
+        
+        #Validate successful save to db
+        new_ammount = existing_data_source_ammount + 1
+        self.assertEqual(DataSource.objects.count(), new_ammount)
+
+    def test_data_source_warga_mutation_can_add_new_data_source_warga(self):
+        pic_ktp = "123456789012"
+        old_ammount = DataSourceWarga.objects.count()
+        data_source_warga = DataSource.objects.create(category=DataSource.Category.WARGA)
+
+        response = self.query(
+            '''
+            mutation dataSourceWargaMutation($input: DataSourceWargaMutationInput!){
+                dataSourceWargaMutation(input: $input){
+                    dataSourceWarga{
+                        picName
+                        picKtp
+                        picPhone
+                        picPosition
+                        province
+                        regency
+                        subDistrict
+                        village
+                        rt
+                        rw
+                        dataSource{id}
+                    }
+                }
+            }
+            ''',
+            op_name='dataSourceWargaMutation',
+            input_data={
+                "picName": "Anjay",
+                "picKtp": pic_ktp,
+                "picPhone": "1231231234",
+                "picPosition": "Head",
+                "province": "Jawa Barat",
+                "regency": "Kebumen",
+                "subDistrict": "Anjaydsitrict",
+                "village": "Desa dusun",
+                "rt": "001",
+                "rw": "002",
+                "dataSource":data_source_warga.pk
+            }
+        )
+        # This validates the status code and if you get errors
+        self.assertResponseNoErrors(response)
+
+        # Validate content
+        content = json.loads(response.content)
+        self.assertEqual(content['data']['dataSourceWargaMutation']
+                        ['dataSourceWarga']['picKtp'], pic_ktp)
+
+        # Validate successful save to db
+        new_ammount = old_ammount + 1
+        self.assertEqual(DataSourceWarga.objects.count(), new_ammount)
+        source_warga = DataSourceWarga.objects.get(pic_ktp=pic_ktp)
+        self.assertEqual(source_warga.pic_name, "Anjay")
+
+    def test_data_source_warga_mutation_can_update_data_source_warga(self):
+        pic_ktp = "123456789012"
+        new_pic_name = "Aryo"
+        data_source_warga = DataSource.objects.create(
+            category=DataSource.Category.WARGA
+        )
+
+        warga_detail = DataSourceWarga.objects.create(
+            pic_ktp = pic_ktp,
+            pic_name = 'wargai',
+            pic_phone = '123456789012',
+            pic_position = 'Ketua RT',
+            province = 'Test Barat',
+            regency = 'Kabupaten test',
+            sub_district = 'Testmatan',
+            village = 'Desa tes',
+            rt = '001',
+            rw = '002',
+            data_source = data_source_warga
+        )
+
+        response = self.query(
+            '''
+            mutation dataSourceWargaMutation($input: DataSourceWargaMutationInput!){
+                dataSourceWargaMutation(input: $input){
+                    dataSourceWarga{
+                        picName
+                    }
+                }
+            }
+            ''',
+            op_name='dataSourceWargaMutation',
+            input_data={
+                "picName": new_pic_name,
+                "picKtp": pic_ktp,
+                "picPhone": "1231231234",
+                "picPosition": "Head",
+                "province": "Jawa Barat",
+                "regency": "Kebumen",
+                "subDistrict": "Anjaydsitrict",
+                "village": "Desa dusun",
+                "rt": "001",
+                "rw": "002",
+                "dataSource":data_source_warga.pk,
+                "id": warga_detail.pk
+            }
+        )
+
+        #Validate success update data source warga
+        source = DataSourceWarga.objects.get(pic_ktp=pic_ktp)
+        self.assertEqual(source.pic_name, new_pic_name)
+
+    def test_data_source_pekerja_mutation_can_add_new_data_source_pekerja(self):
+        pic_ktp = "123456789012"
+        pic_name = "lulu"
+        old_ammount = DataSourcePekerja.objects.count()
+        data_source_pekerja = DataSource.objects.create(
+            category=DataSource.Category.PEKERJA
+        )
+        
+        response = self.query(
+            '''
+            mutation dataSourcePekerjaMutation($input: DataSourcePekerjaMutationInput!){
+                dataSourcePekerjaMutation(input: $input){
+                        dataSourcePekerja{
+                        picName
+                        picKtp
+                        picPhone
+                        picPosition
+                        profession
+                        location
+                        dataSource{id}
+                    }
+                }
+            }
+
+            ''',
+            op_name='dataSourcePekerjaMutation',
+            input_data={
+                "picName": pic_name,
+                "picKtp": pic_ktp,
+                "picPhone": "098765432123",
+                "picPosition": "Leader",
+                "profession": "Programmer",
+                "location": "Bogor",
+                "dataSource": data_source_pekerja.pk,
+            }
+        )
+
+        # This validates the status code and if you get errors
+        self.assertResponseNoErrors(response)
+
+        # Validate content
+        content = json.loads(response.content)
+        self.assertEqual(content['data']['dataSourcePekerjaMutation']
+                        ['dataSourcePekerja']['picKtp'], pic_ktp)
+
+        # Validate successful save to db
+        new_ammount = old_ammount + 1
+        self.assertEqual(DataSourcePekerja.objects.count(), new_ammount)
+        source = DataSourcePekerja.objects.get(pic_ktp=pic_ktp)
+        self.assertEqual(source.pic_name, pic_name)
+
+
+    def test_data_source_pekerja_mutation_can_update_data_source_pekerja(self):
+        pic_ktp = "123456789012"
+        new_pic_name = "Aryo"
+
+        data_source_pekerja = DataSource.objects.create(
+            category=DataSource.Category.PEKERJA
+        )
+        source_pekerja = DataSourcePekerja.objects.create(
+            pic_name = 'wargai',
+            pic_ktp = pic_ktp,
+            pic_phone = '123456789012',
+            pic_position = 'Ketua RT',
+            profession = 'tester',
+            location = 'jl tester',
+            data_source=data_source_pekerja,
+        )
+
+        response = self.query(
+            '''
+            mutation dataSourcePekerjaMutation($input: DataSourcePekerjaMutationInput!){
+                dataSourcePekerjaMutation(input: $input){
+                        dataSourcePekerja{
+                        picName
+                    }
+                }
+            }
+            ''',
+            op_name = "dataSourcePekerjaMutation",
+            input_data={
+                "picName": new_pic_name,
+                "picKtp": pic_ktp,
+                "picPhone": '123456789012',
+                "picPosition": 'Ketua RT',
+                "profession": 'tester',
+                "location": 'jl tester',
+                "dataSource": data_source_pekerja.pk,
+                "id": source_pekerja.pk
+            }
+        )
+
+        #Validate successful update to data source pekerja 
+        source = DataSourcePekerja.objects.get(pic_ktp=pic_ktp)
+        self.assertEqual(source.pic_name, new_pic_name)
+
+    def test_data_source_institusi_mutation_can_add_new_data_source_institusi(self):
+        pic_ktp = "109872901098"
+        pic_name = "Susi"
+        old_ammount = DataSourceInstitusi.objects.count()
+        data_source_institusi = DataSource.objects.create(
+            category=DataSource.Category.INSTITUSI
+        )
+
+        response = self.query(
+            '''
+            mutation dataSourceInstitusiMutation($input: DataSourceInstitusiMutationInput!){
+                dataSourceInstitusiMutation(input: $input){
+                    dataSourceInstitusi{
+                        picName
+                        picKtp
+                        picPhone
+                        picPosition
+                    }
+                }
+            }
+        
+            ''',
+            op_name="dataSourceInstitusiMutation",
+            input_data={
+                "picName": pic_name,
+                "picKtp": pic_ktp,
+                "picPhone": "09876544323",
+                "picPosition": "Vice",
+                "name": "Pesantren Yatim",
+                "province": "Jawa Barat",
+                "subDistrict": "Dusun",
+                "village": "desa",
+                "rt": "002",
+                "rw": "001",
+                "address": "Jalan duku empat 1",
+                "dataSource": data_source_institusi.pk,
+            }
+        )
+
+        # This validates the status code and if you get errors
+        self.assertResponseNoErrors(response)
+
+        #Validate content
+        content = json.loads(response.content)
+        self.assertEqual(content['data']['dataSourceInstitusiMutation']
+                        ['dataSourceInstitusi']['picKtp'], pic_ktp)
+
+        #Validate success save to db
+        new_ammount = old_ammount + 1
+        self.assertEqual(DataSourceInstitusi.objects.count(), new_ammount)
+        source = DataSourceInstitusi.objects.get(pic_ktp=pic_ktp)
+        self.assertEqual(source.pic_name, pic_name)
+
+
+    def test_data_source_institusi_mutation_can_update_data_source_institusi(self):
+        pic_ktp = '12345678901234'
+        new_pic_name = 'Rofi Arief'
+
+        data_source_institusi = DataSource.objects.create(
+            category=DataSource.Category.INSTITUSI
+        )
+
+        source_institusi = DataSourceInstitusi.objects.create(
+            pic_ktp = pic_ktp,
+            pic_name = 'Rofi',
+            pic_phone = '123456789012',
+            pic_position = 'Head',
+            name = 'Institusi Bandung',
+            province = 'Jawa Barat',
+            sub_district = 'Bogor',
+            village = 'Desa',
+            rt = '001',
+            rw = '001',
+            address = 'Jalan suatu desa no 1',
+            data_source = data_source_institusi,
+        )
+
+        response = self.query(
+            '''
+            mutation dataSourceInstitusiMutation($input: DataSourceInstitusiMutationInput!){
+                dataSourceInstitusiMutation(input: $input){
+                    dataSourceInstitusi{
+                        picName
+                        picKtp
+                        picPhone
+                        picPosition
+                    }
+                }
+            }
+            ''',
+            op_name = 'dataSourceInstitusiMutation',
+            input_data = {
+                "picKtp": pic_ktp,
+                "picName": new_pic_name,
+                "picPhone": "123456789012",
+                "picPosition": "Head",
+                "name": "Institusi Bandung",
+                "province": "Jawa Barat",
+                "subDistrict": "Bogor",
+                "village": "Desa",
+                "rt": "001",
+                "rw": "001",
+                "address": "Jalan suatu desa no 1",
+                "dataSource": data_source_institusi.pk,
+                "id": source_institusi.pk,
+            }
+        )
+
+        #Validate success update data source institusi
+        source = DataSourceInstitusi.objects.get(pic_ktp=pic_ktp)
+        self.assertEqual(source.pic_name, new_pic_name)
diff --git a/sizakat/schema.py b/sizakat/schema.py
index 683f54f..9dcc6cd 100644
--- a/sizakat/schema.py
+++ b/sizakat/schema.py
@@ -2,7 +2,7 @@ import graphene
 
 from graphene_django import DjangoObjectType
 
-from .mustahik.mutations import MustahikMutation, DeleteMustahik
+from .mustahik.mutations import MustahikMutation, DeleteMustahik, DataSourceMutation, DataSourceWargaMutation, DataSourceInstitusiMutation, DataSourcePekerjaMutation
 from .mustahik.query import MustahikQuery
 
 ABOUT = ('Si Zakat merupakan sistem informasi untuk membantu masjid dalam '
@@ -20,6 +20,6 @@ class Query(MustahikQuery, graphene.ObjectType):
 class Mutation(graphene.ObjectType):
     mustahik_mutation = MustahikMutation.Field()
     delete_mustahik = DeleteMustahik.Field()
-
+    
 
 schema = graphene.Schema(query=Query, mutation=Mutation)
-- 
GitLab


From b752cb6c8d671a0796cc62240307868ac56480e3 Mon Sep 17 00:00:00 2001
From: abdurrafiarief <abdurrafiarief@gmail.com>
Date: Thu, 30 Jul 2020 20:11:24 +0700
Subject: [PATCH 34/41] [GREEN]Implemented mutation for data sources

---
 sizakat/schema.py | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/sizakat/schema.py b/sizakat/schema.py
index 9dcc6cd..fddb2fa 100644
--- a/sizakat/schema.py
+++ b/sizakat/schema.py
@@ -20,6 +20,10 @@ class Query(MustahikQuery, graphene.ObjectType):
 class Mutation(graphene.ObjectType):
     mustahik_mutation = MustahikMutation.Field()
     delete_mustahik = DeleteMustahik.Field()
-    
+    dataSource_mutation = DataSourceMutation.Field()
+    dataSourceWarga_mutation = DataSourceWargaMutation.Field()
+    dataSourceInstitusi_mutation = DataSourceInstitusiMutation.Field()
+    dataSourcePekerja_mutation = DataSourcePekerjaMutation.Field()
+
 
 schema = graphene.Schema(query=Query, mutation=Mutation)
-- 
GitLab


From 6f594867cb8bcba231b40861f046dae6bd0a5b55 Mon Sep 17 00:00:00 2001
From: addffa <adli.daffa5@gmail.com>
Date: Sat, 1 Aug 2020 20:27:34 +0700
Subject: [PATCH 35/41] [REFACTOR] memindahkan fungsi get detil ke model

---
 sizakat/mustahik/models.py |  8 ++++++++
 sizakat/mustahik/types.py  | 12 +++---------
 sizakat/schema.py          |  6 +++++-
 3 files changed, 16 insertions(+), 10 deletions(-)

diff --git a/sizakat/mustahik/models.py b/sizakat/mustahik/models.py
index be83d10..bf21785 100644
--- a/sizakat/mustahik/models.py
+++ b/sizakat/mustahik/models.py
@@ -51,6 +51,14 @@ class DataSource(models.Model):
 
     category = models.CharField(max_length=32, choices=Category.choices)
 
+    def get_source_detail(self):
+        if self.category == DataSource.Category.INSTITUSI:
+            return DataSourceInstitusi.objects.get(data_source=self)
+        if self.category == DataSource.Category.PEKERJA:
+            return DataSourcePekerja.objects.get(data_source=self)
+        if self.category == DataSource.Category.WARGA:
+            return DataSourceWarga.objects.get(data_source=self)
+
 
 class DataSourceDetail(models.Model):
     class Meta:
diff --git a/sizakat/mustahik/types.py b/sizakat/mustahik/types.py
index fdb73dc..2b1a7a2 100644
--- a/sizakat/mustahik/types.py
+++ b/sizakat/mustahik/types.py
@@ -45,12 +45,6 @@ class DataSourceType(DjangoObjectType):
             'datasourceinstitusi', 'datasourcepekerja', 'datasourcewarga'
         )
 
-    data_source_detail = graphene.Field(DataSourceDetailType)
-
-    def resolve_data_source_detail(self, info):
-        if self.category == DataSource.Category.INSTITUSI:
-            return DataSourceInstitusi.objects.get(data_source__pk=self.pk)
-        if self.category == DataSource.Category.PEKERJA:
-            return DataSourcePekerja.objects.get(data_source__pk=self.pk)
-        if self.category == DataSource.Category.WARGA:
-            return DataSourceWarga.objects.get(data_source__pk=self.pk)
+    data_source_detail = graphene.Field(
+        DataSourceDetailType, source='get_source_detail'
+    )
diff --git a/sizakat/schema.py b/sizakat/schema.py
index fddb2fa..bd0198b 100644
--- a/sizakat/schema.py
+++ b/sizakat/schema.py
@@ -2,7 +2,11 @@ import graphene
 
 from graphene_django import DjangoObjectType
 
-from .mustahik.mutations import MustahikMutation, DeleteMustahik, DataSourceMutation, DataSourceWargaMutation, DataSourceInstitusiMutation, DataSourcePekerjaMutation
+from .mustahik.mutations import (
+    MustahikMutation, DeleteMustahik, DataSourceMutation,
+    DataSourceWargaMutation, DataSourceInstitusiMutation,
+    DataSourcePekerjaMutation
+)
 from .mustahik.query import MustahikQuery
 
 ABOUT = ('Si Zakat merupakan sistem informasi untuk membantu masjid dalam '
-- 
GitLab


From 503ec723ed42c380e06798b08d391777fcf3d896 Mon Sep 17 00:00:00 2001
From: addffa <adli.daffa5@gmail.com>
Date: Sat, 1 Aug 2020 20:39:06 +0700
Subject: [PATCH 36/41] [RED] membuat test delete data source mutation

---
 sizakat/mustahik/tests.py | 173 +++++++++++++++++++++-----------------
 1 file changed, 94 insertions(+), 79 deletions(-)

diff --git a/sizakat/mustahik/tests.py b/sizakat/mustahik/tests.py
index b41b715..e5d262d 100644
--- a/sizakat/mustahik/tests.py
+++ b/sizakat/mustahik/tests.py
@@ -45,18 +45,18 @@ class MustahikModelTestCase(TestCase):
         )
 
         institusi_detail = DataSourceInstitusi.objects.create(
-            pic_ktp = '12345678901234',
-            pic_name = 'instisusi',
-            pic_phone = '123456789012',
-            pic_position = 'Head',
-            name = 'Institusi Bandung',
-            province = 'Jawa Barat',
-            sub_district = 'Bogor',
-            village = 'Desa',
-            rt = '001',
-            rw = '001',
-            address = 'Jalan suatu desa no 1',
-            data_source = data_source_institusi,
+            pic_ktp='12345678901234',
+            pic_name='instisusi',
+            pic_phone='123456789012',
+            pic_position='Head',
+            name='Institusi Bandung',
+            province='Jawa Barat',
+            sub_district='Bogor',
+            village='Desa',
+            rt='001',
+            rw='001',
+            address='Jalan suatu desa no 1',
+            data_source=data_source_institusi,
         )
 
         data_source_warga = DataSource.objects.create(
@@ -64,17 +64,17 @@ class MustahikModelTestCase(TestCase):
         )
 
         warga_detail = DataSourceWarga.objects.create(
-            pic_ktp = '12345678901111',
-            pic_name = 'wargai',
-            pic_phone = '123456789012',
-            pic_position = 'Ketua RT',
-            province = 'Test Barat',
-            regency = 'Kabupaten test',
-            sub_district = 'Testmatan',
-            village = 'Desa tes',
-            rt = '001',
-            rw = '002',
-            data_source = data_source_warga
+            pic_ktp='12345678901111',
+            pic_name='wargai',
+            pic_phone='123456789012',
+            pic_position='Ketua RT',
+            province='Test Barat',
+            regency='Kabupaten test',
+            sub_district='Testmatan',
+            village='Desa tes',
+            rt='001',
+            rw='002',
+            data_source=data_source_warga
         )
 
     def test_mustahik_creation(self):
@@ -91,7 +91,7 @@ class MustahikModelTestCase(TestCase):
     def test_data_source_warga_creation(self):
         data_source_warga = DataSourceWarga.objects.get(pic_ktp='12345678901111')
         self.assertTrue(isinstance(data_source_warga, DataSourceWarga))
-    
+
     def test_data_source_institusi_creation(self):
         data_source_institusi = DataSourceInstitusi.objects.get(pic_ktp='12345678901234')
         self.assertTrue(isinstance(data_source_institusi, DataSourceInstitusi))
@@ -128,7 +128,6 @@ class MustahikGraphQLTestCase(GraphQLTestCase):
             data_source=data_source_warga
         )
 
-
     def test_about_query(self):
         response = self.query('{ about }')
         self.assertResponseNoErrors(response)
@@ -466,9 +465,9 @@ class MustahikGraphQLTestCase(GraphQLTestCase):
             }
             ''',
             op_name='dataSourceQuery',
-            variables={'id1': warga_detail.data_source.pk, 
-                        'id2': pekerja_detail.data_source.pk, 
-                        'id3': institusi_detail.data_source.pk}
+            variables={'id1': warga_detail.data_source.pk,
+                       'id2': pekerja_detail.data_source.pk,
+                       'id3': institusi_detail.data_source.pk}
         )
 
         self.assertResponseNoErrors(response)
@@ -481,7 +480,6 @@ class MustahikGraphQLTestCase(GraphQLTestCase):
         self.assertEqual(content['data']['q3']['id'], str(institusi_detail.data_source.pk))
         self.assertEqual(content['data']['q3']['detail']['__typename'], 'DataSourceInstitusiType')
 
-
     def test_data_source_mutation_can_add_new_data_source(self):
         existing_data_source_ammount = DataSource.objects.count()
         response = self.query(
@@ -496,18 +494,18 @@ class MustahikGraphQLTestCase(GraphQLTestCase):
             ''',
             op_name='dataSourceMutation',
             input_data={
-                'category':'WARGA',
+                'category': 'WARGA',
             }
         )
 
-        #This validates the status code and if you get errors
+        # This validates the status code and if you get errors
         self.assertResponseNoErrors(response)
 
-        #Validate content
+        # Validate content
         content = json.loads(response.content)
         self.assertEqual(content['data']['dataSourceMutation']['dataSource']['category'], "WARGA")
-        
-        #Validate successful save to db
+
+        # Validate successful save to db
         new_ammount = existing_data_source_ammount + 1
         self.assertEqual(DataSource.objects.count(), new_ammount)
 
@@ -548,7 +546,7 @@ class MustahikGraphQLTestCase(GraphQLTestCase):
                 "village": "Desa dusun",
                 "rt": "001",
                 "rw": "002",
-                "dataSource":data_source_warga.pk
+                "dataSource": data_source_warga.pk
             }
         )
         # This validates the status code and if you get errors
@@ -557,7 +555,7 @@ class MustahikGraphQLTestCase(GraphQLTestCase):
         # Validate content
         content = json.loads(response.content)
         self.assertEqual(content['data']['dataSourceWargaMutation']
-                        ['dataSourceWarga']['picKtp'], pic_ktp)
+                         ['dataSourceWarga']['picKtp'], pic_ktp)
 
         # Validate successful save to db
         new_ammount = old_ammount + 1
@@ -573,17 +571,17 @@ class MustahikGraphQLTestCase(GraphQLTestCase):
         )
 
         warga_detail = DataSourceWarga.objects.create(
-            pic_ktp = pic_ktp,
-            pic_name = 'wargai',
-            pic_phone = '123456789012',
-            pic_position = 'Ketua RT',
-            province = 'Test Barat',
-            regency = 'Kabupaten test',
-            sub_district = 'Testmatan',
-            village = 'Desa tes',
-            rt = '001',
-            rw = '002',
-            data_source = data_source_warga
+            pic_ktp=pic_ktp,
+            pic_name='wargai',
+            pic_phone='123456789012',
+            pic_position='Ketua RT',
+            province='Test Barat',
+            regency='Kabupaten test',
+            sub_district='Testmatan',
+            village='Desa tes',
+            rt='001',
+            rw='002',
+            data_source=data_source_warga
         )
 
         response = self.query(
@@ -608,12 +606,12 @@ class MustahikGraphQLTestCase(GraphQLTestCase):
                 "village": "Desa dusun",
                 "rt": "001",
                 "rw": "002",
-                "dataSource":data_source_warga.pk,
+                "dataSource": data_source_warga.pk,
                 "id": warga_detail.pk
             }
         )
 
-        #Validate success update data source warga
+        # Validate success update data source warga
         source = DataSourceWarga.objects.get(pic_ktp=pic_ktp)
         self.assertEqual(source.pic_name, new_pic_name)
 
@@ -624,7 +622,7 @@ class MustahikGraphQLTestCase(GraphQLTestCase):
         data_source_pekerja = DataSource.objects.create(
             category=DataSource.Category.PEKERJA
         )
-        
+
         response = self.query(
             '''
             mutation dataSourcePekerjaMutation($input: DataSourcePekerjaMutationInput!){
@@ -660,7 +658,7 @@ class MustahikGraphQLTestCase(GraphQLTestCase):
         # Validate content
         content = json.loads(response.content)
         self.assertEqual(content['data']['dataSourcePekerjaMutation']
-                        ['dataSourcePekerja']['picKtp'], pic_ktp)
+                         ['dataSourcePekerja']['picKtp'], pic_ktp)
 
         # Validate successful save to db
         new_ammount = old_ammount + 1
@@ -668,7 +666,6 @@ class MustahikGraphQLTestCase(GraphQLTestCase):
         source = DataSourcePekerja.objects.get(pic_ktp=pic_ktp)
         self.assertEqual(source.pic_name, pic_name)
 
-
     def test_data_source_pekerja_mutation_can_update_data_source_pekerja(self):
         pic_ktp = "123456789012"
         new_pic_name = "Aryo"
@@ -677,12 +674,12 @@ class MustahikGraphQLTestCase(GraphQLTestCase):
             category=DataSource.Category.PEKERJA
         )
         source_pekerja = DataSourcePekerja.objects.create(
-            pic_name = 'wargai',
-            pic_ktp = pic_ktp,
-            pic_phone = '123456789012',
-            pic_position = 'Ketua RT',
-            profession = 'tester',
-            location = 'jl tester',
+            pic_name='wargai',
+            pic_ktp=pic_ktp,
+            pic_phone='123456789012',
+            pic_position='Ketua RT',
+            profession='tester',
+            location='jl tester',
             data_source=data_source_pekerja,
         )
 
@@ -696,7 +693,7 @@ class MustahikGraphQLTestCase(GraphQLTestCase):
                 }
             }
             ''',
-            op_name = "dataSourcePekerjaMutation",
+            op_name="dataSourcePekerjaMutation",
             input_data={
                 "picName": new_pic_name,
                 "picKtp": pic_ktp,
@@ -709,7 +706,7 @@ class MustahikGraphQLTestCase(GraphQLTestCase):
             }
         )
 
-        #Validate successful update to data source pekerja 
+        # Validate successful update to data source pekerja
         source = DataSourcePekerja.objects.get(pic_ktp=pic_ktp)
         self.assertEqual(source.pic_name, new_pic_name)
 
@@ -755,18 +752,17 @@ class MustahikGraphQLTestCase(GraphQLTestCase):
         # This validates the status code and if you get errors
         self.assertResponseNoErrors(response)
 
-        #Validate content
+        # Validate content
         content = json.loads(response.content)
         self.assertEqual(content['data']['dataSourceInstitusiMutation']
-                        ['dataSourceInstitusi']['picKtp'], pic_ktp)
+                         ['dataSourceInstitusi']['picKtp'], pic_ktp)
 
-        #Validate success save to db
+        # Validate success save to db
         new_ammount = old_ammount + 1
         self.assertEqual(DataSourceInstitusi.objects.count(), new_ammount)
         source = DataSourceInstitusi.objects.get(pic_ktp=pic_ktp)
         self.assertEqual(source.pic_name, pic_name)
 
-
     def test_data_source_institusi_mutation_can_update_data_source_institusi(self):
         pic_ktp = '12345678901234'
         new_pic_name = 'Rofi Arief'
@@ -776,18 +772,18 @@ class MustahikGraphQLTestCase(GraphQLTestCase):
         )
 
         source_institusi = DataSourceInstitusi.objects.create(
-            pic_ktp = pic_ktp,
-            pic_name = 'Rofi',
-            pic_phone = '123456789012',
-            pic_position = 'Head',
-            name = 'Institusi Bandung',
-            province = 'Jawa Barat',
-            sub_district = 'Bogor',
-            village = 'Desa',
-            rt = '001',
-            rw = '001',
-            address = 'Jalan suatu desa no 1',
-            data_source = data_source_institusi,
+            pic_ktp=pic_ktp,
+            pic_name='Rofi',
+            pic_phone='123456789012',
+            pic_position='Head',
+            name='Institusi Bandung',
+            province='Jawa Barat',
+            sub_district='Bogor',
+            village='Desa',
+            rt='001',
+            rw='001',
+            address='Jalan suatu desa no 1',
+            data_source=data_source_institusi,
         )
 
         response = self.query(
@@ -803,8 +799,8 @@ class MustahikGraphQLTestCase(GraphQLTestCase):
                 }
             }
             ''',
-            op_name = 'dataSourceInstitusiMutation',
-            input_data = {
+            op_name='dataSourceInstitusiMutation',
+            input_data={
                 "picKtp": pic_ktp,
                 "picName": new_pic_name,
                 "picPhone": "123456789012",
@@ -821,6 +817,25 @@ class MustahikGraphQLTestCase(GraphQLTestCase):
             }
         )
 
-        #Validate success update data source institusi
+        # Validate success update data source institusi
         source = DataSourceInstitusi.objects.get(pic_ktp=pic_ktp)
         self.assertEqual(source.pic_name, new_pic_name)
+
+    def test_delete_datasource_mutations_can_delete_datasource(self):
+        count = DataSource.objects.count()
+        datasource = DataSource.objects.all()[0]
+        response = self.query(
+            '''
+                mutation deleteDataSource($id: ID) {
+                    deleteDataSource(id: $id) {
+                        deleted
+                    }
+                }
+            ''',
+            op_name='deleteDataSource',
+            variables={'id': datasource.pk}
+        )
+        self.assertResponseNoErrors(response)
+        content = json.loads(response.content)
+        self.assertTrue(content['data']['deleteDataSource']['deleted'])
+        self.assertEquals(DataSource.objects.count(), count-1)
-- 
GitLab


From 3b25d162688daf1d4adab77ace6bb7358dde94c3 Mon Sep 17 00:00:00 2001
From: addffa <adli.daffa5@gmail.com>
Date: Sat, 1 Aug 2020 20:39:49 +0700
Subject: [PATCH 37/41] [GREEN] implementasi delete datasource mutation

---
 sizakat/mustahik/mutations.py | 27 ++++++++++++++++++++++++---
 sizakat/schema.py             | 11 ++++++-----
 2 files changed, 30 insertions(+), 8 deletions(-)

diff --git a/sizakat/mustahik/mutations.py b/sizakat/mustahik/mutations.py
index 439afef..1b0b6ab 100644
--- a/sizakat/mustahik/mutations.py
+++ b/sizakat/mustahik/mutations.py
@@ -2,9 +2,15 @@ import graphene
 
 from graphene_django.forms.mutation import DjangoModelFormMutation
 
-from .forms import MustahikForm, DataSourceForm, DataSourceWargaForm, DataSourceInstitusiForm, DataSourcePekerjaForm
-from .models import Mustahik
-from .types import MustahikType, DataSourceInstitusiType,  DataSourcePekerjaType, DataSourceWargaType, DataSourceType
+from .forms import (
+    MustahikForm, DataSourceForm, DataSourceWargaForm,
+    DataSourceInstitusiForm, DataSourcePekerjaForm
+)
+from .models import Mustahik, DataSource
+from .types import (
+    MustahikType, DataSourceInstitusiType,
+    DataSourcePekerjaType, DataSourceWargaType, DataSourceType
+)
 
 
 class MustahikMutation(DjangoModelFormMutation):
@@ -31,24 +37,39 @@ class DeleteMustahik(graphene.Mutation):
         deleted = True
         return DeleteMustahik(deleted=deleted, id_mustahik=id, name=name, no_ktp=no_ktp)
 
+
 class DataSourceMutation(DjangoModelFormMutation):
     dataSource = graphene.Field(DataSourceType)
 
     class Meta:
         form_class = DataSourceForm
 
+
+class DeleteDataSource(graphene.Mutation):
+    class Arguments:
+        id = graphene.ID()
+
+    deleted = graphene.Boolean()
+
+    def mutate(self, info, id):
+        DataSource.objects.get(pk=id).delete()
+        return DeleteDataSource(deleted=True)
+
+
 class DataSourceWargaMutation(DjangoModelFormMutation):
     dataSourceWarga = graphene.Field(DataSourceWargaType)
 
     class Meta:
         form_class = DataSourceWargaForm
 
+
 class DataSourceInstitusiMutation(DjangoModelFormMutation):
     dataSourceInstitusi = graphene.Field(DataSourceInstitusiType)
 
     class Meta:
         form_class = DataSourceInstitusiForm
 
+
 class DataSourcePekerjaMutation(DjangoModelFormMutation):
     dataSourcePekerja = graphene.Field(DataSourcePekerjaType)
 
diff --git a/sizakat/schema.py b/sizakat/schema.py
index bd0198b..9c8325d 100644
--- a/sizakat/schema.py
+++ b/sizakat/schema.py
@@ -5,7 +5,7 @@ from graphene_django import DjangoObjectType
 from .mustahik.mutations import (
     MustahikMutation, DeleteMustahik, DataSourceMutation,
     DataSourceWargaMutation, DataSourceInstitusiMutation,
-    DataSourcePekerjaMutation
+    DataSourcePekerjaMutation, DeleteDataSource
 )
 from .mustahik.query import MustahikQuery
 
@@ -24,10 +24,11 @@ class Query(MustahikQuery, graphene.ObjectType):
 class Mutation(graphene.ObjectType):
     mustahik_mutation = MustahikMutation.Field()
     delete_mustahik = DeleteMustahik.Field()
-    dataSource_mutation = DataSourceMutation.Field()
-    dataSourceWarga_mutation = DataSourceWargaMutation.Field()
-    dataSourceInstitusi_mutation = DataSourceInstitusiMutation.Field()
-    dataSourcePekerja_mutation = DataSourcePekerjaMutation.Field()
+    data_source_mutation = DataSourceMutation.Field()
+    data_source_warga_mutation = DataSourceWargaMutation.Field()
+    data_source_institusi_mutation = DataSourceInstitusiMutation.Field()
+    data_source_pekerja_mutation = DataSourcePekerjaMutation.Field()
+    delete_data_source = DeleteDataSource.Field()
 
 
 schema = graphene.Schema(query=Query, mutation=Mutation)
-- 
GitLab


From 6d9d07531ff7f6901daff4db2cef0e80abaeafcd Mon Sep 17 00:00:00 2001
From: addffa <adli.daffa5@gmail.com>
Date: Sat, 1 Aug 2020 20:54:21 +0700
Subject: [PATCH 38/41] [REFACTOR] revisi fungsi delete mutation

---
 sizakat/mustahik/mutations.py | 15 ++++-----------
 sizakat/mustahik/tests.py     |  7 ++-----
 2 files changed, 6 insertions(+), 16 deletions(-)

diff --git a/sizakat/mustahik/mutations.py b/sizakat/mustahik/mutations.py
index 1b0b6ab..f3a993c 100644
--- a/sizakat/mustahik/mutations.py
+++ b/sizakat/mustahik/mutations.py
@@ -22,20 +22,13 @@ class MustahikMutation(DjangoModelFormMutation):
 
 class DeleteMustahik(graphene.Mutation):
     class Arguments:
-        id = graphene.ID()
+        id = graphene.ID(required=True)
 
     deleted = graphene.Boolean()
-    id_mustahik = graphene.ID()
-    name = graphene.String()
-    no_ktp = graphene.String()
 
     def mutate(self, info, id):
-        mustahik = Mustahik.objects.get(pk=id)
-        name = mustahik.name
-        no_ktp = mustahik.no_ktp
-        mustahik.delete()
-        deleted = True
-        return DeleteMustahik(deleted=deleted, id_mustahik=id, name=name, no_ktp=no_ktp)
+        Mustahik.objects.get(pk=id).delete()
+        return DeleteMustahik(deleted=True)
 
 
 class DataSourceMutation(DjangoModelFormMutation):
@@ -47,7 +40,7 @@ class DataSourceMutation(DjangoModelFormMutation):
 
 class DeleteDataSource(graphene.Mutation):
     class Arguments:
-        id = graphene.ID()
+        id = graphene.ID(required=True)
 
     deleted = graphene.Boolean()
 
diff --git a/sizakat/mustahik/tests.py b/sizakat/mustahik/tests.py
index e5d262d..bf74be9 100644
--- a/sizakat/mustahik/tests.py
+++ b/sizakat/mustahik/tests.py
@@ -278,12 +278,9 @@ class MustahikGraphQLTestCase(GraphQLTestCase):
         mustahik_id = mustahik.pk
         response = self.query(
             '''
-                mutation deleteMustahik($id: ID) {
+                mutation deleteMustahik($id: ID!) {
                     deleteMustahik(id: $id) {
                         deleted
-                        idMustahik
-                        name
-                        noKtp
                     }
                 }
             ''',
@@ -826,7 +823,7 @@ class MustahikGraphQLTestCase(GraphQLTestCase):
         datasource = DataSource.objects.all()[0]
         response = self.query(
             '''
-                mutation deleteDataSource($id: ID) {
+                mutation deleteDataSource($id: ID!) {
                     deleteDataSource(id: $id) {
                         deleted
                     }
-- 
GitLab


From 6123747aa5e11ab7807dee145edb75d9bbe754d7 Mon Sep 17 00:00:00 2001
From: addffa <adli.daffa5@gmail.com>
Date: Sun, 2 Aug 2020 22:50:01 +0700
Subject: [PATCH 39/41] [GREEN] implementasi upload photo

---
 sizakat/mustahik/forms.py     |  9 +++++++--
 sizakat/mustahik/mutations.py | 19 +++++++++++++++++++
 sizakat/validators.py         |  6 ++++++
 3 files changed, 32 insertions(+), 2 deletions(-)

diff --git a/sizakat/mustahik/forms.py b/sizakat/mustahik/forms.py
index 11351c7..9af65d6 100644
--- a/sizakat/mustahik/forms.py
+++ b/sizakat/mustahik/forms.py
@@ -1,6 +1,6 @@
 from django import forms
 
-from .models import Mustahik, DataSource, DataSourceWarga, DataSourceInstitusi, DataSourcePekerja 
+from .models import Mustahik, DataSource, DataSourceWarga, DataSourceInstitusi, DataSourcePekerja
 
 
 class MustahikForm(forms.ModelForm):
@@ -15,8 +15,10 @@ class MustahikForm(forms.ModelForm):
             'status',
             'gender',
             'data_source',
+            'photo',
         ]
 
+
 class DataSourceForm(forms.ModelForm):
     class Meta:
         model = DataSource
@@ -24,6 +26,7 @@ class DataSourceForm(forms.ModelForm):
             'category',
         ]
 
+
 class DataSourceWargaForm(forms.ModelForm):
     class Meta:
         model = DataSourceWarga
@@ -41,6 +44,7 @@ class DataSourceWargaForm(forms.ModelForm):
             'data_source',
         ]
 
+
 class DataSourceInstitusiForm(forms.ModelForm):
     class Meta:
         model = DataSourceInstitusi
@@ -59,6 +63,7 @@ class DataSourceInstitusiForm(forms.ModelForm):
             'data_source',
         ]
 
+
 class DataSourcePekerjaForm(forms.ModelForm):
     class Meta:
         model = DataSourcePekerja
@@ -70,4 +75,4 @@ class DataSourcePekerjaForm(forms.ModelForm):
             'profession',
             'location',
             'data_source',
-        ]
\ No newline at end of file
+        ]
diff --git a/sizakat/mustahik/mutations.py b/sizakat/mustahik/mutations.py
index f3a993c..46375b9 100644
--- a/sizakat/mustahik/mutations.py
+++ b/sizakat/mustahik/mutations.py
@@ -1,6 +1,8 @@
 import graphene
 
 from graphene_django.forms.mutation import DjangoModelFormMutation
+from graphene_django.types import ErrorType
+from sizakat.validators import validate_photo
 
 from .forms import (
     MustahikForm, DataSourceForm, DataSourceWargaForm,
@@ -19,6 +21,23 @@ class MustahikMutation(DjangoModelFormMutation):
     class Meta:
         form_class = MustahikForm
 
+    @classmethod
+    def mutate_and_get_payload(cls, root, info, **input):
+        form = cls.get_form(root, info, **input)
+        photo = info.context.FILES['photo']
+        if not validate_photo(photo):
+            form.add_error('photo', 'invalid photo format')
+
+        if form.is_valid():
+            mustahik = form.save(commit=False)
+            mustahik.photo = photo
+            mustahik.save()
+            kwargs = {cls._meta.return_field_name: mustahik}
+            return cls(errors=[], **kwargs)
+        else:
+            errors = ErrorType.from_errors(form.errors)
+            return cls(errors=errors)
+
 
 class DeleteMustahik(graphene.Mutation):
     class Arguments:
diff --git a/sizakat/validators.py b/sizakat/validators.py
index 7b55e88..0d042ed 100644
--- a/sizakat/validators.py
+++ b/sizakat/validators.py
@@ -3,3 +3,9 @@ from django.core.validators import RegexValidator
 validate_numeric_character = RegexValidator(
     r'^[0-9]*$', 'Numeric character only.'
 )
+
+
+def validate_photo(photo):
+    valid_extensions = ['jpg', 'png']
+    photo_extension = photo.name.split('.')[-1]
+    return photo_extension in valid_extensions
-- 
GitLab


From 92fe44bacf0767502558d58291732314540198a8 Mon Sep 17 00:00:00 2001
From: addffa <adli.daffa5@gmail.com>
Date: Mon, 3 Aug 2020 08:34:53 +0700
Subject: [PATCH 40/41] [REFACTOR] mengubah mutasi upload photo

---
 sizakat/mustahik/mutations.py | 7 ++++---
 sizakat/mustahik/tests.py     | 2 ++
 2 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/sizakat/mustahik/mutations.py b/sizakat/mustahik/mutations.py
index 46375b9..166bb3a 100644
--- a/sizakat/mustahik/mutations.py
+++ b/sizakat/mustahik/mutations.py
@@ -24,13 +24,14 @@ class MustahikMutation(DjangoModelFormMutation):
     @classmethod
     def mutate_and_get_payload(cls, root, info, **input):
         form = cls.get_form(root, info, **input)
-        photo = info.context.FILES['photo']
-        if not validate_photo(photo):
+        photo = info.context.FILES.get('photo', None)
+        if photo and not validate_photo(photo):
             form.add_error('photo', 'invalid photo format')
 
         if form.is_valid():
             mustahik = form.save(commit=False)
-            mustahik.photo = photo
+            if photo:
+                mustahik.photo = photo
             mustahik.save()
             kwargs = {cls._meta.return_field_name: mustahik}
             return cls(errors=[], **kwargs)
diff --git a/sizakat/mustahik/tests.py b/sizakat/mustahik/tests.py
index bf74be9..efb4698 100644
--- a/sizakat/mustahik/tests.py
+++ b/sizakat/mustahik/tests.py
@@ -157,6 +157,7 @@ class MustahikGraphQLTestCase(GraphQLTestCase):
                 "birthdate": "1998-03-12",
                 "status": "MISKIN",
                 "gender": "L",
+                "photo": "",
                 "dataSource": data_source.pk
             }
         )
@@ -205,6 +206,7 @@ class MustahikGraphQLTestCase(GraphQLTestCase):
                 "birthdate": "1987-06-05",
                 "status": "MUSAFIR",
                 "gender": "L",
+                "photo": "",
                 "id": mustahik.pk,
                 "dataSource": data_source.pk
             }
-- 
GitLab


From a7529d0a8cdaa433fe1fcfcfe8f85c6f2409d4a4 Mon Sep 17 00:00:00 2001
From: addffa <adli.daffa5@gmail.com>
Date: Mon, 3 Aug 2020 08:35:32 +0700
Subject: [PATCH 41/41] [CHORES] menambahkan konfigurasi media file

---
 sizakat/settings.py | 4 +++-
 sizakat/urls.py     | 7 +++++++
 2 files changed, 10 insertions(+), 1 deletion(-)

diff --git a/sizakat/settings.py b/sizakat/settings.py
index 9f583db..0e562c2 100644
--- a/sizakat/settings.py
+++ b/sizakat/settings.py
@@ -139,4 +139,6 @@ USE_TZ = True
 # https://docs.djangoproject.com/en/3.0/howto/static-files/
 
 STATIC_URL = '/static/'
-STATIC_ROOT = os.path.join(BASE_DIR, "staticfiles")
+STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
+MEDIA_URL = '/img/'
+MEDIA_ROOT = os.path.join(BASE_DIR, 'images')
diff --git a/sizakat/urls.py b/sizakat/urls.py
index b1ae6ca..7b47e4e 100644
--- a/sizakat/urls.py
+++ b/sizakat/urls.py
@@ -13,6 +13,8 @@ Including another URLconf
     1. Import the include() function: from django.urls import include, path
     2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
 """
+from django.conf import settings
+from django.conf.urls.static import static
 from django.contrib import admin
 from django.urls import path
 from django.views.decorators.csrf import csrf_exempt
@@ -22,3 +24,8 @@ urlpatterns = [
     path('admin/', admin.site.urls),
     path('graphql/', csrf_exempt(GraphQLView.as_view(graphiql=True))),
 ]
+
+if settings.DEBUG:
+    urlpatterns += static(
+        settings.MEDIA_URL, document_root=settings.MEDIA_ROOT
+    )
-- 
GitLab