Fakultas Ilmu Komputer UI

models.py 12 KB
Newer Older
Saul Andre's avatar
Saul Andre committed
1
import random
2
import datetime
3
import math
4

5
from django.contrib.postgres import search
6
from django.core.exceptions import ValidationError
7
from django.core.validators import MinValueValidator, MaxValueValidator
8
from django.db import models
9
from django.utils import timezone
10

igor lestin sianipar's avatar
igor lestin sianipar committed
11
12
13
from authentication.models import User

VERIFICATION_STATUS = [
14
15
    ("PENDING", "Diproses"),
    ("APPROVE", "Diterima"),
16
    ("DISAPPROVE", "Ditolak"),
17
    ("REVISION", "Perbaikan"),
18
    ("BLOCKED", "Diblokir"),
igor lestin sianipar's avatar
igor lestin sianipar committed
19
]
Samuel Dimas's avatar
Samuel Dimas committed
20

21

Samuel Dimas's avatar
Samuel Dimas committed
22
# Create your models here.
23
24


25
def get_random_color():
Saul Andre's avatar
Saul Andre committed
26
    color = "%06x" % random.randint(0, 0xFFFFFF)
27
28
    return color

29

30
31
def current_year():
    return datetime.date.today().year
Saul Andre's avatar
Saul Andre committed
32

33

Saul Andre's avatar
Saul Andre committed
34
35
class Category(models.Model):
    name = models.CharField(max_length=20)
36
37
    description = models.TextField(blank=False, default="")
    archived = models.BooleanField(default=False, blank=False)
38
    archived_by = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
Saul Andre's avatar
Saul Andre committed
39
40
41
42

    def __str__(self):
        return self.name

43

44
class MateriManager(models.Manager):
45
46
47
48
49
50
51
52
    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)
53

54
    def search(self, search_text):
55
56
57
58
59
60
61
        search_vector = None
        for field, weight in Materi.SEARCH_INDEX:
            if search_vector is None:
                search_vector = search.SearchVector(field, weight=weight)
            else:
                search_vector += search.SearchVector(field, weight=weight)

62
63
64
65
66
        search_query = search.SearchQuery(search_text)

        search_rank = search.SearchRank(search_vector, search_query)

        search_result = (
67
68
69
70
            self.get_queryset()
            .filter(_search_vector=search_query)
            .annotate(rank=search_rank)
            .order_by("-rank")
71
72
73
74
        )

        return search_result

75

76
77
78
class SoftDeletionQuerySet(models.query.QuerySet):
    def delete(self):
        return super(SoftDeletionQuerySet, self).update(deleted_at=timezone.now())
79

80

81
82
83
84
class SoftDeleteModel(models.Model):
    deleted_at = models.DateTimeField(blank=True, null=True)

    all_objects = MateriManager(alive_only=False)
85

86
87
    class Meta:
        abstract = True
88

89
90
91
92
    def soft_delete(self):
        self.deleted_at = timezone.now()
        self.save()

93

94
class Materi(SoftDeleteModel):
igor lestin sianipar's avatar
igor lestin sianipar committed
95
96
    cover = models.ImageField()
    content = models.FileField()
97
98
    title = models.CharField(max_length=50, default="Judul")
    author = models.CharField(max_length=30, default="Penyusun")
igor lestin sianipar's avatar
igor lestin sianipar committed
99
    uploader = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
Samuel Dimas Partogi's avatar
Samuel Dimas Partogi committed
100
    publisher = models.CharField(max_length=30, default="Penerbit")
101
    release_year = models.IntegerField(default=current_year)
Samuel Dimas Partogi's avatar
Samuel Dimas Partogi committed
102
    pages = models.IntegerField(default=0)
103
104
105
    descriptions = models.TextField(default="Deskripsi")
    status = models.CharField(
        max_length=30, choices=VERIFICATION_STATUS, default=VERIFICATION_STATUS[0][0])
Saul Andre's avatar
Saul Andre committed
106
    categories = models.ManyToManyField(Category)
107
108
    date_created = models.DateTimeField(default=timezone.now)
    date_modified = models.DateTimeField(auto_now=True)
109
    yt_video_id = models.CharField(max_length=100, blank=True, null=True)
