Coverage for C:\Users\svjatoslavsmatvejevs\AppData\Local\Programs\Python\Python39\Lib\site-packages\report\report.py: 0%

112 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2023-01-13 09:58 +0200

1"""Command-line interface for report of Monaco race. 

2 

3Class: 

4 OrderedNamespace 

5 Cli 

6""" 

7import os 

8import sys 

9import argparse 

10from dataclasses import dataclass, field 

11from typing import Any 

12 

13from tabulate import tabulate 

14 

15import constant as c 

16from race import Race 

17 

18 

19class OrderedNamespace(argparse.Namespace): 

20 """Namespace with ordered arguments. 

21 

22 Adds dictionary which saves order of input arguments in cli. 

23 """ 

24 

25 def __init__(self, **kwargs): 

26 """Initialize with new attribute 

27 

28 Args: 

29 kwargs: arguments in parser 

30 """ 

31 # _order will save input arguments in given order 

32 self.__dict__["_order"] = [None] 

33 super().__init__(**kwargs) 

34 

35 def __setattr__(self, attr: str, value: Any): 

36 """Set attributes. 

37 

38 _order will contain only attribute names, that was 

39 provided in cli in preserved order. 

40 

41 Args: 

42 attr: Attributes in Namespace. 

43 value: Values of attributes in Namespace. 

44 """ 

45 super().__setattr__(attr, value) 

46 if attr in self._order: 

47 self.__dict__["_order"].clear() 

48 self.__dict__["_order"].append(attr) 

49 

50 def ordered(self) -> list[tuple]: 

51 """Generate values for attributes in _order. 

52 

53 Returns: 

54 Return _order pairs of attributes and values. 

55 """ 

56 return [(attr, getattr(self, attr)) for attr in self._order 

57 if attr != "files"] 

58 

59 def only_file_path(self): 

60 """Checks argument quantity. 

61 

62 Checks if any argument was given beside files path argument. 

63 

64 Raises: 

65 UserWarning: If only files path was given as argument. 

66 """ 

67 if len(self.__dict__["_order"]) == 1: 

68 raise UserWarning("Application should use at least one command argument!") 

69 

70 

71@dataclass 

72class Cli: 

73 print_func: dict = field(default_factory=dict) 

74 args: argparse.Namespace = field(init=False) 

75 race: Race = field(init=False) 

76 

77 def __post_init__(self): 

78 # Save all print functions in dictionary in order to make execution simpler. 

79 self.print_func = {"report": self.print_report, 

80 "time": self.print_time, 

81 "date": self.print_date_of_the_race, 

82 "racers_number": self.print_number_of_racers, 

83 "driver": self.print_driver, 

84 "racers": self.print_racers, 

85 "teams": self.print_teams, 

86 "team_racers": self.print_racers_in_team, 

87 "full_report": self.print_full_report} 

88 

89 @staticmethod 

90 def get_path_to_files(path: str) -> dict[str: str]: 

91 """Get path to the log files. 

92 

93 Args: 

94 path: Path of the folder where log files are located. 

95 

96 Returns: 

97 Dictionary with file name as key and path to the file as value. 

98 

99 Raises: 

100 UserWarning: if not all files where found in passed path. 

101 NotADirectoryError: if directory name is file name. 

102 """ 

103 # Get path of the log files from directory if file name is in lookup list 

104 files = {os.path.splitext(f)[0]: os.path.join(path, f) 

105 for f in os.listdir(path) 

106 if f.lower() in c.REPORT_FILES} 

107 # Check if dictionary has all necessary files. 

108 if len(files) != len(c.REPORT_FILES): 

109 raise UserWarning("File missing. Make sure that folder contains files:" 

110 f" {c.REPORT_FILES}") 

111 return files 

112 

113 def print_date_of_the_race(self): 

114 """Print date of the race.""" 

115 print("\nDate of the race:") 

116 print(f"\t{self.race.get_data_of_race()}") 

117 

118 def print_time(self): 

119 """Print start time of the 1st qualification.""" 

120 print("\n1st qualification round started at:") 

121 print(f"\t{self.race.get_start_time_of_q1()}") 

122 

123 def print_number_of_racers(self): 

124 """Print number of racers in the race.""" 

125 print("\nNumber of the racers in the race:") 

126 print(f"\t{self.race.get_number_of_racers()}") 

127 

128 def print_report(self): 

129 """Print race report. 

130 

131 Prints race report in nice table look. 

132 """ 

133 # Build report of the race. 

134 race_report = self.race.build_report() 

135 racers = [] 

136 print("\nReport of the race:") 

137 # Loop through report sorted by lap time 

138 for num, racer in enumerate(sorted(race_report, key=lambda x: x[2])): 

139 # Convert time lap to appropriate string format. 

140 racer[2] = str(racer[2])[3: -3] 

141 # Insert racer's place. 

142 racer.insert(0, str(num + 1)) 

143 racers.append(racer) 

144 # Separate first 15 places. 

145 if num + 1 == 15: 

146 sep = "-" * len(racer[3]) 

147 racers.append(["--", sep, sep, sep]) 

