statistics: lookup service also with their subclass reference_id (#68390)

This commit is contained in:
Benjamin Dauvergne 2022-09-12 22:36:54 +02:00
parent 1b51c4082f
commit 21249f84bb
4 changed files with 34 additions and 6 deletions

View File

@ -1633,7 +1633,12 @@ class StatisticsAPI(ViewSet):
if service and 'service' in allowed_filters:
service_slug, ou_slug = service
kwargs['service'] = get_object_or_404(Service, slug=service_slug, ou__slug=ou_slug)
# look for the Service child and parent instances, see #68390 and #64853
subclass_service_instance = get_object_or_404(
Service.objects.select_subclasses(), slug=service_slug, ou__slug=ou_slug
)
service_instance = Service(pk=subclass_service_instance.pk)
kwargs['service'] = [subclass_service_instance, service_instance]
elif services_ou and 'services_ou' in allowed_filters:
kwargs['services_ou'] = get_object_or_404(OrganizationalUnit, slug=services_ou)

View File

@ -14,7 +14,9 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import functools
import logging
import operator
import re
from collections import defaultdict
from contextlib import contextmanager
@ -196,7 +198,12 @@ class EventType(models.Model):
class EventQuerySet(QuerySet):
@classmethod
def _which_references_query(cls, instance_or_model_class_or_queryset):
if isinstance(instance_or_model_class_or_queryset, type) and issubclass(
if isinstance(instance_or_model_class_or_queryset, list):
return functools.reduce(
operator.or_,
(cls._which_references_query(ref) for ref in instance_or_model_class_or_queryset),
)
elif isinstance(instance_or_model_class_or_queryset, type) and issubclass(
instance_or_model_class_or_queryset, models.Model
):
ct = ContentType.objects.get_for_model(instance_or_model_class_or_queryset)
@ -221,6 +228,8 @@ class EventQuerySet(QuerySet):
return q
def which_references(self, instance_or_queryset):
if instance_or_queryset == []:
return self.none()
return self.filter(self._which_references_query(instance_or_queryset))
def from_cursor(self, cursor):

View File

@ -56,13 +56,21 @@ class EventTypeWithHow(EventTypeWithService):
def get_method_statistics(
cls, group_by_time, service=None, services_ou=None, users_ou=None, start=None, end=None
):
which_references = None
if services_ou and not service:
service = Service.objects.filter(ou=services_ou)
services = Service.objects.filter(ou=services_ou)
if not services:
which_references = []
else:
# look for the Service child and parent instances, see #68390 and #64853
which_references = [services, list(services.select_subclasses())]
elif service:
which_references = service
qs = cls.get_statistics(
group_by_time=group_by_time,
group_by_field='how',
which_references=service,
which_references=which_references,
users_ou=users_ou,
start=start,
end=end,

View File

@ -39,6 +39,7 @@ from authentic2.apps.journal.models import Event, EventType
from authentic2.custom_user.models import Profile, ProfileType
from authentic2.models import APIClient, Attribute, AttributeValue, AuthorizedRole, PasswordReset, Service
from authentic2.utils.misc import good_next_url
from authentic2_idp_cas.models import Service as CASService
from django_rbac.models import SEARCH_OP
from ..utils import assert_event, basic_authorization_header, get_link_from_mail, login
@ -2631,7 +2632,10 @@ def test_api_statistics(app, admin, freezer, event_type_name, event_name):
user = User.objects.create(username='john.doe', email='john.doe@example.com', ou=get_default_ou())
ou = OU.objects.create(name='Second OU', slug='second')
portal = Service.objects.create(name='portal', slug='portal', ou=ou)
agendas = Service.objects.create(name='agendas', slug='agendas', ou=get_default_ou())
agendas = CASService.objects.create(
name='agendas', slug='agendas', ou=get_default_ou(), urls='https://agenda.example.net'
)
agenda_service = Service.objects.get(name='agendas')
method = {'how': 'password-on-https'}
method2 = {'how': 'france-connect'}
@ -2645,7 +2649,9 @@ def test_api_statistics(app, admin, freezer, event_type_name, event_name):
)
freezer.move_to('2020-03-04 13:00')
Event.objects.create(type=event_type, references=[agendas], data=dict(method, service_name=str(agendas)))
Event.objects.create(
type=event_type, references=[agenda_service], data=dict(method, service_name=str(agendas))
)
Event.objects.create(type=event_type, references=[portal], data=dict(method2, service_name=str(portal)))
resp = app.get('/api/statistics/%s/?time_interval=month' % event_name, headers=headers)