mirror of
				https://gitlab.crans.org/bde/nk20
				synced 2025-10-30 23:39:54 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			401 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			401 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| # Copyright (C) 2018-2025 by BDE ENS Paris-Saclay
 | |
| # SPDX-License-Identifier: GPL-3.0-or-later
 | |
| 
 | |
| from datetime import date
 | |
| 
 | |
| import django_tables2 as tables
 | |
| from django.db.models import Q
 | |
| from django.urls import reverse_lazy
 | |
| from django.utils.html import format_html
 | |
| from django.utils.translation import gettext_lazy as _
 | |
| from django_tables2 import A
 | |
| from note_kfet.middlewares import get_current_request
 | |
| from permission.backends import PermissionBackend
 | |
| 
 | |
| from .models import WEIClub, WEIRegistration, Bus, BusTeam, WEIMembership
 | |
| 
 | |
| 
 | |
| class WEITable(tables.Table):
 | |
|     """
 | |
|     List all WEI.
 | |
|     """
 | |
|     class Meta:
 | |
|         attrs = {
 | |
|             'class': 'table table-condensed table-striped table-hover'
 | |
|         }
 | |
|         model = WEIClub
 | |
|         template_name = 'django_tables2/bootstrap4.html'
 | |
|         fields = ('name', 'year', 'date_start', 'date_end',)
 | |
|         row_attrs = {
 | |
|             'class': 'table-row',
 | |
|             'id': lambda record: "row-" + str(record.pk),
 | |
|             'data-href': lambda record: reverse_lazy('wei:wei_detail', args=(record.pk,))
 | |
|         }
 | |
| 
 | |
| 
 | |
| class WEIRegistrationTable(tables.Table):
 | |
|     """
 | |
|     List all WEI registrations.
 | |
|     """
 | |
|     user = tables.LinkColumn(
 | |
|         'member:user_detail',
 | |
|         args=[A('user__pk')],
 | |
|     )
 | |
| 
 | |
|     edit = tables.LinkColumn(
 | |
|         'wei:wei_update_registration',
 | |
|         orderable=False,
 | |
|         args=[A('pk')],
 | |
|         verbose_name=_("Edit"),
 | |
|         text=_("Edit"),
 | |
|         attrs={
 | |
|             'a': {
 | |
|                 'class': 'btn btn-warning',
 | |
|                 'data-turbolinks': 'false',
 | |
|             }
 | |
|         }
 | |
|     )
 | |
| 
 | |
|     validate = tables.Column(
 | |
|         verbose_name=_("Validate"),
 | |
|         orderable=True,
 | |
|         accessor='validate_status',
 | |
|         attrs={
 | |
|             'th': {
 | |
|                 'id': 'validate-membership-header'
 | |
|             }
 | |
|         }
 | |
|     )
 | |
| 
 | |
|     delete = tables.LinkColumn(
 | |
|         'wei:wei_delete_registration',
 | |
|         args=[A('pk')],
 | |
|         orderable=False,
 | |
|         verbose_name=_("Delete"),
 | |
|         text=_("Delete"),
 | |
|         attrs={
 | |
|             'th': {
 | |
|                 'id': 'delete-membership-header'
 | |
|             },
 | |
|             'a': {
 | |
|                 'class': 'btn btn-danger',
 | |
|                 'data-type': 'delete-membership'
 | |
|             }
 | |
|         },
 | |
|     )
 | |
| 
 | |
|     def render_deposit_type(self, record):
 | |
|         if record.first_year:
 | |
|             return format_html("∅")
 | |
|         if record.deposit_type == 'check':
 | |
|             # TODO Install Font Awesome 6 to acces more icons (and keep compaibility with current used v4)
 | |
