BPC SurveyManager - Creating Surveys - Rules Scripting

From RiskWiki

Jump to: navigation, search

Contents

BPC SurveyManager Rules Scripting Language

Introduction

BPC SurveyManager builds every page displayed on your browser dynamically. In most cases a basic assumed rule is used each time you post a page of answers to questions which essentially says "display the set of questions listed for next page group". So many users will never need to use the rules engine and scripting capabilities, but in reality SurveyManager wants to build each page by selecting the questions to display based on the answers given to the questions so far. To do this it has a small but powerful scriptig language for rules.


The Rules Scripting language defines how a rule executes. Each question can have (optionally) an indefinite number of rules, or none at all. When a Survey form is submitted (posted), the question responses for the entire page (form) are first written to the response table. Then the rules engine is run for each question in the current (submitted) form's question list.


This bahaviour means that all the responses for every question on the current page are available to every rule of every question on the page when rules are evaluated. So while rules are notionally attached to a question, in fact you could design your rules so that only one question on the page handled all rules for every question, and the question with the rule list could in fact be the first question on the page.


Further, rules can access the reponses from every question answered to date on the survey, and indeed any response from any question recieved for any survey in any organisation by the current user to date..


Rules provide a number of facilities from validation that has not been performed on the browser already, to selecting follow-on questions to ask based on the response to a question (either thse current question or any previous question in this or any other survey), to calling plugin libraries, etc. The rules provide the flow of control for a survey.


As the rules engine processes the form's rules, the question list for the next page is progressively populated by the rules. If after all the rules have been run, there are no questions in the question list, the Question Script for the QScript surveybody tag is used, or, if none, the QScript field vlue of the surveyheader, or if none the default page size is used to determine the next questions in the question list.


It is quite possible to make both recursive and looping survey question lists with the rules engine, so some thought is required when designing the rules. Simple surveys can be built without ever considering the rules engine (or defining any rules).


Note that a question included in the question list is still not guaranteed to be displayed to a given user. The question list is further filtered by the current instance and any filters attached to the question that must also be held by the current user in order to display.

  • NOTE FOR BPC SurveyManager V7 library users - You must list only 1 JumpFunction + followon command on for each of the true/false parts of a rule.
  • NOTE FOR BPC SurveyManager V8 library users - You may list many JumpFunction + 1 followon command on for each of the true/false parts of a rule, separated by commas.


Defining Question Rules

Question rules are stored in a dedicated rules table and linked back to the individual questions by OrgID , SurveyID and Question ID. Thus while questions can be shared across multiple surveys, the question rules are specific to the instance of a question in a specific survey.


The BPC Survey Manager Rules Script is similar to prolog or expert systems syntax, where each rule is essentially a boolean test with a part to execute if the boolean test is true and a part to execute if the test is false. The True and False parts can either identify other rules to execute, add questions to the question list, or invoke a plugin library.


Central to the operation of rules is the concept of the QRL (Question Resource Locator). This is analagous to a URL, only for questions. It uniquely identifies a survey and question or group of questions and a rule or all rules for a question.


The basic form of a rule is:


"RuleQRL = If Boolean_condition then { If_true_actionlist } else { If_false-actionlist }"


A RuleQRL is a QRL (Question Resource Locator) of the form:

SID.QID:RID which means SurveyID.QuestionID:RuleID
Where the RuleID MUST be a number.  For a given question, rules are executed in RuleID order (think line number in a program file).


Rules for an SID.QID are executed in numeric order according to the RID.


When entering rules into the BPC SMDeskTop V7, BPC RiskManager Survey Centre or BPC SurveyManager Web Client the 'If' 'then' 'else' '{' '}' symbols are assumed and should not be entered into the fields. In this document we will use the shorthand:

"RuleQRL Boolean_condition { If_true_actionlist } , { If_false-actionlist }"


Example Rules Are:

  • Simple always True test:
S0001.QID1:1 True { next } , { end }

Read this as: For Survey S0001, question ID QID1 rule 1, if True do the next rule else do stop processing rules for this question. Since the value of True is always True, this rule will NEVER execute the false part, and will always execute the next rule in the question if one exists.


  • Is the value provided by the user is greater than 10
