Fakultas Ilmu Komputer UI

Commit ceacaca2 authored by Alfian Fuadi Rafli's avatar Alfian Fuadi Rafli
Browse files

[#51] Material: Search/Query By Title

parent 7989fff1
# Generated by Django 3.1 on 2020-10-09 11:19
import django.contrib.postgres.search
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('app', '0018_merge_20201009_0700'),
]
operations = [
migrations.AddField(
model_name='materi',
name='_search_vector',
field=django.contrib.postgres.search.SearchVectorField(editable=False, null=True),
),
]
# Generated by Django 3.1 on 2020-10-09 13:39
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('app', '0019_materi__search_vector'),
('app', '0019_auto_20201009_1829'),
]
operations = [
]
import random
from django.contrib.postgres import search
from django.core.exceptions import ValidationError
from django.core.validators import MinValueValidator, MaxValueValidator
from django.db import models
......@@ -34,35 +35,59 @@ class Category(models.Model):
return self.name
class MateriManager(models.Manager):
def search(self, search_text):
search_vector = search.SearchVector("title", weight="A")
search_query = search.SearchQuery(search_text)
search_rank = search.SearchRank(search_vector, search_query)
search_result = (
self.get_queryset().filter(_search_vector=search_query).annotate(rank=search_rank).order_by("-rank")
)
return search_result
class Materi(models.Model):
cover = models.ImageField()
content = models.FileField()
title = models.CharField(max_length=50, default='Judul')
author = models.CharField(max_length=30, default='Penyusun')
title = models.CharField(max_length=50, default="Judul")
author = models.CharField(max_length=30, default="Penyusun")
uploader = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
publisher = models.CharField(max_length=30, default="Penerbit")
pages = models.IntegerField(default=0)
descriptions = models.TextField(default="Deskripsi")
status = models.CharField(
max_length=30, choices=VERIFICATION_STATUS, default=VERIFICATION_STATUS[0][0])
status = models.CharField(max_length=30, choices=VERIFICATION_STATUS, default=VERIFICATION_STATUS[0][0])
categories = models.ManyToManyField(Category)
date_created = models.DateTimeField(default=timezone.now)
date_modified = models.DateTimeField(auto_now=True)
_search_vector = search.SearchVectorField(null=True, editable=False)
objects = MateriManager()
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
if "update_fields" not in kwargs or "_search_vector" not in kwargs["update_fields"]:
self._search_vector = search.SearchVector("title", weight="A")
self.save(update_fields=["_search_vector"])
@property
def is_published(self):
published = False
if self.verificationreport_set.exists():
report = self.verificationreport_set.latest('timestamp')
published = True if report.status == 'Diterima' else False
report = self.verificationreport_set.latest("timestamp")
published = True if report.status == "Diterima" else False
return published
@property
def published_date(self):
published_date = None
if self.verificationreport_set.exists():
report = self.verificationreport_set.latest('timestamp')
if report.status == 'Diterima':
report = self.verificationreport_set.latest("timestamp")
if report.status == "Diterima":
published_date = report.timestamp
return published_date
......@@ -71,13 +96,13 @@ class Materi(models.Model):
count = Like.objects.filter(materi=self).count()
return count
class Comment(models.Model):
username = models.CharField(max_length=100)
profile = models.CharField(max_length=100, default=getRandomColor)
comment = models.CharField(max_length=240, default="comments")
materi = models.ForeignKey(Materi, models.SET_NULL, null=True)
user = models.ForeignKey(
User, on_delete=models.SET_NULL, blank=True, null=True)
user = models.ForeignKey(User, on_delete=models.SET_NULL, blank=True, null=True)
timestamp = models.DateTimeField(default=timezone.now)
def __str__(self):
......@@ -89,22 +114,20 @@ class Like(models.Model):
timestamp = models.DateTimeField(default=timezone.now)
session_id = models.CharField(max_length=32, blank=False)
class ReqMaterial(models.Model):
title = models.CharField(max_length=100)
timestamp = models.DateTimeField(default=timezone.now)
class ViewStatistics(models.Model):
materi = models.ForeignKey(
Materi, models.SET_NULL, null=True, related_name="baca")
materi = models.ForeignKey(Materi, models.SET_NULL, null=True, related_name="baca")
timestamp = models.DateTimeField(default=timezone.now)
class DownloadStatistics(models.Model):
materi = models.ForeignKey(
Materi, models.SET_NULL, null=True, related_name="unduh")
downloader = models.ForeignKey(
User, models.SET_NULL, blank=True, null=True, related_name="riwayat_unduh")
materi = models.ForeignKey(Materi, models.SET_NULL, null=True, related_name="unduh")
downloader = models.ForeignKey(User, models.SET_NULL, blank=True, null=True, related_name="riwayat_unduh")
timestamp = models.DateTimeField(default=timezone.now)
......@@ -154,6 +177,7 @@ class RatingContributor(models.Model):
else:
raise ValidationError("Rating score must be integer between 1-5")
class LaporanMateri(models.Model):
materi = models.ForeignKey(Materi, on_delete=models.CASCADE, max_length=120)
user = models.ForeignKey(User, on_delete=models.CASCADE)
......
......@@ -12,10 +12,8 @@ from django.core.files import File
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, reverse
from django.db.utils import IntegrityError
from django.urls import resolve
from django.test import Client, RequestFactory, TestCase, TransactionTestCase
from pytz import timezone
from time import sleep
......@@ -25,19 +23,39 @@ from administration.utils import id_generator
from app.views import UploadMateriHTML, add_rating_materi
from authentication.models import User
from digipus.settings import TIME_ZONE
from .models import (Category, Comment, DownloadStatistics, Materi, Like,
Rating, ReqMaterial, RatingContributor, ViewStatistics)
from .views import (DaftarKatalog, DashboardKontributorView, DetailMateri,
ProfilKontributorView, SuksesLoginAdminView,
SuksesLoginKontributorView, SuntingProfilView,
ProfilAdminView, PostsView, SuntingProfilAdminView,
RevisiMateriView, ReqMateriView, KatalogPerKontributorView,
UploadMateriView, UploadMateriExcelView)
from .models import (
Category,
Comment,
DownloadStatistics,
Materi,
Like,
Rating,
ReqMaterial,
RatingContributor,
ViewStatistics,
)
from .views import (
DaftarKatalog,
DashboardKontributorView,
DetailMateri,
ProfilKontributorView,
SuksesLoginAdminView,
SuksesLoginKontributorView,
SuntingProfilView,
ProfilAdminView,
PostsView,
SuntingProfilAdminView,
RevisiMateriView,
ReqMateriView,
KatalogPerKontributorView,
UploadMateriView,
UploadMateriExcelView,
)
from app.forms import SuntingProfilForm
from app.utils.fileManagementUtil import get_random_filename, remove_image_exifdata
ERROR_403_MESSAGE = 'Kamu harus login untuk mengakses halaman ini'
ERROR_403_MESSAGE = "Kamu harus login untuk mengakses halaman ini"
from django.test import LiveServerTestCase
from selenium import webdriver
......@@ -47,7 +65,6 @@ from webdriver_manager.chrome import ChromeDriverManager
from selenium.common.exceptions import NoSuchElementException
class DaftarKatalogTest(TestCase):
def test_daftar_katalog_url_exist(self):
url = "/"
......@@ -72,38 +89,102 @@ class DaftarKatalogTest(TestCase):
resp = Materi.objects.get(id=materi.id)
self.assertEqual(resp, materi)
def test_materi_model_generate_search_vector_after_save(self):
Materi(title="Eating book").save()
search_vector_new_materi = list(Materi.objects.values_list("_search_vector", flat=True))
expected_search_vector = ["'book':2A 'eat':1A"]
self.assertSequenceEqual(search_vector_new_materi, expected_search_vector)
def test_search_text_on_empty_database(self):
search_query = "test"
search_result = list(Materi.objects.search(search_query))
expected_search_result = []
self.assertSequenceEqual(search_result, expected_search_result)
def test_search_text_on_unmatched_data(self):
Materi(title="test satu sekarang").save()
Materi(title="test dua nanti").save()
search_query = "besok"
search_result = list(Materi.objects.search(search_query))
expected_search_result = []
self.assertSequenceEqual(search_result, expected_search_result)
def test_search_text_return_list_matched_by_rank(self):
materi_2 = Materi(title="ini lumayan cocok lumayan cocok")
materi_2.save()
materi_1 = Materi(title="ini sangat cocok sangat cocok sangat cocok")
materi_1.save()
materi_4 = Materi(title="ini tidak")
materi_4.save()
materi_3 = Materi(title="ini sedikit cocok")
materi_3.save()
search_query = "ini cocok"
search_result = list(Materi.objects.search(search_query))
expected_search_result = [materi_1, materi_2, materi_3]
self.assertSequenceEqual(search_result, expected_search_result)
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}/"
......@@ -123,51 +204,58 @@ class DaftarKatalogPerKontributorTest(TestCase):
response = self.client.get(self.url)
list_materi = Materi.objects.filter(uploader=self.contributor)
data = response.context_data['materi_list']
data = response.context_data["materi_list"]
self.assertEqual(len(list_materi), len(data))
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.anonymous = get_user_model().objects.create_user(
**self.anonymous_credential, name="Anonymous"
**self.contributor_credential, name="Kontributor", is_contributor=True
)
self.cover = SimpleUploadedFile(
"ExampleCover921.jpg", b"Test file")
self.anonymous = get_user_model().objects.create_user(**self.anonymous_credential, name="Anonymous")
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)
......@@ -176,15 +264,14 @@ class DetailMateriTest(TestCase):
def test_detail_materi_using_detail_materi_template(self):
response = Client().get(self.url)
self.assertTemplateUsed(response, 'app/detail_materi.html')
self.assertTemplateUsed(response, "app/detail_materi.html")
def test_detail_materi_using_detail_materi_func(self):
found = resolve(self.url)
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)
......@@ -192,8 +279,7 @@ 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)
......@@ -205,8 +291,7 @@ 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
......@@ -225,8 +310,7 @@ 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")
......@@ -245,38 +329,37 @@ class DetailMateriTest(TestCase):
def test_delete_comments_by_admin(self):
url = self.url
self.client.post(url, {"comment": "This is new comment by Anonymous"})
deleteURL = "/delete/" + str(self.materi1.id) + "/" + str(
Comment.objects.get(comment="This is new comment by Anonymous").id)
deleteURL = (
"/delete/"
+ str(self.materi1.id)
+ "/"
+ str(Comment.objects.get(comment="This is new comment by Anonymous").id)
)
self.client.get(deleteURL)
self.assertEqual(Comment.objects.all().filter(
comment="This is new comment by Anonymous").count(), 0)
self.assertEqual(Comment.objects.all().filter(comment="This is new comment by Anonymous").count(), 0)
def test_tombol_citasiAPA(self):
response = self.client.get(self.url)
self.assertContains(response, 'Citate APA')
self.assertContains(response, "Citate APA")
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
self.assertIn(expected,
response.context["citationAPA"])
expected = self.materi1.author + " . (n.d) . " + self.materi1.title + " . " + self.materi1.publisher
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
self.assertIn(expected,
response.context["citationAPA"])
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)
......@@ -289,12 +372,20 @@ 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"])
def test_citation_IEEE_materi_has_published_date(self):
......@@ -303,37 +394,46 @@ 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')