Coverage for cli.py: 0.00%

133 statements  

« prev     ^ index     » next       coverage.py v7.10.6, created at 2026-04-13 14:11 +0000

1#!/usr/bin/env python3 

2""" 

3CLI tool for managing the sports league. 

4Quick way to add games and manage data via the backend API. 

5""" 

6 

7import sys 

8from datetime import datetime 

9 

10import httpx 

11import typer 

12from rich.console import Console 

13from rich.prompt import Confirm, IntPrompt, Prompt 

14from rich.table import Table 

15 

16app = typer.Typer() 

17console = Console() 

18 

19# API base URL - adjust if your backend runs on a different port 

20API_BASE_URL = "http://localhost:8000" 

21 

22 

23def get_teams(): 

24 """Fetch all teams from the API.""" 

25 try: 

26 response = httpx.get(f"{API_BASE_URL}/api/teams") 

27 response.raise_for_status() 

28 return response.json() 

29 except httpx.RequestError as e: 

30 console.print(f"[red]Error connecting to API: {e}[/red]") 

31 sys.exit(1) 

32 except httpx.HTTPStatusError as e: 

33 console.print(f"[red]API error: {e}[/red]") 

34 sys.exit(1) 

35 

36 

37@app.command() 

38def add_game( 

39 date: str | None = typer.Option(None, "--date", "-d", help="Game date (YYYY-MM-DD)"), 

40 home_team: str | None = typer.Option(None, "--home", "-h", help="Home team name"), 

41 away_team: str | None = typer.Option(None, "--away", "-a", help="Away team name"), 

42 home_score: int | None = typer.Option(None, "--home-score", "-hs", help="Home team score"), 

43 away_score: int | None = typer.Option(None, "--away-score", "-as", help="Away team score"), 

44): 

45 """Add a new game to the league.""" 

46 console.print("[bold cyan]Add New Game[/bold cyan]\n") 

47 

48 # Get list of teams for validation and display 

49 teams = get_teams() 

50 team_names = [team["name"] for team in teams] 

51 

52 # Interactive mode if not all parameters provided 

53 if not all([date, home_team, away_team, home_score is not None, away_score is not None]): 

54 # Show available teams 

55 table = Table(title="Available Teams") 

56 table.add_column("Team Name", style="cyan") 

57 table.add_column("City", style="green") 

58 

59 for team in sorted(teams, key=lambda x: x["name"]): 

60 table.add_row(team["name"], team["city"]) 

61 

62 console.print(table) 

63 console.print() 

64 

65 # Get game details interactively 

66 if not date: 

67 default_date = datetime.now().strftime("%Y-%m-%d") 

68 date = Prompt.ask("Game date (YYYY-MM-DD)", default=default_date) 

69 

70 if not home_team: 

71 home_team = Prompt.ask("Home team", choices=team_names) 

72 

73 if not away_team: 

74 # Filter out home team from choices 

75 away_choices = [t for t in team_names if t != home_team] 

76 away_team = Prompt.ask("Away team", choices=away_choices) 

77 

78 if home_score is None: 

79 home_score = IntPrompt.ask("Home team score") 

80 

81 if away_score is None: 

82 away_score = IntPrompt.ask("Away team score") 

83 

84 # Validate teams 

85 if home_team not in team_names: 

86 console.print(f"[red]Error: '{home_team}' is not a valid team[/red]") 

87 return 

88 

89 if away_team not in team_names: 

90 console.print(f"[red]Error: '{away_team}' is not a valid team[/red]") 

91 return 

92 

93 if home_team == away_team: 

94 console.print("[red]Error: Home and away teams cannot be the same[/red]") 

95 return 

96 

97 # Validate date format 

98 try: 

99 datetime.strptime(date, "%Y-%m-%d") 

100 except ValueError: 

101 console.print("[red]Error: Invalid date format. Use YYYY-MM-DD[/red]") 

102 return 

103 

104 # Display game summary 

105 console.print("\n[bold]Game Summary:[/bold]") 

106 console.print(f"Date: [cyan]{date}[/cyan]") 

107 console.print(f"Home: [green]{home_team}[/green] - Score: [yellow]{home_score}[/yellow]") 

108 console.print(f"Away: [blue]{away_team}[/blue] - Score: [yellow]{away_score}[/yellow]") 

