Strona główna systemu konsultacji w przeglądarce i strona ankiety o krytej pływalni na telefonie

Programowanie

System konsultacji i ankiet Gminy Stary Sącz

Nowy system konsultacji społecznych dla Gminy Stary Sącz: anonimowe ankiety zgodne z WCAG 2.2 AA, ocena ryzyka zamiast captchy obrazkowej, tryb kiosku i ankiety na zaproszenie. Następca zamkniętego systemu, którego nie dało się już utrzymać.

16.10.2026

Gmina ma obowiązek pytać mieszkańców o zdanie przed ważnymi decyzjami. Do tej pory służył do tego stary, zamknięty system, którego nikt w urzędzie nie potrafił już utrzymać. Zbudowałem nowy, na tym samym stosie co portal gminy.

System konsultacji i ankiet działa pod adresem konsultacje.starysacz.um.gov.pl. Odpowiedzi są anonimowe, formularz spełnia WCAG 2.2 AA i działa nawet bez JavaScriptu.

Strona główna systemu konsultacji w przeglądarce i strona ankiety o krytej pływalni na telefonie
Strona główna systemu i ankieta o krytej pływalni w widoku na telefon

Punkt wyjścia: stary system, którego nie dało się naprawić

Zanim napisałem pierwszą linijkę, przejrzałem stary serwer. Stał na CentOS 7 z PHP 7.2 i MySQL 5.6, czyli na oprogramowaniu bez wsparcia producenta od lat.

Sama aplikacja miała kilka poważnych wad:

  • kod kontrolerów był zaszyfrowany ionCube, więc logiki nie dało się ani sprawdzić, ani poprawić,
  • wynik zapisywał się jako osobny wiersz na każde pytanie, bez powiązania z respondentem,
  • przez to nie dało się zrobić żadnej tabeli krzyżowej, na przykład odpowiedzi według wieku,
  • nie było żadnej ochrony przed wielokrotnym głosowaniem,
  • brakowało pytań otwartych, rankingu i logiki warunkowej.

Wniosek był prosty: nowy system musi zapisywać ankietę jako całość, a archiwum trzech dawnych konsultacji wystarczy przenieść jednorazowym skryptem.

Ankieta, którą da się wypełnić bez frustracji

Pierwszym zadaniem dla systemu była ankieta o krytej pływalni w Starym Sączu. To ona wyznaczyła zakres pierwszej wersji, a reszta poczekała na kolejne etapy.

Formularz prowadzi mieszkańca krok po kroku:

  • jedna sekcja na ekran i wyraźna informacja, na którym kroku jesteś,
  • pytania pojawiają się w zależności od wcześniejszych odpowiedzi, a ukryte nie są wymagane,
  • typy pytań: jednokrotny i wielokrotny wybór, skala, ranking, pole tekstowe,
  • opcja „inne, jakie?” i odpowiedzi wykluczające, na przykład „nie korzystam”,
  • szkic zapisuje się w przeglądarce, więc odświeżenie strony nie kasuje odpowiedzi.
Strona ankiety z terminem i czasem wypełnienia obok drugiego kroku formularza z listą kroków po lewej
Strona ankiety i drugi krok formularza z pytaniami warunkowymi

Dostępność od pierwszego pola

Ankieta gminna musi dać się wypełnić czytnikiem ekranu, samą klawiaturą i przy powiększeniu 400 procent. Dlatego:

  • każde pytanie z opcjami to fieldset z legendą,
  • błędy pokazują się w podsumowaniu na górze z linkami do pól i przy samym pytaniu,
  • ranking działa przez wybór z listy, bez przeciągania myszą (WCAG 2.5.7),
  • pola wyboru mają obszar kliknięcia co najmniej 24 na 24 piksele,
  • walidacja i logika warunkowa działają po stronie serwera, więc formularz działa także bez JavaScriptu.

Każda zmiana przechodzi automatyczny audyt narzędziem axe.

Formularz ankiety z podsumowaniem błędów na górze i komunikatem przy pytaniu bez odpowiedzi
Podsumowanie błędów z linkiem do pytania, które wymaga odpowiedzi

Anonimowo, ale bez zasypywania wynikami

Ankieta bez logowania kusi, żeby wypełnić ją sto razy. Z drugiej strony nie chciałem zbierać adresów IP ani zmuszać mieszkańców do zakładania kont.

Rozwiązaniem jest ocena ryzyka. Żadne wypełnienie nie jest odrzucane, tylko dostaje punkty i listę powodów. W panelu wyników redakcja filtruje podejrzane odpowiedzi, a w eksporcie są osobne kolumny z oceną.

Zamiast reCAPTCHA działa ALTCHA, otwarta biblioteka proof-of-work. Liczy w tle, nie wymaga klikania w obrazki i nie wysyła nic do Google.

Tabela oceny ryzyka wypełnienia: honeypot 60, brak ALTCHA 40, za szybko 30, seria z jednej sieci 30, identyczne odpowiedzi 20, powtórka 20
Ocena ryzyka wypełnienia: wagi poszczególnych sygnałów

Odcisk urządzenia nie jest adresem IP, tylko skrótem HMAC z tajnym kluczem i identyfikatorem ankiety. Po zamknięciu ankiety nocne zadanie w Directusie zeruje wszystkie odciski. Zostają tylko oznaczenia, których nie da się już powiązać z żadnym urządzeniem.

Kanały i tryb kiosku

Ta sama ankieta może mieć kilka kanałów: Facebook, szkoły, punkt PSZOK, zebranie wiejskie. Link kanału jest podpisany HMAC, więc ręczna zmiana parametru nic nie da. Panel generuje do każdego kanału kod QR w SVG i PNG do druku.

Kanał kontrolowany włącza tryb kiosku. Po wysłaniu ankiety tablet pokazuje „Dziękujemy, przekaż urządzenie kolejnej osobie” i czysty formularz. Dzięki temu można zbierać odpowiedzi na zebraniach, bez oznaczania ich jako powtórki z jednego urządzenia.

Panel dla redakcji

Kreatorem ankiet jest panel Directusa: sekcje, pytania, opcje i warunki wyświetlania. Wyniki, eksport, kanały i kody QR są w osobnym panelu w Astro. Logowanie odbywa się kontem Directusa, a uprawnienia pilnuje Directus.

  • liczba odpowiedzi według dni i kanałów, wykres dla każdego pytania zamkniętego,
  • filtry: kanał, mieszkańcy i osoby spoza gminy, bez powtórek, tylko podejrzane,
  • eksport do CSV, który Excel otwiera z polskimi znakami, oraz do XLSX ze słownikiem kodów,
  • jeden wiersz na respondenta, więc wyniki od razu nadają się do analizy.

Osobny hook blokuje usunięcie ankiety, pytania lub opcji, do których są już przypisane odpowiedzi. Redaktor dostaje czytelny komunikat zamiast cichej utraty danych.

Ankiety na zaproszenie

Szybko okazało się, że ten sam mechanizm przyda się wewnątrz gminy. Ankieta rozpoznawcza IT trafia do jednostek podległych: szkół, przedszkoli, ośrodków. Każda jednostka dostaje własny link, a wyniki są poufne.

  • ankieta na zaproszenie nie istnieje publicznie, bez ważnego linku adres zwraca 404,
  • link nie jest przechowywany w bazie, panel wylicza go z sekretu, więc kopia zapasowa nie zawiera działających linków,
  • odpowiedzi zapisują się po każdym kroku i można wrócić do ankiety tym samym linkiem,
  • zaproszenia i przypomnienia wychodzą e-mailem prosto z panelu.

Archiwum i portal gminy

Trzy konsultacje ze starego systemu, z lat 2021 i 2024, trafiły do nowego razem z opisami i dokumentami. Po drodze skrypt naprawił uszkodzone polskie znaki i wyczyścił HTML.

Konsultacja projektu Strategii Rozwoju 2021-2030 przeniesiona ze starego systemu, z terminem i listą dokumentów
Konsultacja projektu Strategii Rozwoju przeniesiona ze starego systemu

System udostępnia publiczne API z listą trwających konsultacji i gotowy widget. Portal gminy pobiera dane po stronie serwera i pokazuje baner „Trwają konsultacje społeczne” tylko wtedy, gdy rzeczywiście coś trwa. Ten sam widget mogą wstawić szkoły i stowarzyszenia.

Stack

Celowo ten sam co w portalu gminy, żeby jedna osoba mogła utrzymać oba systemy:

  • Directus 11 z własną bazą PostgreSQL 16 i Redisem, oddzielnie od CMS portalu,
  • Astro 7 w trybie SSR, TailwindCSS v4 i DaisyUI v5 z własnym motywem,
  • ALTCHA zamiast reCAPTCHA,
  • wspólne z portalem MinIO, Matomo i Uptime Kuma,
  • wdrożenia przez OneDev CI z kopią bazy i healthcheckami.

Osobna baza to świadoma decyzja. Odpowiedzi mieszkańców mają inne okresy przechowywania i inne uprawnienia niż treści portalu, więc nie powinny leżeć w tej samej bazie.

Najtrudniejsze w ankiecie gminnej nie jest zebranie odpowiedzi, tylko zebranie ich tak, żeby można im było uwierzyć i nie wiedzieć przy tym nic o osobie, która odpowiada.

- Aleksander Kowal

Co dalej

  • weryfikacja SMS przez własną bramkę GSM w urzędzie: jeden numer, jeden głos, bez przechowywania numerów,
  • punktacja dojrzałości IT jednostek i mapa ciepła w ankiecie rozpoznawczej,
  • rejestr konsultacji prawnych z dokumentami, uwagami i raportem końcowym.