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

OCHA-DAP / hdx-ckan / #5649

26 Aug 2024 11:34AM UTC coverage: 72.882% (-0.1%) from 72.998%
#5649

Pull #6412

coveralls-python

web-flow
Merge branch 'dev' into feature/HDX-9990-implement-new-contact-contributor-form
Pull Request #6412: HDX-9960 & HDX-9987 contact the contributor & HDX Connect new pages

146 of 321 new or added lines in 10 files covered. (45.48%)

1 existing line in 1 file now uncovered.

11750 of 16122 relevant lines covered (72.88%)

0.73 hits per line

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

68.18
/ckanext-hdx_org_group/ckanext/hdx_org_group/helpers/organization_helper.py
1
'''
2
Created on Jan 14, 2015
3

4
@author: alexandru-m-g
5
'''
6

7
import json
1✔
8
import logging
1✔
9
import os
1✔
10
import six
1✔
11

12
import ckanext.hdx_search.cli.click_feature_search_command as lunr
1✔
13
import ckanext.hdx_theme.helpers.helpers as h
1✔
14
import ckanext.hdx_users.helpers.mailer as hdx_mailer
1✔
15
from sqlalchemy import func
1✔
16
import ckanext.hdx_org_group.helpers.static_lists as static_lists
1✔
17

18
import ckan.lib.dictization as dictization
1✔
19
import ckan.lib.dictization.model_dictize as model_dictize
1✔
20
import ckan.lib.dictization.model_save as model_save
1✔
21
import ckan.lib.helpers as helpers
1✔
22
import ckan.lib.navl.dictization_functions
1✔
23
import ckan.lib.plugins as lib_plugins
1✔
24
import ckan.lib.uploader as uploader
1✔
25
import ckan.logic as logic
1✔
26
import ckan.logic.action as core
1✔
27
import ckan.model as model
1✔
28
import ckan.plugins as plugins
1✔
29
from collections import OrderedDict
1✔
30
from ckan.common import _, c, config
1✔
31
import ckan.plugins.toolkit as toolkit
1✔
32
import ckan.lib.base as base
1✔
33

34
BUCKET = str(uploader.get_storage_path()) + '/storage/uploads/group/'
1✔
35
abort = base.abort
1✔
36

37
log = logging.getLogger(__name__)
1✔
38
chained_action = toolkit.chained_action
1✔
39
get_action = logic.get_action
1✔
40
check_access = logic.check_access
1✔
41
_get_or_bust = logic.get_or_bust
1✔
42
_validate = ckan.lib.navl.dictization_functions.validate
1✔
43

44
NotFound = logic.NotFound
1✔
45
ValidationError = logic.ValidationError
1✔
46

47

48
def filter_and_sort_results_case_insensitive(results, sort_by, q=None, has_datasets=False):
1✔
49
    '''
50
    :param results: list of organizations to filter/sort
51
    :type results: list[dict]
52
    :param sort_by:
53
    :type sort_by: str
54
    :param q:
55
    :type q: str
56
    :param has_datasets: True if it should filter out orgs without at least one datasets
57
    :type has_datasets: bool
58
    :return: sorted/filtered list
59
    :rtype: list[dict]
60
    '''
61

62
    filtered_results = results
1✔
63
    if q:
1✔
64
        q = q.lower()
×
65
        filtered_results = [org for org in filtered_results
×
66
                            if q in org.get('title', '').lower() or q in org.get('name', '')
67
                            or q in org.get('description', '').lower()]
68
    if has_datasets:
1✔
69
        filtered_results = [org for org in filtered_results if 'regular_package_count' in org and org['regular_package_count']]
1✔
70

71
    if filtered_results:
1✔
72
        if sort_by == 'title asc':
×
73
            return sorted(filtered_results, key=lambda x: x.get('title', '').lower())
×
74
        elif sort_by == 'title desc':
×
75
            return sorted(filtered_results, key=lambda x: x.get('title', '').lower(), reverse=True)
×
76
        elif sort_by == 'datasets desc':
×
77
            return sorted(filtered_results, key=lambda x: x.get('package_count', 0), reverse=True)
×
78
        elif sort_by == 'datasets asc':
×
79
            return sorted(filtered_results, key=lambda x: x.get('package_count', 0))
×
80
        elif sort_by == 'popularity':
