Merge pull request #40 from Sosokker/iteration3

Iteration3 Add Trending and more Test + Modify view to return context about trend and update new poll + user data
This commit is contained in:
Sirin Puenggun 2023-09-16 00:20:51 +07:00 committed by GitHub
commit 30bf6a9341
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 638 additions and 23 deletions

View File

@ -6,8 +6,9 @@
"question_text": "Python vs C++, which one is better in your opinion?", "question_text": "Python vs C++, which one is better in your opinion?",
"pub_date": "2023-09-11T06:31:14Z", "pub_date": "2023-09-11T06:31:14Z",
"end_date": "2023-09-29T20:31:49Z", "end_date": "2023-09-29T20:31:49Z",
"short_description": "Cool kids have polls", "short_description": "🐍 vs 🇨 ++",
"long_description": "No description provide for this poll.", "long_description": "Let them fight.",
"trend_score": 60.0,
"tags": [] "tags": []
} }
}, },
@ -18,8 +19,9 @@
"question_text": "The chicken and the egg, which came first?", "question_text": "The chicken and the egg, which came first?",
"pub_date": "2023-09-11T02:50:04Z", "pub_date": "2023-09-11T02:50:04Z",
"end_date": "2023-10-18T23:50:19Z", "end_date": "2023-10-18T23:50:19Z",
"short_description": "Cool kids have polls", "short_description": "Classic question.",
"long_description": "No description provide for this poll.", "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": [] "tags": []
} }
}, },
@ -30,8 +32,9 @@
"question_text": "So far so good?", "question_text": "So far so good?",
"pub_date": "2023-08-03T06:50:43Z", "pub_date": "2023-08-03T06:50:43Z",
"end_date": "2023-11-28T19:50:53Z", "end_date": "2023-11-28T19:50:53Z",
"short_description": "Cool kids have polls", "short_description": "If you know, you know.",
"long_description": "No description provide for this poll.", "long_description": "The sentence where you will hear once every week.",
"trend_score": 120.0,
"tags": [] "tags": []
} }
}, },
@ -42,8 +45,61 @@
"question_text": "Do you love Django?", "question_text": "Do you love Django?",
"pub_date": "2023-09-11T19:51:12Z", "pub_date": "2023-09-11T19:51:12Z",
"end_date": "2023-09-29T17:51:18Z", "end_date": "2023-09-29T17:51:18Z",
"short_description": "Cool kids have polls", "short_description": "Djago, I love it!",
"long_description": "No description provide for this poll.", "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. Its 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": [] "tags": []
} }
}, },
@ -119,6 +175,198 @@
"choice_text": "No comment." "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, youd 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", "model": "polls.vote",
"pk": 1, "pk": 1,
@ -254,6 +502,114 @@
"question": 1 "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", "model": "polls.sentimentvote",
"pk": 1, "pk": 1,
@ -361,5 +717,113 @@
"question": 2, "question": 2,
"vote_types": true "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
}
} }
] ]

View File

