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/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..63e2c0d 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,29 @@ 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 + else: + 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..d378745 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 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(), True) + + 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 63393f6..08e2adf 100644 Binary files a/requirements.txt and b/requirements.txt differ 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