×
81
            pass
×
82
    return filtered_results
1✔
83

84

85
# def hdx_get_featured_orgs(context, data_dict):
86
#     orgs = list()
87
#     # Pull resource with data on our featured orgs
88
#     resource_id = config.get('hdx.featured_org_config')  # Move this to common_config once data team has set things up
89
#     user = context.get('user')
90
#     userobj = context.get('auth_user_obj')
91
#     reset_thumbnails = data_dict.get('reset_thumbnails', 'false')
92
#     featured_config = get_featured_orgs_config(user, userobj, resource_id)
93
#
94
#     for cfg in featured_config:
95
#         # getting the first 3 rows/organizations
96
#         if len(orgs) < 3:
97
#             # Check for screencap
98
#             file_path = BUCKET + cfg.get('org_name') + '_thumbnail.png'
99
#             exists = os.path.isfile(file_path)
100
#             expired = False
101
#             if exists:
102
#                 timestamp = datetime.fromtimestamp(os.path.getmtime(file_path))
103
#                 expire = timestamp + timedelta(days=7)
104
#                 expired = datetime.utcnow() > expire
105
#             reset = not exists or expired
106
#             if reset or reset_thumbnails == 'true':
107
#                 # Build new screencap
108
#                 context['reset'] = reset
109
#                 context['file_path'] = file_path
110
#                 context['cfg'] = cfg
111
#                 log.info("Triggering screenshot for " + cfg.get('org_name'))
112
#                 get_action('hdx_trigger_screencap')(context, data_dict)
113
#
114
#             org_dict = get_action('organization_show')(context, {'id': cfg.get('org_name')})
115
#
116
#             # Build highlight data
117
#             org_dict['highlight'] = get_featured_org_highlight(context, org_dict, cfg)
118
#
119
#             # checking again here if the file was generated
120
#             exists = os.path.isfile(file_path)
121
#             if exists:
122
#                 org_dict['featured_org_thumbnail'] = "/image/" + cfg['org_name'] + '_thumbnail.png'
123
#             else:
124
#                 org_dict['featured_org_thumbnail'] = "/images/featured_orgs_placeholder" + str(len(orgs)) + ".png"
125
#             orgs.append(org_dict)
126
#     return orgs
127

128

129
def get_viz_title_from_extras(org_dict):
1✔
130
    try:
×
131
        for item in org_dict.get('extras'):
×
132
            if item.get('key') == 'visualization_config':
×
133
                result = json.loads(item.get('value')).get('vis-title') or json.loads(item.get('value')).get(
×
134
                    'viz-title')
135
                return result
×
136
    except:
×
137
        return None
×
138
    return None
×
139

140

141
def get_value_dict_from_extras(org_dict, key='visualization_config'):
1✔
142
    try:
×
143
        for item in org_dict.get('extras'):
×
144
            if item.get('key') == key:
×
145
                return json.loads(item.get('value'))
×
146
    except:
×
147
        return None
×
148
    return None
×
149

150

151
def get_featured_org_highlight(context, org_dict, config):
1✔
152
    link = helpers.url_for('organization_read', id=org_dict.get('name'))
1✔
153
    title = ''
1✔
154
    description = ''
1✔
155
    if config.get('highlight_asset_type').strip().lower() == 'key figures':
1✔
156
        description = 'Key Figures'
1✔
157
        link += '#key-figures'
1✔
158
    if config.get('highlight_asset_type').strip().lower() == 'interactive data':
1✔
159
        description = 'Interactive Data: '
×
160
        link += '#interactive-data'
×
161
        title = get_viz_title_from_extras(org_dict)
×
162
    return {'link': link, 'description': description, 'type': config.get('highlight_asset_type'), 'title': title}
1✔
163

164

165
# def get_featured_orgs_config(user, userobj, resource_id):
166
#     context = {'model': model, 'session': model.Session,
167
#                'user': user, 'for_view': True,
168
#                'auth_user_obj': userobj}
169
#     featured_org_dict = {
170
#         'datastore_config': {
171
#             'resource_id': resource_id
172
#         }
173
#     }
174
#     datastore_access = data_access.DataAccess(featured_org_dict)
175
#     datastore_access.fetch_data_generic(context)
176
#     org_items = datastore_access.get_top_line_items()
177
#
178
#     return org_items
179

