diff --git a/app/migrations/0014_rating.py b/app/migrations/0014_rating.py new file mode 100644 index 0000000000000000000000000000000000000000..69ae6aa1e7dcd3345443a22672118f591ef8e680 --- /dev/null +++ b/app/migrations/0014_rating.py @@ -0,0 +1,30 @@ +# Generated by Django 3.0.3 on 2020-09-29 11:30 + +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', '0013_auto_20200919_2055'), + ] + + operations = [ + migrations.CreateModel( + name='Rating', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('timestamp', models.DateTimeField(default=django.utils.timezone.now)), + ('score', models.IntegerField()), + ('materi', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='app.Materi')), + ('user', models.ForeignKey(blank=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + options={ + 'unique_together': {('materi', 'user')}, + }, + ), + ] diff --git a/app/models.py b/app/models.py index c0ac9363b9d45df75d4d8a2577db499bdaa18ef4..0e4a43fbb1a7961d11f88947bfd305cf17a6ce83 100644 --- a/app/models.py +++ b/app/models.py @@ -1,5 +1,6 @@ import random +from django.core.exceptions import ValidationError from django.db import models from django.utils import timezone @@ -12,6 +13,7 @@ VERIFICATION_STATUS = [ ("REVISION", "Perbaikan"), ] + # Create your models here. @@ -108,3 +110,22 @@ class DummyDownloadStatistics(models.Model): class DummyComment(models.Model): item = models.ForeignKey(Comment, on_delete=models.CASCADE) + + +class Rating(models.Model): + materi = models.ForeignKey(Materi, models.CASCADE) + user = models.ForeignKey(User, on_delete=models.CASCADE, blank=True) + timestamp = models.DateTimeField(default=timezone.now) + score = models.IntegerField() + + def save(self, force_insert=False, force_update=False, using=None, update_fields=None): + if 1 <= self.score <= 5: + super().save(force_insert, force_update, using, update_fields) + else: + raise ValidationError("Rating score must be integer between 1-5") + + def __str__(self): + return "Material:{} | User:{} | Rating:{}".format(self.materi.title, self.user.name, self.score) + + class Meta: + unique_together = ["materi", "user"] diff --git a/app/tests.py b/app/tests.py index ba1381f98d2382ce272903692a7868110bab461b..f8fff26448ef2e708bab73e61a4c2f49009d72eb 100644 --- a/app/tests.py +++ b/app/tests.py @@ -1,25 +1,21 @@ import json from django.contrib.auth import get_user_model -from django.core import serializers -from django.core.files import File +from django.core.exceptions import ValidationError from django.core.files.uploadedfile import SimpleUploadedFile -from django.test import Client, RequestFactory, TestCase +from django.db import IntegrityError +from django.test import Client, TestCase from django.urls import resolve from administration.utils import id_generator -from app.views import UploadMateriHTML, UploadMateriView +from app.views import UploadMateriView from authentication.models import User - -from .models import Category, Comment, Materi, Like, ViewStatistics, DownloadStatistics +from .models import Category, Comment, Materi, Like, Rating from .views import (DaftarKatalog, DashboardKontributorView, DetailMateri, ProfilKontributorView, SuksesLoginAdminView, SuksesLoginKontributorView, SuntingProfilView, ProfilAdminView, CommentsView, SuntingProfilAdminView, RevisiMateriView) -from app.views import UploadMateriHTML, UploadMateriView -from authentication.models import User - class DaftarKatalogTest(TestCase): def test_daftar_katalog_url_exist(self): @@ -928,4 +924,98 @@ class RevisiMateriTest(TestCase): response = self.client.get(self.url) self.assertEqual(response.status_code, 200) # Logout - self.client.logout() \ No newline at end of file + self.client.logout() + + +class RatingMateriTest(TestCase): + def setUp(self): + self.url = '/administration/' + 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.materi1 = Materi.objects.all()[0] + self.materi2 = Materi.objects.all()[1] + + def test_rating_model_can_be_created_with_proper_parameter(self): + Rating(materi=self.materi1, user=self.user_one, score=5).save() + rating = Rating.objects.first() + self.assertEqual(rating.materi, self.materi1) + self.assertEqual(rating.user, self.user_one) + self.assertTrue(0 < rating.score < 6) + self.assertEqual(rating.__str__(), "Material:Materi 1 | User:User One | Rating:5") + + 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)) + + 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)) + + 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() + Rating(materi=self.materi1, user=self.user_two, score=2).save() + rating_one = Rating.objects.get(user=self.user_one) + rating_two = Rating.objects.get(user=self.user_two) + self.assertEqual(rating_one.materi, self.materi1) + self.assertEqual(rating_two.materi, self.materi1) + self.assertEqual(rating_one.user, self.user_one) + self.assertEqual(rating_two.user, self.user_two) + + def test_one_user_should_be_able_to_be_related_to_two_rating(self): + Rating(materi=self.materi1, user=self.user_one, score=3).save() + Rating(materi=self.materi2, user=self.user_one, score=3).save() + rating_one = Rating.objects.filter(materi=self.materi1).first() + rating_two = Rating.objects.filter(materi=self.materi2).first() + self.assertEqual(rating_one.materi, self.materi1) + self.assertEqual(rating_two.materi, self.materi2) + self.assertEqual(rating_one.user, self.user_one) + self.assertEqual(rating_two.user, self.user_one) + + def test_two_rating_should_not_have_same_user_and_materi(self): + 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)) + + def test_materi_in_rating_should_not_be_null(self): + with self.assertRaises(IntegrityError): + Rating(user=self.user_one, score=1).save() + + def test_user_in_rating_should_not_be_null(self): + with self.assertRaises(IntegrityError): + Rating(materi=self.materi1, score=1).save() + + def test_score_in_rating_should_not_be_null(self): + with self.assertRaises(TypeError): + Rating(materi=self.materi1, user=self.user_one).save()