feat(zglos_publikacje): nowy wielokrokowy wizard zgłaszania publikacji#119
Merged
Conversation
620f78f to
412b8a0
Compare
Przebudowa formularza zgłaszania publikacji z jednoetapowego na wielokrokowy wizard z kafelkowym wyborem typu publikacji i formy dostępu. Rozdzielenie "artykuł lub monografia" na osobne typy, nowe pola (wydawca, wydawnictwo nadrzędne z QuerySetSequence autocomplete łączącym BPP i PBN), obsługa wielu plików PDF, konfigurowalne per-typ wymaganie opłat w admin uczelni. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…uń pole wydawca - Kafelek "Monografia" przemianowany na "Książka / Monografia" — nie każda książka to monografia w ścisłym sensie - Pole link (strona_www) wymagane zawsze niezależnie od trybu dostępu — PBN wymaga tego pola; help_text wyjaśnia, że dla dostępu ograniczonego wystarczy link do strony wydawcy lub katalogu BN/NUKAT - Usunięto pola "wydawca" i "wydawca_zgloszenia" z formularza użytkownika; wydawca ustawiany ręcznie przez bibliotekarza w adminie (konieczny do wyliczenia punktów i slotów) - _set_wydawca nie resetuje wydawcy gdy pola nieobecne w formularzu - Migracja scalająca dwa równoległe 0411 (bpp) - 7 nowych testów jednostkowych Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…dzaju zgłoszenia Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3fd1586 to
0824de4
Compare
Docker Build Cloud's layer cache occasionally returned a stale bpp_base (missing bpp-sync-deps after a dev->feature rebase). Force no-cache on the base target; cache mounts (apt/uv/npm/yarn) keep downloads fast, and app services already use contexts=target:base so they pick up the freshly-built image instead of pulling from the registry. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…mport
authserver's INSTALLED_APPS doesn't include password_policies, but
urls_auth_server -> views -> middleware transitively imported
password_policies.middleware at module level, which loads
password_policies.models and fails with:
RuntimeError: Model class password_policies.models.PasswordChangeRequired
doesn't declare an explicit app_label and isn't in an application in
INSTALLED_APPS.
Extract EXTERNAL_AUTH_BACKENDS into its own tiny module
django_bpp.external_auth so views.py can import it without transitively
touching middleware.py. middleware.py is now only imported when
password_policies is in INSTALLED_APPS (appserver), not authserver.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…point bpp-sync-deps was a 13-line wrapper around a single `uv sync` command, baked into every production image and gated by BPP_SYNC_DEPS_ON_START. Risk: any accidental env var flip would run uv sync in production. Remove the shared script entirely: - delete docker/bpp_base/sync-deps.sh and its COPY in the bpp_base Dockerfile - drop the `bpp-sync-deps` call from every production entrypoint (appserver, workerserver, beatserver, denorm-queue) so prod images carry zero dev-only logic - in docker-compose.yml (which is explicitly dev-only), override each service's entrypoint to run `uv sync --frozen --all-extras --no-extra=dev --no-install-project` before the production entrypoint. This is needed only because dev shadows .venv with a volume; prod bakes .venv into the image. - drop the BPP_SYNC_DEPS_ON_START flag everywhere Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
# Conflicts: # docker-bake.hcl
Add `pytest_configure` hook that invokes `make assets` so that CSS + .mo files are guaranteed up-to-date regardless of how pytest is invoked (bezpośrednio przez `uv run pytest` vs `make tests`). BPP UI depends on built assets; there is no pure-Python mode. Use UV_NO_SYNC=1 for the make call so that the `uv run` invocations inside Makefile targets (notably `compilemessages`) do not re-sync the venv and strip `testcontainers` + other dev-extras, which would crash the testcontainers_bpp pytest plugin before collection.
Dwa rozłączne, odkryte razem błędy:
1) Bug: `pliki` field używa `MultipleFileInput` (widget z
`allow_multiple_selected=True`) ale klasa pola to zwykły
`forms.FileField`. Przy uploadzie ≥1 pliku widget zwraca listę,
a `FileField.to_python` próbuje `.name` na liście → crash z
komunikatem "Nie wysłano żadnego pliku. Sprawdź typ kodowania
formularza.". Dodany `MultipleFileField` rozpakowujący listę
przez single_file_clean dla każdego elementu.
2) Testy Playwright klikały `input[value='...']` oraz
`#id-wizard-submit` na krokach 0/1, ale:
- Radio input jest ukryty wewnątrz `<label class="tile-card">`
(CSS `display:none`) — Playwright odmawia klik niewidocznego.
- Przycisk `#id-wizard-submit` na krokach 0/1 nie istnieje —
szablon nadpisuje `{% block wizard_buttons %}` pustym blokiem;
submit odbywa się przez JS handler na kafelku.
Zmieniono na `.tile-card[data-value='...']` (single click).
3) `test_zglos_publikacje_ograniczony_dostep` przekazywał
`strona_www=""`, ale po `5b68c5b4` pole jest `required=True` —
HTML5 blokował submit przed roundtripem. Teraz używa default URL;
walidacja brakującego pliku (server-side) dalej działa jako
intencja testu.
Dwa jednolinijkowe bugi infrastrukturalne ujawnione przez CI runs na branchu: 1) Tests (Playwright) faili z "BrowserType.launch: Executable doesn't exist at /root/.cache/ms-playwright/...". Powód: step `Install Playwright browsers` uruchamia `docker compose run --rm test-runner` (ephemeral), więc cache browserów w /root/.cache/ms-playwright znika z kontenerem. Kolejny step (Tests Playwright) startuje nowy kontener bez browserów. Fix: named volume `playwright_cache` mapujący ten katalog, żeby persystował między `compose run --rm`. 2) Lint changed files > Ruff check faili na src/zglos_publikacje/migrations/0023_... z błędem I001 (import ordering). Pyproject ma `extend-exclude` z migracjami, ale ruff przy JAWNIE podanych ścieżkach ignoruje excludes bez `--force-exclude`. Fix: dodać flagę do obu kroków ruffa (check + format --check).
Paginator (np. django-autocomplete-light) rzucał
UnorderedObjectListWarning dla sekwencji w ZrodloAutocomplete — każdy
sub-queryset ma tam `.order_by("nazwa","pk")` i jest sliced, więc
kolejność wyników jest deterministyczna, ale oryginalna klasa
`QuerySetSequence.ordered` zwraca True tylko gdy `.order_by()` wywołano
na samej sekwencji — czego nie można zrobić na sliced sub-querysetach
(Django 4+: "Cannot reorder a query once a slice has been taken").
`_OrderedSlicedQuerySetSequence` dziedziczy i nadpisuje `ordered`
tak, że zwraca True gdy każdy sub-queryset jest faktycznie
uporządkowany (delegacja do `qs.ordered` każdego z nich) — bez fake'u.
Jeśli którykolwiek sub-queryset jest nieuporządkowany, sekwencja
nadal raportuje False i ostrzeżenie jest uzasadnione.
Dodaje drugi, ergonomiczny sposób włączenia budowy obrazów dla feature branchy: label `docker-build` na PR-ze. Zalety nad istniejącym plikiem-flagą `.docker-build`: - label żyje w metadanych PR-a, nie w drzewie repo — merge do mastera nie przenosi stanu „build-on-push" nigdzie, kolejne feature branche nie dziedziczą flagi przypadkiem. - toggle jednym poleceniem: `gh pr edit <PR#> --add-label docker-build` / `--remove-label docker-build`, bez commitów. - typ `labeled` zapewnia, że dodanie labela na już-pushniętym commicie od razu odpala workflow — bez konieczności pustego push'a. Kompatybilne wstecznie: `.docker-build` i `workflow_dispatch` nadal działają bez zmian. `check-flag` sprawdza warunki w kolejności: workflow_dispatch → PR label → master → .docker-build. Bezpieczeństwo: `HAS_DOCKER_BUILD_LABEL` ewaluuje się jako string `true`/`false` w kontekście GitHub Actions (nie shell interpolation); w `run:` porównujemy zmienną środowiskową.
Uruchamiał się 11min/21tur, kosztował ~$10 USD per push, ale nie produkował żadnego review ani komentarza na PR-ze (brak `write` w permissions + `use_sticky_comment: false` + puste inline comments). Do odkomentowania jak konfiguracja plugina `code-review@claude-code-plugins` + action-u zostanie ustawiona tak, żeby faktycznie postowała review.
…likacje # Conflicts: # .github/workflows/build-docker-images.yml # conftest.py
…e_20260416_2124 Po wczesniejszym merge'u dev w galezi powstaly dwie galezie liscie 0413 (bppuser_autor_onetoone i merge_20260416_2124). Migracja 0414 sciaga je do jednego liscia, zeby `migrate` nie wybuchal CommandError'em. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…formy dostepu Tekst pomocniczy pola "Link do publikacji lub DOI" jest teraz dobierany z tablicy STRONA_WWW_HELP_TEXT na podstawie kombinacji (rodzaj, forma dostepu) zgodnie z uwagami od bibliotekarki: - artykul: bez fragmentu o BN/NUKAT (dotyczy tylko monografii/rozdzialu) - monografia/rozdzial w dostepie ograniczonym: pojawia sie odsylacz do katalogow Biblioteki Narodowej i NUKAT - inne (np. materialy konferencyjne): bez wzmianki o PBN; pole nie jest wymagane, bo PBN tych publikacji nie zbiera; przy dostepie ograniczonym pozostaje wymagany plik PDF Macierz w SPEC_NOWY_FORMULARZ.md zaktualizowana, zeby odzwierciedlac faktyczne zachowanie kodu. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Dla rodzaju POZOSTALE (Inne) help_text pola strona_www uproszczony: pusty dla otwartego dostępu, samo zdanie informacyjne dla ograniczonego (bez prefiksu PBN i bez dopisku o materiałach konferencyjnych). - Krok 1 (forma dostępu): trzy kafelki ułożone w kolumnie zamiast kwadratowej siatki 2x2 z rozjechanym przyciskiem "Poprzedni krok". - Nad kafelkami pojawia się badge z ikoną i nazwą wybranego rodzaju publikacji (np. "📄 Artykuł"), żeby użytkownik widział kontekst wyboru. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…la dev buildów
- /app/.git-sha w obrazie bpp_base zawiera commit, z którego zbudowano runtime;
pozwala szybko zdiagnozować sytuacje, gdzie pull tagu zwraca stare pliki
(np. ktoś deployuje nie ten tag).
- Stopka ("wersja X (commit YYYYYYY)") pokazuje krótki SHA tylko dla obrazów
developerskich (PR/feature), żeby release master zachował czyste oznaczenie
numerem wersji. BPP_BUILD_FLAVOR ustawia workflow: master->release, reszta->dev.
- Wartości GIT_SHA i BPP_BUILD_FLAVOR są wstrzykiwane jako ENV w runtime stage
bpp_base, przekazywane przez docker-bake.hcl i workflow build-docker-images.yml.
Test: src/bpp/tests/test_templatetags/test_bpp_version.py pokrywa: brak env,
"unknown", flavor=release (puste), flavor=dev (krótki SHA), default flavor=dev.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Stopka dla obrazów developerskich pokazuje teraz "wersja X (119-merge, commit YYYYYYY)" — pomaga to namierzyć, którego PR-a/brancha dotyczy deployowany obraz, bo sam SHA łatwo pomylić, a tag obrazu jest tym, co ktoś wpisał w docker-compose.yml. BPP_IMAGE_TAG przekazywane od workflow (steps.tag.outputs.final_tag) przez bake'a do bpp_base runtime stage jako ENV. Master release nadal pokazuje tylko numer wersji (BPP_BUILD_FLAVOR=release). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Zwiększone padding (0.75 → 1.25rem) oraz rozmiar ikony (2.5 → 3rem), żeby pasek "wybrany rodzaj" w step_forma_dostepu był wyraźniejszy i nie wyglądał jak wąski niebieski pasek.
…erge) Tester może teraz pullować iplweb/bpp_appserver:feature-foo-bar zamiast pamiętać numer PR. Workflow promuje staging tag pod oba aliasy: :<PR#>-merge (kanoniczny) i :<sanitized_branch_name> (alias). Każdy push do PR-a re-promuje oba, więc nazwa brancha pozostaje świeża dopóki PR żyje. Dla master release (TAG_LATEST=true) i feature-pushy bez PR (final_tag = branch_tag) alias się nie tworzy. HEAD_REF (źródło source brancha PR-a) jest sanityzowany sed-em zanim trafi do imagetools — bezpieczne nawet dla branch names z ukośnikami czy znakami specjalnymi. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Stopka pokazuje teraz "wersja X (119-merge, feature-nowe-zglos-publikacje,
commit YYYYYYY)" — od razu widać i kanoniczny tag PR-a, i sanityzowaną
nazwę gałęzi. Wcześniej stopka znała tylko jeden alias (final_tag),
więc nie było jasne że można też pullować po nazwie brancha.
Pipeline: workflow przekazuje branch_tag do bake'a, bake do bpp_base
runtime, a tam ENV BPP_BRANCH_TAG zostaje wczytany przez nowy
template tag {% bpp_branch_tag %}. Master release ma BPP_BUILD_FLAVOR=
release → tag zwraca pusty string, stopka pokazuje samą wersję.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Plik .docker-build juz nie istnieje (skasowany w poprzednim commicie), wiec elif sprawdzajacy `[ -f ".docker-build" ]` byl dormantnym kodem. Zastapione: push na non-master (czyli feature/fix/hotfix przez restrykcje triggera) → buduj zawsze. Realizuje user-intent "auto-build na feature branches" — bez tego push na feature spadalby na else (skip), a `.docker-build` flag nie istnieje. Komentarze i opisy aktualizowane — bez wzmianek o pliku flagi. Pozostale `docker-build` w workflow to label PR-a (mechanizm zostaje). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wycofuje gating labelem .docker-build/docker-build na rzecz prostszej zasady: master/main push i workflow_dispatch buduja zawsze (release flow + manual override), pozostale (PR sync, feature/fix/hotfix push bez PR) — tylko gdy actor=mpasternak. Inni contributorzy nie pala Docker Cloud minutek; jesli trzeba zbudowac obraz dla cudzego PR-a: `gh workflow run build-docker-images.yml --ref <branch>`. Dev branch dopisany jawnie do komentarza w pushu jako "intentionally excluded" — push do dev nie odpala buildu (intermediate state nie zasluguje na obraz, release leci przez master). Dodany main do triggerow obok master (gdyby kiedys repo zmienilo default branch — single source of truth). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
# Conflicts: # src/zglos_publikacje/tests/test_playwright/test_zglos_publikacje.py
# Conflicts: # src/deduplikator_autorow/views/__init__.py # src/deduplikator_autorow/views/duplicates.py # src/deduplikator_autorow/views/export.py # src/deduplikator_autorow/views/helpers.py # src/deduplikator_autorow/views/ignore.py # src/deduplikator_autorow/views/merge.py # src/deduplikator_autorow/views/scan.py
W kroku 2 wizarda pole `pliki` (`MultipleFileField` + `<input multiple>`)
przyjmowało N plików, ale do `Zgloszenie_Publikacji_Zalacznik` trafiał
zero, jeden lub żaden — zależnie od ścieżki kodu.
Dwa nakładające się błędy:
1. `_process_files` czytało `self.request.FILES.getlist("2-pliki")`,
ale w `done()` `request.FILES` to zawartość ostatniego kroku
wizarda (autorzy/opłaty), nie kroku 2. Efekt: 0 załączników.
2. `formtools.wizard.storage.base.set_step_files` iteruje
`files.items()` po `MultiValueDict`, co dla pól z `<input multiple>`
gubi wszystkie wartości poza ostatnią. Efekt po fixie #1: tylko 1
załącznik niezależnie od liczby uploadowanych plików.
Rozwiązanie:
- `process_step_files` zapisuje wszystkie pliki bezpośrednio do
`file_storage` i listę metadanych do `storage.extra_data["pliki_list"]`.
Standardowy storage formtools nadal dostaje swoje (re-walidacja
w `render_done` zobaczy 1 plik = przejdzie clean()).
- `_process_files` w `done()` czyta z `extra_data` i tworzy
`Zgloszenie_Publikacji_Zalacznik` dla każdego pliku, sprzątając
tmp-y po skopiowaniu do permanent storage.
- `_wyczysc_tmp_pliki` usuwa stare tmp-y przy ponownym submitie
kroku 2 (powrót w wizardzie).
Admin:
- `Zgloszenie_PublikacjiAdmin.pliki_do_pobrania` (readonly_field)
wyświetla listę wszystkich załączników + legacy pole `plik`.
- Nowy `pobierz_zalacznik_view` z X-Accel-Redirect dla pojedynczych
Zgloszenie_Publikacji_Zalacznik.
Testy regresji:
- `test_pelny_formularz_ograniczony_jeden_plik` — wizard OGRANICZONY
z 1 plikiem, asercja `zalaczniki.count() == 1`.
- `test_pelny_formularz_ograniczony_wiele_plikow` — z 3 plikami,
asercja `zalaczniki.count() == 3`. Bez fixa testy fail-ują;
z fixem przechodzą. Webtest zamiast playwright bo
`webtest_app.post(..., upload_files=[...])` obsługuje multipart
z duplikatami klucza, a playwright na step 3→4 jest flaky przez
auto-uzupełnianie jednostki w `autorform_dependant.js`.
Dodatkowo w tym samym commicie:
- Wyniesienie inline `<style>` z `step_base.html`/`step_rodzaj.html`/
`step_forma_dostepu.html` do `_wizard_forms.scss` (nowy plik
importowany w `app-{blue,green,orange}.scss`).
- `Zgloszenie_Publikacji_DaneForm.clean` defensywnie dla `self.files`
w postaci dict (re-walidacja z storage formtools nie zwraca
`MultiValueDict`).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Trzy współbieżne zmiany w wizardzie zgłaszania publikacji:
1) **Bug**: gdy user wybrał wydawnictwo nadrzędne (lub wydawcę)
z autocomplete, do `Zgloszenie_Publikacji` trafiał tylko tekst
etykiety, FK (`wydawnictwo_nadrzedne_bpp`/`_pbn`,
`wydawca_bpp`/`_pbn`) zostawał `None`.
Powód: `PublicWydawcaAutocomplete` i
`PublicWydawnictwoNadrzedneAutocomplete` miały nadpisany
`get_result_value`, który zwracał etykietę HTML zamiast
`<ct_pk>-<obj_pk>` (formatu, którego oczekuje QSS widget przy
POST). W rezultacie `<option value="...">` miał w sobie HTML
etykiety, a `_resolve_qss_value()` w `done()` nie potrafił
rozparsować referencji do obiektu.
Fix: usunąć obie nadpisówki — domyślne `get_result_value`
z `dal_queryset_sequence.views` zwraca poprawny format.
2) **Re-walidacja widget z malformed value** (z poprzedniego commita
zostawione w forms.py): `TolerantQuerySetSequenceSelect2` filtruje
bogusowe wartości przed `split('-', 1)` w `filter_choices_to_render`,
żeby re-render formularza po validation error nie wybuchał z 500.
3) **UX**: help_text pola `strona_www` (link do publikacji lub DOI)
wzbogacony o `_FULL_URL_TEKST` ze wskazówką:
- prefiks `https://`/`http://`
- jeżeli sam DOI — dodaj `https://dx.doi.org/[numer DOI]`
Suffix dochodzi do wszystkich 8 kombinacji `(rodzaj, forma_dostepu)`
w `STRONA_WWW_HELP_TEXT`, w tym do `POZOSTALE/OTWARTY` która
wcześniej miała pusty help_text.
4) **CSS**: `.select2-container { display: block !important;
margin-bottom: 1rem !important }` + spacing między select2 a
następującym `.help-text`. Bez tego help_text crispy renderowany
pod widgetem QSS wpadał w pole selecta (Foundation daje natywnemu
`<select>` margin: 0 0 1rem, ale select2 wstawia `<span>` które
tego nie dziedziczy).
5) **Migracja merge** `0415_merge_20260504_0907.py` — łączy `0414_*`
z dev (code review fixy) z `0414_merge_20260427_1123` (z dev'owego
merge w naszej gałęzi).
Testy regresji (webtest, pełne ścieżki przez wizard):
- `test_rozdzial_z_picknietym_wydawnictwem_nadrzednym_zapisuje_FK`
— RAW format `<ct>-<pk>` (jak picnięty z autocomplete) → asercja
`wn_bpp_id == ksiazka.pk` (regression dla bugu #1).
- `test_pelny_formularz_monografia_otwarty` — pełna ścieżka MONOGRAFII
z asercjami: zgłoszenie utworzone, brak `wydawnictwo_nadrzedne_*`
(forma usuwa pola), brak `wydawca_*` (świadomie usunięte z formularza
w 5b68c5b — bibliotekarz uzupełnia w adminie). Test pilnuje tej
decyzji projektowej; gdy ktoś przywróci pole `wydawca`, ten test
trzeba świadomie zaktualizować.
- (z poprzedniego commita pozostają)
`test_pelny_formularz_rozdzial_z_freetextem_wn`,
`test_rozdzial_z_bogus_wn_picker_nie_wybucha_na_re_renderze`,
`test_rozdzial_z_typed_text_w_picker_lzdaje_jako_freetext`.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
ARTYKUL=5,MONOGRAFIA=4,INNE=6)Zgloszenie_Publikacji_Zalacznik)Test plan
uv run python src/manage.py migrate-- sprawdzenie migracjiuv run pytest src/zglos_publikacje/ -x-- testy jednostkowe i integracyjnemake tests-only-playwright-- testy E2E🤖 Generated with Claude Code