Commit b47acb093b139c8fd93f8081afdd4a81de2da2c4
1 parent
ac46279c
Exists in
master
and in
3 other branches
Adding groups create/edit/replicate
Showing
10 changed files
with
265 additions
and
6 deletions
Show diff stats
amadeus/static/css/base/amadeus.css
| ... | ... | @@ -73,7 +73,7 @@ a:focus { |
| 73 | 73 | /* side bar menu ends*/ |
| 74 | 74 | |
| 75 | 75 | /* category app starts */ |
| 76 | -.category-panel > .panel-heading, .subject-panel > .panel-heading, .special-panel > .panel-heading, .topic-panel > .panel-heading { | |
| 76 | +.category-panel > .panel-heading, .subject-panel > .panel-heading, .special-panel > .panel-heading, .topic-panel > .panel-heading, .group-panel > .panel-heading { | |
| 77 | 77 | padding: 2px 0px; |
| 78 | 78 | } |
| 79 | 79 | ... | ... |
amadeus/static/css/themes/green.css
| ... | ... | @@ -59,6 +59,14 @@ a, a:focus, a:hover { |
| 59 | 59 | background-color: #039BE5 !important; |
| 60 | 60 | } |
| 61 | 61 | |
| 62 | +.group-panel > .panel-heading { | |
| 63 | + background-color: #FFFFFF !important; | |
| 64 | +} | |
| 65 | + | |
| 66 | +.group-panel .panel-title, .group-panel .category-header i, .group-panel .category-course-link { | |
| 67 | + color: #000000 !important; | |
| 68 | +} | |
| 69 | + | |
| 62 | 70 | .topic-panel > .panel-heading { |
| 63 | 71 | background-color: #7BA5B9 !important; |
| 64 | 72 | } | ... | ... |
students_group/forms.py
| ... | ... | @@ -2,6 +2,8 @@ |
| 2 | 2 | from django import forms |
| 3 | 3 | from django.utils.translation import ugettext_lazy as _ |
| 4 | 4 | |
| 5 | +from subjects.models import Subject | |
| 6 | + | |
| 5 | 7 | from .models import StudentsGroup |
| 6 | 8 | |
| 7 | 9 | class StudentsGroupForm(forms.ModelForm): |
| ... | ... | @@ -11,6 +13,11 @@ class StudentsGroupForm(forms.ModelForm): |
| 11 | 13 | super(StudentsGroupForm, self).__init__(*args, **kwargs) |
| 12 | 14 | |
| 13 | 15 | self.subject = kwargs['initial'].get('subject', None) |
| 16 | + | |
| 17 | + if self.instance.id: | |
| 18 | + self.subject = self.instance.subject | |
| 19 | + | |
| 20 | + self.fields['participants'].queryset = self.subject.students.all() | |
| 14 | 21 | |
| 15 | 22 | def clean_name(self): |
| 16 | 23 | name = self.cleaned_data.get('name', '') | ... | ... |
| ... | ... | @@ -0,0 +1,17 @@ |
| 1 | +# -*- coding: utf-8 -*- | |
| 2 | +# Generated by Django 1.10 on 2017-01-18 21:00 | |
| 3 | +from __future__ import unicode_literals | |
| 4 | + | |
| 5 | +from django.db import migrations | |
| 6 | + | |
| 7 | +from django.contrib.postgres.operations import UnaccentExtension | |
| 8 | + | |
| 9 | +class Migration(migrations.Migration): | |
| 10 | + | |
| 11 | + dependencies = [ | |
| 12 | + ('students_group', '0001_initial'), | |
| 13 | + ] | |
| 14 | + | |
| 15 | + operations = [ | |
| 16 | + UnaccentExtension() | |
| 17 | + ] | ... | ... |
| ... | ... | @@ -0,0 +1,113 @@ |
| 1 | +{% load widget_tweaks static i18n %} | |
| 2 | + | |
| 3 | +<form method="post" action="" enctype="multipart/form-data"> | |
| 4 | + {% csrf_token %} | |
| 5 | + {% for field in form %} | |
| 6 | + {% if field.auto_id == 'id_participants' %} | |
| 7 | + <div class="panel-group" id="professors_accordion" role="tablist" aria-multiselectable="true"> | |
| 8 | + <div class="panel panel-info"> | |
| 9 | + <div class="panel-heading"> | |
| 10 | + <div class="row"> | |
| 11 | + <div class="col-md-12"> | |
| 12 | + <a data-parent="#professors_accordion" data-toggle="collapse" href="#participants"> | |
| 13 | + <h4 class="panel-title"> | |
| 14 | + <button class="btn btn-default btn-xs text-center cat-selector"><i class="fa fa-angle-right fa-2x" aria-hidden="true"></i></button><label for="{{ field.auto_id }}">{{ field.label }}</label> | |
| 15 | + </h4> | |
| 16 | + </a> | |
| 17 | + </div> | |
| 18 | + </div> | |
| 19 | + </div> | |
| 20 | + <div id="participants" class="panel-collapse collapse"> | |
| 21 | + <p><em>{% trans 'Attribute students to group' %}:</em></p> | |
| 22 | + {% render_field field class='form-control' %} | |
| 23 | + </div> | |
| 24 | + </div> | |
| 25 | + </div> | |
| 26 | + {% else %} | |
| 27 | + <div class="form-group {% if form.has_error %} has-error {% endif %} is-fileinput"> | |
| 28 | + <label for="{{ field.auto_id }}" class="control-label">{{ field.label }}</label> | |
| 29 | + | |
| 30 | + {% if field.auto_id == 'id_description' %} | |
| 31 | + {% render_field field class='form-control text_wysiwyg' %} | |
| 32 | + {% else %} | |
| 33 | + {% render_field field class='form-control' %} | |
| 34 | + {% endif %} | |
| 35 | + </div> | |
| 36 | + {% endif %} | |
| 37 | + | |
| 38 | + <span class="help-block">{{ field.help_text }}</span> | |
| 39 | + | |
| 40 | + {% if field.errors %} | |
| 41 | + <div class="row"> | |
| 42 | + </br> | |
| 43 | + <div class="alert alert-danger alert-dismissible" role="alert"> | |
| 44 | + <button type="button" class="close" data-dismiss="alert" aria-label="Close"> | |
| 45 | + <span aria-hidden="true">×</span> | |
| 46 | + </button> | |
| 47 | + <ul> | |
| 48 | + {% for error in field.errors %} | |
| 49 | + <li>{{ error }}</li> | |
| 50 | + {% endfor %} | |
| 51 | + </ul> | |
| 52 | + </div> | |
| 53 | + </div> | |
| 54 | + {% endif %} | |
| 55 | + {% endfor %} | |
| 56 | + <div class="row text-center"> | |
| 57 | + <input type="submit" value="{% trans 'Save' %}" class="btn btn-success btn-raised" /> | |
| 58 | + </div> | |
| 59 | +</form> | |
| 60 | + | |
| 61 | +<script type="text/javascript"> | |
| 62 | + $('#id_participants').multiSelect({ | |
| 63 | + selectableHeader: "<input type='text' class='search-input category-search-users' autocomplete='off' placeholder=' '>", | |
| 64 | + selectionHeader: "<input type='text' class='search-input category-search-users' autocomplete='off' placeholder=''>", | |
| 65 | + afterInit: function(ms){ | |
| 66 | + var that = this, | |
| 67 | + $selectableSearch = that.$selectableUl.prev(), | |
| 68 | + $selectionSearch = that.$selectionUl.prev(), | |
| 69 | + selectableSearchString = '#'+that.$container.attr('id')+' .ms-elem-selectable:not(.ms-selected)', | |
| 70 | + selectionSearchString = '#'+that.$container.attr('id')+' .ms-elem-selection.ms-selected'; | |
| 71 | + | |
| 72 | + that.qs1 = $selectableSearch.quicksearch(selectableSearchString) | |
| 73 | + .on('keydown', function(e){ | |
| 74 | + if (e.which === 40){ | |
| 75 | + that.$selectableUl.focus(); | |
| 76 | + return false; | |
| 77 | + } | |
| 78 | + }); | |
| 79 | + | |
| 80 | + that.qs2 = $selectionSearch.quicksearch(selectionSearchString) | |
| 81 | + .on('keydown', function(e){ | |
| 82 | + if (e.which == 40){ | |
| 83 | + that.$selectionUl.focus(); | |
| 84 | + return false; | |
| 85 | + } | |
| 86 | + }); | |
| 87 | + }, | |
| 88 | + afterSelect: function(){ | |
| 89 | + this.qs1.cache(); | |
| 90 | + this.qs2.cache(); | |
| 91 | + }, | |
| 92 | + afterDeselect: function(){ | |
| 93 | + this.qs1.cache(); | |
| 94 | + this.qs2.cache(); | |
| 95 | + } | |
| 96 | + });// Used to create multi-select css style | |
| 97 | + | |
| 98 | + $('.collapse').on('show.bs.collapse', function (e) { | |
| 99 | + if($(this).is(e.target)){ | |
| 100 | + var btn = $(this).parent().find('.fa-angle-right'); | |
| 101 | + | |
| 102 | + btn.switchClass("fa-angle-right", "fa-angle-down", 250, "easeInOutQuad"); | |
| 103 | + } | |
| 104 | + }); | |
| 105 | + | |
| 106 | + $('.collapse').on('hide.bs.collapse', function (e) { | |
| 107 | + if($(this).is(e.target)){ | |
| 108 | + var btn = $(this).parent().find('.fa-angle-down'); | |
| 109 | + | |
| 110 | + btn.switchClass("fa-angle-down", "fa-angle-right", 250, "easeInOutQuad"); | |
| 111 | + } | |
| 112 | + }); | |
| 113 | +</script> | |
| 0 | 114 | \ No newline at end of file | ... | ... |
students_group/templates/groups/create.html
| ... | ... | @@ -4,15 +4,24 @@ |
| 4 | 4 | |
| 5 | 5 | {% block breadcrumbs %} |
| 6 | 6 | {{ block.super }} |
| 7 | - | |
| 8 | - {% trans 'Create Group' as bread %} | |
| 9 | - {% breadcrumb bread 'groups:create' subject.slug %} | |
| 7 | + | |
| 8 | + {% if group %} | |
| 9 | + {% trans 'Replicate: ' as bread %} | |
| 10 | + | |
| 11 | + {% with bread|add:group.name as bread_slug %} | |
| 12 | + {% breadcrumb bread_slug 'groups:replicate' subject.slug group.slug %} | |
| 13 | + {% endwith %} | |
| 14 | + {% else %} | |
| 15 | + {% trans 'Create Group' as bread %} | |
| 16 | + {% breadcrumb bread 'groups:create' subject.slug %} | |
| 17 | + {% endif %} | |
| 10 | 18 | {% endblock %} |
| 11 | 19 | |
| 12 | 20 | {% block content %} |
| 13 | 21 | <div class="card"> |
| 14 | 22 | <div class="card-content"> |
| 15 | 23 | <div class="card-body"> |
| 24 | + {% include 'groups/_form.html' %} | |
| 16 | 25 | </div> |
| 17 | 26 | </div> |
| 18 | 27 | </div> | ... | ... |
students_group/templates/groups/index.html
| ... | ... | @@ -27,14 +27,43 @@ |
| 27 | 27 | {% endif %} |
| 28 | 28 | |
| 29 | 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> | |
| 30 | + <div class="col-md-12 text-center"> | |
| 31 | + <a href="{% url 'groups:create' subject.slug %}" class="btn btn-raised btn-success">{% trans "Create Group" %}</a> | |
| 32 | 32 | </div> |
| 33 | 33 | </div> |
| 34 | 34 | |
| 35 | 35 | <div class="col-md-12 cards-content"> |
| 36 | 36 | <div class="panel-group" id="accordion" role="tablist" aria-multiselectable="true"> |
| 37 | 37 | {% for group in groups %} |
| 38 | + <div class="panel panel-info group-panel"> | |
| 39 | + <div class="panel-heading"> | |
| 40 | + <div class="row"> | |
| 41 | + <div class="col-md-12 category-header"> | |
| 42 | + <h4 class="panel-title"> | |
| 43 | + <a class="category-course-link pull-left" data-parent="#accordion" data-toggle="collapse" href="#{{group.slug}}"> | |
| 44 | + <button class="btn btn-default btn-xs text-center cat-selector"><i class="fa fa-angle-right fa-2x" aria-hidden="true"></i></button> {{ group }} | |
| 45 | + </a> | |
| 46 | + </h4> | |
| 47 | + | |
| 48 | + <div class="col-md-5 pull-right category-card-items"> | |
| 49 | + <a href="" id="moreActions" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> | |
| 50 | + <i class="fa fa-ellipsis-v" aria-hidden="true"></i> | |
| 51 | + </a> | |
| 52 | + <ul class="dropdown-menu pull-right" aria-labelledby="moreActions"> | |
| 53 | + <li> | |
| 54 | + <a href="{% url 'groups:replicate' subject.slug group.slug %}"> | |
| 55 | + <i class="fa fa-files-o" aria-hidden="true"></i> {% trans 'Replicate' %} | |
| 56 | + </a> | |
| 57 | + </li> | |
| 58 | + <li><a href="{% url 'groups:update' subject.slug group.slug %}"><i class="fa fa-pencil fa-fw" aria-hidden="true"></i> {% trans 'Edit' %}</a></li> | |
| 59 | + <li><a href=""><i class="fa fa-trash fa-fw" aria-hidden="true"></i> {% trans 'Remove' %}</a></li> | |
| 60 | + </ul> | |
| 61 | + </div> | |
| 62 | + </div> | |
| 63 | + </div> | |
| 64 | + </div> | |
| 65 | + </div> | |
| 66 | + | |
| 38 | 67 | {% endfor %} |
| 39 | 68 | |
| 40 | 69 | {% pagination request paginator page_obj %} | ... | ... |
| ... | ... | @@ -0,0 +1,25 @@ |
| 1 | +{% extends 'groups/index.html' %} | |
| 2 | + | |
| 3 | +{% load i18n django_bootstrap_breadcrumbs %} | |
| 4 | + | |
| 5 | +{% block breadcrumbs %} | |
| 6 | + {{ block.super }} | |
| 7 | + | |
| 8 | + {% trans 'Update: ' as bread %} | |
| 9 | + | |
| 10 | + {% with bread|add:group.name as bread_slug %} | |
| 11 | + {% breadcrumb bread_slug 'groups:update' subject.slug group.slug %} | |
| 12 | + {% endwith %} | |
| 13 | +{% endblock %} | |
| 14 | + | |
| 15 | +{% block content %} | |
| 16 | + <div class="card"> | |
| 17 | + <div class="card-content"> | |
| 18 | + <div class="card-body"> | |
| 19 | + {% include 'groups/_form.html' %} | |
| 20 | + </div> | |
| 21 | + </div> | |
| 22 | + </div> | |
| 23 | + <br clear="all" /> | |
| 24 | + <br clear="all" /> | |
| 25 | +{% endblock %} | ... | ... |
students_group/urls.py
| ... | ... | @@ -4,4 +4,6 @@ from . import views |
| 4 | 4 | urlpatterns = [ |
| 5 | 5 | url(r'^(?P<slug>[\w_-]+)/$', views.IndexView.as_view(), name='index'), |
| 6 | 6 | url(r'^create/(?P<slug>[\w_-]+)/$', views.CreateView.as_view(), name='create'), |
| 7 | + url(r'^update/(?P<sub_slug>[\w_-]+)/(?P<slug>[\w_-]+)/$', views.UpdateView.as_view(), name='update'), | |
| 8 | + url(r'^replicate/(?P<slug>[\w_-]+)/(?P<group_slug>[\w_-]+)/$', views.CreateView.as_view(), name='replicate'), | |
| 7 | 9 | ] |
| 8 | 10 | \ No newline at end of file | ... | ... |
students_group/views.py
| ... | ... | @@ -70,6 +70,13 @@ class CreateView(LoginRequiredMixin, generic.edit.CreateView): |
| 70 | 70 | slug = self.kwargs.get('slug', '') |
| 71 | 71 | |
| 72 | 72 | initial['subject'] = get_object_or_404(Subject, slug = slug) |
| 73 | + | |
| 74 | + if self.kwargs.get('group_slug'): | |
| 75 | + group = get_object_or_404(StudentsGroup, slug = self.kwargs['group_slug']) | |
| 76 | + initial = initial.copy() | |
| 77 | + initial['description'] = group.description | |
| 78 | + initial['name'] = group.name | |
| 79 | + initial['participants'] = group.participants.all() | |
| 73 | 80 | |
| 74 | 81 | return initial |
| 75 | 82 | |
| ... | ... | @@ -95,9 +102,51 @@ class CreateView(LoginRequiredMixin, generic.edit.CreateView): |
| 95 | 102 | |
| 96 | 103 | context['subject'] = subject |
| 97 | 104 | |
| 105 | + if self.kwargs.get('group_slug'): | |
| 106 | + group = get_object_or_404(StudentsGroup, slug = self.kwargs['group_slug']) | |
| 107 | + | |
| 108 | + context['title'] = _('Replicate Group') | |
| 109 | + | |
| 110 | + context['group'] = group | |
| 111 | + | |
| 98 | 112 | return context |
| 99 | 113 | |
| 100 | 114 | def get_success_url(self): |
| 101 | 115 | messages.success(self.request, _('The group "%s" was created successfully!')%(self.object.name)) |
| 102 | 116 | |
| 117 | + return reverse_lazy('groups:index', kwargs = {'slug': self.object.subject.slug}) | |
| 118 | + | |
| 119 | +class UpdateView(LoginRequiredMixin, generic.UpdateView): | |
| 120 | + login_url = reverse_lazy("users:login") | |
| 121 | + redirect_field_name = 'next' | |
| 122 | + | |
| 123 | + template_name = 'groups/update.html' | |
| 124 | + model = StudentsGroup | |
| 125 | + form_class = StudentsGroupForm | |
| 126 | + context_object_name = 'group' | |
| 127 | + | |
| 128 | + def dispatch(self, request, *args, **kwargs): | |
| 129 | + slug = self.kwargs.get('sub_slug', '') | |
| 130 | + subject = get_object_or_404(Subject, slug = slug) | |
| 131 | + | |
| 132 | + if not has_subject_permissions(request.user, subject): | |
| 133 | + return redirect(reverse_lazy('subjects:home')) | |
| 134 | + | |
| 135 | + return super(UpdateView, self).dispatch(request, *args, **kwargs) | |
| 136 | + | |
| 137 | + def get_context_data(self, **kwargs): | |
| 138 | + context = super(UpdateView, self).get_context_data(**kwargs) | |
| 139 | + | |
| 140 | + context['title'] = _('Update Group') | |
| 141 | + | |
| 142 | + slug = self.kwargs.get('sub_slug', '') | |
| 143 | + subject = get_object_or_404(Subject, slug = slug) | |
| 144 | + | |
| 145 | + context['subject'] = subject | |
| 146 | + | |
| 147 | + return context | |
| 148 | + | |
| 149 | + def get_success_url(self): | |
| 150 | + messages.success(self.request, _('The group "%s" was updated successfully!')%(self.object.name)) | |
| 151 | + | |
| 103 | 152 | return reverse_lazy('groups:index', kwargs = {'slug': self.object.subject.slug}) |
| 104 | 153 | \ No newline at end of file | ... | ... |