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

OCHA-DAP / hdx-ckan / #4574

19 Sep 2023 02:18PM UTC coverage: 69.617% (-0.006%) from 69.623%
#4574

push

coveralls-python

danmihaila
HDX-9010 - adding test for adding inactive in title

11200 of 16088 relevant lines covered (69.62%)

0.7 hits per line

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

83.29
/ckanext-hdx_package/ckanext/hdx_package/helpers/custom_validator.py
1
'''
2
Created on Apr 11, 2014
3

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

7
import bisect
1✔
8
import datetime
1✔
9
import logging
1✔
10
import json
1✔
11
import re
1✔
12

13
import six
1✔
14
import ckan.model as model
1✔
15
import ckan.authz as authz
1✔
16
import ckan.plugins.toolkit as tk
1✔
17
import ckan.lib.navl.dictization_functions as df
1✔
18
from ckan.common import _, c
1✔
19

20
import ckanext.hdx_package.helpers.caching as caching
1✔
21
import ckanext.hdx_package.helpers.resource_triggers.geopreview as geopreview
1✔
22

23
from ckanext.hdx_package.helpers.constants import FILE_WAS_UPLOADED, NO_DATA
1✔
24
from ckanext.hdx_package.helpers.date_helper import DaterangeParser
1✔
25
from ckanext.hdx_package.helpers.resource_triggers.fs_check import FS_CHECK_FORMATS
1✔
26

27
missing = df.missing
1✔
28
StopOnError = df.StopOnError
1✔
29
Invalid = df.Invalid
1✔
30
get_action = tk.get_action
1✔
31
check_access = tk.check_access
1✔
32

33
NotAuthorized = tk.NotAuthorized
1✔
34

35
log = logging.getLogger(__name__)
1✔
36

37
_DATASET_PREVIEW_FIRST_RESOURCE = 'first_resource'
1✔
38
_DATASET_PREVIEW_RESOURCE_ID = 'resource_id'
1✔
39
_DATASET_PREVIEW_NO_PREVIEW = 'no_preview'
1✔
40
DATASET_PREVIEW_VALUES_LIST = [_DATASET_PREVIEW_FIRST_RESOURCE, _DATASET_PREVIEW_RESOURCE_ID,
1✔
41
                               _DATASET_PREVIEW_NO_PREVIEW]
42

43

44
# same as not_empty, but ignore whitespaces
45
def not_empty_ignore_ws(key, data, errors, context):
1✔
46
    value = data.get(key)
1✔
47
    if not value or value is missing:
1✔
48
        errors[key].append(_('Missing value'))
1✔
49
        raise StopOnError
1✔
50
    value = value.strip()
1✔
51
    if not value or value is missing:
1✔
52
        errors[key].append(_('Missing value'))
×
53
        raise StopOnError
×
54

55

56
def groups_not_empty(key, data, errors, context):
1✔
57
    """
58
    When creating a package, groups cannot be empty
59
    """
60
    # All the extra logic here is needed to deal with the multi-step wizard used for creating a new dataset
61
    # We need to make sure that the validation only runs at the last step of the wizard
62

63
    # allow_partial_update = context.get('allow_partial_update', False)
64
    # allow_state_change = context.get('allow_state_change', False)
65
    first_phase = False
1✔
66

67
    for data_key, data_value in data.items():
1✔
68
        if data_key[0] == '__extras':
1✔
69
            wizard_phase = data_value.get('_ckan_phase', 'Other')
1✔
70
            if wizard_phase == 'dataset_new_1':
1✔
71
                first_phase = True
×
72
                break
×
73

74
    group_list = caching.cached_group_list()
1✔
75
    country_names = [group['name'] for group in group_list if group.get('name')]
1✔
76
    country_ids = [group['id'] for group in group_list]
1✔
77
    country_names.sort()
1✔
78
    country_ids.sort()
1✔
79

80
    if not first_phase:
1✔
81
        error_msg = _('Missing value')
1✔
82
        problem_appeared = False
1✔
83
        try:
1✔
84
            num_of_groups = max((key[1] for key in data.keys() if key[0] == 'groups')) + 1
1✔
85
        except ValueError as e:
1✔
86
            num_of_groups = 0
1✔
87
            problem_appeared = True
1✔
88

89
        for group_idx in range(0, num_of_groups):
1✔
90
            group_correct = False
1✔
91
            group_id = data.get(('groups', group_idx, 'id'))
1✔
92
            if group_id and _in_sorted_list(group_id, country_ids):
1✔
93
                group_correct = True
1✔
94
            else:
95
                group_name = data.get(('groups', group_idx, 'name'))
1✔
96
                if group_name and _in_sorted_list(group_name, country_names):
1✔
97
                    group_correct = True
1✔
98

99
            if not group_correct:
1✔
100
                error_msg = _('Wrong country code or id')
1✔
101
                problem_appeared = True
1✔
102
                break
1✔
103

104
        if problem_appeared:
1✔
105
            errors[key].append(error_msg)
1✔
106
            raise StopOnError
1✔
107
    return None
1✔
108

109

110
def _in_sorted_list(value, sorted_list):
1✔
111
    index = bisect.bisect_left(sorted_list, value)
1✔
112
    if index != len(sorted_list) and sorted_list[index] == value:
1✔
113
        return True
1✔
114
    return False
1✔
115

116

117
def detect_format(key, data, errors, context):
1✔
118
    '''
