Skip to content

feat(profil-autora): konfigurowalny profil autora — sekcje, samodzielna edycja, eksport BibTeX/RIS, RAD-on - #385

Merged
mpasternak merged 52 commits into
devfrom
feature/profil-autora
Oct 24, 2026
Merged

mpasternak merged 52 commits into
devfrom
feature/profil-autora

Conversation

@mpasternak

@mpasternak mpasternak commented Jun 18, 2026 •

Copy link
Copy Markdown
Member

Cel

Podstrona autora (/bpp/autor/<pk|slug>/) przestaje być „metadane + jeden formularz wyszukiwania”. Staje się dwukolumnowym profilem złożonym z konfigurowalnych sekcji: uczelnia ustawia ich kolejność, widoczność i limit. Autor może sam uzupełnić zdjęcie, biogram i wyróżnione prace.

Specyfikacje i notatki z sesji:

  • docs/superpowers/specs/2026-06-18-profil-autora-i-podstrona-design.md (projekt bazowy)
  • docs/superpowers/specs/2026-06-19-profil-autora-rewizja-2col-HANDOFF.md (rewizja: 2 kolumny, układ per uczelnia)
  • docs/superpowers/specs/2026-06-19-profil-autora-faza2-NEXT-HANDOFF.md (stan po Fazie 2)
  • docs/superpowers/specs/2026-07-07-radon-osiagniecia-autora-design.md (osiągnięcia z RAD-on)

PR zaczynał jako „Faza 1”. W trakcie weszła też większość pierwotnej „Fazy 2” (samodzielna edycja, eksport, edytor układu), więc opis obejmuje całość.

Co wchodzi

Podstrona autora

  • Dwie kolumny. Lewa to wizytówka: zdjęcie, biogram, jednostka, identyfikatory, metryki, stopnie, historia zatrudnienia, wyszukiwarka, raport, eksport. Prawa to statystyki, wykresy i listy. Na wąskim ekranie kolumny układają się jedna pod drugą.
  • Rejestr 30 typów sekcji (bpp/profil_autora.py) i buildery danych (bpp/profil_autora_dane.py). Puste sekcje same się ukrywają. Dane pochodzą z cache Rekord/Autorzy/AuthorConnection.
  • Sekcje:
    • podsumowanie dorobku;
    • statystyki wg charakteru formalnego, klikalne do multiseeka;
    • wykresy roczne: liczba prac, punkty MNiSW, IF, za ostatnie 10 lat (OKNO_LAT_WYKRESU), czysty SVG/HTML bez bibliotek;
    • najlepsze wg PK/IF, najnowsze artykuły i prace zwarte, ostatnio edytowane;
    • źródła, dyscypliny, współautorzy (z linkiem do istniejącego grafu powiązań 2D/3D), cytowania;
    • wyróżnione prace.
  • Klik w całą pozycję listy prac działa przez stretched-link, a linki w opisie (np. DOI) dalej działają.
  • Identyfikatory: linki do profili WoS ResearcherID, Scopus, Google Scholar i portalu uczelni. Etykietę portalu ustawia uczelnia.
  • Osiągnięcia z RAD-on OpenData: projekty, patenty i osiągnięcia pobierane po ORCID po stronie przeglądarki (@iplweb/radon-opendata). Treść trafia na stronę wyłącznie przez textContent.
  • Raport autora: linki (bieżący rok, ostatnie 4 lata, formularz szczegółowy) pokazują się tylko wtedy, gdy DefinicjaRaportu(slug="raport-autorow") jest widoczna dla użytkownika.

Konfiguracja przez uczelnię (admin Uczelnia)

  • uklad_profilu_autora: układ obu kolumn edytowany kafelkowo (drag-drop na jquery-ui sortable z Grappelli, checkbox widoczności, limit). Zapisuje się do JSON-a walidowanego przez waliduj_uklad. Nowe sekcje dochodzą do zapisanego układu automatycznie (forward-compat).
  • szerokosc_lewej_kolumny i etykieta_profilu_uczelni.

Dane autora (admin Autor i „Mój profil”)

  • Nowe pola Autor:
    • zdjecie: przy zapisie EXIF → center-crop → WebP 400×400, limit 5 MB;
    • biogram + biogram_format: Markdown lub HTML, czyszczony przez safe_biogram_html (bogatsza lista tagów niż globalne safe_html, bez treści script/style, linki z rel="nofollow noopener");
    • wos_researcherid, scopus_author_id, google_scholar_id, url_profilu_uczelni.
  • Nowy model WybranaPublikacjaAutora (GenericFK do publikacji) z inline'em w adminie.
  • Samodzielna edycja w /bpp/profil/edycja/: upload zdjęcia, biogram z podglądem na żywo i picker wyróżnionych prac (/bpp/profil/edycja/wybrane-publikacje/). Autocomplete Select2 szuka tylko po własnych pracach autora, a własność jest sprawdzana po stronie serwera przy każdej akcji.

Eksport publikacji autora

  • BibTeX (istniejący bpp/export/bibtex.py) i RIS (nowy bpp/export/ris.py).
  • Eksport biegnie asynchronicznie przez Celery (model AutorEksportTask), ze stroną postępu i pobraniem pliku (/bpp/eksport-autora/<uuid>/). Limit wynosi MAKS_EKSPORT = 5000 prac.
  • Bezpośrednie /bpp/autor/<pk>/eksport.bib i .ris zostają.

