From e132f4d94a4d9479d5c14145c5144796f0b990eb Mon Sep 17 00:00:00 2001 From: test test Date: Wed, 26 May 2021 15:13:43 +0200 Subject: [PATCH] Update imports, modules, and migrate some code to python 3 / django 3 --- .gitignore | 3 + .vscode/settings.json | 3 + Dockerfile | 10 +- app/settings.py | 149 +++-- app/urls.py | 23 +- requirements-new.txt | 18 - requirements-old.txt | 163 ++++++ requirements.txt | 151 +---- teleforma/admin.py | 118 ++-- teleforma/decorators.py | 19 +- teleforma/exam/admin.py | 5 +- teleforma/exam/forms.py | 21 +- teleforma/exam/models.py | 241 ++++---- teleforma/exam/urls.py | 16 +- teleforma/exam/views.py | 244 ++++---- teleforma/fields.py | 35 +- teleforma/forms.py | 155 ++--- .../commands/teleforma-delete-users.py | 18 +- .../commands/teleforma-export-users.py | 28 +- teleforma/middleware.py | 62 +- teleforma/models/__init__.py | 11 +- teleforma/models/ae.py | 27 +- teleforma/models/appointment.py | 118 ++-- teleforma/models/core.py | 537 ++++++++++-------- teleforma/models/crfpa.py | 263 +++++---- teleforma/models/messages.py | 27 +- teleforma/models/pro.py | 134 +++-- .../0001_initial.py | 0 ...__add_coursetype__add_field_course_type.py | 0 .../0003_auto__add_field_course_chat_room.py | 0 ...ing_category__del_field_course_category.py | 0 ...0005_auto__add_field_profile_date_added.py | 0 .../0006_auto.py | 0 ...chat_room__add_field_professor_training.py | 0 ...08_auto__add_field_course_date_modified.py | 0 ...d_field_media_description__add_field_me.py | 0 .../0010_auto__chg_field_training_category.py | 0 ...to__add_livestream__add_streamingserver.py | 0 ...12_auto__add_field_conference_streaming.py | 0 ...0013_auto__del_field_profile_date_added.py | 0 ...d_documenttype__add_field_document_type.py | 0 ...dure__del_category__del_oral__add_perio.py | 0 .../0016_auto.py | 0 .../0017_auto__add_field_course_number.py | 0 ...o__add_payment__add_field_training_cost.py | 0 .../0019_auto__chg_field_course_number.py | 0 .../0020_auto__chg_field_training_cost.py | 0 .../0021_auto__chg_field_course_type.py | 0 ...22_auto__add_field_student_network_only.py | 0 .../0023_auto.py | 0 ...tion__del_field_training_synthesis_note.py | 0 .../0025_auto__del_field_course_type.py | 0 .../0026_auto.py | 0 ...hesis_note__add_field_course_obligation.py | 0 ..._type__add_field_livestream_course__add.py | 0 .../0029_auto.py | 0 ...re__add_field_student_oral_speciality__.py | 0 ...d_student_period__chg_field_student_iej.py | 0 ...ing__chg_field_document_conference__chg.py | 0 .../0033_auto__add_field_course_magistral.py | 0 ...034_auto__add_field_documenttype_number.py | 0 ...ssor_training__del_field_student_period.py | 0 ...36_auto__del_field_document_course_type.py | 0 ...aming__chg_field_livestream_course_type.py | 0 ...38_auto__add_field_conference_public_id.py | 0 ...0039_auto__chg_field_conference_comment.py | 0 ...ourse__del_field_livestream_course_type.py | 0 .../0041_auto__del_field_media_is_live.py | 0 .../0042_auto__del_field_media_item.py | 0 ...dia_course_type__chg_field_media_course.py | 0 .../0044_auto__add_field_department_domain.py | 0 ...chg_field_training_cost__chg_field_trai.py | 0 ...nt_mime_type__add_field_media_mime_type.py | 0 ...__add_answer__add_seminar__add_field_do.py | 0 .../0048_auto__chg_field_seminar_price.py | 0 ...dd_testimonialtemplate__add_testimonial.py | 0 ...50_auto__add_field_testimonial_template.py | 0 ...ield_testimonialtemplate_template_doc__.py | 0 ...late_text__add_field_testimonialtemplat.py | 0 ...r_concerned__add_field_seminar_keywords.py | 0 .../0054_auto__add_aestudent.py | 0 ...dd_field_document_period__add_field_doc.py | 0 ...6_auto__add_field_conference_department.py | 0 .../0057_auto__add_field_document_iej.py | 0 ...58_auto__add_field_course_title_tweeter.py | 0 ...059_auto__add_field_document_annal_year.py | 0 .../0060_auto__chg_field_student_training.py | 0 .../0061_auto__del_field_student_training.py | 0 ...altemplate__del_seminar__del_question__.py | 0 ...to__add_field_department_default_period.py | 0 .../0064_auto__add_field_document_session.py | 0 .../0065_auto__chg_field_document_session.py | 0 ...wifi_login__add_field_profile_wifi_pass.py | 0 ...ent_file__chg_field_documentsimple_file.py | 0 ...68_auto__add_field_professor_department.py | 0 .../0069_auto__add_field_student_period.py | 0 .../0070_auto__add_payment.py | 0 .../0071_auto__add_field_payment_collected.py | 0 ...ount__add_field_student_application_fee.py | 0 ...auto__add_field_student_date_subscribed.py | 0 ...chg_field_training_cost__chg_field_trai.py | 0 ...upedmessage__add_field_period_date_begi.py | 0 ...d_student_promo_code__add_field_student.py | 0 ...ption__add_field_course_procedure__add_.py | 0 ...g_available__add_field_student_training.py | 0 ...auto__add_field_student_date_registered.py | 0 ...d_date_end__chg_field_period_date_begin.py | 0 ...sword_init__add_field_period_message_pl.py | 0 .../0082_auto__add_field_payment_type.py | 0 .../0083_auto__add_payback.py | 0 ...eld_training_parent__add_field_conferen.py | 0 .../0085_auto__add_field_period_parent.py | 0 .../0086_auto__add_field_period_is_open.py | 0 ...87_auto__add_field_period_date_exam_end.py | 0 ...088_auto__add_field_course_exam_scripts.py | 0 ...nt__del_field_course_exam_scripts__add_.py | 0 .../0090_auto.py | 0 .../0091_auto__del_field_document_period.py | 0 .../0092_auto__add_home.py | 0 ...d_field_home_video__chg_field_home_text.py | 0 .../0094_auto__add_field_course_quiz.py | 0 .../0095_auto__del_field_course_quiz.py | 0 .../0096_auto__add_newsitem.py | 0 .../0097_auto__add_field_newsitem_period.py | 0 ...ppointment__add_field_period_book_delay.py | 0 ...appointmentperiod__add_appointmentday__.py | 0 ..._chg_field_period_appointment_slot_size.py | 0 ...dd_unique_appointment_slot_jury_slot_nb.py | 0 ...od_start__add_field_appointmentperiod_e.py | 0 ...od_period__add_field_appointmentperiod_.py | 0 ...ield_appointmentperiod_book_delay__del_.py | 0 ..._add_field_appointmentperiod_book_delay.py | 0 .../0106_auto__add_field_period_nb_script.py | 0 .../0107_auto__add_field_student_balance.py | 0 ...o__add_field_documenttype_for_corrector.py | 0 .../0109_set_allowed_correctors.py | 0 ...o__add_field_period_date_close_accounts.py | 0 ..._field_home_modified_at__add_field_home.py | 0 .../0112_auto.py | 0 ...o__add_field_course_last_professor_sent.py | 0 .../0114_add_field_Student_fascicule.py | 0 ...15_auto__add_field_conference_streaming.py | 0 ..._auto__add_field_training_platform_only.py | 0 ..._period_date_inscription_start__add_fie.py | 0 ...ed__add_field_payment_scheduled__add_fi.py | 0 ...latform_only__add_field_student_comment.py | 0 .../0119_auto__add_corrector.py | 0 ...hday_place__add_field_profile_ss_number.py | 0 ..._add_field_student_balance_intermediary.py | 0 ...learning_fascicle__add_field_training_c.py | 0 .../0123_auto__add_field_training_duration.py | 0 ...0124_auto__add_field_student_receipt_id.py | 0 ...home_visible_title__chg_field_home_text.py | 0 ...126_auto__add_field_profile_nationality.py | 0 .../0127_auto__add_field_profile_siret.py | 0 ...od_course__add_field_appointmentperiod_.py | 0 ...od_bbb_room__add_field_student_restrict.py | 0 .../0130_auto__add_field_payment_date_paid.py | 0 ...field_period_department__add_field_medi.py | 0 .../0132_auto__del_field_media_item.py | 0 .../__init__.py | 0 teleforma/templatetags/payment.py | 2 +- teleforma/templatetags/teleforma_tags.py | 163 ++++-- teleforma/templatetags/webclass.py | 2 +- teleforma/urls.py | 380 +++++++------ teleforma/views/__init__.py | 12 +- teleforma/views/ae.py | 13 +- teleforma/views/appointment.py | 23 +- teleforma/views/core.py | 526 ++++++++--------- teleforma/views/crfpa.py | 67 ++- teleforma/views/home.py | 15 +- teleforma/views/pages.py | 20 +- teleforma/views/payment.py | 95 ++-- teleforma/views/pro.py | 31 +- teleforma/views/profile.py | 36 +- teleforma/webclass/admin.py | 7 +- teleforma/webclass/forms.py | 30 +- teleforma/webclass/models.py | 190 +++---- .../0001_initial.py | 0 .../0002_auto__add_webclassrecord.py | 0 ...to__add_field_webclassrecord_bbb_server.py | 0 .../0004_auto__add_field_webclass_end_date.py | 0 .../__init__.py | 0 teleforma/webclass/urls.py | 26 +- teleforma/webclass/views.py | 55 +- 185 files changed, 2311 insertions(+), 1981 deletions(-) create mode 100644 .vscode/settings.json delete mode 100644 requirements-new.txt create mode 100644 requirements-old.txt rename teleforma/{migrations => south_migrations}/0001_initial.py (100%) rename teleforma/{migrations => south_migrations}/0002_auto__add_coursetype__add_field_course_type.py (100%) rename teleforma/{migrations => south_migrations}/0003_auto__add_field_course_chat_room.py (100%) rename teleforma/{migrations => south_migrations}/0004_auto__add_field_training_category__del_field_course_category.py (100%) rename teleforma/{migrations => south_migrations}/0005_auto__add_field_profile_date_added.py (100%) rename teleforma/{migrations => south_migrations}/0006_auto.py (100%) rename teleforma/{migrations => south_migrations}/0007_auto__del_field_course_chat_room__add_field_professor_training.py (100%) rename teleforma/{migrations => south_migrations}/0008_auto__add_field_course_date_modified.py (100%) rename teleforma/{migrations => south_migrations}/0009_auto__add_field_media_title__add_field_media_description__add_field_me.py (100%) rename teleforma/{migrations => south_migrations}/0010_auto__chg_field_training_category.py (100%) rename teleforma/{migrations => south_migrations}/0011_auto__add_livestream__add_streamingserver.py (100%) rename teleforma/{migrations => south_migrations}/0012_auto__add_field_conference_streaming.py (100%) rename teleforma/{migrations => south_migrations}/0013_auto__del_field_profile_date_added.py (100%) rename teleforma/{migrations => south_migrations}/0014_auto__add_documenttype__add_field_document_type.py (100%) rename teleforma/{migrations => south_migrations}/0015_auto__del_speciality__del_procedure__del_category__del_oral__add_perio.py (100%) rename teleforma/{migrations => south_migrations}/0016_auto.py (100%) rename teleforma/{migrations => south_migrations}/0017_auto__add_field_course_number.py (100%) rename teleforma/{migrations => south_migrations}/0018_auto__add_payment__add_field_training_cost.py (100%) rename teleforma/{migrations => south_migrations}/0019_auto__chg_field_course_number.py (100%) rename teleforma/{migrations => south_migrations}/0020_auto__chg_field_training_cost.py (100%) rename teleforma/{migrations => south_migrations}/0021_auto__chg_field_course_type.py (100%) rename teleforma/{migrations => south_migrations}/0022_auto__add_field_student_network_only.py (100%) rename teleforma/{migrations => south_migrations}/0023_auto.py (100%) rename teleforma/{migrations => south_migrations}/0024_auto__del_field_training_obligation__del_field_training_synthesis_note.py (100%) rename teleforma/{migrations => south_migrations}/0025_auto__del_field_course_type.py (100%) rename teleforma/{migrations => south_migrations}/0026_auto.py (100%) rename teleforma/{migrations => south_migrations}/0027_auto__add_field_course_synthesis_note__add_field_course_obligation.py (100%) rename teleforma/{migrations => south_migrations}/0028_auto__add_field_document_course_type__add_field_livestream_course__add.py (100%) rename teleforma/{migrations => south_migrations}/0029_auto.py (100%) rename teleforma/{migrations => south_migrations}/0030_auto__add_field_student_procedure__add_field_student_oral_speciality__.py (100%) rename teleforma/{migrations => south_migrations}/0031_auto__chg_field_student_period__chg_field_student_iej.py (100%) rename teleforma/{migrations => south_migrations}/0032_auto__chg_field_professor_training__chg_field_document_conference__chg.py (100%) rename teleforma/{migrations => south_migrations}/0033_auto__add_field_course_magistral.py (100%) rename teleforma/{migrations => south_migrations}/0034_auto__add_field_documenttype_number.py (100%) rename teleforma/{migrations => south_migrations}/0035_auto__del_field_professor_training__del_field_student_period.py (100%) rename teleforma/{migrations => south_migrations}/0036_auto__del_field_document_course_type.py (100%) rename teleforma/{migrations => south_migrations}/0037_auto__add_field_livestream_streaming__chg_field_livestream_course_type.py (100%) rename teleforma/{migrations => south_migrations}/0038_auto__add_field_conference_public_id.py (100%) rename teleforma/{migrations => south_migrations}/0039_auto__chg_field_conference_comment.py (100%) rename teleforma/{migrations => south_migrations}/0040_auto__del_field_livestream_course__del_field_livestream_course_type.py (100%) rename teleforma/{migrations => south_migrations}/0041_auto__del_field_media_is_live.py (100%) rename teleforma/{migrations => south_migrations}/0042_auto__del_field_media_item.py (100%) rename teleforma/{migrations => south_migrations}/0043_auto__chg_field_media_course_type__chg_field_media_course.py (100%) rename teleforma/{migrations => south_migrations}/0044_auto__add_field_department_domain.py (100%) rename teleforma/{migrations => south_migrations}/0045_auto__chg_field_training_code__chg_field_training_cost__chg_field_trai.py (100%) rename teleforma/{migrations => south_migrations}/0046_auto__add_field_document_mime_type__add_field_media_mime_type.py (100%) rename teleforma/{migrations => south_migrations}/0047_auto__del_payment__add_question__add_answer__add_seminar__add_field_do.py (100%) rename teleforma/{migrations => south_migrations}/0048_auto__chg_field_seminar_price.py (100%) rename teleforma/{migrations => south_migrations}/0049_auto__add_testimonialtemplate__add_testimonial.py (100%) rename teleforma/{migrations => south_migrations}/0050_auto__add_field_testimonial_template.py (100%) rename teleforma/{migrations => south_migrations}/0051_auto__add_documentsimple__del_field_testimonialtemplate_template_doc__.py (100%) rename teleforma/{migrations => south_migrations}/0052_auto__del_field_testimonialtemplate_text__add_field_testimonialtemplat.py (100%) rename teleforma/{migrations => south_migrations}/0053_auto__add_field_seminar_concerned__add_field_seminar_keywords.py (100%) rename teleforma/{migrations => south_migrations}/0054_auto__add_aestudent.py (100%) rename teleforma/{migrations => south_migrations}/0055_auto__add_field_media_period__add_field_document_period__add_field_doc.py (100%) rename teleforma/{migrations => south_migrations}/0056_auto__add_field_conference_department.py (100%) rename teleforma/{migrations => south_migrations}/0057_auto__add_field_document_iej.py (100%) rename teleforma/{migrations => south_migrations}/0058_auto__add_field_course_title_tweeter.py (100%) rename teleforma/{migrations => south_migrations}/0059_auto__add_field_document_annal_year.py (100%) rename teleforma/{migrations => south_migrations}/0060_auto__chg_field_student_training.py (100%) rename teleforma/{migrations => south_migrations}/0061_auto__del_field_student_training.py (100%) rename teleforma/{migrations => south_migrations}/0062_auto__del_answer__del_testimonialtemplate__del_seminar__del_question__.py (100%) rename teleforma/{migrations => south_migrations}/0063_auto__add_field_department_default_period.py (100%) rename teleforma/{migrations => south_migrations}/0064_auto__add_field_document_session.py (100%) rename teleforma/{migrations => south_migrations}/0065_auto__chg_field_document_session.py (100%) rename teleforma/{migrations => south_migrations}/0066_auto__add_field_profile_wifi_login__add_field_profile_wifi_pass.py (100%) rename teleforma/{migrations => south_migrations}/0067_auto__chg_field_document_file__chg_field_documentsimple_file.py (100%) rename teleforma/{migrations => south_migrations}/0068_auto__add_field_professor_department.py (100%) rename teleforma/{migrations => south_migrations}/0069_auto__add_field_student_period.py (100%) rename teleforma/{migrations => south_migrations}/0070_auto__add_payment.py (100%) rename teleforma/{migrations => south_migrations}/0071_auto__add_field_payment_collected.py (100%) rename teleforma/{migrations => south_migrations}/0072_auto__add_optionalfee__add_discount__add_field_student_application_fee.py (100%) rename teleforma/{migrations => south_migrations}/0073_auto__add_field_student_date_subscribed.py (100%) rename teleforma/{migrations => south_migrations}/0074_auto__chg_field_training_code__chg_field_training_cost__chg_field_trai.py (100%) rename teleforma/{migrations => south_migrations}/0075_auto__add_studentgroup__add_groupedmessage__add_field_period_date_begi.py (100%) rename teleforma/{migrations => south_migrations}/0076_auto__add_coursegroup__add_field_student_promo_code__add_field_student.py (100%) rename teleforma/{migrations => south_migrations}/0077_auto__add_field_training_description__add_field_course_procedure__add_.py (100%) rename teleforma/{migrations => south_migrations}/0078_auto__add_field_training_available__add_field_student_training.py (100%) rename teleforma/{migrations => south_migrations}/0079_auto__add_field_student_date_registered.py (100%) rename teleforma/{migrations => south_migrations}/0080_auto__chg_field_period_date_end__chg_field_period_date_begin.py (100%) rename teleforma/{migrations => south_migrations}/0081_auto__add_field_period_date_password_init__add_field_period_message_pl.py (100%) rename teleforma/{migrations => south_migrations}/0082_auto__add_field_payment_type.py (100%) rename teleforma/{migrations => south_migrations}/0083_auto__add_payback.py (100%) rename teleforma/{migrations => south_migrations}/0084_auto__add_webclassgroup__add_field_training_parent__add_field_conferen.py (100%) rename teleforma/{migrations => south_migrations}/0085_auto__add_field_period_parent.py (100%) rename teleforma/{migrations => south_migrations}/0086_auto__add_field_period_is_open.py (100%) rename teleforma/{migrations => south_migrations}/0087_auto__add_field_period_date_exam_end.py (100%) rename teleforma/{migrations => south_migrations}/0088_auto__add_field_course_exam_scripts.py (100%) rename teleforma/{migrations => south_migrations}/0089_auto__add_field_period_department__del_field_course_exam_scripts__add_.py (100%) rename teleforma/{migrations => south_migrations}/0090_auto.py (100%) rename teleforma/{migrations => south_migrations}/0091_auto__del_field_document_period.py (100%) rename teleforma/{migrations => south_migrations}/0092_auto__add_home.py (100%) rename teleforma/{migrations => south_migrations}/0093_auto__add_field_home_video__chg_field_home_text.py (100%) rename teleforma/{migrations => south_migrations}/0094_auto__add_field_course_quiz.py (100%) rename teleforma/{migrations => south_migrations}/0095_auto__del_field_course_quiz.py (100%) rename teleforma/{migrations => south_migrations}/0096_auto__add_newsitem.py (100%) rename teleforma/{migrations => south_migrations}/0097_auto__add_field_newsitem_period.py (100%) rename teleforma/{migrations => south_migrations}/0098_auto__add_field_period_enable_appointment__add_field_period_book_delay.py (100%) rename teleforma/{migrations => south_migrations}/0099_auto__add_appointmentjury__add_appointmentperiod__add_appointmentday__.py (100%) rename teleforma/{migrations => south_migrations}/0100_auto__chg_field_period_appointment_slot_size.py (100%) rename teleforma/{migrations => south_migrations}/0101_auto__add_unique_appointment_slot_jury_slot_nb.py (100%) rename teleforma/{migrations => south_migrations}/0102_auto__add_field_appointmentperiod_start__add_field_appointmentperiod_e.py (100%) rename teleforma/{migrations => south_migrations}/0103_auto__del_field_appointmentperiod_period__add_field_appointmentperiod_.py (100%) rename teleforma/{migrations => south_migrations}/0104_auto__del_appointmentday__del_field_appointmentperiod_book_delay__del_.py (100%) rename teleforma/{migrations => south_migrations}/0105_auto__add_field_appointmentperiod_book_delay.py (100%) rename teleforma/{migrations => south_migrations}/0106_auto__add_field_period_nb_script.py (100%) rename teleforma/{migrations => south_migrations}/0107_auto__add_field_student_balance.py (100%) rename teleforma/{migrations => south_migrations}/0108_auto__add_field_documenttype_for_corrector.py (100%) rename teleforma/{migrations => south_migrations}/0109_set_allowed_correctors.py (100%) rename teleforma/{migrations => south_migrations}/0110_auto__add_field_period_date_close_accounts.py (100%) rename teleforma/{migrations => south_migrations}/0111_auto__add_field_home_title__add_field_home_modified_at__add_field_home.py (100%) rename teleforma/{migrations => south_migrations}/0112_auto.py (100%) rename teleforma/{migrations => south_migrations}/0113_auto__add_field_course_last_professor_sent.py (100%) rename teleforma/{migrations => south_migrations}/0114_add_field_Student_fascicule.py (100%) rename teleforma/{migrations => south_migrations}/0115_auto__add_field_conference_streaming.py (100%) rename teleforma/{migrations => south_migrations}/0116_auto__add_field_training_platform_only.py (100%) rename teleforma/{migrations => south_migrations}/0117_auto__add_parameters__add_field_period_date_inscription_start__add_fie.py (100%) rename teleforma/{migrations => south_migrations}/0117_auto__del_field_payment_collected__add_field_payment_scheduled__add_fi.py (100%) rename teleforma/{migrations => south_migrations}/0118_auto__add_field_training_platform_only__add_field_student_comment.py (100%) rename teleforma/{migrations => south_migrations}/0119_auto__add_corrector.py (100%) rename teleforma/{migrations => south_migrations}/0120_auto__add_field_profile_birthday_place__add_field_profile_ss_number.py (100%) rename teleforma/{migrations => south_migrations}/0121_auto__add_field_student_balance_intermediary.py (100%) rename teleforma/{migrations => south_migrations}/0122_auto__add_field_training_cost_elearning_fascicle__add_field_training_c.py (100%) rename teleforma/{migrations => south_migrations}/0123_auto__add_field_training_duration.py (100%) rename teleforma/{migrations => south_migrations}/0124_auto__add_field_student_receipt_id.py (100%) rename teleforma/{migrations => south_migrations}/0125_auto__add_field_home_visible_title__chg_field_home_text.py (100%) rename teleforma/{migrations => south_migrations}/0126_auto__add_field_profile_nationality.py (100%) rename teleforma/{migrations => south_migrations}/0127_auto__add_field_profile_siret.py (100%) rename teleforma/{migrations => south_migrations}/0128_auto__add_field_appointmentperiod_course__add_field_appointmentperiod_.py (100%) rename teleforma/{migrations => south_migrations}/0129_auto__del_field_appointmentperiod_bbb_room__add_field_student_restrict.py (100%) rename teleforma/{migrations => south_migrations}/0130_auto__add_field_payment_date_paid.py (100%) rename teleforma/{migrations => south_migrations}/0131_auto__add_mediatranscoded__chg_field_period_department__add_field_medi.py (100%) rename teleforma/{migrations => south_migrations}/0132_auto__del_field_media_item.py (100%) rename teleforma/{migrations => south_migrations}/__init__.py (100%) rename teleforma/webclass/{migrations => south_migrations}/0001_initial.py (100%) rename teleforma/webclass/{migrations => south_migrations}/0002_auto__add_webclassrecord.py (100%) rename teleforma/webclass/{migrations => south_migrations}/0003_auto__add_field_webclassrecord_bbb_server.py (100%) rename teleforma/webclass/{migrations => south_migrations}/0004_auto__add_field_webclass_end_date.py (100%) rename teleforma/webclass/{migrations => south_migrations}/__init__.py (100%) diff --git a/.gitignore b/.gitignore index 3e379309..309249ec 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,6 @@ pip-log.txt #Mr Developer .mr.developer.cfg + +#python venv +py_env diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..839ddd71 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "python.pythonPath": "py_env/bin/python3" +} \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index f3cfc30d..cd8967eb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -41,12 +41,12 @@ ENV LANG fr_FR.UTF-8 ENV LANGUAGE fr_FR:fr ENV LC_ALL fr_FR.UTF-8 -# COPY requirements.txt /srv -# RUN pip3 install -r requirements.txt +COPY requirements.txt /srv +RUN pip3 install -r requirements.txt -# COPY lib /srv/lib -# COPY bin/build/local/setup_lib.sh /srv -# RUN /srv/setup_lib.sh +COPY lib /srv/lib +COPY bin/build/local/setup_lib.sh /srv +RUN /srv/setup_lib.sh WORKDIR /srv/src/teleforma COPY setup.py /srv/src/teleforma diff --git a/app/settings.py b/app/settings.py index 257be230..367b8675 100644 --- a/app/settings.py +++ b/app/settings.py @@ -1,9 +1,11 @@ # -*- coding: utf-8 -*- # Django settings for sandbox project. +from django.utils.encoding import force_text +import warnings import os import sys -from django.core.urlresolvers import reverse_lazy +from django.urls import reverse_lazy # import environ sys.dont_write_bytecode = True @@ -11,26 +13,34 @@ sys.dont_write_bytecode = True DEBUG = True if os.environ.get('DEBUG') == 'True' else False TEMPLATE_DEBUG = DEBUG -import warnings + +BASE_DIR = os.path.dirname(os.path.dirname(__file__)) + warnings.showwarning = lambda *x: None ADMINS = ( ('Guillaume Pellerin', 'webmaster@parisson.com'), ('Gael le Mignot', 'gael@pilotsystems.net'), -# ('Admin CRFPA', 'admin-crfpa@pre-barreau.com'), + # ('Admin CRFPA', 'admin-crfpa@pre-barreau.com'), ) MANAGERS = ADMINS DATABASES = { 'default': { - 'ENGINE': 'django.db.backends.mysql', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'. - 'NAME': os.environ.get('MYSQL_DATABASE'), # Or path to database file if using sqlite3. - 'USER': os.environ.get('MYSQL_USER'), # Not used with sqlite3. - 'PASSWORD': os.environ.get('MYSQL_PASSWORD'), # Not used with sqlite3. - 'HOST': 'db', # Set to empty string for localhost. Not used with sqlite3. - 'PORT': '', # Set to empty string for default. Not used with sqlite3. - 'OPTIONS' : { 'init_command' : 'SET storage_engine=InnoDB', }, + # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'. + 'ENGINE': 'django.db.backends.mysql', + # Or path to database file if using sqlite3. + 'NAME': os.environ.get('MYSQL_DATABASE'), + # Not used with sqlite3. + 'USER': os.environ.get('MYSQL_USER'), + # Not used with sqlite3. + 'PASSWORD': os.environ.get('MYSQL_PASSWORD'), + # Set to empty string for localhost. Not used with sqlite3. + 'HOST': 'db', + # Set to empty string for default. Not used with sqlite3. + 'PORT': '', + 'OPTIONS': {'init_command': 'SET storage_engine=InnoDB', }, } } @@ -46,8 +56,9 @@ TIME_ZONE = 'Europe/Paris' # Language code for this installation. All choices can be found here: # http://www.i18nguy.com/unicode/language-identifiers.html LANGUAGE_CODE = 'fr' -LANGUAGES = [ ('fr', 'French'), - ('en', 'English'), +LANGUAGES = [ + ('fr', 'French'), + ('en', 'English'), ] SITE_ID = 1 @@ -92,7 +103,7 @@ STATICFILES_DIRS = ( STATICFILES_FINDERS = ( 'django.contrib.staticfiles.finders.FileSystemFinder', 'django.contrib.staticfiles.finders.AppDirectoriesFinder', -# 'django.contrib.staticfiles.finders.DefaultStorageFinder', + # 'django.contrib.staticfiles.finders.DefaultStorageFinder', ) # Make this unique, and don't share it with anybody. @@ -107,7 +118,7 @@ TEMPLATE_LOADERS = ( ) -MIDDLEWARE_CLASSES = ( +MIDDLEWARE = ( 'django.middleware.common.CommonMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', @@ -121,15 +132,8 @@ MIDDLEWARE_CLASSES = ( ROOT_URLCONF = 'urls' -# TEMPLATE_DIRS = ( -# # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates". -# # Always use forward slashes, even on Windows. -# # Don't forget to use absolute paths, not relative paths. -# '/srv/src/teleforma/teleforma/templates/', -# ) INSTALLED_APPS = ( - 'suit', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', @@ -137,31 +141,29 @@ INSTALLED_APPS = ( 'django.contrib.messages', 'django.contrib.staticfiles', 'django.contrib.admin', - 'south', + # 'south', 'teleforma', 'teleforma.webclass', 'teleforma.exam', 'jsonrpc', 'sorl.thumbnail', - 'django_extensions', - 'pagination', + # 'django_extensions', + # 'pagination', 'postman', - 'timezones', - 'jqchat', - 'googletools', - 'extra_views', + # 'timezones', + # 'googletools', + # 'extra_views', 'captcha', 'django_nvd3', - 'bootstrap3', - 'bootstrap_pagination', - 'django_user_agents', + # 'bootstrap3', + # 'bootstrap_pagination', + # 'django_user_agents', 'tinymce', - 'multichoice', - 'true_false', - 'essay', - 'quiz', + # 'multichoice', + # 'true_false', + # 'essay', + # 'quiz', 'pdfannotator', - 'captcha', # 'telemeta', ) @@ -176,26 +178,23 @@ TEMPLATE_CONTEXT_PROCESSORS = ( 'teleforma.context_processors.periods', ) +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [os.path.join(BASE_DIR, 'templates')], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] -# BASE_DIR = os.path.dirname(__file__) - -# TEMPLATES = [ -# { -# 'BACKEND': 'django.template.backends.django.DjangoTemplates', -# 'DIRS': [ -# os.path.join(BASE_DIR, 'templates') -# ], -# 'APP_DIRS': True, -# 'OPTIONS': { -# 'context_processors': [ -# 'django.template.context_processors.debug', -# 'django.template.context_processors.request', -# 'django.contrib.auth.context_processors.auth', -# 'django.contrib.messages.context_processors.messages', -# ], -# }, -# },] - +DEFAULT_AUTO_FIELD = 'django.db.models.AutoField' TELEMETA_ORGANIZATION = 'Pré-Barreau - CRFPA' TELEMETA_SUBJECTS = ('Barreau', 'CRFPA', 'e-learning') @@ -220,9 +219,9 @@ SESSION_EXPIRE_AT_BROWSER_CLOSE = False EMAIL_HOST = 'localhost' DEFAULT_FROM_EMAIL = 'crfpa@pre-barreau.com' SERVER_EMAIL = 'crfpa@pre-barreau.com' -EMAIL_SUBJECT_PREFIX = '[' + TELEMETA_ORGANIZATION.decode('utf8') + '] ' +EMAIL_SUBJECT_PREFIX = '[' + TELEMETA_ORGANIZATION + '] ' -POSTMAN_AUTO_MODERATE_AS=True +POSTMAN_AUTO_MODERATE_AS = True #FILE_PROTECTION_METHOD = 'xsendfile' @@ -245,10 +244,6 @@ TELECASTER_LIVE_STREAMING_PORT = 443 TELECASTER_LIVE_ICECAST_STREAMING_PORT = 8000 TELECASTER_LIVE_STREAM_M_STREAMING_PORT = 8080 -JQCHAT_DISPLAY_COUNT = 100 -JQCHAT_DISPLAY_TIME = 72 -JQCHAT_DATE_FORMAT = "D-H:i" - PASSWORD_HASHERS = [ 'django.contrib.auth.hashers.PBKDF2PasswordHasher', ] @@ -261,21 +256,17 @@ FILE_UPLOAD_TEMP_DIR = '/tmp' SESSION_ENGINE = "unique_session.backends.session_backend" UNIQUE_SESSION_WHITELIST = (1, 2042) -SOUTH_MIGRATION_MODULES = { - 'captcha': 'captcha.south_migrations', -} - -SUIT_CONFIG = { - 'ADMIN_NAME': 'TeleForma Admin', -} +# SOUTH_MIGRATION_MODULES = { +# 'captcha': 'captcha.south_migrations', +# } # Cache backend is optional, but recommended to speed up user agent parsing -#CACHES = { +# CACHES = { # 'default': { # 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', # 'LOCATION': '127.0.0.1:11211', # } -#} +# } # Name of cache backend to cache user agents. If it not specified default # cache alias will be used. Set to `None` to disable caching. @@ -291,13 +282,13 @@ TINYMCE_DEFAULT_CONFIG = { } # Sherlock's online payment -PAYMENT_SHERLOCKS_PATH='/opt/sherlocks2' -PAYMENT_PARAMETERS = { 'merchant_id' : { 'Semestrielle': "040109417200053", +PAYMENT_SHERLOCKS_PATH = '/opt/sherlocks2' +PAYMENT_PARAMETERS = {'merchant_id': {'Semestrielle': "040109417200053", 'Estivale': "040109417200054", }, - 'merchant_country': 'fr', - 'currency_code': '978', - 'language': 'fr' -} + 'merchant_country': 'fr', + 'currency_code': '978', + 'language': 'fr' + } LOGGING = { 'version': 1, @@ -328,7 +319,6 @@ LOGGING = { } -from django.utils.encoding import force_text def show_user_as(user): professor = user.professor.all() is_corrector = False @@ -336,9 +326,8 @@ def show_user_as(user): return "#"+str(user.id) else: return force_text(user) + + POSTMAN_SHOW_USER_AS = show_user_as #THUMBNAIL_FORCE_OVERWRITE = True - -JQCHAT_DISPLAY_COUNT = 50 -JQCHAT_DISPLAY_TIME = 48 diff --git a/app/urls.py b/app/urls.py index 9acecf4e..c9d8ce94 100644 --- a/app/urls.py +++ b/app/urls.py @@ -1,33 +1,32 @@ # -*- coding: utf-8 -*- -from django.conf.urls.defaults import * from django.http import HttpResponse - +from django.conf.urls import include, url +from django.views.i18n import JavaScriptCatalog # Uncomment the next two lines to enable the admin: from django.contrib import admin admin.autodiscover() -js_info_dict = { - 'packages': ('teleforma'), -} +js_info_dict = ['teleforma'] -urlpatterns = patterns('', +urlpatterns = [ # Example: # (r'^sandbox/', include('sandbox.foo.urls')), # Uncomment the admin/doc line below and add 'django.contrib.admindocs' # to INSTALLED_APPS to enable admin documentation: # (r'^admin/doc/', include('django.contrib.admindocs.urls')), - url(r'^admin/', include(admin.site.urls)), + url(r'^admin/', admin.site.urls), # TeleForma - (r'^', include('teleforma.urls')), + url(r'^', include('teleforma.urls')), # Languages - (r'^i18n/', include('django.conf.urls.i18n')), - (r'^jsi18n/$', 'django.views.i18n.javascript_catalog', js_info_dict), - (r'^robots\.txt$', lambda r: HttpResponse("User-agent: *\nDisallow: /", mimetype="text/plain")), + url(r'^i18n/', include('django.conf.urls.i18n')), + url(r'^jsi18n/$', JavaScriptCatalog.as_view(packages=js_info_dict)), + url(r'^robots\.txt$', lambda r: HttpResponse( + "User-agent: *\nDisallow: /", mimetype="text/plain")), #url(r'^pdfviewer/', include('webviewer.urls')), url(r'^pdfannotator/', include('pdfannotator.urls')), url(r'^captcha/', include('captcha.urls')), -) +] diff --git a/requirements-new.txt b/requirements-new.txt deleted file mode 100644 index ed16fa32..00000000 --- a/requirements-new.txt +++ /dev/null @@ -1,18 +0,0 @@ -django==1.4.19 -South==0.8.2 # a supprimer -django-pagination==1.0.7 # a supprimer -django-postman==3.2.0 -django-extensions==1.2.1 -django-notes==0.2.2 # a supprimer -django-timezones==0.2 -crocodoc # a supprimer -django-registration==0.8 -django-extra-views==0.6.5 # a supprimer -django-simple-captcha==0.4.6 # a maj -django-suit==0.2.12 # a maj -django-nvd3==0.8.2 -django-user-agents==0.3.0 -xhtml2pdf==0.2.5 -html5lib==1.1 -django-tinymce==1.5.4 -sorl-thumbnail==11.12 diff --git a/requirements-old.txt b/requirements-old.txt new file mode 100644 index 00000000..bf287ff2 --- /dev/null +++ b/requirements-old.txt @@ -0,0 +1,163 @@ +bigbluebutton-api-python==0.0.9 +bleach==3.3.0 +bs4==0.0.1 +cached-property==1.3.0 +celery==2.5.3 +certifi==14.5.14 +chardet==2.1.1 +cl==0.0.3 +configobj==4.7.2 +configparser==4.0.2 +crocodoc==0.1.1 +debtcollector==1.22.0 +decorator==4.0.10 +Django==1.11.29 +django-bootstrap-pagination==1.5.1 +django-bootstrap3==6.1.0 +django-cacheops==2.1.1 +django-celery==2.5.5 +django-debug-toolbar==1.0.1 +django-email-extras==0.1.13 +django-extensions==1.2.1 +django-extra-views==0.7.0 +django-forms-builder==0.8.5 +django-google-tools==1.0.0 +django-linguo==1.3.3 +django-longerusernameandemail==0.5.6 +django-markup-mixin==0.1.0 +django-model-utils==2.3.1 +django-mptt==0.5.2 +django-notes==0.2.2 +django-nvd3==0.8.2 +django-pagination==1.0.7 +django-picklefield==0.2.1 +-e git+https://git.parisson.com/git/django-postman.git@master#egg=django-postman +django-quiz-app==0.5.1 +django-redis-cache==0.13.0 +django-registration==1.0 +django-simple-captcha==0.4.6 +django-suit==0.2.12 +django-timezones==0.2 +django-tinymce==1.5.4 +-e git+https://git.parisson.com/git/django-unique-session.git@master#egg=django-unique-session +-e git+https://github.com/Parisson/django-jqchat.git@master#egg=django-jqchat +django-user-agents==0.3.0 +docopt==0.6.2 +docutils==0.8.1 +enum34==1.1.6 +feedparser==5.1.3 +filebrowser-safe==0.2.28 +fpconst==0.7.2 +funcsigs==0.4 +functools32==3.2.3.post2 +funcy==1.2 +future==0.18.2 +gdata==2.0.18 +h5py==2.10.0 +html5lib==1.1 +httplib2==0.8 +ipaddress==1.0.17 +ipython==5.1.0 +ipython-genutils==0.1.0 +iso8601==0.1.11 +Jinja2==2.7.2 +jsonpatch==1.12 +jsonpointer==1.10 +jsonschema==2.5.1 +jxmlease==1.0.3 +keyring==3.8 +keystoneauth1==2.1.0 +kombu==2.5.16 +lazr.uri==1.0.5 +lockfile==0.8 +logilab-astng==0.24.3 +logilab-common==1.4.4 +longerusername==0.4 +mailer==0.7 +Mako==0.7.0 +Markdown==2.1.1 +MarkupSafe==0.15 +mock==1.0.1 +monotonic==0.5 +msgpack-python==0.4.6 +mutagen==1.20 +mysqlclient==1.4.6 +NavAdd==0.1.1 +netaddr==0.7.18 +netifaces==0.10.4 +numpy==1.7.0 +oauth==1.0.1 +oauth2==1.5.211 +oauthlib==0.6.0 +oslo.config==3.2.0 +oslo.i18n==3.1.0 +oslo.serialization==2.2.0 +oslo.utils==3.3.0 +packaging==20.9 +paramiko==1.10.1 +pathlib2==2.1.0 +pbr==1.8.1 +pexpect==4.2.1 +pickleshare==0.7.4 +Pillow==2.5.0 +prettytable==0.7.2 +prompt-toolkit==1.0.7 +psutil==5.8.0 +ptyprocess==0.7.0 +pycosat==0.6.1 +pycrypto==2.6 +pydot==1.0.28 +pygit==0.1 +Pygments==2.1.3 +pylint==0.25.1 +pymemcache==3.4.1 +pyparsing==2.0.2 +pyPdf==1.13 +PyPDF2==1.26.0 +python-bidi==0.4.2 +python-dateutil==1.5 +python-ebml==0.2.1 +python-gnupg==0.4.7 +python-keystoneclient==2.0.0 +python-nvd3==0.13.7 +python-openid==2.2.5 +python-slugify==0.0.9 +pytz==2013.9 +pyxdg==0.19 +PyYAML==3.12 +pyzmq==13.1.0 +redis==2.10.3 +reportlab==3.3.0 +requests==2.11.1 +requests-oauthlib==0.3.3 +scipy==0.10.1 +simplegeneric==0.8.1 +simplejson==2.5.2 +six==1.10.0 +sorl-thumbnail==12.3 +South==0.8.2 +Sphinx==1.1.3 +sphinx-me==0.2.1 +sqlparse==0.1.10 +stevedore==1.32.0 +texttable==0.8.7 +traitlets==4.3.1 +ua-parser==0.3.6 +Unidecode==0.4.18 +unittest2==0.5.1 +urllib3==1.7.1 +user-agents==0.3.2 +uwsgitop==0.10 +vatnumber==1.1 +wadllib==1.3.4 +warlock==1.2.0 +wcwidth==0.1.7 +webencodings==0.5.1 +websocket-client==0.40.0 +Werkzeug==0.9.4 +wrapt==1.10.6 +xhtml2pdf==0.2.5 +xlrd==0.6.1 +xlwt==0.7.5 +zipstream==1.0.2 +zope.interface==3.6.1 diff --git a/requirements.txt b/requirements.txt index bd116e70..d6aadbb9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,134 +1,23 @@ -bigbluebutton-api-python==0.0.9 -bleach==3.3.0 -bs4==0.0.1 -cached-property==1.3.0 -certifi==14.5.14 -chardet==2.1.1 -cl==0.0.3 -configobj==4.7.2 -configparser==4.0.2 -debtcollector==1.22.0 -decorator==4.0.10 -Django==1.4.19 -django-bootstrap-pagination==1.5.1 -django-bootstrap3==6.1.0 -django-cacheops==2.1.1 -django-celery==2.5.5 -django-debug-toolbar==1.0.1 -django-email-extras==0.1.13 -django-extensions==1.2.1 -django-extra-views==0.7.0 -django-forms-builder==0.8.5 -django-google-tools==1.0.0 -django-json-rpc==0.6.1 -django-linguo==1.3.3 -django-longerusernameandemail==0.5.6 -django-markup-mixin==0.1.0 -django-model-utils==2.3.1 +bigbluebutton-api-python==0.0.11 +docutils==0.17.1 +Django==3.2.3 +# django-extensions==1.2.1 +# django-timezones==0.2 +# django-registration==3.1.2 +django-json-rpc==0.7.1 +# django-google-tools==1.1.0 django-nvd3==0.8.2 -django-pagination==1.0.7 -django-picklefield==0.2.1 --e git+https://git.parisson.com/git/django-postman.git@master#egg=django-postman -django-quiz-app==0.5.1 -django-redis-cache==0.13.0 -django-registration==1.0 -django-simple-captcha==0.4.6 -django-suit==0.2.12 -django-timezones==0.2 -django-tinymce==1.5.4 --e git+https://git.parisson.com/git/django-unique-session.git@master#egg=django-unique-session --e git+https://github.com/Parisson/django-jqchat.git@master#egg=django-jqchat -django-user-agents==0.3.0 -docopt==0.6.2 -docutils==0.8.1 -enum34==1.1.6 -feedparser==5.1.3 -filebrowser-safe==0.2.28 -fpconst==0.7.2 -funcsigs==0.4 -functools32==3.2.3.post2 -funcy==1.2 -future==0.18.2 -gdata==2.0.18 -html5lib==1.1 -httplib2==0.8 -ipaddress==1.0.17 -ipython==5.1.0 -ipython-genutils==0.1.0 -iso8601==0.1.11 -Jinja2==2.7.2 -jsonpatch==1.12 -jsonpointer==1.10 -jsonschema==2.5.1 +django-postman==4.2 +django-simple-captcha==0.5.14 # a maj +django-tinymce==3.3.0 +django-user-agents==0.4.0 jxmlease==1.0.3 -keyring==3.8 -keystoneauth1==2.1.0 -kombu==2.5.16 -lazr.uri==1.0.5 -lockfile==0.8 -longerusername==0.4 -mailer==0.7 -Mako==0.7.0 -Markdown==2.1.1 -MarkupSafe==0.15 -mock==1.0.1 -monotonic==0.5 -msgpack-python==0.4.6 -mysqlclient==1.4.6 -NavAdd==0.1.1 -netaddr==0.7.18 -netifaces==0.10.4 -numpy==1.7.0 -packaging==20.9 -pathlib2==2.1.0 -pbr==1.8.1 -pexpect==4.2.1 -pickleshare==0.7.4 -Pillow==2.5.0 -prettytable==0.7.2 -prompt-toolkit==1.0.7 -psutil==5.8.0 -ptyprocess==0.7.0 -pylint==0.25.1 -pymemcache==3.4.1 -pyparsing==2.0.2 -pyPdf==1.13 -PyPDF2==1.26.0 -python-bidi==0.4.2 -python-dateutil==1.5 -python-ebml==0.2.1 -python-keystoneclient==2.0.0 -python-nvd3==0.13.7 -python-openid==2.2.5 -python-slugify==0.0.9 -pytz==2013.9 -pyxdg==0.19 -PyYAML==3.12 -pyzmq==13.1.0 -redis==2.10.3 -reportlab==3.3.0 -simplegeneric==0.8.1 -simplejson==2.5.2 -sorl-thumbnail==12.3 -South==0.8.2 -sqlparse==0.1.10 -stevedore==1.32.0 -texttable==0.8.7 -ua-parser==0.3.6 -Unidecode==0.4.18 -unittest2==0.5.1 -urllib3==1.7.1 -user-agents==0.3.2 -uwsgitop==0.10 -vatnumber==1.1 -wadllib==1.3.4 -warlock==1.2.0 -wcwidth==0.1.7 -webencodings==0.5.1 -Werkzeug==0.9.4 -wrapt==1.10.6 +mysqlclient==2.0.3 +numpy==1.20.3 +# django-user-agents==0.3.0 +# html5lib==1.1 +sorl-thumbnail==12.7.0 +unidecode==1.2.0 xhtml2pdf==0.2.5 -xlrd==0.6.1 -xlwt==0.7.5 -zipstream==1.0.2 -zope.interface==3.6.1 +xlrd==2.0.1 +xlwt==1.3.0 \ No newline at end of file diff --git a/teleforma/admin.py b/teleforma/admin.py index a9cb028d..89914cca 100644 --- a/teleforma/admin.py +++ b/teleforma/admin.py @@ -1,19 +1,28 @@ # -*- coding: utf-8 -*- -from teleforma.models import * -from teleforma.views import * -from teleforma.exam.models import * -from teleforma.exam.admin import * -from teleforma.templatetags.teleforma_tags import to_recipients +import csv +import datetime + from django.contrib import admin -from django.contrib.auth.models import User, Group -from django.contrib.auth.admin import UserAdmin from django.contrib.admin import SimpleListFilter -from django.utils.translation import ugettext_lazy as _ -from django.http import HttpResponse +from django.contrib.auth.admin import UserAdmin +from django.contrib.auth.models import User from django.core import serializers -from django.contrib.admin.helpers import ActionForm -from django import forms -import csv +from django.http import HttpResponse +from django.utils.translation import ugettext_lazy as _ + +from .exam.admin import QuotaInline +from .models.appointment import (Appointment, AppointmentJury, + AppointmentPeriod, AppointmentSlot) +from .models.core import (Conference, Course, CourseType, Department, Document, + DocumentSimple, DocumentType, LiveStream, Media, + MediaTranscoded, Organization, Period, Professor, + Room, StreamingServer) +from .models.crfpa import (IEJ, Corrector, Discount, Home, NewsItem, + OptionalFee, Parameters, Payback, Payment, Profile, + Student, Training) +from .models.messages import GroupedMessage, StudentGroup +from .views.crfpa import CorrectorXLSBook, UserXLSBook + class PeriodListFilter(SimpleListFilter): @@ -31,7 +40,7 @@ class PeriodListFilter(SimpleListFilter): in the right sidebar. """ - return ( (period.name, period.name) for period in Period.objects.all() ) + return ((period.name, period.name) for period in Period.objects.all()) def queryset(self, request, queryset): """ @@ -48,30 +57,36 @@ class PeriodListFilter(SimpleListFilter): class PaymentInline(admin.StackedInline): model = Payment + class OptionalFeeInline(admin.StackedInline): model = OptionalFee extra = 1 + class DiscountInline(admin.StackedInline): model = Discount extra = 1 + class PaybackInline(admin.StackedInline): model = Payback extra = 1 + class StudentInline(admin.StackedInline): model = Student extra = 1 -#TODO fix max_length +# TODO fix max_length # class StudentGroupForm(ActionForm): # group_name = forms.CharField(_('Group'), required=False) + class StudentGroupAdmin(admin.ModelAdmin): model = StudentGroup filter_horizontal = ['students'] + class BalanceFilter(admin.SimpleListFilter): title = _(u'balance') @@ -86,9 +101,9 @@ class BalanceFilter(admin.SimpleListFilter): human-readable name for the option that will appear in the right sidebar. """ - return [ ('ltz', u'négative'), - ('eqz', u'zéro'), - ('gtz', u'positive') ] + return [('ltz', u'négative'), + ('eqz', u'zéro'), + ('gtz', u'positive')] def queryset(self, request, queryset): """ @@ -98,11 +113,11 @@ class BalanceFilter(admin.SimpleListFilter): """ value = self.value() if value == 'ltz': - return queryset.filter(balance__lt = 0) + return queryset.filter(balance__lt=0) elif value == 'eqz': - return queryset.filter(balance = 0) + return queryset.filter(balance=0) elif value == 'gtz': - return queryset.filter(balance__gt = 0) + return queryset.filter(balance__gt=0) else: return queryset @@ -114,16 +129,16 @@ class StudentAdmin(admin.ModelAdmin): inlines = [PaymentInline, OptionalFeeInline, DiscountInline, PaybackInline] search_fields = ['user__first_name', 'user__last_name', 'user__username'] list_filter = ['user__is_active', 'restricted', 'is_subscribed', 'platform_only', PeriodListFilter, - 'trainings', 'iej', 'procedure', 'written_speciality', 'oral_speciality', - 'oral_1', 'oral_2', 'fascicule', BalanceFilter ] + 'trainings', 'iej', 'procedure', 'written_speciality', 'oral_speciality', + 'oral_1', 'oral_2', 'fascicule', BalanceFilter] list_display = ['student_name', 'restricted', 'get_trainings', 'platform_only', 'total_payments', 'total_fees', 'balance', 'balance_intermediary'] - readonly_fields = [ 'balance', 'balance_intermediary' ] + readonly_fields = ['balance', 'balance_intermediary'] actions = ['export_xls', 'write_message', 'add_to_group'] # action_form = StudentGroupForm def get_trainings(self, instance): - return ' - '.join([unicode(training) for training in instance.trainings.all()]) + return ' - '.join([str(training) for training in instance.trainings.all()]) def student_name(self, instance): return instance.user.last_name + ' ' + instance.user.first_name @@ -139,7 +154,7 @@ class StudentAdmin(admin.ModelAdmin): return response def export_xls(self, request, queryset): - book = UserXLSBook(students = queryset) + book = UserXLSBook(students=queryset) book.write() response = HttpResponse(mimetype="application/vnd.ms-excel") response['Content-Disposition'] = 'attachment; filename=users.xls' @@ -160,13 +175,14 @@ class StudentAdmin(admin.ModelAdmin): class CorrectorAdmin(admin.ModelAdmin): model = Corrector list_filter = ['user__is_active', 'period'] - list_display = ['__unicode__', 'period', 'pay_status', + list_display = ['__str__', 'period', 'pay_status', 'date_registered'] actions = ['export_xls'] - search_fields = ['user__username', 'user__first_name', 'user__last_name', 'user__email'] + search_fields = ['user__username', 'user__first_name', + 'user__last_name', 'user__email'] def export_xls(self, request, queryset): - book = CorrectorXLSBook(correctors = queryset) + book = CorrectorXLSBook(correctors=queryset) book.write() response = HttpResponse(mimetype="application/vnd.ms-excel") response['Content-Disposition'] = 'attachment; filename=correcteurs.xls' @@ -175,6 +191,7 @@ class CorrectorAdmin(admin.ModelAdmin): export_xls.short_description = "Export vers XLS" + class ProfessorProfileInline(admin.StackedInline): model = Professor filter_horizontal = ['courses'] @@ -197,7 +214,7 @@ class UserProfileAdmin(UserAdmin): class TrainingAdmin(admin.ModelAdmin): model = Training filter_horizontal = ['synthesis_note', 'obligation', 'procedure', 'oral_speciality', - 'written_speciality', 'oral_1', 'oral_2','magistral'] + 'written_speciality', 'oral_1', 'oral_2', 'magistral'] exclude = ['options'] @@ -212,6 +229,7 @@ class DocumentAdmin(admin.ModelAdmin): list_filter = ('course', 'periods', 'date_added', 'type') search_fields = ['course__code', 'course__title', 'type__name'] + class ConferenceDateBeginFilter(admin.SimpleListFilter): title = _(u'date de début') @@ -227,9 +245,9 @@ class ConferenceDateBeginFilter(admin.SimpleListFilter): in the right sidebar. """ conferences = Conference.objects.all() - dates = [ c.date_begin.date() for c in conferences if c.date_begin ] + dates = [c.date_begin.date() for c in conferences if c.date_begin] dates = set(dates) - res = [ ( d.strftime('%Y%m%d'), d.strftime('%d/%m/%Y')) for d in dates ] + res = [(d.strftime('%Y%m%d'), d.strftime('%d/%m/%Y')) for d in dates] return sorted(res)[::-1] def queryset(self, request, queryset): @@ -240,16 +258,19 @@ class ConferenceDateBeginFilter(admin.SimpleListFilter): """ value = self.value() if value: - date = datetime.date(int(value[:4]), int(value[4:6]), int(value[6:])) + date = datetime.date(int(value[:4]), int( + value[4:6]), int(value[6:])) rng = (datetime.datetime.combine(date, datetime.time.min), datetime.datetime.combine(date, datetime.time.max)) - return queryset.filter(conference__date_begin__range = rng) + return queryset.filter(conference__date_begin__range=rng) else: return queryset + class MediaTranscodedInline(admin.TabularInline): model = MediaTranscoded + class MediaAdmin(admin.ModelAdmin): exclude = ['readers'] search_fields = ['id', 'title', 'course__title', 'course__code'] @@ -260,22 +281,26 @@ class MediaAdmin(admin.ModelAdmin): class ConferenceAdmin(admin.ModelAdmin): exclude = ['readers'] list_filter = ('course', 'period', 'date_begin', 'session') - search_fields = ['public_id', 'id', 'course__code', 'course__title', 'session'] + search_fields = ['public_id', 'id', + 'course__code', 'course__title', 'session'] class HomeAdmin(admin.ModelAdmin): list_filter = ('enabled',) - search_fields = [ 'periods__name', 'title', 'text' ] + search_fields = ['periods__name', 'title', 'text'] list_display = ('title', 'enabled', 'modified_at') readonly_fields = ('modified_at',) + def get_form(self, request, obj=None, **kwargs): form = super(HomeAdmin, self).get_form(request, obj, **kwargs) form.base_fields['video'].queryset = Media.objects.filter(type='webm') return form + class ParametersAdmin(admin.ModelAdmin): pass + class NewsItemAdmin(admin.ModelAdmin): list_filter = ('deleted', 'course', 'creator') list_display = ('title', 'course', 'creator', 'deleted') @@ -296,7 +321,8 @@ class AppointmentJuryInline(admin.StackedInline): class AppointmentPeriodAdmin(admin.ModelAdmin): - list_display = ('name', 'periods_names', 'start', 'end', 'enable_appointment') + list_display = ('name', 'periods_names', 'start', + 'end', 'enable_appointment') # inlines = [ AppointmentDayInline ] @@ -313,9 +339,11 @@ class AppointmentPeriodAdmin(admin.ModelAdmin): class AppointmentSlotAdmin(admin.ModelAdmin): list_filter = ('date', 'appointment_period') - list_display = ('date', 'appointment_period', 'mode', 'start', 'nb', 'get_nb_jury') + list_display = ('date', 'appointment_period', + 'mode', 'start', 'nb', 'get_nb_jury') inlines = [AppointmentJuryInline] + class AppointmentJuryAdmin(admin.ModelAdmin): list_filter = ('slot',) list_display = ('name', 'slot') @@ -332,19 +360,19 @@ class AppointmentAdmin(admin.ModelAdmin): response['Content-Disposition'] = 'attachment; filename=rendezvous.csv' writer = csv.writer(response) - writer.writerow(['date', 'creneau', 'nom', 'prenom', 'email', 'iej', 'jury', 'mode']) + writer.writerow(['date', 'creneau', 'nom', 'prenom', + 'email', 'iej', 'jury', 'mode']) + def csv_encode(item): - if isinstance(item, unicode): - return item.encode('utf-8') - else: - return item + return item for app in queryset: user = app.student student = user.student.all()[0] - row = [ app.day.strftime('%d/%m/%Y'), app.start, user.last_name, user.first_name, user.email, student.iej, app.jury.name, app.slot.mode ] - row = [ csv_encode(col) for col in row ] + row = [app.day.strftime('%d/%m/%Y'), app.start, user.last_name, + user.first_name, user.email, student.iej, app.jury.name, app.slot.mode] + row = [csv_encode(col) for col in row] writer.writerow(row) @@ -382,5 +410,3 @@ admin.site.register(AppointmentPeriod, AppointmentPeriodAdmin) admin.site.register(AppointmentSlot, AppointmentSlotAdmin) admin.site.register(AppointmentJury, AppointmentJuryAdmin) admin.site.register(Appointment, AppointmentAdmin) - - diff --git a/teleforma/decorators.py b/teleforma/decorators.py index cdeb87a0..382bef18 100644 --- a/teleforma/decorators.py +++ b/teleforma/decorators.py @@ -1,11 +1,12 @@ -import urlparse +import urllib.parse as urlparse from functools import wraps + from django.conf import settings from django.contrib.auth import REDIRECT_FIELD_NAME -from django.core.exceptions import PermissionDenied -from django.utils.decorators import available_attrs from django.shortcuts import redirect -from teleforma.models.crfpa import Student + +from .models.crfpa import Student + def user_passes_test(login_url=None, redirect_field_name=REDIRECT_FIELD_NAME): """ @@ -15,7 +16,7 @@ def user_passes_test(login_url=None, redirect_field_name=REDIRECT_FIELD_NAME): """ def decorator(view_func): - @wraps(view_func, assigned=available_attrs(view_func)) + @wraps(view_func) def _wrapped_view(request, *args, **kwargs): user = request.user restricted = False @@ -28,7 +29,7 @@ def user_passes_test(login_url=None, redirect_field_name=REDIRECT_FIELD_NAME): restricted = student.restricted if not restricted: return view_func(request, *args, **kwargs) - + if restricted: return redirect('teleforma-unauthorized') @@ -36,10 +37,10 @@ def user_passes_test(login_url=None, redirect_field_name=REDIRECT_FIELD_NAME): # If the login url is the same scheme and net location then just # use the path as the "next" url. login_scheme, login_netloc = urlparse.urlparse(login_url or - settings.LOGIN_URL)[:2] + settings.LOGIN_URL)[:2] current_scheme, current_netloc = urlparse.urlparse(path)[:2] if ((not login_scheme or login_scheme == current_scheme) and - (not login_netloc or login_netloc == current_netloc)): + (not login_netloc or login_netloc == current_netloc)): path = request.get_full_path() from django.contrib.auth.views import redirect_to_login return redirect_to_login(path, login_url, redirect_field_name) @@ -61,8 +62,6 @@ def access_required(function=None, redirect_field_name=REDIRECT_FIELD_NAME, logi return actual_decorator - - # def access_required(function=None, redirect_field_name=REDIRECT_FIELD_NAME, login_url=None): # """ # Decorator for views that checks that the user is logged in, redirecting diff --git a/teleforma/exam/admin.py b/teleforma/exam/admin.py index cdcde77b..d2759f4e 100644 --- a/teleforma/exam/admin.py +++ b/teleforma/exam/admin.py @@ -1,9 +1,6 @@ # -*- coding: utf-8 -*- -from teleforma.admin import * -from teleforma.exam.models import * +from ..exam.models import Quota, Script, ScriptPage, ScriptType from django.contrib import admin -from django.contrib.auth.models import User -from django.contrib.auth.admin import UserAdmin from django.template.defaultfilters import filesizeformat diff --git a/teleforma/exam/forms.py b/teleforma/exam/forms.py index 1f578462..278624bc 100644 --- a/teleforma/exam/forms.py +++ b/teleforma/exam/forms.py @@ -1,7 +1,10 @@ -from django.forms import ModelForm, HiddenInput -from teleforma.exam.models import * -from django.forms.models import inlineformset_factory +from django import forms +from django.conf import settings from django.core.exceptions import ValidationError +from django.forms import ModelForm +from ..exam.models import Script +from ..models.core import get_n_choices + def validate_session(nb): def validator(value): @@ -9,6 +12,7 @@ def validate_session(nb): raise ValidationError(u'session invalide: %s' % value) return validator + class ScriptForm(ModelForm): def __init__(self, *args, **kwargs): @@ -16,15 +20,15 @@ class ScriptForm(ModelForm): super(ScriptForm, self).__init__(*args, **kwargs) self.fields['score'].localize = True nb = period.nb_script or settings.TELEFORMA_EXAM_MAX_SESSIONS - self.fields['session'] = forms.ChoiceField(choices = get_n_choices(nb + 1), - validators = [ validate_session(nb) ]) + self.fields['session'] = forms.ChoiceField(choices=get_n_choices(nb + 1), + validators=[validate_session(nb)]) self.fields['file'].required = True class Meta: model = Script exclude = ['uuid', 'mime_type', 'sha1', 'url', 'type' - 'date_submitted', 'date_rejected', 'date_marked', - 'box_uuid',] + 'date_submitted', 'date_rejected', 'date_marked', + 'box_uuid', ] #hidden_fields = ['status'] @@ -34,6 +38,7 @@ class ScoreForm(ScriptForm): self.fields['file'].required = False self.fields['score'].required = True + class MassScoreForm(ScoreForm): def __init__(self, *args, **kwargs): super(MassScoreForm, self).__init__(*args, **kwargs) @@ -45,7 +50,7 @@ class MassScoreForm(ScoreForm): errors = {} valid = [] - + for key in self.data.keys(): if key.startswith('student'): student = self.data[key] diff --git a/teleforma/exam/models.py b/teleforma/exam/models.py index 8f80ea22..bb19e87a 100755 --- a/teleforma/exam/models.py +++ b/teleforma/exam/models.py @@ -35,25 +35,31 @@ """ from __future__ import division -import os, uuid, time, hashlib, mimetypes, tempfile, datetime, urllib -from django.db import models +import datetime +import mimetypes +import os +import urllib +import uuid + +from django.conf import settings from django.contrib.auth.models import User from django.contrib.sites.models import Site -from django.db.models import Q, Max, Min +from django.db import models from django.db.models.signals import post_save -from django.conf import settings -from django.utils.translation import ugettext, ugettext_lazy as _ -from django.template.defaultfilters import slugify - -from teleforma.models import * from django.template.loader import render_to_string -from postman.utils import email_visitor, notify_user +from django.urls.base import reverse_lazy +from django.utils.translation import ugettext +from django.utils.translation import ugettext_lazy as _ from postman.models import Message +from postman.utils import notify_user +from teleforma.models.core import Course, Period +from ..models.core import session_choices app = 'teleforma' + class MetaCore: app_label = 'exam' @@ -77,22 +83,22 @@ class MetaCore: # box_client = Client(oauth) SCRIPT_STATUS = ((0, _('rejected')), (1, _('draft')), (2, _('submitted')), - (3, _('pending')),(4, _('marked')), (5, _('read')), - (6, _('backup')), (7, _('stat')) ) + (3, _('pending')), (4, _('marked')), (5, _('read')), + (6, _('backup')), (7, _('stat'))) REJECT_REASON = (('unreadable', _('unreadable')), - ('bad orientation', _('bad orientation')), - ('bad framing', _('bad framing')), - ('incomplete', _('incomplete')), - ('wrong course', _('wrong course')), - ('duplicate', _('duplicate')), - ('other', _('other')), - ('wrong format', _('wrong format')), - ('unreadable file', _('unreadable file')), - ('no file', _('no file')), - ('error retrieving file', _('error retrieving file')), - ('file too large', _('file too large')), - ) + ('bad orientation', _('bad orientation')), + ('bad framing', _('bad framing')), + ('incomplete', _('incomplete')), + ('wrong course', _('wrong course')), + ('duplicate', _('duplicate')), + ('other', _('other')), + ('wrong format', _('wrong format')), + ('unreadable file', _('unreadable file')), + ('no file', _('no file')), + ('error retrieving file', _('error retrieving file')), + ('file too large', _('file too large')), + ) cache_path = settings.MEDIA_ROOT + 'cache/' script_path = settings.MEDIA_ROOT + 'scripts/' @@ -117,9 +123,11 @@ def sha1sum_file(filename): sha1.update(chunk) return sha1.hexdigest() + def mimetype_file(path): return mimetypes.guess_type(path)[0] + def check_unique_mimetype(l): i = 0 for d in l: @@ -135,56 +143,62 @@ def check_unique_mimetype(l): class Quota(models.Model): - course = models.ForeignKey(Course, related_name="quotas", verbose_name=_('course'), null=True, blank=True, on_delete=models.SET_NULL) - corrector = models.ForeignKey(User, related_name="quotas", verbose_name=_('corrector'), null=True, blank=True, on_delete=models.SET_NULL) - period = models.ForeignKey(Period, related_name='quotas', verbose_name=_('period'), null=True, blank=True, on_delete=models.SET_NULL) - session = models.CharField(_('session'), choices=session_choices, max_length=16, default="1") + course = models.ForeignKey(Course, related_name="quotas", verbose_name=_( + 'course'), null=True, blank=True, on_delete=models.SET_NULL) + corrector = models.ForeignKey(User, related_name="quotas", verbose_name=_( + 'corrector'), null=True, blank=True, on_delete=models.SET_NULL) + period = models.ForeignKey(Period, related_name='quotas', verbose_name=_( + 'period'), null=True, blank=True, on_delete=models.SET_NULL) + session = models.CharField( + _('session'), choices=session_choices, max_length=16, default="1") value = models.IntegerField(_('value')) date_start = models.DateField(_('date start')) date_end = models.DateField(_('date end')) - script_type = models.ForeignKey('ScriptType', related_name='quotas', verbose_name=_('type'), null=True, blank=True, on_delete=models.SET_NULL) + script_type = models.ForeignKey('ScriptType', related_name='quotas', verbose_name=_( + 'type'), null=True, blank=True, on_delete=models.SET_NULL) class Meta(MetaCore): verbose_name = _('Quota') verbose_name_plural = _('Quotas') ordering = ['-date_end'] - def __unicode__(self): - title = ' - '.join([unicode(self.corrector), self.course.title, + def __str__(self): + title = ' - '.join([str(self.corrector), self.course.title, str(self.all_script_count) + '/' + str(self.value)]) if self.script_type: - title = ' - '.join([title, unicode(self.script_type)]) + title = ' - '.join([title, str(self.script_type)]) if self.date_start: - title = ' - '.join([title, unicode(self.date_start)]) + title = ' - '.join([title, str(self.date_start)]) if self.date_end: - title = ' - '.join([title, unicode(self.date_end)]) + title = ' - '.join([title, str(self.date_end)]) return title def script_count(self, statuses): if self.corrector: - q = self.corrector.corrector_scripts.filter(status__in = statuses) + q = self.corrector.corrector_scripts.filter(status__in=statuses) q = q.filter(course=self.course) q = q.filter(period=self.period) q = q.filter(session=self.session) # Careful, MySQL considers '2019-07-28 11:42:00" to not be >= "2019-07-28" start = self.date_start - end = self.date_end + datetime.timedelta(days = 1) - q = q.filter(date_submitted__gte=start).filter(date_submitted__lte=end) + end = self.date_end + datetime.timedelta(days=1) + q = q.filter(date_submitted__gte=start).filter( + date_submitted__lte=end) return q.count() else: return 0 @property def all_script_count(self): - return self.script_count(statuses = (3,4,5)) + return self.script_count(statuses=(3, 4, 5)) @property def pending_script_count(self): - return self.script_count(statuses = (3,)) + return self.script_count(statuses=(3,)) @property def marked_script_count(self): - return self.script_count(statuses = (4,5)) + return self.script_count(statuses=(4, 5)) @property def level(self): @@ -201,9 +215,11 @@ class Quota(models.Model): class BaseResource(models.Model): date_added = models.DateTimeField(_('date added'), auto_now_add=True) - date_modified = models.DateTimeField(_('date modified'), auto_now=True, null=True) + date_modified = models.DateTimeField( + _('date modified'), auto_now=True, null=True) uuid = models.CharField(_('UUID'), blank=True, max_length=512) - mime_type = models.CharField(_('MIME type'), max_length=128, blank=True, null=True) + mime_type = models.CharField( + _('MIME type'), max_length=128, blank=True, null=True) sha1 = models.CharField(_('sha1'), blank=True, max_length=512) class Meta(MetaCore): @@ -214,15 +230,18 @@ class BaseResource(models.Model): self.uuid = uuid.uuid4() super(BaseResource, self).save(*args, **kwargs) - def __unicode__(self): - return unicode(self.uuid) + def __str__(self): + return str(self.uuid) class ScriptPage(BaseResource): - script = models.ForeignKey('Script', related_name='pages', verbose_name=_('script'), blank=True, null=True) - file = models.FileField(_('Page file'), upload_to='script_pages/%Y/%m/%d', max_length=1024, blank=True) - image = models.ImageField(_('Image file'), upload_to='script_pages/%Y/%m/%d', blank=True) + script = models.ForeignKey('Script', related_name='pages', verbose_name=_( + 'script'), blank=True, null=True, on_delete=models.SET_NULL) + file = models.FileField( + _('Page file'), upload_to='script_pages/%Y/%m/%d', max_length=1024, blank=True) + image = models.ImageField( + _('Image file'), upload_to='script_pages/%Y/%m/%d', blank=True) rank = models.IntegerField(_('rank'), blank=True, null=True) class Meta(MetaCore): @@ -232,55 +251,68 @@ class ScriptPage(BaseResource): class ScriptType(models.Model): - name = models.CharField(_('name'), max_length='512', blank=True) + name = models.CharField(_('name'), max_length=512, blank=True) class Meta: verbose_name = _('ScriptType') verbose_name_plural = _('ScriptTypes') - def __unicode__(self): + def __str__(self): return self.name class Script(BaseResource): - course = models.ForeignKey(Course, related_name="scripts", verbose_name=_('course'), null=True, on_delete=models.SET_NULL) + course = models.ForeignKey(Course, related_name="scripts", verbose_name=_( + 'course'), null=True, on_delete=models.SET_NULL) period = models.ForeignKey(Period, related_name='scripts', verbose_name=_('period'), - null=True, blank=True, on_delete=models.SET_NULL) + null=True, blank=True, on_delete=models.SET_NULL) session = models.CharField(_('session'), max_length=16, default="1") - type = models.ForeignKey(ScriptType, related_name='scripts', verbose_name=_('type'), null=True, blank=True, on_delete=models.SET_NULL) - author = models.ForeignKey(User, related_name="author_scripts", verbose_name=_('author'), null=True, blank=True, on_delete=models.SET_NULL) - corrector = models.ForeignKey(User, related_name="corrector_scripts", verbose_name=_('corrector'), blank=True, null=True, on_delete=models.SET_NULL) - file = models.FileField(_('PDF file'), upload_to='scripts/%Y/%m/%d', max_length=1024, blank=True) - box_uuid = models.CharField(_('Box UUID'), max_length='256', blank=True) - score = models.FloatField(_('score'), blank=True, null=True, help_text="/20") + type = models.ForeignKey(ScriptType, related_name='scripts', verbose_name=_( + 'type'), null=True, blank=True, on_delete=models.SET_NULL) + author = models.ForeignKey(User, related_name="author_scripts", verbose_name=_( + 'author'), null=True, blank=True, on_delete=models.SET_NULL) + corrector = models.ForeignKey(User, related_name="corrector_scripts", verbose_name=_( + 'corrector'), blank=True, null=True, on_delete=models.SET_NULL) + file = models.FileField( + _('PDF file'), upload_to='scripts/%Y/%m/%d', max_length=1024, blank=True) + box_uuid = models.CharField(_('Box UUID'), max_length=256, blank=True) + score = models.FloatField(_('score'), blank=True, + null=True, help_text="/20") comments = models.TextField(_('comments'), blank=True) - status = models.IntegerField(_('status'), choices=SCRIPT_STATUS, blank=True) - reject_reason = models.CharField(_('reason'), choices=REJECT_REASON, max_length='256', blank=True) - date_submitted = models.DateTimeField(_('date submitted'), null=True, blank=True) + status = models.IntegerField( + _('status'), choices=SCRIPT_STATUS, blank=True) + reject_reason = models.CharField( + _('reason'), choices=REJECT_REASON, max_length=256, blank=True) + date_submitted = models.DateTimeField( + _('date submitted'), null=True, blank=True) date_marked = models.DateTimeField(_('date marked'), null=True, blank=True) - date_rejected = models.DateTimeField(_('date rejected'), null=True, blank=True) - url = models.CharField(_('URL'), max_length='2048', blank=True) + date_rejected = models.DateTimeField( + _('date rejected'), null=True, blank=True) + url = models.CharField(_('URL'), max_length=2048, blank=True) @property def title(self): if self.type: - title = [self.course.title, self.type.name, _("Session") + ' ' + self.session, unicode(self.date_added)] + title = [self.course.title, self.type.name, _( + "Session") + ' ' + self.session, str(self.date_added)] else: - title = [self.course.title, _("Session") + ' ' + self.session, unicode(self.date_added)] + title = [self.course.title, _( + "Session") + ' ' + self.session, str(self.date_added)] if self.author: - title.append(unicode(self.author.first_name) + ' ' + unicode(self.author.last_name)) + title.append(str(self.author.first_name) + + ' ' + str(self.author.last_name)) return ' - '.join(title) - def __unicode__(self): - return unicode(self.title) + def __str__(self): + return str(self.title) def get_absolute_url(self): period_id = None if self.period: period_id = self.period.id return reverse_lazy('teleforma-exam-script-detail', - kwargs={'pk':self.id, 'period_id': period_id}) + kwargs={'pk': self.id, 'period_id': period_id}) class Meta(MetaCore): verbose_name = _('Script') @@ -293,11 +325,11 @@ class Script(BaseResource): quota_list = [] all_quotas = self.course.quotas.filter(date_start__lte=self.date_submitted, - date_end__gte=self.date_submitted, - session=self.session, - period=self.period) + date_end__gte=self.date_submitted, + session=self.session, + period=self.period) - ## Commented to not filter by type anymore + # Commented to not filter by type anymore # quotas = all_quotas.filter(script_type=self.type) # if not quotas: # quotas = all_quotas.filter(script_type=None) @@ -307,11 +339,11 @@ class Script(BaseResource): if quotas: for quota in quotas: if quota.value: - quota_list.append({'obj':quota, 'level': quota.level}) + quota_list.append({'obj': quota, 'level': quota.level}) lower_quota = sorted(quota_list, key=lambda k: k['level'])[0] self.corrector = lower_quota['obj'].corrector else: - #FIXME: default corrector goes to settings + # FIXME: default corrector goes to settings self.corrector = User.objects.filter(is_superuser=True)[1] self.status = 3 @@ -349,7 +381,7 @@ class Script(BaseResource): self.mark() elif self.status == 0 and self.reject_reason: self.reject() - #HOTFIX + # HOTFIX if not self.mime_type: self.mime_type = 'application/pdf' super(Script, self).save(*args, **kwargs) @@ -362,32 +394,34 @@ class Script(BaseResource): old_abs_list = old_abs.split(os.sep) old_abs_root = old_abs_list[:-1] - old_rel = unicode(self.file) + old_rel = str(self.file) old_rel_list = old_rel.split(os.sep) old_rel_root = old_rel_list[:-1] filename, ext = os.path.splitext(old_abs_list[-1]) - new_abs = os.sep.join(old_abs_root) + os.sep + unicode(self.uuid) + ext - new_rel = os.sep.join(old_rel_root) + os.sep + unicode(self.uuid) + ext + new_abs = os.sep.join(old_abs_root) + os.sep + str(self.uuid) + ext + new_rel = os.sep.join(old_rel_root) + os.sep + str(self.uuid) + ext if not os.path.exists(new_abs): os.symlink(old_abs, new_abs) if not self.url: - self.url = settings.MEDIA_URL + unicode(new_rel) + self.url = settings.MEDIA_URL + str(new_rel) @property def safe_url(self): domain = Site.objects.get_current().domain url = self.url - url = url.replace('http://e-learning.crfpa.pre-barreau.com', '//' + domain) + url = url.replace( + 'http://e-learning.crfpa.pre-barreau.com', '//' + domain) return urllib.quote(url) def unquoted_url(self): domain = Site.objects.get_current().domain url = self.url - url = url.replace('http://e-learning.crfpa.pre-barreau.com', '//' + domain) + url = url.replace( + 'http://e-learning.crfpa.pre-barreau.com', '//' + domain) return url def has_annotations_file(self): @@ -396,39 +430,6 @@ class Script(BaseResource): """ return os.path.exists(os.path.join(settings.WEBVIEWER_ANNOTATIONS_PATH, self.uuid+'.xfdf')) - def box_upload(self): - sleep = 10 - max_loop = 12 - loop = 0 - - self.box_uuid = crocodoc.document.upload(url=self.url) - - while True: - statuses = crocodoc.document.status([self.box_uuid,]) - if (len(statuses) != 0): - if (statuses[0].get('error') == None): - if statuses[0]['status'] == 'DONE': - self.box_upload_done = 1 - self.save() - break - else: - loop += 1 - time.sleep(sleep) - if loop > max_loop: - break - else: - print 'File upload failed :(' - print ' Error Message: ' + statuses[0]['error'] - if 'too large' in statuses[0]['error']: - self.auto_reject('file too large') - elif 'retrieving file' in statuses[0]['error']: - self.auto_reject('error retrieving file') - break - else: - print 'failed :(' - print ' Statuses were not returned.' - break - def auto_reject(self, mess): self.reject_reason = mess self.status = 0 @@ -475,7 +476,8 @@ class Script(BaseResource): a = ugettext('Script') v = ugettext('marked') subject = '%s %s' % (a, v) - mess = Message(sender=self.corrector, recipient=self.author, subject=subject[:119], body=text) + mess = Message(sender=self.corrector, recipient=self.author, + subject=subject[:119], body=text) mess.moderation_status = 'a' mess.save() site = Site.objects.all()[0] @@ -490,7 +492,8 @@ class Script(BaseResource): a = ugettext('Script') v = ugettext('rejected') subject = '%s %s' % (a, v) - mess = Message(sender=self.corrector, recipient=self.author, subject=subject[:119], body=text) + mess = Message(sender=self.corrector, recipient=self.author, + subject=subject[:119], body=text) mess.moderation_status = 'a' mess.save() notify_user(mess, 'acceptance', site) @@ -504,7 +507,7 @@ def set_file_properties(sender, instance, **kwargs): if mime_type: instance.mime_type = mimetype_file(instance.file.path) trig_save = True - #HOTFIX + # HOTFIX else: instance.mime_type = 'application/pdf' if not instance.sha1: @@ -527,5 +530,7 @@ def set_file_properties(sender, instance, **kwargs): # instance.image = path -post_save.connect(set_file_properties, sender=Script, dispatch_uid="script_post_save") -post_save.connect(set_file_properties, sender=ScriptPage, dispatch_uid="scriptpage_post_save") +post_save.connect(set_file_properties, sender=Script, + dispatch_uid="script_post_save") +post_save.connect(set_file_properties, sender=ScriptPage, + dispatch_uid="scriptpage_post_save") diff --git a/teleforma/exam/urls.py b/teleforma/exam/urls.py index 6f5bd7d5..d8977f19 100644 --- a/teleforma/exam/urls.py +++ b/teleforma/exam/urls.py @@ -32,16 +32,11 @@ # # Authors: Guillaume Pellerin -import os.path -from django.conf.urls import patterns, url, include -from django.http import HttpResponse -from teleforma.exam.models import * -from teleforma.exam.views import * -from jsonrpc import jsonrpc_site +from teleforma.exam.views import MassScoreCreateView, ScoreCreateView, ScriptCreateView, ScriptView, ScriptsPendingView, ScriptsRejectedView, ScriptsScoreAllView, ScriptsScoreCourseView, ScriptsTreatedView, ScriptsView, get_correctors, get_mass_students +from django.conf.urls import url -urlpatterns = patterns('', - +urlpatterns = [ url(r'^scripts/periods/(?P.*)/(?P.*)/detail/$', ScriptView.as_view(), name="teleforma-exam-script-detail"), url(r'^scripts/periods/(?P.*)/list/$', ScriptsView.as_view(), name="teleforma-exam-script-list"), url(r'^scripts/periods/(?P.*)/create/$', ScriptCreateView.as_view(), name="teleforma-exam-script-create"), @@ -56,7 +51,4 @@ urlpatterns = patterns('', url(r'^scripts/get-correctors/$', get_correctors, name="teleforma-exam-get-correctors"), url(r'^scripts/get-mass-students/$', get_mass_students, name="teleforma-exam-get-mass-students"), - # url(r'^exam/periods/(?P.*)/quotas/$', QuotasView.as_view(), name="teleforma-exam-quotas"), - - -) +] diff --git a/teleforma/exam/views.py b/teleforma/exam/views.py index 1b021193..91ecb905 100755 --- a/teleforma/exam/views.py +++ b/teleforma/exam/views.py @@ -2,20 +2,32 @@ # -*- coding: utf-8 -*- # Create your views here. -from teleforma.exam.models import * -from teleforma.exam.forms import * -from teleforma.views.core import * -from teleforma.decorators import access_required - -from django.views.generic.edit import CreateView, UpdateView, DeleteView -from django.core.urlresolvers import reverse_lazy, reverse -from django.utils.translation import ugettext_lazy as _ +import datetime import json -import numpy as np +import numpy as np +from django.conf import settings +from django.contrib import messages from django.contrib.auth.decorators import permission_required +from django.contrib.auth.models import User from django.db.models import Q -from django.contrib import messages +from django.http.response import HttpResponse +from django.shortcuts import redirect +from django.urls import reverse_lazy +from django.utils.decorators import method_decorator +from django.utils.translation import ugettext +from django.utils.translation import ugettext_lazy as _ +from django.views.generic.base import View +from django.views.generic.edit import CreateView, UpdateView +from django.views.generic.list import ListView +from teleforma.decorators import access_required +from teleforma.models.crfpa import Student +from teleforma.views.core import CourseAccessMixin, get_courses + +from ..exam.forms import MassScoreForm, ScoreForm, ScriptForm +from ..exam.models import Quota, Script, ScriptType +from ..models.core import Course, Document, DocumentType, Period, get_n_choices +from ..views.core import access_error, contact_message STUDENT = 0 CORRECTOR = 1 @@ -28,10 +40,10 @@ class ScriptMixinView(View): """ Get the list of pk of courses current user is allowed to access """ - courses = [ c['course'] for c in get_courses(self.request.user) ] - courses = [ c for c in courses if c.has_exam_scripts ] - courses = [ c for c in courses if c.is_for_period(self.period) ] - return [ c.id for c in courses ] + courses = [c['course'] for c in get_courses(self.request.user)] + courses = [c for c in courses if c.has_exam_scripts] + courses = [c for c in courses if c.is_for_period(self.period)] + return [c.id for c in courses] def get_context_data(self, **kwargs): context = super(ScriptMixinView, self).get_context_data(**kwargs) @@ -39,13 +51,14 @@ class ScriptMixinView(View): context['period'] = self.period course_pk_list = self.get_course_pk_list() context['courses'] = Course.objects.filter(pk__in=course_pk_list) - context['script_service_url'] = getattr(settings, 'TELEFORMA_EXAM_SCRIPT_SERVICE_URL') + context['script_service_url'] = getattr( + settings, 'TELEFORMA_EXAM_SCRIPT_SERVICE_URL') self.nb_script = self.period.nb_script or settings.TELEFORMA_EXAM_MAX_SESSIONS if getattr(settings, 'TELEFORMA_EXAM_SCRIPT_UPLOAD', True) and self.period.date_exam_end: upload = datetime.datetime.now() <= self.period.date_exam_end - cur_scripts = Script.objects.filter(period = self.period, - author = self.request.user)\ - .exclude(status=0).count() + cur_scripts = Script.objects.filter(period=self.period, + author=self.request.user)\ + .exclude(status=0).count() allowed_scripts = self.nb_script * len(self.get_course_pk_list()) if cur_scripts >= allowed_scripts: upload = False @@ -57,6 +70,7 @@ class ScriptMixinView(View): return context + class ScriptsListMixinView(ScriptMixinView): def get_profile(self): @@ -75,26 +89,34 @@ class ScriptsListMixinView(ScriptMixinView): context['profile'] = self.get_profile() if context['profile'] >= CORRECTOR: - correctors = User.objects.filter(corrector_scripts__in=self.get_base_queryset()).order_by('last_name').distinct() - context['correctors_list'] = [(str(corrector.id), corrector.get_full_name()) for corrector in correctors] - context['corrector_selected'] = self.request.GET.get('corrector', str(self.request.user.id)) + correctors = User.objects.filter( + corrector_scripts__in=self.get_base_queryset()).order_by('last_name').distinct() + context['correctors_list'] = [ + (str(corrector.id), corrector.get_full_name()) for corrector in correctors] + context['corrector_selected'] = self.request.GET.get( + 'corrector', str(self.request.user.id)) session_choices = get_n_choices(self.nb_script + 1) context['sessions_list'] = session_choices context['session_selected'] = self.request.GET.get('session') types = ScriptType.objects.all() - context['types_list'] = [(str(type.id), type.name) for type in types] + context['types_list'] = [(str(type.id), type.name) + for type in types] context['type_selected'] = self.request.GET.get('type') - courses = Course.objects.filter(scripts__in=self.get_base_queryset()).distinct() - context['courses_list'] = [(str(course.id), course.title) for course in courses] + courses = Course.objects.filter( + scripts__in=self.get_base_queryset()).distinct() + context['courses_list'] = [ + (str(course.id), course.title) for course in courses] context['course_selected'] = self.request.GET.get('course') context['platform_only'] = self.request.GET.get('platform_only') - context['student_name'] = self.request.GET.get('student_name') or '' + context['student_name'] = self.request.GET.get( + 'student_name') or '' return context + class ScriptView(ScriptMixinView, CourseAccessMixin, UpdateView): model = Script - template_name='exam/script_detail.html' + template_name = 'exam/script_detail.html' form_class = ScriptForm def get_form_kwargs(self): @@ -105,28 +127,29 @@ class ScriptView(ScriptMixinView, CourseAccessMixin, UpdateView): def get_success_url(self): period = Period.objects.get(id=self.kwargs['period_id']) - return reverse_lazy('teleforma-exam-scripts-pending', kwargs={'period_id':period.id}) + return reverse_lazy('teleforma-exam-scripts-pending', kwargs={'period_id': period.id}) def get_context_data(self, **kwargs): context = super(ScriptView, self).get_context_data(**kwargs) script = self.get_object() context['script'] = script context['course'] = script.course - context['mark_fields'] = ['score', 'comments' ] - context['reject_fields'] = ['reject_reason' ] + context['mark_fields'] = ['score', 'comments'] + context['reject_fields'] = ['reject_reason'] - doc_type = DocumentType.objects.get(id=settings.TELEFORMA_EXAM_TOPIC_DEFAULT_DOC_TYPE_ID) + doc_type = DocumentType.objects.get( + id=settings.TELEFORMA_EXAM_TOPIC_DEFAULT_DOC_TYPE_ID) topics = Document.objects.filter(course=script.course, periods__in=(script.period,), - session=script.session, type=doc_type) + session=script.session, type=doc_type) topic = None if topics: topic = topics[0] context['topic'] = topic access = self.request.user == script.author or \ - self.request.user == script.corrector or \ - self.request.user.is_superuser or \ - self.request.user.is_staff or self.request.user.professor.all() + self.request.user == script.corrector or \ + self.request.user.is_superuser or \ + self.request.user.is_staff or self.request.user.professor.all() if not access: context['access_error'] = access_error @@ -146,7 +169,7 @@ class ScriptView(ScriptMixinView, CourseAccessMixin, UpdateView): class ScriptsView(ScriptsListMixinView, ListView): model = Script - template_name='exam/scripts.html' + template_name = 'exam/scripts.html' status_filter = None def get_form_queryset(self): @@ -166,10 +189,10 @@ class ScriptsView(ScriptsListMixinView, ListView): if corrector: QT &= Q(corrector__id=int(corrector)) if platform_only: - QT &= Q(author__student__platform_only = int(platform_only)) + QT &= Q(author__student__platform_only=int(platform_only)) if student_name: - QT &= Q(author__student__user__first_name__icontains=student_name) | Q(author__student__user__last_name__icontains=student_name) | Q(author__student__user__email=student_name) | Q(author__student__user__username=student_name) - + QT &= Q(author__student__user__first_name__icontains=student_name) | Q(author__student__user__last_name__icontains=student_name) | Q( + author__student__user__email=student_name) | Q(author__student__user__username=student_name) return QT @@ -189,7 +212,7 @@ class ScriptsView(ScriptsListMixinView, ListView): professor = user.professor.all() if professor: professor = professor[0] - courses_id = [ c['id'] for c in professor.courses.values('id') ] + courses_id = [c['id'] for c in professor.courses.values('id')] QT |= Q(course_id__in=courses_id) base_qs = base_qs.filter(QT) @@ -225,7 +248,7 @@ class ScriptsPendingView(ScriptsView): class ScriptsTreatedView(ScriptsView): - status_filter = (4, 5 ,7) + status_filter = (4, 5, 7) def get_context_data(self, **kwargs): context = super(ScriptsTreatedView, self).get_context_data(**kwargs) @@ -246,30 +269,33 @@ class ScriptsRejectedView(ScriptsView): class ScriptCreateView(ScriptMixinView, CreateView): model = Script - template_name='exam/script_form.html' + template_name = 'exam/script_form.html' form_class = ScriptForm def get_success_url(self): - return reverse_lazy('teleforma-exam-scripts-pending', kwargs={'period_id':self.period.id}) + return reverse_lazy('teleforma-exam-scripts-pending', kwargs={'period_id': self.period.id}) def form_valid(self, form): scripts = Script.objects.filter(course=form.cleaned_data['course'], session=form.cleaned_data['session'], author=self.request.user, period=self.period).exclude(status=0) if scripts: - messages.error(self.request, _("Error: you have already submitted a script for this session, the same course and the same type!")) + messages.error(self.request, _( + "Error: you have already submitted a script for this session, the same course and the same type!")) return redirect('teleforma-exam-script-create', self.period.id) else: form.instance.author = self.request.user - messages.info(self.request, _("You have successfully submitted your script. It will be processed in the next hours.")) + messages.info(self.request, _( + "You have successfully submitted your script. It will be processed in the next hours.")) return super(ScriptCreateView, self).form_valid(form) def form_invalid(self, form): - messages.error(self.request, _("There was a problem with your submission. Please try again, later if possible.")) + messages.error(self.request, _( + "There was a problem with your submission. Please try again, later if possible.")) return super(ScriptCreateView, self).form_invalid(form) def get_context_data(self, **kwargs): context = super(ScriptCreateView, self).get_context_data(**kwargs) - context['create_fields'] = ['course', 'session', 'file' ] + context['create_fields'] = ['course', 'session', 'file'] context['form'].fields['course'].queryset = context['courses'] return context @@ -284,7 +310,6 @@ class ScriptCreateView(ScriptMixinView, CreateView): return kwargs - class ScriptUpdateView(UpdateView): model = Script @@ -294,7 +319,7 @@ class ScriptUpdateView(UpdateView): class QuotasView(ListView): model = Quota - template_name='exam/quotas.html' + template_name = 'exam/quotas.html' @method_decorator(access_required) def dispatch(self, *args, **kwargs): @@ -305,11 +330,12 @@ class ScriptsScoreAllView(ScriptsTreatedView): perso_name = 'Moyenne personnelle' - template_name='exam/scores.html' + template_name = 'exam/scores.html' def score_data_setup(self, x, y): if not x['x']: - messages.warning(self.request, _("You must add your score to access to the statistics.")) + messages.warning(self.request, _( + "You must add your score to access to the statistics.")) chartdata = x i = 1 @@ -327,10 +353,10 @@ class ScriptsScoreAllView(ScriptsTreatedView): 'extra': { 'x_is_date': False, 'x_axis_format': '', - 'chart_attr': { 'reduceXTicks': 0 }, + 'chart_attr': {'reduceXTicks': 0}, 'tag_script_js': True, - 'jquery_on_ready': False,} - } + 'jquery_on_ready': False, } + } return data def get_context_data(self, **kwargs): @@ -346,7 +372,7 @@ class ScriptsScoreAllView(ScriptsTreatedView): is_staff = user.is_staff staff_or_teacher = is_staff or user.professor.exists() - QT = Q(period_id=period_id, status__in = self.status_filter) + QT = Q(period_id=period_id, status__in=self.status_filter) all_scripts = Script.objects.filter(QT).exclude(score=None) if is_staff: @@ -364,16 +390,16 @@ class ScriptsScoreAllView(ScriptsTreatedView): for script in scripts.values('session'): sessions.add(script['session']) - sessions = sorted(sessions, key = lambda v: int(v)) + sessions = sorted(sessions, key=lambda v: int(v)) sessions_x = {'x': sessions} def by_session(scripts): - res = { s: [] for s in sessions } + res = {s: [] for s in sessions} for script in scripts.values('score', 'session'): if script['session'] in res: if script['score']: res[script['session']].append(script['score']) - return [ np.mean(res[s]) for s in sessions ] + return [np.mean(res[s]) for s in sessions] if not staff_or_teacher: scores.append({'name': self.perso_name, @@ -398,20 +424,21 @@ class ScriptsScoreCourseView(ScriptsScoreAllView): class ScoreCreateView(ScriptCreateView): - template_name='exam/score_form.html' + template_name = 'exam/score_form.html' form_class = ScoreForm def form_invalid(self, form): - messages.error(self.request, "Il y une erreur sur ce formulaire, veuillez le corriger.") + messages.error( + self.request, "Il y une erreur sur ce formulaire, veuillez le corriger.") return super(ScriptCreateView, self).form_invalid(form) def get_success_url(self): period = Period.objects.get(id=self.kwargs['period_id']) - return reverse_lazy('teleforma-exam-scripts-scores-all', kwargs={'period_id':period.id}) + return reverse_lazy('teleforma-exam-scripts-scores-all', kwargs={'period_id': period.id}) def get_context_data(self, **kwargs): context = super(ScoreCreateView, self).get_context_data(**kwargs) - context['create_fields'] = ['course', 'session', 'type', 'score' ] + context['create_fields'] = ['course', 'session', 'type', 'score'] return context @method_decorator(access_required) @@ -424,39 +451,41 @@ def get_correctors(request): course = request.GET.get('course') correctors = [] - for corrector in User.objects.filter(quotas__period__id = period, quotas__course__id = course).order_by('last_name').distinct(): - correctors.append({'label': '%s %s' % (corrector.last_name, corrector.first_name), 'value':corrector.id}) + for corrector in User.objects.filter(quotas__period__id=period, quotas__course__id=course).order_by('last_name').distinct(): + correctors.append({'label': '%s %s' % ( + corrector.last_name, corrector.first_name), 'value': corrector.id}) dump = json.dumps(correctors) return HttpResponse(dump, content_type='application/json') + class MassScoreCreateView(ScoreCreateView): - template_name='exam/mass_score_form.html' + template_name = 'exam/mass_score_form.html' form_class = MassScoreForm def get_allowed_students(self, course_id, session): """ Get allowed students for given course and session """ - students = Student.objects.filter(period = self.period) + students = Student.objects.filter(period=self.period) # Exclude students who already have a script for this session - scripts = Script.objects.filter(period = self.period, - session = session, - course_id = course_id).values('author_id') + scripts = Script.objects.filter(period=self.period, + session=session, + course_id=course_id).values('author_id') scripts = set([s['author_id'] for s in scripts]) - students = students.exclude(user_id__in = scripts) + students = students.exclude(user_id__in=scripts) res = [] for student in students: - user = student.user - # FIXME : Filter those who access the course, but that's very slow, - # so I disable it for now - we'll see if we can do that faster later - # courses = get_courses(user) - # if course_id in [ c['course'].id for c in courses ]: - res.append({ 'id': user.id, - 'name': str(user) }) + user = student.user + # FIXME : Filter those who access the course, but that's very slow, + # so I disable it for now - we'll see if we can do that faster later + # courses = get_courses(user) + # if course_id in [ c['course'].id for c in courses ]: + res.append({'id': user.id, + 'name': str(user)}) return res @@ -467,7 +496,7 @@ class MassScoreCreateView(ScoreCreateView): allowed_students = self.get_allowed_students(course.id, session) - allowed_students = set([ str(s['id']) for s in allowed_students ]) + allowed_students = set([str(s['id']) for s in allowed_students]) done = set() nb_errors = 0 nb_ok = 0 @@ -481,34 +510,38 @@ class MassScoreCreateView(ScoreCreateView): continue nb_ok += 1 done.add(student) - obj = Script(course = course, - period = self.period, - session = session, - author_id = student, - type = sc_type, - score = score, - status = 7) + obj = Script(course=course, + period=self.period, + session=session, + author_id=student, + type=sc_type, + score=score, + status=7) obj.save() - messages.info(self.request, "%d notes créées, %d en erreur (copie déjà existante, ...)" % (nb_ok, nb_errors)) + messages.info(self.request, "%d notes créées, %d en erreur (copie déjà existante, ...)" % ( + nb_ok, nb_errors)) return redirect(self.get_success_url()) def get_context_data(self, **kwargs): context = super(MassScoreCreateView, self).get_context_data(**kwargs) - context['create_fields'] = ['course', 'session', 'type' ] - context['rows'] = [ { 'student_name': 'student%d' % i, - 'score_name': 'score%d' % i } for i in range(20) ] + context['create_fields'] = ['course', 'session', 'type'] + context['rows'] = [{'student_name': 'student%d' % i, + 'score_name': 'score%d' % i} for i in range(20)] for row in context['rows']: student = self.request.POST.get(row['student_name'], '') label = "" if student: user = User.objects.get(pk=student) - label = "%s %s - %s" % (user.last_name, user.first_name, user.username) - row['student_value'] = self.request.POST.get(row['student_name'], '') + label = "%s %s - %s" % (user.last_name, + user.first_name, user.username) + row['student_value'] = self.request.POST.get( + row['student_name'], '') row['student_label'] = label row['score_value'] = self.request.POST.get(row['score_name'], '') - row['error'] = context['form'].table_errors.get(row['student_name'], None) + row['error'] = context['form'].table_errors.get( + row['student_name'], None) return context @method_decorator(permission_required('is_superuser')) @@ -524,25 +557,26 @@ def get_mass_students(request): session = request.GET.get('session') course_id = request.GET.get('course_id') q = request.GET.get('q[term]') - students = Student.objects.filter(period = period).filter(Q(user__username__icontains=q) | Q(user__last_name__icontains=q) | Q(user__first_name__icontains=q)) + students = Student.objects.filter(period=period).filter(Q(user__username__icontains=q) | Q( + user__last_name__icontains=q) | Q(user__first_name__icontains=q)) # Exclude students who already have a script for this session - scripts = Script.objects.filter(period = period, - session = session, - course_id = course_id).values('author_id') + scripts = Script.objects.filter(period=period, + session=session, + course_id=course_id).values('author_id') scripts = set([s['author_id'] for s in scripts]) - students = students.exclude(user_id__in = scripts) + students = students.exclude(user_id__in=scripts) res = [] for student in students: - user = student.user - # FIXME : Filter those who access the course, but that's very slow, - # so I disable it for now - we'll see if we can do that faster later - # courses = get_courses(user) - # if course_id in [ c['course'].id for c in courses ]: - res.append({ 'id': user.id, - 'text': "%s %s - %s" % (user.last_name, user.first_name, user.username) }) - - data = {'results':res, 'pagination':{'more':False}} + user = student.user + # FIXME : Filter those who access the course, but that's very slow, + # so I disable it for now - we'll see if we can do that faster later + # courses = get_courses(user) + # if course_id in [ c['course'].id for c in courses ]: + res.append({'id': user.id, + 'text': "%s %s - %s" % (user.last_name, user.first_name, user.username)}) + + data = {'results': res, 'pagination': {'more': False}} return HttpResponse(json.dumps(data), content_type='application/json') diff --git a/teleforma/fields.py b/teleforma/fields.py index 8f7a096d..92b6173f 100644 --- a/teleforma/fields.py +++ b/teleforma/fields.py @@ -1,18 +1,13 @@ -import django.db.models as models -from django.forms import ModelForm, TextInput, Textarea -from south.modelsinspector import add_introspection_rules -from django.core.exceptions import ValidationError -from django.core import exceptions -from django import forms import datetime -from django.utils.translation import ugettext_lazy as _ import re -try: - from django.contrib.auth import get_user_model # Django 1.5 -except ImportError: - from postman.future_1_5 import get_user_model +import django.db.models as models +from django import forms +from django.contrib.auth import get_user_model +from django.core import exceptions +from django.forms import Textarea +from django.utils.translation import ugettext_lazy as _ class ShortTextField(models.TextField): @@ -23,10 +18,6 @@ class ShortTextField(models.TextField): ) return super(ShortTextField, self).formfield(**kwargs) -add_introspection_rules([], ["^teleforma\.fields\.ShortTextField"]) - - - class Duration(object): @@ -79,7 +70,7 @@ class Duration(object): return Duration(hours=hours, minutes=minutes, seconds=seconds) except TypeError: - print groups + print(groups) raise else: raise ValueError("Malformed duration string: " + str) @@ -97,14 +88,14 @@ def normalize_field(args, default_value=None): The default value can also be overriden with the default=value argument. """ required = False - if args.has_key('required'): + if 'required' in args: required = args['required'] args.pop('required') args['blank'] = not required if not required: - if not args.has_key('default'): + if 'default' not in args: if args.get('null'): args['default'] = None elif default_value is not None: @@ -123,7 +114,7 @@ class DurationField(models.Field): description = _("Duration") - __metaclass__ = models.SubfieldBase + # __metaclass__ = models.SubfieldBase default_error_messages = { 'invalid': _('Enter a valid duration in HH:MM[:ss] format.'), @@ -138,7 +129,7 @@ class DurationField(models.Field): def to_python(self, value): if value is None: return None - if isinstance(value, int) or isinstance(value, long): + if isinstance(value, int): return Duration(seconds=value) if isinstance(value, datetime.time): return Duration(hours=value.hour, minutes=value.minute, seconds=value.second) @@ -169,10 +160,10 @@ class DurationField(models.Field): if val is None: data = '' else: - data = unicode(val) + data = str(val) return data def formfield(self, **kwargs): defaults = {'form_class': forms.CharField} defaults.update(kwargs) - return super(DurationField, self).formfield(**defaults) \ No newline at end of file + return super(DurationField, self).formfield(**defaults) diff --git a/teleforma/forms.py b/teleforma/forms.py index 4a96541f..777faa71 100644 --- a/teleforma/forms.py +++ b/teleforma/forms.py @@ -1,27 +1,26 @@ # -*- coding: utf-8 -*- -from StringIO import StringIO +import datetime +from io import StringIO +from captcha.fields import CaptchaField +from django import forms +from django.contrib.auth.models import User +from django.core.exceptions import ValidationError from django.core.files.uploadedfile import SimpleUploadedFile -from models.core import Period, CourseType -from models.crfpa import IEJ, Training, PAY_STATUS_CHOICES -from teleforma.models import * -from django.forms import ModelForm, ModelChoiceField, ModelMultipleChoiceField, BooleanField, ImageField, CharField, \ - DateField, FileInput, ChoiceField -from django.forms.extras.widgets import SelectDateWidget -from postman.forms import WriteForm as PostmanWriteForm -from postman.fields import BasicCommaSeparatedUserField - -from registration.forms import RegistrationForm +from django.forms import (BooleanField, CharField, ChoiceField, DateField, + FileInput, ImageField, ModelChoiceField, ModelForm, + ModelMultipleChoiceField) +from django.template.defaultfilters import slugify from django.utils.translation import ugettext_lazy as _ -from extra_views import CreateWithInlinesView, UpdateWithInlinesView -from captcha.fields import CaptchaField - -from teleforma.models.core import Course, Professor -from teleforma.models.crfpa import Profile as UserProfile -from tinymce.widgets import TinyMCE -from itertools import cycle -from django.core.files.images import get_image_dimensions from PIL import Image +from postman.fields import BasicCommaSeparatedUserField +from postman.forms import WriteForm as PostmanWriteForm +from tinymce.widgets import TinyMCE + +from .models.core import Conference, Course, Period, payment_schedule_choices +from .models.crfpa import IEJ, PAY_STATUS_CHOICES, Corrector, NewsItem +from .models.crfpa import Profile as UserProfile +from .models.crfpa import Student, Training LEVEL_CHOICES = [ ('', '---------'), @@ -47,9 +46,10 @@ def get_unique_username(first_name, last_name): return username[:30] -class ConferenceForm(ModelForm): - class Meta: - model = Conference +# class ConferenceForm(ModelForm): +# class Meta: +# model = Conference +# fields = '__all__' # @@ -66,12 +66,14 @@ class ConferenceForm(ModelForm): class UserProfileForm(ModelForm): class Meta: model = UserProfile + fields = '__all__' class UserForm(ModelForm): # profile address = CharField(label=_('Address'), max_length=255) - address_detail = CharField(label=_('Address detail'), max_length=255, required=False) + address_detail = CharField( + label=_('Address detail'), max_length=255, required=False) postal_code = CharField(label=_('Postal code'), max_length=255) city = CharField(label=_('City'), max_length=255) country = CharField(label=_('Country'), max_length=255) @@ -83,7 +85,7 @@ class UserForm(ModelForm): level = ChoiceField(label=_('Studying level'), choices=LEVEL_CHOICES) iej = ModelChoiceField(label='IEJ', queryset=IEJ.objects.all()) - platform_only = forms.ChoiceField(choices = TRUE_FALSE_CHOICES, + platform_only = forms.ChoiceField(choices=TRUE_FALSE_CHOICES, label='E-learning uniquement', widget=forms.Select()) period = ModelChoiceField(label='Période', @@ -96,22 +98,24 @@ class UserForm(ModelForm): help_text="Matière de procédure", queryset=Course.objects.filter(procedure=True)) written_speciality = ModelChoiceField(label='Specialité écrite', - queryset=Course.objects.filter(written_speciality=True), + queryset=Course.objects.filter( + written_speciality=True), help_text="Matière juridique de spécialité") oral_1 = ModelChoiceField(label='Souscription à l\'oral de langue (option)', help_text="Matière d’oral de langue (en option)", queryset=Course.objects.filter(oral_1=True)) - promo_code = CharField(label=_('Code promo'), max_length=100, required=False) + promo_code = CharField(label=_('Code promo'), + max_length=100, required=False) payment_schedule = ChoiceField(label=_(u'Échéancier de paiement'), - choices=payment_schedule_choices, - required=True) + choices=payment_schedule_choices, + required=True) - fascicule = forms.ChoiceField(choices = TRUE_FALSE_CHOICES, + fascicule = forms.ChoiceField(choices=TRUE_FALSE_CHOICES, label='Envoi postal des fascicules', required=False, widget=forms.Select()) - + # no model captcha = CaptchaField() accept = BooleanField() @@ -120,14 +124,15 @@ class UserForm(ModelForm): model = User fields = ['first_name', 'last_name', 'email'] - def __init__(self, *args, **kwargs): super(UserForm, self).__init__(*args, **kwargs) self.fields['first_name'].required = True self.fields['last_name'].required = True self.fields['email'].required = True - self.user_fields = ['first_name', 'last_name', 'email', 'address', 'address_detail', 'postal_code', 'city', 'country', 'telephone', 'birthday', 'portrait'] - self.training_fields = ['level', 'iej', 'platform_only', 'fascicule', 'period', 'training', 'procedure', 'written_speciality', 'oral_1'] + self.user_fields = ['first_name', 'last_name', 'email', 'address', 'address_detail', + 'postal_code', 'city', 'country', 'telephone', 'birthday', 'portrait'] + self.training_fields = ['level', 'iej', 'platform_only', 'fascicule', + 'period', 'training', 'procedure', 'written_speciality', 'oral_1'] def clean_portrait(self): image = self.cleaned_data['portrait'] @@ -135,11 +140,11 @@ class UserForm(ModelForm): 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 @@ -163,15 +168,15 @@ class UserForm(ModelForm): user.is_active = False if commit: user.save() - profile = Profile(user=user, - address=data['address'], - address_detail=data.get('address_detail'), - postal_code=data['postal_code'], - city=data['city'], - country=data['country'], - telephone=data['telephone'], - birthday=data['birthday'] - ) + profile = UserProfile(user=user, + address=data['address'], + address_detail=data.get('address_detail'), + postal_code=data['postal_code'], + city=data['city'], + country=data['country'], + telephone=data['telephone'], + birthday=data['birthday'] + ) if commit: profile.save() platform_only = data.get('platform_only') == 'True' and True or False @@ -204,19 +209,21 @@ class UserForm(ModelForm): student.trainings.add(data.get('training', None)) return user + class CorrectorForm(ModelForm): # profile address = CharField(label=_('Address'), max_length=255) - address_detail = CharField(label=_('Address detail'), max_length=255, required=False) + address_detail = CharField( + label=_('Address detail'), max_length=255, required=False) postal_code = CharField(label=_('Postal code'), max_length=255) city = CharField(label=_('City'), max_length=255) country = CharField(label=_('Country'), max_length=255) telephone = CharField(label=_('Telephone'), max_length=255) birthday = DateField(label=_('Birthday'), help_text="Au format jj/mm/aaaa") - birthday_place = CharField(label='Lieu de naissance', max_length=255) - nationality = CharField(label='Nationalité', max_length=255) + birthday_place = CharField(label='Lieu de naissance', max_length=255) + nationality = CharField(label='Nationalité', max_length=255) ss_number = CharField(label='N° de sécurité sociale', - max_length=15) + max_length=15) siret = CharField(label='N° SIRET', max_length=13, required=False) # corrector @@ -224,12 +231,12 @@ class CorrectorForm(ModelForm): queryset=Period.objects.filter(is_open=True, date_inscription_start__lte=datetime.datetime.now(), date_inscription_end__gte=datetime.datetime.now())) - pay_status = forms.ChoiceField(choices = PAY_STATUS_CHOICES, - label='Statut', - widget=forms.Select()) + pay_status = forms.ChoiceField(choices=PAY_STATUS_CHOICES, + label='Statut', + widget=forms.Select()) courses = ModelMultipleChoiceField(label='Matière', - queryset=Course.objects.all().exclude(title="Aucune").order_by('title'), - widget=forms.CheckboxSelectMultiple()) + queryset=Course.objects.all().exclude(title="Aucune").order_by('title'), + widget=forms.CheckboxSelectMultiple()) # no model captcha = CaptchaField() # accept = BooleanField() @@ -238,18 +245,19 @@ class CorrectorForm(ModelForm): model = User fields = ['first_name', 'last_name', 'email'] - def __init__(self, *args, **kwargs): super(CorrectorForm, self).__init__(*args, **kwargs) self.fields['first_name'].required = True self.fields['last_name'].required = True self.fields['email'].required = True - self.user_fields = ['first_name', 'last_name', 'email', 'address', 'address_detail', 'postal_code', 'city', 'country', 'telephone', 'birthday', 'birthday_place', 'nationality', 'ss_number', 'siret'] + self.user_fields = ['first_name', 'last_name', 'email', 'address', 'address_detail', 'postal_code', + 'city', 'country', 'telephone', 'birthday', 'birthday_place', 'nationality', 'ss_number', 'siret'] self.training_fields = ['courses', 'period', 'pay_status'] def clean_siret(self): if self.data['pay_status'] == 'honoraires' and not self.cleaned_data['siret'].strip(): - raise ValidationError("Le SIRET est obligatoire si vous choississez le statut honoraires") + raise ValidationError( + "Le SIRET est obligatoire si vous choississez le statut honoraires") return self.data['siret'] def save(self, commit=True): @@ -264,25 +272,25 @@ class CorrectorForm(ModelForm): user.is_active = False if commit: user.save() - profile = Profile(user=user, - address=data['address'], - address_detail=data.get('address_detail'), - postal_code=data['postal_code'], - city=data['city'], - country=data['country'], - telephone=data['telephone'], - birthday=data['birthday'], - birthday_place=data['birthday_place'], - ss_number=data['ss_number'], - siret=data['siret'], - nationality=data['nationality'] - ) + profile = UserProfile(user=user, + address=data['address'], + address_detail=data.get('address_detail'), + postal_code=data['postal_code'], + city=data['city'], + country=data['country'], + telephone=data['telephone'], + birthday=data['birthday'], + birthday_place=data['birthday_place'], + ss_number=data['ss_number'], + siret=data['siret'], + nationality=data['nationality'] + ) if commit: profile.save() corrector = Corrector(user=user, - period=data.get('period'), - pay_status=data.get('pay_status'), - ) + period=data.get('period'), + pay_status=data.get('pay_status'), + ) corrector.save() corrector.courses = data.get('courses') return user @@ -298,7 +306,8 @@ class NewsItemForm(ModelForm): class WriteForm(PostmanWriteForm): - recipients = BasicCommaSeparatedUserField(label=(_("Recipients"), _("Recipient")), help_text='') + recipients = BasicCommaSeparatedUserField( + label=(_("Recipients"), _("Recipient")), help_text='') course = ModelChoiceField(queryset=Course.objects.all(), required=False) class Meta(PostmanWriteForm.Meta): diff --git a/teleforma/management/commands/teleforma-delete-users.py b/teleforma/management/commands/teleforma-delete-users.py index 9495b616..518839f4 100644 --- a/teleforma/management/commands/teleforma-delete-users.py +++ b/teleforma/management/commands/teleforma-delete-users.py @@ -20,17 +20,17 @@ class Command(BaseCommand): row.write(0, user.last_name) row.write(1, user.first_name) row.write(9, user.email) - row.write(2, unicode(student.iej)) + row.write(2, str(student.iej)) code = student.training.code if student.platform_only: code = 'I - ' + code - row.write(3, unicode(code)) - row.write(4, unicode(student.procedure.code)) - row.write(5, unicode(student.written_speciality.code)) - row.write(6, unicode(student.oral_speciality.code)) - row.write(7, unicode(student.oral_1.code)) - row.write(8, unicode(student.oral_2.code)) - row.write(15, unicode(student.period)) + row.write(3, str(code)) + row.write(4, str(student.procedure.code)) + row.write(5, str(student.written_speciality.code)) + row.write(6, str(student.oral_speciality.code)) + row.write(7, str(student.oral_1.code)) + row.write(8, str(student.oral_2.code)) + row.write(15, str(student.period)) profile = Profile.objects.filter(user=user) if profile: @@ -54,7 +54,7 @@ class Command(BaseCommand): row.write(3, 'FORMATION') row.write(4, 'PROC') row.write(5, 'Ecrit Spe') - row.write(6, unicode('Oral Spe')) + row.write(6, str('Oral Spe')) row.write(7, 'ORAL 1') row.write(8, 'ORAL 2') row.write(9, 'MAIL') diff --git a/teleforma/management/commands/teleforma-export-users.py b/teleforma/management/commands/teleforma-export-users.py index 7ec6f204..0824939c 100644 --- a/teleforma/management/commands/teleforma-export-users.py +++ b/teleforma/management/commands/teleforma-export-users.py @@ -1,12 +1,6 @@ -from optparse import make_option -from django.conf import settings -from django.core.management.base import BaseCommand, CommandError +from django.core.management.base import BaseCommand from django.contrib.auth.models import User -from django.template.defaultfilters import slugify from teleforma.models import * -import logging -import codecs -import xlrd from xlwt import Workbook class Command(BaseCommand): @@ -23,17 +17,17 @@ class Command(BaseCommand): row.write(0, user.last_name) row.write(1, user.first_name) row.write(9, user.email) - row.write(2, unicode(student.iej)) + row.write(2, str(student.iej)) code = student.training.code if student.platform_only: code = 'I - ' + code - row.write(3, unicode(code)) - row.write(4, unicode(student.procedure.code)) - row.write(5, unicode(student.written_speciality.code)) - row.write(6, unicode(student.oral_speciality.code)) - row.write(7, unicode(student.oral_1.code)) - row.write(8, unicode(student.oral_2.code)) - row.write(15, unicode(student.period)) + row.write(3, str(code)) + row.write(4, str(student.procedure.code)) + row.write(5, str(student.written_speciality.code)) + row.write(6, str(student.oral_speciality.code)) + row.write(7, str(student.oral_1.code)) + row.write(8, str(student.oral_2.code)) + row.write(15, str(student.period)) profile = Profile.objects.filter(user=user) if profile: @@ -44,7 +38,7 @@ class Command(BaseCommand): row.write(13, profile.telephone) row.write(14, profile.date_added.strftime("%d/%m/%Y")) - print 'exported: ' + user.first_name + ' ' + user.last_name + ' ' + user.username + print ('exported: ' + user.first_name + ' ' + user.last_name + ' ' + user.username) def export(self): self.book = Workbook() @@ -57,7 +51,7 @@ class Command(BaseCommand): row.write(3, 'FORMATION') row.write(4, 'PROC') row.write(5, 'Ecrit Spe') - row.write(6, unicode('Oral Spe')) + row.write(6, str('Oral Spe')) row.write(7, 'ORAL 1') row.write(8, 'ORAL 2') row.write(9, 'MAIL') diff --git a/teleforma/middleware.py b/teleforma/middleware.py index 61ec3ef2..adc5f4b5 100644 --- a/teleforma/middleware.py +++ b/teleforma/middleware.py @@ -1,65 +1,49 @@ -class OnlyOneUser(object): - - def process_request(self, request): - if not request.user.is_anonymous(): - profile = UserProfile.objects.get(user=request.user) - cur_session_key = profile.last_session_key - if cur_session_key and cur_session_key != request.session.session_key: - sessions = Session.objects.filter(session_key=cur_session_key) - if sessions: - for session in sessions: - Session.objects.get(session_key=cur_session_key).delete() - #the following can be optimized(do not save each time if value not changed) - profile.session_key = request.session.session_key - profile.save() - - - -import re -from django.utils.text import compress_string -from django.utils.cache import patch_vary_headers from django import http - +from django.conf import settings try: - import settings XS_SHARING_ALLOWED_ORIGINS = settings.XS_SHARING_ALLOWED_ORIGINS XS_SHARING_ALLOWED_METHODS = settings.XS_SHARING_ALLOWED_METHODS XS_SHARING_ALLOWED_HEADERS = settings.XS_SHARING_ALLOWED_HEADERS except: XS_SHARING_ALLOWED_ORIGINS = '*' - XS_SHARING_ALLOWED_METHODS = ['POST','GET','OPTIONS', 'PUT', 'DELETE'] + XS_SHARING_ALLOWED_METHODS = ['POST', 'GET', 'OPTIONS', 'PUT', 'DELETE'] XS_SHARING_ALLOWED_HEADERS = ['Origin', 'Content-Type', 'Accept'] - - + + class XsSharing(object): """ This middleware allows cross-domain XHR using the html5 postMessage API. - - + + Access-Control-Allow-Origin: http://foo.example Access-Control-Allow-Methods: POST, GET, OPTIONS, PUT, DELETE """ + def process_request(self, request): - + if 'HTTP_ACCESS_CONTROL_REQUEST_METHOD' in request.META: response = http.HttpResponse() - response['Access-Control-Allow-Origin'] = XS_SHARING_ALLOWED_ORIGINS - response['Access-Control-Allow-Methods'] = ",".join( XS_SHARING_ALLOWED_METHODS ) - response['Access-Control-Allow-Headers'] = ",".join( XS_SHARING_ALLOWED_HEADERS ) - + response['Access-Control-Allow-Origin'] = XS_SHARING_ALLOWED_ORIGINS + response['Access-Control-Allow-Methods'] = ",".join( + XS_SHARING_ALLOWED_METHODS) + response['Access-Control-Allow-Headers'] = ",".join( + XS_SHARING_ALLOWED_HEADERS) + return response - + return None - + def process_response(self, request, response): # Avoid unnecessary work if response.has_header('Access-Control-Allow-Origin'): return response - - response['Access-Control-Allow-Origin'] = XS_SHARING_ALLOWED_ORIGINS - response['Access-Control-Allow-Methods'] = ",".join( XS_SHARING_ALLOWED_METHODS ) - response['Access-Control-Allow-Headers'] = ",".join( XS_SHARING_ALLOWED_HEADERS ) - + + response['Access-Control-Allow-Origin'] = XS_SHARING_ALLOWED_ORIGINS + response['Access-Control-Allow-Methods'] = ",".join( + XS_SHARING_ALLOWED_METHODS) + response['Access-Control-Allow-Headers'] = ",".join( + XS_SHARING_ALLOWED_HEADERS) + return response diff --git a/teleforma/models/__init__.py b/teleforma/models/__init__.py index cc80ad53..7db638dc 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 * +from .core import * +from .crfpa import * +from .ae import * +from .messages import * +from .appointment import * diff --git a/teleforma/models/ae.py b/teleforma/models/ae.py index 52c2948f..67c33765 100644 --- a/teleforma/models/ae.py +++ b/teleforma/models/ae.py @@ -35,22 +35,25 @@ """ import django.db.models as models +from django.contrib.auth.models import User from django.utils.translation import ugettext_lazy as _ -from teleforma.models.core import * +from ..models import app_label +from ..models.core import MetaCore -class AEStudent(Model): +class AEStudent(models.Model): - user = ForeignKey(User, related_name='ae_student', verbose_name=_('user'), unique=True ) - period = ManyToManyField('Period', related_name='ae_student', verbose_name=_('period'), - blank=True, null=True) - platform_only = BooleanField(_('platform only')) - courses = ManyToManyField('Course', related_name="ae_student", - verbose_name=_('courses'), - blank=True, null=True) - - def __unicode__(self): + user = models.OneToOneField( + 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: @@ -59,4 +62,4 @@ class AEStudent(Model): class Meta(MetaCore): db_table = app_label + '_' + 'ae_student' verbose_name = _('AE student') - ordering = ['user__last_name'] \ No newline at end of file + ordering = ['user__last_name'] diff --git a/teleforma/models/appointment.py b/teleforma/models/appointment.py index f17d931e..d4fee3ef 100644 --- a/teleforma/models/appointment.py +++ b/teleforma/models/appointment.py @@ -2,33 +2,37 @@ import datetime -from django.db.models import * -from teleforma.models.core import * from django.contrib.auth.models import User -from django.utils.translation import ugettext_lazy as _ -from django.core import urlresolvers -from django.utils.functional import cached_property from django.core.cache import cache -CACHE_KEY = 'appointment' +from django.db import models +from django.utils.functional import cached_property +from django.utils.translation import ugettext_lazy as _ + +from ..models import app_label +from ..models.core import MetaCore, Period +CACHE_KEY = 'appointment' APPOINTMENT_MODE = (('presentiel', 'Presentiel'), ('distance', 'A distance')) def timing(f): def wrap(*args): - time1 = time.time() + time1 = datetime.time.time() ret = f(*args) - time2 = time.time() - print '%s function took %0.3f ms' % (f.func_name, (time2-time1)*1000.0) + time2 = datetime.time() + print('%s function took %0.3f ms' % + (f.func_name, (time2-time1)*1000.0)) return ret return wrap -class AppointmentPeriod(Model): + +class AppointmentPeriod(models.Model): periods = models.ManyToManyField(Period, related_name='appointment_periods', verbose_name=u"Période") name = models.CharField(_('name'), max_length=255) - course = models.ForeignKey("Course", verbose_name=_("Course"), on_delete=models.SET_NULL, default=19, blank=True, null=True) + course = models.ForeignKey("Course", verbose_name=_( + "Course"), on_delete=models.SET_NULL, default=19, blank=True, null=True) # nb_appointments = models.IntegerField("nombre de rendez-vous autorisé sur la période", # default=1) @@ -44,16 +48,18 @@ class AppointmentPeriod(Model): appointment_mail_text = models.TextField("message à inclure dans le mail de confirmation de rendez-vous pour le présentiel", blank=True, null=True) appointment_mail_text_distance = models.TextField("message à inclure dans le mail de confirmation de rendez-vous pour les rendez-vous à distance", - blank=True, null=True) - appointment_slot_size = models.IntegerField("écart entre les créneaux d'inscription (minutes)", default=40) + blank=True, null=True) + appointment_slot_size = models.IntegerField( + "écart entre les créneaux d'inscription (minutes)", default=40) # bbb_room = models.URLField("salon bbb", help_text='Lien vers le salon BBB pour les inscriptions à distance (ex: https://bbb.parisson.com/b/yoa-mtc-a2e). La salle doit avoir été au préalable créé par un membre du jury sur https://bbb.parisson.com.', null=True, blank=True, max_length=200) - def __unicode__(self): + def __str__(self): return self.name def get_appointment(self, user): - q = Appointment.objects.filter(student=user, slot__appointment_period=self) + q = Appointment.objects.filter( + student=user, slot__appointment_period=self) if q: return q.get() return None @@ -73,17 +79,18 @@ class AppointmentPeriod(Model): today = datetime.date.today() for slot in AppointmentSlot.objects.filter(appointment_period=self).order_by('start'): - cache_key = '%s_%s_%s-%s' % (CACHE_KEY, self.id, slot.date, slot.mode) + cache_key = '%s_%s_%s-%s' % (CACHE_KEY, + self.id, slot.date, slot.mode) dayData = cache.get(cache_key) # dayData = None slot_key = str(slot.date) + "-" + slot.mode if not dayData: - slotData = {'instance':slot, - 'slots':slot.slots, + slotData = {'instance': slot, + 'slots': slot.slots, 'mode': slot.mode, - 'get_visible_jurys':slot.get_visible_jurys, - 'get_nb_of_visible_jurys':slot.get_nb_of_visible_jurys, - 'has_available_slot':slot.has_available_slot} + 'get_visible_jurys': slot.get_visible_jurys, + 'get_nb_of_visible_jurys': slot.get_nb_of_visible_jurys, + 'has_available_slot': slot.has_available_slot} if slot_key not in days: days[slot_key] = {} days[slot_key]['date'] = slot.date @@ -107,7 +114,7 @@ class AppointmentPeriod(Model): cache.set(cache_key, days[day], 1800) # print days - return sorted(days.values(), key=lambda d:d['date']) + return sorted(days.values(), key=lambda d: d['date']) @cached_property def modes(self): @@ -166,38 +173,20 @@ class AppointmentPeriod(Model): verbose_name_plural = "périodes de prise de rendez-vous" -# class AppointmentDay(Model): -# appointment_period = models.ForeignKey(AppointmentPeriod, -# related_name = "days", -# verbose_name = u"Période de prise de rendez-vous") -# date = models.DateField('date') -# -# def __unicode__(self): -# return self.date and self.date.strftime('%d/%m/%Y') or u'' -# -# class Meta(MetaCore): -# db_table = app_label + '_appointment_date' -# verbose_name = "date de prise de rendez-vous" -# verbose_name_plural = "dates de prise de rendez-vous" -# - - -class AppointmentSlot(Model): - # day = models.ForeignKey(AppointmentDay, - # related_name = 'slots', - # verbose_name = 'jour') +class AppointmentSlot(models.Model): appointment_period = models.ForeignKey(AppointmentPeriod, related_name="slots", - verbose_name=u"Période de prise de rendez-vous", null=True, blank=False) - mode = models.CharField('Mode', choices=APPOINTMENT_MODE, default='presentiel', max_length=20) - + verbose_name=u"Période de prise de rendez-vous", null=True, blank=False, on_delete=models.CASCADE) + mode = models.CharField('Mode', choices=APPOINTMENT_MODE, + default='presentiel', max_length=20) + date = models.DateField('date', null=True, blank=False) start = models.TimeField("heure du premier créneau (heure d'arrivée)") nb = models.IntegerField('nombre de créneaux') - def __unicode__(self): - return unicode(self.date) + ' ' + (self.start and self.start.strftime('%H:%M') or '') + def __str__(self): + return str(self.date) + ' ' + (self.start and self.start.strftime('%H:%M') or '') class Meta(MetaCore): ordering = ('id',) @@ -205,11 +194,6 @@ class AppointmentSlot(Model): verbose_name = "créneau de rendez-vous" verbose_name_plural = "créneaux de rendez-vous" - - # @property - # def slots_from_same_day(self): - # slots = AppointmentSlot.objets.filter(appointment_period=self.appointment_period, date=self.date) - def get_nb_jury(self): return self.jurys.count() get_nb_jury.short_description = "Nombre de jurys" @@ -244,10 +228,12 @@ class AppointmentSlot(Model): jurys_slots = [] for jury in jurys: - jurys_slots.append([ap.slot_nb for ap in self.appointments.filter(jury=jury, slot=self)]) + jurys_slots.append( + [ap.slot_nb for ap in self.appointments.filter(jury=jury, slot=self)]) for i in range(self.nb): - arrival = datetime.datetime.combine(self.date, self.start) + datetime.timedelta(minutes=i * size) + arrival = datetime.datetime.combine( + self.date, self.start) + datetime.timedelta(minutes=i * size) start = arrival + datetime.timedelta(minutes=60) end = start + datetime.timedelta(minutes=size) @@ -261,7 +247,8 @@ class AppointmentSlot(Model): # compute if a slot is available for each jury sjurys = [] for j, jury in enumerate(jurys): - sjurys.append({'id': jury.id, 'available': i not in jurys_slots[j]}) + sjurys.append( + {'id': jury.id, 'available': i not in jurys_slots[j]}) slot_info['jurys'] = sjurys res.append(slot_info) @@ -285,18 +272,17 @@ class AppointmentSlot(Model): return self.appointment_period.work_day_between(today, self.date) >= delay -class AppointmentJury(Model): +class AppointmentJury(models.Model): slot = models.ForeignKey(AppointmentSlot, related_name='jurys', - verbose_name='creneau', null=True, blank=False) + verbose_name='creneau', null=True, blank=False, on_delete=models.SET_NULL) name = models.CharField(_('name'), max_length=255) address = models.TextField("adresse", null=True, blank=True) bbb_room = models.URLField("salon bbb", help_text='Lien vers le salon BBB pour les inscriptions à distance (ex: https://bbb.parisson.com/b/yoa-mtc-a2e). La salle doit avoir été au préalable créé par un membre du jury sur https://bbb.parisson.com.', null=True, blank=True, max_length=200) # account = models.ForeignKey(User, verbose_name=_("User"), on_delete=models.SET_NULL, blank=True, null=True) - - def __unicode__(self): + def __str__(self): return self.name class Meta(MetaCore): @@ -305,16 +291,16 @@ class AppointmentJury(Model): verbose_name = "jury" -class Appointment(Model): +class Appointment(models.Model): slot = models.ForeignKey(AppointmentSlot, related_name="appointments", - verbose_name=u"créneau") + verbose_name=u"créneau", on_delete=models.CASCADE) student = models.ForeignKey(User, related_name="appointments", - verbose_name="étudiant") + verbose_name="étudiant", on_delete=models.CASCADE) jury = models.ForeignKey(AppointmentJury, related_name="appointments", verbose_name="jury", on_delete=models.SET_NULL, blank=False, null=True) slot_nb = models.IntegerField('numéro du créneau') - def __unicode__(self): + def __str__(self): return u"%s (%s, %s)" % (self.student, self.real_date_human, self.jury) @@ -334,7 +320,8 @@ class Appointment(Model): @property def start(self): - dt = datetime.datetime.combine(datetime.date.today(), self.arrival) + datetime.timedelta(minutes=60) + dt = datetime.datetime.combine( + datetime.date.today(), self.arrival) + datetime.timedelta(minutes=60) return datetime.time(dt.hour, dt.minute, 0) @property @@ -350,7 +337,8 @@ class Appointment(Model): """ start = self.slot.start delta = self.slot_nb * self.appointment_period.appointment_slot_size - dt = datetime.datetime.combine(datetime.date.today(), start) + datetime.timedelta(minutes=delta) + dt = datetime.datetime.combine( + datetime.date.today(), start) + datetime.timedelta(minutes=delta) return datetime.time(dt.hour, dt.minute, 0) @property diff --git a/teleforma/models/core.py b/teleforma/models/core.py index 2d4eeef5..83813f43 100755 --- a/teleforma/models/core.py +++ b/teleforma/models/core.py @@ -34,36 +34,24 @@ # Author: Guillaume Pellerin """ -import os -import re -import pwd -import time -import urllib -import string import datetime import mimetypes +import os +import string -from django.db.models import * -from teleforma.fields import * import django.db.models as models -from django.utils.translation import ugettext_lazy as _ +from django.conf import settings from django.contrib.auth.models import User -from django.core.exceptions import ValidationError -from django.contrib.contenttypes import generic -import jqchat.models -from django.core.paginator import InvalidPage, EmptyPage -from django.template.defaultfilters import slugify +from django.core.paginator import InvalidPage +from django.db import models +from django.forms.fields import FileField +from django.template.defaultfilters import random, slugify +from django.urls import reverse_lazy +from django.utils.translation import ugettext_lazy as _ +# from quiz.models import Quiz from sorl.thumbnail import default as sorl_default -from django.core.urlresolvers import reverse, reverse_lazy -from django.conf import settings -from quiz.models import Quiz -HAS_TELEMETA = False -try: - from telemeta.models.media import MediaItem - HAS_TELEMETA = True -except ImportError: - pass +from ..fields import ShortTextField HAS_TELEMETA = False if 'telemeta' in settings.INSTALLED_APPS: @@ -75,17 +63,20 @@ app_label = 'teleforma' def get_n_choices(n): return [(str(x), str(y)) for x in range(1, n) for y in range(1, n) if x == y] + def get_nint_choices(n): return [(x, y) for x in range(1, n) for y in range(1, n) if x == y] + session_choices = get_n_choices(settings.TELEFORMA_EXAM_MAX_SESSIONS+1) server_choices = [('icecast', 'icecast'), ('stream-m', 'stream-m')] -streaming_choices = [('mp3', 'mp3'), ('ogg', 'ogg'), ('webm', 'webm'), ('mp4', 'mp4')] -mimetypes.add_type('video/webm','.webm') +streaming_choices = [('mp3', 'mp3'), ('ogg', 'ogg'), + ('webm', 'webm'), ('mp4', 'mp4')] +mimetypes.add_type('video/webm', '.webm') ITEM_TRANSODING_STATUS = ((0, _('broken')), (1, _('pending')), (2, _('processing')), - (3, _('done')), (5, _('ready'))) + (3, _('done')), (5, _('ready'))) payment_choices = [ ('online', u'en ligne'), @@ -102,18 +93,20 @@ payment_schedule_choices = [ ] STATUS_CHOICES = ( - (0, _('Hidden')), - (1, _('Private')), - (2, _('Draft')), - (3, _('Public')), - ) + (0, _('Hidden')), + (1, _('Private')), + (2, _('Draft')), + (3, _('Public')), +) WEIGHT_CHOICES = get_nint_choices(5) + def get_random_hash(): hash = random.getrandbits(128) return "%032x" % hash + def get_user_role(user): if user.is_superuser: return 'superuser' @@ -124,6 +117,7 @@ def get_user_role(user): else: return 'corrector' + class MetaCore: app_label = app_label @@ -131,9 +125,10 @@ class MetaCore: class Organization(models.Model): name = models.CharField(_('name'), max_length=255) - description = models.CharField(_('description'), max_length=255, blank=True) + description = models.CharField( + _('description'), max_length=255, blank=True) - def __unicode__(self): + def __str__(self): return self.name class Meta(MetaCore): @@ -144,12 +139,15 @@ class Organization(models.Model): class Department(models.Model): name = models.CharField(_('name'), max_length=255) - description = models.CharField(_('description'), max_length=255, blank=True) - organization = models.ForeignKey('Organization', related_name='department', verbose_name=_('organization')) + description = models.CharField( + _('description'), max_length=255, blank=True) + organization = models.ForeignKey( + 'Organization', related_name='department', verbose_name=_('organization'), on_delete=models.CASCADE) domain = models.CharField(_('Master domain'), max_length=255, blank=True) - default_period = models.ForeignKey('Period', related_name='departments', verbose_name=_('period'), null=True, blank=True, on_delete=models.SET_NULL) + default_period = models.ForeignKey('Period', related_name='departments', verbose_name=_( + 'period'), null=True, blank=True, on_delete=models.SET_NULL) - def __unicode__(self): + def __str__(self): return self.name @property @@ -161,27 +159,37 @@ class Department(models.Model): verbose_name = _('department') -class Period(Model): +class Period(models.Model): - name = models.CharField(_('name'), max_length=255) - description = models.CharField(_('description'), max_length=255, blank=True) - parent = models.ForeignKey('Period', related_name='children', verbose_name=_('parent'), blank=True, null=True) - department = ForeignKey('Department', related_name='period', - verbose_name=_('department'), - blank=True, null=True) - date_begin = models.DateField(_('begin date'), null=True, blank=True) - date_end = models.DateField(_('end date'), null=True, blank=True) - date_password_init = models.DateField(_("date d'init de mot de passe"), null=True, blank=True) - message_platform = models.TextField(_('message pour internaute'), blank=True) - message_local = models.TextField(_('message pour presentielle'), blank=True) + name = models.CharField(_('name'), max_length=255) + description = models.CharField( + _('description'), max_length=255, blank=True) + parent = models.ForeignKey('Period', related_name='children', verbose_name=_( + 'parent'), blank=True, null=True, on_delete=models.SET_NULL) + department = models.ForeignKey('Department', related_name='period', + verbose_name=_('department'), + blank=True, null=True, on_delete=models.SET_NULL) + date_begin = models.DateField(_('begin date'), null=True, blank=True) + date_end = models.DateField(_('end date'), null=True, blank=True) + date_password_init = models.DateField( + _("date d'init de mot de passe"), null=True, blank=True) + message_platform = models.TextField( + _('message pour internaute'), blank=True) + message_local = models.TextField( + _('message pour presentielle'), blank=True) is_open = models.BooleanField(_('is open'), default=True) - date_exam_end = models.DateTimeField(_("date de fin d'examens"), null=True, blank=True) - nb_script = models.IntegerField(_("nombre maximal de copies"), null=True, blank=True) - date_close_accounts = models.DateField("date de fermeture des comptes étudiants", null = True, blank = True) - date_inscription_start = models.DateField("date d'ouverture des inscriptions", null = True, blank = True) - date_inscription_end = models.DateField("date de fermeture des inscriptions", null=True, blank=True) - - def __unicode__(self): + date_exam_end = models.DateTimeField( + _("date de fin d'examens"), null=True, blank=True) + nb_script = models.IntegerField( + _("nombre maximal de copies"), null=True, blank=True) + date_close_accounts = models.DateField( + "date de fermeture des comptes étudiants", null=True, blank=True) + date_inscription_start = models.DateField( + "date d'ouverture des inscriptions", null=True, blank=True) + date_inscription_end = models.DateField( + "date de fermeture des inscriptions", null=True, blank=True) + + def __str__(self): return self.name class Meta(MetaCore): @@ -190,12 +198,13 @@ class Period(Model): ordering = ['name'] -class CourseType(Model): +class CourseType(models.Model): - name = models.CharField(_('name'), max_length=255) - description = models.CharField(_('description'), max_length=255, blank=True) + name = models.CharField(_('name'), max_length=255) + description = models.CharField( + _('description'), max_length=255, blank=True) - def __unicode__(self): + def __str__(self): return self.name class Meta(MetaCore): @@ -203,8 +212,8 @@ class CourseType(Model): verbose_name = _('course type') def to_dict(self): - dict = {'name' : self.name, - 'description' : self.description, + dict = {'name': self.name, + 'description': self.description, } return dict @@ -214,15 +223,17 @@ class CourseType(Model): self.save() -class Course(Model): +class Course(models.Model): department = models.ForeignKey('Department', related_name='course', - verbose_name=_('department')) + verbose_name=_('department'), on_delete=models.CASCADE) title = models.CharField(_('title'), max_length=255) - description = models.CharField(_('description'), max_length=255, blank=True) + description = models.CharField( + _('description'), max_length=255, blank=True) code = models.CharField(_('code'), max_length=255) title_tweeter = models.CharField(_('tweeter title'), max_length=255) - date_modified = models.DateTimeField(_('date modified'), auto_now=True, null=True) + date_modified = models.DateTimeField( + _('date modified'), auto_now=True, null=True) number = models.IntegerField(_('number'), blank=True, null=True) synthesis_note = models.BooleanField(_('synthesis note')) obligation = models.BooleanField(_('obligations')) @@ -233,35 +244,39 @@ class Course(Model): oral_1 = models.BooleanField(_('oral_1')) oral_2 = models.BooleanField(_('oral_2')) has_exam_scripts = models.BooleanField(_("copies d'examen"), default=True) - quiz = models.ManyToManyField(Quiz, verbose_name=_('quiz'), blank=True, null=True) + # quiz = models.ManyToManyField( + # Quiz, verbose_name=_('quiz'), blank=True, null=True) # last professor which received a student message on automatic mode - last_professor_sent = models.ForeignKey('Professor', blank=True, null=True) + last_professor_sent = models.ForeignKey( + 'Professor', blank=True, null=True, on_delete=models.SET_NULL) periods = models.ManyToManyField('Period', related_name="courses", verbose_name=u'Périodes associées', - blank=True, null=True) + blank=True) - def __unicode__(self): + def __str__(self): return self.title @property def slug(self): - return slugify(unicode(self.code)) + return slugify(str(self.code)) def to_dict(self): - dict = {'organization' : self.department.organization.name, - 'department' : self.department.name, - 'title' : self.title, - 'description' : self.description, - 'code' : self.code, - 'title_tweeter' : self.title_tweeter, - 'number' : str(self.number), + dict = {'organization': self.department.organization.name, + 'department': self.department.name, + 'title': self.title, + 'description': self.description, + 'code': self.code, + 'title_tweeter': self.title_tweeter, + 'number': str(self.number), } return dict def from_dict(self, data): - organization, c = Organization.objects.get_or_create(name=data['organization']) - self.department, c = Department.objects.get_or_create(name=data['department'], organization=organization) + organization, c = Organization.objects.get_or_create( + name=data['organization']) + self.department, c = Department.objects.get_or_create( + name=data['department'], organization=organization) self.title = data['title'] self.description = data['description'] self.code = data['code'] @@ -274,7 +289,7 @@ class Course(Model): """ Check if it's available for given period """ - periods = [ p['id'] for p in self.periods.values('id') ] + periods = [p['id'] for p in self.periods.values('id')] return not periods or period.id in periods class Meta(MetaCore): @@ -288,9 +303,9 @@ class CourseGroup(models.Model): name = models.CharField(_('name'), max_length=255) courses = models.ManyToManyField(Course, related_name="course_groups", verbose_name=_('courses'), - blank=True, null=True) + blank=True) - def __unicode__(self): + def __str__(self): return u"CourseGroup" class Meta(MetaCore): @@ -298,18 +313,18 @@ class CourseGroup(models.Model): verbose_name = _('course group') -class Professor(Model): +class Professor(models.Model): - user = models.ForeignKey(User, related_name='professor', - verbose_name=_('user'), unique=True) + user = models.OneToOneField(User, related_name='professor', + verbose_name=_('user'), unique=True, on_delete=models.CASCADE) courses = models.ManyToManyField('Course', related_name="professor", - verbose_name=_('courses'), - blank=True, null=True) + verbose_name=_('courses'), + blank=True) department = models.ForeignKey('Department', related_name='professor', - verbose_name=_('department'), - blank=True, null=True, on_delete=models.SET_NULL) + verbose_name=_('department'), + blank=True, null=True, on_delete=models.SET_NULL) - def __unicode__(self): + def __str__(self): if self.user.first_name and self.user.last_name: return self.user.last_name + ' ' + self.user.first_name[0] + '.' else: @@ -319,13 +334,13 @@ class Professor(Model): data = {'username': self.user.username, 'first_name': self.user.first_name, 'last_name': self.user.last_name, - 'email' : self.user.email, + 'email': self.user.email, 'courses': [course.code for course in self.courses.all()], - } + } return data def get_absolute_url(self): - return reverse_lazy('teleforma-profile-detail', kwargs={'username':self.user.username}) + return reverse_lazy('teleforma-profile-detail', kwargs={'username': self.user.username}) class Meta(MetaCore): db_table = app_label + '_' + 'professor' @@ -333,13 +348,15 @@ class Professor(Model): ordering = ['user__last_name'] -class Room(Model): +class Room(models.Model): - organization = models.ForeignKey('Organization', related_name='room', verbose_name=_('organization')) - name = models.CharField(_('name'), max_length=255) - description = models.CharField(_('description'), max_length=255, blank=True) + organization = models.ForeignKey( + 'Organization', related_name='room', verbose_name=_('organization'), on_delete=models.CASCADE) + name = models.CharField(_('name'), max_length=255) + description = models.CharField( + _('description'), max_length=255, blank=True) - def __unicode__(self): + def __str__(self): return self.organization.name + ' - ' + self.name class Meta(MetaCore): @@ -349,28 +366,31 @@ class Room(Model): class Conference(models.Model): - public_id = models.CharField(_('public_id'), max_length=255, blank=True) - department = models.ForeignKey('Department', related_name='conference', verbose_name=_('department'), - null=True, blank=True, on_delete=models.SET_NULL) - period = models.ForeignKey('Period', related_name='conference', verbose_name=_('period'), - null=True, blank=True, on_delete=models.SET_NULL) - course = models.ForeignKey('Course', related_name='conference', verbose_name=_('course')) - course_type = models.ForeignKey('CourseType', related_name='conference', verbose_name=_('course type')) - professor = models.ForeignKey('Professor', related_name='conference', verbose_name=_('professor'), - blank=True, null=True, on_delete=models.SET_NULL) - session = models.CharField(_('session'), choices=session_choices, - max_length=16, default="1") - room = models.ForeignKey('Room', related_name='conference', verbose_name=_('room'), - null=True, blank=True) - comment = ShortTextField(_('comment'), max_length=255, blank=True) - date_begin = models.DateTimeField(_('begin date'), null=True, blank=True) - date_end = models.DateTimeField(_('end date'), null=True, blank=True) - readers = models.ManyToManyField(User, related_name="conference", verbose_name=_('readers'), - blank=True, null=True) - status = models.IntegerField(_('status'), choices=STATUS_CHOICES, default=2) - streaming = models.BooleanField(_('streaming'), default=True) + public_id = models.CharField(_('public_id'), max_length=255, blank=True) + department = models.ForeignKey('Department', related_name='conference', verbose_name=_('department'), + null=True, blank=True, on_delete=models.SET_NULL) + period = models.ForeignKey('Period', related_name='conference', verbose_name=_('period'), + null=True, blank=True, on_delete=models.SET_NULL) + course = models.ForeignKey( + 'Course', related_name='conference', verbose_name=_('course'), on_delete=models.CASCADE) + course_type = models.ForeignKey( + 'CourseType', related_name='conference', verbose_name=_('course type'), on_delete=models.CASCADE) + professor = models.ForeignKey('Professor', related_name='conference', verbose_name=_('professor'), + blank=True, null=True, on_delete=models.SET_NULL) + session = models.CharField(_('session'), choices=session_choices, + max_length=16, default="1") + room = models.ForeignKey('Room', related_name='conference', verbose_name=_('room'), + null=True, blank=True, on_delete=models.SET_NULL) + comment = ShortTextField(_('comment'), max_length=255, blank=True) + date_begin = models.DateTimeField(_('begin date'), null=True, blank=True) + date_end = models.DateTimeField(_('end date'), null=True, blank=True) + readers = models.ManyToManyField(User, related_name="conference", verbose_name=_('readers'), + blank=True) + 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) + blank=True, null=True, on_delete=models.SET_NULL) @property def description(self): @@ -383,17 +403,17 @@ class Conference(models.Model): self.course_type.name.lower()]) return slug - def __unicode__(self): + def __str__(self): if self.professor: list = [self.course.department.name, self.course.title, - self.course_type.name, self.session, - self.professor.user.first_name, - self.professor.user.last_name, - str(self.date_begin)] + self.course_type.name, self.session, + self.professor.user.first_name, + self.professor.user.last_name, + str(self.date_begin)] else: list = [self.course.department.name, self.course.title, - self.course_type.name, self.session, - str(self.date_begin)] + self.course_type.name, self.session, + str(self.date_begin)] return ' - '.join(list) @property @@ -410,13 +430,19 @@ class Conference(models.Model): super(Conference, self).save(*args, **kwargs) def to_dict(self): - dict = [{'id':'public_id','value': self.public_id, 'class':'', 'label': 'public_id'}, - {'id':'organization','value': self.course.department.organization, 'class':'', 'label': 'Organization'}, - {'id': 'department', 'value': self.course.department , 'class':'', 'label': 'Department'}, - {'id': 'period', 'value': self.period, 'class':'', 'label': 'Period'}, - {'id': 'professor', 'value': self.professor, 'class':'' , 'label': 'Professor'}, - {'id': 'session', 'value': self.session, 'class':'' , 'label': 'Session'}, - {'id': 'comment', 'value': self.comment, 'class':'' , 'label': 'Comment'}, + dict = [{'id': 'public_id', 'value': self.public_id, 'class': '', 'label': 'public_id'}, + {'id': 'organization', 'value': self.course.department.organization, + 'class': '', 'label': 'Organization'}, + {'id': 'department', 'value': self.course.department, + 'class': '', 'label': 'Department'}, + {'id': 'period', 'value': self.period, + 'class': '', 'label': 'Period'}, + {'id': 'professor', 'value': self.professor, + 'class': '', 'label': 'Professor'}, + {'id': 'session', 'value': self.session, + 'class': '', 'label': 'Session'}, + {'id': 'comment', 'value': self.comment, + 'class': '', 'label': 'Comment'}, ] return dict @@ -435,7 +461,7 @@ class Conference(models.Model): 'date_begin': self.date_begin.strftime('%Y %m %d %H %M %S') if self.date_begin else 'None', 'date_end': self.date_end.strftime('%Y %m %d %H %M %S') if self.date_end else 'None', 'web_class_group': self.web_class_group.name if self.web_class_group else 'None', - } + } if self.room: data['room'] = self.room.name @@ -447,13 +473,14 @@ class Conference(models.Model): data['streams'].append({'host': stream.server.host, 'port': stream.server.port, 'server_type': stream.server.type, - 'stream_type': stream.stream_type }) + 'stream_type': stream.stream_type}) return data def from_json_dict(self, data): self.public_id = data['id'] self.course, c = Course.objects.get_or_create(code=data['course_code']) - self.course_type, c = CourseType.objects.get_or_create(name=data['course_type']) + self.course_type, c = CourseType.objects.get_or_create( + name=data['course_type']) if 'streaming' in data: if data['streaming'] == 'False': @@ -461,7 +488,8 @@ class Conference(models.Model): else: self.streaming = True - organization, c = Organization.objects.get_or_create(name=data['organization']) + organization, c = Organization.objects.get_or_create( + name=data['organization']) if data['department'] != 'None': self.department, c = Department.objects.get_or_create(name=data['department'], @@ -487,18 +515,18 @@ class Conference(models.Model): if data['date_end'] != 'None': dl = data['date_end'].split(' ') self.date_end = datetime.datetime(int(dl[0]), int(dl[1]), int(dl[2]), - int(dl[3]), int(dl[4]), int(dl[5])) + int(dl[3]), int(dl[4]), int(dl[5])) if data['comment'] != 'None': self.comment = data['comment'] if 'room' in data.keys(): self.room, c = Room.objects.get_or_create(name=data['room'], - organization=organization) + organization=organization) if 'web_class_group' in data.keys(): if data['web_class_group'] != 'None': - self.web_class_group = WebClassGroup.objet.get(name=data['web_class_group']) - + self.web_class_group = WebClassGroup.objet.get( + name=data['web_class_group']) class Meta(MetaCore): db_table = app_label + '_' + 'conference' @@ -506,18 +534,20 @@ class Conference(models.Model): ordering = ['-date_begin'] -class StreamingServer(Model): +class StreamingServer(models.Model): element_type = 'streamingserver' - host = models.CharField(_('host'), max_length=255) - port = models.CharField(_('port'), max_length=32) - type = models.CharField(_('type'), choices=server_choices, max_length=32) - description = models.CharField(_('description'), max_length=255, blank=True) + host = models.CharField(_('host'), max_length=255) + port = models.CharField(_('port'), max_length=32) + type = models.CharField(_('type'), choices=server_choices, max_length=32) + description = models.CharField( + _('description'), max_length=255, blank=True) source_password = models.CharField(_('source password'), max_length=32) - admin_password = models.CharField(_('admin password'), max_length=32, blank=True) + admin_password = models.CharField( + _('admin password'), max_length=32, blank=True) - def __unicode__(self): + def __str__(self): return self.host + ':' + self.port + ' - ' + self.type class Meta(MetaCore): @@ -525,18 +555,18 @@ class StreamingServer(Model): verbose_name = _('streaming server') -class LiveStream(Model): +class LiveStream(models.Model): element_type = 'livestream' - conference = models.ForeignKey('Conference', related_name='livestream', - verbose_name=_('conference'), - blank=True, null=True, on_delete=models.SET_NULL) - server = models.ForeignKey('StreamingServer', related_name='livestream', - verbose_name=_('streaming server')) - stream_type = models.CharField(_('Streaming type'), - choices=streaming_choices, max_length=32) - streaming = models.BooleanField(_('streaming')) + conference = models.ForeignKey('Conference', related_name='livestream', + verbose_name=_('conference'), + blank=True, null=True, on_delete=models.SET_NULL) + server = models.ForeignKey('StreamingServer', related_name='livestream', + verbose_name=_('streaming server'), on_delete=models.CASCADE) + stream_type = models.CharField(_('Streaming type'), + choices=streaming_choices, max_length=32) + streaming = models.BooleanField(_('streaming')) @property def slug(self): @@ -560,14 +590,14 @@ class LiveStream(Model): url = '' if self.server.type == 'stream-m': url = 'http://' + self.server.host + ':' + self.server.port + \ - '/snapshot/' + self.slug + '/snapshot/' + self.slug return url @property def url(self): return 'http://' + self.server.host + ':' + self.server.port + '/' + self.mount_point - def __unicode__(self): + def __str__(self): if self.conference: return self.conference.description else: @@ -578,18 +608,22 @@ class LiveStream(Model): verbose_name = _('live stream') -class MediaBase(Model): +class MediaBase(models.Model): "Base media resource" - title = models.CharField(_('title'), max_length=255, blank=True) - description = models.CharField(_('description'), max_length=255, blank=True) - credits = models.CharField(_('credits'), max_length=255, blank=True) - date_added = models.DateTimeField(_('date added'), auto_now_add=True, null=True) - date_modified = models.DateTimeField(_('date modified'), auto_now=True, null=True) - code = models.CharField(_('code'), max_length=255, blank=True) - is_published = models.BooleanField(_('published')) - mime_type = models.CharField(_('mime type'), max_length=255, blank=True) - weight = models.IntegerField(_('weight'), choices=WEIGHT_CHOICES, default=1, blank=True) + title = models.CharField(_('title'), max_length=255, blank=True) + description = models.CharField( + _('description'), max_length=255, blank=True) + credits = models.CharField(_('credits'), max_length=255, blank=True) + date_added = models.DateTimeField( + _('date added'), auto_now_add=True, null=True) + date_modified = models.DateTimeField( + _('date modified'), auto_now=True, null=True) + code = models.CharField(_('code'), max_length=255, blank=True) + is_published = models.BooleanField(_('published')) + mime_type = models.CharField(_('mime type'), max_length=255, blank=True) + weight = models.IntegerField( + _('weight'), choices=WEIGHT_CHOICES, default=1, blank=True) def get_fields(self): return self._meta.fields @@ -598,16 +632,17 @@ class MediaBase(Model): abstract = True -class DocumentType(Model): +class DocumentType(models.Model): - name = models.CharField(_('name'), max_length=255) - description = models.CharField(_('description'), max_length=255, blank=True) - number = models.IntegerField(_('number'), blank=True, null=True) - for_corrector = models.BooleanField('autorisé aux correcteurs', - blank = True, null = False, - default = False) + name = models.CharField(_('name'), max_length=255) + description = models.CharField( + _('description'), max_length=255, blank=True) + number = models.IntegerField(_('number'), blank=True, null=True) + for_corrector = models.BooleanField('autorisé aux correcteurs', + blank=True, null=False, + default=False) - def __unicode__(self): + def __str__(self): return self.name class Meta(MetaCore): @@ -620,27 +655,28 @@ class Document(MediaBase): element_type = 'document' - course = models.ForeignKey('Course', related_name='document', verbose_name=_('course')) - course_type = models.ManyToManyField('CourseType', related_name='document', - verbose_name=_('course type'), blank=True) - conference = models.ForeignKey('Conference', related_name='document', verbose_name=_('conference'), - blank=True, null=True, on_delete=models.SET_NULL) + course = models.ForeignKey( + 'Course', related_name='document', verbose_name=_('course'), on_delete=models.CASCADE) + course_type = models.ManyToManyField('CourseType', related_name='document', + verbose_name=_('course type'), blank=True) + conference = models.ForeignKey('Conference', related_name='document', verbose_name=_('conference'), + blank=True, null=True, on_delete=models.SET_NULL) # period = models.ForeignKey('Period', related_name='document', verbose_name=_('period'), # null=True, blank=True, on_delete=models.SET_NULL) - periods = models.ManyToManyField('Period', related_name='documents', verbose_name=_('periods'), - null=True, blank=True) - type = models.ForeignKey('DocumentType', related_name='document', verbose_name=_('type'), - blank=True, null=True) - session = models.CharField(_('session'), choices=session_choices, - max_length=16, default="1") - iej = models.ForeignKey('IEJ', related_name='document', verbose_name=_('iej'), - blank=True, null=True, on_delete=models.SET_NULL) - is_annal = models.BooleanField(_('annal')) - annal_year = models.IntegerField(_('year'), blank=True, null=True) - file = FileField(_('file'), upload_to='items/%Y/%m/%d', db_column="filename", - max_length=1024, blank=True) - readers = models.ManyToManyField(User, related_name="document", verbose_name=_('readers'), - blank=True, null=True) + periods = models.ManyToManyField('Period', related_name='documents', verbose_name=_('periods'), + blank=True) + type = models.ForeignKey('DocumentType', related_name='document', verbose_name=_('type'), + blank=True, null=True, on_delete=models.SET_NULL) + session = models.CharField(_('session'), choices=session_choices, + max_length=16, default="1") + iej = models.ForeignKey('IEJ', related_name='document', verbose_name=_('iej'), + blank=True, null=True, on_delete=models.SET_NULL) + is_annal = models.BooleanField(_('annal')) + annal_year = models.IntegerField(_('year'), blank=True, null=True) + file = models.FileField(_('file'), upload_to='items/%Y/%m/%d', db_column="filename", + max_length=1024, blank=True) + readers = models.ManyToManyField(User, related_name="document", verbose_name=_('readers'), + blank=True) def is_image(self): is_url_image = False @@ -654,9 +690,9 @@ class Document(MediaBase): def set_mime_type(self): self.mime_type = mimetypes.guess_type(self.file.path)[0] - def __unicode__(self): - types = ' - '.join([unicode(t) for t in self.course_type.all()]) - return ' - '.join([unicode(self.course), unicode(types), self.title, str(self.date_added) ]) + def __str__(self): + types = ' - '.join([str(t) for t in self.course_type.all()]) + return ' - '.join([str(self.course), str(types), self.title, str(self.date_added)]) def save(self, **kwargs): if not self.is_annal: @@ -673,12 +709,12 @@ class DocumentSimple(MediaBase): element_type = 'document_simple' - period = models.ForeignKey('Period', related_name='document_simple', verbose_name=_('period'), - null=True, blank=True, on_delete=models.SET_NULL) - file = FileField(_('file'), upload_to='items/%Y/%m/%d', db_column="filename", - max_length=1024, blank=True) - readers = models.ManyToManyField(User, related_name="document_simple", verbose_name=_('readers'), - blank=True, null=True) + period = models.ForeignKey('Period', related_name='document_simple', verbose_name=_('period'), + null=True, blank=True, on_delete=models.SET_NULL) + file = models.FileField(_('file'), upload_to='items/%Y/%m/%d', db_column="filename", + max_length=1024, blank=True) + readers = models.ManyToManyField(User, related_name="document_simple", verbose_name=_('readers'), + blank=True) def is_image(self): is_url_image = False @@ -692,7 +728,7 @@ class DocumentSimple(MediaBase): def set_mime_type(self): self.mime_type = mimetypes.guess_type(self.file.path)[0] - def __unicode__(self): + def __str__(self): return self.title def save(self, **kwargs): @@ -704,17 +740,19 @@ class DocumentSimple(MediaBase): ordering = ['-date_added'] - -class MediaTranscoded(Model): +class MediaTranscoded(models.Model): "Item file transcoded" element_type = 'transcoded item' - item = models.ForeignKey('Media', related_name="transcoded", verbose_name=_('item')) - mimetype = models.CharField(_('mime_type'), max_length=255, blank=True) - date_added = DateTimeField(_('date'), auto_now_add=True) - status = models.IntegerField(_('status'), choices=ITEM_TRANSODING_STATUS, default=1) - file = models.FileField(_('file'), upload_to='items/%Y/%m/%d', max_length=1024, blank=True) + item = models.ForeignKey( + 'Media', related_name="transcoded", verbose_name=_('item'), on_delete=models.CASCADE) + mimetype = models.CharField(_('mime_type'), max_length=255, blank=True) + date_added = models.DateTimeField(_('date'), auto_now_add=True) + status = models.IntegerField( + _('status'), choices=ITEM_TRANSODING_STATUS, default=1) + file = models.FileField( + _('file'), upload_to='items/%Y/%m/%d', max_length=1024, blank=True) @property def mime_type(self): @@ -731,7 +769,7 @@ class MediaTranscoded(Model): else: return self.mimetype - def __unicode__(self): + def __str__(self): if self.item.title: return self.item.title + ' - ' + self.mime_type else: @@ -746,22 +784,25 @@ class Media(MediaBase): element_type = 'media' - conference = models.ForeignKey('Conference', related_name='media', verbose_name=_('conference'), - blank=True, null=True, on_delete=models.SET_NULL) - course = models.ForeignKey('Course', related_name='media', verbose_name=_('course'), - blank=True, null=True) - course_type = models.ForeignKey('CourseType', related_name='media', verbose_name=_('course type'), - blank=True, null=True) - period = models.ForeignKey('Period', related_name='media', verbose_name=_('period'), - null=True, blank=True, on_delete=models.SET_NULL) + conference = models.ForeignKey('Conference', related_name='media', verbose_name=_('conference'), + blank=True, null=True, on_delete=models.SET_NULL) + course = models.ForeignKey('Course', related_name='media', verbose_name=_('course'), + blank=True, null=True, on_delete=models.SET_NULL) + course_type = models.ForeignKey('CourseType', related_name='media', verbose_name=_('course type'), + 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) - type = models.CharField(_('type'), choices=streaming_choices, max_length=32) - readers = models.ManyToManyField(User, related_name="media", verbose_name=_('readers'), - blank=True, null=True) - file = models.FileField(_('file'), upload_to='items/%Y/%m/%d', max_length=1024, null=True, blank=False) - poster_file = models.FileField(_('poster file'), upload_to='items/%Y/%m/%d', max_length=255, null=True, blank=False) + 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'), + blank=True) + file = models.FileField( + _('file'), upload_to='items/%Y/%m/%d', max_length=1024, null=True, blank=False) + poster_file = models.FileField( + _('poster file'), upload_to='items/%Y/%m/%d', max_length=255, null=True, blank=False) def set_mime_type(self): if self.item.file: @@ -772,7 +813,7 @@ class Media(MediaBase): self.mime_type = mime_type self.save() - def __unicode__(self): + def __str__(self): if self.conference: return self.conference.description elif self.course: @@ -790,7 +831,8 @@ class Media(MediaBase): def poster_url(self, geometry='640'): url = '' if self.poster_file: - url = sorl_default.backend.get_thumbnail(self.poster_file, geometry).url + url = sorl_default.backend.get_thumbnail( + self.poster_file, geometry).url return url class Meta(MetaCore): @@ -833,7 +875,8 @@ class NamePaginator(object): current_page.add([], letter) continue - sub_list = chunks[letter] # the items in object_list starting with this letter + # the items in object_list starting with this letter + sub_list = chunks[letter] new_page_count = len(sub_list) + current_page.count # first, check to see if sub_list will fit or it needs to go onto a new page. @@ -850,7 +893,8 @@ class NamePaginator(object): current_page.add(sub_list, letter) # if we finished the for loop with a page that isn't empty, add it - if current_page.count > 0: self.pages.append(current_page) + if current_page.count > 0: + self.pages.append(current_page) def page(self, num): """Returns a Page object for the given 1-based page number.""" @@ -866,6 +910,7 @@ class NamePaginator(object): """Returns the total number of pages""" return len(self.pages) + class NamePage(object): def __init__(self, paginator): self.paginator = paginator @@ -881,14 +926,16 @@ class NamePage(object): if len(self.letters) > 0: self.letters.sort(key=str.upper) return self.letters[0] - else: return None + else: + return None @property def end_letter(self): if len(self.letters) > 0: self.letters.sort(key=str.upper) return self.letters[-1] - else: return None + else: + return None @property def number(self): @@ -905,3 +952,21 @@ class NamePage(object): return self.start_letter else: return '%c-%c' % (self.start_letter, self.end_letter) + + +class WebClassGroup(models.Model): + + name = models.CharField(_('name'), max_length=255) + iejs = models.ManyToManyField('IEJ', related_name="web_class_group", verbose_name=_('IEJ'), + blank=True) + + class Meta(MetaCore): + verbose_name = _('web class group') + verbose_name_plural = _('web class group') + ordering = ['name'] + + def to_json_dict(self): + data = {'name': self.name, + 'iejs': [iej.name for iej in self.iejs.all()], + } + return data diff --git a/teleforma/models/crfpa.py b/teleforma/models/crfpa.py index 70c06273..f483ece9 100755 --- a/teleforma/models/crfpa.py +++ b/teleforma/models/crfpa.py @@ -34,24 +34,32 @@ # Author: Guillaume Pellerin """ +import datetime + import django.db.models as models +from django.contrib.auth.models import User +from django.db.models import signals +from django.urls.base import reverse_lazy from django.utils.translation import ugettext_lazy as _ -from teleforma.models.core import * from tinymce.models import HTMLField -from django.db.models import signals +from ..models.core import (Course, Media, MetaCore, payment_choices, + payment_schedule_choices) + +app_label = 'teleforma' months_choices = [] -for i in range(1,13): +for i in range(1, 13): months_choices.append((i, datetime.date(2015, i, 1).strftime('%B'))) -class IEJ(Model): +class IEJ(models.Model): name = models.CharField(_('name'), max_length=255) - description = models.CharField(_('description'), max_length=255, blank=True) + description = models.CharField( + _('description'), max_length=255, blank=True) - def __unicode__(self): + def __str__(self): return self.name class Meta(MetaCore): @@ -61,65 +69,53 @@ class IEJ(Model): ordering = ['name'] -class WebClassGroup(models.Model): - - name = models.CharField(_('name'), max_length=255) - iejs = models.ManyToManyField('IEJ', related_name="web_class_group", verbose_name=_('IEJ'), - blank=True, null=True) - - class Meta(MetaCore): - verbose_name = _('web class group') - verbose_name_plural = _('web class group') - ordering = ['name'] - - def to_json_dict(self): - data = {'name': self.name, - 'iejs': [iej.name for iej in self.iejs.all()], - } - return data - - -class Training(Model): +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) - synthesis_note = models.ManyToManyField('CourseType', related_name="training_synthesis_note", verbose_name=_('synthesis note'), - blank=True, null=True) + description = models.CharField( + _('description'), max_length=512, blank=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) + synthesis_note = models.ManyToManyField('CourseType', related_name="training_synthesis_note", verbose_name=_('synthesis note'), + 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) + verbose_name=_('procedure'), + blank=True) written_speciality = models.ManyToManyField('CourseType', related_name="training_written_speciality", - verbose_name=_('written speciality'), - blank=True, null=True) + verbose_name=_( + 'written speciality'), + blank=True) oral_speciality = models.ManyToManyField('CourseType', related_name="training_oral_speciality", - verbose_name=_('oral speciality'), - blank=True, null=True) + verbose_name=_('oral speciality'), + blank=True) oral_1 = models.ManyToManyField('CourseType', related_name="training_oral_1", - verbose_name=_('oral 1'), - blank=True, null=True) + verbose_name=_('oral 1'), + blank=True) oral_2 = models.ManyToManyField('CourseType', related_name="training_oral_2", - verbose_name=_('oral 2'), - blank=True, null=True) + verbose_name=_('oral 2'), + blank=True) options = models.ManyToManyField('CourseType', related_name="training_options", - verbose_name=_('options'), - blank=True, null=True) + verbose_name=_('options'), + blank=True) magistral = models.ManyToManyField('CourseType', related_name="training_magistral", - verbose_name=_('magistral'), - blank=True, null=True) + verbose_name=_('magistral'), + blank=True) cost = models.FloatField(_('cost'), blank=True, null=True) - cost_elearning_fascicle = models.FloatField(_('e-learning cost with fascicle'), blank=True, null=True) - cost_elearning_nofascicle = models.FloatField(_('e-learning cost without fascicle'), blank=True, null=True) + cost_elearning_fascicle = models.FloatField( + _('e-learning cost with fascicle'), blank=True, null=True) + cost_elearning_nofascicle = models.FloatField( + _('e-learning cost without fascicle'), blank=True, null=True) available = models.BooleanField(_('available')) platform_only = models.BooleanField(_('e-learning platform only')) duration = models.IntegerField(u"Durée en heures", default=0) - def __unicode__(self): + def __str__(self): if self.name and self.period: return ' - '.join([self.name, self.period.name]) else: @@ -136,55 +132,64 @@ class Training(Model): verbose_name = _('training') -class Student(Model): +class Student(models.Model): "A student profile" - user = models.ForeignKey(User, related_name='student', verbose_name=_('user'), unique=True) - 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) - portrait = models.ImageField(max_length=500, upload_to='portraits/', blank=True, null=True) + user = models.OneToOneField(User, related_name='student', + verbose_name=_('user'), unique=True, on_delete=models.CASCADE) + 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) + portrait = models.ImageField( + max_length=500, upload_to='portraits/', blank=True, null=True) iej = models.ForeignKey('IEJ', related_name='student', verbose_name=_('iej'), - blank=True, null=True, on_delete=models.SET_NULL) + blank=True, null=True, on_delete=models.SET_NULL) trainings = models.ManyToManyField('Training', related_name='student_trainings', verbose_name=_('trainings'), - blank=True, null=True) + blank=True) # deprecated, replaced by trainings field training = models.ForeignKey('Training', related_name='student_training', verbose_name=_('training'), - blank=True, null=True, limit_choices_to={'available': True}) + blank=True, null=True, limit_choices_to={'available': True}, on_delete=models.SET_NULL) procedure = models.ForeignKey('Course', related_name="procedure_students", - verbose_name=_('procedure'), help_text="Matière de procédure", - blank=True, null=True, limit_choices_to={'procedure': True}) + verbose_name=_('procedure'), help_text="Matière de procédure", + blank=True, null=True, limit_choices_to={'procedure': True}, on_delete=models.SET_NULL) written_speciality = models.ForeignKey('Course', related_name="written_speciality_students", - verbose_name=_('written speciality'), help_text="Matière juridique de spécialité", - blank=True, null=True, limit_choices_to={'written_speciality': True}) + verbose_name=_('written speciality'), help_text="Matière juridique de spécialité", + blank=True, null=True, limit_choices_to={'written_speciality': True}, on_delete=models.SET_NULL) written_speciality = models.ForeignKey('Course', related_name="written_speciality_2students", - verbose_name=_('written speciality'), help_text="Matière juridique de spécialité", - blank=True, null=True, limit_choices_to={'written_speciality': True}) + verbose_name=_('written speciality'), help_text="Matière juridique de spécialité", + blank=True, null=True, limit_choices_to={'written_speciality': True}, on_delete=models.SET_NULL) oral_speciality = models.ForeignKey('Course', related_name="oral_speciality_students", verbose_name=_('oral speciality'), help_text="Matière d’oral de spécialité (matière incluse dans la formation approfondie, en option pour toutes les autres formations)", - blank=True, null=True, limit_choices_to={'oral_speciality': True}) + blank=True, null=True, limit_choices_to={'oral_speciality': True}, on_delete=models.SET_NULL) oral_1 = models.ForeignKey('Course', related_name="oral_1_students", verbose_name=_('oral de langue (option)'), - help_text="Matière d’oral de langue (en option)", - blank=True, null=True, limit_choices_to={'oral_1': True}) + help_text="Matière d’oral de langue (en option)", + blank=True, null=True, limit_choices_to={'oral_1': True}, on_delete=models.SET_NULL) oral_2 = models.ForeignKey('Course', related_name="oral_2_students", verbose_name=_('oral 2 (option)'), - help_text="Matière d’oral technique 2 (en option)", - blank=True, null=True, limit_choices_to={'oral_2': True}) + help_text="Matière d’oral technique 2 (en option)", + blank=True, null=True, limit_choices_to={'oral_2': True}, on_delete=models.SET_NULL) options = models.ForeignKey('Course', related_name="options_students", verbose_name=_('options'), - blank=True, null=True) + blank=True, null=True, on_delete=models.SET_NULL) period = models.ForeignKey('Period', related_name='student', verbose_name=_('period'), - blank=True, null=True, on_delete=models.SET_NULL) - platform_only = models.BooleanField(_('e-learning platform only')) - application_fees = models.BooleanField(_('application fees'), blank=True, default=True) + blank=True, null=True, on_delete=models.SET_NULL) + platform_only = models.BooleanField(_('e-learning platform only')) + application_fees = models.BooleanField( + _('application fees'), blank=True, default=True) default_application_fees = 40 - subscription_fees = models.FloatField(_('subscription fees'), help_text='€', blank=True, null=True) + subscription_fees = models.FloatField( + _('subscription fees'), help_text='€', blank=True, null=True) promo_code = models.CharField(_('promo code'), blank=True, max_length=100) - date_registered = models.DateTimeField(_('registration date'), auto_now_add=True, null=True, blank=True) - date_subscribed = models.DateTimeField(_('subscription date'), null=True, blank=True) + date_registered = models.DateTimeField( + _('registration date'), auto_now_add=True, null=True, blank=True) + date_subscribed = models.DateTimeField( + _('subscription date'), null=True, blank=True) is_subscribed = models.BooleanField(_('subscribed')) confirmation_sent = models.BooleanField(_('confirmation sent')) level = models.CharField(_('studying level'), blank=True, max_length=100) - balance = models.FloatField(_('balance de paiement'), help_text='€', blank=True, null=True) - balance_intermediary = models.FloatField('balance de paiement intermédiaire', help_text='€', blank=True, null=True) + balance = models.FloatField( + _('balance de paiement'), help_text='€', blank=True, null=True) + balance_intermediary = models.FloatField( + 'balance de paiement intermédiaire', help_text='€', blank=True, null=True) fascicule = models.BooleanField(_('envoi des fascicules'), blank=True, default=False) @@ -201,7 +206,7 @@ class Student(Model): receipt_id = models.IntegerField('numéro de facture', blank=True, null=True, unique=True) - def __unicode__(self): + def __str__(self): try: return self.user.last_name + ' ' + self.user.first_name except: @@ -256,13 +261,15 @@ class Student(Model): def update_balance(self): old = self.balance - new = round(self.total_payments - self.total_fees + self.total_paybacks, 2) + new = round(self.total_payments - + self.total_fees + self.total_paybacks, 2) save = False if old != new: self.balance = new save = True old_int = self.balance_intermediary - new_int = round(self.total_payments_all - self.total_fees + self.total_paybacks, 2) + new_int = round(self.total_payments_all - + self.total_fees + self.total_paybacks, 2) if old_int != new_int: self.balance_intermediary = new_int save = True @@ -270,7 +277,7 @@ class Student(Model): self.save() def get_absolute_url(self): - return reverse_lazy('teleforma-profile-detail', kwargs={'username':self.user.username}) + return reverse_lazy('teleforma-profile-detail', kwargs={'username': self.user.username}) class Meta(MetaCore): db_table = app_label + '_' + 'student' @@ -278,33 +285,43 @@ class Student(Model): verbose_name_plural = _('Students') ordering = ['user__last_name', '-date_subscribed'] + def update_balance_signal(sender, instance, *args, **kwargs): if sender is Student: instance.update_balance() elif sender in (Discount, OptionalFee, Payment, Payback): instance.student.update_balance() + signals.post_save.connect(update_balance_signal) signals.post_delete.connect(update_balance_signal) + class Profile(models.Model): "User profile extension" - user = models.ForeignKey(User, related_name='profile', verbose_name=_('user'), unique=True) + user = models.OneToOneField(User, related_name='profile', + verbose_name=_('user'), unique=True, on_delete=models.CASCADE) address = models.CharField(_('Address'), max_length=255, blank=True) - address_detail = models.CharField(_('Address detail'), max_length=255, blank=True, null=True) - postal_code = models.CharField(_('Postal code'), max_length=255, blank=True) + address_detail = models.CharField( + _('Address detail'), max_length=255, blank=True, null=True) + postal_code = models.CharField( + _('Postal code'), max_length=255, blank=True) city = models.CharField(_('City'), max_length=255, blank=True) country = models.CharField(_('Country'), max_length=255, blank=True) language = models.CharField(_('Language'), max_length=255, blank=True) telephone = models.CharField(_('Telephone'), max_length=255, blank=True) - expiration_date = models.DateField(_('Expiration_date'), blank=True, null=True) + expiration_date = models.DateField( + _('Expiration_date'), blank=True, null=True) init_password = models.BooleanField(_('Password initialized')) wifi_login = models.CharField(_('WiFi login'), max_length=255, blank=True) wifi_pass = models.CharField(_('WiFi pass'), max_length=255, blank=True) - birthday = models.DateField(_('birthday'), blank=True, null=True, help_text="jj/mm/aaaa") - birthday_place = models.CharField('Lieu de naissance', max_length=255, blank=True, null=True) - nationality = models.CharField('Nationalité', max_length=255, null=True, blank=True) + birthday = models.DateField( + _('birthday'), blank=True, null=True, help_text="jj/mm/aaaa") + birthday_place = models.CharField( + 'Lieu de naissance', max_length=255, blank=True, null=True) + nationality = models.CharField( + 'Nationalité', max_length=255, null=True, blank=True) ss_number = models.CharField('Sécurité sociale', max_length=15, blank=True, null=True) siret = models.CharField('Siret', @@ -315,32 +332,34 @@ class Profile(models.Model): verbose_name = _('profile') - PAY_STATUS_CHOICES = [ ('honoraires', 'Honoraires'), ('salarie', 'Salarié'), ] -class Corrector(Model): + + +class Corrector(models.Model): "A corrector profile, only used for registration for the moment" - user = models.ForeignKey(User, related_name='corrector', verbose_name=_('user'), unique=True) + user = models.OneToOneField( + User, related_name='corrector', verbose_name=_('user'), unique=True, on_delete=models.CASCADE) period = models.ForeignKey('Period', related_name='corrector', verbose_name=_('period'), - blank=True, null=True, on_delete=models.SET_NULL) - courses = models.ManyToManyField("Course", verbose_name=_("Course"), blank=True, null=True) + blank=True, null=True, on_delete=models.SET_NULL) + courses = models.ManyToManyField( + "Course", verbose_name=_("Course"), blank=True) pay_status = models.CharField('Statut', choices=PAY_STATUS_CHOICES, - max_length=64, blank=True, null=True, - default='honoraire') - - date_registered = models.DateTimeField(_('registration date'), auto_now_add=True, null=True, blank=True) + max_length=64, blank=True, null=True, + default='honoraire') + date_registered = models.DateTimeField( + _('registration date'), auto_now_add=True, null=True, blank=True) - def __unicode__(self): + def __str__(self): try: return self.user.last_name + ' ' + self.user.first_name except: return '' - class Meta(MetaCore): db_table = app_label + '_' + 'corrector' verbose_name = _('Correcteur') @@ -351,7 +370,8 @@ class Corrector(Model): class Payment(models.Model): "a payment from a student" - student = models.ForeignKey(Student, related_name='payments', verbose_name=_('student')) + student = models.ForeignKey( + Student, related_name='payments', verbose_name=_('student'), on_delete=models.CASCADE) value = models.FloatField(_('amount'), help_text='€') month = models.IntegerField(_('month'), choices=months_choices, default=1, blank=True, null=True) @@ -377,9 +397,11 @@ class Payment(models.Model): class Discount(models.Model): "a discount for a student subscription" - student = models.ForeignKey(Student, related_name='discounts', verbose_name=_('student')) + student = models.ForeignKey( + Student, related_name='discounts', verbose_name=_('student'), on_delete=models.CASCADE) value = models.FloatField(_('amount'), help_text='€') - description = models.CharField(_('description'), max_length=255, blank=True) + description = models.CharField( + _('description'), max_length=255, blank=True) class Meta(MetaCore): db_table = app_label + '_' + 'discounts' @@ -390,9 +412,11 @@ class Discount(models.Model): class OptionalFee(models.Model): "an optional fee for a student subscription" - student = models.ForeignKey(Student, related_name='optional_fees', verbose_name=_('student')) + student = models.ForeignKey( + Student, related_name='optional_fees', verbose_name=_('student'), on_delete=models.CASCADE) value = models.FloatField(_('amount'), help_text='€') - description = models.CharField(_('description'), max_length=255, blank=True) + description = models.CharField( + _('description'), max_length=255, blank=True) class Meta(MetaCore): db_table = app_label + '_' + 'optional_fees' @@ -403,9 +427,11 @@ class OptionalFee(models.Model): class Payback(models.Model): "an payback for a student subscription" - student = models.ForeignKey(Student, related_name='paybacks', verbose_name=_('student')) + student = models.ForeignKey( + Student, related_name='paybacks', verbose_name=_('student'), on_delete=models.CASCADE) value = models.FloatField(_('amount'), help_text='€') - description = models.CharField(_('description'), max_length=255, blank=True) + description = models.CharField( + _('description'), max_length=255, blank=True) class Meta(MetaCore): db_table = app_label + '_' + 'paybacks' @@ -417,13 +443,15 @@ class Home(models.Model): title = models.CharField('Title (interne)', max_length=255, default="Page d'accueil") - visible_title = models.CharField(_('Title'), max_length=255, null=True, blank=True) + visible_title = models.CharField( + _('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) + 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) periods = models.ManyToManyField('Period', related_name="home_texts", verbose_name=u'Périodes associées', - blank=True, null=True) + blank=True) enabled = models.BooleanField(u'Activé', default=True) class Meta(MetaCore): @@ -434,12 +462,13 @@ class Home(models.Model): """ Check if it's available for given period """ - periods = [ p['id'] for p in self.periods.values('id') ] + periods = [p['id'] for p in self.periods.values('id')] return not periods or period.id in periods - def __unicode__(self): + def __str__(self): return self.title + class Parameters(models.Model): """ used to store various unique parameters """ @@ -460,30 +489,30 @@ class Parameters(models.Model): except cls.DoesNotExist: return cls() + class NewsItem(models.Model): title = models.CharField(_('Title'), max_length=255) - course = models.ForeignKey(Course, related_name='newsitems', verbose_name=_('course')) - period = models.ForeignKey('Period', related_name='newsitems', verbose_name=_('period'), blank=False, null=True) + course = models.ForeignKey( + Course, related_name='newsitems', verbose_name=_('course'), on_delete=models.CASCADE) + period = models.ForeignKey('Period', related_name='newsitems', verbose_name=_( + 'period'), blank=False, null=True, on_delete=models.SET_NULL) text = HTMLField('Texte') created = models.DateTimeField(_('date created'), auto_now_add=True) - creator = models.ForeignKey(User, related_name='newsitems', verbose_name="Créateur") + creator = models.ForeignKey( + User, related_name='newsitems', verbose_name="Créateur", on_delete=models.CASCADE) deleted = models.BooleanField('Supprimé') class Meta(MetaCore): verbose_name = "Actualité" verbose_name_plural = "Actualités" - def __unicode__(self): + def __str__(self): return "NewsItem %s" % str(self.id) - def can_edit(self, request): return request.user.is_staff or request.user.id == self.creator.id def can_delete(self, request): return request.user.is_staff or request.user.id == self.creator.id - - - diff --git a/teleforma/models/messages.py b/teleforma/models/messages.py index 470570b4..72903c83 100644 --- a/teleforma/models/messages.py +++ b/teleforma/models/messages.py @@ -1,9 +1,15 @@ -from teleforma.models import * +import datetime + from django.contrib.auth.models import User +from django.contrib.sites.models import Site +from django.db import models from django.utils.translation import ugettext_lazy as _ -from postman.utils import email_visitor, notify_user from postman.models import Message +from postman.utils import notify_user + +from ..models import MetaCore, Student +from ..models.core import app_label class StudentGroup(models.Model): @@ -11,13 +17,13 @@ class StudentGroup(models.Model): name = models.CharField(_('name'), max_length=255) students = models.ManyToManyField(Student, related_name="groups", verbose_name=_('students'), - blank=True, null=True) + blank=True) class Meta(MetaCore): db_table = app_label + '_' + 'student_groups' verbose_name = _('Student group') - def __unicode__(self): + def __str__(self): return self.name @@ -25,11 +31,11 @@ class GroupedMessage(models.Model): """(GroupedMessage description)""" group = models.ForeignKey('StudentGroup', related_name='grouped_messages', - verbose_name=_('group'), - blank=True, null=True, on_delete=models.SET_NULL) + verbose_name=_('group'), + blank=True, null=True, on_delete=models.SET_NULL) sender = models.ForeignKey(User, related_name='grouped_messages', - verbose_name=_('sender'), - blank=True, null=True, on_delete=models.SET_NULL) + verbose_name=_('sender'), + blank=True, null=True, on_delete=models.SET_NULL) subject = models.CharField(_('subject'), max_length=119) message = models.TextField(_('message')) to_send = models.BooleanField(_('to send'), default=False) @@ -40,7 +46,7 @@ class GroupedMessage(models.Model): db_table = app_label + '_' + 'grouped_messages' verbose_name = _('Grouped message') - def __unicode__(self): + def __str__(self): return self.group.name + ' ' + self.subject def save(self, *args, **kwargs): @@ -55,7 +61,8 @@ class GroupedMessage(models.Model): site = Site.objects.all()[0] users = [student.user for student in self.group.students.all()] for user in users: - mess = Message(sender=self.sender, recipient=user, subject=self.subject[:119], body=self.message) + mess = Message(sender=self.sender, recipient=user, + subject=self.subject[:119], body=self.message) mess.moderation_status = 'a' mess.save() notify_user(mess, 'acceptance', site) diff --git a/teleforma/models/pro.py b/teleforma/models/pro.py index 30fa2e54..b2ef7189 100644 --- a/teleforma/models/pro.py +++ b/teleforma/models/pro.py @@ -35,40 +35,48 @@ """ import django.db.models as models +from django.contrib.auth.models import User from django.utils.translation import ugettext_lazy as _ -from teleforma.models.core import * - -class Seminar(Model): - - course = models.ForeignKey(Course, related_name='seminar', verbose_name=_('course')) - title = models.CharField(_('title'), max_length=255, blank=True) - price = models.FloatField(_('price'), blank=True, null=True) - status = models.IntegerField(_('status'), choices=STATUS_CHOICES, default=2, blank=True) - rank = models.IntegerField(_('rank')) - concerned = models.CharField(_('public concerned'), max_length=1024, blank=True) - - doc_1 = models.ForeignKey(DocumentSimple, related_name="seminar_doc1", - verbose_name=_('document 1'), - blank=True, null=True) - media = models.ForeignKey(Media, related_name="seminar", - verbose_name=_('media'), - blank=True, null=True) - doc_2 = models.ForeignKey(DocumentSimple, related_name="seminar_doc2", - verbose_name=_('document 2'), - blank=True, null=True) - doc_correct = models.ForeignKey(DocumentSimple, related_name="seminar_doccorrect", - verbose_name=_('corrected document'), - blank=True, null=True) - suscribers = models.ManyToManyField(User, related_name="seminar", verbose_name=_('suscribers'), +from ..fields import DurationField +from ..models import app_label +from ..models.core import (STATUS_CHOICES, WEIGHT_CHOICES, Course, + DocumentSimple, Media, MetaCore, Organization) + + +class Seminar(models.Model): + + course = models.ForeignKey( + Course, related_name='seminar', verbose_name=_('course')) + title = models.CharField(_('title'), max_length=255, blank=True) + price = models.FloatField(_('price'), blank=True, null=True) + status = models.IntegerField( + _('status'), choices=STATUS_CHOICES, default=2, blank=True) + rank = models.IntegerField(_('rank')) + concerned = models.CharField( + _('public concerned'), max_length=1024, blank=True) + + doc_1 = models.ForeignKey(DocumentSimple, related_name="seminar_doc1", + verbose_name=_('document 1'), + blank=True, null=True) + media = models.ForeignKey(Media, related_name="seminar", + verbose_name=_('media'), + blank=True, null=True) + doc_2 = models.ForeignKey(DocumentSimple, related_name="seminar_doc2", + verbose_name=_('document 2'), + blank=True, null=True) + doc_correct = models.ForeignKey(DocumentSimple, related_name="seminar_doccorrect", + verbose_name=_('corrected document'), + blank=True, null=True) + suscribers = models.ManyToManyField(User, related_name="seminar", verbose_name=_('suscribers'), blank=True, null=True) - date_added = models.DateTimeField(_('date added'), auto_now_add=True) - date_modified = models.DateTimeField(_('date modified'), auto_now=True) - duration = DurationField(_('approximative duration')) - keywords = models.CharField(_('keywords'), max_length=1024, blank=True) + date_added = models.DateTimeField(_('date added'), auto_now_add=True) + date_modified = models.DateTimeField(_('date modified'), auto_now=True) + duration = DurationField(_('approximative duration')) + keywords = models.CharField(_('keywords'), max_length=1024, blank=True) - def __unicode__(self): + def __str__(self): return ' - '.join([self.course.title, str(self.rank), self.title]) class Meta(MetaCore): @@ -76,18 +84,19 @@ class Seminar(Model): verbose_name = _('Seminar') -class Question(Model): - - seminar = models.ForeignKey(Seminar, verbose_name=_('seminar')) - title = models.CharField(_('title'), max_length=255, blank=True) - question = models.TextField(_('question')) - rank = models.IntegerField(_('rank')) - weight = models.IntegerField(_('weight'), choices=WEIGHT_CHOICES, default=1) - min_nchar = models.IntegerField(_('minimum numbers of characters')) - status = models.IntegerField(_('status'), choices=STATUS_CHOICES, default=2) +class Question(models.Model): + seminar = models.ForeignKey(Seminar, verbose_name=_('seminar')) + title = models.CharField(_('title'), max_length=255, blank=True) + question = models.TextField(_('question')) + rank = models.IntegerField(_('rank')) + weight = models.IntegerField( + _('weight'), choices=WEIGHT_CHOICES, default=1) + min_nchar = models.IntegerField(_('minimum numbers of characters')) + status = models.IntegerField( + _('status'), choices=STATUS_CHOICES, default=2) - def __unicode__(self): + def __str__(self): return ' - '.join([self.seminar.__unicode__(), str(self.rank), self.title]) class Meta(MetaCore): @@ -95,15 +104,18 @@ class Question(Model): verbose_name = _('Question') -class Answer(Model): +class Answer(models.Model): - user = models.ForeignKey(User, related_name=_("answer"), verbose_name=_('user')) - question = models.ForeignKey(Question, related_name=_("answer"), verbose_name=_('question')) - answer = models.TextField(_('answer')) - status = models.IntegerField(_('status'), choices=STATUS_CHOICES, default=2) - validated = models.BooleanField(_('validated')) + user = models.ForeignKey(User, related_name=_( + "answer"), verbose_name=_('user')) + question = models.ForeignKey(Question, related_name=_( + "answer"), verbose_name=_('question')) + answer = models.TextField(_('answer')) + status = models.IntegerField( + _('status'), choices=STATUS_CHOICES, default=2) + validated = models.BooleanField(_('validated')) - def __unicode__(self): + def __str__(self): return ' - '.join([self.question, self.user]) def validate(self): @@ -116,16 +128,16 @@ class Answer(Model): verbose_name = _('Answer') -class TestimonialTemplate(Model): +class TestimonialTemplate(models.Model): organization = models.ForeignKey(Organization, related_name='testimonial_template', - verbose_name=_('organization')) - description = models.TextField(_('description'), blank=True) - comments = models.TextField(_('comments'), blank=True) - document = models.ForeignKey(DocumentSimple, related_name=_("testimonial_template"), - verbose_name=_('template')) + verbose_name=_('organization')) + description = models.TextField(_('description'), blank=True) + comments = models.TextField(_('comments'), blank=True) + document = models.ForeignKey(DocumentSimple, related_name=_("testimonial_template"), + verbose_name=_('template')) - def __unicode__(self): + def __str__(self): return ' - '.join([self.organization.name, self.description]) class Meta(MetaCore): @@ -133,16 +145,16 @@ class TestimonialTemplate(Model): verbose_name = _('Testimonial template') -class Testimonial(Model): +class Testimonial(models.Model): - seminar = models.ForeignKey(Seminar, verbose_name=_('seminar')) - user = models.ForeignKey(User, related_name=_("testimonial"), verbose_name=_('user')) - template = models.ForeignKey(TestimonialTemplate, related_name=_("testimonial"), - verbose_name=_('testimonial_template')) - document = models.ForeignKey(DocumentSimple, related_name=_("testimonial"), - blank=True, null=True) + seminar = models.ForeignKey(Seminar, verbose_name=_('seminar')) + user = models.ForeignKey(User, related_name=_( + "testimonial"), verbose_name=_('user')) + template = models.ForeignKey(TestimonialTemplate, related_name=_("testimonial"), + verbose_name=_('testimonial_template')) + document = models.ForeignKey(DocumentSimple, related_name=_("testimonial"), + blank=True, null=True) class Meta(MetaCore): db_table = app_label + '_' + 'testimonial' verbose_name = _('Testimonial') - diff --git a/teleforma/migrations/0001_initial.py b/teleforma/south_migrations/0001_initial.py similarity index 100% rename from teleforma/migrations/0001_initial.py rename to teleforma/south_migrations/0001_initial.py diff --git a/teleforma/migrations/0002_auto__add_coursetype__add_field_course_type.py b/teleforma/south_migrations/0002_auto__add_coursetype__add_field_course_type.py similarity index 100% rename from teleforma/migrations/0002_auto__add_coursetype__add_field_course_type.py rename to teleforma/south_migrations/0002_auto__add_coursetype__add_field_course_type.py diff --git a/teleforma/migrations/0003_auto__add_field_course_chat_room.py b/teleforma/south_migrations/0003_auto__add_field_course_chat_room.py similarity index 100% rename from teleforma/migrations/0003_auto__add_field_course_chat_room.py rename to teleforma/south_migrations/0003_auto__add_field_course_chat_room.py diff --git a/teleforma/migrations/0004_auto__add_field_training_category__del_field_course_category.py b/teleforma/south_migrations/0004_auto__add_field_training_category__del_field_course_category.py similarity index 100% rename from teleforma/migrations/0004_auto__add_field_training_category__del_field_course_category.py rename to teleforma/south_migrations/0004_auto__add_field_training_category__del_field_course_category.py diff --git a/teleforma/migrations/0005_auto__add_field_profile_date_added.py b/teleforma/south_migrations/0005_auto__add_field_profile_date_added.py similarity index 100% rename from teleforma/migrations/0005_auto__add_field_profile_date_added.py rename to teleforma/south_migrations/0005_auto__add_field_profile_date_added.py diff --git a/teleforma/migrations/0006_auto.py b/teleforma/south_migrations/0006_auto.py similarity index 100% rename from teleforma/migrations/0006_auto.py rename to teleforma/south_migrations/0006_auto.py diff --git a/teleforma/migrations/0007_auto__del_field_course_chat_room__add_field_professor_training.py b/teleforma/south_migrations/0007_auto__del_field_course_chat_room__add_field_professor_training.py similarity index 100% rename from teleforma/migrations/0007_auto__del_field_course_chat_room__add_field_professor_training.py rename to teleforma/south_migrations/0007_auto__del_field_course_chat_room__add_field_professor_training.py diff --git a/teleforma/migrations/0008_auto__add_field_course_date_modified.py b/teleforma/south_migrations/0008_auto__add_field_course_date_modified.py similarity index 100% rename from teleforma/migrations/0008_auto__add_field_course_date_modified.py rename to teleforma/south_migrations/0008_auto__add_field_course_date_modified.py diff --git a/teleforma/migrations/0009_auto__add_field_media_title__add_field_media_description__add_field_me.py b/teleforma/south_migrations/0009_auto__add_field_media_title__add_field_media_description__add_field_me.py similarity index 100% rename from teleforma/migrations/0009_auto__add_field_media_title__add_field_media_description__add_field_me.py rename to teleforma/south_migrations/0009_auto__add_field_media_title__add_field_media_description__add_field_me.py diff --git a/teleforma/migrations/0010_auto__chg_field_training_category.py b/teleforma/south_migrations/0010_auto__chg_field_training_category.py similarity index 100% rename from teleforma/migrations/0010_auto__chg_field_training_category.py rename to teleforma/south_migrations/0010_auto__chg_field_training_category.py diff --git a/teleforma/migrations/0011_auto__add_livestream__add_streamingserver.py b/teleforma/south_migrations/0011_auto__add_livestream__add_streamingserver.py similarity index 100% rename from teleforma/migrations/0011_auto__add_livestream__add_streamingserver.py rename to teleforma/south_migrations/0011_auto__add_livestream__add_streamingserver.py diff --git a/teleforma/migrations/0012_auto__add_field_conference_streaming.py b/teleforma/south_migrations/0012_auto__add_field_conference_streaming.py similarity index 100% rename from teleforma/migrations/0012_auto__add_field_conference_streaming.py rename to teleforma/south_migrations/0012_auto__add_field_conference_streaming.py diff --git a/teleforma/migrations/0013_auto__del_field_profile_date_added.py b/teleforma/south_migrations/0013_auto__del_field_profile_date_added.py similarity index 100% rename from teleforma/migrations/0013_auto__del_field_profile_date_added.py rename to teleforma/south_migrations/0013_auto__del_field_profile_date_added.py diff --git a/teleforma/migrations/0014_auto__add_documenttype__add_field_document_type.py b/teleforma/south_migrations/0014_auto__add_documenttype__add_field_document_type.py similarity index 100% rename from teleforma/migrations/0014_auto__add_documenttype__add_field_document_type.py rename to teleforma/south_migrations/0014_auto__add_documenttype__add_field_document_type.py diff --git a/teleforma/migrations/0015_auto__del_speciality__del_procedure__del_category__del_oral__add_perio.py b/teleforma/south_migrations/0015_auto__del_speciality__del_procedure__del_category__del_oral__add_perio.py similarity index 100% rename from teleforma/migrations/0015_auto__del_speciality__del_procedure__del_category__del_oral__add_perio.py rename to teleforma/south_migrations/0015_auto__del_speciality__del_procedure__del_category__del_oral__add_perio.py diff --git a/teleforma/migrations/0016_auto.py b/teleforma/south_migrations/0016_auto.py similarity index 100% rename from teleforma/migrations/0016_auto.py rename to teleforma/south_migrations/0016_auto.py diff --git a/teleforma/migrations/0017_auto__add_field_course_number.py b/teleforma/south_migrations/0017_auto__add_field_course_number.py similarity index 100% rename from teleforma/migrations/0017_auto__add_field_course_number.py rename to teleforma/south_migrations/0017_auto__add_field_course_number.py diff --git a/teleforma/migrations/0018_auto__add_payment__add_field_training_cost.py b/teleforma/south_migrations/0018_auto__add_payment__add_field_training_cost.py similarity index 100% rename from teleforma/migrations/0018_auto__add_payment__add_field_training_cost.py rename to teleforma/south_migrations/0018_auto__add_payment__add_field_training_cost.py diff --git a/teleforma/migrations/0019_auto__chg_field_course_number.py b/teleforma/south_migrations/0019_auto__chg_field_course_number.py similarity index 100% rename from teleforma/migrations/0019_auto__chg_field_course_number.py rename to teleforma/south_migrations/0019_auto__chg_field_course_number.py diff --git a/teleforma/migrations/0020_auto__chg_field_training_cost.py b/teleforma/south_migrations/0020_auto__chg_field_training_cost.py similarity index 100% rename from teleforma/migrations/0020_auto__chg_field_training_cost.py rename to teleforma/south_migrations/0020_auto__chg_field_training_cost.py diff --git a/teleforma/migrations/0021_auto__chg_field_course_type.py b/teleforma/south_migrations/0021_auto__chg_field_course_type.py similarity index 100% rename from teleforma/migrations/0021_auto__chg_field_course_type.py rename to teleforma/south_migrations/0021_auto__chg_field_course_type.py diff --git a/teleforma/migrations/0022_auto__add_field_student_network_only.py b/teleforma/south_migrations/0022_auto__add_field_student_network_only.py similarity index 100% rename from teleforma/migrations/0022_auto__add_field_student_network_only.py rename to teleforma/south_migrations/0022_auto__add_field_student_network_only.py diff --git a/teleforma/migrations/0023_auto.py b/teleforma/south_migrations/0023_auto.py similarity index 100% rename from teleforma/migrations/0023_auto.py rename to teleforma/south_migrations/0023_auto.py diff --git a/teleforma/migrations/0024_auto__del_field_training_obligation__del_field_training_synthesis_note.py b/teleforma/south_migrations/0024_auto__del_field_training_obligation__del_field_training_synthesis_note.py similarity index 100% rename from teleforma/migrations/0024_auto__del_field_training_obligation__del_field_training_synthesis_note.py rename to teleforma/south_migrations/0024_auto__del_field_training_obligation__del_field_training_synthesis_note.py diff --git a/teleforma/migrations/0025_auto__del_field_course_type.py b/teleforma/south_migrations/0025_auto__del_field_course_type.py similarity index 100% rename from teleforma/migrations/0025_auto__del_field_course_type.py rename to teleforma/south_migrations/0025_auto__del_field_course_type.py diff --git a/teleforma/migrations/0026_auto.py b/teleforma/south_migrations/0026_auto.py similarity index 100% rename from teleforma/migrations/0026_auto.py rename to teleforma/south_migrations/0026_auto.py diff --git a/teleforma/migrations/0027_auto__add_field_course_synthesis_note__add_field_course_obligation.py b/teleforma/south_migrations/0027_auto__add_field_course_synthesis_note__add_field_course_obligation.py similarity index 100% rename from teleforma/migrations/0027_auto__add_field_course_synthesis_note__add_field_course_obligation.py rename to teleforma/south_migrations/0027_auto__add_field_course_synthesis_note__add_field_course_obligation.py diff --git a/teleforma/migrations/0028_auto__add_field_document_course_type__add_field_livestream_course__add.py b/teleforma/south_migrations/0028_auto__add_field_document_course_type__add_field_livestream_course__add.py similarity index 100% rename from teleforma/migrations/0028_auto__add_field_document_course_type__add_field_livestream_course__add.py rename to teleforma/south_migrations/0028_auto__add_field_document_course_type__add_field_livestream_course__add.py diff --git a/teleforma/migrations/0029_auto.py b/teleforma/south_migrations/0029_auto.py similarity index 100% rename from teleforma/migrations/0029_auto.py rename to teleforma/south_migrations/0029_auto.py diff --git a/teleforma/migrations/0030_auto__add_field_student_procedure__add_field_student_oral_speciality__.py b/teleforma/south_migrations/0030_auto__add_field_student_procedure__add_field_student_oral_speciality__.py similarity index 100% rename from teleforma/migrations/0030_auto__add_field_student_procedure__add_field_student_oral_speciality__.py rename to teleforma/south_migrations/0030_auto__add_field_student_procedure__add_field_student_oral_speciality__.py diff --git a/teleforma/migrations/0031_auto__chg_field_student_period__chg_field_student_iej.py b/teleforma/south_migrations/0031_auto__chg_field_student_period__chg_field_student_iej.py similarity index 100% rename from teleforma/migrations/0031_auto__chg_field_student_period__chg_field_student_iej.py rename to teleforma/south_migrations/0031_auto__chg_field_student_period__chg_field_student_iej.py diff --git a/teleforma/migrations/0032_auto__chg_field_professor_training__chg_field_document_conference__chg.py b/teleforma/south_migrations/0032_auto__chg_field_professor_training__chg_field_document_conference__chg.py similarity index 100% rename from teleforma/migrations/0032_auto__chg_field_professor_training__chg_field_document_conference__chg.py rename to teleforma/south_migrations/0032_auto__chg_field_professor_training__chg_field_document_conference__chg.py diff --git a/teleforma/migrations/0033_auto__add_field_course_magistral.py b/teleforma/south_migrations/0033_auto__add_field_course_magistral.py similarity index 100% rename from teleforma/migrations/0033_auto__add_field_course_magistral.py rename to teleforma/south_migrations/0033_auto__add_field_course_magistral.py diff --git a/teleforma/migrations/0034_auto__add_field_documenttype_number.py b/teleforma/south_migrations/0034_auto__add_field_documenttype_number.py similarity index 100% rename from teleforma/migrations/0034_auto__add_field_documenttype_number.py rename to teleforma/south_migrations/0034_auto__add_field_documenttype_number.py diff --git a/teleforma/migrations/0035_auto__del_field_professor_training__del_field_student_period.py b/teleforma/south_migrations/0035_auto__del_field_professor_training__del_field_student_period.py similarity index 100% rename from teleforma/migrations/0035_auto__del_field_professor_training__del_field_student_period.py rename to teleforma/south_migrations/0035_auto__del_field_professor_training__del_field_student_period.py diff --git a/teleforma/migrations/0036_auto__del_field_document_course_type.py b/teleforma/south_migrations/0036_auto__del_field_document_course_type.py similarity index 100% rename from teleforma/migrations/0036_auto__del_field_document_course_type.py rename to teleforma/south_migrations/0036_auto__del_field_document_course_type.py diff --git a/teleforma/migrations/0037_auto__add_field_livestream_streaming__chg_field_livestream_course_type.py b/teleforma/south_migrations/0037_auto__add_field_livestream_streaming__chg_field_livestream_course_type.py similarity index 100% rename from teleforma/migrations/0037_auto__add_field_livestream_streaming__chg_field_livestream_course_type.py rename to teleforma/south_migrations/0037_auto__add_field_livestream_streaming__chg_field_livestream_course_type.py diff --git a/teleforma/migrations/0038_auto__add_field_conference_public_id.py b/teleforma/south_migrations/0038_auto__add_field_conference_public_id.py similarity index 100% rename from teleforma/migrations/0038_auto__add_field_conference_public_id.py rename to teleforma/south_migrations/0038_auto__add_field_conference_public_id.py diff --git a/teleforma/migrations/0039_auto__chg_field_conference_comment.py b/teleforma/south_migrations/0039_auto__chg_field_conference_comment.py similarity index 100% rename from teleforma/migrations/0039_auto__chg_field_conference_comment.py rename to teleforma/south_migrations/0039_auto__chg_field_conference_comment.py diff --git a/teleforma/migrations/0040_auto__del_field_livestream_course__del_field_livestream_course_type.py b/teleforma/south_migrations/0040_auto__del_field_livestream_course__del_field_livestream_course_type.py similarity index 100% rename from teleforma/migrations/0040_auto__del_field_livestream_course__del_field_livestream_course_type.py rename to teleforma/south_migrations/0040_auto__del_field_livestream_course__del_field_livestream_course_type.py diff --git a/teleforma/migrations/0041_auto__del_field_media_is_live.py b/teleforma/south_migrations/0041_auto__del_field_media_is_live.py similarity index 100% rename from teleforma/migrations/0041_auto__del_field_media_is_live.py rename to teleforma/south_migrations/0041_auto__del_field_media_is_live.py diff --git a/teleforma/migrations/0042_auto__del_field_media_item.py b/teleforma/south_migrations/0042_auto__del_field_media_item.py similarity index 100% rename from teleforma/migrations/0042_auto__del_field_media_item.py rename to teleforma/south_migrations/0042_auto__del_field_media_item.py diff --git a/teleforma/migrations/0043_auto__chg_field_media_course_type__chg_field_media_course.py b/teleforma/south_migrations/0043_auto__chg_field_media_course_type__chg_field_media_course.py similarity index 100% rename from teleforma/migrations/0043_auto__chg_field_media_course_type__chg_field_media_course.py rename to teleforma/south_migrations/0043_auto__chg_field_media_course_type__chg_field_media_course.py diff --git a/teleforma/migrations/0044_auto__add_field_department_domain.py b/teleforma/south_migrations/0044_auto__add_field_department_domain.py similarity index 100% rename from teleforma/migrations/0044_auto__add_field_department_domain.py rename to teleforma/south_migrations/0044_auto__add_field_department_domain.py diff --git a/teleforma/migrations/0045_auto__chg_field_training_code__chg_field_training_cost__chg_field_trai.py b/teleforma/south_migrations/0045_auto__chg_field_training_code__chg_field_training_cost__chg_field_trai.py similarity index 100% rename from teleforma/migrations/0045_auto__chg_field_training_code__chg_field_training_cost__chg_field_trai.py rename to teleforma/south_migrations/0045_auto__chg_field_training_code__chg_field_training_cost__chg_field_trai.py diff --git a/teleforma/migrations/0046_auto__add_field_document_mime_type__add_field_media_mime_type.py b/teleforma/south_migrations/0046_auto__add_field_document_mime_type__add_field_media_mime_type.py similarity index 100% rename from teleforma/migrations/0046_auto__add_field_document_mime_type__add_field_media_mime_type.py rename to teleforma/south_migrations/0046_auto__add_field_document_mime_type__add_field_media_mime_type.py diff --git a/teleforma/migrations/0047_auto__del_payment__add_question__add_answer__add_seminar__add_field_do.py b/teleforma/south_migrations/0047_auto__del_payment__add_question__add_answer__add_seminar__add_field_do.py similarity index 100% rename from teleforma/migrations/0047_auto__del_payment__add_question__add_answer__add_seminar__add_field_do.py rename to teleforma/south_migrations/0047_auto__del_payment__add_question__add_answer__add_seminar__add_field_do.py diff --git a/teleforma/migrations/0048_auto__chg_field_seminar_price.py b/teleforma/south_migrations/0048_auto__chg_field_seminar_price.py similarity index 100% rename from teleforma/migrations/0048_auto__chg_field_seminar_price.py rename to teleforma/south_migrations/0048_auto__chg_field_seminar_price.py diff --git a/teleforma/migrations/0049_auto__add_testimonialtemplate__add_testimonial.py b/teleforma/south_migrations/0049_auto__add_testimonialtemplate__add_testimonial.py similarity index 100% rename from teleforma/migrations/0049_auto__add_testimonialtemplate__add_testimonial.py rename to teleforma/south_migrations/0049_auto__add_testimonialtemplate__add_testimonial.py diff --git a/teleforma/migrations/0050_auto__add_field_testimonial_template.py b/teleforma/south_migrations/0050_auto__add_field_testimonial_template.py similarity index 100% rename from teleforma/migrations/0050_auto__add_field_testimonial_template.py rename to teleforma/south_migrations/0050_auto__add_field_testimonial_template.py diff --git a/teleforma/migrations/0051_auto__add_documentsimple__del_field_testimonialtemplate_template_doc__.py b/teleforma/south_migrations/0051_auto__add_documentsimple__del_field_testimonialtemplate_template_doc__.py similarity index 100% rename from teleforma/migrations/0051_auto__add_documentsimple__del_field_testimonialtemplate_template_doc__.py rename to teleforma/south_migrations/0051_auto__add_documentsimple__del_field_testimonialtemplate_template_doc__.py diff --git a/teleforma/migrations/0052_auto__del_field_testimonialtemplate_text__add_field_testimonialtemplat.py b/teleforma/south_migrations/0052_auto__del_field_testimonialtemplate_text__add_field_testimonialtemplat.py similarity index 100% rename from teleforma/migrations/0052_auto__del_field_testimonialtemplate_text__add_field_testimonialtemplat.py rename to teleforma/south_migrations/0052_auto__del_field_testimonialtemplate_text__add_field_testimonialtemplat.py diff --git a/teleforma/migrations/0053_auto__add_field_seminar_concerned__add_field_seminar_keywords.py b/teleforma/south_migrations/0053_auto__add_field_seminar_concerned__add_field_seminar_keywords.py similarity index 100% rename from teleforma/migrations/0053_auto__add_field_seminar_concerned__add_field_seminar_keywords.py rename to teleforma/south_migrations/0053_auto__add_field_seminar_concerned__add_field_seminar_keywords.py diff --git a/teleforma/migrations/0054_auto__add_aestudent.py b/teleforma/south_migrations/0054_auto__add_aestudent.py similarity index 100% rename from teleforma/migrations/0054_auto__add_aestudent.py rename to teleforma/south_migrations/0054_auto__add_aestudent.py diff --git a/teleforma/migrations/0055_auto__add_field_media_period__add_field_document_period__add_field_doc.py b/teleforma/south_migrations/0055_auto__add_field_media_period__add_field_document_period__add_field_doc.py similarity index 100% rename from teleforma/migrations/0055_auto__add_field_media_period__add_field_document_period__add_field_doc.py rename to teleforma/south_migrations/0055_auto__add_field_media_period__add_field_document_period__add_field_doc.py diff --git a/teleforma/migrations/0056_auto__add_field_conference_department.py b/teleforma/south_migrations/0056_auto__add_field_conference_department.py similarity index 100% rename from teleforma/migrations/0056_auto__add_field_conference_department.py rename to teleforma/south_migrations/0056_auto__add_field_conference_department.py diff --git a/teleforma/migrations/0057_auto__add_field_document_iej.py b/teleforma/south_migrations/0057_auto__add_field_document_iej.py similarity index 100% rename from teleforma/migrations/0057_auto__add_field_document_iej.py rename to teleforma/south_migrations/0057_auto__add_field_document_iej.py diff --git a/teleforma/migrations/0058_auto__add_field_course_title_tweeter.py b/teleforma/south_migrations/0058_auto__add_field_course_title_tweeter.py similarity index 100% rename from teleforma/migrations/0058_auto__add_field_course_title_tweeter.py rename to teleforma/south_migrations/0058_auto__add_field_course_title_tweeter.py diff --git a/teleforma/migrations/0059_auto__add_field_document_annal_year.py b/teleforma/south_migrations/0059_auto__add_field_document_annal_year.py similarity index 100% rename from teleforma/migrations/0059_auto__add_field_document_annal_year.py rename to teleforma/south_migrations/0059_auto__add_field_document_annal_year.py diff --git a/teleforma/migrations/0060_auto__chg_field_student_training.py b/teleforma/south_migrations/0060_auto__chg_field_student_training.py similarity index 100% rename from teleforma/migrations/0060_auto__chg_field_student_training.py rename to teleforma/south_migrations/0060_auto__chg_field_student_training.py diff --git a/teleforma/migrations/0061_auto__del_field_student_training.py b/teleforma/south_migrations/0061_auto__del_field_student_training.py similarity index 100% rename from teleforma/migrations/0061_auto__del_field_student_training.py rename to teleforma/south_migrations/0061_auto__del_field_student_training.py diff --git a/teleforma/migrations/0062_auto__del_answer__del_testimonialtemplate__del_seminar__del_question__.py b/teleforma/south_migrations/0062_auto__del_answer__del_testimonialtemplate__del_seminar__del_question__.py similarity index 100% rename from teleforma/migrations/0062_auto__del_answer__del_testimonialtemplate__del_seminar__del_question__.py rename to teleforma/south_migrations/0062_auto__del_answer__del_testimonialtemplate__del_seminar__del_question__.py diff --git a/teleforma/migrations/0063_auto__add_field_department_default_period.py b/teleforma/south_migrations/0063_auto__add_field_department_default_period.py similarity index 100% rename from teleforma/migrations/0063_auto__add_field_department_default_period.py rename to teleforma/south_migrations/0063_auto__add_field_department_default_period.py diff --git a/teleforma/migrations/0064_auto__add_field_document_session.py b/teleforma/south_migrations/0064_auto__add_field_document_session.py similarity index 100% rename from teleforma/migrations/0064_auto__add_field_document_session.py rename to teleforma/south_migrations/0064_auto__add_field_document_session.py diff --git a/teleforma/migrations/0065_auto__chg_field_document_session.py b/teleforma/south_migrations/0065_auto__chg_field_document_session.py similarity index 100% rename from teleforma/migrations/0065_auto__chg_field_document_session.py rename to teleforma/south_migrations/0065_auto__chg_field_document_session.py diff --git a/teleforma/migrations/0066_auto__add_field_profile_wifi_login__add_field_profile_wifi_pass.py b/teleforma/south_migrations/0066_auto__add_field_profile_wifi_login__add_field_profile_wifi_pass.py similarity index 100% rename from teleforma/migrations/0066_auto__add_field_profile_wifi_login__add_field_profile_wifi_pass.py rename to teleforma/south_migrations/0066_auto__add_field_profile_wifi_login__add_field_profile_wifi_pass.py diff --git a/teleforma/migrations/0067_auto__chg_field_document_file__chg_field_documentsimple_file.py b/teleforma/south_migrations/0067_auto__chg_field_document_file__chg_field_documentsimple_file.py similarity index 100% rename from teleforma/migrations/0067_auto__chg_field_document_file__chg_field_documentsimple_file.py rename to teleforma/south_migrations/0067_auto__chg_field_document_file__chg_field_documentsimple_file.py diff --git a/teleforma/migrations/0068_auto__add_field_professor_department.py b/teleforma/south_migrations/0068_auto__add_field_professor_department.py similarity index 100% rename from teleforma/migrations/0068_auto__add_field_professor_department.py rename to teleforma/south_migrations/0068_auto__add_field_professor_department.py diff --git a/teleforma/migrations/0069_auto__add_field_student_period.py b/teleforma/south_migrations/0069_auto__add_field_student_period.py similarity index 100% rename from teleforma/migrations/0069_auto__add_field_student_period.py rename to teleforma/south_migrations/0069_auto__add_field_student_period.py diff --git a/teleforma/migrations/0070_auto__add_payment.py b/teleforma/south_migrations/0070_auto__add_payment.py similarity index 100% rename from teleforma/migrations/0070_auto__add_payment.py rename to teleforma/south_migrations/0070_auto__add_payment.py diff --git a/teleforma/migrations/0071_auto__add_field_payment_collected.py b/teleforma/south_migrations/0071_auto__add_field_payment_collected.py similarity index 100% rename from teleforma/migrations/0071_auto__add_field_payment_collected.py rename to teleforma/south_migrations/0071_auto__add_field_payment_collected.py diff --git a/teleforma/migrations/0072_auto__add_optionalfee__add_discount__add_field_student_application_fee.py b/teleforma/south_migrations/0072_auto__add_optionalfee__add_discount__add_field_student_application_fee.py similarity index 100% rename from teleforma/migrations/0072_auto__add_optionalfee__add_discount__add_field_student_application_fee.py rename to teleforma/south_migrations/0072_auto__add_optionalfee__add_discount__add_field_student_application_fee.py diff --git a/teleforma/migrations/0073_auto__add_field_student_date_subscribed.py b/teleforma/south_migrations/0073_auto__add_field_student_date_subscribed.py similarity index 100% rename from teleforma/migrations/0073_auto__add_field_student_date_subscribed.py rename to teleforma/south_migrations/0073_auto__add_field_student_date_subscribed.py diff --git a/teleforma/migrations/0074_auto__chg_field_training_code__chg_field_training_cost__chg_field_trai.py b/teleforma/south_migrations/0074_auto__chg_field_training_code__chg_field_training_cost__chg_field_trai.py similarity index 100% rename from teleforma/migrations/0074_auto__chg_field_training_code__chg_field_training_cost__chg_field_trai.py rename to teleforma/south_migrations/0074_auto__chg_field_training_code__chg_field_training_cost__chg_field_trai.py diff --git a/teleforma/migrations/0075_auto__add_studentgroup__add_groupedmessage__add_field_period_date_begi.py b/teleforma/south_migrations/0075_auto__add_studentgroup__add_groupedmessage__add_field_period_date_begi.py similarity index 100% rename from teleforma/migrations/0075_auto__add_studentgroup__add_groupedmessage__add_field_period_date_begi.py rename to teleforma/south_migrations/0075_auto__add_studentgroup__add_groupedmessage__add_field_period_date_begi.py diff --git a/teleforma/migrations/0076_auto__add_coursegroup__add_field_student_promo_code__add_field_student.py b/teleforma/south_migrations/0076_auto__add_coursegroup__add_field_student_promo_code__add_field_student.py similarity index 100% rename from teleforma/migrations/0076_auto__add_coursegroup__add_field_student_promo_code__add_field_student.py rename to teleforma/south_migrations/0076_auto__add_coursegroup__add_field_student_promo_code__add_field_student.py diff --git a/teleforma/migrations/0077_auto__add_field_training_description__add_field_course_procedure__add_.py b/teleforma/south_migrations/0077_auto__add_field_training_description__add_field_course_procedure__add_.py similarity index 100% rename from teleforma/migrations/0077_auto__add_field_training_description__add_field_course_procedure__add_.py rename to teleforma/south_migrations/0077_auto__add_field_training_description__add_field_course_procedure__add_.py diff --git a/teleforma/migrations/0078_auto__add_field_training_available__add_field_student_training.py b/teleforma/south_migrations/0078_auto__add_field_training_available__add_field_student_training.py similarity index 100% rename from teleforma/migrations/0078_auto__add_field_training_available__add_field_student_training.py rename to teleforma/south_migrations/0078_auto__add_field_training_available__add_field_student_training.py diff --git a/teleforma/migrations/0079_auto__add_field_student_date_registered.py b/teleforma/south_migrations/0079_auto__add_field_student_date_registered.py similarity index 100% rename from teleforma/migrations/0079_auto__add_field_student_date_registered.py rename to teleforma/south_migrations/0079_auto__add_field_student_date_registered.py diff --git a/teleforma/migrations/0080_auto__chg_field_period_date_end__chg_field_period_date_begin.py b/teleforma/south_migrations/0080_auto__chg_field_period_date_end__chg_field_period_date_begin.py similarity index 100% rename from teleforma/migrations/0080_auto__chg_field_period_date_end__chg_field_period_date_begin.py rename to teleforma/south_migrations/0080_auto__chg_field_period_date_end__chg_field_period_date_begin.py diff --git a/teleforma/migrations/0081_auto__add_field_period_date_password_init__add_field_period_message_pl.py b/teleforma/south_migrations/0081_auto__add_field_period_date_password_init__add_field_period_message_pl.py similarity index 100% rename from teleforma/migrations/0081_auto__add_field_period_date_password_init__add_field_period_message_pl.py rename to teleforma/south_migrations/0081_auto__add_field_period_date_password_init__add_field_period_message_pl.py diff --git a/teleforma/migrations/0082_auto__add_field_payment_type.py b/teleforma/south_migrations/0082_auto__add_field_payment_type.py similarity index 100% rename from teleforma/migrations/0082_auto__add_field_payment_type.py rename to teleforma/south_migrations/0082_auto__add_field_payment_type.py diff --git a/teleforma/migrations/0083_auto__add_payback.py b/teleforma/south_migrations/0083_auto__add_payback.py similarity index 100% rename from teleforma/migrations/0083_auto__add_payback.py rename to teleforma/south_migrations/0083_auto__add_payback.py diff --git a/teleforma/migrations/0084_auto__add_webclassgroup__add_field_training_parent__add_field_conferen.py b/teleforma/south_migrations/0084_auto__add_webclassgroup__add_field_training_parent__add_field_conferen.py similarity index 100% rename from teleforma/migrations/0084_auto__add_webclassgroup__add_field_training_parent__add_field_conferen.py rename to teleforma/south_migrations/0084_auto__add_webclassgroup__add_field_training_parent__add_field_conferen.py diff --git a/teleforma/migrations/0085_auto__add_field_period_parent.py b/teleforma/south_migrations/0085_auto__add_field_period_parent.py similarity index 100% rename from teleforma/migrations/0085_auto__add_field_period_parent.py rename to teleforma/south_migrations/0085_auto__add_field_period_parent.py diff --git a/teleforma/migrations/0086_auto__add_field_period_is_open.py b/teleforma/south_migrations/0086_auto__add_field_period_is_open.py similarity index 100% rename from teleforma/migrations/0086_auto__add_field_period_is_open.py rename to teleforma/south_migrations/0086_auto__add_field_period_is_open.py diff --git a/teleforma/migrations/0087_auto__add_field_period_date_exam_end.py b/teleforma/south_migrations/0087_auto__add_field_period_date_exam_end.py similarity index 100% rename from teleforma/migrations/0087_auto__add_field_period_date_exam_end.py rename to teleforma/south_migrations/0087_auto__add_field_period_date_exam_end.py diff --git a/teleforma/migrations/0088_auto__add_field_course_exam_scripts.py b/teleforma/south_migrations/0088_auto__add_field_course_exam_scripts.py similarity index 100% rename from teleforma/migrations/0088_auto__add_field_course_exam_scripts.py rename to teleforma/south_migrations/0088_auto__add_field_course_exam_scripts.py diff --git a/teleforma/migrations/0089_auto__add_field_period_department__del_field_course_exam_scripts__add_.py b/teleforma/south_migrations/0089_auto__add_field_period_department__del_field_course_exam_scripts__add_.py similarity index 100% rename from teleforma/migrations/0089_auto__add_field_period_department__del_field_course_exam_scripts__add_.py rename to teleforma/south_migrations/0089_auto__add_field_period_department__del_field_course_exam_scripts__add_.py diff --git a/teleforma/migrations/0090_auto.py b/teleforma/south_migrations/0090_auto.py similarity index 100% rename from teleforma/migrations/0090_auto.py rename to teleforma/south_migrations/0090_auto.py diff --git a/teleforma/migrations/0091_auto__del_field_document_period.py b/teleforma/south_migrations/0091_auto__del_field_document_period.py similarity index 100% rename from teleforma/migrations/0091_auto__del_field_document_period.py rename to teleforma/south_migrations/0091_auto__del_field_document_period.py diff --git a/teleforma/migrations/0092_auto__add_home.py b/teleforma/south_migrations/0092_auto__add_home.py similarity index 100% rename from teleforma/migrations/0092_auto__add_home.py rename to teleforma/south_migrations/0092_auto__add_home.py diff --git a/teleforma/migrations/0093_auto__add_field_home_video__chg_field_home_text.py b/teleforma/south_migrations/0093_auto__add_field_home_video__chg_field_home_text.py similarity index 100% rename from teleforma/migrations/0093_auto__add_field_home_video__chg_field_home_text.py rename to teleforma/south_migrations/0093_auto__add_field_home_video__chg_field_home_text.py diff --git a/teleforma/migrations/0094_auto__add_field_course_quiz.py b/teleforma/south_migrations/0094_auto__add_field_course_quiz.py similarity index 100% rename from teleforma/migrations/0094_auto__add_field_course_quiz.py rename to teleforma/south_migrations/0094_auto__add_field_course_quiz.py diff --git a/teleforma/migrations/0095_auto__del_field_course_quiz.py b/teleforma/south_migrations/0095_auto__del_field_course_quiz.py similarity index 100% rename from teleforma/migrations/0095_auto__del_field_course_quiz.py rename to teleforma/south_migrations/0095_auto__del_field_course_quiz.py diff --git a/teleforma/migrations/0096_auto__add_newsitem.py b/teleforma/south_migrations/0096_auto__add_newsitem.py similarity index 100% rename from teleforma/migrations/0096_auto__add_newsitem.py rename to teleforma/south_migrations/0096_auto__add_newsitem.py diff --git a/teleforma/migrations/0097_auto__add_field_newsitem_period.py b/teleforma/south_migrations/0097_auto__add_field_newsitem_period.py similarity index 100% rename from teleforma/migrations/0097_auto__add_field_newsitem_period.py rename to teleforma/south_migrations/0097_auto__add_field_newsitem_period.py diff --git a/teleforma/migrations/0098_auto__add_field_period_enable_appointment__add_field_period_book_delay.py b/teleforma/south_migrations/0098_auto__add_field_period_enable_appointment__add_field_period_book_delay.py similarity index 100% rename from teleforma/migrations/0098_auto__add_field_period_enable_appointment__add_field_period_book_delay.py rename to teleforma/south_migrations/0098_auto__add_field_period_enable_appointment__add_field_period_book_delay.py diff --git a/teleforma/migrations/0099_auto__add_appointmentjury__add_appointmentperiod__add_appointmentday__.py b/teleforma/south_migrations/0099_auto__add_appointmentjury__add_appointmentperiod__add_appointmentday__.py similarity index 100% rename from teleforma/migrations/0099_auto__add_appointmentjury__add_appointmentperiod__add_appointmentday__.py rename to teleforma/south_migrations/0099_auto__add_appointmentjury__add_appointmentperiod__add_appointmentday__.py diff --git a/teleforma/migrations/0100_auto__chg_field_period_appointment_slot_size.py b/teleforma/south_migrations/0100_auto__chg_field_period_appointment_slot_size.py similarity index 100% rename from teleforma/migrations/0100_auto__chg_field_period_appointment_slot_size.py rename to teleforma/south_migrations/0100_auto__chg_field_period_appointment_slot_size.py diff --git a/teleforma/migrations/0101_auto__add_unique_appointment_slot_jury_slot_nb.py b/teleforma/south_migrations/0101_auto__add_unique_appointment_slot_jury_slot_nb.py similarity index 100% rename from teleforma/migrations/0101_auto__add_unique_appointment_slot_jury_slot_nb.py rename to teleforma/south_migrations/0101_auto__add_unique_appointment_slot_jury_slot_nb.py diff --git a/teleforma/migrations/0102_auto__add_field_appointmentperiod_start__add_field_appointmentperiod_e.py b/teleforma/south_migrations/0102_auto__add_field_appointmentperiod_start__add_field_appointmentperiod_e.py similarity index 100% rename from teleforma/migrations/0102_auto__add_field_appointmentperiod_start__add_field_appointmentperiod_e.py rename to teleforma/south_migrations/0102_auto__add_field_appointmentperiod_start__add_field_appointmentperiod_e.py diff --git a/teleforma/migrations/0103_auto__del_field_appointmentperiod_period__add_field_appointmentperiod_.py b/teleforma/south_migrations/0103_auto__del_field_appointmentperiod_period__add_field_appointmentperiod_.py similarity index 100% rename from teleforma/migrations/0103_auto__del_field_appointmentperiod_period__add_field_appointmentperiod_.py rename to teleforma/south_migrations/0103_auto__del_field_appointmentperiod_period__add_field_appointmentperiod_.py diff --git a/teleforma/migrations/0104_auto__del_appointmentday__del_field_appointmentperiod_book_delay__del_.py b/teleforma/south_migrations/0104_auto__del_appointmentday__del_field_appointmentperiod_book_delay__del_.py similarity index 100% rename from teleforma/migrations/0104_auto__del_appointmentday__del_field_appointmentperiod_book_delay__del_.py rename to teleforma/south_migrations/0104_auto__del_appointmentday__del_field_appointmentperiod_book_delay__del_.py diff --git a/teleforma/migrations/0105_auto__add_field_appointmentperiod_book_delay.py b/teleforma/south_migrations/0105_auto__add_field_appointmentperiod_book_delay.py similarity index 100% rename from teleforma/migrations/0105_auto__add_field_appointmentperiod_book_delay.py rename to teleforma/south_migrations/0105_auto__add_field_appointmentperiod_book_delay.py diff --git a/teleforma/migrations/0106_auto__add_field_period_nb_script.py b/teleforma/south_migrations/0106_auto__add_field_period_nb_script.py similarity index 100% rename from teleforma/migrations/0106_auto__add_field_period_nb_script.py rename to teleforma/south_migrations/0106_auto__add_field_period_nb_script.py diff --git a/teleforma/migrations/0107_auto__add_field_student_balance.py b/teleforma/south_migrations/0107_auto__add_field_student_balance.py similarity index 100% rename from teleforma/migrations/0107_auto__add_field_student_balance.py rename to teleforma/south_migrations/0107_auto__add_field_student_balance.py diff --git a/teleforma/migrations/0108_auto__add_field_documenttype_for_corrector.py b/teleforma/south_migrations/0108_auto__add_field_documenttype_for_corrector.py similarity index 100% rename from teleforma/migrations/0108_auto__add_field_documenttype_for_corrector.py rename to teleforma/south_migrations/0108_auto__add_field_documenttype_for_corrector.py diff --git a/teleforma/migrations/0109_set_allowed_correctors.py b/teleforma/south_migrations/0109_set_allowed_correctors.py similarity index 100% rename from teleforma/migrations/0109_set_allowed_correctors.py rename to teleforma/south_migrations/0109_set_allowed_correctors.py diff --git a/teleforma/migrations/0110_auto__add_field_period_date_close_accounts.py b/teleforma/south_migrations/0110_auto__add_field_period_date_close_accounts.py similarity index 100% rename from teleforma/migrations/0110_auto__add_field_period_date_close_accounts.py rename to teleforma/south_migrations/0110_auto__add_field_period_date_close_accounts.py diff --git a/teleforma/migrations/0111_auto__add_field_home_title__add_field_home_modified_at__add_field_home.py b/teleforma/south_migrations/0111_auto__add_field_home_title__add_field_home_modified_at__add_field_home.py similarity index 100% rename from teleforma/migrations/0111_auto__add_field_home_title__add_field_home_modified_at__add_field_home.py rename to teleforma/south_migrations/0111_auto__add_field_home_title__add_field_home_modified_at__add_field_home.py diff --git a/teleforma/migrations/0112_auto.py b/teleforma/south_migrations/0112_auto.py similarity index 100% rename from teleforma/migrations/0112_auto.py rename to teleforma/south_migrations/0112_auto.py diff --git a/teleforma/migrations/0113_auto__add_field_course_last_professor_sent.py b/teleforma/south_migrations/0113_auto__add_field_course_last_professor_sent.py similarity index 100% rename from teleforma/migrations/0113_auto__add_field_course_last_professor_sent.py rename to teleforma/south_migrations/0113_auto__add_field_course_last_professor_sent.py diff --git a/teleforma/migrations/0114_add_field_Student_fascicule.py b/teleforma/south_migrations/0114_add_field_Student_fascicule.py similarity index 100% rename from teleforma/migrations/0114_add_field_Student_fascicule.py rename to teleforma/south_migrations/0114_add_field_Student_fascicule.py diff --git a/teleforma/migrations/0115_auto__add_field_conference_streaming.py b/teleforma/south_migrations/0115_auto__add_field_conference_streaming.py similarity index 100% rename from teleforma/migrations/0115_auto__add_field_conference_streaming.py rename to teleforma/south_migrations/0115_auto__add_field_conference_streaming.py diff --git a/teleforma/migrations/0116_auto__add_field_training_platform_only.py b/teleforma/south_migrations/0116_auto__add_field_training_platform_only.py similarity index 100% rename from teleforma/migrations/0116_auto__add_field_training_platform_only.py rename to teleforma/south_migrations/0116_auto__add_field_training_platform_only.py diff --git a/teleforma/migrations/0117_auto__add_parameters__add_field_period_date_inscription_start__add_fie.py b/teleforma/south_migrations/0117_auto__add_parameters__add_field_period_date_inscription_start__add_fie.py similarity index 100% rename from teleforma/migrations/0117_auto__add_parameters__add_field_period_date_inscription_start__add_fie.py rename to teleforma/south_migrations/0117_auto__add_parameters__add_field_period_date_inscription_start__add_fie.py diff --git a/teleforma/migrations/0117_auto__del_field_payment_collected__add_field_payment_scheduled__add_fi.py b/teleforma/south_migrations/0117_auto__del_field_payment_collected__add_field_payment_scheduled__add_fi.py similarity index 100% rename from teleforma/migrations/0117_auto__del_field_payment_collected__add_field_payment_scheduled__add_fi.py rename to teleforma/south_migrations/0117_auto__del_field_payment_collected__add_field_payment_scheduled__add_fi.py diff --git a/teleforma/migrations/0118_auto__add_field_training_platform_only__add_field_student_comment.py b/teleforma/south_migrations/0118_auto__add_field_training_platform_only__add_field_student_comment.py similarity index 100% rename from teleforma/migrations/0118_auto__add_field_training_platform_only__add_field_student_comment.py rename to teleforma/south_migrations/0118_auto__add_field_training_platform_only__add_field_student_comment.py diff --git a/teleforma/migrations/0119_auto__add_corrector.py b/teleforma/south_migrations/0119_auto__add_corrector.py similarity index 100% rename from teleforma/migrations/0119_auto__add_corrector.py rename to teleforma/south_migrations/0119_auto__add_corrector.py diff --git a/teleforma/migrations/0120_auto__add_field_profile_birthday_place__add_field_profile_ss_number.py b/teleforma/south_migrations/0120_auto__add_field_profile_birthday_place__add_field_profile_ss_number.py similarity index 100% rename from teleforma/migrations/0120_auto__add_field_profile_birthday_place__add_field_profile_ss_number.py rename to teleforma/south_migrations/0120_auto__add_field_profile_birthday_place__add_field_profile_ss_number.py diff --git a/teleforma/migrations/0121_auto__add_field_student_balance_intermediary.py b/teleforma/south_migrations/0121_auto__add_field_student_balance_intermediary.py similarity index 100% rename from teleforma/migrations/0121_auto__add_field_student_balance_intermediary.py rename to teleforma/south_migrations/0121_auto__add_field_student_balance_intermediary.py diff --git a/teleforma/migrations/0122_auto__add_field_training_cost_elearning_fascicle__add_field_training_c.py b/teleforma/south_migrations/0122_auto__add_field_training_cost_elearning_fascicle__add_field_training_c.py similarity index 100% rename from teleforma/migrations/0122_auto__add_field_training_cost_elearning_fascicle__add_field_training_c.py rename to teleforma/south_migrations/0122_auto__add_field_training_cost_elearning_fascicle__add_field_training_c.py diff --git a/teleforma/migrations/0123_auto__add_field_training_duration.py b/teleforma/south_migrations/0123_auto__add_field_training_duration.py similarity index 100% rename from teleforma/migrations/0123_auto__add_field_training_duration.py rename to teleforma/south_migrations/0123_auto__add_field_training_duration.py diff --git a/teleforma/migrations/0124_auto__add_field_student_receipt_id.py b/teleforma/south_migrations/0124_auto__add_field_student_receipt_id.py similarity index 100% rename from teleforma/migrations/0124_auto__add_field_student_receipt_id.py rename to teleforma/south_migrations/0124_auto__add_field_student_receipt_id.py diff --git a/teleforma/migrations/0125_auto__add_field_home_visible_title__chg_field_home_text.py b/teleforma/south_migrations/0125_auto__add_field_home_visible_title__chg_field_home_text.py similarity index 100% rename from teleforma/migrations/0125_auto__add_field_home_visible_title__chg_field_home_text.py rename to teleforma/south_migrations/0125_auto__add_field_home_visible_title__chg_field_home_text.py diff --git a/teleforma/migrations/0126_auto__add_field_profile_nationality.py b/teleforma/south_migrations/0126_auto__add_field_profile_nationality.py similarity index 100% rename from teleforma/migrations/0126_auto__add_field_profile_nationality.py rename to teleforma/south_migrations/0126_auto__add_field_profile_nationality.py diff --git a/teleforma/migrations/0127_auto__add_field_profile_siret.py b/teleforma/south_migrations/0127_auto__add_field_profile_siret.py similarity index 100% rename from teleforma/migrations/0127_auto__add_field_profile_siret.py rename to teleforma/south_migrations/0127_auto__add_field_profile_siret.py diff --git a/teleforma/migrations/0128_auto__add_field_appointmentperiod_course__add_field_appointmentperiod_.py b/teleforma/south_migrations/0128_auto__add_field_appointmentperiod_course__add_field_appointmentperiod_.py similarity index 100% rename from teleforma/migrations/0128_auto__add_field_appointmentperiod_course__add_field_appointmentperiod_.py rename to teleforma/south_migrations/0128_auto__add_field_appointmentperiod_course__add_field_appointmentperiod_.py diff --git a/teleforma/migrations/0129_auto__del_field_appointmentperiod_bbb_room__add_field_student_restrict.py b/teleforma/south_migrations/0129_auto__del_field_appointmentperiod_bbb_room__add_field_student_restrict.py similarity index 100% rename from teleforma/migrations/0129_auto__del_field_appointmentperiod_bbb_room__add_field_student_restrict.py rename to teleforma/south_migrations/0129_auto__del_field_appointmentperiod_bbb_room__add_field_student_restrict.py diff --git a/teleforma/migrations/0130_auto__add_field_payment_date_paid.py b/teleforma/south_migrations/0130_auto__add_field_payment_date_paid.py similarity index 100% rename from teleforma/migrations/0130_auto__add_field_payment_date_paid.py rename to teleforma/south_migrations/0130_auto__add_field_payment_date_paid.py diff --git a/teleforma/migrations/0131_auto__add_mediatranscoded__chg_field_period_department__add_field_medi.py b/teleforma/south_migrations/0131_auto__add_mediatranscoded__chg_field_period_department__add_field_medi.py similarity index 100% rename from teleforma/migrations/0131_auto__add_mediatranscoded__chg_field_period_department__add_field_medi.py rename to teleforma/south_migrations/0131_auto__add_mediatranscoded__chg_field_period_department__add_field_medi.py diff --git a/teleforma/migrations/0132_auto__del_field_media_item.py b/teleforma/south_migrations/0132_auto__del_field_media_item.py similarity index 100% rename from teleforma/migrations/0132_auto__del_field_media_item.py rename to teleforma/south_migrations/0132_auto__del_field_media_item.py diff --git a/teleforma/migrations/__init__.py b/teleforma/south_migrations/__init__.py similarity index 100% rename from teleforma/migrations/__init__.py rename to teleforma/south_migrations/__init__.py diff --git a/teleforma/templatetags/payment.py b/teleforma/templatetags/payment.py index 73dbc70f..9fe54c9a 100644 --- a/teleforma/templatetags/payment.py +++ b/teleforma/templatetags/payment.py @@ -2,7 +2,7 @@ from django import template from datetime import date -from teleforma.models import Payment +from ..models import Payment register = template.Library() @register.inclusion_tag('payment/payment_summary.html', diff --git a/teleforma/templatetags/teleforma_tags.py b/teleforma/templatetags/teleforma_tags.py index ab4d9e54..e75bc89a 100644 --- a/teleforma/templatetags/teleforma_tags.py +++ b/teleforma/templatetags/teleforma_tags.py @@ -32,20 +32,26 @@ # # Authors: Guillaume Pellerin +import datetime +import json +import re +import urllib.parse as urlparse + from django import template +from django.conf import settings +from django.contrib.auth.models import User +from django.db.models.query_utils import Q from django.shortcuts import get_object_or_404 -import json -from timezones.utils import localtime_for_timezone +from django.urls.base import reverse +from django.utils.encoding import force_text, smart_str +from django.utils.safestring import mark_safe from django.utils.translation import ugettext_lazy as _ -from urlparse import urlparse from docutils.core import publish_parts -from django.utils.encoding import smart_str, force_unicode -from django.utils.safestring import mark_safe -from teleforma.models.core import Document -from teleforma.models.crfpa import Course, NewsItem -from teleforma.views import get_courses -from teleforma.exam.models import * +from ..exam.models import Quota, Script +from ..models.core import Document, Professor +from ..models.crfpa import IEJ, Course, NewsItem, Training +from ..views import get_courses register = template.Library() @@ -53,16 +59,19 @@ register = template.Library() title = _('General tweeter') title = _('Local tweeter') + class TeleFormaVersionNode(template.Node): def render(self, context): from teleforma import __version__ return __version__ + @register.tag def teleforma_version(parser, token): "Get TeleForma version number" return TeleFormaVersionNode() + @register.filter def parse_urls(text): output = '' @@ -73,31 +82,37 @@ def parse_urls(text): output += block return output + @register.filter('startswith') def startswith(text, starts): - if isinstance(text, basestring): + if isinstance(text, str): return text.startswith(starts) return False + @register.tag def value_from_settings(parser, token): try: # split_contents() knows not to split quoted strings. tag_name, var = token.split_contents() except ValueError: - raise template.TemplateSyntaxError, "%r tag requires a single argument" % token.contents.split()[0] + raise template.TemplateSyntaxError("%r tag requires a single argument" % token.contents.split()[0]) return ValueFromSettings(var) + class ValueFromSettings(template.Node): def __init__(self, var): self.arg = template.Variable(var) + def render(self, context): return settings.__getattr__(str(self.arg)) + @register.filter def user_courses(user): return get_courses(user) + @register.filter def to_recipients(users): list = [] @@ -105,9 +120,6 @@ def to_recipients(users): list.append(user.username) return ':'.join(list) -@register.filter -def localtime(value, timezone): - return localtime_for_timezone(value, timezone) @register.filter def or_me(value, arg): @@ -117,12 +129,13 @@ def or_me(value, arg): Typical usage: sender|or_me:user """ - if not isinstance(value, (unicode, str)): - value = unicode(value) - if not isinstance(arg, (unicode, str)): - arg = unicode(arg) + if not isinstance(value, str): + value = str(value) + if not isinstance(arg, str): + arg = str(arg) return _('me') if value == arg else value + @register.filter def yes_no(bool): if bool: @@ -130,6 +143,7 @@ def yes_no(bool): else: return _('No') + @register.filter def get_item(dictionary, key): try: @@ -137,21 +151,25 @@ def get_item(dictionary, key): except AttributeError: return dictionary[key] + @register.filter def from_course_type(contents, type): if contents: return contents.filter(course_type=type) + @register.filter def streaming_only(contents): if contents: return contents.filter(streaming=True) + @register.filter def from_doc_type(contents, type): if contents: return contents.filter(type=type) + @register.filter def from_period(contents, period): if contents: @@ -160,25 +178,27 @@ def from_period(contents, period): else: return contents.filter(period=period) -@register.assignment_tag + +@register.simple_tag def get_all_professors(): return Professor.objects.all() -@register.assignment_tag + +@register.simple_tag def get_all_professors_with_courses(): professors = [] for professor in Professor.objects.order_by('user__last_name').all(): name = professor.user.last_name + professor.user.first_name if name: professors.append({ - 'username':professor.user.username, - 'name':professor.user.last_name + " " + professor.user.first_name, - 'courses':json.dumps([course.id for course in professor.courses.all()]) + 'username': professor.user.username, + 'name': professor.user.last_name + " " + professor.user.first_name, + 'courses': json.dumps([course.id for course in professor.courses.all()]) }) return professors -@register.assignment_tag +@register.simple_tag def get_all_correctors_with_courses(): correctors = {} @@ -195,39 +215,45 @@ def get_all_correctors_with_courses(): if name: result.append({ 'id': corrector.id, - 'username':corrector.username, - 'name':corrector.last_name + " " + corrector.first_name, - 'courses':json.dumps(list(correctors[corrector])) + 'username': corrector.username, + 'name': corrector.last_name + " " + corrector.first_name, + 'courses': json.dumps(list(correctors[corrector])) }) - result = sorted(result, key=lambda corrector:int(corrector['id'])) + result = sorted(result, key=lambda corrector: int(corrector['id'])) return result -@register.assignment_tag +@register.simple_tag def get_all_admins(): return User.objects.filter(is_superuser=True).order_by('last_name') -@register.assignment_tag + +@register.simple_tag def get_all_trainings(): return Training.objects.all() -@register.assignment_tag + +@register.simple_tag def get_all_iejs(): return IEJ.objects.all() -@register.assignment_tag + +@register.simple_tag def get_all_courses(): return Course.objects.all() -@register.assignment_tag + +@register.simple_tag def get_telecaster(): return 'telecaster' in settings.INSTALLED_APPS -@register.assignment_tag + +@register.simple_tag def get_googletools(): return 'googletools' in settings.INSTALLED_APPS -@register.assignment_tag + +@register.simple_tag def show_chat(user): """ everybody should see the chat panel, except the correctors """ professor = user.professor.all() @@ -245,6 +271,7 @@ def get_audio_id(media): return m.id return + @register.filter def get_video_id(media): if media.conference: @@ -254,6 +281,7 @@ def get_video_id(media): return m.id return + @register.filter def get_host(url, host): u = urlparse(url) @@ -263,31 +291,36 @@ def get_host(url, host): else: return url + @register.filter def published(doc): if doc: return doc.filter(is_published=True) + def scripts_count(user, period, statuses): if not period: return '' Q1 = Q(author=user) Q2 = Q(corrector=user) - scripts = Script.objects.filter(Q1 | Q2).filter(status__in = statuses, - period = period) + scripts = Script.objects.filter(Q1 | Q2).filter(status__in=statuses, + period=period) if scripts: return ' (' + str(len(scripts)) + ')' else: return '' + @register.simple_tag def untreated_scripts_count(user, period): return scripts_count(user, period, (3,)) + @register.simple_tag def treated_scripts_count(user, period): return scripts_count(user, period, (4,)) + @register.simple_tag def get_training_profile(user): text = '' @@ -298,37 +331,41 @@ def get_training_profile(user): if student.platform_only: text += 'Internaute - ' for training in student.trainings.all(): - text += unicode(training) + ' ' + text += str(training) + ' ' return text + @register.inclusion_tag('teleforma/inc/newsitems_portlet.html', takes_context=True) def newsitems_portlet(context, course_id, period_id): request = context['request'] user = request.user + def get_data(newsitem): return { - 'id':newsitem.id, - 'title':newsitem.title, - 'text':newsitem.text, - 'creator':newsitem.creator, - 'created':newsitem.created, - 'can_edit':newsitem.can_edit(request), - 'can_delete':newsitem.can_delete(request), + 'id': newsitem.id, + 'title': newsitem.title, + 'text': newsitem.text, + 'creator': newsitem.creator, + 'created': newsitem.created, + 'can_edit': newsitem.can_edit(request), + 'can_delete': newsitem.can_delete(request), } course = get_object_or_404(Course, id=course_id) - course_newsitems = [get_data(news) for news in NewsItem.objects.filter(deleted=False, course__id=course_id, period_id=period_id).order_by('-created')] - all_newsitems = [get_data(news) for news in NewsItem.objects.filter(deleted=False, period_id=period_id).order_by('-created')] + course_newsitems = [get_data(news) for news in NewsItem.objects.filter( + deleted=False, course__id=course_id, period_id=period_id).order_by('-created')] + all_newsitems = [get_data(news) for news in NewsItem.objects.filter( + deleted=False, period_id=period_id).order_by('-created')] can_add = False if user.is_staff or user.professor.count(): can_add = True return { - 'can_add':can_add, - 'course':course, - 'period_id':period_id, - 'course_newsitems':course_newsitems, - 'all_newsitems':all_newsitems - } + 'can_add': can_add, + 'course': course, + 'period_id': period_id, + 'course_newsitems': course_newsitems, + 'all_newsitems': all_newsitems + } ##### FROM TELEMETA ##### @@ -337,23 +374,27 @@ def newsitems_portlet(context, course_id, period_id): def description(): return settings.TELEFORMA_DESCRIPTION + @register.simple_tag def organization(): return settings.TELEFORMA_ORGANIZATION + @register.simple_tag def current_year(): return datetime.datetime.now().strftime("%Y") + @register.filter def render_flatpage(content): parsed = "" path = getattr(content, 'path', '') - if isinstance(content, basestring): + if isinstance(content, str): content = content.split("\n") for line in content: - match = re.match('^(\.\. *(?:_[^:]*:|(?:\|\w+\|)? *image::) *)([^ ]+) *$', line) + match = re.match( + '^(\.\. *(?:_[^:]*:|(?:\|\w+\|)? *image::) *)([^ ]+) *$', line) if match: directive, urlname = match.groups() line = directive @@ -364,12 +405,16 @@ def render_flatpage(content): if i == 0: line += reverse(urlname) elif urlname[:1] != '/': - line += reverse('telemeta-flatpage', args=[path + '/../' + urlname]) + line += reverse('telemeta-flatpage', + args=[path + '/../' + urlname]) else: line += urlname parsed += line + "\n" - parts = publish_parts(source=smart_str(parsed), writer_name="html4css1", settings_overrides={}) - return mark_safe('
\n' + force_unicode(parts["html_body"]) + '
') -render_flatpage.is_safe = True \ No newline at end of file + parts = publish_parts(source=smart_str( + parsed), writer_name="html4css1", settings_overrides={}) + return mark_safe('
\n' + force_text(parts["html_body"]) + '
') + + +render_flatpage.is_safe = True diff --git a/teleforma/templatetags/webclass.py b/teleforma/templatetags/webclass.py index c7698b0a..8bdb649a 100644 --- a/teleforma/templatetags/webclass.py +++ b/teleforma/templatetags/webclass.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- from django import template -from teleforma.models.crfpa import Period +from ..models.core import Period register = template.Library() diff --git a/teleforma/urls.py b/teleforma/urls.py index 673104a8..b0e3190d 100644 --- a/teleforma/urls.py +++ b/teleforma/urls.py @@ -33,16 +33,39 @@ # Authors: Guillaume Pellerin import os.path -from django.conf.urls import patterns, url, include -from django.conf import settings -from django.views.generic.base import RedirectView, TemplateView -from django.views.generic.list import ListView -from teleforma.models import * -from teleforma.views import * -from teleforma.forms import * -from registration.views import * + +from django.conf.urls import include, url +from django.contrib.auth.views import (LoginView, LogoutView, + PasswordChangeDoneView, + PasswordChangeView, + PasswordContextMixin, + PasswordResetCompleteView, + PasswordResetConfirmView, + PasswordResetDoneView, + PasswordResetView) +from django.views.generic.base import TemplateView from jsonrpc import jsonrpc_site +from teleforma.views.home import HomeView + +from .views.appointment import Appointments, cancel_appointment +from .views.core import (ConferenceListView, ConferenceView, CourseListView, + CoursePendingListView, CourseView, DocumentView, + HelpView, HomeRedirectView, MediaTranscodedView, + MediaView, MediaViewEmbed) +from .views.crfpa import (AnnalsCourseView, AnnalsIEJView, AnnalsView, + CorrectorAddView, CorrectorCompleteView, + CorrectorRegistrationPDFView, + CorrectorRegistrationPDFViewDownload, + CRFPAProfileView, NewsItemCreate, NewsItemDelete, + NewsItemList, NewsItemUpdate, ReceiptPDFView, + ReceiptPDFViewDownload, RegistrationPDFView, + RegistrationPDFViewDownload, UserAddView, + UserCompleteView, UserLoginView, UsersExportView, + UsersView, WriteView, update_training) +from .views.payment import (PaymentStartView, bank_auto, bank_cancel, + bank_fail, bank_success) + htdocs_forma = os.path.dirname(__file__) + '/static/teleforma/' profile_view = CRFPAProfileView() document = DocumentView() @@ -50,153 +73,194 @@ media = MediaView() home_view = HomeView() media_transcoded = MediaTranscodedView -urlpatterns = patterns('', - - # login / logout - url(r'^login/$', 'django.contrib.auth.views.login', {'template_name': 'teleforma/login.html'}, - name="telemeta-login"), - url(r'^accounts/login/$', 'django.contrib.auth.views.login', {'template_name': 'registration/login.html'}, - name="teleforma-login"), - url(r'^logout/$', 'django.contrib.auth.views.logout', name="teleforma-logout"), - - # (r'^accounts/register0/$', RegistrationView.as_view(), {'form_class':CustomRegistrationForm}), - url(r'^accounts/register/$', UserAddView.as_view(), name="teleforma-register"), - url(r'^accounts/register/(?P.*)/complete/$', UserCompleteView.as_view(), name="teleforma-register-complete"), - url(r'^accounts/register/(?P.*)/download/$', RegistrationPDFViewDownload.as_view(), name="teleforma-registration-download"), - url(r'^accounts/register/(?P.*)/view/$', RegistrationPDFView.as_view(), name="teleforma-registration-view"), - - url(r'^correctors/register/$', CorrectorAddView.as_view(), name="teleforma-corrector-register"), - url(r'^correctors/register/(?P.*)/complete/$', CorrectorCompleteView.as_view(), name="teleforma-corrector-register-complete"), - url(r'^correctors/register/(?P.*)/download/$', CorrectorRegistrationPDFViewDownload.as_view(), name="teleforma-corrector-registration-download"), - url(r'^correctors/register/(?P.*)/view/$', CorrectorRegistrationPDFView.as_view(), name="teleforma-corrector-registration-view"), - - url(r'^users/(?P[A-Za-z0-9+@._-]+)/profile/$', profile_view.profile_detail, - name="teleforma-profile-detail"), - url(r'^accounts/(?P[A-Za-z0-9+@._-]+)/profile/$', profile_view.profile_detail, name="teleforma-profile-detail"), - url(r'^accounts/(?P[A-Za-z0-9+@._-]+)/profile/edit/$', profile_view.profile_edit, name="teleforma-profile-edit"), - - # Registration - url(r'^accounts/password_change/$', 'django.contrib.auth.views.password_change', {'template_name': 'registration/password_change_form.html'}, name="teleforma-password-change"), - url(r'^accounts/password_change_done/$', 'django.contrib.auth.views.password_change_done', {'template_name': 'registration/password_change_done.html'}, name="teleforma-password-change-done"), - - url(r'^accounts/password_reset/$', 'django.contrib.auth.views.password_reset', {'template_name': 'registration/password_reset_form.html', 'email_template_name': 'registration/password_reset_email.html'}, name="teleforma-password-reset"), - url(r'^accounts/password_reset_done/$', 'django.contrib.auth.views.password_reset_done', {'template_name': 'registration/password_reset_done.html'}, name="teleforma-password-reset-done"), - url(r'^accounts/password_reset_confirm/(?P[A-Za-z0-9._-]+)/(?P[A-Za-z0-9._-]+)/$', 'django.contrib.auth.views.password_reset_confirm', {'template_name': 'registration/password_reset_confirm.html'}, name="teleforma-password-reset-confirm"), - url(r'^accounts/password_reset_complete/$', 'django.contrib.auth.views.password_reset_complete', {'template_name': 'registration/password_reset_complete.html'}, name="teleforma-password-reset-complete"), - url(r'^accounts/password_reset_complete/$', 'django.contrib.auth.views.password_reset_complete', {'template_name': 'registration/password_reset_complete.html'}, name="teleforma-password-reset-complete"), - - - url(r'^captcha/', include('captcha.urls')), - - # Help - url(r'^help/$', HelpView.as_view(), name="teleforma-help"), - - # Home - url(r'^$', HomeRedirectView.as_view(), name="teleforma-home"), - - # Flat pages - url(r'^pages/(?P.*)$', home_view.render_flatpage, name="teleforma-flatpage"), - # Unauthorized - url(r'^unauthorized/$', TemplateView.as_view(template_name="teleforma/unauthorized.html"), name="teleforma-unauthorized"), - - - # Desk - url(r'^desk/$', HomeRedirectView.as_view(), name="teleforma-desk"), - url(r'^desk/periods/(?P.*)/courses/$', CourseListView.as_view(), name="teleforma-desk-period-list"), - url(r'^desk/periods/(?P.*)/courses_pending/$', CoursePendingListView.as_view(), name="teleforma-desk-period-pending"), - url(r'^desk/periods/(?P.*)/courses/(?P.*)/detail/$', CourseView.as_view(), - name="teleforma-desk-period-course"), - - - url(r'^desk/periods/(?P.*)/medias/transcode/(?P.*)/detail/$', MediaTranscodedView.as_view(), name="teleforma-media-transcoded"), - url(r'^desk/periods/(?P.*)/medias/transcode/(?P.*)/download/$', media_transcoded.download, name="teleforma-media-transcoded-download"), - url(r'^desk/periods/(?P.*)/medias/transcode/(?P.*)/stream/$', media_transcoded.stream, name="teleforma-media-transcoded-stream"), - url(r'^desk/periods/(?P.*)/medias/(?P.*)/detail/$', MediaView.as_view(), name="teleforma-media-detail"), - url(r'^desk/periods/(?P.*)/medias/(?P.*)/embed/$', MediaViewEmbed.as_view(), name="teleforma-media-embed"), - url(r'^desk/periods/(?P.*)/medias/(?P.*)/download/$', media.download, name="teleforma-media-download"), - url(r'^desk/periods/(?P.*)/medias/(?P.*)/stream/$', media.stream, name="teleforma-media-stream"), - - url(r'^desk/documents/(?P.*)/detail/$', DocumentView.as_view(), - name="teleforma-document-detail"), - url(r'^desk/documents/(?P.*)/download/$', document.download, - name="teleforma-document-download"), - url(r'^desk/documents/(?P.*)/view/$', document.view, - name="teleforma-document-view"), - - url(r'^archives/annals/$', AnnalsView.as_view(), name="teleforma-annals"), - url(r'^archives/annals/by-iej/(\w+)/$', AnnalsIEJView.as_view(), name="teleforma-annals-iej"), - url(r'^archives/annals/by-course/(\w+)/$', AnnalsCourseView.as_view(), name="teleforma-annals-course"), - - url(r'^desk/periods/(?P.*)/conferences/(?P.*)/video/$', - ConferenceView.as_view(), name="teleforma-conference-detail"), - url(r'^desk/periods/(?P.*)/conferences/(?P.*)/audio/$', - ConferenceView.as_view(template_name="teleforma/course_conference_audio.html"), - name="teleforma-conference-audio"), - url(r'^desk/conference_record/$', ConferenceRecordView.as_view(), - name="teleforma-conference-record"), - url(r'^desk/periods/(?P.*)/conferences/list/$', ConferenceListView.as_view(), - name="teleforma-conferences"), - - # APPOINTMENTS - url(r'^desk/periods/(?P.*)/appointments/(?P.*)/$', Appointments.as_view(), - name="teleforma-appointments"), - url(r'^desk/periods/appointments/cancel$', cancel_appointment, - name="teleforma-appointment-cancel"), - - # Postman - url(r'^messages/write/(?:(?P[^/#]+)/)?$', WriteView.as_view(), name='postman_write'), - url(r'^messages/', include('postman.urls')), - - - # Users - url(r'^users/training/(?P.*)/iej/(?P.*)/course/(?P.*)/list/$', - UsersView.as_view(), name="teleforma-users"), - - url(r'^users/training/(?P.*)/iej/(?P.*)/course/(?P.*)/export/$', - UsersExportView.as_view(), name="teleforma-users-export"), - - url(r'^users/(?P.*)/login/$', UserLoginView.as_view(), name="teleforma-user-login"), - - # Ajax update training - url(r'^update-training/(?P.*)/$', update_training, name="update-training"), - - # News Item - url(r'^desk/periods/(?P.*)/medias/(?P.*)/detail/$', MediaView.as_view(), name="teleforma-media-detail"), - url(r'^newsitems/create', NewsItemCreate.as_view(), name='newsitem-create'), - url(r'^newsitems/update/(?P.*)', NewsItemUpdate.as_view(), name='newsitem-update'), - url(r'^newsitems/delete/(?P.*)', NewsItemDelete.as_view(), name='newsitem-delete'), - url(r'^newsitems/(?P.*)/list', NewsItemList.as_view(), name='newsitem-list'), - - # JSON RPC - url(r'json/$', jsonrpc_site.dispatch, name='jsonrpc_mountpoint'), - url(r'jsonrpc/$', jsonrpc_site.dispatch, name='jsonrpc_mountpoint'), - -# url(r'^private_files/', include('private_files.urls')), - - # JQCHAT - url(r'^', include('jqchat.urls')), - - # EXAM - url(r'^', include('teleforma.exam.urls')), - - # WEBCLASS - url(r'^', include('teleforma.webclass.urls')), - - # Payment - url(r'^payment/(?P.*)/start/$', PaymentStartView.as_view(), - name="teleforma-payment-start"), - - url(r'^payment/bank_auto/(?P.*)', - bank_auto, name='teleforma-bank-auto'), - url(r'^payment/bank_success/(?P.*)', - bank_success, name='teleforma-bank-success'), - url(r'^payment/bank_cancel/(?P.*)', - bank_cancel, name='teleforma-bank-cancel'), - - url(r'^echec-de-paiement', - bank_fail, name='teleforma-bank-fail'), - - url(r'^accounts/(?P[A-Za-z0-9+@._-]+)/receipt/download/$', ReceiptPDFViewDownload.as_view(), name="teleforma-receipt-download"), - url(r'^accounts/(?P[A-Za-z0-9+@._-]+)/receipt/view/$', ReceiptPDFView.as_view(), name="teleforma-receipt-view"), - -) +urlpatterns = [ + # login / logout + url(r'^login/$', LoginView.as_view(template_name='teleforma/login.html'), + name="telemeta-login"), + url(r'^accounts/login/$', LoginView.as_view(template_name='registration/login.html'), + name="teleforma-login"), + url(r'^logout/$', LogoutView.as_view(), + name="teleforma-logout"), + + # (r'^accounts/register0/$', RegistrationView.as_view(), {'form_class':CustomRegistrationForm}), + url(r'^accounts/register/$', UserAddView.as_view(), + name="teleforma-register"), + url(r'^accounts/register/(?P.*)/complete/$', + UserCompleteView.as_view(), name="teleforma-register-complete"), + url(r'^accounts/register/(?P.*)/download/$', + RegistrationPDFViewDownload.as_view(), name="teleforma-registration-download"), + url(r'^accounts/register/(?P.*)/view/$', + RegistrationPDFView.as_view(), name="teleforma-registration-view"), + + url(r'^correctors/register/$', CorrectorAddView.as_view(), + name="teleforma-corrector-register"), + url(r'^correctors/register/(?P.*)/complete/$', + CorrectorCompleteView.as_view(), name="teleforma-corrector-register-complete"), + url(r'^correctors/register/(?P.*)/download/$', + CorrectorRegistrationPDFViewDownload.as_view(), name="teleforma-corrector-registration-download"), + url(r'^correctors/register/(?P.*)/view/$', + CorrectorRegistrationPDFView.as_view(), name="teleforma-corrector-registration-view"), + + url(r'^users/(?P[A-Za-z0-9+@._-]+)/profile/$', profile_view.profile_detail, + name="teleforma-profile-detail"), + url(r'^accounts/(?P[A-Za-z0-9+@._-]+)/profile/$', + profile_view.profile_detail, name="teleforma-profile-detail"), + url(r'^accounts/(?P[A-Za-z0-9+@._-]+)/profile/edit/$', + profile_view.profile_edit, name="teleforma-profile-edit"), + + # Registration + url(r'^accounts/password_change/$', PasswordChangeView.as_view(template_name='registration/password_change_form.html'), + name="teleforma-password-change"), + url(r'^accounts/password_change_done/$', PasswordChangeDoneView.as_view( + template_name='registration/password_change_done.html'), name="teleforma-password-change-done"), + + url(r'^accounts/password_reset/$', PasswordResetView.as_view(template_name='registration/password_reset_form.html', + email_template_name='registration/password_reset_email.html'), name="teleforma-password-reset"), + url(r'^accounts/password_reset_done/$', PasswordResetDoneView.as_view( + template_name='registration/password_reset_done.html'), name="teleforma-password-reset-done"), + url(r'^accounts/password_reset_confirm/(?P[A-Za-z0-9._-]+)/(?P[A-Za-z0-9._-]+)/$', PasswordResetConfirmView.as_view( + template_name='registration/password_reset_confirm.html'), name="teleforma-password-reset-confirm"), + url(r'^accounts/password_reset_complete/$', PasswordResetCompleteView.as_view(template_name='registration/password_reset_complete.html'), + name="teleforma-password-reset-complete"), + url(r'^accounts/password_reset_complete/$', PasswordResetCompleteView.as_view( + template_name='registration/password_reset_complete.html'), name="teleforma-password-reset-complete"), + + + url(r'^captcha/', include('captcha.urls')), + + # Help + url(r'^help/$', HelpView.as_view(), name="teleforma-help"), + + # Home + url(r'^$', HomeRedirectView.as_view(), + name="teleforma-home"), + + # Flat pages + url(r'^pages/(?P.*)$', home_view.render_flatpage, + name="teleforma-flatpage"), + # Unauthorized + url(r'^unauthorized/$', TemplateView.as_view(template_name="teleforma/unauthorized.html"), + name="teleforma-unauthorized"), + + + # Desk + url(r'^desk/$', HomeRedirectView.as_view(), + name="teleforma-desk"), + url(r'^desk/periods/(?P.*)/courses/$', + CourseListView.as_view(), name="teleforma-desk-period-list"), + url(r'^desk/periods/(?P.*)/courses_pending/$', + CoursePendingListView.as_view(), name="teleforma-desk-period-pending"), + url(r'^desk/periods/(?P.*)/courses/(?P.*)/detail/$', CourseView.as_view(), + name="teleforma-desk-period-course"), + + + url(r'^desk/periods/(?P.*)/medias/transcode/(?P.*)/detail/$', + MediaTranscodedView.as_view(), name="teleforma-media-transcoded"), + url(r'^desk/periods/(?P.*)/medias/transcode/(?P.*)/download/$', + media_transcoded.download, name="teleforma-media-transcoded-download"), + url(r'^desk/periods/(?P.*)/medias/transcode/(?P.*)/stream/$', + media_transcoded.stream, name="teleforma-media-transcoded-stream"), + url(r'^desk/periods/(?P.*)/medias/(?P.*)/detail/$', + MediaView.as_view(), name="teleforma-media-detail"), + url(r'^desk/periods/(?P.*)/medias/(?P.*)/embed/$', + MediaViewEmbed.as_view(), name="teleforma-media-embed"), + url(r'^desk/periods/(?P.*)/medias/(?P.*)/download/$', + media.download, name="teleforma-media-download"), + url(r'^desk/periods/(?P.*)/medias/(?P.*)/stream/$', + media.stream, name="teleforma-media-stream"), + + url(r'^desk/documents/(?P.*)/detail/$', DocumentView.as_view(), + name="teleforma-document-detail"), + url(r'^desk/documents/(?P.*)/download/$', document.download, + name="teleforma-document-download"), + url(r'^desk/documents/(?P.*)/view/$', document.view, + name="teleforma-document-view"), + + url(r'^archives/annals/$', AnnalsView.as_view(), + name="teleforma-annals"), + url(r'^archives/annals/by-iej/(\w+)/$', + AnnalsIEJView.as_view(), name="teleforma-annals-iej"), + url(r'^archives/annals/by-course/(\w+)/$', + AnnalsCourseView.as_view(), name="teleforma-annals-course"), + + url(r'^desk/periods/(?P.*)/conferences/(?P.*)/video/$', + ConferenceView.as_view(), name="teleforma-conference-detail"), + url(r'^desk/periods/(?P.*)/conferences/(?P.*)/audio/$', + ConferenceView.as_view( + template_name="teleforma/course_conference_audio.html"), + name="teleforma-conference-audio"), + url(r'^desk/periods/(?P.*)/conferences/list/$', ConferenceListView.as_view(), + name="teleforma-conferences"), + + # APPOINTMENTS + url(r'^desk/periods/(?P.*)/appointments/(?P.*)/$', Appointments.as_view(), + name="teleforma-appointments"), + url(r'^desk/periods/appointments/cancel$', cancel_appointment, + name="teleforma-appointment-cancel"), + + # Postman + url(r'^messages/write/(?:(?P[^/#]+)/)?$', + WriteView.as_view(), name='postman_write'), + url(r'^messages/', include('postman.urls')), + + + # Users + url(r'^users/training/(?P.*)/iej/(?P.*)/course/(?P.*)/list/$', + UsersView.as_view(), name="teleforma-users"), + + url(r'^users/training/(?P.*)/iej/(?P.*)/course/(?P.*)/export/$', + UsersExportView.as_view(), name="teleforma-users-export"), + + url(r'^users/(?P.*)/login/$', + UserLoginView.as_view(), name="teleforma-user-login"), + + # Ajax update training + url(r'^update-training/(?P.*)/$', + update_training, name="update-training"), + + # News Item + url(r'^desk/periods/(?P.*)/medias/(?P.*)/detail/$', + MediaView.as_view(), name="teleforma-media-detail"), + url(r'^newsitems/create', NewsItemCreate.as_view(), + name='newsitem-create'), + url(r'^newsitems/update/(?P.*)', + NewsItemUpdate.as_view(), name='newsitem-update'), + url(r'^newsitems/delete/(?P.*)', + NewsItemDelete.as_view(), name='newsitem-delete'), + url(r'^newsitems/(?P.*)/list', + NewsItemList.as_view(), name='newsitem-list'), + + # JSON RPC + url(r'json/$', jsonrpc_site.dispatch, + name='jsonrpc_mountpoint'), + url(r'jsonrpc/$', jsonrpc_site.dispatch, + name='jsonrpc_mountpoint'), + + # url(r'^private_files/', include('private_files.urls')), + + # EXAM + url(r'^', include('teleforma.exam.urls')), + + # WEBCLASS + url(r'^', include('teleforma.webclass.urls')), + + # Payment + url(r'^payment/(?P.*)/start/$', PaymentStartView.as_view(), + name="teleforma-payment-start"), + + url(r'^payment/bank_auto/(?P.*)', + bank_auto, name='teleforma-bank-auto'), + url(r'^payment/bank_success/(?P.*)', + bank_success, name='teleforma-bank-success'), + url(r'^payment/bank_cancel/(?P.*)', + bank_cancel, name='teleforma-bank-cancel'), + + url(r'^echec-de-paiement', + bank_fail, name='teleforma-bank-fail'), + + url(r'^accounts/(?P[A-Za-z0-9+@._-]+)/receipt/download/$', + ReceiptPDFViewDownload.as_view(), name="teleforma-receipt-download"), + url(r'^accounts/(?P[A-Za-z0-9+@._-]+)/receipt/view/$', + ReceiptPDFView.as_view(), name="teleforma-receipt-view"), + + ] diff --git a/teleforma/views/__init__.py b/teleforma/views/__init__.py index f194bee7..1c7cba89 100644 --- a/teleforma/views/__init__.py +++ b/teleforma/views/__init__.py @@ -1,7 +1,7 @@ -from core import * -from crfpa import * +from .core import * +from .crfpa import * #from pro import * -from appointment import * -from payment import * -from profile import * -from home import * +from .appointment import * +from .payment import * +from .profile import * +from .home import * diff --git a/teleforma/views/ae.py b/teleforma/views/ae.py index f92c088e..efae5cdd 100644 --- a/teleforma/views/ae.py +++ b/teleforma/views/ae.py @@ -33,7 +33,8 @@ # Authors: Guillaume Pellerin -from teleforma.views.core import * +from ..models.core import Course, CourseType +from .core import format_courses def get_ae_courses(user, date_order=False, num_order=False): @@ -49,7 +50,7 @@ def get_ae_courses(user, date_order=False, num_order=False): if professor: professor = user.professor.get() courses = format_courses(courses, queryset=professor.courses.all(), - types=types) + types=types) elif student: student = user.ae_student.get() @@ -57,17 +58,17 @@ def get_ae_courses(user, date_order=False, num_order=False): for course in s_courses: courses = format_courses(courses, course=course, - types=types) + types=types) magistrals = Course.objects.filter(magistral=True) if magistrals: courses = format_courses(courses, - queryset=magistrals, - types=types) + queryset=magistrals, + types=types) elif user.is_staff or user.is_superuser: courses = format_courses(courses, queryset=Course.objects.all(), - types=types) + types=types) else: courses = None diff --git a/teleforma/views/appointment.py b/teleforma/views/appointment.py index d287c346..b511e2d8 100644 --- a/teleforma/views/appointment.py +++ b/teleforma/views/appointment.py @@ -1,20 +1,19 @@ # -*- coding: utf-8 -*- -from django.views.generic import View -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.contrib import messages from django.core.cache import cache +from django.core.mail import send_mail +from django.db import IntegrityError +from django.http import HttpResponse, HttpResponseRedirect +from django.shortcuts import get_object_or_404, redirect, render +from django.template.loader import render_to_string +from django.urls import reverse +from django.views.generic import View -from teleforma.models.appointment import AppointmentPeriod, Appointment, AppointmentSlot, CACHE_KEY, APPOINTMENT_MODE - -from teleforma.views.core import get_periods +from ..models.appointment import (CACHE_KEY, Appointment, AppointmentPeriod, + AppointmentSlot) +from ..views.core import get_periods class Appointments(View): diff --git a/teleforma/views/core.py b/teleforma/views/core.py index 07d5b8d7..15dfcb27 100644 --- a/teleforma/views/core.py +++ b/teleforma/views/core.py @@ -33,77 +33,53 @@ # # Authors: Guillaume Pellerin -import mimetypes import datetime -import random -import urllib -import urllib2 -import json - -from jsonrpc import jsonrpc_method +import mimetypes +from html import escape +from io import StringIO -from django.utils.decorators import method_decorator -from django.contrib.auth import authenticate, login, get_backends -from django.template import RequestContext, loader, Context -from django import template -from django.http import HttpResponse, HttpResponseRedirect -from django.http import Http404 -from django.shortcuts import render_to_response, redirect, get_object_or_404 -from django.views.generic import * -from django.views.generic.base import * from django.conf import settings -from django.contrib import auth from django.contrib import messages from django.contrib.auth.decorators import login_required, permission_required -from django.core.context_processors import csrf -from django.forms.models import modelformset_factory, inlineformset_factory from django.contrib.auth.models import User -from django.utils.translation import ugettext -from django.utils.translation import ugettext_lazy as _ -from django.contrib.auth.forms import UserChangeForm -from django.core.exceptions import ObjectDoesNotExist -from django.contrib.syndication.views import Feed -from django.core.paginator import Paginator from django.contrib.contenttypes.models import ContentType +from django.http import Http404, HttpResponse, HttpResponseRedirect +from django.http.response import StreamingHttpResponse +from django.shortcuts import redirect, render as django_render +from django.template import Context, RequestContext, loader +from django.urls import reverse +from django.utils.decorators import method_decorator +from django.utils.translation import ugettext_lazy as _ +from django.views.generic import * +from django.views.generic.base import * from django.views.generic.edit import FormView -from django.core.urlresolvers import reverse, reverse_lazy +from jsonrpc import jsonrpc_method 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, WebclassRecord -import pages from teleforma.decorators import access_required -import jqchat.models -from xlwt import Workbook - -from cgi import escape -from cStringIO import StringIO from xhtml2pdf import pisa -try: - from telecaster.models import * - from telecaster.tools import * -except: - pass +from ..forms import * +from ..models import * +from ..models.appointment import AppointmentPeriod +from ..webclass.models import Webclass, WebclassRecord +from .pages import get_page_content +def render(request, template, data=None, mimetype=None): + return django_render(template, data, context_instance=RequestContext(request), + mimetype=mimetype) -def render(request, template, data = None, mimetype = None): - return render_to_response(template, data, context_instance=RequestContext(request), - mimetype=mimetype) def format_courses(courses, course=None, queryset=None, types=None): if queryset: for c in queryset: if c and c.code != 'X': courses.append({'course': c, 'types': types.all(), - 'date': c.date_modified, 'number': c.number}) + 'date': c.date_modified, 'number': c.number}) elif course: if course.code != 'X': courses.append({'course': course, 'types': types.all(), - 'date': course.date_modified, 'number': course.number}) + 'date': course.date_modified, 'number': course.number}) return courses @@ -129,25 +105,26 @@ def stream_from_file(__file): def get_room(content_type=None, id=None, name=None, period=None): - if settings.TELEFORMA_GLOBAL_TWEETER: - name = 'site' - - if settings.TELEFORMA_PERIOD_TWEETER and period: - name = name + '-' + period - - if settings.TELEFORMA_GLOBAL_TWEETER: - rooms = jqchat.models.Room.objects.filter(name=name[:20]) - else: - rooms = jqchat.models.Room.objects.filter(name=name[:20], - content_type=content_type, - object_id=id) - if not rooms: - room = jqchat.models.Room.objects.create(content_type=content_type, - object_id=id, - name=name[:20]) - else: - room = rooms[0] - return room + return None + # if settings.TELEFORMA_GLOBAL_TWEETER: + # name = 'site' + + # if settings.TELEFORMA_PERIOD_TWEETER and period: + # name = name + '-' + period + + # if settings.TELEFORMA_GLOBAL_TWEETER: + # rooms = jqchat.models.Room.objects.filter(name=name[:20]) + # else: + # rooms = jqchat.models.Room.objects.filter(name=name[:20], + # content_type=content_type, + # object_id=id) + # if not rooms: + # room = jqchat.models.Room.objects.create(content_type=content_type, + # object_id=id, + # name=name[:20]) + # else: + # room = rooms[0] + # return room def get_access(obj, courses): @@ -157,8 +134,11 @@ def get_access(obj, courses): access = True return access + access_error = _('Access not allowed.') -contact_message = _('Please login or contact the website administator to get a private access.') +contact_message = _( + 'Please login or contact the website administator to get a private access.') + def get_host(request): host = request.META['HTTP_HOST'] @@ -168,6 +148,7 @@ def get_host(request): host = '127.0.0.1' return host + def get_periods(user): periods = [] @@ -195,6 +176,7 @@ def get_periods(user): return periods + def get_default_period(periods): if not periods: return None @@ -209,11 +191,11 @@ def content_to_pdf(content, dest, encoding='utf-8', **kwargs): Write into *dest* file object the given html *content*. Return True if the operation completed successfully. """ - from xhtml2pdf import pisa src = StringIO(content.encode(encoding)) pdf = pisa.pisaDocument(src, dest, encoding=encoding, **kwargs) return not pdf.err + def content_to_response(content, filename=None): """ Return a pdf response using given *content*. @@ -223,8 +205,9 @@ def content_to_response(content, filename=None): response['Content-Disposition'] = 'attachment; filename=%s' % filename return response + def render_to_pdf(request, template, context, filename=None, encoding='utf-8', - **kwargs): + **kwargs): """ Render a pdf response using given *request*, *template* and *context*. """ @@ -248,10 +231,12 @@ def serve_media(media_path, content_type="", buffering=True, streaming=False): return nginx_media_accel(media_path, content_type=content_type, buffering=buffering, streaming=streaming) else: - response = StreamingHttpResponse(stream_from_file(media_path), content_type=content_type) + response = StreamingHttpResponse( + stream_from_file(media_path), content_type=content_type) filename = os.path.basename(media_path) if not streaming: - response['Content-Disposition'] = 'attachment; ' + 'filename=' + filename + response['Content-Disposition'] = 'attachment; ' + \ + 'filename=' + filename return response @@ -262,7 +247,8 @@ def nginx_media_accel(media_path, content_type="", buffering=True, streaming=Fal url = settings.MEDIA_URL + os.path.relpath(media_path, settings.MEDIA_ROOT) filename = os.path.basename(media_path) if not streaming: - response['Content-Disposition'] = "attachment; filename=%s" % (filename) + response['Content-Disposition'] = "attachment; filename=%s" % ( + filename) response['Content-Type'] = content_type response['X-Accel-Redirect'] = url @@ -307,7 +293,8 @@ class PeriodAccessMixin(View): def render_to_response(self, context): period = context['period'] if not period in get_periods(self.request.user): - messages.warning(self.request, _("You do NOT have access to this resource and then have been redirected to your desk.")) + messages.warning(self.request, _( + "You do NOT have access to this resource and then have been redirected to your desk.")) return redirect('teleforma-home') return super(PeriodAccessMixin, self).render_to_response(context) @@ -321,11 +308,13 @@ class CourseAccessMixin(PeriodAccessMixin): def get_context_data(self, **kwargs): context = super(CourseAccessMixin, self).get_context_data(**kwargs) - context['all_courses'] = get_courses(self.request.user, num_order=True, period=self.period) + context['all_courses'] = get_courses( + self.request.user, num_order=True, period=self.period) # If we are a corrector but not a professor, limit object types role = get_user_role(self.request.user) if role == "corrector": - context['doc_types'] = DocumentType.objects.filter(for_corrector = True) + context['doc_types'] = DocumentType.objects.filter( + for_corrector=True) context['show_media'] = False else: context['doc_types'] = DocumentType.objects.all() @@ -336,20 +325,22 @@ class CourseAccessMixin(PeriodAccessMixin): class CourseListView(CourseAccessMixin, ListView): model = Course - template_name='teleforma/courses.html' + template_name = 'teleforma/courses.html' def get_context_data(self, **kwargs): context = super(CourseListView, self).get_context_data(**kwargs) context['room'] = get_room(name='site', period=context['period'].name) context['list_view'] = True - context['courses'] = sorted(context['all_courses'], key=lambda k: k['date'], reverse=True)[:1] + context['courses'] = sorted( + context['all_courses'], key=lambda k: k['date'], reverse=True)[:1] user = self.request.user is_student = user.student.all().count() # appointments_open = False appointments = [] if is_student: - available_courses = [course['course'] for course in context['all_courses']] - for appointment in AppointmentPeriod.objects.filter(periods=context['period'], course__in=available_courses): + available_courses = [course['course'] + for course in context['all_courses']] + for appointment in AppointmentPeriod.objects.filter(periods=context['period'], course__in=available_courses): if appointment.is_open: found = False for existing in appointments: @@ -359,7 +350,8 @@ class CourseListView(CourseAccessMixin, ListView): appointments.append(appointment) context['appointments'] = appointments # check if user appointment is next - user_appointment = Appointment.objects.filter(student=user, slot__mode='distance', slot__appointment_period__periods=context['period']) + user_appointment = Appointment.objects.filter( + student=user, slot__mode='distance', slot__appointment_period__periods=context['period']) if user_appointment: user_appointment = user_appointment[0] now = datetime.datetime.now() @@ -367,7 +359,7 @@ class CourseListView(CourseAccessMixin, ListView): if user_appointment.real_date - datetime.timedelta(hours=1) < now < user_appointment.real_date + datetime.timedelta(hours=1): context['current_appointement'] = user_appointment - homes = Home.objects.filter(enabled = True).order_by('-modified_at') + homes = Home.objects.filter(enabled=True).order_by('-modified_at') for home in homes: if home.is_for_period(context['period']): context['home_title'] = home.visible_title @@ -379,7 +371,8 @@ class CourseListView(CourseAccessMixin, ListView): student = user.student.all()[0] slots = [] to_subscribe = [] - student_courses = [course['course'] for course in get_courses(user)] + student_courses = [course['course'] + for course in get_courses(user)] for webclass in Webclass.published.filter(period=self.period, iej=student.iej, course__in=student_courses): # if webclass.course not in student_courses: # continue @@ -400,9 +393,10 @@ class CourseListView(CourseAccessMixin, ListView): @jsonrpc_method('teleforma.get_course_list') def get_course_list(request, organization_name, department_name): - from teleforma.models import Organization, Department + from teleforma.models import Department, Organization organization = Organization.objects.get(name=organization_name) - department = Department.objects.get(organization=organization, name=department_name) + department = Department.objects.get( + organization=organization, name=department_name) return [course.to_dict() for course in Course.objects.filter(department=department)] @jsonrpc_method('teleforma.get_course_type_list') @@ -411,11 +405,13 @@ class CourseListView(CourseAccessMixin, ListView): def pull(request, organization_name, department_name): organization = Organization.objects.get(name=organization_name) - department = Department.objects.get(name=department_name, organization=organization) + department = Department.objects.get( + name=department_name, organization=organization) url = 'http://' + department.domain + '/json/' s = ServiceProxy(url) - remote_list = s.teleforma.get_course_list(organization_name, department.name) + remote_list = s.teleforma.get_course_list( + organization_name, department.name) for course_dict in remote_list['result']: course = Course.objects.filter(code=course_dict['code']) if not course: @@ -427,7 +423,8 @@ class CourseListView(CourseAccessMixin, ListView): remote_list = s.teleforma.get_course_type_list() if remote_list['result']: for course_type_dict in remote_list['result']: - course_type = CourseType.objects.filter(name=course_type_dict['name']) + course_type = CourseType.objects.filter( + name=course_type_dict['name']) if not course_type: course_type = CourseType() else: @@ -437,12 +434,12 @@ class CourseListView(CourseAccessMixin, ListView): @jsonrpc_method('teleforma.get_dep_courses') def get_dep_courses(request, id): department = Department.objects.get(id=id) - return [{'id': str(c.id), 'name': unicode(c)} for c in department.course.all()] + return [{'id': str(c.id), 'name': str(c)} for c in department.course.all()] @jsonrpc_method('teleforma.get_dep_periods') def get_dep_periods(request, id): department = Department.objects.get(id=id) - return [{'id': str(c.id), 'name': unicode(c)} for c in department.period.all()] + return [{'id': str(c.id), 'name': str(c)} for c in department.period.all()] class CourseView(CourseAccessMixin, DetailView): @@ -455,10 +452,12 @@ class CourseView(CourseAccessMixin, DetailView): courses = [] for c in context['all_courses']: if c['course'] == course: - courses = format_courses(courses, course=course, types=c['types']) + courses = format_courses( + courses, course=course, types=c['types']) context['courses'] = courses # context['notes'] = course.notes.all().filter(author=self.request.user) - content_type = ContentType.objects.get(app_label="teleforma", model="course") + content_type = ContentType.objects.get( + app_label="teleforma", model="course") context['room'] = get_room(name=course.code, period=context['period'].name, content_type=content_type, id=course.id) @@ -472,7 +471,8 @@ class CourseView(CourseAccessMixin, DetailView): if student: try: - webclass = Webclass.published.filter(period=self.period, course=course, iej=student.iej)[0] + webclass = Webclass.published.filter( + period=self.period, course=course, iej=student.iej)[0] except IndexError: pass if webclass: @@ -481,8 +481,9 @@ class CourseView(CourseAccessMixin, DetailView): context['webclass_slot'] = webclass_slot try: - context['webclass_records'] = WebclassRecord.get_records(context['period'], course) - except Exception, e: + context['webclass_records'] = WebclassRecord.get_records( + context['period'], course) + except Exception as e: print(e) context['webclass_error'] = True return context @@ -497,27 +498,31 @@ class CourseView(CourseAccessMixin, DetailView): media_list = [] for media in course.media.all(): if media.is_published and media.file and media.conference and 'video' in media.mime_type: - urls = [ {'url': settings.MEDIA_URL + unicode(media.file), 'mime_type': media.mime_type} ] + urls = [{'url': settings.MEDIA_URL + + str(media.file), 'mime_type': media.mime_type}] for transcoded in media.transcoded.all(): - urls.append({'url':settings.MEDIA_URL + unicode(transcoded.file), 'mime_type': transcoded.mime_type}) - media_list.append({'session': media.conference.session, 'urls': urls, 'poster': media.poster_url()}) + urls.append( + {'url': settings.MEDIA_URL + str(transcoded.file), 'mime_type': transcoded.mime_type}) + media_list.append( + {'session': media.conference.session, 'urls': urls, 'poster': media.poster_url()}) return media_list class CoursePendingListView(CourseListView): - template_name='teleforma/courses_pending.html' + template_name = 'teleforma/courses_pending.html' def get_context_data(self, **kwargs): context = super(CoursePendingListView, self).get_context_data(**kwargs) - context['courses'] = sorted(context['all_courses'], key=lambda k: k['date'], reverse=True) + context['courses'] = sorted( + context['all_courses'], key=lambda k: k['date'], reverse=True) return context class MediaView(CourseAccessMixin, DetailView): model = Media - template_name='teleforma/course_media.html' + template_name = 'teleforma/course_media.html' def get_context_data(self, **kwargs): context = super(MediaView, self).get_context_data(**kwargs) @@ -528,13 +533,14 @@ class MediaView(CourseAccessMixin, DetailView): context['course'] = media.course context['type'] = media.course_type # context['notes'] = media.notes.all().filter(author=self.request.user) - content_type = ContentType.objects.get(app_label="teleforma", model="course") + content_type = ContentType.objects.get( + app_label="teleforma", model="course") room_name = media.course.code if media.conference.web_class_group: room_name += '_' + media.conference.public_id - context['room'] = get_room(name=room_name,period=context['period'].name, + context['room'] = get_room(name=room_name, period=context['period'].name, content_type=content_type, id=media.course.id) @@ -573,10 +579,11 @@ class MediaView(CourseAccessMixin, DetailView): def download(self, request, period_id, pk): return self.stream(request, period_id, pk, streaming=False) + class MediaTranscodedView(CourseAccessMixin, DetailView): model = MediaTranscoded - template_name='teleforma/course_media_transcoded.html' + template_name = 'teleforma/course_media_transcoded.html' def get_context_data(self, **kwargs): context = super(MediaTranscodedView, self).get_context_data(**kwargs) @@ -590,13 +597,14 @@ class MediaTranscodedView(CourseAccessMixin, DetailView): context['course'] = media.course context['type'] = media.course_type # context['notes'] = media.notes.all().filter(author=self.request.user) - content_type = ContentType.objects.get(app_label="teleforma", model="course") + content_type = ContentType.objects.get( + app_label="teleforma", model="course") room_name = media.course.code if media.conference.web_class_group: room_name += '_' + media.conference.public_id - context['room'] = get_room(name=room_name,period=context['period'].name, + context['room'] = get_room(name=room_name, period=context['period'].name, content_type=content_type, id=media.course.id) @@ -627,7 +635,7 @@ class MediaTranscodedView(CourseAccessMixin, DetailView): class MediaPendingView(ListView): model = Media - template_name='teleforma/media_pending.html' + template_name = 'teleforma/media_pending.html' def get_queryset(self): return Media.objects.filter(is_published=False) @@ -645,13 +653,13 @@ class MediaPendingView(ListView): class MediaViewEmbed(DetailView): model = Media - template_name='teleforma/course_media_video_embed.html' + template_name = 'teleforma/course_media_video_embed.html' class DocumentView(CourseAccessMixin, DetailView): model = Document - template_name='teleforma/course_document.html' + template_name = 'teleforma/course_document.html' def get_context_data(self, **kwargs): context = super(DocumentView, self).get_context_data(**kwargs) @@ -678,9 +686,9 @@ class DocumentView(CourseAccessMixin, DetailView): #mimetype = mimetypes.guess_type(document.file.path)[0] #extension = mimetypes.guess_extension(mimetype) #response = HttpResponse(fsock, mimetype=mimetype) - #response['Content-Disposition'] = "attachment; filename=%s%s" % \ + # response['Content-Disposition'] = "attachment; filename=%s%s" % \ # (document.title.encode('utf8'), extension) - #return response + # return response else: return redirect('teleforma-home') @@ -693,7 +701,7 @@ class DocumentView(CourseAccessMixin, DetailView): #mimetype = mimetypes.guess_type(document.file.path)[0] #extension = mimetypes.guess_extension(mimetype) #response = HttpResponse(fsock, mimetype=mimetype) - #return response + # return response else: return redirect('teleforma-home') @@ -701,7 +709,7 @@ class DocumentView(CourseAccessMixin, DetailView): class ConferenceView(CourseAccessMixin, DetailView): model = Conference - template_name='teleforma/course_conference.html' + template_name = 'teleforma/course_conference.html' def get_context_data(self, **kwargs): context = super(ConferenceView, self).get_context_data(**kwargs) @@ -709,7 +717,8 @@ class ConferenceView(CourseAccessMixin, DetailView): context['course'] = conference.course context['type'] = conference.course_type # context['notes'] = conference.notes.all().filter(author=self.request.user) - content_type = ContentType.objects.get(app_label="teleforma", model="course") + content_type = ContentType.objects.get( + app_label="teleforma", model="course") room_name = conference.course.code if conference.web_class_group: @@ -765,14 +774,16 @@ class ConferenceListView(View): s = ServiceProxy(url) remote_list = s.teleforma.get_conference_list() for conf_dict in remote_list['result']: - conference = Conference.objects.filter(public_id=conf_dict['id']) + conference = Conference.objects.filter( + public_id=conf_dict['id']) if not conference: conference = Conference() conference.from_json_dict(conf_dict) def push(request, organization_name, department_name): - organization = organization.objects.get(name=organization_name) - department = Department.objects.get(name=department_name, organization=organization) + organization = Organization.objects.get(name=organization_name) + department = Department.objects.get( + name=department_name, organization=organization) url = 'http://' + department.domain + '/json/' s = ServiceProxy(url) remote_list = s.teleforma.get_conference_list()['result'] @@ -783,138 +794,145 @@ class ConferenceListView(View): def live_message(conference): - from jqchat.models import Message - user, c = User.objects.get_or_create(username='bot') - content_type = ContentType.objects.get(app_label="teleforma", model="course") - room = get_room(name=conference.course.code, period=conference.period.name, - content_type=content_type, - id=conference.course.id) - text = _("A new live conference has started : ") - text += 'http://' + Site.objects.all()[0].domain + reverse('teleforma-conference-detail', - kwargs={'period_id': conference.period.id, 'pk': conference.id}) - message = Message.objects.create_message(user, room, text) - - -class ConferenceRecordView(FormView): - "Conference record form : TeleCaster module required" - - model = Conference - form_class = ConferenceForm - template_name='teleforma/course_conference_record.html' - hidden_fields = ['started', 'date_begin', 'date_end', 'public_id', 'readers'] - - def get_context_data(self, **kwargs): - context = super(ConferenceRecordView, self).get_context_data(**kwargs) - context['mime_type'] = 'video/webm' - status = Status() - status.update() - context['hidden_fields'] = self.hidden_fields - context['room'] = get_room(name='monitor') - return context - - def get_success_url(self): - return reverse('teleforma-conference-detail', kwargs={'period_id': self.conference.period.id, - 'pk':self.conference.id}) - - def form_valid(self, form): - form.save() - uuid = get_random_hash() - conference = form.instance - conference.date_begin = datetime.datetime.now() - conference.public_id = uuid - conference.save() - self.conference = conference - status = Status() - status.get_hosts() - - stations = settings.TELECASTER_CONF - for station in stations: - type = station['type'] - conf = station['conf'] - port = station['port'] - server_type = station['server_type'] - server, c = StreamingServer.objects.get_or_create(host=status.ip, port=port, type=server_type) - station = Station(conference=conference, public_id=uuid) - station.setup(conf) - try: - station.start() - except: - continue - station.save() - stream = LiveStream(conference=conference, server=server, - stream_type=type, streaming=True) - stream.save() - if server_type == 'stream-m': - try: - self.snapshot('http://localhost:8080/snapshot/monitor', station.output_dir) - except: - pass - - try: - live_message(self.conference) - except: - pass - - try: - self.push() - except: - pass - - return super(ConferenceRecordView, self).form_valid(form) - - def snapshot(self, url, dir): - width = 160 - height = 90 - img = urllib.urlopen(url) - path = dir + os.sep + 'preview.webp' - f = open(path, 'w') - f.write(img.read()) - f.close() - command = 'dwebp ' + path + ' -o ' + dir + os.sep + 'preview.png &' - os.system(command) - - @method_decorator(access_required) - def dispatch(self, *args, **kwargs): - return super(ConferenceRecordView, self).dispatch(*args, **kwargs) - - @jsonrpc_method('teleforma.create_conference') - def create(request, conf_dict): - if isinstance(conf_dict, dict): - conferences = Conference.objects.filter(public_id=conf_dict['id']) - if not conferences: - conference = Conference() - conference.from_json_dict(conf_dict) - conference.save() - - if conference.streaming: - for stream in conf_dict['streams']: - host = getattr(settings, "TELECASTER_LIVE_STREAMING_SERVER", stream['host']) - server_type = stream['server_type'] - stream_type = stream['stream_type'] - if server_type == 'icecast': - port = getattr(settings, "TELECASTER_LIVE_ICECAST_STREAMING_PORT", stream['port']) - elif server_type == 'stream-m': - port = getattr(settings, "TELECASTER_LIVE_STREAM_M_STREAMING_PORT", stream['port']) - #site = Site.objects.all()[0] - server, c = StreamingServer.objects.get_or_create(host=host, - port=port, - type=server_type) - stream = LiveStream(conference=conference, server=server, - stream_type=stream_type, streaming=True) - stream.save() - - if not conference.web_class_group: - try: - live_message(conference) - except: - pass - else: - raise 'Error : input must be a conference dictionnary' - - def push(self): - url = 'http://' + self.conference.department.domain + '/json/' - s = ServiceProxy(url) - s.teleforma.create_conference(self.conference.to_json_dict()) + from jqchat.models import Message + user, c = User.objects.get_or_create(username='bot') + content_type = ContentType.objects.get( + app_label="teleforma", model="course") + room = get_room(name=conference.course.code, period=conference.period.name, + content_type=content_type, + id=conference.course.id) + text = _("A new live conference has started : ") + text += 'http://' + Site.objects.all()[0].domain + reverse('teleforma-conference-detail', + kwargs={'period_id': conference.period.id, 'pk': conference.id}) + message = Message.objects.create_message(user, room, text) + + +# class ConferenceRecordView(FormView): +# "Conference record form : TeleCaster module required" + +# model = Conference +# form_class = ConferenceForm +# template_name = 'teleforma/course_conference_record.html' +# hidden_fields = ['started', 'date_begin', +# 'date_end', 'public_id', 'readers'] + +# def get_context_data(self, **kwargs): +# context = super(ConferenceRecordView, self).get_context_data(**kwargs) +# context['mime_type'] = 'video/webm' +# status = Status() +# status.update() +# context['hidden_fields'] = self.hidden_fields +# context['room'] = get_room(name='monitor') +# return context + +# def get_success_url(self): +# return reverse('teleforma-conference-detail', kwargs={'period_id': self.conference.period.id, +# 'pk': self.conference.id}) + +# def form_valid(self, form): +# form.save() +# uuid = get_random_hash() +# conference = form.instance +# conference.date_begin = datetime.datetime.now() +# conference.public_id = uuid +# conference.save() +# self.conference = conference +# status = Status() +# status.get_hosts() + +# stations = settings.TELECASTER_CONF +# for station in stations: +# type = station['type'] +# conf = station['conf'] +# port = station['port'] +# server_type = station['server_type'] +# server, c = StreamingServer.objects.get_or_create( +# host=status.ip, port=port, type=server_type) +# station = Station(conference=conference, public_id=uuid) +# station.setup(conf) +# try: +# station.start() +# except: +# continue +# station.save() +# stream = LiveStream(conference=conference, server=server, +# stream_type=type, streaming=True) +# stream.save() +# if server_type == 'stream-m': +# try: +# self.snapshot( +# 'http://localhost:8080/snapshot/monitor', station.output_dir) +# except: +# pass + +# try: +# live_message(self.conference) +# except: +# pass + +# try: +# self.push() +# except: +# pass + +# return super(ConferenceRecordView, self).form_valid(form) + +# def snapshot(self, url, dir): +# width = 160 +# height = 90 +# img = urllib.urlopen(url) +# path = dir + os.sep + 'preview.webp' +# f = open(path, 'w') +# f.write(img.read()) +# f.close() +# command = 'dwebp ' + path + ' -o ' + dir + os.sep + 'preview.png &' +# os.system(command) + +# @method_decorator(access_required) +# def dispatch(self, *args, **kwargs): +# return super(ConferenceRecordView, self).dispatch(*args, **kwargs) + +# @jsonrpc_method('teleforma.create_conference') +# def create(request, conf_dict): +# if isinstance(conf_dict, dict): +# conferences = Conference.objects.filter(public_id=conf_dict['id']) +# if not conferences: +# conference = Conference() +# conference.from_json_dict(conf_dict) +# conference.save() + +# if conference.streaming: +# for stream in conf_dict['streams']: +# host = getattr( +# settings, "TELECASTER_LIVE_STREAMING_SERVER", stream['host']) +# server_type = stream['server_type'] +# stream_type = stream['stream_type'] +# if server_type == 'icecast': +# port = getattr( +# settings, "TELECASTER_LIVE_ICECAST_STREAMING_PORT", stream['port']) +# elif server_type == 'stream-m': +# port = getattr( +# settings, "TELECASTER_LIVE_STREAM_M_STREAMING_PORT", stream['port']) +# #site = Site.objects.all()[0] +# server, c = StreamingServer.objects.get_or_create(host=host, +# port=port, +# type=server_type) +# stream = LiveStream(conference=conference, server=server, +# stream_type=stream_type, streaming=True) +# stream.save() + +# if not conference.web_class_group: +# try: +# live_message(conference) +# except: +# pass +# else: +# raise 'Error : input must be a conference dictionnary' + +# def push(self): +# url = 'http://' + self.conference.department.domain + '/json/' +# s = ServiceProxy(url) +# s.teleforma.create_conference(self.conference.to_json_dict()) class ProfessorListView(View): @@ -933,7 +951,8 @@ class ProfessorListView(View): remote_list = s.teleforma.get_professor_list() for professor_dict in remote_list['result']: - user, c = User.objects.get_or_create(username=professor_dict['username']) + user, c = User.objects.get_or_create( + username=professor_dict['username']) user.first_name = professor_dict['first_name'] user.last_name = professor_dict['last_name'] user.email = professor_dict['email'] @@ -964,17 +983,18 @@ class WebClassGroupView(View): remote_list = s.teleforma.get_class_group_list() for class_group_dict in remote_list['result']: - class_group, c = WebClassGroup.objects.get_or_create(name=class_group_dict['name']) + class_group, c = WebClassGroup.objects.get_or_create( + name=class_group_dict['name']) class HelpView(TemplateView): - template_name='teleforma/help.html' + template_name = 'teleforma/help.html' def get_context_data(self, **kwargs): context = super(HelpView, self).get_context_data(**kwargs) - context['page_content'] = pages.get_page_content(self.request, 'help', - ignore_slash_issue=True) + context['page_content'] = get_page_content(self.request, 'help', + ignore_slash_issue=True) return context def dispatch(self, *args, **kwargs): diff --git a/teleforma/views/crfpa.py b/teleforma/views/crfpa.py index d22f1257..43b527b1 100644 --- a/teleforma/views/crfpa.py +++ b/teleforma/views/crfpa.py @@ -31,34 +31,49 @@ # knowledge of the CeCILL license and that you accept its terms. # # Authors: Guillaume Pellerin -from django.core.exceptions import ValidationError, PermissionDenied -from teleforma.models.crfpa import Parameters -from teleforma.models.core import Period -from teleforma.views.core import * -from teleforma.forms import WriteForm -from teleforma.views.profile import ProfileView -from teleforma.decorators import access_required -from registration.views import * -from extra_views import CreateWithInlinesView, UpdateWithInlinesView, InlineFormSet -from postman.views import WriteView as PostmanWriteView -from postman.forms import AnonymousWriteForm -from django.utils.translation import ugettext_lazy as _ -from django.views.decorators.csrf import csrf_exempt -from django.db.models import Q -from django.db.models import Max -from django.http import HttpResponseForbidden -from django.forms.formsets import all_valid -from django.core.exceptions import ValidationError -from django.contrib.sites.models import Site -import xlrd +import datetime +import xlrd +from django.contrib.auth import get_backends, login +from django.contrib.auth.decorators import login_required, permission_required +from django.contrib.auth.models import User +from django.contrib.sites.models import Site +from django.core.exceptions import PermissionDenied, ValidationError +from django.core.paginator import InvalidPage +from django.db.models import Max, Q +from django.forms.formsets import all_valid +from django.http import HttpResponseForbidden +from django.http.response import HttpResponse, HttpResponseRedirect +from django.shortcuts import get_object_or_404, redirect, render +from django.template.defaultfilters import slugify +from django.urls.base import reverse, reverse_lazy +from django.utils.decorators import method_decorator +from django.utils.translation import ugettext_lazy as _ +from django.views.decorators.csrf import csrf_exempt +from django.views.generic.base import TemplateView, View +from django.views.generic.edit import CreateView, DeleteView, UpdateView +from django.views.generic.list import ListView +from postman.forms import AnonymousWriteForm +from postman.views import WriteView as PostmanWriteView +from xlwt import Workbook + +from ..decorators import access_required +from ..forms import (CorrectorForm, NewsItemForm, UserForm, WriteForm, + get_unique_username) +from ..models.core import Course, CourseType, Document, NamePaginator, Period +from ..models.crfpa import (IEJ, Discount, NewsItem, Parameters, Payback, + Payment, Profile, Student, Training) +from ..views.core import (PDFTemplateResponseMixin, format_courses, + get_courses, get_periods, months_choices, + payment_choices) +from ..views.profile import ProfileView ORAL_OPTION_PRICE = 250 def get_course_code(obj): if obj: - return unicode(obj.code) + return str(obj.code) else: return '' @@ -269,7 +284,7 @@ class UserXLSBook(object): row.write(1, user.first_name) row.write(2, student.portrait and student.portrait.url or '') row.write(8, user.email) - row.write(3, unicode(student.iej)) + row.write(3, str(student.iej)) codes = [] for training in student.trainings.values('code'): @@ -277,7 +292,7 @@ class UserXLSBook(object): codes.append('I - ' + training['code']) else: codes.append(training['code']) - row.write(4, unicode(' '.join(codes))) + row.write(4, str(' '.join(codes))) row.write(5, self.get_course_code(student.procedure_id)) row.write(6, self.get_course_code(student.written_speciality_id)) @@ -791,7 +806,7 @@ class RegistrationPDFViewDownload(RegistrationPDFView): user = User.objects.get(username=self.kwargs['username']) # user = self.get_object() student = user.student.all()[0] - prefix = unicode(_('Registration')) + prefix = str(_('Registration')) filename = '_'.join([prefix, student.user.first_name, student.user.last_name]) filename += '.pdf' return filename.encode('utf-8') @@ -954,7 +969,7 @@ class RegistrationPDFViewDownload(RegistrationPDFView): user = User.objects.get(username=self.kwargs['username']) # user = self.get_object() student = user.student.all()[0] - prefix = unicode(_('Registration')) + prefix = str(_('Registration')) filename = '_'.join([prefix, student.user.first_name, student.user.last_name]) filename += '.pdf' return filename.encode('utf-8') @@ -968,7 +983,7 @@ class CorrectorRegistrationPDFViewDownload(RegistrationPDFView): user = User.objects.get(username=self.kwargs['username']) # user = self.get_object() corrector = user.corrector.all()[0] - prefix = unicode(_('Registration')) + prefix = str(_('Registration')) filename = '_'.join([prefix, corrector.user.first_name, corrector.user.last_name]) filename += '.pdf' return filename.encode('utf-8') diff --git a/teleforma/views/home.py b/teleforma/views/home.py index b549b43c..64df4534 100644 --- a/teleforma/views/home.py +++ b/teleforma/views/home.py @@ -34,26 +34,27 @@ # Authors: Olivier Guilyardi # Guillaume Pellerin -import pages -from django.shortcuts import render, redirect from django.contrib import auth from django.http import HttpResponse +from django.shortcuts import redirect, render + +from .pages import MalformedPagePath, PageAttachment, get_page_content + class HomeView(object): """Provide general web UI methods""" def render_flatpage(self, request, path): try: - content = pages.get_page_content(request, path) - except pages.MalformedPagePath: + content = get_page_content(request, path) + except MalformedPagePath: return redirect(request.path + '/') - if isinstance(content, pages.PageAttachment): + if isinstance(content, PageAttachment): return HttpResponse(content, content.mimetype()) else: - return render(request, 'teleforma/flatpage.html', {'page_content': content }) + return render(request, 'teleforma/flatpage.html', {'page_content': content}) def logout(self, request): auth.logout(request) return redirect('teleforma-home') - diff --git a/teleforma/views/pages.py b/teleforma/views/pages.py index f805f5bd..86333187 100644 --- a/teleforma/views/pages.py +++ b/teleforma/views/pages.py @@ -6,6 +6,7 @@ import teleforma PAGES_ROOT = os.path.join(os.path.dirname(teleforma.__file__), 'pages') + class PageTextContent(object): def __init__(self, filename, path): self.filename = filename @@ -17,16 +18,17 @@ class PageTextContent(object): yield line.rstrip('\r\n') file.close() - def __unicode__(self): + def __str__(self): file = open(self.filename, 'r') data = file.read() file.close() return data + class PageAttachment(object): def __init__(self, filename, path): self.filename = filename - self.path = path + self.path = path def mimetype(self): type, encoding = mimetypes.guess_type(self.filename) @@ -43,12 +45,15 @@ class PageAttachment(object): file.close() + def language_code(request=None): - code = (request and getattr(request, 'LANGUAGE_CODE', None)) or settings.LANGUAGE_CODE + code = (request and getattr(request, 'LANGUAGE_CODE', None) + ) or settings.LANGUAGE_CODE cut = re.split('[_-]', code) code = cut[0] return code.lower() + def project_dir(): import settings as settings_mod if '__init__.py' in settings_mod.__file__: @@ -61,6 +66,7 @@ def project_dir(): return project_directory + def resolve_page_file(root, relative_path, ignore_slash_issue=False): root = os.path.realpath(root) filename = None @@ -75,7 +81,7 @@ def resolve_page_file(root, relative_path, ignore_slash_issue=False): filename = rst break elif os.path.isfile(current): - filename = current + filename = current is_attachment = True elif not os.path.isdir(current): break @@ -101,18 +107,20 @@ def resolve_page_file(root, relative_path, ignore_slash_issue=False): return None + def get_page_content(request, relative_path, ignore_slash_issue=False): lang = language_code(request) userroot = os.path.join(project_dir(), 'telemeta-pages') rootlist = [os.path.join(userroot, lang), os.path.join(userroot, 'default'), os.path.join(PAGES_ROOT, lang), os.path.join(PAGES_ROOT, 'default')] for root in rootlist: - content = resolve_page_file(root, relative_path, ignore_slash_issue=ignore_slash_issue) + content = resolve_page_file( + root, relative_path, ignore_slash_issue=ignore_slash_issue) if content: return content return None + class MalformedPagePath(Exception): pass - diff --git a/teleforma/views/payment.py b/teleforma/views/payment.py index 41e38707..21364131 100644 --- a/teleforma/views/payment.py +++ b/teleforma/views/payment.py @@ -1,46 +1,47 @@ # -*- coding: utf-8 -*- -from teleforma.models.crfpa import Payment, Student -from teleforma.models.core import Period -from teleforma.views.core import * -from django.utils.translation import ugettext_lazy as _ -from django.views.decorators.csrf import csrf_exempt -from django.db.models import Q -from django.http import HttpResponseForbidden -from django.core.exceptions import ValidationError, PermissionDenied +import datetime +import logging +import pprint +import subprocess + from django.conf import settings -from django.contrib.sites.models import get_current_site +from django.contrib.sites.shortcuts import get_current_site +from django.core.exceptions import PermissionDenied from django.core.mail import send_mail from django.template.loader import render_to_string +from django.utils.translation import ugettext_lazy as _ +from django.views.decorators.csrf import csrf_exempt -import pprint -import datetime -import commands +from ..models.crfpa import Payment +from ..views.core import * -import logging log = logging.getLogger('payment') + def call_scherlocks(what, data, merchant_id): """ Perform a Scherlock's call, with parameters in data what is either 'request' or 'response', the program to call """ log.info('call_scherlocks %r %r' % (what, data)) - requestbin = os.path.join(settings.PAYMENT_SHERLOCKS_PATH, 'bin/static', what) + requestbin = os.path.join( + settings.PAYMENT_SHERLOCKS_PATH, 'bin/static', what) params = dict(data) params['pathfile'] = os.path.join(settings.PAYMENT_SHERLOCKS_PATH, 'param/pathfile.' + merchant_id) - params = ' '.join([ '%s=%s' % (k,v) for k,v in params.items() ]) + params = ' '.join(['%s=%s' % (k, v) for k, v in params.items()]) cmdline = requestbin + ' ' + params - status, out = commands.getstatusoutput(cmdline) + status, out = subprocess.getstatusoutput(cmdline) if status: - raise OSError, "error calling %s" % cmdline + raise OSError("error calling %s" % cmdline) res = out.split('!')[1:-1] if int(res[0]): - raise ValueError, "Scherlock's returned %s" % res[1] + raise ValueError("Scherlock's returned %s" % res[1]) return res[2:] + def check_payment_info(data): """ Check that the payment info are valid @@ -50,7 +51,8 @@ def check_payment_info(data): log.info('check_payment_info %s %s' % (response_code, cvv_response_code)) - return response_code == '00' + return response_code == '00' + def process_payment(request, payment): """ @@ -66,15 +68,15 @@ def process_payment(request, payment): current_site = get_current_site(request) root = 'https://%s' % (current_site.domain) - kwargs = { 'merchant_id': merchant_id } + kwargs = {'merchant_id': merchant_id} params['normal_return_url'] = root + reverse('teleforma-bank-success', - kwargs = kwargs) + kwargs=kwargs) params['cancel_return_url'] = root + reverse('teleforma-bank-cancel', - kwargs = kwargs) + kwargs=kwargs) params['automatic_response_url'] = root + reverse('teleforma-bank-auto', - kwargs = kwargs) + kwargs=kwargs) pprint.pprint(params) - res = call_scherlocks('request', params, merchant_id = merchant_id) + res = call_scherlocks('request', params, merchant_id=merchant_id) return res[0] @@ -98,15 +100,16 @@ class PaymentStartView(DetailView): def dispatch(self, *args, **kwargs): return super(PaymentStartView, self).dispatch(*args, **kwargs) + @csrf_exempt def bank_auto(request, merchant_id): """ Bank automatic callback - """ - res = call_scherlocks('response', { 'message': request.POST['DATA'] }, - merchant_id = merchant_id) + """ + res = call_scherlocks('response', {'message': request.POST['DATA']}, + merchant_id=merchant_id) order_id = res[24] - payment = Payment.objects.get(pk = order_id) + payment = Payment.objects.get(pk=order_id) if check_payment_info(res) and payment.type == 'online' and not payment.online_paid: payment.online_paid = True payment.date_paid = datetime.datetime.now() @@ -120,11 +123,12 @@ def bank_auto(request, merchant_id): 'mto': payment.student.user.email, 'student': payment.student } - message = render_to_string('teleforma/messages/email_account_activated.txt', data) - send_mail("Inscription à la formation Pré-Barreau", message, data['mfrom'], [ data['mto'] ], - fail_silently=False) + message = render_to_string( + 'teleforma/messages/email_account_activated.txt', data) + send_mail("Inscription à la formation Pré-Barreau", message, data['mfrom'], [data['mto']], + fail_silently=False) student.save() - + payment.save() log.info('bank_auto validating order_id %s' % (order_id)) tmpl_name = 'payment_ok' @@ -135,38 +139,40 @@ def bank_auto(request, merchant_id): res = 'OK - Cancelled' user = payment.student.user - data = { 'mfrom': settings.DEFAULT_FROM_EMAIL, - 'mto': user.email, - 'student': user, - 'amount': payment.value, } - + data = {'mfrom': settings.DEFAULT_FROM_EMAIL, + 'mto': user.email, + 'student': user, + 'amount': payment.value, } + subject_template = 'payment/email_%s_subject.txt' % tmpl_name message_template = 'payment/email_%s.txt' % tmpl_name 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'] ], + send_mail(subject, message, data['mfrom'], [data['mto']], fail_silently=True) - + return HttpResponse(res) + @csrf_exempt def bank_success(request, merchant_id): """ Bank success callback """ log.info("bank_success %r" % request.POST) - res = call_scherlocks('response', { 'message': request.POST['DATA'] }, - merchant_id = merchant_id) + res = call_scherlocks('response', {'message': request.POST['DATA']}, + merchant_id=merchant_id) if check_payment_info(res): - order_id = res[24]; - payment = Payment.objects.get(pk = order_id) + order_id = res[24] + payment = Payment.objects.get(pk=order_id) if payment.type == 'online' and payment.online_paid and (payment.student.user_id == request.user.pk or request.user.is_superuser): - return render_to_response('payment/payment_validate.html', + return render_to_response('payment/payment_validate.html', {'payment': payment, }, context_instance=RequestContext(request)) return HttpResponseRedirect('/echec-de-paiement') + @csrf_exempt def bank_cancel(request, merchant_id): """ @@ -174,6 +180,7 @@ def bank_cancel(request, merchant_id): """ return HttpResponseRedirect('/echec-de-paiement') + def bank_fail(request): """ Display message when a payment failed diff --git a/teleforma/views/pro.py b/teleforma/views/pro.py index 4f738a27..2eb0012b 100644 --- a/teleforma/views/pro.py +++ b/teleforma/views/pro.py @@ -33,14 +33,18 @@ # Authors: Guillaume Pellerin +from django.utils.decorators import method_decorator +from django.views.generic.detail import DetailView +from django.views.generic.edit import FormView + +from ..decorators import access_required +from ..models.pro import Answer, Question, Seminar -from teleforma.views.core import * -from teleforma.decorators import access_required class SeminarView(DetailView): model = Seminar - template_name='teleforma/seminar_detail.html' + template_name = 'teleforma/seminar_detail.html' @method_decorator(access_required) def dispatch(self, *args, **kwargs): @@ -54,32 +58,25 @@ class SeminarView(DetailView): context['progress'] = self.progress(user, seminar) return context - def progress(user, seminar): + def progress(user, seminar): """return the user progress of a seminar in percent""" progress = 0 total = 0 - - docs = [seminar.doc_1, seminar.doc_2, seminar.media, seminar.doc_correct] + + docs = [seminar.doc_1, seminar.doc_2, + seminar.media, seminar.doc_correct] for doc in docs: total += doc.weight if user in doc.readers: progress += doc.weight - + questions = Question.objects.filter(seminar=seminar, status=3) for question in questions: total += question.weight - answer = Answer.objects.filter(question=question, validated=True, user=user) + answer = Answer.objects.filter( + question=question, validated=True, user=user) if answer: progress += question.weight return int(progress*100/total) - - -class AnswerView(FormView): - - model = Answer - form_class = AnswerForm - template_name='teleforma/answer.html' - - diff --git a/teleforma/views/profile.py b/teleforma/views/profile.py index 0a077ff1..eb05d9a0 100644 --- a/teleforma/views/profile.py +++ b/teleforma/views/profile.py @@ -34,12 +34,17 @@ # # Authors: Olivier Guilyardi # # Guillaume Pellerin -from django.contrib.auth.models import User +from django.contrib import messages +from django.contrib.auth.decorators import login_required from django.contrib.auth.forms import UserChangeForm +from django.contrib.auth.models import User +from django.shortcuts import redirect, render from django.utils.decorators import method_decorator -from django.contrib.auth.decorators import login_required -from teleforma.models.crfpa import Profile as UserProfile -from teleforma.forms import UserProfileForm +from django.utils.translation import ugettext + +from ..forms import UserProfileForm +from ..models.crfpa import Profile as UserProfile + class ProfileView(object): """Provide Collections web UI methods""" @@ -52,24 +57,26 @@ class ProfileView(object): except: profile = None - return render(request, template, {'profile' : profile, 'usr': user}) + return render(request, template, {'profile': profile, 'usr': user}) @method_decorator(login_required) def profile_edit(self, request, username, template='teleforma/profile_edit.html'): if request.user.is_superuser: - user_hidden_fields = ['profile-user', 'user-password', 'user-last_login', 'user-date_joined'] + user_hidden_fields = [ + 'profile-user', 'user-password', 'user-last_login', 'user-date_joined'] else: user_hidden_fields = ['user-username', 'user-is_staff', 'profile-user', 'user-is_active', - 'user-password', 'user-last_login', 'user-date_joined', 'user-groups', - 'user-user_permissions', 'user-is_superuser', 'profile-expiration_date'] + 'user-password', 'user-last_login', 'user-date_joined', 'user-groups', + 'user-user_permissions', 'user-is_superuser', 'profile-expiration_date'] user = User.objects.get(username=username) if user != request.user and not request.user.is_staff: mess = ugettext('Access not allowed') title = ugettext('User profile') + ' : ' + username + ' : ' + mess - description = ugettext('Please login or contact the website administator to get a private access.') + description = ugettext( + 'Please login or contact the website administator to get a private access.') messages.error(request, title) - return render(request, 'teleforma/messages.html', {'description' : description}) + return render(request, 'teleforma/messages.html', {'description': description}) try: profile = user.get_profile() @@ -77,8 +84,10 @@ class ProfileView(object): profile = UserProfile(user=user) if request.method == 'POST': - user_form = UserChangeForm(request.POST, instance=user, prefix='user') - profile_form = UserProfileForm(request.POST, instance=profile, prefix='profile') + user_form = UserChangeForm( + request.POST, instance=user, prefix='user') + profile_form = UserProfileForm( + request.POST, instance=profile, prefix='profile') if user_form.is_valid() and profile_form.is_valid(): user_form.save() profile_form.save() @@ -88,5 +97,4 @@ class ProfileView(object): profile_form = UserProfileForm(instance=profile, prefix='profile') forms = [user_form, profile_form] return render(request, template, {'forms': forms, 'usr': user, - 'user_hidden_fields': user_hidden_fields}) - + 'user_hidden_fields': user_hidden_fields}) diff --git a/teleforma/webclass/admin.py b/teleforma/webclass/admin.py index 6e69c7ba..f9b5dbdf 100644 --- a/teleforma/webclass/admin.py +++ b/teleforma/webclass/admin.py @@ -1,7 +1,8 @@ # -*- coding: utf-8 -*- -from teleforma.admin import * -from teleforma.webclass.models import * from django.contrib import admin +from teleforma.webclass.models import (BBBServer, Webclass, WebclassRecord, + WebclassSlot) + class BBBServerAdmin(admin.ModelAdmin): model = BBBServer @@ -35,4 +36,4 @@ class WebclassRecordAdmin(admin.ModelAdmin): admin.site.register(BBBServer, BBBServerAdmin) admin.site.register(Webclass, WebclassAdmin) -admin.site.register(WebclassRecord, WebclassRecordAdmin) \ No newline at end of file +admin.site.register(WebclassRecord, WebclassRecordAdmin) diff --git a/teleforma/webclass/forms.py b/teleforma/webclass/forms.py index d190ec6d..c7a7bc0c 100644 --- a/teleforma/webclass/forms.py +++ b/teleforma/webclass/forms.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- -from datetime import datetime -from django.forms import Form, ModelChoiceField, ChoiceField -from teleforma.models.core import Course, Period -from teleforma.webclass.models import get_records, WebclassSlot, WebclassRecord, BBBServer -from django.core.exceptions import ValidationError +from django.forms import ChoiceField, Form + +from ..models.core import Course, Period +from ..webclass.models import (BBBServer, WebclassRecord, WebclassSlot, + get_records) + class WebclassRecordsForm(Form): @@ -16,7 +17,7 @@ class WebclassRecordsForm(Form): courses = Course.objects.all() all_records = self.get_records_by_course() - + for course in courses: # get list of webclass webclasses = course.webclass.filter(period=self.period).all() @@ -33,12 +34,16 @@ class WebclassRecordsForm(Form): vocabulary = [('none', 'Aucun')] # for each bbb record for the current course, add an option to the field - + for record in records: - webclass_slot = WebclassSlot.objects.get(pk=record['slot'].id) - label = u"%s à %s - %s" % (record['start_date'].strftime('%d/%m/%Y %H:%M'), record['end_date'].strftime('%H:%M'), webclass_slot.professor.user.last_name) - vocabulary.append((str(record['id']) + ";" + str(record['server_id']), label)) - self.fields[field_name] = ChoiceField(label=course.title, choices=vocabulary, required=False) + webclass_slot = WebclassSlot.objects.get( + pk=record['slot'].id) + label = u"%s à %s - %s" % (record['start_date'].strftime( + '%d/%m/%Y %H:%M'), record['end_date'].strftime('%H:%M'), webclass_slot.professor.user.last_name) + vocabulary.append( + (str(record['id']) + ";" + str(record['server_id']), label)) + self.fields[field_name] = ChoiceField( + label=course.title, choices=vocabulary, required=False) def get_records_by_course(self): """ @@ -58,5 +63,6 @@ class WebclassRecordsForm(Form): course_id = key.replace('course_', '') course = Course.objects.get(pk=course_id) server = BBBServer.objects.get(pk=server_id) - record = WebclassRecord(course=course, period=self.period, record_id=record_id, bbb_server=server) + record = WebclassRecord( + course=course, period=self.period, record_id=record_id, bbb_server=server) record.save() diff --git a/teleforma/webclass/models.py b/teleforma/webclass/models.py index f30e257a..aa0297b4 100644 --- a/teleforma/webclass/models.py +++ b/teleforma/webclass/models.py @@ -1,22 +1,22 @@ # -*- coding: utf-8 -*- -import datetime -from datetime import timedelta, date import calendar -from unidecode import unidecode +import datetime +from datetime import date, timedelta -from django.db.models import * -from django.contrib.auth.models import User -from teleforma.fields import * 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 +from django.contrib.auth.models import User +from django.db import models +from django.db.models.signals import post_save +from django.dispatch import receiver +from django.template.defaultfilters import slugify +from django.utils import translation +from django.utils.translation import ugettext_lazy as _ +from jxmlease import XMLDictNode, XMLListNode +from teleforma.fields import DurationField +from unidecode import unidecode translation.activate('fr') app_label = 'teleforma' @@ -25,9 +25,9 @@ DAYS_CHOICES = [(i, _(calendar.day_name[i])) for i in range(7)] # BBB_SERVER = "https://bbb.parisson.com/bigbluebutton/" # BBB_SECRET = "uOzkrTnWly1jusr0PYcrlwhvKhZG1ZYDOrSvxgP70" STATUS_CHOICES = ( - (2, _('Draft')), - (3, _('Public')), - ) + (2, _('Draft')), + (3, _('Public')), +) class MetaCore: @@ -38,7 +38,8 @@ def get_records_from_bbb(**kwargs): """get records info from bbb xml""" records = [] for server in BBBServer.objects.all(): - recordings = server.get_instance().get_recordings(**kwargs).get_field('recordings') + recordings = server.get_instance().get_recordings( + **kwargs).get_field('recordings') if hasattr(recordings, 'get'): recordings = recordings['recording'] if type(recordings) is XMLDictNode: @@ -68,7 +69,8 @@ def get_records_from_bbb(**kwargs): # we try to get metadata added to bbb record during the recording slot = None try: - slot = WebclassSlot.objects.get(pk=int(recording['metadata'].get('slotid').decode())) + slot = WebclassSlot.objects.get( + pk=int(recording['metadata'].get('slotid').decode())) except WebclassSlot.DoesNotExist: # this happen if the slot is deleted in django admin continue @@ -80,9 +82,10 @@ def get_records_from_bbb(**kwargs): data['duration'] = data['end'] - data['start'] records.append(data) - records = sorted(records, key=lambda r:r['start']) + records = sorted(records, key=lambda r: r['start']) return records + def get_records(period_id=None, course_id=None, rooms=None, recording_id=None): """ get all records, filtered """ # if not rooms: @@ -100,13 +103,12 @@ def get_records(period_id=None, course_id=None, rooms=None, recording_id=None): if not all_records: return [] - all_records = sorted(all_records, key=lambda record:-record['start']) + all_records = sorted(all_records, key=lambda record: -record['start']) # for record in all_records: # vocabulary.append((record['url'], record['start'])) return all_records - class BBBServer(models.Model): url = models.CharField("Url du serveur BBB", max_length=100) api_key = models.CharField("API Key", max_length=100) @@ -119,24 +121,33 @@ class BBBServer(models.Model): def get_instance(self): return BigBlueButton(self.url, self.api_key) - def __unicode__(self): + def __str__(self): return "Serveur %d" % self.id + class PublishedManager(models.Manager): def get_query_set(self): return super(PublishedManager, self).get_query_set().filter(status=3).exclude(end_date__lt=date.today()) + 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', default="00:30:00") - max_participants = models.IntegerField('Nombre maxium de participants par créneau', blank=True, null=True, default=80) - end_date = models.DateField('date de fin', blank=True, null=True) - status = models.IntegerField(_('status'), choices=STATUS_CHOICES, default=2) + 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'), on_delete=models.CASCADE) + iej = models.ManyToManyField( + 'teleforma.IEJ', related_name='webclass', verbose_name=_('iej'), blank=True) + bbb_server = models.ForeignKey( + 'BBBServer', related_name='webclass', verbose_name='Serveur BBB', on_delete=models.CASCADE) + duration = DurationField('Durée de la conférence', default="00:30:00") + max_participants = models.IntegerField( + 'Nombre maxium de participants par créneau', blank=True, null=True, default=80) + end_date = models.DateField('date de fin', blank=True, null=True) + status = models.IntegerField( + _('status'), choices=STATUS_CHOICES, default=2) objects = models.Manager() published = PublishedManager() @@ -146,7 +157,7 @@ class Webclass(models.Model): verbose_name = _('webclass') verbose_name_plural = _('webclass') - def __unicode__(self): + def __str__(self): return "Webclass %d : %s" % (self.id, self.course.title) def get_slot(self, user): @@ -156,21 +167,26 @@ class Webclass(models.Model): except WebclassSlot.DoesNotExist: return None + class SlotPublishedManager(models.Manager): def get_query_set(self): return super(SlotPublishedManager, self).get_query_set().filter(webclass__status=3).exclude(webclass__end_date__lt=date.today()) + 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) + webclass = models.ForeignKey( + 'Webclass', related_name='slots', on_delete=models.CASCADE) + 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) + 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) objects = models.Manager() published = SlotPublishedManager() @@ -179,10 +195,9 @@ class WebclassSlot(models.Model): db_table = app_label + '_' + 'webclass_slot' verbose_name = _('webclass slot') - def __unicode__(self): + def __str__(self): return "Webclass slot : " + str(self.id) - @property def remaining_participant_slot(self): """ @@ -203,7 +218,8 @@ class WebclassSlot(models.Model): """ start hour + duration """ - date = datetime.datetime.combine(datetime.date.today(), self.start_hour) + timedelta(seconds=self.webclass.duration.as_seconds()) + date = datetime.datetime.combine(datetime.date.today( + ), self.start_hour) + timedelta(seconds=self.webclass.duration.as_seconds()) return date.time() @property @@ -216,7 +232,8 @@ class WebclassSlot(models.Model): """ 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) + 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 @@ -233,14 +250,14 @@ class WebclassSlot(models.Model): # site_url = 'https://' + request.get_host() webclass = self.webclass params = { - 'moderatorPW':self.room_password, - 'attendeePW':'pwattendee', + '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, + 'welcome': "Pré-Barreau : Bienvenue sur la conférence \"%s\"." % (webclass.course.title.encode('utf-8'),), + 'record': True, # 'autoStartRecording':True, - 'muteOnStart':True, - 'allowModsToUnmuteUsers':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' @@ -249,21 +266,21 @@ class WebclassSlot(models.Model): # 'customStyleUrl': site_url+"/static/teleforma/css/bbb.css" } meta = { - 'origin':'crfpa', - 'periodid': webclass.period.id, - 'courseid': webclass.course.id, - 'webclassid': webclass.id, - 'slotid': self.id, - 'professor': self.professor.user.username, - } - print params + 'origin': 'crfpa', + 'periodid': webclass.period.id, + 'courseid': webclass.course.id, + 'webclassid': webclass.id, + 'slotid': self.id, + 'professor': self.professor.user.username, + } + print(params) try: - result = self.bbb.create_meeting(self.room_id, params=params, meta=meta) + result = self.bbb.create_meeting( + self.room_id, params=params, meta=meta) except BBBException as e: print(e) raise - def get_join_webclass_url(self, request, user, username=None): """ Get url to BBB meeting. @@ -300,7 +317,8 @@ class WebclassSlot(models.Model): if days_ahead < 0: days_ahead += 7 next_date = now + datetime.timedelta(days_ahead) - next_date = next_date.replace(hour=self.start_hour.hour, minute=self.start_hour.minute) + next_date = next_date.replace( + hour=self.start_hour.hour, minute=self.start_hour.minute) if self.webclass.end_date and next_date.date() > self.webclass.end_date: return None return next_date @@ -313,7 +331,8 @@ class WebclassSlot(models.Model): now = datetime.datetime.now() next_webclass_date_begin = self.next_webclass_date() if next_webclass_date_begin: - next_webclass_date_end = next_webclass_date_begin + timedelta(seconds=self.webclass.duration.as_seconds()) + next_webclass_date_end = next_webclass_date_begin + \ + timedelta(seconds=self.webclass.duration.as_seconds()) else: return "none" begin_minus_1_hour = next_webclass_date_begin - timedelta(hours=1) @@ -342,38 +361,6 @@ class WebclassSlot(models.Model): """ """ 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): @@ -381,22 +368,23 @@ def create_webclass_room(sender, **kwargs): 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')) - record_id = models.CharField("Enregistrement BBB", max_length=255) + period = models.ForeignKey('teleforma.Period', verbose_name=_('period'), on_delete=models.CASCADE) + course = models.ForeignKey( + 'teleforma.Course', related_name='webclass_records', verbose_name=_('course'), on_delete=models.CASCADE) + record_id = models.CharField("Enregistrement BBB", max_length=255) # not used for now, but may be handy if we need to optimize performance - bbb_server = models.ForeignKey('BBBServer', related_name='webclass_records', verbose_name='Serveur BBB') - created = models.DateTimeField("Date de la conférence", auto_now_add=True) + bbb_server = models.ForeignKey( + 'BBBServer', related_name='webclass_records', verbose_name='Serveur BBB', on_delete=models.CASCADE) + created = models.DateTimeField("Date de la conférence", auto_now_add=True) class Meta(MetaCore): db_table = app_label + '_' + 'webclass_record' verbose_name = 'enregistrement' verbose_name_plural = 'enregistrements' - def __unicode__(self): + def __str__(self): return "Enregistrement webclass %d" % self.id @staticmethod diff --git a/teleforma/webclass/migrations/0001_initial.py b/teleforma/webclass/south_migrations/0001_initial.py similarity index 100% rename from teleforma/webclass/migrations/0001_initial.py rename to teleforma/webclass/south_migrations/0001_initial.py diff --git a/teleforma/webclass/migrations/0002_auto__add_webclassrecord.py b/teleforma/webclass/south_migrations/0002_auto__add_webclassrecord.py similarity index 100% rename from teleforma/webclass/migrations/0002_auto__add_webclassrecord.py rename to teleforma/webclass/south_migrations/0002_auto__add_webclassrecord.py diff --git a/teleforma/webclass/migrations/0003_auto__add_field_webclassrecord_bbb_server.py b/teleforma/webclass/south_migrations/0003_auto__add_field_webclassrecord_bbb_server.py similarity index 100% rename from teleforma/webclass/migrations/0003_auto__add_field_webclassrecord_bbb_server.py rename to teleforma/webclass/south_migrations/0003_auto__add_field_webclassrecord_bbb_server.py diff --git a/teleforma/webclass/migrations/0004_auto__add_field_webclass_end_date.py b/teleforma/webclass/south_migrations/0004_auto__add_field_webclass_end_date.py similarity index 100% rename from teleforma/webclass/migrations/0004_auto__add_field_webclass_end_date.py rename to teleforma/webclass/south_migrations/0004_auto__add_field_webclass_end_date.py diff --git a/teleforma/webclass/migrations/__init__.py b/teleforma/webclass/south_migrations/__init__.py similarity index 100% rename from teleforma/webclass/migrations/__init__.py rename to teleforma/webclass/south_migrations/__init__.py diff --git a/teleforma/webclass/urls.py b/teleforma/webclass/urls.py index 87d77617..f1416c3f 100644 --- a/teleforma/webclass/urls.py +++ b/teleforma/webclass/urls.py @@ -32,19 +32,23 @@ # # Authors: Guillaume Pellerin -from django.conf.urls import patterns, url, include -from django.http import HttpResponse -from teleforma.webclass.views import * +from django.conf.urls import url +from ..webclass.views import (WebclassAppointment, + WebclassProfessorAppointments, + WebclassRecordsFormView, WebclassRecordView, + join_webclass) -urlpatterns = patterns('', +urlpatterns = [ url(r'^desk/webclass_appointments/(?P.*)$', WebclassAppointment.as_view(), - name="teleforma-webclass-appointments"), - url(r'^desk/webclass_calendar/$', WebclassProfessorAppointments.as_view(), name="teleforma-webclass-professor"), - url(r'^desk/webclass_record$', WebclassRecordView.as_view(), name="teleforma-webclass-record"), - url(r'^admin/periods/(?P.*)/webclass_records_form/$', WebclassRecordsFormView.as_view(), name="teleforma-webclass-records-form"), + name="teleforma-webclass-appointments"), + url(r'^desk/webclass_calendar/$', WebclassProfessorAppointments.as_view(), + name="teleforma-webclass-professor"), + url(r'^desk/webclass_record$', WebclassRecordView.as_view(), + name="teleforma-webclass-record"), + url(r'^admin/periods/(?P.*)/webclass_records_form/$', + WebclassRecordsFormView.as_view(), name="teleforma-webclass-records-form"), url(r'^desk/webclass/(?P.*)/join/$', join_webclass, - name="teleforma-webclass-join"), - -) + name="teleforma-webclass-join") +] diff --git a/teleforma/webclass/views.py b/teleforma/webclass/views.py index 673cedbc..22932b08 100644 --- a/teleforma/webclass/views.py +++ b/teleforma/webclass/views.py @@ -1,24 +1,17 @@ # -*- coding: utf-8 -*- -from django.views.generic import View, TemplateView, FormView -from django.utils.decorators import method_decorator from django.contrib import messages -from django.contrib.auth.decorators import login_required, permission_required -from django.http import HttpResponse -from django.shortcuts import redirect, get_object_or_404, render -from django.template.loader import render_to_string +from django.contrib.auth.decorators import permission_required 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.webclass.forms import WebclassRecordsForm -from teleforma.decorators import access_required +from django.shortcuts import get_object_or_404, redirect, render +from django.urls import reverse +from django.utils.decorators import method_decorator +from django.views.generic import FormView, TemplateView, View -from teleforma.views.core import get_periods, get_courses +from ..decorators import access_required +from ..views.core import get_courses, get_periods +from ..webclass.forms import WebclassRecordsForm +from ..webclass.models import Webclass, WebclassSlot class WebclassProfessorAppointments(TemplateView): @@ -26,15 +19,18 @@ class WebclassProfessorAppointments(TemplateView): def get_context_data(self, **kwargs): """ """ - context = super(WebclassProfessorAppointments, self).get_context_data(**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.published.filter(professor=user.professor.get(), webclass__status=3).order_by('day', 'start_hour') + context['slots'] = WebclassSlot.published.filter( + professor=user.professor.get(), webclass__status=3).order_by('day', 'start_hour') print(context['slots']) return context + class WebclassAppointment(View): template_name = 'webclass/appointments.html' @@ -47,12 +43,13 @@ class WebclassAppointment(View): student = student[0] # check period period_id = webclass.period.id - periods = [ p for p in get_periods(user) if int(p.id) == 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 ] + 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 ? @@ -89,12 +86,12 @@ class WebclassAppointment(View): return u"Ce créneau n'est plus disponible." # Check we don't have another appointment on this period - webclass = slot.webclass + webclass = slot.webclass 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) + webclass = get_object_or_404(Webclass, id=pk) rights = self.check_rights(request.user, webclass) if rights: return rights @@ -105,18 +102,19 @@ class WebclassAppointment(View): msg = self.check_slot_validity(user, slot) - if not msg: + 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})) + 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) + webclass = get_object_or_404(Webclass, id=pk) rights = self.check_rights(request.user, webclass) if rights: return rights @@ -146,6 +144,7 @@ class WebclassAppointment(View): # fail_silently=False) # return data + class WebclassRecordView(TemplateView): template_name = 'webclass/record.html' @@ -169,7 +168,7 @@ class WebclassRecordsFormView(FormView): def form_valid(self, form): form.save_records() return super(WebclassRecordsFormView, self).form_valid(form) - + @method_decorator(permission_required('is_superuser')) @method_decorator(access_required) def dispatch(self, *args, **kwargs): -- 2.39.5