ExpressionScript - プレゼンテーション
From LimeSurvey Manual
はじめに
LimeSurveyでは、新しいExpression Scriptモジュールを使用して、より複雑な分岐、評価、検証、および文言調整がサポートされています。LimeSurveyがサーバー内で行う置換、条件、評価の管理方法を置き換えることができます。また、ランタイムデータベースの読み込みのほとんどが不要になるため、処理速度が大幅に向上します。Expression ScriptはDr. Thomas White(TMSWhite)が開発しました。
主な定義
- 式: 中括弧で囲まれたもの:
- 中括弧の内側の先頭または末尾に空白があってはならない
- 式の内容はEMによって評価されるため、数式、関数、複雑な文字列および日付処理を含めることが可能
- 文言調整: "パイピング"と呼ぶこともある。条件によってテキストを変更するプロセス:
- すべての'置換フィールド'、参加者データ、回答データにアクセス可能
- 質問、回答、およびそれらのプロパティに簡単にもアクセス可能
- 出現条件 式: 質問の表示可否を制御する新たな質問属性:
- 出現条件式がある場合、trueと判断された場合にのみ質問が表示される
- 内部的には、すべてのarray_filterおよびarray_filter_excludeコマンドがサブ質問レベルの出現条件となる
- SGQA (古い変数の命名方法):
- Survey-Group-Question-Answer(アンケート、グループ、質問、回答)の頭文字のこと
- SGQA変数名は、123X5X382X971のような形式で、サブ質問の接尾辞がついている場合もある
- これらの変数名は、その元になっているS/Q/G/Aデータベースコードに紐づいており、多くの場合変更する必要がある
- 式質問タイプ: 計算結果を保存したり、データベースに格納したりするための新しい質問タイプ:
- 固定的なテキストを表示する質問のようなもので、"この質問をいつも隠す"と設定してもデータベースに格納される
- 質問コード: EMでの使用が推奨される変数名:
- 質問の目的を示すわかりやすい名前にし、複雑なロジックの可読性を高めることが可能
- 質問コードは数字で始めることはできないので、質問番号を質問コードにするときは、"q1"、"q1a"、"g1q2"のようにする
- データをSPSSまたはRにエクスポートする際、これが変数名になるため、統計分析を行う場合は、ユニークにする必要がある
Expression Scriptを使用する必要性
必ず使わなければならないわけではありません。作成するアンケートがどれだけ複雑かによって変わります。
例えば、条件エディターでは、アンケートの質問に適用できるいくつかの基本的な式を扱います。ただし、条件エディタには制限があります。EMが使用されているのはこのためです。Expression Scriptがカスタマイズの可能性を広げています。
条件と出現条件式の両方を使えるか?
はい。質問によって条件エディターを使用することも、出現条件式を使用することもできます。
条件と式の両方を同じ質問で設定することはできません。 条件が設定されると、出現条件のフィールドに書き込まれた式は使われません。さらに、出現条件式フィールドは手動では編集できません。
しかし、質問内で式と条件の両方を使用する方法があります。上で述べたように、条件式は出現条件式のフィールドを置き換えます。完了したら、新しく作成された式を確認し、テキストエディターにコピーします。条件エディターで作成した条件を削除し、残りの式と一緒にテキストエディターにコピーした条件ベースの式を追加して質問を編集します。
条件と出現条件のどちらを選ぶべきか?
各々の長所と短所のリストを以下に示します。
スタイル | 長所 | 短所 |
---|---|---|
条件 | 1. シンプルな条件を作成するための優れたGUI 2. 文書化が容易でサポートチームが理解しやすいGUI |
1. 簡単な比較のみをサポートし、"AND"/"OR"条件はうまく機能しない 2. カスケード条件が不安定 3. データベースに負荷がかかり、長いアンケートが重くなる 4. 条件のリロードにかかる不具合が報告されている 5. GUIに拡張性がなく、何十、何百、または何千もの質問があるときにうまくいかない 6. SGQA名を使用しなければならないので、紙ベースのアンケートを変換するのが遅くなることがある 7. 複雑な分岐が必要な場合、カスタムコードロジックを書くプログラマーが必要なことがある |
出現条件 | 1. 80以上の関数と数学/文字列演算子を含む非常に複雑なロジックをサポート 2. カスケードロジックの完全サポート 3. 高速 - 余計なデータベースアクセスがなく、多数の質問があるアンケートをサポート 4. SGQAコードを必要としないため、リロードロジックで問題が生じない 5. 構文ハイライト機能は多数の質問があるアンケートにも対応 6. 既存の紙ベースのアンケートの電子化に簡単かつ迅速に対応可能 7. 半構造化インタビューや疫学調査もプログラマーを必要とせず容易に作成可能 |
1. 単純な条件に適したGUIがない(代わりに構文ハイライト機能を使用) |
- ニーズに合ったものを使用することをお勧めします。
- EM機能の詳細については、こちらをクリックしてください。
はじめに
式マネージャーを始める最善の方法は以下のとおりです。
- https://www.limesurvey.org/en/download から最新の安定版をインストールする
- Import and explore some サンプルアンケートをインポートして確認する
- 使用例やHow-to、ステップバイステップの例を参照する
- EMに関するドキュメント(このページ)を読む
- 分離された式の単体テスト(高度)
- すべてのEM関数と演算子、PHPとJavaScriptの結果の使用例を表示
- PHPとJavaScriptのバージョンで異なる結果を生成する関数はほとんどないので、このページの内容を踏まえ、EMロジックを適切に計画することが可能
- 分離された式の単体テスト(高度)
用語
EMの機能を説明するため、以下の単語を使用します。
- 出現条件を基にした分岐 - 質問は出現条件を満たす場合のみ表示されます(出現条件を満たさない場合、表示されず、データベースにはNULLとして記録されます)。質問エディターパネルと質問グループエディターパネルに出現条件フィールドがあります。後者は、各質問に同じ条件をコピーしたり、グループと質問レベルの条件ロジックを組み合わせたりすることなく、一連の条件をグループ全体に適用するために使用します。
- 文言調整 - どの質問を使うかを決めたら、文言調整(パイピングとも言います)によって質問の文言を決定します。これは、単純な置換(例: {TOKEN:FIRSTNAME})をするだけでなく、性別や回答者の数に応じて語句を変化させることができます。また、他の質問に回答したか(または回答内容)に基づいて、配信するメッセージを変更することもできます。
- 式 - EMでは、式による演算結果を格納する質問タイプ式が新たに追加されました。演算結果は、たとえページ上で非表示であっても、データベースに書き込まれます。これにより、暗黙的なスコア計算、複雑な式に基づくナビゲーション、評価、およびレポートの作成・保存が可能になります。
出現条件とカスケードされた出現条件
すべての質問タイプで、質問が表示されるかどうかを制御する出現条件オプション が追加されました。EMは、アンケートに登場する順に出現条件式を処理します。式が真(または式がない - 古い形式のアンケート向け)の場合、質問が表示されます。出現条件式が偽の場合、質問は非表示になり、その値はデータベースでNULLになります。グループに出現条件を満たす質問がない場合、グループ全体がスキップされます。
さらに、式に含まれる変数のいずれかの出現条件が偽の場合、式は常に偽と評価されます。これにより出現条件の入れ子が可能になり、各々の質問に対して非常に長い出現条件の式を書く必要がなくなります。
たとえば、5つの質問Q1〜Q5があり、Q1に回答したらQ2、Q2に回答したらQ3、のように表示したいとします。出現条件の式は次のようになります。
質問コード | 出現条件 | 質問 |
---|---|---|
Q1 | 1 | 名前は何ですか。 |
Q2 | Q1 | {Q1}さん、何歳ですか。 |
Q3 | Q2 | {Q2}歳なのですね。結婚していますか。 |
Q4 | Q3 == "Y" | {Q1}さん、結婚して何年になりますか。 |
Q5 | Q4 | {Q1}さん、子供は何人いますか。 |
グループレベルの出現条件
ExpressionScriptは、グループレベルの出現条件もサポートしています。これにより、ループの実装が容易になります。例えば、最大10の事柄(製品や世帯の構成人員など)に関する情報を収集するため、まずその数を質問するとします(例えば、世帯の構成人数や、リストの中から好きな製品をいくつか選んでもらうなど)。後から繰り返し聞くべき回数が分かったら、10個用意したグループごとに{count>=1}、{count>=2}、... {count>=10}などのグループレベルの出現条件を設定することができます。それぞれのグループ内では、質問レベルの条件付きロジック(例:回答者の性別や年齢別の質問)を作成することができます。質問レベル、グループレベルの出現条件の式は、質問を表示すべきかを決定するため両方が使われます。
例を確認するには、次のアンケートをインポートします。国勢調査の例
以下のスクリーンショットでは、回答者が少なくとももう1人の同居者と一緒に住んでいる場合、グループPerson 1が表示されている(出現条件を満たしている)ことがわかります。
文言調整/パイプ
中括弧内のものはすべて式として扱われるようになりました(後述する例外が1つあります)。式はすべてのLimeReplacementFields、すべての変数(いくつかのエイリアスを介して)、すべての一般的な演算子(数学、論理、比較)、数多くの関数(クライアント側でも動的に機能する)にアクセスします。
これらの式を使用すると、次のようなことができます。
- 先行する質問への回答に基づいて文言調整されたメッセージを回答者に表示する
- 評価を作成し、その結果に基づいて評価結果を表示する(条件付きで分岐、またはメッセージを表示する)(評価モジュール自体は使用しない)
- 質問、回答、およびレポート内の動詞、名詞を変化させる
- アンケートの最後で、"回答を表示する"ページの前に回答の要約を表示する
式
式と呼ばれる新しい質問タイプがあります。これは、表示されている値をデータベースに格納することを除き、テキスト表示質問タイプと似ています。したがって、式の質問テキストに評価計算が含まれている場合、パブリックまたはプライベート統計情報内に表示できる変数の値がデータベースに格納されます。
構文
中カッコ内に含まれるものはすべて式と見なされます(ただし、先頭または末尾に空白があってはなりません。これは、ExpressionScript が埋め込みJavaScriptを処理しないようにするためです)。
開く括弧の後、閉じる括弧の前に空白がない限り、式は複数の行にまたがっても構いません。特に次のような入れ子のif()構文が見やすくなります。
{if(is_empty(PFTotals),
'',
if(PFTotals >= -5 && PFTotals <= -4,
'Very Soft',
if(PFTotals >= -3 && PFTotals <= -2,
'Soft',
if(PFTotals == -1,
'Somewhat Soft',
if(PFTotals == 0,
'Moderate',
if(PFTotals == 1,
'Somewhat Hard',
if(PFTotals >= 2 && PFTotals <= 3,
'Hard',
if(PFTotals >= 4 && PFTotals <= 5,
'Very Hard',
''
)
)
)
)
)
)
)
)}
ExpressionScriptは次の構文をサポートしています。
- すべての標準的な数学演算子 (例: +,-,*,/,!)
- すべての標準的な比較演算子(例: <,<=,==,!=,>,>=, およびこれらと同等のもの: lt,le,eq,ne,gt,ge)
- 括弧(式のグルーピング)
- 条件演算子(例: &&,||, およびこれらと同等のもの: and,or)
- シングルクォートやダブルクォートで囲まれた文字列(それぞれが異なるクォートタイプの文字列を埋め込むことができます)
- コンマ演算子(式のリストを持つことができ、最終結果を返すことができます)
- 代入演算子(=)
- 定義済みの変数(質問、質問属性、回答を参照するため) - 例: すべてのSGQAコード
- 定義済みの関数(既に80以上ありますが、簡単に追加できます)
演算子
EM構文は、通常の演算子の優先順位に従います。
レベル | 演算子 | 説明 |
---|---|---|
1 | () | グループ化または関数呼び出しのための括弧 |
2 | ! - + | 単項演算子: 否定、正負 |
3 | * / | 乗算、除算 |
4 | + - | 加算、減算 |
5 | < <= > >= lt le gt ge | 比較演算子 |
6 | == != eq ne | 同等比較 |
7 | and | 論理積 AND |
8 | or | 論理和 OR |
9 | = | 代入演算子 |
10 | , | コンマ演算子 |
番号と文字列の不一致およびアルファベット/数字の比較に関する注意
値を不等号や等号で比較する場合は、値の型の不一致に注意してください。ユーザーが入力した値や選択した回答コードが明確に数値であれば、数値として使用されます。
いずれかの値が"
で囲まれている場合は、文字列としての比較が行われます。数値として比較したい場合は"
で囲んではいけません。
たとえば、Q0.NAOKが数値質問で値が9である場合、Q0.NAOK > "50"
はtrueとなります。これは、演算子>
が、これを数値の比較ではなく、文字列の比較とみなすためです。
整数値を確実に比較するには、intval(Q0.NAOK) > 50
を使用します。Q0.NAOKが数値でない(空または文字列)場合は、intval(Q0.NAOK) === 0となります。文字列値を比較する("A" < "B")場合は、直接strcmpを使用します。strcmp(Q0.NAOK,"B")
またはstrcmp(Q0.NAOK,"A5")
代入演算子(=)を使用する際の注意
代入演算子は、予期せぬ副作用を引き起こす可能性があるため、絶対に必要でない限り、使用しないでください。たとえば、先行する回答の値を変更した場合、その質問と現在の質問との間のカスケードされた出現条件と検証ロジックは再計算されないため、内部的に一貫性のないデータになりえます(たとえば、NULLであるべきものに回答が残る、回答すべきなのにスキップされる、など)。一般に、変数に値を割り当てる場合は、式の質問タイプを作成し、その値を設定するための式を使用する必要があります。しかし、本当に必要なことがまれにあるため、この演算子が用意されています。
この演算子について注意を促すため、構文式の中では赤いフォントで表示されています("=="と混同しないようにするため)。
代入演算子を使用する
代入演算子を使用する主な理由は次のとおりです。
- 既定値を設定できない質問の既定値を式を使って設定する必要がある(ユーザーインターフェイスでは回答オプションの1つを選択できるが、式を入力することができないリスト、ラジオなど)。ただし、LimeSurveyは式がその質問に対する許容可能な回答を生成できるかどうかを検証できないため、注意して使用してください。
- 後続の回答に基づいて先行する質問への回答を強制的に変更する必要がある
- など...
この目的のために、式マネージャーシステムを使用することができます。この目的のため、式を使用する方がよいでしょう。
例:
- 質問への回答を小文字にする:
{QCODE=strtolower(QCODE.NAOK)}
- アンケートの開始時に配列質問タイプに既定の回答を設定する:
{Q1_SQ1=(is_empty(Q1_SQ1.NAOK),"A99",Q1_SQ1.NAOK)}
- アンケート開始時に配列テキストの質問タイプに対する既定の回答を設定する:
{Q1_SQY1_SQX1 = (is_empty(Q1_SQY1_SQX1.NAOK),"Inserted answer", Q1_SQY1_SQX1.NAOK)}
- 条件付きの回答を設定する:
{QCODE=if(YesNo="Y","A1","")}
XSS セキュリティ
XSSを有効にすると、式マネージャーシステムの一部がつかえなくなります。
- 式でHTMLタグを開き、別の式で閉じる
- URL内で複雑な式を使用する
例と解決策
{if( 1 ,"<strong>","")}information{if( 1 ,"</strong>","")}
はXSSでは正常に動作しないため、次のようにします。{if(1,"<strong>information</strong>","information")}
<a href="/script.php?value={if(QCODE == "Y","yes","no")}">next</a>
は、ここでは式の質問を使います。なぜなら、完全な質問コードがOKだからです。<a href="/script.php?value={EQUATION.NAOK}">next</a>
変数へのアクセス
ExpressionScriptでは、必要な変数に読み取り専用でアクセスできます。下位互換性を保つため、次のものにアクセスすることができます。
- TOKEN:xxx - トークンの属性値(例: TOKEN:FIRSTNAME、TOKEN:ATTRIBUTE_5)(匿名でないアンケート向け)
- INSERTANS:SGQA - 回答として表示する値(例: "はい")。{QCODE.shown}と同じです。
- テンプレート内で使われるすべての{XXX}
- 質問テキストでは、{QID}を質問IDで置き換え、{SGQ}を質問のSGQAで置き換えることができます。
さらに、ExpressionScriptでは、質問コード(データベース内の質問テーブルの'タイトル'列)で変数を参照できます。これは、データをSPSS、R、またはSASにエクスポートするときに使用される変数ラベルでもあります。例えば、名前、年齢、性別について質問がある場合、12345X13X22、12345X13X23、12345X13X24といった形ではなく、name、age、genderといった形で変数を呼び出すことができます。これにより、式が読みやすく、ロジックが検証しやすくなり、また、グループや質問の番号を把握することなく質問をシャッフルすることが可能になります。
重要: 先行するページや質問で発生する変数を参照するのが安全です。
さらに、ExpressionScriptを使用すると、多くの質問プロパティにアクセスできます。
Qcode | Qcode.codeの別名 | {implode(',',name,gender)} | 'Tom','M' |
Qcode.code | 出現条件を満たす場合に質問に対して選択された回答コード(出現条件を満たさない場合は空白)、またはコード化された質問でない場合のテキスト値 | {implode(',',name.code,gender.code)} | 'Tom','M' |
Qcode.NAOK | Qcodeと同じ - NAOKの議論を参照 | {gender.NAOK} | 'M' |
Qcode.value | 出現条件を満たす場合の質問の評価値(出現条件を満たさない場合は空白)、またはコード化された質問でない場合のテキスト値 | {gender.value} | '1' |
Qcode.valueNAOK | Qcode.valueと同じ - NAOKについての議論を参照 | {gender.valueNAOK} | '1' |
Qcode.shown | 質問で表示される値 | {implode(',',name.shown,gender.shown)} | 'Tom','Male' |
Qcode.question | 質問のテキスト | {gender.question} | 'What is your gender?' |
Qcode.mandatory | 質問への回答が必須かどうか(Y/N) | {gender.mandatory} | 'N' |
Qcode.qid | 内部の質問番号(連番ではない) | {gender.qid} | 337 |
Qcode.type | 質問タイプ | {gender.type} | 'G' |
Qcode.jsName | このページで宣言されているかどうかにかかわらず、質問の正しいjavascript名 | {gender.jsName} | 'java1827X3X337' |
Qcode.gid | 内部のグループ番号(連番ではない) | {gender.gid} | 3 |
Qcode.qseq | 0から始まる質問の連番 | {gender.qseq} | 5 |
Qcode.gseq | 0から始まるグループの連番 | {gender.gseq} | 1 |
Qcode.relevanceStatus | その時点で質問が表示条件を満たすかどうか(0/1) | {gender.relevanceStatus} | 1 |
Qcode.relevance | 質問レベルの出現条件の式 | {gender.relevance} | '!is_empty(name)' |
Qcode.grelevance | グループレベルの出現条件の式 | {gender.grelevance} | 'num_children >= 5' |
Qcode.sgqa | 質問に対応するSGQA値 | {gender.sgqa} | '1827X3X337' |
HTMLエディターの問題
HTMLエディターを使用すると、一部の文字がHTML実体に置き換えられます。
- & → &
- < → <
- > → >
HTMLエディターを使用する場合は、次のものを使用する必要があります。
- & に対しては and
- < に対してはlt
- <= に対してはle
- > に対してはgt
- >= に対してはge
式の中のHTML表現を消去することをお勧めします。 LimeSurvey HTMLエディターを使用する場合は、エディターの左上にある"ソース"ボタンをクリックして、式に関連しないすべての文字を削除します(例:
、
など)。
Qcode変数の命名
Qcodeを構成する方法(およびいくつかのプロパティにアクセスする方法)の質問タイプ別の詳細は次のとおりです。一般に、Qcodeは次のように構成されます。
QuestionCode . '_' . SubQuestionID . '_' . ScaleId
commentとotherの場合、対応する質問コードはそれぞれQuestionCode_comment、QuestionCode_otherです。
タイプ | 説明 | コード | サブ質問 | 回答の選択肢 | 尺度 | 回答コード | 回答の表示 | 出現条件 |
---|---|---|---|---|---|---|---|---|
5 | 5点選択ラジオボタン | Q1 | 1-5 | {Q1} | {Q1.shown} | {Q1==3} | ||
B | 配列(10点選択)ラジオボタン | Q2 | L1-L6 | 1-10 | {Q2_L2} | {Q2_L2.shown} | {Q2_L2==7} | |
A | 配列(5点選択)ラジオボタン | Q3 | 1-5 | 1-5 | {Q3_1} | {Q3_1.shown} | {Q3_1>=3} | |
1 | 配列(フレキシブルラベル)二元スケール | Q4 | sq1-sq5 | 0:a1-a3 | 1:b1-b3 | {Q4_sq1_0} | {Q4_sq1_1.shown} | {Q4_sq1_1=='b2'} |
H | 配列(フレキシブル) - 列形式 | Q5 | 1-5 | s,m,t | {Q5_1} | {Q5_1.shown} | {Q5_1=='s'} | |
F | 配列(フレキシブル) - 行形式 | Q6 | F1-F5 | 1-5 | {Q6_F3} | {Q6_F3.shown} | {Q6_F3==4} | |
E | 配列(増加/同じ/減少)ラジオボタン | Q7 | 1-7 | I,S,D | {Q7_4} | {Q7_4.shown} | {Q7_4=='D'} | |
: | 配列(複数フレキシブル)1から10 | Q8 | ls1,todo,ls2 | min,max,avg | {Q8_ls1_max} | {Q8_ls2_avg.shown} | {Q8_ls2_min==7} | |
; | 配列(複数フレキシブル)テキスト | Q9 | hp,st,sw | 1st,2nd,3rd | {Q9_hp_3rd} | {Q9_hp_3rd.shown} | {Q9_hp_3rd=='Peter'} | |
C | 配列(はい/わからない/いいえ)ラジオボタン | Q10 | 1-5 | Y,N,U | {Q10_1} | {Q10_1.shown} | {Q10_3=='Y'} | |
X | テキスト質問 | Q11 | {Q11.shown} | |||||
D | 日付 | Q12 | {Q12} | {Q12.shown} | ||||
* | 式 | Q13 | {Q13} | {Q13.shown} | {Q13>5} | |||
~124~ | ファイルアップロード(アップロードされたファイルの数) | Q14 | {Q14} | {Q14>0} | ||||
G | 性別ドロップダウンリスト | Q15 | M,F | {Q15} | {Q15.shown} | {Q15=='M'} | ||
U | 非常に長い自由文 | Q16 | {Q16} | {Q16.shown} | {strlen(Q16)>100} | |||
I | 言語質問 | Q17 | {Q17} | {Q17.shown} | {Q17=='en'} | |||
! | リスト - ドロップダウン | Q18 | 1-5 | {Q18} | {Q18.shown} | {Q18==3} | ||
L | リスト ドロップダウン/ラジオボタン | Q19 | A-Z | {Q19} | {Q19.shown} | {Q19=='X'} | ||
O | コメント付きリスト ドロップダウン/ラジオボタン+テキスト | Q20 | A-F | {Q20},{Q20comment} | {Q20.shown} | {Q20=='B'} | ||
T | 長い自由文 | Q21 | {Q21} | {Q21.shown} | {strstr(Q21,'hello')>0} | |||
M | 複数選択チェックボックス | Q22 | A-F、その他 | {Q22_E}, {Q22_other} | {Q22_E.shown}, {Q22_other.shown} | {Q22_E=='Y'} | ||
P | コメント付き複数選択 チェックボックス+テキスト | Q23 | A-F | {Q23_D}, {Q23_Dcomment} | {Q23_D.shown} | {!is_empty(Q23)} | ||
K | 複数数値質問 | Q24 | self,mom,dad | {Q24_self} | {Q24_self.shown} | {Q24_self>30} | ||
Q | 複数テキスト | Q25 | A-F | {Q25_B} | {Q25_B.shown} | {substr(Q25_B,1,1)=='Q'} | ||
N | 数値質問タイプ | Q26 | {Q26} | {Q26.shown} | {Q26 > 30} | |||
R | 順位 | Q27 | 1-4 | {Q27_1} | {Q27_1.shown} | {Q27_1==3} | ||
S | 短い自由文 | Q28 | {Q28} | {Q28.shown} | {Q28=='mine'} | |||
Y | はい/いいえラジオボタン | Q29 | {Q29} | {Q29.shown} | {Q29=='Y'} |
NAOKの利用
NAOK --> "Not Applicable"(該当なし、NA)は問題ない(OK)
NAOKは、すべてまたは一部の変数が出現条件を満たさないということです。"Not Applicable"(該当なし、NA)は問題ない(OK)。
例: count(Q1_SQ1,Q1_SQ2,Q1_SQ3,Q1_SQ4) は、Q1のサブ質問の一つがフィルターされている場合、常に空文字となります。このような質問で、チェックされたサブ質問の数をカウントするには、count(Q1_SQ1.NAOK,Q1_SQ2.NAOK,Q1_SQ3.NAOK,Q1_SQ4.NAOK)とします。サブ質問が隠されている場合、式マネージャーは空文字を返します。
NAOKがない場合、1つの質問または1つのサブ質問が非表示になっている場合、式マネージャーは常に空の文字列を返します(falseを返すのと同じです)。
.shownは常にNAOKの仕組み(非表示の時は空文字)を使っていますが、回答のコードが必要な場合は、質問コードの後に.NAOKを付加するとよいでしょう。それが必要で、内容を理解している場合は別です。
詳細は、入れ子条件の上書きの項にあります。
予約変数 "this"、"self"、"that"
回答されたサブ質問の数を数えたり、スコアを合計するなど、質問の全体を評価したい場合がよくあります。また、質問の特定の行や列(行や列の合計を取得してデータベースに格納するなど)を処理したい場合もあります。以下の予約変数により、そのプロセスを簡単に実現できます。
変数"This"
"this"変数は、"質問全体の検証式と"サブ質問の検証式"のオプションで排他的に使用されます(後者はGUIからは使えません)。該当の質問内の各セルの変数名に展開されます。したがって、各エントリーが3より大きいかどうかを確認する場合は、"サブ質問の検証式"を(this > 3)に設定します。
変数"Self"
"self"と"that"変数はより強力で、式を処理する前に展開されるマクロとして機能します。"self"変数の構文は次のとおりです。
- self
- self.suffix
- self.sub-selector
- self.sub-selector.suffix
- suffixは、通常のqcode接尾辞のいずれかです(例: NAOK、value、shown)。
- sub-selectorは次のいずれかです。
- comments - コメントのサブ質問のみ(例えば、コメント付き複数選択、コメント付きリスト)
- nocomments - コメントでないサブ質問
- sq_X - Xは、行または列の識別子。パターンXと一致するサブ質問のみが選択されます。検索は完全なコード識別子で行われ、sq_Xは、サブ質問nX、X、Xnとマッチし選択されることに注意してください(例えば、sq_1を使用する場合はサブ質問a1、1a、1、11または001が含まれます)。二元スケール質問タイプのサブ質問コードはQCODE_SQCODE_1、QCODE_SQCODE_1であり、順位付け質問タイプのサブ質問コードはQCODE_1、QCODE_2、....となります。
例:
- 質問に対して何らかの回答があったか? -> {count(self.NAOK)>0}
- この質問の評価点数は? -> {sum(self.value)}
行と列の合計を取得することもできます。列A-Eと行1-5の数字の配列があるとします。
- 総合計は? -> {sum(self.NAOK)}
- 列Bの合計は? -> {sum(self.sq_B.NAOK)}
- 行3の合計は? -> {sum(self.sq_3.NAOK)}
変数"That"
"that"は"self"に似ていますが、他の質問を参照することができます。構文は次のとおりです。
- that.qname
- that.qname.suffix
- that.qname.sub-selector
- that.qname.sub-selector.suffix
qnameは、サブ質問拡張がない質問の名前です。質問 'q1'を作成したら、それがqnameとなります。
例:
- 質問q1に対して何らかの回答があったか? -> {count(that.q1.NAOK)>0}
- q2の評価点数は? -> {sum(that.q2.NAOK)}
- q3の総合計は? -> {sum(that.q3.NAOK)}
- q4の列Cの合計は? -> {sum(that.q4.sq_C.NAOK)}
- q4の行2の合計は? -> {sum(that.q4.sq_2.NAOK)}
"self"と"that"変数は、出現条件、検証、文言調整の式で使用できます。
注意すべき点は、ロジックファイルの表示機能を使用すると、"self"と"that"を展開した値が表示されることです。これにより、生成される実際の式が表示され、その結果、変数が存在することを検証できるようになります。かなり長い式になることもあるので、混乱しているように見えるかもしれません。しかし、質問を編集すると、"self"や"that"を使っている元の式が表示されます。
- 式に使用される各変数を明示的に命名する場合。
- サブ質問を持たない変数を使用する場合(例:単一回答の質問)。そのような場合に、変数にプレフィックスとして"that"をつけると、予期しない結果が出る危険があります。
関数へのアクセス
ExpressionScriptでは、以下に示すように、数学関数、文字列関数、およびユーザー定義関数が利用できます。これらの関数にはPHPとJavaScriptに同等のものがあるので、サーバ側(PHP)とクライアント側(JavaScript)で同じように動作します。新しい機能を追加するのは簡単です。
実装済み関数
現在、以下の関数が利用可能です。
関数 | 意味 | 構文 |
---|---|---|
abs | 絶対値 | number abs(数値) |
acos | アークコサイン | number acos(数値) |
addslashes | 文字列をスラッシュで引用 | string addslashes(文字列) |
asin | アークサイン | number asin(数値) |
atan | アークタンジェント | number atan(数値) |
atan2 | 2変数のアークタンジェント | number atan2(数値, 数値) |
ceil | 切り上げ | number ceil(数値) |
checkdate | グレゴリオ暦で有効な日付であれば真(1)を返す | bool checkdate(月,日,年) |
convert_value | 変換元リストにある数値を対応する変換先リストの数値に変換する | number convert_value(変換する数値, 一致フラグ, 変換元リスト, 変換先リスト) |
cos | コサイン | number cos(数値) |
count | リスト内で回答された(空白でない)質問の数 | number count(引数1, 引数2, ..., 引数N) |
countif | 回答された質問のうち、比較値と等しいリスト内の回答の数 | number countif(比較値, 引数1, 引数2, ... 引数N) |
countifop | 回答された質問のうち、条件を満たすリスト内の回答の数(回答 演算子 比較値) | number countifop(演算子, 比較値, 引数1, 引数2, ... 引数N) |
date | 指定した、もしくはローカルの日付/時刻を書式化する | string date(日付書式[, 指定時刻=time()]) |
exp | eの累乗を計算する | number exp(数値) |
fixnum | 必要に応じてカンマで区切られた数字を表示する | string fixnum(数値) |
floor | 切り捨て | number floor(数値) |
gmdate | 指定した、もしくは現在のGMTの日付/時刻を書式化する | string gmdate(日付書式 [, 指定時刻=time()]) |
html_entity_decode | HTML エンティティを文字に変換する(常にENT_QUOTESとUTF-8を使用) | string html_entity_decode(文字列) |
htmlentities | 文字をHTML エンティティに変換する(常にENT_QUOTESとUTF-8を使用) | string htmlentities(文字列) |
expr_mgr_htmlspecialchars | 特殊文字をHTML エンティティに変換する(常にENT_QUOTESとUTF-8を使用) | string htmlspecialchars(文字列) |
expr_mgr_htmlspecialchars_decode | HTML エンティティを特殊文字に変換するHTML エンティティに変換する | string htmlspecialchars_decode(文字列) |
idate | 指定した、もしくは現在のローカルな時刻/日付を整数として整形する | string idate(文字列 [, 指定時刻=time()]) |
if | Excelスタイルのif(条件,trueの時の結果,falseの時の結果) | if(条件,trueの時の結果,falseの時の結果) |
implode | 配列要素を文字列により連結する | string implode(区切り文字,引数1,引数2,...,引数N) |
intval | 変数の整数としての値を取得する | int intval(数値 [, 基数=10]) |
is_empty | 変数が空であるかどうかをを判定する | bool is_empty(変数) |
is_float | 変数の型が浮動小数点型かどうかを判定する | bool is_float(変数) |
is_int | 変数が整数型かどうかを判定する | bool is_int(変数) |
is_nan | 値が数値でないかどうかを判定する | bool is_nan(変数) |
is_null | 変数がNULLかどうかを判定する | bool is_null(変数) |
is_numeric | 変数が数字または数値を示す文字列であるかを判定する | bool is_numeric(変数) |
is_string | 変数の型が文字列かどうかを判定する | bool is_string(変数) |
join (2.0 build 130129から追加) | 要素を新しい文字列として結合する | join(引数1, 引数2, ... 引数N) |
list | 空白でない値をコンマ区切りリストで返す | string list(引数1, 引数2, ... 引数N) |
listifop (3.16.1 から追加) | リスト内の質問のうち、条件(属性 演算子 値)を満たす質問属性(retProp)を抽出し、指定の文字で区切ったリストとして返す | string listifop(属性, 演算子, 値, 質問属性, 区切り文字, sgqa1, sgqa2, ... sgqaN) |
ltrim | 文字列の最初から空白 (もしくはその他の文字) を取り除く | string ltrim(文字列 [, 削除する文字のリスト]) |
max | 最大値を探す | number max(引数1, 引数2, ... 引数N) |
min | 最小値を探す | number min(引数1, 引数2, ... 引数N) |
mktime | 日付をUNIXのタイムスタンプとして取得する(引数は省略可) | number mktime([時 [, 分 [, 秒 [, 月 [, 日 [, 年 ]]]]]]) |
モジュロ関数 | モジュロ関数はまだサポートされていません。代わりにfloor()関数を使用します。 | floor(x/y)==(x/y) |
nl2br | 改行文字の前にHTMLの改行タグを挿入する | string nl2br(文字列) |
number_format | 数字を千位毎にグループ化してフォーマットする | string number_format(数値) |
pi | 円周率 | number pi() |
pow | 指数表現 | number pow(基数, 指数) |
quoted_printable_decode | quoted-printable文字列を8ビット文字列に変換する | string quoted_printable_decode(文字列) |
quoted_printable_encode | 8ビット文字列をquoted-printable文字列に変換する | string quoted_printable_encode(文字列) |
quotemeta | メタ文字をクォートする | string quotemeta(文字列) |
rand | 乱数を生成する(例) | int rand()またはint rand(最小値, 最大値) |
regexMatch | 文字列を正規表現で比較する | bool regexMatch(パターン,文字列) |
round | 数値を任意の精度に丸める | number round(値 [, 丸める桁位置]) |
rtrim | 文字列の最後から空白(またはその他の文字)を取り除く | string rtrim(文字列 [, 削除する文字のリスト]) |
sin | サイン | number sin(数値) |
sprintf | フォーマットされた文字列を返 | string sprintf(フォーマット, 引数1, 引数2, ... 引数N) |
sqrt | 平方根 | number sqrt(数値) |
stddev | リストの数値について標本の標準偏差を計算する | number stddev(引数1, 引数2, ... 引数N) |
str_pad | 文字列を固定長になるまで他の文字列で埋める | string str_pad(入力文字列, 文字列の長さ [, 埋める文字]) |
str_repeat | 文字列を反復する | string str_repeat(入力文字列, 繰り返し回数) |
str_replace | 検索文字列に一致したすべての文字列を置換する | string str_replace(検索文字列, 置き換える値, 対象の文字列) |
strcasecmp | 大文字小文字を区別しないバイナリセーフな文字列比較を行う | int strcasecmp(文字列1, 文字列2) |
strcmp | バイナリセーフな文字列比較を行う | int strcmp(文字列1, 文字列2) |
strip_tags | 文字列からHTMLおよびPHPタグを取り除く | string strip_tags(文字列, 取り除かないタグ) |
stripos | 大文字小文字を区別せずに文字列が最初に現れる位置を探す(0から始まり、現れなければfalse) | int stripos(対象文字列, 検索文字列 [, 開始位置=0]) |
stripslashes | クォートされた文字列のクォート部分を取り除く | string stripslashes(文字列) |
stristr | 大文字小文字を区別しないstrstr | string stristr(対象文字列, 検索文字列 [, 最初の一致箇所より前のみを返すかのフラグ=false]) |
strlen | 文字列の長さを得る | int strlen(文字列) |
strpos | Unicode文字列内の部分文字列が最初に現れる場所を見つける(0から始まり、現れなければfalse) | int strpos(対象文字列, 検索文字列 [ 開始位置=0]) |
strrev | 文字列を逆順にする | string strrev(文字列) |
strstr | 文字列が最初に現れる位置を見つける | string strstr(対象文字列, 検索文字列[, 最初の一致箇所より前のみを返すかのフラグ=false]) |
strtolower | 文字列を小文字にする | string strtolower(文字列) |
strtotime | 英文形式の日付をUnixタイムスタンプに変換する | int strtotime(文字列) |
strtoupper | 文字列を大文字にする | string strtoupper(文字列) |
substr | unicode文字列の一部分を返す | string substr(文字列, 開始位置 [, 長さ]) |
sum | 配列中の値の合計 | number sum(引数1, 引数2, ... 引数N) |
sumifop | 回答された質問のうち、条件を満たすリスト内の回答の合計(回答 演算子 比較値) | number sumifop(演算子, 値, 引数1, 引数2, ... 引数N) |
tan | タンジェント | number tan(数値) |
time | 現在のUnixタイムスタンプを返す | number time() |
trim | 文字列の先頭および最後から空白(またはその他の文字)を取り除く | string trim(文字列 [, 削除する文字のリスト]) |
ucwords | 文字列の各単語の最初の文字を大文字にする | string ucwords(文字列) |
unique | 空白以外の回答のすべてが一意である場合にtrueを返す | boolean unique(引数1, ..., 引数N) |
<span id="Create_new_expression_functions_with_plugin_ (New in 4.0.0 )">
プラグインを使って新しい式関数を作成する (4.0.0 から追加)
コアに存在しない新しい機能が必要な場合は、プラグインを使って作成できます。このような新しい関数は、expressionManagerStart/jaイベントを使用して作成します。
ExpressionScriptとローカル変数
ページのJavaScriptを適切に構築するため、ページで使われている変数とJavaScriptのIDをEMに指定しなければなりません(例: document.getElementById(x))。また、他のページにどの変数が設定されているかも指定する必要があります(これにより、必要な<input type='hidden' value='x'>フィールドが存在しているか確認できます)。
条件の入れ子
いずれかの変数がfalseである場合、式全体はfalseになります。たとえば、次の表では、N/Aは変数の1つがtrueでないことを意味します。
演算子 | 例 | a | b | 結果 |
---|---|---|---|---|
+ (単項演算子) | +a | N/A | false | |
! | !a | N/A | false | |
== (またはeq) | a == b | N/A | 5 | false |
== (またはeq) | a == b | N/A | 0 | false |
== (またはeq) | a == b | N/A | N/A | false |
!= (またはne) | a != b | N/A | 5 | false |
!= (またはne) | a != b | N/A | N/A | false |
!= (またはne) | a != b | N/A | 0 | false |
> (またはgt) | a > b | N/A | 5 | false |
>= (またはge) | a >= b | N/A | 5 | false |
< (またはlt) | a < b | N/A | 5 | false |
<= (またはle) | a <= b | N/A | 5 | false |
and | a and b | N/A | 5 | false |
and | a and b | N/A | N/A | false |
or | a or b | N/A | N/A | false |
or | a or b | N/A | 5 | false |
+ | a + b | N/A | 5 | false |
* | a * b | N/A | 5 | false |
/ | a / b | 5 | N/A | false |
() | (a) | N/A | false | |
(exp) | (a && b) | N/A | 5 | false |
(exp) 演算子 (exp) | (b + b) > (a && b) | N/A | 5 | false |
関数 | sum(a,b,b) | N/A | 5 | false |
関数 | max(a,b) | N/A | 5 | false |
関数 | min(a,b) | N/A | 5 | false |
関数 | implode(', ',a,b,a,b) | N/A | 5 | false |
関数 | if(a,a,b) | N/A | 5 | false |
関数 | is_empty(a) | N/A | false | |
関数 | is_empty(a) | 0(または空白) | true | |
関数 | !is_empty(a) | N/A | false |
入れ子条件の上書き
出現条件を満たした回答の合計を表示したいとします。式 {sum(q1,q2,q3,...,qN)}を使おうとするでしょう。しかし、これは内部的にLEMif(LEManyNA("q1","q2","q3",...,"qN"),"",sum(LEMval("q1"),LEMval("q2"),LEMval("q3"),...,LEMval("qN")))に変換されます。これにより、値q1〜qNのいずれかが出現条件を満たさない場合、式は常にfalseを返します。この場合、sum()はすべての質問が出現して回答されない限り"0"となります。
この問題を回避するために、各変数には".NAOK"という接尾辞(「Not Applicable」はOK)を追加することができます。このような場合、次のような現象が発生します。変数q1.NAOKがあるとします。
- q1はLEManyNA()節には追加されません
- LEMval('q1')は回答が出現条件を満たしているかを確認し、そうでない場合は""を返します(出現条件を満たさない回答は無視されますが、式全体を無効にしません)。
したがって、合計を求める方法は、次の式となります。sum(q1.NAOK,q2.NAOK,q3.NAOK,...,qN.NAOK)
.NAOK接尾辞を使用することで、いくつかの経路を経た後、共通の経路に収束するアンケートを設計することができます。例えば、回答者が正常な範囲外となるような回答をしたとします。アンケート作成者は、有効な結果を得られない可能性があることを回答者に警告し、アンケートを継続するかどうかを尋ねることができます。彼らが"はい"と答えた場合、残りの質問が表示されます。"残りの質問"の条件は、最初に正常な範囲内で回答があったかどうか、または正常な範囲外で回答した場合にのみ出現する質問に"はい"と回答したかどうかをチェックします。
ExpressionScriptはどのように条件付き文言調整を実現するか?
ここでは、カスタマイズ(質問タイプ'expr'は式を意味します)の例を示します。
質問コード | 出現条件 | 質問タイプ | 質問 |
---|---|---|---|
name | 1 | テキスト | 名前は何ですか。 |
age | 1 | テキスト | 何歳ですか。 |
badage | !is_empty(age) | 式 | {(age<16) or (age>80)} |
agestop | badage | メッセージ | {name}さん。あなたの年齢は本アンケートの対象年齢{if( (age<16),'より低いです',if( (age>80),'より高いです','に一致します') ) }。 |
kids | !badage | はい・いいえ | 子供はいますか。 |
parents | 1 | 式 | {!badage && kids=='Y'} |
numKids | parents | テキスト | 子供は何人ですか。 |
kid1 | parents && numKids >= 1 | テキスト | 一人目の子供は何歳ですか。 |
kid2 | parents && numKids >= 2 | テキスト | 二人目の子供は何歳ですか。 |
kid3 | parents && numKids >= 3 | テキスト | 三人目の子供は何歳ですか。 |
kid4 | parents && numKids >= 4 | テキスト | 四人目の子供は何歳ですか。 |
kid5 | parents && numKids >= 5 | テキスト | 五人目の子供は何歳ですか。 |
sumage | 1 | 式 | {sum(kid1.NAOK,kid2.NAOK,kid3.NAOK,kid4.NAOK,kid5.NAOK)} |
report | parents | テキスト | {name}さん、あなたは{age}歳で、{numKids}人の子供がいます。最初の{min(numKids,5)}人の子供の年齢の合計は{sumage}です。 |
このアンケート例をダウンロードするには、次のリンクをクリックしてください。子供の数のアンケート例
これらの質問はすべて1つのページ(たとえば同じグループ)に入れることができ、出現条件に合致する質問のみが表示されます。さらに、子供の年齢を入力すると、最後の質問のsum()式がページ上で動的に更新されます。
ExpressionScriptで、それぞれの式を名前付き<span>要素で囲むことによってこの機能が実現します。値が変更されるたびに、その<span>要素に表示される式を再計算し、再表示します。そのようなカスタマイズされた式が同じページに数十、または数百もあっても、ページは一度の画面更新ですべて再表示されます。
構文ハイライト
式の入力と検証を支援するために、EMでは以下のような構文強調表示機能を提供しています。
構文ハイライトの種類と意味
色 | サンプル | 意味 | ツールチップ | コメント |
---|---|---|---|---|
黄褐色の背景 | サンプル | 式全体 | なし | 式として認識される中括弧内のもの(先頭または末尾の空白がない)は、周囲のテキストと区別できるよう黄褐色の背景で色分けされます。 |
太字赤色のテキスト | サンプル | エラー | エラーに関する説明 | 未知の変数または関数のエラーの可能性があります。アンケートは正常に機能せず、該当する式に依存する質問は回答者に表示されません。 |
青色のテキスト | サンプル | 関数名 | 関数の意味と構文 | 関数名、または開く括弧が続くので関数とみなされるものは、太字の青いテキストで表示されます。ツールチップには、その関数の意味と許容される構文が表示されます。 |
灰色のテキスト | サンプル | 文字列 | なし | シングルもしくはダブルクォートで囲まれた文字列が灰色のテキストとして表示されます。 |
水色のテキスト | サンプル | 同じページにセットされた変数 | [名前またはSGQAコード]: 質問; 値; それぞれの値のコードが示された回答リスト | 現在編集中の質問と同じページに設定されている変数は水色のテキストで表示されます(javascriptで更新されます)。ツールチップには、名前(INSERTANS:xxxを使用した場合)またはSGQAコード(新しい命名システムを使用した場合)、実際の質問、および現在の値(ない場合は空欄)が表示されます。列挙された値セットからの選択を期待する質問タイプの場合、値を表示するためのコードが表示されます。 |
緑色のテキスト | サンプル | 先行するページでセットされた変数 | [名前またはSGQAコード]: 質問; 値; それぞれの値のコードが示された回答リスト | 先行するページに設定されている変数は太字の緑色のテキストで表示されます。ツールチップには、名前(INSERTANS:xxxを使用した場合)またはSGQAコード(新しい命名システムを使用した場合)、実際の質問、および現在の値(ない場合は空欄)が表示されます。列挙された値セットからの選択を期待する質問タイプの場合、値を表示するためのコードが表示されます。 |
太字桃色のテキスト | サンプル | 後続のページでセットされた変数 一般的には、アンケート開始時は空で、インデックスや前への移動を使用した時に値が入ります。 | [名前またはSGQAコード]: 質問; 値; それぞれの値のコードが示された回答リスト | 後続のページに設定されている変数は太字の桃色のテキストで表示されます。ツールチップには、名前(INSERTANS:xxxを使用した場合)またはSGQAコード(新しい命名システムを使用した場合)、実際の質問、および現在の値(ない場合は空欄)が表示されます。列挙された値セットからの選択を期待する質問タイプの場合、値を表示するためのコードが表示されます。 |
太字の黄褐色のテキスト | サンプル | LimeSurveyの置換値 | 値 | LimeSurveyの置換文字列({TOKEN:xxx}、{PRIVACY_MESSAGE}など)は太字の黄褐色のテキストとして表示されます。 |
赤色のテキスト | サンプル | 代入演算子(=) | 注意喚起メッセージ | 代入演算子(=)を使用すると、演算子は赤色のテキストで表示されます。これは、aとbは等しいこと(a == b)を確認しようとしているとき、誤ってbの値をaに代入すること(a=b)を防ぐのに役立ちます。 |
通常の黒色のテキスト | サンプル | 句読点 | なし | 式の内の他の句読点はすべて、通常の黒文字で表示されます。 |
赤枠で囲まれたテキスト | 赤い太線で囲まれたエラー | 構文エラー | エラーの説明 | 検出された構文エラーは、エラーを赤い枠線で囲んで表示されます。ツールチップにエラーが表示されます。例としては、括弧の不一致、未定義関数の使用、関数に渡す引数の数の誤り、構造化が不十分な式(変数間の演算子の欠落など)、読み取り専用変数に値を代入しようとする、変数でないものに値を代入しようとする、サポートされていない構文を使用するなどです。構文エラー検出システムは、複数のエラーがあっても式内のエラーが1つあるとレポートします。ただし、エラーが検出された場合は、少なくとも1つのエラーが表示されます。 |
参考記事
ExpressionScriptのサンプルアンケート
ユースケースとHowTo
ステップバイステップの例
開発者向け参考情報