diff --git a/CHANGELOG b/CHANGELOG index 2a42eb7b2dc97f42b6054be1a0ed0a4bc62d1638..06cc73179f84e5f152586acad608940462265877 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -40,7 +40,9 @@ minerva (12.3.0~alpha.0) unstable; urgency=low * Small improvement: when exporting reaction and elements there is possibility to filter by (sub)map (#615) * Small improvement: during first opening of a map, the zoom level is - automatically computed if no default information is provided + automatically computed if no default information is provided (#776) + * Small improvement: when adding new project columns "Root map", "Mapping + file" and "Map type" are merged (#700) * Bug fix: progress bar of gene genome mapping upload is refreshing properly (#728) * Bug fix: when editing project Disease and Organism could not be removed diff --git a/frontend-js/src/main/css/global.css b/frontend-js/src/main/css/global.css index a99d1a4ac0775f5de79591abac9c2e8ab5a6e819..c907f80b286c212caf2fd72bff14e1c5bba4b7be 100644 --- a/frontend-js/src/main/css/global.css +++ b/frontend-js/src/main/css/global.css @@ -654,6 +654,10 @@ h1 { margin: 5px; } +.minerva-project-submaps-tab [name=submapName] { + width: 100%; +} + /** tabs in dialogs */ diff --git a/frontend-js/src/main/js/gui/admin/AddProjectDialog.js b/frontend-js/src/main/js/gui/admin/AddProjectDialog.js index 1c968e003d9abff9aab1712af2a65805cbe4195c..7b98ba22ae59f22d0ad8084d0e654cf1c936fb0e 100644 --- a/frontend-js/src/main/js/gui/admin/AddProjectDialog.js +++ b/frontend-js/src/main/js/gui/admin/AddProjectDialog.js @@ -550,10 +550,6 @@ AddProjectDialog.prototype.createSubmapsTabContent = function () { title: 'File name' }, { title: 'Name' - }, { - title: 'Root map' - }, { - title: 'Mapping file' }, { title: 'Map type' }] @@ -565,51 +561,30 @@ AddProjectDialog.prototype.createSubmapsTabContent = function () { self.getEntryByFilename(filename).getData().name = $(input).val(); }); - $(submapsTable).on("change", "[name='submapRoot']", function () { - var input = this; - if (!$(input).is(":checked")) { - this.checked = true; - GuiConnector.info("One model must be marked as a root"); - return false; - } - - var filename = $(input).attr("data"); - var checkboxes = $("[name='submapRoot']", submapsTable); - for (var i = 0; i < checkboxes.length; i++) { - var checkbox = checkboxes[i]; - if ($(checkbox).attr("data") !== filename) { - $(checkbox).attr('checked', false); - self.getEntryByFilename($(checkbox).attr("data")).getData().root = false; - } - } - self.getEntryByFilename(filename).getData().root = $(input).is(":checked"); - }); - - $(submapsTable).on("change", "[name='submapMapping']", function () { - var input = this; - var filename = $(input).attr("data"); - var checkboxes = $("[name='submapMapping']", submapsTable); - for (var i = 0; i < checkboxes.length; i++) { - var checkbox = checkboxes[i]; - if ($(checkbox).attr("data") !== filename) { - $(checkbox).attr('checked', false); - self.getEntryByFilename($(checkbox).attr("data")).getData().mapping = false; - } - } - self.getEntryByFilename(filename).getData().mapping = $(input).is(":checked"); - }); - $(submapsTable).on("change", "[name='submapType']", function () { var input = this; var filename = $(input).attr("data"); var configuration = self.getConfiguration(); var mapTypes = configuration.getMapTypes(); + + var selectedId = $(input).val(); + + var data = self.getEntryByFilename(filename).getData(); + data.type = undefined; + data.root = false; + data.mapping = false; for (var j = 0; j < mapTypes.length; j++) { var mapType = mapTypes[j]; - if (mapType.id === $(input).val()) { - self.getEntryByFilename(filename).getData().type = mapType; + if (mapType.id === selectedId) { + data.type = mapType; } } + if (selectedId === "ROOT") { + data.root = true; + } + if (selectedId === "MAPPING") { + data.mapping = true; + } }); self.addListener("onZipFileUpload", function () { @@ -620,38 +595,40 @@ AddProjectDialog.prototype.createSubmapsTabContent = function () { for (var i = 0; i < entries.length; i++) { var entry = entries[i]; if (entry.getType() === "MAP") { - var row = []; - var rootCheckbox; - if (entry.getData().root) { - rootCheckbox = "<input name='submapRoot' type='checkbox' data='" + entry.getFilename() + "' checked='checked'/>"; - } else { - rootCheckbox = "<input name='submapRoot' type='checkbox' data='" + entry.getFilename() + "'/>"; - } - var mappingCheckbox; - if (entry.getData().mapping) { - mappingCheckbox = "<input name='submapMapping' type='checkbox' data='" + entry.getFilename() + "' checked='checked'/>"; - } else { - mappingCheckbox = "<input name='submapMapping' type='checkbox' data='" + entry.getFilename() + "'/>"; - } - + var row = [], selected; var typeSelect = "<select data='" + entry.getFilename() + "' name='submapType'>"; - typeSelect += "<option value='" + entry.getData().type.id + "' selected>" + entry.getData().type.name + "</option>"; var mapTypes = configuration.getMapTypes(); for (var j = 0; j < mapTypes.length; j++) { var mapType = mapTypes[j]; - if (mapType !== entry.getData().type) { - typeSelect += "<option value='" + mapType.id + "' >" + mapType.name + "</option>"; + if (mapType === entry.getData().type) { + selected = " selected"; + } else { + selected = ""; } + typeSelect += "<option value='" + mapType.id + "' " + selected + ">" + mapType.name + "</option>"; } + + if (entry.getData().root) { + selected = " selected"; + } else { + selected = ""; + } + typeSelect += "<option value='ROOT' " + selected + ">ROOT</option>"; + + if (entry.getData().mapping) { + selected = " selected"; + } else { + selected = ""; + } + typeSelect += "<option value='MAPPING' " + selected + ">MAPPING</option>"; + typeSelect += "</select>"; row[0] = entry.getFilename(); row[1] = "<input data='" + entry.getFilename() + "' name='submapName' value='" + entry.getData().name + "'/>"; - row[2] = rootCheckbox; - row[3] = mappingCheckbox; - row[4] = typeSelect; + row[2] = typeSelect; data.push(row); } } @@ -1238,6 +1215,29 @@ AddProjectDialog.prototype.checkValidity = function () { error += "<li>projectId can contain only alphanumeric characters and -_</li>"; isValid = false; } + + var rootExist = 0, i; + for (i = 0; i < self.getZipEntries().length; i++) { + if (self.getZipEntries()[i].getData().root) { + rootExist++; + } + } + if (self.getZipEntries().length > 0 && rootExist !== 1) { + error += "<li>one root map must be selected</li>"; + isValid = false; + } + + var mappingExist = 0; + for (i = 0; i < self.getZipEntries().length; i++) { + if (self.getZipEntries()[i].getData().mapping) { + mappingExist++; + } + } + if (mappingExist > 1) { + error += "<li>only one mapping map can be selected</li>"; + isValid = false; + } + GuiConnector.showProcessing(); return Promise.resolve().then(function () { if (self.getDisease() !== "") { @@ -1390,15 +1390,17 @@ AddProjectDialog.prototype.createZipEntry = function (jsZipEntry, zipObject) { } data.name = name; }).then(function () { - var configuration = self.getConfiguration(); - var mapTypes = configuration.getMapTypes(); - for (var i = 0; i < mapTypes.length; i++) { - if (mapTypes[i].id === "UNKNOWN") { - data.type = mapTypes[i]; + if (!data.root && !data.mapping) { + var configuration = self.getConfiguration(); + var mapTypes = configuration.getMapTypes(); + for (var i = 0; i < mapTypes.length; i++) { + if (mapTypes[i].id === "UNKNOWN") { + data.type = mapTypes[i]; + } + } + if (data.type === undefined) { + data.type = mapTypes[0]; } - } - if (data.type === undefined) { - data.type = mapTypes[0]; } }); } @@ -1414,7 +1416,7 @@ AddProjectDialog.prototype.createZipEntry = function (jsZipEntry, zipObject) { /** * - * @returns {Array} + * @returns {ZipEntry[]} */ AddProjectDialog.prototype.getZipEntries = function () { return this._zipEntries; diff --git a/frontend-js/src/main/js/gui/admin/ZipEntry.js b/frontend-js/src/main/js/gui/admin/ZipEntry.js index 9a4c40d851b26773abfe953dfc35c96de0454122..92546fd6c95d3a76f86444c76d945e81adc0c6a5 100644 --- a/frontend-js/src/main/js/gui/admin/ZipEntry.js +++ b/frontend-js/src/main/js/gui/admin/ZipEntry.js @@ -7,7 +7,7 @@ var types = ["IMAGE", "OVERLAY", "MAP"]; * @param {Object} params * @param {string} params.type * @param {string} params.filename - * @param {string} params.data + * @param {Object} params.data * @constructor */ function ZipEntry(params) { @@ -54,7 +54,7 @@ ZipEntry.prototype.getFilename = function () { /** * - * @param {string} data + * @param {Object} data */ ZipEntry.prototype.setData = function (data) { this._data = data; @@ -62,7 +62,7 @@ ZipEntry.prototype.setData = function (data) { /** * - * @returns {string} + * @returns {{type: MapType, root:boolean, mapping: boolean}} */ ZipEntry.prototype.getData = function () { return this._data; diff --git a/frontend-js/src/test/js/gui/admin/AddProjectDialog-test.js b/frontend-js/src/test/js/gui/admin/AddProjectDialog-test.js index 39f24b29cfb567db550a9d51ab986cee9eea6b6f..ab76c0c9c4724f41d9f8fd2d12715b134b891ea1 100644 --- a/frontend-js/src/test/js/gui/admin/AddProjectDialog-test.js +++ b/frontend-js/src/test/js/gui/admin/AddProjectDialog-test.js @@ -323,6 +323,22 @@ describe('AddProjectDialog', function () { return dialog.destroy(); }); }); + it('for zip file', function () { + var dialog = createDialog(); + + var file = fs.readFileSync("testFiles/map/complex_model_with_submaps.zip"); + file.name = "complex_model_with_submaps.zip"; + return dialog.init().then(function () { + dialog.setFileContent(""); + return dialog.setZipFileContent(file); + }).then(function () { + dialog.setProjectId("invalid_id"); + return dialog.checkValidity(); + }).then().finally(function () { + return dialog.destroy(); + }); + }); + }); describe('isIgnoredZipEntry', function () { diff --git a/rest-api/src/main/java/lcsb/mapviewer/api/projects/ProjectRestImpl.java b/rest-api/src/main/java/lcsb/mapviewer/api/projects/ProjectRestImpl.java index 43c39d1351dff98cc63c65f03225db3469f7ce2b..85b4297df08e2ea81537c167ec3cd33ff4036454 100644 --- a/rest-api/src/main/java/lcsb/mapviewer/api/projects/ProjectRestImpl.java +++ b/rest-api/src/main/java/lcsb/mapviewer/api/projects/ProjectRestImpl.java @@ -440,7 +440,10 @@ public class ProjectRestImpl extends BaseRestImpl { String submodelTypeKey = "zip-entries[" + fileIndex + "][_data][type][id]"; String rootKey = "zip-entries[" + fileIndex + "][_data][root]"; String mappingKey = "zip-entries[" + fileIndex + "][_data][mapping]"; - SubmodelType mapType = SubmodelType.valueOf((String) data.get(submodelTypeKey).get(0)); + + String mapTypeString = getStringValue(data.get(submodelTypeKey), SubmodelType.UNKNOWN.name()); + SubmodelType mapType = SubmodelType.valueOf(mapTypeString); + String name = (String) data.get("zip-entries[" + fileIndex + "][_data][name]").get(0); Boolean root = getBoolValue(data.get(rootKey), false); Boolean mapping = getBoolValue(data.get(mappingKey), false); @@ -474,6 +477,21 @@ public class ProjectRestImpl extends BaseRestImpl { } } + private String getStringValue(List<Object> list, String defaultValue) { + if (list == null) { + return defaultValue; + } + if (list.size() == 0) { + return defaultValue; + } + Object obj = list.get(0); + if (obj instanceof String) { + return (String) list.get(0); + } else { + return obj.toString(); + } + } + /** * Method that computes md5 hash for a given {@link String}. * diff --git a/rest-api/src/test/java/lcsb/mapviewer/api/projects/ProjectRestImplTest.java b/rest-api/src/test/java/lcsb/mapviewer/api/projects/ProjectRestImplTest.java index 202f5047d2e74c559c650e306463f9d5c1a15e2c..50143516ac9973bd68b0ef15ec7989ced560df87 100644 --- a/rest-api/src/test/java/lcsb/mapviewer/api/projects/ProjectRestImplTest.java +++ b/rest-api/src/test/java/lcsb/mapviewer/api/projects/ProjectRestImplTest.java @@ -236,6 +236,25 @@ public class ProjectRestImplTest extends RestTestFunctions { } } + @Test + public void testExtractZipEntriesWithNoMapType() throws Exception { + try { + MultiValueMap<String, Object> data = new LinkedMultiValueMap<>(); + data.put("zip-entries[0][_type]", createLinkedList("MAP")); + data.put("zip-entries[0][_filename]", createLinkedList("main.xml")); + data.put("zip-entries[0][_data][root]", createLinkedList("true")); + data.put("zip-entries[0][_data][name]", createLinkedList("main")); + List<ZipEntryFile> result = _projectRestImpl.extractZipEntries(data); + assertNotNull(result); + assertEquals(1, result.size()); + assertTrue(result.get(0) instanceof ModelZipEntryFile); + + } catch (Exception e) { + e.printStackTrace(); + throw e; + } + } + private LinkedList<Object> createLinkedList(Object string) { LinkedList<Object> result = new LinkedList<>(); result.add(string);