180

181
def hdx_get_group_activity_list(context, data_dict):
1✔
182
    from ckanext.hdx_package.helpers import helpers as hdx_package_helpers
×
183

184
    group_uuid = data_dict.get('group_uuid', None)
×
185
    if group_uuid:
×
186
        check_access('group_show', context, data_dict)
×
187

188
        model = context['model']
×
189
        offset = data_dict.get('offset', 0)
×
190
        limit = int(
×
191
            data_dict.get('limit', config.get('ckan.activity_list_limit', 31)))
192

193
        activity_objects = model.activity.group_activity_list(group_uuid,
×
194
                                                              limit=limit, offset=offset)
195
        activity_stream = model_dictize.activity_list_dictize(
×
196
            activity_objects, context)
197
    else:
198
        if 'group_type' in data_dict and data_dict['group_type'] == 'organization':
×
199
            activity_stream = get_action(
×
200
                'organization_activity_list')(context, data_dict)
201
        else:
202
            activity_stream = get_action(
×
203
                'group_activity_list')(context, data_dict)
204
    offset = int(data_dict.get('offset', 0))
×
205
    extra_vars = {
×
206
        'controller': 'group',
207
        'action': 'activity',
208
        'id': data_dict['id'],
209
        'offset': offset,
210
    }
211
    return hdx_package_helpers._activity_list(context, activity_stream, extra_vars)
×
212

213

214
# def compile_less(result, translate_func=None):
215
#     return True
216
    # base_color = '#007CE0'  # default value
217
    # logo_use_org_color = "false"
218
    #
219
    # def get_value_from_result(key):
220
    #     value = result.get(key)
221
    #     if not value:
222
    #         extra = next((e for e in (result.get('extras') or []) if e['key'] == key), None)
223
    #         if extra:
224
    #             value = extra.get('value')
225
    #     return value
226
    #
227
    # less_code_list = get_value_from_result('less')
228
    # customization = get_value_from_result('customization')
229
    #
230
    # if customization:
231
    #     try:
232
    #         variables = json.loads(customization)
233
    #         base_color = variables.get('highlight_color', '#007CE0') or '#007CE0'
234
    #         logo_use_org_color = variables.get('use_org_color', 'false')
235
    #     except:
236
    #         base_color = '#007CE0'
237
    #
238
    # if less_code_list:
239
    #     less_code = less_code_list.strip()
240
    #     if less_code:
241
    #         less_code = _add_custom_less_code(base_color, logo_use_org_color) + less_code
242
    #         css_dest_dir = '/organization/' + result['name']
243
    #         compiler = less.LessCompiler(less_code, css_dest_dir, result['name'],
244
    #                                      h.hdx_get_extras_element(result, value_key="modified_at"),
245
    #                                      translate_func=translate_func)
246
    #         compilation_result = compiler.compile_less()
247
    #         result['less_compilation'] = compilation_result
248

249

250
# def _add_custom_less_code(base_color, logo_use_org_color):
251
#     # Add base color definition
252
#     less_code = '\n\r@wfpBlueColor: ' + base_color + ';\n\r'
253
#     if not 'true' == logo_use_org_color:
254
#         less_code += '@logoBackgroundColor: #FAFAFA; @logoBorderColor: #CCCCCC;'
255
#     return less_code
256

257

258
def hdx_organization_update(context, data_dict):
1✔
259
    test = True if config.get('ckan.site_id') == 'test.ckan.net' else False
1✔
260
    result = hdx_group_or_org_update(context, data_dict, is_org=True)
1✔
261
    if not test:
1✔
262
        lunr.build_index()
×
263

264
    # hdx_generate_embedded_preview(result)
265
    return result
1✔
266

267

268
# def hdx_generate_embedded_preview(result):
269
#     org_name = result.get('name') or result.get('id')
270
#     vis_config = get_value_dict_from_extras(result, 'visualization_config')
271
#     if vis_config and vis_config.get('visualization-select') == 'embedded-preview':
272
#         selector = vis_config.get('vis-preview-selector', None)
273
#         url = vis_config.get('vis-url')
274
#         file_path = BUCKET + org_name + '_embedded_preview.png'
275
#         hdx_capturejs(url, file_path, selector, renderdelay=15000)
276
#         return True
277
#     return False
278

279

280
def remove_image(filename):
1✔
281
    if not filename.startswith('http'):
×
282
        try:
×
283
            os.remove(uploader.get_storage_path() + '/storage/uploads/group/' + filename)
×
284
        except:
×
285
            return False
×
286
    return True
×
287

288

289
def hdx_group_create(context, data_dict):
1✔
290
    return _run_core_group_org_action(context, data_dict, core.create.group_create)
1✔
291

292

293
def hdx_group_update(context, data_dict):
1✔
294
    return _run_core_group_org_action(context, data_dict, core.update.group_update)
1✔
295

296

297
def hdx_group_delete(context, data_dict):
1✔
298
    return _run_core_group_org_action(context, data_dict, core.delete.group_delete)
1✔
299

300
def _check_user_is_maintainer(user_id, org_id):
1✔
301
    group = model.Group.get(org_id)
1✔
302
    result = logic.get_action('package_search')({}, {
1✔
303
        'q': '*:*',
304
        'fq': 'maintainer:{0}, organization:{1}'.format(user_id, group.name),
305
        'rows': 100,
306
    })
307

308
    if len(result['results']) > 0:
1✔
309
        return True
1✔
310
    return False
1✔
311

312
@chained_action
1✔
313
def organization_member_delete(original_action, context, data_dict):
1✔
314
    user_id = data_dict.get('user_id') or data_dict.get('user')
1✔
315
    if not user_id:
1✔
316
        user_id = model.User.get(data_dict.get('username')).id
1✔
317

318
    if _check_user_is_maintainer(user_id, data_dict.get('id')):
1✔
319
        abort(403, _('User is set as maintainer for datasets belonging to this org. Can\t delete, please change maintainer first'))
1✔
320

321
    return original_action(context, data_dict)
1✔
322

323
@chained_action
1✔
324
def organization_member_create(original_action, context, data_dict):
1✔
325
    user_id = data_dict.get('user') or data_dict.get('user_id')
1✔
326
    if not user_id:
1✔
327
        user_id = model.User.get(data_dict.get('username')).id
1✔
328

329
    if data_dict.get('role') == 'member':
1✔
330
        if _check_user_is_maintainer(user_id, data_dict.get('id')):
1✔
331
            abort(403, _('User is set as maintainer for datasets belonging to this org. Can\'t change role to \'member\', please change maintainer first'))
×
332

333
    return original_action(context, data_dict)
1✔
334

335
def hdx_organization_create(context, data_dict):
1✔
336
    data_dict['type'] = 'organization'
1✔
337
    test = True if config.get('ckan.site_id') == 'test.ckan.net' else False
1✔
338
    result = hdx_group_or_org_create(context, data_dict, is_org=True)
1✔
339
    if not test:
1✔
340
        lunr.build_index()
×
341

342
    # hdx_generate_embedded_preview(result)
343
    return result
1✔
344

345

346
def hdx_organization_delete(context, data_dict):
1✔
347
    return _run_core_group_org_action(context, data_dict, core.delete.organization_delete)
1✔
348

349

350
def _run_core_group_org_action(context, data_dict, core_action):
1✔
351
    '''
352
    Runs core ckan action with lunr update
353
    '''
354
    test = True if config.get('ckan.site_id') == 'test.ckan.net' else False
1✔
355
    result = core_action(context, data_dict)
1✔
356
    if not test:
1✔
357
        lunr.build_index()
×
358
    return result
1✔
359

360

361
def hdx_group_or_org_update(context, data_dict, is_org=False):
1✔
362
    # Overriding default so that orgs can have multiple images
363
    model = context['model']
1✔
364
    user = context['user']
1✔
365
    session = context['session']
1✔
366
    id = _get_or_bust(data_dict, 'id')
1✔
367

368
    group = model.Group.get(id)
1✔
369
    context["group"] = group
1✔
370
    if group is None:
1✔
371
        raise NotFound('Group was not found.')
×
372

373
    data_dict['type'] = group.type
1✔
374

375
    # get the schema
376
    group_plugin = lib_plugins.lookup_group_plugin(group.type)
1✔
377
    try:
1✔
378
        schema = group_plugin.form_to_db_schema_options({'type': 'update',
1✔
379
                                                         'api': 'api_version' in context,
380
                                                         'context': context})
381
    except AttributeError:
×
382
        schema = group_plugin.form_to_db_schema()
×
383

384
    if is_org:
1✔
385
        check_access('organization_update', context, data_dict)
1✔
386
    else:
387
        check_access('group_update', context, data_dict)
×
388

389

390
    try:
1✔
391
        customization = json.loads(group.extras['customization'])
1✔
392
    except:
×
393
        customization = {'image_sq': '', 'image_rect': ''}
×
394

395
    try:
1✔
396
        data_dict['customization'] = json.loads(data_dict['customization'])
1✔
397
    except:
1✔
398
        data_dict['customization'] = {}
1✔
399

400
    # If we're removing the image
401
    upload_sq = _manage_image_upload_for_org('image_sq', customization, data_dict)
1✔
402
    upload_rect = _manage_image_upload_for_org('image_rect', customization, data_dict)
1✔
403

404
    storage_path = uploader.get_storage_path()
1✔
405
    ##Rearrange things the way we need them
406
    try:
1✔
407
        if data_dict['image_sq'] != '' and data_dict['image_sq'] != None:
1✔
408
            data_dict['customization']['image_sq'] = data_dict['image_sq']
×
409
        else:
410
            data_dict['customization']['image_sq'] = customization['image_sq']
×
411
    except KeyError:
1✔
412
        data_dict['customization']['image_sq'] = ''
1✔
413

414
    try:
1✔
415
        if data_dict['image_rect'] != '' and data_dict['image_rect'] != None:
1✔
416
            data_dict['customization']['image_rect'] = data_dict['image_rect']
×
417
        else:
418
            data_dict['customization']['image_rect'] = customization['image_rect']
×
419
    except KeyError:
1✔
420
        data_dict['customization']['image_rect'] = ''
1✔
421

422
    data_dict['customization'] = json.dumps(data_dict['customization'])
1✔
423

424
    if 'api_version' not in context:
1✔
425
        # old plugins do not support passing the schema so we need
426
        # to ensure they still work
427
        try:
1✔
428
            group_plugin.check_data_dict(data_dict, schema)
1✔
429
        except TypeError:
1✔
430
            group_plugin.check_data_dict(data_dict)
1✔
431

432
    data, errors = lib_plugins.plugin_validate(
1✔
433
        group_plugin, context, data_dict, schema,
434
        'organization_update' if is_org else 'group_update')
435
    log.debug('group_update validate_errs=%r user=%s group=%s data_dict=%r',
1✔
436
              errors, context.get('user'),
437
              context.get('group').name if context.get('group') else '',
438
              data_dict)
439

440
    if errors:
1✔
441
        session.rollback()
×
442
        raise ValidationError(errors)
×
443

444
    contains_packages = 'packages' in data_dict
1✔
445

446
    group = model_save.group_dict_save(
1✔
447
        data, context,
448
        prevent_packages_update=is_org or not contains_packages
449
    )
450

451
    if is_org:
1✔
452
        plugin_type = plugins.IOrganizationController
1✔
453
    else:
454
        plugin_type = plugins.IGroupController
×
455

456
    for item in plugins.PluginImplementations(plugin_type):
1✔
457
        item.edit(group)
1✔
458

459
    if is_org:
1✔
460
        activity_type = 'changed organization'
1✔
461
    else:
462
        activity_type = 'changed group'
×
463

464
    activity_dict = {
1✔
465
            'user_id': model.User.by_name(six.ensure_text(user)).id,
466
            'object_id': group.id,
467
            'activity_type': activity_type,
468
            }
469
    # Handle 'deleted' groups.
470
    # When the user marks a group as deleted this comes through here as
471
    # a 'changed' group activity. We detect this and change it to a 'deleted'
472
    # activity.
473
    if group.state == u'deleted':
1✔
474
        if session.query(ckan.model.Activity).filter_by(
×
475
                object_id=group.id, activity_type='deleted').all():
476
            # A 'deleted group' activity for this group has already been
477
            # emitted.
478
            # FIXME: What if the group was deleted and then activated again?
479
            activity_dict = None
×
480
        else:
481
            # We will emit a 'deleted group' activity.
482
            activity_dict['activity_type'] = \
×
483
                'deleted organization' if is_org else 'deleted group'
484
    if activity_dict is not None:
1✔
485
        activity_dict['data'] = {
1✔
486
            'group': dictization.table_dictize(group, context)
487
        }
488
        activity_create_context = {
1✔
489
            'model': model,
490
            'user': user,
491
            'defer_commit': True,
492
            'ignore_auth': True,
493
            'session': session
494
        }
495
        get_action('activity_create')(activity_create_context, activity_dict)
1✔
496
        # TODO: Also create an activity detail recording what exactly changed
497
        # in the group.
498

499
    if upload_sq:
1✔
500
        upload_sq.upload(uploader.get_max_image_size())
×
501

502
    if upload_rect:
1✔
503
        upload_rect.upload(uploader.get_max_image_size())
×
504

505
    if not context.get('defer_commit'):
1✔
506
        model.repo.commit()
1✔
507

508
    return model_dictize.group_dictize(group, context)
1✔
509

510

511
def _manage_image_upload_for_org(image_field, customization, data_dict):
1✔
512
    clear_field = 'clear_{}'.format(image_field)
1✔
513
    if clear_field in data_dict and data_dict[clear_field]:
1✔
514
        remove_image(customization[image_field])
×
515
        data_dict['customization'][image_field] = ''
×
516
        # customization[image_field] = ''
517

518
    upload_field = '{}_upload'.format(image_field)
1✔
519
    if data_dict.get(upload_field):
1✔
520
        # If old image exists remove it
521
        if customization[image_field]:
×
522
            remove_image(customization[image_field])
×
523

524
        upload_obj = uploader.Upload('group', customization[image_field])
×
525
        upload_obj.update_data_dict(data_dict, image_field,
×
526
                                 upload_field, 'clear_upload')
527

528
        return upload_obj
×
529
    return None
1✔
530

531

532
def hdx_group_or_org_create(context, data_dict, is_org=False):
1✔
533
    # Overriding default so that orgs can have multiple images
534

535
    model = context['model']
1✔
536
    user = context['user']
1✔
537
    session = context['session']
1✔
538
    data_dict['is_organization'] = is_org
1✔
539

540
    if is_org:
1✔
541
        check_access('organization_create', context, data_dict)
1✔
542
    else:
543
        check_access('group_create', context, data_dict)
×
544

545
    # get the schema
546
    group_type = data_dict.get('type')
1✔
547
    group_plugin = lib_plugins.lookup_group_plugin(group_type)
1✔
548
    try:
1✔
549
        schema = group_plugin.form_to_db_schema_options({
1✔
550
            'type': 'create', 'api': 'api_version' in context,
551
            'context': context})
552
    except AttributeError:
×
553
        schema = group_plugin.form_to_db_schema()
×
554

555
    # try:
556
    #     customization = json.loads(group.extras['customization'])
557
    # except:
558
    customization = {'image_sq': '', 'image_rect': ''}
1✔
559

560
    try:
1✔
561
        data_dict['customization'] = json.loads(data_dict['customization'])
1✔
562
    except:
1✔
563
        data_dict['customization'] = {}
1✔
564

565
    if 'image_sq_upload' in data_dict and data_dict['image_sq_upload'] != '' and data_dict['image_sq_upload'] != None:
1✔
566
        # If old image was uploaded remove it
567
        if customization['image_sq']:
×
568
            remove_image(customization['image_sq'])
×
569

570
        upload1 = uploader.Upload('group', customization['image_sq'])
×
571
        upload1.update_data_dict(data_dict, 'image_sq',
×
572
                                 'image_sq_upload', 'clear_upload')
573

574
    if 'image_rect_upload' in data_dict and data_dict['image_rect_upload'] != '' and data_dict[
1✔
575
        'image_rect_upload'] != None:
576
        if customization['image_rect']:
×
577
            remove_image(customization['image_rect'])
×
578
        upload2 = uploader.Upload('group', customization['image_rect'])
×
579
        upload2.update_data_dict(data_dict, 'image_rect',
×
580
                                 'image_rect_upload', 'clear_upload')
581

582
    storage_path = uploader.get_storage_path()
1✔
583
    ##Rearrange things the way we need them
584
    try:
1✔
585
        if data_dict['image_sq'] != '' and data_dict['image_sq'] != None:
1✔
586
            data_dict['customization']['image_sq'] = data_dict['image_sq']
×
587
        else:
