If you haven’t seen the first part of Testing User Knowledge With Flow, I’d suggest that you look at it here: Part One. Having a good understanding of the decisions that I made in setup and security will be important in understanding the flow and how it works.
The beauty of this setup is that you can test your users’ knowledge of not only salesforce related content but also business related content, as well as produce actionable results for not only the user but management. Get ready to build something that will make some jaws drop!
Naming conventions:
First thing’s first, with any flow, naming conventions are important:
- Prefix variables with “var”
- Prefix sobjects with “sObj”
- Prefix collections with “col”
- Prefix lookups with “get”, updates with “update” and deletes with “delete”
Trust me, naming conventions is just as important in flow as it is in code. Your brain will thank me.
Now on to the flow:
1. Create a lookup to get the exam information. We will say that the exam name is equal to a variable, which we will then pass in through our button (or visualforce page, both will work). We will then save the fields to the sobject Exam and the Id to the varExam variable for later reference.
2. Create a create element. We will need to first create the attempt at the exam so that we can reference its ID later for when we will update the record with the score and in order to create “Problem Question” records, we must have a record ID to associate with the records so this will come before asking each question.
3. We will then get all questions that are associated with the exam. This is where two requirements come into play:
- Only active questions should be available for the exam.
- To meet this requirement we will say that IsActive__c is equal to true.
- Questions must rotate in order.
- We will sort questions by the Random__c formula field referenced in Part One of testing user knowledge with flow.
All questions will be assigned to a collection of questions which we can then loop through for testing to see if the related answers are correct.
4. We will loop through the questions and assign them to an individual record for manipulation, sObjQuestion.
5. A decision element will need to be placed after the loop. This will let us determine if we are under or over the number of questions that we should ask. This allows for meeting the business requirement of asking a certain number of questions from a pool of questions. In order to determine if we are under or over the number of questions to ask, as we go through the flow, we will increment a counter through an assignment element.
6. Here’s where the real fun begins! We’re going to take the number of questions to ask and say if it is equal to 1 then it should go through the “One Answer” flow, with only one dynamic choice selection. If has more than one correct answer, then it should go through the multiple answer dynamic choice section of the flow. Thanks to having a rollup summary on the question with the number of right answers, we can easily evaluate this without needing to use a counter.
One Choice setup:
1. We will create a screen with a dynamic choice. On this one choice, we are going to put the question text in the header and inform the user to select the number of right answers (which should be one). In our dynamic choice, the results should be sorted by the “Random__c” field. This will ensure that questions always appear in a random order. We are then going to assign the field variables for reference in the flow.
2. After that, we will ask if the one choice was indeed correct. If it was, we will assign it to one of two assignments. If it is right, we will increment the number of questions right. If it is wrong, we will increment the number of questions wrong and also add it to a collection for problem questions. By incrementing the number of questions right and wrong, we can use a formula to divide the number of questions right by the number of questions wrong + the number of questions right to get the total score for the exam. The same will hold true for multiple choice answers.
For number of questions right:
For number of questions wrong and problem question creation:
3. From there, we will increment the “Number of Questions” to ask counter.
Multiple questions:
1. After our decision point for number of questions, we’re going to do a similar concept for placing the question text and the number of choices. However, we’re also going to add a variable for an error message. It’s also important to make sure that for the dynamic choice that we do not assign it to a variable. If assigned to a variable, it will only take the last selected choice, which we do not want.
2. In the next element, we are going to assign the multiselect choice to a variable. This will then return a semi-colon delimited string.
3. We are then going to make a decision element that checks to see if the number of selected choices matches the number of right answers with the below formula:
CEILING({!multSelectLength}/20)
If there is only one selected option, the formula will return .9 and the CEILING() function will round it up. If there are two options selected, 1.9 will be returned and will be rounded up to 2.
In our decision element, we will then say if it is equal to the number of choices, continue with the flow, otherwise we will assign our error message from a text template which contains:
<font color="red">You must select the required amount of choices.</font>
And will return an error such as the following:
4. Proceeding with the flow, we will do a fast lookup to get the correct answers associated with the question (yes, I know we’re doing a fast lookup in a loop and we really want to avoid that, unfortunately, it cannot be avoided here since we need to get all answers associated with questions and flow does not have an option to do something similar to a map in Apex).
5. From there, we will loop through the answers and go to a decision element that says “is this not a correct answer?” If not, we’re going to keep going through the loop. If we finish the loop, then we’re going to assume that all choices were correct and we did everything right.
To say if we have a correct answer, we’re going to use the assigned multi-select answer string and see if it contains the correct answer we retrieved:
CONTAINS({!varMultiSelectAnswers}, {!sObjAnswerLoop.Id} )
If it is wrong, just as we did for the one question choice, we will assign it a problem question record and add it to the problem record collection for a fast create once we exit our loop.
Exiting the loop:
1. Now that we’re out of the loop and have checked to see if we have all right answers, we can calculate our final score and do a record update with our exam with user record.
({!numberOfQuestionsRight} / ({!numberOfQuestionsRight} + {!numberOfQuestionsWrong})) * 100
2. Now we will ask ourselves, were there any problem questions? If the collection of problem collections is empty, we do not need to create problem question records (and we would get an error if we tried), otherwise, we will do a fast create of problem questions from the collection.
When done, your final flow should look something like this:
Lastly, we will display a screen that says thanks for taking the test! In my next post, I plan to cover, sending an HTML email to your user with his or her results, covering retakes and allowing for a certain number and rewarding badges.
Awesome work here🍻 I used some of these techniques when I designed an exam registration form for our customers and employees.