• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OCHA-DAP / hdx-ckan / #4675

14 Nov 2023 11:17AM UTC coverage: 70.161% (-0.07%) from 70.233%
#4675

push

coveralls-python

web-flow
Merge pull request #6130 from OCHA-DAP/dev

dev into prod

3 of 23 new or added lines in 3 files covered. (13.04%)

1 existing line in 1 file now uncovered.

11312 of 16123 relevant lines covered (70.16%)

0.7 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

59.94
/ckanext-hdx_theme/ckanext/hdx_theme/helpers/actions.py
1
import logging
1✔
2
import json
1✔
3

4
import sqlalchemy
1✔
5

6
import ckan.logic as logic
1✔
7
import ckan.plugins.toolkit as tk
1✔
8
import ckan.lib.dictization.model_dictize as model_dictize
1✔
9
import ckan.lib.search as search
1✔
10
import ckan.authz as new_authz
1✔
11
import ckan.model as model
1✔
12
import ckan.logic.action.get as ckan_get
1✔
13

14
import ckanext.hdx_package.helpers.caching as caching
1✔
15
import ckanext.hdx_theme.helpers.counting_actions as counting
1✔
16
import ckanext.hdx_theme.util.mail as hdx_mail
1✔
17
import ckanext.hdx_theme.hxl.transformers.transformers as transformers
1✔
18
import ckan.authz as authz
1✔
19

20
from ckanext.hdx_theme.helpers.hdx_stats import HDXStatsHelper
1✔
21
from ckanext.hdx_theme.hxl.proxy import do_hxl_transformation, transform_response_to_dict_list
1✔
22

23
from ckanext.hdx_theme.util.analytics_stats import HDXStats, HDXStatsAnalyticsSender
1✔
24

25
_check_access = tk.check_access
1✔
26
_get_or_bust = tk.get_or_bust
1✔
27
config = tk.config
1✔
28
g = tk.g
1✔
29
_ = tk._
1✔
30
NotFound = logic.NotFound
1✔
31

32
log = logging.getLogger(__name__)
1✔
33

34

35
def member_list(context, data_dict=None):
1✔
36
    '''Return the members of a group.
37

38
    Modified copy of the original ckan member_list action to also return
39
    the non-translated capacity (role)
40

41
    :rtype: list of (id, type, translated capacity, capacity ) tuples
42

43
    '''
44
    model = context['model']
1✔
45

46
    group = model.Group.get(_get_or_bust(data_dict, 'id'))
1✔
47
    if not group:
1✔
48
        raise logic.NotFound
1✔
49
    # user = context.get('user')
50
    if group.state == 'deleted' and (not g.userobj or not g.userobj.sysadmin):
1✔
51
        raise logic.NotFound
×
52

53
    obj_type = data_dict.get('object_type', None)
1✔
54
    capacity = data_dict.get('capacity', None)
1✔
55
    show_user_info = data_dict.get('user_info', False)
1✔
56
    show_sysadmin_info = data_dict.get('sysadmin_info', False)
1✔
57
    q_term = data_dict.get('q', None)
1✔
58

59
    # User must be able to update the group to remove a member from it
60
    _check_access('group_show', context, data_dict)
1✔
61

62
    q = model.Session.query(model.Member, model.User). \
1✔
63
        filter(model.Member.table_id == model.User.id). \
64
        filter(model.Member.group_id == group.id). \
65
        filter(model.Member.state == "active")
66

67
    if q_term and q_term != '':
1✔
68
        q = q.filter(sqlalchemy.or_(
1✔
69
            model.User.fullname.ilike('%' + q_term + '%'),
70
            model.User.name.ilike('%' + q_term + '%')
71
        )
72
        )
73

74
    if obj_type:
1✔
75
        q = q.filter(model.Member.table_name == obj_type)
1✔
76
    if capacity:
1✔
77
        q = q.filter(model.Member.capacity == capacity)
1✔
78

79
    trans = new_authz.roles_trans()
1✔
80

81
    def translated_capacity(capacity):
1✔
82
        try:
1✔
83
            return trans[capacity]
1✔
84
        except KeyError:
1✔
85
            return capacity
1✔
86

87
    if show_user_info:
1✔
88
        if show_sysadmin_info:
1✔
89
            return [(m.table_id, m.table_name, translated_capacity(m.capacity), m.capacity,
1✔
90
                     u.fullname if u.fullname else u.name, u.sysadmin)
91
                    for m, u in q.all()]
92
        else:
93
            return [(m.table_id, m.table_name, translated_capacity(m.capacity), m.capacity,
1✔
94
                     u.fullname if u.fullname else u.name)
95
                    for m, u in q.all()]
96
    else:
97
        return [(m.table_id, m.table_name, translated_capacity(m.capacity), m.capacity)
1✔
98
                for m, u in q.all()]
99

100
@logic.side_effect_free
1✔
101
def cached_group_list(context, data_dict):
1✔
102
    # to make things simpler for caching there's no argument passed
103
    groups = caching.cached_group_list()
1✔
104
    return groups
1✔
105

106

107
def invalidate_cache_for_groups(context, data_dict):
1✔
108
    _check_access('invalidate_cache_for_groups', context, data_dict)
1✔
109
    caching.invalidate_group_caches()
1✔
110

111

112
@logic.side_effect_free
1✔
113
def cached_organization_list(context, data_dict):
1✔
114
    orgs = caching.cached_organization_list()
1✔
115

116
    _refresh_pkg_count_on_org_list(orgs)
1✔
117

118
    return orgs
1✔
119

120

121
def _refresh_pkg_count_on_org_list(orgs):
1✔
122
    query_params = {
1✔
123
        'start': 0,
124
        'rows': 1,
125
        'fq': 'private: false',
126
        'fl': 'id name',
127
        'facet': 'true',
128
        'facet.pivot': ['organization,archived'],
129
        'facet.limit': 2000,
130
    }
131
    # search_result = tk.get_action('package_search')({}, query_params)
132
    query = search.query_for(model.Package)
1✔
133
    query.run(query_params)
1✔
134
    org_name_to_pkg_count = query.raw_response.get('facet_counts', {}).get('facet_pivot', {}).get(
1✔
135
        'organization,archived', {})
136
    org_name_to_pkg_count_dict = {}
1✔
137
    for org in org_name_to_pkg_count:
1✔
138
        org_name_to_pkg_count_dict[org.get('value')] = org
1✔
139

140
    try:
1✔
141
        for org in orgs:
1✔
142
            _org = org_name_to_pkg_count_dict.get(org['name'])
1✔
143
            org['regular_package_count'] = 0
1✔
144
            org['archived_package_count'] = 0
1✔
145
            if _org and 'pivot' in _org:
1✔
146
                for item in _org.get('pivot'):
×
147
                    if item.get('field') == u'archived' and item.get('value') == 'false':
×
148
                        org['regular_package_count'] = item.get('count', 0)
×
149
                    elif item.get('field') == u'archived' and item.get('value') == 'true':
×
150
                        org['archived_package_count'] = item.get('count', 0)
×
151

152
    except Exception as ex:
×
153
        log.info(ex)
×
154

155

156
def invalidate_cache_for_organizations(context, data_dict):
1✔
157
    _check_access('invalidate_cache_for_organizations', context, data_dict)
×
158
    caching.invalidate_cached_organization_list()
×
159

160

161
def invalidate_cached_resource_id_apihighways(context, data_dict):
1✔
162
    _check_access('invalidate_cached_resource_id_apihighways', context, data_dict)
×
163
    caching.invalidate_cached_resource_id_apihighways()
×
164

165

166
def invalidate_region(context, data_dict):
1✔
167
    _check_access('invalidate_region', context, data_dict)
×
168

169
    from ckanext.hdx_theme.util.jql import dogpile_jql_region
×
170
    from ckanext.hdx_package.helpers.caching import dogpile_org_group_lists_region, dogpile_pkg_external_region
×
171
    from ckanext.hdx_org_group.helpers.caching import dogpile_country_region
×
172
    from ckanext.hdx_theme.helpers.caching import dogpile_requests_region
×
173

