diff --git a/administration/services.py b/administration/services.py
new file mode 100644
index 0000000000000000000000000000000000000000..66cb34abca20fe9638c209298fa7796147453520
--- /dev/null
+++ b/administration/services.py
@@ -0,0 +1,206 @@
+from dateutil.relativedelta import relativedelta
+from django.db.models import Count
+from django.utils import timezone
+from datetime import datetime
+
+from administration.forms import PeriodForm
+from administration.models import VerificationSetting
+from app.models import LaporanMateri, Materi
+
+
+class DateHelperService:
+
+    @staticmethod
+    def get_start_end_date(period):
+        if period == 'ALL_TIME':
+            return (None, None)
+        elif period.split("_")[0] == "LAST":
+            end, start = DateHelperService.handle_last(period)
+            return (start, end)
+        elif period.split("_")[0] == "CURRENT":
+            end, start = DateHelperService.handle_current(period)
+            return (start, end)
+        elif period.split("_")[0] == "MINUS":
+            end, start = DateHelperService.handle_minus(period)
+            return (start, end)
+        else:
+            return (None, None)
+
+    @staticmethod
+    def handle_minus(period):
+        start = timezone.now()
+        end = None
+        delta = int(period.split("_")[1])
+        if period.split("_")[2] == "MONTH":
+            start = start.replace(day=1, hour=0, minute=0,
+                                  second=0, microsecond=0)
+            start = start - relativedelta(months=delta)
+            end = start + relativedelta(months=1)
+        elif period.split("_")[2] == "YEAR":
+            start = start.replace(month=1, day=1, hour=0,
+                                  minute=0, second=0, microsecond=0)
+            start = start - relativedelta(years=delta)
+            end = start + relativedelta(years=1)
+        return end, start
+
+    @staticmethod
+    def handle_current(period):
+        start = timezone.now()
+        end = None
+        if period.split("_")[1] == "MONTH":
+            start = start.replace(day=1, hour=0, minute=0,
+                                  second=0, microsecond=0)
+            end = start + relativedelta(months=1)
+        elif period.split("_")[1] == "YEAR":
+            start = start.replace(month=1, day=1, hour=0,
+                                  minute=0, second=0, microsecond=0)
+            end = start + relativedelta(years=1)
+        return end, start
+
+    @staticmethod
+    def handle_last(period):
+        end = timezone.now()
+        delta = 7
+        if period.split("_")[1] == "WEEK":
+            delta = 7
+        if period.split("_")[1] == "MONTH":
+            delta = 30
+        if period.split("_")[1] == "QUARTER":
+            delta = 90
+        if period.split("_")[1] == "YEAR":
+            delta = 365
+        start = end - relativedelta(days=delta)
+        return end, start
+
+class DetailVerificationService:
+
+    @staticmethod
+    def init_data(context, materi):
+        context["materi_data"] = materi
+        context["kriteria_list"] = VerificationSetting.objects.filter(
+            archived=False)
+        if materi.status == "PENDING" or materi.status == "REVISION":
+            riwayat = False
+        else:
+            riwayat = True
+        context["riwayat"] = riwayat
+        context["verification_report"] = materi.verificationreport_set.first()
+
+    @staticmethod
+    def initilize_kriteria(items) -> list:
+        kriteria = []
+        for item in items:
+            if "kriteria" in item[0]:
+                verification_item_id = item[0].split("-")[1]
+                verif_obj = VerificationSetting.objects.get(
+                    id=int(verification_item_id))
+                kriteria_value = {
+                    "title": verif_obj.title,
+                    "status": True if item[1] == "1" else False
+                }
+                kriteria.append(kriteria_value)
+        return kriteria
+
+    @staticmethod
+    def action_approve(materi):
+        DetailVerificationService.save_materi_status(materi, "APPROVE")
+
+    @staticmethod
+    def action_disapprove(materi):
+        DetailVerificationService.save_materi_status(materi, "DISAPPROVE")
+
+    @staticmethod
+    def save_materi_status(materi, status: str):
+        materi.status = status
+        materi.save()
+
+class StatisticService:
+
+    @staticmethod
+    def get_filter_set(period, kwargs):
+        filter_set = {}
+        form = PeriodForm()
+        DEFAULT_PERIOD = "LAST_MONTH"
+        if form.is_valid():
+            form_data = form.cleaned_data
+            if len(form_data["categories"]) == 0:
+                form_data["categories"] = None
+
+            if form_data["period"] == "":
+                form_data["period"] = DEFAULT_PERIOD
+
+            # check if using custom date range
+            end, start = StatisticService.init_date(DEFAULT_PERIOD, form_data)
+            filter_set["start"] = start
+            filter_set["end"] = end
+            filter_set["categories"] = form_data["categories"]
+        return filter_set
+
+    @staticmethod
+    def init_date(default_period, form_data):
+        if form_data["start_date"] is not None and form_data["end_date"] is not None:
+            start = timezone.make_aware(datetime.combine(
+                form_data["start_date"], datetime.min.time()))
+            end = timezone.make_aware(datetime.combine(
+                form_data["end_date"], datetime.min.time()))
+        elif form_data["period"] is not None:
+            start, end = DateHelperService.get_start_end_date(form_data["period"])
+        else:
+            start, end = DateHelperService.get_start_end_date(default_period)
+        return end, start
+
+    @staticmethod
+    def generate_view_stat(chart_data, colors, data_sets, label, time_step):
+        for label, color, dataset in zip(label, colors, data_sets):
+            result = {
+                "label": label,
+                "backgroundColor": color,
+                "borderColor": color,
+                "borderWidth": 1
+            }
+            data = []
+            total = 0
+            s = 0
+            e = 1
+            while e < len(time_step):
+                todays = dataset.filter(
+                    timestamp__gte=time_step[s], timestamp__lt=time_step[e]).count()
+                data.append(todays)
+                total += todays
+                s += 1
+                e += 1
+            result["data"] = data
+            chart_data["charts"].append(result)
+            chart_data["total"].append(total)
+
+
+    @staticmethod
+    def get_query_set(user, object_statistic, **kwargs):
+        if user.is_admin:
+            return object_statistic.objects.filter(materi__status="APPROVE")
+        else:
+            return object_statistic.objects.filter(materi__status="APPROVE", materi__uploader=user.id)
+
+
+    @staticmethod
+    def filter_qs(qs, filter_set):
+        if filter_set.get("categories", None) is not None:
+            qs = qs.filter(materi__categories__in=filter_set.get("categories"))
+        if filter_set.get("start") is not None and filter_set.get("end") is not None:
+            s = filter_set.get("start")
+            e = filter_set.get("end")
+            qs = qs.filter(timestamp__gte=s, timestamp__lt=e)
+        return qs.distinct()
+
+class LaporanMateriService:
+
+    @staticmethod
+    def initilize_report():
+        laporan_materi = LaporanMateri.objects.filter(is_rejected=False)
+        materi_dilaporkan = Materi.objects \
+            .filter(laporanmateri__id__in=laporan_materi, status="APPROVE") \
+            .annotate(jumlah_laporan=Count('laporanmateri__materi_id')) \
+            .order_by('-jumlah_laporan') \
+            .distinct()
+        materi_diblokir = Materi.objects.filter(status="BLOCKED")
+        return laporan_materi, materi_dilaporkan, materi_diblokir
diff --git a/administration/views.py b/administration/views.py
index 864f01e4963587b36be31a21ab89cf0284ad9e97..a4cdab81fc557da42e75d73185dab0936dd81f49 100644
--- a/administration/views.py
+++ b/administration/views.py
@@ -1,19 +1,15 @@
-import json
-from datetime import datetime, date
-
 from django.core.exceptions import PermissionDenied
 from django.contrib.auth.hashers import make_password
 from django.db.models import Count
 from django.http import HttpResponseRedirect, JsonResponse
-from django.shortcuts import get_object_or_404, render
+from django.shortcuts import get_object_or_404
 from django.views.generic import TemplateView, View
 from django.contrib import messages
-from django.utils import timezone, lorem_ipsum
-from dateutil.relativedelta import relativedelta
+from django.utils import timezone
 from administration.models import VerificationReport, VerificationSetting, DeletionHistory
 from administration.forms import CategoryForm, VerificationSettingForm, RegistrasiAdminForm, PeriodForm, EditAdminStatusForm
-from app.models import Category, Materi, ViewStatistics, DownloadStatistics, Comment, Like, getRandomColor, LaporanMateri
-from app.views import permission_denied
+from administration.services import StatisticService, DetailVerificationService, LaporanMateriService
+from app.models import Category, Materi, ViewStatistics, DownloadStatistics, Comment, Like, LaporanMateri
 from authentication.models import User
 from datetime import datetime
 
@@ -24,52 +20,6 @@ from django.core import management
 ADMINISTRATION_MANAGEMENT = "/administration/kelola-admin/"
 ADMINISTRATION_REPORT = "/administration/laporan-materi/"
 