119
    resource url should not be empty
120
    '''
121

122
    current_format = data.get(key)
1✔
123
    if not current_format or isinstance(current_format, df.Missing):
1✔
124
        url = data.get((key[0], key[1], 'url'))
1✔
125
        file_format = geopreview.detect_format_from_extension(url)
1✔
126
        if not file_format:
1✔
127
            name = data.get((key[0], key[1], 'name'))
×
128
            file_format = geopreview.detect_format_from_extension(name)
×
129
        if file_format:
1✔
130
            data[key] = file_format
1✔
131
            return file_format
1✔
132
        err_message = "We couldn't determine your file type. If it is a compressed format (zip, etc), please  \
×
133
                      indicate the primary format of the data files inside compressed file."
134
        errors[key].append(_(err_message))
×
135
        raise df.StopOnError()
×
136

137
    return current_format
1✔
138

139

140
def hdx_keep_if_fs_check_format(key, data, errors, context):
1✔
141
    _format = data.get((key[0], key[1], 'format'))
1✔
142
    if _format and _format.lower() in FS_CHECK_FORMATS:
1✔
143
        return data.get(key)
1✔
144
    else:
145
        data.pop(key, None)
1✔
146
        raise df.StopOnError()
1✔
147

148

149
def to_lower(current_value):
1✔
150
    if current_value:
1✔
151
        return current_value.lower()
1✔
152
    return current_value
×
153

154

155
def hdx_show_subnational(key, data, errors, context):
1✔
156
    '''
157
    resource url should not be empty
158
    '''
159

160
    current_value = data.get(key)
1✔
161
    if not current_value or isinstance(current_value, df.Missing):
1✔
162
        data[key] = "0"
1✔
163
        return data[key]
1✔
164
    if current_value in ["true", "True", "1"]:
1✔
165
        data[key] = "1"
1✔
166
        return data[key]
1✔
167
    if current_value in ["false", "False", "0", None]:
1✔
168
        data[key] = "0"
1✔
169
        return data[key]
1✔
170

171
    data[key] = "0"
1✔
172
    return data[key]
1✔
173

174

175
def find_package_creator(key, data, errors, context):
1✔
176
    current_creator = data.get(key)
1✔
177
    if not current_creator:
1✔
178
        user = c.user or c.author
1✔
179
        if user:
1✔
180
            data[key] = user
1✔
181
            current_creator = user
1✔
182

183
    return current_creator
1✔
184

185

186
def hdx_find_package_maintainer(key, data, errors, context):
1✔
187
    try:
1✔
188
        user_obj = model.User.get(data.get(key))
1✔
189
    except Exception as ex:
×
190
        raise df.Invalid(_('Maintainer does not exist. Please add valid user ID'))
×
191

192
    org_id = data.get(('owner_org',))
1✔
193
    if not org_id:
1✔
194
        raise df.Invalid(_('Organizations owner does not exist. Please add an organization ID'))
1✔
195

196
    members = get_action('hdx_member_list')(context, {'org_id': org_id})
1✔
197

198
    if user_obj and ((user_obj.id in members.get('all')) or user_obj.sysadmin):
1✔
199
        data[key] = user_obj.id
1✔
200
        return data[key]
1✔
201
    raise df.Invalid(_('Maintainer does not exist or is not a member of current owner organization.'
1✔
202
                       ' Please add valid user ID'))
203

204

205
def hdx_dataset_preview_validator(key, data, errors, context):
1✔
206
    try:
1✔
207
        dataset_preview = str(data.get(key))
1✔
208
        if dataset_preview and dataset_preview in DATASET_PREVIEW_VALUES_LIST:
1✔
209
            return data[key]
1✔
210
        data[key] = _DATASET_PREVIEW_FIRST_RESOURCE
×
211
    except Exception as ex:
1✔
212
        data[key] = _DATASET_PREVIEW_FIRST_RESOURCE
1✔
213
    return data[key]
1✔
214

215

216
def general_not_empty_if_other_selected(other_key, other_compare_value):
1✔
217
    '''
