Firestore, Fetch API ja JSON/XML - viikkotehtävä 5
Toteutetaan javascriptin ja Fetch APIn avulla aiemmilta viikoilta tuttu tulospalvelujärjestelmä single-page applikaationa (SPA). Tehtävässä on ideana osata käyttää Firestoren rajapintaa sekä tehdä WWW-palvelimelle sopivia apuohjelmia, joita voi sitten kutsua Javascript-ohjelmasta
Yhteiset vaatimukset:
- Käytä Firestore-tietokantaa natiivimoodissa
- Sovelluksen on toimittava yhdellä ja samalla www-sivulla ilman, että sivua ladataan sovelluksen käyttämisen aikana uudelleen. Javascriptin avulla hoidetaan sisällön muutokset dynaamisesti. Poikkeus: kirjautuminen saa tapahtua eri sivulla.
- WWW-palvelimella mahdollisesti tarvittavat ohjelmat pitää toteuttaa Flask-sovelluksina.
- Muotoilut pitää tehdä erillisellä CSS-tiedostolla.
- Kommunikointi javascript-sovelluksen ja www-palvelimen välillä on toteutettava Fetch API -rajapinnan tai Firestoren javascript-rajapinnan kautta.
- Sivun on oltava validia HTML5:sta
- Sivun on toimittava ja se on testattava sekä Google Chrome-selaimella että Firefoxilla
- Älä käytä innerHTML-metodia tai muuta vastaavia funktioita, jotka sallivat syöttää DOM-puuhun merkkijonoja DOM-rajapinnan ohitse.
- Halutessaan saa javascript-sovelluksen toteuttamisessa käyttää Reactia
- Sovelluksen voi sijoittaa users.jyu.fi-palvelimelle tai Googlen App Engine -palveluun
users.jyu.fi-palvelimella saatat joutut rajoittamaan urllib3-kirjaston version:
pip install urllib3==1.26.6
- Kannattaa lukea seuraavat: History API, Get realtime updates with Cloud Firestore, Cloud Firestore: On data constraints and evolvability ja Should I query my Firebase database directly, or use Cloud Functions?
Taso 1
Luo itsellesi fork valmiista pohjasta
Firestoreen (native mode) on tallennettava valmiiksi hieman vastaavat tiedot, kuin mitä käytettiin aiemmissa viikkotehtävissä. Tässä sisältö on helposti käsiteltävässä muodossa. Muista varmistaa, että sarjat liittyvät oikeaan kilpailuun.
kilpailut = [{"kisaid": 1, "kisanimi":"Jäärogaining", "loppuaika": "2023-03-17 20:00:00", "alkuaika": "2023-03-15 09:00:00"}, {"kisaid": 2, "kisanimi":"Fillarirogaining", "loppuaika": "2016-03-17 20:00:00", "alkuaika": "2016-03-15 09:00:00"}, {"kisaid": 3, "kisanimi":"Kintturogaining", "loppuaika": "2017-03-18 20:00:00", "alkuaika": "2017-03-18 09:00:00"},{"kisaid": 99, "kisanimi":"Jäärogaining", "loppuaika": "2021-05-01 20:00:00", "alkuaika": "2021-05-01 12:00:00"}]
sarjat = [{"sarjanimi":"4 h", "kisa": 1, "kesto": 4, "sarjaid": 1}, {"sarjanimi":"2 h", "kisa": 1, "kesto": 2, "sarjaid": 2}, {"sarjanimi":"8 h", "kisa": 1, "kesto": 8, "sarjaid": 3},{"sarjanimi":"Pikkusarja", "kisa": 3, "kesto": 4, "sarjaid": 4},{"sarjanimi":"8 h", "kisa": 3, "kesto": 8, "sarjaid": 5}, {"sarjanimi":"Isosarja", "kisa": 3, "kesto": 8, "sarjaid": 6},{"sarjanimi":"Pääsarja", "kisa": 2, "kesto": 4, "sarjaid":7},{"sarjanimi":"2 h", "kisa": 2, "kesto": 2, "sarjaid": 8}]
Suunnittele tarjolla olevia tietoja varten sopiva tallennushierarkia Firestoreen. Tallenna edellä valmiina annetut tiedot Firestoreen.
Toteuta seuraavanlainen sovellus:
- Sovellukseen täytyy kirjautua google-tunnuksella. Sovelluksen mihinkään osaan ei saa päästä käsiksi kirjautumatta ellei näissä ohjeissa mainita poikkeuksia.
- Sovelluksen käyttöliittymänä toimii yksi staattinen www-sivu, jonka on oltava XHTML-yhteensopiva (mediatyyppi: application/xhtml+xml). Katso ja kokeile mallipohjaa ja sivun välilehtiä. Javascriptin ja CSS:n avulla voi sivulla esittää dynaamisesti eri asioita tilanteesta riippuen. Kannattaa kirjoittaa kaikki mahdollinen (staattinen) sisältö valmiiksi.
- Linkitä sivuun javascript-ohjelma, joka hoitaa varsinaisen toiminnallisuuden ja kommunikoi suoraan Firestore-tietokannan kanssa ja tarvittaessa myös palvelimella olevan Flask-sovelluksen kanssa.
- Sivulla on "Omat joukkueet"-välilehdellä listaus
kaikista järjestelmään kirjautuneen käyttäjän luomista joukkueista.
- Listasta voi valita haluamansa joukkueen aktiiviseksi ja muokattavaksi.
- Oletuksena valittuna on listauksen ensimmäinen joukkue.
- Listauksen viimeisenä on aina valinta "Lisää uusi joukkue".
- "Joukkueen tiedot"-välilehdellä on lomake, jolla lisätään uusi joukkue tai muutetaan
valittuna olevan joukkueen tietoja.
- Lomakkeella kysytään joukkueen nimi ja jäsenten nimet. Tiedot ovat koko ajan muutettavissa ja päivitettävissä. Kahta samannimistä joukkuetta ei voi luoda.
- Epäonnistuneesta joukkueen lisäämisestä tai muokkaamisesta pitää näyttää virheellisen kentän yhteydessä virheilmoitus. Käytä Constraint validation APIia (setCustomValidity ja reportValidity).
- Alert-funktiota ei saa käyttää.
- Jos varsinainen lisäys- tai muokkausoperaatio epäonnistuu, niin näytä virhe tallenna-painikkeen yhteydessä.
- Onnistuneesta joukkueen lisäämisestä tai tietojen muuttamisesta täytyy ilmoittaa selkeästi esim. seuraavilla teksteillä: "Joukkue xxx on lisätty" ja "Joukkueen xxx tiedot on tallennettu"
- Joukkueen nimi ei saa olla tyhjä tai koostua pelkistä välilyönneistä
- Jäseniä on oltava vähintään kaksi. Jäseniä saa olla enintään viisi. Jäsenen nimi ei saa olla tyhjä, koostua pelkistä välilyönneistä tai sisältää numeroita. Kaikilla kunkin joukkueen jäsenillä on oltava uniikki nimi. Muissa joukkueissa saa olla samoja nimiä.
- Lomakkeen alapuolella on kaksitasoinen listaus kaikista kilpailuista ja kunkin kilpailun sarjoista.
- Kukin sarjan nimi on linkki, jota klikkaamalla joukkue ilmoittautuu kyseiseen sarjaan.
- Sarja, johon joukkue on ilmoittautuneena, esitetään korostettuna.
- Joukkue voi olla ilmoittautuneena vain yhteen sarjaan kussakin kilpailussa.
- Jos valitussa sarjassa on jo samanniminen joukkue, ei tätä sarjaa voi valita
- Joukkue voi poistaa kokonaan ilmoittautumisensa tietystä kilpailusta klikkaamalla jo korostettuna olevan sarjan nimeä. Varmista ilmoittautumisen poistaminen window.confirm-dialogilla.
- Huom! Sivu ei saa vaihtua tai latautua uudelleen linkkejä klikattaessa.
- Käyttäjä voi ilmoittaa joukkueensa kaikkiin tarjolla oleviin kilpailuihin, mutta vain yhteen sarjaan per kilpailu.
- Joukkue ei välttämättä ole ilmoittautunut mihinkään sarjaan / kilpailuun. Näytä näiden kohdalla listauksessa kilpailun nimen alla teksti: "Ei ole ilmoittautunut kilpailuun"
- Sovellus listaa "Joukkueet"-välilehdellä kolmitasoisena listana kaikki Firestoreen
tallennetut kilpailut, sarjat, joukkueet ja joukkueiden jäsenet.
Joukkueet listataan
sarjan nimen mukaan aakkosjärjestyksessä ja sarjan sisällä joukkueen nimen mukaan
aakkosjärjestyksessä. Listaa myös joukkueen jäsenet aakkosjärjestyksessä.
- Joukkuelistaus pyydetään omalta Flask-sovellukselta, jonka on palautettava joukkuelistaus JSON-muodossa. Et siis voi pyytää joukkuelistausta suoraan javascriptilla Firestore-tietokannasta vaan sinun on käytettävä Fetch-pyyntöä omalle sovellukselle
- Javascript-sovellus muuntaa saamansa JSON-muodon WWW-sivulla esitettävään muotoon
- "Joukkueet"-sivun listauksen on oltava ajantasalla eli omien joukkueiden tietojen muuttuminen pitää näkyä heti listauksessa
Taso 3
Laajenna taso 1 -mukaista ohjelmaa seuraavilla tavoilla:
- Joukkueen nimi ei voi olla sama, kuin sellaisen joukkueen, joka jo löytyy niistä sarjoista, joihin joukkue on ilmoittautunut tai yrittää ilmoittautua. Vertailu on caseinsensitive eikä nimen alussa tai lopussa olevaa whitespacea huomioida. Huomioi tämä rajoite joukkueen nimeä muutettaessa ja yritettäessä ilmoittaa joukkuetta johonkin sarjaan. Joukkueen nimeä ei siis voi muuttaa nimeksi, joka on jo käytössä sellaisessa sarjassa, johon joukkue on ilmoittautunut.
- Mahdollista joukkueen poistaminen, jos joukkue ei ole ilmoittautunut mihinkään kilpailuun (sarjaan). Joukkueen voi poistaa raahaamalla kyseisen joukkueen "Omat joukkueet"-sivulla olevaan roskakoriin. Käytä siis drag & dropia Onnistuneesta poistamisesta pitää antaa selkeä ilmoitus. vrt. ykköstason ilmoitukset joukkueen lisäämisestä ja tietojen muuttamisesta.
- Joukkuetta ei saa pystyä poistamaan (raahaamaan), jos joukkue on ilmoittautuneena johonkin sarjaan
- Joukkuelistan on pysyttävä automaattisesti ajantasalla, vaikka joku muu muokkaisi tietokantaa samaan aikaan. Kokeile käyttämällä useammalla selaimella privaattitilassa samaan aikaan. Tämä on toteutettava kommunikoimalla javascriptin avulla suoraan firestore-tietokannan kanssa.
- Päivitä Firestore-tietokannan rakennetta siten, että tietokantaan voidaan
tallentaa myös kaikille kilpailuille yhteiset rastit. Rasteista täytyy
tallentaa rastin koodi ja rastin koordinaatit (latitude ja longitude).
Lisää Firestoreen valmiiksi seuraavat rastit :
rastit = [{"lat": 62.13028, "lon": 25.666688, "koodi": "Tuntematon"}, {"lat": 62.120776, "lon": 25.542413, "koodi": "66"}, {"lat": 62.156532, "lon": 25.496872, "koodi": "6D"}, {"lat": 62.112172, "lon": 25.714338, "koodi": "91"}, {"lat": 62.099795, "lon": 25.544984, "koodi": "48"}, {"lat": 62.133029, "lon": 25.737019, "koodi": "31"}, {"lat": 62.110562, "lon": 25.518665, "koodi": "85"}, {"lat": 62.115047, "lon": 25.615203, "koodi": "69"}, {"lat": 62.088183, "lon": 25.729848, "koodi": "99"}, {"lat": 62.11183, "lon": 25.644512, "koodi": "60"}, {"lat": 62.148123, "lon": 25.618079, "koodi": "63"}, {"lat": 62.134681, "lon": 25.605762, "koodi": "70"}, {"lat": 62.13028, "lon": 25.666688, "koodi": "LAHTO"}, {"lat": 62.10393, "lon": 25.63595, "koodi": "90"}, {"lat": 62.122986, "lon": 25.573049, "koodi": "34"}, {"lat": 62.11906, "lon": 25.628228, "koodi": "37"}, {"lat": 62.089674, "lon": 25.652877, "koodi": "5C"}, {"lat": 62.129767, "lon": 25.626533, "koodi": "44"}, {"lat": 62.086189, "lon": 25.695688, "koodi": "79"}, {"lat": 62.127323, "lon": 25.597278, "koodi": "82"}, {"lat": 62.095187, "lon": 25.628236, "koodi": "64"}, {"lat": 62.141243, "lon": 25.509358, "koodi": "6F"}, {"lat": 62.136462, "lon": 25.668097, "koodi": "41"}, {"lat": 62.153864, "lon": 25.540227, "koodi": "40"}, {"lat": 62.102194, "lon": 25.673997, "koodi": "5A"}, {"lat": 62.144852, "lon": 25.493141, "koodi": "92"}, {"lat": 62.118784, "lon": 25.718561, "koodi": "5B"}, {"lat": 62.121247, "lon": 25.678314, "koodi": "49"}, {"lat": 62.111294, "lon": 25.553191, "koodi": "78"}, {"lat": 62.098636, "lon": 25.691051, "koodi": "56"}, {"lat": 62.078212, "lon": 25.733259, "koodi": "42"}, {"lat": 62.139918, "lon": 25.535011, "koodi": "67"}, {"lat": 62.138397, "lon": 25.56252, "koodi": "7C"}, {"lat": 62.091567, "lon": 25.680401, "koodi": "96"}, {"lat": 62.13232, "lon": 25.498431, "koodi": "53"}, {"lat": 62.132964, "lon": 25.57761, "koodi": "95"}, {"lat": 62.142319, "lon": 25.590916, "koodi": "76"}, {"lat": 62.15146, "lon": 25.50711, "koodi": "46"}, {"lat": 62.126591, "lon": 25.704639, "koodi": "58"}, {"lat": 62.147298, "lon": 25.665822, "koodi": "83"}, {"lat": 62.125561, "lon": 25.558017, "koodi": "51"}, {"lat": 62.087827, "lon": 25.671071, "koodi": "97"}, {"lat": 62.147942, "lon": 25.563169, "koodi": "5E"}, {"lat": 62.124222, "lon": 25.649234, "koodi": "94"}, {"lat": 62.100104, "lon": 25.586932, "koodi": "47"}, {"lat": 62.153364, "lon": 25.52873, "koodi": "74"}, {"lat": 62.099512, "lon": 25.522034, "koodi": "73"}, {"lat": 62.126639, "lon": 25.750133, "koodi": "7B"}, {"lat": 62.141674, "lon": 25.718473, "koodi": "6A"}, {"lat": 62.107914, "lon": 25.61344, "koodi": "43"}, {"lat": 62.093545, "lon": 25.716227, "koodi": "71"}, {"lat": 62.101185, "lon": 25.565572, "koodi": "77"}, {"lat": 62.153435, "lon": 25.560594, "koodi": "33"}, {"lat": 62.09468, "lon": 25.647515, "koodi": "6E"}, {"lat": 62.100413, "lon": 25.728135, "koodi": "80"}, {"lat": 62.131251, "lon": 25.540316, "koodi": "7E"}, {"lat": 62.149572, "lon": 25.597308, "koodi": "68"}, {"lat": 62.134123, "lon": 25.682473, "koodi": "7A"}, {"lat": 62.109962, "lon": 25.7288, "koodi": "89"}, {"lat": 62.115924, "lon": 25.569589, "koodi": "45"}, {"lat": 62.135094, "lon": 25.523811, "koodi": "57"}, {"lat": 62.147825, "lon": 25.513792, "koodi": "38"}, {"lat": 62.113906, "lon": 25.668757, "koodi": "81"}, {"lat": 62.141654, "lon": 25.628636, "koodi": "50"}, {"lat": 62.081466, "lon": 25.686679, "koodi": "7D"}, {"lat": 62.108717, "lon": 25.589347, "koodi": "54"}, {"lat": 62.146315, "lon": 25.645642, "koodi": "72"}, {"lat": 62.095246, "lon": 25.732937, "koodi": "62"}, {"lat": 62.149229, "lon": 25.576022, "koodi": "86"}, {"lat": 62.123662, "lon": 25.531059, "koodi": "5D"}, {"lat": 62.142258, "lon": 25.526039, "koodi": "88"}, {"lat": 62.144101, "lon": 25.694017, "koodi": "32"}, {"lat": 62.125632, "lon": 25.49602, "koodi": "6B"}, {"lat": 62.131769, "lon": 25.669574, "koodi": "MAALI"}, {"lat": 62.115241, "lon": 25.743788, "koodi": "65"}, {"lat": 62.093203, "lon": 25.606234, "koodi": "55"}, {"lat": 62.117266, "lon": 25.694911, "koodi": "75"}, {"lat": 62.156431, "lon": 25.519131, "koodi": "93"}, {"lat": 62.147942, "lon": 25.531926, "koodi": "61"}, {"lat": 62.128162, "lon": 25.724837, "koodi": "36"}, {"lat": 62.118778, "lon": 25.524245, "koodi": "39"}, {"lat": 62.115914, "lon": 25.503483, "koodi": "59"}, {"lat": 62.140919, "lon": 25.648821, "koodi": "35"}, {"lat": 62.094023, "lon": 25.661916, "koodi": "84"}, {"lat": 62.120424, "lon": 25.599044, "koodi": "52"}, {"lat": 62.131207, "lon": 25.650436, "koodi": "98"}, {"lat": 62.127514, "lon": 25.674748, "koodi": "5F"}, {"lat": 62.10758, "lon": 25.687644, "koodi": "6C"}]
- Lisää www-sivulle uusi välilehti "Rastit". Lisää tälle sivulle lomake, jolla voi lisätä, poistaa ja muokata rasteja. Lomakkeen toiminnot on toteutettava javascriptilla ilman, että sivu latautuu uudelleen. Lomakkeen vieressä on listaus jo olemassa olevista rasteista.
- Tieto käytettävissä olevista rasteista on pyydettävä omalta Flask-sovellukselta XML-muodossa
- Rasteja voi poistaa, muuttaa ja lisätä
- Rastikoodia saa muuttaa vapaasti. Myös rastin sijaintia (lat ja lon) saa muuttaa, mutta arvoiksi kelpuutetaan vain kokonaisluvut ja liukuluvut
- Uusia rasteja saa lisätä vapaasti, mutta kahdella rastilla ei saa olla samaa rastikoodia.
- Rastikoodi ei saa olla tyhjä tai koostua pelkistä välilyönneistä. Rastikoodin alussa tai lopussa olevia välilyöntejä ei huomioida eikä tallenneta
- Rasteihin tehdyt muutokset tallennetaan vasta, kun kaikki muutokset on tehty eli kaikki muutokset (lisäykset, poistot, muutokset) tallennetaan tietokantaan kerralla. Varmista tallentaminen window.confirm-dialogilla.
Taso 5
Laajenna taso 3 -mukaista ohjelmaa seuraavilla ominaisuuksilla:
- Suunnittele ja toteuta Firestoreen järkevä tallennusratkaisu joukkueen kilpailukohtaisia rastileimauksia varten
- Rastileimaukset eivät saa mennä rikki rasteja muuteltaessa
- Lisää www-sivulle uusi välilehti "Leimaukset", jossa olevalla lomakkeella joukkue voi lisätä, poistaa tai muokata kaikkia oman joukkueensa kilpailukohtaisia rastileimauksia. Sivua ei saa ladata uudelleen vaan lomakkeen toiminnallisuus on toteutettava javascriptin avulla. Vain kirjautuneen käyttäjän joukkueiden leimauksia saa muokata.
- Leimaukset välilehdellä valitaan ensin kilpailu, jonka leimauksia joukkue haluaa muokata.
- Jo mahdollisesti olemassa olevat rastileimaukset listataan lomakkeella muokattaviksi aikajärjestyksessä vanhin ensimmäisenä. Kaikkia leimauksia voi muokata samanaikaisesti
- Leimattava rasti valitaan raahaamalla rasti lomakkeen vieressä olevasta rastilistauksesta. Käytä drag & dropia
- Rastileimaukselle on myös annettava leimauskellonaika. Kellonaika ei voi olla ennen kilpailun alkuaikaa. Käytä datetime-local-tyyppistä kenttää. Tässä ei tarvitse huomioida aikavyöhykkeitä.
- Uusia rastileimauksia voi lisätä 1-N kappaletta samalla kerralla
- Minkä tahansa leimauksen voi poistaa raahaamalla kyseisen leimauksen sivulla olevaan roskakoriin
- Minkä tahansa leimauksen voi muuttaa vaihtamalla leimauksen kellonaikaa tai raahaamalla leimauksen päälle uuden rastin.
- Jos valitussa kilpailussa ei ole rasteja, ei myöskään rastileimauksia voi lisätä
- Jos kirjautuneella henkilöllä ei ole vielä joukkuetta missään kilpailussa, ei leimausvälilehteä ole ollenkaan tarjolla.
- Rastia ei saa poistaa ellei samalla poisteta tai tyhjätä siihen liittyviä rastileimauksia.
- Jos poistettavaa rastia on leimattu, kysy ennen poistamista mitä tehdään rastileimauksille: poistetaanko leimaukset vai tyhjätäänkö leimaukset? Kysy tämän jälkeen vielä varmistus, että haluatko varmasti poistaa rastin?
- Tyhjättäessä leimaukset merkitään kohdistumaan rastiin, jonka koodi on "Tuntematon". Jos tämäkin rasti on poistettu, jää leimauksen rastiviite kokonaan tyhjäksi (None, null tms.). Varmista, että myös tälläisiä rastileimauksia voi muokata.
- Rastileimauksiin tehdyt muutokset tallennetaan vasta, kun kaikki muutokset on tehty eli kaikki tehdyt muutokset (lisäykset, poistot, muutokset) tallennetaan tietokantaan samaan aikaan. Varmista tallentaminen window.confirm-dialogilla. Rastileimaukset on päivitettävä XML-muotoisena datana eli javascript-sovellus lähettää palvelimelle päivitettävät rastileimaukset sopivassa XML-muodossa.
- Rastileimausten oikeellisuus on varmistettava palvelimella eli leimauksen ajan on oltava järkevä, leimattava rasti on löydyttävä olemassa olevien rastien joukosta, leimauksen voi lisätä vain kirjautuneena olevalle käyttäjälle jne.
- Huolehdi, että tietokanta ei hajoa vaikka useampi käyttäjä muokkaisi samaan aikaan samoja leimaustietoja
Käyttäjien kommentit