From fc925fa54393110ab270e9af2d35526e08c92625 Mon Sep 17 00:00:00 2001
From: Yoan Le Clanche
Date: Tue, 2 May 2023 12:09:48 +0200
Subject: [PATCH] Django 3 migration
---
app/settings.py | 42 ++++++---
app/urls.py | 1 +
etc/apache2-dev.conf | 3 -
requirements-dev.txt | 2 +-
requirements.txt | 21 ++++-
teleforma/admin.py | 43 +++++----
.../0001_initial.py | 0
...02_auto__chg_field_script_reject_reason.py | 0
..._script_file__chg_field_scriptpage_file.py | 0
.../0004_auto__add_field_quota_script_type.py | 0
...chg_field_quota_course__chg_field_quota.py | 0
...me_type__chg_field_scriptpage_mime_type.py | 0
.../__init__.py | 0
teleforma/forms.py | 31 ++++---
.../teleforma-convert-boolean-fields.py | 18 ++++
teleforma/migrations/0001_initial.py | 91 ++++---------------
teleforma/models/__init__.py | 1 -
teleforma/models/ae.py | 65 -------------
teleforma/models/core.py | 19 ++--
teleforma/models/crfpa.py | 34 +++----
teleforma/models/pro.py | 2 +-
teleforma/templates/postman/base_write.html | 2 +-
.../registration/registration_complete.html | 2 +-
teleforma/templates/teleforma/annals.html | 8 +-
.../templates/teleforma/appointments.html | 2 +-
.../teleforma/course_conference.html | 8 +-
.../teleforma/course_conference_audio.html | 4 +-
.../templates/teleforma/course_detail.html | 2 +-
.../templates/teleforma/course_document.html | 2 +-
.../templates/teleforma/course_media.html | 9 +-
.../teleforma/course_media_transcoded.html | 1 -
.../teleforma/course_media_video_embed.html | 2 +-
teleforma/templates/teleforma/courses.html | 10 +-
.../templates/teleforma/courses_pending.html | 2 +-
.../teleforma/inc/conference_list.html | 4 +-
.../teleforma/inc/document_list.html | 4 +-
.../templates/teleforma/inc/media_list.html | 2 +-
.../templates/teleforma/inc/newsitem.html | 4 +-
.../teleforma/inc/newsitems_portlet.html | 4 +-
.../templates/teleforma/inc/user_list.html | 4 +-
.../templates/teleforma/newsitem_list.html | 4 +-
.../templates/teleforma/profile_detail.html | 2 +-
.../templates/teleforma/search_criteria.html | 4 +-
teleforma/templates/teleforma/users.html | 8 +-
teleforma/templates/telemeta/base.html | 4 +-
teleforma/templatetags/teleforma_tags.py | 4 +-
teleforma/urls.py | 7 --
teleforma/views/core.py | 66 ++++++--------
teleforma/webclass/migrations/0001_initial.py | 2 +-
49 files changed, 229 insertions(+), 321 deletions(-)
rename teleforma/exam/{migrations => south_migrations}/0001_initial.py (100%)
rename teleforma/exam/{migrations => south_migrations}/0002_auto__chg_field_script_reject_reason.py (100%)
rename teleforma/exam/{migrations => south_migrations}/0003_auto__chg_field_script_file__chg_field_scriptpage_file.py (100%)
rename teleforma/exam/{migrations => south_migrations}/0004_auto__add_field_quota_script_type.py (100%)
rename teleforma/exam/{migrations => south_migrations}/0005_auto__add_field_quota_session__chg_field_quota_course__chg_field_quota.py (100%)
rename teleforma/exam/{migrations => south_migrations}/0006_auto__chg_field_script_mime_type__chg_field_scriptpage_mime_type.py (100%)
rename teleforma/exam/{migrations => south_migrations}/__init__.py (100%)
create mode 100644 teleforma/management/commands/teleforma-convert-boolean-fields.py
delete mode 100644 teleforma/models/ae.py
diff --git a/app/settings.py b/app/settings.py
index 76100369..72c1387e 100644
--- a/app/settings.py
+++ b/app/settings.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
# Django settings for sandbox project.
-from django.utils.encoding import force_text
+from django.utils.encoding import force_str
import warnings
import os
import sys
@@ -30,12 +30,18 @@ MANAGERS = ADMINS
DATABASES = {
'default': {
- 'ENGINE': 'django.db.backends.mysql', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
- 'NAME': 'teleforma', # Or path to database file if using sqlite3.
- 'USER': 'teleforma', # Not used with sqlite3.
- 'PASSWORD': 'HMYsrZLEtYeBrvER', # Not used with sqlite3.
- 'HOST': '', # Set to empty string for localhost. Not used with sqlite3.
- 'PORT': '', # Set to empty string for default. Not used with sqlite3.
+ # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
+ 'ENGINE': 'django.db.backends.postgresql_psycopg2',
+ # Or path to database file if using sqlite3.
+ 'NAME': os.environ.get('POSTGRES_DATABASE'),
+ # Not used with sqlite3.
+ 'USER': os.environ.get('POSTGRES_USER'),
+ # Not used with sqlite3.
+ 'PASSWORD': os.environ.get('POSTGRES_PASSWORD'),
+ # Set to empty string for localhost. Not used with sqlite3.
+ 'HOST': os.environ.get('DB_HOST'),
+ # Set to empty string for default. Not used with sqlite3.
+ 'PORT': os.environ.get('DB_PORT'),
}
}
@@ -140,6 +146,8 @@ INSTALLED_APPS = (
'django.contrib.admin',
# 'south',
'teleforma',
+ 'teleforma.exam',
+ 'teleforma.webclass',
'sorl.thumbnail',
# 'django_extensions',
'dj_pagination',
@@ -241,17 +249,25 @@ TELECASTER_LIVE_ICECAST_STREAMING_PORT = 8000
TELECASTER_LIVE_STREAM_M_STREAMING_PORT = 8888
# CRFPA or AE or PRO
-TELEFORMA_E_LEARNING_TYPE = 'CRFPA'
+TELEFORMA_E_LEARNING_TYPE = 'AE'
+TELEFORMA_ORGANIZATION = 'Pré-Barreau - Avocats étrangers'
+TELEFORMA_SUBJECTS = ('Barreau', 'Avocats', 'étrangers', 'e-learning')
+TELEFORMA_DESCRIPTION = "E-learning Pré-Barreau - Avocats étrangers"
TELEFORMA_GLOBAL_TWEETER = False
TELEFORMA_PERIOD_TWEETER = True
TELEFORMA_EXAM_TOPIC_DEFAULT_DOC_TYPE_ID = 4
TELEFORMA_EXAM_SCRIPT_UPLOAD = True
TELEFORMA_REGISTER_DEFAULT_DOC_ID = 5506
-TELEFORMA_PERIOD_DEFAULT_ID = 22
-TELEFORMA_EXAM_MAX_SESSIONS = 15
+TELEFORMA_PERIOD_DEFAULT_ID = 11
+TELEFORMA_EXAM_MAX_SESSIONS = 10
TELEFORMA_EXAM_SCRIPT_MAX_SIZE = 20480000
TELEFORMA_EXAM_SCRIPT_SERVICE_URL = '/webviewer/teleforma.html'
+# courses choice in register form
+TELEFORMA_REGISTER_COURSE_CHOICE = (28, 29, 30, 31)
+# courses added to all registered users
+TELEFORMA_REGISTER_COURSE_AUTOREGISTER = (27, 34, 35, 36, 37)
+
TELECASTER_LIVE_STREAMING_SERVER = 'stream4.parisson.com'
TELECASTER_LIVE_STREAMING_PORT = 443
TELECASTER_LIVE_ICECAST_STREAMING_PORT = 8000
@@ -269,6 +285,10 @@ FILE_UPLOAD_TEMP_DIR = '/tmp'
SESSION_ENGINE = "unique_session.backends.session_backend"
UNIQUE_SESSION_WHITELIST = (1, 2042)
+RECAPTCHA_PUBLIC_KEY = '6Ldq5DgbAAAAADkKg19JXlhx6F1XUQDsrXfXqSP6'
+RECAPTCHA_PRIVATE_KEY = '6Ldq5DgbAAAAAOVDOeF2kH8i2e2VSNHpqlinbpAJ'
+RECAPTCHA_REQUIRED_SCORE = 0.85
+
# SOUTH_MIGRATION_MODULES = {
# 'captcha': 'captcha.south_migrations',
# }
@@ -345,7 +365,7 @@ def show_user_as(user):
if user.quotas.count() and not professor and not user.is_superuser:
return "#"+str(user.id)
else:
- return force_text(user)
+ return force_str(user)
POSTMAN_SHOW_USER_AS = show_user_as
diff --git a/app/urls.py b/app/urls.py
index 355c941c..84b68022 100644
--- a/app/urls.py
+++ b/app/urls.py
@@ -1,4 +1,5 @@
# -*- coding: utf-8 -*-
+from django.conf import settings
from django.http import HttpResponse
from django.conf.urls import include, url
from django.views.i18n import JavaScriptCatalog
diff --git a/etc/apache2-dev.conf b/etc/apache2-dev.conf
index 87917d8d..d47c932d 100644
--- a/etc/apache2-dev.conf
+++ b/etc/apache2-dev.conf
@@ -551,8 +551,5 @@ SSLRandomSeed connect builtin
RewriteEngine on
ProxyPreserveHost on
- RewriteCond %{HTTP:Connection} Upgrade [NC]
- RewriteCond %{HTTP:Upgrade} websocket [NC]
- RewriteRule ^/(.*)$ ws://channels:8000/$1 [P,L]
RewriteRule ^(.*)$ http://app:8000$1 [P,L]
diff --git a/requirements-dev.txt b/requirements-dev.txt
index d1cc50e1..3a22361c 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -1,2 +1,2 @@
ipdb==0.13.8
-django-debug-toolbar
\ No newline at end of file
+django-debug-toolbar==3.8.1
\ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
index da76a6c2..e802cc87 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,6 +1,9 @@
bigbluebutton-api-python==0.0.11
+channels==3.0.4
+channels_redis==3.4.0
docutils==0.17.1
-Django==3.2.3
+Django==3.2.13
+djangorestframework==3.13.1
# django-extensions==1.2.1
# django-timezones==0.2
# django-registration==3.1.2
@@ -10,17 +13,27 @@ django-json-rpc==0.7.1
# django-google-tools==1.1.0
django-nvd3==0.8.2
django-postman==4.2
-django-simple-captcha==0.5.14 # a maj
django-tinymce==3.3.0
-e git+https://git.parisson.com/git/django-unique-session.git@master#egg=django-unique-session
django-user-agents==0.4.0
+django-recaptcha==2.0.6
jxmlease==1.0.3
mysqlclient==2.0.3
numpy==1.20.3
# django-user-agents==0.3.0
# html5lib==1.1
+requests
sorl-thumbnail==12.7.0
unidecode==1.2.0
-xhtml2pdf==0.2.5
+weasyprint==52.5
xlrd==2.0.1
-xlwt==1.3.0
\ No newline at end of file
+xlwt==1.3.0
+psycopg2==2.8.6
+redis==3.5.3
+uwsgi==2.0.19
+daphne==3.0.2
+pymemcache==3.4.4
+django-debug-toolbar==3.2.1
+uvicorn[standard]==0.18.1
+httpx==0.23.3
+ipython
diff --git a/teleforma/admin.py b/teleforma/admin.py
index b133ad86..ec05e40f 100644
--- a/teleforma/admin.py
+++ b/teleforma/admin.py
@@ -2,6 +2,7 @@
import csv
import datetime
+from django.conf import settings
from django.contrib import admin
from django.contrib.admin import SimpleListFilter
from django.contrib.auth.admin import UserAdmin
@@ -9,6 +10,9 @@ from django.contrib.auth.models import User
from django.core import serializers
from django.http import HttpResponse
from django.utils.translation import ugettext_lazy as _
+from django import forms
+from django.contrib.admin.helpers import ActionForm
+
from .exam.admin import QuotaInline
from .models.appointment import (Appointment, AppointmentJury,
@@ -81,9 +85,8 @@ class StudentInline(admin.StackedInline):
model = Student
extra = 1
-# TODO fix max_length
-# class StudentGroupForm(ActionForm):
-# group_name = forms.CharField(_('Group'), required=False)
+class StudentGroupForm(ActionForm):
+ group_name = forms.CharField(label=_('Group'), max_length=255, required=False)
class StudentGroupAdmin(admin.ModelAdmin):
@@ -189,8 +192,8 @@ else:
actions = ['export_xls', 'write_message', 'add_to_group']
action_form = StudentGroupForm
- def get_trainings(self, instance):
- return ' - '.join([str(training) for training in instance.trainings.all()])
+ def get_trainings(self, instance):
+ return ' - '.join([str(training) for training in instance.trainings.all()])
def student_name(self, instance):
return instance.user.last_name + ' ' + instance.user.first_name
@@ -205,23 +208,23 @@ else:
serializers.serialize("json", queryset, stream=response)
return response
- def export_xls(self, request, queryset):
- book = UserXLSBook(students=queryset)
- book.write()
- response = HttpResponse(content_type="application/vnd.ms-excel")
- response['Content-Disposition'] = 'attachment; filename=users.xls'
- book.book.save(response)
- return response
+ def export_xls(self, request, queryset):
+ book = UserXLSBook(students=queryset)
+ book.write()
+ response = HttpResponse(content_type="application/vnd.ms-excel")
+ response['Content-Disposition'] = 'attachment; filename=users.xls'
+ book.book.save(response)
+ return response
- export_xls.short_description = "Export vers XLS"
+ export_xls.short_description = "Export vers XLS"
- def add_to_group(self, request, queryset):
- group_name = request.POST['group_name']
- group, c = StudentGroup.objects.get_or_create(name=group_name)
- for student in queryset:
- group.students.add(student)
- # self.message_user(request, ("Successfully added to group : %s") % (group_name,), messages.SUCCESS)
- add_to_group.short_description = "Ajouter au groupe"
+ def add_to_group(self, request, queryset):
+ group_name = request.POST['group_name']
+ group, c = StudentGroup.objects.get_or_create(name=group_name)
+ for student in queryset:
+ group.students.add(student)
+ # self.message_user(request, ("Successfully added to group : %s") % (group_name,), messages.SUCCESS)
+ add_to_group.short_description = "Ajouter au groupe"
class CorrectorAdmin(admin.ModelAdmin):
diff --git a/teleforma/exam/migrations/0001_initial.py b/teleforma/exam/south_migrations/0001_initial.py
similarity index 100%
rename from teleforma/exam/migrations/0001_initial.py
rename to teleforma/exam/south_migrations/0001_initial.py
diff --git a/teleforma/exam/migrations/0002_auto__chg_field_script_reject_reason.py b/teleforma/exam/south_migrations/0002_auto__chg_field_script_reject_reason.py
similarity index 100%
rename from teleforma/exam/migrations/0002_auto__chg_field_script_reject_reason.py
rename to teleforma/exam/south_migrations/0002_auto__chg_field_script_reject_reason.py
diff --git a/teleforma/exam/migrations/0003_auto__chg_field_script_file__chg_field_scriptpage_file.py b/teleforma/exam/south_migrations/0003_auto__chg_field_script_file__chg_field_scriptpage_file.py
similarity index 100%
rename from teleforma/exam/migrations/0003_auto__chg_field_script_file__chg_field_scriptpage_file.py
rename to teleforma/exam/south_migrations/0003_auto__chg_field_script_file__chg_field_scriptpage_file.py
diff --git a/teleforma/exam/migrations/0004_auto__add_field_quota_script_type.py b/teleforma/exam/south_migrations/0004_auto__add_field_quota_script_type.py
similarity index 100%
rename from teleforma/exam/migrations/0004_auto__add_field_quota_script_type.py
rename to teleforma/exam/south_migrations/0004_auto__add_field_quota_script_type.py
diff --git a/teleforma/exam/migrations/0005_auto__add_field_quota_session__chg_field_quota_course__chg_field_quota.py b/teleforma/exam/south_migrations/0005_auto__add_field_quota_session__chg_field_quota_course__chg_field_quota.py
similarity index 100%
rename from teleforma/exam/migrations/0005_auto__add_field_quota_session__chg_field_quota_course__chg_field_quota.py
rename to teleforma/exam/south_migrations/0005_auto__add_field_quota_session__chg_field_quota_course__chg_field_quota.py
diff --git a/teleforma/exam/migrations/0006_auto__chg_field_script_mime_type__chg_field_scriptpage_mime_type.py b/teleforma/exam/south_migrations/0006_auto__chg_field_script_mime_type__chg_field_scriptpage_mime_type.py
similarity index 100%
rename from teleforma/exam/migrations/0006_auto__chg_field_script_mime_type__chg_field_scriptpage_mime_type.py
rename to teleforma/exam/south_migrations/0006_auto__chg_field_script_mime_type__chg_field_scriptpage_mime_type.py
diff --git a/teleforma/exam/migrations/__init__.py b/teleforma/exam/south_migrations/__init__.py
similarity index 100%
rename from teleforma/exam/migrations/__init__.py
rename to teleforma/exam/south_migrations/__init__.py
diff --git a/teleforma/forms.py b/teleforma/forms.py
index 358ca36b..7cb8535f 100644
--- a/teleforma/forms.py
+++ b/teleforma/forms.py
@@ -2,7 +2,7 @@
import datetime
from io import BytesIO
-from captcha.fields import CaptchaField
+from captcha.fields import ReCaptchaField
from django import forms
from django.conf import settings
from django.contrib.auth.models import User
@@ -55,6 +55,7 @@ def get_unique_username(first_name, last_name):
class BBBConferenceForm(ModelForm):
class Meta:
model = Conference
+ fields = '__all__'
class UserProfileForm(ModelForm):
class Meta:
@@ -110,7 +111,7 @@ if settings.TELEFORMA_E_LEARNING_TYPE != 'AE':
widget=forms.Select())
# no model
- captcha = CaptchaField()
+ captcha = ReCaptchaField()
accept = BooleanField()
class Meta:
@@ -132,23 +133,23 @@ if settings.TELEFORMA_E_LEARNING_TYPE != 'AE':
return image
#width, height = get_image_dimensions(image)
#ratio = float(height) / float(width)
- #if ratio > 2.5 or ratio < 1:
+ # if ratio > 2.5 or ratio < 1:
# raise ValidationError({'portrait': "L'image n'est pas au format portrait."})
NEW_HEIGHT = 230
NEW_WIDTH = 180
- #if width < NEW_WIDTH or height < NEW_HEIGHT:
+ # if width < NEW_WIDTH or height < NEW_HEIGHT:
# raise ValidationError({'portrait': "L'image est trop petite. Elle doit faire au moins %spx de large et %spx de hauteur." % (NEW_WIDTH, NEW_HEIGHT)})
- # resize image
- img = Image.open(image.file)
- new_image = img.resize((NEW_WIDTH, NEW_HEIGHT), Image.ANTIALIAS)
- if new_image.mode == "RGBA":
- new_image = new_image.convert("RGB")
+ # resize image
+ img = Image.open(image.file)
+ new_image = img.resize((NEW_WIDTH, NEW_HEIGHT), Image.ANTIALIAS)
+ if new_image.mode == "RGBA":
+ new_image = new_image.convert("RGB")
- temp = BytesIO()
- new_image.save(temp, 'jpeg')
- temp.seek(0)
- return SimpleUploadedFile('temp', temp.read())
+ temp = BytesIO()
+ new_image.save(temp, 'jpeg')
+ temp.seek(0)
+ return SimpleUploadedFile('temp', temp.read())
def save(self, commit=True):
@@ -234,7 +235,7 @@ else:
required=True)
# no model
- captcha = CaptchaField()
+ captcha = ReCaptchaField()
accept = BooleanField()
class Meta:
@@ -323,7 +324,7 @@ class CorrectorForm(ModelForm):
queryset=Course.objects.all().exclude(title="Aucune").order_by('title'),
widget=forms.CheckboxSelectMultiple())
# no model
- captcha = CaptchaField()
+ captcha = ReCaptchaField()
# accept = BooleanField()
class Meta:
diff --git a/teleforma/management/commands/teleforma-convert-boolean-fields.py b/teleforma/management/commands/teleforma-convert-boolean-fields.py
new file mode 100644
index 00000000..fcbd33de
--- /dev/null
+++ b/teleforma/management/commands/teleforma-convert-boolean-fields.py
@@ -0,0 +1,18 @@
+from django.conf import settings
+from django.apps import apps
+from django.core.management.base import BaseCommand, CommandError
+import django.db.models as models
+
+class Command(BaseCommand):
+ help = "Issue SQL commands to fix boolean fields after MySQL migration"
+ admin_email = 'webmaster@parisson.com'
+
+ def handle(self, *args, **options):
+ for app, _ in apps.all_models.items():
+ app_models = apps.get_app_config(app).get_models()
+ for model in app_models:
+ table = model._meta.db_table
+ for field in model._meta.fields:
+ if isinstance(field, models.BooleanField):
+ field_name = field.column
+ print(f"alter table {table} alter {field_name} type boolean using case when {field_name}=0 then false else true end;")
diff --git a/teleforma/migrations/0001_initial.py b/teleforma/migrations/0001_initial.py
index d5866158..32bb774b 100644
--- a/teleforma/migrations/0001_initial.py
+++ b/teleforma/migrations/0001_initial.py
@@ -1,4 +1,4 @@
-# Generated by Django 3.2.3 on 2021-06-01 15:28
+# Generated by Django 3.2.13 on 2023-04-26 11:59
from django.conf import settings
from django.db import migrations, models
@@ -42,7 +42,7 @@ class Migration(migrations.Migration):
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('public_id', models.CharField(blank=True, max_length=255, verbose_name='public_id')),
- ('session', models.CharField(choices=[('1', '1'), ('2', '2'), ('3', '3'), ('4', '4'), ('5', '5'), ('6', '6'), ('7', '7'), ('8', '8'), ('9', '9'), ('10', '10'), ('11', '11'), ('12', '12'), ('13', '13'), ('14', '14'), ('15', '15')], default='1', max_length=16, verbose_name='session')),
+ ('session', models.CharField(choices=[('1', '1'), ('2', '2'), ('3', '3'), ('4', '4'), ('5', '5'), ('6', '6'), ('7', '7'), ('8', '8'), ('9', '9'), ('10', '10')], default='1', max_length=16, verbose_name='session')),
('comment', teleforma.fields.ShortTextField(blank=True, max_length=255, verbose_name='comment')),
('date_begin', models.DateTimeField(blank=True, null=True, verbose_name='begin date')),
('date_end', models.DateTimeField(blank=True, null=True, verbose_name='end date')),
@@ -65,14 +65,6 @@ class Migration(migrations.Migration):
('title_tweeter', models.CharField(max_length=255, verbose_name='tweeter title')),
('date_modified', models.DateTimeField(auto_now=True, null=True, verbose_name='date modified')),
('number', models.IntegerField(blank=True, null=True, verbose_name='number')),
- ('synthesis_note', models.BooleanField(verbose_name='synthesis note')),
- ('obligation', models.BooleanField(verbose_name='obligations')),
- ('magistral', models.BooleanField(verbose_name='magistral')),
- ('procedure', models.BooleanField(verbose_name='procedure')),
- ('written_speciality', models.BooleanField(verbose_name='written_speciality')),
- ('oral_speciality', models.BooleanField(verbose_name='oral_speciality')),
- ('oral_1', models.BooleanField(verbose_name='oral_1')),
- ('oral_2', models.BooleanField(verbose_name='oral_2')),
('has_exam_scripts', models.BooleanField(default=True, verbose_name="copies d'examen")),
],
options={
@@ -232,7 +224,7 @@ class Migration(migrations.Migration):
('restricted', models.BooleanField(default=False, help_text="Cocher cette case lorsque vous voulez que l'étudiant puisse se connecter, mais ne pas avoir accès aux cours.", verbose_name='Accès restreint')),
('portrait', models.ImageField(blank=True, max_length=500, null=True, upload_to='portraits/')),
('platform_only', models.BooleanField(verbose_name='e-learning platform only')),
- ('application_fees', models.BooleanField(blank=True, default=True, verbose_name='application fees')),
+ ('application_fees', models.BooleanField(blank=True, default=False, verbose_name='application fees')),
('subscription_fees', models.FloatField(blank=True, help_text='â¬', null=True, verbose_name='subscription fees')),
('promo_code', models.CharField(blank=True, max_length=100, verbose_name='promo code')),
('date_registered', models.DateTimeField(auto_now_add=True, null=True, verbose_name='registration date')),
@@ -247,32 +239,15 @@ class Migration(migrations.Migration):
('payment_schedule', models.CharField(blank=True, choices=[('split', 'en plusieurs fois'), ('once', 'en une seule fois')], default='split', max_length=64, null=True, verbose_name='échéancier de paiement')),
('comment', models.TextField(blank=True, null=True, verbose_name='commentaire')),
('receipt_id', models.IntegerField(blank=True, null=True, unique=True, verbose_name='numéro de facture')),
+ ('courses', models.ManyToManyField(blank=True, related_name='student', to='teleforma.Course', verbose_name='courses')),
('iej', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='student', to='teleforma.iej', verbose_name='iej')),
- ('options', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='options_students', to='teleforma.course', verbose_name='options')),
- ('oral_1', models.ForeignKey(blank=True, help_text='Matière dâoral de langue (en option)', limit_choices_to={'oral_1': True}, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='oral_1_students', to='teleforma.course', verbose_name='oral de langue (option)')),
- ('oral_2', models.ForeignKey(blank=True, help_text='Matière dâoral technique 2 (en option)', limit_choices_to={'oral_2': True}, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='oral_2_students', to='teleforma.course', verbose_name='oral 2 (option)')),
- ('oral_speciality', models.ForeignKey(blank=True, help_text='Matière dâoral de spécialité (matière incluse dans la formation approfondie, en option pour toutes les autres formations)', limit_choices_to={'oral_speciality': True}, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='oral_speciality_students', to='teleforma.course', verbose_name='oral speciality')),
- ('period', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='student', to='teleforma.period', verbose_name='period')),
- ('procedure', models.ForeignKey(blank=True, help_text='Matière de procédure', limit_choices_to={'procedure': True}, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='procedure_students', to='teleforma.course', verbose_name='procedure')),
+ ('period', models.ManyToManyField(blank=True, related_name='student', to='teleforma.Period', verbose_name='period')),
],
options={
- 'verbose_name': 'Student',
- 'verbose_name_plural': 'Students',
- 'db_table': 'teleforma_student',
- 'ordering': ['user__last_name', '-date_subscribed'],
- },
- ),
- migrations.CreateModel(
- name='WebClassGroup',
- fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('name', models.CharField(max_length=255, verbose_name='name')),
- ('iejs', models.ManyToManyField(blank=True, related_name='web_class_group', to='teleforma.IEJ', verbose_name='IEJ')),
- ],
- options={
- 'verbose_name': 'web class group',
- 'verbose_name_plural': 'web class group',
- 'ordering': ['name'],
+ 'verbose_name': 'AE student',
+ 'verbose_name_plural': 'AE students',
+ 'db_table': 'teleforma_ae_student',
+ 'ordering': ['user__last_name'],
},
),
migrations.CreateModel(
@@ -288,17 +263,8 @@ class Migration(migrations.Migration):
('available', models.BooleanField(verbose_name='available')),
('platform_only', models.BooleanField(verbose_name='e-learning platform only')),
('duration', models.IntegerField(default=0, verbose_name='Durée en heures')),
- ('magistral', models.ManyToManyField(blank=True, related_name='training_magistral', to='teleforma.CourseType', verbose_name='magistral')),
- ('obligation', models.ManyToManyField(blank=True, related_name='training_obligation', to='teleforma.CourseType', verbose_name='obligations')),
- ('options', models.ManyToManyField(blank=True, related_name='training_options', to='teleforma.CourseType', verbose_name='options')),
- ('oral_1', models.ManyToManyField(blank=True, related_name='training_oral_1', to='teleforma.CourseType', verbose_name='oral 1')),
- ('oral_2', models.ManyToManyField(blank=True, related_name='training_oral_2', to='teleforma.CourseType', verbose_name='oral 2')),
- ('oral_speciality', models.ManyToManyField(blank=True, related_name='training_oral_speciality', to='teleforma.CourseType', verbose_name='oral speciality')),
('parent', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='children', to='teleforma.training', verbose_name='parent')),
('period', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='training', to='teleforma.period', verbose_name='period')),
- ('procedure', models.ManyToManyField(blank=True, related_name='training_procedure', to='teleforma.CourseType', verbose_name='procedure')),
- ('synthesis_note', models.ManyToManyField(blank=True, related_name='training_synthesis_note', to='teleforma.CourseType', verbose_name='synthesis note')),
- ('written_speciality', models.ManyToManyField(blank=True, related_name='training_written_speciality', to='teleforma.CourseType', verbose_name='written speciality')),
],
options={
'verbose_name': 'training',
@@ -317,11 +283,6 @@ class Migration(migrations.Migration):
'db_table': 'teleforma_student_groups',
},
),
- migrations.AddField(
- model_name='student',
- name='training',
- field=models.ForeignKey(blank=True, limit_choices_to={'available': True}, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='student_training', to='teleforma.training', verbose_name='training'),
- ),
migrations.AddField(
model_name='student',
name='trainings',
@@ -332,11 +293,6 @@ class Migration(migrations.Migration):
name='user',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='student', to=settings.AUTH_USER_MODEL, unique=True, verbose_name='user'),
),
- migrations.AddField(
- model_name='student',
- name='written_speciality',
- field=models.ForeignKey(blank=True, help_text='Matière juridique de spécialité', limit_choices_to={'written_speciality': True}, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='written_speciality_2students', to='teleforma.course', verbose_name='written speciality'),
- ),
migrations.CreateModel(
name='Room',
fields=[
@@ -362,7 +318,7 @@ class Migration(migrations.Migration):
('language', models.CharField(blank=True, max_length=255, verbose_name='Language')),
('telephone', models.CharField(blank=True, max_length=255, verbose_name='Telephone')),
('expiration_date', models.DateField(blank=True, null=True, verbose_name='Expiration_date')),
- ('init_password', models.BooleanField(verbose_name='Password initialized')),
+ ('init_password', models.BooleanField(default=False, verbose_name='Password initialized')),
('wifi_login', models.CharField(blank=True, max_length=255, verbose_name='WiFi login')),
('wifi_pass', models.CharField(blank=True, max_length=255, verbose_name='WiFi pass')),
('birthday', models.DateField(blank=True, help_text='jj/mm/aaaa', null=True, verbose_name='birthday')),
@@ -564,7 +520,7 @@ class Migration(migrations.Migration):
('is_published', models.BooleanField(verbose_name='published')),
('mime_type', models.CharField(blank=True, max_length=255, verbose_name='mime type')),
('weight', models.IntegerField(blank=True, choices=[(1, 1), (2, 2), (3, 3), (4, 4)], default=1, verbose_name='weight')),
- ('session', models.CharField(choices=[('1', '1'), ('2', '2'), ('3', '3'), ('4', '4'), ('5', '5'), ('6', '6'), ('7', '7'), ('8', '8'), ('9', '9'), ('10', '10'), ('11', '11'), ('12', '12'), ('13', '13'), ('14', '14'), ('15', '15')], default='1', max_length=16, verbose_name='session')),
+ ('session', models.CharField(choices=[('1', '1'), ('2', '2'), ('3', '3'), ('4', '4'), ('5', '5'), ('6', '6'), ('7', '7'), ('8', '8'), ('9', '9'), ('10', '10')], default='1', max_length=16, verbose_name='session')),
('is_annal', models.BooleanField(verbose_name='annal')),
('annal_year', models.IntegerField(blank=True, null=True, verbose_name='year')),
('file', models.FileField(blank=True, db_column='filename', max_length=1024, upload_to='items/%Y/%m/%d', verbose_name='file')),
@@ -632,6 +588,11 @@ class Migration(migrations.Migration):
name='periods',
field=models.ManyToManyField(blank=True, related_name='courses', to='teleforma.Period', verbose_name='Périodes associées'),
),
+ migrations.AddField(
+ model_name='course',
+ name='types',
+ field=models.ManyToManyField(blank=True, related_name='course', to='teleforma.CourseType', verbose_name='types'),
+ ),
migrations.CreateModel(
name='Corrector',
fields=[
@@ -684,11 +645,6 @@ class Migration(migrations.Migration):
name='room',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='conference', to='teleforma.room', verbose_name='room'),
),
- migrations.AddField(
- model_name='conference',
- name='web_class_group',
- field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='conferences', to='teleforma.webclassgroup', verbose_name='web class group'),
- ),
migrations.CreateModel(
name='AppointmentSlot',
fields=[
@@ -731,21 +687,6 @@ class Migration(migrations.Migration):
'ordering': ('id',),
},
),
- migrations.CreateModel(
- name='AEStudent',
- fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('platform_only', models.BooleanField(verbose_name='platform only')),
- ('courses', models.ManyToManyField(blank=True, related_name='ae_student', to='teleforma.Course', verbose_name='courses')),
- ('period', models.ManyToManyField(blank=True, related_name='ae_student', to='teleforma.Period', verbose_name='period')),
- ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='ae_student', to=settings.AUTH_USER_MODEL, unique=True, verbose_name='user')),
- ],
- options={
- 'verbose_name': 'AE student',
- 'db_table': 'teleforma_ae_student',
- 'ordering': ['user__last_name'],
- },
- ),
migrations.CreateModel(
name='Appointment',
fields=[
diff --git a/teleforma/models/__init__.py b/teleforma/models/__init__.py
index cf70770c..5603dfc9 100644
--- a/teleforma/models/__init__.py
+++ b/teleforma/models/__init__.py
@@ -1,6 +1,5 @@
from .core import *
from .crfpa import *
# from pro import *
-from .ae import *
from .messages import *
from .appointment import *
diff --git a/teleforma/models/ae.py b/teleforma/models/ae.py
deleted file mode 100644
index 3dc4baa3..00000000
--- a/teleforma/models/ae.py
+++ /dev/null
@@ -1,65 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-"""
- teleforma
-
- Copyright (c) 2012 Guillaume Pellerin
-
-# 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.
-
-# Author: Guillaume Pellerin
-"""
-
-import django.db.models as models
-from django.contrib.auth.models import User
-from django.utils.translation import ugettext_lazy as _
-
-from ..models import app_label
-from ..models.core import MetaCore
-
-
-class AEStudent(models.Model):
-
- user = models.ForeignKey(
- User, related_name='ae_student', verbose_name=_('user'), unique=True, on_delete=models.CASCADE)
- period = models.ManyToManyField('Period', related_name='ae_student', verbose_name=_('period'),
- blank=True)
- platform_only = models.BooleanField(_('platform only'))
- courses = models.ManyToManyField('Course', related_name="ae_student",
- verbose_name=_('courses'),
- blank=True)
-
- def __str__(self):
- try:
- return self.user.last_name + ' ' + self.user.first_name
- except:
- return ''
-
- class Meta(MetaCore):
- db_table = app_label + '_' + 'ae_student'
- verbose_name = _('AE student')
- ordering = ['user__last_name']
diff --git a/teleforma/models/core.py b/teleforma/models/core.py
index af555b3d..6a1dfbc1 100755
--- a/teleforma/models/core.py
+++ b/teleforma/models/core.py
@@ -252,10 +252,10 @@ class Course(models.Model):
periods = models.ManyToManyField('Period', related_name="courses",
verbose_name=u'Périodes associées',
- blank=True, null=True)
+ blank=True)
types = models.ManyToManyField('CourseType', related_name="course",
verbose_name=_('types'),
- blank=True, null=True)
+ blank=True)
def __str__(self):
return self.title
@@ -392,8 +392,8 @@ class Conference(models.Model):
status = models.IntegerField(
_('status'), choices=STATUS_CHOICES, default=2)
streaming = models.BooleanField(_('streaming'), default=True)
- web_class_group = models.ForeignKey('WebClassGroup', related_name='conferences', verbose_name=_('web class group'),
- blank=True, null=True, on_delete=models.SET_NULL)
+ # web_class_group = models.ForeignKey('WebClassGroup', related_name='conferences', verbose_name=_('web class group'),
+ # blank=True, null=True, on_delete=models.SET_NULL)
@property
def description(self):
@@ -599,7 +599,7 @@ class LiveStream(models.Model):
@property
def url(self):
if self.server.type == 'stream-m':
- return 'https://' + self.server.host + '/stream/video/consume/' + self.slug
+ return 'https://' + self.server.host + '/stream/video/consume/' + self.slug
else:
return 'https://' + self.server.host + '/stream/audio/' + self.slug
@@ -798,9 +798,6 @@ class Media(MediaBase):
blank=True, null=True, on_delete=models.SET_NULL)
period = models.ForeignKey('Period', related_name='media', verbose_name=_('period'),
null=True, blank=True, on_delete=models.SET_NULL)
- if HAS_TELEMETA:
- item = models.ForeignKey(MediaItem, related_name='media',
- verbose_name='item', blank=True, null=True, on_delete=models.SET_NULL)
type = models.CharField(
_('type'), choices=streaming_choices, max_length=32)
readers = models.ManyToManyField(User, related_name="media", verbose_name=_('readers'),
@@ -959,10 +956,10 @@ class NamePage(object):
else:
return '%c-%c' % (self.start_letter, self.end_letter)
-class StudentMixin(Model):
- user = models.ForeignKey(User, related_name='student', verbose_name=_('user'), unique=True)
+class StudentMixin(models.Model):
+ user = models.ForeignKey(User, related_name='student', verbose_name=_('user'), unique=True, on_delete=models.CASCADE)
trainings = models.ManyToManyField('Training', related_name='student_trainings', verbose_name=_('trainings'),
- blank=True, null=True)
+ blank=True)
iej = models.ForeignKey('IEJ', related_name='student', verbose_name=_('iej'),
blank=True, null=True, on_delete=models.SET_NULL)
restricted = models.BooleanField("Accès restreint", help_text="Cocher cette case lorsque vous voulez que l'étudiant puisse se connecter, mais ne pas avoir accès aux cours.", default=False)
diff --git a/teleforma/models/crfpa.py b/teleforma/models/crfpa.py
index 01c11253..12d2b681 100755
--- a/teleforma/models/crfpa.py
+++ b/teleforma/models/crfpa.py
@@ -44,7 +44,7 @@ from django.urls.base import reverse_lazy
from django.utils.translation import ugettext_lazy as _
from tinymce.models import HTMLField
-from ..models.core import (Course, Media, MetaCore, payment_choices,
+from ..models.core import (Course, Media, MetaCore, StudentMixin, payment_choices,
payment_schedule_choices)
app_label = 'teleforma'
@@ -75,36 +75,36 @@ class Training(models.Model):
code = models.CharField(_('code'), max_length=255)
name = models.CharField(_('name'), max_length=255, blank=True)
description = models.CharField(_('description'), max_length=512, blank=True)
- period = models.ForeignKey('Period', related_name='training', verbose_name=_('period'), blank=True, null=True)
- parent = models.ForeignKey('Training', related_name='children', verbose_name=_('parent'), blank=True, null=True)
+ period = models.ForeignKey('Period', related_name='training', verbose_name=_('period'), blank=True, null=True, on_delete=models.SET_NULL)
+ parent = models.ForeignKey('Training', related_name='children', verbose_name=_('parent'), blank=True, null=True, on_delete=models.SET_NULL)
if settings.TELEFORMA_E_LEARNING_TYPE != 'AE':
synthesis_note = models.ManyToManyField('CourseType', related_name="training_synthesis_note", verbose_name=_('synthesis note'),
- blank=True, null=True)
+ blank=True)
obligation = models.ManyToManyField('CourseType', related_name="training_obligation",
verbose_name=_('obligations'),
- blank=True, null=True)
+ blank=True)
procedure = models.ManyToManyField('CourseType', related_name="training_procedure",
verbose_name=_('procedure'),
- blank=True, null=True)
+ blank=True)
written_speciality = models.ManyToManyField('CourseType', related_name="training_written_speciality",
verbose_name=_('written speciality'),
- blank=True, null=True)
+ blank=True)
oral_speciality = models.ManyToManyField('CourseType', related_name="training_oral_speciality",
verbose_name=_('oral speciality'),
- blank=True, null=True)
+ blank=True)
oral_1 = models.ManyToManyField('CourseType', related_name="training_oral_1",
verbose_name=_('oral 1'),
- blank=True, null=True)
+ blank=True)
oral_2 = models.ManyToManyField('CourseType', related_name="training_oral_2",
verbose_name=_('oral 2'),
- blank=True, null=True)
+ blank=True)
options = models.ManyToManyField('CourseType', related_name="training_options",
verbose_name=_('options'),
- blank=True, null=True)
+ blank=True)
magistral = models.ManyToManyField('CourseType', related_name="training_magistral",
verbose_name=_('magistral'),
- blank=True, null=True)
+ blank=True)
cost = models.FloatField(_('cost'), blank=True, null=True)
cost_elearning_fascicle = models.FloatField(
@@ -167,11 +167,11 @@ if settings.TELEFORMA_E_LEARNING_TYPE != 'AE':
else:
class Student(StudentMixin):
- period = ManyToManyField('Period', related_name='student', verbose_name=_('period'),
- blank=True, null=True)
- courses = ManyToManyField('Course', related_name="student",
+ period = models.ManyToManyField('Period', related_name='student', verbose_name=_('period'),
+ blank=True)
+ courses = models.ManyToManyField('Course', related_name="student",
verbose_name=_('courses'),
- blank=True, null=True)
+ blank=True)
# to stay in sync with crfpa
training = None
@@ -350,7 +350,7 @@ class Home(models.Model):
_('Title'), max_length=255, null=True, blank=True)
text = models.TextField('Texte', blank=True)
video = models.ForeignKey(Media, verbose_name="Video", null=True, blank=True, on_delete=models.SET_NULL)
- modified_at = models.DateTimeField(u'Date de modification', auto_now=True, default=datetime.datetime.now)
+ modified_at = models.DateTimeField(u'Date de modification', auto_now=True)
periods = models.ManyToManyField('Period', related_name="home_texts",
verbose_name=u'Périodes associées',
blank=True)
diff --git a/teleforma/models/pro.py b/teleforma/models/pro.py
index 2f89dee6..8aab47cf 100644
--- a/teleforma/models/pro.py
+++ b/teleforma/models/pro.py
@@ -69,7 +69,7 @@ class Seminar(models.Model):
verbose_name=_('corrected document'),
blank=True, null=True)
suscribers = models.ManyToManyField(User, related_name="seminar", verbose_name=_('suscribers'),
- blank=True, null=True)
+ blank=True)
date_added = models.DateTimeField(_('date added'), auto_now_add=True)
date_modified = models.DateTimeField(_('date modified'), auto_now=True)
diff --git a/teleforma/templates/postman/base_write.html b/teleforma/templates/postman/base_write.html
index 186813e3..b99f7ad0 100644
--- a/teleforma/templates/postman/base_write.html
+++ b/teleforma/templates/postman/base_write.html
@@ -15,7 +15,7 @@
{% trans "Pour les questions concernant l'organisation des cours, le planning, les documents de cours ou les copies, adressez-vous à Admin-CRFPA." %}
{% trans "Pour les questions concernant uniquement l'accès à la plateforme et aux médias vidéo ou audio, lire d'abord" %}
- la page
+ la page
d'aide {% trans "puis adressez-vous à Support technique." %}
diff --git a/teleforma/templates/registration/registration_complete.html b/teleforma/templates/registration/registration_complete.html
index acb6a200..de6eb9c0 100644
--- a/teleforma/templates/registration/registration_complete.html
+++ b/teleforma/templates/registration/registration_complete.html
@@ -15,7 +15,7 @@ Afin de compléter votre inscription, il est nécessaire dâimprimer le formula
-
{% trans "Download" %}
+
{% trans "Download" %}
Une fois votre dossier reçu et confirmé par nos services, vous recevrez un mail vous invitant à procéder au règlement des arrhes par carte bancaire. Votre inscription ne sera validée quâau terme de cette ultime étape.
diff --git a/teleforma/templates/teleforma/annals.html b/teleforma/templates/teleforma/annals.html
index 38e52df2..5ae0288c 100644
--- a/teleforma/templates/teleforma/annals.html
+++ b/teleforma/templates/teleforma/annals.html
@@ -17,7 +17,7 @@
{% block courses %}
{% for c in all_courses %}
{% with c.course as course %}
- {{ course.title }}
+ {{ course.title }}
{% endwith %}
{% endfor %}
{% endblock courses %}
@@ -32,7 +32,7 @@
@@ -65,11 +65,11 @@
{% for doc in docs %}
- {% if doc.file %} {% endif %} {{ doc.title }}{% if doc.file %}{% endif %} |
+ {% if doc.file %} {% endif %} {{ doc.title }}{% if doc.file %}{% endif %} |
{{ year }} |
- {% if doc.file %} {% endif %} |
+ {% if doc.file %} {% endif %} |
{% endfor %}
diff --git a/teleforma/templates/teleforma/appointments.html b/teleforma/templates/teleforma/appointments.html
index 9809e68b..9c10ea5c 100644
--- a/teleforma/templates/teleforma/appointments.html
+++ b/teleforma/templates/teleforma/appointments.html
@@ -219,7 +219,7 @@
{% if ap_period.appointment.can_cancel %}
-