Merge branch 'dev' of https://git.immortal.host/clubesportsudes/team-tryouts into audit/securite-maintenabilite-standards
This commit is contained in:
@@ -7,6 +7,14 @@ from flask import Blueprint, flash, redirect, render_template, request, url_for
|
||||
from flask_babel import gettext as _
|
||||
from flask_login import current_user, login_required
|
||||
from marshmallow import ValidationError
|
||||
from flask import Blueprint, render_template, redirect, url_for, flash, request
|
||||
from flask_login import login_required, current_user
|
||||
from app.extensions import db
|
||||
from app.models import (
|
||||
Admin, Coach, Manager, Player,
|
||||
User, Tryout, Evaluation, TryoutRegistration,
|
||||
OrgTeam, GAME_POSITIONS, EVALUATION_CRITERIA,
|
||||
)
|
||||
from sqlalchemy import func
|
||||
from sqlalchemy.orm import aliased
|
||||
|
||||
@@ -27,6 +35,34 @@ from app.validators import EvaluationSchema
|
||||
evaluations_bp = Blueprint('evaluations', __name__, url_prefix='/evaluations')
|
||||
|
||||
|
||||
def validate_score(score_value):
|
||||
"""Validate that a score is between 1 and 10."""
|
||||
if score_value is None:
|
||||
return None
|
||||
try:
|
||||
score = int(score_value)
|
||||
if 1 <= score <= 10:
|
||||
return score
|
||||
return None
|
||||
except (ValueError, TypeError):
|
||||
return None
|
||||
|
||||
|
||||
def compute_overall(scores):
|
||||
"""Average the non-None scores, or return None if there are none."""
|
||||
valid = [s for s in scores if s is not None]
|
||||
return sum(valid) / len(valid) if valid else None
|
||||
|
||||
|
||||
def _apply_evaluation(evaluation, scores, comments, position):
|
||||
"""Write validated scores/comments/position onto an Evaluation instance."""
|
||||
for field_name, _ in EVALUATION_CRITERIA:
|
||||
setattr(evaluation, field_name, scores[field_name])
|
||||
evaluation.overall_score = compute_overall(list(scores.values()))
|
||||
evaluation.comments = comments
|
||||
evaluation.position_recommendation = position
|
||||
|
||||
|
||||
@evaluations_bp.route('')
|
||||
@login_required
|
||||
def list_evaluations():
|
||||
@@ -228,3 +264,94 @@ def players_to_evaluate(tryout_id):
|
||||
players.append({'player': p, 'evaluated': existing is not None, 'registration': reg})
|
||||
|
||||
return render_template('pages/players_to_evaluate.html', tryout=tryout, players=players)
|
||||
|
||||
|
||||
@evaluations_bp.route('/<int:tryout_id>/batch', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def batch_evaluate(tryout_id):
|
||||
"""Evaluate multiple players at once in a tryout.
|
||||
|
||||
GET renders a single form listing every selected player with their
|
||||
evaluation criteria. POST saves (creates or updates) all of them.
|
||||
"""
|
||||
if not current_user.can_evaluate():
|
||||
flash('You do not have permission to evaluate players.', 'danger')
|
||||
return redirect(url_for('main.dashboard'))
|
||||
|
||||
tryout = Tryout.query.get_or_404(tryout_id)
|
||||
if not current_user.can_manage_this_tryout(tryout):
|
||||
flash('You do not have permission to evaluate players in this tryout.', 'danger')
|
||||
return redirect(url_for('tryouts.list_tryouts'))
|
||||
|
||||
# Resolve selected player ids (query string on GET, hidden fields on POST).
|
||||
player_ids = []
|
||||
for raw in request.values.getlist('player_ids'):
|
||||
try:
|
||||
pid = int(raw)
|
||||
except (ValueError, TypeError):
|
||||
continue
|
||||
if pid not in player_ids:
|
||||
player_ids.append(pid)
|
||||
|
||||
if not player_ids:
|
||||
flash('Please select at least one player to evaluate.', 'warning')
|
||||
return redirect(url_for('evaluations.players_to_evaluate', tryout_id=tryout_id))
|
||||
|
||||
players = []
|
||||
for pid in player_ids:
|
||||
player = User.query.get(pid)
|
||||
if not player or not isinstance(player, Player):
|
||||
continue
|
||||
is_registered = TryoutRegistration.query.filter_by(
|
||||
tryout_id=tryout_id, player_id=pid,
|
||||
).first() is not None
|
||||
if not is_registered:
|
||||
continue
|
||||
existing = Evaluation.query.filter_by(
|
||||
tryout_id=tryout_id, player_id=pid, evaluator_id=current_user.id,
|
||||
).first()
|
||||
existing_scores = {
|
||||
field_name: getattr(existing, field_name) if existing else None
|
||||
for field_name, _ in EVALUATION_CRITERIA
|
||||
}
|
||||
players.append({
|
||||
'player': player,
|
||||
'existing': existing,
|
||||
'existing_scores': existing_scores,
|
||||
})
|
||||
|
||||
if not players:
|
||||
flash('No valid players selected for evaluation.', 'danger')
|
||||
return redirect(url_for('evaluations.players_to_evaluate', tryout_id=tryout_id))
|
||||
|
||||
if request.method == 'POST':
|
||||
saved = 0
|
||||
for entry in players:
|
||||
pid = entry['player'].id
|
||||
scores = {
|
||||
field_name: validate_score(request.form.get(f'{field_name}_{pid}'))
|
||||
for field_name, _ in EVALUATION_CRITERIA
|
||||
}
|
||||
comments = request.form.get(f'comments_{pid}')
|
||||
position = request.form.get(f'position_recommendation_{pid}')
|
||||
|
||||
existing = entry['existing']
|
||||
if existing:
|
||||
_apply_evaluation(existing, scores, comments, position)
|
||||
else:
|
||||
evaluation = Evaluation(
|
||||
tryout_id=tryout_id, player_id=pid,
|
||||
evaluator_id=current_user.id,
|
||||
)
|
||||
_apply_evaluation(evaluation, scores, comments, position)
|
||||
db.session.add(evaluation)
|
||||
saved += 1
|
||||
|
||||
db.session.commit()
|
||||
flash(f'Saved evaluations for {saved} player(s).', 'success')
|
||||
return redirect(url_for('tryouts.view_tryout', tryout_id=tryout_id))
|
||||
|
||||
return render_template('pages/batch_evaluate.html',
|
||||
tryout=tryout, players=players,
|
||||
evaluation_criteria=EVALUATION_CRITERIA,
|
||||
game_positions=GAME_POSITIONS)
|
||||
|
||||
+1270
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user