Compare commits

...
Sign in to create a new pull request.

10 commits

11 changed files with 125 additions and 10 deletions

View file

@ -1,6 +1,7 @@
from django.urls import path
from bds import views
from shared.views import SympaListView
app_name = "bds"
urlpatterns = [
@ -21,4 +22,10 @@ urlpatterns = [
name="members.expired",
),
path("members/reset", views.ResetMembershipView.as_view(), name="members.reset"),
# Sympa export view
path(
"sympa/members/",
SympaListView.as_view(filters={"bds__is_member": True}),
name="export.sympa",
),
]

View file

@ -27,6 +27,9 @@ ALLOWED_HOSTS = []
DEBUG = True
EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
SYMPA_PASSWORD = b"sympa"
SYMPA_USERNAME = b"sympa"
if TESTING:
PASSWORD_HASHERS = ["django.contrib.auth.hashers.MD5PasswordHasher"]

View file

@ -26,6 +26,9 @@ EMAIL_HOST = credentials.get("EMAIL_HOST")
DEFAULT_AUTO_FIELD = "django.db.models.AutoField"
SYMPA_PASSWORD = credentials["SYMPA_PASSWORD"].encode()
SYMPA_USERNAME = credentials["SYMPA_USERNAME"].encode()
##
# Installed Apps configuration

View file

@ -30,6 +30,9 @@ LDAP_SERVER_URL = credentials.get("LDAP_SERVER_URL")
DEFAULT_AUTO_FIELD = "django.db.models.AutoField"
SYMPA_PASSWORD = credentials["SYMPA_PASSWORD"].encode()
SYMPA_USERNAME = credentials["SYMPA_USERNAME"].encode()
##
# Installed Apps configuration
@ -201,7 +204,10 @@ CHANNEL_LAYERS = credentials.get_json(
},
)
CORS_ORIGIN_WHITELIST = credentials.get("CORS_ORIGIN_WHITELIST", [])
ASGI_APPLICATION = "gestioasso.routing.application"
CORS_ALLOWED_ORIGINS = credentials.get("CORS_ALLOWED_ORIGINS", [])
CSRF_TRUSTED_ORIGINS = [f"https://{host}" for host in ALLOWED_HOSTS]
SITE_ID = 1

View file

@ -22,10 +22,10 @@
<section class="actulist">
{% if actus.has_previous %}
<a class="block prev-actus" href="?page={{ actus.previous_page_number }}{% for key,value in request.GET.items %}{% ifnotequal key 'page' %}&amp;{{ key }}={{ value }}{% endifnotequal %}{% endfor %}">{% trans "Actualités plus récentes" %}</a>
<a class="block prev-actus" href="?page={{ actus.previous_page_number }}{% for key,value in request.GET.items %}{% if key != 'page' %}&amp;{{ key }}={{ value }}{% endif %}{% endfor %}">{% trans "Actualités plus récentes" %}</a>
{% endif %}
{% if actus.has_next %}
<a class="block next-actus" href="?page={{ actus.next_page_number }}{% for key,value in request.GET.items %}{% ifnotequal key 'page' %}&amp;{{ key }}={{ value }}{% endifnotequal %}{% endfor %}">{% trans "Actualités plus anciennes" %}</a>
<a class="block next-actus" href="?page={{ actus.next_page_number }}{% for key,value in request.GET.items %}{% if key != 'page' %}&amp;{{ key }}={{ value }}{% endif %}{% endfor %}">{% trans "Actualités plus anciennes" %}</a>
{% endif %}
{% for actu in page.actus %}
@ -44,10 +44,10 @@
{% endfor %}
{% if actus.has_previous %}
<a class="block prev-actus" href="?page={{ actus.previous_page_number }}{% for key,value in request.GET.items %}{% ifnotequal key 'page' %}&amp;{{ key }}={{ value }}{% endifnotequal %}{% endfor %}">{% trans "Actualités plus récentes" %}</a>
<a class="block prev-actus" href="?page={{ actus.previous_page_number }}{% for key,value in request.GET.items %}{% if key != 'page' %}&amp;{{ key }}={{ value }}{% endif %}{% endfor %}">{% trans "Actualités plus récentes" %}</a>
{% endif %}
{% if actus.has_next %}
<a class="block next-actus" href="?page={{ actus.next_page_number }}{% for key,value in request.GET.items %}{% ifnotequal key 'page' %}&amp;{{ key }}={{ value }}{% endifnotequal %}{% endfor %}">{% trans "Actualités plus anciennes" %}</a>
<a class="block next-actus" href="?page={{ actus.next_page_number }}{% for key,value in request.GET.items %}{% if key != 'page' %}&amp;{{ key }}={{ value }}{% endif %}{% endfor %}">{% trans "Actualités plus anciennes" %}</a>
{% endif %}
</section>
{% endblock %}

View file

@ -4,6 +4,16 @@ from django.views.generic.base import TemplateView
from django_cas_ng import views as django_cas_views
from gestioncof import csv_views, views
from shared.views import SympaListView
sympa_patterns = [
path(
f"{mailing}/",
SympaListView.as_view(filters={f"profile__mailing_{mailing}": True}),
name=f"sympa.{mailing}",
)
for mailing in ["bda", "bda_revente", "cof", "unernestaparis"]
]
export_patterns = [
path("members", views.export_members, name="export.members"),
@ -162,4 +172,8 @@ urlpatterns = [
# Clubs
# -----
path("clubs/", include(clubs_patterns)),
# -----
# Sympa export
# -----
path("sympa/", include(sympa_patterns)),
]

View file

@ -1,3 +1,5 @@
from asgiref.sync import sync_to_async
from ..decorators import kfet_is_team
from ..utils import DjangoJsonWebsocketConsumer, PermConsumerMixin
from .open import kfet_open
@ -19,7 +21,7 @@ class OpenKfetConsumer(PermConsumerMixin, DjangoJsonWebsocketConsumer):
"""Send current status on connect."""
await super().connect()
group = "team" if kfet_is_team(self.user) else "base"
group = "team" if await sync_to_async(kfet_is_team)(self.user) else "base"
await self.channel_layer.group_add(f"kfet.open.{group}", self.channel_name)

View file

@ -31,6 +31,28 @@
{% endif %}
</div>
<aside>
<div class="heading">
{{ positive_count }}
<span class="sub">compte{{ positive_count|pluralize }} en positif</span>
</div>
<div class="heading">
{{ positives_sum|floatformat:2 }}€
<span class="sub">de positif total</span>
</div>
</aside>
<aside>
<div class="heading">
{{ negative_count }}
<span class="sub">compte{{ negative_count|pluralize }} en négatif</span>
</div>
<div class="heading">
{{ negatives_sum|floatformat:2 }}€
<span class="sub">de négatif total</span>
</div>
</aside>
{% endblock %}
{% block main %}

View file

@ -1,6 +1,7 @@
import json
import math
from asgiref.sync import sync_to_async
from channels.generic.websocket import AsyncJsonWebsocketConsumer
from django.core.cache import cache
from django.core.serializers.json import DjangoJSONEncoder
@ -95,7 +96,7 @@ class PermConsumerMixin:
"""Check permissions on connection."""
self.user = self.scope["user"]
if self.user.has_perms(self.perms_connect):
if await sync_to_async(self.user.has_perms)(self.perms_connect):
await super().connect()
else:
await self.close()

View file

@ -184,7 +184,16 @@ class DemandeSoireeView(FormView):
@teamkfet_required
def account(request):
accounts = Account.objects.select_related("cofprofile__user").order_by("trigramme")
return render(request, "kfet/account.html", {"accounts": accounts})
positive_accounts = Account.objects.filter(balance__gte=0).exclude(trigramme="#13")
negative_accounts = Account.objects.filter(balance__lt=0).exclude(trigramme="#13")
return render(request, "kfet/account.html", {
"accounts": accounts,
"positive_count": positive_accounts.count(),
"positives_sum": sum(acc.balance for acc in positive_accounts),
"negative_count": negative_accounts.count(),
"negatives_sum": sum(acc.balance for acc in negative_accounts),
})
@login_required
@ -1171,6 +1180,13 @@ def kpsul_perform_operations(request):
operationgroup.amount += operation.amount
if operation.type == Operation.DEPOSIT:
required_perms.add("kfet.perform_deposit")
if request.user.profile.account_kfet == on_acc:
data["errors"].append(
{
"code": "auto_deposit",
"message": ("Impossible de charger son propre trigramme"),
}
)
if operation.type == Operation.EDIT:
required_perms.add("kfet.edit_balance_account")
need_comment = True

View file

@ -1,12 +1,53 @@
import base64
from collections import namedtuple
from typing import Any
from dal import autocomplete
from django.conf import settings
from django.contrib.auth import get_user_model
from django.core.exceptions import ImproperlyConfigured
from django.http import Http404
from django.views.generic import TemplateView
from django.http import Http404, HttpResponse
from django.views.generic import TemplateView, View
from shared.autocomplete import ModelSearch
User = get_user_model()
class SympaListView(View):
realm = "sympa"
username = settings.SYMPA_USERNAME
password = settings.SYMPA_PASSWORD
filters: dict[str, Any] = {}
def dispatch(self, request, *args, **kwargs):
if "HTTP_AUTHORIZATION" in request.META:
auth = request.META["HTTP_AUTHORIZATION"].split()
if len(auth) == 2 and auth[0].lower() == "basic":
name, passwd = base64.b64decode(auth[1]).split(b":")
if name == self.username and passwd == self.password:
return self.render_to_response(request, *args, **kwargs)
return HttpResponse(
status=401, headers={"WWW-Authenticate": f'Basic realm="{self.realm}"'}
)
def render_to_response(self, request, *args, **kwargs):
"""
Renders a list of emails in a text response.
"""
users = User.objects.filter(**self.filters)
return HttpResponse(
b"\n".join(u.email.encode("utf-8") for u in users if u.email),
content_type="text/plain",
)
class Select2QuerySetView(ModelSearch, autocomplete.Select2QuerySetView):
"""Compatibility layer between ModelSearch and Select2QuerySetView."""