From f7ea4409269914173df68da5e1549ed12192496e Mon Sep 17 00:00:00 2001
From: maeries <8ij4neqiu@mozmail.com>
Date: Fri, 21 Mar 2025 16:07:43 +0100
Subject: [PATCH] added an overview page for every competition

---
 competition_page.py   | 182 ++++++++++++++++++++++++++++++++++++++++++
 main.py               |  59 +++++++++-----
 team_page.py          |   6 +-
 world_ranking_list.py |   4 +-
 4 files changed, 227 insertions(+), 24 deletions(-)
 create mode 100644 competition_page.py

diff --git a/competition_page.py b/competition_page.py
new file mode 100644
index 0000000..adcb53a
--- /dev/null
+++ b/competition_page.py
@@ -0,0 +1,182 @@
+from datetime import datetime
+from tools import clean_filename
+
+def get_competition_page_html(self, competition_id):
+    competition_data = self.competition_history[competition_id]
+    full_competition_name = self.get_full_competition_name(competition_id)
+    html_content = f"""<!DOCTYPE html>
+    <html lang="en">
+    <head>
+        <meta charset="UTF-8">
+        <meta name="viewport" content="width=device-width, initial-scale=1.0">
+        <title>{full_competition_name} - Results</title>
+        {self.header_snippet}
+        <style>
+            body {{
+                font-family: Arial, sans-serif;
+                line-height: 1.6;
+                margin: 0;
+                padding: 20px;
+                max-width: 1200px;
+                margin: 0 auto;
+            }}
+            h1, h2 {{
+                color: #333;
+                animation: fadeInUp 0.5s ease-out forwards;
+            }}
+            table {{
+                border-collapse: collapse;
+                width: 100%;
+                margin-top: 20px;
+                box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
+                border-radius: 5px;
+                overflow: hidden;
+                animation: fadeInUp 0.5s ease-out forwards;
+                opacity: 0;
+            }}
+
+            th, td {{
+                padding: 12px 15px;
+                text-align: left;
+            }}
+
+            th {{
+                background-color: #f2f2f2;
+                color: #333;
+                font-weight: bold;
+                text-transform: uppercase;
+                border-bottom: 2px solid {self.BORDER_COLOR};
+            }}
+
+            tr {{
+                background-color: white;
+                transition: background-color 0.3s ease;
+            }}
+
+            tr:nth-child(even) {{
+                background-color: #f9f9f9;
+            }}
+
+            tr:hover {{
+                background-color: #f5f5f5;
+            }}
+            p {{
+                animation: fadeInUp 0.5s ease-out forwards;
+            }}
+            .chart-container {{
+                margin-top: 30px;
+                display: flex;
+                justify-content: center;
+                width: 100%;
+                animation: fadeInUp 0.5s ease-out forwards;
+                opacity: 0;
+                box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
+                border-radius: 5px;
+                overflow: hidden;
+                background-color: white;
+                padding: 20px;
+                box-sizing: border-box;
+            }}
+            .chart-container img {{
+                max-width: 100%;
+                height: auto;
+            }}
+            a {{
+                color: {self.LINK_COLOR};
+                text-decoration: none;
+                transition: color 0.3s ease;
+            }}
+            a:hover {{
+                color: {self.LINK_COLOR_HOVER};
+                text-decoration: none;
+            }}
+            .summary-stats {{
+                display: flex;
+                justify-content: space-around;
+                margin: 20px 0;
+                flex-wrap: wrap;
+                animation: fadeInUp 0.5s ease-out forwards;
+            }}
+            .stat-box {{
+                padding: 15px;
+                border: 1px solid #ddd;
+                border-radius: 5px;
+                margin: 10px;
+                flex: 1;
+                min-width: 150px;
+                text-align: center;
+                background-color: #f9f9f9;
+            }}
+            .stat-value {{
+                font-size: 24px;
+                font-weight: bold;
+                color: #333;
+            }}
+            .stat-label {{
+                font-size: 14px;
+                color: #666;
+            }}
+            .positive {{
+                color: green;
+            }}
+            .negative {{
+                color: red;
+            }}
+            @keyframes fadeInUp {{
+                from {{
+                    opacity: 0;
+                    transform: translateY(20px);
+                }}
+                to {{
+                    opacity: 1;
+                    transform: translateY(0);
+                }}
+            }}
+        </style>
+    </head>
+    <body>
+        <h1>{full_competition_name} - Results</h1>
+        <p><a href="../index.html">← Back to World Ranking</a></p>
+    """
+
+    html_content += """
+        </div>
+
+        <!--
+        <div class="chart-container">
+            fancy diagram here
+        </div>
+        -->
+
+        <h2>Results</h2>
+        <table>
+            <tr>
+                <th>Placement</th>
+                <th>Team</th>
+                <th>Elo Before</th>
+                <th>Elo Change</th>
+            </tr>
+    """
+
+    # Add rows for each competition
+    for team in competition_data['results']:
+        elo_change = team[3]
+        change_class = "positive" if elo_change > 0 else "negative" if elo_change < 0 else ""
+
+        html_content += f"""
+            <tr>
+                <td>{team[0]}</td>
+                <td><a href="../team/{clean_filename(team[1])}.html">{team[1]}</a></td>
+                <td>{int(team[2])}</td>
+                <td class="{change_class}">{'+' if elo_change > 0 else ''}{elo_change:.1f}</td>
+            </tr>"""
+
+    html_content += """
+        </table>
+
+        <p><small>Generated on {}</small></p>
+    </body>
+    </html>
+    """.format(datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
+
+    return html_content
\ No newline at end of file
diff --git a/main.py b/main.py
index b585c1e..4bc3d11 100644
--- a/main.py
+++ b/main.py
@@ -10,6 +10,7 @@ import numpy as np
 import pandas as pd
 from matplotlib.colors import LinearSegmentedColormap
 
+from competition_page import get_competition_page_html
 from team_page import get_team_page_html
 from tools import *
 from world_ranking_list import get_world_ranking_html
@@ -62,16 +63,21 @@ class EloSystem:
         """Create output directory if it doesn't exist"""
         os.makedirs(self.output_directory, exist_ok=True)
         os.makedirs(os.path.join(self.output_directory, 'images'), exist_ok=True)
+        os.makedirs(os.path.join(self.output_directory, 'team'), exist_ok=True)
+        os.makedirs(os.path.join(self.output_directory, 'comp'), exist_ok=True)
 
-    def get_competition_name(self, id):
-        return self.competition_meta_data[self.competition_meta_data["id"] == int(id)]['abbr'].values[0].strip()
+    def get_competition_name(self, competition_id):
+        return self.competition_meta_data[self.competition_meta_data["id"] == int(competition_id)]['abbr'].values[0].strip()
 
-    def get_competition_date(self, id):
-        date_string = self.competition_meta_data[self.competition_meta_data["id"] == int(id)]['date'].values[0]
+    def get_full_competition_name(self, competition_id):
+        return self.competition_meta_data[self.competition_meta_data["id"] == int(competition_id)]['name'].values[0].strip()
+
+    def get_competition_date(self, competition_id):
+        date_string = self.competition_meta_data[self.competition_meta_data["id"] == int(competition_id)]['date'].values[0]
         return datetime.strptime(date_string, "%Y-%m-%d")
     
-    def get_competition_type(self, id):
-        return self.competition_meta_data[self.competition_meta_data["id"] == int(id)]['kind'].values[0].strip()
+    def get_competition_type(self, competition_id):
+        return self.competition_meta_data[self.competition_meta_data["id"] == int(competition_id)]['kind'].values[0].strip()
 
     def load_and_process_csv_files(self):
         """Load all CSV files and process them to calculate Elo ratings"""
@@ -117,7 +123,7 @@ class EloSystem:
                                     'history': [],
                                     'delta_elo': 0
                                 }
