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:
|
||||
model = DataLayer
|
||||
fields = ('geojson', 'name', 'display_on_load', 'rank')
|
||||
fields = ('geojson', 'name', 'display_on_load', 'rank', 'settings')
|
||||
|
||||
|
||||
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")
|
||||
)
|
||||
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(
|
||||
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(
|
||||
blank=True, null=True, verbose_name=_("settings"), default=dict
|
||||
|
@ -308,6 +312,9 @@ class DataLayer(NamedModel):
|
|||
help_text=_("Display this layer on load."),
|
||||
)
|
||||
rank = models.SmallIntegerField(default=0)
|
||||
settings = models.JSONField(
|
||||
blank=True, null=True, verbose_name=_("settings"), default=dict
|
||||
)
|
||||
|
||||
class Meta:
|
||||
ordering = ("rank",)
|
||||
|
@ -340,7 +347,14 @@ class DataLayer(NamedModel):
|
|||
|
||||
@property
|
||||
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):
|
||||
new = self.__class__.objects.get(pk=self.pk)
|
||||
|
|
|
@ -248,11 +248,6 @@ L.U.DataLayer = L.Evented.extend({
|
|||
}
|
||||
this.setUmapId(data.id)
|
||||
this.setOptions(data)
|
||||
this.backupOptions()
|
||||
this.connectToMap()
|
||||
if (this.displayedOnLoad()) this.show()
|
||||
if (!this.umap_id) this.isDirty = true
|
||||
|
||||
// Retrocompat
|
||||
if (this.options.remoteData && 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) {
|
||||
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.map.on('moveend', this.onMoveEnd, this)
|
||||
this.map.on('zoomend', this.onZoomEnd, this)
|
||||
})
|
||||
this.map.on('zoomend', this.onZoomEnd, this)
|
||||
},
|
||||
|
||||
onMoveEnd: function (e) {
|
||||
|
@ -1185,6 +1184,7 @@ L.U.DataLayer = L.Evented.extend({
|
|||
formData.append('name', this.options.name)
|
||||
formData.append('display_on_load', !!this.options.displayOnLoad)
|
||||
formData.append('rank', this.getRank())
|
||||
formData.append('settings', JSON.stringify(this.options))
|
||||
// Filename support is shaky, don't do it for now.
|
||||
const blob = new Blob([JSON.stringify(geojson)], { type: 'application/json' })
|
||||
formData.append('geojson', blob)
|
||||
|
|
|
@ -27,10 +27,11 @@ class TileLayerFactory(factory.django.DjangoModelFactory):
|
|||
|
||||
|
||||
class UserFactory(factory.django.DjangoModelFactory):
|
||||
username = 'Joe'
|
||||
username = "Joe"
|
||||
email = factory.LazyAttribute(
|
||||
lambda a: '{0}@example.com'.format(a.username).lower())
|
||||
password = factory.PostGenerationMethodCall('set_password', '123123')
|
||||
lambda a: "{0}@example.com".format(a.username).lower()
|
||||
)
|
||||
password = factory.PostGenerationMethodCall("set_password", "123123")
|
||||
|
||||
class Meta:
|
||||
model = User
|
||||
|
@ -41,32 +42,32 @@ class MapFactory(factory.django.DjangoModelFactory):
|
|||
slug = "test-map"
|
||||
center = DEFAULT_CENTER
|
||||
settings = {
|
||||
'geometry': {
|
||||
'coordinates': [13.447265624999998, 48.94415123418794],
|
||||
'type': 'Point'
|
||||
"geometry": {
|
||||
"coordinates": [13.447265624999998, 48.94415123418794],
|
||||
"type": "Point",
|
||||
},
|
||||
'properties': {
|
||||
'datalayersControl': True,
|
||||
'description': 'Which is just the Danube, at the end',
|
||||
'displayCaptionOnLoad': False,
|
||||
'displayDataBrowserOnLoad': False,
|
||||
'displayPopupFooter': False,
|
||||
'licence': '',
|
||||
'miniMap': False,
|
||||
'moreControl': True,
|
||||
'name': 'Cruising on the Donau',
|
||||
'scaleControl': True,
|
||||
'tilelayer': {
|
||||
'attribution': u'\xa9 OSM Contributors',
|
||||
'maxZoom': 18,
|
||||
'minZoom': 0,
|
||||
'url_template': 'http://{s}.osm.fr/{z}/{x}/{y}.png'
|
||||
"properties": {
|
||||
"datalayersControl": True,
|
||||
"description": "Which is just the Danube, at the end",
|
||||
"displayCaptionOnLoad": False,
|
||||
"displayDataBrowserOnLoad": False,
|
||||
"displayPopupFooter": False,
|
||||
"licence": "",
|
||||
"miniMap": False,
|
||||
"moreControl": True,
|
||||
"name": "Cruising on the Donau",
|
||||
"scaleControl": True,
|
||||
"tilelayer": {
|
||||
"attribution": "\xa9 OSM Contributors",
|
||||
"maxZoom": 18,
|
||||
"minZoom": 0,
|
||||
"url_template": "http://{s}.osm.fr/{z}/{x}/{y}.png",
|
||||
},
|
||||
'tilelayersControl': True,
|
||||
'zoom': 7,
|
||||
'zoomControl': True
|
||||
"tilelayersControl": True,
|
||||
"zoom": 7,
|
||||
"zoomControl": True,
|
||||
},
|
||||
'type': 'Feature'
|
||||
"type": "Feature",
|
||||
}
|
||||
|
||||
licence = factory.SubFactory(LicenceFactory)
|
||||
|
@ -81,7 +82,10 @@ class DataLayerFactory(factory.django.DjangoModelFactory):
|
|||
name = "test datalayer"
|
||||
description = "test description"
|
||||
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:
|
||||
model = DataLayer
|
||||
|
@ -90,7 +94,7 @@ class DataLayerFactory(factory.django.DjangoModelFactory):
|
|||
def login_required(response):
|
||||
assert response.status_code == 200
|
||||
j = json.loads(response.content.decode())
|
||||
assert 'login_required' in j
|
||||
redirect_url = reverse('login')
|
||||
assert j['login_required'] == redirect_url
|
||||
assert "login_required" in j
|
||||
redirect_url = reverse("login")
|
||||
assert j["login_required"] == redirect_url
|
||||
return True
|
||||
|
|
|
@ -17,6 +17,7 @@ def post_data():
|
|||
return {
|
||||
"name": "name",
|
||||
"display_on_load": True,
|
||||
"settings": '{"displayOnLoad": true, "browsable": true, "name": "name"}',
|
||||
"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"}}',
|
||||
}
|
||||
|
@ -61,6 +62,7 @@ def test_update(client, datalayer, map, post_data):
|
|||
j = json.loads(response.content.decode())
|
||||
assert "id" in j
|
||||
assert datalayer.pk == j["id"]
|
||||
assert j["browsable"] is True
|
||||
assert Path(modified_datalayer.geojson.path).exists()
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue