diff --git a/mysite/settings.py b/mysite/settings.py index d4d41bc..572a80b 100644 --- a/mysite/settings.py +++ b/mysite/settings.py @@ -26,15 +26,8 @@ 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='UTC', cast=str) - -USE_I18N = True - -USE_TZ = True # Application definition INSTALLED_APPS = [ @@ -113,7 +106,7 @@ AUTH_PASSWORD_VALIDATORS = [ LANGUAGE_CODE = 'en-us' -TIME_ZONE = 'UTC' +TIME_ZONE = config('TIME_ZONE', default='UTC', cast=str) USE_I18N = True @@ -124,6 +117,9 @@ USE_TZ = True # https://docs.djangoproject.com/en/4.2/howto/static-files/ STATIC_URL = '/static/' +# ! On production -> Configure your web server to serve the files in STATIC_ROOT under the URL STATIC_URL +# * https://docs.djangoproject.com/en/4.2/howto/static-files/deployment/ +STATIC_ROOT = BASE_DIR / "assets" STATICFILES_DIRS = [BASE_DIR] # Default primary key field type diff --git a/polls/admin.py b/polls/admin.py index 2fc596f..ee1e244 100644 --- a/polls/admin.py +++ b/polls/admin.py @@ -11,6 +11,7 @@ class ChoiceInline(admin.TabularInline): class QuestionAdmin(admin.ModelAdmin): fieldsets = [ (None, {"fields": ["question_text"]}), + ("Published date", {"fields": ["pub_date"], "classes": ["collapse"]}), ("End date", {"fields": ["end_date"], "classes": ["collapse"]}), ("Vote count", {"fields": ["up_vote_count", "down_vote_count"]}), ("Participant count", {"fields": ["participant_count"]}), diff --git a/polls/migrations/0008_alter_question_pub_date.py b/polls/migrations/0008_alter_question_pub_date.py new file mode 100644 index 0000000..02205c6 --- /dev/null +++ b/polls/migrations/0008_alter_question_pub_date.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.4 on 2023-09-09 15:16 + +from django.db import migrations, models +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('polls', '0007_question_participant_count'), + ] + + operations = [ + 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 313d213..1e06a4e 100644 --- a/polls/models.py +++ b/polls/models.py @@ -23,16 +23,32 @@ class Question(models.Model): Attributes: question_text (str): The text of the poll question. pub_date (datetime): The date and time when the question was published. + end_date (datetime): The date and time when the question will end. + long_description (str): The long description of the poll question. + short_description (str): The short description of the poll question. + up_vote_count (int): The number of up votes the question has received. + down_vote_count (int): The number of down votes the question has received. + participant_count (int): The number of participants in the poll. """ 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) + long_description = models.TextField( + max_length=2000, default="No description provide for this poll." + ) + pub_date = models.DateTimeField( + "date published", default=timezone.now, editable=True + ) 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)]) + 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): """ @@ -101,13 +117,13 @@ class Question(models.Model): time_left_str = "" if days > 0: - time_left_str += f"{int(days)} D " + time_left_str += f"{int(days)} Days " elif hours > 0: - time_left_str += f"{int(hours)} H " + time_left_str += f"{int(hours)} Hours " elif minutes > 0: - time_left_str += f"{int(minutes)} M " + time_left_str += f"{int(minutes)} Mins " elif seconds > 0: - time_left_str += f"{int(seconds)} S " + time_left_str += f"{int(seconds)} Sec " return time_left_str.strip() @@ -116,6 +132,7 @@ class Question(models.Model): return self.calculate_time_left() def calculate_vote_percentage(self): + """Calculate the percentage of up votes and down votes.""" total_vote = self.up_vote_count + self.down_vote_count if total_vote == 0: return (0, 0) @@ -146,25 +163,32 @@ class Choice(models.Model): question = models.ForeignKey(Question, on_delete=models.CASCADE) choice_text = models.CharField(max_length=200) - votes = models.PositiveIntegerField(default=0, validators=[MinValueValidator(0), MaxValueValidator(2147483647)]) + 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) + 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' + return "w-0" ratio = self.votes / total_votes scaled_value = ratio * 48 - return f'w-{int(round(scaled_value))}' + 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 + """Calculate percentage of votes for all choices.""" + total_votes_for_question = ( + self.question.choice_set.aggregate(Sum("votes"))["votes__sum"] or 0 + ) if total_votes_for_question == 0: return 0 @@ -176,4 +200,3 @@ class Choice(models.Model): Returns a string representation of the choice. """ return self.choice_text - \ No newline at end of file diff --git a/polls/static/polls/js/detail.js b/polls/static/polls/js/detail.js new file mode 100644 index 0000000..86e7e5a --- /dev/null +++ b/polls/static/polls/js/detail.js @@ -0,0 +1,38 @@ +const toggleChoice = (button, choiceId) => { + const choiceInput = document.querySelector(`input[name="choice"][value="${choiceId}"]`); + + if (choiceInput) { + // already selected -> unselect it + if (choiceInput.checked) { + choiceInput.checked = false; + button.classList.remove('bg-green-500', 'border-solid', 'border-2', 'border-black', 'hover:bg-green-600'); + button.classList.add('bg-white', 'border-solid', 'border-2', 'border-black', 'hover:bg-white'); + // Clear display + document.getElementById('selected-choice').textContent = 'Please Select a Choiceπ'; + } else { + // Unselect all choices + document.querySelectorAll('input[name="choice"]').forEach((choice) => { + choice.checked = false; + }); + + // Select the clicked choice + choiceInput.checked = true; + + // Reset the style of all choice buttons + document.querySelectorAll('.choice-button').forEach((btn) => { + btn.classList.remove('bg-green-500', 'border-solid', 'border-2', 'border-black', 'hover:bg-green-600'); + btn.classList.add('bg-white', 'border-solid', 'border-2', 'border-black', 'hover:bg-white'); + }); + + button.classList.remove('bg-white', 'border-solid', 'border-2', 'border-black', 'hover:bg-white'); + button.classList.add('bg-green-500', 'border-solid', 'border-2', 'border-black', 'hover:bg-green-600'); + + const choiceText = button.textContent.trim(); + document.getElementById('selected-choice').textContent = `You select: ${choiceText}`; + } + + // Enable the "Vote" button -> if select + const voteButton = document.getElementById('vote-button'); + voteButton.disabled = !document.querySelector('input[name="choice"]:checked'); + } +}; \ No newline at end of file diff --git a/polls/templates/polls/base.html b/polls/templates/polls/base.html index 7987a04..fc6124b 100644 --- a/polls/templates/polls/base.html +++ b/polls/templates/polls/base.html @@ -2,16 +2,20 @@ -
- - - - - -