From e0c7995f28794fa4f7ddf09bb73fe36bc02ab538 Mon Sep 17 00:00:00 2001 From: sosokker Date: Tue, 5 Sep 2023 21:25:04 +0700 Subject: [PATCH 1/3] Add is_published, can_vote to QuestionModel/ Add test for can_vote, is_published is_published -> Check if the specific question able to be published can_vote -> Can vote or not, check it from pub time Delete some unnecessary files --- ...estion_end_date_alter_question_pub_date.py | 24 ++++++++ polls/models.py | 25 +++++++- polls/static/polls/style.css | 0 polls/templates/polls/home.html | 2 +- polls/tests.py | 58 ++++++++++++++++++ polls/views.py | 10 ++- requirements.txt | Bin 174 -> 72 bytes 7 files changed, 115 insertions(+), 4 deletions(-) create mode 100644 polls/migrations/0002_question_end_date_alter_question_pub_date.py delete mode 100644 polls/static/polls/style.css diff --git a/polls/migrations/0002_question_end_date_alter_question_pub_date.py b/polls/migrations/0002_question_end_date_alter_question_pub_date.py new file mode 100644 index 0000000..56fe44f --- /dev/null +++ b/polls/migrations/0002_question_end_date_alter_question_pub_date.py @@ -0,0 +1,24 @@ +# Generated by Django 4.2.4 on 2023-09-05 13:47 + +from django.db import migrations, models +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('polls', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='question', + name='end_date', + field=models.DateTimeField(null=True, verbose_name='date ended'), + ), + migrations.AlterField( + model_name='question', + name='pub_date', + field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='date published'), + ), + ] diff --git a/polls/models.py b/polls/models.py index 7252b32..4e42bb7 100644 --- a/polls/models.py +++ b/polls/models.py @@ -26,7 +26,8 @@ class Question(models.Model): """ question_text = models.CharField(max_length=200) - pub_date = models.DateTimeField("date published") + pub_date = models.DateTimeField("date published", default=timezone.now) + end_date = models.DateTimeField("date ended", null=True) def was_published_recently(self): """ @@ -53,6 +54,28 @@ class Question(models.Model): """ return self.question_text + def is_published(self): + """ + Checks if the question is published or not. + + Returns: + bool: True if the question is published, else False. + """ + now = timezone.now() + return now >= self.pub_date + + def can_vote(self): + """ + Checks if the question can be voted on or not. + + Returns: + bool: True if the question is published and not ended, else False. + """ + now = timezone.now() + if self.end_date is None: + return self.pub_date <= now + return self.pub_date <= now <= self.end_date + class Choice(models.Model): """ diff --git a/polls/static/polls/style.css b/polls/static/polls/style.css deleted file mode 100644 index e69de29..0000000 diff --git a/polls/templates/polls/home.html b/polls/templates/polls/home.html index ad8a1c8..14a995a 100644 --- a/polls/templates/polls/home.html +++ b/polls/templates/polls/home.html @@ -10,7 +10,7 @@

Recent Polls

-

Total number of polls: {{ total_polls }}

+

Total number of polls: {{ total_open_polls }}

