Commit 17e3fac49c0a1fd50866f6cee9ae1b5ea6a29ff3
Exists in
master
and in
5 other branches
Merge branch 'dev' of https://github.com/amadeusproject/amadeuslms into dev
Showing
35 changed files
with
837 additions
and
210 deletions
Show diff stats
core/migrations/0001_initial.py
1 | 1 | # -*- coding: utf-8 -*- |
2 | -# Generated by Django 1.10 on 2016-09-22 05:46 | |
2 | +# Generated by Django 1.10 on 2016-09-30 15:39 | |
3 | 3 | from __future__ import unicode_literals |
4 | 4 | |
5 | 5 | import autoslug.fields |
6 | -from django.conf import settings | |
7 | 6 | from django.db import migrations, models |
8 | 7 | import django.db.models.deletion |
9 | 8 | |
... | ... | @@ -13,7 +12,6 @@ class Migration(migrations.Migration): |
13 | 12 | initial = True |
14 | 13 | |
15 | 14 | dependencies = [ |
16 | - migrations.swappable_dependency(settings.AUTH_USER_MODEL), | |
17 | 15 | ] |
18 | 16 | |
19 | 17 | operations = [ |
... | ... | @@ -33,7 +31,6 @@ class Migration(migrations.Migration): |
33 | 31 | name='Action_Resource', |
34 | 32 | fields=[ |
35 | 33 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), |
36 | - ('action', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.Action', verbose_name='Action_Applied')), | |
37 | 34 | ], |
38 | 35 | options={ |
39 | 36 | 'verbose_name': 'Action_Resource', |
... | ... | @@ -45,8 +42,6 @@ class Migration(migrations.Migration): |
45 | 42 | fields=[ |
46 | 43 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), |
47 | 44 | ('datetime', models.DateTimeField(auto_now_add=True, verbose_name='Date and Time of action')), |
48 | - ('action_resource', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.Action_Resource', verbose_name='Action_Resource')), | |
49 | - ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Actor')), | |
50 | 45 | ], |
51 | 46 | options={ |
52 | 47 | 'verbose_name': 'Log', |
... | ... | @@ -61,8 +56,6 @@ class Migration(migrations.Migration): |
61 | 56 | ('read', models.BooleanField(default=False, verbose_name='Read')), |
62 | 57 | ('datetime', models.DateTimeField(auto_now_add=True, verbose_name='Date and Time of action')), |
63 | 58 | ('action_resource', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.Action_Resource', verbose_name='Action_Resource')), |
64 | - ('actor', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='notification_Performer', to=settings.AUTH_USER_MODEL, verbose_name='Perfomer')), | |
65 | - ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='notification_Actor', to=settings.AUTH_USER_MODEL, verbose_name='User')), | |
66 | 59 | ], |
67 | 60 | options={ |
68 | 61 | 'verbose_name': 'Notification', |
... | ... | @@ -74,7 +67,7 @@ class Migration(migrations.Migration): |
74 | 67 | fields=[ |
75 | 68 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), |
76 | 69 | ('name', models.CharField(max_length=100, verbose_name='Name')), |
77 | - ('slug', autoslug.fields.AutoSlugField(editable=False, null=True, populate_from='name', unique=True, verbose_name='Slug')), | |
70 | + ('slug', autoslug.fields.AutoSlugField(editable=False, populate_from='name', unique=True, verbose_name='Slug')), | |
78 | 71 | ('created_date', models.DateField(auto_now_add=True, verbose_name='Created Date')), |
79 | 72 | ('url', models.CharField(default='', max_length=100, verbose_name='URL')), |
80 | 73 | ], |
... | ... | @@ -83,9 +76,4 @@ class Migration(migrations.Migration): |
83 | 76 | 'verbose_name_plural': 'Resources', |
84 | 77 | }, |
85 | 78 | ), |
86 | - migrations.AddField( | |
87 | - model_name='action_resource', | |
88 | - name='resource', | |
89 | - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.Resource', verbose_name='Resource'), | |
90 | - ), | |
91 | 79 | ] | ... | ... |
... | ... | @@ -0,0 +1,50 @@ |
1 | +# -*- coding: utf-8 -*- | |
2 | +# Generated by Django 1.10 on 2016-09-30 15:39 | |
3 | +from __future__ import unicode_literals | |
4 | + | |
5 | +from django.conf import settings | |
6 | +from django.db import migrations, models | |
7 | +import django.db.models.deletion | |
8 | + | |
9 | + | |
10 | +class Migration(migrations.Migration): | |
11 | + | |
12 | + initial = True | |
13 | + | |
14 | + dependencies = [ | |
15 | + ('core', '0001_initial'), | |
16 | + migrations.swappable_dependency(settings.AUTH_USER_MODEL), | |
17 | + ] | |
18 | + | |
19 | + operations = [ | |
20 | + migrations.AddField( | |
21 | + model_name='notification', | |
22 | + name='actor', | |
23 | + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='notification_Performer', to=settings.AUTH_USER_MODEL, verbose_name='Perfomer'), | |
24 | + ), | |
25 | + migrations.AddField( | |
26 | + model_name='notification', | |
27 | + name='user', | |
28 | + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='notification_Actor', to=settings.AUTH_USER_MODEL, verbose_name='User'), | |
29 | + ), | |
30 | + migrations.AddField( | |
31 | + model_name='log', | |
32 | + name='action_resource', | |
33 | + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.Action_Resource', verbose_name='Action_Resource'), | |
34 | + ), | |
35 | + migrations.AddField( | |
36 | + model_name='log', | |
37 | + name='user', | |
38 | + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Actor'), | |
39 | + ), | |
40 | + migrations.AddField( | |
41 | + model_name='action_resource', | |
42 | + name='action', | |
43 | + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.Action', verbose_name='Action_Applied'), | |
44 | + ), | |
45 | + migrations.AddField( | |
46 | + model_name='action_resource', | |
47 | + name='resource', | |
48 | + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.Resource', verbose_name='Resource'), | |
49 | + ), | |
50 | + ] | ... | ... |
core/models.py
... | ... | @@ -6,20 +6,20 @@ from autoslug.fields import AutoSlugField |
6 | 6 | |
7 | 7 | class Action(models.Model): |
8 | 8 | """ |
9 | - It represents an Action on the program by a User such as "create post", | |
9 | + It represents an Action on the program by a User such as "create post", | |
10 | 10 | "visualize post", etc. It is supposed to be created everytime we want an aciton |
11 | 11 | """ |
12 | 12 | |
13 | 13 | name = models.CharField(_('Name'), max_length = 100) |
14 | 14 | created_date = models.DateField(_('Created Date'), auto_now_add=True) |
15 | - | |
15 | + | |
16 | 16 | class Meta: |
17 | 17 | verbose_name = "Action" |
18 | 18 | verbose_name_plural = "Actions" |
19 | 19 | |
20 | 20 | def __str__(self): |
21 | 21 | return self.name |
22 | - | |
22 | + | |
23 | 23 | |
24 | 24 | class Resource(models.Model): |
25 | 25 | """ |
... | ... | @@ -27,38 +27,38 @@ class Resource(models.Model): |
27 | 27 | Example: Pool was answered (Resource: Pool), PDF was visualized(Resource: PDF). |
28 | 28 | |
29 | 29 | Attributes: |
30 | - @name: name of the resource affected, it will be unique because a resource can be affecte | |
30 | + @name: name of the resource affected, it will be unique because a resource can be affecte | |
31 | 31 | by a huge amount of actions |
32 | 32 | @created_date: The date the resource was created |
33 | 33 | @link: Which URL made that resource able to find |
34 | 34 | """ |
35 | 35 | |
36 | 36 | name = models.CharField(_('Name'), max_length =100) |
37 | - slug = AutoSlugField(_("Slug"), populate_from='name', unique=True, null=True) | |
37 | + slug = AutoSlugField(_("Slug"), populate_from='name', unique=True) | |
38 | 38 | created_date = models.DateField(_('Created Date'), auto_now_add=True) |
39 | 39 | url = models.CharField(_('URL'), max_length =100, default="") |
40 | 40 | |
41 | 41 | |
42 | 42 | class Meta: |
43 | 43 | verbose_name = "Resource" |
44 | - verbose_name_plural = "Resources" | |
44 | + verbose_name_plural = "Resources" | |
45 | 45 | |
46 | 46 | def __str__(self): |
47 | 47 | return self.name |
48 | 48 | |
49 | 49 | |
50 | 50 | class Action_Resource(models.Model): |
51 | - | |
51 | + | |
52 | 52 | action = models.ForeignKey(Action , verbose_name= _('Action_Applied')) |
53 | 53 | resource = models.ForeignKey(Resource, verbose_name = _('Resource')) |
54 | - | |
54 | + | |
55 | 55 | class Meta: |
56 | 56 | verbose_name = "Action_Resource" |
57 | 57 | verbose_name_plural = "Action_Resources" |
58 | 58 | |
59 | 59 | def __str__(self): |
60 | 60 | return ''.join([self.action.name, " / ", self.resource.name]) |
61 | - | |
61 | + | |
62 | 62 | |
63 | 63 | class Notification(models.Model): |
64 | 64 | """ |
... | ... | @@ -77,7 +77,7 @@ class Notification(models.Model): |
77 | 77 | datetime = models.DateTimeField(_("Date and Time of action"), auto_now_add = True) |
78 | 78 | action_resource = models.ForeignKey(Action_Resource, verbose_name = _('Action_Resource')) |
79 | 79 | actor = models.ForeignKey(User, related_name = _('%(class)s_Performer'), verbose_name= _('Perfomer'), null = True) |
80 | - | |
80 | + | |
81 | 81 | class Meta: |
82 | 82 | verbose_name = _("Notification") |
83 | 83 | verbose_name_plural = _("Notifications") | ... | ... |
core/static/js/base/amadeus.js
... | ... | @@ -134,6 +134,10 @@ This functions get the next 5 notifications from the user given a "step"(an amou |
134 | 134 | function getNotifications(step){ |
135 | 135 | $.get('/getNotifications', |
136 | 136 | {'steps':step, 'amount': 5}, function(data){ |
137 | - console.log(data); | |
137 | + $("#notification-dropdown").append(data); | |
138 | + $('#notification-see-more').remove(); | |
139 | + var seemore = '<li><a onclick="getNotifications('+(step+5)+')"> <div id="notification-see-more" class="list-group-item"> <div class="row-content"><p class="list-group-item-text">See More</p> </div> </a></li>'; | |
140 | + $("#notification-dropdown").append(seemore); | |
141 | + $("#notification-count").text(step+5); | |
138 | 142 | }); |
139 | 143 | } | ... | ... |
core/templates/base.html
... | ... | @@ -58,14 +58,14 @@ |
58 | 58 | <div class="navbar-collapse collapse navbar-responsive-collapse"> |
59 | 59 | <ul class="nav navbar-nav navbar-right notifications"> |
60 | 60 | <li class="" data-toggle="tooltip" data-placement="bottom" title data-original-title="notifications"> |
61 | - <a class="dropdown-toggle" data-toggle="dropdown"> <span class="badge notification-count">{{notifications.count}}</span><i class="fa fa-bell" aria-hidden="true"></i></a> | |
61 | + <a class="dropdown-toggle" data-toggle="dropdown"> <span id="notification-count" class="badge notification-count">{{notifications.count}}</span><i class="fa fa-bell" aria-hidden="true"></i></a> | |
62 | 62 | <ul id="notification-dropdown" class="dropdown-menu"> |
63 | 63 | <li class="dropdown-header">Notifications</li> |
64 | 64 | {% include "notifications.html" %} |
65 | 65 | |
66 | 66 | <li> |
67 | 67 | <a onclick="getNotifications(5)"> |
68 | - <div class="list-group-item"> | |
68 | + <div id="notification-see-more" class="list-group-item"> | |
69 | 69 | <div class="row-content"> |
70 | 70 | <p class="list-group-item-text">See More</p> |
71 | 71 | </div> | ... | ... |
core/templates/notifications.html
courses/migrations/0001_initial.py
1 | 1 | # -*- coding: utf-8 -*- |
2 | -# Generated by Django 1.10 on 2016-09-28 20:27 | |
2 | +# Generated by Django 1.10 on 2016-09-30 15:39 | |
3 | 3 | from __future__ import unicode_literals |
4 | 4 | |
5 | 5 | import autoslug.fields |
6 | -from django.conf import settings | |
7 | 6 | from django.db import migrations, models |
8 | 7 | import django.db.models.deletion |
9 | 8 | |
... | ... | @@ -13,8 +12,7 @@ class Migration(migrations.Migration): |
13 | 12 | initial = True |
14 | 13 | |
15 | 14 | dependencies = [ |
16 | - migrations.swappable_dependency(settings.AUTH_USER_MODEL), | |
17 | - ('core', '0001_initial'), | |
15 | + ('core', '0002_auto_20160930_1239'), | |
18 | 16 | ] |
19 | 17 | |
20 | 18 | operations = [ |
... | ... | @@ -23,7 +21,7 @@ class Migration(migrations.Migration): |
23 | 21 | fields=[ |
24 | 22 | ('resource_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='core.Resource')), |
25 | 23 | ('limit_date', models.DateTimeField(verbose_name='Deliver Date')), |
26 | - ('student', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='student')), | |
24 | + ('all_students', models.BooleanField(default=False, verbose_name='All Students')), | |
27 | 25 | ], |
28 | 26 | bases=('core.resource',), |
29 | 27 | ), |
... | ... | @@ -55,21 +53,18 @@ class Migration(migrations.Migration): |
55 | 53 | ('init_date', models.DateField(verbose_name='Begin of Course Date')), |
56 | 54 | ('end_date', models.DateField(verbose_name='End of Course Date')), |
57 | 55 | ('image', models.ImageField(blank=True, upload_to='courses/', verbose_name='Image')), |
58 | - ('category', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='courses.Category', verbose_name='Category')), | |
59 | - ('professors', models.ManyToManyField(related_name='courses_professors', to=settings.AUTH_USER_MODEL, verbose_name='Professors')), | |
60 | - ('students', models.ManyToManyField(related_name='courses_student', to=settings.AUTH_USER_MODEL, verbose_name='Students')), | |
61 | 56 | ], |
62 | 57 | options={ |
58 | + 'ordering': ('create_date', 'name'), | |
63 | 59 | 'verbose_name': 'Course', |
64 | 60 | 'verbose_name_plural': 'Courses', |
65 | - 'ordering': ('create_date', 'name'), | |
66 | 61 | }, |
67 | 62 | ), |
68 | 63 | migrations.CreateModel( |
69 | 64 | name='Material', |
70 | 65 | fields=[ |
71 | 66 | ('resource_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='core.Resource')), |
72 | - ('student', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='student')), | |
67 | + ('all_students', models.BooleanField(default=False, verbose_name='All Students')), | |
73 | 68 | ], |
74 | 69 | bases=('core.resource',), |
75 | 70 | ), |
... | ... | @@ -85,13 +80,11 @@ class Migration(migrations.Migration): |
85 | 80 | ('end_date', models.DateField(verbose_name='End of Subject Date')), |
86 | 81 | ('create_date', models.DateTimeField(auto_now_add=True, verbose_name='Creation Date')), |
87 | 82 | ('update_date', models.DateTimeField(auto_now=True, verbose_name='Date of last update')), |
88 | - ('course', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='subjects', to='courses.Course', verbose_name='Course')), | |
89 | - ('professors', models.ManyToManyField(related_name='subjects', to=settings.AUTH_USER_MODEL, verbose_name='Professors')), | |
90 | 83 | ], |
91 | 84 | options={ |
85 | + 'ordering': ('create_date', 'name'), | |
92 | 86 | 'verbose_name': 'Subject', |
93 | 87 | 'verbose_name_plural': 'Subjects', |
94 | - 'ordering': ('create_date', 'name'), | |
95 | 88 | }, |
96 | 89 | ), |
97 | 90 | migrations.CreateModel( |
... | ... | @@ -101,7 +94,6 @@ class Migration(migrations.Migration): |
101 | 94 | ('name', models.CharField(max_length=100, verbose_name='Name')), |
102 | 95 | ('slug', autoslug.fields.AutoSlugField(editable=False, populate_from='name', unique=True, verbose_name='Slug')), |
103 | 96 | ('description', models.TextField(blank=True, verbose_name='Description')), |
104 | - ('subjects', models.ManyToManyField(to='courses.Subject')), | |
105 | 97 | ], |
106 | 98 | options={ |
107 | 99 | 'verbose_name': 'subject category', |
... | ... | @@ -118,23 +110,11 @@ class Migration(migrations.Migration): |
118 | 110 | ('create_date', models.DateTimeField(auto_now_add=True, verbose_name='Creation Date')), |
119 | 111 | ('update_date', models.DateTimeField(auto_now=True, verbose_name='Date of last update')), |
120 | 112 | ('visible', models.BooleanField(default=False, verbose_name='Visible')), |
121 | - ('owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Owner')), | |
122 | - ('subject', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='courses.Subject', verbose_name='Subject')), | |
123 | 113 | ], |
124 | 114 | options={ |
115 | + 'ordering': ('create_date', 'name'), | |
125 | 116 | 'verbose_name': 'Topic', |
126 | 117 | 'verbose_name_plural': 'Topics', |
127 | - 'ordering': ('create_date', 'name'), | |
128 | 118 | }, |
129 | 119 | ), |
130 | - migrations.AddField( | |
131 | - model_name='material', | |
132 | - name='topic', | |
133 | - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='courses.Topic', verbose_name='Topic'), | |
134 | - ), | |
135 | - migrations.AddField( | |
136 | - model_name='activity', | |
137 | - name='topic', | |
138 | - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='courses.Topic', verbose_name='Topic'), | |
139 | - ), | |
140 | 120 | ] | ... | ... |
... | ... | @@ -0,0 +1,80 @@ |
1 | +# -*- coding: utf-8 -*- | |
2 | +# Generated by Django 1.10 on 2016-09-30 15:39 | |
3 | +from __future__ import unicode_literals | |
4 | + | |
5 | +from django.conf import settings | |
6 | +from django.db import migrations, models | |
7 | +import django.db.models.deletion | |
8 | + | |
9 | + | |
10 | +class Migration(migrations.Migration): | |
11 | + | |
12 | + initial = True | |
13 | + | |
14 | + dependencies = [ | |
15 | + migrations.swappable_dependency(settings.AUTH_USER_MODEL), | |
16 | + ('courses', '0001_initial'), | |
17 | + ] | |
18 | + | |
19 | + operations = [ | |
20 | + migrations.AddField( | |
21 | + model_name='topic', | |
22 | + name='owner', | |
23 | + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Owner'), | |
24 | + ), | |
25 | + migrations.AddField( | |
26 | + model_name='topic', | |
27 | + name='subject', | |
28 | + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='courses.Subject', verbose_name='Subject'), | |
29 | + ), | |
30 | + migrations.AddField( | |
31 | + model_name='subjectcategory', | |
32 | + name='subjects', | |
33 | + field=models.ManyToManyField(to='courses.Subject'), | |
34 | + ), | |
35 | + migrations.AddField( | |
36 | + model_name='subject', | |
37 | + name='course', | |
38 | + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='subjects', to='courses.Course', verbose_name='Course'), | |
39 | + ), | |
40 | + migrations.AddField( | |
41 | + model_name='subject', | |
42 | + name='professors', | |
43 | + field=models.ManyToManyField(related_name='subjects', to=settings.AUTH_USER_MODEL, verbose_name='Professors'), | |
44 | + ), | |
45 | + migrations.AddField( | |
46 | + model_name='material', | |
47 | + name='students', | |
48 | + field=models.ManyToManyField(related_name='materials', to=settings.AUTH_USER_MODEL, verbose_name='Students'), | |
49 | + ), | |
50 | + migrations.AddField( | |
51 | + model_name='material', | |
52 | + name='topic', | |
53 | + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='materials', to='courses.Topic', verbose_name='Topic'), | |
54 | + ), | |
55 | + migrations.AddField( | |
56 | + model_name='course', | |
57 | + name='category', | |
58 | + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='courses.Category', verbose_name='Category'), | |
59 | + ), | |
60 | + migrations.AddField( | |
61 | + model_name='course', | |
62 | + name='professors', | |
63 | + field=models.ManyToManyField(related_name='courses_professors', to=settings.AUTH_USER_MODEL, verbose_name='Professors'), | |
64 | + ), | |
65 | + migrations.AddField( | |
66 | + model_name='course', | |
67 | + name='students', | |
68 | + field=models.ManyToManyField(related_name='courses_student', to=settings.AUTH_USER_MODEL, verbose_name='Students'), | |
69 | + ), | |
70 | + migrations.AddField( | |
71 | + model_name='activity', | |
72 | + name='students', | |
73 | + field=models.ManyToManyField(related_name='activities', to=settings.AUTH_USER_MODEL, verbose_name='Students'), | |
74 | + ), | |
75 | + migrations.AddField( | |
76 | + model_name='activity', | |
77 | + name='topic', | |
78 | + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='activities', to='courses.Topic', verbose_name='Topic'), | |
79 | + ), | |
80 | + ] | ... | ... |
courses/models.py
... | ... | @@ -88,17 +88,19 @@ It is one kind of possible resources available inside a Topic. |
88 | 88 | Activity is something that has a deadline and has to be delivered by the student |
89 | 89 | """ |
90 | 90 | class Activity(Resource): |
91 | - topic = models.ForeignKey(Topic, verbose_name = _('Topic')) | |
91 | + topic = models.ForeignKey(Topic, verbose_name = _('Topic'), related_name='activities') | |
92 | 92 | limit_date = models.DateTimeField(_('Deliver Date')) |
93 | - student = models.ForeignKey(User, verbose_name = _('student')) | |
93 | + students = models.ManyToManyField(User, verbose_name = _('Students'), related_name='activities') | |
94 | + all_students = models.BooleanField(_('All Students'), default=False) | |
94 | 95 | |
95 | 96 | |
96 | 97 | """ |
97 | 98 | It represents any Material inside a topic, be it a file, a link, etc. |
98 | 99 | """ |
99 | 100 | class Material(Resource): |
100 | - topic = models.ForeignKey(Topic, verbose_name = _('Topic')) | |
101 | - student = models.ForeignKey(User, verbose_name = _('student')) | |
101 | + topic = models.ForeignKey(Topic, verbose_name = _('Topic'), related_name='materials') | |
102 | + students = models.ManyToManyField(User, verbose_name = _('Students'), related_name='materials') | |
103 | + all_students = models.BooleanField(_('All Students'), default=False) | |
102 | 104 | |
103 | 105 | """ |
104 | 106 | It is a category for each subject. |
... | ... | @@ -112,4 +114,3 @@ class SubjectCategory(models.Model): |
112 | 114 | class Meta: |
113 | 115 | verbose_name = _('subject category') |
114 | 116 | verbose_name_plural = _('subject categories') |
115 | - | ... | ... |
forum/migrations/0001_initial.py
1 | 1 | # -*- coding: utf-8 -*- |
2 | -# Generated by Django 1.10 on 2016-09-28 20:27 | |
2 | +# Generated by Django 1.10 on 2016-09-30 15:39 | |
3 | 3 | from __future__ import unicode_literals |
4 | 4 | |
5 | -from django.conf import settings | |
6 | 5 | from django.db import migrations, models |
7 | 6 | import django.db.models.deletion |
8 | 7 | |
... | ... | @@ -12,8 +11,7 @@ class Migration(migrations.Migration): |
12 | 11 | initial = True |
13 | 12 | |
14 | 13 | dependencies = [ |
15 | - migrations.swappable_dependency(settings.AUTH_USER_MODEL), | |
16 | - ('courses', '0001_initial'), | |
14 | + ('courses', '0002_auto_20160930_1239'), | |
17 | 15 | ] |
18 | 16 | |
19 | 17 | operations = [ |
... | ... | @@ -38,8 +36,6 @@ class Migration(migrations.Migration): |
38 | 36 | ('message', models.TextField(verbose_name='Post message')), |
39 | 37 | ('modification_date', models.DateTimeField(auto_now=True, verbose_name='Modification Date')), |
40 | 38 | ('post_date', models.DateTimeField(auto_now_add=True, verbose_name='Post Date')), |
41 | - ('forum', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='forum.Forum', verbose_name='Forum')), | |
42 | - ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Autor')), | |
43 | 39 | ], |
44 | 40 | options={ |
45 | 41 | 'verbose_name': 'Post', |
... | ... | @@ -54,7 +50,6 @@ class Migration(migrations.Migration): |
54 | 50 | ('modification_date', models.DateTimeField(auto_now=True, verbose_name='Modification Date')), |
55 | 51 | ('answer_date', models.DateTimeField(auto_now_add=True, verbose_name='Answer Date')), |
56 | 52 | ('post', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='forum.Post', verbose_name='Post')), |
57 | - ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Autor')), | |
58 | 53 | ], |
59 | 54 | options={ |
60 | 55 | 'verbose_name': 'Post Answer', | ... | ... |
... | ... | @@ -0,0 +1,35 @@ |
1 | +# -*- coding: utf-8 -*- | |
2 | +# Generated by Django 1.10 on 2016-09-30 15:39 | |
3 | +from __future__ import unicode_literals | |
4 | + | |
5 | +from django.conf import settings | |
6 | +from django.db import migrations, models | |
7 | +import django.db.models.deletion | |
8 | + | |
9 | + | |
10 | +class Migration(migrations.Migration): | |
11 | + | |
12 | + initial = True | |
13 | + | |
14 | + dependencies = [ | |
15 | + migrations.swappable_dependency(settings.AUTH_USER_MODEL), | |
16 | + ('forum', '0001_initial'), | |
17 | + ] | |
18 | + | |
19 | + operations = [ | |
20 | + migrations.AddField( | |
21 | + model_name='postanswer', | |
22 | + name='user', | |
23 | + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Autor'), | |
24 | + ), | |
25 | + migrations.AddField( | |
26 | + model_name='post', | |
27 | + name='forum', | |
28 | + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='forum.Forum', verbose_name='Forum'), | |
29 | + ), | |
30 | + migrations.AddField( | |
31 | + model_name='post', | |
32 | + name='user', | |
33 | + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Autor'), | |
34 | + ), | |
35 | + ] | ... | ... |
forum/models.py
... | ... | @@ -41,6 +41,15 @@ class Post(models.Model): |
41 | 41 | def __str__(self): |
42 | 42 | return ''.join([self.user.name, " / ", str(self.post_date)]) |
43 | 43 | |
44 | + def is_modified(self): | |
45 | + create = self.post_date.strftime("%Y-%m-%d %H:%M:%S") | |
46 | + edit = self.modification_date.strftime("%Y-%m-%d %H:%M:%S") | |
47 | + | |
48 | + if create != edit: | |
49 | + return True | |
50 | + | |
51 | + return False | |
52 | + | |
44 | 53 | """ |
45 | 54 | It represents an answer to a forum's post |
46 | 55 | """ | ... | ... |
forum/static/js/forum.js
... | ... | @@ -50,6 +50,54 @@ function showForum(url, forum_id) { |
50 | 50 | $('#forumModal').modal(); |
51 | 51 | } |
52 | 52 | |
53 | +/* | |
54 | +* | |
55 | +* Function to load form to edit post | |
56 | +* | |
57 | +*/ | |
58 | +function edit_post(url, post_id) { | |
59 | + $.ajax({ | |
60 | + url: url, | |
61 | + success: function(data) { | |
62 | + $("#post_"+post_id).find(".post_content").hide(); | |
63 | + $("#post_"+post_id).find(".post_content").after(data); | |
64 | + | |
65 | + var frm = $("#post_"+post_id).find(".edit_post_form"); | |
66 | + frm.submit(function () { | |
67 | + $.ajax({ | |
68 | + type: frm.attr('method'), | |
69 | + url: frm.attr('action'), | |
70 | + data: frm.serialize(), | |
71 | + success: function (data) { | |
72 | + $("#post_"+post_id).parent().after(data); | |
73 | + frm.parent().parent().remove(); | |
74 | + }, | |
75 | + error: function(data) { | |
76 | + console.log(frm.serialize()); | |
77 | + console.log('Error'); | |
78 | + } | |
79 | + }); | |
80 | + return false; | |
81 | + }); | |
82 | + } | |
83 | + }); | |
84 | +} | |
85 | + | |
86 | +/* | |
87 | +* | |
88 | +* Function to cancel post edition | |
89 | +* | |
90 | +*/ | |
91 | +function cancelEditPost(post_id) { | |
92 | + $("#post_"+post_id).find(".post_content").show(); | |
93 | + $("#post_"+post_id).find(".edit_post_form").remove(); | |
94 | +} | |
95 | + | |
96 | +/* | |
97 | +* | |
98 | +* Function to delete a post | |
99 | +* | |
100 | +*/ | |
53 | 101 | function delete_post(url, post) { |
54 | 102 | var csrftoken = getCookie('csrftoken'); |
55 | 103 | |
... | ... | @@ -74,36 +122,4 @@ function answer(id, url) { |
74 | 122 | }); |
75 | 123 | |
76 | 124 | $("#post_"+id).find(".answer_post").show(); |
77 | -} | |
78 | - | |
79 | -function showPosts(url, forum) { | |
80 | - if ($("#collapse" + forum).hasClass('in')) { | |
81 | - $("#collapse" + forum).collapse('hide'); | |
82 | - } else { | |
83 | - $.ajax({ | |
84 | - url: url, | |
85 | - data: {'forum': forum}, | |
86 | - success: function(data) { | |
87 | - $("#collapse" + forum).find(".well").html(data); | |
88 | - } | |
89 | - }); | |
90 | - | |
91 | - $("#collapse" + forum).collapse('show'); | |
92 | - } | |
93 | -} | |
94 | - | |
95 | -function showPostsAnswers(url, post) { | |
96 | - if ($("#collapse" + post).hasClass('in')) { | |
97 | - $("#collapse" + post).collapse('hide'); | |
98 | - } else { | |
99 | - $.ajax({ | |
100 | - url: url, | |
101 | - data: {'post': post}, | |
102 | - success: function(data) { | |
103 | - $("#collapse" + post).find(".well").html(data); | |
104 | - } | |
105 | - }); | |
106 | - | |
107 | - $("#collapse" + post).collapse('show'); | |
108 | - } | |
109 | 125 | } |
110 | 126 | \ No newline at end of file | ... | ... |
forum/templates/post/post_list.html
... | ... | @@ -17,17 +17,24 @@ |
17 | 17 | <i class="material-icons">more_horiz</i> |
18 | 18 | </a> |
19 | 19 | <ul class="dropdown-menu" aria-labelledby="dropdownMenu1"> |
20 | - <li><a href="javascript:void(0)"><i class="material-icons">create</i> {% trans 'Edit' %}</a></li> | |
20 | + <li><a href="javascript:edit_post('{% url 'forum:update_post' post.id %}', '{{ post.id }}')"><i class="material-icons">create</i> {% trans 'Edit' %}</a></li> | |
21 | 21 | <li><a href="javascript:javascript:delete_post('{% url 'forum:delete_post' post.id %}', '{{ post.id }}')"><i class="material-icons">delete_sweep</i> {% trans 'Remove' %}</a></li> |
22 | 22 | </ul> |
23 | 23 | </div> |
24 | 24 | {% endif %} |
25 | 25 | </div> |
26 | 26 | </h3> |
27 | - <div class="card-data"> | |
28 | - <p class="comment-date"><i class="fa fa-clock-o"></i> {{ post.post_date }}</p> | |
27 | + <div class="post_content"> | |
28 | + <div class="card-data"> | |
29 | + <p class="comment-date"> | |
30 | + <i class="fa fa-clock-o"></i> {{ post.post_date }} | |
31 | + {% if post.is_modified %} | |
32 | + <em> - {% trans 'Edited' %}</em> | |
33 | + {% endif %} | |
34 | + </p> | |
35 | + </div> | |
36 | + <p class="comment-text">{{ post.message|linebreaks }}</p> | |
29 | 37 | </div> |
30 | - <p class="comment-text">{{ post.message|linebreaks }}</p> | |
31 | 38 | <div class="answer_post"></div> |
32 | 39 | </div> |
33 | 40 | </div> | ... | ... |
forum/templates/post/post_render.html
... | ... | @@ -15,17 +15,24 @@ |
15 | 15 | <i class="material-icons">more_horiz</i> |
16 | 16 | </a> |
17 | 17 | <ul class="dropdown-menu" aria-labelledby="dropdownMenu1"> |
18 | - <li><a href="javascript:void(0)"><i class="material-icons">create</i> {% trans 'Edit' %}</a></li> | |
18 | + <li><a href="javascript:edit_post('{% url 'forum:update_post' post.id %}', '{{ post.id }}')"></li> | |
19 | 19 | <li><a href="javascript:delete_post('{% url 'forum:delete_post' post.id %}', '{{ post.id }}')"><i class="material-icons">delete_sweep</i> {% trans 'Remove' %}</a></li> |
20 | 20 | </ul> |
21 | 21 | </div> |
22 | 22 | {% endif %} |
23 | 23 | </div> |
24 | 24 | </h3> |
25 | - <div class="card-data"> | |
26 | - <p class="comment-date"><i class="fa fa-clock-o"></i> {{ post.post_date }}</p> | |
25 | + <div class="post_content"> | |
26 | + <div class="card-data"> | |
27 | + <p class="comment-date"> | |
28 | + <i class="fa fa-clock-o"></i> {{ post.post_date }} | |
29 | + {% if post.post_date != post.modifiction_date %} | |
30 | + <em> - {% trans 'Edited' %}</em> | |
31 | + {% endif %} | |
32 | + </p> | |
33 | + </div> | |
34 | + <p class="comment-text">{{ post.message|linebreaks }}</p> | |
27 | 35 | </div> |
28 | - <p class="comment-text">{{ post.message|linebreaks }}</p> | |
29 | 36 | <div class="answer_post"></div> |
30 | 37 | </div> |
31 | 38 | </div> |
32 | 39 | \ No newline at end of file | ... | ... |
... | ... | @@ -0,0 +1,37 @@ |
1 | +{% load i18n permission_tags list_post %} | |
2 | +{% load widget_tweaks %} | |
3 | + | |
4 | +<form class="edit_post_form" method="post" action="{% url 'forum:update_post' post.id %}" enctype="multipart/form-data"> | |
5 | + {% csrf_token %} | |
6 | + {% for field in form %} | |
7 | + {% if field.field.widget.input_type == 'hidden' %} | |
8 | + {% render_field field class='form-control' %} | |
9 | + {% else %} | |
10 | + <div class="form-group {% if form.has_error %} has-error {% endif %} is-fileinput"> | |
11 | + <div class="input-group"> | |
12 | + {% render_field field class='form-control' placeholder="Post a message" %} | |
13 | + <span class="help-block">{{ field.help_text }}</span> | |
14 | + {% if field.errors %} | |
15 | + <div class="row"> | |
16 | + <br /> | |
17 | + <div class="alert alert-danger alert-dismissible" role="alert"> | |
18 | + <button type="button" class="close" data-dismiss="alert" aria-label="Close"> | |
19 | + <span aria-hidden="true">×</span> | |
20 | + </button> | |
21 | + <ul> | |
22 | + {% for error in field.errors %} | |
23 | + <li>{{ error }}</li> | |
24 | + {% endfor %} | |
25 | + </ul> | |
26 | + </div> | |
27 | + </div> | |
28 | + {% endif %} | |
29 | + </div> | |
30 | + </div> | |
31 | + <div class="pull-right"> | |
32 | + <button type="button" onclick="cancelEditPost('{{ post.id }}')" class="btn btn-danger btn-raised">{% trans 'Cancel' %}</button> | |
33 | + <button type="submit" class="btn btn-primary btn-raised">{% trans 'Save changes' %}</button> | |
34 | + </div> | |
35 | + {% endif %} | |
36 | + {% endfor %} | |
37 | +</form> | |
0 | 38 | \ No newline at end of file | ... | ... |
forum/templatetags/list_post.py
... | ... | @@ -14,6 +14,6 @@ def list_posts(request, forum): |
14 | 14 | 'request': request, |
15 | 15 | } |
16 | 16 | |
17 | - context['posts'] = Post.objects.filter(forum = forum) | |
17 | + context['posts'] = Post.objects.filter(forum = forum).order_by('post_date') | |
18 | 18 | |
19 | 19 | return context |
20 | 20 | \ No newline at end of file | ... | ... |
forum/urls.py
... | ... | @@ -6,10 +6,10 @@ from . import views |
6 | 6 | urlpatterns = [ |
7 | 7 | url(r'^$', views.ForumIndex.as_view(), name='index'), |
8 | 8 | url(r'^create$', views.CreateForumView.as_view(), name='create'), |
9 | - url(r'^posts$', views.PostIndex.as_view(), name='posts'), | |
10 | 9 | url(r'^create_post$', views.CreatePostView.as_view(), name='create_post'), |
11 | - url(r'^render_post/([\w_-]+)/$', views.render_post, name='render_post'), | |
10 | + url(r'^update_post/(?P<pk>[\w_-]+)/$', views.PostUpdateView.as_view(), name='update_post'), | |
12 | 11 | url(r'^delete_post/(?P<pk>[\w_-]+)/$', views.PostDeleteView.as_view(), name='delete_post'), |
12 | + url(r'^render_post/([\w_-]+)/$', views.render_post, name='render_post'), | |
13 | 13 | url(r'^post_deleted/$', views.post_deleted, name='deleted_post'), |
14 | 14 | url(r'^post_answers$', views.PostAnswerIndex.as_view(), name='post_answers'), |
15 | 15 | url(r'^reply_post$', views.CreatePostAnswerView.as_view(), name='reply_post'), | ... | ... |
forum/views.py
... | ... | @@ -36,20 +36,6 @@ class CreateForumView(LoginRequiredMixin, generic.edit.CreateView): |
36 | 36 | form_class = ForumForm |
37 | 37 | success_url = reverse_lazy('forum:index') |
38 | 38 | |
39 | -class PostIndex(LoginRequiredMixin, generic.ListView): | |
40 | - login_url = reverse_lazy("core:home") | |
41 | - redirect_field_name = 'next' | |
42 | - | |
43 | - template_name = "post/post_list.html" | |
44 | - context_object_name = 'posts' | |
45 | - | |
46 | - def get_queryset(self): | |
47 | - forum = get_object_or_404(Forum, slug = self.request.GET.get('forum', '')) | |
48 | - | |
49 | - context = Post.objects.filter(forum = forum) | |
50 | - | |
51 | - return context | |
52 | - | |
53 | 39 | class CreatePostView(LoginRequiredMixin, generic.edit.CreateView): |
54 | 40 | login_url = reverse_lazy("core:home") |
55 | 41 | redirect_field_name = 'next' |
... | ... | @@ -77,6 +63,19 @@ def render_post(request, post): |
77 | 63 | |
78 | 64 | return render(request, "post/post_render.html", context) |
79 | 65 | |
66 | +class PostUpdateView(LoginRequiredMixin, generic.UpdateView): | |
67 | + login_url = reverse_lazy("core:home") | |
68 | + redirect_field_name = 'next' | |
69 | + | |
70 | + form_class = PostForm | |
71 | + model = Post | |
72 | + template_name = "post/post_update_form.html" | |
73 | + | |
74 | + def get_success_url(self): | |
75 | + self.success_url = reverse('forum:render_post', args = (self.object.id, )) | |
76 | + | |
77 | + return self.success_url | |
78 | + | |
80 | 79 | class PostDeleteView(LoginRequiredMixin, generic.DeleteView): |
81 | 80 | login_url = reverse_lazy("core:home") |
82 | 81 | redirect_field_name = 'next' | ... | ... |
poll/admin.py
1 | 1 | from django.contrib import admin |
2 | 2 | |
3 | -# Register your models here. | |
3 | +from .models import Poll, Answer | |
4 | + | |
5 | +class PollAdmin(admin.ModelAdmin): | |
6 | + list_display = ['name', 'slug','limit_date'] | |
7 | + search_fields = ['name','slug'] | |
8 | + | |
9 | +class AnswerAdmin(admin.ModelAdmin): | |
10 | + list_display = ['answer','order'] | |
11 | + search_fields = ['answer'] | |
12 | + | |
13 | +admin.site.register(Poll, PollAdmin) | |
14 | +admin.site.register(Answer, AnswerAdmin) | ... | ... |
... | ... | @@ -0,0 +1,35 @@ |
1 | +from django import forms | |
2 | +from django.utils.translation import ugettext_lazy as _ | |
3 | +from users.models import User | |
4 | +from .models import Poll | |
5 | + | |
6 | +class PollForm(forms.ModelForm): | |
7 | + | |
8 | + def __init__(self, *args, **kwargs): | |
9 | + super(PollForm, self).__init__(*args, **kwargs) | |
10 | + self.fields["all_students"].required = False | |
11 | + self.fields["all_students"].initial = False | |
12 | + self.fields["students"].required = False | |
13 | + | |
14 | + def clean_all_students(self): | |
15 | + if('all_students' not in self.data): | |
16 | + if('students' in self.data): | |
17 | + return False | |
18 | + raise forms.ValidationError(_('It is required one these fields.')) | |
19 | + else: | |
20 | + all_students = self.data['all_students'] | |
21 | + if(not all_students): | |
22 | + raise forms.ValidationError(_('It is required one these fields.')) | |
23 | + return True | |
24 | + | |
25 | + | |
26 | + class Meta: | |
27 | + model = Poll | |
28 | + fields = ['name','limit_date','students','all_students'] | |
29 | + | |
30 | + widgets = { | |
31 | + 'name': forms.TextInput(attrs={'placeholder': 'Question?'}), | |
32 | + 'limit_date': forms.DateTimeInput( | |
33 | + attrs={'placeholder': 'Maximum date permited to resolve the poll'}), | |
34 | + 'student': forms.Select(), | |
35 | + } | ... | ... |
poll/migrations/0001_initial.py
1 | 1 | # -*- coding: utf-8 -*- |
2 | -# Generated by Django 1.10 on 2016-09-28 20:27 | |
2 | +# Generated by Django 1.10 on 2016-09-30 15:40 | |
3 | 3 | from __future__ import unicode_literals |
4 | 4 | |
5 | 5 | from django.db import migrations, models |
... | ... | @@ -11,7 +11,7 @@ class Migration(migrations.Migration): |
11 | 11 | initial = True |
12 | 12 | |
13 | 13 | dependencies = [ |
14 | - ('courses', '0001_initial'), | |
14 | + ('courses', '0002_auto_20160930_1239'), | |
15 | 15 | ] |
16 | 16 | |
17 | 17 | operations = [ |
... | ... | @@ -23,20 +23,19 @@ class Migration(migrations.Migration): |
23 | 23 | ('order', models.PositiveSmallIntegerField(verbose_name='Order')), |
24 | 24 | ], |
25 | 25 | options={ |
26 | - 'verbose_name': 'Answer', | |
27 | 26 | 'verbose_name_plural': 'Answers', |
28 | 27 | 'ordering': ('order',), |
28 | + 'verbose_name': 'Answer', | |
29 | 29 | }, |
30 | 30 | ), |
31 | 31 | migrations.CreateModel( |
32 | 32 | name='Poll', |
33 | 33 | fields=[ |
34 | 34 | ('activity_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='courses.Activity')), |
35 | - ('question', models.CharField(max_length=300, verbose_name='Question')), | |
36 | 35 | ], |
37 | 36 | options={ |
38 | - 'verbose_name': 'Poll', | |
39 | 37 | 'verbose_name_plural': 'Polls', |
38 | + 'verbose_name': 'Poll', | |
40 | 39 | }, |
41 | 40 | bases=('courses.activity',), |
42 | 41 | ), | ... | ... |
poll/models.py
... | ... | @@ -6,7 +6,6 @@ from core.models import Resource |
6 | 6 | from courses.models import Activity |
7 | 7 | |
8 | 8 | class Poll(Activity): |
9 | - question = models.CharField(_('Question'), max_length = 300) | |
10 | 9 | |
11 | 10 | class Meta: |
12 | 11 | #ordering = ('create_date','name') |
... | ... | @@ -14,7 +13,7 @@ class Poll(Activity): |
14 | 13 | verbose_name_plural = _('Polls') |
15 | 14 | |
16 | 15 | def __str__(self): |
17 | - return str(self.question) + str("/") + str(self.topic) | |
16 | + return str(self.name) + str("/") + str(self.topic) | |
18 | 17 | |
19 | 18 | class Answer(models.Model): |
20 | 19 | answer = models.CharField(_("Answer"), max_length = 200) |
... | ... | @@ -27,4 +26,4 @@ class Answer(models.Model): |
27 | 26 | verbose_name_plural = _('Answers') |
28 | 27 | |
29 | 28 | def __str__(self): |
30 | - return str(self.question) + str("/") + str(self.topic) | |
29 | + return str(self.answer) + str("/") + str(self.poll) | ... | ... |
... | ... | @@ -0,0 +1,12 @@ |
1 | +from rolepermissions.permissions import register_object_checker | |
2 | +from amadeus.roles import SystemAdmin | |
3 | + | |
4 | +@register_object_checker() | |
5 | +def edit_poll(role, user, poll): | |
6 | + if (role == SystemAdmin): | |
7 | + return True | |
8 | + | |
9 | + if (user in poll.topic.subject.professors.all()): | |
10 | + return True | |
11 | + | |
12 | + return False | ... | ... |
... | ... | @@ -0,0 +1,206 @@ |
1 | +{% extends "topic/index.html" %} | |
2 | + | |
3 | +{% load i18n widget_tweaks dict_access static%} | |
4 | + | |
5 | +{% block style %} | |
6 | + <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script> | |
7 | +{% endblock %} | |
8 | + | |
9 | +{% block content %} | |
10 | +<!-- Modal (remember to change the ids!!!) --> | |
11 | +<div class="modal fade" id="poll" tabindex="-1" role="dialog" aria-labelledby="myModalLabel"> | |
12 | + <div class="modal-dialog" role="document"> | |
13 | + <div class="modal-content"> | |
14 | + | |
15 | + <!-- Modal Header --> | |
16 | + <div class="modal-header"> | |
17 | + | |
18 | + <!-- Put your title here!!! --> | |
19 | + <h4 class="modal-title" id="myModalLabel">{% trans "Create a Poll" %}</h4> | |
20 | + | |
21 | + </div> | |
22 | + <!-- Modal Body --> | |
23 | + <div class="modal-body"> | |
24 | + | |
25 | + <!-- Put ONLY your content here!!! --> | |
26 | + <div class="conteiner"> | |
27 | + <div class="row form-group"> | |
28 | + <div class="col-md-1"> | |
29 | + </br> | |
30 | + <label><span class="glyphicon glyphicon-hand-right"></span></label> | |
31 | + </div> | |
32 | + <div class="col-md-10"> | |
33 | + <div class="has-success"> | |
34 | + <input form="form" type="text" name="{{form.name.name}}" {% if form.name.value != None %}value="{{form.name.value}}" {% endif %} class="form-control" placeholder='{% trans "Question?" %}'> | |
35 | + <span class="help-block">{% trans "A Question to be answered" %}</span> | |
36 | + </div> | |
37 | + </div> | |
38 | + {% if form.name.errors %} | |
39 | + <div class="col-md-10 not_submited"> | |
40 | + </br> | |
41 | + <div class="alert alert-danger alert-dismissible" role="alert"> | |
42 | + <button type="button" class="close" data-dismiss="alert" aria-label="Close"> | |
43 | + <span aria-hidden="true">×</span> | |
44 | + </button> | |
45 | + <ul> | |
46 | + {% for error in form.name.errors %} | |
47 | + <li>{{ error }}</li> | |
48 | + {% endfor %} | |
49 | + </ul> | |
50 | + </div> | |
51 | + </div> | |
52 | + {% endif %} | |
53 | + </div> | |
54 | + <form id="form" class="" action="" method="post"> | |
55 | + {% csrf_token %} | |
56 | + {% for key in keys %} | |
57 | + <div class="row form-group"> | |
58 | + <div class="col-md-1"> | |
59 | + </br> | |
60 | + <label><span class="glyphicon glyphicon-move"></span></label> | |
61 | + </div> | |
62 | + <div class="col-md-10"> | |
63 | + <div class="has-success is-empty"> | |
64 | + <input type="text" name="{{key}}" class="form-control" placeholder='{% trans "Answer" %}' value="{{ answers|value:key }}"> | |
65 | + <span class="help-block">{% trans "Possible answer for the question" %}</span> | |
66 | + </div> | |
67 | + </div> | |
68 | + <div class="col-md-1"> | |
69 | + </br> | |
70 | + <label><span class="glyphicon glyphicon-remove" onclick="this.parentNode.parentNode.parentNode.parentNode.removeChild(this.parentNode.parentNode.parentNode);"></span></label> | |
71 | + </div> | |
72 | + </div> | |
73 | + {% empty %} | |
74 | + <div class="row form-group"> | |
75 | + <div class="col-md-1"> | |
76 | + </br> | |
77 | + <label><span class="glyphicon glyphicon-move"></span></label> | |
78 | + </div> | |
79 | + <div class="col-md-10"> | |
80 | + <div class="has-success is-empty"> | |
81 | + <input type="text" name="1" class="form-control" placeholder='{% trans "Answer" %}'> | |
82 | + <span class="help-block">{% trans "Possible answer for the question" %}</span> | |
83 | + </div> | |
84 | + </div> | |
85 | + <div class="col-md-1"> | |
86 | + </br> | |
87 | + <label><span class="glyphicon glyphicon-remove" onclick="this.parentNode.parentNode.parentNode.parentNode.removeChild(this.parentNode.parentNode.parentNode);"></span></label> | |
88 | + </div> | |
89 | + </div> | |
90 | + {% endfor %} | |
91 | + </form> | |
92 | + </br> | |
93 | + </div> | |
94 | + <button type="button" id="add" class="btn btn-primary btn-block btn-sm">add</button> | |
95 | + <div class="row form-group"> | |
96 | + <label for="{{ form.limit_date.auto_id }}">{{ form.limit_date.label }}</label> | |
97 | + {% render_field form.limit_date class="form-control" form="form"%} | |
98 | + {# <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 %}>#} | |
99 | + {% if form.limit_date.errors %} | |
100 | + <div class="not_submited"> | |
101 | + </br> | |
102 | + <div class="alert alert-danger alert-dismissible" role="alert"> | |
103 | + <button type="button" class="close" data-dismiss="alert" aria-label="Close"> | |
104 | + <span aria-hidden="true">×</span> | |
105 | + </button> | |
106 | + <ul> | |
107 | + {% for error in form.limit_date.errors %} | |
108 | + <li>{{ error }}</li> | |
109 | + {% endfor %} | |
110 | + </ul> | |
111 | + </div> | |
112 | + </div> | |
113 | + {% endif %} | |
114 | + </div> | |
115 | + | |
116 | + <div class="row form-group"> | |
117 | + <label for="{{ form.students.auto_id }}">{{ form.students.label }}</label> | |
118 | + {% render_field form.students class="form-control" form="form"%} | |
119 | + </div> | |
120 | + <div class="row form-group"> | |
121 | + <div class="checkbox"> | |
122 | + <label> | |
123 | + {% render_field form.all_students class="form-control" form="form" %}<span class="checkbox-material"><span class="check"></span></span> {{form.all_students.label }} | |
124 | + {# <input form="form" type="checkbox" name="{{form.all_students.name}}"><span class="checkbox-material"><span class="check"></span></span> {{ form.all_students.label }}#} | |
125 | + </label> | |
126 | + </div> | |
127 | + {% if form.all_students.errors %} | |
128 | + <div class="not_submited"> | |
129 | + </br> | |
130 | + <div class="alert alert-danger alert-dismissible" role="alert"> | |
131 | + <button type="button" class="close" data-dismiss="alert" aria-label="Close"> | |
132 | + <span aria-hidden="true">×</span> | |
133 | + </button> | |
134 | + <ul> | |
135 | + {% for error in form.all_students.errors %} | |
136 | + <li>{{ error }}</li> | |
137 | + {% endfor %} | |
138 | + </ul> | |
139 | + </div> | |
140 | + </div> | |
141 | + {% endif %} | |
142 | + | |
143 | + {# <label for="{{ form.all_students.auto_id }}">{{ form.all_students.label }}</label>#} | |
144 | + {# {% render_field form.all_students class="form-control" form="form"%}#} | |
145 | + </div> | |
146 | + | |
147 | + </div> | |
148 | + | |
149 | + <!-- Modal Footer --> | |
150 | + <div class="modal-footer"> | |
151 | + | |
152 | + <!-- Don't remove that!!! --> | |
153 | + <button type="button" class="btn btn-danger btn-raised" data-dismiss="modal">{% trans "Close" %}</button> | |
154 | + | |
155 | + <!-- Put curtom buttons here!!! --> | |
156 | + <button type="submite" id="button" form="form" class="btn btn-primary btn-raised">{% trans "Create" %}</button> | |
157 | + </div> | |
158 | + | |
159 | + </div> | |
160 | + </div> | |
161 | +</div> | |
162 | +<script type="text/javascript"> | |
163 | +// Este js tem que ficar aqui se não a tag "trans" não vai funcionar | |
164 | +$(window).ready(function() { // utilizado para abrir o modal quando tiver tido algum erro no preenchimento do formulario | |
165 | + if($('.not_submited').length){ | |
166 | + $('#poll').modal('show'); | |
167 | + } | |
168 | +}); | |
169 | +$( "#form" ).sortable({ // utilizado para fazer a re-organização das respostas | |
170 | + delay: 100, | |
171 | + distance: 5, | |
172 | + update: function( event, ui ) { | |
173 | + var cont = 1; | |
174 | + $("#form div div div input").each(function(){ | |
175 | + $(this).attr('name',cont++); | |
176 | + }); | |
177 | + }, | |
178 | +}); | |
179 | +name = 2; | |
180 | +$("#add").click(function() { // utilizado para adicionar um novo campo de resposta | |
181 | + //Obs: não funcionar se estiver importado no head, só funciona se estiver no final do arquivo | |
182 | + $("#form").append('\ | |
183 | + <div class="row form-group">\ | |
184 | + <div class="col-md-1">\ | |
185 | + </br>\ | |
186 | + <label><span class="glyphicon glyphicon-move"></span></label>\ | |
187 | + </div>\ | |
188 | + <div class="col-md-10">\ | |
189 | + <div class="has-success is-empty">\ | |
190 | + <input type="text" name="1" class="form-control" placeholder="{% trans "Answer" %}">\ | |
191 | + <span class="help-block">{% trans "Possible answer for the question" %}</span>\ | |
192 | + </div>\ | |
193 | + </div>\ | |
194 | + <div class="col-md-1">\ | |
195 | + </br>\ | |
196 | + <label><span class="glyphicon glyphicon-remove" onclick="this.parentNode.parentNode.parentNode.parentNode.removeChild(this.parentNode.parentNode.parentNode);"></span></label>\ | |
197 | + </div>\ | |
198 | + </div>'); | |
199 | + var cont = 1; | |
200 | + $("#form div div div input").each(function(){ | |
201 | + $(this).attr('name',cont++); | |
202 | + }); | |
203 | +}); | |
204 | +</script> | |
205 | +<a href="" data-toggle="modal" data-target="#poll">modal</a> | |
206 | +{% endblock content %} | ... | ... |
poll/templates/poll/poll.html
... | ... | @@ -1,52 +0,0 @@ |
1 | -{% extends "topic/index.html" %} | |
2 | - | |
3 | -{% load i18n %} | |
4 | - | |
5 | -{% block style %} | |
6 | - <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script> | |
7 | -{% endblock %} | |
8 | - | |
9 | -{% block content %} | |
10 | -<div class="col-md-8 col-md-offset-2"> | |
11 | - <div class="panel panel-primary"> | |
12 | - <div class="panel-heading"> | |
13 | - <h3 class="panel-title"> | |
14 | - <span class="glyphicon glyphicon-hand-right"></span> Question?</h3> | |
15 | - </div> | |
16 | - <div class="container-fluid"> | |
17 | - <form id="form" class="" action="" method="post"> | |
18 | - <div class="row form-group"> | |
19 | - <div class="col-md-1"> | |
20 | - </br> | |
21 | - <label><a href=""><span class="glyphicon glyphicon-move"></span></a></label> | |
22 | - </div> | |
23 | - <div class="col-md-10"> | |
24 | - <div class="form-control-md has-success is-empty"> | |
25 | - <input type="text" class="form-control" placeholder="Email address default size"> | |
26 | - <span class="help-block">{% trans "Possible Answer" %}</span> | |
27 | - </div> | |
28 | - </div> | |
29 | - <div class="col-md-1"> | |
30 | - </br> | |
31 | - <label><a href=""><span class="glyphicon glyphicon-remove" onclick="this.parentNode.parentNode.parentNode.parentNode.removeChild(this.parentNode.parentNode.parentNode);"></span></a></label> | |
32 | - </div> | |
33 | - </div> | |
34 | - </form> | |
35 | - </br> | |
36 | - </div> | |
37 | - | |
38 | - </div> | |
39 | - <div class="panel-footer text-center"> | |
40 | - <button type="button" id="add" class="btn btn-primary btn-block btn-sm">add</button> | |
41 | - </div> | |
42 | - </div> | |
43 | -<script type="text/javascript"> | |
44 | -$( "#form" ).sortable({ | |
45 | - delay: 100, | |
46 | - distance: 5, | |
47 | -}); | |
48 | -$("#add").click(function() { | |
49 | - $("#form").append('<div class="row form-group"><div class="col-md-1"></br><label><a href=""><span class="glyphicon glyphicon-move"></span></a></label></div><div class="col-md-10"><div class="form-group-md has-success is-empty"><input type="text" class="form-control" placeholder="Email address default size"><span class="help-block">Please enter a valid email address</span></div></div><div class="col-md-1"></br><label><a href=""><span class="glyphicon glyphicon-remove" onclick="this.parentNode.parentNode.parentNode.parentNode.removeChild(this.parentNode.parentNode.parentNode);"></span></a></label></div></br></div>'); | |
50 | -}); | |
51 | -</script> | |
52 | -{% endblock content %} |
... | ... | @@ -0,0 +1,99 @@ |
1 | +{% extends "topic/index.html" %} | |
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"> | |
9 | + | |
10 | + <!-- Modal Header --> | |
11 | + <div class="modal-header"> | |
12 | + | |
13 | + <!-- Put your title here!!! --> | |
14 | + <h4 class="modal-title" id="myModalLabel">{{form.question}}</h4> | |
15 | + | |
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">×</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> | |
96 | + </div> | |
97 | +</div> | |
98 | +<a href="" data-toggle="modal" data-target="#poll">modal</a> | |
99 | +{% endblock content %} | ... | ... |
poll/urls.py
... | ... | @@ -3,6 +3,7 @@ from django.conf.urls import url |
3 | 3 | from . import views |
4 | 4 | |
5 | 5 | urlpatterns = [ |
6 | - url(r'^to/poll/to/$', views.Poll.as_view(), name='poll'), | |
6 | + url(r'^create/(?P<slug>[\w\-_]+)/$', views.CreatePoll.as_view(), name='create_poll'), # topic slug | |
7 | + url(r'^update/(?P<slug>[\w\-_]+)/$', views.UpdatePoll.as_view(), name='update_poll'), # poll slug | |
7 | 8 | |
8 | 9 | ] | ... | ... |
poll/views.py
... | ... | @@ -8,31 +8,116 @@ from django.core.urlresolvers import reverse_lazy |
8 | 8 | from django.utils.translation import ugettext_lazy as _ |
9 | 9 | from rolepermissions.verifications import has_role |
10 | 10 | from rolepermissions.verifications import has_object_permission |
11 | +# from django.views.generic.edit import FormMixin | |
11 | 12 | |
12 | -# from .forms import CourseForm, UpdateCourseForm, CategoryForm, SubjectForm,TopicForm | |
13 | -# from .models import Course, Subject, Category,Topic, SubjectCategory | |
13 | +from .forms import PollForm | |
14 | +from .models import Poll, Answer | |
14 | 15 | from core.mixins import NotificationMixin |
15 | 16 | from users.models import User |
16 | -from courses.models import Course | |
17 | +from courses.models import Course, Topic | |
17 | 18 | |
18 | -class Poll(generic.TemplateView): | |
19 | +class CreatePoll(LoginRequiredMixin,generic.CreateView): | |
19 | 20 | |
20 | - # login_url = reverse_lazy("core:home") | |
21 | - # redirect_field_name = 'next' | |
22 | - # model = Course | |
23 | - # context_object_name = 'course' | |
24 | - template_name = 'poll/poll.html' | |
25 | - # queryset = Course.objects.all() | |
21 | + login_url = reverse_lazy("core:home") | |
22 | + redirect_field_name = 'next' | |
23 | + model = Poll | |
24 | + form_class = PollForm | |
25 | + context_object_name = 'poll' | |
26 | + template_name = 'poll/create_update.html' | |
27 | + success_url = reverse_lazy('core:home') | |
26 | 28 | |
27 | - # def get_queryset(self): | |
28 | - # return Course.objects.all()[0] | |
29 | + def form_invalid(self, form,**kwargs): | |
30 | + context = super(CreatePoll, 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 | + self.object = form.save(commit = False) | |
43 | + topic = get_object_or_404(Topic, slug = self.kwargs.get('slug')) | |
44 | + self.object.topic = topic | |
45 | + self.object.save() | |
46 | + | |
47 | + for key in self.request.POST: | |
48 | + if(key != 'csrfmiddlewaretoken' and key != 'name' and key != 'limit_date' and key != 'all_students' and key != 'students'): | |
49 | + answer = Answer(answer=self.request.POST[key],order=key,poll=self.object) | |
50 | + answer.save() | |
51 | + | |
52 | + return super(CreatePoll, self).form_valid(form) | |
53 | + | |
54 | + def get_context_data(self, **kwargs): | |
55 | + context = super(CreatePoll, self).get_context_data(**kwargs) | |
56 | + topic = get_object_or_404(Topic, slug = self.kwargs.get('slug')) | |
57 | + context['course'] = topic.subject.course | |
58 | + context['subject'] = topic.subject | |
59 | + context['subjects'] = topic.subject.course.subjects.all() | |
60 | + return context | |
61 | + | |
62 | +class UpdatePoll(LoginRequiredMixin,generic.UpdateView): | |
63 | + | |
64 | + login_url = reverse_lazy("core:home") | |
65 | + redirect_field_name = 'next' | |
66 | + model = Poll | |
67 | + form_class = PollForm | |
68 | + context_object_name = 'poll' | |
69 | + template_name = 'poll/create_update.html' | |
70 | + success_url = reverse_lazy('core:home') | |
71 | + | |
72 | + def dispatch(self, *args, **kwargs): | |
73 | + poll = get_object_or_404(Poll, slug = self.kwargs.get('slug')) | |
74 | + if(not has_object_permission('edit_poll', self.request.user, poll)): | |
75 | + return self.handle_no_permission() | |
76 | + return super(UpdatePoll, self).dispatch(*args, **kwargs) | |
77 | + | |
78 | + def get_object(self, queryset=None): | |
79 | + return get_object_or_404(Poll, slug = self.kwargs.get('slug')) | |
80 | + | |
81 | + def form_invalid(self, form,**kwargs): | |
82 | + context = super(UpdatePoll, self).form_invalid(form) | |
83 | + answers = {} | |
84 | + for key in self.request.POST: | |
85 | + if(key != 'csrfmiddlewaretoken' and key != 'name' and key != 'limit_date' and key != 'all_students' and key != 'students'): | |
86 | + answers[key] = self.request.POST[key] | |
87 | + | |
88 | + keys = sorted(answers) | |
89 | + context.context_data['answers'] = answers | |
90 | + context.context_data['keys'] = keys | |
91 | + return context | |
92 | + | |
93 | + def form_valid(self, form): | |
94 | + poll = self.object | |
95 | + poll = form.save(commit = False) | |
96 | + poll.answers.all().delete() | |
97 | + poll.save() | |
98 | + | |
99 | + | |
100 | + for key in self.request.POST: | |
101 | + if(key != 'csrfmiddlewaretoken' and key != 'name' and key != 'limit_date' and key != 'all_students' and key != 'students'): | |
102 | + answer = Answer(answer=self.request.POST[key],order=key,poll=poll) | |
103 | + answer.save() | |
104 | + | |
105 | + return super(UpdatePoll, self).form_valid(form) | |
29 | 106 | |
30 | 107 | def get_context_data(self, **kwargs): |
31 | - context = super(Poll, self).get_context_data(**kwargs) | |
32 | - course = Course.objects.all()[0] | |
33 | - context['course'] = course | |
34 | - context['subject'] = course.subjects.all()[0] | |
35 | - context['subjects'] = course.subjects.all() | |
36 | - # if (has_role(self.request.user,'system_admin')): | |
37 | - # context['subjects'] = self.object.course.subjects.all() | |
108 | + context = super(UpdatePoll, self).get_context_data(**kwargs) | |
109 | + poll = self.object | |
110 | + context['course'] = poll.topic.subject.course | |
111 | + context['subject'] = poll.topic.subject | |
112 | + context['subjects'] = poll.topic.subject.course.subjects.all() | |
113 | + | |
114 | + answers = {} | |
115 | + for answer in poll.answers.all(): | |
116 | + # print (key.answer) | |
117 | + answers[answer.order] = answer.answer | |
118 | + | |
119 | + keys = sorted(answers) | |
120 | + context['answers'] = answers | |
121 | + context['keys'] = keys | |
122 | + | |
38 | 123 | return context | ... | ... |
users/migrations/0001_initial.py
users/templates/list_users.html
... | ... | @@ -66,7 +66,7 @@ |
66 | 66 | <a href="javascript:void(0)" class="btn btn-primary btn-lg" data-toggle="modal" data-target="#DeleteModal">{% trans 'Delete' %}</a> |
67 | 67 | </div> |
68 | 68 | </div> |
69 | - | |
69 | + | |
70 | 70 | |
71 | 71 | <!-- Modal --> |
72 | 72 | <div class="modal fade" id="DeleteModal" tabindex="-1" role="dialog" aria-labelledby="DeleteModalLabel"> |
... | ... | @@ -77,11 +77,11 @@ |
77 | 77 | <h4 class="modal-title" id="DeleteModalLabel">Confirm delete</h4> |
78 | 78 | </div> |
79 | 79 | <div class="modal-body"> |
80 | - Are you sure you want to delete? | |
80 | + Are you sure you want to delete the user <b>{{acc.name}}</b>? | |
81 | 81 | </div> |
82 | 82 | <div class="modal-footer"> |
83 | - <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button> | |
84 | - <button type="button" class="btn btn-primary">Delete</button> | |
83 | + <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button> | |
84 | + <button type="button" class="btn btn-primary"> <a href="{% url 'users:delete' acc.username %}">Delete</a></button> | |
85 | 85 | </div> |
86 | 86 | </div> |
87 | 87 | </div> | ... | ... |
users/urls.py
... | ... | @@ -7,6 +7,7 @@ urlpatterns = [ |
7 | 7 | url(r'^create/$', views.Create.as_view(), name='create'), |
8 | 8 | url(r'^edit/(?P<username>[\w_-]+)/$', views.Update.as_view(), name='update'), |
9 | 9 | url(r'^view/(?P<username>[\w_-]+)/$', views.View.as_view(), name='view'), |
10 | + url(r'^delete/(?P<username>[\w_-]+)/$', views.delete, name='delete'), | |
10 | 11 | url(r'^profile/$', views.Profile.as_view(), name='profile'), |
11 | 12 | url(r'^profile/editar/(?P<username>[\w_-]+)/$', views.EditProfile.as_view(), name='edit_profile'), |
12 | 13 | # | ... | ... |
users/views.py
1 | -from django.shortcuts import get_object_or_404 | |
1 | +from django.shortcuts import get_object_or_404,redirect | |
2 | 2 | from django.db.models import Q |
3 | 3 | from django.views import generic |
4 | 4 | from django.contrib import messages |
... | ... | @@ -95,6 +95,14 @@ class View(LoginRequiredMixin, generic.DetailView): |
95 | 95 | slug_field = 'username' |
96 | 96 | slug_url_kwarg = 'username' |
97 | 97 | |
98 | +def delete(request,username): | |
99 | + user = get_object_or_404(User,username = username) | |
100 | + user.delete() | |
101 | + messages.success(request,_("User deleted Successfully!")) | |
102 | + return redirect('users:manage') | |
103 | + | |
104 | + | |
105 | + | |
98 | 106 | class UpdateUser(LoginRequiredMixin, generic.edit.UpdateView): |
99 | 107 | |
100 | 108 | allowed_roles = ['student'] | ... | ... |