Lets vote on this questions... Questions have voting feature now. · pythonDrive/bootcamp@68ae6d0 · GitHub
Skip to content

Commit 68ae6d0

Browse files
Lets vote on this questions... Questions have voting feature now.
1 parent 3e66336 commit 68ae6d0

9 files changed

Lines changed: 155 additions & 36 deletions

File tree

Lines changed: 17 additions & 0 deletions
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Generated by Django 2.0.3 on 2018-06-12 19:03
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('qa', '0006_remove_question_liked'),
10+
]
11+
12+
operations = [
13+
migrations.AddField(
14+
model_name='question',
15+
name='total_votes',
16+
field=models.IntegerField(default=0),
17+
),
18+
]

bootcamp/qa/models.py

Lines changed: 11 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,8 @@ class Question(models.Model):
8080
slug = models.SlugField(max_length=80, null=True, blank=True)
8181
status = models.CharField(max_length=1, choices=STATUS, default=DRAFT)
8282
content = models.TextField(max_length=2500)
83-
liked = models.ManyToManyField(settings.AUTH_USER_MODEL,
84-
blank=True, related_name="liked_question")
8583
has_answer = models.BooleanField(default=False)
84+
total_votes = models.IntegerField(default=0)
8685
votes = GenericRelation(Vote)
8786
tags = TaggableManager()
8887
objects = QuestionQuerySet.as_manager()
@@ -103,19 +102,6 @@ def save(self, *args, **kwargs):
103102
def __str__(self):
104103
return self.title
105104

106-
def switch_like(self, user):
107-
if user in self.liked.all():
108-
self.liked.remove(user)
109-
110-
else:
111-
self.liked.add(user)
112-
113-
@property
114-
def count_votes(self):
115-
upvotes = self.votes.filter(value=True).count()
116-
downvotes = self.votes.filter(value=False).count()
117-
return upvotes - downvotes
118-
119105
@property
120106
def count_answers(self):
121107
return Answer.objects.filter(question=self).count()
@@ -124,6 +110,12 @@ def count_answers(self):
124110
def count_likers(self):
125111
return self.liked.count()
126112

113+
def count_votes(self):
114+
dvotes = self.votes.filter(value=False).count()
115+
uvotes = self.votes.filter(value=True).count()
116+
self.total_votes = uvotes - dvotes
117+
self.save()
118+
127119
def get_likers(self):
128120
return self.liked.all()
129121

@@ -154,11 +146,11 @@ class Meta:
154146
def __str__(self): # pragma: no cover
155147
return self.content
156148

157-
@property
158149
def count_votes(self):
159-
upvotes = self.votes.filter(value=True).count()
160-
downvotes = self.votes.filter(value=False).count()
161-
return upvotes - downvotes
150+
dvotes = self.votes.filter(value=False).count()
151+
uvotes = self.votes.filter(value=True).count()
152+
self.total_votes = uvotes - dvotes
153+
self.save()
162154

163155
def accept_answer(self):
164156
answer_set = Answer.objects.filter(question=self.question)

bootcamp/qa/urls.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,5 @@
1111
name='ask_question'),
1212
url(r'^propose-answer/(?P<question_id>\d+)/$',
1313
views.CreateAnswerView.as_view(), name='propose_answer'),
14+
url(r'^question/vote/$', views.question_vote, name='question_vote'),
1415
]

bootcamp/qa/views.py

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
1+
from django.db.utils import IntegrityError
12
from django.conf import settings
23
from django.contrib.auth.decorators import login_required
34
from django.contrib.auth.mixins import LoginRequiredMixin
45
from django.contrib import messages
5-
from django.http import JsonResponse
6+
from django.http import HttpResponseBadRequest, JsonResponse
67
from django.urls import reverse
78
from django.utils.translation import ugettext as _
89
from django.views.generic import CreateView, ListView, DetailView
910

1011
from bootcamp.helpers import ajax_required
11-
from bootcamp.qa.models import Question, Answer
12+
from bootcamp.qa.models import Question, Answer, Vote
1213
from bootcamp.qa.forms import QuestionForm
1314

1415

@@ -61,3 +62,33 @@ def get_success_url(self):
6162
messages.success(self.request, self.message)
6263
return reverse(
6364
"qa:question_detail", kwargs={"pk": self.kwargs["question_id"]})
65+
66+
67+
@login_required
68+
@ajax_required
69+
def question_vote(request):
70+
"""Function view to receive AJAX call, returns the count of votes a given
71+
question has recieved."""
72+
if request.method == "POST":
73+
question_id = request.POST["question"]
74+
value = None
75+
if request.POST["value"] == "U":
76+
value = True
77+
78+
else:
79+
value = False
80+
81+
question = Question.objects.get(pk=question_id)
82+
try:
83+
question.votes.update_or_create(
84+
user=request.user, defaults={"value": value}, )
85+
question.count_votes()
86+
return JsonResponse({"votes": question.total_votes})
87+
88+
except IntegrityError:
89+
return JsonResponse({'status': 'false',
90+
'message': _("Database integrity error.")},
91+
status=500)
92+
93+
else:
94+
return HttpResponseBadRequest(content=_("Wrong request type."))

