Skip to content

Address #74 review: export coverage, CSV safety, and backfill memory - #76

Open
asuworks wants to merge 8 commits into
comses:mainfrom
asuworks:fix/pr74-review-followup
Open

asuworks wants to merge 8 commits into
comses:mainfrom
asuworks:fix/pr74-review-followup

Conversation

@asuworks

@asuworks asuworks commented Sep 27, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Follow-up to #74, addressing @alee's review.

Export tests, numbered as in the review.
Every case uses the suggested export_rows helper, so write_all and stream must agree:

  1. non-primary publications are excluded;
  2. platforms and sponsors are encoded as one column per level;
  3. creators and tags are rejected as unsupported many-to-many attributes;
  4. a publication without authors exports an empty cell;
  5. embedded newlines and quotes round-trip;
  6. several publications export one row each, in id order; get_publications() now orders by id, because rows otherwise followed PostgreSQL's physical order;
  7. cells starting with a formula character get a leading ', 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=True and 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 on np.float under 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.
  • Migration 0038 kept every updated publication in memory before writing.
    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, and make migrations-check pass;
  • both backfill variants ran twice against the production dump with identical results.

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.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Critical CSV-injection protections remain incomplete.

Review effort: Lite
Findings: 2 High severity

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.

Comment thread citation/export_data.py Outdated
Comment thread citation/export_data.py
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.
@asuworks asuworks self-assigned this Sep 27, 2026
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