Veracity Logo

3577014
Statements

55794
Actors

387255
Activities

675
LRSs

Back to TOC

User Manual

Custom Dashboards, Graphs and Analytics Processors

Veracity Learning offers several ways to generate custom dashboards. At the most basic level, you can place widgets (graphs or tables) on a dashboard page. Each widget is customizable with a set of options like what actor, what object or what verb to use. Beyond that, we offer a set of highly flexible widgets that can be used to build custom bar, pie and line charts. These widgets include a point and click interface to build many common queries. We also offer a fully customizable Aggregation widget, which uses the same query language as our aggregation endpoint. This widget allows you to build very sophisticated analyses and pair them with a huge variety of graphic options.

For onsite, enterprise customers, your server can be customized with additional widgets through an easy to use API.

Table of Contents

Custom Dashboards

The most basic way to get additional insight into your data is to use our custom dashboard feature. Each custom dashboard can have a set of configurable widgets on it, each of which shows a graph or table.

To Create a Custom Dashboard

Start by loading the analytics page for your LRS. Do this by clicking the "Analytics" button on the left side menu.

Then open up the "Custom Dashboards" menu item on left, and find "New Dashboard".

This will create a new custom dashboard with the default title "Custom Reports". From here you can add widgets, configure widgets, and set the title. Click the "Edit Dashboard" button.

Add a widget.

You'll now see a popup with a list of widget types you can select. See below for a description of all the various widgets available. Choose a widget from the list by clicking on it.

When you select a widget, one will be added to the dashboard. It will appear at the end, so if you have many already, you might need to scroll down to see it. New widgets are set up with their default configuration. For some widgets, the defaults produce a graph, but others require additional configuration before they will work.

Widgets can be removed, sorted and configured with the icons on the top right. These are only visible when the dashboard is in "edit" mode. Click the "Configure Widget" icon.

Every widget exposes a set of parameters that can modify how the widget works. Generally, widgets expose properties to help scope their graph to a particular actor, object or verb. Many also allow you to override the global time range.

Many of the fields in the widget configuration search your database to suggest values. Begin typing in these fields to search for object, actors, or verbs.

You must click "Ok" to save the parameters. Use the grey "X" in the upper right to discard the changes. You might find that your parameters result in an empty graph. This simply means that there is not enough data to display a chart. Note that when you exit edit mode, widgets without enough data to display will be hidden completely.

You can set the title of the dashboard with the "Set Title" button. Click "Ok" to save the title.

All custom dashboards will be shown on the list in the left hand menu. Anyone with analytics permissions on the LRS can view these dashboards.

All Widgets

The following widgets are available to be placed on a custom dashboard. If you have an onsite enterprise installation, your custom Analytic Processors will be listed as widgets along with the built in options.

  • score over time

    A line chart showing activity for a given object. Allow selection of the object, verb type, and time span. Can be limited to an actor or activity.

  • activity by authority

    A pie chart showing the proportion of statements from each Authority. The Authority is used to identify the source of a statement, and is usually (but not always) associated with an access key.

  • top objects by verb

    A bar chart of the top objects for a given verb. Can be limited to an actor or activity.

  • activity over time

    A line chart showing activity for a given object. Allow selection of the object, verb type, and time span. Can be limited to an actor, activity, verb or combination.

  • most active actors

    A stacked bar chart of most active actors, stacked by verb type. Can be limited to a specific Activity.

  • recently active objects

    A pie chart showing the most active objects. When pie pieces are clicked it will drill down into the object overview dashboard. Can be limited to a specific actor.

  • verb type

    A bar chart showing the relative frequency of verbs. Can be limited to a single actor, activity, or combination of each.

  • activity overview

    A table with general stats about this activity.

  • agent overview

    A table with general stats about an actor.

  • event table

    A table of statements. You can choose which actor, verb, or object.

  • generic bar graph

    A highly customizable generic bar chart.

  • generic pie chart

    A highly customizable generic pie chart.

  • aggregation pipeline

    A highly customizable generic bar chart.

  • generic time series graph

    A highly customizable generic time series chart. Groups by timestamps, with a value you can specify.

  • context activity over time

    A line chart showing activity for a given context object. Allows selection of the object, verb type, and time span.

  • statement viewer

    A generic statement viewer. No configuration other than date, but allows client-side searching and filtering.

Custom Bar, Pie and Serial Charts

Veracity Learning LRS exposes a highly customizable set of widgets that can build a huge variety of charts. These are the Generic Pie, Generic Bar and Generic Serial charts. Each exposes a query builder that will help you build complex queries.

In general, each works the same way, but renders the results differently. Let's look at the parameters for a generic bar chart.

These three widgets all work on the same principle. Filter all the xAPI statements into a smaller set, then group them into buckets by some criteria. For each bucket, we can compute the min, max, average or standard deviation for different parts of the statement.

Clicking the dropdown in "MatchFilter" will give you a list of fields from the xAPI statement specification.

In this case, we've selected the Object.id value. Clicking in Match Value will list the top hits for this value. In this case, the courses listed are the most requested object ids used in xAPI statements. You can also type words into the box to search.

The grouping key works the same way - we are selecting which part of the statement will be used to group the statements. In the example below, we create groups by the verb.id. So, wherever the verb.id is the same, we create another bar in the bar chart.

The grouping operator tells us what metric to compute. The sum, average, or deviation?

The group operator value tells Veracity Learning LRS where to get the values. Choosing a grouping operator of "sum" and a grouping operator value of "1" means "Count the statements that went into each group."

So, the setup below gives us a bar chart of the total statements for each verb where the object.id is "http://asu.edu/courses/an"


The setup below would create a bar chart showing the average score of all statements on each object where the actor name is "Jonathan Mendez". Each bar will represent one object.id, and the height of the bar will be the average score for that actor on that object.

Extensions in Generic Graphs

For most common scenarios, we've created specific widgets, so you don't have to create your own custom query for most cases. However, the generic graphs are the only way to generate charts over xAPI Extensions.

You can use a special syntax to ask the chart to use an extension. Instead of choosing from the list, you can type statement.context.extensionname.

You must add the statement. prefix to the path name to let the system know you wish to select an extension. The xAPI specification allows for several extension points

  • statement.object.definition.extensions
  • statement.result.extensions
  • statement.context.extensions

In the future, you'll be able to use a similar syntax to access statement metadata, like the IP address, session identifier, or even the HTTP headers.

Complex Queries in Generic Graphs

You can even add complex conditions to the Match Value. If you provide a JSON string, it will be used as the match value. This JSON can use the MongoDB query language query language to specify conditions.

Here, I've added a "less than" operator to the scaled score query.

You can even use top level logical operators like "$and", "$or" or "$not". In the image below, I've pasted the following into the Match Value box

   [
       {statement.result.score.scaled: {$gt:.3} },
       {statement.result.score.scaled: {$lt:.9} }
   ]


This selects statements where the score is greater than .3 or less than .9.

If this is still not flexible enough for your needs, read on to the next section.

Custom Aggregations on a Dashboard

The most flexible, generic and powerful tool available (without writing a plugin widget in the enterprise version) is the Aggregation Widget. This tool marries up the aggregation query API with the charting and dashboard system.

You can input an entire aggregation pipeline algorithm, and map it to a graph. This tool also exposes the full configuration for the graphing system, so you can change colors, borders, orientation, or even use other graph types like a funnel chart, candlestick chart, or even a world map.

Check out AmCharts for full documentation on the possibilities.

When you open the setup for the aggregation pipeline widget, you'll find 2 large text boxes - one for the aggregation pipeline query, and one for the graph configuration.
In the top box, you can input an aggregation pipeline in the JSON format. Read here for the format and syntax you can use here.


Here is a query that is set up to build a bar chart of the count of verbs. This pipeline is quite similar to the queries in the Generic Bar Chart section, but could be much, much more complicated. Here's what is rendered from this configuration.

For reference, the query is the same as on the aggregation documentation page:

[
    {    
        $match:{
            $and:[
                {statement.timestamp :{ $lt: { $parseDate:{date:"Tue Mar 27 2019 16:25:40 GMT-0400 (Eastern Daylight Time)"}}}},
                {statement.timestamp :{ $gt: { $parseDate:{date:"Tue Mar 20 2017 16:25:40 GMT-0400 (Eastern Daylight Time)"}}}},
            ]
        }
    },
    {
        $group:{
            _id:"$statement.verb.id",
            count:{$sum:1}
        }
    }
]

Note that we use a relaxed JSON parser. You don't need to add quotes around every property name. You can also use the $parseDate, $parseRegex and $parseNum operators to input dates and regular expressions.

The second box is JSON to configure the graphic display. Veracity Learning LRS includes some common configurations that you can call on by including a "constructor" field.

{
    constructor:{
        type:"BarChart",
        params:["_id","count"]
    }
}

This Graph Configuration initializes a common template: a bar chart where "_id" is the name of the bar, and "count" is the value. Here are some other common templates you can load using the same technique

  • A bar chart where the bar title is the _id property from each result from the pipeline, and the height of the bar is the value of the "count" field.
{
    constructor:{
        type:"BarChart",
        params:["_id","count"]
    }
}
  • A pie chart where the pie slice title is the _id property from each result from the pipeline, and the size of the slice is the value of the "count" field.
{
    constructor:{
        type:"PieChart",
        params:["_id","count"]
    }
}
  • A serial chart where the X axis is the value from the field "date" from each result from the pipeline, and the height of the line is is the value of the "value" field.
{
    constructor:{
        type:"SerialChart",
        params:["date","value"]
    }
}
  • A table where the columns are the values from the fields "name","title","time","count".
{
    constructor:{
        type:"Table",
        params:["name","title","time","count"]
    }
}

Other than the Table, each configuration object represents a JSON chart config from the AmCharts library. Other properties will be merged into the configuration generated by the constructor.

The above configuration removes the legend.

This example shows how you can change graph rendering properties.

{
    constructor:{
        type:"PieChart",
        params:["_id","count"]
    },
    legend:{
       disabled:true
    },
    series: [ 
        {
            slices:{
                fillOpacity:0,
                strokeWidth: 3,
                strokeOpacity: 1
            }
        }
    ]
}


You can also set colors by providing a list.

{
    constructor:{
        type:"PieChart",
        params:["_id","count"]
    },
    legend:{
       disabled:true
    },
    series: [ 
        {
            colors:{ list: ["#000070","#000090","#0000C0","#0000E0","#0000E0","#0000E0","#0000E0","#0000ED"] },
            slices:{
                fillOpacity:1,
                strokeWidth: 5,
                strokeOpacity: 1,
                stroke:"#000000",  
            }
        }
    ]
}

The constructor type set up the data series for you. You can generate multi-part graphs by overriding the data series we generate. You can generate multi series graphs like the one below. Here, we add a second data series. I'm not going to show it, but we added a second field to each computed slice.

{
    constructor:{
        type:"PieChart",
        params:["_id","count"]
    },
    legend:{
       disabled:true
    },
    series: [ 
        {
            innerRadius:"30%",
            outerRadius:"60%"
        },
        {
            dataFields: { value: 'avgScore', category: '_id' },
            innerRadius:"61%",
            outerRadius:"100%"
        }
        
    ]
}

As you can see, you can create a vast array of different graphs with the Aggregation Pipeline Widget. Again, here are some references for the various JSON formats used:

Love this feature, but wish you could reuse each pipeline multiple times, with parameterized inputs? Read on to find out how to create totally custom widgets with Javascript and our analytics plugin features.

