Re: Koostetta laivaseminaariesitelmäni aiheista

[vastaus aiempaan viestiin]

Kirjoittaja: Kimmo Vehkalahti
Sähköposti:    -
Päiväys: 5.4.2006 23:44

Laivaseminaarissa Tukholmassa pitämäni esitelmän otsikkona oli
"Survo-keskeinen työskentelytapa muiden ohjelmien hyödyntämisessä".
Kävin melko ripeässä tahdissa läpi seitsemän esimerkkiä, joista
ohessa käsittelen toista:

2) Kurssipalautteiden massa-Survonta 200-sivuiseksi Word-raportiksi

Helsingin yliopiston matematiikan ja tilastotieteen laitoksella kerätään
opiskelijoilta palautetta kaikista kursseista nettilomakkeella. Anonyymit
vastaukset saapuvat palautejärjestelmän vastuuhenkilölle sähköpostitse.
Niistä kootaan raportti kahdesti vuodessa. Tämän vuoden alusta lähtien
raportti on tehty lähes automaattisesti Survolla. Tulin tehneeksi noin
10 sukroa ja C-modulin, joilla aiempi raskas "copy-paste" -urakka hoituu
aivan hetkessä. Kokonaan työtä ei ole haluttu automatisoida, sillä on
havaittu että mm. harjoitusten pitäjien nimet saadaan luotettavammin
selville avokysymyksinä kuin valmiista listasta valittuna.

Jälleen hyödynnetään sitä, että Word osaa lukea HTML-tiedostoja. Tässä
tosin HTML-tiedostot luodaan Survossa C-modulilla eikä PRINT-operaatiolla
kuten esimerkeissäni 1) ja 7).

