Skip to content

Commit

Permalink
Create EMLDistributions collection
Browse files Browse the repository at this point in the history
- move Distributions logic from EML211 model to collection
- include unit tests for the collection

Issue #1380
  • Loading branch information
robyngit committed Aug 16, 2023
1 parent 1bf858c commit b51a7bc
Show file tree
Hide file tree
Showing 5 changed files with 282 additions and 89 deletions.
112 changes: 112 additions & 0 deletions src/js/collections/metadata/eml/EMLDistributions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
"use strict";

define(["backbone", "models/metadata/eml211/EMLDistribution"], function (
Backbone,
EMLDistribution
) {
/**
* @class EMLDistributions
* @classdesc A collection of EMLDistributions.
* @classcategory Collections/Metadata/EML
* @since x.x.x
*/
var EMLDistributions = Backbone.Collection.extend(
/** @lends EMLDistributions.prototype */
{
/**
* The reference to the model class that this collection is made of.
* @type EMLDistribution
*/
model: EMLDistribution,

/**
* Find the distribution that has all of the matching attributes. This
* will return true if the distribution has all of the attributes, even if
* it has more attributes than the ones passed in. Only the first matching
* distribution will be returned.
* @param {object} attributes - The attributes to match
* @param {boolean} partialMatch - If true, then the attribute values in
* the distribution models only need to partially match the attribute
* values given. If false, then the attributes must match exactly.
* @return {EMLDistribution|undefined} The matching distribution, or
* undefined if there is no match.
*/
findByAttributes: function (attributes, partialMatch = false) {
return this.find((d) => {
return Object.keys(attributes).every((key) => {
const val = d.get(key);
if (partialMatch) {
return val.includes(attributes[key]);
}
return val === attributes[key];
});
});
},

/**
* Remove the distribution that has all of the matching attributes. This
* will remove the first distribution that has all of the attributes, even
* if it has more attributes than the ones passed in.
* @param {object} attributes - The attributes to match
* @param {boolean} partialMatch - If true, then the attribute values in
* the distribution models only need to partially match the attribute
* values given. If false, then the attributes must match exactly.
* @return {EMLDistribution|undefined} The matching distribution, or
* undefined if there is no match.
*/
removeByAttributes: function (attributes, partialMatch = false) {
const dist = this.findByAttributes(attributes, partialMatch);
if (dist) {
return this.remove(dist);
}
},

/**
* Make sure that the EML dataset element has a distribution node with the
* location where the data package can be viewed. This will be either the
* view URL for the member node being used or the DOI.org URL if the
* dataset has one. This method will look for the old distribution URL and
* update it if it exists, or add a new distribution node if it doesn't.
* @param {string} url - The URL to add to the dataset distribution
* @param {string[]} oldIDs - The old PIDs, seriesIds, or current PID to
* remove from the dataset distribution
* @return {EMLDistribution} The distribution that was added or updated
*/
addDatasetDistributionURL: function (url, oldIDs = []) {
if (!url) {
console.warn("No URL given to addDatasetDistributionURL");
return;
}

// Reference to this collection
const dists = this;
// The URL function used for dataset distribution URLs
const func = "information";

// Remove any distribution models with the old PID, seriesId, or current
// PID in the URL (only if the URL function is "information")
if (dists.length && oldIDs.length) {
oldIDs.forEach((url) => {
dists.removeByAttributes({ url: id, urlFunction: func }, true);
});
}

// Add a new distribution with the view URL
return dists.add({ url: url, urlFunction: urlFunction });
},

/**
* Update the DOM for each distribution in this collection with the
* current model state.
* @return {object[]} An array of jQuery DOM objects for each distribution
* in this collection.
*/
updateDOMs: function (doms) {
const objectDOMs = this.map((model) => model.updateDOM());
return objectDOMs;
},
}
);

return EMLDistributions;
});
134 changes: 45 additions & 89 deletions src/js/models/metadata/eml211/EML211.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ define(['jquery', 'underscore', 'backbone', 'uuid',
'models/metadata/eml211/EMLKeywordSet',
'models/metadata/eml211/EMLTaxonCoverage',
'models/metadata/eml211/EMLTemporalCoverage',
'models/metadata/eml211/EMLDistribution',
'collections/metadata/eml/EMLDistributions',
'models/metadata/eml211/EMLEntity',
'models/metadata/eml211/EMLDataTable',
'models/metadata/eml211/EMLOtherEntity',
Expand All @@ -19,7 +19,7 @@ define(['jquery', 'underscore', 'backbone', 'uuid',
'models/metadata/eml211/EMLAnnotation'],
function($, _, Backbone, uuid, Units, ScienceMetadata, DataONEObject,
EMLGeoCoverage, EMLKeywordSet, EMLTaxonCoverage, EMLTemporalCoverage,
EMLDistribution, EMLEntity, EMLDataTable, EMLOtherEntity, EMLParty,
EMLDistributions, EMLEntity, EMLDataTable, EMLOtherEntity, EMLParty,
EMLProject, EMLText, EMLMethods, EMLAnnotations, EMLAnnotation) {

/**
Expand Down Expand Up @@ -55,7 +55,7 @@ define(['jquery', 'underscore', 'backbone', 'uuid',
keywordSets: [], //array of EMLKeywordSet objects
additionalInfo: [],
intellectualRights: "This work is dedicated to the public domain under the Creative Commons Universal 1.0 Public Domain Dedication. To view a copy of this dedication, visit https://creativecommons.org/publicdomain/zero/1.0/.",
distribution: [], // array of EMLDistribution objects
distributions: new EMLDistributions(), // EMLDistribution collection
geoCoverage : [], //an array for EMLGeoCoverages
temporalCoverage : [], //an array of EMLTempCoverage models
taxonCoverage : [], //an array of EMLTaxonCoverages
Expand Down Expand Up @@ -513,14 +513,18 @@ define(['jquery', 'underscore', 'backbone', 'uuid',
type: attributeName
}));
}
//EML Distribution modules are stored in EMLDistribution models
else if(_.contains(emlDistribution, thisNode.localName)) {
if(typeof modelJSON[thisNode.localName] == "undefined") modelJSON[thisNode.localName] = [];

modelJSON[thisNode.localName].push(new EMLDistribution({
objectDOM: thisNode,
parentModel: model
}, { parse: true }));
//EML Distribution info is stored in an EMLDistribution collection
else if (_.contains(emlDistribution, thisNode.localName)) {
// Create the collection if it doesn't exist
const distName = thisNode.localName
let distCollection = modelJSON[distName]
if (!distCollection) {
modelJSON[distName] = distCollection = new EMLDistributions();
}
// Add the distribution to the collection
const distAttrs = { objectDOM: thisNode, parentModel: model }
const distOpts = { parse: true }
distCollection.add(distAttrs, distOpts);
}
//The EML Project is stored in the EMLProject model
else if(thisNode.localName == "project"){
Expand Down Expand Up @@ -1029,21 +1033,15 @@ define(['jquery', 'underscore', 'backbone', 'uuid',
}

// Serialize the distribution
const distributions = this.get('distribution');
if (distributions && distributions.length > 0) {
// Remove existing nodes
datasetNode.children('distribution').remove();
// Get the updated DOMs
const distributionDOMs = distributions.map(d => d.updateDOM());
// Insert the updated DOMs in their correct positions
distributionDOMs.forEach((dom, i) => {
const insertAfter = this.getEMLPosition(eml, 'distribution');
if (insertAfter) {
insertAfter.after(dom);
} else {
datasetNode.append(dom);
}
});
datasetNode.children('distribution').remove();
const distributionDOMs = this.getDistributions().updateDOMs();
if (distributionDOMs.length) {
const insertAfter = this.getEMLPosition(eml, 'distribution');
if (insertAfter) {
insertAfter.after(distributionDOMs);
} else {
datasetNode.append(distributionDOMs);
}
}

//Detach the project elements from the DOM
Expand Down Expand Up @@ -1438,44 +1436,24 @@ define(['jquery', 'underscore', 'backbone', 'uuid',
},

/**
* Adds a new EMLDistribution model to the distribution array
* @param {object} attributes - The attributes to set on the new
* EMLDistribution model
* @param {object} options - Options to pass to the new EMLDistribution
* model
* Get the distribution model collection on this EML model. If there is no
* distribution collection, then one is created, set on the EML model, and
* returned.
* @return {EMLDistributions} The distribution collection on this EML model
*/
addDistribution: function (attributes, options) {
getDistributions: function () {
try {
const distributions = this.get('distribution') || [];
const newDistribution = new EMLDistribution(attributes, options);
distributions.push(newDistribution);
this.set('distribution', distributions);
let distributions = this.get('distribution');
if (!distributions) {
distributions = new EMLDistributions();
this.set('distribution', distributions);
}
return distributions;
} catch (e) {
console.log("Couldn't add a distribution to the EML model", e);
console.log("Couldn't get the distributions from the EML model", e);
}
},

/**
* Find the distribution that has all of the matching attributes. This will
* return true if the distribution has all of the attributes, even if it
* has more attributes than the ones passed in.
* @param {object} attributes - The attributes to match
* @param {boolean} partialMatch - If true, then the attributes only need
* to partially match. If false, then the attributes must match exactly.
*/
findDistribution: function (attributes, partialMatch = false) {
const distributions = this.get('distribution') || [];
return distributions.find(d => {
return Object.keys(attributes).every(key => {
const val = d.get(key);
if (partialMatch) {
return val.includes(attributes[key]);
}
return val === attributes[key];
});
});
},

/**
* Make sure that the EML dataset element has a distribution node with the
* location where the data package can be viewed. This will be either the
Expand All @@ -1484,38 +1462,16 @@ define(['jquery', 'underscore', 'backbone', 'uuid',
* if it exists, or add a new distribution node if it doesn't.
*/
addDatasetDistributionURL: function () {
const model = this;
const oldPid = this.get('oldPid');
const newPid = this.get('id');
const seriesId = this.get('seriesId');
const IDs = [oldPid, newPid, seriesId]

// Remove any distribution models with the old PID, seriesId, or current
// PID in the URL (only if the URL function is "information")
const distributions = this.get('distribution') || [];
IDs.forEach(id => {
const distributions = model.get('distribution');
if(!distributions || !distributions.length) return;
const dist = this.findDistribution(
{ url: id, urlFunction: 'information' }, true);
if (dist) {
// Remove the distribution model from the array
distributions.splice(distributions.indexOf(dist), 1);
}
});


// Add a new distribution node with the view URL
const viewURL = this.getCanonicalDOIIRI() || this.createViewURL();
if (viewURL) {
this.addDistribution({
url: viewURL,
urlFunction: 'information'
});
} else {
console.log('Could not add a distribution node with the view URL');
try {
// Old distribution URLs could exist for any of the old or current
const IDs = [ this.get('oldPid'), this.get('id'), this.get('seriesId')]
// The new distribution URL will be the view URL or DOI URL
const viewURL = this.getCanonicalDOIIRI() || this.createViewURL();
// Add the new distribution URL to the collection
this.getDistributions().addDatasetDistributionURL(viewURL, IDs);
} catch (e) {
console.log("Couldn't add the distribution URL to the EML model", e);
}

},


Expand Down
9 changes: 9 additions & 0 deletions src/js/models/metadata/eml211/EMLDistribution.js
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@ define(["jquery", "underscore", "backbone", "models/DataONEObject"], function (
/*
* Makes a copy of the original XML DOM and updates it with the new values
* from the model.
* @return {Element} The updated XML DOM
*/
updateDOM: function () {
const objectDOM =
Expand Down Expand Up @@ -357,10 +358,18 @@ define(["jquery", "underscore", "backbone", "models/DataONEObject"], function (
else return false;
},

/*
* Trigger a change event on the parent EML model
*/
trickleUpChange: function () {
MetacatUI.rootDataPackage?.packageModel?.set("changed", true);
},

/*
* Formats the given XML string to be human-readable
* @param {string} xmlString - The XML string to format
* @return {string} - The formatted XML string
*/
formatXML: function (xmlString) {
return DataONEObject.prototype.formatXML.call(this, xmlString);
},
Expand Down
1 change: 1 addition & 0 deletions test/config/tests.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"./js/specs/unit/collections/metadata/eml/EMLMissingValueCodes.spec.js",
"./js/specs/unit/models/metadata/eml211/EMLMissingValueCode.spec.js",
"./js/specs/unit/models/metadata/eml211/EMLDistribution.spec.js",
"./js/specs/unit/collections/metadata/eml/EMLDistributions.spec.js",
"./js/specs/unit/models/maps/assets/CesiumImagery.spec.js",
"./js/specs/unit/collections/maps/Geohashes.spec.js",
"./js/specs/unit/models/connectors/Filters-Map.spec.js",
Expand Down
Loading

0 comments on commit b51a7bc

Please sign in to comment.