Skip to content

Commit e64882b

Browse files
committed
initial commit
0 parents  commit e64882b

4 files changed

Lines changed: 300 additions & 0 deletions

File tree

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
name: Update Dashboard
2+
3+
on:
4+
schedule:
5+
# Runs daily at midnight UTC
6+
- cron: '0 0 * * *'
7+
workflow_dispatch:
8+
# Allows manual triggering from the GitHub Actions tab
9+
10+
jobs:
11+
update:
12+
runs-on: ubuntu-latest
13+
14+
steps:
15+
- name: Checkout Dashboard Repo
16+
uses: actions/checkout@v4
17+
18+
- name: Set up Python
19+
uses: actions/setup-python@v5
20+
with:
21+
python-version: '3.x'
22+
23+
- name: Clone Sibling Repositories
24+
run: |
25+
git clone https://github.com/LorranSutter/advent-of-code.git ../advent-of-code
26+
git clone https://github.com/LorranSutter/everybody-codes.git ../everybody-codes
27+
git clone https://github.com/LorranSutter/leet-code.git ../leet-code
28+
29+
- name: Generate Dashboard README
30+
run: python3 generate_dashboard.py
31+
32+
- name: Commit and Push Changes
33+
run: |
34+
git config --global user.name "github-actions[bot]"
35+
git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
36+
37+
if [ -n "$(git status --porcelain README.md)" ]; then
38+
git add README.md
39+
git commit -m "chore: update progress statistics [skip ci]"
40+
git push
41+
else
42+
echo "No progress updates detected. Skipping commit."
43+
fi

.gitignore

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Byte-compiled / optimized / DLL files
2+
__pycache__/
3+
*.py[cod]
4+
*$py.class
5+
6+
# Environments
7+
.venv/
8+
venv/
9+
ENV/
10+
env/
11+
.env
12+
.env.local
13+
14+
# IDEs
15+
.vscode/
16+
.idea/
17+
*.suo
18+
*.ntvs*
19+
*.njsproj
20+
*.sln
21+
*.swp
22+
23+
# Go output files (just in case)
24+
*.out
25+
*.exe
26+
*.test
27+
bin/
28+
vendor/
29+
30+
# Local testing clones
31+
/advent-of-code/
32+
/everybody-codes/
33+
/leet-code/

