define(["jquery", "underscore", "backbone", "promise", "common/QueryService"], (
$,
_,
Backbone,
Promise,
QueryService,
) => {
"use strict";
/**
* @class Stats
* @classdesc This model contains all a collection of statistics/metrics about
* a collection of DataONE objects
* @classcategory Models
* @name Stats
* @augments Backbone.Model
* @class
*/
const Stats = Backbone.Model.extend(
/** @lends Stats.prototype */ {
/**
* Default attributes for Stats models
* @type {object}
* @property {string} query - The base query that defines the data
* collection to get statistis about.
* @property {string} postQuery - A copy of the `query`, but without any
* URL encoding
* @property {boolean} isSystemMetadataQuery - If true, the `query` set on
* this model is only filtering on system metadata fields which are common
* between both metadata and data objects.
* @property {number} metadataCount - The number of metadata objects in
* this data collection @readonly
* @property {number} dataCount - The number of data objects in this data
* collection
* @property {number} totalCount - The number of metadata and data objects
* in this data collection. Essentially this is the sum of metadataCount
* and dataCount
* @property {number|string[]} metadataFormatIDs - An array of metadata
* formatIds and the number of metadata objects with that formatId. Uses
* same structure as Solr facet counts: ["text/csv", 5]
* @property {number|string[]} dataFormatIDs - An array of data formatIds
* and the number of data objects with that formatId. Uses same structure
* as Solr facet counts: ["text/csv", 5]
* @property {string} firstUpdate - The earliest upload date for any
* object in this collection, excluding uploads of obsoleted objects
* @property {number|string[]} dataUpdateDates - An array of date strings
* and the number of data objects uploaded on that date. Uses same
* structure as Solr facet counts: ["2015-08-02", 5]
* @property {number|string[]} metadataUpdateDates An array of date
* strings and the number of data objects uploaded on that date. Uses same
* structure as Solr facet counts: ["2015-08-02", 5]
* @property {string} firstBeginDate - An ISO date string of the earliest
* year that this data collection describes, from the science metadata
* @property {string} lastEndDate - An ISO date string of the latest year
* that this data collection describes, from the science metadata
* @property {string} firstPossibleDate - The first possible date (as a
* string) that data could have been collected. This is to weed out badly
* formatted dates when sending queries.
* @property {object} temporalCoverage A simple object of date ranges (the
* object key) and the number of metadata objects uploaded in that date
* range (the object value). Example: { "1990-2000": 5 }
* @property {number} queryCoverageFrom - The year to start the temporal
* coverage range query
* @property {number} queryCoverageUntil - The year to end the temporal
* coverage range query
* @property {number} metadataTotalSize - The total number of bytes of all
* metadata files
* @property {number} dataTotalSize - The total number of bytes of all
* data files
* @property {number} totalSize - The total number of bytes or metadata
* and data files
* @property {boolean} hideMetadataAssessment - If true, metadata
* assessment scores will not be retrieved
* @property {Image} mdqScoresImage - The Image objet of an aggregated
* metadata assessment chart
* @property {number} maxQueryLength - The maximum query length that will
* be sent via GET to the query service. Queries that go beyond this
* length will be sent via POST, if POST is enabled in the AppModel
* @property {string} mdqImageId - The identifier to use in the request
* for the metadata assessment chart
*/
defaults() {
return {
query: "*:* ",
postQuery: "",
isSystemMetadataQuery: false,
metadataCount: 0,
dataCount: 0,
totalCount: 0,
metadataFormatIDs: [],
dataFormatIDs: [],
firstUpload: 0,
totalUploads: 0,
metadataUploads: null,
dataUploads: null,
metadataUploadDates: null,
dataUploadDates: null,
firstUpdate: null,
dataUpdateDates: null,
metadataUpdateDates: null,
firstBeginDate: null,
lastEndDate: null,
firstPossibleDate: "1800-01-01T00:00:00Z",
temporalCoverage: 0,
queryCoverageFrom: null,
queryCoverageUntil: null,
metadataTotalSize: null,
dataTotalSize: null,
totalSize: null,
hideMetadataAssessment: false,
mdqScoresImage: null,
mdqImageId: null,
// HTTP GET requests are typically limited to 2,083 characters. So
// query lengths should have this maximum before switching over to
// HTTP POST
maxQueryLength: 2000,
};
},
/**
* This function serves as a shorthand way to get all of the statistics
* stored in the model
*/
getAll() {
// Only get the MetaDIG scores if MetacatUI is configured to display
// metadata assesments AND this model has them enabled, too.
if (
!MetacatUI.appModel.get("hideSummaryMetadataAssessment") &&
!this.get("hideMetadataAssessment")
) {
this.getMdqScores();
}
// Send the call the get both the metadata and data stats
this.getMetadataStats();
this.getDataStats();
},
/**
* Queries for statistics about metadata objects
*/
getMetadataStats() {
let query = this.get("query");
let beginDateLimit;
let endDateLimit;
// Collect facet queries in an array we can pass directly
const facetQueries = [];
// How many years back should we look for temporal coverage?
const lastYear =
this.get("queryCoverageUntil") || new Date().getUTCFullYear();
const firstYear = this.get("queryCoverageFrom") || 1950;
const totalYears = lastYear - firstYear;
const today = new Date().getUTCFullYear();
const yearsFromToday = {
fromBeginning: today - firstYear,
fromEnd: today - lastYear,
};
// Determine our year range/bin size
let binSize = 1;
if (totalYears > 10 && totalYears <= 20) {
binSize = 2;
} else if (totalYears > 20 && totalYears <= 50) {
binSize = 5;
} else if (totalYears > 50 && totalYears <= 100) {
binSize = 10;
} else if (totalYears > 100) {
binSize = 25;
}
// Count all the datasets with coverage before the first year in the
// year range queries
beginDateLimit = new Date(
Date.UTC(firstYear - 1, 11, 31, 23, 59, 59, 999),
);
facetQueries.push(
`{!key=<${firstYear}}(beginDate:[* TO ${beginDateLimit.toISOString()}/YEAR])`,
);
// Construct our facet.queries for the beginDate and endDates, starting
// with all years before this current year
let key = "";
for (
let yearsAgo = yearsFromToday.fromBeginning;
yearsAgo >= yearsFromToday.fromEnd && yearsAgo > 0;
yearsAgo -= binSize
) {
// The query logic here is: If the beginnning year is anytime before
// or during the last year of the bin AND the ending year is anytime
// after or during the first year of the bin, it counts.
if (binSize === 1) {
// Querying for just the current year needs to be treated a bit
// differently and won't be caught in our for loop
if (lastYear === today) {
const oneYearFromNow = new Date(Date.UTC(today + 1, 0, 1));
const now = new Date();
facetQueries.push(
`{!key=${lastYear}}(beginDate:[* TO ${oneYearFromNow.toISOString()}/YEAR] AND endDate:[${now.toISOString()}/YEAR TO *])`,
);
} else {
key = today - yearsAgo;
// The coverage should start sometime in this year range or
// earlier.
beginDateLimit = new Date(Date.UTC(today - (yearsAgo - 1), 0, 1));
// The coverage should end sometime in this year range or later.
endDateLimit = new Date(Date.UTC(today - yearsAgo, 0, 1));
facetQueries.push(
`{!key=${key}}(beginDate:[* TO ${beginDateLimit.toISOString()}/YEAR] AND endDate:[${endDateLimit.toISOString()}/YEAR TO *])`,
);
}
}
// If this is the last date range
else if (yearsAgo <= binSize) {
// Get the last year that will be included in this bin
const firstYearInBin = today - yearsAgo;
const lastYearInBin = lastYear;
// Label the facet query with a key for easier parsing Because this
// is the last year range, which could be uneven with the other year
// ranges, use the exact end year
key = `${firstYearInBin}-${lastYearInBin}`;
// The coverage should start sometime in this year range or earlier.
// Because this is the last year range, which could be uneven with
// the other year ranges, use the exact end year
beginDateLimit = new Date(
Date.UTC(lastYearInBin, 11, 31, 23, 59, 59, 999),
);
// The coverage should end sometime in this year range or later.
endDateLimit = new Date(Date.UTC(firstYearInBin, 0, 1));
facetQueries.push(
`{!key=${key}}(beginDate:[* TO ${beginDateLimit.toISOString()}/YEAR] AND endDate:[${endDateLimit.toISOString()}/YEAR TO *])`,
);
}
// For all other bins,
else {
// Get the last year that will be included in this bin
const firstYearInBin = today - yearsAgo;
const lastYearInBin = today - yearsAgo + binSize - 1;
// Label the facet query with a key for easier parsing
key = `${firstYearInBin}-${lastYearInBin}`;
// The coverage should start sometime in this year range or earlier.
// var beginDateLimit = new Date(Date.UTC(today - (yearsAgo -
// binSize), 0, 1));
beginDateLimit = new Date(
Date.UTC(lastYearInBin, 11, 31, 23, 59, 59, 999),
);
// The coverage should end sometime in this year range or later.
endDateLimit = new Date(Date.UTC(firstYearInBin, 0, 1));
facetQueries.push(
`{!key=${key}}(beginDate:[* TO ${beginDateLimit.toISOString()}/YEAR] AND endDate:[${endDateLimit.toISOString()}/YEAR TO *])`,
);
}
}
if (this.get("postQuery")) {
query = this.get("postQuery");
} else if (this.get("searchModel")) {
query = this.get("searchModel").getQuery(undefined, {
forPOST: true,
});
this.set("postQuery", query);
}
const facetFormatIdField = "formatId";
const facetBeginDateField = "beginDate";
const facetEndDateField = "endDate";
const facetRangeField = "dateUploaded";
const opts = {
q: query, // already unencoded
rows: 0,
filterQueries: [
"-formatId:*dataone.org/collections*",
"-formatId:*dataone.org/portals*",
"formatType:METADATA",
"-obsoletedBy:*",
],
statsFields: ["size"],
facets: [facetFormatIdField, facetBeginDateField, facetEndDateField],
facetQueries,
facetLimit: -1,
facetRange: facetRangeField,
facetRangeStart: "1900-01-01T00:00:00.000Z",
facetRangeEnd: new Date().toISOString(),
facetRangeGap: "+1MONTH",
extraParams: [
["f.formatId.facet.mincount", "1"],
["f.formatId.facet.missing", "false"],
["f.beginDate.facet.mincount", "1"],
["f.endDate.facet.mincount", "1"],
["f.beginDate.facet.missing", "false"],
["f.endDate.facet.missing", "false"],
["f.dateUploaded.facet.missing", "true"],
],
};
QueryService.queryWithFetch(opts).then(
this.setMetadataStats.bind(this),
);
},
/**
* Parses and saves the metadata statistics returned from Solr
* @param {object} data The Solr response object
* @see {@link Stats#getMetadataStats}
* @since 2.36.0
*/
setMetadataStats(data) {
const model = this;
if (!data || !data.response || !data.response.numFound) {
// Store falsey data
model.set("totalCount", 0);
model.trigger("change:totalCount");
model.set("metadataCount", 0);
model.trigger("change:metadataCount");
model.set("metadataFormatIDs", ["", 0]);
model.set("firstUpdate", null);
model.set("metadataUpdateDates", []);
model.set("temporalCoverage", 0);
model.trigger("change:temporalCoverage");
} else {
// Save tthe number of metadata docs found
model.set("metadataCount", data.response.numFound);
model.set(
"totalCount",
model.get("dataCount") + data.response.numFound,
);
// Save the format ID facet counts
if (
data.facet_counts &&
data.facet_counts.facet_fields &&
data.facet_counts.facet_fields.formatId
) {
model.set(
"metadataFormatIDs",
data.facet_counts.facet_fields.formatId,
);
} else {
model.set("metadataFormatIDs", ["", 0]);
}
// Save the metadata update date counts
if (
data.facet_counts &&
data.facet_counts.facet_ranges &&
data.facet_counts.facet_ranges.dateUploaded
) {
// Find the index of the first update date
const updateFacets =
data.facet_counts.facet_ranges.dateUploaded.counts;
let cropAt = 0;
for (let i = 1; i < updateFacets.length; i += 2) {
// If there was at least one update/upload in this date range,
// then save this as the first update
if (typeof updateFacets[i] === "number" && updateFacets[i] > 0) {
// Save the first first update date
cropAt = i;
model.set("firstUpdate", updateFacets[i - 1]);
// Save the update dates, but crop out months that are empty
model.set(
"metadataUpdateDates",
updateFacets.slice(cropAt + 1),
);
i = updateFacets.length;
}
}
// If no update dates were found, save falsey values
if (cropAt === 0) {
model.set("firstUpdate", null);
model.set("metadataUpdateDates", []);
}
}
// Save the temporal coverage dates
if (data.facet_counts && data.facet_counts.facet_queries) {
// Find the beginDate and facets so we can store the earliest
// beginDate
if (
data.facet_counts.facet_fields &&
data.facet_counts.facet_fields.beginDate
) {
const earliestBeginDate = _.find(
data.facet_counts.facet_fields.beginDate,
(value) =>
typeof value === "string" &&
parseInt(value.substring(0, 4), 10) > 1000,
);
if (earliestBeginDate) {
model.set("firstBeginDate", earliestBeginDate);
}
}
// Find the endDate and facets so we can store the latest endDate
if (
data.facet_counts.facet_fields &&
data.facet_counts.facet_fields.endDate
) {
let latestEndDate;
const endDates = data.facet_counts.facet_fields.endDate;
const nextYear = new Date().getUTCFullYear() + 1;
let i = 0;
// Iterate over each endDate and find the first valid one.
// (After year 1000 but not after today)
while (!latestEndDate && i < endDates.length) {
let endDate = endDates[i];
if (typeof endDate === "string") {
endDate = parseInt(endDate.substring(0, 3), 10);
if (endDate > 1000 && endDate < nextYear) {
latestEndDate = endDate;
}
}
i += 1;
}
// Save the latest endDate if one was found
if (latestEndDate) {
model.set("lastEndDate", latestEndDate);
}
}
// Save the temporal coverage year ranges
const tempCoverages = data.facet_counts.facet_queries;
model.set("temporalCoverage", tempCoverages);
}
// Get the total size of all the files in the index
if (
data.stats &&
data.stats.stats_fields &&
data.stats.stats_fields.size &&
data.stats.stats_fields.size.sum
) {
// Save the size sum
model.set("metadataTotalSize", data.stats.stats_fields.size.sum);
// If there is a data size sum,
if (typeof model.get("dataTotalSize") === "number") {
// Add it to the metadata size sum as the total sum
model.set(
"totalSize",
model.get("dataTotalSize") + data.stats.stats_fields.size.sum,
);
}
}
}
},
/**
* Queries for statistics about data objects
*/
getDataStats() {
// Get the query string from this model
let query = this.get("query") || "";
// If there is a query set on the model, do a join on the resourceMap
// field
if (
query.trim() !== "*:*" &&
query.trim().length > 0 &&
!this.get("isSystemMetadataQuery") &&
MetacatUI.appModel.get("enableSolrJoins")
) {
query = `{!join from=resourceMap to=resourceMap}${query}`;
}
if (this.get("postQuery")) {
query = this.get("postQuery");
} else if (this.get("searchModel")) {
query = this.get("searchModel").getQuery(undefined, {
forPOST: true,
});
this.set("postQuery", query);
}
const opts = {
q: query,
rows: 0,
filterQueries: ["formatType:DATA", "-obsoletedBy:*"],
statsFields: ["size"],
facets: ["formatId"],
facetLimit: -1,
facetRange: "dateUploaded",
facetRangeStart: "1900-01-01T00:00:00.000Z",
facetRangeEnd: new Date().toISOString(),
facetRangeGap: "+1MONTH",
extraParams: [
["f.formatId.facet.mincount", "1"],
["f.formatId.facet.missing", "false"],
["f.dateUploaded.facet.missing", "true"],
],
};
QueryService.queryWithFetch(opts).then(this.setDataStats.bind(this));
},
/**
* Parses and saves the data statistics returned from Solr
* @param {object} data The Solr response object
* @see {@link Stats#getDataStats}
* @since 2.36.0
*/
setDataStats(data) {
const model = this;
if (!data || !data.response || !data.response.numFound) {
// Store falsey data
model.set("dataCount", 0);
model.trigger("change:dataCount");
model.set("dataFormatIDs", ["", 0]);
model.set("dataUpdateDates", []);
model.set("dataTotalSize", 0);
if (typeof model.get("metadataTotalSize") === "number") {
// Use the metadata total size as the total size
model.set("totalSize", model.get("metadataTotalSize"));
}
} else {
// Save the number of data docs found
model.set("dataCount", data.response.numFound);
model.set(
"totalCount",
model.get("metadataCount") + data.response.numFound,
);
// Save the format ID facet counts
if (
data.facet_counts &&
data.facet_counts.facet_fields &&
data.facet_counts.facet_fields.formatId
) {
model.set("dataFormatIDs", data.facet_counts.facet_fields.formatId);
} else {
model.set("dataFormatIDs", ["", 0]);
}
// Save the data update date counts
if (
data.facet_counts &&
data.facet_counts.facet_ranges &&
data.facet_counts.facet_ranges.dateUploaded
) {
// Find the index of the first update date
const updateFacets =
data.facet_counts.facet_ranges.dateUploaded.counts;
let cropAt = 0;
for (let i = 1; i < updateFacets.length; i += 2) {
// If there was at least one update/upload in this date range,
// then save this as the first update
if (typeof updateFacets[i] === "number" && updateFacets[i] > 0) {
// Save the first first update date
cropAt = i;
model.set("firstUpdate", updateFacets[i - 1]);
// Save the update dates, but crop out months that are empty
model.set("dataUpdateDates", updateFacets.slice(cropAt + 1));
i = updateFacets.length;
}
}
// If no update dates were found, save falsey values
if (cropAt === 0) {
model.set("firstUpdate", null);
model.set("dataUpdateDates", []);
}
}
// Get the total size of all the files in the index
if (
data.stats &&
data.stats.stats_fields &&
data.stats.stats_fields.size &&
data.stats.stats_fields.size.sum
) {
// Save the size sum
model.set("dataTotalSize", data.stats.stats_fields.size.sum);
// If there is a metadata size sum,
if (model.get("metadataTotalSize") > 0) {
// Add it to the data size sum as the total sum
model.set(
"totalSize",
model.get("metadataTotalSize") +
data.stats.stats_fields.size.sum,
);
}
}
}
},
/**
* Retrieves an image of the metadata assessment scores
*/
getMdqScores() {
try {
const myImage = new Image();
const model = this;
myImage.crossOrigin = ""; // or "anonymous"
// Call the function with the URL we want to load, but then chain the
// promise then() method on to the end of it. This contains two
// callbacks
const serviceUrl = MetacatUI.appModel.get("mdqScoresServiceUrl");
if (!serviceUrl) {
this.set("mdqScoresImage", this.defaults().mdqScoresImage);
this.trigger("change:mdqScoresImage");
return;
}
if (
Array.isArray(MetacatUI.appModel.get("mdqAggregatedSuiteIds")) &&
MetacatUI.appModel.get("mdqAggregatedSuiteIds").length
) {
const suite = MetacatUI.appModel.get("mdqAggregatedSuiteIds")[0];
let id;
if (
this.get("mdqImageId") &&
typeof this.get("mdqImageId") === "string"
) {
id = this.get("mdqImageId");
} else if (MetacatUI.appView.currentView) {
id = MetacatUI.appView.currentView.model.get("seriesId");
}
// If no ID was found, exit without getting the image
if (!id) {
return;
}
const url = `${serviceUrl}?id=${id}&suite=${suite}`;
this.imgLoad(url).then((response) => {
// The first runs when the promise resolves, with the
// request.reponse specified within the resolve() method.
const imageURL = window.URL.createObjectURL(response);
myImage.src = imageURL;
model.set("mdqScoresImage", myImage);
// The second runs when the promise is rejected, and logs the
// Error specified with the reject() method.
});
} else {
this.set("mdqScoresImage", this.defaults().mdqScoresImage);
}
} catch (e) {
// If the image fails to load, set the mdqScoresImage to null
this.set("mdqScoresImage", this.defaults().mdqScoresImage);
this.trigger("change:mdqScoresImage");
this.set("mdqScoresError", e.message || "Image failed to load");
}
},
/**
* Retrieves an image via a Promise. Primarily used by
* {@link Stats#getMdqScores}
* @param {string} url - The URL of the image
* @returns {Promise} A Promise that resolves with the image Blob if the
* request is successful, or rejects with an Error if it fails
*/
imgLoad(url) {
// Create new promise with the Promise() constructor; This has as its
// argument a function with two parameters, resolve and reject
const model = this;
return new Promise((resolve, reject) => {
// Standard XHR to load an image
const request = new XMLHttpRequest();
request.open("GET", url);
request.responseType = "blob";
// When the request loads, check whether it was successful
request.onload = () => {
if (request.status === 200) {
// If successful, resolve the promise by passing back the request
// response
resolve(request.response);
} else {
// If it fails, reject the promise with a error message
reject(
new Error(
`Image didn't load successfully; error code:${request.statusText}`,
),
);
model.set("mdqScoresError", request.statusText);
}
};
request.onerror = () => {
// Also deal with the case when the entire request fails to begin
// with This is probably a network error, so reject the promise with
// an appropriate message
reject(new Error("There was a network error."));
};
// Send the request
request.send();
});
},
/**
* Sends a Solr query to get the earliest beginDate. If there are no
* beginDates in the index, then it searches for the earliest endDate.
*/
async getFirstBeginDate() {
let firstBeginDate = await this.getEarliestBeginDate();
if (!firstBeginDate) {
firstBeginDate = await this.getEarliestEndDate();
}
this.set("firstBeginDate", firstBeginDate);
if (!firstBeginDate) {
this.set("lastEndDate", null);
}
},
/**
* Gets the earliest beginDate from the Solr index
* @returns {Promise<Date|null>} A promise that resolves with the earliest
* beginDate found in the Solr index, or null if none was found
*/
async getEarliestBeginDate() {
const specialQueryParams = ` AND beginDate:[${this.get(
"firstPossibleDate",
)} TO ${new Date().toISOString()}] AND -obsoletedBy:* AND -formatId:*dataone.org/collections* AND -formatId:*dataone.org/portals*`;
let query = this.get("query") + specialQueryParams;
// Get the unencoded query string
if (this.get("postQuery")) {
query = this.get("postQuery") + specialQueryParams;
} else if (this.get("searchModel")) {
query = this.get("searchModel").getQuery(undefined, {
forPOST: true,
});
this.set("postQuery", query);
query += specialQueryParams;
}
const opts = {
q: decodeURIComponent(query),
rows: 1,
sort: "beginDate asc",
fields: "beginDate",
};
return QueryService.queryWithFetch(opts)
.then((data) => QueryService.parseResponse(data))
.then((docs) => {
return docs?.length ? new Date(docs[0].beginDate) : null;
});
},
/**
* Gets the earliest endDate from the Solr index
* @returns {Promise<Date|null>} A promise that resolves with the earliest
* endDate found in the Solr index, or null if none was found
*/
async getEarliestEndDate() {
const query = `${this.get("query")} AND endDate:[${this.get(
"firstPossibleDate",
)} TO ${new Date().toISOString()}] AND -obsoletedBy:*`;
const opts = {
q: query,
rows: 1,
sort: "endDate asc",
fields: "endDate",
};
return QueryService.queryWithFetch(opts)
.then((data) => QueryService.parseResponse(data))
.then((docs) => (docs?.length ? new Date(docs[0].endDate) : null));
},
// Getting total number of replicas for repository profiles
getTotalReplicas(memberNodeID) {
const model = this;
if (!memberNodeID) return;
const opts = {
q: `replicaMN:${memberNodeID} AND -datasource:${memberNodeID} AND formatType:METADATA AND -obsoletedBy:*`,
rows: 0,
};
QueryService.queryWithFetch(opts)
.then((data) => {
model.set("totalReplicas", data?.response?.numFound || 0);
})
.catch(() => {
model.set("totalReplicas", 0);
});
},
/**
* Gets the latest endDate from the Solr index
*/
getLastEndDate() {
const model = this;
const now = new Date();
const specialQueryParams =
` AND endDate:[${this.get("firstPossibleDate")} TO ${now.toISOString()}]` +
" AND -obsoletedBy:* AND -formatId:*dataone.org/collections* AND -formatId:*dataone.org/portals*";
let query = this.get("query") + specialQueryParams;
if (this.get("postQuery")) {
query = this.get("postQuery") + specialQueryParams;
} else if (this.get("searchModel")) {
const base = this.get("searchModel").getQuery(undefined, {
forPOST: true,
});
this.set("postQuery", base);
query = base + specialQueryParams;
}
const opts = {
q: query,
rows: 1,
sort: "endDate desc",
fields: "endDate",
};
QueryService.queryWithFetch(opts)
.then((data) => QueryService.parseResponse(data))
.then((docs) => {
if (!docs?.length) {
model.set("lastEndDate", null);
return;
}
const endDate = new Date(docs[0].endDate);
model.set(
"lastEndDate",
endDate.getUTCFullYear() > now.getUTCFullYear() ? now : endDate,
);
});
},
/**
* Given the query or URL, determine whether this model should send GET or
* POST requests, because of URL length restrictions in browsers.
* @param {string} queryOrURLString - The full query or URL that will be
* sent to the query service
* @returns {string} The request type to use. Either `GET` or `POST`
*/
getRequestType(queryOrURLString) {
// If POSTs to the query service are disabled completely, use GET
if (MetacatUI.appModel.get("disableQueryPOSTs")) {
return "GET";
}
// If POSTs are enabled and the URL is over the maximum, use POST
if (
queryOrURLString &&
queryOrURLString.length > this.get("maxQueryLength")
) {
return "POST";
}
// Otherwise, default to GET
return "GET";
},
/**
* @deprecated as of MetacatUI version 2.12.0. Use
* {@link Stats#getMetadataStats} and {@link Stats#getDataStats} to get
* the formatTypes. This function may be removed in a future release.
*/
getFormatTypes() {
this.getMetadataStats();
this.getDataStats();
},
/**
* @deprecated as of MetacatUI version 2.12.0. Use
* {@link Stats#getDataStats} to get the formatTypes. This function may be
* removed in a future release.
*/
getDataFormatIDs() {
this.getDataStats();
},
/**
* @deprecated as of MetacatUI version 2.12.0. Use
* {@link Stats#getMetadataStats} to get the formatTypes. This function
* may be removed in a future release.
*/
getMetadataFormatIDs() {
this.getMetadataStats();
},
/**
* @deprecated as of MetacatUI version 2.12.0. Use
* {@link Stats#getMetadataStats} and {@link Stats#getDataStats} to get
* the formatTypes. This function may be removed in a future release.
*/
getUpdateDates() {
this.getMetadataStats();
this.getDataStats();
},
/**
* @deprecated as of MetacatUI version 2.12.0. Use
* {@link Stats#getMetadataStats} to get the formatTypes. This function
* may be removed in a future release.
*/
getCollectionYearFacets() {
this.getMetadataStats();
},
},
);
return Stats;
});