diff --git a/app/models.py b/app/models.py
index 54c7f628b2c86b816f4063aaa38fe8a773588ba8..a656e06d8001905f541a082a364260c1077b1f66 100644
--- a/app/models.py
+++ b/app/models.py
@@ -100,6 +100,12 @@ class Materi(models.Model):
         count = Like.objects.filter(materi=self).count()
         return count
 
+    @property
+    def is_like(self):
+        like = False
+        if Like.objects.filter(materi=self).exists():
+            like = True
+
 
 class Comment(models.Model):
     username = models.CharField(max_length=100)
diff --git a/app/templates/app/includes/sidebar.html b/app/templates/app/includes/sidebar.html
index d39ff63293f6cf657aa4a061625f75f928c140e9..ff0bb3adb420af54f491adcdc994bd422f1991d6 100644
--- a/app/templates/app/includes/sidebar.html
+++ b/app/templates/app/includes/sidebar.html
@@ -31,6 +31,11 @@
             <span>Statisik Materi</span>
         </a>
     </li>
+    <li class="nav-item">
+        <a class="nav-link" href="{% url 'favorite' %}">
+            <span>Favorite Materi</span>
+        </a>
+    </li>
     <li class="nav-item">
         <a class="nav-link" href="{% url 'posts' %}">
         <span>Materi Diunggah</span></a>
diff --git a/app/templates/user_favorite_materi.html b/app/templates/user_favorite_materi.html
new file mode 100644
index 0000000000000000000000000000000000000000..7ca9ccffc1b172244c76e97fe59a9db72a231a0f
--- /dev/null
+++ b/app/templates/user_favorite_materi.html
@@ -0,0 +1,64 @@
+{% extends 'app/base_dashboard.html' %}
+{% load static %}
+
+{% block title %}
+<title>Materi Favorite | Digipus</title>
+{% endblock %}
+
+{% block stylesheets %}
+<link rel="stylesheet" type="text/css" href="{% static 'app/css/user_uploaded_posts.css' %}"/>
+{% endblock %}
+
+{% block content %}
+
+<div style="padding: 1rem 0" id="like">
+	{% if materi %}
+		{% for _, mat in materi.items %}
+		<div id="mat-{{ mat.data.id }}">
+			<div class="posts-space-between-container bg-white rounded shadow" style="margin: 0.5rem 2rem; padding: 1rem;">
+				<div id="posts-user-profile">
+					{% if mat.data.cover %}
+					<img id="posts-img" src="{{ post.data.cover.url }}" alt="profile-picture"/>
+					{% else %}
+					</div style="background-color: grey; width: 100px; height: 100px;">
+					{% endif %}
+					<div id="posts-info">
+						<span><a class="ml-auto p-1 link" style="text-align: left; font-size: 2rem;" href="{% url 'detail-materi' post.data.id %}">
+							{{ mat.data.title }}
+						</a></span>
+						<span style="font-size: 0.75rem; padding-left: 0.3rem;">{{ mat.data.date_created }}</span>
+					</div>
+				</div>
+				<div class="posts-vertically-centered ml-auto pr-4">
+					<span id="post-like-count-{{post.data.id}}">{{ mat.data.like_count }}</span>
+					<span>Like</span>
+				</div>
+				<div class="posts-vertically-centered">
+					<span>{{ mat.comments|length }}</span>
+					<span>Komentar</span>
+				</div>
+			</div>
+            {% for comment in mat.comments %}
+			<div id="post-{{ post.data.id }}-comment-{{ comment.id }}">
+				<div id="posts-comment-info" class="bg-white rounded shadow" >
+					<img id="posts-profile-picture" src="{{ comment.user.profile_picture.url }}" alt="profile-picture" width="40px" height="40px" style="margin: 18px"/>
+					<div style="display: flex; align-items: center;">
+						<div style="display: flex; flex-direction: column;">
+							<span style="font-size: 0.9rem;"><strong>{{ comment.user.name }}</strong> - {{ comment.timestamp }}</span>
+							{{ comment.comment }}
+						</div>
+					</div>
+				</div>
+			</div>
+            {% endfor %}
+		</div>
+		{% endfor %}
+	{% else %}
+		<div class="text-center h5">
+			Anda belum memiliki materi yang disukai
+		</div>
+	{% endif %}
+
+</div>
+
+{% endblock %}
diff --git a/app/tests.py b/app/tests.py
index 1b5bc1b7a5e25923071e85a85ebb7bde4e9c853b..653a3733f635a56a8b39fdd3906ac2b5f3a83d30 100644
--- a/app/tests.py
+++ b/app/tests.py
@@ -14,7 +14,7 @@ from django.core.exceptions import PermissionDenied, ValidationError
 from django.core.files.uploadedfile import SimpleUploadedFile
 from django.core.management import call_command
 from django.test import Client, TestCase, TransactionTestCase
-from django.urls import resolve
+from django.urls import resolve, reverse
 from django.db.utils import IntegrityError
 from django.test import Client, RequestFactory, TestCase, TransactionTestCase
 from pytz import timezone
@@ -58,6 +58,7 @@ from .views import (
     UploadMateriExcelView,
     PasswordChangeViews,
     password_success,
+    MateriFavorite,
 )
 from app.forms import SuntingProfilForm, year_choices
 from app.utils.fileManagementUtil import get_random_filename, remove_image_exifdata
@@ -72,6 +73,7 @@ from webdriver_manager.chrome import ChromeDriverManager
 from selenium.common.exceptions import NoSuchElementException
 
 
+
 class DaftarKatalogTest(TestCase):
     def test_daftar_katalog_url_exist(self):
         url = "/"
@@ -187,50 +189,34 @@ class DaftarKatalogPerKontributorTest(TestCase):
     def setUp(self):
         self.client = Client()
 
-        self.contributor_credential = {"email": "kontributor@gov.id", "password": "passwordtest"}
+        self.contributor_credential = {
+                "email": "kontributor@gov.id",
+                "password": "passwordtest"
+            }
 
-        self.contributor_credential_2 = {"email": "kontributor2@gov.id", "password": "passwordtest"}
+        self.contributor_credential_2 = {
+                "email": "kontributor2@gov.id",
+                "password": "passwordtest"
+            }
 
         self.contributor = get_user_model().objects.create_user(
-            **self.contributor_credential, name="Kontributor 1", is_contributor=True
-        )
+            **self.contributor_credential, name="Kontributor 1", is_contributor=True)
         self.contributor2 = get_user_model().objects.create_user(
-            **self.contributor_credential_2, name="Kontributor 2", is_contributor=True
-        )
+            **self.contributor_credential_2, name="Kontributor 2", is_contributor=True)
 
-        self.cover = SimpleUploadedFile("Cherprang_Areekul40_nJM9dGt.jpg", b"Test file")
+        self.cover = SimpleUploadedFile(
+            "Cherprang_Areekul40_nJM9dGt.jpg", b"Test file")
         self.content = SimpleUploadedFile("Bahan_PA_RKK.pdf", 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 2",
-            author="Agas",
-            uploader=self.contributor,
-            publisher="Kelas SC",
-            descriptions="Deskripsi Materi 2",
-            status="APPROVE",
-            cover=self.cover,
-            content=self.content,
-        ).save()
-        Materi(
-            title="Materi 3",
-            author="Agas",
-            uploader=self.contributor2,
-            publisher="Kelas SC",
-            descriptions="Deskripsi Materi 3",
-            status="APPROVE",
-            cover=self.cover,
-            content=self.content,
-        ).save()
+        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 2", author="Agas", uploader=self.contributor,
+               publisher="Kelas SC", descriptions="Deskripsi Materi 2",
+               status="APPROVE", cover=self.cover, content=self.content).save()
+        Materi(title="Materi 3", author="Agas", uploader=self.contributor2,
+               publisher="Kelas SC", descriptions="Deskripsi Materi 3",
+               status="APPROVE", cover=self.cover, content=self.content).save()
 
         self.url = f"/profil/{self.contributor.email}/"
 
@@ -257,51 +243,44 @@ class DaftarKatalogPerKontributorTest(TestCase):
 class DetailMateriTest(TestCase):
     def setUp(self):
         self.client = Client()
-        self.admin_credential = {"email": "admin@gov.id", "password": "passwordtest"}
-        self.contributor_credential = {"email": "kontributor@gov.id", "password": "passwordtest"}
-        self.anonymous_credential = {"email": "anonymous@gov.id", "password": "passwordtest"}
-        self.admin = get_user_model().objects.create_user(**self.admin_credential, name="Admin", is_admin=True)
+        self.admin_credential = {
+            "email": "admin@gov.id",
+            "password": "passwordtest"
+        }
+        self.contributor_credential = {
+            "email": "kontributor@gov.id",
+            "password": "passwordtest"
+        }
+        self.anonymous_credential = {
+            "email": "anonymous@gov.id",
+            "password": "passwordtest"
+        }
+        self.admin = get_user_model().objects.create_user(
+            **self.admin_credential, name="Admin", is_admin=True)
         self.contributor = get_user_model().objects.create_user(
-            **self.contributor_credential, name="Kontributor", is_contributor=True
+            **self.contributor_credential, name="Kontributor", is_contributor=True)
+        self.anonymous = get_user_model().objects.create_user(
+            **self.anonymous_credential, name="Anonymous"
         )
-        self.anonymous = get_user_model().objects.create_user(**self.anonymous_credential, name="Anonymous")
-        self.cover = SimpleUploadedFile("ExampleCover921.jpg", b"Test file")
+        self.cover = SimpleUploadedFile(
+            "ExampleCover921.jpg", b"Test file")
         self.content = SimpleUploadedFile("ExampleFile921.pdf", 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 1", author="Agas", uploader=self.contributor,
+               publisher="Kelas SC", descriptions="Deskripsi Materi 1",
+               status="APPROVE", cover=self.cover, content=self.content).save()
         self.materi1 = Materi.objects.first()
         self.url = "/materi/" + str(self.materi1.id) + "/"
 
-        self.materi_with_published_date = Materi.objects.create(
-            title="Materi 1",
-            author="Agas",
-            uploader=self.contributor,
-            publisher="Kelas SC",
-            descriptions="Deskripsi Materi 1",
-            status="APPROVE",
-            cover=self.cover,
-            content=self.content,
-            date_modified=datetime.now(),
-            date_created=datetime.now(),
-        )
+        self.materi_with_published_date = Materi.objects.create(title="Materi 1", author="Agas", uploader=self.contributor,
+                                                 publisher="Kelas SC", descriptions="Deskripsi Materi 1",
+                                                 status="APPROVE", cover=self.cover, content=self.content,
+                                                 date_modified=datetime.now(), date_created=datetime.now())
         self.materi_with_published_date_url = "/materi/" + str(self.materi_with_published_date.id) + "/"
-        VerificationReport.objects.create(
-            report='{"feedback": "Something", "kriteria": [{"title": "Kriteria 1", "status": true},'
-            + ' {"title": "Kriteria 2", "status": true}, {"title": "Kriteria 3", "status": true}]}',
-            timestamp="2020-10-09 06:21:33",
-            status="Diterima",
-            materi=self.materi_with_published_date,
-            user=self.materi_with_published_date.uploader,
-        )
+        VerificationReport.objects.create(report='{"feedback": "Something", "kriteria": [{"title": "Kriteria 1", "status": true},' + \
+            ' {"title": "Kriteria 2", "status": true}, {"title": "Kriteria 3", "status": true}]}', \
+            timestamp="2020-10-09 06:21:33", status="Diterima", materi= self.materi_with_published_date, \
+            user=self.materi_with_published_date.uploader)
 
     def test_detail_materi_url_exist(self):
         response = Client().get(self.url)
