Live
JoukkueHub
SaaS-palvelu urheilujoukkueiden ylläpitoon
EskDev
Oikeita sovelluksia, ei pelkkiä harjoituksia — web, mobile ja Raspberry Pi.
Live
Python
Live
PWA
Play Store
Mobile
IoT
Peli
Projekti
SaaS harrastejoukkueille: oma logo, värit ja osoite, yhteinen kausi.
Joukkueet tarvitsevat hallintasovelluksen, jossa kaikki hallinta toimii yhden palvelun kautta. Yhden seuran sisäinen työkalu ei riitä, kun jokainen joukkue haluaa oman ilmeen ja osoitteen ilman että tiedot sekoittuvat.
JoukkueHub laajentaa JALPA-prototyypin SaaS-palveluksi. Joukkue saa itse muokattavat logon, värit ja osoitteen muotoa seura.joukkuehub.fi. Data rajataan team_id:llä. Toimii selaimessa ja asennettavana PWA:na.
Nimenhuuto ja toistuvat treenit samassa kalenterissa.
Kortit kehittyvät tilastojen mukaan, sama ajatus kuin JALPAssa.
Joukkuekohtainen logo, värit ja subdomain.
Ylläpito näkee joukkueet yhdestä paikasta.
Monivuokraajamalli: jokaisen kyselyn data rajataan joukkueen team_id:hen, jotta seuran asetukset ja rosteri eivät vuoda naapuriin.
JALPA oli yhden joukkueen sovellus. JoukkueHub irrottaa ilmeen ja osoitteen tenant-kohtaisiksi.
Ulkoasu saa vaihtua vapaasti. Tietokannan rivi ei saa näkyä väärälle joukkueelle.
Palvelu on kehitysvaiheessa ja toistaiseksi ilmainen testikäyttöön.
Projekti
Python-botti, joka vertaa jalkapallon tilastoja kertoimiin ja kokoaa poiminnat yhteen näkymään.
Kertoimien ja joukkueen viime pelien, sekä tilastojen vertailu käsin on hidasta. Maksullinen rajapinta kuluu nopeasti, jos joka ajolla haetaan kaikkea.
BetBot on Python-botti ja yksityinen selaindashboard. Asetuksista valitaan sarjat, aikaväli ja haettavat markkinat. Ajo tallentaa poiminnat, eikä botti aseta vetoja itse vaan valinnat jäävät käyttäjälle.
Sarjat, hakuikkuna ja haettavat markkinat valitaan ennen ajoa.
Viimeisin ajo, poimintojen määrä ja rajapinnan kulutus samalla sivulla.
Kohteet, jotka eivät täyttäneet ehtoa, jäävät listaan tarkistettavaksi.
Tuloksen voi ladata, ja ajosta lähtee ilmoitus.
Ottelut ja tilastot haetaan maksullisesta API-Footballista. Asetukset rajaavat sarjat ja markkinat, jotta kiintiö ei kulu turhaan.
Jokainen ajo ei tarvitse samoja sarjoja. Rajat ovat asetussivulla, jotta maksullisia kutsuja lähtee vain se, mitä sillä kertaa tarvitaan.
Poiminnat tallentuvat ja näkyvät dashboardissa. Päätös jää ihmiselle.
Oma työkalu. Julkista demoa ei ole.
Projekti
NHL-fantasymanageri kaveriporukalle: draftaa, treidaa, lukitse kentällinen.
Fantasialiigoja on jo olemassa, mutta mielenkiintona oli tehdä täysin oma versio koukuttavalla pelattavuudella, sekä hienolla käyttöliittymällä.
GM-Maestro on PWA/selainpeli, jossa liiga perustetaan tai siihen liitytään koodilla. Snake-draft, muokattava pisteytys, vapaiden pelaajien markkina, treidit ja kokoonpanon lukitus otteluiden ajaksi. NHL-tilastot synkataan automaattisesti.
Reaaliaikainen vuorojärjestys ja pelaajahaku.
Liigakohtaiset säännöt. NHL-tilastot tulevat synkasta, ei käsin.
Vaihda pelaajia tai nosta vapailta agentteilta.
Aktiivinen kentällinen ja vaihtopenkki. Lukko seuraa otteluita.
REST-rajapinta kantaa liigan, draftin ja rosterin. Selain on PWA, joten sama näkymä asentuu myös puhelimen aloitusnäytölle.
Pisteytys on muokattava. Yksi kovakoodattu NHL-malli ei riitä kaveriporukoiden eri sopimuksiin.
Kokoonpano lukittuu otteluiden mukaan, jotta pisteitä ei siirretä pudotuksen jälkeen.
Perusta liiga tai liity koodilla. Peli on julki osoitteessa gm-maestro.fi.
Projekti
YT JalPan kausi yhdessä PWA:ssa: nimenhuuto, talous ja kausikortit.
Joukkueen arki hajosi viesteihin: kuka tulee treeneihin, kuka on loukkaantunut, kenellä on sakkoja ja onko kausimaksu maksettu.
Seuran sisäinen web-sovellus pelaajille ja hallinnolle. Tumma teema, seuran keltainen. Sen voi asentaa puhelimen aloitusnäytölle. Korvasi aiemman React Native -prototyypin.
KOK alkaa kaudella 75:stä. +1 maali, +1 syöttö, +1 osallistuttu harjoitus, −1 jokaista kahta jäähyminuuttia kohti. Erikoiskortit: maaliputki (vähintään 2 peräkkäistä peliä maaleilla) ja treeniputki (vähintään 10 peräkkäistä harjoitusta).
Harjoitukset, pelit ja toistuvat viikkotapahtumat. Vastaus on Tulen tai En tule.
NHL-henkinen kausikortti: KOK, laukaus, syöttö, harjoitukset ja jäähyt.
Maalit, syötöt, pisteet ja jäähyminuutit kaudelta.
Sakot, maksutila ja kausikohtainen seuranta.
Kausimaksut, joukkueen menot, MobilePay-linkit ja maksun merkintä.
Aktiiviset loukkaantumiset ja palautumisarvio.
Roster, pelaajien lisäys, sakot, pisteet, tapahtumat ja laskutus.
Pelaajanumero, nimi ja salasana. Ensimmäisellä kerralla salasana asetetaan itse.
Selain on asennettava PWA (manifest ja service worker). API vaatii Bearer-tokenin kirjautumista lukuun ottamatta. Postgres pyörii Dockerissa. Tuotannossa nginx tarjoilee buildin ja PM2 ajaa API:n.
/api/auth
/api/events
/api/fines
/api/points
/api/invoices
/api/injuries
/api/player-cards
Kortin arvo sitoo maalit, syötöt, treenit ja jäähyt samaan kaavaan, jotta tilasto ei jää pelkäksi taulukoksi.
Aktiivinen kausi on frontendin SEASON-vakiossa ja backendin CURRENT_SEASON-arvossa. Molemmat pitää pitää samana.
Aiempi Android-prototyyppi vaihtui web-pinoon, jonka joukkue asentaa aloitusnäytölle ilman kauppaa.
Sisäinen joukkuesovellus. Lähdekoodi ja tuotantorakenne ovat GitHubissa.
Projekti
Sananselityspeli samaan huoneeseen. Ei tiliä, ei verkkoa, ei mainoksia.
Seurapelit kaatuvat siihen, että jokainen tarvitsee tilin, verkon tai oman laitteensa.
Yksi Android-puhelin kiertää porukassa. Kolme pelimuotoa, ennätykset talteen laitteelle, eivätkä sanat toistu saman pelin sisällä.
Kaksi pelaajaa, aloitus 20 sekuntia. Oikea vastaus: +1 piste ja +5 sekuntia. Ohitus: −3 sekuntia. Katto 30 sekuntia. Ennätys tallentuu.
Yksi selittää, kaksi arvaa. Piste sekä selittäjälle että arvaajalle. Selittäjä vaihtuu, eniten pisteitä voittanut voittaa.
2–6 paria, kierroksittain. Oikea vastaus +1. Väärästä vastauksesta voi valita rangaistuksen.
Sanat ovat uniikkeja pelin sisällä. Ei mainoksia, ei kirjautumista.
Peli ei puhu palvelimelle. Ennätykset ja pelin tila pysyvät laitteessa, joten sama puhelin riittää koko porukalle.
Seurapeli ei saa odottaa verkkoa. Siksi ei tiliä eikä taustapalvelua.
Aikahaasteessa oikea vastaus pidentää kierrosta ja ohitus syö sitä. Ennätys on syy pelata uudestaan.
Julkaistu Play Storessa. Läpäissyt suljetun testauksen ja tuotantohyväksynnän.
Projekti
Ryhmän rahat, kauppalista, reseptit ja ilmoitustaulu samassa sovelluksessa.
Perheen menot, ostokset ja ruokaideat elävät eri keskusteluissa, eikä kukaan näe samaa saldoa.
React Native -sovellus jaetulle ryhmälle. Etusivu kokoaa tulot, menot ja saldon. Listat ja ilmoitukset kulkevat ryhmässä. Keittiöapuri ehdottaa reseptejä kuvasta tai ilman.
Vuoden ja kuukauden tulot, menot ja saldo. Rivin voi rajata kategoriaan.
Meno tai tulo, kuvaus ja kategoria. Etusivulta pääsee suoraan kirjaukseen.
Jaetut listat ryhmän kesken, reaaliaikaisesti.
Kuva jääkaapista, kuivakaapista tai pakastimesta. Avustaja ehdottaa aterioita raaka-aineista.
Viikko, arki tai vaihtelu. Reseptin saa talteen ja sen voi viedä kauppalistalle.
Ryhmän tapahtumat aikajärjestyksessä.
Ryhmän kulut ja listat kulkevat Firebasen kautta. Ruoka ja AI käyttää Geminiä. Avain syötetään kerran Google AI Studioon ja se säilyy laitteella, kuten näkymä itse kertoo.
Ruoka ja AI -näkymä sanoo suoraan: avain lisätään kerran ja säilytetään laitteella.
Etusivu tervehtii ryhmän nimellä. Listat, kulut ja ilmoitustaulu on tehty jaettaviksi.
Sovelluksella ei ole julkista osoitetta tällä sivulla. Neljä näkymää alla.
Projekti
Raspberry Pi laskee sisään ja ulos, vaikka verkko olisi poikki.
Kävijämäärä pitää nähdä heti ravintolassa myös silloin, kun internetiä ei ole.
Raspberry Pi 5 laskee sisään- ja uloskäynnit liikkeentunnistuksella. Flask palvelee luvut, JSON pitää ne laitteella ja React Native -sovellus näyttää tilanteen. AJAX päivittää näkymän.
Laskenta toimii ilman internetyhteyttä.
Sisään ja ulos erotellaan laitteessa, ei käsin.
Luku päivittyy näkymään ilman koko sivun latausta.
React Native -sovellus samaan laskuriin.
Tiedot tallentuvat paikallisesti, eivät pilveen.
Laskenta ja tallennus pysyvät Pi:llä. Mobiilisovellus on näkymä samaan paikalliseen palveluun, ei erillinen pilvituote.
Ravintolan ovi ei odota yhteyttä. Siksi laskuri on suunniteltu offline-ensin.
Flask ja JSON ovat laitteen ydin. React Native on tapa katsoa samaa lukua puhelimesta.
Projekti elää laitteessa, ei julkisena demona.
Kaupan pitää näyttää ehjä polku: selaa, kirjaudu, täytä kori, tilaa, ja ylläpitäjä näkee tilauksen.
PHP ja MySQL. Haku ja tuotelista ovat julkisia, tilaaminen vaatii kirjautumisen. Kori päivittyy AJAXilla. Admin käsittelee tilaukset. Banneri muistuttaa, että kyse on harjoituksesta.
Tuotteet, kuvaus ja hinta. Yläpalkissa haku.
Kori päivittyy ilman koko sivun latausta (kori_ajax.php).
Rekisteröityminen ja kirjautuminen. Salasanat hashataan.
Kyselyt prepared statementeina, ei kasattuna merkkijonona.
Tilaus lähtee korista kassan kautta.
Tuotteiden ylläpito ja tilausten tilan päivitys.
Sivut ovat PHP:tä: kirjautuminen, rekisteröityminen, ostoskori, kassa, tilaus ja admin. Kori irrotettiin omaksi AJAX-pyynnöksi, jotta hylly ei lataudu uudestaan joka lisäyksellä.
PDO ja prepared statements pitävät käyttäjän syötteen erillään SQL:stä. Salasana tallentuu hashina.
Etusivun banneri sanoo suoraan: ei oikeita tuotteita, ei maksuliikennettä, ei oikeaa kaupankäyntiä.
Tunnukset voi luoda keksityillä tiedoilla. Ei oikeita tuotteita eikä maksuja.
Projekti
Poliisi vastaan rosvo selaimessa. Selviä pisimpään, nouse Hall of Fameen.
Kahden pelaajan ajo samaan selaimeen ilman pelimoottoria: yhteinen rata, eri ohjaimet, aika joka ratkaisee.
Takaa-ajo on puhdas JavaScript ja HTML5 Canvas. Poliisi (WASD) yrittää kolaroida rosvon (nuolet). Rosvo voittaa pitämällä etäisyyttä. Aika ratkaisee, ja vain viisi parasta mahtuu Hall of Fameen.
Pelaaja 1 on poliisi (WASD), pelaaja 2 rosvo (nuolet).
Kaupunki, Rata ja Jäärata. Eri maasto, eri ajettavuus.
Piste on selviytymisaika. Vain top 5 jää listalle.
Ajettavuus lukee maastomaskia, ei pelkkää taustakuvaa.
Aula mainitsee PS5-ohjaimet, jos niitä on kytketty.
Ei kirjastoja eikä palvelinta. Pelisilmukka piirtää, lukee syötteen ja päättää ajettavuuden maskin perusteella. Siksi peli on rajattu työpöytäselaimiin.
Rata ei ole vain kuva. Maski kertoo, missä auto pysyy ja missä ei.
Molemmat autot jakavat saman silmukan. Ero on ohjaimessa ja tavoitteessa: kiinni vai karkuun.
Vain työpöytäselain. Kaksi pelaajaa samalla näppäimistöllä tai ohjaimilla.
Visan pitää laskea pisteet niin, ettei oikea vastaus elä vain selaimessa, ja nimimerkin pitää olla siisti.
Yksi PHP-lomake, kymmenen kysymystä ja kolme vaihtoehtoa. Nimimerkistä jätetään vain kirjaimet, numerot ja alaviiva. Pisteet lasketaan palvelimella ja rivi kirjoitetaan data/tietovisa.json-tiedostoon.
Jokaisessa kolme vaihtoehtoa. Aihe sivulla on teknologia, ei jääkiekko.
Palvelin jättää jäljelle vain merkit a–z, 0–9 ja alaviivan.
Oikea vastaus verrataan palvelimen taulukkoon, ei pelkkään selaimen valintaan.
Tulosrivit kirjoitetaan JSONiin LOCK_EX-lukolla, jotta kaksi pelaajaa ei riko tiedostoa.
10/10 näyttää palkinnon. 9 pistettä kertoo, että se oli lähellä.
Ei tietokantapalvelinta. Tulokset ovat yksi JSON-tiedosto pelin kansiossa. Kirjoitus lukitaan, jotta rinnakkaiset vastaukset eivät korvaa toisiaan.
Lomake lähettää valinnat POST-pyyntönä. Piste lasketaan vasta, kun PHP vertaa ne oikeisiin vastauksiin.
Visa ei tarvitse MySQLiä. Tiedosto ja lukko riittävät nimimerkin ja pistemäärän tallentamiseen.
Kymmenen oikein avaa palkintonäkymän. Yhdeksän pistettä jää niukasti vajaaksi.