mirror of
https://github.com/Sosokker/ku-polls.git
synced 2025-12-19 05:24:05 +01:00
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
179 lines
5.6 KiB
Python
179 lines
5.6 KiB
Python
"""
|
|
This module defines the models for the polls app.
|
|
|
|
It includes the Question and Choice models, which represent poll questions
|
|
and the choices associated with them. These models are used to store and
|
|
get poll data in the database.
|
|
|
|
Attributes:
|
|
None
|
|
"""
|
|
|
|
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):
|
|
"""
|
|
Represents a poll question.
|
|
|
|
Attributes:
|
|
question_text (str): The text of the poll question.
|
|
pub_date (datetime): The date and time when the question was published.
|
|
"""
|
|
|
|
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):
|
|
"""
|
|
Checks if the question was published recently or not.
|
|
|
|
Returns:
|
|
bool: True if the question was published within the last day, else False.
|
|
"""
|
|
now = timezone.now()
|
|
return now - timezone.timedelta(days=1) <= self.pub_date <= now
|
|
|
|
@admin.display(
|
|
boolean=True,
|
|
ordering="pub_date",
|
|
description="Published recently?",
|
|
)
|
|
def was_published_recently(self):
|
|
now = timezone.now()
|
|
return now - timezone.timedelta(days=1) <= self.pub_date <= now
|
|
|
|
def __str__(self):
|
|
"""
|
|
Returns a string representation of the question.
|
|
"""
|
|
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
|
|
|
|
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):
|
|
"""
|
|
Represents a choice for a poll question.
|
|
|
|
Attributes:
|
|
question (Question): The poll question to which the choice belongs.
|
|
choice_text (str): The text of the choice.
|
|
votes (int): The number of votes the choice has received.
|
|
"""
|
|
|
|
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)])
|
|
|
|
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.
|
|
"""
|
|
return self.choice_text
|
|
|