Lähtökohtana ovat kurssikohtaiset tekstitiedostot, joissa on peräkkäin
seuraavannäköisiä viestejä (tässä esimerkkinä hieman peitelty näyte
'linmalsov'-nimisestä tiedostosta):

    From helinfo@cc.helsinki.fi  Mon Jan 16 14:21:46 2006
    Date: Mon, 16 Jan 2006 15:29:22 +0200 (EET)
    From: WWW-manageri <helinfo@cc.helsinki.fi> 
    To: xxxxxxxx@solmu.math.helsinki.fi
    Subject: Mail from WWW (http://mathstat.helsinki.fi/kurssit/kysely/)
    Reply-to: helinfo+postitalomake@cc.helsinki.fi
    Sender: helinfo+postitalomake@cc.helsinki.fi
    X-Sender: http://www.helsinki.fi/bin/postitalomake 
    Message-Id: <200601161529.1b9e43cb9fb209432@www.helsinki.fi  
    MIME-Version: 1.0
    Content-Type: text/plain; charset=ISO-8859-1
    Content-Transfer-Encoding: 8bit
    Status: RO
    X-Status:
    X-Keywords:


    This is a form sent to you by the WWW-server:

    Kurssin_nimi = Lineaaristen mallien sovellutukset
    Harjoitusten_pitäjä = Kimmo Vehkalahti
    Ohjausten_pitäjä =
    1_Vaatimustaso = Melko vaikea
    2_Opiskeluilmapiiri = Melko hyvä
    3_Kurssimateriaali = Erittäin hyvä
    4_Luennoijan_selkeys = Olen täysin samaa mieltä.
    5_Kommentit_sisaltö_luennoija = Luentomoniste oli hyvä ja [...]
    6_Tehtävien_taso = Sopivia
    7_Harjoitusten_pitäjä_onnistui = Olen täysin samaa mieltä.
    7b_Ohjausten_pitäjä_onnistui = Valitse tästä
    8_Harjoituksista_apua = Erittäin paljon
    9_Harjoitusten_paras_muoto = Mielestäni käytäntö oli hyvä. [...]
    10_Harjoituskommentit =
    11_Kokeiden_taso = Valitse tästä
    12_Kokeiden_onnituminen = Valitse tästä
    13_Koekommentit =
    14_Osallistumisaktiivisuus = Jonkin verran (työskentelin pari kertaa viikossa)
    15_Syy_kurssi_kesken =
    16_Kommentit_itsestänsä = Kiire oli aikamoinen [...]
    17_Tyytyväisyys = Olen erittäin tyytyväinen.
    18_Parasta_kurssissa = Käytännön harjoitukset, asioiden looginen [...]
    19_Huonointa_kurssissa =
    20_Kehitystarpeet =

    REMOTE-HOST: xxxxxxxxx.xxxxxxxxx.helsinki.fi (128.214.xxx.xxx)
    HTTP-REFERER: http://mathstat.helsinki.fi/kurssit/kysely/ 
    FORM-ID: 1234567890

Osa vastauksista on valintoja annetuista vaihtoehdoista (kuten nrot 1-4),
osa avovastauksia (kuten nrot 5 ja 9), jotka saattavat olla pituudeltaan
tuhansia merkkejä. Lomakesysteemi katkoo ne n. 1000 merkin pituisiksi,
mutta tietyistä yksityiskohdista voi päätellä onko kyseessä jatkorivi.

Tällaisten tiedostojen kimppuun sukrot käyvät ja alkavat muokata dataa
hieman systemaattisempaan muotoon. En voi tässä mennä yksityiskohtiin,
mutta koetan poimia joitain hauskoja kohtia esiin. Kokonaisuudessaan
raportointi koostuu 10 vaiheesta (allaoleva on karkea dokumentaationi
jonka perusteella olen myöhemmin tekemässä tarkempaa selostetta; "MK"
viittaa palautejärjestelmän vastuuhenkilöön; teemme työtä vaiheittain
ja vuorotellen vaihtaen tiedostoja laitoksen serverin kautta):

0. /MUUNNA
   -> tekee kaikista .txt
      .txt = eräitä teknisiä muunnoksia sekä merkkimuunnoksia

1. /HAR1
   -> tekee kaikista .har1 (käyttää .txt)
      .har1= harjoitustenpitäjät lomakkeilla annetuissa muodoissa,
             1. rivi: kurssin nimi, 2. rivi: luennoijan nimi

2. /OHJ1 <anal1|anal2|anal1sv|anal2sv> 
   -> tekee yksitellen jokaisesta .ohj1 (käyttää .txt)
      .ohj1= ohjaustenpitäjät lomakkeilla annetuissa muodoissa

3. MK muodostaa käsin *.har2 ja *.ohj2 -tiedostot
      .har2= lomakkeella annetut ja korjatut nimet peräkkäisillä riveillä,
             1. ja 2. rivi: korjatut kurssin ja luennoijan nimet
      .ohj2= muuten kuin .har2 mutta ei kurssin ja luennoijan nimiä

4. /SVEN
   -> muuntaa sv ja en -vastausvaihtoehdot suomeksi, .txt -> .dat
      .dat = kanonisoitu numeeristen vastausvaihtoehtojen kielen osalta

5. /HAR2
   -> tekee kaikista .har ja .nam (käyttää .har1 ja .dat)
      .har = aakkostettu harjoitusten pitäjien lista
      .dat = kanonisoitu harjoitusten pitäjien osalta
      .nam = kurssin ja luennoijan nimet (1. ja 2. rivi)

6. /OHJ2 <anal1|anal2|anal1sv|anal2sv> 
   -> tekee yksitellen .ohj (käyttää .ohj2 ja .dat)
      .ohj = aakkostettu ohjausten pitäjien lista
      .dat = kanonisoitu ohjausten pitäjien osalta

7. /HTML
   -> tekee kaikista .out (PALAUTE-modulilla: käyttää .dat, .har, .nam, .ohj)
      .out = html-muotoinen kooste palautteista
   -> tekee kaikista .html
      .html= kuten .out mutta eräitä merkkimuunnoksia

8. /NAM
   -> yhdistää .html-tiedostot yhdeksi raportti.html-tiedostoksi kurssien
      nimien mukaiseen aakkosjärjestykseen (käyttää .nam)

9. MK muodostaa lopullisen raportin Wordillä raportti.html-tiedostosta
   - sisällysluettelo automaattisesti Wordin toiminnolla
     Insert - Reference - Index and Tables - Table of Contents
   - kansi ja esipuhe + pari liitettä

Tiedostoja syntyy pilvin pimein. Tammikuussa tehdyn ensimmäisen ns.
"tuotantoajon" alkutilanteessa oli 40 tiedostoa; survonnan tauottua
tiedostoja oli 380. :) Suurin osa niistä on apusälää (.har, .nam jne.).
Varsinainen lopputulos, raportti.html, on kooltaan n. 500 KB.

                            *  *  *

