poster2json 0.9.2__tar.gz → 0.9.4__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 (22) hide show
  1. {poster2json-0.9.2 → poster2json-0.9.4}/PKG-INFO +1 -1
  2. {poster2json-0.9.2 → poster2json-0.9.4}/poster2json/extract.py +23 -49
  3. {poster2json-0.9.2 → poster2json-0.9.4}/pyproject.toml +1 -1
  4. {poster2json-0.9.2 → poster2json-0.9.4}/LICENSE.md +0 -0
  5. {poster2json-0.9.2 → poster2json-0.9.4}/README.md +0 -0
  6. {poster2json-0.9.2 → poster2json-0.9.4}/poster2json/__init__.py +0 -0
  7. {poster2json-0.9.2 → poster2json-0.9.4}/poster2json/__main__.py +0 -0
  8. {poster2json-0.9.2 → poster2json-0.9.4}/poster2json/cli.py +0 -0
  9. {poster2json-0.9.2 → poster2json-0.9.4}/poster2json/funders.py +0 -0
  10. {poster2json-0.9.2 → poster2json-0.9.4}/poster2json/gui.py +0 -0
  11. {poster2json-0.9.2 → poster2json-0.9.4}/poster2json/identifiers.py +0 -0
  12. {poster2json-0.9.2 → poster2json-0.9.4}/poster2json/language.py +0 -0
  13. {poster2json-0.9.2 → poster2json-0.9.4}/poster2json/normalize.py +0 -0
  14. {poster2json-0.9.2 → poster2json-0.9.4}/poster2json/orcid.py +0 -0
  15. {poster2json-0.9.2 → poster2json-0.9.4}/poster2json/ror.py +0 -0
  16. {poster2json-0.9.2 → poster2json-0.9.4}/poster2json/schemas/poster_schema.json +0 -0
  17. {poster2json-0.9.2 → poster2json-0.9.4}/poster2json/standards.py +0 -0
  18. {poster2json-0.9.2 → poster2json-0.9.4}/poster2json/tests/__init__.py +0 -0
  19. {poster2json-0.9.2 → poster2json-0.9.4}/poster2json/tests/conftest.py +0 -0
  20. {poster2json-0.9.2 → poster2json-0.9.4}/poster2json/utils.py +0 -0
  21. {poster2json-0.9.2 → poster2json-0.9.4}/poster2json/validate.py +0 -0
  22. {poster2json-0.9.2 → poster2json-0.9.4}/poster2json/xy_cut.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: poster2json
3
- Version: 0.9.2
3
+ Version: 0.9.4
4
4
  Summary: Convert scientific posters (PDF/images) to structured JSON metadata using Large Language Models
5
5
  License: MIT
6
6
  License-File: LICENSE.md
