Skip to content

Javascript API for Experimenters (first draft)

BobEvans edited this page Nov 21, 2014 · 2 revisions
For the actual code to be documented, it is currently checked into the repo under Paco/assets/custom_base.js. I have several experiments that we have run using this api. The api itself is a mix of responsibilities and is a bit uneven at this point. I have developed it with a bit of prediction about what it needs to do and the needs of these first experiments, so I expect it to change, perhaps drastically, with more use. 

So, far it is organized into a couple of javascript modules:
* paco - This is the root namespace and it has a bunch of miscellaneous things under it as well as specific modules and services

* paco.db - for access to the responses. Also has code for testing against an html storage db with the idea that users could develop an experiment in standard html tools (the browser) by including the js lib and then copying that into the Paco experiment form text area.

* paco.renderer - this is a collection of routines to build dom elements for rendering the various types of inputs, e.g., likert scales, open text, lists, photo chooser, etc..

As an aside, here's about the underlying design of the js api for Paco developers. The js library runs in a client html environment like an embedded WebView on Android or a UIWebkitView on iOS and in a browser on the web (eventually). There is a the top layer of the api paco.db.saveEvent, etc. This calls to a lower api layer that is device specific (Android, iOS, Web) that has access to js objects that bridge to the native resources. For example, the paco.db js api provides services to experiment developers. On Android, there is a js object injected into the embedded web environment by the native app that provides db calls to the underlying Android sqlite store we use. I mention all of this because the goal is to build up an api for experimenters that abstracts away the device-specific details as much as possible. This gives us cross-platform capabilities for experimenters and hopefully makes it more modular, and thus easier to support execution on more devices.

Now some detail about each module's functions:
paco.db exports these apis:

paco.db
      saveEvent:,
      getAllEvents,
      getLastEvent,
      getLastNEvents(n),
      getResponsesForEventNTimesAgo(n),
      getAnswerNTimesAgoFor(item),
      getLastAnswerFor(item),
      getAnswerBeforeLastFor(item),
      getMostRecentAnswerFor(item),
      getMostRecentAnswerTodayFor(item),
      recordEvent(responseObjectTuple)

paco.experimentService
    getExperiment(experiment)
    saveExperiment(experiment, saveCallback)  the callback returns an object with success or failure. 

    This api replaces paco.experiment which was used in the early days to return an experiment object.

paco.createDisplay
   This returns an object that wraps a dom element for a div in which to populate ui elements. It provides a very naive api:
    setTitle to set an H1 title on the div
    add(domElement) takes a string to add to the underlying div within a p element.

paco.createResponseEventForExperimentWithResponses

   creates the object to record a set of answers. It contains the necessary top-level properties, e.g., scheduledTime, plus the response objects for each input in the experiment.

paco.createResponsesForInputs
   This returns an object where the execute code can store the answers gathered from the html form elements when the user answers.

paco.validate: a function to validate each of the responses against the input type constraints

paco.random: returns a pseudo random number

paco.executor: This is a service for a custom experiment to call back into the native app to let it know that it is done controlling the world (gathering data). This is used by the custom save button to tell the native app to do whatever it does after the user hits save, e.g., show the Feedback screen.

paco.photoService: this provides access to the native photo taking capabilities on the device (usually an embedded object that calls the intent for the camera app.
   launch(callback) - this is what an experimentor would use.
   photoResult - this is exposed to make the Android callback api work to get the photo result, not called by users

paco.notificationService
   createNotification - this allows the user to create one notification in the tray
   removeNotification - this removes that one custom notification from the tray

paco.renderer
  renderPrompt - render the question text/prompt
  renderTextShort - builds a text input field


renderScale = builds a scale input item with radio buttons and labels
renderPhotoButton = builds the item with a photo to let the user capture a photo
renderPlainText = builds a plain text label in a span
renderList = builds a list choice box
renderInput = takes an experiment input and builds the appropriate concrete display element for its type
renderInputs = takes a collection of input objects and calls renderInput on each. Part of exposing these is to generate a default javascript rendering that the user can then override as they wish. For example, maybe they only want to override one question's display in which case they can use the defaults for all others.
renderBreak = renders an html break element
renderForm = high level function that renders a form for all inputs
renderCustomExperimentForm = this strips the custom rendering code into a script section and an html section and adds it into the document/dom/runtime in the embedded webview. It expects that the custom rendering code has a script section at least, delimited by <script> tags, and that after that it might have an html section as well. This is a very loose parsing and needs to be cleaned up as well with more explicit structure for the experimenters to use.
loadCustomExperiment = there is redundancy with this and renderCustomExperimentForm that is unnecessary at this point because the old experiments are all done. I need to reduce these to one.
renderSaveButton = renderSaveButton;
registerValidationErrorMarkingCallback = registerValidationErrorMarkingCallback;
renderFeedback = This renders the feedback form for either custom feedback or default feedback - much like custom form rendering but for feedback. Also, sometimes the experimenter may want to cancel the default feedback loop so they can incorporate the feedback in their run loop (e.g. creating a custom form that renders input items and feedback items at their own choosing maybe like a cycling headsup display that stays up but periodically shows questions until the user clicks a done button to end the experiment for that time.
paco.execute: this is not currently called, but it is another top-level method for setting everything up and launching a custom rendering. I have experimented with a couple of ways of doing this and sadly some of them, like this one, are still in the code. Though it is not currently called, it is a good reminder for me when I am creating custom experiments that only want to override a few things since it includes all the code for a default rendering that I need to copy over such as setting up conditional logic, validation and save callback functions.

runCustomExperiment - this is the current loop that loads up the default skeleton.html page, rigs up the infrastructure for the experiment form, and then calls the custom script's main method passing it the experiment and the root div for display.

One other api that needs a place in the namespace is window.email.sendEmail which provides access to send an email through the on-phone email app (android only).

--

That is a an overview of the custom api. Like I said, it is still a bit of a mess and in an early stage. Luckily, I am heavily involved in all the experiments using it so we don't have to worry too much about backward compatibility as it gets cleaned up.

If you are interested in how this all gets hooked up checkout the Paco Android class ExperimentExecutorCustomRendering.showForm method as that is the root method for setting this up.

It builds the embedded webview, injects javascript-to-native bridge objects, and loads the custom_skeleton.html file from the assets folder. That html page loads js libraries and then calls runCustomExperiment.

Clone this wiki locally