diff --git a/CHANGELOG.md b/CHANGELOG.md index 08d8b37c4c7ab8a7a9298fbb374e36f5709cd966..1d875d84be1918f9827cf88df279f5b7430352ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,35 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] + +### Added + +### Changed + +### Deprecated + +### Removed + +### Fixed + +### Security + +### Documentation + +## [0.7.0] - 2022-05-31 +(Florian Spreckelsen) + +### Added + +* [#172](https://gitlab.com/caosdb/caosdb-webui/-/issues/172) - Map can handle + geo locations in list of references. + +### Fixed + +* [#276](https://gitlab.indiscale.com/caosdb/src/caosdb-webui/-/issues/276) + documentation couldn't be built because of a too long module name. + ## [0.6.0] - 2022-05-03 (Daniel Hornung) @@ -442,4 +471,3 @@ This is the last Bootstrap-3 compatible release. property value if the actual value was an empty string. ### Security (in case of vulnerabilities) - diff --git a/DEPENDENCIES.md b/DEPENDENCIES.md index 14b2adf30ad23bb7eaa224083c6bbb9c7f4f8a0f..0a2ee2a93ad4813a99166f7d7817d7cf5d5554fb 100644 --- a/DEPENDENCIES.md +++ b/DEPENDENCIES.md @@ -1,4 +1,4 @@ -* CaosDB Server 0.7.2 +* CaosDB Server 0.7.3 * Make 4.2.0 # Java Script Libraries (included in this repository) diff --git a/RELEASE_GUIDELINES.md b/RELEASE_GUIDELINES.md index f0856c6ad63d9d3a6720705a264d4f13482954a5..819db35cb2e8c1a360fdec278ed4618fa81ab00f 100644 --- a/RELEASE_GUIDELINES.md +++ b/RELEASE_GUIDELINES.md @@ -18,14 +18,20 @@ guidelines of the CaosDB Project 2. Check all general prerequisites. -3. update src/doc/conf.py version +3. Update `src/doc/conf.py` version and check that the correct caosdb-servre + version is listed in `DEPENDENCIES.md`. 4. Merge the release branch into the main branch. 5. Tag the latest commit of the main branch with `v<VERSION>`. -6. Delete the release branch. +6. Create gitlab releases on gitlab.indiscale.com and on gitlab.com for new + tag. Add most recent section of the changelog to release description. -7. Merge the main branch back into the dev branch. +7. Delete the release branch. -8. Prepare CHANGELOG for next release cycle. +8. Merge the main branch back into the dev branch. + +9. Prepare for next release cycle: + * `CHANGELOG.md`: "Unreleased" section + * `src/doc/conf.py`: Bump to next version number and `x.y.z-SNAPSHOT` for the `release` variable. diff --git a/misc/map_test_data.py b/misc/map_test_data.py index addc0e8c7f52cc60d228e079e5b76f5893be74d4..eda56c0ef06fc73b42d062e4fe7536aaf49b8286 100755 --- a/misc/map_test_data.py +++ b/misc/map_test_data.py @@ -18,6 +18,9 @@ datamodel.extend([ "MapObject" ).add_property("longitude", importance=caosdb.OBLIGATORY ).add_property("latitude", importance=caosdb.OBLIGATORY), + caosdb.RecordType( + "PathObject" + ).add_property("MapObject", datatype=caosdb.LIST("MapObject")), ]) datamodel.insert() @@ -25,19 +28,23 @@ datamodel.insert() # test data +testdata = caosdb.Container() +path = caosdb.Record() +path.add_parent("PathObject") +path.add_property("MapObject", datatype=caosdb.LIST("MapObject"), value=[]) +testdata.append(path) +for i in range(100): + loc = caosdb.Record( + "Object-{}".format(i) + ).add_parent("MapObject" + ).add_property("longitude", random.gauss(-42.0, 5) + ).add_property("latitude", random.gauss(77.0, 5)) + testdata.append(loc) + path.get_property("MapObject").value.append(loc) -testdata = caosdb.Container() -for i in range(100): - testdata.append( - caosdb.Record( - "Object-{}".format(i) - ).add_parent("MapObject" - ).add_property("longitude", random.gauss(-42.0, 5) - ).add_property("latitude", random.gauss(77.0, 5)) - ) testdata.insert(); diff --git a/src/core/js/caosdb.js b/src/core/js/caosdb.js index a2c1f55ce49e409b0153c888197002868b90a637..39109d444ff4bc8bbd0f2b2811004967565a3361 100644 --- a/src/core/js/caosdb.js +++ b/src/core/js/caosdb.js @@ -5,8 +5,8 @@ * Copyright (C) 2018-2020 Alexander Schlemmer * Copyright (C) 2018 Research Group Biomedical Physics, * Max-Planck-Institute for Dynamics and Self-Organization Göttingen - * Copyright (C) 2019-2020 IndiScale GmbH (info@indiscale.com) - * Copyright (C) 2019-2020 Timm Fitschen (t.fitschen@indiscale.com) + * Copyright (C) 2019-2022 IndiScale GmbH (info@indiscale.com) + * Copyright (C) 2019-2022 Timm Fitschen (t.fitschen@indiscale.com) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -604,6 +604,7 @@ function getPropertyFromElement(propertyelement, names = undefined) { if (valel && valel.textContent.length > 0) { value_string = valel.textContent; } else if (valel && valel.value && valel.value.length > 0) { + // this is the case when the valel is an input coming from edit_mode. value_string = valel.value; } @@ -642,7 +643,6 @@ function getPropertyFromElement(propertyelement, names = undefined) { } } - return property; } @@ -734,6 +734,16 @@ var _constructXpaths = function (selectors) { * * `getPropertyValues(entities, [["Geo Location", "latitude"], ["Geo Location", "longitude"]])` * + * When the entitieshave normal non-list references to the "Geo Location" the + * result looks like this: + * + * `[[ "50", "-39"], ...]` + * + * When the entities have a LIST of thre Geo Locations the result looks like + * this: + * + * `[[[ "50", "51", "52"], [ "-39", "-38", "-37" ]], ...]`. + * * Use empty strings for selector elements when the property name is irrelevant: * * `getPropertyValues(entities, [["", "latitude"], ["", "longitude"]])` @@ -748,11 +758,14 @@ var _constructXpaths = function (selectors) { * special cases ("name", "description", "unit", etc.) are to be added when * needed. * - * @param {XMLElement[]) entities + * @param {XMLElement[]} entities * @param {String[][]} selectors - * @return {String[][]} A table of the property values for each entity. + * @return {String[][]} A table of the property values for each entity (index + * order is `[row][column]`). Each row is an entity, each column is a value + * (or an array of values, when the entity has list properties). */ var getPropertyValues = function (entities, selectors) { + // @review Florian Spreckelsen 2022-05-06 const entity_iter = entities.evaluate("/Response/Record", entities); const table = []; @@ -762,9 +775,20 @@ var getPropertyValues = function (entities, selectors) { while (current_entity) { const row = []; for (let expr of xpaths) { - const property = entities.evaluate(expr, current_entity).iterateNext(); - if (typeof property != "undefined" && property != null) { - row.push(property.textContent.trim()); + const property_iter = entities.evaluate(expr, current_entity); + var property = property_iter.iterateNext(); + if (typeof property !== "undefined" && property !== null) { + // handle lists and single values + var values = []; + while (property !== null) { + values.push(property.textContent.trim()); + property = property_iter.iterateNext(); + } + if(values.length < 2) { + // wasn't a list + values = values[0]; + } + row.push(values); } else { row.push(undefined) } diff --git a/src/core/js/ext_map.js b/src/core/js/ext_map.js index b08c7968f10069cb9446dc0537907741e1a6d316..39e6d510f9bbdf8e18426dc9b56577a235850a8d 100644 --- a/src/core/js/ext_map.js +++ b/src/core/js/ext_map.js @@ -376,6 +376,8 @@ var caosdb_map = new function () { * @callback {mapEntityPopupGenerator} * @param {HTMLElement} entity - in HTML representation * @param {DataModelConfig} datamodel + * @param {Number} lat - latitude + * @param {Number} lng - longitude * @return {HTMLElement} a popup element. */ @@ -400,7 +402,7 @@ var caosdb_map = new function () { this._get_with_POV = function (props) { var pov = "" for (let p of props) { - pov = pov + ` WITH ${p} `; + pov = pov + ` WITH "${p}" `; } return pov; } @@ -444,12 +446,12 @@ var caosdb_map = new function () { var pov = undefined; if (typeof ids === "undefined") { pov = (caosdb_map._get_with_POV(props) + - ` WITH ${datamodel.lat} AND ${datamodel.lng}`); + ` WITH ( "${datamodel.lat}" AND "${datamodel.lng}" )`); } else { pov = caosdb_map._get_id_POV(ids); } - return `SELECT parent,${selector}${datamodel.lat},${selector}${datamodel.lng} FROM ENTITY ${recordtype} ${pov} `; + return `SELECT parent,${selector}${datamodel.lat},${selector}${datamodel.lng} FROM ENTITY "${recordtype}" ${pov} `; } @@ -541,12 +543,25 @@ var caosdb_map = new function () { * recordtype) */ this._set_subprops_at_top = function (entities, depth, datamodel) { + // @review Florian Spreckelsen 2022-05-06 var latlong = caosdb_map._get_leaf_prop(entities, depth, datamodel); for (let rec_id in latlong) { + const is_list_lat = Array.isArray(latlong[rec_id][0]); + const is_list_lng = Array.isArray(latlong[rec_id][0]); + var lat, lng; + if (is_list_lat) { + var lat_val = `<Value>${latlong[rec_id][0].join("</Value><Value>")}</Value>`; + lat = `<Property name="${datamodel.lat}" datatype="LIST<lat>">${lat_val}</Property>`; + var lng_val = `<Value>${latlong[rec_id][1].join("</Value><Value>")}</Value>`; + lng = `<Property name="${datamodel.lng}" datatype="LIST<lng>">${lng_val}</Property>`; + } else { + lat = `<Property name="${datamodel.lat}">${latlong[rec_id][0]}</Property>`; + lng = `<Property name="${datamodel.lng}">${latlong[rec_id][1]}</Property>`; + } let tmp_rec = caosdb_map._get_toplvl_rec_with_id(entities, rec_id); - tmp_rec.append(str2xml(`<Property name="${datamodel.lat}">${latlong[rec_id][0]}</Property>`).firstElementChild); - tmp_rec.append(str2xml(`<Property name="${datamodel.lng}">${latlong[rec_id][1]}</Property>`).firstElementChild); + tmp_rec.append(str2xml(lat).firstElementChild); + tmp_rec.append(str2xml(lng).firstElementChild); } } @@ -569,7 +584,7 @@ var caosdb_map = new function () { results = await transformation.transformEntities(entities); } else { results = await caosdb_map.query( - `FIND ENTITY WITH ${datamodel.lat} AND ${datamodel.lng}`); + `FIND ENTITY WITH ( "${datamodel.lat}" AND "${datamodel.lng}" )`); } const container = $('<div>').append(results)[0]; @@ -586,11 +601,12 @@ var caosdb_map = new function () { * @param {HTMLElement} entity - an entity in HTML representation. * @param {DataModelConfig} datamodel - configuration of the properties * used for the coordinates. + * @param {Number} lat - latitude + * @param {Number} lng - longitude * @returns {HTMLElement} a popup element. */ - this._make_map_popup = function (entity, datamodel) { - const lat = getProperty(entity, datamodel.lat); - const lng = getProperty(entity, datamodel.lng); + this._make_map_popup = function (entity, datamodel, lat, lng) { + // @review Florian Spreckelsen 2022-05-06 const role_label = $(entity).find( ".label.caosdb-f-entity-role").first().clone(); const parent_list = caosdb_map.make_parent_labels(entity); @@ -603,8 +619,7 @@ var caosdb_map = new function () { extra_loc_hint = `<div>Location of related ${path[path.length-1]}<div>`; } const loc = $(`<div class="small text-muted">${extra_loc_hint} - Lat: ${dms_lat} Lng: ${dms_lng} - </div>`); + Lat: ${dms_lat} Lng: ${dms_lng}</div>`); const ret = $('<div/>') .append(role_label) .append(parent_list) @@ -858,8 +873,8 @@ var caosdb_map = new function () { let panel = this.create_map_panel(); - if(!show) { - $(panel).hide(); + if (!show) { + $(panel).hide(); } $('nav').first().after(panel); @@ -994,8 +1009,8 @@ var caosdb_map = new function () { } this.config = config; var show_map = config.show; - if(sessionStorage["caosdb_map.show"]) { - show_map = JSON.parse(sessionStorage["caosdb_map.show"]); + if (sessionStorage["caosdb_map.show"]) { + show_map = JSON.parse(sessionStorage["caosdb_map.show"]); } this.init_select_handler(); this.init_view_change_handler(); @@ -1095,8 +1110,8 @@ var caosdb_map = new function () { // indicate that the map is ready: map button is present and // map is hidden or shown but initialized in either case. - this._map.whenReady(()=>{ - document.body.dispatchEvent(caosdb_map.map_ready); + this._map.whenReady(() => { + document.body.dispatchEvent(caosdb_map.map_ready); }); } catch (err) { logger.error("Could not initialize the map.", @@ -1518,6 +1533,34 @@ var caosdb_map = new function () { */ this.query = query; + /* + * Create a single map marker for the given entity. + * + * @param {HTMLElement} map_entity - the entity. + * @param {DataModelConfig} datamodel - specifies the properties for + * coordinates. + * @param {number} lat - latitude + * @param {number} lng - longitude + * @param {DivIcon_options} icon_options + * @param {number} zIndexOffset - zIndexOffset of the marker. + * @param {mapEntityPopupGenerator} [make_popup] - creates popup content. + * @returns {L.Marker} the marker for the map. + */ + var _create_single_entity_marker = function (map_entity, datamodel, lat, + lng, icon_options, zIndexOffset, make_popup) { + // @review Florian Spreckelsen 2022-05-06 + var marker = L.marker([lat, lng], { + icon: L.divIcon(icon_options) + }); + + if (zIndexOffset) { + marker.setZIndexOffset(zIndexOffset); + } + if (make_popup) { + marker.bindPopup(make_popup(map_entity, datamodel, lat, lng)); + } + return marker; + } /** * Create markers for the map for an array of entities. @@ -1531,32 +1574,82 @@ var caosdb_map = new function () { * @param {DivIcon_options} icon_options * @returns {L.Marker[]} an array of markers for the map. */ - this.create_entity_markers = function (entities, datamodel, make_popup, zIndexOffset, icon_options) { - logger.trace("enter create_entity_markers", entities, datamodel, zIndexOffset, icon_options); + this.create_entity_markers = function (entities, datamodel, make_popup, + zIndexOffset, icon_options) { + // @review Florian Spreckelsen 2022-05-06 + logger.trace("enter create_entity_markers", entities, datamodel, + zIndexOffset, icon_options); var ret = [] for (const map_entity of entities) { - var lat = getProperty(map_entity, datamodel.lat); - var lng = getProperty(map_entity, datamodel.lng); + var lat_vals = getProperty(map_entity, datamodel.lat); + var lng_vals = getProperty(map_entity, datamodel.lng); + + if (!lng_vals || !lng_vals) { + logger.debug("undefined latitude or longitude", + map_entity, lat_vals, lng_vals); + continue; + } + + // we need lat_vals and lng_lavs to be arrays so we make them + // be one + var is_list_lat = true; + if (!Array.isArray(lat_vals)) { + lat_vals = [lat_vals]; + is_list_lat = false; + } + var is_list_lng = true; + if (!Array.isArray(lng_vals)) { + lng_vals = [lng_vals]; + is_list_lng = false; + } - if (lat && lng) { - logger.debug(`create entity marker at [${lat}, ${lng}] for`, + // both array's length must match + if (is_list_lng !== is_list_lat || + (is_list_lat && is_list_lng && + lat_vals.length !== lng_vals.length)) { + logger.error("Cannot show this entity on the map. " + + "Its lat/long properties have different lenghts: ", map_entity); - var marker = L.marker([lat, lng], { - icon: L.divIcon(icon_options) - }); + continue; + } - if (zIndexOffset) { - marker.setZIndexOffset(zIndexOffset); - } - if (make_popup) { - marker.bindPopup(make_popup(map_entity, datamodel)); - } + + // zip both arrays + // [lat1, lat2, ... latN] + // [lng1, lng2, ... lngN] + // into one + // [[lat1,lng1],[lat2,lng2],... [latN,lngN]] + var latlngs = lat_vals.map(function (e, i) { + return [e, lng_vals[i]]; + }); + logger.debug(`create point marker(s) at ${latlngs} for`, + map_entity); + for (let latlng of latlngs) { + var marker = _create_single_entity_marker(map_entity, + datamodel, latlng[0], latlng[1], + icon_options, zIndexOffset, make_popup); ret.push(marker); - } else { - logger.debug("undefined latitude or longitude", - map_entity, lat, lng); } + + /* Code for showing a PATH on the map. + * Maybe we re-use it later + * + logger.debug(`create path line at ${latlngs} for`, + map_entity); + + var opts = {color:'red', smoothFactor: 10.0, weight: 1.5, opacity: 0.5}; + var opts_2 = {color:'green', smoothFactor: 10.0, weight: 3, opacity: 0.5}; + var path = L.polyline(latlngs, opts); + if (make_popup) { + path.bindPopup(make_popup(map_entity, datamodel, lat, lng)); + } + path.on("mouseover",()=>path.setStyle(opts_2)); + path.on("mouseout",()=>path.setStyle(opts)); + ret.push(path); + * + * + */ } return ret; } diff --git a/src/core/js/reference_resolver/caosdb_default_person.js b/src/core/js/reference_resolver/caosdb_default_person.js index 24f098c81d1c3f2c1f6dac6e9f6fe7d5b72f5667..dc750865c9ebf7aa01385d8f471d8d051c335fae 100644 --- a/src/core/js/reference_resolver/caosdb_default_person.js +++ b/src/core/js/reference_resolver/caosdb_default_person.js @@ -23,8 +23,9 @@ /** * @module caosdb_default_person_reference + * @version 0.1 * - * Replace the reference to a Person Record by the values of that + * @description Replace the reference to a Person Record by the values of that * Record's firstname and lastname properties. * * TODO: Make name(s) of person RecordType(s) and names of firstname diff --git a/src/doc/conf.py b/src/doc/conf.py index b346d4e97b31ebd983fe74e9847f42cf3950a6ca..3c2113f358253d9ef278d5094790e8a97b0053f2 100644 --- a/src/doc/conf.py +++ b/src/doc/conf.py @@ -26,9 +26,9 @@ copyright = '2022, IndiScale GmbH' author = 'Daniel Hornung' # The short X.Y version -version = '0.6.0' +version = '0.7.0' # The full version, including alpha/beta/rc tags -release = '0.6.0' +release = '0.7.0' # -- General configuration --------------------------------------------------- diff --git a/test/core/js/modules/caosdb.js.js b/test/core/js/modules/caosdb.js.js index 49b26b61d3448cbc0c9b8dd50b536e282d134dec..b9925f28aa8eee3a828b17ea2f0dcd6b050f2711 100644 --- a/test/core/js/modules/caosdb.js.js +++ b/test/core/js/modules/caosdb.js.js @@ -491,59 +491,37 @@ QUnit.test("_constructXpaths", function (assert) { QUnit.test("getPropertyValues", function (assert) { const test_response = str2xml(` -<Response srid="851d063d-121b-4b67-98d4-6e5ad4d31e72" timestamp="1606214042274" baseuri="https://localhost:10443" count="8"> - <Query string="select Campaign.responsible.firstname from icecore" results="8"> - <ParseTree>(cq select (prop_sel (prop_subsel (selector_txt C a m p a i g n) . (prop_subsel (selector_txt r e s p o n s i b l e) . (prop_subsel (selector_txt f i r s t n a m e ))))) from (entity icecore) <EOF>)</ParseTree> - <Role/> - <Entity>icecore</Entity> - <Selection> - <Selector name="Campaign.responsible.firstname"/> - </Selection> - </Query> +<Response> <Record id="6525" name="Test_IceCore_1"> - <Permissions/> <Property datatype="Campaign" id="6430" name="Campaign"> <Record id="6516" name="Test-2020_Camp1"> - <Permissions/> <Property datatype="REFERENCE" id="168" name="responsible"> <Record id="6515" name="Test_Scientist"> - <Permissions/> <Property datatype="DOUBLE" id="151" name="latitude" importance="FIX"> 1.34 - <Permissions/> </Property> <Property datatype="DOUBLE" id="151" name="longitude" importance="FIX"> 2 - <Permissions/> </Property> </Record> - <Permissions/> </Property> </Record> - <Permissions/> </Property> </Record> <Record id="6526" name="Test_IceCore_2"> - <Permissions/> <Property datatype="Campaign" id="6430" name="Campaign"> <Record id="6516" name="Test-2020_Camp1"> - <Permissions/> <Property datatype="REFERENCE" id="168" name="responsible"> <Record id="6515" name="Test_Scientist"> - <Permissions/> <Property datatype="DOUBLE" id="151" name="latitude" importance="FIX"> 3 - <Permissions/> </Property> <Property datatype="DOUBLE" id="151" name="longitude" importance="FIX"> 4.8345 - <Permissions/> </Property> </Record> - <Permissions/> </Property> </Record> - <Permissions/> </Property> </Record> </Response>`); @@ -553,6 +531,57 @@ QUnit.test("getPropertyValues", function (assert) { [["6525" ,"1.34", "2"], ["6526", "3", "4.8345"]]); }); +QUnit.test("getPropertyValues - with list of references", function (assert) { + const test_response = str2xml(` +<Response> + <Record id="7393"> + <Version id="7f04ebc3a09d43f8711371a1d62905e5fc6af80f" head="true" /> + <Parent id="7392" name="PathObject" /> + <Property datatype="LIST<MapObject>" id="7391" name="MapObject"> + <Value> + <Record id="7394" name="Object-0"> + <Version id="4c3b4a7ef4abc4d3b6045968f3b5f028d82baab2" head="true" /> + <Property datatype="DOUBLE" id="7389" name="longitude" importance="FIX" unit="°"> + -44.840238182501864 + </Property> + <Property datatype="DOUBLE" id="7390" name="latitude" importance="FIX" unit="°"> + 83.98152416509532 + </Property> + </Record> + </Value> + <Value> + <Record id="7395" name="Object-1"> + <Version id="42fbe0c9be68c356f81f590cddbdd3d5fc17cba4" head="true" /> + <Property datatype="DOUBLE" id="7389" name="longitude" importance="FIX" unit="°"> + -35.60247552143245 + </Property> + <Property datatype="DOUBLE" id="7390" name="latitude" importance="FIX" unit="°"> + 73.86388403927366 + </Property> + </Record> + </Value> + <Value> + <Record id="7396" name="Object-2"> + <Version id="45b71028261061e94ae198eaaa66af0612004173" head="true" /> + <Property datatype="DOUBLE" id="7389" name="longitude" importance="FIX" unit="°"> + -42.429495631197724 + </Property> + <Property datatype="DOUBLE" id="7390" name="latitude" importance="FIX" unit="°"> + 74.95382063506622 + </Property> + </Record> + </Value> + </Property> + </Record> +</Response>`); + + assert.propEqual( + getPropertyValues(test_response, [["id"], ["", "latitude"],["", + "longitude"]]), [["7393", ["83.98152416509532", "73.86388403927366", + "74.95382063506622"], ["-44.840238182501864", "-35.60247552143245", + "-42.429495631197724"]]]); +}); + // Test for bug 103 // If role is File when creating XML for entities, checksum, path and size must be given. QUnit.test("unset_file_attributes", function(assert) { diff --git a/test/core/js/modules/ext_map.js.js b/test/core/js/modules/ext_map.js.js index 4425b2e79076d92819cc31e2f5852ed1305bff54..3e55506b9abc8742ae59bf958febd9f63f968ba9 100644 --- a/test/core/js/modules/ext_map.js.js +++ b/test/core/js/modules/ext_map.js.js @@ -43,6 +43,31 @@ QUnit.module("ext_map.js", { lng + `</div> <div class="caosdb-f-property-value">5.23</div> </div> +</div>`; + // This one has lists of lat/lng. + this.test_map_entity_2 = ` +<div class="caosdb-entity-panel caosdb-properties"> + <div class="caosdb-id">1234</div> + <div class="list-group-item caosdb-f-entity-property"> + <div class="caosdb-property-datatype">LIST</div> + <div class="caosdb-property-name">` + + lat + `</div> + <div class="caosdb-f-property-value"> + <div class="caosdb-value-list"><div + class="caosdb-f-property-single-raw-value">1.231</div> + <div class="caosdb-f-property-single-raw-value">1.232</div></div> + </div> + </div> + <div class="list-group-item caosdb-f-entity-property"> + <div class="caosdb-property-datatype">LIST</div> + <div class="caosdb-property-name">` + + lng + `</div> + <div class="caosdb-f-property-value"> + <div class="caosdb-value-list"><div + class="caosdb-f-property-single-raw-value">5.231</div> + <div class="caosdb-f-property-single-raw-value">5.232</div></div> + </div> + </div> </div>`; }, beforeEach: function (assert) { @@ -177,7 +202,6 @@ QUnit.test("create_map_view", function (assert) { map = caosdb_map.create_map_view(map_panel[0], view_config); - console.log(map_panel[0]); assert.ok(map instanceof L.Map, "map instance created"); assert.equal($(map_panel).find(".leaflet-container").length, 1, "map_panel has .leaflet-container child"); assert.ok(map._crs instanceof L.Proj.CRS, "map has special crs"); @@ -208,8 +232,24 @@ QUnit.test("create_entity_markers", function (assert) { assert.notOk(markers[0].getPopup(), "no popup"); // with popup - var markers = caosdb_map.create_entity_markers(entities, datamodel, () => "popup"); + markers = caosdb_map.create_entity_markers(entities, datamodel, () => "popup"); assert.ok(markers[0].getPopup(), "has popup"); + + + // test with list of lat/lng + entities = $(this.test_map_entity_2).toArray(); + markers = caosdb_map.create_entity_markers(entities, datamodel); + assert.equal(markers.length, 2, "has two marker"); + assert.ok(markers[0] instanceof L.Marker, "is marker"); + assert.ok(markers[1] instanceof L.Marker, "is marker"); + + latlng = markers[0]._latlng; + assert.equal(latlng.lat, "1.231", "latitude set"); + assert.equal(latlng.lng, "5.231", "longitude set"); + + latlng = markers[1]._latlng; + assert.equal(latlng.lat, "1.232", "latitude set"); + assert.equal(latlng.lng, "5.232", "longitude set"); }); @@ -263,9 +303,9 @@ QUnit.test("_get_with_POV ", function (assert) { assert.equal(caosdb_map._get_with_POV( []), "", "no POV"); assert.equal(caosdb_map._get_with_POV( - ["lol"]), " WITH lol ", "single POV"); + ["lol"]), " WITH \"lol\" ", "single POV"); assert.equal(caosdb_map._get_with_POV( - ["lol", "hi"]), " WITH lol WITH hi ", "with two POV"); + ["lol", "hi"]), " WITH \"lol\" WITH \"hi\" ", "with two POV"); }); @@ -274,71 +314,49 @@ QUnit.test("_get_select_with_path ", function (assert) { assert.throws(() => caosdb_map._get_select_with_path(this.datamodel, []), /Supply at least a RecordType./, "missing value"); assert.equal(caosdb_map._get_select_with_path( this.datamodel, - ["RealRT"]), "SELECT parent,latitude,longitude FROM ENTITY RealRT WITH latitude AND longitude ", "RT only"); + ["RealRT"]), "SELECT parent,latitude,longitude FROM ENTITY \"RealRT\" WITH ( \"latitude\" AND \"longitude\" ) ", "RT only"); assert.equal(caosdb_map._get_select_with_path( this.datamodel, - ["RealRT", "prop1"]), "SELECT parent,prop1.latitude,prop1.longitude FROM ENTITY RealRT WITH prop1 WITH latitude AND longitude ", "RT with one prop"); + ["RealRT", "prop1"]), "SELECT parent,prop1.latitude,prop1.longitude FROM ENTITY \"RealRT\" WITH \"prop1\" WITH ( \"latitude\" AND \"longitude\" ) ", "RT with one prop"); assert.equal(caosdb_map._get_select_with_path( this.datamodel, - ["RealRT", "prop1", "prop2"]), "SELECT parent,prop1.prop2.latitude,prop1.prop2.longitude FROM ENTITY RealRT WITH prop1 WITH prop2 WITH latitude AND longitude ", "RT with two props"); + ["RealRT", "prop1", "prop2"]), "SELECT parent,prop1.prop2.latitude,prop1.prop2.longitude FROM ENTITY \"RealRT\" WITH \"prop1\" WITH \"prop2\" WITH ( \"latitude\" AND \"longitude\" ) ", "RT with two props"); }); QUnit.test("_get_leaf_prop", async function (assert) { const test_response = str2xml(` -<Response srid="851d063d-121b-4b67-98d4-6e5ad4d31e72" timestamp="1606214042274" baseuri="https://localhost:10443" count="8"> - <Query string="select Campaign.responsible.firstname from icecore" results="8"> - <ParseTree>(cq select (prop_sel (prop_subsel (selector_txt C a m p a i g n) . (prop_subsel (selector_txt r e s p o n s i b l e) . (prop_subsel (selector_txt f i r s t n a m e ))))) from (entity icecore) <EOF>)</ParseTree> - <Role/> - <Entity>icecore</Entity> - <Selection> - <Selector name="Campaign.responsible.firstname"/> - </Selection> - </Query> +<Response> <Record id="6525" name="Test_IceCore_1"> - <Permissions/> <Property datatype="Campaign" id="6430" name="Campaign"> <Record id="6516" name="Test-2020_Camp1"> - <Permissions/> <Property datatype="REFERENCE" id="168" name="responsible"> <Record id="6515" name="Test_Scientist"> - <Permissions/> <Property datatype="DOUBLE" id="151" name="latitude" importance="FIX"> 1.34 - <Permissions/> </Property> <Property datatype="DOUBLE" id="151" name="longitude" importance="FIX"> 2 - <Permissions/> </Property> </Record> - <Permissions/> </Property> </Record> - <Permissions/> </Property> </Record> <Record id="6526" name="Test_IceCore_2"> - <Permissions/> <Property datatype="Campaign" id="6430" name="Campaign"> <Record id="6516" name="Test-2020_Camp1"> - <Permissions/> <Property datatype="REFERENCE" id="168" name="responsible"> <Record id="6515" name="Test_Scientist"> - <Permissions/> <Property datatype="DOUBLE" id="151" name="latitude" importance="FIX"> 3 - <Permissions/> </Property> <Property datatype="DOUBLE" id="151" name="longitude" importance="FIX"> 4.8345 - <Permissions/> </Property> </Record> - <Permissions/> </Property> </Record> - <Permissions/> </Property> </Record> </Response>`); @@ -373,6 +391,85 @@ QUnit.test("_get_leaf_prop", async function (assert) { }); + +QUnit.test("_get_leaf_prop - list of referenced map entities", async function (assert) { + const test_response = str2xml(` +<Response> + <Record id="7393"> + <Version id="7f04ebc3a09d43f8711371a1d62905e5fc6af80f" head="true" /> + <Parent id="7392" name="PathObject" /> + <Property datatype="LIST<MapObject>" id="7391" name="MapObject"> + <Value> + <Record id="7394" name="Object-0"> + <Version id="4c3b4a7ef4abc4d3b6045968f3b5f028d82baab2" head="true" /> + <Property datatype="DOUBLE" id="7389" name="longitude" importance="FIX" unit="°"> + -44.840238182501864 + </Property> + <Property datatype="DOUBLE" id="7390" name="latitude" importance="FIX" unit="°"> + 83.98152416509532 + </Property> + </Record> + </Value> + <Value> + <Record id="7395" name="Object-1"> + <Version id="42fbe0c9be68c356f81f590cddbdd3d5fc17cba4" head="true" /> + <Property datatype="DOUBLE" id="7389" name="longitude" importance="FIX" unit="°"> + -35.60247552143245 + </Property> + <Property datatype="DOUBLE" id="7390" name="latitude" importance="FIX" unit="°"> + 73.86388403927366 + </Property> + </Record> + </Value> + <Value> + <Record id="7396" name="Object-2"> + <Version id="45b71028261061e94ae198eaaa66af0612004173" head="true" /> + <Property datatype="DOUBLE" id="7389" name="longitude" importance="FIX" unit="°"> + -42.429495631197724 + </Property> + <Property datatype="DOUBLE" id="7390" name="latitude" importance="FIX" unit="°"> + 74.95382063506622 + </Property> + </Record> + </Value> + </Property> + </Record> +</Response> +`); + var leaves = caosdb_map._get_leaf_prop(test_response, 1, this.datamodel) + + assert.equal(Object.keys(leaves).length, 1, "number of records"); + var leave = leaves["7393"]; + assert.ok(leave, "has entity"); + assert.deepEqual(leave, [["83.98152416509532", + "73.86388403927366", "74.95382063506622"], [ "-44.840238182501864", + "-35.60247552143245", "-42.429495631197724" ]]); + + assert.equal( + caosdb_map._get_toplvl_rec_with_id(test_response, "7393")["id"], + "7393", + "number of records"); + + caosdb_map._set_subprops_at_top( + test_response, 1, this.datamodel, { + "7393": [["83.98152416509532", "73.86388403927366", + "74.95382063506622"], [ "-44.840238182501864", + "-35.60247552143245", "-42.429495631197724" ]] }) + assert.equal($(test_response).find(`[name='longitude']`).length, + 4, + "number lng props"); + assert.equal($(test_response).find(`[name='latitude']`).length, + 4, + "number lat props"); + // after transforming, the long/lat props should be accessible + var html_ents = await transformation.transformEntities(test_response); + assert.deepEqual( + getProperty(html_ents[0], "longitude"), [ "-44.840238182501864", + "-35.60247552143245", "-42.429495631197724" ], + "longitude of first rec"); + +}); + QUnit.test("_get_id_POV", function (assert) { assert.equal(caosdb_map._get_id_POV([]), "WITH ", "no POV"); assert.equal(caosdb_map._get_id_POV([5]), "WITH id=5", "one id"); diff --git a/test/core/js/modules/form_panel.js.js b/test/core/js/modules/form_panel.js.js index bc8343d4a65233e025039e7476861fb998c2abbc..be4c4ad8e66402adedf8c386ff086be4d7cb7955 100644 --- a/test/core/js/modules/form_panel.js.js +++ b/test/core/js/modules/form_panel.js.js @@ -55,7 +55,7 @@ QUnit.test("create_show_form_callback ", function (assert) { help: help_text, }, ], }; - cb = form_panel.create_show_form_callback( panel_id, title, csv_form_config); + const cb = form_panel.create_show_form_callback( panel_id, title, csv_form_config); assert.equal(typeof cb, "function", "function created"); cb() }); diff --git a/test/docker/Dockerfile b/test/docker/Dockerfile index 2880d864bc7df86bf7cade4d6c232b387e513bfd..d3062ce7e240d7cef9a02be5c421cde6b35ae5c7 100644 --- a/test/docker/Dockerfile +++ b/test/docker/Dockerfile @@ -15,7 +15,7 @@ RUN apt-get update \ && apt-get install -f RUN pip3 install pylint pytest -RUN pip3 install caosdb>=0.5.2 +RUN pip3 install caosdb>=0.7.4 RUN pip3 install pandas RUN pip3 install git+https://gitlab.com/caosdb/caosdb-advanced-user-tools.git@dev # For automatic documentation