mirror of
https://github.com/Sosokker/ku-polls.git
synced 2025-12-19 21:44:06 +01:00
Add up/down Vote + Update admin field
This commit is contained in:
parent
c3b5984e33
commit
f6d0239ec6
133
data/polls.json
133
data/polls.json
@ -8,9 +8,6 @@
|
|||||||
"end_date": "2023-09-29T20:31:49Z",
|
"end_date": "2023-09-29T20:31:49Z",
|
||||||
"short_description": "Cool kids have polls",
|
"short_description": "Cool kids have polls",
|
||||||
"long_description": "No description provide for this poll.",
|
"long_description": "No description provide for this poll.",
|
||||||
"up_vote_count": 5,
|
|
||||||
"down_vote_count": 0,
|
|
||||||
"participant_count": 6,
|
|
||||||
"tags": []
|
"tags": []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -23,9 +20,6 @@
|
|||||||
"end_date": "2023-10-18T23:50:19Z",
|
"end_date": "2023-10-18T23:50:19Z",
|
||||||
"short_description": "Cool kids have polls",
|
"short_description": "Cool kids have polls",
|
||||||
"long_description": "No description provide for this poll.",
|
"long_description": "No description provide for this poll.",
|
||||||
"up_vote_count": 1,
|
|
||||||
"down_vote_count": 0,
|
|
||||||
"participant_count": 0,
|
|
||||||
"tags": []
|
"tags": []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -38,9 +32,6 @@
|
|||||||
"end_date": "2023-11-28T19:50:53Z",
|
"end_date": "2023-11-28T19:50:53Z",
|
||||||
"short_description": "Cool kids have polls",
|
"short_description": "Cool kids have polls",
|
||||||
"long_description": "No description provide for this poll.",
|
"long_description": "No description provide for this poll.",
|
||||||
"up_vote_count": 1,
|
|
||||||
"down_vote_count": 0,
|
|
||||||
"participant_count": 0,
|
|
||||||
"tags": []
|
"tags": []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -53,9 +44,6 @@
|
|||||||
"end_date": "2023-09-29T17:51:18Z",
|
"end_date": "2023-09-29T17:51:18Z",
|
||||||
"short_description": "Cool kids have polls",
|
"short_description": "Cool kids have polls",
|
||||||
"long_description": "No description provide for this poll.",
|
"long_description": "No description provide for this poll.",
|
||||||
"up_vote_count": 10,
|
|
||||||
"down_vote_count": 0,
|
|
||||||
"participant_count": 0,
|
|
||||||
"tags": []
|
"tags": []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -144,7 +132,7 @@
|
|||||||
"model": "polls.vote",
|
"model": "polls.vote",
|
||||||
"pk": 2,
|
"pk": 2,
|
||||||
"fields": {
|
"fields": {
|
||||||
"choice": 2,
|
"choice": 1,
|
||||||
"user": 3,
|
"user": 3,
|
||||||
"question": 1
|
"question": 1
|
||||||
}
|
}
|
||||||
@ -153,7 +141,7 @@
|
|||||||
"model": "polls.vote",
|
"model": "polls.vote",
|
||||||
"pk": 3,
|
"pk": 3,
|
||||||
"fields": {
|
"fields": {
|
||||||
"choice": 1,
|
"choice": 2,
|
||||||
"user": 2,
|
"user": 2,
|
||||||
"question": 1
|
"question": 1
|
||||||
}
|
}
|
||||||
@ -248,6 +236,24 @@
|
|||||||
"question": 3
|
"question": 3
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"model": "polls.vote",
|
||||||
|
"pk": 14,
|
||||||
|
"fields": {
|
||||||
|
"choice": 7,
|
||||||
|
"user": 3,
|
||||||
|
"question": 4
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "polls.vote",
|
||||||
|
"pk": 15,
|
||||||
|
"fields": {
|
||||||
|
"choice": 2,
|
||||||
|
"user": 6,
|
||||||
|
"question": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"model": "polls.sentimentvote",
|
"model": "polls.sentimentvote",
|
||||||
"pk": 1,
|
"pk": 1,
|
||||||
@ -256,5 +262,104 @@
|
|||||||
"question": 1,
|
"question": 1,
|
||||||
"vote_types": false
|
"vote_types": false
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "polls.sentimentvote",
|
||||||
|
"pk": 2,
|
||||||
|
"fields": {
|
||||||
|
"user": 2,
|
||||||
|
"question": 2,
|
||||||
|
"vote_types": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "polls.sentimentvote",
|
||||||
|
"pk": 3,
|
||||||
|
"fields": {
|
||||||
|
"user": 2,
|
||||||
|
"question": 4,
|
||||||
|
"vote_types": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "polls.sentimentvote",
|
||||||
|
"pk": 4,
|
||||||
|
"fields": {
|
||||||
|
"user": 2,
|
||||||
|
"question": 3,
|
||||||
|
"vote_types": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "polls.sentimentvote",
|
||||||
|
"pk": 5,
|
||||||
|
"fields": {
|
||||||
|
"user": 2,
|
||||||
|
"question": 1,
|
||||||
|
"vote_types": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "polls.sentimentvote",
|
||||||
|
"pk": 6,
|
||||||
|
"fields": {
|
||||||
|
"user": 6,
|
||||||
|
"question": 1,
|
||||||
|
"vote_types": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "polls.sentimentvote",
|
||||||
|
"pk": 7,
|
||||||
|
"fields": {
|
||||||
|
"user": 6,
|
||||||
|
"question": 3,
|
||||||
|
"vote_types": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "polls.sentimentvote",
|
||||||
|
"pk": 8,
|
||||||
|
"fields": {
|
||||||
|
"user": 6,
|
||||||
|
"question": 4,
|
||||||
|
"vote_types": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "polls.sentimentvote",
|
||||||
|
"pk": 9,
|
||||||
|
"fields": {
|
||||||
|
"user": 3,
|
||||||
|
"question": 1,
|
||||||
|
"vote_types": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "polls.sentimentvote",
|
||||||
|
"pk": 10,
|
||||||
|
"fields": {
|
||||||
|
"user": 3,
|
||||||
|
"question": 3,
|
||||||
|
"vote_types": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "polls.sentimentvote",
|
||||||
|
"pk": 11,
|
||||||
|
"fields": {
|
||||||
|
"user": 3,
|
||||||
|
"question": 4,
|
||||||
|
"vote_types": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "polls.sentimentvote",
|
||||||
|
"pk": 12,
|
||||||
|
"fields": {
|
||||||
|
"user": 3,
|
||||||
|
"question": 2,
|
||||||
|
"vote_types": true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@ -13,11 +13,10 @@ class QuestionAdmin(admin.ModelAdmin):
|
|||||||
(None, {"fields": ["question_text"]}),
|
(None, {"fields": ["question_text"]}),
|
||||||
("Published date", {"fields": ["pub_date"], "classes": ["collapse"]}),
|
("Published date", {"fields": ["pub_date"], "classes": ["collapse"]}),
|
||||||
("End date", {"fields": ["end_date"], "classes": ["collapse"]}),
|
("End date", {"fields": ["end_date"], "classes": ["collapse"]}),
|
||||||
("Sentiment Vote count", {"fields": ["up_vote_count", "down_vote_count"]}),
|
|
||||||
]
|
]
|
||||||
list_display = ["question_text", "pub_date", "end_date", "was_published_recently", "can_vote"]
|
list_display = ["question_text", "pub_date", "end_date", "was_published_recently", "can_vote"]
|
||||||
inlines = [ChoiceInline]
|
inlines = [ChoiceInline]
|
||||||
list_filter = ["pub_date", ]
|
list_filter = ["pub_date", "end_date"]
|
||||||
search_fields = ["question_text"]
|
search_fields = ["question_text"]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,25 @@
|
|||||||
|
# Generated by Django 4.2.4 on 2023-09-14 19:15
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('polls', '0013_alter_vote_question'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='question',
|
||||||
|
name='down_vote_count',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='question',
|
||||||
|
name='participant_count',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='question',
|
||||||
|
name='up_vote_count',
|
||||||
|
),
|
||||||
|
]
|
||||||
@ -12,7 +12,6 @@ Attributes:
|
|||||||
from django.db import models, IntegrityError
|
from django.db import models, IntegrityError
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.core.validators import MaxValueValidator, MinValueValidator
|
|
||||||
from django.db.models import Sum
|
from django.db.models import Sum
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
|
|
||||||
@ -49,10 +48,6 @@ class Question(models.Model):
|
|||||||
long_description = models.TextField(max_length=2000, default="No description provide for this poll.")
|
long_description = models.TextField(max_length=2000, default="No description provide for this poll.")
|
||||||
tags = models.ManyToManyField(Tag, blank=True)
|
tags = models.ManyToManyField(Tag, blank=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):
|
def was_published_recently(self):
|
||||||
"""
|
"""
|
||||||
Checks if the question was published recently or not.
|
Checks if the question was published recently or not.
|
||||||
@ -164,7 +159,6 @@ class Question(models.Model):
|
|||||||
def upvote(self, user):
|
def upvote(self, user):
|
||||||
try:
|
try:
|
||||||
self.sentimentvote_set.create(user=user, question=self, vote_types=True)
|
self.sentimentvote_set.create(user=user, question=self, vote_types=True)
|
||||||
self.up_vote_count += 1
|
|
||||||
self.save()
|
self.save()
|
||||||
except IntegrityError:
|
except IntegrityError:
|
||||||
vote = self.sentimentvote_set.filter(user=user)
|
vote = self.sentimentvote_set.filter(user=user)
|
||||||
@ -175,11 +169,9 @@ class Question(models.Model):
|
|||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def downvote(self, user):
|
def downvote(self, user):
|
||||||
try:
|
try:
|
||||||
self.sentimentvote_set.create(user=user, question=self, vote_types=False)
|
self.sentimentvote_set.create(user=user, question=self, vote_types=False)
|
||||||
self.up_vote_count += 1
|
|
||||||
self.save()
|
self.save()
|
||||||
except IntegrityError:
|
except IntegrityError:
|
||||||
vote = self.sentimentvote_set.filter(user=user)
|
vote = self.sentimentvote_set.filter(user=user)
|
||||||
@ -190,6 +182,14 @@ class Question(models.Model):
|
|||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@property
|
||||||
|
def up_vote_count(self):
|
||||||
|
return self.sentimentvote_set.filter(question=self, vote_types=True).count()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def down_vote_count(self):
|
||||||
|
return self.sentimentvote_set.filter(question=self, vote_types=False).count()
|
||||||
|
|
||||||
|
|
||||||
class Choice(models.Model):
|
class Choice(models.Model):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@ -11,6 +11,16 @@
|
|||||||
<div class="flex flex-wrap items-center space-x-2">
|
<div class="flex flex-wrap items-center space-x-2">
|
||||||
<header class="flex items-center justify-center">
|
<header class="flex items-center justify-center">
|
||||||
<div class="flex flex-wrap items-center space-x-2">
|
<div class="flex flex-wrap items-center space-x-2">
|
||||||
|
<form method="post" action="{% url "polls:upvote" question.id %}">
|
||||||
|
{% csrf_token %}
|
||||||
|
<button class="flex items-center whitespace-nowrap rounded-full border-black border-2 border-solid bg-neutral-200 px-5 py-2 text-sm font-bold text-black transition duration-150 ease-in-out hover:scale-[101%] hover:bg-green-500 focus:bg-orange-700 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2 active:bg-orange-900"
|
||||||
|
type="submit" {% if user_voted == 'upvote' %}disabled{% endif %}>Upvote👍</button>
|
||||||
|
</form>
|
||||||
|
<form method="post" action="{% url "polls:downvote" question.id %}">
|
||||||
|
{% csrf_token %}
|
||||||
|
<button class="flex items-center whitespace-nowrap rounded-full border-black border-2 border-solid bg-neutral-200 px-5 py-2 text-sm font-bold text-black transition duration-150 ease-in-out hover:scale-[101%] hover:bg-yellow-500 focus:bg-orange-700 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2 active:bg-orange-900"
|
||||||
|
type="submit" {% if user_voted == 'downvote' %}disabled{% endif %}>Downvote👎</button>
|
||||||
|
</form>
|
||||||
<!--End-->
|
<!--End-->
|
||||||
<button
|
<button
|
||||||
onclick="window.location.href='{% url 'polls:index' %}'"
|
onclick="window.location.href='{% url 'polls:index' %}'"
|
||||||
|
|||||||
@ -9,4 +9,6 @@ urlpatterns = [
|
|||||||
path("<int:pk>/results/", views.ResultsView.as_view(), name="results"),
|
path("<int:pk>/results/", views.ResultsView.as_view(), name="results"),
|
||||||
path("<int:question_id>/vote/", views.vote, name="vote"),
|
path("<int:question_id>/vote/", views.vote, name="vote"),
|
||||||
path("signup/", views.SignUpView.as_view(), name="signup"),
|
path("signup/", views.SignUpView.as_view(), name="signup"),
|
||||||
|
path("upvote/<int:question_id>", views.up_down_vote, {'vote_type' : 'upvote'}, name="upvote"),
|
||||||
|
path("downvote/<int:question_id>", views.up_down_vote, {'vote_type' : 'downvote'}, name="downvote"),
|
||||||
]
|
]
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import logging
|
import logging
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from django.shortcuts import get_object_or_404, render, redirect
|
from django.shortcuts import get_object_or_404, render, redirect
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
@ -86,9 +87,18 @@ class ResultsView(LoginRequiredMixin, generic.DetailView):
|
|||||||
model = Question
|
model = Question
|
||||||
template_name = "polls/results.html"
|
template_name = "polls/results.html"
|
||||||
|
|
||||||
def render_to_response(self, context, **response_kwargs):
|
def get_context_data(self, **kwargs: Any) -> dict[str, Any]:
|
||||||
return render(self.request, self.template_name, context)
|
context = super().get_context_data(**kwargs)
|
||||||
|
|
||||||
|
user_voted = None
|
||||||
|
question = self.get_object()
|
||||||
|
if question.sentimentvote_set.filter(user=self.request.user, question=question, vote_types=True).exists():
|
||||||
|
user_voted = 'upvote'
|
||||||
|
elif question.sentimentvote_set.filter(user=self.request.user, question=question, vote_types=False).exists():
|
||||||
|
user_voted = 'downvote'
|
||||||
|
|
||||||
|
context['user_voted'] = user_voted
|
||||||
|
return context
|
||||||
|
|
||||||
class SignUpView(generic.CreateView):
|
class SignUpView(generic.CreateView):
|
||||||
form_class = SignUpForm
|
form_class = SignUpForm
|
||||||
@ -139,6 +149,22 @@ def vote(request, question_id):
|
|||||||
return redirect("polls:index")
|
return redirect("polls:index")
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def up_down_vote(request, question_id, vote_type):
|
||||||
|
ip = get_client_ip(request)
|
||||||
|
question = get_object_or_404(Question, pk=question_id)
|
||||||
|
|
||||||
|
if request.method == "POST":
|
||||||
|
if vote_type == "upvote":
|
||||||
|
if question.upvote(request.user):
|
||||||
|
messages.success(request, "You upvoted this Poll😊")
|
||||||
|
elif vote_type == "downvote":
|
||||||
|
if question.downvote(request.user):
|
||||||
|
messages.success(request, "You downvoted this Poll😭")
|
||||||
|
|
||||||
|
return redirect(reverse("polls:results", args=(question_id,)))
|
||||||
|
|
||||||
|
|
||||||
# https://stackoverflow.com/questions/4581789/how-do-i-get-user-ip-address-in-django
|
# https://stackoverflow.com/questions/4581789/how-do-i-get-user-ip-address-in-django
|
||||||
def get_client_ip(request):
|
def get_client_ip(request):
|
||||||
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
|
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user