Custom Plugin Analytics Processors

The Enterprise version of Veracity Learning LRS allows you to use a powerful JavaScript API to create entirely new widgets. To create a new widget, you make a plugin Analytics Processor. These processors can export a MongoDB Aggregation Pipeline or an ElasticSearch Aggregation query, and expose various parameters to the UI. These parameters are then displayed on the configuration dialog for each instance of the widget. This allows you to create reusable widgets that can be placed multiple times onto a dashboard, then configured, exported, or embedded in other systems. You can use a variety of utility functions that we provide, or completely go "off-road" and generate your own logic.

The first step is to create a file in the analytics/customProcessors/ folder under your Veracity Learning install. Note that if you have a large scale installation with multiple front end machines, each will need the same file in the same location. Also be careful to set the file permissions so that the account running the web server can access the file.

You'll have to restart the server when you create a new plugin analytic processor, but after it's been created, you can just edit the file. The server will reload automatically.

A basic plugin Analytics Processor looks like this:

module.exports = class MyProcessor extends AnalyticProcessor{
    constructor(params,db,lrs) {
        super(params,db,lrs);
        console.log("Wow, in the  derived constructor!",params);
        
        this.pipeline = [
            ...CommonStages(this,{
                range:true,
                limit:true
            }),
            {
                $match: {
                    "statement.object.id": this.param("activity")
                }
            },
            {
                $limit: 10
            }, 
            {
                $group: {
                    _id: "$statement.actor.id",
                    count: {
                        $sum: 1
                    }
                }
            }
        ];

        this.chartSetup = new BarChart("_id","count");
        this.map = MapToActorNameAsync("_id");
    }
    map(val)
    {
      //  console.log(val);
        return  val;
    }
    filter(val)
    {
        return Math.random() > .5;
    }
    exec(results)
    {
        console.log(results);
        return results;
    }
    static getConfiguration() {
        let conf =  new ProcessorConfiguration("Demo", ProcessorConfiguration.widgetType.graph, ProcessorConfiguration.widgetSize.small);
        conf.addParameter("activity", new ActivityPicker("Activity","Choose the activity to plot"), true);
        return conf;
    }
}

Notice how the constructor extends AnalyticProcessor, and sets this.pipeline. This is a MongoDB Aggregation processor. Unlike the Aggregation Widget above, it "parameterizes" a part of the query by adding this.param("activity") at a certain point. The static method getConfiguration tells the GUI a bit about how to display the parameters. It says, basically, that the user should pick a value for the "activity" parameter, and that they should be offered choices from the systems registry of xAPI "activities". There are various picker types available, listed below. Note that this technology is behind all the built-in widget types!

You can also see that the class has map, filter,and exec functions. These allow you to execute some JavaScript on the results of the Aggregation query, for cases where you just can't get the logic into a Mongo Aggregation Pipeline. Each of these functions may even perform asynchronous work using the async keyword in ES6.

  • map(val)

    Takes in each value from the result stream and transforms it, returning a new object that will replace the result.
  • filter(val)

    Takes each value and returns a boolean. If false, the value is removed from the result set.
  • exec(results)

    Takes the whole set of results at once, and returns a new set of results.

These functions are called in the order: filter, map, exec.

In the example above, you can see that in the constructor, the map function (this.map) is replaced. We generate a new mapping function by calling the utility MapToActorNameAsync. This utility will replace the value of "_id" in each result with the actor's name, by looking up the actor from the system's registry where the results '_id' property value is the IFI for the actor. So, an object that looks like this:

{
    _id:"mailto:[email protected]",
    averageScore:"100",
    daysMissed:0,
    count:100
}

becomes this:

{
    _id:"Rob Chadwick",
    averageScore:"100",
    daysMissed:0,
    count:100
}

