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/core/reuse/api.py
CHANGED
|
@@ -1,176 +1,175 @@
|
|
|
1
|
-
from bson.objectid import ObjectId
|
|
2
1
|
from datetime import datetime
|
|
3
2
|
|
|
3
|
+
from bson.objectid import ObjectId
|
|
4
4
|
from flask import request
|
|
5
5
|
|
|
6
|
-
from udata.api import
|
|
6
|
+
from udata.api import API, api, errors
|
|
7
7
|
from udata.api.parsers import ModelApiParser
|
|
8
8
|
from udata.auth import admin_permission
|
|
9
|
-
from udata.models import Dataset
|
|
10
|
-
from udata.utils import id_or_404
|
|
11
|
-
|
|
12
9
|
from udata.core.badges import api as badges_api
|
|
13
10
|
from udata.core.badges.fields import badge_fields
|
|
14
11
|
from udata.core.dataset.api_fields import dataset_ref_fields
|
|
15
12
|
from udata.core.followers.api import FollowAPI
|
|
16
13
|
from udata.core.storages.api import (
|
|
17
|
-
|
|
14
|
+
image_parser,
|
|
15
|
+
parse_uploaded_image,
|
|
16
|
+
uploaded_image_fields,
|
|
18
17
|
)
|
|
18
|
+
from udata.models import Dataset
|
|
19
|
+
from udata.utils import id_or_404
|
|
19
20
|
|
|
20
21
|
from .api_fields import (
|
|
21
|
-
reuse_fields,
|
|
22
|
-
|
|
22
|
+
reuse_fields,
|
|
23
|
+
reuse_page_fields,
|
|
23
24
|
reuse_suggestion_fields,
|
|
24
|
-
reuse_topic_fields
|
|
25
|
+
reuse_topic_fields,
|
|
26
|
+
reuse_type_fields,
|
|
25
27
|
)
|
|
28
|
+
from .constants import REUSE_TOPICS, REUSE_TYPES
|
|
26
29
|
from .forms import ReuseForm
|
|
27
30
|
from .models import Reuse
|
|
28
|
-
from .constants import REUSE_TYPES, REUSE_TOPICS
|
|
29
31
|
from .permissions import ReuseEditPermission
|
|
30
32
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
SUGGEST_SORTING = '-metrics.followers'
|
|
33
|
+
DEFAULT_SORTING = "-created_at"
|
|
34
|
+
SUGGEST_SORTING = "-metrics.followers"
|
|
34
35
|
|
|
35
36
|
|
|
36
37
|
class ReuseApiParser(ModelApiParser):
|
|
37
38
|
sorts = {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
39
|
+
"title": "title",
|
|
40
|
+
"created": "created_at",
|
|
41
|
+
"last_modified": "last_modified",
|
|
42
|
+
"datasets": "metrics.datasets",
|
|
43
|
+
"followers": "metrics.followers",
|
|
44
|
+
"views": "metrics.views",
|
|
44
45
|
}
|
|
45
46
|
|
|
46
47
|
def __init__(self):
|
|
47
48
|
super().__init__()
|
|
48
|
-
self.parser.add_argument(
|
|
49
|
-
self.parser.add_argument(
|
|
50
|
-
self.parser.add_argument(
|
|
51
|
-
self.parser.add_argument(
|
|
52
|
-
self.parser.add_argument(
|
|
53
|
-
self.parser.add_argument(
|
|
54
|
-
self.parser.add_argument(
|
|
49
|
+
self.parser.add_argument("dataset", type=str, location="args")
|
|
50
|
+
self.parser.add_argument("tag", type=str, location="args")
|
|
51
|
+
self.parser.add_argument("organization", type=str, location="args")
|
|
52
|
+
self.parser.add_argument("owner", type=str, location="args")
|
|
53
|
+
self.parser.add_argument("type", type=str, location="args")
|
|
54
|
+
self.parser.add_argument("topic", type=str, location="args")
|
|
55
|
+
self.parser.add_argument("featured", type=bool, location="args")
|
|
55
56
|
|
|
56
57
|
@staticmethod
|
|
57
58
|
def parse_filters(reuses, args):
|
|
58
|
-
if args.get(
|
|
59
|
+
if args.get("q"):
|
|
59
60
|
# Following code splits the 'q' argument by spaces to surround
|
|
60
61
|
# every word in it with quotes before rebuild it.
|
|
61
62
|
# This allows the search_text method to tokenise with an AND
|
|
62
63
|
# between tokens whereas an OR is used without it.
|
|
63
|
-
phrase_query =
|
|
64
|
+
phrase_query = " ".join([f'"{elem}"' for elem in args["q"].split(" ")])
|
|
64
65
|
reuses = reuses.search_text(phrase_query)
|
|
65
|
-
if args.get(
|
|
66
|
-
if not ObjectId.is_valid(args[
|
|
67
|
-
api.abort(400,
|
|
68
|
-
reuses = reuses.filter(datasets=args[
|
|
69
|
-
if args.get(
|
|
70
|
-
reuses = reuses.filter(featured=args[
|
|
71
|
-
if args.get(
|
|
72
|
-
reuses = reuses.filter(topic=args[
|
|
73
|
-
if args.get(
|
|
74
|
-
reuses = reuses.filter(type=args[
|
|
75
|
-
if args.get(
|
|
76
|
-
reuses = reuses.filter(tags=args[
|
|
77
|
-
if args.get(
|
|
78
|
-
if not ObjectId.is_valid(args[
|
|
79
|
-
api.abort(400,
|
|
80
|
-
reuses = reuses.filter(organization=args[
|
|
81
|
-
if args.get(
|
|
82
|
-
if not ObjectId.is_valid(args[
|
|
83
|
-
api.abort(400,
|
|
84
|
-
reuses = reuses.filter(owner=args[
|
|
66
|
+
if args.get("dataset"):
|
|
67
|
+
if not ObjectId.is_valid(args["dataset"]):
|
|
68
|
+
api.abort(400, "Dataset arg must be an identifier")
|
|
69
|
+
reuses = reuses.filter(datasets=args["dataset"])
|
|
70
|
+
if args.get("featured"):
|
|
71
|
+
reuses = reuses.filter(featured=args["featured"])
|
|
72
|
+
if args.get("topic"):
|
|
73
|
+
reuses = reuses.filter(topic=args["topic"])
|
|
74
|
+
if args.get("type"):
|
|
75
|
+
reuses = reuses.filter(type=args["type"])
|
|
76
|
+
if args.get("tag"):
|
|
77
|
+
reuses = reuses.filter(tags=args["tag"])
|
|
78
|
+
if args.get("organization"):
|
|
79
|
+
if not ObjectId.is_valid(args["organization"]):
|
|
80
|
+
api.abort(400, "Organization arg must be an identifier")
|
|
81
|
+
reuses = reuses.filter(organization=args["organization"])
|
|
82
|
+
if args.get("owner"):
|
|
83
|
+
if not ObjectId.is_valid(args["owner"]):
|
|
84
|
+
api.abort(400, "Owner arg must be an identifier")
|
|
85
|
+
reuses = reuses.filter(owner=args["owner"])
|
|
85
86
|
return reuses
|
|
86
87
|
|
|
87
88
|
|
|
88
|
-
ns = api.namespace(
|
|
89
|
+
ns = api.namespace("reuses", "Reuse related operations")
|
|
89
90
|
|
|
90
|
-
common_doc = {
|
|
91
|
-
'params': {'reuse': 'The reuse ID or slug'}
|
|
92
|
-
}
|
|
91
|
+
common_doc = {"params": {"reuse": "The reuse ID or slug"}}
|
|
93
92
|
|
|
94
93
|
reuse_parser = ReuseApiParser()
|
|
95
94
|
|
|
96
95
|
|
|
97
|
-
@ns.route(
|
|
96
|
+
@ns.route("/", endpoint="reuses")
|
|
98
97
|
class ReuseListAPI(API):
|
|
99
|
-
@api.doc(
|
|
98
|
+
@api.doc("list_reuses")
|
|
100
99
|
@api.expect(reuse_parser.parser)
|
|
101
100
|
@api.marshal_with(reuse_page_fields)
|
|
102
101
|
def get(self):
|
|
103
102
|
args = reuse_parser.parse()
|
|
104
103
|
reuses = Reuse.objects(deleted=None, private__ne=True)
|
|
105
104
|
reuses = reuse_parser.parse_filters(reuses, args)
|
|
106
|
-
sort = args[
|
|
107
|
-
return reuses.order_by(sort).paginate(args[
|
|
105
|
+
sort = args["sort"] or ("$text_score" if args["q"] else None) or DEFAULT_SORTING
|
|
106
|
+
return reuses.order_by(sort).paginate(args["page"], args["page_size"])
|
|
108
107
|
|
|
109
108
|
@api.secure
|
|
110
|
-
@api.doc(
|
|
109
|
+
@api.doc("create_reuse")
|
|
111
110
|
@api.expect(reuse_fields)
|
|
112
|
-
@api.response(400,
|
|
111
|
+
@api.response(400, "Validation error")
|
|
113
112
|
@api.marshal_with(reuse_fields)
|
|
114
113
|
def post(self):
|
|
115
|
-
|
|
114
|
+
"""Create a new object"""
|
|
116
115
|
form = api.validate(ReuseForm)
|
|
117
116
|
return form.save(), 201
|
|
118
117
|
|
|
119
118
|
|
|
120
|
-
@ns.route(
|
|
121
|
-
@api.response(404,
|
|
122
|
-
@api.response(410,
|
|
119
|
+
@ns.route("/<reuse:reuse>/", endpoint="reuse", doc=common_doc)
|
|
120
|
+
@api.response(404, "Reuse not found")
|
|
121
|
+
@api.response(410, "Reuse has been deleted")
|
|
123
122
|
class ReuseAPI(API):
|
|
124
|
-
@api.doc(
|
|
123
|
+
@api.doc("get_reuse")
|
|
125
124
|
@api.marshal_with(reuse_fields)
|
|
126
125
|
def get(self, reuse):
|
|
127
|
-
|
|
126
|
+
"""Fetch a given reuse"""
|
|
128
127
|
if reuse.deleted and not ReuseEditPermission(reuse).can():
|
|
129
|
-
api.abort(410,
|
|
128
|
+
api.abort(410, "This reuse has been deleted")
|
|
130
129
|
return reuse
|
|
131
130
|
|
|
132
131
|
@api.secure
|
|
133
|
-
@api.doc(
|
|
132
|
+
@api.doc("update_reuse")
|
|
134
133
|
@api.expect(reuse_fields)
|
|
135
134
|
@api.marshal_with(reuse_fields)
|
|
136
135
|
@api.response(400, errors.VALIDATION_ERROR)
|
|
137
136
|
def put(self, reuse):
|
|
138
|
-
|
|
139
|
-
request_deleted = request.json.get(
|
|
137
|
+
"""Update a given reuse"""
|
|
138
|
+
request_deleted = request.json.get("deleted", True)
|
|
140
139
|
if reuse.deleted and request_deleted is not None:
|
|
141
|
-
api.abort(410,
|
|
140
|
+
api.abort(410, "This reuse has been deleted")
|
|
142
141
|
ReuseEditPermission(reuse).test()
|
|
143
142
|
form = api.validate(ReuseForm, reuse)
|
|
144
143
|
return form.save()
|
|
145
144
|
|
|
146
145
|
@api.secure
|
|
147
|
-
@api.doc(
|
|
148
|
-
@api.response(204,
|
|
146
|
+
@api.doc("delete_reuse")
|
|
147
|
+
@api.response(204, "Reuse deleted")
|
|
149
148
|
def delete(self, reuse):
|
|
150
|
-
|
|
149
|
+
"""Delete a given reuse"""
|
|
151
150
|
if reuse.deleted:
|
|
152
|
-
api.abort(410,
|
|
151
|
+
api.abort(410, "This reuse has been deleted")
|
|
153
152
|
ReuseEditPermission(reuse).test()
|
|
154
153
|
reuse.deleted = datetime.utcnow()
|
|
155
154
|
reuse.save()
|
|
156
|
-
return
|
|
155
|
+
return "", 204
|
|
157
156
|
|
|
158
157
|
|
|
159
|
-
@ns.route(
|
|
158
|
+
@ns.route("/<reuse:reuse>/datasets/", endpoint="reuse_add_dataset")
|
|
160
159
|
class ReuseDatasetsAPI(API):
|
|
161
160
|
@api.secure
|
|
162
|
-
@api.doc(
|
|
161
|
+
@api.doc("reuse_add_dataset", **common_doc)
|
|
163
162
|
@api.expect(dataset_ref_fields)
|
|
164
|
-
@api.response(200,
|
|
163
|
+
@api.response(200, "The dataset is already present", reuse_fields)
|
|
165
164
|
@api.marshal_with(reuse_fields, code=201)
|
|
166
165
|
def post(self, reuse):
|
|
167
|
-
|
|
168
|
-
if
|
|
169
|
-
api.abort(400,
|
|
166
|
+
"""Add a dataset to a given reuse"""
|
|
167
|
+
if "id" not in request.json:
|
|
168
|
+
api.abort(400, "Expect a dataset identifier")
|
|
170
169
|
try:
|
|
171
|
-
dataset = Dataset.objects.get_or_404(id=id_or_404(request.json[
|
|
170
|
+
dataset = Dataset.objects.get_or_404(id=id_or_404(request.json["id"]))
|
|
172
171
|
except Dataset.DoesNotExist:
|
|
173
|
-
msg =
|
|
172
|
+
msg = "Dataset {0} does not exists".format(request.json["id"])
|
|
174
173
|
api.abort(404, msg)
|
|
175
174
|
if dataset in reuse.datasets:
|
|
176
175
|
return reuse
|
|
@@ -179,102 +178,102 @@ class ReuseDatasetsAPI(API):
|
|
|
179
178
|
return reuse, 201
|
|
180
179
|
|
|
181
180
|
|
|
182
|
-
@ns.route(
|
|
181
|
+
@ns.route("/badges/", endpoint="available_reuse_badges")
|
|
183
182
|
class AvailableDatasetBadgesAPI(API):
|
|
184
|
-
@api.doc(
|
|
183
|
+
@api.doc("available_reuse_badges")
|
|
185
184
|
def get(self):
|
|
186
|
-
|
|
185
|
+
"""List all available reuse badges and their labels"""
|
|
187
186
|
return Reuse.__badges__
|
|
188
187
|
|
|
189
188
|
|
|
190
|
-
@ns.route(
|
|
189
|
+
@ns.route("/<reuse:reuse>/badges/", endpoint="reuse_badges")
|
|
191
190
|
class ReuseBadgesAPI(API):
|
|
192
|
-
@api.doc(
|
|
191
|
+
@api.doc("add_reuse_badge", **common_doc)
|
|
193
192
|
@api.expect(badge_fields)
|
|
194
193
|
@api.marshal_with(badge_fields)
|
|
195
194
|
@api.secure(admin_permission)
|
|
196
195
|
def post(self, reuse):
|
|
197
|
-
|
|
196
|
+
"""Create a new badge for a given reuse"""
|
|
198
197
|
return badges_api.add(reuse)
|
|
199
198
|
|
|
200
199
|
|
|
201
|
-
@ns.route(
|
|
200
|
+
@ns.route("/<reuse:reuse>/badges/<badge_kind>/", endpoint="reuse_badge")
|
|
202
201
|
class ReuseBadgeAPI(API):
|
|
203
|
-
@api.doc(
|
|
202
|
+
@api.doc("delete_reuse_badge", **common_doc)
|
|
204
203
|
@api.secure(admin_permission)
|
|
205
204
|
def delete(self, reuse, badge_kind):
|
|
206
|
-
|
|
205
|
+
"""Delete a badge for a given reuse"""
|
|
207
206
|
return badges_api.remove(reuse, badge_kind)
|
|
208
207
|
|
|
209
208
|
|
|
210
|
-
@ns.route(
|
|
209
|
+
@ns.route("/<reuse:reuse>/featured/", endpoint="reuse_featured")
|
|
211
210
|
@api.doc(**common_doc)
|
|
212
211
|
class ReuseFeaturedAPI(API):
|
|
213
|
-
@api.doc(
|
|
212
|
+
@api.doc("feature_reuse")
|
|
214
213
|
@api.secure(admin_permission)
|
|
215
214
|
@api.marshal_with(reuse_fields)
|
|
216
215
|
def post(self, reuse):
|
|
217
|
-
|
|
216
|
+
"""Mark a reuse as featured"""
|
|
218
217
|
reuse.featured = True
|
|
219
218
|
reuse.save()
|
|
220
219
|
return reuse
|
|
221
220
|
|
|
222
|
-
@api.doc(
|
|
221
|
+
@api.doc("unfeature_reuse")
|
|
223
222
|
@api.secure(admin_permission)
|
|
224
223
|
@api.marshal_with(reuse_fields)
|
|
225
224
|
def delete(self, reuse):
|
|
226
|
-
|
|
225
|
+
"""Unmark a reuse as featured"""
|
|
227
226
|
reuse.featured = False
|
|
228
227
|
reuse.save()
|
|
229
228
|
return reuse
|
|
230
229
|
|
|
231
230
|
|
|
232
|
-
@ns.route(
|
|
233
|
-
@ns.doc(
|
|
234
|
-
|
|
235
|
-
|
|
231
|
+
@ns.route("/<id>/followers/", endpoint="reuse_followers")
|
|
232
|
+
@ns.doc(
|
|
233
|
+
get={"id": "list_reuse_followers"}, post={"id": "follow_reuse"}, delete={"id": "unfollow_reuse"}
|
|
234
|
+
)
|
|
236
235
|
class FollowReuseAPI(FollowAPI):
|
|
237
236
|
model = Reuse
|
|
238
237
|
|
|
239
238
|
|
|
240
239
|
suggest_parser = api.parser()
|
|
241
240
|
suggest_parser.add_argument(
|
|
242
|
-
|
|
243
|
-
|
|
241
|
+
"q", help="The string to autocomplete/suggest", location="args", required=True
|
|
242
|
+
)
|
|
244
243
|
suggest_parser.add_argument(
|
|
245
|
-
|
|
246
|
-
|
|
244
|
+
"size", type=int, help="The amount of suggestion to fetch", location="args", default=10
|
|
245
|
+
)
|
|
247
246
|
|
|
248
247
|
|
|
249
|
-
@ns.route(
|
|
248
|
+
@ns.route("/suggest/", endpoint="suggest_reuses")
|
|
250
249
|
class ReusesSuggestAPI(API):
|
|
251
|
-
@api.doc(
|
|
250
|
+
@api.doc("suggest_reuses")
|
|
252
251
|
@api.expect(suggest_parser)
|
|
253
252
|
@api.marshal_list_with(reuse_suggestion_fields)
|
|
254
253
|
def get(self):
|
|
255
|
-
|
|
254
|
+
"""Reuses suggest endpoint using mongoDB contains"""
|
|
256
255
|
args = suggest_parser.parse_args()
|
|
257
|
-
reuses = Reuse.objects(deleted=None, private__ne=True, title__icontains=args[
|
|
256
|
+
reuses = Reuse.objects(deleted=None, private__ne=True, title__icontains=args["q"])
|
|
258
257
|
return [
|
|
259
258
|
{
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
259
|
+
"id": reuse.id,
|
|
260
|
+
"title": reuse.title,
|
|
261
|
+
"slug": reuse.slug,
|
|
262
|
+
"image_url": reuse.image,
|
|
264
263
|
}
|
|
265
|
-
for reuse in reuses.order_by(SUGGEST_SORTING).limit(args[
|
|
264
|
+
for reuse in reuses.order_by(SUGGEST_SORTING).limit(args["size"])
|
|
266
265
|
]
|
|
267
266
|
|
|
268
267
|
|
|
269
|
-
@ns.route(
|
|
268
|
+
@ns.route("/<reuse:reuse>/image", endpoint="reuse_image")
|
|
270
269
|
@api.doc(**common_doc)
|
|
271
270
|
class ReuseImageAPI(API):
|
|
272
271
|
@api.secure
|
|
273
|
-
@api.doc(
|
|
272
|
+
@api.doc("reuse_image")
|
|
274
273
|
@api.expect(image_parser) # Swagger 2.0 does not support formData at path level
|
|
275
274
|
@api.marshal_with(uploaded_image_fields)
|
|
276
275
|
def post(self, reuse):
|
|
277
|
-
|
|
276
|
+
"""Upload a new reuse image"""
|
|
278
277
|
ReuseEditPermission(reuse).test()
|
|
279
278
|
parse_uploaded_image(reuse.image)
|
|
280
279
|
reuse.save()
|
|
@@ -282,21 +281,19 @@ class ReuseImageAPI(API):
|
|
|
282
281
|
return reuse
|
|
283
282
|
|
|
284
283
|
|
|
285
|
-
@ns.route(
|
|
284
|
+
@ns.route("/types/", endpoint="reuse_types")
|
|
286
285
|
class ReuseTypesAPI(API):
|
|
287
|
-
@api.doc(
|
|
286
|
+
@api.doc("reuse_types")
|
|
288
287
|
@api.marshal_list_with(reuse_type_fields)
|
|
289
288
|
def get(self):
|
|
290
|
-
|
|
291
|
-
return [{
|
|
292
|
-
for id, label in REUSE_TYPES.items()]
|
|
289
|
+
"""List all reuse types"""
|
|
290
|
+
return [{"id": id, "label": label} for id, label in REUSE_TYPES.items()]
|
|
293
291
|
|
|
294
292
|
|
|
295
|
-
@ns.route(
|
|
293
|
+
@ns.route("/topics/", endpoint="reuse_topics")
|
|
296
294
|
class ReuseTopicsAPI(API):
|
|
297
|
-
@api.doc(
|
|
295
|
+
@api.doc("reuse_topics")
|
|
298
296
|
@api.marshal_list_with(reuse_topic_fields)
|
|
299
297
|
def get(self):
|
|
300
|
-
|
|
301
|
-
return [{
|
|
302
|
-
for id, label in REUSE_TOPICS.items()]
|
|
298
|
+
"""List all reuse topics"""
|
|
299
|
+
return [{"id": id, "label": label} for id, label in REUSE_TOPICS.items()]
|