|
7 | 7 | import json
|
8 | 8 | from typing import Optional, Dict, Any, List, Union
|
9 | 9 | from datetime import datetime, timedelta
|
| 10 | +from collections import defaultdict |
10 | 11 |
|
11 | 12 | from git_py_stats.git_operations import run_git_command
|
12 | 13 |
|
@@ -305,6 +306,94 @@ def changelogs(config: Dict[str, Union[str, int]], author: Optional[str] = None)
|
305 | 306 | next_date = date # Update next_date for the next iteration
|
306 | 307 |
|
307 | 308 |
|
| 309 | +def commits_calendar_by_author(config: Dict[str, Union[str, int]], author: Optional[str]) -> None: |
| 310 | + """ |
| 311 | + Displays a calendar of commits by author |
| 312 | +
|
| 313 | + Args: |
| 314 | + config: Dict[str, Union[str, int]]: Config dictionary holding env vars. |
| 315 | + author: Optional[str]: The author's name to filter commits by. |
| 316 | +
|
| 317 | + Returns: |
| 318 | + None |
| 319 | + """ |
| 320 | + |
| 321 | + # Initialize variables similar to the Bash version |
| 322 | + author_option = f"--author={author}" if author else "" |
| 323 | + |
| 324 | + # Grab the config options from our config.py. |
| 325 | + # config.py should give fallbacks for these, but for sanity, |
| 326 | + # lets also provide some defaults just in case. |
| 327 | + merges = config.get("merges", "--no-merges") |
| 328 | + since = config.get("since", "") |
| 329 | + until = config.get("until", "") |
| 330 | + log_options = config.get("log_options", "") |
| 331 | + pathspec = config.get("pathspec", "") |
| 332 | + |
| 333 | + # Original git command: |
| 334 | + # git -c log.showSignature=false log --use-mailmap $_merges \ |
| 335 | + # --date=iso --author="$author" "$_since" "$_until" $_log_options \ |
| 336 | + # --pretty='%ad' $_pathspec |
| 337 | + cmd = [ |
| 338 | + "git", |
| 339 | + "-c", |
| 340 | + "log.showSignature=false", |
| 341 | + "log", |
| 342 | + "--use-mailmap", |
| 343 | + "--date=iso", |
| 344 | + f"--author={author}", |
| 345 | + "--pretty=%ad", |
| 346 | + ] |
| 347 | + |
| 348 | + if author_option: |
| 349 | + cmd.append(author_option) |
| 350 | + |
| 351 | + cmd.extend([since, until, log_options, merges, pathspec]) |
| 352 | + |
| 353 | + # Remove any empty space from the cmd |
| 354 | + cmd = [arg for arg in cmd if arg] |
| 355 | + |
| 356 | + print(f"Commit Activity Calendar for '{author}'") |
| 357 | + |
| 358 | + # Get commit dates |
| 359 | + output = run_git_command(cmd) |
| 360 | + if not output: |
| 361 | + print("No commits found.") |
| 362 | + return |
| 363 | + |
| 364 | + print("\n Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec") |
| 365 | + |
| 366 | + count = defaultdict(lambda: defaultdict(int)) |
| 367 | + for line in output.strip().split("\n"): |
| 368 | + try: |
| 369 | + date_str = line.strip().split(" ")[0] |
| 370 | + date_obj = datetime.strptime(date_str, "%Y-%m-%d") |
| 371 | + weekday = date_obj.isoweekday() # 1=Mon, ..., 7=Sun |
| 372 | + month = date_obj.month |
| 373 | + count[weekday][month] += 1 |
| 374 | + except ValueError: |
| 375 | + continue |
| 376 | + |
| 377 | + # Print the calendar |
| 378 | + weekdays = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"] |
| 379 | + for d in range(1, 8): |
| 380 | + row = f"{weekdays[d-1]:<5} " |
| 381 | + for m in range(1, 13): |
| 382 | + c = count[d][m] |
| 383 | + if c == 0: |
| 384 | + out = "..." |
| 385 | + elif c <= 9: |
| 386 | + out = "░░░" |
| 387 | + elif c <= 19: |
| 388 | + out = "▒▒▒" |
| 389 | + else: |
| 390 | + out = "▓▓▓" |
| 391 | + row += out + (" " if m < 12 else "") |
| 392 | + print(row) |
| 393 | + |
| 394 | + print("\nLegend: ... = 0 ░░░ = 1–9 ▒▒▒ = 10–19 ▓▓▓ = 20+ commits") |
| 395 | + |
| 396 | + |
308 | 397 | def my_daily_status(config: Dict[str, Union[str, int]]) -> None:
|
309 | 398 | """
|
310 | 399 | Displays the user's commits from the last day.
|
|
0 commit comments