218

219
    :param other_key: the key of the field that influences this "_other" field. Ex. 'methodology', 'license_id'
220
    :type other_key: str
221
    :param other_compare_value: value of "other_key" field that maked this "_other" field mandatory. Ex. 'Other', 'hdx-other'
222
    :type other_compare_value: str
223
    :return: the validator function
224
    :rtype: not_empty_if_other_selected
225
    '''
226

227
    def not_empty_if_other_selected(key, data, errors, context):
1✔
228
        value = data.get(key)
1✔
229
        other_value = data.get((other_key,))
1✔
230
        if not value and other_value == other_compare_value:
1✔
231
            errors[key].append(_('Missing value'))
×
232
            raise StopOnError
×
233
        elif other_value != other_compare_value:
1✔
234
            del data[key]
1✔
235

236
            # Don't go further in the validation chain. Ex: convert to extras doesn't need to be called
237
            raise StopOnError
1✔
238

239
    return not_empty_if_other_selected
1✔
240

241

242
def hdx_convert_values_to_boolean_for_dataset_preview(key, data, errors, context):
1✔
243
    '''
244
    convert values to boolean and also sets the dataset_preview to false for the other resources
245
    '''
246

247
    value = data.get(key)
1✔
248
    if value in (True, False, 'True', 'False'):
1✔
249
        pass
×
250
    elif value in ('1', 1):
1✔
251
        # set others on False
252
        i = 0
×
253
        while True:
254
            temp_key_name = ('resources', i, 'name')
×
255
            temp_key_preview = ('resources', i, 'dataset_preview_enabled')
×
256
            if not data.get(temp_key_name):
×
257
                break
×
258
            data[temp_key_preview] = False
×
259
            i += 1
×
260
        data[key] = True
×
261

262
    elif value in ('0', 0):
1✔
263
        data[key] = False
×
264
    else:
265
        # value not in ('1',1,'0',0, True, False, 'True', 'False'):
266
        data[key] = None
1✔
267
    return data[key]
1✔
268

269

270
def hdx_convert_list_item_to_extras(key, data, errors, context):
1✔
271
    # Get the current extras index
272
    current_indexes = [k[1] for k in data.keys()
1✔
273
                       if len(k) > 1 and k[0] == 'extras']
274

275
    new_index = max(current_indexes) + 1 if current_indexes else 0
1✔
276

277
    data[('extras', new_index, 'key')] = '__'.join((str(item) for item in key))
1✔
278
    data[('extras', new_index, 'value')] = data[key]
1✔
279

280

281
def hdx_convert_from_extras_to_list_item(key, data, errors, context):
1✔
282
    def remove_from_extras(data, key):
1✔
283
        to_remove = []
1✔
284
        for data_key, data_value in data.items():
1✔
285
            if (data_key[0] == 'extras'
1✔
286
                and data_key[1] == key):
287
                to_remove.append(data_key)
1✔
288
        for item in to_remove:
1✔
289
            del data[item]
1✔
290

291
    keys_to_remove = []
1✔
292
    key_value_to_add = []
1✔
293

294
    for data_key, data_value in data.items():
1✔
295
        if isinstance(data_value, six.string_types):
1✔
296
            data_value_parts = data_value.split('__')  # Example: ['customviz', 0, 'url']
1✔
297
            key_parts = key[-1].split('__')  # Example ['customviz', 'url']
1✔
298
            if data_key[0] == 'extras' and data_key[-1] == 'key' \
1✔
299
                and len(data_value_parts) == 3 and len(key_parts) == 2 \
300
                and data_value_parts[0] == key_parts[0] and data_value_parts[2] == key_parts[1]:
301
                list_name = key_parts[0]
1✔
302
                property_name = key_parts[1]
1✔
303

304
                # current_indexes = [k[1] for k in data.keys()
305
                #                    if len(k) == 3 and k[0] == list_name and k[2] == property_name]
306
                # index = max(current_indexes) + 1 if current_indexes else 0
307

308
                key_value_to_add.append({
1✔
309
                    'key': (list_name, data_key[1], property_name),
310
                    'value': data[('extras', data_key[1], 'value')]
311
                })
312
                keys_to_remove.append(data_key[1])
1✔
313

314
    for key_val in key_value_to_add:
1✔
315
        data[key_val['key']] = key_val['value']
1✔
316

317
    for k in keys_to_remove:
1✔
318
        remove_from_extras(data, k)
1✔
319

320

321
def hdx_boolean_string_converter(value, context):
1✔
322
    '''
