# timOhjeet

csPlugin attribuutit

Lue myös: YAML-kieli TIMissä.

1. Arvonta

Kaikkiin alla esiteltyihin attribuutteihin voi soveltaa arvontaa, jolloin attribuuteille voi saada satunnaisia arvoja. Tilanteesta toki sitten riippuu, saadaanko tällä aikaiseksi hyvää vai pahaa :-) Mutta oikein käytettynä arvonnalla voidaan saada opiskelijoille toisistaan eroavia tehtäviä.

# answerlimit

2. Vastauskertojen rajoittaminen

``` {plugin="csPlugin" #itseisarvo}
type: cs,comtest,doc
answerLimit: 5
...

Jos answerlimit rajaa ei anna, on oletus ääretön muille plugineille paitsi mmcq:lle (monivalintakysymykselle) se on yksi. Jos kirjoittaa

answerLimit:

niin vastauskertojen lukumäärä on rajaton vaikka olisi kyse mistä tahansa pluginista.

Vastausaikaa voidaan rajoittaa:

starttime: '2016-08-25 23:00:05'
deadline: '2016-08-25 23:10:05'

Näistä jompikumpi voi puuttua, jolloin sen rajan väli ei rajoita. Aika tulkitaan UTC:na.

# points

3. Pisteiden antaminen

Alla on lueteltu pisteiden antamisen vaihtoehdot. Pisteet lasketaan yhteen, jollei ole cumulative: false, jolloin korkeimman pistemäärän tuottava osio otetaan huomioon. Tällöin myös jos ajaa testit eivätkä ne mene läpi, niin tuloksena voi olla 0 p, vaikka koodi kääntyisikin, jos testin kääntymisestä ei ole annettu erikseen pisteitä.

Kaikkia alla olevia vaihtoehtoja ei ole pakko käyttää kerralla.

answerLimit: 5      # montako kertaa tehtävää saa yrittää  
-pointsRule:        # tämän alle säännöt miten pisteitä saa
    multiplier: 1.0 # Millä luvulla kerrotaan tehtävästä saadut pisteet 
    maxPoints: "Voit saada max 2 pistettä" # teksti, jolla kuvataan pisteiden kertyminen
    min_points: 0   # estetään negatiivisten pisteiden saaminen (oletus 0)
    max_points: 5   # estetään ettei pisteiden summa tule tätä korkeammaksi (oletus 1e100)
    readpointskeep: false # jätetäänkö seuraavassa pisteet tulosteeseen
    readpoints: "Pisteet: (.*)\n" # etsitään (regex ja poistetaan ellei edellä ole true) 
                     # tulosteesta rivi Pisteet:
                     # ja otetaan tuosta sulkujen välinen osa pisteiden arvoksi.
                     # pisteet menevät output-osioon
    allowUserMin: 0  # minimipistemäärä, jonka käyttäjä saa itse antaa, oletus = 0
    allowUserMax: 2  # maksimipistemäärä, jonka käyttäjä saa itse antaa
    code: 0.8        # jos puuttuu, niin oletus on 1
    expectCode: |    # reg exp koodista, joka pitää täyttää, jotta saa code-pisteet
        pappa 
        borde 
        spela 
        \.
    expectCode: byCode # voi sanoa näinkin, jos halutaan, että byCode-attribuutti 
                       # sellaisenaan on oikea vastaus
                       #  pitää kuitenkin huomata, että regexp merkit aiheuttavat 
                       # haasteita :-!
    expectCodePlain: "kissa"
                       # vastauksen pitää olla täsmälleen tämä teksti 
                       # (voi olla montakin riviä)
    expectCodePlain: byCode: # ehkä järjestelytehtävään paras valinta, jos on olemassa
                             # vain oikea tai väärä järjestys, eli ei tarvita regexpejä
    compile: 0.1       # käännöksen onnistumisesta saa 0.1 pistettä
    run: 0.2           # jos ajo menee virheittä 0.2 pistettä
    output: 0.8        # out vastaa vaadittua (oletus 1 p)
    expectOutput: '.*Summa.*5$' # regexp outputille
    expectOutputPlain: 'Summa on 5'
                       # ohjelman tuloksen pitää olla täsmälleen tämä teksti
                       # paitsi lopun välilyöntien ja rivinvaihtojen määrä saa heittää
    test: 1            # testien läpimeno tuottaa 1 p
    doc: 0.1           # jos ajetaan dokumentit, niin saa 0.1 pistettä edellyttäen, että
                       # muita pisteitä on jo vähintään doc_limit verran
    doc_limit: 0.5     # raja, jota enemmän pitää olla muita pisteitä, että saa doc-pisteet.
                       # oletus on 0.5.  Jos tähän laittaa 0, saa aina doc-pisteet

Mitähän regexp-formaattia TIM tässä käyttää?

  • VL: Python 3.x
27 Sep 18 (edited 27 Sep 18)

Katso myös numberRuler käyttöohjeista.

Mikäli pisterajoittimissa on allowUserMax, niin silloin käyttäjä saa antaa pisteitä noissa rajoissa. Käyttäjän pisteet tällöin voittavat mahdollisen automaatin antamat pisteet. Mikäli mitään muita pistesääntöjä ei ole, silloin käyttäjän on itse annettava pisteet.

Tekstivastauksessa voisi olla vaikkapa lyhimmillään

-pointsRule:    
    expectCode: .{10,}  # eli vähintään 10 merkkiä niin saa 1 p (koska code puuttuu)

Tuossa ei saa olla rivinvaihtoa noissa 10:ssä merkissä, koska oletuksena piste (.) ei tarkoita rivinvaihtoa. Jos halutaan että myös rivinvaihto lasketaan noihin 10 merkkiin, muoto pitää olla jompikumpi seuraavista:

    expectCode: '(.|\s){10,}'  
    expectCode: '[\S\s]){10,}'

Pelkästään ajettavaksi tarkoitetuissa ohjelmissa, joissa outputtia ei voi vertailla järkevästi, voisi käyttää esim

-pointsRule:    
    compile: 0.3
    run: 0.7

Lyhyen säännön voi kirjoittaa myös seuraavasti:

-pointsRule: { run: 1 }
-pointsRule: { expectCode: ".{10,}" }

Toisaalta voidaan antaa cumulative attribuutti (oletus=true), jolla saadaan pisteiden lasku niin, että jokainen toteutuva ominaisuus lisää pisteitä, esim:

-pointsRule: 
    cumulative: true
    compile: 0.1
    run: 0.1
    output: 0.3
    testcompile: 0.1
    testrun: 0.1
    test: 0.3
    expectOutput: ".*Summa.*5$"

Tällöin testien ajaminen ei nollaa tavallisesta ajosta saatuja pisteitä.

Lisää regular expressioneista:

Example where the user code must include text like:

f=25
f = 25
-pointsRule:
    code: 2.0
    expectCode: "(.*\n)*.*f *= *25.*(.*\n)*"

Explanations:

  (.*\n)    row that can contain anything
               . = anything
               * = 0-n times
  *         previous () any time 0-n
  .*        anything in this line before letter f
  f         the letter f must be there
   *        any number of spaces
  =         equal sign must be there
   *        again any number of spaces, even *
  25        there must be letters 2 and 5
  .*        after that there might be any text on that line
  (.*\n)*   and again any number of lines whatever text

Minimissään pistesäännöt pitää olla:

-pointsRule: {} 

ja tämä riittää esim. MathCheckille, koska sille on oletuksena

readpoints: '<!--!points! (.*) -->'

ja MathCheck tuottaa pisteet tällaiseen muotoon. Mikäli ei haluta arvostelua, jätetään pointsRule kokonaan pois.

4. Koodin hakeminen toisesta tiedostosta

Attribuuteilla file: tai byFile voi hakea pohjaksi valmiin lähdekoodin esim. versionhallinnasta:

``` {plugin="csPlugin" #AstiaD7}
type: java/jcomtest/doc
header: "Astia-luokka muutoskuuntelijalla"
stem: "Tässä muuteltu Astia-luokka, jossa muutoskuuntelija.  Tälle ei tarvitse tehdä muuta kuin kääntää."
maxrows: 15

path: user/d7
justCompile: true
button: Käännä
byFile: https://svn.cc.jyu.fi/srv/svn/ohj2/esimerkit/k2016/demot/tehtavat/src/demo/d7/Astia.java
```

Tässä on se "vika", että TIM tallentaa nopeussyistä haetut tiedostot omaan sisäiseen välimuistiin. Tällöin alkuperäistiedostoon tehdyt muutokset eivät näy enää plugineissa.

Välimuistin voi tyhjentää eri plugineista komennoilla:

https://tim.jyu.fi/cs/refresh
https://tim.jyu.fi/svn/refresh

jolloin seuraavalla pluginin käyttökerralla TIM hakee tiedostot alkuperäisestä lähteestä.

# cache

5. Tehtävän tuloksen laittaminen välimuistiin

5.1 Miksi?

Jos "tehtävä" on sellainen, että sen tuloksen ei ole tarkoitus muuttua tekijän toimenpiteiden seurauksena, niin silloin tehtävään kannattaa laittaa päälle attribuutti cache: true, jolloin ajon tuottama kuva ja/tai teksti tallennetaan välimuistiin ja annetaan sieltä seuraavalla katselukerralla.

Periaatteessa osan cache-attribuutin ominaisuuksista saa sillä, että tekee tavallisen pluginin ja sitten ajaa sen ja ottaa kuvakaappauksen, jonka lataa TIMiin kuvana. Mutta tällöin jokaisen muutoksen jälkeen kuvakaappauksen joutuisi ottamaan ja lataamaan uudelleen. Cachen ansiosta riittää vain tehdä muutos kuvan lähdekoodiin ja kuva on TIMissä aina automaattisesti oikein valmiina.

Attribuutin cache aikana tehty kuva näytetään vain niille, joilla on oikeus nähdä dokumentti. Kuvasta ei muodostu verkon yli kenen tahansa haettavissa olevaa kuvaa.

5.2 Peruskäyttö

Esimerkiksi:

``` {plugin="csPlugin" #uml2}
type: run
cache: true 
isHtml: true
cmds: java -jar /cs/java/plantuml.jar -tsvg uml.txt; cat uml.svg 
filename: uml.txt
fullprogram: |!!
@startuml
Alice -> Bob: Soita mulle
Bob --> Alice: Saatan soittaakkin
Alice -> Bob: Kivaa
Alice <-- Bob: Ring
@enduml
!!
```
# uml2-1
AliceAliceBobBobSoita mulleSaatan soittaakkinKivaaRing

Eli jos edellä on "vain" tarkoitus että käyttäjälle näytetään tuo kuva, mutta käyttäjän ei ole tarkoitus muuttaa koodia, saadaan suoritus erittäin paljon nopeammaksi cache: true -attribuutilla. Mikäli pluginin tekijä muuttaa pluginin koodia, suoritetaan ajo uudelleen automaattisesti ja uuden ajon tuottama kuva tallennetaan välimuistiin. Silloin entisen ajon tuottama kuva tuhotaan sieltä.

Attribuutti isHtml: true tarvitaan, mikäli ohjelma tuottaa outputtiin kuvan HTML-muotoisena, eli esimerkiksi SVG-kuvan kuten plantuml tuottaa. Tällöin tulosta ei laiteta console-tyyliseen kehykseen. Mikäli ohjelma tuottaa PNG-kuvan, ei tätä attribuuttia tarvita.

Kuvan tuottamiseen tarvittavan koodin kehittämisen aikana voi olla järkevää pitää cache-attribuutti kommentissa (esimerkiksi niin, että aloittaa rivin #-merkillä) ja ajaa kuvan tekemistä tavallisena pluginina. Kun kuva on mieleinen, voi kommenttimerkin riisua cache-attribuutin edestä pois.

Tätä toimintoa voidaan käyttää minkä tahansa ohjelman kanssa, joka tuottaa kuvan tai tekstiä, esimerkiksi Python:

``` {plugin="csPlugin" #pythonkuvaaja}
type: py
cache: true
imgsource: image.png
fullprogram: |!!
import numpy as np
import matplotlib.pyplot as plt
BYCODEBEGIN
plt.plot([1, 3, 2,1,6,9,1 ])
BYCODEEND
plt.savefig('image.png')
!!
```
# pythonkuvaaja-1

Huomaa, että cache-tilassa ajetaan se koodi, mikä ajetaan sen jälkeen kun painetaan reset linkkiä tehtävätilassa. Eli käyttäjän mahdollisesti tekemät muutokset eivät vaikuta cache-kuvaan. Siksi mahdolliset muutokset pitää ensin kopioida "tehtävän" lähdekoodiin ja sitten laittaa cache-attribuutti todeksi.

5.3 Tyhjentämisen estäminen

Joskus on kuitenkin tapauksia, jolloin entisen kuvan tuhoaminen ei ole järkevää. Esimerkki tällaisesta tapauksesta on sellainen, jossa ajettava koodi muuttuu käyttäjäkohtaisesti. Esimerkiksi edellä haluttaisiin Bobin tilalle käyttäjän nimi:

``` {plugin="csPlugin" #uml2}
type: run
cache: true 
cacheClear: false
isHtml: true
cmds: java -jar /cs/java/plantuml.jar -tsvg uml.txt; cat uml.svg 
filename: uml.txt
fullprogram: |!!
@startuml
Alice -> %%username%%: Soita mulle
%%username%% --> Alice: Saatan soittaakkin
Alice -> %%username%%: Kivaa
Alice <-- %%username%%: Ring
@enduml
!!
```
# uml2-2
AliceAliceAnonymousAnonymousSoita mulleSaatan soittaakkinKivaaRing

Tällöin attribuutilla cacheClear: false, voidaan estää vanhan kuvan tuhoaminen. Ilman tuota attribuuttia jokaisen käyttäjän kohdalla kuva laskettaisiin uudelleen ja välimuistin hyöty menisi hukkaan. Kieltämällä tuhoaminen jää välimuistiin jokaiselle käyttäjälle valmiiksi hänelle laskettu kuva. Toki tämä kuluttaa välimuistia ja se voidaan tämän takia välillä tyhjentää, mutta sitten kun kuvia tarvitaan, ne lasketaan automaattisesti uudelleen.

Jos kuvassa on esimerkiksi päiväykseen tms. liittyvä muuttuva tekijä, niin silloin cacheClear: falsea ei kannata käyttää, koska kuva kuuluukin laskea kaikille käyttäjille uudelleen päiväyksen vaihtuessa ja silloin vanhan kuva saadaankin tuhota.

5.4 Otsikot

Kuvan alle saa normaaliin tapaan alaotsikon footer-attribuutilla:

footer: Kuva 7, binääripuu

Mikäli haluaa säilyttää osin plugin välillä normaalikäytössä ja silloin käyttää eri alaotsikkoa, voi cache-käyttöä varten antaa oman alaotsikon cacheFooter-attribuutilla

cacheFooter: Kuva joka on tehty käyttäjää %%username%% varten
# uml2-3
AliceAliceAnonymousAnonymousSoita mulleSaatan soittaakkinKivaaRing
Kuva joka on tehty käyttäjää Anonymous varten

5.5 Tyylit

Mikäli ohjelma tulostaa esimerkiksi tekstiä näytölle, niin se tulee oletuksena console-tyylillä.

``` {plugin="csPlugin" #shell}
type: shell
cache: true
fullprogram: echo 'Hello World!'
```
# shell
Hello World!

Haluttaessa tyyliä voidaan vaihtaa attribuutilla cacheClass:

``` {plugin="csPlugin" #shell2}
type: shell
cache: true
cacheClass: htmlresult
fullprogram: echo 'Hello World!'
```
# shell2
Hello World!

Luokan voi myös jättää tyhjäksi:

cacheClass:

jolloin teksti tulostuu normaalilla TIMin tyylillä.

Kuville oletuksena on htmlResult, joka keskittää kuvan. Jos kuva halutaan vasempaan reunaan, voi luokan laittaa tyhjäksi.

5.6 Vakiosisältö

Joskus voi olla tilanteita, joissa esimerkiksi ohjelman tuottamaa kuvaa pitää vielä käsin muokata. Silti ohjelman lähdekoodin säilyttäminen helposti saatavilla olisi mukavaa.

Tällöin voidaan menetellä seuraavasti:

  1. Lisää csPlugin, jossa on ohjelma, joka piirtää ajettaessa kuvan selaimelle.

  2. Aja ja muokkaa ohjelmaa, kunnes kuva on muokkaamista vaille valmis

  3. Ota kuvasta kuvakaappaus

  4. Muokkaa kuvaa tarpeen mukaan

  5. Lataa kuva TIMiin, kopioi osoite talteen

  6. Lisää pluginin koodiin attribuutti tyyliin:

     cacheHtml: <img src="/static/images/tim-logo.svg" alt="TIM logo"width="42">
  7. Jos kuva pitää toistaa uudelleen, kommentoi rivi cacheHtml laittamalla # sen eteen ja jatka kohdasta 2.

5.7 Käyttö LaTeX-tulostuksia varten

Toistaiseksi tämä ei toimi automaattisesti LaTeX-pohjaisten tulostusten kanssa, vaan niitä varten pitää ottaa kuvakaappus ja ilmoittaa kuva erikseen LaTeXin tulostukseen, ks. Tulostuksen ohje.

# attributes

6. Muita attribuutteja

Attribuuttien nimet ja oletukset. Jos oletusta ei ole sanottu, se on tyhjä.

"type","cs"                - pluginin tyyppi
"mode"                     - ACE-editorin moodi.  Oletuksena se, mikä tulee kielestä.
"file"                     - mikä tiedosto haetaan pohjaksi.  Yleensä joku kohta
                             korvataan replace-attribuutin avulla byCode
                             tai byFile-sisällöllä.
"program"                  - kuten file, mutta ohjelman sisältö voidaan kirjoittaa
                             TIMissä.
"fullprogram"              - katso tarkempi selostus omassa luvussaan
"fullfile"                 - katso tarkempi selostus omassa luvussaan
"filename"                 - millä nimellä käyttäjän syntynyt tiedosto tallennetaan
                             Jos tyyppinä text/args, tämä on se mitä käyttäjä
                             kirjoittaa args-kohtaan
"nosave",false             - dataa ei tallenneta, vaikka siihen jotakin kirjoittaisi
"nocode",false             - koodi poistetaan levyalueelta käännöksen jälkeen
                             tällä voi olla sivuvaikutus että ei näe käännösvirheitä
"upload"                   - näyteään upload-painike
"uploadbycode"             - näytetään upload-painke, mutta upload ei mene palvelimelle,
                             vaan käyttäjän kirjoittaman koodin tilalle
"uploadstem"               - upload-painikkeen eteen tuleva teksti
"uploadautosave", false    - kun tekstipohjainen upload on tehty, 
                             tallenetaanko (ja ajetaan) automaattisesti
"lang"                     - kieli, vaihtoehdot fi ja en, oletus en
"width"                    - iframe-pluginin leveys
"height"                   - iframe-pluginin korkeus
"-min-height"              - pienin korkeus jonka plugin vie.  Asettamalla tämän voi
                             vähentää "hyppelyä". Esim:  -min-height: 400px
"-max-height"              - suurin korkeus jonka plugin enintään vie. 
                             Jos plugin vie enemmän tilaa, tulee vierityspalkki reunaan. 
                              Esim:  -max-height: 800px
"iframeopts",""            - optiot iframelle.  csPluginissa oletus on:
                             iframeopts: 'seamless="seamless" 
                             sandbox="allow-scripts allow-forms allow-same-origin"' 
"table"                    - taunon alustus, esim. table: s10 tai
                             table: "1,2,3,4,5,6&ma=4&mb=5&ialku=0&iloppu=5"
"variables"                - taunon muuttujat, esim: a=3;b=2;c
"indices"                  - taunon indkesimuuttujat, esim: i=3;j=2;k
"replace"                  - mikä sana riviltä pitää löytyä, jotta se 
                             korvataan byCode-koodilla
"taunotype"                - vaihtoehtona ptauno, jolloin alkiot näkyvissä, muuten piilossa
"stem"                     - pluginin tekstiksi tuleva osa
"header"                   - ennen pluginia tuleva teksti (otsikko)
"footer"                   - plugin jälkeen tuleva teksti (esim. kuvatekstin tapaan)
"iframe",false             - tuleeko iframe
"usercode",""              - käyttäjän kirjoittama koodi (ei siis varsinaisesti attribuutti)
"codeunder",false          - näytetäänkö kokonaiskoodi alapuolella
"codeover",false           - näytetäänkö kokonaiskoodi yläpuolella
"open",!scope.isFirst      - avataanko sisältö automaattisesti
"viewCode",false           - näytetäänkö koko koodi oletuksena
"rows",1                   - montako riviä tekstialueeseen pohjaksi
"maxrows",100              - montako riviä korkeintaan ennen kuin rupeaa rullaamaan
"byCode"                   - teksti, jolla replace-kohdassa sanottu rivi korvataan ja
                             jonka tilalle tulee käyttäjän kirjoittama koodi
"byFile",""                - tekee saman kuin byCode, mutta hakee sisällön tiedostosta.
                             Ei saa olla yhtäaikaa byCode kanssa. Mikäli program tai file
                             puuttuu, niin tämä tulee kokonaisuudessaan editoitavaksi 
                             pohjaksi.
"outReplace",""            - ennen tuloksen lähettämistä outputtiin tullut teksti korvataan,
                             regexpinä
                             Jos halutaan monta korvausta, voidaan tämä esittää 
                             listana tyyliin
                             
                             outReplace:
                                - replace: Kissa
                                - by: Koira
                                - replace: Kana.*
                                - by: Lintu
                                
                            Mikäli by puuttuu, käytetään jokaisessa outBy arvoa 
                            (oletuksena tyhjä)
"outBy",""                 - millä outReplace korvataan?
"placeholder","Write your code here"
                           - jos syöttöalue jää tyhjäksi, mitä näytetään
"inputplaceholder","Write your input here"
                           - jos input-alue jää tyhjäksi, mitä näytetään
"argsplaceholder",scope.isText ? "Write file name here" : "Write your program args here"
                           - jos argumenttien syöttöalue jää tyhjäksi, mitä näytetään
"argsstem",scope.isText ? "File name:" : "Args:"
                           - argumentti-alueen otsikko
"userinput",""             - pohjateskti input-alueeseen
"userargs",scope.isText ? scope.filename : ""
                           - pohja argumenttikohtaan
"inputstem",""             - input-alueen otsikko
"inputrows",1              - input alueen rivimäärä
"toggleEditor",scope.isSimcir ? "True" : false
                           - voiko editorin piilottaa
"indent",-1                - sisennyksen minimimäärä
"user_id"                  - käyttäjän tunnus
"isHtml",false             - jos ajon palaute on html, poisteen XML-otsikko-osa siitä
"autorun", false           - ajetaanko plugin heti, kun dokumentti aukeaa
"autoupdate",false         - päivitetäänkö sisältö automaattisesti.  Jos jokin ms aika,
                             niin käytetään sitä aikaväliä
"noConsoleClear", false    - kielletään tulostuskonsolin tyhjennys ajojen välillä;
                             lähinnä järkevä autoupdaten kanssa
"canvasWidth",700          - piirtoalueen koko
"canvasHeight",300
"button",""                - Aja-painikkeeseen tuleva teksti
"noeditor",scope.isSimcir ? "True" : false
                           - piilotetaanko editori oletuksena
"norun",false              - piilotetaanko Aja-painike
"normal","Tavallinen"          - Tavallisen editorin linkin teksti
"highlight","Highlight"    - ACE-editorin teksti
"parsons","Parsons"        - Parsons-editorin teksti
"jsparsons","JS-Parsons"   - JavaScript-parsons editorin teksti
"editorMode",-1            - mikä on aloitusindkesi editorin rullaamiselle
                              -1 = käytetään käyttäjen edellistä valintaa
"showCodeOn","Näytä koko koodi" - teksti kokokoodin näyttämisen linkille
"showCodeOff","Piilota muu koodi" - teksti koko koodin piilottamiselle
"resetText","Alusta"       - Alusta-linkin teksti
"blind",false              - piilotetaanko vastaajan tiedot
"words",false              - käytetäänkö Parsons-editoria sana-muodossa
"editorModes","01"         - mitä editoreja rullataan editorin vaihdosta
                               0 = tavallinen
                               1 = ACE
                               2 = Parsons
                               3 = JS_parsons (kevyempi)
"path"                     - mihin hakemistoon.  Järkevä vaihtoehto on "user", jolloin
                             käyttäjälle tehdään oma "pysyvä" hakemisto ja
                             sitä voi käyttää peräkkäisillä ajokerroilla.
                             Katso tästä attribuutista lisää omasta luvustaan.
"savestate"                - mihin tiedostoon tallennetaan shell-ikkunan tila, jotta
                             seuraava käynnistys jatkuu samasta kohdasta.  Oletuksena
                             tilaa ei tallenneta.  Arvo voisi olla esim. 
                               savestate: "~/run/.state"
                             Samassa dokumentissa voi olla useita eri "tiloja".
                             webconsolet vaihtavat tilariviä sen mukaan, mitä niihin
                             liittyvissä savestateissa on polkuna.
"buttons"                  - rivinvaihdoilla eroteltu lista painikkeista, jotka tulee
                             editorin alapuolelle ja joita painamalla
                             painikkeessa oleva teksti kopioidaan edit-alueeseen

                             Esim:
                             
                                buttons: |!!
                                $
                                \int
                                ^
                                _
                                {
                                }
                                !!
                                
                            Katso lisää omasta luvust.
"cmd"                      - komento, joka ajetaan, jos type: run tai useimmilla kääntäjillä
                             normaalin ajokomennon sijaan. 
                             Jos type: run, tiedoston nimi liitetään perään.
                             Mahdollinen userargs liitetään komennon perään.
                             Esim:
                                filename: example/Laskuja.java
                                type: run
                                cmd: javac
"cmds"                     - Pythonin format-jono, jolla voidaan tehdä ajokomento,
                             johon liitetään tiedoston nimi kohtaan {0}.  
                             userargs liitetään kohtaan {1}.
                             Esim:
                                type: run
                                button: Käännä
                                filename: example/Laskuja.java
                                cmds: "javac {0}"
                             Tai:
                                type: cc
                                cmds: valgrind -q --leak-check=full {0}
"runargs",""               - agumentit, jotka liitetään em. komennon perään ja tämän perään
                             tulee vielä käyttäjän antaman argumentit
"justCompile",false        - tekee pelkän käännöksen
"justCmd",false            - ei käännä, tekee vain cmd tai cmds attribuutissa 
                             sanotun komennon
"justSave",false           - vain tallentaa (mutta voi käyttää oikean kielen tyyppiä)
"languages",all            - puolipisteellä eroteltu lista kieliä, joita
                             käytetään, mikäli type:ssä on ensimmäisenä sana all.
                             Ilman tätä attribuuttia kaikki kielet ovat listassa
"selectedLanguage",type    - mikä on kielilistan oletuskieli
"delay",0                  - kuinka kauan (ms) odotetaan ennen kuin kaapataan 
                             kuva Java-ajossa.
                             Jos arvo on negatiivinen, ajetaan itse ohjelma eri säikeessä
                             Esim. JavaFX tarvitsee tämän negatiivisen arvon
                             (n. -700 toimii).
rect,                      - alue, josta kuva kaapataan muodossa x,y,leveys,korkeus
opt,                       - C/C++ kääntäjille menevät optiot
"validityCheck",""         - minkälainen regexp suoritetaan käyttäjän "koodille"
                             ennen kuin se edes päästetään ajoon selaimen päässä
"validityCheckMessage",""  - mikä teksti, jos em. ehto ei täyty.  Mikäli ei anneta,
                             tulostaa em. ehdon.
"validityCheckForceSave",false  - tallentaa vaikka tarkastus ei mene läpi
"wrap","-1"                - kuinka pitkiä rivejä saa kirjoittaa. Jos >=0,
                             näkyy editorin alapuolella pieni input-kenttä, jossa arvoa
                             saa vaihtaa.  0 tai pienempi ? ei-wrap.  text-tyypille
                             oletuksena 70.
"maxConsole",20000         - pisin sallittu konsolitulostus
"extrafiles"               - attribuutti ylimääräisten tiedostojen käsittelyyn.  
                             Pidempi selitys alempana.
"emptyResult","No result"  - mikä tulos näytetään, jos SQL-kyselyn tulos on tyhjä
"errorcondition", ""       - RegExp ehto, jonka löytyessä käyttäjän koodista 
                             ajaminen jätetään
                             tekemättä ja näytetään errormessage
"errormessage": "Not allowed to use: " + errorcondition
"warncondition: ""         - RegExp ehto, jonka löytyessä käyttäjän koodista 
                             ajaminen tehdään,
                             mutta ajon jälkeen näytetään warnmessage
"warnmessage: "Not recommended to use: " + warncondition
"dockercontainer", "timimages/cs3": oletuksena ajetaan cs3-imageilla
"mockconsole", "True"        Toistaiseksi vain C# - konsoli, joka kaiuttaa inputin näyttöön
                             niin syöttö vaikuttaa aidommalta.  Filttereitä varten kannattaa
                             ottaa pois käytöstä.
"showRuntime", "False"       Näytetäänkö suoritusajat oikeaan alanurkkaan?
"savedText", "Saved {0}"     Teksti joka näytetään ei-ajettavien 
                             tallentamisesta.  {0} korvataan tiedoston nimellä.
"docaddr", ""                Osoite josta muodostetaan dokumenttien säilytyspaikka.
                             Tämän ansiosta dokumenttien osoite on aina vakio.
"locpath", ""                lokaali suhteellinen polku josta dokumenttejä ruvetaan
                             tekemään
"nofilesave", "False"        editorin dataa ei tallenneta tiedostoon.
                             Hyödyllinen oikestaan vain massdokumentteja tehtäessä.
"git"                        asetukset gitRegille ja gitCheckille
"gitDefaults"                oletusarvoja git lähteille, gitRegille ja gitCheckille
"rootPath", sama kuin path   juuripolku, joka otetaan mukaan ajoon dockeriin
"masterPath"                 polku masters-kansiossa olevaan kansioon.
                             Käytetään master-tiedostolähteessä ja ExtCheckissä
"files"                      lista tiedostolähteistä (kts. ohje alempana). Tarvitaan vain, kun halutaan 
                             palauttaa useampi tiedosto yhdessä pluginissa
"moreFiles"                 lista tiedostolähteistä (kts. ohje alempana).
"deleteFiles"                lista poistettavista tiedostoista
"mounts"                     lista "mounteista jotka tehdään.
                             Toisteiseksi toimii vain:
                             
                                mounts: [ohj1Content]
                             
                             Tällöin käyttäjän hakemiston alla näkyy
                             Content-kansio, jossa on Ohj1-kurssin
                             kuvia.
                            
                             Lista voidaan ylläpitäjien toimesta lisätä tarpeen vaatiessa.
"classname"                  mikäli C# luokantunistus ei löydä oikeaa pääluokkaa
                             voidaan se antaa tällä, esim
                             
                                classname: Tankkipeli
                                classname: demo7.AngryLego
"sourcefiles"               mitkä tiedostot käännetään.  Toimii toistaiseksi vain C#.
                            Jypeli-ohjelemissa pitää sanoa myös Ohjelma.cs
                            Ellei pääohjelmaa ole itse tehty johonkin luokkaan.
                            Esim:

                                sourcefiles: Ohjelma.cs HangryDog.cs Luokat.cs
"jsBrowserConsole", "False" Käytetäänkö type: js ohjelmissa omaa konsolia (false) 
                            vai selaimen konsolia (true)
"editorreadonly", "False"   Onko editori readonly-tilassa
"save_video", "false"       Tallennetaanko videota ajosta. Toistaiseksi
                            vain type: jypeli.  Ks tarkemmin oma alaluku
"disableUnchanged", false   Laitetaanko Tallenna harmaaksi tallennuksen jälkeen.
                            Toimii tyypeille: text, xml, css ja md.
undo:                       Tietue siitä miten undo toimii
  button: "Palauta viimeisin"   linkin teksti (oletus ei käytössä eli tyhjä)
  title: "Palauttaa..."         ohjeteksti
  confirmation: "Haluatko.."    Varmistusikkunan teksti (tyhjä => ei varmistusta)
"copyLink", "copy"          Teksti joka tulee siihen millä editorin sisältö kopioidaan
"hide"                      Tietue jossa kerrotaan mitkä elemnetit piilotetaan.  Toistaiseksi
                            {wrap: false, changed: false} 
                            wrap: näytetäänkö wrap-ohjaimet
                            changed: näytetäänkö vasen vihreä palkki, että teksti on muuttunut

ACE-editorin modeja voi katsoa esim: kitchen-sink.

# buttons

7. buttons

Attribuutilla buttons voidaan määritellä mitä painikkeita käyttäjälle tulee. Painikkeiden painaminen lisää editoriin tekstiä.

7.1 Perusmuoto

Perusmuodossa luetellaan allekkain painikkeiden tekstit. Silloin nämä samat tekstit tulevat editoriin sellaisenaan.

Esimerkiksi:

buttons: |!!
a = 5
print()
!!

saadaan painikkeet:

# btn1

Jos haluatan että lisäyksen jälkeen kursori on tietyssä kohdassa, voidaan tämä paikka merkitä kursorimerkillä (, tulee editorin Charactres/Cursor):

buttons: |!!
a = 5
print(⁞)
!!
# btn1cursor
# btndifftext

7.2 Editoriin eri teksti kuin painikkeessa näkyy

Mikäli painikkeisiin tarvitaan enemmän toimintoja, esimerkiksi erilainen painikkeen teksti kuin mitä tekstiä lisätään editoriin, pitää käyttää muotoa jossa sanotaan taulukossa (eli haksulkeissa):

  • näkyvä teksti
  • editoriin lisättävä teksti
  • mahdollinen ohjeteksti, joka näkyy kun kursori viedään painikkeen päälle
buttons: |!!
["sijoita 5", "a = 5\n", "sijoittaa arvon 5 muuttujaan a"]
["tulosta a", "print(a)"]
["⏎", "\n", "uusi rivi"]
!!
# btn2
# askuser

7.3 Arvojen kysyminen käyttäjältä

Vastaavasti jos halutaan "aukkoja", joihin käyttäjältä kysytään arvo ennen teksti sijoittamista, voidaan käyttää \\? -merkintää kysyttävien arvojen kohdalla.

buttons: |!!
["sijoita", "a = \\?\n", "sijoittaa arvon muuttujaan a"]
["tulosta", "print(\\?)\n"]
["⏎", "\n", "uusi rivi"]
!!
# btn3

Edellä tulee aina vakiokysymys. Usein halutaan muotoilla itse kysymystä ja mahdollisesti vielä käyttää oikeellisuustarkistusta, eli lisätään perään taulukoita, joiden sisältö on:

  • oletusarvo
  • kysymysteksti
  • regexp jolla oikeellisuus tarkistetaan (optio)
  • virheilmoitus (optio)

Toistaiseksi kaikki pitää saada yhdelle riville yhden painikkeen osalta

buttons: |!!
["sijoita", "\\? = \\?\n", "sijoittaa arvon muuttujaan", ["a", "Mihin muuttujaan sijoitetaan?", "^[A-Za-z][A-Za-z0-9]*$", "Pitää olla yksi kirjain ja mahdollisesti kirjaimia ja numeroita"], ["5", "Mikä arvo", "^[0-9]+$", "Pitää olla luku"]]
["tulosta", "print(\\?)\n","", ["a", "Mikä muuttuja tulostetaan"]]
["⏎", "\n", "uusi rivi"]
!!
# btn4
# mathbuttons

7.4 Matematiikka ja painikkeet

Painiketekstit voi ajaa TeX-renderöijän läpi lisäämällä painikkeiden []-muotoon merkkijono "math" ja kirjoittamalla LaTeX-koodi \\( \\) tai \\[ \\]-merkkien sisään.

Huomaa, että YAML-syntaksin takia \-merkit pitää kirjoittaa muodossa \\.

# btn-tex1

Lisäämällä pluginiin miniSnippets-luokan voi tehdä yksinkertaisen matikkaeditorin:

# btn-tex2

7.5 Painikkeiden ulkoasun muuttaminen

Mikäli painikkeiden ulkoasua halutaan muuttaa, voidaan tälle tehdä oma TIM-tyyli (ks: Tyylien laadintaopas) tai laittaa dokumentin tai preamblen css-asetuksiin esimerkiksi:

css: |!!
p.csRunSnippets {
    margin: 5px 0 0 0;
}
.csRunSnippets button {
    font-size: 12px;
    font-family: serif;
    height: 18px;
}  
!!

7.6 mdButtons

Attribuutilla mdButtons voidaan määritellä samat asiat kuin edellä käyttäen YAML-syntkaksia. Lisäetuna on että voidaan käyttää kaikkea MarkDown-syntaksia.

Aikaisempi esimerkki olisi tällä syntaksilla:

mdButtons: 
 - text: sijoita
   data: \? = \?\n
   expl: sijoittaa arvon muuttujaan
   placeholders:
     - default: a
       text: Mihin muuttujaan sijoitetaan?
       pattern: ^[A-Za-z][A-Za-z0-9]*$
       error: Pitää olla yksi kirjain ja mahdollisesti kirjaimia ja numeroita
     - default: "5" 
       text: Mikä arvo 
       pattern: ^[0-9]+$
       error: Pitää olla luku
 - text: tulosta
   data: print(\?)\n
   placeholders:
     - default: a
       text: Mikä muuttuja tulostetaan
 - text: ⏎
   data: \n
   expl: "uusi rivi"
 - text: md:![](https://tim.jyu.fi/static/images/tim-logo.svg)
   data: "TIM"
# btn4b

Molempia syntakseja buttons ja mdButtons voi käyttää samassa pluginissa. Silloin ensin näytetään buttons-attribuutilla määritellyt painikkeet.

8. Cleaning the compiler error messages

Sometimes in the compiler error messages there might be something that is not meant to be shown to students. For example, the code might have some kind of check functions that checks the code. If there are compiler errors, the whole source code is shown to students. To remove the extra code, one can use cutErrors.

Suppose the generated source code looks like:

public class Calculations {
    public static void main(String[] args) {
        int  value = userFunction(params);
        ...
        check();
    }
    
    public static int userFunction(...) {
       ...
    }
    
    // BEGINCHECK
    
    public static void check() {
       // call user function with different parameters and check
       // if get right results 
    }
    // ENDCHECK
}    
    

Suppose one wants to remove everything between BEGINCHECK and ENDCHECK and remove the call to check() function. The cutErrors could be like:

cutErrors:
  -
    replace: "(// BEGINCHECK.* ENDCHECK)"
    by: ""
  - 
    replace: "(  check\\(\\);)"
    by: ""

If the value of by attribute is empty, it can be dropped away like:

cutErrors:
  -
    replace: "(// BEGINCHECK.* ENDCHECK)"
  - 
    replace: "(  check\\(\\);)"

that can be shortened like:

cutErrors:
  - replace: "(// BEGINCHECK.* ENDCHECK)"
  - replace: "(  check\\(\\);)"

or even:

cutErrors:
  - "(// BEGINCHECK.* ENDCHECK)"
  - "(  check\\(\\);)"

If there is only one item to replace by "", the shortest allowed form is:

cutErrors: "(// BEGINCHECK.* ENDCHECK)"

The search rule is RegEx with dot matching also \n. The area to replace must be in (). That means that to use chars ( and ) as normal characters, they must be prefixed by \\ (note two of them). As done in the example, to replace check(), one must write:

check\\(\\)

9. save_video

Attribuutilla save_video voidaan määrittää Jypeli-ohjelma tallentamaan tietty pätkä videota ja sitten kuvan sijaan ohjelman alle tulee video.

Aliattribuutit:

frames:       - kuinka monta ruutua kaapataan (oletus 60)
skip_farmes:  - kuinka monta ruutua jätetään jo askeleessa väliin (oletus 0)
fps:          - millä nopeudella video tehdään (oletus 30)

Oletuksilla, eli kirjoittamalla vain

skip_fames:

Saataisiin 60 kuvaa (joka vastaa 1 sek ajoa) ja se tehdään 30 fps videoksi, eli video kestää silloin 2 sek. Jos halutaan ajan kanssa 1:1, voi laittaa joko

fps: 60

tai

skip_frames: 1

Pitkissä videoissa joutuu sallittua ajoaikaa kasvattamaan.

Esimerkki:

``` {plugin="csPlugin" #lumiukko1}
type: jypeli/doc
nomain: true
filename: Lumiukko
ulimit: ulimit -t 30 -f 80000  # kasvatetaan suoritusaikaa (s)
timeout: 30000                 # ja aikaa jonka selain odottaa vastausta (ms)
save_video:
  frames: 600  # ajetaan 600 päivityskierrosta (MonoGamessa tämä vastaa oletuksena 10 s)
  skip_frames: "0" # Otetaan joka päivityskierroksesta kuva
  fps: 30 # 30 otettua kuvaa/s = 20 s video
byCode: |!!
using Jypeli;
public class Lumiukko : PhysicsGame
{
  public override void Begin()
  {
    Camera.ZoomToLevel();
    Level.Background.Color = Color.Black;
    Level.CreateBorders();

    var p1 = new PhysicsObject(100.0, 100.0, Shape.Circle);
    Add(p1);
    p1.Hit(RandomGen.NextVector(1000, 1000));
  }
}
!!
```
# lumiukko1

10. fullprogram ja fullfile

Jotta olisi helpompi ylläpitää ulkoisella IDE:llä ohjelmia, jotka samalla tarkistavat toimintaansa, on attribuutit fullprogram ja fullfile, joiden on tarkoitus tuottaa samaa sisältöä, mitä saataisiin joukolla replaceN ja byCodeN -attribuuteilla. Ideana on, että koko ohjelmaa voidaan sellaisenaan kehittää ja testata IDE:ssä ja sitten kopioida sisältö muuttamattomana TIMiin.

Esimerkki:

``` {#t1kuutosia plugin="csPlugin"}
type: cs/comtest/doc
-pointsRule: 
    readpoints: "RANDOMCHECK: (.*)\n"
-deleteLine: "(\n#[^\n]*)"
header: MontakoKuutosta
filename: Laskuja
stem: "Muuta funktio niin että se palauttaa kuutosten lukumäärän"

fullprogram: |!!
public class Laskuja
{
    public static void Main()
    {
        int m = MontakoKuutosta(3, 5);
        System.Console.WriteLine("Kuutosia oli " + m);
        System.Console.WriteLine("Kuutosia oli " + MontakoKuutosta(1, 6));
        Tarkista(); // REMOVELINE
    }


#if true
    // DELETEBEGIN
    /// <summary>
    /// Funktio palauttaa kuinka monta parametria on arvolla 6
    /// </summary>
    /// <param name="a">ensimmäinen luku</param>
    /// <param name="b">toinen luku</param>
    /// <returns>lukumäärän parametreista arvolla 6</returns>
    /// <example>
    /// <pre name="test">
    ///     Laskuja.MontakoKuutosta(1,1) === 0;
    ///     Laskuja.MontakoKuutosta(1,6) === 1;
    ///     Laskuja.MontakoKuutosta(6,6) === 2;
    /// </pre>
    /// </example>
    public static int MontakoKuutosta(int a, int b)
    {
        int lkm = 0;
        if (a == 6) lkm++;
        if (b == 6) lkm++;
        return lkm;
    }
    // DELETEEND
#else 
    // BYCODEBEGIN
    /// <summary>
    /// Funktio palauttaa suuremman luvuista a ja b
    /// </summary>
    /// <param name="a">ensimmäinen luku</param>
    /// <param name="b">toinen luku</param>
    /// <returns>suurempi luvuista a ja b</returns>
    /// <example>
    /// <pre name="test">
    ///   // Laskuja.Suurempi(2,1) === 2;
    /// </pre>
    /// </example>
    public static int Suurempi(int a, int b)
    {
        if (a >= b)
        {
            return a;
        }
        return b;
    }
    // BYCODEEND
#endif
    // REMOVEBEGIN


    public static void Tarkista()
    {
        System.Console.WriteLine("-----------------------------------------------------------------");
        double p = 0;
        p += t(1, 1);
        p += t(2, 6);
        p += t(6, 2);
        p += t(6, 6);
        p += t(7, 6);
        p *= 2.0 / 5;
        System.Console.WriteLine("Pisteet: {0:0.00}", p);
        System.Console.WriteLine("RANDOMCHECK: {0:0.00}", p);
    }


    public static int t(int a, int b)
    {
        int tuli = MontakoKuutosta(a, b);
        int piti = Malli(a, b);
        if (tuli == piti) return 1;
        System.Console.WriteLine("Syöte: a={0}, b={1}, palautti: {2}, piti palauttaa {3}", a, b, tuli, piti);
        return 0;
    }


    public static int Malli(int a, int b)
    {
        int m = 0;
        if (a == 6) m++;
        if (b == 6) m++;
        return m;
    }
    // REMOVEEND
}
!!
```

Edellä on tarkoituksena saada koodi, jossa IDEssä ajettuna käytetään tuota mallivastauksena toimivaa #if true osaa. TIMissä ajettuna käyttäjälle näytetään BYCODEBEGIN/BYCODEEND välillä olevaa pohjafunktiota, jonka on vain tarkoituksena toimia mallina miten funktio kommentoidaan ja testataan.

Käyttäjältä poistetaan näkyvistä REMOVEBEGIN/REMOVEEND välinen osa Näytä koko koodi-näkymässä sekä myös mahdollisten virheilmoitusten tapauksessa. Samoin poistetaan pääohjelmassa oleva Tarkista-rivi. Kuitenkin nämä rivit menevät kääntäjälle. Kokonaan käännöksestä TIMissä poistetaan DELETEBEGIN/DELETEEND väli sekä deleteLine-attribuutilla sanotut rivit, eli tässä tapauksessa ne, jotka alkavat #-merkillä. Alempana on esimerkki miltä näkymä näyttää TIMissä käyttäjälle.

Attribuutti fullprogram voidaan korvata myös attribuutilla fullfile, jolloin annetaan URL, josta ohjelma luetaan. Sisältö voi olla täysin samanlainen kuin edellä. Tämän tarkoituksena on mahdollistaa koodin ylläpito esimerkiksi versiohallinnassa. Mikäli attribuuttien edessä on miinus, ei käyttäjä näe muuta koodin osaa kuin pohjakoodiksi tulleen osan. Itse fullX-osa ei koskaan tule käyttäjän näkyville.

Netistä haettavat tiedostot puskuroidaan, jolloin jos niitä muutetaan, on syytä tyhjentää puskurit:

https://tim.jyu.fi/cs/refresh

Esimerkin avainsanojen merkitys:

  • REMOVEBEGIN - aloittaa lohkon, joka poistetaan virheilmoituksista ja dokumentaatiosta. Käännettävään koodiin lohko jää. Koko rivi, jolla tämä avainsana poistuu. Tämä käytössä jos attribuuttia cutErrors ei anneta.
  • REMOVEEND - lopettaa em lohkon
  • DELETEBEGIN - aloittaa lohkon joka poistetaan kaikkialta
  • DELETEEND - lopettaa em lohkon
  • BYCODEBEGIN - aloittaa lohkon, joka korvataan käyttäjän vastauksella. tämän lohkon sisältö menee oletukseksi käyttäjän vastaukseen
  • BYCODEEND - lopettaa em. lohkon
  • REMOVELINE - regexp sääntö, jonka toteuttava osa poistetaan virheilmoituksista ja dokumentaatiosta
  • DELETELINE - regexp sääntö, jonka toteuttava osa tuhotaan kokonaan
  • RANDOMCHECK - jono, joka korvataan satunnaisella heksajonolla, joka on ainutkertainen jokaisella ajokerralla. Tällä pienennetään mahdollisuutta huijata pistelaskua liian yksinkertaisilla tavoilla
  • #if - C#-kielen makro, jolla voidaan säätää, meneekö joku koodinosa käännökseen vaiko ei. Edellä on haluttu, että käyttäjän syötteeksi tulee oletuksena aivan eri funktio, jota myös halutaan IDE:ssä helposti testata, mitä tulee tulokseksi. Varsinainen itse tehty funktio syödään TIMissä pois. Jatkossa voi tulla vielä avain sanat MODELANSWERBEGIN ja MODELANSWEREND. Attribuutilla deleteLine tuhotaan TÄSSÄ esimerkissä #-alkuiset rivit pois näkyvistä. Python-koodissa eikä muissa tätä ole syytä olla ainakaan samassa muodossa.

Nuo REMOVE- yms alkuiset rivit kannattaa aloittaa ko. kielen kommentilla, siis Pythonissa #-merkillä.

Huomaa että BYCODEBEGIN/BYCODEEND lohkon sisällä ei saa olla muita "muokkaavia" lohkoja!

Edellä jää käyttäjälle vielä mahdollisuus huijata tekemällä omaksi vastauksekseen kutsun:

    return Malli(a,b);

Tätä voidaan estää, mikäli mallifunktion nimessä käytetään satunnaisjonoa, eli korvataan funktion kutsu ja nimi tyyliin:

       int piti = MalliRANDOMCHECK(a, b);
...    
    public static int MalliRANDOMCHECK(int a, int b)
    

jolloin ajon aikana funktion nimi voi olla mitä tahansa. Periaatteessa käyttäjä voi selvittää tämänkin nimen, mutta se vaatii jo enemmän osaamista. Toinen mahdollisuus, jota on hieman vaikeampi kiertää, on lisätä kutsuun ylimääräinen parametri:

       int piti = Malli(a, b, "RANDOMCHECK");
...    
    public static int MalliRANDOMCHECK(int a, int b, string check)
    {
       if ( check != "RANDOMCHECK" ) ... huijaus ...

Alla jälkimmäisellä tavalla toteutettu plugin (tosin muutettu niin, että funktio on valmiiksi oikean niminen ja kääntyvä), jota saa yrittää saada toimimaan huijaamalla. Tavoitteena on saada itselleen tuosta 5 pistettä (oletuksena oikeasta tulee 2p).

# t1kuutosia

10.1 Extra files

Yksi isompi attribuutti on extrafiles, jolla on tarkoitus hakea mahdollisesti ajossa tarvittavia aputiedostoja. Muoto on:

-extrafiles:
    -
        name: temp/k6_with_NAND.txt
        delete: true
        text: |
            {
                "devices":[
                {"type":"DC","id":"dev0","x":56,"y":144,"label":"DC"},
            ...
    -
        text: kissa   # tämä saan nimekseen extrafile2
    - 
        name: kuva.png
        type: bin
        file: "http://oma.com/kuva.png"

Eli kyseessä on taulukko, jossa luetellaan tarvittavat tiedostot. Tiedoston sisältö voidaan antaa tekstinä kuten 1. esimerkissä tai sitten hakea netistä. Jos tyyppiä ei anneta, haetaan tiedosto tekstinä. Attribuutti delete: true aiheuttaa sen, että ajon jälkeen tiedosto tuhotaan.

Attribuuttia voidaan käyttää kaikkien tyyppien kanssa ja tiedostot haetaan ennen kääntämis- ja ajamiskomentoja.

# extrafiles

11. Arvostelu extrafiles avulla

Esimerkki, jossa pitäisi antaa joku kissaeläimen nimi ja sitten saa pisteitä seuraavasti:

- tiikeri: 0.8 p
- kissa: 0.5 p
- muut 0 p

ja noista kustakin tulee joku palaute:

# lueKissa

Koodi, jolla em. plugin on tuotettu:

``` {#lueKissa plugin="csPlugin"}
type: text
path: user
placeholder: "kirjoita joku kissaeläimen nimi tähän"
filename: vastaus.txt
-pointsRule:      
    readpoints: "Pisteet: (.*)\n" 
cmd: "python3 arvostele.py vastaus.txt"
-extrafiles:
    -
        name: arvostele.py
        text: |!!
import sys
import re
lines = open(sys.argv[1], 'r').read()
if re.match(".*tiikeri.*", lines):
    print("Pisteet: 0.8")
    print("Tämä oli hyvä")        
elif re.match(".*kissa.*", lines):
    print("Pisteet: 0.5")
    print("Aika tavallinen")
    
else:
    print("Pisteet: 0.0")
    print("En tunne tätä kissaeläimeksi!")
!!        
```

Edellä voitasiin Pisteet-sanan tilalla käyttää sanaa RANDOMCHECK, jos halutaan pienentää kaappauksen mahdollisuutta. Tosin tässä kun käyttäjä ei pääse kirjoittamaan ajettavaa koodia, on sen todennäköisyys aika pieni muutenkin.

12. Palautteen antaminen HTML:änä tai kuvana

Palautteen voi antaa em. tavalla vaikka Python-skriptillä tekstinä tai sitten myös lisäksi kuvana tai HTML:nä lisäämällä attribuutit (kaikki tai vain yhden)

-replyImage: URLKUVAAN JONNEKIN

-replyHTML: |!!
<p>HTMLää joka näkyy vastauksen jälkeen</p>
!!

-replyMD: |!! 
**Hyvä %%username%%!** Huomasit että se on $3x^2 + 2$

!!

Palaute tulee keskitettynä tehtävän alle. Jos haluat että kaikki dokumentin palautteet ovat jollakin eri tavalla, niin lisää dokumentin asetuksiin esimerkiksi:

css: |!!
.htmlresult {
    text-align: left;
    margin-left: 3em;
    margin-right: 3em;
}
!!

13. Vinkkien ostaminen

Edellistä voisi käyttää hyväkseen esimerkiksi siten, että on "tehtävä", jonka ainoa toiminto on "ostaa vinkki". Tämän painamisesta annetaan vaikkapa -2p ja siten kun on painettu, näytetään vinkkiteksti.

``` {plugin="csPlugin" #xt2kaosto2}
type: text
wrap: -1
filename: Vinkki_käytetty
header: "Osta vinkki lukumäärän laskemisesta: (-2p)"
stem: 'md:Jos haluat vinkin ja mallivastauksen,
voit ostaa sen tästä. [Älä vaan osta]{.red} tätä jos et tarvitse, **ostoa ei voi palauttaa**!
Jos vinkki häviää näytöstä, saat sen takaisin ilman lisähintaa "ostamalla" uudelleen.
'
editorModes: 
button: "Osta vinkki 2:lla pisteellä"
-pointsRule: 
    code: -2
    expectCode: .*
    min_points: -2
-replyMD: |!!
Aloita siitä, että **kissa** on hieno eläin!
!!
```

14. Pluginin tyyppi/Kielet

Pluginin type attribuutti voi saada arvoja:

"jypeli",       - C\# JyPeli-ohjelma 
"java",         - perus Java-ohjelma
"graphics",     - Java-ohjelma, johon linkitetään Graphics-kirjasto 
"cc",           - C-ohjelma 
"c++",          - C++ -ohjelma 
"shell",        - shell -ohjelma 
"py",           - Python-ohjelma 
"fs",           - F# -ohjelma 
"clisp",        - CommonLisp -ohjelma 
"jjs",          - JavaScript palvelimella 
"psql",         - PostgreSQL 
"sql",          - SQLite
"alloy",        - ??? 
"text",         - pelkkä teksti, ainoastaan tallennetaan käyttäjän levylle 
"cs",           - C# (oletus)
"run",          - komento, joka ajetaan
"md",           - md (esim. ohjeiden kirjoittamista varten)
"js",           - JavaScript selaimessa
"sage",         - Sage-matematiikka
"simcir",       - SimCir-piirisimulointi
"r"             - R-kieli
"all"           - mikä tahansa kieli, tällöin tulee valintalista, kielet
                  voi rajata languages attribuutilla
"xml"           - periaatteesa sama kuin text, mutta syntax highlight xml:än mukaan                  
"css"           - periaatteesa sama kuin text, mutta syntax highlight css:än mukaan
"octave"        - Octave
"lua"           - Lua
"upload"        - tekee pluginista Upload-pluginin.  Oletuksena poistetaan
                  kaikki muu turha.  Upload voi olla käytössä yhtäaikaa
                  ilmankin tätä, kun käyttää upload-attribuuttia, mutta silloin
                  pitää itse poistaa turhia painikkeita yms., jos niitä ei haluta.
"html"          - HTML opetukseen
"glowscript"    - 3D java grafiikka
"vpython"       - kuten em, mutta Pythonmaisella kielellä
"swift"         - Applen uusi kieli
"octave"        - Octave matematiikka 
"processing"    - Processing-kieli
"wescheme"      - WeScheme
"kotlin"        - Kotlin
"fortran"       - Fortran
"scala"         - Scala
"rust"          - Rust
"pascal"        - Free Pascal
"go"            - GO, kääntäjä on, mutta ei vielä valmista toimintoa, käännä sh:lla
"extcheck"      - ExtCheck, tarkemmat ohjeet myöhemmin
"gitreg"        - GitReg, rekisteröintilaatikko ulkoiseen git palveluun.
"ts"            - TypeScript
"mathcheck"     - MathCheck 
"maxima"        - Maxima

Attribuutin type yhteyteen voidaan kauttaviivalla liittää muitakin määreitä: language/modifier 1/modifier 2/... language määrää kielen ja modifiereilla voidaan muokata käyttäytymistä. Esimerkkejä:

type: cs/comtest        - voidaan ajaa ComTestejä ja tulee Test-painike
type  java/input/args   - tulee näkyviin input-alue ja args-alue 
type: csconsole/shell   - shell, jota käytetään erilaisesta konsolista
type: cs/doc            - tulee myös Document-linkki, josta tulee Doxygen-dokumentti
type: all/comtest       - tulee lista valittavista kielistä

input-määreen lisäksi voidaan tarvita attribuutti stdin, jolla kerrotaan, mikä tiedosto liitetään oletussyötteeseen:

stdin: input.txt

ACE-editorin korostusta vaihdetaan tyypin mukaan.

Attribuutin type modifierit voi saada arvoja:

"jypeli",       - C\# JyPeli-ohjelma 
"graphics",     - Java-ohjelma, johon linkitetään Graphics-kirjasto 
"tauno"         - Tauno
"tiny"          - Pienempi laatikko
"input"         - Lisää input laatikon
"args"          - Lisää args laatikon
"doc"           - Lisää Document-linkki, josta tulee Doxygen-dokumentti
"csconsole"     - C# konsoli
"parsons"       - Parsons editori
"gitcheck"      - Tarkastaa, onko käyttäjä rekisteröitynyt
                 ulkoiseen git palveluun ja tekee asioita sen perusteella.

15. path-attribuutti

  1. Ei path attribuuttia (siellä pluginissa)
    • jokaiselle ajokerralle tulee uniikki oma polku ja tiedot tuhotaan ajon jälkeen
    • ei sovi SQL-kurssille
  1. path: user
    • käyttäjälle luodaan TIMin /tmp/user hakemistoon oma käyttäjäkohtainen hakemisto (nimi salattu) eli /tmp/user/HASH ja sen alle dokumenttikohtainen hakemisto, johon tehtävän data (tiedostot, käännökset yms) tallentuu. Eli hakemisto on /tmp/usr/HASH/docid
    • voi käyttää saman dokumentin sisälle samassa dokumenteissa olleita tehtävien tiedostoja
    • ei toimi, jos esim. samaa tietokantaa halutaan jatkaa toisessa dokumentissa, eli esim. kukin demokerta on omana dokumenttinaan
  1. path: user/demot
    • user perään voidaan antaa kiinteä nimi, ja nyt data tallentuu /tmp/user/HASH/demot hakemistoon (nimi voi toki olla jokin muukin kuin demot ja se voi mennä syvemmällekin)
    • voidaan käyttää samoja tiedostoja eri dokumenttien välillä. Sopii esim. tuohon, että demossa 1 tehdään tietokanta ja muissa demoissa käytetään sitä samaa.
  1. rootPath määritelty:
    • rootPathin arvo laitetaan pathin eteen
    • esimerkiksi rootPath: user ja path: demot yhdessä tarkoittavat, että path on user/demot

Kohdissa 2, 3 ja 4 pitää muistaa, että /tmp voidaan tuhota milloin tahansa, jolloin käyttäjälle pitää olla ohje, kuinka voi alustaa tilanteen takaisin. Käyttäjän vastaukset ovat TIMin tietokannassa ja niihin nuo hakemistot eivät vaikuta. Ainoastaan tehtävissä syntyviin "tilapäistiedostoihin".

Jos päätyy kohtiin 2, 3 tai 4, kirjoittamista helpottaa, jos pistää samana pysyvät attribuutit dokumentin alkuun settings-lohkoon tyyliin:

``` {settings=""}
global_plugin_attrs:
    csPlugin:
        path: user/demot
```

jolloin dokumentin jokainen plugin saa tuon saman attribuutin.

15.1 rootPath-attribuutti

Kun tämä on määritelty, otetaan docker kontin juuripoluksi tämä mukaan. Ohjelman ajo kuitenkin suoritetaan path-attribuutin mukaisessa polussa.

Esimerkki:

rootPath: user/cplusplus
path: Module1/exercise1

Näillä arvoilla dockeriin otetaan user/cplusplus (user sama kuin yllä pathissä) kansion alla olevat tiedostot mukaan docker konttiin ajoa varten. Työskentelykansio ohjelman ajolle on kuitenkin user/cplusplus/Module1/exercise1.

16. files, moreFiles ja deleteFiles -attribuutit

deleteFiles on lista tiedostoista ja kansioista, jotka poistetaan muiden tiedostojen tallentamisen jälkeen, mutta ennen kuin ohjelma ajetaan. Esimerkiksi:

deleteFiles:
    - src
    - test

poistaa src ja test -nimiset tiedostot tai kansiot.

files -attribuutilla annetaan lista tiedostoista, jotka sisällytetään vastaukseen ja tallennetaan tietokantaan. moreFiles toimii muuten samalla tavalla, mutta siinä listattuja tiedostoja ei sisällytetä tallenneta tietokantaan.

Jokaiselle tiedostolle määritellään source, joka on oletuksena "editor" ja kertoo, mistä tiedosto saadaan. source voi saada seuraavat arvot ([]-merkit tarkoittavat valinnaista osaa):

  • "editor"
    • Tiedoston sisältö kysytään editorin avulla käyttäjältä
    • Ei voi käyttää moreFilesissä
  • "upload"
    • Tiedoston sisältö ladataan käyttäjän koneelta ja tallennetaan serverille.
  • "uploadByCode"
    • Tiedoston sisältö luetaan käyttäjän koneelta ja lähetetään serverille samoin tavoin kuin editorin sisältö
    • Ei voi käyttää moreFilesissä
  • "master:[root tai task, oletuksena task]:[r (regex) tai g (glob), oletuksena g]:<subPath>"
    • Tiedosto kopioidaan masterPathin määräämästä kansiosta
    • Jos root: <masterPath>/<subPath>
    • Jos task: <masterPath>/<taskPath>/<subPath>
      • Kun rootPath on määritelty, taskPath on sama kuin path-attribuutti
      • Kun rootPath ei ole määritelty, taskPath on tyhjä
    • r tai g (regex tai glob) määrää, miten subPathiä käsitellään
  • "git:<url>[;<subPath>[;<glob>[;<branch>]]]"
    • url määrittelee repon
    • subPath kertoo mistä kansiosta alkaen tiedostoja haetaan globin avulla. Oletuksena "" eli juurikansio.
    • glob on glob tyylinen polku, joka kertoo mikä kansio tai mitkä tiedostot tallennetaan. Oletuksena "**/*" eli kaikki.
    • branch kertoo, mistä git haarasta tiedostot haetaan. Oletuksena "master"
    • tiedostot tallennetaan "path" (kts. alla) määreen mukaiseen paikkaan siten, että subPath vastaa pathia (kts. esimerkit alla)

Kaikille tiedostoille on myös määriteltävä "path", joka kertoo, mihin ja millä nimellä kyseinen tiedosto tallennetaan. "/"-merkkiin päättyvä path tarkoittaa, että path on kansio, jonka sisälle tiedosto tallennetaan. path on muotoa [root tai task, oletuksena task]:<polku>. Kun määritellään "root", polku otetaan tarkoittavan suhteellista polkua rootPathiin verrattuna. Kun määritellään "task", polku otetaan tarkoittavan suhteellista polkua path attribuuttiin verrattuna.

Jokaiselle tiedostolle voi myös määritellä "maxSize"n, joka kertoo kuinka suuri tiedosto saa maksimissaan olla. Tällä hetkellä tämä tarkastetaan palvelimen puolella vain "files"issä määriteltyjen git tiedostojen kanssa. Lisäksi upload ja uploadByCode tiedostojen koko tarkastetaan käyttäjän selaimessa (älä siis luota siihen, että tiedosto ei voi olla suurempi, koska käyttäjä voi periaatteessa kiertää sen).

Esimerkkejä:

rootPath: user/example
path: first
files:
    - source: editor        # kysytään editorilla käyttäjältä
      path: src/main.c      # tallennetaan tiedostoon user/example/first/src/main.c
    - source: git:git@gitlab.com:tim-jyu/tim.git;timApp/modules;cs/*
            # haetaan gitlab.com:tim-jyu/tim.git reposta, kansiosta timApp/modules
            # kaikki tiedostot, jotka sopivat "cs/*" -globiin
      path: root:gitfiles/   # tiedostot tallennetaan user/example/gitfiles-kansioon
            # esimerkiksi tiedosto cs.py git repon cs-kansiossa tallennettaisiin
            # polkuun user/example/gitfiles/cs/cs.py
            # (eli subPath "timApp/modules" jätettiin pois välistä)
      maxSize: 100           # yksittäisen tiedoston maksimi koko 100 kB
      maxTotalSize: 200      # tiedostojen koko yhteensä maksimissaan 200 kB
moreFiles:
    - source: master:cplusplus/first/test/*  # kaikki tiedostot polusta masters/cplusplus/first/test
      path: test/                            # tallennetaan user/example/first/test kansioon
    - source: master:cplusplus/first/scripts/*  # kaikki tiedostot polusta masters/cplusplus/first/scripts
      path: root:scripts/                       # tallennetaan user/example/scripts kansioon

Eri sourcejen kaikki attribuutit (oletusarvo pilkun jälkeen):

yhteiset:
    "source" # lähdetyyppi (ja tarvittaessa polku)
    "path" # tiedoston tallennuspolku
    "maxSize" # tiedoston maksimikoko
    "byCode" # tiedoston aloitus sisältö (ei taida toimia muiden kuin editorin kanssa)
editor:
    "mode", kielen antama # ACE-editorin moodi
    "placeholder" # editorin placeholder teksti
    "canClose", false # voiko tiedoston sulkea selaimessa (buginen, ei suositeltavaa)
    "canRename", false # voiko tiedoston nimetä uudelleen selaimessa (buginen, ei suositeltavaa)
upload:
    "paths" # lista eri tiedostonimistä, jotka ohjataan tähän tiedostolähteeseen (kts. alla)
    "extensions" # lista tiedostopäätteistä, jotka ohjataan tähän tiedostolähteeseen (kts. alla)
    maxSizen oletusarvo maxSize-attribuutista
uploadByCode:
    "show", false # näytetäänkö tiedosto editorissa. Voi olla true, false tai "loaded".
                  # Jos "loaded", näytetään lataamisen jälkeen
    ...samat kuin editorissa ja uploadissa
git:
    "maxTotalSize" # tiedostojen maksimiyhteiskoko

Kun käyttäjä yrittää ladata useamman tiedoston kerralla, path ja extensions -kenttiä käytetään näiden tiedostojen jakamiseen oikeisille upload ja uploadByCode -lähteille, kun lähteitä on useampia. Esimerkiksi voidaan laittaa yhteen lähteeseen ".h" extension ja toiseen ".c", jolloin *.h tiedostot laitetaan ensimmäiseen ja *.c tiedostot toiseen lähteeseen, kun ladataan useampi tiedosto kerralla.

17. Review

Review-toimintoa (velpit) varten on mahdollisuus antaa attribuutti

review: true

tai kutsua parametrilla ?review=true. Tällöin palautetaan pelkkä käyttäjän vastauksesta riippuva HTML-muoto, jossa on mahdollisesti myös input ja args-osiin annetut vastaukset. Vastaukset on ympäröity divillä, jossa on review-tyyli, jolla voi määrittää vastausalueen ulkoasun:

<div class="review">
<pre>System.Console.WriteLine(&quot;Moi&quot;);</pre>
<p>Input:</p>
<pre>Mun syöte</pre>
<p>Args:</p>
<pre>Mun argsit</pre>
</div>

18. ExtCheck

Tarkoituksena on pystyä ajamaan ulkoinen arvosteluohjelma, joka antaa pisteet ja mukautetun tulosteen. Arvosteluohjelma voi olla esimerkiksi python skripti, joka kopioidaan master-lähteestä. Jos arvosteluohjelma ei ole itse kehitetty, on todennäköisesti tarpeellista tehdä välikäsiskripti, joka ajaa arvosteluohjelman, lukee sen tuloksen ja tulostaa sen oikeassa formaatissa TIMille.

Attribuutit:

command: yksittäinen ajettava komento. Tästä lisää alempana
commands: sanakirja eri komennoista. commandKey:llä valitaan tästä komento.
commandKey: komennon avain commands-sanakirjaan jos commandiä ei ole määritely, oletuksena 'run'
jsFiles: lista polkuja javascript tiedostoihin, jotka ladataan käyttäjälle.
        Polut voi olla absoluuttisia (esim. /cs/masters/... tai https://...)
        tai suhteellisia (ei ala '/' tai 'http'), jolloin se on suhteellinen masterPath-attribuuttiin.
cssFiles: sama kuin jsFiles, mutta css tiedostoille

Komennot voivat olla joko lista argumentteja tai yksittäinen merkkijono, kuten pythonin subprocess.run funktiolle annetaan. Kaikki 'points_rule'-merkkijonot komennoissa korvataan points_rule jsonilla.

Komennon tulee tulostaa standardi outputtiin json objekti seuraavilla kentillä:

    points: annetut pisteet
    max_points: maksimi pisteet. Annetut pisteet skaalataan tämän avulla
            timissä määriteltyihin maksimipisteisiin.
    penalties: sanakirja, jonka avaimet ovat points_rulessa määriteltyjä
            penaltyjä ja jonka arvot on true, false tai merkkijono, joka kertoo mikä on väärin.
            Merkkijono tarkoittaa true. Arvot on oletuksena false eli ei rankaisua.
    output_boxes: lista objekteja {
        hide: <boolean: piilotetaanko elementti oletuksena>,
        title: {
            classes: otsikko elementin css luokat,
            content: otsikon sisältö,
            isHTML: onko otsikko html,
            isAngular: onko otsikko angularia
        },
        content: {
            classes: sisältö elementin css luokat,
            content: sisältö,
            isHTML: onko sisältö html,
            isAngular: onko sisältö angularia
        }
    }

Tästä olisi kiva nähdä kokonainen toimiva esimerkki.

02 Feb 22

19. Git

19.1 git ja gitDefaults -attribuutit

git:
    "onError"   # mitä tehdään, kun sattuu virhe gitcheckissä
    "library"   # käytettävä git kirjasto
    "fields"    # kirjastolle annettavat tunnistetiedot (kts. GitReg)
    "askFields" # käyttäjältä kysytyt kirjastolle annettavat tunnistetiedot (kts. GitReg)
    "repo"      # repon tiedot (kts. GitReg)

19.2 Kirjastot

Kirjastoja on tällä hetkellä kaksi: gitea ja gitea_aalto. Oletuksena courses-git.comnet.aalto.fi -osoitteelle käytetään gitea_aalto-kirjastoa.

TODO: lisää kirjastokohtaisia asioita

19.3 GitReg-kieli

GitReg luo käyttäjän ja/tai repon halutulle git palvelulle, kunhan sille löytyy sopiva kirjasto. Käyttäjän tiedot annetaan git-attribuutin fields ja askFields -kentillä. askFields on lista tiedoista, jotka kysytään käyttäjältä ja lähetetään eteenpäin kirjastolle. fields on sanakirja (dictionary), jonka avaimet ovat tietojen nimiä ja arvot muotoa

value: <arvo>
onError: raise, ask tai none. Oletuksena raise

valueen laitetaan arvo, joka annetaan kirjastolle, ja onError kertoo, mitä tehdään, jos kirjasto ei hyväksy kyseistä arvoa. onError vaihtoehdot:

raise: rekisteröinti keskeytetään ja käyttäjälle näytetään virheviesti
ask: annettu arvo heitätään pois ja kysytään uusi arvo käyttäjältä
none: arvoksi asetetaan None

Esimerkki:

git:
    fields:
        name: 
            value: %%realname%%   # otetaan käyttäjän nimi TIMistä
        email: 
            value: %%useremail%%  # otetaan käyttäjän sähköposti TIMistä
            onError: ask          # jos sähköpostiosoite ei kelpaa, kysytään se käyttäjältä
    askFields:
        - username                # kysytään käyttäjältä, minkä käyttäjänimen hän haluaa

repo-kenttä on muotoa

name: <repon nimi>
owner: <repon omistaja>                 # oletuksena kirjaston TIM itse
fork: <bool; onko repo fork>            # oletuksena false
oldName: <kopioitavan repon nimi>       # oletuksena sama kuin name
oldOwner: <kopioitavan repon omistaja>  # oletuksena sama kuin owner
librarySpecific: <kirjasto kohtaiset tiedot>

19.4 GitCheck-modifier

GitCheck tarkastaa onko git-attribuutissa määritelty käyttäjä olemassa (fields-kentän arvojen perusteella, gitcheck ei kysy mitään käyttäjältä) ja toimii git-attribuutin onError-kentän mukaisesti.

onError vaihtoehdot:

raise: käyttäjälle näytetään virheviesti ja tehtävän ajaminen estetään
create: repo yritetään luoda, jos sitä ei ole olemassa. Jos ongelma on, että
        käyttäjää ei ole olemassa, tehdään raise.
remove: fields-kentästä poistetaan lähteet, jotka ovat samasta reposta kuin
        gitDefaultsissa mainittu repo.
removeAll: ields-kentästä poistetaan kaikki git-lähteet

20. Ehdotuksia

20.1 Vanhoja ehdotuksia TIMin käsiteltäviksi attribuuteiksi:

Näistä osa on jo toteutettukin. Jos on

-pointsRule:
    divideBy: 6

niin tuo tarkoittaisi että se lukema, minkä plugin antaa, talletetaan 6:lla jaettuna.

Sitten ehkä jatkossa vielä jotakin, millä saa tuon RegExp-kokeen säännöt:

https://tim.jyu.fi/view/ohj2/materiaali/RegExp

Ehkä tyyliin (oletus että plugin palauttaa maksimisuorituksesta 10 p):

-pointsRule:
    max: 10
    min: 5
    dec: 0.5

21. Moniriviset YAML-attribuutit

Seuraava yhteenveto on otettu:

                      >     |            "     '     >-     >+     |-     |+
-------------------------|------|-----|-----|-----|------|------|------|------  
Trailing spaces   | Kept | Kept |     |     |     | Kept | Kept | Kept | Kept
Single newline => | _    | \n   | _   | _   | _   | _    |  _   | \n   | \n
Double newline => | \n   | \n\n | \n  | \n  | \n  | \n   |  \n  | \n\n | \n\n
Final newline  => | \n   | \n   |     |     |     |      |  \n  |      | \n
Final dbl nl's => |      |      |     |     |     |      | Kept |      | Kept  
In-line newlines  | No   | No   | No  | \n  | No  | No   | No   | No   | No
Spaceless newlines| No   | No   | No  | \   | No  | No   | No   | No   | No 
Single quote      | '    | '    | '   | '   | ''  | '    | '    | '    | '
Double quote      | "    | "    | "   | \"  | "   | "    | "    | "    | "
Backslash         | \    | \    | \   | \\  | \   | \    | \    | \    | \
" #", ": "        | Ok   | Ok   | No  | Ok  | Ok  | Ok   | Ok   | Ok   | Ok
Can start on same | No   | No   | Yes | Yes | Yes | No   | No   | No   | No
line as key       |

Examples
Note the trailing spaces on the line before "spaces."

- >
  very "long"
  'string' with

  paragraph gap, \n and        
  spaces.
- | 
  very "long"
  'string' with

  paragraph gap, \n and        
  spaces.
- very "long"
  'string' with

  paragraph gap, \n and        
  spaces.
- "very \"long\"
  'string' with

  paragraph gap, \n and        
  s\
  p\
  a\
  c\
  e\
  s."
- 'very "long"
  ''string'' with

  paragraph gap, \n and        
  spaces.'
- >- 
  very "long"
  'string' with

  paragraph gap, \n and        
  spaces.
[
  "very \"long\" 'string' with\nparagraph gap, \\n and         spaces.\n", 
  "very \"long\"\n'string' with\n\nparagraph gap, \\n and        \nspaces.\n", 
  "very \"long\" 'string' with\nparagraph gap, \\n and spaces.", 
  "very \"long\" 'string' with\nparagraph gap, \n and spaces.", 
  "very \"long\" 'string' with\nparagraph gap, \\n and spaces.", 
  "very \"long\" 'string' with\nparagraph gap, \\n and         spaces."
]

These are the current permissions for this document; please modify if needed. You can always modify these permissions from the manage page.