From ec28be65564a04a2d07f1d7276ed6a09fe115b42 Mon Sep 17 00:00:00 2001
From: SAMUEL TUPA FEBRIAN <samuel.tupa@ui.ac.id>
Date: Thu, 29 Oct 2020 18:58:51 +0700
Subject: [PATCH] [#107] Material Statistic: Download Count

---
 app/migrations/0025_review.py        |  30 +++++++
 app/services.py                      |   4 +
 app/templates/app/detail_materi.html |   8 ++
 app/tests.py                         | 124 ++++++++++++++++++++++++++-
 app/views.py                         |   1 +
 5 files changed, 163 insertions(+), 4 deletions(-)
 create mode 100644 app/migrations/0025_review.py

diff --git a/app/migrations/0025_review.py b/app/migrations/0025_review.py
new file mode 100644
index 0000000..79b7b89
--- /dev/null
+++ b/app/migrations/0025_review.py
@@ -0,0 +1,30 @@
+# Generated by Django 3.1 on 2020-10-29 11:22
+
+import app.models
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+import django.utils.timezone
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+        ('app', '0024_merge_20201026_0812'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='Review',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('username', models.CharField(max_length=100)),
+                ('profile', models.CharField(default=app.models.getRandomColor, max_length=100)),
+                ('review', models.TextField(default='review')),
+                ('timestamp', models.DateTimeField(default=django.utils.timezone.now)),
+                ('materi', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='app.materi')),
+                ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)),
+            ],
+        ),
+    ]
diff --git a/app/services.py b/app/services.py
index 538d8f9..d83bf00 100644
--- a/app/services.py
+++ b/app/services.py
@@ -134,6 +134,10 @@ class DetailMateriService:
             user_name = request.user.name
         return user_name
 
+    @staticmethod
+    def init_materi_download_count(context, materi):
+        context["materi_download_count"] = materi.unduh.all().count()
+
 class CitationService:
 
     @staticmethod
