WEI Survey (work in progress)

This commit is contained in:
Yohann D'ANELLO 2020-04-19 20:35:49 +02:00
parent b62fa4cc6d
commit 8113c5cd61
19 changed files with 255 additions and 18 deletions

4
.gitignore vendored
View File

@ -1,7 +1,7 @@
# Server config files # Server config forms
nginx_note.conf nginx_note.conf
# Byte-compiled / optimized / DLL files # Byte-compiled / optimized / DLL forms
dist dist
build build
__pycache__ __pycache__

View File

@ -184,7 +184,7 @@ class InvoiceRenderView(LoginRequiredMixin, View):
except IOError as e: except IOError as e:
raise e raise e
finally: finally:
# Delete all temporary files # Delete all temporary forms
shutil.rmtree(tmp_dir) shutil.rmtree(tmp_dir)
return response return response

View File

@ -0,0 +1,10 @@
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
from .registration import *
from .surveys import *
__all__ = [
'WEIForm', 'WEIRegistrationForm', 'WEIMembershipForm', 'BusForm', 'BusTeamForm',
'WEISurvey', 'WEISurveyInformation', 'WEISurveyAlgorithm', 'CurrentSurvey',
]

View File

@ -5,7 +5,7 @@ from django import forms
from django.contrib.auth.models import User from django.contrib.auth.models import User
from note_kfet.inputs import AmountInput, DatePickerInput, Autocomplete, ColorWidget from note_kfet.inputs import AmountInput, DatePickerInput, Autocomplete, ColorWidget
from .models import WEIClub, WEIRegistration, Bus, BusTeam, WEIMembership, WEIRole from wei.models import WEIClub, WEIRegistration, Bus, BusTeam, WEIMembership, WEIRole
class WEIForm(forms.ModelForm): class WEIForm(forms.ModelForm):

View File

@ -0,0 +1,12 @@
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
from .base import WEISurvey, WEISurveyInformation, WEISurveyAlgorithm
from .wei2020 import WEISurvey2020
__all__ = [
'WEISurvey', 'WEISurveyInformation', 'WEISurveyAlgorithm', 'CurrentSurvey',
]
CurrentSurvey = WEISurvey2020

View File

@ -0,0 +1,61 @@
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
from wei.models import WEIClub, WEIRegistration
class WEISurvey:
year = None
step = 0
def __init__(self, registration):
self.registration = registration
self.information = self.get_survey_information_class()(registration)
def get_wei(self):
return WEIClub.objects.get(year=self.year)
def get_survey_information_class(self):
raise NotImplementedError
def get_form_class(self):
raise NotImplementedError
def update_form(self, form):
pass
@staticmethod
def get_algorithm_class():
raise NotImplementedError
def form_valid(self, form):
raise NotImplementedError
def save(self):
self.information.save(self.registration)
def select_bus(self, bus_pk):
self.information.selected_bus_pk = bus_pk
class WEISurveyInformation:
valid = False
selected_bus_pk = None
def __init__(self, registration):
self.__dict__.update(registration.information)
def save(self, registration):
registration.information = self.__dict__
registration.save()
class WEISurveyAlgorithm:
def get_survey_class(self):
raise NotImplementedError
def get_registrations(self):
return WEIRegistration.objects.filter(wei__year=self.get_survey_class().year, first_year=True).all()
def run_algorithm(self):
raise NotImplementedError

View File

@ -0,0 +1,52 @@
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
from django import forms
from .base import WEISurvey, WEISurveyInformation, WEISurveyAlgorithm
from ...models import Bus
class WEISurveyForm2020(forms.Form):
bus = forms.ModelChoiceField(
Bus.objects,
)
def set_registration(self, registration):
self.fields["bus"].queryset = Bus.objects.filter(wei=registration.wei)
class WEISurveyInformation2020(WEISurveyInformation):
chosen_bus_pk = None
class WEISurvey2020(WEISurvey):
year = 2020
def get_survey_information_class(self):
return WEISurveyInformation2020
def get_form_class(self):
return WEISurveyForm2020
def update_form(self, form):
form.set_registration(self.registration)
def form_valid(self, form):
self.information.chosen_bus_pk = form.cleaned_data["bus"].pk
self.save()
@staticmethod
def get_algorithm_class():
return WEISurveyAlgorithm2020
class WEISurveyAlgorithm2020(WEISurveyAlgorithm):
def get_survey_class(self):
return WEISurvey2020
def run_algorithm(self):
for registration in self.get_registrations():
survey = self.get_survey_class()(registration)
survey.select_bus(survey.information.chosen_bus_pk)
survey.save()

