|
6 | 6 | import csv |
7 | 7 | import io |
8 | 8 | import re |
9 | | -# 加入 colorama 支援 |
10 | | -try: |
11 | | - from colorama import Fore, Style, init as colorama_init |
12 | | - colorama_init() |
13 | | - COLORAMA = True |
14 | | -except ImportError: |
15 | | - COLORAMA = False |
16 | | - class Dummy: |
17 | | - RESET = '' |
18 | | - RED = '' |
19 | | - GREEN = '' |
20 | | - YELLOW = '' |
21 | | - BLUE = '' |
22 | | - CYAN = '' |
23 | | - WHITE = '' |
24 | | - LIGHTBLACK_EX = '' |
25 | | - Fore = Style = Dummy() |
26 | | - |
27 | | -# rich 支援 |
28 | | -try: |
29 | | - from rich.console import Console |
30 | | - from rich.table import Table |
31 | | - from rich import box as rich_box |
32 | | - RICH = True |
33 | | - _console = Console() |
34 | | -except ImportError: |
35 | | - RICH = False |
36 | 9 |
|
37 | 10 | def ascii_bar_chart(data, value_key, label_key='ext', width=40, title=None): |
38 | 11 | """ |
@@ -225,73 +198,8 @@ def format_size(num_bytes): |
225 | 198 |
|
226 | 199 | def print_table(rows, headers=None, title=None): |
227 | 200 | """ |
228 | | - Print a list of dicts as a pretty aligned table. 若有 rich 則用 rich.Table。 |
| 201 | + Print a list of dicts as a pretty aligned table. |
229 | 202 | """ |
230 | | - rows = [r for r in rows if r is not None] |
231 | | - if not rows: |
232 | | - print("No data to display.") |
233 | | - return |
234 | | - if headers is None: |
235 | | - headers = list(rows[0].keys()) |
236 | | - # rich table |
237 | | - if RICH: |
238 | | - table = Table(title=title, box=rich_box.SIMPLE_HEAVY, show_lines=False, expand=True) |
239 | | - for h in headers: |
240 | | - # 每欄固定最大寬度,且不自動換行,超過用 ... |
241 | | - table.add_column(str(h), style="cyan", overflow="ellipsis", no_wrap=True, max_width=18) |
242 | | - for row in rows: |
243 | | - row_data = [] |
244 | | - for h in headers: |
245 | | - v = row.get(h, '') |
246 | | - # 根據欄位自動上色 |
247 | | - if isinstance(v, float): |
248 | | - v = f"{v:.1f}" |
249 | | - vstr = str(v) |
250 | | - # 數值欄位 |
251 | | - if h in ('total_lines', 'lines', 'size', 'code_lines', 'covered', 'changes'): |
252 | | - try: |
253 | | - num = float(str(vstr).replace(',', '').replace(' KB','').replace(' MB','').replace(' GB','')) |
254 | | - if num >= 1000: |
255 | | - vstr = f"[green]{vstr}[/green]" |
256 | | - elif num >= 100: |
257 | | - vstr = f"[yellow]{vstr}[/yellow]" |
258 | | - else: |
259 | | - vstr = f"[grey50]{vstr}[/grey50]" |
260 | | - except Exception: |
261 | | - pass |
262 | | - elif 'percent' in h or '%' in vstr: |
263 | | - try: |
264 | | - num = float(vstr.replace('%','').replace(' ','').replace('(','').replace(')','')) |
265 | | - if num >= 50: |
266 | | - vstr = f"[green]{vstr}[/green]" |
267 | | - elif num >= 10: |
268 | | - vstr = f"[yellow]{vstr}[/yellow]" |
269 | | - else: |
270 | | - vstr = f"[grey50]{vstr}[/grey50]" |
271 | | - except Exception: |
272 | | - pass |
273 | | - elif 'complexity' in h: |
274 | | - try: |
275 | | - num = float(vstr) |
276 | | - if num >= 10: |
277 | | - vstr = f"[red]{vstr}[/red]" |
278 | | - elif num >= 3: |
279 | | - vstr = f"[yellow]{vstr}[/yellow]" |
280 | | - else: |
281 | | - vstr = f"[green]{vstr}[/green]" |
282 | | - except Exception: |
283 | | - pass |
284 | | - elif h in ('path', 'file', 'ext'): |
285 | | - vstr = f"[cyan]{vstr}[/cyan]" |
286 | | - elif h in ('reasons', 'desc', 'type') and vstr: |
287 | | - vstr = f"[yellow]{vstr}[/yellow]" |
288 | | - elif h == 'author' and row.get('workload_percent','').endswith('%') and float(row.get('workload_percent','0').replace('%','')) >= 50: |
289 | | - vstr = f"[green]{vstr}[/green]" |
290 | | - row_data.append(vstr) |
291 | | - table.add_row(*row_data) |
292 | | - _console.print(table) |
293 | | - return |
294 | | - # fallback: colorama/純文字 |
295 | 203 | # 過濾掉 None |
296 | 204 | rows = [r for r in rows if r is not None] |
297 | 205 | if not rows: |
@@ -339,74 +247,14 @@ def print_table(rows, headers=None, title=None): |
339 | 247 | formatted_rows.append(new_row) |
340 | 248 | col_widths = [max(len(str(h)), max(len(str(row.get(h, ''))) for row in formatted_rows)) for h in headers] |
341 | 249 | if title: |
342 | | - if COLORAMA: |
343 | | - print(Fore.BLUE + f"\n{title}" + Style.RESET_ALL) |
344 | | - else: |
345 | | - print(f"\n{title}") |
| 250 | + print(f"\n{title}") |
346 | 251 | # Print header |
347 | | - if COLORAMA: |
348 | | - header_line = ' | '.join(Fore.CYAN + str(h).ljust(w) + Style.RESET_ALL for h, w in zip(headers, col_widths)) |
349 | | - else: |
350 | | - header_line = ' | '.join(str(h).ljust(w) for h, w in zip(headers, col_widths)) |
| 252 | + header_line = ' | '.join(str(h).ljust(w) for h, w in zip(headers, col_widths)) |
351 | 253 | print(header_line) |
352 | 254 | print('-+-'.join('-'*w for w in col_widths)) |
353 | 255 | # Print rows |
354 | 256 | for row in formatted_rows: |
355 | | - colored_cells = [] |
356 | | - for h, w in zip(headers, col_widths): |
357 | | - v = str(row.get(h, '')) |
358 | | - # 根據欄位自動上色 |
359 | | - if COLORAMA: |
360 | | - # 數值欄位 |
361 | | - if h in ('total_lines', 'lines', 'size', 'code_lines', 'covered', 'changes'): |
362 | | - try: |
363 | | - num = float(v.replace(',', '').replace(' KB','').replace(' MB','').replace(' GB','')) |
364 | | - if num >= 1000: |
365 | | - color = Fore.GREEN |
366 | | - elif num >= 100: |
367 | | - color = Fore.YELLOW |
368 | | - else: |
369 | | - color = Fore.LIGHTBLACK_EX |
370 | | - v = color + v + Style.RESET_ALL |
371 | | - except Exception: |
372 | | - pass |
373 | | - # 百分比欄位 |
374 | | - elif 'percent' in h or '%' in v: |
375 | | - try: |
376 | | - num = float(v.replace('%','').replace(' ','').replace('(','').replace(')','')) |
377 | | - if num >= 50: |
378 | | - color = Fore.GREEN |
379 | | - elif num >= 10: |
380 | | - color = Fore.YELLOW |
381 | | - else: |
382 | | - color = Fore.LIGHTBLACK_EX |
383 | | - v = color + v + Style.RESET_ALL |
384 | | - except Exception: |
385 | | - pass |
386 | | - # 複雜度欄位 |
387 | | - elif 'complexity' in h: |
388 | | - try: |
389 | | - num = float(v) |
390 | | - if num >= 10: |
391 | | - color = Fore.RED |
392 | | - elif num >= 3: |
393 | | - color = Fore.YELLOW |
394 | | - else: |
395 | | - color = Fore.GREEN |
396 | | - v = color + v + Style.RESET_ALL |
397 | | - except Exception: |
398 | | - pass |
399 | | - # 路徑/檔名 |
400 | | - elif h in ('path', 'file', 'ext'): |
401 | | - v = Fore.CYAN + v + Style.RESET_ALL |
402 | | - # 警告/建議/違規 |
403 | | - elif h in ('reasons', 'desc', 'type') and v: |
404 | | - v = Fore.YELLOW + v + Style.RESET_ALL |
405 | | - # 主要作者 |
406 | | - elif h == 'author' and row.get('workload_percent','').endswith('%') and float(row.get('workload_percent','0').replace('%','')) >= 50: |
407 | | - v = Fore.GREEN + v + Style.RESET_ALL |
408 | | - colored_cells.append(v.ljust(w)) |
409 | | - print(' | '.join(colored_cells)) |
| 257 | + print(' | '.join(str(row.get(h, '')).ljust(w) for h, w in zip(headers, col_widths))) |
410 | 258 |
|
411 | 259 | def csv_report(data, headers=None): |
412 | 260 | """ |
|
0 commit comments