323
    Return a boolean for value.
324
    Return value when value is a python bool type.
325
    Return True for strings 'true', 'yes', 't', 'y', and '1'.
326
    Return False in all other cases, including when value is an empty string or
327
    None
328
    '''
329
    if value is missing or value is None:
1✔
330
        return "false"
1✔
331
    if isinstance(value, bool):
1✔
332
        return "true" if value else "false"
1✔
333
    if value.lower() in ['true', 'yes', 't', 'y', '1']:
1✔
334
        return "true"
1✔
335
    return "false"
×
336

337

338
def hdx_assume_missing_is_true(value, context):
1✔
339
    if value is missing or value is None:
1✔
340
        return "true"
1✔
341
    return value
1✔
342

343

344
def hdx_isodate_to_string_converter(value, context):
1✔
345
    if isinstance(value, datetime.datetime):
1✔
346
        return value.isoformat()
1✔
347
    return None
1✔
348

349

350
def reset_on_file_upload(key, data, errors, context):
1✔
351
    resource_id = data.get(key[:-1] + ('id',))
1✔
352
    if resource_id and resource_id in context.get(FILE_WAS_UPLOADED, set()):
1✔
353
        data.pop(key, None)
1✔
354

355

356
def hdx_resource_keep_prev_value_unless_sysadmin(key, data, errors, context):
1✔
357
    '''
358
    By default, this should inject the value from the previous version.
359
    The exception is if the user is a sysadmin, then the new value is used.
