Desktop User Guides > Professional > Interview scripting > Writing interview scripts > Scriptwriting rules and recommendations
 
Scriptwriting rules and recommendations
Rules
Do not waste time laying out statements in the metadata section in a particular way if you are using UNICOM Intelligence Professional. The mrScriptMetadata DSC that reads the .mdd file reformats the metadata section each time it opens a file.
the example scripts and script snippets in the interview scripting documentation do not always use this layout, because of screen and printing constraints.
Statements in the routing section can be split across lines as long as each line except the last ends with an underscore symbol.
Unless unbounded, or containing a very large number of iterations, include the expand keyword in all grid and loop definitions.
Recommendations
In the metadata section
Make questions and information items names reflect the purpose or content of the question or item.
Avoid names that are the same as or very similar to interview scripting keywords.
Give questions and other items mixed case names.
Define unique response names for the special Other, No Answer, Don't Know, and Refuse to Answer responses rather than using a dash (–). Using a dash generates a response whose name matches the special keyword, and this is unlikely to be unique within the script as a whole.
Do not include special responses such as other or na, or response ordering keywords such as rot or ran in shared lists unless you want these responses and settings to apply every time the list is used.
In multiple-language projects, use info statements in the metadata section of the script for all non-question text that requires translation. Any text that exists in the routing section cannot be translated and will always be displayed in the questionnaire’s base language. This is particularly important for error messages.
When setting Initial text that should be replaced, put the text in brackets; for example, “<Type your answer here>”.
If style, validation, and response constants apply to all routing contexts, set them in the metadata section. For example, default values for questions typically apply to all routing contexts, so should be set in the metadata section. Conversely, some styles might only apply to a specific routing context, so should be set in that context in the routing section.
In scripts that copy information between the interview and the sample record, use similar names for questions and sample fields that are related. For example, if the Age sample field contains the participant’s age you might call the question in the script QAge. Alternatively, you might decide to give all sample fields names that start with “Sample”, so the Age question in the script could refer to the SampleAge field in the sample record. If your script has sample quotas, you might want to use identical names for questions and sample fields. For more information, see Sample quotas when you have No Access to the sample database.
In the routing section
Use indentation and blank lines to make the structure of the section easy to follow. (When working in UNICOM Intelligence Professional, you can indent using tabs, but select the option that converts tabs to spaces to ensure a smooth transition to other tools.)
Give temporary (Dim’d) variables lower-case names and place these declarations at the top of the routing section.
Give constants uppercase names.
Use comments to explain complex code blocks: see Comments.
Use { } (braces) to refer to categorical values, instead of enclosing values in double quotation marks; for example:
Q1.Categories[{Other}]
Use category names instead of indexes when referring to categorical values as this makes scripts easier to read. For example, in a loop called Color in which red is the fourth item in the loop control list, use If (Color[{Red}].Likes.Response = {No}) rather than If (Color[3].Likes.Response = {No}) to test whether the respondent likes red.
Use If...Then with End If, instead of using the single-line syntax.
Start all label names (used for Gotos) in column 1.
Start Sub and Function declarations and Ends in column 1, but indent their contents.
It is easy to create infinite loops when using Goto, so consider replacing Goto with an If statement with the appropriate condition. For example, instead of:
If Q1 = {Yes} Then Goto Q3X
Q2.Ask()
Goto Q4X
Q3X:
  Q3.Ask()
Q4X:
  Q4.Ask()
use:
If Q1 <> {Yes} Then
  Q2.Ask()
Else
  Q3.Ask()
End If
Q4.Ask()
Always type object references in full. Object references can often be abbreviated by assuming that default properties will be used wherever a property name is not defined. However, this is bad programming practice and should be avoided, especially when writing interview scripts that other people might work on. For example, use Q1.Response.Categories[{Other}] instead of Q1.Response.Other since the latter can be used only if the category is named Other. The exceptions are Item and Value so, for example, use Q1.Response rather than Q1.Response.Value.
Put JavaScript in separate .js files and use an <mrRef> tag to include them in the template file.
When writing a loop where the number of repetitions is controlled by the response to a numeric question, define the number of repetitions in full rather than using the abbreviated [..variable_name] notation. Loops defined using this notation use a lower boundary of 0 which means that the loop will be repeated one more time than you will normally require. In addition, always ensure that you define a start value that is greater than 0 otherwise the same problem will arise. For example, if you write:
PersonLoop.QuestionFilter = NumPeople
PersonLoop.Ask()
and PersonLoop is defined in the metadata section as either:
PersonLoop loop [..8] fields
(
  ...
)
or:
PersonLoop loop [0..8] fields
(
  ...
)
the loop will be repeated for 0 to NumPeople times, which is one more time than required.
Efficient scripting
Try to define default styles in a template or cascading stylesheet rather than using the DefaultStyles object.
When a series of routing statements applies to the same object, for example, you are defining a number of styles for the same question, use a With statement to define the portion of code that is common to all statements rather than typing each statement in full. For example:
With Sports.Label.Style.Font
  .Family = "'Palatino Linotype'
  'Times New Roman'"
  .Size = 16
  .Effects = FontEffects.feBold + FontEffects.feItalic
End With
rather than:
Sports.Label.Style.Font.Family = "'Palatino Linotype'
    'Times New Roman'"
