Share Status of Quotas from a Survey

Scripting Solutions

Additional scripting solutions will be added in the future. Please reach out to Alchemer with comments and suggestions on solutions you'd like to see via the link here.

Goal

Share the status of a survey's quotas with other members of an organization or team.

Effort:   

Solution

This solution requires users create a new survey to display the quota counts of the primary survey. Users share a link to this survey to the stakeholders needing to monitor the primary survey quotas. Only Logic and Distributed Logic quotas display, not the Overall Quota.
 

   Step 1:  Create a New Survey   

1. Create a survey.

2. Set the first page to auto-submit.

3. Add the following code to the survey via Style tab > HTML/CSS Editor (lower right of page) > Custom CSS to format the quotas:

/* Format quotas */
.sg-http-content {
  display: none;
}

th, td {
  border: 1px solid black;
  padding: 10px;
  text-align: right;
}

th {
  background-color: lightgray;
}

table {
  border-collapse: collapse;
}

4. Add a new Page #2
 

   Step 2:     Add a Webhook Action to the top of the new Page #2 with the following settings:

SettingValue
MethodGet
URLapi.alchemer.com/v5/survey/1234567/quotas?api_token=***&api_token_secret=***

Replace api.alchemer.com with your datacenter:
  • US:  api.alchemer.com
  • CA:  api.alchemer-ca.com
  • EU:  api.alchemer.eu

Replace 1234567 with the survey ID containing the quotas

Replace the end with your API token

Choose: https

What do you want to do with the data/content returned from the URL?Display it


   Step 3:    Add a Javascript Action with the code below

/* Alchemer v01

   Display the quotas returned from a Webhook Action 

   Documentation and updates: https://help.alchemer.com/help/share-status-of-quotas-from-a-survey
*/

document.addEventListener("DOMContentLoaded", function() {

  // * * * * * * * * * * * * * *
  // * no changes needed below *
  // * * * * * * * * * * * * * *

  const LOG = true

  /**
   * Helper to display error dialog and throw new Error
   */
  const assert = (bool, msg) => {
    if (!bool)
      throw new Error(msg)
  }
   
  /***
   * Sort two strings in alpha order
   */
  const alphaSortFn = (a, b) => a.localeCompare(b)

  /***
   * Sort two quotas in alpha order
   */
  const quotaSortFn = (a, b) => alphaSortFn(a.name, b.name)

  /***
   * Parse all quotas from webhook call.
   *
   * return (quota array) array of quota objects with format:
           {
             "id": "2600",
             "name": "fill-Male-A",
             "description": "",
             "responses": "0",
             "limit": "100",
             "distributed": "false"
            }
   */
  const getAllQuotas = () => {

    // get webhook call result elem
    const webhookResultElem = document.querySelector('.sg-http-content')

    // test
    if (!webhookResultElem)
      throw new Error("Can't find Webhook data. Check that Webhook Action is on this page and has 'Display it' selected")

    if (LOG) console.log("\n---- Raw return from webhook ----\n" + webhookResultElem.innerText + "\n---- ^^^^^^^^^ ----\n")
    
    const quotasJSON = webhookResultElem.innerText

    if (!quotasJSON)
      throw new Error("Webhook call failed with no data returned")

    const parsed = JSON.parse(quotasJSON)

    // check webhook return 
    if (!parsed.result_ok) {
      if (parsed.code === 403 && parsed.message.startsWith('POST calls are not allowed'))
        throw new Error("Webhook call failed, check that the Webhook Action's Method is set to 'Get'")  
      throw new Error("Webhook call failed, check that you entered the correct datacenter, API token, and Survey ID: " + parsed.code + ' - ' + parsed.message)
    }
    if (LOG) console.log("getAllQuotas() = ", parsed.quotas)
    if (!parsed.quotas.length)
      throw new Error("No quotas were found, check that Webhook is referencing the correct survey ID")

    return parsed.quotas
  }

  /***
   * Get an index for a continuous value.
   *
   * Example:
   *     ['green','orange','red'][getIndex(0.2, 1, 3)] // 'green'
   *
   * Example:
   *     getIndex(-25, 100, 3) // 0
   *     getIndex(0,   100, 3) // 0
   *     getIndex(40,  100, 3) // 1
   *     getIndex(80,  100, 3) // 2
   *     getIndex(100, 100, 3) // 2
   *     getIndex(200, 100, 3) // 2
   *
   * x (int/float) the continuous value
   * max (int/float) index will be calculated based on 0-max, however actual x value can be outside this range
   * numIndexes (int) the return will be in the range 0-(numIndexes - 1) 
   */
  const getIndex = (x, max , numIndexes) => {
    const index = x / (max / numIndexes)
    // min/max to keep it in the range of 0 to (numIndexes-1)
    return Math.max(
             Math.min(
               Math.floor(index), 
               (numIndexes - 1)),
             0)
  }

  /***
   * Get an HTML formatted display for the % quota filled column
   * 
   * responses (int) number of responses received
   * limit (int) the quoto limit
   * return (html text) calculated percent with color formatting for red, orange, green
   */
  const formatPercentFilled = (responses, limit) => {
    const percent = responses / limit * 100
    const color = ['red', 'orange', 'green'][getIndex(percent, 100, 3)] 
    return `<span style="color:${color};">${(percent).toFixed(0)}%</span>`
  }

  /***
   * Show quotas 
   */
  const showQuotas = () => {

    let quotas = getAllQuotas()

    // log
    if (LOG) {
      console.log("quotas = ")
      console.log(
        quotas.map((quota) => `  ${quota.name} - ${quota.responses}` )
        .sort(alphaSortFn)
        .join('\n'))
    }

    // table inside a div that provides css layout of a Text element 
    let tableHTML =
        '<div class="sg-question sg-type-instruction">'
      +   '<table style="margin-left: auto;  margin-right: auto;">'
      +     `<tr><th>Quota</th><th>Current</th><th>Limit</th><th>% filled</th></tr>`
      +     quotas
               .sort(quotaSortFn)
               .map(quota => `<tr><td>${quota.name}</td><td>${quota.responses}</td><td>${quota.limit}</td><td>${formatPercentFilled(quota.responses, quota.limit)}</td></tr>`)
               .join('')
      +   '</table>'
      +   '</br>'
      + '</div>'

    let elemWebhook = document.querySelector('.sg-http-content')
    elemWebhook.insertAdjacentHTML('afterend', tableHTML);
   }

  /**
   * main()
   */
  try {
    showQuotas()
  }
  catch (err) {
    console.error("ERROR.message ", err.message)
    console.error("ERROR ", err)
    alert("Javascript error: " + err.message)
  }
})
Basic Standard Market Research HR Professional Full Access Reporting
Free Individual Team & Enterprise
Feature Included In