Projet

Général

Profil

0002-statistics-allow-filtering-by-users-OU-49670.patch

Valentin Deniaud, 25 février 2021 17:49

Télécharger (15,3 ko)

Voir les différences:

Subject: [PATCH 2/3] statistics: allow filtering by users OU (#49670)

 src/authentic2/api_views.py           | 29 ++++++++++----
 src/authentic2/apps/journal/models.py |  2 +-
 src/authentic2/journal_event_types.py | 16 +++++---
 tests/test_api.py                     | 55 +++++++++++++++++++++------
 tests/test_journal.py                 | 22 +++++++++--
 5 files changed, 95 insertions(+), 29 deletions(-)
src/authentic2/api_views.py
1125 1125

  
1126 1126
    time_interval = serializers.ChoiceField(choices=TIME_INTERVAL_CHOICES, default='month')
1127 1127
    service = ServiceOUField(child=serializers.SlugField(max_length=256), required=False)
1128
    ou = serializers.SlugField(required=False, allow_blank=False, max_length=256)
1128
    services_ou = serializers.SlugField(required=False, allow_blank=False, max_length=256)
1129
    users_ou = serializers.SlugField(required=False, allow_blank=False, max_length=256)
1130
    ou = serializers.SlugField(required=False, allow_blank=False, max_length=256)  # legacy
1129 1131
    start = serializers.DateTimeField(required=False, input_formats=['iso-8601', '%Y-%m-%d'])
1130 1132
    end = serializers.DateTimeField(required=False, input_formats=['iso-8601', '%Y-%m-%d'])
1131 1133

  
......
1171 1173
        for action in self.get_extra_actions():
1172 1174
            url = self.reverse_action(action.url_name)
1173 1175
            filters = common_filters.copy()
1174
            if 'ou' in action.filters:
1175
                filters.append({'id': 'ou', 'label': _('Organizational Unit'), 'options': ous})
1176
            if 'services_ou' in action.filters:
1177
                filters.append(
1178
                    {'id': 'services_ou', 'label': _('Services organizational unit'), 'options': ous}
1179
                )
1180
            if 'users_ou' in action.filters:
1181
                filters.append(
1182
                    {'id': 'users_ou', 'label': _('Users organizational unit'), 'options': ous}
1183
                )
1176 1184
            if 'service' in action.filters:
1177 1185
                filters.append({'id': 'service', 'label': _('Service'), 'options': services})
1178 1186
            data = {
......
1206 1214
        }
1207 1215

  
1208 1216
        allowed_filters = getattr(self, self.action).filters
1209
        service, ou = data.get('service'), data.get('ou')
1217
        service = data.get('service')
1218
        services_ou = data.get('services_ou') or data.get('ou')  # legacy 'ou' filter
1219
        users_ou = data.get('users_ou')
1220

  
1210 1221
        if service and 'service' in allowed_filters:
1211 1222
            service_slug, ou_slug = service
1212 1223
            kwargs['service'] = get_object_or_404(Service, slug=service_slug, ou__slug=ou_slug)
1213
        elif ou and 'ou' in allowed_filters:
1214
            kwargs['ou'] = get_object_or_404(get_ou_model(), slug=ou)
1224
        if services_ou and 'services_ou' in allowed_filters:
1225
            kwargs['services_ou'] = get_object_or_404(get_ou_model(), slug=services_ou)
1226
        if users_ou and 'users_ou' in allowed_filters:
1227
            kwargs['users_ou'] = get_object_or_404(get_ou_model(), slug=users_ou)
1215 1228

  
1216 1229
        return Response({
1217 1230
            'data': getattr(klass, method)(**kwargs),
1218 1231
            'err': 0,
1219 1232
        })
1220 1233

  
1221
    @stat(name=_('Login count by authentication type'), filters=('ou', 'service'))
1234
    @stat(name=_('Login count by authentication type'), filters=('services_ou', 'users_ou', 'service'))
1222 1235
    def login(self, request):
1223 1236
        return self.get_statistics(request, UserLogin, 'get_method_statistics')
1224 1237

  
......
1230 1243
    def service_ou_login(self, request):
1231 1244
        return self.get_statistics(request, UserLogin, 'get_service_ou_statistics')
1232 1245

  
1233
    @stat(name=_('Registration count by type'), filters=('ou', 'service'))
1246
    @stat(name=_('Registration count by type'), filters=('services_ou', 'users_ou', 'service'))
1234 1247
    def registration(self, request):
1235 1248
        return self.get_statistics(request, UserRegistration, 'get_method_statistics')
1236 1249

  
src/authentic2/apps/journal/models.py
143 143
            values.append(group_by_field)
144 144

  
145 145
        if which_references:
146
            qs = qs.which_references(which_references)
146
            qs = qs.filter(which_references)
147 147

  
148 148
        if group_by_references:
149 149
            values.append('reference_ids')
src/authentic2/journal_event_types.py
15 15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 16

  
17 17
from django.contrib.contenttypes.models import ContentType
18
from django.db.models import Q
18 19
from django.utils.translation import ugettext_lazy as _
19 20

  
20 21
from authentic2.custom_user.models import get_attributes_map
21
from authentic2.apps.journal.models import EventTypeDefinition, n_2_pairing_rev
22
from authentic2.apps.journal.models import EventTypeDefinition, n_2_pairing_rev, EventQuerySet
22 23
from authentic2.apps.journal.utils import form_to_old_new, Statistics
23 24
from authentic2.custom_user.models import User
24 25

  
......
53 54
        super().record(user=user, session=session, service=service, data={'how': how})
54 55

  
55 56
    @classmethod
56
    def get_method_statistics(cls, group_by_time, service=None, ou=None, start=None, end=None):
57
        if ou:
58
            service = Service.objects.filter(ou=ou)
57
    def get_method_statistics(cls, group_by_time, service=None, services_ou=None, users_ou=None, start=None, end=None):
58
        which_references = Q()
59
        if service:
60
            which_references &= EventQuerySet._which_references_query(service)
61
        if services_ou:
62
            which_references &= EventQuerySet._which_references_query(Service.objects.filter(ou=services_ou))
63
        if users_ou:
64
            which_references &= EventQuerySet._which_references_query(User.objects.filter(ou=users_ou))
59 65

  
60 66
        qs = cls.get_statistics(
61
            group_by_time=group_by_time, group_by_field='how', which_references=service, start=start, end=end
67
            group_by_time=group_by_time, group_by_field='how', which_references=which_references, start=start, end=end
62 68
        )
63 69
        stats = Statistics(qs, time_interval=group_by_time)
64 70

  
tests/test_api.py
2052 2052
                "default": "month",
2053 2053
            },
