api: add new filters to get overlapping subscriptions (#71579) #9

Merged
lguerin merged 1 commits from wip/71579-api-subscriptions-overlapping into main 2022-12-09 07:58:56 +01:00
2 changed files with 69 additions and 35 deletions

View File

@ -2251,8 +2251,8 @@ agendas_events_check_status = MultipleAgendasEventsCheckStatus.as_view()
class SubscriptionFilter(filters.FilterSet):
date_start = filters.DateFilter(lookup_expr='gte')
date_end = filters.DateFilter(lookup_expr='lt')
date_start = filters.DateFilter(method='do_nothing')
date_end = filters.DateFilter(method='do_nothing')
class Meta:
lguerin marked this conversation as resolved Outdated

Pas super convaincue par l'ajout de nouveaux filtres date_xx_overlaps.
Plutôt changer le comportement des filtres date_start et date_end existants ?

Pas super convaincue par l'ajout de nouveaux filtres date_xx_overlaps. Plutôt changer le comportement des filtres date_start et date_end existants ?

Si on n'a rien qui les utilise, oui pour changer le comportement.

Si on n'a rien qui les utilise, oui pour changer le comportement.

OK, ce n'est pas utilisé dans publik-famille, on va dire que ce n'est pas utilisé.

OK, ce n'est pas utilisé dans publik-famille, on va dire que ce n'est pas utilisé.
model = Subscription
@ -2262,6 +2262,28 @@ class SubscriptionFilter(filters.FilterSet):
'date_end',
]
def do_nothing(self, queryset, name, value):
return queryset
def filter_queryset(self, queryset):
queryset = super().filter_queryset(queryset)
overlaps = {k: self.form.cleaned_data[k] for k in ['date_start', 'date_end']}
if any(overlaps.values()):
if not all(overlaps.values()):
missing = [k for k, v in overlaps.items() if not v][0]
not_missing = [k for k, v in overlaps.items() if v][0]
raise ValidationError(
{missing: _('This filter is required when using "%s" filter.') % not_missing}
vdeniaud marked this conversation as resolved
Review

J'ai l'impression qu'il y a moyen de déléguer la validation de filtres dépendants l'un de l'autre à django-filters https://rpkilby.github.io/django-filter/guide/usage.html#process-filters-together-with-meta-groups.

J'ai l'impression qu'il y a moyen de déléguer la validation de filtres dépendants l'un de l'autre à django-filters https://rpkilby.github.io/django-filter/guide/usage.html#process-filters-together-with-meta-groups.
Review

Ca aurait été bien, mais je ne trouve pas les groups dans le code ni dans la doc officielle: https://django-filter.readthedocs.io/en/main/guide/usage.html#the-filter

Ca aurait été bien, mais je ne trouve pas les groups dans le code ni dans la doc officielle: https://django-filter.readthedocs.io/en/main/guide/usage.html#the-filter
Review

Zut j'ai pas vérifié que c'était la doc officielle, c'est la faute à Google !

Zut j'ai pas vérifié que c'était la doc officielle, c'est la faute à Google !
)
queryset = queryset.extra(
where=["(date_start, date_end) OVERLAPS (%s, %s)"],
params=[
self.form.cleaned_data['date_start'],
self.form.cleaned_data['date_end'],
],
)
return queryset
class SubscriptionsAPI(ListAPIView):
filter_backends = (filters.DjangoFilterBackend,)

View File

@ -75,7 +75,7 @@ def test_api_list_subscription_filter_user_external_id(app, user):
assert [d['id'] for d in resp.json['data']] == []
def test_api_list_subscription_filter_date_start(app, user):
def test_api_list_subscription_filter_date_overlaps(app, user):
agenda = Agenda.objects.create(label='Foo bar', kind='events')
subscription1 = Subscription.objects.create(
agenda=agenda,
@ -92,47 +92,59 @@ def test_api_list_subscription_filter_date_start(app, user):
app.authorization = ('Basic', ('john.doe', 'password'))
resp = app.get('/api/agenda/%s/subscription/' % agenda.slug, params={'date_start': '2021-09-01'})
assert [d['id'] for d in resp.json['data']] == [subscription1.pk, subscription2.pk]
resp = app.get('/api/agenda/%s/subscription/' % agenda.slug, params={'date_start': '2021-09-02'})
assert [d['id'] for d in resp.json['data']] == [subscription2.pk]
resp = app.get('/api/agenda/%s/subscription/' % agenda.slug, params={'date_start': '2022-09-02'})
assert [d['id'] for d in resp.json['data']] == []
resp = app.get(
'/api/agenda/%s/subscription/' % agenda.slug, params={'date_start': 'wrong-format'}, status=400
'/api/agenda/%s/subscription/' % agenda.slug,
params={'date_start': '2021-08-31', 'date_end': '2021-09-01'},
)
assert resp.json['err_class'] == 'invalid filters'
def test_api_list_subscription_filter_date_end(app, user):
agenda = Agenda.objects.create(label='Foo bar', kind='events')
subscription1 = Subscription.objects.create(
agenda=agenda,
user_external_id='xxx',
date_start=datetime.date(year=2021, month=9, day=1),
date_end=datetime.date(year=2021, month=10, day=1),
assert [d['id'] for d in resp.json['data']] == []
resp = app.get(
'/api/agenda/%s/subscription/' % agenda.slug,
params={'date_start': '2021-08-31', 'date_end': '2021-09-02'},
)
subscription2 = Subscription.objects.create(
agenda=agenda,
user_external_id='xxx',
date_start=datetime.date(year=2022, month=9, day=1),
date_end=datetime.date(year=2022, month=10, day=1),
)
app.authorization = ('Basic', ('john.doe', 'password'))
resp = app.get('/api/agenda/%s/subscription/' % agenda.slug, params={'date_end': '2022-10-02'})
assert [d['id'] for d in resp.json['data']] == [subscription1.pk, subscription2.pk]
resp = app.get('/api/agenda/%s/subscription/' % agenda.slug, params={'date_end': '2021-10-02'})
assert [d['id'] for d in resp.json['data']] == [subscription1.pk]
resp = app.get('/api/agenda/%s/subscription/' % agenda.slug, params={'date_end': '2021-10-01'})
resp = app.get(
'/api/agenda/%s/subscription/' % agenda.slug,
params={'date_start': '2021-09-02', 'date_end': '2021-09-30'},
)
assert [d['id'] for d in resp.json['data']] == [subscription1.pk]
resp = app.get(
'/api/agenda/%s/subscription/' % agenda.slug,
params={'date_start': '2021-09-30', 'date_end': '2021-10-01'},
)
assert [d['id'] for d in resp.json['data']] == [subscription1.pk]
resp = app.get(
'/api/agenda/%s/subscription/' % agenda.slug,
params={'date_start': '2021-10-01', 'date_end': '2021-10-02'},
)
assert [d['id'] for d in resp.json['data']] == []
resp = app.get(
'/api/agenda/%s/subscription/' % agenda.slug,
params={'date_start': '2021-09-15', 'date_end': '2022-09-15'},
)
assert [d['id'] for d in resp.json['data']] == [subscription1.pk, subscription2.pk]
resp = app.get(
'/api/agenda/%s/subscription/' % agenda.slug, params={'date_end': 'wrong-format'}, status=400
'/api/agenda/%s/subscription/' % agenda.slug,
params={'date_start': 'wrong-format', 'date_end': '2021-09-01'},
status=400,
)
assert resp.json['err_class'] == 'invalid filters'
resp = app.get(
'/api/agenda/%s/subscription/' % agenda.slug,
params={'date_start': '2021-08-31', 'date_end': 'wrong-format'},
status=400,
)
assert resp.json['err_class'] == 'invalid filters'
resp = app.get(
'/api/agenda/%s/subscription/' % agenda.slug, params={'date_start': '2021-08-31'}, status=400
)
assert resp.json['err_class'] == 'invalid filters'
assert resp.json['errors']['date_end'] == 'This filter is required when using "date_start" filter.'
resp = app.get(
'/api/agenda/%s/subscription/' % agenda.slug, params={'date_end': '2021-09-01'}, status=400
)
assert resp.json['err_class'] == 'invalid filters'
assert resp.json['errors']['date_start'] == 'This filter is required when using "date_end" filter.'
def test_api_create_subscription(app, user):