udata 9.1.2.dev30355__py2.py3-none-any.whl → 9.1.2.dev30454__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 +111 -134
- 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 +58 -55
- 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/cors.py +99 -0
- 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/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 +8 -6
- udata/tests/api/test_auth_api.py +395 -321
- udata/tests/api/test_base_api.py +33 -35
- 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 +79 -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_cors.py +62 -0
- 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.dev30454.dist-info}/METADATA +7 -3
- udata-9.1.2.dev30454.dist-info/RECORD +706 -0
- udata-9.1.2.dev30355.dist-info/RECORD +0 -704
- {udata-9.1.2.dev30355.dist-info → udata-9.1.2.dev30454.dist-info}/LICENSE +0 -0
- {udata-9.1.2.dev30355.dist-info → udata-9.1.2.dev30454.dist-info}/WHEEL +0 -0
- {udata-9.1.2.dev30355.dist-info → udata-9.1.2.dev30454.dist-info}/entry_points.txt +0 -0
- {udata-9.1.2.dev30355.dist-info → udata-9.1.2.dev30454.dist-info}/top_level.txt +0 -0
|
@@ -2,27 +2,20 @@ from flask import url_for
|
|
|
2
2
|
|
|
3
3
|
from udata.models import License, Organization
|
|
4
4
|
|
|
5
|
-
__all__ = (
|
|
6
|
-
'TerritoryDataset', 'ResourceBasedTerritoryDataset', 'TERRITORY_DATASETS'
|
|
7
|
-
)
|
|
5
|
+
__all__ = ("TerritoryDataset", "ResourceBasedTerritoryDataset", "TERRITORY_DATASETS")
|
|
8
6
|
|
|
9
7
|
|
|
10
|
-
TERRITORY_DATASETS = {
|
|
11
|
-
'commune': {},
|
|
12
|
-
'departement': {},
|
|
13
|
-
'region': {},
|
|
14
|
-
'country': {}
|
|
15
|
-
}
|
|
8
|
+
TERRITORY_DATASETS = {"commune": {}, "departement": {}, "region": {}, "country": {}}
|
|
16
9
|
|
|
17
10
|
|
|
18
11
|
class TerritoryDataset(object):
|
|
19
12
|
order = 0
|
|
20
|
-
id =
|
|
21
|
-
title =
|
|
22
|
-
organization_id =
|
|
23
|
-
url_template =
|
|
24
|
-
description =
|
|
25
|
-
license_id =
|
|
13
|
+
id = ""
|
|
14
|
+
title = ""
|
|
15
|
+
organization_id = ""
|
|
16
|
+
url_template = ""
|
|
17
|
+
description = ""
|
|
18
|
+
license_id = "fr-lo"
|
|
26
19
|
|
|
27
20
|
def __init__(self, territory):
|
|
28
21
|
self.territory = territory
|
|
@@ -33,8 +26,7 @@ class TerritoryDataset(object):
|
|
|
33
26
|
|
|
34
27
|
@property
|
|
35
28
|
def slug(self):
|
|
36
|
-
return
|
|
37
|
-
territory_id=self.territory.id, id=self.id)
|
|
29
|
+
return "{territory_id}:{id}".format(territory_id=self.territory.id, id=self.id)
|
|
38
30
|
|
|
39
31
|
@property
|
|
40
32
|
def organization(self):
|
|
@@ -46,19 +38,22 @@ class TerritoryDataset(object):
|
|
|
46
38
|
|
|
47
39
|
|
|
48
40
|
class ResourceBasedTerritoryDataset(TerritoryDataset):
|
|
49
|
-
dataset_id =
|
|
50
|
-
resource_id =
|
|
51
|
-
territory_attr =
|
|
52
|
-
csv_column =
|
|
41
|
+
dataset_id = ""
|
|
42
|
+
resource_id = ""
|
|
43
|
+
territory_attr = ""
|
|
44
|
+
csv_column = ""
|
|
53
45
|
|
|
54
46
|
def url_for(self, external=False):
|
|
55
|
-
return url_for(
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
47
|
+
return url_for(
|
|
48
|
+
"territories.territory_dataset_resource",
|
|
49
|
+
territory=self.territory,
|
|
50
|
+
dataset=self.dataset_id,
|
|
51
|
+
resource_id=self.resource_id,
|
|
52
|
+
territory_attr=self.territory_attr,
|
|
53
|
+
csv_column=self.csv_column,
|
|
54
|
+
_external=external,
|
|
55
|
+
)
|
|
56
|
+
|
|
62
57
|
url = property(url_for)
|
|
63
58
|
|
|
64
59
|
@property
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import logging
|
|
2
|
-
|
|
3
2
|
from datetime import datetime
|
|
4
3
|
|
|
5
|
-
|
|
6
|
-
from udata.auth import login_required, current_user
|
|
4
|
+
from udata.auth import current_user, login_required
|
|
7
5
|
from udata.models import Organization, User
|
|
8
6
|
|
|
9
7
|
from .models import Transfer
|
|
@@ -14,28 +12,27 @@ log = logging.getLogger(__name__)
|
|
|
14
12
|
|
|
15
13
|
@login_required
|
|
16
14
|
def request_transfer(subject, recipient, comment):
|
|
17
|
-
|
|
15
|
+
"""Initiate a transfer request"""
|
|
18
16
|
TransferPermission(subject).test()
|
|
19
17
|
if recipient == (subject.organization or subject.owner):
|
|
20
|
-
raise ValueError(
|
|
21
|
-
'Recipient should be different than the current owner')
|
|
18
|
+
raise ValueError("Recipient should be different than the current owner")
|
|
22
19
|
transfer = Transfer.objects.create(
|
|
23
20
|
owner=subject.organization or subject.owner,
|
|
24
21
|
recipient=recipient,
|
|
25
22
|
subject=subject,
|
|
26
|
-
comment=comment
|
|
23
|
+
comment=comment,
|
|
27
24
|
)
|
|
28
25
|
return transfer
|
|
29
26
|
|
|
30
27
|
|
|
31
28
|
@login_required
|
|
32
29
|
def accept_transfer(transfer, comment=None):
|
|
33
|
-
|
|
30
|
+
"""Accept an incoming a transfer request"""
|
|
34
31
|
TransferResponsePermission(transfer).test()
|
|
35
32
|
|
|
36
33
|
transfer.responded = datetime.utcnow()
|
|
37
34
|
transfer.responder = current_user._get_current_object()
|
|
38
|
-
transfer.status =
|
|
35
|
+
transfer.status = "accepted"
|
|
39
36
|
transfer.response_comment = comment
|
|
40
37
|
transfer.save()
|
|
41
38
|
|
|
@@ -53,12 +50,12 @@ def accept_transfer(transfer, comment=None):
|
|
|
53
50
|
|
|
54
51
|
@login_required
|
|
55
52
|
def refuse_transfer(transfer, comment=None):
|
|
56
|
-
|
|
53
|
+
"""Refuse an incoming a transfer request"""
|
|
57
54
|
TransferResponsePermission(transfer).test()
|
|
58
55
|
|
|
59
56
|
transfer.responded = datetime.utcnow()
|
|
60
57
|
transfer.responder = current_user._get_current_object()
|
|
61
|
-
transfer.status =
|
|
58
|
+
transfer.status = "refused"
|
|
62
59
|
transfer.response_comment = comment
|
|
63
60
|
transfer.save()
|
|
64
61
|
|
udata/features/transfer/api.py
CHANGED
|
@@ -1,40 +1,44 @@
|
|
|
1
1
|
from flask import request
|
|
2
2
|
|
|
3
|
-
from udata.api import
|
|
3
|
+
from udata.api import API, api, base_reference, fields
|
|
4
4
|
from udata.core.dataset.api_fields import dataset_ref_fields
|
|
5
5
|
from udata.core.organization.api_fields import org_ref_fields
|
|
6
6
|
from udata.core.reuse.api_fields import reuse_ref_fields
|
|
7
7
|
from udata.core.user.api_fields import user_ref_fields
|
|
8
|
-
from udata.models import
|
|
8
|
+
from udata.models import Dataset, Organization, Reuse, User, db
|
|
9
9
|
from udata.utils import id_or_404
|
|
10
10
|
|
|
11
|
-
from .actions import
|
|
11
|
+
from .actions import accept_transfer, refuse_transfer, request_transfer
|
|
12
12
|
from .models import TRANSFER_STATUS, Transfer
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
14
|
+
RESPONSE_TYPES = ["accept", "refuse"]
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
transfer_request_fields = api.model(
|
|
18
|
+
"TransferRequest",
|
|
19
|
+
{
|
|
20
|
+
"subject": fields.Nested(
|
|
21
|
+
base_reference, required=True, description="The transfered subject"
|
|
22
|
+
),
|
|
23
|
+
"recipient": fields.Nested(
|
|
24
|
+
base_reference,
|
|
25
|
+
required=True,
|
|
26
|
+
description=("The transfer recipient, " "either an user or an organization"),
|
|
27
|
+
),
|
|
28
|
+
"comment": fields.String(
|
|
29
|
+
description="An explanation about the transfer request", required=True
|
|
30
|
+
),
|
|
31
|
+
},
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
transfer_response_fields = api.model(
|
|
36
|
+
"TransferResponse",
|
|
37
|
+
{
|
|
38
|
+
"response": fields.String(description="The response", required=True, enum=RESPONSE_TYPES),
|
|
39
|
+
"comment": fields.String(description="An optional comment about the transfer response"),
|
|
40
|
+
},
|
|
41
|
+
)
|
|
38
42
|
|
|
39
43
|
person_mapping = {
|
|
40
44
|
User: user_ref_fields,
|
|
@@ -46,95 +50,98 @@ subject_mapping = {
|
|
|
46
50
|
Reuse: reuse_ref_fields,
|
|
47
51
|
}
|
|
48
52
|
|
|
49
|
-
transfer_fields = api.model(
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
53
|
+
transfer_fields = api.model(
|
|
54
|
+
"Transfer",
|
|
55
|
+
{
|
|
56
|
+
"id": fields.String(readonly=True, description="The transfer unique identifier"),
|
|
57
|
+
"owner": fields.Polymorph(
|
|
58
|
+
person_mapping,
|
|
59
|
+
readonly=True,
|
|
60
|
+
description=("The user or organization currently owning " "the transfered object"),
|
|
61
|
+
),
|
|
62
|
+
"recipient": fields.Polymorph(
|
|
63
|
+
person_mapping,
|
|
64
|
+
readonly=True,
|
|
65
|
+
description=("The user or organization receiving " "the transfered object"),
|
|
66
|
+
),
|
|
67
|
+
"subject": fields.Polymorph(
|
|
68
|
+
subject_mapping, readonly=True, description="The transfered object"
|
|
69
|
+
),
|
|
70
|
+
"comment": fields.String(readonly=True, description="A comment about the transfer request"),
|
|
71
|
+
"created": fields.ISODateTime(description="The transfer request date", readonly=True),
|
|
72
|
+
"status": fields.String(
|
|
73
|
+
enum=list(TRANSFER_STATUS), description="The current transfer request status"
|
|
74
|
+
),
|
|
75
|
+
"responded": fields.ISODateTime(description="The transfer response date", readonly=True),
|
|
76
|
+
"reponse_comment": fields.String(
|
|
77
|
+
readonly=True, description="A comment about the transfer response"
|
|
78
|
+
),
|
|
79
|
+
},
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
ns = api.namespace("transfer")
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@ns.route("/", endpoint="transfers")
|
|
80
87
|
class TransferRequestsAPI(API):
|
|
81
|
-
@api.doc(
|
|
88
|
+
@api.doc("list_transfers")
|
|
82
89
|
@api.marshal_list_with(transfer_fields)
|
|
83
90
|
def get(self):
|
|
84
|
-
|
|
91
|
+
"""List all transfer requests"""
|
|
85
92
|
pass
|
|
86
93
|
|
|
87
|
-
@api.doc(
|
|
94
|
+
@api.doc("request_transfer")
|
|
88
95
|
@api.expect(transfer_request_fields)
|
|
89
96
|
@api.marshal_with(transfer_fields)
|
|
90
97
|
def post(self):
|
|
91
|
-
|
|
98
|
+
"""Initiate transfer request"""
|
|
92
99
|
data = request.json
|
|
93
100
|
|
|
94
|
-
subject_model = db.resolve_model(data[
|
|
95
|
-
subject_id = data[
|
|
101
|
+
subject_model = db.resolve_model(data["subject"])
|
|
102
|
+
subject_id = data["subject"]["id"]
|
|
96
103
|
try:
|
|
97
104
|
subject = subject_model.objects.get(id=subject_id)
|
|
98
105
|
except subject_model.DoesNotExist:
|
|
99
106
|
msg = 'Unkown subject id "{0}"'.format(subject_id)
|
|
100
|
-
ns.abort(400, errors={
|
|
107
|
+
ns.abort(400, errors={"subject": msg})
|
|
101
108
|
|
|
102
|
-
recipient_model = db.resolve_model(data[
|
|
103
|
-
recipient_id = data[
|
|
109
|
+
recipient_model = db.resolve_model(data["recipient"])
|
|
110
|
+
recipient_id = data["recipient"]["id"]
|
|
104
111
|
try:
|
|
105
112
|
recipient = recipient_model.objects.get(id=recipient_id)
|
|
106
113
|
except recipient_model.DoesNotExist:
|
|
107
114
|
msg = 'Unkown recipient id "{0}"'.format(recipient_id)
|
|
108
|
-
ns.abort(400, errors={
|
|
115
|
+
ns.abort(400, errors={"recipient": msg})
|
|
109
116
|
|
|
110
|
-
comment = data.get(
|
|
117
|
+
comment = data.get("comment")
|
|
111
118
|
|
|
112
119
|
transfer = request_transfer(subject, recipient, comment)
|
|
113
120
|
|
|
114
121
|
return transfer, 201
|
|
115
122
|
|
|
116
123
|
|
|
117
|
-
@ns.route(
|
|
124
|
+
@ns.route("/<id>/", endpoint="transfer")
|
|
118
125
|
class TransferRequestAPI(API):
|
|
119
|
-
@api.doc(
|
|
126
|
+
@api.doc("get_transfer")
|
|
120
127
|
@api.marshal_with(transfer_fields)
|
|
121
128
|
def get(self, id):
|
|
122
|
-
|
|
129
|
+
"""Fetch a transfer request given its identifier"""
|
|
123
130
|
return Transfer.objects.get_or_404(id=id_or_404(id))
|
|
124
131
|
|
|
125
|
-
@api.doc(
|
|
132
|
+
@api.doc("respond_to_transfer")
|
|
126
133
|
@api.expect(transfer_response_fields)
|
|
127
134
|
@api.marshal_with(transfer_fields)
|
|
128
135
|
def post(self, id):
|
|
129
|
-
|
|
136
|
+
"""Respond to a transfer request"""
|
|
130
137
|
transfer = Transfer.objects.get_or_404(id=id_or_404(id))
|
|
131
138
|
|
|
132
139
|
data = request.json
|
|
133
|
-
comment = data.get(
|
|
140
|
+
comment = data.get("comment")
|
|
134
141
|
|
|
135
|
-
if data[
|
|
142
|
+
if data["response"] == "accept":
|
|
136
143
|
return accept_transfer(transfer, comment)
|
|
137
|
-
elif data[
|
|
144
|
+
elif data["response"] == "refuse":
|
|
138
145
|
return refuse_transfer(transfer, comment)
|
|
139
146
|
else:
|
|
140
147
|
raise ValueError()
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import logging
|
|
2
|
-
|
|
3
2
|
from datetime import datetime
|
|
4
3
|
|
|
5
4
|
from udata.i18n import lazy_gettext as _
|
|
@@ -7,13 +6,13 @@ from udata.mongo import db
|
|
|
7
6
|
|
|
8
7
|
log = logging.getLogger(__name__)
|
|
9
8
|
|
|
10
|
-
__all__ = (
|
|
9
|
+
__all__ = ("Transfer",)
|
|
11
10
|
|
|
12
11
|
|
|
13
12
|
TRANSFER_STATUS = {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
"pending": _("Pending"),
|
|
14
|
+
"accepted": _("Accepted"),
|
|
15
|
+
"refused": _("Refused"),
|
|
17
16
|
}
|
|
18
17
|
|
|
19
18
|
|
|
@@ -22,19 +21,19 @@ class Transfer(db.Document):
|
|
|
22
21
|
recipient = db.GenericReferenceField(required=True)
|
|
23
22
|
subject = db.GenericReferenceField(required=True)
|
|
24
23
|
comment = db.StringField()
|
|
25
|
-
status = db.StringField(choices=list(TRANSFER_STATUS), default=
|
|
24
|
+
status = db.StringField(choices=list(TRANSFER_STATUS), default="pending")
|
|
26
25
|
|
|
27
26
|
created = db.DateTimeField(default=datetime.utcnow, required=True)
|
|
28
27
|
|
|
29
28
|
responded = db.DateTimeField()
|
|
30
|
-
responder = db.ReferenceField(
|
|
29
|
+
responder = db.ReferenceField("User")
|
|
31
30
|
response_comment = db.StringField()
|
|
32
31
|
|
|
33
32
|
meta = {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
33
|
+
"indexes": [
|
|
34
|
+
"owner",
|
|
35
|
+
"recipient",
|
|
36
|
+
"created",
|
|
37
|
+
"status",
|
|
39
38
|
]
|
|
40
39
|
}
|
|
@@ -1,32 +1,36 @@
|
|
|
1
|
-
from udata.models import Transfer
|
|
2
|
-
from udata.features.notifications.actions import notifier
|
|
3
|
-
|
|
4
|
-
|
|
5
1
|
import logging
|
|
6
2
|
|
|
3
|
+
from udata.features.notifications.actions import notifier
|
|
4
|
+
from udata.models import Transfer
|
|
5
|
+
|
|
7
6
|
log = logging.getLogger(__name__)
|
|
8
7
|
|
|
9
8
|
|
|
10
|
-
@notifier(
|
|
9
|
+
@notifier("transfer_request")
|
|
11
10
|
def transfer_request_notifications(user):
|
|
12
|
-
|
|
11
|
+
"""Notify user about pending transfer requests"""
|
|
13
12
|
orgs = [o for o in user.organizations if o.is_member(user)]
|
|
14
13
|
notifications = []
|
|
15
14
|
|
|
16
|
-
qs = Transfer.objects(recipient__in=[user] + orgs, status=
|
|
15
|
+
qs = Transfer.objects(recipient__in=[user] + orgs, status="pending")
|
|
17
16
|
# Only fetch required fields for notification serialization
|
|
18
17
|
# Greatly improve performances and memory usage
|
|
19
|
-
qs = qs.only(
|
|
18
|
+
qs = qs.only("id", "created", "subject")
|
|
20
19
|
|
|
21
20
|
# Do not dereference subject (so it's a DBRef)
|
|
22
21
|
# Also improve performances and memory usage
|
|
23
22
|
for transfer in qs.no_dereference():
|
|
24
|
-
notifications.append(
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
23
|
+
notifications.append(
|
|
24
|
+
(
|
|
25
|
+
transfer.created,
|
|
26
|
+
{
|
|
27
|
+
"id": transfer.id,
|
|
28
|
+
"subject": {
|
|
29
|
+
"class": transfer.subject["_cls"].lower(),
|
|
30
|
+
"id": transfer.subject["_ref"].id,
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
)
|
|
34
|
+
)
|
|
31
35
|
|
|
32
36
|
return notifications
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
from udata.auth import Permission, UserNeed
|
|
2
|
-
|
|
3
|
-
from udata.models import User, Organization
|
|
4
|
-
|
|
5
2
|
from udata.core.organization.permissions import OrganizationAdminNeed
|
|
3
|
+
from udata.models import Organization, User
|
|
6
4
|
|
|
7
5
|
|
|
8
6
|
class TransferPermission(Permission):
|
|
9
|
-
|
|
7
|
+
"""Permissions to transfer an object assets"""
|
|
8
|
+
|
|
10
9
|
def __init__(self, subject):
|
|
11
10
|
if subject.organization:
|
|
12
11
|
need = OrganizationAdminNeed(subject.organization.id)
|
|
@@ -16,7 +15,8 @@ class TransferPermission(Permission):
|
|
|
16
15
|
|
|
17
16
|
|
|
18
17
|
class TransferResponsePermission(Permission):
|
|
19
|
-
|
|
18
|
+
"""Permissions to transfer an object assets"""
|
|
19
|
+
|
|
20
20
|
def __init__(self, transfer):
|
|
21
21
|
if isinstance(transfer.recipient, Organization):
|
|
22
22
|
need = OrganizationAdminNeed(transfer.recipient.id)
|
udata/forms/__init__.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
|
|
3
3
|
import wtforms_json
|
|
4
|
+
|
|
4
5
|
wtforms_json.init()
|
|
5
6
|
from flask_mongoengine.wtf import model_form # noqa
|
|
6
7
|
|
|
@@ -14,9 +15,11 @@ log = logging.getLogger(__name__)
|
|
|
14
15
|
|
|
15
16
|
class CommonFormMixin(object):
|
|
16
17
|
def process(self, formdata=None, obj=None, data=None, extra_filters=None, **kwargs):
|
|
17
|
-
|
|
18
|
+
"""Wrap the process method to store the current object instance"""
|
|
18
19
|
self._obj = obj
|
|
19
|
-
super(CommonFormMixin, self).process(
|
|
20
|
+
super(CommonFormMixin, self).process(
|
|
21
|
+
formdata, obj, data, extra_filters=extra_filters, **kwargs
|
|
22
|
+
)
|
|
20
23
|
|
|
21
24
|
|
|
22
25
|
class Form(CommonFormMixin, FlaskForm):
|