Store DataLayer's settings in DB
This allows to known the full datalayer behaviour without needing to load all the data, including the zoom from and to (new settings), but also the color for example. This will help also understanding datalayers usage and making stats. But no data migration is provided, it's retrocompatible (data migration in OSM FR servers would be huge, so let's see if it's really needed).
This commit is contained in:
parent
bb922d1418
commit
fa090b89df
6 changed files with 79 additions and 40 deletions
|
@ -59,7 +59,7 @@ class DataLayerForm(forms.ModelForm):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = DataLayer
|
model = DataLayer
|
||||||
fields = ('geojson', 'name', 'display_on_load', 'rank')
|
fields = ('geojson', 'name', 'display_on_load', 'rank', 'settings')
|
||||||
|
|
||||||
|
|
||||||
class MapSettingsForm(forms.ModelForm):
|
class MapSettingsForm(forms.ModelForm):
|
||||||
|
|
19
umap/migrations/0012_datalayer_settings.py
Normal file
19
umap/migrations/0012_datalayer_settings.py
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
# Generated by Django 4.2.2 on 2023-08-16 05:24
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("umap", "0011_alter_map_edit_status_alter_map_share_status"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="datalayer",
|
||||||
|
name="settings",
|
||||||
|
field=models.JSONField(
|
||||||
|
blank=True, default=dict, null=True, verbose_name="settings"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
|
@ -176,10 +176,14 @@ class Map(NamedModel):
|
||||||
settings.AUTH_USER_MODEL, blank=True, verbose_name=_("editors")
|
settings.AUTH_USER_MODEL, blank=True, verbose_name=_("editors")
|
||||||
)
|
)
|
||||||
edit_status = models.SmallIntegerField(
|
edit_status = models.SmallIntegerField(
|
||||||
choices=EDIT_STATUS, default=get_default_edit_status, verbose_name=_("edit status")
|
choices=EDIT_STATUS,
|
||||||
|
default=get_default_edit_status,
|
||||||
|
verbose_name=_("edit status"),
|
||||||
)
|
)
|
||||||
share_status = models.SmallIntegerField(
|
share_status = models.SmallIntegerField(
|
||||||
choices=SHARE_STATUS, default=get_default_share_status, verbose_name=_("share status")
|
choices=SHARE_STATUS,
|
||||||
|
default=get_default_share_status,
|
||||||
|
verbose_name=_("share status"),
|
||||||
)
|
)
|
||||||
settings = models.JSONField(
|
settings = models.JSONField(
|
||||||
blank=True, null=True, verbose_name=_("settings"), default=dict
|
blank=True, null=True, verbose_name=_("settings"), default=dict
|
||||||
|
@ -308,6 +312,9 @@ class DataLayer(NamedModel):
|
||||||
help_text=_("Display this layer on load."),
|
help_text=_("Display this layer on load."),
|
||||||
)
|
)
|
||||||
rank = models.SmallIntegerField(default=0)
|
rank = models.SmallIntegerField(default=0)
|
||||||
|
settings = models.JSONField(
|
||||||
|
blank=True, null=True, verbose_name=_("settings"), default=dict
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ("rank",)
|
ordering = ("rank",)
|
||||||
|
@ -340,7 +347,14 @@ class DataLayer(NamedModel):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def metadata(self):
|
def metadata(self):
|
||||||
return {"name": self.name, "id": self.pk, "displayOnLoad": self.display_on_load}
|
# Retrocompat: minimal settings for maps not saved after settings property
|
||||||
|
# has been introduced
|
||||||
|
obj = self.settings or {
|
||||||
|
"name": self.name,
|
||||||
|
"displayOnLoad": self.display_on_load,
|
||||||
|
}
|
||||||
|
obj["id"] = self.pk
|
||||||
|
return obj
|
||||||
|
|
||||||
def clone(self, map_inst=None):
|
def clone(self, map_inst=None):
|
||||||
new = self.__class__.objects.get(pk=self.pk)
|
new = self.__class__.objects.get(pk=self.pk)
|
||||||
|
|
|
@ -248,11 +248,6 @@ L.U.DataLayer = L.Evented.extend({
|
||||||
}
|
}
|
||||||
this.setUmapId(data.id)
|
this.setUmapId(data.id)
|
||||||
this.setOptions(data)
|
this.setOptions(data)
|
||||||
this.backupOptions()
|
|
||||||
this.connectToMap()
|
|
||||||
if (this.displayedOnLoad()) this.show()
|
|
||||||
if (!this.umap_id) this.isDirty = true
|
|
||||||
|
|
||||||
// Retrocompat
|
// Retrocompat
|
||||||
if (this.options.remoteData && this.options.remoteData.from) {
|
if (this.options.remoteData && this.options.remoteData.from) {
|
||||||
this.options.fromZoom = this.options.remoteData.from
|
this.options.fromZoom = this.options.remoteData.from
|
||||||
|
@ -260,11 +255,15 @@ L.U.DataLayer = L.Evented.extend({
|
||||||
if (this.options.remoteData && this.options.remoteData.to) {
|
if (this.options.remoteData && this.options.remoteData.to) {
|
||||||
this.options.toZoom = this.options.remoteData.to
|
this.options.toZoom = this.options.remoteData.to
|
||||||
}
|
}
|
||||||
|
this.backupOptions()
|
||||||
|
this.connectToMap()
|
||||||
|
if (this.displayedOnLoad() && this.showAtZoom()) this.show()
|
||||||
|
if (!this.umap_id) this.isDirty = true
|
||||||
|
|
||||||
this.onceLoaded(function () {
|
this.onceLoaded(function () {
|
||||||
this.map.on('moveend', this.onMoveEnd, this)
|
this.map.on('moveend', this.onMoveEnd, this)
|
||||||
this.map.on('zoomend', this.onZoomEnd, this)
|
|
||||||
})
|
})
|
||||||
|
this.map.on('zoomend', this.onZoomEnd, this)
|
||||||
},
|
},
|
||||||
|
|
||||||
onMoveEnd: function (e) {
|
onMoveEnd: function (e) {
|
||||||
|
@ -1185,6 +1184,7 @@ L.U.DataLayer = L.Evented.extend({
|
||||||
formData.append('name', this.options.name)
|
formData.append('name', this.options.name)
|
||||||
formData.append('display_on_load', !!this.options.displayOnLoad)
|
formData.append('display_on_load', !!this.options.displayOnLoad)
|
||||||
formData.append('rank', this.getRank())
|
formData.append('rank', this.getRank())
|
||||||
|
formData.append('settings', JSON.stringify(this.options))
|
||||||
// Filename support is shaky, don't do it for now.
|
// Filename support is shaky, don't do it for now.
|
||||||
const blob = new Blob([JSON.stringify(geojson)], { type: 'application/json' })
|
const blob = new Blob([JSON.stringify(geojson)], { type: 'application/json' })
|
||||||
formData.append('geojson', blob)
|
formData.append('geojson', blob)
|
||||||
|
|
|
@ -27,10 +27,11 @@ class TileLayerFactory(factory.django.DjangoModelFactory):
|
||||||
|
|
||||||
|
|
||||||
class UserFactory(factory.django.DjangoModelFactory):
|
class UserFactory(factory.django.DjangoModelFactory):
|
||||||
username = 'Joe'
|
username = "Joe"
|
||||||
email = factory.LazyAttribute(
|
email = factory.LazyAttribute(
|
||||||
lambda a: '{0}@example.com'.format(a.username).lower())
|
lambda a: "{0}@example.com".format(a.username).lower()
|
||||||
password = factory.PostGenerationMethodCall('set_password', '123123')
|
)
|
||||||
|
password = factory.PostGenerationMethodCall("set_password", "123123")
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = User
|
model = User
|
||||||
|
@ -41,32 +42,32 @@ class MapFactory(factory.django.DjangoModelFactory):
|
||||||
slug = "test-map"
|
slug = "test-map"
|
||||||
center = DEFAULT_CENTER
|
center = DEFAULT_CENTER
|
||||||
settings = {
|
settings = {
|
||||||
'geometry': {
|
"geometry": {
|
||||||
'coordinates': [13.447265624999998, 48.94415123418794],
|
"coordinates": [13.447265624999998, 48.94415123418794],
|
||||||
'type': 'Point'
|
"type": "Point",
|
||||||
},
|
},
|
||||||
'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,
|
"displayCaptionOnLoad": False,
|
||||||
'displayDataBrowserOnLoad': False,
|
"displayDataBrowserOnLoad": False,
|
||||||
'displayPopupFooter': False,
|
"displayPopupFooter": False,
|
||||||
'licence': '',
|
"licence": "",
|
||||||
'miniMap': False,
|
"miniMap": False,
|
||||||
'moreControl': True,
|
"moreControl": True,
|
||||||
'name': 'Cruising on the Donau',
|
"name": "Cruising on the Donau",
|
||||||
'scaleControl': True,
|
"scaleControl": True,
|
||||||
'tilelayer': {
|
"tilelayer": {
|
||||||
'attribution': u'\xa9 OSM Contributors',
|
"attribution": "\xa9 OSM Contributors",
|
||||||
'maxZoom': 18,
|
"maxZoom": 18,
|
||||||
'minZoom': 0,
|
"minZoom": 0,
|
||||||
'url_template': 'http://{s}.osm.fr/{z}/{x}/{y}.png'
|
"url_template": "http://{s}.osm.fr/{z}/{x}/{y}.png",
|
||||||
},
|
},
|
||||||
'tilelayersControl': True,
|
"tilelayersControl": True,
|
||||||
'zoom': 7,
|
"zoom": 7,
|
||||||
'zoomControl': True
|
"zoomControl": True,
|
||||||
},
|
},
|
||||||
'type': 'Feature'
|
"type": "Feature",
|
||||||
}
|
}
|
||||||
|
|
||||||
licence = factory.SubFactory(LicenceFactory)
|
licence = factory.SubFactory(LicenceFactory)
|
||||||
|
@ -81,7 +82,10 @@ class DataLayerFactory(factory.django.DjangoModelFactory):
|
||||||
name = "test datalayer"
|
name = "test datalayer"
|
||||||
description = "test description"
|
description = "test description"
|
||||||
display_on_load = True
|
display_on_load = True
|
||||||
geojson = factory.django.FileField(data="""{"type":"FeatureCollection","features":[{"type":"Feature","geometry":{"type":"Point","coordinates":[13.68896484375,48.55297816440071]},"properties":{"_umap_options":{"color":"DarkCyan","iconClass":"Ball"},"name":"Here","description":"Da place anonymous again 755"}}],"_umap_options":{"displayOnLoad":true,"name":"Donau","id":926}}""") # noqa
|
settings = {"displayOnLoad": True, "browsable": True, name: "test datalayer"}
|
||||||
|
geojson = factory.django.FileField(
|
||||||
|
data="""{"type":"FeatureCollection","features":[{"type":"Feature","geometry":{"type":"Point","coordinates":[13.68896484375,48.55297816440071]},"properties":{"_umap_options":{"color":"DarkCyan","iconClass":"Ball"},"name":"Here","description":"Da place anonymous again 755"}}],"_umap_options":{"displayOnLoad":true,"name":"Donau","id":926}}"""
|
||||||
|
) # noqa
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = DataLayer
|
model = DataLayer
|
||||||
|
@ -90,7 +94,7 @@ class DataLayerFactory(factory.django.DjangoModelFactory):
|
||||||
def login_required(response):
|
def login_required(response):
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
j = json.loads(response.content.decode())
|
j = json.loads(response.content.decode())
|
||||||
assert 'login_required' in j
|
assert "login_required" in j
|
||||||
redirect_url = reverse('login')
|
redirect_url = reverse("login")
|
||||||
assert j['login_required'] == redirect_url
|
assert j["login_required"] == redirect_url
|
||||||
return True
|
return True
|
||||||
|
|
|
@ -17,6 +17,7 @@ def post_data():
|
||||||
return {
|
return {
|
||||||
"name": "name",
|
"name": "name",
|
||||||
"display_on_load": True,
|
"display_on_load": True,
|
||||||
|
"settings": '{"displayOnLoad": true, "browsable": true, "name": "name"}',
|
||||||
"rank": 0,
|
"rank": 0,
|
||||||
"geojson": '{"type":"FeatureCollection","features":[{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-3.1640625,53.014783245859235],[-3.1640625,51.86292391360244],[-0.50537109375,51.385495069223204],[1.16455078125,52.38901106223456],[-0.41748046875,53.91728101547621],[-2.109375,53.85252660044951],[-3.1640625,53.014783245859235]]]},"properties":{"_umap_options":{},"name":"Ho god, sounds like a polygouine"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[1.8017578124999998,51.16556659836182],[-0.48339843749999994,49.710272582105695],[-3.1640625,50.0923932109388],[-5.60302734375,51.998410382390325]]},"properties":{"_umap_options":{},"name":"Light line"}},{"type":"Feature","geometry":{"type":"Point","coordinates":[0.63720703125,51.15178610143037]},"properties":{"_umap_options":{},"name":"marker he"}}],"_umap_options":{"displayOnLoad":true,"name":"new name","id":1668,"remoteData":{},"color":"LightSeaGreen","description":"test"}}',
|
"geojson": '{"type":"FeatureCollection","features":[{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-3.1640625,53.014783245859235],[-3.1640625,51.86292391360244],[-0.50537109375,51.385495069223204],[1.16455078125,52.38901106223456],[-0.41748046875,53.91728101547621],[-2.109375,53.85252660044951],[-3.1640625,53.014783245859235]]]},"properties":{"_umap_options":{},"name":"Ho god, sounds like a polygouine"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[1.8017578124999998,51.16556659836182],[-0.48339843749999994,49.710272582105695],[-3.1640625,50.0923932109388],[-5.60302734375,51.998410382390325]]},"properties":{"_umap_options":{},"name":"Light line"}},{"type":"Feature","geometry":{"type":"Point","coordinates":[0.63720703125,51.15178610143037]},"properties":{"_umap_options":{},"name":"marker he"}}],"_umap_options":{"displayOnLoad":true,"name":"new name","id":1668,"remoteData":{},"color":"LightSeaGreen","description":"test"}}',
|
||||||
}
|
}
|
||||||
|
@ -61,6 +62,7 @@ def test_update(client, datalayer, map, post_data):
|
||||||
j = json.loads(response.content.decode())
|
j = json.loads(response.content.decode())
|
||||||
assert "id" in j
|
assert "id" in j
|
||||||
assert datalayer.pk == j["id"]
|
assert datalayer.pk == j["id"]
|
||||||
|
assert j["browsable"] is True
|
||||||
assert Path(modified_datalayer.geojson.path).exists()
|
assert Path(modified_datalayer.geojson.path).exists()
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue