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/organization/api.py
CHANGED
|
@@ -1,179 +1,177 @@
|
|
|
1
1
|
from datetime import datetime
|
|
2
2
|
|
|
3
|
-
from flask import
|
|
3
|
+
from flask import make_response, redirect, request, url_for
|
|
4
4
|
from mongoengine.queryset.visitor import Q
|
|
5
5
|
|
|
6
|
-
from udata.api import
|
|
6
|
+
from udata.api import API, api, errors
|
|
7
7
|
from udata.api.parsers import ModelApiParser
|
|
8
8
|
from udata.auth import admin_permission, current_user
|
|
9
9
|
from udata.core.badges import api as badges_api
|
|
10
10
|
from udata.core.badges.fields import badge_fields
|
|
11
|
-
from udata.core.followers.api import FollowAPI
|
|
12
|
-
from udata.utils import multi_to_dict
|
|
13
|
-
from udata.rdf import (
|
|
14
|
-
RDF_EXTENSIONS, negociate_content, graph_response
|
|
15
|
-
)
|
|
16
|
-
|
|
17
|
-
from .forms import (
|
|
18
|
-
OrganizationForm, MembershipRequestForm, MembershipRefuseForm, MemberForm
|
|
19
|
-
)
|
|
20
|
-
from .models import Organization, MembershipRequest, Member
|
|
21
|
-
from .constants import ORG_ROLES
|
|
22
|
-
from .permissions import (
|
|
23
|
-
EditOrganizationPermission, OrganizationPrivatePermission
|
|
24
|
-
)
|
|
25
|
-
from .rdf import build_org_catalog
|
|
26
|
-
from .tasks import notify_membership_request, notify_membership_response
|
|
27
|
-
from .api_fields import (
|
|
28
|
-
org_fields,
|
|
29
|
-
org_page_fields,
|
|
30
|
-
org_role_fields,
|
|
31
|
-
request_fields,
|
|
32
|
-
member_fields,
|
|
33
|
-
refuse_membership_fields,
|
|
34
|
-
org_suggestion_fields
|
|
35
|
-
)
|
|
36
|
-
|
|
37
11
|
from udata.core.dataset.api import DatasetApiParser
|
|
38
12
|
from udata.core.dataset.api_fields import dataset_page_fields
|
|
39
13
|
from udata.core.dataset.models import Dataset
|
|
40
14
|
from udata.core.discussions.api import discussion_fields
|
|
41
15
|
from udata.core.discussions.models import Discussion
|
|
16
|
+
from udata.core.followers.api import FollowAPI
|
|
42
17
|
from udata.core.reuse.api_fields import reuse_fields
|
|
43
18
|
from udata.core.reuse.models import Reuse
|
|
44
19
|
from udata.core.storages.api import (
|
|
45
|
-
|
|
20
|
+
image_parser,
|
|
21
|
+
parse_uploaded_image,
|
|
22
|
+
uploaded_image_fields,
|
|
46
23
|
)
|
|
24
|
+
from udata.rdf import RDF_EXTENSIONS, graph_response, negociate_content
|
|
25
|
+
from udata.utils import multi_to_dict
|
|
47
26
|
|
|
27
|
+
from .api_fields import (
|
|
28
|
+
member_fields,
|
|
29
|
+
org_fields,
|
|
30
|
+
org_page_fields,
|
|
31
|
+
org_role_fields,
|
|
32
|
+
org_suggestion_fields,
|
|
33
|
+
refuse_membership_fields,
|
|
34
|
+
request_fields,
|
|
35
|
+
)
|
|
36
|
+
from .constants import ORG_ROLES
|
|
37
|
+
from .forms import (
|
|
38
|
+
MemberForm,
|
|
39
|
+
MembershipRefuseForm,
|
|
40
|
+
MembershipRequestForm,
|
|
41
|
+
OrganizationForm,
|
|
42
|
+
)
|
|
43
|
+
from .models import Member, MembershipRequest, Organization
|
|
44
|
+
from .permissions import EditOrganizationPermission, OrganizationPrivatePermission
|
|
45
|
+
from .rdf import build_org_catalog
|
|
46
|
+
from .tasks import notify_membership_request, notify_membership_response
|
|
48
47
|
|
|
49
|
-
DEFAULT_SORTING =
|
|
50
|
-
SUGGEST_SORTING =
|
|
48
|
+
DEFAULT_SORTING = "-created_at"
|
|
49
|
+
SUGGEST_SORTING = "-metrics.followers"
|
|
51
50
|
|
|
52
51
|
|
|
53
52
|
class OrgApiParser(ModelApiParser):
|
|
54
53
|
sorts = {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
54
|
+
"name": "name",
|
|
55
|
+
"reuses": "metrics.reuses",
|
|
56
|
+
"datasets": "metrics.datasets",
|
|
57
|
+
"followers": "metrics.followers",
|
|
58
|
+
"views": "metrics.views",
|
|
59
|
+
"created": "created_at",
|
|
60
|
+
"last_modified": "last_modified",
|
|
62
61
|
}
|
|
63
62
|
|
|
64
63
|
@staticmethod
|
|
65
64
|
def parse_filters(organizations, args):
|
|
66
|
-
if args.get(
|
|
65
|
+
if args.get("q"):
|
|
67
66
|
# Following code splits the 'q' argument by spaces to surround
|
|
68
67
|
# every word in it with quotes before rebuild it.
|
|
69
68
|
# This allows the search_text method to tokenise with an AND
|
|
70
69
|
# between tokens whereas an OR is used without it.
|
|
71
|
-
phrase_query =
|
|
70
|
+
phrase_query = " ".join([f'"{elem}"' for elem in args["q"].split(" ")])
|
|
72
71
|
organizations = organizations.search_text(phrase_query)
|
|
73
72
|
return organizations
|
|
74
73
|
|
|
75
74
|
|
|
76
|
-
ns = api.namespace(
|
|
75
|
+
ns = api.namespace("organizations", "Organization related operations")
|
|
77
76
|
|
|
78
77
|
organization_parser = OrgApiParser()
|
|
79
78
|
|
|
80
|
-
common_doc = {
|
|
81
|
-
'params': {'org': 'The organization ID or slug'}
|
|
82
|
-
}
|
|
79
|
+
common_doc = {"params": {"org": "The organization ID or slug"}}
|
|
83
80
|
|
|
84
81
|
|
|
85
|
-
@ns.route(
|
|
82
|
+
@ns.route("/", endpoint="organizations")
|
|
86
83
|
class OrganizationListAPI(API):
|
|
87
|
-
|
|
88
|
-
|
|
84
|
+
"""Organizations collection endpoint"""
|
|
85
|
+
|
|
86
|
+
@api.doc("list_organizations")
|
|
89
87
|
@api.expect(organization_parser.parser)
|
|
90
88
|
@api.marshal_with(org_page_fields)
|
|
91
89
|
def get(self):
|
|
92
|
-
|
|
90
|
+
"""List or search all organizations"""
|
|
93
91
|
args = organization_parser.parse()
|
|
94
92
|
organizations = Organization.objects(deleted=None)
|
|
95
93
|
organizations = organization_parser.parse_filters(organizations, args)
|
|
96
94
|
|
|
97
|
-
sort = args[
|
|
98
|
-
return organizations.order_by(sort).paginate(args[
|
|
95
|
+
sort = args["sort"] or ("$text_score" if args["q"] else None) or DEFAULT_SORTING
|
|
96
|
+
return organizations.order_by(sort).paginate(args["page"], args["page_size"])
|
|
99
97
|
|
|
100
98
|
@api.secure
|
|
101
|
-
@api.doc(
|
|
99
|
+
@api.doc("create_organization", responses={400: "Validation error"})
|
|
102
100
|
@api.expect(org_fields)
|
|
103
101
|
@api.marshal_with(org_fields, code=201)
|
|
104
102
|
def post(self):
|
|
105
|
-
|
|
103
|
+
"""Create a new organization"""
|
|
106
104
|
form = api.validate(OrganizationForm)
|
|
107
105
|
organization = form.save()
|
|
108
106
|
return organization, 201
|
|
109
107
|
|
|
110
108
|
|
|
111
|
-
@ns.route(
|
|
112
|
-
@api.response(404,
|
|
113
|
-
@api.response(410,
|
|
109
|
+
@ns.route("/<org:org>/", endpoint="organization", doc=common_doc)
|
|
110
|
+
@api.response(404, "Organization not found")
|
|
111
|
+
@api.response(410, "Organization has been deleted")
|
|
114
112
|
class OrganizationAPI(API):
|
|
115
|
-
@api.doc(
|
|
113
|
+
@api.doc("get_organization")
|
|
116
114
|
@api.marshal_with(org_fields)
|
|
117
115
|
def get(self, org):
|
|
118
|
-
|
|
116
|
+
"""Get a organization given its identifier"""
|
|
119
117
|
if org.deleted and not OrganizationPrivatePermission(org).can():
|
|
120
|
-
api.abort(410,
|
|
118
|
+
api.abort(410, "Organization has been deleted")
|
|
121
119
|
return org
|
|
122
120
|
|
|
123
121
|
@api.secure
|
|
124
|
-
@api.doc(
|
|
122
|
+
@api.doc("update_organization")
|
|
125
123
|
@api.expect(org_fields)
|
|
126
124
|
@api.marshal_with(org_fields)
|
|
127
125
|
@api.response(400, errors.VALIDATION_ERROR)
|
|
128
|
-
@api.response(410,
|
|
126
|
+
@api.response(410, "Organization has been deleted")
|
|
129
127
|
def put(self, org):
|
|
130
|
-
|
|
128
|
+
"""
|
|
131
129
|
Update a organization given its identifier
|
|
132
130
|
|
|
133
131
|
:raises PermissionDenied:
|
|
134
|
-
|
|
135
|
-
request_deleted = request.json.get(
|
|
132
|
+
"""
|
|
133
|
+
request_deleted = request.json.get("deleted", True)
|
|
136
134
|
if org.deleted and request_deleted is not None:
|
|
137
|
-
api.abort(410,
|
|
135
|
+
api.abort(410, "Organization has been deleted")
|
|
138
136
|
EditOrganizationPermission(org).test()
|
|
139
137
|
form = api.validate(OrganizationForm, org)
|
|
140
138
|
return form.save()
|
|
141
139
|
|
|
142
140
|
@api.secure
|
|
143
|
-
@api.doc(
|
|
144
|
-
@api.response(204,
|
|
141
|
+
@api.doc("delete_organization")
|
|
142
|
+
@api.response(204, "Organization deleted")
|
|
145
143
|
def delete(self, org):
|
|
146
|
-
|
|
144
|
+
"""Delete a organization given its identifier"""
|
|
147
145
|
if org.deleted:
|
|
148
|
-
api.abort(410,
|
|
146
|
+
api.abort(410, "Organization has been deleted")
|
|
149
147
|
EditOrganizationPermission(org).test()
|
|
150
148
|
org.deleted = datetime.utcnow()
|
|
151
149
|
org.save()
|
|
152
|
-
return
|
|
150
|
+
return "", 204
|
|
153
151
|
|
|
154
152
|
|
|
155
|
-
@ns.route(
|
|
156
|
-
@api.response(404,
|
|
157
|
-
@api.response(410,
|
|
153
|
+
@ns.route("/<org:org>/catalog", endpoint="organization_rdf", doc=common_doc)
|
|
154
|
+
@api.response(404, "Organization not found")
|
|
155
|
+
@api.response(410, "Organization has been deleted")
|
|
158
156
|
class OrganizationRdfAPI(API):
|
|
159
|
-
@api.doc(
|
|
157
|
+
@api.doc("rdf_organization")
|
|
160
158
|
def get(self, org):
|
|
161
159
|
format = RDF_EXTENSIONS[negociate_content()]
|
|
162
|
-
url = url_for(
|
|
160
|
+
url = url_for("api.organization_rdf_format", org=org.id, format=format)
|
|
163
161
|
return redirect(url)
|
|
164
162
|
|
|
165
163
|
|
|
166
|
-
@ns.route(
|
|
167
|
-
@api.response(404,
|
|
168
|
-
@api.response(410,
|
|
164
|
+
@ns.route("/<org:org>/catalog.<format>", endpoint="organization_rdf_format", doc=common_doc)
|
|
165
|
+
@api.response(404, "Organization not found")
|
|
166
|
+
@api.response(410, "Organization has been deleted")
|
|
169
167
|
class OrganizationRdfFormatAPI(API):
|
|
170
|
-
@api.doc(
|
|
168
|
+
@api.doc("rdf_organization_format")
|
|
171
169
|
def get(self, org, format):
|
|
172
170
|
if org.deleted:
|
|
173
171
|
api.abort(410)
|
|
174
172
|
params = multi_to_dict(request.args)
|
|
175
|
-
page = int(params.get(
|
|
176
|
-
page_size = int(params.get(
|
|
173
|
+
page = int(params.get("page", 1))
|
|
174
|
+
page_size = int(params.get("page_size", 100))
|
|
177
175
|
datasets = Dataset.objects(organization=org).visible().paginate(page, page_size)
|
|
178
176
|
catalog = build_org_catalog(org, datasets, format=format)
|
|
179
177
|
# bypass flask-restplus make_response, since graph_response
|
|
@@ -181,85 +179,79 @@ class OrganizationRdfFormatAPI(API):
|
|
|
181
179
|
return make_response(*graph_response(catalog, format))
|
|
182
180
|
|
|
183
181
|
|
|
184
|
-
@ns.route(
|
|
182
|
+
@ns.route("/badges/", endpoint="available_organization_badges")
|
|
185
183
|
class AvailableOrganizationBadgesAPI(API):
|
|
186
|
-
@api.doc(
|
|
184
|
+
@api.doc("available_organization_badges")
|
|
187
185
|
def get(self):
|
|
188
|
-
|
|
186
|
+
"""List all available organization badges and their labels"""
|
|
189
187
|
return Organization.__badges__
|
|
190
188
|
|
|
191
189
|
|
|
192
|
-
@ns.route(
|
|
190
|
+
@ns.route("/<org:org>/badges/", endpoint="organization_badges")
|
|
193
191
|
class OrganizationBadgesAPI(API):
|
|
194
|
-
@api.doc(
|
|
192
|
+
@api.doc("add_organization_badge", **common_doc)
|
|
195
193
|
@api.expect(badge_fields)
|
|
196
194
|
@api.marshal_with(badge_fields)
|
|
197
195
|
@api.secure(admin_permission)
|
|
198
196
|
def post(self, org):
|
|
199
|
-
|
|
197
|
+
"""Create a new badge for a given organization"""
|
|
200
198
|
return badges_api.add(org)
|
|
201
199
|
|
|
202
200
|
|
|
203
|
-
@ns.route(
|
|
201
|
+
@ns.route("/<org:org>/badges/<badge_kind>/", endpoint="organization_badge")
|
|
204
202
|
class OrganizationBadgeAPI(API):
|
|
205
|
-
@api.doc(
|
|
203
|
+
@api.doc("delete_organization_badge", **common_doc)
|
|
206
204
|
@api.secure(admin_permission)
|
|
207
205
|
def delete(self, org, badge_kind):
|
|
208
|
-
|
|
206
|
+
"""Delete a badge for a given organization"""
|
|
209
207
|
return badges_api.remove(org, badge_kind)
|
|
210
208
|
|
|
211
209
|
|
|
212
|
-
from udata.models import ContactPoint
|
|
213
210
|
from udata.core.contact_point.api import ContactPointApiParser
|
|
214
211
|
from udata.core.contact_point.api_fields import contact_point_page_fields
|
|
215
|
-
|
|
212
|
+
from udata.models import ContactPoint
|
|
216
213
|
|
|
217
214
|
contact_point_parser = ContactPointApiParser()
|
|
218
215
|
|
|
219
216
|
|
|
220
|
-
@ns.route(
|
|
217
|
+
@ns.route("/<org:org>/contacts/", endpoint="org_contact_points")
|
|
221
218
|
class OrgContactAPI(API):
|
|
222
|
-
@api.doc(
|
|
219
|
+
@api.doc("get_organization_contact_point")
|
|
223
220
|
@api.marshal_with(contact_point_page_fields)
|
|
224
221
|
def get(self, org):
|
|
225
|
-
|
|
222
|
+
"""List all organization contact points"""
|
|
226
223
|
args = contact_point_parser.parse()
|
|
227
224
|
contact_points = ContactPoint.objects.owned_by(org)
|
|
228
|
-
return contact_points.paginate(args[
|
|
225
|
+
return contact_points.paginate(args["page"], args["page_size"])
|
|
229
226
|
|
|
230
227
|
|
|
231
228
|
requests_parser = api.parser()
|
|
232
229
|
requests_parser.add_argument(
|
|
233
|
-
|
|
234
|
-
type=str,
|
|
235
|
-
help='If provided, only return requests ith a given status',
|
|
236
|
-
location='args'
|
|
230
|
+
"status", type=str, help="If provided, only return requests ith a given status", location="args"
|
|
237
231
|
)
|
|
238
232
|
|
|
239
233
|
|
|
240
|
-
@ns.route(
|
|
241
|
-
doc=common_doc)
|
|
234
|
+
@ns.route("/<org:org>/membership/", endpoint="request_membership", doc=common_doc)
|
|
242
235
|
class MembershipRequestAPI(API):
|
|
243
236
|
@api.secure
|
|
244
|
-
@api.doc(
|
|
237
|
+
@api.doc("list_membership_requests")
|
|
245
238
|
@api.expect(requests_parser)
|
|
246
|
-
@api.response(403,
|
|
239
|
+
@api.response(403, "Not Authorized")
|
|
247
240
|
@api.marshal_list_with(request_fields)
|
|
248
241
|
def get(self, org):
|
|
249
|
-
|
|
242
|
+
"""List membership requests for a given organization"""
|
|
250
243
|
OrganizationPrivatePermission(org).test()
|
|
251
244
|
args = requests_parser.parse_args()
|
|
252
|
-
if args[
|
|
253
|
-
return [r for r in org.requests if r.status == args[
|
|
245
|
+
if args["status"]:
|
|
246
|
+
return [r for r in org.requests if r.status == args["status"]]
|
|
254
247
|
else:
|
|
255
248
|
return org.requests
|
|
256
249
|
|
|
257
250
|
@api.secure
|
|
258
251
|
@api.marshal_with(request_fields)
|
|
259
252
|
def post(self, org):
|
|
260
|
-
|
|
261
|
-
membership_request = org.pending_request(
|
|
262
|
-
current_user._get_current_object())
|
|
253
|
+
"""Apply for membership to a given organization."""
|
|
254
|
+
membership_request = org.pending_request(current_user._get_current_object())
|
|
263
255
|
code = 200 if membership_request else 201
|
|
264
256
|
|
|
265
257
|
form = api.validate(MembershipRequestForm, membership_request)
|
|
@@ -281,27 +273,26 @@ class MembershipAPI(API):
|
|
|
281
273
|
for membership_request in org.requests:
|
|
282
274
|
if membership_request.id == id:
|
|
283
275
|
return membership_request
|
|
284
|
-
api.abort(404,
|
|
276
|
+
api.abort(404, "Unknown membership request id")
|
|
285
277
|
|
|
286
278
|
|
|
287
|
-
@ns.route(
|
|
288
|
-
endpoint='accept_membership')
|
|
279
|
+
@ns.route("/<org:org>/membership/<uuid:id>/accept/", endpoint="accept_membership")
|
|
289
280
|
class MembershipAcceptAPI(MembershipAPI):
|
|
290
281
|
@api.secure
|
|
291
|
-
@api.doc(
|
|
282
|
+
@api.doc("accept_membership", **common_doc)
|
|
292
283
|
@api.marshal_with(member_fields)
|
|
293
284
|
def post(self, org, id):
|
|
294
|
-
|
|
285
|
+
"""Accept user membership to a given organization."""
|
|
295
286
|
EditOrganizationPermission(org).test()
|
|
296
287
|
membership_request = self.get_or_404(org, id)
|
|
297
288
|
|
|
298
289
|
if org.is_member(membership_request.user):
|
|
299
290
|
return org.member(membership_request.user), 409
|
|
300
291
|
|
|
301
|
-
membership_request.status =
|
|
292
|
+
membership_request.status = "accepted"
|
|
302
293
|
membership_request.handled_by = current_user._get_current_object()
|
|
303
294
|
membership_request.handled_on = datetime.utcnow()
|
|
304
|
-
member = Member(user=membership_request.user, role=
|
|
295
|
+
member = Member(user=membership_request.user, role="editor")
|
|
305
296
|
|
|
306
297
|
org.members.append(member)
|
|
307
298
|
org.count_members()
|
|
@@ -312,18 +303,17 @@ class MembershipAcceptAPI(MembershipAPI):
|
|
|
312
303
|
return member
|
|
313
304
|
|
|
314
305
|
|
|
315
|
-
@ns.route(
|
|
316
|
-
endpoint='refuse_membership')
|
|
306
|
+
@ns.route("/<org:org>/membership/<uuid:id>/refuse/", endpoint="refuse_membership")
|
|
317
307
|
class MembershipRefuseAPI(MembershipAPI):
|
|
318
308
|
@api.secure
|
|
319
309
|
@api.expect(refuse_membership_fields)
|
|
320
|
-
@api.doc(
|
|
310
|
+
@api.doc("refuse_membership", **common_doc)
|
|
321
311
|
def post(self, org, id):
|
|
322
|
-
|
|
312
|
+
"""Refuse user membership to a given organization."""
|
|
323
313
|
EditOrganizationPermission(org).test()
|
|
324
314
|
membership_request = self.get_or_404(org, id)
|
|
325
315
|
form = api.validate(MembershipRefuseForm)
|
|
326
|
-
membership_request.status =
|
|
316
|
+
membership_request.status = "refused"
|
|
327
317
|
membership_request.handled_by = current_user._get_current_object()
|
|
328
318
|
membership_request.handled_on = datetime.utcnow()
|
|
329
319
|
membership_request.refusal_comment = form.comment.data
|
|
@@ -335,16 +325,16 @@ class MembershipRefuseAPI(MembershipAPI):
|
|
|
335
325
|
return {}, 200
|
|
336
326
|
|
|
337
327
|
|
|
338
|
-
@ns.route(
|
|
328
|
+
@ns.route("/<org:org>/member/<user:user>", endpoint="member", doc=common_doc)
|
|
339
329
|
class MemberAPI(API):
|
|
340
330
|
@api.secure
|
|
341
331
|
@api.expect(member_fields)
|
|
342
332
|
@api.marshal_with(member_fields, code=201)
|
|
343
|
-
@api.doc(
|
|
344
|
-
@api.response(403,
|
|
345
|
-
@api.response(409,
|
|
333
|
+
@api.doc("create_organization_member")
|
|
334
|
+
@api.response(403, "Not Authorized")
|
|
335
|
+
@api.response(409, "User is already member", member_fields)
|
|
346
336
|
def post(self, org, user):
|
|
347
|
-
|
|
337
|
+
"""Add a member into a given organization."""
|
|
348
338
|
EditOrganizationPermission(org).test()
|
|
349
339
|
if org.is_member(user):
|
|
350
340
|
return org.member(user), 409
|
|
@@ -360,9 +350,9 @@ class MemberAPI(API):
|
|
|
360
350
|
@api.secure
|
|
361
351
|
@api.expect(member_fields)
|
|
362
352
|
@api.marshal_with(member_fields)
|
|
363
|
-
@api.doc(
|
|
353
|
+
@api.doc("update_organization_member", responses={403: "Not Authorized"})
|
|
364
354
|
def put(self, org, user):
|
|
365
|
-
|
|
355
|
+
"""Update member status into a given organization."""
|
|
366
356
|
EditOrganizationPermission(org).test()
|
|
367
357
|
member = org.member(user)
|
|
368
358
|
form = api.validate(MemberForm, member)
|
|
@@ -372,130 +362,133 @@ class MemberAPI(API):
|
|
|
372
362
|
return member
|
|
373
363
|
|
|
374
364
|
@api.secure
|
|
375
|
-
@api.doc(
|
|
365
|
+
@api.doc("delete_organization_member", responses={403: "Not Authorized"})
|
|
376
366
|
def delete(self, org, user):
|
|
377
|
-
|
|
367
|
+
"""Delete member from an organization"""
|
|
378
368
|
EditOrganizationPermission(org).test()
|
|
379
369
|
member = org.member(user)
|
|
380
370
|
if member:
|
|
381
371
|
Organization.objects(id=org.id).update_one(pull__members=member)
|
|
382
372
|
org.reload()
|
|
383
373
|
org.count_members()
|
|
384
|
-
return
|
|
374
|
+
return "", 204
|
|
385
375
|
else:
|
|
386
376
|
api.abort(404)
|
|
387
377
|
|
|
388
378
|
|
|
389
|
-
@ns.route(
|
|
390
|
-
@ns.doc(
|
|
391
|
-
|
|
392
|
-
|
|
379
|
+
@ns.route("/<id>/followers/", endpoint="organization_followers")
|
|
380
|
+
@ns.doc(
|
|
381
|
+
get={"id": "list_organization_followers"},
|
|
382
|
+
post={"id": "follow_organization"},
|
|
383
|
+
delete={"id": "unfollow_organization"},
|
|
384
|
+
)
|
|
393
385
|
class FollowOrgAPI(FollowAPI):
|
|
394
386
|
model = Organization
|
|
395
387
|
|
|
396
388
|
|
|
397
389
|
suggest_parser = api.parser()
|
|
398
390
|
suggest_parser.add_argument(
|
|
399
|
-
|
|
400
|
-
|
|
391
|
+
"q", help="The string to autocomplete/suggest", location="args", required=True
|
|
392
|
+
)
|
|
401
393
|
suggest_parser.add_argument(
|
|
402
|
-
|
|
403
|
-
|
|
394
|
+
"size", type=int, help="The amount of suggestion to fetch", location="args", default=10
|
|
395
|
+
)
|
|
404
396
|
|
|
405
397
|
|
|
406
|
-
@ns.route(
|
|
398
|
+
@ns.route("/suggest/", endpoint="suggest_organizations")
|
|
407
399
|
class OrganizationSuggestAPI(API):
|
|
408
|
-
@api.doc(
|
|
400
|
+
@api.doc("suggest_organizations")
|
|
409
401
|
@api.expect(suggest_parser)
|
|
410
402
|
@api.marshal_list_with(org_suggestion_fields)
|
|
411
403
|
def get(self):
|
|
412
|
-
|
|
404
|
+
"""Organizations suggest endpoint using mongoDB contains"""
|
|
413
405
|
args = suggest_parser.parse_args()
|
|
414
|
-
orgs = Organization.objects(
|
|
406
|
+
orgs = Organization.objects(
|
|
407
|
+
Q(name__icontains=args["q"]) | Q(acronym__icontains=args["q"]), deleted=None
|
|
408
|
+
)
|
|
415
409
|
return [
|
|
416
410
|
{
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
411
|
+
"id": org.id,
|
|
412
|
+
"name": org.name,
|
|
413
|
+
"acronym": org.acronym,
|
|
414
|
+
"slug": org.slug,
|
|
415
|
+
"image_url": org.logo,
|
|
422
416
|
}
|
|
423
|
-
for org in orgs.order_by(SUGGEST_SORTING).limit(args[
|
|
417
|
+
for org in orgs.order_by(SUGGEST_SORTING).limit(args["size"])
|
|
424
418
|
]
|
|
425
419
|
|
|
426
420
|
|
|
427
|
-
@ns.route(
|
|
421
|
+
@ns.route("/<org:org>/logo", endpoint="organization_logo")
|
|
428
422
|
@api.doc(**common_doc)
|
|
429
423
|
class AvatarAPI(API):
|
|
430
424
|
@api.secure
|
|
431
|
-
@api.doc(
|
|
425
|
+
@api.doc("organization_logo")
|
|
432
426
|
@api.expect(image_parser) # Swagger 2.0 does not support formData at path level
|
|
433
427
|
@api.marshal_with(uploaded_image_fields)
|
|
434
428
|
def post(self, org):
|
|
435
|
-
|
|
429
|
+
"""Upload a new logo"""
|
|
436
430
|
EditOrganizationPermission(org).test()
|
|
437
431
|
parse_uploaded_image(org.logo)
|
|
438
432
|
org.save()
|
|
439
|
-
return {
|
|
433
|
+
return {"image": org.logo}
|
|
440
434
|
|
|
441
435
|
@api.secure
|
|
442
|
-
@api.doc(
|
|
436
|
+
@api.doc("resize_organization_logo")
|
|
443
437
|
@api.expect(image_parser) # Swagger 2.0 does not support formData at path level
|
|
444
438
|
@api.marshal_with(uploaded_image_fields)
|
|
445
439
|
def put(self, org):
|
|
446
|
-
|
|
440
|
+
"""Set the logo BBox"""
|
|
447
441
|
EditOrganizationPermission(org).test()
|
|
448
442
|
parse_uploaded_image(org.logo)
|
|
449
|
-
return {
|
|
443
|
+
return {"image": org.logo}
|
|
450
444
|
|
|
451
445
|
|
|
452
446
|
dataset_parser = DatasetApiParser()
|
|
453
447
|
|
|
454
448
|
|
|
455
|
-
@ns.route(
|
|
449
|
+
@ns.route("/<org:org>/datasets/", endpoint="org_datasets")
|
|
456
450
|
class OrgDatasetsAPI(API):
|
|
457
|
-
@api.doc(
|
|
451
|
+
@api.doc("list_organization_datasets")
|
|
458
452
|
@api.expect(dataset_parser.parser)
|
|
459
453
|
@api.marshal_with(dataset_page_fields)
|
|
460
454
|
def get(self, org):
|
|
461
|
-
|
|
455
|
+
"""List organization datasets (including private ones when member)"""
|
|
462
456
|
args = dataset_parser.parse()
|
|
463
457
|
qs = Dataset.objects.owned_by(org)
|
|
464
458
|
if not OrganizationPrivatePermission(org).can():
|
|
465
459
|
qs = qs(private__ne=True)
|
|
466
|
-
return
|
|
467
|
-
.paginate(args['page'], args['page_size']))
|
|
460
|
+
return qs.order_by(args["sort"]).paginate(args["page"], args["page_size"])
|
|
468
461
|
|
|
469
462
|
|
|
470
|
-
@ns.route(
|
|
463
|
+
@ns.route("/<org:org>/reuses/", endpoint="org_reuses")
|
|
471
464
|
class OrgReusesAPI(API):
|
|
472
|
-
@api.doc(
|
|
465
|
+
@api.doc("list_organization_reuses")
|
|
473
466
|
@api.marshal_list_with(reuse_fields)
|
|
474
467
|
def get(self, org):
|
|
475
|
-
|
|
468
|
+
"""List organization reuses (including private ones when member)"""
|
|
476
469
|
qs = Reuse.objects.owned_by(org)
|
|
477
470
|
if not OrganizationPrivatePermission(org).can():
|
|
478
471
|
qs = qs(private__ne=True)
|
|
479
472
|
return list(qs)
|
|
480
473
|
|
|
481
474
|
|
|
482
|
-
@ns.route(
|
|
475
|
+
@ns.route("/<org:org>/discussions/", endpoint="org_discussions")
|
|
483
476
|
class OrgDiscussionsAPI(API):
|
|
484
|
-
@api.doc(
|
|
477
|
+
@api.doc("list_organization_discussions")
|
|
485
478
|
@api.marshal_list_with(discussion_fields)
|
|
486
479
|
def get(self, org):
|
|
487
|
-
|
|
488
|
-
reuses = Reuse.objects(organization=org).only(
|
|
489
|
-
datasets = Dataset.objects(organization=org).only(
|
|
480
|
+
"""List organization discussions"""
|
|
481
|
+
reuses = Reuse.objects(organization=org).only("id")
|
|
482
|
+
datasets = Dataset.objects(organization=org).only("id")
|
|
490
483
|
subjects = list(reuses) + list(datasets)
|
|
491
|
-
qs = Discussion.objects(subject__in=subjects).order_by(
|
|
484
|
+
qs = Discussion.objects(subject__in=subjects).order_by("-created")
|
|
492
485
|
return list(qs)
|
|
493
486
|
|
|
494
487
|
|
|
495
|
-
@ns.route(
|
|
488
|
+
@ns.route("/roles/", endpoint="org_roles")
|
|
496
489
|
class OrgRolesAPI(API):
|
|
497
|
-
@api.doc(
|
|
490
|
+
@api.doc("org_roles")
|
|
498
491
|
@api.marshal_list_with(org_role_fields)
|
|
499
492
|
def get(self):
|
|
500
|
-
|
|
501
|
-
return [{
|
|
493
|
+
"""List all possible organization roles"""
|
|
494
|
+
return [{"id": key, "label": value} for (key, value) in ORG_ROLES.items()]
|