-                            teams_in_competition.append((rank, team_name))
+                            teams_in_competition.append([rank, team_name, None, None])
             except FileNotFoundError:
                 print(f"file {csv_file} not found: skipping")
                 continue
@@ -158,18 +164,25 @@ class EloSystem:
         # Store post-competition Elo ratings and changes
         for team in teams_in_order:
             rank = teams_in_order.index(team) + 1
+            previous_elo = self.teams[team]['elo']
+            elo_change = self.teams[team]['delta_elo']
 
             # Store in team history
             self.teams[team]['history'].append({
                 'date': date.strftime("%Y-%m-%d"),
                 'competition': self.get_competition_name(competition_id),
+                'competition_id': competition_id,
                 'rank': rank,
                 'participants': len(teams_in_order),
-                'previous_elo': self.teams[team]['elo'],
+                'previous_elo': previous_elo,
                 'new_elo': self.teams[team]['elo'] + self.teams[team]['delta_elo'],
-                'elo_change': self.teams[team]['delta_elo']
+                'elo_change': elo_change
             })
 
+            # Store in competition history
+            self.competition_history[competition_id]['results'][rank-1][2] = previous_elo
+            self.competition_history[competition_id]['results'][rank-1][3] = elo_change
+
             self.teams[team]['elo'] = self.teams[team]['elo'] + self.teams[team]['delta_elo']
             self.teams[team]['delta_elo'] = 0
 
