Conditions Engine
From LimeSurvey Manual
Updates
- New UI skeleton
The UI skeleton
I think UI cannot be explained through words, so some screencasts.
The Variables manager
The Conditions manager
The Actions manager
The Connections manager
Current working backend implementation
Database tables
In the current implementation, there are four tables in the database to store conditions. They are: conditions, actions, bindings, condition_action_mappings.
- conditions table: It will store conditions in RPN format.
- actions table: It will store actions.
- bindings table: It will store info on the different variables used in the conditions in he conditions table.
- condition_action_mappings table: It will connect the conditions to the actions through IDs.
Modification of getVar function of expression engine
- Using the Binding Model, get the bindings related to the variable which has called the function.
- Function Plugin name is contained in the type attribute of the bindings.
- Use the type attribute to include the file and call the function.
- The function uses the fake_answer table to get answer to question based on question number which also a part of the bindings.
- Return the value found to the evaluate function of the variable which called it.
The conditions controller
Currently scans through all the conditions in the conditions table and evaluates and performs actions based on the evaluations.
Operand plugin
It currently is a function in a file of the same name as the function which gets the value of a variable based on the attributes passed to it.
Example of How the implementation works
- Suppose the conditions table contains a condition "ab<20".
- When I call the evaluate_conditions function of conditions controller, it fetches the above condition from the Condition model.
- Then it asks the Expression Engine to create an expression out of it and evaluate it.
- The evaluate function evaluates each operand and operator.
- In evaluation of the operand (in this case ab which is a previous question operand), it uses the getVar function which finds if the variable has a binding attached to it in the Binding model. If not found it finds if the vars array of the Expression Engine has some bindings otherwise it returns null.
- If a binding is found in the Binding model, the stored bindings are unserialized and the "type" attribute (in this case "PreviousQuestion") is used to call the function plug-in connected to that type of operand.
- The operand function plug-in returns value of the variable based on the other attributes (in this case "question_number") supplied using the fake_answers table.
- This value is return all the way up to the evaluate function of the expression which then uses it to evaluate the expression.
- Based on the response, the conditions controller performs the action mapped to the condition based on the ConditionActionMapping model.
Usage of Expression Engine for binding variables to operand classes(The Plan):
- Modify getVar function to be able to get variables and their keywords from the bindings table.
- Binding of keywords to variables will be done during condition building and stored in the bindings table.
- An inside look on how evaluate function of VariableExpression can be changed:
- First get all bindings using the getVar function.
- Each Variable will have an optional keyword "type" for use with operands. Whenever "type" keyword is detected in the binding array it will be handled differently.
- Suppose "Type"="FUQT" and "Question_number"="12345"
- If ("type" keyword is there) Build classname of respective out of type keyword.
- Match if all necessary keywords ( "Question_number" here) are available using the type keyword from the operands table ("Operand_Name") .
- Now use readoperand function of class to return the value using the other keywords as arguments.
- readoperand function is a function of the operand class which can find a particular operand's value if given enough values.
- Finally return that value to the Expression Engine
Relating to DBSE
The storage of conditions will be kept separate from DBSE. It will only contact the DBSE for getting answers, etc. Similarly, DBSE will contact Conditions Manager to inform of changes to questions, answers, etc. using an observer.
The Conditions Manager (A vague idea)
It consists of following major functions:
Conditions_storage()- This part will do the job of handling the GUI while storing conditions and actions into the three tables. Three possible functions:
- Storeconditions() – The GUI gets a datatype as input which this function looks into the operands table to select the proper plug-in. It creates an object of that plug-in datatype. Activates functions in the plug-in to display context specific property in the GUI.User selects property in GUI which is stored in the object.The ConvertOPerand function turns the Object into string.All Operands after conversion are concatenated with the operator symbols and changed into RPN using the RPN converter.The strings are then stored into the conditions table with unique ID.
- StoreActions()-It stores the actions in the action table depending upon input from the GUI.
- Action_condition_connector() – This function connects the action and the conditions together.
Conditions_ retriever()- This function will do the job of getting conditions from the condition table, action table and condition_action table and converting it into infix form.
Conditions_trigger()-
- This function is supposed to help in triggering a particular condition while running a survey.
- We don’t want all conditions in a conditions table to be evaluated to check whether they are true everytime a new question is answered.
- This function will scan the condition strings for keywords to pinpoint which conditions need to be evaluated.
Conditions_implementer()-
- This function will first use the conditions_trigger() to check which conditions need to be evaluated.
- It will then get the conditions using function condition_retriever().
- The evaluate function of the ExpressionEngine will then return a boolean value which will then be used to perform a particular action based on the ID of the particular action.
- If the evaluate function returns a true then a function actions_retriever() is accessed which retrieves that action depending upon the ID in the condition_actions table.
- Finally, the perform() function will tell the survey engine or jscript to perform the particular action.
Operands ( currently implemented as a function plugin, the following may not apply )
The basics
Each operand plug-in will have a class contained in a file which will be an extension of the VariableExpression class of the EE. Possible fields : Operand_code, Operand_text.
Example class:
class PQ_plugin
{
QuestionID;
Other keywords;
convertoperand(); //returns the coded name of the object(from the operand table)
getoperand(); //gets all the keywords and there representations to be used in the form in the GUI
readoperandval(); // uses getoperand() to get keywords and contains statements to access the value
}
Each function is specific to an operand, so all keyword handling is abstracted from the conditions manager's eyes. All that is the operand plug-in's work.
Grammar for operand storage
An example of the representation of an operand is FUQT_1234. This implies the FUQT class and file number 1234. We don't need to explicitly define in the operand string what a particular keyword points to. All this will be done by the getoperand function of a particular plug-in class depending upon the keywords defined in the class. Each plug-in will have its own set of keywords, and getoperand, readoperandval and convertoperand functions. Each plug-in's functions will define exactly how to perform the task implied by the function's name with respect to that type of operand.All the routing work of giving the task of reading, converting to the plug-in will be done by the conditions_manager which will try to find a class of similar name as the datatype or some modification of it in a fixed directory.
Operand role in conditions_storage() :
On getting input through the GUI, the conditions manager looks up the operand table and tries to find a class of similar name and create its object. The getoperand() function then comes into action and provides the GUI with data on what type of form elements need to be dynamically created for the keywords input(like selecting what keyword related to that operand is to be used in the condition). After all necessary objects are created, the convertoperand() function converts the operand into code form using operand table.
Operand role in conditions_retriever() :
It will use getoperand() function to get the operand data (like keywords) and also use the operand table to display the operand in text form wherever necessary.
Operand role in conditions_implementer() :
Uses readoperandval()which in turn uses getoperand to get data on keywords. It(readoperandval() has functions and statements to gain access to the value of the operand.
Excerpts from DBSE-CE meeting 6/7/2010
1. Conditions and Actions will remain independent objects.
2. Actions will use the same Expression Engine
3. DBSE will provide observer to let us know when to trigger conditions/Actions
4. A new possible table-layout of 3 tables : conditions-actions, bindings, and conditions-actions mappings
5. An example of condition : {var} getanswer 'filesize' geattribute 1000000 <
6. {var} is accessed through bindings table i.e all necessary data like(survey_id, question_id) stored in bindings table.