Skip to content

Deduplikator autorów: tryb ogólny + UI overhaul#192

Merged
mpasternak merged 31 commits into
devfrom
feature/deduplikator-autorow-general
May 3, 2026
Merged

Deduplikator autorów: tryb ogólny + UI overhaul#192
mpasternak merged 31 commits into
devfrom
feature/deduplikator-autorow-general

Conversation

@mpasternak
Copy link
Copy Markdown
Member

Summary

  • Nowy tryb "ogólny" — znajduje duplikaty wśród autorów spoza listy PBN; przycisk "Skanuj duplikaty" uruchamia dwie fazy (PBN + ogólny) sekwencyjnie; status PARTIAL_COMPLETED gdy faza ogólna zostanie anulowana
  • UI overhaul — uproszczone menu (bez BETA), badge trybu, button-group filtra, kolorowe chipy powodów (Foundation icons), klamp pewności do 0–100%, AJAX dla "Nie jest duplikatem", grupowanie przycisków per-kandydat (Podgląd/Decyzja/Scalanie)
  • Modele — nowy `IgnoredAuthor` (FK→Autor) dla trybu ogólnego, rename `IgnoredAuthor`→`IgnoredScientist` dla starego, pola `phase`/`scan_mode`/`PARTIAL_COMPLETED` na DuplicateScanRun + DuplicateCandidate
  • Algorytm fazy ogólnej — utils: `cluster` (union-find), `main_selection` (hierarchia), `meta` (pre-load), `analysis_meta` (scoring bez SQL), `search_general` (generator par); pomijanie klastrów z autorami z osoba_instytucji
  • scal_autorow_view backwards-compat: akceptuje zarówno `main_autor_id` jak i `main_scientist_id` (mapowanie przez `rekord_w_bpp`); `ignore_author` rozdzielony na `ignore_scientist` + `ignore_autor`
  • Eksport XLSX zawiera kolumnę "Tryb" + ORCID
  • Logowanie — tworzenie autorów przez autocomplete logowane jako Django admin LogEntry
  • Refactor — `views.py` (1177L) rozbity na pakiet `views/` (helpers/duplicates/merge/ignore/scan/export, 7 plików)

Test plan

  • 178 testów `src/deduplikator_autorow/` przechodzi (`pytest -n auto`)
  • Smoke test ręczny: uruchomić skan PBN+ogólny, sprawdzić filtr trybu, eksport XLSX, scal_autorow z obu trybów, mark non-duplicate AJAX
  • Sprawdzić że `make assets` po pull-u (zmiany SCSS/JS w UI overhaul)
  • Migracje: `uv run python src/manage.py migrate deduplikator_autorow` na świeżej bazie (nowe pola + IgnoredAuthor model)

🤖 Generated with Claude Code

mpasternak and others added 27 commits May 1, 2026 09:33
Pierwszy krok przygotowania pod tryb general — istniejący IgnoredAuthor
był specyficzny dla PBN (FK→Scientist) i zwalniamy nazwę pod nowy model
ignorujący autorów BPP w trybie ogólnym.
…neral

Dodaje in-memory generator par kandydatów oparty o buckety nazwisk:
iteruje po bucketach (skipuje > BUCKET_MAX_SIZE=200 z warningiem),
generuje pary nieuporządkowane (pk_a < pk_b), deduplikuje symetryczne
(autor może trafić do wielu bucketów przez compound nazwisko / reverse),
filtruje przez ignored_pks/notadup_pks i emituje tylko pary
score >= MIN_CONFIDENCE_TO_STORE (50).

Rozszerza analiza_pary_meta o detekcję compound nazwisk po
nazwisko_parts: pełna permutacja członów (np. 'Gal-Cisoń' ↔
'Cisoń-Gal') daje +35, częściowe pokrycie +20 — bez tego sygnał
z bucketu reverse-compound nie miał szans przekroczyć progu.
…eral

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

