Skip to content

feat(importer): ranked autor candidates z preselekcją najlepszego#240

Open
mpasternak wants to merge 6 commits into
devfrom
feat/autor-kandydaci
Open

feat(importer): ranked autor candidates z preselekcją najlepszego#240
mpasternak wants to merge 6 commits into
devfrom
feat/autor-kandydaci

Conversation

@mpasternak
Copy link
Copy Markdown
Member

Summary

  • Nowy framework discovery dopasowania autorów (znajdz_kandydatow_autora) zwracający posortowaną listę kandydatów z metadanymi (pewność, powód, liczba publikacji) zamiast pojedynczego Autor | None
  • Wizard CrossRef pokazuje teraz rozwijaną listę alternatyw w przypadku ambiguity (np. Lech-Marańda Ewa vs Lech-Maranda Ewa dla wsadu CrossRef-owego Eva Lech-Maranda) z preselekcją najlepszego (więcej publikacji, ORCID, tytuł)
  • matchuj_autora przepisany jako thin wrapper nad nowym API — disambiguatory (jednostka, tytuł) działają jak tie-breakery, nie jak hard filtry — pełne BC dla wszystkich istniejących callerów (PBN, polon, dyscypliny, crossref)

Architektura

znajdz_kandydatow_autora(imiona, nazwisko, *, max_wyniki=10) -> list[KandydatAutora]
  ├─ strategia 1.00 — iexact pełne imię + nazwisko
  ├─ strategia 0.95 — iexact pierwsze imię + nazwisko
  └─ strategia 0.85 — PL↔EN (v↔w + klastry + Unaccent)
  dedup po pk (najwyższa pewność wygrywa)
  sort DESC: (pewnosc, ORCID, tytuł, publikacji, pk)

Brak strategii "tylko nazwisko" — musi się zgadzać przynajmniej imię w jakiejś formie, żeby nie zwracać listy 100 Kowalskich z różnymi imionami.

Wizard _auto_match_authors: WYMAGA_INGERENCJI z sugerowanym → preselect matched_autor, zapis do ImportedAuthor_Candidate (M2M z metadanymi). UI pokazuje <details> z listą alternatyw.

Reprodukcja zgłoszenia użytkownika

CrossRef DOI 10.1016/j.cyto.2013.08.002 zawiera autora {given: "Eva", family: "Lech-Maranda"}. W bazie są 2 autorów (z diakrytykiem i bez). Wcześniej: status UNMATCHED. Teraz: AUTO_LOOSE z preselectowanym tym z ORCID/większą liczbą publikacji + dropdown z drugim do ręcznej zmiany.

Test plan

  • 15 testów znajdz_kandydatow_autora (strategie, ranking, deduplikacja, case Lech-Maranda)
  • 4 testy _auto_match_authors (zapis kandydatów do M2M, render template'a)
  • 2 testy BC (jednostka jako disambiguator, tytuł jako disambiguator) — regresje znalezione przez self-review
  • 614/614 testów zielone w import_common, importer_publikacji, crossref_bpp
  • Pełna regresja na callerach matchuj_autora (PBN, polon, dyscypliny, ewaluacja2021) — pre-existing failure w celery mock test niezwiązany
  • Smoke test ręczny w wizardzie z DOI z zgłoszenia po deployu

Decyzje projektowe (potwierdzone przed implementacją)

  1. Próg pewności 0.85 dla PL↔EN — OK
  2. Strategia "tylko nazwisko" — odrzucona przez usera (musi się zgadzać imię)
  3. Pełna tabela M2M ImportedAuthor_Candidate zamiast JSONField — wybrana dla explainability w admin
  4. Liczenie publikacji ad-hoc — 3 zagregowane queries per call (skalowanie liniowe z typami tabel, nie z liczbą kandydatów)

🤖 Generated with Claude Code

mpasternak and others added 6 commits May 21, 2026 14:17
Wizard importera publikacji (CrossRef) ma case zglaszany przez usera:
w bazie istnieje 2 autorow o tym samym nazwisku (Lech-Marańda Ewa /
Lech-Maranda Ewa), CrossRef daje "Eva Lech-Maranda". Stary kod
zwracal None (ambiguity) → user wpadal na unmatched, mial probowac
manualnie.

Nowy framework discovery (znajdz_kandydatow_autora) zwraca POSORTOWANA
liste kandydatow:

- 1.00 — exact iexact pelne imiona + nazwisko
- 0.95 — exact iexact pierwsze imie + nazwisko
- 0.85 — PL↔EN (warianty v↔w + klastry imion + Unaccent nazwiska)

Brak strategii "tylko nazwisko" — musi sie zgadzac przynajmniej imie
w jakiejs formie, zeby nie zwracac 100 Kowalskich. Sortowanie DESC po
(pewnosc, ORCID, tytul, liczba publikacji, pk).

Komparator.porownaj_author zwraca WynikPorownania z .sugerowany +
.kandydaci. _auto_match_authors zapisuje liste do nowej tabeli M2M
ImportedAuthor_Candidate z metadanymi (pewnosc, powod, publikacji).

UI wizardu: rozwijana lista alternatyw z badge'ami przy "Autor w BPP"
gdy >1 kandydatów. Klikniecie alternatywy POSTuje author-match z
innym pk → row sie odswieza.

matchuj_autora przepisany jako thin wrapper z disambiguatorami
(jednostka / tytul_str jako tie-breakery, NIE hard filtry) i
fallbackami do historycznej jednostki / ORCID-tytulu. BC zachowane
dla wszystkich istniejacych call-sites (PBN, polon, dyscypliny,
crossref).

Liczenie publikacji per kandydat: 3 zagregowane queries zamiast 30
per-instance count() (skalowanie z liczba kandydatow * 3 tabele).

Testy: 614 passed (15 nowych dla znajdz_kandydatow_autora + 4 dla
_auto_match_authors + 2 BC regresje znalezione przez self-review).

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

Trzy zmiany UX zglaszane przez usera po pierwszej iteracji:

1. Modal "Edycja autora" mial wymuszone height:70vh — przy malej
   zawartosci (3 pola formularza) byl wieksy niz potrzeba. Zmiana
   na height:auto + max-height:90vh + top:5vh (Foundation Reveal
   ma domyslnie bottom:0 ktore tez trzeba zresetowac do auto).

2. Klik w caly wiersz wczesniej tez otwieral modal. Konfliktowal z
   innymi klikalnymi elementami w wierszu — dropdown kandydatow,
   formy hx-post do przepiecia autora, przycisk ORCID. Wylaczono
   row-handler, modal otwiera tylko ".btn-edit-author".

3. Kolumny "Autor w BPP" + "Jednostka" zlaczone w jedna ("Autor /
   jednostka"). Po kliku na kandydata w dropdownie zmiana jednostki
   jest natychmiast widoczna w tej samej komorce. Format kazdego
   kandydata w dropdownie: "Autor · jednostka" zamiast samego autora
   — od razu widac "skad on jest", co pomaga przy disambiguacji
   homonimow z roznych wydzialow.

Prefetch select_related rozszerzony o autor__aktualna_jednostka
zeby nie zrobic N+1 przy renderowaniu kandydatow.

Playwright test zaktualizowany — klika ".btn-edit-author" zamiast
calego wiersza.

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

Tekst byl ledwo czytelny — Foundation <small> daje 80% rozmiaru
bazowego font-size, plus w komorce tabeli juz mamy default size
mniejszy niz body. Wynik: trudne do przeczytania.

Zachowane <small> z poprzednich iteracji UI (kolumna ORCID, kolumna
dyscyplina) — to nie jest zmiana mojego feature, zostawiam jak bylo.

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

Faza A (quick wins, bez przebudowy):

- Tabela skondensowana z 8 do 6 kolumn: ORCID dostawcy pod nazwiskiem
  w "Autor (dane dostawcy)", "Źródło dyscypliny" wciągnięte do komorki
  dyscypliny jako "źródło: ...". "Autor w BPP / jednostka" dostaje
  width:40% — najszersza, bo niesie najwiecej informacji.
- Link "↗" do /admin/bpp/autor/<id>/change/ obok wybranego autora
  ORAZ obok kazdego kandydata w dropdownie — user moze zweryfikowac
  ID, jednostke, historie publikacji.
- Affordance dla "kliknij kandydata, aby zmienic wybor":
  - chevron "›" przed nazwiskiem kandydata
  - label "Kliknij innego kandydata, aby zmienic wybor" nad lista
  - hover background-color zmienia sie na #f0f0f0
  - wybrany kandydat ma stale tlo #e8f4ff (badge "wybrany")
- Wiersz tabeli zachowuje jasnoszare tlo (#fafafa) gdy <details>
  kandydatow jest rozwiniety — handler 'toggle' togluje klase
  .row-details-open.
- Select2 w modalu: dropdownParent: $('body') zamiast $modal
  (modal ma overflow:auto ktore wycinalo dropdown).

Faza B (in-place edycja autor/jednostka/dyscyplina):

- Nowy URL author-edit-form (GET): renderuje wiersz w trybie edit
  z 3 select2 inline + Save/Cancel.
- Nowy URL author-row (GET): renderuje normalny wiersz — uzywany
  jako cancel z trybu edit.
- Klik na maly button "olowek" w komorce "Autor w BPP / jednostka"
  → hx-get edit-form → swap calego wiersza z edit-template.
- Submit (Save ✓) → POST author-match (juz istnieje) → swap z
  normalnym wierszem przez ten sam mechanizm.
- Cancel (✗) → GET author-row → swap z normalnym wierszem (bez zmian).
- htmx:afterSwap event reinicjuje select2 w nowym wierszu
  (autocomplete dla autor/jednostka, statyczne dyscypliny dla autora
  + roku publikacji).
- Modal "Edytuj (modal)" zostaje jako fallback dla edge case "wymus
  autora calkowicie spoza kandydatow" — przeszedl rename z "Edytuj".

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

User feedback: poprzednia iteracja UI (skondensowane kolumny + inline
edit przez pencil + dropdown kandydatow w wierszu) byla GORSZA niz
wczesniej. Cofamy to wszystko i przenosimy wybor autora do modala.

Tabela (revert do 8 kolumn):

- # | Autor (dane dostawcy) | ORCID | Dopasowanie | Autor w BPP |
  Jednostka | Dyscyplina | Zrodlo dyscypliny | Akcje
- Inline pencil button usuniety — modal jest jedyna sciezka edycji.
- W kolumnie "Dopasowanie" pod statusem pojawia sie badge
  "X kandydatow" gdy znajdz_kandydatow_autora znalazl wielu — link
  wzrokowy ze user powinien otworzyc modal.
- Dropdown kandydatow w wierszu USUNIETY (partial author_candidates).

Modal (rozbudowany):

- Nowy partial modal_candidates.html — lista klikalnych kandydatow
  z badge'ami (pewnosc, powod strategii, liczba publikacji, ORCID,
  PBN). Wybrany podswietlony (modal-candidate-selected).
- Sekcja kandydatow ladowana AJAX-em przy otwarciu modala
  (AuthorCandidatesModalView GET zwraca HTML partial).
- Pod selectem autora linki do:
  - admin BPP (/admin/bpp/autor/<pk>/change/)
  - publiczny widok BPP (/bpp/autor/<slug>/ — pokazuje prace autora)
  - ORCID (https://orcid.org/<orcid>) — jesli ma
  - PBN (https://pbn.nauka.gov.pl/core/#/person/view/<pbn_uid_id>)
    — jesli ma pbn_uid_id
- AuthorInfoView (GET) zwraca JSON {pk, slug, display, orcid,
  pbn_uid_id} — JS odpala go po zmianie autora w select2 i
  aktualizuje linki dynamicznie.
- Klik na kandydata: przepisuje select2 autora + jednostki,
  odswieza linki + dyscypliny, podswietla wybrany.

Usuniete (po revertcie):
- URL/View AuthorEditFormView, AuthorRowView
- Template author_row_edit.html
- Template author_candidates.html (dropdown w wierszu)
- Inline edit CSS/JS (grid select2 + Save/Cancel + reinit po swap)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ORCID w tabeli

User feedback po pierwszej iteracji:

1. Caly wiersz tabeli znow klikalny -> otwiera modal. Wzorzec
   wraca, ale teraz btn-edit-author w wierszu zachowuje stopPropagation
   (zeby nie open dwa razy gdyby user kliknal precyzyjnie w przycisk).

2. Wciecia + bold w modalu. Lista kandydatow ma padding-left:1rem,
   sekcja linkow autora rowniez. Pola formularza (label > "Autor w BPP",
   "Jednostka", "Dyscyplina") maja font-weight:bold + padding-left:1rem.

3. User-friendly etykiety:
   - "admin BPP" -> "otworz w module redagowania"
   - "strona w BPP (prace)" -> "otworz strone autora"
   - "PBN" -> "strona w PBN"
   - "ORCID" -> "strona ORCID"
   - "iexact" / "iexact_pierwsze_imie" / "polish_english" -> dokladne /
     pierwsze imie / wariant PL/EN (przez powod_display property na
     modelu ImportedAuthor_Candidate)
   - "0.85" / "1.00" -> "85%" / "100%" (przez pewnosc_procent property)

4. Pod nazwiskiem matched_autor w kolumnie "Autor w BPP" pokazujemy
   ORCID jesli autor go ma. Plus badge "X kandydatow" przenosi sie z
   kolumny "Dopasowanie" tutaj — kolumna "Dopasowanie" zostaje czysta
   (tylko status), zeby DataTables filter mogl agregowac po
   unikalnych wartosciach (4 zamiast 4 * liczba kandydatow).

5. Auto-okreslanie dyscypliny w modalu: gdy dla wybranego autora +
   roku publikacji autocomplete zwraca DOKLADNIE jedna dyscypline a
   user nie ma jeszcze nic wybranego, JS auto-selectuje ja. Backend
   mial juz ten mechanizm (ostatnia_dyscyplina w AuthorMatchView).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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