linkml-reference-validator 0.3.0rc3__tar.gz → 0.4.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (255) hide show
  1. linkml_reference_validator-0.3.0rc3/README.md → linkml_reference_validator-0.4.0/PKG-INFO +43 -0
  2. linkml_reference_validator-0.3.0rc3/PKG-INFO → linkml_reference_validator-0.4.0/README.md +22 -21
  3. linkml_reference_validator-0.4.0/benchmarks/repeated_quotes.py +49 -0
  4. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/docs/concepts/content-types.md +2 -0
  5. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/docs/how-to/fetch-full-text-and-pdfs.md +107 -3
  6. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/docs/how-to/repair-validation-errors.md +31 -0
  7. linkml_reference_validator-0.4.0/docs/how-to/use-local-files-and-urls.md +279 -0
  8. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/docs/how-to/validate-urls.md +73 -17
  9. linkml_reference_validator-0.4.0/docs/reference/cache-format.md +150 -0
  10. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/docs/reference/cli.md +35 -0
  11. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/docs/troubleshooting.md +72 -2
  12. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/mkdocs.yml +1 -0
  13. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/pyproject.toml +1 -1
  14. linkml_reference_validator-0.4.0/skills/linkml-reference-validator/SKILL.md +67 -0
  15. linkml_reference_validator-0.4.0/skills/linkml-reference-validator/references/configuration.md +99 -0
  16. linkml_reference_validator-0.4.0/skills/linkml-reference-validator/references/guardrails.md +102 -0
  17. linkml_reference_validator-0.4.0/src/linkml_reference_validator/__init__.py +23 -0
  18. linkml_reference_validator-0.4.0/src/linkml_reference_validator/cache.py +295 -0
  19. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/src/linkml_reference_validator/cli/cache.py +59 -1
  20. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/src/linkml_reference_validator/cli/lookup.py +1 -1
  21. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/src/linkml_reference_validator/etl/acquire.py +79 -0
  22. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/src/linkml_reference_validator/etl/extract/__init__.py +7 -2
  23. linkml_reference_validator-0.4.0/src/linkml_reference_validator/etl/extract/drawio.py +341 -0
  24. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/src/linkml_reference_validator/etl/extract/html.py +225 -7
  25. linkml_reference_validator-0.4.0/src/linkml_reference_validator/etl/extract/pdf.py +169 -0
  26. linkml_reference_validator-0.4.0/src/linkml_reference_validator/etl/extract/svg.py +1382 -0
  27. linkml_reference_validator-0.4.0/src/linkml_reference_validator/etl/extract/svg_geometry.py +407 -0
  28. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/src/linkml_reference_validator/etl/extract/xml.py +3 -14
  29. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/src/linkml_reference_validator/etl/fulltext/__init__.py +11 -5
  30. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/src/linkml_reference_validator/etl/fulltext/base.py +20 -8
  31. linkml_reference_validator-0.4.0/src/linkml_reference_validator/etl/fulltext/bioc.py +161 -0
  32. linkml_reference_validator-0.4.0/src/linkml_reference_validator/etl/fulltext/bookshelf.py +242 -0
  33. linkml_reference_validator-0.4.0/src/linkml_reference_validator/etl/fulltext/epmc.py +197 -0
  34. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/src/linkml_reference_validator/etl/fulltext/pmc.py +2 -21
  35. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/src/linkml_reference_validator/etl/identifiers.py +13 -1
  36. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/src/linkml_reference_validator/etl/reference_fetcher.py +334 -143
  37. linkml_reference_validator-0.4.0/src/linkml_reference_validator/etl/rules.py +260 -0
  38. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/src/linkml_reference_validator/etl/sources/file.py +43 -3
  39. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/src/linkml_reference_validator/etl/sources/pmid.py +45 -0
  40. linkml_reference_validator-0.4.0/src/linkml_reference_validator/etl/sources/url.py +303 -0
  41. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/src/linkml_reference_validator/matching.py +11 -0
  42. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/src/linkml_reference_validator/models.py +24 -4
  43. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/src/linkml_reference_validator/validation/fuzzy_text_utils.py +180 -2
  44. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/src/linkml_reference_validator/validation/repairer.py +12 -5
  45. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/src/linkml_reference_validator/validation/supporting_text_validator.py +63 -9
  46. linkml_reference_validator-0.4.0/tests/fixtures/bookshelf/esummary_20301710.xml +48 -0
  47. linkml_reference_validator-0.4.0/tests/fixtures/bookshelf/gene_NBK1116/men1.nxml +347 -0
  48. linkml_reference_validator-0.4.0/tests/fixtures/bookshelf/pubmed_20301710.xml +4 -0
  49. linkml_reference_validator-0.4.0/tests/test_bookshelf_provider.py +457 -0
  50. linkml_reference_validator-0.4.0/tests/test_bot_challenge.py +227 -0
  51. linkml_reference_validator-0.4.0/tests/test_cache_validation.py +512 -0
  52. linkml_reference_validator-0.4.0/tests/test_drawio.py +327 -0
  53. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/test_extractor_fidelity.py +11 -20
  54. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/test_extractors.py +64 -0
  55. linkml_reference_validator-0.4.0/tests/test_fulltext_providers.py +903 -0
  56. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/test_jats_tables.py +3 -1
  57. linkml_reference_validator-0.4.0/tests/test_meaning_changes.py +137 -0
  58. linkml_reference_validator-0.4.0/tests/test_normalized_content_cache.py +71 -0
  59. linkml_reference_validator-0.4.0/tests/test_pdf_url_title.py +228 -0
  60. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/test_reference_fetcher.py +55 -1
  61. linkml_reference_validator-0.4.0/tests/test_rules.py +130 -0
  62. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/test_sources.py +27 -0
  63. linkml_reference_validator-0.4.0/tests/test_svg_edges.py +596 -0
  64. linkml_reference_validator-0.4.0/tests/test_svg_extractor.py +1047 -0
  65. linkml_reference_validator-0.4.0/tests/test_url_fragment_cache.py +257 -0
  66. linkml_reference_validator-0.4.0/tests/test_url_html_sanitize.py +160 -0
  67. linkml_reference_validator-0.4.0/tests/test_url_html_to_text.py +226 -0
  68. linkml_reference_validator-0.4.0/tests/test_url_source_cache_version.py +147 -0
  69. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/test_validation_config.py +3 -1
  70. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/uv.lock +1 -1
  71. linkml_reference_validator-0.3.0rc3/docs/how-to/use-local-files-and-urls.md +0 -150
  72. linkml_reference_validator-0.3.0rc3/src/linkml_reference_validator/__init__.py +0 -5
  73. linkml_reference_validator-0.3.0rc3/src/linkml_reference_validator/etl/extract/pdf.py +0 -74
  74. linkml_reference_validator-0.3.0rc3/src/linkml_reference_validator/etl/sources/url.py +0 -148
  75. linkml_reference_validator-0.3.0rc3/tests/test_fulltext_providers.py +0 -356
  76. linkml_reference_validator-0.3.0rc3/tests/test_pmc_modern_container.py +0 -75
  77. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/.claude/.mcp.json +0 -0
  78. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/.claude/settings.json +0 -0
  79. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/.copier-answers.yml +0 -0
  80. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/.devcontainer/devcontainer.json +0 -0
  81. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/.devcontainer/post_create.sh +0 -0
  82. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/.editorconfig +0 -0
  83. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/.github/actions/claude-code-action/action.yml +0 -0
  84. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/.github/actions/claude-issue-summarize-action/action.yml +0 -0
  85. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/.github/actions/claude-issue-triage-action/action.yml +0 -0
  86. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/.github/ai-controllers.json +0 -0
  87. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/.github/copilot-instructions.md +0 -0
  88. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/.github/copilot-setup-steps.yml +0 -0
  89. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/.github/dependabot.yml +0 -0
  90. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/.github/workflows/claude-code-review.yml +0 -0
  91. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/.github/workflows/claude-issue-summarize.yml +0 -0
  92. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/.github/workflows/claude-issue-triage.yml +0 -0
  93. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/.github/workflows/claude-ontology-review.yml +0 -0
  94. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/.github/workflows/claude.yml +0 -0
  95. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/.github/workflows/deploy-docs.yaml +0 -0
  96. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/.github/workflows/dragon-ai.yml +0 -0
  97. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/.github/workflows/main.yaml +0 -0
  98. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/.github/workflows/pypi-publish.yaml +0 -0
  99. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/.gitignore +0 -0
  100. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/.goosehints +0 -0
  101. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/.pre-commit-config.yaml +0 -0
  102. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/.yamllint.yaml +0 -0
  103. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/AGENTS.md +0 -0
  104. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/BUG_GEO_ACCESSION_TO_UID.md +0 -0
  105. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/BUG_ZENODO_DOI_AND_URL_HANDLING.md +0 -0
  106. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/CLAUDE.md +0 -0
  107. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/CLI-ISSUES.md +0 -0
  108. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/CODE_OF_CONDUCT.md +0 -0
  109. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/CONTRIBUTING.md +0 -0
  110. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/FEATURE_REQUEST_REPAIR.md +0 -0
  111. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/LICENSE +0 -0
  112. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/REVIEW2.md +0 -0
  113. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/REVIEW3.md +0 -0
  114. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/ai.just +0 -0
  115. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/copilot-instructions.md +0 -0
  116. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/docs/about.md +0 -0
  117. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/docs/assets/zotero-find-full-text.png +0 -0
  118. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/docs/concepts/editorial-conventions.md +0 -0
  119. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/docs/concepts/how-it-works.md +0 -0
  120. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/docs/elements/.gitkeep +0 -0
  121. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/docs/how-to/add-reference-source.md +0 -0
  122. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/docs/how-to/export-cache-to-zotero.md +0 -0
  123. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/docs/how-to/skip-unsupported-references.md +0 -0
  124. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/docs/how-to/validate-clinical-trials.md +0 -0
  125. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/docs/how-to/validate-dois.md +0 -0
  126. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/docs/how-to/validate-entrez.md +0 -0
  127. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/docs/how-to/validate-obo-files.md +0 -0
  128. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/docs/how-to/validate-titles.md +0 -0
  129. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/docs/index.md +0 -0
  130. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/docs/notebooks/01_getting_started.ipynb +0 -0
  131. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/docs/notebooks/02_advanced_usage.ipynb +0 -0
  132. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/docs/notebooks/03_python_api.ipynb +0 -0
  133. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/docs/notebooks/04_obo_validation.ipynb +0 -0
  134. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/docs/notebooks/05_geo_validation.ipynb +0 -0
  135. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/docs/quickstart.md +0 -0
  136. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/docs/reference/splitting.md +0 -0
  137. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/docs/setup-guide.md +0 -0
  138. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/docs/superpowers/plans/2026-06-12-modular-pdf-fulltext-fetching.md +0 -0
  139. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/docs/superpowers/plans/2026-08-05-private-library-full-text.md +0 -0
  140. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/docs/superpowers/specs/2026-06-12-modular-pdf-fulltext-fetching-design.md +0 -0
  141. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/docs/superpowers/specs/2026-08-05-private-library-full-text-design.md +0 -0
  142. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/docs/templates-linkml/README.md +0 -0
  143. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/docs/todo.md +0 -0
  144. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/docs/tutorials/complete-workflow.md +0 -0
  145. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/examples/README.md +0 -0
  146. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/justfile +0 -0
  147. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/mypy.ini +0 -0
  148. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/notebooks/01_getting_started.ipynb +0 -0
  149. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/notebooks/02_advanced_usage.ipynb +0 -0
  150. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/notebooks/03_python_api.ipynb +0 -0
  151. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/notebooks/README.md +0 -0
  152. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/project/README.md +0 -0
  153. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/project.justfile +0 -0
  154. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/pytest.ini +0 -0
  155. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/src/linkml_reference_validator/_version.py +0 -0
  156. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/src/linkml_reference_validator/cli/__init__.py +0 -0
  157. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/src/linkml_reference_validator/cli/repair.py +0 -0
  158. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/src/linkml_reference_validator/cli/shared.py +0 -0
  159. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/src/linkml_reference_validator/cli/validate.py +0 -0
  160. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/src/linkml_reference_validator/etl/__init__.py +0 -0
  161. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/src/linkml_reference_validator/etl/csl_export.py +0 -0
  162. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/src/linkml_reference_validator/etl/extract/base.py +0 -0
  163. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/src/linkml_reference_validator/etl/fulltext/epmc_preprint.py +0 -0
  164. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/src/linkml_reference_validator/etl/fulltext/json_api.py +0 -0
  165. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/src/linkml_reference_validator/etl/fulltext/loader.py +0 -0
  166. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/src/linkml_reference_validator/etl/fulltext/openalex.py +0 -0
  167. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/src/linkml_reference_validator/etl/fulltext/unpaywall.py +0 -0
  168. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/src/linkml_reference_validator/etl/fulltext/zotero.py +0 -0
  169. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/src/linkml_reference_validator/etl/sources/__init__.py +0 -0
  170. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/src/linkml_reference_validator/etl/sources/base.py +0 -0
  171. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/src/linkml_reference_validator/etl/sources/clinicaltrials.py +0 -0
  172. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/src/linkml_reference_validator/etl/sources/doi.py +0 -0
  173. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/src/linkml_reference_validator/etl/sources/entrez.py +0 -0
  174. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/src/linkml_reference_validator/etl/sources/json_api.py +0 -0
  175. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/src/linkml_reference_validator/etl/sources/loader.py +0 -0
  176. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/src/linkml_reference_validator/etl/sources/ppr.py +0 -0
  177. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/src/linkml_reference_validator/etl/sources/utils.py +0 -0
  178. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/src/linkml_reference_validator/etl/text_extractor.py +0 -0
  179. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/src/linkml_reference_validator/field_detection.py +0 -0
  180. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/src/linkml_reference_validator/plugins/REVIEW.md +0 -0
  181. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/src/linkml_reference_validator/plugins/__init__.py +0 -0
  182. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/src/linkml_reference_validator/plugins/reference_validation_plugin.py +0 -0
  183. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/src/linkml_reference_validator/validation/__init__.py +0 -0
  184. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/__init__.py +0 -0
  185. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/conftest.py +0 -0
  186. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/data/test_data_deep_nesting_invalid.yaml +0 -0
  187. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/data/test_data_deep_nesting_valid.yaml +0 -0
  188. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/data/test_data_invalid.yaml +0 -0
  189. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/data/test_data_valid.yaml +0 -0
  190. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/data/test_schema.yaml +0 -0
  191. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/data/test_schema_deep_nesting.yaml +0 -0
  192. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/fixtures/GEO_GSE12345.md +0 -0
  193. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/fixtures/PMC_TEST001.md +0 -0
  194. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/fixtures/PMC_TEST001.txt +0 -0
  195. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/fixtures/PMC_TEST002.md +0 -0
  196. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/fixtures/PMC_TEST002.txt +0 -0
  197. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/fixtures/PMID_TEST001.md +0 -0
  198. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/fixtures/PMID_TEST001.txt +0 -0
  199. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/fixtures/PMID_TEST002.md +0 -0
  200. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/fixtures/PMID_TEST002.txt +0 -0
  201. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/fixtures/PMID_TEST003.md +0 -0
  202. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/fixtures/PMID_TEST003.txt +0 -0
  203. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/fixtures/clinicaltrials_NCT00000001.md +0 -0
  204. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/fixtures/fulltext_html/README.md +0 -0
  205. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/fixtures/fulltext_html/dspace-32894695.html +0 -0
  206. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/fixtures/fulltext_html/iris-19049553.html +0 -0
  207. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/fixtures/fulltext_html/plos-0000308.html +0 -0
  208. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/fixtures/jats/PMC5593426.xml +0 -0
  209. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/fixtures/jats/README.md +0 -0
  210. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/fixtures/sample.obo +0 -0
  211. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/test_absent_abstract_recovery.py +0 -0
  212. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/test_acquire.py +0 -0
  213. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/test_cache_author_recovery.py +0 -0
  214. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/test_cli.py +0 -0
  215. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/test_cli_cache_enrich.py +0 -0
  216. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/test_cli_cache_export.py +0 -0
  217. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/test_cli_cache_reference.py +0 -0
  218. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/test_cli_lookup.py +0 -0
  219. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/test_cli_repair.py +0 -0
  220. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/test_clinicaltrials_cache.py +0 -0
  221. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/test_clinicaltrials_integration.py +0 -0
  222. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/test_deep_nesting.py +0 -0
  223. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/test_doi_preprint.py +0 -0
  224. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/test_e2e_integration.py +0 -0
  225. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/test_empty_snippets.py +0 -0
  226. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/test_epmc_preprint_provider.py +0 -0
  227. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/test_field_detection.py +0 -0
  228. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/test_frontmatter_and_doi_case.py +0 -0
  229. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/test_fulltext_loader.py +0 -0
  230. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/test_fuzzy_suggestions.py +0 -0
  231. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/test_geo_integration.py +0 -0
  232. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/test_html_fulltext_acceptance.py +0 -0
  233. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/test_identifiers.py +0 -0
  234. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/test_json_api_source.py +0 -0
  235. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/test_ligature_normalization.py +0 -0
  236. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/test_models.py +0 -0
  237. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/test_no_cache_downgrade.py +0 -0
  238. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/test_open_access_policy.py +0 -0
  239. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/test_optional_linkml_dependency.py +0 -0
  240. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/test_plugin_integration.py +0 -0
  241. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/test_pmc_fulltext.py +0 -0
  242. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/test_pmid_network.py +0 -0
  243. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/test_ppr_source.py +0 -0
  244. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/test_real_validation.py +0 -0
  245. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/test_repair.py +0 -0
  246. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/test_serve_stale_html_optin.py +0 -0
  247. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/test_source_utils.py +0 -0
  248. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/test_split_supporting_text.py +0 -0
  249. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/test_supporting_text_validator.py +0 -0
  250. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/test_text_extractor.py +0 -0
  251. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/test_title_validation.py +0 -0
  252. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/test_trust_cached_entries.py +0 -0
  253. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/test_unknown_slot_traversal.py +0 -0
  254. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/test_validation_counts.py +0 -0
  255. {linkml_reference_validator-0.3.0rc3 → linkml_reference_validator-0.4.0}/tests/test_zotero_provider.py +0 -0
