-
Notifications
You must be signed in to change notification settings - Fork 11
[DELETE] HowTo Form JSON
The JSON layout provided must be an array, i.e. wrapped in []
, where each item in the array is an object, i.e. wrapped in {}
. We recommend that you use a text editor like VS Code that automatically checks for syntax errors.
Each item must have a type
attribute. The following types are allowed:
- checkbox
- counter
- counterallownegative
- badcounter
- textblock
- slider
- timeslider
- multiselect
- derived
- spacer
- h2
- h3
spacer: This one simply inserts a spacer at this point in the form. It DOES NOT require an id
or label
. Example:
{
"type": "spacer"
}
h2: This is a header element. It DOES NOT require an id
but DOES require a label
attribute, containing the text to display. Example:
{
"type": "h2",
"label": "Autonomous"
}
h3: This is a subheader element. It DOES NOT require an id
but DOES require a label
attribute, containing the text to display. Example:
{
"type": "h3",
"label": "Upper Hub"
}
checkbox: This is a checkbox, i.e. a yes/no question. It DOES require a unique id
in camelCase, and requires a label
attribute, containing the question. Example:
{
"type": "checkbox",
"label": "Did they taxi (move out from the tarmac)?",
"id": "didTaxi"
}
counter: This is a counter, i.e. a number that can be increased or decreased which cannot go below zero. It DOES require a unique id
in camelCase, and requires a label
attribute, containing the question. Example:
{
"type": "counter",
"label": "Cargo scored by robot",
"id": "autoHighScored"
}
counterallownegative: This is a counter, i.e. a number that can be increased or decreased and CAN go below zero. An example for when you may allow it to go below zero is if a robot moves a game piece away from where it needs to rest to score points, effectively un-scoring a point. It DOES require a unique id
in camelCase, and requires a label
attribute, containing the question. Example:
{
"type": "counterallownegative",
"label": "Cargo scored by robot",
"id": "autoHighScored"
}
textblock: This is a text block, i.e. a multi-line text input. It DOES require a unique id
in camelCase, and requires a label
attribute, containing the question. Example:
{
"type": "textblock",
"label": "If you had to pick a favorite FRC team other than the team you are on which would it be and why?",
"id": "iceBreakerAnswer"
},
slider: This is a slider, i.e. a number input that can be adjusted by dragging a slider. It DOES require a unique id
in camelCase, and requires a label
attribute, containing the question. It also requires an options
attribute, containing min
, max
, and step
. step
is allowed to be negative, in which case, the slider will be reversed. Example:
{
"type": "slider",
"label": "How would you rate this robot's defensive ability?",
"id": "autoPoints",
"options": {
"min": 0,
"max": 5,
"step": 1
}
}
timeslider: This is a slider, i.e. a number input that can be adjusted by dragging a slider. Timesliders display in minutes and seconds instead of just a number. It DOES require a unique id
in camelCase, and requires a label
attribute, containing the question. It also requires an options
attribute, containing min
, max
, and step
. Example:
{
"type": "timeslider",
"label": "How much time was on the clock when they started balancing?",
"id": "chargingTimeStart",
"options": {
"min": 0,
"max": 90,
"step": -5
}
}
multiselect: This is a multi-select, i.e. a dropdown question with multiple possible answers to pick between. It DOES require a unique id
in camelCase, and requires a label
attribute, containing the question. It also requires an options
attribute, containing an array of strings. Example:
{
"type": "multiselect",
"label": "Charging station status",
"options": [
"N/A",
"Docked",
"Engaged"
],
"id": "autoChargingStation"
}
derived: This is a derived value, i.e. a value that is calculated based on other values. It DOES require a unique id
in camelCase, but does NOT require a label. It requires an operations
attribute, containing an array of operations. Speaking of...
Each operation must have an operator
attribute, which is a string. All operators EXCEPT multiselect must have an operands
attribute, which is an array.
The operator
attribute can be one of the following:
-
sum
: Sums all the operands. Operands: [a, b, c, ...], where each can be either a number, a reference to an intermediate variable earlier in the chain of operations (a string starting with$
), or theid
of another item in the form layout. -
multiply
: Multiplies all the operands together. Operands: [a, b, c, ...], where each can be either a number, a reference to an intermediate variable earlier in the chain of operations (a string starting with$
), or theid
of another item in the form layout. -
subtract
: Subtracts the second operand from the first. Operands: [minuend, subtrahend], where each can be either a number, a reference to an intermediate variable earlier in the chain of operations (a string starting with$
), or theid
of another item in the form layout. -
divide
: Divides the first operand by the second. Operands: [dividend, divisor], where each can be either a number, a reference to an intermediate variable earlier in the chain of operations (a string starting with$
), or theid
of another item in the form layout. If the divisor is zero, then the result will be zero, which is not mathematically accurate but results in more helpful metrics. -
min
ormax
: Returns the minimum or maximum of the two operands. Operands: [a, b], where each can be either a number, a reference to an intermediate variable earlier in the chain of operations (a string starting with$
), or theid
of another item in the form layout. -
log
: Returns the logarithm of the first operand with base of the second. Operands: [x, base], where base must be a number, but x can be either a number, a reference to an intermediate variable earlier in the chain of operations (a string starting with$
), or theid
of another item in the form layout. -
abs
: Returns the absolute value of the operand. Operands: [x], where x can be either a number, a reference to an intermediate variable earlier in the chain of operations (a string starting with$
), or theid
of another item in the form layout. -
condition
: Returns the second operand if the first operand is true, or the third operand if the first operand is false. Operands: [boolean, valueIfTrue, valueIfFalse], where the first operand must be a reference to an intermediate variable earlier in the chain of operations or theid
of another item in the form layout; and the second and third operands can be either a number, a reference to an intermediate variable in the chain of operations, or theid
of another item in the form layout. -
gt
(greater than),gte
(greater than or equal),lt
(less than),lte
(less than or equal),eq
(equal),ne
(not equal): Returns 1 if the comparison is true, or 0 if the comparison is false. Operands: [a, b], where each can be either a number, a reference to an intermediate variable earlier in the chain of operations (a string starting with$
), or theid
of another item in the form layout. If used with acondition
operation further down the chain, the 1 will be treated as true and 0 treated as false. -
multiselect
: Since themultiselect
form option gives a list of strings, and you can't do math with strings, this derived operation lets you apply a numerical value to a given multiselect option. Instead of anoperands
attribute, the multiselect requires aquantifiers
attribute, which is an object. In the earlier example where charging station status has the options["N/A", "Docked", and "Engaged"]
, the associatedquantifiers
attribute would be"quantifiers": { "N/A": 0, "Docked": 8, "Engaged": 12 }
in order to attach point values to the 2023 game's charging station.
The result of the LAST operation in the chain will be the final result that endss up in the metric. All preceeding operations MUST have an as
field, which will let it be referenced by operands later in the chain.
In operations that expect a number (most operations), a true/false (e.g. the result of a checkbox) will be treated as 1 and 0, respectively.
Important: You SHOULD NOT fetch the final value of OTHER derived metrics inside the operands of a derived metric (i.e. you can only fetch the values of a checkbox, counter, slider, multiselect, etc). It would be computationally (and programmatically) complex to track the dependencies of certain derived metrics on others, and it takes nanoseconds to calculate each step in the operation chain, so the operations inside a derived metric should be "self contained", i.e. not depend on others.
Let's break down a relatively simple derived metric. For the 2022 and 2023 seasons, Team 102 created a metric called "reliability factor" which gives an at-a-glance view of the robot's overall reliability when you look at their average scores. 102's form included a checkbox with the id "diedDuringMatch" and a checkbox with the id "recoveredFromFreeze", whose meanings should be self explanatory. If the robot DID NOT die, the metric should be 1, if they died WITHOUT freezing, the metric should be 0, and if they died BUT RECOVERED DURING THE MATCH, the metric should be 0.5.
Since we know a checked checkbox is treated as a 1 and an unchecked checkbox is treated as a 0, we can express this metric with the formula: (0.5 * recoveredFromFreeze) + (1 - diedDuringMatch)
. Since we don't expect the students to check recoveredFromFreeze when diedDuringMatch is not checked, we do not expect the result to be greater than 1. (More on that later...)
Here's the derived metric for reliability factor:
{
"type": "derived",
"operations": [
{
"operator": "multiply",
"operands": [ 0.5, "recoveredFromFreeze" ],
"as": "recover"
},
{
"operator": "subtract",
"operands": [ 1, "diedDuringMatch" ],
"as": "1minusdied"
},
{
"operator": "sum",
"operands": [ "$1minusdied", "$recover" ]
}
],
"id": "reliabilityFactor"
}
The first operation calculates the value inside the first set of parentheses (0.5 * recoveredFromFreeze
) by using multiply with the hardcoded value 0.5 and fetching the value from recoveredFromFreeze, and then similarly the second operation calculates the value inside the second set of parentheses, but this time using the subtract operand. Note the use of as
in the first two operations. Additionally, note that the contents of the as
field DO NOT start with a $
.
The last operation simply sums them together. Note that both items in the operands
field start with a $
. The $
at the start tells the computing function to check from the pool of intermediate variables INSTEAD of checking from the pool of completed form elements (checkboxes, sliders, etc).
As an idea, you can use derived metrics to catch "impossible" scenarios as a way to check and see if the scouters might be putting in bogus data or making mistakes. In the previous example, we noted that the result should never be 1.5. How do we catch that?
We can do it in just 2 operations by just checking (recoveredFromFreeze - diedDuringMatch) > 0
. If recoveredFromFreeze = true (1) and diedDuringMatch = false (0), then the result will be 1. Otherwise, the result will be 0. The scouting lead can then at a glance see if there's potential bad data if the "Recover From Freeze🚩" metric is ever non zero.
{
"type": "derived",
"operations": [
{
"operator": "subtract",
"operands": [ "recoveredFromFreeze", "diedDuringMatch" ],
"as": "recMinusDied"
},
{
"operator": "gt",
"operands": [ "$recMinusDied", 0 ]
}
],
"display_as": "number",
"id": "recoverWithoutFreeze🚩"
},