Dokumentacja użytkownika: docs/uzytkownik/przegladanie-i-wyszukiwanie.md, docs/administrator/ogolna.md, docs/redaktor/autorzy.md.

Przy okazji (poza samym profilem)

  • multiseek: poprawna etykieta charakteru formalnego w polu wyszukiwania;
  • nowe raporty: „Raport szczegółowy” wypełnia autora w polu Select2;
  • fulltext: test nie zależy już od danych bazowych Typ_Odpowiedzialnosci.

Migracje (bpp)

Migracja Co robi
0444_autor_biogram_… Autor.zdjecie, biogram, biogram_format, uklad_profilu; model WybranaPublikacjaAutora
0445_remove_autor_uklad_profilu_… układ przechodzi z Autor na Uczelnia.uklad_profilu_autora
0447_autoreksporttask model AutorEksportTask
0448_uczelnia_szerokosc_lewej_kolumny_… Uczelnia.szerokosc_lewej_kolumny, układ obu kolumn
0492_autor_identyfikatory_profili identyfikatory WoS/Scopus/Scholar/portal + Uczelnia.etykieta_profilu_uczelni
0446, 0454, 0487, 0491, 0493 puste migracje scalające graf po kolejnych mergach dev

Testy

  • 147 testów w src/bpp/tests/test_profil/ (21 plików):
    • czyste jednostki: biogram, zdjęcie, układ, identyfikatory, RIS;
    • modele i admin;
    • widok: sekcje, ukrywanie, raport, eksport synchroniczny i asynchroniczny;
    • samodzielna edycja i picker wyróżnionych prac, z kontrolą własności;
  • tests/js/radon-profil.test.js (vitest).
  • Lokalnie, 2026-10-24, po scaleniu z dev: pełne make tests przeszło (pytest 10156 passed, Playwright 179 passed, vitest 124 passed).

⚠️ Do zrobienia przy scalaniu

make baseline-update (migracje 0444–0493 zmieniają schemat), potem commit baseline-sql/baseline.sql i baseline-sql/baseline.meta.json. Świadomie nie robione w gałęzi, żeby nie produkować konfliktów na dużym pliku.

Poza zakresem

  • Drobny polish z notatek: aria-label z tytułem pracy na nakładce listy prac, ściągawka Markdown przy biogramie.
  • Wartości do kalibracji po obejrzeniu na prawdziwych danych: okno 10 lat na wykresach rocznych, 3 linie line-clamp opisu, domyślna szerokość kolumn.

🤖 Generated with Claude Code

https://claude.ai/code/session_01V5NgRzHfVBb6qGi5agZDvw

mpasternak and others added 13 commits June 18, 2026 21:40
… autora

Projekt z sesji brainstormingu: konfigurowalne sekcje podstrony autora
(kolejnosc/widocznosc/limit), nowe pola Autor (zdjecie WebP 400x400,
biogram + biogram_format md/html), model WybranaPublikacjaAutora, rejestr
sekcji w kodzie + uklad_profilu JSON, render z auto-ukrywaniem pustych,
linki do raportu wg widoczny_dla, wspolautorzy z AuthorConnection + CTA do
browsera 2D/3D. Dostawa fazowa: Faza 1 (model+render+admin), Faza 2
(self-service edytor + RIS).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rejestr sekcji

Czyste jednostki (TDD, 27 testów):
- bpp.util.biogram.renderuj_biogram: Markdown/HTML → bezpieczny HTML przez nowy
  safe_biogram_html (bogatszy zestaw tagów blokowych niż wąski globalny
  safe_html; usuwa treść script/style; linki dostają rel=nofollow noopener).
- bpp.util.obrazy.przetworz_zdjecie_autora: EXIF→crop kwadrat→WebP 400x400.
- bpp.profil_autora: katalog typów sekcji + walidacja i rozwiązywanie układu
  (kolejność/widoczność/limit, wymuszona wyszukiwarka, forward-compat).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…utora

Autor: zdjecie (ImageField), biogram + biogram_format (md/html),
uklad_profilu (JSONField) oraz cached_property biogram_html.
Nowy model WybranaPublikacjaAutora (GenericFK do polimorficznej publikacji).
Migracja 0444. Baseline odświeżyć przy scalaniu (make baseline-update).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… + style

- profil_autora_dane: buildery danych każdej sekcji (listy prac, statystyki,
  wykres lat, dyscypliny, źródła, współautorzy z AuthorConnection) + auto-
  ukrywanie pustych sekcji.
- AutorView: render sekcji wg uklad_profilu (lub domyślnego) + linki do
  raportu autora (bieżący rok / 4 lata / formularz) gated DefinicjaRaportu
  .widoczny_dla.
- browse/autor.html: stały nagłówek (z awatarem) + pętla sekcji; formularz
  wyszukiwania wydzielony do sekcji autor_sekcje/wyszukiwarka.html; partiale
  wszystkich sekcji; embed-kod bez zmian.
- AutorAdmin: fieldset profilu (zdjęcie/biogram/format/układ), przetwarzanie
  zdjęcia w AutorForm.clean_zdjecie (≤5MB → WebP 400x400), inline wybranych
  publikacji.