@@ -1417,7 +1417,7 @@ def _generate(model, tokenizer, prompt: str, max_tokens: int) -> str:
1417
1417
  EXTRACTION_PROMPT = """Convert this scientific poster text to JSON format.
1418
1418
 
1419
1419
  CRITICAL RULES:
1420
- 1. Extract ALL required fields: creators, titles, publicationYear, subjects, descriptions, conference, version
1420
+ 1. Extract ALL required fields: creators, titles, publicationYear, subjects, descriptions
1421
1421
  2. Create SEPARATE sections for EACH distinct topic/header found in the poster
1422
1422
  3. Use the poster's OWN section headers exactly as they appear. Lines prefixed with "## " indicate detected headers from the poster layout. Standard headers (Abstract, Introduction, Methods, Results, Discussion, Conclusions, References, Acknowledgements) are common examples, but always prefer the poster's actual headers over generic ones.
1423
1423
  4. Each section must have its OWN "sectionTitle" and "sectionContent"
@@ -1435,9 +1435,7 @@ JSON SCHEMA (all top-level fields are REQUIRED):
1435
1435
  "publicationYear": null,
1436
1436
  "subjects": [{{"subject": "keyword1"}}, {{"subject": "keyword2"}}, {{"subject": "keyword3"}}],
1437
1437
  "descriptions": [{{"description": "A 3-4 sentence summary of the full poster...", "descriptionType": "Abstract"}}],
1438
- "conference": null,
1439
1438
  "researchField": null,
1440
- "version": null,
1441
1439
  "content": {{
1442
1440
  "sections": [
1443
1441
  {{"sectionTitle": "Introduction", "sectionContent": "Full verbatim text of this section from the poster..."}},
@@ -1452,14 +1450,10 @@ JSON SCHEMA (all top-level fields are REQUIRED):
1452
1450
  EXTRACTION NOTES:
1453
1451
  - publicationYear: Extract if a year is printed on the poster. If not found, set to null.
1454
1452
  - subjects: Extract 3-5 keywords from poster content
1455
- - descriptions: Write a 3-4 sentence summary of the full poster; descriptionType should be "Abstract" if the poster has an abstract or summary, otherwise choose the most appropriate type from: Abstract, Methods, SeriesInformation, TableOfContents, TechnicalInfo, Other
1456
- - titles: If the poster title is ALL CAPS, convert to proper Title Case preserving acronyms (e.g. "RESEARCH ON SARS-CoV-2" not "RESEARCH ON SARS-COV-2")
1457
- - conference: Extract from text visible on the poster (header, footer, logos). If not found, set to null.
1458
- - imageCaptions/tableCaptions: Include captions for figures/tables on the poster. If none exist, use [].
1459
- - version: Extract if printed on the poster. If not found, set to null.
1460
- - rightsList: OPTIONAL - include if license/copyright info found on poster
1453
+ - descriptions: Write a 3-4 sentence summary of the full poster.
1454
+ - titles: If the poster title is ALL CAPS, convert to proper Title Case preserving acronyms (e.g. "RESEARCH ON SARS-CoV-2" not "RESEARCH ON SARS-COV-2")- imageCaptions/tableCaptions: Include captions for figures/tables on the poster. If none exist, use [].
1461
1455
  - researchField: MUST be exactly one of: "Health Sciences" | "Life Sciences" | "Physical Sciences" | "Social Sciences" — or null if unclear.
1462
- - GROUNDING: For metadata fields (conference, publicationYear, version), only extract values that appear as text on the poster. If not found, use null. For section content, copy ALL text verbatim — do not skip or shorten.
1456
+ - GROUNDING: For metadata fields (publicationYear), only extract values that appear as text on the poster. If not found, use null. For section content, copy ALL text verbatim — do not skip or shorten.
1463
1457
 
1464
1458
  POSTER TEXT TO CONVERT:
1465
1459
  {raw_text}
@@ -1467,12 +1461,11 @@ POSTER TEXT TO CONVERT:
1467
1461
  OUTPUT VALID JSON ONLY:"""
1468
1462
 
1469
1463
  FALLBACK_PROMPT = """Convert poster text to JSON. REQUIRED FIELDS:
1470
- 1. creators, titles, publicationYear, subjects, descriptions, conference, version, content
1464
+ 1. creators, titles, publicationYear, subjects, descriptions, content
1471
1465
  2. SEPARATE section for EACH header found in the poster text. Use the poster's own headers. Lines starting with "## " are detected headers.
1472
1466
  3. Copy ALL text EXACTLY verbatim — every line of poster text must appear in a section
1473
1467
  4. If title is ALL CAPS, convert to Title Case preserving acronyms (SARS-CoV-2, not SARS-COV-2)
1474
- 5. conference: extract if visible on the poster. If not found, set to null.
1475
- 6. imageCaptions/tableCaptions: for figures/tables on the poster. If none, use [].
1468
+ 5. imageCaptions/tableCaptions: for figures/tables on the poster. If none, use [].
1476
1469
 
1477
1470
  {{
1478
1471
  "creators": [{{"name": "LastName, FirstName", "givenName": "FirstName", "familyName": "LastName", "affiliation": ["Institution"]}}],
@@ -1480,9 +1473,7 @@ FALLBACK_PROMPT = """Convert poster text to JSON. REQUIRED FIELDS:
1480
1473
  "publicationYear": null,
1481
1474
  "subjects": [{{"subject": "keyword1"}}, {{"subject": "keyword2"}}],
1482
1475
  "descriptions": [{{"description": "3-4 sentence summary of the full poster", "descriptionType": "Abstract"}}],
1483
- "conference": null,
1484
1476
  "researchField": null,
1485
- "version": null,
1486
1477
  "content": {{
1487
1478
  "sections": [{{"sectionTitle": "Header", "sectionContent": "Full verbatim text of this section..."}}]
1488
1479
  }},