-def get_start_end_date(period):
-    if period == 'ALL_TIME':
-        return (None, None)
-    elif period.split("_")[0] == "LAST":
-        end = timezone.now()
-        delta = 7
-        if period.split("_")[1] == "WEEK":
-            delta = 7
-        if period.split("_")[1] == "MONTH":
-            delta = 30
-        if period.split("_")[1] == "QUARTER":
-            delta = 90
-        if period.split("_")[1] == "YEAR":
-            delta = 365
-        start = end - relativedelta(days=delta)
-        return (start, end)
-    elif period.split("_")[0] == "CURRENT":
-        start = timezone.now()
-        if period.split("_")[1] == "MONTH":
-            start = start.replace(day=1, hour=0, minute=0,
-                                  second=0, microsecond=0)
-            end = start + relativedelta(months=1)
-            return (start, end)
-        elif period.split("_")[1] == "YEAR":
-            start = start.replace(month=1, day=1, hour=0,
-                                  minute=0, second=0, microsecond=0)
-            end = start + relativedelta(years=1)
-            return (start, end)
-    elif period.split("_")[0] == "MINUS":
-        start = timezone.now()
-        delta = int(period.split("_")[1])
-        if period.split("_")[2] == "MONTH":
-            start = start.replace(day=1, hour=0, minute=0,
-                                  second=0, microsecond=0)
-            start = start - relativedelta(months=delta)
-            end = start + relativedelta(months=1)
-            return (start, end)
-        elif period.split("_")[2] == "YEAR":
-            start = start.replace(month=1, day=1, hour=0,
-                                  minute=0, second=0, microsecond=0)
-            start = start - relativedelta(years=delta)
-            end = start + relativedelta(years=1)
-            return (start, end)
-    else:
-        return (None, None)
-
 
 class VerificationView(TemplateView):
     template_name = "verif.html"
@@ -104,44 +54,25 @@ class DetailVerificationView(TemplateView):
         context = super(DetailVerificationView,
                         self).get_context_data(**kwargs)
         materi = get_object_or_404(Materi, pk=kwargs["pk"])
-        context["materi_data"] = materi
-        context["kriteria_list"] = VerificationSetting.objects.filter(
-            archived=False)
-        if materi.status == "PENDING" or materi.status == "REVISION":
-            riwayat = False
-        else:
-            riwayat = True
-        context["riwayat"] = riwayat
-        context["verification_report"] = materi.verificationreport_set.first()
+        DetailVerificationService.init_data(context, materi)
         return context
 
+
     def post(self, request, *args, **kwargs):
         materi = get_object_or_404(Materi, pk=kwargs["pk"])
         feedback = request.POST.get("feedback", "")
         action = request.POST.get("action", None)
 
         report = {}
-        kriteria = []
-        for item in request.POST.items():
-            if "kriteria" in item[0]:
-                verification_item_id = item[0].split("-")[1]
-                verif_obj = VerificationSetting.objects.get(
-                    id=int(verification_item_id))
-                kriteria_value = {
-                    "title": verif_obj.title,
-                    "status": True if item[1] == "1" else False
-                }
-                kriteria.append(kriteria_value)
-
-        report["kriteria"] = kriteria
+        items = request.POST.items()
+
+        report["kriteria"] = DetailVerificationService.initilize_kriteria(items)
         report["feedback"] = feedback
 
         if action == "approve" and feedback != "":
-            materi.status = "APPROVE"
-            materi.save()
+            DetailVerificationService.action_approve(materi)
         elif action == "disapprove" and feedback != "":
-            materi.status = "DISAPPROVE"
-            materi.save()
+            DetailVerificationService.action_disapprove(materi)
         else:
             context = self.get_context_data(**kwargs)
             context["error"] = True
@@ -151,6 +82,7 @@ class DetailVerificationView(TemplateView):
         verif_report.save()
         return HttpResponseRedirect("/administration/")
 
+
     def get(self, request, *args, **kwargs):
         context = self.get_context_data(**kwargs)
         return self.render_to_response(context=context)
@@ -291,56 +223,9 @@ class StatisticsView(TemplateView):
         context["periodForm"] = PeriodForm(self.request.GET)
         return context
 
-    def get_filter_set(self, **kwargs):
-        filter_set = {}
-        form = PeriodForm(self.request.GET)
-        DEFAULT_PERIOD = "LAST_MONTH"
-        if form.is_valid():
-            form_data = form.cleaned_data
-            if len(form_data["categories"]) == 0:
-                form_data["categories"] = None
-
-            if form_data["period"] == "":
-                form_data["period"] = DEFAULT_PERIOD
-
-            # check if using custom date range
-            if form_data["start_date"] is not None and form_data["end_date"] is not None:
-                start = timezone.make_aware(datetime.combine(
-                    form_data["start_date"], datetime.min.time()))
-                end = timezone.make_aware(datetime.combine(
-                    form_data["end_date"], datetime.min.time()))
-            elif form_data["period"] is not None:
-                start, end = get_start_end_date(form_data["period"])
-            else:
-                start, end = get_start_end_date(DEFAULT_PERIOD)
-            filter_set["start"] = start
-            filter_set["end"] = end
-            filter_set["categories"] = form_data["categories"]
-        return filter_set
-
-    def get_query_set(self, object_statistic, **kwargs):
-        if self.request.user.is_admin:
-            return object_statistic.objects.filter(materi__status="APPROVE")
-        else:
-            return object_statistic.objects.filter(materi__status="APPROVE", materi__uploader=self.request.user.id)
-
-    def filter_qs(self, qs, filter_set):
-        if filter_set.get("categories", None) is not None:
-            qs = qs.filter(materi__categories__in=filter_set.get("categories"))
-        if filter_set.get("start") is not None and filter_set.get("end") is not None:
-            s = filter_set.get("start")
-            e = filter_set.get("end")
-            qs = qs.filter(timestamp__gte=s, timestamp__lt=e)
-        return qs.distinct()
 
-    # def generate_table_data(self, data_sets):
-    #     table_data = {}
 
-    #     counter = 1
-    #     row_materi_table = []
-    #     for item in data_sets:
 
