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

OCHA-DAP / hdx-ckan / #5935

05 Dec 2024 10:26AM UTC coverage: 74.957% (+0.07%) from 74.883%
#5935

Pull #6496

coveralls-python

danmihaila
HDX-10398 add testing
Pull Request #6496: HDX-10398 export data completeness and rename "completness"

54 of 65 new or added lines in 4 files covered. (83.08%)

62 existing lines in 3 files now uncovered.

12562 of 16759 relevant lines covered (74.96%)

0.75 hits per line

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

83.22
/ckanext-hdx_package/ckanext/hdx_package/views/dataset.py
1
import csv
1✔
2
import io
1✔
3
import json
1✔
4
import logging
1✔
5
import re
1✔
6

7
from flask import Blueprint, make_response
1✔
8
from flask.views import MethodView
1✔
9
from typing import Any, Optional, Union
1✔
10
from six import text_type
1✔
11

12
from ckan.types import Response
1✔
13
from ckan.lib.mailer import MailerException
1✔
14

15
import ckan.authz as authz
1✔
16
import ckan.lib.captcha as captcha
1✔
17
import ckan.lib.navl.dictization_functions as dictization_functions
1✔
18
import ckan.model as model
1✔
19
import ckan.plugins.toolkit as tk
1✔
20

21
import ckanext.hdx_package.helpers.analytics as analytics
1✔
22
import ckanext.hdx_package.helpers.custom_pages as cp_h
1✔
23
import ckanext.hdx_package.helpers.custom_validator as vd
1✔
24
import ckanext.hdx_package.helpers.membership_data as membership_data
1✔
25
import ckanext.hdx_search.helpers.search_history as search_history
1✔
26
import ckanext.hdx_package.controller_logic.dataset_view_logic as dataset_view_logic
1✔
27
from ckanext.hdx_package.controller_logic.dataset_contact_contributor_logic import DatasetContactContributorLogic
1✔
28
from ckanext.hdx_package.controller_logic.dataset_request_access_logic import DatasetRequestAccessLogic
1✔
29
from ckanext.hdx_users.controller_logic.notification_platform_logic import verify_unsubscribe_token
1✔
30

31
from ckan.views.dataset import _setup_template_variables
1✔
32

33
from ckanext.hdx_package.helpers import resource_grouping
1✔
34
from ckanext.hdx_package.helpers.constants import PACKAGE_METADATA_FIELDS_MAP, RESOURCE_METADATA_FIELDS_MAP
1✔
35
from ckanext.hdx_package.helpers.membership_data import contributor_topics
1✔
36
from ckanext.hdx_package.helpers.helpers import filesize_format
1✔
37
from ckanext.hdx_package.helpers.util import find_approx_download
1✔
38
from ckanext.hdx_package.views.light_dataset import generic_search
1✔
39
from ckanext.hdx_theme.helpers.helpers import markdown_extract_strip
1✔
40
from ckanext.hdx_theme.util.jql import fetch_downloads_per_week_for_dataset
1✔
41
from ckanext.hdx_theme.util.light_redirect import check_redirect_needed
1✔
42

43
from ckanext.hdx_org_group.views.organization_join import set_custom_rect_logo_url
1✔
44
from ckanext.hdx_users.helpers.notification_platform import check_notifications_enabled_for_dataset
1✔
45

46
log = logging.getLogger(__name__)
1✔
47

48
config = tk.config
1✔
49
get_action = tk.get_action
1✔
50
check_access = tk.check_access
1✔
51
request = tk.request
1✔
52
render = tk.render
1✔
53
abort = tk.abort
1✔
54
redirect = tk.redirect_to
1✔
55
_ = tk._
1✔
56
h = tk.h
1✔
57
g = tk.g
1✔
58

59
NotAuthorized = tk.NotAuthorized
1✔
60
NotFound = tk.ObjectNotFound
1✔
61
ValidationError = tk.ValidationError
1✔
62

63
hdx_dataset = Blueprint(u'hdx_dataset', __name__, url_prefix=u'/dataset')
1✔
64
hdx_search = Blueprint(u'hdx_search', __name__, url_prefix=u'/search')
1✔
65

66

67
@check_redirect_needed
1✔
68
def search():
1✔
69
    query_string = request.args.get('q', u'')
1✔
70
    if g.userobj and query_string:
1✔
71
        search_history.store_search(query_string, g.userobj.id)
1✔
72
    return generic_search(u'search/search.html')
1✔
73

74

75
@check_redirect_needed
1✔
76
def read(id):
1✔
77
    """
78
    Display the package, includes HDX additions for continuous browsing
79
    """
80

81
    context = {'model': model, 'session': model.Session,
1✔
82
               'user': g.user, 'for_view': True,
83
               'auth_user_obj': g.userobj}
84
    data_dict = {'id': id, 'include_tracking': True}
1✔
85

86
    # check if package exists
87
    try:
1✔
88
        pkg_dict = get_action('package_show')(context, data_dict)
