Repository navigation
Conversation
Every case runs through both write_all and stream and asserts that they agree.
Without an explicit order, rows followed PostgreSQL's physical order, which changes after updates.
Prefix a single quote to text cells that start with =, +, -, @, tab, or carriage return, following the OWASP CSV injection guidance.
Compare sorted primary key lists instead of sets so duplicate rows fail the assertions.
export() crashed on np.float under NumPy 2, and pandas 3 silently ignored the chained assignments that group platforms and sponsors beyond the top 50. Also move ArrayAgg to order_by, since ordering is deprecated in Django 5.2.
The backfill kept every updated publication in memory before writing. On the production dump it now peaks at 100 MiB instead of 1045 MiB with identical results. 0038 has not run in production yet.
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Critical CSV-injection protections remain incomplete.
Review effort: Lite
Findings: 2
Open (2)
What changed in this PR
This PR improves publication/research exports, graph filtering, and publication-year backfill performance.
Changes:
- Adds export, CSV-safety, and graph-filter regression tests.
- Fixes deterministic ordering and pandas/NumPy compatibility.
- Batches migration updates to reduce memory usage.
| File | Summary |
|---|---|
tests/test_migrations.py |
Tests batched year backfills. |
tests/test_graphviz_data.py |
Covers graph-filter edge cases. |
tests/test_export_data.py |
Adds export and CSV-safety coverage. |
citation/migrations/0038_backfill_publication_year_published.py |
Processes backfill updates in batches. |
citation/export_data.py |
Fixes export behavior and compatibility; two critical CSV-injection gaps remain in multi-file export and newline handling. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
The current OWASP CSV injection guidance also lists line feed and the full-width =, +, -, and @ used in some locales.
export() wrote database values straight to pandas to_csv(), bypassing the curator exporter's sanitizer.
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.

Summary
Follow-up to #74, addressing @alee's review.
Export tests, numbered as in the review.
Every case uses the suggested
export_rowshelper, sowrite_allandstreammust agree:creatorsandtagsare rejected as unsupported many-to-many attributes;get_publications()now orders by id, because rows otherwise followed PostgreSQL's physical order;', following the OWASP CSV injection guidance:=,+,-,@, tab, carriage return, line feed, and the full-width=,+,-,@.The multi-file
export()applies the same neutralization.No current value in either export starts with one of these characters, and the change lives in separate commits (96da7f4, 905ea09, 60da0e5) that can be dropped if the files are only read programmatically.
Graph filter tests cover a publication without a year under
require_year=Trueand a multi-author publication returned once per author.Assertions compare sorted lists instead of sets, so duplicate rows fail.
Also:
export(), the multi-file research export, crashed onnp.floatunder NumPy 2, and pandas 3 silently ignored the chained assignments that group platforms and sponsors beyond the top 50.Both are fixed and covered by a test that writes all eleven files; on the production dump the export completes in about 10 s.
On the production dump (290,922 publications) it now peaks at 100 MiB instead of 1,045 MiB, fills the same 289,278 years, and runs in about 35 s instead of 41 s.
This edits a merged migration before it has run in production; 49d0179 can be dropped if you would rather not.
Verification
make test(92 tests),make check, andmake migrations-checkpass;