Skip to content

fix: link do PBN na stronie rekordu w instalacji multi-hosted - #761

Merged
mpasternak merged 1 commit into
devfrom
fix-link-do-pbn-multi-hosted
Aug 16, 2026
Merged

fix: link do PBN na stronie rekordu w instalacji multi-hosted#761
mpasternak merged 1 commit into
devfrom
fix-link-do-pbn-multi-hosted

Conversation

@mpasternak

Copy link
Copy Markdown
Member

Problem

Na instalacji UAFM (nauka.uafm.edu.pl) przycisk „Otwórz w PBN" na stronie rekordu prowadził do adresu None.

Potwierdzone na żywym HTML-u z produkcji, przed jakąkolwiek zmianą:

<button class="button open-button" title="Otwórz w PBN"
        data-open-url=None data-target=_blank>

Dane rekordu są w porządku — PBN UID (69b414514904f675735a2405) jest poprawny. Problem jest w budowaniu URL-a.

Przyczyna źródłowa

  1. Szablon praca_tabela_mono.html wołał {{ praca.link_do_pbn }}.
  2. Django template nie umie podać argumentu metodzie → LinkDoPBNMixin.link_do_pbn() leciała bez uczelnia.
  3. Bez uczelni metoda robi fallback na get_single_uczelnia_or_none(), które przy więcej niż jednej uczelni zwraca None — celowo, zgodnie z polityką „nie ma uczelni domyślnej" z audytu multi-hosted.
  4. Metoda zwraca None, a Django renderuje to dosłownie jako tekst None.

Stąd bug widoczny tylko w instalacjach multi-hosted (UAFM), a nie w single-install.

Ta klasa bugów była już łatana punktowo — filtr link_do_pi (track 7b) oraz strona autora (FD#390) dostały uczelnię z requestu — ale gałąź link_do_pbn na stronie rekordu została pominięta.

Rozwiązanie

  • Nowy filtr link_do_pbn w src/bpp/templatetags/prace.py, wzorowany na istniejącym link_do_pi: przekazuje uczelnię oglądającego z kontekstu szablonu do metody modelu.
  • {% if pbn_url %} wokół przycisku — gdy uczelni nie da się ustalić, przycisk po prostu nie istnieje, zamiast prowadzić donikąd. None w Django template jest cichy (nie ma wyjątku), więc guard jest konieczny.
  • _uczelnia_albo_none() — context processor wstawia do kontekstu placeholder NiezdefiniowanaUczelnia (klasa bez pk) gdy host się nie mapuje. Przekazanie go do metody modelu wysypałoby render na pbn_api_root; helper normalizuje to do None. Używa go również link_do_pi (ta sama latentna pułapka).

Przy okazji ten sam wzorzec naprawiony w linku do wydawnictwa nadrzędnego z PBN (miał href="None") oraz w legacy praca_tabela.html.

Weryfikacja

  • Repro-test przed poprawką: FAILED (odtwarza dokładnie produkcyjny HTML). Po: PASSED.
  • 495 passed, 1 skippedsrc/bpp/tests/test_views, test_models/test_struktura, test_safe_tytul, test_autorzy_dla_opisu_skrocony, src/oswiadczenia.
  • pre-commit — wszystkie hooki zielone.

Uwagi dla recenzenta

.pre-commit-config.yamlpraca_tabela.html ma 3 pre-existing błędy djlint (ten sam wzorzec {% if %}<a>{% else %}<a>{% endif %}, który konfiguracja już opisuje jako odroczony tech-debt i który dotyczy 3 innych plików na liście wyjątków). Zmierzone: diff nie dodaje ani jednego nowego błędu (3 przed → 3 po, tylko numer linii przesunięty). Szablon jest legacy — instalowany do dbtemplates migracją 0295, ale żaden widok ani {% include %} go nie używa (strona rekordu renderuje praca_tabela_mono.html). Alternatywą było cofnięcie poprawki w tym pliku.

Poza zakresem tego PR-a — ten sam wzorzec bare-call żyje jeszcze w ~10 miejscach, które w multi-hosted pokażą to samo None:

  • src/pbn_api/templates/admin/*/change_form.html ({{ original.link_do_pbn }})
  • src/importer_publikacji/templates/.../step_done.html
  • src/przemapuj_zrodla_pbn/templates/.../przemapuj_zrodlo.html

🤖 Generated with Claude Code

https://claude.ai/code/session_01WSUsgzYoDNnXpXGAn5otJg

Na stronie rekordu przycisk "Otworz w PBN" prowadzil do adresu "None"
w instalacjach z wiecej niz jedna uczelnia (potwierdzone na nauka.uafm.edu.pl:
`data-open-url=None`).

Przyczyna: szablon wolal `{{ praca.link_do_pbn }}` — Django template nie umie
podac argumentu metodzie, wiec `LinkDoPBNMixin.link_do_pbn()` leciala bez
`uczelnia` i robila fallback na `get_single_uczelnia_or_none()`, ktore przy >1
uczelni zwraca None (celowo — nie ma "uczelni domyslnej"). Metoda zwracala
None, a Django renderowalo to doslownie jako napis "None".

Rozwiazanie analogiczne do istniejacego filtra `link_do_pi` (track 7b) i do
strony autora (FD#390): nowy filtr `link_do_pbn` przekazuje uczelnie
ogladajacego z kontekstu. Gdy uczelni nie da sie ustalic — przycisk nie jest
renderowany zamiast prowadzic donikad.

Dodatkowo `_uczelnia_albo_none()` normalizuje placeholder
`NiezdefiniowanaUczelnia` (klasa bez `pk`, wstawiana przez context processor
gdy host sie nie mapuje) do None — przekazanie go do metody modelu wysypaloby
render na `pbn_api_root`. Uzywa go rowniez `link_do_pi`.

Ten sam wzorzec naprawiony przy okazji w linku do wydawnictwa nadrzednego
z PBN (mial `href="None"`) oraz w legacy `praca_tabela.html`. Ten ostatni ma
pre-existing dlug djlint (ten sam wzorzec `{% if %}<a>{% else %}<a>{% endif %}`
co inne pliki na liscie wyjatkow) — diff nie dodaje nowych bledow (3 przed,
3 po), wiec plik trafia na istniejaca liste exclude.

Testy: repro (multi-homed) + regresja (single-install).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WSUsgzYoDNnXpXGAn5otJg
@mpasternak
mpasternak force-pushed the fix-link-do-pbn-multi-hosted branch from c8c819d to df1a718 Compare August 16, 2026 11:47
@mpasternak

Copy link
Copy Markdown
Member Author

Odpowiedź na recenzję (Codex) — wszystkie trzy punkty zaadresowane

Gałąź zrebase'owana na origin/dev (była 2 commity w tyle, dokładnie w tych plikach) i commit zamendowany.

Major — praca_tabela.html × opis_bibliograficzny()VariableDoesNotExist

Potwierdzone empirycznie, nie tylko z lektury:

django.template.base.VariableDoesNotExist: Failed lookup for key [uczelnia] in
[{'True': True, 'False': False, 'None': None}, {'praca': <Wydawnictwo_Zwarte: …>, 'links': None}]

Moje pierwotne założenie („szablon jest martwy") było błędne — grep szukał {% include %}, a szablon jest wybierany po nazwie z bazy przez SzablonDlaOpisuBibliograficznego. dev sam to zresztą stwierdza w komentarzu dodanym w #732 (linie 7–9) i dowozi migrację 0488_purge_praca_tabela_dbtemplate.py, żeby edycje pliku w ogóle miały efekt.

Poprawka strukturalna, nie punktowa: link_do_pbn przestaje być filtrem z argumentem, a staje się simple_tag(takes_context=True) — czyta uczelnia z kontekstu sam. Django rozwija argumenty filtrów zachłannie i poza blokiem try, więc każdy {% with x=…|filtr:uczelnia %} byłby miną w każdym renderze bez context processorów. Tag zamienia „brak zmiennej" z błędu składniowego w zwykłe „nie ma uczelni".

Użycie: {% link_do_pbn praca as pbn_url %}.

Dodatkowa obserwacja, której nie ma w recenzji, a która potwierdza, że brak linku jest tu poprawny, a nie tylko bezpieczny: opis_bibliograficzny_cache jest zapisywany per rekord, nie per uczelnia. Link zależny od uczelni oglądającego przeciekałby w nim między tenantami.

Test: src/bpp/tests/test_models/test_opis_bibliograficzny_wariant_pbn.py (czerwony przed poprawką, zielony po).

Minor — link_do_pi i fałszywy alarm do Rollbara

Potwierdzone. Wartownik _UCZELNIA_NIE_PODANA odróżnia teraz „filtr zawołany bez argumentu" (legacy-fallback w metodzie modelu) od „argument podany, ale uczelni nie da się ustalić" (zwracamy brak linku bez dotykania bazy).

Zweryfikowane przez zdegradowanie kodu do wersji sprzed poprawki — test faktycznie łapie regresję:

AssertionError: Expected 'report_message' to not have been called. Called 1 times.
Calls: [call("Znaleziono duplikaty PublikacjaInstytucji_V2 dla objectId_id=… …", level='warning', …)]

Test: src/bpp/tests/test_templatetags/test_link_do_pi_placeholder.py.

Nit — wykluczenie w .pre-commit-config.yaml

Wycofane w całości — plik nie jest już wykluczany z djlinta, a mimo to hook przechodzi na zielono.

Najpierw spróbowałem zawęzić wyciszenie ({# djlint:off H025 #} wokół feralnej pętli) — nie działa: niezbalansowany <a> psuje parser także niżej, więc </td> dalej wychodził jako sierota. Więc naprawiona została przyczyna:

  1. Pętla autorów przepisana na jeden węzeł <a> zamiast otwarcia i zamknięcia w osobnych gałęziach {% if %}.
  2. Przy okazji wyszedł realny bug w markupie: zdublowany </td> w wierszu OpenAccess (linie 314–315). Usunięty.
  3. http://doi.org/https://doi.org/ (H022).

Refaktor jest bezzmianowy semantycznie i zostało to zmierzone, nie założone — porównałem znak po znaku wyrenderowany opis dla wszystkich czterech trybów links:

links wynik
None (ten cache'owany) identyczny
"normal" identyczny
"cokolwiek" identyczny
"admin" zniknęła zbłąkana spacja wewnątrz <a>: <a href="…"> Kowalski Jan</a><a href="…">Kowalski Jan</a>

Kontrakt wszystkich czterech trybów przykryty testem charakteryzującym: src/bpp/tests/test_models/test_opis_bibliograficzny_wariant_autorzy.py (napisany i uruchomiony przed refaktorem, żeby był świadkiem, a nie potwierdzeniem).

Open question — czy praca_tabela.html ma pozostać wspierany?

Tak. dev traktuje go jako żywy wariant: #732 dodał tam oznacz_jezyk (WCAG 3.1.2), dołożył migrację czyszczącą osierocony wiersz dbtemplates i testy test_wcag/test_purge_dbtemplate.py. Dlatego został naprawiony i objęty linterem, a nie wykluczony.

Weryfikacja

  • 1000 passed, 1 skippedtest_views, test_models, test_templatetags, test_wcag, test_opis_bibliograficzny, test_dbtemplates_sync, pbn_api/test_link_do_pi_per_uczelnia, oswiadczenia.
  • pre-commit — wszystkie hooki zielone, bez żadnego nowego wykluczenia.

Poza zakresem (bez zmian)

Ten sam wzorzec bare-call link_do_pbn żyje jeszcze w szablonach admina pbn_api, w importer_publikacji i przemapuj_zrodla_pbn — w multi-hosted pokażą to samo None. Osobny PR.

Znalazłem też przy okazji rzecz starszą niż ten PR i tu nietkniętą: wariant praca_tabela.html nie renderuje się na niezapisanej instancji (denorm woła opis_bibliograficzny() w pre_save, a szablon sięga po praca.streszczenia.existsValueError). Do osobnego zgłoszenia.

@mpasternak
mpasternak merged commit 8aec022 into dev Aug 16, 2026
22 checks passed
@mpasternak
mpasternak deleted the fix-link-do-pbn-multi-hosted branch August 16, 2026 19:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant