summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormahesh2017-08-24 23:26:46 +0530
committermahesh2017-08-24 23:26:46 +0530
commit751dc83729e5bf57b2dd1a2798bb7b527a2e3584 (patch)
treefa1d11bd00ac9060cfa36056f62d1321455a4916
parent6ab383c02085936ecc20ffeb4b9f41ca642256c3 (diff)
parent9d5c4a01fd7856f1ef8793b75a9734324c254344 (diff)
downloadonline_test-751dc83729e5bf57b2dd1a2798bb7b527a2e3584.tar.gz
online_test-751dc83729e5bf57b2dd1a2798bb7b527a2e3584.tar.bz2
online_test-751dc83729e5bf57b2dd1a2798bb7b527a2e3584.zip
Merge branch 'master' of https://github.com/fossee/online_test into yaml_questions
-rw-r--r--online_test/settings.py3
-rw-r--r--yaksh/decorators.py40
-rw-r--r--yaksh/forms.py17
-rw-r--r--yaksh/models.py5
-rw-r--r--yaksh/send_emails.py32
-rw-r--r--yaksh/static/yaksh/js/course.js31
-rw-r--r--yaksh/templates/yaksh/course_detail.html103
-rw-r--r--yaksh/templates/yaksh/login.html8
-rw-r--r--yaksh/templates/yaksh/moderator_dashboard.html2
-rw-r--r--yaksh/test_views.py168
-rw-r--r--yaksh/urls.py1
-rw-r--r--yaksh/views.py101
12 files changed, 437 insertions, 74 deletions
diff --git a/online_test/settings.py b/online_test/settings.py
index 4ca2967..90cce9d 100644
--- a/online_test/settings.py
+++ b/online_test/settings.py
@@ -113,7 +113,8 @@ EMAIL_HOST_USER = 'email_host_user'
EMAIL_HOST_PASSWORD = 'email_host_password'
-# Set EMAIL_BACKEND to 'django.core.mail.backends.smtp.EmailBackend' in production
+# Set EMAIL_BACKEND to 'django.core.mail.backends.smtp.EmailBackend'
+# in production
EMAIL_BACKEND = 'django.core.mail.backends.dummy.EmailBackend'
# SENDER_EMAIL, REPLY_EMAIL, PRODUCTION_URL, IS_DEVELOPMENT are used in email
diff --git a/yaksh/decorators.py b/yaksh/decorators.py
index f0d354c..9e9bc6d 100644
--- a/yaksh/decorators.py
+++ b/yaksh/decorators.py
@@ -1,12 +1,42 @@
-from django.shortcuts import render_to_response
+from django.shortcuts import render_to_response, redirect
from django.conf import settings
from django.template import RequestContext
+# Local imports
+from yaksh.forms import ProfileForm
+
+
+def user_has_profile(user):
+ return hasattr(user, 'profile')
+
+
+def has_profile(func):
+ """
+ This decorator is used to check if the user account has a profile.
+ If the user does not have a profile then redirect the user to
+ profile edit page.
+ """
+
+ def _wrapped_view(request, *args, **kwargs):
+ if user_has_profile(request.user):
+ return func(request, *args, **kwargs)
+ ci = RequestContext(request)
+ if request.user.groups.filter(name='moderator').exists():
+ template = 'manage.html'
+ else:
+ template = 'user.html'
+ form = ProfileForm(user=request.user, instance=None)
+ context = {'template': template, 'form': form}
+ return render_to_response('yaksh/editprofile.html', context,
+ context_instance=ci)
+ return _wrapped_view
+
def email_verified(func):
- """ This decorator is used to check if email is verified.
- If email is not verified then redirect user for email
- verification
+ """
+ This decorator is used to check if email is verified.
+ If email is not verified then redirect user for email
+ verification.
"""
def is_email_verified(request, *args, **kwargs):
@@ -14,7 +44,7 @@ def email_verified(func):
user = request.user
context = {}
if not settings.IS_DEVELOPMENT:
- if user.is_authenticated() and hasattr(user, 'profile'):
+ if user.is_authenticated() and user_has_profile(user):
if not user.profile.is_email_verified:
context['success'] = False
context['msg'] = "Your account is not verified. \
diff --git a/yaksh/forms.py b/yaksh/forms.py
index 3459be9..2740497 100644
--- a/yaksh/forms.py
+++ b/yaksh/forms.py
@@ -181,9 +181,13 @@ class QuizForm(forms.ModelForm):
user = kwargs.pop('user')
course_id = kwargs.pop('course')
super(QuizForm, self).__init__(*args, **kwargs)
- self.fields['prerequisite'] = forms.ModelChoiceField(
- queryset=Quiz.objects.filter(course__id=course_id,
- is_trial=False))
+
+ prerequisite_list = Quiz.objects.filter(
+ course__id=course_id,
+ is_trial=False
+ ).exclude(id=self.instance.id)
+
+ self.fields['prerequisite'] = forms.ModelChoiceField(prerequisite_list)
self.fields['prerequisite'].required = False
self.fields['course'] = forms.ModelChoiceField(
queryset=Course.objects.filter(id=course_id), empty_label=None)
@@ -240,6 +244,13 @@ class QuizForm(forms.ModelForm):
</p>
""")
+ def clean_prerequisite(self):
+ prereq = self.cleaned_data['prerequisite']
+ if prereq and prereq.prerequisite:
+ if prereq.prerequisite.id == self.instance.id:
+ raise forms.ValidationError("Please set another prerequisite quiz")
+ return prereq
+
class Meta:
model = Quiz
exclude = ["is_trial"]
diff --git a/yaksh/models.py b/yaksh/models.py
index b4c665c..d9218ef 100644
--- a/yaksh/models.py
+++ b/yaksh/models.py
@@ -96,11 +96,6 @@ def get_model_class(model):
return model_class
-def has_profile(user):
- """ check if user has profile """
- return True if hasattr(user, 'profile') else False
-
-
def get_upload_dir(instance, filename):
return os.sep.join((
'question_%s' % (instance.question.id), filename
diff --git a/yaksh/send_emails.py b/yaksh/send_emails.py
index 24215dd..ae49f23 100644
--- a/yaksh/send_emails.py
+++ b/yaksh/send_emails.py
@@ -7,11 +7,14 @@ from string import digits, punctuation
import hashlib
from textwrap import dedent
import smtplib
+import os
# Django imports
from django.utils.crypto import get_random_string
from django.conf import settings
-from django.core.mail import send_mass_mail, send_mail
+from django.core.mail import EmailMultiAlternatives, send_mail
+from django.core.files.storage import default_storage
+from django.core.files.base import ContentFile
def generate_activation_key(username):
@@ -57,3 +60,30 @@ def send_user_mail(user_mail, key):
success = False
return success, msg
+
+def send_bulk_mail(subject, email_body, recipients, attachments):
+ try:
+ text_msg = ""
+ msg = EmailMultiAlternatives(subject, text_msg, settings.SENDER_EMAIL,
+ recipients
+ )
+ msg.attach_alternative(email_body, "text/html")
+ if attachments:
+ for file in attachments:
+ path = default_storage.save('attachments/'+file.name,
+ ContentFile(file.read())
+ )
+ msg.attach_file(os.sep.join((settings.MEDIA_ROOT, path)),
+ mimetype="text/html"
+ )
+ default_storage.delete(path)
+ msg.send()
+
+ message = "Email Sent Successfully"
+
+ except Exception as exc_msg:
+ message = """Error: {0}. Please check email address.\
+ If email address is correct then
+ Please contact {1}.""".format(exc_msg, settings.REPLY_EMAIL)
+
+ return message
diff --git a/yaksh/static/yaksh/js/course.js b/yaksh/static/yaksh/js/course.js
index 5b79e68..8fb2773 100644
--- a/yaksh/static/yaksh/js/course.js
+++ b/yaksh/static/yaksh/js/course.js
@@ -35,4 +35,35 @@ $(".reject").change( function(){
});
}
});
+
+$(function() {
+ $('textarea#email_body').froalaEditor({
+ heightMin: 200,
+ heightMax: 200
+ })
+ });
+
+$("#send_mail").click(function(){
+ var subject = $("#subject").val();
+ var body = $('#email_body').val();
+ var status = false;
+ var selected = [];
+ $('#reject input:checked').each(function() {
+ selected.push($(this).attr('value'));
+ });
+
+ if (subject == '' || body == ''){
+ $("#error_msg").html("Please enter mail details");
+ $("#dialog").dialog();
+ }
+ else if (selected.length == 0){
+ $("#error_msg").html("Please select atleast one user");
+ $("#dialog").dialog();
+ }
+ else {
+ status = true;
+ }
+ return status;
+});
+
});
diff --git a/yaksh/templates/yaksh/course_detail.html b/yaksh/templates/yaksh/course_detail.html
index cd4144f..bcada42 100644
--- a/yaksh/templates/yaksh/course_detail.html
+++ b/yaksh/templates/yaksh/course_detail.html
@@ -6,6 +6,13 @@
{% block script %}
<script language="JavaScript" type="text/javascript" src="{{ URL_ROOT }}/static/yaksh/js/course.js"></script>
+<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/froala-editor/2.5.1/js/froala_editor.min.js"></script>
+<script src="https://code.jquery.com/ui/1.9.1/jquery-ui.js"></script>
+{% endblock %}
+{% block css %}
+<link rel="stylesheet" href="https://code.jquery.com/ui/1.9.1/themes/base/jquery-ui.css">
+<link href="https://cdnjs.cloudflare.com/ajax/libs/froala-editor/2.5.1/css/froala_editor.min.css" rel="stylesheet" type="text/css" />
+<link href="https://cdnjs.cloudflare.com/ajax/libs/froala-editor/2.5.1/css/froala_style.min.css" rel="stylesheet" type="text/css" />
{% endblock %}
{% block content %}
<br/>
@@ -13,9 +20,17 @@
<div class="row">
<div class="col-sm-3 col-md-2 sidebar">
<ul class="nav nav-sidebar">
- <li><a href="#student-requests" id="request"> Requested Students </a></li>
- <li><a href="#enrolled-students" id="enroll-students"> Enrolled Students </a></li>
- <li><a href="#rejected-students" id="reject-students"> Rejected Students </a></li>
+ {% if state == 'mail'%}
+ <li><a href="{{URL_ROOT}}/exam/manage/course_detail/{{course.id}}/">
+ Go to Course Details</a></li>
+ {% else %}
+ <li><a href="#student-requests" id="request">
+ Requested Students </a></li>
+ <li><a href="#enrolled-students" id="enroll-students">
+ Enrolled Students </a></li>
+ <li><a href="#rejected-students" id="reject-students">
+ Rejected Students </a></li>
+ {% endif %}
<li>
<a href="{{URL_ROOT}}/exam/manage/toggle_status/{{ course.id }}/">
{% if course.active %}Deactivate Course {% else %} Activate Course {% endif %}</a>
@@ -24,16 +39,70 @@
<a href="{{URL_ROOT}}/exam/manage/duplicate_course/{{ course.id }}/">
Clone Course</a>
</li>
+ <li>
+ <a href="{{URL_ROOT}}/exam/manage/send_mail/{{ course.id }}/">
+ Send Mail</a>
+ </li>
</ul>
</div>
</div>
<div class="col-md-9 col-md-offset-2 main">
<div class="row">
+ {% if message %}
+ <div class="alert alert-warning" role="alert">
+ <center>
+ <strong> {{ message }} </strong>
+ </center>
+ </div>
+ {% endif %}
+ {% if state == 'mail' %}
+ <div id="enrolled-students">
+ <center><b><u>Send Mails to Students</u></b></center><br>
+ {% if course.get_enrolled %}
+ <input type="checkbox" class="reject"/>&nbsp;<font size="2">Select all</font>
+ <div id="reject">
+ <form action="{{URL_ROOT}}/exam/manage/send_mail/{{ course.id }}/" method="post" id="send_mail_form">
+ {% csrf_token %}
+ <table class="table table-striped">
+ <th></th>
+ <th></th>
+ <th>Full Name</th>
+ <th>Email</th>
+ <th>Roll Number</th>
+ <th>Institute</th>
+ <th>Department</th>
+ {% for enrolled in course.get_enrolled %}
+ <tr>
+ <td><input type="checkbox" name="check" value="{{ enrolled.id }}"></td>
+ <td>{{ forloop.counter }}.</td>
+ <td> {{ enrolled.get_full_name|title }} </td>
+ <td> {{enrolled.email}}</td>
+ <td> {{enrolled.profile.roll_number}}</td>
+ <td> {{enrolled.profile.institute}}</td>
+ <td> {{enrolled.profile.department}}</td>
+ </tr>
+ {% endfor %}
+ </table>
+ <br>
+ <textarea name="subject" id="subject" placeholder="Email Subject" cols="50"></textarea>
+ <br><br>
+ <textarea name="body" id="email_body"></textarea><br>
+ Attachments: <input type="file" name="email_attach" multiple="">
+ <br>
+ <button class="btn btn-success" type="submit" name='send_mail' value='send_mail' id="send_mail">
+ Send Mail to Selected Students</button>
+ </div>
+ {% endif %}
+ </form>
+ </div>
+ {% else %}
<div id="student-requests">
<center><b><u>Requests</u></b></center><br>
{% if course.get_requests %}
<input type="checkbox" class="checkall"/>&nbsp;<font size="2">Select all</font>
<div id="enroll-all">
+ <form action="{{URL_ROOT}}/exam/manage/enroll/{{ course.id }}/" method="post">
+ {% csrf_token %}
<table class="table table-striped">
<th></th>
<th></th>
@@ -43,9 +112,7 @@
<th>Institute</th>
<th>Department</th>
<th>Enroll/Reject</th>
- <form action="{{URL_ROOT}}/exam/manage/enroll/{{ course.id }}/" method="post">
- {% csrf_token %}
- {% for request in course.get_requests %}
+ {% for request in course.get_requests %}
<tr>
<td><input type="checkbox" name="check" value="{{ request.id }}"></td>
<td>{{ forloop.counter }}.</td>
@@ -76,6 +143,8 @@
{% if course.get_enrolled %}
<input type="checkbox" class="reject"/>&nbsp;<font size="2">Select all</font>
<div id="reject">
+ <form action="{{URL_ROOT}}/exam/manage/enrolled/reject/{{ course.id }}/" method="post" id="reject-form">
+ {% csrf_token %}
<table class="table table-striped">
<th></th>
<th></th>
@@ -86,9 +155,7 @@
<th>Department</th>
<th>Reject</th>
{% for enrolled in course.get_enrolled %}
- <form action="{{URL_ROOT}}/exam/manage/enrolled/reject/{{ course.id }}/" method="post">
- {% csrf_token %}
- <tr>
+ <tr>
<td><input type="checkbox" name="check" value="{{ enrolled.id }}"></td>
<td>{{ forloop.counter }}.</td>
<td> {{ enrolled.get_full_name|title }} </td>
@@ -99,11 +166,12 @@
<td><a class="btn btn-danger"
href="{{URL_ROOT}}/exam/manage/enrolled/reject/{{ course.id }}/{{ enrolled.id }}/">
Reject </a>
- </td>
- </tr>
+ </td>
+ </tr>
{% endfor %}
</table>
- <button class="btn btn-danger" type="submit" name='reject' value='reject'>Reject Selected</button>
+ <button class="btn btn-danger" type="submit" name='reject' value='reject'>
+ Reject Selected</button>
</div>
{% endif %}
</form>
@@ -114,6 +182,8 @@
{% if course.get_rejected %}
<input type="checkbox" class="enroll"/>&nbsp;<font size="2">Select all</font>
<div id="enroll">
+ <form action="{{URL_ROOT}}/exam/manage/enroll/rejected/{{ course.id }}/" method="post">
+ {% csrf_token %}
<table class="table table-striped">
<th></th>
<th></th>
@@ -123,9 +193,7 @@
<th>Institute</th>
<th>Department</th>
<th>Enroll</th>
- {% for rejected in course.get_rejected %}
- <form action="{{URL_ROOT}}/exam/manage/enroll/rejected/{{ course.id }}/" method="post">
- {% csrf_token %}
+ {% for rejected in course.get_rejected %}
<tr>
<td><input type="checkbox" name="check" value="{{ rejected.id }}"></td>
<td>{{ forloop.counter }}.</td>
@@ -149,6 +217,11 @@
{% endif %}
</form>
</div>
+ {% endif %}
</div>
</div>
+<!-- Dialog to display error message -->
+<div id="dialog" title="Alert">
+ <p id="error_msg"></p>
+</div>
{% endblock %}
diff --git a/yaksh/templates/yaksh/login.html b/yaksh/templates/yaksh/login.html
index e4b5933..f40b12f 100644
--- a/yaksh/templates/yaksh/login.html
+++ b/yaksh/templates/yaksh/login.html
@@ -36,6 +36,14 @@
<li>Scales to over 500+ simultaneous users.</li>
</ul>
</p>
+ <br/>
+ <p><b>Fork us at:</b>
+ <a class = "btn btn-social-icon btn-github"
+ href ="https://github.com/fossee/online_test">
+
+ <span class="fa fa-github" style="font-size:48px"></span>
+ </p>
+ </a>
</div>
</div>
diff --git a/yaksh/templates/yaksh/moderator_dashboard.html b/yaksh/templates/yaksh/moderator_dashboard.html
index faccffe..c61675d 100644
--- a/yaksh/templates/yaksh/moderator_dashboard.html
+++ b/yaksh/templates/yaksh/moderator_dashboard.html
@@ -20,7 +20,7 @@
{{ paper.quiz.course.name }}
</td>
<td>
- <a href="{{URL_ROOT}}/exam/manage/monitor/{{paper.id}}/">{{ paper.quiz.description }}</a>
+ <a href="{{URL_ROOT}}/exam/manage/monitor/{{ paper.quiz.id }}/">{{ paper.quiz.description }}</a>
</td>
<td>
{{ answer_papers|length }} user(s)
diff --git a/yaksh/test_views.py b/yaksh/test_views.py
index 0060ed8..064c39d 100644
--- a/yaksh/test_views.py
+++ b/yaksh/test_views.py
@@ -21,8 +21,9 @@ from django.conf import settings
from django.core.files.uploadedfile import SimpleUploadedFile
from yaksh.models import User, Profile, Question, Quiz, QuestionPaper,\
- QuestionSet, AnswerPaper, Answer, Course, StandardTestCase, has_profile,\
+ QuestionSet, AnswerPaper, Answer, Course, StandardTestCase,\
AssignmentUpload, FileUpload
+from yaksh.decorators import user_has_profile
class TestUserRegistration(TestCase):
@@ -90,18 +91,18 @@ class TestProfile(TestCase):
self.user2.delete()
- def test_has_profile_for_user_without_profile(self):
+ def test_user_has_profile_for_user_without_profile(self):
"""
If no profile exists for user passed as argument return False
"""
- has_profile_status = has_profile(self.user1)
+ has_profile_status = user_has_profile(self.user1)
self.assertFalse(has_profile_status)
- def test_has_profile_for_user_with_profile(self):
+ def test_user_has_profile_for_user_with_profile(self):
"""
If profile exists for user passed as argument return True
"""
- has_profile_status = has_profile(self.user2)
+ has_profile_status = user_has_profile(self.user2)
self.assertTrue(has_profile_status)
@@ -206,6 +207,30 @@ class TestProfile(TestCase):
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'yaksh/editprofile.html')
+ def test_edit_profile_get_for_user_without_profile(self):
+ """
+ If no profile exists a blank profile form will be displayed
+ """
+ self.client.login(
+ username=self.user1.username,
+ password=self.user1_plaintext_pass
+ )
+ response = self.client.get(reverse('yaksh:edit_profile'))
+ self.assertEqual(response.status_code, 200)
+ self.assertTemplateUsed(response, 'yaksh/editprofile.html')
+
+ def test_edit_profile_get_for_user_with_profile(self):
+ """
+ If profile exists a editprofile.html template will be rendered
+ """
+ self.client.login(
+ username=self.user2.username,
+ password=self.user2_plaintext_pass
+ )
+ response = self.client.get(reverse('yaksh:edit_profile'))
+ self.assertEqual(response.status_code, 200)
+ self.assertTemplateUsed(response, 'yaksh/editprofile.html')
+
def test_update_email_for_user_post(self):
""" POST request to update email if multiple users with same email are
found
@@ -249,6 +274,16 @@ class TestStudentDashboard(TestCase):
timezone='UTC'
)
+ # student without profile
+ self.student_no_profile_plaintext_pass = 'student2'
+ self.student_no_profile = User.objects.create_user(
+ username='student_no_profile',
+ password=self.student_no_profile_plaintext_pass,
+ first_name='first_name',
+ last_name='last_name',
+ email='student_no_profile@test.com'
+ )
+
# moderator
self.user_plaintext_pass = 'demo'
self.user = User.objects.create_user(
@@ -291,6 +326,30 @@ class TestStudentDashboard(TestCase):
redirection_url = '/exam/login/?next=/exam/quizzes/'
self.assertRedirects(response, redirection_url)
+ def test_student_dashboard_get_for_user_without_profile(self):
+ """
+ If no profile exists a blank profile form will be displayed
+ """
+ self.client.login(
+ username=self.student_no_profile.username,
+ password=self.student_no_profile_plaintext_pass
+ )
+ response = self.client.get(reverse('yaksh:quizlist_user'))
+ self.assertEqual(response.status_code, 200)
+ self.assertTemplateUsed(response, 'yaksh/editprofile.html')
+
+ def test_student_dashboard_get_for_user_with_profile(self):
+ """
+ If profile exists a editprofile.html template will be rendered
+ """
+ self.client.login(
+ username=self.student.username,
+ password=self.student_plaintext_pass
+ )
+ response = self.client.get(reverse('yaksh:quizlist_user'))
+ self.assertEqual(response.status_code, 200)
+ self.assertTemplateUsed(response, 'yaksh/quizzes_user.html')
+
def test_student_dashboard_all_courses_get(self):
"""
Check student dashboard for all non hidden courses
@@ -1195,6 +1254,7 @@ class TestAddTeacher(TestCase):
username=self.user.username,
password=self.user_plaintext_pass
)
+
teacher_id_list = []
for i in range(5):
@@ -1833,6 +1893,70 @@ class TestCourseDetail(TestCase):
self.assertEqual(response.status_code, 200)
course = Course.objects.get(name="Python Course")
self.assertFalse(course.active)
+ self.assertEqual(self.user1_course, response.context['course'])
+ self.assertEqual(response.status_code, 200)
+ self.assertTemplateUsed(response, 'yaksh/course_detail.html')
+
+ def test_send_mail_to_course_students(self):
+ """ Check if bulk mail is sent to multiple students enrolled in a course
+ """
+ self.client.login(
+ username=self.user1.username,
+ password=self.user1_plaintext_pass
+ )
+ self.student2 = User.objects.create_user(
+ username='demo_student2',
+ password=self.student_plaintext_pass,
+ first_name='student_first_name',
+ last_name='student_last_name',
+ email='demo_student2@test.com'
+ )
+ self.student3 = User.objects.create_user(
+ username='demo_student3',
+ password=self.student_plaintext_pass,
+ first_name='student_first_name',
+ last_name='student_last_name',
+ email='demo_student3@test.com'
+ )
+ self.student4 = User.objects.create_user(
+ username='demo_student4',
+ password=self.student_plaintext_pass,
+ first_name='student_first_name',
+ last_name='student_last_name',
+ email='demo_student4@test.com'
+ )
+ user_ids = [self.student.id, self.student2.id, self.student3.id,
+ self.student4.id]
+ user_emails = [self.student.email, self.student2.email,
+ self.student3.email, self.student4.email]
+
+ self.user1_course.students.add(*user_ids)
+ attachment = SimpleUploadedFile("file.txt", b"Test")
+ email_data = {
+ 'send_mail': 'send_mail', 'email_attach': [attachment],
+ 'subject': 'test_bulk_mail', 'body': 'Test_Mail',
+ 'check': user_ids
+ }
+ self.client.post(reverse(
+ 'yaksh:send_mail', kwargs={'course_id': self.user1_course.id}),
+ data=email_data
+ )
+ attachment_file = mail.outbox[0].attachments[0][0]
+ subject = mail.outbox[0].subject
+ body = mail.outbox[0].alternatives[0][0]
+ recipients = mail.outbox[0].recipients()
+ self.assertEqual(attachment_file, "file.txt")
+ self.assertEqual(subject, "test_bulk_mail")
+ self.assertEqual(body, "Test_Mail")
+ self.assertSequenceEqual(recipients, user_emails)
+
+ # Test for get request in send mail
+ get_response = self.client.get(reverse(
+ 'yaksh:send_mail', kwargs={'course_id': self.user1_course.id})
+ )
+ self.assertEqual(get_response.status_code, 200)
+ self.assertEqual(get_response.context['course'], self.user1_course)
+ self.assertEqual(get_response.context['state'], 'mail')
class TestEnrollRequest(TestCase):
@@ -2496,6 +2620,16 @@ class TestModeratorDashboard(TestCase):
position='Moderator',
timezone='UTC'
)
+
+ self.mod_no_profile_plaintext_pass = 'demo2'
+ self.mod_no_profile = User.objects.create_user(
+ username='demo_user2',
+ password=self.mod_no_profile_plaintext_pass,
+ first_name='user_first_name22',
+ last_name='user_last_name',
+ email='demo2@test.com'
+ )
+
self.mod_group.user_set.add(self.user)
self.course = Course.objects.create(name="Python Course",
enrollment="Enroll Request", creator=self.user)
@@ -2589,6 +2723,30 @@ class TestModeratorDashboard(TestCase):
self.assertEqual(response.status_code, 200)
self.assertRedirects(response, '/exam/quizzes/')
+ def test_moderator_dashboard_get_for_user_without_profile(self):
+ """
+ If no profile exists a blank profile form will be displayed
+ """
+ self.client.login(
+ username=self.mod_no_profile.username,
+ password=self.mod_no_profile_plaintext_pass
+ )
+ response = self.client.get(reverse('yaksh:quizlist_user'))
+ self.assertEqual(response.status_code, 200)
+ self.assertTemplateUsed(response, 'yaksh/editprofile.html')
+
+ def test_moderator_dashboard_get_for_user_with_profile(self):
+ """
+ If profile exists a editprofile.html template will be rendered
+ """
+ self.client.login(
+ username=self.user.username,
+ password=self.user_plaintext_pass
+ )
+ response = self.client.get(reverse('yaksh:quizlist_user'))
+ self.assertEqual(response.status_code, 200)
+ self.assertTemplateUsed(response, 'yaksh/quizzes_user.html')
+
def test_moderator_dashboard_get_all_quizzes(self):
"""
Check moderator dashboard to get all the moderator created quizzes
diff --git a/yaksh/urls.py b/yaksh/urls.py
index 87ee655..4aa3276 100644
--- a/yaksh/urls.py
+++ b/yaksh/urls.py
@@ -67,6 +67,7 @@ urlpatterns = [
name="enroll_user"),
url(r'manage/enroll/rejected/(?P<course_id>\d+)/(?P<user_id>\d+)/$',
views.enroll, {'was_rejected': True}),
+ url(r'manage/send_mail/(?P<course_id>\d+)/$', views.send_mail, name="send_mail"),
url(r'manage/reject/(?P<course_id>\d+)/(?P<user_id>\d+)/$', views.reject,
name="reject_user"),
url(r'manage/enrolled/reject/(?P<course_id>\d+)/(?P<user_id>\d+)/$',
diff --git a/yaksh/views.py b/yaksh/views.py
index 68253bc..81d180b 100644
--- a/yaksh/views.py
+++ b/yaksh/views.py
@@ -32,7 +32,7 @@ except ImportError:
# Local imports.
from yaksh.models import get_model_class, Quiz, Question, QuestionPaper, QuestionSet, Course
from yaksh.models import Profile, Answer, AnswerPaper, User, TestCase, FileUpload,\
- has_profile, StandardTestCase, McqTestCase,\
+ StandardTestCase, McqTestCase,\
StdIOBasedTestCase, HookTestCase, IntegerTestCase,\
FloatTestCase, StringTestCase
from yaksh.forms import UserRegisterForm, UserLoginForm, QuizForm,\
@@ -42,8 +42,8 @@ from yaksh.forms import UserRegisterForm, UserLoginForm, QuizForm,\
from .settings import URL_ROOT
from yaksh.models import AssignmentUpload
from .file_utils import extract_files
-from .send_emails import send_user_mail, generate_activation_key
-from .decorators import email_verified
+from .send_emails import send_user_mail, generate_activation_key, send_bulk_mail
+from .decorators import email_verified, has_profile
def my_redirect(url):
@@ -127,6 +127,7 @@ def user_logout(request):
@login_required
+@has_profile
@email_verified
def quizlist_user(request, enrolled=None):
"""Show All Quizzes that is available to logged-in user."""
@@ -143,7 +144,11 @@ def quizlist_user(request, enrolled=None):
courses = user.students.all()
title = 'Enrolled Courses'
else:
- courses = Course.objects.filter(active=True, is_trial=False, hidden=False)
+ courses = Course.objects.filter(
+ active=True, is_trial=False
+ ).exclude(
+ ~Q(requests=user), ~Q(rejected=user), hidden=True
+ )
title = 'All Courses'
context = {'user': user, 'courses': courses, 'title': title}
@@ -256,32 +261,27 @@ def add_quiz(request, course_id, quiz_id=None):
if form.is_valid():
form.save()
return my_redirect("/exam/manage/courses/")
- else:
- context["form"] = form
- return my_render_to_response('yaksh/add_quiz.html',
- context,
- context_instance=ci)
+
else:
quiz = Quiz.objects.get(id=quiz_id)
form = QuizForm(request.POST, user=user, course=course_id,
- instance=quiz)
+ instance=quiz
+ )
if form.is_valid():
form.save()
- context["quiz_id"] = quiz_id
return my_redirect("/exam/manage/courses/")
+
else:
- if quiz_id is None:
- form = QuizForm(course=course_id, user=user)
- else:
- quiz = Quiz.objects.get(id=quiz_id)
- form = QuizForm(user=user,course=course_id, instance=quiz)
- context["quiz_id"] = quiz_id
- context["form"] = form
- return my_render_to_response('yaksh/add_quiz.html',
- context,
- context_instance=ci)
+ quiz = Quiz.objects.get(id=quiz_id) if quiz_id else None
+ form = QuizForm(user=user,course=course_id, instance=quiz)
+ context["quiz_id"] = quiz_id
+ context["form"] = form
+ return my_render_to_response('yaksh/add_quiz.html',
+ context,
+ context_instance=ci)
@login_required
+@has_profile
@email_verified
def prof_manage(request, msg=None):
"""Take credentials of the user with professor/moderator
@@ -735,6 +735,39 @@ def enroll(request, course_id, user_id=None, was_rejected=False):
@login_required
@email_verified
+def send_mail(request, course_id, user_id=None):
+ user = request.user
+ ci = RequestContext(request)
+ if not is_moderator(user):
+ raise Http404('You are not allowed to view this page')
+
+ course = get_object_or_404(Course, pk=course_id)
+ if not course.is_creator(user) and not course.is_teacher(user):
+ raise Http404('This course does not belong to you')
+
+ message = None
+ if request.method == 'POST':
+ user_ids = request.POST.getlist('check')
+ if request.POST.get('send_mail') == 'send_mail':
+ users = User.objects.filter(id__in=user_ids)
+ recipients = [student.email for student in users]
+ email_body = request.POST.get('body')
+ subject = request.POST.get('subject')
+ attachments = request.FILES.getlist('email_attach')
+ message = send_bulk_mail(
+ subject, email_body, recipients, attachments
+ )
+ context = {
+ 'course': course, 'message': message,
+ 'state': 'mail'
+ }
+ return my_render_to_response(
+ 'yaksh/course_detail.html', context, context_instance=ci
+ )
+
+
+@login_required
+@email_verified
def reject(request, course_id, user_id=None, was_enrolled=False):
user = request.user
ci = RequestContext(request)
@@ -750,8 +783,10 @@ def reject(request, course_id, user_id=None, was_enrolled=False):
else:
reject_ids = [user_id]
if not reject_ids:
- return my_render_to_response('yaksh/course_detail.html', {'course': course},
- context_instance=ci)
+ message = "Please select atleast one User"
+ return my_render_to_response('yaksh/course_detail.html',
+ {'course': course, "message": message},
+ context_instance=ci)
users = User.objects.filter(id__in=reject_ids)
course.reject(was_enrolled, *users)
return course_detail(request, course_id)
@@ -1188,6 +1223,7 @@ def grade_user(request, quiz_id=None, user_id=None, attempt_number=None):
@login_required
+@has_profile
@email_verified
def view_profile(request):
""" view moderators and users profile """
@@ -1197,20 +1233,12 @@ def view_profile(request):
template = 'manage.html'
else:
template = 'user.html'
- context = {'template': template}
- if has_profile(user):
- context['user'] = user
- return my_render_to_response('yaksh/view_profile.html', context)
- else:
- form = ProfileForm(user=user)
- msg = True
- context['form'] = form
- context['msg'] = msg
- return my_render_to_response('yaksh/editprofile.html', context,
- context_instance=ci)
+ context = {'template': template, 'user': user}
+ return my_render_to_response('yaksh/view_profile.html', context)
@login_required
+@has_profile
@email_verified
def edit_profile(request):
""" edit profile details facility for moderator and students """
@@ -1222,10 +1250,7 @@ def edit_profile(request):
else:
template = 'user.html'
context = {'template': template}
- if has_profile(user):
- profile = Profile.objects.get(user_id=user.id)
- else:
- profile = None
+ profile = Profile.objects.get(user_id=user.id)
if request.method == 'POST':
form = ProfileForm(request.POST, user=user, instance=profile)