style: formater le depot avec ruff format
QUA-002, premiere moitie. **Ce commit ne fait que reformater** : aucun changement de comportement, aucune ligne de logique touchee. 72 fichiers, 4 restaient deja conformes. Il est isole exprès, pour que `git log -p` sur les commits voisins reste lisible. `quote-style = "preserve"` etait deja pose dans pyproject.toml, ce qui evite le brassage guillemets simples / doubles : le diff porte sur les retours a la ligne, l indentation des appels longs et les virgules finales, pas sur le style de chaine. Verification : 263 tests passent avant et apres, ruff check propre. L activation en CI arrive dans le commit suivant, separement, pour que ce diff-ci ne contienne rien d autre. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
+63
-67
@@ -12,7 +12,15 @@ Usage:
|
||||
|
||||
import re
|
||||
from flask_babel import lazy_gettext as _l
|
||||
from marshmallow import Schema, fields, validate, ValidationError, pre_load, validates_schema, EXCLUDE
|
||||
from marshmallow import (
|
||||
Schema,
|
||||
fields,
|
||||
validate,
|
||||
ValidationError,
|
||||
pre_load,
|
||||
validates_schema,
|
||||
EXCLUDE,
|
||||
)
|
||||
from app.models import USER_TYPES
|
||||
|
||||
|
||||
@@ -20,58 +28,55 @@ from app.models import USER_TYPES
|
||||
# Custom Validators
|
||||
# =============================================================================
|
||||
|
||||
PASSWORD_POLICY = re.compile(
|
||||
r'^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{8,}$'
|
||||
)
|
||||
PASSWORD_POLICY = re.compile(r'^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{8,}$')
|
||||
"""Password policy: minimum 8 chars, 1 uppercase, 1 lowercase, 1 digit."""
|
||||
|
||||
|
||||
def validate_password(value):
|
||||
"""Validate password meets strength requirements.
|
||||
|
||||
|
||||
Requires: minimum 8 characters, at least one uppercase letter,
|
||||
one lowercase letter, and one digit.
|
||||
|
||||
|
||||
Args:
|
||||
value: The password string to validate.
|
||||
|
||||
|
||||
Raises:
|
||||
ValidationError: If password does not meet requirements.
|
||||
"""
|
||||
if not PASSWORD_POLICY.match(value):
|
||||
raise ValidationError(_l(
|
||||
'Password must be at least 8 characters with uppercase, '
|
||||
'lowercase, and a number.'
|
||||
))
|
||||
raise ValidationError(
|
||||
_l('Password must be at least 8 characters with uppercase, lowercase, and a number.')
|
||||
)
|
||||
|
||||
|
||||
def validate_username(value):
|
||||
"""Validate username format.
|
||||
|
||||
|
||||
Usernames must be 3-30 characters and contain only alphanumeric
|
||||
characters, underscores, and hyphens.
|
||||
|
||||
|
||||
Args:
|
||||
value: The username string to validate.
|
||||
|
||||
|
||||
Raises:
|
||||
ValidationError: If username does not meet requirements.
|
||||
"""
|
||||
if not re.match(r'^[a-zA-Z0-9_-]{3,30}$', value):
|
||||
raise ValidationError(_l(
|
||||
'Username must be 3-30 characters (letters, numbers, underscore, hyphen).'
|
||||
))
|
||||
raise ValidationError(
|
||||
_l('Username must be 3-30 characters (letters, numbers, underscore, hyphen).')
|
||||
)
|
||||
|
||||
|
||||
def validate_discord_username(value):
|
||||
"""Validate Discord username format if provided.
|
||||
|
||||
|
||||
Accepts empty strings (optional field). Validates that the username
|
||||
matches common Discord username patterns.
|
||||
|
||||
|
||||
Args:
|
||||
value: The Discord username to validate.
|
||||
|
||||
|
||||
Raises:
|
||||
ValidationError: If the format is invalid.
|
||||
"""
|
||||
@@ -83,12 +88,12 @@ def validate_discord_username(value):
|
||||
|
||||
def validate_discord_user_id(value):
|
||||
"""Validate Discord user ID (snowflake) if provided.
|
||||
|
||||
|
||||
Discord user IDs are 17-20 digit numbers.
|
||||
|
||||
|
||||
Args:
|
||||
value: The Discord user ID to validate.
|
||||
|
||||
|
||||
Raises:
|
||||
ValidationError: If the format is invalid.
|
||||
"""
|
||||
@@ -100,12 +105,12 @@ def validate_discord_user_id(value):
|
||||
|
||||
def validate_phone(value):
|
||||
"""Validate optional phone number format.
|
||||
|
||||
|
||||
Accepts empty strings. Validates common phone formats.
|
||||
|
||||
|
||||
Args:
|
||||
value: The phone number to validate.
|
||||
|
||||
|
||||
Raises:
|
||||
ValidationError: If the format is invalid.
|
||||
"""
|
||||
@@ -120,21 +125,23 @@ def validate_phone(value):
|
||||
# Validation Schemas
|
||||
# =============================================================================
|
||||
|
||||
|
||||
class StripMixin(Schema):
|
||||
"""Mixin that automatically strips whitespace from all string fields
|
||||
and ignores unknown fields (e.g., csrf_token from Flask-WTF)."""
|
||||
|
||||
|
||||
class Meta:
|
||||
"""Marshmallow Meta options."""
|
||||
|
||||
unknown = EXCLUDE # Ignore csrf_token and other unknown fields
|
||||
|
||||
|
||||
@pre_load
|
||||
def strip_strings(self, data, **kwargs):
|
||||
"""Strip whitespace from all string values in the input data.
|
||||
|
||||
|
||||
Args:
|
||||
data: The input dictionary.
|
||||
|
||||
|
||||
Returns:
|
||||
dict: Data with stripped strings.
|
||||
"""
|
||||
@@ -145,11 +152,12 @@ class StripMixin(Schema):
|
||||
|
||||
class LoginSchema(StripMixin):
|
||||
"""Validate login form input.
|
||||
|
||||
|
||||
Fields:
|
||||
username: 3-30 chars, required.
|
||||
password: Non-empty, required.
|
||||
"""
|
||||
|
||||
username = fields.String(
|
||||
required=True,
|
||||
validate=validate.Length(min=1, max=80, error=_l('Username is required.')),
|
||||
@@ -162,7 +170,7 @@ class LoginSchema(StripMixin):
|
||||
|
||||
class RegisterSchema(StripMixin):
|
||||
"""Validate player registration form input.
|
||||
|
||||
|
||||
Fields:
|
||||
username: 3-30 chars alphanumeric, required.
|
||||
email: Valid email, required.
|
||||
@@ -174,6 +182,7 @@ class RegisterSchema(StripMixin):
|
||||
discord_username: Optional, valid format.
|
||||
league_os_profile: Optional URL.
|
||||
"""
|
||||
|
||||
username = fields.String(
|
||||
required=True,
|
||||
validate=[
|
||||
@@ -223,10 +232,10 @@ class RegisterSchema(StripMixin):
|
||||
@validates_schema
|
||||
def validate_password_match(self, data, **kwargs):
|
||||
"""Ensure confirm_password matches password.
|
||||
|
||||
|
||||
Args:
|
||||
data: The validated data dictionary.
|
||||
|
||||
|
||||
Raises:
|
||||
ValidationError: If passwords do not match.
|
||||
"""
|
||||
@@ -236,7 +245,7 @@ class RegisterSchema(StripMixin):
|
||||
|
||||
class CreateUserSchema(StripMixin):
|
||||
"""Validate president-created user form input.
|
||||
|
||||
|
||||
Fields:
|
||||
username: 3-30 chars alphanumeric, required.
|
||||
email: Valid email, required.
|
||||
@@ -245,6 +254,7 @@ class CreateUserSchema(StripMixin):
|
||||
role: Must be valid role, required.
|
||||
phone: Optional, valid phone format.
|
||||
"""
|
||||
|
||||
username = fields.String(
|
||||
required=True,
|
||||
validate=[
|
||||
@@ -267,10 +277,7 @@ class CreateUserSchema(StripMixin):
|
||||
)
|
||||
role = fields.String(
|
||||
required=True,
|
||||
validate=validate.OneOf(
|
||||
USER_TYPES,
|
||||
error=_l('Invalid role selected.')
|
||||
),
|
||||
validate=validate.OneOf(USER_TYPES, error=_l('Invalid role selected.')),
|
||||
)
|
||||
phone = fields.String(
|
||||
validate=validate_phone,
|
||||
@@ -281,7 +288,7 @@ class CreateUserSchema(StripMixin):
|
||||
|
||||
class EditUserSchema(StripMixin):
|
||||
"""Validate president-edited user form input.
|
||||
|
||||
|
||||
Fields:
|
||||
full_name: 1-100 chars, required.
|
||||
email: Valid email, required.
|
||||
@@ -294,6 +301,7 @@ class EditUserSchema(StripMixin):
|
||||
league_os_profile: Optional.
|
||||
games: Optional list.
|
||||
"""
|
||||
|
||||
full_name = fields.String(
|
||||
required=True,
|
||||
validate=validate.Length(min=1, max=100, error=_l('Full name is required.')),
|
||||
@@ -304,10 +312,7 @@ class EditUserSchema(StripMixin):
|
||||
)
|
||||
role = fields.String(
|
||||
required=True,
|
||||
validate=validate.OneOf(
|
||||
USER_TYPES,
|
||||
error=_l('Invalid role selected.')
|
||||
),
|
||||
validate=validate.OneOf(USER_TYPES, error=_l('Invalid role selected.')),
|
||||
)
|
||||
is_active_account = fields.Boolean(load_default=True)
|
||||
phone = fields.String(
|
||||
@@ -341,7 +346,7 @@ class EditUserSchema(StripMixin):
|
||||
|
||||
class EditProfileSchema(StripMixin):
|
||||
"""Validate self-edit profile form input.
|
||||
|
||||
|
||||
Fields:
|
||||
username: 3-30 chars, required.
|
||||
full_name: 1-100 chars, required.
|
||||
@@ -353,6 +358,7 @@ class EditProfileSchema(StripMixin):
|
||||
league_os_profile: Optional.
|
||||
games: Optional list.
|
||||
"""
|
||||
|
||||
username = fields.String(
|
||||
required=True,
|
||||
validate=[
|
||||
@@ -399,11 +405,12 @@ class EditProfileSchema(StripMixin):
|
||||
|
||||
class UploadContractSchema(StripMixin):
|
||||
"""Validate contract upload form input.
|
||||
|
||||
|
||||
Fields:
|
||||
player_id: Integer, required.
|
||||
notes: Optional text.
|
||||
"""
|
||||
|
||||
player_id = fields.Integer(
|
||||
required=True,
|
||||
validate=validate.Range(min=1, error=_l('Player must be selected.')),
|
||||
@@ -417,33 +424,27 @@ class UploadContractSchema(StripMixin):
|
||||
|
||||
class OneOnOneRequestSchema(StripMixin):
|
||||
"""Validate One on One session request form input.
|
||||
|
||||
|
||||
Fields:
|
||||
date: Date string (YYYY-MM-DD), required.
|
||||
start_time: Time string (HH:MM), required.
|
||||
end_time: Time string (HH:MM), required.
|
||||
points: Optional text.
|
||||
"""
|
||||
|
||||
date = fields.String(
|
||||
required=True,
|
||||
validate=validate.Regexp(
|
||||
r'^\d{4}-\d{2}-\d{2}$',
|
||||
error=_l('Date must be in YYYY-MM-DD format.')
|
||||
r'^\d{4}-\d{2}-\d{2}$', error=_l('Date must be in YYYY-MM-DD format.')
|
||||
),
|
||||
)
|
||||
start_time = fields.String(
|
||||
required=True,
|
||||
validate=validate.Regexp(
|
||||
r'^\d{2}:\d{2}$',
|
||||
error=_l('Start time must be in HH:MM format.')
|
||||
),
|
||||
validate=validate.Regexp(r'^\d{2}:\d{2}$', error=_l('Start time must be in HH:MM format.')),
|
||||
)
|
||||
end_time = fields.String(
|
||||
required=True,
|
||||
validate=validate.Regexp(
|
||||
r'^\d{2}:\d{2}$',
|
||||
error=_l('End time must be in HH:MM format.')
|
||||
),
|
||||
validate=validate.Regexp(r'^\d{2}:\d{2}$', error=_l('End time must be in HH:MM format.')),
|
||||
)
|
||||
points = fields.String(
|
||||
validate=validate.Length(max=2000, error=_l('Points must be 2000 characters or less.')),
|
||||
@@ -454,22 +455,17 @@ class OneOnOneRequestSchema(StripMixin):
|
||||
|
||||
class DisponibilityAddSchema(StripMixin):
|
||||
"""Validate disponibility block addition.
|
||||
|
||||
|
||||
Fields:
|
||||
day_of_week: Integer 0-6, required.
|
||||
start_time: Time string (HH:MM), required.
|
||||
"""
|
||||
|
||||
day_of_week = fields.Integer(
|
||||
required=True,
|
||||
validate=validate.Range(
|
||||
min=0, max=6,
|
||||
error=_l('Day must be 0 (Monday) to 6 (Sunday).')
|
||||
),
|
||||
validate=validate.Range(min=0, max=6, error=_l('Day must be 0 (Monday) to 6 (Sunday).')),
|
||||
)
|
||||
start_time = fields.String(
|
||||
required=True,
|
||||
validate=validate.Regexp(
|
||||
r'^\d{2}:\d{2}$',
|
||||
error=_l('Start time must be in HH:MM format.')
|
||||
),
|
||||
)
|
||||
validate=validate.Regexp(r'^\d{2}:\d{2}$', error=_l('Start time must be in HH:MM format.')),
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user