Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/pbn_client/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"""

from .auth import OAuthMixin
from .authors import normalize_author_name
from .client import PBNClient
from .identifiers import is_valid_object_id, parse_publication_id
from .mixins import (
Expand Down Expand Up @@ -53,4 +54,5 @@
"smart_content",
"is_valid_object_id",
"parse_publication_id",
"normalize_author_name",
]
40 changes: 40 additions & 0 deletions src/pbn_client/authors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
"""Normalizacja danych osobowych autora z niespójnych JSON-ów PBN."""

#: Klucze imienia w kolejności preferencji (unia wariantów spotykanych w PBN).
_FIRST_NAME_KEYS = ("firstName", "givenNames", "name")

#: Klucze nazwiska w kolejności preferencji.
_LAST_NAME_KEYS = ("familyName", "lastName")


def _first_present(data, keys):
"""Zwróć pierwszą niepustą wartość spod ``keys`` w ``data`` (albo None)."""
for key in keys:
value = data.get(key)
if value:
return value
return None


def normalize_author_name(author):
"""Sprowadź autora z PBN do ``{"lastName": ..., "firstName": ...}``.

PBN podaje dane osobowe niespójnie, zależnie od endpointu: nazwisko
jako ``lastName`` albo ``familyName``, imię jako ``firstName``,
``givenNames`` albo ``name``. Czasem zamiast słownika przychodzi
goły PBN UID (string) — wtedy nie mamy danych osobowych.

Kolejność preferencji:

- ``lastName``: ``familyName`` → ``lastName`` → ``None``
- ``firstName``: ``firstName`` → ``givenNames`` → ``name`` → ``None``

Puste stringi traktowane są jak brak wartości. Wejście inne niż dict
(None, string-UID, cokolwiek) daje oba pola ``None``.
"""
if not isinstance(author, dict):
return {"lastName": None, "firstName": None}
return {
"lastName": _first_present(author, _LAST_NAME_KEYS),
"firstName": _first_present(author, _FIRST_NAME_KEYS),
}
88 changes: 88 additions & 0 deletions tests/test_authors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
"""Testy normalizacji autora z niespójnych JSON-ów PBN.

PBN podaje nazwisko raz jako ``lastName``, raz jako ``familyName``,
a imię jako ``firstName``, ``givenNames`` albo ``name`` — w zależności
od endpointu. ``normalize_author_name`` sprowadza wszystkie warianty
do jednego kształtu ``{"lastName": ..., "firstName": ...}``.
"""

import pytest

from pbn_client import normalize_author_name


def test_firstname_lastname_payload():
assert normalize_author_name({"firstName": "Jan", "lastName": "Kowalski"}) == {
"lastName": "Kowalski",
"firstName": "Jan",
}


def test_familyname_givennames_payload():
assert normalize_author_name(
{"familyName": "Nowak", "givenNames": "Anna Maria"}
) == {"lastName": "Nowak", "firstName": "Anna Maria"}


def test_name_only_payload():
assert normalize_author_name({"name": "Zofia"}) == {
"lastName": None,
"firstName": "Zofia",
}


def test_givennames_only_payload():
assert normalize_author_name({"givenNames": "Piotr"}) == {
"lastName": None,
"firstName": "Piotr",
}


def test_lastname_only_payload():
assert normalize_author_name({"lastName": "Wiśniewska"}) == {
"lastName": "Wiśniewska",
"firstName": None,
}


def test_missing_everything():
assert normalize_author_name({}) == {"lastName": None, "firstName": None}


def test_irrelevant_keys_only():
assert normalize_author_name({"orcid": "0000-0001-2345-6789"}) == {
"lastName": None,
"firstName": None,
}


@pytest.mark.parametrize("value", [None, "5e70930d878c28a04b8efd23", 42, ["x"]])
def test_non_dict_input(value):
assert normalize_author_name(value) == {"lastName": None, "firstName": None}


def test_union_familyname_wins_over_lastname():
assert normalize_author_name({"familyName": "Wolski", "lastName": "Kowalski"}) == {
"lastName": "Wolski",
"firstName": None,
}


def test_union_firstname_wins_over_givennames_and_name():
assert normalize_author_name(
{"firstName": "Jan", "givenNames": "Janusz", "name": "Jasiek"}
) == {"lastName": None, "firstName": "Jan"}


def test_union_givennames_wins_over_name():
assert normalize_author_name({"givenNames": "Ewa", "name": "Ewelina"}) == {
"lastName": None,
"firstName": "Ewa",
}


def test_empty_string_values_fall_through():
# PBN potrafi przysłać puste stringi — traktujemy je jak brak wartości.
assert normalize_author_name(
{"firstName": "", "givenNames": "Adam", "lastName": "", "familyName": ""}
) == {"lastName": None, "firstName": "Adam"}
Loading