- SCSS: style sekcji (awatar, listy, tabele statystyk, wykres słupkowy,
  współautorzy, linki raportu).

Testy: 4 integracyjne (render biogramu/wyszukiwarki, ukrywanie sekcji,
linki raportu public/inactive). Cała suita test_profil + istniejące
test_autor_autorzy zielone (43) na świeżym kontenerze — bez regresji.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Django-generated migracja miała linie >88 znaków; CI "Lint changed files"
uruchamia ruff-format na zmienionym zakresie z --exit-non-zero-on-fix.
To nowy plik migracji (nie istniejący), więc formatowanie jest dozwolone.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Import bpp.util.biogram należy do grupy first-party (po bpp.util), nie do
grupy third-party po tinymce. Poprawka nie trafiła do poprzedniego commita.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Spisane decyzje z 2026-06-19: układ 2-kolumnowy (lewa=klasyka+wizytówka,
prawa=kafelki analityczne), układ globalny per-Uczelnia (multi-uczelnia,
get_for_request), edytor kafelkowy admin-only, autor edytuje tylko
biogram+zdjęcie, klik w całą pozycję pracy, line-clamp opisów, wykres
liniowy >10 lat, klik w charakter → wyszukiwarka, historia zatrudnienia,
wykresy PK/IF. Dokument samowystarczalny dla wznowienia po resecie sesji.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ablon 2-kolumnowy

Rewizja 2-kolumnowa (§3.1–§3.3 handoffa):

- Układ profilu jest teraz GLOBALNY per-Uczelnia (multi-uczelnia):
  nowe pole Uczelnia.uklad_profilu_autora (JSONField). Usunięto
  Autor.uklad_profilu (override per-autor zbędny). Migracja 0445
  (AddField Uczelnia + RemoveField Autor; 0444 nietknięta).
- rozwiaz_uklad(uczelnia), przygotuj_sekcje(autor, uczelnia, request).
- Rejestr KATALOG_SEKCJI obejmuje tylko PRAWĄ kolumnę: usunięto
  biogram/wyszukiwarka/eksport oraz pole TypSekcji.obowiazkowa.
  Dodano klucze wykres_pk_lata + wykres_if_lata (buildery w §3.5).
- Szablon browse/autor.html przebudowany na Foundation grid w HTML
  (cell large-4 lewa: zdjęcie→biogram→jednostka→[historia §3.7]→
  identyfikatory→metryki→stopnie→opis→cytowania→wyszukiwarka→raport;
  cell large-8 prawa: pętla sekcji). Biogram i wyszukiwarka są stałe
  w lewej kolumnie (nie konfigurowalne).
- Admin: usunięto uklad_profilu z AutorForm/fieldsetu (edytor układu
  na Uczelni dochodzi osobnym commitem).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- _lista_prac.html: usunięto link [szczegóły]; każda pozycja <li> niesie
  data-href = praca.get_absolute_url, role="link", tabindex=0; opis w
  kontenerze .autor-page__praca-opis (line-clamp dochodzi w SCSS).
- autor.html: delegowany handler kliku/Enter nawiguje do data-href,
  pomijając kliknięcia w wewnętrzne <a> (DOI itp.) — bez zagnieżdżania
  <a> w <a>.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Wspólny partial _wykres_lata.html: >10 punktów → liniowy SVG
  (polyline + punkty z tooltipami, oś Y odwrócona transformem żeby
  liczyć współrzędne widthratio bez odejmowania), do 10 → słupki.
- _agreguj_po_latach(pary) — czysta agregacja (rok, wartosc) → suma/rok
  + maks + suma; pomija None.
- Buildery _wykres_pk_lata (suma punkty_kbn/rok) i _wykres_if_lata
  (suma impact_factor/rok; None gdy suma IF == 0 → auto-hide). _wykres_lata
  zyskuje etykietę. DISTINCT z id neutralizuje duplikaty joinu autorzy.
- Sekcje wykres_lata/wykres_pk_lata/wykres_if_lata delegują do partiala.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… (§3.6)

- statystyki_charakter.html: każdy wiersz to POST do bpp:browse_build_search
  z autor=<pk> + charakter_formalny=<nazwa> (+ suggested-title). Nazwa, nie
  pk — CharakterFormalnyQueryObject.value_from_web rozwiązuje po `nazwa`
  (z potomkami MPTT).
- BuildSearch.post: dodano charakter_box przez CharakterFormalnyQueryObject,
  wpięty w oba warianty zrob_formularz (z/bez wydziałów). Zmiana addytywna —
  aktywna tylko gdy POST zawiera charakter_formalny.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Autor.historia_zatrudnienia() → Autor_Jednostka filtrowane po autorze,
  select_related(jednostka, funkcja), order_by(-rozpoczal_prace).
- _historia_zatrudnienia.html: lista „Jednostka — od–do (funkcja)";
  „obecnie" gdy brak zakonczyl_prace, „?" gdy brak rozpoczal_prace.
  Pusta historia → sekcja się nie renderuje.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
….1 admin + SCSS)

- UczelniaAdmin: nowy fieldset „Profil autora (podstrona)" z polem
  uklad_profilu_autora (JSON w textarea + opis). MVP edytora globalnego
  układu prawej kolumny.
