mirror of
				https://gitlab.crans.org/bde/nk20
				synced 2025-11-04 09:12:11 +01:00 
			
		
		
		
	Compare commits
	
		
			6 Commits
		
	
	
		
			round_tran
			...
			0962a3735e
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					0962a3735e | ||
| 
						 | 
					9907cfbd86 | ||
| 
						 | 
					ad90887691 | ||
| 
						 | 
					47d2476b51 | ||
| 
						 | 
					5d8720cf46 | ||
| 
						 | 
					8700144dea | 
@@ -1,4 +1,4 @@
 | 
			
		||||
{% extends "base.html" %}
 | 
			
		||||
{% extends "base_search.html" %}
 | 
			
		||||
{% comment %}
 | 
			
		||||
SPDX-License-Identifier: GPL-3.0-or-later
 | 
			
		||||
{% endcomment %}
 | 
			
		||||
@@ -44,6 +44,8 @@ SPDX-License-Identifier: GPL-3.0-or-later
 | 
			
		||||
    <h3 class="card-header text-center">
 | 
			
		||||
        {% trans "All activities" %}
 | 
			
		||||
    </h3>
 | 
			
		||||
    {% render_table table %}
 | 
			
		||||
    {% render_table all %}
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
{{ block.super }}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
{% comment %}
 | 
			
		||||
SPDX-License-Identifier: GPL-3.0-or-later
 | 
			
		||||
{% endcomment %}
 | 
			
		||||
{% load i18n perms pretty_money %}
 | 
			
		||||
{% load i18n perms pretty_money dict_get %}
 | 
			
		||||
{% url 'activity:activity_detail' activity.pk as activity_detail_url %}
 | 
			
		||||
 | 
			
		||||
<div id="activity_info" class="card bg-light shadow mb-3">
 | 
			
		||||
@@ -53,6 +53,12 @@ SPDX-License-Identifier: GPL-3.0-or-later
 | 
			
		||||
            <dt class="col-xl-6">{% trans 'opened'|capfirst %}</dt>
 | 
			
		||||
            <dd class="col-xl-6">{{ activity.open|yesno }}</dd>
 | 
			
		||||
        </dl>
 | 
			
		||||
        {% if show_entries|dict_get:activity %}
 | 
			
		||||
            <h2 class="text-center">
 | 
			
		||||
                {{ entries_count|dict_get:activity }}
 | 
			
		||||
                {% if entries_count|dict_get:activity >= 2 %}{% trans "entries" %}{% else %}{% trans "entry" %}{% endif %}
 | 
			
		||||
            </h2>
 | 
			
		||||
        {% endif %}
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div class="card-footer text-center">
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										0
									
								
								apps/activity/templatetags/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								apps/activity/templatetags/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										12
									
								
								apps/activity/templatetags/dict_get.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								apps/activity/templatetags/dict_get.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay
 | 
			
		||||
# SPDX-License-Identifier: GPL-3.0-or-later
 | 
			
		||||
 | 
			
		||||
from django import template
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def dict_get(d, key):
 | 
			
		||||
    return d.get(key)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
register = template.Library()
 | 
			
		||||
register.filter('dict_get', dict_get)
 | 
			
		||||
@@ -67,32 +67,65 @@ class ActivityListView(ProtectQuerysetMixin, LoginRequiredMixin, MultiTableMixin
 | 
			
		||||
    tables = [
 | 
			
		||||
        lambda data: ActivityTable(data, prefix="all-"),
 | 
			
		||||
        lambda data: ActivityTable(data, prefix="upcoming-"),
 | 
			
		||||
        lambda data: ActivityTable(data, prefix="search-"),
 | 
			
		||||
    ]
 | 
			
		||||
    extra_context = {"title": _("Activities")}
 | 
			
		||||
 | 
			
		||||
    def get_queryset(self, **kwargs):
 | 
			
		||||
        return super().get_queryset(**kwargs).distinct()
 | 
			
		||||
        """
 | 
			
		||||
        Filter the user list with the given pattern.
 | 
			
		||||
        """
 | 
			
		||||
        return super().get_queryset().distinct()
 | 
			
		||||
 | 
			
		||||
    def get_tables_data(self):
 | 
			
		||||
        # first table = all activities, second table = upcoming
 | 
			
		||||
        # first table = all activities, second table = upcoming, third table = search
 | 
			
		||||
 | 
			
		||||
        # table search
 | 
			
		||||
        qs = self.get_queryset().order_by('-date_start')
 | 
			
		||||
        if "search" in self.request.GET and self.request.GET['search']:
 | 
			
		||||
            pattern = self.request.GET['search']
 | 
			
		||||
 | 
			
		||||
            # check regex
 | 
			
		||||
            valid_regex = is_regex(pattern)
 | 
			
		||||
            suffix = '__iregex' if valid_regex else '__istartswith'
 | 
			
		||||
            prefix = '^' if valid_regex else ''
 | 
			
		||||
            qs = qs.filter(Q(**{f'name{suffix}': prefix + pattern})
 | 
			
		||||
                           | Q(**{f'organizer__name{suffix}': prefix + pattern})
 | 
			
		||||
                           | Q(**{f'organizer__note__alias__name{suffix}': prefix + pattern}))
 | 
			
		||||
        else:
 | 
			
		||||
            qs = qs.none()
 | 
			
		||||
        search_table = qs.filter(PermissionBackend.filter_queryset(self.request, Activity, 'view'))
 | 
			
		||||
 | 
			
		||||
        return [
 | 
			
		||||
            self.get_queryset().order_by("-date_start"),
 | 
			
		||||
            Activity.objects.filter(date_end__gt=timezone.now())
 | 
			
		||||
                            .filter(PermissionBackend.filter_queryset(self.request, Activity, "view"))
 | 
			
		||||
                            .distinct()
 | 
			
		||||
                            .order_by("date_start")
 | 
			
		||||
                            .order_by("date_start"),
 | 
			
		||||
            search_table,
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
    def get_context_data(self, **kwargs):
 | 
			
		||||
        context = super().get_context_data(**kwargs)
 | 
			
		||||
 | 
			
		||||
        tables = context["tables"]
 | 
			
		||||
        for name, table in zip(["table", "upcoming"], tables):
 | 
			
		||||
        for name, table in zip(["all", "upcoming", "table"], tables):
 | 
			
		||||
            context[name] = table
 | 
			
		||||
 | 
			
		||||
        started_activities = self.get_queryset().filter(open=True, valid=True).distinct().all()
 | 
			
		||||
        context["started_activities"] = started_activities
 | 
			
		||||
 | 
			
		||||
        entries_count = {}
 | 
			
		||||
        show_entries = {}
 | 
			
		||||
        for activity in started_activities:
 | 
			
		||||
            if activity.activity_type.manage_entries:
 | 
			
		||||
                entries = Entry.objects.filter(activity=activity)
 | 
			
		||||
                entries_count[activity] = entries.count()
 | 
			
		||||
 | 
			
		||||
                show_entries[activity] = True
 | 
			
		||||
        context["entries_count"] = entries_count
 | 
			
		||||
        context["show_entries"] = show_entries
 | 
			
		||||
 | 
			
		||||
        return context
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -137,6 +170,14 @@ class ActivityDetailView(ProtectQuerysetMixin, LoginRequiredMixin, MultiTableMix
 | 
			
		||||
                "placeholder": ""
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if self.object.activity_type.manage_entries:
 | 
			
		||||
            entries = Entry.objects.filter(activity=self.object)
 | 
			
		||||
            context["entries_count"] = {self.object: entries.count()}
 | 
			
		||||
 | 
			
		||||
            context["show_entries"] = {self.object: timezone.now() > timezone.localtime(self.object.date_start)}
 | 
			
		||||
        else:
 | 
			
		||||
            context["entries_count"] = {self.object: 0}
 | 
			
		||||
            context["show_entries"] = {self.object: False}
 | 
			
		||||
 | 
			
		||||
        return context
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,7 @@
 | 
			
		||||
# SPDX-License-Identifier: GPL-3.0-or-later
 | 
			
		||||
 | 
			
		||||
import django_tables2 as tables
 | 
			
		||||
from django.utils.translation import gettext_lazy as _
 | 
			
		||||
 | 
			
		||||
from .models import Food
 | 
			
		||||
 | 
			
		||||
@@ -10,10 +11,25 @@ class FoodTable(tables.Table):
 | 
			
		||||
    """
 | 
			
		||||
    List all foods.
 | 
			
		||||
    """
 | 
			
		||||
    qr_code_numbers = tables.Column(empty_values=(), verbose_name=_("QR Codes"), orderable=False)
 | 
			
		||||
 | 
			
		||||
    date = tables.Column(empty_values=(), verbose_name=_("Arrival/creation date"), orderable=False)
 | 
			
		||||
 | 
			
		||||
    def render_date(self, record):
 | 
			
		||||
        if record.__class__.__name__ == "BasicFood":
 | 
			
		||||
            return record.arrival_date.strftime("%d/%m/%Y %H:%M")
 | 
			
		||||
        elif record.__class__.__name__ == "TransformedFood":
 | 
			
		||||
            return record.creation_date.strftime("%d/%m/%Y %H:%M")
 | 
			
		||||
        else:
 | 
			
		||||
            return "--"
 | 
			
		||||
 | 
			
		||||
    def render_qr_code_numbers(self, record):
 | 
			
		||||
        return ", ".join(str(q.qr_code_number) for q in record.QR_code.all())
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        model = Food
 | 
			
		||||
        template_name = 'django_tables2/bootstrap4.html'
 | 
			
		||||
        fields = ('name', 'owner', 'allergens', 'expiry_date')
 | 
			
		||||
        fields = ('name', 'owner', 'qr_code_numbers', 'allergens', 'date', 'expiry_date')
 | 
			
		||||
        row_attrs = {
 | 
			
		||||
            'class': 'table-row',
 | 
			
		||||
            'data-href': lambda record: 'detail/' + str(record.pk),
 | 
			
		||||
 
 | 
			
		||||
@@ -34,6 +34,12 @@ SPDX-License-Identifier: GPL-3.0-or-later
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
  <div class="card-body">
 | 
			
		||||
    <div class="form-check">
 | 
			
		||||
      <label for="stock_only" class="form-check-label">
 | 
			
		||||
          <input id="stock_only" name="stock_only" type="checkbox" class="checkboxinput form-check-input" checked>
 | 
			
		||||
          {% trans "Filter with only food in stock" %}
 | 
			
		||||
      </label>
 | 
			
		||||
    </div>
 | 
			
		||||
      <input id="searchbar" type="text" class="form-control"
 | 
			
		||||
          placeholder="{% trans "Search by attribute such as name..." %}">
 | 
			
		||||
  </div>
 | 
			
		||||
@@ -114,7 +120,26 @@ SPDX-License-Identifier: GPL-3.0-or-later
 | 
			
		||||
  {% endif %}
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<script type="text/javascript">
 | 
			
		||||
 | 
			
		||||
    let old_pattern = null;
 | 
			
		||||
    let searchbar_obj = $("#searchbar");
 | 
			
		||||
    let stock_only_obj = $("#stock_only");
 | 
			
		||||
 | 
			
		||||
    function reloadTable() {
 | 
			
		||||
        let pattern = searchbar_obj.val();
 | 
			
		||||
 | 
			
		||||
        $("#dynamic-table").load(location.pathname + "?search=" + pattern.replace(" ", "%20") + (
 | 
			
		||||
            stock_only_obj.is(':checked') ? "" : "&stock=1") + " #dynamic-table");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    searchbar_obj.keyup(reloadTable);
 | 
			
		||||
    stock_only_obj.change(reloadTable);
 | 
			
		||||
 | 
			
		||||
    $(document).on("click", ".table-row", function () {
 | 
			
		||||
        window.document.location = $(this).data("href");
 | 
			
		||||
    });
 | 
			
		||||
</script>
 | 
			
		||||
<script>
 | 
			
		||||
  document.addEventListener('DOMContentLoaded', function() {
 | 
			
		||||
      document.getElementById('goButton').addEventListener('click', function(event) {
 | 
			
		||||
 
 | 
			
		||||
@@ -65,9 +65,13 @@ class FoodListView(ProtectQuerysetMixin, LoginRequiredMixin, MultiTableMixin, Li
 | 
			
		||||
            suffix = '__iregex' if valid_regex else '__istartswith'
 | 
			
		||||
            prefix = '^' if valid_regex else ''
 | 
			
		||||
            qs = qs.filter(Q(**{f'name{suffix}': prefix + pattern})
 | 
			
		||||
                           | Q(**{f'owner__name{suffix}': prefix + pattern}))
 | 
			
		||||
                           | Q(**{f'owner__name{suffix}': prefix + pattern})
 | 
			
		||||
                           | Q(**{f'owner__note__alias__name{suffix}': prefix + pattern}))
 | 
			
		||||
        else:
 | 
			
		||||
            qs = qs.none()
 | 
			
		||||
        if "stock" not in self.request.GET or not self.request.GET["stock"] == '1':
 | 
			
		||||
            qs = qs.filter(end_of_life='')
 | 
			
		||||
 | 
			
		||||
        search_table = qs.filter(PermissionBackend.filter_queryset(self.request, Food, 'view'))
 | 
			
		||||
        # table open
 | 
			
		||||
        open_table = self.get_queryset().order_by('expiry_date').filter(
 | 
			
		||||
@@ -95,6 +99,7 @@ class FoodListView(ProtectQuerysetMixin, LoginRequiredMixin, MultiTableMixin, Li
 | 
			
		||||
                owner=club, end_of_life='').filter(
 | 
			
		||||
                    PermissionBackend.filter_queryset(self.request, Food, 'view')
 | 
			
		||||
            ))
 | 
			
		||||
 | 
			
		||||
        return [search_table, open_table, served_table] + club_table
 | 
			
		||||
 | 
			
		||||
    def get_context_data(self, **kwargs):
 | 
			
		||||
@@ -218,7 +223,7 @@ class BasicFoodCreateView(ProtectQuerysetMixin, ProtectedCreateView):
 | 
			
		||||
        copy = self.request.GET.get('copy', None)
 | 
			
		||||
        if copy is not None:
 | 
			
		||||
            food = BasicFood.objects.get(pk=copy)
 | 
			
		||||
            print(context['form'].fields)
 | 
			
		||||
 | 
			
		||||
            for field in context['form'].fields:
 | 
			
		||||
                if field == 'allergens':
 | 
			
		||||
                    context['form'].fields[field].initial = getattr(food, field).all()
 | 
			
		||||
 
 | 
			
		||||
@@ -29,8 +29,8 @@ SPDX-License-Identifier: GPL-3.0-or-later
 | 
			
		||||
    const input = document.querySelector("input[name='phone_number']");
 | 
			
		||||
    const form = document.querySelector("#profile-form");
 | 
			
		||||
 | 
			
		||||
    if (!input || !form) {
 | 
			
		||||
        console.error("Input phone_number ou form introuvable.");
 | 
			
		||||
    if (!input || !form || input.type === "hidden" || input.disabled || input.readOnly) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const iti = window.intlTelInput(input, {
 | 
			
		||||
 
 | 
			
		||||
@@ -66,6 +66,8 @@ $(document).ready(function () {
 | 
			
		||||
      arr.push(last)
 | 
			
		||||
 | 
			
		||||
      last.quantity = 1
 | 
			
		||||
      
 | 
			
		||||
      
 | 
			
		||||
 | 
			
		||||
      if (last.note.club) {
 | 
			
		||||
        $('#last_name').val(last.note.name)
 | 
			
		||||
@@ -111,7 +113,8 @@ $(document).ready(function () {
 | 
			
		||||
    dest.removeClass('d-none')
 | 
			
		||||
    $('#dest_note_list').removeClass('d-none')
 | 
			
		||||
    $('#debit_type').addClass('d-none')
 | 
			
		||||
 | 
			
		||||
    $('#reason').val('')
 | 
			
		||||
  
 | 
			
		||||
    $('#source_note_label').text(select_emitters_label)
 | 
			
		||||
    $('#dest_note_label').text(select_receveirs_label)
 | 
			
		||||
 | 
			
		||||
@@ -134,6 +137,7 @@ $(document).ready(function () {
 | 
			
		||||
    dest.val('')
 | 
			
		||||
    dest.tooltip('hide')
 | 
			
		||||
    $('#debit_type').addClass('d-none')
 | 
			
		||||
    $('#reason').val('Rechargement note')
 | 
			
		||||
 | 
			
		||||
    $('#source_note_label').text(transfer_type_label)
 | 
			
		||||
    $('#dest_note_label').text(select_receveir_label)
 | 
			
		||||
@@ -162,6 +166,7 @@ $(document).ready(function () {
 | 
			
		||||
    dest.addClass('d-none')
 | 
			
		||||
    dest.tooltip('hide')
 | 
			
		||||
    $('#debit_type').removeClass('d-none')
 | 
			
		||||
    $('#reason').val('')
 | 
			
		||||
 | 
			
		||||
    $('#source_note_label').text(select_emitter_label)
 | 
			
		||||
    $('#dest_note_label').text(transfer_type_label)
 | 
			
		||||
 
 | 
			
		||||
@@ -4430,6 +4430,22 @@
 | 
			
		||||
            "description": "Modifier le type de caution de mon inscription WEI tant qu'elle n'est pas validée"
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "model": "permission.permission",
 | 
			
		||||
        "pk": 298,
 | 
			
		||||
        "fields": {
 | 
			
		||||
            "model": [
 | 
			
		||||
                "wei",
 | 
			
		||||
                "bus"
 | 
			
		||||
            ],
 | 
			
		||||
            "query": "{\"pk\": [\"membership\", \"weimembership\", \"bus\", \"pk\"], \"wei__date_end__gte\": [\"today\"]}",
 | 
			
		||||
            "type": "change",
 | 
			
		||||
            "mask": 2,
 | 
			
		||||
            "field": "information_json",
 | 
			
		||||
            "permanent": false,
 | 
			
		||||
            "description": "Modifier les informations du bus"
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "model": "permission.permission",
 | 
			
		||||
        "pk": 311,
 | 
			
		||||
@@ -4833,7 +4849,10 @@
 | 
			
		||||
                221,
 | 
			
		||||
                247,
 | 
			
		||||
                258,
 | 
			
		||||
                259
 | 
			
		||||
                259,
 | 
			
		||||
                260,
 | 
			
		||||
                263,
 | 
			
		||||
                265
 | 
			
		||||
            ]
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
@@ -4845,7 +4864,6 @@
 | 
			
		||||
            "name": "Pr\u00e9sident\u22c5e de club",
 | 
			
		||||
            "permissions": [
 | 
			
		||||
                62,
 | 
			
		||||
                135,
 | 
			
		||||
                142
 | 
			
		||||
            ]
 | 
			
		||||
        }
 | 
			
		||||
@@ -5122,7 +5140,8 @@
 | 
			
		||||
                289,
 | 
			
		||||
                290,
 | 
			
		||||
                291,
 | 
			
		||||
                293
 | 
			
		||||
                293,
 | 
			
		||||
                298
 | 
			
		||||
            ]
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
@@ -5233,7 +5252,9 @@
 | 
			
		||||
                168,
 | 
			
		||||
                176,
 | 
			
		||||
                177,
 | 
			
		||||
                197
 | 
			
		||||
                197,
 | 
			
		||||
                311,
 | 
			
		||||
                319
 | 
			
		||||
            ]
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
@@ -5313,7 +5334,8 @@
 | 
			
		||||
                289,
 | 
			
		||||
                290,
 | 
			
		||||
                291,
 | 
			
		||||
                293
 | 
			
		||||
                293,
 | 
			
		||||
                298
 | 
			
		||||
            ]
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 
 | 
			
		||||
@@ -28,8 +28,8 @@ SPDX-License-Identifier: GPL-3.0-or-later
 | 
			
		||||
    const input = document.querySelector("input[name='emergency_contact_phone']");
 | 
			
		||||
    const form = document.querySelector("#registration-form");
 | 
			
		||||
 | 
			
		||||
    if (!input || !form) {
 | 
			
		||||
        console.error("Input phone_number ou form introuvable.");
 | 
			
		||||
    if (!input || !form || input.type === "hidden" || input.disabled || input.readOnly) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const iti = window.intlTelInput(input, {
 | 
			
		||||
 
 | 
			
		||||
@@ -39,8 +39,8 @@ SPDX-License-Identifier: GPL-3.0-or-later
 | 
			
		||||
    const input = document.querySelector("input[name='phone_number']");
 | 
			
		||||
    const form = document.querySelector("#profile_form");
 | 
			
		||||
 | 
			
		||||
    if (!input || !form) {
 | 
			
		||||
        console.error("Input phone_number ou form introuvable.");
 | 
			
		||||
    if (!input || !form || input.type === "hidden" || input.disabled || input.readOnly) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const iti = window.intlTelInput(input, {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user