Seuraavaksi esittelen joitakin poimintoja sukrojen toteutuksesta:

/HAR1-sukro kaivaa jokaisesta kurssitiedostosta harjoitusten pitäjien
nimet (jotka on siis annettu avovastauksina) ja muokkaa näin saadusta
listasta helposti luettavan ja tarkistettavan.

Oheisessa kohtaa koodia on sukromuistipaikassa W1 ao. kurssin koodinimi,
tässä tapauksessa "anal1" (Analyysi I). SEARCH:illä saadaan sekä haetut
tiedot kenttään että niiden lukumäärä sukromuistiin:

    / def Wlkm=W3 Wx=W4
    *FILES={print W1}.txt RUN=1 SHOW=0 TUTSTACK=1{R}{save stack}
    *SEARCH HPIT ={home}{act}{erase}
    *REPLACE "HPIT = ","",C{act}{home}{erase}
    *{d5}{u5}{ins line}{ins line}{ins line}{ins line}{u3}

Lukumäärä poimitaan sukromuistista ja pystytetään sen kokoinen data
yhtä muuttujaa (Nimi) varten:

    *TUTSTACK{act}{R}{find @}{erase}{line start}{del stack}{load stack}
    *{save word Wlkm}{erase}
    *DATA A A+1,A+{print Wlkm},A,A-1{R}
    *A{copy}{R}{line start}{r80}{erase}{line start}{R}
    *Nimi{home}{l4}A{r}
    *{pre}{search}TUTSTACK{R}{erase}A+{print Wlkm}+2={act}{l} {save word Wx}
    *{jump END-10,Wx,1}{R}

