Commit 58603d7cf7d4bcbf70b04d103cfda1930145b3ed
Exists in
master
and in
3 other branches
Merge branch 'refactoring' of https://github.com/amadeusproject/amadeuslms into refactoring
Showing
30 changed files
with
853 additions
and
65 deletions
Show diff stats
amadeus/settings.py
amadeus/static/css/.sass-cache/a7f87b90919294b62ab4f8079e31dcda8f485534/green.sassc
No preview for this file type
amadeus/static/css/base/amadeus.css
@@ -1227,4 +1227,70 @@ div.dataTables_wrapper div.dataTables_paginate { | @@ -1227,4 +1227,70 @@ div.dataTables_wrapper div.dataTables_paginate { | ||
1227 | display: inline; | 1227 | display: inline; |
1228 | margin-right: 30px; | 1228 | margin-right: 30px; |
1229 | } | 1229 | } |
1230 | -/* End Reports */ | ||
1231 | \ No newline at end of file | 1230 | \ No newline at end of file |
1231 | + | ||
1232 | +.delete-row{ | ||
1233 | + float: right; | ||
1234 | + background-color: gray; | ||
1235 | + color: white; | ||
1236 | + padding: 5px 20px; | ||
1237 | + font-size: 12px; | ||
1238 | + text-decoration: none; | ||
1239 | + border: none; | ||
1240 | + border-radius: 2px; | ||
1241 | + position: relative; | ||
1242 | + margin: 10px 20px; | ||
1243 | + font-weight: 700; | ||
1244 | + text-transform: uppercase; | ||
1245 | + letter-spacing: 0; | ||
1246 | +} | ||
1247 | +/* End Reports */ | ||
1248 | + | ||
1249 | +/* Chat */ | ||
1250 | +.participants-group { | ||
1251 | + margin-left: 40px; | ||
1252 | +} | ||
1253 | + | ||
1254 | +.participant { | ||
1255 | + margin-bottom: 10px; | ||
1256 | + border-width: 1px; | ||
1257 | + border-style: solid; | ||
1258 | + padding-right: 10px; | ||
1259 | + height: 70px; | ||
1260 | +} | ||
1261 | + | ||
1262 | +.participant .user-img { | ||
1263 | + height: 70px; | ||
1264 | + padding: 3px 0px 5px 0px; | ||
1265 | + text-align: center; | ||
1266 | +} | ||
1267 | + | ||
1268 | +.participant .user-img img { | ||
1269 | + height: 100%; | ||
1270 | + margin: 0 auto; | ||
1271 | +} | ||
1272 | + | ||
1273 | +.participant .user-info { | ||
1274 | + padding: 7px 15px; | ||
1275 | +} | ||
1276 | +.participant .user-info .status { | ||
1277 | + width: 20px; | ||
1278 | + height: 20px; | ||
1279 | + border-width: 1px; | ||
1280 | + border-style: solid; | ||
1281 | + border-radius: 10px; | ||
1282 | + margin-right: 10px; | ||
1283 | + position: relative; | ||
1284 | + top: 3px; | ||
1285 | + display: inline-block; | ||
1286 | + border-spacing: 0; | ||
1287 | + border-collapse: collapse; | ||
1288 | +} | ||
1289 | +.participant .user-info h4 { | ||
1290 | + font-weight: 700; | ||
1291 | + line-height: 2.0; | ||
1292 | +} | ||
1293 | + | ||
1294 | +.participant .buttons { | ||
1295 | + padding: 7px 0px; | ||
1296 | +} | ||
1297 | +/* End Chat */ | ||
1232 | \ No newline at end of file | 1298 | \ No newline at end of file |
amadeus/static/css/themes/green.css
@@ -471,6 +471,19 @@ a.add-row { | @@ -471,6 +471,19 @@ a.add-row { | ||
471 | .goal_divider { | 471 | .goal_divider { |
472 | border-top-color: #888; } | 472 | border-top-color: #888; } |
473 | 473 | ||
474 | +.participant { | ||
475 | + border-color: #888; } | ||
476 | + | ||
477 | +.participant .user-info h4 { | ||
478 | + color: #59b75c; } | ||
479 | + | ||
480 | +.participant .user-info .status { | ||
481 | + border-color: #888; | ||
482 | + background-color: #F5F5F5; } | ||
483 | + | ||
484 | +.participant .user-info .status.active { | ||
485 | + background-color: #59b75c; } | ||
486 | + | ||
474 | @media(max-width: 768px) { | 487 | @media(max-width: 768px) { |
475 | .navbar .navbar-nav .dropdown .dropdown-menu li > a { | 488 | .navbar .navbar-nav .dropdown .dropdown-menu li > a { |
476 | color: #333333 !important; } | 489 | color: #333333 !important; } |
amadeus/static/css/themes/green.css.map
1 | { | 1 | { |
2 | "version": 3, | 2 | "version": 3, |
3 | -"mappings": "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", | 3 | +"mappings": "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", |
4 | "sources": ["green.sass"], | 4 | "sources": ["green.sass"], |
5 | "names": [], | 5 | "names": [], |
6 | "file": "green.css" | 6 | "file": "green.css" |
amadeus/static/css/themes/green.sass
@@ -642,6 +642,19 @@ a.add-row | @@ -642,6 +642,19 @@ a.add-row | ||
642 | .goal_divider | 642 | .goal_divider |
643 | border-top-color: #888 | 643 | border-top-color: #888 |
644 | 644 | ||
645 | +.participant | ||
646 | + border-color: #888 | ||
647 | + | ||
648 | +.participant .user-info h4 | ||
649 | + color: #59b75c | ||
650 | + | ||
651 | +.participant .user-info .status | ||
652 | + border-color: #888 | ||
653 | + background-color: $default-white | ||
654 | + | ||
655 | +.participant .user-info .status.active | ||
656 | + background-color: #59b75c | ||
657 | + | ||
645 | @media(max-width: 768px) | 658 | @media(max-width: 768px) |
646 | .navbar .navbar-nav .dropdown .dropdown-menu li > a | 659 | .navbar .navbar-nav .dropdown .dropdown-menu li > a |
647 | color: #333333 !important | 660 | color: #333333 !important |
amadeus/templates/base.html
@@ -206,10 +206,12 @@ | @@ -206,10 +206,12 @@ | ||
206 | <span class="badge notify_badge mural_badge" {% if mural_notifications_count == 0 %} style="display:none" {% endif %}>{% if mural_notifications_count > 99 %} +99 {% else %} {{ mural_notifications_count }} {% endif %}</span> | 206 | <span class="badge notify_badge mural_badge" {% if mural_notifications_count == 0 %} style="display:none" {% endif %}>{% if mural_notifications_count > 99 %} +99 {% else %} {{ mural_notifications_count }} {% endif %}</span> |
207 | </a> | 207 | </a> |
208 | </li> | 208 | </li> |
209 | - <li class="item" data-toggle="tooltip" data-placement="right" title="{% trans "Messages" %}"> | ||
210 | - <i class="fa fa-envelope-o" aria-hidden="true"></i> | 209 | + <li class="item {{ chat_menu_active }} action_icon" data-toggle="tooltip" data-placement="right" title="{% trans "Messages" %}"> |
210 | + <a href="{% url 'chat:manage_general' %}"> | ||
211 | + <i class="fa fa-envelope-o" aria-hidden="true"></i> | ||
212 | + </a> | ||
211 | </li> | 213 | </li> |
212 | - <li class="item {{ pendencies_menu_active }} action_icon" data-toggle="tooltip" data-placement="right" title="{% trans "Pendencias" %}"> | 214 | + <li class="item {{ pendencies_menu_active }} action_icon" data-toggle="tooltip" data-placement="right" title="{% trans "Pendencies" %}"> |
213 | <a href="{% url 'notifications:manage' %}"> | 215 | <a href="{% url 'notifications:manage' %}"> |
214 | <i class="fa fa-exclamation-triangle" aria-hidden="true"></i> | 216 | <i class="fa fa-exclamation-triangle" aria-hidden="true"></i> |
215 | {% if notifications_count > 0 %} | 217 | {% if notifications_count > 0 %} |
@@ -253,15 +255,17 @@ | @@ -253,15 +255,17 @@ | ||
253 | </a> | 255 | </a> |
254 | </li> | 256 | </li> |
255 | <li class="item {{ mural_menu_active }} action_icon" data-toggle="tooltip" data-placement="top" title="{% trans "Mural" %}"> | 257 | <li class="item {{ mural_menu_active }} action_icon" data-toggle="tooltip" data-placement="top" title="{% trans "Mural" %}"> |
256 | - <a href="{% url 'mural:manage_general' %}" | 258 | + <a href="{% url 'mural:manage_general' %}"> |
257 | <i class="fa fa-list" aria-hidden="true" ></i> | 259 | <i class="fa fa-list" aria-hidden="true" ></i> |
258 | <span class="badge notify_badge mural_badge" {% if mural_notifications_count == 0 %} style="display:none" {% endif %}>{% if mural_notifications_count > 99 %} +99 {% else %} {{ mural_notifications_count }} {% endif %}</span> | 260 | <span class="badge notify_badge mural_badge" {% if mural_notifications_count == 0 %} style="display:none" {% endif %}>{% if mural_notifications_count > 99 %} +99 {% else %} {{ mural_notifications_count }} {% endif %}</span> |
259 | </a> | 261 | </a> |
260 | </li> | 262 | </li> |
261 | - <li class="item" data-toggle="tooltip" data-placement="top" title="{% trans "Messages" %}"> | ||
262 | - <i class="fa fa-envelope-o" aria-hidden="true"></i> | 263 | + <li class="item {{ chat_menu_active }} action_icon" data-toggle="tooltip" data-placement="top" title="{% trans "Messages" %}"> |
264 | + <a href="{% url 'chat:manage_general' %}"> | ||
265 | + <i class="fa fa-envelope-o" aria-hidden="true"></i> | ||
266 | + </a> | ||
263 | </li> | 267 | </li> |
264 | - <li class="item {{ pendencies_menu_active }} action_icon" data-toggle="tooltip" data-placement="top" title="{% trans "Pendencias" %}"> | 268 | + <li class="item {{ pendencies_menu_active }} action_icon" data-toggle="tooltip" data-placement="top" title="{% trans "Pendencies" %}"> |
265 | <a href="{% url 'notifications:manage' %}"> | 269 | <a href="{% url 'notifications:manage' %}"> |
266 | <i class="fa fa-exclamation-triangle" aria-hidden="true"></i> | 270 | <i class="fa fa-exclamation-triangle" aria-hidden="true"></i> |
267 | {% if notifications_count > 0 %} | 271 | {% if notifications_count > 0 %} |
amadeus/urls.py
@@ -30,6 +30,7 @@ urlpatterns = [ | @@ -30,6 +30,7 @@ urlpatterns = [ | ||
30 | url(r'^subjects/', include('subjects.urls', namespace = 'subjects')), | 30 | url(r'^subjects/', include('subjects.urls', namespace = 'subjects')), |
31 | url(r'^groups/', include('students_group.urls', namespace = 'groups')), | 31 | url(r'^groups/', include('students_group.urls', namespace = 'groups')), |
32 | url(r'^topics/', include('topics.urls', namespace = 'topics')), | 32 | url(r'^topics/', include('topics.urls', namespace = 'topics')), |
33 | + url(r'^chat/', include('chat.urls', namespace = 'chat')), | ||
33 | url(r'^mural/', include('mural.urls', namespace = 'mural')), | 34 | url(r'^mural/', include('mural.urls', namespace = 'mural')), |
34 | url(r'^webpages/', include('webpage.urls', namespace = 'webpages')), | 35 | url(r'^webpages/', include('webpage.urls', namespace = 'webpages')), |
35 | url(r'^ytvideo/', include('youtube_video.urls', namespace = 'youtube')), | 36 | url(r'^ytvideo/', include('youtube_video.urls', namespace = 'youtube')), |
@@ -0,0 +1,128 @@ | @@ -0,0 +1,128 @@ | ||
1 | +# -*- coding: utf-8 -*- | ||
2 | +# Generated by Django 1.10.4 on 2017-03-10 22:13 | ||
3 | +from __future__ import unicode_literals | ||
4 | + | ||
5 | +import chat.models | ||
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 | + ('subjects', '0014_auto_20170130_1828'), | ||
17 | + migrations.swappable_dependency(settings.AUTH_USER_MODEL), | ||
18 | + ('categories', '0014_auto_20170224_0023'), | ||
19 | + ] | ||
20 | + | ||
21 | + operations = [ | ||
22 | + migrations.CreateModel( | ||
23 | + name='ChatFavorites', | ||
24 | + fields=[ | ||
25 | + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), | ||
26 | + ], | ||
27 | + ), | ||
28 | + migrations.CreateModel( | ||
29 | + name='ChatVisualizations', | ||
30 | + fields=[ | ||
31 | + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), | ||
32 | + ('viewed', models.BooleanField(default=False, verbose_name='Viewed')), | ||
33 | + ('date_viewed', models.DateTimeField(blank=True, null=True, verbose_name='Date/Time Viewed')), | ||
34 | + ], | ||
35 | + ), | ||
36 | + migrations.CreateModel( | ||
37 | + name='Conversation', | ||
38 | + fields=[ | ||
39 | + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), | ||
40 | + ('_my_subclass', models.CharField(max_length=200)), | ||
41 | + ], | ||
42 | + options={ | ||
43 | + 'abstract': False, | ||
44 | + }, | ||
45 | + ), | ||
46 | + migrations.CreateModel( | ||
47 | + name='TalkMessages', | ||
48 | + fields=[ | ||
49 | + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), | ||
50 | + ('text', models.TextField(blank=True, verbose_name='Comment')), | ||
51 | + ('image', models.ImageField(blank=True, null=True, upload_to=chat.models.upload_filename, validators=[chat.models.validate_img_extension], verbose_name='Image')), | ||
52 | + ('create_date', models.DateTimeField(auto_now_add=True, verbose_name='Create Date')), | ||
53 | + ], | ||
54 | + ), | ||
55 | + migrations.CreateModel( | ||
56 | + name='CategoryTalk', | ||
57 | + fields=[ | ||
58 | + ('conversation_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='chat.Conversation')), | ||
59 | + ('space', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='talk_category', to='categories.Category', verbose_name='Category')), | ||
60 | + ], | ||
61 | + options={ | ||
62 | + 'abstract': False, | ||
63 | + }, | ||
64 | + bases=('chat.conversation',), | ||
65 | + ), | ||
66 | + migrations.CreateModel( | ||
67 | + name='GeneralTalk', | ||
68 | + fields=[ | ||
69 | + ('conversation_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='chat.Conversation')), | ||
70 | + ('space', models.IntegerField(blank=True, default=0, verbose_name='Space')), | ||
71 | + ], | ||
72 | + options={ | ||
73 | + 'abstract': False, | ||
74 | + }, | ||
75 | + bases=('chat.conversation',), | ||
76 | + ), | ||
77 | + migrations.CreateModel( | ||
78 | + name='SubjectTalk', | ||
79 | + fields=[ | ||
80 | + ('conversation_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='chat.Conversation')), | ||
81 | + ('space', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='talk_subject', to='subjects.Subject', verbose_name='Subject')), | ||
82 | + ], | ||
83 | + options={ | ||
84 | + 'abstract': False, | ||
85 | + }, | ||
86 | + bases=('chat.conversation',), | ||
87 | + ), | ||
88 | + migrations.AddField( | ||
89 | + model_name='talkmessages', | ||
90 | + name='talk', | ||
91 | + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='message_talk', to='chat.Conversation', verbose_name='Conversation'), | ||
92 | + ), | ||
93 | + migrations.AddField( | ||
94 | + model_name='talkmessages', | ||
95 | + name='user', | ||
96 | + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='message_user', to=settings.AUTH_USER_MODEL, verbose_name='User'), | ||
97 | + ), | ||
98 | + migrations.AddField( | ||
99 | + model_name='conversation', | ||
100 | + name='user_one', | ||
101 | + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='talk_user_start', to=settings.AUTH_USER_MODEL, verbose_name='User One'), | ||
102 | + ), | ||
103 | + migrations.AddField( | ||
104 | + model_name='conversation', | ||
105 | + name='user_two', | ||
106 | + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='talk_user_end', to=settings.AUTH_USER_MODEL, verbose_name='User Two'), | ||
107 | + ), | ||
108 | + migrations.AddField( | ||
109 | + model_name='chatvisualizations', | ||
110 | + name='message', | ||
111 | + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='chat_visualization_message', to='chat.TalkMessages', verbose_name='Message'), | ||
112 | + ), | ||
113 | + migrations.AddField( | ||
114 | + model_name='chatvisualizations', | ||
115 | + name='user', | ||
116 | + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='chat_visualization_user', to=settings.AUTH_USER_MODEL, verbose_name='User'), | ||
117 | + ), | ||
118 | + migrations.AddField( | ||
119 | + model_name='chatfavorites', | ||
120 | + name='message', | ||
121 | + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='chat_favorites_message', to='chat.TalkMessages', verbose_name='Message'), | ||
122 | + ), | ||
123 | + migrations.AddField( | ||
124 | + model_name='chatfavorites', | ||
125 | + name='user', | ||
126 | + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='chat_favorites_user', to=settings.AUTH_USER_MODEL, verbose_name='User'), | ||
127 | + ), | ||
128 | + ] |
@@ -0,0 +1,56 @@ | @@ -0,0 +1,56 @@ | ||
1 | +import os | ||
2 | +import time | ||
3 | +from django.db import models | ||
4 | +from django.core import validators | ||
5 | +from django.core.exceptions import ValidationError | ||
6 | +from django.utils.translation import ugettext_lazy as _ | ||
7 | + | ||
8 | +from topics.decorators import always_as_child | ||
9 | + | ||
10 | +from categories.models import Category | ||
11 | +from subjects.models import Subject | ||
12 | +from topics.models import KnowsChild | ||
13 | +from users.models import User | ||
14 | + | ||
15 | +def validate_img_extension(value): | ||
16 | + valid_formats = ['image/jpeg','image/x-citrix-jpeg','image/png','image/x-citrix-png','image/x-png','image/gif'] | ||
17 | + | ||
18 | + if hasattr(value.file, 'content_type'): | ||
19 | + if not value.file.content_type in valid_formats: | ||
20 | + raise ValidationError(_('File not supported.')) | ||
21 | + | ||
22 | +def upload_filename(instance, filename): | ||
23 | + path = "chat/" | ||
24 | + filename = str(int(time.time())) + "_" + filename | ||
25 | + | ||
26 | + return os.path.join(path, filename) | ||
27 | + | ||
28 | +class Conversation(KnowsChild): | ||
29 | + user_one = models.ForeignKey(User, verbose_name = _('User One'), related_name = 'talk_user_start') | ||
30 | + user_two = models.ForeignKey(User, verbose_name = _('User Two'), related_name = 'talk_user_end') | ||
31 | + | ||
32 | +class GeneralTalk(Conversation): | ||
33 | + space = models.IntegerField(_('Space'), default = 0, blank = True) | ||
34 | + | ||
35 | +class CategoryTalk(Conversation): | ||
36 | + space = models.ForeignKey(Category, verbose_name = ('Category'), related_name = 'talk_category', null = True) | ||
37 | + | ||
38 | +class SubjectTalk(Conversation): | ||
39 | + space = models.ForeignKey(Subject, verbose_name = _('Subject'), related_name = 'talk_subject') | ||
40 | + | ||
41 | +class TalkMessages(models.Model): | ||
42 | + text = models.TextField(_('Comment'), blank = True) | ||
43 | + image = models.ImageField(verbose_name = _('Image'), null=True, blank = True, upload_to = upload_filename, validators = [validate_img_extension]) | ||
44 | + talk = models.ForeignKey(Conversation, verbose_name = _('Conversation'), related_name = 'message_talk') | ||
45 | + user = models.ForeignKey(User, verbose_name = _('User'), related_name = 'message_user') | ||
46 | + create_date = models.DateTimeField(_('Create Date'), auto_now_add = True) | ||
47 | + | ||
48 | +class ChatVisualizations(models.Model): | ||
49 | + viewed = models.BooleanField(_('Viewed'), default = False) | ||
50 | + message = models.ForeignKey(TalkMessages, verbose_name = _('Message'), related_name = 'chat_visualization_message', null = True) | ||
51 | + user = models.ForeignKey(User, verbose_name = _('User'), related_name = "chat_visualization_user", null = True) | ||
52 | + date_viewed = models.DateTimeField(_('Date/Time Viewed'), null = True, blank = True) | ||
53 | + | ||
54 | +class ChatFavorites(models.Model): | ||
55 | + message = models.ForeignKey(TalkMessages, verbose_name = _('Message'), related_name = 'chat_favorites_message', null = True) | ||
56 | + user = models.ForeignKey(User, verbose_name = _('User'), related_name = "chat_favorites_user", null = True) | ||
0 | \ No newline at end of file | 57 | \ No newline at end of file |
@@ -0,0 +1,14 @@ | @@ -0,0 +1,14 @@ | ||
1 | +{% load i18n chat_tags %} | ||
2 | + | ||
3 | +<div class="col-md-12 participant panel"> | ||
4 | + <div class="col-md-1 user-img"> | ||
5 | + <img src="{{ participant.image_url }}" class="img-responsive" /> | ||
6 | + </div> | ||
7 | + <div class="col-md-6 user-info"> | ||
8 | + <h4><div class="status {{ participant|is_online:online_users }}"></div> {{ participant }}</h4> | ||
9 | + </div> | ||
10 | + <div class="col-md-4 buttons pull-right text-center"> | ||
11 | + <a href="" class="btn btn-raised btn-default">{% trans 'See Profile' %}</a> | ||
12 | + <a href="" class="btn btn-raised btn-success">{% trans 'Send Message' %}</a> | ||
13 | + </div> | ||
14 | +</div> |
@@ -0,0 +1,67 @@ | @@ -0,0 +1,67 @@ | ||
1 | +{% extends 'base.html' %} | ||
2 | + | ||
3 | +{% load static i18n pagination %} | ||
4 | +{% load django_bootstrap_breadcrumbs %} | ||
5 | + | ||
6 | +{% block breadcrumbs %} | ||
7 | + {{ block.super }} | ||
8 | + | ||
9 | + {% trans 'Messages General' as general %} | ||
10 | + | ||
11 | + {% breadcrumb general 'chat:manage_general' %} | ||
12 | +{% endblock %} | ||
13 | + | ||
14 | +{% block content %} | ||
15 | + <div id="core-subjects-options-div"> | ||
16 | + <ul class="core-subjects-options mural-tabs"> | ||
17 | + <a href="{% url 'chat:manage_general' %}"><li data-chat="general" class="active">{% trans "General" %} (<span>{{ totals.general }}</span>)</li></a> | ||
18 | + <a href=""><li data-chat="categories">{% trans "Per Category" %} (<span>{{ totals.category }}</span>)</li></a> | ||
19 | + <a href=""><li data-chat="subjects">{% trans "Per Subject" %} (<span>{{ totals.subject }}</span>)</li></a> | ||
20 | + </ul> | ||
21 | + </div> | ||
22 | + | ||
23 | + <div class="col-md-12 cards-content"> | ||
24 | + <div class="row"> | ||
25 | + <div class="col-md-12 col-sm-12 col-xs-12"> | ||
26 | + <div class="panel panel-default"> | ||
27 | + <div class="panel-body"> | ||
28 | + <div class="col-md-8"> | ||
29 | + <form action="" method="GET" class="form-horizontal"> | ||
30 | + <div class="form-group"> | ||
31 | + <div class="col-md-11 col-sm-11 col-xs-11"> | ||
32 | + <input type="text" class="form-control" name="search" placeholder="{% trans 'Search...' %}" /> | ||
33 | + </div> | ||
34 | + <div class="col-md-1 col-sm-1 col-xs-1"> | ||
35 | + <button type="submit" class="btn btn-fab btn-fab-mini"> | ||
36 | + <i class="fa fa-search"></i> | ||
37 | + </button> | ||
38 | + </div> | ||
39 | + </div> | ||
40 | + </form> | ||
41 | + </div> | ||
42 | + <div class="col-md-4"> | ||
43 | + <a href="{% url 'chat:participants_general' %}" class="pull-right btn btn-default btn-raised btn-md">{% trans 'List all participants' %}</a> | ||
44 | + </div> | ||
45 | + </div> | ||
46 | + </div> | ||
47 | + | ||
48 | + {% if conversations.count > 0 %} | ||
49 | + <div class="panel panel-info panel-body"> | ||
50 | + <h2 class="my-subjects-title"><b>{% trans 'Conversations' %}</b></h2> | ||
51 | + | ||
52 | + {% for chat in conversations %} | ||
53 | + {% include 'chat/_view.html' %} | ||
54 | + {% endfor %} | ||
55 | + </div> | ||
56 | + {% else %} | ||
57 | + <div class="text-center no-subjects"> | ||
58 | + <i class="fa fa-envelope-o"></i> | ||
59 | + <h4>{% trans 'You do not posses messages in this space yet.' %}</h4> | ||
60 | + </div> | ||
61 | + {% endif %} | ||
62 | + </div> | ||
63 | + </div> | ||
64 | + </div> | ||
65 | + | ||
66 | + <div class="modal fade" id="post-modal-form" tabindex="-1" role="dialog" aria-labelledby="myModalLabel"></div> | ||
67 | + {% endblock %} | ||
0 | \ No newline at end of file | 68 | \ No newline at end of file |
@@ -0,0 +1,75 @@ | @@ -0,0 +1,75 @@ | ||
1 | +{% extends 'base.html' %} | ||
2 | + | ||
3 | +{% load static i18n pagination chat_tags %} | ||
4 | +{% load django_bootstrap_breadcrumbs %} | ||
5 | + | ||
6 | +{% block breadcrumbs %} | ||
7 | + {{ block.super }} | ||
8 | + | ||
9 | + {% trans 'Messages General' as general %} | ||
10 | + {% trans 'Participants' as participants_bread %} | ||
11 | + | ||
12 | + {% breadcrumb general 'chat:manage_general' %} | ||
13 | + {% breadcrumb participants_bread 'chat:participants_general' %} | ||
14 | +{% endblock %} | ||
15 | + | ||
16 | +{% block content %} | ||
17 | + {% users_online as online_users %} | ||
18 | + | ||
19 | + <div id="core-subjects-options-div"> | ||
20 | + <ul class="core-subjects-options mural-tabs"> | ||
21 | + <a href="{% url 'chat:manage_general' %}"><li data-chat="general" class="active">{% trans "General" %} (<span>{{ totals.general }}</span>)</li></a> | ||
22 | + <a href=""><li data-chat="categories">{% trans "Per Category" %} (<span>{{ totals.category }}</span>)</li></a> | ||
23 | + <a href=""><li data-chat="subjects">{% trans "Per Subject" %} (<span>{{ totals.subject }}</span>)</li></a> | ||
24 | + </ul> | ||
25 | + </div> | ||
26 | + | ||
27 | + <div class="col-md-12 cards-content"> | ||
28 | + <div class="row"> | ||
29 | + <div class="col-md-12 col-sm-12 col-xs-12"> | ||
30 | + <div class="panel panel-default"> | ||
31 | + <div class="panel-body"> | ||
32 | + <div class="col-md-8"> | ||
33 | + <form action="" method="GET" class="form-horizontal"> | ||
34 | + <div class="form-group"> | ||
35 | + <div class="col-md-11 col-sm-11 col-xs-11"> | ||
36 | + <input type="text" class="form-control" name="search" placeholder="{% trans 'Search...' %}" /> | ||
37 | + </div> | ||
38 | + <div class="col-md-1 col-sm-1 col-xs-1"> | ||
39 | + <button type="submit" class="btn btn-fab btn-fab-mini"> | ||
40 | + <i class="fa fa-search"></i> | ||
41 | + </button> | ||
42 | + </div> | ||
43 | + </div> | ||
44 | + </form> | ||
45 | + </div> | ||
46 | + <div class="col-md-4"> | ||
47 | + <a href="{% url 'chat:participants_general' %}" class="pull-right btn btn-default btn-raised btn-md">{% trans 'List all participants' %}</a> | ||
48 | + </div> | ||
49 | + </div> | ||
50 | + </div> | ||
51 | + | ||
52 | + {% if participants.count > 0 %} | ||
53 | + <div class="panel category-panel-content panel-body"> | ||
54 | + <h2 class="my-subjects-title"><b>{% trans 'Participants' %}</b></h2> | ||
55 | + | ||
56 | + <div class="participants-group"> | ||
57 | + {% for participant in participants %} | ||
58 | + {% include 'chat/_view_participant.html' %} | ||
59 | + {% endfor %} | ||
60 | + </div> | ||
61 | + | ||
62 | + {% pagination request paginator page_obj %} | ||
63 | + </div> | ||
64 | + {% else %} | ||
65 | + <div class="text-center no-subjects"> | ||
66 | + <i class="fa fa-envelope-o"></i> | ||
67 | + <h4>{% trans 'There is no other participants in this space yet.' %}</h4> | ||
68 | + </div> | ||
69 | + {% endif %} | ||
70 | + </div> | ||
71 | + </div> | ||
72 | + </div> | ||
73 | + | ||
74 | + <div class="modal fade" id="post-modal-form" tabindex="-1" role="dialog" aria-labelledby="myModalLabel"></div> | ||
75 | + {% endblock %} | ||
0 | \ No newline at end of file | 76 | \ No newline at end of file |
@@ -0,0 +1,26 @@ | @@ -0,0 +1,26 @@ | ||
1 | +from django import template | ||
2 | +from django.utils import timezone | ||
3 | +from django.contrib.sessions.models import Session | ||
4 | + | ||
5 | +register = template.Library() | ||
6 | + | ||
7 | +@register.assignment_tag | ||
8 | +def users_online(): | ||
9 | + sessions = Session.objects.filter(expire_date__gte = timezone.now()) | ||
10 | + | ||
11 | + uid_list = [] | ||
12 | + | ||
13 | + # Build a list of user ids from that query | ||
14 | + for session in sessions: | ||
15 | + data = session.get_decoded() | ||
16 | + uid_list.append(data.get('_auth_user_id', None)) | ||
17 | + | ||
18 | + return uid_list | ||
19 | + | ||
20 | +@register.filter(name = 'is_online') | ||
21 | +def is_online(user, online_list): | ||
22 | + if str(user.id) in online_list: | ||
23 | + print(str(user.id) in online_list) | ||
24 | + return "active" | ||
25 | + | ||
26 | + return "" | ||
0 | \ No newline at end of file | 27 | \ No newline at end of file |
@@ -0,0 +1,7 @@ | @@ -0,0 +1,7 @@ | ||
1 | +from django.conf.urls import url | ||
2 | +from . import views | ||
3 | + | ||
4 | +urlpatterns = [ | ||
5 | + url(r'^$', views.GeneralIndex.as_view(), name='manage_general'), | ||
6 | + url(r'^participants/$', views.GeneralParticipants.as_view(), name='participants_general'), | ||
7 | +] | ||
0 | \ No newline at end of file | 8 | \ No newline at end of file |
@@ -0,0 +1,76 @@ | @@ -0,0 +1,76 @@ | ||
1 | +from django.shortcuts import get_object_or_404, redirect, render | ||
2 | +from django.core.paginator import Paginator, EmptyPage | ||
3 | +from django.http import Http404 | ||
4 | +from django.views import generic | ||
5 | +from django.contrib import messages | ||
6 | +from django.http import JsonResponse | ||
7 | +from django.template.loader import render_to_string | ||
8 | +from django.core.urlresolvers import reverse, reverse_lazy | ||
9 | +from django.utils.translation import ugettext_lazy as _ | ||
10 | +from django.contrib.auth.mixins import LoginRequiredMixin | ||
11 | +from django.db.models import Q | ||
12 | + | ||
13 | +from users.models import User | ||
14 | + | ||
15 | +from .models import Conversation, ChatVisualizations | ||
16 | + | ||
17 | +class GeneralIndex(LoginRequiredMixin, generic.ListView): | ||
18 | + login_url = reverse_lazy("users:login") | ||
19 | + redirect_field_name = 'next' | ||
20 | + | ||
21 | + template_name = 'chat/list.html' | ||
22 | + context_object_name = "conversations" | ||
23 | + paginate_by = 10 | ||
24 | + | ||
25 | + totals = {} | ||
26 | + | ||
27 | + def get_queryset(self): | ||
28 | + user = self.request.user | ||
29 | + page = self.request.GET.get('page', False) | ||
30 | + | ||
31 | + conversations = Conversation.objects.filter(Q(user_one = user) | Q(user_two = user)) | ||
32 | + | ||
33 | + self.totals['general'] = ChatVisualizations.objects.filter(user = user, viewed = False, message__talk__generaltalk__isnull = False).count() | ||
34 | + self.totals['category'] = ChatVisualizations.objects.filter(user = user, viewed = False, message__talk__categorytalk__isnull = False).count() | ||
35 | + self.totals['subject'] = ChatVisualizations.objects.filter(user = user, viewed = False, message__talk__subjecttalk__isnull = False).count() | ||
36 | + | ||
37 | + return conversations | ||
38 | + | ||
39 | + def get_context_data(self, **kwargs): | ||
40 | + context = super(GeneralIndex, self).get_context_data(**kwargs) | ||
41 | + | ||
42 | + context['title'] = _('Messages') | ||
43 | + context['totals'] = self.totals | ||
44 | + context['chat_menu_active'] = 'subjects_menu_active' | ||
45 | + | ||
46 | + return context | ||
47 | + | ||
48 | +class GeneralParticipants(LoginRequiredMixin, generic.ListView): | ||
49 | + login_url = reverse_lazy("users:login") | ||
50 | + redirect_field_name = 'next' | ||
51 | + | ||
52 | + template_name = 'chat/list_participants.html' | ||
53 | + context_object_name = "participants" | ||
54 | + paginate_by = 10 | ||
55 | + | ||
56 | + totals = {} | ||
57 | + | ||
58 | + def get_queryset(self): | ||
59 | + user = self.request.user | ||
60 | + | ||
61 | + users = User.objects.all().exclude(id = user.id) | ||
62 | + | ||
63 | + self.totals['general'] = ChatVisualizations.objects.filter(user = user, viewed = False, message__talk__generaltalk__isnull = False).count() | ||
64 | + self.totals['category'] = ChatVisualizations.objects.filter(user = user, viewed = False, message__talk__categorytalk__isnull = False).count() | ||
65 | + self.totals['subject'] = ChatVisualizations.objects.filter(user = user, viewed = False, message__talk__subjecttalk__isnull = False).count() | ||
66 | + | ||
67 | + return users | ||
68 | + | ||
69 | + def get_context_data(self, **kwargs): | ||
70 | + context = super(GeneralParticipants, self).get_context_data(**kwargs) | ||
71 | + | ||
72 | + context['title'] = _('Messages - Participants') | ||
73 | + context['totals'] = self.totals | ||
74 | + context['chat_menu_active'] = 'subjects_menu_active' | ||
75 | + | ||
76 | + return context | ||
0 | \ No newline at end of file | 77 | \ No newline at end of file |
reports/forms.py
@@ -2,8 +2,36 @@ from django import forms | @@ -2,8 +2,36 @@ from django import forms | ||
2 | from django.utils.translation import ugettext_lazy as _ | 2 | from django.utils.translation import ugettext_lazy as _ |
3 | import datetime | 3 | import datetime |
4 | 4 | ||
5 | +from django.forms.formsets import BaseFormSet | ||
5 | 6 | ||
6 | 7 | ||
8 | +class BaseResourceAndTagFormset(BaseFormSet): | ||
9 | + def clean(self): | ||
10 | + """ | ||
11 | + Adds validation to check that no two links have the same anchor or URL | ||
12 | + and that all links have both an anchor and URL. | ||
13 | + """ | ||
14 | + print("here 2") | ||
15 | + print(self.errors) | ||
16 | + if any(self.errors): | ||
17 | + return | ||
18 | + | ||
19 | + for form in self.forms: | ||
20 | + print(form) | ||
21 | + | ||
22 | +class ResourceAndTagForm(forms.Form): | ||
23 | + | ||
24 | + resource = forms.ChoiceField(label=_("Kind Of Resource"), required=True) | ||
25 | + tag = forms.ChoiceField(label=_('Tag'), required=True) | ||
26 | + | ||
27 | + def __init__(self, *args, **kwargs): | ||
28 | + super(ResourceAndTagForm, self).__init__(*args, **kwargs) | ||
29 | + if kwargs.get('initial'): | ||
30 | + initial = kwargs['initial'] | ||
31 | + self.fields['resource'].choices = [(classes.__name__, classes.__name__) for classes in initial['class_name']] | ||
32 | + self.fields['tag'].choices = [(tag.id, tag.name) for tag in initial['tag']] | ||
33 | + | ||
34 | + | ||
7 | class CreateInteractionReportForm(forms.Form): | 35 | class CreateInteractionReportForm(forms.Form): |
8 | topic = forms.ChoiceField( label= _("Topics to select data from")) | 36 | topic = forms.ChoiceField( label= _("Topics to select data from")) |
9 | init_date = forms.DateField() | 37 | init_date = forms.DateField() |
@@ -17,21 +45,29 @@ class CreateInteractionReportForm(forms.Form): | @@ -17,21 +45,29 @@ class CreateInteractionReportForm(forms.Form): | ||
17 | 45 | ||
18 | def __init__(self, *args, **kwargs): | 46 | def __init__(self, *args, **kwargs): |
19 | super(CreateInteractionReportForm, self).__init__(*args, **kwargs) | 47 | super(CreateInteractionReportForm, self).__init__(*args, **kwargs) |
20 | - | ||
21 | initial = kwargs['initial'] | 48 | initial = kwargs['initial'] |
22 | topics = list(initial['topic']) | 49 | topics = list(initial['topic']) |
23 | self.subject = initial['subject'] #so we can check date cleaned data | 50 | self.subject = initial['subject'] #so we can check date cleaned data |
24 | self.fields['topic'].choices = [(topic.id, topic.name) for topic in topics] | 51 | self.fields['topic'].choices = [(topic.id, topic.name) for topic in topics] |
25 | self.fields['topic'].choices.append((_("All"), _("All"))) | 52 | self.fields['topic'].choices.append((_("All"), _("All"))) |
26 | 53 | ||
54 | + | ||
55 | + def clean(self): | ||
56 | + cleaned_data = super(CreateInteractionReportForm, self).clean() | ||
57 | + init_date = cleaned_data.get("init_date") | ||
58 | + end_date = cleaned_data.get("end_date") | ||
59 | + if init_date and end_date: | ||
60 | + if init_date > end_date: | ||
61 | + raise forms.ValidationError(_("The initial date can't be after the end one.")) | ||
62 | + | ||
27 | def clean_init_date(self): | 63 | def clean_init_date(self): |
28 | - init_date = self.cleaned_data.get('init_date') | 64 | + init_date = self.cleaned_data['init_date'] |
29 | if init_date < self.subject.init_date: | 65 | if init_date < self.subject.init_date: |
30 | self._errors['init_date'] = [_('This date should be right or after ' + str(self.subject.init_date) + ', which is when the subject started. ')] | 66 | self._errors['init_date'] = [_('This date should be right or after ' + str(self.subject.init_date) + ', which is when the subject started. ')] |
31 | return init_date | 67 | return init_date |
32 | 68 | ||
33 | def clean_end_date(self): | 69 | def clean_end_date(self): |
34 | - end_date = self.cleaned_data.get('init_date') | ||
35 | - if end_date > self.subject.init_date: | ||
36 | - self._errors['end_date'] = [_('This date should be right or before ' + str(self.subject.init_date) + ', which is when the subject finishes. ')] | 70 | + end_date = self.cleaned_data['end_date'] |
71 | + if end_date > self.subject.end_date: | ||
72 | + self._errors['end_date'] = [_('This date should be right or before ' + str(self.subject.end_date) + ', which is when the subject finishes. ')] | ||
37 | return end_date | 73 | return end_date |
38 | \ No newline at end of file | 74 | \ No newline at end of file |
reports/templates/reports/_form.html
1 | {% load widget_tweaks static i18n %} | 1 | {% load widget_tweaks static i18n %} |
2 | 2 | ||
3 | <form action="" method="post">{% csrf_token %} | 3 | <form action="" method="post">{% csrf_token %} |
4 | - {% for field in form %} | ||
5 | - {% if field.auto_id == 'id_init_date' or field.auto_id == 'id_end_date' %} | ||
6 | - <label> {{field.label}} </label> | ||
7 | - {% render_field field class='form-control date-picker' %} | ||
8 | - | ||
9 | - {% else %} | ||
10 | - <label> {{field.label}} </label> | ||
11 | - {% render_field field class='form-control' %} | ||
12 | - {% endif %} | ||
13 | - | ||
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> | 4 | + <p>{% trans "General Parameters" %}</p><hr> |
5 | + {% if form.errors %} | ||
6 | + <div class="alert alert-danger alert-dismissible" role="alert"> | ||
7 | + <button type="button" class="close" data-dismiss="alert" aria-label="Close"> | ||
8 | + <span aria-hidden="true">×</span> | ||
9 | + </button> | ||
10 | + <ul> | ||
11 | + {% for key, message in form.errors.items %} | ||
12 | + <li>{{message}}</li> | ||
13 | + {% endfor %} | ||
14 | + </ul> | ||
27 | </div> | 15 | </div> |
28 | {% endif %} | 16 | {% endif %} |
17 | + {% for field in form %} | ||
18 | + | ||
19 | + | ||
20 | + {% if field.auto_id == 'id_init_date' or field.auto_id == 'id_end_date' %} | ||
21 | + <label> {{field.label}} </label> | ||
22 | + {% render_field field class='form-control date-picker' %} | ||
23 | + | ||
24 | + | ||
25 | + {% elif field.auto_id == 'id_from_mural' %} | ||
26 | + <p>{% trans "Data Source" %}</p><hr> | ||
27 | + <label> {{field.label}} </label> | ||
28 | + {% render_field field class='form-control' %} | ||
29 | + {% else %} | ||
30 | + <label> {{field.label}} </label> | ||
31 | + {% render_field field class='form-control' %} | ||
32 | + {% endif %} | ||
33 | + | ||
34 | + {% if field.errors %} | ||
35 | + <div class="row"> | ||
36 | + </br> | ||
37 | + <div class="alert alert-danger alert-dismissible" role="alert"> | ||
38 | + <button type="button" class="close" data-dismiss="alert" aria-label="Close"> | ||
39 | + <span aria-hidden="true">×</span> | ||
40 | + </button> | ||
41 | + <ul> | ||
42 | + {% for error in field.errors %} | ||
43 | + <li>{{ error }}</li> | ||
44 | + {% endfor %} | ||
45 | + </ul> | ||
46 | + </div> | ||
47 | + </div> | ||
48 | + {% endif %} | ||
29 | {% endfor %} | 49 | {% endfor %} |
50 | + | ||
51 | + <!---Adding the selector --> | ||
52 | + <div class="panel-group" id="resources_accordion" role="tablist" aria-multiselectable="true"> | ||
53 | + <div class="panel panel-info"> | ||
54 | + <div class="panel-heading"> | ||
55 | + <div class="row"> | ||
56 | + <div class="col-md-12"> | ||
57 | + <a data-parent="#resources_accordion" data-toggle="collapse" href="#resources"> | ||
58 | + <h4 class="panel-title"> | ||
59 | + <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="resources">{% trans "Interaction with resources" %}</label> | ||
60 | + </h4> | ||
61 | + </a> | ||
62 | + </div> | ||
63 | + </div> | ||
64 | + </div> | ||
65 | + | ||
66 | + <div id="resources" class="panel-collapse collapse"> | ||
67 | + {{ resource_tag_formset.management_form }} | ||
68 | + | ||
69 | + | ||
70 | + {% for resource_tag_form in resource_tag_formset %} | ||
71 | + | ||
72 | + {% if resource_tag_form.anchor.errors %} | ||
73 | + {% for error in resource_tag_form.anchor.errors %} | ||
74 | + {{ error|escape }} | ||
75 | + {% endfor %} | ||
76 | + {% endif %} | ||
77 | + <div class="resource-tag-formset"> | ||
78 | + {% for field in resource_tag_form %} | ||
79 | + <label>{{field.label}}</label> | ||
80 | + {% render_field field class="form-control" %} | ||
81 | + | ||
82 | + | ||
83 | + {% endfor %} | ||
84 | + </div> | ||
85 | + {% endfor %} | ||
86 | + </div> | ||
87 | + </div> | ||
88 | + </div> | ||
89 | + | ||
30 | <div class="row text-center"> | 90 | <div class="row text-center"> |
31 | <input type="submit" value="Search" class="btn btn-success btn-raised" /> | 91 | <input type="submit" value="Search" class="btn btn-success btn-raised" /> |
32 | </div> | 92 | </div> |
33 | </form> | 93 | </form> |
94 | + | ||
95 | + |
reports/templates/reports/create.html
@@ -11,6 +11,12 @@ | @@ -11,6 +11,12 @@ | ||
11 | {% breadcrumb 'analytics' '' %} | 11 | {% breadcrumb 'analytics' '' %} |
12 | {% endblock %} | 12 | {% endblock %} |
13 | 13 | ||
14 | +{% block javascript %} | ||
15 | + {{block.super}} | ||
16 | + <script type="text/javascript" src="{% static "js/jquery.formset.js" %} "></script> | ||
17 | + | ||
18 | +{% endblock javascript %} | ||
19 | + | ||
14 | {% block content %} | 20 | {% block content %} |
15 | 21 | ||
16 | <div class="panel panel-info topic-panel"> | 22 | <div class="panel panel-info topic-panel"> |
@@ -41,5 +47,44 @@ | @@ -41,5 +47,44 @@ | ||
41 | 47 | ||
42 | {% include "reports/_form.html" %} | 48 | {% include "reports/_form.html" %} |
43 | 49 | ||
44 | - | 50 | + <script type="text/javascript" src="{% static "reports/js/report.js" %}"></script> |
51 | + <script> | ||
52 | + | ||
53 | + | ||
54 | + $('.resource-tag-formset').formset({ | ||
55 | + addText: 'add data source', | ||
56 | + deleteText: 'remove data source', | ||
57 | + added: function(object){ | ||
58 | + $.get("{% url 'subjects:reports:get_resource_and_tags' %}?subject_id={{subject.id}}", function(data){ | ||
59 | + fields = object.children("select"); | ||
60 | + | ||
61 | + for(var j = 0; j < data.resources.length; j++){ | ||
62 | + fields[0].options[fields[0].options.length] = new Option(data.resources[j].name,data.resources[j].id); | ||
63 | + | ||
64 | + } | ||
65 | + //Set initial tag options | ||
66 | + var form_topic = $("select#id_topic :selected").filter(":selected").val(); //get user selected topic option | ||
67 | + $.get("{% url 'subjects:reports:get_tags' %}?resource_class_name="+fields[0].value+"&subject_id={{subject.id}}"+"&topic_choice="+form_topic, function(data){ | ||
68 | + fields[1].options.length = 0; | ||
69 | + for(var j = 0; j < data.tags.length; j++){ | ||
70 | + fields[1].options[fields[1].options.length] = new Option(data.tags[j].name,data.tags[j].id); | ||
71 | + } | ||
72 | + }); | ||
73 | + | ||
74 | + //Modify tags fields - on resource type value modifies | ||
75 | + fields[0].onchange = function(item){ | ||
76 | + //it request all tags associated with that resource | ||
77 | + var form_topic = $("select#id_topic :selected").filter(":selected").val(); //get user selected topic option | ||
78 | + | ||
79 | + $.get("{% url 'subjects:reports:get_tags' %}?resource_class_name="+item.target.value+"&"+"subject_id={{subject.id}}"+"&topic_choice="+form_topic, function(data){ | ||
80 | + fields[1].options.length = 0; | ||
81 | + for(var j = 0; j < data.tags.length; j++){ | ||
82 | + fields[1].options[fields[1].options.length] = new Option(data.tags[j].name,data.tags[j].id); | ||
83 | + } | ||
84 | + }); | ||
85 | + }; | ||
86 | + }); | ||
87 | + }, | ||
88 | + }); | ||
89 | + </script> | ||
45 | {% endblock content %} | 90 | {% endblock content %} |
46 | \ No newline at end of file | 91 | \ No newline at end of file |
reports/templates/reports/view.html
@@ -6,9 +6,9 @@ | @@ -6,9 +6,9 @@ | ||
6 | 6 | ||
7 | {% block breadcrumbs %} | 7 | {% block breadcrumbs %} |
8 | {{ block.super }} | 8 | {{ block.super }} |
9 | - {% breadcrumb view.subject.category 'subjects:cat_view' view.subject.category.slug %} | ||
10 | - {% breadcrumb view.subject 'subjects:view' view.subject.slug %} | ||
11 | - {% breadcrumb 'analytics' '' %} | 9 | + {% breadcrumb subject.category 'subjects:cat_view' subject.category.slug %} |
10 | + {% breadcrumb subject 'subjects:view' subject.slug %} | ||
11 | + {% breadcrumb 'Analytics' '' %} | ||
12 | {% endblock %} | 12 | {% endblock %} |
13 | 13 | ||
14 | {% block content %} | 14 | {% block content %} |
@@ -65,10 +65,6 @@ | @@ -65,10 +65,6 @@ | ||
65 | <ul id="report-info"> | 65 | <ul id="report-info"> |
66 | <li> {{data.values|length}} {% trans "register(s)" %} </li> | 66 | <li> {{data.values|length}} {% trans "register(s)" %} </li> |
67 | <li> | 67 | <li> |
68 | - <i class="fa fa-download" aria-hidden="true"></i> {% trans "Variable Descriptions" %} | ||
69 | - | ||
70 | - </li> | ||
71 | - <li> | ||
72 | <i class="fa fa-download" aria-hidden="true"></i> {% trans "Interactions Data" %} | 68 | <i class="fa fa-download" aria-hidden="true"></i> {% trans "Interactions Data" %} |
73 | </li> | 69 | </li> |
74 | </ul> | 70 | </ul> |
reports/urls.py
@@ -5,4 +5,6 @@ from . import views | @@ -5,4 +5,6 @@ from . import views | ||
5 | urlpatterns = [ | 5 | urlpatterns = [ |
6 | url(r'^create/interactions/$', views.ReportView.as_view(), name='create_interaction'), | 6 | url(r'^create/interactions/$', views.ReportView.as_view(), name='create_interaction'), |
7 | url(r'^view/interactions/$', views.ViewReportView.as_view(), name='view_report'), | 7 | url(r'^view/interactions/$', views.ViewReportView.as_view(), name='view_report'), |
8 | + url(r'^get/resources/$', views.get_resources, name='get_resource_and_tags'), | ||
9 | + url(r'^get/tags/$', views.get_tags, name='get_tags'), | ||
8 | ] | 10 | ] |
9 | \ No newline at end of file | 11 | \ No newline at end of file |
reports/views.py
@@ -13,9 +13,11 @@ from django.db.models import Q | @@ -13,9 +13,11 @@ from django.db.models import Q | ||
13 | from django.contrib.auth.mixins import LoginRequiredMixin | 13 | from django.contrib.auth.mixins import LoginRequiredMixin |
14 | from datetime import datetime, date | 14 | from datetime import datetime, date |
15 | from subjects.models import Subject | 15 | from subjects.models import Subject |
16 | -from .forms import CreateInteractionReportForm | 16 | +from .forms import CreateInteractionReportForm, ResourceAndTagForm, BaseResourceAndTagFormset |
17 | from log.models import Log | 17 | from log.models import Log |
18 | +from topics.models import Resource, Topic | ||
18 | 19 | ||
20 | +from django.forms import formset_factory | ||
19 | 21 | ||
20 | class ReportView(LoginRequiredMixin, generic.FormView): | 22 | class ReportView(LoginRequiredMixin, generic.FormView): |
21 | template_name = "reports/create.html" | 23 | template_name = "reports/create.html" |
@@ -40,7 +42,24 @@ class ReportView(LoginRequiredMixin, generic.FormView): | @@ -40,7 +42,24 @@ class ReportView(LoginRequiredMixin, generic.FormView): | ||
40 | subject = Subject.objects.get(id=self.request.GET['subject_id']) | 42 | subject = Subject.objects.get(id=self.request.GET['subject_id']) |
41 | 43 | ||
42 | context['subject'] = subject | 44 | context['subject'] = subject |
43 | - | 45 | + |
46 | + topics = subject.topic_subject.all() | ||
47 | + #get all resources associated with topics | ||
48 | + tags = [] | ||
49 | + for topic in topics: | ||
50 | + resources_set = topic.resource_topic.all() | ||
51 | + for resource in resources_set: | ||
52 | + for tag in resource.tags.all(): | ||
53 | + tags.append(tag) | ||
54 | + | ||
55 | + | ||
56 | + classes = Resource.__subclasses__() | ||
57 | + | ||
58 | + | ||
59 | + #set formset | ||
60 | + resourceTagFormSet = formset_factory(ResourceAndTagForm, formset=BaseResourceAndTagFormset) | ||
61 | + resourceTagFormSet = resourceTagFormSet(initial=[{'class_name': classes, 'tag':tags}]) | ||
62 | + context['resource_tag_formset'] = resourceTagFormSet | ||
44 | return context | 63 | return context |
45 | 64 | ||
46 | def get_success_url(self): | 65 | def get_success_url(self): |
@@ -53,6 +72,10 @@ class ReportView(LoginRequiredMixin, generic.FormView): | @@ -53,6 +72,10 @@ class ReportView(LoginRequiredMixin, generic.FormView): | ||
53 | get_params += key + "=" + str(value) + "&" | 72 | get_params += key + "=" + str(value) + "&" |
54 | 73 | ||
55 | 74 | ||
75 | + for form_data in self.formset_data: | ||
76 | + for key, value in form_data.items(): | ||
77 | + get_params += key + "=" + str(value) + "&" | ||
78 | + | ||
56 | #retrieving subject id for data purposes | 79 | #retrieving subject id for data purposes |
57 | for key, value in self.request.GET.items(): | 80 | for key, value in self.request.GET.items(): |
58 | get_params += key + "=" + str(value) | 81 | get_params += key + "=" + str(value) |
@@ -65,9 +88,30 @@ class ReportView(LoginRequiredMixin, generic.FormView): | @@ -65,9 +88,30 @@ class ReportView(LoginRequiredMixin, generic.FormView): | ||
65 | POST variables and then checked for validity. | 88 | POST variables and then checked for validity. |
66 | """ | 89 | """ |
67 | form = self.get_form() | 90 | form = self.get_form() |
68 | - if form.is_valid(): | ||
69 | 91 | ||
92 | + subject = Subject.objects.get(id=self.request.GET['subject_id']) | ||
93 | + | ||
94 | + topics = subject.topic_subject.all() | ||
95 | + #get all resources associated with topics | ||
96 | + tags = [] | ||
97 | + for topic in topics: | ||
98 | + resources_set = topic.resource_topic.all() | ||
99 | + for resource in resources_set: | ||
100 | + for tag in resource.tags.all(): | ||
101 | + tags.append(tag) | ||
102 | + | ||
103 | + classes = Resource.__subclasses__() | ||
104 | + amount_of_forms = self.request.POST['form-TOTAL_FORMS'] | ||
105 | + initial_datum = {'class_name': classes , 'tag': tags} | ||
106 | + initial_data = [] | ||
107 | + for i in range(int(amount_of_forms)): | ||
108 | + initial_data.append(initial_datum) | ||
109 | + | ||
110 | + resourceTagFormSet = formset_factory(ResourceAndTagForm, formset=BaseResourceAndTagFormset) | ||
111 | + resources_formset = resourceTagFormSet(self.request.POST, initial = initial_data) | ||
112 | + if form.is_valid() and resources_formset.is_valid(): | ||
70 | self.form_data = form.cleaned_data | 113 | self.form_data = form.cleaned_data |
114 | + self.formset_data = resources_formset.cleaned_data | ||
71 | return self.form_valid(form) | 115 | return self.form_valid(form) |
72 | else: | 116 | else: |
73 | return self.form_invalid(form) | 117 | return self.form_invalid(form) |
@@ -86,6 +130,8 @@ class ViewReportView(LoginRequiredMixin, generic.TemplateView): | @@ -86,6 +130,8 @@ class ViewReportView(LoginRequiredMixin, generic.TemplateView): | ||
86 | context['init_date'] = params_data['init_date'] | 130 | context['init_date'] = params_data['init_date'] |
87 | context['end_date'] = params_data['end_date'] | 131 | context['end_date'] = params_data['end_date'] |
88 | context['subject'] = subject | 132 | context['subject'] = subject |
133 | + print(params_data) | ||
134 | + | ||
89 | if params_data['from_mural']: | 135 | if params_data['from_mural']: |
90 | context['data'], context['header'] = self.get_mural_data(subject, params_data['init_date'], params_data['end_date']) | 136 | context['data'], context['header'] = self.get_mural_data(subject, params_data['init_date'], params_data['end_date']) |
91 | return context | 137 | return context |
@@ -115,22 +161,22 @@ class ViewReportView(LoginRequiredMixin, generic.TemplateView): | @@ -115,22 +161,22 @@ class ViewReportView(LoginRequiredMixin, generic.TemplateView): | ||
115 | create_date__range=(init_date, end_date)) | 161 | create_date__range=(init_date, end_date)) |
116 | 162 | ||
117 | #number of help posts created by the student | 163 | #number of help posts created by the student |
118 | - interactions['v01'] = help_posts_made_by_user.count() | 164 | + interactions['number of help posts created by the user'] = help_posts_made_by_user.count() |
119 | 165 | ||
120 | help_posts = SubjectPost.objects.filter(action="help", create_date__range=(init_date, end_date), | 166 | help_posts = SubjectPost.objects.filter(action="help", create_date__range=(init_date, end_date), |
121 | space__id=subject.id) | 167 | space__id=subject.id) |
122 | 168 | ||
123 | #comments count on help posts created by the student | 169 | #comments count on help posts created by the student |
124 | - interactions['v02'] = Comment.objects.filter(post__in = help_posts.filter(user=student), | 170 | + interactions['amount of comments on help posts created by the student'] = Comment.objects.filter(post__in = help_posts.filter(user=student), |
125 | create_date__range=(init_date, end_date)).count() | 171 | create_date__range=(init_date, end_date)).count() |
126 | 172 | ||
127 | 173 | ||
128 | #count the amount of comments made by the student on posts made by one of the professors | 174 | #count the amount of comments made by the student on posts made by one of the professors |
129 | - interactions['v03'] = Comment.objects.filter(post__in = help_posts.filter(user__in= subject.professor.all()), create_date__range=(init_date, end_date), | 175 | + interactions['amount of comments made by the student on teachers help posts'] = Comment.objects.filter(post__in = help_posts.filter(user__in= subject.professor.all()), create_date__range=(init_date, end_date), |
130 | user=student).count() | 176 | user=student).count() |
131 | 177 | ||
132 | #comments made by the user on other users posts | 178 | #comments made by the user on other users posts |
133 | - interactions['v04'] = Comment.objects.filter(post__in = help_posts.exclude(user=student), | 179 | + interactions['amount of comments made by the student on other students help posts'] = Comment.objects.filter(post__in = help_posts.exclude(user=student), |
134 | create_date__range=(init_date, end_date), | 180 | create_date__range=(init_date, end_date), |
135 | user= student).count() | 181 | user= student).count() |
136 | 182 | ||
@@ -141,7 +187,7 @@ class ViewReportView(LoginRequiredMixin, generic.TemplateView): | @@ -141,7 +187,7 @@ class ViewReportView(LoginRequiredMixin, generic.TemplateView): | ||
141 | for comment in comments_by_teacher: | 187 | for comment in comments_by_teacher: |
142 | help_posts_ids.append(comment.post.id) | 188 | help_posts_ids.append(comment.post.id) |
143 | #number of help posts created by the user that the teacher commented on | 189 | #number of help posts created by the user that the teacher commented on |
144 | - interactions['v05'] = help_posts.filter(user=student, id__in = help_posts_ids).count() | 190 | + interactions['Number of help posts created by the user that the teacher commented on'] = help_posts.filter(user=student, id__in = help_posts_ids).count() |
145 | 191 | ||
146 | 192 | ||
147 | comments_by_others = Comment.objects.filter(user__in=subject.students.exclude(id = student.id)) | 193 | comments_by_others = Comment.objects.filter(user__in=subject.students.exclude(id = student.id)) |
@@ -149,39 +195,78 @@ class ViewReportView(LoginRequiredMixin, generic.TemplateView): | @@ -149,39 +195,78 @@ class ViewReportView(LoginRequiredMixin, generic.TemplateView): | ||
149 | for comment in comments_by_teacher: | 195 | for comment in comments_by_teacher: |
150 | help_posts_ids.append(comment.post.id) | 196 | help_posts_ids.append(comment.post.id) |
151 | #number of help posts created by the user others students commented on | 197 | #number of help posts created by the user others students commented on |
152 | - interactions['v06'] = help_posts.filter(user=student, id__in = help_posts_ids).count() | 198 | + interactions['number of help posts created by the user others students commented on'] = help_posts.filter(user=student, id__in = help_posts_ids).count() |
153 | 199 | ||
154 | #Number of student visualizations on the mural of the subject | 200 | #Number of student visualizations on the mural of the subject |
155 | - interactions['v07'] = MuralVisualizations.objects.filter(post__in = SubjectPost.objects.filter(space__id=subject.id), | 201 | + interactions['Number of student visualizations on the mural of the subject'] = MuralVisualizations.objects.filter(post__in = SubjectPost.objects.filter(space__id=subject.id), |
156 | user = student).count() | 202 | user = student).count() |
157 | 203 | ||
158 | 204 | ||
205 | + #VAR08 - | ||
206 | + | ||
159 | #VAR20 - number of access to mural between 6 a.m to 12a.m. | 207 | #VAR20 - number of access to mural between 6 a.m to 12a.m. |
160 | - interactions['v20'] = Log.objects.filter(action="access", resource="subject", | 208 | + interactions[' number of access to mural between 6 a.m to 12a.m.'] = Log.objects.filter(action="access", resource="subject", |
161 | user_id= student.id, context__contains = {'subject_id' : subject.id}, datetime__hour__range = (5, 11)).count() | 209 | user_id= student.id, context__contains = {'subject_id' : subject.id}, datetime__hour__range = (5, 11)).count() |
162 | 210 | ||
163 | - #VAR21 - number of access to mural between 6 a.m to 12a.m. | ||
164 | - interactions['v21'] = Log.objects.filter(action="access", resource="subject", | 211 | + #VAR21 - number of access to mural between 0 p.m to 6p.m. |
212 | + interactions['number of access to mural between 0 p.m to 6p.m.'] = Log.objects.filter(action="access", resource="subject", | ||
165 | user_id= student.id, context__contains = {'subject_id' : subject.id}, datetime__hour__range = (11, 17)).count() | 213 | user_id= student.id, context__contains = {'subject_id' : subject.id}, datetime__hour__range = (11, 17)).count() |
166 | #VAR22 | 214 | #VAR22 |
167 | - interactions['v22'] = Log.objects.filter(action="access", resource="subject", | 215 | + interactions['number of access to mural between 6 p.m to 12p.m.'] = Log.objects.filter(action="access", resource="subject", |
168 | user_id= student.id, context__contains = {'subject_id' : subject.id}, datetime__hour__range = (17, 23)).count() | 216 | user_id= student.id, context__contains = {'subject_id' : subject.id}, datetime__hour__range = (17, 23)).count() |
169 | 217 | ||
170 | #VAR23 | 218 | #VAR23 |
171 | - interactions['v23'] = Log.objects.filter(action="access", resource="subject", | 219 | + interactions['number of access to mural between 0 a.m to 6a.m.'] = Log.objects.filter(action="access", resource="subject", |
172 | user_id= student.id, context__contains = {'subject_id' : subject.id}, datetime__hour__range = (23, 5)).count() | 220 | user_id= student.id, context__contains = {'subject_id' : subject.id}, datetime__hour__range = (23, 5)).count() |
173 | 221 | ||
174 | #VAR24 through 30 | 222 | #VAR24 through 30 |
175 | day_numbers = [0, 1, 2, 3, 4, 5, 6] | 223 | day_numbers = [0, 1, 2, 3, 4, 5, 6] |
176 | day_names = ["sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"] | 224 | day_names = ["sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"] |
177 | for day_num in day_numbers: | 225 | for day_num in day_numbers: |
178 | - interactions['v'+ str(24+day_num)] = Log.objects.filter(action="access", resource="subject", | 226 | + interactions['number of access to the subject on '+ day_names[day_num]] = Log.objects.filter(action="access", resource="subject", |
179 | user_id= student.id, context__contains = {'subject_id' : subject.id}, datetime__week_day = day_num).count() | 227 | user_id= student.id, context__contains = {'subject_id' : subject.id}, datetime__week_day = day_num).count() |
180 | 228 | ||
181 | for value in interactions.values(): | 229 | for value in interactions.values(): |
182 | data[student].append(value) | 230 | data[student].append(value) |
183 | - if len(header) <= 1: | ||
184 | - for key in interactions.keys(): | ||
185 | - header.append(key) | 231 | + |
232 | + | ||
233 | + for key in interactions.keys(): | ||
234 | + header.append(key) | ||
186 | return data, header | 235 | return data, header |
187 | 236 | ||
237 | + | ||
238 | + | ||
239 | +def get_resources(request): | ||
240 | + | ||
241 | + #get all possible resources | ||
242 | + classes = Resource.__subclasses__() | ||
243 | + | ||
244 | + data = {} | ||
245 | + | ||
246 | + | ||
247 | + data['resources']= [ {'id':class_name.__name__, 'name':class_name.__name__} for class_name in classes] | ||
248 | + return JsonResponse(data) | ||
249 | + | ||
250 | + | ||
251 | +def get_tags(request): | ||
252 | + resource_type = request.GET['resource_class_name'] | ||
253 | + subject = Subject.objects.get(id=request.GET['subject_id']) | ||
254 | + topic_choice = request.GET["topic_choice"] | ||
255 | + if topic_choice.lower() == "all": | ||
256 | + topics = subject.topic_subject.all() | ||
257 | + else: | ||
258 | + topics = [Topic.objects.get(id=int(topic_choice))] | ||
259 | + data = {} | ||
260 | + tags = [] | ||
261 | + for topic in topics: | ||
262 | + resource_set = Resource.objects.select_related(resource_type.lower()).filter(topic = topic) | ||
263 | + | ||
264 | + for resource in resource_set: | ||
265 | + if resource._my_subclass == resource_type.lower(): | ||
266 | + for tag in resource.tags.all(): | ||
267 | + tags.append(tag) | ||
268 | + | ||
269 | + | ||
270 | + | ||
271 | + data['tags'] = [ {'id':tag.id, 'name':tag.name} for tag in tags] | ||
272 | + return JsonResponse(data) |
topics/views.py
@@ -57,7 +57,6 @@ class CreateView(LoginRequiredMixin, LogMixin, generic.edit.CreateView): | @@ -57,7 +57,6 @@ class CreateView(LoginRequiredMixin, LogMixin, generic.edit.CreateView): | ||
57 | subject = get_object_or_404(Subject, slug = slug) | 57 | subject = get_object_or_404(Subject, slug = slug) |
58 | 58 | ||
59 | self.object.subject = subject | 59 | self.object.subject = subject |
60 | - print (subject.topic_subject.count()) | ||
61 | self.object.order = subject.topic_subject.count() + 1 | 60 | self.object.order = subject.topic_subject.count() + 1 |
62 | 61 | ||
63 | self.object.save() | 62 | self.object.save() |