sw-admin jest backendem w napisanym w Pythonie z wykorzystaniem frameworka Flask, który odpowiada za podstrony /admin w SW.
Na serwerze produkcyjnym sw-admin jest uruchamiany za pomocą narzędzia Gunicorn, co pozwala mu działać w kilku procesach jednocześnie oraz prosty sposobem być wywoływanym z Openresty.
Katalog templates/ zawiera kod HTML dla każdej podstrony. Wykożystane są domyślne templatki z Flaska - biblioteka Jinja2 oraz Bootstrap.
| Interfejs |
|---|
![]() |
Logowanie jest zrealizowane z użyciem Flaskowego LoginManager. Każda funkcja ubrana w @login_required przekierowuje do /admin/login jeżeli uzytkownik nie jest zalogowany. ID zalogowanego użytkownika odpowiadające ID w bazie danych można uzyskać wywołując current_user.get_id().
Przekierowanie niezalogowanych użytkowników jest zrealizowane w taki sposób:
@login_manager.unauthorized_handler
def unauthorized():
return redirect('/admin/login', code=303)Uwaga! Adnotacja @login_required musi występować poniżej adnotacji @app.route('...') - jeżeli wystąpi linijkę wyżej nie ma ona żadnego efektu. Przykład:
# bad code - everyone can see this regardless of whether they are logged in or not
@login_required
@app.route('/admin/addpoll', methods=['GET'])
def admin_addpoll():
return render_template('addpoll.html')# good code - only logged in users can see this
@app.route('/admin/addpoll', methods=['GET'])
@login_required
def admin_addpoll():
return render_template('addpoll.html')Otwarcie strony /admin/login wywołuje funkcję admin_login() w Pythonie. Naciśnięcie na guzik "Zaloguj się" wywołuje admin_login_post().
Templatka HTML dla logowania: templates/login.html. Rozszerza templatkę templates/base.html.
| Interfejs |
|---|
![]() |
Strona wyświetla listę głosowań z bazy danych (tabela polls), które:
- nie są zakończone (
closed=falsew bazie danych) - są stworzone przez użytkownika który jest zalogowany (
owner_userw bazie danych jest równe ID użytkownika zalogowanego)
Funkcja w pythonie reagująca na otwarcie strony: admin_polls()
Templatka HTML: templates/polls.html (z parametrem closed=False) - rozszerza templates/baseloggedin.html która rozszerza templates/base.html.
| Interfejs |
|---|
![]() |
Strona wyświetla listę głosowań z bazy danych (tabela polls), które:
- są zakończone (
closed=truew bazie danych) - są stworzone przez użytkownika który jest zalogowany (
owner_userw bazie danych jest równe ID użytkownika zalogowanego)
Funkcja w pythonie reagująca na otwarcie strony: admin_closedpolls()
Templatka HTML: templates/polls.html (z parametrem closed=True) - rozszerza templates/baseloggedin.html która rozszerza templates/base.html.
| Interfejs |
|---|
![]() |
Dodaje nowe głosowanie z owner_user ustawionym jako ID aktualnie zalogowanego użytkownika.
Funkcja w pythonie reagująca na otwarcie strony: admin_addpoll()
Funkcja w pythonie reagująca na naciśnięcie przycisku "Dodaj głosowanie": admin_addpoll_post()
Templatka HTML: templates/addpoll.html - rozszerza templates/baseloggedin.html która rozszerza templates/base.html.
| Interfejs |
|---|
![]() |
Pozwala na edytowanie głosowania z id poll_id. Przed wykonaniem czegokolwiek sprawdza czy głosowanie należy do aktualnie zalogowanego użytkownika (owner_user).
Logika jest bardzo podobna co przy dodawaniu głosowania (/admin/addpoll) - z takimi różnicami że wartości w interfejsie są od razu załadowane z tabeli polls, a naciśnięcie przycisku "Zapisz głosowanie" nie tworzy nowego głosowania ale modyfikuje już istniejące.
Funkcja w pythonie reagująca na otwarcie strony: admin_editpoll()
Funkcja w pythonie reagująca na naciśnięcie przycisku "Zapisz głosowanie": admin_editpoll_post()
Templatka HTML: templates/editpoll.html - rozszerza templates/baseloggedin.html która rozszerza templates/base.html.
| Interfejs |
|---|
![]() |
Duplikowanie głosowania wypełnia wartości w interfejsie tak jak edytowanie głosowania, jednak naciśnięcie przycisku "Dodaj głosowanie" na dole strony powoduje dodanie nowego głosowania zamiast edycji istniejącego.
Funkcja w pythonie reagująca na otwarcie strony: admin_copypoll()
Rekacja na przycisk wykorzystuje logikę dodawania głosowania - jest to POST do /admin/addpoll wywołujący admin_addpoll_post().
Templatka HTML: templates/copypoll.html - rozszerza templates/editpoll.html która rozszerza templates/baseloggedin.html która rozszerza templates/base.html.
| Interfejs |
|---|
![]() |
Podgląd strony wykorzystuje tą samą logikę jak generacja pliku /opt/sw/poll/{poll_id}/index.html przy starcie wysyłki maili, jedynie ze zmienioną ścieżką. Dlatego przy podglądzie generowany jest plik /opt/sw/poll/{poll_id}/index-peek.html, po czym jest on czytany i zwracany do przeglądarki.
Funkcja w pythonie: look_at_poll()
Funkcja w pythonie nie robiąca nic oprócz przekierowania użytkownika do listy głosowań, ale reagująca na przycisk "Głosuj" naciśnięty przy podglądzie: admin_peek_vote().
Templatka HTML: templates/vote.html - rozszerza templates/base.html.
Interfejs rozsyłki maili zależy od stanu w jakim znajduje się głosowanie.
Funkcja w pythonie reagująca na wejście na stronę: admin_sendout()
Interfejs w zależności od stanu głosowania w bazie danych:
closed |
mailing_active |
planned_start_sending |
Interfejs |
|---|---|---|---|
false |
false |
NULL |
![]() |
false |
false |
not NULL |
![]() |
false |
true |
cokolwiek | ![]() |
true |
cokolwiek | cokolwiek | ![]() |
Przyciski w widoku rozsyłki maili:
| Przycisk Akcja POST i funkcja ją obsługująca |
Co dzieje się po naciśnięciu |
|---|---|
Akcja: /admin/sendout/plan?id={poll_id}
Funkcja: admin_sendout_plan_post()
|
Gdy minie zaplanowana godzina, sw_start_planned_mailing.py automatyczine zaimportuje sw_admin.py
i wywoła activate_mailing() symulując naciśnięcie na guzik "Aktywuj teraz", rozpoczynając głosowanie.
|
Akcja: /admin/sendout/unplan?id={poll_id}
Funkcja: admin_sendout_unplan_post()
|
|
Akcja: /admin/sendout/activatemailing?id={poll_id}
Funkcja: admin_sendout_activatemailing_post()
|
Ta funkcjonalność ( activate_mailing() z sw_admin.py) jest też wywoływana przez sw_start_planned_mailing.py
gdy planned_start_sending jest ustawiony na datę/godzinę która nadeszła
|
Akcja: /admin/sendout/deactivatemailing?id={poll_id}
Funkcja: admin_sendout_deactivatemailing_post()
|
|
Akcja: /admin/sendout/queueall?id={poll_id}
Funkcja: admin_sendout_queueall_post()
|
|
Akcja: /admin/sendout/queueselected?id={poll_id}
Funkcja: admin_sendout_queueselected_post()
|
|
Jakie adresy email znajdują się na listach w interfejsie:
| Lista | Adres znajduje się w possible_recipients |
Adres znajduje się w sending_out_to |
Adres znajduje się w sent_to |
|---|---|---|---|
![]() |
✔️ | ❌ | ❌ |
![]() |
✔️ | ✔️ | ❌ |
![]() |
✔️ | ✔️ | ✔️ |
Dla klaryfikacji tabelki: w "Wysłano do" wyświetlają się adresy które są we wszystkich trzech tablicach, a w "Jeszcze nie wysłane" adresy które są jedynie w possible_recipients.
| Interfejs |
|---|
![]() |
Wyniki głosowania są liczone przez skrypt countvotes.py przy otwarciu strony z wynikami.
Głosy są zapisane w osobnych plikach w katalogu /opt/sw/polls/{poll_id}/results/. Skrypt czyta każdy plik w tym katalogu i podaje wyniki, które są pokazywane na stronie.
Format plików z głosami
Przykład pliku z głosem gdzie głosujący wybrał opcje 0 ("Jan Kowalski"), 2 ("Jan Nowak"), oraz 13 ("Adam Nowak") (pierwsza linijka pusta):
option_0=Jan%20Kowalski&option_2=Jan%20Nowak&option_13=Adam%20Nowak
Przykład pliku gdzie głosujący wybrał jedynie opcję 1 ("Jan Nowakowski"):
option_1=Jan%20Nowakowski
Przykład pliku gdzie głosujący oddał pusty głos (pusty plik):
Funkcja w pythonie reagująca na otwarcie strony: admin_results()
Templatka HTML: templates/results.html - rozszerza templates/baseloggedin.html która rozszerza templates/base.html.




