S0001.QID2:1 gt(value,10) { .QID4, end }, { .QID3, end }


Read this as: For Survey S0001, question ID QID2 rule 1, If the response value for this question is greater then 10, do the rules in Question ID QID4 of this survey, else do the rules for Question ID QID3. In either case stop processing rules for this question.


  • Is the value provided by the user outside a range of numbers
S0001.QID3:1 or(lt([.],0), gt([.], 11)) { @(.QID4), next }, { .QID3.2, end }


Read this as: For Survey S0001, question ID QID3 rule 1, If the response value (note the alternate way of referencing the current question response) for this question is less then 0 or the current response is greater than 11, then ask Question QID4 from the current survey and process the next rule, else do the rules starting from Question ID QID3 rule 2 of this survey, and end rules processing for the current question.


  • Check if the value provided by the user is between two numbers, a equal to a number or the value of a different question equal to a number. Select some questions to display
S0001.QID3:2 or(and( gteq([.], 11), lteq(value, 20)), eq([.], 1), lt([.QID6],0)) { @( .QID24, .[PAGE2]), next }, { @(S0002.QID3), next }


Read this as: For Survey S0001, question ID QID3 rule 2, If the response value for this question is greater than or equal to 11 or less than or equal to 20, OR equal to 1 OR the response to question QID6 of the current survey is less than 0, then ask Question QID24 and all questions in QuestionGroup PAGE2 from the current survey and process the next rule for this question, else and ask the question in S0002 Qusetion ID QID3, and process the next rule in the current question.

Note: .QID24 and .[PAGE2] are QRL's. QRL's are the universal addressing mechanism used throughout SurveyManager to locate a survey, question, question list or rule. See the section on QRL's (below) for a complete breakdown of the format of a QRL.


NOTE ALSO: While gt, lt, gteq, lteq, eq, set all take TWO arguments, 'and' and 'or' can take an indefinite list of arguments.


NOTE ALSO: While the rules engine, and the QList generator can generate a list of questions from mixed surveys, the page builder does not yet handle this, so in this case the @(S0002.QID3) would actually present QID3 from S0001 to the survey user.


  • Override the response value for a question
S0001.QID4:3 set(.QID3, 1) { prev }, { end }

Read this as: For SID S001 QID QID4, set the response value for question QID3 to the value 1 and re-process the previous rule.

  • Select a list of questions to ask
S0001.QID4:1 gt(value, 10) {next }, {@(.QID6, .QID8, .QID9), end }

Read this as: Here we ask a simple list of questions if the current response was greater than 10.


About Boolean Conditions

The Boolean Condition expression is a test of true or false. Boolean tests can be nested with brackets to an indefinite number of levels. If the result is 'true' the true part of the rule is executed (the if_true_actionlist) , while if the result is False, the false part is executed (the if_false_actionlist).


A boolean expression is a boolean function followed by the argument(s) (if any) that the function uses in its test. So the simple boolean expression Grammar is:


BooleanExpression = BooleanArg | BooleanConstant | UnaryBooleanFunction( BooleanExpression ) | BinaryBooleanFunction( LeftHandBooleanArg, RightHandBooleanArg ) | ListBooleanFunction( BooleanExpressionList )
BooleanExpressionList = BooleanExpression, BooleanExpression, ...


Where = means 'must be one of' and '|' means 'or' and ... means 'repeat as needed'.


Therefore a boolean expression can be a single Boolean argument, single boolean constant with no arguments (True or False) or a Unary function with one argument, or a binary test with two arguments or a boolean list function of other boolean expressions. In all cases, a boolean expression must ultimately evaluate to True or False


We will consider each in turn:

