Kirjoituskilpailusovelluksen (Writing Contest Web App) avulla voidaan järjestää kirjoituskilpailuja, joissa kerätään ja arvioidaan lyhyitä tekstejä, kuten runoja, aforismeja tai esseitä.
-
Luo virtuaaliympäristö:
python3 -m venv venv
Linux/Mac:
source venv/bin/activate
Windows:
venv\Scripts\activate
-
Asenna
flask
-kirjasto:pip install flask
-
Tarkista, että
sqlite3
on asennetuna:sqlite3 --version
Jos
sqlite3
ei löydy, asenna se.Linux:
sudo apt install sqlite3
Mac:
brew install sqlite3
-
Vaihtoehto 1: Luo tietokanta demosisällöllä:
sqlite3 database.db < init.sql
Vaihtoehto 2: Luo tyhjä tietokanta (vain kirjallisuuslajit luodaan valmiiksi):
sqlite3 database.db < schema.sql
-
Voit käynnistää sovelluksen:
flask run
tai
flask run --debug
Voit kirjautua demoon pääkäyttäjänä käyttäjätunnuksella admin
ja salasanalla admin
. Pääkäyttäjänä voit hallinnoida kilpailuja, käyttäjiä ja kilpailutöitä.
Jos luot tyhjän tietokannan, niin ensimmäisestä rekisteröidystä käyttäjästä tulee automaattisesti pääkäyttäjä, joka voi luoda, muokata ja poistaa käyttäjiä, kilpailuja ja kilpailutöitä.
Kirjautumaton käyttäjä ei näe Arvioi, Tekstisi ja Ylläpito-välilehtiä. Voit luoda toisen käyttäjätunnuksen, jolla on peruskäyttäjän oikeudet. Peruskäyttäjä ei näe Ylläpito-välilehteä.
Työn etenemistä voi seurata alla olevasta tehtävälistasta.
- Luo GitHubiin julkinen repositorio harjoitustyötä varten. Nimeä repositoriosi kuvaavasti.
- Valitse projektille aihe ja kirjoita README.md-tiedostoon kuvaus, joka esittelee sovelluksen keskeiset toiminnot.
- Kirjoita kuvaus samassa muodossa kuin aloitussivun esimerkkiaiheissa ja esimerkkisovelluksessa.
- Kirjaudu Labtooliin ja ilmoita siellä projektisi GitHub-osoite.
- Tavoitteena on, että sovelluksessa on ainakin seuraavat toiminnot:
- Käyttäjä pystyy luomaan tunnuksen ja kirjautumaan sisään sovellukseen.
- Käyttäjä pystyy lisäämään, muokkaamaan ja poistamaan tietokohteita.
- Käyttäjä näkee sovellukseen lisätyt tietokohteet.
- Käyttäjä pystyy etsimään tietokohteita hakusanalla tai muulla perusteella.
- README.md-tiedoston tulee kuvata, mikä on sovelluksen nykyinen tilanne.
- Tavoitteena on, että sovelluksessa on ainakin seuraavat toiminnot:
- Sovelluksessa on käyttäjäsivut, jotka näyttävät tilastoja ja käyttäjän lisäämät tietokohteet.
- Käyttäjä pystyy valitsemaan tietokohteelle yhden tai useamman luokittelun. Mahdolliset luokat ovat tietokannassa.
- Käyttäjä pystyy lähettämään toisen käyttäjän tietokohteeseen liittyen jotain lisätietoa, joka tulee näkyviin sovelluksessa.
- README.md-tiedoston tulee kuvata, mikä on sovelluksen nykyinen tilanne.
Huomioitavaa kurssityön arvioinnissa:
- Sovelluksen kaikki ominaisuudet on käytettävissä käyttäjätunnuksella
admin
ja salasanallaadmin
- Arviointia varten on hyvä rekisteröidä myös peruskäyttäjä, jolla ei ole ylläpito-oikeuksia
- Tietokohteiden haku hakusanalla tai suodattamalla on toteutettu ylläpitokäyttöliittymään
- Sovelluksen koodi on jaettu Flask Blueprint -moduuleiksi kansiossa
routes/
- Demodataan on lisätty kilpailu, jonka arviointijakso päättyy 31.8.2025
- "Tekstisi"-sivu on kurssivaatimusten käyttäjäsivu, josta listaa käyttäjän kilpailutyöt ja näyttää tilastotietona käyttäjän jättämien kilpailutöiden ja arvosteluiden lukumäärän
- Kilpailun asetuksissa valinnat "anonyymi arviointi" ja "julkinen arviointi" ovat erillisiä valintoja (eivät toistensa vastakohtia):
- anonyymi arviointi tarkoittaa, että kilpailutyön tekijän nimeä ei esitetä arvioijalle
- julkinen arviointi tarkoittaa, että kaikki käyttäjät voivat arvoida kilpailutöitä; muussa tapauksessa vain salaisen avaimen sisältämän linkin saaneet voivat arvioida kilpailutöitä
- Vertaisarvioinneissa ja kurssin ohjaajien palautteessa ehdotetut seuraavat parannukset on toteutettu:
- koodin liian pitkät rivit on jaettu useammalle riville
- pitkäksi kasvanut
app.py
on jaettu pienemmiksi moduuleiksi kansiossaroutes/
- sovelluksen asennusohjeisiin on lisätty puuttuneet yksityiskohdat
- SQL-injektioriski tiedostossa
sql.py
on korjattu käyttämällä parametreja - kirjautuneelta käyttäjältä piilotetaan rekisteröitymis- ja kirjautumislinkit
- demodataan lisätty arvostelu, jolla on pitkä arviointiperiodi, joten se on arvioitavissa
- Koska sovellus kasvoi suhteellisen laajaksi, toteutin sille kattavat yksikkötestit
- yksikkötestit helpotti merkittävästi muutosten tekemistä sovellukseen
- yksikkötestit eivät olleet kurssin vaatimuksissa, mutta koin ne hyödyllisiksi
- testikattavuus on tällä hetkellä 95 %, tarkempi testikattavuusraportti löytyy alta
- Testaus suurilla datamäärillä on tehty ja testien raportit löytyvät alta
- Olen korjannut kaikki
pylint *.py
komennolla löytämäni varoitukset ja virheet
Arvostelusivulla mainitut toteutetut vaatimukset:
- Käyttäjä pystyy luomaan tunnuksen ja kirjautumaan sisään sovellukseen
- Käyttäjä pystyy lisäämään, muokkaamaan ja poistamaan tietokohteita
- Käyttäjä näkee sovellukseen lisätyt tietokohteet
- Käyttäjä pystyy etsimään tietokohteita hakusanalla tai muulla perusteella
- Käyttäjäsivu näyttää tilastoja ja käyttäjän lisäämät tietokohteet
- Käyttäjä pystyy valitsemaan tietokohteelle yhden tai useamman luokittelun
- Käyttäjä pystyy lisäämään tietokohteeseen toissijaisia tietokohteita
- Sovellus toteutettu kurssimateriaalin mukaisesti
- Sovellus toteutettu Pythonilla käyttäen Flask-kirjastoa
- Sovellus käyttää SQLite-tietokantaa
- Kehitystyössä käytetty Gitiä ja GitHubia
- Sovelluksen käyttöliittymä muodostuu HTML-sivuista
- Sovelluksessa ei ole käytetty JavaScript-koodia
- Tietokantaa käytetään suoraan SQL-komennoilla (ei ORMia)
- Kirjaston flask lisäksi käytössä ei muita erikseen asennettavia kirjastoja
- Käyttäjän yleiskokemus sovelluksen toimivuudesta
- Käyttäjän yleiskokemus sovelluksen käytettävyydestä
- Käyttäjän lähettämässä tekstissä rivinvaihdot näkyvät selaimessa
- Kuvissa käytetty alt-attribuuttia [emojeissa on käytetty
aria-label
-kuvauksia] - Lomakkeissa käytetty label-elementtiä
- CSS:n avulla toteutettu ulkoasu (itse tehty, ei CSS-kirjastoa)
- Kehitystyön aikana on tehty commiteja säännöllisesti
- Commit-viestit on kirjoitettu englanniksi
- Commitit ovat hyviä kokonaisuuksia ja niissä on hyvät viestit
- Versionhallinnassa ei ole sinne kuulumattomia tiedostoja
- Tiedosto README.md antaa hyvän kuvan sovelluksesta
- Yleiskuva koodin laadusta (selkeys, luettavuus ja tyyli)
- Sisennyksen leveys on neljä välilyöntiä
- Koodi on kirjoitettu englanniksi
- Muuttujien ja funktioiden nimet muotoa total_count (ei totalCount)
- Merkkijonoissa käytetty aina joko ' tai "
- Välit oikein =-merkin ympärillä
- Välit oikein ,-merkin ympärillä
- Ei koodia tyyliin
if success return True else return False
- Jos funktio palauttaa arvon, tulee olla useita mahdollisia palautusarvoja
- Ei sulkeita if- ja while-rakenteiden ehtojen ympärillä
- Ei ehtoja tyyliin result == None ja result is None
- Taulut ja sarakkeet on nimetty englanniksi
- Taulut ja sarakkeet on nimetty hyvin
- Käytetty REFERENCES-määrettä, kun viittaus toiseen tauluun
- Käytetty UNIQUE-määrettä, kun tulee olla eri arvo joka rivillä
- Ei kyselyjä muotoa SELECT *
- Pitkät SQL-komennot jaettu usealle riville
- Kaikki tiedot haetaan yhdellä SQL-kyselyllä, jos järkevästi mahdollista
- Koodissa ei tehdä asioita, jotka voi mielekkäästi tehdä SQL:ssä
- Käytetty try/except SQL-komennon ympärillä vain aiheellisesti
- Salasanat tallennetaan tietokantaan asianmukaisesti
- Käyttäjän oikeus nähdä sivun sisältö tarkastetaan
- Käyttäjän oikeus lähettää lomake tarkastetaan
- Käyttäjän syötteet tarkastetaan ennen tietokantaan lisäämistä
- SQL-komennoissa käytetty parametreja
- Sivut muodostetaan sivupohjien kautta
- Lomakkeissa on estetty CSRF-aukko
- Sovellusta testattu suurella tietomäärällä ja raportoitu tulokset
- Sovelluksessa käytössä tietokohteiden sivutus
- Tietokantaan lisätty indeksi, joka nopeuttaa suuren tietomäärän käsittelyä
✅ = toteutettu
- Pääkäyttäjä luodaan automaattisesti. ✅
- Pääkäyttäjä voi luoda, muokata ja poistaa käyttäjiä. ✅
- Pääkäyttäjä voi luoda, muokata ja poistaa kilpailutöitä. ✅
- Pääkäyttäjä voi luoda, muokata ja poistaa kilpailuja. ✅
- Pääkäyttäjä voi suodattaa kilpailutöitä kilpailun ja käyttäjän nimen perusteella. ✅
- Pääkäyttäjä voi valita kilpailulle luokittelun: ✅
- Anonyymi arviointi: kirjoittaja ei ole tunnistettavissa ✅
- Avoin arviointi: kaikki saavat arvioida kilpailutöitä ✅
- Tulokset julkisia: kaikki näkevät kilpailun tulokset ✅
- Kirjallisuuslaji: runo/aforismi/essee ✅
- Käyttäjä voi luoda, muokata ja poistaa tunnuksen. ✅
- Käyttäjä voi kirjautua sisään sovellukseen. ✅
- Käyttäjä näkee sovellukseen lisätyt kilpailut. ✅
- Käyttäjä voi lisätä, muokata ja poistaa oman tekstin. ✅
- Tekstin muokkaus ja poisto on sallittu vain kilpailun keräysvaiheessa. ✅
- Käyttäjä voi arvioida muiden kilpailutöitä antamalla pistemäärän 1-5. ✅
- Sovelluksessa on käyttäjäsivu ("Tekstisi"-sivu) kilpailuista. ✅
- Käyttäjäsivu on käyttäjän sijoitus ja pistemäärä päättyneiden kilpailujen osalta. ✅
- Käyttäjä voi tarkastella kilpailun tuloksia. ✅
Testasin tietokannan nopeutta suurilla tietomäärillä ilman indeksejä ja lisäämällä indeksit. Haut, jotka kohdistuvat suuriin tietomääriin nopeutuivat merkittävästi:
Step 1: Recreate DB and populate with random data...
--- Table Record Counts ---
Users : 1001
Contests : 100
Entries : 5000
Reviews : 10000
Classes : 5
Step 2: Timing queries WITHOUT indexes...
Step 3: Adding indexes...
Step 4: Timing queries WITH indexes...
--- Table Record Counts ---
Users : 1001
Contests : 100
Entries : 5000
Reviews : 10000
Classes : 5
--- Query Timing Comparison ---
Query | No Index (s) | With Index (s) | Speedup
--------------------------------------------------------------------------------
search_user_by_username | 0.000074 | 0.000088 | 0.84x
list_entries_for_contest | 0.000136 | 0.000160 | 0.86x
list_entries_for_user | 0.002259 | 0.000022 | 102.10x
search_entries_by_username | 0.002740 | 0.003433 | 0.80x
list_reviews_for_entry | 0.000691 | 0.000047 | 14.76x
list_contests_for_class | 0.000058 | 0.000084 | 0.69x
--------------------------------------------------------------------------------
--- Table Record Counts ---
Users : 1001
Contests : 1000
Entries : 50000
Reviews : 100000
Classes : 5
Step 2: Timing queries WITHOUT indexes...
Step 3: Adding indexes...
Step 4: Timing queries WITH indexes...
--- Table Record Counts ---
Users : 1001
Contests : 1000
Entries : 50000
Reviews : 100000
Classes : 5
--- Query Timing Comparison ---
Query | No Index (s) | With Index (s) | Speedup
--------------------------------------------------------------------------------
search_user_by_username | 0.000087 | 0.000082 | 1.06x
list_entries_for_contest | 0.000223 | 0.000207 | 1.08x
list_entries_for_user | 0.024582 | 0.000233 | 105.69x
search_entries_by_username | 0.033925 | 0.033920 | 1.00x
list_reviews_for_entry | 0.007820 | 0.000063 | 124.88x
list_contests_for_class | 0.000487 | 0.000419 | 1.16x
--------------------------------------------------------------------------------
--- Table Record Counts ---
Users : 1001
Contests : 10000
Entries : 500000
Reviews : 1000000
Classes : 5
Step 2: Timing queries WITHOUT indexes...
Step 3: Adding indexes...
Step 4: Timing queries WITH indexes...
--- Table Record Counts ---
Users : 1001
Contests : 10000
Entries : 500000
Reviews : 1000000
Classes : 5
--- Query Timing Comparison ---
Query | No Index (s) | With Index (s) | Speedup
--------------------------------------------------------------------------------
search_user_by_username | 0.000082 | 0.000086 | 0.96x
list_entries_for_contest | 0.000283 | 0.000204 | 1.39x
list_entries_for_user | 0.221362 | 0.001838 | 120.46x
search_entries_by_username | 0.305147 | 0.311820 | 0.98x
list_reviews_for_entry | 0.077450 | 0.000070 | 1109.75x
list_contests_for_class | 0.003987 | 0.003726 | 1.07x
--------------------------------------------------------------------------------
--- Table Record Counts ---
Users : 10001
Contests : 10000
Entries : 500000
Reviews : 1000000
Classes : 5
Step 2: Timing queries WITHOUT indexes...
Step 3: Adding indexes...
Step 4: Timing queries WITH indexes...
--- Table Record Counts ---
Users : 10001
Contests : 10000
Entries : 500000
Reviews : 1000000
Classes : 5
--- Query Timing Comparison ---
Query | No Index (s) | With Index (s) | Speedup
--------------------------------------------------------------------------------
search_user_by_username | 0.000077 | 0.000082 | 0.94x
list_entries_for_contest | 0.004344 | 0.000214 | 20.26x
list_entries_for_user | 2.409421 | 0.000147 | 16399.88x
search_entries_by_username | 0.407845 | 0.386730 | 1.05x
list_reviews_for_entry | 0.070866 | 0.000082 | 863.79x
list_contests_for_class | 0.004155 | 0.003808 | 1.09x
--------------------------------------------------------------------------------
Testikattavuus on tällä hetkellä 95 %. Testikattavuusraportti luotiin komennolla pytest --cov --cov-report=html --cov-report=term
:
Name Stmts Miss Cover Missing
--------------------------------------------------
app.py 37 0 100%
db.py 43 0 100%
routes/__init__.py 0 0 100%
routes/admin.py 329 25 92%
routes/auth.py 110 0 100%
routes/entries.py 191 27 86%
routes/main.py 91 0 100%
sql.py 146 0 100%
users.py 76 0 100%
utils.py 30 0 100%
--------------------------------------------------
TOTAL 1053 52 95%