(P) Sairaala

Tavoite: Opettelen modulaarisuutta olio-ohjelmoinnissa sekä sitä, miten luokat (oliot) voivat olla yhteydessä toisiinsa. Harjoittelen valmiin koodin lukemista sekä osoittimien käyttämistä ja dynaamista muistinhallintaa. Samalla kertaan STL:n säiliöitä. Myös ohjelman jakamista osiin funktioita ja luokkia käyttäen on syytä pohtia.

Ohjeita: Hae ohjelmakoodipohja: templates/10/hospital/ -> student/10/hospital/. Koodipohjassa on aika monta tiedostoa ja luokkaa, mutta ne ovat hyvin yksinkertaisia ja helposti ymmärrettäviä.

Pohjakoodissa on joitakin komentoja toteutettu valmiiksi. Voit aloittaa kokeilemalla, miten ne toimivat.

Tärkeää

Ennen kuin aloitat, lue huolellisesti koko tehtävänanto. Huomaa erityisesti kohdat Ohjelman toteuttaminen osissa (vaaditut commitit) sekä Erityisvaatimukset.

Kohdasta Arviointi huomaa erityisesti, että ohjelman pitää ensin läpäistä kaikki automaattitestit. Muuten työ ei mene assistenttien arvioitavaksi, eikä siitä saa pisteitä.

Huomautus

Tämä projekti tehdään itsenäisenä työskentelynä. Parityöskentely ei ole sallittua, vaan sitä pidetään plagiointina.

Toteuta valmiiksi ohjelma, joka käynnistyessään lukee käyttäjän antamia komentoja. Komennosta riippuen ohjelma joko tallentaa tietoja sopivaan tietorakenteeseen, poistaa niitä, tai tekee hakuja kyseiseen tietorakenteeseen.

Alkukommentti ja palautteen kieli

Samoin kuin ensimmäisessä projektissa, tässäkin projektissa vaaditaan alkukommentti. Tällä kertaa alkukommenttia ei anneta valmiina, vaan sinun pitää kirjoittaa se itse. Mallia voit katsoa ensimmäisen projektin tehtävänannosta (4.5 (P) Binairo).

Palautteen kieli valitaan palautuslaatikossa (tämän sivun lopussa). Palautteen kielenä on oletuksena suomi, mutta voit vaihtaa sen, jos haluat assistenttien antaman palautteen englanniksi. Kielivalinta määräytyy sen perusteella, minkä kielivalinnan olet tehnyt viimeisimmän palautuksen palautuslaatikossa.

Ohjelman toiminnan kuvaus

Ohjelmassa on luokat Hospital, CarePeriod, Person, Date ja Cli sekä moduuli Utils.

Luokan Person oliot (ilmentymät) voivat olla joko potilaita tai sairaalan henkilökuntaa (mutta eivät molempia). Henkilökuntaa ei ole eroteltu sen tarkemmin, esimerkiksi jaoteltu lääkäreihin ja hoitajiin. Henkilöiden tunnistamiseen voi käyttää nimeä (kuten on tehty esimerkkiajoissa alempana) tai mitä tahansa merkkijonoa.

Sairaalan henkilökunta on pysyvää. Rekrytoinnin jälkeen jäsenet säilyvät henkilökunnassa ohjelman loppuun asti. (Ohjelmassa on rekrytointikomento mutta ei komentoa henkilökunnan poistamiseen.) Potilaita voidaan sekä lisätä että poistaa. Potilasta lisättäessä luodaan myös uusi hoitojakso, ja kaikki luodut hoitojaksot säilyvät ohjelman loppuun asti. Samalla potilaalla voi olla useita hoitojaksoja ohjelman aikana. Toisin sanoen sama potilas voi tulla sairaalaan ja päästä sairaalasta useita kertoja ohjelman aikana.

Luokka CarePeriod kuvaa yhden potilaan yhden hoitojakson, mutta kuten edellä mainittiin, samalla potilaalla voi olla useita hoitojaksoja. Hoitojaksolla on ainakin alkamispäivämäärä, ja päättyneillä hoitojaksoilla on myös päättymispäivämäärä. Potilaan lisäksi hoitojaksolla on henkilökunnan jäseniä 0-n kappaletta. Sama henkilö voi olla työntekijänä usealla (saman potilaan tai eri potilaiden) hoitojaksolla.

Potilaalla voi myös olla lääkkeitä. Lääke on kuvattu ohjelmassa tietueena (struct) eikä luokkana, koska lääkkeellä ei ole mitään operaatioita. Kyseinen tietue löytyy luokasta Person. Lääkkeet ovat potilaskohtaisia, ja ne säilyvät hoitojaksosta toiseen. Kun (ja vain kun) potilas on hoitojaksolla sairaalassa, hänelle voidaan lisätä lääkkeitä tai poistaa niitä, mutta hoitojakson päättyessä potilaalla säilyy ne lääkkeet, jotka hänelle on määrätty ja joita ei ole poistettu. Sen sijaan potilasta hoitaneet henkilökunnan jäsenet ovat hoitojaksokohtaisia, ja eri hoitojaksoilla potilasta voivat hoitaa eri henkilöt (tai samat).

Luokka Cli (command line interpreter) kuvaa komentotulkin, eli se hallinnoi ohjelmassa käytettyjä komentoja. Moduuli Utils tarjoaa muutaman yleiskäyttöisen funktion. Kumpaankaan näistä (Cli tai Utils) ei tarvitse tehdä mitään muutoksia. Näissä moduuleissa tarkistetaan esimerkiksi, antaako käyttäjä komennolle oikean määrän oikean tyyppisiä parametreja. Tarkistukset on toteutettu valmiiksi, joten niistä ei tarvitse huolehtia.

Luokka Hospital hallinnoi koko ohjelmaa, ja siitä luodaan vain yksi olio. Sairaalassa on potilaita, henkilökuntaa sekä hoitojaksoja.

Luokkien väliset suhteet näkyvät alla olevassa kuvassa.

../../_images/hospital_fi.png

Luokasta A luokkaan B on piirretty nuoli, jos luokka A tuntee luokan B. Tällöin luokan A attribuuttina on luokan B ilmentymä (tai osoitin siihen). Luokka Cli ja moduuli Utils on jätetty kuvasta pois, koska ne ovat apumoduuleja, eivätkä varsinaisesti kuulu sairaalajärjestelmään. Nuolten päälle on tarvittaessa kirjoitettu selvennys, joka voi auttaa ymmärtämään, miksi yhteys tarvitaan.

Kuvassa syntymäpäivänuoli on katkoviivalla, koska tällainen yhteys voisi hyvin luontevasti olla olemassa, mutta ohjelmassa tätä ei oikeasti käytetä.

Kuten edellä kerrottiin, sama henkilö voi esiintyä useassa eri paikassa. Esimerkiksi sama potilas voi olla potilaana usealla hoitojaksolla, ja sama henkilö voi työskennellä usealla hoitojaksolla. Tästä syystä tarvitset osoittimia. Kukin henkilö luodaan vain kerran, ja jos/kun henkilöä tarvitaan eri paikoissa, käytetään osoitinta kyseiseen henkilöön.

Yleistä asiaa komennoista

Ohjelmassa on aika monta komentoa, mutta sinun tehtävänäsi on toteuttaa vain noin puolet niistä.

Ohjelmalle ei anneta syötetiedostoa, joten alussa sairaala on tyhjä, eli siellä ei ole henkilökuntaa eikä potilaita. Lisäykset (samoin kuin poistot) tehdään komennoilla. Komentojen joukossa on kuitenkin READ_FROM, joka lukee komennot annetusta tiedostosta (ks. komento 3 valmiiksi toteutetuista komennoista).

Ohjelman käynnistyessä sekä aina kun käyttäjältä odotetaan syötettä, tulostetaan rivin alkuun:

Hosp>

Tähän kehotteeseen ohjelman käyttäjä voi syöttää komentoja (tai vain painaa Enter-näppäintä). Komennot voidaan kirjoittaa isoilla tai pienillä kirjaimilla tai käyttää sekaisin isoja ja pieniä kirjaimia. Näin ollen lopetuskomennon sallittuja muotoja ovat esimerkiksi QUIT, quit ja Quit. Komennoilla on myös muutaman kirjaimen pituiset lyhenteet, jotka on kerrottu HELP-komennon yhteydessä. Esimerkiksi lopetuskomennon lyhenne on Q (tai pienellä kirjoitettuna q).

Kullakin komennolla on määrätty määrä parametreja. Parametrit näkyvät kunkin komennon kuvauksessa kulmasulkujen sisällä. Jos parametri koostuu useammasta sanasta, se pitää kirjoittaa lainausmerkkien sisään. Jos parametreja on liikaa tai liian vähän, tulostetaan virheilmoitus:

Error: Wrong amount of parameters.

Parametrien määrän tarkastaminen on toteutettu pohjakoodiin, joten siitä ei tarvitse huolehtia.

Jos käyttäjä antaa jonkin muun komennon kuin ohjelman tunteman, tulostetaan virheilmoitus:

Hosp> jotakin
Error: Unknown commands given.
Hosp>
Hosp>

Kun käyttäjän syöttämä komento on suoritettu, ohjelma tulostaa kehotteen uudelleen. Tätä jatketaan kunnes käyttäjä syöttää komennon QUIT.

Jos käyttäjä pelkästään painaa Enter-näppäintä antamatta mitään komentoa, kehote tulostuu uudelleen, kuten näkyy viimeisimmässä esimerkissä. Tässä mielessä ohjelma toimii kuten Linux-etätyöpöydän komentorivi.

Seuraavissa kahdessa osiossa on ensin kuvattu toteutetut komennot ja sen jälkeen toteutettavat kommennot. Luonnollisesti ensimmäistä näistä osioista ei tarvitse lukea kovin tarkkaan, koska riittää osata käyttää komentoja. Jälkimmäinen osio taas tulee lukea tarkasti, koska tehtäväsi on toteuttaa nämä komennot.

Toteutetut komennot virheilmoituksineen

  1. QUIT - Ohjelman suoritus päätty paluuarvoon EXIT_SUCCESS ilman, että ohjelma tulostaa mitään. Komennolla ei ole parametreja, mutta jos käyttäjä antaa parametreja, niitä ei huomioida.

  2. HELP - Komento tulostaa kaikki käytössä olevat ole komennot lyhenteineen. Kullakin rivillä näkyy ensin komennon kuvaus ja kaksoispisteen jälkeen komento ja sen lyhenne:

    Hosp> HELP
    Recruit staff : RECRUIT R
    Take patient to hospital : ENTER E
    Take patient from hospital : LEAVE L
    Assign staff for a patient : ASSIGN_STAFF AS
    Add medicine for a patient : ADD_MEDICINE AM
    Remove medicine from a patient : REMOVE_MEDICINE RM
    Print patient's info : PRINT_PATIENT_INFO PPI
    Print care periods per staff : PRINT_CARE_PERIODS PCPS
    Print all used medicines : PRINT_ALL_MEDICINES PAM
    Print all staff : PRINT_ALL_STAFF PAS
    Print all patients : PRINT_ALL_PATIENTS PAP
    Print current patients : PRINT_CURRENT_PATIENTS PCP
    Set date : SET_DATE SD
    Advance date : ADVANCE_DATE AD
    Read : READ_FROM RF
    Help : HELP H
    Quit : QUIT Q
    Hosp>
    
  3. READ_FROM <tiedosto> - Komennolla voidaan lukea annetusta tiedostosta komennot parametreineen, mikä voi helpottaa ohjelman testaamista. Pohjakoodien mukana saat tiedoston assignment.input, joka sisältää esimerkkiajoissa esiintyneitä komentoja. Tiedostossa on käytetty komentojen lyhenteitä. Tiedoston sisältö on seuraava:

    e Pekka
    r Jussi
    as Jussi Pekka
    ad 2
    l Pekka
    e Pekka
    am Burana 200 2 Pekka
    q
    

    Kyseinen tiedosto voidaan antaa lukukomennolle parametrina seuraavasti:

    Hosp> READ_FROM assignment.input
    Input read from file: assignment.input
    Hosp>
    

    Tällä tavalla saat suoritettua kaikki tiedoston sisältämät onnistuneet komennot nopeammin, kuin jos kirjoittaisit ne yksitellen kehotteen jälkeen.

    Huomaa, että tiedosto assignment.input sisältää myös sellaisia komentoja, joita ei ole toteutettu pohjakoodissa.

    Jos tiedostossa oleva komento on virheellinen, esimerkiksi parametreja on väärä määrä, mitään virheilmoitusta ei tulostu, kuten komentoriviltä tulostuisi. Lisäksi tiedostossa täytyy olla viimeisenä komentona QUIT (tai sen lyhenne), sillä muuten tiedoston lukeminen ei lopu koskaan. Noudata siis edellä kerrottuja ohjeita, jos käytät itse kirjoittamaasi syötetiedostoa testatessasi ohjelmaa. Ohjelman testaaminen (tavalla tai toisella) on erittäin suositeltavaa.

    Jos komennon parametriksi annetaan tuntematon tiedosto, tulostetaan virheilmoitus:

    Hosp> READ_FROM ei_loydy.txt
    Error: Can't read given file.
    

    Tämän jälkeen ohjelman toiminta päättyy paluuarvoon EXIT_SUCCESS.

  4. SET_DATE <pp> <kk> <vvvv> - Komennolla voidaan asettaa uudeksi päivämääräksi päivämäärä pp.kk.vvvv. (Nykyiseksi päivämääräksi on asetettu 24.2.2024 tiedostossa utils.hh, ja halutessasi voit muuttaa sitä myös koodiin, mutta tämä komento hoitaa saman asian.) Komennolla on kolme kokonaislukuparametria. Jos päivä (tai kuukausi) on numeroltaan 1, se voidaan antaa joko muodossa 1 tai 01, ja sama pätee muihin 1-numeroisiin lukuihin. Esimerkiksi:

    Hosp> SET_DATE 1 04 2024
    Date has been set to 1.4.2024
    Hosp>
    

    Jos parametriksi annetaan liian suuri luku, joka ei sovi päiväksi tai kuukaudeksi, kyseiseksi luvuksi asetetaan 1. Esimerkiksi:

    Hosp> SET_DATE 32 13 2024
    Date has been set to 1.1.2024
    Hosp>
    

    Jos jokin parametreista on jotakin muuta kuin positiivinen kokonaisluku, ohjelma tulostaa virheilmoituksen:

    Hosp> SET_DATE 1 3 w
    Error: Wrong type of parameters.
    
  5. ADVANCE_DATE <ei-negatiivinen kokonaisluku> - Komennolla voidaan siirtää nykyistä päivämäärä parametrina annetun ei-negatiivisen kokonaisluvun verran päiviä eteenpäin. Jos komennolle antaa parametriksi nollan, saa selville nykyisen päivämäärän. Esimerkiksi:

    Hosp> ADVANCE_DATE 0
    New date is 24.2.2024
    Hosp>
    

    Varsinainen siirtäminen tapahtuu seuraavasti:

    Hosp> ADVANCE_DATE 7
    New date is 2.3.2024
    Hosp>
    

    Jos parametriksi annetaan jotakin muuta kuin ei-negatiivinen kokonaisluku, tulostetaan virheilmoitus:

    Hosp> ADVANCE_DATE -1
    Error: Wrong type of parameters.
    Hosp>
    
  6. RECRUIT <id> - Komennolla voi lisätä sairaalan henkilökuntaan uuden jäsenen. Parametrina annetaan tunniste, esimerkiksi nimi. (Annettua tunnistetta voidaan käyttää parametrina muissa komennoissa.) Esimerkki henkilökunnan lisäämisestä:

    Hosp> RECRUIT Jussi
    A new staff member has been recruited.
    Hosp>
    

    Jos samanniminen henkilö on henkilökunnassa jo ennestään, tulostetaan virheilmoitus:

    Hosp> RECRUIT Jussi
    Error: A new staff member has been recruited.
    Hosp> RECRUIT Jussi
    Error: Already exists: Jussi
    Hosp>
    
  7. PRINT_ALL_STAFF - Komento tulostaa sairaalaan rekrytoidun henkilökunnan. Jäsenet tulostetaan allekkain aakkosjärjestyksessä. Jos henkilökuntaa ei ole yhtään, tulostetaan None. Esimerkiksi:

    Hos> PRINT_ALL_STAFF
    None
    Hosp> RECRUIT Jussi
    A new staff member has been recruited.
    Hosp>  PRINT_ALL_STAFF
    Jussi
    Hosp> RECRUIT Jukka
    A new staff member has been recruited.
    Hosp>  PRINT_ALL_STAFF
    Jukka
    Jussi
    
  8. ADD_MEDICINE <lääkkeen nimi> <vahvuus> <annostus> <potilaan id> - Komennolla potilaalle voidaan lisätä lääke, jonka vahvuus (milligrammoina) ja annostus annetaan kokonaislukuina.

    Lääkkkeitä voidaan määrätä vain sellaisille potilaille, jotka ovat tällä hetkellä sairaalassa. Seuraavissa esimerkeissä oletetaan, että Pekka on potilaana sairaalassa:

    Hosp> ADD_MEDICINE Burana 200 2 Pekka
    Medicine added for: Pekka
    Hosp>
    

    Jos potilaalle on jo ennestään määrätty kyseinen lääke, komennolla voidaan muuttaa lääkemääräystä (vahvuutta ja/tai annostusta). Jos koko lääkemääräys (nimi, vahvuus, annostus) on sama kuin potilaalla on ollut ennestään, ei tapahdu mitään, eikä mitään virheilmoitusta anneta. Esimerkiksi:

    Hosp> ADD_MEDICINE Burana 400 2 Pekka
    Medicine added for: Pekka
    Hosp> ADD_MEDICINE Burana 400 2 Pekka
    Medicine added for: Pekka
    Hosp>
    

    Jos vahvuudeksi tai annostukseksi annetaan jotakin muuta kuin kokonaislukuja, tulostetaan virheilmoitus:

    Hosp> ADD_MEDICINE Voltaren xx yy Pekka
    Error: Wrong type of parameters.
    Hosp>
    

    Jos parametrina annettua potilasta ei löydy (hän ei ole sillä hetkellä sairaalassa), tulostetaan virheilmoitus:

    Hosp> ADD_MEDICINE Voltaren 100 1 joku
    Error: Can't find anything matching: joku
    Hosp>
    
  9. REMOVE_MEDICINE <lääkkeen nimi> <potilaan id> - Komennolla potilaalta voidaan poistaa lääke. Lääkkeitä voi poistaa vain sairaalassa tällä hetkellä olevilta potilailta. Alla olevissa esimerkeissä oletaan, että Pekka on potilaana sairaalassa:

    Hosp> REMOVE_MEDICINE Burana Pekka
    Medicine removed from: Pekka
    Hosp>
    

    Jos kyseistä lääkettä ei ole määrätty potilaalle, ei tapahdu mitään, eikä mitään virheilmoitusta anneta. Esimerkiksi:

    Hosp> REMOVE_MEDICINE Burana Pekka
    Medicine removed from: Pekka
    Hosp> REMOVE_MEDICINE Burana Pekka
    Medicine removed from: Pekka
    Hosp>
    

    Jos parametrina annettua potilasta ei löydy (hän ei ole sillä hetkellä sairaalassa), tulostetaan virheilmoitus:

    Hosp> REMOVE_MEDICINE Burana joku
    Error: Can't find anything matching: joku
    Hosp>
    

