]> git.parisson.com Git - teleforma.git/commitdiff
WIP
authorYoan Le Clanche <yoanl@pilotsystems.net>
Thu, 4 Jun 2020 13:59:20 +0000 (15:59 +0200)
committerYoan Le Clanche <yoanl@pilotsystems.net>
Thu, 4 Jun 2020 13:59:20 +0000 (15:59 +0200)
24 files changed:
teleforma/exam/templates/exam/mass_score_form.html
teleforma/exam/templates/exam/score_form.html
teleforma/exam/templates/exam/scores.html
teleforma/exam/templates/exam/script_form.html
teleforma/exam/templates/exam/scripts.html
teleforma/templates/postman/base_folder.html
teleforma/templates/teleforma/appointments.html
teleforma/templates/teleforma/course.html
teleforma/templates/teleforma/course_detail.html
teleforma/templates/teleforma/inc/media_list.html
teleforma/templates/telemeta/base.html
teleforma/urls.py
teleforma/views/core.py
teleforma/webclass/__init__.py [new file with mode: 0644]
teleforma/webclass/admin.py [new file with mode: 0644]
teleforma/webclass/migrations/0001_initial.py [new file with mode: 0644]
teleforma/webclass/migrations/0002_auto__add_webclassrecord.py [new file with mode: 0644]
teleforma/webclass/migrations/__init__.py [new file with mode: 0644]
teleforma/webclass/models.py [new file with mode: 0644]
teleforma/webclass/templates/webclass/appointments.html [new file with mode: 0644]
teleforma/webclass/templates/webclass/appointments_professor.html [new file with mode: 0644]
teleforma/webclass/templates/webclass/inc/webclass_list.html [new file with mode: 0644]
teleforma/webclass/urls.py [new file with mode: 0644]
teleforma/webclass/views.py [new file with mode: 0644]

index a05bfbca9837c4a9a05c60a29be1fa25559aba6b..de9d4fcf0a20aa56412524b861ca324c521134dc 100644 (file)
 </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 %}
index c47faf47481c8519fb752ef2fabcefe2a74977c4..6ba49a26f64b1bcda90dde4c7df4a5dcad30feb0 100644 (file)
 </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>
index cc82af1dccf4e797587ee781647e4d66f76f8c70..7953c62fb607c65b952790dcbea79e3a95b12cef 100644 (file)
 </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%' %}
index e038a7016948ce2f3cb9beafaec83f29cff475eb..3f37aad3614ccdc7dedcb4ad0aad476bad719a30 100644 (file)
 </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>
index 7bae5cef1700df0d9d9e2b425c2fd21689e246d0..1b6182520d725fa6d1827a7bdf3447b8ee51b895 100644 (file)
 {% 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 %}
index 1fd48140fe60f8cf593f08fc72f22be2f1c6ee97..7bd38b86b6dc719263984049cab204d163df3f4b 100644 (file)
 {% 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" >
index 1231ad44d49a0fc8e83287fd752e9b6843dbe7c7..664d10e55a1273eb7cc5e5a61d0664d3f14be207 100644 (file)
 
 {% 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 ?
index 81d940f322967b8a5d1bb215b543869be0b37fae..aa1643e2b71d7d9748e7a7b29cf08c1caeb886f8 100644 (file)
@@ -3,6 +3,7 @@
 
 {% 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>
index af1ba6e57526c3ebfa1af2b75966739f23b35aae..9e8d65e624ff80b05b952a87beac52ff8180c8b4 100644 (file)
@@ -22,7 +22,11 @@ $(document).ready(function(){
 
 <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/>
@@ -69,6 +73,15 @@ $(document).ready(function(){
 
         </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>
index 346bcfd53ec37353380c50df596e9e4c9e5589c5..76c2de5ff709878af08c0921fbd5b7907234474f 100644 (file)
                  <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;">
index 003543452f4d0556d7e62158169a70fe02f1bd58..9c1a37102e2cc2dab730b13fc4af94633606b01b 100644 (file)
@@ -131,6 +131,10 @@ alt="logo" />
     </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">&nbsp;{% trans "Scores" %}</a></li>
   {% else %}
@@ -187,6 +191,15 @@ alt="logo" />
     {% 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 %}
 
index eb0ea5a6e43fdf15f22f78839aa7db987c98e947..e2d00d4d899cadb8362a7caf89351454cb804be5 100644 (file)
@@ -156,6 +156,8 @@ urlpatterns = patterns('',
     # EXAM
     url(r'^', include('teleforma.exam.urls')),
                        
+    # WEBCLASS
+    url(r'^', include('teleforma.webclass.urls')),
 
     # Payment
     url(r'^payment/(?P<pk>.*)/start/$', PaymentStartView.as_view(),
index 0d661b742c98a805c454de0871e0dec3e66c1eeb..f6be6f167b9454b6e32d61cb0d2e539561f5a53e 100644 (file)
@@ -73,6 +73,7 @@ from jsonrpc.proxy import ServiceProxy
 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
@@ -398,7 +399,7 @@ class CourseView(CourseAccessMixin, DetailView):
 
     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 = []
@@ -411,6 +412,23 @@ class CourseView(CourseAccessMixin, DetailView):
         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)
@@ -469,6 +487,7 @@ class MediaView(CourseAccessMixin, DetailView):
         if not access:
             context['access_error'] = access_error
             context['message'] = contact_message
+
         return context
 
     @method_decorator(login_required)
diff --git a/teleforma/webclass/__init__.py b/teleforma/webclass/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/teleforma/webclass/admin.py b/teleforma/webclass/admin.py
new file mode 100644 (file)
index 0000000..adaa339
--- /dev/null
@@ -0,0 +1,38 @@
+# -*- 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
diff --git a/teleforma/webclass/migrations/0001_initial.py b/teleforma/webclass/migrations/0001_initial.py
new file mode 100644 (file)
index 0000000..48b2a4d
--- /dev/null
@@ -0,0 +1,240 @@
+# -*- 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
diff --git a/teleforma/webclass/migrations/0002_auto__add_webclassrecord.py b/teleforma/webclass/migrations/0002_auto__add_webclassrecord.py
new file mode 100644 (file)
index 0000000..45114df
--- /dev/null
@@ -0,0 +1,195 @@
+# -*- 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
diff --git a/teleforma/webclass/migrations/__init__.py b/teleforma/webclass/migrations/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/teleforma/webclass/models.py b/teleforma/webclass/models.py
new file mode 100644 (file)
index 0000000..62de763
--- /dev/null
@@ -0,0 +1,320 @@
+# -*- 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
diff --git a/teleforma/webclass/templates/webclass/appointments.html b/teleforma/webclass/templates/webclass/appointments.html
new file mode 100644 (file)
index 0000000..de9b8b9
--- /dev/null
@@ -0,0 +1,65 @@
+{% 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 %}
diff --git a/teleforma/webclass/templates/webclass/appointments_professor.html b/teleforma/webclass/templates/webclass/appointments_professor.html
new file mode 100644 (file)
index 0000000..7dbe561
--- /dev/null
@@ -0,0 +1,27 @@
+{% 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 %}
diff --git a/teleforma/webclass/templates/webclass/inc/webclass_list.html b/teleforma/webclass/templates/webclass/inc/webclass_list.html
new file mode 100644 (file)
index 0000000..e5aba91
--- /dev/null
@@ -0,0 +1,73 @@
+{% 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 %}
diff --git a/teleforma/webclass/urls.py b/teleforma/webclass/urls.py
new file mode 100644 (file)
index 0000000..29bb9b2
--- /dev/null
@@ -0,0 +1,48 @@
+# -*- 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"),
+
+)
diff --git a/teleforma/webclass/views.py b/teleforma/webclass/views.py
new file mode 100644 (file)
index 0000000..9b10005
--- /dev/null
@@ -0,0 +1,169 @@
+# -*- 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