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
udata/core/user/api.py
CHANGED
|
@@ -1,23 +1,22 @@
|
|
|
1
1
|
from flask_security import current_user, logout_user
|
|
2
2
|
from slugify import slugify
|
|
3
3
|
|
|
4
|
-
from udata.api import
|
|
4
|
+
from udata.api import API, api
|
|
5
5
|
from udata.api.parsers import ModelApiParser
|
|
6
|
-
from udata.core import storages
|
|
7
6
|
from udata.auth import admin_permission
|
|
8
|
-
from udata.
|
|
9
|
-
|
|
10
|
-
from udata.core.dataset.api_fields import (
|
|
11
|
-
community_resource_fields, dataset_fields
|
|
12
|
-
)
|
|
13
|
-
from udata.core.followers.api import FollowAPI
|
|
7
|
+
from udata.core import storages
|
|
8
|
+
from udata.core.dataset.api_fields import community_resource_fields, dataset_fields
|
|
14
9
|
from udata.core.discussions.actions import discussions_for
|
|
15
10
|
from udata.core.discussions.api import discussion_fields
|
|
11
|
+
from udata.core.followers.api import FollowAPI
|
|
16
12
|
from udata.core.reuse.api_fields import reuse_fields
|
|
17
13
|
from udata.core.storages.api import (
|
|
18
|
-
|
|
14
|
+
image_parser,
|
|
15
|
+
parse_uploaded_image,
|
|
16
|
+
uploaded_image_fields,
|
|
19
17
|
)
|
|
20
18
|
from udata.core.user.models import Role
|
|
19
|
+
from udata.models import CommunityResource, Dataset, Reuse, User
|
|
21
20
|
|
|
22
21
|
from .api_fields import (
|
|
23
22
|
apikey_fields,
|
|
@@ -26,331 +25,332 @@ from .api_fields import (
|
|
|
26
25
|
user_fields,
|
|
27
26
|
user_page_fields,
|
|
28
27
|
user_role_fields,
|
|
29
|
-
user_suggestion_fields
|
|
28
|
+
user_suggestion_fields,
|
|
30
29
|
)
|
|
31
|
-
from .forms import
|
|
32
|
-
|
|
30
|
+
from .forms import UserProfileAdminForm, UserProfileForm
|
|
33
31
|
|
|
34
|
-
DEFAULT_SORTING =
|
|
32
|
+
DEFAULT_SORTING = "-created_at"
|
|
35
33
|
|
|
36
34
|
|
|
37
35
|
class UserApiParser(ModelApiParser):
|
|
38
36
|
sorts = {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
37
|
+
"last_name": "last_name",
|
|
38
|
+
"first_name": "first_name",
|
|
39
|
+
"datasets": "metrics.datasets",
|
|
40
|
+
"reuses": "metrics.reuses",
|
|
41
|
+
"followers": "metrics.followers",
|
|
42
|
+
"views": "metrics.views",
|
|
43
|
+
"created": "created_at",
|
|
46
44
|
}
|
|
47
45
|
|
|
48
46
|
|
|
49
|
-
ns = api.namespace(
|
|
50
|
-
me = api.namespace(
|
|
47
|
+
ns = api.namespace("users", "User related operations")
|
|
48
|
+
me = api.namespace("me", "Connected user related operations")
|
|
51
49
|
|
|
52
50
|
user_parser = UserApiParser()
|
|
53
51
|
|
|
54
52
|
filter_parser = api.parser()
|
|
55
53
|
filter_parser.add_argument(
|
|
56
|
-
|
|
57
|
-
|
|
54
|
+
"q", type=str, help="The string to filter items", location="args", required=False
|
|
55
|
+
)
|
|
58
56
|
|
|
59
57
|
|
|
60
|
-
@me.route(
|
|
58
|
+
@me.route("/", endpoint="me")
|
|
61
59
|
class MeAPI(API):
|
|
62
60
|
@api.secure
|
|
63
|
-
@api.doc(
|
|
61
|
+
@api.doc("get_me")
|
|
64
62
|
@api.marshal_with(me_fields)
|
|
65
63
|
def get(self):
|
|
66
|
-
|
|
64
|
+
"""Fetch the current user (me) identity"""
|
|
67
65
|
return current_user._get_current_object()
|
|
68
66
|
|
|
69
67
|
@api.secure
|
|
70
|
-
@api.doc(
|
|
68
|
+
@api.doc("update_me")
|
|
71
69
|
@api.expect(me_fields)
|
|
72
70
|
@api.marshal_with(me_fields)
|
|
73
|
-
@api.response(400,
|
|
71
|
+
@api.response(400, "Validation error")
|
|
74
72
|
def put(self, **kwargs):
|
|
75
|
-
|
|
73
|
+
"""Update my profile"""
|
|
76
74
|
user = current_user._get_current_object()
|
|
77
75
|
form = api.validate(UserProfileForm, user)
|
|
78
76
|
return form.save()
|
|
79
77
|
|
|
80
78
|
@api.secure
|
|
81
|
-
@api.doc(
|
|
82
|
-
@api.response(204,
|
|
79
|
+
@api.doc("delete_me")
|
|
80
|
+
@api.response(204, "Object deleted")
|
|
83
81
|
def delete(self, **kwargs):
|
|
84
|
-
|
|
82
|
+
"""Delete my profile"""
|
|
85
83
|
user = current_user._get_current_object()
|
|
86
84
|
user.mark_as_deleted()
|
|
87
85
|
logout_user()
|
|
88
|
-
return
|
|
86
|
+
return "", 204
|
|
89
87
|
|
|
90
88
|
|
|
91
|
-
@me.route(
|
|
89
|
+
@me.route("/avatar", endpoint="my_avatar")
|
|
92
90
|
class AvatarAPI(API):
|
|
93
91
|
@api.secure
|
|
94
|
-
@api.doc(
|
|
92
|
+
@api.doc("my_avatar")
|
|
95
93
|
@api.expect(image_parser)
|
|
96
94
|
@api.marshal_with(uploaded_image_fields)
|
|
97
95
|
def post(self):
|
|
98
|
-
|
|
96
|
+
"""Upload a new avatar"""
|
|
99
97
|
parse_uploaded_image(current_user.avatar)
|
|
100
98
|
current_user.save()
|
|
101
|
-
return {
|
|
99
|
+
return {"image": current_user.avatar}
|
|
102
100
|
|
|
103
101
|
|
|
104
|
-
@me.route(
|
|
102
|
+
@me.route("/reuses/", endpoint="my_reuses")
|
|
105
103
|
class MyReusesAPI(API):
|
|
106
104
|
@api.secure
|
|
107
|
-
@api.doc(
|
|
105
|
+
@api.doc("my_reuses")
|
|
108
106
|
@api.marshal_list_with(reuse_fields)
|
|
109
107
|
def get(self):
|
|
110
|
-
|
|
108
|
+
"""List all my reuses (including private ones)"""
|
|
111
109
|
return list(Reuse.objects.owned_by(current_user.id))
|
|
112
110
|
|
|
113
111
|
|
|
114
|
-
@me.route(
|
|
112
|
+
@me.route("/datasets/", endpoint="my_datasets")
|
|
115
113
|
class MyDatasetsAPI(API):
|
|
116
114
|
@api.secure
|
|
117
|
-
@api.doc(
|
|
115
|
+
@api.doc("my_datasets")
|
|
118
116
|
@api.marshal_list_with(dataset_fields)
|
|
119
117
|
def get(self):
|
|
120
|
-
|
|
118
|
+
"""List all my datasets (including private ones)"""
|
|
121
119
|
return list(Dataset.objects.owned_by(current_user.id))
|
|
122
120
|
|
|
123
121
|
|
|
124
|
-
@me.route(
|
|
122
|
+
@me.route("/metrics/", endpoint="my_metrics")
|
|
125
123
|
class MyMetricsAPI(API):
|
|
126
124
|
@api.secure
|
|
127
|
-
@api.doc(
|
|
125
|
+
@api.doc("my_metrics")
|
|
128
126
|
@api.marshal_list_with(me_metrics_fields)
|
|
129
127
|
def get(self):
|
|
130
|
-
|
|
128
|
+
"""Fetch the current user (me) metrics"""
|
|
131
129
|
return current_user._get_current_object()
|
|
132
130
|
|
|
133
131
|
|
|
134
|
-
@me.route(
|
|
132
|
+
@me.route("/org_datasets/", endpoint="my_org_datasets")
|
|
135
133
|
class MyOrgDatasetsAPI(API):
|
|
136
134
|
@api.secure
|
|
137
|
-
@api.doc(
|
|
135
|
+
@api.doc("my_org_datasets")
|
|
138
136
|
@api.expect(filter_parser)
|
|
139
137
|
@api.marshal_list_with(dataset_fields)
|
|
140
138
|
def get(self):
|
|
141
|
-
|
|
142
|
-
q = filter_parser.parse_args().get(
|
|
139
|
+
"""List all datasets related to me and my organizations."""
|
|
140
|
+
q = filter_parser.parse_args().get("q")
|
|
143
141
|
owners = list(current_user.organizations) + [current_user.id]
|
|
144
|
-
datasets = Dataset.objects.owned_by(*owners).order_by(
|
|
142
|
+
datasets = Dataset.objects.owned_by(*owners).order_by("-last_modified")
|
|
145
143
|
if q:
|
|
146
144
|
datasets = datasets.filter(title__icontains=q)
|
|
147
145
|
return list(datasets)
|
|
148
146
|
|
|
149
147
|
|
|
150
|
-
@me.route(
|
|
148
|
+
@me.route("/org_community_resources/", endpoint="my_org_community_resources")
|
|
151
149
|
class MyOrgCommunityResourcesAPI(API):
|
|
152
150
|
@api.secure
|
|
153
|
-
@api.doc(
|
|
151
|
+
@api.doc("my_org_community_resources")
|
|
154
152
|
@api.expect(filter_parser)
|
|
155
153
|
@api.marshal_list_with(community_resource_fields)
|
|
156
154
|
def get(self):
|
|
157
|
-
|
|
158
|
-
q = filter_parser.parse_args().get(
|
|
155
|
+
"""List all community resources related to me and my organizations."""
|
|
156
|
+
q = filter_parser.parse_args().get("q")
|
|
159
157
|
owners = list(current_user.organizations) + [current_user.id]
|
|
160
|
-
community_resources =
|
|
161
|
-
.order_by('-last_modified'))
|
|
158
|
+
community_resources = CommunityResource.objects.owned_by(*owners).order_by("-last_modified")
|
|
162
159
|
if q:
|
|
163
|
-
community_resources = community_resources.filter(
|
|
164
|
-
title__icontains=q)
|
|
160
|
+
community_resources = community_resources.filter(title__icontains=q)
|
|
165
161
|
return list(community_resources)
|
|
166
162
|
|
|
167
163
|
|
|
168
|
-
@me.route(
|
|
164
|
+
@me.route("/org_reuses/", endpoint="my_org_reuses")
|
|
169
165
|
class MyOrgReusesAPI(API):
|
|
170
166
|
@api.secure
|
|
171
|
-
@api.doc(
|
|
167
|
+
@api.doc("my_org_reuses")
|
|
172
168
|
@api.expect(filter_parser)
|
|
173
169
|
@api.marshal_list_with(reuse_fields)
|
|
174
170
|
def get(self):
|
|
175
|
-
|
|
176
|
-
q = filter_parser.parse_args().get(
|
|
171
|
+
"""List all reuses related to me and my organizations."""
|
|
172
|
+
q = filter_parser.parse_args().get("q")
|
|
177
173
|
owners = list(current_user.organizations) + [current_user.id]
|
|
178
|
-
reuses = Reuse.objects.owned_by(*owners).order_by(
|
|
174
|
+
reuses = Reuse.objects.owned_by(*owners).order_by("-last_modified")
|
|
179
175
|
if q:
|
|
180
176
|
reuses = reuses.filter(title__icontains=q)
|
|
181
177
|
return list(reuses)
|
|
182
178
|
|
|
183
179
|
|
|
184
|
-
@me.route(
|
|
180
|
+
@me.route("/org_discussions/", endpoint="my_org_discussions")
|
|
185
181
|
class MyOrgDiscussionsAPI(API):
|
|
186
182
|
@api.secure
|
|
187
|
-
@api.doc(
|
|
183
|
+
@api.doc("my_org_discussions")
|
|
188
184
|
@api.expect(filter_parser)
|
|
189
185
|
@api.marshal_list_with(discussion_fields)
|
|
190
186
|
def get(self):
|
|
191
|
-
|
|
192
|
-
q = filter_parser.parse_args().get(
|
|
187
|
+
"""List all discussions related to my organizations."""
|
|
188
|
+
q = filter_parser.parse_args().get("q")
|
|
193
189
|
discussions = discussions_for(current_user._get_current_object())
|
|
194
|
-
discussions = discussions.order_by(
|
|
190
|
+
discussions = discussions.order_by("-created")
|
|
195
191
|
if q:
|
|
196
192
|
decoded = q
|
|
197
193
|
discussions = discussions.filter(title__icontains=decoded)
|
|
198
194
|
return list(discussions)
|
|
199
195
|
|
|
200
196
|
|
|
201
|
-
@me.route(
|
|
197
|
+
@me.route("/apikey", endpoint="my_apikey")
|
|
202
198
|
class ApiKeyAPI(API):
|
|
203
199
|
@api.secure
|
|
204
|
-
@api.doc(
|
|
200
|
+
@api.doc("generate_apikey")
|
|
205
201
|
@api.marshal_with(apikey_fields)
|
|
206
|
-
@api.response(201,
|
|
202
|
+
@api.response(201, "API Key generated")
|
|
207
203
|
def post(self):
|
|
208
|
-
|
|
204
|
+
"""(Re)Generate my API Key"""
|
|
209
205
|
current_user.generate_api_key()
|
|
210
206
|
current_user.save()
|
|
211
207
|
return current_user, 201
|
|
212
208
|
|
|
213
209
|
@api.secure
|
|
214
|
-
@api.doc(
|
|
215
|
-
@api.response(204,
|
|
210
|
+
@api.doc("clear_apikey")
|
|
211
|
+
@api.response(204, "API Key deleted/cleared")
|
|
216
212
|
def delete(self):
|
|
217
|
-
|
|
213
|
+
"""Clear/destroy an apikey"""
|
|
218
214
|
current_user.apikey = None
|
|
219
215
|
current_user.save()
|
|
220
|
-
return
|
|
216
|
+
return "", 204
|
|
221
217
|
|
|
222
218
|
|
|
223
|
-
@ns.route(
|
|
219
|
+
@ns.route("/", endpoint="users")
|
|
224
220
|
class UserListAPI(API):
|
|
225
221
|
model = User
|
|
226
222
|
fields = user_fields
|
|
227
223
|
form = UserProfileForm
|
|
228
224
|
|
|
229
225
|
@api.secure(admin_permission)
|
|
230
|
-
@api.doc(
|
|
226
|
+
@api.doc("list_users")
|
|
231
227
|
@api.expect(user_parser.parser)
|
|
232
228
|
@api.marshal_with(user_page_fields)
|
|
233
229
|
def get(self):
|
|
234
|
-
|
|
230
|
+
"""List all users"""
|
|
235
231
|
args = user_parser.parse()
|
|
236
232
|
users = User.objects(deleted=None)
|
|
237
|
-
if args[
|
|
238
|
-
search_users = users.search_text(args[
|
|
239
|
-
if args[
|
|
240
|
-
return search_users.order_by(args[
|
|
233
|
+
if args["q"]:
|
|
234
|
+
search_users = users.search_text(args["q"])
|
|
235
|
+
if args["sort"]:
|
|
236
|
+
return search_users.order_by(args["sort"]).paginate(args["page"], args["page_size"])
|
|
241
237
|
else:
|
|
242
|
-
return search_users.order_by(
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
238
|
+
return search_users.order_by("$text_score").paginate(
|
|
239
|
+
args["page"], args["page_size"]
|
|
240
|
+
)
|
|
241
|
+
if args["sort"]:
|
|
242
|
+
return users.order_by(args["sort"]).paginate(args["page"], args["page_size"])
|
|
243
|
+
return users.order_by(DEFAULT_SORTING).paginate(args["page"], args["page_size"])
|
|
247
244
|
|
|
248
245
|
@api.secure(admin_permission)
|
|
249
|
-
@api.doc(
|
|
246
|
+
@api.doc("create_user")
|
|
250
247
|
@api.expect(user_fields)
|
|
251
248
|
@api.marshal_with(user_fields, code=201)
|
|
252
|
-
@api.response(400,
|
|
249
|
+
@api.response(400, "Validation error")
|
|
253
250
|
def post(self):
|
|
254
|
-
|
|
251
|
+
"""Create a new object"""
|
|
255
252
|
form = api.validate(UserProfileAdminForm)
|
|
256
253
|
user = form.save()
|
|
257
254
|
return user, 201
|
|
258
255
|
|
|
259
256
|
|
|
260
|
-
@ns.route(
|
|
257
|
+
@ns.route("/<user:user>/avatar", endpoint="user_avatar")
|
|
261
258
|
class UserAvatarAPI(API):
|
|
262
259
|
@api.secure(admin_permission)
|
|
263
|
-
@api.doc(
|
|
260
|
+
@api.doc("user_avatar")
|
|
264
261
|
@api.expect(image_parser)
|
|
265
262
|
@api.marshal_with(uploaded_image_fields)
|
|
266
263
|
def post(self, user):
|
|
267
|
-
|
|
264
|
+
"""Upload a new avatar for a given user"""
|
|
268
265
|
parse_uploaded_image(user.avatar)
|
|
269
266
|
user.save()
|
|
270
|
-
return {
|
|
271
|
-
|
|
267
|
+
return {"image": user.avatar}
|
|
272
268
|
|
|
273
269
|
|
|
274
270
|
delete_parser = api.parser()
|
|
275
271
|
delete_parser.add_argument(
|
|
276
|
-
|
|
277
|
-
|
|
272
|
+
"no_mail",
|
|
273
|
+
type=bool,
|
|
274
|
+
help="Do not send a mail to notify the user of the deletion",
|
|
275
|
+
location="args",
|
|
276
|
+
default=False,
|
|
277
|
+
)
|
|
278
278
|
|
|
279
|
-
@ns.route('/<user:user>/', endpoint='user')
|
|
280
|
-
@api.response(404, 'User not found')
|
|
281
|
-
@api.response(410, 'User is not active or has been deleted')
|
|
282
|
-
class UserAPI(API):
|
|
283
279
|
|
|
284
|
-
|
|
280
|
+
@ns.route("/<user:user>/", endpoint="user")
|
|
281
|
+
@api.response(404, "User not found")
|
|
282
|
+
@api.response(410, "User is not active or has been deleted")
|
|
283
|
+
class UserAPI(API):
|
|
284
|
+
@api.doc("get_user")
|
|
285
285
|
@api.marshal_with(user_fields)
|
|
286
286
|
def get(self, user):
|
|
287
|
-
|
|
287
|
+
"""Get a user given its identifier"""
|
|
288
288
|
if current_user.is_anonymous or not current_user.sysadmin:
|
|
289
289
|
if not user.active:
|
|
290
|
-
api.abort(410,
|
|
290
|
+
api.abort(410, "User is not active")
|
|
291
291
|
if user.deleted:
|
|
292
|
-
api.abort(410,
|
|
292
|
+
api.abort(410, "User has been deleted")
|
|
293
293
|
return user
|
|
294
294
|
|
|
295
295
|
@api.secure(admin_permission)
|
|
296
|
-
@api.doc(
|
|
296
|
+
@api.doc("update_user")
|
|
297
297
|
@api.expect(user_fields)
|
|
298
298
|
@api.marshal_with(user_fields)
|
|
299
|
-
@api.response(400,
|
|
299
|
+
@api.response(400, "Validation error")
|
|
300
300
|
def put(self, user):
|
|
301
|
-
|
|
301
|
+
"""Update a user given its identifier"""
|
|
302
302
|
form = api.validate(UserProfileAdminForm, user)
|
|
303
303
|
return form.save()
|
|
304
304
|
|
|
305
305
|
@api.secure(admin_permission)
|
|
306
|
-
@api.doc(
|
|
306
|
+
@api.doc("delete_user")
|
|
307
307
|
@api.expect(delete_parser)
|
|
308
|
-
@api.response(204,
|
|
309
|
-
@api.response(403,
|
|
308
|
+
@api.response(204, "Object deleted")
|
|
309
|
+
@api.response(403, "When trying to delete yourself")
|
|
310
310
|
def delete(self, user):
|
|
311
|
-
|
|
311
|
+
"""Delete a user given its identifier"""
|
|
312
312
|
args = delete_parser.parse_args()
|
|
313
313
|
if user.deleted:
|
|
314
|
-
api.abort(410,
|
|
314
|
+
api.abort(410, "User has already been deleted")
|
|
315
315
|
if user == current_user._get_current_object():
|
|
316
|
-
api.abort(
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
316
|
+
api.abort(
|
|
317
|
+
403, "You cannot delete yourself with this API. " + 'Use the "me" API instead.'
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
user.mark_as_deleted(notify=not args["no_mail"])
|
|
321
|
+
return "", 204
|
|
321
322
|
|
|
322
323
|
|
|
323
|
-
from udata.models import ContactPoint
|
|
324
324
|
from udata.core.contact_point.api import ContactPointApiParser
|
|
325
325
|
from udata.core.contact_point.api_fields import contact_point_page_fields
|
|
326
|
-
|
|
326
|
+
from udata.models import ContactPoint
|
|
327
327
|
|
|
328
328
|
contact_point_parser = ContactPointApiParser()
|
|
329
329
|
|
|
330
330
|
|
|
331
|
-
@ns.route(
|
|
331
|
+
@ns.route("/<user:user>/contacts/", endpoint="user_contact_points")
|
|
332
332
|
class OrgContactAPI(API):
|
|
333
|
-
@api.doc(
|
|
333
|
+
@api.doc("get_user_contact_point")
|
|
334
334
|
@api.marshal_with(contact_point_page_fields)
|
|
335
335
|
def get(self, user):
|
|
336
|
-
|
|
336
|
+
"""List all user contact points"""
|
|
337
337
|
args = contact_point_parser.parse()
|
|
338
338
|
contact_points = ContactPoint.objects.owned_by(user)
|
|
339
|
-
return contact_points.paginate(args[
|
|
339
|
+
return contact_points.paginate(args["page"], args["page_size"])
|
|
340
340
|
|
|
341
341
|
|
|
342
|
-
@ns.route(
|
|
343
|
-
@ns.doc(
|
|
344
|
-
|
|
345
|
-
|
|
342
|
+
@ns.route("/<id>/followers/", endpoint="user_followers")
|
|
343
|
+
@ns.doc(
|
|
344
|
+
get={"id": "list_user_followers"}, post={"id": "follow_user"}, delete={"id": "unfollow_user"}
|
|
345
|
+
)
|
|
346
346
|
class FollowUserAPI(FollowAPI):
|
|
347
347
|
model = User
|
|
348
348
|
|
|
349
349
|
@api.secure
|
|
350
350
|
@api.doc(notes="You can't follow yourself.")
|
|
351
|
-
@api.response(403,
|
|
351
|
+
@api.response(403, "When trying to follow yourself")
|
|
352
352
|
def post(self, id):
|
|
353
|
-
|
|
353
|
+
"""Follow a user given its ID"""
|
|
354
354
|
if id == str(current_user.id):
|
|
355
355
|
api.abort(403, "You can't follow yourself")
|
|
356
356
|
return super(FollowUserAPI, self).post(id)
|
|
@@ -358,41 +358,40 @@ class FollowUserAPI(FollowAPI):
|
|
|
358
358
|
|
|
359
359
|
suggest_parser = api.parser()
|
|
360
360
|
suggest_parser.add_argument(
|
|
361
|
-
|
|
362
|
-
|
|
361
|
+
"q", help="The string to autocomplete/suggest", location="args", required=True
|
|
362
|
+
)
|
|
363
363
|
suggest_parser.add_argument(
|
|
364
|
-
|
|
365
|
-
|
|
364
|
+
"size", type=int, help="The amount of suggestion to fetch", location="args", default=10
|
|
365
|
+
)
|
|
366
366
|
|
|
367
367
|
|
|
368
|
-
@ns.route(
|
|
368
|
+
@ns.route("/suggest/", endpoint="suggest_users")
|
|
369
369
|
class SuggestUsersAPI(API):
|
|
370
|
-
@api.doc(
|
|
370
|
+
@api.doc("suggest_users")
|
|
371
371
|
@api.expect(suggest_parser)
|
|
372
372
|
@api.marshal_list_with(user_suggestion_fields)
|
|
373
373
|
def get(self):
|
|
374
|
-
|
|
374
|
+
"""Suggest users"""
|
|
375
375
|
args = suggest_parser.parse_args()
|
|
376
376
|
users = User.objects(
|
|
377
|
-
deleted=None,
|
|
378
|
-
slug__icontains=slugify(args['q'], separator='-', to_lower=True)
|
|
377
|
+
deleted=None, slug__icontains=slugify(args["q"], separator="-", to_lower=True)
|
|
379
378
|
)
|
|
380
379
|
return [
|
|
381
380
|
{
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
381
|
+
"id": user.id,
|
|
382
|
+
"first_name": user.first_name,
|
|
383
|
+
"last_name": user.last_name,
|
|
384
|
+
"avatar_url": user.avatar,
|
|
385
|
+
"slug": user.slug,
|
|
387
386
|
}
|
|
388
|
-
for user in users.order_by(DEFAULT_SORTING).limit(args[
|
|
387
|
+
for user in users.order_by(DEFAULT_SORTING).limit(args["size"])
|
|
389
388
|
]
|
|
390
389
|
|
|
391
390
|
|
|
392
|
-
@ns.route(
|
|
391
|
+
@ns.route("/roles/", endpoint="user_roles")
|
|
393
392
|
class UserRolesAPI(API):
|
|
394
|
-
@api.doc(
|
|
393
|
+
@api.doc("user_roles")
|
|
395
394
|
@api.marshal_list_with(user_role_fields)
|
|
396
395
|
def get(self):
|
|
397
|
-
|
|
398
|
-
return [{
|
|
396
|
+
"""List all possible user roles"""
|
|
397
|
+
return [{"name": role.name} for role in Role.objects()]
|