110

111
112
    _search_vector = search.SearchVectorField(null=True, editable=False)

113
114
115
116
117
118
119
120
    SEARCH_INDEX = (
        ("title", "A"),
        ("author", "A"),
        ("publisher", "C"),
        ("descriptions", "C"),
        ("uploader", "D"),
    )

121
122
123
124
    objects = MateriManager()

    def save(self, *args, **kwargs):
        super().save(*args, **kwargs)
125
126
        search_index = {field: weight for (
            field, weight) in Materi.SEARCH_INDEX}
127
128
129
130
131
132
133
134
135
        if "update_fields" in kwargs:
            is_search_index_updated = bool(
                set(search_index.keys()) & set(kwargs["update_fields"])
            )

        if ("update_fields" not in kwargs) or (is_search_index_updated):
            self._search_vector = None
            for field, weight in search_index.items():
                if self._search_vector is None:
136
137
                    self._search_vector = search.SearchVector(
                        field, weight=weight)
138
                else:
139
140
                    self._search_vector += search.SearchVector(
                        field, weight=weight)
141
142
143

            self.save(update_fields=["_search_vector"])

144
145
146
147
    @property
    def is_published(self):
        published = False
        if self.verificationreport_set.exists():
148
149
            report = self.verificationreport_set.latest("timestamp")
            published = True if report.status == "Diterima" else False
150
151
152
153
154
155
        return published

    @property
    def published_date(self):
        published_date = None
        if self.verificationreport_set.exists():
156
157
            report = self.verificationreport_set.latest("timestamp")
            if report.status == "Diterima":
158
159
                published_date = report.timestamp
        return published_date
160

161
162
163
164
    @property
    def like_count(self):
        count = Like.objects.filter(materi=self).count()
        return count
165

166
167
168
169
    @property
    def comment_count(self):
        count = Comment.objects.filter(materi=self).count()
        return count
170

171
172
173
174
175
    @property
    def review_count(self):
        count = Review.objects.filter(materi=self).count()
        return count

176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
    @staticmethod
    def earliest_materi_timestamp():
        return Materi.objects.earliest('date_created').date_created.timestamp()

    @property
    def seconds_since_earliest_materi(self):
        return self.date_created.timestamp() - Materi.earliest_materi_timestamp()

    @property
    def view_count(self):
        count = ViewStatistics.objects.filter(materi=self).count()
        return count

    @property
    def hot_score(self):
        view_score = math.log(max(self.view_count, 1), 10)
192
        time_score = self.seconds_since_earliest_materi / 604800  # 1 week
193
194
        return round(view_score + time_score, 7)

195
196
197
198
199
    @property
    def is_like(self):
        like = False
        if Like.objects.filter(materi=self).exists():
            like = True
200
        return like
201

202

203
class Comment(models.Model):
204
    username = models.CharField(max_length=100)
205
    profile = models.CharField(max_length=100, default=get_random_color)
206
    comment = models.CharField(max_length=240, default="comments")
207
    materi = models.ForeignKey(Materi, models.SET_NULL, null=True)
208
209
    user = models.ForeignKey(
        User, on_delete=models.SET_NULL, blank=True, null=True)
210
    timestamp = models.DateTimeField(default=timezone.now)
211
212

    def __str__(self):
213
        return self.username
214

215
216
217
218
    @property
    def like_count(self):
        count = LikeComment.objects.filter(comment=self).count()
        return count
219

220
221
222
223
224
    @property
    def dislike_count(self):
        count = DislikeComment.objects.filter(comment=self).count()
        return count

225

226
227
class Review(models.Model):
    username = models.CharField(max_length=100)
228
    profile = models.CharField(max_length=100, default=get_random_color)
229
230
    review = models.TextField(default="review")
    materi = models.ForeignKey(Materi, models.SET_NULL, null=True)
231
232
    user = models.ForeignKey(
        User, on_delete=models.SET_NULL, blank=True, null=True)
233
234
235
236
237
    timestamp = models.DateTimeField(default=timezone.now)

    def __str__(self):
        return self.username

238
239
240
241
242
243
244
245
246
247
248
249

