from datetime import datetime from xml.etree.ElementTree import Element, SubElement, tostring from app.db.models import Publication, Researcher ATOM_NS = "http://www.w3.org/2005/Atom" DC_NS = "http://purl.org/dc/elements/1.1/" EXTRA_NS = "http://example.org/orcid-extra" # namespace para campos extendidos class SWORDGenerator: @staticmethod def generate_feed_xml(researcher: Researcher, publications: list[Publication]) -> bytes: feed = Element("feed", { "xmlns": ATOM_NS, "xmlns:dc": DC_NS, "xmlns:extra": EXTRA_NS }) SubElement(feed, "title").text = f"Publications for {researcher.orcid_id}" author = SubElement(feed, "author") SubElement(author, "name").text = researcher.name or "Unknown" SubElement(feed, "updated").text = datetime.utcnow().isoformat() + "Z" SubElement(feed, "id").text = f"urn:uuid:{researcher.id}" for pub in publications: entry = SubElement(feed, "entry") SubElement(entry, "id").text = f"urn:uuid:{pub.id}" SubElement(entry, "updated").text = datetime.utcnow().isoformat() + "Z" # Title SubElement(entry, f"{{{DC_NS}}}title").text = pub.title or "Untitled" # Subtitle if pub.subtitle: SubElement(entry, f"{{{EXTRA_NS}}}subtitle").text = pub.subtitle # DOI if pub.doi: SubElement(entry, f"{{{DC_NS}}}identifier").text = f"doi:{pub.doi}" # Journal if pub.journal: SubElement(entry, f"{{{DC_NS}}}source").text = pub.journal # URL if pub.url: SubElement(entry, f"{{{DC_NS}}}relation").text = pub.url # Short description if pub.short_description: SubElement(entry, f"{{{DC_NS}}}description").text = pub.short_description # Citation if pub.citation_value: cit = SubElement(entry, f"{{{EXTRA_NS}}}citation") SubElement(cit, "type").text = pub.citation_type or "unknown" SubElement(cit, "value").text = pub.citation_value # Language if pub.language_code: SubElement(entry, f"{{{DC_NS}}}language").text = pub.language_code # Country if pub.country: SubElement(entry, f"{{{EXTRA_NS}}}country").text = pub.country # External IDs if pub.external_ids: ext_ids_el = SubElement(entry, f"{{{EXTRA_NS}}}external_ids") for ext in pub.external_ids: ext_el = SubElement(ext_ids_el, "external_id") for k, v in ext.items(): if isinstance(v, dict) and "value" in v: SubElement(ext_el, k).text = v["value"] else: SubElement(ext_el, k).text = str(v) # Contributors if pub.contributors: contribs_el = SubElement(entry, f"{{{EXTRA_NS}}}contributors") for c in pub.contributors: c_el = SubElement(contribs_el, "contributor") SubElement(c_el, "name").text = c.get("name") SubElement(c_el, "orcid").text = c.get("orcid") SubElement(c_el, "role").text = c.get("role") # Date if pub.pub_year: date_str = str(pub.pub_year) if pub.pub_month: date_str += f"-{pub.pub_month:02d}" if pub.pub_day: date_str += f"-{pub.pub_day:02d}" SubElement(entry, f"{{{DC_NS}}}date").text = date_str # Type if pub.type: SubElement(entry, f"{{{DC_NS}}}type").text = pub.type # Status (new / updated / unchanged) if hasattr(pub, "status") and pub.status: SubElement(entry, f"{{{EXTRA_NS}}}status").text = pub.status # Last modified if pub.last_modified: SubElement(entry, f"{{{EXTRA_NS}}}last_modified").text = pub.last_modified.isoformat() return tostring(feed, encoding="utf-8", xml_declaration=True)