- SCSS (_autor-bem.scss): klikalna pozycja listy prac (hover/focus) +
  line-clamp opisu (~3 linie); wykres liniowy SVG (linia/punkty/oś X);
  statystyki jako link-przycisk; historia zatrudnienia; drobne style
  kolumn (bez nadpisywania klas grid Foundation).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Comment thread src/bpp/templates/browse/autor.html Fixed
Comment thread src/bpp/templates/browse/autor.html Fixed
…direction)

CodeQL zgłosił 2 high-severity alerty dla zapisu wartości z DOM
(data-href) do window.location. data-href pochodzi z get_absolute_url
(zaufane), ale dla pewności i zadowolenia skanera nawigujemy WYŁĄCZNIE
do ścieżek względnych w obrębie serwisu: walidacja /^\/[^/]/ odcina
javascript:, //host i http(s)://. Wspólny helper nawigujDoPracy().

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Comment thread src/bpp/templates/browse/autor.html Fixed
mpasternak and others added 14 commits June 19, 2026 12:21
…odeQL)

CodeQL js/xss-through-dom flagował zapis wartości z DOM do
window.location (możliwy javascript:). Eliminujemy sink całkowicie:
zamiast data-href + JS używamy pustej nakładki <a> (position:absolute)
pokrywającej całą pozycję; wewnętrzne linki opisu (DOI) wynosimy ponad
nakładkę przez z-index. Prawdziwy, dostępny <a>, zero JS, brak
zagnieżdżenia <a> w <a>.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…za 2)

Zalogowany autor (user.autor) edytuje WŁASNY biogram (MD/HTML + podgląd
na żywo) i zdjęcie w „Mój profil":

- ProfilEdycjaView (UpdateView na request.user.autor) + AutorProfilForm
  (zdjecie/biogram/biogram_format), reużycie clean_zdjecie +
  przetworz_zdjecie_autora. WymagajAutoraMixin: anonim → login, zalogowany
  bez autora → przekierowanie na „Mój profil" z komunikatem.
- ProfilBiogramPodgladView — AJAX podgląd przez renderuj_biogram (ten sam
  pipeline sanityzacji co przy zapisie; XSS wycięty).
- URL-e bpp:profil-edycja, bpp:profil-biogram-podglad. Szablon z live
  preview (debounce, CSRF). Link „Edytuj swoją stronę" na „Mój profil".
- Układ NIE jest edytowalny przez autora (globalny per-Uczelnia).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…lnosci)

test_publication_fulltext_search_uses_weighted_cached_fields wołał
dodaj_autora() (wymaga Typ_Odpowiedzialnosci "aut."), ale nie deklarował
tej zależności — polegał na danych bazowych, które poprzedzający test
transakcyjny mógł wyczyścić (flush). Dodanie testów w §3.8 zmieniło
podział pytest-split na shardy i ujawniło tę latentną kruchość (shard 0).
Dodajemy fixture typy_odpowiedzialnosci, która deterministycznie tworzy
pełny zestaw typów (w tym "aut.").

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Samowystarczalny handoff: stan brancha (CI zielone, PR #385), architektura,
lekcje (CodeQL stretched-link, §3.6 charakter po nazwie, pytest-split shardy),
świadomie odłożone (baseline-update + merge), oraz kandydaci na dalej
(weryfikacja wizualna, edytor kafelkowy układu, eksport BibTeX/RIS, picker
wyróżnionych prac).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… kalibracja)

Wykresy roczne (Publikacje/Punkty MNiSW/Impact Factor w latach) nie miały
żadnego odniesienia na osi Y — wartości czytelne tylko po najechaniu. Dodaje
lekką oś Y bez zależności (czysty SVG/HTML/CSS) dla obu wariantów:

- liniowy (>10 lat): kolumna HTML `wykres-osY` (maks u góry, 0 u dołu) obok
  SVG; dwie linie siatki jako `<line>` w grupie transformowanej (y=200 maks,
  y=100 połowa) z `vector-effect: non-scaling-stroke` — bez dystorsji.
- słupkowy (≤10 lat): etykieta maks (HTML) + górna linia siatki na poziomie
  najwyższego słupka.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Dwa publiczne endpointy zwracające plik do pobrania ze wszystkimi
publikacjami autora:

- GET /bpp/autor/<pk>/eksport.bib  → BibTeX (reuse bpp.export.bibtex)
- GET /bpp/autor/<pk>/eksport.ris  → RIS (nowy moduł bpp.export.ris,
  zbudowany na wzór bibtex.py: per-typ konwertery + dispatcher)

Plus dwa przyciski "Eksport publikacji" (Foundation, fi-download) w lewej
kolumnie strony autora.

Limit MAKS_EKSPORT=5000 chroni przed OOM przy autorach z ogromną liczbą
prac; przy obcięciu BibTeX dostaje komentarz "% Wyeksportowano pierwsze
N z M", RIS (format liniowy) polega na samym limicie.

Testy TDD: src/bpp/tests/test_profil/test_eksport.py (6 testów, HTTP +
unit + edge: pusty autor / 404).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… dev)