diff --git a/app/templates/app/detail_materi.html b/app/templates/app/detail_materi.html
index fb1b1d0..278c5de 100644
--- a/app/templates/app/detail_materi.html
+++ b/app/templates/app/detail_materi.html
@@ -164,6 +164,14 @@ div.review {
                         <p class="info-content">{{materi_data.content.size|filesizeformat}}</p>
                     </dd>
                 </div>
+                <div class="info" id="1">
+                    <dl class="col col-4">
+                        <dt class="info-name">Jumlah Download</dt>
+                    </dl>
+                    <dd>
+                        <p class="info-content">{{materi_download_count}}</p>
+                    </dd>
+                </div>
             </div>
             <div class="buttons d-flex flex-row bd-highlight mb-1">
                 <a href="{% url 'view-materi' materi_data.id %}"
diff --git a/app/tests.py b/app/tests.py
index 6279d54..32e9147 100644
--- a/app/tests.py
+++ b/app/tests.py
@@ -1,6 +1,7 @@
 import json, tempfile, os, mock, base64
 import pandas as pd
 from io import StringIO
+import re
 import time
 from django.test import override_settings
 
@@ -42,6 +43,10 @@ from .models import (
     ViewStatistics,
 )
 
+from .services import (
+    DetailMateriService,
+)
+
 from .views import (
     DaftarKatalog,
     DashboardKontributorView,
@@ -277,6 +282,20 @@ class DaftarKatalogPerKontributorTest(TestCase):
 
 
 class DetailMateriTest(TestCase):
+    def _get_materi_info_html(self, info_name, info_value):
+        info_html = '<div class="info" id="1"><dl class="col col-4">'
+        info_html += f'<dt class="info-name">{info_name}</dt>' + '</dl><dd>'
+        info_html += f'<p class="info-content">{info_value}</p>' + '</dd></div>'
+        return info_html
+
+    def check_materi_info_in_html(self, info_name, info_value, html_content):
+        expected_content = self._get_materi_info_html(info_name, info_value)
+        self.assertIn(expected_content, re.sub(">\s*<","><", html_content))
+
+    def check_materi_info_not_in_html(self, info_name, info_value, html_content):
+        expected_content = self._get_materi_info_html(info_name, info_value)
+        self.assertNotIn(expected_content, re.sub(">\s*<","><", html_content))
+
     def setUp(self):
         self.client = Client()
         self.admin_credential = {
@@ -302,11 +321,19 @@ class DetailMateriTest(TestCase):
             "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()
-        self.materi1 = Materi.objects.first()
+        self.materi1 = 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)
+        self.materi2 = Materi.objects.create(title="Materi 2", author="Agad", 
+                        uploader=self.contributor, publisher="Kelas SM", 
+                        descriptions="Deskripsi Materi 2", status="APPROVE", 
+                        cover=self.cover, content=self.content)
         self.url = "/materi/" + str(self.materi1.id) + "/"
+        self.download_url1 = self.url + "unduh"
+        self.url2 = "/materi/" + str(self.materi2.id) + "/"
+        self.download_url2 = self.url2 + "unduh"
+        self.dcount_info_name = "Jumlah Download"
 
         self.materi_with_published_date = Materi.objects.create(title="Materi 1", author="Agas", uploader=self.contributor,
                                                  publisher="Kelas SC", descriptions="Deskripsi Materi 1",
@@ -698,6 +725,95 @@ class DetailMateriTest(TestCase):
         self.assertEqual(last_url, '/materi/%d/' % self.materi1.id)
         self.assertEqual(status_code, 302)
 
+    def test_download_count_is_in_init_context(self):
+        context = {}
+        DetailMateriService.init_materi_download_count(context, self.materi1)
+        self.assertIn('materi_download_count', context.keys())
+
+    def test_download_count_is_integer(self):
+        context = {}
+        DetailMateriService.init_materi_download_count(context, self.materi1)
+        self.assertEqual(type(context['materi_download_count']), int)
+
+    def test_download_count_when_no_download(self):
+        context = {}
+        DetailMateriService.init_materi_download_count(context, self.materi1)
+        self.assertEqual(context['materi_download_count'], 0)
+
+    def test_download_count_when_single_download(self):
+        self.client.get(self.download_url1)
+        context = {}
+        DetailMateriService.init_materi_download_count(context, self.materi1)
+        self.assertEqual(context['materi_download_count'], 1)
+
+    def test_download_count_when_multiple_download(self):
+        self.client.get(self.download_url1)
+        self.client.get(self.download_url1)
+        self.client.get(self.download_url1)
+        context = {}
+        DetailMateriService.init_materi_download_count(context, self.materi1)
+        self.assertEqual(context['materi_download_count'], 3)
+
+    def test_different_material_has_different_download_count(self):
+        self.client.get(self.download_url1)
+        self.client.get(self.download_url1)
+        self.client.get(self.download_url1)
+
+        self.client.get(self.download_url2)
+        self.client.get(self.download_url2)
+
+        context1 = {}
+        context2 = {}
+        DetailMateriService.init_materi_download_count(context1, self.materi1)
+        DetailMateriService.init_materi_download_count(context2, self.materi2)
+        self.assertNotEqual(context1['materi_download_count'], context2['materi_download_count'])
+        self.assertEqual(context1['materi_download_count'], 3)
+        self.assertEqual(context2['materi_download_count'], 2)
+    
+    def test_download_count_displayed_on_template_when_no_download(self):
+        response = self.client.get(self.url)
+        html = response.content.decode("utf-8")
+        self.check_materi_info_in_html(self.dcount_info_name, 0, html)
+
+    def test_download_count_displayed_on_template_when_single_download(self):
+        self.client.get(self.download_url1)
+
+        response = self.client.get(self.url)
+        html = response.content.decode("utf-8")
+        self.check_materi_info_in_html(self.dcount_info_name, 1, html)
+
+    def test_download_count_displayed_on_template_when_multiple_download(self):
+        self.client.get(self.download_url1)
+        self.client.get(self.download_url1)
+        self.client.get(self.download_url1)
+        self.client.get(self.download_url1)
+
+        response = self.client.get(self.url)
+        html = response.content.decode("utf-8")
+        self.check_materi_info_in_html(self.dcount_info_name, 4, html)
+
+    def test_different_material_has_different_download_count_on_templates(self):
+        self.client.get(self.download_url1)
+        self.client.get(self.download_url1)
+
+        self.client.get(self.download_url2)
+        self.client.get(self.download_url2)
+        self.client.get(self.download_url2)
+        self.client.get(self.download_url2)
+
+        response = self.client.get(self.url)
+        response2 = self.client.get(self.url2)
+
+        html = response.content.decode("utf-8")
+        html2 = response2.content.decode("utf-8")
+
+        dcount_materi1 = self.materi1.unduh.all().count()
+        dcount_materi2 = self.materi2.unduh.all().count()
+
+        self.check_materi_info_in_html(self.dcount_info_name, dcount_materi1, html)
+        self.check_materi_info_not_in_html(self.dcount_info_name, dcount_materi2, html)
+        self.check_materi_info_in_html(self.dcount_info_name, dcount_materi2, html2)
+        self.check_materi_info_not_in_html(self.dcount_info_name, dcount_materi1, html2)
 
 class PostsViewTest(TestCase):
 
diff --git a/app/views.py b/app/views.py
index 05a15a9..94a3095 100644
--- a/app/views.py
+++ b/app/views.py
@@ -112,6 +112,7 @@ class DetailMateri(TemplateView):
         DetailMateriService.init_context_data(context, materi, self.request.session)
         published_date = DetailMateriService.set_published_date(materi)
         DetailMateriService.init_citation_and_materi_rating(context, materi, published_date, self.request)
+        DetailMateriService.init_materi_download_count(context, materi)
 
         if self.request.user.is_authenticated:
             materi_rating = Rating.objects.filter(materi=materi, user=self.request.user).first()
-- 
GitLab