Projet

Général

Profil

0001-base_adresses-work-around-missing-departments-and-re.patch

Benjamin Dauvergne, 27 juin 2022 18:02

Télécharger (8,65 ko)

Voir les différences:

Subject: [PATCH] base_adresses: work around missing departments and regions in
 generic api.geo files (#66625)

 .../migrations/0030_auto_20220627_1511.py     |  18 +++
 passerelle/apps/base_adresse/models.py        | 126 +++++++++++++-----
 2 files changed, 113 insertions(+), 31 deletions(-)
 create mode 100644 passerelle/apps/base_adresse/migrations/0030_auto_20220627_1511.py
passerelle/apps/base_adresse/migrations/0030_auto_20220627_1511.py
1
# Generated by Django 2.2.28 on 2022-06-27 13:11
2

  
3
from django.db import migrations, models
4

  
5

  
6
class Migration(migrations.Migration):
7

  
8
    dependencies = [
9
        ('base_adresse', '0029_auto_20220624_0827'),
10
    ]
11

  
12
    operations = [
13
        migrations.AlterField(
14
            model_name='regionmodel',
15
            name='code',
16
            field=models.CharField(max_length=3, verbose_name='Region code'),
17
        ),
18
    ]
passerelle/apps/base_adresse/models.py
1 1
import datetime
2 2
import gzip
3
import itertools
3 4
import json
4 5
from io import StringIO
5 6
from urllib import parse as urlparse
......
502 503
                    error = 'invalid json, got: %s' % response.text
503 504
        if error:
504 505
            self.logger.error('failed to update api geo data for endpoint %s: %s', endpoint, error)
505
            return
506
            return {}
506 507
        if not result:
507 508
            raise Exception('api geo returns empty json')
508 509
        return result
509 510

  
510 511
    def update_api_geo_data(self):
511
        regions_json = self.get_api_geo_endpoint('regions')
512
        departments_json = self.get_api_geo_endpoint('departements')
512
        regions_json = {region['code']: region for region in self.get_api_geo_endpoint('regions')}
513
        departements_json = {
514
            departement['code']: departement for departement in self.get_api_geo_endpoint('departements')
515
        }
513 516
        cities_json = self.get_api_geo_endpoint('communes')
514
        if not (regions_json and departments_json and cities_json):
517

  
518
        if not regions_json or not departements_json or not cities_json:
515 519
            return
516
        start_update = timezone.now()
517 520

  
518
        for data in regions_json:
519
            defaults = {
520
                'name': data['nom'],
521
            }
522
            self.regionmodel_set.update_or_create(code=data['code'], defaults=defaults)
523
        self.regionmodel_set.filter(last_update__lt=start_update).delete()
521
        regions = {}
522
        departements = {}
524 523

  
525
        for data in departments_json:
526
            defaults = {
527
                'name': data['nom'],
528
                'region': self.regionmodel_set.get(code=data['codeRegion']),
524
        def get_region(code_region):
525
            if code_region not in regions:
526
                data = regions_json.get(code_region) or self.get_api_geo_endpoint(f'regions/{code_region}')
527
                if not data:
528
                    return None
529
                region, created = self.regionmodel_set.get_or_create(
530
                    code=data['code'], defaults={'name': data['nom']}
531
                )
532
                if not created and region.name != data['nom']:
533
                    region.name = data['nom']
534
                    region.save()
535
                regions[code_region] = region
536
            return regions[code_region]
537

  
538
        def get_departement(code_departement):
539
            if code_departement not in departements:
540
                data = departements_json.get(code_departement) or self.get_api_geo_endpoint(
541
                    f'departements/{code_departement}'
542
                )
543
                if not data:
544
                    return None
545
                region = get_region(data['codeRegion'])
546
                departement, created = self.departmentmodel_set.get_or_create(
547
                    code=data['code'], defaults={'name': data['nom'], 'region': region}
548
                )
549
                if not created and departement.name != data['nom'] or departement.region != region:
550
                    departement.name = data['nom']
551
                    departement.region = region
552
                    departement.save()
553
                departements[code_departement] = departement
554
            return departements[code_departement]
555

  
556
        for code_region in regions_json:
557
            get_region(code_region)
558

  
559
        for code_departement in departements_json:
560
            get_departement(code_departement)
561

  
562
        def grouper(it, size):
563
            '''Split iterator in equal size chunk of `size` elements.'''
564
            it = iter(it)
565
            return iter(lambda: tuple(itertools.islice(it, size)), ())
566

  
567
        city_pks = set()
568

  
569
        for batch_data in grouper(cities_json, 1000):
570
            batch_data = list(batch_data)
571
            cities = {
572
                (city.code, city.zipcode): city
573
                for city in self.citymodel_set.filter(code__in=[x['code'] for x in batch_data])
529 574
            }
530
            self.departmentmodel_set.update_or_create(code=data['code'], defaults=defaults)
531
        self.departmentmodel_set.filter(last_update__lt=start_update).delete()
532

  
533
        for data in cities_json:
534
            for zipcode in data['codesPostaux']:
535
                defaults = {
536
                    'name': data['nom'],
537
                    'population': data.get('population', 0),
538
                }
539
                if data.get('codeDepartement'):
540
                    defaults['department'] = self.departmentmodel_set.get(code=data['codeDepartement'])
541
                if data.get('codeRegion'):
542
                    defaults['region'] = self.regionmodel_set.get(code=data['codeRegion'])
543
                self.citymodel_set.update_or_create(code=data['code'], zipcode=zipcode, defaults=defaults)
544
        self.citymodel_set.filter(last_update__lt=start_update).delete()
575
            for data in batch_data:
576
                for zipcode in data['codesPostaux']:
577
                    defaults = {
578
                        'name': data['nom'],
579
                        'population': data.get('population', 0),
580
                    }
581
                    if data.get('codeDepartement'):
582
                        departement = get_departement(data['codeDepartement'])
583
                        if not departement:
584
                            continue
585
                        defaults['department'] = departement
586
                    if data.get('codeRegion'):
587
                        region = get_region(data['codeRegion'])
588
                        if not region:
589
                            continue
590
                        defaults['region'] = region
591
                    if (data['code'], zipcode) in cities:
592
                        city, created = cities[(data['code'], zipcode)], False
593
                    else:
594
                        city, created = self.citymodel_set.get_or_create(
595
                            code=data['code'], zipcode=zipcode, defaults=defaults
596
                        )
597
                    if not created and any(
598
                        getattr(city, key) != defaults.get(key)
599
                        for key in ['name', 'population', 'department', 'region']
600
                    ):
601
                        for key in ['name', 'population', 'department', 'region']:
602
                            setattr(city, key, defaults.get(key))
603
                        city.save()
604
                    city_pks.add(city.pk)
605

  
606
        self.regionmodel_set.exclude(code__in=regions.keys()).delete()
607
        self.departmentmodel_set.exclude(code__in=departements.keys()).delete()
608
        self.citymodel_set.exclude(pk__in=city_pks).delete()
545 609

  
546 610
    def clean_addresses_cache(self):
547 611
        old_addresses = self.addresscachemodel_set.filter(
......
597 661

  
598 662
    name = models.CharField(_('Region name'), max_length=150)
599 663
    unaccent_name = models.CharField(_('Region name ascii char'), max_length=150, null=True)
600
    code = models.CharField(_('Region code'), max_length=2)
664
    code = models.CharField(_('Region code'), max_length=3)
601 665
    last_update = models.DateTimeField(_('Last update'), null=True, auto_now=True)
602 666

  
603 667
    resource = models.ForeignKey(BaseAdresse, on_delete=models.CASCADE, verbose_name=_('BAN Connector'))
604
-