@@ -2012,6 +2003,17 @@ def _postprocess_json(
2012
2003
  # Drop LLM-hallucinated formats — set deterministically from file extension
2013
2004
  result.pop("formats", None)
2014
2005
 
2006
+ # version is a fixed provenance marker, not extracted by the model.
2007
+ result["version"] = "Posters.science automated"
2008
+
2009
+ # descriptionType is auto-filled to its default; only the description text
2010
+ # (the summary) is model-generated.
2011
+ descs = result.get("descriptions")
2012
+ if isinstance(descs, list):
2013
+ for d in descs:
2014
+ if isinstance(d, dict) and d.get("description"):
2015
+ d["descriptionType"] = "Abstract"
2016
+
2015
2017
  # Ensure caption fields exist and normalize with auto-generated IDs
2016
2018
  for key, ctype in [("imageCaptions", "fig"), ("tableCaptions", "table")]:
2017
2019
  if key not in result:
@@ -2019,35 +2021,9 @@ def _postprocess_json(
2019
2021
  elif isinstance(result[key], (dict, list)):
2020
2022
  result[key] = _normalize_captions(result[key], caption_type=ctype)
2021
2023
 
2022
- # Strip placeholder/hallucinated conference values
2023
- conf = result.get("conference")
2024
- if isinstance(conf, dict):
2025
- cn = conf.get("conferenceName", "")
2026
- if isinstance(cn, str) and _is_placeholder(cn):
2027
- conf.pop("conferenceName", None)
2028
- # Ground-truth check: conference name must appear in the poster text
2029
- cn = conf.get("conferenceName", "")
2030
- if isinstance(cn, str) and cn and raw_text:
2031
- cn_lower = cn.lower().strip()
2032
- if cn_lower not in raw_text.lower():
2033
- result["conference"] = None
2034
- conf = None
2035
- if conf is not None:
2036
- cl = conf.get("conferenceLocation", "")
2037
- if isinstance(cl, str) and _is_placeholder(cl):
2038
- conf.pop("conferenceLocation", None)
2039
- cu = conf.get("conferenceUrl", "")
2040
- if isinstance(cu, str) and _is_placeholder(cu):
2041
- conf.pop("conferenceUrl", None)
2042
- # Fix 4: validate conferenceUri is a real URL
2043
- for uri_key in ("conferenceUri", "conferenceUrl"):
2044
- val = conf.get(uri_key, "")
2045
- if isinstance(val, str) and val and not val.startswith(("http://", "https://")):
2046
- conf.pop(uri_key, None)
2047
- # Collapse to null if no meaningful fields remain
2048
- meaningful = {k for k, v in conf.items() if v}
2049
- if not meaningful:
2050
- result["conference"] = None
2024
+ # Conference is not extracted by the model at all; it is supplied by the
2025
+ # repository or entered on the platform. Drop anything the model emits.
2026
+ result.pop("conference", None)
2051
2027
 
2052
2028
  # Publisher is auto-set by posters.science — strip if the LLM emitted one
2053
2029
  result.pop("publisher", None)
@@ -2249,11 +2225,9 @@ def _postprocess_json(
2249
2225
  if isinstance(title_obj, dict) and "title" in title_obj:
2250
2226
  title_obj["title"] = _clean_unicode_artifacts(title_obj.get("title", ""))
2251
2227
 
2252
- # Normalize licenses to SPDX form when confidently matched
2253
- if "rightsList" in result:
2254
- from .normalize import normalize_rights_list
2255
-
2256
- result["rightsList"] = normalize_rights_list(result["rightsList"])
2228
+ # Licenses are not extracted from the poster by the model; rights are
2229
+ # provided by the repository or platform upstream. Drop any the model emits.
2230
+ result.pop("rightsList", None)
2257
2231
 
2258
2232
  # Cleanup + dedupe subjects
2259
2233
  if "subjects" in result:
@@ -1,7 +1,7 @@
1
1
  [tool.poetry]
2
2
 
3
3
  name = "poster2json"
4
- version = "0.9.2"
4
+ version = "0.9.4"
5
5
  description = "Convert scientific posters (PDF/images) to structured JSON metadata using Large Language Models"
6
6
 
7
7
  packages = [{ include = "poster2json" }]
File without changes
File without changes