Customizing Reports

Reports are fully customizable and Golden Helix is happy to offer customization services. For more information contact your account manager.

Users with JavaScript development experience can extensively modify templates. To access the development console click the Gear menu and select Developer JavaScript Console. This will bring up a WebKit-like dev console. Click on the icon in the lower left corner to open the JavaScript console. Note that after making any changes to JavaScript files you will need to select Reload Report Template from the Gear menu to update the current namespace with those changes.

Templates consist of three main parts:

  • Layout Object: In the report.js file a variable layout is defined. This is a object which defines the form that is laid out in the report tab.
  • Template: Each template contains a template file which defines the layout of the rendered report. Handlebars is used as the templating language.
  • Render Function: The render function in report.js combines the information from the form and sources in VarSeq to create a context to pass to the compiled Handlebars template for rendering.

VarSeq provides four main objects to the JavaScript namespace which can be accessed via functions on the form object.

  • form.getProjectData(): Returns an object which corresponds to data in the project. The samples property contains the meta-data associated with the samples in the project. The sources property contains meta-data about the sources used during annotation. The variants property contains the annotated variants in the selected set of variants.
  • form.getInputData(): Returns an object containing the user input from the form in the report tab.
  • form.getParamsData() Returns an object containing the user input from the template configuration dialog.
  • form.templateText(): Returns a string containing the handlebars template. You must compile this string into a template prior to passing the context to render report.

When the form is created in the report tab, each of the items in the layout object tries to call it’s autofill function. If the form item supports the choices property, and the choices are specified as a function, this function will be called as well. The choices function is called before the autofill function so that the autofill function may make use of the newly set choices. Each autofill function is passed the project, input, and variants objects as described above. Additionally record set forms are passed a fourth argument containing the variant used to construct the form. Finally the last argument passed to the autofill and choices function is the thisFrom object. The this form object contains the state of all of the form objects in the current form. The thisForm object allows form items to change based on updates to other items in the same form. The function should return the autofill value to populate the associated widget with.

Whenever the Create Report icon is pressed the render function is called. It is passed all four of the previously described variable as arguments. The render function should return the fully rendered output string (i.e. valid HTML).

Additional JavaScript helper functions have been included in scripts/templateUtils.js. The lodash and string.js libraries are included by default as well. You can include any other client-side libraries that do not require a DOM by importing them at the top of report.js. Note, that this import is not a true ES6 style import; it instead prepends the library code prior to evaluation.

Bootstrap is included as a CSS framework.

The Layout

There is a defined list of widgets that VarSeq includes that can be used to construct the report form. All of the available widgets can be found in the following image and are described in detail below.

Widgets Avalable for Report Construction

To add a widget to the form, add the corresponding javascript object to the layout section of the report.js file.

Each of the form widget is divded into sections which are objects with the following properties:

  • id - used to access the object
  • label - the label for the input field in the form in VarSeq
  • context - specifies if the input is either at the sample, variant, or other record level. It will commonly be one of the following values:
    • sample - These widgets provide report fields for the current sample.
    • variant - These widgets are associated with a variant record set. At the top of the section will be a widget to select the “Variant Set” that defines variants for this section. Each selected variant will have all the contained widgets instantiated for it. Widgets in these sections will have an extra variant value passed to its autoFill function.
    • cnv - Similar to variant but contains selected records from a CNV table.
    • coverageregion - Similar to variant but contains selected records from a Coverage Region table.

Each item corresponds to a widget which will be created in the form. These widget have share the following common properties:

  • id - used to retrieve data
  • label - provides a label in the input form
  • doc - help string displayed on hover of i icon in input form
  • required - boolean denoting if widget must be filled out by end user
  • type - defines the type of widget to be displayed using the following strings:
  • autoFill - function which returns a value to dynamically fill the widget’s input. Form has different parameters passed to it based on the context of the container for the widget.
    • sample context arguments include project, input and thisForm
    • variant or other non-sample context arguments include project, input, variant and thisForm
  • default - static default value for a widget (should not be used with autoFill)

Additionally each widget has some unique values. These parameters allow you to customize the widgit behavior.

Short Text Edit

The short text edit widget provides a sigle line for text entry. The specification of completion values allow for quick editing when the value is one of several common values.

Properties:

  • value - text in line edit
  • placeholder - text in line edit when empty
  • completions - if specified this must be a list of values, or a function which return a list of value. The list of values are then used to populate the autocompleter. If a function is specified it will be called each time that the value of the widget changes. The arguments passed to the function are the same as the arguments that are passed to the autofill function with the addition of the current widget value, which is passes as the last argument.

Example in report layout:

{
  id: 'shortTextId',
  label: 'Short Text Label',
  type: 'shortText',
  placeholder: "place holder text",
  completions: ['one', 'two', 'three']
}

Multi Line Edit

Provides free form text edit widget with an adjustable size.

Properties:

  • value - test in line edit
  • heightInLines - (integer) How many lines to provide for text input
{
  id:'longTextId',
  label: 'Long Text',
  type: 'longText',
  heightInLines: 5
}

Rich Text Edit

Provides the same features as the Multi Line Edit widget, with the addition of rich text formating.

Properties:

  • value - text in line edit
  • heightInLines - (integer) how many lines to provide for input
{
  id: 'richText',
  label: 'Rich Text',
  type:'richTextEdit',
  heightInLines: 6
}

Choice

Creates a widget with a dropdown menu with multiple choices. An unset value may also be specified to leave the entry as missing.

Properties:

  • value - enum
  • unsetName - When set this provides an option for an ‘unsetName’ for Missing.
  • choices - String list of the posible choices, or a function which returns a string list of choices. This function will be passed the same values that are passed to autofill function.
  • symbols - [optional] choices are used for display only and these strings will be used to represent them. The length of this string must match the number of values in th choices list.
{
  id: 'choiceId',
  label: 'Choice',
  type: 'choice',
  unsetName: 'Missing Choice',
  choices: [ 'choice 1' , 'choice 2','choice 3'],
}

ChoiceText

Creates a widget with a text input and predefined choices available from a dropdown menu. An unset value may also be specified to leave the entry as missing.

This differs from the Choices widget by allowing free form text input as well as choices that are defined by autofill-like function.

Properties:

  • value - enum
  • unsetName - When set this provides an option for an ‘unsetName’ for Missing.
  • choices - String list of the posible choices, or a function which returns a string list of choices. This function will be passed the same values that are passed to autofill function.
{
  id: 'transcriptName',
  label: 'Reported Transcript',
  type: 'choiceText',
  unsetName: 'Not Reported',
  choices: function(project, input, variant, thisForm) {
    return _.chain(variant).get('transcript_2').pluck('TranscriptName').value();
  },
  autoFill: function(project, input, variant) {
    var clinicallyRelevant = _.chain(variant)
                              .get('transcript')
                              .get('TranscriptNameClinicallyRelevant')
                              .value();
    return clinicallyRelevant.length > 0 ? clinicallyRelevant[0] : '';
  }
}

Integer Edit

This widget allows for free form entry of integers within a specified minimum and max value.

Properties:

  • value : integer
  • placeholder : text placed in the line edit when empty
  • min - minimum value allowed
  • max - maximum value allowed
  • step - (int) change made to the value with the up and down arrow keys
  • completions - if specified this must be a list of values, or a function which return a list of value. The list of values are then used to populate the autocompleter. If a function is specified it will be called each time that the value of the widget changes. The arguments passed to the function are the same as the arguments that are passed to the autofill function with the addition of the current widget value, which is passes as the last argument.
{
  id: 'intId',
  label: 'Input an Integer',
  type: 'int',
  placeholder: 'enter and int value',
  min: 0,
  max: 10,
  step: 2
}

Real Edit

This widget allows for free form entry of real values within a specified minimum and max value.

Properties:

  • value : double
  • placeholder : text placed in the line edit when empty
  • min - minimum value allowed
  • max - maximum value allowed
  • step - (double) change made to the value with the up and down arrow keys
  • completions - if specified this must be a list of values, or a function which return a list of value. The list of values are then used to populate the autocompleter. If a function is specified it will be called each time that the value of the widget changes. The arguments passed to the function are the same as the arguments that are passed to the autofill function with the addition of the current widget value, which is passes as the last argument.
{
  id: 'realId',
  label: 'Insert a Real Value',
  type: 'real',
  placeholder: 'enter a real value',
  min: 0,
  max: 10,
  step: 0.5
}

Static Text

This provides a text value which can be specified with the ‘default’ parameter, or read in dynamically with the autofill parameter.

Properties:

  • value : string value
  • staticValue: string in the label for the value
{
  id: 'staticTextId',
  label: 'Static Text',
  type: 'staticText',
  default: 'Static text value'
}

File Select

This widget allows you to select an image to include in the report.

Properties:

  • value : byte array
  • width : target width in pixels
  • height : target height in pixels
{
  id: 'fileSelectId',
  label: 'File Select',
  type:'filePath',
  width: 300,
  height: 400
}

Date Time Edit

The date time widget can be used to specify when an event occurred. The format for the value in this field can be specified with the following symbols.

Format:

d the numeric day
dd the numeric day with a leading zero
ddd the abbreviated day name
dddd the full day name
M the numeric month
MM the numeric month with a leading zero
MMM the abbreviated month name
MMMM the full month name
yy the last two digits of the year
yyyy the full numeric year

Properties:

  • value: (int) unix epoch time in ms
  • format: datetime format string
{
  id: 'dateTimeEditId',
  label: 'Date Time',
  type: 'dateTimeEdit',
  format: 'YYYYMMDD',
  showPopup: true
}

Note the autoFill function for this widget expects the returned date to be in the msec since Unix epoch format in local time zone. It is suggested to simply return the toTime() value from a JavaScript “Date” object. For example:

{
  id: 'dob',
  label: 'Date of Birth',
  doc: 'Enter patient birthday',
  type: 'dateTimeEdit',
  autoFill: function(project, input, thisForm) {
    # Assuming DOB field as added to the sample on import in form YYYY-MM-DD
    var date = project.samples.Current.entity.DOB;
    var b = date.split(/\D/);
    return (new Date(b[0], b[1]-1, b[2])).getTime();
  }
}

Sign Off

The signoff provides the first and last name of the user who clicks the signoff button in VarSeq. If the report is not signed off the value returned by this widget will be an empty list.

Properties:

  • value : list of values if singed off [ first name, last name, signoff date (unix epoch time in ms)
{
  id: 'signOffId',
  type: 'signOff',
  label: 'Sign Off'
}

The Render Function

The render function of VarSeq is given the current project data, input context, report parameters, and the template text and is expected to return the rendered report as the returned string value.

var render = function(templateText, // form.templateText()
                      params,       // form.getParamsData()
                      project,      // form.getProjectData()
                      input         // form.getInputData()
                      ) {


  // take the values from the layout widgets and map them
  // back to the layout widgets
  var labeledInput = templateUtils.labelInput(layout, input);

  // combine the labeled inputs with the values loaded from
  // the report's param file
  var ctx = _.extend(labeledInput, params);

  // Join the project state data with the layout widget data
  ctx = preprocessContext(ctx, project);

  //  Process the form data with the project data to set the values
  //  used in the report template
  var records = _.get(input,
                      'variants.records',
                      []).concat(_.get(input, 'incidental.records'));
  var variantList = _.get(ctx, 'variants', []);
  ctx.references = templateUtils.getReferences(records, project.variants);
  ctx.variantsByGene = groupVariantsByGene(variantList);
  ctx.geneSummary = createGeneSummary(params, project, input);
  ctx.reportName = project.samples.Current.entity.Name + " Report";


  // Load the template into handlebars
  var template = Handlebars.compile(templateText, {noEscape: true});

  //  render the report, and return the completed html
  return template(ctx);
};

The render function is located in the report.js file. It can be replaced with a custom render function or adjusted to meet the required output.

Example: Adding a Data Field to a Report

To illustrate how templates are organized, let’s walk through a complete example where a field is added to the report input form and the data is used in the final report.

First, we need to add a widget to the input form. We can do this by modifying the layout variable in report.js.

Let’s make a widget for an alphanumeric lab id. We’ll assume that this id will be defined in a TSV file used during the import process. We’ll place this widget in a lab reference section. To accomplish this we’ll insert following object in the layout array. Its position in the array defines its position in the VarSeq form.

{
    id: 'labRef',
    label: 'Laboratory Reference Information',
    context: 'sample',
    items: [
      {
        id: 'labId',
        type: 'shortText',
        label: 'Identification Number',
        doc: 'Internal Reference Number',
        required: true,
        autoFill: function(project, input) { return _.get(project,
          'samples.Current.importedId', '');
        }
      }
    ]
}

One thing to note is how the autoFill function is written. In order to be robust to different project setups, it is good practice to use lodash’s get function which tolerates null references and return a default value rather than throwing a runtime error. In this case, our default value is simply the empty string.

When developing more complex autoFill functions it can be useful to execute them from the developer console. To do so enter layout into the console and you’ll see the layout object that you defined in your report.js file. Find the autoFill property on the layout item of interest and call it with the correct arguments. In our example I can call:

layout[3].items[0].autoFill(form.getProjectData(), form.getInputData());

The function will return whatever the function will fill into the input text box.

After adding this to our layout array, we now need to reload the report form by clicking on the Gear icon and selecting Reload Report Template. You should now see a new widget in your layout. Type something in the box and open a developer console via the Gear Menu. Expand the console by clicking on the button on the lower left. In the console type:

> form.getInputData().labRef.labId

You should see the text you just typed into the box in VarSeq. In fact all the input values are returned by the getInputData() function on the form object. Try typing:

> form.getInputData()

In the console you’ll see an navigable object containing your input values. Similarly, you can inspect the values in the template configuration dialog with the function getParamsData() and project specific data including annotations on the current variants with getProjectData().

So the next task is to get our labId into our template. To pass this value to the template, we bind it to the context which in turn is passed to the compiled template. For more information on how this process works refer to the Handlebars documentation.

To bind the value to the context, navigate to the render function. After the line:

var ctx = _.extend(labeledInput, params);

Add:

ctx.labId = _.get(input, 'labRef.labId', '');

This line assigns the output of lodash’s get function to the property labId on the ctx object. For more information on this function see the lodash documentation.

Now when the ctx object is passed as an argument to the compiled template in the line:

return template(ctx);

the labId property can be used in the template.

Finally, we need to edit the actual template file to use this new property. Open the template.tpl file that exists next to report.js. You’ll notice this file contains what looks like HTML with a lot of curly braces. These curly braces allow us to access the properties on the ctx object passed to the template.

Let’s add the lab id to the existing references information. Navigate to the section that looks like:

<div class="col-xs-5 reference-information">
  <h4>Reference Information</h4>
  <dl class="dl-horizontal">
    {{#eachInMap reference}}
    <dt>{{value.label}}</dt><dd>{{value.data}}</dd>
    {{/eachInMap}}
  </dl>
</div>

Modify it so it looks like:

<div class="col-xs-5 reference-information">
  <h4>Reference Information</h4>
  <dl class="dl-horizontal">
    {{#eachInMap reference}}
    <dt>{{value.label}}</dt><dd>{{value.data}}</dd>
    {{/eachInMap}}
    <dt>Identification Number:</dt><dd>{{labId}}</dd>
  </dl>
</div>

Now save the file, return to VarSeq, reload the template via the Gear Menu. Make sure to enter some text in the “Identification Number:” form widget and press the Play button to generate the template. You should see the text that you entered in the form as the last item in the Reference Information section in the generate report.

You may have noticed that many of the items in the template take the form of {{foo.bar.data}} and {{foo.bar.label}}. This format comes from the automatic labeling that we do in the render function when we call templateUtils.labelInput. What this utility function does is use the labels we created in the layout variable for the data in input. This provides for consistent labeling between our form and template. It is entirely optional, but it is good to note that we could have used the line:

<dt>{{labRef.labId.label}}</dt><dd>{{labRef.labId.data}}</dd>

and generated the same output. This especially useful when we have many items in a section and want to add them to the template without writing all the HTML by hand. We could use the eachInMap custom Handlebars helper that is in the report.js function. Using this syntax we can write:

{{#eachInMap labRef}}
<dt>{{value.label}}</dt><dd>{{value.data}}</dd>
{{/eachInMap}}

This snippet will iterate over all the values in the labRef object that was bound to the context. For each property in the the object, the eachInMap helper will place the property name in the dt tag and value for the property in the dd tag. This is purely for convenience and is not necessary for simple template modification.

You have now seen how data flows through the report template system. This template system is pure JavaScript and users can access and modify data anyway prior to sending it off to the template. We encourage users to get familiar with the objects passed to the render function (templateText, params, project, and input). These variables can be accessed in the developer console by using the functions templateText, getParamsData, getProjectData, and getInputData on the form object. Accessing the data contained in these objects is at the core of the report building process. After you find the information you need, just build up a context with that data and pass it to your template.

Example: Selecting a Variant Transcript

To illustrate how the choices and autoFill functions work lets create an example which allows users to select from the list of transcripts for a selected variant.

To make the transcripts available for the variants in the project we will make RefSeq Genes a required annotation. To do this add transcript to the requiredSources variable in the report.

var requiredSources = ["transcript"];

This will prompt the user with a red warning triangle if they add the report to a project that hasn’t already been annotated with the gene transcript algorithm.

Once the variants have been annotated with the gene transcripts the variants as they are passed into the form will have properties for each of the three field groups that are added when the gene annotation is performed:

  • transcript - RefSeq Genes 105 Interim v1, NCBI
  • transcript_2 - Transcript Interactions RefSeq Genes 105 Interim v1, NCBI
  • transcript_3 - Aux Fields RefSeq Genes 105 Interim v1, NCBI

We can examine one of these the value of one of the variants by adding a variant to the report record set, and opening the “Developer JavaScript Console” for the report. Next get the project variants by calling the form.getProjectData() function. This will output an object which when expanded will look like the following:

**Developer JavaScript Console**

As you can see, in this project there are two variants in the projectData. The variant with the key of 4 is expanded. In this variant object’s attributes we can see there is a property for each of the fields added by the gene annotation (transcript, transcript_2, transcript_3). By expanding these properties we can see the values of the fields in each of these source groups.

Here we can see that variant 4 has four different transcripts represented by the four objects in the transcript_2 list. The first transcript in this example is shown expaned above with a HGVS.c notation of NM_001281493.1:c.2532+14A>T.

In this example we are going to use the HGVSc field values to identify the different transcripts. The user will select the transcript from from a choice widget. We will then update other widgets in the form based on the users selection.

The first step will be to create the choice widget which allows the user to select the transcript. We add this to the variant section of the layout to create a transcript selector for each of the variants that is added to the report, and add a choices function to populate the widget with the HGVS.c values for the current variant.

{
   id: 'variants',
   label: 'Genetic Variants',
   context: 'variant',
   items: [
     {
       id: 'transcript_choice',
       label: 'HGVS.c',
       type: 'choice',
       choices: function(project, input, variant){
         return _.chain(variant).get('transcript_2').pluck('HGVSc').value();
       },
       required: true,
       doc: "The HGVS.c notation of the selected transcript"
     },

Next we will create the several label widgets to display information about the users transcript selection.

{
  id: 'dist_of_tx',
  label: 'Dist of Tx',
  type: 'label',
  autoFill: function(project, input, variant, formState) {
    // lookup the current selection of the 'transcript_choice' widget
    var hgvsc = formState['transcript_choice'];

    // using the selected hgvs.c notionation find the transcript object
    // that was selected in this form's variant
    var transcript_2 = _.chain(variant).get('transcript_2');
    var selected_tx = transcript_2.find({'HGVSc' : hgvsc}).value();

    //return the DistofTx of selected value for the current selected value
    return selected_tx['DistofTx'];
  }
},
{
  id: 'ontology',
  label: 'Ontology',
  type: 'label',
  autoFill: function(project, input, variant, formState) {
    var hgvsc = formState['transcript_choice'];
    var transcript_2 = _.chain(variant).get('transcript_2');
    var selected_tx = transcript_2.find({'HGVSc' : hgvsc}).value();
    return selected_tx['SequenceOntology'];
  }
},

In each of the above widgets the autofill function is used to set the state of the label widget based on state of the transcript_choice widget. The HGVS.c notation selected in the HGVS.c choices widget is stored in the formState. Each widget following the HGVS.c choices widget can look up the current selected value and use it to set it’s state in it’s autofill function.

**Transcript Selection Widgets**

This will create a form with three widgets for each of the variants selected. Each form will have three widgets an HGVS.c widget, a Dist of Tx, and an Ontology widget, which are updated depending on the selected transcript.