The constructor also sets the chartConfig in the line this.chartSetup = new BarChart("_id","count"); This generates a new BarChart where the bars are named by the value of the _id field, and the height of the bars is read from the count field. This mirrors the configuration of the Chart Config in the Aggregation Widget, so read over that documentation for more info. The structure of the chartConfig field depends on the engine value, and is documented below.

So, now you can see a basic set up, let's list the various utilities we provide for you to use.


Processor Configuration

Every processor must export a configuration object from a static method called getConfiguration

static getConfiguration() {
        let conf =  new ProcessorConfiguration("Demo", ProcessorConfiguration.widgetType.graph, ProcessorConfiguration.widgetSize.small);
        conf.addParameter("activity", new ActivityPicker("Activity","Choose the activity to plot"), true);
        return conf;
    }

This configuration sets a few things.

ProcessorConfiguration(title:String,type:Enum,Size:Enum)
  • Title
    The displayed name of the associated Widget.
  • type
    An enumeration of types. Valid values are
    • ProcessorConfiguration.widgetType.graph
    • ProcessorConfiguration.widgetType.table
    • ProcessorConfiguration.widgetType.iconList
    • ProcessorConfiguration.widgetType.statementViewer
  • size
    An enumeration of sizes. These sizes are just requests - the system will attempt to fill the space available.
    • ProcessorConfiguration.widgetSize.small
    • ProcessorConfiguration.widgetSize.medium
    • ProcessorConfiguration.widgetSize.large
    • ProcessorConfiguration.widgetSize.xlarge
    • ProcessorConfiguration.widgetSize.xxlarge

A processor configuration also has a few functions you can call to set other options.

  • setDescription (text)
    The description text block on the widgets page.
  • setCacheLifetime (time:String)
    A human interval like 5 seconds or 10 minutes or 3 days. Sets the amount of time the analytics will be cached.
  • setRefreshSeconds (time:Number)
    The chart will automatically refresh after this interval.
  • setEnableWidgetChrome (show:Boolean)
    Add or remove the title bar and other chrome on the GUI.
  • addParameter (parameterName, paramType, default_value, required)
    Add a parameter picker to the configuration page.
    • parameterName
      The name of the param. You'll access the value sent by calling this.param(parameterName)
    • paramType
      A parameter type object. Defines the type of picker available to the user. See below.
    • default_value
      The value that will be returned from this.param when the user does not supply a value
    • required
      A boolean that tells the GUI that the parameter is required. If the widget has any parameters that are required and not set, the GUI will prompt the user to configure the widget

Parameter Types

Used to tell the system what sort of GUI to present the user when they are configuring a widget based on the processor. Each should be created with new. The values are in the global scope, so you can type new Text("user sees this").

  • ActivityPicker (title, required, description)
    This picker type will allow the user to search for xAPI Objects or activities. The value returned will be the ID of the activity.
  • ActorPicker (title, required, description)
    This picker type will allow the user to search for xAPI Actors or Agents. The value returned will be the IFI of the agent.
  • ClassPicker (title, required, description)
    This picker type will allow the user to search the "classes" that are set up in the LRS. Classes are groups of learners. The value returned will be the UUID of the class.
  • CoursePicker (title, required, description)
    This picker type will allow the user to search the "courses" that are registered in the LRS. Courses are lists of "content". The value returned will be the UUID of the course.
  • LessonPicker (title, required, description)
    This picker type will allow the user to search the "content" that are registered in the LRS. Content is an xAPI activity that is registered in the system with additional metadata. The UUID of the content object will be returned.
  • Text (title, description)
    This picker type will allow the user input any text value.
  • NumberText (title, description)
    The picker will let the user enter text into a textbox. This value will be parsed into a number for you.
  • TimeSpan (title, description)
    This picker will allow the user to select from hourly, daily, monthly, or yearly. The value returned will be a string that can be used with Date.toString to cast a date to the given span. This operates by taking the "floor" of the DateTime at a given value. For instance, '%Y-%m-%dT12:00:00Z' is returned for "daily". Calling Date.toString('%Y-%m-%dT12:00:00Z') returns the same value for all timestamps on a given day. This can be used with a $group operator to group up all statements on a particular day.
  • TimeRange (title, description)
    This picker will allow a user to choose a time range. They are offered either a predefined string like "today" or "this week", or they can choose a specific range. The value returned is a JS object in the form {from:Date, to:Date}.
  • Verb (title, description)
    This picker lets the user choose from a predefined list of common xAPI verbs.
  • ChoicePicker (title, required, description,choices)
    This picker renders a "select". The choices parameter should be an array in the form [{text:String, value:String}]. The user is shown the text, but the value you receive is the value.

Mapping functions

These utilities make it easier for you to specify common mapping transforms. Each is a function in the global scope. The return value of these functions are themselves functions, and should be assigned to this.map. For instance:

this.map = MapToCourseName("_id");
  • MapToActorName (inkey,outkey)
    The value of the result where the key is inkey should be an xAPI Agent. This function will find the actor name in the xAPI Agent object, and place it in the result under the key outkey. If outkey is undefined, it is assumed to be the same as inkey. For instance if this.map = MapToActorName("_id","name") and the input object is:
      {
          _id: {
              mbox:"mailto:[email protected]",
              account:{
                  name:"Rob C",
                  homePage:"https://www.veracity.it"
              }
          }
      }
    
    
    The mapping function would output
      {
          _id: {
              mbox:"mailto:[email protected]",
              account:{
                  name:"Rob C",
                  homePage:"https://www.veracity.it"
              }
          },
          name:"Rob C"
      }
    
    
  • MapToActorEmail (inkey,outkey)
    Similar to the above MapToActorName, but for email.
  • MapToVerbDisplay (inkey,outkey)
    Similar to the above MapToActorName, but for verbs. Finds the proper verb display string in a verb definition.
  • MapToCourseName (inkey,outkey)
    Similar to the above MapToActorName, but for xAPI activities. Finds and attaches the best title for an activity by looking in the activity definition language maps.
  • VerbIDToDisplay (inkey,outkey)
    Given a verb IRI, select the last segment of the IRI for display. Splits the value by "/" then return the last portion.
  • MapToActorNameAsync (inkey,outkey)
    Finds the actor name by examining the canonical tables. This is an asynchronous operation, and should only be used when you have an actor IFI without the rest of the actor definition. The canonical tables keep track of the last display name used in an xAPI statement for the given IFI.
  • MapToCoursesNameAsync (inkey,outkey)
    Finds the object name by examining the canonical tables. This is an asynchronous operation, and should only be used when you have an object id without the rest of the object definition. The canonical tables keep track of the last display name used in an xAPI statement for the given ID.


Chart Configuration

Processors use the chartConfig field to store configuration for their widget renderer. Most widgets in Veracity Learning LRS are of the type graph. This is set in the constructor of the ProcessorConfiguration object. A widget with the type graph or table requires additional information on how to build the graph, and how it maps to the results of the query. Similar to the constructor field in the Chart Setup in the Aggregation Widget, we allow you to call on some common constructors to build the JSON object that represents the graph that is drawn into the widget. We expose some common types with global constructors, but you aren't limited to these. Check out AmCharts for full documentation on the possibilities. You can set the value of this.chartConfig in the constructor or in the exec function. Call these utilities with the new keyword in the global scope.

this.chartConfig = new BarChart("_id","count");

This creates an AmCharts configuration object that looks like this. Setting the value to the below JSON is identical.