CI testuje branch scalony z dev. Dev dodał 0444_alter_uczelnia_wyszukiwanie
(gałąź dev), profil dodał 0444_autor_biogram → 0445_remove_autor_uklad_profilu
(gałąź feature) — wspólny rodzic 0443 → dwa liście grafu migracji. 0446 to
pusta migracja scalająca (operations=[]) zależna od obu liści.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Zalogowany autor zarządza listą WŁASNYCH wyróżnionych publikacji na swojej
podstronie: dodaje (autouzupełnianie po własnym dorobku), usuwa i zmienia
kolejność. Persystencja do istniejącego modelu WybranaPublikacjaAutora
(bez nowej migracji).

Bezpieczeństwo egzekwowane serwerowo na każdej akcji:
- WymagajAutoraMixin gatuje dostęp (anonim->login, brak user.autor->profil),
- dodanie waliduje przynależność Rekordu do Rekord.objects.prace_autora(autor),
- usunięcie/przesunięcie filtruje wiersze przez autor=request.user.autor,
- duplikat (unique_together) to przyjazny no-op, nie 500.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… polish)

Opis bibliograficzny zawiera HTML (<b>/<i>); na liście i w podpowiedziach
JS wstawia tekst przez textContent (bezpiecznie, bez DOM-XSS), więc surowe
tagi były widoczne dosłownie. striptags (szablon) + strip_tags (autocomplete)
dają spójny, czytelny tekst.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…(§3.2)

Zastępuje surowy textarea JSON dla Uczelnia.uklad_profilu_autora
przyjaznym, kafelkowym edytorem drag-drop w adminie uczelni:

- EdytorUkladuWidget (src/bpp/admin/widgets/uklad_profilu.py): renderuje
  kafelek dla KAŻDEJ sekcji katalogu (zapisana kolejność + doklejone
  brakujące z domyślem, forward-compat jak rozwiaz_uklad), z checkboxem
  widoczności, selectem limitu tylko dla sekcji ma_limit i ukrytym
  inputem z kanonicznym JSON-em.
- Szablon admin/widgets/uklad_profilu.html (emoji, single-line {# #}).
- JS uklad_profilu_edytor.js: jquery-ui .sortable() (z Grappelli) +
  re-serializacja kafelków do ukrytego inputa na drag/checkbox/limit,
  na django.jQuery, bez błędów konsoli.
- UczelniaAdminForm z clean_uklad_profilu_autora() sanityzującym przez
  waliduj_uklad(); podpięty jako form na UczelniaAdmin; opis fieldsetu
  zaktualizowany (drag-drop zamiast "wpisz JSON").

Serializacja zgodna ze schematem {"klucz","widoczna","limit"}
(walidowanym przez waliduj_uklad). 11 testów (TDD), wszystkie zielone.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ji 0446)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ia, węższe wykresy

Feedback ze strony autora:
- kolumny large-4/large-8 -> large-6/large-6 (równy podział 50/50);
- zdjęcie większe (220px) i klikalne -> powiększenie w modalu Reveal
  (fallback bez JS: <a href> otwiera pełny obraz);
- historia zatrudnienia: bez sztucznej Obcej jednostki i bez wierszy bez
  daty rozpoczęcia (koniec z pytajnikami); filtr w modelu + przez AutorView;
- wykresy roczne domyślnie ostatnie 10 lat (OKNO_LAT_WYKRESU) i węższy blok
  (max-width) - czytelniejsze niż pełna, rozlana historia.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Klik w charakter na stronie autora budował poprawne zapytanie, ale pole
VALUE_LIST w multiseeku było puste: zapisywana goła nazwa nie równała się
żadnej opcji selecta (te są etykietami MPTT z prefiksem/spacją). Dodaje
CharakterFormalnyQueryObject.value_to_web mapujący nazwę na dokładną etykietę
listy - pole pokazuje wartość, zapytanie bez zmian.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
mpasternak and others added 7 commits June 20, 2026 13:35
…onie autora

Drobny feedback wizualny:
- modal powiększenia zdjęcia: klik otwiera TYLKO modal (preventDefault na
  linku), bez nawigacji do pełnego obrazu w nowym oknie; samo zdjęcie bez
  białego marginesu/tła Foundation Reveal, zaokrąglone rogi (selektor
  dwuklasowy .reveal.<modal> bije bazowe .reveal niezależnie od kolejności
  importów);
- statystyki wg charakteru: linki wyglądają jak zwykłe linki serwisu (kolor
  motywu $anchor-color, podkreślenie na hover, bez „kwadratowej ramki"
  / outline / box-shadow);
- wyszukiwarka „Opracowane w latach": checkbox i rok ZAWSZE w jednej linii
  (flex + nowrap), 3 lata w wierszu zamiast 6 (large-4) — czytelniej
  w węższej (50/50) lewej kolumnie.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Link „Raport szczegółowy…" ze strony autora prowadził do pustego formularza —
pole wyboru obiektu (Select2) nie miało wybranego autora. Teraz link przekazuje
?obiekt=<pk>, a RaportFormView.get_initial czyta ten parametr i ustawia initial,
dzięki czemu DAL renderuje wybraną opcję (nazwisko autora). Działa też dla
poziomu jednostki/wydziału (ten sam mechanizm). Zły/nieistniejący pk nie wywala
formularza.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
)

Eksport publikacji autora budowany dotychczas synchronicznie w requeście
(N+1 przy materializacji [r.original ...]) przeniesiony do zadania Celery
ze stroną postępu (HTMX) i pobraniem-gdy-gotowe.