Toteutettavat komennot virheilmoituksineen

  1. ENTER <id> - Komennolla voi lisätä uuden potilaan sairaalaan. Parametrina annetaan tunniste, esimerkiksi potilaan nimi. (Annettua tunnistetta voidaan käyttää parametrina muissa komennoissa.) Esimerkki potilaan lisäämisestä:

    Hosp> ENTER Pekka
    A new patient has entered.
    Hosp>
    

    Potilaan saapuessa sairaalaan luodaan myös uusi hoitojakso, jonka alkupäivämääräksi laitetaan Utils-moduulin today-muuttujan sen hetkinen arvo.

    Jos samanniminen potilas on jo ennestään, tulostetaan virheilmoitus:

    Hosp> ENTER Pekka
    A new patient has entered.
    Hosp> ENTER Pekka
    Error: Already exists: Pekka
    Hosp>
    

    Kuitenkin jos potilas välillä lähtee sairaalasta (komennolla LEAVE), hänet voi lisätä uudelleen (uudelle hoitojaksolle). Tällaisessa tapauksessa ei luoda uutta potilasta, vaan ohjelman pitää säilyttää tiedot kaikista sairaalassa joskus olleista potilaista. Riittää kuitenkin säilyttää vain saman ajon aikana syntyneet tiedot. Jos ohjelma käynnistetään uudelleen, sairaalassa ei aluksi ole henkilökuntaa eikä potilaita.

    Uusi hoitojakso luodaan joka tapauksessa riippumatta siitä, onko potilas ollut ennestään sairaalassa vai ei.

  2. LEAVE <id> - Komennolla voi poistaa potilaan sairaalasta, mutta tiedot hänen hoitojaksostaan säilytetään (hoitojakson päivämäärät sekä henkilökunta). Esimerkiksi:

    Hosp> LEAVE Pekka
    Patient left hospital, care period closed.
    Hosp>
    

    Kun potilas poistuu sairaalasta, hänen nykyinen hoitojaksonsa päättyy, jolloin hoitojaksolle voidaan asettaa päättymispäiväksi nykyinen päivämäärä.

    Jos poistettavaa potilasta ei löydy (hän ei ole tällä hetkellä sairaalassa), tulostetaan virheilmoitus:

    Hosp> LEAVE Pekka
    Patient left hospital, care period closed.
    Hosp> LEAVE Pekka
    Error: Can't find anything matching: Pekka
    Hosp>
    

    Seuraavassa esimerkissä näkyy, miten potilas voi ensin tulla sairaalaan ja sitten lähteä ja tulla taas takaisin:

    Hosp> ENTER Pekka
    A new patient has entered.
    Hosp> ENTER Pekka
    Error: Already exists: Pekka
    Hosp> LEAVE Pekka
    Patient left hospital, care period closed.
    Hosp> ENTER Pekka
    A new patient has entered.
    Hosp>
    
  3. ASSIGN_STAFF <henkilökunnan jäsenen id> <potilaan id> - Komennolla voidaan sijoittaa annettu henkilökunnan jäsen työskentelemään annetun potilaan nykyisellä hoitojaksolla. Esimerkiksi:

    Hosp> ENTER Pekka
    A new patient has entered.
    Hosp> RECRUIT Jussi
    A new staff member has been recruited.
    Hosp> ASSIGN_STAFF Jussi Pekka
    Staff assigned for: Pekka
    Hosp>
    

    Jos sama henkilökunnan jäsen yritetään sijoittaa samalle potilaalle uudelleen, ei tapahdu mitään, eikä mitään virheilmoitusta anneta:

    Hosp> ASSIGN_STAFF Jussi Pekka
    Staff assigned for: Pekka
    Hosp> ASSIGN_STAFF Jussi Pekka
    Staff assigned for: Pekka
    

    Seuraavassa esimerkissä oletetaan, että yllä olevat komennot on suoritettu ensin. Jos henkilökunnan jäsentä tai potilasta ei löydy, ohjelma tulostaa alla näkyvän virheilmoituksen:

    Hosp> ASSIGN_STAFF Jukka Pekka
    Error: Can't find anything matching: Jukka
    Hosp>
    Hosp> LEAVE Pekka
    Patient left hospital, care period closed.
    Hosp> ASSIGN_STAFF Jussi Pekka
    Error: Can't find anything matching: Pekka
    Hosp>
    Hosp> ASSIGN_STAFF Jukka Pekka
    Error: Can't find anything matching: Jukka
    Hosp>
    

    Yleisemmin sanottuna, jos ensimmäistä tunnistetta (id) ei löydy, ilmoitetaan siitä. Jos ensimmäinen löytyy mutta toinen ei, ilmoitetaan toisesta. Jos kumpaakaan ei löydy, ilmoitetaan vain ensimmäisen puuttumisesta.

  4. PRINT_PATIENT_INFO <potilaan id> - Komento tulostaa tiedot potilaan kaikista hoitojaksoista sekä potilaan lääkityksen. Hoitojaksoista tulostetaan henkilökunta sekä alkupäivämäärä, päättyneistä hoitojaksoista tulostetaan lisäksi myös päättymispäivämäärä. Hoitojaksot tulostetaan aikajärjestyksessä (aikaisempi ensin). Henkilökunta ja lääkkeet tulostetaan aakkosjärjestyksessä. Jos henkilökuntaa tai lääkkeitä ei ole, niiden kohdalle tulostetaan None. Esimerkiksi:

    Hosp> ENTER Pekka
    A new patient has entered.
    Hosp> PRINT_PATIENT_INFO Pekka
    * Care period: 24.2.2024 -
      - Staff: None
    * Medicines: None
    Hosp>
    Hosp> ADD_MEDICINE Burana 200 2 Pekka
    Medicine added for: Pekka
    Hosp> ADD_MEDICINE Panadol 500 1 Pekka
    Medicine added for: Pekka
    Hosp>
    Hosp> RECRUIT Jussi
    A new staff member has been recruited.
    Hosp> RECRUIT Jukka
    A new staff member has been recruited.
    Hosp>
    Hosp> ASSIGN_STAFF Jussi Pekka
    Staff assigned for: Pekka
    Hosp> ASSIGN_STAFF Jukka Pekka
    Staff assigned for: Pekka
    Hosp>
    Hosp> PRINT_PATIENT_INFO Pekka
    * Care period: 24.2.2024 -
      - Staff: Jukka Jussi
    * Medicines:
      - Burana 200 mg x 2
      - Panadol 500 mg x 1
    Hosp>
    Hosp> ADVANCE_DATE 2
    New date is 26.2.2024
    Hosp> LEAVE Pekka
    Patient left hospital, care period closed.
    Hosp> ADVANCE_DATE 3
    New date is 29.2.2024
    Hosp>
    Hosp> ENTER Pekka
    A new patient has entered.
    Hosp> PRINT_PATIENT_INFO Pekka
    * Care period: 24.2.2024 - 26.2.2024
      - Staff: Jukka Jussi
    * Care period: 29.2.2024 -
      - Staff: None
    * Medicines:
      - Burana 200 mg x 2
      - Panadol 500 mg x 1
    Hosp>
    

    Jos annettua potilasta ei löydy, ohjelma tulostaa virheilmoituksen:

    Hosp> PRINT_PATIENT_INFO joku
    Error: Can't find anything matching: joku
    Hosp>
    

    Komento toimii kaikilla sairaalassa joskus olleilla potilailla. Toisin sanoen potilas jää löytymättä vain, jos hän ei ole koskaan ollut sairaalassa.

    Kuten ohjelman toiminnan kuvauksessa kerrottiin, lääkitys on potilaskohtaista, eli se ei riipu hoitojaksoista. Potilaalle sijoitetut henkilökunnan jäsenet taas ovat hoitojaksokohtaisia.

  5. PRINT_CARE_PERIODS <henkilökunnan jäsenen id> - Komento tulostaa hoitojaksot, joilla annettu henkilökunnan jäsen on työskennellyt tai työskentelee. Hoitojaksoista tulostetaan potilas sekä alkupäivämäärä, päättyneistä hoitojaksoista tulostetaan lisäksi myös päättymispäivämäärä. Hoitojaksot tulostetaan aikajärjestyksessä (aikaisempi ensin). Jos annettu henkilökunnan jäsen on rekrytoitu, mutta häntä ei ole sijoitettu työskentelemään kenenkään potilaan hoitojaksolle (komennolla ASSIGN_STAFF), ohjelma tulostaa None. Esimerkiksi:

    Hosp> RECRUIT Jussi
    A new staff member has been recruited.
    Hosp> PRINT_CARE_PERIODS Jussi
    None
    Hosp> ENTER Pekka
    A new patient has entered.
    Hosp> ENTER Matti
    A new patient has entered.
    Hosp> ASSIGN_STAFF Jussi Pekka
    Staff assigned for: Pekka
    Hosp> ASSIGN_STAFF Jussi Matti
    Staff assigned for: Matti
    Hosp> ADVANCE_DATE 2
    New date is 26.2.2024
    Hosp> LEAVE Matti
    Patient left hospital, care period closed.
    Hosp>
    Hosp> PRINT_CARE_PERIODS Jussi
    24.2.2024 -
    * Patient: Pekka
    24.2.2024 - 26.2.2024
    * Patient: Matti
    Hosp>
    

    Jos annettua henkilökunnan jäsentä ei löydy, ohjelma tulostaa virheilmoituksen:

    Hosp> PRINT_CARE_PERIODS joku
    Error: Can't find anything matching: joku
    Hosp>
    
  6. PRINT_ALL_MEDICINES - Komento tulostaa kaikkien sairaalassa joskus olleiden (myös nykyisten) potilaiden tällä hetkellä käytössä olevat lääkkeet. Lääkkeet tulostetaan aakkosjärjestyksessä, ja kustakin lääkkeestä kerrotaan, kenelle sitä on määrätty. Potilaat, joilla on käytössä sama lääke, tulostetaan aakkosjärjestyksessä. Lääkkeet ovat samoja, jos niillä on sama nimi, vahvuuksista tai annostuksista ei välitetä. Jos yhtään lääkettä ei ole käytössä, tulostetaan None. Esimerkiksi:

    Hosp> PRINT_ALL_MEDICINES
    None
    Hosp> ENTER Pekka
    Hosp> ADD_MEDICINE Burana 200 2 Pekka
    Medicine added for: Pekka
    Hosp> ADD_MEDICINE Panadol 500 1 Pekka
    Medicine added for: Pekka
    Hosp>
    Hosp> ENTER Matti
    Hosp> ADD_MEDICINE Burana 400 2 Matti
    Medicine added for: Matti
    Hosp>
    Hosp> PRINT_ALL_MEDICINES
    Burana prescribed for
    * Matti
    * Pekka
    Panadol prescribed for
    * Pekka
    Hosp>
    Hosp> REMOVE_MEDICINE Burana Pekka
    Medicine removed from: Pekka
    Hosp> PRINT_ALL_MEDICINES
    Burana prescribed for
    * Matti
    Panadol prescribed for
    * Pekka
    Hosp>
    Hosp> LEAVE Pekka
    Patient left hospital, care period closed.
    Hosp> PRINT_ALL_MEDICINES
    Burana prescribed for
    * Matti
    Panadol prescribed for
    * Pekka
    Hosp>
    
  7. PRINT_ALL_PATIENTS - Komento tulostaa tiedot kaikista sairaalassa joskus olleista potilaista, myös nykyisistä. Potilaiden tiedot tulostetaan potilaan tunnisteen (id) mukaisessa aakkosjärjestyksessä. Jos sairaalassa ei ole yhtään potilasta, tulostetaan None.

    Komento antaa saman tulostuksen kuin komento PRINT_PATIENT_INFO, mutta ikään kuin kyseinen komento suoritettaisiin niin monta kertaa, kuin sairaalassa on ollut potilaita. Ennen kunkin potilaan tietojen tulostamista, tulostetaan kyseisen potilaan tunniste (id).

    Jos oletaan, että edellisen komennon (PRINT_ALL_MEDICINES) esimerkkiajon mukaiset toiminnot on suoritettu, ohjelma tulostaa:

    Hosp> PRINT_ALL_PATIENTS
    Matti
    * Care period: 24.02.2024 -
      - Staff: None
    * Medicines:
      - Burana 400 mg x 2
    Pekka
    * Care period: 24.02.2024 - 24.02.2024
      - Staff: None
    * Medicines:
      - Panadol 500 mg x 1
    Hosp>
    

    Jos sairaalassa ei olisi koskaan ollut yhtään potilasta, komennon tulostus näyttäisi tältä:

    Hosp> PRINT_ALL_PATIENTS
    None
    Hosp>
    
  8. PRINT_CURRENT_PATIENTS - Komento tulostaa tiedot kaikista sairaalan nykyisistä potilaista. Potilaiden tiedot tulostetaan potilaan nimen mukaisessa aakkosjärjestyksessä.

    Komento antaa saman tulostuksen kuin komento PRINT_PATIENT_INFO, mutta ikään kuin kyseinen komento suoritettaisiin niin monta kertaa, kuin sairaalassa on tällä hetkellä potilaita. Ennen kunkin potilaan tietojen tulostamista, tulostetaan kyseisen potilaan nimi.

    Jos oletaan, että aikaisemman komennon (PRINT_ALL_MEDICINES) esimerkkiajon mukaiset toiminnot on suoritettu, ohjelma tulostaa:

    Hosp> PRINT_CURRENT_PATIENTS
    Matti
    * Care period: 24.02.2024 -
      - Staff: None
    * Medicines:
      - Burana 400 mg x 2
    Hosp>
    

    Jos sairaalassa ei olisi tällä hetkellä yhtään potilasta, komennon tulostus näyttäisi tältä:

    Hosp> PRINT_CURRENT_PATIENTS
    None
    Hosp>
    

