"""Pure helpers shared by the four record overview pages: project, dataset, sample and measurement.
What the pages work out from a record, including who is credited on it, lives on
``fairdm.core.plugins.RecordOverviewPlugin``. What stays here has no shared subject: how a name is
written in a citation, how a partial date becomes a date, and how data is serialised into a script
element.
"""
import json
from datetime import date
from django.urls import NoReverseMatch, reverse
from django.utils.formats import date_format
from partial_date import PartialDate
[docs]
def safe_reverse(name, **kwargs):
"""Reverse a URL name, giving ``None`` where Django would raise.
Args:
name: The URL name.
**kwargs: The URL's keyword arguments.
Returns:
The URL, or ``None`` when no route matches.
"""
try:
return reverse(name, kwargs=kwargs or None)
except NoReverseMatch:
return None
[docs]
def as_date(partial):
"""Turn a partial date into a plain date, for comparing and sorting.
A year or a month is padded out to the first day, so never show the result of this call to a
reader; use :func:`format_partial_date` for that.
Args:
partial: A ``PartialDate``, a date, or ``None``.
Returns:
The date, or ``None`` when there is nothing to convert.
"""
if partial is None:
return None
value = getattr(partial, "date", partial)
return value if isinstance(value, date) else None
[docs]
def author_name(contributor):
"""Write a contributor's name as a citation lists it: "Keller, A.".
Args:
contributor: A person or organisation.
Returns:
The last name and first initial, or the contributor's own string form when it has no
first and last name.
"""
last = getattr(contributor, "last_name", "")
first = getattr(contributor, "first_name", "")
if last and first:
return f"{last}, {first[0]}."
return str(contributor)
[docs]
def sentence_case(text):
"""Capitalise the first letter only, so "XRF measurements" keeps its acronym.
Args:
text: The text to capitalise.
Returns:
The text with its first letter in upper case and the rest untouched.
"""
text = str(text)
return text[:1].upper() + text[1:]
[docs]
def json_ld(data):
"""Serialise ``data`` for an inline ``<script>``.
The characters that could end the element are escaped, so user-written names and descriptions
can never break out of it.
Args:
data: Anything ``json.dumps`` accepts; other values are written as their string form.
Returns:
The JSON text, safe to place inside a script element.
"""
return (
json.dumps(data, default=str)
.replace("<", "\\u003c")
.replace(">", "\\u003e")
.replace("&", "\\u0026")
)