- Model AutorEksportTask (UUID pk, public) w src/bpp/models/eksport_autora.py
- @shared_task generuj_eksport_autora + cleanup usun_stare_eksporty_autora
  w src/bpp/tasks.py; parytet bajt-w-bajt z widokiem synchronicznym
- Widoki Start/Status/Download (publiczne, anonimowe; autoryzacja przez
  UUID + status), URL-e bpp:autor_eksport_{start,status,pobierz}
- Szablony task_status.html + _progress.html (HTMX poll co 2s, Foundation,
  auto-download); przyciski na stronie autora przepięte na flow async
- Stare synchroniczne URL-e/widoki zachowane (back-compat)
- Beat-schedule cleanup co 24h; migracja 0447

Mitigacja nadużyć anonimowych: dedupe (reuse zadania autor+format w oknie
5 min) + twardy limit MAKS_EKSPORT=5000.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…isie

Wyszukiwarka „dodaj wyróżnioną pracę" filtrowała tylko po tytule. Teraz pyta
po pełnym opisie bibliograficznym (opis_bibliograficzny_cache), więc znajduje
własne prace także po nazwisku WSPÓŁAUTORA, źródle czy roku — jak globalna
wyszukiwarka. Każde słowo musi wystąpić (AND), więc „Nowak 2020" zawęża
sensownie. Nadal wyłącznie w obrębie własnych prac autora (bezpieczeństwo).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Zdjęcie + biogram w jednej wizytówce; zdjęcie pływa w lewo, biogram opływa je
z prawej i od dołu (overflow:hidden domyka float). Wcześniej zdjęcie było
osobnym blokiem i zostawiało dużo pustego miejsca po prawej w szerszej (50/50)
lewej kolumnie.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Każdy dawniej-sztywny blok lewej kolumny podstrony autora dostaje własny
partial w browse/autor_sekcje/ (zdjecie, biogram, jednostka, historia,
dyscypliny_naukowe, identyfikatory, metryki, stopnie, poprzednie_nazwiska,
opis, cytowania, raport, eksport). Markup przeniesiony 1:1 z autor.html
(zachowane bramki {% if %} i zamykające HR). historia_zatrudnienia.html
reużywa istniejący _historia_zatrudnienia.html.

Pliki na razie nieużywane (czysto addytywny krok) — wpięcie w układ
2-kolumnowy w kolejnym commicie. Bez zmiany zachowania, suita zielona.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Każdy element podstrony autora (lewa i prawa kolumna) jest teraz pozycją
rejestru otagowaną kolumną (lewa/prawa) z kolejnością, widocznością i
limitem — edytowalną per-Uczelnia. Domyślny układ odwzorowuje dzisiejszą
stronę (te same bloki lewej, te same sekcje prawej, 50/50).

Rejestr (profil_autora.py):
- TypSekcji.kolumna (+ template_only); schemat pozycji
  {klucz, kolumna, widoczna, limit}.
- KATALOG_SEKCJI obejmuje wszystkie bloki lewej kolumny + dotychczasowe
  prawe. Blok tożsamości „Dyscypliny naukowe" jako dyscypliny_naukowe
  (różny od statystycznej sekcji dyscypliny).
- waliduj_uklad uzupełnia brakującą kolumnę z katalogu (back-compat dla
  starych zapisów bez kolumny → prawa).
- rozwiaz_uklad zwraca dict {lewa, prawa}; sekcje spoza configu doklejane
  w swojej domyślnej kolumnie.

Dane (profil_autora_dane.py): przygotuj_sekcje zwraca obie kolumny;
sekcje template-only zawsze dołączane (partial sam się bramkuje),
data-driven pomijane gdy builder zwróci None.

Szablon (autor.html): obie kolumny renderowane z configu; szerokość lewej
z Uczelnia.szerokosc_lewej_kolumny (prawa = 12 - lewa). Float zdjęcia
domyka kolumna (flow-root) + clear na sekcjach (SCSS) — biogram opływa
zdjęcie jak dziś.

Model: Uczelnia.szerokosc_lewej_kolumny (4/6/8, default 6), migracja 0448.

Admin: dwustrefowy edytor kafelkowy (lewa ⇄ prawa, jquery-ui sortable
connectWith), serializacja z kolumną; pole szerokości w fieldset
„Profil autora (podstrona)".