{ forWidgetType: 'graph',
  balloon:
   { borderThickness: 0,
     borderAlpha: 0,
     fillAlpha: 0,
     horizontalPadding: 0,
     verticalPadding: 0,
     shadowAlpha: 0 },
  export: { enabled: true, fileName: 'Veracity_data' },
  type: 'XYChart',
  labelsEnabled: false,
  engine: 'amcharts4',
  colors:
   { list:
      [ '#00BBBB',
        '#006E6E',
        '#159800',
        '#001F7C',
        '#1FE200',
        '#0133C8',
        '#00BBBB',
        '#006E6E',
        '#159800',
        '#001F7C',
        '#1FE200',
        '#0133C8',
        '#00BBBB',
        '#006E6E',
        '#159800',
        '#001F7C',
        '#1FE200',
        '#0133C8' ] },
  xAxes:
   [ { id: 'c1',
       type: 'CategoryAxis',
       dataFields: { category: '_id' },
       renderer:
        { minGridDistance: 60,
          grid: { strokeOpacity: 0.05 },
          labels:
           { rotation: 45,
             truncate: true,
             maxWidth: 200,
             verticalCenter: 'top',
             horizontalCenter: 'left' } } } ],
  exporting: { menu: {} },
  yAxes:
   [ { id: 'v1',
       type: 'ValueAxis',
       dataFields: { value: 'count' },
       renderer: { grid: { strokeOpacity: 0.05 } } } ],
  series:
   [ { id: 's1',
       xAxis: 'c1',
       yAxis: 'v1',
       type: 'ColumnSeries',
       name: 'Series Title',
       stacked: false,
       columns: { tooltipText: '{categoryX}:{valueY}' },
       colors:
        { list:
           [ '#00BBBB',
             '#006E6E',
             '#159800',
             '#001F7C',
             '#1FE200',
             '#0133C8',
             '#00BBBB',
             '#006E6E',
             '#159800',
             '#001F7C',
             '#1FE200',
             '#0133C8',
             '#00BBBB',
             '#006E6E',
             '#159800',
             '#001F7C',
             '#1FE200',
             '#0133C8' ] },
       dataFields: { categoryX: '_id', valueY: 'count' } } ],
   }

Notice the final line, dataFields: { categoryX: '_id', valueY: 'count' } } ]. This is where the BarChart constructor uses its parameters. The rest of this is the default configuration for a BarChart in Veracity. It sets up the colors, patterns, themes and legend that are used for all BarCharts in our system. There are a few additional values on this object that are worth discussing:

  • engine - Veracity Learning LRS actually includes several graphing libraries. We've deprecated AmCharts3 and D3, so please always set this to "amcharts4".
  • forWidgetType - this tells the system that this config is intended for "graphs". Widgets can actually have a few other types that are not graphs, like the Table. You don't need to set this. When using the utilities, it's set automatically. This is to prevent assigning a BarChart config to a graph whose static configuration sets the type to "Table"

Chart Config Utilities

  • BarChart (category,value)
    A bar chart where the bars are labeled by the category field, and the value comes from the value field.

  • PieChart (category,value)
    A pie chart where the bars are labeled by the category field, and the value comes from the value field.

  • SerialChart (category,value)
    An XY chart where the X axis is the value of "category", and the Y axis is the value of "value" from each result document.

  • MultilineChart (lines,categoryField)
    An XY chart with multiple lines. categoryField is the name of the X value for each datapoint, and lines is an array of string that represent each Y value. For instance, if the data looks like this:

    [
     {line1:10,line2:20,line3:23,Xval:1},
     {line1:11,line2:24,line3:3,Xval:2}
     ...
     {line1:103,line2:2,line3:365,Xval:10}
    ]
    

    Then to generate a proper multiline chart, you would set the chart config as:

    this.chartSetup = new MultilineChart(["line1","line2","line3"],"Xval");
    
  • ErrorBars (categoryField, valueField, errorField)
    An XY chart that shows crosses to represent a value and a range around the value. categoryField and valueField work just like a BarChart, and errorField represents the range size around valueField in the center.

  • StackedBarChart (categoryField,stacks)
    A bar chart where each bar is subdivided into stacks. CategoryField is the name (and grouping key) for each bar, and stacks is an array of strings that are the keys for the value fields. Data will be automatically processed, so you can provide a series of documents like this:

    [
      {name:"Rob",courseA:10}
      {name:"Tim",courseB:0}
      {name:"Rob",courseB:130}
      {name:"Tim",courseA:50}
    ]
    

    Then to generate a proper stacked bar chart, you would set the chart config as:

    this.chartSetup = new StackedBarChart("name",["courseA","courseB"]);
    
  • Table (column1,column2,...)
    A data table, where each column is one of the parameters from the constructor. This ChartConfig is only useful when the WidgetType is "table".

Other Widget Types

Not every widget needs to be a graph rendered by a chart engine. We have a handful of other Widget Types that render various HTML.

  • Graph
    A graph, rendered by a graph engine (generally "AmCharts4"). Described in detail above.
  • Table
    Renders a data table. Set the chartConfig to new Table(...) when using this type. The constructor for Table is documented above.
  • ProgressChange
    Renders an single large icon, a large value, and a string underneath. Use this to show a single value on the widget. It requires no configuration, but assumes your data is formatted as below. Remember, if the values that are returned from your query don't match this format, you can fix them in the this.exec function. Only the first value in the array is used.
    [
      {
          icon: 'fa-check', //A Font Awesome icon class
          change: '10 Attempts', //The title value
          subtext: '', //A smaller line of text underneath
      },
    ];
    
  • IconList
    A list of several entries, each with an icon, title and subtext.
    [
      {
          icon: 'fa-check', //A Font Awesome icon class
          title: '10 Attempts', //The title value
          subtext: '', //A smaller line of text underneath
          color:"red" //A CSS color value
      }
      ...
    ];
    
  • StatementViewer
    Renders statements with a special renderer. Each document in the result set should be a full xAPI statement.

Using ElasticSearch

You can use ElasticSearch instead of MongoDB for your queries. To do so, you should extend your Processor from a different base class, ElasticAnalyticProcessor. When using this base class, the value of this.pipeline is meaningless. Instead, you must populate this.query and this.aggregation. The this.query value is an array of ElasticSearch DSL fragments, and this.aggregation is a full ElasticSearch aggregation. Note that you must include an aggregation. The results from this aggregation are piped into your map,filter, and exec functions.

Be sure to initialize the query with this.query = this.commonQueryStages({ range: true }); if you want to obey the dashboard global time range.

Using ElasticSearch is VASTLY faster for most common queries and analytics, so prefer this option unless you need to execute complex queries that can only be represented as a MongoDB Aggregation Pipeline.

class ESCompletionsByStudent extends ElasticAnalyticProcessor {
    constructor(parameters, db, lrs) {
        super(parameters, db, lrs);

        this.query = this.commonQueryStages({ range: true });

        if (this.param('verb')) {
            this.query.push({
                term: {
                    'verb.id': this.param('verb'),
                },
            });
        }
        if (this.param('object')) {
            this.query.push({
                term: {
                    'object.id': this.param('object'),
                },
            });
        }
        this.aggregation = {
            terms: {
                field: 'actor.id',
                size: 10,
                order: {
                    _count: 'desc',
                },
            },
        };

        // find the name of each actor by actorID
        if (this.param('verb')) this.map = multiMap(mapToActorNameAsync('key', 'actorName'), mapToVerbDisplay('verb', 'verb'));
        if (!this.param('verb')) this.map = mapToActorNameAsync('key', 'actorName');

        this.chartSetup = new BarChart('actorName', 'doc_count');
   
    }

Overriding this.compute

If you wish not to use the MongoDB or ElasticSearch interfaces we provide, you can override the compute function. This function is async and should return an array of values that will be piped into map,filter, and exec.

this.compute = async function GetDataFromAnotherServer()
{
    let url = this.param('url');
    let request = require('request');
    let values = await request.get(url);
    return values;
}

This notional example returns some value from another server, where the server address is a parameter. You can use this method to generate any analysis algorithm you wish.