View File

View File

@ -0,0 +1,2 @@
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later

View File

@ -0,0 +1,2 @@
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later

View File

@ -0,0 +1,14 @@
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
from django.core.management import BaseCommand
from django.utils.translation import gettext_lazy as _
from wei.forms import CurrentSurvey
class Command(BaseCommand):
help = _("Attribute to each first year member a bus for the WEI")
def handle(self, *args, **options):
CurrentSurvey.get_algorithm_class()().run_algorithm()

View File

@ -64,6 +64,27 @@ class Bus(models.Model):
verbose_name=_("description"), verbose_name=_("description"),
) )
information_json = models.TextField(
default="{}",
verbose_name=_("survey information"),
help_text=_("Information about the survey for new members, encoded in JSON"),
)
@property
def information(self):
"""
The information about the survey for new members are stored in a dictionary that can evolve following the years.
The dictionary is stored as a JSON string.
"""
return json.loads(self.information_json)
@information.setter
def information(self, information):
"""
Store information as a JSON string
"""
self.information_json = json.dumps(information)
def __str__(self): def __str__(self):
return self.name return self.name

View File

@ -5,7 +5,7 @@ from django.urls import path
from .views import CurrentWEIDetailView, WEIListView, WEICreateView, WEIDetailView, WEIUpdateView,\ from .views import CurrentWEIDetailView, WEIListView, WEICreateView, WEIDetailView, WEIUpdateView,\
BusCreateView, BusManageView, BusUpdateView, BusTeamCreateView, BusTeamManageView, BusTeamUpdateView,\ BusCreateView, BusManageView, BusUpdateView, BusTeamCreateView, BusTeamManageView, BusTeamUpdateView,\
WEIRegister1AView, WEIRegister2AView, WEIUpdateRegistrationView, WEIValidateRegistrationView WEIRegister1AView, WEIRegister2AView, WEIUpdateRegistrationView, WEIValidateRegistrationView, WEISurveyView
app_name = 'wei' app_name = 'wei'
@ -27,4 +27,5 @@ urlpatterns = [
path('register/<int:wei_pk>/2A+/myself/', WEIRegister2AView.as_view(), name="wei_register_2A_myself"), path('register/<int:wei_pk>/2A+/myself/', WEIRegister2AView.as_view(), name="wei_register_2A_myself"),
path('edit-registration/<int:pk>/', WEIUpdateRegistrationView.as_view(), name="wei_update_registration"), path('edit-registration/<int:pk>/', WEIUpdateRegistrationView.as_view(), name="wei_update_registration"),
path('validate/<int:pk>/', WEIValidateRegistrationView.as_view(), name="validate_registration"), path('validate/<int:pk>/', WEIValidateRegistrationView.as_view(), name="validate_registration"),
path('survey/<int:pk>/', WEISurveyView.as_view(), name="wei_survey"),
] ]

View File

@ -9,6 +9,7 @@ from django.db.models import Q
from django.urls import reverse_lazy from django.urls import reverse_lazy
from django.views.generic import DetailView, UpdateView, CreateView, RedirectView from django.views.generic import DetailView, UpdateView, CreateView, RedirectView
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.views.generic.edit import BaseFormView
from django_tables2 import SingleTableView from django_tables2 import SingleTableView
from member.models import Membership, Club from member.models import Membership, Club
from note.models import Transaction, NoteClub from note.models import Transaction, NoteClub
@ -17,7 +18,7 @@ from permission.backends import PermissionBackend
from permission.views import ProtectQuerysetMixin from permission.views import ProtectQuerysetMixin
from .models import WEIClub, WEIRegistration, WEIMembership, Bus, BusTeam, WEIRole from .models import WEIClub, WEIRegistration, WEIMembership, Bus, BusTeam, WEIRole
from .forms import WEIForm, WEIRegistrationForm, BusForm, BusTeamForm, WEIMembershipForm from .forms import WEIForm, WEIRegistrationForm, BusForm, BusTeamForm, WEIMembershipForm, CurrentSurvey
from .tables import WEITable, WEIRegistrationTable, BusTable, BusTeamTable, WEIMembershipTable from .tables import WEITable, WEIRegistrationTable, BusTable, BusTeamTable, WEIMembershipTable
@ -302,8 +303,7 @@ class WEIRegister1AView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
def get_success_url(self): def get_success_url(self):
self.object.refresh_from_db() self.object.refresh_from_db()
# TODO Replace it with the link of the survey return reverse_lazy("wei:wei_survey", kwargs={"pk": self.object.pk})
return reverse_lazy("wei:wei_detail", kwargs={"pk": self.object.wei.pk})
class WEIRegister2AView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView): class WEIRegister2AView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
@ -342,7 +342,7 @@ class WEIRegister2AView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
def get_success_url(self): def get_success_url(self):
self.object.refresh_from_db() self.object.refresh_from_db()
return reverse_lazy("wei:wei_detail", kwargs={"pk": self.object.wei.pk}) return reverse_lazy("wei:wei_survey", kwargs={"pk": self.object.pk})
class WEIUpdateRegistrationView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView): class WEIUpdateRegistrationView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
@ -447,3 +447,36 @@ class WEIValidateRegistrationView(ProtectQuerysetMixin, LoginRequiredMixin, Crea
def get_success_url(self): def get_success_url(self):
self.object.refresh_from_db() self.object.refresh_from_db()
return reverse_lazy("wei:wei_detail", kwargs={"pk": self.object.club.pk}) return reverse_lazy("wei:wei_detail", kwargs={"pk": self.object.club.pk})
class WEISurveyView(BaseFormView, DetailView):
model = WEIRegistration
template_name = "wei/survey.html"
survey = None
def setup(self, request, *args, **kwargs):
ret = super().setup(request, *args, **kwargs)
return ret
def get_form_class(self):
if not self.survey:
self.survey = CurrentSurvey(self.get_object())
return self.survey.get_form_class()
def get_form(self, form_class=None):
form = super().get_form(form_class)
self.survey.update_form(form)
return form
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["club"] = self.object.wei
context["title"] = _("Survey WEI")
return context
def form_valid(self, form):
self.survey.form_valid(form)
return super().form_valid(form)
def get_success_url(self):
return reverse_lazy('wei:wei_survey', args=(self.get_object().pk,))

View File

@ -168,11 +168,11 @@ LOCALE_PATHS = [os.path.join(BASE_DIR, "locale")]
FIXTURE_DIRS = [os.path.join(BASE_DIR, "note_kfet/fixtures")] FIXTURE_DIRS = [os.path.join(BASE_DIR, "note_kfet/fixtures")]
# Static files (CSS, JavaScript, Images) # Static forms (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.2/howto/static-files/ # https://docs.djangoproject.com/en/2.2/howto/static-files/
# Absolute path to the directory static files should be collected to. # Absolute path to the directory static forms should be collected to.
# Don't put anything in this directory yourself; store your static files # Don't put anything in this directory yourself; store your static forms
# in apps' "static/" subdirectories and in STATICFILES_DIRS. # in apps' "static/" subdirectories and in STATICFILES_DIRS.
# Example: "/var/www/example.com/static/" # Example: "/var/www/example.com/static/"
STATIC_ROOT = os.path.join(BASE_DIR, "static/") STATIC_ROOT = os.path.join(BASE_DIR, "static/")
@ -181,7 +181,7 @@ STATIC_ROOT = os.path.join(BASE_DIR, "static/")
STATICFILES_DIRS = [] STATICFILES_DIRS = []
CRISPY_TEMPLATE_PACK = 'bootstrap4' CRISPY_TEMPLATE_PACK = 'bootstrap4'
DJANGO_TABLES2_TEMPLATE = 'django_tables2/bootstrap4.html' DJANGO_TABLES2_TEMPLATE = 'django_tables2/bootstrap4.html'
# URL prefix for static files. # URL prefix for static forms.
# Example: "http://example.com/static/", "http://static.example.com/" # Example: "http://example.com/static/", "http://static.example.com/"
STATIC_URL = '/static/' STATIC_URL = '/static/'

View File

@ -10310,7 +10310,7 @@ jQuery.isNumeric = function( obj ) {
// Register as a named AMD module, since jQuery can be concatenated with other // Register as a named AMD module, since jQuery can be concatenated with other
// files that may use define, but not via a proper concatenation script that // forms that may use define, but not via a proper concatenation script that
// understands anonymous AMD modules. A named AMD is safest and most robust // understands anonymous AMD modules. A named AMD is safest and most robust
// way to register. Lowercase jquery is used because AMD module names are // way to register. Lowercase jquery is used because AMD module names are
// derived from file names, and jQuery is normally delivered in a lowercase // derived from file names, and jQuery is normally delivered in a lowercase

View File

@ -23,7 +23,7 @@
var S2 = var S2 =
(function () { (function () {
// Restore the Select2 AMD loader so it can be used // Restore the Select2 AMD loader so it can be used
// Needed mostly in the language files, where the loader is not inserted // Needed mostly in the language forms, where the loader is not inserted
if (jQuery && jQuery.fn && jQuery.fn.select2 && jQuery.fn.select2.amd) { if (jQuery && jQuery.fn && jQuery.fn.select2 && jQuery.fn.select2.amd) {
var S2 = jQuery.fn.select2.amd; var S2 = jQuery.fn.select2.amd;
} }
@ -4774,7 +4774,7 @@ S2.define('select2/defaults',[
} catch (ex) { } catch (ex) {
// The translation could not be loaded at all. Sometimes this is // The translation could not be loaded at all. Sometimes this is
// because of a configuration problem, other times this can be // because of a configuration problem, other times this can be
// because of how Select2 helps load all possible translation files. // because of how Select2 helps load all possible translation forms.
if (options.debug && window.console && console.warn) { if (options.debug && window.console && console.warn) {
console.warn( console.warn(
'Select2: The language file for "' + name + '" could not be ' + 'Select2: The language file for "' + name + '" could not be ' +
@ -6428,7 +6428,7 @@ S2.define('jquery.select2',[
// Hold the AMD module references on the jQuery function that was just loaded // Hold the AMD module references on the jQuery function that was just loaded
// This allows Select2 to use the internal loader outside of this file, such // This allows Select2 to use the internal loader outside of this file, such
// as in the language files. // as in the language forms.
jQuery.fn.select2.amd = S2; jQuery.fn.select2.amd = S2;
// Return the Select2 instance for anyone who is importing it. // Return the Select2 instance for anyone who is importing it.

File diff suppressed because one or more lines are too long

29
templates/wei/survey.html Normal file
View File

@ -0,0 +1,29 @@
{% extends "member/noteowner_detail.html" %}
{% load i18n %}
{% load crispy_forms_tags %}
{% block profile_info %}
{% include "wei/weiclub_info.html" %}
{% endblock %}
{% block profile_content %}
<div class="card">
<div class="card-header text-center">
<h4>{% trans "Survey WEI" %}</h4>
</div>
<div class="card-body">
<dl class="row">
<dt class="col-xl-6">{% trans 'user'|capfirst %}</dt>
<dd class="col-xl-6">{{ object.user }}</dd>
</dl>
<form method="post">
{% csrf_token %}
{{ form|crispy }}
<div class="card-footer text-center">
<input class="btn btn-success" type="submit" value="{% trans "Next" %}"/>
</div>
</form>
</div>
</div>
{% endblock %}