@ -4,7 +4,7 @@
"pk": 1, "pk": 1,
"fields": { "fields": {
"password": "pbkdf2_sha256$600000$aDh9a1PXxcXAb8z3YIjAPX$NVH24kt/wMad+0fZcCii738dfojI4vL2ffXOwNRuLz4=", "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, "is_superuser": true,
"username": "admin", "username": "admin",
"first_name": "", "first_name": "",
@ -22,7 +22,7 @@
"pk": 2, "pk": 2,
"fields": { "fields": {
"password": "pbkdf2_sha256$600000$quZKLKT8Ec3TQgpdqlCkpX$o+VOOnRDLGf64qjHb239Yvsre74tPkC8hw1qH1un/hk=", "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, "is_superuser": false,
"username": "tester1", "username": "tester1",
"first_name": "", "first_name": "",
@ -40,7 +40,7 @@
"pk": 3, "pk": 3,
"fields": { "fields": {
"password": "pbkdf2_sha256$600000$1xGp6EDCoaljdTlSdVT1Mn$UID0Woeh8hwW7LtchH+hKzqdKTDeITTxQ/0DGvfG3CY=", "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, "is_superuser": false,
"username": "tester3", "username": "tester3",
"first_name": "", "first_name": "",
@ -58,7 +58,7 @@
"pk": 4, "pk": 4,
"fields": { "fields": {
"password": "pbkdf2_sha256$600000$fJJcIwAuIESYwZDBOqBv8t$YEDVCgg/xJOqAOiAdvGvvqgi1jgn1YfYHJE9yx2JWTA=", "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, "is_superuser": false,
"username": "tester2", "username": "tester2",
"first_name": "", "first_name": "",
@ -94,7 +94,7 @@
"pk": 6, "pk": 6,
"fields": { "fields": {
"password": "pbkdf2_sha256$600000$5rNKsClojvcsBqzrEmAzy5$XpeAUCrzeLG42H+8o4HBVqifKd0cQuWcEhFax/dxS5M=", "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, "is_superuser": false,
"username": "tester4", "username": "tester4",
"first_name": "", "first_name": "",
@ -106,5 +106,23 @@
"groups": [], "groups": [],
"user_permissions": [] "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": []
}
} }
] ]

View File

@ -13,8 +13,10 @@ class QuestionAdmin(admin.ModelAdmin):
(None, {"fields": ["question_text"]}), (None, {"fields": ["question_text"]}),
("Published date", {"fields": ["pub_date"], "classes": ["collapse"]}), ("Published date", {"fields": ["pub_date"], "classes": ["collapse"]}),
("End date", {"fields": ["end_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] inlines = [ChoiceInline]
list_filter = ["pub_date", "end_date"] list_filter = ["pub_date", "end_date"]
search_fields = ["question_text"] search_fields = ["question_text"]

View File

@ -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),
),
]

View File

@ -46,6 +46,7 @@ class Question(models.Model):
end_date = models.DateTimeField("date ended", null=True) end_date = models.DateTimeField("date ended", null=True)
short_description = models.CharField(max_length=200, default="Cool kids have polls") 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.") 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) tags = models.ManyToManyField(Tag, blank=True)
def was_published_recently(self): def was_published_recently(self):
@ -157,6 +158,9 @@ class Question(models.Model):
# ! Most of the code from https://stackoverflow.com/a/70869267 # ! Most of the code from https://stackoverflow.com/a/70869267
def upvote(self, user): 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: try:
self.sentimentvote_set.create(user=user, question=self, vote_types=True) self.sentimentvote_set.create(user=user, question=self, vote_types=True)
self.save() self.save()
@ -170,6 +174,9 @@ class Question(models.Model):
return True return True
def downvote(self, user): 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: try:
self.sentimentvote_set.create(user=user, question=self, vote_types=False) self.sentimentvote_set.create(user=user, question=self, vote_types=False)
self.save() self.save()
@ -190,6 +197,38 @@ class Question(models.Model):
def down_vote_count(self): def down_vote_count(self):
return self.sentimentvote_set.filter(question=self, vote_types=False).count() 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): class Choice(models.Model):
""" """
@ -228,9 +267,25 @@ class Vote(models.Model):
# ! Most of the code from https://stackoverflow.com/a/70869267 # ! Most of the code from https://stackoverflow.com/a/70869267
class SentimentVote(models.Model): 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) user = models.ForeignKey(User, on_delete=models.CASCADE)
question = models.ForeignKey(Question, on_delete=models.CASCADE) question = models.ForeignKey(Question, on_delete=models.CASCADE)
vote_types = models.BooleanField() vote_types = models.BooleanField()
class Meta: 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'] unique_together = ['user', 'question']

View File

@ -83,7 +83,7 @@
<h2 class="text-2xl font-bold bg-gradient-to-r from-red-600 via-orange-600 to-yellow-600 bg-clip-text text-transparent lg:inline">Top Polls Today</h2> <h2 class="text-2xl font-bold bg-gradient-to-r from-red-600 via-orange-600 to-yellow-600 bg-clip-text text-transparent lg:inline">Top Polls Today</h2>
<hr class="h-px my-4 bg-gray-200 border-0 dark:bg-gray-400" /> <hr class="h-px my-4 bg-gray-200 border-0 dark:bg-gray-400" />
<div class="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3"> <div class="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3">
{% for question in latest_question_list %} {% for question in latest_question_list.trend_poll %}
<div class="relative"> <div class="relative">
<!-- INFO --> <!-- INFO -->
<div class="rounded-lg bg-white p-4 shadow-md border-solid border-2 border-yellow-500 relative z-10 transform translate-y-0 hover:translate-y-1 transition-transform"> <div class="rounded-lg bg-white p-4 shadow-md border-solid border-2 border-yellow-500 relative z-10 transform translate-y-0 hover:translate-y-1 transition-transform">
@ -129,7 +129,7 @@
<h2 class="mb-4 text-2xl font-bold">All Polls</h2> <h2 class="mb-4 text-2xl font-bold">All Polls</h2>
<hr class="h-px my-4 bg-gray-200 border-0 dark:bg-gray-400" /> <hr class="h-px my-4 bg-gray-200 border-0 dark:bg-gray-400" />
<div class="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-2 xl:grid-cols-3"> <div class="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-2 xl:grid-cols-3">
{% for question in latest_question_list %} {% for question in latest_question_list.all_poll %}
<div class="relative"> <div class="relative">
<!-- INFO --> <!-- INFO -->
<div class="rounded-lg bg-white p-4 shadow-md border-solid border-2 border-neutral-500 relative z-10 transform translate-y-0 hover:translate-y-1 transition-transform"> <div class="rounded-lg bg-white p-4 shadow-md border-solid border-2 border-neutral-500 relative z-10 transform translate-y-0 hover:translate-y-1 transition-transform">

View File

@ -12,7 +12,7 @@ class QuestionIndexViewTests(TestCase):
""" """
response = self.client.get(reverse("polls:index")) response = self.client.get(reverse("polls:index"))
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertQuerySetEqual(response.context["latest_question_list"], []) self.assertQuerySetEqual(response.context["latest_question_list"]["all_poll"], [])
def test_past_question(self): def test_past_question(self):
""" """
@ -24,7 +24,7 @@ class QuestionIndexViewTests(TestCase):
question.save() question.save()
response = self.client.get(reverse("polls:index")) response = self.client.get(reverse("polls:index"))
self.assertQuerySetEqual( self.assertQuerySetEqual(
response.context["latest_question_list"], response.context["latest_question_list"]["all_poll"],
[question], [question],
) )
@ -37,7 +37,7 @@ class QuestionIndexViewTests(TestCase):
future_question.pub_date = timezone.now() + timezone.timedelta(days=30) future_question.pub_date = timezone.now() + timezone.timedelta(days=30)
future_question.save() future_question.save()
response = self.client.get(reverse("polls:index")) response = self.client.get(reverse("polls:index"))
self.assertQuerySetEqual(response.context["latest_question_list"], []) self.assertQuerySetEqual(response.context["latest_question_list"]["all_poll"], [])
def test_future_question_and_past_question(self): def test_future_question_and_past_question(self):
""" """
@ -54,7 +54,7 @@ class QuestionIndexViewTests(TestCase):
response = self.client.get(reverse("polls:index")) response = self.client.get(reverse("polls:index"))
self.assertQuerySetEqual( self.assertQuerySetEqual(
response.context["latest_question_list"], response.context["latest_question_list"]["all_poll"],
[past_question], [past_question],
) )
@ -72,6 +72,6 @@ class QuestionIndexViewTests(TestCase):
response = self.client.get(reverse("polls:index")) response = self.client.get(reverse("polls:index"))
self.assertQuerySetEqual( self.assertQuerySetEqual(
response.context["latest_question_list"], response.context["latest_question_list"]["all_poll"],
[question2, question1], [question2, question1],
) )

View File

@ -52,7 +52,7 @@ class QuestionModelTests(TestCase):
response = self.client.get(reverse("polls:index")) response = self.client.get(reverse("polls:index"))
self.assertQuerySetEqual( self.assertQuerySetEqual(
response.context["latest_question_list"], response.context["latest_question_list"]["all_poll"],
[question], [question],
) )

View File

@ -0,0 +1,42 @@
from django.test import TransactionTestCase, Client
from django.contrib.auth.models import User
from .base import create_question
from ..views import up_down_vote
# ! https://stackoverflow.com/questions/24588520/testing-several-integrityerrors-in-the-same-django-unittest-test-case
# * https://stackoverflow.com/questions/44450533/difference-between-testcase-and-transactiontestcase-classes-in-django-test
class UpDownVoteViewTest(TransactionTestCase):
@classmethod
def setUp(cls) -> None:
cls.user = User.objects.create_user(username="test_user", password="12345abc")
cls.q1 = create_question(question_text="test 1")
cls.client = Client()
def test_vote_up_once(self):
self.client.login(username="test_user", password="12345abc")
self.q1.upvote(self.user)
self.assertFalse(self.q1.upvote(self.user))
def test_vote_down_once(self):
self.client.login(username="test_user", password="12345abc")
self.q1.downvote(self.user)
self.assertFalse(self.q1.downvote(self.user))
def test_can_change_up_to_down(self):
self.client.login(username="test_user", password="12345abc")
self.q1.upvote(self.user)
self.q1.downvote(self.user)
count_up = self.q1.sentimentvote_set.filter(vote_types=True).count()
count_down = self.q1.sentimentvote_set.filter(vote_types=False).count()
self.assertEqual(count_up, 0)
self.assertEqual(count_down, 1)
def test_can_change_up_to_down(self):
self.client.login(username="test_user", password="12345abc")
self.q1.downvote(self.user)
self.q1.upvote(self.user)
count_up = self.q1.sentimentvote_set.filter(vote_types=True).count()
count_down = self.q1.sentimentvote_set.filter(vote_types=False).count()
self.assertEqual(count_up, 1)
self.assertEqual(count_down, 0)

View File

@ -8,6 +8,7 @@ from django.utils import timezone
from django.urls import reverse_lazy, reverse from django.urls import reverse_lazy, reverse
from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib import messages from django.contrib import messages
from django.contrib.auth import authenticate, login
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.db.models import Q from django.db.models import Q
@ -29,10 +30,18 @@ class IndexView(generic.ListView):
Return the last published questions that is published and haven't ended yet. Return the last published questions that is published and haven't ended yet.
""" """
now = timezone.now() now = timezone.now()
return Question.objects.filter( all_poll_queryset = Question.objects.filter(
Q(pub_date__lte=now) & ((Q(end_date__gte=now) | Q(end_date=None))) Q(pub_date__lte=now) & ((Q(end_date__gte=now) | Q(end_date=None)))
).order_by("-pub_date") ).order_by("-pub_date")
trend_poll_queryset = Question.objects.filter(
Q(pub_date__lte=now) & ((Q(end_date__gte=now) | Q(end_date=None))) & Q(trend_score__gte=100)
).order_by("trend_score", "end_date")[:3]
queryset = {'all_poll' : all_poll_queryset,
'trend_poll' : trend_poll_queryset,}
return queryset
class DetailView(LoginRequiredMixin, generic.DetailView): class DetailView(LoginRequiredMixin, generic.DetailView):
""" """
@ -105,6 +114,13 @@ class SignUpView(generic.CreateView):
success_url = reverse_lazy('login') success_url = reverse_lazy('login')
template_name = 'registration/signup.html' template_name = 'registration/signup.html'
def form_valid(self, form):
valid = super(SignUpView, self).form_valid(form)
username, password = form.cleaned_data.get("username"), form.cleaned_data.get("password1")
user = authenticate(username=username, password=password)
login(self.request, user)
return valid
@login_required @login_required
def vote(request, question_id): def vote(request, question_id):