|             return format_html("""
 | |
|         <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640" width="1.5em" height="1.5em"
 | |
|         fill="currentColor" style="position: relative; left: -0.15em;">
 | |
|             <path d="
 | |
|                 M128 128C92.7 128 64 156.7 64 192L64 448C64 483.3 92.7 512 128 512L512 512
 | |
|                 C547.3 512 576 483.3 576 448L576 192C576 156.7 547.3 128 512 128L128 128z
 | |
|                 M360 352L488 352C501.3 352 512 362.7 512 376C512 389.3 501.3 400 488 400L360 400
 | |
|                 C346.7 400 336 389.3 336 376C336 362.7 346.7 352 360 352z
 | |
|                 M336 264C336 250.7 346.7 240 360 240L488 240C501.3 240 512 250.7 512 264
 | |
|                 C512 277.3 501.3 288 488 288L360 288C346.7 288 336 277.3 336 264z
 | |
|                 M212 208C223 208 232 217 232 228L232 232L240 232C251 232 260 241 260 252
 | |
|                 C260 263 251 272 240 272L192.5 272C185.6 272 180 277.6 180 284.5
 | |
|                 C180 290.6 184.4 295.8 190.4 296.8L232.1 303.8C257.4 308 276 329.9 276 355.6
 | |
|                 C276 381.7 257 403.3 232 407.4L232 412.1C232 423.1 223 432.1 212 432.1
 | |
|                 C201 432.1 192 423.1 192 412.1L192 408.1L168 408.1C157 408.1 148 399.1 148 388.1
 | |
|                 C148 377.1 157 368.1 168 368.1L223.5 368.1C230.4 368.1 236 362.5 236 355.6
 | |
|                 C236 349.5 231.6 344.3 225.6 343.3L183.9 336.3C158.5 332 140 310.1 140 284.5
 | |
|                 C140 255.7 163.2 232.3 192 232L192 228C192 217 201 208 212 208z
 | |
|             " />
 | |
|         </svg>
 | |
|     """)
 | |
|         if record.deposit_type == 'note':
 | |
|             return format_html("<i class=\"fa fa-exchange\"></i>")
 | |
| 
 | |
|     def render_validate(self, record):
 | |
|         hasperm = PermissionBackend.check_perm(
 | |
|             get_current_request(), "wei.add_weimembership", WEIMembership(
 | |
|                 club=record.wei,
 | |
|                 user=record.user,
 | |
|                 date_start=date.today(),
 | |
|                 date_end=date.today(),
 | |
|                 fee=0,
 | |
|                 registration=record,
 | |
|             )
 | |
|         )
 | |
|         if not hasperm:
 | |
|             return format_html("<span class='no-perm'></span>")
 | |
| 
 | |
|         url = reverse_lazy('wei:validate_registration', args=(record.pk,))
 | |
|         text = _('Validate')
 | |
|         status = record.validation_status
 | |
|         if status == 2:
 | |
|             btn_class = 'btn-secondary'
 | |
|             tooltip = _("The user does not have enough money.")
 | |
|         elif status == 1:
 | |
|             btn_class = 'btn-info'
 | |
|             tooltip = _("The user is in first year. You may validate the credit, the algorithm will run later.")
 | |
|         else:
 | |
|             btn_class = 'btn-success'
 | |
|             tooltip = _("The user has enough money, you can validate the registration.")
 | |
| 
 | |
|         return format_html(f"<a class=\"btn {btn_class}\" data-type='validate-membership' data-toggle=\"tooltip\" "
 | |
|                            f"title=\"{tooltip}\" href=\"{url}\">{text}</a>")
 | |
| 
 | |
|     def render_delete(self, record):
 | |
|         hasperm = PermissionBackend.check_perm(get_current_request(), "wei.delete_weimembership", record)
 | |
|         return _("Delete") if hasperm else format_html("<span class='no-perm'></span>")
 | |
| 
 | |
|     class Meta:
 | |
|         attrs = {
 | |
|             'class': 'table table-condensed table-striped table-hover'
 | |
|         }
 | |