174
    region_map = {
×
175
        'jql': dogpile_jql_region,
176
        'org_group': dogpile_org_group_lists_region,
177
        'country': dogpile_country_region,
178
        'requests': dogpile_requests_region,
179
        'pkg_external': dogpile_pkg_external_region
180
    }
181

182
    region_name = data_dict.get('name', '')
×
183
    region = region_map.get(region_name)
×
184
    message = 'Couldn\'t invalidate cache for region ' + region_name
×
185
    if region:
×
186
        region.invalidate()
×
187
        message = 'Successfully invalidated cache for region ' + region_name
×
188

189
    return {
×
190
        'message': message
191
    }
192

193

194
def hdx_basic_user_info(context, data_dict):
1✔
195
    result = {}
1✔
196

197
    _check_access('hdx_basic_user_info', context, data_dict)
1✔
198

199
    model = context['model']
1✔
200
    id = data_dict.get('id', None)
1✔
201
    if id:
1✔
202
        user_obj = model.User.get(id)
1✔
203
        if user_obj is None:
1✔
204
            raise logic.NotFound
×
205
        else:
206
            ds_num = counting.count_user_datasets(id)
1✔
207
            org_num = counting.count_user_orgs(id)
1✔
208
            grp_num = counting.count_user_grps(id)
1✔
209
            result = _create_user_dict(user_obj, ds_num=ds_num, org_num=org_num, grp_num=grp_num)
1✔
210
    return result
1✔
211

212

213
def _create_user_dict(user_obj, **kw):
1✔
214
    result = {'display_name': user_obj.fullname or user_obj.name,
1✔
215
              'created': user_obj.created.isoformat(),
216
              'name': user_obj.name,
217
              'email': user_obj.email,
218
              'email_hash': user_obj.email_hash,
219
              'id': user_obj.id}
220
    result.update(kw)
1✔
221
    return result
1✔
222

223

224
# def hdx_get_sys_admins(context, data_dict):
225
#     # TODO: check access that user is logged in
226
#     q = model.Session.query(model.User).filter(model.User.sysadmin == True)
227
#     return [{'name': m.name, 'display_name': m.fullname or m.name, 'email': m.email} for m in q.all()]
228

229

230
def hdx_send_editor_request_for_org(context, data_dict):
1✔
231
    _check_access('hdx_send_editor_request_for_org', context, data_dict)
1✔
232

233
    body = _('New request editor/admin role\n' \
1✔
234
             'Full Name: {fn}\n' \
235
             'Username: {username}\n' \
236
             'Email: {mail}\n' \
237
             'Organisation: {org}\n' \
238
             'Message from user: {msg}\n' \
239
             '(This is an automated mail)' \
240
             '').format(fn=data_dict['display_name'], username=data_dict['name'], mail=data_dict['email'],
241
                        org=data_dict['organization'], msg=data_dict.get('message', ''))
242
    if config.get('hdx.onboarding.send_confirmation_email', 'false') == 'true':
1✔
243
        hdx_mail.send_mail(data_dict['admins'], _('New Request Membership'), body, one_email=True)
1✔
244

245

246
@logic.side_effect_free
1✔
247
def hdx_get_indicator_values(context, data_dict):
1✔
248
    '''
249
    Makes a call to the REST API that provides the indicator values
250
    Current param supported are:
251
    :param it: indicator types
252
    :type it: list
253
    :param l: locations
254
    :type l: list
255
    :param s: sources
256
    :type s: list
257
    :param minTime: the start year
258
    :type minTime: int
259
    :param maxTime: the end year
260
    :type maxTime: int
261
    :param periodType: filter the period by another criteria
262
    :type periodType: string
263
    :param pageNum: for pagination - page number
264
    :type pageNum: int
265
    :param pageSize: for pagination - max number of items in result
266
    :type pageSize: int
267
    :param lang: language
268
    :type lang: string
269
    '''
270

271
    transformer = transformers.FilterTransformer('#country+code', data_dict.get('l'))
×
272
    source_url = config.get('hdx.locations.toplines_url')
×
273
    if source_url:
×
274
        mapping = {
×
275
            '#country+code': 'countryCode',
276
            '#date': 'date',
277
            '#indicator+name': 'indicatorTypeName',
278
            '#indicator+unit': 'unitName',
279
            '#meta+source': 'sourceName',
280
            '#meta+url': 'datasetLink',
281
            '#value+amount': 'value',
282
        }
283
        result = transform_response_to_dict_list(do_hxl_transformation(source_url, transformer), mapping)
×
284

285
        for item in result:
×
286
            if 'value' in item:
×
287
                item['value'] = float(item['value'])
×
288

289
        return result
×
290

291
    return []
×
292

293

294
# @logic.side_effect_free
295
# def hdx_get_indicator_available_periods(context, data_dict):
296
#     '''
297
#     Makes a call to the REST API that provides the indicator values
298
#     Current param supported are:
299
#     :param it: indicator types
300
#     :type it: list
301
#     :param l: locations
302
#     :type l: list
303
#     :param s: sources
304
#     :type s: list
305
#     :param minTime: the start year
306
#     :type minTime: int
307
#     :param maxTime: the end year
308
#     :type maxTime: int
309
#     '''
310
#
311
#     endpoint = config.get('hdx.rest.indicator.endpoint.facets') + "/available-periods" + '?'
312
#
313
#     filter_list = []
314
#
315
#     for param_name in ['it', 'l', 'ds', 's', 'minTime', 'maxTime']:
316
#         param_values = data_dict.get(param_name, None)
317
#         filter_list = _add_to_filter_list(param_values, param_name, filter_list)
318
#
319
#     filter_list.sort()
320
#     url = endpoint + "&".join(filter_list)
321
#
322
#     return _make_rest_api_request(url)
323

324

325
def _add_to_filter_list(src, param_name, filter_list):
1✔
326
    if src:
×
327
        if isinstance(src, list):
×
328
            temp_filters = [
×
329
                '{}={}'.format(param_name, elem) for elem in src]
330
            filter_list = filter_list + temp_filters
×
331
        else:
332
            filter_list.append('{}={}'.format(param_name, src))
×
333

334
    return filter_list
×
335

336

337
@logic.side_effect_free
1✔
338
def hdx_carousel_settings_show(context, data_dict):
1✔
339
    '''
340
    :returns: list of dictionaries representing the setting for each carousel item. Returns default if nothing is in db.
341
    :rtype: list of dict
342
    '''
343
    from ckanext.hdx_theme.helpers.initial_carousel_settings import INITIAL_CAROUSEL_DATA
1✔
344

345
    carousel_settings = []
1✔
346
    setting_value_json = model.get_system_info('hdx.carousel.config', config.get('hdx.carousel.config'))
1✔
347
    if setting_value_json:
1✔
348
        try:
×
349
            carousel_settings = json.loads(setting_value_json)
×
350
        except TypeError as e:
×
351
            log.warn('The "hdx.carousel.config" setting is not a proper json string')
×
352

353
    if not carousel_settings and not context.get('not_initial'):
1✔
354
        carousel_settings = INITIAL_CAROUSEL_DATA
1✔
355
        for i, item in enumerate(carousel_settings):
1✔
356
            item['order'] = i + 1
1✔
357

358
    return carousel_settings
1✔
359

360

361
@logic.side_effect_free
1✔
362
def hdx_quick_links_settings_show(context, data_dict):
1✔
363
    '''
364
    :returns: list of dictionaries representing the setting for each quick_links item. Returns default if nothing is in db.
365
    :rtype: list of dict
366
    '''
367

368
    quick_links_settings = []
1✔
369
    setting_value_json = model.get_system_info('hdx.quick_links.config', config.get('hdx.quick_links.config'))
1✔
370
    if setting_value_json:
1✔
371
        try:
×
372
            quick_links_settings = json.loads(setting_value_json)
×
NEW
373
            for item in quick_links_settings:
×
NEW
374
                if 'archived' not in item:
×
NEW
375
                    item['archived']='false'
×
376
        except TypeError as e:
×
NEW
377
            log.warning('The "hdx.quick_links.config" setting is not a proper json string')
×
378

379
    return quick_links_settings
1✔
380

381

382
@logic.side_effect_free
1✔
383
def package_links_settings_show(context, data_dict):
1✔
384
    '''
385
    :returns: list of dictionaries representing the setting for each package_links item. Returns default if nothing is in db.
386
    :rtype: list of dict
387
    '''
388

389
    package_links_settings = []
×
390
    setting_value_json = model.get_system_info('hdx.package_links.config', config.get('hdx.package_links.config'))
×
391
    if setting_value_json:
×
392
        try:
×
393
            package_links_settings = json.loads(setting_value_json)
×
394
        except TypeError as e:
×
395
            log.warn('The "hdx.package_links.config" setting is not a proper json string')
×
396

397
    return package_links_settings
×
398

399

400
def hdx_carousel_settings_update(context, data_dict):
1✔
401
    '''
402

403
    :param 'hdx.carousel.config': a list with the carousel settings
404
    :type 'hdx.carousel.config': list
405
    :return: The JSON string that is the value of the new 'hdx.carousel.config'
406
    :rtype: str
407
    '''
408

409
    logic.check_access('hdx_carousel_update', context, {})
×
410

411
    settings = data_dict.get('hdx.carousel.config')
×
412
    settings_json = json.dumps(settings)
×
413
    model.set_system_info('hdx.carousel.config', settings_json)
×
414
    return settings_json
×
415

416

417
def hdx_quick_links_settings_update(context, data_dict):
1✔
418
    '''
419

420
    :param 'hdx.quick_links.config': a list with the quick_links settings
421
    :type 'hdx.quick_links.config': list
422
    :return: The JSON string that is the value of the new 'hdx.quick_links.config'
423
    :rtype: str
424
    '''
425

426
    logic.check_access('hdx_quick_links_update', context, {})
×
427

428
    settings = data_dict.get('hdx.quick_links.config')
×
429
    settings_json = json.dumps(settings)
×
430
    model.set_system_info('hdx.quick_links.config', settings_json)
×
431
    return settings_json
×
432

433

434
def package_links_settings_update(context, data_dict):
1✔
435
    '''
436

437
    :param 'hdx.package_links.config': a list with the package_links settings
438
    :type 'hdx.package_links.config': list
439
    :return: The JSON string that is the value of the new 'hdx.package_links.config'
440
    :rtype: str
441
    '''
442

443
    logic.check_access('hdx_quick_links_update', context, {})
×
444

445
    settings = data_dict.get('hdx.package_links.config')
×
446
    settings_json = json.dumps(settings)
×
447
    model.set_system_info('hdx.package_links.config', settings_json)
×
448
    return settings_json
×
449

450

451
@logic.side_effect_free
1✔
452
def package_links_by_id_list(context, data_dict):
1✔
453
    '''
454
    :returns: list of dictionaries representing the settings for a package_id
455
    :rtype: list of dict
456
    '''
457

458
    package_links_settings = []
1✔
459
    if data_dict.get('id'):
1✔
460
        setting_value_json = model.get_system_info('hdx.package_links.config', config.get('hdx.package_links.config'))
1✔
461
        if setting_value_json:
1✔
462
            try:
×
463
                _all_pls = json.loads(setting_value_json)
×
464
                for item in _all_pls:
×
465
                    if item.get('package_list'):
×
466
                        pkg_list = item.get('package_list').split(',')
×
467
                        if pkg_list and data_dict.get('id') in pkg_list:
×
468
                            package_links_settings.append(item)
×
469
            except TypeError as e:
×
470
                log.warn('The "hdx.package_links.config" setting is not a proper json string')
×
471
    return package_links_settings
1✔
472

473

