progression 50% pour evolution des joueurs
CI - Security, Lint & Tests / validate (push) Failing after 1m13s

This commit is contained in:
cedrick2711
2026-09-05 11:30:13 -04:00
parent cf551867c9
commit f468217745
22 changed files with 2235 additions and 8 deletions
+21 -1
View File
@@ -21,6 +21,11 @@ from app.models._constants import (
PLATFORM_CODES,
TRN_URLS,
EVALUATION_CRITERIA,
OBJECTIVE_TYPES,
OBJECTIVE_STATUSES,
OBJECTIVE_PRIORITIES,
OBJECTIVE_CHECK_FREQUENCIES,
DEFAULT_OBJECTIVE_TEMPLATES,
)
# =========================================================================
@@ -96,4 +101,19 @@ from app.models.personal_note import PersonalNote
from app.models.one_on_one_request import OneOnOneRequest
from app.models.admin_settings import AppSettings
from app.models.backup_record import BackupRecord
from app.models.audit_log import AuditLog
from app.models.audit_log import AuditLog
# =========================================================================
# Layer 12: player evolution objectives
# =========================================================================
from app.models.objective import Objective # noqa: F401 — new table for create_all()
# =========================================================================
# Layer 13: player evolution entries (KPI measurements)
# =========================================================================
from app.models.objective_entry import ObjectiveEntry # noqa: F401
# =========================================================================
# Layer 14: objective comments (coach feedback, no existing-schema change)
# =========================================================================
from app.models.objective_comment import ObjectiveComment # noqa: F401
+73
View File
@@ -69,6 +69,79 @@ PLATFORM_CODES = {
PLATFORM_DEFAULTS = {'Apex Legends': 'pc', 'Rainbow Six Siege': 'ubi', 'Rocket League': 'epic'}
# Objective-related constants for the Player Evolution planning feature.
# Ordered lists of (value, human_label) pairs for form fields and display.
OBJECTIVE_TYPES = [
('individual', 'Individuel'),
('team', 'Équipe'),
]
OBJECTIVE_STATUSES = [
('active', 'Actif'),
('pending', 'En attente'),
('completed', 'Terminé'),
('overdue', 'En retard'),
]
OBJECTIVE_PRIORITIES = [
('low', 'Faible'),
('medium', 'Moyenne'),
('high', 'Élevée'),
]
OBJECTIVE_CHECK_FREQUENCIES = [
('weekly', 'Hebdomadaire'),
('biweekly', 'Bi-hebdomadaire'),
('monthly', 'Mensuelle'),
]
# Default templates seeded on first run.
# (priority, title, description, kpi_metric, kpi_target, kpi_unit, type, category)
DEFAULT_OBJECTIVE_TEMPLATES = [
# Quantitative
('medium', 'Améliorer le taux de headshot (HS)',
'Augmenter le pourcentage de têtes sur les kills totaux.',
'Taux de headshot', 45.0, '%', 'individual', 'Mécaniques'),
('medium', 'Augmenter le K/D ratio',
'Améliorer le ratio kills/morts sur les matchs compétitifs.',
'K/D Ratio', 1.2, '', 'individual', 'Performance'),
('medium', 'Atteindre un KDA cible',
'Maintenir un ratio kills/deaths/assists au-dessus du seuil.',
'KDA', 3.0, '', 'individual', 'Performance'),
('medium', 'Améliorer la précision',
'Augmenter le pourcentage de tirs touchés par partie.',
'Précision', 40.0, '%', 'individual', 'Mécaniques'),
('medium', 'Augmenter les CS par minute',
'Atteindre une moyenne de last-hits par minute en jeu.',
'CS/min', 8.0, '', 'individual', 'Farm'),
('medium', 'Réduire les morts par match',
'Diminuer le nombre de décès moyens par partie compétitive.',
'Morts/match', 5.0, '', 'individual', 'Survie'),
('medium', 'Augmenter les dégâts par round',
'Améliorer le damage output moyen par round joué.',
'Dégâts/round', 150.0, '', 'individual', 'Performance'),
('medium', 'Améliorer le taux de victoire',
'Augmenter le pourcentage de parties gagnées sur la saison.',
'Winrate', 55.0, '%', 'team', 'Compétitif'),
# Qualitative
('medium', 'Améliorer le positionnement',
'Travailler le placement sur la carte pour réduire les angles exposés.',
None, None, None, 'individual', 'Positionnement'),
('medium', 'Améliorer la communication',
'Être plus vocal et clair dans les callouts en équipe.',
None, None, None, 'individual', 'Communication'),
('medium', 'Développer la conscience de carte',
'Anticiper les rotations adverses et mieux lire la minimap.',
None, None, None, 'individual', 'Game Sense'),
('medium', 'Améliorer la prise de décision',
'Réduire les décisions risquées et améliorer le choix des engagements.',
None, None, None, 'individual', 'Decision'),
('medium', 'Renforcer la cohésion d\'équipe',
'Participer activement aux stratégies collectives et aux drills.',
None, None, None, 'team', 'Équipe'),
]
TRN_URLS = {
'Valorant': 'https://tracker.gg/valorant/profile/riot/{username}',
'League of Legends': 'https://tracker.gg/lol/riot/profile/{username}',
+84
View File
@@ -0,0 +1,84 @@
"""Player evolution objective — individual or team KPI-based goal."""
from app.extensions import db
from app.time_utils import utc_now_naive
class Objective(db.Model):
"""Player evolution objective — a KPI-driven plan for individual players
or teams, created and tracked by coaching staff.
Covers the full lifecycle: draft (pending), active, completed, overdue.
Supports arbitrary KPI metrics, optional tags, and verifiable progress.
"""
__tablename__ = 'objectives'
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(200), nullable=False)
description = db.Column(db.Text, nullable=True)
# Category
type = db.Column(
db.String(20),
nullable=False,
default='individual',
)
status = db.Column(
db.String(20),
nullable=False,
default='active',
)
priority = db.Column(
db.String(20),
nullable=False,
default='medium',
)
category = db.Column(db.String(50), nullable=True)
is_template = db.Column(db.Boolean, default=False, nullable=False)
# Assignment (individual player / org team)
player_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=True)
team_id = db.Column(db.Integer, db.ForeignKey('org_teams.id'), nullable=True)
# KPI fields
kpi_metric = db.Column(db.String(120), nullable=True)
kpi_target = db.Column(db.Numeric(10, 2), nullable=True)
kpi_unit = db.Column(db.String(20), nullable=True)
kpi_precision = db.Column(db.Integer, nullable=True)
# Timeline
start_date = db.Column(db.Date, nullable=True)
due_date = db.Column(db.Date, nullable=False)
check_frequency = db.Column(db.String(30), nullable=True)
# Progress tracking (0–100)
progress = db.Column(db.Integer, default=0, nullable=False)
# Misc
tags = db.Column(db.String(255), nullable=True)
# Who created it
created_by = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
# Timestamps
created_at = db.Column(db.DateTime, default=utc_now_naive)
updated_at = db.Column(db.DateTime, default=utc_now_naive, onupdate=utc_now_naive)
# -----------------------------------------------------------------------
# Relationships
# -----------------------------------------------------------------------
player = db.relationship(
'User',
foreign_keys=[player_id],
backref=db.backref('objectives', lazy='dynamic'),
)
team = db.relationship(
'OrgTeam',
foreign_keys=[team_id],
backref=db.backref('objectives', lazy='dynamic'),
)
creator = db.relationship(
'User',
foreign_keys=[created_by],
)
+35
View File
@@ -0,0 +1,35 @@
"""Coach comments on a specific objective."""
from app.extensions import db
from app.time_utils import utc_now_naive
class ObjectiveComment(db.Model):
"""A coach's comment on a player-evolution objective.
This is a separate table from PersonalNote — no existing schema
is altered. The objective-form page also *reads* PersonalNote and
TeamNote for context, but never writes to those tables.
"""
__tablename__ = 'objective_comments'
id = db.Column(db.Integer, primary_key=True)
objective_id = db.Column(
db.Integer,
db.ForeignKey('objectives.id'),
nullable=False,
)
coach_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
content = db.Column(db.Text, nullable=False)
created_at = db.Column(db.DateTime, default=utc_now_naive)
updated_at = db.Column(db.DateTime, default=utc_now_naive, onupdate=utc_now_naive)
# -----------------------------------------------------------------------
# Relationships
# -----------------------------------------------------------------------
objective = db.relationship(
'Objective',
backref=db.backref('comments', lazy='dynamic', order_by='ObjectiveComment.created_at'),
)
coach = db.relationship('User', foreign_keys=[coach_id])
+39
View File
@@ -0,0 +1,39 @@
"""Player evolution — KPI measurement entries for objectives."""
from app.extensions import db
from app.time_utils import utc_now_naive
class ObjectiveEntry(db.Model):
"""A single KPI measurement recorded for an objective.
Each entry records a value at a point in time so the evolution
graph can plot historical data and compute a trend line.
"""
__tablename__ = 'objective_entries'
id = db.Column(db.Integer, primary_key=True)
objective_id = db.Column(
db.Integer,
db.ForeignKey('objectives.id'),
nullable=False,
)
value = db.Column(db.Numeric(10, 2), nullable=True)
recorded_at = db.Column(db.DateTime, nullable=False, default=utc_now_naive)
note = db.Column(db.Text, nullable=True)
# Who recorded it
recorded_by = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
# Timestamps
created_at = db.Column(db.DateTime, default=utc_now_naive)
# -----------------------------------------------------------------------
# Relationships
# -----------------------------------------------------------------------
objective = db.relationship(
'Objective',
backref=db.backref('entries', lazy='dynamic', order_by='ObjectiveEntry.recorded_at'),
)
recorder = db.relationship('User', foreign_keys=[recorded_by])