@@ -1,3 +1,24 @@
1
+ Metadata-Version: 2.5
2
+ Name: linkml_reference_validator
3
+ Version: 0.4.0
4
+ Summary: Validation of supporting text from references and publications
5
+ Author-email: Chris Mungall <cjmungall@lbl.gov>
6
+ License-Expression: Apache-2.0
7
+ License-File: LICENSE
8
+ Requires-Python: <4.0,>=3.10
9
+ Requires-Dist: beautifulsoup4>=4.9.0
10
+ Requires-Dist: biopython>=1.80
11
+ Requires-Dist: jsonpath-ng>=1.6.0
12
+ Requires-Dist: linkml-runtime>=1.9.4
13
+ Requires-Dist: lxml>=5.0.0
14
+ Requires-Dist: pydantic>=2.0.0
15
+ Requires-Dist: pypdf>=4.0.0
16
+ Requires-Dist: rapidfuzz>=3.14.3
17
+ Requires-Dist: requests>=2.28.0
18
+ Requires-Dist: ruamel-yaml>=0.18.0
19
+ Requires-Dist: typer>=0.9.0
20
+ Description-Content-Type: text/markdown
21
+
1
22
  # linkml-reference-validator
2
23
 
3
24
  [![Tests](https://img.shields.io/badge/tests-91%20passing-brightgreen)]()
@@ -37,6 +58,28 @@ linkml-validate -s schema.yaml data.yaml \
37
58
 
38
59
  ---
39
60
 
61
+ ## Agent skill
62
+
63
+ Install the [linkml-reference-validator workflow skill](skills/linkml-reference-validator/SKILL.md) with the
64
+ [skills CLI](https://skills.sh/) (requires Node.js). Preview available skills first:
65
+
66
+ ```bash
67
+ npx skills add linkml/linkml-reference-validator --list
68
+ npx skills add linkml/linkml-reference-validator --skill linkml-reference-validator
69
+ ```
70
+
71
+ Installation defaults to the current project. Use `-a codex` or
72
+ `-a claude-code` to select an agent; add `-g` for your user-wide skills directory:
73
+
74
+ ```bash
75
+ npx skills add linkml/linkml-reference-validator --skill linkml-reference-validator -a codex -g
76
+ ```
77
+
78
+ The skill helps agents configure reference QC, interpret failures and coverage,
79
+ and set up repository hooks and CI using bundled reference guides. Install the
80
+ runtime separately as described above; installing the skill does not activate
81
+ hooks or configure CI, credentials, or data sources.
82
+
40
83
  ## Why Use This Tool?
41
84
 
42
85
  Scientific data often includes claims supported by quotes from publications. But how do you know the quotes are accurate?
@@ -1,24 +1,3 @@
1
- Metadata-Version: 2.5
2
- Name: linkml_reference_validator
3
- Version: 0.3.0rc3
4
- Summary: Validation of supporting text from references and publications
5
- Author-email: Chris Mungall <cjmungall@lbl.gov>
6
- License-Expression: Apache-2.0
7
- License-File: LICENSE
8
- Requires-Python: <4.0,>=3.10
9
- Requires-Dist: beautifulsoup4>=4.9.0
10
- Requires-Dist: biopython>=1.80
11
- Requires-Dist: jsonpath-ng>=1.6.0
12
- Requires-Dist: linkml-runtime>=1.9.4
13
- Requires-Dist: lxml>=4.9.0
14
- Requires-Dist: pydantic>=2.0.0
15
- Requires-Dist: pypdf>=4.0.0
16
- Requires-Dist: rapidfuzz>=3.14.3
17
- Requires-Dist: requests>=2.28.0
18
- Requires-Dist: ruamel-yaml>=0.18.0
19
- Requires-Dist: typer>=0.9.0
20
- Description-Content-Type: text/markdown
21
-
22
1
  # linkml-reference-validator
23
2
 
24
3
  [![Tests](https://img.shields.io/badge/tests-91%20passing-brightgreen)]()
@@ -58,6 +37,28 @@ linkml-validate -s schema.yaml data.yaml \
58
37
 
59
38
  ---
60
39
 
40
+ ## Agent skill
41
+
42
+ Install the [linkml-reference-validator workflow skill](skills/linkml-reference-validator/SKILL.md) with the
43
+ [skills CLI](https://skills.sh/) (requires Node.js). Preview available skills first:
44
+
45
+ ```bash
46
+ npx skills add linkml/linkml-reference-validator --list
47
+ npx skills add linkml/linkml-reference-validator --skill linkml-reference-validator
48
+ ```
49
+
50
+ Installation defaults to the current project. Use `-a codex` or
51
+ `-a claude-code` to select an agent; add `-g` for your user-wide skills directory:
52
+
53
+ ```bash
54
+ npx skills add linkml/linkml-reference-validator --skill linkml-reference-validator -a codex -g
55
+ ```
56
+
57
+ The skill helps agents configure reference QC, interpret failures and coverage,
58
+ and set up repository hooks and CI using bundled reference guides. Install the
59
+ runtime separately as described above; installing the skill does not activate
60
+ hooks or configure CI, credentials, or data sources.
61
+
61
62
  ## Why Use This Tool?
62
63
 
63
64
  Scientific data often includes claims supported by quotes from publications. But how do you know the quotes are accurate?
@@ -0,0 +1,49 @@
1
+ """Time repeated matching against a local UTF-8 document, without network access.
2
+
3
+ Run the same script and input with each checkout's package on PYTHONPATH:
4
+ uv run python benchmarks/repeated_quotes.py /path/to/cached/publication.md
5
+
6
+ Only successful matching is timed; this is a CPU microbenchmark, not end-to-end
7
+ CLI validation. It includes each validator's first normalization of the body.
8
+ """
9
+
10
+ import argparse
11
+ import json
12
+ import re
13
+ from pathlib import Path
14
+ from statistics import median
15
+ from time import perf_counter
16
+
17
+ from linkml_reference_validator.models import ReferenceContent, ReferenceValidationConfig
18
+ from linkml_reference_validator.validation.supporting_text_validator import SupportingTextValidator
19
+
20
+
21
+ def main() -> None:
22
+ """Report median time to match evenly spaced literal excerpts from a document."""
23
+ parser = argparse.ArgumentParser(description=__doc__)
24
+ parser.add_argument("document", type=Path)
25
+ parser.add_argument("--quotes", type=int, default=100)
26
+ parser.add_argument("--rounds", type=int, default=3)
27
+ args = parser.parse_args()
28
+ content = args.document.read_text(encoding="utf-8")
29
+ # Each query is taken verbatim from the source; avoid editorial-bracket and
30
+ # ellipsis syntax, which would change what the matching API searches for.
31
+ candidates = [m.group() for m in re.finditer(r"[\w ,;:-]{30,100}", content)]
32
+ if args.quotes < 1 or args.rounds < 1 or not candidates:
33
+ parser.error("positive --quotes/--rounds and a document with prose are required")
34
+ quotes = [candidates[i * len(candidates) // args.quotes] for i in range(args.quotes)]
35
+ reference = ReferenceContent(reference_id="TEST:benchmark", content=content)
36
+ elapsed = []
37
+ for _ in range(args.rounds):
38
+ validator = SupportingTextValidator(ReferenceValidationConfig())
39
+ start = perf_counter()
40
+ for quote in quotes:
41
+ if not validator.find_text_in_reference(quote, reference).found:
42
+ raise SystemExit(f"Excerpt unexpectedly failed validation: {quote!r}")
43
+ elapsed.append(perf_counter() - start)
44
+ print(json.dumps({"characters": len(content), "quotes": len(quotes),
45
+ "rounds_seconds": elapsed, "median_seconds": median(elapsed)}))
46
+
47
+
48
+ if __name__ == "__main__":
49
+ main()
@@ -11,6 +11,8 @@ When fetching references, the validator tracks what type of content was retrieve
11
11
  | `abstract_only` | PMID, DOI | Only the abstract was available |
12
12
  | `summary` | GEO, BioProject, BioSample, ClinicalTrials | Database summary/description field |
13
13
  | `local_file` | file: | Content read from local filesystem |
14
+ | `local_svg` | file: (`.svg`) | Text an SVG figure renders, a blank line between labels; a quote must fall within one label |
15
+ | `local_drawio` | file: (`.drawio`) | Labels a drawio diagram draws, read like `local_svg` |
14
16
  | `url` | url: | Content fetched from web URL |
15
17
  | `unavailable` | Any | No content could be retrieved |
16
18
 
@@ -21,17 +21,34 @@ The validator tries providers in order until one returns usable full text.
21
21
  The default order is:
22
22
 
23
23
  ```
24
- pmc → epmc_preprint → unpaywall → openalex
24
+ pmc → bookshelf → epmc_preprint → unpaywall → openalex
25
25
  ```
26
26
 
27
27
  - **`pmc`** — PubMed Central open-access subset (XML/HTML full text for many
28
28
  biomedical articles).
29
+ - **`bookshelf`** — NCBI Bookshelf chapters, such as GeneReviews, read from a
30
+ local copy of NCBI's archive (see [NCBI Bookshelf](#ncbi-bookshelf-genereviews)
31
+ below). Does nothing until `bookshelf_archive_dir` is set.
29
32
  - **`epmc_preprint`** — Europe PMC preprint body text via the `fulltextRepo`
30
33
  PDF route (see [Preprints](#preprints) below).
31
34
  - **`unpaywall`** — Unpaywall open-access lookup by DOI (often a publisher or
32
35
  repository PDF).
33
36
  - **`openalex`** — OpenAlex open-access location (PDF/HTML).
34
37
 
38
+ Two more built-in providers are available but not in the default chain; add
39
+ them to `full_text_providers` to use them:
40
+
41
+ - **`bioc`** — PMC Open Access articles as BioC passages, from NCBI's BioNLP
42
+ BioC API, by PMID. The reference list and the competing-interest,
43
+ author-contribution and funding statements are left out, so a snippet quoting
44
+ a cited paper cannot validate against the paper citing it.
45
+ - **`epmc`** — Europe PMC's `fullTextXML` for open-access articles, by PMID.
46
+ This is the same JATS that `pmc` reads, served by Europe PMC rather than
47
+ NCBI, so after `pmc` it rarely finds anything new. It is useful as a second
48
+ route when NCBI is down or rate-limiting, or in place of `pmc` to avoid
49
+ depending on Entrez. Not to be confused with `epmc_preprint`, which handles
50
+ preprints only.
51
+
35
52
  Providers are skipped silently if they have nothing for a given reference, so
36
53
  the chain degrades gracefully: a miss in `pmc` simply moves on to `unpaywall`,
37
54
  and so on. If no provider yields full text, the reference keeps whatever
@@ -45,7 +62,8 @@ your config YAML or on the `ReferenceValidationConfig` object):
45
62
  | Key | Default | Description |
46
63
  |-----|---------|-------------|
47
64
  | `fetch_full_text` | `true` | Attempt to obtain full text via the provider chain when a metadata source does not already return full text. |
48
- | `full_text_providers` | `[pmc, epmc_preprint, unpaywall, openalex]` | Ordered list of provider names to try until one yields usable full text. |
65
+ | `full_text_providers` | `[pmc, bookshelf, epmc_preprint, unpaywall, openalex]` | Ordered list of provider names to try until one yields usable full text. `bioc` and `epmc` are also built in but must be added explicitly. |
66
+ | `bookshelf_archive_dir` | `null` | Directory of unpacked NCBI Bookshelf books, read by the `bookshelf` provider. |
49
67
  | `private_cache_dir` | `~/.cache/linkml-reference-validator/private` | Separate research cache for closed/user-library full text; never read by validation. |
50
68
  | `pdf_backend` | `pypdf` | Name of the PDF text-extraction backend. |
51
69
  | `download_pdfs` | `true` | If true, persist downloaded PDFs to the files cache directory. |
@@ -66,6 +84,7 @@ email: you@example.org
66
84
  fetch_full_text: true
67
85
  full_text_providers:
68
86
  - pmc
87
+ - bookshelf
69
88
  - epmc_preprint
70
89
  - unpaywall
71
90
  - openalex
@@ -73,6 +92,91 @@ pdf_backend: pypdf
73
92
  download_pdfs: true
74
93
  ```
75
94
 
95
+ ## NCBI Bookshelf (GeneReviews)
96
+
97
+ A PMID can point to a chapter on NCBI Bookshelf rather than a journal article.
98
+ Every [GeneReviews](https://www.ncbi.nlm.nih.gov/books/NBK1116/) entry is one
99
+ of these. Such a chapter has no PMC copy, so without help the reference keeps
100
+ only its PubMed summary, and a quote from the chapter body fails as
101
+ abstract-only.
102
+
103
+ The archive described below is for **caching** a chapter, not for validating
104
+ against it. Validate quotes against the cached document. Once a chapter has
105
+ been fetched, its full text is in the reference cache (`PMID_31536184.md`),
106
+ and every later validation reads that file. There's no archive and no network
107
+ call. So the large download is a one-time step on the machine that builds the
108
+ cache, not something every run or every collaborator needs. See
109
+ [Validate against the cache, not the archive](#validate-against-the-cache-not-the-archive).
110
+
111
+ Bookshelf's web pages can't be fetched by a script: NCBI answers with a
112
+ reCAPTCHA page. Instead, the `bookshelf` provider reads chapters from NCBI's
113
+ [litarch archive](https://ftp.ncbi.nlm.nih.gov/pub/litarch/), which you
114
+ download once per book:
115
+
116
+ ```bash
117
+ linkml-reference-validator cache bookshelf-download NBK1116 --archive-dir bookshelf
118
+ ```
119
+
120
+ GeneReviews (`NBK1116`) is a 640 MB download. Only the chapter XML is kept,
121
+ about 200 MB. Then point the config at the same directory:
122
+
123
+ ```yaml
124
+ bookshelf_archive_dir: bookshelf
125
+ ```
126
+
127
+ The PubMed record names the chapter, so no extra lookup is needed:
128
+ `PMID:31536184` is `gene/glutaric-a1`, read from
129
+ `bookshelf/gene_NBK1116/glutaric-a1.nxml`. The cached entry records
130
+ `full_text_provider: bookshelf`, and `full_text_url` holds the chapter's
131
+ Bookshelf page.
132
+
133
+ A chapter is used only when its own `<license>` is `open-access`. GeneReviews
134
+ allows copying for noncommercial research with credit and no modification. A
135
+ cache holds a verbatim copy, and its `full_text_url` links back to the
136
+ chapter, so the text goes into the public cache like PMC full text does.
137
+
138
+ To refresh a book, run the download again: it replaces the book's folder. Do
139
+ this to pick up new or revised chapters.
140
+
141
+ **References cached before the archive was set up** keep their abstract. The
142
+ chain recorded that it couldn't read the chapter (`full_text_declined`, or
143
+ `full_text_attempted` on a cache from an older version), and it doesn't retry
144
+ on its own. Re-fetch them:
145
+
146
+ ```bash
147
+ linkml-reference-validator cache reference PMID:31536184 --force
148
+ ```
149
+
150
+ ### Validate against the cache, not the archive
151
+
152
+ The archive is only read when a chapter is **fetched**. That happens the first
153
+ time a reference is seen, and on an explicit `--force`. Validation reads the
154
+ cached document like any other cached reference. In practice:
155
+
156
+ 1. On one machine, download the book, set `bookshelf_archive_dir`, and cache
157
+ the chapters you cite (`cache reference PMID:...`, or a validation run).
158
+ 2. Commit the reference cache: the `PMID_*.md` files, which hold the chapter
159
+ text. Don't commit the archive.
160
+ 3. Everywhere else, including CI and collaborators, validate as usual. No
161
+ archive is needed, `bookshelf_archive_dir` can stay unset, and nothing is
162
+ downloaded from NCBI.
163
+
164
+ What happens when the archive is missing:
165
+
166
+ - **A cached chapter is never lost to a refresh that can't reach it.** If a
167
+ newer version of this tool marks the entry stale and re-fetches it, the
168
+ refresh gets only the abstract. The cached chapter is then kept and still
169
+ used, with a warning, and the entry stays stale until a run with the
170
+ archive refreshes it. For a committed cache you'd rather not re-fetch at
171
+ all, set `trust_cached_entries: true`.
172
+ - **`--force` is the exception.** It replaces the entry with whatever the
173
+ fetch returns, so forcing a Bookshelf reference without the archive swaps
174
+ the chapter for its abstract (it warns as it does). Only force these
175
+ references where the archive is available.
176
+ - **A reference that was never cached** still needs the archive once.
177
+ Without it, the reference is cached with its abstract only.
178
+ `full_text_declined: bookshelf_archive_not_configured` records why.
179
+
76
180
  ## Private manuscripts in Zotero
77
181
 
78
182
  The opt-in **`zotero`** provider can find non-open manuscripts that are already
@@ -270,7 +374,7 @@ the text came from. These provenance fields are written alongside the content:
270
374
 
271
375
  | Field | Description |
272
376
  |-------|-------------|
273
- | `full_text_provider` | Name of the provider that supplied the full text (e.g. `pmc`, `epmc_preprint`, `unpaywall`, or a custom name). |
377
+ | `full_text_provider` | Name of the provider that supplied the full text (e.g. `pmc`, `bioc`, `epmc`, `epmc_preprint`, `unpaywall`, or a custom name). |
274
378
  | `oa_status` | Open-access status reported by the provider (e.g. `gold`, `green`, `bronze`). |
275
379
  | `license` | License of the open-access copy, when known. |
276
380
  | `local_pdf_path` | Path (relative to the cache directory) to the downloaded PDF, when one was persisted. |
@@ -28,6 +28,21 @@ Validation uses an explicit ligature table instead of broad Unicode NFKC
28
28
  normalization so this change does not equate scientific notation such as `H₂O`
29
29
  with `H2O`, `10⁶` with `106`, or the micro sign `µ` with Greek `μ`.
30
30
 
31
+ ### Unmapped PDF glyphs do not match
32
+
33
+ Some PDFs draw a glyph without saying which character it is, so text extraction
34
+ gets no character at all. This is common for `fi`/`fl` ligatures, and sometimes
35
+ happens for digits or minus signs. PDF extraction writes U+FFFD (`�`) in its
36
+ place and logs a warning with the count, so cached text reads `in�ammation`.
37
+ Validation does not guess the missing character: a quote containing
38
+ `inflammation` will not match that text. Check the cached file for `�` and
39
+ quote around the damaged word or supply better full text.
40
+
41
+ Matching treats `�` like punctuation, so the damaged word splits in two: the
42
+ cached text above reads as `in ammation`. A quote that starts or ends inside
43
+ the damaged word still matches, so `ammation of the gut` matches
44
+ `in�ammation of the gut`.
45
+
31
46
  ## Quick Start
32
47
 
33
48
  ### Repair a Single Quote
@@ -121,6 +136,22 @@ Suggested fix (85%): "Haemophilus influenzae type b" → "H. influenzae type b"
121
136
 
122
137
  Use with caution - verify the suggestion preserves the intended meaning.
123
138
 
139
+ A correction that would change what the quote claims is reported, but never
140
+ offered or applied as the fix:
141
+
142
+ - **It drops a word of the quote,** such as "no" or "in mice".
143
+ - **It swaps a word for one that differs only in a prefix,** such as
144
+ "unaffected" for "affected", "hyperphosphorylated" for
145
+ "hypophosphorylated", or "intracellular" for "extracellular". This holds
146
+ even when the quote also has a typo ("unaffectd").
147
+ - **It adds a negation** the quote doesn't have, such as "not", "never",
148
+ "without" or "failed to".
149
+
150
+ Words of three letters or fewer must match exactly, so "in" is never
151
+ corrected to "on". These checks err towards caution, so some real typos go
152
+ unfixed and need fixing by hand: a typo in a word's first few letters can look
153
+ like a changed prefix ("ctivation" reads as "activation" without its "a").
154
+
124
155
  ### 4. Removal Recommendation (Very Low Confidence)
125
156
 
126
157
  Flags text that appears fabricated or hallucinated:
@@ -0,0 +1,279 @@
1
+ # Using Local Files and URLs as References
2
+
3
+ In addition to PubMed IDs and DOIs, the validator supports local files and web URLs as reference sources. This is useful when your supporting text comes from internal documentation, research notes, or web pages.
4
+
5
+ ## Local File References
6
+
7
+ Use the `file:` prefix to reference local files:
8
+
9
+ ```bash
10
+ linkml-reference-validator validate text \
11
+ "JAK1 binds to the receptor complex" \
12
+ file:./research/jak-signaling-notes.md
13
+ ```
14
+
15
+ ### Supported File Types
16
+
17
+ - **Markdown** (`.md`) - Title extracted from first `# heading`
18
+ - **Plain text** (`.txt`) - Content used as-is
19
+ - **HTML** (`.html`) - Content preserved including HTML entities
20
+
21
+ ### Path Resolution
22
+
23
+ **Absolute paths** always work:
24
+
25
+ ```bash
26
+ file:/Users/me/research/notes.md
27
+ ```
28
+
29
+ **Relative paths** are resolved in order:
30
+
31
+ 1. If `reference_base_dir` is configured, paths resolve relative to it
32
+ 2. Otherwise, paths resolve relative to the current working directory
33
+
34
+ Note: cached references use the resolved absolute path in the reference ID, so
35
+ the cache file name reflects the full path.
36
+
37
+ ### Configuring a Base Directory
38
+
39
+ Set a base directory for all relative file references:
40
+
41
+ ```python
42
+ from linkml_reference_validator.models import ReferenceValidationConfig
43
+ from pathlib import Path
44
+
45
+ config = ReferenceValidationConfig(
46
+ reference_base_dir=Path("./references"),
47
+ )
48
+ ```
49
+
50
+ Then `file:notes.md` resolves to `./references/notes.md`.
51
+
52
+ ### Example: Validating Against Research Notes
53
+
54
+ Create a research file:
55
+
56
+ ```markdown
57
+ # JAK-STAT Signaling Pathway
58
+
59
+ JAK1 binds to the receptor complex and initiates downstream signaling.
60
+ This leads to STAT phosphorylation and nuclear translocation.
61
+ ```
62
+
63
+ Validate:
64
+
65
+ ```bash
66
+ linkml-reference-validator validate text \
67
+ "JAK1 binds to the receptor complex" \
68
+ file:./jak-signaling.md
69
+ ```
70
+
71
+ ## URL References
72
+
73
+ Use the `url:` prefix to reference web pages (the prefix is optional for bare
74
+ HTTP/HTTPS URLs and will be normalized to `url:` internally):
75
+
76
+ ```bash
77
+ linkml-reference-validator validate text \
78
+ "Climate change affects biodiversity" \
79
+ url:https://example.org/climate-report.html
80
+ ```
81
+
82
+ ### Caching
83
+
84
+ URLs are cached the same way as PMID and DOI references:
85
+
86
+ - First fetch downloads and caches the content
87
+ - Subsequent validations use the cached version
88
+ - Use `linkml-reference-validator cache reference url:https://... --force` to re-fetch
89
+
90
+ ### Title Extraction
91
+
92
+ For HTML pages, the title is the `citation_title` meta tag when present, then the `<title>` tag. For PDFs, the validator tries a publisher landing page's `citation_title` and then the PDF's embedded title. When neither is found, and for other content types, the URL itself is used as the title. See [Validating URLs](validate-urls.md#titles).
93
+
94
+ ### Example: Validating Against a Web Page
95
+
96
+ ```bash
97
+ # First validation fetches and caches
98
+ linkml-reference-validator validate text \
99
+ "The quick brown fox jumps over the lazy dog" \
100
+ url:https://example.com/pangram-examples.html
101
+
102
+ # Subsequent validations use cache
103
+ linkml-reference-validator validate text \
104
+ "A quick brown fox" \
105
+ url:https://example.com/pangram-examples.html
106
+ ```
107
+
108
+ ## Diagrams as References (SVG)
109
+
110
+ A `file:` reference can point at an `.svg` figure, such as a pathway diagram.
111
+ The quote is then matched against the text the figure **renders**, not its
112
+ markup:
113
+
114
+ ```bash
115
+ linkml-reference-validator validate text \
116
+ "loss of PTEN activates AKT" \
117
+ file:./figures/pathway.svg
118
+ ```
119
+
120
+ - Each `<text>` element is one label. A label split over several `<tspan>`
121
+ lines can be quoted across the break.
122
+ - A quote must fall within one label. A figure with a `PTEN` label and a
123
+ separate `activates AKT` label does not support `"PTEN activates AKT"`,
124
+ however close together they sit: neither label says it. To cite both, quote
125
+ them as separate parts, `"PTEN ... activates AKT"`.
126
+ - HTML labels inside `<foreignObject>`, which is how drawio's **Export as SVG**
127
+ writes them, are read too.
128
+ - Text the reader cannot see is never quotable. That covers `<desc>`, `<title>`,
129
+ `<metadata>`, comments, attribute values (`id`, `fill`, `class`, ...),
130
+ `<defs>`/`<symbol>` content that no `<use>` draws, and text hidden by
131
+ `display:none`, `visibility:hidden`, zero `opacity`, `fill="none"` or a zero
132
+ `font-size`, whether set on the element, inherited from a group, or set by a
133
+ `<style>` rule.
134
+ - If the extractor cannot tell what the figure renders, the reference is
135
+ `unavailable` rather than read as raw markup. That happens when a `<style>`
136
+ rule hides or shows text through a selector other than a type, class or id
137
+ selector (`g > text`, `:first-child`, `@media`), when visibility depends on
138
+ `var()` or `calc()`, when a stylesheet is imported or linked, when the SVG
139
+ contains a `<script>` or animates visibility, or when something changes the
140
+ characters drawn: CSS `content`, a string `list-style-type` or `quotes`,
141
+ `-webkit-text-security`, a `unicode-bidi` override, `<bdo>`, or a bidi
142
+ control character in the text.
143
+
144
+ **Edges.** A connector between two labelled nodes also gives an edge line,
145
+ `"<source> <edge label> <target>"`, so a relational quote such as
146
+ `"PTEN inhibits AKT"` can be checked. The connector is an element (usually a
147
+ `<g>` holding the line and its label) whose `data-source` and `data-target`
148
+ attributes name the two nodes' ids:
149
+
150
+ ```xml
151
+ <defs><marker id="arrow"><path d="M0 0 L10 5 L0 10 z"/></marker></defs>
152
+ <g id="pten"><rect x="0" y="0" width="60" height="30"/><text x="10" y="20">PTEN</text></g>
153
+ <g id="akt"><rect x="200" y="0" width="60" height="30"/><text x="210" y="20">AKT</text></g>
154
+ <g data-source="pten" data-target="akt">
155
+ <path d="M60 15 L200 15" stroke="black" marker-end="url(#arrow)"/>
156
+ <text x="110" y="10">inhibits</text>
157
+ </g>
158
+ ```
159
+
160
+ The attributes are not drawn, so they are only believed when the drawing
161
+ agrees: the connector's stroked path must join the two nodes' shapes, one end
162
+ on each (transforms applied, with a little slack for an arrowhead), and the
163
+ connector, its label and both nodes and their labels must all be visible.
164
+
165
+ Arrowheads drawn as markers give the direction:
166
+
167
+ - **One arrowhead** (a `marker-start` or `marker-end`, set on the path or
168
+ inherited) points at the node at that end, and the line reads from the other
169
+ node to it. This holds even when it's the reverse of `data-source` →
170
+ `data-target`: the drawing wins, as in drawio.
171
+ - **Arrowheads at both ends** make the line quotable both ways.
172
+ - **No arrowhead** (a plain line, or a marker that draws nothing) reads in the
173
+ attributes' order, `data-source` first. This is a known limitation: nothing
174
+ drawn gives that order, so swapping the two attributes reverses the line
175
+ for the same picture.
176
+ - **An arrowhead drawn as a separate shape** gives no edge line: which way
177
+ such a shape points can't be read, and the attributes' order could
178
+ contradict it. This is any other `<polygon>`, `<polyline>`, `<path>` or
179
+ `<line>` in the connector, wherever it sits; any `<use>` or `<image>`; and a
180
+ `<rect>`, `<circle>` or `<ellipse>` at an end of the line or on a node. A
181
+ `<rect>`, `<circle>` or `<ellipse>` in mid-line, such as a label's
182
+ background, is not taken for an arrowhead.
183
+ - **Arrowheads at a bent line's inner vertices** (`marker-mid`, or the
184
+ `marker` shorthand) give no edge line, since each may point either way.
185
+
186
+ A marker counts as an arrowhead when it names a `<marker>` in the figure that
187
+ draws something: a shape that paints, at a size above zero. When that can't be
188
+ told from the markup (a `var()` value, or a `<use>`, text or image inside the
189
+ marker), the connector gives no edge line, since neither direction is safe. The
190
+ marker's `orient` is not read: a marker drawn at a fixed angle is taken to
191
+ point along the path. A node is the shapes it draws inside the
192
+ element its id names; its text, and shapes that are hidden or paint nothing,
193
+ do not count towards its box. Geometry the extractor cannot place from
194
+ attributes gives no edge line either: CSS `transform` or other CSS geometry,
195
+ `clip-path`, `mask` or `filter`, or an animation that moves shapes. A node drawn only through `<use>`, inside a
196
+ nested `<svg>`, or sized in `%` or `em` gives no edge line.
197
+
198
+ Known limits: a label drawn as several `<text>` elements, one per line, is
199
+ read as several labels, so quote its lines with `...` between them. Text
200
+ clipped by `clip-path` or `mask`, placed outside the
201
+ `viewBox`, scaled to nothing, covered by another shape, drawn in the
202
+ background colour, or cut short by CSS `text-overflow` or `-webkit-line-clamp`
203
+ is still read. Text converted to outlines is not text and
204
+ cannot be quoted.
205
+
206
+ ## Diagrams as References (drawio)
207
+
208
+ A `file:` reference can also point at a native drawio diagram, `.drawio` or
209
+ `.drawio.xml`, compressed or not. It is read the same way as an SVG: one label
210
+ per drawn cell, quotes held to one label, and an edge line
211
+ `"<source> <edge label> <target>"` for each drawn edge between two labelled
212
+ cells. In drawio an edge's source and target are what it is drawn between, so
213
+ no geometry check is needed; its line and both cells' labels must still be
214
+ visible. The arrows give the direction, as for SVG: an edge drawn pointing
215
+ back at its source reads target first, one with arrows at both ends reads both
216
+ ways, and one with no arrow reads source first. Only markers that point count
217
+ as arrows: arrowheads (`classic`, `block`, `open`, `async` and their thin or
218
+ double forms) and the inhibition T-bar `baseDash`. An oval, diamond, dash or
219
+ ER symbol at an end is not an arrow. A marker drawio doesn't list as either
220
+ gives no edge line. Every page of the file is read.
221
+
222
+ A `.drawio.svg` file (drawio's editable SVG export) is read as an SVG: its
223
+ labels are quotable, but it carries no `data-source`/`data-target`, so it
224
+ gives no edge lines. Point at the `.drawio` source to quote its edges.
225
+
226
+ Not read: cells marked invisible, in a hidden layer, or inside a collapsed
227
+ group; labels switched off (`noLabel`) or drawn with zero `opacity`,
228
+ `textOpacity` or `fontSize`, or with no `fontColor`; layer and page names,
229
+ tooltips, styles and other attributes. HTML labels follow the same visibility
230
+ rules as SVG `<foreignObject>` labels. A label computed from placeholders
231
+ (`placeholders="1"`), or HTML that cannot be read statically, makes the
232
+ reference `unavailable`.
233
+
234
+ ## Using in Data Files
235
+
236
+ Both file and URL references work in LinkML data files:
237
+
238
+ ```yaml
239
+ # data.yaml
240
+ - id: local-evidence
241
+ supporting_text: JAK1 binds to the receptor complex
242
+ reference: file:./research/jak-notes.md
243
+
244
+ - id: web-evidence
245
+ supporting_text: Climate impacts are accelerating
246
+ reference: url:https://example.org/climate-report.html
247
+ ```
248
+
249
+ ## Reference Type Summary
250
+
251
+ | Prefix | Example | Source |
252
+ |--------|---------|--------|
253
+ | `PMID:` | `PMID:16888623` | PubMed via NCBI Entrez |
254
+ | `DOI:` | `DOI:10.1038/nature12373` | Crossref API |
255
+ | `file:` | `file:./notes.md` | Local filesystem |
256
+ | `url:` | `url:https://example.com` | Web (HTTP/HTTPS) |
257
+
258
+ ## Best Practices
259
+
260
+ ### For Local Files
261
+
262
+ - Keep reference files in a dedicated directory
263
+ - Use `reference_base_dir` for consistent path resolution
264
+ - Use markdown for structured content with clear headings
265
+
266
+ ### For URLs
267
+
268
+ - Prefer stable URLs (avoid query parameters that change)
269
+ - Be aware that web content may change (cache helps with reproducibility)
270
+ - Consider downloading important pages as local files for long-term stability
271
+
272
+ ## Limitations
273
+
274
+ - **Local files are read as text**, apart from `.svg`, `.drawio` and `.drawio.xml` figures
275
+ (see above).
276
+ An `.html` file is matched against its raw markup.
277
+ - **Local PDF files**: Not yet supported as `file:` references. PDFs fetched by `url:` are supported.
278
+ - **Authentication**: URLs requiring login are not supported
279
+ - **Dynamic content**: JavaScript-rendered pages may not work