From 1b996629da12bb0287fd6c691fec49ecf00f7234 Mon Sep 17 00:00:00 2001 From: sosokker Date: Sat, 9 Sep 2023 21:20:23 +0700 Subject: [PATCH] Refine UI/UX - Add new Feature (Model) - Refine View 1. Refine UI/UX Apply Tailwinds Rewrite all page 2. Add new feature to model Add new field - description - long - short - use default + editable instead of auto_add_now - track up vote - Change timezone - Add time left track -Add participant -Add up/down vote attribute -- Choice - Add total_vote -Add calculate_percentage of vote 3. Refine View -Redirect instead of home page -Add context to result, detail view -Apply ChartJS -Delete home --- data/polls-v1.json | 136 +++++++++++- mysite/settings.py | 7 +- mysite/urls.py | 4 +- polls/admin.py | 2 + ...er_choice_votes_alter_question_pub_date.py | 24 +++ .../0004_alter_question_pub_date.py | 19 ++ ...ion_question_short_description_and_more.py | 28 +++ ..._down_vote_count_question_up_vote_count.py | 24 +++ .../0007_question_participant_count.py | 19 ++ polls/models.py | 90 +++++++- polls/static/polls/base.css | 196 +----------------- polls/templates/polls/base.html | 40 ++-- polls/templates/polls/detail.html | 120 +++++++++-- polls/templates/polls/home.html | 26 --- polls/templates/polls/index.html | 106 ++++++++-- polls/templates/polls/results.html | 120 ++++++++++- polls/tests.py | 2 - polls/views.py | 61 +++--- 18 files changed, 694 insertions(+), 330 deletions(-) create mode 100644 polls/migrations/0003_alter_choice_votes_alter_question_pub_date.py create mode 100644 polls/migrations/0004_alter_question_pub_date.py create mode 100644 polls/migrations/0005_question_long_description_question_short_description_and_more.py create mode 100644 polls/migrations/0006_question_down_vote_count_question_up_vote_count.py create mode 100644 polls/migrations/0007_question_participant_count.py delete mode 100644 polls/templates/polls/home.html diff --git a/data/polls-v1.json b/data/polls-v1.json index c120753..1c95e50 100644 --- a/data/polls-v1.json +++ b/data/polls-v1.json @@ -4,7 +4,13 @@ "pk": 1, "fields": { "question_text": "Python vs C++, which one is better in your opinion?", - "pub_date": "2023-08-28T13:38:42Z" + "short_description": "Cool kids have polls", + "long_description": "No description provide for this poll.", + "pub_date": "2023-08-28T13:38:42Z", + "end_date": "2025-09-24T22:56:52Z", + "up_vote_count": 0, + "down_vote_count": 0, + "participant_count": 0 } }, { @@ -12,7 +18,55 @@ "pk": 2, "fields": { "question_text": "The chicken and the egg, which came first?", - "pub_date": "2023-08-28T13:39:16Z" + "short_description": "Cool kids have polls", + "long_description": "No description provide for this poll.", + "pub_date": "2023-08-28T13:39:16Z", + "end_date": "2023-11-05T06:30:16Z", + "up_vote_count": 12, + "down_vote_count": 4, + "participant_count": 11 + } +}, +{ + "model": "polls.question", + "pk": 3, + "fields": { + "question_text": "Do you love Django?", + "short_description": "Cool kids have polls", + "long_description": "No description provide for this poll.", + "pub_date": "2023-11-07T15:40:31Z", + "end_date": "2023-09-20T13:41:33Z", + "up_vote_count": 3000, + "down_vote_count": 50, + "participant_count": 555 + } +}, +{ + "model": "polls.question", + "pk": 4, + "fields": { + "question_text": "So far so good?", + "short_description": "Cool kids have polls", + "long_description": "No description provide for this poll.", + "pub_date": "2023-09-09T06:30:50.690Z", + "end_date": "2023-09-30T12:54:30Z", + "up_vote_count": 0, + "down_vote_count": 0, + "participant_count": 234 + } +}, +{ + "model": "polls.question", + "pk": 5, + "fields": { + "question_text": "Do you love Django?", + "short_description": "Cool kids have polls", + "long_description": "No description provide for this poll.", + "pub_date": "2023-09-09T13:42:27.501Z", + "end_date": "2023-09-21T13:42:17Z", + "up_vote_count": 1, + "down_vote_count": 1, + "participant_count": 1 } }, { @@ -21,7 +75,7 @@ "fields": { "question": 2, "choice_text": "Chicken", - "votes": 5 + "votes": 55 } }, { @@ -30,7 +84,7 @@ "fields": { "question": 2, "choice_text": "Egg", - "votes": 2 + "votes": 56 } }, { @@ -39,7 +93,7 @@ "fields": { "question": 1, "choice_text": "Python!", - "votes": 2 + "votes": 6 } }, { @@ -48,6 +102,78 @@ "fields": { "question": 1, "choice_text": "C++", + "votes": 3 + } +}, +{ + "model": "polls.choice", + "pk": 8, + "fields": { + "question": 3, + "choice_text": "Yeah for sure!", + "votes": 0 + } +}, +{ + "model": "polls.choice", + "pk": 9, + "fields": { + "question": 3, + "choice_text": "nah", + "votes": 0 + } +}, +{ + "model": "polls.choice", + "pk": 13, + "fields": { + "question": 4, + "choice_text": "Yes!", + "votes": 5 + } +}, +{ + "model": "polls.choice", + "pk": 14, + "fields": { + "question": 4, + "choice_text": "No!", + "votes": 4 + } +}, +{ + "model": "polls.choice", + "pk": 15, + "fields": { + "question": 4, + "choice_text": "Okay!", + "votes": 5 + } +}, +{ + "model": "polls.choice", + "pk": 16, + "fields": { + "question": 4, + "choice_text": "Thank you!", + "votes": 2 + } +}, +{ + "model": "polls.choice", + "pk": 17, + "fields": { + "question": 5, + "choice_text": "Yeah for sure!", + "votes": 2 + } +}, +{ + "model": "polls.choice", + "pk": 18, + "fields": { + "question": 5, + "choice_text": "nah", "votes": 2 } } diff --git a/mysite/settings.py b/mysite/settings.py index 3906c9f..d4d41bc 100644 --- a/mysite/settings.py +++ b/mysite/settings.py @@ -26,10 +26,15 @@ SECRET_KEY = config('SECRET_KEY', default='k2pd1p)zwe0qy0k25=sli+7+n^vd-0h*&6vga #! SECURITY WARNING: don't run with debug turned on in production! DEBUG = config('DEBUG', default=False, cast=bool) +LANGUAGE_CODE = 'en-us' + ALLOWED_HOSTS = config('ALLOWED_HOSTS', default='*', cast=Csv()) -TIME_ZONE = config('TIME_ZONE', default='Asia/Bangkok', cast=str) +TIME_ZONE = config('TIME_ZONE', default='UTC', cast=str) +USE_I18N = True + +USE_TZ = True # Application definition INSTALLED_APPS = [ diff --git a/mysite/urls.py b/mysite/urls.py index b5d5113..1b67d3f 100644 --- a/mysite/urls.py +++ b/mysite/urls.py @@ -1,10 +1,10 @@ from django.contrib import admin from django.urls import include, path -from polls.views import HomeView +from django.views.generic import RedirectView urlpatterns = [ - path('', HomeView.as_view(), name='home'), + path('', RedirectView.as_view(pattern_name='polls:index'), name='home_redirect'), path("polls/", include("polls.urls")), path('admin/', admin.site.urls), ] diff --git a/polls/admin.py b/polls/admin.py index 0580846..2fc596f 100644 --- a/polls/admin.py +++ b/polls/admin.py @@ -12,6 +12,8 @@ class QuestionAdmin(admin.ModelAdmin): fieldsets = [ (None, {"fields": ["question_text"]}), ("End date", {"fields": ["end_date"], "classes": ["collapse"]}), + ("Vote count", {"fields": ["up_vote_count", "down_vote_count"]}), + ("Participant count", {"fields": ["participant_count"]}), ] list_display = ["question_text", "pub_date", "end_date", "was_published_recently"] inlines = [ChoiceInline] diff --git a/polls/migrations/0003_alter_choice_votes_alter_question_pub_date.py b/polls/migrations/0003_alter_choice_votes_alter_question_pub_date.py new file mode 100644 index 0000000..434fa51 --- /dev/null +++ b/polls/migrations/0003_alter_choice_votes_alter_question_pub_date.py @@ -0,0 +1,24 @@ +# Generated by Django 4.2.4 on 2023-09-09 05:27 + +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('polls', '0002_question_end_date_alter_question_pub_date'), + ] + + operations = [ + migrations.AlterField( + model_name='choice', + name='votes', + field=models.PositiveIntegerField(default=0, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(2147483647)]), + ), + migrations.AlterField( + model_name='question', + name='pub_date', + field=models.DateTimeField(auto_now_add=True, verbose_name='date published'), + ), + ] diff --git a/polls/migrations/0004_alter_question_pub_date.py b/polls/migrations/0004_alter_question_pub_date.py new file mode 100644 index 0000000..ed28b91 --- /dev/null +++ b/polls/migrations/0004_alter_question_pub_date.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.4 on 2023-09-09 08:15 + +from django.db import migrations, models +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('polls', '0003_alter_choice_votes_alter_question_pub_date'), + ] + + operations = [ + migrations.AlterField( + model_name='question', + name='pub_date', + field=models.DateTimeField(default=django.utils.timezone.now, editable=False, verbose_name='date published'), + ), + ] diff --git a/polls/migrations/0005_question_long_description_question_short_description_and_more.py b/polls/migrations/0005_question_long_description_question_short_description_and_more.py new file mode 100644 index 0000000..35299d7 --- /dev/null +++ b/polls/migrations/0005_question_long_description_question_short_description_and_more.py @@ -0,0 +1,28 @@ +# Generated by Django 4.2.4 on 2023-09-09 08:29 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('polls', '0004_alter_question_pub_date'), + ] + + operations = [ + migrations.AddField( + model_name='question', + name='long_description', + field=models.TextField(default='No description provide for this poll.', max_length=2000), + ), + migrations.AddField( + model_name='question', + name='short_description', + field=models.CharField(default='Cool kids have polls', max_length=200), + ), + migrations.AlterField( + model_name='question', + name='question_text', + field=models.CharField(max_length=100), + ), + ] diff --git a/polls/migrations/0006_question_down_vote_count_question_up_vote_count.py b/polls/migrations/0006_question_down_vote_count_question_up_vote_count.py new file mode 100644 index 0000000..2b70532 --- /dev/null +++ b/polls/migrations/0006_question_down_vote_count_question_up_vote_count.py @@ -0,0 +1,24 @@ +# Generated by Django 4.2.4 on 2023-09-09 08:33 + +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('polls', '0005_question_long_description_question_short_description_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='question', + name='down_vote_count', + field=models.PositiveIntegerField(default=0, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(2147483647)]), + ), + migrations.AddField( + model_name='question', + name='up_vote_count', + field=models.PositiveIntegerField(default=0, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(2147483647)]), + ), + ] diff --git a/polls/migrations/0007_question_participant_count.py b/polls/migrations/0007_question_participant_count.py new file mode 100644 index 0000000..df81db9 --- /dev/null +++ b/polls/migrations/0007_question_participant_count.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.4 on 2023-09-09 08:36 + +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('polls', '0006_question_down_vote_count_question_up_vote_count'), + ] + + operations = [ + migrations.AddField( + model_name='question', + name='participant_count', + field=models.PositiveIntegerField(default=0, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(2147483647)]), + ), + ] diff --git a/polls/models.py b/polls/models.py index f765548..313d213 100644 --- a/polls/models.py +++ b/polls/models.py @@ -9,12 +9,11 @@ Attributes: None """ -import datetime - from django.db import models from django.utils import timezone from django.contrib import admin from django.core.validators import MaxValueValidator, MinValueValidator +from django.db.models import Sum class Question(models.Model): @@ -26,9 +25,14 @@ class Question(models.Model): pub_date (datetime): The date and time when the question was published. """ - question_text = models.CharField(max_length=200) - pub_date = models.DateTimeField("date published", auto_now_add=True) + question_text = models.CharField(max_length=100) + 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.") + pub_date = models.DateTimeField("date published", default=timezone.now, editable=False) end_date = models.DateTimeField("date ended", null=True) + up_vote_count = models.PositiveIntegerField(default=0, validators=[MinValueValidator(0), MaxValueValidator(2147483647)]) + down_vote_count = models.PositiveIntegerField(default=0, validators=[MinValueValidator(0), MaxValueValidator(2147483647)]) + participant_count = models.PositiveIntegerField(default=0, validators=[MinValueValidator(0), MaxValueValidator(2147483647)]) def was_published_recently(self): """ @@ -38,7 +42,7 @@ class Question(models.Model): bool: True if the question was published within the last day, else False. """ now = timezone.now() - return now - datetime.timedelta(days=1) <= self.pub_date <= now + return now - timezone.timedelta(days=1) <= self.pub_date <= now @admin.display( boolean=True, @@ -47,7 +51,7 @@ class Question(models.Model): ) def was_published_recently(self): now = timezone.now() - return now - datetime.timedelta(days=1) <= self.pub_date <= now + return now - timezone.timedelta(days=1) <= self.pub_date <= now def __str__(self): """ @@ -78,6 +82,57 @@ class Question(models.Model): else: return self.pub_date <= now <= self.end_date + def calculate_time_left(self): + """ + Calculate the time left until the end date. + + Returns: + str: A formatted string representing the time left. + """ + if self.end_date is None: + return "No end date" + + now = timezone.now() + time_left = self.end_date - now + + days, seconds = divmod(time_left.total_seconds(), 86400) + hours, seconds = divmod(seconds, 3600) + minutes, seconds = divmod(seconds, 60) + + time_left_str = "" + if days > 0: + time_left_str += f"{int(days)} D " + elif hours > 0: + time_left_str += f"{int(hours)} H " + elif minutes > 0: + time_left_str += f"{int(minutes)} M " + elif seconds > 0: + time_left_str += f"{int(seconds)} S " + + return time_left_str.strip() + + @property + def time_left(self): + return self.calculate_time_left() + + def calculate_vote_percentage(self): + total_vote = self.up_vote_count + self.down_vote_count + if total_vote == 0: + return (0, 0) + + up_vote_percentage = self.up_vote_count / total_vote * 100 + down_vote_percentage = self.down_vote_count / total_vote * 100 + + return (int(up_vote_percentage), int(down_vote_percentage)) + + @property + def up_vote_percentage(self): + return self.calculate_vote_percentage()[0] + + @property + def down_vote_percentage(self): + return self.calculate_vote_percentage()[1] + class Choice(models.Model): """ @@ -93,6 +148,29 @@ class Choice(models.Model): choice_text = models.CharField(max_length=200) votes = models.PositiveIntegerField(default=0, validators=[MinValueValidator(0), MaxValueValidator(2147483647)]) + def tailwind_width_class(self): + """ + Calculate and return the Tailwind CSS width class based on the 'votes' percentage. + """ + total_votes = self.question.choice_set.aggregate(Sum('votes')).get('votes__sum', 0) + #! Tailwind w-0 to w-48 + if total_votes == 0: + return 'w-0' + + ratio = self.votes / total_votes + + scaled_value = ratio * 48 + + return f'w-{int(round(scaled_value))}' + + def calculate_percentage(self): + total_votes_for_question = self.question.choice_set.aggregate(Sum('votes'))['votes__sum'] or 0 + + if total_votes_for_question == 0: + return 0 + else: + return round((self.votes / total_votes_for_question) * 100, 2) + def __str__(self): """ Returns a string representation of the choice. diff --git a/polls/static/polls/base.css b/polls/static/polls/base.css index afd26f9..3186c50 100644 --- a/polls/static/polls/base.css +++ b/polls/static/polls/base.css @@ -1,191 +1,9 @@ /*! NAVBAR */ -header { - background-color: #1C1C1C; - color: #fff; - padding: 20px; - text-align: center; - display: flex; - justify-content: center; - align-items: center; - box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1); - border-radius: 10px; -} - -.nav-container { - display: flex; - justify-content: center; - align-items: center; -} - -.nav-left h1 a { - text-decoration: none; - color: #fff; - font-size: 24px; -} - - -.nav-right ul { - list-style: none; - padding: 0; - margin: 0; - display: flex; - align-items: center; -} - -.nav-right li { - margin: 0 10px; -} - -.nav-right a { - text-decoration: none; - color: #fff; - font-weight: bold; - padding: 10px 20px; - border: 2px solid #fff; - border-radius: 5px; - transition: background-color 0.3s ease, color 0.3s ease; -} - -.nav-right a:hover { - background-color: #fff; - color: #007bff; -} - -/*! HOME AND POLL CARD */ - -.hero-section { - background-size: cover; - background-position: center; - text-align: center; - color: #1c1c1c; -} - -.hero-content { - max-width: 800px; - margin: 0 auto; -} - -h1 { - font-size: 36px; - margin-bottom: 20px; -} - -.polls-section { - background-size: cover; - background-position: center; - text-align: center; - color: #1c1c1c; -} - -.poll-cards { - display: flex; - flex-wrap: wrap; - gap: 20px; - justify-content: center; - margin-top: 30px; -} - -.poll-card { - background-color: #fff; - border: 1px solid #e0e0e0; - padding: 20px; - border-radius: 5px; - box-shadow: 0px 3px 6px rgba(0, 0, 0, 0.1); - transition: transform 0.3s ease; - cursor: pointer; -} - -.poll-card:hover { - transform: translateY(-5px); -} - -/*! DETAILED */ - -.poll-details { - padding: 30px; - background-color: #fff; - border-radius: 10px; - box-shadow: 0px 3px 6px rgba(0, 0, 0, 0.1); -} - -.poll-form { - text-align: center; -} - -.poll-question { - font-size: 24px; - margin-bottom: 20px; -} - -.error-message { - color: red; - margin-bottom: 10px; -} - -.choice { - display: flex; - align-items: center; - margin: 10px 0; -} - -.choice input[type="radio"] { - margin-right: 10px; -} - -.choice-text { - font-size: 18px; -} - -.vote-button { - background-color: #007bff; - color: #fff; - padding: 10px 20px; - border: none; - border-radius: 5px; - cursor: pointer; - transition: background-color 0.3s ease, color 0.3s ease; -} - -.vote-button:hover { - background-color: #0056b3; -} - -/*! RESULT */ - -.poll-results { - text-align: center; - padding: 30px; - background-color: #fff; - border-radius: 10px; - box-shadow: 0px 3px 6px rgba(0, 0, 0, 0.1); -} - -.poll-question { - font-size: 24px; - margin-bottom: 20px; -} - -.choice-list { - list-style: none; - padding: 0; - margin: 20px 0; - text-align: left; -} - -.choice-item { - font-size: 18px; - margin: 10px 0; -} - -.vote-again { - display: inline-block; - margin-top: 20px; - text-decoration: none; - color: #007bff; - transition: color 0.3s ease; -} - -.vote-again:hover { - color: #0056b3; -} \ No newline at end of file +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 700; + src: local('Open Sans Extrabold'), local('OpenSans-Extrabold'), + url('https://fonts.googleapis.com/css2?family=Kanit&family=Open+Sans&family=Roboto:ital@1&display=swap') format('truetype'); + } \ No newline at end of file diff --git a/polls/templates/polls/base.html b/polls/templates/polls/base.html index ae54f87..7987a04 100644 --- a/polls/templates/polls/base.html +++ b/polls/templates/polls/base.html @@ -1,25 +1,17 @@ {% load static %} - - - MySite Polls - - - - -
-
- - -
- {% block content %} - {% endblock content %} -
- - + + + + + + + + + + Your Poll Website + + + {% block content %} + {% endblock content %} + + \ No newline at end of file diff --git a/polls/templates/polls/detail.html b/polls/templates/polls/detail.html index 8ca20b6..20fe9e2 100644 --- a/polls/templates/polls/detail.html +++ b/polls/templates/polls/detail.html @@ -2,23 +2,109 @@ {% block content %}
-
-
- {% csrf_token %} -
- {{ question.question_text }} - {% if error_message %} -

{{ error_message }}

- {% endif %} - {% for choice in question.choice_set.all %} -
- - + +
+

{{ question_text }}

+
+

{{ long_description }}

+ + +
+
+ πŸ‘€ {{ participant_count }} Participants + πŸ‘ {{ question.up_vote_percentage }}% Upvoted + πŸ‘Ž {{ question.down_vote_percentage }}% Downvoted +
+
+ + +
+

Which one would you prefer:

+ +
+ + {% csrf_token %} +
+
Please Select a Choice😊
- {% endfor %} -
- -
-
+ {% if error_message %} +
+

{{ error_message }}

+
+
+ + {% for choice in question.choice_set.all %} + + {% endfor %} +
+ + + +
+ + Back to Polls + + + + +
+ + + +
+ + {% endblock content %} diff --git a/polls/templates/polls/home.html b/polls/templates/polls/home.html deleted file mode 100644 index 14a995a..0000000 --- a/polls/templates/polls/home.html +++ /dev/null @@ -1,26 +0,0 @@ -{% extends 'polls/base.html' %} - -{% block content %} -
-
-
-

Welcome to KU Polls

-

Explore and participate in our weird poll questions.

-
-
-
-

Recent Polls

-

Total number of polls: {{ total_open_polls }}

-
- {% if latest_question_list %} - {% for question in latest_question_list %} - - {% endfor %} - {% else %} -

No polls are available.

- {% endif %} -
-
-{% endblock content %} diff --git a/polls/templates/polls/index.html b/polls/templates/polls/index.html index f07875d..94ca849 100644 --- a/polls/templates/polls/index.html +++ b/polls/templates/polls/index.html @@ -1,19 +1,97 @@ {% extends 'polls/base.html' %} {% block content %} -
-

Recent Polls

-
- {% if latest_question_list %} - {% for question in latest_question_list %} -
-

{{ question.question_text }}

-

Published on: {{ question.pub_date|date:"F j, Y" }}

-
- {% endfor %} - {% else %} -

No polls are available.

- {% endif %} +
+ + + + + -
+
{% endblock content %} diff --git a/polls/templates/polls/results.html b/polls/templates/polls/results.html index dd55c8d..3415e92 100644 --- a/polls/templates/polls/results.html +++ b/polls/templates/polls/results.html @@ -2,16 +2,116 @@ {% block content %}
-
-
-

{{ question.question_text }}

-
-
    + +
    +

    {{ question.question_text }}

    +
    + + +
    +

    Result Summary:

    + {% for choice in question.choice_set.all %} -
  • {{ choice.choice_text }} β€” {{ choice.votes }} vote{{ choice.votes|pluralize }}
  • +
    + {{ choice.choice_text }} +
    + πŸ‘ {{ choice.votes }} +
    +
    +
    +
    +
    {% endfor %} -
- Vote again? -
+ + + +
+ +
+

πŸ•΅οΈ Statistics

+ {{ question.participant_count }} Participants πŸ‘€ + πŸ‘ {{ question.up_vote_percentage }}% + πŸ‘Ž {{ question.down_vote_percentage }}% +
+ + +
+

πŸ‘‹ Vote Percentage

+
+ +
+
+ + +
+

πŸ‘ Vote Count

+
+ +
+
+
+ + + + Back to Polls + +
-{% endblock content %} + + +{% endblock content %} \ No newline at end of file diff --git a/polls/tests.py b/polls/tests.py index 9141b10..187c2ab 100644 --- a/polls/tests.py +++ b/polls/tests.py @@ -124,7 +124,6 @@ class QuestionIndexViewTests(TestCase): """ response = self.client.get(reverse("polls:index")) self.assertEqual(response.status_code, 200) - self.assertContains(response, "No polls are available.") self.assertQuerySetEqual(response.context["latest_question_list"], []) def test_past_question(self): @@ -150,7 +149,6 @@ class QuestionIndexViewTests(TestCase): future_question.pub_date = timezone.now() + timezone.timedelta(days=30) future_question.save() response = self.client.get(reverse("polls:index")) - self.assertContains(response, "No polls are available.") self.assertQuerySetEqual(response.context["latest_question_list"], []) def test_future_question_and_past_question(self): diff --git a/polls/views.py b/polls/views.py index 4c058fa..e28c069 100644 --- a/polls/views.py +++ b/polls/views.py @@ -8,42 +8,15 @@ from django.views.generic import TemplateView from .models import Choice, Question -class HomeView(TemplateView): - """ - Provide a view for Home page(first page). - """ - - template_name = 'polls/home.html' - - def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) - all_questions = Question.objects.all() - #* Check if the question is published and can be voted. Then, sort by pub_date - published_questions = [q for q in all_questions if q.is_published() and q.can_vote()] - latest_published_questions = sorted(published_questions, key=lambda q: q.pub_date, reverse=True)[:5] - - context['latest_question_list'] = latest_published_questions - context['total_open_polls'] = sum(1 for q in published_questions if q.end_date is None) - context['total_polls'] = all_questions.count() - return context - - class IndexView(generic.ListView): - """ - Provide a view for Index page that list all polls. - """ + """View for index.html.""" template_name = "polls/index.html" context_object_name = "latest_question_list" def get_queryset(self): - """ - Return the last five published questions (not including those set to be - published in the future). - """ - return Question.objects.filter(pub_date__lte=timezone.now()).order_by("-pub_date")[ - :5 - ] + """Return the last five published questions.""" + return Question.objects.filter(pub_date__lte=timezone.now()).order_by("-pub_date")[:5] class DetailView(generic.DetailView): @@ -61,15 +34,35 @@ class DetailView(generic.DetailView): """ return Question.objects.filter(pub_date__lte=timezone.now()) + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + + question = self.object + + context['question_text'] = question.question_text + context['short_description'] = question.short_description + context['long_description'] = question.long_description + context['pub_date'] = question.pub_date + context['end_date'] = question.end_date + context['up_vote_count'] = question.up_vote_count + context['down_vote_count'] = question.down_vote_count + context['participant_count'] = question.participant_count + + return context + class ResultsView(generic.DetailView): - """ - Provide a view for result page that show up when user submit on of the choices. - """ - model = Question template_name = "polls/results.html" + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context['question'] = self.object + return context + + def render_to_response(self, context, **response_kwargs): + return render(self.request, self.template_name, context) + def vote(request, question_id): """