Mit den bisher vorgestellten Mitteln fällt es schwer, Berechnungen durchzuführen, die sich z.B. zur Steuerung des Interview-Ablaufs nutzen lassen. Wenn beispielsweise per compute eine Variable definiert wird, die eine Berechnung darstellt:
compute a = b+c;
dann wird bei jedem Zugriff auf die Variable
a
(z.B. innerhalb von Berechnungen
oder Bedingungen) der aktuelle Wert der Addition
b+c
ermittelt und als Wert von
a
benutzt. Damit können aber z.B.
keine Zähler realisiert werden. Eine Definition wie
compute count = count+1;
würde zu einer endlosen Rekursion führen, da bei dem
Zugriff auf den Wert von count
die
Berechnung count+1
durchgeführt wird,
die aber wiederum einen Zugriff auf
count
darstellt, und so weiter.
Action-Blöcke schaffen hier Abhilfe und erlauben es, bei der Steuerung Quotenstände oder Zufallsauswahlen zu berücksichtigen oder Berechnungen bei Start oder Wiederaufnahme eines Interviews durchzuführen. Dabei stellen Action-Blöcke Kontrollstrukturen zur Verfügung, wie sie aus herkömmlichen Programmiersprachen bekannt sind: wenn-dann, von-bis usw.
Ein ActionBlock enthält eine beliebige Anzahl von Befehlen und wird an ein bestimmtes Ereignis gekoppelt, bei dessen Eintreten er ausgeführt wird. Hierbei gibt es sowohl aufs Interview bezogene Ereignisse (Start, Abbruch, beendet) als auch fragespezifische (vor dem Anzeigen, nach Klick auf »weiter« etc.). Konkret stehen zur Verfügung:
Interview-spezifisch:
startActionBlock (wird beim Start ausgeführt, auch bei Wiederaufnahme. Das gilt auch bei abgeschlossenen Fällen oder einer deaktivierten Studie, damit Q. eventuelle Meldungen in der richtigen Sprache ausgeben kann.)
resumeActionBlock (wird beim Wiederaufnahme nach einem Abbruch ausgeführt)
timeoutActionBlock (wird ausgeführt, bevor ein Interview wg. zu langer Inaktivität abgebrochen wird)
cmplActionBlock (wird ausgeführt wenn ein Interview beendet wird, egal mit welchem Status)
Frage- bzw. Block-spezifisch:
initActionBlock (wird beim Initialisieren vor dem Anzeigen einer Frage oder eines Blocks ausgeführt)
preAssertionActionBlock (wird nach Klick auf den submit-Button, aber vor dem Validieren der asserts ausgeführt)
continueActionblock (wird nach erfolgreicher Validierung einer Frage oder eines Blocks vor dem Weiterleiten zum nächsten Screen ausgeführt)
Innerhalb dieser Befehlsblöcke können keine Variablen definiert werden; dies muss vorher mit compute geschehen. Eine Ausnahme hiervon bilden Zählvariablen von Schleifen.
compute count=0; SingleQ q1; ... flt=count lt 3; continueActionBlock={ inc(count); }; SingleQ q2; ... flt=count lt 3; continueActionBlock={ inc(count); }; SingleQ q3; ... flt=count lt 3; continueActionBlock={ inc(count); }; SingleQ q4; ... flt=count lt 3; continueActionBlock={ inc(count); }; SingleQ q5; ... flt=count lt 3; continueActionBlock={ inc(count); }; block zufall=(q1 q2 q3 q4 q5); random;
Beim Befehl inc
(für
»increment«) handelt es sich um einen Action-Befehl. Er
erhöht den Zähler count
nach jeder
Frage um eins. Außerdem wird jede Frage danach
gefiltert, ob der Zählerstand 3 erreicht ist.
Die Befehle eines ActionBlocks müssen innerhalb geschweifter Klammern stehen. Sie dienen auch zum Definieren weiterer Blockstrukturen innerhalb eines ActionBlocks etwa bei Bedingungen und Schleifen.
Q. erlaubt es zwar ActionBlöcke auch an Screens zu schreiben, eine Funktion/Bedeutung haben ActionBlöcke allerdings nur an Fragen oder teilweise an Blöcken ([Init|Continue]ActionBlock).
Man kann die Action-Befehle in zwei Kategorien aufteilen: »normale Befehle« und Befehle zur Ablaufsteuerung (Bedingungen, Schleifen).
Mit Action-Befehlen lassen sich Dinge wie Zähler, Bedingungen und Schleifen realisieren. Die folgenden alphabetisch sortierten Action-Befehle können nur innerhalb von Action-Blöcken verwendet werden.
add
ersetzt die erste
Missing
-Stelle eines ArraysARRAY
durchVALUE
. Wenn der Array vollständig belegt ist, macht die Anweisung nichts.add(ARRAY, VALUE);
Array container[3]; actionblock set_container = { set( container[1] = 1 ); set( container[3] = 3 ); }; actionblock add_container = { add(container, 7); }; continueActionblock = { print(container); }; // ergibt in actionlog.txt: // PrintAction: variable: container; value: 1 7 3
appendText
hängt
TEXT
anTextElement TEXTVAR
an.appendText(TEXTVAR, "TEXT");
appendText(textelem, "... wird angehängt");
changePBar
setzt den Fortschrittsbalken um
#QUESTIONS
Stellen weiter, sofernBEDINGUNG
zutrifft. Alternativ (und gebräuchlicher) kann der Fortschrittsbalken auch innerhalb von Filtern für Fragen, Screens und Blöcken definiert werden, siehe „Filter an Fragen, Blöcken, Screens“.changePBar((BEDINGUNG) #QUESTIONS);
changePBar((var eq 1) 2);
clear
abhängig vom
arrayInitMode
wird jedem Element vonARRAY
der Missing-Wert_missing
oder der jeweilige Stellenindex zugewiesen.clear(ARRAY);
[bei arrayInitMode=missing] ARRAY-Inhalt vor clear(): { 1, 2, 3, 4, 5 } ARRAY-Inhalt nach clear(): { , , , , }
dec
steht für decrement und verringert den Wert von
VAR
um 1.dec(VAR);
exit
Mit exit wird das Interview am Ende des aktuellen Actionblocks mit
FINISHCODE
beendet und die Endseite angezeigt.exit(FINISHCODE);
findMinValIndex
,findMaxValIndex
Die Funktionen suchen in einem Array (ARR) den niedrigsten bzw. den höchsten Wert und speichern den Index der Fundstelle in einer Variablen (VAR).
findMinValIndex(ARR, VAR)
compute min = 0; compute max = 0; set( arr = [1 3 5 -1 6:9 0 0] ); // {1, 3, 5, -1, 6, 7, 8, 9, 0, 0} findMinValIndex(arr, min); // min erhält Wert 4 findMaxValIndex(arr, max); // max erhält Wert 8
getCountValue
getCountValue(VAR, "KEY")
ruft Zählerstände ab, die in der Datei
countvars.lst
im Studienverzeichnis gepflegt werden. Dabei wird zuerst der ZählerKEY
inkrementiert und anschließend an die VariableVAR
übergeben.compute z1 = 0; textq welcome; text=" Herzlich Willkommen!<br /> Sie sind der @insert(z1). Teilnehmer an dieser Befragung! "; initactionblock={ getCountValue(z1, "zaehler1"); };
Mit dem Anzeigen der Frage wird der Zählerstand erhöht. Die Nachrichten werden der Reihe nach wie folgt angezeigt:
Sie sind der 1. Teilnehmer an dieser Befragung! Sie sind der 2. Teilnehmer an dieser Befragung! Sie sind der 3. Teilnehmer an dieser Befragung!
Die Datei
countvars.lst
besteht aus einfachen Key-Value Paaren:zaehler1 0 zaehler2 0 zaehler3 0
inc
steht für increment und erhöht den Wert von
VAR
um 1.inc(VAR);
incQuota
erhöht den Wert einer
QUOTAVAR
explizit um 1.incQuota(QUOTAVAR);
insertLabels
hängt in der Labelliste von Frage
FRAGENAME
nach dem Label mit CodeCODE
ein neues LabelLABEL
ein. Je nach Fragetyp muss hierbei darauf geachtet werden, dass die ursprünglich angelegte Frage auch genug Platz hat, alle möglichen Antworten zu speichern. Wäre die Frage im gegebenen Beispiel eine MultiQ, müsste mitoversizeArray(1);
dafür gesorgt werden, dass die ursprünglich mit 2 Labels angelegte Frage ausreichend Platz dafür hat auch alle 3 Antwortcodes zu speichern.insertLabels(FRAGENAME, CODE, [ LABEL ]);
multiq bekannt; labels= 1 "Lafite Rothschild" 3 "Haut-Brion" ; initActionBlock={ insertLabels(bekannt, 1, [ 2 "Mouton Rothschild" ]); }; oversizeArray(1);
lalign
verschiebt jedes Element von
ARRAY
, das ungleich dem Missing-Wert_missing
ist, nach links (zum kleinsten Index), indem es so viele Plätze wie möglich mit Missing-Werten tauscht, siehe auchralign
.lalign(ARRAY);
ARRAY-Inhalt vor lalign(): { , 2, 3, , 5, 6, , , } ARRAY-Inhalt nach lalign(): { 2, 3, 5, 6, , , , , }
load
kopiert den Inhalt von Variable
VAR2
inVAR1
. Der Unterschied zuset
ist, dass nicht nur ein Wert/Zahl/Ergebnis zugewiesen wird, sondern der gesamte Inhalt einer Variable in eine andere. So lassen sich z.B. auch ganze Array-Inhalte kopieren.load(VAR1 = VAR2);
performHttpRequest
dient zur Belegung von Variablen und Fragen eines Interviews. Der Befehl stellt eine HTTP Anfrage an die Webadresse
URL
und erwartet eine Antwort mit Daten im JSON Format. Der zweite Parameterdata="INFO"
ist optional und kann zusätzliche Informationen bei der Anfrage übermitteln. Die angegebenen Werte werden in die benannten Variablen übernommen, sofern sie existieren. Der Befehl funktioniert analog zureadJsonDataFile
.performHttpRequest( url="URL"; data="INFO"; );
performHttpRequest( url="http://www.gessgroup.de/AntwortServlet"; data="optionale Zusatzinformation"; ); /////////////////////////////// // Server-Antwort: /////////////////////////////// // { // "variables": { // "singleq": 4, // "multiq": [2, 3, 5, 7, 9], // "openq": "dies ist ein text", // "numq": [14, 19, 5, 27], // "sgq.1": [1,2,3,2], // "mgq.1.1": [1,2,3], // "mgq.1.2": [2,1] // } // }
pow
aus den angegebenen Zahlen wird mit
pow(BASE, EXPONENT)
eine Potenz gebildet und schreibt sie in die VariableVAR
.set(var = pow(10, 3)); // 10^3
print
schreibt den Inhalt von Variable
VAR
in eine Zeile der Dateiactionlog.lst
in dastext
-Verzeichnis der Studie. Ist die Datei nicht vorhanden, wird sie neu angelegt.print(VAR);
printText
schreibt den angegebenen TEXT in eine Zeile der Datei
actionlog.lst
in dastext
-Verzeichnis der Studie. Ist die Datei nicht vorhanden, wird sie neu angelegt.printText("TEXT");
ralign
verschiebt jedes Element von
ARRAY
, das ungleich dem Missing-Wert_missing
ist, nach rechts (zum größten Index), indem es so viele Plätze wie möglich mit Missing-Werten tauscht, siehe auchlalign
.ralign(ARRAY);
ARRAY-Inhalt vor ralign(): { , 2, 3, , 5, 6, , , } ARRAY-Inhalt nach ralign(): { , , , , , 2, 3, 5, 6 }
readDataFile
dient zur Belegung von Variablen und Fragen eines Interviews. Der Befehl sucht in einer strukturierten Datendatei
FILE
nach einer Zeile mit SchlüsselwertVAR
, und übernimmt die darin angegebenen Werte in die entsprechenden Variablen, sofern sie existieren.FILE
muss sich imtext
-Ordner der jeweiligen Studie befinden.readDataFile(FILE, VAR);
// z.B. für ID 67 readDataFile("data1.txt", _respid); /////////////////////////////// // data1.txt /////////////////////////////// // ... // 67 ( (p_intnr "einunddreißig") (s1 "6") ) // ...
Bitte beachten: Wenn VAR eine numerische Information ist (angelegt z.B. Compute oder NumQ), sollte DecimalFormat (s. „ Textersatz mit @insert “ verwendet werden. Sonst kann es passieren, dass große Zahlen in Exponentialschreibweise gewandelt oder eine Nachkommastelle zugefügt werden und dadurch die Suche keine Treffer liefert.
readJsonDataFile
,JSON
dient zur Belegung von Variablen und Fragen eines Interviews. Der Befehl liest einen JSON formatierten Datensatz
FILE
ein und übernimmt angegebene Werte in die benannten Variablen, sofern sie existieren.FILE
muss sich imPreset
-Ordner der Q. Installation befinden (siehe auch „Konfigurationsdatei qonline.cfg“).readJsonDataFile(FILE);
readJsonDataFile("data1.json"); /////////////////////////////// // data1.json /////////////////////////////// // { // "variables": { // "singleq": 4, // "multiq": [2, 3, 5, 7, 9], // "openq": "dies ist ein text", // "numq": [14, 19, 5, 27], // "sgq.1": [1,2,3,2], // "mgq.1.1": [1,2,3], // "mgq.1.2": [2,1] // } // }
readTextRessource
liest eine Sprach-Ressourcen-Datei
FILE
aus dem text-Verzeichnis der Studie ein und ändert zu diesem Zeitpunkt alle in der Quelle enthaltenen Textinformationen.readTextRessource(FILE);
if(lang eq 1) { readTextRessource("englisch.lrs"); };
remove
ersetzt einen oder mehrere Werte
VAL
inARRAY
durch_missing
.remove(ARRAY, VAL, VAL, ...);
ARRAY-Inhalt vor remove(): { 3, 2, 3, 5, 5, 6, 1, 6, 6 } ARRAY-Inhalt nach remove(ARRAY, 2, 6): { 3, , 3, 5, 5, , 1, , }
removeDuplicates
entfernt Werte, die mehrfach in
ARRAY
enthalten sind. Nur ihr jeweils erstes Vorkommnis bleibt erhalten. Alle weiteren werden durch den Missingwert_missing
ersetzt.removeDuplicates(ARRAY);
ARRAY-Inhalt vor removeDuplicates(): { 3, 2, 3, 5, 5, 6, 1, 6, 6 } ARRAY-Inhalt nach removeDuplicates(): { 3, 2, , 5, , 6, 1, , }
rotateLeft
versetzt die Werte eines
ARRAY
um die angegebene Stellenanzahl nach links.rotateLeft(ARRAY, ANZ-STELLEN);
ARRAY-Inhalt vor rotateLeft(): {1 2 3 4 5} ARRAY-Inhalt nach rotateLeft(ARR, 2) : {3 4 5 1 2}
round
rundet den Wert der angegebenen Variable
VAR
auf eine ganze Zahl.set(var = round(var));
script
bettet Javascript Syntax in Actionblöcke ein. Mit
@insert()
können Werte von Q. Variablen ins Javascript eingebunden werden.@save(VAR = VAL);
definiert welche Werte am Ende der Ausführungslogik in welche Q. Variablen zurückgespielt werden. Dieser Befehl benötigt Java 8 und ist unter Android noch nicht verfügbar.script("JAVASCRIPT");
compute nhTestResult = 5; compute x = 0; compute y = 0; compute z = 0; Array nhTestArray[3]; OpenQ te; vararray xyz = (x y z te nhTestArray); // ... Actionblock ab = { script(" var x = 1; var y = 2; var z = @insert(nhTestResult); var arr = new Array(); arr.push(x); arr.push(y); arr.push(z); @save(xyz[1]) = x; @save(xyz[2]) = y; @save(xyz[3]) = z; @save(nhTestResult) = x+y+z; @save(xyz[5]) = arr; @save(xyz[4]) = 'nachher'; "); };
set
weist Variable
VAR
den WertVALUE
zu.VALUE
kann auch das Ergebnis einer Berechnung sein.set(VAR = VALUE);
set(var = 0); set(var = a * b + 1);
Im Gebrauch mit Arrays kann
set
einen Array in einem Schritt belegen. Ab Arrayindex 1 werden dabei fortlaufend alle Stellen belegt, für die Werte angegeben wurden. Überschüssige Werte werden nicht berücksichtigt.array arr[10]; set( arr = [1 3 5 -1 6:9 0 0] ); // {1, 3, 5, -1, 6, 7, 8, 9, 0, 0}
(Die Schreibweise
6:9
kürzt die Angabe 6 7 8 9 ab.)setCurrentScreen
Setzt den aktuellen Screen neu und greift sofort bei nächster Teilnehmerinteraktion. Verwirft Eingaben in Screens, die ein Timeout überdauern oder wenn
setCurrentScreen
in einem InitActionBlock verwendet wird.setCurrentScreen(SCREEN);
// Kann syntaktisch mit/ohne Anführungszeichen geschrieben werden. // Ohne Anführungszeichen wird die Screenexistenz geprüft TimeoutActionBlock = { setCurrentScreen(m4); }; // Mit Anführungszeichen kann z.B. @insert() verwendet werden // setCurrentScreen("@insert(te_m4)"); // setCurrentScreen("m4");
setDecimalSeparator
legt das Dezimaltrennzeichen fest (Voreinstellung Komma: ",")
setDecimalSeparator(".");
setGroupingSeparator
legt das Tausendertrennzeichen fest (Voreinstellung Punkt: ".")
setGroupingSeparator(",");
setNextScreen
Mit dem Befehl
setNextScreen
wird in der Umfrage ein einmalig definierter Sprung initiiert, ohne dass der vorher definierte Pfad dabei verändert wird.setNextScreen
kann an beliebiger Stelle für einen beliebigen Screen verwendet werden. Bei der Benutzung ist zu beachten, dass der Fortschrittsbalken nicht kompatibel mit dem Überspringen der Fragen oder Screens ist und daher weder parallel zum Interviewverlauf fortschreitet, noch parallel abschließt.setNextScreen(SOURCE,TARGETSCREEN);
setNextScreen(a,c);
shuffle
ordnet die Inhalte von
ARRAY
zufällig neu an.shuffle(ARRAY);
ARRAY-Inhalt vor shuffle(): { 1, 2, 3, 4, 5 } ARRAY-Inhalt nach shuffle(): { 4, 3, 5, 1, 2 }
strCutLeft
schneidet die ersten ANZAHL Zeichen des Inhalts von VAR ab und speichert das Resultat in TEXTELEMENT.
strCutLeft(TEXTELEMENT, VAR, ANZAHL);
TextElement cutlery; OpenQ q1; text="Bitte etwas eingeben"; continueActionBlock={ strCutLeft(cutlery, q1, 2); };
strCutRight
schneidet die letzten ANZAHL Zeichen des Inhalts von VAR ab und speichert das Resultat in TEXTELEMENT.
strCutRight(TEXTELEMENT, VAR, ANZAHL);
syncsort
sortiert die Inhalte von
ARR1
aufsteigend (ascending
) oder absteigend (descending
) an. Synchron zu dem Schema, wie die Elemente inARR1
ihre Positionen verändern, wird auchARR2
umsortiert.syncsort(ARR1, ARR2, ascending|descending);
syncsort(array1, array2, ascending); // ARRAY-Inhalte vor syncsort(): array1{ 3, 7, 1, 9, 4 } array2{ 1, 2, 3, 4, 5 } // ARRAY-Inhalte nach syncsort(): array1{ 1, 3, 4, 7, 9 } array2{ 3, 1, 5, 2, 4 }
writeJsonToSurvey
schreibt einen Rohdatensatz FILE in den Studienordner der Studie SURVEYNAME, der die angegebenen Variablen
VAR
im JSON Format enthält.writeJsonToSurvey(SURVEYNAME, FILE, (VAR, VAR, ...));
Das Beispiel:
writeJsonToSurvey(beispielstudie, "case123.json", (sq1 sgq.1));
ergibt
/////////////////////////////// // case123.json /////////////////////////////// { "rvars": [ { "name": "sq1", "value": "3.0" }, { "name": "sgq.1", "value": "4.0 4.0 5.0 5.0" } ] }
Für die Ablaufsteuerung bietet Q. gängige Kontrollstrukturen an.
if-else
: Wenn-Dannfor
: Schleife (von-bis)forEach
: Schleife (für-jeden)while
: Schleife (solange)
if(<Bedingung>) { ... };
Das if kann auch mit einem else kombiniert werden:
if(<Bedingung>) { ... } else { ... };
Es stehen zwei Typen von Schleifen zur Verfügung: Die for-Schleife und die while-Schleife. Bei der for-Schleife wird eine Zählvariable angelegt, die in Einer-Schritten vom Start- bis zum Zielwert bei jedem Schleifendurchlauf inkrementiert wird. Als Start- und Zielwert können sowohl Zahlenwerte als auch Variablen angegeben werden.
for(x=1 to 10) { ... };
Die Schleife wird 10 Mal ausgeführt, und x durchläuft dabei die Werte von 1 bis 10.
for(y=var1 to var2) { ... };
Hier sind als Start- und Zielwerte Variablen angegeben. Dabei ist natürlich zu beachten, dass man hiermit Endlosschleifen bauen kann, die nie abbrechen, und vor allem wenn die Start- und Zielvariablen innerhalb der Schleife manipuliert werden, sind sehr seltsame Verhaltensweisen möglich.
Außerdem ist zu beachten, dass die Zählvariable neu angelegt wird, und daher vorher keine Variable mit dem gleichen Namen vorhanden gewesen sein darf. Das führt auch dazu, dass die Zählvariablen verschiedener for-Schleifen auch verschiedene Namen haben müssen.
Eine andere Art der for-Schleife, ist die forEach
. Mit ihr kann eine
Serie bestimmter Werte abgearbeitet werden.
forEach(y=1 3 5 7 9 50:55) { ... durchläuft y mit 1, 3, 5, 7, 9, 50, 51, 52, 53, 54, 55 };
Bei der while-Schleife gibt es keine Zählvariable, sondern die Schleife wird solange wiederholt, wie die angegebene Bedingung erfüllt ist:
while(<Bedingung>) { ... };