-    #         # return res
 
     def generate_chart_data(self, start, end, data_sets):
         chart_data = {
@@ -354,41 +239,21 @@ class StatisticsView(TemplateView):
         for i in time_step:
             chart_data["label"].append(i.strftime("%d/%m/%Y"))
 
-        # generate view stat
-        for label, color, dataset in zip(label, colors, data_sets):
-            result = {
-                "label": label,
-                "backgroundColor": color,
-                "borderColor": color,
-                "borderWidth": 1
-            }
-            data = []
-            total = 0
-            s = 0
-            e = 1
-            while e < len(time_step):
-                todays = dataset.filter(
-                    timestamp__gte=time_step[s], timestamp__lt=time_step[e]).count()
-                data.append(todays)
-                total += todays
-                s += 1
-                e += 1
-            result["data"] = data
-            chart_data["charts"].append(result)
-            chart_data["total"].append(total)
+        StatisticService.generate_view_stat(chart_data, colors, data_sets, label, time_step)
         return chart_data
 
+
     def get(self, request, *args, **kwargs):
         context = self.get_context_data(**kwargs)
-        filter_set = self.get_filter_set(**kwargs)
-        view_set = self.get_query_set(ViewStatistics, **kwargs)
-        view_set = self.filter_qs(view_set, filter_set)
-        download_set = self.get_query_set(DownloadStatistics, **kwargs)
-        download_set = self.filter_qs(download_set, filter_set)
-        like_set = self.get_query_set(Like, **kwargs)
-        like_set = self.filter_qs(like_set, filter_set)
-        comment_set = self.get_query_set(Comment, **kwargs)
-        comment_set = self.filter_qs(comment_set, filter_set)
+        filter_set = StatisticService.get_filter_set(self.request.GET,**kwargs)
+        view_set = StatisticService.get_query_set(self.request.user,ViewStatistics, **kwargs)
+        view_set = StatisticService.filter_qs(view_set, filter_set)
+        download_set = StatisticService.get_query_set(self.request.user, DownloadStatistics, **kwargs)
+        download_set = StatisticService.filter_qs(download_set, filter_set)
+        like_set = StatisticService.get_query_set(self.request.user, Like, **kwargs)
+        like_set = StatisticService.filter_qs(like_set, filter_set)
+        comment_set = StatisticService.get_query_set(self.request.user, Comment, **kwargs)
+        comment_set = StatisticService.filter_qs(comment_set, filter_set)
 
         data_sets = [view_set, download_set, like_set, comment_set]
         chart_data = self.generate_chart_data(
@@ -657,13 +522,11 @@ class LaporanMateriView(TemplateView):
 
     def get(self, request, *args, **kwargs):
         context = super(LaporanMateriView, self).get_context_data(**kwargs)
-        context["laporan_materi"] = LaporanMateri.objects.filter(is_rejected=False)
-        context["materi_dilaporkan"] = Materi.objects \
-            .filter(laporanmateri__id__in=context["laporan_materi"], status="APPROVE") \
-            .annotate(jumlah_laporan=Count('laporanmateri__materi_id')) \
-            .order_by('-jumlah_laporan') \
-            .distinct()
-        context["materi_diblokir"] = Materi.objects.filter(status="BLOCKED")
+        laporan_materi, materi_dilaporkan, \
+        materi_diblokir = LaporanMateriService.initilize_report()
+        context["laporan_materi"] = laporan_materi
+        context["materi_dilaporkan"] = materi_dilaporkan
+        context["materi_diblokir"] = materi_diblokir
         return self.render_to_response(context=context)
 
 class LaporanMateriDetailView(TemplateView):
@@ -700,4 +563,3 @@ def tolak_laporan(request, *args, **kwargs):
     laporan.save()
 
     return HttpResponseRedirect(ADMINISTRATION_REPORT + str(laporan.materi_id))
-
diff --git a/app/services.py b/app/services.py
new file mode 100644
index 0000000000000000000000000000000000000000..65199ff7103c9fb56c63cef5b89984cf38495765
--- /dev/null
+++ b/app/services.py
@@ -0,0 +1,426 @@
+import datetime
+import os
+import random
+
+from django.contrib import messages
+from django.contrib.auth.models import AnonymousUser
+from django.core.exceptions import ValidationError
+from django.db.models import Count, Q
+from django.shortcuts import get_object_or_404
+from pydrive.auth import GoogleAuth
+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
+from app.utils.fileManagementUtil import get_random_filename, remove_image_exifdata
+from digipus import settings
+
+
+class DafterKatalogService:
+
+    @staticmethod
+    def search_materi(get_search, lst_materi, url):
+        url = url + "&search={0}".format(get_search)
+        lst_materi = (
+            lst_materi.search(get_search)
+                .filter(
+                Q(author__icontains=get_search)
+                | Q(uploader__name__icontains=get_search)
+                | Q(descriptions__icontains=get_search)
+                | Q(publisher__icontains=get_search)
+            )
+                .distinct()
+        )
+        return lst_materi, url
+
+    @staticmethod
+    def filter_materi_by_kategori(get_kategori, lst_materi, url):
+        url = url + "&kategori={0}".format(get_kategori)
+        kategori = Category.objects.get(pk=get_kategori)
+        lst_materi = lst_materi.filter(categories=kategori.pk)
+        return lst_materi, url
+
+    @staticmethod
+    def apply_sort(get_sort, lst_materi, url):
+        url = url + "&sort={0}".format(get_sort)
+        if (get_sort == "judul"):
+            lst_materi = lst_materi.order_by('title')
+        elif (get_sort == "penulis"):
+            lst_materi = lst_materi.order_by('author')
+        elif (get_sort == "pengunggah"):
+            lst_materi = lst_materi.order_by('uploader')
+        elif (get_sort == "terbaru"):
+            lst_materi = lst_materi.order_by('-date_created')
+        elif (get_sort == "terlama"):
+            lst_materi = lst_materi.order_by('date_created')
+        elif (get_sort == "terpopuler"):
+            lst_materi = lst_materi.annotate(count=Count('like__id')).order_by('-count')
+        elif (get_sort == "jumlah_unduh"):
+            lst_materi = lst_materi.annotate(count=Count('unduh__id')).order_by('-count')
+        return lst_materi, url
+
+    @staticmethod
+    def apply_options(lst_materi, request, url):
+        should_random = bool(request.GET.get("random"))
+        get_search = request.GET.get("search")
+        if get_search:
+            lst_materi, url = DafterKatalogService.search_materi(get_search, lst_materi, url)
+        get_kategori = request.GET.get("kategori")
+        if get_kategori:
+            lst_materi, url = DafterKatalogService.filter_materi_by_kategori(get_kategori, lst_materi, url)
+        get_sort = request.GET.get("sort")
+        if get_sort:
+            lst_materi, url = DafterKatalogService.apply_sort(get_sort, lst_materi, url)
+        if should_random:
+            lst_materi = DafterKatalogService.apply_random(lst_materi)
+        return lst_materi, url
+
+    @staticmethod
+    def apply_random(lst_materi):
+        return random.sample(list(lst_materi), len(lst_materi))
+
+
+class DetailMateriService:
+
+    @staticmethod
+    def init_context_data(context, materi, session):
+        context["session_id"] = session.session_key
+        context["materi_data"] = materi
+        context["report"] = VerificationReport.objects.filter(materi=materi)
+        context["has_liked"] = Like.objects.filter(materi=materi, session_id=session.session_key).exists()
+
+
+    @staticmethod
+    def set_published_date(materi):
+        published_date = ""
+        if materi.published_date == None:
+            published_date = "n.d"
+        else:
+            published_date = materi.published_date.strftime("%Y-%m-%d %H:%M")
+        return published_date
+
+
+    @staticmethod
+    def init_citation_and_materi_rating(context, materi, published_date, request):
+        citation_apa = materi.author + " . (" + published_date + ") . " + materi.title + " . " + materi.publisher
+        context["citationAPA"] = citation_apa
+        context["citationIEEE"] = CitationService.get_citation_ieee(request, materi)
+        context["materi_rating_score"] = 0
+
+    @staticmethod
+    def find_comment_like_dislike(query_set_for_comment, session):
+        has_liked = {}
+        has_disliked = {}
+        for comment in query_set_for_comment:
+            has_liked[comment.id] = LikeComment.objects.filter(comment=comment,
+                                                               session_id=session.session_key).exists()
+            has_disliked[comment.id] = DislikeComment.objects.filter(comment=comment,
+                                                                     session_id=session.session_key).exists()
+        return has_disliked, has_liked
+
+    @staticmethod
+    def get_user_name(request):
+        user_name = ""
+        if isinstance(request.user, AnonymousUser):
+            user_name = "Anonim"
+        elif request.user.is_admin:
+            user_name = "Admin"
+        elif request.user.is_contributor:
+            user_name = request.user.name
+        return user_name
+
+class CitationService:
+
+    @staticmethod
+    def get_citation_ieee(request, materi):
+        current_date = datetime.datetime.now()
+        current_day = str(current_date.day)
+        current_month = current_date.strftime("%b")
+        current_year = str(current_date.year)
+        published_date = CitationService._get_ieee_publish_date(materi)
+
+        author_list = materi.author.split(",")
+        author_list_abbrv = ""
+        for author_name in author_list:
+            author_name_split = author_name.split(" ")
+            author_name_abbrv = ""
+            for j, name in enumerate(author_name_split):
+                if j < (len(author_name_split) - 1):
+                    abbrv_name = name[0].upper()
+                    author_name_abbrv = author_name_abbrv + abbrv_name + ". "
+                else:
+                    author_name_abbrv = author_name_abbrv + name
+            author_list_abbrv = author_list_abbrv + author_name_abbrv + ", "
+
+        citation_result = (
+                author_list_abbrv
+                + materi.title
+                + ". "
+                + materi.publisher
+                + ", "
+                + published_date
+                + ". "
+                + "Accessed on: "
+                + current_month
+                + ". "
+                + current_day
+                + ", "
+                + current_year
+                + ". [Online]. "
+                + "Available: "
+                + request.build_absolute_uri()
+        )
+        return citation_result
+
+    @staticmethod
+    def _get_ieee_publish_date(materi):
+        if materi.published_date == None:
+            published_date = "n.d"
+        else:
+            published_date = materi.published_date.strftime("%Y")
+        return published_date
+
+class LikeDislikeService:
+
+    @staticmethod
+    def apply_like_materi(materi_id, session_id):
+        materi = get_object_or_404(Materi, pk=materi_id)
+        has_liked = Like.objects.filter(materi=materi, session_id=session_id).exists()
+        if has_liked:
+            like = get_object_or_404(Like, materi=materi, session_id=session_id)
+            like.delete()
+            response = {"success": True, "liked": True}
+        else:
+            Like(materi=materi, session_id=session_id).save()
+            response = {"success": True, "liked": False}
+        return response
+
+
+    @staticmethod
+    def apply_comment_like(comment_id, session_id):
+        comment = get_object_or_404(Comment, pk=comment_id)
+        has_liked = LikeComment.objects.filter(comment=comment, session_id=session_id).exists()
+        if has_liked:
+            like = get_object_or_404(LikeComment, comment=comment, session_id=session_id)
+            like.delete()
+            response = {"success": True, "liked": True, "comment_id": comment_id}
+        else:
+            LikeComment(comment=comment, session_id=session_id).save()
+            response = {"success": True, "liked": False, "comment_id": comment_id}
+        return response
+
+
+    @staticmethod
+    def apply_comment_dislike(comment_id, session_id):
+        comment = get_object_or_404(Comment, pk=comment_id)
+        has_disliked = DislikeComment.objects.filter(comment=comment, session_id=session_id).exists()
+        if has_disliked:
+            dislike = get_object_or_404(DislikeComment, comment=comment, session_id=session_id)
+            dislike.delete()
+            response = ({"success": True, "disliked": True, "comment_id": comment_id})
+        else:
+            DislikeComment(comment=comment, session_id=session_id).save()
+            response = ({"success": True, "disliked": False, "comment_id": comment_id})
+        return response
+
+
+class MateriFieldValidationHelperService:
+
+    @staticmethod
+    def validate_materi_rating_params(materi_id, rating_score):
+        if materi_id is None or rating_score is None:
+            response = {"success": False, "msg": "Missing param"}
+            status_code = 422
+            is_valid = False
+            return is_valid, materi_id, rating_score, response, status_code
+        try:
+            rating_score = int(rating_score)
+        except ValueError:
+            response = {"success": False, "msg": "rating_score must be an integer"}
+            status_code = 422
+            is_valid = False
+            return is_valid, materi_id, rating_score, response, status_code
+        try:
+            materi_id = int(materi_id)
+        except ValueError:
+            response = {"success": False, "msg": "materi_id must be an integer"}
+            status_code = 422
+            is_valid = False
+            return is_valid, materi_id, rating_score, response, status_code
+        if rating_score not in range(1, 6):
+            response = {"success": False, "msg": "Rating must be an integer from 1 to 5"}
+            status_code = 422
+            is_valid = False
+            return is_valid, materi_id, rating_score, response, status_code
+        return True, materi_id, rating_score, {}, 200
+
+
+
+    @staticmethod
+    def validate_materi_rating(materi_id, user):
+        materi = Materi.objects.filter(pk=materi_id).first()
+        if materi is None:
+            response = {"success": False, "msg": "Materi does not exist"}
+            status_code = 422
+            is_valid = False
+            return is_valid, materi, response, status_code
+        if Rating.objects.filter(materi=materi, user=user).first() is not None:
+            response = {"success": False, "msg": "Rating already exist"}
+            status_code = 409
+            is_valid = False
+            return is_valid, materi, response, status_code
+        return True, materi, {}, 201
+
+
+
+class DownloadViewMateriHelperService:
+
+    @staticmethod
+    def build_materi_response(fh, file_path, materi, mimetype, request, response):
+        response["Content-Disposition"] = "attachment; filename=" + os.path.basename(file_path)
+        if request.user.is_authenticated:
+            DownloadStatistics(materi=materi, downloader=request.user).save()
+        else:
+            downloaded_materi = DownloadStatistics.objects.create(materi=materi)
+            if "downloaded_materi" not in request.session:
+                request.session["downloaded_materi"] = []
+            request.session["downloaded_materi"].append(downloaded_materi.pk)
+            request.session.modified = True
+        return response
+
+    @staticmethod
+    def build_view_materi_response(file_path, materi, response):
+        response["Content-Disposition"] = "inline; filename=" + os.path.basename(file_path)
+        ViewStatistics(materi=materi).save()
+
+
+class UploadMateriService:
+
+    @staticmethod
+    def validate_extension(value):
+        ext = os.path.splitext(value.name)[1]  # [0] returns path+filename
+        valid_extensions = [".pdf", ".doc", ".docx", ".jpg", ".png", ".xlsx", ".xls", ".mp4", ".mp3"]
+        if not ext.lower() in valid_extensions:
+            raise ValidationError("Unsupported file extension.")
+
+    @staticmethod
+    def validate_file_extension(konten, request):
+        is_file_extension_valid = True
+        try:
+            UploadMateriService.validate_extension(konten)
+        except ValidationError:
+            messages.error(request, "Materi gagal diunggah, format file tidak sesuai")
+            is_file_extension_valid = False
+        return is_file_extension_valid
+
+    @staticmethod
+    def upload_materi(form, materi):
+        materi.save()
+        kateg = form.cleaned_data["categories"]
+        for i in kateg:
+            materi.categories.add(i)
+
+
+    @staticmethod
+    def validate_excel_categories(categories, excel, i, message):
+        for c in excel["Categories"][i].split(","):
+            sel_cat = categories.filter(name=c)
+            if sel_cat.count() == 0:
+                message = f"Kategori %s tidak ditemukan" % c
+                break
+        return message
+
+
+    @staticmethod
+    def validate_excel_field_length(excel, field_length, i, message):
+        if len(excel["Title"][i]) > field_length["title"]:
+            message = f"Title maksimal %d karakter" % field_length["title"]
+        if len(excel["Author"][i]) > field_length["author"]:
+            message = f"Author maksimal %d karakter" % field_length["author"]
+        if len(excel["Publisher"][i]) > field_length["publisher"]:
+            message = f"Publisher maksimal %d karakter" % field_length["publisher"]
+        return message
+
+    @staticmethod
+    def upload_materi_excel(categories, excel, request, row):
+        for i in range(row):
+            materi = Materi(
+                title=excel["Title"][i],
+                author=excel["Author"][i],
+                publisher=excel["Publisher"][i],
+                descriptions=excel["Description"][i],
+                uploader=request.user,
+            )
+            materi.save()
+
+            for c in excel["Categories"][i].split(","):
+                materi.categories.add(categories.get(name=c))
+
+class EditProfileService:
+
+    @staticmethod
+    def update_profile_picture(current_user, request):
+        f_name = request.FILES["profile_picture"].name
+        f_name = get_random_filename(f_name)
+        f_path = settings.MEDIA_ROOT + "/" + f_name
+        request.FILES["profile_picture"].name = f_name
+        form = SuntingProfilForm(request.POST, request.FILES, instance=current_user)
+        form.save()
+        remove_image_exifdata(f_path)
+
+class RevisiMateriService:
+
+    @staticmethod
+    def revisi_materi(form, request):
+        materi = form.save(commit=False)
+        materi.uploader = request.user
+        materi.status = "REVISION"
+        materi.save()
+        kateg = form.cleaned_data["categories"]
+        for i in kateg:
+            materi.categories.add(i)
+        materi.save()
+
+
+class DownloadHistoryService:
+
+    @staticmethod
+    def get_riwayat_as_guest(request):
+        has_downloaded_materi = "downloaded_materi" in request.session
+        downloaded_materi = request.session["downloaded_materi"] if has_downloaded_materi else []
+        riwayat_list = DownloadStatistics.objects.filter(pk__in=downloaded_materi).order_by("-timestamp")
+        return riwayat_list
+
+    @staticmethod
+    def get_riwayat_authenticated_user(current_user):
+        riwayat_list = current_user.riwayat_unduh.all().order_by("-timestamp")
+        return riwayat_list
+
+    @staticmethod
+    def init_data_guest_user(context, request):
+        riwayat_list = DownloadHistoryService.get_riwayat_as_guest(request)
+        context["riwayat_list"] = riwayat_list
+        context["user_name"] = "Guest"
+
+    @staticmethod
+    def init_data_authenticated_user(context, current_user):
+        riwayat_list = DownloadHistoryService.get_riwayat_authenticated_user(current_user)
+        context["riwayat_list"] = riwayat_list
+        context["user_name"] = current_user.name
+
+
+class GoogleDriveUploadService:
+
+    @staticmethod
+    def upload_to_gdrive(file_path, title):
+        gauth = GoogleAuth()
+        gauth.LocalWebserverAuth()
+
+        drive = GoogleDrive(gauth)
+        file1 = drive.CreateFile()
+        file1.SetContentFile(file_path)
+        file1["title"] = title
+        print("title: %s, mimeType: %s" % (file1["title"], file1["mimeType"]))
+        file1.Upload()
diff --git a/app/tests.py b/app/tests.py
index 6f040bdb2730f9345e35723f04c5731ff4681194..a233ab6f822bf9590054382bd09b1ec5464561df 100644
--- a/app/tests.py
+++ b/app/tests.py
@@ -584,13 +584,13 @@ class DetailMateriTest(TestCase):
         response = Client().get(self.url)
         self.assertContains(response, "Google Drive")
 
-    @mock.patch("app.views.upload_to_gdrive")
+    @mock.patch("app.services.GoogleDriveUploadService.upload_to_gdrive")
     def test_save_to_gdrive_with_nonexistent_materi(self, mock_upload_to_gdrive):
         response = self.client.get("/materi/%s/save-to-gdrive/" % 0)
         mock_upload_to_gdrive.assert_not_called()
         self.assertEqual(response.status_code, 404)
 
-    @mock.patch("app.views.upload_to_gdrive")
+    @mock.patch("app.services.GoogleDriveUploadService.upload_to_gdrive")
     def test_save_to_gdrive_with_valid_materi(self, mock_upload_to_gdrive):
         response = self.client.get("/materi/%s/save-to-gdrive/" % self.materi1.id, follow=True)
         last_url, status_code = response.redirect_chain[-1]
diff --git a/app/views.py b/app/views.py
index c48aebbc8659d0532996163478278099cc975852..ce550f5f8f3e95999c111f89f65f3c9f8eb9bcf2 100644
--- a/app/views.py
+++ b/app/views.py
@@ -1,45 +1,35 @@
 import mimetypes
 import os
-import datetime
+from io import BytesIO
 
+import django
+import pandas as pd
 from django.conf import settings
 from django.contrib import messages
-from django.contrib.auth.models import AnonymousUser
-from django.core.exceptions import PermissionDenied, ValidationError
-from django.db.models import Q, Count
+from django.core.exceptions import PermissionDenied
+from django.core.paginator import Paginator
+from django.db.models import Q
 from django.http import (Http404, HttpResponse, HttpResponseRedirect,
                          JsonResponse)
-from django.urls import reverse
 from django.shortcuts import get_object_or_404, redirect
 from django.template import loader
+from django.urls import reverse
 from django.views import defaults
-from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
 from django.views.generic import TemplateView
-from administration.models import VerificationReport
+
 from app.forms import SuntingProfilForm, UploadMateriForm, RatingContributorForm
 from app.models import (
     Category,
     Comment,
-    DislikeComment,
     Materi,
-    Like,
-    LikeComment,
-    ViewStatistics,
-    DownloadStatistics,
     ReqMaterial,
     Rating,
-    RatingContributor,
 )
-from app.utils.fileManagementUtil import get_random_filename, remove_image_exifdata
 from authentication.models import User
-import django
-import pandas as pd
-from io import BytesIO
-from django.contrib import messages
-from pydrive.auth import GoogleAuth
-from pydrive.drive import GoogleDrive
-from pydrive.auth import AuthenticationRejected
-import random
+from .services import DafterKatalogService, DetailMateriService, LikeDislikeService, MateriFieldValidationHelperService, \
+    DownloadViewMateriHelperService, UploadMateriService, EditProfileService, RevisiMateriService, \
+    DownloadHistoryService, GoogleDriveUploadService
+
 
 def permission_denied(request, exception, template_name="error_403.html"):
     return defaults.permission_denied(request, exception, template_name)
@@ -59,47 +49,8 @@ class DaftarKatalog(TemplateView):
         lstMateri = Materi.objects.filter(status="APPROVE").order_by("date_modified")
         url = ""
 
-        getSearch = request.GET.get("search")
-        if getSearch:
-            url = url + "&search={0}".format(getSearch)
-            lstMateri = (
-                lstMateri.search(getSearch)
-                .filter(
-                    Q(author__icontains=getSearch)
-                    | Q(uploader__name__icontains=getSearch)
-                    | Q(descriptions__icontains=getSearch)
-                    | Q(publisher__icontains=getSearch)
-                )
-                .distinct()
-            )
+        lstMateri, url = DafterKatalogService.apply_options(lstMateri, request, url)
 
-        getKategori = request.GET.get("kategori")
-        if getKategori:
-            url = url + "&kategori={0}".format(getKategori)
-            kategori = Category.objects.get(pk=getKategori)
-            lstMateri = lstMateri.filter(categories=kategori.pk)
-
-        getSort = request.GET.get("sort")
-        if getSort:
-            url = url + "&sort={0}".format(getSort)
-            if(getSort == "judul"):
-                lstMateri = lstMateri.order_by('title')
-            elif(getSort == "penulis"):
-                lstMateri = lstMateri.order_by('author')
-            elif(getSort == "pengunggah"):
-                lstMateri = lstMateri.order_by('uploader')
-            elif(getSort == "terbaru"):
-                lstMateri = lstMateri.order_by('-date_created')
-            elif(getSort == "terlama"):
-                lstMateri = lstMateri.order_by('date_created')
-            elif(getSort == "terpopuler"):
-                lstMateri = lstMateri.annotate(count=Count('like__id')).order_by('-count')
-            elif(getSort == "jumlah_unduh"):
-                lstMateri = lstMateri.annotate(count=Count('unduh__id')).order_by('-count')
-
-        should_random = bool(request.GET.get("random"))
-        if should_random:
-            lstMateri = random.sample(list(lstMateri), len(lstMateri))
 
         context["materi_list"] = lstMateri
         paginator = Paginator(context["materi_list"], 15)
@@ -150,19 +101,9 @@ class DetailMateri(TemplateView):
         if not self.request.session or not self.request.session.session_key:
             self.request.session.save()
         materi = get_object_or_404(Materi, pk=kwargs["pk"])
-        context["session_id"] = self.request.session.session_key
-        context["materi_data"] = materi
-        context["report"] = VerificationReport.objects.filter(materi=materi)
-        context["has_liked"] = Like.objects.filter(materi=materi, session_id=self.request.session.session_key).exists()
-        publishedDate = ""
-        if materi.published_date == None:
-            publishedDate = "n.d"
-        else:
-            publishedDate = materi.published_date.strftime("%Y-%m-%d %H:%M")
-        citationAPA = materi.author + " . (" + publishedDate + ") . " + materi.title + " . " + materi.publisher
-        context["citationAPA"] = citationAPA
-        context["citationIEEE"] = get_citation_ieee(self.request, materi)
-        context["materi_rating_score"] = 0
+        DetailMateriService.init_context_data(context, materi, self.request.session)
+        published_date = DetailMateriService.set_published_date(materi)
+        DetailMateriService.init_citation_and_materi_rating(context, materi, published_date, self.request)
 
         if self.request.user.is_authenticated:
             materi_rating = Rating.objects.filter(materi=materi, user=self.request.user).first()
@@ -173,28 +114,17 @@ class DetailMateri(TemplateView):
 
         return context
 
+
     def get(self, request, *args, **kwargs):
         context = self.get_context_data(**kwargs)
         query_set_for_comment = Comment.objects.filter(materi=context["materi_data"])
-        has_liked = {}
-        has_disliked = {}
-        for comment in query_set_for_comment:
-            has_liked[comment.id] = LikeComment.objects.filter(comment=comment, session_id=self.request.session.session_key).exists()
-            has_disliked[comment.id] = DislikeComment.objects.filter(comment=comment, session_id=self.request.session.session_key).exists()
+        has_disliked, has_liked = DetailMateriService.find_comment_like_dislike(query_set_for_comment, self.request.session)
         context["comment_data"] = query_set_for_comment
         context["has_liked"] = has_liked
         context["has_disliked"] = has_disliked
         return self.render_to_response(context=context)
 
-    def get_user_name(self, request):
-        user_name = ""
-        if isinstance(request.user, AnonymousUser):
-            user_name = "Anonim"
-        elif request.user.is_admin:
-            user_name = "Admin"
-        elif request.user.is_contributor:
-            user_name = request.user.name
-        return user_name
+
 
     def post(self, request, *args, **kwargs):
         commentText = request.POST.get("comment", None)
@@ -210,7 +140,7 @@ class DetailMateri(TemplateView):
         user_obj = request.user if request.user.is_authenticated else None
         if user_obj:
             comment = Comment.objects.create(
-                comment=commentText, username=self.get_user_name(request), materi=materi, user=user_obj
+                comment=commentText, username=DetailMateriService.get_user_name(request), materi=materi, user=user_obj
             )
             comment.save()
         return HttpResponseRedirect(request.path)
@@ -222,15 +152,7 @@ def toggle_like(request):
         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"})
-        materi = get_object_or_404(Materi, pk=materi_id)
-        has_liked = Like.objects.filter(materi=materi, session_id=session_id).exists()
-        if has_liked:
-            like = get_object_or_404(Like, materi=materi, session_id=session_id)
-            like.delete()
-            return JsonResponse({"success": True, "liked": True})
-        else:
-            Like(materi=materi, session_id=session_id).save()
-            return JsonResponse({"success": True, "liked": False})
+        return JsonResponse(LikeDislikeService.apply_like_materi(materi_id, session_id))
     else:
         return JsonResponse({"success": False, "msg": "Unsuported method"})
 
@@ -244,85 +166,28 @@ def delete_comment(request, pk_materi, pk_comment):
     return HttpResponseRedirect(url)
 
 def toggle_like_comment(request):
+    comment_id = 0
     if request.method == "POST":
         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})
-        comment = get_object_or_404(Comment, pk=comment_id)
-        has_liked = LikeComment.objects.filter(comment=comment, session_id=session_id).exists()
-        if has_liked:
-            like = get_object_or_404(LikeComment, comment=comment, session_id=session_id)
-            like.delete()
-            return JsonResponse({"success": True, "liked": True, "comment_id": comment_id})
-        else:
-            LikeComment(comment=comment, session_id=session_id).save()
-            return JsonResponse({"success": True, "liked": False, "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})
 
+
 def toggle_dislike_comment(request):
+    comment_id = 0
     if request.method == "POST":
         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})
-        comment = get_object_or_404(Comment, pk=comment_id)
-        has_disliked = DislikeComment.objects.filter(comment=comment, session_id=session_id).exists()
-        if has_disliked:
-            dislike = get_object_or_404(DislikeComment, comment=comment, session_id=session_id)
-            dislike.delete()
-            return JsonResponse({"success": True, "disliked": True, "comment_id": comment_id})
-        else:
-            DislikeComment(comment=comment, session_id=session_id).save()
-            return JsonResponse({"success": True, "disliked": False, "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})
 
-def get_citation_ieee(request, materi):
-    current_date = datetime.datetime.now()
-    current_day = str(current_date.day)
-    current_month = current_date.strftime("%b")
-    current_year = str(current_date.year)
-    published_date = ""
-    if materi.published_date == None:
-        published_date = "n.d"
-    else:
-        published_date = materi.published_date.strftime("%Y")
-
-    author_list = materi.author.split(",")
-    author_list_abbrv = ""
-    for author_name in author_list:
-        author_name_split = author_name.split(" ")
-        author_name_abbrv = ""
-        for j, name in enumerate(author_name_split):
-            if j < (len(author_name_split) - 1):
-                abbrv_name = name[0].upper()
-                author_name_abbrv = author_name_abbrv + abbrv_name + ". "
-            else:
-                author_name_abbrv = author_name_abbrv + name
-        author_list_abbrv = author_list_abbrv + author_name_abbrv + ", "
-
-    citation_result = (
-        author_list_abbrv
-        + materi.title
-        + ". "
-        + materi.publisher
-        + ", "
-        + published_date
-        + ". "
-        + "Accessed on: "
-        + current_month
-        + ". "
-        + current_day
-        + ", "
-        + current_year
-        + ". [Online]. "
-        + "Available: "
-        + request.build_absolute_uri()
-    )
-    return citation_result
-
 
 def add_rating_materi(request):
     if request.method == "POST" and request.user.is_authenticated:
@@ -330,28 +195,21 @@ def add_rating_materi(request):
         materi_id = request.POST.get("materi_id", None)
         rating_score = request.POST.get("rating_score", None)
 
-        if materi_id is None or rating_score is None:
-            return JsonResponse({"success": False, "msg": "Missing param"}, status=422)
-        try:
-            rating_score = int(rating_score)
-        except ValueError:
-            return JsonResponse({"success": False, "msg": "rating_score must be an integer"}, status=422)
 
-        try:
-            materi_id = int(materi_id)
-        except ValueError:
-            return JsonResponse({"success": False, "msg": "materi_id must be an integer"}, status=422)
+        is_valid_params, materi_id, \
+        rating_score, response, \
+        status_code = MateriFieldValidationHelperService.\
+            validate_materi_rating_params(materi_id,rating_score)
 
-        if rating_score not in range(1, 6):
-            return JsonResponse({"success": False, "msg": "Rating must be an integer from 1 to 5"}, status=422)
+        if not is_valid_params:
+            return JsonResponse(response, status=status_code)
 
-        materi = Materi.objects.filter(pk=materi_id).first()
+        is_valid_rating, materi, \
+        response, status_code = MateriFieldValidationHelperService.\
+            validate_materi_rating(materi_id, request.user)
 
-        if materi is None:
-            return JsonResponse({"success": False, "msg": "Materi does not exist"}, status=422)
-
-        if Rating.objects.filter(materi=materi, user=request.user).first() is not None:
-            return JsonResponse({"success": False, "msg": "Rating already exist"}, status=409)
+        if not is_valid_rating:
+            return JsonResponse(response, status=status_code)
 
         Rating(materi=materi, user=request.user, score=rating_score).save()
         return JsonResponse(
@@ -360,6 +218,7 @@ def add_rating_materi(request):
     return JsonResponse({"success": False, "msg": "Forbidden"}, status=403)
 
 
+
 def download_materi(request, pk):
     materi = get_object_or_404(Materi, pk=pk)
     path = materi.content.path
@@ -368,17 +227,9 @@ def download_materi(request, pk):
         try:
             mimetype = mimetypes.guess_type(file_path)
             with open(file_path, "rb") as fh:
-                response = HttpResponse(fh.read(), content_type=mimetype[0])
-                response["Content-Disposition"] = "attachment; filename=" + os.path.basename(file_path)
-                if request.user.is_authenticated:
-                    DownloadStatistics(materi=materi, downloader=request.user).save()
-                else:
-                    downloaded_materi = DownloadStatistics.objects.create(materi=materi)
-                    if "downloaded_materi" not in request.session:
-                        request.session["downloaded_materi"] = []
-                    request.session["downloaded_materi"].append(downloaded_materi.pk)
-                    request.session.modified = True
-                return response
+                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.")
     else:
@@ -394,8 +245,7 @@ def view_materi(request, pk):
         try:
             with open(file_path, "rb") as fh:
                 response = HttpResponse(fh.read(), content_type=mimetype[0])
-                response["Content-Disposition"] = "inline; filename=" + os.path.basename(file_path)
-                ViewStatistics(materi=materi).save()
+                DownloadViewMateriHelperService.build_view_materi_response(file_path, materi, response)
                 return response
         except Exception as e:
             raise Http404("File tidak dapat ditemukan.")
@@ -424,15 +274,9 @@ class UploadMateriView(TemplateView):
             materi = form.save(commit=False)
             materi.uploader = request.user
             konten = form.cleaned_data["content"]
-            try:
-                self.validate_file_extension(konten)
-            except ValidationError:
-                messages.error(request, "Materi gagal diunggah, format file tidak sesuai")
+            if not UploadMateriService.validate_file_extension(konten, request):
                 return HttpResponseRedirect("/unggah/")
-            materi.save()
-            kateg = form.cleaned_data["categories"]
-            for i in kateg:
-                materi.categories.add(i)
+            UploadMateriService.upload_materi(form, materi)
             messages.success(request, "Materi berhasil diunggah, periksa riwayat unggah anda")
             return HttpResponseRedirect("/unggah/")
         else:
@@ -441,6 +285,8 @@ class UploadMateriView(TemplateView):
             messages.error(request, "Terjadi kesalahan pada pengisian data")
             return self.render_to_response(context)
 
+
+
     def get(self, request, *args, **kwargs):
         if request.user.is_authenticated == False or not request.user.is_contributor:
             raise PermissionDenied(request)
@@ -449,11 +295,7 @@ class UploadMateriView(TemplateView):
         context["form"] = UploadMateriForm
         return self.render_to_response(context)
 
-    def validate_file_extension(self, value):
-        ext = os.path.splitext(value.name)[1]  # [0] returns path+filename
-        valid_extensions = [".pdf", ".doc", ".docx", ".jpg", ".png", ".xlsx", ".xls", ".mp4", ".mp3"]
-        if not ext.lower() in valid_extensions:
-            raise ValidationError("Unsupported file extension.")
+
 
 
 class UploadMateriHTML(TemplateView):
@@ -518,20 +360,9 @@ class UploadMateriExcelView(TemplateView):
         for i in range(row):
 
             # Validate Categories
-            for c in excel["Categories"][i].split(","):
-                sel_cat = categories.filter(name=c)
-                if sel_cat.count() == 0:
-                    message = f"Kategori %s tidak ditemukan" % c
-                    break
-
-            if len(excel["Title"][i]) > field_length["title"]:
-                message = f"Title maksimal %d karakter" % field_length["title"]
-
-            if len(excel["Author"][i]) > field_length["author"]:
-                message = f"Author maksimal %d karakter" % field_length["author"]
+            message = UploadMateriService.validate_excel_categories(categories, excel, i, message)
 
-            if len(excel["Publisher"][i]) > field_length["publisher"]:
-                message = f"Publisher maksimal %d karakter" % field_length["publisher"]
+            message = UploadMateriService.validate_excel_field_length(excel, field_length, i, message)
 
             if message != None:
                 break
@@ -542,24 +373,15 @@ class UploadMateriExcelView(TemplateView):
 
         # Second pass, save data
         with django.db.transaction.atomic():
-            for i in range(row):
-                materi = Materi(
-                    title=excel["Title"][i],
-                    author=excel["Author"][i],
-                    publisher=excel["Publisher"][i],
-                    descriptions=excel["Description"][i],
-                    uploader=request.user,
-                )
-                materi.save()
-
-                for c in excel["Categories"][i].split(","):
-                    materi.categories.add(categories.get(name=c))
+            UploadMateriService.upload_materi_excel(categories, excel, request, row)
 
         messages.success(request, "Materi berhasil diunggah")
 
         return HttpResponseRedirect("/unggah_excel/")
 
 
+
+
 class DashboardKontributorView(TemplateView):
     template_name = "dashboard.html"
 
@@ -651,15 +473,7 @@ class SuntingProfilView(TemplateView):
 
             # Removing exifdata from profile picture on upload
             if request.FILES:
-                f_name = request.FILES["profile_picture"].name
-                f_name = get_random_filename(f_name)
-                f_path = settings.MEDIA_ROOT + "/" + f_name
-                request.FILES["profile_picture"].name = f_name
-
-                form = SuntingProfilForm(request.POST, request.FILES, instance=current_user)
-                form.save()
-
-                remove_image_exifdata(f_path)
+                EditProfileService.update_profile_picture(current_user, request)
             else:
                 form.save()
             return HttpResponseRedirect("/profil/")
@@ -702,15 +516,7 @@ class SuntingProfilAdminView(TemplateView):
 
             # Removing exifdata from profile picture on upload
             if request.FILES:
-                f_name = request.FILES["profile_picture"].name
-                f_name = get_random_filename(f_name)
-                f_path = settings.MEDIA_ROOT + "/" + f_name
-                request.FILES["profile_picture"].name = f_name
-
-                form = SuntingProfilForm(request.POST, request.FILES, instance=current_user)
-                form.save()
-
-                remove_image_exifdata(f_path)
+                EditProfileService.update_profile_picture(current_user, request)
             else:
                 form.save()
             return HttpResponseRedirect("/profil-admin/")
@@ -839,14 +645,7 @@ class RevisiMateriView(TemplateView):
         current_materi = get_object_or_404(Materi, pk=kwargs["pk"])
         form = UploadMateriForm(request.POST, request.FILES, instance=current_materi)
         if form.is_valid():
-            materi = form.save(commit=False)
-            materi.uploader = request.user
-            materi.status = "REVISION"
-            materi.save()
-            kateg = form.cleaned_data["categories"]
-            for i in kateg:
-                materi.categories.add(i)
-            materi.save()
+            RevisiMateriService.revisi_materi(form, request)
             return HttpResponseRedirect("/dashboard/")
         else:
             context = self.get_context_data(**kwargs)
@@ -881,36 +680,18 @@ class DownloadHistoryView(TemplateView):
         context = self.get_context_data(**kwargs)
         if request.user.is_authenticated:
             current_user = self.request.user
-            riwayat_list = current_user.riwayat_unduh.all().order_by("-timestamp")
-            context["riwayat_list"] = riwayat_list
-            context["user_name"] = current_user.name
+            DownloadHistoryService.init_data_authenticated_user(context, current_user)
         else:
-            has_downloaded_materi = "downloaded_materi" in request.session
-            downloaded_materi = request.session["downloaded_materi"] if has_downloaded_materi else []
-            riwayat_list = DownloadStatistics.objects.filter(pk__in=downloaded_materi).order_by("-timestamp")
-            context["riwayat_list"] = riwayat_list
-            context["user_name"] = "Guest"
+            DownloadHistoryService.init_data_guest_user(context, request)
         return self.render_to_response(context)
 
 
-def upload_to_gdrive(file_path, title):
-    gauth = GoogleAuth()
-    gauth.LocalWebserverAuth()
-
-    drive = GoogleDrive(gauth)
-    file1 = drive.CreateFile()
-    file1.SetContentFile(file_path)
-    file1["title"] = title
-    print("title: %s, mimeType: %s" % (file1["title"], file1["mimeType"]))
-    file1.Upload()
-
-
 def save_to_gdrive(request, pk):
     materi = get_object_or_404(Materi, pk=pk)
     path = materi.content.path
     file_path = os.path.join(settings.MEDIA_ROOT, path)
     if os.path.exists(file_path):
-        upload_to_gdrive(file_path, materi.title)
+        GoogleDriveUploadService.upload_to_gdrive(file_path, materi.title)
     else:
         raise Http404("File tidak dapat ditemukan.")
 
diff --git a/authentication/services.py b/authentication/services.py
new file mode 100644
index 0000000000000000000000000000000000000000..9b3c3d30dc97ea00637f3a41726f950b745101e3
--- /dev/null
+++ b/authentication/services.py
@@ -0,0 +1,22 @@
+from django.contrib.auth import login
+from django.http import QueryDict
+
+
+class LoginService:
+    @staticmethod
+    def get_user_redirect_url(request):
+        if request.user.is_admin:
+            redirect_to = "/sukses-admin/"
+        elif request.user.is_contributor:
+            redirect_to = "/sukses-kontributor/"
+        else:
+            redirect_to = "/"
+        return redirect_to
+    @staticmethod
+    def get_redirect_login_sucess(request, user):
+        login(request, user)
+        querystring = QueryDict(request.META['QUERY_STRING'])
+        redirect_to = LoginService.get_user_redirect_url(request)
+        if 'next' in querystring:
+            redirect_to = querystring['next']
+        return redirect_to
diff --git a/authentication/views.py b/authentication/views.py
index 03408cbaf9d5204e6bf89900b327cc6f1e564691..c33e983e6562b3e40b242cdd54a447a0db57d7ff 100644
--- a/authentication/views.py
+++ b/authentication/views.py
@@ -3,20 +3,49 @@ from django.http import HttpResponseRedirect, QueryDict
 from django.views.generic import TemplateView
 from django.conf import settings
 from django.contrib import messages
+from .services import LoginService
 import urllib
 import json
 
+
+
 class Login(TemplateView):
     
     def dispatch(self, request, *args, **kwargs):
         if request.user.is_authenticated:
-            if request.user.is_admin:
-                redirect_to = "/sukses-admin/"
-            elif request.user.is_contributor:
-                redirect_to = "/sukses-kontributor/"
+            redirect_to = LoginService.get_user_redirect_url(request)
             return HttpResponseRedirect(redirect_to)
         return super(Login, self).dispatch(request, *args, **kwargs)
 
+
+    def do_login(self, context, email, password, request, result):
+        if result['success']:
+            return self.handle_recaptcha_success(context, email, password, request)
+        else:
+            messages.error(request, 'Invalid reCAPTCHA. Please try again.')
+            return self.render_to_response(context=context)
+
+    def handle_recaptcha_success(self, context, email, password, request):
+        user = authenticate(email=email, password=password)
+        if user is not None:
+            redirect_to = LoginService.get_redirect_login_sucess(request, user)
+            return HttpResponseRedirect(redirect_to)
+        else:
+            context["error_message"] = "Email atau Password anda salah."
+            return self.render_to_response(context=context)
+
+    def get_recaptcha_result(self, recaptcha_response):
+        url = 'https://www.google.com/recaptcha/api/siteverify'
+        values = {
+            'secret': settings.GOOGLE_RECAPTCHA_SECRET_KEY,
+            'response': recaptcha_response
+        }
+        data = urllib.parse.urlencode(values).encode()
+        req = urllib.request.Request(url, data=data)
+        response = urllib.request.urlopen(req)
+        result = json.loads(response.read().decode())
+        return result
+
     def get_template_names(self):
         if self.request.path == "/login_admin/":
             template_name = "login_admin.html"
@@ -44,32 +73,7 @@ class Login(TemplateView):
             return self.render_to_response(context=context)
         else: 
             recaptcha_response = request.POST.get('g-recaptcha-response')
-            url = 'https://www.google.com/recaptcha/api/siteverify'
-            values = {
-                'secret': settings.GOOGLE_RECAPTCHA_SECRET_KEY,
-                'response': recaptcha_response
-            }
-            data = urllib.parse.urlencode(values).encode()
-            req =  urllib.request.Request(url, data=data)
-            response = urllib.request.urlopen(req)
-            result = json.loads(response.read().decode())
- 
-            if result['success']:
-                user = authenticate(email=email, password=password)
-                if user is not None:
-                    login(request, user)
-                    redirect_to = "/"
-                    querystring = QueryDict(request.META['QUERY_STRING'])
-                    if request.user.is_admin:
-                        redirect_to = "/sukses-admin/"
-                    elif request.user.is_contributor:
-                        redirect_to = "/sukses-kontributor/"
-                    if 'next' in querystring:
-                        redirect_to = querystring['next']
-                    return HttpResponseRedirect(redirect_to)
-                else:
-                    context["error_message"] = "Email atau Password anda salah."
-                    return self.render_to_response(context=context)
-            else:
-                messages.error(request, 'Invalid reCAPTCHA. Please try again.')
-            return self.render_to_response(context=context)
+            result = self.get_recaptcha_result(recaptcha_response)
+
+            return self.do_login(context, email, password, request, result)
+
diff --git a/forum/services.py b/forum/services.py
new file mode 100644
index 0000000000000000000000000000000000000000..56c5e72eff676dde768e7c905ca9ce64f4944b4c
--- /dev/null
+++ b/forum/services.py
@@ -0,0 +1,33 @@
+
+class ForumService:
+
+    @staticmethod
+    def create_forum_discussion(form, user):
+        form.instance.user = user
+        form.save()
+        materi = form.cleaned_data.get('materi')
+        for data in materi:
+            form.instance.materi.add(data)
+
+
+    @staticmethod
+    def build_forum_form_detail_data(discussion_object, form, user):
+        form.instance.user = user
+        form.instance.discussion = discussion_object
+        form.save()
+        materi = form.cleaned_data.get('materi')
+        for data in materi:
+            form.instance.materi.add(data)
+
+
+    @staticmethod
+    def delete_comment(discussion_comment, request):
+        discussion = discussion_comment.discussion
+        if discussion_comment.user == request.user:
+            discussion_comment.delete()
+        return discussion
+
+    @staticmethod
+    def delete_discussion(discussion, request):
+        if discussion.user == request.user:
+            discussion.delete()
\ No newline at end of file
diff --git a/forum/views.py b/forum/views.py
index 233eb833e0d2f53e0a6b5ca67c451e7a226f7c00..45a3b0e6efdb0aa7e32fbe8351d2af12bbd36149 100644
--- a/forum/views.py
+++ b/forum/views.py
@@ -8,6 +8,7 @@ from django.views.generic.list import MultipleObjectMixin
 
 from forum.forms import DiscussionForm, DiscussionCommentForm
 from forum.models import Discussion, DiscussionComment
+from forum.services import ForumService
 
 URL_FORUM_HOME_PAGE = '/forum'
 URL_LOGIN_PAGE = '/login'
@@ -26,11 +27,8 @@ class ForumCreateDiscussion(LoginRequiredMixin, CreateView):
     login_url = URL_LOGIN_PAGE
 
     def form_valid(self, form):
-        form.instance.user = self.request.user
-        form.save()
-        materi = form.cleaned_data.get('materi')
-        for data in materi:
-            form.instance.materi.add(data)
+        user = self.request.user
+        ForumService.create_forum_discussion(form, user)
         return super().form_valid(form)
 
 
@@ -42,8 +40,7 @@ class ForumDeleteDiscussion(LoginRequiredMixin, DeleteView):
 
     def delete(self, request, *args, **kwargs):
         discussion = self.get_object()
-        if discussion.user == request.user:
-            discussion.delete()
+        ForumService.delete_discussion(discussion, request)
         return redirect(URL_FORUM_HOME_PAGE)
 
 
@@ -64,19 +61,16 @@ class ForumDiscussionDetail(FormMixin, DetailView, MultipleObjectMixin):
 
     def post(self, request, *args, **kwargs):
         form = self.get_form()
-        form.instance.user = self.request.user
-        form.instance.discussion = self.get_object()
-        form.save()
-
-        materi = form.cleaned_data.get('materi')
-        for data in materi:
-            form.instance.materi.add(data)
+        user = self.request.user
+        discussion_object = self.get_object()
+        ForumService.build_forum_form_detail_data(discussion_object, form, user)
 
         if form.is_valid():
             return self.form_valid(form)
         else:
             return self.form_invalid(form)
 
+
     def form_valid(self, form):
         form.save()
         return super().form_valid(form)
@@ -89,7 +83,5 @@ class ForumDiscussionCommentDelete(LoginRequiredMixin, DeleteView):
 
     def delete(self, request, *args, **kwargs):
         discussion_comment = self.get_object()
-        discussion = discussion_comment.discussion
-        if discussion_comment.user == request.user:
-            discussion_comment.delete()
+        discussion = ForumService.delete_comment(discussion_comment, request)
         return HttpResponseRedirect(reverse('forum_discussion_detail', kwargs={'pk': discussion.pk}))
diff --git a/news/services.py b/news/services.py
new file mode 100644
index 0000000000000000000000000000000000000000..813b4a5bdac282152aaa74674239135f1754879e
--- /dev/null
+++ b/news/services.py
@@ -0,0 +1,31 @@
+from news.forms import NewsForm
+from news.models import News
+
+
+class NewsService:
+
+    @staticmethod
+    def create_news(content, cover, title):
+        news_data = News.objects.create(
+            title=title,
+            content=content,
+            cover=cover
+        )
+        news_data.save()
+
+
+    @staticmethod
+    def update_news_form(news_data, request):
+        form = NewsForm(request.POST or None,
+                        request.FILES or None,
+                        instance=news_data)
+        is_form_valid = form.is_valid()
+        if form.is_valid():
+            form.save()
+        return is_form_valid
+
+
+    @staticmethod
+    def delete_news(news_data):
+        news_data.cover.delete()
+        news_data.delete()
diff --git a/news/views.py b/news/views.py
index 296f774b906429eb9cd3b2902d50d6143ce3537d..f16a1dec553cd9608d3ff7b2cd9a21f39218a7d5 100644
--- a/news/views.py
+++ b/news/views.py
@@ -6,6 +6,7 @@ from django.views.decorators.csrf import csrf_exempt
 from .forms import NewsForm
 
 from .models import News
+from .services import NewsService
 
 html_news_form = "news_form.html"
 html_news_list = "news_list.html"
@@ -20,15 +21,14 @@ def post_news_form(request):
     
     form = NewsForm(request.POST or None, request.FILES or None)
     if form.is_valid():
-        news_data = News.objects.create(
-            title=request.POST["title"],
-            content=request.POST["content"],
-            cover=request.FILES.get("cover", False)
-        )
-        news_data.save()
+        title = request.POST["title"]
+        content = request.POST["content"]
+        cover = request.FILES.get("cover", False)
+        NewsService.create_news(content, cover, title)
         return redirect(news_list_url)
     return redirect("/")
 
+
 @csrf_exempt
 @login_required(login_url=login_url)
 def delete_news_by_id(request, id_news):
@@ -39,10 +39,10 @@ def delete_news_by_id(request, id_news):
         news_data = News.objects.get(pk=id_news)
     except News.DoesNotExist:
         return redirect(news_list_url)
-    news_data.cover.delete()
-    news_data.delete()
+    NewsService.delete_news(news_data)
     return redirect(news_list_url)
 
+
 @login_required(login_url=login_url)
 def show_news_form(request):
     if request.user.is_admin is False:
@@ -64,13 +64,8 @@ def update_news_form(request, id_news):
         return redirect(news_list_url)
     
     form = NewsForm(instance=news_data)
-    if request.method == "POST":
-        form = NewsForm(request.POST or None,
-                        request.FILES or None,
-                        instance=news_data)
-        if form.is_valid():
-            form.save()
-            return redirect(news_list_url)
+    if request.method == "POST" and NewsService.update_news_form(news_data, request):
+        return redirect(news_list_url)
 
     response = {
         'form': form,
@@ -79,6 +74,7 @@ def update_news_form(request, id_news):
     }
     return render(request, html_news_form, response)
 
+
 @login_required(login_url=login_url)
 def show_news_list(request):
     if request.user.is_admin is False:
@@ -87,4 +83,4 @@ def show_news_list(request):
     response = {
         "news_list" : News.objects.all()
     }
-    return render(request, html_news_list, response)
\ No newline at end of file
+    return render(request, html_news_list, response)
diff --git a/register/services.py b/register/services.py
new file mode 100644
index 0000000000000000000000000000000000000000..7d2b7971869329a0b4edd6f9336334b16fd5b6a7
--- /dev/null
+++ b/register/services.py
@@ -0,0 +1,20 @@
+from django.contrib.auth.hashers import make_password
+
+class RegistrationService:
+
+    @staticmethod
+    def create_new_contributor(data, form):
+        new_user = form.save(commit=False)
+        new_user.password = make_password(data["password"])
+        new_user.is_contributor = True
+        new_user.save()
+        return new_user
+
+
+    @staticmethod
+    def create_new_admin(data, form):
+        new_user = form.save(commit=False)
+        new_user.password = make_password(data["password"])
+        new_user.is_admin = True
+        new_user.is_active = False
+        new_user.save()
\ No newline at end of file
diff --git a/register/views.py b/register/views.py
index 4d9d339882ddaea3f1583b7cd4a7342c214476ee..d54425ead396c475f3a35d6d8b2b0580fb5a8b7d 100644
--- a/register/views.py
+++ b/register/views.py
@@ -1,11 +1,10 @@
 from django.contrib.auth import login
-from django.contrib.auth.hashers import make_password
 from django.http import HttpResponseRedirect
 from django.views.generic import TemplateView
 
 from register.forms import UserForm
-
 # Create your views here.
+from register.services import RegistrationService
 
 
 class index(TemplateView):
@@ -19,10 +18,7 @@ class index(TemplateView):
         data = request.POST.copy()
         form = UserForm(request.POST)
         if form.is_valid():
-            new_user = form.save(commit=False)
-            new_user.password = make_password(data["password"])
-            new_user.is_contributor = True
-            new_user.save()
+            new_user = RegistrationService.create_new_contributor(data, form)
             login(request, new_user)
             return HttpResponseRedirect("/sukses-kontributor/")
         else:
@@ -30,6 +26,7 @@ class index(TemplateView):
             context["form"] = form
             return self.render_to_response(context)
 
+
     def get(self, request, *args, **kwargs):
         context = self.get_context_data(**kwargs)
         context["form"] = UserForm
@@ -49,16 +46,12 @@ class RegistrasiAdmin(TemplateView):
         context = self.get_context_data(**kwargs)
         context["form"] = form
         if form.is_valid():
-            new_user = form.save(commit=False)
-            new_user.password = make_password(data["password"])
-            new_user.is_admin = True
-            new_user.is_active = False
-            new_user.save()
+            RegistrationService.create_new_admin(data, form)
             context["message"] = "Please wait for our internal team to accept your admin account"
             return self.render_to_response(context)
-        
         return self.render_to_response(context)
 
+
     def get(self, request, *args, **kwargs):
         context = self.get_context_data(**kwargs)
         context["form"] = UserForm
diff --git a/traffic_statistics/service.py b/traffic_statistics/service.py
new file mode 100644
index 0000000000000000000000000000000000000000..96b3464c0beeb843408819edbdb8833f7abc1a2a
--- /dev/null
+++ b/traffic_statistics/service.py
@@ -0,0 +1,27 @@
+from django.utils.timezone import localdate
+
+from app.models import Like, Comment, ViewStatistics, DownloadStatistics
+from traffic_statistics.constants import FIELD_LIKES, FIELD_COMMENTS, FIELD_VIEWS, FIELD_DOWNLOADS, DATE_FORMAT
+from traffic_statistics.utils.statistics import generate_initial_traffic_count_dict, get_daily_traffic_count
+
+
+def build_statistic_data(end_date, start_date):
+    today = localdate()
+    start_date = min(start_date, today)
+    end_date = min(end_date, today)
+    statistics_dict = generate_initial_traffic_count_dict(start_date, end_date)
+    daily_traffic_counts = [
+        get_daily_traffic_count(Like, start_date, end_date),
+        get_daily_traffic_count(Comment, start_date, end_date),
+        get_daily_traffic_count(ViewStatistics, start_date, end_date),
+        get_daily_traffic_count(DownloadStatistics, start_date, end_date),
+    ]
+    labels = [FIELD_LIKES, FIELD_COMMENTS, FIELD_VIEWS, FIELD_DOWNLOADS]
+    for (daily_all_traffic_count, label) in zip(daily_traffic_counts, labels):
+
+        for daily_traffic_count in daily_all_traffic_count:
+            date, count = daily_traffic_count
+
+            date_str = date.strftime(DATE_FORMAT)
+            statistics_dict[date_str][label] = count
+    return statistics_dict
\ No newline at end of file
diff --git a/traffic_statistics/views.py b/traffic_statistics/views.py
index 6dfdea2791b9a3fb96027b43d47be0684eea154c..c71fa370d4c0aa3c4f81cc7161bf47f702de5a5a 100644
--- a/traffic_statistics/views.py
+++ b/traffic_statistics/views.py
@@ -1,26 +1,8 @@
 from django.http import JsonResponse
 from django.views.generic import ListView
-from django.utils.timezone import localdate
 
-from app.models import (
-    Comment,
-    DownloadStatistics,
-    Like,
-    ViewStatistics,
-)
-from traffic_statistics.constants import (
-    DATE_FORMAT,
-    FIELD_COMMENTS,
-    FIELD_DOWNLOADS,
-    FIELD_LIKES,
-    FIELD_VIEWS,
-)
 from traffic_statistics.utils.requests import extract_date_range_params
-from traffic_statistics.utils.statistics import (
-    generate_initial_traffic_count_dict,
-    get_daily_traffic_count,
-)
-
+from .service import build_statistic_data
 
 class StatisticsAPIView(ListView):
 
@@ -30,26 +12,6 @@ class StatisticsAPIView(ListView):
         except Exception as e:
             return JsonResponse(data={"error": str(e)}, status=400)
 
-        today = localdate()
-        start_date = min(start_date, today)
-        end_date = min(end_date, today)
-
-        statistics_dict = generate_initial_traffic_count_dict(start_date, end_date)
-
-        daily_traffic_counts = [
-            get_daily_traffic_count(Like, start_date, end_date),
-            get_daily_traffic_count(Comment, start_date, end_date),
-            get_daily_traffic_count(ViewStatistics, start_date, end_date),
-            get_daily_traffic_count(DownloadStatistics, start_date, end_date),
-        ]
-        labels = [FIELD_LIKES, FIELD_COMMENTS, FIELD_VIEWS, FIELD_DOWNLOADS]
-
-        for (daily_all_traffic_count, label) in zip(daily_traffic_counts, labels):
-
-            for daily_traffic_count in daily_all_traffic_count:
-                date, count = daily_traffic_count
-
-                date_str = date.strftime(DATE_FORMAT)
-                statistics_dict[date_str][label] = count
+        statistics_dict = build_statistic_data(end_date, start_date)
 
         return JsonResponse(data=statistics_dict, status=200)