@@ -317,7 +296,8 @@ class DetailMateriTest(TestCase):
         self.assertEqual(found.func.__name__, DetailMateri.as_view().__name__)
 
     def test_category_models_can_create_new_object(self):
-        test = Category.objects.create(id="1", name="medis", description="kategori medis")
+        test = Category.objects.create(
+            id="1", name="medis", description="kategori medis")
         countData = Category.objects.all().count()
         self.assertEqual(1, countData)
         self.assertNotEqual(0, countData)
@@ -325,7 +305,8 @@ class DetailMateriTest(TestCase):
         self.assertNotEqual(test.__str__(), "saul")
 
     def test_comment_models_can_create_new_object(self):
-        test = Comment.objects.create(username="saul", profile="121212", comment="232323")
+        test = Comment.objects.create(
+            username="saul", profile="121212", comment="232323")
         countData = Comment.objects.all().count()
         self.assertEqual(1, countData)
         self.assertNotEqual(0, countData)
@@ -337,7 +318,8 @@ class DetailMateriTest(TestCase):
         self.client.login(**self.anonymous_credential)
         response = self.client.post(url, {"comment": ""})
         self.assertIn("error_message", response.context)
-        self.assertIn("Anda belum menuliskan komentar", response.context["error_message"])
+        self.assertIn("Anda belum menuliskan komentar",
+                      response.context["error_message"])
 
     def test_comment_rendered_to_template(self):
         url = self.url
@@ -356,7 +338,8 @@ class DetailMateriTest(TestCase):
     def test_comment_by_kontributor(self):
         url = self.url
         self.client.login(**self.contributor_credential)
-        self.client.post(url, {"comment": "This is new comment by Contributor"})
+        self.client.post(
+            url, {"comment": "This is new comment by Contributor"})
         response = self.client.get(url)
         self.assertContains(response, "Kontributor")
 
@@ -512,23 +495,25 @@ class DetailMateriTest(TestCase):
 
     def test_hasil_citasi_APA_materi_has_no_published_date(self):
         response = self.client.get(self.url)
-        expected = self.materi1.author + " . (n.d) . " + self.materi1.title + " . " + self.materi1.publisher
+        expected = self.materi1.author + " . (n.d) . " + self.materi1.title + " . " + self.materi1.publisher	
         self.assertIn(expected, response.context["citationAPA"])
+        self.assertIn(expected,
+                      response.context["citationAPA"])
 
     def test_hasil_citasi_APA_materi_has_published_date(self):
         response = self.client.get(self.materi_with_published_date_url)
-        published_date = self.materi_with_published_date.published_date.strftime("%Y-%m-%d %H:%M")
-        expected = (
-            self.materi_with_published_date.author
-            + " . ("
-            + published_date
-            + ") . "
-            + self.materi_with_published_date.title
-            + " . "
-            + self.materi_with_published_date.publisher
-        )
+        published_date = self.materi_with_published_date.published_date.strftime("%Y-%m-%d %H:%M")	
+        expected = (	
+            self.materi_with_published_date.author	
+            + " . ("	
+            + published_date	
+            + ") . "	
+            + self.materi_with_published_date.title	
+            + " . "	
+            + self.materi_with_published_date.publisher	
+        )	
         self.assertIn(expected, response.context["citationAPA"])
-
+    
     def test_citation_IEEE_button(self):
         response = self.client.get(self.url)
         self.assertContains(response, "Citate IEEE")
@@ -540,19 +525,19 @@ class DetailMateriTest(TestCase):
         current_month = current_date.strftime("%b")
         current_year = str(current_date.year)
 
-        expected = (
-            "Agas, "
-            + "Materi 1. "
-            + "Kelas SC, n.d. "
-            + "Accessed on: "
-            + current_month
-            + ". "
-            + current_day
-            + ", "
-            + current_year
-            + ". [Online]. "
-            + "Available: http://testserver"
-            + self.url
+        expected = (	
+            "Agas, "	
+            + "Materi 1. "	
+            + "Kelas SC, n.d. "	
+            + "Accessed on: "	
+            + current_month	
+            + ". "	
+            + current_day	
+            + ", "	
+            + current_year	
+            + ". [Online]. "	
+            + "Available: http://testserver"	
+            + self.url	
         )
         self.assertIn(expected, response.context["citationIEEE"])
 
@@ -562,45 +547,37 @@ class DetailMateriTest(TestCase):
         current_day = str(current_date.day)
         current_month = current_date.strftime("%b")
         current_year = str(current_date.year)
-        published_date = self.materi_with_published_date.published_date.strftime("%Y")
-
-        expected = (
-            "Agas, "
-            + "Materi 1. "
-            + "Kelas SC, "
-            + published_date
-            + ". "
-            + "Accessed on: "
-            + current_month
-            + ". "
-            + current_day
-            + ", "
-            + current_year
-            + ". [Online]. "
-            + "Available: http://testserver"
-            + self.materi_with_published_date_url
-        )
+        published_date = self.materi_with_published_date.published_date.strftime('%Y')
+
+        expected = "Agas, " + \
+                   "Materi 1. " + \
+                   "Kelas SC, " + published_date + ". " +\
+                   "Accessed on: " + current_month + ". " + current_day + ", " + current_year + \
+                   ". [Online]. " + \
+                   "Available: http://testserver" + self.materi_with_published_date_url
         self.assertIn(expected, response.context["citationIEEE"])
 
     def test_tombol_bagikan_google_drive(self):
         response = Client().get(self.url)
-        self.assertContains(response, "Google Drive")
+        self.assertContains(response, 'Google Drive')
 
     @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)
+        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.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)
+        response = self.client.get(
+            '/materi/%s/save-to-gdrive/' % self.materi1.id, follow=True)
         last_url, status_code = response.redirect_chain[-1]
         mock_upload_to_gdrive.assert_called_once()
-        self.assertEqual(last_url, "/materi/%d/" % self.materi1.id)
+        self.assertEqual(last_url, '/materi/%d/' % self.materi1.id)
         self.assertEqual(status_code, 302)
 
 class PostsViewTest(TestCase):
+
     @classmethod
     def generate_posts_data(cls, user):
         POST_COUNT = 3
@@ -614,7 +591,11 @@ class PostsViewTest(TestCase):
 
         post_comment_group_dict = {}
         for i in range(POST_COUNT):
-            post = Materi.objects.create(uploader=user, cover=sample_file, content=sample_file,)
+            post = Materi.objects.create(
+                uploader=user,
+                cover=sample_file,
+                content=sample_file,
+            )
             post.categories.add(sample_category)
 
             post_comment_group_dict[post.id] = {
@@ -622,11 +603,13 @@ class PostsViewTest(TestCase):
                 "comments": [],
             }
 
-            for j in range(LIKES_COUNT_PER_POST[i]):
+            for j in range (LIKES_COUNT_PER_POST[i]):
                 Like.objects.create(
-                    timestamp=datetime.now(), materi=post, session_id=("dummysession-" + str(i) + "-" + str(j))
+                    timestamp=datetime.now(), 
+                    materi=post, 
+                    session_id=("dummysession-" + str(i) + '-' + str(j))
                 )
-
+            
         for i, post_id in enumerate(post_comment_group_dict):
             post = post_comment_group_dict[post_id]["data"]
 
@@ -641,8 +624,11 @@ class PostsViewTest(TestCase):
 
     @classmethod
     def setUpTestData(cls):
-        cls.url = "/posts/"
-        cls.user_credentials = {"email": "user@email.com", "password": "justpass"}
+        cls.url = '/posts/'
+        cls.user_credentials = {
+            "email": "user@email.com",
+            "password": "justpass"
+        }
         cls.user = User.objects.create_user(**cls.user_credentials, is_contributor=True)
         cls.data = cls.generate_posts_data(cls.user)
 
@@ -686,17 +672,20 @@ class PostsViewTest(TestCase):
         response = self._request_as_user()
 
         posts = list(self.data.keys())
-        comments = {i: [comment.id for comment in self.data[post_id]["comments"]] for i, post_id in enumerate(posts)}
+        comments = {
+            i: [comment.id for comment in self.data[post_id]["comments"]]
+            for i, post_id in enumerate(posts)
+        }
 
         self.assertRegex(
             str(response.content),
-            rf'.*(<div id="post-{posts[2]}">)'
-            + rf'.*(<div id="post-{posts[2]}-comment-{comments[2][0]}">)'
-            + rf'.*(<div id="post-{posts[2]}-comment-{comments[2][1]}">)'
-            + rf'.*(<div id="post-{posts[2]}-comment-{comments[2][2]}">)'
-            + rf'.*(<div id="post-{posts[1]}">)'
-            + rf'.*(<div id="post-{posts[0]}">)'
-            + rf'.*(<div id="post-{posts[0]}-comment-{comments[0][0]}">)',
+            rf'.*(<div id="post-{posts[2]}">)' + \
+            rf'.*(<div id="post-{posts[2]}-comment-{comments[2][0]}">)' + \
+            rf'.*(<div id="post-{posts[2]}-comment-{comments[2][1]}">)' + \
+            rf'.*(<div id="post-{posts[2]}-comment-{comments[2][2]}">)' + \
+            rf'.*(<div id="post-{posts[1]}">)' + \
+            rf'.*(<div id="post-{posts[0]}">)' + \
+            rf'.*(<div id="post-{posts[0]}-comment-{comments[0][0]}">)'
         )
 
     def test_like_count_written_correctly_on_template(self):
@@ -706,10 +695,10 @@ class PostsViewTest(TestCase):
             post = self.data[post_id]["data"]
 
             self.assertContains(
-                response, '<span id="post-like-count-' + str(post.id) + '">' + str(post.like_count) + "</span>",
+                response,
+                '<span id="post-like-count-' + str(post.id) + '">' +  str(post.like_count) + '</span>',
             )
 
-
 class TemplateLoaderTest(TestCase):
     def test_template_loader_url_exist(self):
         url = "/test-page.html"
@@ -739,43 +728,52 @@ class TemplateLoaderTest(TestCase):
 class UploadPageTest(TestCase):
     def setUp(self):
         self.client = Client()
-        self.user = User.objects._create_user(email="kontributor@gov.id", password="kontributor", is_contributor=True)
-        self.admin = User.objects.create_admin(email="admin@gov.id", password="admin")
+        self.user = User.objects._create_user(email="kontributor@gov.id",
+                                              password="kontributor", is_contributor=True)
+        self.admin = User.objects.create_admin(email="admin@gov.id",
+                                               password="admin")
 
     def test_upload_page_using_login_func(self):
         found = resolve("/unggah/")
-        self.assertEqual(found.func.__name__, UploadMateriView.as_view().__name__)
+        self.assertEqual(found.func.__name__,
+                         UploadMateriView.as_view().__name__)
 
     def test_upload_page_url_is_exist(self):
         # Positive test
-        self.client.login(email="kontributor@gov.id", password="kontributor")
+        self.client.login(email="kontributor@gov.id",
+                          password="kontributor")
         response = self.client.get("/unggah/")
         self.assertEqual(response.status_code, 200)
 
         # Negative tests
-        self.client.login(email="kontributor@gov.id", password="kontributor")
+        self.client.login(email="kontributor@gov.id",
+                          password="kontributor")
         response = Client().get("/fake/")
         self.assertEqual(response.status_code, 404)
 
-    def test_upload_page_url_admin_doesnt_exist(self):
-        self.client.login(email="admin@gov.id", password="admin")
+    def test_upload_page_url_admin_doesnt_exist(self):        
+        self.client.login(email="admin@gov.id",
+                          password="admin")
         response = self.client.get("/unggah/")
         self.assertEqual(response.status_code, 403)
 
