Author SHA1 Message Date
cedrick2711 a616c79663 regler probleme avec la creation d'equipe et 1 manager par tryout changer pour plusieurs
CI - Security, Lint & Tests / validate (push) Failing after 58s
2026-08-19 21:16:25 -04:00
10 changed files with 103 additions and 27 deletions
+10
View File
@@ -37,3 +37,13 @@ tryout_coaches = db.Table(
'coach_id', db.Integer, db.ForeignKey('users.id', ondelete='CASCADE'), primary_key=True
),
)
tryout_managers = db.Table(
'tryout_managers',
db.Column(
'tryout_id', db.Integer, db.ForeignKey('tryouts.id', ondelete='CASCADE'), primary_key=True
),
db.Column(
'manager_id', db.Integer, db.ForeignKey('users.id', ondelete='CASCADE'), primary_key=True
),
)
+18 -3
View File
@@ -1,7 +1,7 @@
"""Tryout event for player evaluations and team formation."""
from app.extensions import db
from app.models._associations import tryout_coaches
from app.models._associations import tryout_coaches, tryout_managers
from app.time_utils import utc_now_naive
@@ -20,16 +20,17 @@ class Tryout(db.Model):
max_players = db.Column(db.Integer, nullable=True)
created_by = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
target_org_team_id = db.Column(db.Integer, db.ForeignKey('org_teams.id'), nullable=True)
manager_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=True)
manager_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=True) # deprecated, kept for migration
coach_id = db.Column(
db.Integer, db.ForeignKey('users.id'), nullable=True
) # deprecated, kept for migration
created_at = db.Column(db.DateTime, default=utc_now_naive)
creator = db.relationship('User', foreign_keys=[created_by], backref='created_tryouts')
manager = db.relationship('User', foreign_keys=[manager_id], backref='managed_tryouts')
manager = db.relationship('User', foreign_keys=[manager_id], backref='managed_tryouts') # deprecated
coach = db.relationship('User', foreign_keys=[coach_id], backref='_deprecated_coached_tryouts')
coaches = db.relationship('User', secondary=tryout_coaches, backref='coached_tryouts')
managers = db.relationship('User', secondary=tryout_managers, backref='managed_tryouts_m2m')
registrations = db.relationship('TryoutRegistration', backref='tryout', lazy='dynamic')
evaluations = db.relationship('Evaluation', backref='tryout', lazy='dynamic')
teams = db.relationship('Team', backref='tryout', lazy='dynamic')
@@ -47,3 +48,17 @@ class Tryout(db.Model):
if self.end_date is not None:
return self.end_date < today
return self.date < today
def get_managers(self):
"""Managers attached to this tryout, both legacy and many-to-many."""
manager_list = list(self.managers)
if not manager_list and self.manager:
return [self.manager]
return manager_list
def get_coaches(self):
"""Coaches attached to this tryout, both legacy and many-to-many."""
coach_list = list(self.coaches)
if not coach_list and self.coach:
return [self.coach]
return coach_list
+12 -2
View File
@@ -21,7 +21,11 @@ class Manager(User):
return True
def can_manage_this_tryout(self, tryout):
return tryout.created_by == self.id or tryout.manager_id == self.id
return (
tryout.created_by == self.id
or tryout.manager_id == self.id
or any(m.id == self.id for m in tryout.managers)
)
def can_manage_this_org_team(self, org_team):
return True
@@ -32,7 +36,13 @@ class Manager(User):
from app.models.tryout.tryout import Tryout
return (
Tryout.query.filter(or_(Tryout.created_by == self.id, Tryout.manager_id == self.id))
Tryout.query.filter(
or_(
Tryout.created_by == self.id,
Tryout.manager_id == self.id,
Tryout.managers.any(id=self.id),
)
)
.order_by(Tryout.date)
.all()
)
+17 -4
View File
@@ -51,7 +51,7 @@ def can_manage():
def tryout_form_payload():
"""The tryout form, shaped for marshmallow (ARCH-005)."""
return form_payload(list_fields=('coach_ids',), optional_blank=())
return form_payload(list_fields=('coach_ids', 'manager_ids'), optional_blank=())
def coaches_from_ids(coach_ids):
@@ -67,6 +67,13 @@ def coaches_from_ids(coach_ids):
return User.query.filter(User.id.in_(coach_ids), User.role == 'coach').all()
def managers_from_ids(manager_ids):
"""The manager accounts behind these ids, filtered by role."""
if not manager_ids:
return []
return User.query.filter(User.id.in_(manager_ids), User.role == 'manager').all()
def _users_by_id(user_ids):
"""Load these users in one query, keyed by id.
@@ -160,12 +167,12 @@ def create_tryout():
created_by=current_user.id,
status='upcoming',
target_org_team_id=data['target_org_team_id'],
manager_id=data['manager_id'],
)
db.session.add(tryout)
db.session.flush()
tryout.coaches = coaches_from_ids(data['coach_ids'])
tryout.managers = managers_from_ids(data['manager_ids'])
db.session.commit()
flash(_('Tryout created successfully!'), 'success')
@@ -221,8 +228,14 @@ def edit_tryout(tryout_id):
tryout.location = data['location']
tryout.max_players = data['max_players']
tryout.target_org_team_id = data['target_org_team_id']
tryout.manager_id = data['manager_id']
# Only update staff lists when the form explicitly sends them.
# An absent checkbox group (all unchecked or JS failed) means
# \"don't change\", not \"remove everyone\".
if 'coach_ids' in request.form:
tryout.coaches = coaches_from_ids(data['coach_ids'])
if 'manager_ids' in request.form:
tryout.managers = managers_from_ids(data['manager_ids'])
db.session.commit()
flash(_('Tryout updated successfully!'), 'success')
@@ -241,7 +254,7 @@ def view_tryout(tryout_id):
if isinstance(current_user, Admin):
can_view = True
elif isinstance(current_user, Manager):
can_view = tryout.created_by == current_user.id or tryout.manager_id == current_user.id
can_view = current_user.can_manage_this_tryout(tryout)
elif isinstance(current_user, Coach):
can_view = current_user.can_manage_this_tryout(tryout)
elif isinstance(current_user, Player):
+1 -1
View File
@@ -43,7 +43,7 @@
<div class="form-group col-4">
<label for="{{ field_name }}_{{ pid }}">{{ label }} (1-10)</label>
<div class="score-input">
<input type="range" id="{{ field_name }}_{{ pid }}" name="{{ field_name }}_{{ pid }}" min="1" max="10" value="{{ existing_scores[field_name] or 5 }}" oninput="this.nextElementSibling.textContent = this.value">
<input type="range" id="{{ field_name }}_{{ pid }}" name="{{ field_name }}_{{ pid }}" min="1" max="10" value="{{ existing_scores[field_name] or 5 }}" data-mirror>
<span class="range-value">{{ existing_scores[field_name] or 5 }}</span>
</div>
</div>
+11 -3
View File
@@ -15,7 +15,7 @@
<table class="table">
<thead>
<tr>
<th><input type="checkbox" id="select-all" onclick="toggleAll(this)"></th>
<th><input type="checkbox" id="select-all" data-action="toggle-all"></th>
<th>Player</th>
<th>Contact</th>
<th>Attendance</th>
@@ -70,12 +70,20 @@
</div>
</div>
<script>
function toggleAll(master) {
{% endblock %}
{% block scripts %}
<script nonce="{{ csp_nonce }}">
function toggleAll() {
var boxes = document.querySelectorAll('.player-check');
var master = document.getElementById('select-all');
for (var i = 0; i < boxes.length; i++) {
boxes[i].checked = master.checked;
}
}
registerActions({
'toggle-all': toggleAll,
});
</script>
{% endblock %}
+3
View File
@@ -300,6 +300,9 @@
</div>
</div>
{% endblock %}
{% block scripts %}
<script nonce="{{ csp_nonce }}">
function showCreateForm() {
document.getElementById('createTeamForm').classList.remove('hidden');
+14 -7
View File
@@ -67,15 +67,22 @@
</select>
</div>
<div class="form-group col-6">
<label for="manager_id">{{ _('Assigned Manager') }}</label>
<select id="manager_id" name="manager_id" class="form-select">
<option value="">{{ _('-- No manager assigned --') }}</option>
<label>{{ _('Assigned Managers') }}</label>
<div class="checkbox-grid">
{% for manager in managers %}
<option value="{{ manager.id }}" {% if tryout and tryout.manager_id == manager.id %}selected{% endif %}>
{{ manager.username }}
</option>
{% set is_checked = false %}
{% if tryout %}
{% for m in tryout.get_managers() %}
{% if m.id == manager.id %}{% set is_checked = true %}{% endif %}
{% endfor %}
</select>
{% endif %}
<label class="checkbox-label">
<input type="checkbox" name="manager_ids" value="{{ manager.id }}" {% if is_checked %}checked{% endif %}>
<span>{{ manager.username }}</span>
</label>
{% endfor %}
</div>
<small class="text-muted">{{ _('Select one or more managers for this tryout.') }}</small>
</div>
</div>
<div class="form-row">
+15 -6
View File
@@ -70,16 +70,22 @@
<span class="detail-value">{{ tryout.target_org_team.name if tryout.target_org_team else 'Not specified' }}</span>
</div>
<div class="detail-item">
<span class="detail-label">Manager</span>
<span class="detail-value">{{ tryout.manager.username if tryout.manager else 'Not assigned' }}</span>
<span class="detail-label">Manager(s)</span>
<span class="detail-value">
{% set mgrs = tryout.get_managers() %}
{% if mgrs %}
{{ mgrs | map(attribute='username') | join(', ') }}
{% else %}
Not assigned
{% endif %}
</span>
</div>
<div class="detail-item">
<span class="detail-label">Coaches</span>
<span class="detail-value">
{% if tryout.coaches %}
{{ tryout.coaches | map(attribute='username') | join(', ') }}
{% elif tryout.coach %}
{{ tryout.coach.username }}
{% set cos = tryout.get_coaches() %}
{% if cos %}
{{ cos | map(attribute='username') | join(', ') }}
{% else %}
Not assigned
{% endif %}
@@ -534,6 +540,9 @@
{% endif %}
</div>
{% endblock %}
{% block scripts %}
<style>
.presence-toggle-btn {
padding: 2px 7px;
+1
View File
@@ -949,6 +949,7 @@ class TryoutSchema(StripMixin):
target_org_team_id = fields.Integer(allow_none=True, load_default=None)
manager_id = fields.Integer(allow_none=True, load_default=None)
coach_ids = fields.List(fields.Integer(), load_default=list)
manager_ids = fields.List(fields.Integer(), load_default=list)
@validates_schema
def validate_span(self, data, **kwargs):