1. BooleanArg

  • BooleanArg : value - The value of the current question's response for this user
  • BooleanArg : [.] - The value of the current question's response for this user
  • BooleanArg : [QRL] - The value of the QRL defined question's response for this user of the form 'SID.QID'
  • BooleanArg : pid - The PID (Person ID) of the current user
  • BooleanArg : pname - The Name matching the PID (Person ID) of the current user
  • BooleanArg : pemail - The email address matching the PID (Person ID) of the current user
  • BooleanArg : prole - The role (User/Guest/Admin) matching the PID (Person ID) of the current user in this org
  • BooleanArg : pgrole - The role (User/Guest/Admin) matching the PID (Person ID) of the current user in this database
  • BooleanArg : oid - The OrgID of the current survey
  • BooleanArg : sid - The SID (Survey ID) of the current user
  • BooleanArg : qid - The qID (Question ID) of the current user
  • BooleanArg : qgrp - The SID of the current user
  • BooleanArg : iid - The IID (InstanceID) of the current user
  • BooleanArg : "***" - where *** is some quoted string.
  • BooleanArg : prp( "propertyname" ) - Returns the Current value of a Temporary, Person, Survey, QuestionGroup or Question property (in that order).
  • BooleanArg : xfl( [QRL] ) - Returns the current exception trap value.
  • BooleanArg : qry( tablename, field ) - Returns the current value of a field in a dataset (for current OID,SID,QID,RID,PID,).


2. BooleanConstant

  • booleanfunction : True - Always true
  • booleanfunction : False - Always false


3. UnaryBooleanFunction( BooleanExpression )

  • booleanfunction : Not( BooleanExpression ) - True if ( BooleanExpression ) is false.
  • booleanfunction : Do( QRL ) - True if the boolean expression in the rule identified in the QRL is true. A QRL in this case is ALWAYS interpreted as a rule reference. If the rule number is ommited, rule 1 is assumed.


4. BinaryBooleanFunction( LeftHandBooleanArg, RightHandBooleanArg )

  • booleanfunction : lt(BooleanArg,10) - True if value is less than 10
  • booleanfunction : lteq(BooleanArg,10) - True if value is less than or equal to 10
  • booleanfunction : gt(BooleanArg,10) - True if value is greater than 10
  • booleanfunction : gteq(BooleanArg,10) - True if value is greater than or equal to 10
  • booleanfunction : eq(BooleanArg,10) - True if value is equal to 10
  • booleanfunction : tstXf(BooleanArg) - True if value is outside of exception trap range. The Exception trap is stored on a question.
  • booleanfunction : sx(BooleanArg,"willy") - True if value sounds like "willy"
  • booleanfunction : pm(BooleanArg,"I * like ? chocalate.") - True if value is sounds like the right hand string (where * matches any word(s) and ? matches any word.)


5. ListBooleanFunction( BooleanFunctionList )

  • booleanfunction : and( booleanfunction , booleanfunction, ... ,booleanfunction ) - True if all booleanfunction's in the list are true else false
  • booleanfunction : or( booleanfunction, booleanfunction, ... ,booleanfunction ) - True if one of the booleanfunction's in the list is true else false
  • booleanfunction : sxl(value,"willy","wonka","wilbur" ) - True if value sounds like "willy or wonka or wilbur"
  • booleanfunction : pml(value,"I * like ? chocalate.","I * like ? cake.","I * like ? teeth.") - True if value is sounds like any of the right hand quoted strings (where * matches any word(s) and ? matches any word.)
  • booleanfunction : exe( plgname, plgaction, plgargs...) - true if a Call to "plgaction" method in Plugin "plgname" with a comma separated list of plgargs returns true. Plugins are Plugin DLLs following the BPC-SM API standard.


About Boolean Action Lists

The Command Syntax

There are two possible outcomes of a boolean condition: True or False. Each of these possible outcome has its own action list. The "true" action list is therefore a list of actions to take if the boolean condition evaluates to true, and the "false" action list is exeuted if the boolean evaluates to false.


Note that in the boolean conditions QRL references were enclosed in [], to distinguish them from strings. In the action section strings are not generally legal (except in sadm, exe and set) so with the exception of those three functions, QRLs should NOT be encased in []. In fact in the action section the [] has a different meaning. A .[quesgroup] refers to a question group, NOT a question, and .[. for 20] means "the next 20 questions from the last question asked".


The basic form of an action list is:

JumpFunction, JumpFunction, ..., JumpFunctionFollowOn 


The term "JumpFunction" is essentially historic, as the first action lists were just lists of questions to ask on the next page of the survey, and therefore implied that the user was "jumping" to a specific list of questions. Over the years the power of the JumpFunction has been expanded beyond merely "jumping", but the term has remained.


Action List Execution Order And Behaviour

