diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 40af2c49879743f0f0fee25367178727bb23dd1b..2407079397556a6ed3efa0f28c2c01e0ddc8a13d 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -100,8 +100,11 @@ Dev Docker Image:
     - .docker-image
     - .development
   allow_failure: true
+  when: manual
 
 Prod Docker Image:
   extends:
     - .docker-image
-    - .production
\ No newline at end of file
+    - .production
+  when: manual
+  allow_failure: true
\ No newline at end of file
diff --git a/administration/choices.py b/administration/choices.py
index b4b352705d0da75888e1d33100826f0a2955e2d0..5b87902fa0272d14adf1d2a03e9deda56a0ad733 100644
--- a/administration/choices.py
+++ b/administration/choices.py
@@ -18,7 +18,7 @@ BASE_PERIODE = [
 ]
 
 
-def genereatePeriodeChoices():
+def generate_periode_choices():
     periode = BASE_PERIODE
     now = timezone.now()
     # Month
diff --git a/administration/forms.py b/administration/forms.py
index 3c350d456e8a335027f923edb5c008e01af802bc..3d6c08fb34a406bd3f64066fd06917ac3e39b5cd 100644
--- a/administration/forms.py
+++ b/administration/forms.py
@@ -3,7 +3,7 @@ from crispy_forms.layout import Layout, Submit, Row, Column, Reset, ButtonHolder
 from crispy_forms.bootstrap import InlineCheckboxes
 from django import forms
 
-from administration.choices import genereatePeriodeChoices
+from administration.choices import generate_periode_choices
 from administration.models import VerificationSetting
 from app.models import Category
 from authentication.models import User
@@ -88,7 +88,7 @@ class RegistrasiAdminForm(forms.ModelForm):
 
 class PeriodForm(forms.Form):
     period = forms.ChoiceField(
-        choices=genereatePeriodeChoices(), label="Periode", required=False)
+        choices=generate_periode_choices(), label="Periode", required=False)
     start_date = forms.DateField(
         widget=DateInput, label="Waktu mulai", required=False)
     end_date = forms.DateField(
@@ -102,14 +102,16 @@ class PeriodForm(forms.Form):
         for field_name, field in self.fields.items():
             field.widget.attrs['class'] = 'form-control'
 
+        period_css_class = 'form-group col-md-4 mb-0'
+
         self.helper = FormHelper()
         self.helper.attrs["name"] = "filter-form"
         self.helper.form_method = 'get'
         self.helper.layout = Layout(
             Row(
-                Column('period', css_class='form-group col-md-4 mb-0'),
-                Column('start_date', css_class='form-group col-md-4 mb-0'),
-                Column('end_date', css_class='form-group col-md-4 mb-0'),
+                Column('period', css_class=period_css_class),
+                Column('start_date', css_class=period_css_class),
+                Column('end_date', css_class=period_css_class),
                 css_class='form-row'
             ),
             InlineCheckboxes('categories'),
@@ -130,10 +132,9 @@ class PeriodForm(forms.Form):
         if start_date is None and end_date is not None:
             self.add_error("start_date",
                            "masukan waktu mulai")
-        if start_date is not None and end_date is not None:
-            if start_date > end_date:
-                self.add_error("end_date",
-                               "waktu selesai sebelum waktu mulai")
+        if start_date is not None and end_date is not None and start_date > end_date:
+            self.add_error("end_date",
+                            "waktu selesai sebelum waktu mulai")
 
 
 class EditAdminStatusForm(forms.ModelForm):
diff --git a/administration/templates/administration/data_statistik.html b/administration/templates/administration/data_statistik.html
index 17df72575d35c2a457fe0ae1500c4af991e0abdd..6550ab09f0da645981f302b133620c4e5a691c02 100644
--- a/administration/templates/administration/data_statistik.html
+++ b/administration/templates/administration/data_statistik.html
@@ -97,20 +97,6 @@
             <!-- Card Header - Dropdown -->
             <div class="card-header py-3 d-flex flex-row align-items-center justify-content-between">
                 <h6 class="m-0 font-weight-bold text-primary">Statistik</h6>
-                <!-- <div class="dropdown no-arrow">
-                    <a class="dropdown-toggle" href="#" role="button" id="dropdownMenuLink" data-toggle="dropdown"
-                        aria-haspopup="true" aria-expanded="false">
-                        <em class="fas fa-ellipsis-v fa-sm fa-fw text-gray-400"></em>
-                    </a>
-                    <div class="dropdown-menu dropdown-menu-right shadow animated--fade-in"
-                        aria-labelledby="dropdownMenuLink">
-                        <div class="dropdown-header">Dropdown Header:</div>
-                        <a class="dropdown-item" href="#">Action</a>
-                        <a class="dropdown-item" href="#">Another action</a>
-                        <div class="dropdown-divider"></div>
-                        <a class="dropdown-item" href="#">Something else here</a>
-                    </div>
-                </div> -->
             </div>
             <!-- Card Body -->
             <div class="card-body" style="overflow:auto!important; display:inline-block!important; height: 750px;">
@@ -128,7 +114,7 @@
         </div>
         <div class="card-body">
             <div class="table-responsive">
-                <table class="table table-bordered" id="dataTableRiwayat" width="100%" cellspacing="0"
+                <table class="table table-bordered" id="dataTableRiwayat"
                     aria-describedby="titleTabelRiwayat">
                     <thead>
                         <tr>
diff --git a/administration/templates/kelola_kontributor.html b/administration/templates/kelola_kontributor.html
index 9fee87a117739e6a0c9b1c6f06b6976a87d7a621..2732d032cea4b89f97c85ff29ee83efe4982f2a0 100644
--- a/administration/templates/kelola_kontributor.html
+++ b/administration/templates/kelola_kontributor.html
@@ -17,7 +17,7 @@
   </div>
   <div class="card-body">
     <div class="table-responsive">
-      <table aria-describedby="table-description" class="table table-bordered" id="dataTable" width="100%" cellspacing="0">
+      <table aria-describedby="table-description" class="table table-bordered" id="dataTable">
         <thead>
           <tr>
             <th scope="col">Nama</th>
diff --git a/administration/templates/laporan_materi.html b/administration/templates/laporan_materi.html
index 33e489890c0eaa9979f3fd06567f559e8df7ebdb..25849b3e3a73b782054b3ea0157aa1f6ba389089 100644
--- a/administration/templates/laporan_materi.html
+++ b/administration/templates/laporan_materi.html
@@ -21,7 +21,7 @@
   </div>
   <div class="card-body">
     <div class="table-responsive">
-      <table class="table table-bordered" id="dataTable" width="100%" cellspacing="0">
+      <table class="table table-bordered" id="dataTable">
         <thead>
           <tr>
             <th scope="col">Judul</th>
diff --git a/administration/templates/setting_verifikasi.html b/administration/templates/setting_verifikasi.html
index 754365bfcad93c8b93214cc6ffcd34cf33006243..8e39eb3e0369693d472622c0bd6718dbb8a17580 100644
--- a/administration/templates/setting_verifikasi.html
+++ b/administration/templates/setting_verifikasi.html
@@ -186,7 +186,7 @@
             </div>
             <div class="card-body">
               <div class="table-responsive">
-                <table aria-describedby="table-description" class="table table-bordered" id="dataTable" width="100%" cellspacing="0">
+                <table aria-describedby="table-description" class="table table-bordered" id="dataTable">
                   <thead>
                     <tr>
                       <th scope="col">Judul Verifikasi</th>
@@ -235,29 +235,6 @@
     <em class="fas fa-angle-up"></em>
   </a>
 
-  <!-- <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"
-    integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo"
-    crossorigin="anonymous"></script>
-  <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"
-    integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1"
-    crossorigin="anonymous"></script>
-  <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"
-    integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM"
-    crossorigin="anonymous"></script>
-
-
-  <script src="https://code.jquery.com/jquery-3.5.0.min.js"
-    integrity="sha256-xNzN2a4ltkB44Mc/Jz3pT4iU1cmeR0FkXs4pru/JxaQ=" crossorigin="anonymous"></script>
-
-  
-  <script src="{% static 'js/sb-admin-2.min.js' %}"></script>
-
-  <script src="{% static 'vendor/datatables/jquery.dataTables.min.js' %}"></script>
-  <script src="{% static 'vendor/datatables/dataTables.bootstrap4.min.js' %}"></script>
-
-  
-  <script src="{% static 'js/demo/datatables-demo.js' %}"></script> -->
-
 </body>
 
 </html>
\ No newline at end of file
diff --git a/administration/templates/verif.html b/administration/templates/verif.html
index ebbbd1dd4127eedbb697cd895906bb638ecd69c2..6b1a6688024b5356c4a31b3bb72cdc1872024555 100644
--- a/administration/templates/verif.html
+++ b/administration/templates/verif.html
@@ -61,7 +61,7 @@
   </div>
   <div class="card-body">
     <div class="table-responsive">
-      <table class="table table-bordered" id="dataTableRiwayat" width="100%" cellspacing="0" aria-describedby="titleTabelRiwayat">
+      <table class="table table-bordered" id="dataTableRiwayat" aria-describedby="titleTabelRiwayat">
         <thead>
           <tr>
             <th scope="col">Judul Materi</th>
diff --git a/administration/tests.py b/administration/tests.py
index 6ba9e50b788c568dd4ce4661922f25283005647b..cc7b72df30e9b727df09ed8e055755dd5751577f 100644
--- a/administration/tests.py
+++ b/administration/tests.py
@@ -10,7 +10,9 @@ from app.models import Category, Materi, LaporanMateri
 from authentication.models import User
 from bs4 import BeautifulSoup
 
-from datetime import datetime
+from datetime import datetime, timedelta
+
+from .forms import PeriodForm
 
 EDIT_ENDPOINT = "/edit"
 ERROR_403_MESSAGE = 'Kamu harus login untuk mengakses halaman ini'
@@ -1659,3 +1661,50 @@ class EditKontributorStatusTests(TestCase):
         self.assertEqual(response.status_code, 403)
 
 
+class PeriodFormTests(TestCase):
+
+    def test_validation_error_when_start_date_is_none(self):
+        data = {
+            'start_date': None,
+            'end_date': datetime.now(),
+        }
+        form = PeriodForm(data)
+        self.assertFalse(form.is_valid())
+        self.assertEqual(
+            form["start_date"].errors,
+            ['masukan waktu mulai']
+        )
+
+    def test_validation_error_when_end_date_is_none(self):
+        data = {
+            'start_date': datetime.now(),
+            'end_date': None,
+        }
+        form = PeriodForm(data)
+        self.assertFalse(form.is_valid())
+        self.assertEqual(
+            form["end_date"].errors,
+            ['masukan waktu selesai']
+        )
+
+    def test_validation_error_when_start_date_greater_than_end_date(self):
+        current_time = datetime.now()
+        data = {
+            'start_date': current_time + timedelta(days = 1),
+            'end_date': current_time,
+        }
+        form = PeriodForm(data)
+        self.assertFalse(form.is_valid())
+        self.assertEqual(
+            form["end_date"].errors,
+            ['waktu selesai sebelum waktu mulai']
+        )
+
+    def test_form_valid_when_data_valid(self):
+        current_time = datetime.now()
+        data = {
+            'start_date': current_time - timedelta(days = 1),
+            'end_date': current_time,
+        }
+        form = PeriodForm(data)
+        self.assertTrue(form.is_valid())
diff --git a/administration/views.py b/administration/views.py
index 65519deb8353157a6cce53d405d1936f1c78dc72..43a42b877c2b712001de03d85c77c362a8fab0b7 100644
--- a/administration/views.py
+++ b/administration/views.py
@@ -19,6 +19,8 @@ from django.core import management
 
 ADMINISTRATION_MANAGEMENT = "/administration/kelola-admin/"
 ADMINISTRATION_REPORT = "/administration/laporan-materi/"
+ADMINISTRATION_VERIFICATION_SETTING = "/administration/setting/verification/"
+ADMINISTRATION_CATEGORY_SETTING = "/administration/setting/category/"
 
 
 class VerificationView(TemplateView):
@@ -115,7 +117,7 @@ class VerificationSettingView(TemplateView):
         form = VerificationSettingForm(request.POST)
         if form.is_valid():
             form.save()
-            return HttpResponseRedirect("/administration/setting/verification/")
+            return HttpResponseRedirect(ADMINISTRATION_VERIFICATION_SETTING)
         else:
             context = self.get_context_data(**kwargs)
             context["form"] = form
@@ -146,7 +148,7 @@ class CategorySettingView(TemplateView):
         form = CategoryForm(request.POST)
         if form.is_valid():
             form.save()
-            return HttpResponseRedirect("/administration/setting/category/")
+            return HttpResponseRedirect(ADMINISTRATION_CATEGORY_SETTING)
         else:
             context = self.get_context_data(**kwargs)
             context["form"] = form
@@ -351,7 +353,7 @@ class EditVerificationView(TemplateView):
             request.POST, instance=verification_object)
         if form.is_valid():
             form.save()
-            return HttpResponseRedirect("/administration/setting/verification/")
+            return HttpResponseRedirect(ADMINISTRATION_VERIFICATION_SETTING)
         else:
             context = self.get_context_data(**kwargs)
             context["form"] = form
@@ -383,7 +385,7 @@ class EditCategoryView(TemplateView):
             request.POST, instance=category_object)
         if form.is_valid():
             form.save()
