Files
team-tryouts/app/routes/users/_shared.py

90 lines
3.1 KiB
Python

"""Helpers used by more than one route module in this package.
Nothing here touches the blueprint: these are plain functions, so a test
can call them with a request context and nothing else.
"""
from flask_babel import gettext as _
from app.extensions import db
# Re-exported: these two moved to app/forms.py once the match and tryout
# routes needed them as well (ARCH-005). Importing them from here still
# works, so the thirty call sites in this package did not have to move.
from app.forms import flash_validation_errors, form_payload # noqa: F401
from app.models import Admin, Coach, Manager, Player, Scout, UserGamertag
ALLOWED_CONTRACT_EXTENSIONS = {'pdf'}
ALLOWED_SIGNED_EXTENSIONS = {'pdf'}
#: Every PDF starts with this. Checking the name alone accepted a file
#: called anything.pdf holding anything at all.
PDF_SIGNATURE = b'%PDF-'
#: USER_TYPE → model class, for create_user.
USER_CLASS_MAP = {
'admin': Admin,
'manager': Manager,
'coach': Coach,
'player': Player,
'scout': Scout,
}
def pdf_upload_error(file, allowed_extensions):
"""Why this upload is not an acceptable PDF, or None if it is.
upload_signed_contract checked nothing beyond a non-empty filename —
ALLOWED_SIGNED_EXTENSIONS was declared and never read — so a player
could put an arbitrary file on the server under a name the application
later hands back for download (SEC-021).
Args:
file: The uploaded FileStorage, or None.
allowed_extensions: Extensions to accept, lowercase and without dot.
Returns:
str | None: A message to flash, or None when the file is acceptable.
"""
if file is None or not file.filename:
return _('No file selected.')
stem, dot, extension = file.filename.rpartition('.')
if not (stem and dot) or extension.lower() not in allowed_extensions:
return _('Only PDF files are allowed for contracts.')
head = file.stream.read(len(PDF_SIGNATURE))
file.stream.seek(0)
if head != PDF_SIGNATURE:
return _('That file is not a PDF, whatever its name says.')
return None
def update_user_gamertags(user, selected_games):
"""Update gamertags for a user from validated dynamic form fields."""
from app.forms import form_gamertags
submitted = form_gamertags(selected_games)
existing_gamertags = {gt.game: gt for gt in user.gamertags}
for game in selected_games:
payload = submitted.get(game)
existing = existing_gamertags.get(game)
if payload:
if existing:
existing.gamertag = payload['gamertag']
existing.platform = payload['platform']
else:
gt = UserGamertag(
user_id=user.id,
game=game,
gamertag=payload['gamertag'],
platform=payload['platform'],
)
db.session.add(gt)
elif existing:
db.session.delete(existing)
for game in existing_gamertags:
if game not in selected_games:
db.session.delete(existing_gamertags[game])