</div>
<br />
-{% if messages %}
+{% comment %} {% if messages %}
<div class="messages">
{% for message in messages %}
<div{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</div>
{% endfor %}
</div>
-{% endif %}
+{% endif %} {% endcomment %}
<div class="course_content" id="media_infos" style="font-size: 115%;">
<form method="post" id="_ScriptForm" action="" enctype="multipart/form-data" data-ajax="false">{% csrf_token %}
</div>
<br />
-{% if messages %}
+{% comment %} {% if messages %}
<div class="messages">
{% for message in messages %}
<div{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</div>
{% endfor %}
</div>
-{% endif %}
+{% endif %} {% endcomment %}
<div>Ce formulaire vous permet d'enregistrer la note d'une copie papier <b>déjà corrigée hors de la plateforme e-learning</b>.</div>
</div>
<br />
-{% if messages %}
+{% comment %} {% if messages %}
<div class="messages">
{% for message in messages %}
<div{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</div>
{% endfor %}
</div>
-{% endif %}
+{% endif %} {% endcomment %}
<br />
{% include_container data.chartcontainer 500 '100%' %}
</div>
<br />
-{% if messages %}
+{% comment %} {% if messages %}
<div class="messages">
{% for message in messages %}
<div{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</div>
{% endfor %}
</div>
-{% endif %}
+{% endif %} {% endcomment %}
<br />
<div>Ce formulaire vous permet de soumettre une copie <b>non corrigée</b> à la correction en ligne.</div>
{% block answers %}
<div class="course_title">{{ title }}</div>
-{% if messages %}
+{% comment %} {% if messages %}
<div class="messages">
{% for message in messages %}
<div{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</div>
{% endfor %}
</div>
-{% endif %}
+{% endif %} {% endcomment %}
<br />
{% if profile > 0 %}
{% autopaginate pm_messages %}{% paginate %}
</div>
-{% if messages %}
+{% comment %} {% if messages %}
<div class="messages">
{% for message in messages %}
<div{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</div>
{% endfor %}
</div>
-{% endif %}
+{% endif %} {% endcomment %}
<form id="_messageForm" action="{% block pm_form_action %}{% endblock %}" method="post">{% csrf_token %}
<table id="pm_messages" class="listing" >
{% block content %}
- {% if messages %}
- <ul class="messages">
- {% for message in messages %}
- <li{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</li>
- {% endfor %}
- </ul>
- {% endif %}
-
-
-
<div id="booking-confirm" title="Réservation">
<p>
Êtes-vous sûr de vouloir réserver ce créneau ?
{% block courses %}
+
<table class="listing" style="width:100%;margin-top: 1em">
<tr>
<td style="border-bottom:1px solid #6A0307;color:#6A0307;font-size: 120%">{{ course.title }}{% if course.description %} - {{ course.description }}{% endif %}</td>
<div class="desk_center">
-
+<section id="webclass-registering">
+ {% if webclass and not webclass_slot %}
+ Vous n'êtes pas inscrit à la webconférence de cette matière. <a href="{% url teleforma-webclass-appointments webclass.id %}">Cliquez-ici pour choisir un créneau horaire</a>.
+ {% endif %}
+</section>
<br/><br/>
</div>
{% endfor %}
+
+ <div class="course">
+ <div class="course_title">{{ course.title }} - Webclass{% if course.description %} - {{ course.description }}{% endif %}
+ </div>
+ {% block webclass %}
+ TEST
+ {% include "webclass/inc/webclass_list.html" %}
+ {% endblock %}
+ </div>
{% endwith %}
{% endfor %}
</div>
<img src="/static/teleforma/images/play_168.png" width="100%" alt="{% trans 'Click here' %}" />
</div>
{% endthumbnail %}
+ {% else %}
+ {% trans 'Click here' %}
{% endif %}
{% endfor %}
{% else %}
- {% trans 'Click here' %}
+ <div>{% trans 'Click here' %}</div>
{% endif %}
+ <div>{% trans 'Click here' %}</div>
</a>
</td>
<td {% if forloop.first %}class="border-top"{% endif %} width="60%" style="padding-left: 1em;">
</li>
{% endif %}
+ {% if user.professor.count %}
+ <li><a href="{% url teleforma-webclass-professor %}" class="yellow">Webclass</a></li>
+ {% endif %}
+
{% if periods|length == 1 %}
<li><a href="{% url teleforma-exam-scripts-scores-all periods.0.id %}" class="green"> {% trans "Scores" %}</a></li>
{% else %}
{% block postman_menu %}
{% endblock postman_menu %}
+
+ {% if messages %}
+ <ul class="messages">
+ {% for message in messages %}
+ <li{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</li>
+ {% endfor %}
+ </ul>
+ {% endif %}
+
{% block content %}
{% endblock %}
# EXAM
url(r'^', include('teleforma.exam.urls')),
+ # WEBCLASS
+ url(r'^', include('teleforma.webclass.urls')),
# Payment
url(r'^payment/(?P<pk>.*)/start/$', PaymentStartView.as_view(),
from teleforma.models import *
from teleforma.forms import *
from teleforma.models.appointment import AppointmentPeriod
+from teleforma.webclass.models import Webclass, WebclassSlot
from telemeta.views import *
import jqchat.models
from xlwt import Workbook
model = Course
- def get_context_data(self, **kwargs):
+ def get_context_data(self, *args, **kwargs):
context = super(CourseView, self).get_context_data(**kwargs)
course = self.get_object()
courses = []
context['room'] = get_room(name=course.code, period=context['period'].name,
content_type=content_type,
id=course.id)
+
+ # webclass
+ webclass = None
+ webclass_slot = None
+ student = self.request.user.student.all()
+ if student:
+ student = student[0]
+
+ if student:
+ try:
+ webclass = Webclass.objects.get(period=self.period, course=course, iej=student.iej)
+ except Webclass.DoesNotExist:
+ pass
+ if webclass:
+ webclass_slot = webclass.get_slot(self.request.user)
+ context['webclass'] = webclass
+ context['webclass_slot'] = webclass_slot
return context
@method_decorator(login_required)
if not access:
context['access_error'] = access_error
context['message'] = contact_message
+
return context
@method_decorator(login_required)
--- /dev/null
+# -*- coding: utf-8 -*-
+from teleforma.admin import *
+from teleforma.webclass.models import *
+from django.contrib import admin
+
+class BBBServerAdmin(admin.ModelAdmin):
+ model = BBBServer
+ list_display = ('url', 'api_key')
+
+class WebclassSlotInline(admin.StackedInline):
+ model = WebclassSlot
+ raw_id_fields = ('participants',)
+ readonly_fields = ('room_id', 'room_password')
+ extra = 5
+
+class WebclassAdmin(admin.ModelAdmin):
+ inlines = [WebclassSlotInline]
+ list_filter = ('course', 'period', 'iej')
+ list_display = ('course', 'period')
+ filter_horizontal = ('iej',)
+ search_fields = ['id', 'course__code', 'course__title']
+
+class WebclassRecordAdmin(admin.ModelAdmin):
+ list_filter = ('course', 'period')
+ list_display = ('course', 'period')
+ search_fields = ['id', 'course__code', 'course__title']
+
+ # def get_form(self, request, obj=None, **kwargs):
+ # form = super(WebclassRecordAdmin, self).get_form(request, obj, **kwargs)
+ # form.base_fields['url'] = forms.ChoiceField(choices=get_all_records())
+ # return form
+
+
+
+
+admin.site.register(BBBServer, BBBServerAdmin)
+admin.site.register(Webclass, WebclassAdmin)
+admin.site.register(WebclassRecord, WebclassRecordAdmin)
\ No newline at end of file
--- /dev/null
+# -*- 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 model 'BBBServer'
+ db.create_table('teleforma_bbb_server', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('url', self.gf('django.db.models.fields.CharField')(max_length=100)),
+ ('api_key', self.gf('django.db.models.fields.CharField')(max_length=100)),
+ ))
+ db.send_create_signal('webclass', ['BBBServer'])
+
+ # Adding model 'Webclass'
+ db.create_table('teleforma_webclass', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('department', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='webclass', null=True, on_delete=models.SET_NULL, to=orm['teleforma.Department'])),
+ ('period', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='webclass', null=True, on_delete=models.SET_NULL, to=orm['teleforma.Period'])),
+ ('course', self.gf('django.db.models.fields.related.ForeignKey')(related_name='webclass', to=orm['teleforma.Course'])),
+ ('bbb_server', self.gf('django.db.models.fields.related.ForeignKey')(related_name='webclass', to=orm['webclass.BBBServer'])),
+ ('duration', self.gf('telemeta.models.core.DurationField')(default='0', blank=True)),
+ ('max_participants', self.gf('django.db.models.fields.IntegerField')(null=True, blank=True)),
+ ('status', self.gf('django.db.models.fields.IntegerField')(default=2)),
+ ))
+ db.send_create_signal('webclass', ['Webclass'])
+
+ # Adding M2M table for field iej on 'Webclass'
+ m2m_table_name = db.shorten_name('teleforma_webclass_iej')
+ db.create_table(m2m_table_name, (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('webclass', models.ForeignKey(orm['webclass.webclass'], null=False)),
+ ('iej', models.ForeignKey(orm['teleforma.iej'], null=False))
+ ))
+ db.create_unique(m2m_table_name, ['webclass_id', 'iej_id'])
+
+ # Adding model 'WebclassSlot'
+ db.create_table('teleforma_webclass_slot', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('webclass', self.gf('django.db.models.fields.related.ForeignKey')(related_name='slots', to=orm['webclass.Webclass'])),
+ ('day', self.gf('django.db.models.fields.IntegerField')()),
+ ('start_hour', self.gf('django.db.models.fields.TimeField')()),
+ ('professor', self.gf('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', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
+ ('room_password', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
+ ))
+ db.send_create_signal('webclass', ['WebclassSlot'])
+
+ # Adding M2M table for field participants on 'WebclassSlot'
+ m2m_table_name = db.shorten_name('teleforma_webclass_slot_participants')
+ db.create_table(m2m_table_name, (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('webclassslot', models.ForeignKey(orm['webclass.webclassslot'], null=False)),
+ ('user', models.ForeignKey(orm['auth.user'], null=False))
+ ))
+ db.create_unique(m2m_table_name, ['webclassslot_id', 'user_id'])
+
+
+ def backwards(self, orm):
+ # Deleting model 'BBBServer'
+ db.delete_table('teleforma_bbb_server')
+
+ # Deleting model 'Webclass'
+ db.delete_table('teleforma_webclass')
+
+ # Removing M2M table for field iej on 'Webclass'
+ db.delete_table(db.shorten_name('teleforma_webclass_iej'))
+
+ # Deleting model 'WebclassSlot'
+ db.delete_table('teleforma_webclass_slot')
+
+ # Removing M2M table for field participants on 'WebclassSlot'
+ db.delete_table(db.shorten_name('teleforma_webclass_slot_participants'))
+
+
+ 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.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
--- /dev/null
+# -*- 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 model 'WebclassRecord'
+ db.create_table('teleforma_webclass_record', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('period', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['teleforma.Period'])),
+ ('course', self.gf('django.db.models.fields.related.ForeignKey')(related_name='webclass_records', to=orm['teleforma.Course'])),
+ ('url', self.gf('django.db.models.fields.CharField')(max_length=255)),
+ ('created', self.gf('django.db.models.fields.DateTimeField')()),
+ ))
+ db.send_create_signal('webclass', ['WebclassRecord'])
+
+
+ def backwards(self, orm):
+ # Deleting model 'WebclassRecord'
+ db.delete_table('teleforma_webclass_record')
+
+
+ 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'"},
+ 'course': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'webclass_records'", 'to': "orm['teleforma.Course']"}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'period': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['teleforma.Period']"}),
+ 'url': ('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
--- /dev/null
+# -*- coding: utf-8 -*-
+
+import datetime
+from datetime import timedelta
+import calendar
+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
+from django.template.defaultfilters import slugify
+from django.db.models.signals import post_save
+from django.dispatch import receiver
+from bigbluebutton_api_python import BigBlueButton
+from bigbluebutton_api_python.exception import BBBException
+from jxmlease import XMLListNode, XMLDictNode
+
+translation.activate('fr')
+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"
+
+
+class MetaCore:
+ app_label = 'webclass'
+
+def get_all_records():
+ all_records = []
+ recordings = []
+ recordings_xml = self.bbb.get_recordings(self.room_id).get_field('recordings')
+ if hasattr(recordings_xml, 'get'):
+ recordings = recordings_xml['recording']
+ if type(recordings) is XMLDictNode:
+ recordings = [recordings]
+ for recording in recordings:
+ recording.prettyprint()
+ url = recording.get('playback', {}).get('format', {}).get('url')
+ if url:
+ url = url.decode()
+ data = {
+ 'start': int(recording['startTime'].decode()),
+ 'end': int(recording['endTime'].decode()),
+ 'url': url,
+ 'state': recording['state'].decode(),
+ }
+ data['duration'] = data['end'] - data['start']
+ all_records.append(data)
+
+ if not all_records:
+ return []
+
+ all_records = sorted(all_records, key=lambda record:-record['duration'])
+ vocabulary = []
+ for record in all_records:
+ vocabulary.append((record['url'], record['duration']))
+ return longest_record
+
+
+
+class BBBServer(models.Model):
+ url = models.CharField("Url du serveur BBB", max_length=100)
+ api_key = models.CharField("API Key", max_length=100)
+
+ class Meta(MetaCore):
+ db_table = app_label + '_' + 'bbb_server'
+ verbose_name = _('BBB server')
+ verbose_name_plural = _('BBB servers')
+
+ def get_instance(self):
+ return BigBlueButton(self.url, self.api_key)
+
+ def __unicode__(self):
+ return "Serveur %d" % self.id
+
+class Webclass(models.Model):
+
+ department = models.ForeignKey('teleforma.Department', related_name='webclass', verbose_name=_('department'), on_delete=models.SET_NULL, blank=True, null=True)
+ period = models.ForeignKey('teleforma.Period', related_name='webclass', verbose_name=_('period'), on_delete=models.SET_NULL, blank=True, null=True)
+ 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)
+ status = models.IntegerField(_('status'), choices=STATUS_CHOICES, default=2)
+
+
+ class Meta(MetaCore):
+ db_table = app_label + '_' + 'webclass'
+ verbose_name = _('webclass')
+ verbose_name_plural = _('webclass')
+
+ def __unicode__(self):
+ return "Webclass %d : %s" % (self.id, self.course.title)
+
+ def get_slot(self, user):
+ """ return webclass slot or None if user is not subscribed """
+ try:
+ return WebclassSlot.objects.get(webclass=self, participants=user)
+ except WebclassSlot.DoesNotExist:
+ return None
+
+
+class WebclassSlot(models.Model):
+ """ Webclass slot """
+ webclass = models.ForeignKey('Webclass', related_name='slots')
+ day = models.IntegerField('Jour du créneau', choices=DAYS_CHOICES)
+ start_hour = models.TimeField('heure du créneau')
+ professor = models.ForeignKey('teleforma.Professor', related_name='webclass_slot', verbose_name=_('professor'),
+ on_delete=models.SET_NULL, blank=True, null=True)
+ participants = models.ManyToManyField(User, related_name="webclass_slot", verbose_name=_('participants'),
+ blank=True, null=True)
+ 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)
+
+ class Meta(MetaCore):
+ db_table = app_label + '_' + 'webclass_slot'
+ verbose_name = _('webclass slot')
+
+ def __unicode__(self):
+ return "Webclass slot : " + str(self.id)
+
+ @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
+
+ @property
+ def end_hour(self):
+ """
+ start hour + duration
+ """
+ date = datetime.datetime.combine(datetime.date.today(), self.start_hour) + timedelta(seconds=self.webclass.duration.as_seconds())
+ return date.time()
+
+ @property
+ def bbb(self):
+ return self.webclass.bbb_server.get_instance()
+
+ def prepare_webclass(self):
+ """
+ generate room id and moderator password
+ """
+ if not self.room_id:
+ # not sure why, but the slug contains accent
+ room_id = "%s-w%d-s%d" % (unidecode(slugify(self.webclass.course.title)), self.webclass.id, self.id)
+ password = User.objects.make_random_password()
+ self.room_id = room_id
+ self.room_password = password
+ self.save()
+
+ def create_webclass_room(self, request):
+ """ create a BBB room and generate meeting id and moderator password """
+ if self.room_id:
+ try:
+ # check if meeting already exists
+ self.get_webclass_info()
+ except BBBException:
+ year = datetime.datetime.now().year
+ # site_url = 'https://' + request.get_host()
+ webclass = self.webclass
+ params = {
+ 'moderatorPW':self.room_password,
+ 'attendeePW':'pwattendee',
+ # 'maxParticipants':self.webclass_max_participants + 1,
+ 'welcome':"Pré-Barreau : Bienvenue sur la conférence \"%s\"." % (webclass.course.title.encode('utf-8'),),
+ 'record':True,
+ # 'autoStartRecording':True,
+ 'muteOnStart':True,
+ 'allowModsToUnmuteUsers':True,
+ '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",
+ 'bannerColor': "#003768",
+ # 'customStyleUrl': site_url+"/static/teleforma/css/bbb.css"
+ }
+ print params
+ try:
+ result = self.bbb.create_meeting(self.room_id, params=params)
+ except BBBException as e:
+ print(e)
+ raise
+
+
+ def get_join_webclass_url(self, request, user, username=None):
+ """
+ Get url to BBB meeting.
+ If user are professor or staff, provide the url with the moderator password
+ """
+ self.create_webclass_room(request)
+ username = user.get_full_name()
+ is_professor = len(user.professor.all()) >= 1
+ is_staff = user.is_staff or user.is_superuser
+ password = 'pwattendee'
+ if is_professor or is_staff:
+ password = self.room_password
+ params = {'userID': user.username}
+ return self.bbb.get_join_meeting_url(username, self.room_id, password, params)
+
+ def get_fake_join_webclass_url(self, username):
+ """
+ Fake join url for testing purpose
+ Get url to BBB meeting.
+ If user are professor or staff, provide the url with the moderator password
+ """
+ self.create_webclass_room()
+ password = 'pwattendee'
+ params = {'userID': username}
+ return self.bbb.get_join_meeting_url(username, self.room_id, password, params)
+
+ def next_webclass_date(self):
+ """
+ get the next webclass date for this slot
+ (or today webclass if this is the current day)
+ """
+ now = datetime.datetime.now()
+ days_ahead = self.day - now.weekday()
+ if days_ahead < 0:
+ days_ahead += 7
+ next_date = now + datetime.timedelta(days_ahead)
+ next_date.replace(hour=self.start_hour.hour, minute=self.start_hour.minute)
+ return next_date
+
+ @property
+ def status(self):
+ """ is webclass running, about to start, or finished ?
+ state : future, past, almost, ingoing
+ """
+ now = datetime.datetime.now()
+ next_webclass_date_begin = self.next_webclass_date()
+ next_webclass_date_end = next_webclass_date_begin + timedelta(seconds=self.webclass.duration.as_seconds())
+ begin_minus_1_hour = next_webclass_date_begin - timedelta(hours=1)
+ if now < begin_minus_1_hour:
+ # conference not yet started
+ status = "future"
+ elif next_webclass_date_end + timedelta(hours=1) < now:
+ # conference expired
+ status = "past"
+ elif begin_minus_1_hour < now < next_webclass_date_begin:
+ # conference can be joined
+ status = "almost"
+ else:
+ status = "ingoing"
+
+ return status
+
+ def is_webclass_running(self):
+ """ Is webclass currently running ? """
+ # print(self.get_webclass_info())
+ return self.bbb.is_meeting_running(self.room_id).get_field('running').decode() == 'true' or False
+
+ def get_webclass_info(self):
+ """ """
+ print(self.room_id)
+ print(self.bbb.get_meeting_info(self.room_id))
+ return self.bbb.get_meeting_info(self.room_id)
+
+ # def get_record(self):
+ # """ get longest published record for the current conference """
+ # all_records = []
+ # recordings = []
+ # recordings_xml = self.bbb.get_recordings(self.room_id).get_field('recordings')
+ # if hasattr(recordings_xml, 'get'):
+ # recordings = recordings_xml['recording']
+ # if type(recordings) is XMLDictNode:
+ # recordings = [recordings]
+ # for recording in recordings:
+ # recording.prettyprint()
+ # url = recording.get('playback', {}).get('format', {}).get('url')
+ # if url:
+ # url = url.decode()
+ # data = {
+ # 'start': int(recording['startTime'].decode()),
+ # 'end': int(recording['endTime'].decode()),
+ # 'url': url,
+ # 'state': recording['state'].decode(),
+ # }
+ # data['duration'] = data['end'] - data['start']
+ # all_records.append(data)
+
+ # if not all_records:
+ # return None
+ # all_records = sorted(all_records, key=lambda record:-record['duration'])
+
+ # longest_record = all_records[0]
+ # if not longest_record['url'] or longest_record['state'] != 'published':
+ # return None
+ # return longest_record
+
+
+@receiver(post_save, sender=WebclassSlot)
+def create_webclass_room(sender, **kwargs):
+ instance = kwargs['instance']
+ instance.prepare_webclass()
+
+
+
+class WebclassRecord(models.Model):
+
+ period = models.ForeignKey('teleforma.Period', verbose_name=_('period'))
+ course = models.ForeignKey('teleforma.Course', related_name='webclass_records', verbose_name=_('course'))
+ url = models.CharField("Enregistrement BBB", max_length=255)
+ created = models.DateTimeField("Date de la conférence")
+
+ class Meta(MetaCore):
+ db_table = app_label + '_' + 'webclass_record'
+ verbose_name = 'enregistrement'
+ verbose_name_plural = 'enregistrements'
+
+ def __unicode__(self):
+ return "Enregistrement webclass %d" % self.id
--- /dev/null
+{% extends "telemeta/base.html" %}
+{% load teleforma_tags %}
+{% load i18n %}
+
+
+{% block extra_javascript %}
+
+ <script type="text/javascript">
+ var currentForm;
+
+ $(document).ready(function () {
+
+ $("#booking-confirm").dialog({
+ autoOpen: false,
+ resizable: false,
+ modal: true,
+ buttons: {
+ 'Confirmer': function () {
+ currentForm.submit();
+ },
+ "Annuler": function () {
+ $(this).dialog('close');
+ }
+ }
+ });
+
+ $('.booking_form').submit(function () {
+ currentForm = this;
+ // fill dialog with selected date
+ $('#date_placeholder').text($(currentForm).find('.booking_date').text());
+
+ $('#booking-confirm').dialog('open');
+ return false;
+ });
+
+
+ })
+ </script>
+
+{% endblock extra_javascript %}
+
+
+{% block content %}
+
+ <div id="booking-confirm" title="Réservation">
+ <p>
+ Êtes-vous sûr de vouloir réserver ce créneau ? Vous ne pourrez plus modifier votre choix.
+ </p>
+
+ <h2>Créneau horaire</h2>
+ <strong id="date_placeholder"></strong>
+ </div>
+
+
+ {% for slot in slots %}
+ <form class="booking_form" method="POST">
+ <span class="booking_date">{{slot.day}} de {{slot.start_hour|date:"H\hi"}}}} à {{slot.end_hour|date:"H\hi"}}}}</span>
+ {% csrf_token %}
+ <input type="hidden" name="slot_id" value="{{slot.id}}" />
+ <input type="submit" value="Sélectionner" />
+ </form>
+ {% endfor %}
+
+
+{% endblock content %}
--- /dev/null
+{% extends "telemeta/base.html" %}
+{% load i18n %}
+{% load telemeta_utils %}
+{% load teleforma_tags %}
+
+{% block head_title %}Calendrier des Webclass{% endblock %}
+
+{% block title %}
+Calendrier des Webclass
+{% endblock %}
+
+
+{% block infra_javascript %}
+{% endblock infra_javascript %}
+
+{% block content %}
+<div>
+ {% for slot in slots %}
+ <div class="webclass-appointment" style="margin-bottom:10px">
+ <span><strong>{{slot.get_day_display}}</strong> de <strong>{{slot.start_hour|date:"H\hi"}}</strong> à <strong>{{slot.end_hour|date:"H\hi"}}</strong></span> : {{slot.participants.count}} participant{{slot.participants.count|pluralize}}
+ <a href="{% url teleforma-webclass-join slot.id %}" target="_blank" class="conference-big-button component_icon button icon_next">Rejoindre la conférence</a>
+ </div>
+ {% empty %}
+ <p>Aucune webclasse programmée.</p>
+ {% endfor %}
+</div>
+{% endblock content %}
--- /dev/null
+{% load teleforma_tags %}
+{% load i18n %}
+
+{% if webclass_slot %}
+<div class="course_content content_video">
+<div class="course_subtitle">
+ <h3><img src="/static/telemeta/images/item_title.png" width="10px" alt="" /> Webclass live</h3>
+</div>
+ <table class="listing" width="100%">
+ <tbody>
+ {% if webclass_slot %}
+ <tr>
+ <td>
+ <p>Vous êtes inscrit pour les webconférence du
+ <strong>{{webclass_slot.get_day_display}} de
+ {{webclass_slot.start_hour|date:"H\hi"}} à
+ {{webclass_slot.end_hour|date:"H\hi"}}</strong>
+ avec le professeur <strong>{{webclass_slot.professor.user.last_name}}</strong>.</p>
+ {{webclass_slot.status}}
+ {% if webclass_slot.status == 'past' %}
+ <p>La webconférence est terminée.</p>
+ {% elif webclass_slot.status == 'ingoing' %}
+ <p>La webconférence est en cours.</p>
+ <a href="{% url teleforma-webclass-join webclass_slot.id %}" target="_blank" class="conference-big-button component_icon button icon_next">Cliquez ici pour rejoindre la conférence</a>
+ {% elif webclass_slot.status == 'almost' %}
+ <p>Le webconférence est accessible mais elle n'a pas encore démarré.</p>
+ <a href="{% url teleforma-webclass-join webclass_slot.id %}" target="_blank" class="conference-big-button component_icon button icon_next">Cliquez ici pour rejoindre la conférence</a>
+ {% endif %}
+ </td>
+ </tr>
+ {% endif %}
+
+ {% for conference in conferences|from_period:period %}
+ {% for stream in conference.livestream.all %}
+ {% if stream.stream_type == 'webm' %}
+ <tr>
+ {% if stream.streaming %}
+ <td {% if forloop.first %}class="border-top"{% endif %} width="230px">
+ <a href="{% url teleforma-conference-detail period.id stream.conference.id %}" title="{% trans "View" %}">
+ <img id="snapshot-{{ stream.course.code }}-{{ stream.course_type }}" src="{{ stream.snapshot_url }}" width="100%" alt="{% trans 'Click here' %}" />
+ </a>
+ </td>
+ <td {% if forloop.first %}class="border-top"{% endif %} width="60%" style="padding-left: 1em;">
+ <div>
+ <dl class="listing" style="font-size: 1.2em;">
+ <dt>{% trans "Title" %}</dt><dd>{{ stream.conference.course.title }}</dd>
+ <dt>{% trans "Session" %}</dt><dd>{{ stream.conference.session }}</dd>
+ {% if stream.conference.professor.user.username %}
+ <dt>{% trans "Professor" %}</dt><dd><a href="{% url telemeta-profile-detail stream.conference.professor.user.username %}" target="_blank">{{ stream.conference.professor }}</a></dd>
+ {% endif %}
+ <dt>{% trans "Begin" %}</dt><dd>{{ stream.conference.date_begin }}</dd>
+ </dl>
+ </div>
+ </td>
+ <td {% if forloop.first %}class="border-top"{% endif %} width="10%" align="center">
+ {% if stream.streaming %}
+ <img src="/static/teleforma/images/network-wireless.png" style="vertical-align:middle" title="streaming" />
+ <img src="/static/telemeta/images/media-record.png" style="vertical-align:middle" title="recording" />
+ {% endif %}
+ </td>
+ {% else %}
+ <div style="padding-left: 1em;">
+
+ </div>
+ {% endif %}
+ </tr>
+ {% endif %}
+ {% endfor %}
+ {% endfor %}
+ </tbody>
+ </table>
+</div>
+{% endif %}
--- /dev/null
+# -*- coding: utf-8 -*-
+# Copyright (c) 2007-2012 Parisson SARL
+
+# This software is a computer program whose purpose is to backup, analyse,
+# transcode and stream any audio content with its metadata over a web frontend.
+
+# This software is governed by the CeCILL license under French law and
+# abiding by the rules of distribution of free software. You can use,
+# modify and/ or redistribute the software under the terms of the CeCILL
+# license as circulated by CEA, CNRS and INRIA at the following URL
+# "http://www.cecill.info".
+
+# As a counterpart to the access to the source code and rights to copy,
+# modify and redistribute granted by the license, users are provided only
+# with a limited warranty and the software's author, the holder of the
+# economic rights, and the successive licensors have only limited
+# liability.
+
+# In this respect, the user's attention is drawn to the risks associated
+# with loading, using, modifying and/or developing or reproducing the
+# software by the user in light of its specific status of free software,
+# that may mean that it is complicated to manipulate, and that also
+# therefore means that it is reserved for developers and experienced
+# professionals having in-depth computer knowledge. Users are therefore
+# encouraged to load and test the software's suitability as regards their
+# requirements in conditions enabling the security of their systems and/or
+# data to be ensured and, more generally, to use and operate it in the
+# same conditions as regards security.
+
+# The fact that you are presently reading this means that you have had
+# knowledge of the CeCILL license and that you accept its terms.
+#
+# Authors: Guillaume Pellerin <yomguy@parisson.com>
+
+from django.conf.urls import patterns, url, include
+from django.http import HttpResponse
+from teleforma.webclass.views import *
+
+
+urlpatterns = patterns('',
+ url(r'^desk/webclass_appointments/(?P<pk>.*)$', WebclassAppointment.as_view(),
+ name="teleforma-webclass-appointments"),
+ url(r'^desk/webclass_calendar/$', WebclassProfessorAppointments.as_view(), name="teleforma-webclass-professor"),
+ url(r'^desk/webclass/(?P<pk>.*)/join/$',
+ join_webclass,
+ name="teleforma-webclass-join"),
+
+)
--- /dev/null
+# -*- coding: utf-8 -*-
+
+from django.views.generic import View, TemplateView
+from django.contrib import messages
+from django.http import HttpResponse
+from django.shortcuts import redirect, get_object_or_404, render
+from django.template.loader import render_to_string
+from django.http import HttpResponse, HttpResponseRedirect
+from django.core.urlresolvers import reverse, reverse_lazy
+from django.db import IntegrityError
+from django.core.mail import send_mail
+from django.conf import settings
+from django.core.cache import cache
+
+from teleforma.webclass.models import Webclass, WebclassSlot
+
+from teleforma.views.core import get_periods, get_courses
+
+
+class WebclassProfessorAppointments(TemplateView):
+ template_name = 'webclass/appointments_professor.html'
+
+ def get_context_data(self, **kwargs):
+ """ """
+ context = super(WebclassProfessorAppointments, self).get_context_data(**kwargs)
+
+ 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')
+ print(context['slots'])
+ return context
+
+class WebclassAppointment(View):
+ template_name = 'webclass/appointments.html'
+
+ def check_rights(self, user, webclass):
+ if not user.is_authenticated():
+ return HttpResponseRedirect(reverse('teleforma-login'))
+ student = user.student.all().count()
+ if not student:
+ return HttpResponse('Unauthorized', status=401)
+ # check period
+ period_id = webclass.period.id
+ periods = [ p for p in get_periods(user) if int(p.id) == period_id ]
+ if not periods:
+ return HttpResponse('Unauthorized', status=401)
+ # check courses
+ course_id = webclass.course.id
+ courses = [ c for c in get_courses(user) if int(c['course'].id) == course_id ]
+ if not courses:
+ return HttpResponse('Unauthorized', status=401)
+ # Student is in the right IEJ ?
+ if not student.iej in webclass.iej.all():
+ return HttpResponse('Unauthorized', status=401)
+ return
+
+ def render(self, request, webclass):
+ # Ensure user is logged in, a student, and has access to current period
+ 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})
+
+ def check_slot_validity(self, user, slot):
+ """
+ Check if we can register to this exact slot
+ """
+ student = user.student.all()[0]
+
+ # Check if there is still space for one student
+ if not slot.participant_slot_available:
+ return u"Ce créneau n'est plus disponible."
+
+ # # Check we don't have another appointment on this period
+ if webclass.get_slot(user):
+ return u"Vous êtes déjà inscrit."
+
+ def post(self, request, pk):
+ webclass = get_object_or_404(Webclass, id = pk)
+ rights = self.check_rights(request.user, webclass)
+ if rights:
+ return rights
+
+ user = request.user
+ slot_id = int(request.POST.get('slot_id'))
+ slot = WebclassSlot.objects.get(pk=slot_id)
+
+ msg = self.check_slot_validity(user, slot)
+
+ if not msg:
+ slot.participants.add(user)
+ slot.save()
+ # self.send_ap_mail(ap)
+ messages.add_message(request, messages.INFO, "Votre réservation a bien été prise en compte.")
+ return HttpResponseRedirect(reverse('teleforma-desk-period-course', kwargs={'period_id':webclass.period.id, 'pk':webclass.course.id}))
+ else:
+ messages.add_message(request, messages.ERROR, msg)
+ return self.render(request, webclass)
+
+ def get(self, request, pk):
+ webclass = get_object_or_404(Webclass, id = pk)
+ rights = self.check_rights(request.user, webclass)
+ if rights:
+ return rights
+ return self.render(request, webclass)
+
+ # def send_ap_mail(self, ap):
+ # """
+ # Send the confirm mail to student
+ # """
+ # data = { 'mfrom': settings.DEFAULT_FROM_EMAIL,
+ # 'mto': ap.student.email,
+ # 'jury_address': ap.jury.address,
+ # 'date': ap.real_date,
+ # 'student': ap.student,
+ # 'main_text': ap.appointment_period.appointment_mail_text }
+ # # DEBUG
+ # # data['mto'] = "yoanl@pilotsystems.net"
+ # # data['mto'] = "dorothee.lavalle@pre-barreau.com"
+ # # data['mto'] = "gael@pilotsystems.net"
+
+ # subject_template = 'teleforma/messages/email_appointment_sujet.txt'
+ # message_template = 'teleforma/messages/email_appointment.txt'
+ # subject = render_to_string(subject_template, data)
+ # subject = ''.join(subject.splitlines())
+ # message = render_to_string(message_template, data)
+ # send_mail(subject, message, data['mfrom'], [ data['mto'] ],
+ # fail_silently=False)
+ # return data
+
+
+
+def join_webclass(request, pk):
+ webclass_slot = WebclassSlot.objects.get(pk=int(pk))
+ # webclass = webclass_slot.webclass
+ # fake debug links
+ # username = request.GET.get('username')
+ # if username:
+ # return redirect(webclass.get_fake_join_webclass_url(request, username))
+ user = request.user
+ authorized = False
+
+ # staff or professor ?
+ is_professor = len(user.professor.all()) >= 1
+ is_staff = user.is_staff or user.is_superuser
+ if is_professor or is_staff:
+ authorized = True
+
+ # student registered ?
+ if not authorized:
+ if user in webclass_slot.participants.all():
+ authorized = True
+
+ if authorized:
+ return redirect(webclass_slot.get_join_webclass_url(request, user))
+ else:
+ return HttpResponse('Unauthorized', status=401)
\ No newline at end of file