Ohjelman rakenne noudattelee kolmitasoista kerrosarkkitehtuuria, ja koodin pakkausrakenne on seuraava:
Pakkaus ui sisältää käyttöliittymästä, services sovelluslogiikasta ja repositories tietojen pysyväistallennuksesta vastaavan koodin. Pakkaus entities sisältää luokkia, jotka kuvastavat sovelluksen käyttämiä tietokohteita.
Käyttöliittymä sisältää kolme erillistä näkymää:
- Kirjautuminen
- Uuden käyttäjän luominen
- Todo-lista
Jokainen näistä on toteutettu omana luokkanaan. Näkymistä yksi on aina kerrallaan näkyvänä. Näkymien näyttämisestä vastaa UI-luokka. Käyttöliittymä on pyritty eristämään täysin sovelluslogiikasta. Se ainoastaan kutsuu TodoService-luokan metodeja.
Kun sovelluksen todo-listan tilanne muuttuu, eli uusi käyttäjä kirjautuu, todoja merkitään tehdyksi tai niitä luodaan, kutsutaan sovelluksen metodia initialize_todo_list joka renderöi todolistanäkymän uudelleen sovelluslogiikalta saamansa näytettävien todojen listan perusteella.
Sovelluksen loogisen tietomallin muodostavat luokat User ja Todo, jotka kuvaavat käyttäjiä ja käyttäjien tehtäviä:
classDiagram
Todo "*" --> "1" User
class User{
username
password
}
class Todo{
id
content
done
}
Toiminnallisista kokonaisuuksista vastaa luokkan TodoService ainoa olio. Luokka tarjoaa kaikille käyttäliittymän toiminnoille oman metodin. Näitä ovat esimerkiksi:
login(username, password)
get_undone_todos()
create_todo(content)
set_todo_done(todo_id)
TodoService pääsee käsiksi käyttäjiin ja todoihin tietojen tallennuksesta vastaavan pakkauksessa repositories sijaitsevien luokkien TodoRepository ja UserRepository kautta. Luokkien toteutuksen injektoidaan sovelluslogiikalle konstruktorikutsun yhteydessä.
TodoService
-luokan ja ohjelman muiden osien suhdetta kuvaava luokka/pakkauskaavio:
Pakkauksen repositories luokat TodoRepository
ja UserRepository
huolehtivat tietojen tallettamisesta. TodoRepository
-luokka tallentee tietoa CSV-tiedostoon, kun taas UserRepository
-luokka SQLite-tietokantaan.
Luokat noudattavat Repository -suunnittelumallia ja ne on tarvittaessa mahdollista korvata uusilla toteutuksilla, jos sovelluksen datan talletustapaa päätetään vaihtaa. Sovelluslogiikan testauksessa hyödynnetäänkin tätä siten, että testeissä käytetään tiedostoon ja tietokantaan tallentavien olioiden sijaan keskusmuistiin tallentavia toteutuksia.
Sovellus tallettaa käyttäjien ja todojen tiedot erillisiin tiedostoihin.
Sovelluksen juureen sijoitettu konfiguraatiotiedosto .env määrittelee tiedostojen nimet.
Sovellus tallettaa tehtävät CSV-tiedostoon seuraavassa formaatissa:
65eef813-330a-4714-887b-2bda4d744487;opiskele pythonia;1;kalle
5749b61f-f312-45ef-94a1-71a758feee2b;kirjoita dokumentaatio;0;matti
Eli tehtävän id, sisältö, tehtystatus (0 = ei tehty, 1 = on tehty) ja käyttäjän käyttäjätunnus. Kenttien arvot erotellaan puolipisteellä (;).
Käyttäjät tallennetaan SQLite-tietokannan tauluun users
, joka alustetaan initialize_database.py-tiedostossa.
Kuvataan seuraavaksi sovelluksen toimintalogiikka muutaman päätoiminnallisuuden osalta sekvenssikaaviona.
Kun kirjautumisnäkymän syötekenttiin kirjoitetetataan käyttäjätunnus ja salasana, jonka jälkeen klikataan painiketta Login, etenee sovelluksen kontrolli seuraavasti:
sequenceDiagram
actor User
participant UI
participant TodoService
participant UserRepository
User->>UI: click "Login" button
UI->>TodoService: login("kalle", "kalle123")
TodoService->>UserRepository: find_by_username("kalle")
UserRepository-->>TodoService: user
TodoService-->>UI: user
UI->UI: show_todos_view()
Painikkeen painamiseen reagoiva tapahtumankäsittelijä kutsuu sovelluslogiikan TodoService
metodia login antaen parametriksi käyttäjätunnuksen ja salasanan. Sovelluslogiikka selvittää UserRepository
:n avulla onko käyttäjätunnus olemassa. Jos on, tarkastetaan täsmääkö salasanat. Jos salasanat täsmäävät, kirjautuminen onnistuu. Tämän seurauksena käyttöliittymä vaihtaa näkymäksi TodosView
:n, eli sovelluksen varsinaisen päänäkymän ja renderöi näkymään kirjautuneen käyttäjän todot eli tekemättömät tehtävät.
Kun uuden käyttäjän luomisnäkymässä on syötetty käyttäjätunnus, joka ei ole jo käytössä sekä salasana, jonka jälkeen klikataan painiketta "Create" etenee sovelluksen kontrolli seuraavasti:
sequenceDiagram
actor User
participant UI
participant TodoService
participant UserRepository
participant matti
User->>UI: click "Create user" button
UI->>TodoService: create_user("matti", "matti123")
TodoService->>UserRepository: find_by_username("matti")
UserRepository-->>TodoService: None
TodoService->>matti: User("matti", "matti123")
TodoService->>UserRepository: create(matti)
UserRepository-->>TodoService: user
TodoService-->>UI: user
UI->>UI: show_todos_view()
Tapahtumakäsittelijä kutsuu sovelluslogiikan metodia create_user antaen parametriksi luotavan käyttäjän tiedot. Sovelluslogiikka selvittää UserRepository
:n avulla onko käyttäjätunnus olemassa. Jos ei, eli uuden käyttäjän luominen on mahdollista, luo sovelluslogiikka User
-olion ja tallettaa sen kutsumalla UserRepository
:n metodia create
. Tästä seurauksena on se, että käyttöliittymä vaihtaa näkymäksi TodosView
:n. Luotu käyttäjä kirjataan automaattisesti sisään.
Uuden todon luovan "Create"-painikkeen klikkaamisen jälkeen sovelluksen kontrolli eteneeseuraavasti:
sequenceDiagram
actor User
participant UI
participant TodoService
participant TodoRepository
participant todo
User->>UI: click "Create"
UI->>TodoService: create_todo("vie roskat")
TodoService->>todo: Todo("vie roskat", kalle)
TodoService->>TodoRepository: create(todo)
TodoRepository-->>TodoService: todo
TodoService-->>UI: todo
UI->>UI: initialize_todo_list()
Tapahtumakäsittelijä kutsuu sovelluslogiikan metodia create_todo antaen parametriksi luotavan työn tiedot. Sovelluslogiikka luo uuden Todo
-olion ja tallettaa sen kutsumalla TodoRepository
:n metodia create
. Tästä seurauksena on se, että käyttöliittymä päivittää näytettävät todot kutsumalla omaa metodiaan initialize_todo_list
.
Sama periaate toistoo sovelluksen kaikissa toiminnallisuuksissa, käyttöliittymän tapahtumakäsittelijä kutsuu sopivaa sovelluslogiikan metodia, sovelluslogiikka päivittää todojen tai kirjautuneen käyttäjän tilaa. Kontrollin palatessa käyttäliittymään, päivitetään tarvittaessa todojen lista sekä aktiivinen näkyvä.
Graafisen käyttöliittymän koodissa on jonkin verran toisteisuuttaa, josta voisi toteuttaa omia komponenttejaan. Esimerkiksi pylint ilmoittaa toisteisesta koodista luokissa CreateUserview ja LoginView.