1✔
89
        pkg = context['package']
1✔
90

91
        showcase_list = []
1✔
92
        # Needed because of showcase validation convert_package_name_or_id_to_id_for_type_dataset()
93
        current_pkg_type = pkg_dict.get('type')
1✔
94

95
        if current_pkg_type == 'dataset':
1✔
96
            context_showcase = {'model': model, 'session': model.Session,
1✔
97
                       'user': g.user, 'for_view': True,
98
                       'auth_user_obj': g.userobj}
99
            _showcase_list = get_action('ckanext_package_showcase_list')(context_showcase,
1✔
100
                                                                         {'package_id': pkg_dict['id']})
101
            if _showcase_list:
1✔
102
                showcase_list = sorted(_showcase_list, key=lambda i: i.get('metadata_modified'), reverse=True)
×
103
            pkg_dict['showcase_count'] = len(_showcase_list)
1✔
104
        else:
105
            return abort(404, _('Package type is not dataset'))
×
106
    except (NotFound, NotAuthorized):
×
107
        return abort(404, _('Dataset not found'))
×
108

109
    log.debug('Reading dataset {}: checking which resources can be previewed'.format(pkg_dict.get('name')))
1✔
110
    # can the resources be previewed?
111
    for resource in pkg_dict['resources']:
1✔
112
        resource_views = [] if resource.get('in_quarantine') is True else get_action('resource_view_list')(context, {
1✔
113
            'id': resource['id']})
114
        resource['has_views'] = bool(_find_default_view(resource, resource_views))
1✔
115
        resource['resource_views'] = resource_views
1✔
116

117
        # if helpers.is_ckan_domain(resource['url']):
118
        #     resource['url'] = helpers.make_url_relative(resource['url'])
119
        #
120
        # if resource.get('perma_link') and helpers.is_ckan_domain(resource['perma_link']):
121
        #     resource['perma_link'] = helpers.make_url_relative(resource['perma_link'])
122

123
    # dealing with resource grouping
124
    resource_grouping.set_show_groupings_flag(pkg_dict)
1✔
125
    if pkg_dict.get('x_show_grouping'):
1✔
126
        resource_grouping.add_other_grouping_if_needed(pkg_dict)
×
127

128
    package_type = pkg_dict['type'] or 'dataset'
1✔
129
    _setup_template_variables(context, {'id': id}, package_type=package_type)
1✔
130

131
    # package_saver.PackageSaver().render_package(c.pkg_dict, context)
132

133
    log.debug('Reading dataset {}: setting analytics data for template'.format(pkg_dict.get('name')))
1✔
134
    # set dataset type for google analytics - modified by HDX
135
    analytics_is_cod = analytics.is_cod(pkg_dict)
1✔
136
    # c.analytics_is_indicator = analytics.is_indicator(c.pkg_dict)
137
    analytics_is_archived = analytics.is_archived(pkg_dict)
1✔
138
    analytics_group_names, analytics_group_ids = analytics.extract_locations_in_json(pkg_dict)
1✔
139
    analytics_dataset_availability = analytics.dataset_availability(pkg_dict)
1✔
140
    analytics_came_from = analytics.came_from(request.args)
1✔
141
    analytics_supports_notifications = analytics.supports_notifications(pkg_dict)
1✔
142

143
    # changes done for indicator
144
    act_data_dict = {'id': pkg_dict['id'], 'limit': 7}
1✔
145
    log.debug('Reading dataset {}: getting activity list for dataset'.format(pkg_dict.get('name')))
1✔
146

147
    hdx_activities = get_action(u'package_activity_list')(context, act_data_dict)
1✔
148

149
    pkg_dict['approx_total_downloads'] = find_approx_download(pkg_dict.get('total_res_downloads', 0))
1✔
150

151
    # Constructing the email body
152
    log.debug('Reading dataset {}: constructing email body'.format(pkg_dict.get('name')))
1✔
153
    notes = pkg_dict.get('notes') if pkg_dict.get('notes') else _('No description available')
1✔
154
    pkg_dict['social_mail_body'] = _('Description:%0D%0A') + h.markdown_extract(
1✔
155
        notes) + ' %0D%0A'
156

157
    membership = membership_data.get_membership_by_user(g.user or g.author, pkg.owner_org, g.userobj)
1✔
158

159
    user_has_edit_rights = h.check_access('package_update', {'id': pkg_dict['id']})
1✔
160

161
    # analytics charts
162
    log.debug('Reading dataset {}: getting data for analytics charts'.format(pkg_dict.get('name')))
1✔
163
    downloads_last_weeks = fetch_downloads_per_week_for_dataset(pkg_dict['id']).values()
1✔
164
    stats_downloads_last_weeks = list(downloads_last_weeks)
1✔
165

166
    # tags&custom_pages
167
    log.debug('Reading dataset {}: finding custom page list for this dataset'.format(pkg_dict.get('name')))