{% if latest_question_list %} {% for question in latest_question_list %} diff --git a/polls/tests.py b/polls/tests.py index d740da3..fb49a5d 100644 --- a/polls/tests.py +++ b/polls/tests.py @@ -37,6 +37,64 @@ class QuestionModelTests(TestCase): self.assertIs(recent_question.was_published_recently(), True) + def test_is_published_with_future_question(self): + """ + is_published() should return False for questions whos pub_date is in the + future. + """ + future_date = timezone.now() + datetime.timedelta(days=30) + future_question = Question(pub_date=future_date) + self.assertIs(future_question.is_published(), False) + + def test_is_published_with_past_question(self): + """ + is_published() should return True for questions whose pub_date is in the + past. + """ + past_date = timezone.now() - datetime.timedelta(days=1) + past_question = Question(pub_date=past_date) + self.assertIs(past_question.is_published(), True) + + def test_can_vote_with_question_not_ended(self): + """ + can_vote() should return True for questions that are published and have not + ended. + """ + pub_date = timezone.now() - datetime.timedelta(hours=1) + end_date = timezone.now() + datetime.timedelta(hours=1) + question = Question(pub_date=pub_date, end_date=end_date) + self.assertIs(question.can_vote(), True) + + def test_can_vote_with_question_ended(self): + """ + can_vote() should return False for questions that are published but have + ended. + """ + pub_date = timezone.now() - datetime.timedelta(hours=2) + end_date = timezone.now() - datetime.timedelta(hours=1) + question = Question(pub_date=pub_date, end_date=end_date) + self.assertIs(question.can_vote(), False) + + def test_can_vote_with_question_no_end_date(self): + """ + can_vote() should return True for questions that are published and have no + specified end date. + """ + pub_date = timezone.now() - datetime.timedelta(hours=1) + question = Question(pub_date=pub_date, end_date=None) + self.assertIs(question.can_vote(), True) + + def test_can_vote_with_question_ending_in_future(self): + """ + can_vote() should return False for questions that are published but have + an end date in the future. + """ + pub_date = timezone.now() - datetime.timedelta(hours=1) + end_date = timezone.now() + datetime.timedelta(hours=2) + question = Question(pub_date=pub_date, end_date=end_date) + self.assertIs(question.can_vote(), False) + + def create_question(question_text, days): """ Create a question with the given `question_text` and published the diff --git a/polls/views.py b/polls/views.py index 6180804..4c058fa 100644 --- a/polls/views.py +++ b/polls/views.py @@ -17,8 +17,14 @@ class HomeView(TemplateView): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - context['latest_question_list'] = Question.objects.filter(pub_date__lte=timezone.now()).order_by("-pub_date")[:5] - context['total_polls'] = Question.objects.count() + 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 diff --git a/requirements.txt b/requirements.txt index 63393f6a378f095aa05fec6953618e6a51c54249..08e2adfaea1b34370a037d2f723bc67cc16d5632 100644 GIT binary patch delta 33 ocmZ3-=)wR0p9@14Ln1>SLpnn~gWW`ZeJ(o&TLxnWJq8N~0Ha<9q5uE@ literal 174 zcmXwzK@NgI3`O7Cgri6%%p|&T=S3!>f+08pF68jYrxVgNY5LmN{+Gzela_%y4=xH% z$w^sJaNx>|NUxc<(O{$`7)|_`XvBJs)N Date: Tue, 5 Sep 2023 21:44:19 +0700 Subject: [PATCH 2/3] Externalize DEBUG, TIMEZONE, ALLOW_HOST and Add Install instruction in README.md --- README.md | 2 ++ mysite/settings.py | 13 +++++++------ sample.env | 10 ++++++++++ 3 files changed, 19 insertions(+), 6 deletions(-) create mode 100644 sample.env diff --git a/README.md b/README.md index 26841a6..fb28b26 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,8 @@ or **Don't forget to change `your_secret_key` to your secret key (without quote)** +**You can look at `sample.env` for more information and others environment variables to set.** + 4. Run these commands ```bash python manage.py migrate diff --git a/mysite/settings.py b/mysite/settings.py index 7a27f5c..3906c9f 100644 --- a/mysite/settings.py +++ b/mysite/settings.py @@ -11,7 +11,7 @@ https://docs.djangoproject.com/en/4.2/ref/settings/ """ from pathlib import Path -from decouple import config +from decouple import config, Csv # Build paths inside the project like this: BASE_DIR / 'subdir'. BASE_DIR = Path(__file__).resolve().parent.parent @@ -20,14 +20,15 @@ BASE_DIR = Path(__file__).resolve().parent.parent # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/4.2/howto/deployment/checklist/ -# SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = config('SECRET_KEY', default='fake-secret-key') +#! SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = config('SECRET_KEY', default='k2pd1p)zwe0qy0k25=sli+7+n^vd-0h*&6vga6oldq=781+7qw') -# SECURITY WARNING: don't run with debug turned on in production! -DEBUG = True +#! SECURITY WARNING: don't run with debug turned on in production! +DEBUG = config('DEBUG', default=False, cast=bool) -ALLOWED_HOSTS = [] +ALLOWED_HOSTS = config('ALLOWED_HOSTS', default='*', cast=Csv()) +TIME_ZONE = config('TIME_ZONE', default='Asia/Bangkok', cast=str) # Application definition diff --git a/sample.env b/sample.env new file mode 100644 index 0000000..c5af6b1 --- /dev/null +++ b/sample.env @@ -0,0 +1,10 @@ +# Copy this file to .env and edit the values +# Create a secret key using (todo: how to create a secret key?) +SECRET_KEY = secret-key-value-without-quotes +# Set DEBUG to True for development, False for actual use +DEBUG = False +# ALLOWED_HOSTS is a comma-separated list of hosts that can access the app. +# You can use wildcard chars (*) and IP addresses. Use * for any host. +ALLOWED_HOSTS = *.ku.th, localhost, 127.0.0.1, ::1 +# Your timezone +TIME_ZONE = Asia/Bangkok \ No newline at end of file From 215be4d95c2d80cf4db70b37945c79b62af25e09 Mon Sep 17 00:00:00 2001 From: sosokker Date: Tue, 5 Sep 2023 21:55:41 +0700 Subject: [PATCH 3/3] Modify test case and logic of can_vote in Question model --- polls/models.py | 3 ++- polls/tests.py | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/polls/models.py b/polls/models.py index 4e42bb7..63e2c0d 100644 --- a/polls/models.py +++ b/polls/models.py @@ -74,7 +74,8 @@ class Question(models.Model): now = timezone.now() if self.end_date is None: return self.pub_date <= now - return self.pub_date <= now <= self.end_date + else: + return self.pub_date <= now <= self.end_date class Choice(models.Model): diff --git a/polls/tests.py b/polls/tests.py index fb49a5d..d378745 100644 --- a/polls/tests.py +++ b/polls/tests.py @@ -86,13 +86,13 @@ class QuestionModelTests(TestCase): def test_can_vote_with_question_ending_in_future(self): """ - can_vote() should return False for questions that are published but have - an end date in the future. + can_vote() should return True for questions that are published and + the current time is within the allowed voting period. """ pub_date = timezone.now() - datetime.timedelta(hours=1) end_date = timezone.now() + datetime.timedelta(hours=2) question = Question(pub_date=pub_date, end_date=end_date) - self.assertIs(question.can_vote(), False) + self.assertIs(question.can_vote(), True) def create_question(question_text, days):