Wyodrębnia istniejące ciało scan_for_duplicates do _run_pbn_phase
i przepina task na orkiestrację dwóch faz (PBN → general) w jednym
DuplicateScanRun. Cancellation w fazie PBN daje status CANCELLED
(bez wyników general), w fazie general daje PARTIAL_COMPLETED
(wyniki PBN zachowane). Pole `phase` ustawiane na 'pbn'/'general'
w trakcie pracy. Zachowane behaviour PBN: replace mode, polling
scan_run.status między autorami, periodyczny update progress.

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

- scal_autorow_view: accept main_autor_id/duplicate_autor_id (preferred) +
  legacy main_scientist_id/duplicate_scientist_id mapped via Scientist.rekord_w_bpp;
  view now calls scal_autora directly with Autor objects (the scal_autorow wrapper
  in utils/merge.py stays untouched for any external callers).
- Split ignore endpoint: ignore_author -> ignore_scientist (writes IgnoredScientist),
  add new ignore_autor (writes IgnoredAuthor with FK->Autor). Reset endpoints
  renamed accordingly: reset_ignored_authors -> reset_ignored_scientists, plus
  new reset_ignored_autorzy. URL names + paths updated; template references
  adjusted in duplicate_authors.html.

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

- views.scan_status_view: dodaj PARTIAL_COMPLETED do listy "finished",
  żeby frontend JS poll przestał odpytywać po przejściu skanu w ten stan.
- tasks._run_general_phase: użyj nowego helpera
  _calculate_priority_from_meta zamiast calculate_author_priority
  (2 SQL per candidate). Helper czyta z meta-cache; przybliżenie
  ma_dyscypline (any year vs 2022-2025) udokumentowane jako TODO,
  bo priority to sort hint, nie correctness invariant.
- utils.meta.build_autor_meta: dorzuć poprzednie_nazwiska,
  pokazuj_poprzednie_nazwiska, pseudonim do .only(), bo Autor.__str__
  je czyta — bez tego str(autor) w hot-path emituje 2 deferred-load
  SQL per autor.
- utils.export.export_duplicates_to_xlsx: materializuj queryset raz
  (list(...)), żeby Counter i list-comprehension nie iterowały dwa
  razy (każda iteracja = pełny SQL).
- views._get_next_candidate_group: zamień .values_list().distinct() na
  iterację z dedupe w Pythonie. PostgreSQL DISTINCT + ORDER BY z
  annotacją to tricky semantics — Django może odrzucić annotation
  z SELECT, dając runtime error lub niedeterministyczną kolejność.
- testy: scan_status_view dla PARTIAL_COMPLETED, regresja
  per-candidate SQL w _run_general_phase.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…igacja wyszukiwania, make assets

- Publikacje: nadpisz Foundation .callout a font-weight:bolder na normal
  (tytuł w <b> pozostaje bold, reszta normalna)
- Top bar: justify-content flex-start, szukajka zaraz po przyciskach trybu
- Możliwe duplikaty: nagłówek w osobnym wierszu, przyciski pewności poniżej;
  cały rząd ukryty gdy tylko jedna kategoria kandydatów
- Wyszukiwanie: dodana nawigacja Poprzedni/Następny po wynikach szukania
  (skip_count + search_has_prev/search_has_next)
- Makefile: stamp file zamiast per-CSS-target, SCSS wildcard obejmuje
  src/*/static/*/scss/*.scss, stamp usuwany w make clean
…in LogEntry

When a new author is created through the autocomplete 'create' dialog,
a LogEntry (ADDITION) is now recorded in Django admin history with
change_message='Utworzono z formularza autocomplete', making it possible
to trace who created the author and from where.
…ublikacje, nie-duplikaty

- Napraw ładowanie CSS: block extra_css nie istniał w hierarchii template'ów
  (bare.html->base.html), CSS nigdy się nie ładował. Zmieniono na
  {% block extrahead %} z {{ block.super }}.
- Bold publikacji: selektor .callout .deduplikator-autorow__publication-item a
  (spec. 0,2,1) wygrywa z Foundation .callout a:not(.close-button) (0,1,1)
- Lista publikacji: usunięte kolorowe border-left, obramowanie i padding —
  teraz zwykła lista tekstowa
- Top bar: usunięta etykieta 'Pokaż wyniki:', same przyciski trybu po lewej,
  wyszukiwarka po prawej (space-between)
- Przycisk 'Szukaj': border-radius: 0 po prawej gdy widoczny przycisk 'X'
  (czyszczenie wyszukiwania)
- Nie-duplikaty: licznik w sidebarze aktualizuje się po AJAX-owym oznaczeniu
  kandydata jako nie-duplikat (span#not-duplicate-count + JS increment)
- Empty state: gdy search_lastname aktywny i brak wyników — 'Nie znaleziono
  takich autorów' zamiast 'Gratulacje'
… XLSX, naprawa top_bar HTML, testy

- Hard reject kandydatów z rozłącznymi imionami (analysis_meta + analysis)
- Kolumny ORCID i PBN URL w eksporcie XLSX
- Endpoint lastname-suggestions do autouzupełniania
- reason_display.py — utils do wyświetlania powodów duplikacji
- Naprawa osieroconego <li> w top_bar.html (djlint H025)
- Zmiana etykiety 'deduplikator autorów PBN' → 'deduplikator autorów'
- Dodanie 'deduplikator autorów' do menu narzędzia
- .gitignore: .grunt-build-stamp
- CLAUDE.md: dodano reguły komentarzy Django i uv run
- Nowe testy: merge_all_refresh, reasons_display, xlsx_orcid_and_pbn_url
Konflikt: dev rozbił views.py (864L) na pakiet views/ (7 plików),
feature rozbudował views.py do 1177L (rename ignore_author →
ignore_scientist+ignore_autor, nowe helpery i widoki).

Rozstrzygnięcie: zachowano monolityczny views.py z feature, usunięto
pakiet views/ z dev. Re-split do pakietu zostanie wykonany w kolejnym
commicie z uwzględnieniem nowych funkcji feature.
…ckage

Powtórzenie splitu z dev (commit 9179092) z uwzględnieniem nowych
funkcji wprowadzonych na feature branchu:
- helpers.py: + _read_param, _scientist_id_to_autor_id, _resolve_autor_id,
  get_running_scan, _get_pending_candidates_for_main_autor,
  _get_next_candidate_group
- duplicates.py: + lastname_suggestions
- ignore.py: split ignore_author → ignore_scientist + ignore_autor;
  reset_ignored_authors → reset_ignored_scientists + reset_ignored_autorzy;
  + _trigger_rescan_after_reset
- merge.py: scal_autorow_view + delete_author (bez zmian funkcjonalnych)
- scan.py: start_scan_view, cancel_scan_view, scan_status_view
- export.py: download_duplicates_xlsx
- __init__.py: re-export wszystkich symboli (backward compat z urls.py i
  testami importującymi z deduplikator_autorow.views)

views.py usunięty.
messages.error(request, "Brak wymaganego parametru: scientist_pk")
def _respond(success, message, status=200, level="success"):
if is_ajax:
return JsonResponse({"success": success, "message": message}, status=status)
messages.error(request, "Brak wymaganego parametru: candidate_id")
def _respond(success, message, status=200):
if is_ajax:
return JsonResponse({"success": success, "message": message}, status=status)
Comment thread src/deduplikator_autorow/views/merge.py Outdated
duplicate_autor = Autor.objects.get(pk=duplicate_autor_id)
except Autor.DoesNotExist as e:
return JsonResponse(
{"success": False, "error": f"Nie znaleziono autora: {e}"},
Comment thread src/deduplikator_autorow/views/merge.py Outdated
"success": False,
"error": f"Błąd podczas scalania autorów: {str(e)}",
},
{"success": False, "error": f"Błąd podczas scalania autorów: {str(e)}"},
mpasternak and others added 2 commits May 3, 2026 21:56
… through an exception'

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
… through an exception'

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
mpasternak and others added 2 commits May 3, 2026 21:57
… through an exception'

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
… through an exception'

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
@mpasternak mpasternak merged commit e22775d into dev May 3, 2026
9 of 10 checks passed
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