mirror of https://gitlab.crans.org/bde/nk20
WEI Survey (work in progress)
This commit is contained in:
parent
b62fa4cc6d
commit
8113c5cd61
|
@ -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__
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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',
|
||||||
|
]
|
|
@ -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):
|
|
@ -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
|
|
@ -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
|
|
@ -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()
|
|
@ -0,0 +1,2 @@
|
||||||
|
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
@ -0,0 +1,2 @@
|
||||||
|
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
@ -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()
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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"),
|
||||||
]
|
]
|
||||||
|
|
|
@ -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,))
|
||||||
|
|
|
@ -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/'
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
@ -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 %}
|
Loading…
Reference in New Issue