@@ -208,22 +221,19 @@ class EloSystem:
 
     def generate_website(self):
         """Generate static HTML website with Elo ratings and visualizations"""
-        # Create main index page
-        self.create_world_ranking()
-
-        # # Create team-specific pages
-        # for team_name in self.teams:
-        #     self.create_team_page(team_name)
 
         # If num_processes not specified, use number of CPU cores
         num_processes = int(mp.cpu_count() / 2)
         print(f"staring computation on {num_processes} cores")
-
-        # Create a pool of workers
         pool = mp.Pool(processes=num_processes)
+
+        self.create_world_ranking()
+
+        for competition in self.competition_history:
+            self.create_competition_page(competition)
+
         pool.map(self.create_team_page, self.teams)
 
-        # Create visualizations
         self.create_visualizations()
 
         print(f"Website generated in {self.output_directory}")
@@ -253,7 +263,18 @@ class EloSystem:
         html_content = get_team_page_html(self, team_name, team_data, team_competitions)
 
         # Write the HTML file
-        with open(os.path.join(self.output_directory, f'team_{clean_filename(team_name)}.html'), 'w',
+        with open(os.path.join(self.output_directory, 'team', f'{clean_filename(team_name)}.html'), 'w',
+                  encoding='utf-8') as f:
+            f.write(html_content)
+
+    def create_competition_page(self, competition_id):
+        """Create a dedicated page for a specific team"""
+        print(f"Generating Competition site for {competition_id}")
+
+        html_content = get_competition_page_html(self, competition_id)
+
+        # Write the HTML file
+        with open(os.path.join(self.output_directory, 'comp', f'{str(competition_id)}.html'), 'w',
                   encoding='utf-8') as f:
             f.write(html_content)
 
diff --git a/team_page.py b/team_page.py
index e603b7e..e62d74a 100644
--- a/team_page.py
+++ b/team_page.py
@@ -134,7 +134,7 @@ def get_team_page_html(self, team_name, team_data, team_competitions):
     </head>
     <body>
         <h1>{team_name} - Elo Profile</h1>
-        <p><a href="index.html">← Back to Rankings</a></p>
+        <p><a href="../index.html">← Back to World Ranking</a></p>
 
         <div class="summary-stats">
             <div class="stat-box">
@@ -167,7 +167,7 @@ def get_team_page_html(self, team_name, team_data, team_competitions):
         </div>
 
         <div class="chart-container">
-            <img src="images/{}_history.png" alt="{} Elo History">
+            <img src="../images/{}_history.png" alt="{} Elo History">
         </div>
 
         <h2>Competition History</h2>
@@ -190,7 +190,7 @@ def get_team_page_html(self, team_name, team_data, team_competitions):
         html_content += f"""
             <tr>
                 <td>{comp['date']}</td>
-                <td>{comp['competition']}</td>
+                <td><a href=../comp/{comp['competition_id']}.html>{comp['competition']}</a></td>
                 <td>{comp['rank']} / {comp['participants']}</td>
                 <td>{int(comp['previous_elo'])}</td>
                 <td>{int(comp['new_elo'])}</td>
diff --git a/world_ranking_list.py b/world_ranking_list.py
index a530828..4c270a1 100644
--- a/world_ranking_list.py
+++ b/world_ranking_list.py
@@ -216,7 +216,7 @@ def get_world_ranking_html(self, sorted_teams):
         html_content += f"""
             <tr>
                 <td>{rank}</td>
-                <td><a href="team_{clean_filename(team_name)}.html">{team_name}</a></td>
+                <td><a href="team/{clean_filename(team_name)}.html">{team_name}</a></td>
                 <td>{int(team_data['elo'])}</td>
                 <td>{team_data['competitions']}</td>
             </tr>"""
@@ -233,7 +233,7 @@ def get_world_ranking_html(self, sorted_teams):
             </div>
 
             <div style="display: flex; justify-content: center;">
-                <a href="index.html"><button class="nav-button">Back to Home</button></a>
+                <a href="../index.html"><button class="nav-button">Back to Home</button></a>
             </div>
         </div>
     </body>
-- 
GitLab