2054 2054
            {
2055
                'id': 'ou',
2056
                'label': 'Organizational Unit',
2055
                'id': 'services_ou',
2056
                'label': 'Services organizational unit',
2057
                'options': [{'id': 'default', 'label': 'Default organizational unit'}],
2058
            },
2059
            {
2060
                'id': 'users_ou',
2061
                'label': 'Users organizational unit',
2057 2062
                'options': [{'id': 'default', 'label': 'Default organizational unit'}],
2058 2063
            },
2059 2064
            {'id': 'service', 'label': 'Service', 'options': []},
......
2081 2086

  
2082 2087
    service = Service.objects.create(name='Service1', slug='service1', ou=get_default_ou())
2083 2088
    service = Service.objects.create(name='Service2', slug='service2', ou=get_default_ou())
2084
    login_stats['filters'][2]['options'].append({'id': 'service1 default', 'label': 'Service1'})
2085
    login_stats['filters'][2]['options'].append({'id': 'service2 default', 'label': 'Service2'})
2089
    login_stats['filters'][3]['options'].append({'id': 'service1 default', 'label': 'Service1'})
2090
    login_stats['filters'][3]['options'].append({'id': 'service2 default', 'label': 'Service2'})
2086 2091

  
2087 2092
    resp = app.get('/api/statistics/', headers=headers)
