Source code for relaton.serializers.bibxml.authors
from typing import cast, List, Optional
from lxml import objectify
from lxml.etree import _Element
from ...models.bibdata import Contributor
from ...models.contacts import ContactMethod, Address
from ...models.orgs import Organization
from ...models.people import PersonAffiliation
from ...models.strings import GenericStringValue
from ...util import as_list
__all__ = (
'create_author',
'is_author',
'AUTHOR_ROLES',
)
E = objectify.E
AUTHOR_ROLES = set(('author', 'editor', 'publisher'))
"""Relaton contributor role types that represent xml2rfc authors."""
is_author = (
lambda contrib:
len(set([r.type for r in as_list(contrib.role or [])]) & AUTHOR_ROLES) > 0
)
"""Returns ``True`` if given Relaton contributor instance
represents an author in xml2rfc domain."""
[docs]def create_author(contributor: Contributor) -> _Element:
if not is_author(contributor):
raise ValueError(
"Unable to construct <author>: incompatible roles")
if not contributor.organization and not contributor.person:
raise ValueError(
"Unable to construct <author>: "
"neither an organization nor a person")
author_el = E.author()
roles: List[str] = [
r.type
for r in (contributor.role or [])
if r.type
]
if 'editor' in roles:
author_el.set('role', 'editor')
org: Optional[Organization] = None
if contributor.organization:
org = contributor.organization
elif contributor.person:
affiliations: List[PersonAffiliation] = \
as_list(contributor.person.affiliation or [])
if len(affiliations) > 0:
org = affiliations[0].organization
else:
org = None
if org is not None:
# Organization
if (
org.abbreviation is not None
and org.abbreviation.content == 'IANA'
) or (
org.name
and any(
name
for name in as_list(org.name)
if name.content == 'Internet Assigned Numbers Authority')
):
org_el = E.organization('IANA')
else:
org_el = E.organization(as_list(org.name)[0].content)
if org.abbreviation is not None:
org_el.set('abbrev', org.abbreviation.content)
author_el.append(org_el)
# Address & postal
contacts: List[ContactMethod] = as_list(org.contact or [])
postal_contacts: List[Address] = [
c.address for c in contacts
if c.address and c.address.country
]
if len(postal_contacts) > 0 or org.url:
addr = E.address()
if len(postal_contacts) > 0:
contact = postal_contacts[0]
postal = E.postal(
E.country(contact.country)
)
if contact.city:
postal.append(E.city(contact.city))
addr.append(postal)
if org.url:
addr.append(E.uri(org.url))
author_el.append(addr)
if contributor.person:
name = contributor.person.name
# Simplify initials
# from a list of formatted strings to a list of plain strings
initials: List[str] = []
if name.given:
initials = [
# We don’t expect trailing full stops in initials
# Workaround for bad source data, in effect
i.content.strip()
for i in cast(
List[GenericStringValue],
as_list(name.given.formatted_initials or []),
)
]
if name.completename:
author_el.set('fullname', name.completename.content)
else:
# Craft a complete name based on what we have
# It’s clunky and error-prone,
# but the alternative is only having a surname
# in absence of ``completename``,
# and ``completename`` is optional in Relaton.
if name.given:
forenames = ' '.join(
f.content
for f in as_list(name.given.forename or [])
)
else:
forenames = None
author_el.set('fullname', ('%s%s%s%s%s' % (
f"{name.prefix.content} " if name.prefix else '',
f"{forenames} " if forenames else '',
' '.join(initials) if len(initials) > 0 else '',
f"{name.surname.content} " if name.surname else '',
f"{name.addition.content} " if name.addition else '',
)).strip())
# Even if completename is given, these can still be provided:
if name.surname:
author_el.set('surname', name.surname.content)
if len(initials) > 0:
author_el.set('initials', ' '.join(initials))
return author_el