Commit ac46279c9d432abf68ac7cd007e3c3d134bad238
1 parent
e9573f81
Exists in
master
and in
3 other branches
Adding groups app
Showing
20 changed files
with
330 additions
and
2 deletions
Show diff stats
amadeus/settings.py
amadeus/urls.py
@@ -27,6 +27,7 @@ urlpatterns = [ | @@ -27,6 +27,7 @@ urlpatterns = [ | ||
27 | url(r'^$', index, name = 'home'), | 27 | url(r'^$', index, name = 'home'), |
28 | url(r'^categories/', include('categories.urls', namespace = 'categories')), | 28 | url(r'^categories/', include('categories.urls', namespace = 'categories')), |
29 | url(r'^subjects/', include('subjects.urls', namespace = 'subjects')), | 29 | url(r'^subjects/', include('subjects.urls', namespace = 'subjects')), |
30 | + url(r'^groups/', include('students_group.urls', namespace = 'groups')), | ||
30 | url(r'^topics/', include('topics.urls', namespace = 'topics')), | 31 | url(r'^topics/', include('topics.urls', namespace = 'topics')), |
31 | url(r'^mailsender/', include('mailsender.urls', namespace = 'mailsender')), | 32 | url(r'^mailsender/', include('mailsender.urls', namespace = 'mailsender')), |
32 | url(r'^security/', include('security.urls', namespace = 'security')), | 33 | url(r'^security/', include('security.urls', namespace = 'security')), |
@@ -0,0 +1,21 @@ | @@ -0,0 +1,21 @@ | ||
1 | +# -*- coding: utf-8 -*- | ||
2 | +# Generated by Django 1.10 on 2017-01-18 20:11 | ||
3 | +from __future__ import unicode_literals | ||
4 | + | ||
5 | +from django.conf import settings | ||
6 | +from django.db import migrations, models | ||
7 | + | ||
8 | + | ||
9 | +class Migration(migrations.Migration): | ||
10 | + | ||
11 | + dependencies = [ | ||
12 | + ('categories', '0006_auto_20170102_1856'), | ||
13 | + ] | ||
14 | + | ||
15 | + operations = [ | ||
16 | + migrations.AlterField( | ||
17 | + model_name='category', | ||
18 | + name='coordinators', | ||
19 | + field=models.ManyToManyField(blank=True, related_name='Coordenadores', to=settings.AUTH_USER_MODEL), | ||
20 | + ), | ||
21 | + ] |
@@ -0,0 +1,36 @@ | @@ -0,0 +1,36 @@ | ||
1 | +# coding=utf-8 | ||
2 | +from django import forms | ||
3 | +from django.utils.translation import ugettext_lazy as _ | ||
4 | + | ||
5 | +from .models import StudentsGroup | ||
6 | + | ||
7 | +class StudentsGroupForm(forms.ModelForm): | ||
8 | + subject = None | ||
9 | + | ||
10 | + def __init__(self, *args, **kwargs): | ||
11 | + super(StudentsGroupForm, self).__init__(*args, **kwargs) | ||
12 | + | ||
13 | + self.subject = kwargs['initial'].get('subject', None) | ||
14 | + | ||
15 | + def clean_name(self): | ||
16 | + name = self.cleaned_data.get('name', '') | ||
17 | + | ||
18 | + if self.instance.id: | ||
19 | + same_name = self.subject.group_subject.filter(name__unaccent__iexact = name).exclude(id = self.instance.id).count() | ||
20 | + else: | ||
21 | + same_name = self.subject.group_subject.filter(name__unaccent__iexact = name).count() | ||
22 | + | ||
23 | + if same_name > 0: | ||
24 | + self._errors['name'] = [_('This subject already has a group with this name')] | ||
25 | + | ||
26 | + return ValueError | ||
27 | + | ||
28 | + return name | ||
29 | + | ||
30 | + class Meta: | ||
31 | + model = StudentsGroup | ||
32 | + fields = ['name', 'description', 'participants'] | ||
33 | + widgets = { | ||
34 | + 'description': forms.Textarea, | ||
35 | + 'participants': forms.SelectMultiple, | ||
36 | + } | ||
0 | \ No newline at end of file | 37 | \ No newline at end of file |
@@ -0,0 +1,38 @@ | @@ -0,0 +1,38 @@ | ||
1 | +# -*- coding: utf-8 -*- | ||
2 | +# Generated by Django 1.10 on 2017-01-18 20:11 | ||
3 | +from __future__ import unicode_literals | ||
4 | + | ||
5 | +import autoslug.fields | ||
6 | +from django.conf import settings | ||
7 | +from django.db import migrations, models | ||
8 | +import django.db.models.deletion | ||
9 | + | ||
10 | + | ||
11 | +class Migration(migrations.Migration): | ||
12 | + | ||
13 | + initial = True | ||
14 | + | ||
15 | + dependencies = [ | ||
16 | + migrations.swappable_dependency(settings.AUTH_USER_MODEL), | ||
17 | + ('subjects', '0012_auto_20170112_1408'), | ||
18 | + ] | ||
19 | + | ||
20 | + operations = [ | ||
21 | + migrations.CreateModel( | ||
22 | + name='StudentsGroup', | ||
23 | + fields=[ | ||
24 | + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), | ||
25 | + ('name', models.CharField(max_length=200, verbose_name='Name')), | ||
26 | + ('slug', autoslug.fields.AutoSlugField(editable=False, populate_from='name', unique=True, verbose_name='Slug')), | ||
27 | + ('description', models.TextField(blank=True, verbose_name='Description')), | ||
28 | + ('create_date', models.DateTimeField(auto_now_add=True, verbose_name='Create Date')), | ||
29 | + ('last_update', models.DateTimeField(auto_now=True, verbose_name='Last Update')), | ||
30 | + ('participants', models.ManyToManyField(blank=True, related_name='group_participants', to=settings.AUTH_USER_MODEL, verbose_name='Participants')), | ||
31 | + ('subject', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='group_subject', to='subjects.Subject', verbose_name='Subject')), | ||
32 | + ], | ||
33 | + options={ | ||
34 | + 'verbose_name': 'Students Group', | ||
35 | + 'verbose_name_plural': 'Students Groups', | ||
36 | + }, | ||
37 | + ), | ||
38 | + ] |
@@ -0,0 +1,22 @@ | @@ -0,0 +1,22 @@ | ||
1 | +from django.db import models | ||
2 | +from autoslug.fields import AutoSlugField | ||
3 | +from django.utils.translation import ugettext_lazy as _ | ||
4 | + | ||
5 | +from subjects.models import Subject | ||
6 | +from users.models import User | ||
7 | + | ||
8 | +class StudentsGroup(models.Model): | ||
9 | + name = models.CharField(_('Name'), max_length = 200) | ||
10 | + slug = AutoSlugField(_("Slug"), populate_from = 'name', unique = True) | ||
11 | + description = models.TextField(_('Description'), blank = True) | ||
12 | + subject = models.ForeignKey(Subject, verbose_name = _('Subject'), related_name = 'group_subject', null = True) | ||
13 | + participants = models.ManyToManyField(User, verbose_name = _('Participants'), related_name = 'group_participants', blank = True) | ||
14 | + create_date = models.DateTimeField(_('Create Date'), auto_now_add = True) | ||
15 | + last_update = models.DateTimeField(_('Last Update'), auto_now = True) | ||
16 | + | ||
17 | + class Meta: | ||
18 | + verbose_name = _('Students Group') | ||
19 | + verbose_name_plural = _('Students Groups') | ||
20 | + | ||
21 | + def __str__(self): | ||
22 | + return self.name | ||
0 | \ No newline at end of file | 23 | \ No newline at end of file |
@@ -0,0 +1,21 @@ | @@ -0,0 +1,21 @@ | ||
1 | +{% extends 'groups/index.html' %} | ||
2 | + | ||
3 | +{% load i18n django_bootstrap_breadcrumbs %} | ||
4 | + | ||
5 | +{% block breadcrumbs %} | ||
6 | + {{ block.super }} | ||
7 | + | ||
8 | + {% trans 'Create Group' as bread %} | ||
9 | + {% breadcrumb bread 'groups:create' subject.slug %} | ||
10 | +{% endblock %} | ||
11 | + | ||
12 | +{% block content %} | ||
13 | + <div class="card"> | ||
14 | + <div class="card-content"> | ||
15 | + <div class="card-body"> | ||
16 | + </div> | ||
17 | + </div> | ||
18 | + </div> | ||
19 | + <br clear="all" /> | ||
20 | + <br clear="all" /> | ||
21 | +{% endblock %} |
@@ -0,0 +1,44 @@ | @@ -0,0 +1,44 @@ | ||
1 | +{% extends 'subjects/view.html' %} | ||
2 | + | ||
3 | +{% load static i18n pagination %} | ||
4 | +{% load django_bootstrap_breadcrumbs %} | ||
5 | + | ||
6 | +{% block javascript%} | ||
7 | + {{ block.super }} | ||
8 | +{% endblock%} | ||
9 | + | ||
10 | +{% block breadcrumbs %} | ||
11 | + {{ block.super }} | ||
12 | + | ||
13 | + {% trans 'Groups' as bread %} | ||
14 | + {% breadcrumb bread 'groups:index' subject.slug %} | ||
15 | +{% endblock %} | ||
16 | + | ||
17 | +{% block content %} | ||
18 | + {% if messages %} | ||
19 | + {% for message in messages %} | ||
20 | + <div class="alert alert-{{ message.tags }} alert-dismissible" role="alert"> | ||
21 | + <button type="button" class="close" data-dismiss="alert" aria-label="Close"> | ||
22 | + <span aria-hidden="true">×</span> | ||
23 | + </button> | ||
24 | + <p>{{ message }}</p> | ||
25 | + </div> | ||
26 | + {% endfor %} | ||
27 | + {% endif %} | ||
28 | + | ||
29 | + <div class='row'> | ||
30 | + <div class="col-md-offset-4 col-md-3"> | ||
31 | + <a href="{% url 'groups:create' subject.slug %}" class="btn btn-raised btn-success btn-block">{% trans "Create Group" %}</a> | ||
32 | + </div> | ||
33 | + </div> | ||
34 | + | ||
35 | + <div class="col-md-12 cards-content"> | ||
36 | + <div class="panel-group" id="accordion" role="tablist" aria-multiselectable="true"> | ||
37 | + {% for group in groups %} | ||
38 | + {% endfor %} | ||
39 | + | ||
40 | + {% pagination request paginator page_obj %} | ||
41 | + </div> | ||
42 | + </div> | ||
43 | + | ||
44 | +{% endblock %} | ||
0 | \ No newline at end of file | 45 | \ No newline at end of file |
@@ -0,0 +1,7 @@ | @@ -0,0 +1,7 @@ | ||
1 | +from django.conf.urls import url | ||
2 | +from . import views | ||
3 | + | ||
4 | +urlpatterns = [ | ||
5 | + url(r'^(?P<slug>[\w_-]+)/$', views.IndexView.as_view(), name='index'), | ||
6 | + url(r'^create/(?P<slug>[\w_-]+)/$', views.CreateView.as_view(), name='create'), | ||
7 | +] | ||
0 | \ No newline at end of file | 8 | \ No newline at end of file |
@@ -0,0 +1,103 @@ | @@ -0,0 +1,103 @@ | ||
1 | +from django.shortcuts import get_object_or_404, redirect, render | ||
2 | +from django.views import generic | ||
3 | +from django.contrib import messages | ||
4 | +from django.http import JsonResponse | ||
5 | +from django.core.urlresolvers import reverse, reverse_lazy | ||
6 | +from django.utils.translation import ugettext_lazy as _ | ||
7 | +from django.contrib.auth.mixins import LoginRequiredMixin | ||
8 | + | ||
9 | +from amadeus.permissions import has_subject_permissions | ||
10 | + | ||
11 | +from subjects.models import Subject | ||
12 | + | ||
13 | +from .models import StudentsGroup | ||
14 | +from .forms import StudentsGroupForm | ||
15 | + | ||
16 | +class IndexView(LoginRequiredMixin, generic.ListView): | ||
17 | + login_url = reverse_lazy("users:login") | ||
18 | + redirect_field_name = 'next' | ||
19 | + | ||
20 | + model = StudentsGroup | ||
21 | + context_object_name = 'groups' | ||
22 | + template_name = 'groups/index.html' | ||
23 | + paginate_by = 10 | ||
24 | + | ||
25 | + def dispatch(self, request, *args, **kwargs): | ||
26 | + slug = self.kwargs.get('slug', '') | ||
27 | + subject = get_object_or_404(Subject, slug = slug) | ||
28 | + | ||
29 | + if not has_subject_permissions(request.user, subject): | ||
30 | + return redirect(reverse_lazy('subjects:home')) | ||
31 | + | ||
32 | + return super(IndexView, self).dispatch(request, *args, **kwargs) | ||
33 | + | ||
34 | + def get_queryset(self): | ||
35 | + slug = self.kwargs.get('slug', '') | ||
36 | + subject = get_object_or_404(Subject, slug = slug) | ||
37 | + | ||
38 | + return StudentsGroup.objects.filter(subject = subject) | ||
39 | + | ||
40 | + def get_context_data(self, **kwargs): | ||
41 | + context = super(IndexView, self).get_context_data(**kwargs) | ||
42 | + | ||
43 | + slug = self.kwargs.get('slug', '') | ||
44 | + subject = get_object_or_404(Subject, slug = slug) | ||
45 | + | ||
46 | + context['title'] = _('Students Groups') | ||
47 | + context['subject'] = subject | ||
48 | + | ||
49 | + return context | ||
50 | + | ||
51 | +class CreateView(LoginRequiredMixin, generic.edit.CreateView): | ||
52 | + login_url = reverse_lazy("users:login") | ||
53 | + redirect_field_name = 'next' | ||
54 | + | ||
55 | + template_name = 'groups/create.html' | ||
56 | + form_class = StudentsGroupForm | ||
57 | + | ||
58 | + def dispatch(self, request, *args, **kwargs): | ||
59 | + slug = self.kwargs.get('slug', '') | ||
60 | + subject = get_object_or_404(Subject, slug = slug) | ||
61 | + | ||
62 | + if not has_subject_permissions(request.user, subject): | ||
63 | + return redirect(reverse_lazy('subjects:home')) | ||
64 | + | ||
65 | + return super(CreateView, self).dispatch(request, *args, **kwargs) | ||
66 | + | ||
67 | + def get_initial(self): | ||
68 | + initial = super(CreateView, self).get_initial() | ||
69 | + | ||
70 | + slug = self.kwargs.get('slug', '') | ||
71 | + | ||
72 | + initial['subject'] = get_object_or_404(Subject, slug = slug) | ||
73 | + | ||
74 | + return initial | ||
75 | + | ||
76 | + def form_valid(self, form): | ||
77 | + self.object = form.save(commit = False) | ||
78 | + | ||
79 | + slug = self.kwargs.get('slug', '') | ||
80 | + subject = get_object_or_404(Subject, slug = slug) | ||
81 | + | ||
82 | + self.object.subject = subject | ||
83 | + | ||
84 | + self.object.save() | ||
85 | + | ||
86 | + return super(CreateView, self).form_valid(form) | ||
87 | + | ||
88 | + def get_context_data(self, **kwargs): | ||
89 | + context = super(CreateView, self).get_context_data(**kwargs) | ||
90 | + | ||
91 | + context['title'] = _('Create Topic') | ||
92 | + | ||
93 | + slug = self.kwargs.get('slug', '') | ||
94 | + subject = get_object_or_404(Subject, slug = slug) | ||
95 | + | ||
96 | + context['subject'] = subject | ||
97 | + | ||
98 | + return context | ||
99 | + | ||
100 | + def get_success_url(self): | ||
101 | + messages.success(self.request, _('The group "%s" was created successfully!')%(self.object.name)) | ||
102 | + | ||
103 | + return reverse_lazy('groups:index', kwargs = {'slug': self.object.subject.slug}) | ||
0 | \ No newline at end of file | 104 | \ No newline at end of file |
subjects/templates/subjects/subject_card.html
@@ -18,6 +18,7 @@ | @@ -18,6 +18,7 @@ | ||
18 | <ul class="dropdown-menu pull-right" aria-labelledby="moreActions"> | 18 | <ul class="dropdown-menu pull-right" aria-labelledby="moreActions"> |
19 | <li><a href="{% url 'subjects:replicate' subject.slug %}"><i class="fa fa-files-o fa-fw" aria-hidden="true"></i>{% trans 'Replicate' %}</a></li> | 19 | <li><a href="{% url 'subjects:replicate' subject.slug %}"><i class="fa fa-files-o fa-fw" aria-hidden="true"></i>{% trans 'Replicate' %}</a></li> |
20 | <li><a href="{% url 'subjects:update' subject.slug %}"><i class="fa fa-pencil fa-fw" aria-hidden="true"></i>{% trans 'Edit' %}</a></li> | 20 | <li><a href="{% url 'subjects:update' subject.slug %}"><i class="fa fa-pencil fa-fw" aria-hidden="true"></i>{% trans 'Edit' %}</a></li> |
21 | + <li><a href="{% url 'groups:index' subject.slug %}"><i class="fa fa-group fa-fw" aria-hidden="true"></i>{% trans 'Groups' %}</a></li> | ||
21 | <li><a href="javascript:delete_subject.get('{% url 'subjects:delete' subject.slug %}?view=index','#subject','#modal_subject')"><i class="fa fa-trash fa-fw" aria-hidden="true"></i> {% trans 'Remove' %}</a></li> | 22 | <li><a href="javascript:delete_subject.get('{% url 'subjects:delete' subject.slug %}?view=index','#subject','#modal_subject')"><i class="fa fa-trash fa-fw" aria-hidden="true"></i> {% trans 'Remove' %}</a></li> |
22 | </ul> | 23 | </ul> |
23 | {% endif %} | 24 | {% endif %} |
subjects/templates/subjects/view.html
@@ -48,6 +48,7 @@ | @@ -48,6 +48,7 @@ | ||
48 | <ul class="dropdown-menu pull-right" aria-labelledby="moreActions"> | 48 | <ul class="dropdown-menu pull-right" aria-labelledby="moreActions"> |
49 | <li><a href="{% url 'subjects:replicate' subject.slug %}"><i class="fa fa-files-o fa-fw" aria-hidden="true"></i>{% trans 'Replicate' %}</a></li> | 49 | <li><a href="{% url 'subjects:replicate' subject.slug %}"><i class="fa fa-files-o fa-fw" aria-hidden="true"></i>{% trans 'Replicate' %}</a></li> |
50 | <li><a href="{% url 'subjects:update' subject.slug %}"><i class="fa fa-pencil fa-fw" aria-hidden="true"></i>{% trans 'Edit' %}</a></li> | 50 | <li><a href="{% url 'subjects:update' subject.slug %}"><i class="fa fa-pencil fa-fw" aria-hidden="true"></i>{% trans 'Edit' %}</a></li> |
51 | + <li><a href="{% url 'groups:index' subject.slug %}"><i class="fa fa-group fa-fw" aria-hidden="true"></i>{% trans 'Groups' %}</a></li> | ||
51 | <li><a href="javascript:delete_subject.get('{% url 'subjects:delete' subject.slug %}?view=index','#subject','#modal_subject')"><i class="fa fa-trash fa-fw" aria-hidden="true"></i> {% trans 'Remove' %}</a></li> | 52 | <li><a href="javascript:delete_subject.get('{% url 'subjects:delete' subject.slug %}?view=index','#subject','#modal_subject')"><i class="fa fa-trash fa-fw" aria-hidden="true"></i> {% trans 'Remove' %}</a></li> |
52 | </ul> | 53 | </ul> |
53 | {% endif %} | 54 | {% endif %} |
@@ -0,0 +1,19 @@ | @@ -0,0 +1,19 @@ | ||
1 | +# -*- coding: utf-8 -*- | ||
2 | +# Generated by Django 1.10 on 2017-01-18 20:11 | ||
3 | +from __future__ import unicode_literals | ||
4 | + | ||
5 | +from django.db import migrations | ||
6 | + | ||
7 | + | ||
8 | +class Migration(migrations.Migration): | ||
9 | + | ||
10 | + dependencies = [ | ||
11 | + ('topics', '0003_auto_20170116_2101'), | ||
12 | + ] | ||
13 | + | ||
14 | + operations = [ | ||
15 | + migrations.AlterModelOptions( | ||
16 | + name='topic', | ||
17 | + options={'ordering': ['order'], 'verbose_name': 'Topic', 'verbose_name_plural': 'Topics'}, | ||
18 | + ), | ||
19 | + ] |
topics/templates/topics/create.html
1 | {% extends 'subjects/view.html' %} | 1 | {% extends 'subjects/view.html' %} |
2 | 2 | ||
3 | -{% load django_bootstrap_breadcrumbs %} | 3 | +{% load i18n django_bootstrap_breadcrumbs %} |
4 | 4 | ||
5 | {% block breadcrumbs %} | 5 | {% block breadcrumbs %} |
6 | {{ block.super }} | 6 | {{ block.super }} |
7 | + | ||
7 | {% trans 'Create Topic' as bread %} | 8 | {% trans 'Create Topic' as bread %} |
8 | {% breadcrumb bread 'topics:create' subject.slug %} | 9 | {% breadcrumb bread 'topics:create' subject.slug %} |
9 | {% endblock %} | 10 | {% endblock %} |
topics/templates/topics/update.html
1 | {% extends 'subjects/view.html' %} | 1 | {% extends 'subjects/view.html' %} |
2 | 2 | ||
3 | -{% load django_bootstrap_breadcrumbs %} | 3 | +{% load i18n django_bootstrap_breadcrumbs %} |
4 | 4 | ||
5 | {% block breadcrumbs %} | 5 | {% block breadcrumbs %} |
6 | {{ block.super }} | 6 | {{ block.super }} |
7 | + | ||
7 | {% trans 'Update Topic' as bread %} | 8 | {% trans 'Update Topic' as bread %} |
8 | {% breadcrumb bread 'topics:update' subject.slug topic.slug %} | 9 | {% breadcrumb bread 'topics:update' subject.slug topic.slug %} |
9 | {% endblock %} | 10 | {% endblock %} |