README.md

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# 🏆 Coding Challenges Dashboard
2+
3+
Welcome to my central hub for tracking coding challenges! This repository consolidates my progress across various programming platforms and events.
4+
5+
Rather than pinning multiple individual repositories, this dashboard dynamically aggregates and displays up-to-date stats from my active challenge repositories.
6+
7+
## 🔗 Repositories Included
8+
9+
- **[Advent of Code](https://github.com/LorranSutter/advent-of-code)**: Annual December coding puzzles (Python).
10+
- **[Everybody Codes](https://github.com/LorranSutter/everybody-codes)**: Annual November story-based coding challenges (Python).
11+
- **[LeetCode](https://github.com/LorranSutter/leet-code)**: Algorithm and problem-solving practice (Go).
12+
13+
---
14+
15+
<!-- SUMMARY:START -->
16+
## 📊 Consolidated Progress
17+
18+
> ### 🏆 **Grand Total: 275 coding challenges completed!**
19+
>
20+
> - **Advent of Code**: 95/100 parts (95.0%)
21+
> - **Everybody Codes**: 52/54 parts (96.3%)
22+
> - **LeetCode**: 128 problems solved
23+
24+
### [🎄 Advent of Code](https://github.com/LorranSutter/advent-of-code)
25+
26+
> **Overall: 95/100 parts solved (95%)**
27+
28+
### [2023](https://github.com/LorranSutter/advent-of-code/tree/main/2023/)
29+
30+
`███████████████████░` **28/30** parts solved (93%)
31+
32+
### [2024](https://github.com/LorranSutter/advent-of-code/tree/main/2024/)
33+
34+
`███████████████████░` **45/48** parts solved (94%)
35+
36+
### [2025](https://github.com/LorranSutter/advent-of-code/tree/main/2025/)
37+
38+
`████████████████████` **22/22** parts solved (100%)
39+
40+
---
41+
42+
### [🦆 Everybody Codes](https://github.com/LorranSutter/everybody-codes)
43+
44+
> **Overall: 52/54 parts solved (96%)**
45+
46+
### [2024 — Story: Echoes of Enigmatus](https://github.com/LorranSutter/everybody-codes/tree/main/2024/story/)
47+
48+
`████████████████████` **9/9** parts solved (100%)
49+
50+
### [2025 — Event: The Song of Ducks and Dragons](https://github.com/LorranSutter/everybody-codes/tree/main/2025/event/)
51+
52+
`███████████████████░` **43/45** parts solved (96%)
53+
54+
---
55+
56+
### [💡 LeetCode](https://github.com/LorranSutter/leet-code)
57+
58+
> **Overall: 128 problems solved**
59+
<!-- SUMMARY:END -->
60+
61+
---
62+
63+
## 🛠️ How It Works
64+
65+
This repository is fully automated:
66+
1. A **GitHub Actions Workflow** (`update-dashboard.yml`) runs daily.
67+
2. It dynamically clones the latest `main` branches of the three source repositories.
68+
3. It executes `generate_dashboard.py` to scan each repository's `README.md` progress markers, rewrite relative links to absolute ones, calculate the grand totals, and update this file.
69+
4. If and only if changes are detected, it commits and pushes the updated README.

generate_dashboard.py

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
#!/usr/bin/env python3
2+
import re
3+
from pathlib import Path
4+
5+
ROOT = Path(__file__).parent
6+
7+
# Repositories configuration
8+
REPOS = {
9+
"advent-of-code": {
10+
"title": "🎄 Advent of Code",
11+
"url": "https://github.com/LorranSutter/advent-of-code",
12+
"branch": "main",
13+
},
14+
"everybody-codes": {
15+
"title": "🦆 Everybody Codes",
16+
"url": "https://github.com/LorranSutter/everybody-codes",
17+
"branch": "main",
18+
},
19+
"leet-code": {
20+
"title": "💡 LeetCode",
21+
"url": "https://github.com/LorranSutter/leet-code",
22+
"branch": "main",
23+
}
24+
}
25+
26+
def extract_summary_block(readme_path: Path) -> str:
27+
"""Extracts everything between <!-- SUMMARY:START --> and <!-- SUMMARY:END -->."""
28+
if not readme_path.exists():
29+
return ""
30+
content = readme_path.read_text()
31+
match = re.search(r"<!-- SUMMARY:START -->\s*(.*?)\s*<!-- SUMMARY:END -->", content, re.DOTALL)
32+
if match:
33+
return match.group(1).strip()
34+
return ""
35+
36+
def rewrite_relative_links(text: str, repo_url: str, branch: str) -> str:
37+
"""Replaces relative links like [name](./path) with absolute GitHub links."""
38+
# Matches [text](./path/to/thing)
39+
pattern = r"\[([^\]]+)\]\(\./([^\)]*)\)"
40+
replacement = rf"[\1]({repo_url}/tree/{branch}/\2)"
41+
return re.sub(pattern, replacement, text)
42+
43+
def parse_stats(text: str, repo_id: str):
44+
"""Parses solved numbers from the 'Overall' line in the summary."""
45+
if not text:
46+
return None
47+
48+
if repo_id in ("advent-of-code", "everybody-codes"):
49+
# Pattern: > **Overall: 95/100 parts solved (95%)**
50+
match = re.search(r"Overall:\s*(\d+)/(\d+)\s*parts solved", text)
51+
if match:
52+
return {
53+
"solved": int(match.group(1)),
54+
"total": int(match.group(2)),
55+
"type": "parts"
56+
}
57+
elif repo_id == "leet-code":
58+
# Pattern: > **Overall: X problems solved**
59+
match = re.search(r"Overall:\s*(\d+)\s*problems solved", text)
60+
if match:
61+
return {
62+
"solved": int(match.group(1)),
63+
"total": None,
64+
"type": "problems"
65+
}
66+
return None
67+
68+
def main():
69+
dashboard_readme = ROOT / "README.md"
70+
if not dashboard_readme.exists():
71+
print(f"⚠️ README.md not found at {dashboard_readme}")
72+
return
73+
74+
extracted_sections = {}
75+
stats = {}
76+
77+
for repo_id, config in REPOS.items():
78+
sibling_readme = ROOT.parent / repo_id / "README.md"
79+
raw_summary = extract_summary_block(sibling_readme)
80+
81+
if raw_summary:
82+
# Extract statistics
83+
repo_stats = parse_stats(raw_summary, repo_id)
84+
if repo_stats:
85+
stats[repo_id] = repo_stats
86+
87+
# Clean up the local summary header (e.g. remove "## 📊 Progress" since we make our own structure)
88+
clean_summary = re.sub(r"^## 📊 Progress\s*\n", "", raw_summary)
89+
90+
# Rewrite links
91+
rewritten = rewrite_relative_links(clean_summary, config["url"], config["branch"])
92+
extracted_sections[repo_id] = rewritten
93+
else:
94+
extracted_sections[repo_id] = f"*(No stats available for {config['title']})*"
95+
96+
# Build the dashboard content
97+
lines = []
98+
lines.append("## 📊 Consolidated Progress")
99+
lines.append("")
100+
101+
# Calculate Grand Totals
102+
total_challenges = 0
103+
aoc_solved = stats.get("advent-of-code", {}).get("solved", 0)
104+
aoc_total = stats.get("advent-of-code", {}).get("total", 0)
105+
ebc_solved = stats.get("everybody-codes", {}).get("solved", 0)
106+
ebc_total = stats.get("everybody-codes", {}).get("total", 0)
107+
lc_solved = stats.get("leet-code", {}).get("solved", 0)
108+
109+
total_challenges = aoc_solved + ebc_solved + lc_solved
110+
111+
# Overall Badge-like summary
112+
lines.append(f"> ### 🏆 **Grand Total: {total_challenges} coding challenges completed!**")
113+
lines.append(">")
114+
if "advent-of-code" in stats:
115+
lines.append(f"> - **Advent of Code**: {aoc_solved}/{aoc_total} parts ({aoc_solved/aoc_total*100:.1f}%)")
116+
if "everybody-codes" in stats:
117+
lines.append(f"> - **Everybody Codes**: {ebc_solved}/{ebc_total} parts ({ebc_solved/ebc_total*100:.1f}%)")
118+
if "leet-code" in stats:
119+
lines.append(f"> - **LeetCode**: {lc_solved} problems solved")
120+
lines.append("")
121+
122+
# Detail sections for each repo
123+
for repo_id, config in REPOS.items():
124+
lines.append(f"### [{config['title']}]({config['url']})")
125+
lines.append("")
126+
lines.append(extracted_sections[repo_id])
127+
lines.append("")
128+
lines.append("---")
129+
lines.append("")
130+
131+
# Remove the trailing horizontal rule
132+
if lines and lines[-2] == "---":
133+
lines = lines[:-3]
134+
135+
unified_summary = "\n".join(lines).strip()
136+
137+
# Inject into the dashboard README
138+
content = dashboard_readme.read_text()
139+
start_marker = "<!-- SUMMARY:START -->"
140+
end_marker = "<!-- SUMMARY:END -->"
141+
block = f"{start_marker}\n{unified_summary}\n{end_marker}"
142+
143+
if start_marker in content and end_marker in content:
144+
pattern = re.compile(
145+
re.escape(start_marker) + r".*?" + re.escape(end_marker),
146+
re.DOTALL,
147+
)
148+
new_content = pattern.sub(block, content)
149+
dashboard_readme.write_text(new_content)
150+
print(f"✅ Successfully updated {dashboard_readme}")
151+
else:
152+
print("❌ Could not find markers in README.md. Please make sure <!-- SUMMARY:START --> and <!-- SUMMARY:END --> exist.")
153+
154+
if __name__ == "__main__":
155+
main()

0 commit comments

Comments
 (0)