-            return HttpResponseRedirect("/administration/setting/category/")
+            return HttpResponseRedirect(ADMINISTRATION_CATEGORY_SETTING)
         else:
             context = self.get_context_data(**kwargs)
             context["form"] = form
@@ -483,15 +485,15 @@ def delete_contributor(request, *args, **kwargs):
 def delete_verification(request, *args, **kwargs):
     if not request.user.is_authenticated or not request.user.is_admin:
         raise PermissionDenied(request)
-    queryObject = get_object_or_404(VerificationSetting,
+    query_object = get_object_or_404(VerificationSetting,
                                     pk=kwargs["pk_verification"])
-    queryObject.archived = True
-    queryObject.description = "Telah dihapus pada " + \
+    query_object.archived = True
+    query_object.description = "Telah dihapus pada " + \
         str(timezone.now().strftime("%m/%d/%Y, %H:%M:%S")) + " WIB"
-    queryObject.archived_by = request.user
-    queryObject.save()
+    query_object.archived_by = request.user
+    query_object.save()
     messages.success(request, "Point verifikasi berhasil dihapus")
-    return HttpResponseRedirect("/administration/setting/verification/")
+    return HttpResponseRedirect(ADMINISTRATION_VERIFICATION_SETTING)
 
 
 def delete_category(request, *args, **kwargs):
@@ -499,16 +501,16 @@ def delete_category(request, *args, **kwargs):
         raise PermissionDenied(request)
     category_name = get_object_or_404(Category,
                                       pk=kwargs["pk_category"]).name
-    queryObject = get_object_or_404(Category,
+    query_object = get_object_or_404(Category,
                                     pk=kwargs["pk_category"])
-    queryObject.archived = True
-    queryObject.description = "Telah dihapus pada " + \
+    query_object.archived = True
+    query_object.description = "Telah dihapus pada " + \
         str(timezone.now().strftime("%m/%d/%Y, %H:%M:%S")) + " WIB"
-    queryObject.archived_by = request.user
-    queryObject.save()
+    query_object.archived_by = request.user
+    query_object.save()
     messages.success(request, "Kategori " +
                      category_name + " berhasil dihapus")
-    return HttpResponseRedirect("/administration/setting/category/")
+    return HttpResponseRedirect(ADMINISTRATION_CATEGORY_SETTING)
 
 def generatedummy(request):
     if request.user.is_authenticated is False or request.user.is_admin is False:
diff --git a/app/forms.py b/app/forms.py
index 5b8b3438c71bbe7efcb9dbee7746662563efb42b..266720e86f912083e2c1335923114cc5c21c4bd3 100644
--- a/app/forms.py
+++ b/app/forms.py
@@ -10,7 +10,6 @@ def year_choices():
 class UploadMateriForm(forms.ModelForm):
     
     categories = forms.ModelMultipleChoiceField(queryset=Category.objects.all(),widget=forms.CheckboxSelectMultiple(attrs={'style' : 'column-count:2'}),required=True)
-    #categories.widget.attrs["style"] = "column-count:2"
     release_year = forms.TypedChoiceField(coerce=int, choices=year_choices, initial=datetime.date.today().year)
     yt_video_id = forms.CharField(label="Youtube Video Id", \
     help_text="This is not required.<br>\
diff --git a/app/management/commands/generatedummy.py b/app/management/commands/generatedummy.py
index 3137d034c75d656ffafdea6d91999cf8f7eb1329..4cd4af451e71b2acfb47c525a28651a7ddd11fc0 100644
--- a/app/management/commands/generatedummy.py
+++ b/app/management/commands/generatedummy.py
@@ -24,7 +24,7 @@ class Command(BaseCommand):
         counter = 0
         category_name = f"Kategori dummy {counter}"
         dummy_category = []
-        for i in range(num_of_category):
+        for _ in range(num_of_category):
             while (Category.objects.filter(name=category_name).exists()):
                 counter += 1
                 category_name = f"Kategori dummy {counter}"
@@ -38,7 +38,7 @@ class Command(BaseCommand):
         counter = 0
         criterion_name = f"Kriteria dummy {counter}"
         dummy_criteria = []
-        for i in range(num_of_criteria):
+        for _ in range(num_of_criteria):
             while (VerificationSetting.objects.filter(title=criterion_name).exists()):
                 counter += 1
                 criterion_name = f"Kriteria dummy {counter}"
@@ -67,8 +67,7 @@ class Command(BaseCommand):
         counter = 0
         dummy_user = []
         email = f"admin-dummy-{counter}@email.com"
-        end_date = timezone.now()
-        for i in range(num_of_user):
+        for _ in range(num_of_user):
             while (User.objects.filter(email=email).exists()):
                 counter += 1
                 email = f"admin-dummy-{counter}@email.com"
@@ -140,8 +139,6 @@ class Command(BaseCommand):
                 verif_report = VerificationReport(
                     report=report_field, materi=materi, user=admin, status=materi.get_status_display(), timestamp=timestamp)
                 verif_report.save()
-            else:
-                pass
 
         return materi
 
diff --git a/app/management/commands/generatetraffic.py b/app/management/commands/generatetraffic.py
index f0c26950815807e2dd3339df8da9c4ea738a971c..6ca5cdba2a4f283e71e7277ab8cfe96fe766cb24 100644
--- a/app/management/commands/generatetraffic.py
+++ b/app/management/commands/generatetraffic.py
@@ -8,7 +8,7 @@ from django.core.files.uploadedfile import SimpleUploadedFile
 from django.utils import timezone
 
 from app.models import Materi, Comment, Like, DownloadStatistics, ViewStatistics, DummyLike, DummyViewStatistics, DummyDownloadStatistics, DummyComment
-from app.management.commands.utils import SECONDS_IN_DAY, get_time_before, get_random_datetime, generate_list_of_random_datetime, generate_random_string, getRandomColor, getLoremWithLength
+from app.management.commands.utils import SECONDS_IN_DAY, get_time_before, get_random_datetime, generate_list_of_random_datetime, generate_random_string, get_random_color, get_lorem_with_length
 
 
 class Command(BaseCommand):
@@ -50,7 +50,7 @@ class Command(BaseCommand):
 
     def _comment_materi(self, timestamp, materi):
         item = Comment(materi=materi, timestamp=timestamp,
-                       profile=getRandomColor(), comment=getLoremWithLength(240))
+                       profile=get_random_color(), comment=get_lorem_with_length(240))
         item.save()
         DummyComment(item=item).save()
 
@@ -64,16 +64,7 @@ class Command(BaseCommand):
         s_date = materi_published_date[0]
         s_date = s_date.replace(day=s_date.day+1, hour=0,
                                 minute=0, second=0, microsecond=0)
-        # reports = []
         while (s_date < today):
-            # report = {
-            #     "s_date": s_date,
-            #     "visit": 0,
-            #     "view": 0,
-            #     "download": 0,
-            #     "like": 0,
-            #     "comment": 0,
-            # }
             today_materi = [i for i in materi if i.published_date < s_date]
             visiting_user = options["baseline"]
             visiting_user += int(options["coef_time"] * r_day) + \
@@ -82,8 +73,6 @@ class Command(BaseCommand):
                                 (1 + uniform(-options["coef_visit_range"],
                                              options["coef_visit_range"])))
             active_user = int(visiting_user * options["coef_read"])
-            # report["visit"] = visiting_user
-            # report["view"] = active_user
             times = generate_list_of_random_datetime(
                 s_date, s_date+timedelta(days=1), active_user)
             for timestamp in times:
@@ -91,23 +80,9 @@ class Command(BaseCommand):
                 self._view_materi(timestamp, selected_materi)
                 if random() < options["coef_download"]:
                     self._download_materi(timestamp, selected_materi)
-                    # report["download"] += 1
                 if random() < options["coef_like"]:
                     self._like_materi(timestamp, selected_materi)
-                    # report["like"] += 1
                 if random() < options["coef_comment"]:
                     self._comment_materi(timestamp, selected_materi)
-                    # report["comment"] += 1
             s_date = s_date + timedelta(days=1)
             r_day += 1
-            # reports.append(report)
-        # for i in range
-        # for i in reports:
-        #     self.stdout.write(self.style.SUCCESS(f"Today is {i['s_date']}"))
-        #     self.stdout.write(self.style.SUCCESS(f"User visit {i['visit']}"))
-        #     self.stdout.write(self.style.SUCCESS(f"User view {i['view']}"))
-        #     self.stdout.write(self.style.SUCCESS(
-        #         f"User download {i['download']}"))
-        #     self.stdout.write(self.style.SUCCESS(f"User like {i['like']}"))
-        #     self.stdout.write(self.style.SUCCESS(
-        #         f"User comment {i['comment']}"))
diff --git a/app/management/commands/removetraffic.py b/app/management/commands/removetraffic.py
index 73afceda4a06d41f29892df66ad8c20d97a86db5..06f7bc73ceb7bf60a1d54a7d27cc288cbdd987aa 100644
--- a/app/management/commands/removetraffic.py
+++ b/app/management/commands/removetraffic.py
@@ -8,7 +8,7 @@ from django.core.files.uploadedfile import SimpleUploadedFile
 from django.utils import timezone, lorem_ipsum
 
 from app.models import DummyLike, DummyViewStatistics, DummyDownloadStatistics, DummyComment
-from app.management.commands.utils import SECONDS_IN_DAY, get_time_before, get_random_datetime, generate_list_of_random_datetime, generate_random_string, getRandomColor
+from app.management.commands.utils import SECONDS_IN_DAY, get_time_before, get_random_datetime, generate_list_of_random_datetime, generate_random_string, get_random_color
 
 
 class Command(BaseCommand):
diff --git a/app/management/commands/utils.py b/app/management/commands/utils.py
index 35884ddbeac236a264a0ab4e7fe96c32f2ea0833..9a394e2c0b1052f551d2e34cde12de9fca5a8bde 100644
--- a/app/management/commands/utils.py
+++ b/app/management/commands/utils.py
@@ -9,12 +9,12 @@ from django.utils import timezone, lorem_ipsum
 SECONDS_IN_DAY = 86400
 
 
-def getRandomColor():
+def get_random_color():
     color = "%06x" % randint(0, 0xFFFFFF)
     return color
 
 
-def getLoremWithLength(n):
+def get_lorem_with_length(n):
     while True:
         s = lorem_ipsum.sentence()
         if len(s) < n:
@@ -43,7 +43,7 @@ def get_random_datetime(start_date, end_date, max_delta_seconds=None, min_delta_
 
 def generate_list_of_random_datetime(start, end, n):
     res = []
-    for i in range(n):
+    for _ in range(n):
         res.append(get_random_datetime(start, end))
     res.sort()
     return res
@@ -56,4 +56,4 @@ def get_last_year():
 
 
 def generate_random_string(n):
-    return(''.join(choice(ascii_letters) for i in range(n)))
+    return(''.join(choice(ascii_letters) for _ in range(n)))
diff --git a/app/migrations/0003_auto_20200509_2108.py b/app/migrations/0003_auto_20200509_2108.py
index 844d9d85ad27f5f5b3102597c4e8e8ef6173f8fd..4b47af81379e4fb8d8d7aefb1801016621a76828 100644
--- a/app/migrations/0003_auto_20200509_2108.py
+++ b/app/migrations/0003_auto_20200509_2108.py
@@ -14,6 +14,6 @@ class Migration(migrations.Migration):
         migrations.AlterField(
             model_name='comment',
             name='profile',
-            field=models.CharField(default=app.models.getRandomColor, max_length=100),
+            field=models.CharField(default=app.models.get_random_color, max_length=100),
         ),
     ]
diff --git a/app/migrations/0025_review.py b/app/migrations/0025_review.py
index 79b7b89a4f655cf6851c7265539222047eef56e1..c57943a977e74551909a596ea4277cb656d905f9 100644
--- a/app/migrations/0025_review.py
+++ b/app/migrations/0025_review.py
@@ -20,7 +20,7 @@ class Migration(migrations.Migration):
             fields=[
                 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                 ('username', models.CharField(max_length=100)),
-                ('profile', models.CharField(default=app.models.getRandomColor, max_length=100)),
+                ('profile', models.CharField(default=app.models.get_random_color, max_length=100)),
                 ('review', models.TextField(default='review')),
                 ('timestamp', models.DateTimeField(default=django.utils.timezone.now)),
                 ('materi', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='app.materi')),
diff --git a/app/migrations/0027_readlater.py b/app/migrations/0027_readlater.py
new file mode 100644
index 0000000000000000000000000000000000000000..4652422241846b94a23e930332c0a3736173c890
--- /dev/null
+++ b/app/migrations/0027_readlater.py
@@ -0,0 +1,29 @@
+# Generated by Django 3.1 on 2020-10-30 13:26
+
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+import django.utils.timezone
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+        ('app', '0027_auto_20201030_1648'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='ReadLater',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('timestamp', models.DateTimeField(default=django.utils.timezone.now)),
+                ('materi', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='app.materi')),
+                ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
+            ],
+            options={
+                'unique_together': {('materi', 'user')},
+            },
+        ),
+    ]
diff --git a/app/models.py b/app/models.py
index e531ad121c188ab50d73c93dfbedd3eafb464389..fb932f7546c54b0f22af0e02c9e1212f123b51f6 100644
--- a/app/models.py
+++ b/app/models.py
@@ -21,7 +21,7 @@ VERIFICATION_STATUS = [
 # Create your models here.
 
 
-def getRandomColor():
+def get_random_color():
     color = "%06x" % random.randint(0, 0xFFFFFF)
     return color
 
@@ -140,11 +140,12 @@ class Materi(SoftDeleteModel):
         like = False
         if Like.objects.filter(materi=self).exists():
             like = True
+        return like
 
 
 class Comment(models.Model):
     username = models.CharField(max_length=100)
-    profile = models.CharField(max_length=100, default=getRandomColor)
+    profile = models.CharField(max_length=100, default=get_random_color)
     comment = models.CharField(max_length=240, default="comments")
     materi = models.ForeignKey(Materi, models.SET_NULL, null=True)
     user = models.ForeignKey(User, on_delete=models.SET_NULL, blank=True, null=True)
@@ -165,7 +166,7 @@ class Comment(models.Model):
 
 class Review(models.Model):
     username = models.CharField(max_length=100)
-    profile = models.CharField(max_length=100, default=getRandomColor)
+    profile = models.CharField(max_length=100, default=get_random_color)
     review = models.TextField(default="review")
     materi = models.ForeignKey(Materi, models.SET_NULL, null=True)
     user = models.ForeignKey(User, on_delete=models.SET_NULL, blank=True, null=True)
@@ -272,3 +273,11 @@ class LaporanMateri(models.Model):
     laporan = models.TextField(validators=[MinValueValidator(30), MaxValueValidator(120)], default="")
     timestamp = models.DateTimeField(default=timezone.now)
     is_rejected = models.BooleanField(default=False)
+
+class ReadLater(models.Model):
+    materi = models.ForeignKey(Materi, on_delete=models.CASCADE)
+    user = models.ForeignKey(User, on_delete=models.CASCADE)
+    timestamp = models.DateTimeField(default=timezone.now)
+
+    class Meta:
+        unique_together = ["materi", "user"]
\ No newline at end of file
diff --git a/app/services.py b/app/services.py
index ab9d56301e62f2fd921bed9e76d4bf0a8c8aea0d..300dbf45b0873615d1d1586a7dd788038eafc4d6 100644
--- a/app/services.py
+++ b/app/services.py
@@ -15,7 +15,7 @@ from pydrive.drive import GoogleDrive
 from administration.models import VerificationReport
 from app.forms import SuntingProfilForm
 from app.models import Category, Like, LikeComment, DislikeComment, Materi, Comment, Rating, DownloadStatistics, \
-    ViewStatistics
+    ViewStatistics, ReadLater
 from app.utils.fileManagementUtil import get_random_filename, remove_image_exifdata
 from digipus import settings
 import requests
@@ -343,7 +343,6 @@ class UploadMateriService:
         
     @staticmethod
     def validate_yt_video_url(value):
-        is_yt_id_valid = True
         r = requests.get('http://www.youtube.com/watch?v='+value)
         if "\"playabilityStatus\":{\"status\":\"ERROR\"" in r.text:
             raise ValidationError("Invalid Youtube video ID")
@@ -470,3 +469,18 @@ class GoogleDriveUploadService:
         file1["title"] = title
         print("title: %s, mimeType: %s" % (file1["title"], file1["mimeType"]))
         file1.Upload()
+
+class ReadLaterService:
+    
+    @staticmethod
+    def toggle_read_later(materi_id, current_user):
+        materi = get_object_or_404(Materi, pk=materi_id)
+        read_later_item_exist = ReadLater.objects.filter(materi=materi, user=current_user).exists()
+        if read_later_item_exist:
+            read_later_item = get_object_or_404(ReadLater, materi=materi, user=current_user)
+            read_later_item.delete()
+            response = {"success": True, "read_later_checked": False}
+        else:
+            ReadLater(materi=materi, user=current_user).save()
+            response = {"success": True, "read_later_checked": True}
+        return response
\ No newline at end of file
diff --git a/app/templates/app/base_admin.html b/app/templates/app/base_admin.html
index 7c8b851b015df98ead395e4bc31c80b72531268e..ff080f96ee2d739e2d7837189a7f9df131341030 100644
--- a/app/templates/app/base_admin.html
+++ b/app/templates/app/base_admin.html
@@ -55,13 +55,6 @@
           <span>Sunting Profil</span></a>
       </li>
 
-      <!--
-      <li class="nav-item">
-        <a class="nav-link" href="/sunting/">
-          <span>Sunting Profil</span></a>
-      </li>
-    -->
-
     </ul>
     <!-- End of Sidebar -->
 
diff --git a/app/templates/app/detail_materi.html b/app/templates/app/detail_materi.html
index 72d758c9e304adc8fbdae477fbbc9e3709fcd450..c083179bf1378998081c5f819bd78cf70b8c4816 100644
--- a/app/templates/app/detail_materi.html
+++ b/app/templates/app/detail_materi.html
@@ -22,7 +22,8 @@ div.review {
 {% block verification %} {% endblock verification %}
 <div id="fb-root"></div>
 <div class="container-fluid p-0 bg detail-materi-color">
-    <nav class="navbar navbar-expand navbar-light bg-white topbar mb-4 static-top shadow">
+    <span class="modal-title black-text" id="navbarLabel" style="display: none;">Navbar</span>
+    <nav class="navbar navbar-expand navbar-light bg-white topbar mb-4 static-top shadow" aria-labelledby="navbarLabel">
 
         <!-- Sidebar Toggle (Topbar) -->
         <button id="sidebarToggleTop" class="btn btn-link d-md-none rounded-circle mr-3">
@@ -106,7 +107,7 @@ div.review {
         <div class="col col-3 cover">
             <img src={{materi_data.cover.url}} alt="cover">
         </div>
-        <div class="col col-6 ml-3 book">
+        <div class="col col-8 ml-3 book">
             <h2>{{materi_data.title}}</h2>
             <div class="category-wrapper">
                 {% for category in materi_data.categories.all %}
@@ -256,18 +257,39 @@ div.review {
                         </div>
                     </div>
                 </div>
+
+                    {% if is_in_read_later_list %}
+                    <button class="btn btn-book shadow-sm p-2 mr-2 bg-primary text-white rounded align-self-center"
+                        type="button" id="readLaterButton" aria-haspopup="true" aria-expanded="false"
+                        onclick="postToggleReadLater()">
+                        <em class="align-self-center far fa-check-square" id="readLaterText"></em> Baca Nanti
+                    </button>
+                    {% else %}
+                    <button class="btn btn-book shadow-sm p-2 mr-2 bg-white text-primary rounded align-self-center"
+                        type="button" id="readLaterButton" aria-haspopup="true" aria-expanded="false"
+                        onclick="postToggleReadLater()">
+                        <em class="align-self-center far fa-square" id="readLaterText"></em> Baca Nanti
+                    </button>
+                    {% endif %}
                 {% else %}
                 <button class="btn dropdown-toggle btn-book shadow-sm p-2 mr-2 bg-white rounded align-self-center"
                     type="button" id="dropdownMenuButton" aria-haspopup="true" aria-expanded="false" data-toggle="modal"
                     data-target="#notLoggedInModal">
                     <em class="align-self-center far fa-star"></em> Beri Rating
                 </button>
+                
+                <button class="btn btn-book shadow-sm p-2 mr-2 bg-white text-primary rounded align-self-center"
+                        type="button" id="readLaterButton" aria-haspopup="true" aria-expanded="false"
+                        onclick="postToggleReadLater()">
+                        <em class="align-self-center far fa-square" id="readLaterText"></em> Baca Nanti
+                </button>
                 {% endif %}
             </div>
         </div>
     </div>
     <div class="row menu-wrapper mr-4 ml-4 p-3">
-        <nav class="navbar navbar-expand-sm border-top border-bottom p-0 mt-3 mb-3">
+        <span class="modal-title black-text" id="navbarLabelTwo" style="display: none;">Navbar</span>
+        <nav class="navbar navbar-expand-sm border-top border-bottom p-0 mt-3 mb-3" aria-labelledby="navbarLabelTwo">
             <ul class="navbar-nav">
                 <li class="nav-item">
                     <a class="nav-link" href="#deskripsi">Deskripsi</a>
@@ -603,6 +625,37 @@ div.review {
         });
     }
 
+    function postToggleReadLater() {
+        $.ajaxSetup({
+            beforeSend: function (xhr, settings) {
+                if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
+                    xhr.setRequestHeader("X-CSRFToken", csrftoken);
+                }
+            }
+        });
+
+        $.ajax({
+            type: 'POST',
+            url: "{% url 'toggle-read-later' %}",
+            data: {
+                'materi_id': "{{ materi_data.id }}",
+            },
+            success: changeReadLaterButton,
+            dataType: 'html'
+        });
+    }
+
+    function changeReadLaterButton(data, jqXHR) {
+        var data = $.parseJSON(data)
+        if (data['read_later_checked']) {
+            $('#readLaterButton').removeClass("bg-white text-primary").addClass("bg-primary text-white")
+            $('#readLaterText').removeClass("fa-square").addClass("fa-check-square")
+        } else {
+            $('#readLaterButton').removeClass("bg-primary text-white").addClass("bg-white text-primary")
+            $('#readLaterText').removeClass("fa-check-square").addClass("fa-square")
+        }
+    }
+
     function LikePost(data, jqXHR) {
         var data = $.parseJSON(data)
         var likeCount = parseInt($('.info-content')[6].textContent)
diff --git a/app/templates/app/includes/sidebar.html b/app/templates/app/includes/sidebar.html
index ff0bb3adb420af54f491adcdc994bd422f1991d6..69425f3e24b473e9953c78d53353d3d128c23dda 100644
--- a/app/templates/app/includes/sidebar.html
+++ b/app/templates/app/includes/sidebar.html
@@ -27,7 +27,7 @@
     </li>
 
     <li class="nav-item">
-        <a class="nav-link" href="#">
+        <a class="nav-link" href="{% url 'stats' %}">
             <span>Statisik Materi</span>
         </a>
     </li>
diff --git a/app/templates/app/includes/sidebar_profile.html b/app/templates/app/includes/sidebar_profile.html
index 69c80bba4e59efd2d2a0496a690ebe3e5a555966..d28a4c58f129d2221169186cec9927b3c1e203c9 100644
--- a/app/templates/app/includes/sidebar_profile.html
+++ b/app/templates/app/includes/sidebar_profile.html
@@ -27,4 +27,9 @@
         <a class="nav-link" href="/given-rating/">
             <span>Rating Diberikan</span></a>
     </li>
+
+    <li class="nav-item">
+        <a class="nav-link" href="/baca-nanti">
+            <span>Baca Nanti</span></a>
+    </li>
 </ul>
\ No newline at end of file
diff --git a/app/templates/app/katalog_materi.html b/app/templates/app/katalog_materi.html
index 224232c1a609e82a1004dd0183bd6ac83059cd48..4a06d4c5a4233e6766f70a3617cf61c9755260e2 100644
--- a/app/templates/app/katalog_materi.html
+++ b/app/templates/app/katalog_materi.html
@@ -160,7 +160,7 @@
                     {% for item in materi_list %}
                     <div class="card book">
                         <img src={{item.cover.url}} class="card-img-top" alt="cover"
-                            style="height:200px; widows: 200px;; overflow: hidden;"></img>
+                            style="height:200px; widows: 200px; overflow: hidden;"></img>
                         <div class="card-body">
                             <h5 class="card-title">{{item.title}}</h5>
                             <p class="card-text">{{item.author}}</p>
diff --git a/app/templates/baca-nanti.html b/app/templates/baca-nanti.html
new file mode 100644
index 0000000000000000000000000000000000000000..969728371ec78c7c24241afa335c070f8b3553f7
--- /dev/null
+++ b/app/templates/baca-nanti.html
@@ -0,0 +1,84 @@
+{% extends 'app/base_profile.html' %}
+{% load static %}
+
+{% block title %}
+    <title>Baca Nanti | Digipus</title>
+{% endblock %}
+{% block stylesheets %}
+    <link rel="stylesheet" type="text/css" href="{% static 'app/css/katalog_materi.css' %}">
+    <!-- Optional JavaScript -->
+    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
+    <script src="https://code.jquery.com/jquery-3.5.1.min.js"
+            integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
+    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"
+            integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1"
+            crossorigin="anonymous"></script>
+    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"
+            integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM"
+            crossorigin="anonymous"></script>
+    
+    <!-- Bootstrap core CSS -->
+    <link href="../../static/app/vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet">
+
+    <!-- Custom styles for this template -->
+    <link href="../../static/app/css/heroic-features.css" rel="stylesheet">
+
+    
+    <link rel="icon" type="image/png" href="{% static 'images/icons/logo.ico' %}" />
+    
+    
+    <link rel="stylesheet" type="text/css" href="{% static 'fonts/font-awesome-4.7.0/css/font-awesome.min.css' %}">
+    
+    
+    <link rel="stylesheet" type="text/css" href="{% static 'vendor/animate/animate.css' %}">
+    
+    <link rel="stylesheet" type="text/css" href="{% static 'vendor/css-hamburgers/hamburgers.min.css' %}">
+    
+    <link rel="stylesheet" type="text/css" href="{% static 'vendor/animsition/css/animsition.min.css' %}">
+    
+    <link rel="stylesheet" type="text/css" href="{% static 'vendor/select2/select2.min.css' %}">
+    
+    <link rel="stylesheet" type="text/css" href="{% static 'vendor/daterangepicker/daterangepicker.css' %}">
+     
+    <link rel="stylesheet" type="text/css" href="{% static 'css/styles.css' %}">
+    <link rel="stylesheet" type="text/css" href="{% static 'css/util.css' %}">
+
+{% endblock %}
+
+{% block content %}
+    <div class="container">
+        <div class="col-20">
+            <h1 class="mt-2">Daftar Materi Yang Belum Dibaca</h1>
+            <hr class="mt-0 mb-4">
+
+            {% if read_later_list %}
+            <div class="container row content">
+                <div class="col-20 books">
+                    {% for item in read_later_list %}
+                    <div class="card book">
+                        <img src={{item.materi.cover.url}} class="card-img-top" alt="cover"
+                            style="height:200px; widows: 200px; overflow: hidden;"></img>
+                        <div class="card-body">
+                            <h5 class="card-title">{{item.materi.title}}</h5>
+                            <p class="card-text">{{item.materi.author}}</p>
+                            <p class="card-text">Diunggah oleh
+                                <a class="card-link" href="{% url 'katalog-per-kontributor' item.materi.uploader.email %}">
+                                    {{item.materi.uploader.name}}
+                                </a>
+                            </p>
+                            <a href="{% url 'view-materi' item.materi.id %}" class="btn btn-book">Baca</a>
+                            <a href="{% url 'detail-materi' item.materi.id %}" class="btn btn-book">Detail</a>
+                        </div>
+                    </div>
+                    {% endfor %}
+                </div>
+            </div>
+            {% else %}
+                <h1>Anda Tidak Memiliki Daftar Baca Nanti</h1>
+            {% endif %}
+        </div>
+    </div>
+
+
+{% endblock %}
+
diff --git a/app/templates/statistik.html b/app/templates/statistik.html
new file mode 100644
index 0000000000000000000000000000000000000000..839895c7ddb791c8f492088381767aa6560d70de
--- /dev/null
+++ b/app/templates/statistik.html
@@ -0,0 +1,72 @@
+{% extends 'app/base_dashboard.html' %}
+{% load static %}
+
+{% block title %}
+<title>Unggah Materi dari Excel | Digipus</title>
+{% endblock %}
+
+{% block stylesheets %}
+<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.min.js"
+    integrity="sha512-d9xgZrVZpmmQlfonhQUvTR7lMPtO7NkZMkA0ABN3PHCbKA5nqylQ/yWlFAyY6hYgdF1Qh6nYiuADWwKB4C2WSw=="
+    crossorigin="anonymous"></script>
+<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.min.css"
+    integrity="sha512-/zs32ZEJh+/EO2N1b0PEdoA10JkdC3zJ8L5FTiQu82LR9S/rOQNfQN7U59U9BC12swNeRAz3HSzIL2vpp4fv3w=="
+    crossorigin="anonymous" />
+{% endblock %}
+
+{% block content %}
+
+
+<div class="container">
+
+    <h1 class="h3 mb-2 text-gray-800">Summary Materi per Kategori</h1>
+
+    <div class="card-body">
+        <div class="table-responsive">
+            <table class="table table-bordered">
+                <thead>
+                    <th>Kategori</th>
+                    <th>Jumlah Materi</th>
+                </thead>
+                <tbody>
+                    {% for s in stats %}
+                    <tr>
+                        <td>{{s.name}}</td>
+                        <td>{{s.num}}</td>
+                    </tr>
+                    {% endfor %}
+                </tbody>
+            </table>
+        </div>
+    </div>
+
+    <div class="col-lg-6 mx-auto">
+        <canvas id="myChart" width="400" height="400"></canvas>
+    </div>
+
+</div>
+
+<script type="text/javascript">
+
+    function displayChart(data) {
+        var ctx = document.getElementById('myChart').getContext('2d');
+
+        var myChart = new Chart(ctx, {
+            type: 'pie',
+            data: data
+        });
+    }
+
+
+    document.addEventListener("DOMContentLoaded", function(){
+        fetch('/stats?data=json')
+        .then(function(response) { 
+            return response.json()
+        })
+        .then(function(json) {
+            displayChart(json)
+       })
+    });
+
+</script>
+{% endblock %}
\ No newline at end of file
diff --git a/app/tests.py b/app/tests.py
index 9f10c73f27f528bd814fb834b1084391f60514d9..5b83a94176217c709516472a1584b0b800e4c7b6 100644
--- a/app/tests.py
+++ b/app/tests.py
@@ -3,6 +3,7 @@ import pandas as pd
 from io import StringIO
 import re
 import time
+import random
 from django.test import override_settings
 
 from bs4 import BeautifulSoup
@@ -41,6 +42,7 @@ from .models import (
     ReqMaterial,
     RatingContributor,
     ViewStatistics,
+    ReadLater
 )
 
 from .services import (
@@ -3443,3 +3445,195 @@ class MateriRecommendationTest(TestCase):
         list = [int(id) for id in re.findall(r"Materi\s(\d+)[^\d]", response.content.decode())]
         self.assertEqual(list, [1, 2])
     
+class BacaNantiTest(TestCase):
+    def setUp(self):
+        self.contributor_credential = {
+            "email": "kontributor@gov.id",
+            "password": id_generator()
+        }
+        self.user_one_credential = {
+            "email": "user_one@user.id",
+            "password": id_generator()
+        }
+        self.user_two_credential = {
+            "email": "user_two@user.id",
+            "password": id_generator()
+        }
+        self.contributor = get_user_model().objects.create_user(
+            **self.contributor_credential, name="Kontributor", is_contributor=True
+        )
+        self.user_one = get_user_model().objects.create_user(**self.user_one_credential, name="User One")
+        self.user_two = get_user_model().objects.create_user(**self.user_two_credential, name="User Two")
+        self.cover = SimpleUploadedFile(
+            "cover.jpg",
+            b"Test file"
+        )
+        self.content = SimpleUploadedFile(
+            "content.txt",
+            b"Test file"
+        )
+        Materi(title="Materi 1", author="Agas", uploader=self.contributor,
+               publisher="Kelas SC", descriptions="Deskripsi Materi 1",
+               status="APPROVE", cover=self.cover, content=self.content).save()
+        Materi(title="Materi Dua", author="Author", uploader=self.contributor,
+               publisher="Publisher", descriptions="Deskripsi Materi Dua",
+               status="APPROVE", cover=self.cover, content=self.content).save()
+        self.materi1 = Materi.objects.filter(title='Materi 1').get()
+        self.materi2 = Materi.objects.filter(title='Materi Dua').get()
+        self.url = '/baca-nanti/'
+        self.toggle_url = '/baca-nanti-toggle/'
+        self.url_materi = '/materi/{}/'.format(self.materi1.id)
+
+    def test_readlater_object_can_be_created(self):
+        ReadLater(materi=self.materi1, user=self.user_one).save()
+        read_later = ReadLater.objects.first()
+        self.assertEqual(read_later.materi, self.materi1)
+        self.assertEqual(read_later.user, self.user_one)
+
+    def test_readlater_materi_must_not_unique(self):
+        ReadLater(materi=self.materi1, user=self.user_one).save()
+        ReadLater(materi=self.materi1, user=self.user_two).save()
+        read_later_one = ReadLater.objects.get(user=self.user_one)
+        read_later_two = ReadLater.objects.get(user=self.user_two)
+        self.assertEqual(read_later_one.materi, self.materi1)
+        self.assertEqual(read_later_one.user, self.user_one)
+        self.assertEqual(read_later_two.materi, self.materi1)
+        self.assertEqual(read_later_two.user, self.user_two)
+    
+    def test_readlater_user_must_not_unique(self):
+        ReadLater(materi=self.materi1, user=self.user_one).save()
+        ReadLater(materi=self.materi2, user=self.user_one).save()
+        read_later_one = ReadLater.objects.get(materi=self.materi1)
+        read_later_two = ReadLater.objects.get(materi=self.materi2)
+        self.assertEqual(read_later_one.materi, self.materi1)
+        self.assertEqual(read_later_one.user, self.user_one)
+        self.assertEqual(read_later_two.materi, self.materi2)
+        self.assertEqual(read_later_two.user, self.user_one)
+
+    def test_readlater_materi_combined_with_user_must_be_unique(self):
+        with self.assertRaises(IntegrityError) as context:
+            ReadLater(materi=self.materi1, user=self.user_one).save()
+            ReadLater(materi=self.materi1, user=self.user_one).save()
+        self.assertTrue('already exists' in str(context.exception))
+
+    def test_readlater_materi_cant_null(self):
+        with self.assertRaises(IntegrityError):
+            ReadLater(user=self.user_one).save()
+
+    def test_readlater_user_cant_null(self):
+        with self.assertRaises(IntegrityError):
+            ReadLater(materi=self.materi1).save()
+    
+    def test_readlater_profile_page_url_exist(self):
+        self.client.login(**self.user_one_credential)
+        response = self.client.get(self.url)
+        self.assertEqual(response.status_code, 200)
+
+    def test_readlater_profile_page_using_template(self):
+        self.client.login(**self.user_one_credential)
+        response = self.client.get(self.url)
+        self.assertTemplateUsed(response=response, template_name="baca-nanti.html")
+    
+    def test_toggle_readlater_url_exist(self):
+        self.client.login(**self.user_one_credential)
+        response = self.client.post(self.toggle_url, {'materi_id': self.materi1.id})
+        self.assertEqual(response.status_code, 200)
+
+    def test_checking_readlater_in_materi_create_object(self):
+        self.client.login(**self.user_one_credential)
+        self.client.post(self.toggle_url, {'materi_id': self.materi1.id})
+        read_later_exist = ReadLater.objects.filter(materi=self.materi1, user=self.user_one).exists()
+        self.assertEqual(read_later_exist, True)
+    
+    def test_unchecking_readlater_in_materi_delete_object(self):
+        self.client.login(**self.user_one_credential)
+        self.client.post(self.toggle_url, {'materi_id': self.materi1.id})
+        sleep(1)
+        self.client.post(self.toggle_url, {'materi_id': self.materi1.id})
+        read_later_exist = ReadLater.objects.filter(materi=self.materi1, user=self.user_one).exists()
+        self.assertEqual(read_later_exist, False)
+
+    def test_checking_readlater_in_materi_with_complete_paramater_return_success(self):
+        self.client.login(**self.user_one_credential)
+        response = self.client.post(self.toggle_url, {'materi_id': self.materi1.id})
+        self.assertJSONEqual(
+            str(response.content, encoding='utf-8'),
+            {"success": True, "read_later_checked": True}
+        )
+
+    def test_unchecking_readlater_in_materi_with_complete_paramater_return_success(self):
+        self.client.login(**self.user_one_credential)
+        self.client.post(self.toggle_url, {'materi_id': self.materi1.id})
+        sleep(1)
+        response = self.client.post(self.toggle_url, {'materi_id': self.materi1.id})
+        self.assertJSONEqual(
+            str(response.content, encoding='utf-8'),
+            {"success": True, "read_later_checked": False}
+        )
+    
+    def test_toggle_readlater_return_if_method_snot_post(self):
+        self.client.login(**self.user_one_credential)
+        response = self.client.get(self.toggle_url, {'materi_id': self.materi1.id})
+        self.assertJSONEqual(
+            str(response.content, encoding='utf-8'),
+            {"success": False, "msg": "Unsuported method"}
+        )
+    
+    def test_toggle_readlater_return_if_paramater_materi_id_not_found(self):
+        self.client.login(**self.user_one_credential)
+        response = self.client.post(self.toggle_url)
+        self.assertJSONEqual(
+            str(response.content, encoding='utf-8'),
+            {"success": False, "msg": "Missing parameter"}
+        )
+
+class MateriStatsTest(TestCase):
+
+    def setUp(self):
+        self.credential = {
+            'email':"kontributor@gov.id", 
+            'password':"P@ssw0rd", 
+        }
+
+        self.path = '/stats/'
+        self.path_json = '/stats/?data=json'
+        self.header = 'Summary Materi per Kategori'
+
+        self.contributor = User.objects.create_contributor(**self.credential, name="kontributor")
+        self.client = Client()
+
+        categories = []
+        for i in range(10):
+            cat = Category(name=f'Cat{i}', description=f'Cat{i} description')
+            cat.save()
+            categories.append(cat)
+
+        for i in range(10):
+            for j in range(random.randint(1, 10)):
+                m = Materi(title=f'Title{i}-{j}')
+                m.save()
+                m.categories.add(categories[i])
+                m.save()
+
+
+    def test_stats_has_correct_template(self):
+        self.client.login(**self.credential)
+        response = self.client.get(self.path)
+        self.assertTemplateUsed(response, 'statistik.html')
+
+    def test_stats_as_authenticated(self):
+        self.client.login(**self.credential)
+        response = self.client.get(self.path)
+        self.assertContains(response, self.header)
+
+    def test_stats_as_anonymous(self):
+        response = self.client.get(self.path)
+        self.assertEqual(response.status_code, 302) #redirect
+        response = self.client.get(self.path_json)
+        self.assertEqual(response.status_code, 302) #redirect
+
+    def test_stats_api_correct_data(self):
+        self.client.login(**self.credential)
+        response = self.client.get(self.path_json)
+        jobj = json.loads(response.content)
+        self.assertEqual(len(jobj['labels']), 6)
diff --git a/app/urls.py b/app/urls.py
index cb90cebb28e11f2fa069f0da848db7a581bc9d64..9df11eb5fc440636480622cbb9bcf0dcd129adb3 100644
--- a/app/urls.py
+++ b/app/urls.py
@@ -2,10 +2,11 @@ from django.contrib.auth import views as auth_views
 from django.urls import path, re_path
 
 from app import views
-from app.views import (DashboardKontributorView, ProfilView,
+from app.views import (DashboardKontributorView, ProfilView, StatisticsView,
                        SuksesLoginAdminView, SuksesLoginKontributorView, DownloadHistoryView,
                        SuntingProfilView, UploadMateriHTML, UploadMateriView, UploadMateriExcelView, PostsView,
-                       ReqMateriView, KatalogPerKontributorView, MateriFavorite, PasswordChangeViews, password_success, SubmitVisitorView)
+                       ReqMateriView, KatalogPerKontributorView, MateriFavorite, PasswordChangeViews, password_success, 
+                       SubmitVisitorView, ReadLaterView)
 
 
 urlpatterns = [
@@ -41,6 +42,9 @@ urlpatterns = [
     path("password_success/", views.password_success, name="password_success"),
     path("given-rating/", views.see_given_rating, name="see_given_rating"),
     path("submit-visitor/", SubmitVisitorView.as_view(), name="submit-visitor"),
+    path("baca-nanti/", ReadLaterView.as_view(), name="read-later"),
+    path("baca-nanti-toggle/", views.toggle_readlater, name="toggle-read-later"),
+    path("stats/", StatisticsView.as_view(), name="stats"),
     path("reset_password/", 
         auth_views.PasswordResetView.as_view(template_name="password_reset.html"), 
         name="reset_password"),
@@ -53,5 +57,4 @@ urlpatterns = [
     path("reset_password_complete/", 
         auth_views.PasswordResetCompleteView.as_view(template_name="password_reset_done.html"), 
         name="password_reset_complete"),
-    
 ]
diff --git a/app/utils/fileManagementUtil.py b/app/utils/fileManagementUtil.py
index d693b01fea0cbd035f0fa6e7ef347cdac1d0373b..a31e0aaad64e0098b073338bc4bb6a67d1de504e 100644
--- a/app/utils/fileManagementUtil.py
+++ b/app/utils/fileManagementUtil.py
@@ -12,6 +12,5 @@ def get_random_filename(f_name):
 def remove_image_exifdata(f_path):
     img = Image.open(f_path)
     img.save(f_path)
-    return   
 
 
diff --git a/app/views.py b/app/views.py
index a0062ed070c2065cc77c3e37757c749b70f470d1..233bd53d4547d2f212c8db0629c8d3c85ff3e10c 100644
--- a/app/views.py
+++ b/app/views.py
@@ -12,7 +12,7 @@ from django.contrib.auth.views import PasswordChangeView
 from django.core.exceptions import PermissionDenied, FieldError
 from django.core.mail import send_mail
 from django.core.paginator import Paginator
-from django.db.models import Q, Avg
+from django.db.models import Q, Avg, Count
 from django.http import (Http404, HttpResponse, HttpResponseRedirect,
                          JsonResponse)
 from django.shortcuts import get_object_or_404, redirect
@@ -31,13 +31,21 @@ from app.models import (
     Materi,
     ReqMaterial,
     Rating, RatingContributor,
-    SubmitVisitor
+    SubmitVisitor,
+    ReadLater
 )
 from authentication.models import User
 from .services import DafterKatalogService, DetailMateriService, LikeDislikeService, MateriFieldValidationHelperService, \
     DownloadViewMateriHelperService, UploadMateriService, EditProfileService, RevisiMateriService, \
-    DownloadHistoryService, GoogleDriveUploadService
+    DownloadHistoryService, GoogleDriveUploadService, ReadLaterService
 
+MISSING_PARAMETER_MESSAGE = "Missing parameter"
+UNSUPPORTED_MESSAGE = "Unsuported method"
+FILE_NOT_FOUND_MESSAGE = "File tidak dapat ditemukan."
+UNGGAH_HTML = "unggah.html"
+UNGGAH_URL = "/unggah/"
+UNGGAH_EXCEL_URL = "/unggah_excel/"
+LOGIN_URL = "/login/"
 
 def permission_denied(request, exception, template_name="error_403.html"):
     return defaults.permission_denied(request, exception, template_name)
@@ -54,13 +62,13 @@ class DaftarKatalog(TemplateView):
         context = self.get_context_data(**kwargs)
         context["kategori_list"] = Category.objects.all()
 
-        lstMateri = Materi.objects.filter(status="APPROVE").order_by("date_modified")
+        lst_materi = Materi.objects.filter(status="APPROVE").order_by("date_modified")
         url = ""
 
-        lstMateri, url = DafterKatalogService.apply_options(lstMateri, request, url)
+        lst_materi, url = DafterKatalogService.apply_options(lst_materi, request, url)
 
 
-        context["materi_list"] = lstMateri
+        context["materi_list"] = lst_materi
         paginator = Paginator(context["materi_list"], 15)
         page_number = request.GET.get("page")
         page_obj = paginator.get_page(page_number)
@@ -154,6 +162,12 @@ class DetailMateri(TemplateView):
             if materi_rating is not None:
                 context['materi_rating_score'] = materi_rating.score
 
+            materi_read_later = ReadLater.objects.filter(materi=materi, user=self.request.user).first()
+            if materi_read_later is not None:
+                context['is_in_read_later_list'] = True
+            else:
+                context['is_in_read_later_list'] = False
+
         context['is_authenticated'] = self.request.user.is_authenticated
 
         return context
@@ -219,10 +233,10 @@ def toggle_like(request):
         materi_id = request.POST.get("materi_id", None)
         session_id = request.POST.get("session_id", None)
         if materi_id is None or session_id is None:
-            return JsonResponse({"success": False, "msg": "Missing parameter"})
+            return JsonResponse({"success": False, "msg": MISSING_PARAMETER_MESSAGE})
         return JsonResponse(LikeDislikeService.apply_like_materi(materi_id, session_id))
     else:
-        return JsonResponse({"success": False, "msg": "Unsuported method"})
+        return JsonResponse({"success": False, "msg": UNSUPPORTED_MESSAGE})
 
 
 def delete_comment(request, pk_materi, pk_comment):
@@ -239,10 +253,10 @@ def toggle_like_comment(request):
         comment_id = request.POST.get("comment_id", None)
         session_id = request.POST.get("session_id", None)
         if comment_id is None or session_id is None:
-            return JsonResponse({"success": False, "msg": "Missing parameter", "comment_id": comment_id})
+            return JsonResponse({"success": False, "msg": MISSING_PARAMETER_MESSAGE, "comment_id": comment_id})
         return JsonResponse(LikeDislikeService.apply_comment_like(comment_id, session_id))
     else:
-        return JsonResponse({"success": False, "msg": "Unsuported method", "comment_id": comment_id})
+        return JsonResponse({"success": False, "msg": UNSUPPORTED_MESSAGE, "comment_id": comment_id})
 
 
 def toggle_dislike_comment(request):
@@ -251,10 +265,10 @@ def toggle_dislike_comment(request):
         comment_id = request.POST.get("comment_id", None)
         session_id = request.POST.get("session_id", None)
         if comment_id is None or session_id is None:
-            return JsonResponse({"success": False, "msg": "Missing parameter", "comment_id": comment_id})
+            return JsonResponse({"success": False, "msg": MISSING_PARAMETER_MESSAGE, "comment_id": comment_id})
         return JsonResponse(LikeDislikeService.apply_comment_dislike(comment_id, session_id))
     else:
-        return JsonResponse({"success": False, "msg": "Unsuported method", "comment_id": comment_id})
+        return JsonResponse({"success": False, "msg": UNSUPPORTED_MESSAGE, "comment_id": comment_id})
 
 
 def add_rating_materi(request):
@@ -298,10 +312,10 @@ def download_materi(request, pk):
                 return DownloadViewMateriHelperService.build_materi_response(fh, file_path,
                                                                              materi, mimetype, request,
                                                                              HttpResponse(fh.read(), content_type=mimetype[0]))
-        except Exception as e:
-            raise Http404("File tidak dapat ditemukan.")
+        except Exception:
+            raise Http404(FILE_NOT_FOUND_MESSAGE)
     else:
-        raise Http404("File tidak dapat ditemukan.")
+        raise Http404(FILE_NOT_FOUND_MESSAGE)
 
 
 def view_materi(request, pk):
@@ -315,10 +329,10 @@ def view_materi(request, pk):
                 response = HttpResponse(fh.read(), content_type=mimetype[0])
                 DownloadViewMateriHelperService.build_view_materi_response(file_path, materi, response)
                 return response
-        except Exception as e:
-            raise Http404("File tidak dapat ditemukan.")
+        except Exception:
+            raise Http404(FILE_NOT_FOUND_MESSAGE)
     else:
-        raise Http404("File tidak dapat ditemukan.")
+        raise Http404(FILE_NOT_FOUND_MESSAGE)
 
 
 def delete_materi(request, pk):
@@ -330,7 +344,7 @@ def delete_materi(request, pk):
     return HttpResponseRedirect("/dashboard/")
 
 class UploadMateriView(TemplateView):
-    template_name = "unggah.html"
+    template_name = UNGGAH_HTML
     context = {}
 
     def get_context_data(self, **kwargs):
@@ -347,10 +361,10 @@ class UploadMateriView(TemplateView):
             konten = form.cleaned_data["content"]
             yt_url_id = form.cleaned_data['yt_video_id']
             if not UploadMateriService.validate_file_extension(konten, request, yt_url_id):
-                return HttpResponseRedirect("/unggah/")
+                return HttpResponseRedirect(UNGGAH_URL)
             UploadMateriService.upload_materi(form, materi)
             messages.success(request, "Materi berhasil diunggah, periksa riwayat unggah anda")
-            return HttpResponseRedirect("/unggah/")
+            return HttpResponseRedirect(UNGGAH_URL)
         else:
             context = self.get_context_data(**kwargs)
             context["form"] = form
@@ -369,12 +383,12 @@ class UploadMateriView(TemplateView):
 
 
 class UploadMateriHTML(TemplateView):
-    template_name = "unggah.html"
+    template_name = UNGGAH_HTML
     context = {}
 
     def get_template_names(self):
-        if self.request.path == "/unggah/":
-            template_name = "unggah.html"
+        if self.request.path == UNGGAH_URL:
+            template_name = UNGGAH_HTML
         return template_name
 
 
@@ -383,7 +397,7 @@ class UploadMateriExcelView(TemplateView):
     context = {}
 
     def get_template_names(self):
-        if self.request.path == "/unggah_excel/":
+        if self.request.path == UNGGAH_EXCEL_URL:
             template_name = "unggah_excel.html"
         return template_name
 
@@ -439,7 +453,7 @@ class UploadMateriExcelView(TemplateView):
 
         if message != None:
             messages.error(request, message)
-            return HttpResponseRedirect("/unggah_excel/")
+            return HttpResponseRedirect(UNGGAH_EXCEL_URL)
 
         # Second pass, save data
         with django.db.transaction.atomic():
@@ -447,7 +461,7 @@ class UploadMateriExcelView(TemplateView):
 
         messages.success(request, "Materi berhasil diunggah")
 
-        return HttpResponseRedirect("/unggah_excel/")
+        return HttpResponseRedirect(UNGGAH_EXCEL_URL)
 
 
 
@@ -529,7 +543,7 @@ class ReqMateriView(TemplateView):
 
     def dispatch(self, request, *args, **kwargs):
         if request.user.is_authenticated == False:
-            return HttpResponseRedirect("/login/")
+            return HttpResponseRedirect(LOGIN_URL)
         return super(ReqMateriView, self).dispatch(request, *args, **kwargs)
 
     def get_context_data(self, **kwargs):
@@ -544,7 +558,7 @@ class ReqMateriView(TemplateView):
     def post(self, request, *args, **kwargs):
         title = request.POST.get("title", None)
         if title is None:
-            return JsonResponse({"success": False, "msg": "Missing parameter"})
+            return JsonResponse({"success": False, "msg": MISSING_PARAMETER_MESSAGE})
         ReqMaterial(title=title).save()
         return JsonResponse({"success": True, "msg": "Permintaan materi berhasil dikirimkan"})
 
@@ -662,7 +676,7 @@ def pages(request):
         template = loader.get_template(load_template)
         return HttpResponse(template.render(context, request))
 
-    except Exception as e:
+    except Exception:
 
         template = loader.get_template("error-404.html")
         return HttpResponse(template.render(context, request))
@@ -692,7 +706,7 @@ def save_to_gdrive(request, pk):
     if os.path.exists(file_path):
         GoogleDriveUploadService.upload_to_gdrive(file_path, materi.title)
     else:
-        raise Http404("File tidak dapat ditemukan.")
+        raise Http404(FILE_NOT_FOUND_MESSAGE)
 
     return HttpResponseRedirect(reverse('detail-materi', kwargs={'pk': pk}))
 
@@ -761,7 +775,7 @@ class SubmitVisitorView(TemplateView):
 
     def dispatch(self, request, *args, **kwargs):
         if request.user.is_authenticated == False:
-            return HttpResponseRedirect("/login/")
+            return HttpResponseRedirect(LOGIN_URL)
         return super(SubmitVisitorView, self).dispatch(request, *args, **kwargs)
 
     def get_context_data(self, **kwargs):
@@ -777,6 +791,91 @@ class SubmitVisitorView(TemplateView):
         email = request.POST.get("email", None)
         user_id = request.POST.get("user_id", None)
         if title is None:
-            return JsonResponse({"success": False, "msg": "Missing parameter"})
+            return JsonResponse({"success": False, "msg": MISSING_PARAMETER_MESSAGE})
         SubmitVisitor(msg=title, user_id=user_id, email=email).save()
         return JsonResponse({"success": True, "msg": "Buku tamu berhasil ditambahkan"})
+
+class ReadLaterView(TemplateView):
+    template_name = 'baca-nanti.html'
+
+    def dispatch(self, request, *args, **kwargs):
+        if not request.user.is_authenticated:
+            raise PermissionDenied(request)
+        return super(ReadLaterView, self).dispatch(request, *args, **kwargs)
+
+    def get_context_data(self, **kwargs):
+        context = super(ReadLaterView, self).get_context_data(**kwargs)
+        return context
+
+    def get(self, request, *args, **kwargs):
+        context = self.get_context_data(**kwargs)
+        user = self.request.user
+        context["read_later_list"] = ReadLater.objects.filter(user=user).order_by('-timestamp')
+        return self.render_to_response(context)
+
+def toggle_readlater(request):
+    if request.method == "POST":
+        materi_id = request.POST.get("materi_id", None)
+        if materi_id is None:
+            return JsonResponse({"success": False, "msg": MISSING_PARAMETER_MESSAGE})
+        
+        return JsonResponse(ReadLaterService.toggle_read_later(materi_id, request.user))
+    else:
+        return JsonResponse({"success": False, "msg": UNSUPPORTED_MESSAGE})
+
+class StatisticsView(TemplateView):
+    template_name = "statistik.html"
+
+    def dispatch(self, request, *args, **kwargs):
+        if request.user.is_authenticated == False:
+            return HttpResponseRedirect(LOGIN_URL)
+        return super(StatisticsView, self).dispatch(request, *args, **kwargs)
+
+    def get_stat_json(self):
+        query = Category.objects.annotate(num=Count('materi')).order_by('-num')
+
+        # Take maximum 10 Category
+        result = []
+        for e in query:
+            if len(result) >= 6:
+                break
+            else:
+                result.append(e)
+
+        chart_data = {
+                'labels': [e.name for e in result],
+                'datasets': [{
+                    'label': 'Jumlah Materi per Kategori',
+                    'data': [e.num for e in result],
+                    'backgroundColor': [
+                        'rgba(255, 99, 132, 0.2)',
+                        'rgba(54, 162, 235, 0.2)',
+                        'rgba(255, 206, 86, 0.2)',
+                        'rgba(75, 192, 192, 0.2)',
+                        'rgba(153, 102, 255, 0.2)',
+                        'rgba(255, 159, 64, 0.2)'
+                    ],
+                    'borderColor': [
+                        'rgba(255, 99, 132, 1)',
+                        'rgba(54, 162, 235, 1)',
+                        'rgba(255, 206, 86, 1)',
+                        'rgba(75, 192, 192, 1)',
+                        'rgba(153, 102, 255, 1)',
+                        'rgba(255, 159, 64, 1)'
+                    ],
+                    'borderWidth': 1
+                }]
+            }
+        return chart_data
+
+    def get(self, request, *args, **kwargs):
+        if request.GET.get('data') == 'json':
+            return JsonResponse(self.get_stat_json())
+
+        else:
+            context = self.get_context_data(**kwargs)
+
+            query = Category.objects.annotate(num=Count('materi'))
+            context['stats'] = query
+
+            return self.render_to_response(context)
diff --git a/authentication/migrations/0009_user_is_email_verified.py b/authentication/migrations/0009_user_is_email_verified.py
new file mode 100644
index 0000000000000000000000000000000000000000..adf7d380f2cb8ee84b633398fae73c1bf1ba332a
--- /dev/null
+++ b/authentication/migrations/0009_user_is_email_verified.py
@@ -0,0 +1,18 @@
+# Generated by Django 3.1 on 2020-10-31 02:00
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('authentication', '0008_user_is_subscribing_to_material_comments'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='user',
+            name='is_email_verified',
+            field=models.BooleanField(default=False),
+        ),
+    ]
diff --git a/digipus/__pycache__/settings.cpython-36.pyc b/digipus/__pycache__/settings.cpython-36.pyc
index f90737f5493bb81ce1a0ac50e7dda3b442006d25..43d2d1c4169e7113ebfa287197b9840fd637a652 100644
Binary files a/digipus/__pycache__/settings.cpython-36.pyc and b/digipus/__pycache__/settings.cpython-36.pyc differ
diff --git a/docker-compose.yml b/docker-compose.yml
index a595488cc40c670433ef120484d40fb3b8627b70..4e5ad6dd70b2ccf2e693243aca890fac65884131 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -6,15 +6,15 @@ services:
     env_file:
       - .env
     ports:
-    - 8000:8000
+      - 8000:8000
     depends_on:
       - db
   db:
     image: postgres:12
     volumes:
-    - postgres_data:/var/lib/postgresql/data/
-    - static_volume:/home/digipus/staticfiles
-    - media_volume:/home/digipus/mediafiles
+      - postgres_data:/var/lib/postgresql/data/
+      - static_volume:/home/digipus/staticfiles
+      - media_volume:/home/digipus/mediafiles
     env_file:
       - .env.db
 
diff --git a/formating.sh b/formating.sh
index 573630b66894365044f164ee4d3dbcdf802480f4..73d7ed9afaf80894229017fb1c3d4c3430d093eb 100644
--- a/formating.sh
+++ b/formating.sh
@@ -1,3 +1,5 @@
+#!/bin/bash
+
 # Run autopep8
 git ls-files | grep -v 'migrations' | grep -v 'settings.py' | grep -v 'manage.py' | grep -E '.py$' | xargs autopep8 --in-place --recursive
 # Run pylint
diff --git a/register/forms.py b/register/forms.py
index 1b3b5ba717ffba57f45ea4d4c1f995be00ae2651..cf038b1c2867bd1d155932d8eeccc4e70ec8c192 100644
--- a/register/forms.py
+++ b/register/forms.py
@@ -46,9 +46,9 @@ class UserForm(forms.ModelForm):
         nomor_telpon = self.cleaned_data.get("nomor_telpon")
         if not User.objects.filter(nomor_telpon=nomor_telpon).exists():
             try:
-                no_telp = int(nomor_telpon)
+                int(nomor_telpon)
                 return nomor_telpon
-            except:
+            except ValueError:
                 raise forms.ValidationError("Hanya masukkan angka")
         raise forms.ValidationError("Nomor telepon sudah digunakan untuk mendaftar akun")
 
diff --git a/register/migrations/0001_initial.py b/register/migrations/0001_initial.py
index 1e4289a53802e6f31bc9a08e28ed9bccc4374dc6..b0aac4fa68c6c22d7c6dca72ea91c42504361238 100644
--- a/register/migrations/0001_initial.py
+++ b/register/migrations/0001_initial.py
@@ -1,4 +1,4 @@
-# Generated by Django 3.1 on 2020-10-30 16:27
+# Generated by Django 3.1 on 2020-10-31 02:00
 
 from django.conf import settings
 from django.db import migrations, models
diff --git a/register/templates/index.html b/register/templates/index.html
index 59038d68afae38d6f70fe4e4d419237c5fcbe6ce..3870a89255f5a95712e4c74ab58f64d7a119a7e6 100644
--- a/register/templates/index.html
+++ b/register/templates/index.html
@@ -42,53 +42,45 @@
                             Registrasi Kontributor
                         </div>
                         <div class="wrap-input100 validate-input" data-validate="Valid email is required: ex@abc.xyz">
-                            <!-- <input class="input100" type="text" name="nama"> -->
                             {{ form.name }}
                             <span class="focus-input100"></span>
                             <span class="label-input100">Nama</span>
                         </div>
                         <div class="wrap-input100 validate-input" data-validate="Password is required">
-                            <!-- <input class="input100" type="text" name="pekerjaan"> -->
                             {{ form.instansi }}
                             <span class="focus-input100"></span>
                             <span class="label-input100">Instansi/Pekerjaan</span>
                         </div>
                         <div class="wrap-input100 validate-input" data-validate="Valid email is required: ex@abc.xyz">
-                            <!-- <input class="input100" type="text" name="nama"> -->
                             {{ form.nik.errors }}
                             {{ form.nik }}
                             <span class="focus-input100"></span>
                             <span class="label-input100">NIK</span>
                         </div>
                         <div class="wrap-input100 validate-input" data-validate="Valid email is required: ex@abc.xyz">
-                            <!-- <input class="input100" type="text" name="nama"> -->
                             {{ form.alamat }}
                             <span class="focus-input100"></span>
                             <span class="label-input100">Alamat</span>
                         </div>
                         <div class="wrap-input100 validate-input" data-validate="Valid email is required: ex@abc.xyz">
-                            <!-- <input class="input100" type="text" name="nama"> -->
                             {{ form.email.errors }}
                             {{ form.email }}
                             <span class="focus-input100"></span>
                             <span class="label-input100">Email</span>
                         </div>
                         <div class="wrap-input100 validate-input" data-validate="Valid email is required: ex@abc.xyz">
-                            <!-- <input class="input100" type="text" name="nama"> -->
                             {{ form.nomor_telpon.errors}}
                             {{ form.nomor_telpon }}
                             <span class="focus-input100"></span>
                             <span class="label-input100">Nomor Telepon</span>
                         </div>
                         <div class="wrap-input100 validate-input" data-validate="Valid email is required: ex@abc.xyz">
-                            <!-- <input class="input100" type="text" name="nama"> -->
                             {{ form.password.errors }}
                             {{ form.password }}
                             <span class="focus-input100"></span>
                             <span class="label-input100">Kata Sandi</span>
                         </div>
                         <div class="wrap-input100 validate-input" data-validate="Valid email is required: ex@abc.xyz">
-                            <!-- <input class="input100" type="text" name="nama"> -->
                             {{ form.password2 }}
                             <span class="focus-input100"></span>
                             <span class="label-input100">Ketik Ulang Kata Sandi</span>
diff --git a/register/templates/index_admin.html b/register/templates/index_admin.html
index e1bfa08d23e59ead23670c8536ac12166057964d..aaf9eb6e344b81492f8af6b0fdda4da1c5a1053c 100644
--- a/register/templates/index_admin.html
+++ b/register/templates/index_admin.html
@@ -45,53 +45,45 @@
                             Registrasi Admin
                         </div>
                         <div class="wrap-input100 validate-input" data-validate="Valid email is required: ex@abc.xyz">
-                            <!-- <input class="input100" type="text" name="nama"> -->
                             {{ form.name }}
                             <span class="focus-input100"></span>
                             <span class="label-input100">Nama</span>
                         </div>
                         <div class="wrap-input100 validate-input" data-validate="Password is required">
-                            <!-- <input class="input100" type="text" name="pekerjaan"> -->
                             {{ form.instansi }}
                             <span class="focus-input100"></span>
                             <span class="label-input100">Instansi/Pekerjaan</span>
                         </div>
                         <div class="wrap-input100 validate-input" data-validate="Valid email is required: ex@abc.xyz">
-                            <!-- <input class="input100" type="text" name="nama"> -->
                             {{ form.nik.errors }}
                             {{ form.nik }}
                             <span class="focus-input100"></span>
                             <span class="label-input100">NIK</span>
                         </div>
                         <div class="wrap-input100 validate-input" data-validate="Valid email is required: ex@abc.xyz">
-                            <!-- <input class="input100" type="text" name="nama"> -->
                             {{ form.alamat }}
                             <span class="focus-input100"></span>
                             <span class="label-input100">Alamat</span>
                         </div>
                         <div class="wrap-input100 validate-input" data-validate="Valid email is required: ex@abc.xyz">
-                            <!-- <input class="input100" type="text" name="nama"> -->
                             {{ form.email.errors }}
                             {{ form.email }}
                             <span class="focus-input100"></span>
                             <span class="label-input100">Email</span>
                         </div>
                         <div class="wrap-input100 validate-input" data-validate="Valid email is required: ex@abc.xyz">
-                            <!-- <input class="input100" type="text" name="nama"> -->
                             {{ form.nomor_telpon.errors }}
                             {{ form.nomor_telpon }}
                             <span class="focus-input100"></span>
                             <span class="label-input100">Nomor Telepon</span>
                         </div>
                         <div class="wrap-input100 validate-input" data-validate="Valid email is required: ex@abc.xyz">
-                            <!-- <input class="input100" type="text" name="nama"> -->
                             {{ form.password.errors }}
                             {{ form.password }}
                             <span class="focus-input100"></span>
                             <span class="label-input100">Kata Sandi</span>
                         </div>
                         <div class="wrap-input100 validate-input" data-validate="Valid email is required: ex@abc.xyz">
-                            <!-- <input class="input100" type="text" name="nama"> -->
                             {{ form.password2 }}
                             <span class="focus-input100"></span>
                             <span class="label-input100">Ketik Ulang Kata Sandi</span>
diff --git a/register/templates/index_umum.html b/register/templates/index_umum.html
index 792c676faf603769a6723620270260af2b5931fa..7aa60dcb4c5066ab2b54dfd42b7b6cb65bc56960 100644
--- a/register/templates/index_umum.html
+++ b/register/templates/index_umum.html
@@ -42,53 +42,45 @@
                             Registrasi Umum
                         </div>
                         <div class="wrap-input100 validate-input" data-validate="Valid email is required: ex@abc.xyz">
-                            <!-- <input class="input100" type="text" name="nama"> -->
                             {{ form.name }}
                             <span class="focus-input100"></span>
                             <span class="label-input100">Nama</span>
                         </div>
                         <div class="wrap-input100 validate-input" data-validate="Password is required">
-                            <!-- <input class="input100" type="text" name="pekerjaan"> -->
                             {{ form.instansi }}
                             <span class="focus-input100"></span>
                             <span class="label-input100">Instansi/Pekerjaan</span>
                         </div>
                         <div class="wrap-input100 validate-input" data-validate="Valid email is required: ex@abc.xyz">
-                            <!-- <input class="input100" type="text" name="nama"> -->
                             {{ form.nik.errors }}
                             {{ form.nik }}
                             <span class="focus-input100"></span>
                             <span class="label-input100">NIK</span>
                         </div>
                         <div class="wrap-input100 validate-input" data-validate="Valid email is required: ex@abc.xyz">
-                            <!-- <input class="input100" type="text" name="nama"> -->
                             {{ form.alamat }}
                             <span class="focus-input100"></span>
                             <span class="label-input100">Alamat</span>
                         </div>
                         <div class="wrap-input100 validate-input" data-validate="Valid email is required: ex@abc.xyz">
-                            <!-- <input class="input100" type="text" name="nama"> -->
                             {{ form.email.errors }}
                             {{ form.email }}
                             <span class="focus-input100"></span>
                             <span class="label-input100">Email</span>
                         </div>
                         <div class="wrap-input100 validate-input" data-validate="Valid email is required: ex@abc.xyz">
-                            <!-- <input class="input100" type="text" name="nama"> -->
                             {{ form.nomor_telpon.errors}}
                             {{ form.nomor_telpon }}
                             <span class="focus-input100"></span>
                             <span class="label-input100">Nomor Telepon</span>
                         </div>
                         <div class="wrap-input100 validate-input" data-validate="Valid email is required: ex@abc.xyz">
-                            <!-- <input class="input100" type="text" name="nama"> -->
                             {{ form.password.errors }}
                             {{ form.password }}
                             <span class="focus-input100"></span>
                             <span class="label-input100">Kata Sandi</span>
                         </div>
                         <div class="wrap-input100 validate-input" data-validate="Valid email is required: ex@abc.xyz">
-                            <!-- <input class="input100" type="text" name="nama"> -->
                             {{ form.password2 }}
                             <span class="focus-input100"></span>
                             <span class="label-input100">Ketik Ulang Kata Sandi</span>
diff --git a/register/tests.py b/register/tests.py
index 799c0d2ea8e8610447e7397ca152b6a7f64e0b07..8aec57933bf62f767fe961bc83dab1c980912225 100644
--- a/register/tests.py
+++ b/register/tests.py
@@ -29,11 +29,11 @@ class RegisterPageTest(TestCase):
     def test_status_using_index_func(self):
         # Positive tests
         found = resolve("/registrasi/")
-        self.assertEqual(found.func.__name__, views.index.__name__)
+        self.assertEqual(found.func.__name__, views.Index.__name__)
 
         # Negative tests
         found = resolve("/admin/")
-        self.assertNotEqual(found.func, views.index)
+        self.assertNotEqual(found.func, views.Index)
 
     def test_register_title(self):
         response = Client().get("/registrasi/")
@@ -628,7 +628,7 @@ class RegisterPublicTest(TestCase):
 
         # Negative tests
         found = resolve("/admin/")
-        self.assertNotEqual(found.func, views.index)
+        self.assertNotEqual(found.func, views.Index)
 
     def test_register_title(self):
         response = Client().get(self.reg_path)
diff --git a/register/urls.py b/register/urls.py
index 53d6a6ed6a4629ab084d3bac362718086b6126f6..d3d7b0a6a1e75151b4f7e962289d4d1a22747188 100644
--- a/register/urls.py
+++ b/register/urls.py
@@ -5,7 +5,7 @@ from . import views
 app_name = "register"
 
 urlpatterns = [
-    path("", views.index.as_view()),
+    path("", views.Index.as_view()),
     path("umum/", views.RegistrasiUmum.as_view()),
     path("admin/", views.RegistrasiAdmin.as_view()),
     path("verify-email/<str:token>", views.verify_email, name='verify-email'),
diff --git a/register/views.py b/register/views.py
index 91952d6907cb91b74dfd2a6e5e451f0c0a0f26c4..c409342524aea3ddec0ffde708083df02e41bd3f 100644
--- a/register/views.py
+++ b/register/views.py
@@ -9,12 +9,14 @@ from django.db import models
 from django.http import HttpResponseRedirect
 from django.views.generic import TemplateView
 from django.core.exceptions import PermissionDenied
+from django.core.exceptions import ValidationError
 
 from register.forms import UserForm
 from register.services import RegistrationService
 
 
-class index(TemplateView):
+
+class Index(TemplateView):
     template_name = "index.html"
 
     def get_context_data(self, **kwargs):
@@ -134,7 +136,7 @@ def verify_email(request, token):
             return render(request, template_name, {
                 'message': f'Link verifikasi telah expire'
                 })
-    except:
+    except (User.DoesNotExist, ValidationError):
         return render(request, template_name, {
             'message': f'Email gagal diverifikasi'
             })