@@ -1999,7 +1999,7 @@ def create_game_history_entry(user, match):
19991999 user = user ,
20002000 match = match ,
20012001 defaults = {
2002- "is_completed" : match .lobby .match_status == 3 , # Lobby.Finished
2002+ "is_completed" : match .lobby .match_status == Lobby . Finished , # Lobby.Finished is 3
20032003 "result" : outcome ,
20042004 },
20052005 )
@@ -2031,7 +2031,8 @@ def get(self, request, game_id):
20312031 if start_date :
20322032 entries = entries .filter (date_played__gte = start_date )
20332033
2034- entries = entries .select_related ("match" ).order_by ("-date_played" )
2034+ # Order by date_played ascending for accurate streak calculations first
2035+ ordered_entries_for_streaks = entries .select_related ("match" ).order_by ("date_played" )
20352036
20362037 # Calculate statistics
20372038 total_played = entries .count ()
@@ -2042,16 +2043,35 @@ def get(self, request, game_id):
20422043
20432044 win_rate = (wins / total_played ) * 100 if total_played > 0 else 0
20442045
2046+ longest_win_streak = self ._calculate_longest_streak (ordered_entries_for_streaks , GameHistory .WIN )
2047+ longest_loss_streak = self ._calculate_longest_streak (ordered_entries_for_streaks , GameHistory .LOSE )
2048+ current_streak_type , current_streak_count = self ._calculate_current_streak (ordered_entries_for_streaks )
2049+ average_playtime_seconds = self ._calculate_average_playtime (entries ) # Can use original entries for this
2050+ average_playtime = (
2051+ timedelta (seconds = average_playtime_seconds ) if average_playtime_seconds is not None else None
2052+ )
2053+
2054+ # For opponent-based stats, we can use the original entries collection
2055+ opponent_stats = self ._calculate_opponent_stats (request .user , entries )
2056+
20452057 context = {
20462058 "game" : game ,
2047- "entries" : entries ,
2059+ "entries" : entries . select_related ( "match" ). order_by ( "-date_played" ), # Re-order for display
20482060 "selected_period" : period ,
20492061 "total_played" : total_played ,
20502062 "wins" : wins ,
20512063 "losses" : losses ,
20522064 "draws" : draws ,
20532065 "incomplete" : incomplete ,
20542066 "win_rate" : win_rate ,
2067+ "longest_win_streak" : longest_win_streak ,
2068+ "longest_loss_streak" : longest_loss_streak ,
2069+ "current_streak_type" : current_streak_type ,
2070+ "current_streak_count" : current_streak_count ,
2071+ "average_playtime" : average_playtime ,
2072+ "most_played_with" : opponent_stats ["most_played_with" ],
2073+ "favorite_opponent" : opponent_stats ["favorite_opponent" ],
2074+ "nemesis_opponent" : opponent_stats ["nemesis_opponent" ],
20552075 "period_options" : [
20562076 {"value" : "all" , "label" : "All Time" },
20572077 {"value" : "7days" , "label" : "Last 7 Days" },
@@ -2063,6 +2083,101 @@ def get(self, request, game_id):
20632083 }
20642084 return render (request , "games/game_history.html" , context )
20652085
2086+ def _calculate_longest_streak (self , entries , outcome_type ):
2087+ max_streak = 0
2088+ current_streak = 0
2089+ for entry in entries :
2090+ if entry .result == outcome_type :
2091+ current_streak += 1
2092+ else :
2093+ max_streak = max (max_streak , current_streak )
2094+ current_streak = 0
2095+ max_streak = max (max_streak , current_streak ) # Final check
2096+ return max_streak
2097+
2098+ def _calculate_current_streak (self , entries ):
2099+ if not entries :
2100+ return "N/A" , 0
2101+
2102+ # .last() requires an ordered queryset
2103+ last_entry = entries .last ()
2104+ if not last_entry or last_entry .result not in [GameHistory .WIN , GameHistory .LOSE ]:
2105+ return "N/A" , 0 # Not a win or loss
2106+
2107+ streak_type = last_entry .result
2108+ current_streak_count = 0
2109+
2110+ # Iterate in reverse order from the original ascending sort for streaks
2111+ for entry in reversed (entries ):
2112+ if entry .result == streak_type :
2113+ current_streak_count += 1
2114+ else :
2115+ break
2116+
2117+ streak_label = "Win" if streak_type == GameHistory .WIN else "Loss"
2118+ return streak_label , current_streak_count
2119+
2120+ def _calculate_average_playtime (self , entries ):
2121+ total_duration_seconds = 0
2122+ completed_games_count = 0
2123+ for entry in entries :
2124+ if entry .is_completed and entry .match and entry .match .start_time and entry .match .end_time :
2125+ duration = entry .match .end_time - entry .match .start_time
2126+ total_duration_seconds += duration .total_seconds ()
2127+ completed_games_count += 1
2128+
2129+ if completed_games_count > 0 :
2130+ return total_duration_seconds / completed_games_count
2131+ return None
2132+
2133+ def _calculate_opponent_stats (self , current_user , entries ):
2134+ opponent_play_count = {}
2135+ opponent_win_count = {}
2136+ opponent_loss_count = {}
2137+
2138+ for entry in entries :
2139+ match = entry .match
2140+ if not match :
2141+ continue
2142+
2143+ opponents_in_match = [player for player in match .players .all () if player != current_user ]
2144+
2145+ for opponent in opponents_in_match :
2146+ opponent_play_count [opponent ] = opponent_play_count .get (opponent , 0 ) + 1
2147+
2148+ if entry .result == GameHistory .WIN :
2149+ opponent_win_count [opponent ] = opponent_win_count .get (opponent , 0 ) + 1
2150+ elif entry .result == GameHistory .LOSE :
2151+ opponent_loss_count [opponent ] = opponent_loss_count .get (opponent , 0 ) + 1
2152+
2153+ most_played_with_opponent = None
2154+ if opponent_play_count :
2155+ most_played_with_opponent_user = max (opponent_play_count , key = opponent_play_count .get )
2156+ most_played_with_opponent = {
2157+ "user" : most_played_with_opponent_user ,
2158+ "count" : opponent_play_count [most_played_with_opponent_user ],
2159+ }
2160+
2161+ favorite_opponent_user = None
2162+ if opponent_win_count :
2163+ favorite_opponent_user_candidate = max (opponent_win_count , key = opponent_win_count .get )
2164+ max_wins_against = opponent_win_count [favorite_opponent_user_candidate ]
2165+ if max_wins_against > 0 :
2166+ favorite_opponent_user = {"user" : favorite_opponent_user_candidate , "count" : max_wins_against }
2167+
2168+ nemesis_opponent_user = None
2169+ if opponent_loss_count :
2170+ nemesis_opponent_user_candidate = max (opponent_loss_count , key = opponent_loss_count .get )
2171+ max_losses_against = opponent_loss_count [nemesis_opponent_user_candidate ]
2172+ if max_losses_against > 0 :
2173+ nemesis_opponent_user = {"user" : nemesis_opponent_user_candidate , "count" : max_losses_against }
2174+
2175+ return {
2176+ "most_played_with" : most_played_with_opponent ,
2177+ "favorite_opponent" : favorite_opponent_user ,
2178+ "nemesis_opponent" : nemesis_opponent_user ,
2179+ }
2180+
20662181
20672182class MatchStatsView (DetailView ):
20682183 model = Tournament
0 commit comments