diff --git a/app/models.py b/app/models.py
index fb932f7546c54b0f22af0e02c9e1212f123b51f6..b4d3fda4b9f0563394814cb97af4647a76cfdafd 100644
--- a/app/models.py
+++ b/app/models.py
@@ -1,5 +1,6 @@
 import random
 import datetime
+import math
 
 from django.contrib.postgres import search
 from django.core.exceptions import ValidationError
@@ -135,6 +136,25 @@ class Materi(SoftDeleteModel):
         count = Review.objects.filter(materi=self).count()
         return count
 
+    @staticmethod
+    def earliest_materi_timestamp():
+        return Materi.objects.earliest('date_created').date_created.timestamp()
+
+    @property
+    def seconds_since_earliest_materi(self):
+        return self.date_created.timestamp() - Materi.earliest_materi_timestamp()
+
+    @property
+    def view_count(self):
+        count = ViewStatistics.objects.filter(materi=self).count()
+        return count
+
+    @property
+    def hot_score(self):
+        view_score = math.log(max(self.view_count, 1), 10)
+        time_score = self.seconds_since_earliest_materi / 604800 # 1 week
+        return round(view_score + time_score, 7)
+
     @property
     def is_like(self):
         like = False
diff --git a/app/services.py b/app/services.py
index 4ae99b55b1349e03cdcf79d9ef244944592b64b9..6b298bf76b65929bf2a458ee31869639f6023a8a 100644
--- a/app/services.py
+++ b/app/services.py
@@ -63,6 +63,9 @@ class DafterKatalogService:
             lst_materi = lst_materi.order_by('date_created')
         elif (get_sort == "terpopuler"):
             lst_materi = lst_materi.annotate(count=Count('like__id')).order_by('-count')
+        elif (get_sort == "terhangat"):
+            lst_materi = sorted(lst_materi, 
+                key=lambda t: (t.hot_score, t.date_created), reverse=True)
         elif (get_sort == "jumlah_unduh"):
             lst_materi = lst_materi.annotate(count=Count('unduh__id')).order_by('-count')
         elif (get_sort == "jumlah_tampilan"):
diff --git a/app/templates/app/katalog_materi.html b/app/templates/app/katalog_materi.html
index 62736dfe989318cc88f0b122fb4ca01e737cc96a..9309d5d8bbe9223685f91ffb7169f08256718193 100644
--- a/app/templates/app/katalog_materi.html
+++ b/app/templates/app/katalog_materi.html
@@ -134,6 +134,9 @@
                                         <li>
                                             <a href="?sort=terpopuler">terpopuler</a>
                                         </li>
+                                        <li>
+                                            <a href="?sort=terhangat">terhangat</a>
+                                        </li>
                                         <li>
                                             <a href="?sort=judul">judul</a>
                                         </li>
diff --git a/app/tests.py b/app/tests.py
index d65e62e06fe7eda4938aa653c4da9a976b4b3100..79d25ec49eda35ade3c1db8746815e3017c0b7ce 100644
--- a/app/tests.py
+++ b/app/tests.py
@@ -6,6 +6,8 @@ import random
 import re
 import tempfile
 import time
+import itertools
+from django.test import override_settings
 from datetime import datetime
 from io import StringIO
 from time import sleep
@@ -27,6 +29,7 @@ from django.core.files import File
 from django.core.files.uploadedfile import SimpleUploadedFile
 from django.core.management import call_command
 from django.db.utils import IntegrityError
+from pytz import timezone, UTC
 from django.test import (Client, RequestFactory, TestCase, TransactionTestCase,
                          override_settings)
 from django.urls import resolve, reverse
@@ -207,6 +210,163 @@ class DaftarKatalogSortingByJumlahTampilanTest(TestCase):
         response = self.client.get("/?sort=jumlah_tampilan")
         self.assertRegex(str(response.content), rf'.*Materi 2.*Materi 1.*')
 