Action lists are evaluated left to right, and immediately they are encountered by the rules engine. They are essentially a comma separated list of actions.


To understand how the behaviour of the rules engine with action lists, it is best to think of it the list as a mix of:

  • questions to ask the user (that effect the user's display AFTER all rules on this page have been evaluated),
  • classic programmer command jumps which redirect the flow of rules execution immediately they are encountered,
  • calls to external plugins that return to the action in the list immediately after the call has completed


Consequently, it is possible to construct an action list where some members of the list will never be executed, for example, if the action list contains an action that causes the rules engine to jump to another rule, no actions in the list after that action will be executed.


JumpFunctions

A JumpFunction is one of:


Select A Rule To Execute

  • jumpfunction : next - do next rule or ques
  • jumpfunction : prev - do prev rule or ques
  • jumpfunction : end - stop processing rules for this question
  • jumpfunction : ninl - do "next if no list" this is a rarely used option, primarilly used internally. It issues a "next rule line" command after the current jump function only if the current jumpfunction is the last in a comma separated list of jumpfunctions. It is safe to ignore this as you will probably never need it. It is most useful where you have written a plugin library and wish to return jumpfunction instructions from the library. In that case you can safely return the NINL command in the jumpnode with an "ask list" to display and trigger a "next" jump when you otherwise don't know if the next would be issued. The next will only occur if the if the calling jumpnode is also the last (right most) in a list of jumpnodes.
  • jumpfunction : QRL - jump to question rule indicated by the QRL in this survey
  • jumpfunction : g(QRL) - Go To the rule identified in the QRL (Same as the simple QRL option above)


Decide which questions to ask

Multiple versions of the 'ask' command are allowed to support various rules definition interfaces. This is for syntactic convenience rather than semantic effect. Some users find the work 'ask' or 'askpage' easier then '@'. Programmers tend to prefer the shorter form '@'.

  • jumpfunction : @( QRL, QRL,.. ) - ask questions identified by the QRL list (containing 1 or more QRL's) on the next survey page
  • jumpfunction : ASK( QRL, QRL,.. ) - (Same as @) ask questions identifiedby the QRL list (containing 1 or more QRL's) on the next survey page
  • jumpfunction : ASKPAGE( QRL, QRL,.. ) - (Similar to @) ask questions identifiedby the page/QGroup list (containing 1 or more QGroup names) on the next survey page. The purpose of this command is to allow scripts that hide the QRL details. Eg: ASKPAGE( Page01, Page02) as opposed to @(.[Pape01],.[Page02]) - which does the same thing but looks more complex.
  • jumpfunction : SHOWPAGE( QRL, QRL,.. ) - (Same as ASKPAGE) ask questions identifiedby the page/QGroup list (containing 1 or more QRL's) on the next survey page


Do something with responses received

  • jumpfunction : SADM(a,b,c,d,e) - perform admin ops - see below for an explaination of the arguments.
  • jumpfunction : EXE( plgname, plgaction, plgargs...) - Execute a "plgaction" method in Plugin "plgname" with a comma separated list of plgargs returns true. Plugins are Plugin DLLs following the BPC-SM API standard.
  • jumpfunction : SET( QID, somevalue ) - Assign some value to the SID.QID response for this person. Note - it is possible to assign values to another survey.
  • jumpfunction : CLR( QRL, QRL,.. ) - Clears the response(s) for the current SID.QID response for this person. Note - it is possible to clear responses for another survey. Clearing is effected by deleting the response entirely. The QRL is a full QRL, which means you can use all the range commands available in a QRL (see the 'ask' discussion) - including clearing an entire survey response set, or a QGroup/Page at a time. It is locked to the current OID, IID, PID and REALMID. If all you want to do is clear a value while keeping a response record, use SET instead.
  • jumpfunction : CLRPAGE( QGroup, QGroup,.. ) - Clears the response(s) for the current SID.[QGroup] responses for this person. Note - it is possible to clear responses for another survey. Clearing is effected by deleting the responses entirely. The QGroup is a simplified QGroup/Page name - Not a QRL - (see the 'askpage' discussion) - and clears an entire QGroup/Page response set at a time. It is locked to the current OID, IID, PID and REALMID. If all you want to do is clear a value while keeping a response record, use SET instead.
  • jumpfunction : PCLR( property_name ) - clear the current value of the named property of the current user. Property names are any string surrounded by " ".
  • jumpfunction : PADD( property_id, somevalue ) - append a value to the end of the current value of a property of the current user. If the the property already has value(s), a ',' is added followed by the new value, making a comma separated list.
  • jumpfunction : PSET( property_id, somevalue ) - set the property of the current user to a value replacing the previous value.


Depracated - do not use

  • jumpfunction : E( QRL, QRL,.. ) - same as '@' + GLV property, ask questions identified by the QRL list (containing 1 or more QRL's) on the next survey page displaying the previous response. Use the GLV property instead. (DEPRACATED - Do Not Use)


Complex Boolean Action List Commands Explained

SADM

 The  SAdm command looks like: SADM( sid, cbxlist, action, instance) where
                         sid is a qrl is as above (but refers to the quest response) and
                         cbxlist is a qrl with the cbxuser list in its sub parts and
                         action is either publish | distribute | lock | unlock
                         instance is either all | current | an instanceid


All values in the SADM command may be retrieved from responses, or porperties, etc as well as hard-coded.

Set

The Set command parameters look like:

"Set(First_Arg, Second_Arg)"
 The First_Arg is a QRL address (Constrained to the current PID)


 The Second_Arg is one of:
        Second_Arg is a bracketed expression of the form opval,responstr, ques optype. Eg. "( 2, 'Fred', selectop)"
        Second_Arg is a bracketed expression of the form opval,responstr. Eg. "( 2, 'Fred') - use existing or ques optype"
        Second_Arg is an unbracketed espression of the form value. Eg. "value" meaning current question response's value set
        Second_Arg is an unbracketed espression of the form sss.qqq. Eg. "SURV1.QID002" meaning that question's response value set

PADD, PSET

The Property Set commands have similar parameters to the Set command and look like:

"PSet(First_Arg, Second_Arg)"
 The First_Arg is interpreted as a property_id (property name) if it is surrounded by " " and a QRL address (Constrained to the current PID) whose response string value contains a property name to use, otherwise.


 The Second_Arg is one of:
        Second token is a quoted expression of the form "fred" or "sss.qqq" Eg. "SURV1.QID002" meaning that question - 
               not its response value.
        Second token is an unquoted string with the word "value". Eg. "value" meaning current response value (responsestr)
        Second token is an unbracketed expression of the form sss.qqq. Eg. "SURV1.QID002" meaning that question's response value (responsestr)


There is a subtle but important use of the PSET/PADD commands. Apart from the obvious use of setting property values from survey questions, the commands can be used to populate a property value with a list of question QRLs. This is the main use of the PADD command which forms a comma separated list of values. When this list contains QRLs it becomes a QRLList. The QRL List can be read by the annotation survey layout to provide the list of questions to display for a given user.


Apart from the obvious use of displaying a list of questions to be answered for a survey page, the technique can be used to display a list of opinions, instructions or advices that change depending on a user's responses. Remember that while we call everything the BPC SurveyManager handles "a survey" and its content "questions" - a survey can in fact simply be a list of statements like a database of instructions, or information extracts (ie. not questions, at all). The PADD property command can therefore be used to decide what pieces such information to display by building a property list that is read by another survey.


Since a survey layout can be easilly designed to display multiple surveys on a page at once, you can have a dynamically changing commentary running in, say, a left hand panel, while a conventional survey is run in a right hand panel.


JumpFunction Examples

Some Examples include:

  • jumpfunction : next //do next rule or ques
  • jumpfunction : prev //do prev rule or ques
  • jumpfunction : end //stop processing rules for this question
  • jumpfunction : .QID1 //jump to question qid1 rule 1 in this survey
  • jumpfunction : .QID2:1 //jump to question qid2 rule 1 in this survey
  • jumpfunction : S001.QID1 //jump to question qid1 rule 1 in survey s0001
  • jumpfunction : S001.QID1:2 // jump to question qid1 rule 2 in survey s0001
  • jumpfunction : @(S002.QID5) // ask ques of SurveyId
  • jumpfunction : @(S002.[.QID5 to .QID14]) // ask ques of SurveyId from qid5 to qid14
  • jumpfunction : @(S002.[QS1]) // ask questions in QuesGroupId QS1 of SurveyId S002
  • jumpfunction : @(.QID5) // ask ques qid5 in this survey
  • jumpfunction : @(.QID5, .QID6, S002.QID7), end // ask questions in list and finish processing rules
  • jumpfunction : @([QS1]) // ask questions in QuesGroupId in this survey
  • jumpfunction : @(.) // ask this question (again)
  • jumpfunction : g(S002) // evaluate the first rule in SurveyId S002
  • jumpfunction : g(S001.QID1:2) // jump to question qid1 rule 2 in survey s0001
  • jumpfunction : SADM(a,b,c,d,e) // perform admin ops


Where:

@g = goto style jump - continue with target's QID list. (set current eosjump to eosjump - ie. no change)


About The JumpFunctionFollowOn

The last item in the Boolean Action List is the JumpFunctionFollowOn command. Its purpose is to tell the rules engine what rule to exectute next (after it has done whatever it was told to do in the action list. There are three follow on commands.


A JumpFunctionFollowOn is one of:


  • jumpfunction : next //do next rule or ques
  • jumpfunction : prev //do prev rule or ques
  • jumpfunction : end //stop processing rules for this question


If no followon is specified, next is assumed.



Access To Properties

Rules property retrieval can access most properties in the database and property setting commands can access those specific to the active user, but some properties are specifically restricted regardless. These Page Properties can not be accessed from rules:

hidden - lastques, nextques, etc...eopjump, eosjump


Properties that Effect Rule Execution

By default, when a page/form is recieved from a user a rule is only executed for a question on a page that actually has a response (either just posted, or previously posted). This means that an "infoop" question (a text display with no reponse capability) with a rule attached will never execute - because the survey engine will never get a response from an infoop! Yet, survey manager allows you to attach a rule to these questions. To find out why, read on...


Sometimes you want the rules attached to a question to ALWAYS execute, regardless of whether a response was received for that question. One such case might be where you attach rules to infoop questions. Your can force this by setting the "AlwaysDo" property of the question concerned to "True". When this question is displayed on a users browser, and they post the page back to survey engine, the rules of any question - including infoops with the "AlwaysDo" property set to true will always be executed.


This can be a very useful facility, partcicularly where you set the property on a section heading, pagebreak or other static text area, and design your pages so that all questions in a question group (think "page" for simplicity - but note that many question groups can in fact be displayed on a single page at once) are displayed as a group (rather than cherry picked to just display some questions from a group). In this case, rather than attaching rules to each question, you might attach all the rules for a group to the section heading "question" instead as these rules can range over all the data received for the group regardless.


Notes on designing question rules

You need to think carefully about the way you want your survey to work when designing the rules AND when deciding to which question you will attach the rules on any given page/question group.


1. Where should I put my rules?

The simplest scenario is that a rule deals with the response to the question to which it is attached, and there are a number of short forms in QRLs that are designed to make that especially favourable and simple to do - eg "[.]" or "value" refers to the response to the question to which the rule has been attached.


However, in many situations the ordering and selection of questions to display next may depend on the answers to several questions on the current page (or even previous pages or other surveys). In this case it might be easier to attach all the rules for a page or question group to one question that will always be displayed for that page or question group - such as a section heading with the AlwaysDo property set to True.


The downside of this is that in order to look at a response value you will have to refer to the questions by their ID in a QRL eg "[.Ques002]", rather than "[.]" the anonymous shortform. So if you chage the question id later, the rules for the group will be silently invalidated. But it does simplify the rules authorship a bit.


The other issue related to this is that when the QSort order is set to no sort (the default and recommended scenario) questions will be displayed in the order they are added to the questionlist for the next page by the rules. This means that the order in which rules execute might be important to you. Notionally, rules are executed in the order in which they were displayed on the submitted page. This is usually exactly what you want, BUT some times this results in an undesirable sort order for questions on the next page. Attaching all the rules to one question in a group is the easiest way to control with certainty the order in which questions are added to the question list. There are other options, but they are more complex to handle.



BackLinks



CopyRight Bishop Phillips Consulting Pty Ltd 1997-2012 ( BPC SurveyManager - Creating Surveys - Rules Scripting )
Personal tools