Commit b47b02d60e1e3030c88bb0c6bf333b02a13955c7

Authored by Matheus Lins
2 parents 6b2389de 604de34d

Merge branch 'dev' of https://github.com/amadeusproject/amadeuslms into dev

amadeus/settings.py
... ... @@ -185,11 +185,13 @@ MESSAGE_TAGS = {
185 185  
186 186 #Send email for forgot Password
187 187 EMAIL_USE_TLS = True
  188 +DEFAULT_FROM_EMAIL = 'test@gmail.com'
  189 +SERVER_EMAIL = 'test@gmail.com'
188 190 EMAIL_HOST = 'smtp.gmail.com'
189   -EMAIL_PORT = 25
  191 +EMAIL_PORT = 587
190 192 EMAIL_HOST_USER = 'amadeusteste@gmail.com'
191 193 EMAIL_HOST_PASSWORD = 'amadeusteste'
192   -# EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
  194 +EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
193 195  
194 196 #s3direct
195 197  
... ...
courses/models.py
... ... @@ -91,7 +91,7 @@ Activity is something that has a deadline and has to be delivered by the student
91 91 """
92 92 class Activity(Resource):
93 93 topic = models.ForeignKey(Topic, verbose_name = _('Topic'), related_name='activities')
94   - limit_date = models.DateTimeField(_('Deliver Date'))
  94 + limit_date = models.DateField(_('Deliver Date'))
95 95 students = models.ManyToManyField(User, verbose_name = _('Students'), related_name='activities')
96 96 all_students = models.BooleanField(_('All Students'), default=False)
97 97  
... ... @@ -100,7 +100,7 @@ class ActivityFile(models.Model):
100 100 diet = models.ForeignKey('Activity', related_name='files')
101 101 name = models.CharField(max_length=100)
102 102  
103   - def __str__(self):
  103 + def __str__(self):
104 104 return self.name
105 105  
106 106 class Meta:
... ...
links/admin.py
1 1 from django.contrib import admin
2 2  
3   -# Register your models here.
  3 +from .models import Link
  4 +
  5 +class LinkAdmin(admin.ModelAdmin):
  6 + list_display = ['name', 'link','description']
  7 + search_fields = ['name', 'link','description']
  8 +
  9 +
  10 +admin.site.register(Link, LinkAdmin)
... ...
links/views.py
... ... @@ -10,23 +10,29 @@ from .forms import *
10 10  
11 11 # Create your views here.
12 12 class CreateLink(generic.CreateView):
13   - template_name = 'links/'
  13 + template_name = 'links/link_modal.html'
14 14 form_class = CreateLinkForm
15 15 success_url = reverse_lazy()
  16 + context_object_name = 'links'
  17 +
16 18 def form_valid(self, form):
17   - form.save()
18   - messages.success(self.request, _('Link created successfully!'))
19   - return super(CreateLink, self).form_valid(form)
  19 + form.save()
  20 + messages.success(self.request, _('Link created successfully!'))
  21 + return super(CreateLink, self).form_valid(form)
  22 + def get_context_data(self, **kwargs):
  23 + context = {}
  24 + context['links'] = Link.objects.all()
  25 + return context
20 26  
21 27  
22 28 class DeleteLink(generic.DeleteView):
23   -
  29 + pass
24 30 class UpdateLink(generic.UpdateView):
25 31 template_name = 'links/'
26 32 form_class = UpdateLinkForm
27 33 success_url = reverse_lazy()
28 34 def form_valid(self, form):
29   - form.save()
30   - messages.success(self.request, _('Link updated successfully!'))
  35 + form.save()
  36 + messages.success(self.request, _('Link updated successfully!'))
31 37  
32   - return super(UpdateLink, self).form_valid(form)
  38 + return super(UpdateLink, self).form_valid(form)
... ...
poll/permissions.py
... ... @@ -10,3 +10,13 @@ def edit_poll(role, user, poll):
10 10 return True
11 11  
12 12 return False
  13 +
  14 +@register_object_checker()
  15 +def delete_poll(role, user, poll):
  16 + if (role == SystemAdmin):
  17 + return True
  18 +
  19 + if (user in poll.topic.subject.professors.all()):
  20 + return True
  21 +
  22 + return False
... ...
poll/templates/poll/view.html
1   -{% extends "topic/index.html" %}
  1 +{% extends "poll/create.html" %}
2 2  
3   -{% load i18n widget_tweaks dict_access static%}
4   -{% block content %}
5   -<!-- Modal (remember to change the ids!!!) -->
6   -<div class="modal fade" id="poll" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
7   - <div class="modal-dialog" role="document">
8   - <div class="modal-content">
  3 +{% load i18n dict_access static%}
9 4  
10   - <!-- Modal Header -->
11   - <div class="modal-header">
  5 +{% block style %}
  6 +{% endblock %}
12 7  
13   - <!-- Put your title here!!! -->
14   - <h4 class="modal-title" id="myModalLabel">{{form.question}}</h4>
  8 +{% block title_poll %}
  9 +<!-- Put your title here!!! -->
  10 +<h4 class="modal-title" id="myModalLabel">{{poll}}</h4>
  11 +{% endblock title_poll %}
15 12  
16   - </div>
17   - <!-- Modal Body -->
18   - <div class="modal-body">
19   -
20   - <!-- Put ONLY your content here!!! -->
21   - <div class="conteiner">
22   - </div>
23   - <form id="form" method="post">
24   - {% csrf_token %}
25   - {% for key in keys %}
26   - <div class="row form-group">
27   - <div class="col-md-1">
28   - </br>
29   - <label><span class="glyphicon glyphicon-move"></span></label>
30   - </div>
31   - <div class="col-md-10">
32   - <div class="has-success is-empty">
33   - <input type="text" name="{{key}}" class="form-control" placeholder='{% trans "Answer" %}' value="{{ answers|value:key }}">
34   - <span class="help-block">{% trans "Possible answer for the question" %}</span>
35   - </div>
36   - </div>
37   - <div class="col-md-1">
38   - </br>
39   - <label><span class="glyphicon glyphicon-remove" onclick="this.parentNode.parentNode.parentNode.parentNode.removeChild(this.parentNode.parentNode.parentNode);"></span></label>
40   - </div>
41   - </div>
42   - {% empty %}
43   - <div class="row form-group">
44   - <div class="col-md-1">
45   - </br>
46   - <label><span class="glyphicon glyphicon-move"></span></label>
47   - </div>
48   - <div class="col-md-10">
49   - <div class="has-success is-empty">
50   - <input type="text" name="1" class="form-control" placeholder='{% trans "Answer" %}'>
51   - <span class="help-block">{% trans "Possible answer for the question" %}</span>
52   - </div>
53   - </div>
54   - <div class="col-md-1">
55   - </br>
56   - <label><span class="glyphicon glyphicon-remove" onclick="this.parentNode.parentNode.parentNode.parentNode.removeChild(this.parentNode.parentNode.parentNode);"></span></label>
57   - </div>
58   - </div>
59   - {% endfor %}
60   - </form>
61   - </br>
62   - </div>
63   - <button type="button" id="add" class="btn btn-primary btn-block btn-sm">add</button>
64   - <div class="row form-group">
65   - <input form="form" class="form-control" type="date" name="{{form.limit_date.name}}" {% if form.limit_date.value != None %}value="{% if form.limit_date.value.year %}{{form.limit_date.value|date:'Y-m-d'}}{% else %}{{form.limit_date.value}}{% endif %}"{% endif %}>
66   - {% if form.limit_date.errors %}
67   - <div class="not_submited">
68   - </br>
69   - <div class="alert alert-danger alert-dismissible" role="alert">
70   - <button type="button" class="close" data-dismiss="alert" aria-label="Close">
71   - <span aria-hidden="true">&times;</span>
72   - </button>
73   - <ul>
74   - {% for error in form.limit_date.errors %}
75   - <li>{{ error }}</li>
76   - {% endfor %}
77   - </ul>
78   - </div>
79   - </div>
80   - {% endif %}
81   - </div>
82   -
83   - </div>
84   -
85   - <!-- Modal Footer -->
86   - <div class="modal-footer">
87   -
88   - <!-- Don't remove that!!! -->
89   - <button type="button" class="btn btn-danger btn-raised" data-dismiss="modal">{% trans "Close" %}</button>
90   -
91   - <!-- Put curtom buttons here!!! -->
92   - <button type="submite" id="button" form="form" class="btn btn-primary btn-raised">{% trans "Create" %}</button>
93   - </div>
94   -
95   - </div>
  13 +{% block content_poll %}
  14 +<!-- Put ONLY your content here!!! -->
  15 +<div class="row">
  16 + <div class="col-md-10 col-md-offset-1">
  17 + <h3>{% trans "Limit date:" %} {{poll.limit_date|date:'d/m/y'}}</h3>
96 18 </div>
97 19 </div>
98   -<a href="" data-toggle="modal" data-target="#poll">modal</a>
99   -{% endblock content %}
  20 +{% endblock content_poll %}
  21 +
  22 +{% block button_save %}
  23 +<!-- Put curtom buttons here!!! -->
  24 +<button type="submite" id="button" form="form" class="btn btn-primary btn-raised">{% trans "Answer" %}</button>
  25 +{% endblock button_save %}
... ...
poll/tests/__init__.py 0 → 100644
poll/tests/poll.py 0 → 100644
... ... @@ -0,0 +1,131 @@
  1 +# coding=utf-8
  2 +
  3 +from django.test import TestCase, Client
  4 +from django.core.urlresolvers import reverse
  5 +
  6 +from rolepermissions.shortcuts import assign_role
  7 +
  8 +from courses.models import Category, Course, Subject, Topic
  9 +from poll.models import Poll
  10 +from users.models import User
  11 +
  12 +class PollTestCase(TestCase):
  13 + def setUp(self):
  14 + self.client = Client()
  15 +
  16 + self.user_professor = User.objects.create_user(
  17 + username = 'professor',
  18 + email = 'professor@amadeus.com',
  19 + is_staff = False,
  20 + is_active = True,
  21 + password = 'testing',
  22 + type_profile = 1
  23 + )
  24 + assign_role(self.user_professor, 'professor')
  25 +
  26 + self.user_student = User.objects.create_user(
  27 + username = 'student',
  28 + email = 'student@amadeus.com',
  29 + is_staff = False,
  30 + is_active = True,
  31 + password = 'testing',
  32 + type_profile = 2
  33 + )
  34 + assign_role(self.user_student, 'student')
  35 +
  36 + self.category = Category(
  37 + name = 'Categoria Teste',
  38 + )
  39 + self.category.save()
  40 +
  41 + self.course = Course(
  42 + name = 'Curso Teste',
  43 + max_students = 50,
  44 + objectivies = "",
  45 + content = "",
  46 + init_register_date = '2016-08-26',
  47 + end_register_date = '2016-10-01',
  48 + init_date = '2016-10-05',
  49 + end_date = '2017-10-05',
  50 + category = self.category
  51 + )
  52 + self.course.save()
  53 +
  54 + self.subject = Subject(
  55 + name = 'Subject Test',
  56 + description = "description of the subject test",
  57 + visible = True,
  58 + course = self.course,
  59 + init_date = '2016-10-05',
  60 + end_date = '2017-10-05',
  61 + )
  62 + self.subject.save()
  63 +
  64 + self.subject.professors.add(self.user_professor)
  65 +
  66 + self.topic = Topic(
  67 + name = 'Topic Test',
  68 + description = "description of the topic test",
  69 + subject = self.subject,
  70 + owner = self.user_professor,
  71 + visible = True,
  72 + )
  73 + self.topic.save()
  74 +
  75 + self.poll = Poll(
  76 + name = 'Poll Test',
  77 + limit_date = '2016-10-05',
  78 + topic = self.topic,
  79 + )
  80 + self.poll.save()
  81 +
  82 + def test_poll_create(self):
  83 + self.client.login(username='professor', password='testing')
  84 + poll = self.topic.activities.all().count()
  85 + url = reverse('course:poll:create_poll',kwargs={'slug':self.topic.slug})
  86 + data = {
  87 + "name": 'create poll test',
  88 + "limit_date":'2016-10-06',
  89 + "all_students":True,
  90 + }
  91 + response = self.client.post(url, data)
  92 + self.assertEqual(poll + 1, self.topic.activities.all().count()) # create a new poll
  93 + self.client.login(username='student', password='testing')
  94 + poll = self.topic.activities.all().count()
  95 + response = self.client.post(url, data)
  96 + self.assertEqual(poll, self.topic.activities.all().count()) # don't create a new poll
  97 +
  98 + def test_poll_update(self):
  99 + self.client.login(username='professor', password='testing')
  100 + url = reverse('course:poll:update_poll',kwargs={'slug':self.poll.slug})
  101 + title_poll = 'new poll name'
  102 + data = {
  103 + "name": title_poll,
  104 + "limit_date":'2016-11-06',
  105 + "all_students":True,
  106 + }
  107 + self.assertNotEqual(title_poll, self.topic.activities.all()[self.topic.activities.all().count() - 1].name) # old name
  108 + response = self.client.post(url, data)
  109 + self.assertEqual(title_poll,self.topic.activities.all()[self.topic.activities.all().count() - 1].name) # new name
  110 + poll_student = 'new poll name student'
  111 + data = {
  112 + "name": poll_student,
  113 + "limit_date":'2016-11-06',
  114 + "all_students":True,
  115 + }
  116 + self.client.login(username='student', password='testing')
  117 + self.assertNotEqual(poll_student, self.topic.activities.all()[self.topic.activities.all().count() - 1].name) # old name
  118 + response = self.client.post(url, data)
  119 + self.assertNotEqual(poll_student, self.topic.activities.all()[self.topic.activities.all().count() - 1].name) # new name
  120 +
  121 + def test_poll_delete(self):
  122 + url = reverse('course:poll:delete_poll',kwargs={'slug':self.poll.slug})
  123 + self.client.login(username='student', password='testing')
  124 + poll = self.topic.activities.all().count()
  125 + response = self.client.post(url)
  126 + self.assertEqual(poll,self.topic.activities.all().count())
  127 +
  128 + self.client.login(username='professor', password='testing')
  129 + poll = self.topic.activities.all().count()
  130 + response = self.client.post(url)
  131 + self.assertEqual(poll - 1,self.topic.activities.all().count())
... ...
poll/urls.py
... ... @@ -3,10 +3,9 @@ from django.conf.urls import url
3 3 from . import views
4 4  
5 5 urlpatterns = [
  6 + url(r'^view/(?P<slug>[\w\-_]+)/$', views.ViewPoll.as_view(), name='view_poll'), # poll slug
6 7 url(r'^create/(?P<slug>[\w\-_]+)/$', views.CreatePoll.as_view(), name='create_poll'), # topic slug
7 8 url(r'^update/(?P<slug>[\w\-_]+)/$', views.UpdatePoll.as_view(), name='update_poll'), # poll slug
8   - url(r'^teste/(?P<slug>[\w\-_]+)/$', views.CreatePollModal.as_view(), name='teste'), # poll
9   - url(r'^mymodal/', views.MyModal.as_view(), name='mymodal'),
10 9 url(r'^delete/(?P<slug>[\w\-_]+)/$', views.DeletePoll.as_view(), name='delete_poll'), # poll
11 10  
12 11 ]
... ...
poll/views.py
... ... @@ -17,8 +17,63 @@ from core.mixins import NotificationMixin
17 17 from users.models import User
18 18 from courses.models import Course, Topic
19 19  
20   -class CreatePoll(LoginRequiredMixin,generic.CreateView):
  20 +class ViewPoll(LoginRequiredMixin,generic.DetailView):
21 21  
  22 + model = Poll
  23 + context_object_name = 'poll'
  24 + template_name = 'poll/view.html'
  25 +
  26 + def get_object(self, queryset=None):
  27 + return get_object_or_404(Poll, slug = self.kwargs.get('slug'))
  28 +
  29 + def form_invalid(self, form,**kwargs):
  30 + context = super(ViewPoll, self).form_invalid(form)
  31 + answers = {}
  32 + for key in self.request.POST:
  33 + if(key != 'csrfmiddlewaretoken' and key != 'name' and key != 'limit_date' and key != 'all_students' and key != 'students'):
  34 + answers[key] = self.request.POST[key]
  35 +
  36 + keys = sorted(answers)
  37 + context.context_data['answers'] = answers
  38 + context.context_data['keys'] = keys
  39 + return context
  40 +
  41 + def form_valid(self, form):
  42 + poll = self.object
  43 + poll = form.save(commit = False)
  44 + poll.answers.all().delete()
  45 + poll.save()
  46 +
  47 +
  48 + for key in self.request.POST:
  49 + if(key != 'csrfmiddlewaretoken' and key != 'name' and key != 'limit_date' and key != 'all_students' and key != 'students'):
  50 + answer = Answer(answer=self.request.POST[key],order=key,poll=poll)
  51 + answer.save()
  52 +
  53 + return super(ViewPoll, self).form_valid(form)
  54 +
  55 + def get_context_data(self, **kwargs):
  56 + context = super(ViewPoll, self).get_context_data(**kwargs)
  57 + poll = self.object
  58 + context['course'] = poll.topic.subject.course
  59 + context['subject'] = poll.topic.subject
  60 + context['subjects'] = poll.topic.subject.course.subjects.all()
  61 +
  62 + answers = {}
  63 + for answer in poll.answers.all():
  64 + answers[answer.order] = answer.answer
  65 +
  66 + keys = sorted(answers)
  67 + context['answers'] = answers
  68 + context['keys'] = keys
  69 +
  70 + print (context)
  71 + return context
  72 +
  73 +
  74 +class CreatePoll(LoginRequiredMixin,HasRoleMixin,generic.CreateView):
  75 +
  76 + allowed_roles = ['professor', 'system_admin']
22 77 login_url = reverse_lazy("core:home")
23 78 redirect_field_name = 'next'
24 79 model = Poll
... ... @@ -60,8 +115,9 @@ class CreatePoll(LoginRequiredMixin,generic.CreateView):
60 115 context['subjects'] = topic.subject.course.subjects.all()
61 116 return context
62 117  
63   -class UpdatePoll(LoginRequiredMixin,generic.UpdateView):
  118 +class UpdatePoll(LoginRequiredMixin,HasRoleMixin,generic.UpdateView):
64 119  
  120 + allowed_roles = ['professor', 'system_admin']
65 121 login_url = reverse_lazy("core:home")
66 122 redirect_field_name = 'next'
67 123 model = Poll
... ... @@ -123,7 +179,7 @@ class UpdatePoll(LoginRequiredMixin,generic.UpdateView):
123 179  
124 180 return context
125 181  
126   -class DeletePoll(LoginRequiredMixin, generic.DeleteView):
  182 +class DeletePoll(LoginRequiredMixin, HasRoleMixin, generic.DeleteView):
127 183  
128 184 allowed_roles = ['professor', 'system_admin']
129 185 login_url = reverse_lazy("core:home")
... ... @@ -131,11 +187,11 @@ class DeletePoll(LoginRequiredMixin, generic.DeleteView):
131 187 model = Poll
132 188 template_name = 'poll/remove.html'
133 189  
134   - # def dispatch(self, *args, **kwargs):
135   - # poll = get_object_or_404(Poll, slug = self.kwargs.get('slug'))
136   - # if(not has_object_permission('delete_subject', self.request.user, subject)):
137   - # return self.handle_no_permission()
138   - # return super(DeleteSubjectView, self).dispatch(*args, **kwargs)
  190 + def dispatch(self, *args, **kwargs):
  191 + poll = get_object_or_404(Poll, slug = self.kwargs.get('slug'))
  192 + if(not has_object_permission('delete_poll', self.request.user, poll)):
  193 + return self.handle_no_permission()
  194 + return super(DeletePoll, self).dispatch(*args, **kwargs)
139 195  
140 196  
141 197 def get_context_data(self, **kwargs):
... ... @@ -150,70 +206,3 @@ class DeletePoll(LoginRequiredMixin, generic.DeleteView):
150 206  
151 207 def get_success_url(self):
152 208 return reverse_lazy('course:view_topic', kwargs={'slug' : self.object.topic.slug})
153   -
154   -
155   -from django_modalview.generic.edit import ModalCreateView
156   -from django_modalview.generic.component import ModalResponse
157   -
158   -class CreatePollModal(LoginRequiredMixin,ModalCreateView):
159   -
160   - login_url = reverse_lazy("core:home")
161   - redirect_field_name = 'next'
162   - model = Poll
163   - form_class = PollForm
164   - context_object_name = 'poll'
165   - template_name = 'poll/create.html'
166   - success_url = reverse_lazy('core:home')
167   -
168   - def form_invalid(self, form,**kwargs):
169   - context = super(CreatePollModal, self).form_invalid(form)
170   - answers = {}
171   - for key in self.request.POST:
172   - if(key != 'csrfmiddlewaretoken' and key != 'name' and key != 'limit_date' and key != 'all_students' and key != 'students'):
173   - answers[key] = self.request.POST[key]
174   -
175   - keys = sorted(answers)
176   - context.context_data['answers'] = answers
177   - context.context_data['keys'] = keys
178   - return context
179   -
180   - def form_valid(self, form):
181   - self.object = form.save(commit = False)
182   - topic = get_object_or_404(Topic, slug = self.kwargs.get('slug'))
183   - self.object.topic = topic
184   - self.object.save()
185   -
186   - for key in self.request.POST:
187   - if(key != 'csrfmiddlewaretoken' and key != 'name' and key != 'limit_date' and key != 'all_students' and key != 'students'):
188   - answer = Answer(answer=self.request.POST[key],order=key,poll=self.object)
189   - answer.save()
190   -
191   - return super(CreatePollModal, self).form_valid(form)
192   -
193   - def get_context_data(self, **kwargs):
194   - context = super(CreatePollModal, self).get_context_data(**kwargs)
195   - topic = get_object_or_404(Topic, slug = self.kwargs.get('slug'))
196   - context['course'] = topic.subject.course
197   - context['subject'] = topic.subject
198   - context['subjects'] = topic.subject.course.subjects.all()
199   - return context
200   -
201   -
202   -from django_modalview.generic.base import ModalTemplateView
203   -
204   -class MyModal(ModalTemplateView):
205   - '''
206   - This modal inherit of ModalTemplateView, so it just display a text without logic.
207   - '''
208   - def __init__(self, *args, **kwargs):
209   - '''
210   - You have to call the init method of the parent, before to overide the values:
211   - - title: The title display in the modal-header
212   - - icon: The css class that define the modal's icon
213   - - description: The content of the modal.
214   - - close_button: A button object that has several attributes.(explain below)
215   - '''
216   - super(MyModal, self).__init__(*args, **kwargs)
217   - self.title = "My modal"
218   - self.description = "This is my description"
219   - self.icon = "icon-mymodal"
... ...