From 5b284a0157f168eb3f70a70b168c0b006a22ca5d Mon Sep 17 00:00:00 2001 From: Paul Saxe Date: Sat, 17 Oct 2026 13:42:18 -0400 Subject: [PATCH] Format books, and any other entry type, instead of 'no handler' Books were printed as "Do not have a handler for 'book'" with the raw record (Hansen & McDonald, cited by SEAMM's MD analysis). Adds an ACS-style book format and a generic one for other types, and fixes format_software, which passed self twice. Co-Authored-By: Claude Opus 5.5 --- reference_handler/reference_handler.py | 78 +++++++++++++++++-- .../tests/test_reference_handler.py | 37 +++++++++ 2 files changed, 110 insertions(+), 5 deletions(-) diff --git a/reference_handler/reference_handler.py b/reference_handler/reference_handler.py index 8b02eb7..2d32bbf 100644 --- a/reference_handler/reference_handler.py +++ b/reference_handler/reference_handler.py @@ -6,7 +6,6 @@ """ import sqlite3 -import pprint import re import bibtexparser @@ -259,10 +258,10 @@ def dump(self, outfile=None, fmt="bibtex", level=3): plain_text = self.format_inbook(parse) elif entry_type == "phdthesis": plain_text = self.format_phdthesis(parse) + elif entry_type == "book": + plain_text = self.format_book(parse) else: - plain_text = f"Do not have a handler for '{entry_type}':" - plain_text += "\n" - plain_text += pprint.pformat(parse) + plain_text = self.format_other(parse) plain_text = decode_latex(plain_text) plain_text = self.decode_math_symbols(plain_text) @@ -740,7 +739,76 @@ def format_misc(self, data): return result def format_software(self, data): - return self.format_misc(self, data) + return self.format_misc(data) + + def format_book(self, data): + """Format a book BibTex record + + ACS style: + Hansen, J.-P.; McDonald, I. R. Theory of Simple Liquids, 4th ed.; + Academic Press: Oxford, 2013. DOI: 10.1016/C2010-0-66723-X + """ + result = "" + names = data.get("author", data.get("editor")) + if names is not None: + result += "; ".join(names.split(" and ")) + if "author" not in data: + result += ", Eds" + if result[-1] != ".": + result += "." + if "title" in data: + result += " " + data["title"].rstrip(".") + if "edition" in data: + result += f", {data['edition']} ed." + result += ";" if "publisher" in data else "." + if "publisher" in data: + result += f" {data['publisher']}" + if "address" in data: + result += f": {data['address']}" + result += "," + if "year" in data: + result += f" {data['year']}." + if "doi" in data: + result += f" DOI: {data['doi']}" + elif "url" in data: + result += f" {data['url']}" + return result.strip() + + def format_other(self, data): + """Format a record of a type without its own style: the authors, title, + where it appeared, the year and the DOI or URL.""" + parts = [] + names = data.get("author", data.get("editor")) + if names is not None: + text = "; ".join(names.split(" and ")) + parts.append(text if text.endswith(".") else text + ".") + if "title" in data: + parts.append(data["title"].rstrip(".") + ".") + where = next( + ( + data[key] + for key in ( + "journal", + "booktitle", + "publisher", + "institution", + "school", + ) + if key in data + ), + None, + ) + if where is not None and "year" in data: + parts.append(f"{where}, {data['year']}.") + elif where is not None: + parts.append(f"{where}.") + elif "year" in data: + parts.append(f"{data['year']}.") + if "doi" in data: + parts.append(f"DOI: {data['doi']}") + elif "url" in data: + parts.append(data["url"]) + return " ".join(parts) def format_inbook(self, data): """Format a chapter or part of a book or series BibTex record diff --git a/reference_handler/tests/test_reference_handler.py b/reference_handler/tests/test_reference_handler.py index 09477be..6ba6231 100644 --- a/reference_handler/tests/test_reference_handler.py +++ b/reference_handler/tests/test_reference_handler.py @@ -378,3 +378,40 @@ def test_cite_return(): assert lammps_id1 == lammps_id2 assert lammps_id1 == lammps_id3 assert namd_id == 2 + + +def test_format_book_software_and_other(): + """Books, software, and types without their own style are formatted, not + reported as having no handler.""" + import bibtexparser + + from reference_handler import Reference_Handler + + book = bibtexparser.loads("""@book{HansenMcDonald, + author = {Jean-Pierre Hansen and Ian R. McDonald}, + title = {Theory of Simple Liquids}, + edition = {4th}, + publisher = {Academic Press}, + address = {Oxford}, + year = {2013}, + doi = {10.1016/C2010-0-66723-X} + }""").entries[0] + text = Reference_Handler.format_book(None, book) + assert text == ( + "Jean-Pierre Hansen; Ian R. McDonald. Theory of Simple Liquids, 4th ed.; " + "Academic Press: Oxford, 2013. DOI: 10.1016/C2010-0-66723-X" + ) + + software = {"author": "A. Person", "title": "Code", "version": "1.0"} + handler = Reference_Handler.__new__(Reference_Handler) + assert handler.format_software(software).startswith("A. Person. Code, version") + + report = { + "author": "A. Person", + "title": "A report.", + "institution": "An Institute", + "year": "2020", + } + assert Reference_Handler.format_other(None, report) == ( + "A. Person. A report. An Institute, 2020." + )