api/ants: check existing appointments before creating/deleting (#83558)
gitea/ants-hub/pipeline/head This commit looks good
Details
gitea/ants-hub/pipeline/head This commit looks good
Details
This commit is contained in:
parent
dcb622a48e
commit
0d4155ed3d
|
@ -4,8 +4,9 @@ import logging
|
|||
import re
|
||||
|
||||
import requests
|
||||
from django.db import OperationalError
|
||||
from django.db.models import F, Q
|
||||
from django.db.transaction import atomic
|
||||
from django.db.transaction import atomic, set_rollback
|
||||
|
||||
from ants_hub.data.models import Config, RendezVous
|
||||
from ants_hub.timezone import localtime, now
|
||||
|
@ -93,6 +94,27 @@ class APIDoublon:
|
|||
raise ANTSError('response is not a dict', data)
|
||||
return data
|
||||
|
||||
def status(self, application_id):
|
||||
return self.statuses([application_id]).get(application_id, {})
|
||||
|
||||
def existing_rdv(self, rdv):
|
||||
identifiant_predemande = rdv.identifiant_predemande.upper()
|
||||
appointments = self.status(identifiant_predemande).get('appointments', [])
|
||||
|
||||
ref = [
|
||||
('meeting_point', rdv.lieu.nom),
|
||||
('appointment_date', localtime(rdv.date).strftime('%Y-%m-%dT%H:%M:%S')),
|
||||
]
|
||||
|
||||
matching_appointments = []
|
||||
for appointment in appointments:
|
||||
for key, value in ref:
|
||||
if appointment[key] != value:
|
||||
break
|
||||
else:
|
||||
matching_appointments.append(appointment)
|
||||
return matching_appointments
|
||||
|
||||
|
||||
def push_rdv(rdv):
|
||||
identifiant_predemande = rdv.identifiant_predemande.upper()
|
||||
|
@ -103,10 +125,39 @@ def push_rdv(rdv):
|
|||
return True
|
||||
|
||||
api_doublon = APIDoublon()
|
||||
if rdv.canceled is None:
|
||||
return api_doublon.create_rdv(rdv)
|
||||
|
||||
delete = False
|
||||
create = False
|
||||
|
||||
existing = api_doublon.existing_rdv(rdv)
|
||||
|
||||
if rdv.canceled:
|
||||
action = 'deleted'
|
||||
if existing:
|
||||
delete = True
|
||||
else:
|
||||
return api_doublon.delete_rdv(rdv)
|
||||
action = 'created'
|
||||
if len(existing) > 1:
|
||||
delete = True
|
||||
create = True
|
||||
elif existing:
|
||||
if existing[0]['management_url'] != rdv.gestion_url:
|
||||
# L'URL de gestion a changé, on supprime le RdV existant
|
||||
delete = True
|
||||
create = True
|
||||
else:
|
||||
return True
|
||||
else:
|
||||
create = True
|
||||
|
||||
success = True
|
||||
if delete:
|
||||
success = success and api_doublon.delete_rdv(rdv)
|
||||
|
||||
if create:
|
||||
success = success and api_doublon.create_rdv(rdv)
|
||||
|
||||
return success and (delete or create) and action
|
||||
|
||||
|
||||
def get_rdvs_to_upload():
|
||||
|
@ -120,17 +171,23 @@ def upload_rdvs():
|
|||
rdvs = get_rdvs_to_upload()
|
||||
start = now()
|
||||
for rdv in rdvs.distinct():
|
||||
with atomic():
|
||||
try:
|
||||
rdv = rdvs.select_for_update(of=('self',)).get(pk=rdv.pk)
|
||||
except RendezVous.DoesNotExist:
|
||||
continue
|
||||
try:
|
||||
if push_rdv(rdv):
|
||||
logger.info('pushed rdv(%s) of lieu %s', rdv, rdv.lieu)
|
||||
try:
|
||||
with atomic():
|
||||
try:
|
||||
rdv = rdvs.select_for_update(of=('self',)).get(pk=rdv.pk)
|
||||
except RendezVous.DoesNotExist:
|
||||
continue
|
||||
try:
|
||||
RendezVous.objects.filter(pk=rdv.pk).update(last_upload=start)
|
||||
except ANTSError as e:
|
||||
logger.warning('unable to push rdv(%s) of lieu %s: %r', rdv, rdv.lieu, e)
|
||||
result = push_rdv(rdv)
|
||||
if result:
|
||||
logger.info(f'{result} rdv(%s) of lieu %s', rdv, rdv.lieu)
|
||||
continue
|
||||
except ANTSError as e:
|
||||
logger.warning('unable to push rdv(%s) of lieu %s: %r', rdv, rdv.lieu, e)
|
||||
set_rollback(True)
|
||||
except OperationalError:
|
||||
pass
|
||||
|
||||
|
||||
def get_status_of_predemandes(identifiant_predemandes: list):
|
||||
|
|
|
@ -368,6 +368,25 @@ class TestAPIV2Push:
|
|||
assert RendezVous.objects.filter(canceled__isnull=True).count() == 1
|
||||
assert RendezVous.objects.filter(last_upload__isnull=True).count() == 1
|
||||
|
||||
responses.add(
|
||||
responses.GET,
|
||||
'https://api-coordination.rendezvouspasseport.ants.gouv.fr/api/status',
|
||||
json={
|
||||
'ABCD123456': {
|
||||
'appointments': [
|
||||
{
|
||||
'meeting_point': 'Mairie de pouetpouet',
|
||||
'appointment_date': '2023-04-03T12:15:00',
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
status=200,
|
||||
match=[
|
||||
responses.matchers.header_matcher({'x-rdv-opt-auth-token': 'abcd'}),
|
||||
],
|
||||
)
|
||||
|
||||
post_response = responses.add(
|
||||
responses.POST,
|
||||
'https://api-coordination.rendezvouspasseport.ants.gouv.fr/api/appointments',
|
||||
|
@ -418,6 +437,25 @@ class TestAPIV2Push:
|
|||
|
||||
RendezVous.objects.update(canceled=now(), last_update=now())
|
||||
|
||||
responses.replace(
|
||||
responses.GET,
|
||||
'https://api-coordination.rendezvouspasseport.ants.gouv.fr/api/status',
|
||||
json={
|
||||
'ABCD123456': {
|
||||
'appointments': [
|
||||
{
|
||||
'meeting_point': 'Mairie',
|
||||
'appointment_date': '2023-04-03T12:15:00',
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
status=200,
|
||||
match=[
|
||||
responses.matchers.header_matcher({'x-rdv-opt-auth-token': 'abcd'}),
|
||||
],
|
||||
)
|
||||
|
||||
call_command('upload-rdvs')
|
||||
|
||||
assert post_response.call_count == 1
|
||||
|
@ -473,6 +511,15 @@ class TestAPIV2Push:
|
|||
],
|
||||
)
|
||||
def test_post_error(self, response_kwargs, db, freezer):
|
||||
responses.add(
|
||||
responses.GET,
|
||||
'https://api-coordination.rendezvouspasseport.ants.gouv.fr/api/status',
|
||||
json={},
|
||||
status=200,
|
||||
match=[
|
||||
responses.matchers.header_matcher({'x-rdv-opt-auth-token': 'abcd'}),
|
||||
],
|
||||
)
|
||||
post_response = responses.add(
|
||||
responses.POST,
|
||||
'https://api-coordination.rendezvouspasseport.ants.gouv.fr/api/appointments',
|
||||
|
@ -521,6 +568,24 @@ class TestAPIV2Push:
|
|||
],
|
||||
)
|
||||
def test_delete_error(self, response_kwargs, db, freezer):
|
||||
responses.add(
|
||||
responses.GET,
|
||||
'https://api-coordination.rendezvouspasseport.ants.gouv.fr/api/status',
|
||||
json={
|
||||
'ABCD123456': {
|
||||
'appointments': [
|
||||
{
|
||||
'meeting_point': 'Mairie',
|
||||
'appointment_date': '2023-04-03T12:15:00',
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
status=200,
|
||||
match=[
|
||||
responses.matchers.header_matcher({'x-rdv-opt-auth-token': 'abcd'}),
|
||||
],
|
||||
)
|
||||
post_response = responses.add(
|
||||
responses.DELETE,
|
||||
'https://api-coordination.rendezvouspasseport.ants.gouv.fr/api/appointments',
|
||||
|
@ -537,6 +602,101 @@ class TestAPIV2Push:
|
|||
assert RendezVous.objects.count() == 1
|
||||
assert RendezVous.objects.filter(last_upload__isnull=True).count() == 1
|
||||
|
||||
@responses.activate
|
||||
def test_delete_duplicates(self, db, freezer):
|
||||
assert RendezVous.objects.count() == 1
|
||||
assert RendezVous.objects.filter(canceled__isnull=True).count() == 1
|
||||
assert RendezVous.objects.filter(last_upload__isnull=True).count() == 1
|
||||
|
||||
responses.add(
|
||||
responses.GET,
|
||||
'https://api-coordination.rendezvouspasseport.ants.gouv.fr/api/status',
|
||||
json={
|
||||
'ABCD123456': {
|
||||
'appointments': [
|
||||
{
|
||||
'meeting_point': 'Mairie',
|
||||
'appointment_date': '2023-04-03T12:15:00',
|
||||
},
|
||||
{
|
||||
'meeting_point': 'Mairie',
|
||||
'appointment_date': '2023-04-03T12:15:00',
|
||||
},
|
||||
]
|
||||
}
|
||||
},
|
||||
status=200,
|
||||
match=[
|
||||
responses.matchers.header_matcher({'x-rdv-opt-auth-token': 'abcd'}),
|
||||
],
|
||||
)
|
||||
|
||||
post_response = responses.add(
|
||||
responses.POST,
|
||||
'https://api-coordination.rendezvouspasseport.ants.gouv.fr/api/appointments',
|
||||
json={'success': True},
|
||||
status=200,
|
||||
match=[responses.matchers.header_matcher({'x-rdv-opt-auth-token': 'abcd'})],
|
||||
)
|
||||
delete_response = responses.add(
|
||||
responses.DELETE,
|
||||
'https://api-coordination.rendezvouspasseport.ants.gouv.fr/api/appointments',
|
||||
json={'rowcount': 1},
|
||||
status=200,
|
||||
match=[responses.matchers.header_matcher({'x-rdv-opt-auth-token': 'abcd'})],
|
||||
)
|
||||
|
||||
call_command('upload-rdvs')
|
||||
|
||||
assert post_response.call_count == 1
|
||||
assert delete_response.call_count == 1
|
||||
|
||||
@responses.activate
|
||||
def test_delete_management_url_changed(self, db, freezer):
|
||||
assert RendezVous.objects.count() == 1
|
||||
assert RendezVous.objects.filter(canceled__isnull=True).count() == 1
|
||||
assert RendezVous.objects.filter(last_upload__isnull=True).count() == 1
|
||||
|
||||
responses.add(
|
||||
responses.GET,
|
||||
'https://api-coordination.rendezvouspasseport.ants.gouv.fr/api/status',
|
||||
json={
|
||||
'ABCD123456': {
|
||||
'appointments': [
|
||||
{
|
||||
'meeting_point': 'Mairie',
|
||||
'appointment_date': '2023-04-03T12:15:00',
|
||||
'management_url': 'xyz',
|
||||
},
|
||||
]
|
||||
}
|
||||
},
|
||||
status=200,
|
||||
match=[
|
||||
responses.matchers.header_matcher({'x-rdv-opt-auth-token': 'abcd'}),
|
||||
],
|
||||
)
|
||||
|
||||
post_response = responses.add(
|
||||
responses.POST,
|
||||
'https://api-coordination.rendezvouspasseport.ants.gouv.fr/api/appointments',
|
||||
json={'success': True},
|
||||
status=200,
|
||||
match=[responses.matchers.header_matcher({'x-rdv-opt-auth-token': 'abcd'})],
|
||||
)
|
||||
delete_response = responses.add(
|
||||
responses.DELETE,
|
||||
'https://api-coordination.rendezvouspasseport.ants.gouv.fr/api/appointments',
|
||||
json={'rowcount': 1},
|
||||
status=200,
|
||||
match=[responses.matchers.header_matcher({'x-rdv-opt-auth-token': 'abcd'})],
|
||||
)
|
||||
|
||||
call_command('upload-rdvs')
|
||||
|
||||
assert post_response.call_count == 1
|
||||
assert delete_response.call_count == 1
|
||||
|
||||
|
||||
class TestGetStatusOfPredemandes:
|
||||
@pytest.fixture(autouse=True)
|
||||
|
|
Loading…
Reference in New Issue