class LikeComment(models.Model):
    comment = models.ForeignKey(Comment, models.SET_NULL, null=True)
    timestamp = models.DateTimeField(default=timezone.now)
    session_id = models.CharField(max_length=32, blank=False)


class DislikeComment(models.Model):
    comment = models.ForeignKey(Comment, models.SET_NULL, null=True)
    timestamp = models.DateTimeField(default=timezone.now)
    session_id = models.CharField(max_length=32, blank=False)

250
251
252
253
254
255

class Like(models.Model):
    materi = models.ForeignKey(Materi, models.SET_NULL, null=True)
    timestamp = models.DateTimeField(default=timezone.now)
    session_id = models.CharField(max_length=32, blank=False)

256

257
258
259
260
class ReqMaterial(models.Model):
    title = models.CharField(max_length=100)
    timestamp = models.DateTimeField(default=timezone.now)

261

insan ramadhan's avatar
insan ramadhan committed
262
263
264
265
266
267
class SubmitVisitor(models.Model):
    user_id = models.CharField(max_length=50)
    email = models.CharField(max_length=50)
    msg = models.CharField(max_length=100)
    timestamp = models.DateTimeField(default=timezone.now)

268
269

class ViewStatistics(models.Model):
270
271
    materi = models.ForeignKey(
        Materi, models.SET_NULL, null=True, related_name="baca")
272
273
274
275
    timestamp = models.DateTimeField(default=timezone.now)


class DownloadStatistics(models.Model):
276
277
278
279
    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")
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
    timestamp = models.DateTimeField(default=timezone.now)


class DummyLike(models.Model):
    item = models.ForeignKey(Like, on_delete=models.CASCADE)


class DummyViewStatistics(models.Model):
    item = models.ForeignKey(ViewStatistics, on_delete=models.CASCADE)


class DummyDownloadStatistics(models.Model):
    item = models.ForeignKey(DownloadStatistics, on_delete=models.CASCADE)


class DummyComment(models.Model):
    item = models.ForeignKey(Comment, on_delete=models.CASCADE)
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315


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"]
316
317
318
319


class RatingContributor(models.Model):
    timestamp = models.DateTimeField(auto_now=True)
320
321
322
323
324
325
    score = models.PositiveIntegerField(
        validators=[MinValueValidator(1), MaxValueValidator(5)])
    contributor = models.ForeignKey(
        User, on_delete=models.CASCADE, related_name='contributor')
    user = models.ForeignKey(
        User, on_delete=models.CASCADE, related_name='user')
326
327
328
329
330

    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:
331
            raise ValidationError("Rating score must be integer between 1-5")
332

333
334
    class Meta:
        unique_together = ["contributor", "user"]
335

336

337
class LaporanMateri(models.Model):
338
339
    materi = models.ForeignKey(
        Materi, on_delete=models.CASCADE, max_length=120)
340
    user = models.ForeignKey(User, on_delete=models.CASCADE)
341
342
    laporan = models.TextField(
        validators=[MinValueValidator(30), MaxValueValidator(120)], default="")
343
    timestamp = models.DateTimeField(default=timezone.now)
344
    is_rejected = models.BooleanField(default=False)
Anthony Dewa Priyasembada's avatar
Anthony Dewa Priyasembada committed
345

346
347
348
349
350
351
352

class GuestBook(models.Model):
    name = models.TextField(default='')
    job = models.TextField(default='')
    gender = models.CharField(max_length=6, default="Male")


Anthony Dewa Priyasembada's avatar
Anthony Dewa Priyasembada committed
353
354
355
356
357
358
class ReadLater(models.Model):
    materi = models.ForeignKey(Materi, on_delete=models.CASCADE)
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    timestamp = models.DateTimeField(default=timezone.now)

    class Meta:
359
360
        unique_together = ["materi", "user"]

361

362
class NotifikasiKontributor(models.Model):
363
364
    materi = models.ForeignKey(
        Materi, on_delete=models.CASCADE, max_length=120)
365
366
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    feedback = models.CharField(max_length=20, default="")
367

368
369
370
    def __str__(self):
        return "Status: {}".format(self.feedback)

371

372
373
class AdminNotification(models.Model):
    materi = models.ForeignKey(Materi, on_delete=models.CASCADE)