Conversation
OpenAPI ChangesNo changes detected Unexpected changes? Ensure your branch is up-to-date with |
Everything under an OLX export's static/ directory was ingested, including assets left over from earlier offerings. u-lab carries 27 syllabus files back to a 2014 edition, and they dominated retrieval for the questions they are worst at answering. documents_from_olx now skips, in addition to staff-only subtrees: - static files no block refers to, matched on filename against the course text so that "asset-v1:...+type@asset+block/<name>" links count as well as "/static/<name>", and percent-encoded and entity-escaped spellings do too - legacy transcripts (subs_<id>.srt.sjson) whose video id no block declares, read from video elements rather than by matching raw attribute text - the asset manifests and info/updates.items.json, which list or mention every asset and would otherwise keep all of them alive; live announcements in that file still count as references, deleted ones do not Staff-only files are excluded as reference sources too, so an asset only a hidden block mentions is unreferenced. unpublish_staff_only_files becomes unpublish_excluded_files and walks the same exclusion function, so what ingestion skips and what the cleanup removes cannot drift. It gains --dry-run and a --report CSV written by the command process, since the tasks fan out across workers. audit_olx_references reports the same filter against an extracted archive, so the per-course numbers can be reproduced without S3. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
It only counted what the unreferenced-static filter drops, so it under-reported what unpublish_excluded_files would actually unpublish - it missed the static files caught by the staff-only set. It now walks excluded_olx_paths, the same function ingestion and the cleanup use, and splits the total by location and file class. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The repo does not generally test management commands, and this one collided with recreate_index_test on CI: learning_resources/management has no __init__.py, so pytest named both modules commands.<name> and the second import failed. edx_shared_test covers dry_run and the report rows at the function level. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A hidden video's transcripts go into the staff-only set, but u.lab keeps a staff-only duplicate of many videos, so the live copy's transcripts went with them: 279 transcripts of visible videos in 15.671.1x were excluded even though static_olx_references named the live video block as their referrer. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
mbertrand
force-pushed
the
mb/skip-unreferenced-olx-static-files
branch
from
September 16, 2026 13:42
190ef62 to
4cda9e1
Compare
staff_only_olx_paths deduped blocks first-wins while walking, so a block hanging under both a visible and a staff-only vertical was hidden or not depending on stack order. In 15.671.1x that dropped video/ec404ea2 and the six transcripts it declares, though a visible vertical holds it too. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
Multi-ID YouTube declarations can cause valid legacy transcripts to be incorrectly excluded.
Get a fresh assessment by requesting another Copilot review.
Review details
- Files reviewed: 10/10 changed files
- Comments generated: 2
- Review effort level: Balanced
The legacy attribute is a comma-separated "<speed>:<id>" list. Splitting the whole value on ":" kept only the last entry's id, so transcripts named for any other speed's id looked orphaned and were dropped. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
audit_olx_references already answers what the CSV answered, off any extracted tree with no DB, worker or --resource-ids, so the report flag and the rows it threaded through the result backend can go. The return type goes back to int. static_olx_references returned the name of each file's referrer, but its only caller tested it against None, so it now returns the referenced and unreferenced sets instead. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
unpublish_excluded_content_files now returns a row per run — run_id, excluded, unpublished and the run's total content files — instead of a bare count, so the command can print a line per run and a summary per source. Counts rather than paths, so the payload is bounded by run count and crosses the result backend at any scale, which is what the old per-file CSV could not do. --report writes the same rows as CSV, and no longer needs --resource-ids. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
What are the relevant tickets?
Closes https://github.com/mitodl/hq/issues/13350
Description (What does it do?)
documents_from_olxnow skips static files nothing in the course refers to, matching on filename against the text of every block so thatasset-v1:...+type@asset+block/<name>links count as well as/static/<name>, and percent-encoded and entity-escaped spellings do too.subs_<id>.srt.sjson) are kept only when a video block declares their id viasub/youtube/youtube_id_1_0. Those are read by parsing the video elements, not by matching attribute text, sosub='x'andsub = "x"count.info/updates.items.jsonare no longer ingested and no longer count as references. Announcements the course team deleted don't keep an asset alive; live ones still do.unpublish_staff_only_filesbecomesunpublish_excluded_filesand walks the same exclusion function, so what ingestion skips and what the cleanup removes can't drift. It gains--dry-run, and reports what each run excludes out of the content files it has, optionally as a CSV.audit_olx_referencesreports the same exclusions against an extracted archive without touching the DB.How can this be tested?
Uses 15.671.1x, the course from the issue.
Pull and extract the 3T2026 archive the counts below come from (560MB, so this is the slow step):
A newer nightly archive works too, it just won't match the numbers exactly.
See what the filter excludes, no DB needed:
To exercise the command itself, create the run and the content files ingestion would produce from that archive, without putting 5430 files through tika:
seed.pydocker compose run --rm -v /tmp/archives:/archives web \ ./manage.py shell -c "exec(open('seed.py').read())"5430 paths collapse to 4932 keys because
get_edx_module_idfolds spaces to underscores.The 2014 syllabus the issue names is served, and
--dry-runsays what would go without touching it:excludedcounts the rows the archive excludes whatever their publish state,unpublishedonly the ones this call would flip, so a re-run shows the same 3852 excluded and 0 to unpublish.Drop
--dry-runto apply it, then re-check the counts and the same curl:The curl from step 4 now returns
{"count":0,"paths":[]}, and the same run queuesdeindex_run_content_filesandremove_unpublished_run_content_files.Additional Context
I ran step 2 against the current archive of one course per edX source. mit_edx 6.002.1x is the control: nothing in it is unreferenced, and the 434 transcripts it loses are all ones only staff-only blocks link, so 2162 of 2596 stay. Staff-only, manifests, transcripts and other static sum to the excluded total; videos counts staff-only blocks of all video blocks rather than files, so it sits outside that sum; one video carries a transcript per language.
Across those four, the filter never drops a transcript a visible video block declares: 7605 transcripts on disk, zero violations. 15.671.1x is the hard case, with 143 of its 224 video blocks staff-only; its 81 visible ones declare 574 transcript files in 13 languages, 560 of them in the archive, and all 560 are kept. Nor is the content lost — every distinctive line of the dropped
01_Intro_SPT_ITA.srtsurvives verbatim in the kept1dd98469-ed52-4163-b76f-9f3866b10e93-it.srt, so what goes is an older copy of a replaced video.Worth knowing before this runs on production: after the command, 15.671.1x keeps 5 of its 137 static documents, and none of them is a syllabus. The current run's syllabus link (
u-lab_1x_Syllabus_2022.pdf) lives inhtml/1b8267754ed64889a30cfdf09a120e95.html, which is avisible_to_staff_onlyblock, so #3909 already excludes the block and this PR now excludes the file it links. That's consistent — learners can't see that link either — but if we'd rather a run's syllabus survive regardless, that's a separate rule and I'd rather add it deliberately than by accident.Two limits I'd rather state than have found in review. An asset mentioned only inside a hidden block that sits inline in a parent XML file is still kept, because there's no separate file to exclude from the scan; all four archives are pointer-style and #3909 found no production course with inline staff-only blocks, so this is unobserved, but the answer-key exclusion is best-effort rather than a guarantee.