mirror of https://gitlab.crans.org/bde/nk20
Improve survey, comment code
This commit is contained in:
parent
473d3c3546
commit
16af9ac0ea
|
@ -4,7 +4,7 @@ from datetime import timedelta, datetime
|
|||
|
||||
from django import forms
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.utils.translation import gettext as _
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from member.models import Club
|
||||
from note.models import NoteUser, Note
|
||||
from note_kfet.inputs import DateTimePickerInput, Autocomplete
|
||||
|
|
|
@ -46,6 +46,7 @@ class UserCreateView(CreateView):
|
|||
del wei_form.fields["user"]
|
||||
del wei_form.fields["caution_check"]
|
||||
del wei_form.fields["first_year"]
|
||||
del wei_form.fields["information_json"]
|
||||
context["wei_form"] = wei_form
|
||||
context["wei_registration_form"] = WEISignupForm()
|
||||
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
|
||||
from django.contrib import admin
|
||||
|
||||
from .models import WEIClub, WEIRegistration, WEIRole, Bus, BusTeam
|
||||
|
||||
from .models import WEIClub, WEIRegistration, WEIMembership, WEIRole, Bus, BusTeam
|
||||
|
||||
admin.site.register(WEIClub)
|
||||
admin.site.register(WEIRegistration)
|
||||
admin.site.register(WEIMembership)
|
||||
admin.site.register(WEIRole)
|
||||
admin.site.register(Bus)
|
||||
admin.site.register(BusTeam)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from django_filters.rest_framework import DjangoFilterBackend
|
||||
from rest_framework.filters import SearchFilter
|
||||
from api.viewsets import ReadProtectedModelViewSet
|
||||
|
||||
|
@ -16,8 +16,9 @@ class WEIClubViewSet(ReadProtectedModelViewSet):
|
|||
"""
|
||||
queryset = WEIClub.objects.all()
|
||||
serializer_class = WEIClubSerializer
|
||||
filter_backends = [SearchFilter]
|
||||
filter_backends = [SearchFilter, DjangoFilterBackend]
|
||||
search_fields = ['$name', ]
|
||||
filterset_fields = ['name', 'year', ]
|
||||
|
||||
|
||||
class BusViewSet(ReadProtectedModelViewSet):
|
||||
|
@ -28,8 +29,9 @@ class BusViewSet(ReadProtectedModelViewSet):
|
|||
"""
|
||||
queryset = Bus.objects.all()
|
||||
serializer_class = BusSerializer
|
||||
filter_backends = [SearchFilter]
|
||||
filter_backends = [SearchFilter, DjangoFilterBackend]
|
||||
search_fields = ['$name', ]
|
||||
filterset_fields = ['name', 'wei', ]
|
||||
|
||||
|
||||
class BusTeamViewSet(ReadProtectedModelViewSet):
|
||||
|
@ -40,5 +42,6 @@ class BusTeamViewSet(ReadProtectedModelViewSet):
|
|||
"""
|
||||
queryset = BusTeam.objects.all()
|
||||
serializer_class = BusTeamSerializer
|
||||
filter_backends = [SearchFilter]
|
||||
filter_backends = [SearchFilter, DjangoFilterBackend]
|
||||
search_fields = ['$name', ]
|
||||
filterset_fields = ['name', 'bus', 'wei', ]
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from .registration import *
|
||||
from .surveys import *
|
||||
from .registration import WEIForm, WEIRegistrationForm, WEIMembershipForm, BusForm, BusTeamForm
|
||||
from .surveys import WEISurvey, WEISurveyInformation, WEISurveyAlgorithm, CurrentSurvey
|
||||
|
||||
__all__ = [
|
||||
'WEIForm', 'WEIRegistrationForm', 'WEIMembershipForm', 'BusForm', 'BusTeamForm',
|
||||
|
|
|
@ -3,9 +3,10 @@
|
|||
|
||||
from django import forms
|
||||
from django.contrib.auth.models import User
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from note_kfet.inputs import AmountInput, DatePickerInput, Autocomplete, ColorWidget
|
||||
|
||||
from wei.models import WEIClub, WEIRegistration, Bus, BusTeam, WEIMembership, WEIRole
|
||||
from ..models import WEIClub, WEIRegistration, Bus, BusTeam, WEIMembership, WEIRole
|
||||
|
||||
|
||||
class WEIForm(forms.ModelForm):
|
||||
|
@ -25,7 +26,7 @@ class WEIForm(forms.ModelForm):
|
|||
class WEIRegistrationForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = WEIRegistration
|
||||
exclude = ('wei', 'information_json', )
|
||||
exclude = ('wei', )
|
||||
widgets = {
|
||||
"user": Autocomplete(
|
||||
User,
|
||||
|
@ -42,6 +43,12 @@ class WEIRegistrationForm(forms.ModelForm):
|
|||
class WEIMembershipForm(forms.ModelForm):
|
||||
roles = forms.ModelMultipleChoiceField(queryset=WEIRole.objects)
|
||||
|
||||
def clean(self):
|
||||
cleaned_data = super().clean()
|
||||
if cleaned_data["team"] is not None and cleaned_data["team"].bus != cleaned_data["bus"]:
|
||||
self.add_error('bus', _("This team doesn't belong to the given team."))
|
||||
return cleaned_data
|
||||
|
||||
class Meta:
|
||||
model = WEIMembership
|
||||
fields = ('roles', 'bus', 'team',)
|
||||
|
|
|
@ -1,46 +1,18 @@
|
|||
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from django.db.models import QuerySet
|
||||
from django.forms import Form
|
||||
|
||||
from ...models import WEIClub, WEIRegistration, Bus
|
||||
|
||||
|
||||
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):
|
||||
self.information.selected_bus_pk = bus.pk
|
||||
self.information.selected_bus_name = bus.name
|
||||
self.information.valid = True
|
||||
|
||||
|
||||
class WEISurveyInformation:
|
||||
"""
|
||||
Abstract data of the survey.
|
||||
"""
|
||||
valid = False
|
||||
selected_bus_pk = None
|
||||
selected_bus_name = None
|
||||
|
@ -48,22 +20,142 @@ class WEISurveyInformation:
|
|||
def __init__(self, registration):
|
||||
self.__dict__.update(registration.information)
|
||||
|
||||
def get_selected_bus(self):
|
||||
def get_selected_bus(self) -> Optional[Bus]:
|
||||
"""
|
||||
If the algorithm ran, return the prefered bus according to the survey.
|
||||
In the other case, return None.
|
||||
"""
|
||||
if not self.valid:
|
||||
return None
|
||||
return Bus.objects.get(pk=self.selected_bus_pk)
|
||||
|
||||
def save(self, registration):
|
||||
def save(self, registration) -> None:
|
||||
"""
|
||||
Store the data of the survey into the database, with the information of the registration.
|
||||
"""
|
||||
registration.information = self.__dict__
|
||||
registration.save()
|
||||
|
||||
|
||||
class WEISurveyAlgorithm:
|
||||
def get_survey_class(self):
|
||||
"""
|
||||
Abstract algorithm that attributes a bus to each new member.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def get_survey_class(cls):
|
||||
"""
|
||||
The class of the survey associated with this algorithm.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def get_registrations(self):
|
||||
return WEIRegistration.objects.filter(wei__year=self.get_survey_class().year, first_year=True).all()
|
||||
@classmethod
|
||||
def get_registrations(cls) -> QuerySet:
|
||||
"""
|
||||
Queryset of all first year registrations
|
||||
"""
|
||||
return WEIRegistration.objects.filter(wei__year=cls.get_survey_class().get_year(), first_year=True)
|
||||
|
||||
def run_algorithm(self):
|
||||
@classmethod
|
||||
def get_buses(cls) -> QuerySet:
|
||||
"""
|
||||
Queryset of all buses of the associated wei.
|
||||
"""
|
||||
return Bus.objects.filter(wei__year=cls.get_survey_class().get_year())
|
||||
|
||||
def run_algorithm(self) -> None:
|
||||
"""
|
||||
Once this method implemented, run the algorithm that attributes a bus to each first year member.
|
||||
This method can be run in command line through ``python manage.py wei_algorithm``
|
||||
See ``wei.management.commmands.wei_algorithm``
|
||||
This method must call Survey.select_bus for each survey.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class WEISurvey:
|
||||
"""
|
||||
Survey associated to a first year WEI registration.
|
||||
The data is stored into WEISurveyInformation, this class acts as a manager.
|
||||
This is an abstract class: this has to be extended each year to implement custom methods.
|
||||
"""
|
||||
|
||||
def __init__(self, registration: WEIRegistration):
|
||||
self.registration = registration
|
||||
self.information = self.get_survey_information_class()(registration)
|
||||
|
||||
@classmethod
|
||||
def get_year(cls) -> int:
|
||||
"""
|
||||
Get year of the wei concerned by the type of the survey.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
@classmethod
|
||||
def get_wei(cls) -> WEIClub:
|
||||
"""
|
||||
The WEI associated to this kind of survey.
|
||||
"""
|
||||
return WEIClub.objects.get(year=cls.get_year())
|
||||
|
||||
@classmethod
|
||||
def get_survey_information_class(cls):
|
||||
"""
|
||||
The class of the data (extending WEISurveyInformation).
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def get_form_class(self) -> Form:
|
||||
"""
|
||||
The form class of the survey.
|
||||
This is proper to the status of the survey: the form class can evolve according to the progress of the survey.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def update_form(self, form) -> None:
|
||||
"""
|
||||
Once the form is instanciated, the information can be updated with the information of the registration
|
||||
and the information of the survey.
|
||||
This method is called once the form is created.
|
||||
"""
|
||||
pass
|
||||
|
||||
def form_valid(self, form) -> None:
|
||||
"""
|
||||
Called when the information of the form are validated.
|
||||
This method should update the information of the survey.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def is_complete(self) -> bool:
|
||||
"""
|
||||
Return True if the survey is complete.
|
||||
If the survey is complete, then the button "Next" will display some text for the end of the survey.
|
||||
If not, the survey is reloaded and continues.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def save(self) -> None:
|
||||
"""
|
||||
Store the information of the survey into the database.
|
||||
"""
|
||||
self.information.save(self.registration)
|
||||
self.registration.save()
|
||||
|
||||
@classmethod
|
||||
def get_algorithm_class(cls):
|
||||
"""
|
||||
Algorithm class associated to the survey.
|
||||
The algorithm, extending WEISurveyAlgorithm, should associate a bus to each first year member.
|
||||
The association is not permanent: that's only a suggestion.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def select_bus(self, bus) -> None:
|
||||
"""
|
||||
Set the suggestion into the data of the membership.
|
||||
:param bus: The bus suggested.
|
||||
"""
|
||||
self.information.selected_bus_pk = bus.pk
|
||||
self.information.selected_bus_name = bus.name
|
||||
self.information.valid = True
|
||||
|
|
|
@ -8,29 +8,50 @@ from ...models import Bus
|
|||
|
||||
|
||||
class WEISurveyForm2020(forms.Form):
|
||||
"""
|
||||
Survey form for the year 2020.
|
||||
For now, that's only a Bus selector.
|
||||
TODO: Do a better survey (later)
|
||||
"""
|
||||
bus = forms.ModelChoiceField(
|
||||
Bus.objects,
|
||||
)
|
||||
|
||||
def set_registration(self, registration):
|
||||
"""
|
||||
Filter the bus selector with the buses of the current WEI.
|
||||
"""
|
||||
self.fields["bus"].queryset = Bus.objects.filter(wei=registration.wei)
|
||||
|
||||
|
||||
class WEISurveyInformation2020(WEISurveyInformation):
|
||||
"""
|
||||
We store the id of the selected bus. We store only the name, but is not used in the selection:
|
||||
that's only for humans that try to read data.
|
||||
"""
|
||||
chosen_bus_pk = None
|
||||
chosen_bus_name = None
|
||||
|
||||
|
||||
class WEISurvey2020(WEISurvey):
|
||||
year = 2020
|
||||
"""
|
||||
Survey for the year 2020.
|
||||
"""
|
||||
@classmethod
|
||||
def get_year(cls):
|
||||
return 2020
|
||||
|
||||
def get_survey_information_class(self):
|
||||
@classmethod
|
||||
def get_survey_information_class(cls):
|
||||
return WEISurveyInformation2020
|
||||
|
||||
def get_form_class(self):
|
||||
return WEISurveyForm2020
|
||||
|
||||
def update_form(self, form):
|
||||
"""
|
||||
Filter the bus selector with the buses of the WEI.
|
||||
"""
|
||||
form.set_registration(self.registration)
|
||||
|
||||
def form_valid(self, form):
|
||||
|
@ -39,13 +60,26 @@ class WEISurvey2020(WEISurvey):
|
|||
self.information.chosen_bus_name = bus.name
|
||||
self.save()
|
||||
|
||||
@staticmethod
|
||||
def get_algorithm_class():
|
||||
@classmethod
|
||||
def get_algorithm_class(cls):
|
||||
return WEISurveyAlgorithm2020
|
||||
|
||||
def is_complete(self) -> bool:
|
||||
"""
|
||||
The survey is complete once the bus is chosen.
|
||||
"""
|
||||
return self.information.chosen_bus_pk is not None
|
||||
|
||||
|
||||
class WEISurveyAlgorithm2020(WEISurveyAlgorithm):
|
||||
def get_survey_class(self):
|
||||
"""
|
||||
The algorithm class for the year 2020.
|
||||
For now, the algorithm is quite simple: the selected bus is the chosen bus.
|
||||
TODO: Improve this algorithm.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def get_survey_class(cls):
|
||||
return WEISurvey2020
|
||||
|
||||
def run_algorithm(self):
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
@ -4,7 +4,7 @@
|
|||
from django.core.management import BaseCommand
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from wei.forms import CurrentSurvey
|
||||
from ...forms import CurrentSurvey
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
|
|
|
@ -5,7 +5,8 @@ from django.urls import path
|
|||
|
||||
from .views import CurrentWEIDetailView, WEIListView, WEICreateView, WEIDetailView, WEIUpdateView,\
|
||||
BusCreateView, BusManageView, BusUpdateView, BusTeamCreateView, BusTeamManageView, BusTeamUpdateView,\
|
||||
WEIRegister1AView, WEIRegister2AView, WEIUpdateRegistrationView, WEIValidateRegistrationView, WEISurveyView
|
||||
WEIRegister1AView, WEIRegister2AView, WEIUpdateRegistrationView, WEIValidateRegistrationView,\
|
||||
WEISurveyView, WEISurveyEndView
|
||||
|
||||
|
||||
app_name = 'wei'
|
||||
|
@ -28,4 +29,5 @@ urlpatterns = [
|
|||
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"),
|
||||
path('survey/<int:pk>/end/', WEISurveyEndView.as_view(), name="wei_survey_end"),
|
||||
]
|
||||
|
|
|
@ -6,8 +6,9 @@ from datetime import datetime, date
|
|||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.contrib.auth.models import User
|
||||
from django.db.models import Q
|
||||
from django.shortcuts import redirect
|
||||
from django.urls import reverse_lazy
|
||||
from django.views.generic import DetailView, UpdateView, CreateView, RedirectView
|
||||
from django.views.generic import DetailView, UpdateView, CreateView, RedirectView, TemplateView
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.views.generic.edit import BaseFormView
|
||||
from django_tables2 import SingleTableView
|
||||
|
@ -294,6 +295,7 @@ class WEIRegister1AView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
|
|||
form.fields["user"].disabled = True
|
||||
del form.fields["first_year"]
|
||||
del form.fields["caution_check"]
|
||||
del form.fields["information_json"]
|
||||
return form
|
||||
|
||||
def form_valid(self, form):
|
||||
|
@ -332,6 +334,7 @@ class WEIRegister2AView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
|
|||
del form.fields["ml_events_registration"]
|
||||
del form.fields["ml_art_registration"]
|
||||
del form.fields["ml_sport_registration"]
|
||||
del form.fields["information_json"]
|
||||
|
||||
return form
|
||||
|
||||
|
@ -398,6 +401,7 @@ class WEIValidateRegistrationView(ProtectQuerysetMixin, LoginRequiredMixin, Crea
|
|||
def get_form(self, form_class=None):
|
||||
form = super().get_form(form_class)
|
||||
registration = WEIRegistration.objects.get(pk=self.kwargs["pk"])
|
||||
form.fields["bus"].widget.attrs["api_url"] = "/api/wei/bus/?wei=" + str(registration.wei.pk)
|
||||
if registration.first_year:
|
||||
del form.fields["roles"]
|
||||
survey = CurrentSurvey(registration)
|
||||
|
@ -457,20 +461,35 @@ class WEIValidateRegistrationView(ProtectQuerysetMixin, LoginRequiredMixin, Crea
|
|||
|
||||
|
||||
class WEISurveyView(BaseFormView, DetailView):
|
||||
"""
|
||||
Display the survey for the WEI for first
|
||||
"""
|
||||
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(self, request, *args, **kwargs):
|
||||
obj = self.get_object()
|
||||
if not self.survey:
|
||||
self.survey = CurrentSurvey(obj)
|
||||
# If the survey is complete, then display the end page.
|
||||
if self.survey.is_complete():
|
||||
return redirect(reverse_lazy('wei:wei_survey_end', args=(self.survey.registration.pk,)))
|
||||
# Non first year members don't have a survey
|
||||
if not obj.first_year:
|
||||
return redirect(reverse_lazy('wei:wei_survey_end', args=(self.survey.registration.pk,)))
|
||||
return super().get(request, *args, **kwargs)
|
||||
|
||||
def get_form_class(self):
|
||||
if not self.survey:
|
||||
self.survey = CurrentSurvey(self.get_object())
|
||||
"""
|
||||
Get the survey form. It may depend on the current state of the survey.
|
||||
"""
|
||||
return self.survey.get_form_class()
|
||||
|
||||
def get_form(self, form_class=None):
|
||||
"""
|
||||
Update the form with the data of the survey.
|
||||
"""
|
||||
form = super().get_form(form_class)
|
||||
self.survey.update_form(form)
|
||||
return form
|
||||
|
@ -482,8 +501,21 @@ class WEISurveyView(BaseFormView, DetailView):
|
|||
return context
|
||||
|
||||
def form_valid(self, form):
|
||||
"""
|
||||
Update the survey with the data of the 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,))
|
||||
|
||||
|
||||
class WEISurveyEndView(TemplateView):
|
||||
template_name = "wei/survey_end.html"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context["club"] = WEIRegistration.objects.get(pk=self.kwargs["pk"]).wei
|
||||
context["title"] = _("Survey WEI")
|
||||
return context
|
||||
|
|
|
@ -11,7 +11,7 @@ $(document).ready(function () {
|
|||
name_field = "name";
|
||||
let input = target.val();
|
||||
|
||||
$.getJSON(api_url + "?format=json&search=^" + input + api_url_suffix, function(objects) {
|
||||
$.getJSON(api_url + (api_url.includes("?") ? "&" : "?") + "format=json&search=^" + input + api_url_suffix, function(objects) {
|
||||
let html = "";
|
||||
|
||||
objects.results.forEach(function (obj) {
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
{% 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">
|
||||
<p>
|
||||
{% blocktrans %}
|
||||
The survey is now ended. Your answers have been saved.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -167,3 +167,15 @@
|
|||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block extrajavascript %}
|
||||
<script>
|
||||
function autocompleted(obj, prefix) {
|
||||
console.log(prefix);
|
||||
if (prefix === "id_bus") {
|
||||
console.log(obj);
|
||||
$("#id_team").attr('api_url', '/api/wei/team/?bus=' + obj.id);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
|
Loading…
Reference in New Issue