Kun ohjelma toiminta lopetaan, se tulostaa vielä henkilöt, joille varattu tila vapautetaan. Tämä voi olla hyödyllistä, kun testaat ohjelmaa omilla testeilläsi. Jos ohjelmassa on käytetty edellisissä esimerkeissä esiintyneitä henkilöitä, tulostetaan:

Hosp> QUIT
Person Jukka destructed.
Person Jussi destructed.
Person Matti destructed.
Person Pekka destructed.
Press <RETURN> to close this window...

Automaattitesteissä oletetaan kuitenkin, että tällaisia tulostuksia ei ole, joten ennen Plussa-palautusta poista luokan Person purkajasta sen ainoa koodirivi.

Ohjelman moduulit

Koodipohja koostuu pääohjelmamoduulista sekä kuudesta muusta moduulista. Pääohjelmamoduuli on hyvin yksinkertainen. Se vain käynnistää komentotulkin (command line interpreter, CLI), joka pyörii niin kauan kuin käyttäjä antaa komennon QUIT. Pääohjelmaa ei ole tarpeen muuttaa.

Ohjelmaan kuuluu moduuli Utils, joka ei ole luokka vaan nimiavaruus. Se tarjoaa tutun apufunktion split, funktion is_numeric sekä päivämäärän today. Päivämäärän arvoa voit halutessasi päivittää tiedostosta utils.hh. Tällä hetkellä päivämääränä on 24.2.2024. Muilta osin tätä moduulia ei ole tarpeen muuttaa. Päivämäärää voi muuttaa myös komennolla SET_DATE.