360
    '''
361

362
    if data[key] is missing:
×
363
        data.pop(key, None)
×
364

365
    user = context.get('user')
×
366
    ignore_auth = context.get('ignore_auth')
×
367
    allowed_to_change = ignore_auth or (user and authz.is_sysadmin(user))
×
368

369
    if not allowed_to_change:
×
370
        data.pop(key, None)
×
371
        resource_id = data.get(key[:-1] + ('id',))
×
372
        package_id = data.get(('id',))
×
373
        if resource_id:
×
374
            specific_key = key[2]
×
375
            context_key = 'resource_' + resource_id
×
376
            resource_dict = context.get(context_key)
×
377
            if not resource_dict:
×
378
                resource_dict = __get_previous_resource_dict(context, package_id, resource_id)
×
379
                context[context_key] = resource_dict
×
380
            if resource_dict:
×
381
                old_value = resource_dict.get(specific_key)
×
382
                if old_value is not None:
×
383
                    data[key] = old_value
×
384

385
    if key not in data:
×
386
        raise StopOnError
×
387

388

389
# def hdx_update_field_if_value_wrapper(context_field, value):
390
#     def hdx_update_field_if_value(key, data, errors, context):
391
#         a = 10
392
#
393
#     return hdx_update_field_if_value
394
def hdx_update_microdata(key, data, errors, context):
1✔
395
    if not data.get(key):
1✔
396
        pkg_id = data.get(('id',))
1✔
397
        if pkg_id:
1✔
398
            # pkg_dict = __get_previous_package_dict(context, pkg_id)
399
            # if pkg_dict.get('resources', [])[key[1]].get('in_quarantine') and not data.get(key):
400
            #     data[key[:2] + ('microdata',)] = False
401
            res_id = data.get(key[:-1] + ('id',))
1✔
402
            if res_id:
1✔
403
                # resource exists
404
                res_dict = __get_previous_resource_dict(context, pkg_id, res_id)
1✔
405
                if res_dict and res_dict.get('in_quarantine'):
1✔
406
                    data[key[:2] + ('microdata',)] = False
1✔
407

408

409
def hdx_update_in_quarantine_by_microdata(key, data, errors, context):
1✔
410
    if data.get(key):
1✔
411
        pkg_id = data.get(('id',))
1✔
412
        if pkg_id:
1✔
413
            res_id = data.get(key[:-1] + ('id',))
1✔
414
            if res_id:
1✔
415
                # resource exists
416
                res_dict = __get_previous_resource_dict(context, pkg_id, res_id)
1✔
417
                # check if previous value was not microdata
418
                if res_dict and not res_dict.get('microdata'):
1✔
419
                    data[key[:2] + ('in_quarantine',)] = True
1✔
420
            # new resource will be put in quarantine
421
            else:
422
                data[key[:2] + ('in_quarantine',)] = True
×
423
        # if new package, resource will be in quarantine
424
        else:
425
            data[key[:2] + ('in_quarantine',)] = True
1✔
426

427

428
def hdx_update_data_frequency_by_archived(key, data, errors, context):
1✔
429
    if data.get(key) and data.get(key) == 'true':
1✔
430
        data[(u'data_update_frequency',)] = '-1'
1✔
431

432

433
def hdx_add_update_fs_check_info(key, data, errors, context):
1✔
434
    try:
1✔
435
        fs_check_value = data.get(key)
1✔
436

437
        # if fs_check_value is dict, then is a new value coming and we need to process it.
438
        if fs_check_value and isinstance(fs_check_value, dict):
1✔
439
            if context.get('allow_fs_check_field'):
1✔
440
                pkg_id = data.get(('id',))
1✔
441
                resource_id = data.get(key[:-1] + ('id',))
1✔
442

443
                # resource update
444
                if resource_id:
1✔
445
                    resource_dict = __get_previous_resource_dict(context, pkg_id, resource_id) or {}
1✔
446
                    specific_key = key[2]
1✔
447
                    old_value = resource_dict.get(specific_key)
1✔
448

449
                    # if fs_check_info exists, then append new value
450
                    if old_value is not None:
1✔
451
                        if isinstance(old_value, str):
1✔
452
                            old_value = json.loads(old_value.replace('\'', '"'))
1✔
453
                        if not isinstance(old_value, list):
1✔
454
                            old_value = [old_value]
×
455
                        old_value.append(fs_check_value)
1✔
456
                    else:
457
                        old_value = [fs_check_value]
1✔
458
                    data[key] = old_value[-10:]
1✔
459

460
                # resource create
461
                else:
462
                    # if not isinstance(data[key], list):
463
                    data[key] = [data[key]]
1✔
464
            # else:
465
            #     # loads current value as py object to allow the next validator to use the value
466
            #     if isinstance(data.get(key), str):
467
            #         try:
468
            #             data[key] = json.loads(data[key].replace('\'', '"'))
469
            #         except:
470
            #             log.error("fs_check_info contains a strange string: " + str(data[key]))
471

472
            log.info("done with add update fs_check_info")
1✔
473
    except Exception as ex:
×
474
        log.info(ex)
×
475

476

477
def hdx_package_keep_prev_value_unless_field_in_context_wrapper(context_field, resource_level=False):
1✔
478
    def hdx_package_keep_prev_value_unless_field_in_context(key, data, errors, context):
1✔
479
        '''
480
        By default, this should inject the value from the previous version.
481
        The exception is if the 'context_field' key is in the context.
482
        NOTE, we don't check whether user is sysadmin. The api action that set the
483
        'context_field' should do any checks.