|         order_by = ('validate', 'user',)
 | |
|         model = WEIRegistration
 | |
|         template_name = 'django_tables2/bootstrap4.html'
 | |
|         fields = ('user', 'user__first_name', 'user__last_name', 'first_year', 'deposit_given',
 | |
|                   'deposit_type', 'edit', 'validate', 'delete',)
 | |
|         row_attrs = {
 | |
|             'class': 'table-row',
 | |
|             'id': lambda record: "row-" + str(record.pk),
 | |
|             'data-href': lambda record: record.pk
 | |
|         }
 | |
| 
 | |
| 
 | |
| class WEIMembershipTable(tables.Table):
 | |
|     user = tables.LinkColumn(
 | |
|         'wei:wei_update_membership',
 | |
|         args=[A('pk')],
 | |
|     )
 | |
| 
 | |
|     year = tables.Column(
 | |
|         accessor=A("pk"),
 | |
|         verbose_name=_("Year"),
 | |
|     )
 | |
| 
 | |
|     bus = tables.LinkColumn(
 | |
|         'wei:manage_bus',
 | |
|         args=[A('bus__pk')],
 | |
|     )
 | |
| 
 | |
|     team = tables.LinkColumn(
 | |
|         'wei:manage_bus_team',
 | |
|         args=[A('team__pk')],
 | |
|     )
 | |
| 
 | |
|     def render_year(self, record):
 | |
|         return str(record.user.profile.ens_year) + "A"
 | |
| 
 | |
|     def render_registration__deposit_type(self, record):
 | |
|         if record.registration.first_year:
 | |
|             return format_html("∅")
 | |
|         if record.registration.deposit_type == 'check':
 | |
|             # TODO Install Font Awesome 6 to acces more icons (and keep compaibility with current used v4)
 | |
|             return format_html("""
 | |
|         <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640" width="1.5em" height="1.5em"
 | |
|         fill="currentColor" style="position: relative; left: -0.15em;">
 | |
|             <path d="
 | |
|                 M128 128C92.7 128 64 156.7 64 192L64 448C64 483.3 92.7 512 128 512L512 512
 | |
|                 C547.3 512 576 483.3 576 448L576 192C576 156.7 547.3 128 512 128L128 128z
 | |
|                 M360 352L488 352C501.3 352 512 362.7 512 376C512 389.3 501.3 400 488 400L360 400
 | |
|                 C346.7 400 336 389.3 336 376C336 362.7 346.7 352 360 352z
 | |
|                 M336 264C336 250.7 346.7 240 360 240L488 240C501.3 240 512 250.7 512 264
 | |
|                 C512 277.3 501.3 288 488 288L360 288C346.7 288 336 277.3 336 264z
 | |
|                 M212 208C223 208 232 217 232 228L232 232L240 232C251 232 260 241 260 252
 | |
|                 C260 263 251 272 240 272L192.5 272C185.6 272 180 277.6 180 284.5
 | |
|                 C180 290.6 184.4 295.8 190.4 296.8L232.1 303.8C257.4 308 276 329.9 276 355.6
 | |
|                 C276 381.7 257 403.3 232 407.4L232 412.1C232 423.1 223 432.1 212 432.1
 | |
|                 C201 432.1 192 423.1 192 412.1L192 408.1L168 408.1C157 408.1 148 399.1 148 388.1
 | |
|                 C148 377.1 157 368.1 168 368.1L223.5 368.1C230.4 368.1 236 362.5 236 355.6
 | |
|                 C236 349.5 231.6 344.3 225.6 343.3L183.9 336.3C158.5 332 140 310.1 140 284.5
 | |
|                 C140 255.7 163.2 232.3 192 232L192 228C192 217 201 208 212 208z
 | |
|             " />
 | |
|         </svg>
 | |
|     """)
 | |
|         if record.registration.deposit_type == 'note':
 | |