Komentotulkki eli luokka Cli on määritelty ja toteutettu tiedostoissa cli.hh ja cli.cpp. Luokka on annettu koodipohjassa valmiina, eikä siihen tarvitse tehdä muutoksia. Luokan tehtävänä on tunnistaa komennot käyttäjän syötteistä. Kaikki komennot toteutetaan luokassa Hospital. Komentotulkin otsikkotiedostossa on komentovektori, joka sisältää funktio-osoittimia. Sen perusteella näet missä Hospital-luokan jäsenfunktiossa mikäkin komento on toteutettu. Muilta osin komentotulkin toimintaa ei ole välttämätöntä ymmärtää. Erityisesti funktio-osoittimien osaamista/ymmärtämistä ei vaadita.

Luokka Date kuvaa päivämääriä. Kukin päivämäärän osa (päivä, kuukausi, vuosi) on esitetty kokonaislukuna. Päivämäärää voidaan siirtää parametrina saadun päivälukumäärän verran eteenpäin metodilla advance. Metodi is_default kertoo, onko päivämäärä oletuspäivämäärä eli päivämäärä, jossa päivän, kuukauden ja vuoden arvot ovat nollia. Jos hoitojakso ei ole vielä päättynyt, sen loppupäivämäärä on oletuspäivämäärä. Metodia is_default voi siis käyttää selvittämään, onko hoitojakso päättynyt. Lisäksi Date-luokassa on metodit päivämäärän asettamiseen ja tulostamiseen. Päivämäärien yhtäsuuruutta voidaan vertailla. Luokalla on myös operaattori <: päivämäärä a on pienempi kuin päivämäärä b, jos a edeltää päivämäärää b. (Vertailuoperaattorifunktioille ei nykyisessä koodissa ole käyttöä.) Luokkaan Date ei tarvitse tehdä muutoksia.

Luokka Person kuvaa henkilöitä: potilaita ja sairaalan henkilökuntaa. Henkilöt tunnistetaan attribuutin id_ avulla. Edellä olevissa esimerkeissä tunnisteina käytettiin nimiä. Tällöin henkilöillä pitää olla yksikäsitteiset nimet. Periaatteessa potilas ja henkilökunnan jäsen voisivat olla samannimisiä, mutta silloin heidät tulkittaisiin eri henkilöiksi (eri olioiksi). Luokalla Person on myös attribuutti date_of_birth_, mutta sitä ei välttämättä tarvittaisi. Luokan attribuutti medicines_ on map, joka sisältää henkilölle (potilaalle) määrätyt lääkkeet. Henkilökunnalle ei pystytä lisäämään lääkkeitä, koska lääkkeitä voi määrätä vain sairaalassa sillä hetkellä oleville potilaille. Luokkaa Person ei tarvitse muuttaa, ellet sitten halua lisätä sinne metodeita, joita kutsut muihin luokkiin toteuttamistasi metodeista. (Muista kuitenkin poistaa luokan purkajasta tulostusrivi, kuten neuvottiin edellisen osion lopussa.)

