Skip to content

feat(view for 990 forms): redirect to latest uploaded form - #3643

Merged
relyks merged 4 commits into
masterfrom
self-updating-link-for-990-form
Sep 3, 2026
Merged

relyks merged 4 commits into
masterfrom
self-updating-link-for-990-form

Conversation

@relyks

@relyks relyks commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Add /current-990-form, which redirects to the highest-year matching Form 990 PDF in static/files.
  • Update every public Form 990 link to use the stable endpoint.
  • Add coverage for latest-file selection and missing files.

Why

Uploading a new Sefaria_YYYY_990_Public.pdf now automatically makes it the public Form 990 without changing page links.

Validation

  • DJANGO_SETTINGS_MODULE=sefaria.settings python -m pytest -q reader/tests/current_990_form_test.py

@relyks
relyks marked this pull request as ready for review August 19, 2026 08:17
@gitvelocity-reviewer

Copy link
Copy Markdown

📊 Code Quality Score: 8/100

Base Score 32 × ESF 0.25 = 8

Category Score Factors
🔭 Scope 7/20 Five files are modified: reader/views.py adds the new view, sites/sefaria/urls.py adds the route, static/js/StaticPages.jsx updates two link hrefs, templates/static/he/ways-to-give.html updates one link, and reader/tests/current_990_form_test.py is a new test file. All changes are within the static-page-serving subsystem.
🏗️ Architecture 5/20 The change converts two static file references into a server-side redirect view, following the same pattern already used by annual_report in reader/views.py. No new module boundary or dependency is introduced.
⚙️ Implementation 6/20 current_990_form uses re.compile with a walrus operator inside a list comprehension to extract year integers from filenames, then calls max() on the resulting list of (year, filename) tuples to select the latest file. The STATICFILES_DIRS[0] index and os.listdir call are unguarded against an empty list or a missing directory.
⚠️ Risk 4/20 The two replaced links in StaticPages.jsx and ways-to-give.html now go through a server redirect instead of directly to a file; a misconfigured STATICFILES_DIRS or absent files directory would surface as an unhandled IndexError or FileNotFoundError rather than an Http404. The change is easily reverted by restoring the hardcoded hrefs.
✅ Quality 9/15 current_990_form_test.py provides two pytest tests using tmp_path and monkeypatch: one verifies the redirect targets the 2025 file over 2023 while ignoring Sefaria_2026_Annual_Report.pdf, and one verifies Http404 is raised when no matching file exists. No test covers an empty STATICFILES_DIRS list or a missing files subdirectory.
🔒 Perf / Security 1/5 The view performs a single os.listdir call on each request with no caching. No security-specific work is present beyond what the existing Django request handling provides.

Was this score accurate? 👍 Yes · 👎 No

How this was scored →

Scored by GitVelocity · How are scores calculated?

@relyks
relyks requested a review from yitzhakc August 21, 2026 19:59
@relyks
relyks added this pull request to the merge queue Sep 3, 2026
Merged via the queue into master with commit 8a3076c Sep 3, 2026
20 checks passed
@gitvelocity-reviewer

Copy link
Copy Markdown

📊 Code Quality Score: 15/100

Base Score 37 × ESF 0.4 (Small: 67 effective lines, 5 files) = 14.8, rounded to 15

Category Score Factors
🔭 Scope 7/20 Five files are modified: reader/views.py adds two functions, sites/sefaria/urls.py adds one URL pattern, static/js/StaticPages.jsx updates two href values, templates/static/he/ways-to-give.html updates one href, and reader/tests/current_990_form_test.py is created new. The change is contained within the donation/static-pages subsystem with no database or external service involvement.
🏗️ Architecture 5/20 get_current_990_form_filename is extracted as a standalone @lru_cache(maxsize=1)-decorated function separate from the current_990_form view, so the cache is independently clearable and the file-discovery logic is independently testable. No new module boundary or external dependency is introduced.
⚙️ Implementation 7/20 get_current_990_form_filename uses a walrus-operator list comprehension with re.compile(r'^Sefaria_(\d{4})_990_Public\.pdf$') to extract year integers, then selects the maximum via max(). Http404 is raised when the list is empty. The current_990_form view calls the cached helper and returns a redirect to STATIC_URL + 'files/' + filename.
⚠️ Risk 5/20 The lru_cache persists for the process lifetime, so a newly uploaded 990 PDF will not be served until the process restarts; this behavior is not documented in the code or a runbook. A missing or misconfigured STATICFILES_DIRS[0]/files/ directory raises Http404. The change is reversible by restoring the hardcoded hrefs.
✅ Quality 11/15 current_990_form_test.py provides four tests: URL resolution via resolve, redirect to the highest-year PDF with decoy files present, cache hit verification by counting os.listdir calls via monkeypatch, and Http404 when no matching PDF exists. The autouse fixture calls get_current_990_form_filename.cache_clear() before and after each test. No test covers the case where STATICFILES_DIRS is empty or files/ is absent as a directory.
🔒 Perf / Security 2/5 @lru_cache(maxsize=1) prevents repeated filesystem scans within a process lifetime. The redirect target is derived from filenames matched by a strict regex against os.listdir output, not from user-supplied input, so there is no injection surface on the redirect URL.

Was this score accurate? 👍 Yes · 👎 No

How this was scored →

Scored by GitVelocity · How are scores calculated?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants