Skip to content

Commit f7c0944

Browse files
committed
many minor improvements to field processing and reporting
1 parent a049d19 commit f7c0944

18 files changed

Lines changed: 849 additions & 90 deletions

ARCHITECTURE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ important than maximal throughput.
2626
URL, or file led to the observation.
2727
6. Handle real-world bibliography messiness without treating every textual
2828
difference as an error.
29+
7. Prefer slow and accurate analysis.
2930

3031
## Non-Goals
3132

CHANGELOG.md

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,32 @@
22

33
## Unreleased
44

5+
### Reports
6+
- Added a documentation page explaining Markdown report flags, field comparison
7+
flags, entry-type-specific required fields, normalization behavior, and fetch
8+
manifests.
9+
- Reworked Markdown reports to reduce redundant checklist text. Each entry now
10+
has a compact general-flags table for source discovery, provider errors,
11+
required fields, comparison availability, PDF candidates, and confidence.
12+
Field-level review flags now appear as a `Flag` column in the field
13+
comparison table alongside importance, status, BibTeX value, source value,
14+
and diagnostic note.
15+
- `@inproceedings` and related proceedings/chapter entries now compare the
16+
container title as `booktitle`, not `journal`, in the default field
17+
comparison set.
18+
- `@book` and related chapter entries with an `editor` but no `author` now
19+
compare the creator field as `editor`, rather than reporting a missing
20+
`author`.
21+
22+
### Validation transparency
23+
- `check_bibliography` now emits a clear `@warn` when it falls back to
24+
`CandidateProvider` (no fixture, no explicit `providers`, `use_apis=false`),
25+
since that fallback only echoes each entry's own title/doi/url back as its
26+
"source" and cannot validate anything against an independent record.
27+
- `compare_entry` now adds an explicit note to the `EntryReport` itself in
28+
that same situation, so the limitation is visible in the generated
29+
Markdown/INC report, not only in a log line a reader may not see.
30+
531
### Architecture
632
- Split monolithic `src/PaperFetch.jl` into focused include files:
733
`normalize.jl`, `bib.jl`, `identifiers.jl`, `providers.jl`, `compare.jl`,
@@ -38,6 +64,39 @@
3864
API responses are written to and read from a local directory to support
3965
repeat runs without re-querying providers.
4066

67+
### Bug fixes (round 4)
68+
- Fixed `pubmed_search_ids`: NCBI returns `idlist:[]` (not an omitted field)
69+
for any search with zero hits, which is the common case for non-biomedical
70+
DOIs. JSON3 parses an empty JSON array with no element-type hint as eltype
71+
`Union{}`, so `String.(...)` over it produced a `Vector{Union{}}` instead of
72+
`Vector{String}`. That failed to dispatch on
73+
`pubmed_summary_records(::ApiProvider, ::Vector{String}; ...)`, so every
74+
non-biomedical lookup was silently converted into a spurious
75+
`pubmed-error`/`pubmed-search-error` source carrying a `MethodError`
76+
instead of cleanly reporting "no PubMed hit". Switched to an explicitly
77+
`String[...]`-typed comprehension so the empty case stays `Vector{String}`.
78+
- Fixed misleading "year" reason in `compare_entry`'s discarded-candidate
79+
notes: the diagnostic listed "year" as a hard-mismatch reason whenever a
80+
publication-year gap existed at all, even a gap of 0 or 1 that does not
81+
meet the actual hard-mismatch threshold (`>=2`, or `>=3` for books). Added
82+
`year_hard_mismatch` and reused it both in `source_hard_mismatch` and in the
83+
note-building code so the explanation always matches the real cutoff.
84+
85+
### Bug fixes (round 3)
86+
- Fixed `sources_for(::ApiProvider, ...)`: an identifier such as a DOI that
87+
resolved successfully but pointed at the wrong work (mistyped or swapped
88+
DOI) previously suppressed the title/author search fallback, because the
89+
fallback only ran when *no* usable source was found at all. Added
90+
`identifier_source_conflicts` (reusing the same hard-mismatch comparison as
91+
`compare_entry`) so the fallback also runs when every identifier-resolved
92+
source hard-mismatches the entry's title or author, letting the package find
93+
the correct work under a different DOI.
94+
- Improved `compare_entry` diagnostics: discarded hard-mismatch candidates are
95+
now reported in `EntryReport.notes` even when a reliable replacement source
96+
is found, and an explicit note is added when the chosen best source was
97+
found via title/author search under a DOI different from the one in the
98+
bibliography, so the inconsistency stays visible to the reviewer.
99+
41100
### Bug fixes (round 2)
42101
- Fixed `openlibrary_isbn_records`: author objects from the ISBN endpoint only
43102
carry a key path (`/authors/OL1A`), not an inline name. Authors are now only

README.md

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,14 @@ or separate AI-assisted editing task can improve the bibliography deliberately.
3030
page ranges, Unicode/LaTeX accents, DOI URL variants, author initials, and
3131
similar harmless differences. URL paths and queries keep their case, and
3232
reordered author lists are flagged for review rather than silently accepted.
33+
- Uses entry-type-aware comparisons: proceedings and chapter entries compare
34+
`booktitle`, and edited books can use `editor` instead of `author`.
35+
- Matches URLs found in `url`, `note`, or `howpublished`, including LaTeX
36+
`\url{...}` forms, when checking URL-backed references.
3337
- Writes Markdown and INC reports; INC is a spreadsheet-friendly CSV-like format
3438
handled by IncCSV.jl.
35-
- Optionally downloads PDFs from explicit PDF candidate URLs and writes a fetch
36-
manifest.
39+
- Optionally downloads PDFs from explicit PDF candidate URLs and writes fetch
40+
manifests.
3741

3842
## What PaperFetch Does Not Do
3943

@@ -82,6 +86,11 @@ The Markdown report is meant for direct reading. The INC report is meant for
8286
spreadsheets and downstream tooling. CLI report names default to the input file
8387
stem; pass `--report-basename NAME` to choose a different basename.
8488

89+
Each Markdown entry keeps the original BibTeX key, then shows general flags for
90+
source discovery, provider errors, required fields, PDF candidates, and
91+
confidence. Field-by-field comparisons include a `Flag` column so green, amber,
92+
red, and ignored review signals are visible next to the relevant value.
93+
8594
## Live API Checks
8695

8796
Live provider lookup is opt-in:
@@ -120,11 +129,17 @@ Outputs include:
120129

121130
- `paperfetch_out/references.md`
122131
- `paperfetch_out/references.inc`
132+
- `paperfetch_out/manifest.md`
123133
- `paperfetch_out/manifest.inc`
124134
- downloaded `*.pdf` files when candidate URLs are available and reachable
125135

126-
Entries without PDF candidates are recorded as `skipped`, not as validation
127-
failures.
136+
`manifest.md` is the human-readable fetch table. `manifest.inc` is the
137+
spreadsheet/tooling manifest. Entries without PDF candidates are recorded as
138+
`skipped`, not as validation failures.
139+
140+
The manifest records the reference key, compact title, fetch status, local file
141+
when downloaded, source URL, and a short diagnostic such as "no PDF candidate",
142+
"downloaded from ...", or a failed HTTP/content-type reason.
128143

129144
## Credential-Assisted Fetching
130145

@@ -173,6 +188,8 @@ paths[:inc]
173188
results, manifest = fetch_pdfs(reports, "paperfetch_out")
174189
```
175190

191+
This writes both `paperfetch_out/manifest.md` and `paperfetch_out/manifest.inc`.
192+
176193
For deterministic offline runs, pass a fixture instead of live APIs:
177194

178195
```julia
@@ -210,7 +227,8 @@ julia --project=. test/online/runtests.jl
210227
## Documentation
211228

212229
The documentation includes a quickstart, examples, API reference, and notes on
213-
live providers, caching, rate limiting, and building a stand-alone executable:
230+
live providers, report formats, fetch manifests, caching, rate limiting, and
231+
building a stand-alone executable:
214232

215233
https://mroughan.github.io/PaperFetch.jl/dev
216234

docs/make.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ makedocs(;
2020
"Home" => "index.md",
2121
"Examples" => "examples.md",
2222
"Live Providers" => "providers.md",
23+
"Reports and Manifests" => "reports.md",
2324
"Stand-Alone Executable" => "standalone.md",
2425
"API Reference" => "api.md",
2526
],

docs/src/examples.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,15 @@ Not every bibliography item is a journal article or has a PDF:
5454
PaperFetch.jl records the absence of a PDF candidate as a fetch status, not as
5555
a bibliography error.
5656

57+
Book-like entries use book-specific fields during comparison. `@inbook` and
58+
`@incollection` compare container metadata as `booktitle`, and `@book`,
59+
`@inbook`, and `@incollection` entries can use `editor` as the creator field
60+
when no `author` field is present.
61+
62+
URL-backed examples may place URLs in either `url` or common prose fields such
63+
as `note` and `howpublished`. LaTeX `\url{...}` macros are normalized before
64+
comparison.
65+
5766
## Plain DOI Lists
5867

5968
Plain text input is also accepted:
@@ -77,6 +86,7 @@ julia --project=. -e 'using PaperFetch; PaperFetch.main()' -- \
7786
--email your.email@example.edu \
7887
--use-apis \
7988
--cache-dir .paperfetch_cache \
89+
--rate-limit-seconds 0.05 \
8090
--outdir paperfetch_out
8191
```
8292

@@ -102,3 +112,8 @@ julia --project=. test/online/runtests.jl
102112
The online runner checks that each example returns at least one non-error
103113
source, has an exact normalized DOI match, writes reports, and finds at least
104114
one PDF candidate across the set.
115+
116+
The generated Markdown report uses entry-level general flags plus field-level
117+
flags in the comparison table. Fetch mode also writes `manifest.md` and
118+
`manifest.inc`, which are the best first place to inspect why a PDF did or did
119+
not download.

docs/src/index.md

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ table with metadata, suitable for spreadsheets and downstream tooling. CLI
3737
report names default to the input file stem; pass `--report-basename NAME` to
3838
choose a different basename.
3939

40+
Markdown reports preserve the original BibTeX keys. Each entry has a compact
41+
general-flags table for source discovery, provider errors, required fields, PDF
42+
candidates, and confidence, followed by a field comparison table with a `Flag`
43+
column for green, amber, red, or ignored review signals.
44+
4045
## Julia API Tutorial
4146

4247
```julia
@@ -65,15 +70,20 @@ Fetch mode uses explicit PDF candidate URLs from source metadata:
6570
results, manifest = fetch_pdfs(reports, "paperfetch_out")
6671
```
6772

68-
Entries without PDF candidates are recorded as `skipped`, not as validation
69-
failures.
73+
This writes `manifest.md`, a human-readable table of references and fetch
74+
diagnostics, plus `manifest.inc` for spreadsheet and tooling use. Entries
75+
without PDF candidates are recorded as `skipped`, not as validation failures.
7076

7177
## Design Commitments
7278

7379
- BibTeX input is parsed, never rewritten.
7480
- DOI and similar identifiers use exact normalized comparison.
7581
- Titles, authors, pages, journal names, and other bibliographic text are
7682
compared with documented normalization.
83+
- Edited books may use `editor` instead of `author`; proceedings and chapter
84+
entries compare their container as `booktitle`.
85+
- URLs found in `note` or `howpublished`, including LaTeX `\url{...}` macros,
86+
can be matched to source URL metadata.
7787
- URL normalization preserves path and query case while canonicalizing DOI
7888
resolver links and HTTP(S) hosts.
7989
- Author order is treated as meaningful. Reordered author lists are marked for

docs/src/providers.md

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,18 +40,28 @@ PaperFetch.jl chooses lookup routes from the identifiers and entry type it can
4040
see:
4141

4242
- DOI-backed entries can use Crossref, OpenAlex, Unpaywall, DataCite, Semantic
43-
Scholar, PubMed, CORE, and Figshare.
43+
Scholar, PubMed, CORE, and Figshare. DOI strings can be recovered from the
44+
`doi` field, DOI resolver URLs, and common misplaced fields such as `note`,
45+
`url`, and `howpublished`.
4446
- arXiv identifiers use the arXiv API.
4547
- PMID values use PubMed.
4648
- ISBN-backed books can use Open Library and Google Books.
47-
- Entries with only a title can use title-and-author search.
49+
- Entries with only a title, or entries whose identifier resolves to an
50+
obviously different work, can use title-and-author fallback search.
4851
- URL-backed entries can check the URL and read common citation metadata from
49-
the landing page.
52+
the landing page. URLs can be taken from `url`, `note`, or `howpublished`,
53+
including LaTeX `\url{...}` macros.
5054

5155
Provider results are candidates, not automatic truth. The report records
5256
agreement, conflicts, missing fields, and provider errors so a person can decide
5357
what should be fixed in the original BibTeX.
5458

59+
Book-like entries are handled with their expected fields. Proceedings and
60+
chapter entries compare `booktitle` rather than `journal`; edited books can
61+
compare `editor` as the creator when no `author` field is present. A large year
62+
gap for a book is treated as evidence that a provider may have returned a
63+
different edition.
64+
5565
## Caching And Rate Limiting
5666

5767
`--cache-dir` stores API and landing-page responses by request URL and headers.

docs/src/reports.md

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
# Reports and Manifests
2+
3+
PaperFetch.jl reports are the main output of a check. They are designed to
4+
support review, not automatic editing: the input `.bib` file is never rewritten.
5+
6+
## Check Reports
7+
8+
`check` mode writes two files:
9+
10+
- a Markdown report for direct human review;
11+
- an INC report for spreadsheets and downstream tooling.
12+
13+
From the command line, report names default to the input file stem:
14+
15+
```bash
16+
julia --project=. -e 'using PaperFetch; PaperFetch.main()' -- \
17+
check examples/01_exact_article.bib \
18+
--fixture examples/metadata_fixture.json \
19+
--outdir paperfetch_out
20+
```
21+
22+
This writes `paperfetch_out/01_exact_article.md` and
23+
`paperfetch_out/01_exact_article.inc`. Use `--report-basename NAME` to choose a
24+
different basename.
25+
26+
Direct Julia API calls use `paperfetch_report` unless `basename` is supplied:
27+
28+
```julia
29+
paths = write_reports(reports, "paperfetch_out"; basename="references")
30+
```
31+
32+
## Markdown Report Layout
33+
34+
Each entry section starts with the original BibTeX key. Keys are preserved as
35+
written, including underscores and punctuation.
36+
37+
Each entry then has a general-flags table for entry-level review signals:
38+
39+
- whether source metadata was found;
40+
- whether providers returned errors;
41+
- whether required fields are present;
42+
- whether any field comparisons were possible;
43+
- whether PDF candidates were discovered;
44+
- the confidence score.
45+
46+
Field-level details are shown in a separate comparison table. The `Flag` column
47+
summarizes each field as green, amber, red, or ignored. This avoids repeating a
48+
full checklist while keeping the signal close to the field value being reviewed.
49+
50+
## Required And Supplementary Fields
51+
52+
Required fields depend on the BibTeX entry type. For example:
53+
54+
- `@article` expects `author`, `title`, `journal`, and `year`;
55+
- `@inproceedings` expects `author`, `title`, `booktitle`, and `year`;
56+
- `@book` accepts either `author` or `editor`, plus `title`, `publisher`, and
57+
`year`;
58+
- `@inbook` and `@incollection` accept either `author` or `editor` and compare
59+
their container title as `booktitle`.
60+
61+
Supplementary fields such as `doi`, `url`, `pages`, `volume`, `number`, `isbn`,
62+
and `edition` still appear in comparisons when present or when source metadata
63+
reports them, but their absence is usually marked amber rather than red.
64+
65+
Common bibliography-manager fields such as `abstract`, `keywords`, `file`,
66+
`timestamp`, and similar local metadata are treated as ignored for reference-list
67+
validation.
68+
69+
## Normalization
70+
71+
Comparison is intentionally tolerant but explicit. Titles are normalized before
72+
comparison and before title-based search: braces, TeX-style quotes, common LaTeX
73+
formatting commands, accents, punctuation, case, and whitespace are normalized.
74+
75+
Author and editor lists use the same name-normalization logic. Full names can
76+
match initials, accents are normalized, and `et al.` is treated as a review flag
77+
rather than an automatic conflict. Reordered creator lists are marked ambiguous
78+
because author order is often meaningful.
79+
80+
DOIs are stricter. Bare DOI strings, `doi:` prefixes, `doi.org` URLs, and
81+
`dx.doi.org` URLs are canonicalized to the same DOI, but a different DOI remains
82+
a conflict.
83+
84+
URLs are compared after canonicalizing hosts and DOI resolver links. URLs found
85+
inside `note` or `howpublished`, including LaTeX `\url{...}` macros, can be used
86+
as a fallback for a missing `url` field.
87+
88+
## Fetch Manifests
89+
90+
`fetch` mode first performs the same check workflow and then attempts only
91+
explicit PDF candidate URLs from source metadata:
92+
93+
```bash
94+
julia --project=. -e 'using PaperFetch; PaperFetch.main()' -- \
95+
fetch references.bib \
96+
--email your.email@example.edu \
97+
--use-apis \
98+
--cache-dir .paperfetch_cache \
99+
--outdir paperfetch_out
100+
```
101+
102+
Fetch mode writes:
103+
104+
- the normal Markdown and INC check reports;
105+
- `manifest.md`, a human-readable table of fetch outcomes;
106+
- `manifest.inc`, a spreadsheet/tooling manifest;
107+
- downloaded PDF files when a candidate URL succeeds.
108+
109+
The manifest records the BibTeX key, a compact reference title, fetch status,
110+
local file path, source URL, and a short diagnostic. Entries with no PDF
111+
candidate are recorded as `skipped`; this is normal for websites, datasets,
112+
books, landing-page-only records, and many paywalled articles.
113+
114+
Failed PDF candidates include diagnostics such as HTTP status, non-PDF content
115+
types, likely landing/login/paywall pages, or missing remote files.

0 commit comments

Comments
 (0)