From 988cb53de1fc8b6250395812800e6e216b38e93f Mon Sep 17 00:00:00 2001 From: bl3kunja Date: Sat, 18 Apr 2026 19:39:32 +0200 Subject: [PATCH] Fixes to tournament. Refactor analysis and arhive with new treeview. --- tv_app.py => app.py | 52 +- templates/modern_archive_index.html | 1118 +++++++++---------------- templates/modern_player_analysis.html | 616 +++++--------- templates/modern_player_stats.html | 540 ++++++------ templates/tournament.html | 1 + 5 files changed, 952 insertions(+), 1375 deletions(-) rename tv_app.py => app.py (97%) diff --git a/tv_app.py b/app.py similarity index 97% rename from tv_app.py rename to app.py index 17ba32d..4c4bd4d 100644 --- a/tv_app.py +++ b/app.py @@ -524,6 +524,43 @@ def view_player_stats(player_id): # Enhanced API endpoints for the modern archive system +@app.route('/api/archive/tournament//standings') +def api_tournament_standings(filename): + filepath = os.path.join(ARCHIVE_DIR, filename) + data = load_archive_file(filepath) + if not data: + return jsonify({'error': 'not found'}), 404 + results_data = data.get('results', {}) + tournament_type = data.get('tournament', {}).get('tournament_type', '20_targets') + participants = [] + for player_id, p in results_data.get('participants', {}).items(): + targets = p.get('targets', {}) + tens = calculate_tens_from_targets(targets) + participants.append({'name': p['name'], 'total_score': p['total_score'], 'tens_count': tens}) + participants.sort(key=lambda x: (x['total_score'], x['tens_count']), reverse=True) + for i, p in enumerate(participants): + p['rank'] = i + 1 + return jsonify({ + 'kind': 'tournament', + 'tournament_type': tournament_type, + 'created_at': results_data.get('created_at', ''), + 'archived_at': data.get('archived_at', ''), + 'participants': participants + }) + +@app.route('/api/archive/league//standings') +def api_league_standings(filename): + filepath = os.path.join(LEAGUE_ARCHIVE_DIR, filename) + data = load_archive_file(filepath) + if not data: + return jsonify({'error': 'not found'}), 404 + league_data = data.get('league', {}) + tournament_type = league_data.get('tournament_type', '20_targets') + calculate_league_final_scores(league_data) + participants = get_league_final_rankings(league_data) + rows = [{'rank': p['rank'], 'name': p['name'], 'final_score': p['final_score'], 'total_score': p['total_score'], 'tournaments_participated': p['tournaments_participated']} for p in participants] + return jsonify({'kind': 'league', 'tournament_type': tournament_type, 'archived_at': data.get('archived_at', ''), 'participants': rows}) + @app.route('/api/archive/stats', methods=['GET']) def api_get_archive_stats(): """API endpoint to get archive overview statistics""" @@ -583,6 +620,10 @@ def api_get_players_with_stats(): for player in all_players: player_stats = analyze_player_performance(player['id'], archives_data) + total_tens = sum( + player_stats['shot_accuracy'][t]['tens'] + for t in player_stats['shot_accuracy'] + ) players_with_stats.append({ 'id': player['id'], 'name': player['name'], @@ -593,7 +634,8 @@ def api_get_players_with_stats(): 'best_tournament_score': player_stats['best_tournament_score'], 'average_tournament_score': player_stats['average_tournament_score'], 'total_shots_fired': player_stats['total_shots_fired'], - 'performance_trend': player_stats['tournament_scores'][-8:] if len(player_stats['tournament_scores']) >= 8 else player_stats['tournament_scores'] # Last 8 tournaments for mini chart + 'total_tens': total_tens, + 'performance_trend': player_stats['tournament_scores'][-8:] if len(player_stats['tournament_scores']) >= 8 else player_stats['tournament_scores'] } }) @@ -649,12 +691,13 @@ def view_archived_tournament(filename): 'created_at': str(tournament_data.get('created_at', '')) } - # Use the existing results display template but with archived data + embed = request.args.get('embed') == '1' return render_template('results_display.html', tournament=sanitized_tournament, results=results_data, participants=participants, archived=True, + embed=embed, archive_info={ 'filename': filename, 'archived_at': data.get('archived_at'), @@ -679,11 +722,12 @@ def view_archived_league(filename): calculate_league_final_scores(league_data) participants = get_league_final_rankings(league_data) - # Use the existing league results display template but with archived data + embed = request.args.get('embed') == '1' return render_template('league_scoreboard_display.html', league=league_data, participants=participants, archived=True, + embed=embed, archive_info={ 'filename': filename, 'archived_at': data.get('archived_at'), @@ -2497,4 +2541,4 @@ def set_language(language): }), 400 if __name__ == '__main__': - app.run(host='0.0.0.0', port=5000, debug=True) \ No newline at end of file + app.run(host='0.0.0.0', port=4000, debug=True) \ No newline at end of file diff --git a/templates/modern_archive_index.html b/templates/modern_archive_index.html index fdbc1ec..202ba55 100644 --- a/templates/modern_archive_index.html +++ b/templates/modern_archive_index.html @@ -2,7 +2,7 @@ - Arhiv + Arhiv @@ -10,453 +10,216 @@ -
- -
-
- 🎖️ -
{{ leagues|length if leagues else 0 }}
-
Zaključene Lige
+
+ + +
+
+
📚 Turnirji
+
0
-
- 🏆 -
{{ tournaments|length if tournaments else 0 }}
-
Skupaj Turnirjev
-
-
- 💪 -
{{ tournaments|selectattr('tournament_type', 'equalto', '40_targets')|list|length if tournaments else 0 }}
-
40-Tarčni Turnirji
-
-
- -
{{ tournaments|selectattr('tournament_type', 'equalto', '20_targets')|list|length if tournaments else 0 }}
-
20-Tarčni Turnirji
-
-
- 🎯 -
{{ tournaments|selectattr('tournament_type', 'equalto', '4_targets')|list|length if tournaments else 0 }}
-
4-Tarčni Turnirji
-
-
- 👥 -
{{ stats.total_players if stats else 0 }}
-
Omogočeni Igralci
+
+ +
+ + + + + +
+
- - {% if tournaments %} -
-
- 🏆 Turnirji + +
+ +
+
🏆
+
Izberi zapis iz drevesa
- -
- + -
- Filtrer: - - - - + - - - - - - - - - - - - - - {% for tournament in tournaments %} - - - - - - - - {% endfor %} - -
DatumTipIgralciKrogiAkcija
{{ tournament.archived_at[:10] if tournament.archived_at != 'Unknown' else 'Unknown' }} - - {% if tournament.tournament_type == '40_targets' %}💪 40 - {% elif tournament.tournament_type == '20_targets' %}⚡ 20 - {% elif tournament.tournament_type == '4_targets' %}🎯 4 - {% else %}Other{% endif %} - - {{ tournament.participants_count }}{{ tournament.total_rounds | default('N/A') }} - Ogled -
- - -
- {% endif %} - - - {% if leagues %} -
-
- 🎖️ Ligaška Prvenstva -
- - -
- - -
- Filtrer: - - - - -
- - -
- - - - - - - - - - - - - {% for league in leagues %} - - - - - - - - {% endfor %} - -
DatumTipIgralciNapredovanjeAkcija
{{ league.archived_at[:10] if league.archived_at != 'Unknown' else 'Unknown' }} - - {% if league.tournament_type == '40_targets' %}💪 40 - {% elif league.tournament_type == '20_targets' %}⚡ 20 - {% elif league.tournament_type == '4_targets' %}🎯 4 - {% else %}Other{% endif %} - - {{ league.participants_count }}{{ league.completed_tournaments }}/{{ league.total_tournaments }} - Ogled -
- - -
- {% endif %} - -
+
+
+
diff --git a/templates/modern_player_analysis.html b/templates/modern_player_analysis.html index a0f27af..e781b6d 100644 --- a/templates/modern_player_analysis.html +++ b/templates/modern_player_analysis.html @@ -37,7 +37,8 @@ body { font-family: Arial, sans-serif; background: #f5f5f5; - min-height: 100vh; + height: 100vh; + overflow: hidden; color: #333; } @@ -385,6 +386,133 @@ font-weight: 500; } + /* ── PLAYERS SPLIT LAYOUT ── */ + .players-layout { + display: flex; + gap: 8px; + height: calc(100vh - 50px); + padding: 8px 20px 20px 20px; + } + + .players-left-panel { + width: 260px; + flex-shrink: 0; + display: flex; + flex-direction: column; + background: white; + border-radius: 12px; + box-shadow: 0 4px 12px rgba(0,0,0,0.1); + overflow: hidden; + border: 1px solid #e9ecef; + } + + .players-panel-header { + display: flex; align-items: center; justify-content: space-between; + padding: 8px 12px; + background: linear-gradient(135deg, #28a745 0%, #1e7e34 100%); + border-radius: 12px 12px 0 0; + flex-shrink: 0; + } + .players-panel-header-title { + font-size: 0.8rem; font-weight: 700; color: white; letter-spacing: 0.3px; + overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; + } + .players-panel-count { + font-size: 0.7rem; font-weight: 600; + background: rgba(255,255,255,0.25); color: white; + padding: 2px 7px; border-radius: 10px; + } + + .players-sort-bar { + padding: 6px 8px; + border-bottom: 1px solid #e9ecef; + flex-shrink: 0; + background: #f8f9fa; + display: flex; + flex-direction: column; + gap: 5px; + } + .players-search { + width: 100%; border: 1px solid #dee2e6; border-radius: 6px; + padding: 5px 9px; font-size: 0.78rem; outline: none; background: white; + } + .players-search:focus { border-color: #28a745; } + + .players-sort-btns { + display: flex; gap: 4px; + } + .psort-btn { + flex: 1; + padding: 2px 7px; + border: 2px solid #dee2e6; background: white; border-radius: 6px; + cursor: pointer; font-size: 0.6rem; font-weight: 600; color: #666; + transition: all 0.15s; white-space: nowrap; text-align: center; + } + .psort-btn.active { background: #28a745; border-color: #1e7e34; color: white; } + .psort-btn:hover:not(.active) { border-color: #28a745; color: #28a745; } + + .player-tree-list { + flex: 1; overflow-y: auto; overflow-x: hidden; + } + .player-tree-list::-webkit-scrollbar { width: 5px; } + .player-tree-list::-webkit-scrollbar-thumb { background: #d1d5db; border-radius: 3px; } + + .player-tree-item { + display: flex; align-items: center; + padding: 8px 10px 8px 14px; + cursor: pointer; border-left: 4px solid transparent; + gap: 8px; transition: all 0.15s; + } + .player-tree-item:hover { background: #f0faf3; border-left-color: #7dcf95; } + .player-tree-item.active { + background: #28a745; border-left-color: #28a745; + font-weight: 700; color: white; + } + .player-tree-avatar { + width: 28px; height: 28px; border-radius: 50%; + background: #e9ecef; display: flex; align-items: center; justify-content: center; + font-size: 0.75rem; font-weight: 700; color: #666; flex-shrink: 0; + } + .player-tree-item.active .player-tree-avatar { background: rgba(255,255,255,0.25); color: white; } + .player-tree-info { flex: 1; min-width: 0; } + .player-tree-name { font-size: 0.82rem; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } + .player-tree-meta { font-size: 0.65rem; color: #999; } + .player-tree-item.active .player-tree-meta { color: rgba(255,255,255,0.75); } + .player-tree-score { + font-size: 0.75rem; font-weight: 700; color: #28a745; flex-shrink: 0; + } + .player-tree-item.active .player-tree-score { color: rgba(255,255,255,0.9); } + .player-tree-rank { + font-size: 1.1rem; flex-shrink: 0; line-height: 1; + } + .player-tree-rank-num { + width: 24px; height: 24px; border-radius: 50%; + background: #cfe2ff; color: #084298; + display: flex; align-items: center; justify-content: center; + font-size: 0.6rem; font-weight: 700; flex-shrink: 0; + } + .player-tree-item.active .player-tree-rank-num { + background: rgba(255,255,255,0.3); color: white; + } + + .players-right-panel { + flex: 1; overflow: hidden; min-width: 0; + display: flex; flex-direction: column; + } + #playerDetailEmbed { flex: 1; min-height: 0; display: flex; flex-direction: column; } + #playerDetailEmbed iframe { flex: 1; min-height: 0; height: 100%; width: 100%; border: none; border-radius: 12px; box-shadow: 0 4px 12px rgba(0,0,0,0.1); } + + .player-placeholder { + flex: 1; display: flex; flex-direction: column; + align-items: center; justify-content: center; + color: #ccc; gap: 10px; + background: white; border-radius: 12px; + box-shadow: 0 4px 12px rgba(0,0,0,0.1); + border: 1px solid #e9ecef; + } + .player-placeholder-icon { font-size: 2.5rem; } + .player-placeholder-text { font-size: 0.95rem; } + /* Compact Controls */ .controls { display: flex; @@ -707,90 +835,44 @@ -
- -
-
- 👥 -
Loading...
-
Total Players
+
+ + +
+
+
👥 Igralci
+
0
-
- 🎯 -
Loading...
-
Total Tournaments
+
+ +
+ + + +
-
- 🏆 -
Loading...
-
20 Targets
-
-
- 🎖️ -
Loading...
-
40 Targets
-
-
- 🥇 -
Loading...
-
4 Targets
+
+
- -
-
- - -
- - -
-
Overall Champions by Tournament Type
- -
-
-
-

Loading tournament data...

-
-
-
- - -
-
Izberi igralca za analizo
- - -
- - - -
- - -
-
-
-

Loading players...

-
-
+ +
+
+
👤
+
Izberi igralca iz seznama
+
+
@@ -1149,299 +1287,121 @@
-
- -
-
-
-
-
🎯
-
4T
-
{{ stats.best_4_targets_score if stats.best_4_targets_score > 0 else 0 }}
+
+ + +
+ + +
+
+ + + +
+
+
0
Iger
+
0
Najboljši
+
0
Povprečje
+
+
+ +
+ + +
+
+ 🎯 Natančnost strelov + Kako pogosto zadeneš posamezno vrednost
-
-
-
20T
-
{{ stats.best_20_targets_score if stats.best_20_targets_score > 0 else 0 }}
-
-
-
💪
-
40T
-
{{ stats.best_40_targets_score if stats.best_40_targets_score > 0 else 0 }}
+
+
-
Best Scores
-
-
- 🎯 -
0
-
Most Common
-
-
- 🔫 -
{{ stats.total_shots_fired|default(0) }}
-
Total Shots
-
-
- 🏆 -
{{ stats.total_tournaments }}
-
Tournaments
-
-
- 👑 -
{{ stats.total_leagues|default(0) }}
-
Leagues
-
-
- -
-
📊 Overall Shot Accuracy
- - -
- -
- -
- - -
- -
-
- - -
-
-
10
-
0
-
-
-
9
-
0
-
-
-
8
-
0
-
-
-
7
-
0
-
-
-
6
-
0
-
-
-
5
-
0
-
-
-
4
-
0
-
-
-
3
-
0
-
-
-
2
-
0
-
-
-
1
-
0
-
-
-
0
-
0
-
-
-
- - -
-
📊 Filtered Analysis
- - -
- - - -
- - -
-
-
-
-
0
-
Games
+ +
+
+
+
+ 📈 Napredek + Rezultati skozi čas po turnirjih +
+
-
-
0
-
Best
-
-
-
0
-
Average
-
-
-
0
-
Most Common
-
-
-
-
- - -
-
- -
-
- - -
- -
-
-
- +
+
+ 🕸 Profil + Razporeditev strelov +
+
- -
-
-
- +
+ +
+ + +
+
+
📜 Zgodovina
+
+ + +
+
+ +
+ +
+ {% if stats.tournament_history %} + {% for tournament in stats.tournament_history %} +
+
{{ tournament.date[:10] if tournament.date != 'Unknown' else '?' }}
+
+ {{ tournament.tournament_type.replace('_targets','T') }} +
+
{{ tournament.score }}
+
+ {% endfor %} + {% else %} +
🎯
Ni zgodovine
+ {% endif %}
-
-
-
- - -
-
📜 Player History
- - -
- - -
- - -
- {% if stats.tournament_history %} -
- - - - - - - - - - - - {% for tournament in stats.tournament_history %} - - - - - - - - {% endfor %} - -
DateTypeScoreShotsAction
{{ tournament.date[:10] if tournament.date != 'Unknown' else 'Unknown' }}{{ tournament.tournament_type.replace('_', ' ')|title }}{{ tournament.score }}{{ tournament.shots_fired }}View →
-
- {% else %} -
-
🎯
-
No tournament history
-
- {% endif %} -
- - -
- {% if stats.league_history %} -
- - - - - - - - - - - - - - - - - - - - - {% for league in stats.league_history %} - - + +
+ {% if stats.league_history %} + {% for league in stats.league_history %} +
+
{{ league.date[:10] if league.date != 'Unknown' else '?' }}
+
{% if league.tournament_results %} - {% for i in range(league.tournament_results|length) %} - {% set result = league.tournament_results[i] %} - {% set tournament_num = i + 1 %} - {% set is_excluded = league.excluded_tournament and league.excluded_tournament == tournament_num %} - {% set is_joker = result.joker or not result.participated %} -
- {% endfor %} - {% else %} - - - - - + {% for i in range(league.tournament_results|length) %} + {% set result = league.tournament_results[i] %} + {% set is_excluded = league.excluded_tournament and league.excluded_tournament == (i+1) %} + {% set is_joker = result.joker or not result.participated %} + + {% if is_joker %}🃏{% elif is_excluded %}{{ result.score if result else '-' }}{% else %}{{ result.score if result else '-' }}{% endif %} + + {% endfor %} {% endif %} - - - - {% endfor %} - -
DateTournament ScoresFinal ScoreAction
T1T2T3T4T5
{{ league.date[:10] if league.date != 'Unknown' else 'Unknown' }} - {% if is_joker %}🃏{% elif is_excluded %}{{ result.score if result else '-' }}{% else %}{{ result.score if result else '-' }}{% endif %} - -----{{ league.final_score }}View →
-
- {% else %} -
-
🏆
-
No league history
-
- {% endif %} -
-
+
+
{{ league.final_score }}
+
+
+ {% endfor %} + {% else %} +
🏆
Ni zgodovine
+ {% endif %} +
+
+
-
+
diff --git a/templates/tournament.html b/templates/tournament.html index ec564e6..3c5fffc 100644 --- a/templates/tournament.html +++ b/templates/tournament.html @@ -850,6 +850,7 @@ flex-direction: column; gap: 6px; overflow-y: auto; + max-height: 510px; flex: 1; }