Dataa muokataan FILE-operaatioilla ja otetaan lopuksi kenttään (nimet
A, AA, AAA jne. vaikuttavat syystäkin vähän kiireessä keksityiltä):

    *FILE COPY A TO NEW AA{act}{R}
    *FILE SORT AA BY Nimi TO AAA{act}{R}
    *FILE AGGR AAA BY Nimi TO AAAA{ref set 3}{R}
    *VARIABLES:{R}
    *N    N     -{R}
    *Nimi FIRST Nimi{R}
    *END{R}{ref jump 3}{act}{jump END-10,END+1,1}
    *FILE SORT AAAA BY -N TO AAAAA / SAVE=1{act}{R}
    *FILE STATUS AAAAA{act}{ref set 3}
    *{search}FIELDS:{R}{R}
    *{next word}{next word}{next word}{next word}
    *{r9}(###){ref jump 3}{line start}FILE UPDATE{act}
    *{jump END-10,END+1,1}
    /{tempo 2}
    *FILE LOAD -AAAAA CUR+2 / DELIMITER=|{act}{R}
    *SAVEW {print W1}.har1{act}

Tässä on näkymä siitä, kun sukro on tehnyt em. vaiheet (olen vähän
muutellut ihmisten nimiä tässä muuten oikeassa tilanteessa):

    *FILES=anal1.txt RUN=1 SHOW=0 TUTSTACK=1
    *
    *A+20+2 75
    *DATA A A+1,A+20,A,A-1
    *AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    ANimi
    *Riina Vehanen
    *Mikko Vihko
    *Juha Saavalainen
    *Mikko Vihko
    *useita, tapio verälä
    *Mikko Vihko
    *Juha Saavalainen
    *Tapio Verälä
    *Seppo Linden
    *Tapio Verälä
    *Mikko Vihko
    *en muista
    *Osmo Kalteva
    *Osmo Kalteva
    *Osmo Kalteva
    *Tapio Verälä
    *Markku Rantalainen
    *Timo Vuori
    *Seppo Linden
    *Osmo Kalteva
    *
    *
    *FILE COPY A TO NEW AA
    *FILE SORT AA BY Nimi TO AAA
    *FILE AGGR AAA BY Nimi TO AAAA
    *VARIABLES:
    *N    N     -
    *Nimi FIRST Nimi
    *END
    *FILE SORT AAAA BY -N TO AAAAA / SAVE=1
    *FILE UPDATE AAAAA
    *Aggregated from data AAA by variable Nimi
    *FIELDS: (active)
    *   1 NA_   2 N        (###)
    *   2 SA_  80 Nimi
    *END
    *Survo data file AAAAA: record=114 bytes, M1=6 L=64  M=2 N=10
    *FILE LOAD -AAAAA CUR+2 / DELIMITER=|
    *SAVEW anal1.har1
    *   4|Mikko Vihko
    *   4|Osmo Kalteva
    *   3|Tapio Verälä
    *   2|Seppo Linden
    *   2|Juha Saavalainen
    *   1|en muista
    *   1|Markku Rantalainen
    *   1|Riina Vehanen
    *   1|Timo Vuori
    *   1|useita, tapio verälä

Tästä on helppo tarkistaa oikeat ja virheelliset tiedot. Useimmiten
alkupään tiedot ovat oikein (kuten tässäkin heti viisi ensimmäistä).
Ne voi tarkistaja vain poistaa; epämääräiset korvataan tietyllä
merkkiyhdistelmällä (ne niputetaan raportissa yhteen luokkaan).

Samalla tekniikalla sukro hakee kurssin nimen (myös avovastaus) ja
valitsee jakauman moodiluokan edustajan ehdolle (tarkistettavaksi). Näin
sitäkään ei tarvitse yleensä erikseen kirjoittaa missään vaiheessa.

                            *  *  *

/SVEN-sukro ("SVenskaENglish", nimet ovat mitä ovat...) muuttelee
muunkieliset datat suomenkielisiksi, jotta jatkokäsittely olisi
yksinkertaisempaa. Tässä rakennetaan TXTCONV-asetelmaa, jossa pitää
ottaa huomioon sellainen yksityiskohta, että merkkijonoja korvattaessa
uuden merkkijonon on oltava vähintään vanhan pituinen, muuten jäljelle
jää "roippeita" vanhasta.

Conversions-kentän rakennuskomentoja (huomaa tyhjät {} rivin aluissa;
estävät tehokkaasti vahinkoaktivoinnit sukroa kirjoittaessa!):

    *{}SET CUR+1,END,CUR-1{act}{home}{erase}
    *{}PUTEND CUR+1,END .txt{act}{home}{erase}
    *{R}{r30}{block}{block}{block}{block}{u}{line start}
    *{}PUTEND CUR+1,END .dat{act}{home}{erase}{R}{ref set 1}
    *{jump END-2,END+2,1}
    *CONVERSIONS:{R}

Yksityiskohta kaaviosta (kysymyksen 7a vaihtoehdot ruotsista suomeksi):

    /  7a.
    *t "Välj här#                             "Valitse tästä#{R}
    *t "Jag är nog helt av annan åsikt.#      "Olen täysin eri mieltä.#{R}
    *t "Jag är delvis av annan åsikt.#        "Olen jonkin verran eri mieltä.#{R}
    *t "Kan inte säga någonting om det.#      "En osaa sanoa.#{R}
    *t "Jag är delvis av samma åsikt.#        "Olen melko samaa mieltä.#{R}
    *t "Jag instämmer helt.#                  "Olen täysin samaa mieltä.#{R}

Risuaidat (#) selittyvät sillä, että sukro täydentää nämä lopuksi siten
että merkkijonot ovat samanmittaiset (lisäämällä blankkoja):

    + B0: {R}{line start}{save word W2}
    - if W2 '=' END then goto B10
    *{r3}{find #}{save cursor W2,Wc0}{Wc0=Wc0-4}{next word}{Wc1=0}
    + B1: {r}{save char W2}
    - if W2 '=' # then goto B2
    *{Wc1=Wc1+1}
    - if Wc1 >= Wc0 then goto B9 else goto B1
    + B2: {ins} {ins}{Wc1=Wc1+1}
    - if Wc1 >= Wc0 then goto B9 else goto B2
    + B9: {goto B0}
    + B10: {ref jump 1}
    *{u}{ins line}
    *REPLACE "#","char(10)#" C{act}{home}{erase}
    *REPLACE # " C{act}{home}{erase}
    *{R}

Nuo "char(10)":t varmistavat, että konversiot koskevat vain rivin loppuun
päättyviä, ei esimerkiksi keskellä avovastausta mahdollisesti olevia muuten
samoja merkkijonoja.

                            *  *  *

/HAR2 muokkaa edellä käsin korjatuista harjoitustenpitäjien luetteloista
siistimmät listat käyttäen samantapaista tekniikka kuin /HAR1, nyt vain
tarvitaan vähän lisää viilailuja. Systemaattinen haku SEARCH:illä on
aivan sama kuin edellä, mutta viilailuja on kenttään rakennettavan datan
survomisessa:

   *FILE COPY A TO NEW AA{act}{R}
   *FILE EXPAND AA{act}{R}
   *VAR Sukunimi:S35=MISSING TO AA{act}{R}
   *FILE EXPAND AA{act}{R}
   *VAR Etunimi:S35=MISSING TO AA{act}{R}
   *väli=pos(Nimi,sp){R}
   *VAR str(Sukunimi)=str(Nimi,väli+1) TO AA{act}{R}
   *VAR str(Etunimi)=str(Nimi,1,väli) TO AA{act}{R}
   *..........{R}
   *VAR str(Sukunimi)=str(Etunimi) TO AA / CASES=Sukunimi,MISSING{act}{R}
   *..........{R}
   *FILE SORT AA BY Sukunimi,Etunimi TO AAA / SAVE=1{act}{R}
   *FILE AGGR AAA BY Sukunimi TO AAAA{ref set 3}{R}
   *VARIABLES:{R}
   /N    N -
   *Nimi FIRST Nimi{R}
   *END{R}{ref jump 3}{act}{jump END-10,END+1,1}
   *FILE LOAD -AAAA CUR+2{act}{R}
   *DELETE{home}{act}{erase}
   *SAVEP {print W1}.har{act}

Lopputulos em. pseudonimillä näyttää tältä (XYZ on em. kaatoluokan symboli):

    Osmo Kalteva
    Seppo Linden
    Juha Saavalainen
    Riina Vehanen
    Tapio Verälä
    Mikko Vihko
    Timo Vuori
    XYZ

                            *  *  *

/NAM-sukro järjestää loppuvaiheessa kurssikohtaiset HTML-tiedostot
aakkosjärjestykseen kurssin nimen mukaan. Kurssin nimi löytyy /HAR2:n
jäljiltä .nam-tiedoston ensimmäiseltä riviltä. Tässä pieni luuppi,
jossa toimitaan rivi riviltä tiedostonimien listassa:

   *{ref set 1}{ref set 2}
   + A: {ref jump 2}{R}{save word W1}{erase}
   - if W1 '=' {sp} then goto B
   *{ref set 2}
   *LOADW {print W1}.nam,1,1,CUR{act}{line start}
   *{ins}                              {ins}{home}
   *{print W1}
   *{goto A}
   + B: {ref jump 1}{r30}
   *AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA{u}{line start}

Tässä on listan alkua luupin jäljiltä:

                                  AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    aksjoukko                     Aksiomaattinen joukko-oppi
    anal1                         Analyysi I
    anal1sv                       Analys I
    anper                         Analyysin peruskurssi
    data                          Data-analyysi I
    design                        Design and analysis of experiments
    diffyht1                      Differentiaaliyhtälöt I
    diffyht2                      Differentiaaliyhtälöt II
    diskr                         Johdatus diskreettiin matematiikkaan
    funkt1                        Funktioteoria I
    gradusem                      Gradu-seminaari
    laskint                       Laskentaintensiiviset tilastolliset menetelmät
    linmal                        Yleistetyt lineaariset mallit
    linmalsov                     Lineaaristen mallien sovellukset
    ...

Lajitteluavain ("AAAA...") on siis jo paikoillaan, nyt vain järjestetään,
sitten pyyhitään kurssien nimet pois ja muokataan tiedostojen nimistä
valmis rimpsu käyttöjärjestelmän COPY-komentoa varten, joka lopulta
(binäärikopiointina) yhdistää tiedostot peräkkäin oikeaan järjestykseen:

   *SORT CUR+2,END,CUR+1{act}{R}{erase}
   /
   *CLEAR CUR+1,END{home}{r30}{act}{home}{erase}
   /
   *INSERT 6{home}{act}{erase}
   /
   *PUTEND CUR+1,END .html+&{act}{home}{erase}
   /
   *REPLACE "+&"," + &",C{act}{home}{erase}
   /
   *>copy _alku_.html + &{R}
   *{jump END-5,END,1}{find +}{erase} + _loppu_.html raportti.html /B
   *{ref jump 1}{act}

Tilanne näyttää siis käytännössä tällaiselta:

   *>copy _alku_.html + & 
   *      aksjoukko.html + & 
   *      anal1sv.html + & 
   *      anal1.html + & 
   *      anper.html + & 
   *      data.html + & 
   *      design.html + & 
   *      ...
   *      vektan.html + & 
   *      linmal.html  + _loppu_.html raportti.html /B

Tässähän käytetään hyväksi Survon melko uutta ominaisuutta, siis sitä
että käyttöjärjestelmäkomennot voi jakaa usealle riville &-merkin avulla
(aika kätevää tässä; komento on kaikkiaan yli 40 riviä pitkä!).

C-modulissa on järjestetty niin, että HTML-tiedostot talletetaan ilman
alku- ja loppukoristeita; ne tulevat mukaan vasta tuossa kopioinnissa
yhteisesti koko raportille. Niissä ei ole sinänsä mitään ihmeellistä:

_alku_.html:
    <HTML> 
    <HEAD> 
    <TITLE>Kurssipalaute / Matematiikan ja tilastotieteen laitos</TITLE> 
    <!-- Created with SURVO MM --> 
    </HEAD> 
    <BODY> 

_loppu_.html:
    </BODY> 
    </HTML> 

                            *  *  *

Tämä oli ihan hauska ohjelmointitehtävä! Paljon sottaisia yksityiskohtia,
mutta kaikista selvisi mukavasti. Raskaimman työn tekee Survon C-moduli,
joka muodostaa sukrojen siistimästä datasta HTML-tiedostot. Tuon vaiheen
koodaus sukrona olisi ollut mitä luultavimmin turhan sotkuista hommaa,
mutta C:llä se sujui hyvin. Esivalmistelut puolestaan olivat ilman muuta
paremmin sukrojen kuin C-modulin heiniä.

Alkukesästä palauteraportointi pyörähtänee jälleen. Saa nähdä löytyykö
ohjelmista korjattavaa vai pääsenkö pelkillä nappien painalluksilla. ;)

- Kimmo

Vastaukset:
[ei vastauksia]

Survo-keskustelupalstan (2001-2013) viestit arkistoitiin aika ajoin sukrolla, joka automaattisesti rakensi viesteistä (yli 1600 kpl) HTML-muotoisen sivukokonaisuuden. Vuoden 2013 alusta Survo-keskustelua on jatkettu entistäkin aktiivisemmin osoitteessa forum.survo.fi. Tervetuloa mukaan!

Etusivu  |  Keskustelu
Copyright © Survo Systems 2001-2013. All rights reserved.
Updated 2013-06-15.