Ohjelmassa on myös luokka CarePeriod, joka kuvaa hoitojaksoja. Luokan toteuttaminen on jätetty sinun tehtäväksesi.

Luokka Hospital kuvaa sairaalaa (joita yleisesti voisi olla useitakin mutta joita tässä ohjelmassa on vain yksi). Luokassa on tiedot kaikista sairaalan hoitojaksoista sekä henkilöistä, jotka voivat olla potilaita tai henkilökuntaa. Näitä tietoja varten luokassa on säiliöt, joihin tallennetaan uusia alkioita käyttäjän komentojen perusteella. Kaikki sairaalaan kohdistuvat komennot toteutetaan tässä luokassa.

Huomaa, että samalla potilaalla voi olla useita hoitojaksoja, ja sama henkilö voi työskennellä usealla hoitojaksolla. Tällaisissa tapauksissa luodaan vain yksi Person-olio, johon on osoittimet useasta eri paikasta (tietorakenteesta). Tällaisessa tilanteessa osoittimet ovat välttämättömiä.

Moduulien (luokkien) väliset suhteet tulevat ilmi tehtävänannon alussa esitetystä kuvasta.

Tarkempi tehtävänanto

Tehtävänäsi on täydentää luokat Hospital ja CarePeriod niin, että komennot toimivat edellä kuvatulla tavalla. Muitakin luokkia saat muuttaa tarpeen mukaan. Voit ehkä kokea tarpeelliseksi lisätä johonkin luokkaan uusia metodeja. Useimmat tietorakenteet on annettu valmiina.

Luokka CarePeriod on melkein kokonaan tyhjä, ja sen saat suunnitella ja toteuttaa kokonaan itse. Luokasta Hospital puuttuu toteutukset metodeille:

  • enter
  • leave
  • assign_staff
  • print_patient_info
  • print_care_periods
  • print_all_medicines
  • print_all_patients
  • print_current_patients.

Kunkin näistä on tarkoitus toteuttaa samanniminen puuttuva komento.

Myös muita apufunktioita saa toteuttaa ja lisätä attribuutteja.

Koodi (esimerkiksi tulostuskoodi) kannattaa sijoittaa siihen luokkaan, jonka tietoja käsitellään (tulostetaan). Esimerkiksi tulostusketju voi lähteä jostakin “isommasta” luokasta, joka ehkä itse tulostaa jotakin ja pyytää sitten osiaan tulostamaan osuutensa. Tässä isommalla luokalla tarkoitetaan luokkaa, jolla on osinaan (attribuutteinaan) toisten luokkien ilmentymiä.

Tehtävää tehdessäsi kannattaa juurikin kiinnittää huomiota siihen, miten toisen luokan ilmentymät (oliot tai osoittimet niihin) voivat olla toisen luokan attribuutteina.

Ohjelmassa on monenlaisia henkilöitä: henkilökuntaa, sairaalassa tällä hetkellä olevia potilaita ja siellä joskus olleita potilaita. Kaikki nämä ovat kuitenkin luokan Person ilmentymiä. Eri henkilöitä voi erotella lisäämällä Person-luokkaan sopivia totuusarvoisia (bool) tai luettelotyyppisiä (enum) attribuutteja. Tai voit koota erilaiset henkilöt eri säiliöihin (mikä on ehkä parempi ratkaisu). Luokassa Hospital on esimerkiksi attribuutti current_patients_, joka sisältää sairaalassa tällä hetkellä olevat potilaat.

Monien komentofunktioiden (komennon toteuttava metodi) parametrina on params, jonka tyyppinä on Params. Kyseessä on vektori, jonka määrittely on tiedostossa hospital.hh. Parametrien määrät tarkistetaan Cli-luokan exec-metodissa, joten kaikissa toteutettavissa funktioissa voit olettaa, että params-vektorissa on juuri sen verran alkioita, kuin kyseinen komento vaatii.

Joillakin komennoilla ei ole lainkaan parametreja, jolloin vastaavat komentofunktiot eivät käytä saamaansa params-parametria. Silloin tämän parametrin nimi on jätetty pois. Tyyppiä ei kuitenkaan voi jättää pois, sillä funktiota on kutsuttu funktio-osoittimen kautta. Kaikilla samantyyppisen funktio-osoittimen kautta kutsutuilla funktioilla täytyy olla sama määrä samantyyppisiä parametreja. Kaikki tämä on kuitenkin toteutettu pohjakoodiin, joten asiasta ei tarvitse huolehtia. Tehtävän tekeminen ei siis vaadi funktio-osoittimien osaamista.

Vihjeitä

  • Aloita tutustumalla valmiiseen koodiin. Annettua koodia ei kuitenkaan tarvitse ymmärtää perin pohjin. Ei haittaa, vaikka et ymmärtäisi luokan Cli koodia. Muista luokista kannattaa tutkia, mitä metodeja ne tarjoavat (julkiset metodit) ja mitä attribuutteja niillä on. Tutustu metodien toteutusten yksityiskohtiin vasta, jos/kun huomaat tarvitsevasi kyseisten yksityiskohtien tuntemusta. Useimmissa tapauksissa riittää, että päättelet metodin tekemän toiminnon sen nimestä.
  • Ennen ohjelman toteuttamista, mieti huolellisesti, miten rakennat ohjelman niin, että pystyt vastaamaan annettuihin vaatimuksiin.
  • Tässä projektissa pääpaino on modulaarisuudella sekä osoittimilla. Joudut kuitenkin lisäämään pari STL-säiliötä, mutta niiden valinnan ei pitäisi tuottaa hankaluuksia enää tässä vaiheessa.
    • Mikä olisi sopiva säiliö sairaalan hoitojaksoille, kun hoitojaksot halutaan käydä läpi lisäysjärjestyksessä?
    • Mikä olisi sopiva säiliö hoitojakson henkilökunnalle, kun henkilökunnan jäsenet halutaan tulostaa aakkosjärjestyksessä?
  • Huomaa, että luokan sisäiset const-funktiot eivät voi kutsua funktioita, joista puuttuu määre const. Sanan const puuttuminen aiheuttaa kääntäjässä [-fpermissive]-virheen. Eli funktio, jota on kielletty muuttamasta olion tilaa, ei saa kutsua funktioita, jotka voivat muokata olion tilaa.
  • Muista, että oletuksena ohjelma käännetään build-hakemistoon. Testaamista varten kannattaa siirtää mahdolliset syötetiedostot tähän samaan hakemistoon.

