To simplify migration from Open Data Kit (ODK) or compatible systems (Survey123, SurveyCTO, KoBoToolBox, etc) we present the possible/recommended alternatives in Survey Solutions for:
Individual system implementation of these features may vary. Online documentation for ODK was used as a reference when compiling the list of alternatives (accessed August 2021). For specifics of function implementation in different systems, refer to their respective documentation pages, e.g. for Survey123, for SurveyCTO, etc.
Where we leave a note "#no alternative", this is to be interpreted that there is no 1:1 mapping of an ODK feature to a corresponding Survey Solutions feature, and no obvious workaround. Yet this is usually not a dead end, and a workaround may be formulated for a particular situation.
Please note the basic correspondence between the questionnaire elements:
ODK, SurveyCTO, Survey123, KoBoToolBox | Survey Solutions |
---|---|
Group | Section or sub-section |
Repeated group | Roster |
Question | Question |
Calculate | Variable |
Note | Static text |
Relevance | Enabling condition |
Constraint | Validation |
Question types
The following table follows the list of question types present in ODK and compatible systems (SurveyCTO, Survey123, KoBoToolBox, etc) and shows a possible equivalent or alternative in Survey Solutions.
ODK, SurveyCTO, Survey123, KoBoToolBox | Survey Solutions |
---|---|
Text widget | Text question |
Number widget | Numeric question |
Date and time widget | Date question. For time use a formatted string question, with a pattern such as "##:##" |
Select widget | Single-select question, multi-select question Select questions have several presentation alternatives in XForms and likewise in Survey Solutions. Selection from an external file is not supported in Survey Solutions - all possible alternatives must be part of the questionnaire. |
Rank widget | Multi-select question |
Location widget | GPS location question |
Image widget | Image question |
Audio widget | Audio question |
File upload widget | #no alternative. Existing images may be uploaded as files using an image question. |
Barcode widget | Barcode question |
Range widget | Single-select or numeric questions |
Note widget | Static text |
URL widget | Static text Read more about hyperlinks |
Printer widget | #no equivalent |
Trigger/acknowledge widget | Categorical single-select question with condition on the remaining part of the questionnaire. |
signature widget | Image question with signature flag. |
Grouping multiple widgets on the same screen | Questions can be placed in the same section/sub-section, to appear on the same screen, or placed in different sections/sub-sections to appear on the different screens. |
Grid of selects on the same screen | See matrix presentation of categorical questions (CAWI only). |
Hidden questions | Questions of most types can be marked as hidden. See, Designer limitations by question type |
Operators
The following table follows the list of operators present in ODK and compatible systems (SurveyCTO, Survey123, KoBoToolBox, etc) and shows a possible equivalent or alternative in Survey Solutions.
ODK, SurveyCTO, Survey123, KoBoToolBox | Survey Solutions |
---|---|
▶ Math operators | |
+ (addition) | + (addition) |
- (subtraction) | - (subtraction) |
* (mutliplication) | * (multiplication) |
div (division) | / (division) |
mod (modulo (division remainder)) | % (modulo (division remainder)) |
▶ Comparison operators Note that relational operators in C# used in Survey Solutions can also be used to compare strings. | |
= (equal to) | == (equal to) |
!= (not equal to) | != (not equal to) |
> (greater than) | > (greater than) |
>= (greater than or equal) | >= (greater than or equal) |
< (less than) | < (less than) |
<= (less than or equal) | <= (less than or equal) |
▶ Boolean operators | |
and | && |
or | || |
▶ Path operators | |
. | self |
.. | #no direct equivalent. Use @rowcode or @rowindex depending on the context. For example: MEMBERS[2].@rowcode is identical to 2 (if such a member was included in the roster MEMBERS) |
Functions
The following table follows the list of functions present in ODK and compatible systems (SurveyCTO, Survey123, KoBoToolBox, etc) and shows a possible equivalent or alternative in Survey Solutions.
Some functions have several possible alternatives, and the exact choice may depend on the context.
ODK, SurveyCTO, Survey123, KoBoToolBox | Survey Solutions | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
▶ Control Flow | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if(expression, then, else) | expression ? then : else | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
position(xpath) | @rowindex - returns the position of the current roster item in its parent roster. It can also be used with non-current objects: MEMBERS[fatherid].@rowindex returns the index of the row of the person with the code equal to fatherid in the roster ROSTER. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
once(expression) | #no equivalent. An expression in Survey Solutions may not be calculated only once, instead the system must be able to recalculate any expression on demand. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
▶ Accessing response values | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
▸▸ Select questions | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
selected(space_delimited_array, string) | space_delimited_array.split().Contains(string) - literally; Q.Contains(c) - for checking whether a code c is selected in multiple-select question Q (since the answerto multiple-select questions in Survey Solutions is an array of selected codes). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
selected-at(space_delimited_array, n) | space_delimited_array.split()[n] - literally; Q[n]==c - for checking whether a code c is selected as choice n in multiple-select question Q (since the answerto multiple-select questions in Survey Solutions is an array of selected codes). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
count-selected(multi_select_question) | multi_select_question.Length | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
jr:choice-name(choice_name, 'select_question') | #no equivalent. Access to localized strings in syntax has been intentionally disabled. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
▸▸ Repeat groups | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
indexed-repeat(name, group, i[, sub_grp, sub_i[, sub_sub_grp, sub_sub_i]]) | group[i].name | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
count(nodeset) | nodeset.Count() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
count-non-empty(nodeset) | ROSTER.Count(x=>IsAnswered(x.q)) - the Survey Solutions equivalent counts the number of answered questions q in the roster ROSTER. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
sum(nodeset) | ROSTER.Sum(x=>x.q) - the Survey Solutions equivalent sums the values of answers to questions q in the roster ROSTER. Empty values (unanswered questions) do not affect the calculation of the sum (are ignored). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
max(nodeset) | ROSTER.Max(x=>x.q)- the Survey Solutions equivalent returns the maximum of values of answers to questions q in the roster ROSTER. Empty values (unanswered questions) are ignored. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
min(nodeset) | ROSTER.Min(x=>x.q)- the Survey Solutions equivalent returns the minimum of values of answers to questions q in the roster ROSTER. Empty values (unanswered questions) are ignored. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
▶ Strings | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
▸▸ Searching and matching strings | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
regex(string, expression) | new System.Text.RegularExpressions.Regex(expression).Match(string) - note that the regular expression in Survey Solutions will not be the same as in ODK-based systems and needs to be transformed. Example: find the first 4-digit number in the string s: new System.Text.RegularExpressions.Regex(@"\b\d{4}\b").Match(s).ToString() For the value of s="19 April 1980 - 16 November 2021" returns "1980". | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
contains(string, substring) | string.Contains(substring) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
starts-with(string, substring) | string.StartsWith(substring) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
ends-with(string, substring) | string.EndsWith(substring) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
substr(string, start[, end]) | string.Substring(start, length) Note: (length=end-start+1) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
substring-before(string, target) | string.Contains(target) ? string.Substring(0, IndexOf(target)) : String.Empty | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
substring-after(string, target) | string.Contains(target) ? string.Substring(IndexOf(target)+target.Length) : String.Empty | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
translate(string, fromchars, tochars) | string.Replace(fromchars,tochars) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
string-length(string) | string.Length | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
normalize-space(string) | string.StrTrim() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
▸▸ Combining strings | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
concat(arg [, arg1 [, arg2 [, argk [...]]]]) | String.Concat(arg1,arg2,...,argk) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
join(separator, nodeset) | String.Join(separator,ROSTER.Select(x=>x.q)) - concatenates answers to question q of roster ROSTER. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
▸▸ Converting to and from strings | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
boolean-from-string(string) | string.InList("true","1") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
string(arg) | arg.ToString() - objects of some types may require specification of a format or may use a default format that depends on the system. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
▶ Mathematics | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
▸▸ Number handling | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
round(number, places) | Math.Round(number, places) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
int(number) | (int)(Math.Truncate(number) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
number(arg) | Double.Parse(arg) for decimals, (arg==true)?1:0 for Boolean values | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
digest() | #no equivalent Formally digest algorithms are implemented in System.Security.Cryptography, such as System.Security.Cryptography.MD5(), but access to these classes has been disabled for security reasons. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
▸▸ Calculation | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
pow(number, power)Math.Pow(number, power) | log(number) | Math.Log(number) | log10(number) | Math.Log10(number) | abs(number) | Math.Abs(number) | sin(number) | Math.Sin(number) | cos(number) | Math.Cos(number) | tan(number) | Math.Tan(number) | asin(number) | Math.Asin(number) | acos(number) | Math.Acos(number) | atan(number) | Math.Atan(number) | atan2(number) | Math.Atan2(number) | sqrt(number) | Math.Sqrt(number) | exp(x) | Math.Exp(x) | exp10(x) | Math.Pow(10,x) | pi() | Math.PI | ▶ Date and time | today() | #no equivalent. | Access to DateTime.Now() has been disabled to avoid logical mistakes. now() | #no equivalent. | Access to DateTime.Now() has been disabled to avoid logical mistakes. Use a current timestamp question at the beginning of the interview. Refer to that date/time when you need the reference time. ▸▸ Converting dates and times | decimal-date-time(dt) | dt.Subtract(DateTime.Parse("01-Jan-1970")).Days | date(days) | new DateTime(1970, 1, 1, 0, 0, 0, 0).AddDays(days).Date | decimal-time(t) | Survey Solutions doesn't have a separate time class. If you separately have HOUR, MIN, SEC of the value oft, then decimal-time(time) is new TimeSpan(HOUR,MIN,SEC) / (24*60*60) . | ▸▸ Formatting dates and times for display | format-date(date, format), format-date-time(dateTime, format) refer to
Standard date and time format strings
and Custom date and time format strings. | Example: new DateTime(1970, 4, 19, 13, 45, 06, 200).ToString(“dd MMM HH:mm:ss”) produces 19 Apr 13:45:06 ▶ Geography | area(nodeset | geoshape) | #no equivalent. | distance(nodeset | geoshape | geotrace) | #no equivalent. | For distance between two specific locations A and B use Survey Solutions function A.GpsDistance(B) or A.GpsDistanceKm(B). ▶ Utility | random() | Quest.IRnd() - returns a single random number, | new Random() - create a new random number generator object. randomize() | #no equivalent. | For randomization refer to examples in the public questionnaires. uuid([length]) | Guid.NewGuid().ToString("D") | #no alternative to generating a UUID of a specified length. useful link. boolean(arg) | (arg!=0) for numbers; | (arg!="") for strings (see also function IsNullOrEmpty()); etc for other types. not(arg) | !arg | coalesce(arg1, arg2) | arg1??arg2 | checklist(min, max, response1[, response2[, responsek[, ...]]]) | new []{response1,response2,...,responsek}.Count(x=>x>min && x<=max) | weighted-checklist(min, max, reponse, weight[, response, weight[, response, weight[, response, weight[, ... ]]]) | (description) ROSTER.Where(x=>x.response=="yes").Sum(x=>x.weight).InRange(min,max) | |
If you notice any incorrect mapping in this table, feel free to raise the issue in the users’ forum.