ITV0110 3. töö 2010: Lihtne serverirakendus: vormid ja cgi

Allikas: Lambda

NB! See on arhiveeritud ülesanne 2010 aastast, mitte hetkel kehtiv ülesanne!

Mis tuleb teha

Kolmanda praktikumi ülesandeks on ehitada veebisüsteem, mille kaudu saab seamängu (seesama pig, mis teise praktikumi teemaks) üle võrgu brauseriga kahe inimese vahel mängida.

Sinu loodav süsteem on veidi sarnane näiteks facebookis mängitavale zynga pokkerile, kuid viimasest muidugi palju lihtsam.

Ülesanne koosneb kolmest üksteisega seotud pisikesest süsteemist:

  • Mänguhuviliste teadetahvel, kus saab näha mängukuulutusi ja sisestada oma mänguhuvi.
  • Teadetetahvlilt leitud partneriga mängu alustamine.
  • Kahe inimese omavaheline reaalne mäng üle võrgu.

Praktikumi arvestamine ja hindamine

Praktikum annab maksimaalselt 15 punkti.

Esimesed kaks osa (teadetetahvli tegemine ja sealt leitud partneriga mängu alustamine) on kohustuslik osa praktikumist ja nende korralik realiseerimine annab 7 punkti. Praktikumi ei arvestata, kui nendes osades on suuremad puudujäägid.

Omavahelise mängu realiseerimine (kolmas alamsüsteem) annab lisaks 8 punkti. Suuremate puudujääkide korral selles osas on praktikum siiski arvestatud.

Pane tähele, et selles praktikumis on kõigi ülesannete realiseerimiseks vajalik koodihulk väga väike, samas nõuab tõsist pingutust välja mõelda, kuidas kõiki asju täpselt teha ja esitada ja kuidas nad omavahel kokku käivad. Samuti võtab päris päris palju aega katsetamine ja esmatutvus võõra progekeelega.

Tehnoloogilised nõuded

  • Kõik serverirakendused tuleb realiseerida Pythonis. Vaata korra dokumentatsiooni-sisukorda, seejärel loe tutorialit.
  • Rakendus peab olema võrgus vabalt brauseriga ligipääsetav, mitte lihtsalt töötama näiteks sinu laptopis.
  • Serverarvutina võid - nagu teises praktikumis - kasutada kas dijkstrat või omaenda vabalt valitud lemmikserverit.
  • Serveris tuleb infot (loe järgmisest peatükist) hoida
    • ühises teadetetahvli failis.
    • iga mängu jaoks eraldi loodavates mängufailides.
  • Kasutada tuleb nimelt lihtsaid inimloetavaid faile, andmebaasimootoreid kasutada ei tohi (andmebaasimootorite jaoks on neljas praks).
  • Loodud veebilehed peavad sisaldama html-i, css-i ja javascripti (pluss pilte), javat ja flashi jne kasutada ei tohi.

Turvaküsimustega ei pea selles praksis tegelema! Seega on täiesti ok, kui mängija saab ise anda cgi-le parameetrid näiteks võõra mängu kohta ja sellega teiste mängu ära rikkuda, pettusi teha jne. Turvaküsimused oleks selle praksi jaoks lihtsalt liiga töö- ja tähelepanumahukad.

Miks skriptikeel (siin praksis Python) ja mitte C või Java? Eeskätt seepärast, et C ja Java jaoks juba on eraldi kursused, kus neid kasutatakse. Teiseks seepärast, et skriptikeeltes on väiksemaid/lihtsamaid veebirakendusi veidi mugavam kirjutada, kui kompileeritud keeltes.

Miks just Python? Vaata Tiobe progekeelte populaarsuse indeksit (ja lisaks ntx seda ja seda): kõige populaarsemad nn skriptikeeled on PHP ja Python. Kummagi jaoks on meil üks praktikum. PHP ja Python on üksteisest ka väga erinevad (Perl ja PHP on hoopis sarnasemad), seega on nad sobivad näited skriptikeelte spektri eri otstest.

Ülesande detailid

Kolm alamsüsteemi pevad olema järgmise funktsionaalsuse ja ehitusega:

Mänguhuviliste teadetetahvel

Teadetetahvlil on toodud mänguhuviliste info, igaühe juures mängu alustamise nupp ja lisaks vorm oma info sisestamiseks tahvlile.

Mänguhuviliste infot hoitakse serveris eraldi tahvli-failis csv kujul näiteks nii:

Peeter Lamp, lamp@www.ee, 200
Jaan Karu,jaan@karu.ee,1000
Mihkel Mets,mihkel@gmail.com,100


Oma mänguhuvi teadaandmiseks täidad tahvlil oleva vormi, kus (samamoodi kui teises praktikumis) on olemas väljad oma nime ja emaili ja mängupandava summa suurusega. Täidetud vormi saadad serveris olevale cgi rakendusele, kes lisab sinu info serveris olevasse tahvli-faili.

Saatmise järel antakse kasutajale tagasi html leht, mis ennast automaatselt X sekundi järel refreshib (pane X algul 1 sekundi peale) ja kontrollib, kas keegi on sinuga mängida soovinud (kuidas, seda vaata järgmiste osade kirjeldusest). Kui Y sekundi järel (algul sea Y näiteks 30 või 60 sekundi peale, peaasi, et saad seda kergesti koodis muuta) kedagi ei ilmu, siis kuvatakse uuesti tahvlit ja saad jälle oma mänguhuvi sisestada.

Kuulutuste vaatamiseks ehita järgmine cgi programm, mis loeb serveris olevat tahvli-faili ja genereerib sellest inimesele mugavalt loetava mängukuulutuste html lehe.

Lehel peab olema tabel mänguhuvilise nime, emaili ja mängupandava summa suurusega. Kuulutuste järjekord vali programmeerides ise. Kuulutused, mis on vanemad kui Y sekundit (sama Y mis eelmises lõigus!), ei ole enam aktiivsed ja neid ei kuvata üldse.

Igal teadetetahvli kuulutusel peab olema "mängi" nupp, millele vajutades alustatakse mängu.

Teadetetahvlilt leitud partneriga mängu alustamine

"Mängi" nupule vajutamise peale käivitakse serveris mängu alustamise programm, mis kõigepealt avab tahvlile liitumisega sarnase vormi, kuhu on infoks pandud kirja tahvlilt leitud isiku info, kellega mängu alustatakse (sh mängus olev rahasumma) ja millega küsitakse omakorda liituva mängija infot.

Vormile rakendatakse samasugust kontrolli, nagu teadetetahvliga liitumise vormile.

Vormi saatmisel serverisse ehitab server uue faili, kus hakkab hoidma mängijate ja mängu käigu infot: mõlemi mängija info ning info mängu seisu kohta: mõlemi mängija punktisummad, kelle käik, käigul oleva mängija ahela-punktisumma (st veeretamiste ahela summa). Algseis oleks näiteks selline:

Jaan Karu,jaan@karu.ee,1000
Mihkel Mets,mihkel@gmail.com
0
0
1
0

Seepeale vastab server mängu alustamise lehega, kus on kirjas mängijate info ja pakutakse võimalust teha esimest käiku.

Teadetetahvlilt leitud partneriga mängimine

Kahe inimese omavahelist mängu realiseeriv programm loeb ühenduse võtmise järel kõigepealt sisse mängufaili, näiteks

Jaan Karu,jaan@karu.ee,1000
Mihkel Mets,mihkel@gmail.com
30
38
1
10

kus 30 ja 38 oleks Jaani ja Mihkli punktisummad, 1 näitab, et käigul on Mihkel ja 10 on käigulolija hetke ahela-punktisumma.

Seejärel genereeritakse üks kahest võimalikust põhivariandist:

  • Kui ühenduse võtja ei ole parajasti käigul (vaadatakse mängufailist), antakse talle tagasi leht, kus kuvatakse viimast seisu ning ühtegi aktiivset nuppu ei ole, selle asemel on lehel info a la "praegu on käigul vastane, palun oota". Leht peab ennast iga X sekundi järel automaatselt uuendama (algul sea X ühe sekundi peale) selleks, et tuvastada, kas vastase mängukord on lõppenud või ei.
  • Kui ühenduse võtja on parajasti käigul, arvutab programm ühe juhusliku täringuviske, uuendab mängufailis mängija ahela-punkte (ja 1 viskamisel muudab ka mängija punktisummat ja käigujärjekorda) ning ehitab vastuseks uue lehele vastavalt uuendatud mängufaili seisule: visatud täring ja kas uus ahela-skoor või teade, et vise läheb üle vastasele.

Kui mängija otsustab visete ahela katkestada (st vajutab vastavale nupule vormil) uuendab programm mängufailis mängija punktisummat ja muudab käigulolijat.

Kui mänguprogramm tuvastab, et mäng on lõppenud, kuvab ta kummalegi mängijale lõppseisu ja võitja ja summa, mis üks peaks teisele maksma.

Igati OK on ka veidi täiustada käikude kuvamist, eeskätt selliselt, et parasjagu mitte käigul olevale mängijale kuvatakse ka teise mängija visatud täringud ja valikud (aga ta ise sellal midagi teha/muuta muidugi ei saa). Kohustuslik see samas ei ole.

Soovitused programmeerimiseks

Kõigepeal tutvu cgi programmide kirjutamise esmaste põhimõtetega loengumaterjalidest ja sisuliseks arusaamiseks loe cgi loengu materjalide alt teisi materjale lisaks.

Samuti on sul vaja veidi tutvuda linuxi käsurea elementaarkäskudega (kopeerimine, kustutamine, õiguste muutmine jne). Neid leiad googeldades, aga praksi jaoks peaks piisama sellest lühiõpetusest, millele tasub esimest korda lugedes kõrvale tutvuda selle tutoriali peatükkidega üks kuni viis.

Python

Kui sa ei ole varem Pythonis cgi-sid programmeerinud, tutvu kõigepealt Pythoni keele ja cgi teegiga: loe tutoriali algust (keerulised andmestruktuurid, moodulite ja klasside värgi võid esialgu lugemata jätta) ja cgi teegi õpetust.

Hea mõte on paralleelselt teha lahti Pythoni interpretaator ja katsetada kõrvale asju otse Pythoni käsureal. Kui su masinas Pythonit pole, siis installeeri!

Paljud kasulikud asjad on kirjas Pythoni teegi dokumentatsioonis. Konkreetsete küsimuste korral aitab tihti googeldamine.

NB! Pythonil on väga palju teeke ja mitmeid mittetriviaalseid keelekonstruktsioone. Sul ei lähe rõhuvat enamust nendest vaja, praktikumiks piisab väga esmastest asjadest (listid, stringid, numbrid) ning teekidest ei lähe samuti muud vaja, kui failide lugemine/kirjutamine, stringioperatsioonid ja cgi. Ära kuluta kohe aega kõigi Pythoni teekide ja detailide uurimisele, pigem alusta peale esmast tutvust koodi kirjutamist.

