diff --git a/frontend-js/package-lock.json b/frontend-js/package-lock.json index 806db19df37adfc12420410e385f2c0ed8308c75..8ca24c0f2abdfdbdbe76cfbfb890cb063abab86e 100644 --- a/frontend-js/package-lock.json +++ b/frontend-js/package-lock.json @@ -1167,6 +1167,14 @@ "cssom": "0.3.2" } }, + "csv-stringify": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/csv-stringify/-/csv-stringify-4.3.1.tgz", + "integrity": "sha512-VRjPYIUzex5kfbsOY7LaJcNE2qMWGQQAanb3/Vv85WbOgA+dAfDNfwntRvv335icJgGYrnTX403WxJxRVpLDFA==", + "requires": { + "lodash.get": "4.4.2" + } + }, "cycle": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz", @@ -3210,6 +3218,11 @@ "lodash._isiterateecall": "3.0.9" } }, + "lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" + }, "lodash.isarguments": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", diff --git a/frontend-js/package.json b/frontend-js/package.json index a2588cdc805c28bfaf19249ffce1299093e15b8d..784d067b99236b36e02d3a5b36b945b7e9b73ec6 100644 --- a/frontend-js/package.json +++ b/frontend-js/package.json @@ -39,6 +39,7 @@ "uglifyjs": "^2.4.10" }, "dependencies": { + "csv-stringify": "^4.0.0", "datatables.net-rowreorder": "^1.2.5", "dual-listbox": "1.0.7", "file-saver": "^1.3.8", diff --git a/frontend-js/src/main/js/ServerConnector.js b/frontend-js/src/main/js/ServerConnector.js index 3fe2bccb564e98d46aae66166208362e35ef70d6..a280321ccfd922715e01fcadecc34a74ce26bcac 100644 --- a/frontend-js/src/main/js/ServerConnector.js +++ b/frontend-js/src/main/js/ServerConnector.js @@ -1604,7 +1604,7 @@ ServerConnector.getMaxSearchDistance = function () { * * @param {number} overlayId * @param {string} projectId - * @returns {PromiseLike<DataOverlay>} + * @returns {PromiseLike<DataOverlay>|Promise<DataOverlay>} */ ServerConnector.getOverlayById = function (overlayId, projectId) { var self = this; diff --git a/frontend-js/src/main/js/gui/leftPanel/PublicationListDialog.js b/frontend-js/src/main/js/gui/leftPanel/PublicationListDialog.js index 23a09b5aef797a253d6927957a824b491a931ab2..f98d955f51547ccfbf530460df515e8f99d330a3 100644 --- a/frontend-js/src/main/js/gui/leftPanel/PublicationListDialog.js +++ b/frontend-js/src/main/js/gui/leftPanel/PublicationListDialog.js @@ -11,8 +11,11 @@ var IdentifiedElement = require('../../map/data/IdentifiedElement'); var Reaction = require('../../map/data/Reaction'); var Functions = require('../../Functions'); +// noinspection JSUnusedLocalSymbols var logger = require('../../logger'); +var stringify = require('csv-stringify'); + /** * * @param {Object} params @@ -40,14 +43,14 @@ PublicationListDialog.prototype.createPublicationListDialogGui = function () { var self = this; var head = Functions.createElement({ type: "thead", - content: "<tr>" + "<th>Pubmed ID</th>" + - "<th>Title</th>" + - "<th>Authors</th>" + - "<th>Journal</th>" + - "<th>Year</th>" + - "<th>Elements on map</th>" + - "<th>Submaps</th>" + - "</tr>" + content: "<tr>" + "<th>Pubmed ID</th>" + + "<th>Title</th>" + + "<th>Authors</th>" + + "<th>Journal</th>" + + "<th>Year</th>" + + "<th>Elements on map</th>" + + "<th>Submaps</th>" + + "</tr>" }); var body = Functions.createElement({ type: "tbody" @@ -74,7 +77,7 @@ PublicationListDialog.prototype.createPublicationListDialogGui = function () { */ PublicationListDialog.prototype._dataTableAjaxCall = function (data, callback) { var self = this; - return ServerConnector.getPublications({ + return self.getServerConnector().getPublications({ start: data.start, length: data.length, sortColumn: self.getColumnsDefinition()[data.order[0].column].name, @@ -89,7 +92,6 @@ PublicationListDialog.prototype._dataTableAjaxCall = function (data, callback) { var row = []; var submaps = {}; - submaps[self.getMap().getId()] = true; row[0] = "<a href='" + publication.link + "'>" + publication.id + "</a>"; row[1] = publication.title; row[2] = publication.authors.join(); @@ -174,6 +176,35 @@ PublicationListDialog.prototype.show = function () { ajax: function (data, callback, settings) { resolve(self._dataTableAjaxCall(data, callback, settings)); }, + dom: 'Bfrtip', + buttons: [ + { + text: 'CSV', + action: function (e, data) { + GuiConnector.showProcessing(); + return self.getServerConnector().getPublications({ + start: 0, + length: data.page.info().recordsDisplay, + sortColumn: self.getColumnsDefinition()[data.order()[0][0]].name, + sortOrder: data.order()[0][1], + search: data.search() + }).then(function (publicationList) { + return self.publicationListAsCsvString(publicationList); + }).then(function (result) { + var blob = new Blob([result], { + type: "text/plain;charset=utf-8" + }); + var FileSaver = require("file-saver"); + return FileSaver.saveAs(blob, "publications.csv"); + }).catch(function(error){ + GuiConnector.alert(error); + }).finally(function(){ + GuiConnector.hideProcessing(); + }); + + } + } + ], columns: self.getColumnsDefinition() }); }); @@ -223,4 +254,69 @@ PublicationListDialog.prototype.destroy = function () { } }; +/** + * + * @returns {Promise} + */ +PublicationListDialog.prototype.publicationListToArray = function (publicationList) { + var self = this; + var result = []; + + var elementsToFetch = []; + + publicationList.data.map(function (entry) { + var publication = entry.publication.article; + var elements = entry.elements; + for (var j = 0; j < elements.length; j++) { + elementsToFetch.push(new IdentifiedElement(elements[j])); + } + }); + return self.getProject().getBioEntitiesByIdentifiedElements(elementsToFetch).then(function(){ + return Promise.all(publicationList.data.map(function (entry) { + var publication = entry.publication.article; + var elements = entry.elements; + var row = []; + row[0] = publication.id; + row[1] = publication.title; + row[2] = publication.authors.join(); + row[3] = publication.journal; + row[4] = publication.year; + row[5] = ""; + row[6] = ""; + + var submaps = []; + for (var j = 0; j < elements.length; j++) { + var modelId = elements[j].modelId; + if (submaps[modelId] === undefined) { + row[6] += self.getMap().getSubmapById(modelId).getModel().getName() + ", "; + submaps[elements[j].modelId] = true; + } + } + return Promise.all(elements.map(function (element) { + var model = self.getMap().getSubmapById(element.modelId).getModel(); + return model.getByIdentifiedElement(new IdentifiedElement(element)).then(function (reaction) { + row[5] += reaction.getElementId() + ","; + }); + })).then(function () { + result.push(row); + }); + })); + }).then(function () { + return result; + }); + +}; + +PublicationListDialog.prototype.publicationListAsCsvString = function (publicationList) { + var self = this; + + return self.publicationListToArray(publicationList).then(function(data){ + return new Promise(function(resolve){ + stringify(data,function(err, output){ + resolve(output); + }); + }); + }); +}; + module.exports = PublicationListDialog; diff --git a/frontend-js/src/main/js/map/data/Project.js b/frontend-js/src/main/js/map/data/Project.js index 16884ab0d22979a3879a7e35a587e81094e2fa4e..eaf88c554d57672371201be2d271971168e68799 100644 --- a/frontend-js/src/main/js/map/data/Project.js +++ b/frontend-js/src/main/js/map/data/Project.js @@ -315,7 +315,7 @@ Project.prototype.getDisease = function () { /** * - * @param {AnnotationOptions} disease + * @param {AnnotationOptions|null} disease */ Project.prototype.setDisease = function (disease) { if (disease !== undefined && disease !== null) { @@ -335,7 +335,7 @@ Project.prototype.getOrganism = function () { /** * - * @param {AnnotationOptions} organism + * @param {AnnotationOptions|null} organism */ Project.prototype.setOrganism = function (organism) { if (organism !== undefined && organism !== null) { @@ -553,5 +553,34 @@ Project.prototype.getElementsPointingToSubmap = function (modelId) { }); }; +/** + * + * @param {IdentifiedElement[]} elements + * @param {boolean} complete + * @returns {Promise} + */ +Project.prototype.getBioEntitiesByIdentifiedElements = function (elements, complete) { + var self = this; + var elementsByModelId = []; + var i; + for (i = 0; i < elements.length; i++) { + var element = elements[i]; + var modelId = element.getModelId(); + if (elementsByModelId[modelId] === undefined) { + elementsByModelId[modelId] = []; + } + elementsByModelId[modelId].push(element); + } + + var models = self.getModels(); + var promises = []; + for (i = 0; i < models.length; i++) { + if (elementsByModelId[models[i].getId()] !== undefined) { + promises.push(models[i].getByIdentifiedElements(elementsByModelId[models[i].getId()], complete)); + } + } + return Promise.all(promises); +}; + module.exports = Project; diff --git a/frontend-js/src/main/js/map/data/Reaction.js b/frontend-js/src/main/js/map/data/Reaction.js index e9f9daa2033185f4ca9fac51aed7a5395cb073d2..73085d47022d09cdec387ac0bcc8702f8f82d68c 100644 --- a/frontend-js/src/main/js/map/data/Reaction.js +++ b/frontend-js/src/main/js/map/data/Reaction.js @@ -197,6 +197,14 @@ Reaction.prototype.getReactionId = function () { return this._reactionId; }; +/** + * + * @returns {string} + */ +Reaction.prototype.getElementId = function () { + return this.getReactionId(); +}; + /** * * @param {string} reactionId diff --git a/frontend-js/src/test/js/gui/leftPanel/PublicationListDialog-test.js b/frontend-js/src/test/js/gui/leftPanel/PublicationListDialog-test.js index a300fbc33475f89d75a81ef2fff497e8091ef37a..f7f989c35cbbe5060f0164bedd0fe6913f1b2a01 100644 --- a/frontend-js/src/test/js/gui/leftPanel/PublicationListDialog-test.js +++ b/frontend-js/src/test/js/gui/leftPanel/PublicationListDialog-test.js @@ -4,6 +4,7 @@ require('../../mocha-config.js'); var PublicationListDialog = require('../../../../main/js/gui/leftPanel/PublicationListDialog'); var ServerConnector = require('../../ServerConnector-mock'); +var Functions = require('../../../../main/js/Functions'); var chai = require('chai'); var assert = chai.assert; @@ -54,4 +55,42 @@ describe('PublicationListDialog', function () { }); }); + it('publicationListAsCsvString', function () { + + var dialog; + return ServerConnector.getProject().then(function (project) { + dialog = createPublicationListDialog(helper.createCustomMap(project)); + // noinspection JSAccessibilityCheck + return ServerConnector.getPublications({ + start: 0, + length: 10 + }); + }).then(function (publicationList) { + return dialog.publicationListAsCsvString(publicationList); + }).then(function (result) { + assert.ok(Functions.isString(result)); + assert.ok(result.indexOf("re21") >= 0); + }).finally(function () { + dialog.destroy(); + }); + }); + + it('publicationListToArray', function () { + var dialog; + return ServerConnector.getProject().then(function (project) { + dialog = createPublicationListDialog(helper.createCustomMap(project)); + // noinspection JSAccessibilityCheck + return ServerConnector.getPublications({ + start: 0, + length: 10 + }); + }).then(function (publicationList) { + return dialog.publicationListToArray(publicationList); + }).then(function (result) { + assert.equal(10, result.length); + }).finally(function () { + dialog.destroy(); + }); + }); + }); diff --git a/web/src/main/webapp/index.xhtml b/web/src/main/webapp/index.xhtml index 385a0661cc00cc1b9b47dab145170db49ddc30e8..8fbeb0d72246a8043df301a3f24fa11fffef74fa 100644 --- a/web/src/main/webapp/index.xhtml +++ b/web/src/main/webapp/index.xhtml @@ -17,11 +17,13 @@ <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script> <script src="https://cdn.datatables.net/1.10.13/js/jquery.dataTables.min.js"></script> <script src="https://cdn.datatables.net/rowreorder/1.2.3/js/dataTables.rowReorder.min.js"></script> - + <script src="https://cdn.datatables.net/buttons/1.5.2/js/dataTables.buttons.min.js"></script> <link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.13/css/jquery.dataTables.min.css"/> <link rel="stylesheet" type="text/css" href="https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css"/> <link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/rowreorder/1.2.3/css/rowReorder.dataTables.min.css"/> + <link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/buttons/1.5.2/css/buttons.dataTables.min.css"/> + <link rel="shortcut icon" href="./resources/images/favicon.png" type="image/png" />