Projet

Général

Profil

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

Valentin Deniaud, 15 avril 2020 11:16

Télécharger (7,2 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 +++++++++++++----
 passerelle/settings.py                        |  1 +
 tests/test_opengis.py                         | 30 ++++++++++++++++++-
 4 files changed, 79 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

  
......
397 397
                  'bbox': {
398 398
                      'description': _('Only include results inside bounding box'),
399 399
                      'example_value': '-0.489,51.28,0.236,51.686'
400
                  },
401
                  'q': {
402
                      'description': _('Text search for specified properties'),
403
                      'example_value': 'library'
400 404
                  }
401 405
              },
402 406
              show=False)
403
    def query(self, request, query_slug, bbox=None):
407
    def query(self, request, query_slug, bbox=None, q=None):
404 408
        query = get_object_or_404(Query, resource=self, slug=query_slug)
405
        return query.q(request, bbox)
409
        return query.q(request, bbox, q)
406 410

  
407 411
    def export_json(self):
408 412
        d = super(OpenGIS, self).export_json()
......
452 456

  
453 457
    typename = models.CharField(_('Feature type'), max_length=256)
454 458
    filter_expression = models.TextField(_('XML filter'), blank=True)
459
    index_properties = models.CharField(
460
        verbose_name=_('Properties for searching'),
461
        help_text=_('Comma separated list such as property1,property2'),
462
        max_length=1024,
463
        blank=True)
455 464

  
456 465
    delete_view = 'opengis-query-delete'
457 466
    edit_view = 'opengis-query-edit'
......
465 474
        endpoint.parameters = resource_endpoint.parameters
466 475
        return endpoint
467 476

  
468
    def q(self, request, bbox):
477
    def q(self, request, bbox, q):
469 478
        features = self.features.all()
470 479
        if not features.exists():
471 480
            raise APIError('Data is not synchronized yet. Retry in a few minutes.')
......
477 486
                               'floating point numbers of the form lonmin,latmin,lonmax,latmax')
478 487
            features = features.filter(lon__gte=lonmin, lon__lte=lonmax, lat__gte=latmin,
479 488
                                       lat__lte=latmax)
489
        if q:
490
            features = features.filter(text__search=simplify(q))
480 491
        data = {
481 492
            'type': 'FeatureCollection',
482 493
            'name': self.typename
......
494 505
            except (KeyError, TypeError):
495 506
                self.resource.logger.warning('invalid coordinates in geometry: %s', geometry)
496 507
                continue
497
            features.append(FeatureCache(query=self, lat=lat, lon=lon, data=feature))
508
            text = ''
509
            if self.index_properties:
510
                properties = [x.strip() for x in self.index_properties.split(',') if x.strip()]
511
                values = [val for prop, val in feature.get('properties', {}).items()
512
                          if prop in properties]
513
                text = simplify(' '.join(values))
514
            features.append(FeatureCache(query=self, lat=lat, lon=lon, text=text, data=feature))
498 515
        with transaction.atomic():
499 516
            self.features.all().delete()
500 517
            FeatureCache.objects.bulk_create(features)
......
512 529
        verbose_name=_('Query'))
513 530
    lat = models.FloatField()
514 531
    lon = models.FloatField()
532
    text = models.CharField(max_length=2048)
515 533
    data = JSONField()
passerelle/settings.py
115 115
    'django.contrib.messages',
116 116
    'django.contrib.staticfiles',
117 117
    'django.contrib.admin',
118
    'django.contrib.postgres',
118 119
    # base app
119 120
    'passerelle.base',
120 121
    # connectors
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=hugo victor')
669
    assert len(resp.json['features']) == 2
670

  
671
    resp = app.get(endpoint + '?q=nomatch')
672
    assert len(resp.json['features']) == 0
645 673

  
646 674

  
647 675
def test_opengis_export_import(query):
648
-