484
        '''
485

486
        if data[key] is missing:
1✔
487
            data.pop(key, None)
1✔
488

489
        allow_context_field = context.get(context_field)
1✔
490

491
        if not allow_context_field:
1✔
492
            data.pop(key, None)
1✔
493
            pkg_id = data.get(('id',))
1✔
494

495
            if resource_level:
1✔
496
                resource_id = data.get(key[:-1] + ('id',))
1✔
497
                if resource_id:
1✔
498
                    resource_dict = __get_previous_resource_dict(context, pkg_id, resource_id) or {}
1✔
499
                    specific_key = key[2]
1✔
500
                    old_value = resource_dict.get(specific_key)
1✔
501
                    if old_value is not None:
1✔
502
                        data[key] = old_value
1✔
503

504
            # package level
505
            else:
506
                if pkg_id:
1✔
507
                    pkg_dict = __get_previous_package_dict(context, pkg_id)
1✔
508
                    old_value = pkg_dict.get(key[0], None)
1✔
509
                    if old_value is not None:
1✔
510
                        data[key] = old_value
1✔
511
        if key not in data:
1✔
512
            raise StopOnError
1✔
513

514
    return hdx_package_keep_prev_value_unless_field_in_context
1✔
515

516

517
def hdx_keep_prev_value_if_empty(key, data, errors, context):
1✔
518
    new_value = data.get(key)
1✔
519
    if new_value is missing or (not new_value and new_value is not False):  # False is not an empty value
1✔
520
        data.pop(key, None)
1✔
521
        pkg_id = data.get(('id',))
1✔
522
        if pkg_id:
1✔
523
            prev_package_dict = __get_previous_package_dict(context, pkg_id)
1✔
524
            old_value = prev_package_dict.get(key[0], None)
1✔
525
            if old_value:
1✔
526
                data[key] = old_value
1✔
527

528
    if isinstance(new_value, six.string_types) and not new_value.strip():
1✔
529
        data.pop(key, None)
1✔
530

531
    if key not in data:
1✔
532
        raise StopOnError
1✔
533

534

535
def hdx_delete_unless_field_in_context(context_field):
1✔
536
    '''
537
    :param context_field: the field in the context which tells us if it's ok to allow the value through
538
    :type context_field: str
539
    :return:
540
    :rtype: function
541
    '''
542

543
    def hdx_delete_unless_forced(key, data, errors, context):
1✔
544
        if not context.get(context_field):
1✔
545
            data.pop(key, None)
1✔
546

547
    return hdx_delete_unless_forced
1✔
548

549

550
def hdx_delete_unless_authorized_wrapper(auth_function):
1✔
551
    '''
552
    :param auth_function: the auth function to run through check_access()
553
    :type auth_function: str
554
    :return:
555
    :rtype: function