+
+class DaftarKatalogSortingByTerhangatTest(TestCase):
+    @classmethod
+    def generate_view_materi(cls, materi, view_count):
+        for _ in itertools.repeat(None, view_count):
+            ViewStatistics.objects.create(materi=materi)
+
+    def get_displayed_materi_in_number(self):
+        response = self.client.get("/?sort=terhangat")
+        lst = [int(id) for id in re.findall(r"Materi\s(\d+)[^\d]", 
+                                            response.content.decode())]
+        return lst
+
+    def setUp(self):
+        self.contributor_credential = {
+            "email": "kontributor@gov.id",
+            "password": id_generator()
+        }
+        self.contributor = get_user_model().objects.create_user(
+            name="Kontributor",
+            is_contributor=True,
+            **self.contributor_credential, 
+        )
+        self.client = Client()
+        content = b"Test file"
+        self.cover = SimpleUploadedFile(
+            "cover.jpg",
+            content
+        )
+        self.content = SimpleUploadedFile(
+            "content.txt",
+            content
+        )
+
+        self.materi_data = {
+            "author": "Reyhan",
+            "uploader": self.contributor,
+            "publisher": "Publisher",
+            "descriptions": "Deskripsi Materi",
+            "status": "APPROVE",
+            "cover": self.cover,
+            "content": self.content,
+        }
+
+    def test_1_week_difference_give_1_hot_score_difference(self):
+        materi1 = Materi.objects.create(
+            title='Materi 1',
+            date_created=datetime(2020, 10, 1, 7, 0, 0, tzinfo=UTC),
+            **self.materi_data
+        )
+        materi2 = Materi.objects.create(
+            title='Materi 2',
+            date_created=datetime(2020, 10, 8, 7, 0, 0, tzinfo=UTC),
+            **self.materi_data
+        )
+        materi3 = Materi.objects.create(
+            title='Materi 3',
+            date_created=datetime(2020, 10, 15, 7, 0, 0, tzinfo=UTC),
+            **self.materi_data
+        )
+        self.generate_view_materi(materi1, 1)
+        self.generate_view_materi(materi2, 1)
+        self.generate_view_materi(materi3, 1)
+
+        self.assertAlmostEqual(materi3.hot_score - materi2.hot_score, 1)
+        self.assertAlmostEqual(materi2.hot_score - materi1.hot_score, 1)
+
+    def test_10_exponential_view_count_difference_give_1_hot_score_difference(self):
+        materi1 = Materi.objects.create(
+            title='Materi 1',
+            date_created=datetime(2020, 10, 1, 7, 0, 0, tzinfo=UTC),
+            **self.materi_data
+        )
+        materi2 = Materi.objects.create(
+            title='Materi 2',
+            date_created=datetime(2020, 10, 1, 7, 0, 0, tzinfo=UTC),
+            **self.materi_data
+        )
+        materi3 = Materi.objects.create(
+            title='Materi 3',
+            date_created=datetime(2020, 10, 1, 7, 0, 0, tzinfo=UTC),
+            **self.materi_data
+        )
+        self.generate_view_materi(materi1, 1)
+        self.generate_view_materi(materi2, 10)
+        self.generate_view_materi(materi3, 100)
+
+        self.assertAlmostEqual(materi3.hot_score - materi2.hot_score, 1)
+        self.assertAlmostEqual(materi2.hot_score - materi1.hot_score, 1)
+
+    def test_0_and_1_views_has_the_same_hot_score(self):
+        materi1 = Materi.objects.create(
+            title='Materi 1',
+            date_created=datetime(2020, 10, 1, 7, 0, 0, tzinfo=UTC),
+            **self.materi_data
+        )
+        materi2 = Materi.objects.create(
+            title='Materi 2',
+            date_created=datetime(2020, 10, 1, 7, 0, 0, tzinfo=UTC),
+            **self.materi_data
+        )
+        self.generate_view_materi(materi1, 0)
+        self.generate_view_materi(materi2, 1)
+
+        self.assertAlmostEqual(materi1.hot_score, materi2.hot_score)
+        
+    def test_page_has_option_sort_by_hottest(self):
+        response = self.client.get("/")
+        self.assertIn("terhangat", response.content.decode())
+
+    def test_page_display_sort_by_hottest(self):
+        materi1 = Materi.objects.create(
+            title='Materi 1',
+            date_created=datetime(2020, 10, 1, 7, 0, 0, tzinfo=UTC),
+            **self.materi_data
+        )
+        materi2 = Materi.objects.create(
+            title='Materi 2',
+            date_created=datetime(2020, 10, 1, 7, 0, 0, tzinfo=UTC),
+            **self.materi_data
+        )
+        materi3 = Materi.objects.create(
+            title='Materi 3',
+            date_created=datetime(2020, 10, 8, 7, 0, 0, tzinfo=UTC),
+            **self.materi_data
+        )
+        materi4 = Materi.objects.create(
+            title='Materi 4',
+            date_created=datetime(2020, 10, 9, 7, 0, 0, tzinfo=UTC),
+            **self.materi_data
+        )
+        self.generate_view_materi(materi1, 11)
+        self.generate_view_materi(materi2, 10)
+        self.generate_view_materi(materi3, 1)
+        self.generate_view_materi(materi4, 1)
+
+        lst = self.get_displayed_materi_in_number()
+        self.assertEqual(lst, [4, 1, 3, 2])
+
+    def test_prefer_newest_materi_if_hot_score_is_same(self):
+        materi1 = Materi.objects.create(
+            title='Materi 1',
+            date_created=datetime(2020, 10, 1, 7, 0, 0, tzinfo=UTC),
+            **self.materi_data
+        )
+        materi2 = Materi.objects.create(
+            title='Materi 2',
+            date_created=datetime(2020, 10, 8, 7, 0, 0, tzinfo=UTC),
+            **self.materi_data
+        )
+        self.generate_view_materi(materi1, 10)
+        self.generate_view_materi(materi2, 1)
+
+        lst = self.get_displayed_materi_in_number()
+        self.assertEqual(lst, [2, 1])
+
+
 class DaftarKatalogSortingByJumlahKomentarTest(TestCase):
     def setUp(self):
         self.client = Client()
@@ -3465,7 +3625,7 @@ class MateriRecommendationTest(TestCase):
         response = Client().get("/?recommendation=1")
         list = [int(id) for id in re.findall(r"Materi\s(\d+)[^\d]", response.content.decode())]
         self.assertEqual(list, [1, 2])
-    
+
 class BacaNantiTest(TestCase):
     def setUp(self):
         self.contributor_credential = {
diff --git a/digipus/__pycache__/settings.cpython-36.pyc b/digipus/__pycache__/settings.cpython-36.pyc
index 43d2d1c4169e7113ebfa287197b9840fd637a652..2e8d5505327c65aad19bcc06e556bd3f737fca54 100644
Binary files a/digipus/__pycache__/settings.cpython-36.pyc and b/digipus/__pycache__/settings.cpython-36.pyc differ