Skip to content
Open
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
10 changes: 9 additions & 1 deletion src/bpp/export/bibtex.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,15 @@ def generate_bibtex_key(wydawnictwo) -> str:
key_parts = []

try:
first_author = wydawnictwo.autorzy_dla_opisu().first()
# `autorzy_dla_opisu()` NIE zawsze zwraca queryset. Zwraca też zwykłą
# listę (prefetch przez `Prefetch(to_attr=...)` albo `[]` dla obiektu
# bez `pk`) oraz `FakeSet` doktoratu/habilitacji. Tylko queryset ma
# `first()` — dla pozostałych trzeba wziąć pierwszy element wprost.
# Ten sam idiom stosuje `bpp.models.util`.
autorzy = wydawnictwo.autorzy_dla_opisu()
first_author = (
autorzy.first() if hasattr(autorzy, "first") else next(iter(autorzy), None)
)
if first_author and hasattr(first_author, "autor"):
surname = first_author.autor.nazwisko
# Clean surname for key
Expand Down
14 changes: 14 additions & 0 deletions src/bpp/models/praca_doktorska.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@ class FakeSet(list):
def all(self):
return self

def first(self):
# Doktorat/habilitacja ma dokładnie jednego autora (pole
# `autor`, nie relacja M2M), ale kod wołający traktuje wynik
# jak queryset. Bez `first()` leciał AttributeError — m.in.
# w bpp.export.bibtex.generate_bibtex_key.
return self[0] if self else None

def select_related(self, *args, **kw):
return self

Expand All @@ -47,6 +54,13 @@ def exclude(self, *args, **kw):
return self

def exists(self):
# NIE jest to sprzeczność z `first()` powyżej. `exists()` na
# FakeSet jest osiągalne wyłącznie po łańcuchu `exclude(...)`
# (patrz Rekord.ma_odpiete_dyscypliny), a `exclude()` tutaj
# IGNORUJE warunki i zwraca self. Twarde False znaczy więc
# „brak autorów z odpiętą dyscypliną", nie „brak autorów" —
# doktorat nie ma czego odpinać. Gdyby zwracało True, strona
# rekordu renderowałaby pusty box „odpięte dyscypliny".
return False

ret = FakeAutorDoktoratuHabilitacji()
Expand Down
5 changes: 5 additions & 0 deletions src/bpp/newsfragments/+bibtex-klucz-doktoratu.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Klucz cytowania w eksporcie BibTeX znów zawiera nazwisko pierwszego autora —
dotyczyło to prac doktorskich i habilitacyjnych oraz rekordów otwieranych
bezpośrednio ze strony rekordu. Wcześniej klucz cichcem gubił nazwisko
i zostawało samo ``<rok>_id<numer>``; wyjątek był połykany, więc eksport
działał, tylko z gorszym kluczem.
62 changes: 62 additions & 0 deletions src/bpp/tests/test_export/test_bibtex_klucz_doktoratu.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
"""Klucz BibTeX dla pracy doktorskiej/habilitacyjnej zawiera nazwisko autora.

Regresja (Rollbar #447): ``Praca_Doktorska.autorzy_dla_opisu()`` zwraca
``FakeSet`` — listę udającą queryset, bo doktorat ma dokładnie jednego autora
w polu ``autor``, a nie relację wiele-do-wielu. ``FakeSet`` nie miał metody
``first()``, więc ``generate_bibtex_key`` leciało na ``AttributeError``.
Wyjątek był połykany (``zaloguj_polkniety_wyjatek``), więc eksport nie padał —
tyle że klucz cichcem gubił nazwisko i zostawało samo ``<rok>_id<pk>``.
"""

import pytest
from model_bakery import baker

from bpp.export.bibtex import generate_bibtex_key
from bpp.models import Autor, Wydawnictwo_Ciagle
from bpp.models.util import prefetch_dane_strony_rekordu


@pytest.mark.django_db
def test_klucz_bibtex_doktoratu_zawiera_nazwisko_autora(doktorat, autor_jan_kowalski):
doktorat.autor = autor_jan_kowalski
doktorat.rok = 2020
doktorat.save()

klucz = generate_bibtex_key(doktorat)

assert klucz == f"Kowalski_2020_id{doktorat.pk}"


@pytest.mark.django_db
def test_klucz_bibtex_habilitacji_zawiera_nazwisko_autora(
habilitacja, autor_jan_kowalski
):
habilitacja.autor = autor_jan_kowalski
habilitacja.rok = 2021
habilitacja.save()

klucz = generate_bibtex_key(habilitacja)

assert klucz == f"Kowalski_2021_id{habilitacja.pk}"


@pytest.mark.django_db
def test_klucz_bibtex_dziala_gdy_autorzy_sa_zwykla_lista(typy_odpowiedzialnosci):
"""``autorzy_dla_opisu()`` bywa ZWYKŁĄ LISTĄ, nie tylko querysetem.

``bpp.models.util`` zwraca stamtąd trzy różne kształty: queryset, ``[]``
dla obiektu bez ``pk`` oraz listę podstawioną przez
``prefetch_dane_strony_rekordu`` (``Prefetch(to_attr=...)``). Czwarty to
``FakeSet`` doktoratu. Żaden z listowych nie ma ``first()``, więc
naprawienie samego ``FakeSet`` zostawiłoby tę samą pułapkę obok.
"""
autor = baker.make(Autor, nazwisko="Nowak", imiona="Anna")
wyd = baker.make(Wydawnictwo_Ciagle, tytul_oryginalny="Artykuł", rok=2019)
wyd.dodaj_autora(autor, baker.make("bpp.Jednostka"))

prefetch_dane_strony_rekordu(wyd)
assert isinstance(wyd.autorzy_dla_opisu(), list), (
"prefetch miał podstawić listę — bez tego test nie bada tej ścieżki"
)

assert generate_bibtex_key(wyd) == f"Nowak_2019_id{wyd.pk}"