Commit ac46279c9d432abf68ac7cd007e3c3d134bad238

Authored by Zambom
1 parent e9573f81

Adding groups app

amadeus/settings.py
... ... @@ -58,6 +58,7 @@ INSTALLED_APPS = [
58 58 'log',
59 59 'categories',
60 60 'subjects',
  61 + 'students_group',
61 62 'topics',
62 63 'mailsender',
63 64 'security',
... ...
amadeus/urls.py
... ... @@ -27,6 +27,7 @@ urlpatterns = [
27 27 url(r'^$', index, name = 'home'),
28 28 url(r'^categories/', include('categories.urls', namespace = 'categories')),
29 29 url(r'^subjects/', include('subjects.urls', namespace = 'subjects')),
  30 + url(r'^groups/', include('students_group.urls', namespace = 'groups')),
30 31 url(r'^topics/', include('topics.urls', namespace = 'topics')),
31 32 url(r'^mailsender/', include('mailsender.urls', namespace = 'mailsender')),
32 33 url(r'^security/', include('security.urls', namespace = 'security')),
... ...
categories/migrations/0007_auto_20170118_1711.py 0 → 100644
... ... @@ -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 + ]
... ...
students_group/__init__.py 0 → 100644
students_group/admin.py 0 → 100644
... ... @@ -0,0 +1,3 @@
  1 +from django.contrib import admin
  2 +
  3 +# Register your models here.
... ...
students_group/apps.py 0 → 100644
... ... @@ -0,0 +1,5 @@
  1 +from django.apps import AppConfig
  2 +
  3 +
  4 +class StudentsGroupConfig(AppConfig):
  5 + name = 'students_group'
... ...
students_group/forms.py 0 → 100644
... ... @@ -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 37 \ No newline at end of file
... ...
students_group/migrations/0001_initial.py 0 → 100644
... ... @@ -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 + ]
... ...
students_group/migrations/__init__.py 0 → 100644
students_group/models.py 0 → 100644
... ... @@ -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 23 \ No newline at end of file
... ...
students_group/templates/groups/create.html 0 → 100644
... ... @@ -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 %}
... ...
students_group/templates/groups/index.html 0 → 100644
... ... @@ -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">&times;</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 45 \ No newline at end of file
... ...
students_group/tests.py 0 → 100644
... ... @@ -0,0 +1,3 @@
  1 +from django.test import TestCase
  2 +
  3 +# Create your tests here.
... ...
students_group/urls.py 0 → 100644
... ... @@ -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 8 \ No newline at end of file
... ...
students_group/views.py 0 → 100644
... ... @@ -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 104 \ No newline at end of file
... ...
subjects/templates/subjects/subject_card.html
... ... @@ -18,6 +18,7 @@
18 18 <ul class="dropdown-menu pull-right" aria-labelledby="moreActions">
19 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 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 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>&nbsp;{% trans 'Remove' %}</a></li>
22 23 </ul>
23 24 {% endif %}
... ...
subjects/templates/subjects/view.html
... ... @@ -48,6 +48,7 @@
48 48 <ul class="dropdown-menu pull-right" aria-labelledby="moreActions">
49 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 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 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>&nbsp;{% trans 'Remove' %}</a></li>
52 53 </ul>
53 54 {% endif %}
... ...
topics/migrations/0004_auto_20170118_1711.py 0 → 100644
... ... @@ -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 1 {% extends 'subjects/view.html' %}
2 2  
3   -{% load django_bootstrap_breadcrumbs %}
  3 +{% load i18n django_bootstrap_breadcrumbs %}
4 4  
5 5 {% block breadcrumbs %}
6 6 {{ block.super }}
  7 +
7 8 {% trans 'Create Topic' as bread %}
8 9 {% breadcrumb bread 'topics:create' subject.slug %}
9 10 {% endblock %}
... ...
topics/templates/topics/update.html
1 1 {% extends 'subjects/view.html' %}
2 2  
3   -{% load django_bootstrap_breadcrumbs %}
  3 +{% load i18n django_bootstrap_breadcrumbs %}
4 4  
5 5 {% block breadcrumbs %}
6 6 {{ block.super }}
  7 +
7 8 {% trans 'Update Topic' as bread %}
8 9 {% breadcrumb bread 'topics:update' subject.slug topic.slug %}
9 10 {% endblock %}
... ...