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/models.py
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
from blinker import Signal
|
|
2
|
-
from mongoengine.signals import
|
|
2
|
+
from mongoengine.signals import post_save, pre_save
|
|
3
3
|
from werkzeug.utils import cached_property
|
|
4
4
|
|
|
5
|
-
from udata.core.
|
|
5
|
+
from udata.core.owned import Owned, OwnedQuerySet
|
|
6
|
+
from udata.core.storages import default_image_basename, images
|
|
6
7
|
from udata.frontend.markdown import mdstrip
|
|
7
8
|
from udata.i18n import lazy_gettext as _
|
|
8
|
-
from udata.models import
|
|
9
|
-
from udata.utils import hash_url
|
|
9
|
+
from udata.models import BadgeMixin, WithMetrics, db
|
|
10
10
|
from udata.uris import endpoint_for
|
|
11
|
-
from udata.
|
|
11
|
+
from udata.utils import hash_url
|
|
12
|
+
|
|
12
13
|
from .constants import IMAGE_MAX_SIZE, IMAGE_SIZES, REUSE_TOPICS, REUSE_TYPES
|
|
13
14
|
|
|
14
|
-
__all__ = (
|
|
15
|
+
__all__ = ("Reuse",)
|
|
15
16
|
|
|
16
17
|
|
|
17
18
|
class ReuseQuerySet(OwnedQuerySet):
|
|
@@ -19,25 +20,23 @@ class ReuseQuerySet(OwnedQuerySet):
|
|
|
19
20
|
return self(private__ne=True, datasets__0__exists=True, deleted=None)
|
|
20
21
|
|
|
21
22
|
def hidden(self):
|
|
22
|
-
return self(db.Q(private=True) |
|
|
23
|
-
db.Q(datasets__0__exists=False) |
|
|
24
|
-
db.Q(deleted__ne=None))
|
|
23
|
+
return self(db.Q(private=True) | db.Q(datasets__0__exists=False) | db.Q(deleted__ne=None))
|
|
25
24
|
|
|
26
25
|
|
|
27
26
|
class Reuse(db.Datetimed, WithMetrics, BadgeMixin, Owned, db.Document):
|
|
28
27
|
title = db.StringField(required=True)
|
|
29
|
-
slug = db.SlugField(
|
|
30
|
-
|
|
28
|
+
slug = db.SlugField(
|
|
29
|
+
max_length=255, required=True, populate_from="title", update=True, follow=True
|
|
30
|
+
)
|
|
31
31
|
description = db.StringField(required=True)
|
|
32
32
|
type = db.StringField(required=True, choices=list(REUSE_TYPES))
|
|
33
33
|
url = db.StringField(required=True)
|
|
34
34
|
urlhash = db.StringField(required=True, unique=True)
|
|
35
35
|
image_url = db.StringField()
|
|
36
36
|
image = db.ImageField(
|
|
37
|
-
fs=images, basename=default_image_basename, max_size=IMAGE_MAX_SIZE,
|
|
38
|
-
|
|
39
|
-
datasets = db.ListField(
|
|
40
|
-
db.ReferenceField('Dataset', reverse_delete_rule=db.PULL))
|
|
37
|
+
fs=images, basename=default_image_basename, max_size=IMAGE_MAX_SIZE, thumbnails=IMAGE_SIZES
|
|
38
|
+
)
|
|
39
|
+
datasets = db.ListField(db.ReferenceField("Dataset", reverse_delete_rule=db.PULL))
|
|
41
40
|
tags = db.TagListField()
|
|
42
41
|
topic = db.StringField(required=True, choices=list(REUSE_TOPICS))
|
|
43
42
|
# badges = db.ListField(db.EmbeddedDocumentField(ReuseBadge))
|
|
@@ -51,28 +50,31 @@ class Reuse(db.Datetimed, WithMetrics, BadgeMixin, Owned, db.Document):
|
|
|
51
50
|
deleted = db.DateTimeField()
|
|
52
51
|
|
|
53
52
|
def __str__(self):
|
|
54
|
-
return self.title or
|
|
53
|
+
return self.title or ""
|
|
55
54
|
|
|
56
55
|
__badges__ = {}
|
|
57
56
|
|
|
58
57
|
__metrics_keys__ = [
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
58
|
+
"discussions",
|
|
59
|
+
"datasets",
|
|
60
|
+
"followers",
|
|
61
|
+
"views",
|
|
63
62
|
]
|
|
64
63
|
|
|
65
64
|
meta = {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
65
|
+
"indexes": [
|
|
66
|
+
"$title",
|
|
67
|
+
"created_at",
|
|
68
|
+
"last_modified",
|
|
69
|
+
"metrics.datasets",
|
|
70
|
+
"metrics.followers",
|
|
71
|
+
"metrics.views",
|
|
72
|
+
"urlhash",
|
|
73
|
+
]
|
|
74
|
+
+ Owned.meta["indexes"],
|
|
75
|
+
"ordering": ["-created_at"],
|
|
76
|
+
"queryset_class": ReuseQuerySet,
|
|
77
|
+
"auto_create_index_on_save": True,
|
|
76
78
|
}
|
|
77
79
|
|
|
78
80
|
before_save = Signal()
|
|
@@ -83,7 +85,7 @@ class Reuse(db.Datetimed, WithMetrics, BadgeMixin, Owned, db.Document):
|
|
|
83
85
|
after_delete = Signal()
|
|
84
86
|
on_delete = Signal()
|
|
85
87
|
|
|
86
|
-
verbose_name = _(
|
|
88
|
+
verbose_name = _("reuse")
|
|
87
89
|
|
|
88
90
|
@classmethod
|
|
89
91
|
def pre_save(cls, sender, document, **kwargs):
|
|
@@ -92,10 +94,10 @@ class Reuse(db.Datetimed, WithMetrics, BadgeMixin, Owned, db.Document):
|
|
|
92
94
|
|
|
93
95
|
@classmethod
|
|
94
96
|
def post_save(cls, sender, document, **kwargs):
|
|
95
|
-
if
|
|
97
|
+
if "post_save" in kwargs.get("ignores", []):
|
|
96
98
|
return
|
|
97
99
|
cls.after_save.send(document)
|
|
98
|
-
if kwargs.get(
|
|
100
|
+
if kwargs.get("created"):
|
|
99
101
|
cls.on_create.send(document)
|
|
100
102
|
else:
|
|
101
103
|
cls.on_update.send(document)
|
|
@@ -103,7 +105,7 @@ class Reuse(db.Datetimed, WithMetrics, BadgeMixin, Owned, db.Document):
|
|
|
103
105
|
cls.on_delete.send(document)
|
|
104
106
|
|
|
105
107
|
def url_for(self, *args, **kwargs):
|
|
106
|
-
return endpoint_for(
|
|
108
|
+
return endpoint_for("reuses.show", "api.reuse", reuse=self, *args, **kwargs)
|
|
107
109
|
|
|
108
110
|
display_url = property(url_for)
|
|
109
111
|
|
|
@@ -129,8 +131,8 @@ class Reuse(db.Datetimed, WithMetrics, BadgeMixin, Owned, db.Document):
|
|
|
129
131
|
|
|
130
132
|
def clean(self):
|
|
131
133
|
super(Reuse, self).clean()
|
|
132
|
-
|
|
133
|
-
if not self.urlhash or
|
|
134
|
+
"""Auto populate urlhash from url"""
|
|
135
|
+
if not self.urlhash or "url" in self._get_changed_fields():
|
|
134
136
|
self.urlhash = hash_url(self.url)
|
|
135
137
|
|
|
136
138
|
@classmethod
|
|
@@ -146,18 +148,18 @@ class Reuse(db.Datetimed, WithMetrics, BadgeMixin, Owned, db.Document):
|
|
|
146
148
|
@cached_property
|
|
147
149
|
def json_ld(self):
|
|
148
150
|
result = {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
151
|
+
"@context": "http://schema.org",
|
|
152
|
+
"@type": "CreativeWork",
|
|
153
|
+
"alternateName": self.slug,
|
|
154
|
+
"dateCreated": self.created_at.isoformat(),
|
|
155
|
+
"dateModified": self.last_modified.isoformat(),
|
|
156
|
+
"url": endpoint_for("reuses.show", "api.reuse", reuse=self, _external=True),
|
|
157
|
+
"name": self.title,
|
|
158
|
+
"isBasedOnUrl": self.url,
|
|
157
159
|
}
|
|
158
160
|
|
|
159
161
|
if self.description:
|
|
160
|
-
result[
|
|
162
|
+
result["description"] = mdstrip(self.description)
|
|
161
163
|
|
|
162
164
|
if self.organization:
|
|
163
165
|
author = self.organization.json_ld
|
|
@@ -167,26 +169,28 @@ class Reuse(db.Datetimed, WithMetrics, BadgeMixin, Owned, db.Document):
|
|
|
167
169
|
author = None
|
|
168
170
|
|
|
169
171
|
if author:
|
|
170
|
-
result[
|
|
172
|
+
result["author"] = author
|
|
171
173
|
|
|
172
174
|
return result
|
|
173
175
|
|
|
174
176
|
@property
|
|
175
177
|
def views_count(self):
|
|
176
|
-
return self.metrics.get(
|
|
178
|
+
return self.metrics.get("views", 0)
|
|
177
179
|
|
|
178
180
|
def count_datasets(self):
|
|
179
|
-
self.metrics[
|
|
180
|
-
self.save(signal_kwargs={
|
|
181
|
+
self.metrics["datasets"] = len(self.datasets)
|
|
182
|
+
self.save(signal_kwargs={"ignores": ["post_save"]})
|
|
181
183
|
|
|
182
184
|
def count_discussions(self):
|
|
183
185
|
from udata.models import Discussion
|
|
184
|
-
|
|
186
|
+
|
|
187
|
+
self.metrics["discussions"] = Discussion.objects(subject=self, closed=None).count()
|
|
185
188
|
self.save()
|
|
186
189
|
|
|
187
190
|
def count_followers(self):
|
|
188
191
|
from udata.models import Follow
|
|
189
|
-
|
|
192
|
+
|
|
193
|
+
self.metrics["followers"] = Follow.objects(until=None).followers(self).count()
|
|
190
194
|
self.save()
|
|
191
195
|
|
|
192
196
|
|
udata/core/reuse/permissions.py
CHANGED
udata/core/reuse/search.py
CHANGED
|
@@ -1,54 +1,57 @@
|
|
|
1
1
|
import datetime
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
|
|
3
|
+
from udata.core.reuse.api import DEFAULT_SORTING, ReuseApiParser
|
|
4
|
+
from udata.models import Organization, Reuse, User
|
|
5
5
|
from udata.search import (
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
BoolFilter,
|
|
7
|
+
Filter,
|
|
8
|
+
ModelSearchAdapter,
|
|
9
|
+
ModelTermsFilter,
|
|
10
|
+
register,
|
|
8
11
|
)
|
|
9
|
-
from udata.core.reuse.api import ReuseApiParser, DEFAULT_SORTING
|
|
10
12
|
from udata.utils import to_iso_datetime
|
|
11
13
|
|
|
12
|
-
|
|
13
|
-
__all__ = ('ReuseSearch', )
|
|
14
|
+
__all__ = ("ReuseSearch",)
|
|
14
15
|
|
|
15
16
|
|
|
16
17
|
@register
|
|
17
18
|
class ReuseSearch(ModelSearchAdapter):
|
|
18
19
|
model = Reuse
|
|
19
|
-
search_url =
|
|
20
|
+
search_url = "reuses/"
|
|
20
21
|
|
|
21
22
|
sorts = {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
"created": "created_at",
|
|
24
|
+
"datasets": "metrics.datasets",
|
|
25
|
+
"followers": "metrics.followers",
|
|
26
|
+
"views": "metrics.views",
|
|
26
27
|
}
|
|
27
28
|
|
|
28
29
|
filters = {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
30
|
+
"tag": Filter(),
|
|
31
|
+
"organization": ModelTermsFilter(model=Organization),
|
|
32
|
+
"owner": ModelTermsFilter(model=User),
|
|
33
|
+
"type": Filter(),
|
|
34
|
+
"badge": Filter(),
|
|
35
|
+
"featured": BoolFilter(),
|
|
36
|
+
"topic": Filter(),
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
@classmethod
|
|
39
40
|
def is_indexable(cls, reuse):
|
|
40
|
-
return
|
|
41
|
-
len(reuse.datasets) > 0 and
|
|
42
|
-
not reuse.private)
|
|
41
|
+
return reuse.deleted is None and len(reuse.datasets) > 0 and not reuse.private
|
|
43
42
|
|
|
44
43
|
@classmethod
|
|
45
44
|
def mongo_search(cls, args):
|
|
46
45
|
reuses = Reuse.objects(deleted=None, private__ne=True)
|
|
47
46
|
reuses = ReuseApiParser.parse_filters(reuses, args)
|
|
48
47
|
|
|
49
|
-
sort =
|
|
50
|
-
|
|
51
|
-
|
|
48
|
+
sort = (
|
|
49
|
+
cls.parse_sort(args["sort"])
|
|
50
|
+
or ("$text_score" if args["q"] else None)
|
|
51
|
+
or DEFAULT_SORTING
|
|
52
|
+
)
|
|
53
|
+
offset = (args["page"] - 1) * args["page_size"]
|
|
54
|
+
return reuses.order_by(sort).skip(offset).limit(args["page_size"]), reuses.count()
|
|
52
55
|
|
|
53
56
|
@classmethod
|
|
54
57
|
def serialize(cls, reuse):
|
|
@@ -57,10 +60,10 @@ class ReuseSearch(ModelSearchAdapter):
|
|
|
57
60
|
if reuse.organization:
|
|
58
61
|
org = Organization.objects(id=reuse.organization.id).first()
|
|
59
62
|
organization = {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
63
|
+
"id": str(org.id),
|
|
64
|
+
"name": org.name,
|
|
65
|
+
"public_service": 1 if org.public_service else 0,
|
|
66
|
+
"followers": org.metrics.get("followers", 0),
|
|
64
67
|
}
|
|
65
68
|
elif reuse.owner:
|
|
66
69
|
owner = User.objects(id=reuse.owner.id).first()
|
|
@@ -70,20 +73,20 @@ class ReuseSearch(ModelSearchAdapter):
|
|
|
70
73
|
extras[key] = to_iso_datetime(value) if isinstance(value, datetime.datetime) else value
|
|
71
74
|
|
|
72
75
|
return {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
76
|
+
"id": str(reuse.id),
|
|
77
|
+
"title": reuse.title,
|
|
78
|
+
"description": reuse.description,
|
|
79
|
+
"url": reuse.url,
|
|
80
|
+
"created_at": to_iso_datetime(reuse.created_at),
|
|
81
|
+
"views": reuse.metrics.get("views", 0),
|
|
82
|
+
"followers": reuse.metrics.get("followers", 0),
|
|
83
|
+
"datasets": reuse.metrics.get("datasets", 0),
|
|
84
|
+
"featured": 1 if reuse.featured else 0,
|
|
85
|
+
"organization": organization,
|
|
86
|
+
"owner": str(owner.id) if owner else None,
|
|
87
|
+
"type": reuse.type,
|
|
88
|
+
"topic": reuse.topic,
|
|
89
|
+
"tags": reuse.tags,
|
|
90
|
+
"badges": [badge.kind for badge in reuse.badges],
|
|
91
|
+
"extras": extras,
|
|
89
92
|
}
|
udata/core/reuse/signals.py
CHANGED
udata/core/reuse/tasks.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from udata import mail
|
|
2
|
-
from udata.i18n import lazy_gettext as _
|
|
3
2
|
from udata.core import storages
|
|
3
|
+
from udata.i18n import lazy_gettext as _
|
|
4
4
|
from udata.models import Activity, Discussion, Follow, Transfer
|
|
5
5
|
from udata.tasks import get_logger, job, task
|
|
6
6
|
|
|
@@ -9,10 +9,10 @@ from .models import Reuse
|
|
|
9
9
|
log = get_logger(__name__)
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
@job(
|
|
12
|
+
@job("purge-reuses")
|
|
13
13
|
def purge_reuses(self):
|
|
14
14
|
for reuse in Reuse.objects(deleted__ne=None):
|
|
15
|
-
log.info(f
|
|
15
|
+
log.info(f"Purging reuse {reuse}")
|
|
16
16
|
# Remove followers
|
|
17
17
|
Follow.objects(following=reuse).delete()
|
|
18
18
|
# Remove discussions
|
|
@@ -42,5 +42,4 @@ def notify_new_reuse(reuse_id):
|
|
|
42
42
|
else:
|
|
43
43
|
recipients = None
|
|
44
44
|
if recipients:
|
|
45
|
-
mail.send(_(
|
|
46
|
-
dataset=dataset)
|
|
45
|
+
mail.send(_("New reuse"), recipients, "new_reuse", reuse=reuse, dataset=dataset)
|
udata/core/site/api.py
CHANGED
|
@@ -1,115 +1,112 @@
|
|
|
1
1
|
from bson import ObjectId
|
|
2
|
-
|
|
3
|
-
from flask import request, redirect, url_for, json, make_response
|
|
2
|
+
from flask import json, make_response, redirect, request, url_for
|
|
4
3
|
from mongoengine import Q
|
|
5
4
|
|
|
6
|
-
from udata.api import
|
|
5
|
+
from udata.api import API, api, fields
|
|
7
6
|
from udata.auth import admin_permission
|
|
8
7
|
from udata.core.dataservices.models import Dataservice
|
|
9
|
-
from udata.models import Dataset, Reuse
|
|
10
|
-
from udata.utils import multi_to_dict
|
|
11
|
-
from udata.rdf import (
|
|
12
|
-
CONTEXT, RDF_EXTENSIONS,
|
|
13
|
-
negociate_content, graph_response
|
|
14
|
-
)
|
|
15
|
-
|
|
16
8
|
from udata.core.dataset.api_fields import dataset_fields
|
|
17
9
|
from udata.core.reuse.api_fields import reuse_fields
|
|
10
|
+
from udata.models import Dataset, Reuse
|
|
11
|
+
from udata.rdf import CONTEXT, RDF_EXTENSIONS, graph_response, negociate_content
|
|
12
|
+
from udata.utils import multi_to_dict
|
|
18
13
|
|
|
19
14
|
from .models import current_site
|
|
20
15
|
from .rdf import build_catalog
|
|
21
16
|
|
|
22
|
-
site_fields = api.model(
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
description=
|
|
27
|
-
|
|
28
|
-
}
|
|
17
|
+
site_fields = api.model(
|
|
18
|
+
"Site",
|
|
19
|
+
{
|
|
20
|
+
"id": fields.String(description="The Site unique identifier", required=True),
|
|
21
|
+
"title": fields.String(description="The site display title", required=True),
|
|
22
|
+
"metrics": fields.Raw(
|
|
23
|
+
attribute=lambda o: o.get_metrics(), description="The associated metrics", default={}
|
|
24
|
+
),
|
|
25
|
+
},
|
|
26
|
+
)
|
|
29
27
|
|
|
30
28
|
|
|
31
|
-
@api.route(
|
|
29
|
+
@api.route("/site/", endpoint="site")
|
|
32
30
|
class SiteAPI(API):
|
|
33
|
-
|
|
34
|
-
@api.doc(id='get_site')
|
|
31
|
+
@api.doc(id="get_site")
|
|
35
32
|
@api.marshal_with(site_fields)
|
|
36
33
|
def get(self):
|
|
37
|
-
|
|
34
|
+
"""Site-wide variables"""
|
|
38
35
|
return current_site
|
|
39
36
|
|
|
40
37
|
|
|
41
|
-
@api.route(
|
|
38
|
+
@api.route("/site/home/datasets/", endpoint="home_datasets")
|
|
42
39
|
class SiteHomeDatasetsAPI(API):
|
|
43
|
-
@api.doc(
|
|
40
|
+
@api.doc("get_home_datasets")
|
|
44
41
|
# @api.secure(admin_permission)
|
|
45
42
|
@api.marshal_list_with(dataset_fields)
|
|
46
43
|
def get(self):
|
|
47
|
-
|
|
44
|
+
"""List homepage datasets"""
|
|
48
45
|
return current_site.settings.home_datasets
|
|
49
46
|
|
|
50
47
|
@api.secure(admin_permission)
|
|
51
|
-
@api.doc(
|
|
52
|
-
@api.expect(([str],
|
|
48
|
+
@api.doc("set_home_datasets")
|
|
49
|
+
@api.expect(([str], "Dataset IDs to put in homepage"))
|
|
53
50
|
@api.marshal_list_with(dataset_fields)
|
|
54
51
|
def put(self):
|
|
55
|
-
|
|
52
|
+
"""Set the homepage datasets editorial selection"""
|
|
56
53
|
if not isinstance(request.json, list):
|
|
57
|
-
api.abort(400,
|
|
54
|
+
api.abort(400, "Expect a list of dataset IDs")
|
|
58
55
|
ids = [ObjectId(id) for id in request.json]
|
|
59
56
|
current_site.settings.home_datasets = Dataset.objects.bulk_list(ids)
|
|
60
57
|
current_site.save()
|
|
61
58
|
return current_site.settings.home_datasets
|
|
62
59
|
|
|
63
60
|
|
|
64
|
-
@api.route(
|
|
61
|
+
@api.route("/site/home/reuses/", endpoint="home_reuses")
|
|
65
62
|
class SiteHomeReusesAPI(API):
|
|
66
|
-
@api.doc(
|
|
63
|
+
@api.doc("get_home_reuses")
|
|
67
64
|
@api.secure(admin_permission)
|
|
68
65
|
@api.marshal_list_with(reuse_fields)
|
|
69
66
|
def get(self):
|
|
70
|
-
|
|
67
|
+
"""List homepage featured reuses"""
|
|
71
68
|
return current_site.settings.home_reuses
|
|
72
69
|
|
|
73
70
|
@api.secure(admin_permission)
|
|
74
|
-
@api.doc(
|
|
75
|
-
@api.expect(([str],
|
|
71
|
+
@api.doc("set_home_reuses")
|
|
72
|
+
@api.expect(([str], "Reuse IDs to put in homepage"))
|
|
76
73
|
@api.marshal_list_with(reuse_fields)
|
|
77
74
|
def put(self):
|
|
78
|
-
|
|
75
|
+
"""Set the homepage reuses editorial selection"""
|
|
79
76
|
if not isinstance(request.json, list):
|
|
80
|
-
api.abort(400,
|
|
77
|
+
api.abort(400, "Expect a list of reuse IDs")
|
|
81
78
|
ids = [ObjectId(id) for id in request.json]
|
|
82
79
|
current_site.settings.home_reuses = Reuse.objects.bulk_list(ids)
|
|
83
80
|
current_site.save()
|
|
84
81
|
return current_site.settings.home_reuses
|
|
85
82
|
|
|
86
83
|
|
|
87
|
-
@api.route(
|
|
84
|
+
@api.route("/site/data.<format>", endpoint="site_dataportal")
|
|
88
85
|
class SiteDataPortal(API):
|
|
89
86
|
def get(self, format):
|
|
90
|
-
|
|
91
|
-
url = url_for(
|
|
87
|
+
"""Root RDF endpoint with content negociation handling"""
|
|
88
|
+
url = url_for("api.site_rdf_catalog_format", format=format)
|
|
92
89
|
return redirect(url)
|
|
93
90
|
|
|
94
91
|
|
|
95
|
-
@api.route(
|
|
92
|
+
@api.route("/site/catalog", endpoint="site_rdf_catalog")
|
|
96
93
|
class SiteRdfCatalog(API):
|
|
97
94
|
def get(self):
|
|
98
|
-
|
|
95
|
+
"""Root RDF endpoint with content negociation handling"""
|
|
99
96
|
format = RDF_EXTENSIONS[negociate_content()]
|
|
100
|
-
url = url_for(
|
|
97
|
+
url = url_for("api.site_rdf_catalog_format", format=format)
|
|
101
98
|
return redirect(url)
|
|
102
99
|
|
|
103
100
|
|
|
104
|
-
@api.route(
|
|
101
|
+
@api.route("/site/catalog.<format>", endpoint="site_rdf_catalog_format")
|
|
105
102
|
class SiteRdfCatalogFormat(API):
|
|
106
103
|
def get(self, format):
|
|
107
104
|
params = multi_to_dict(request.args)
|
|
108
|
-
page = int(params.get(
|
|
109
|
-
page_size = int(params.get(
|
|
105
|
+
page = int(params.get("page", 1))
|
|
106
|
+
page_size = int(params.get("page_size", 100))
|
|
110
107
|
datasets = Dataset.objects.visible()
|
|
111
|
-
if
|
|
112
|
-
datasets = datasets.filter(tags=params.get(
|
|
108
|
+
if "tag" in params:
|
|
109
|
+
datasets = datasets.filter(tags=params.get("tag", ""))
|
|
113
110
|
datasets = datasets.paginate(page, page_size)
|
|
114
111
|
|
|
115
112
|
# We need to add Dataservice to the catalog.
|
|
@@ -119,14 +116,14 @@ class SiteRdfCatalogFormat(API):
|
|
|
119
116
|
# - Do not duplicate the datasets (each dataset is present once in the catalog)
|
|
120
117
|
# - Do not duplicate the dataservices (each dataservice is present once in the catalog)
|
|
121
118
|
# - Every referenced dataset for one dataservices present on the page (hard to do)
|
|
122
|
-
#
|
|
119
|
+
#
|
|
123
120
|
# Multiple solutions are possible but none check all the constraints.
|
|
124
121
|
# The selected one is to put all the dataservices referencing at least one of the dataset on
|
|
125
122
|
# the page at the end of it. It means dataservices could be duplicated (present on multiple pages)
|
|
126
123
|
# and these dataservices may referenced some datasets not present in the current page. It's working
|
|
127
124
|
# if somebody is doing the same thing as us (keeping the list of all the datasets IDs for the entire catalog then
|
|
128
125
|
# listing all dataservices in a second pass)
|
|
129
|
-
# Another option is to do some tricky Mongo requests to order/group datasets by their presence in some dataservices but
|
|
126
|
+
# Another option is to do some tricky Mongo requests to order/group datasets by their presence in some dataservices but
|
|
130
127
|
# it could be really hard to do with a n..n relation.
|
|
131
128
|
# Let's keep this solution simple right now and iterate on it in the future.
|
|
132
129
|
dataservices_filter = Q(datasets__in=[d.id for d in datasets])
|
|
@@ -143,9 +140,9 @@ class SiteRdfCatalogFormat(API):
|
|
|
143
140
|
return make_response(*graph_response(catalog, format))
|
|
144
141
|
|
|
145
142
|
|
|
146
|
-
@api.route(
|
|
143
|
+
@api.route("/site/context.jsonld", endpoint="site_jsonld_context")
|
|
147
144
|
class SiteJsonLdContext(API):
|
|
148
145
|
def get(self):
|
|
149
146
|
response = make_response(json.dumps(CONTEXT))
|
|
150
|
-
response.headers[
|
|
147
|
+
response.headers["Content-Type"] = "application/ld+json"
|
|
151
148
|
return response
|
udata/core/site/factories.py
CHANGED
udata/core/site/forms.py
CHANGED
|
@@ -3,13 +3,12 @@ from udata.i18n import lazy_gettext as _
|
|
|
3
3
|
|
|
4
4
|
from .models import Site
|
|
5
5
|
|
|
6
|
-
__all__ = (
|
|
6
|
+
__all__ = ("SiteConfigForm",)
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
class SiteConfigForm(ModelForm):
|
|
10
10
|
model_class = Site
|
|
11
11
|
|
|
12
|
-
title = fields.StringField(_(
|
|
13
|
-
keywords = fields.TagField(_(
|
|
14
|
-
feed_size = fields.IntegerField(
|
|
15
|
-
_('Feed size'), description=_('Number of elements in feeds'))
|
|
12
|
+
title = fields.StringField(_("Title"), [validators.DataRequired()])
|
|
13
|
+
keywords = fields.TagField(_("Tags"), description=_("Some SEO keywords"))
|
|
14
|
+
feed_size = fields.IntegerField(_("Feed size"), description=_("Number of elements in feeds"))
|