2088 2093
    assert login_stats in resp.json['data']
......
2093 2098
    'event_type_name,event_name', [('user.login', 'login'), ('user.registration', 'registration')]
2094 2099
)
2095 2100
def test_api_statistics(app, admin, freezer, event_type_name, event_name):
2101
    OU = get_ou_model()
2096 2102
    headers = basic_authorization_header(admin)
2097 2103

  
2098 2104
    resp = app.get('/api/statistics/login/?time_interval=month', headers=headers)
2099 2105
    assert resp.json == {"data": {"series": [], "x_labels": []}, "err": 0}
2100 2106

  
2101
    user = User.objects.create(username='john.doe', email='john.doe@example.com')
2102
    portal = Service.objects.create(name='portal', slug='portal', ou=get_default_ou())
2107
    user = User.objects.create(username='john.doe', email='john.doe@example.com', ou=get_default_ou())
2108
    ou = OU.objects.create(name='Second OU', slug='second')
2109
    portal = Service.objects.create(name='portal', slug='portal', ou=ou)
2103 2110
    agendas = Service.objects.create(name='agendas', slug='agendas', ou=get_default_ou())
2104 2111

  
2105 2112
    method = {'how': 'password-on-https'}
......
2110 2117

  
2111 2118
    freezer.move_to('2020-02-03 12:00')
2112 2119
    event = Event.objects.create(type=event_type, references=[portal], data=method)
2113
    event = Event.objects.create(type=event_type, references=[agendas], data=method)
2120
    event = Event.objects.create(type=event_type, references=[agendas, user], data=method)
2114 2121

  
2115 2122
    freezer.move_to('2020-03-04 13:00')
2116 2123
    event = Event.objects.create(type=event_type, references=[agendas], data=method)
......
2131 2138
    data['series'].sort(key=lambda x: x['label'])
2132 2139
    assert month_data == data
2133 2140

  
2134
    resp = app.get('/api/statistics/%s/?time_interval=month&ou=default' % event_name, headers=headers)
2141
    resp = app.get(
2142
        '/api/statistics/%s/?time_interval=month&services_ou=default' % event_name, headers=headers
2143
    )
2135 2144
    data = resp.json['data']
2136 2145
    data['series'].sort(key=lambda x: x['label'])
2137 2146
    assert data == {
2138 2147
        'x_labels': ['2020-02', '2020-03'],
2139
        'series': [{'label': 'FranceConnect', 'data': [None, 1]}, {'label': 'password', 'data': [2, 1]}],
2148
        'series': [{'label': 'password', 'data': [1, 1]}],
2140 2149
    }
2141 2150

  
2151
    # legacy way to filter by service OU
2152
    services_ou_data = data
2153
    resp = app.get('/api/statistics/%s/?time_interval=month&ou=default' % event_name, headers=headers)
2154
    data = resp.json['data']
2155
    data['series'].sort(key=lambda x: x['label'])
2156
    assert services_ou_data == data
2157

  
2158
    resp = app.get(
2159
        '/api/statistics/%s/?time_interval=month&users_ou=default&service=agendas default' % event_name, headers=headers
2160
    )
2161
    data = resp.json['data']
2162
    assert data == {
2163
        'x_labels': ['2020-02'],
2164
        'series': [{'label': 'password', 'data': [1]}],
2165
    }
2166

  
2167
    resp = app.get('/api/statistics/%s/?time_interval=month&users_ou=default' % event_name, headers=headers)
2168
    data = resp.json['data']
2169
    assert data == {'x_labels': ['2020-02'], 'series': [{'label': 'password', 'data': [1]}]}
2170

  
2142 2171
    resp = app.get(
2143 2172
        '/api/statistics/%s/?time_interval=month&service=agendas default' % event_name, headers=headers
2144 2173
    )
......
2168 2197
    assert data == {'x_labels': ['2020-02'], 'series': [{'label': 'password', 'data': [2]}]}
