Projet

Général

Profil

0001-opengis-add-text-search-for-queries-40743.patch

Valentin Deniaud, 14 avril 2020 12:04

Télécharger (6,69 ko)

Voir les différences:

Subject: [PATCH] opengis: add text search for queries (#40743)

 .../migrations/0009_auto_20200407_1544.py     | 26 +++++++++++++++++
 passerelle/apps/opengis/models.py             | 28 +++++++++++++++----
 tests/test_opengis.py                         | 27 +++++++++++++++++-
 3 files changed, 75 insertions(+), 6 deletions(-)
 create mode 100644 passerelle/apps/opengis/migrations/0009_auto_20200407_1544.py
passerelle/apps/opengis/migrations/0009_auto_20200407_1544.py
1
# -*- coding: utf-8 -*-
2
# Generated by Django 1.11.18 on 2020-04-07 13:44
3
from __future__ import unicode_literals
4

  
5
from django.db import migrations, models
6

  
7

  
8
class Migration(migrations.Migration):
9

  
10
    dependencies = [
11
        ('opengis', '0008_featurecache'),
12
    ]
13

  
14
    operations = [
15
        migrations.AddField(
16
            model_name='featurecache',
17
            name='text',
18
            field=models.CharField(default='', max_length=2048),
19
            preserve_default=False,
20
        ),
21
        migrations.AddField(
22
            model_name='query',
23
            name='index_properties',
24
            field=models.CharField(blank=True, help_text='Comma separated list such as property1,property2', max_length=1024, verbose_name='Properties for searching'),
25
        ),
26
    ]
passerelle/apps/opengis/models.py
34 34

  
35 35
from passerelle.base.models import BaseResource, BaseQuery
36 36
from passerelle.utils.api import endpoint
37
from passerelle.utils.conversion import num2deg
37
from passerelle.utils.conversion import num2deg, simplify
38 38
from passerelle.utils.jsonresponse import APIError
39 39

  
40 40

  
......
380 380
                  'bbox': {
381 381
                      'description': _('Only include results inside bounding box'),
382 382
                      'example_value': '-0.489,51.28,0.236,51.686'
383
                  },
384
                  'q': {
385
                      'description': _('Text search for specified properties'),
386
                      'example_value': 'library'
383 387
                  }
384 388
              },
385 389
              show=False)
386
    def query(self, request, query_slug, bbox=None):
390
    def query(self, request, query_slug, bbox=None, q=None):
387 391
        query = get_object_or_404(Query, resource=self, slug=query_slug)
388
        return query.q(request, bbox)
392
        return query.q(request, bbox, q)
389 393

  
390 394
    def export_json(self):
391 395
        d = super(OpenGIS, self).export_json()
......
435 439

  
436 440
    typename = models.CharField(_('Feature type'), max_length=256)
437 441
    filter_expression = models.TextField(_('XML filter'), blank=True)
442
    index_properties = models.CharField(
443
        verbose_name=_('Properties for searching'),
444
        help_text=_('Comma separated list such as property1,property2'),
445
        max_length=1024,
446
        blank=True)
438 447

  
439 448
    delete_view = 'opengis-query-delete'
440 449
    edit_view = 'opengis-query-edit'
......
448 457
        endpoint.parameters = resource_endpoint.parameters
449 458
        return endpoint
450 459

  
451
    def q(self, request, bbox):
460
    def q(self, request, bbox, q):
452 461
        features = self.features.all()
453 462
        if not features.exists():
454 463
            raise APIError('Data is not synchronized yet. Retry in a few minutes.')
......
460 469
                               'floating point numbers of the form lonmin,latmin,lonmax,latmax')
461 470
            features = features.filter(lon__gte=lonmin, lon__lte=lonmax, lat__gte=latmin,
462 471
                                       lat__lte=latmax)
472
        if q:
473
            features = features.filter(text__icontains=simplify(q))
463 474
        data = {
464 475
            'type': 'FeatureCollection',
465 476
            'name': self.typename
......
477 488
            except (KeyError, TypeError):
478 489
                self.resource.logger.warning('invalid coordinates in geometry: %s', geometry)
479 490
                continue
480
            features.append(FeatureCache(query=self, lat=lat, lon=lon, data=feature))
491
            text = ''
492
            if self.index_properties:
493
                properties = [x.strip() for x in self.index_properties.split(',') if x.strip()]
494
                values = [val for prop, val in feature.get('properties', {}).items()
495
                          if prop in properties]
496
                text = simplify(' '.join(values))
497
            features.append(FeatureCache(query=self, lat=lat, lon=lon, text=text, data=feature))
481 498
        with transaction.atomic():
482 499
            self.features.all().delete()
483 500
            FeatureCache.objects.bulk_create(features)
......
495 512
        verbose_name=_('Query'))
496 513
    lat = models.FloatField()
497 514
    lon = models.FloatField()
515
    text = models.CharField(max_length=2048)
498 516
    data = JSONField()
tests/test_opengis.py
641 641
    resp = app.get(connector.get_absolute_url())
642 642
    assert query.name in resp.text
643 643
    assert query.description in resp.text
644
    assert '/opengis/test/query/test_query/?bbox=' in resp.text
644
    assert '/opengis/test/query/test_query/' in resp.text
645
    assert 'bbox=-0.489,51.28,0.236,51.686' in resp.text
646
    assert 'q=library' in resp.text
647

  
648

  
649
@mock.patch('passerelle.utils.Request.get')
650
def test_opengis_query_text_search(mocked_get, app, connector, query):
651
    endpoint = utils.generic_endpoint_url('opengis', 'query/test_query/', slug=connector.slug)
652
    mocked_get.side_effect = geoserver_geolocated_responses
653
    query.update_cache()
654

  
655
    resp = app.get(endpoint + '?q=grenoble')
656
    assert len(resp.json['features']) == 0
657

  
658
    query.index_properties = 'nom_commune,nom_voie'
659
    query.save()
660
    query.update_cache()
661

  
662
    resp = app.get(endpoint + '?q=grenoble')
663
    assert len(resp.json['features']) == 4
664

  
665
    resp = app.get(endpoint + '?q=victor')
666
    assert len(resp.json['features']) == 2
667

  
668
    resp = app.get(endpoint + '?q=nomatch')
669
    assert len(resp.json['features']) == 0
645 670

  
646 671

  
647 672
def test_opengis_export_import(query):
648
-