588
            data_dict['customization']['image_sq'] = customization['image_sq']
×
589
    except KeyError:
1✔
590
        data_dict['customization']['image_sq'] = ''
1✔
591

592
    try:
1✔
593
        if data_dict['image_rect'] != '' and data_dict['image_rect'] != None:
1✔
594
            data_dict['customization']['image_rect'] = data_dict['image_rect']
×
595
        else:
596
            data_dict['customization']['image_rect'] = customization['image_rect']
×
597
    except KeyError:
1✔
598
        data_dict['customization']['image_rect'] = ''
1✔
599

600
    data_dict['customization'] = json.dumps(data_dict['customization'])
1✔
601

602
    if 'api_version' not in context:
1✔
603
        # old plugins do not support passing the schema so we need
604
        # to ensure they still work
605
        try:
1✔
606
            group_plugin.check_data_dict(data_dict, schema)
1✔
607
        except TypeError as e:
1✔
608
            group_plugin.check_data_dict(data_dict)
1✔
609

610
    data, errors = lib_plugins.plugin_validate(
1✔
611
        group_plugin, context, data_dict, schema,
612
        'organization_create' if is_org else 'group_create')
613
    log.debug('group_create validate_errs=%r user=%s group=%s data_dict=%r',
1✔
614
              errors, context.get('user'), data_dict.get('name'), data_dict)
615

616
    if errors:
1✔
617
        session.rollback()
1✔
618
        raise ValidationError(errors)
1✔
619

620
    group = model_save.group_dict_save(data, context)
1✔
621

622
    # Needed to let extensions know the group id
623
    session.flush()
1✔
624

625
    if is_org:
1✔
626
        plugin_type = plugins.IOrganizationController
1✔
627
    else:
628
        plugin_type = plugins.IGroupController
×
629

630
    for item in plugins.PluginImplementations(plugin_type):
1✔
631
        item.create(group)
1✔
632

633
    if is_org:
1✔
634
        activity_type = 'new organization'
1✔
635
    else:
636
        activity_type = 'new group'
×
637

638
    user_id = model.User.by_name(six.ensure_text(user)).id
1✔
639

640
    activity_dict = {
1✔
641
        'user_id': user_id,
642
        'object_id': group.id,
643
        'activity_type': activity_type,
644
        'data': {
645
            'group': ckan.lib.dictization.table_dictize(group, context)
646
        }
647
    }
648
    activity_create_context = {
1✔
649
        'model': model,
650
        'user': user,
651
        'defer_commit': True,
652
        'ignore_auth': True,
653
        'session': session
654
    }
655
    logic.get_action('activity_create')(activity_create_context, activity_dict)
1✔
656

657
    try:
1✔
658
        upload1.upload(uploader.get_max_image_size())
1✔
659
    except:
1✔
660
        pass
1✔
661

662
    try:
1✔
663
        upload2.upload(uploader.get_max_image_size())
1✔
664
    except:
1✔
665
        pass
1✔
666

667
    if not context.get('defer_commit'):
1✔
668
        model.repo.commit()
1✔
669
    context["group"] = group
1✔
670
    context["id"] = group.id
1✔
671

672
    # creator of group/org becomes an admin
673
    # this needs to be after the repo.commit or else revisions break
674
    member_dict = {
1✔
675
        'id': group.id,
676
        'object': user_id,
677
        'object_type': 'user',
678
        'capacity': 'admin',
679
    }
680
    member_create_context = {
1✔
681
        'model': model,
682
        'user': user,
683
        'ignore_auth': True,  # we are not a member of the group at this point
684
        'session': session
685
    }
686
    logic.get_action('member_create')(member_create_context, member_dict)
1✔
687

688
    log.debug('Created object %s' % group.name)
1✔
689

690
    return_id_only = context.get('return_id_only', False)
1✔
691
    action = 'organization_show' if is_org else 'group_show'
1✔
692

693
    output = context['id'] if return_id_only \
1✔
694
        else get_action(action)(context, {'id': group.id})
695

696
    return output
1✔
697

698

699
# def recompile_everything(context):
700
#     orgs = get_action('organization_list')(context, {'all_fields': False})
701
#     if orgs:
702
#         for org_name in orgs:
703
#             org = get_action('hdx_light_group_show')(context, {'id': org_name})
704
#             compile_less(org, translate_func=lambda str: str)
705

