Source: src/js/views/MdqRunView.js

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;
});