109 

110 # Confirm before submitting 

111 if not Confirm.ask("\nAdd this game?"): 

112 console.print("[yellow]Game not added.[/yellow]") 

113 return 

114 

115 # Submit to API 

116 game_data = { 

117 "game_date": date, 

118 "home_team": home_team, 

119 "away_team": away_team, 

120 "home_score": home_score, 

121 "away_score": away_score, 

122 } 

123 

124 try: 

125 response = httpx.post(f"{API_BASE_URL}/api/games", json=game_data) 

126 response.raise_for_status() 

127 console.print("[green]✓ Game added successfully![/green]") 

128 except httpx.HTTPStatusError as e: 

129 console.print(f"[red]Error adding game: {e.response.text}[/red]") 

130 except httpx.RequestError as e: 

131 console.print(f"[red]Error connecting to API: {e}[/red]") 

132 

133 

134@app.command() 

135def list_teams(): 

136 """List all teams in the league.""" 

137 teams = get_teams() 

138 

139 table = Table(title="League Teams") 

140 table.add_column("Team Name", style="cyan") 

141 table.add_column("City", style="green") 

142 

143 for team in sorted(teams, key=lambda x: x["name"]): 

144 table.add_row(team["name"], team["city"]) 

145 

146 console.print(table) 

147 console.print(f"\n[dim]Total teams: {len(teams)}[/dim]") 

148 

149 

150@app.command() 

151def recent_games(limit: int = typer.Option(10, "--limit", "-l", help="Number of games to show")): 

152 """Show recent games.""" 

153 try: 

154 response = httpx.get(f"{API_BASE_URL}/api/games") 

155 response.raise_for_status() 

156 games = response.json() 

157 

158 # Sort by date and get recent games 

159 games.sort(key=lambda x: x["game_date"], reverse=True) 

160 recent = games[:limit] 

161 

162 table = Table(title=f"Recent {limit} Games") 

163 table.add_column("Date", style="cyan") 

164 table.add_column("Home Team", style="green") 

165 table.add_column("Score", style="yellow", justify="center") 

166 table.add_column("Away Team", style="blue") 

167 

168 for game in recent: 

169 score = f"{game['home_score']} - {game['away_score']}" 

170 table.add_row(game["game_date"], game["home_team"], score, game["away_team"]) 

171 

172 console.print(table) 

173 

174 except httpx.RequestError as e: 

175 console.print(f"[red]Error connecting to API: {e}[/red]") 

176 except httpx.HTTPStatusError as e: 

177 console.print(f"[red]API error: {e}[/red]") 

178 

179 

180@app.command() 

181def table(): 

182 """Show the current league table.""" 

183 try: 

184 response = httpx.get(f"{API_BASE_URL}/api/table") 

185 response.raise_for_status() 

186 standings = response.json() 

187 

188 table = Table(title="League Table") 

189 table.add_column("Pos", style="dim", width=4) 

190 table.add_column("Team", style="cyan") 

191 table.add_column("P", justify="right", width=4) 

192 table.add_column("W", justify="right", width=4) 

193 table.add_column("D", justify="right", width=4) 

194 table.add_column("L", justify="right", width=4) 

195 table.add_column("GF", justify="right", width=4) 

196 table.add_column("GA", justify="right", width=4) 

197 table.add_column("GD", justify="right", width=4) 

198 table.add_column("Pts", style="bold yellow", justify="right", width=4) 

199 

200 for i, team in enumerate(standings, 1): 

201 table.add_row( 

202 str(i), 

203 team["team"], 

204 str(team["played"]), 

205 str(team["wins"]), 

206 str(team["draws"]), 

207 str(team["losses"]), 

208 str(team["goals_for"]), 

209 str(team["goals_against"]), 

210 str(team["goal_difference"]), 

211 str(team["points"]), 

212 ) 

213 

214 console.print(table) 

215 

216 except httpx.RequestError as e: 

217 console.print(f"[red]Error connecting to API: {e}[/red]") 

218 except httpx.HTTPStatusError as e: 

219 console.print(f"[red]API error: {e}[/red]") 

220 

221 

222def main(): 

223 """Main entry point for the CLI.""" 

224 app() 

225 

226 

227if __name__ == "__main__": 

228 main()