|             return format_html("<i class=\"fa fa-exchange\"></i>")
 | |
| 
 | |
|     class Meta:
 | |
|         attrs = {
 | |
|             'class': 'table table-condensed table-striped table-hover'
 | |
|         }
 | |
|         model = WEIMembership
 | |
|         template_name = 'django_tables2/bootstrap4.html'
 | |
|         fields = ('user', 'user__last_name', 'user__first_name', 'registration__gender', 'user__profile__department',
 | |
|                   'year', 'bus', 'team', 'registration__deposit_given', 'registration__deposit_type')
 | |
|         row_attrs = {
 | |
|             'class': 'table-row',
 | |
|             'id': lambda record: "row-" + str(record.pk),
 | |
|         }
 | |
| 
 | |
| 
 | |
| class WEIRegistration1ATable(tables.Table):
 | |
|     user = tables.LinkColumn(
 | |
|         'wei:wei_bus_1A',
 | |
|         args=[A('pk')],
 | |
|     )
 | |
| 
 | |
|     preferred_bus = tables.Column(
 | |
|         verbose_name=_('preferred bus').capitalize,
 | |
|         accessor='pk',
 | |
|         orderable=False,
 | |
|     )
 | |
| 
 | |
|     def render_preferred_bus(self, record):
 | |
|         information = record.information
 | |
|         return information['selected_bus_name'] if 'selected_bus_name' in information else "—"
 | |
| 
 | |
|     class Meta:
 | |
|         attrs = {
 | |
|             'class': 'table table-condensed table-striped table-hover'
 | |
|         }
 | |
|         model = WEIRegistration
 | |
|         template_name = 'django_tables2/bootstrap4.html'
 | |
|         fields = ('user', 'user__last_name', 'user__first_name', 'gender',
 | |
|                   'user__profile__department', 'preferred_bus', 'membership__bus', )
 | |
|         row_attrs = {
 | |
|             'class': lambda record: '' if 'selected_bus_pk' in record.information else 'bg-danger',
 | |
|         }
 | |
| 
 | |
| 
 | |
| class BusTable(tables.Table):
 | |
|     name = tables.LinkColumn(
 | |
|         'wei:manage_bus',
 | |
|         args=[A('pk')],
 | |
|     )
 | |
| 
 | |
|     teams = tables.Column(
 | |
|         accessor=A("teams"),
 | |
|         verbose_name=_("Teams"),
 | |
|         attrs={
 | |
|             "td": {
 | |
|                 "class": "text-truncate",
 | |
|             }
 | |
|         }
 | |
|     )
 | |
| 
 | |
|     count = tables.Column(
 | |
|         verbose_name=_("Members count"),
 | |
|     )
 | |
| 
 | |
|     def render_teams(self, value):
 | |
|         return ", ".join(team.name for team in value.order_by('name').all())
 | |
| 
 | |
|     def render_count(self, value):
 | |
|         return str(value) + " " + (str(_("members")) if value > 1 else str(_("member")))
 | |
| 
 | |
|     class Meta:
 | |
|         attrs = {
 | |
|             'class': 'table table-condensed table-striped table-hover'
 | |
|         }
 | |
|         model = Bus
 | |
|         template_name = 'django_tables2/bootstrap4.html'
 | |
|         fields = ('name', 'teams', )
 | |
|         row_attrs = {
 | |
|             'class': 'table-row',
 | |
|             'id': lambda record: "row-" + str(record.pk),
 | |
|         }
 | |
| 
 | |
| 
 | |
| class BusTeamTable(tables.Table):
 | |
|     name = tables.LinkColumn(
 | |
|         'wei:manage_bus_team',
 | |
|         args=[A('pk')],
 | |
|     )
 | |
| 
 | |
