Table of contents
Randomizing the answer categories
Problem
You want to randomize the order of presentation of choices of answers to a question.
Solution
Add the ROTATION keywork to the question name line.
Discussion
The ROTATION keyword added anywhere on the question name line (except in the first position, obviously, as that one is reserved to the question name) tells CallWeb to shuffle the display of answer categories in random order. However, categories with a code greater or equal to 900 are always left at the end of the list of answer categories and are not subjected to randomization; this way, "Don't know" type answers can remain at the end of the list of choices.
Answer choices can be randomized in the same manner when questions are laid out in table format. The order of presentation of the entire table is then driven by the order selected for the first question in the table.
Displaying only some answer categories
You want to display only some of the answer categories attached to a question, according to other survey responses.
Insert an answer category display condition such as:
Each answer category of a question may be assigned a display condition. If one is, the answer category is displayed only if the condition is "true". In the example above, the choice "Globe and Mail" is displayed only if question/field CITY equals 5.
Answer category display conditions are independent from one another:
When questions are presented in table format, the answer categories of the first question in the table define the columns of the table, even if the answer categories bear display conditions. However, the radio button or checkbox or text box corresponding to an answer category which has a "false" display condition is left out of the table (i.e., the corresponding table cell is left empty).
Displaying only some entries in a large list of answer categories
You have a large list of possible answer categories and you want to display only a relevant subset of them to the respondent.
Use the SUBSET keywork or the CODESIN keyword on the question name line.
SUBSET
There are times when there are too many answer categories to show them all on-screen (e.g., a list of all municipalities in the country). The list of categories can then be reduced by using the SUBSET feature. When this feature is activated, only categories with a label containing certain characters are displayed. Here is how this works.
First, create a question with an open-end category that collects the few characters to search for. For example, insert a question requesting that the respondent enters a few characters of the name of the municipality where they reside; this question could be named "CHARS_OF_CITYNAME".
Second, create a regular question containing all possible answer categories (like all city names). Add other categories that you want to display all the time (e.g., Other, DK/NR) as required and give them an "F" behaviour (for "force display"). Add the keywork SUBSET= on the question name line of the question definition and complete it with the name of the variable containing the characters to search for, as in SUBSET=CHARS_OF_CITYNAME.
That's it. Upon using the questionnaire, only categories containing the few characters entered in the first question will be displayed when the second question is called up. Of course, these two questions cannot be part of the same screen as the formatting of the second requires that the answer from the first be available. The two questions need not immediately follow each other.
SUBSET type questions otherwise behave exactly like any other question. Therefore, a display condition can be applied to display the SUBSET question only when the initial question requesting a few characters to subset on has been correctly answered (and not left, for example, on a "Don't know").
CODESIN
The CODESIN= parameter of the question name line also performs a selection in the answer categories of the question to which it is attached. In this case, the only categories shown are those whose code is found in the open-end part named after CODESIN=. In the open-end part, the list of codes must be separated with "mu" characters (µ).
For example, the following code extracts part numbers from one data base based on price and displays only those part numbers in the follow-up question:
Randomly selecting a subset of possible answer categories
You want to show a random subset of possible answer categories to the respondent.
Use the select_random_combination function:
where
Sometimes, it is necessary to select a random subset of answer categories to be offered to the respondent. An example would be to randomly select two magazines and to ask the respondent to select the one they prefer. The special function select_random_combination is used for this purpose, along with the display of answer choices using category display conditions.
In the context of a CALCUL question, let's analyze the following call to this function:
This would place, in Q1 (which must have a maximum number of answers greater than 1 to accommodate pairs of selections or trios, etc.), a random selection of two integers between 1 and 5. The random selection will necessarily be different from the selections stored in Q2 and Q3 but it could include an integer already selected in Q2 or Q3.
This would place, in Q1, a random selection of three integers between 1 and 9. The random selection will necessarily be different from the selections stored in Q2 and Q3 and it cannot include an integer already selected in Q2 or Q3.
It is usually preferable to calculate these random selections only once so that, if the respondent backtracks in the questionnaire, he/she doesn't get assigned a different random selection. The following code achieves this:
If it is not possible to find a combination because of the constraints imposed by the programmer, the function returns "---". This would happen:
The result from this function can be used in another question which will display answer categories based on the calculated question. For example:
will display only the choices that were randomly selected in the Q1 subset.
Randomly selecting a subset of selected answer categories
You want to select a random combination of answers provided to a multiple-answer question.
Use a CALCUL question with an expression like the following:
Sometimes, it is necessary to select a random subset of answers provided to a multiple-selection question. An example would be to randomly select two magazines among a list of magazines selected by the respondent. The special function random_subset is used for this purpose.
This function selects a random subset of the answers provided and returns the result as a value that can be used on another multiple-selection question. If the respondent originally offered fewer answers than required in the random subset, all of their answers will be included in the "random subset".
The result of this function can be used in display conditions as if it had been produced in response to a multiple-selection question.
It is possible to randomly select a single answer as well (e.g., RANDOMSELECTION = random_subset($MULTIQ,1)); such a selection would be stored in a single-answer question, not a multiple-selection question.
Adding an initial message to a drop-down list
You want to create a drop-down list of answer categories and force respondents to select an answer other than the default one.
Create a non-selectable initial drop-down list category with code the following:
Usually, the initial category shown by default off a drop-down list is empty or is a message such as "Select a category". This initial category is selected by default in HTML but this is usually not an answer that the questionnaire designer wishes to record (it would be like recording the absence of a selection in a radio group).
The code for the proposed first category creates an initial category labelled "Select a category" and makes it non-selectable — which, in the context of a drop-down list, means that selecting it produces an error message.
Creating a question requesting a value and a unit
You want to ask a respondent for a number which could be expressed in different units.
Questions like this are called "unit-type questions". They have two components: an open-end numeric value and a single-choice series of units. For example, asking for the distance between home and work could be a unit-type question: the open-end numeric value is the number of units of distance and the single-choice series refers to the unit of measurement such as kilometers and miles. From CallWeb's standpoint, what is particular to unit-type questions is that the open-end part should not correspond to a particular unit choice and that the selection of a unit which is not the same code as the open-end must be permissible.
When a code corresponding to an open-end part has an N behaviour code, CallWeb expects that an answer other than that code will be selected by the respondent.
Adding headers in answer sets
You want to insert headers in an answer set to visually regroup answer categories.
Insert non-selectable categories in the answer set as in:
Answer categories tagged with an N behaviour code are considered "non selectable". That means that they are displayed on screen but that no method of selecting them is offered: they don't come with a radio button, nor a check box. Therefore, only their label is displayed and they can play the role of headers in the answer set.
Such headers can be made visually more evident by bolding them, boxing them or changing the color of the text. All of these appearance changes can be implemented through CSS styles and a style can be applied to header text using the SPAN HTML tag as in:
Ensuring that category permutations are the same for two questions
You want the answer categories of two questions to be presented in random order but in the same order for both questions.
Use the ROTATION=Qx feature to create parallel permutations.
Say, in question Q1, you are asking whether one has read each magazine in a list over the past month. You want that list to be presented in random order to avoid order effects. You can simply add the ROTATION keyword to the question name line.
Let's add a second question Q2 which uses the same list; you want the order of presentation of magazines in Q2 to be the same as in Q1. Then use ROTATION=Q1 on the Q2 question line. This indicates that the answer categories for Q2 should be in the same order as those in Q1.
If Q1 and Q2 are in a question permutation, it is possible that CallWeb would display Q2 before Q1. In such circumstance, there would be no random order to apply to the answer categories in Q2 and CallWeb would generate an answer category permutation for Q2 independently from Q1. In this example, to make sure that Q1 and Q2 use parallel permutation, use ROTATION=Q2 for Q1 and ROTATION=Q1 for Q2. The first question displayed will control the persentation of the answer categories of the second one.
This recipe applies to two questions. If more than two questions must share a random order of answer caregories, make sure that one of them is always displayed first and make the other ones dependent upon that first question.
Drawing answer categories from an external source
You want to ask for a selection among choices that are not predetermined but rather extracted from an external source.
Use the CATEGORIES keywork on the question name line.
Sometimes, the categories that you want the respondent to choose from are not predetermined. For example, you could want a schoolboard to select from a list of schools that is extracted from a data base maintained outside of CallWeb. While there are other strategies available if the external list is short, a long external list is best handled with the CATEGORIES question type.
The CATEGORIES question type identifies the name of an open-end part which contains the categories to be displayed. That open-end part must contain a string of pairs of codes and labels all delimited with a mu character ($dlm_niveau1 if used in a calculation). Here is an example of such a pair of variables.
ASCHOOL_LIST contains a mu-delimited list of school codes and school labels. In this example, the list is created by a direct CALCULation but it could be created from an extraction from another data base that is post-formatted as a mu-delimited list. ASCHOOL_LIST is then used as the argument to the CATEGORIES options of DELEGATIONS.
DELEGATIONS is a CATEGORIES question using ASCHOOL_LIST as input. This means that the options "Label 1" to "Label 10" are displayed in DELEGATIONS (as implicit answer codes 1 to 10 because the numbering of answers starts at 1 and goes up to the number of external categories). By default, each category label is displayed with its category code appended, in parentheses; it is possible to avoid appending the category code by adding a tiles (~) immediately after the equal sign (as in CATEGORIES=~ASCHOOL_LIST). A CATEGORIES question needs an open-ended part to store the selected codes (9999 in the example); the open-ended part code should have a behaviour code of "I" to make it invisible to the respondent. The selected of external codes are stored in the open-ended part of the CATEGORIES question, delimited by mu characters.
Keeping the table columns the same width
You want to ensure that all columns in a table are the same width.
Use the Pixels option of the TABLE pound instruction.
When questions and answers are presented in matrix format (with questions as rows and answers as columns, usually), it is visually pleasing that all answer columns be the same width. In the case of columns housing a scale (such as a satisfaction or an agreement scale), keeping columns all the same width reinforces the interval-scale nature of the set of responses.
The TABLE pound instruction possesses an option to specify the minimum number of pixels to be used by each column (other than the first, row label column). In the following example,
the "80" indicates that each column should use at least 80 pixels.
The Pixels value is the minimum column width. If one word in the header cell is larger than that value, the column accommodates that larger width. In order to ensure that the column uses exactly the number of pixels stated by the Pixels option, hyphenate long words by adding a hyphen followed by a <br> (line break).
Inserting an other box in a table
You want to lay out several items in a table format and add an "other" choice at the end.
Lists presented in table format sometimes end with an "Other, specify" item, as in the following incomplete example of a data table:
The following code could produce this output. The MUST parameters ensure that an answer is required in both questions if one is given in either question.
Note that <BOX></BOX> insertions can be put anywhere where text is expected (e.g., in answer categories, in notes, even in question text).
Displaying semantic differential scales
You want to lay out items in a semantic differential format.
This table...
...is produced with the following code:
A battery of semantic differential scales typically focus on one object and tests different attributes of that object using scales that different end-points, such as repulsive vs. attractive, old-fashion vs. modern, cheap vs. expensive.
How does the CallWeb code works?
A similar technique can be used to create scales that are recurringly labelled at both ends as in:
This table uses the following code:
Using the top left corner of the table
You want to add information (text or otherwise) in the top left corner of a questionnaire table.
Use the CORNER (or COIN) parameter of the TABLE pound instruction.
A typical questionnaire table layout defined by this TABLE pound instruction looks as follows.
Note that the top left corner of this table is left unused. To add information (typically text) in this table box, use the CORNER parameter of the TABLE pound instruction, as in:
The CORNER text (a.k.a. COIN) needs to be surrounded by parentheses so that CallWeb knows where the CORNER information starts and finishes. If you need to include parentheses in the text, use the HTML special codes ( for an open parenthesis and ) for a closed parenthesis. Different language segments can also be provided using the usual "bracket-language code-bracket" syntax as in:
It is also possible to change the corner information when the header line is repeated within a table. For example:
This is requires two steps:
The highest priority in selecting the CORNER text is given to SUFFIX instructions, then to the question name line, and finally to the TABLE pound instruction.
Modifying the appearance of recalled text
You want to modify the way recalled text is displayed on screen.
Use the SUBSTITUT CSS style or create a new CSS and tag the recalled text using that style.
Text that is recalled using the &QUESTIONNAME syntax or the &&AQUESTIONNAME syntax is automatically tagged with the SUBSTITUT CSS style. You can change the appearance of the recalled text by adjusting this style in the project styles.css file.
You can also implement finer adjustments by tagging the recall using a custom style as in:
and defining that new style in the styles.css file. For example, the following style definition displays the recalled text entirely in lowercase:
Displaying different text upon recall of an answer
You want to recall a previous response but display text that is different from the original answer.
Use the ALIAS feature of answer categories and a recall which uses the ALIAS number.
It is sometimes useful to recall a label other than the one displayed originally when asking the question. For example, say you ask the following question:
and you want to the follow-up question to read Was this accident... in the first case, Was this incident... in the second case and Was this event... in the third case.
You could create a second CALCUL question to compute a synonym and recall these labels or you could use the EXECUTE syntax and the Perl ternary operator to display a label conditional to the previous answer. But there is a simpler solution: label aliases.
In the example above, one would code the intial question as follows:
and use the following recall in the subsequent question:
The &Q1 recalls the answers from Q1 and the #1 states that ALIAS1 must be used. If one category did not possess an ALIAS1, the standard label would be displayed. There can be more than one alias on any given code; the one corresponding to the recall number is displayed.
Controlling where to show the progress bar
You want to control where the progress bar is shown on screen.
Use the appropriate pound instructions.
Three pound instructions affect the placement of the progress bar (also called thermometer) on the questionnaire page.
# Display thermometer determines whether the progress bar is displayed at all. Its default value is NONE which means that the progress bar is not displayed.
Other possible values of # Display thermometer activate the display of the progress bar and determine whether it is presented at the TOP (alternatively HAUT) or at the BOTTOM (alternatively, BAS) of the questionnaire. The same pound insrtuction identifies whether the progress bar is expressed as a percentage of the questionnaire that is completed or as a number of pages left in the questionnaire; the former corresponds to the keyword PERCENT (alternatively, POURCENTAGE) and the latter to NUMBER (alternatively, NOMBRE).
Thus, the following instruction displays the progress bar at the top of the questionnaire, as the percentage of the questionnaire that is completed:
The following instruction displays the progress bar at the bottom of the questionnaire, as the number of pages left in the questionnaire:
Thus, this instruction places the progress bar between the Previous Page and Next Page buttons:
In template mode, the placement of the progress bar is also affected by # Display thermometer but the instructions &*BUTTONSTOP and &*BUTTONBOTTOM place the "top" buttons (defined by # Button order top) and the "bottom" buttons (defined by # Button order bottom) anywhere on the page. Additionally, the instructions &*BUTTONH and &*BUTTONV, using the same series of key letters listed above, display a horizontal or a vertical list of buttons anywhere on the page.
Formatting the progress bar
You want to control the appearence of the progress bar.
No less than nine pound instructions control the appearence of the progress bar. The easiest way to identify them is using an image like this one.
Formatting buttons, boxes and dropdown lists
You want to control the appearence of buttons, boxes and dropdown lists.
Use the .BUTTON, .TEXTBOX and .DROPDOWN CSS styles in the style.css file located in the project directory.
Questionnaire HTML buttons are formatted according to the .BUTTON CSS style whereas questionnaire open-end text use the .TEXTBOX CSS style, numeric boxes use .NUMBOX, and the dropdown lists follow the .DROPDOWN CSS style. All CSS properties relevant to these HTML objects can be adjusted.
For example, here is an image of a typical (unformatted) HTML button: Let's create the following CSS .BUTTON style and store it in the style.css file for the project:
Here is how the button now displays: (Cute!) Buttons can also benefit from a visual effect when the mouse travels over them. Let's redefine the .BUTTON style and add a .BUTTON:hover style as follows. (We can't show the result here but you can try it in your projects.)
The same is true for text boxes. Here is an unformatted text box:
Here is how the text box now displays:
Numeric boxes are controlled by the .NUMBOX format. Distinguishing numeric boxes from text boxes allows things like the right-justification of numbers in numeric boxes (using text-align: right;).
Let's see dropdown lists. Here is an unformatted dropdown list: OneTwo
Here is how the droprown now displays: DROPDOWNOneTwo
CATI CONTEXT: In the CATI interface, buttons are used for a variety of purposes. CallWeb offers a collection of CSS styles that can produce more professional-looking pages. The :hover trick can be used to "animate" the buttons.
Adaptative button text
You want the text on a button to change according to circumstances, such as at the end of sections in a questionnaire.
Use conditionnally recalled values in the pound instructions that define the text on buttons.
First, let's clarify which pound instructions control the text of the buttons (note that the same trick could be used to change the image used by image-based buttons):
Let's focus on the "Next page" button (# Forward text) — the discussion applies to the other instructions.
The common use of this instruction is as follows:
This will show the text "Next page >>>" on the submit button on every page that is not CULDESAC or SUBMIT.
Let's separate the text of the button from the # Forward text instruction. The following # Recall instruction creates a &#NEXTBUTTON recall that outputs the button text:
Now, let's say that the questionnaire designer would prefer to have a button that says "Submit section 1" and the end of section 1. They could use the following instructions:
This solution assumes that ENDS1 is the question displayed at the end of Section 1. Any other condition could be used. Several conditions could be created as well as in this:
Adapting to mobile devices
You want to display questionnaires differently on (small) mobile devices.
Use conditionnally recalled values in # STYLESHEET and # TEMPLATE, and turn on # ACTIVATE THE MOBILE MODE.
Mobile devices are smart phones and tablets which can display questionnaire pages but offer much less screen space than typical computer displays. Displaying a questionnaire in a readable form on these devices requires formatting for the screen size and, sometimes, modifying the flow of the questionnaire.
The key ingredients are:
Displaying questions in random order
You want to display a set of questions in random order.
Use the PERMUTATION pound instruction.
One page of the CallWeb technical documentation explains the details of the PERMUTATION pound instruction.
Ending a questionnaire without an exit URL
You want to end a questionnaire without re-direction to an exit URL.
The # URL instruction defines a URL where respondents are redirected once they leave the questionnaire (i.e., once they attempt to go past the last question). In standard circumstances where there is no need to redirect to another site or elsewhere on the current site (and where respondent's workstations are set up to disallow URL redirection), it may be preferable to NOT use # URL and to display the Thank You page in a standard question.
One advantage of this approach is that there is no need to create static pages for the Thank You page; another one is that you are certain that the Thank You page will have the same appearance as the rest of the questionnaire.
Exiting to a different URL depending upon circumstances
You want to redirect the respondent to a different URL depending upon circumstances.
If a respondent goes beyond the last question in the questionnaire, the URL pound instruction is used to redirect the browser session to another URL (e.g., a client Web site, a thank-you page, etc.). Only one URL is allowed for each language of the questionnaire.
What if the respondent must be redirected to a different URL based on responses offered in the questionnaire (or prepopulated data)? Calculate a URL into an open-end part and recall it into the URL pound instruction, as in:
Remember that recalls in URL pound instructions must use the Perl recall syntax.
As for the calculation expression above, it uses the Perl condition testing function with a logical expression within the parentheses followed by the result of a "true" condition, a colon and the result for a "false" condition.
Suspending a questionnaire, then performing some task
You want the respondent to be able to suspend the questionnaire and you want the questionnaire to perform some action after that request.
The following code suspends the questionnaire and sends an e-mail to the respondent.
Sometimes, respondents want to stop filling out a questionnaire and resume later. This is easily achieved by re-using a link that was sent with an invitation to take part in the survey if access is password-protected (# Type enquete = 2). However, if access was open or if an access-code was provided upon entry, respondents are unlikely to have the information required to return.
In these circumstances, the Stop button can be displayed with a pound instruction like the following:
which activates the Stop button and instructs CallWeb to go directly to the INTERRUPT question once the button is clicked (clicking the Stop button also saves the data already provided on that page but does not validate them).
The interruption destination question can do anything. The following code shows how to request that an e-mail address be supplied and how to send a message containing a link back to the questionnaire — right where the interview left off.
Displaying a question only at the call centre
You want to display a question only to interviewers at the call centre.
Insert a display condition using the browser's computer IP address as in:
The situation is this: you are running a dual mode survey (concurrently on the Web and as a telephone survey) and you want some questions to be reserved to interviewers in the call centre (i.e., on the internal network as opposed to on the open Internet).
To do so, implement a display condition which identifies internal network IP addresses, such as the one above. A question's display condition is a logical expression describing the circumstances when the question is displayed. Make sure to use the right network address segment as the internal network may use a different addressing range.
In the example above, the logical expression must use Perl syntax in order to make use of the $contexte data — hence the use of the braces around the condition. The Perl "=~" operator means "contains" while the circumflex accent at the beginning of the pattern anchors the comparison to the beginning of the IP address.
Note that the same logic can be used to calculate a value (with a CALCUL question) dependent upon the IP address of the respondent's computer.
Displaying a question in only one language
You want to display a question only to respondents using a certain language.
Insert a display condition such as:
Say some text must be displayed only in French. Using the question's display condition (a logical expression describing the circumstances when the question is displayed) and the special operator LANGUAGE(), it is possible to zoom in on cases being filled out only in one language.
In a multi-language questionnaire, it would also be possible to identify more than one language using the following syntax:
The LANGUAGE() operator is synonym with LANGUE().
Testing that responses total 100%
You want to make sure that responses to a series of questions total 100% (or whatever other value).
Use a Test pound instruction as in:
The Test pound instruction can be used to implement any specialised logical test. In the one above, the test is triggered by receiving an answer to question Q1A. The test is then implemented: the message is displayed if the sum of four open-end parts is not equal to 100. The "type" parameter specifies that the error message should be delivered in the context of questions presented in a table format (assuming that the four open-end questions were displayed in this manner).
The test can be more or less complex. It can also use other questions in the questionnaire. For example, the following condition verifies whether the sum of four answers exceeds the value of another answer:
Changing to another language programmatically
In the course of a questionnaire, you want to change to another language based on an answer to a previous question.
Use a CALCUL question as in:
The $_lang system variable controls the language in which the questionnaire is displayed. It contains a two-letter code from the ISO classification of languages.
The CALCUL question above attributes "EN" to $_lang if Q1 equals 1 and "FR" otherwise. This scheme can be extended to more languages or other sources of data such as prepopulated information: if you have imported information on the respondent's preferred language of communication, you can use it in a similar CALCUL to adjust the language of the questionnaire accordingly.
Branching out to any URL
In the course of a questionnaire, you branch to a URL of your choosing, inside CallWeb or outside.
Use a GOTOURL question as in:
A GOTOURL question is used to calculate a URL that the questionnaire will branch to right away. The relevant options are placed in the question text segment of the question definition. Options are separated by commas. Relevant options are as follows:
The display condition is respected but simple skips are not since the "skip" will be to the calculated URL.
The value of the first response category is stored in the data base if the GOTOURL question was activated. Thus, at least one category is required.
Determining how many responses were given to a question
You want to find out how many answers a respondent has given to a certain question.
The n_selections function returns the number of selections a respondent has made in question QUESTION among the list of codes which follows the name of the question.
Determining how many responses were given to a series of questions
You want to find out how many answers a respondent has given to a series question.
The n_such function returns the number of selections a respondent has made in a list of questions among the list of codes which precedes the name of the question. In the example above, only answers with values 1, 4 and 9 are counted and only answers provided to questions lying between Q1 and Q10 (including these boundaries) are included. The syntax of the list of questions allows for ranges (as above) as well as individual question names and concatenations using commas as in: "Q1-Q5, Q7, Q8-Q10".
Adding a specialized function to CallWeb
You want to add a calculation capability that does not exist in CallWeb.
Create a Perl subroutine and put it in a file called cwplugins.pl alongside cwroutines.pl.
While CallWeb offers a wealth of functions and allows for any Perl expression as part of CALCULations, it is possible that one particular treatment may not be readily available in the CallWeb toolbox. You may extend CallWeb possibilities by programming a Perl function to do what you want and adding it to a file called "cwplugins.pl" which must be located in the same directory as cwroutines.pl. Note that the cwplugins.pl file must end with a line containing "1;" (i.e., compiling this library will return a "true" value).
Values from the questionnaire data stream can be passed to these functions and results returned to CallWeb variables through CALCUL questions as in the following example: Q3 = somefunction($Q1,$Q2) which provides the function named "somefunction" with the current values of questions Q1 and Q2, and store a result in question Q3.
Bringing data to a common unit
You want to bring to a common unit of measurement numeric data which was collected on different units.
There are two possibilities:
First, a note of clarification on the actual problem. This recipe discusses the situation where numeric data was collected (e.g., one's weight or one's salary) which requires a numeric value as well as a unit of measurement (e.g., inches vs. centimiters, dollars per hour, per week, per month), typically collected using a unit-type question. The questionnaire designer wants to calculate a unique value which will bring all responses to a common unit (e.g., centimiters or dollars per year).
If the unit multipliers are integers, it is possible to assign the multiplier as the category code for the unit and to simply multiply this code with the numeric value supplied. For example:
If the unit multipliers are not integers (a limitation of answer category codes), use a calculation and the ternary operator (the "implicit if" operator). For example:
Calculating time spent on a page
You want to calculate how long someone has spent on a certain page.
The first approach works well if you need only a few time counters or if you need to make reference to the time value within the questionnaire (e.g., to remind the respondent to take their time if they appear to be speeding through the questionnaire). It consists of adding two computed questions:
The second approach uses BASEclicks. Once the BASEclick project is compiled and available, CallWeb systematically stores the time when each page is shown in each project which include the "# BASEclicks = yes" instruction; CallWeb also stores the time lapsed since the previous entry for a given IP address in a given project. The data in the BASEclicks project can be analyzed using any and all of the existing CallWeb utility programs. Beware of the fact that this project grows quite rapidly to many thousands of records on a busy server.
Debugging a complex calculation
You need to determine why a calculation does not work.
Use "print" commands in an anonymous subroutine structure.
We realize this discussion is technical; so are complex calculations.
Most of the problems with complex calculations have to do with the absence of the dollar sign as prefix to variable names (required in the Perl context), with variables not being properly initialized and with improper mathematical syntax. Therefore, use the following debugging guidelines.
Recalculating a CALCUL question en masse
You need to recalculate a CALCUL question for several data records.
Use cwnav.cgi.
One of the features of cwnav.cgi is labelled "Update using a CALCUL question"; it is displayed only if the questionnaire includes CALCUL questions. Using the drop-down list, select which CALCUL questions to process and click the "Action!" button. On the follow-up page, insert the "UPDATE" (all caps) keyword to confirm the recalculation request. Only records which fit the case selection criteria at the top of the cwnav.cgi page are accessed. Only records which suit the CALCUL question display condition are affected; skips within the questionnaire are not taken into account. System variables (starting with an underscore) cannot be modified by this procedure.
Examples of use of this procedure include:
Verifying an e-mail address
You need to make sure that an e-mail address obtained in a questionnaire is valid.
Use the test_email_address function in a CALCUL question.
Getting e-mail addresses as part of Web data collection or telephone surveys is notoriously error-prone. The test_email_address function verifies whether a given e-mail address is (probably) deliverable. This function requires that the Net::Telnet Perl module be installed on the server. Calling this function is as simple as:
where ATEST is the open-end part that contains the result of the test and AEMAILADDRESS is the name of the open-end part containing the e-mail address.
The function returns one of the following codes. The CallWeb script can then branch to the appropriate next place in the questionnaire using habitual flow control tools like branching and display conditions.
Adding or subtracting time from a date-time value
You need to add or subtract time (seconds, minutes, hours, days) from a date-time value expressed as YYYYMMDD or YYYYMMDDHHMMSS.
Use the add_to_date function in a CALCUL question.
CallWeb stores dates as YYYYMMDD values and date-time as YYYYMMDDHHMMSS values. For example, $contexte{date} contains the current date formatted as YYYYMMDD (currently 20240418) and $contexte{dateheure} contains the current date and time formatted as YYYYMMDDHHMMSS (currently 20240418145548). While comparisons are easy, other manipulations of such values are not. Adding one day to 20091231 returns 20100101, for example. The add_to_date function helps with date and date-time calculations.
Here is the general syntax of the add_to_date function:
ADATE2 = add_to_date($ADATE1,value,type_of_offset)
So, add_to_date(20091231,1,"DAY") adds one day to New Year's Eve 2009 while add_to_date(20091231235959,1,"SECOND") moves from 2009 to 2010. Nevative values subtract from the stated date.
The result returned by add_to_date has the same length as the input value. Thus, adding less than one day to a YYYYMMDD value will return the same date value.
Drawing a data chart
You want to draw a chart from CallWeb data and insert it in a questionnaire.
Use the calc_graph function in a CALCUL question.
CallWeb uses the Highcharts JavaScript system to produce data charts. The calc_graph function provides an easy way to build these charts.
calc_graph can be callwed in a CALCUL question or in an <execute></execute> substitution. The call works this way:
Here is the list of available options; only one is mandatory.
Closing data collection at a particular date or time
You want to forbid access to a questionnaire past a certain date or time.
Use the "Deny access if" pound instruction as in:
The "Deny access if" pound instruction contains a logical condition which denies access to the questionnaire if it is true. This condition is tested every time callweb.cgi receives a request. In the example above, access is denied if the current date is June 12, 2006 or later. Other $contexte information can also be used to control access.
In the example above, the logical expression must use Perl syntax in order to make use of the $contexte data — hence the use of the braces around the condition.
The "Deny access if" pound instruction is the strongest barrier to entry since it is tested with every call to callweb.cgi. Alternative methods (using questionnaire logic) can only be implemented at specific places in the script.
This instruction displays system message 28 which is fully customizable, like any other system message.
Controlling access to a questionnaire by IP address
You want to allow access to a questionnaire only to workstations at a certain IP address.
The "Deny access if" pound instruction contains a logical condition which denies access to the questionnaire if it is true. This condition is tested every time callweb.cgi receives a request. In the example above, access is denied if the IP address of the computer accessing the questionnaire does not begin with 55.54.53. Other $contexte information can also be used to control access.
In the example above, the logical expression must use Perl syntax in order to make use of the $contexte data — hence the use of the braces around the condition. The Perl "!~" operator means "does not contain" while the circumflex accent at the beginning of the pattern anchors the comparison to the beginning of the IP address.
Closing access once a certain number of completed questionnaires is reached
You want to disallow access to a questionnaire once a certain number of questionnaires have been completed.
There are two solutions to this problem:
The purpose of a QUOTA question is to flow questionnaires to an alternate route (typically to a "Thanks anyway" page) once a certain number of questionnaires have been completed in a certain group (or in the entire field work). The syntax of QUOTA questions is fully explained in the CallWeb documentation. The key advantage of QUOTA questions compared to the second solution proposed below is that they are easily sensitive to complex situations and they are resource-efficient in that they are only tested when they are reached.
The second solution is to add a "Deny access if" pound instruction which returns "true" once a certain number of completed questionnaires are found in the data base. The syntax of this instruction could be as follows:
In this example, access to the questionnaire is denied when 300 cases have "QEND=1" in the data base of project "project_name". Of course, the WHERE clause could contain any legitimate MySQL expression based on the questions in the questionnaire. Note also that the expression is within braces since it is evaluated as a Perl expression. The main advantage of this solution is that it can block the project at any time while filling out the questionnaire since "Deny access if" is tested upon each call to callweb.cgi (hence the threshhold stated will not be exceeded); this is also the key weakness of this solution: the test (and the call to MySQL to count completed cases) is implemented often and could become a burden on the system if numbers grow very high. This instruction displays system message 28 which is fully customizable, like any other system message.
Giving restricted access to some individuals
You want to give restricted access to some projects and/or to some modules to some individuals.
Only the main utilities directory (defined in the CallWeb configuration file) has access to all projects. Even in the case of that directory, only modules that are available in it can be used (obviously).
Creating a secondary utilities directory offers the possibility of putting only the required modules in it, thereby restricting which actions users of that directory can take.
Also, only projects which explicitly list the secondary utilities directory (via the "Visible depuis" pound instruction) can be accessed from that directory.
Password-protecting a directory
You want to request a password to access a certain directory via a Web-browser.
Use the Apache .htaccess feature.
.htaccess files provide a way to make configuration changes on a per-directory basis. A file, containing one or more configuration directives, is placed in a particular document directory, and the directives apply to that directory, and all subdirectories thereof. Password protection is one of the features offered by .htaccess files.
Providing short links to questionnaires
You want to set up very short links to questionnaires.
Use the Apache RedirectMatch feature.
CallWeb's short URLs allow for compact URLs. The syntax of short URLs is as follows
While the first two pieces of information are mandatory (the language and the project), the telkey and the first question values are not; they should be supplied if the context requires it.
Once could dispense with the reference to callweb.cgi in the URL by including callweb.cgi in the list of allowed starting pages in the Apache configuration. This is done by adding a DirectoryIndex instruction to the Apache configuration file:
The short URL above would then become http://domain.ext?en:survey:123456:Q1.
Of course, one would be well advised to use a short domain name rather than a long one. For example, http://97.ca translates into necessarily shorter and easier to communicate URLs than http://surveycenter.yourpreferredsupplier.qc.ca.
Another strategy is to use Apache's RedirectMatch instruction; it is extremely powerful. It can rewrite a URL according to components of the address supplied. For example, the following accepts the URL http://callweb.ca/survey and redirects the user to the following address http://callweb.ca/productionsite/callweb.cgi?_lang=EN&_proj=thissurvey:
This instruction takes the URL http://callweb.ca/survey/en/abcd and redirects to http://callweb.ca/productionsite/callweb.cgi?_lang=en&_proj=thissurvey&_telkey=abcd:
Also, if no language parameter is passed along, CallWeb defaults to the browser preferred language (determined by the respondent), assuming it exists in the questionnaire. So, the RedirectMatch instruction above can be rewritten to avoid the language parameter altogether and to let CallWeb serve the respondent's preferred language:
This way http://callweb.ca/survey/abcd becomes http://callweb.ca/productionsite/callweb.cgi?_proj=thissurvey&_telkey=abcd and the respondent gets his/her preferred language if it exists (and the questionnaire default language if it does not).
Offering an "Unsubscribe" mechanism
You want to offer a method for respondents to unsubscribe from future invitation e-mails.
Add an "unsubscribe" calculation to the questionnaire and a link to it in invitation e-mail messages.
It might be counter-intuitive, but offering a way for potential respondents to exclude themselves from invitation e-mail messages is actually a good thing, for a number of reasons. Offering a way to unsubscribe:
Offering an unsubscribe mechanism is a two-step process. First, create a calculation and an information screen for this purpose in the questionnaire. Make sure that this section of the questionnaire is isolated from the rest of the questionnaire so that it is not reached in the normal course of filling out the questionnaire. For example:
The calculation can be anything that suits your needs. The solution above adds "_UNSUB" to the e-mail address so that the address is no longer deliverable. This has the advantage of keeping the address information for future reference (i.e., "email@domain.com" becomes "email@domain.com_UNSUB" but the original e-mail address is still accessible in this field) and of requiring no other intervention to avoid further mailings to this respondent.
Second, insert a link in the invitation messages to trigger that portion of the questionnaire. In the example above, the link could look like this:
This link opens the case referenced by the current _telkey, in project PROJECT, at question UNSUB, and displays the unsubscribe confirmation message.
A URL to the unsubscribe feature in the questionnaire can be supplied in the "list unsubscribe" option of cwemail.cgi and in the Autoemail pound instruction. Some Web mail system recognize this instruction and display a special button using the URL. A default value for "list unsubscribe" can be defined in the "list unsubscribe" pound instruction.
Setting up access with a modifiable password
You want questionnaire respondents to have the ability to change their login password.
Use the "Password" type of "# Survey Type".
The "Password" "# Survey Type" is the only one that allows CallWeb to distinguish between a user name and a password, and to extend respondents the ability to change their password at will. Several other instructions affect how this survey type works. All of these considerations are listed below.
[1] The _password field is available only after a new compilation. Existing projects compiled before the availability of this field can be converted simply by accessing each project using any of the administrative modules.
[2] I.e., when used, these regular expressions are surrounded by the characters "^" and "$". References to regular expressions: 1, 2
Two-factor authentication and more access control
You want to confirm that the respondent is the intended target for the questionnaire.
Use the "2FA" pound instruction.
When the 2FA pound instruction exists and has been validated by compilation, CallWeb allows response into the questionnaire only once a two-factor authentication cookie has been set. This is done by entering a randomly generated code in the CallWeb script; the code is generated by CallWeb and sent to the respondent's email address. Let's see the details starting with the syntax of # 2FA.
Here is a working example.
Controlled access on an open project
You want to constrain respondents to a single questionnaire but also want to allow unknown respondents in.
You could use # Control by cookie but another, maybe more robust, solution is to add a font-end project to control access.
The # Control by cookie activates a cookie-based control system which prevents the creation of a second questionnaire by a given computer. However, it is limited to fully open projects, works in callweb.cgi only, not in cwx.cgi, and is not immuned to users deleting the cookie or using a second browser to access more questionnaires.
The general solution proposed here is to create a small project (hereby called the gatekeeper) that is used to control access to the second, real questionnaire. The gatekeeper project requests the respondent's email address and emails a one-time use code to authenticate the user at that address. Once the code is correctly entered, the gatekeeper project looks up the _telkey corresponding to the email address in the second project or creates a record in the second project, and flows the respondent to that record in the main project. This can work even if the second project is closed. It can also allow or disallow access by email addresses that are not already prepopped into the second project.
Gatekeeper project
Here are example code snipplets.
Main project
The requirements of the main project are minimal.
E-mailing during the course of the questionnaire
You want to send an e-mail to the respondent during the course of the questionnaire.
Using a CALCUL question, it is possible to send an e-mail in the course of completing the questionnaire. The syntax of such a question is suggested above.
The "from_e-mail_address" and the "to_e-mail_address" can be written out — in which case the at-sign must be accompanied by a backslash as in "info\@callweb.ca" (a Perl requirement). They could also be questionnaire fields. In this case, the CALCUL syntax would look like this:
The "MESSAGETEXT_VARIABLE_NAME" must be defined in the questionnaire and be of the EMAIL type. See the section on invitation e-mails for information on this question type.
After execution, the "OUTPUT_VARIABLE_NAME" contains the date and time when the message was sent or zero if the process was not successful.
Creating an open-end part allowing only unique values
You want to create an open-end part which accepts only values which have not yet been recorded in the same field.
The following Test pound instruction offers this service:
Let's say one question requests a file number and that no two cases in the data base can refer to the same file number.
The Test instruction above is triggered by question ANUMBER before any data are recorded in the data base (thereby avoiding duplicate file number data). The trigger condition is key here:
Recording a Web completion as part of a dual-mode project
In the context of a dual-mode survey, you want self-completions done on the Web to be taken into consideration in CATI field management.
WEBCOMPLETED CALCUL## This question is located where the questionnaire is considered complete% Question _cetappel = &add_call_cetappel("WEB","Completed") ## The previous line MUST attribute the result to "_cetappel" ## The first argument is the name of the "interviewer" associated with the completed questionnaire. ## The second argument is the exact result code (see cwcodescati.cgi) to attribute to the "call".% Note% Categories% Skips% Condition # The line below must be adjusted to the IP address of CATI interviewers { $contexte{ip} !~ /192.168.1.1/ }% Open part! ####################################################################################################
CallWeb projects set up for management in CATI context are controlled by a series of specialized modules. Telephone numbers are fed to interviewers, among other factors, on the basis of the completion status of the questionnaire. When questionnaires are available for completion via CATI and via the open Web, questionnaires completed without CATI assistance must be identified.
This identification takes the form of a fake telephone call entry added to the call history of a case that is completed over the Web. This addition is performed by a CALCUL question using the add_call_cetappel function; the result must imperatively be sent to the _cetappel variable. The CALCUL function must bear a display condition to be activated only when the questionnaire is completed over the Web; one way to do this is to exclude from this calculation all of the IP addresses specific to the CATI environment.
Activating the CallWeb pretest mode and accepting respondent feedback
You want survey respondents to have the possibility to add comments about any question of the questionnaire.
Activate the CallWeb pretest mode using the PRETEST pound instruction.
In pretest mode, CallWeb displays a hyperlink besides each question in the questionnaire. This hyperlink is to a one-page questionnaire called BASEpretest which querries the respondent or the interviewer about the nature and the content of the comment they want to leave on a particular question. The link contains the name of the project from which BASEpretest was called, the _telkey of the originating questionnaire and the name of the question from which BASEpretest was called. These pieces of information are stored along with the respondent feedback in the BASEpretest data base for further analysis.
The CallWeb pretest mode is activated when:
The content of the PRETEST pound instruction is the text or HTML code that will be hyperlinked by CallWeb. It is what the respondent sees in the questionnaire. For example, the instruction
displays a hyperlinked icon next to each question. Meanwhile, the instruction
simply displays the word "Pretest" hyperlinked. Note that one PRETEST segment must exist for each language defined in the questionnaire, such as:
This system can be used to collect interviewers' or respondents' comments during a pretest or respondents' comments during a more qualitative survey.
Coding open-end questions
You want to code open-end questions into a numeric field.
Use cwnav.cgi along with the "Mass edit mode".
The cwnav.cgi module displays a selection of cases (based on the criteria supplied) and a subset of questions from the questionnaire. Its "Mass edit" mode makes every field that is displayed editable.
To code open-end questions, follow these steps:
A new page is displayed with the cases and fields you selected. Any change made to the data are saved when the Action! button is clicked again.
Managing a do-not-call list in CATI mode
You want to use a list of telephone numbers that you never want called, in a CATI context.
Use BASEdonotcall to store the telephone numbers.
If the BASEdonotcall project exists, CallWeb refuses to prepopulate cases corresponding to one of the telephone numbers found in that project; moreover, CallWeb does not dispatch numbers found in the do-not-call list to interviewers. This behaviour can be turned off on a project by project basis by adding the following pound instruction to the project questionnaire: # Use do not call list = no.
BASEdonotcall can store the following information:
Telephone numbers may be added to BASEdonotcall in three manners:
Naming the sending e-mail address
You want to send mass, customized e-mail messages and have a real name appear in the From field.
Format the sending e-mail address of cwemail.cgi as follows: Company Name <email@company.com>
If a simple e-mail address is used in the sending address field of cwemail.cgi, that simple address is displayed in the message From field at the receiving end. To have a more descriptive name appear in the From field instead, place that name left of the actual e-mail address and place "smaller than" and "greater than" signs on each side of the e-mail address.
Recording face-to-face interviews
You want to record face-to-face interviews using the computer microphone.
Create a CALCUL question to start and stop the recording using the record_wav function.
To record face-to-face interviews (typically conducted using laptops or netbooks), the CallWeb computer needs:
In CallWeb, recording is initiated by a CALCUL question calling upon the record_wav function as in:
This stops any current recording, starts the recording and places the name of the recording file in AFILENAME. A second CALCUL question is used to stop recording. It also uses the record_wav function:
The result of this call is to stop the recording and to return, in PROCESSES, the number of processes stopped.
The real difficulty in using this functionality is to give the Apache user the permission to start and stop recordings. This is done by adding lines such as the following ones to the /etc/sudoers file (assuming Apache runs under the "apache" user and that "someuser" has access to the rec and kill commands):
Finally, add a line such as this one to the CallWeb usagerXXX.conf file; it contains the Linux prefix instructions necessary for the Apache user to issue the rec and kill commands):
Pushing data into a project during an interview
You want to place data or create a record in another project during an interview.
Create a CALCUL question using the put_values_in_case function.
Say you need to store events in an event-based CallWeb data base and you don't want to use a RELATION question (for example, you want to store purchases made by the respondent the previous day). You need a way to ask a few questions, store these answers in another project (purchase-based) and loop back to the purchase questions. The looping back is performed by a skip, but the storing is trickier; the put_values_in_case function performs it.
The syntax of put_values_in_case is as follows:
The function put_values_in_case places the content of AQMx in the AQ1 field of the record identified by the _telkey "some_telkey" of project "some_project", as well as the content of QNy in Q3 and the content of QOz in Q10. There can be any number of such transfers in a single use of the function.The destination project is specified in the "_proj" key. Note that the function uses the Perl attribution operator (=>), NOT the equal sign.
If the data needs to be pushed into a particular record of that project, the _telkey of that record must be specified in the _telkey key. If no _telkey key is specified, put_values_in_case creates a new record in the destination project. If the destination _telkey is specified but does not exist, the function creates a record with that _telkey in the destination project. If a record needs to be created, the new _telkey contains 24 random letters and numbers unless a _pattern option was passed to the function; the _pattern option follows the "# Telkey pattern" syntax.
put_values_in_case returns the _telkey that was affected (i.e., the existing _telkey that was updated or the _telkey of the record that was created).
Identifying straightlining
You want to identify straightlining behaviour and you want to react to it.
Use the straightlined function in a Test pound instruction.
Straightlining occurs when a survey respondent selects the same answer to a series of questions. Typically, this is seen as a way to speed through a questionnaire, not answering thoughtfully.
The straightlined function can test for this behavour and, coupled with the # TEST instruction, it can react to it. This function is used as follows:
It returns a 1 if all answers from Qn to Qm are the same while disregarding empty answers (which would correspond to false display conditions, for example). QFLAG is an optional question name; if it is present two things happen:
Couple this with a # TEST instruction and you get something like this:
That will display the error message and the matrix (presumably) if the respondent straightlined Q1 to Q10. If the respondent moves on without changing their answers, CallWeb accepts the straightlined answers because QFLAG contains a 1, thanks to the first test performed.
Preparing a large prepop
You need to prepop a large number of data fields. You want to simplify the task and reduce the risk of human error.
Use cwprepop.cgi to create a template of the questions to include in your .scw file based on the data to prepop.
Let's say you received a data file of administrative data that need to be included in a survey data base — maybe to recall some administrative values in the questionnaire or to produce an integrated data base at the end of the data collection to ease the statistical analysis. There are dozens (or hundreds!) of data fields, some open-ended, some simply categorised. There are alphanumeric fields and numeric fields, and among the latter, there are integer fields and real values. What a mess! It is going to take hours to craft the CallWeb questions to properly prepop all of these data, and there is a clear risk that some data will be given an incorrect data type.
Call cwprepop.cgi to the rescue. From the integrated module (cw.cgi), cwprepop.cgi can be instructed to "create an scw from a tab-delimited". Here is what you have to do:
Here is what CallWeb will do:
Review the .scw file that is produced to make sure that CallWeb's assumptions and observations are reasonable in your own situation.
Commenting a questionnaire
You need add comments to the questionnaire but you want to hide them easily.
Use a custom CSS style and the CSS "display" property.
CallWeb includes the ability to place comments inside a script but some may find it less flexible than they would want.
You can also use CSS styles to your benefit in this case. For example, you can create a COMMENT style in the project style.css file (or other style sheet name) that could like like this:
Then, comments can be inserted in the questionnaire using SPAN tags, such as:
This comment will appear in a text box formatted across the page when the questionnaire is displayed, including in Print mode. When the comments are no longer necessary, simply change the COMMENT style from display: block; to display: none; and comments will magically disappear.