474
def hdx_organization_list_for_user(context, data_dict):
1✔
475
    '''Return the organizations that the user has a given permission for.
476

477
    By default this returns the list of organizations that the currently
478
    authorized user can edit, i.e. the list of organizations that the user is an
479
    admin of.
480

481
    Specifically it returns the list of organizations that the currently
482
    authorized user has a given permission (for example: "manage_group") against.
483

484
    When a user becomes a member of an organization in CKAN they're given a
485
    "capacity" (sometimes called a "role"), for example "member", "editor" or
486
    "admin".
487

488
    Each of these roles has certain permissions associated with it. For example
489
    the admin role has the "admin" permission (which means they have permission
490
    to do anything). The editor role has permissions like "create_dataset",
491
    "update_dataset" and "delete_dataset".  The member role has the "read"
492
    permission.
493

494
    This function returns the list of organizations that the authorized user
495
    has a given permission for. For example the list of organizations that the
496
    user is an admin of, or the list of organizations that the user can create
497
    datasets in. This takes account of when permissions cascade down an
498
    organization hierarchy.
499

500
    :param permission: the permission the user has against the
501
        returned organizations, for example ``"read"`` or ``"create_dataset"``
502
        (optional, default: ``"edit_group"``)
503
    :type permission: string
504

505
    :returns: list of organizations that the user has the given permission for
506
    :rtype: list of dicts
507

508
    '''
509
    model = context['model']
×
510
    # added by HDX (from latest ckan code)
511
    if data_dict.get('id'):
×
512
        user_obj = model.User.get(data_dict['id'])
×
513
        if not user_obj:
×
514
            raise NotFound
×
515
        user = user_obj.name
×
516
    else:
517
        user = context['user']
×
518

519
    _check_access('organization_list_for_user', context, data_dict)
×
520
    sysadmin = authz.is_sysadmin(user)
×
521

522
    orgs_q = model.Session.query(model.Group) \
×
523
        .filter(model.Group.is_organization == True) \
524
        .filter(model.Group.state == 'active')
525

526
    if not sysadmin:
×
527
        # for non-Sysadmins check they have the required permission
528

529
        # NB 'edit_group' doesn't exist so by default this action returns just
530
        # orgs with admin role
531
        permission = data_dict.get('permission', 'edit_group')
×
532

533
        roles = authz.get_roles_with_permission(permission)
×
534

535
        if not roles:
×
536
            return []
×
537
        user_id = authz.get_user_id_for_username(user, allow_none=True)
×
538
        if not user_id:
×
539
            return []
×
540

541
        q = model.Session.query(model.Member, model.Group) \
×
542
            .filter(model.Member.table_name == 'user') \
543
            .filter(model.Member.capacity.in_(roles)) \
544
            .filter(model.Member.table_id == user_id) \
545
            .filter(model.Member.state == 'active') \
546
            .join(model.Group)
547

548
        group_ids = set()
×
549
        roles_that_cascade = \
×
550
            authz.check_config_permission('roles_that_cascade_to_sub_groups')
551
        for member, group in q.all():
×
552
            if member.capacity in roles_that_cascade:
×
553
                group_ids |= set([
×
554
                    grp_tuple[0] for grp_tuple
555
                    in group.get_children_group_hierarchy(type='organization')
556
                ])
557
            group_ids.add(group.id)
×
558

559
        if not group_ids:
×
560
            return []
×
561

562
        orgs_q = orgs_q.filter(model.Group.id.in_(group_ids))
×
563

564
    orgs_list = model_dictize.group_list_dictize(orgs_q.all(), context)
×
565
    return orgs_list
×
566

567

568
def hdx_push_general_stats(context, data_dict):
1✔
569

570
    _check_access('hdx_push_general_stats', context, {})
1✔
571
    stats_helper = HDXStatsHelper(context, compute_user_stats=False).fetch_data()
1✔
572

573
    analytics_sender = HDXStatsAnalyticsSender(stats_helper)
1✔
574
    analytics_sender.send_to_queue()
1✔
575

576
    if analytics_sender.pushed_successfully:
1✔
577
        return analytics_sender.stats_dict
1✔
578
    else:
579
        raise Exception('There was a problem pushing the stats to the server')
×
580

581

582
@logic.side_effect_free
1✔
583
def hdx_general_statistics(context, data_dict):
1✔
584

585
    stats_helper = HDXStatsHelper(context).fetch_data()
1✔
586