-    def test_upload_page_url_admin_cant_upload(self):
-        self.client.login(email="admin@gov.id", password="admin")
+    def test_upload_page_url_admin_cant_upload(self):        
+        self.client.login(email="admin@gov.id",
+                          password="admin")
         response = self.client.post("/unggah/")
         self.assertEqual(response.status_code, 403)
 
     def test_upload_page_template(self):
         url = "/unggah/"
-        self.client.login(email="kontributor@gov.id", password="kontributor")
+        self.client.login(email="kontributor@gov.id",
+                          password="kontributor")
         response = self.client.get(url)
         expected_template_name = "unggah.html"
         self.assertTemplateUsed(response, expected_template_name)
 
     def test_upload_page_title(self):
-        self.client.login(email="kontributor@gov.id", password="kontributor")
+        self.client.login(email="kontributor@gov.id",
+                          password="kontributor")
         response = self.client.get("/unggah/")
 
         # Positive tests
@@ -785,7 +783,8 @@ class UploadPageTest(TestCase):
         self.assertNotContains(response, "Anything")
 
     def test_upload_page_form_field(self):
-        self.client.login(email="kontributor@gov.id", password="kontributor")
+        self.client.login(email="kontributor@gov.id",
+                          password="kontributor")
         response = self.client.get("/unggah/")
 
         # Positive tests
@@ -798,32 +797,38 @@ class UploadPageTest(TestCase):
 class UploadExcelPageTest(TestCase):
     def setUp(self):
         self.client = Client()
-        self.user = User.objects._create_user(email="kontributor@gov.id", password="kontributor", is_contributor=True)
+        self.user = User.objects._create_user(email="kontributor@gov.id",
+                                              password="kontributor", is_contributor=True)
 
     def test_upload_excel_page_using_login_func(self):
         found = resolve("/unggah_excel/")
-        self.assertEqual(found.func.__name__, UploadMateriExcelView.as_view().__name__)
+        self.assertEqual(found.func.__name__,
+                         UploadMateriExcelView.as_view().__name__)
 
     def test_uplaod_excel_page_url_is_exist(self):
         # Positive test
-        self.client.login(email="kontributor@gov.id", password="kontributor")
+        self.client.login(email="kontributor@gov.id",
+                          password="kontributor")
         response = self.client.get("/unggah_excel/")
         self.assertEqual(response.status_code, 200)
 
         # Negative tests
-        self.client.login(email="kontributor@gov.id", password="kontributor")
+        self.client.login(email="kontributor@gov.id",
+                          password="kontributor")
         response = Client().get("/fake/")
         self.assertEqual(response.status_code, 404)
 
     def test_upload_excel_page_template(self):
         url = "/unggah_excel/"
-        self.client.login(email="kontributor@gov.id", password="kontributor")
+        self.client.login(email="kontributor@gov.id",
+                          password="kontributor")
         response = self.client.get(url)
         expected_template_name = "unggah_excel.html"
         self.assertTemplateUsed(response, expected_template_name)
 
     def test_upload_excel_page_title(self):
-        self.client.login(email="kontributor@gov.id", password="kontributor")
+        self.client.login(email="kontributor@gov.id",
+                          password="kontributor")
         response = self.client.get("/unggah_excel/")
 
         # Positive tests
@@ -833,7 +838,8 @@ class UploadExcelPageTest(TestCase):
         self.assertNotContains(response, "Anything")
 
     def test_upload_excel_page_form_field(self):
-        self.client.login(email="kontributor@gov.id", password="kontributor")
+        self.client.login(email="kontributor@gov.id",
+                          password="kontributor")
         response = self.client.get("/unggah_excel/")
 
         # Positive tests
@@ -849,144 +855,150 @@ class UploadExcelPageTest(TestCase):
         categories1 = "Machine Learning,Deep Learning,Computer Science"
         description1 = "A series of Deep Learning breakthroughs have boosted the whole field of machine learning over the last decade. Now that machine learning is thriving, even programmers who know close to nothing about this technology can use simple, efficient tools to implement programs capable of learning from data. This practical book shows you how."
 
-        if "title" in field_lengths:
-            title1 = title1[: field_lengths["title"]]
+        if 'title' in field_lengths:
+            title1 = title1[:field_lengths['title']]
 
-        if "author" in field_lengths:
-            author1 = author1[: field_lengths["author"]]
+        if 'author' in field_lengths:
+            author1 = author1[:field_lengths['author']]
 
-        if "publisher" in field_lengths:
-            publisher1 = publisher1[: field_lengths["publisher"]]
+        if 'publisher' in field_lengths:
+            publisher1 = publisher1[:field_lengths['publisher']]
 
         if len(categories) > 0:
-            categories1 = ",".join(categories)
-
-        data_frame = pd.DataFrame(
-            {
-                "Title": [title1],
-                "Author": [author1],
-                "Publisher": [publisher1],
-                "Categories": [categories1],
-                "Description": [description1],
-            }
-        )
+            categories1 = ','.join(categories)
+
+        data_frame = pd.DataFrame({
+            'Title': [title1],
+            'Author': [author1],
+            'Publisher': [publisher1],
+            'Categories': [categories1],
+            'Description': [description1],
+        })
 
-        file_path = os.path.join(settings.MEDIA_ROOT, "dummy.xlsx")
+        file_path = os.path.join(settings.MEDIA_ROOT, 'dummy.xlsx')
 
-        writer = pd.ExcelWriter(file_path, engine="xlsxwriter")  # pylint: disable=abstract-class-instantiated
+        writer = pd.ExcelWriter(file_path, engine='xlsxwriter') #pylint: disable=abstract-class-instantiated
         data_frame.to_excel(writer, index=0)
         writer.save()
 
         return file_path, data_frame
 
     def test_upload_excel_upload_file_title_error(self):
-        self.client.login(email="kontributor@gov.id", password="kontributor")
+        self.client.login(email="kontributor@gov.id",
+                          password="kontributor")
 
         field_lengths = {
-            "author": 30,
-            "publisher": 30,
+            'author':30,
+            'publisher':30,
         }
         file_name, data_frame = self.create_dummy_excel(field_lengths=field_lengths)
 
-        with open(file_name, "rb") as fp:
-            response = self.client.post("/unggah_excel/", {"excel": fp})
-
+        with open(file_name, 'rb') as fp:
+            response = self.client.post("/unggah_excel/", {'excel': fp})
+        
         messages = list(dj_messages.get_messages(response.wsgi_request))
         msg_text = messages[0].message
 
-        self.assertIn("Title", msg_text)
+        self.assertIn('Title', msg_text)
 
     def test_upload_excel_upload_file_author_error(self):
-        self.client.login(email="kontributor@gov.id", password="kontributor")
+        self.client.login(email="kontributor@gov.id",
+                          password="kontributor")
 
         field_lengths = {
-            "title": 50,
-            "publisher": 30,
+            'title':50,
+            'publisher':30,
         }
         file_name, data_frame = self.create_dummy_excel(field_lengths=field_lengths)
 
-        with open(file_name, "rb") as fp:
-            response = self.client.post("/unggah_excel/", {"excel": fp})
-
+        with open(file_name, 'rb') as fp:
+            response = self.client.post("/unggah_excel/", {'excel': fp})
+        
         messages = list(dj_messages.get_messages(response.wsgi_request))
         msg_text = messages[0].message
 
-        self.assertIn("Author", msg_text)
+        self.assertIn('Author', msg_text)
+
 
     def test_upload_excel_upload_file_publisher_error(self):
-        self.client.login(email="kontributor@gov.id", password="kontributor")
+        self.client.login(email="kontributor@gov.id",
+                          password="kontributor")
 
         field_lengths = {
-            "title": 50,
-            "author": 30,
+            'title':50,
+            'author':30,
         }
         file_name, data_frame = self.create_dummy_excel(field_lengths=field_lengths)
 
-        with open(file_name, "rb") as fp:
-            response = self.client.post("/unggah_excel/", {"excel": fp})
-
+        with open(file_name, 'rb') as fp:
+            response = self.client.post("/unggah_excel/", {'excel': fp})
+        
         messages = list(dj_messages.get_messages(response.wsgi_request))
         msg_text = messages[0].message
 
-        self.assertIn("Publisher", msg_text)
+        self.assertIn('Publisher', msg_text)
 
     def test_upload_excel_upload_file_categories_error(self):
-        self.client.login(email="kontributor@gov.id", password="kontributor")
+        self.client.login(email="kontributor@gov.id",
+                          password="kontributor")
 
         field_lengths = {
-            "title": 50,
-            "author": 30,
-            "publisher": 30,
+            'title':50,
+            'author':30,
+            'publisher':30,
         }
         file_name, data_frame = self.create_dummy_excel(field_lengths=field_lengths)
 
-        with open(file_name, "rb") as fp:
-            response = self.client.post("/unggah_excel/", {"excel": fp})
-
+        with open(file_name, 'rb') as fp:
+            response = self.client.post("/unggah_excel/", {'excel': fp})
+        
         messages = list(dj_messages.get_messages(response.wsgi_request))
         msg_text = messages[0].message
 
-        self.assertIn("Kategori", msg_text)
+        self.assertIn('Kategori', msg_text)
 
     def test_upload_excel_upload_file_success(self):
-        self.client.login(email="kontributor@gov.id", password="kontributor")
+        self.client.login(email="kontributor@gov.id",
+                          password="kontributor")
 
-        Category(name="Computer Science").save()
-        Category(name="Machine Learning").save()
-        Category(name="Deep Learning").save()
+        Category(name='Computer Science').save()
+        Category(name='Machine Learning').save()
+        Category(name='Deep Learning').save()
 
         field_lengths = {
-            "title": 50,
-            "author": 30,
-            "publisher": 30,
+            'title':50,
+            'author':30,
+            'publisher':30,
         }
 
-        categories = ["Computer Science", "Machine Learning", "Deep Learning"]
-
+        categories = ['Computer Science','Machine Learning','Deep Learning']
+        
         file_name, data_frame = self.create_dummy_excel(field_lengths=field_lengths, categories=categories)
 
-        with open(file_name, "rb") as fp:
-            response = self.client.post("/unggah_excel/", {"excel": fp})
-
-        title = data_frame["Title"][0]
+        with open(file_name, 'rb') as fp:
+            response = self.client.post("/unggah_excel/", {'excel': fp})
+        
+        title = data_frame['Title'][0]
         self.assertTrue(Materi.objects.get(title=title))
 
     def test_upload_excel_download_template(self):
-        self.client.login(email="kontributor@gov.id", password="kontributor")
+        self.client.login(email="kontributor@gov.id",
+                          password="kontributor")
 
         response = self.client.get("/unggah_excel/?template=1")
+        
+        self.assertEquals(response['Content-Type'],'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
+        self.assertEquals(response['Content-Disposition'],'attachment; filename=template.xlsx')
 
-        self.assertEquals(
-            response["Content-Type"], "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
-        )
-        self.assertEquals(response["Content-Disposition"], "attachment; filename=template.xlsx")
 
 
 class DashboardKontributorViewTest(TestCase):
     def setUp(self):
         self.client = Client()
-        self.kontributor = User.objects.create_contributor(email="kontributor@gov.id", password="kontributor")
-        self.admin = User.objects.create_admin(email="admin@gov.id", password="admin")
+        self.kontributor = User.objects.create_contributor(email="kontributor@gov.id",
+                                                           password="kontributor")
+        self.admin = User.objects.create_admin(email="admin@gov.id",
+                                               password="admin")
         self.url = "/dashboard/"
         self.view = DashboardKontributorView
         self.template_name = "dashboard.html"
@@ -997,7 +1009,8 @@ class DashboardKontributorViewTest(TestCase):
 
     def test_dashboard_kontributor_template(self):
         # Login
-        self.client.login(email="kontributor@gov.id", password="kontributor")
+        self.client.login(email="kontributor@gov.id",
+                          password="kontributor")
         # Test
         response = self.client.get(self.url)
         self.assertTemplateUsed(response, self.template_name)
@@ -1006,7 +1019,8 @@ class DashboardKontributorViewTest(TestCase):
 
     def test_dashboard_kontributor_url(self):
         # Login
-        self.client.login(email="kontributor@gov.id", password="kontributor")
+        self.client.login(email="kontributor@gov.id",
+                          password="kontributor")
         # Test
         response = self.client.get(self.url)
         self.assertEqual(response.status_code, 200)
@@ -1016,7 +1030,8 @@ class DashboardKontributorViewTest(TestCase):
     def test_dashboard_kontributor_access(self):
         # Kontributor
         # Login
-        self.client.login(email="kontributor@gov.id", password="kontributor")
+        self.client.login(email="kontributor@gov.id",
+                          password="kontributor")
         # Test
         response = self.client.get(self.url)
         self.assertEqual(response.status_code, 200)
@@ -1025,7 +1040,8 @@ class DashboardKontributorViewTest(TestCase):
 
         # Admin
         # Login
-        self.client.login(email="admin@gov.id", password="admin")
+        self.client.login(email="admin@gov.id",
+                          password="admin")
         # Test
         response = self.client.get(self.url)
         self.assertEqual(response.status_code, 403)
@@ -1066,8 +1082,10 @@ class DeleteMateriTest(TestCase):
 class ProfilAdminTest(TestCase):
     def setUp(self):
         self.client = Client()
-        self.kontributor = User.objects.create_contributor(email="kontributor@gov.id", password="kontributor")
-        self.admin = User.objects.create_admin(email="admin@gov.id", password="admin")
+        self.kontributor = User.objects.create_contributor(email="kontributor@gov.id",
+                                                           password="kontributor")
+        self.admin = User.objects.create_admin(email="admin@gov.id",
+                                               password="admin")
         self.url = "/profil-admin/"
         self.view = ProfilAdminView
         self.template_name = "profil-admin.html"
@@ -1078,7 +1096,8 @@ class ProfilAdminTest(TestCase):
 
     def test_profil_admin_template(self):
         # Login
-        self.client.login(email="admin@gov.id", password="admin")
+        self.client.login(email="admin@gov.id",
+                          password="admin")
         # Test
         response = self.client.get(self.url)
         self.assertTemplateUsed(response, self.template_name)
@@ -1090,7 +1109,8 @@ class ProfilAdminTest(TestCase):
         response = self.client.get(self.url)
         self.assertNotEqual(response.status_code, 200)
 
-        self.client.login(email="admin@gov.id", password="admin")
+        self.client.login(email="admin@gov.id",
+                          password="admin")
         # Test
         response = self.client.get(self.url)
         self.assertEqual(response.status_code, 200)
@@ -1101,8 +1121,10 @@ class ProfilAdminTest(TestCase):
 class ProfilKontributorTest(TestCase):
     def setUp(self):
         self.client = Client()
-        self.kontributor = User.objects.create_contributor(email="kontributor@gov.id", password="kontributor")
-        self.admin = User.objects.create_admin(email="admin@gov.id", password="admin")
+        self.kontributor = User.objects.create_contributor(email="kontributor@gov.id",
+                                                           password="kontributor")
+        self.admin = User.objects.create_admin(email="admin@gov.id",
+                                               password="admin")
         self.url = "/profil/"
         self.view = ProfilKontributorView
         self.template_name = "profil.html"
@@ -1113,7 +1135,8 @@ class ProfilKontributorTest(TestCase):
 
     def test_profil_kontributor_template(self):
         # Login
-        self.client.login(email="kontributor@gov.id", password="kontributor")
+        self.client.login(email="kontributor@gov.id",
+                          password="kontributor")
         # Test
         response = self.client.get(self.url)
         self.assertTemplateUsed(response, self.template_name)
@@ -1122,7 +1145,8 @@ class ProfilKontributorTest(TestCase):
 
     def test_profil_kontributor_url(self):
         # Login
-        self.client.login(email="kontributor@gov.id", password="kontributor")
+        self.client.login(email="kontributor@gov.id",
+                          password="kontributor")
         # Test
         response = self.client.get(self.url)
         self.assertEqual(response.status_code, 200)
@@ -1132,7 +1156,8 @@ class ProfilKontributorTest(TestCase):
     def test_profil_kontributor_access(self):
         # Kontributor
         # Login
-        self.client.login(email="kontributor@gov.id", password="kontributor")
+        self.client.login(email="kontributor@gov.id",
+                          password="kontributor")
         # Test
         response = self.client.get(self.url)
         self.assertEqual(response.status_code, 200)
@@ -1141,7 +1166,8 @@ class ProfilKontributorTest(TestCase):
 
         # Admin
         # Login
-        self.client.login(email="admin@gov.id", password="admin")
+        self.client.login(email="admin@gov.id",
+                          password="admin")
         # Test
         response = self.client.get(self.url)
         self.assertEqual(response.status_code, 403)
@@ -1163,8 +1189,10 @@ class ProfilKontributorTest(TestCase):
 class SuntingProfilTest(TestCase):
     def setUp(self):
         self.client = Client()
-        self.kontributor = User.objects.create_contributor(email="kontributor@gov.id", password="kontributor")
-        self.admin = User.objects.create_admin(email="admin@gov.id", password="admin")
+        self.kontributor = User.objects.create_contributor(email="kontributor@gov.id",
+                                                           password="kontributor")
+        self.admin = User.objects.create_admin(email="admin@gov.id",
+                                               password="admin")
         self.url = "/sunting/"
         self.view = SuntingProfilView
         self.template_name = "sunting.html"
@@ -1175,7 +1203,8 @@ class SuntingProfilTest(TestCase):
 
     def test_sunting_profile_template(self):
         # Login
-        self.client.login(email="kontributor@gov.id", password="kontributor")
+        self.client.login(email="kontributor@gov.id",
+                          password="kontributor")
         # Test
         response = self.client.get(self.url)
         self.assertTemplateUsed(response, self.template_name)
@@ -1184,7 +1213,8 @@ class SuntingProfilTest(TestCase):
 
     def test_sunting_profile_url(self):
         # Login
-        self.client.login(email="kontributor@gov.id", password="kontributor")
+        self.client.login(email="kontributor@gov.id",
+                          password="kontributor")
         # Test
         response = self.client.get(self.url)
         self.assertEqual(response.status_code, 200)
@@ -1194,7 +1224,8 @@ class SuntingProfilTest(TestCase):
     def test_sunting_profile_access(self):
         # Kontributor
         # Login
-        self.client.login(email="kontributor@gov.id", password="kontributor")
+        self.client.login(email="kontributor@gov.id",
+                          password="kontributor")
         # Test
         response = self.client.get(self.url)
         self.assertEqual(response.status_code, 200)
@@ -1215,7 +1246,7 @@ class SuntingProfilTest(TestCase):
             "alamat": "alamat",
             "nomor_telpon": "123456789",
             "twitter": "Twit",
-            "instagram": "https://instagram.com/test_ig",
+            "instagram": "https://instagram.com/test_ig"
         }
         form = SuntingProfilForm(data=form_data)
 
@@ -1227,8 +1258,10 @@ class SuntingProfilTest(TestCase):
 class SuntingProfilAdminTest(TestCase):
     def setUp(self):
         self.client = Client()
-        self.kontributor = User.objects.create_contributor(email="kontributor@gov.id", password="kontributor")
-        self.admin = User.objects.create_admin(email="admin@gov.id", password="admin")
+        self.kontributor = User.objects.create_contributor(email="kontributor@gov.id",
+                                                           password="kontributor")
+        self.admin = User.objects.create_admin(email="admin@gov.id",
+                                               password="admin")
         self.url = "/sunting-admin/"
         self.view = SuntingProfilAdminView
         self.template_name = "sunting_admin.html"
@@ -1239,7 +1272,8 @@ class SuntingProfilAdminTest(TestCase):
 
     def test_sunting_profile_admin_template(self):
         # Login
-        self.client.login(email="admin@gov.id", password="admin")
+        self.client.login(email="admin@gov.id",
+                          password="admin")
         # Test
         response = self.client.get(self.url)
         self.assertTemplateUsed(response, self.template_name)
@@ -1248,7 +1282,8 @@ class SuntingProfilAdminTest(TestCase):
 
     def test_sunting_profile_admin_url(self):
         # Login
-        self.client.login(email="admin@gov.id", password="admin")
+        self.client.login(email="admin@gov.id",
+                          password="admin")
         # Test
         response = self.client.get(self.url)
         self.assertEqual(response.status_code, 200)
@@ -1258,7 +1293,8 @@ class SuntingProfilAdminTest(TestCase):
     def test_sunting_profile_admin_access(self):
         # Kontributor
         # Login
-        self.client.login(email="admin@gov.id", password="admin")
+        self.client.login(email="admin@gov.id",
+                          password="admin")
         # Test
         response = self.client.get(self.url)
         self.assertEqual(response.status_code, 200)
@@ -1269,8 +1305,10 @@ class SuntingProfilAdminTest(TestCase):
 class SuksesLoginKontributorTest(TestCase):
     def setUp(self):
         self.client = Client()
-        self.kontributor = User.objects.create_contributor(email="kontributor@gov.id", password="kontributor")
-        self.admin = User.objects.create_admin(email="admin@gov.id", password="admin")
+        self.kontributor = User.objects.create_contributor(email="kontributor@gov.id",
+                                                           password="kontributor")
+        self.admin = User.objects.create_admin(email="admin@gov.id",
+                                               password="admin")
         self.url = "/sukses-kontributor/"
         self.view = SuksesLoginKontributorView
         self.template_name = "sukses_kontri.html"
@@ -1281,7 +1319,8 @@ class SuksesLoginKontributorTest(TestCase):
 
     def test_sukses_login_kontributor_template(self):
         # Login
-        self.client.login(email="kontributor@gov.id", password="kontributor")
+        self.client.login(email="kontributor@gov.id",
+                          password="kontributor")
         # Test
         response = self.client.get(self.url)
         self.assertTemplateUsed(response, self.template_name)
@@ -1290,7 +1329,8 @@ class SuksesLoginKontributorTest(TestCase):
 
     def test_sukses_login_kontributor_url(self):
         # Login
-        self.client.login(email="kontributor@gov.id", password="kontributor")
+        self.client.login(email="kontributor@gov.id",
+                          password="kontributor")
         # Test
         response = self.client.get(self.url)
         self.assertEqual(response.status_code, 200)
@@ -1300,7 +1340,8 @@ class SuksesLoginKontributorTest(TestCase):
     def test_sukses_login_kontributor_access(self):
         # Kontributor
         # Login
-        self.client.login(email="kontributor@gov.id", password="kontributor")
+        self.client.login(email="kontributor@gov.id",
+                          password="kontributor")
         # Test
         response = self.client.get(self.url)
         self.assertEqual(response.status_code, 200)
@@ -1311,8 +1352,10 @@ class SuksesLoginKontributorTest(TestCase):
 class SuksesLoginAdminTest(TestCase):
     def setUp(self):
         self.client = Client()
-        self.kontributor = User.objects.create_contributor(email="kontributor@gov.id", password="kontributor")
-        self.admin = User.objects.create_admin(email="admin@gov.id", password="admin")
+        self.kontributor = User.objects.create_contributor(email="kontributor@gov.id",
+                                                           password="kontributor")
+        self.admin = User.objects.create_admin(email="admin@gov.id",
+                                               password="admin")
         self.url = "/sukses-admin/"
         self.view = SuksesLoginAdminView
         self.template_name = "sukses_admin.html"
@@ -1323,7 +1366,8 @@ class SuksesLoginAdminTest(TestCase):
 
     def test_sukses_login_admin_template(self):
         # Login
-        self.client.login(email="admin@gov.id", password="admin")
+        self.client.login(email="admin@gov.id",
+                          password="admin")
         # Test
         response = self.client.get(self.url)
         self.assertTemplateUsed(response, self.template_name)
@@ -1332,7 +1376,8 @@ class SuksesLoginAdminTest(TestCase):
 
     def test_sukses_login_admin_url(self):
         # Login
-        self.client.login(email="admin@gov.id", password="admin")
+        self.client.login(email="admin@gov.id",
+                          password="admin")
         # Test
         response = self.client.get(self.url)
         self.assertEqual(response.status_code, 200)
@@ -1342,7 +1387,8 @@ class SuksesLoginAdminTest(TestCase):
     def test_sukses_login_admin_access(self):
         # Kontributor
         # Login
-        self.client.login(email="admin@gov.id", password="admin")
+        self.client.login(email="admin@gov.id",
+                          password="admin")
         # Test
         response = self.client.get(self.url)
         self.assertEqual(response.status_code, 200)
@@ -1352,27 +1398,29 @@ class SuksesLoginAdminTest(TestCase):
 
 class LikeMateriTest(TestCase):
     def setUp(self):
-        self.contributor_credential = {"email": "kontributor@gov.id", "password": id_generator()}
+        self.contributor_credential = {
+            "email": "kontributor@gov.id",
+            "password": id_generator()
+        }
         self.contributor = get_user_model().objects.create_user(
             **self.contributor_credential, name="Kontributor", is_contributor=True
         )
         self.client = Client()
-        self.url_like = "/materi/like/"
+        self.url_like = '/materi/like/'
         content = b"Test file"
-        self.cover = SimpleUploadedFile("cover.jpg", content)
-        self.content = SimpleUploadedFile("content.txt", content)
-        Materi(
-            title="Materi 1",
-            author="Agas",
-            uploader=self.contributor,
-            publisher="Kelas SC",
-            descriptions="Deskripsi Materi 1",
-            status="PENDING",
-            cover=self.cover,
-            content=self.content,
-        ).save()
+        self.cover = SimpleUploadedFile(
+            "cover.jpg",
+            content
+        )
+        self.content = SimpleUploadedFile(
+            "content.txt",
+            content
+        )
+        Materi(title="Materi 1", author="Agas", uploader=self.contributor,
+               publisher="Kelas SC", descriptions="Deskripsi Materi 1",
+               status="PENDING", cover=self.cover, content=self.content).save()
         self.materi1 = Materi.objects.first()
-        self.url_materi = f"/materi/{self.materi1.id}/"
+        self.url_materi = f'/materi/{self.materi1.id}/'
 
     def test_get_method(self):
         response = self.client.get(self.url_like)
@@ -1388,7 +1436,10 @@ class LikeMateriTest(TestCase):
         response = self.client.get(self.url_materi)
         session_id = response.context["session_id"]
         materi_id = response.context["materi_data"].id
-        payload = {"materi_id": materi_id, "session_id": session_id}
+        payload = {
+            'materi_id': materi_id,
+            'session_id': session_id
+        }
         ajax_response = Client().post(self.url_like, payload)
         num_of_likes = Like.objects.filter(materi=self.materi1).count()
         self.assertEqual(num_of_likes, 1)
@@ -1402,7 +1453,10 @@ class LikeMateriTest(TestCase):
         response = self.client.get(self.url_materi)
         session_id = response.context["session_id"]
         materi_id = response.context["materi_data"].id
-        payload = {"materi_id": materi_id, "session_id": session_id}
+        payload = {
+            'materi_id': materi_id,
+            'session_id': session_id
+        }
         ajax_response = Client().post(self.url_like, payload)
         num_of_likes = Like.objects.filter(materi=self.materi1).count()
         self.assertEqual(num_of_likes, 1)
@@ -1411,7 +1465,10 @@ class LikeMateriTest(TestCase):
         response = self.client.get(self.url_materi)
         session_id = response.context["session_id"]
         materi_id = response.context["materi_data"].id
-        payload = {"materi_id": materi_id, "session_id": session_id}
+        payload = {
+            'materi_id': materi_id,
+            'session_id': session_id
+        }
         ajax_response = Client().post(self.url_like, payload)
         num_of_likes = Like.objects.filter(materi=self.materi1).count()
         self.assertEqual(num_of_likes, 0)
@@ -1425,7 +1482,10 @@ class LikeMateriTest(TestCase):
         response = self.client.get(self.url_materi)
         session_id = response.context["session_id"]
         materi_id = response.context["materi_data"].id
-        payload = {"materi_id": materi_id, "session_id": session_id}
+        payload = {
+            'materi_id': materi_id,
+            'session_id': session_id
+        }
         ajax_response = Client().post(self.url_like, payload)
         num_of_likes = Like.objects.filter(materi=self.materi1).count()
         self.assertEqual(num_of_likes, 1)
@@ -1434,7 +1494,10 @@ class LikeMateriTest(TestCase):
         response = Client().get(self.url_materi)
         session_id = response.context["session_id"]
         materi_id = response.context["materi_data"].id
-        payload = {"materi_id": materi_id, "session_id": session_id}
+        payload = {
+            'materi_id': materi_id,
+            'session_id': session_id
+        }
         ajax_response = Client().post(self.url_like, payload)
         num_of_likes = Like.objects.filter(materi=self.materi1).count()
         self.assertEqual(num_of_likes, 2)
@@ -1448,7 +1511,7 @@ class LikeMateriTest(TestCase):
         response = self.client.get(self.url_materi)
         materi_id = response.context["materi_data"].id
         payload = {
-            "materi_id": materi_id,
+            'materi_id': materi_id,
         }
         ajax_response = Client().post(self.url_like, payload)
         ajax_response = json.loads(ajax_response.content)
@@ -1459,7 +1522,9 @@ class LikeMateriTest(TestCase):
         # missing materi id
         response = self.client.get(self.url_materi)
         session_id = response.context["session_id"]
-        payload = {"session_id": session_id}
+        payload = {
+            'session_id': session_id
+        }
         ajax_response = Client().post(self.url_like, payload)
         ajax_response = json.loads(ajax_response.content)
         num_of_likes = Like.objects.filter(materi=self.materi1).count()
@@ -1469,24 +1534,26 @@ class LikeMateriTest(TestCase):
 
 class ViewMateriStatissticsTest(TestCase):
     def setUp(self):
-        self.contributor_credential = {"email": "kontributor@gov.id", "password": id_generator()}
+        self.contributor_credential = {
+            "email": "kontributor@gov.id",
+            "password": id_generator()
+        }
         self.contributor = get_user_model().objects.create_user(
             **self.contributor_credential, name="Kontributor", is_contributor=True
         )
         self.client = Client()
         content = b"Test file"
-        self.cover = SimpleUploadedFile("cover.jpg", content)
-        self.content = SimpleUploadedFile("content.txt", content)
-        Materi(
-            title="Materi 1",
-            author="Agas",
-            uploader=self.contributor,
-            publisher="Kelas SC",
-            descriptions="Deskripsi Materi 1",
-            status="PENDING",
-            cover=self.cover,
-            content=self.content,
-        ).save()
+        self.cover = SimpleUploadedFile(
+            "cover.jpg",
+            content
+        )
+        self.content = SimpleUploadedFile(
+            "content.txt",
+            content
+        )
+        Materi(title="Materi 1", author="Agas", uploader=self.contributor,
+               publisher="Kelas SC", descriptions="Deskripsi Materi 1",
+               status="PENDING", cover=self.cover, content=self.content).save()
         self.materi1 = Materi.objects.first()
         self.url = f"/materi/{self.materi1.id}/view"
 
@@ -1509,24 +1576,26 @@ class ViewMateriStatissticsTest(TestCase):
 
 class DownloadMateriStatissticsTest(TestCase):
     def setUp(self):
-        self.contributor_credential = {"email": "kontributor@gov.id", "password": id_generator()}
+        self.contributor_credential = {
+            "email": "kontributor@gov.id",
+            "password": id_generator()
+        }
         self.contributor = get_user_model().objects.create_user(
             **self.contributor_credential, name="Kontributor", is_contributor=True
         )
         self.client = Client()
         content = b"Test file"
-        self.cover = SimpleUploadedFile("cover.jpg", content)
-        self.content = SimpleUploadedFile("content.txt", content)
-        Materi(
-            title="Materi 1",
-            author="Agas",
-            uploader=self.contributor,
-            publisher="Kelas SC",
-            descriptions="Deskripsi Materi 1",
-            status="PENDING",
-            cover=self.cover,
-            content=self.content,
-        ).save()
+        self.cover = SimpleUploadedFile(
+            "cover.jpg",
+            content
+        )
+        self.content = SimpleUploadedFile(
+            "content.txt",
+            content
+        )
+        Materi(title="Materi 1", author="Agas", uploader=self.contributor,
+               publisher="Kelas SC", descriptions="Deskripsi Materi 1",
+               status="PENDING", cover=self.cover, content=self.content).save()
         self.materi1 = Materi.objects.first()
         self.url = f"/materi/{self.materi1.id}/unduh"
 
@@ -1553,22 +1622,24 @@ class RevisiMateriTest(TestCase):
         self.view = RevisiMateriView
         self.template_name = "revisi.html"
 
-        self.contributor_credential = {"email": "kontributor@gov.id", "password": id_generator()}
+        self.contributor_credential = {
+            "email": "kontributor@gov.id",
+            "password": id_generator()
+        }
         self.contributor = get_user_model().objects.create_user(
             **self.contributor_credential, name="Kontributor", is_contributor=True
         )
-        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="REVISION",
-            cover=self.cover,
-            content=self.content,
-        ).save()
+        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="REVISION", cover=self.cover, content=self.content).save()
 
         self.materi1 = Materi.objects.first()
         self.url = "/revisi/materi/" + str(self.materi1.id) + "/"
@@ -1604,9 +1675,9 @@ class RevisiMateriTest(TestCase):
         self.assertEqual(response.status_code, 200)
         # Logout
         self.client.logout()
-
-
+    
 class GenerateDummyCommandTest(TestCase):
+
     def setUp(self):
         self.material_numbers = [5, 10, 25, 100]
         self.invalid_material_numbers = [-100, -10, -1, 0, 1, 2, 3, 4]
@@ -1615,7 +1686,10 @@ class GenerateDummyCommandTest(TestCase):
     def test_command_output_with_given_num_of_materi(self):
         for num_of_materi in self.material_numbers:
             call_command("generatedummy", num_of_materi, stdout=self.stdout)
-            self.assertIn(f"Successfully created {num_of_materi} materi", self.stdout.getvalue())
+            self.assertIn(
+                f"Successfully created {num_of_materi} materi",
+                self.stdout.getvalue()
+            )
 
     def test_command_should_generate_materi_objects(self):
         for num_of_materi in self.material_numbers:
@@ -1630,6 +1704,7 @@ class GenerateDummyCommandTest(TestCase):
 
 
 class RemoveDummyCommandTest(TestCase):
+
     def test_calling_remove_dummy_command_should_remove_generated_dummy_objects(self):
         stdout = StringIO()
         call_command("generatedummy", 50)
@@ -1646,40 +1721,41 @@ class RemoveDummyCommandTest(TestCase):
 
 class RatingMateriTest(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_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="PENDING",
-            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.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="PENDING", 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.all()[0]
         self.materi2 = Materi.objects.all()[1]
-        self.url_rate = "/materi/rate/"
-        self.url_materi = "/materi/{}/".format(self.materi1.id)
+        self.url_rate = '/materi/rate/'
+        self.url_materi = '/materi/{}/'.format(self.materi1.id)
 
     def test_rating_model_can_be_created_with_proper_parameter(self):
         Rating(materi=self.materi1, user=self.user_one, score=5).save()
@@ -1692,12 +1768,12 @@ class RatingMateriTest(TestCase):
     def test_rating_model_should_not_be_created_with_rating_more_than_5(self):
         with self.assertRaises(ValidationError) as context:
             Rating(materi=self.materi1, user=self.user_one, score=6).save()
-        self.assertTrue("Rating score must be integer between 1-5" in str(context.exception))
+        self.assertTrue('Rating score must be integer between 1-5' in str(context.exception))
 
     def test_rating_model_should_not_be_created_with_rating_less_than_1(self):
         with self.assertRaises(ValidationError) as context:
             Rating(materi=self.materi1, user=self.user_one, score=0).save()
-        self.assertTrue("Rating score must be integer between 1-5" in str(context.exception))
+        self.assertTrue('Rating score must be integer between 1-5' in str(context.exception))
 
     def test_one_materi_should_be_able_to_be_related_to_multiple_rating(self):
         Rating(materi=self.materi1, user=self.user_one, score=1).save()
@@ -1723,7 +1799,7 @@ class RatingMateriTest(TestCase):
         with self.assertRaises(IntegrityError) as context:
             Rating(materi=self.materi1, user=self.user_one, score=1).save()
             Rating(materi=self.materi1, user=self.user_one, score=2).save()
-        self.assertTrue("already exists" in str(context.exception))
+        self.assertTrue('already exists' in str(context.exception))
 
     def test_materi_in_rating_should_not_be_null(self):
         with self.assertRaises(IntegrityError):
@@ -1749,14 +1825,14 @@ class RatingMateriTest(TestCase):
         self.assertEqual(response.status_code, 403)
 
     def test_rating_materi_post_not_authenticated_should_return_403_forbidden(self):
-        response = self.client.post(self.url_rate, {"materi_id": 1, "rating_score": 5})
+        response = self.client.post(self.url_rate, {'materi_id': 1, 'rating_score': 5})
         response_json = json.loads(response.content)
         self.assertEqual(response_json.get("success", None), False)
         self.assertEqual(response_json.get("msg", None), "Forbidden")
         self.assertEqual(response.status_code, 403)
 
     def test_rating_materi_not_authenticated_post_wrong_param_should_return_403_forbidden(self):
-        for data in [{}, {"materi_id": 1}, {"rating_score": 1}, {"rating_score": "STRING", "materi_id": "STRING"}]:
+        for data in [{}, {'materi_id': 1}, {'rating_score': 1}, {'rating_score': 'STRING', 'materi_id': 'STRING'}]:
             response = self.client.post(self.url_rate, data)
             response_json = json.loads(response.content)
             self.assertEqual(response_json.get("success", None), False)
@@ -1765,7 +1841,7 @@ class RatingMateriTest(TestCase):
 
     def test_rating_materi_authenticated_post_missing_param(self):
         self.client.login(**self.user_one_credential)
-        for data in [{"rating_score": 1}, {"materi_id": 1}, {}]:
+        for data in [{'rating_score': 1}, {'materi_id': 1}, {}]:
             response = self.client.post(self.url_rate, data)
             response_json = json.loads(response.content)
             self.assertEqual(response_json.get("success", None), False)
@@ -1774,7 +1850,7 @@ class RatingMateriTest(TestCase):
 
     def test_rating_materi_authenticated_materi_id_doesnt_exist_should_return_422(self):
         self.client.login(**self.user_one_credential)
-        response = self.client.post(self.url_rate, {"materi_id": 123456, "rating_score": 5})
+        response = self.client.post(self.url_rate, {'materi_id': 123456, 'rating_score': 5})
         response_json = json.loads(response.content)
         self.assertEqual(response_json.get("success", None), False)
         self.assertEqual(response_json.get("msg", None), "Materi does not exist")
@@ -1782,13 +1858,13 @@ class RatingMateriTest(TestCase):
 
     def test_rating_materi_authenticated_param_wrong_data_type_should_return_422(self):
         self.client.login(**self.user_one_credential)
-        response = self.client.post(self.url_rate, {"materi_id": "STRING", "rating_score": 5})
+        response = self.client.post(self.url_rate, {'materi_id': "STRING", 'rating_score': 5})
         response_json = json.loads(response.content)
         self.assertEqual(response_json.get("success", None), False)
         self.assertEqual(response_json.get("msg", None), "materi_id must be an integer")
         self.assertEqual(response.status_code, 422)
 
-        response = self.client.post(self.url_rate, {"materi_id": 1, "rating_score": "STRING"})
+        response = self.client.post(self.url_rate, {'materi_id': 1, 'rating_score': "STRING"})
         response_json = json.loads(response.content)
         self.assertEqual(response_json.get("success", None), False)
         self.assertEqual(response_json.get("msg", None), "rating_score must be an integer")
@@ -1798,7 +1874,7 @@ class RatingMateriTest(TestCase):
         self.client.login(**self.user_one_credential)
         for i in range(1, 6):
             Rating.objects.all().delete()
-            response = self.client.post(self.url_rate, {"materi_id": self.materi1.id, "rating_score": i})
+            response = self.client.post(self.url_rate, {'materi_id': self.materi1.id, 'rating_score': i})
             response_json = json.loads(response.content)
             # self.assertEqual(response_json.get("success", None), True)
             self.assertEqual(response_json.get("msg", None), "Rating successfully created")
@@ -1806,7 +1882,7 @@ class RatingMateriTest(TestCase):
 
         for i in [-100, -7, -6, -1, 0, 6, 7, 100]:
             Rating.objects.all().delete()
-            response = self.client.post(self.url_rate, {"materi_id": self.materi1.id, "rating_score": i})
+            response = self.client.post(self.url_rate, {'materi_id': self.materi1.id, 'rating_score': i})
             response_json = json.loads(response.content)
             # self.assertEqual(response_json.get("success", None), False)
             self.assertEqual(response_json.get("msg", None), "Rating must be an integer from 1 to 5")
@@ -1815,8 +1891,8 @@ class RatingMateriTest(TestCase):
     def test_user_should_not_able_to_rate_materi_twice(self):
         self.client.login(**self.user_one_credential)
         Rating.objects.all().delete()
-        self.client.post(self.url_rate, {"materi_id": self.materi1.id, "rating_score": 1})
-        response = self.client.post(self.url_rate, {"materi_id": self.materi1.id, "rating_score": 2})
+        self.client.post(self.url_rate, {'materi_id': self.materi1.id, 'rating_score': 1})
+        response = self.client.post(self.url_rate, {'materi_id': self.materi1.id, 'rating_score': 2})
         response_json = json.loads(response.content)
         # self.assertEqual(response_json.get("success", None), False)
         self.assertEqual(response_json.get("msg", None), "Rating already exist")
@@ -1825,23 +1901,23 @@ class RatingMateriTest(TestCase):
     def test_user_authenticated_visit_unrated_should_get_0_materi_rating_score_context(self):
         self.client.login(**self.user_one_credential)
         response = self.client.get(self.url_materi)
-        self.assertEqual(0, response.context.get("materi_rating_score"))
+        self.assertEqual(0, response.context.get('materi_rating_score'))
 
     def test_user_not_authenticated_visit_unrated_should_get_0_materi_rating_score_context(self):
         response = self.client.get(self.url_materi)
-        self.assertEqual(0, response.context.get("materi_rating_score"))
+        self.assertEqual(0, response.context.get('materi_rating_score'))
 
     def test_user_authenticated_visit_rated_should_get_correct_materi_rating_score_context(self):
         self.client.login(**self.user_one_credential)
         Rating(materi=self.materi1, user=self.user_one, score=1).save()
         response = self.client.get(self.url_materi)
-        self.assertEqual(1, response.context.get("materi_rating_score"))
-
+        self.assertEqual(1, response.context.get('materi_rating_score'))
 
 class fileManagementUtilTest(TestCase):
     def setUp(self):
         self.filename = "image_with_exif_data.gif"
-        self.file_content = open(settings.BASE_DIR + "/app/test_files/" + self.filename, "rb").read()
+        self.file_content = open(settings.BASE_DIR + "/app/test_files/" +
+                self.filename, "rb").read()
 
     def test_get_random_filename_isCorrect(self):
         generated_name = get_random_filename(self.filename)
@@ -1862,20 +1938,30 @@ class fileManagementUtilTest(TestCase):
             sanitized_img = open(image_with_exif_data_path, "rb").read()
 
             self.assertTrue(len(sanitized_img) < len(self.file_content))
-            self.assertTrue(b"<exif:" not in sanitized_img)
-
+            self.assertTrue(b'<exif:' not in sanitized_img)
 
 class RequestMateriTest(TestCase):
     def setUp(self):
         self.client = Client()
-        self.admin_credential = {"email": "admin@gov.id", "password": "passwordtest"}
-        self.contributor_credential = {"email": "kontributor@gov.id", "password": "passwordtest"}
-        self.anonymous_credential = {"email": "anonymous@gov.id", "password": "passwordtest"}
-        self.admin = get_user_model().objects.create_user(**self.admin_credential, name="Admin", is_admin=True)
+        self.admin_credential = {
+            "email": "admin@gov.id",
+            "password": "passwordtest"
+        }
+        self.contributor_credential = {
+            "email": "kontributor@gov.id",
+            "password": "passwordtest"
+        }
+        self.anonymous_credential = {
+            "email": "anonymous@gov.id",
+            "password": "passwordtest"
+        }
+        self.admin = get_user_model().objects.create_user(
+            **self.admin_credential, name="Admin", is_admin=True)
         self.contributor = get_user_model().objects.create_user(
-            **self.contributor_credential, name="Kontributor", is_contributor=True
+            **self.contributor_credential, name="Kontributor", is_contributor=True)
+        self.anonymous = get_user_model().objects.create_user(
+            **self.anonymous_credential, name="Anonymous"
         )
-        self.anonymous = get_user_model().objects.create_user(**self.anonymous_credential, name="Anonymous")
         self.url = "/req-materi/"
         self.template_name = "req_materi.html"
 
@@ -1893,7 +1979,7 @@ class RequestMateriTest(TestCase):
         response = self.client.get(self.url)
 
         self.assertEqual(response.status_code, 302)
-        self.assertEqual(response["location"], "/login/")
+        self.assertEqual(response['location'], '/login/')
 
     def test_saving_and_retrieving_material_requests(self):
         first_material_request = ReqMaterial(title="Material 1").save()
@@ -1904,13 +1990,16 @@ class RequestMateriTest(TestCase):
     def test_can_save_a_POST_request_and_return_correct_response_message(self):
         self.client.login(**self.contributor_credential)
 
-        response = self.client.post(self.url, data={"title": "Requested Material"})
+        response = self.client.post(self.url,
+                         data={
+                             'title': 'Requested Material'
+                         })
         self.assertEqual(ReqMaterial.objects.count(), 1)
 
         new_material_request = ReqMaterial.objects.first()
-        self.assertEqual(new_material_request.title, "Requested Material")
+        self.assertEqual(new_material_request.title, 'Requested Material')
 
-        self.assertIn("Permintaan materi berhasil dikirimkan", response.content.decode())
+        self.assertIn('Permintaan materi berhasil dikirimkan', response.content.decode())
         self.client.logout()
 
     def test_given_no_title_should_not_save_request_and_return_correct_response_message(self):
@@ -1919,13 +2008,16 @@ class RequestMateriTest(TestCase):
         response = self.client.post(self.url)
         self.assertEqual(ReqMaterial.objects.count(), 0)
 
-        self.assertIn("Missing parameter", response.content.decode())
+        self.assertIn('Missing parameter', response.content.decode())
         self.client.logout()
 
 
 class RatingContributorTest(TransactionTestCase):
     def setUp(self):
-        self.contributor_credential = {"email": "kontributor@gov.id", "password": id_generator()}
+        self.contributor_credential = {
+            "email": "kontributor@gov.id",
+            "password": id_generator()
+        }
         self.contributor = get_user_model().objects.create_user(
             **self.contributor_credential, name="Kontributor", is_contributor=True
         )
@@ -1958,53 +2050,62 @@ class RatingContributorTest(TransactionTestCase):
         self.client.post(url, data={"user": self.contributor.id, "score": 0})
         self.assertEqual(0, RatingContributor.objects.filter(user=self.contributor.id).count())
 
-
 class UserDownloadHistoryTest(TestCase):
     def setUp(self):
-        self.user1_credential = {"email": "anonim1@gov.id", "password": id_generator()}
-        self.user1_anonim = get_user_model().objects.create_user(**self.user1_credential, name="Anonim1")
-        self.user2_credential = {"email": "anonim2@gov.id", "password": id_generator()}
-        self.user2_anonim = get_user_model().objects.create_user(**self.user2_credential, name="Anonim2")
-        self.contributor_credential = {"email": "kontributor@gov.id", "password": id_generator()}
+        self.user1_credential = {
+            "email": "anonim1@gov.id",
+            "password": id_generator()
+        }
+        self.user1_anonim = get_user_model().objects.create_user(
+            **self.user1_credential, name="Anonim1")
+        self.user2_credential = {
+            "email": "anonim2@gov.id",
+            "password": id_generator()
+        }
+        self.user2_anonim = get_user_model().objects.create_user(
+            **self.user2_credential, name="Anonim2")
+        self.contributor_credential = {
+            "email": "kontributor@gov.id",
+            "password": id_generator()
+        }
         self.contributor = get_user_model().objects.create_user(
             **self.contributor_credential, name="Kontributor", is_contributor=True
         )
         self.client = Client()
         content = b"Test file"
-        self.cover = SimpleUploadedFile("cover.jpg", content)
-        self.content = SimpleUploadedFile("content.txt", content)
-        Materi(
-            title="Materi 1",
-            author="Agas",
-            uploader=self.contributor,
-            publisher="Kelas SC",
-            descriptions="Deskripsi Materi 1",
-            status="PENDING",
-            cover=self.cover,
-            content=self.content,
-        ).save()
+        self.cover = SimpleUploadedFile(
+            "cover.jpg",
+            content
+        )
+        self.content = SimpleUploadedFile(
+            "content.txt",
+            content
+        )
+        Materi(title="Materi 1", author="Agas", uploader=self.contributor,
+               publisher="Kelas SC", descriptions="Deskripsi Materi 1",
+               status="PENDING", cover=self.cover, content=self.content).save()
         self.materi1 = Materi.objects.first()
         self.download_url = f"/materi/{self.materi1.id}/unduh"
         self.history_url = "/download-history/"
-
+	
     def test_multiple_insert_download_statistic_with_user(self):
         DownloadStatistics(materi=self.materi1, downloader=self.user1_anonim).save()
         num_of_downloads = self.user1_anonim.riwayat_unduh.all().count()
         self.assertEqual(num_of_downloads, 1)
-
+		
         DownloadStatistics(materi=self.materi1, downloader=self.user1_anonim).save()
         num_of_downloads = self.user1_anonim.riwayat_unduh.all().count()
         self.assertEqual(num_of_downloads, 2)
-
+		
     def test_download_statistics_bound_to_specific_user(self):
         DownloadStatistics(materi=self.materi1, downloader=self.user1_anonim).save()
         num_of_downloads = self.user1_anonim.riwayat_unduh.all().count()
         self.assertEqual(num_of_downloads, 1)
-
+		
         DownloadStatistics(materi=self.materi1).save()
         num_of_downloads = self.user1_anonim.riwayat_unduh.all().count()
         self.assertEqual(num_of_downloads, 1)
-
+		
         DownloadStatistics(materi=self.materi1, downloader=self.user2_anonim).save()
         user1_num_of_downloads = self.user1_anonim.riwayat_unduh.all().count()
         user2_num_of_downloads = self.user2_anonim.riwayat_unduh.all().count()
@@ -2014,18 +2115,19 @@ class UserDownloadHistoryTest(TestCase):
     def test_registered_user_download(self):
         # Login
         self.client.login(**self.user1_credential)
-
+		
         self.client.get(self.download_url)
         num_of_downloads = self.user1_anonim.riwayat_unduh.all().count()
         self.assertEqual(num_of_downloads, 1)
-
+		
         # Logout
         self.client.logout()
-
+		
     def test_unregistered_user_download(self):
         self.client.get(self.download_url)
-        downloaded_materi = self.client.session["downloaded_materi"]
-        num_of_downloads = DownloadStatistics.objects.filter(pk__in=downloaded_materi).count()
+        downloaded_materi = self.client.session['downloaded_materi']
+        num_of_downloads = DownloadStatistics.objects.filter(
+                            pk__in=downloaded_materi).count()
         self.assertEqual(num_of_downloads, 1)
 
     def test_registered_user_multiple_download(self):
@@ -2038,50 +2140,53 @@ class UserDownloadHistoryTest(TestCase):
         self.client.get(self.download_url)
         num_of_downloads = self.user1_anonim.riwayat_unduh.all().count()
         self.assertEqual(num_of_downloads, 2)
-
+		
         # Logout
         self.client.logout()
-
+		
     def test_unregistered_user_multiple_download(self):
         self.client.get(self.download_url)
-        downloaded_materi = self.client.session["downloaded_materi"]
-        num_of_downloads = DownloadStatistics.objects.filter(pk__in=downloaded_materi).count()
+        downloaded_materi = self.client.session['downloaded_materi']
+        num_of_downloads = DownloadStatistics.objects.filter(
+                            pk__in=downloaded_materi).count()
         self.assertEqual(num_of_downloads, 1)
 
         self.client.get(self.download_url)
-        downloaded_materi = self.client.session["downloaded_materi"]
-        num_of_downloads = DownloadStatistics.objects.filter(pk__in=downloaded_materi).count()
+        downloaded_materi = self.client.session['downloaded_materi']
+        num_of_downloads = DownloadStatistics.objects.filter(
+                            pk__in=downloaded_materi).count()
         self.assertEqual(num_of_downloads, 2)
-
+		
     def test_registered_user_doesnt_use_session_when_download(self):
         # Login
         self.client.login(**self.user1_credential)
-
+		
         self.client.get(self.download_url)
-        self.assertFalse("downloaded_materi" in self.client.session)
-
+        self.assertFalse('downloaded_materi' in self.client.session)
+		
         # Logout
         self.client.logout()
-
+	
     def test_download_history_bound_to_specific_user(self):
         # Login Anonym 1
         self.client.login(**self.user1_credential)
         self.client.get(self.download_url)
         num_of_downloads = self.user1_anonim.riwayat_unduh.all().count()
         self.assertEqual(num_of_downloads, 1)
-
+		
         # Logout Anonym 1
         self.client.logout()
-
-        # Unregistered User download
+		
+		# Unregistered User download
         self.client.get(self.download_url)
         user1_num_of_downloads = self.user1_anonim.riwayat_unduh.all().count()
-        downloaded_materi = self.client.session["downloaded_materi"]
-        guest_num_of_downloads = DownloadStatistics.objects.filter(pk__in=downloaded_materi).count()
+        downloaded_materi = self.client.session['downloaded_materi']
+        guest_num_of_downloads = DownloadStatistics.objects.filter(
+                            pk__in=downloaded_materi).count()
         self.assertEqual(user1_num_of_downloads, 1)
         self.assertEqual(guest_num_of_downloads, 1)
-
-        # Login Anonym 2
+		
+		# Login Anonym 2
         self.client.login(**self.user2_credential)
         self.client.get(self.download_url)
         user1_num_of_downloads = self.user1_anonim.riwayat_unduh.all().count()
@@ -2089,70 +2194,62 @@ class UserDownloadHistoryTest(TestCase):
         self.assertEqual(user1_num_of_downloads, 1)
         self.assertEqual(guest_num_of_downloads, 1)
         self.assertEqual(user2_num_of_downloads, 1)
-
+		
         # Logout Anonym 2
         self.client.logout()
 
-
 class DownloadHistoryViewTest(TestCase):
     def setUp(self):
-        self.user_credential = {"email": "anonim1@gov.id", "password": id_generator()}
-        self.user_anonim = get_user_model().objects.create_user(**self.user_credential, name="Anonim")
-        self.contributor_credential = {"email": "kontributor@gov.id", "password": id_generator()}
+        self.user_credential = {
+            "email": "anonim1@gov.id",
+            "password": id_generator()
+        }
+        self.user_anonim = get_user_model().objects.create_user(
+            **self.user_credential, name="Anonim")
+        self.contributor_credential = {
+            "email": "kontributor@gov.id",
+            "password": id_generator()
+        }
         self.contributor = get_user_model().objects.create_user(
             **self.contributor_credential, name="Kontributor", is_contributor=True
         )
         self.client = Client()
-
+		
         content1 = b"Test file"
         content2 = b"File Test"
-
-        self.cover1 = SimpleUploadedFile("cover1.jpg", content1)
-        self.content1 = SimpleUploadedFile("content1.txt", content1)
-
-        self.cover2 = SimpleUploadedFile("cover2.jpg", content2)
-        self.content2 = SimpleUploadedFile("content2.txt", content2)
-
-        self.materi1 = Materi.objects.create(
-            title="Materi 1",
-            author="Agas",
-            uploader=self.contributor,
-            publisher="Kelas SC",
-            descriptions="Deskripsi Materi 1",
-            status="PENDING",
-            cover=self.cover1,
-            content=self.content1,
-        )
-        self.materi2 = Materi.objects.create(
-            title="Materi 2",
-            author="Danin",
-            uploader=self.contributor,
-            publisher="Kelas DDP",
-            descriptions="Deskripsi Materi 2",
-            status="PENDING",
-            cover=self.cover2,
-            content=self.content2,
-        )
-
+		
+        self.cover1 = SimpleUploadedFile("cover1.jpg",content1)
+        self.content1 = SimpleUploadedFile("content1.txt",content1)
+		
+        self.cover2 = SimpleUploadedFile("cover2.jpg",content2)
+        self.content2 = SimpleUploadedFile("content2.txt",content2)
+		
+        self.materi1 = Materi.objects.create(title="Materi 1", author="Agas", uploader=self.contributor,
+               publisher="Kelas SC", descriptions="Deskripsi Materi 1",
+               status="PENDING", cover=self.cover1, content=self.content1)
+        self.materi2 = Materi.objects.create(title="Materi 2", author="Danin", uploader=self.contributor,
+               publisher="Kelas DDP", descriptions="Deskripsi Materi 2",
+               status="PENDING", cover=self.cover2, content=self.content2)
+			   
         self.download_url1 = f"/materi/{self.materi1.id}/unduh"
         self.download_url2 = f"/materi/{self.materi2.id}/unduh"
         self.history_url = "/download-history/"
-
+		
         # Login
         self.client.login(**self.user_credential)
 
     def tearDown(self):
         # Logout
         self.client.logout()
-
+		
     def test_allow_registered_user(self):
         response = self.client.get(self.history_url)
         self.assertEqual(response.status_code, 200)
-
+	
     def test_allow_unregistered_user(self):
-        # Forced Logout
+	    # Forced Logout
         self.client.logout()
-
+		
         response = self.client.get(self.history_url)
         self.assertEqual(response.status_code, 200)
 
@@ -2162,82 +2259,82 @@ class DownloadHistoryViewTest(TestCase):
 
     def test_download_history_has_user_name(self):
         response = self.client.get(self.history_url)
-        resp_html = response.content.decode("utf8")
+        resp_html = response.content.decode('utf8')
         self.assertIn(self.user_anonim.name, resp_html)
-
+		
     def test_registered_user_download_history_correctly_displayed(self):
         self.client.get(self.download_url1)
         self.client.get(self.download_url2)
         self.client.get(self.download_url1)
-
+		
         jkt_timezone = timezone(TIME_ZONE)
-
+		
         download_history = self.user_anonim.riwayat_unduh.all()
         response = self.client.get(self.history_url)
-        resp_html = response.content.decode("utf8")
+        resp_html = response.content.decode('utf8')
         for riwayat in download_history:
             downloaded_materi = riwayat.materi
             self.assertIn(downloaded_materi.title, resp_html)
             self.assertIn(downloaded_materi.author, resp_html)
-
+			
             jkt_timestamp = riwayat.timestamp.astimezone(jkt_timezone)
             self.assertIn(jkt_timestamp.strftime("%d %B %Y %H:%M:%S"), resp_html)
-
+			
     def test_unregistered_user_download_history_correctly_displayed(self):
         self.client.logout()
 
         self.client.get(self.download_url1)
         self.client.get(self.download_url2)
         self.client.get(self.download_url1)
-
+		
         jkt_timezone = timezone(TIME_ZONE)
-
+		
         response = self.client.get(self.history_url)
-        resp_html = response.content.decode("utf8")
-        for riwayat_id in self.client.session["downloaded_materi"]:
+        resp_html = response.content.decode('utf8')
+        for riwayat_id in self.client.session['downloaded_materi']:
             riwayat = DownloadStatistics.objects.get(pk=riwayat_id)
             downloaded_materi = riwayat.materi
             self.assertIn(downloaded_materi.title, resp_html)
             self.assertIn(downloaded_materi.author, resp_html)
-
+			
             jkt_timestamp = riwayat.timestamp.astimezone(jkt_timezone)
             self.assertIn(jkt_timestamp.strftime("%d %B %Y %H:%M:%S"), resp_html)
-
+			
     def test_download_history_not_display_if_user_changed(self):
         self.client.get(self.download_url1)
         self.client.get(self.download_url2)
         self.client.get(self.download_url1)
 
         self.client.logout()
-
+		
         jkt_timezone = timezone(TIME_ZONE)
-
+		
         download_history = self.user_anonim.riwayat_unduh.all()
         response = self.client.get(self.history_url)
-        resp_html = response.content.decode("utf8")
+        resp_html = response.content.decode('utf8')
         for riwayat in download_history:
             downloaded_materi = riwayat.materi
             self.assertNotIn(downloaded_materi.title, resp_html)
             self.assertNotIn(downloaded_materi.author, resp_html)
-
+			
             jkt_timestamp = riwayat.timestamp.astimezone(jkt_timezone)
             self.assertNotIn(jkt_timestamp.strftime("%d %B %Y %H:%M:%S"), resp_html)
-
+			
     def test_unregistered_user_download_history_wont_be_saved_if_user_changes(self):
         self.client.logout()
 
         self.client.get(self.download_url1)
         self.client.get(self.download_url2)
         self.client.get(self.download_url1)
-
+		
         self.client.get(self.history_url)
 
         self.client.login(**self.user_credential)
         self.client.logout()
-        self.assertFalse("downloaded_materi" in self.client.session)
-
+        self.assertFalse('downloaded_materi' in self.client.session)
+			
     def test_download_history_sorted_by_download_time(self):
-        # download with 1 second interval to differ download time
+		# download with 1 second interval to differ download time
         self.client.get(self.download_url1)
         sleep(1)
         self.client.get(self.download_url2)
@@ -2245,15 +2342,15 @@ class DownloadHistoryViewTest(TestCase):
         self.client.get(self.download_url1)
         sleep(1)
         self.client.get(self.download_url2)
-
+		
         response = self.client.get(self.history_url)
-        resp_html = response.content.decode("utf8")
-
+        resp_html = response.content.decode('utf8')
+		
         table_html = ("<table" + resp_html.split("<table")[1]).split("</table>")[0] + "</table>"
-        soup = BeautifulSoup(table_html, "html.parser")
-        histories_html = soup.find("tbody").find_all("tr")
+        soup = BeautifulSoup(table_html, 'html.parser')
+        histories_html = soup.find('tbody').find_all('tr')
         prev_timestamp = None
-
+		
         for riwayat_html in histories_html:
             materi_data = riwayat_html.find_all("td")
             date_format = "%d %B %Y %H:%M:%S"
@@ -2261,35 +2358,32 @@ class DownloadHistoryViewTest(TestCase):
             if prev_timestamp:
                 self.assertTrue(prev_timestamp > materi_timestamp)
             prev_timestamp = materi_timestamp
-
+			
     def test_no_history_display_message(self):
         no_history_msg = "Anda belum mengunduh materi. Silahkan unduh materi yang anda butuhkan"
         response = self.client.get(self.history_url)
-        resp_html = response.content.decode("utf8")
+        resp_html = response.content.decode('utf8')
         self.assertIn(no_history_msg, resp_html)
 
 
 class MateriModelTest(TestCase):
+    
     def setUp(self):
-        self.contributor = User.objects.create(
-            email="kontributor@gov.id", password="passwordtest", name="kontributor", is_contributor=True
+        self.contributor = User.objects.create( 
+            email="kontributor@gov.id",
+            password="passwordtest",
+            name="kontributor",
+            is_contributor=True
         )
 
-        self.cover = SimpleUploadedFile("ExampleCover221.jpg", b"Test file")
+        self.cover = SimpleUploadedFile(
+            "ExampleCover221.jpg", b"Test file")
         self.content = SimpleUploadedFile("ExampleFile221.pdf", b"Test file")
 
-        self.materi = Materi.objects.create(
-            title="Materi 1",
-            author="Agas",
-            uploader=self.contributor,
-            publisher="Kelas SC",
-            descriptions="Deskripsi Materi 1",
-            status="APPROVE",
-            cover=self.cover,
-            content=self.content,
-            date_modified=datetime.now(),
-            date_created=datetime.now(),
-        )
+        self.materi = Materi.objects.create(title="Materi 1", author="Agas", uploader=self.contributor,
+                                            publisher="Kelas SC", descriptions="Deskripsi Materi 1",
+                                            status="APPROVE", cover=self.cover, content=self.content,
+                                            date_modified=datetime.now(), date_created=datetime.now())
 
     def test_like_count_return_zero_when_there_is_no_like(self):
         self.assertEqual(0, self.materi.like_count)
@@ -2301,6 +2395,24 @@ class MateriModelTest(TestCase):
         Like.objects.create(timestamp=datetime.now(), materi=self.materi, session_id="dummysessionid2")
         self.assertEqual(2, self.materi.like_count)
 
+class MateriFavoriteTest(TestCase):
+    @classmethod
+    def setUpTestData(cls):
+        cls.url = '/favorite/'
+        cls.user_credentials = {
+            "email": "user@email.com",
+            "password": "justpass"
+        }
+        cls.user = User.objects.create_user(**cls.user_credentials, is_contributor=True)
+
+    def _request_as_user(self):
+        self.client.login(**self.user_credentials)
+        return self.client.get(self.url)
+
+    def test_url_resolves_to_favorite_view(self):
+        found = resolve(self.url)
+        self.assertEqual(found.func.__name__, MateriFavorite.as_view().__name__)
+
 class RandomizedMateriTest(TestCase):
     def setUp(self):
         self.contributor = User.objects.create(
@@ -2398,4 +2510,4 @@ class ChangePasswordTest(TestCase):
         # Logout
         self.client.logout()
 
-    
\ No newline at end of file
+    
diff --git a/app/urls.py b/app/urls.py
index 8061b3f691d49e91c8f5ea4f99e66efc780b81e5..56e7eee36409cd6c26132011cf0f23fc781079ec 100644
--- a/app/urls.py
+++ b/app/urls.py
@@ -5,7 +5,7 @@ from app.views import (DashboardKontributorView, ProfilKontributorView,
                        SuksesLoginAdminView, SuksesLoginKontributorView, DownloadHistoryView,
                        SuntingProfilView, UploadMateriHTML, UploadMateriView, UploadMateriExcelView,
                        ProfilAdminView, PostsView, SuntingProfilAdminView,
-                       ReqMateriView, KatalogPerKontributorView, PasswordChangeViews, password_success)
+                       ReqMateriView, KatalogPerKontributorView, MateriFavorite, PasswordChangeViews, password_success)
 from django.contrib.auth import views as auth_views
 urlpatterns = [
     path("", views.DaftarKatalog.as_view(), name="daftar_katalog"),
@@ -37,6 +37,7 @@ urlpatterns = [
          name="katalog-per-kontributor"),
     path("materi/rate/", views.add_rating_materi, name="rate-materi"),
     path("materi/<int:pk>/save-to-gdrive/", views.save_to_gdrive, name="save-to-gdrive"),
+    path("favorite/", MateriFavorite.as_view(), name="favorite"),
     path("change-password/", PasswordChangeViews.as_view(template_name='change-password.html')),
     path("password_success/", views.password_success, name="password_success"),
 ]
diff --git a/app/views.py b/app/views.py
index 8ad0176a7630e0c6501d330b4a27045a9ef19bf3..b0f703e4b6fa032a5c5eb0aac2bfcd172da904de 100644
--- a/app/views.py
+++ b/app/views.py
@@ -701,10 +701,38 @@ def save_to_gdrive(request, pk):
 
     return HttpResponseRedirect(reverse('detail-materi', kwargs={'pk': pk}))
 
+class MateriFavorite(TemplateView):
+
+    template_name = "user_favorite_materi.html"
+
+    def dispatch(self, request, *args, **kwargs):
+        if not request.user.is_authenticated:
+            raise PermissionDenied(request)
+        return super(MateriFavorite, self).dispatch(request, *args, **kwargs)
+
+    def get(self, request, *args, **kwargs):
+        context = super().get_context_data(**kwargs)
+        user = self.request.user
+
+        materi = Materi.objects.filter(like=True)
+        likes_data = { mat.id: { "data": mat, "comments": [] } for mat in materi }
+
+        comments = Comment.objects \
+            .filter(materi__id__in=likes_data.keys()) \
+            .order_by("-timestamp")
+
+        for comment in comments:
+            likes_data[comment.materi.id]["comments"].append(comment)
+
+        context["user"] = user
+        context["likes"] = likes_data
+
+        return self.render_to_response(context=context)
+
 class PasswordChangeViews(PasswordChangeView):
     
     from_class = PasswordChangeForm
     success_url = reverse_lazy('password_success')
 
 def password_success(request):
-    return render(request, 'password_success.html', {})
\ No newline at end of file
+    return render(request, 'password_success.html', {})