148 print(tabulate(racers, 

149 headers=["", "Full Name", "Team", "Q1 Time"], 

150 tablefmt=c.TABLE_FORMAT)) 

151 

152 def print_driver(self, driver: str): 

153 """Print driver information. 

154 

155 Args: 

156 driver: Driver full name. 

157 """ 

158 try: 

159 driver = self.race.get_racer(driver) 

160 print(f"\nInformation about {driver[1]}:") 

161 print(tabulate([driver], 

162 headers=["Abbr", "Full Name", "Team", "Q1 Time"], 

163 tablefmt=c.TABLE_FORMAT)) 

164 except UserWarning as err: 

165 print(err) 

166 

167 def print_racers(self, order: str): 

168 """Print list of racers in the race. 

169 

170 Args: 

171 order: Flag for racers order. 

172 """ 

173 racers = [[racer] for racer in 

174 self.race.get_names_of_racers(reverse=c.ORDER[order])] 

175 print(tabulate(racers, 

176 headers=["List of racers in the race:"], 

177 tablefmt=c.TABLE_FORMAT)) 

178 

179 def print_teams(self, order: str): 

180 """Print list of teams in the race. 

181 

182 Args: 

183 order: Flag for racers order. 

184 """ 

185 teams = [[team] for team in self.race.get_teams(reverse=c.ORDER[order])] 

186 print(tabulate(teams, 

187 headers=["List of teams:"], 

188 tablefmt=c.TABLE_FORMAT)) 

189 

190 def print_racers_in_team(self, team: str): 

191 """Print racers in specific team. 

192 

193 Args: 

194 team: Name of the team. 

195 """ 

196 try: 

197 racers = self.race.get_racers_in_team(team.upper()) 

198 print(f"\nRacers from {team.upper()} team:") 

199 print(tabulate(racers, 

200 headers=["Abbr", "Full Name", "Team", "Q1 Time"], 

201 tablefmt=c.TABLE_FORMAT, )) 

202 except UserWarning as err: 

203 print(err) 

204 

205 def print_full_report(self): 

206 """Print full report of the race. 

207 

208 Full report consists of date of the race, time of the 1st qualification, 

209 number of racers in the race and report of 1st qualification. 

210 """ 

211 self.print_date_of_the_race() 

212 self.print_time() 

213 self.print_number_of_racers() 

214 self.print_report() 

215 

216 def cli(self): 

217 """Implement command-line interface 

218 

219 Uses argparse to get input from command-line 

220 and print according result. 

221 """ 

222 parser = argparse.ArgumentParser( 

223 prog="Report of Monaco 2018 racing:", 

224 description="Get information about the race from the log files." 

225 ) 

226 parser.add_argument( 

227 "-f", "--files", required=True, 

228 help="directory path where the race log files are located (required argument)" 

229 ) 

230 parser.add_argument( 

231 "-r", "--report", action="store_true", 

232 help="report of the 1st qualification result" 

233 ) 

234 parser.add_argument( 

235 "-d", "--date", action="store_true", 

236 help="date of the 1st qualification" 

237 ) 

238 parser.add_argument( 

239 "-t", "--time", action="store_true", 

240 help="start time of the 1st qualifications" 

241 ) 

242 parser.add_argument( 

243 "--racers-number", action="store_true", 

244 help="number of the racers in the race" 

245 ) 

246 parser.add_argument( 

247 "--driver", 

248 help="information about specific driver" 

249 ) 

250 parser.add_argument( 

251 "--racers", 

252 const=c.ORDER_CHOICES[0], 

253 nargs="?", 

254 choices=c.ORDER_CHOICES, 

255 help="""list of all racers in the race in asc or" 

256 desc order (default is asc)""" 

257 ) 

258 parser.add_argument( 

259 "--teams", 

260 const=c.ORDER_CHOICES[0], 

261 nargs="?", 

262 choices=c.ORDER_CHOICES, 

263 help="""list of all teams in the race in asc " 

264 or desc order (default is asc)""" 

265 ) 

266 parser.add_argument( 

267 "--team-racers", 

268 help="list of all racers from specific team" 

269 ) 

270 parser.add_argument( 

271 "--full-report", action="store_true", 

272 help="combines --date, --time --racers-number and --report" 

273 ) 

274 

275 self.args = parser.parse_args(namespace=OrderedNamespace()) 

276 

277 try: 

278 # Check if only files argument was passed 

279 self.args.only_file_path() 

280 except UserWarning as err: 

281 print(err) 

282 parser.print_usage() 

283 sys.exit() 

284 

285 try: 

286 # Get log files from file directory 

287 file_path = self.get_path_to_files(self.args.files) 

288 except (FileNotFoundError, NotADirectoryError, UserWarning) as err: 

289 sys.exit(err) 

290 

291 # Create instance with log files passed as parameter. 

292 self.race = Race(log_files=file_path) 

293 

294 # Go through passed arguments and execute according print functions. 

295 for arg, value in self.args.ordered(): 

296 if arg in ["driver", "racers", "teams", "team_racers"]: 

297 self.print_func[arg](value) 

298 else: 

299 self.print_func[arg]() 

300 

301 

302if __name__ == "__main__": 

303 cli = Cli() 

304 cli.cli()