Fakultas Ilmu Komputer UI

Commit 40ea5598 authored by Mutia Rahmatun Husna's avatar Mutia Rahmatun Husna
Browse files

[#39]Material: Like/Favorite (User View)

parent 9eb16994
...@@ -100,6 +100,12 @@ class Materi(models.Model): ...@@ -100,6 +100,12 @@ class Materi(models.Model):
count = Like.objects.filter(materi=self).count() count = Like.objects.filter(materi=self).count()
return count return count
@property
def is_like(self):
like = False
if Like.objects.filter(materi=self).exists():
like = True
class Comment(models.Model): class Comment(models.Model):
username = models.CharField(max_length=100) username = models.CharField(max_length=100)
......
...@@ -31,6 +31,11 @@ ...@@ -31,6 +31,11 @@
<span>Statisik Materi</span> <span>Statisik Materi</span>
</a> </a>
</li> </li>
<li class="nav-item">
<a class="nav-link" href="{% url 'favorite' %}">
<span>Favorite Materi</span>
</a>
</li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="{% url 'posts' %}"> <a class="nav-link" href="{% url 'posts' %}">
<span>Materi Diunggah</span></a> <span>Materi Diunggah</span></a>
......
{% 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 %}
...@@ -14,7 +14,7 @@ from django.core.exceptions import PermissionDenied, ValidationError ...@@ -14,7 +14,7 @@ from django.core.exceptions import PermissionDenied, ValidationError
from django.core.files.uploadedfile import SimpleUploadedFile from django.core.files.uploadedfile import SimpleUploadedFile
from django.core.management import call_command from django.core.management import call_command
from django.test import Client, TestCase, TransactionTestCase 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.db.utils import IntegrityError
from django.test import Client, RequestFactory, TestCase, TransactionTestCase from django.test import Client, RequestFactory, TestCase, TransactionTestCase
from pytz import timezone from pytz import timezone
...@@ -58,6 +58,7 @@ from .views import ( ...@@ -58,6 +58,7 @@ from .views import (
UploadMateriExcelView, UploadMateriExcelView,
PasswordChangeViews, PasswordChangeViews,
password_success, password_success,
MateriFavorite,
) )
from app.forms import SuntingProfilForm, year_choices from app.forms import SuntingProfilForm, year_choices
from app.utils.fileManagementUtil import get_random_filename, remove_image_exifdata from app.utils.fileManagementUtil import get_random_filename, remove_image_exifdata
...@@ -72,6 +73,7 @@ from webdriver_manager.chrome import ChromeDriverManager ...@@ -72,6 +73,7 @@ from webdriver_manager.chrome import ChromeDriverManager
from selenium.common.exceptions import NoSuchElementException from selenium.common.exceptions import NoSuchElementException
class DaftarKatalogTest(TestCase): class DaftarKatalogTest(TestCase):
def test_daftar_katalog_url_exist(self): def test_daftar_katalog_url_exist(self):
url = "/" url = "/"
...@@ -187,50 +189,34 @@ class DaftarKatalogPerKontributorTest(TestCase): ...@@ -187,50 +189,34 @@ class DaftarKatalogPerKontributorTest(TestCase):
def setUp(self): def setUp(self):
self.client = Client() 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 = 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.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") self.content = SimpleUploadedFile("Bahan_PA_RKK.pdf", b"Test file")
Materi( Materi(title="Materi 1", author="Agas", uploader=self.contributor,
title="Materi 1", publisher="Kelas SC", descriptions="Deskripsi Materi 1",
author="Agas", status="APPROVE", cover=self.cover, content=self.content).save()
uploader=self.contributor, Materi(title="Materi 2", author="Agas", uploader=self.contributor,
publisher="Kelas SC", publisher="Kelas SC", descriptions="Deskripsi Materi 2",
descriptions="Deskripsi Materi 1", status="APPROVE", cover=self.cover, content=self.content).save()
status="APPROVE", Materi(title="Materi 3", author="Agas", uploader=self.contributor2,
cover=self.cover, publisher="Kelas SC", descriptions="Deskripsi Materi 3",
content=self.content, status="APPROVE", cover=self.cover, content=self.content).save()
).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}/" self.url = f"/profil/{self.contributor.email}/"
...@@ -257,51 +243,44 @@ class DaftarKatalogPerKontributorTest(TestCase): ...@@ -257,51 +243,44 @@ class DaftarKatalogPerKontributorTest(TestCase):
class DetailMateriTest(TestCase): class DetailMateriTest(TestCase):
def setUp(self): def setUp(self):
self.client = Client() self.client = Client()
self.admin_credential = {"email": "admin@gov.id", "password": "passwordtest"} self.admin_credential = {
self.contributor_credential = {"email": "kontributor@gov.id", "password": "passwordtest"} "email": "admin@gov.id",
self.anonymous_credential = {"email": "anonymous@gov.id", "password": "passwordtest"} "password": "passwordtest"
self.admin = get_user_model().objects.create_user(**self.admin_credential, name="Admin", is_admin=True) }
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 = 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(
self.cover = SimpleUploadedFile("ExampleCover921.jpg", b"Test file") "ExampleCover921.jpg", b"Test file")
self.content = SimpleUploadedFile("ExampleFile921.pdf", b"Test file") self.content = SimpleUploadedFile("ExampleFile921.pdf", b"Test file")
Materi( Materi(title="Materi 1", author="Agas", uploader=self.contributor,
title="Materi 1", publisher="Kelas SC", descriptions="Deskripsi Materi 1",
author="Agas", status="APPROVE", cover=self.cover, content=self.content).save()
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.first()
self.url = "/materi/" + str(self.materi1.id) + "/" self.url = "/materi/" + str(self.materi1.id) + "/"
self.materi_with_published_date = Materi.objects.create( self.materi_with_published_date = Materi.objects.create(title="Materi 1", author="Agas", uploader=self.contributor,
title="Materi 1", publisher="Kelas SC", descriptions="Deskripsi Materi 1",
author="Agas", status="APPROVE", cover=self.cover, content=self.content,
uploader=self.contributor, date_modified=datetime.now(), date_created=datetime.now())
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) + "/" self.materi_with_published_date_url = "/materi/" + str(self.materi_with_published_date.id) + "/"
VerificationReport.objects.create( VerificationReport.objects.create(report='{"feedback": "Something", "kriteria": [{"title": "Kriteria 1", "status": true},' + \
report='{"feedback": "Something", "kriteria": [{"title": "Kriteria 1", "status": true},' ' {"title": "Kriteria 2", "status": true}, {"title": "Kriteria 3", "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, \
timestamp="2020-10-09 06:21:33", user=self.materi_with_published_date.uploader)
status="Diterima",
materi=self.materi_with_published_date,
user=self.materi_with_published_date.uploader,
)
def test_detail_materi_url_exist(self): def test_detail_materi_url_exist(self):
response = Client().get(self.url) response = Client().get(self.url)
...@@ -317,7 +296,8 @@ class DetailMateriTest(TestCase): ...@@ -317,7 +296,8 @@ class DetailMateriTest(TestCase):
self.assertEqual(found.func.__name__, DetailMateri.as_view().__name__) self.assertEqual(found.func.__name__, DetailMateri.as_view().__name__)
def test_category_models_can_create_new_object(self): 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() countData = Category.objects.all().count()
self.assertEqual(1, countData) self.assertEqual(1, countData)
self.assertNotEqual(0, countData) self.assertNotEqual(0, countData)
...@@ -325,7 +305,8 @@ class DetailMateriTest(TestCase): ...@@ -325,7 +305,8 @@ class DetailMateriTest(TestCase):
self.assertNotEqual(test.__str__(), "saul") self.assertNotEqual(test.__str__(), "saul")
def test_comment_models_can_create_new_object(self): 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() countData = Comment.objects.all().count()
self.assertEqual(1, countData) self.assertEqual(1, countData)
self.assertNotEqual(0, countData) self.assertNotEqual(0, countData)
...@@ -337,7 +318,8 @@ class DetailMateriTest(TestCase): ...@@ -337,7 +318,8 @@ class DetailMateriTest(TestCase):
self.client.login(**self.anonymous_credential) self.client.login(**self.anonymous_credential)
response = self.client.post(url, {"comment": ""}) response = self.client.post(url, {"comment": ""})
self.assertIn("error_message", response.context) 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): def test_comment_rendered_to_template(self):
url = self.url url = self.url
...@@ -356,7 +338,8 @@ class DetailMateriTest(TestCase): ...@@ -356,7 +338,8 @@ class DetailMateriTest(TestCase):
def test_comment_by_kontributor(self): def test_comment_by_kontributor(self):
url = self.url url = self.url
self.client.login(**self.contributor_credential) 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) response = self.client.get(url)
self.assertContains(response, "Kontributor") self.assertContains(response, "Kontributor")
...@@ -512,23 +495,25 @@ class DetailMateriTest(TestCase): ...@@ -512,23 +495,25 @@ class DetailMateriTest(TestCase):
def test_hasil_citasi_APA_materi_has_no_published_date(self): def test_hasil_citasi_APA_materi_has_no_published_date(self):
response = self.client.get(self.url) 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"])
self.assertIn(expected,
response.context["citationAPA"])
def test_hasil_citasi_APA_materi_has_published_date(self): def test_hasil_citasi_APA_materi_has_published_date(self):
response = self.client.get(self.materi_with_published_date_url) 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") published_date = self.materi_with_published_date.published_date.strftime("%Y-%m-%d %H:%M")
expected = ( expected = (
self.materi_with_published_date.author self.materi_with_published_date.author
+ " . (" + " . ("
+ published_date + published_date
+ ") . " + ") . "
+ self.materi_with_published_date.title + self.materi_with_published_date.title
+ " . " + " . "
+ self.materi_with_published_date.publisher + self.materi_with_published_date.publisher
) )
self.assertIn(expected, response.context["citationAPA"]) self.assertIn(expected, response.context["citationAPA"])
def test_citation_IEEE_button(self): def test_citation_IEEE_button(self):
response = self.client.get(self.url) response = self.client.get(self.url)
self.assertContains(response, "Citate IEEE") self.assertContains(response, "Citate IEEE")
...@@ -540,19 +525,19 @@ class DetailMateriTest(TestCase): ...@@ -540,19 +525,19 @@ class DetailMateriTest(TestCase):
current_month = current_date.strftime("%b") current_month = current_date.strftime("%b")
current_year = str(current_date.year) current_year = str(current_date.year)
expected = ( expected = (
"Agas, " "Agas, "
+ "Materi 1. " + "Materi 1. "
+ "Kelas SC, n.d. " + "Kelas SC, n.d. "
+ "Accessed on: " + "Accessed on: "
+ current_month + current_month
+ ". " + ". "
+ current_day + current_day
+ ", " + ", "
+ current_year + current_year
+ ". [Online]. " + ". [Online]. "
+ "Available: http://testserver" + "Available: http://testserver"
+ self.url + self.url
) )
self.assertIn(expected, response.context["citationIEEE"]) self.assertIn(expected, response.context["citationIEEE"])
...@@ -562,45 +547,37 @@ class DetailMateriTest(TestCase): ...@@ -562,45 +547,37 @@ class DetailMateriTest(TestCase):
current_day = str(current_date.day) current_day = str(current_date.day)
current_month = current_date.strftime("%b") current_month = current_date.strftime("%b")
current_year = str(current_date.year) current_year = str(current_date.year)
published_date = self.materi_with_published_date.published_date.strftime("%Y") published_date = self.materi_with_published_date.published_date.strftime('%Y')
expected = ( expected = "Agas, " + \
"Agas, " "Materi 1. " + \
+ "Materi 1. " "Kelas SC, " + published_date + ". " +\
+ "Kelas SC, " "Accessed on: " + current_month + ". " + current_day + ", " + current_year + \
+ published_date ". [Online]. " + \
+ ". " "Available: http://testserver" + self.materi_with_published_date_url
+ "Accessed on: "
+ current_month
+ ". "
+ current_day
+ ", "
+ current_year
+ ". [Online]. "
+ "Available: http://testserver"
+ self.materi_with_published_date_url
)
self.assertIn(expected, response.context["citationIEEE"]) self.assertIn(expected, response.context["citationIEEE"])
def test_tombol_bagikan_google_drive(self): def test_tombol_bagikan_google_drive(self):
response = Client().get(self.url) response = Client().get(self.url)
self.assertContains(response, "Google Drive") self.assertContains(response, 'Google Drive')
@mock.patch("app.services.GoogleDriveUploadService.upload_to_gdrive") @mock.patch("app.services.GoogleDriveUploadService.upload_to_gdrive")
def test_save_to_gdrive_with_nonexistent_materi(self, mock_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() mock_upload_to_gdrive.assert_not_called()
self.assertEqual(response.status_code, 404) self.assertEqual(response.status_code, 404)
@mock.patch("app.services.GoogleDriveUploadService.upload_to_gdrive") @mock.patch("app.services.GoogleDriveUploadService.upload_to_gdrive")
def test_save_to_gdrive_with_valid_materi(self, mock_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] last_url, status_code = response.redirect_chain[-1]
mock_upload_to_gdrive.assert_called_once() 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) self.assertEqual(status_code, 302)
class PostsViewTest(TestCase): class PostsViewTest(TestCase):
@classmethod @classmethod
def generate_posts_data(cls, user): def generate_posts_data(cls, user):
POST_COUNT = 3 POST_COUNT = 3
...@@ -614,7 +591,11 @@ class PostsViewTest(TestCase): ...@@ -614,7 +591,11 @@ class PostsViewTest(TestCase):
post_comment_group_dict = {} post_comment_group_dict = {}
for i in range(POST_COUNT): 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.categories.add(sample_category)
post_comment_group_dict[post.id] = { post_comment_group_dict[post.id] = {
...@@ -622,11 +603,13 @@ class PostsViewTest(TestCase): ...@@ -622,11 +603,13 @@ class PostsViewTest(TestCase):
"comments": [], "comments": [],
} }
for j in range(LIKES_COUNT_PER_POST[i]): for j in range (LIKES_COUNT_PER_POST[i]):
Like.objects.create( 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): for i, post_id in enumerate(post_comment_group_dict):
post = post_comment_group_dict[post_id]["data"] post = post_comment_group_dict[post_id]["data"]
...@@ -641,8 +624,11 @@ class PostsViewTest(TestCase): ...@@ -641,8 +624,11 @@ class PostsViewTest(TestCase):
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
cls.url = "/posts/" cls.url = '/posts/'
cls.user_credentials = {"email": "user@email.com", "password": "justpass"} cls.user_credentials = {
"email": "user@email.com",
"password": "justpass"
}
cls.user = User.objects.create_user(**cls.user_credentials, is_contributor=True) cls.user = User.objects.create_user(**cls.user_credentials, is_contributor=True)
cls.data = cls.generate_posts_data(cls.user) cls.data = cls.generate_posts_data(cls.user)
...@@ -686,17 +672,20 @@ class PostsViewTest(TestCase): ...@@ -686,17 +672,20 @@ class PostsViewTest(TestCase):
response = self._request_as_user() response = self._request_as_user()
posts = list(self.data.keys()) 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( self.assertRegex(
str(response.content), str(response.content),
rf'.*(<div id="post-{posts[2]}">)' 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][0]}">)' + \
+ rf'.*(<div id="post-{posts[2]}-comment-{comments[2][1]}">)' 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[2]}-comment-{comments[2][2]}">)' + \
+ rf'.*(<div id="post-{posts[1]}">)' rf'.*(<div id="post-{posts[1]}">)' + \
+ rf'.*(<div id="post-{posts[0]}">)'