diff --git a/data/polls.json b/data/polls.json index 558b94f..6d5302b 100644 --- a/data/polls.json +++ b/data/polls.json @@ -6,8 +6,9 @@ "question_text": "Python vs C++, which one is better in your opinion?", "pub_date": "2023-09-11T06:31:14Z", "end_date": "2023-09-29T20:31:49Z", - "short_description": "Cool kids have polls", - "long_description": "No description provide for this poll.", + "short_description": "🐍 vs 🇨 ++", + "long_description": "Let them fight.", + "trend_score": 60.0, "tags": [] } }, @@ -18,8 +19,9 @@ "question_text": "The chicken and the egg, which came first?", "pub_date": "2023-09-11T02:50:04Z", "end_date": "2023-10-18T23:50:19Z", - "short_description": "Cool kids have polls", - "long_description": "No description provide for this poll.", + "short_description": "Classic question.", + "long_description": "Most biologists state unequivocally that the egg came first. At their most basic level, eggs are just female sex cells. Hard external eggs that can be laid on land (also known as amniotic eggs) were a game changer for vertebrates. Anyway, let select you choice.", + "trend_score": 140.0, "tags": [] } }, @@ -30,8 +32,9 @@ "question_text": "So far so good?", "pub_date": "2023-08-03T06:50:43Z", "end_date": "2023-11-28T19:50:53Z", - "short_description": "Cool kids have polls", - "long_description": "No description provide for this poll.", + "short_description": "If you know, you know.", + "long_description": "The sentence where you will hear once every week.", + "trend_score": 120.0, "tags": [] } }, @@ -42,8 +45,61 @@ "question_text": "Do you love Django?", "pub_date": "2023-09-11T19:51:12Z", "end_date": "2023-09-29T17:51:18Z", - "short_description": "Cool kids have polls", - "long_description": "No description provide for this poll.", + "short_description": "Djago, I love it!", + "long_description": "Django is a high-level Python web framework that encourages rapid development and clean, pragmatic design. Built by experienced developers, it takes care of much of the hassle of web development, so you can focus on writing your app without needing to reinvent the wheel. It’s free and open source.", + "trend_score": 120.0, + "tags": [] + } +}, +{ + "model": "polls.question", + "pk": 6, + "fields": { + "question_text": "Taylor Swift vs Kanye West. Who is better!", + "pub_date": "2023-09-15T15:46:58Z", + "end_date": "2023-11-30T15:47:01Z", + "short_description": "Swifty vs Ye Club", + "long_description": "It's no secret that Taylor Swift and Kanye West's relationship can be described as volatile at best. Let choose!", + "trend_score": 100.0, + "tags": [] + } +}, +{ + "model": "polls.question", + "pk": 7, + "fields": { + "question_text": "What is your preferred text editor or IDE for coding?", + "pub_date": "2023-09-15T15:49:22Z", + "end_date": "2023-11-21T23:00:00Z", + "short_description": "Please do my poll🥺", + "long_description": "My favorite code editor is VScode. What about you?", + "trend_score": 100.0, + "tags": [] + } +}, +{ + "model": "polls.question", + "pk": 8, + "fields": { + "question_text": "Which one is the best RIZZ line?", + "pub_date": "2023-09-14T15:52:03Z", + "end_date": "2023-11-14T17:00:00Z", + "short_description": "RIZZ (noun) How good you are with flirting", + "long_description": "Rizz actually comes from the word charisma. It's a NYC Slang created by a twitch streamer named Kai cenat. This means that you have game, a good flirt line.", + "trend_score": 100.0, + "tags": [] + } +}, +{ + "model": "polls.question", + "pk": 9, + "fields": { + "question_text": "What is the best pizza topping?", + "pub_date": "2023-09-15T16:10:02Z", + "end_date": "2023-12-31T05:00:00Z", + "short_description": "Pizza without topping is not pizza!", + "long_description": "I mean, Yeah here are the choices ;)", + "trend_score": 100.0, "tags": [] } }, @@ -119,6 +175,198 @@ "choice_text": "No comment." } }, +{ + "model": "polls.choice", + "pk": 10, + "fields": { + "question": 6, + "choice_text": "Kanye" + } +}, +{ + "model": "polls.choice", + "pk": 11, + "fields": { + "question": 6, + "choice_text": "Taylor Swift" + } +}, +{ + "model": "polls.choice", + "pk": 12, + "fields": { + "question": 6, + "choice_text": "Neither" + } +}, +{ + "model": "polls.choice", + "pk": 13, + "fields": { + "question": 6, + "choice_text": "Both!" + } +}, +{ + "model": "polls.choice", + "pk": 14, + "fields": { + "question": 6, + "choice_text": "No Opinion" + } +}, +{ + "model": "polls.choice", + "pk": 15, + "fields": { + "question": 7, + "choice_text": "VScode" + } +}, +{ + "model": "polls.choice", + "pk": 16, + "fields": { + "question": 7, + "choice_text": "IntelliJ IDEA" + } +}, +{ + "model": "polls.choice", + "pk": 17, + "fields": { + "question": 7, + "choice_text": "Vim" + } +}, +{ + "model": "polls.choice", + "pk": 18, + "fields": { + "question": 7, + "choice_text": "Emacs" + } +}, +{ + "model": "polls.choice", + "pk": 19, + "fields": { + "question": 7, + "choice_text": "Eclipse" + } +}, +{ + "model": "polls.choice", + "pk": 20, + "fields": { + "question": 8, + "choice_text": "Let me be your CSS to your HTML" + } +}, +{ + "model": "polls.choice", + "pk": 21, + "fields": { + "question": 8, + "choice_text": "Are you my grade? Because Im nervous everytime Im about to see you" + } +}, +{ + "model": "polls.choice", + "pk": 22, + "fields": { + "question": 8, + "choice_text": "If being beautiful was a crime, you’d be on the most wanted list." + } +}, +{ + "model": "polls.choice", + "pk": 23, + "fields": { + "question": 8, + "choice_text": "You look like mom! Mom of our son." + } +}, +{ + "model": "polls.choice", + "pk": 24, + "fields": { + "question": 8, + "choice_text": "Are you from Tennessee cuz u the only ten I see" + } +}, +{ + "model": "polls.choice", + "pk": 25, + "fields": { + "question": 8, + "choice_text": "Ouch i skinned my knee! because I fall for you" + } +}, +{ + "model": "polls.choice", + "pk": 26, + "fields": { + "question": 8, + "choice_text": "Are you semicolon!! cause you alway make me complete." + } +}, +{ + "model": "polls.choice", + "pk": 27, + "fields": { + "question": 8, + "choice_text": "Roses are red, Violets are blue, Something smells is that you!" + } +}, +{ + "model": "polls.choice", + "pk": 28, + "fields": { + "question": 9, + "choice_text": "Pineapples" + } +}, +{ + "model": "polls.choice", + "pk": 29, + "fields": { + "question": 9, + "choice_text": "Orange" + } +}, +{ + "model": "polls.choice", + "pk": 30, + "fields": { + "question": 9, + "choice_text": "Banana" + } +}, +{ + "model": "polls.choice", + "pk": 31, + "fields": { + "question": 8, + "choice_text": "What is the difference betweeen history and you? History is in the past but you are my future." + } +}, +{ + "model": "polls.choice", + "pk": 32, + "fields": { + "question": 1, + "choice_text": "Use both!" + } +}, +{ + "model": "polls.choice", + "pk": 33, + "fields": { + "question": 2, + "choice_text": "Dinosaur" + } +}, { "model": "polls.vote", "pk": 1, @@ -254,6 +502,114 @@ "question": 1 } }, +{ + "model": "polls.vote", + "pk": 16, + "fields": { + "choice": 9, + "user": 1, + "question": 4 + } +}, +{ + "model": "polls.vote", + "pk": 17, + "fields": { + "choice": 6, + "user": 1, + "question": 3 + } +}, +{ + "model": "polls.vote", + "pk": 18, + "fields": { + "choice": 25, + "user": 2, + "question": 8 + } +}, +{ + "model": "polls.vote", + "pk": 19, + "fields": { + "choice": 15, + "user": 2, + "question": 7 + } +}, +{ + "model": "polls.vote", + "pk": 20, + "fields": { + "choice": 24, + "user": 4, + "question": 8 + } +}, +{ + "model": "polls.vote", + "pk": 21, + "fields": { + "choice": 25, + "user": 3, + "question": 8 + } +}, +{ + "model": "polls.vote", + "pk": 22, + "fields": { + "choice": 14, + "user": 3, + "question": 6 + } +}, +{ + "model": "polls.vote", + "pk": 23, + "fields": { + "choice": 28, + "user": 3, + "question": 9 + } +}, +{ + "model": "polls.vote", + "pk": 24, + "fields": { + "choice": 27, + "user": 6, + "question": 8 + } +}, +{ + "model": "polls.vote", + "pk": 25, + "fields": { + "choice": 15, + "user": 6, + "question": 7 + } +}, +{ + "model": "polls.vote", + "pk": 26, + "fields": { + "choice": 29, + "user": 6, + "question": 9 + } +}, +{ + "model": "polls.vote", + "pk": 27, + "fields": { + "choice": 26, + "user": 1, + "question": 8 + } +}, { "model": "polls.sentimentvote", "pk": 1, @@ -361,5 +717,113 @@ "question": 2, "vote_types": true } +}, +{ + "model": "polls.sentimentvote", + "pk": 14, + "fields": { + "user": 1, + "question": 4, + "vote_types": false + } +}, +{ + "model": "polls.sentimentvote", + "pk": 15, + "fields": { + "user": 1, + "question": 3, + "vote_types": true + } +}, +{ + "model": "polls.sentimentvote", + "pk": 16, + "fields": { + "user": 1, + "question": 2, + "vote_types": false + } +}, +{ + "model": "polls.sentimentvote", + "pk": 17, + "fields": { + "user": 2, + "question": 8, + "vote_types": true + } +}, +{ + "model": "polls.sentimentvote", + "pk": 18, + "fields": { + "user": 2, + "question": 7, + "vote_types": true + } +}, +{ + "model": "polls.sentimentvote", + "pk": 19, + "fields": { + "user": 4, + "question": 8, + "vote_types": true + } +}, +{ + "model": "polls.sentimentvote", + "pk": 20, + "fields": { + "user": 3, + "question": 8, + "vote_types": true + } +}, +{ + "model": "polls.sentimentvote", + "pk": 21, + "fields": { + "user": 3, + "question": 6, + "vote_types": false + } +}, +{ + "model": "polls.sentimentvote", + "pk": 22, + "fields": { + "user": 3, + "question": 9, + "vote_types": false + } +}, +{ + "model": "polls.sentimentvote", + "pk": 23, + "fields": { + "user": 6, + "question": 8, + "vote_types": true + } +}, +{ + "model": "polls.sentimentvote", + "pk": 24, + "fields": { + "user": 6, + "question": 9, + "vote_types": false + } +}, +{ + "model": "polls.sentimentvote", + "pk": 25, + "fields": { + "user": 1, + "question": 8, + "vote_types": true + } } ] diff --git a/data/users.json b/data/users.json index 5f4166d..df0f9e3 100644 --- a/data/users.json +++ b/data/users.json @@ -4,7 +4,7 @@ "pk": 1, "fields": { "password": "pbkdf2_sha256$600000$aDh9a1PXxcXAb8z3YIjAPX$NVH24kt/wMad+0fZcCii738dfojI4vL2ffXOwNRuLz4=", - "last_login": "2023-09-14T16:45:03.576Z", + "last_login": "2023-09-15T17:09:43.310Z", "is_superuser": true, "username": "admin", "first_name": "", @@ -22,7 +22,7 @@ "pk": 2, "fields": { "password": "pbkdf2_sha256$600000$quZKLKT8Ec3TQgpdqlCkpX$o+VOOnRDLGf64qjHb239Yvsre74tPkC8hw1qH1un/hk=", - "last_login": "2023-09-14T13:22:50.921Z", + "last_login": "2023-09-15T17:07:42.782Z", "is_superuser": false, "username": "tester1", "first_name": "", @@ -40,7 +40,7 @@ "pk": 3, "fields": { "password": "pbkdf2_sha256$600000$1xGp6EDCoaljdTlSdVT1Mn$UID0Woeh8hwW7LtchH+hKzqdKTDeITTxQ/0DGvfG3CY=", - "last_login": "2023-09-12T07:09:55.381Z", + "last_login": "2023-09-15T16:36:24.774Z", "is_superuser": false, "username": "tester3", "first_name": "", @@ -58,7 +58,7 @@ "pk": 4, "fields": { "password": "pbkdf2_sha256$600000$fJJcIwAuIESYwZDBOqBv8t$YEDVCgg/xJOqAOiAdvGvvqgi1jgn1YfYHJE9yx2JWTA=", - "last_login": "2023-09-14T11:23:08.948Z", + "last_login": "2023-09-15T16:35:58.248Z", "is_superuser": false, "username": "tester2", "first_name": "", @@ -94,7 +94,7 @@ "pk": 6, "fields": { "password": "pbkdf2_sha256$600000$5rNKsClojvcsBqzrEmAzy5$XpeAUCrzeLG42H+8o4HBVqifKd0cQuWcEhFax/dxS5M=", - "last_login": "2023-09-14T16:44:29.087Z", + "last_login": "2023-09-15T16:37:53.221Z", "is_superuser": false, "username": "tester4", "first_name": "", @@ -106,5 +106,23 @@ "groups": [], "user_permissions": [] } +}, +{ + "model": "auth.user", + "pk": 7, + "fields": { + "password": "1234", + "last_login": null, + "is_superuser": false, + "username": "test", + "first_name": "", + "last_name": "", + "email": "", + "is_staff": false, + "is_active": true, + "date_joined": "2023-09-15T11:53:22.035Z", + "groups": [], + "user_permissions": [] + } } ] diff --git a/polls/admin.py b/polls/admin.py index 5219f0b..4eb18c9 100644 --- a/polls/admin.py +++ b/polls/admin.py @@ -13,8 +13,10 @@ class QuestionAdmin(admin.ModelAdmin): (None, {"fields": ["question_text"]}), ("Published date", {"fields": ["pub_date"], "classes": ["collapse"]}), ("End date", {"fields": ["end_date"], "classes": ["collapse"]}), + ("Short Description", {"fields": ["short_description"], "classes": ["collapse"]}), + ("Long Description", {"fields": ["long_description"], "classes": ["collapse"]}), ] - list_display = ["question_text", "pub_date", "end_date", "was_published_recently", "can_vote"] + list_display = ["question_text", "pub_date", "end_date", "was_published_recently", "can_vote", "trending_score"] inlines = [ChoiceInline] list_filter = ["pub_date", "end_date"] search_fields = ["question_text"] diff --git a/polls/migrations/0015_question_trend_score.py b/polls/migrations/0015_question_trend_score.py new file mode 100644 index 0000000..6b1b3a9 --- /dev/null +++ b/polls/migrations/0015_question_trend_score.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.4 on 2023-09-15 07:23 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('polls', '0014_remove_question_down_vote_count_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='question', + name='trend_score', + field=models.FloatField(default=0.0), + ), + ] diff --git a/polls/models.py b/polls/models.py index 70995ff..3fd50d5 100644 --- a/polls/models.py +++ b/polls/models.py @@ -46,6 +46,7 @@ class Question(models.Model): end_date = models.DateTimeField("date ended", null=True) short_description = models.CharField(max_length=200, default="Cool kids have polls") long_description = models.TextField(max_length=2000, default="No description provide for this poll.") + trend_score = models.FloatField(default=0.0, null=False, blank=False) tags = models.ManyToManyField(Tag, blank=True) def was_published_recently(self): @@ -157,6 +158,9 @@ class Question(models.Model): # ! Most of the code from https://stackoverflow.com/a/70869267 def upvote(self, user): + """create new SentimentVote object that represent upvote (vote_types=True) + return True if user change the vote or vote for the first time else return False + """ try: self.sentimentvote_set.create(user=user, question=self, vote_types=True) self.save() @@ -170,6 +174,9 @@ class Question(models.Model): return True def downvote(self, user): + """create new SentimentVote object that represent downvote (vote_types=False) + return True if user change the vote or vote for the first time else return False + """ try: self.sentimentvote_set.create(user=user, question=self, vote_types=False) self.save() @@ -190,6 +197,38 @@ class Question(models.Model): def down_vote_count(self): return self.sentimentvote_set.filter(question=self, vote_types=False).count() + def trending_score(self, up=None, down=None): + """Return trend score base on the criteria below""" + published_date_duration = timezone.now() - self.pub_date + score = 0 + + if (published_date_duration.seconds < 259200): # Second unit + score += 100 + elif (published_date_duration.seconds < 604800): + score += 75 + elif (published_date_duration.seconds < 2592000): + score += 50 + else: + score += 25 + + if (up == None) and (down == None): + score += ((self.up_vote_count/5) - (self.down_vote_count/5)) * 100 + else: + score += ((up/5) - (down/5)) * 100 + + return score + + + def save(self, *args, **kwargs): + """Modify save method of Question object""" + # to-be-added instance # * https://github.com/django/django/blob/866122690dbe233c054d06f6afbc2f3cc6aea2f2/django/db/models/base.py#L447 + if self._state.adding: + try: + self.trend_score = self.trending_score() + except ValueError: + self.trend_score = self.trending_score(up=0, down=0) + super(Question, self).save(*args, **kwargs) + class Choice(models.Model): """ @@ -228,9 +267,25 @@ class Vote(models.Model): # ! Most of the code from https://stackoverflow.com/a/70869267 class SentimentVote(models.Model): + """ + Represents a sentiment vote for a poll question. + + Attributes: + user (User): The user who cast the sentiment vote. + question (Question): The poll question for which the sentiment vote is cast. + vote_types (bool): Indicates whether the sentiment vote is an upvote (True) or a downvote (False). + + Note: + - When 'vote_types' is True, it represents an upvote or 'Like'. + - When 'vote_types' is False, it represents a downvote or 'Dislike'. + """ user = models.ForeignKey(User, on_delete=models.CASCADE) question = models.ForeignKey(Question, on_delete=models.CASCADE) vote_types = models.BooleanField() class Meta: + """ + unique_together (list of str): Ensures that a user can only cast one sentiment vote (upvote or downvote) + for a specific question. + """ unique_together = ['user', 'question'] \ No newline at end of file diff --git a/polls/templates/polls/index.html b/polls/templates/polls/index.html index fb8fdb2..0dd54a0 100644 --- a/polls/templates/polls/index.html +++ b/polls/templates/polls/index.html @@ -83,7 +83,7 @@