import logging import requests log = logging.getLogger(__name__) class UnpaywallResolver: def __init__(self, email: str, timeout=(10, 30)): self.email = (email or "").strip() self.timeout = timeout self.base_url = "https://api.unpaywall.org/v2" def resolve(self, paper): if not paper.doi: return paper if not self.email: log.warning( "Unpaywall email missing; OA lookup skipped: %s", paper.title, ) return paper doi = paper.doi.strip() try: response = requests.get( f"{self.base_url}/{doi}", params={"email": self.email}, timeout=self.timeout, ) response.raise_for_status() except requests.exceptions.HTTPError as exc: status = ( exc.response.status_code if exc.response is not None else None ) # DOI not present in Unpaywall is not fatal. if status == 404: paper.oa_status = "not_found" return paper raise data = response.json() paper.oa_status = str( data.get("oa_status") or "" ) best = data.get("best_oa_location") or {} pdf_url = best.get("url_for_pdf") or "" # Some OA records do not expose url_for_pdf but do expose a landing URL. # We intentionally do NOT treat url as a PDF here. paper.pdf_url = str(pdf_url).strip() return paper