2169 2198

  
2170 2199
    resp = app.get(
2171
        '/api/statistics/%s/?time_interval=year&service=portal default' % event_name, headers=headers
2200
        '/api/statistics/%s/?time_interval=year&service=portal second' % event_name, headers=headers
2172 2201
    )
2173 2202
    data = resp.json['data']
2174 2203
    data['series'].sort(key=lambda x: x['label'])
......
2187 2216

  
2188 2217
    resp = app.get('/api/statistics/service_ou_%s/?time_interval=month' % event_name, headers=headers)
2189 2218
    data = resp.json['data']
2219
    data['series'].sort(key=lambda x: x['label'])
2190 2220
    assert data == {
2191 2221
        'x_labels': ['2020-02', '2020-03'],
2192
        'series': [{'label': 'Default organizational unit', 'data': [2, 2]}],
2222
        'series': [
2223
            {'label': 'Default organizational unit', 'data': [1, 1]},
2224
            {'label': 'Second OU', 'data': [1, 1]},
2225
        ],
2193 2226
    }
2194 2227

  
2195 2228

  
tests/test_journal.py
450 450

  
451 451
@pytest.mark.parametrize('event_type_name', ['user.login', 'user.registration'])
452 452
def test_statistics(db, event_type_name, freezer):
453
    user = User.objects.create(username='john.doe', email='john.doe@example.com')
454
    user2 = User.objects.create(username='jane.doe', email='jane.doe@example.com')
453
    user = User.objects.create(username='john.doe', email='john.doe@example.com', ou=get_default_ou())
455 454
    ou = OU.objects.create(name='Second OU')
455
    user2 = User.objects.create(username='jane.doe', email='jane.doe@example.com', ou=ou)
456 456

  
457 457
    portal = Service.objects.create(name='portal', slug='portal', ou=ou)
458 458
    agendas = Service.objects.create(name='agendas', slug='agendas', ou=get_default_ou())
......
514 514
        ],
515 515
    }
516 516

  
517
    stats = event_type_definition.get_method_statistics('month', ou=get_default_ou())
517
    stats = event_type_definition.get_method_statistics('month', services_ou=get_default_ou())
518 518
    assert stats == {
519 519
        'x_labels': ['2020-03'],
520 520
        'series': [{'label': 'password', 'data': [2]},],
521 521
    }
522 522

  
523
    stats = event_type_definition.get_method_statistics('month', ou=ou)
523
    stats = event_type_definition.get_method_statistics('month', services_ou=ou)
524 524
    stats['series'].sort(key=lambda x: x['label'])
525 525
    assert stats == {
526 526
        'x_labels': ['2020-02', '2020-03'],
527 527
        'series': [{'label': 'FranceConnect', 'data': [2, None]}, {'label': 'password', 'data': [2, 1]}],
528 528
    }
529 529

  
530
    stats = event_type_definition.get_method_statistics('month', users_ou=ou)
531
    stats['series'].sort(key=lambda x: x['label'])
532
    assert stats == {
533
        'x_labels': ['2020-02'],
534
        'series': [{'label': 'FranceConnect', 'data': [1]}, {'label': 'password', 'data': [1]}],
535
    }
536

  
530 537
    stats = event_type_definition.get_method_statistics('month', service=portal)
531 538
    stats['series'].sort(key=lambda x: x['label'])
532 539
    assert stats == {
......
534 541
        'series': [{'label': 'FranceConnect', 'data': [2, None]}, {'label': 'password', 'data': [2, 1]}],
535 542
    }
536 543

  
544
    stats = event_type_definition.get_method_statistics('month', service=agendas, users_ou=get_default_ou())
545
    stats['series'].sort(key=lambda x: x['label'])
546
    assert stats == {
547
        'x_labels': ['2020-03'],
548
        'series': [{'label': 'password', 'data': [1]}],
549
    }
550

  
537 551
    stats = event_type_definition.get_method_statistics('year')
538 552
    stats['series'].sort(key=lambda x: x['label'])
539 553
    assert stats == {
540
-