From: Yoan Le Clanche Date: Wed, 17 Jun 2020 12:35:05 +0000 (+0200) Subject: V1 X-Git-Tag: 1.4.3~75^2 X-Git-Url: https://git.parisson.com/?a=commitdiff_plain;h=16b3abf2e6f31d80fb07cf3b54f06f47bc5fa8e9;p=teleforma.git V1 --- diff --git a/teleforma/management/commands/teleforma-webclass-send-notifications.py b/teleforma/management/commands/teleforma-webclass-send-notifications.py index 80546f7a..2630e186 100644 --- a/teleforma/management/commands/teleforma-webclass-send-notifications.py +++ b/teleforma/management/commands/teleforma-webclass-send-notifications.py @@ -21,7 +21,7 @@ class Command(BaseCommand): dow = now.weekday() begin = now + datetime.timedelta(minutes=WARN_DELAY_MIN) end = now + datetime.timedelta(minutes=WARN_DELAY_MAX) - slots = WebclassSlot.objects.filter(day = dow, + slots = WebclassSlot.published.filter(day = dow, start_hour__gte = begin.time(), start_hour__lt = end.time()) site = Site.objects.all()[0] diff --git a/teleforma/static/teleforma/css/teleforma.css b/teleforma/static/teleforma/css/teleforma.css index 47f049c8..7f4787e1 100644 --- a/teleforma/static/teleforma/css/teleforma.css +++ b/teleforma/static/teleforma/css/teleforma.css @@ -198,6 +198,20 @@ margin-bottom: 0em; padding: 2px 5px 7px 5px; } +.block { + background-color: #FFF; + -moz-border-radius: 8px 0px 8px 8px; + -webkit-border-radius: 8px 0px 8px 8px; + border-radius: 8px 0px 8px 8px; + padding: 0em 0em 0em 0.8em; + margin-top: 0.8em; + margin-bottom: 0.8em; + border: 1px solid #dfdfdf; +} + +.important { + background-color: rgb(255, 240, 240); +} .exporter { background-color: #fff; diff --git a/teleforma/templates/teleforma/course_detail.html b/teleforma/templates/teleforma/course_detail.html index 9e8d65e6..575f3095 100644 --- a/teleforma/templates/teleforma/course_detail.html +++ b/teleforma/templates/teleforma/course_detail.html @@ -4,104 +4,108 @@ {% block extra_javascript %} {% endblock extra_javascript %} {% block courses %} {% for c in all_courses %} - {% with c.course as course %} -
  • {{ course.title }}
  • - {% endwith %} - {% endfor %} +{% with c.course as course %} +
  • {{ course.title }}
  • +{% endwith %} +{% endfor %} {% endblock courses %} {% block course %}
    -
    {% if webclass and not webclass_slot %} - Vous n'êtes pas inscrit à la webconférence de cette matière. Cliquez-ici pour choisir un créneau horaire. +
    +

    + Vous n'êtes pas inscrit à la webclass de cette matière. Cliquez-ici pour choisir un créneau horaire +

    +
    {% endif %} -
    -

    - {% for c in courses %} - {% with c.course as course %} - {% for type in c.types %} -
    -
    {{ course.title }} - {{ type }}{% if course.description %} - {{ course.description }}{% endif %} -
    +

    + {% for c in courses %} + {% with c.course as course %} + {% for type in c.types %} +
    +
    {{ course.title }} - {{ type }}{% if course.description %} - + {{ course.description }}{% endif %} +
    - {% if type.name == 'Quiz' %} -
    - {% if course.quiz.all %} - - - {% for quiz in course.quiz.all %} - - - {% endfor %} - -
    {{quiz.title}}{{quiz.description}}
    - {% else %} -

    Aucun quiz

    - {% endif %} -
    + {% if type.name == 'Quiz' %} +
    + {% if course.quiz.all %} + + + {% for quiz in course.quiz.all %} + + + {% endfor %} + +
    {{quiz.title}}{{quiz.description}}
    + {% else %} +

    Aucun quiz

    + {% endif %} +
    - {% else %} - {% if show_media %} - {% block conference %} - {% include "teleforma/inc/conference_list.html" %} - {% endblock %} + {% else %} + {% if show_media %} + {% block conference %} + {% include "teleforma/inc/conference_list.html" %} + {% endblock %} - {% block media %} - {% include "teleforma/inc/media_list.html" %} - {% endblock %} - {% endif %} + {% block media %} + {% include "teleforma/inc/media_list.html" %} + {% endblock %} + {% endif %} - {% block document %} - {% with forloop.counter as type_counter %} - {% include "teleforma/inc/document_list.html" %} - {% endwith %} - {% endblock %} - {% endif %} + {% block document %} + {% with forloop.counter as type_counter %} + {% include "teleforma/inc/document_list.html" %} + {% endwith %} + {% endblock %} + {% endif %} -
    - {% endfor %} - -
    -
    {{ course.title }} - Webclass{% if course.description %} - {{ course.description }}{% endif %} -
    - {% block webclass %} - TEST - {% include "webclass/inc/webclass_list.html" %} - {% endblock %} -
    - {% endwith %} - {% endfor %} +
    + {% endfor %} + +
    +
    {{ course.title }} - Webclass{% if course.description %} - + {{ course.description }}{% endif %} +
    + {% block webclass %} + {% include "webclass/inc/webclass_list.html" %} + {% endblock %} +
    + {% endwith %} + {% endfor %}
    {% endblock course %} {% block chat %} {% if course.title_tweeter %} {% if room %} - {% if 'site' in room.name %} - {% with "General tweeter" as title %} - {% include "teleforma/inc/chat_room.html" %} - {% endwith %} - {% else %} - {% with "Tweeter"|add:" "|add:course.title_tweeter as title %} - {% include "teleforma/inc/chat_room.html" %} - {% endwith %} - {% endif %} +{% if 'site' in room.name %} +{% with "General tweeter" as title %} +{% include "teleforma/inc/chat_room.html" %} +{% endwith %} +{% else %} +{% with "Tweeter"|add:" "|add:course.title_tweeter as title %} +{% include "teleforma/inc/chat_room.html" %} +{% endwith %} +{% endif %} {% endif %} {% endif %} {% newsitems_portlet course_id=course.id period_id=period.id %} -{% endblock chat %} - +{% endblock chat %} \ No newline at end of file diff --git a/teleforma/templates/teleforma/courses.html b/teleforma/templates/teleforma/courses.html index 5ee3435a..112cd3ae 100644 --- a/teleforma/templates/teleforma/courses.html +++ b/teleforma/templates/teleforma/courses.html @@ -7,9 +7,9 @@ {% block extra_javascript %} {% endblock extra_javascript %} @@ -18,42 +18,45 @@ $(document).ready(function(){
    -{% block modules %} -
    - -

    playlists{{ period }}

    -
    -
      -{% block courses %} -{% for c in all_courses %} - {% with c.course as course %} -
    • {{ course.title }}
    • - {% endwith %} - {% endfor %} -{% endblock courses %} -
    -
    - -
    - -{% block module-action %} -{% get_telecaster as telecaster %} -{% if telecaster %} - -{% endif %} - -{% if user.is_staff %} - -{% endif %} - -{% endblock module-action %} - -{% block notes %} - -{% endblock notes %} + {% endblock notes %} -{% endblock modules %} + {% endblock modules %}
    @@ -76,102 +79,155 @@ $(document).ready(function(){
    - {% if hasAppointment %} - -
    - {% endif %} + {% if hasAppointment %} + +
    + {% endif %} +
    {% autoescape off %} {{ home_text }} {% endautoescape %} - - {% if home_video.type == 'webm' %} -

    {% if home_video.title %}{{ home_video.title }}{% else %}{{ home_video.course.title }}{% endif %}

    - + + {% if home_video.type == 'webm' %} + + {% endif %} + + {% if webclass_slots or webclass_to_subscribe %} +
    + Webclasse +
    +
    + + + + {% for slot in webclass_slots %} + + + + {% endfor %} + {% for webclass in webclass_to_subscribe %} + + + + {% endfor %} + +
    +

    + {% if slot.status == 'ingoing' %} + La webclasse "{{slot.webclass.course.title}}" est en cours. + {% elif slot.status == 'almost' %} + La webclasse "{{slot.webclass.course.title}}" va démarrer bientôt. {% endif %} - {% endfor %} - {% else %} - {% trans 'Click here' %} - {% endif %} - - - {% endif %} + Cliquez ici pour rejoindre la + webclasse +

    +
    +

    + Vous n'êtes pas inscrit à la webclasse "{{webclass.course.title}}". Cliquez-ici pour choisir un créneau + horaire +

    +
    +
    + {% endif %} + + + {% for c in courses %} + {% with c.course as course %} + {% for type in c.types %} +
    + + + {% if show_media %} + + {% block conference %} + {% include "teleforma/inc/conference_list.html" %} + {% endblock %} + + {% block media %} + {% with "Passed conferences" as title %} + {% include "teleforma/inc/media_list.html" with show_only=1 %} + {% endwith %} + {% endblock %} - {% for c in courses %} - {% with c.course as course %} - {% for type in c.types %} -
    - + {% comment %} + {% block webclass %} + {% with "Web class" as title %} + {% include "teleforma/inc/media_list.html" with show_only=1 %} + {% endwith %} + {% endblock %} + {% endcomment %} + + {% endif %} - {% if show_media %} - - {% block conference %} - {% include "teleforma/inc/conference_list.html" %} - {% endblock %} - - {% block media %} - {% with "Passed conferences" as title %} - {% include "teleforma/inc/media_list.html" with show_only=1 %} - {% endwith %} - {% endblock %} - - {% comment %} - {% block webclass %} - {% with "Web class" as title %} - {% include "teleforma/inc/media_list.html" with show_only=1 %} - {% endwith %} - {% endblock %} - {% endcomment %} - - {% endif %} - - {% block document %} - {% with forloop.counter as type_counter %} - {% include "teleforma/inc/document_list.html" %} - {% endwith %} - {% endblock %} -
    - {% endfor %} + {% block document %} + {% with forloop.counter as type_counter %} + {% include "teleforma/inc/document_list.html" %} {% endwith %} - {% endfor %} + {% endblock %} +
    + {% endfor %} + {% endwith %} + {% endfor %}
    {% endblock course %}
    -{% block status %} -{% get_telecaster as telecaster %} -{% if telecaster %} -
    -

    status {% trans "Status" %}

    -
    -
    - -{% endif %} -{% endblock status %} - -{% block chat %} -{% if room %} -{% with "General tweeter" as title %} -{% include "teleforma/inc/chat_room.html" %} -{% endwith %} -{% endif %} -{% endblock chat %} + {% block status %} + {% get_telecaster as telecaster %} + {% if telecaster %} +
    +

    status + {% trans "Status" %}

    +
    +
    + + {% endif %} + {% endblock status %} + + {% block chat %} + {% if room %} + {% with "General tweeter" as title %} + {% include "teleforma/inc/chat_room.html" %} + {% endwith %} + {% endif %} + {% endblock chat %}
    -{% endblock content %} +{% endblock content %} \ No newline at end of file diff --git a/teleforma/views/core.py b/teleforma/views/core.py index 887683f7..f56174c7 100644 --- a/teleforma/views/core.py +++ b/teleforma/views/core.py @@ -342,7 +342,8 @@ class CourseListView(CourseAccessMixin, ListView): context['room'] = get_room(name='site', period=context['period'].name) context['list_view'] = True context['courses'] = sorted(context['all_courses'], key=lambda k: k['date'], reverse=True)[:1] - is_student = self.request.user.student.all().count() + user = self.request.user + is_student = user.student.all().count() appointments = AppointmentPeriod.objects.filter(periods=context['period']) appointments_open = False for appointment in appointments: @@ -356,6 +357,23 @@ class CourseListView(CourseAccessMixin, ListView): context['home_text'] = home.text context['home_video'] = home.video break + + if is_student: + student = user.student.all()[0] + slots = [] + to_subscribe = [] + student_courses = [course['course'] for course in get_courses(user)] + for webclass in Webclass.published.filter(period=self.period, iej=student.iej, course__in=student_courses): + # if webclass.course not in student_courses: + # continue + slot = webclass.get_slot(user) + if slot and slot.status in ('almost', 'ingoing'): + slots.append(slot) + if not slot: + to_subscribe.append(webclass) + context['webclass_slots'] = slots + context['webclass_to_subscribe'] = to_subscribe + return context @method_decorator(login_required) @@ -422,15 +440,19 @@ class CourseView(CourseAccessMixin, DetailView): if student: try: - webclass = Webclass.objects.get(period=self.period, course=course, iej=student.iej) - except Webclass.DoesNotExist: + webclass = Webclass.published.filter(period=self.period, course=course, iej=student.iej)[0] + except IndexError: pass if webclass: webclass_slot = webclass.get_slot(self.request.user) context['webclass'] = webclass context['webclass_slot'] = webclass_slot - print(WebclassRecord.get_records(context['period'], course)) + try: + context['webclass_records'] = WebclassRecord.get_records(context['period'], course) + except Exception, e: + print(e) + context['webclass_error'] = True return context @method_decorator(login_required) diff --git a/teleforma/webclass/forms.py b/teleforma/webclass/forms.py index 2878a090..627c3529 100644 --- a/teleforma/webclass/forms.py +++ b/teleforma/webclass/forms.py @@ -3,7 +3,7 @@ from datetime import datetime from django.forms import Form, ModelChoiceField, ChoiceField from teleforma.models.core import Course, Period -from teleforma.webclass.models import get_records, WebclassSlot, WebclassRecord +from teleforma.webclass.models import get_records, WebclassSlot, WebclassRecord, BBBServer from django.core.exceptions import ValidationError class WebclassRecordsForm(Form): @@ -22,10 +22,10 @@ class WebclassRecordsForm(Form): courses = Course.objects.all() all_records = self.get_records_by_course() for course in courses: - webclass = course.webclass.count() - if course.webclass.count(): + webclasses = course.webclass.filter(period=self.period).all() + if webclasses: rooms = [] - for webclass in course.webclass.all(): + for webclass in webclasses: for slot in webclass.slots.all(): rooms.append(slot.room_id) @@ -34,10 +34,9 @@ class WebclassRecordsForm(Form): vocabulary = [('none', 'Aucun')] for record in records: - print(record) - webclass_slot = WebclassSlot.objects.get(pk=record['slot_id']) + webclass_slot = WebclassSlot.objects.get(pk=record['slot'].id) label = u"%s à %s - %s" % (record['start_date'].strftime('%d/%m/%Y %H:%M'), record['end_date'].strftime('%H:%M'), webclass_slot.professor.user.last_name) - vocabulary.append((record['id'], label)) + vocabulary.append((str(record['id']) + ";" + str(record['server_id']), label)) self.fields[field_name] = ChoiceField(label=course.title, choices=vocabulary, required=False) def get_records_by_course(self): @@ -50,7 +49,9 @@ class WebclassRecordsForm(Form): def save_records(self): for key, value in self.data.items(): if key.startswith('course') and value != 'none': + record_id, server_id = value.split(';') course_id = key.replace('course_', '') course = Course.objects.get(pk=course_id) - record = WebclassRecord(course=course, period=self.period, record_id=value) + server = BBBServer.objects.get(pk=server_id) + record = WebclassRecord(course=course, period=self.period, record_id=record_id, bbb_server=server) record.save() diff --git a/teleforma/webclass/migrations/0003_auto__add_field_webclassrecord_bbb_server.py b/teleforma/webclass/migrations/0003_auto__add_field_webclassrecord_bbb_server.py new file mode 100644 index 00000000..07d40427 --- /dev/null +++ b/teleforma/webclass/migrations/0003_auto__add_field_webclassrecord_bbb_server.py @@ -0,0 +1,191 @@ +# -*- coding: utf-8 -*- +from south.utils import datetime_utils as datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding field 'WebclassRecord.bbb_server' + db.add_column('teleforma_webclass_record', 'bbb_server', + self.gf('django.db.models.fields.related.ForeignKey')(default=1, related_name='webclass_records', to=orm['webclass.BBBServer']), + keep_default=False) + + + def backwards(self, orm): + # Deleting field 'WebclassRecord.bbb_server' + db.delete_column('teleforma_webclass_record', 'bbb_server_id') + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'quiz.category': { + 'Meta': {'object_name': 'Category'}, + 'category': ('django.db.models.fields.CharField', [], {'max_length': '250', 'unique': 'True', 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + 'quiz.quiz': { + 'Meta': {'object_name': 'Quiz'}, + 'answers_at_end': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'category': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['quiz.Category']", 'null': 'True', 'blank': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'draft': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'exam_paper': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'fail_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'max_questions': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), + 'pass_mark': ('django.db.models.fields.SmallIntegerField', [], {'default': '0', 'blank': 'True'}), + 'random_order': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'single_attempt': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'success_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '60'}), + 'url': ('django.db.models.fields.SlugField', [], {'max_length': '60'}) + }, + 'teleforma.course': { + 'Meta': {'ordering': "['number']", 'object_name': 'Course'}, + 'code': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'date_modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'null': 'True', 'blank': 'True'}), + 'department': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'course'", 'to': "orm['teleforma.Department']"}), + 'description': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'has_exam_scripts': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_professor_sent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['teleforma.Professor']", 'null': 'True', 'blank': 'True'}), + 'magistral': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'number': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'obligation': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'oral_1': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'oral_2': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'oral_speciality': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'periods': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'courses'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['teleforma.Period']"}), + 'procedure': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'quiz': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['quiz.Quiz']", 'null': 'True', 'blank': 'True'}), + 'synthesis_note': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'title_tweeter': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'written_speciality': ('django.db.models.fields.BooleanField', [], {'default': 'False'}) + }, + 'teleforma.department': { + 'Meta': {'object_name': 'Department'}, + 'default_period': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'departments'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['teleforma.Period']"}), + 'description': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'domain': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'organization': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'department'", 'to': "orm['teleforma.Organization']"}) + }, + 'teleforma.iej': { + 'Meta': {'ordering': "['name']", 'object_name': 'IEJ'}, + 'description': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}) + }, + 'teleforma.organization': { + 'Meta': {'object_name': 'Organization'}, + 'description': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}) + }, + 'teleforma.period': { + 'Meta': {'ordering': "['name']", 'object_name': 'Period'}, + 'date_begin': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'date_close_accounts': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'date_end': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'date_exam_end': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'date_inscription_end': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'date_inscription_start': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'date_password_init': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'department': ('telemeta.models.core.ForeignKey', [], {'default': 'None', 'related_name': "'period'", 'null': 'True', 'blank': 'True', 'to': "orm['teleforma.Department']"}), + 'description': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_open': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'message_local': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'message_platform': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'nb_script': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['teleforma.Period']"}) + }, + 'teleforma.professor': { + 'Meta': {'ordering': "['user__last_name']", 'object_name': 'Professor'}, + 'courses': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'professor'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['teleforma.Course']"}), + 'department': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'professor'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['teleforma.Department']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'professor'", 'unique': 'True', 'to': "orm['auth.User']"}) + }, + 'webclass.bbbserver': { + 'Meta': {'object_name': 'BBBServer', 'db_table': "'teleforma_bbb_server'"}, + 'api_key': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'url': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'webclass.webclass': { + 'Meta': {'object_name': 'Webclass', 'db_table': "'teleforma_webclass'"}, + 'bbb_server': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'webclass'", 'to': "orm['webclass.BBBServer']"}), + 'course': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'webclass'", 'to': "orm['teleforma.Course']"}), + 'department': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'webclass'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['teleforma.Department']"}), + 'duration': ('telemeta.models.core.DurationField', [], {'default': "'0'", 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'iej': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'webclass'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['teleforma.IEJ']"}), + 'max_participants': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'period': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'webclass'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['teleforma.Period']"}), + 'status': ('django.db.models.fields.IntegerField', [], {'default': '2'}) + }, + 'webclass.webclassrecord': { + 'Meta': {'object_name': 'WebclassRecord', 'db_table': "'teleforma_webclass_record'"}, + 'bbb_server': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'webclass_records'", 'to': "orm['webclass.BBBServer']"}), + 'course': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'webclass_records'", 'to': "orm['teleforma.Course']"}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'period': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['teleforma.Period']"}), + 'record_id': ('django.db.models.fields.CharField', [], {'max_length': '255'}) + }, + 'webclass.webclassslot': { + 'Meta': {'object_name': 'WebclassSlot', 'db_table': "'teleforma_webclass_slot'"}, + 'day': ('django.db.models.fields.IntegerField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'participants': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'webclass_slot'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['auth.User']"}), + 'professor': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'webclass_slot'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['teleforma.Professor']"}), + 'room_id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'room_password': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'start_hour': ('django.db.models.fields.TimeField', [], {}), + 'webclass': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'slots'", 'to': "orm['webclass.Webclass']"}) + } + } + + complete_apps = ['webclass'] \ No newline at end of file diff --git a/teleforma/webclass/models.py b/teleforma/webclass/models.py index 8616b7c6..cacbbf00 100644 --- a/teleforma/webclass/models.py +++ b/teleforma/webclass/models.py @@ -8,7 +8,6 @@ from unidecode import unidecode from django.db.models import * from telemeta.models import * from teleforma.fields import * -from teleforma.models.core import STATUS_CHOICES import django.db.models as models from django.utils.translation import ugettext_lazy as _ from django.utils import translation @@ -25,6 +24,10 @@ app_label = 'teleforma' DAYS_CHOICES = [(i, _(calendar.day_name[i])) for i in range(7)] # BBB_SERVER = "https://bbb.parisson.com/bigbluebutton/" # BBB_SECRET = "uOzkrTnWly1jusr0PYcrlwhvKhZG1ZYDOrSvxgP70" +STATUS_CHOICES = ( + (2, _('Draft')), + (3, _('Public')), + ) class MetaCore: @@ -35,9 +38,10 @@ def get_records_from_bbb(**kwargs): """get records info from bbb xml""" records = [] for server in BBBServer.objects.all(): - recordings_xml = server.get_instance().get_recordings(**kwargs).get_field('recordings') - if hasattr(recordings_xml, 'get'): - recordings = recordings_xml['recording'] + recordings = server.get_instance().get_recordings(**kwargs).get_field('recordings') + # import pdb;pdb.set_trace() + if hasattr(recordings, 'get'): + recordings = recordings['recording'] if type(recordings) is XMLDictNode: recordings = [recordings] for recording in recordings: @@ -45,21 +49,25 @@ def get_records_from_bbb(**kwargs): url = recording.get('playback', {}).get('format', {}).get('url') if url: url = url.decode() + else: + continue if not recording['metadata'].get('periodid'): continue start = int(recording['startTime'].decode()[:-3]) end = int(recording['endTime'].decode()[:-3]) data = { 'id': recording['recordID'].decode(), + 'server_id': server.id, 'start': start, 'start_date': datetime.datetime.fromtimestamp(start), 'end': end, 'end_date': datetime.datetime.fromtimestamp(end), 'url': url, + 'preview': recording.get('playback', {}).get('format', {}).get('preview', {}).get('images', {}).get('image', [])[0].decode(), 'state': recording['state'].decode(), 'period_id': int(recording['metadata'].get('periodid').decode()), 'course_id': int(recording['metadata'].get('courseid').decode()), - 'slot_id': int(recording['metadata'].get('slotid').decode()), + 'slot': WebclassSlot.objects.get(pk=int(recording['metadata'].get('slotid').decode())) } data['duration'] = data['end'] - data['start'] records.append(data) @@ -104,6 +112,9 @@ class BBBServer(models.Model): def __unicode__(self): return "Serveur %d" % self.id +class PublishedManager(models.Manager): + def get_query_set(self): + return super(PublishedManager, self).get_query_set().filter(status=3) class Webclass(models.Model): @@ -112,10 +123,12 @@ class Webclass(models.Model): course = models.ForeignKey('teleforma.Course', related_name='webclass', verbose_name=_('course')) iej = models.ManyToManyField('teleforma.IEJ', related_name='webclass', verbose_name=_('iej'), blank=True, null=True) bbb_server = models.ForeignKey('BBBServer', related_name='webclass', verbose_name='Serveur BBB') - duration = DurationField('Durée de la conférence') - max_participants = models.IntegerField('Nombre maxium de participants par créneau', blank=True, null=True) + duration = DurationField('Durée de la conférence', default="00:30:00") + max_participants = models.IntegerField('Nombre maxium de participants par créneau', blank=True, null=True, default=80) status = models.IntegerField(_('status'), choices=STATUS_CHOICES, default=2) + published = PublishedManager() + objects = models.Manager() class Meta(MetaCore): db_table = app_label + '_' + 'webclass' @@ -128,11 +141,13 @@ class Webclass(models.Model): def get_slot(self, user): """ return webclass slot or None if user is not subscribed """ try: - return WebclassSlot.objects.get(webclass=self, participants=user) + return WebclassSlot.published.get(webclass=self, participants=user) except WebclassSlot.DoesNotExist: return None - +class SlotPublishedManager(models.Manager): + def get_query_set(self): + return super(SlotPublishedManager, self).get_query_set().filter(webclass__status=3) class WebclassSlot(models.Model): """ Webclass slot """ @@ -146,6 +161,9 @@ class WebclassSlot(models.Model): room_id = models.CharField('id de la conférence BBB (généré automatiquement)', blank=True, null=True, max_length=255) room_password = models.CharField('password du modérateur (généré automatiquement)', blank=True, null=True, max_length=255) + published = SlotPublishedManager() + objects = models.Manager() + class Meta(MetaCore): db_table = app_label + '_' + 'webclass_slot' verbose_name = _('webclass slot') @@ -153,13 +171,21 @@ class WebclassSlot(models.Model): def __unicode__(self): return "Webclass slot : " + str(self.id) + + @property + def remaining_participant_slot(self): + """ + get remaining participant slot + """ + nb_participants = self.participants.count() + return self.webclass.max_participants - nb_participants + @property def participant_slot_available(self): """ is there any slot available for another participants """ - nb_participants = self.participants.count() - return nb_participants < self.webclass.max_participants + return self.remaining_participant_slot > 0 @property def end_hour(self): @@ -204,7 +230,7 @@ class WebclassSlot(models.Model): # 'autoStartRecording':True, 'muteOnStart':True, 'allowModsToUnmuteUsers':True, - 'logo':'https://e-learning.crfpa.pre-barreau.com/static/teleforma/images/logo_pb.png', + # 'logo':'https://e-learning.crfpa.pre-barreau.com/static/teleforma/images/logo_pb.png', 'copyright': "© %d Pré-Barreau" % year, # 'guestPolicy':'ALWAYS_ACCEPT' 'bannerText': "Pré-Barreau", @@ -217,7 +243,7 @@ class WebclassSlot(models.Model): 'courseid': webclass.course.id, 'webclassid': webclass.id, 'slotid': self.id, - 'professor': self.professor.last_name, + 'professor': self.professor.user.username, } print params try: @@ -344,7 +370,9 @@ class WebclassRecord(models.Model): period = models.ForeignKey('teleforma.Period', verbose_name=_('period')) course = models.ForeignKey('teleforma.Course', related_name='webclass_records', verbose_name=_('course')) - record_id = models.CharField("Enregistrement BBB", max_length=255) + record_id = models.CharField("Enregistrement BBB", max_length=255) + # not used for now, but may be handy if we need to optimize performance + bbb_server = models.ForeignKey('BBBServer', related_name='webclass_records', verbose_name='Serveur BBB') created = models.DateTimeField("Date de la conférence", auto_now_add=True) class Meta(MetaCore): @@ -355,15 +383,13 @@ class WebclassRecord(models.Model): def __unicode__(self): return "Enregistrement webclass %d" % self.id - @staticmethod def get_records(period, course): - records = [] + record_ids = set() for record in WebclassRecord.objects.filter(period=period, course=course): - records.append(record.record_id) - - records = get_records_from_bbb(recording_id=','.join(records)) + record_ids.add(record.record_id) + if not record_ids: + return [] + records = get_records_from_bbb(recording_id=','.join(record_ids)) print(records) return records - - diff --git a/teleforma/webclass/templates/webclass/appointments.html b/teleforma/webclass/templates/webclass/appointments.html index c30374fd..ee24c413 100644 --- a/teleforma/webclass/templates/webclass/appointments.html +++ b/teleforma/webclass/templates/webclass/appointments.html @@ -51,15 +51,48 @@
    - - {% for slot in slots %} -
    - {{slot.day}} de {{slot.start_hour|date:"H\hi"}} à {{slot.end_hour|date:"H\hi"}} - {% csrf_token %} - - -
    - {% endfor %} + +

    Inscription à la Webclasse "{{webclass.course.title}}"

    +

    + + + + + + + + + + + + {% for slot in slots %} + + + + + + + + {% endfor %} + +
    HoraireProfesseurPlaces restantesInscription
    + {{slot.get_day_display}} de {{slot.start_hour|date:"H\hi"}} à {{slot.end_hour|date:"H\hi"}} + + {{slot.professor}} + + {{slot.remaining_participant_slot}} + +
    + + {% csrf_token %} + + {% if slot.participant_slot_available %} + + {% else %} + Complet + {% endif %} +
    +
    {% endblock content %} diff --git a/teleforma/webclass/templates/webclass/appointments_professor.html b/teleforma/webclass/templates/webclass/appointments_professor.html index 7dbe5615..9998f8dd 100644 --- a/teleforma/webclass/templates/webclass/appointments_professor.html +++ b/teleforma/webclass/templates/webclass/appointments_professor.html @@ -14,14 +14,46 @@ Calendrier des Webclass {% endblock infra_javascript %} {% block content %} -
    + + + + + + + {% comment %} {% endcomment %} + + + + + {% for slot in slots %} -
    - {{slot.get_day_display}} de {{slot.start_hour|date:"H\hi"}} à {{slot.end_hour|date:"H\hi"}} : {{slot.participants.count}} participant{{slot.participants.count|pluralize}} - Rejoindre la conférence -
    + + + + + {% comment %} {% endcomment %} + + + {% empty %} -

    Aucune webclasse programmée.

    + + + {% endfor %} - + +
    HorairePériodeCoursIEJParticipantsSalon
    + {{slot.get_day_display}} de {{slot.start_hour|date:"H\hi"}} à {{slot.end_hour|date:"H\hi"}} + + {{slot.webclass.period.name}} + + {{slot.webclass.course.title}} + + {% for iej in slot.webclass.iej.all %} + {{iej.name}} + {% endfor %} + + {{slot.participants.count}} + + Rejoindre la conférence +
    Aucune webclasse programmée.
    {% endblock content %} diff --git a/teleforma/webclass/templates/webclass/inc/webclass_list.html b/teleforma/webclass/templates/webclass/inc/webclass_list.html index e5aba91c..3c600e3c 100644 --- a/teleforma/webclass/templates/webclass/inc/webclass_list.html +++ b/teleforma/webclass/templates/webclass/inc/webclass_list.html @@ -1,73 +1,73 @@ {% load teleforma_tags %} {% load i18n %} -{% if webclass_slot %} +{% if webclass_slot or webclass_records %}
    -
    -

    Webclass live

    -
    +
    +

    Webclasse live

    +
    - - {% if webclass_slot %} - - - - {% endif %} - - {% for conference in conferences|from_period:period %} - {% for stream in conference.livestream.all %} - {% if stream.stream_type == 'webm' %} + + {% if webclass_slot %} - {% if stream.streaming %} - - - - {% else %} -
    -
    - {% endif %} + {% endif %} - {% endfor %} - {% endfor %} - + {% for record in webclass_records %} + + + +
    + +
    + + {% endfor %} +
    -

    Vous êtes inscrit pour les webconférence du - {{webclass_slot.get_day_display}} de - {{webclass_slot.start_hour|date:"H\hi"}} à - {{webclass_slot.end_hour|date:"H\hi"}} - avec le professeur {{webclass_slot.professor.user.last_name}}.

    - {{webclass_slot.status}} - {% if webclass_slot.status == 'past' %} -

    La webconférence est terminée.

    - {% elif webclass_slot.status == 'ingoing' %} -

    La webconférence est en cours.

    - Cliquez ici pour rejoindre la conférence - {% elif webclass_slot.status == 'almost' %} -

    Le webconférence est accessible mais elle n'a pas encore démarré.

    - Cliquez ici pour rejoindre la conférence - {% endif %} -
    - - {% trans 'Click here' %} - - -
    -
    -
    {% trans "Title" %}
    {{ stream.conference.course.title }}
    -
    {% trans "Session" %}
    {{ stream.conference.session }}
    - {% if stream.conference.professor.user.username %} -
    {% trans "Professor" %}
    {{ stream.conference.professor }}
    +
    +

    Vous êtes inscrit pour les webclasses du + {{webclass_slot.get_day_display}} de + {{webclass_slot.start_hour|date:"H\hi"}} à + {{webclass_slot.end_hour|date:"H\hi"}} + avec le professeur {{webclass_slot.professor.user.last_name}}.

    + + {% if webclass_slot.status == 'past' %} +

    + La webclasse est terminée. +

    + {% elif webclass_slot.status == 'ingoing' %} +

    + La webclasse est en cours. + Cliquez ici pour rejoindre la + conférence +

    + {% elif webclass_slot.status == 'almost' %} +

    + La webclasse est accessible mais elle n'a pas encore démarré. + Cliquez ici pour rejoindre la + conférence +

    + {% endif %} + {% if webclass_error %}

    Impossible d'afficher la liste des webclasse : le serveur a rencontré une + erreur, veuillez réessayer plus tard.

    {% endif %} -
    {% trans "Begin" %}
    {{ stream.conference.date_begin }}
    - - -
    - {% if stream.streaming %} - - - {% endif %} -
    + + {% trans 'Click here' %} +
    Cliquez-ici
    +
    +
    +
    +
    +
    {% trans "Professor" %}
    +
    {{ record.slot.professor }}
    +
    {% trans "Begin" %}
    +
    {{ record.start_date }}
    +
    +
    +
    -{% endif %} +{% endif %} \ No newline at end of file diff --git a/teleforma/webclass/templates/webclass/record.html b/teleforma/webclass/templates/webclass/record.html new file mode 100644 index 00000000..73a55e2b --- /dev/null +++ b/teleforma/webclass/templates/webclass/record.html @@ -0,0 +1,28 @@ +{% extends "telemeta/base.html" %} +{% load telemeta_utils %} +{% load teleforma_tags %} +{% load i18n %} + + +{% block extra_stylesheets %} + + + +{% endblock extra_stylesheets %} + + + +{% block content %} + +{% endblock content %} + diff --git a/teleforma/webclass/urls.py b/teleforma/webclass/urls.py index 4a8a702f..87d77617 100644 --- a/teleforma/webclass/urls.py +++ b/teleforma/webclass/urls.py @@ -41,6 +41,7 @@ urlpatterns = patterns('', url(r'^desk/webclass_appointments/(?P.*)$', WebclassAppointment.as_view(), name="teleforma-webclass-appointments"), url(r'^desk/webclass_calendar/$', WebclassProfessorAppointments.as_view(), name="teleforma-webclass-professor"), + url(r'^desk/webclass_record$', WebclassRecordView.as_view(), name="teleforma-webclass-record"), url(r'^admin/periods/(?P.*)/webclass_records_form/$', WebclassRecordsFormView.as_view(), name="teleforma-webclass-records-form"), url(r'^desk/webclass/(?P.*)/join/$', join_webclass, diff --git a/teleforma/webclass/views.py b/teleforma/webclass/views.py index 99395fe4..eca42ed5 100644 --- a/teleforma/webclass/views.py +++ b/teleforma/webclass/views.py @@ -1,7 +1,9 @@ # -*- coding: utf-8 -*- from django.views.generic import View, TemplateView, FormView +from django.utils.decorators import method_decorator from django.contrib import messages +from django.contrib.auth.decorators import login_required, permission_required from django.http import HttpResponse from django.shortcuts import redirect, get_object_or_404, render from django.template.loader import render_to_string @@ -28,7 +30,7 @@ class WebclassProfessorAppointments(TemplateView): user = self.request.user if not user.professor: return HttpResponse('Unauthorized', status=401) - context['slots'] = WebclassSlot.objects.filter(professor=user.professor.get(), webclass__status=3).order_by('day', 'start_hour') + context['slots'] = WebclassSlot.published.filter(professor=user.professor.get(), webclass__status=3).order_by('day', 'start_hour') print(context['slots']) return context @@ -62,18 +64,7 @@ class WebclassAppointment(View): user = request.user student = user.student.all()[0] - # Get info - slots = [] - for slot in webclass.slots.order_by('day', 'start_hour'): - slots.append({ - 'id': slot.id, - 'day': slot.get_day_display(), - 'start_hour':slot.start_hour, - 'end_hour': slot.end_hour, - 'professor': slot.professor, - 'available': slot.participant_slot_available - }) - return render(request, self.template_name, {'slots': slots}) + return render(request, self.template_name, {'slots': webclass.slots.order_by('day', 'start_hour'), 'webclass': webclass}) def check_slot_validity(self, user, slot): """ @@ -98,7 +89,7 @@ class WebclassAppointment(View): user = request.user slot_id = int(request.POST.get('slot_id')) - slot = WebclassSlot.objects.get(pk=slot_id) + slot = WebclassSlot.published.get(pk=slot_id) msg = self.check_slot_validity(user, slot) @@ -143,6 +134,15 @@ class WebclassAppointment(View): # fail_silently=False) # return data +class WebclassRecordView(TemplateView): + template_name = 'webclass/record.html' + + def get_context_data(self, **kwargs): + """ """ + context = super(WebclassRecordView, self).get_context_data(**kwargs) + context['record_url'] = self.request.GET.get('url') + return context + class WebclassRecordsFormView(FormView): template_name = 'webclass/records_form.html' @@ -157,10 +157,14 @@ class WebclassRecordsFormView(FormView): def form_valid(self, form): form.save_records() return super(WebclassRecordsFormView, self).form_valid(form) - + + @method_decorator(permission_required('is_superuser')) + @method_decorator(login_required) + def dispatch(self, *args, **kwargs): + return super(WebclassRecordsFormView, self).dispatch(*args, **kwargs) def join_webclass(request, pk): - webclass_slot = WebclassSlot.objects.get(pk=int(pk)) + webclass_slot = WebclassSlot.published.get(pk=int(pk)) # webclass = webclass_slot.webclass # fake debug links # username = request.GET.get('username')