udata 12.0.2.dev15__py3-none-any.whl → 13.0.1.dev21__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.
- udata/api/__init__.py +1 -0
- udata/api_fields.py +10 -4
- udata/app.py +11 -10
- udata/auth/__init__.py +9 -10
- udata/auth/mails.py +137 -45
- udata/auth/views.py +5 -12
- udata/commands/__init__.py +2 -3
- udata/commands/info.py +1 -3
- udata/commands/tests/test_fixtures.py +6 -3
- udata/core/access_type/api.py +18 -0
- udata/core/access_type/constants.py +98 -0
- udata/core/access_type/models.py +44 -0
- udata/core/activity/models.py +1 -1
- udata/core/badges/models.py +1 -1
- udata/core/badges/tasks.py +35 -1
- udata/core/badges/tests/test_commands.py +2 -4
- udata/core/badges/tests/test_model.py +2 -2
- udata/core/badges/tests/test_tasks.py +55 -0
- udata/core/constants.py +1 -0
- udata/core/contact_point/models.py +8 -0
- udata/core/dataservices/api.py +3 -3
- udata/core/dataservices/apiv2.py +3 -1
- udata/core/dataservices/constants.py +0 -29
- udata/core/dataservices/models.py +44 -44
- udata/core/dataservices/rdf.py +2 -1
- udata/core/dataservices/search.py +5 -9
- udata/core/dataservices/tasks.py +33 -0
- udata/core/dataset/api_fields.py +11 -0
- udata/core/dataset/apiv2.py +11 -0
- udata/core/dataset/constants.py +0 -1
- udata/core/dataset/forms.py +29 -0
- udata/core/dataset/models.py +16 -4
- udata/core/dataset/rdf.py +2 -1
- udata/core/dataset/search.py +2 -2
- udata/core/dataset/tasks.py +86 -8
- udata/core/discussions/mails.py +63 -0
- udata/core/discussions/tasks.py +4 -18
- udata/core/metrics/__init__.py +0 -6
- udata/core/organization/api.py +3 -1
- udata/core/organization/mails.py +144 -0
- udata/core/organization/models.py +2 -1
- udata/core/organization/search.py +1 -1
- udata/core/organization/tasks.py +21 -49
- udata/core/pages/tests/test_api.py +0 -2
- udata/core/reuse/api.py +27 -1
- udata/core/reuse/mails.py +21 -0
- udata/core/reuse/models.py +10 -1
- udata/core/reuse/search.py +1 -1
- udata/core/reuse/tasks.py +2 -3
- udata/core/site/models.py +2 -6
- udata/core/spatial/tests/test_api.py +17 -20
- udata/core/spatial/tests/test_models.py +3 -3
- udata/core/user/mails.py +54 -0
- udata/core/user/models.py +2 -3
- udata/core/user/tasks.py +8 -23
- udata/core/user/tests/test_user_model.py +2 -6
- udata/entrypoints.py +0 -5
- udata/features/identicon/tests/test_backends.py +3 -13
- udata/forms/fields.py +3 -3
- udata/forms/widgets.py +2 -2
- udata/frontend/__init__.py +3 -32
- udata/harvest/actions.py +4 -9
- udata/harvest/api.py +5 -14
- udata/harvest/backends/__init__.py +20 -11
- udata/harvest/backends/base.py +2 -2
- udata/harvest/backends/ckan/harvesters.py +2 -1
- udata/harvest/backends/dcat.py +3 -0
- udata/harvest/backends/maaf.py +1 -0
- udata/harvest/commands.py +6 -4
- udata/harvest/forms.py +9 -6
- udata/harvest/tasks.py +3 -5
- udata/harvest/tests/ckan/test_ckan_backend.py +300 -337
- udata/harvest/tests/ckan/test_ckan_backend_errors.py +94 -99
- udata/harvest/tests/ckan/test_ckan_backend_filters.py +128 -122
- udata/harvest/tests/ckan/test_dkan_backend.py +39 -51
- udata/harvest/tests/dcat/datara--5a26b0f6-0ccf-46ad-ac58-734054b91977.rdf.xml +255 -0
- udata/harvest/tests/dcat/datara--f40c3860-7236-4b30-a141-23b8ae33f7b2.rdf.xml +289 -0
- udata/harvest/tests/factories.py +1 -1
- udata/harvest/tests/test_actions.py +11 -9
- udata/harvest/tests/test_api.py +4 -5
- udata/harvest/tests/test_base_backend.py +5 -4
- udata/harvest/tests/test_dcat_backend.py +50 -19
- udata/harvest/tests/test_models.py +2 -4
- udata/harvest/tests/test_notifications.py +2 -4
- udata/harvest/tests/test_tasks.py +2 -3
- udata/mail.py +90 -53
- udata/migrations/2025-01-05-dataservices-fields-changes.py +8 -14
- udata/migrations/2025-10-21-remove-ckan-harvest-modified-at.py +28 -0
- udata/migrations/2025-10-29-harvesters-sources-integrity.py +27 -0
- udata/mongo/taglist_field.py +3 -3
- udata/rdf.py +32 -15
- udata/sentry.py +3 -4
- udata/settings.py +7 -2
- udata/tags.py +5 -5
- udata/tasks.py +3 -3
- udata/templates/mail/message.html +65 -0
- udata/templates/mail/message.txt +16 -0
- udata/tests/__init__.py +40 -58
- udata/tests/api/__init__.py +87 -2
- udata/tests/api/test_activities_api.py +17 -23
- udata/tests/api/test_auth_api.py +2 -4
- udata/tests/api/test_contact_points.py +48 -54
- udata/tests/api/test_dataservices_api.py +57 -37
- udata/tests/api/test_datasets_api.py +146 -49
- udata/tests/api/test_me_api.py +4 -6
- udata/tests/api/test_organizations_api.py +19 -38
- udata/tests/api/test_reports_api.py +0 -4
- udata/tests/api/test_reuses_api.py +92 -19
- udata/tests/api/test_security_api.py +124 -0
- udata/tests/api/test_swagger.py +2 -3
- udata/tests/api/test_tags_api.py +6 -7
- udata/tests/api/test_transfer_api.py +0 -2
- udata/tests/api/test_user_api.py +8 -10
- udata/tests/apiv2/test_datasets.py +0 -4
- udata/tests/apiv2/test_me_api.py +0 -2
- udata/tests/apiv2/test_organizations.py +0 -2
- udata/tests/apiv2/test_swagger.py +2 -3
- udata/tests/apiv2/test_topics.py +0 -2
- udata/tests/cli/test_cli_base.py +14 -12
- udata/tests/cli/test_db_cli.py +51 -54
- udata/tests/contact_point/test_contact_point_models.py +2 -2
- udata/tests/dataservice/test_csv_adapter.py +2 -5
- udata/tests/dataservice/test_dataservice_rdf.py +8 -6
- udata/tests/dataservice/test_dataservice_tasks.py +36 -38
- udata/tests/dataset/test_csv_adapter.py +2 -5
- udata/tests/dataset/test_dataset_actions.py +2 -4
- udata/tests/dataset/test_dataset_commands.py +2 -4
- udata/tests/dataset/test_dataset_events.py +3 -3
- udata/tests/dataset/test_dataset_model.py +6 -7
- udata/tests/dataset/test_dataset_rdf.py +201 -12
- udata/tests/dataset/test_dataset_recommendations.py +2 -2
- udata/tests/dataset/test_dataset_tasks.py +66 -68
- udata/tests/dataset/test_resource_preview.py +39 -48
- udata/tests/dataset/test_transport_tasks.py +2 -2
- udata/tests/features/territories/__init__.py +0 -6
- udata/tests/features/territories/test_territories_api.py +25 -24
- udata/tests/forms/test_current_user_field.py +2 -2
- udata/tests/forms/test_dict_field.py +2 -4
- udata/tests/forms/test_extras_fields.py +2 -3
- udata/tests/forms/test_image_field.py +2 -2
- udata/tests/forms/test_model_field.py +2 -4
- udata/tests/forms/test_publish_as_field.py +2 -4
- udata/tests/forms/test_user_forms.py +26 -29
- udata/tests/frontend/test_auth.py +2 -3
- udata/tests/frontend/test_csv.py +5 -6
- udata/tests/frontend/test_error_handlers.py +2 -3
- udata/tests/frontend/test_hooks.py +5 -7
- udata/tests/frontend/test_markdown.py +3 -4
- udata/tests/helpers.py +2 -7
- udata/tests/metrics/test_metrics.py +52 -48
- udata/tests/metrics/test_tasks.py +154 -150
- udata/tests/organization/test_csv_adapter.py +2 -5
- udata/tests/organization/test_notifications.py +2 -4
- udata/tests/organization/test_organization_model.py +3 -4
- udata/tests/organization/test_organization_rdf.py +2 -8
- udata/tests/plugin.py +6 -110
- udata/tests/reuse/test_reuse_model.py +3 -4
- udata/tests/site/test_site_api.py +0 -2
- udata/tests/site/test_site_csv_exports.py +0 -2
- udata/tests/site/test_site_metrics.py +2 -4
- udata/tests/site/test_site_model.py +2 -2
- udata/tests/site/test_site_rdf.py +4 -7
- udata/tests/test_activity.py +3 -3
- udata/tests/test_api_fields.py +6 -9
- udata/tests/test_cors.py +0 -2
- udata/tests/test_dcat_commands.py +2 -3
- udata/tests/test_discussions.py +2 -7
- udata/tests/test_mail.py +150 -114
- udata/tests/test_migrations.py +413 -419
- udata/tests/test_model.py +10 -11
- udata/tests/test_notifications.py +2 -3
- udata/tests/test_owned.py +3 -3
- udata/tests/test_rdf.py +19 -15
- udata/tests/test_routing.py +5 -5
- udata/tests/test_storages.py +6 -5
- udata/tests/test_tags.py +2 -4
- udata/tests/test_topics.py +2 -4
- udata/tests/test_transfer.py +4 -5
- udata/tests/topic/test_topic_tasks.py +25 -27
- udata/tests/user/test_user_rdf.py +2 -8
- udata/tests/user/test_user_tasks.py +3 -5
- udata/tests/workers/test_jobs_commands.py +2 -2
- udata/tests/workers/test_tasks_routing.py +27 -27
- udata/translations/ar/LC_MESSAGES/udata.mo +0 -0
- udata/translations/ar/LC_MESSAGES/udata.po +369 -435
- udata/translations/de/LC_MESSAGES/udata.mo +0 -0
- udata/translations/de/LC_MESSAGES/udata.po +371 -437
- udata/translations/es/LC_MESSAGES/udata.mo +0 -0
- udata/translations/es/LC_MESSAGES/udata.po +369 -435
- udata/translations/fr/LC_MESSAGES/udata.mo +0 -0
- udata/translations/fr/LC_MESSAGES/udata.po +381 -447
- udata/translations/it/LC_MESSAGES/udata.mo +0 -0
- udata/translations/it/LC_MESSAGES/udata.po +371 -437
- udata/translations/pt/LC_MESSAGES/udata.mo +0 -0
- udata/translations/pt/LC_MESSAGES/udata.po +371 -437
- udata/translations/sr/LC_MESSAGES/udata.mo +0 -0
- udata/translations/sr/LC_MESSAGES/udata.po +372 -438
- udata/translations/udata.pot +379 -440
- udata/utils.py +14 -2
- {udata-12.0.2.dev15.dist-info → udata-13.0.1.dev21.dist-info}/METADATA +1 -2
- {udata-12.0.2.dev15.dist-info → udata-13.0.1.dev21.dist-info}/RECORD +205 -242
- udata/templates/mail/account_deleted.html +0 -5
- udata/templates/mail/account_deleted.txt +0 -6
- udata/templates/mail/account_inactivity.html +0 -40
- udata/templates/mail/account_inactivity.txt +0 -31
- udata/templates/mail/badge_added_association.html +0 -33
- udata/templates/mail/badge_added_association.txt +0 -11
- udata/templates/mail/badge_added_certified.html +0 -33
- udata/templates/mail/badge_added_certified.txt +0 -11
- udata/templates/mail/badge_added_company.html +0 -33
- udata/templates/mail/badge_added_company.txt +0 -11
- udata/templates/mail/badge_added_local_authority.html +0 -33
- udata/templates/mail/badge_added_local_authority.txt +0 -11
- udata/templates/mail/badge_added_public_service.html +0 -33
- udata/templates/mail/badge_added_public_service.txt +0 -11
- udata/templates/mail/discussion_closed.html +0 -47
- udata/templates/mail/discussion_closed.txt +0 -16
- udata/templates/mail/inactive_account_deleted.html +0 -5
- udata/templates/mail/inactive_account_deleted.txt +0 -6
- udata/templates/mail/membership_refused.html +0 -20
- udata/templates/mail/membership_refused.txt +0 -11
- udata/templates/mail/membership_request.html +0 -46
- udata/templates/mail/membership_request.txt +0 -12
- udata/templates/mail/new_discussion.html +0 -44
- udata/templates/mail/new_discussion.txt +0 -15
- udata/templates/mail/new_discussion_comment.html +0 -45
- udata/templates/mail/new_discussion_comment.txt +0 -16
- udata/templates/mail/new_member.html +0 -27
- udata/templates/mail/new_member.txt +0 -11
- udata/templates/mail/new_reuse.html +0 -37
- udata/templates/mail/new_reuse.txt +0 -9
- udata/templates/mail/test.html +0 -6
- udata/templates/mail/test.txt +0 -6
- udata/templates/mail/user_mail_card.html +0 -26
- udata/templates/security/email/base.html +0 -105
- udata/templates/security/email/base.txt +0 -6
- udata/templates/security/email/button.html +0 -3
- udata/templates/security/email/change_notice.html +0 -22
- udata/templates/security/email/change_notice.txt +0 -8
- udata/templates/security/email/confirmation_instructions.html +0 -20
- udata/templates/security/email/confirmation_instructions.txt +0 -7
- udata/templates/security/email/login_instructions.html +0 -19
- udata/templates/security/email/login_instructions.txt +0 -7
- udata/templates/security/email/reset_instructions.html +0 -24
- udata/templates/security/email/reset_instructions.txt +0 -9
- udata/templates/security/email/reset_notice.html +0 -11
- udata/templates/security/email/reset_notice.txt +0 -4
- udata/templates/security/email/welcome.html +0 -24
- udata/templates/security/email/welcome.txt +0 -9
- udata/templates/security/email/welcome_existing.html +0 -32
- udata/templates/security/email/welcome_existing.txt +0 -14
- udata/terms.md +0 -6
- udata/tests/frontend/__init__.py +0 -23
- udata/tests/metrics/conftest.py +0 -15
- {udata-12.0.2.dev15.dist-info → udata-13.0.1.dev21.dist-info}/WHEEL +0 -0
- {udata-12.0.2.dev15.dist-info → udata-13.0.1.dev21.dist-info}/entry_points.txt +0 -0
- {udata-12.0.2.dev15.dist-info → udata-13.0.1.dev21.dist-info}/licenses/LICENSE +0 -0
- {udata-12.0.2.dev15.dist-info → udata-13.0.1.dev21.dist-info}/top_level.txt +0 -0
udata/core/dataset/search.py
CHANGED
|
@@ -54,8 +54,8 @@ class DatasetSearch(ModelSearchAdapter):
|
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
@classmethod
|
|
57
|
-
def is_indexable(cls, dataset):
|
|
58
|
-
return dataset.
|
|
57
|
+
def is_indexable(cls, dataset: Dataset):
|
|
58
|
+
return dataset.is_visible
|
|
59
59
|
|
|
60
60
|
@classmethod
|
|
61
61
|
def mongo_search(cls, args):
|
udata/core/dataset/tasks.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import collections
|
|
2
2
|
import os
|
|
3
|
-
from datetime import datetime
|
|
3
|
+
from datetime import date, datetime
|
|
4
4
|
from tempfile import NamedTemporaryFile
|
|
5
5
|
|
|
6
6
|
from celery.utils.log import get_task_logger
|
|
@@ -9,9 +9,15 @@ from mongoengine import ValidationError
|
|
|
9
9
|
|
|
10
10
|
from udata import models as udata_models
|
|
11
11
|
from udata.core import csv, storages
|
|
12
|
+
from udata.core.badges import tasks as badge_tasks
|
|
13
|
+
from udata.core.constants import HVD
|
|
12
14
|
from udata.core.dataservices.models import Dataservice
|
|
15
|
+
from udata.core.dataset.constants import INSPIRE
|
|
16
|
+
from udata.core.organization.constants import CERTIFIED, PUBLIC_SERVICE
|
|
17
|
+
from udata.core.organization.models import Organization
|
|
13
18
|
from udata.harvest.models import HarvestJob
|
|
14
19
|
from udata.models import Activity, Discussion, Follow, TopicElement, Transfer, db
|
|
20
|
+
from udata.storage.s3 import store_bytes
|
|
15
21
|
from udata.tasks import job
|
|
16
22
|
|
|
17
23
|
from .models import Checksum, CommunityResource, Dataset, Resource
|
|
@@ -85,12 +91,14 @@ def get_queryset(model_cls):
|
|
|
85
91
|
return model_cls.objects.filter(**params).no_cache()
|
|
86
92
|
|
|
87
93
|
|
|
94
|
+
def get_resource_for_csv_export_model(model, dataset):
|
|
95
|
+
for resource in dataset.resources:
|
|
96
|
+
if resource.extras.get("csv-export:model", "") == model:
|
|
97
|
+
return resource
|
|
98
|
+
|
|
99
|
+
|
|
88
100
|
def get_or_create_resource(r_info, model, dataset):
|
|
89
|
-
resource =
|
|
90
|
-
for r in dataset.resources:
|
|
91
|
-
if r.extras.get("csv-export:model", "") == model:
|
|
92
|
-
resource = r
|
|
93
|
-
break
|
|
101
|
+
resource = get_resource_for_csv_export_model(model, dataset)
|
|
94
102
|
if resource:
|
|
95
103
|
for k, v in r_info.items():
|
|
96
104
|
setattr(resource, k, v)
|
|
@@ -121,11 +129,16 @@ def store_resource(csvfile, model, dataset):
|
|
|
121
129
|
return get_or_create_resource(r_info, model, dataset)
|
|
122
130
|
|
|
123
131
|
|
|
124
|
-
def export_csv_for_model(model, dataset):
|
|
132
|
+
def export_csv_for_model(model, dataset, replace: bool = False):
|
|
125
133
|
model_cls = getattr(udata_models, model.capitalize(), None)
|
|
126
134
|
if not model_cls:
|
|
127
135
|
log.error("Unknow model %s" % model)
|
|
128
136
|
return
|
|
137
|
+
|
|
138
|
+
fs_filename_to_remove = None
|
|
139
|
+
if existing_resource := get_resource_for_csv_export_model(model, dataset):
|
|
140
|
+
fs_filename_to_remove = existing_resource.fs_filename
|
|
141
|
+
|
|
129
142
|
queryset = get_queryset(model_cls)
|
|
130
143
|
adapter = csv.get_adapter(model_cls)
|
|
131
144
|
if not adapter:
|
|
@@ -151,6 +164,10 @@ def export_csv_for_model(model, dataset):
|
|
|
151
164
|
else:
|
|
152
165
|
dataset.last_modified_internal = datetime.utcnow()
|
|
153
166
|
dataset.save()
|
|
167
|
+
# remove previous catalog if exists and replace is True
|
|
168
|
+
if replace and fs_filename_to_remove:
|
|
169
|
+
storages.resources.delete(fs_filename_to_remove)
|
|
170
|
+
return resource
|
|
154
171
|
finally:
|
|
155
172
|
csvfile.close()
|
|
156
173
|
os.unlink(csvfile.name)
|
|
@@ -179,7 +196,23 @@ def export_csv(self, model=None):
|
|
|
179
196
|
|
|
180
197
|
models = (model,) if model else ALLOWED_MODELS
|
|
181
198
|
for model in models:
|
|
182
|
-
export_csv_for_model(model, dataset)
|
|
199
|
+
resource = export_csv_for_model(model, dataset, replace=True)
|
|
200
|
+
|
|
201
|
+
# If we are the first day of the month, archive today catalogs
|
|
202
|
+
if (
|
|
203
|
+
current_app.config["EXPORT_CSV_ARCHIVE_S3_BUCKET"]
|
|
204
|
+
and resource
|
|
205
|
+
and date.today().day == 1
|
|
206
|
+
):
|
|
207
|
+
log.info(
|
|
208
|
+
f"Archiving {model} csv catalog on {current_app.config['EXPORT_CSV_ARCHIVE_S3_BUCKET']} bucket"
|
|
209
|
+
)
|
|
210
|
+
with storages.resources.open(resource.fs_filename, "rb") as f:
|
|
211
|
+
store_bytes(
|
|
212
|
+
bucket=current_app.config["EXPORT_CSV_ARCHIVE_S3_BUCKET"],
|
|
213
|
+
filename=f"{current_app.config['EXPORT_CSV_ARCHIVE_S3_FILENAME_PREFIX']}{resource.title}",
|
|
214
|
+
bytes=f.read(),
|
|
215
|
+
)
|
|
183
216
|
|
|
184
217
|
|
|
185
218
|
@job("bind-tabular-dataservice")
|
|
@@ -213,3 +246,48 @@ def bind_tabular_dataservice(self):
|
|
|
213
246
|
log.error(exc_info=e)
|
|
214
247
|
|
|
215
248
|
log.info(f"Bound {datasets.count()} datasets to TabularAPI dataservice")
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
@badge_tasks.register(model=Dataset, badge=HVD)
|
|
252
|
+
def update_dataset_hvd_badge() -> None:
|
|
253
|
+
"""
|
|
254
|
+
Update HVD badges to candidate datasets, based on the hvd tag.
|
|
255
|
+
Only datasets owned by certified and public service organizations are candidate to have a HVD badge.
|
|
256
|
+
"""
|
|
257
|
+
if not current_app.config["HVD_SUPPORT"]:
|
|
258
|
+
log.error("You need to set HVD_SUPPORT if you want to update dataset hvd badge")
|
|
259
|
+
return
|
|
260
|
+
public_certified_orgs = (
|
|
261
|
+
Organization.objects(badges__kind=PUBLIC_SERVICE).filter(badges__kind=CERTIFIED).only("id")
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
datasets = Dataset.objects(
|
|
265
|
+
tags="hvd", badges__kind__ne="hvd", organization__in=public_certified_orgs
|
|
266
|
+
)
|
|
267
|
+
log.info(f"Adding HVD badge to {datasets.count()} datasets")
|
|
268
|
+
for dataset in datasets:
|
|
269
|
+
dataset.add_badge(HVD)
|
|
270
|
+
|
|
271
|
+
datasets = Dataset.objects(tags__nin=["hvd"], badges__kind="hvd")
|
|
272
|
+
log.info(f"Removing HVD badge from {datasets.count()} datasets")
|
|
273
|
+
for dataset in datasets:
|
|
274
|
+
dataset.remove_badge(HVD)
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
@badge_tasks.register(model=Dataset, badge=INSPIRE)
|
|
278
|
+
def update_dataset_inspire_badge() -> None:
|
|
279
|
+
"""
|
|
280
|
+
Update INSPIRE badges to candidate datasets, based on the inspire tag.
|
|
281
|
+
"""
|
|
282
|
+
if not current_app.config["INSPIRE_SUPPORT"]:
|
|
283
|
+
log.error("You need to set INSPIRE_SUPPORT if you want to update dataset INSPIRE badge")
|
|
284
|
+
return
|
|
285
|
+
datasets = Dataset.objects(tags="inspire", badges__kind__ne="inspire")
|
|
286
|
+
log.info(f"Adding INSPIRE badge to {datasets.count()} datasets")
|
|
287
|
+
for dataset in datasets:
|
|
288
|
+
dataset.add_badge(INSPIRE)
|
|
289
|
+
|
|
290
|
+
datasets = Dataset.objects(tags__nin=["inspire"], badges__kind="inspire")
|
|
291
|
+
log.info(f"Removing INSPIRE badge from {datasets.count()} datasets")
|
|
292
|
+
for dataset in datasets:
|
|
293
|
+
dataset.remove_badge(INSPIRE)
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
from udata.core.discussions.models import Discussion, Message
|
|
2
|
+
from udata.i18n import lazy_gettext as _
|
|
3
|
+
from udata.mail import LabelledContent, MailCTA, MailMessage, ParagraphWithLinks
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def new_discussion(discussion: Discussion) -> MailMessage:
|
|
7
|
+
return MailMessage(
|
|
8
|
+
subject=_(
|
|
9
|
+
"A new discussion has been opened on your %(type)s",
|
|
10
|
+
type=discussion.subject.verbose_name,
|
|
11
|
+
),
|
|
12
|
+
paragraphs=[
|
|
13
|
+
ParagraphWithLinks(
|
|
14
|
+
_(
|
|
15
|
+
"You have a new discussion from %(user_or_org)s on your %(type)s %(object)s",
|
|
16
|
+
user_or_org=discussion.organization or discussion.user,
|
|
17
|
+
type=discussion.subject.verbose_name,
|
|
18
|
+
object=discussion.subject,
|
|
19
|
+
)
|
|
20
|
+
),
|
|
21
|
+
LabelledContent(_("Discussion title:"), discussion.title, inline=True),
|
|
22
|
+
LabelledContent(_("Comment:"), discussion.discussion[0].content),
|
|
23
|
+
MailCTA(_("Reply"), discussion.url_for()),
|
|
24
|
+
],
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def new_discussion_comment(discussion: Discussion, comment: Message) -> MailMessage:
|
|
29
|
+
return MailMessage(
|
|
30
|
+
subject=_("A new comment has been added to a discussion"),
|
|
31
|
+
paragraphs=[
|
|
32
|
+
ParagraphWithLinks(
|
|
33
|
+
_(
|
|
34
|
+
"You have a new comment from %(user_or_org)s on your %(type)s %(object)s",
|
|
35
|
+
user_or_org=comment.posted_by_org_or_user,
|
|
36
|
+
type=discussion.subject.verbose_name,
|
|
37
|
+
object=discussion.subject,
|
|
38
|
+
)
|
|
39
|
+
),
|
|
40
|
+
LabelledContent(_("Discussion title:"), discussion.title, inline=True),
|
|
41
|
+
LabelledContent(_("Comment:"), comment.content),
|
|
42
|
+
MailCTA(_("Reply"), discussion.url_for()),
|
|
43
|
+
],
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def discussion_closed(discussion: Discussion, comment: Message | None) -> MailMessage:
|
|
48
|
+
return MailMessage(
|
|
49
|
+
subject=_("A discussion has been closed"),
|
|
50
|
+
paragraphs=[
|
|
51
|
+
ParagraphWithLinks(
|
|
52
|
+
_(
|
|
53
|
+
"The discussion you participated in on the %(type)s %(object)s has been closed by %(user_or_org)s.",
|
|
54
|
+
user_or_org=discussion.closed_by_org_or_user,
|
|
55
|
+
type=discussion.subject.verbose_name,
|
|
56
|
+
object=discussion.subject,
|
|
57
|
+
)
|
|
58
|
+
),
|
|
59
|
+
LabelledContent(_("Discussion title:"), discussion.title, inline=True),
|
|
60
|
+
LabelledContent(_("Comment:"), comment.content) if comment else None,
|
|
61
|
+
MailCTA(_("View the discussion"), discussion.url_for()),
|
|
62
|
+
],
|
|
63
|
+
)
|
udata/core/discussions/tasks.py
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
from udata import mail
|
|
2
|
-
from udata.i18n import lazy_gettext as _
|
|
3
1
|
from udata.tasks import connect, get_logger
|
|
4
2
|
|
|
3
|
+
from . import mails
|
|
5
4
|
from .constants import NOTIFY_DISCUSSION_SUBJECTS
|
|
6
5
|
from .models import Discussion
|
|
7
6
|
from .signals import on_discussion_closed, on_new_discussion, on_new_discussion_comment
|
|
@@ -22,15 +21,7 @@ def owner_recipients(discussion):
|
|
|
22
21
|
def notify_new_discussion(discussion_id):
|
|
23
22
|
discussion = Discussion.objects.get(pk=discussion_id)
|
|
24
23
|
if isinstance(discussion.subject, NOTIFY_DISCUSSION_SUBJECTS):
|
|
25
|
-
|
|
26
|
-
subject = _("Your %(type)s have a new discussion", type=discussion.subject.verbose_name)
|
|
27
|
-
mail.send(
|
|
28
|
-
subject,
|
|
29
|
-
recipients,
|
|
30
|
-
"new_discussion",
|
|
31
|
-
discussion=discussion,
|
|
32
|
-
message=discussion.discussion[0],
|
|
33
|
-
)
|
|
24
|
+
mails.new_discussion(discussion).send(owner_recipients(discussion))
|
|
34
25
|
else:
|
|
35
26
|
log.warning("Unrecognized discussion subject type %s", type(discussion.subject))
|
|
36
27
|
|
|
@@ -42,11 +33,7 @@ def notify_new_discussion_comment(discussion_id, message=None):
|
|
|
42
33
|
if isinstance(discussion.subject, NOTIFY_DISCUSSION_SUBJECTS):
|
|
43
34
|
recipients = owner_recipients(discussion) + [m.posted_by for m in discussion.discussion]
|
|
44
35
|
recipients = list({u.id: u for u in recipients if u != message.posted_by}.values())
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
mail.send(
|
|
48
|
-
subject, recipients, "new_discussion_comment", discussion=discussion, message=message
|
|
49
|
-
)
|
|
36
|
+
mails.new_discussion_comment(discussion, message).send(recipients)
|
|
50
37
|
else:
|
|
51
38
|
log.warning("Unrecognized discussion subject type %s", type(discussion.subject))
|
|
52
39
|
|
|
@@ -58,7 +45,6 @@ def notify_discussion_closed(discussion_id, message=None):
|
|
|
58
45
|
if isinstance(discussion.subject, NOTIFY_DISCUSSION_SUBJECTS):
|
|
59
46
|
recipients = owner_recipients(discussion) + [m.posted_by for m in discussion.discussion]
|
|
60
47
|
recipients = list({u.id: u for u in recipients if u != discussion.closed_by}.values())
|
|
61
|
-
|
|
62
|
-
mail.send(subject, recipients, "discussion_closed", discussion=discussion, message=message)
|
|
48
|
+
mails.discussion_closed(discussion, message).send(recipients)
|
|
63
49
|
else:
|
|
64
50
|
log.warning("Unrecognized discussion subject type %s", type(discussion.subject))
|
udata/core/metrics/__init__.py
CHANGED
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
from udata import entrypoints
|
|
2
|
-
|
|
3
|
-
|
|
4
1
|
def init_app(app):
|
|
5
2
|
# Load all core metrics
|
|
6
3
|
import udata.core.user.metrics # noqa
|
|
@@ -9,6 +6,3 @@ def init_app(app):
|
|
|
9
6
|
import udata.core.dataset.metrics # noqa
|
|
10
7
|
import udata.core.reuse.metrics # noqa
|
|
11
8
|
import udata.core.followers.metrics # noqa
|
|
12
|
-
|
|
13
|
-
# Load metrics from plugins
|
|
14
|
-
entrypoints.get_enabled("udata.metrics", app)
|
udata/core/organization/api.py
CHANGED
|
@@ -49,7 +49,7 @@ from .forms import (
|
|
|
49
49
|
from .models import Member, MembershipRequest, Organization
|
|
50
50
|
from .permissions import EditOrganizationPermission, OrganizationPrivatePermission
|
|
51
51
|
from .rdf import build_org_catalog
|
|
52
|
-
from .tasks import notify_membership_request, notify_membership_response
|
|
52
|
+
from .tasks import notify_membership_request, notify_membership_response, notify_new_member
|
|
53
53
|
|
|
54
54
|
DEFAULT_SORTING = "-created_at"
|
|
55
55
|
SUGGEST_SORTING = "-metrics.followers"
|
|
@@ -472,6 +472,8 @@ class MemberAPI(API):
|
|
|
472
472
|
org.count_members()
|
|
473
473
|
org.save()
|
|
474
474
|
|
|
475
|
+
notify_new_member.delay(str(org.id), str(member.user.email))
|
|
476
|
+
|
|
475
477
|
return member, 201
|
|
476
478
|
|
|
477
479
|
@api.secure
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
from udata.core.organization.models import MembershipRequest, Organization
|
|
2
|
+
from udata.i18n import lazy_gettext as _
|
|
3
|
+
from udata.mail import LabelledContent, MailCTA, MailMessage, ParagraphWithLinks
|
|
4
|
+
from udata.uris import cdata_url
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def new_membership_request(org: Organization, request: MembershipRequest) -> MailMessage:
|
|
8
|
+
return MailMessage(
|
|
9
|
+
subject=_("New membership request"),
|
|
10
|
+
paragraphs=[
|
|
11
|
+
ParagraphWithLinks(
|
|
12
|
+
_(
|
|
13
|
+
"You received a membership request from %(user)s for your organization %(org)s",
|
|
14
|
+
user=request.user,
|
|
15
|
+
org=org,
|
|
16
|
+
)
|
|
17
|
+
),
|
|
18
|
+
LabelledContent(_("Reason for the request:"), request.comment),
|
|
19
|
+
MailCTA(_("See the request"), cdata_url(f"/admin/organizations/{org.id}/members/")),
|
|
20
|
+
],
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def membership_refused(org: Organization) -> MailMessage:
|
|
25
|
+
return MailMessage(
|
|
26
|
+
subject=_("Membership refused"),
|
|
27
|
+
paragraphs=[
|
|
28
|
+
ParagraphWithLinks(
|
|
29
|
+
_(
|
|
30
|
+
"Your membership for the organization %(org)s has been refused",
|
|
31
|
+
org=org,
|
|
32
|
+
)
|
|
33
|
+
),
|
|
34
|
+
],
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def membership_accepted(org: Organization) -> MailMessage:
|
|
39
|
+
return MailMessage(
|
|
40
|
+
subject=_("Your invitation to join an organization has been accepted"),
|
|
41
|
+
paragraphs=[
|
|
42
|
+
ParagraphWithLinks(
|
|
43
|
+
_(
|
|
44
|
+
"Good news! Your request to join the organization %(org)s has been approved.",
|
|
45
|
+
org=org,
|
|
46
|
+
)
|
|
47
|
+
),
|
|
48
|
+
MailCTA(
|
|
49
|
+
_("View the organization"), cdata_url(f"/admin/organizations/{org.id}/datasets")
|
|
50
|
+
),
|
|
51
|
+
],
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def new_member(org: Organization) -> MailMessage:
|
|
56
|
+
return MailMessage(
|
|
57
|
+
subject=_("You have been added as a member of an organization"),
|
|
58
|
+
paragraphs=[
|
|
59
|
+
ParagraphWithLinks(
|
|
60
|
+
_(
|
|
61
|
+
"Good news! You are now a member of %(org)s.",
|
|
62
|
+
org=org,
|
|
63
|
+
)
|
|
64
|
+
),
|
|
65
|
+
MailCTA(
|
|
66
|
+
_("View the organization"), cdata_url(f"/admin/organizations/{org.id}/datasets")
|
|
67
|
+
),
|
|
68
|
+
],
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def badge_added_certified(org: Organization) -> MailMessage:
|
|
73
|
+
return MailMessage(
|
|
74
|
+
subject=_("Your organization has been certified"),
|
|
75
|
+
paragraphs=[
|
|
76
|
+
ParagraphWithLinks(
|
|
77
|
+
_(
|
|
78
|
+
"Good news! Your organization %(org)s has been certified by our team. A badge is now associated with your organization.",
|
|
79
|
+
org=org,
|
|
80
|
+
)
|
|
81
|
+
),
|
|
82
|
+
MailCTA(_("View the organization"), org.self_web_url()),
|
|
83
|
+
],
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def badge_added_public_service(org: Organization) -> MailMessage:
|
|
88
|
+
return MailMessage(
|
|
89
|
+
subject=_("Your organization has been identified as a public service"),
|
|
90
|
+
paragraphs=[
|
|
91
|
+
ParagraphWithLinks(
|
|
92
|
+
_(
|
|
93
|
+
"Good news! Your organization %(org)s has been identified by our team as a public service. A badge is now associated with your organization.",
|
|
94
|
+
org=org,
|
|
95
|
+
)
|
|
96
|
+
),
|
|
97
|
+
MailCTA(_("View the organization"), org.self_web_url()),
|
|
98
|
+
],
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def badge_added_local_authority(org: Organization) -> MailMessage:
|
|
103
|
+
return MailMessage(
|
|
104
|
+
subject=_("Your organization has been identified as a local authority"),
|
|
105
|
+
paragraphs=[
|
|
106
|
+
ParagraphWithLinks(
|
|
107
|
+
_(
|
|
108
|
+
"Good news! Your organization %(org)s has been identified by our team as a local authority. A badge is now associated with your organization.",
|
|
109
|
+
org=org,
|
|
110
|
+
)
|
|
111
|
+
),
|
|
112
|
+
MailCTA(_("View the organization"), org.self_web_url()),
|
|
113
|
+
],
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def badge_added_company(org: Organization) -> MailMessage:
|
|
118
|
+
return MailMessage(
|
|
119
|
+
subject=_("Your organization has been identified as a company"),
|
|
120
|
+
paragraphs=[
|
|
121
|
+
ParagraphWithLinks(
|
|
122
|
+
_(
|
|
123
|
+
"Your organization %(org)s has been identified by our team as a company. A badge is now associated with your organization.",
|
|
124
|
+
org=org,
|
|
125
|
+
)
|
|
126
|
+
),
|
|
127
|
+
MailCTA(_("View the organization"), org.self_web_url()),
|
|
128
|
+
],
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def badge_added_association(org: Organization) -> MailMessage:
|
|
133
|
+
return MailMessage(
|
|
134
|
+
subject=_("Your organization has been identified as an association"),
|
|
135
|
+
paragraphs=[
|
|
136
|
+
ParagraphWithLinks(
|
|
137
|
+
_(
|
|
138
|
+
"Your organization %(org)s has been identified by our team as an association. A badge is now associated with your organization.",
|
|
139
|
+
org=org,
|
|
140
|
+
)
|
|
141
|
+
),
|
|
142
|
+
MailCTA(_("View the organization"), org.self_web_url()),
|
|
143
|
+
],
|
|
144
|
+
)
|
|
@@ -3,6 +3,7 @@ from itertools import chain
|
|
|
3
3
|
|
|
4
4
|
from blinker import Signal
|
|
5
5
|
from flask import url_for
|
|
6
|
+
from flask_babel import LazyString
|
|
6
7
|
from mongoengine.signals import post_save, pre_save
|
|
7
8
|
from werkzeug.utils import cached_property
|
|
8
9
|
|
|
@@ -34,7 +35,7 @@ from .constants import (
|
|
|
34
35
|
|
|
35
36
|
__all__ = ("Organization", "Team", "Member", "MembershipRequest")
|
|
36
37
|
|
|
37
|
-
BADGES: dict[str,
|
|
38
|
+
BADGES: dict[str, LazyString] = {
|
|
38
39
|
PUBLIC_SERVICE: _("Public Service"),
|
|
39
40
|
CERTIFIED: _("Certified"),
|
|
40
41
|
ASSOCIATION: _("Association"),
|
udata/core/organization/tasks.py
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
from udata import mail
|
|
2
1
|
from udata.core import storages
|
|
3
2
|
from udata.core.badges.tasks import notify_new_badge
|
|
4
|
-
from udata.i18n import lazy_gettext as _
|
|
5
3
|
from udata.models import Activity, ContactPoint, Dataset, Follow, Transfer
|
|
6
4
|
from udata.search import reindex
|
|
7
5
|
from udata.tasks import get_logger, job, task
|
|
8
6
|
|
|
7
|
+
from . import mails
|
|
9
8
|
from .constants import ASSOCIATION, CERTIFIED, COMPANY, LOCAL_AUTHORITY, PUBLIC_SERVICE
|
|
10
9
|
from .models import Organization
|
|
11
10
|
|
|
@@ -47,10 +46,11 @@ def notify_membership_request(org_id, request_id):
|
|
|
47
46
|
org = Organization.objects.get(pk=org_id)
|
|
48
47
|
request = next((r for r in org.requests if str(r.id) == request_id), None)
|
|
49
48
|
|
|
49
|
+
if request is None:
|
|
50
|
+
return
|
|
51
|
+
|
|
50
52
|
recipients = [m.user for m in org.by_role("admin")]
|
|
51
|
-
|
|
52
|
-
_("New membership request"), recipients, "membership_request", org=org, request=request
|
|
53
|
-
)
|
|
53
|
+
mails.new_membership_request(org, request).send(recipients)
|
|
54
54
|
|
|
55
55
|
|
|
56
56
|
@task(route="high.mail")
|
|
@@ -58,12 +58,13 @@ def notify_membership_response(org_id, request_id):
|
|
|
58
58
|
org = Organization.objects.get(pk=org_id)
|
|
59
59
|
request = next((r for r in org.requests if str(r.id) == request_id), None)
|
|
60
60
|
|
|
61
|
+
if request is None:
|
|
62
|
+
return
|
|
63
|
+
|
|
61
64
|
if request.status == "accepted":
|
|
62
|
-
|
|
63
|
-
template = "new_member"
|
|
65
|
+
mails.membership_accepted(org).send(request.user)
|
|
64
66
|
else:
|
|
65
|
-
|
|
66
|
-
mail.send(subject, request.user, template, org=org, request=request)
|
|
67
|
+
mails.membership_refused(org).send(request.user)
|
|
67
68
|
|
|
68
69
|
|
|
69
70
|
@task
|
|
@@ -71,8 +72,10 @@ def notify_new_member(org_id, email):
|
|
|
71
72
|
org = Organization.objects.get(pk=org_id)
|
|
72
73
|
member = next((m for m in org.members if m.user.email == email), None)
|
|
73
74
|
|
|
74
|
-
|
|
75
|
-
|
|
75
|
+
if member is None:
|
|
76
|
+
return
|
|
77
|
+
|
|
78
|
+
mails.new_member(org).send(member.user)
|
|
76
79
|
|
|
77
80
|
|
|
78
81
|
@notify_new_badge(Organization, CERTIFIED)
|
|
@@ -82,14 +85,8 @@ def notify_badge_certified(org_id):
|
|
|
82
85
|
"""
|
|
83
86
|
org = Organization.objects.get(pk=org_id)
|
|
84
87
|
recipients = [member.user for member in org.members]
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
subject,
|
|
88
|
-
recipients,
|
|
89
|
-
"badge_added_certified",
|
|
90
|
-
organization=org,
|
|
91
|
-
badge=org.get_badge(CERTIFIED),
|
|
92
|
-
)
|
|
88
|
+
|
|
89
|
+
mails.badge_added_certified(org).send(recipients)
|
|
93
90
|
|
|
94
91
|
|
|
95
92
|
@notify_new_badge(Organization, PUBLIC_SERVICE)
|
|
@@ -99,14 +96,8 @@ def notify_badge_public_service(org_id):
|
|
|
99
96
|
"""
|
|
100
97
|
org = Organization.objects.get(pk=org_id)
|
|
101
98
|
recipients = [member.user for member in org.members]
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
subject,
|
|
105
|
-
recipients,
|
|
106
|
-
"badge_added_public_service",
|
|
107
|
-
organization=org,
|
|
108
|
-
badge=org.get_badge(PUBLIC_SERVICE),
|
|
109
|
-
)
|
|
99
|
+
|
|
100
|
+
mails.badge_added_public_service(org).send(recipients)
|
|
110
101
|
|
|
111
102
|
|
|
112
103
|
@notify_new_badge(Organization, COMPANY)
|
|
@@ -116,10 +107,7 @@ def notify_badge_company(org_id):
|
|
|
116
107
|
"""
|
|
117
108
|
org = Organization.objects.get(pk=org_id)
|
|
118
109
|
recipients = [member.user for member in org.members]
|
|
119
|
-
|
|
120
|
-
mail.send(
|
|
121
|
-
subject, recipients, "badge_added_company", organization=org, badge=org.get_badge(COMPANY)
|
|
122
|
-
)
|
|
110
|
+
mails.badge_added_company(org).send(recipients)
|
|
123
111
|
|
|
124
112
|
|
|
125
113
|
@notify_new_badge(Organization, ASSOCIATION)
|
|
@@ -129,14 +117,7 @@ def notify_badge_association(org_id):
|
|
|
129
117
|
"""
|
|
130
118
|
org = Organization.objects.get(pk=org_id)
|
|
131
119
|
recipients = [member.user for member in org.members]
|
|
132
|
-
|
|
133
|
-
mail.send(
|
|
134
|
-
subject,
|
|
135
|
-
recipients,
|
|
136
|
-
"badge_added_association",
|
|
137
|
-
organization=org,
|
|
138
|
-
badge=org.get_badge(ASSOCIATION),
|
|
139
|
-
)
|
|
120
|
+
mails.badge_added_association(org).send(recipients)
|
|
140
121
|
|
|
141
122
|
|
|
142
123
|
@notify_new_badge(Organization, LOCAL_AUTHORITY)
|
|
@@ -146,13 +127,4 @@ def notify_badge_local_authority(org_id):
|
|
|
146
127
|
"""
|
|
147
128
|
org = Organization.objects.get(pk=org_id)
|
|
148
129
|
recipients = [member.user for member in org.members]
|
|
149
|
-
|
|
150
|
-
'Your organization "%(name)s" has been identified as a local authority', name=org.name
|
|
151
|
-
)
|
|
152
|
-
mail.send(
|
|
153
|
-
subject,
|
|
154
|
-
recipients,
|
|
155
|
-
"badge_added_local_authority",
|
|
156
|
-
organization=org,
|
|
157
|
-
badge=org.get_badge(LOCAL_AUTHORITY),
|
|
158
|
-
)
|
|
130
|
+
mails.badge_added_local_authority(org).send(recipients)
|
udata/core/reuse/api.py
CHANGED
|
@@ -12,6 +12,7 @@ from udata.api_fields import patch, patch_and_save
|
|
|
12
12
|
from udata.auth import admin_permission
|
|
13
13
|
from udata.core.badges import api as badges_api
|
|
14
14
|
from udata.core.badges.fields import badge_fields
|
|
15
|
+
from udata.core.dataservices.models import Dataservice
|
|
15
16
|
from udata.core.dataset.api_fields import dataset_ref_fields
|
|
16
17
|
from udata.core.followers.api import FollowAPI
|
|
17
18
|
from udata.core.organization.models import Organization
|
|
@@ -227,7 +228,8 @@ class ReuseDatasetsAPI(API):
|
|
|
227
228
|
dataset = Dataset.objects.get_or_404(id=id_or_404(request.json["id"]))
|
|
228
229
|
except Dataset.DoesNotExist:
|
|
229
230
|
msg = "Dataset {0} does not exists".format(request.json["id"])
|
|
230
|
-
api.abort(404, msg)
|
|
231
|
+
return api.abort(404, msg)
|
|
232
|
+
|
|
231
233
|
if dataset in reuse.datasets:
|
|
232
234
|
return reuse
|
|
233
235
|
reuse.datasets.append(dataset)
|
|
@@ -235,6 +237,30 @@ class ReuseDatasetsAPI(API):
|
|
|
235
237
|
return reuse, 201
|
|
236
238
|
|
|
237
239
|
|
|
240
|
+
@ns.route("/<reuse:reuse>/dataservices/", endpoint="reuse_add_dataservice")
|
|
241
|
+
class ReuseDataservicesAPI(API):
|
|
242
|
+
@api.secure
|
|
243
|
+
@api.doc("reuse_add_dataservice", **common_doc)
|
|
244
|
+
@api.expect(Dataservice.__ref_fields__)
|
|
245
|
+
@api.response(200, "The dataservice is already present", Reuse.__read_fields__)
|
|
246
|
+
@api.marshal_with(Reuse.__read_fields__, code=201)
|
|
247
|
+
def post(self, reuse):
|
|
248
|
+
"""Add a dataservice to a given reuse"""
|
|
249
|
+
if "id" not in request.json:
|
|
250
|
+
api.abort(400, "Expect a dataservice identifier")
|
|
251
|
+
try:
|
|
252
|
+
dataservice = Dataservice.objects.get_or_404(id=id_or_404(request.json["id"]))
|
|
253
|
+
except Dataservice.DoesNotExist:
|
|
254
|
+
msg = "Dataservice {0} does not exists".format(request.json["id"])
|
|
255
|
+
return api.abort(404, msg)
|
|
256
|
+
|
|
257
|
+
if dataservice in reuse.dataservices:
|
|
258
|
+
return reuse
|
|
259
|
+
reuse.dataservices.append(dataservice)
|
|
260
|
+
reuse.save()
|
|
261
|
+
return reuse, 201
|
|
262
|
+
|
|
263
|
+
|
|
238
264
|
@ns.route("/badges/", endpoint="available_reuse_badges")
|
|
239
265
|
class AvailableDatasetBadgesAPI(API):
|
|
240
266
|
@api.doc("available_reuse_badges")
|