bootcamp/static/js/qa.js

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,37 @@
11
$(function () {
2+
function getCookie(name) {
3+
// Function to get any cookie available in the session.
4+
var cookieValue = null;
5+
if (document.cookie && document.cookie !== '') {
6+
var cookies = document.cookie.split(';');
7+
for (var i = 0; i < cookies.length; i++) {
8+
var cookie = jQuery.trim(cookies[i]);
9+
// Does this cookie string begin with the name we want?
10+
if (cookie.substring(0, name.length + 1) === (name + '=')) {
11+
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
12+
break;
13+
}
14+
}
15+
}
16+
return cookieValue;
17+
};
18+
19+
function csrfSafeMethod(method) {
20+
// These HTTP methods do not require CSRF protection
21+
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
22+
}
23+
24+
var csrftoken = getCookie('csrftoken');
25+
var page_title = $(document).attr("title");
26+
// This sets up every ajax call with proper headers.
27+
$.ajaxSetup({
28+
beforeSend: function(xhr, settings) {
29+
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
30+
xhr.setRequestHeader("X-CSRFToken", csrftoken);
31+
}
32+
}
33+
});
34+
235
$("#publish").click(function () {
336
$("input[name='status']").val("O");
437
$("#question-form").submit();
@@ -8,4 +41,31 @@ $(function () {
841
$("input[name='status']").val("D");
942
$("#question-form").submit();
1043
});
44+
45+
$(".question-vote").click(function () {
46+
var span = $(this);
47+
var question = $(this).closest(".question").attr("question-id");
48+
vote = null;
49+
if ($(this).hasClass("up-vote")) {
50+
vote = "U";
51+
} else {
52+
vote = "D";
53+
}
54+
$.ajax({
55+
url: '/qa/question/vote/',
56+
data: {
57+
'question': question,
58+
'value': vote
59+
},
60+
type: 'post',
61+
cache: false,
62+
success: function (data) {
63+
$('.vote', span).removeClass('voted');
64+
if (vote === "U") {
65+
$(span).addClass('voted');
66+
}
67+
$("#questionVotes").text(data.votes);
68+
}
69+
});
70+
});
1171
});

bootcamp/templates/qa/answer_sample.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
<div class="row answer" answer-id="{{ answer.id }}">
44
{% csrf_token %}
5-
<div class="col-md-2 options">
5+
<div class="col-md-1 options">
66
<i class="fa fa-chevron-up vote up-vote answer-vote {% if request.user in answer.get_up_voters %}voted{% endif %}" aria-hidden="true" title="{% trans 'Click to up vote; click again to toggle' %}"></i>
77
<span class="votes">{{ answer.count_votes }}</span>
88
<i class="fa fa-chevron-down vote down-vote answer-vote {% if request.user in answer.get_down_voters %}voted{% endif %}" aria-hidden="true" title="{% trans 'Click to down vote; click again to toggle' %}"></i>
@@ -14,7 +14,7 @@
1414
<i class="fa fa-check accept" aria-hidden="true" title="{% trans 'Click to accept the answer' %}"></i>
1515
{% endif %}
1616
</div>
17-
<div class="col-md-10">
17+
<div class="col-md-11">
1818
<div class="answer-user">
1919
<a href="#"><img src="{{ answer.user.picture }}" class="user"></a>
2020
<a href="#" class="username">{{ answer.user.username }}</a>

bootcamp/templates/qa/question_detail.html

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,21 +22,18 @@
2222
</a>
2323
<h1>{{ question.title }}</h1>
2424
</div>
25-
<div class="row">
26-
<div class="col-md-2">
25+
<div class="row question" question-id="{{ question.id }}">
26+
<div class="col-md-1">
2727
<div class="question-info options">
2828
<h3>{{ question.count_answers }}</h3>
2929
<small class="text-secondary">{% trans 'Answers' %}</small>
30-
<h3>{{ question.count_likers }}</h3>
31-
<a href="#"><h3><i class="text-danger fa fa-heart{% if request.user not in question.get_likers %}-o{% endif %}" aria-hidden="true"></i></h3></a>
32-
<small class="text-secondary">{% trans 'Likes' %}</small>
33-
<i class="fa fa-chevron-up vote up-vote answer-vote{% if request.user in question.get_upvoters %} voted{% endif %}" aria-hidden="true" title="{% trans 'Click to up vote; click again to toggle' %}"></i>
34-
<h3>{{ question.count_votes }}</h3>
35-
<i class="fa fa-chevron-down vote down-vote answer-vote{% if request.user in question.get_downvoters %} voted{% endif %}" aria-hidden="true" title="{% trans 'Click to down vote; click again to toggle' %}"></i>
30+
<i id="questionUpVote" class="fa fa-chevron-up vote up-vote question-vote{% if request.user in question.get_upvoters %} voted{% endif %}" aria-hidden="true" title="{% trans 'Click to up vote; click again to toggle' %}"></i>
31+
<h3 id="questionVotes">{{ question.total_votes }}</h3>
32+
<i id="questionDownVote" class="fa fa-chevron-down vote down-vote question-vote{% if request.user in question.get_downvoters %} voted{% endif %}" aria-hidden="true" title="{% trans 'Click to down vote; click again to toggle' %}"></i>
3633
<small class="text-secondary">{% trans 'Votes' %}</small>
3734
</div>
3835
</div>
39-
<div class="col-md-10">
36+
<div class="col-md-11">
4037
{% if question.has_answer %}
4138
<i class="fa fa-check-circle" aria-hidden="true"></i>
4239
{% endif %}
@@ -77,3 +74,8 @@ <h4>{% trans 'There are no answers yet.' %}</h4>
7774
</ul>
7875
</div>
7976
{% endblock content %}
77+
78+
79+
{% block modal %}
80+
<script src="{% static 'js/qa.js' %}" type="text/javascript"></script>
81+
{% endblock modal %}

bootcamp/templates/qa/question_sample.html

Lines changed: 2 additions & 4 deletions

0 commit comments

Comments
 (0)