summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorashwinishinde2015-06-19 15:39:01 +0530
committerashwinishinde2015-06-19 15:39:01 +0530
commitbf20363adec8afed8d8e10ad7e0b641c51568cff (patch)
tree324a5ee334456c9ce602ee07aa43912ce5590adb
parent23c44aa78fcf3c015dd4bcaf5b7e8a223e1b6950 (diff)
downloadFOSSEE-Forum-bf20363adec8afed8d8e10ad7e0b641c51568cff.tar.gz
FOSSEE-Forum-bf20363adec8afed8d8e10ad7e0b641c51568cff.tar.bz2
FOSSEE-Forum-bf20363adec8afed8d8e10ad7e0b641c51568cff.zip
Subject: Added Vote Functionality
Description: 1) Implementation of vote for question 2) Added CSS for vote-up and vope-down
-rw-r--r--forums/settings.py7
-rw-r--r--forums/urls.py1
-rw-r--r--requirements.txt1
-rw-r--r--static/website/css/main.css83
-rw-r--r--static/website/images/down1-off.pngbin0 -> 483 bytes
-rw-r--r--static/website/images/down1-on.pngbin0 -> 285 bytes
-rw-r--r--static/website/images/up1-off.pngbin0 -> 458 bytes
-rw-r--r--static/website/images/up1-on.pngbin0 -> 274 bytes
-rw-r--r--static/website/templates/base.html9
-rw-r--r--static/website/templates/get-question.html214
-rw-r--r--static/website/templates/questions.html2
-rw-r--r--static/website/templates/search.html1
-rw-r--r--website/models.py10
-rw-r--r--website/urls.py4
-rw-r--r--website/views.py58
15 files changed, 282 insertions, 108 deletions
diff --git a/forums/settings.py b/forums/settings.py
index b556319..c21d2e0 100644
--- a/forums/settings.py
+++ b/forums/settings.py
@@ -142,6 +142,8 @@ INSTALLED_APPS = (
'compressor',
'debug_toolbar',
'captcha',
+ 'googlesearch',
+ 'gtm',
#'migrate_spoken',
)
@@ -177,11 +179,12 @@ LOGGING = {
TEMPLATE_CONTEXT_PROCESSORS += (
'django.core.context_processors.request',
'website.context_processors.admin_processor',
+
)
COMPRESS_ROOT = PROJECT_DIR + "/static/"
-COMPRESS_ENABLED = True # disable in production Env
-HTML_MINIFY = True # disable in production Env
+COMPRESS_ENABLED = False # disable in production Env
+HTML_MINIFY = False # disable in production Env
HTML_MINIFY = HTML_MINIFY
RECAPTCHA_PUBLIC_KEY = '6LemngMTAAAAAAC0Fkv0CQcavkTIIJ3LTDzi9gMq'
diff --git a/forums/urls.py b/forums/urls.py
index 31c679b..4584b66 100644
--- a/forums/urls.py
+++ b/forums/urls.py
@@ -23,4 +23,5 @@ urlpatterns = patterns('',
url(r"^accounts/confirm/(?P<confirmation_code>\w+)/(?P<username>[\w. @-]+)/$", 'forums.views.confirm', name='confirm'),
url(r"^accounts/profile/(?P<username>[\w. @-]+)/$", 'forums.views.account_profile', name='profile'),
url(r"^accounts/view-profile/(?P<username>[\w. @-]+)/$", 'forums.views.account_view_profile', name='view_profile'),
+ url(r'^search/', include('googlesearch.urls')),
)
diff --git a/requirements.txt b/requirements.txt
index e7beaee..1adb4bb 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -13,3 +13,4 @@ html5lib==1.0b3
six==1.6.1
sqlparse==0.1.11
wsgiref==0.1.2
+django-googlesearch
diff --git a/static/website/css/main.css b/static/website/css/main.css
index 20f220a..774871e 100644
--- a/static/website/css/main.css
+++ b/static/website/css/main.css
@@ -248,6 +248,8 @@ table .question a{
margin: 5px 0px;
}
#content .answer .body {
+ padding-bottom: 10px;
+ ,,
}
#content .answer .user {
padding: 2px 7px;
@@ -401,3 +403,84 @@ table .question a{
font-size: 22px;
overflow: hidden;
}
+
+
+.votes {
+ color: rgb(85, 85, 85);
+ padding-top: 8px;
+ padding-bottom: 8px;
+ text-align: center;
+}
+
+.vote span {
+ display: block;
+ color: rgb(128, 129, 133);
+ font-weight: normal;
+}
+
+.vote-count-post {
+ display: block;
+ font-size: 17px;
+
+}
+
+.votecell {
+ background:#ffffff;
+
+ float:left;
+ width:60px;
+ height:100px;
+ margin-right:5px;
+ padding:0;
+ border-radius: 3px;
+}
+
+.post-text {
+ word-wrap: break-word;
+ width: 660px;
+ font-size: 107%;
+ margin-bottom: 5px;
+ margin-right: 5px;
+ margin-right-value: 5px;
+ margin-right-ltr-source: physical;
+ margin-right-rtl-source: physical;
+ line-height: 130%;
+}
+
+.post-taglist {
+ margin-bottom: 10px;
+ clear: both;
+}
+
+.vote {
+ text-align: center;
+}
+
+
+.vote-up-off, .vote-up-on, .vote-down-off, .vote-down-on, .star-on, .star-off, .comment-up-off, .comment-up-on, .comment-flag, .flag-off, .vote-accepted-off, .vote-accepted-on {
+ text-indent: -9999em;
+ font-size: 1px;
+}
+
+.vote-up-off, .vote-up-on, .vote-down-off, .vote-down-on, .star-on, .star-off {
+ background-position: 0px -265px;
+ display: block;
+ margin: 0px auto;
+ margin-top: 0px;
+ margin-right-value: auto;
+ margin-bottom: 0px;
+ margin-left-value: auto;
+ margin-left-ltr-source: physical;
+ margin-left-rtl-source: physical;
+ margin-right-ltr-source: physical;
+ margin-right-rtl-source: physical;
+ width: 41px;
+ height: 25px;
+}
+
+
+.space {
+ padding-top: 20px;
+}
+
+
diff --git a/static/website/images/down1-off.png b/static/website/images/down1-off.png
new file mode 100644
index 0000000..c720b49
--- /dev/null
+++ b/static/website/images/down1-off.png
Binary files differ
diff --git a/static/website/images/down1-on.png b/static/website/images/down1-on.png
new file mode 100644
index 0000000..84f1a23
--- /dev/null
+++ b/static/website/images/down1-on.png
Binary files differ
diff --git a/static/website/images/up1-off.png b/static/website/images/up1-off.png
new file mode 100644
index 0000000..9fecb01
--- /dev/null
+++ b/static/website/images/up1-off.png
Binary files differ
diff --git a/static/website/images/up1-on.png b/static/website/images/up1-on.png
new file mode 100644
index 0000000..f756707
--- /dev/null
+++ b/static/website/images/up1-on.png
Binary files differ
diff --git a/static/website/templates/base.html b/static/website/templates/base.html
index b174d69..b5d087b 100644
--- a/static/website/templates/base.html
+++ b/static/website/templates/base.html
@@ -21,6 +21,15 @@
{% endcompress %}
</head>
<body>
+ <!-- Google Tag Manager -->
+<noscript><iframe src="//www.googletagmanager.com/ns.html?id=GTM-NQBKKJ"
+height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
+<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
+new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
+j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
+'//www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
+})(window,document,'script','dataLayer','GTM-NQBKKJ');</script>
+<!-- End Google Tag Manager -->
<div id="page-wrapper">
<div id="header-wrapper">
<div id="header-inner">
diff --git a/static/website/templates/get-question.html b/static/website/templates/get-question.html
index c31259b..7c93803 100644
--- a/static/website/templates/get-question.html
+++ b/static/website/templates/get-question.html
@@ -9,17 +9,47 @@
{% endblock %}
{% block content %}
+
<span class="saving hideme">saving . . .</span>
<span class="saved hideme">saved</span>
<div class="row">
+
+
+
<div class="question-wrapper col-lg-12 col-md-12 col-sm-12 col-xs-12">
<div class="question {% if user|can_edit:question %}editable{% endif %}">
+ <!-- VOTE CELL -->
+
+
+
+ <div class="votecell" style="padding-top: 0.0cm;">
+ <div class="vote">
+ {% ifequal thisUserUpvote 0 %}
+ <img src="{% static 'website/images/up1-off.png' %}" class="vote-up" title="This question shows research effort; it is useful and clear"/>
+ {% else %}
+ <img src="{% static 'website/images/up1-on.png' %}" class="vote-up selected" title="This question shows research effort; it is useful and clear"/>
+ {% endifequal %}
+
+ <span class="vote-count-post" id="show_vote_count">
+ {{ net_count }}
+ </span>
+
+ {% ifequal thisUserDownvote 0 %}
+ <img src="{% static 'website/images/down1-off.png' %}" class="vote-down" title="This question does not show any research effort; it is not useful and unclear"/>
+ {% else %}
+ <img src="{% static 'website/images/down1-on.png' %}" class="vote-down selected" title="This question does not show any research effort; it is not useful and unclear"/>
+ {% endifequal %}
+ </div>
+
+ </div>
<div class="title {% if user|can_edit:question %}title-editable{% endif %}">
{{ question.title }}
</div>
<div id="title-edit" class="hideme">
+
+
<h5>Title</h5>
<input class="form-control" type="text">
<h5>Question</h5>
@@ -62,10 +92,13 @@
{{ question.user }}
</span>
</span>
+
</div> <!-- /.question -->
</div>
+
</div> <!-- /.row -->
+
<h4><u>Answers:</u></h4>
<div id="answerPanelWrapper" style="display:none;">
<div id="answerNicPanel" style="display:none;"></div>
@@ -207,80 +240,7 @@
<div class="modal-body">
<h5>The Current question is under:</h5>
- {{ question.category }} <!--
-
- <h5>Move the question to:</h5>
- <div class="row">
- <div class="col-lg-4 col-md-4 col-sm-4 col-xs-4">
- <select id="id_category" class="form-control">
- <option value="Advanced-C++">Advanced-C++</option>
- <option value="BASH">BASH</option>
- <option value="Blender">Blender</option>
- <option value="C-and-C++">C-and-C++</option>
- <option value="CellDesigner">CellDesigner</option>
- <option value="Digital-Divide">Digital-Divide</option>
- <option value="Drupal">Drupal</option>
- <option value="Firefox">Firefox</option>
- <option value="GChemPaint">GChemPaint</option>
- <option value="Geogebra">Geogebra</option>
- <option value="GeoGebra-for-Engineering-drawing">GeoGebra-for-Engineering-drawing</option>
- <option value="GIMP">GIMP</option>
- <option value="GNS3">GNS3</option>
- <option value="GSchem">GSchem</option>
- <option value="Inkscape">Inkscape</option>
- <option value="Java">Java</option>
- <option value="Java-Business-Application">Java-Business-Application</option>
- <option value="KiCad">KiCad</option>
- <option value="KTouch">KTouch</option>
- <option value="KTurtle">KTurtle</option>
- <option value="LaTeX">LaTeX</option>
- <option value="LibreOffice-Suite-Base">LibreOffice-Suite-Base</option>
- <option value="LibreOffice-Suite-Calc">LibreOffice-Suite-Calc</option>
- <option value="LibreOffice-Suite-Draw">LibreOffice-Suite-Draw</option>
- <option value="LibreOffice-Suite-Impress">LibreOffice-Suite-Impress</option>
- <option value="LibreOffice-Suite-Math">LibreOffice-Suite-Math</option>
- <option value="LibreOffice-Suite-Writer">LibreOffice-Suite-Writer</option>
- <option value="Linux">Linux</option>
- <option value="Netbeans">Netbeans</option>
- <option value="Ngspice">Ngspice</option>
- <option value="OpenFOAM">OpenFOAM</option>
- <option value="Orca">Orca</option>
- <option value="Oscad">Oscad</option>
- <option value="PERL">PERL</option>
- <option value="PHP-and-MySQL">PHP-and-MySQL</option>
- <option value="Python">Python</option>
- <option value="Python-Old-Version">Python-Old-Version</option>
- <option value="QCad">QCad</option>
- <option value="R">R</option>
- <option value="Ruby">Ruby</option>
- <option value="Scilab">Scilab</option>
- <option value="Selenium">Selenium</option>
- <option value="Single-Board-Heater-System">Single-Board-Heater-System</option>
- <option value="Spoken-Tutorial-Technology">Spoken-Tutorial-Technology</option>
- <option value="Step">Step</option>
- <option value="Thunderbird">Thunderbird</option>
- <option value="Tux-Typing">Tux-Typing</option>
- <option value="What-is-Spoken-Tutorial">What-is-Spoken-Tutorial</option>
- <option value="Xfig">Xfig</option>
- <option value="General">General FOSS</option>
- </select>
- </div>
- <div class="col-lg-4 col-md-4 col-sm-4 col-xs-4">
- <select id="id_tutorial" class="form-control" disabled>
- <option value="None">Select a Tutorial</option>
- </select>
- </div>
- <div class="col-lg-2 col-md-2 col-sm-2 col-xs-2">
- <select id="id_minute_range" class="form-control" disabled>
- <option value="None">min</option>
- </select>
- </div>
- <div class="col-lg-2 col-md-2 col-sm-2 col-xs-2">
- <select id="id_second_range" class="form-control" disabled>
- <option value="None">sec</option>
- </select>
- </div>
- </div> <!-- /.row -->
+ {{ question.category }}
<hr>
<div class="pull-right">
<a id="question-details-ok" class="btn btn-sm btn-success hideme" data-qid={{ question.id }} href="#">Ok</a>
@@ -291,8 +251,14 @@
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
+<!-- AJAX SCRIPT -->
+
+
-<script src="{% static 'website/js/nicEdit.js' %}" type="text/javascript"></script>
+{% endblock %}
+
+{% block javascript %}
+ <script src="{% static 'website/js/nicEdit.js' %}" type="text/javascript"></script>
<script type="text/javascript">
bkLib.onDomLoaded(function() {
new nicEditor({
@@ -301,34 +267,82 @@
}).panelInstance('id_body');
});
</script>
-{% endblock %}
+ <!-- <script src="{% static 'website/js/thread-user.js' %}"></script>
+ <script src="{% static 'website/js/custom.js' %}"></script> -->
+ <script type="text/javascript">
-{% block javascript %}
- <script src="{% static 'website/js/thread-user.js' %}"></script>
- <script src="{% static 'website/js/custom.js' %}"></script>
- <script>
$(document).ready(function() {
- $vote = $(".vote");
+ $.ajaxSetup({
+ data: {csrfmiddlewaretoken: '{{ csrf_token }}' },
+ });
+
+ $('div.vote img.vote-up').click(function() {
+
- $vote.click(function() {
- if($(this).hasClass("upvote")) {
- /* handle upvote click event */
- if($(this).hasClass("voted")) {
- $(this).removeClass("voted");
- } else {
- $(this).addClass("voted");
- $(this).siblings(".downvote").removeClass("voted");
- }
- } else {
- /* handle downvote click event */
- if($(this).hasClass("voted")) {
- $(this).removeClass("voted");
- } else {
- $(this).addClass("voted");
- $(this).siblings(".upvote").removeClass("voted");
- }
- }
- });
+
+ var id = {{ question.pk }};
+ var vote_type = 'up';
+
+ /* USER HAS ALREADY VOTED */
+
+ if ($(this).hasClass('selected')) {
+ var vote_action = 'recall-vote'
+
+ $.post('/vote_post/', {id:id, type:vote_type, action:vote_action}, function(response) {
+
+ $('img.vote-up').removeAttr('src')
+ .attr('src', '{% static 'website/images/up1-off.png' %}')
+ .removeClass('selected');
+ $('div.vote span.vote-count-post').html(response);
+ });
+ }
+ /* USER WISHES TO VOTE */
+ else {
+ var vote_action = 'vote'
+ $.post('/vote_post/', {id:id, type:vote_type, action:vote_action}, function(response) {
+
+ $('img.vote-up').removeAttr('src')
+ .attr('src', '{% static 'website/images/up1-on.png' %}')
+ .addClass('selected');
+ $('div.vote span.vote-count-post').html(response);
+ });
+ }
});
+
+ /* DOWNVOTE */
+
+ $('div.vote img.vote-down').click(function() {
+
+ var id = {{ question.pk }};
+ var vote_type = 'down';
+
+ /* USER HAS ALREADY DOWN-VOTED */
+
+ if ($(this).hasClass('selected')) {
+ var vote_action = 'recall-vote'
+
+ $.post('/vote_post/', {id:id, type:vote_type, action:vote_action}, function(response) {
+
+ $('img.vote-down').removeAttr('src')
+ .attr('src', '{% static 'website/images/down1-off.png' %}')
+ .removeClass('selected');
+ $('div.vote span.vote-count-post').html(response);
+ });
+ }
+ /* USER WISHES TO VOTE */
+ else {
+ var vote_action = 'vote'
+ $.post('/vote_post/', {id:id, type:vote_type, action:vote_action}, function(response) {
+
+ $('img.vote-down').removeAttr('src')
+ .attr('src', '{% static 'website/images/down1-on.png' %}')
+ .addClass('selected');
+ $('div.vote span.vote-count-post').html(response);
+ });
+ }
+ });
+ });
+
</script>
+
{% endblock %}
diff --git a/static/website/templates/questions.html b/static/website/templates/questions.html
index 98d016d..96f596d 100644
--- a/static/website/templates/questions.html
+++ b/static/website/templates/questions.html
@@ -49,8 +49,8 @@
<td>
{{ question.answer_set.count }}
</td>
+
- <td>
<span class="title" data-toggle="tooltip" data-placement="top" title="{{ question.user }}">
{{ question.user|truncatechars:10 }}
</span>
diff --git a/static/website/templates/search.html b/static/website/templates/search.html
index a7ac917..c374b47 100644
--- a/static/website/templates/search.html
+++ b/static/website/templates/search.html
@@ -1,4 +1,5 @@
{% extends 'website/templates/base.html' %}
+{% load i18n googlesearch_inclusion_tags %}
{% load static %}
{% block content %}
diff --git a/website/models.py b/website/models.py
index c7e43aa..b0a9fc3 100644
--- a/website/models.py
+++ b/website/models.py
@@ -23,13 +23,14 @@ class Issue(models.Model):
class Question(models.Model):
user = models.ForeignKey(User)
category = models.ForeignKey(FossCategory)
- #tutorial = models.ForeignKey(Issue)
title = models.CharField(max_length=200)
body = models.TextField()
date_created = models.DateTimeField(auto_now_add=True)
date_modified = models.DateTimeField(auto_now=True)
views = models.IntegerField(default=1)
- # votes = models.IntegerField(default=0)
+ userUpVotes = models.ManyToManyField(User, blank=True, related_name='postUpVotes')
+ userDownVotes = models.ManyToManyField(User, blank=True, related_name='postDownVotes')
+ num_votes = models.IntegerField(default=0)
def __unicode__(self):
return '%s' % (self.user)
@@ -53,7 +54,10 @@ class Answer(models.Model):
body = models.TextField()
date_created = models.DateTimeField(auto_now_add=True)
date_modified = models.DateTimeField(auto_now=True)
- # votes = models.IntegerField(default=0)
+ #userUpVotes = models.ManyToManyField(User, blank=True, related_name='postUpVotes')
+ #userDownVotes = models.ManyToManyField(User, blank=True, related_name='postDownVotes')
+ upvotes = models.IntegerField(default=0)
+ num_votes = models.IntegerField(default=0)
def user(self):
user = User.objects.get(id=self.uid)
diff --git a/website/urls.py b/website/urls.py
index 1c7d75e..71588af 100644
--- a/website/urls.py
+++ b/website/urls.py
@@ -1,4 +1,5 @@
from django.conf.urls import patterns, include, url
+from website import views
urlpatterns = patterns('',
url(r'^$', 'website.views.home', name='home'),
@@ -9,6 +10,7 @@ urlpatterns = patterns('',
url(r'^answer-comment/$', 'website.views.answer_comment', name='answer_comment'),
url(r'^filter/(?P<category>[^/]+)/$', 'website.views.filter', name='filter'),
url(r'^filter/(?P<category>[^/]+)/$', 'website.views.filter', name='filter'),
+
#url(r'^filter/(?P<category>[^/]+)/(?P<tutorial>[^/]+)/(?P<minute_range>[^/]+)/$', 'website.views.filter', name='filter'),
#url(r'^filter/(?P<category>[^/]+)/(?P<tutorial>[^/]+)/(?P<minute_range>[^/]+)/(?P<second_range>[^/]+)/$', 'website.views.filter', name='filter'),
url(r'^new-question/$', 'website.views.new_question', name='new_question'),
@@ -18,7 +20,7 @@ urlpatterns = patterns('',
url(r'^clear-notifications/$', 'website.views.clear_notifications', name='clear_notifications'),
url(r'^search/$', 'website.views.search', name='search'),
url(r'^unanswered-notification/$', 'website.views.unanswered_notification', name='unanswered_notification'),
-
+ url(r'^vote_post/$', 'website.views.vote_post', name='vote_post'),
# Ajax helpers
url(r'^ajax-tutorials/$', 'website.views.ajax_tutorials', name='ajax_tutorials'),
url(r'^ajax-duration/$', 'website.views.ajax_duration', name='ajax_duration'),
diff --git a/website/views.py b/website/views.py
index f80ee13..c9a2a5f 100644
--- a/website/views.py
+++ b/website/views.py
@@ -55,10 +55,17 @@ def get_question(request, question_id=None, pretty_url=None):
return HttpResponseRedirect('/question/'+ question_id + '/' + pretty_title)
answers = question.answer_set.all()
form = AnswerQuestionForm()
+ thisuserupvote = question.userUpVotes.filter(id=request.user.id).count()
+ thisuserdownvote = question.userDownVotes.filter(id=request.user.id).count()
+
+ net_count = question.userUpVotes.count() - question.userDownVotes.count()
context = {
'question': question,
'answers': answers,
- 'form': form
+ 'form': form,
+ 'thisUserUpvote': thisuserupvote,
+ 'thisUserDownvote': thisuserdownvote,
+ 'net_count': net_count
}
context.update(csrf(request))
@@ -292,6 +299,55 @@ def new_question(request):
context.update(csrf(request))
return render(request, 'website/templates/new-question.html', context)
+
+def vote_post(request):
+
+ print "post"
+ post_id = int(request.POST.get('id'))
+
+ vote_type = request.POST.get('type')
+ vote_action = request.POST.get('action')
+ print post_id
+ cur_post = get_object_or_404(Question, id=post_id)
+
+ thisuserupvote = cur_post.userUpVotes.filter(id=request.user.id).count()
+ thisuserdownvote = cur_post.userDownVotes.filter(id=request.user.id).count()
+
+ initial_votes = cur_post.userUpVotes.count() - cur_post.userDownVotes.count()
+
+ # print "User Initial Upvote and Downvote: %d %d %s " % (thisuserupvote, thisuserdownvote, vote_action)
+
+ #This loop is for voting
+ if vote_action == 'vote':
+ if (thisuserupvote == 0) and (thisuserdownvote == 0):
+ if vote_type == 'up':
+ cur_post.userUpVotes.add(request.user)
+ elif vote_type == 'down':
+ cur_post.userDownVotes.add(request.user)
+ else:
+ return HttpResponse("Error: Unknown vote-type passed.")
+ else:
+ return HttpResponse(initial_votes)
+ #This loop is for canceling vote
+ elif vote_action == 'recall-vote':
+ if (vote_type == 'up') and (thisuserupvote == 1):
+ cur_post.userUpVotes.remove(request.user)
+ elif (vote_type == 'down') and (thisuserdownvote == 1):
+ cur_post.userDownVotes.remove(request.user)
+ else:
+ # "Error - Unknown vote type or no vote to recall"
+ return HttpResponse(initial_votes)
+ else:
+ return HttpResponse("Error: Bad Action.")
+
+ num_votes = cur_post.userUpVotes.count() - cur_post.userDownVotes.count()
+ cur_post.num_votes = num_votes
+ cur_post.save()
+
+ print "Num Votes: %s" % num_votes
+
+ return HttpResponse(num_votes)
+
# Notification Section
@login_required
def user_questions(request, user_id):