-
Notifications
You must be signed in to change notification settings - Fork 1
Storage and fixes #423
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Storage and fixes #423
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
61ce0c4
storage files
jairovelasquez e645a69
Fixed Algolia, added AWS page, changed the make for faster builds and…
jairovelasquez b373d1b
Deleted images, added a log
jairovelasquez 4791046
Fixed versions
jairovelasquez a803d28
Changelog improvements
jairovelasquez 2c16d8d
Merge branch 'master' into storage-and-fixes
jairovelasquez File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,17 +1,21 @@ | ||
| { | ||
| "index_name": "CodioDocs", | ||
| "sitemap_urls": [ | ||
| "https://docs.codio.com/sitemap.xml", | ||
| "https://docs.codio.com/sitemap-student.xml" | ||
| ], | ||
| "selectors": { | ||
| "lvl0": "main h1", | ||
| "lvl1": "main .section h2", | ||
| "lvl2": "main .section h3", | ||
| "lvl3": "main .section h4", | ||
| "lvl4": "main .section h5", | ||
| "lvl5": "main .section h6", | ||
| "text": "main .section p,main .section blockquote" | ||
| }, | ||
| "only_content_level": true | ||
| "index_name": "CodioDocs", | ||
| "sitemap_urls": [ | ||
| "https://docs.codio.com/sitemap.xml", | ||
| "https://docs.codio.com/sitemap-student.xml" | ||
| ], | ||
| "start_urls": [ | ||
| "https://docs.codio.com/", | ||
| "https://docs.codio.com/students/" | ||
| ], | ||
| "only_content_level": true, | ||
| "selectors": { | ||
| "lvl0": "main h1", | ||
| "lvl1": "main h2", | ||
| "lvl2": "main h3", | ||
| "lvl3": "main h4", | ||
| "lvl4": "main h5", | ||
| "lvl5": "main h6", | ||
| "text": "main p, main li, main blockquote, main td" | ||
| } | ||
jairovelasquez marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,241 @@ | ||
| ''' | ||
| python find_unused_images.py | ||
| python find_unused_images.py --delete | ||
| python find_unused_images.py --delete --yes | ||
| python find_unused_images.py --debug | ||
| ''' | ||
|
|
||
| #!/usr/bin/env python3 | ||
| import argparse | ||
jairovelasquez marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| import csv | ||
| import os | ||
| import re | ||
| from pathlib import Path | ||
| from urllib.parse import urlparse | ||
|
|
||
| ROOT_DIRS = [ | ||
| Path(r"source"), | ||
| Path(r"student-source"), | ||
| ] | ||
|
|
||
| # IMAGE_EXTS = {".png", ".jpg", ".jpeg", ".gif", ".svg", ".webp"} # .svg for logos. | ||
| IMAGE_EXTS = {".png", ".jpg", ".jpeg", ".gif", ".webp"} | ||
|
|
||
| # Matches: | ||
| # .. image:: /path.png | ||
| # .. figure:: /path.png | ||
| # .. |Alias| image:: /path.png | ||
| # And when the directive appears inline (e.g., inside a table cell). | ||
| # Accept plain, "quoted", or 'quoted' paths so we handle spaces. | ||
| DIRECTIVE_RE = re.compile( | ||
| r""" | ||
| \.\.\s+ # leading '.. ' | ||
| (?:\|[^|]+\|\s+)? # optional '|Alias| ' | ||
| (?:image|figure)::\s+ # image:: or figure:: | ||
| (?P<path>"[^"]+"|'[^']+'|\S+) # the path (quoted or non-space) | ||
| """, | ||
| re.IGNORECASE | re.VERBOSE, | ||
| ) | ||
|
|
||
| SKIP_DIRS = {"_build", ".venv", "venv", "node_modules", ".git", "__pycache__"} | ||
|
|
||
| def is_url(s: str) -> bool: | ||
| try: | ||
| p = urlparse(s) | ||
| return bool(p.scheme) and bool(p.netloc) | ||
| except Exception: | ||
| return False | ||
jairovelasquez marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| def common_parent(paths: list[Path]) -> Path: | ||
| parts = os.path.commonpath([str(p) for p in paths]) | ||
| return Path(parts) | ||
|
|
||
| def strip_quotes(s: str) -> str: | ||
| if (s.startswith('"') and s.endswith('"')) or (s.startswith("'") and s.endswith("'")): | ||
| return s[1:-1] | ||
| return s | ||
|
|
||
| def normalize_ref(s: str) -> str: | ||
| # normalize backslashes, trim quotes, strip leading/trailing spaces | ||
| s = strip_quotes(s.strip()).replace("\\", "/") | ||
| return s | ||
|
|
||
| def rel_from_any_root(p: Path, roots_abs: list[Path]) -> tuple[Path, str] | None: | ||
| """Return (root, relative_posix) if p is under any root; else None.""" | ||
| for r in roots_abs: | ||
| try: | ||
| rel = p.relative_to(r).as_posix() | ||
| return r, rel | ||
| except ValueError: | ||
| continue | ||
| return None | ||
|
|
||
| def list_image_files(roots_abs: list[Path], debug=False) -> set[tuple[Path, str]]: | ||
| """ | ||
| Return a set of (root, rel_path) for all images under all roots. | ||
| """ | ||
| found: set[tuple[Path, str]] = set() | ||
jairovelasquez marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| for root in roots_abs: | ||
| for dirpath, dirnames, filenames in os.walk(str(root)): | ||
| dirnames[:] = [d for d in dirnames if d not in SKIP_DIRS] | ||
| for fn in filenames: | ||
| if Path(fn).suffix.lower() in IMAGE_EXTS: | ||
| full = (Path(dirpath) / fn).resolve() | ||
| try: | ||
| rel = full.relative_to(root).as_posix() | ||
| found.add((root, rel)) | ||
| except ValueError: | ||
| if debug: | ||
| print(f"[skip-not-under-root] {full}") | ||
| continue | ||
| if debug: | ||
| print(f"[debug] Found {len(found)} images on disk across {len(roots_abs)} root(s).") | ||
| for root, rel in list(sorted(found))[:10]: | ||
| print(f" - [{root.name}] {rel}") | ||
| return found | ||
jairovelasquez marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| def resolve_reference(rst_file: Path, ref: str, roots_abs: list[Path]) -> tuple[Path, str] | None: | ||
| """ | ||
| Resolve an image/figure reference to (root, relative_posix) across multiple roots. | ||
| Strategy: | ||
| * Absolute (/img/foo.png): try each root/<rel>. | ||
| * Relative (img/foo.png or ../img/foo.png): resolve against rst_file and then map to a root. | ||
| * If file doesn't exist, still attempt to map path to a root by anchor (best-effort). | ||
| """ | ||
| if is_url(ref): | ||
| return None | ||
|
|
||
| ref = normalize_ref(ref) | ||
|
|
||
| if ref.startswith("/"): | ||
| rel = ref.lstrip("/") | ||
| for r in roots_abs: | ||
| cand = (r / rel).resolve() | ||
| if cand.exists(): | ||
| return r, rel | ||
| for r in roots_abs: | ||
| try: | ||
| rst_file.relative_to(r) | ||
| return r, rel | ||
| except ValueError: | ||
| continue | ||
| return None | ||
|
|
||
| candidate = (rst_file.parent / ref) | ||
| try: | ||
| cand_abs = candidate.resolve() | ||
| except FileNotFoundError: | ||
| cand_abs = candidate.absolute() | ||
|
|
||
| mapped = rel_from_any_root(cand_abs, roots_abs) | ||
| if mapped: | ||
| return mapped | ||
|
|
||
| for r in roots_abs: | ||
| try: | ||
| rst_file.relative_to(r) | ||
| try: | ||
| rel = cand_abs.relative_to(r).as_posix() | ||
| return r, rel | ||
| except ValueError: | ||
| return r, Path(ref).as_posix() | ||
| except ValueError: | ||
| continue | ||
| return None | ||
|
|
||
| def parse_rst_image_refs(roots_abs: list[Path], debug=False) -> set[tuple[Path, str]]: | ||
| """ | ||
| Parse all .rst files under all roots and collect referenced images as (root, rel_path). | ||
| """ | ||
| refs: set[tuple[Path, str]] = set() | ||
| for root in roots_abs: | ||
| for dirpath, dirnames, filenames in os.walk(str(root)): | ||
| dirnames[:] = [d for d in dirnames if d not in SKIP_DIRS] | ||
| for fn in filenames: | ||
| if not fn.lower().endswith(".rst"): | ||
| continue | ||
| rst_path = Path(dirpath) / fn | ||
| try: | ||
| text = rst_path.read_text(encoding="utf-8") | ||
| except UnicodeDecodeError: | ||
| text = rst_path.read_text(encoding="latin-1") | ||
|
|
||
| for m in DIRECTIVE_RE.finditer(text): | ||
| raw = m.group("path") | ||
| raw = normalize_ref(raw) | ||
| if not any(raw.lower().endswith(ext) for ext in IMAGE_EXTS): | ||
| continue | ||
| resolved = resolve_reference(rst_path.resolve(), raw, roots_abs) | ||
| if resolved is not None: | ||
| refs.add(resolved) | ||
| elif debug: | ||
| print(f"[warn] Could not resolve: {raw} in {rst_path}") | ||
|
|
||
| if debug: | ||
| print(f"[debug] Found {len(refs)} image/figure references across {len(roots_abs)} root(s).") | ||
| for root, rel in list(sorted(refs))[:10]: | ||
| print(f" - [{root.name}] {rel}") | ||
| return refs | ||
|
|
||
| def write_csv(unused: list[tuple[Path, str]], csv_path: Path): | ||
| csv_path.parent.mkdir(parents=True, exist_ok=True) | ||
| with csv_path.open("w", newline="", encoding="utf-8") as f: | ||
| writer = csv.writer(f) | ||
| writer.writerow(["root", "image_path"]) # which root + rel path | ||
| for root, rel in unused: | ||
| writer.writerow([str(root), rel]) | ||
| print(f"[info] CSV written: {csv_path}") | ||
|
|
||
| def delete_files(unused: list[tuple[Path, str]], yes=False): | ||
| if not unused: | ||
| print("[info] No unused images to delete.") | ||
| return | ||
| if not yes: | ||
| confirm = input(f"⚠️ Delete {len(unused)} unused images across all roots? (y/N): ").strip().lower() | ||
| if confirm != "y": | ||
| print("[info] Deletion canceled.") | ||
| return | ||
| deleted = 0 | ||
| for root, rel in unused: | ||
| try: | ||
| (root / rel).unlink(missing_ok=True) | ||
| deleted += 1 | ||
| except Exception as e: | ||
| print(f"[error] Could not delete [{root.name}] {rel}: {e}") | ||
| print(f"[info] Deleted {deleted} unused image(s).") | ||
|
|
||
| def main(): | ||
| parser = argparse.ArgumentParser( | ||
| description="Find or delete unused images referenced via '.. image::', '.. figure::', or '.. |alias| image::' across multiple Sphinx roots." | ||
| ) | ||
| parser.add_argument("--delete", action="store_true", help="Delete unused images instead of listing them") | ||
| parser.add_argument("--yes", action="store_true", help="Skip confirmation prompt when deleting") | ||
| parser.add_argument("--debug", action="store_true", help="Show debug info") | ||
| args = parser.parse_args() | ||
|
|
||
| roots_abs = [p.resolve() for p in ROOT_DIRS] | ||
| for r in roots_abs: | ||
| print(f"[info] Root directory: {r}") | ||
|
|
||
| images_on_disk = list_image_files(roots_abs, debug=args.debug) # set[(root, rel)] | ||
| referenced = parse_rst_image_refs(roots_abs, debug=args.debug) # set[(root, rel)] | ||
|
|
||
| referenced_rels = {rel for (_root, rel) in referenced} | ||
| unused = sorted([(root, rel) for (root, rel) in images_on_disk if rel not in referenced_rels], | ||
| key=lambda x: (str(x[0]), x[1])) | ||
|
|
||
jairovelasquez marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| print(f"[result] Found {len(unused)} unused image(s) across all roots).") | ||
jairovelasquez marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| out_csv = common_parent(roots_abs) / "unused_images.csv" | ||
|
|
||
| if args.delete: | ||
| delete_files(unused, yes=args.yes) | ||
| else: | ||
| write_csv(unused, out_csv) | ||
| if args.debug and unused: | ||
| print("First few unused images:") | ||
| for root, rel in unused[:10]: | ||
| print(f" - [{root.name}] {rel}") | ||
|
|
||
| if __name__ == "__main__": | ||
| main() | ||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Binary file removed
BIN
-3.11 KB
source/_static/img/class_administration/addteachers/myschoolorg.png
Diff not rendered.
Diff not rendered.
Binary file removed
BIN
-25.5 KB
source/_static/img/class_administration/createanorganization/classteststudents.png
Diff not rendered.
Binary file removed
BIN
-25.2 KB
source/_static/img/class_administration/createanorganization/completeform.png
Diff not rendered.
Binary file removed
BIN
-2.33 KB
source/_static/img/class_administration/createanorganization/createorg.png
Diff not rendered.
Binary file removed
BIN
-9.67 KB
source/_static/img/class_administration/createanorganization/org_teams.png
Diff not rendered.
Binary file removed
BIN
-29 KB
source/_static/img/class_administration/createanorganization/studentlogin.png
Diff not rendered.
Binary file removed
BIN
-38.4 KB
source/_static/img/class_administration/createanorganization/test-student-view.png
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Binary file removed
BIN
-9.5 KB
source/_static/img/class_administration/grading/grading-template.png
Diff not rendered.
Diff not rendered.
Binary file removed
BIN
-169 KB
source/_static/img/class_administration/grading/template-example.png
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Binary file removed
BIN
-32.3 KB
source/_static/img/class_administration/navigatingcodio/myclassroom.png
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Binary file removed
BIN
-21.8 KB
source/img/class_administration/createanorganization/classteststudents.png
Diff not rendered.
Binary file removed
BIN
-25.2 KB
source/img/class_administration/createanorganization/completeform.png
Diff not rendered.
Diff not rendered.
Diff not rendered.
Binary file removed
BIN
-29 KB
source/img/class_administration/createanorganization/studentlogin.png
Diff not rendered.
Binary file removed
BIN
-18 KB
source/img/class_administration/createanorganization/test-student-view.png
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Binary file removed
BIN
-46.2 KB
source/img/curriculum_mapped_content/autoassessments/autoexample.png
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.