Sports.Label.Style.Font.Size = 16
Sports.Label.Style.Font.Effects = FontEffects.feBold + FontEffects.feItalic
Use a single For Each statement rather than [..] when setting more than one property in a collection, as each instance of [..] is equivalent to a For Each statement. For example:
Dim ThisCat
For Each ThisCat in Sports.Categories
  With Category.Style
    .ImagePosition = ImagePositions.ipImageOnly
    .ElementAlign = ElementAlignments.eaRight
    Width = 100
  End With
Next
is more efficient than:
Sports.Categories[..].Style.ImagePosition =
  ImagePositions.ipImageOnly
Sports.Categories[..].Style.ElementAlign =
  ElementAlignments.eaRight
Sports.Categories[..].Style.Width = 100
Use custom validation functions to avoid using Goto statements. For example, in the following example, a Goto statement is used to re-ask the question if the constant sum validation fails:
ConstantSumQuestion:
  ConstantSumGrid.Ask()

  Dim category
  total
  total = 0
  For Each category in ConstantSumGrid
    total = total + category.Item[0].Response
  Next
  If total <> 100 Then
    ConstantSumGrid.Errors.AddNew("ConstantSumError", _
      IOM.Questions.ConstantSumError.Label)
    Goto ConstantSumQuestion
  End If
Not only does this approach make the script more difficult to follow and maintain, each attempt to answer the question is stored in the interview history, requiring the respondent to press the Previous navigation button multiple times if backing up in the survey after making multiple attempts to answer the question. The recommended approach is to use custom validation functions. For example, in the following example the constant sum validation is implemented as a custom validation function:
ConstantSumGrid.Validation.Function =
    "ValidateConstantSumGrid"
  ConstantSumGrid.Ask()

Function ValidateConstantSumGrid(Question, IOM, Attempt)
  Dim category,   total
  For Each category in Question
    total = total + category.Item[0].Response
  Next
  If total <> 100 Then
    Question.Errors.AddNew("ConstantSumError", _
      IOM.Questions.ConstantSumError.Label)
    ValidateConstantSumGrid = False
  Else
    ValidateConstantSumGrid = True
  End If
End Function
The use of custom validation function also makes it easier to reuse validation logic across multiple questions.
Use Enums instead of numeric values or string literals to improve readability. For example, use TerminateStatus.tsScriptStopped instead of 1.
The type definitions for the IOM and MDM libraries are registered for use within interview scripts. For Enums from libraries that are not registered, either define a Constant within the script, or use a comment to indicate the enum name. For example:
.Properties["Autocommit Isolation Levels"] = 256 ' READ_UNCOMMITTED
Do not ask questions within Functions or Subs as restarts will not work correctly if they were to occur on the following question; the script in the Function or Sub after the ask statement will not be executed. The only exception to this rule is when the .Ask() statement is the last statement executed in a Sub; only a single question or page can be asked. For example, the following is acceptable:
Sub AskPromotionQ(IOM)

Dim score
  score = IOM.Questions.FinalScore

  If score >= 100 Then
    IOM.Questions.PromotionHigh.Ask()
  ElseIf score >= 50 And score < 100 Then
    IOM.Questions.PromotionMedium.Ask()
  Else
    IOM.Questions.PromotionLow.Ask()
  End If
End Sub
But the following example will not restart correctly on timeout and restart:
Sub AskPromotionQs(IOM)

Dim score
  score = IOM.Questions.FinalScore

  If score >= 100 Then
    IOM.Questions.PromotionHigh.Ask()
  ElseIf score >= 50 And score < 100 Then
    IOM.Questions.PromotionMedium.Ask()
  Else
    IOM.Questions.PromotionLow.Ask()
  End If

  ' NOT recommended. Only a single Ask statement
  ' can appear in a sub-routine and it must be the
  ' last statement executed
  IOM.Questions.PromotionFinal.Ask()
End Sub
It might be easier to never include Ask statements in sub-routines.
When MyQuestion is a variable (not known in advance), use name-based look-up into collections to avoid linear searches:
IOM.Questions[MyQuestion].Ask()
Most collections in UNICOM Intelligence support name-based look-up. If “Q1” is a defined question, use Q1.Ask(), because it is always faster than IOM.Questions[“Q1”].Ask().
Use IsNullObject to check if an object is NULL. Comparing an object to NULL will return True if the default property of the object returns NULL. Using IsNullObject checks to see if the object is NULL without expanding the default property. For more information, see IsNullObject.
To search for an object in a collection: Instead of using the following:
Function CheckIfQuestionExists(IOM, questionName)
  Dim i
  For i=0 to IOM.Questions.Count-1
    If LCase(CText(IOM.Questions[i].QuestionName)) =           LCase(questionName) Then
      CheckIfQuestionExists = True
      Exit Function
    End If
  Next
  CheckIfQuestionExists = False
End Function
...use this instead:
Function CheckIfQuestionExists(IOM, questionName)
  ' FindItem returns NULL if the question doesn't exist
  ' Return True if something other than NULL was returned
  Dim question
  Set question = FindItem(IOM.Questions, questionName)
  CheckIfQuestionExists = Not(IsNullObject(question))
End Function
Use FindItem to search for an item in a collection that might or might not exist. FindItem uses name-based look-up into collections. For more information, see FindItem.
Templates
Templates should meet XHTML standards; in particular, all tags should be written in lower case and each opening tag should have a closing tag.
Default templates should use a cascading style sheet.
Default templates should use relative fonts.
Define layout characteristics as styles in the metadata section rather than in the routing section or in templates if you want them to carry forward into other products such as UNICOM Intelligence Reporter - Survey Tabulation or UNICOM Intelligence Reporter.
See also
Writing interview scripts