587
    results = {
1✔
588
        'datasets': {
589
            'total': stats_helper.datasets_total,
590
            'with_geodata': stats_helper.datasets_with_geodata,
591
            'with_showcases': stats_helper.datasets_with_showcases,
592
            'qa': {
593
                'in_quarantine': stats_helper.datasets_qa_in_quarantine,
594
                HDXStatsHelper.IN_QA: stats_helper.datasets_qa_in_qa,
595
                HDXStatsHelper.QA_COMPLETED: stats_helper.datasets_qa_qa_completed,
596
            }
597
        },
598
        'organizations': {
599
            'total': stats_helper.orgs_total,
600
            'with_datasets': stats_helper.orgs_with_datasets,
601
            'without_datasets': stats_helper.orgs_total - stats_helper.orgs_with_datasets,
602
            'active_last_year': stats_helper.orgs_updating_data_past_year,
603
            'not_active_last_year': stats_helper.orgs_total - stats_helper.orgs_updating_data_past_year,
604
        },
605
        'users': {
606
            'total': stats_helper.active_users_completed + stats_helper.active_users_not_completed,
607
            'completed_registration': stats_helper.active_users_completed,
608
            'not_completed_registration': stats_helper.active_users_not_completed
609
        }
610
    }
611

612
    return results
1✔
613

614

615
# def page_create(context, data_dict):
616
#     '''
617
#     Only sysadmins are allowed to call this action
618
#     '''
619
#     return {'success': False, 'msg': _('Only sysadmins can manage custom pages')}
620

621

622
@logic.side_effect_free
1✔
623
def hdx_user_statistics(context, data_dict):
1✔
624
    _check_access('hdx_user_statistics', context, data_dict)
1✔
625

626
    start_date = data_dict.get('start_date', None)
1✔
627
    end_date = data_dict.get('end_date', None)
1✔
628
    q = model.Session.query(model.User)
1✔
629
    q = q.filter(model.User.state == 'active')
1✔
630
    user_list = q.all()
1✔
631

632
    result = {}
1✔
633
    for user in user_list:
1✔
634
        no_activities = get_activities_by_user_by_date(user.id, start_date, end_date)
1✔
635
        result[user.id] = {
1✔
636
            'id': user.id,
637
            'name': user.name,
638
            'no_of_activities': no_activities
639
        }
640
    return result
1✔
641

642

643
def get_activities_by_user_by_date(user_id, start_date=None, end_date=None):
1✔
644
    result = None
1✔
645
    if user_id:
1✔
646
        q = model.Session.query(model.Activity)
1✔
647
        q = q.filter(model.Activity.user_id == user_id)
1✔
648
        if start_date:
1✔
649
            q = q.filter(model.Activity.timestamp >= start_date)
×
650
        if end_date:
1✔
651
            q = q.filter(model.Activity.timestamp <= end_date)
×
652
        result = q.count()
1✔
653
    return result
1✔
654

655

656
@logic.side_effect_free
1✔
657
def hdx_organization_statistics(context, data_dict):
1✔
658
    _check_access('hdx_user_statistics', context, data_dict)
1✔
659

660
    org_list = tk.get_action('organization_list')(context, {"all_fields": True})
1✔
661

662
    query = model.Session.query(model.Group.id)
1✔
663
    query = query.filter(model.Group.state != 'active')
1✔
664
    query = query.filter(model.Group.is_organization == True)
1✔
665
    query = query.filter(model.Group.type == 'organization')
1✔
666
    orgs = query.all()
1✔
667
    inactive_org_list = []
1✔
668
    for org in orgs:
1✔
669
        inactive_org_list.append(logic.get_action('organization_show')(context, {'id': org.id, 'include_extras': True}))
×
670

671
    return {
1✔
672
        'active_organizations': org_list,
673
        'inactive_organizations': inactive_org_list
674
    }
675

676

677
@logic.side_effect_free
1✔
678
def hdx_activity_detail_list(context, data_dict):
1✔
679
    result = ckan_get.activity_detail_list(context, data_dict)
×
680
    detail_data_list = (
×
681
        detail_data
682
        for detail in result or []
683
        for detail_data in detail.get('data', {}).values()
684
    )
685
    for detail_data_dict in detail_data_list:
×
686
        detail_data_dict.pop('maintainer_email', None)
×
687
        detail_data_dict.pop('author_email', None)
×
688

689
    return result
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc