mirror of
https://gitlab.crans.org/bde/nk20
synced 2024-12-24 00:12:23 +00:00
WEI Survey (work in progress)
This commit is contained in:
parent
b62fa4cc6d
commit
8113c5cd61
4
.gitignore
vendored
4
.gitignore
vendored
@ -1,7 +1,7 @@
|
||||
# Server config files
|
||||
# Server config forms
|
||||
nginx_note.conf
|
||||
|
||||
# Byte-compiled / optimized / DLL files
|
||||
# Byte-compiled / optimized / DLL forms
|
||||
dist
|
||||
build
|
||||
__pycache__
|
||||
|
@ -184,7 +184,7 @@ class InvoiceRenderView(LoginRequiredMixin, View):
|
||||
except IOError as e:
|
||||
raise e
|
||||
finally:
|
||||
# Delete all temporary files
|
||||
# Delete all temporary forms
|
||||
shutil.rmtree(tmp_dir)
|
||||
|
||||
return response
|
||||
|
10
apps/wei/forms/__init__.py
Normal file
10
apps/wei/forms/__init__.py
Normal 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',
|
||||
]
|
@ -5,7 +5,7 @@ from django import forms
|
||||
from django.contrib.auth.models import User
|
||||
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):
|
12
apps/wei/forms/surveys/__init__.py
Normal file
12
apps/wei/forms/surveys/__init__.py
Normal 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
|
61
apps/wei/forms/surveys/base.py
Normal file
61
apps/wei/forms/surveys/base.py
Normal 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
|
52
apps/wei/forms/surveys/wei2020.py
Normal file
52
apps/wei/forms/surveys/wei2020.py
Normal 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()
|
0
apps/wei/management/__init__.py
Normal file
0
apps/wei/management/__init__.py
Normal file
2
apps/wei/management/commands/__init__.py
Normal file
2
apps/wei/management/commands/__init__.py
Normal file
@ -0,0 +1,2 @@
|
||||
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
2
apps/wei/management/commands/_private.py
Normal file
2
apps/wei/management/commands/_private.py
Normal file
@ -0,0 +1,2 @@
|
||||
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
14
apps/wei/management/commands/wei_algorithm.py
Normal file
14
apps/wei/management/commands/wei_algorithm.py
Normal 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()
|
@ -64,6 +64,27 @@ class Bus(models.Model):
|
||||
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):
|
||||
return self.name
|
||||
|
||||
|
@ -5,7 +5,7 @@ from django.urls import path
|
||||
|
||||
from .views import CurrentWEIDetailView, WEIListView, WEICreateView, WEIDetailView, WEIUpdateView,\
|
||||
BusCreateView, BusManageView, BusUpdateView, BusTeamCreateView, BusTeamManageView, BusTeamUpdateView,\
|
||||
WEIRegister1AView, WEIRegister2AView, WEIUpdateRegistrationView, WEIValidateRegistrationView
|
||||
WEIRegister1AView, WEIRegister2AView, WEIUpdateRegistrationView, WEIValidateRegistrationView, WEISurveyView
|
||||
|
||||
|
||||
app_name = 'wei'
|
||||
@ -27,4 +27,5 @@ urlpatterns = [
|
||||
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('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.views.generic import DetailView, UpdateView, CreateView, RedirectView
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.views.generic.edit import BaseFormView
|
||||
from django_tables2 import SingleTableView
|
||||
from member.models import Membership, Club
|
||||
from note.models import Transaction, NoteClub
|
||||
@ -17,7 +18,7 @@ from permission.backends import PermissionBackend
|
||||
from permission.views import ProtectQuerysetMixin
|
||||
|
||||
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
|
||||
|
||||
|
||||
@ -302,8 +303,7 @@ class WEIRegister1AView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
|
||||
|
||||
def get_success_url(self):
|
||||
self.object.refresh_from_db()
|
||||
# TODO Replace it with the link of the survey
|
||||
return reverse_lazy("wei:wei_detail", kwargs={"pk": self.object.wei.pk})
|
||||
return reverse_lazy("wei:wei_survey", kwargs={"pk": self.object.pk})
|
||||
|
||||
|
||||
class WEIRegister2AView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
|
||||
@ -342,7 +342,7 @@ class WEIRegister2AView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
|
||||
|
||||
def get_success_url(self):
|
||||
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):
|
||||
@ -447,3 +447,36 @@ class WEIValidateRegistrationView(ProtectQuerysetMixin, LoginRequiredMixin, Crea
|
||||
def get_success_url(self):
|
||||
self.object.refresh_from_db()
|
||||
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")]
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# Static forms (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/2.2/howto/static-files/
|
||||
|
||||
# Absolute path to the directory static files should be collected to.
|
||||
# Don't put anything in this directory yourself; store your static files
|
||||
# Absolute path to the directory static forms should be collected to.
|
||||
# Don't put anything in this directory yourself; store your static forms
|
||||
# in apps' "static/" subdirectories and in STATICFILES_DIRS.
|
||||
# Example: "/var/www/example.com/static/"
|
||||
STATIC_ROOT = os.path.join(BASE_DIR, "static/")
|
||||
@ -181,7 +181,7 @@ STATIC_ROOT = os.path.join(BASE_DIR, "static/")
|
||||
STATICFILES_DIRS = []
|
||||
CRISPY_TEMPLATE_PACK = 'bootstrap4'
|
||||
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/"
|
||||
STATIC_URL = '/static/'
|
||||
|
||||
|
2
static/admin/js/vendor/jquery/jquery.js
vendored
2
static/admin/js/vendor/jquery/jquery.js
vendored
@ -10310,7 +10310,7 @@ jQuery.isNumeric = function( obj ) {
|
||||
|
||||
|
||||
// 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
|
||||
// way to register. Lowercase jquery is used because AMD module names are
|
||||
// derived from file names, and jQuery is normally delivered in a lowercase
|
||||
|
@ -23,7 +23,7 @@
|
||||
var S2 =
|
||||
(function () {
|
||||
// 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) {
|
||||
var S2 = jQuery.fn.select2.amd;
|
||||
}
|
||||
@ -4774,7 +4774,7 @@ S2.define('select2/defaults',[
|
||||
} catch (ex) {
|
||||
// The translation could not be loaded at all. Sometimes this is
|
||||
// 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) {
|
||||
console.warn(
|
||||
'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
|
||||
// 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;
|
||||
|
||||
// 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
29
templates/wei/survey.html
Normal 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 %}
|
Loading…
Reference in New Issue
Block a user