learning-website-framework1-6 Exercise 2: Audit the Solution Links ===================================================================== The site rewrites every .txt link to /solutions/, so a link only works if that file exists. Write an audit that checks it, first for one course, then for every course with a solutions/ folder. Save as audit_solutions.py: import os, re, sys from urllib.parse import unquote TXT = re.compile(r'href="([^"]*?)([^"/]+\.txt)"', re.I) def audit(course_dir): solutions = set() sol_dir = os.path.join(course_dir, "solutions") if os.path.isdir(sol_dir): solutions = set(os.listdir(sol_dir)) links, missing = 0, [] chapters = [n for n in sorted(os.listdir(course_dir)) if n.endswith(".html") and not n.endswith("_print.html")] for name in chapters: text = open(os.path.join(course_dir, name), encoding="utf-8").read() for _, base in TXT.findall(text): links += 1 if unquote(base) not in solutions: missing.append((name, base)) linked = {unquote(b) for n in chapters for _, b in TXT.findall(open(os.path.join(course_dir, n), encoding="utf-8").read())} orphans = sorted(solutions - linked) return len(chapters), links, len(solutions), missing, orphans if __name__ == "__main__": for d in sys.argv[1:]: ch, links, sols, missing, orphans = audit(d) print(f"{os.path.basename(d)}: {ch} chapters, {links} solution links, {sols} files in solutions/") print(f" links with no matching file: {len(missing)}") for m in missing[:5]: print(f" {m[0]} -> {m[1]}") print(f" solution files no chapter links to: {len(orphans)}") for o in orphans[:5]: print(f" {o}") Save as audit_all.py: import os, sys from audit_solutions import audit root = sys.argv[1] courses = bad = links_total = 0 problems = [] for dirpath, dirnames, filenames in os.walk(root): if os.path.basename(dirpath) == "solutions": course = os.path.dirname(dirpath) ch, links, sols, missing, orphans = audit(course) courses += 1 links_total += links if missing or orphans: bad += 1 problems.append((os.path.relpath(course, root), len(missing), len(orphans))) print(f"courses with a solutions/ folder: {courses}") print(f"solution links checked: {links_total}") print(f"courses with a problem: {bad}") for rel, m, o in problems[:15]: print(f" {rel}: {m} missing, {o} unlinked") Run it on four courses, then on everything: python audit_solutions.py ... python audit_all.py "/claude-projects/website-content/content" Output for four courses (checked by running it): debian-development-machine-setup: 10 chapters, 30 solution links, 30 files in solutions/ links with no matching file: 0 solution files no chapter links to: 0 entra-id-integrations: 10 chapters, 30 solution links, 30 files in solutions/ links with no matching file: 0 solution files no chapter links to: 0 django-rebuild: 12 chapters, 36 solution links, 36 files in solutions/ links with no matching file: 0 solution files no chapter links to: 0 apache-in-depth: 10 chapters, 30 solution links, 30 files in solutions/ links with no matching file: 0 solution files no chapter links to: 0 Output for all courses (checked by running it): courses with a solutions/ folder: 268 solution links checked: 10975 courses with a problem: 7 office-and-productivity-software\excel\fundamentals: 3 missing, 0 unlinked projects\crunchyroll-downloader: 0 missing, 18 unlinked projects\database-regeneration: 0 missing, 18 unlinked projects\gmail-cleanup: 0 missing, 18 unlinked security\web-application-security\owasp-top-10: 1 missing, 0 unlinked web-development\scripting-and-backend\javascript\fundamentals: 54 missing, 0 unlinked web-development\scripting-and-backend\typescript\fundamentals: 0 missing, 27 unlinked What the real audit found ------------------------- 268 courses have a solutions/ folder and 10,975 solution links were checked. Seven courses have a problem: - JavaScript Fundamentals: 60 links but only 3 files in solutions/ (54 dangling links; the files js1-1 ... are missing). - TypeScript Fundamentals: 27 solution files that no chapter links to (the chapters have no solution links). - The three Practical Scripting projects (crunchyroll-downloader, database-regeneration, gmail-cleanup): 18 solution files each that no chapter links to. - OWASP Top 10: one link, owasp1-7_exercise3.txt, with no file. - Excel Fundamentals: three links, excel1-6_challenge1-3.txt, with no files. These have NOT been fixed; they are findings for you to decide on. WHY THIS WORKS AS AN ANSWER --------------------------- The pipeline rewrites links but never checks them, so a dangling link would only be found by a visitor. A short audit over the real folders finds every one in a few seconds, and running it in the build turns each into a failure you can fix before publishing.