Projet

Général

Profil

0001-maps-move-properties-from-layer-to-cell-57760.patch

Valentin Deniaud, 09 novembre 2021 17:32

Télécharger (14,4 ko)

Voir les différences:

Subject: [PATCH 1/2] maps: move properties from layer to cell (#57760)

 combo/apps/maps/forms.py                      |  4 +--
 .../migrations/0017_auto_20211104_1559.py     | 23 ++++++++++++++++
 .../migrations/0018_auto_20211104_1559.py     | 25 ++++++++++++++++++
 .../migrations/0019_auto_20211104_1603.py     | 17 ++++++++++++
 combo/apps/maps/models.py                     | 26 +++++++++++--------
 .../maps/templates/maps/map_cell_form.html    |  2 --
 combo/apps/maps/views.py                      |  3 ++-
 tests/test_maps_cells.py                      | 11 +++++---
 tests/test_maps_manager.py                    | 18 ++++++++++---
 9 files changed, 106 insertions(+), 23 deletions(-)
 create mode 100644 combo/apps/maps/migrations/0017_auto_20211104_1559.py
 create mode 100644 combo/apps/maps/migrations/0018_auto_20211104_1559.py
 create mode 100644 combo/apps/maps/migrations/0019_auto_20211104_1603.py
combo/apps/maps/forms.py
66 66
                'icon_colour',
67 67
                'cache_duration',
68 68
                'include_user_identifier',
69
                'properties',
70 69
                'geojson_query_parameter',
71 70
                'geojson_accepts_circle_param',
72 71
            ]
......
87 86
class MapLayerOptionsForm(forms.ModelForm):
88 87
    class Meta:
89 88
        model = MapLayerOptions
90
        fields = ['map_layer', 'opacity']
89
        fields = ['map_layer', 'opacity', 'properties']
91 90
        widgets = {'opacity': forms.NumberInput(attrs={'step': 0.1, 'min': 0, 'max': 1})}
92 91

  
93 92
    def __init__(self, *args, **kwargs):
......
107 106
        else:
108 107
            self.fields['opacity'].required = True
109 108
            self.fields['opacity'].initial = 1
109
            del self.fields['properties']
combo/apps/maps/migrations/0017_auto_20211104_1559.py
1
# Generated by Django 2.2.19 on 2021-11-04 14:59
2

  
3
from django.db import migrations, models
4

  
5

  
6
class Migration(migrations.Migration):
7

  
8
    dependencies = [
9
        ('maps', '0016_auto_20210927_1945'),
10
    ]
11

  
12
    operations = [
13
        migrations.AddField(
14
            model_name='maplayeroptions',
15
            name='properties',
16
            field=models.CharField(
17
                blank=True,
18
                help_text='List of properties to include, separated by commas',
19
                max_length=500,
20
                verbose_name='Properties',
21
            ),
22
        ),
23
    ]
combo/apps/maps/migrations/0018_auto_20211104_1559.py
1
# Generated by Django 2.2.19 on 2021-11-04 14:59
2

  
3
from django.db import migrations
4

  
5

  
6
def populate_cell_properties(apps, schema_editor):
7
    Map = apps.get_model('maps', 'Map')
8
    MapLayerOptions = apps.get_model('maps', 'MapLayerOptions')
9

  
10
    for cell in Map.objects.all():
11
        for layer in cell.layers.all():
12
            MapLayerOptions.objects.update_or_create(
13
                map_cell=cell, map_layer=layer, defaults={'properties': layer.properties}
14
            )
15

  
16

  
17
class Migration(migrations.Migration):
18

  
19
    dependencies = [
20
        ('maps', '0017_auto_20211104_1559'),
21
    ]
22

  
23
    operations = [
24
        migrations.RunPython(populate_cell_properties, migrations.RunPython.noop),
25
    ]
combo/apps/maps/migrations/0019_auto_20211104_1603.py
1
# Generated by Django 2.2.19 on 2021-11-04 15:03
2

  
3
from django.db import migrations
4

  
5

  
6
class Migration(migrations.Migration):
7

  
8
    dependencies = [
9
        ('maps', '0018_auto_20211104_1559'),
10
    ]
11

  
12
    operations = [
13
        migrations.RemoveField(
14
            model_name='maplayer',
15
            name='properties',
16
        ),
17
    ]
combo/apps/maps/models.py
21 21
from django.conf import settings
22 22
from django.core import serializers, validators
23 23
from django.db import models
24
from django.db.models import OuterRef, Subquery
24 25
from django.urls import reverse
25 26
from django.utils.encoding import python_2_unicode_compatible
26 27
from django.utils.html import escape
......
129 130
    icon_colour = models.CharField(_('Icon colour'), max_length=7, default='#000000')
130 131
    cache_duration = models.PositiveIntegerField(_('Cache duration'), default=60, help_text=_('In seconds.'))
131 132
    include_user_identifier = models.BooleanField(_('Include user identifier in request'), default=True)
132
    properties = models.CharField(
133
        _('Properties'),
134
        max_length=500,
135
        blank=True,
136
        help_text=_('List of properties to include, separated by commas'),
137
    )
138 133
    geojson_query_parameter = models.CharField(
139 134
        _("Query parameter for fulltext requests"),
140 135
        max_length=100,
......
195 190
        layer = next(serializers.deserialize('json', json.dumps([json_layer]), ignorenonexistent=True))
196 191
        layer.save()
197 192

  
198
    def get_geojson(self, request):
193
    def get_geojson(self, request, properties=''):
199 194
        geojson_url = get_templated_url(self.geojson_url)
200 195

  
201 196
        query_parameter = self.geojson_query_parameter
......
252 247
        else:
253 248
            features = data
254 249

  
255
        properties = []
256
        if self.properties:
257
            properties = [x.strip() for x in self.properties.split(',')]
250
        properties = [x.strip() for x in properties.split(',') if x.strip()]
251
        if properties:
258 252
            for feature in features:
259 253
                if 'display_fields' in feature['properties']:
260 254
                    # w.c.s. content, filter fields on varnames
......
457 451
    def get_geojson_layers(self):
458 452
        if not self.pk:
459 453
            return []
454
        options = MapLayerOptions.objects.filter(map_cell=self, map_layer=OuterRef('pk'))
455
        layers = self.layers.filter(kind='geojson').annotate(
456
            properties=Subquery(options.values('properties'))
457
        )
460 458
        return [
461 459
            {
462 460
                'url': reverse('mapcell-geojson', kwargs={'cell_id': self.pk, 'layer_slug': l.slug}),
......
466 464
                'marker_colour': l.marker_colour,
467 465
                'properties': [x.strip() for x in l.properties.split(',')],
468 466
            }
469
            for l in self.layers.filter(kind='geojson')
467
            for l in layers
470 468
        ]
471 469

  
472 470
    def get_cell_extra_context(self, context):
......
537 535
        null=True,
538 536
        help_text=_('Float value between 0 (transparent) and 1 (opaque)'),
539 537
    )
538
    properties = models.CharField(
539
        _('Properties'),
540
        max_length=500,
541
        blank=True,
542
        help_text=_('List of properties to include, separated by commas'),
543
    )
540 544

  
541 545
    class Meta:
542 546
        db_table = 'maps_map_layers'
combo/apps/maps/templates/maps/map_cell_form.html
11 11
    {% for option in options %}
12 12
    <li>
13 13
      <span>{{ option.map_layer.label }} {% if option.map_layer.kind == 'tiles' %}({{ option.map_layer.get_kind_display }}){% endif %}</span>
14
      {% if option.map_layer.kind == 'tiles' %}
15 14
      <a rel="popup" title="{% trans "Edit" %}" class="link-action-icon edit" href="{% url 'maps-manager-cell-edit-layer' page_pk=page.pk cell_reference=cell.get_reference layeroptions_pk=option.pk %}">{% trans "Edit" %}</a>
16
      {% endif %}
17 15
      <a rel="popup" title="{% trans "Delete" %}" class="link-action-icon delete" href="{% url 'maps-manager-cell-delete-layer' page_pk=page.pk cell_reference=cell.get_reference layeroptions_pk=option.pk %}">{% trans "Delete" %}</a>
18 16
    </li>
19 17
    {% endfor %}
combo/apps/maps/views.py
29 29
        layer = get_object_or_404(cell.layers.all(), kind='geojson', slug=kwargs['layer_slug'])
30 30
        if not cell.page.is_visible(request.user) or not cell.is_visible(user=request.user):
31 31
            return HttpResponseForbidden()
32
        geojson = layer.get_geojson(request)
32
        options = cell.maplayeroptions_set.get(map_layer=layer)
33
        geojson = layer.get_geojson(request, options.properties)
33 34
        content_type = 'application/json'
34 35
        return HttpResponse(json.dumps(geojson), content_type=content_type)
tests/test_maps_cells.py
581 581
    cell.title = 'Map'
582 582
    cell.save()
583 583
    layer.save()
584
    MapLayerOptions.objects.create(map_cell=cell, map_layer=layer)
584
    options = MapLayerOptions.objects.create(map_cell=cell, map_layer=layer)
585 585

  
586 586
    with mock.patch('combo.utils.requests_wrapper.RequestsSession.request') as requests_get:
587 587
        layer.geojson_url = 'http://example.org/geojson?t1'
