Fakultas Ilmu Komputer UI

Commit 4baf6312 authored by Rizkhi PH's avatar Rizkhi PH 🤔
Browse files

Merge branch '1706039660-35' into 'master'

[#35] Material: Delete Material (Soft Delete/Recycle Bin)

Closes #35

See merge request !56
parents d5c0345b a3591023
Pipeline #59523 passed with stages
in 15 minutes and 15 seconds
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
<th scope="col">Kontributor</th> <th scope="col">Kontributor</th>
<th scope="col">Status</th> <th scope="col">Status</th>
<th scope="col">Jumlah Like</th> <th scope="col">Jumlah Like</th>
<th scope="col">Pilihan</th>
</tr> </tr>
</thead> </thead>
<tfoot> <tfoot>
...@@ -40,6 +41,7 @@ ...@@ -40,6 +41,7 @@
<th scope="col">Kontributor</th> <th scope="col">Kontributor</th>
<th scope="col">Status</th> <th scope="col">Status</th>
<th scope="col">Jumlah Like</th> <th scope="col">Jumlah Like</th>
<th scope="col">Pilihan</th>
</tr> </tr>
</tr> </tr>
</tfoot> </tfoot>
...@@ -57,6 +59,11 @@ ...@@ -57,6 +59,11 @@
<td>{{ materi.uploader }}</td> <td>{{ materi.uploader }}</td>
<td>{{ materi.status }}</td> <td>{{ materi.status }}</td>
<td>{{ materi.like_count }}</td> <td>{{ materi.like_count }}</td>
<td>
{% if not materi.deleted_at %}<a type="button" href="/materi/{{materi.id}}/delete" class="reject-button button-decoration"
style="background-color:red">Hapus</a>
{% endif %}
</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
......
...@@ -505,7 +505,7 @@ class KelolaMateriView(TemplateView): ...@@ -505,7 +505,7 @@ class KelolaMateriView(TemplateView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(KelolaMateriView, self).get_context_data(**kwargs) context = super(KelolaMateriView, self).get_context_data(**kwargs)
context['materi_list'] = Materi.objects.all() context['materi_list'] = Materi.all_objects.all()
return context return context
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
......
# Generated by Django 3.1 on 2020-10-23 03:23
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('app', '0022_merge_20201011_1122'),
]
operations = [
migrations.AddField(
model_name='materi',
name='deleted_at',
field=models.DateTimeField(blank=True, null=True),
),
]
...@@ -39,6 +39,15 @@ class Category(models.Model): ...@@ -39,6 +39,15 @@ class Category(models.Model):
class MateriManager(models.Manager): class MateriManager(models.Manager):
def __init__(self, *args, **kwargs):
self.alive_only = kwargs.pop('alive_only', True)
super(MateriManager, self).__init__(*args, **kwargs)
def get_queryset(self):
if self.alive_only:
return SoftDeletionQuerySet(self.model).filter(deleted_at=None)
return SoftDeletionQuerySet(self.model)
def search(self, search_text): def search(self, search_text):
search_vector = search.SearchVector("title", weight="A") search_vector = search.SearchVector("title", weight="A")
search_query = search.SearchQuery(search_text) search_query = search.SearchQuery(search_text)
...@@ -51,8 +60,23 @@ class MateriManager(models.Manager): ...@@ -51,8 +60,23 @@ class MateriManager(models.Manager):
return search_result return search_result
class SoftDeletionQuerySet(models.query.QuerySet):
def delete(self):
return super(SoftDeletionQuerySet, self).update(deleted_at=timezone.now())
class Materi(models.Model): class SoftDeleteModel(models.Model):
deleted_at = models.DateTimeField(blank=True, null=True)
all_objects = MateriManager(alive_only=False)
class Meta:
abstract = True
def soft_delete(self):
self.deleted_at = timezone.now()
self.save()
class Materi(SoftDeleteModel):
cover = models.ImageField() cover = models.ImageField()
content = models.FileField() content = models.FileField()
title = models.CharField(max_length=50, default="Judul") title = models.CharField(max_length=50, default="Judul")
...@@ -61,7 +85,7 @@ class Materi(models.Model): ...@@ -61,7 +85,7 @@ class Materi(models.Model):
publisher = models.CharField(max_length=30, default="Penerbit") publisher = models.CharField(max_length=30, default="Penerbit")
release_year = models.IntegerField(default=current_year) release_year = models.IntegerField(default=current_year)
pages = models.IntegerField(default=0) pages = models.IntegerField(default=0)
descriptions = models.TextField(default="Deskripsi") 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) categories = models.ManyToManyField(Category)
date_created = models.DateTimeField(default=timezone.now) date_created = models.DateTimeField(default=timezone.now)
......
...@@ -1071,26 +1071,57 @@ class DashboardKontributorViewTest(TestCase): ...@@ -1071,26 +1071,57 @@ class DashboardKontributorViewTest(TestCase):
html = response.content.decode("utf-8") html = response.content.decode("utf-8")
self.assertIn(ERROR_403_MESSAGE, html) self.assertIn(ERROR_403_MESSAGE, html)
class DeleteMateriTest(TestCase): class DeleteMateriTest(TestCase):
def setUp(self): def setUp(self):
self.client = Client() self.client = Client()
self.admin_credential = {
"email": "admin@gov.id",
"password": id_generator()
}
self.superuser_credential = {
"email": "superuser@gov.id",
"password": id_generator()
}
self.contributor_credential = {
"email": "contributor@gov.id",
"password": id_generator()
}
self.admin = get_user_model().objects.create_user(
**self.admin_credential, name="Admin", is_admin=True)
self.superuser = get_user_model().objects.create_user(
**self.superuser_credential, name="Superuser", is_admin=True, is_superuser=True)
self.content = SimpleUploadedFile( self.content = SimpleUploadedFile(
"content.txt", b"Test") "content.txt", b"Test")
self.cover = SimpleUploadedFile( self.cover = SimpleUploadedFile(
"flower.jpg", b"Test file") "flower.jpg", b"Test file")
self.contributor = User.objects.create_contributor(email="kontributor@gov.id", self.contributor = User.objects.create_contributor(**self.contributor_credential)
password="kontributor")
Materi(title="Materi 1", author="Agas", uploader=self.contributor, Materi(title="Materi 1", author="Agas", uploader=self.contributor,
publisher="Kelas SC", descriptions="Deskripsi Materi 1", publisher="Kelas SC", descriptions="Deskripsi Materi 1",
status="APPROVE", cover=self.cover, content=self.content).save() 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) + "/delete" self.url = "/materi/" + str(self.materi1.id) + "/delete"
def test_url_delete_materi_is_success(self): def test_url_delete_materi_is_success_as_contributor(self):
self.client.login(**self.contributor_credential)
response = self.client.get(self.url)
self.assertEqual(response.status_code, 302)
def test_url_soft_delete_materi_is_success_as_admin(self):
self.client.login(**self.admin_credential)
response = self.client.get(self.url) response = self.client.get(self.url)
self.assertEqual(response.status_code, 302) self.assertEqual(response.status_code, 302)
self.materi1.refresh_from_db()
self.assertNotEqual(self.materi1.deleted_at, None)
def test_url_soft_delete_materi_is_success_as_superuser(self):
self.client.login(**self.superuser_credential)
response = self.client.get(self.url)
self.assertEqual(response.status_code, 302)
self.materi1.refresh_from_db()
self.assertNotEqual(self.materi1.deleted_at, None)
class ProfilViewTest(TestCase): class ProfilViewTest(TestCase):
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
......
...@@ -259,6 +259,9 @@ def view_materi(request, pk): ...@@ -259,6 +259,9 @@ def view_materi(request, pk):
def delete_materi(request, pk): def delete_materi(request, pk):
materi = get_object_or_404(Materi, pk=pk) materi = get_object_or_404(Materi, pk=pk)
if request.user.is_superuser or request.user.is_admin:
materi.soft_delete()
return HttpResponseRedirect("/administration/")
materi.delete() materi.delete()
return HttpResponseRedirect("/dashboard/") return HttpResponseRedirect("/dashboard/")
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment