Google App Engine ja Datastore - viikkotehtävä 6
Tehdään pienelle tietokannalle käyttöliittymä (vrt. vt4) Googlen App Engine -palveluun.
Tämän tehtävän toteuttaminen edellyttää vähintään seuraavien materiaalien ja tehtävien läpikäyntiä:
Yhteiset vaatimukset:
- Tietojen tallentamiseen on käytettävä Google App Enginen tarjoamaa Google NoSQL-tietokantaa (datastore).
- Sovelluksen on toimittava ja oltava testattavissa Googlen App Engine -pilvipalvelussa. Pelkästään omalla koneella olevalla Google App Engine Launcherilla toimiva työ ei kelpaa. Palautettavan työn osoite ei siis saa alkaa http://localhost tai http://127.0.0.1
- Sovellus on toteutettava Flask-frameworkilla
Taso 1
Toteuta sovellus, joka käyttää seuraavanlaista rakennetta Googlen Datastoressa. Kts. Property Types
class Kilpailu(ndb.Model):
def validate_kesto(prop, value):
if not value > 0:
raise ValueError(u"Keston on oltava suurempi kuin nolla")
nimi = ndb.StringProperty(required=True)
alkuaika = ndb.DateTimeProperty(required=True)
loppuaika = ndb.DateTimeProperty(required=True)
kesto = ndb.IntegerProperty(required=True, validator=validate_kesto)
# ylimääritellään put-metodi. Lisätään tarkistus alku- ja loppuajalle
def put(self, *args, **kwargs):
if self.alkuaika > self.loppuaika:
raise ValueError(u"Alkuajan on oltava pienempi kuin loppuajan")
return self._put(*args, **kwargs)
class Sarja(ndb.Model):
def validate_kesto(prop, value):
if not value > 0:
raise ValueError(u"Keston on oltava suurempi kuin nolla")
nimi = ndb.StringProperty(required=True)
kesto = ndb.IntegerProperty(required=True, validator=validate_kesto)
kilpailu = ndb.KeyProperty(required=True,kind=Kilpailu) #viittaus kilpailuun
Datastoreen on tallennettava valmiiksi 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 (Kintturogaining) ja kukin joukkue oikeaan sarjaan.
kilpailut = [{"nimi":u"Jäärogaining", "loppuaika": "2015-03-17 20:00:00", "alkuaika": "2015-03-15 09:00:00", "kesto": 1}, {"nimi":u"Fillarirogaining", "loppuaika": "2016-03-17 20:00:00", "alkuaika": "2016-03-15 09:00:00", "kesto": 1}, {"nimi":u"Kintturogaining", "loppuaika": "2017-03-18 20:00:00", "alkuaika": "2017-03-18 09:00:00", "kesto": 1}]
sarjat = [{"nimi":u"4 h", "kilpailu": u"Kintturogaining", "kesto": 4}, {"nimi":u"2 h", "kilpailu": u"Kintturogaining", "kesto": 2}, {"nimi":u"8 h", "kilpailu": u"Kintturogaining", "kesto": 8}]
Vinkki tietojen lisäämiseen:
from datetime import datetime alkuaika = datetime.strptime("2010-06-04 21:08:12", "%Y-%m-%d %H:%M:%S")
Voit tarpeen mukaan parannella tietorakennetta esim. lisäämällä tarkistuksia, mutta yleisrakennetta ei saa muuttaa tai jo annettuja tarkistuksia ja rajoitteita ei saa poistaa.
Sovellus osaa seuraavat asiat:
- Sovelluksessa voidaan valita jokin olemassaolevista kilpailuista ja sovellus listaa tähän kilpailuun liittyvät sarjat sarjan nimen mukaan aakkosjärjestyksessä. Listauksessa pitää näkyä sarjan nimi ja sarjan kesto.
- Sovelluksessa voi luoda, poistaa ja muuttaa kilpailuja
- Sovelluksessa voi luoda, poistaa ja muuttaa valittuun kilpailuun liittyviä sarjoja
- Toteuta tarvittava lomakkeet WTFormsin avulla. Voit myös käyttää WTForms-appengine-kirjastoa.
- Virhetilanteista on annettava järkevät virheilmoitukset. Sovellus ei saa kaatua.
- Sovelluksen ulkoasun ja tarkan toimintatavan saat itse päättää. Myös javascriptia saa käyttää, jos koet sen tarpeelliseksi.
Taso 3
Laajenna taso 1 -ohjelmaa seuraavilla ominaisuuksilla:
- Lisää datastoreen seuraava luokka ja lisää datastoreen listassa olevat joukkeet:
class Joukkue(ndb.Model): def validate_jasenet(prop, value): jasenet = value if not len(jasenet) > 1: raise ValueError(u"Jäseniä on oltava vähintään kaksi : " + str(jasenet)) nimi = ndb.StringProperty(required=True) # json-muotoon ja siitä purkaminen hoituu automaattisesti eli tähän kenttään voi suoraan tallentaa # melkein minkä tahansa pythonin listan, joka sisältää perustietotyyppejä jasenet = ndb.JsonProperty(required=True, validator=validate_jasenet) sarja = ndb.KeyProperty(required=True,kind=Sarja) #viittaus sarjaan # Kaikki joukkueet kuuluvat kintturogainingiin joukkueet = [{"sarja": "8 h", "nimi": u"Onnenonkijat", "jasenet": ["Antero Paununen", "Pekka Paununen", "Raimo Laine"]}, {"sarja": "8 h", "nimi": u"Mudan Ystävät", "jasenet": ["Kaija Kinnunen", "Teija Kinnunen"]}, {"sarja": "8 h", "nimi": u"Vara 3", "jasenet": [u"barbar", u"foofoo"]}, {"sarja": "8 h", "nimi": u"Tollot", "jasenet": ["Juju", "Tappi"]}, {"sarja": "8 h", "nimi": u"Kahden joukkue", "jasenet": ["Matti Humppa", "Miikka Talvinen"]}, {"sarja": "8 h", "nimi": u"Siskokset", "jasenet": ["Sanna Haavikko", "Seija Kallio"]}, {"sarja": "8 h", "nimi": u"Dynamic Duo", "jasenet": ["Karhusolan Rentukka", "Kutajoen Tiukunen"]}, {"sarja": "8 h", "nimi": u"Toipilas", "jasenet": ["Leena Annila", "Satu Lehtonen"]}, {"sarja": "8 h", "nimi": u"Sopupeli", "jasenet": ["Antti Haukio", "Janne Hautanen", "Taina Pekkanen", "Venla Kujala"]}, {"sarja": "4 h", "nimi": u"Retkellä v 13", "jasenet": ["Henna Venäläinen", "Katja Vitikka"]}, {"sarja": "4 h", "nimi": u"Pelättimet", "jasenet": ["Kari Vaara", "Katja Vaara"]}, {"sarja": "8 h", "nimi": u"Kaakelin putsaajat", "jasenet": ["Jaana Kaajanen", "Mikko Kaajanen", "Timo Ruonanen"]}, {"sarja": "8 h", "nimi": u"Vara 1", "jasenet": [u"barfoo",u"foobar"]}, {"sarja": "2 h", "nimi": u"Hullut fillaristit", "jasenet": ["Hannele Saari", "Paula Kujala"]}, {"sarja": "2 h", "nimi": u"Kotilot", "jasenet": ["Jaana Meikäläinen", "Kaisa Konttinen", "Maija Meikäläinen", "Niina Salonen"]}, {"sarja": "8 h", "nimi": u"Rennot 1", "jasenet": ["Anja Huttunen", "Siru Kananen"]}, {"sarja": "8 h", "nimi": u"Vara 2", "jasenet": [u"bar",u"foo"]}, {"sarja": "4 h", "nimi": u"Vapaat", "jasenet": ["Juha Vapaa", "Matti Vapaa"]}, {"sarja": "8 h", "nimi": u"Susi jo syntyessään", "jasenet": ["Janne Pannunen", "Riku Aarnio"]}, {"sarja": "8 h", "nimi": u"Vara 4", "jasenet": [u"foo",u"bar"]}, {"sarja": "4 h", "nimi": u"Rennot 2", "jasenet": ["Heikki Häkkinen", "Pia Virtanen", "Sari Maaninka"]}, {"sarja": "4 h", "nimi": u"Tähdenlento", "jasenet": ["Anu", "Virva"]}, {"sarja": "8 h", "nimi": u"RogRog", "jasenet": ["Antti Kaakkuri", "Mikko Meikäläinen", "Pekka Kosonen", "Samuli Paavola"]}] - Kaikki valittuun kilpailuun liittyvät joukkueet listataan sarjoittain aakkosjärjestyksessä ja sarjan
sisällä joukkueen nimen mukaan aakkosjärjestyksessä. Listaa myös joukkueen jäsenet aakkosjärjestyksessä. Käytä listauksessa kolmitasoista listaa.
- 2 h
- Joukkue 1
- Jäsen 1
- Jäsen 2
- Joukkue 2
- Jäsen 1
- Jäsen 2
- Joukkue 1
- 4 h
- ...
- 2 h
- Sovelluksessa voi lisätä, poistaa ja muuttaa valittuun kilpailuun liittyviä joukkueita. Joukkueelta on kysyttävä joukkueen nimi, sarja ja jäsenten nimet.
- Sovellusta käyttääkseen on kirjauduttava google-tunnuksella. Kirjautumatta ei saa päästä millekään sovelluksen sivulle.
- Tarkista, että joukkueessa on aina vähintään kaksi jäsentä, mutta enintään viisi jäsentä
- Joukkueessa ei saa olla kahta samannimistä jäsentä
- Joukkueen jäsenen nimi ei saa olla tyhjä
- Tarkista, että kahta samannimistä joukkuetta ei saa lisätä samaan kilpailuun. Varmista, että sama tarkistus toimii järkevästi myös joukkueen tietoja muokattaessa. Joukkueen nimeä täytyy pystyä muuttamaan.
- Tarkista, että kahta samannimistä sarjaa ei saa lisätä samaan kilpailuun. Varmista, että sama tarkistus toimii järkevästi myös sarjan tietoja muokattaessa. Sarjan nimeä täytyy pystyä muokkaamaan.
- Virheellisistä tai puutteellisista syötteistä on ilmoitettava virheilmoituksella virheellisen kentän yhteydessä
Taso 5
Laajenna taso 3 -ohjelmaa seuraavilla ominaisuuksilla:
- Muuta tietokannan rakennetta siten, että tietokantaan voidaan tallentaa myös kilpailukohtaiset rastit. Rasteista täytyy tallentaa rastin koodi, rastin koordinaatit (latitude ja longitude) ja tieto mihin kilpailuun rasti kuuluu. Luo rasteja varten sopiva luokka (malli).
Saman kilpailun sisällä rastien koodien täytyy olla uniikkeja. Toteuta uusien rastien lisääminen ja olemassaolevien rastien muokkaaminen ja poistaminen.
- Latituden on oltava liukuluku väliltä -90 - 90
- Longituden on oltava liukuluku väliltä -180 - 180
- Mitään rastin tietoja ei saa jättää tyhjäksi
Lisää datastoreen valmiiksi seuraavat rastit Kintturogaining-kilpailuun:
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"}] - Toteuta rastileimauksien lisääminen, muokkaaminen ja poistaminen joukkueittain. Tee tietorakenteeseen tarvittavat muutokset. Käytä leimausten tallentamisessa tietokantaan json-muotoa. Tallenna leimaukset suoraan joukkueen yhteyteen. vrt. Joukkueen jäsenet. Rastileimauksesta täytyy lomakkeella kysyä leimauksen kellonaika ja mikä rasti on leimattu. Käytettävissä olevat rastit on tarjottava alasvetovalikossa.

Käyttäjien kommentit