Ohjelman toteuttaminen osissa

Tässä vaiheessa kurssia pitäisi jo olla muodostunut riittävän hyvä käsitys siitä, millaisissa osissa ohjelmia kannattaa toteuttaa ja millaisia committeja kannattaa tehdä.

Erityisvaatimukset

Seuraavia vaatimuksia on noudatettava, mikäli haluaa työstä hyväksytyn arvosanan:

  • Ohjelmassa pitää käyttää dynaamista muistinhallintaa, mutta siinä ei saa olla muistinhallintaan liittyviä virheitä. Kannattaa siis suorittaa valgrind. Osoittimina voit käyttää joko tavallisia osoittimia tai älykkäitä osoittimia tai molempia.
  • Vain STL-kirjaston säiliöt sallitaan, ei esim. Boost-kirjaston.

Arviointi

Jotta työ päätyisi assistenttien arvioitavaksi, sen pitää ensin läpäistä automaattitestit. Jos saat automaattitesteistä 0 pistettä, myös lopullinen pistemääräsi on 0, eikä työ mene assistenttien arvioitavaksi. Automaattitestit ajavat ohjelmallesi myös valgrindin, joten ohjelmasi ei läpäise testejä, jos siinä on muistinhallintaan liittyviä virheitä. Kannattaa siis ajaa valgrind jo ennen tehtävän palauttamista.

Assistentti arvioi ja pisteyttää automaattitestit läpäisseistä (= 1 p) sekä tehtävänannon erikoisvaatimukset täyttävistä töistä viimeisimmän Plussa-palautuksen, joka on tehty määräajan loppuun mennessä, seuraavien arviointikriteerien mukaan:

  • Ratkaisun yleisperiaate: 0-40 pistettä:
    • Tehtävän osaamistavoitteet on saavutettu.
    • Ohjelmakoodi on jaettu funktioita, luokkia ja/tai metodeita käyttäen loogisiksi kokonaisuuksiksi, jotka ovat sopivan pituisia.
    • Jos luokkia ja olioita on käytetty, ne on toteutettu olio-ohjelmoinnin periaatteita noudattaen (ks. 4-kierroksen Tyyliseikoista kohta Olio-ohjelmointi).
    • Tietorakenne ei sisällä toisteista eikä turhaa tietoa. Valittuja tietorakenteita on käytetty järkevästi.
    • Ohjelmakoodi ei sisällä turhaa toisteisuutta eikä muutakaan turhaa.
    • Ohjelmakoodi ei sisällä tarpeettomia rajoitteita tai oletuksia tai muita väkinäisiä ratkaisuita.
    • Ohjelmarakenteet on toteutettu helposti ymmärrettäviksi.
    • Ohjelmakoodissa ei ole käytetty globaaleja muuttujia (globaalit const-vakiot ovat OK).
    • Ohjelman toimintaa ei lopeteta exit-funktiota käyttäen.
    • Ohjelmassa ei ole muistinhallintaan liittyviä virheitä (suorita valgrind).
  • Ohjelmointityyli: -20-0 pistettä:
    • Muuttujat ja funktiot nimetty selkeästi ja kuvaavasti. Nimeämisessä on käytetty vain yhtä kieltä, ei esim. suomea ja englantia sekaisin.
    • Ohjelmassa on käytetty nimettyjä vakioita taikalukujen sijasta.
    • Ohjelmakoodi on asemoitu siististi.
    • Koodirivien pituus on enintään 80 merkkiä.
    • Jokaisen itse kirjoittamasi/editoimasi tiedoston alussa on kommentti, josta käy ilmi tiedoston tarkoitus, nimesi, opiskelijanumerosi ja muut tarvittavat tiedot. (Tarvittaessa katso mallia ensimmäisen projektin tehtävänannosta.)
    • Jokaisen funktion/metodin alussa (otsikkotiedostossa jos sellainen on olemassa) on kommentti, joka kuvaa sen toiminnan, paluuarvon ja parametrit.
    • Kommentteja on muutenkin tarpeellisissa kohdissa.
    • Kommentit liittyvät toteutettuun ohjelmaan eivätkä johonkin sen vanhempaan versioon.
    • Kaikki kommentit on kirjoitettu samalla kielellä (fi/en), mutta kommentoinnissa saa käyttää eri kieltä kuin muuttujien ym. nimissä.
    • Kaikki muuttujat on alustettu.
    • Kääntäjä ei anna koodia käännettäessä varoituksia.
  • Versionhallinnan käyttö: 0-10 pistettä:
    • Committeja on riittävä määrä.
    • Commit-viestien sisältö on selkeä ja kuvaava.

Huomaa yllä olevasta listasta, että tässä vaiheessa tyyliseikkojen oletetaan olevan opiskelijoilla hallussa. Enää et siis saa pisteitä tyylisääntöjen noudattamisesta, vaan niiden käyttämättä jättämisestä sakotetaan. Kannattaa siis tarkistaa, mitä tyyliasioista on kerrottu materiaalin kierroksilla 4 ja 10.

Huomautus arvioinnista

Automaattitesteillä tarkistetaan, toimiiko ohjelma. Assistentit taas kiinnittävät huomiota tyyliseikkoihin ym. yllä lueteltuihin kriteereihin. Periaatteessa on siis mahdollista, että täysin toimivasta ohjelmasta saa nolla pistettä, jos ohjelma ei täytä yllä lueteltuja vaatimuksia.

Erityisesti tyylivaatimuksista on kerrottu tämän kierroksen aikaisemmassa materiaaliosiossa.

Huomautus arvosteltavan työn versiosta

Arvosteluun otetaan ohjelman se versio, josta on tehty viimeisin onnistunut Plussa-palautus (määräaikaan mennessä). Onnistuneella palautuksella tarkoitetaan palautusta, joka on läpäissyt automaattitestit.

Ensimmäisen onnistuneen Plussa-palautuksen jälkeen haluat todennäköisesti vielä korjailla koodisi tyyliä (esim. lisätä kommentteja), jos vain palautusaikaa on vielä jäljellä.

Muista lopuksi kuitenkin tämä: Kun olet palauttanut viimeisen version Gittiin, palauta se vielä kerran Plussaan.

A+ esittää tässä kohdassa tehtävän palautuslomakkeen.