|     color = tables.Column(
 | |
|         attrs={
 | |
|             "td": {
 | |
|                 "style": lambda record: "background-color: #{:06X}; color: #{:06X};"
 | |
|                                         .format(record.color, 0xFFFFFF - record.color, )
 | |
|             }
 | |
|         }
 | |
|     )
 | |
| 
 | |
|     def render_count(self, value):
 | |
|         return str(value) + " " + (str(_("members")) if value > 1 else str(_("member")))
 | |
| 
 | |
|     count = tables.Column(
 | |
|         verbose_name=_("Members count"),
 | |
|     )
 | |
| 
 | |
|     def render_color(self, value):
 | |
|         return "#{:06X}".format(value)
 | |
| 
 | |
|     class Meta:
 | |
|         attrs = {
 | |
|             'class': 'table table-condensed table-striped table-hover'
 | |
|         }
 | |
|         model = BusTeam
 | |
|         template_name = 'django_tables2/bootstrap4.html'
 | |
|         fields = ('name', 'color',)
 | |
|         row_attrs = {
 | |
|             'class': 'table-row',
 | |
|             'id': lambda record: "row-" + str(record.pk),
 | |
|             'data-href': lambda record: reverse_lazy('wei:manage_bus_team', args=(record.pk, ))
 | |
|         }
 | |
| 
 | |
| 
 | |
| class BusRepartitionTable(tables.Table):
 | |
|     name = tables.Column(
 | |
|         verbose_name=_("name").capitalize,
 | |
|         accessor='name',
 | |
|     )
 | |
| 
 | |
|     suggested_first_year = tables.Column(
 | |
|         verbose_name=_("suggested first year").capitalize,
 | |
|         accessor='pk',
 | |
|         orderable=False,
 | |
|     )
 | |
| 
 | |
|     validated_first_year = tables.Column(
 | |
|         verbose_name=_("validated first year").capitalize,
 | |
|         accessor='pk',
 | |
|         orderable=False,
 | |
|     )
 | |
| 
 | |
|     validated_staff = tables.Column(
 | |
|         verbose_name=_("validated staff").capitalize,
 | |
|         accessor='pk',
 | |
|         orderable=False,
 | |
|     )
 | |
| 
 | |
|     size = tables.Column(
 | |
|         verbose_name=_("seat count in the bus").capitalize,
 | |
|         accessor='size',
 | |
|     )
 | |
| 
 | |
|     free_seats = tables.Column(
 | |
|         verbose_name=_("free seats").capitalize,
 | |
|         accessor='pk',
 | |
|         orderable=False,
 | |
|     )
 | |
| 
 | |
|     def render_suggested_first_year(self, record):
 | |
|         registrations = WEIRegistration.objects.filter(Q(membership__isnull=True) | Q(membership__bus__isnull=True),
 | |
|                                                        first_year=True, wei=record.wei)
 | |
|         registrations = [r for r in registrations if 'selected_bus_pk' in r.information]
 | |
|         return sum(1 for r in registrations if r.information['selected_bus_pk'] == record.pk)
 | |
| 
 | |
|     def render_validated_first_year(self, record):
 | |
|         return WEIRegistration.objects.filter(first_year=True, membership__bus=record).count()
 | |
| 
 | |
|     def render_validated_staff(self, record):
 | |
|         return WEIRegistration.objects.filter(first_year=False, membership__bus=record).count()
 | |
| 
 | |
|     def render_free_seats(self, record):
 | |
|         return record.size - self.render_validated_staff(record) - self.render_validated_first_year(record)
 | |
| 
 | |
|     class Meta:
 | |
|         attrs = {
 | |
|             'class': 'table table-condensed table-striped table-hover'
 | |
|         }
 | |
|         models = Bus
 | |
|         template_name = 'django_tables2/bootstrap4.html'
 | |
|         fields = ('name', )
 | |
|         row_attrs = {
 | |
|             'class': 'table-row',
 | |
|             'id': lambda record: "row-" + str(record.pk),
 | |
|         }
 |