Source: src/js/views/MdqRunView.js

/*global define */
define(['jquery', 'underscore', 'backbone', 'd3',
  "models/SolrResult",
  'DonutChart', 'views/CitationView',
  'text!templates/mdqRun.html', 'text!templates/mdqSuites.html', 'text!templates/loading-metrics.html', 'collections/QualityReport'],
  function($, _, Backbone, d3,
    SolrResult,
    DonutChart, CitationView,
    MdqRunTemplate, SuitesTemplate, LoadingTemplate, QualityReport) {
  'use strict';

  /**
  * @class MdqRunView
  * @classdesc A view that fetches and displays a Metadata Assessment Report
  * @classcategory Views
  * @name MdqRunView
  * @extends Backbone.View
  * @constructs
  */
  var MdqRunView = Backbone.View.extend(
    /** @lends MdqRunView.prototype */{

    el: '#Content',

    events: {
      "change #suiteId" : "switchSuite"
    },

    url: null,
    pid: null,
    /**
    * The currently selected/requested suite
    * @type {string}
    */
    suiteId: null,
    /**
    * The list of all potential suites for this theme
    * @type {string[]}
    */
    suiteIdList: [],
    loadingTemplate: _.template(LoadingTemplate),
    template: _.template(MdqRunTemplate),
    breadcrumbContainer: "#breadcrumb-container",

    /**
     * A JQuery selector for the element in the template that will contain the loading
     * image
     * @type {string}
     * @since 2.15.0
     */
    loadingContainer: "#mdqResult",

    initialize: function () {

    },

    switchSuite: function(event) {
      var select = $(event.target);
      var suiteId = $(select).val();
      MetacatUI.uiRouter.navigate("quality/s=" + suiteId + "/" + encodeURIComponent(this.pid), {trigger: false});
      this.suiteId = suiteId;
      this.render();
      return false;
    },

    render: function () {

      var viewRef = this;

      // The suite use for rendering can initially be set via the theme AppModel.
      // If a suite id is request via the metacatui route, then we have to display that
      // suite, and in addition have to display all possible suites for this theme in
      // a selection list, if the user wants to view a different one.
      if (!this.suiteId) {
        this.suiteId = MetacatUI.appModel.get("mdqSuiteIds")[0];
      }

      this.suiteIdList = MetacatUI.appModel.get("mdqSuiteIds");
      this.suiteLabels = MetacatUI.appModel.get("mdqSuiteLabels");
      //this.url = this.mdqRunsServiceUrl + "/" + this.suiteId + "/" + this.pid;

      // Insert the basic template
      this.$el.html(this.template({}));
      // Show breadcrumbs leading back to the dataset & data search
      this.insertBreadcrumbs();
      // Insert the loading image
      this.showLoading();

      if (!this.pid){
        var searchLink = $(document.createElement("a"))
          .attr("href", MetacatUI.root + "/data")
          .text("Search our database");
        var message = $(document.createElement("span"))
          .text(" to see an assessment report for a dataset")
          .prepend(searchLink);
        this.showMessage(message, true, false)
        return
      }

      // Fetch a quality report from the quality server and display it.
      var qualityUrl = MetacatUI.appModel.get("mdqRunsServiceUrl") + viewRef.suiteId + "/" + viewRef.pid;
      var qualityReport = new QualityReport([], { url: qualityUrl, pid: viewRef.pid });
      qualityReport.fetch({ url: qualityUrl });

      this.listenToOnce(qualityReport, "fetchError", function() {
        // Inspect the results to see if a quality report was returned.
        // If not, then submit a request to the quality engine to create the
        // quality report for this pid/suiteId, and inform the user of this.
        var msgText;
        console.log("Error status: " + qualityReport.fetchResponse.status);
        if(qualityReport.fetchResponse.status == 404) {
          msgText = "The assessment report for this dataset is not ready yet. Try checking back in 24 hours to see these results.";
        } else {
          msgText = "There was an error retrieving the assessment report for this dataset.";
          if(typeof qualityReport.fetchResponse.statusText !== 'undefined' && typeof qualityReport.fetchResponse.status !== 'undefined') {
            if(qualityReport.fetchResponse.status != 0)
              msgText += "Error details: " + qualityReport.fetchResponse.statusText;
          }
        }
        this.showMessage(msgText);
      }),

      this.listenToOnce(qualityReport, "fetchComplete", function() {
        var msgText;
        if(qualityReport.runStatus != "success") {
          if(qualityReport.runStatus == "failure") {
              msgText = "There was an error generating the assessment report. The Assessment Server reported this error: " + qualityReport.errorDescription;
          } else if (qualityReport.runStatus == "queued") {
              msgText = "The assessment report is in the Assessment Server queue to be generated. It was queued at: " + qualityReport.timestamp;
          } else {
              msgText = "There was an error retrieving the assessment report."
          }
          this.showMessage(msgText);
          return
        } else {
          viewRef.hideLoading();
        }

        // Filter out the checks with level 'METADATA', as these checks are intended
        // to pass info to metadig-engine indexing (for search, faceting), and not intended for display.
        qualityReport.reset(_.reject(qualityReport.models, function (model) {
            var check = model.get("check");
            if (check.level == "METADATA") {
                return true
            } else {
                return false;
            }
        }));

        var groupedResults = qualityReport.groupResults(qualityReport.models);
        var groupedByType = qualityReport.groupByType(qualityReport.models);

        var data = {
              objectIdentifier: qualityReport.id,
              suiteId: viewRef.suiteId,
              suiteIdList: viewRef.suiteIdList,
              suiteLabels: viewRef.suiteLabels,
              groupedResults: groupedResults,
              groupedByType: groupedByType,
              timestamp: _.now(),
              id: viewRef.pid,
              checkCount: qualityReport.length
        };

        viewRef.$el.html(viewRef.template(data));
        viewRef.insertBreadcrumbs();
        viewRef.drawScoreChart(qualityReport.models, groupedResults);
        viewRef.showCitation();
        viewRef.show();
        viewRef.$('.popover-this').popover();
      });

    },

    /**
     * Updates the message in the loading image
     * @param {string} message The new message to display
     * @param {boolean} [showHelp=true] If set to true, and an email contact is configured
     * in MetacatUI, then the contact email will be shown at the bottom of the message.
     * @param {boolean} [showLink=true] If set to true, a link back to the dataset will be
     * appended to the end of the message.
     * @since 2.15.0
     */
    showMessage : function(message, showHelp = true, showLink = true){
      try {

        var view = this;
        var messageEl = this.loadingEl.find(".message");

        if(!messageEl){
          return
        }

        // Update the message
        messageEl.html(message);

        // Create a link back to the data set
        if(showLink){
          var viewURL = "/view/" + encodeURIComponent(this.pid);
          var backLink = $(document.createElement("a"))
            .text(" Return to the dataset")
          backLink.on("click", function(){
            view.hideLoading();
            MetacatUI.uiRouter.navigate(viewURL, { trigger: true, replace: true });
          })
          messageEl.append(backLink)
        }

        // Show how the user can get more help
        if(showHelp){
          var emailAddress = MetacatUI.appModel.get('emailContact')
          // Don't show help if there's no contact email configured
          if(emailAddress){
            var helpEl = $(
              "<p class='webmaster-email' style='margin-top:20px'>" +
              "<i class='icon-envelope-alt icon icon-on-left'></i>" +
              "Need help? Email us at </p>"
            );
            var emailLink = $(document.createElement("a"))
              .attr("href", "mailto:" + emailAddress)
              .text(emailAddress);
            helpEl.append(emailLink)
            messageEl.append(helpEl)
          }
        }
      }

      catch (error) {
        console.log(
          'There was an error showing a message in a MdqRunView' +
          '. Error details: ' + error
        );
      }
    },

    /**
     * Render a loading image with message
     */
    showLoading: function() {
      try {
        var loadingEl = this.loadingTemplate({
          message: "Retrieving assessment report...",
          character: "none",
          type: "barchart"
        });
        this.loadingEl = $(loadingEl)
        this.$el.find(this.loadingContainer).html(this.loadingEl)
      }
      catch (error) {
        console.log(
          'There was an error showing the loading image in a MdqRunView' +
          '. Error details: ' + error
        );
      }
    },

    /**
     * Remove the loading image and message.
     */
    hideLoading: function() {
      try {
        this.loadingEl.remove();
      }
      catch (error) {
        console.log(
          'There was an error hiding a loading image in a MdqRunView' +
          '. Error details: ' + error
        );
      }
    },

    showCitation: function(){

      var solrResultModel = new SolrResult({
        id: this.pid
      });

      this.listenTo(solrResultModel, "sync", function(){
        var citationView = new CitationView({
          model: solrResultModel,
          createLink: false,
          createTitleLink: true
        });

        citationView.render();

        this.$("#mdqCitation").prepend(citationView.el);
      });
      solrResultModel.getInfo();

    },

    show: function() {
      var view = this;
      this.$el.hide();
      this.$el.fadeIn({duration: "slow"});
    },

    drawScoreChart: function(results, groupedResults){

      var dataCount = results.length;
      var data = [
                  {label: "Pass", count: groupedResults.GREEN.length, perc: groupedResults.GREEN.length/results.length },
                  {label: "Warn", count:  groupedResults.ORANGE.length, perc: groupedResults.ORANGE.length/results.length},
                  {label: "Fail", count: groupedResults.RED.length, perc: groupedResults.RED.length/results.length},
                  {label: "Info", count: groupedResults.BLUE.length, perc: groupedResults.BLUE.length/results.length},
              ];

      var svgClass = "data";

      //If d3 isn't supported in this browser or didn't load correctly, insert a text title instead
      if(!d3){
        this.$('.format-charts-data').html("<h2 class='" + svgClass + " fallback'>" + MetacatUI.appView.commaSeparateNumber(dataCount) + " data files</h2>");

        return;
      }

      //Draw a donut chart
      var donut = new DonutChart({
              id: "data-chart",
              data: data,
              total: dataCount,
              titleText: "checks",
              titleCount: dataCount,
              svgClass: svgClass,
              countClass: "data",
              height: 250,
              width: 250,
              keepOrder: true,
              formatLabel: function(name) {
                return name;
              }
            });
      this.$('.format-charts-data').html(donut.render().el);
    },

    insertBreadcrumbs: function(){
      var breadcrumbs = $(document.createElement("ol"))
                  .addClass("breadcrumb")
                  .append($(document.createElement("li"))
                      .addClass("home")
                      .append($(document.createElement("a"))
                          .attr("href", MetacatUI.root? MetacatUI.root : "/")
                          .addClass("home")
                          .text("Home")))
                  .append($(document.createElement("li"))
                      .addClass("search")
                      .append($(document.createElement("a"))
                          .attr("href", MetacatUI.root + "/data" + ((MetacatUI.appModel.get("page") > 0)? ("/page/" + (parseInt(MetacatUI.appModel.get("page"))+1)) : ""))
                          .addClass("search")
                          .text("Search")))
                  .append($(document.createElement("li"))
                                  .append($(document.createElement("a"))
                          .attr("href", MetacatUI.root + "/view/" + encodeURIComponent(this.pid))
                          .addClass("inactive")
                          .text("Metadata")))
                            .append($(document.createElement("li"))
                                  .append($(document.createElement("a"))
                                  .attr("href", MetacatUI.root + "/quality/" + encodeURIComponent(this.pid))
                                  .addClass("inactive")
                                  .text("Assessment Report")));

      this.$(this.breadcrumbContainer).html(breadcrumbs);
    },
  });
  return MdqRunView;
});