1✔
168
    pkg_dict['page_list'] = cp_h.hdx_get_page_list_for_dataset(context, pkg_dict)
1✔
169

170
    # links to vizualizations
171
    log.debug('Reading dataset {}: finding links list for this dataset'.format(pkg_dict.get('name')))
1✔
172
    pkg_dict['links_list'] = get_action('hdx_package_links_by_id_list')(context, {'id': pkg_dict.get('name')})
1✔
173

174
    log.debug('Reading dataset {}: deciding on the dataset visualization/preview'.format(pkg_dict.get('name')))
1✔
175
    _dataset_preview = None
1✔
176
    if 'resources' in pkg_dict:
1✔
177
        _dataset_preview = pkg_dict.get('dataset_preview', vd._DATASET_PREVIEW_FIRST_RESOURCE)
1✔
178

179
    org_dict = pkg_dict.get('organization') or {}
1✔
180
    org_id = org_dict.get('id', None)
1✔
181
    org_info_dict = _get_org_extras(org_id)
1✔
182
    user_survey_url = org_info_dict.get('user_survey_url')
1✔
183
    pkg_dict['user_survey_url'] = user_survey_url
1✔
184
    if org_info_dict.get('custom_org', False):
1✔
185
        logo_config = _process_customizations(org_info_dict.get('customization', None))
×
186
    else:
187
        logo_config = {}
1✔
188

189
    # notification platform
190
    supports_notifications = check_notifications_enabled_for_dataset(pkg_dict['id'])
1✔
191
    unsubscribe_token = request.args.get('unsubscribe_token', None)
1✔
192
    if unsubscribe_token:
1✔
UNCOV
193
        try:
×
194
            unsubscribe_token = verify_unsubscribe_token(unsubscribe_token, inactivate=False)
×
195
        except Exception as e:
×
196
            unsubscribe_token = None
×
197
            h.flash_error('Your token is invalid or has expired.')
×
198

199
    template_data = {
1✔
200
        'pkg_dict': pkg_dict,
201
        'pkg': pkg,
202
        'showcase_list': showcase_list,
203
        'hdx_activities': hdx_activities,
204
        'membership': membership,
205
        'user_has_edit_rights': user_has_edit_rights,
206
        'unsubscribe_token': unsubscribe_token,
207
        'analytics_is_cod': analytics_is_cod,
208
        'analytics_is_indicator': 'false',
209
        'analytics_is_archived': analytics_is_archived,
210
        'analytics_group_names': analytics_group_names,
211
        'analytics_group_ids': analytics_group_ids,
212
        'analytics_dataset_availability': analytics_dataset_availability,
213
        'analytics_came_from': analytics_came_from,
214
        'analytics_supports_notifications': analytics_supports_notifications,
215
        'stats_downloads_last_weeks': stats_downloads_last_weeks,
216
        'user_survey_url': user_survey_url,
217
        'logo_config': logo_config,
218
        'supports_notifications': supports_notifications,
219
    }
220

221
    if _dataset_preview != vd._DATASET_PREVIEW_NO_PREVIEW:
1✔
222
        view_enabled_resources = [r for r in pkg_dict['resources'] if
1✔
223
                                  r.get('no_preview') != 'true' and r.get('in_quarantine') is not True]
224
        dataset_preview_enabled_list = []
1✔
225
        dataset_preview_disabled_list = []
1✔
226
        if _dataset_preview == vd._DATASET_PREVIEW_RESOURCE_ID:
1✔
UNCOV
227
            for r in view_enabled_resources:
×
UNCOV
228
                if r.get('dataset_preview_enabled') is True:
×
UNCOV
229
                    dataset_preview_enabled_list.append(r)
×
230
                else:
231
                    dataset_preview_disabled_list.append(r)
×
232
            dataset_preview_enabled_list.extend(dataset_preview_disabled_list)
×
233
            view_enabled_resources = dataset_preview_enabled_list
×
234
        for r in view_enabled_resources:
1✔
235
            _res_view = _check_resource(r)
1✔
236
            if _res_view is None:
1✔
237
                continue
1✔
UNCOV
238
            if _res_view.get('type') == 'hdx_geo_preview':
×
UNCOV
239
                template_data['shapes'] = json.dumps(
×
240
                    dataset_view_logic.process_shapes(pkg_dict['resources'], r.get('id')))
UNCOV
241
                return render('package/hdx-read-shape.html', template_data)
×
242
            if _res_view.get('type') == 'hdx_hxl_preview':
×
243
                template_data['default_view'] = _res_view
×
UNCOV
244
                has_modify_permission = authz.is_authorized_boolean('package_update', context, {'id': pkg_dict['id']})
