Source: src/js/models/MetricsModel.js

define(["jquery", "underscore", "backbone"], function ($, _, Backbone) {
  "use strict";

  /**
   * @class Metrics
   * @classdesc A single result from the DataONE Metrics Service
   * @classcategory Models
   * @extends Backbone.Model
   * @constructor
   */
  var Metrics = Backbone.Model.extend(
    /** @lends Metrics.prototype */ {
      /**
       * The name of this Model
       * @type {string}
       */
      type: "Metrics",

      defaults: {
        metricRequest: null,
        startDate: null,
        endDate: null,
        results: null,
        resultDetails: null,
        pid_list: null,
        url: null,
        filterType: null,

        // metrics and metric Facets returned as response from the user
        // datatype: array
        citations: null,
        views: null,
        downloads: null,
        months: null,
        country: null,
        years: null,
        repository: null,
        award: null,
        datasets: null,

        // Total counts for metrics
        totalCitations: null,
        totalViews: null,
        totalDownloads: null,

        // flag to send POST request to the metrics service
        useMetricsPost: false,

        // colelctionQuery for Portal Objects
        filterQueryObject: null,
        forwardCollectionQuery: false,

        metricsRequiredFields: {
          metricName: true,
          pid_list: true,
        },
      },

      metricRequest: {
        metricsPage: {
          total: 0,
          start: 0,
          count: 0,
        },
        metrics: ["citations", "downloads", "views"],
        filterBy: [
          {
            filterType: "",
            values: [],
            interpretAs: "list",
          },
          {
            filterType: "month",
            values: [],
            interpretAs: "range",
          },
        ],
        groupBy: ["month"],
      },

      /**
       * Initializing the Model objects pid and the metricName variables.
       * @param {object} options
       */
      initialize: function (options) {
        if (options && options.pid_list !== "undefined") {
          this.set("pid_list", options.pid_list);
          this.set("filterType", options.type);
        }
        this.set("startDate", "01/01/2012");

        // overwrite forwardCollectionQuery flag
        this.set(
          "forwardCollectionQuery",
          MetacatUI.appModel.get("metricsForwardCollectionQuery"),
        );

        // url for the model that is used to for the fetch() call
        this.url = MetacatUI.appModel.get("metricsUrl");
      },

      /**
       * Overriding the Model's fetch function.
       */
      fetch: function () {
        var fetchOptions = {};
        this.metricRequest.filterBy[0].filterType = this.get("filterType");
        this.metricRequest.filterBy[0].values = this.get("pid_list");

        // TODO: Set the startDate and endDate based on the datePublished and current date
        // respctively.
        this.metricRequest.filterBy[1].values = [];
        this.metricRequest.filterBy[1].values.push(this.get("startDate"));
        this.metricRequest.filterBy[1].values.push(this.getCurrentDate());

        // set custom request settings if we're forwarding a CollectionQuery for a portal
        if (
          this.get("forwardCollectionQuery") &&
          this.get("filterType") === "portal" &&
          this.get("filterQueryObject") != undefined &&
          typeof this.get("filterQueryObject") === "object"
        ) {
          var filterQueryObject = this.get("filterQueryObject");
          if (
            filterQueryObject.filterType != undefined &&
            filterQueryObject.filterType == "query"
          ) {
            if (
              Array.isArray(filterQueryObject.values) &&
              filterQueryObject.values.length > 0
            ) {
              // check if query object exists
              var queryInserted =
                this.metricRequest.filterBy.indexOf(filterQueryObject);

              // Performing HTTP POST?
              if (this.get("useMetricsPost")) {
                // insert query object
                if (queryInserted < 0) {
                  this.metricRequest.filterBy.push(filterQueryObject);
                }

                fetchOptions = _.extend({
                  data: JSON.stringify(this.metricRequest),
                  type: "POST",
                  timeout: 300000,
                });
              } else {
                // insert query object
                var collectionQuery = filterQueryObject.values[0];
                if (collectionQuery.length < 1000 && queryInserted < 0) {
                  this.metricRequest.filterBy.push(filterQueryObject);
                }

                // set the fetch options for
                var model = this;
                fetchOptions = _.extend({
                  data: "metricsRequest=" + JSON.stringify(this.metricRequest),
                  timeout: 300000,
                  // on error recursively call fetch, but this time use POST
                  error: function (response) {
                    model.set("useMetricsPost", "true");
                    model.fetch();
                  },
                });
              }
            }
          }
        }

        // check if we need to set fetchOptions
        if (Object.keys(fetchOptions).length === 0) {
          if (this.get("useMetricsPost")) {
            fetchOptions = _.extend({
              data: JSON.stringify(this.metricRequest),
              type: "POST",
              timeout: 300000,
            });
          } else {
            fetchOptions = _.extend({
              data: "metricsRequest=" + JSON.stringify(this.metricRequest),
              timeout: 300000,
            });
          }
        }

        //This calls the Backbone fetch() function but with our custom fetch options.
        return Backbone.Model.prototype.fetch.call(this, fetchOptions);
      },

      /**
       * Get's a string version of today's date
       * @return {string}
       */
      getCurrentDate: function () {
        var today = new Date();
        var dd = today.getDate();
        var mm = today.getMonth() + 1; //January is 0!

        var yyyy = today.getFullYear();
        if (dd < 10) {
          dd = "0" + dd;
        }
        if (mm < 10) {
          mm = "0" + mm;
        }
        var today = mm + "/" + dd + "/" + yyyy;
        return today;
      },

      /**
       * Parsing the response for setting the Model's member variables.
       */
      parse: function (response) {
        var metricsObject = {
          metricRequest: response.metricsRequest,
          citations: response.results.citations,
          views: response.results.views,
          downloads: response.results.downloads,
          months: response.results.months,
          country: response.results.country,
          resultDetails: response.resultDetails,
          datasets: response.results.datasets,
        };

        if (response.results.citations != null) {
          metricsObject["totalCitations"] = response.results.citations.reduce(
            function (acc, val) {
              return acc + val;
            },
            0,
          );
        } else {
          metricsObject["totalCitations"] = 0;
        }

        if (response.results.downloads != null) {
          metricsObject["totalDownloads"] = response.results.downloads.reduce(
            function (acc, val) {
              return acc + val;
            },
            0,
          );
        } else {
          metricsObject["totalDownloads"] = 0;
        }

        if (response.results.views != null) {
          metricsObject["totalViews"] = response.results.views.reduce(function (
            acc,
            val,
          ) {
            return acc + val;
          }, 0);
        } else {
          metricsObject["totalViews"] = 0;
        }

        //trim off the leading zeros and their corresponding months
        if (response.results.months != null) {
          // iterate all the metrics objects and remove the entry if the counts are 0
          for (var i = 0; i < metricsObject["months"].length; i++) {
            if (
              metricsObject["citations"] != null &&
              metricsObject["views"] != null &&
              metricsObject["downloads"] != null
            ) {
              if (
                metricsObject["citations"][i] == 0 &&
                metricsObject["views"][i] == 0 &&
                metricsObject["downloads"][i] == 0
              ) {
                metricsObject["months"].splice(i, 1);
                metricsObject["citations"].splice(i, 1);
                metricsObject["views"].splice(i, 1);
                metricsObject["downloads"].splice(i, 1);

                // if country facet was part of the request; update object;
                if (metricsObject["country"] != null) {
                  metricsObject["country"].splice(i, 1);
                }

                // modified array size; decrement the counter;
                i--;
              } else {
                break;
              }
            }
          }
        }

        return metricsObject;
      },
    },
  );
  return Metrics;
});