-
Notifications
You must be signed in to change notification settings - Fork 0
Archive Trac docs question response
madscatt edited this page Jun 20, 2026
·
1 revision
Legacy Trac archive page imported from
docs_question_response. Source: https://genapp.rocks/wiki/wiki/docs_question_response. Review age, links, and examples before treating as current.
- Preliminary Design document
- provides the ability for an executable to retrieve response from the user during the executable's run
- not recommended for large multi-core jobs or when running via a batch queue system, since this will likely cause idling esp. if the user's response is significantly delayed
- executable receives additional keys in input json object (similar to udp messaging)
- e.g. input json example important keys
...
"_tcpport" : 1234,
"_tcphost" : "some.host.ip.address",
"_uuid" : "1234-5678-ABCD-EFGH",
...
- this tcp server supports both regular messaging (for reliable sends and for larger messages, since UDP is limited to 64kb) and questions
- all communications over this server use the standard json output format.
- questions are identified with the special "_question" key
- at the top level of the question JSON, a timeout can be provided.
...
"timeout" : 1000,
...
- this will pose a limit on the time available for a user to respond.
- there will always be a system default timeout if you do not specify, which can vary depending on system setup
- the value of the "_question" key will be the question object in the following format
"_question" : {
"id" : "some_question_id_arbitrarily_assigned_by_the_executable"
,"title" : "a title for the question box"
# optionally add icon, default is question.png, standard options are information.png, warning.png, toast.png, skull.png, but other icons can be added (place in output/html5/pngs/)
,"icon" : "warning.png"
,"text" : "opening text, html ok"
# grid controls, see grid notes section
# specify number of columns for the grid (default 2)
,"grid" : 3
# specify more detail instead of just number of columns, this format replaces above
,"grid" :
{
# optionally specify a gap between grid boxes, default "10px"
"gap" : "5px"
# specify the number of columns and their relative widths
,"colwidths" : [ 1, 4 ]
# optionally specify alignment ("left" or "right" or "center"; default "left" )
,"align" : "right"
}
# buttons are optional, if not specified, an "OK" & "Cancel" will be shown
# any number of buttons can be specified
# the return button id will be the specified button text in lower case with all non alphanumeric characters removed,
# in the case below, that would correspond to values for the returned _response:button: of "button1" & "button2"
# note: any number of buttons can be specified
,"buttons" : [ "Button 1", "Button 2" ]
# optional alternate format for buttons with help and specified labels separate from id
,"buttons" : [
{
"id" : "button1"
,"label" : "My Button 1"
,"help" : "help for this button"
}
,{
# id is optional, if not specified, it will be the label converted to lower case and all non alphanumeric characters removed
,"label" : "My Button 2"
,"help" : "help for button 2"
# optionally add "skiprequired", this is useful for buttons like "cancel" or "done", it is set by default for "cancel" if buttons are not specified
,"skiprequired" : "true"
},
# you can mix and match alternate button specs
"Button 3"
]
# optionally add a required message, default is "Not all required fields have been entered."
,"requiredmsg" : "message to display if not all required fields are filled out"
,"fields" : [
# this is identical to the module field input, except that "role" is not required
# only a limited set of fields "type"s are currently supported
# initially supported: file, lrfile, text, checkbox, button
{
"type" : "label"
,"id" : "unique_id_for_this_entry"
,"label" : "text can be html formatted"
,"help" : "some hover help text - optional"
}
,{
"type" : "text"
,"id" : "unique_id_1_for_this_entry"
,"help" : "some hover help text - optional"
# optionally add "required", note if you are using custom buttons, you might consider setting one to "skiprequired":"true"
,"required" : "true"
}
,{
"type" : "integer"
,"id" : "unique_id_1_for_this_entry"
# optionally set a default value
,"default" : 23
# optionally set a minimum value
,"min" : 1
# optionally set a maximum value
,"min" : 100
,"help" : "some hover help text - optional"
# optionally add "required", note if you are using custom buttons, you might consider setting one to "skiprequired":"true"
,"required" : "true"
}
,{
"type" : "float"
,"id" : "unique_id_1_for_this_entry"
# optionally set a default value
,"default" : 123.456
# optionally set a minimum value
,"min" : 1.0
# optionally set a maximum value
,"min" : 100.0
,"help" : "some hover help text - optional"
# optionally add "required", note if you are using custom buttons, you might consider setting one to "skiprequired":"true"
,"required" : "true"
}
# note that textarea is currently for output only (i.e. readonly)
,{
"type" : "textarea"
,"id" : "unique_id_1_for_this_entry"
,"label" : "an optional label"
,"rows" : 10
,"cols" : 80
,"default" : "The text that will appear\ncan be multiline"
,"help" : "some hover help text - optional"
}
# file
# the file will be uploaded into a subdirectory "uploads" in the original directory of the running job.
# if a file of the same name already exists in this directory, a -1 or -2 etc. will be added to the name of the file
# before writing.
# the value of the "id" key in the questions response will have the final file name.
,{
"type" : "file"
,"id" : "unique_id_2_for_this_entry"
,"label" : "upload a file"
,"help" : "some hover help text - optional"
# optionally add "required", note if you are using custom buttons, you might consider setting one to "skiprequired":"true"
,"required" : "true"
}
# lrfile
# the file will be uploaded into a subdirectory "uploads" in the original directory of the running job.
# if a file of the same name already exists in this directory, a -1 or -2 etc. will be added to the name of the file
# before writing.
# note - if the file uploaded from the server is already in the uploads directory (i.e. identical), it will not be renamed.
# the value of the "id" key in the questions response will have the final file name.
,{
"type" : "lrfile"
,"id" : "unique_id_3_for_this_entry"
,"label" : "upload a file"
,"help" : "some hover help text - optional"
# optionally add "required", note if you are using custom buttons, you might consider setting one to "skiprequired":"true"
,"required" : "true"
}
,{
"type" : "checkbox"
,"id" : "unique_id_4_for_this_entry"
,"label" : "checkbox text"
,"help" : "some hover help text - optional"
}
,{
"type" : "listbox"
,"id" : "unique_id_5_for_this_entry"
,"label" : "listbox label"
# for fixed alignment, add a monospace fontfamily
,"fontfamily" : "monospace"
,"values" : [
"row 1 value"
,"row 2 value"
]
# optionally specify return values, otherwise, you will get an index (0 based) into the values
,"returns" : [
"return value for row1"
,"return value for row2"
]
# optionally - if no "returns": are explicitly specified - select a default row by row number (0 based)
,"default" : 3
# optionally - if "returns": are explicitly specified - select a default row by specified return value
,"default" : "return value for row2"
# optionally display multiple rows
,"size" : 5
,"help" : "some hover help text - optional"
# optionally provide a "header" for the listbox
,"header" : "header text for listbox"
# optionally provide a fontsize for the header
,"fontsize" : "0.93em"
# optionally select multiple
,"multiple" : "true"
# optionally add "required", note if you are using custom buttons, you might consider setting one to "skiprequired":"true"
,"required" : "true"
}
]
}
- upon sending of this message to the tcp server, the client will wait for a response on the tcp connection
- an exit timeout should be imposed in the executable, since the user may never respond
- this message will be forwarded to the client UI by the tcpserver and the question will be logged in the mongodb (possibly multiple clients) via the standard zmq->websocket mechanism
- the client UI will recognize the "_question", display the question to the user in a modal box
- when the first client user responds, a message will ajax post to a php script which will in turn, allow the tcp server to send a response
- simultaneous client attempts (multiple clients attached) on the same message will receive notification that this message has already been answered
- if simple enough, they will also receive the value of the other client's response
- the client will receive an response from the tcp connection in the following format (similar to a standard module input object:
"_uuid" : "1234-5678-ABCD-EFGH",
"_response" : {
"id" : "id of question"
,"unique id 1 for this entry" : "value"
,
}
- a library will be created for various executable languages to simplify question/response for the executable as needed
- e.g. python
- when "grid" has been defined at the top-level of the question as shown above, each field has optional grid controls.
- it is only necessary to specify "grid" info a field if you would like to override defaults.
- by default:
- the grid starts at row 1 column 1
- there are two columns
- the 1st one is typically used for "label"s
- the 2nd one is used for "data", such as the text box for inputing text, etc
- when a "field" is processed
- the current row will be incremented from the previous last row
- the current column will be set to 1
- if the label is present (i.e. not an empty string "", it can be a single space " " for a blank label)
- it will be placed in the current column and the current column will be incremented
- the data will be placed in the current column and the current column will be incremented (the final increment serves no real purpose at this time)
- any field can be overridden, both the "label" and the "data".
- if you wish to manually place a field, you must add the information to the field with a "grid" tag
- the "grid" tag's value is an object with optional values for "label", "data" and "align".
- the optional "label" and "data" tags' values are a list of numbers specifying the starting column, number of columns, starting row and number of rows
- you do not have to provide all four values, i.e. just starting column or starting column and number of columns, etc, can be provided
- if you are going to give a starting row, you will currently have to keep track of the row numbers yourself
- the optional "align" tag's value is a string of either "left", "right" or "center"
- the default value is "left" unless specified in the question's top-level "grid" definition
- example 1 - putting the checkbox data in column 1 and the label in column 2:
,{
"type" : "checkbox"
,"id" : "unique_id_4_for_this_entry"
,"label" : "checkbox text"
,"help" : "some hover help text - optional"
,"grid" : {
"label" : [ 2 ]
,"data" : [ 1 ]
}
}
- example 2 - putting a check box in columns 3 and 4 of row 1 and right aligning the text:
- note that the next row will be 2, so if you are going to have further fields, you will likely
- need to specify "grid" also for at least the subsequent field.
,{
"type" : "checkbox"
,"id" : "unique_id_4_for_this_entry"
,"label" : "checkbox text"
,"help" : "some hover help text - optional"
,"grid" : {
"label" : [ 3, 1, 1 ]
,"data" : [ 4, 1, 1 ]
,"align" : "right"
}
}
- here is a python example of using the tcp server for question/response using the canonical "energy" module
#!/usr/bin/python
import json, sys, StringIO, socket
def tcpquestion(json_variables, question, timeout=300, buffersize=65536):
# json_variables is the input json object
# question is either a dict or json string
# timeout is in seconds
# buffersize is for the answer, so if you expect larger than 64k of total json string size, use a larger number
if '_tcphost' not in json_variables or '_tcpport' not in json_variables:
return json.loads('{"error":"no tcp support"}')
# build question
msg = {}
msg['_uuid'] = json_variables['_uuid']
msg['timeout'] = timeout
if isinstance( question, basestring ):
try:
msg['_question'] = json.loads(question)
except ValueError:
return json.loads('{"error":"json question decoding error"}')
elif isinstance( question, dict ):
msg['_question'] = question
else:
return json.loads('{"error":"question must be a json string or dict"}')
msgj = json.dumps(msg)
# a newline is also required when sending a question
msgj += '\n'
# send question
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((json_variables['_tcphost'],json_variables['_tcpport']))
s.send(msgj)
# receive answer
data = s.recv(buffersize)
s.close()
return json.loads(data)
if __name__=='__main__':
argv_io_string = StringIO.StringIO(sys.argv[1])
json_variables = json.load(argv_io_string)
mass = float(json_variables['m'])
speed_of_light = float(json_variables['c'])
import mass_energy
output = {}
# ask silly question if possible
myquestion = '''
{
"id" : "q1"
,"title" : "are you sure?"
,"text" : "<p>header text.</p><hr>"
,"fields" : [
{
"id" : "l1"
,"type" : "label"
,"label" : "<center>this is label text</center>"
}
,{
"id" : "t1"
,"type" : "text"
,"label" : "tell me your name:"
}
,{
"id" : "cb1"
,"type" : "checkbox"
,"label" : "are you sure about the speed of light?"
}
]
}
'''.strip()
answer = tcpquestion( json_variables, myquestion );
# alternatively, one could pass a dict
# mydict = json.loads(myquestion)
# ... manipulations of mydict ...
# answer = tcpquestion( json_variables, mydict );
if 'error' in answer:
# handle error, could be timeout, no tcp support, malformed question json or other
output['question_error'] = answer['error']
if answer['_response']['button'] == 'cancel':
# the user pressed cancel, handle it
output['user_input'] = 'user has canceled'
output['users_name'] = answer['_response']['t1']
output['e'] = mass_energy.einstein(mass,speed_of_light)
output['answer'] = json.dumps(answer)
print json.dumps(output)