Testy: test_uklad / test_edytor_ukladu / test_widok zaktualizowane do
nowego kontraktu (dict, kolumna, dwie strefy, szerokość, przeniesienie
biogramu do prawej); nowy test_przygotuj_sekcje. Cała suita test_profil
zielona (126 passed).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
mpasternak and others added 2 commits July 6, 2026 07:57
# Conflicts:
#	docs/redaktor/autorzy.md
#	src/bpp/admin/uczelnia.py
#	src/bpp/models/autor.py
#	src/bpp/templates/browse/autor.html
#	src/bpp/tests/test_fulltext_search.py
#	src/bpp/views/browse.py
#	src/nowe_raporty/tests/test_generyczne_widoki.py
Po scaleniu z dev renderowanie podstrony autora BEZ skonfigurowanej uczelni
(pusta baza / testy bez fixture'u) wywalało 500: base.html rozwiązuje
`uczelnia.skrot` twardo w breadcrumbie, a context-processor wstrzykuje w takim
przypadku sentinel `NiezdefiniowanaUczelnia` (ma `.skrot="NdU"`). AutorView
jednak przekazywał jawnie `uczelnia=None`, przesłaniając sentinel None-em.

Teraz klucz `uczelnia` trafia do kontekstu tylko gdy uczelnia istnieje; przy
None sentinel z context-processora zostaje nienaruszony. Logika Python-owa
(szer_lewej, link_do_pbn, przygotuj_sekcje, historia_zatrudnienia) dalej
dostaje realne None i jest na nie odporna (getattr / metody tolerują None).

Naprawia 8 testów test_profil/test_widok.py, które renderują podstronę bez
fixture'u `uczelnia`; jednocześnie usuwa latentny 500 w produkcji.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
mpasternak added a commit that referenced this pull request Jul 7, 2026
Rozdział: dziedziczenie wydawcy po wydawnictwie nadrzędnym + podświetlenie wymaganego pola (Freshdesk #385)
mpasternak and others added 3 commits July 7, 2026 21:31
Sekcja na podstronie autora pobierająca na żywo (client-side) dane z
RAD-on scientist/search dla autorów z ORCID: stopnie/tytuły/wykształcenie/
zatrudnienie/dyscypliny. Dopasowanie po nazwisku + weryfikacja po ORCID
(scientist zwraca personalData.orcid). CORS otwarty. Ludzie Nauki i
projekty/publikacje świadomie poza zakresem (CORS zamknięty / brak filtra
po osobie).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…POL-on

Odkryte usługi POL-on filtrowalne po osobie (wszystkie CORS otwarty,
client-side): polon/projects (po kierowniku, weryfikacja projectManagers[].ORCID),
polon/products (patenty, po wynalazcy, inventors[].persons[].relatedOrcid),
polon/artisticAchievements (po autorze, authors[].orcid, + nagrody).
Publikacje świadomie pominięte (są w bazie BPP).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Nowa sekcja template-only 'Osiągnięcia (RAD-on)' na podstronie autora,
widoczna dla autorów z ORCID. Dane pobierane na żywo w przeglądarce z
RAD-on OpenData przez @iplweb/radon-opendata (bundlowany esbuildem),
dopasowane po ORCID: stopnie/tytuły, projekty (z kwotami), patenty,
osiągnięcia artystyczne. Publikacje pominięte (są w bazie BPP).

- profil_autora: KLUCZ_RADON + wpis w KATALOG_SEKCJI (lewa kolumna)
- partial radon_osiagniecia.html: kontener [data-radon-osiagniecia],
  bramka po autor.orcid, podpis 'informacje pobrane z RAD-on'
- radon-profil.js: render przez textContent (anty-XSS), degradacja
  per-kontener bez wyjątku; wpięty w bundle-entry.js
- testy: 3 pytest (katalog + render kontenera) + 5 vitest (render/wire)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
mpasternak and others added 5 commits July 8, 2026 08:52
feat(profil-autora): sekcja osiągnięć z RAD-on OpenData (client-side, po ORCID)
Konflikty (6 plikow) + reczne przeniesienie zmian dev-a do szablonow sekcji:

- src/bpp/admin/autor.py: konflikt importow, oba boki.
- src/bpp/admin/uczelnia.py: import `Wydzial` PORZUCONY (dev usunal
  WydzialInline z tego pliku, wiec zostalby martwy import); metody
  clean_uklad_profilu_autora + clean_ror_id zachowane obie.
- src/bpp/views/browse.py: dev (#438) przebudowal BuildSearch.post na
  bezwarunkowe wydzialy_box + jednostki_nadrzedne_box; wstrzyknieto w to
  charakter_box z galezi (feature galezi, nieobecny w dev).
- src/nowe_raporty/views.py: preselekcja ?obiekt=<pk> z galezi zachowana,
  ale lookup przepiety z cfg.model.objects na cfg.obiekt_queryset() —
  zgodnie z zawezeniem poziomu wprowadzonym w #438 (inaczej pk z URL-a
  obchodzilby ograniczenie, tak jak get_object przed #438).
- src/django_bpp/settings/base.py: CELERY_BEAT_SCHEDULE, oba boki.
- src/bpp/templates/browse/autor.html: przyjeto uklad 2-kolumnowy z
  galezi (stary inline-body zostal rozbity na browse/autor_sekcje/*).

Zmiany dev-a z autor.html przeniesione RECZNIE do szablonow sekcji
(git scalil je bezkonfliktowo, bo pliki sekcji sa nowe):

- autor_sekcje/metryki.html: {% with metryki=autor.metryki.all %} +
  metryki|length zamiast autor.metryki.count (N+1, #632).
- autor_sekcje/cytowania.html: {% with liczba_cytowan=... %} (#632).
- autor_sekcje/wyszukiwarka.html: {% with jednostki_autora=... %} (#632)
  oraz USUNIETY {% csrf_token %} (#650).
- autor_sekcje/statystyki_charakter.html: USUNIETY {% csrf_token %}.

Oba csrf_token sa istotne: cache_publiczny ma bezpiecznik _ZAWIERA_CSRF,
ktory wylacza cache gdy w HTML-u jest token. Pozostawienie ich cofneloby
po cichu cache'owanie strony autora wprowadzone w #650.
…486)

Pusta migracja-zlaczenie: galaz wnosi lisc 0454_merge_20260706_0757,
dev 0486_seed_instytucje_finansujace. Bez tego Django odmawia startu
('Conflicting migrations detected; multiple leaf nodes').
Konflikt package.json: zostaje @iplweb/radon-opendata, basic-ftp 6.2.1
z dev. Migracja 0491 scala liście grafu (0487 profil + 0490 dev).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…portal uczelni

- Nowa sekcja prawej kolumny „Podsumowanie dorobku": liczba publikacji
  (z zakresem lat), łączny Impact Factor, łączne punkty MNiSW. Domyślnie
  na górze kolumny; rozwiaz_uklad wstawia nowe sekcje na ich miejsce
  z katalogu zamiast doklejać na koniec zapisanych układów.
- Autor: wos_researcherid, scopus_author_id, google_scholar_id (walidacja
  formatu, URL budowany z ID) + url_profilu_uczelni. Wklejony adres
  profilu jest normalizowany do identyfikatora (admin i „Mój profil").
- Uczelnia.etykieta_profilu_uczelni — etykieta linku (np. „PPM UML").
- Migracja 0492, dokumentacja, newsfragment, testy.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@mpasternak

Copy link
Copy Markdown
Member Author

Odświeżenie do dev + podsumowanie dorobku i linki do profili

Merge dev → branch (956ece2): konflikt tylko w package.json (zostaje @iplweb/radon-opendata, basic-ftp 6.2.1 z dev); migracja 0491_merge scala liście grafu.

Nowe (931acb8):

  • Sekcja „Podsumowanie dorobku” (prawa kolumna, domyślnie na górze): liczba publikacji (z zakresem lat), łączny IF, łączne punkty MNiSW. rozwiaz_uklad wstawia nowe sekcje na ich miejsce z katalogu, więc uczelnie z zapisanym układem też dostaną podsumowanie na górze, a nie na dole.
  • Autor: wos_researcherid, scopus_author_id, google_scholar_id (walidacja formatu, link budowany z ID) + url_profilu_uczelni. Wklejony cały adres profilu jest zamieniany na ID — w adminie (fieldset „Profile w serwisach zewnętrznych”) i w self-service „Mój profil → Edytuj”.
  • Uczelnia: etykieta_profilu_uczelni (np. „PPM UML”), w sekcji „Profil autora (podstrona)”.
  • Migracja 0492, dokumentacja (użytkownik/redaktor/administrator), newsfragment.

Testy: test_profil + test_admin — 853 passed lokalnie; pre-commit czysty.

⚠️ Przy scalaniu nadal: make baseline-update (migracje 0444–0492).

🤖 Generated with Claude Code

@mpasternak mpasternak changed the title feat(profil-autora): Faza 1 — konfigurowalne sekcje podstrony autora + pola profilu feat(profil-autora): konfigurowalny profil autora — sekcje, samodzielna edycja, eksport BibTeX/RIS, RAD-on Oct 24, 2026
mpasternak and others added 2 commits October 24, 2026 11:22
…katorów

Strona autora:
- układ w pasy (sekcja na całą szerokość albo para „½”) zamiast dwóch
  kolumn; puste sekcje odpadają przed parowaniem, samotna połówka zajmuje
  cały pas, a elastyczne wykresy dobierają się do niej (jeden pod drugim,
  niższe); tag pasy_profilu renderuje sekcje i paruje niepuste
- grupy kart: wizytówka (zdjęcie + biogram), afiliacja (jednostka,
  historia, dyscypliny, stopnie, identyfikatory), wykresy w latach
- wyszukiwarka pod sekcjami, panele 2×2; jednostki w pełnym wierszu
- kafle identyfikatorów: klik w kafel kopiuje, klik w link przenosi;
  jeden kafel PBN zamiast „PBN UID” + „Naukowiec z POL-on”, bez
  legacy PBN ID; system kadrowy tylko dla zalogowanych
- listy prac domyślnie po 5 pozycji; „Podsumowanie dorobku w bazie danych”
- przycisk metryk tylko dla uprawnionych (jak MetrykaDetailView)
- kolory linków/przycisków z motywu zamiast sztywnego #3498db; wyłączony
  globalny hover przycisków dla linków statystyk; wyśrodkowane kafle

Eksport BibTeX/RIS:
- GET pokazuje stronę z przyciskiem, zadanie startuje POST-em; anonim
  przechodzi ALTCHA, „Generuj” widoczny dopiero po weryfikacji
- synchroniczne eksport.bib/.ris tylko dla zalogowanych; linki nofollow

Admin:
- edytor układu: jedna lista z wyborem szerokości (pół / cała)
- migracja 0494: Autor.opis → adnotacje, usunięcie opis/pokazuj_opis
  oraz martwego Uczelnia.szerokosc_lewej_kolumny

Dokumentacja: nowy rozdział „Mój profil autora”, instrukcja redaktora
(pola profilu w rekordzie autora, powiązanie konta z autorem), opis
strony autora i edytora układu.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@mpasternak
mpasternak merged commit 45f1f57 into dev Oct 24, 2026
24 checks passed
@mpasternak
mpasternak deleted the feature/profil-autora branch October 24, 2026 09:44
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.

2 participants