Skip to content

Tabela krzyżowa rekordów i autorów na stronie „Wyszukiwanie zapytaniem" (fazy 2-4) - #686

Open
mpasternak wants to merge 26 commits into
devfrom
feat/zapytanie-tabela-krzyzowa
Open

Tabela krzyżowa rekordów i autorów na stronie „Wyszukiwanie zapytaniem" (fazy 2-4)#686
mpasternak wants to merge 26 commits into
devfrom
feat/zapytanie-tabela-krzyzowa

Conversation

@mpasternak

Copy link
Copy Markdown
Member

Fazy 2-4 planu „wachlarz wyjść dla /zapytanie/". Spiętrzone na #685 (faza 1) —
base tej gałęzi to feat/zapytanie-wyjscia, żeby diff pokazywał wyłącznie nową
pracę. Po zmergowaniu #685 do dev ten PR należy przetargetować na dev.

Co dochodzi

Tabela krzyżowa rekordów (postac=pivot) — ten sam silnik co w wyszukiwaniu
formularzowym, z eksportem macierzy do CSV/XLSX. Eksport macierzy nie podlega
capom rekordowym (25000/5000), bo rozmiar macierzy nie zależy od liczby rekordów
źródłowych; bramka PIVOT_MAX_CELLS pokazuje komunikat zamiast 500.

Tabela krzyżowa autorów — to jest odpowiedź na pytanie „nie wiem, jakie
mógłbym mieć konfiguracje tabeli krzyżowej dla autorów". Zaprojektowane są trzy
bazy agregacji
, bo to metryka decyduje o ścieżce JOIN-u:

Baza Przez co liczy Metryki
K — kadrowa sam bpp_autor liczba autorów
P — prace autorzy (widok bpp_autorzy_mat) liczba prac
U — udziały cache_punktacja_autora_query Σ slotów, Σ pkdaut

Wymiary kadrowe (jednostka, tytuł, stopień, funkcja, płeć, rok urodzenia,
kompletność ORCID / PBN UID / e-maila / ID kadrowego) są dostępne we wszystkich
bazach. Wymiary publikacyjne (rok, dyscyplina, jednostka pracy, typ
odpowiedzialności, charakter formalny) mają osobną ścieżkę ORM per baza i
znikają z selektora tam, gdzie nie mają sensu. Do tego 8 gotowych presetów
(Struktura kadrowa, Audyt kompletności ORCID, Gotowość do PBN, Produktywność
jednostek…).

Eksport kartoteki autorów do CSV/XLSX — kolumny kadrowe plus trzy metryki
dorobku (liczba prac, Σ slotów, Σ pkdaut).

Refaktor: silnik pivota wyprowadzony z bpp/multiseek_registry/pivot.py do
pakietu bpp/pivot/ (core = silnik generyczny, rekord i autor = rejestry
wymiarów). Stary moduł zostaje cienkim re-eksportem — multiseek nie zauważa
zmiany.

Czego świadomie NIE ma

Σ IF / Σ PK / Σ cytowań w pivocie autorskim. To wartości rekordowe: sumowane
per autor zwielokrotniają się między współautorami. Kto ich potrzebuje, używa
pivota rekordowego z wymiarem „jednostka" — tam strategia par liczy je poprawnie.

Dlaczego liczbom można wierzyć

Django zamienia queryset spinający dwie relacje „do wielu" w iloczyn kartezjański,
co zawyża Sum (choć nie Count(distinct=True)). Trzy zabezpieczenia, każde
udowodnione mutacyjnie, nie założone:

  • każdy wymiar bazy P startuje od autorzy__, każdy bazy U od
    cache_punktacja_autora_query__ — przestawienie jednego na cudzą relację daje
    w teście 3.0 ≠ 1.5;
  • eksport kartoteki liczy dwoma osobnymi agregatami — wariant z jednym
    annotate() daje Σ 3.0000 zamiast 1.5000;
  • dedup po PK autora — jego usunięcie daje Σ 1.5 ≠ 0.5.

Eksport i tabela krzyżowa używają identycznych ścieżek ORM, więc nie mogą
pokazać dwóch różnych liczb dla tej samej populacji.

Testy

  • -k "pivot or zapytanie or multiseek": 496 passed
  • pełna suita make tests-without-playwright: 8748 passed, 4 skipped
  • Playwright: 119 passed (nowy test przełączania postaci, 5 przebiegów bez flake'a)
  • pre-commit: czysto
  • zero zmian w src/*/migrations/ i baseline-sql/

25 testów pivota multiseeka przechodzi z niezmienionymi asercjami. Zmieniły się
w nich trzy cele monkeypatch — stała PIVOT_MAX_CELLS przeprowadziła się do
bpp.pivot.core, a shim celowo jej nie re-eksportuje: kopia nazwy pod starą
ścieżką byłaby martwa (patch przestawiłby kopię, nie bramkę), a monkeypatch
domyślnie podnosi AttributeError na nieistniejącym atrybucie, więc pomyłka
wychodzi głośno zamiast dać fałszywie zielony test.

Końcowe review

Przegląd całości (osobny agent) wyłapał 1 Critical i 4 Important — wszystkie
naprawione w 2a59fea2d. Krytyczny: górny pasek eksportu przy postac=pivot gubił
parametry pivot_row/col/val i pobierał domyślną macierz zamiast tej na
ekranie, a strona pokazywała dwa przyciski CSV dające różne pliki. Przepuściły to
testy asertujące wyłącznie obecność napisu „RAZEM" — obecnego w każdej macierzy.
Testy eksportu sprawdzają teraz etykiety żądanych wymiarów i konkretne komórki, a
osobny test przechodzi po wszystkich linkach eksportu na stronie i wymaga
macierzy zgodnej z ekranem.

🤖 Generated with Claude Code

mpasternak and others added 25 commits July 26, 2026 16:22
Eksporty (CSV/XLSX/HTML/DOCX/BibTeX) i tabela krzyżowa na stronie
/zapytanie/ w parytecie z multiseekiem, plus nowy rejestr wymiarów
tabeli krzyżowej dla modelu Autor (bazy: kadrowa / prace / udziały).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- Część IV: usunięta sprzeczność (jeden annotate vs osobne zapytania);
  metryki dorobku liczone dwoma zapytaniami, bo dwa JOIN-y do relacji
  do-wielu w jednym annotate zawyżają sumy
- postać dla model=autor to 'rekordy' + 'pivot' (nie 'lista')
- doprecyzowany routing eksportu przy postac=pivot i sanityzacja ?tytul=

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Cztery fazy: wspólny silnik wyjścia + eksporty rekordów, pivot rekordów,
pakiet bpp/pivot/ + rejestr autorski (baza kadrowa), metryki
bibliometryczne + eksport autorów.

Self-review planu wykrył trzy blokery reużycia report-body-pivot.html
(formularz GET gubiący model/query, relatywne linki eksportu, selektory
nieświadome baz agregacji) — wszystkie rozpisane jako konkretne edycje.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Pre-flight scan planu: testy dotykające Rekordu wymagają fixture denorms
i denorms.flush() (Rekord to zdenormalizowany cache), fixture 'tytul' nie
istnieje (jest 'tytuly'), a dwie asercje były zbyt słabe (dowolne '2'
w wierszu CSV; brak '5000' w komunikacie) — zamienione na parsowanie CSV
i sprawdzenie treści komunikatu.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Task 2 wykrył, że plan brał drugi argument ReportType('list', 'lista') —
etykietę UI — za wartość report_type. postac=tabela nigdy nie trafiłaby
w TABLE_REPORT_TYPES, więc tabela renderowałaby się jako lista, bez błędu.
Poprawione w planie (zadania 2-6, 12) i w specyfikacji.

Druga poprawka: nh3 w sanitize_export_html zdejmuje atrybuty class, więc
asercje testów eksportu HTML/DOCX nie mogą opierać się na klasach CSS —
tylko render na stronie je zachowuje.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Review Task 3: postać 'tabela krzyżowa' była wybieralna, a renderowała
placeholder listy (dla autorów: puste wiersze, bo Autor nie ma
js_safe_pk ani opisu bibliograficznego). Zadanie 3 usuwa ją z list
postaci, zadania 6 i 9 dopisują ją z powrotem wraz z implementacją.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…a bibtex, ikona fi-page-edit

- POSTACIE_REKORD/POSTACIE_AUTOR: wyrzucone "pivot" (bez dedykowanego
  partiala renderowal sie jako zepsuta lista rekordow/autorow).
- postac="bibtex" renderuje teraz wlasny partial multiseeka
  (report-body-bibtex.html) zamiast placeholder-owej listy opisow.
- ikona edycji ✎ -> fi-page-edit (Foundation Icons, zgodnie z konwencja
  repo dla frontendu publicznego) w obu partialach (list/table).
- test_postac_tabela_ma_sumy: asercja na realnej wartosci sumy (dwa
  rekordy, 40+10=50,00), nie tylko na obecnosci kontenera.
- nowe testy regresyjne: pivot niewybieralny/degradujacy, bibtex uzywa
  wlasnego partiala, pager zachowuje postac miedzy stronami, stopka
  "Suma:" tylko na ostatniej stronie tabeli.
…ekiem

Przycisk "Skopiuj wszystko do schowka" w report-body-bibtex.html ma
handler JS tylko inline w multiseekowym common-results.html — na
/zapytanie/ (hide_chrome=True) byl martwy (klik bez efektu). Ten sam
wzorzec co widget usuwania (❌): {% if not hide_chrome %} wokol
.bibtex-actions. Multiseek nie przekazuje hide_chrome, wiec jego
przycisk dziala jak dotad; /zapytanie/ dostaje sam <pre> z BibTeX-em
(w pelni zaznaczalny/kopiowalny recznie).
Zadanie 5 celowo zwróciło pustą krotkę formatów dla model=autor, bo
backend 400-ował każdy format i pasek pokazywałby martwe linki. Po
wdrożeniu eksportu autorów gałąź ma wrócić do (csv, xlsx).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…eksporty, komunikat nieznanego modelu

Test uprawnien testuje teraz sztywno 403 (raise_exception=True gwarantuje
to niezaleznie od stanu logowania — potwierdzone w kodzie mixinow Django).
Usuniete szesc martwych re-eksportow z mymultiseek.py (niepotwierdzone
grepem uzycie poza tym plikiem). Rozdzielony komunikat 400 dla eksportu
autorow i dla realnie nieznanego modelu. Dwa testy degradacji postaci dla
autora dostaly pozytywna asercje na "rekord-id-cell" zamiast samego
"not in".

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Postać "tabela krzyżowa" (postac=pivot) wraca do selektora dla modelu
rekord — zadanie 3 ją usunęło, bo bez implementacji renderowała placeholder
listy. Silnik pivota (parse_pivot_params/zbuduj_pivot/DIMENSIONS/METRICS)
jest brany z multiseeka bez zmian; widok dokłada tylko kontekst i trzy
klucze, których partial potrzebuje pod tym URL-em: form action, ukryte pola
model/query/postac (strona zapytania trzyma filtr w URL-u, nie w sesji jak
multiseek) i prefiks linków eksportu. Eksport macierzy do CSV/XLSX omija
capy rekordowe 25000/5000 — rozmiar macierzy nie zależy od liczby rekordów
źródłowych. Bramka PIVOT_MAX_CELLS pokazuje komunikat na ekranie zamiast 500.

Przy okazji naprawione cztery fałszywie zielone asercje testowe: sprawdzały
obecność gołej nazwy klasy "rekord-id-cell", która występuje na KAŻDEJ
stronie, bo zapytanie.html definiuje dla niej regułę CSS w bloku <style>.
Test "pivot dla rekordu degraduje" przechodził więc nadal, mimo że pivot
dostał już własny render. Asercje celują teraz w '<td class="rekord-id-cell">',
a strażnik wybieralności pivota został odwrócony na model autor, który
własnego rejestru wymiarów jeszcze nie ma.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Silnik tabeli krzyżowej przestał być multiseek-specyficzny w momencie, gdy
dostała ją też strona „Wyszukiwanie zapytaniem", a zadania 8-10 dokładają
drugi rejestr wymiarów (dla modelu Autor, z trzema bazami agregacji). Kod
rozdzielony na generyczny silnik (bpp/pivot/core.py) i rejestr wymiarów
Rekordu (bpp/pivot/rekord.py); multiseek_registry/pivot.py zostaje cienkim
re-eksportem, żeby nie ruszać call-site'ów w szablonach i widokach.

Jedyna zmiana sygnatury: zbuduj_pivot(..., *, model=None) — strategia dedupu
potrzebuje modelu bazowego, dotąd zaszytego jako Rekord. Domyślny None
zachowuje wszystkie istniejące wywołania; zadanie 8 poda tu Autora.

Progi PIVOT_MAX_CELLS/PIVOT_MAX_PAIRS świadomie NIE są re-eksportowane przez
shim: zbuduj_pivot czyta je jako globalne swojego modułu, więc kopia nazwy
pod starą ścieżką byłaby martwa — monkeypatch na niej przestawiłby kopię, nie
bramkę. Trzy testy patchujące progi celują teraz w bpp.pivot.core, gdzie te
stałe mieszkają. Brak nazwy w shimie jest tu cechą, nie luką: monkeypatch
domyślnie podnosi AttributeError na nieistniejącym atrybucie, więc pomyłka
wyjdzie głośno w linii patcha, zamiast dać fałszywie zielony test.

Neutralność: 25 testów pivota multiseeka przechodzi bez zmian w asercjach
(zmienione tylko trzy cele monkeypatcha — patch site idzie za przeprowadzką
stałej dokładnie tak jak import site). Cała suita pivot/zapytanie/multiseek:
458 passed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Drugi rejestr silnika pivota (bpp/pivot/autor.py) obok rejestru rekordowego
z zadania 7 — model Autor, na razie tylko baza K (kadrowa, metryka „liczba
autorów"). Bazy P (liczba prac) i U (Σ slotów, Σ pkdaut) to zadanie 10; seam
dla nich jest gotowy (PivotDimension.expr jako dict per baza, PivotMetric.baza),
więc dołożenie ich nie wymaga przeprojektowania.

core.py: PivotDimension zyskuje annotation (adnotacja ORM dokładana przed
grupowaniem — potrzebna dla wymiarów bool typu „ma ORCID", gdzie grupowanie po
surowym polu dałoby grupę na wartość) oraz expr_dla()/alias() do odczytu
ścieżki per baza; PivotMetric zyskuje baza i distinct_field. Rejestr rekordowy
nie zauważa zmiany — przekazuje expr jako zwykły str; 25 testów bez zmian.

Poprawki względem brief-u: fixture „tytul" w tym repo nie istnieje (jest tylko
„tytuly") — Tytul tworzony inline; test odrzucania wymiaru niedostępnego w
bazie był wsobny przy jednej bazie K (klucz „rok" nie istnieje w DIMENSIONS
autorskich, więc fallback zadziałałby i bez logiki expr_dla) — przemianowany na
to, co faktycznie sprawdza; asercja liczby autorów zastąpiona pełnym mapowaniem
etykieta→suma ze stałą BRAK zamiast wklejonego literału.

Nośność dedupu zmierzona, nie założona: po usunięciu dedupu w
zbuduj_pivot_autora wszystkie 7 testów nadal przechodzi, bo Count(pk,
distinct=True) sam zwija wiersze zwielokrotnione JOIN-em. Dedup zostaje
(poprawny, tani, jednakowy dla baz), ale test przestał twierdzić, że go
dowodzi, a wymóg testu padającego bez dedupu — możliwego dopiero dla metryk Σ,
gdzie Sum nie rozpozna duplikatu — jest dopisany do zadania 10 w planie.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Rejestr pivota autorskiego (bpp.pivot.autor, baza kadrowa K) zostaje
wpięty w /zapytanie/ i eksport: postac=pivot jest teraz wybieralna dla
obu modeli, ZapytanieView._pivot_context i ZapytanieExportView
rozgałęziają się po modelu przez wspólny helper
bpp.pivot.wybierz_rejestr_pivota, eksport macierzy autorskiej (CSV/XLSX)
naprawdę działa i ma link w pasku, a sekcja pomocy dostaje cztery
klikalne presety (struktura kadrowa, ORCID, PBN UID, płeć wg tytułów).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…Σ pkdaut)

Bazy P (prace, przez `autorzy` → Rekord) i U (udziały, przez
`cache_punktacja_autora_query` → Rekord) w rejestrze pivota autorskiego,
razem z wymiarami publikacyjnymi o `expr` per baza: rok, dyscyplina,
jednostka przy pracy, typ odpowiedzialności, charakter formalny.

KRYTYCZNE: każdy wymiar publikacyjny idzie DOKŁADNIE tą samą relacją
do-wielu, po której agreguje metryka jego bazy (P → `autorzy__…`,
U → `cache_punktacja_autora_query__…`). Zmieszanie dwóch relacji daje
iloczyn kartezjański i `Sum` zwraca wielokrotność prawdy — zmierzone:
autor z dwiema pracami i dwoma wierszami udziału daje Σ slotów 3.0
zamiast 1.5. Pilnuje tego test niezawyżania na dokładnej liczbie.

Dedup przez świeży queryset w `zbuduj_pivot_autora`, dotąd redundantny
w bazie K (`Count(pk, distinct=True)` sam zwija duplikaty), staje się
nośny dla metryk Σ — dowodzi tego nowy test, który po zakomentowaniu
tamtej linii pada (0.5 → 1.5).

Do UI dochodzą cztery presety oparte na nowych bazach (produktywność
jednostek, ranking slotowy autorów, udziały dyscyplinowe, wkład
punktowy jednostek) plus test, że każdy preset przeżywa
`parse_pivot_params_autor` bez cichej podmiany wymiaru.

Zdjęty `xfail(strict=True)` z `test_metryka_slotowa_pokazuje_rok`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ZapytanieExportView eksportuje teraz listę autorów (nie tylko macierz
pivota) do CSV/XLSX: dane kadrowe (jednostka, tytuł, ORCID, PBN UID i
inne) plus liczba prac, Σ slotów i Σ pkdaut. _metryki_dorobku() liczy
te trzy metryki DWOMA osobnymi zapytaniami (Count przez relację
"autorzy", Sum przez "cache_punktacja_autora_query") — jeden wspólny
annotate() na dwóch różnych relacjach do-wielu zawyżałby sumy (dowód:
test_eksport_autorow_nie_zawyza_metryk). Te same ścieżki ORM, co
metryki bpp.pivot.autor.METRICS, żeby lista i tabela krzyżowa autorów
nigdy nie pokazały dwóch różnych liczb dla tej samej populacji.

eksport_formaty() (zapytanie.py) pokazuje teraz pasek CSV/XLSX dla
modelu "autor" niezależnie od postaci wyniku — wcześniej ograniczone
do postac="pivot", bo lista 400-owała.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Dopisuje test przegladarkowy dowodzacy, ze wybor postaci wyniku
(#id_postac) faktycznie jedzie przez formularz GET /zapytanie/ - domyslna
tabela redakcyjna ustepuje partialowi multiseeka po przelaczeniu na
"lista", a URL zawiera postac=list. Dodaje sekcje w dokumentacji redaktora
o postaciach wyniku, eksporcie i tabeli krzyzowej strony /zapytanie/.

Newsfragmenty na ta funkcjonalnosc juz istnieja z wczesniejszych zadan
tego planu (zapytanie-eksporty, zapytanie-tabela-krzyzowa[-autorzy],
zapytanie-pivot-autor-metryki-bibliometryczne, zapytanie-eksport-autorow)
- nie dodano duplikatu.

Pelna suita bez Playwrighta: 8748 passed, 4 skipped, 1 xfailed.
Suita Playwright: 119 passed. pre-commit: czysto.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
K1 (krytyczne) — górny pasek eksportu przy postac=pivot ściągał INNĄ macierz
niż widoczna: budował URL z samych model/query/postac, więc gubił
pivot_row/pivot_col/pivot_val. Zmierzone: link z górnego paska zwracał
"Rok,RAZEM / 2026,1,1 / RAZEM,1" (macierz domyślna), a link spod macierzy
"Charakter ogólny (rodzaj),…" — dwa przyciski CSV kilka centymetrów od
siebie, dwa różne pliki. Górny pasek nie renderuje się teraz przy pivocie:
pasek w report-body-pivot.html składa URL z request.GET.urlencode, więc jest
z konstrukcji zgodny z ekranem (także po dołożeniu kolejnego parametru
pivota) i stoi przy selektorach, które macierz konfigurują. Wariant
"dołóż trzy parametry do href-a" odrzucony — zostawiałby dwie listy
parametrów do ręcznego synchronizowania, czyli dokładnie mechanizm, który
K1 wyprodukował.

W1 — pivot autorski nigdy nie pokazywał adnotacji o dublowaniu (twarde
has_autorzy_dim=False), choć baza P dubluje tak samo jak pivot rekordowy.
Zmierzone: jedna praca, dwóch autorów z różnych jednostek, row=jednostka →
GRAND 2 przy jednej unikatowej pracy i BRAK ostrzeżenia (a to jest preset
"Produktywność jednostek"). Flaga liczona teraz uczciwie: nowe pole
PivotDimension.atrybut_rekordu (ten sam kształt co `autorzy` w rejestrze
rekordowym), ustawione dla `rok` i `charakter_formalny`, plus _dubluje():
baza P + jakikolwiek wymiar, który nie jest atrybutem pracy. Po poprawce
jednostka → 2 + True, rok → 1 + False. Tekst notki przeredagowany na
neutralny modelowo.

W2 — distinct=True w METRICS["liczba_prac"] nie było pokryte: wszystkie
testy grupowały po `autor`, gdzie DISTINCT jest no-opem (zmierzone: bez=1,
z=1; przy row=jednostka bez=2, z=1). Dopisany test dwóch współautorów jednej
jednostki (grand_total == 1) plus testy dublowania/niedublowania z W1.
Mutacja "usuń distinct=True" wywala teraz 2 testy.

W3 — testy eksportu macierzy asertowały samo b"RAZEM", które jest w KAŻDEJ
macierzy (także w błędnej domyślnej) — dlatego K1 przeszedł trzy rundy
review. Cztery testy sprawdzają teraz etykiety zamówionych wymiarów i
dokładne wartości komórek (XLSX przez openpyxl, nie przez Content-Type),
doszedł też test biorący WSZYSTKIE linki eksportu ze strony pivota i
wymagający od każdego macierzy zgodnej z ekranem. Mutacja "eksport ignoruje
parametry GET" wywala 4 testy, mutacja "przywróć stary górny pasek" — 2.

W4 — presety były martwe na świeżej stronie: renderowały się z query= i po
kliknięciu dawały 200 bez wyników, bez macierzy i bez komunikatu. Puste
zapytanie nie jest "wszyscy autorzy" — apply_search rzuca DjangoQLParserError
("Unexpected end of input"), więc widok w ogóle nie wchodzi w render_results.
Bez wysłanego zapytania blok pokazuje więc nazwy presetów jako tekst plus
zdanie, co zrobić, żeby ożyły (discoverability bez klikalnej obietnicy);
z zapytaniem — linki jak dotąd, z tekstem mówiącym wprost, że jadą
z WYSŁANYM zapytaniem, nie z niewysłaną treścią pola.

Drobne: prawdziwy docstring PivotDimension.expr_dla (D2); martwa bramka
{% if eksport_formaty %} i nieaktualny komentarz zastąpione realną bramką
postac != "pivot" (D3); jeden słownik TAK/NIE/— brak — dla wartości
logicznych (core.etykieta_bool) używany i przez pivot, i przez eksport
autorów, gdzie orcid_w_pbn jechał dotąd jako "True"/"False" (D5).

Świadomie odłożone (bez zmian): grupy o sumie 0 znikające z macierzy,
paginacja/URL-e admina liczone na darmo przy postac=pivot, rsplit("csv/").

Weryfikacja: pytest -k "pivot or zapytanie or multiseek" → 496 passed
(było 490), pre-commit czysty, zero zmian w migracjach i baseline-sql.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@mpasternak
mpasternak changed the base branch from feat/zapytanie-wyjscia to dev July 27, 2026 00:35
@mpasternak mpasternak closed this Jul 27, 2026
@mpasternak mpasternak reopened this Jul 27, 2026
…krzyzowa

# Conflicts:
#	src/django_bpp/templates/multiseek/report-body-pivot.html
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