......
596 596

  
597 597
    with mock.patch('combo.utils.requests_wrapper.RequestsSession.request') as requests_get:
598 598
        layer.geojson_url = 'http://example.org/geojson?t2'
599
        layer.properties = 'name, hop'
600 599
        layer.save()
600
        options.properties = 'name, hop'
601
        options.save()
601 602
        requests_get.return_value = mock.Mock(
602 603
            content=SAMPLE_GEOJSON_CONTENT, json=lambda: json.loads(SAMPLE_GEOJSON_CONTENT), status_code=200
603 604
        )
......
608 609

  
609 610
    with mock.patch('combo.utils.requests_wrapper.RequestsSession.request') as requests_get:
610 611
        layer.geojson_url = 'http://example.org/geojson?t3'
611
        layer.properties = ''
612 612
        layer.save()
613
        options.properties = ''
614
        options.save()
613 615
        requests_get.return_value = mock.Mock(
614 616
            content=SAMPLE_WCS_GEOJSON_CONTENT,
615 617
            json=lambda: json.loads(SAMPLE_WCS_GEOJSON_CONTENT),
......
621 623

  
622 624
    with mock.patch('combo.utils.requests_wrapper.RequestsSession.request') as requests_get:
623 625
        layer.geojson_url = 'http://example.org/geojson?t4'
624
        layer.properties = 'id'
625 626
        layer.save()
627
        options.properties = 'id'
628
        options.save()
626 629
        requests_get.return_value = mock.Mock(
627 630
            content=SAMPLE_WCS_GEOJSON_CONTENT,
628 631
            json=lambda: json.loads(SAMPLE_WCS_GEOJSON_CONTENT),
tests/test_maps_manager.py
88 88
    assert 'marker_colour' not in resp.context['form'].fields
89 89
    assert 'icon' not in resp.context['form'].fields
90 90
    assert 'icon_colour' not in resp.context['form'].fields
91
    assert 'properties' not in resp.context['form'].fields
92 91
    assert 'geojson_query_parameter' not in resp.context['form'].fields
93 92
    assert 'geojson_accepts_circle_param' not in resp.context['form'].fields
94 93
    resp.forms[0]['label'] = 'Test'
......
150 149
    assert 'marker_colour' not in resp.context['form']
151 150
    assert 'icon' not in resp.context['form']
152 151
    assert 'icon_colour' not in resp.context['form']
153
    assert 'properties' not in resp.context['form']
154 152
    resp.forms[0]['tiles_default'] = False
155 153
    resp = resp.forms[0].submit()
156 154
    assert resp.location.endswith('/manage/maps/')
......
238 236
    assert options.map_layer == layer
239 237

  
240 238
    resp = resp.follow()
241
    assert '/layer/%s/edit/' % options.pk not in resp.text
242 239
    assert list(cell.get_free_geojson_layers()) == []
243 240
    assert list(cell.get_free_tiles_layers()) == [tiles_layer]
244 241
    assert '/add-layer/geojson/' not in resp.text
242

  
243
    resp = resp.click(href='.*/layer/%s/edit/$' % options.pk)
244
    assert 'map_layer' not in resp.context['form'].fields
245
    assert 'opacity' not in resp.context['form'].fields
246
    resp.form['properties'] = 'a, b'
247
    resp = resp.form.submit()
248
    assert resp.status_int == 302
249
    assert resp.location.endswith('/manage/pages/%s/#cell-%s' % (page.pk, cell.get_reference()))
250
    options = MapLayerOptions.objects.get()
251
    options.refresh_from_db()
252
    assert options.properties == 'a, b'
253

  
254
    resp = resp.follow()
245 255
    resp = resp.click(href='.*/layer/%s/delete/$' % options.pk)
246 256
    resp = resp.forms[0].submit()
247 257
    assert resp.status_int == 302
......
253 263
    assert list(cell.get_free_tiles_layers()) == [tiles_layer]
254 264
    resp = resp.click(href='.*/add-layer/tiles/$')
255 265
    assert list(resp.context['form'].fields['map_layer'].queryset) == [tiles_layer]
266
    assert 'properties' not in resp.context['form'].fields
256 267
    resp.forms[0]['map_layer'] = tiles_layer.pk
257 268
    resp.forms[0]['opacity'] = 1
258 269
    resp = resp.forms[0].submit()
......
267 278
    resp = resp.follow()
268 279
    resp = resp.click(href='.*/layer/%s/edit/$' % options.pk)
269 280
    assert 'map_layer' not in resp.context['form'].fields
281
    assert 'properties' not in resp.context['form'].fields
270 282
    resp.forms[0]['opacity'] = 0.5
271 283
    resp = resp.forms[0].submit()
272 284
    assert resp.status_int == 302
273
-