Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,9 @@ jobs:
python -m pip install --upgrade pip
pip install -r requirements.txt

- name: Check for missing migrations
run: python manage.py makemigrations --check --dry-run

- name: Wait for MariaDB
run: |
for i in {1..30}; do
for i in {1..60}; do
if mysqladmin ping -h127.0.0.1 -utestuser -ptestpass --silent; then
echo "MariaDB is up!"
exit 0
Expand All @@ -74,6 +71,15 @@ jobs:
echo "DB_PORT=3306" >> $GITHUB_ENV
echo "SECRET_KEY=testsecrret" >> $GITHUB_ENV

- name: Check for missing migrations
env:
DJANGO_DB_NAME: ${{ env.DB_NAME }}
DJANGO_DB_USER: ${{ env.DB_USER }}
DJANGO_DB_PASSWORD: ${{ env.DB_PASSWORD }}
DJANGO_DB_HOST: ${{ env.DB_HOST }}
DJANGO_DB_PORT: ${{ env.DB_PORT }}
run: python manage.py makemigrations --check --dry-run

- name: Run Django tests
env:
DJANGO_DB_NAME: ${{ env.DB_NAME }}
Expand Down
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,16 @@ mypy .
docker compose logs -f web
```

## Pre-commit

```bash
# depuis un shell avec le venv actif
pre-commit install
pre-commit run --all-files
```

Les hooks locaux utilisent le Python du projet (`.venv/bin/python`) pour les checks Django et `mypy`.

## Arborescence principale

- `bde/` : configuration Django (settings, urls, env)
Expand Down
20 changes: 11 additions & 9 deletions bde/generate_robots.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,21 @@
def get_robots_content() -> str:

if env.DEV_MODE:
return "User-Agent: *\nDisallow: /"
return "User-Agent: *\nDisallow: /\nSitemap: /sitemap.xml\n"

return f"""User-agent: *
Disallow: /{env.ADMIN_URL}
Disallow: /logout/
Disallow: /redirect/
Disallow: /sso/
Disallow: /uploads/
"""
return (
f"User-agent: *\n"
f"Disallow: /{env.ADMIN_URL}\n"
"Disallow: /logout/\n"
"Disallow: /redirect/\n"
"Disallow: /sso/\n"
"Disallow: /uploads/\n"
"Sitemap: /sitemap.xml\n"
)


BASE_DIR = Path(__file__).resolve().parent.parent
STATICFILES_DIR = BASE_DIR / ("staticfiles" if not env.DEBUG else "static")
STATICFILES_DIR = BASE_DIR / "staticfiles"
STATICFILES_DIR.mkdir(exist_ok=True)

robots_path = STATICFILES_DIR / "robots.txt"
Expand Down
5 changes: 5 additions & 0 deletions bde/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@
SESSION_COOKIE_SECURE = env.SESSION_COOKIE_SECURE
CSRF_COOKIE_SECURE = env.CSRF_COOKIE_SECURE

DEFAULT_SEO_TITLE = "BDE UTT"
DEFAULT_SEO_DESCRIPTION = "Le site du Bureau des Etudiants de l'UTT : evenements, services, partenariats et vie associative."
DEFAULT_SEO_IMAGE = "/static/img/bde.png"

if env.CSRF_TRUSTED_ORIGINS:
CSRF_TRUSTED_ORIGINS = env.CSRF_TRUSTED_ORIGINS

Expand All @@ -24,6 +28,7 @@
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"django.contrib.sitemaps",
"mozilla_django_oidc",
"core",
"members",
Expand Down
27 changes: 27 additions & 0 deletions bde/sitemaps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from django.contrib.sitemaps import Sitemap
from django.urls import reverse


class StaticViewSitemap(Sitemap):
changefreq = "weekly"
priority = 0.8

def items(self):
return [
"home",
"contacts",
"events",
"membership",
"partners",
"services",
"members",
"board",
]

def location(self, item):
return reverse(item)


sitemaps = {
"static": StaticViewSitemap,
}
6 changes: 3 additions & 3 deletions bde/templates/legals.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ <h4 class="fw-bold mb-3">
</h4>
<div class="row g-3">
<div class="col-md-6">
<!-- TODO infos BDE -->
<p class="mb-2"><strong>BDE UTT</strong></p>
<p class="text-muted small mb-2">Association loi 1901</p>
<p class="text-muted small mb-1">N° RNA : </p>
<p class="text-muted small">N° RCS : </p>
<p class="text-muted small mb-1">N° RNA : W103000735</p>
<p class="text-muted small mb-1">N° SIRET : 44838667200019</p>
<p class="text-muted small mb-1">N° SIREN : 448386672</p>
</div>
<div class="col-md-6">
<p class="text-muted small mb-2">
Expand Down
18 changes: 18 additions & 0 deletions bde/tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from django.test import SimpleTestCase, override_settings

from bde.generate_robots import get_robots_content


class SEOInfrastructureTests(SimpleTestCase):
def test_sitemap_is_available(self):
response = self.client.get("/sitemap.xml")

self.assertEqual(response.status_code, 200)
self.assertContains(response, "/members/")
self.assertContains(response, "/events/")

@override_settings(DEBUG=True)
def test_robots_declares_sitemap(self):
robots = get_robots_content()

self.assertIn("Sitemap: /sitemap.xml", robots)
8 changes: 8 additions & 0 deletions bde/urls.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
from django.contrib import admin
from django.contrib.sitemaps.views import sitemap
from django.urls import path, include, re_path
from django.conf import settings
from django.conf.urls.static import static
from django.views.static import serve

from bde.env import EnvConfig
from bde.sitemaps import sitemaps

env = EnvConfig()
ADMIN_URL = env.ADMIN_URL
Expand All @@ -22,6 +24,12 @@
serve,
{"path": "robots.txt", "document_root": settings.STATIC_ROOT},
),
path(
"sitemap.xml",
sitemap,
{"sitemaps": sitemaps},
name="django.contrib.sitemaps.views.sitemap",
),
path("sso/", include("mozilla_django_oidc.urls")),
path(ADMIN_URL, admin.site.urls),
]
Expand Down
20 changes: 18 additions & 2 deletions bde/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,29 @@ def legals(request):
return render(
request,
"legals.html",
{**common_data()},
{
**common_data(
request,
seo={
"title": "BDE UTT | Mentions legales",
"description": "Mentions legales du site du Bureau des Etudiants de l'UTT.",
},
)
},
)


def privacy(request):
return render(
request,
"privacy.html",
{**common_data()},
{
**common_data(
request,
seo={
"title": "BDE UTT | Politique de confidentialite",
"description": "Politique de confidentialite et traitement des donnees personnelles du site BDE UTT.",
},
)
},
)
2 changes: 1 addition & 1 deletion members/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ class UserTeamAdmin(admin.ModelAdmin):

@admin.register(UserProfile)
class UserProfileAdmin(admin.ModelAdmin):
list_display = ("user", "picture", "description")
list_display = ("user", "picture", "feminine_role", "description")
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Generated by Django 5.2.6 on 2026-03-08 15:13

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('members', '0003_alter_userteam_team'),
]

operations = [
migrations.AddField(
model_name='userprofile',
name='feminine_role',
field=models.BooleanField(default=False),
),
migrations.AlterField(
model_name='userteam',
name='role',
field=models.CharField(choices=[('PRESIDENT', 'Président'), ('VICE_PRESIDENT', 'Vice-Président'), ('SECRETARY', 'Secrétaire'), ('TREASURER', 'Trésorier'), ('VICE_TREASURER', 'Vice-Trésorier'), ('MANAGER', 'Responsable'), ('MEMBER', 'Membre')], default='MEMBER', max_length=20),
),
]
18 changes: 18 additions & 0 deletions members/migrations/0005_alter_userteam_role.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 5.2.6 on 2026-03-08 23:04

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('members', '0004_userprofile_feminine_role_alter_userteam_role'),
]

operations = [
migrations.AlterField(
model_name='userteam',
name='role',
field=models.CharField(choices=[('PRESIDENT', 'Président'), ('TREASURER', 'Trésorier'), ('VICE_TREASURER', 'Vice-Trésorier'), ('VICE_PRESIDENT', 'Vice-Président'), ('SECRETARY', 'Secrétaire'), ('MANAGER', 'Responsable'), ('MEMBER', 'Membre')], default='MEMBER', max_length=20),
),
]
56 changes: 50 additions & 6 deletions members/models.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,42 @@
from __future__ import annotations

from django.db import models
from django.contrib.auth.models import User
from django.utils.safestring import mark_safe
from utils.models import picture_upload_to
from typing import cast
from typing import Literal, TypedDict, cast


RoleCode = Literal[
"PRESIDENT",
"VICE_PRESIDENT",
"SECRETARY",
"TREASURER",
"VICE_TREASURER",
"MANAGER",
"MEMBER",
]


class TeamTemplate(TypedDict):
name: str
description: str


class UserTeamTemplate(TypedDict):
first_name: str
last_name: str
role: RoleCode
role_display: str
role_suffix: str
profile: "UserProfile | None"


class Team(models.Model):
name: models.CharField = models.CharField(max_length=100)
description: models.TextField = models.TextField(blank=True)

def to_template(self) -> dict[str, str]:
def to_template(self) -> TeamTemplate:
return {"name": self.name, "description": self.description}

def __str__(self) -> str:
Expand All @@ -27,6 +54,7 @@ def _members_picture_upload_to(instance, _) -> str:
)

description: models.TextField = models.TextField(blank=True)
feminine_role: models.BooleanField = models.BooleanField(default=False)

@property
def picture_url(self) -> str:
Expand All @@ -39,11 +67,19 @@ def __str__(self) -> str:

class UserTeam(models.Model):

ROLES = [
FEMINIZABLE_ROLES: set[RoleCode] = {
"PRESIDENT",
"VICE_PRESIDENT",
"TREASURER",
"VICE_TREASURER",
}

ROLES: list[tuple[RoleCode, str]] = [
("PRESIDENT", "Président"),
("TREASURER", "Trésorier"),
("VICE_TREASURER", "Vice-Trésorier"),
("VICE_PRESIDENT", "Vice-Président"),
("SECRETARY", "Secrétaire"),
("TREASURER", "Trésorier"),
("MANAGER", "Responsable"),
("MEMBER", "Membre"),
]
Expand All @@ -60,13 +96,21 @@ class UserTeam(models.Model):
def role_display(self) -> str:
return dict(self.ROLES).get(self.role, "Unknown")

def to_template(self) -> dict[str, object | None]:
def role_suffix(self, profile: UserProfile | None) -> str:
if profile and profile.feminine_role and self.role in self.FEMINIZABLE_ROLES:
return "e"
return ""

def to_template(self) -> UserTeamTemplate:
user = cast(User, self.user)
profile = get_userProfile_from_userTeam(self)
return {
"first_name": user.first_name,
"last_name": user.last_name,
"role": self.role,
"role_display": self.role_display,
"profile": get_userProfile_from_userTeam(self),
"role_suffix": self.role_suffix(profile),
"profile": profile,
}

def __str__(self) -> str:
Expand Down
3 changes: 2 additions & 1 deletion members/templates/board/memberCard.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
alt="{{member.first_name}} {{member.last_name}}" style="width: 72px; height: 72px; object-fit: cover;">
<div class="d-flex flex-column">
<h5 class="card-title fw-bold m-0">{{member.first_name}} {{member.last_name}}</h5>
<p class="m-0 text-secondary">{{member.role_display}}</p>
<p class="m-0 text-secondary">{{ member.role_display }}<span
class="role-suffix{% if not member.role_suffix %} role-suffix--hidden{% endif %}">e</span></p>
</div>
</div>

Expand Down
Loading
Loading