556
    '''
557

558
    def hdx_delete_unless_authorized(key, data, errors, context):
1✔
559
        try:
1✔
560
            check_access(auth_function, context, None)
1✔
561
        except NotAuthorized as e:
1✔
562
            data.pop(key, None)
1✔
563

564
    return hdx_delete_unless_authorized
1✔
565

566

567
def hdx_delete_if_marked_with_no_data(key, data, errors, context):
1✔
568
    if data.get(key) == NO_DATA:
×
569
        data.pop(key, None)
×
570

571

572
def hdx_value_in_list_wrapper(allowed_values, allow_missing):
1✔
573
    def hdx_value_in_list(key, data, errors, context):
1✔
574
        value = data[key]
1✔
575
        value_is_missing = not value or value is missing
1✔
576
        if not allow_missing and value_is_missing:
1✔
577
            raise Invalid(_('Value is missing'))
×
578
        if not value_is_missing and value not in allowed_values:
1✔
579
            raise Invalid(_('Value not in allowed list'))
×
580

581
    return hdx_value_in_list
1✔
582

583

584
def hdx_daterange_possible_infinite_end(key, data, errors, context):
1✔
585
    value = data.get(key)  # type: str
1✔
586
    new_value = DaterangeParser(value).compute_daterange_string(False)
1✔
587
    data[key] = new_value
1✔
588

589

590
def hdx_daterange_possible_infinite_end_dataset_date(key, data, errors, context):
1✔
591
    value = data.get(key)  # type: str
1✔
592
    new_value = DaterangeParser(value).compute_daterange_string(False, end_date_ending=True)
1✔
593
    data[key] = new_value
1✔
594

595

596
def hdx_convert_old_date_to_daterange(key, data, errors, context):
1✔
597
    value = data[key]
1✔
598
    if value and '[' in value and ']' in value and ' TO ' in value:
1✔
599
        return
1✔
600
    try:
1✔
601
        if value:
1✔
602
            dates_list = value.split('-')
1✔
603
            if dates_list:
1✔
604
                start_date = datetime.datetime.strptime(dates_list[0].strip(), '%m/%d/%Y')
1✔
605
                if len(dates_list) == 2:
1✔
606
                    end_date = datetime.datetime.strptime(dates_list[1].strip(), '%m/%d/%Y')
1✔
607
                else:
608
                    end_date = start_date
1✔
609
                data[key] = "[{start_date}T00:00:00 TO {end_date}T23:59:59]".format(
1✔
610
                    start_date=start_date.strftime("%Y-%m-%d"),
611
                    end_date=end_date.strftime("%Y-%m-%d"))
612
    except TypeError as e:
1✔
613
        raise df.Invalid(_('Invalid old HDX date format MM/DD/YYYY. Please use [start_datetime TO end_datetime]'))
×
614
    except ValueError as e:
1✔
615
        raise df.Invalid(_('Invalid old HDX date format MM/DD/YYYY. Please use [start_datetime TO end_datetime]'))
1✔
616

617

618
def hdx_convert_to_json_string(key, data, errors, context):
1✔
619
    value = data[key]
1✔
620
    try:
1✔
621
        data[key] = json.dumps(value)
1✔
622
    except TypeError as e:
×
623
        raise df.Invalid(_('Input is not valid'))
×
624

625

626
def hdx_convert_to_json_string_if_not_string(key, data, errors, context):
1✔
627
    value = data[key]
1✔
628
    if value and not isinstance(value, str):
1✔
629
        return hdx_convert_to_json_string(key, data, errors, context)
1✔
630

631

632
def hdx_convert_from_json_string(key, data, errors, context):
1✔
633
    value = data[key]
1✔
634
    try:
1✔
635
        data[key] = json.loads(value)
1✔
636
    except (ValueError, TypeError) as e:
×
637
        raise df.Invalid(_('Could not parse JSON'))
×
638

639

640
# def hdx_autocompute_grouping(key, data, errors, context):
641
#     current_value = data.get(key)
642
#     if not current_value or current_value is missing:
643
#         daterange_value = data.get(key[:-1] + ('daterange_for_data',))
644
#         if daterange_value and daterange_value is not missing:
645
#             daterange_parser = DaterangeParser(daterange_value)
646
#             data[key] = daterange_parser.human_readable()
647

648
def hdx_float_number(key, data, errors, context):
1✔
649
    value = data[key]
×
650
    try:
×
651
        data[key] = float(value)
×
652
    except (ValueError, TypeError) as e:
×
653
        raise df.Invalid(_('Input is not a float valid number'))
×
654

655

656
def __get_previous_resource_dict(context, package_id, resource_id):
1✔
657
    dataset_dict = __get_previous_package_dict(context, package_id)
1✔
658
    return next((r for r in dataset_dict.get('resources', []) if r['id'] == resource_id), None)
1✔
659

660

661
def __get_previous_package_dict(context, id):
1✔
662
    context_key = 'hdx_prev_package_dict_' + id
1✔
663
    pkg_dict = context.get(context_key)
1✔
664
    if not pkg_dict:
1✔
665
        pkg_dict = get_action('package_show')(context, {'id': id})
1✔
666
        context[context_key] = pkg_dict
1✔
667

668
    return pkg_dict or {}
1✔
669

670

671
def hdx_resources_not_allowed_if_requested_data(key, data, errors, context):
1✔
672
    if data[key] and ((u'resources', 0, 'url') in data or (u'resources', 0, 'name') in data):
1✔
673
        raise df.Invalid(_('By request - HDX Connect datasets can not store resources'))
1✔
674

675

676
DATASERIES_TITLE_PATTERN = re.compile('^[\w ,-]+$', re.UNICODE)
1✔
677
def hdx_dataseries_title_validator(value, context):
1✔
678
    if value:
×
679
        if not DATASERIES_TITLE_PATTERN.match(value):
×
680
            raise Invalid(_('Dataseries title is not valid'))
×
681
    return value
×
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