Arvesta, et dijkstra serveris on installeeritud Python 2.5.2, seega väldi Python 3 spetsiifiliste asjade kasutamist (vt erinevusi 2 ja 3 sarja vahel, muidu võib tekkida komplikatsioone programmide viimisel dijkstrasse. Kui sa midagi eriti keerulist ei tee, siis ei tohiks 3 sarja programmide ja 2 sarja interpretaatoriga siiski suuremaid probleeme tekkida.

Kirjuta mõni väike näiteprogramm ja katseta serveris, kuhu lõpuks süsteemi üles paned (kas siis dijkstra või oma isiklik lemmikserver).

Väga abiks on ka cgitb teegi kasutamine cgi-de mugavamaks debugimiseks: see on väga lihtne, tee seda tingimata.

Arendamine oma või arvutiklassi arvutitel

Kuna sinu süsteem peab lõpuks töötama avalikul veebiserveril, kuid arendust teed oma või arvutiklassi arvutitel, siis kuidas oleks mõistlik tööd korralda?

Peamised variandid on sellised:

  • Kirjutad kõik otse serveris, kasutades sisselogimiseks Putty-t (windows) või ssh-d (linux) ja serveris kasutad redaktoriks näiteks vi-d. Aitab, kui teed lahti mitu akent. Kõige universaalsem viis, aga nõuab vi õppimist, mis võtab aega.
  • Kirjutad faile oma masinas redaktoriga, mis lubab üle võrgu faile avada ja sulgeda, windowsis näiteks winscp abil. Katsetamiseks ja testimiseks hoiad lahti sisse logitud aknaid nagu eelmises variandis. See on suhteliselt mugav viis, kui sulle vi ei meeldi.
  • Arendad süsteemi võimalikult lõpuni windowsis valmis ja siis kannad üle serverisse ja teed vajalikke muutusi. Selleks installeeri oma arvutis python windowsile, arvutiklassides peaks see juba olemas olema. Väga abiks oleks seejuures ka veebiserveri installeerimine, näiteks
  • Kui sul on oma arvutis windows ja põhiliselt kasutad seda, siis kõige soovitavam ja pikemas perspektiivis mõistlikum viis on installeerida windowsile lisaks ubuntu linux wubi installeriga: see installer on täiesti harilik windowsi programm, mille pealepanekul tekib sinu masinasse windowsile lisaks linux. Masinat käivitades pakutakse valikut windowsi ja linuxi vahel. Niimoodi saad enda linuxi käivitada, seal apache käima panna ja arendada kogu asi enne avalikku serverisse kopeerimist valmis oma masinas. NB! Kogu see installeerimine ja süsteemiga tutvumine võtab algul paratamatult rohkem aega, kui ülaltoodud variandid: kaotad ajas lähiperspektiivis, võidad pikemas perspektiivis.
  • Ja loomulikult on võimalik kasutada oma masinas mõnda standalone unixt: linuxit, mõnda bsd-d, mac os X vms kas ainsa süsteemina või dual bootina. Kui kirjutad palju ja tihti koodi linuxi serverite jaoks, siis on see tõenäoliselt kõige mõistlikum variant.

Meeldetuletus: Python + CGI

Vormidelt saadetud data kasutamiseks on Pythonis tore imporditav moodul nimega cgi, mille funktsioonid aitavad vormiinfot söödavamal kujul sisse võtta.

Looge fail test2.py:

#!/usr/bin/python
import cgi

print "Content-type: text/html"
print

print "<html><head><title>test2.py</title></head><body><h1>Hello, World!</h1><p>Lisaparameeter oli "

formdata = cgi.FieldStorage()
if formdata.has_key("lisaparameeter"):
  print formdata['lisaparameeter'].value
else:
  print "puudu"

print ".</p></body>"

Vaadake faili brauseris:
http://dijkstra.cs.ttu.ee/~t0XXX/cgi-bin/test2.py

Parandage faili õigused omaette kirudes, et seda unustati teile teisel korral öelda ja vaadake uuesti.

http://dijkstra.cs.ttu.ee/~t0XXX/cgi-bin/test2.py http://dijkstra.cs.ttu.ee/~t0XXX/cgi-bin/test2.py?lisaparameeter=olemas

Tasub vaadata ka Kasulikke pythoni näitejuppe cgi jaoks.

Tegelik koodikirjutamine

Kõige olulisem soovitus: alusta ülesande esimesest osast - teadetetahvlist - ja realiseeri kõigepealt tema kõige lihtsam funktsionaalsus: teadetahvli kuvamine failist (algul ilma ajapiiranguta ja ridu väljadeks lahti hakkimata) ja seejärel kirje lisamine tahvlile. Kui see korralikult käib, siis täienda esialgset funktsionaalsust vastavalt tahvli nõuetele ülal.

Ära hakka korraga palju funktsionaalsust programmeerima: tee üks väike osa juurde, katseta läbi ja paranda, ning alles seejärel lisa järgmine tükk funktsionaalsust jne.

Kui tahvliosa on korras, alles siis lisa partneriga mängu alustamine ja kui see korras, asu programmeerima mängu ennast. Viimast tee jällegi väikeste tükkide kaupa.

Süsteemis on hulk funktsionaalsust, mis nõuaks justkui hulga erinevate cgi-de tegemist. Üks variant süsteemi ehitada ongi teha mitu cgi-d, teine variant on aga teha üks (või paar) cgi-d, mis saavad vormidelt ette hidden väljale pandud operatsiooninime (a la mypig.cgi?op=storetotable&name=John ...) ja siis kutsuvad välja operatsiooninimele vastavat funktsiooni sellessamas cgi-s.

Andmete esitamine ja kasutamine serveris

Nii tahvlifail kui mängufailid on soovitav luua õigustega, kus kõik võivad faili lugeda ja kirjutada. See on turvalisuse mõttes halb, aga arendamise/debugimise mõttes hea. Siis saad ligi ise ja saavad ligi sinu programmid. Käsurealt saad teha nii:

    chmod a+rw myfile.txt

Täiesti võimalik on olukord, kus Apache poolt käivitatav cgi programm ei ole nõus sinu kataloogis uusi faile tegema: siis tuleks kas muuta oma kataloogi õigusi kõigile loetavaks/kirjutatavaks või teha failid mõnda kataloogi, kus apachel on õigused (ntx /tmp, kuid siis tuleks nende nimele panna ntx sufiksiks sinu matriklinumber, et teistega segi ei läheks) või veidi veidra, aga praksis ok variandina luua ette valmis üks tahvlifail ja suur hulk mängufaile ja siis anda neile kõigile kõigi jaoks lugemis- ja kirjutamisõigused. Sel juhul ei pea sinu programmid üldse uusi faile tegema.

Tahvlifailile kirje lisamiseks võid kirjutada uue kirje lihtsalt faili lõppu uueks reaks. Vanu tahvlikirjeid pole kunagi vaja muuta!

Igale tahvlikirjele pane lisaväljana (ntx esimeseks) kaasa kirje loomise aeg, selle järgi hiljem filtreerid kirjeid ja katkestad refreshimist.

Tahvlifaili sisse lugemisel on kõige mugavam infot hoida kahemõõtmelises massiivis (listis), kus esimene indeks on rea number ja teine tulba number (või siis kasutada sisemise massiivi ehk listi asemel väljanimedega dictionary-t).

Faili lugemisel on üldiselt hea mõte kasutada f.readline() funktsiooni, tahvlifaili puhul võid soovi korral kasutada ka csv teeki, mille abil faili tabeliks lahti hakkimine nõuab veidi vähem koodikirjutamist (aga mitte tingimata palju vähem sinu aega).

Aja määramisel on sul kõige praktilisem kasutada time.time() funktsiooni (otsi see mahukast ajateegist välja: see annab lihtsalt aja ühe numbrina, sekundites peale aastat 1970 vms. Selle numbri saad siis lihtsalt tahvlifaili kirjutada. Lugemisel on seda samuti lihtne võrrelda (ja liita/lahutada) hetkeajaga.

NB! Ära hakka jändama keerukate aasta/minuti/ajatsooni jne jne meetoditega: need on keerulised, nende uurimine võtab hulga aega ja praksis neid vaja ei lähe (mis ei tähenda, et huvi korral ei võiks neid uurida :).

Stringi muutmine numbriks on lihtne (int("12") või float("12")) ning samamoodi on lihtne numbri muutmine stringiks (string(12) või string(12.5)).

Mängukirjele tuleb leiutada failinimi: kõige lihtsam on kasutada vastava tahvlikirje rea numbrit ja nimetada faile näiteks nii: 0.txt, 1.txt, 2.txt jne.

Mängimise ajal pead alati teadma, mis mänguga tegemist, st failinime või -numbrit. Seda on kõige lihtsam realiseerida nii, et cgi programm lisab html lehele hidden väljale (otsi pealkirja "Hidden") failinime või numbri, see saadetakse muu vormi sisuga alati serverisse kaasa ning sinu programm võtab siis failinime või -numbri selleltsamalt vormiväljalt.

Kuidas mängu alustamise nuppu teha? Genereeri serveris nupu kohale näiteks link, kus on parameetriga antud sellesama tahvlifaili vastav reanumber. See reanumber antakse siis mängualustamise lehte tegevale cgi-le, kes omakorda paneb selle sisse valmistehtud lehe vormi hidden-väljale.

Mängufaili muutmise asemel on lihtsam kõik faili read sisse lugeda, mälus vastav info ära muuta ja seejärel terve fail uuesti üle kirjutada. Mängufaili ridu saad hoida näiteks massiivis ehk listis stringidena rea numbrite järgi 0, 1, 2 jne positsioonidel.

Pole vaja hakata aegunud kirjeid tahvlifailis ja aegunud mängufaile ära kustutama: praksis sellist nõuet pole. Keelatud see aga arusaadavalt samuti pole.