udata 9.1.2.dev30355__py2.py3-none-any.whl → 9.1.2.dev30382__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of udata might be problematic. Click here for more details.
- tasks/__init__.py +109 -107
- tasks/helpers.py +18 -18
- udata/__init__.py +4 -4
- udata/admin/views.py +5 -5
- udata/api/__init__.py +135 -124
- udata/api/commands.py +45 -37
- udata/api/errors.py +5 -4
- udata/api/fields.py +23 -21
- udata/api/oauth2.py +55 -74
- udata/api/parsers.py +15 -15
- udata/api/signals.py +1 -1
- udata/api_fields.py +137 -89
- udata/app.py +56 -54
- udata/assets.py +5 -5
- udata/auth/__init__.py +37 -26
- udata/auth/forms.py +23 -15
- udata/auth/helpers.py +1 -1
- udata/auth/mails.py +3 -3
- udata/auth/password_validation.py +19 -15
- udata/auth/views.py +94 -68
- udata/commands/__init__.py +71 -69
- udata/commands/cache.py +7 -7
- udata/commands/db.py +201 -140
- udata/commands/dcat.py +36 -30
- udata/commands/fixtures.py +100 -84
- udata/commands/images.py +21 -20
- udata/commands/info.py +17 -20
- udata/commands/init.py +10 -10
- udata/commands/purge.py +12 -13
- udata/commands/serve.py +41 -29
- udata/commands/static.py +16 -18
- udata/commands/test.py +20 -20
- udata/commands/tests/fixtures.py +26 -24
- udata/commands/worker.py +31 -33
- udata/core/__init__.py +12 -12
- udata/core/activity/__init__.py +0 -1
- udata/core/activity/api.py +59 -49
- udata/core/activity/models.py +28 -26
- udata/core/activity/signals.py +1 -1
- udata/core/activity/tasks.py +16 -10
- udata/core/badges/api.py +6 -6
- udata/core/badges/commands.py +14 -13
- udata/core/badges/fields.py +8 -5
- udata/core/badges/forms.py +7 -4
- udata/core/badges/models.py +16 -31
- udata/core/badges/permissions.py +1 -3
- udata/core/badges/signals.py +2 -2
- udata/core/badges/tasks.py +3 -2
- udata/core/badges/tests/test_commands.py +10 -10
- udata/core/badges/tests/test_model.py +24 -31
- udata/core/contact_point/api.py +19 -18
- udata/core/contact_point/api_fields.py +21 -14
- udata/core/contact_point/factories.py +2 -2
- udata/core/contact_point/forms.py +7 -6
- udata/core/contact_point/models.py +3 -5
- udata/core/dataservices/api.py +26 -21
- udata/core/dataservices/factories.py +13 -11
- udata/core/dataservices/models.py +35 -40
- udata/core/dataservices/permissions.py +4 -4
- udata/core/dataservices/rdf.py +40 -17
- udata/core/dataservices/tasks.py +4 -3
- udata/core/dataset/actions.py +10 -10
- udata/core/dataset/activities.py +21 -23
- udata/core/dataset/api.py +321 -298
- udata/core/dataset/api_fields.py +443 -271
- udata/core/dataset/apiv2.py +305 -229
- udata/core/dataset/commands.py +38 -36
- udata/core/dataset/constants.py +61 -54
- udata/core/dataset/csv.py +70 -74
- udata/core/dataset/events.py +39 -32
- udata/core/dataset/exceptions.py +8 -4
- udata/core/dataset/factories.py +57 -65
- udata/core/dataset/forms.py +87 -63
- udata/core/dataset/models.py +336 -280
- udata/core/dataset/permissions.py +9 -6
- udata/core/dataset/preview.py +15 -17
- udata/core/dataset/rdf.py +156 -122
- udata/core/dataset/search.py +92 -77
- udata/core/dataset/signals.py +1 -1
- udata/core/dataset/tasks.py +63 -54
- udata/core/discussions/actions.py +5 -5
- udata/core/discussions/api.py +124 -120
- udata/core/discussions/factories.py +2 -2
- udata/core/discussions/forms.py +9 -7
- udata/core/discussions/metrics.py +1 -3
- udata/core/discussions/models.py +25 -24
- udata/core/discussions/notifications.py +18 -14
- udata/core/discussions/permissions.py +3 -3
- udata/core/discussions/signals.py +4 -4
- udata/core/discussions/tasks.py +24 -28
- udata/core/followers/api.py +32 -33
- udata/core/followers/models.py +9 -9
- udata/core/followers/signals.py +3 -3
- udata/core/jobs/actions.py +7 -7
- udata/core/jobs/api.py +99 -92
- udata/core/jobs/commands.py +48 -49
- udata/core/jobs/forms.py +11 -11
- udata/core/jobs/models.py +6 -6
- udata/core/metrics/__init__.py +2 -2
- udata/core/metrics/commands.py +34 -30
- udata/core/metrics/models.py +2 -4
- udata/core/metrics/signals.py +1 -1
- udata/core/metrics/tasks.py +3 -3
- udata/core/organization/activities.py +12 -15
- udata/core/organization/api.py +167 -174
- udata/core/organization/api_fields.py +183 -124
- udata/core/organization/apiv2.py +32 -32
- udata/core/organization/commands.py +20 -22
- udata/core/organization/constants.py +11 -11
- udata/core/organization/csv.py +17 -15
- udata/core/organization/factories.py +8 -11
- udata/core/organization/forms.py +32 -26
- udata/core/organization/metrics.py +2 -1
- udata/core/organization/models.py +87 -67
- udata/core/organization/notifications.py +18 -14
- udata/core/organization/permissions.py +10 -11
- udata/core/organization/rdf.py +14 -14
- udata/core/organization/search.py +30 -28
- udata/core/organization/signals.py +7 -7
- udata/core/organization/tasks.py +42 -61
- udata/core/owned.py +38 -27
- udata/core/post/api.py +82 -81
- udata/core/post/constants.py +8 -5
- udata/core/post/factories.py +4 -4
- udata/core/post/forms.py +13 -14
- udata/core/post/models.py +20 -22
- udata/core/post/tests/test_api.py +30 -32
- udata/core/reports/api.py +8 -7
- udata/core/reports/constants.py +1 -3
- udata/core/reports/models.py +10 -10
- udata/core/reuse/activities.py +15 -19
- udata/core/reuse/api.py +123 -126
- udata/core/reuse/api_fields.py +120 -85
- udata/core/reuse/apiv2.py +11 -10
- udata/core/reuse/constants.py +23 -23
- udata/core/reuse/csv.py +18 -18
- udata/core/reuse/factories.py +5 -9
- udata/core/reuse/forms.py +24 -21
- udata/core/reuse/models.py +55 -51
- udata/core/reuse/permissions.py +2 -2
- udata/core/reuse/search.py +49 -46
- udata/core/reuse/signals.py +1 -1
- udata/core/reuse/tasks.py +4 -5
- udata/core/site/api.py +47 -50
- udata/core/site/factories.py +2 -2
- udata/core/site/forms.py +4 -5
- udata/core/site/models.py +94 -63
- udata/core/site/rdf.py +14 -14
- udata/core/spam/api.py +16 -9
- udata/core/spam/constants.py +4 -4
- udata/core/spam/fields.py +13 -7
- udata/core/spam/models.py +27 -20
- udata/core/spam/signals.py +1 -1
- udata/core/spam/tests/test_spam.py +6 -5
- udata/core/spatial/api.py +72 -80
- udata/core/spatial/api_fields.py +73 -58
- udata/core/spatial/commands.py +67 -64
- udata/core/spatial/constants.py +3 -3
- udata/core/spatial/factories.py +37 -54
- udata/core/spatial/forms.py +27 -26
- udata/core/spatial/geoids.py +17 -17
- udata/core/spatial/models.py +43 -47
- udata/core/spatial/tasks.py +2 -1
- udata/core/spatial/tests/test_api.py +115 -130
- udata/core/spatial/tests/test_fields.py +74 -77
- udata/core/spatial/tests/test_geoid.py +22 -22
- udata/core/spatial/tests/test_models.py +5 -7
- udata/core/spatial/translations.py +16 -16
- udata/core/storages/__init__.py +16 -18
- udata/core/storages/api.py +66 -64
- udata/core/storages/tasks.py +7 -7
- udata/core/storages/utils.py +15 -15
- udata/core/storages/views.py +5 -6
- udata/core/tags/api.py +17 -14
- udata/core/tags/csv.py +4 -4
- udata/core/tags/models.py +8 -5
- udata/core/tags/tasks.py +11 -13
- udata/core/tags/views.py +4 -4
- udata/core/topic/api.py +84 -73
- udata/core/topic/apiv2.py +157 -127
- udata/core/topic/factories.py +3 -4
- udata/core/topic/forms.py +12 -14
- udata/core/topic/models.py +14 -19
- udata/core/topic/parsers.py +26 -26
- udata/core/user/activities.py +30 -29
- udata/core/user/api.py +151 -152
- udata/core/user/api_fields.py +132 -100
- udata/core/user/apiv2.py +7 -7
- udata/core/user/commands.py +38 -38
- udata/core/user/factories.py +8 -9
- udata/core/user/forms.py +14 -11
- udata/core/user/metrics.py +2 -2
- udata/core/user/models.py +68 -69
- udata/core/user/permissions.py +4 -5
- udata/core/user/rdf.py +7 -8
- udata/core/user/tasks.py +2 -2
- udata/core/user/tests/test_user_model.py +24 -16
- udata/db/tasks.py +2 -1
- udata/entrypoints.py +35 -31
- udata/errors.py +2 -1
- udata/event/values.py +6 -6
- udata/factories.py +2 -2
- udata/features/identicon/api.py +5 -6
- udata/features/identicon/backends.py +48 -55
- udata/features/identicon/tests/test_backends.py +4 -5
- udata/features/notifications/__init__.py +0 -1
- udata/features/notifications/actions.py +9 -9
- udata/features/notifications/api.py +17 -13
- udata/features/territories/__init__.py +12 -10
- udata/features/territories/api.py +14 -15
- udata/features/territories/models.py +23 -28
- udata/features/transfer/actions.py +8 -11
- udata/features/transfer/api.py +84 -77
- udata/features/transfer/factories.py +2 -1
- udata/features/transfer/models.py +11 -12
- udata/features/transfer/notifications.py +19 -15
- udata/features/transfer/permissions.py +5 -5
- udata/forms/__init__.py +5 -2
- udata/forms/fields.py +164 -172
- udata/forms/validators.py +19 -22
- udata/forms/widgets.py +9 -13
- udata/frontend/__init__.py +31 -26
- udata/frontend/csv.py +68 -58
- udata/frontend/markdown.py +40 -44
- udata/harvest/actions.py +89 -77
- udata/harvest/api.py +294 -238
- udata/harvest/backends/__init__.py +4 -4
- udata/harvest/backends/base.py +128 -111
- udata/harvest/backends/dcat.py +80 -66
- udata/harvest/commands.py +56 -60
- udata/harvest/csv.py +8 -8
- udata/harvest/exceptions.py +6 -3
- udata/harvest/filters.py +24 -23
- udata/harvest/forms.py +27 -28
- udata/harvest/models.py +88 -80
- udata/harvest/notifications.py +15 -10
- udata/harvest/signals.py +13 -13
- udata/harvest/tasks.py +11 -10
- udata/harvest/tests/factories.py +23 -24
- udata/harvest/tests/test_actions.py +136 -166
- udata/harvest/tests/test_api.py +220 -214
- udata/harvest/tests/test_base_backend.py +117 -112
- udata/harvest/tests/test_dcat_backend.py +380 -308
- udata/harvest/tests/test_filters.py +33 -22
- udata/harvest/tests/test_models.py +11 -14
- udata/harvest/tests/test_notifications.py +6 -7
- udata/harvest/tests/test_tasks.py +7 -6
- udata/i18n.py +237 -78
- udata/linkchecker/backends.py +5 -11
- udata/linkchecker/checker.py +23 -22
- udata/linkchecker/commands.py +4 -6
- udata/linkchecker/models.py +6 -6
- udata/linkchecker/tasks.py +18 -20
- udata/mail.py +21 -21
- udata/migrations/2020-07-24-remove-s-from-scope-oauth.py +9 -8
- udata/migrations/2020-08-24-add-fs-filename.py +9 -8
- udata/migrations/2020-09-28-update-reuses-datasets-metrics.py +5 -4
- udata/migrations/2020-10-16-migrate-ods-resources.py +9 -10
- udata/migrations/2021-04-08-update-schema-with-new-structure.py +8 -7
- udata/migrations/2021-05-27-fix-default-schema-name.py +7 -6
- udata/migrations/2021-07-05-remove-unused-badges.py +17 -15
- udata/migrations/2021-07-07-update-schema-for-community-resources.py +7 -6
- udata/migrations/2021-08-17-follow-integrity.py +5 -4
- udata/migrations/2021-08-17-harvest-integrity.py +13 -12
- udata/migrations/2021-08-17-oauth2client-integrity.py +5 -4
- udata/migrations/2021-08-17-transfer-integrity.py +5 -4
- udata/migrations/2021-08-17-users-integrity.py +9 -8
- udata/migrations/2021-12-14-reuse-topics.py +7 -6
- udata/migrations/2022-04-21-improve-extension-detection.py +8 -7
- udata/migrations/2022-09-22-clean-inactive-harvest-datasets.py +16 -14
- udata/migrations/2022-10-10-add-fs_uniquifier-to-user-model.py +6 -6
- udata/migrations/2022-10-10-migrate-harvest-extras.py +36 -26
- udata/migrations/2023-02-08-rename-internal-dates.py +46 -28
- udata/migrations/2024-01-29-fix-reuse-and-dataset-with-private-None.py +10 -8
- udata/migrations/2024-03-22-migrate-activity-kwargs-to-extras.py +6 -4
- udata/migrations/2024-06-11-fix-reuse-datasets-references.py +7 -6
- udata/migrations/__init__.py +123 -105
- udata/models/__init__.py +4 -4
- udata/mongo/__init__.py +13 -11
- udata/mongo/badges_field.py +3 -2
- udata/mongo/datetime_fields.py +13 -12
- udata/mongo/document.py +17 -16
- udata/mongo/engine.py +15 -16
- udata/mongo/errors.py +2 -1
- udata/mongo/extras_fields.py +30 -20
- udata/mongo/queryset.py +12 -12
- udata/mongo/slug_fields.py +38 -28
- udata/mongo/taglist_field.py +1 -2
- udata/mongo/url_field.py +5 -5
- udata/mongo/uuid_fields.py +4 -3
- udata/notifications/__init__.py +1 -1
- udata/notifications/mattermost.py +10 -9
- udata/rdf.py +167 -188
- udata/routing.py +40 -45
- udata/search/__init__.py +18 -19
- udata/search/adapter.py +17 -16
- udata/search/commands.py +44 -51
- udata/search/fields.py +13 -20
- udata/search/query.py +23 -18
- udata/search/result.py +9 -10
- udata/sentry.py +21 -19
- udata/settings.py +262 -198
- udata/sitemap.py +8 -6
- udata/static/chunks/{11.e9b9ca1f3e03d4020377.js → 11.52e531c19f8de80c00cf.js} +3 -3
- udata/static/chunks/{11.e9b9ca1f3e03d4020377.js.map → 11.52e531c19f8de80c00cf.js.map} +1 -1
- udata/static/chunks/{13.038c0d9aa0dfa0181c4b.js → 13.c3343a7f1070061c0e10.js} +2 -2
- udata/static/chunks/{13.038c0d9aa0dfa0181c4b.js.map → 13.c3343a7f1070061c0e10.js.map} +1 -1
- udata/static/chunks/{16.0baa2b64a74a2dcde25c.js → 16.8fa42440ad75ca172e6d.js} +2 -2
- udata/static/chunks/{16.0baa2b64a74a2dcde25c.js.map → 16.8fa42440ad75ca172e6d.js.map} +1 -1
- udata/static/chunks/{19.350a9f150b074b4ecefa.js → 19.9c6c8412729cd6d59cfa.js} +3 -3
- udata/static/chunks/{19.350a9f150b074b4ecefa.js.map → 19.9c6c8412729cd6d59cfa.js.map} +1 -1
- udata/static/chunks/{5.6ebbce2b9b3e696d3da5.js → 5.71d15c2e4f21feee2a9a.js} +3 -3
- udata/static/chunks/{5.6ebbce2b9b3e696d3da5.js.map → 5.71d15c2e4f21feee2a9a.js.map} +1 -1
- udata/static/chunks/{6.d8a5f7b017bcbd083641.js → 6.9139dc098b8ea640b890.js} +3 -3
- udata/static/chunks/{6.d8a5f7b017bcbd083641.js.map → 6.9139dc098b8ea640b890.js.map} +1 -1
- udata/static/common.js +1 -1
- udata/static/common.js.map +1 -1
- udata/storage/s3.py +20 -13
- udata/tags.py +4 -5
- udata/tasks.py +43 -42
- udata/tests/__init__.py +9 -6
- udata/tests/api/__init__.py +5 -6
- udata/tests/api/test_auth_api.py +395 -321
- udata/tests/api/test_base_api.py +31 -33
- udata/tests/api/test_contact_points.py +7 -9
- udata/tests/api/test_dataservices_api.py +211 -158
- udata/tests/api/test_datasets_api.py +823 -812
- udata/tests/api/test_follow_api.py +13 -15
- udata/tests/api/test_me_api.py +95 -112
- udata/tests/api/test_organizations_api.py +301 -339
- udata/tests/api/test_reports_api.py +35 -25
- udata/tests/api/test_reuses_api.py +134 -139
- udata/tests/api/test_swagger.py +5 -5
- udata/tests/api/test_tags_api.py +18 -25
- udata/tests/api/test_topics_api.py +94 -94
- udata/tests/api/test_transfer_api.py +53 -48
- udata/tests/api/test_user_api.py +128 -141
- udata/tests/apiv2/test_datasets.py +290 -198
- udata/tests/apiv2/test_me_api.py +10 -11
- udata/tests/apiv2/test_organizations.py +56 -74
- udata/tests/apiv2/test_swagger.py +5 -5
- udata/tests/apiv2/test_topics.py +69 -87
- udata/tests/cli/test_cli_base.py +8 -8
- udata/tests/cli/test_db_cli.py +21 -19
- udata/tests/dataservice/test_dataservice_tasks.py +8 -12
- udata/tests/dataset/test_csv_adapter.py +44 -35
- udata/tests/dataset/test_dataset_actions.py +2 -3
- udata/tests/dataset/test_dataset_commands.py +7 -8
- udata/tests/dataset/test_dataset_events.py +36 -29
- udata/tests/dataset/test_dataset_model.py +224 -217
- udata/tests/dataset/test_dataset_rdf.py +142 -131
- udata/tests/dataset/test_dataset_tasks.py +15 -15
- udata/tests/dataset/test_resource_preview.py +10 -13
- udata/tests/features/territories/__init__.py +9 -13
- udata/tests/features/territories/test_territories_api.py +71 -91
- udata/tests/forms/test_basic_fields.py +7 -7
- udata/tests/forms/test_current_user_field.py +39 -66
- udata/tests/forms/test_daterange_field.py +31 -39
- udata/tests/forms/test_dict_field.py +28 -26
- udata/tests/forms/test_extras_fields.py +102 -76
- udata/tests/forms/test_form_field.py +8 -8
- udata/tests/forms/test_image_field.py +33 -26
- udata/tests/forms/test_model_field.py +134 -123
- udata/tests/forms/test_model_list_field.py +7 -7
- udata/tests/forms/test_nested_model_list_field.py +117 -79
- udata/tests/forms/test_publish_as_field.py +36 -65
- udata/tests/forms/test_reference_field.py +34 -53
- udata/tests/forms/test_user_forms.py +23 -21
- udata/tests/forms/test_uuid_field.py +6 -10
- udata/tests/frontend/__init__.py +9 -6
- udata/tests/frontend/test_auth.py +7 -6
- udata/tests/frontend/test_csv.py +81 -96
- udata/tests/frontend/test_hooks.py +43 -43
- udata/tests/frontend/test_markdown.py +211 -191
- udata/tests/helpers.py +32 -37
- udata/tests/models.py +2 -2
- udata/tests/organization/test_csv_adapter.py +21 -16
- udata/tests/organization/test_notifications.py +11 -18
- udata/tests/organization/test_organization_model.py +13 -13
- udata/tests/organization/test_organization_rdf.py +29 -22
- udata/tests/organization/test_organization_tasks.py +16 -17
- udata/tests/plugin.py +76 -73
- udata/tests/reuse/test_reuse_model.py +21 -21
- udata/tests/reuse/test_reuse_task.py +11 -13
- udata/tests/search/__init__.py +11 -12
- udata/tests/search/test_adapter.py +60 -70
- udata/tests/search/test_query.py +16 -16
- udata/tests/search/test_results.py +10 -7
- udata/tests/site/test_site_api.py +11 -16
- udata/tests/site/test_site_metrics.py +20 -30
- udata/tests/site/test_site_model.py +4 -5
- udata/tests/site/test_site_rdf.py +94 -78
- udata/tests/test_activity.py +17 -17
- udata/tests/test_discussions.py +292 -299
- udata/tests/test_i18n.py +37 -40
- udata/tests/test_linkchecker.py +91 -85
- udata/tests/test_mail.py +13 -17
- udata/tests/test_migrations.py +219 -180
- udata/tests/test_model.py +164 -157
- udata/tests/test_notifications.py +17 -17
- udata/tests/test_owned.py +14 -14
- udata/tests/test_rdf.py +25 -23
- udata/tests/test_routing.py +89 -93
- udata/tests/test_storages.py +137 -128
- udata/tests/test_tags.py +44 -46
- udata/tests/test_topics.py +7 -7
- udata/tests/test_transfer.py +42 -49
- udata/tests/test_uris.py +160 -161
- udata/tests/test_utils.py +79 -71
- udata/tests/user/test_user_rdf.py +5 -9
- udata/tests/workers/test_jobs_commands.py +57 -58
- udata/tests/workers/test_tasks_routing.py +23 -29
- udata/tests/workers/test_workers_api.py +125 -131
- udata/tests/workers/test_workers_helpers.py +6 -6
- udata/tracking.py +4 -6
- udata/uris.py +45 -46
- udata/utils.py +68 -66
- udata/wsgi.py +1 -1
- {udata-9.1.2.dev30355.dist-info → udata-9.1.2.dev30382.dist-info}/METADATA +3 -2
- udata-9.1.2.dev30382.dist-info/RECORD +704 -0
- udata-9.1.2.dev30355.dist-info/RECORD +0 -704
- {udata-9.1.2.dev30355.dist-info → udata-9.1.2.dev30382.dist-info}/LICENSE +0 -0
- {udata-9.1.2.dev30355.dist-info → udata-9.1.2.dev30382.dist-info}/WHEEL +0 -0
- {udata-9.1.2.dev30355.dist-info → udata-9.1.2.dev30382.dist-info}/entry_points.txt +0 -0
- {udata-9.1.2.dev30355.dist-info → udata-9.1.2.dev30382.dist-info}/top_level.txt +0 -0
udata/utils.py
CHANGED
|
@@ -1,54 +1,54 @@
|
|
|
1
1
|
import hashlib
|
|
2
2
|
import math
|
|
3
3
|
import re
|
|
4
|
+
from datetime import date, datetime
|
|
5
|
+
from math import ceil
|
|
6
|
+
from uuid import UUID, uuid4
|
|
7
|
+
from xml.sax.saxutils import escape
|
|
4
8
|
|
|
5
9
|
import factory
|
|
6
|
-
|
|
7
10
|
from bson import ObjectId
|
|
8
11
|
from bson.errors import InvalidId
|
|
9
|
-
from datetime import date, datetime
|
|
10
|
-
from dateutil.relativedelta import relativedelta
|
|
11
12
|
from dateutil.parser import parse as parse_dt
|
|
13
|
+
from dateutil.relativedelta import relativedelta
|
|
12
14
|
from faker import Faker
|
|
13
15
|
from faker.config import PROVIDERS
|
|
14
16
|
from faker.providers import BaseProvider
|
|
15
17
|
from faker.providers.lorem.la import Provider as LoremProvider
|
|
16
18
|
from flask import abort
|
|
17
|
-
from math import ceil
|
|
18
|
-
from uuid import uuid4, UUID
|
|
19
|
-
from xml.sax.saxutils import escape
|
|
20
19
|
|
|
21
20
|
|
|
22
21
|
def get_by(lst, field, value):
|
|
23
|
-
|
|
22
|
+
"""Find an object in a list given a field value"""
|
|
24
23
|
for row in lst:
|
|
25
|
-
if (
|
|
26
|
-
|
|
24
|
+
if (isinstance(row, dict) and row.get(field) == value) or (
|
|
25
|
+
getattr(row, field, None) == value
|
|
26
|
+
):
|
|
27
27
|
return row
|
|
28
28
|
|
|
29
29
|
|
|
30
30
|
def multi_to_dict(multi):
|
|
31
|
-
|
|
31
|
+
"""Transform a Werkzeug multidictionnary into a flat dictionnary"""
|
|
32
32
|
return dict(
|
|
33
|
-
(key, value[0] if len(value) == 1 else value)
|
|
34
|
-
for key, value in multi.to_dict(False).items()
|
|
33
|
+
(key, value[0] if len(value) == 1 else value) for key, value in multi.to_dict(False).items()
|
|
35
34
|
)
|
|
36
35
|
|
|
37
36
|
|
|
38
|
-
FIRST_CAP_RE = re.compile(
|
|
39
|
-
ALL_CAP_RE = re.compile(
|
|
37
|
+
FIRST_CAP_RE = re.compile("(.)([A-Z][a-z]+)")
|
|
38
|
+
ALL_CAP_RE = re.compile("([a-z0-9])([A-Z])")
|
|
40
39
|
UUID_LENGTH = 36
|
|
41
40
|
|
|
42
41
|
|
|
43
42
|
def camel_to_lodash(name):
|
|
44
|
-
s1 = FIRST_CAP_RE.sub(r
|
|
45
|
-
return ALL_CAP_RE.sub(r
|
|
43
|
+
s1 = FIRST_CAP_RE.sub(r"\1_\2", name)
|
|
44
|
+
return ALL_CAP_RE.sub(r"\1_\2", s1).lower()
|
|
46
45
|
|
|
47
46
|
|
|
48
47
|
class Paginable(object):
|
|
49
|
-
|
|
48
|
+
"""
|
|
50
49
|
A simple helper mixin for pagination
|
|
51
|
-
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
52
|
@property
|
|
53
53
|
def pages(self):
|
|
54
54
|
if self.page_size:
|
|
@@ -75,14 +75,14 @@ class Paginable(object):
|
|
|
75
75
|
def page_end(self):
|
|
76
76
|
return min(self.total, self.page_size * self.page)
|
|
77
77
|
|
|
78
|
-
def iter_pages(self, left_edge=2, left_current=2, right_current=5,
|
|
79
|
-
right_edge=2):
|
|
78
|
+
def iter_pages(self, left_edge=2, left_current=2, right_current=5, right_edge=2):
|
|
80
79
|
last = 0
|
|
81
80
|
for num in range(1, self.pages + 1):
|
|
82
|
-
if (
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
81
|
+
if (
|
|
82
|
+
num <= left_edge
|
|
83
|
+
or (num > self.page - left_current - 1 and num < self.page + right_current)
|
|
84
|
+
or num > self.pages - right_edge
|
|
85
|
+
):
|
|
86
86
|
if last + 1 != num:
|
|
87
87
|
yield None
|
|
88
88
|
yield num
|
|
@@ -90,7 +90,8 @@ class Paginable(object):
|
|
|
90
90
|
|
|
91
91
|
|
|
92
92
|
class Paginator(Paginable):
|
|
93
|
-
|
|
93
|
+
"""A simple paginable implementation"""
|
|
94
|
+
|
|
94
95
|
def __init__(self, page, page_size, total):
|
|
95
96
|
self.page = page
|
|
96
97
|
self.page_size = page_size
|
|
@@ -98,7 +99,7 @@ class Paginator(Paginable):
|
|
|
98
99
|
|
|
99
100
|
|
|
100
101
|
def daterange_start(value):
|
|
101
|
-
|
|
102
|
+
"""Parse a date range start boundary"""
|
|
102
103
|
if not value:
|
|
103
104
|
return None
|
|
104
105
|
elif isinstance(value, datetime):
|
|
@@ -107,7 +108,7 @@ def daterange_start(value):
|
|
|
107
108
|
return value
|
|
108
109
|
|
|
109
110
|
result = parse_dt(value).date()
|
|
110
|
-
dashes = value.count(
|
|
111
|
+
dashes = value.count("-")
|
|
111
112
|
|
|
112
113
|
if dashes >= 2:
|
|
113
114
|
return result
|
|
@@ -120,7 +121,7 @@ def daterange_start(value):
|
|
|
120
121
|
|
|
121
122
|
|
|
122
123
|
def daterange_end(value):
|
|
123
|
-
|
|
124
|
+
"""Parse a date range end boundary"""
|
|
124
125
|
if not value:
|
|
125
126
|
return None
|
|
126
127
|
elif isinstance(value, datetime):
|
|
@@ -129,7 +130,7 @@ def daterange_end(value):
|
|
|
129
130
|
return value
|
|
130
131
|
|
|
131
132
|
result = parse_dt(value).date()
|
|
132
|
-
dashes = value.count(
|
|
133
|
+
dashes = value.count("-")
|
|
133
134
|
|
|
134
135
|
if dashes >= 2:
|
|
135
136
|
# Full date
|
|
@@ -146,22 +147,18 @@ def to_naive_datetime(given_date):
|
|
|
146
147
|
if isinstance(given_date, str):
|
|
147
148
|
given_date = parse_dt(given_date)
|
|
148
149
|
if isinstance(given_date, date) and not isinstance(given_date, datetime):
|
|
149
|
-
return datetime(
|
|
150
|
-
given_date.year,
|
|
151
|
-
given_date.month,
|
|
152
|
-
given_date.day
|
|
153
|
-
)
|
|
150
|
+
return datetime(given_date.year, given_date.month, given_date.day)
|
|
154
151
|
elif isinstance(given_date, datetime):
|
|
155
152
|
return given_date.replace(tzinfo=None)
|
|
156
153
|
return given_date
|
|
157
154
|
|
|
158
155
|
|
|
159
156
|
def to_iso(dt):
|
|
160
|
-
|
|
157
|
+
"""
|
|
161
158
|
Format a date or datetime into an ISO-8601 string
|
|
162
159
|
|
|
163
160
|
Support dates before 1900.
|
|
164
|
-
|
|
161
|
+
"""
|
|
165
162
|
if isinstance(dt, datetime):
|
|
166
163
|
return to_iso_datetime(dt)
|
|
167
164
|
elif isinstance(dt, date):
|
|
@@ -169,43 +166,46 @@ def to_iso(dt):
|
|
|
169
166
|
|
|
170
167
|
|
|
171
168
|
def to_iso_date(dt):
|
|
172
|
-
|
|
169
|
+
"""
|
|
173
170
|
Format a date or datetime into an ISO-8601 date string.
|
|
174
171
|
|
|
175
172
|
Support dates before 1900.
|
|
176
|
-
|
|
173
|
+
"""
|
|
177
174
|
if dt:
|
|
178
|
-
return
|
|
175
|
+
return "{dt.year:04d}-{dt.month:02d}-{dt.day:02d}".format(dt=dt)
|
|
179
176
|
|
|
180
177
|
|
|
181
178
|
def to_iso_datetime(dt):
|
|
182
|
-
|
|
179
|
+
"""
|
|
183
180
|
Format a date or datetime into an ISO-8601 datetime string.
|
|
184
181
|
|
|
185
182
|
Time is set to 00:00:00 for dates.
|
|
186
183
|
|
|
187
184
|
Support dates before 1900.
|
|
188
|
-
|
|
185
|
+
"""
|
|
189
186
|
if dt:
|
|
190
187
|
date_str = to_iso_date(dt)
|
|
191
|
-
time_str =
|
|
192
|
-
dt
|
|
193
|
-
|
|
188
|
+
time_str = (
|
|
189
|
+
"{dt.hour:02d}:{dt.minute:02d}:{dt.second:02d}".format(dt=dt)
|
|
190
|
+
if isinstance(dt, datetime)
|
|
191
|
+
else "00:00:00"
|
|
192
|
+
)
|
|
193
|
+
return "T".join((date_str, time_str))
|
|
194
194
|
|
|
195
195
|
|
|
196
196
|
def to_bool(value):
|
|
197
|
-
|
|
197
|
+
"""
|
|
198
198
|
Transform a value into a boolean with the following rules:
|
|
199
199
|
|
|
200
200
|
- a boolean is returned untouched
|
|
201
201
|
- a string value should match any casinf of 'true' to be True
|
|
202
202
|
- an integer should be superior to zero to be True
|
|
203
203
|
- all other values are False
|
|
204
|
-
|
|
204
|
+
"""
|
|
205
205
|
if isinstance(value, bool):
|
|
206
206
|
return value
|
|
207
207
|
elif isinstance(value, str):
|
|
208
|
-
return value.lower() ==
|
|
208
|
+
return value.lower() == "true" or value.lower() == "t"
|
|
209
209
|
elif isinstance(value, int):
|
|
210
210
|
return value > 0
|
|
211
211
|
else:
|
|
@@ -213,34 +213,34 @@ def to_bool(value):
|
|
|
213
213
|
|
|
214
214
|
|
|
215
215
|
def clean_string(value):
|
|
216
|
-
|
|
216
|
+
"""
|
|
217
217
|
Clean an user input string (Prevent it from containing XSS)
|
|
218
|
-
|
|
218
|
+
"""
|
|
219
219
|
return escape(value)
|
|
220
220
|
|
|
221
221
|
|
|
222
222
|
def not_none_dict(d):
|
|
223
|
-
|
|
223
|
+
"""Filter out None values from a dict"""
|
|
224
224
|
return {k: v for k, v in d.items() if v is not None}
|
|
225
225
|
|
|
226
226
|
|
|
227
227
|
def hash_url(url):
|
|
228
|
-
|
|
229
|
-
return hashlib.sha1(url.encode(
|
|
228
|
+
"""Hash an URL to make it indexable"""
|
|
229
|
+
return hashlib.sha1(url.encode("utf-8")).hexdigest() if url else None
|
|
230
230
|
|
|
231
231
|
|
|
232
232
|
def recursive_get(obj, key):
|
|
233
|
-
|
|
233
|
+
"""
|
|
234
234
|
Get an attribute or a key recursively.
|
|
235
235
|
|
|
236
236
|
:param obj: The object to fetch attribute or key on
|
|
237
237
|
:type obj: object|dict
|
|
238
238
|
:param key: Either a string in dotted-notation ar an array of string
|
|
239
239
|
:type key: string|list|tuple
|
|
240
|
-
|
|
240
|
+
"""
|
|
241
241
|
if not obj or not key:
|
|
242
242
|
return
|
|
243
|
-
parts = key.split(
|
|
243
|
+
parts = key.split(".") if isinstance(key, str) else key
|
|
244
244
|
key = parts.pop(0)
|
|
245
245
|
if isinstance(obj, dict):
|
|
246
246
|
value = obj.get(key, None)
|
|
@@ -250,7 +250,7 @@ def recursive_get(obj, key):
|
|
|
250
250
|
|
|
251
251
|
|
|
252
252
|
def unique_string(length=UUID_LENGTH):
|
|
253
|
-
|
|
253
|
+
"""Generate a unique string"""
|
|
254
254
|
# We need a string at least as long as length
|
|
255
255
|
string = str(uuid4()) * int(math.ceil(length / float(UUID_LENGTH)))
|
|
256
256
|
return string[:length] if length else string
|
|
@@ -263,7 +263,7 @@ def is_uuid(uuid_string, version=4):
|
|
|
263
263
|
# to prevent this, we check the genuine version (without dashes)
|
|
264
264
|
# with the generated hex code. They should be similar.
|
|
265
265
|
uid = UUID(uuid_string, version=version)
|
|
266
|
-
return uid.hex == uuid_string.replace(
|
|
266
|
+
return uid.hex == uuid_string.replace("-", "")
|
|
267
267
|
except ValueError:
|
|
268
268
|
return False
|
|
269
269
|
|
|
@@ -271,9 +271,9 @@ def is_uuid(uuid_string, version=4):
|
|
|
271
271
|
# This is the default providers list
|
|
272
272
|
# We remove the lorum one to replace it
|
|
273
273
|
# with a unicode enabled one below
|
|
274
|
-
PROVIDERS.remove(
|
|
274
|
+
PROVIDERS.remove("faker.providers.lorem")
|
|
275
275
|
|
|
276
|
-
faker = Faker(
|
|
276
|
+
faker = Faker("fr_FR") # Use a unicode/utf-8 based locale
|
|
277
277
|
|
|
278
278
|
|
|
279
279
|
def faker_provider(provider):
|
|
@@ -284,27 +284,29 @@ def faker_provider(provider):
|
|
|
284
284
|
|
|
285
285
|
@faker_provider
|
|
286
286
|
class UDataProvider(BaseProvider):
|
|
287
|
-
|
|
287
|
+
"""
|
|
288
288
|
A Faker provider for udata missing requirements.
|
|
289
289
|
|
|
290
290
|
Might be conributed to upstream Faker project
|
|
291
|
-
|
|
291
|
+
"""
|
|
292
|
+
|
|
292
293
|
def unique_string(self, length=UUID_LENGTH):
|
|
293
|
-
|
|
294
|
+
"""Generate a unique string"""
|
|
294
295
|
return unique_string(length)
|
|
295
296
|
|
|
296
297
|
|
|
297
298
|
@faker_provider # Replace the default lorem provider with a unicode one
|
|
298
299
|
class UnicodeLoremProvider(LoremProvider):
|
|
299
|
-
|
|
300
|
-
|
|
300
|
+
"""A Lorem provider that forces unicode in words"""
|
|
301
|
+
|
|
302
|
+
word_list = [w + "é" for w in LoremProvider.word_list]
|
|
301
303
|
|
|
302
304
|
|
|
303
305
|
def safe_unicode(string):
|
|
304
|
-
|
|
306
|
+
"""Safely transform any object into utf8 decoded str"""
|
|
305
307
|
if string is None:
|
|
306
308
|
return None
|
|
307
|
-
return string.decode(
|
|
309
|
+
return string.decode("utf8") if isinstance(string, bytes) else str(string)
|
|
308
310
|
|
|
309
311
|
|
|
310
312
|
def id_or_404(object_id):
|
udata/wsgi.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: udata
|
|
3
|
-
Version: 9.1.2.
|
|
3
|
+
Version: 9.1.2.dev30382
|
|
4
4
|
Summary: Open data portal
|
|
5
5
|
Home-page: https://github.com/opendatateam/udata
|
|
6
6
|
Author: Opendata Team
|
|
@@ -137,6 +137,7 @@ It is collectively taken care of by members of the
|
|
|
137
137
|
|
|
138
138
|
## Current (in progress)
|
|
139
139
|
|
|
140
|
+
- Add linter and formatter with `pyproject.toml` config, add lint and formatting step in CI, add pre-commit hook to lint and format, update docs and lint and format the code [#3085](https://github.com/opendatateam/udata/pull/3085)
|
|
140
141
|
- Update pinned dependencies according to project dependencies, without updating any project dependencies [#3089](https://github.com/opendatateam/udata/pull/3089)
|
|
141
142
|
- Add "run" button to harvesters (configurable with `HARVEST_ENABLE_MANUAL_RUN`) [#3092](https://github.com/opendatateam/udata/pull/3092)
|
|
142
143
|
|
|
@@ -160,7 +161,7 @@ It is collectively taken care of by members of the
|
|
|
160
161
|
- Prevent tagging as spam owners' messages [#3071](https://github.com/opendatateam/udata/pull/3071)[#3076](https://github.com/opendatateam/udata/pull/3076)
|
|
161
162
|
- Add api endpoint /me/org_topics/ [#3070](https://github.com/opendatateam/udata/pull/3070)
|
|
162
163
|
- Expose dataservices in RDF catalog [#3058](https://github.com/opendatateam/udata/pull/3058) [#3075](https://github.com/opendatateam/udata/pull/3075)
|
|
163
|
-
- CORS: always returns 204 on OPTIONS request [#3046](https://github.com/opendatateam/udata/pull/3046)
|
|
164
|
+
- CORS: always returns 204 on OPTIONS request [#3046](https://github.com/opendatateam/udata/pull/3046)
|
|
164
165
|
|
|
165
166
|
## 9.0.0 (2024-06-07)
|
|
166
167
|
|