diff --git a/aastex/_aastex.py b/aastex/_aastex.py index 9dd9add..7eb7379 100644 --- a/aastex/_aastex.py +++ b/aastex/_aastex.py @@ -80,6 +80,16 @@ class Author(pylatex.base_classes.LatexObject): organization where the work was done is usually given first. """ + altaffiliation: None | str = None + """ + A note about the author which is not an organization, rendered as a + footnote on their name. + + This is where AASTeX puts anything which needs saying about an author + besides where they work, such as ``"Hubble Fellow"``, or ``"Deceased"`` + for an author who died before the article was published. + """ + email: None | str = None """ The email address of the author. @@ -113,6 +123,18 @@ def dumps(self) -> str: options=NoEscape(self.orcid) if self.orcid is not None else None, ).dumps() + # The footnote symbol attaches to whatever precedes it, so this has to + # follow `\author` immediately. + altaffiliation = "" + if self.altaffiliation is not None: + altaffiliation = ( + pylatex.Command( + command="altaffiliation", + arguments=self.altaffiliation, + ).dumps() + + "\n" + ) + # AASTeX v7 requires an `\email` command for every author, # so emit an empty one if no email address was given. email = pylatex.Command( @@ -123,7 +145,7 @@ def dumps(self) -> str: affiliation = "\n".join(a.dumps() for a in self.affiliations) - result += f"{author}\n{email}\n{affiliation}" + result += f"{author}\n{altaffiliation}{email}\n{affiliation}" return result diff --git a/aastex/_tests/test_aastex.py b/aastex/_tests/test_aastex.py index f1ba785..038658e 100644 --- a/aastex/_tests/test_aastex.py +++ b/aastex/_tests/test_aastex.py @@ -65,6 +65,11 @@ def test_dumps(self, a: aastex.Affiliation): aastex.Affiliation("Another Fancy University"), ], ), + aastex.Author( + name="John Roe", + affiliation=aastex.Affiliation("Fancy University"), + altaffiliation="Deceased", + ), ], ) class TestAuthor: @@ -88,6 +93,20 @@ def test_affiliations(self, a: aastex.Author): for affiliation in result: assert affiliation.name in dumps + def test_altaffiliation(self, a: aastex.Author): + result = a.altaffiliation + dumps = a.dumps() + if result is not None: + assert isinstance(result, str) + + # the footnote symbol attaches to the preceding text, so this has + # to follow `\author` and nothing else + author = dumps.index(r"\author") + assert dumps.index(r"\altaffiliation") > author + assert dumps.index(result, author) < dumps.index(r"\email") + else: + assert r"\altaffiliation" not in dumps + def test_orcid(self, a: aastex.Author): result = a.orcid if result is not None: @@ -675,7 +694,12 @@ def _submittable_document() -> aastex.Document: aastex.Author( name="Jane Doe", affiliation=aastex.Affiliation("Fancy University"), - ) + ), + aastex.Author( + name="John Roe", + affiliation=aastex.Affiliation("Fancy University"), + altaffiliation="Deceased", + ), ] section = aastex.Section("A section") section.append("Some text.")