Merge pull request #1396 from umap-project/backup-api
Full map download endpoint
This commit is contained in:
commit
be0cefd02a
12 changed files with 142 additions and 202 deletions
|
@ -319,7 +319,8 @@ table.maps thead tr {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
.leaflet-container a.button {
|
.leaflet-container a.button {
|
||||||
color: #eeeeec;
|
color: #323737;
|
||||||
|
font-size: 13.3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************* */
|
/* ************************************************* */
|
||||||
|
|
|
@ -924,15 +924,6 @@ L.U.Map.include({
|
||||||
ext: '.csv',
|
ext: '.csv',
|
||||||
filetype: 'text/csv',
|
filetype: 'text/csv',
|
||||||
},
|
},
|
||||||
umap: {
|
|
||||||
name: L._('Full map data'),
|
|
||||||
formatter: function (map) {
|
|
||||||
return map.serialize()
|
|
||||||
},
|
|
||||||
ext: '.umap',
|
|
||||||
filetype: 'application/json',
|
|
||||||
selected: true,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
renderEditToolbar: function () {
|
renderEditToolbar: function () {
|
||||||
|
@ -1140,6 +1131,20 @@ L.U.Map.include({
|
||||||
shortUrl.value = this.options.shortUrl
|
shortUrl.value = this.options.shortUrl
|
||||||
}
|
}
|
||||||
L.DomUtil.create('hr', '', container)
|
L.DomUtil.create('hr', '', container)
|
||||||
|
L.DomUtil.add('h4', '', container, L._('Backup data'))
|
||||||
|
const downloadUrl = L.Util.template(this.options.urls.map_download, {
|
||||||
|
map_id: this.options.umap_id,
|
||||||
|
})
|
||||||
|
const link = L.DomUtil.createLink(
|
||||||
|
'button',
|
||||||
|
container,
|
||||||
|
L._('Download full data'),
|
||||||
|
downloadUrl
|
||||||
|
)
|
||||||
|
let name = this.options.name || 'data'
|
||||||
|
name = name.replace(/[^a-z0-9]/gi, '_').toLowerCase()
|
||||||
|
link.setAttribute('download', `${name}.umap`)
|
||||||
|
L.DomUtil.create('hr', '', container)
|
||||||
L.DomUtil.add('h4', '', container, L._('Download data'))
|
L.DomUtil.add('h4', '', container, L._('Download data'))
|
||||||
const typeInput = L.DomUtil.create('select', '', container)
|
const typeInput = L.DomUtil.create('select', '', container)
|
||||||
typeInput.name = 'format'
|
typeInput.name = 'format'
|
||||||
|
@ -1149,12 +1154,6 @@ L.U.Map.include({
|
||||||
container,
|
container,
|
||||||
L._('Only visible features will be downloaded.')
|
L._('Only visible features will be downloaded.')
|
||||||
)
|
)
|
||||||
exportCaveat.id = 'export_caveat_text'
|
|
||||||
const toggleCaveat = () => {
|
|
||||||
if (typeInput.value === 'umap') exportCaveat.style.display = 'none'
|
|
||||||
else exportCaveat.style.display = 'inherit'
|
|
||||||
}
|
|
||||||
L.DomEvent.on(typeInput, 'change', toggleCaveat)
|
|
||||||
for (const key in this.EXPORT_TYPES) {
|
for (const key in this.EXPORT_TYPES) {
|
||||||
if (this.EXPORT_TYPES.hasOwnProperty(key)) {
|
if (this.EXPORT_TYPES.hasOwnProperty(key)) {
|
||||||
option = L.DomUtil.create('option', '', typeInput)
|
option = L.DomUtil.create('option', '', typeInput)
|
||||||
|
@ -1163,18 +1162,11 @@ L.U.Map.include({
|
||||||
if (this.EXPORT_TYPES[key].selected) option.selected = true
|
if (this.EXPORT_TYPES[key].selected) option.selected = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
toggleCaveat()
|
|
||||||
L.DomUtil.createButton(
|
L.DomUtil.createButton(
|
||||||
'button',
|
'button',
|
||||||
container,
|
container,
|
||||||
L._('Download data'),
|
L._('Download data'),
|
||||||
() => {
|
() => this.download(typeInput.value),
|
||||||
if (typeInput.value === 'umap') {
|
|
||||||
this.fullDownload()
|
|
||||||
} else {
|
|
||||||
this.download(typeInput.value)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
this
|
this
|
||||||
)
|
)
|
||||||
this.ui.openPanel({ data: { html: container } })
|
this.ui.openPanel({ data: { html: container } })
|
||||||
|
|
|
@ -272,7 +272,10 @@ L.U.Map.include({
|
||||||
url.searchParams.delete('edit')
|
url.searchParams.delete('edit')
|
||||||
history.pushState({}, '', url)
|
history.pushState({}, '', url)
|
||||||
}
|
}
|
||||||
if (L.Util.queryString('download')) this.download()
|
if (L.Util.queryString('download'))
|
||||||
|
window.location = L.Util.template(this.options.urls.map_download, {
|
||||||
|
map_id: this.options.umap_id,
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
window.onbeforeunload = () => this.isDirty || null
|
window.onbeforeunload = () => this.isDirty || null
|
||||||
|
@ -396,7 +399,6 @@ L.U.Map.include({
|
||||||
},
|
},
|
||||||
|
|
||||||
loadDatalayers: function (force) {
|
loadDatalayers: function (force) {
|
||||||
force = force || L.Util.queryString('download') // In case we are in download mode, let's go strait to loading all data
|
|
||||||
const total = this.datalayers_index.length
|
const total = this.datalayers_index.length
|
||||||
// toload => datalayer metadata remaining to load (synchronous)
|
// toload => datalayer metadata remaining to load (synchronous)
|
||||||
// dataToload => datalayer data remaining to load (asynchronous)
|
// dataToload => datalayer data remaining to load (asynchronous)
|
||||||
|
@ -824,14 +826,8 @@ L.U.Map.include({
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
fullDownload: function () {
|
|
||||||
// Make sure all data is loaded before downloading
|
|
||||||
this.once('dataloaded', () => this.download())
|
|
||||||
this.loadDatalayers(true) // Force load
|
|
||||||
},
|
|
||||||
|
|
||||||
format: function (mode) {
|
format: function (mode) {
|
||||||
const type = this.EXPORT_TYPES[mode || 'umap']
|
const type = this.EXPORT_TYPES[mode]
|
||||||
const content = type.formatter(this)
|
const content = type.formatter(this)
|
||||||
let name = this.options.name || 'data'
|
let name = this.options.name || 'data'
|
||||||
name = name.replace(/[^a-z0-9]/gi, '_').toLowerCase()
|
name = name.replace(/[^a-z0-9]/gi, '_').toLowerCase()
|
||||||
|
@ -1074,24 +1070,6 @@ L.U.Map.include({
|
||||||
return properties
|
return properties
|
||||||
},
|
},
|
||||||
|
|
||||||
serialize: function () {
|
|
||||||
// Do not use local path during unit tests
|
|
||||||
const uri = window.location.protocol === 'file:' ? null : window.location.href
|
|
||||||
const umapfile = {
|
|
||||||
type: 'umap',
|
|
||||||
uri: uri,
|
|
||||||
properties: this.exportOptions(),
|
|
||||||
geometry: this.geometry(),
|
|
||||||
layers: [],
|
|
||||||
}
|
|
||||||
|
|
||||||
this.eachDataLayer((datalayer) => {
|
|
||||||
umapfile.layers.push(datalayer.umapGeoJSON())
|
|
||||||
})
|
|
||||||
|
|
||||||
return JSON.stringify(umapfile, null, 2)
|
|
||||||
},
|
|
||||||
|
|
||||||
saveSelf: function () {
|
saveSelf: function () {
|
||||||
const geojson = {
|
const geojson = {
|
||||||
type: 'Feature',
|
type: 'Feature',
|
||||||
|
|
|
@ -73,7 +73,6 @@ describe('L.U.Controls', function () {
|
||||||
happen.click(qs('.umap-browse-actions .umap-browse-link'))
|
happen.click(qs('.umap-browse-actions .umap-browse-link'))
|
||||||
assert.equal(qsa('#browse_data_datalayer_62 ul li').length, 3)
|
assert.equal(qsa('#browse_data_datalayer_62 ul li').length, 3)
|
||||||
})
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('#exportPanel()', function () {
|
describe('#exportPanel()', function () {
|
||||||
|
|
|
@ -101,131 +101,5 @@ describe('L.U.Map.Export', function () {
|
||||||
'<?xml version="1.0" encoding="UTF-8"?><kml xmlns="http://www.opengis.net/kml/2.2"><Document><Placemark><name>name poly</name><ExtendedData><Data name="name"><value>name poly</value></Data></ExtendedData><Polygon><outerBoundaryIs><LinearRing><coordinates>11.25,53.585984 10.151367,52.975108 12.689209,52.167194 14.084473,53.199452 12.634277,53.618579 11.25,53.585984 11.25,53.585984</coordinates></LinearRing></outerBoundaryIs></Polygon></Placemark><Placemark><name>test</name><ExtendedData><Data name="_umap_options"><value>[object Object]</value></Data><Data name="name"><value>test</value></Data></ExtendedData><Point><coordinates>-0.274658,52.57635</coordinates></Point></Placemark><Placemark><name>test</name><ExtendedData><Data name="_umap_options"><value>[object Object]</value></Data><Data name="name"><value>test</value></Data></ExtendedData><LineString><coordinates>-0.571289,54.476422 0.439453,54.610255 1.724854,53.448807 4.163818,53.988395 5.306396,53.533778 6.591797,53.709714 7.042236,53.350551</coordinates></LineString></Placemark></Document></kml>'
|
'<?xml version="1.0" encoding="UTF-8"?><kml xmlns="http://www.opengis.net/kml/2.2"><Document><Placemark><name>name poly</name><ExtendedData><Data name="name"><value>name poly</value></Data></ExtendedData><Polygon><outerBoundaryIs><LinearRing><coordinates>11.25,53.585984 10.151367,52.975108 12.689209,52.167194 14.084473,53.199452 12.634277,53.618579 11.25,53.585984 11.25,53.585984</coordinates></LinearRing></outerBoundaryIs></Polygon></Placemark><Placemark><name>test</name><ExtendedData><Data name="_umap_options"><value>[object Object]</value></Data><Data name="name"><value>test</value></Data></ExtendedData><Point><coordinates>-0.274658,52.57635</coordinates></Point></Placemark><Placemark><name>test</name><ExtendedData><Data name="_umap_options"><value>[object Object]</value></Data><Data name="name"><value>test</value></Data></ExtendedData><LineString><coordinates>-0.571289,54.476422 0.439453,54.610255 1.724854,53.448807 4.163818,53.988395 5.306396,53.533778 6.591797,53.709714 7.042236,53.350551</coordinates></LineString></Placemark></Document></kml>'
|
||||||
assert.equal(content, expected)
|
assert.equal(content, expected)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should export to umap', function () {
|
|
||||||
const { content, filetype, filename } = this.map.format('umap')
|
|
||||||
assert.equal(filetype, 'application/json')
|
|
||||||
assert.equal(filename, 'name_of_the_map.umap')
|
|
||||||
const expected = {
|
|
||||||
type: 'umap',
|
|
||||||
uri: null,
|
|
||||||
properties: {
|
|
||||||
easing: false,
|
|
||||||
embedControl: true,
|
|
||||||
fullscreenControl: true,
|
|
||||||
searchControl: true,
|
|
||||||
datalayersControl: true,
|
|
||||||
zoomControl: true,
|
|
||||||
permanentCreditBackground: true,
|
|
||||||
slideshow: {},
|
|
||||||
captionMenus: true,
|
|
||||||
captionBar: false,
|
|
||||||
limitBounds: {},
|
|
||||||
overlay: null,
|
|
||||||
tilelayer: {
|
|
||||||
attribution: 'HOT and friends',
|
|
||||||
name: 'HOT OSM-fr server',
|
|
||||||
url_template: 'http://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png',
|
|
||||||
rank: 99,
|
|
||||||
minZoom: 0,
|
|
||||||
maxZoom: 20,
|
|
||||||
id: 2,
|
|
||||||
},
|
|
||||||
licence: '',
|
|
||||||
description: 'The description of the map',
|
|
||||||
name: 'name of the map',
|
|
||||||
displayPopupFooter: false,
|
|
||||||
miniMap: false,
|
|
||||||
moreControl: true,
|
|
||||||
scaleControl: true,
|
|
||||||
scrollWheelZoom: true,
|
|
||||||
zoom: 6,
|
|
||||||
},
|
|
||||||
geometry: {
|
|
||||||
type: 'Point',
|
|
||||||
coordinates: [5.0592041015625, 52.05924589011585],
|
|
||||||
},
|
|
||||||
layers: [
|
|
||||||
{
|
|
||||||
type: 'FeatureCollection',
|
|
||||||
features: [
|
|
||||||
{
|
|
||||||
type: 'Feature',
|
|
||||||
properties: {
|
|
||||||
name: 'name poly',
|
|
||||||
},
|
|
||||||
geometry: {
|
|
||||||
type: 'Polygon',
|
|
||||||
coordinates: [
|
|
||||||
[
|
|
||||||
[11.25, 53.585984],
|
|
||||||
[10.151367, 52.975108],
|
|
||||||
[12.689209, 52.167194],
|
|
||||||
[14.084473, 53.199452],
|
|
||||||
[12.634277, 53.618579],
|
|
||||||
[11.25, 53.585984],
|
|
||||||
[11.25, 53.585984],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'Feature',
|
|
||||||
properties: {
|
|
||||||
_umap_options: {
|
|
||||||
color: 'OliveDrab',
|
|
||||||
},
|
|
||||||
name: 'test',
|
|
||||||
},
|
|
||||||
geometry: {
|
|
||||||
type: 'Point',
|
|
||||||
coordinates: [-0.274658, 52.57635],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'Feature',
|
|
||||||
properties: {
|
|
||||||
_umap_options: {
|
|
||||||
fill: false,
|
|
||||||
},
|
|
||||||
name: 'test',
|
|
||||||
},
|
|
||||||
geometry: {
|
|
||||||
type: 'LineString',
|
|
||||||
coordinates: [
|
|
||||||
[-0.571289, 54.476422],
|
|
||||||
[0.439453, 54.610255],
|
|
||||||
[1.724854, 53.448807],
|
|
||||||
[4.163818, 53.988395],
|
|
||||||
[5.306396, 53.533778],
|
|
||||||
[6.591797, 53.709714],
|
|
||||||
[7.042236, 53.350551],
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
_umap_options: {
|
|
||||||
displayOnLoad: true,
|
|
||||||
browsable: true,
|
|
||||||
editMode: 'advanced',
|
|
||||||
iconClass: 'Default',
|
|
||||||
name: 'Elephants',
|
|
||||||
id: 62,
|
|
||||||
pictogram_url: null,
|
|
||||||
opacity: null,
|
|
||||||
weight: null,
|
|
||||||
fillColor: '',
|
|
||||||
color: '',
|
|
||||||
stroke: true,
|
|
||||||
smoothFactor: null,
|
|
||||||
dashArray: '',
|
|
||||||
fillOpacity: null,
|
|
||||||
fill: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
assert.deepEqual(JSON.parse(content), expected)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -132,6 +132,7 @@ function initMap(options) {
|
||||||
datalayer_version: '/datalayer/{pk}/{name}',
|
datalayer_version: '/datalayer/{pk}/{name}',
|
||||||
pictogram_list_json: '/pictogram/json/',
|
pictogram_list_json: '/pictogram/json/',
|
||||||
map_update_permissions: '/map/{map_id}/update/permissions/',
|
map_update_permissions: '/map/{map_id}/update/permissions/',
|
||||||
|
map_download: '/map/{map_id}/download/',
|
||||||
},
|
},
|
||||||
default_iconUrl: '../src/img/marker.png',
|
default_iconUrl: '../src/img/marker.png',
|
||||||
zoom: 6,
|
zoom: 6,
|
||||||
|
|
|
@ -71,13 +71,11 @@ class MapFactory(factory.django.DjangoModelFactory):
|
||||||
"properties": {
|
"properties": {
|
||||||
"datalayersControl": True,
|
"datalayersControl": True,
|
||||||
"description": "Which is just the Danube, at the end",
|
"description": "Which is just the Danube, at the end",
|
||||||
"displayCaptionOnLoad": False,
|
|
||||||
"displayDataBrowserOnLoad": False,
|
|
||||||
"displayPopupFooter": False,
|
"displayPopupFooter": False,
|
||||||
"licence": "",
|
"licence": "",
|
||||||
"miniMap": False,
|
"miniMap": False,
|
||||||
"moreControl": True,
|
"moreControl": True,
|
||||||
"name": "Cruising on the Donau",
|
"name": name,
|
||||||
"scaleControl": True,
|
"scaleControl": True,
|
||||||
"tilelayer": {
|
"tilelayer": {
|
||||||
"attribution": "\xa9 OSM Contributors",
|
"attribution": "\xa9 OSM Contributors",
|
||||||
|
@ -100,6 +98,7 @@ class MapFactory(factory.django.DjangoModelFactory):
|
||||||
def _adjust_kwargs(cls, **kwargs):
|
def _adjust_kwargs(cls, **kwargs):
|
||||||
# Make sure there is no persistency
|
# Make sure there is no persistency
|
||||||
kwargs["settings"] = copy.deepcopy(kwargs["settings"])
|
kwargs["settings"] = copy.deepcopy(kwargs["settings"])
|
||||||
|
kwargs["settings"]["properties"]["name"] = kwargs["name"]
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|
|
@ -9,12 +9,12 @@ pytestmark = pytest.mark.django_db
|
||||||
|
|
||||||
def test_umap_export(map, live_server, datalayer, page):
|
def test_umap_export(map, live_server, datalayer, page):
|
||||||
page.goto(f"{live_server.url}{map.get_absolute_url()}?share")
|
page.goto(f"{live_server.url}{map.get_absolute_url()}?share")
|
||||||
button = page.get_by_role("button", name="Download data")
|
link = page.get_by_role("link", name="Download full data")
|
||||||
expect(button).to_be_visible()
|
expect(link).to_be_visible()
|
||||||
with page.expect_download() as download_info:
|
with page.expect_download() as download_info:
|
||||||
button.click()
|
link.click()
|
||||||
download = download_info.value
|
download = download_info.value
|
||||||
assert download.suggested_filename == "test_map.umap"
|
assert download.suggested_filename == "umap_backup_test-map.umap"
|
||||||
path = Path("/tmp/") / download.suggested_filename
|
path = Path("/tmp/") / download.suggested_filename
|
||||||
download.save_as(path)
|
download.save_as(path)
|
||||||
downloaded = json.loads(path.read_text())
|
downloaded = json.loads(path.read_text())
|
||||||
|
@ -29,14 +29,12 @@ def test_umap_export(map, live_server, datalayer, page):
|
||||||
"_umap_options": {
|
"_umap_options": {
|
||||||
"browsable": True,
|
"browsable": True,
|
||||||
"displayOnLoad": True,
|
"displayOnLoad": True,
|
||||||
"editMode": "disabled",
|
|
||||||
"inCaption": True,
|
|
||||||
"name": "test datalayer",
|
"name": "test datalayer",
|
||||||
},
|
},
|
||||||
"features": [
|
"features": [
|
||||||
{
|
{
|
||||||
"geometry": {
|
"geometry": {
|
||||||
"coordinates": [13.688965, 48.552978],
|
"coordinates": [13.68896484375, 48.55297816440071],
|
||||||
"type": "Point",
|
"type": "Point",
|
||||||
},
|
},
|
||||||
"properties": {
|
"properties": {
|
||||||
|
@ -51,25 +49,14 @@ def test_umap_export(map, live_server, datalayer, page):
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
"captionBar": False,
|
|
||||||
"captionMenus": True,
|
|
||||||
"datalayersControl": True,
|
"datalayersControl": True,
|
||||||
"description": "Which is just the Danube, at the end",
|
"description": "Which is just the Danube, at the end",
|
||||||
"displayPopupFooter": False,
|
"displayPopupFooter": False,
|
||||||
"easing": False,
|
|
||||||
"embedControl": True,
|
|
||||||
"fullscreenControl": True,
|
|
||||||
"licence": "",
|
"licence": "",
|
||||||
"limitBounds": {},
|
|
||||||
"miniMap": False,
|
"miniMap": False,
|
||||||
"moreControl": True,
|
"moreControl": True,
|
||||||
"name": "test map",
|
"name": "test map",
|
||||||
"overlay": None,
|
|
||||||
"permanentCreditBackground": True,
|
|
||||||
"scaleControl": True,
|
"scaleControl": True,
|
||||||
"scrollWheelZoom": True,
|
|
||||||
"searchControl": True,
|
|
||||||
"slideshow": {},
|
|
||||||
"tilelayer": {
|
"tilelayer": {
|
||||||
"attribution": "© OSM Contributors",
|
"attribution": "© OSM Contributors",
|
||||||
"maxZoom": 18,
|
"maxZoom": 18,
|
||||||
|
|
|
@ -603,3 +603,81 @@ def test_can_send_link_on_anonymous_map_with_cookie(cookieclient, anonymap):
|
||||||
assert resp.status_code == 200
|
assert resp.status_code == 200
|
||||||
assert len(mail.outbox) == 1
|
assert len(mail.outbox) == 1
|
||||||
assert mail.outbox[0].subject == "The uMap edit link for your map: test map"
|
assert mail.outbox[0].subject == "The uMap edit link for your map: test map"
|
||||||
|
|
||||||
|
|
||||||
|
def test_download(client, map, datalayer):
|
||||||
|
url = reverse("map_download", args=(map.pk,))
|
||||||
|
response = client.get(url)
|
||||||
|
assert response.status_code == 200
|
||||||
|
# Test response is a json
|
||||||
|
j = json.loads(response.content.decode())
|
||||||
|
assert j["type"] == "umap"
|
||||||
|
assert j["uri"] == f"http://testserver/en/map/test-map_{map.pk}"
|
||||||
|
assert j["geometry"] == {
|
||||||
|
"coordinates": [13.447265624999998, 48.94415123418794],
|
||||||
|
"type": "Point",
|
||||||
|
}
|
||||||
|
assert j["properties"] == {
|
||||||
|
"datalayersControl": True,
|
||||||
|
"description": "Which is just the Danube, at the end",
|
||||||
|
"displayPopupFooter": False,
|
||||||
|
"licence": "",
|
||||||
|
"miniMap": False,
|
||||||
|
"moreControl": True,
|
||||||
|
"name": "test map",
|
||||||
|
"scaleControl": True,
|
||||||
|
"tilelayer": {
|
||||||
|
"attribution": "© OSM Contributors",
|
||||||
|
"maxZoom": 18,
|
||||||
|
"minZoom": 0,
|
||||||
|
"url_template": "http://{s}.osm.fr/{z}/{x}/{y}.png",
|
||||||
|
},
|
||||||
|
"tilelayersControl": True,
|
||||||
|
"zoom": 7,
|
||||||
|
"zoomControl": True,
|
||||||
|
}
|
||||||
|
assert j["layers"] == [
|
||||||
|
{
|
||||||
|
"_umap_options": {
|
||||||
|
"browsable": True,
|
||||||
|
"displayOnLoad": True,
|
||||||
|
"name": "test datalayer",
|
||||||
|
},
|
||||||
|
"features": [
|
||||||
|
{
|
||||||
|
"geometry": {
|
||||||
|
"coordinates": [13.68896484375, 48.55297816440071],
|
||||||
|
"type": "Point",
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"_umap_options": {"color": "DarkCyan", "iconClass": "Ball"},
|
||||||
|
"description": "Da place anonymous again 755",
|
||||||
|
"name": "Here",
|
||||||
|
},
|
||||||
|
"type": "Feature",
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "FeatureCollection",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("share_status", [Map.PRIVATE, Map.BLOCKED])
|
||||||
|
def test_download_shared_status_map(client, map, datalayer, share_status):
|
||||||
|
map.share_status = share_status
|
||||||
|
map.save()
|
||||||
|
url = reverse("map_download", args=(map.pk,))
|
||||||
|
response = client.get(url)
|
||||||
|
assert response.status_code == 403
|
||||||
|
|
||||||
|
|
||||||
|
def test_download_my_map(client, map, datalayer):
|
||||||
|
map.share_status = Map.PRIVATE
|
||||||
|
map.save()
|
||||||
|
client.login(username=map.owner.username, password="123123")
|
||||||
|
url = reverse("map_download", args=(map.pk,))
|
||||||
|
response = client.get(url)
|
||||||
|
assert response.status_code == 200
|
||||||
|
# Test response is a json
|
||||||
|
j = json.loads(response.content.decode())
|
||||||
|
assert j["type"] == "umap"
|
||||||
|
|
|
@ -289,8 +289,8 @@ def test_user_dashboard_display_user_maps(client, map):
|
||||||
def test_user_dashboard_display_user_maps_distinct(client, map):
|
def test_user_dashboard_display_user_maps_distinct(client, map):
|
||||||
# cf https://github.com/umap-project/umap/issues/1325
|
# cf https://github.com/umap-project/umap/issues/1325
|
||||||
anonymap = MapFactory(name="Map witout owner should not appear")
|
anonymap = MapFactory(name="Map witout owner should not appear")
|
||||||
user1 = UserFactory(username='user1')
|
user1 = UserFactory(username="user1")
|
||||||
user2 = UserFactory(username='user2')
|
user2 = UserFactory(username="user2")
|
||||||
map.editors.add(user1)
|
map.editors.add(user1)
|
||||||
map.editors.add(user2)
|
map.editors.add(user2)
|
||||||
map.save()
|
map.save()
|
||||||
|
@ -298,7 +298,7 @@ def test_user_dashboard_display_user_maps_distinct(client, map):
|
||||||
response = client.get(reverse("user_dashboard"))
|
response = client.get(reverse("user_dashboard"))
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
body = response.content.decode()
|
body = response.content.decode()
|
||||||
assert body.count(map.name) == 1
|
assert body.count(f'<a href="/en/map/test-map_{map.pk}">test map</a>') == 1
|
||||||
assert body.count(anonymap.name) == 0
|
assert body.count(anonymap.name) == 0
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -41,6 +41,11 @@ urlpatterns = [
|
||||||
),
|
),
|
||||||
re_path(r"^i18n/", include("django.conf.urls.i18n")),
|
re_path(r"^i18n/", include("django.conf.urls.i18n")),
|
||||||
re_path(r"^agnocomplete/", include("agnocomplete.urls")),
|
re_path(r"^agnocomplete/", include("agnocomplete.urls")),
|
||||||
|
re_path(
|
||||||
|
r"^map/(?P<map_id>\d+)/download/",
|
||||||
|
can_view_map(views.MapDownload.as_view()),
|
||||||
|
name="map_download",
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
i18n_urls = [
|
i18n_urls = [
|
||||||
|
|
|
@ -607,6 +607,32 @@ class MapView(MapDetailMixin, PermissionsMixin, DetailView):
|
||||||
return Star.objects.filter(by=user, map=self.object).exists()
|
return Star.objects.filter(by=user, map=self.object).exists()
|
||||||
|
|
||||||
|
|
||||||
|
class MapDownload(DetailView):
|
||||||
|
model = Map
|
||||||
|
pk_url_kwarg = "map_id"
|
||||||
|
|
||||||
|
def get_canonical_url(self):
|
||||||
|
return reverse("map_download", args=(self.object.pk,))
|
||||||
|
|
||||||
|
def render_to_response(self, context, *args, **kwargs):
|
||||||
|
geojson = self.object.settings
|
||||||
|
geojson["type"] = "umap"
|
||||||
|
geojson["uri"] = self.request.build_absolute_uri(self.object.get_absolute_url())
|
||||||
|
datalayers = []
|
||||||
|
for datalayer in self.object.datalayer_set.all():
|
||||||
|
with open(datalayer.geojson.path, "rb") as f:
|
||||||
|
layer = json.loads(f.read())
|
||||||
|
if datalayer.settings:
|
||||||
|
layer["_umap_options"] = datalayer.settings
|
||||||
|
datalayers.append(layer)
|
||||||
|
geojson["layers"] = datalayers
|
||||||
|
response = simple_json_response(**geojson)
|
||||||
|
response[
|
||||||
|
"Content-Disposition"
|
||||||
|
] = f'attachment; filename="umap_backup_{self.object.slug}.umap"'
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
class MapViewGeoJSON(MapView):
|
class MapViewGeoJSON(MapView):
|
||||||
def get_canonical_url(self):
|
def get_canonical_url(self):
|
||||||
return reverse("map_geojson", args=(self.object.pk,))
|
return reverse("map_geojson", args=(self.object.pk,))
|
||||||
|
|
Loading…
Reference in a new issue