706

707
# def hdx_capturejs(uri, output_file, selector, renderdelay=90000, waitcapturedelay=10000, viewportsize='1200x800'):
708
#     quoted_selector = '"{}"'.format(selector)
709
#     screenshot_creator = ScreenshotCreator(uri, output_file, quoted_selector,
710
#                                            renderdelay=renderdelay, waitcapturedelay=waitcapturedelay,
711
#                                            http_timeout=None,
712
#                                            viewportsize=viewportsize, mogrify=True, resize='40%')
713
#     return screenshot_creator.execute()
714

715
def notify_admins(data_dict):
1✔
716
    try:
×
717
        if data_dict.get('admins'):
×
718
            # for admin in data_dict.get('admins'):
719
            hdx_mailer.mail_recipient(data_dict.get('admins'), data_dict.get('subject'), data_dict.get('message'))
×
720
    except Exception as e:
×
721
        log.error("Email server error: can not send email to admin users" + e.message)
×
722
        return False
×
723
    log.info("admin users where notified by email")
×
724
    return True
×
725

726

727
def hdx_user_in_org_or_group(group_id, include_pending=False):
1✔
728
    '''
729
    Based on user_in_org_or_group() from ckan.lib.helpers.
730
    Added a flag that includes "pending" requests in the check.
731
    Useful for not showing the "request membership" option for a user that already has done the request.
732
    :param group_id:
733
    :type group_id: str
734
    :param include_pending: if it should include the "pending" state in the check ( not just the "active") (optional)
735
    :type include_pending: bool
736
    :return: True if the user belongs to the group or org. Otherwise False
737
    :rtype: bool
738
    '''
739

740
    # we need a user
741
    if not c.userobj:
1✔
742
        return False
×
743
    # sysadmins can do anything
744
    if c.userobj.sysadmin:
1✔
745
        return True
1✔
746

747
    checked_states = ['active']
1✔
748
    if include_pending:
1✔
749
        checked_states.append('pending')
1✔
750

751
    query = model.Session.query(func.count(model.Member.id)) \
1✔
752
        .filter(model.Member.state.in_(checked_states)) \
753
        .filter(model.Member.table_name == 'user') \
754
        .filter(model.Member.group_id == group_id) \
755
        .filter(model.Member.table_id == c.userobj.id)
756
    length = query.all()[0][0]
1✔
757
    return length != 0
1✔
758

759

760
def hdx_organization_type_dict(include_default_value=None):
1✔
NEW
761
    result = OrderedDict()
×
762

763
    if include_default_value:
×
NEW
764
        result['-1'] = _('-- Please select --')
×
765

NEW
766
    result.update(OrderedDict({t[1]: _(t[0]) for t in static_lists.ORGANIZATION_TYPE_LIST}))
×
767

UNCOV
768
    return result
×
769

770

771
def _find_last_update_for_orgs(org_names):
1✔
772
    org_to_update_time = {}
1✔
773
    if org_names:
1✔
774
        context = {
×
775
            'model': model,
776
            'session': model.Session
777
        }
778
        filter = 'organization:({}) +dataset_type:dataset'.format(' OR '.join(org_names))
×
779

780
        data_dict = {
×
781
            'q': '',
782
            'fq': filter,
783
            'fq_list': ['{!collapse field=organization nullPolicy=expand sort="metadata_modified desc"} '],
784
            'rows': len(org_names),
785
            'start': 0,
786
            'sort': 'metadata_modified desc'
787
        }
788
        query = get_action('package_search')(context, data_dict)
×
789
        org_to_update_time = {d['organization']['name']: d.get('metadata_modified') for d in query['results']}
×
790
    return org_to_update_time
1✔
791

792

793
def org_add_last_updated_field(displayed_orgs):
1✔
794
    org_to_last_update = _find_last_update_for_orgs([o.get('name') for o in displayed_orgs])
1✔
795
    for o in displayed_orgs:
1✔
796
        o['dataset_last_updated'] = org_to_last_update.get(o['name'], o.get('created'))
×
797

798

799
def hdx_organization_type_get_value(org_type_key):
1✔
800
    return next((org_type[0] for org_type in static_lists.ORGANIZATION_TYPE_LIST if org_type[1] == org_type_key),
×
801
                org_type_key)
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