mirror of
				https://gitlab.crans.org/bde/nk20
				synced 2025-10-30 23:39:54 +01:00 
			
		
		
		
	WEI Survey (work in progress)
This commit is contained in:
		
							
								
								
									
										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 %} | ||||
		Reference in New Issue
	
	Block a user