|
1 | 1 | import os |
| 2 | +import re |
2 | 3 | from datetime import datetime |
3 | | -import subprocess |
| 4 | +from zoneinfo import ZoneInfo # built-in since Python 3.9 |
4 | 5 |
|
5 | 6 | README_PATH = "README.md" |
| 7 | +FOLDERS = ["Easy", "Medium", "Hard"] |
| 8 | +EXT = ".java" |
6 | 9 |
|
7 | | -# Count problems |
8 | | -def count_problems(): |
9 | | - count = 0 |
10 | | - for folder in ["Easy", "Medium", "Hard"]: |
| 10 | + |
| 11 | +def count_by_folder(): |
| 12 | + """Return dict of counts per folder and total, counting EXT files recursively.""" |
| 13 | + per = {} |
| 14 | + total = 0 |
| 15 | + for folder in FOLDERS: |
| 16 | + c = 0 |
11 | 17 | if os.path.exists(folder): |
12 | | - for file in os.listdir(folder): |
13 | | - if os.path.isfile(os.path.join(folder, file)): |
14 | | - count += 1 |
15 | | - return count |
16 | | - |
17 | | -# Get last commit date |
18 | | -def last_updated(): |
19 | | - result = subprocess.run( |
20 | | - ["git", "log", "-1", "--format=%cd", "--date=iso"], |
21 | | - capture_output=True, |
22 | | - text=True |
| 18 | + for root, _, files in os.walk(folder): |
| 19 | + for f in files: |
| 20 | + if f.endswith(EXT): |
| 21 | + c += 1 |
| 22 | + per[folder] = c |
| 23 | + total += c |
| 24 | + per["_total"] = total |
| 25 | + return per |
| 26 | + |
| 27 | + |
| 28 | +def human_ist_now(): |
| 29 | + """Current IST time in human-readable format.""" |
| 30 | + ist = datetime.now(ZoneInfo("Asia/Kolkata")) |
| 31 | + return ist.strftime("%d %b %Y, %I:%M %p IST") |
| 32 | + |
| 33 | + |
| 34 | +def replace_line(content: str, label: str, new_line: str) -> tuple[str, bool]: |
| 35 | + """ |
| 36 | + Replace a line starting with the label. |
| 37 | + Works for both placeholder and non-placeholder versions. |
| 38 | +
|
| 39 | + Examples it will match: |
| 40 | + Problems solved: <!--PROBLEMS_COUNT-->3 |
| 41 | + Problems solved: 3 |
| 42 | + Last updated: <!--LAST_UPDATED-->14 Aug 2025, ... |
| 43 | + Last updated: 14 Aug 2025, ... |
| 44 | + """ |
| 45 | + # 1) Prefer placeholder form |
| 46 | + placeholder_patterns = [ |
| 47 | + rf"(?mi)^{re.escape(label)}\s*:?\s*<!--PROBLEMS_COUNT-->.*$", |
| 48 | + rf"(?mi)^{re.escape(label)}\s*:?\s*<!--LAST_UPDATED-->.*$", |
| 49 | + ] |
| 50 | + for pat in placeholder_patterns: |
| 51 | + new_content, n = re.subn(pat, new_line, content) |
| 52 | + if n > 0: |
| 53 | + return new_content, True |
| 54 | + |
| 55 | + # 2) Generic form (no placeholder) |
| 56 | + generic_pat = rf"(?mi)^{re.escape(label)}\s*:?.*$" |
| 57 | + new_content, n = re.subn(generic_pat, new_line, content) |
| 58 | + if n > 0: |
| 59 | + return new_content, True |
| 60 | + |
| 61 | + return content, False |
| 62 | + |
| 63 | + |
| 64 | +def ensure_progress_block(content: str, problems_count: int, updated_time: str) -> str: |
| 65 | + """Append a progress block if neither line exists anywhere.""" |
| 66 | + block = ( |
| 67 | + "\n\n🏆 Progress\n\n" |
| 68 | + f"Problems solved: <!--PROBLEMS_COUNT-->{problems_count}\n" |
| 69 | + f"Last updated: <!--LAST_UPDATED-->{updated_time}\n" |
23 | 70 | ) |
24 | | - return result.stdout.strip() |
| 71 | + return content + block |
| 72 | + |
25 | 73 |
|
26 | 74 | def update_readme(): |
| 75 | + counts = count_by_folder() |
| 76 | + total = counts["_total"] |
| 77 | + updated_time = human_ist_now() |
| 78 | + |
| 79 | + # Debug prints to workflow logs |
| 80 | + print("[update_readme] Counts:", counts) |
| 81 | + print("[update_readme] Total:", total) |
| 82 | + print("[update_readme] Time:", updated_time) |
| 83 | + |
27 | 84 | with open(README_PATH, "r", encoding="utf-8") as f: |
28 | 85 | content = f.read() |
29 | 86 |
|
30 | | - problems_count = count_problems() |
31 | | - updated_time = last_updated() |
| 87 | + # Desired lines |
| 88 | + solved_line = f"Problems solved: <!--PROBLEMS_COUNT-->{total}" |
| 89 | + time_line = f"Last updated: <!--LAST_UPDATED-->{updated_time}" |
32 | 90 |
|
33 | | - content = content.replace( |
34 | | - "Problems solved: <!--PROBLEMS_COUNT-->", |
35 | | - f"Problems solved: {problems_count}" |
36 | | - ) |
37 | | - content = content.replace( |
38 | | - "Last updated: <!--LAST_UPDATED-->", |
39 | | - f"Last updated: {updated_time}" |
40 | | - ) |
| 91 | + changed = False |
| 92 | + |
| 93 | + content2, changed1 = replace_line(content, "Problems solved", solved_line) |
| 94 | + content = content2 |
| 95 | + changed = changed or changed1 |
| 96 | + |
| 97 | + content2, changed2 = replace_line(content, "Last updated", time_line) |
| 98 | + content = content2 |
| 99 | + changed = changed or changed2 |
| 100 | + |
| 101 | + if not (changed1 or changed2): |
| 102 | + # Neither line existed — append a fresh block |
| 103 | + print("[update_readme] Progress block not found; appending a new one.") |
| 104 | + content = ensure_progress_block(content, total, updated_time) |
| 105 | + changed = True |
41 | 106 |
|
42 | 107 | with open(README_PATH, "w", encoding="utf-8") as f: |
43 | 108 | f.write(content) |
44 | 109 |
|
| 110 | + # Exit code / message for clarity (not strictly required) |
| 111 | + if changed: |
| 112 | + print("[update_readme] README.md updated.") |
| 113 | + else: |
| 114 | + print("[update_readme] No changes required.") |
| 115 | + |
| 116 | + |
45 | 117 | if __name__ == "__main__": |
46 | 118 | update_readme() |
0 commit comments