diff --git a/competition_page.py b/competition_page.py index c394d9225f689d8c5b9e54ae934378eb91f761aa..ca083809fd1e0979e9a0947b8748ef84137ab7f2 100644 --- a/competition_page.py +++ b/competition_page.py @@ -98,7 +98,7 @@ def get_competition_page_html(self, competition_id): height: auto; }} - a {{ + a {{< color: black; transition: color 0.3s ease; }} @@ -163,14 +163,12 @@ def get_competition_page_html(self, competition_id): <p><a href="../index.html">← Back to World Ranking</a></p> """ - html_content += """ + html_content += f""" </div> - <!-- <div class="chart-container"> - fancy diagram here + <img src="../images/{competition_id}_results.{self.IMAGE_FORMAT}" alt="{self.get_full_competition_name(competition_id)} Results"> </div> - --> <h2>Results</h2> <table> diff --git a/k-factor evaluation.ods b/k-factor evaluation.ods new file mode 100644 index 0000000000000000000000000000000000000000..0065d6f6b71788db06479e0fda953c1a98db0a67 Binary files /dev/null and b/k-factor evaluation.ods differ diff --git a/main.py b/main.py index e1e0abb3bd5689ecfdc4fc138cda3febdccbbbb3..573293a84ea392a91726bc9fb0b3db9626f5c128 100644 --- a/main.py +++ b/main.py @@ -18,6 +18,7 @@ from world_ranking_list import get_world_ranking_html # Constants for Elo calculation K_FACTOR = 15 INITIAL_ELO = 1500 # Starting Elo for all teams +IMAGE_FORMAT = "webp" def expected_score(rating_a, rating_b): @@ -50,6 +51,7 @@ class EloSystem: raise with open("header-snippet.html") as header_snippet: self.header_snippet = header_snippet.read() + self.IMAGE_FORMAT = IMAGE_FORMAT def load_competition_meta_data(self): if self.competition_type == "combustion": @@ -246,8 +248,10 @@ class EloSystem: self.create_world_ranking() - for competition in self.competition_history: - self.create_competition_page(competition) + #for competition in self.competition_history: + # self.create_competition_page(competition) + + pool.map(self.create_competition_page, self.competition_history) pool.map(self.create_team_page, self.teams) @@ -288,6 +292,9 @@ class EloSystem: """Create a dedicated page for a specific team""" print(f"Generating Competition site for {competition_id}") + # Competition results as bar chart + self.create_competition_result_chart(competition_id) + html_content = get_competition_page_html(self, competition_id) # Write the HTML file @@ -367,7 +374,7 @@ class EloSystem: # plt.tight_layout() # Save the chart - plt.savefig(os.path.join(self.output_directory, 'images', 'top_teams_elo.png'), dpi=100) + plt.savefig(os.path.join(self.output_directory, 'images', 'top_teams_elo.' + self.IMAGE_FORMAT), dpi=100) plt.close() def create_elo_distribution_chart(self): @@ -443,12 +450,11 @@ class EloSystem: plt.tight_layout() # Save the chart - plt.savefig(os.path.join(self.output_directory, 'images', 'elo_distribution.png'), dpi=100) + plt.savefig(os.path.join(self.output_directory, 'images', 'elo_distribution.' + self.IMAGE_FORMAT), dpi=100) plt.close() def create_team_elo_history_chart(self, team_name): """Create a line chart showing a team's Elo history over time""" - plt.figure(figsize=(10, 6)) team_history = self.teams[team_name]['history'] @@ -457,13 +463,28 @@ class EloSystem: dates = [entry['date'] for entry in team_history] elo_values = [entry['new_elo'] for entry in team_history] + ranks = [entry['new_rank'] for entry in team_history] + + # Create the first plot with elo_values + fig, ax2 = plt.subplots(figsize=(12, 6)) - # Create line chart - plt.plot(range(len(dates)), elo_values, marker='o', linestyle='-', color='gray') + ax2.plot(range(len(dates)), ranks, marker='none', linestyle='-', color='gray', label='Rank') + ax2.set_ylabel('Rank', color='gray') + ax2.tick_params(axis='y', labelcolor='gray') + ax2.invert_yaxis() + ax2.set_xticks(range(len(dates))) + ax2.set_xticklabels(dates, rotation=45, ha='right') # ha='right' helps with alignment + + # Create a second y-axis with the same x-axis + ax1 = ax2.twinx() + ax1.plot(range(len(dates)), elo_values, marker='o', linestyle='-', color='black', label='ELO Values') + ax1.set_xlabel('Dates') + ax1.set_ylabel('ELO Rating', color='black') + ax1.tick_params(axis='y', labelcolor='black') # Add competition labels for i, entry in enumerate(team_history): - plt.annotate(entry['competition'] + f" (#{entry['placement']})", + ax1.annotate(entry['competition'] + f" (#{entry['placement']})", (i, entry['new_elo']), textcoords="offset points", xytext=(0, 10), @@ -471,17 +492,84 @@ class EloSystem: fontsize=8, rotation=45) - plt.xticks(range(len(dates)), dates, rotation=45) - plt.ylabel('Elo Rating') plt.title(f'{team_name} - Elo Rating History') - plt.grid(True, alpha=0.3) + ax2.grid(True, axis='x', alpha=0.3) + ax1.grid(True, alpha=0.3) plt.tight_layout() # Save the chart - plt.savefig(os.path.join(self.output_directory, 'images', f'{clean_filename(team_name)}_history.png'), + plt.savefig(os.path.join(self.output_directory, 'images', f'{clean_filename(team_name)}_history.' + self.IMAGE_FORMAT), dpi=100) plt.close() + def create_competition_result_chart(self, competition_id): + """Create a bar chart of the top teams by Elo rating""" + + competition = self.competition_history[competition_id] + + team_names = [result[1] for result in competition['results']] + team_names = team_names[::-1] + elo_change = [result[3] for result in competition['results']] + elo_change = elo_change[::-1] + + fig = plt.figure(figsize=(12, len(team_names)*0.2175+2)) + ax = fig.add_axes([0.3, 0.1, 0.6, 0.8]) + + # Create bar chart + bars = plt.barh(team_names, elo_change, color='none') + + # Add Elo values at the end of each bar + for i, bar in enumerate(bars): + plt.text(bar.get_width() + 5, bar.get_y() + bar.get_height() / 2, + f"{int(elo_change[i])}", va='center') + + # Create custom colormap + colors = [hex_to_normalized_rgb(self.LINK_COLOR), hex_to_normalized_rgb(self.BORDER_COLOR)] # RGB values for #ffff1e and #ffc400 + custom_yellow_cmap = LinearSegmentedColormap.from_list('custom_yellow', colors) + + for bar in bars: + # Get the coordinates of the bar + x0, y0 = bar.get_xy() + width = bar.get_width() + height = bar.get_height() + + # Create a gradient for the bar + gradient = np.linspace(0, 1, 100).reshape(1, -1) + + # Get colors from the colormap + colors = custom_yellow_cmap(gradient) + + # Set the bar's face color to the gradient + bar.set_facecolor('none') # Remove default color + + # Add the gradient rectangle + gradient_rect = patches.Rectangle((x0, y0), width, height, + linewidth=0, + fill=True, + facecolor=custom_yellow_cmap(0.5)) + + # To create a gradient, add an axial gradient transform + gradient_rect.set_facecolor('none') + ax.add_patch(gradient_rect) + + # Create multiple thin rectangles to simulate a gradient + steps = 50 + for i in range(steps): + left = x0 + (i/steps) * width + rect_width = width/steps + color = custom_yellow_cmap(i/steps) + rect = patches.Rectangle((left, y0), rect_width, height, + linewidth=0, + color=color) + ax.add_patch(rect) + + plt.xlabel('Elo Gain') + plt.title('ELo Gain per Team') + + # Save the chart + plt.savefig(os.path.join(self.output_directory, 'images', competition_id + '_results.' + self.IMAGE_FORMAT), dpi=100) + plt.close() + def optimize_k(self): self.load_and_process_csv_files() error_history = [] diff --git a/team_page.py b/team_page.py index 2529757751727bcbcbbfc30fdba87b62fe108418..79cdb44318114ee5c93453d3ad6235ccfdca5aa1 100644 --- a/team_page.py +++ b/team_page.py @@ -163,18 +163,17 @@ def get_team_page_html(self, team_name, team_data, team_competitions): </div> """ - html_content += """ + html_content += f""" </div> <div class="chart-container"> - <img src="../images/{}_history.png" alt="{} Elo History"> + <img src="../images/{clean_filename(team_name)}_history.{self.IMAGE_FORMAT}" alt="{team_name} Elo History"> </div> <h2>Competition History</h2> <div style="overflow: scroll;"> <table> <tr> - <th>Date</th> <th>Comp</th> <th>Placement</th> <th>Rank Before</th> @@ -184,7 +183,7 @@ def get_team_page_html(self, team_name, team_data, team_competitions): <th>Elo After</th> <th>Elo Change</th> </tr> - """.format(clean_filename(team_name), team_name) + """ # Add rows for each competition for comp in team_competitions: @@ -195,8 +194,7 @@ def get_team_page_html(self, team_name, team_data, team_competitions): html_content += f""" <tr> - <td>{comp['date']}</td> - <td><a href=../comp/{comp['competition_id']}.html>{comp['competition']}</a></td> + <td><a href=../comp/{comp['competition_id']}.html>{comp['competition']} {comp['date'][2:4]}</a></td> <td>{comp['placement']} / {comp['participants']}</td> <td>{comp['old_rank']}</td> <td>{comp['new_rank']}</td> diff --git a/world_ranking_list.py b/world_ranking_list.py index 067f6ec24c174e3be7481c8e1acca6189074ac6c..e42c0deed157187b7caa356ad5642864961fa253 100644 --- a/world_ranking_list.py +++ b/world_ranking_list.py @@ -210,7 +210,7 @@ def get_world_ranking_html(self, sorted_teams): <p>Rankings based on {len(self.competition_history)} competitions.</p> <div class="chart-container"> - <img src="images/top_teams_elo.png" alt="Top Teams Elo Chart"> + <img src="images/top_teams_elo.{self.IMAGE_FORMAT}" alt="Top Teams Elo Chart"> </div> <h2>Current Rankings</h2> @@ -234,16 +234,16 @@ def get_world_ranking_html(self, sorted_teams): <td>{team_data['competitions']}</td> </tr>""" - html_content += """ + html_content += f""" </table> <div> <div class="chart-container"> - <img src="images/elo_distribution.png" alt="Elo Distribution"> + <img src="images/elo_distribution.{self.IMAGE_FORMAT}" alt="Elo Distribution"> </div> <div class="timestamp"> - <p>Generated on {}</p> + <p>Generated on {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}</p> </div> <div style="display: flex; justify-content: center;"> @@ -252,6 +252,6 @@ def get_world_ranking_html(self, sorted_teams): </div> </body> </html> - """.format(datetime.now().strftime("%Y-%m-%d %H:%M:%S")) + """ return html_content \ No newline at end of file