×
245
                template_data['hxl_preview_urls'] = {
×
246
                    'onlyView': get_action('hxl_preview_iframe_url_show')({
247
                        'has_modify_permission': has_modify_permission
248
                    }, {
249
                        'resource': _res_view.get('resource'),
250
                        'resource_view': _res_view.get('view'),
251
                        'hxl_preview_mode': 'onlyView'
252
                    })
253
                    # 'edit': get_action('hxl_preview_iframe_url_show')({}, {
254
                    #     'resource': _default_view.get('resource'),
255
                    #     'resource_view': _default_view.get('view'),
256
                    #     'hxl_preview_mode': 'edit'
257
                    # })
258
                }
UNCOV
259
                return render('package/hdx-read-hxl.html', template_data)
×
260

261
    log.debug('Reading dataset {}: rendering template'.format(pkg_dict.get('name')))
1✔
262
    if org_info_dict.get('custom_org', False):
1✔
263
        return render('package/custom_hdx_read.html', template_data)
×
264
    return render('package/hdx_read.html', template_data)
1✔
265

266

267
def _get_org_extras(org_id):
1✔
268
    """
269
    Get the extras for our orgs
270
    """
271
    if not org_id:
1✔
UNCOV
272
        return {}
×
273
    context = {'model': model, 'session': model.Session,
1✔
274
               'user': g.user or g.author,
275
               'include_datasets': False,
276
               'for_view': True}
277
    data_dict = {'id': org_id}
1✔
278
    org_info = get_action(
1✔
279
        'hdx_light_group_show')(context, data_dict)
280

281
    extras_dict = {item['key']: item['value'] for item in org_info.get('extras', {})}
1✔
282
    extras_dict['image_url'] = org_info.get('image_url', None)
1✔
283

284
    return extras_dict
1✔
285

286

287
def _process_customizations(json_string):
1✔
288
    """
289
    Process settings for datasets belonging to custom layouts
290
    """
UNCOV
291
    logo_config = {
×
292
        'logo_bg_color': '',
293
        'highlight_color': '',
294
        'custom_org': True
295
    }
UNCOV
296
    if json_string:
×
UNCOV
297
        custom_dict = json.loads(json_string)
×
UNCOV
298
        highlight_color = custom_dict.get('highlight_color', None)
×
UNCOV
299
        if highlight_color:
×
300
            logo_config['highlight_color'] = highlight_color
×
301
        logo_bg_color = custom_dict.get('logo_bg_color', None)
×
302
        if logo_bg_color:
×
303
            logo_config['logo_bg_color'] = logo_bg_color
×
304
        image_name = custom_dict.get('image_rect', None)
×
305
        if image_name:
×
306
            logo_config['image_url'] = h.url_for('hdx_local_image_server.org_file', filename=image_name)
×
307

308
    return logo_config
×
309

310

311
def _find_default_view(resource, resource_views):
1✔
312
    default_resource_view = resource_views[0] if resource_views else None
1✔
313
    res_format = (resource.get('format', None) or '').lower()
1✔
314
    if 'xlsx' in res_format:
1✔
UNCOV
315
        default_resource_view = next(
×
316
            (rv for rv in resource_views if rv.get('view_type') == 'hdx_hxl_preview'),
317
            default_resource_view)
318

319
    return default_resource_view
1✔
320

321

322
def _check_resource(resource):
1✔
323
    shape_info = dataset_view_logic.has_shape_info(resource)
1✔
324
    if shape_info:
1✔
UNCOV
325
        return shape_info
×
326
    hxl_preview = _has_hxl_views(resource)
1✔
327
    if hxl_preview:
1✔
UNCOV
328
        return hxl_preview
×
329
    return None
1✔
330

331

332
def _has_hxl_views(resource):
1✔
333
    for view in resource.get("resource_views"):
1✔
334
        if view.get("view_type") == 'hdx_hxl_preview':
1✔
UNCOV
335
            return {
×
336
                'type': 'hdx_hxl_preview',
337
                "view_url": h.url_for("resource_view", id=view.get('package_id'),
338
                                      resource_id=view.get('resource_id'), view_id=view.get('id')),
339
                "view": view,
340
                "resource": resource
341
            }
342
    return None
1✔
343

344

345
def delete(id):
1✔
346
    """
347
    Delete package, HDX changed the redirection point
348
    """
349
    if 'cancel' in request.params:
1✔
UNCOV
350
        h.redirect_to(controller='package', action='edit', id=id)
×
351

352
    context = {'model': model, 'session': model.Session,
1✔
353
               'user': g.user or g.author, 'auth_user_obj': g.userobj}
354

355
    try:
1✔
356
        check_access('package_delete', context, {'id': id})
1✔
UNCOV
357
    except NotAuthorized:
×
UNCOV
358
        return abort(403, _('Unauthorized to delete package %s') % '')
×
359

360
    try:
1✔
361
        if request.method == 'POST':
1✔
362
            get_action('hdx_dataset_purge')(context, {'id': id})  # Create new action to fully delete
1✔
363
            h.flash_notice(_('Dataset has been deleted.'))
1✔
364
            return h.redirect_to('dashboard.datasets')
1✔
UNCOV
365
        pkg_dict = get_action('package_show')(context, {'id': id})
×
366
        # dataset_type = pkg_dict['type'] or 'dataset'
UNCOV
367
    except NotAuthorized:
×
UNCOV
368
        return abort(403, _('Unauthorized to delete package %s') % '')
×
369
    except NotFound:
×
UNCOV
370
        return abort(404, _('Dataset not found'))
×
371
    return render('package/confirm_delete.html', {'pkg_dict': pkg_dict})
×
372

373

374
def package_metadata(id):
1✔
375
    '''
376
        Handles downloading .CSV and .JSON package metadata files
377

378
        :returns: json or csv file
379
    '''
380

381
    context = {
1✔
382
        'model': model,
383
        'session': model.Session,
384
        'user': g.user or g.author,
385
        'auth_user_obj': g.userobj
386
    }
387

388
    # check if package exists
389
    try:
1✔
390
        pkg_dict = get_action('package_show')(context, {'id': id})
1✔
391

392
        metadata_fields = PACKAGE_METADATA_FIELDS_MAP
1✔
393
        metadata_resource_fields = RESOURCE_METADATA_FIELDS_MAP
1✔
394

395
        # limit fields
396
        metadata = {field: pkg_dict[field] if field in pkg_dict else None for field, field_data in
1✔
397
                    metadata_fields.items()}
398

399
        file_format = request.params.get('format', '')
1✔
400
        filename = 'metadata-%s' % metadata.get('name')
1✔
401

402
        # add resources
403
        metadata['resources'] = []
1✔
404
        for r in pkg_dict['resources']:
1✔
405
            output_resource_dict = {field: r[field] if field in r else None for field, field_data in
1✔
406
                                    metadata_resource_fields.items()}
407
            # extra processing
408
            output_resource_dict = _process_resource_metadata(output_resource_dict, metadata_resource_fields,
1✔
409
                                                              file_format)
410
            metadata['resources'].append(output_resource_dict)
1✔
411

412
        # process fields
413
        metadata = _process_dataset_metadata(metadata, metadata_fields, file_format)
1✔
414

415
        # analytics.MetadataDownloadAnalyticsSender(file_format=file_format, package_id=id).send_to_queue()
416

417
        buf = io.StringIO()
1✔
418
        if 'json' in file_format:
1✔
419
            json.dump(metadata, buf, indent=4)
1✔
420

421
            output = make_response(buf.getvalue())
1✔
422
            output.headers['Content-Type'] = 'application/json'
1✔
423
            output.headers['Content-Disposition'] = 'attachment; filename="%s.json"' % filename
1✔
424

425
            return output
1✔
426
        elif 'csv' in file_format:
1✔
427
            writer = csv.writer(buf)
1✔
428

429
            # header
430
            writer.writerow(['Field', 'Label', 'Value'])
1✔
431

432
            # content
433
            for k, v in metadata.items():
1✔
434
                label_value = metadata_fields[k] if k in metadata_fields else metadata_resource_fields[
1✔
435
                    re.sub('^resource_([0-9]+)_', '', k)] if k.startswith('resource_') else None
436
                writer.writerow([k, label_value, v])
1✔
437

438
            output = make_response(buf.getvalue())
1✔
439
            output.headers['Content-Type'] = 'text/csv'
1✔
440
            output.headers['Content-Disposition'] = 'attachment; filename="%s.csv"' % filename
1✔
441

442
            return output
1✔
443
    except NotFound:
1✔
UNCOV
444
        return abort(404, _('Dataset not found'))
×
445
    except NotAuthorized:
1✔
446
        return abort(404, _('Dataset not found'))
1✔
447

448
    return abort(404, _('Invalid file format'))
×
449

450

451
def resource_metadata(id, resource_id):
1✔
452
    '''
453
        Handles downloading .CSV and .JSON resource metadata files
454

455
        :returns: json or csv file
456
    '''
457

458
    context = {
1✔
459
        'model': model,
460
        'session': model.Session,
461
        'user': g.user or g.author,
462
        'auth_user_obj': g.userobj
463
    }
464

465
    # check if resource exists
466
    try:
1✔
467
        resource_dict = get_action('resource_show')(context, {'id': resource_id})
1✔
468

469
        metadata_fields = RESOURCE_METADATA_FIELDS_MAP
1✔
470

471
        # limit fields
472
        metadata = {field: resource_dict[field] if field in resource_dict else None for field, field_data in
1✔
473
                    metadata_fields.items()}
474

475
        file_format = request.params.get('format', '')
1✔
476
        filename = 'metadata-%s' % h.hdx_munge_title(metadata.get('name'))
1✔
477

478
        # process fields
479
        metadata = _process_resource_metadata(metadata, metadata_fields, file_format)
1✔
480

481
        # analytics.MetadataDownloadAnalyticsSender(file_format=file_format, resource_id=resource_id).send_to_queue()
482

483
        buf = io.StringIO()
1✔
484
        if 'json' in file_format:
1✔
485
            json.dump(metadata, buf, indent=4)
1✔
486

487
            output = make_response(buf.getvalue())
1✔
488
            output.headers['Content-Type'] = 'application/json'
1✔
489
            output.headers['Content-Disposition'] = 'attachment; filename="%s.json"' % filename
1✔
490

491
            return output
1✔
492
        elif 'csv' in file_format:
1✔
493
            writer = csv.writer(buf)
1✔
494

495
            # header
496
            writer.writerow(['Field', 'Label', 'Value'])
1✔
497

498
            # content
499
            for k, v in metadata.items():
1✔
500
                writer.writerow([k, metadata_fields[k] if k in metadata_fields else None, v])
1✔
501

502
            output = make_response(buf.getvalue())
1✔
503
            output.headers['Content-Type'] = 'text/csv'
1✔
504
            output.headers['Content-Disposition'] = 'attachment; filename="%s.csv"' % filename
1✔
505

506
            return output
1✔
507
    except NotFound:
1✔
UNCOV
508
        return abort(404, _('Resource not found'))
×
509
    except NotAuthorized:
1✔
510
        return abort(404, _('Resource not found'))
1✔
UNCOV
511
    return abort(404, _('Invalid file format'))
×
512

513

514
def _normalize_metadata_lists(old_dict: dict) -> dict:
1✔
515
    new_dict = {}
1✔
516

517
    for dict_key, dict_value in old_dict.items():
1✔
518
        if isinstance(dict_value, list):
1✔
519
            for list_key, list_value in enumerate(dict_value, start=1):
1✔
520
                if isinstance(list_value, dict):
1✔
521
                    for k, v in list_value.items():
1✔
522
                        new_dict['%s_%s_%s' % (dict_key, list_key, k)] = v
1✔
523
                else:
524
                    new_dict[dict_key] = ', '.join(dict_value)
1✔
525
        else:
526
            new_dict[dict_key] = dict_value
1✔
527

528
    return new_dict
1✔
529

530

531
def _process_dataset_metadata(metadata_dict: dict, fields: dict, file_format: str) -> dict:
1✔
532
    if 'notes' in fields:
1✔
533
        metadata_dict['notes'] = markdown_extract_strip(metadata_dict.get('notes'), 0)
1✔
534
    if 'organization' in fields:
1✔
535
        metadata_dict['organization'] = metadata_dict.get('organization').get('title')
1✔
536
    if 'data_update_frequency' in fields:
1✔
537
        data_update_frequency_value = h.hdx_get_frequency_by_value(metadata_dict.get('data_update_frequency'))
1✔
538
        metadata_dict['data_update_frequency'] = data_update_frequency_value or metadata_dict.get(
1✔
539
            'data_update_frequency')
540
    if 'groups' in fields:
1✔
541
        metadata_dict['groups'] = [group['display_name'] for group in metadata_dict.get('groups')]
1✔
542
    if 'tags' in fields:
1✔
543
        metadata_dict['tags'] = [tag['display_name'] for tag in metadata_dict.get('tags')]
1✔
544

545
    if 'csv' in file_format:
1✔
546
        # rename keys
547
        if 'resources' in metadata_dict:
1✔
548
            metadata_dict['resource'] = metadata_dict['resources']
1✔
549
            del metadata_dict['resources']
1✔
550

551
        metadata_dict = _normalize_metadata_lists(metadata_dict)
1✔
552

553
    return metadata_dict
1✔
554

555

556
def _process_resource_metadata(metadata_dict: dict, fields: dict, file_format: str) -> dict:
1✔
557
    if 'size' in fields:
1✔
558
        metadata_dict['size'] = filesize_format(metadata_dict.get('size'))
1✔
559
    if 'description' in fields:
1✔
560
        metadata_dict['description'] = markdown_extract_strip(metadata_dict.get('description'), 0)
1✔
561

562
    # rename keys
563
    if 'package_id' in fields:
1✔
564
        metadata_dict['dataset_id'] = metadata_dict['package_id']
1✔
565
        del metadata_dict['package_id']
1✔
566

567
    return metadata_dict
1✔
568

569

570
class DatasetContactContributorView(MethodView):
1✔
571

572
    def post(self, id: str) -> Union[Response, str]:
1✔
573
        context = {
1✔
574
            u'model': model,
575
            u'session': model.Session,
576
            u'user': g.user or g.author,
577
            u'auth_user_obj': g.userobj,
578
        }
579

580
        data = errors = {}
1✔
581
        try:
1✔
582
            pkg_dict = get_action('package_show')(context, {'id': id})
1✔
583

584
            if pkg_dict.get('is_requestdata_type'):
1✔
585
                return abort(404, _('Dataset not found'))
1✔
586

587
            check_access(u'hdx_send_mail_contributor', context)
1✔
588

589
            dataset_contact_contributor_logic = DatasetContactContributorLogic(context, request)
1✔
590

591
            data_dict = None
1✔
592
            try:
1✔
593
                data_dict = dataset_contact_contributor_logic.read()
1✔
UNCOV
594
            except dictization_functions.DataError:
×
UNCOV
595
                abort(400, _(u'Integrity Error'))
×
596

597
            data, errors = dataset_contact_contributor_logic.validate(data_dict)
1✔
598
            if errors:
1✔
599
                return self.get(id, data, errors)
1✔
600

601
            dataset_contact_contributor_logic.send_mail()
1✔
602

603
            analytics_dict = h.hdx_compute_analytics(pkg_dict)
1✔
604

605
            extra_vars = {
1✔
606
                u'pkg_dict': pkg_dict,
607
                u'analytics': analytics_dict,
608
                u'message_subject': request.form.get('topic'),
609
                u'message_sent': True,
610
            }
611
            return render('package/contact_contributor.html', extra_vars=extra_vars)
1✔
612

613
        except NotFound:
1✔
614
            return abort(404, _('Dataset not found'))
1✔
615

616
        except NotAuthorized:
1✔
617
            came_from = h.url_for('hdx_dataset.contact_contributor', id=id)
1✔
618
            return redirect(h.url_for('hdx_signin.login', info_message_type='contact-contributor', came_from=came_from))
1✔
619

620
        except MailerException as e:
1✔
UNCOV
621
            error_summary = _('Could not send request for: %s') % text_type(e)
×
UNCOV
622
            log.error(error_summary)
×
UNCOV
623
            return self.get(id, data, errors, error_summary)
×
624

625
        except ValidationError as e:
1✔
626
            error_summary = e.error_summary
×
627
            log.error(error_summary)
×
UNCOV
628
            return self.get(id, data, errors, error_summary)
×
629

630
        except Exception as e:
1✔
631
            error_summary = _('Request can not be sent. Contact an administrator')
1✔
632
            log.error(error_summary)
1✔
633
            return self.get(id, data, errors, error_summary)
1✔
634

635
    def get(self, id: str,
1✔
636
            data: Optional[dict[str, Any]] = None,
637
            errors: Optional[dict[str, Any]] = None,
638
            error_summary: Optional[str] = None):
639
        context = {
1✔
640
            u'model': model,
641
            u'session': model.Session,
642
            u'user': g.user or g.author,
643
            u'auth_user_obj': g.userobj,
644
        }
645

646
        try:
1✔
647
            pkg_dict = get_action('package_show')(context, {'id': id})
1✔
648

649
            if pkg_dict.get('is_requestdata_type'):
1✔
650
                return abort(404, _('Dataset not found'))
1✔
651

652
            check_access(u'hdx_send_mail_contributor', context)
1✔
653

654
            analytics_dict = h.hdx_compute_analytics(pkg_dict)
1✔
655

656
            extra_vars = {
1✔
657
                u'pkg_dict': pkg_dict,
658
                u'analytics': analytics_dict,
659
                u'contact_topics': contributor_topics,
660
                u'data': data or {},
661
                u'errors': errors or {},
662
                u'error_summary': error_summary or '',
663
            }
664
            return render('package/contact_contributor.html', extra_vars=extra_vars)
1✔
665

666
        except NotFound:
1✔
667
            return abort(404, _('Dataset not found'))
1✔
668

669
        except NotAuthorized:
1✔
670
            came_from = h.url_for('hdx_dataset.contact_contributor', id=id)
1✔
671
            return redirect(h.url_for('hdx_signin.login', info_message_type='contact-contributor', came_from=came_from))
1✔
672

673

674
class DatasetRequestAccessView(MethodView):
1✔
675

676
    def post(self, id: str) -> Union[Response, str]:
1✔
677
        context = {
1✔
678
            u'model': model,
679
            u'session': model.Session,
680
            u'user': g.user or g.author,
681
            u'auth_user_obj': g.userobj,
682
        }
683

684
        data = errors = {}
1✔
685
        try:
1✔
686
            pkg_dict = get_action('package_show')(context, {'id': id})
1✔
687

688
            check_access(u'hdx_request_access', context)
1✔
689

690
            if not pkg_dict.get('is_requestdata_type'):
1✔
691
                return abort(404, _('Dataset not request data type'))
1✔
692

693
            pending_request = h.hdx_pending_request_data(g.userobj.id, pkg_dict.get('id'))
1✔
694
            if len(pending_request) > 0:
1✔
695
                return redirect('hdx_dataset.request_access', id=id)
1✔
696

697
            dataset_request_access_logic = DatasetRequestAccessLogic(context, request)
1✔
698

699
            data_dict = None
1✔
700
            try:
1✔
701
                data_dict = dataset_request_access_logic.read()
1✔
UNCOV
702
            except dictization_functions.DataError:
×
UNCOV
703
                abort(400, _(u'Integrity Error'))
×
704

705
            data, errors = dataset_request_access_logic.validate(data_dict)
1✔
706
            if errors:
1✔
707
                return self.get(id, data, errors)
1✔
708

709
            request_sent, send_request_message = dataset_request_access_logic.send_request()
1✔
710

711
            if request_sent:
1✔
712
                analytics_dict = h.hdx_compute_analytics(pkg_dict)
1✔
713

714
                extra_vars = {
1✔
715
                    u'pkg_dict': pkg_dict,
716
                    u'analytics': analytics_dict,
717
                    u'request_sent': request_sent,
718
                }
719
                return render('package/request_access.html', extra_vars=extra_vars)
1✔
720
            else:
UNCOV
721
                error_summary = send_request_message
×
UNCOV
722
                log.error(error_summary)
×
UNCOV
723
                return self.get(id, data, errors, error_summary)
×
724

725
        except NotFound:
1✔
726
            return abort(404, _('Dataset not found'))
1✔
727

728
        except NotAuthorized:
1✔
729
            came_from = h.url_for('hdx_dataset.request_access', id=id)
1✔
730
            return redirect(h.url_for('hdx_signin.login', info_message_type='hdx-connect', came_from=came_from))
1✔
731

732
        except MailerException as e:
1✔
UNCOV
733
            error_summary = _('Could not send request for: %s') % text_type(e)
×
UNCOV
734
            log.error(error_summary)
×
UNCOV
735
            return self.get(id, data, errors, error_summary)
×
736

737
        except ValidationError as e:
1✔
738
            error_summary = e.error_summary
×
739
            log.error(error_summary)
×
UNCOV
740
            return self.get(id, data, errors, error_summary)
×
741

742
        except Exception as e:
1✔
743
            error_summary = _('Request can not be sent. Contact an administrator')
1✔
744
            log.error(error_summary)
1✔
745
            return self.get(id, data, errors, error_summary)
1✔
746

747
    def get(self, id: str,
1✔
748
            data: Optional[dict[str, Any]] = None,
749
            errors: Optional[dict[str, Any]] = None,
750
            error_summary: Optional[str] = None):
751
        context = {
1✔
752
            u'model': model,
753
            u'session': model.Session,
754
            u'user': g.user or g.author,
755
            u'auth_user_obj': g.userobj,
756
        }
757

758
        try:
1✔
759
            pkg_dict = get_action('package_show')(context, {'id': id})
1✔
760

761
            check_access(u'hdx_request_access', context)
1✔
762

763
            if not pkg_dict.get('is_requestdata_type'):
1✔
764
                return abort(404, _('Dataset not request data type'))
1✔
765

766
            pending_request = h.hdx_pending_request_data(g.userobj.id, pkg_dict.get('id'))
1✔
767
            if pending_request:
1✔
768
                if not error_summary:
1✔
769
                    error_summary = _('You already have a pending request. Please wait for the reply.')
1✔
770

771
            org_dict = get_action(u'organization_show')(context, {'id': pkg_dict.get('organization', {}).get('id')})
1✔
772
            set_custom_rect_logo_url(org_dict)
1✔
773

774
            analytics_dict = h.hdx_compute_analytics(pkg_dict)
1✔
775

776
            extra_vars = {
1✔
777
                u'pkg_dict': pkg_dict,
778
                u'analytics': analytics_dict,
779
                u'org_dict': org_dict,
780
                u'pending_request': pending_request,
781
                u'data': data or {},
782
                u'errors': errors or {},
783
                u'error_summary': error_summary or '',
784
            }
785
            return render('package/request_access.html', extra_vars=extra_vars)
1✔
786

787
        except NotFound:
1✔
788
            return abort(404, _('Dataset not found'))
1✔
789

790
        except NotAuthorized:
1✔
791
            came_from = h.url_for('hdx_dataset.request_access', id=id)
1✔
792
            return redirect(h.url_for('hdx_signin.login', info_message_type='hdx-connect', came_from=came_from))
1✔
793

794

795
hdx_search.add_url_rule(u'/', view_func=search, strict_slashes=False)
1✔
796
hdx_dataset.add_url_rule(u'/', view_func=search, strict_slashes=False)
1✔
797
hdx_dataset.add_url_rule(u'<id>', view_func=read)
1✔
798
hdx_dataset.add_url_rule(u'/delete/<id>', view_func=delete, methods=[u'GET', u'POST'])
1✔
799
hdx_dataset.add_url_rule(u'/<id>/contact/',
1✔
800
                         view_func=DatasetContactContributorView.as_view(str(u'contact_contributor')),
801
                         methods=[u'GET', u'POST'], strict_slashes=False)
802
hdx_dataset.add_url_rule(u'/<id>/request-access/',
1✔
803
                         view_func=DatasetRequestAccessView.as_view(str(u'request_access')),
804
                         methods=[u'GET', u'POST'], strict_slashes=False)
805
hdx_dataset.add_url_rule(u'<id>/download_metadata', view_func=package_metadata)
1✔
806
hdx_dataset.add_url_rule(u'<id>/resource/<resource_id>/download_metadata', view_func=resource_metadata)
1✔
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