udata 12.0.2.dev10__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 -4
- 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 +10 -12
- 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.py +15 -24
- 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 +24 -42
- 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 +20 -14
- udata/core/organization/mails.py +144 -0
- udata/core/organization/models.py +2 -1
- udata/core/organization/rdf.py +3 -3
- 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 +29 -3
- 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/api.py +27 -19
- udata/core/site/models.py +2 -6
- udata/core/site/rdf.py +2 -2
- 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 -6
- 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/bnodes.xml +17 -1
- 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 +72 -16
- 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/models/__init__.py +0 -2
- udata/mongo/extras_fields.py +4 -3
- udata/mongo/taglist_field.py +3 -3
- udata/rdf.py +65 -20
- udata/sentry.py +3 -4
- udata/settings.py +15 -13
- 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 +65 -97
- udata/tests/api/test_datasets_api.py +171 -56
- 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 +99 -23
- 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 +64 -4
- 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 +205 -16
- 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 +6 -12
- 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 +85 -29
- 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 +66 -4
- {udata-12.0.2.dev10.dist-info → udata-13.0.1.dev21.dist-info}/METADATA +1 -4
- {udata-12.0.2.dev10.dist-info → udata-13.0.1.dev21.dist-info}/RECORD +212 -256
- udata/linkchecker/__init__.py +0 -0
- udata/linkchecker/backends.py +0 -31
- udata/linkchecker/checker.py +0 -75
- udata/linkchecker/commands.py +0 -21
- udata/linkchecker/models.py +0 -9
- udata/linkchecker/tasks.py +0 -55
- 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/tests/test_linkchecker.py +0 -277
- {udata-12.0.2.dev10.dist-info → udata-13.0.1.dev21.dist-info}/WHEEL +0 -0
- {udata-12.0.2.dev10.dist-info → udata-13.0.1.dev21.dist-info}/entry_points.txt +0 -0
- {udata-12.0.2.dev10.dist-info → udata-13.0.1.dev21.dist-info}/licenses/LICENSE +0 -0
- {udata-12.0.2.dev10.dist-info → udata-13.0.1.dev21.dist-info}/top_level.txt +0 -0
|
@@ -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/rdf.py
CHANGED
|
@@ -32,7 +32,7 @@ def organization_to_rdf(org, graph=None):
|
|
|
32
32
|
return o
|
|
33
33
|
|
|
34
34
|
|
|
35
|
-
def build_org_catalog(org, datasets, dataservices,
|
|
35
|
+
def build_org_catalog(org, datasets, dataservices, _format=None, **kwargs):
|
|
36
36
|
graph = Graph(namespace_manager=namespace_manager)
|
|
37
37
|
org_catalog_url = url_for("api.organization_rdf", org=org.id, _external=True)
|
|
38
38
|
|
|
@@ -47,9 +47,9 @@ def build_org_catalog(org, datasets, dataservices, format=None):
|
|
|
47
47
|
for dataservice in dataservices:
|
|
48
48
|
catalog.add(DCAT.service, dataservice_to_rdf(dataservice, graph))
|
|
49
49
|
|
|
50
|
-
values = {"org": org.id}
|
|
50
|
+
values = {**kwargs, "org": org.id}
|
|
51
51
|
|
|
52
52
|
if isinstance(datasets, Paginable):
|
|
53
|
-
paginate_catalog(catalog, graph, datasets,
|
|
53
|
+
paginate_catalog(catalog, graph, datasets, _format, "api.organization_rdf_format", **values)
|
|
54
54
|
|
|
55
55
|
return catalog
|
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
|
|
@@ -24,7 +25,7 @@ from udata.core.storages.api import (
|
|
|
24
25
|
from udata.frontend.markdown import md
|
|
25
26
|
from udata.i18n import gettext as _
|
|
26
27
|
from udata.models import Dataset
|
|
27
|
-
from udata.utils import id_or_404
|
|
28
|
+
from udata.utils import get_rss_feed_list, id_or_404
|
|
28
29
|
|
|
29
30
|
from .api_fields import (
|
|
30
31
|
reuse_suggestion_fields,
|
|
@@ -143,7 +144,7 @@ class ReusesAtomFeedAPI(API):
|
|
|
143
144
|
link=request.url_root,
|
|
144
145
|
)
|
|
145
146
|
|
|
146
|
-
reuses
|
|
147
|
+
reuses = get_rss_feed_list(Reuse.objects.visible(), "created_at")
|
|
147
148
|
for reuse in reuses:
|
|
148
149
|
author_name = None
|
|
149
150
|
author_uri = None
|
|
@@ -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")
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from udata.core.dataset.models import Dataset
|
|
2
|
+
from udata.core.reuse.models import Reuse
|
|
3
|
+
from udata.i18n import lazy_gettext as _
|
|
4
|
+
from udata.mail import LabelledContent, MailCTA, MailMessage, ParagraphWithLinks
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def new_reuse(reuse: Reuse, dataset: Dataset) -> MailMessage:
|
|
8
|
+
return MailMessage(
|
|
9
|
+
subject=_("New reuse on your dataset"),
|
|
10
|
+
paragraphs=[
|
|
11
|
+
ParagraphWithLinks(
|
|
12
|
+
_(
|
|
13
|
+
"A new reuse has been published by %(user_or_org)s on your dataset %(dataset)s",
|
|
14
|
+
user_or_org=reuse.organization or reuse.owner,
|
|
15
|
+
dataset=dataset,
|
|
16
|
+
)
|
|
17
|
+
),
|
|
18
|
+
LabelledContent(_("Reuse title:"), str(reuse.title)),
|
|
19
|
+
MailCTA(_("View the reuse"), reuse.url_for()),
|
|
20
|
+
],
|
|
21
|
+
)
|
udata/core/reuse/models.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from blinker import Signal
|
|
2
2
|
from flask import url_for
|
|
3
|
+
from flask_babel import LazyString
|
|
3
4
|
from mongoengine.signals import post_save, pre_save
|
|
4
5
|
from werkzeug.utils import cached_property
|
|
5
6
|
|
|
@@ -22,7 +23,7 @@ from .constants import IMAGE_MAX_SIZE, IMAGE_SIZES, REUSE_TOPICS, REUSE_TYPES
|
|
|
22
23
|
|
|
23
24
|
__all__ = ("Reuse",)
|
|
24
25
|
|
|
25
|
-
BADGES: dict[str,
|
|
26
|
+
BADGES: dict[str, LazyString] = {}
|
|
26
27
|
|
|
27
28
|
|
|
28
29
|
class ReuseQuerySet(OwnedQuerySet):
|
|
@@ -115,6 +116,14 @@ class Reuse(db.Datetimed, Auditable, WithMetrics, ReuseBadgeMixin, Linkable, Own
|
|
|
115
116
|
"key": "dataset",
|
|
116
117
|
},
|
|
117
118
|
)
|
|
119
|
+
dataservices = field(
|
|
120
|
+
db.ListField(
|
|
121
|
+
field(db.ReferenceField("Dataservice", reverse_delete_rule=db.PULL)),
|
|
122
|
+
),
|
|
123
|
+
filterable={
|
|
124
|
+
"key": "dataservice",
|
|
125
|
+
},
|
|
126
|
+
)
|
|
118
127
|
tags = field(
|
|
119
128
|
db.TagListField(),
|
|
120
129
|
filterable={
|
udata/core/reuse/search.py
CHANGED
|
@@ -40,7 +40,7 @@ class ReuseSearch(ModelSearchAdapter):
|
|
|
40
40
|
|
|
41
41
|
@classmethod
|
|
42
42
|
def is_indexable(cls, reuse: Reuse) -> bool:
|
|
43
|
-
return reuse.
|
|
43
|
+
return reuse.is_visible
|
|
44
44
|
|
|
45
45
|
@classmethod
|
|
46
46
|
def mongo_search(cls, args):
|
udata/core/reuse/tasks.py
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
from udata import mail
|
|
2
1
|
from udata.core import storages
|
|
3
2
|
from udata.core.topic.models import TopicElement
|
|
4
|
-
from udata.i18n import lazy_gettext as _
|
|
5
3
|
from udata.models import Activity, Discussion, Follow, Transfer
|
|
6
4
|
from udata.tasks import get_logger, job, task
|
|
7
5
|
|
|
6
|
+
from . import mails
|
|
8
7
|
from .models import Reuse
|
|
9
8
|
|
|
10
9
|
log = get_logger(__name__)
|
|
@@ -45,4 +44,4 @@ def notify_new_reuse(reuse_id: int) -> None:
|
|
|
45
44
|
else:
|
|
46
45
|
recipients = None
|
|
47
46
|
if recipients:
|
|
48
|
-
|
|
47
|
+
mails.new_reuse(reuse, dataset).send(recipients)
|
udata/core/site/api.py
CHANGED
|
@@ -6,7 +6,7 @@ from udata.auth import admin_permission
|
|
|
6
6
|
from udata.core import csv
|
|
7
7
|
from udata.core.dataservices.csv import DataserviceCsvAdapter
|
|
8
8
|
from udata.core.dataservices.models import Dataservice
|
|
9
|
-
from udata.core.dataset.api import DatasetApiParser
|
|
9
|
+
from udata.core.dataset.api import DatasetApiParser, catalog_parser
|
|
10
10
|
from udata.core.dataset.csv import ResourcesCsvAdapter
|
|
11
11
|
from udata.core.dataset.search import DatasetSearch
|
|
12
12
|
from udata.core.dataset.tasks import get_queryset as get_csv_queryset
|
|
@@ -47,39 +47,47 @@ class SiteAPI(API):
|
|
|
47
47
|
return current_site
|
|
48
48
|
|
|
49
49
|
|
|
50
|
-
@api.route("/site/data.<
|
|
50
|
+
@api.route("/site/data.<_format>", endpoint="site_dataportal")
|
|
51
51
|
class SiteDataPortal(API):
|
|
52
|
-
def get(self,
|
|
52
|
+
def get(self, _format):
|
|
53
53
|
"""Root RDF endpoint with content negociation handling"""
|
|
54
|
-
url = url_for("api.site_rdf_catalog_format",
|
|
54
|
+
url = url_for("api.site_rdf_catalog_format", _format=_format)
|
|
55
55
|
return redirect(url)
|
|
56
56
|
|
|
57
57
|
|
|
58
58
|
@api.route("/site/catalog", endpoint="site_rdf_catalog")
|
|
59
59
|
class SiteRdfCatalog(API):
|
|
60
|
+
@api.expect(catalog_parser)
|
|
60
61
|
def get(self):
|
|
61
62
|
"""Root RDF endpoint with content negociation handling"""
|
|
62
|
-
|
|
63
|
-
|
|
63
|
+
_format = RDF_EXTENSIONS[negociate_content()]
|
|
64
|
+
# We sanitize the args used as kwargs in url_for
|
|
65
|
+
params = catalog_parser.parse_args()
|
|
66
|
+
url = url_for("api.site_rdf_catalog_format", _format=_format, **params)
|
|
64
67
|
return redirect(url)
|
|
65
68
|
|
|
66
69
|
|
|
67
|
-
@api.route("/site/catalog.<
|
|
70
|
+
@api.route("/site/catalog.<_format>", endpoint="site_rdf_catalog_format")
|
|
68
71
|
class SiteRdfCatalogFormat(API):
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
datasets
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
datasets =
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
72
|
+
@api.expect(catalog_parser)
|
|
73
|
+
def get(self, _format):
|
|
74
|
+
"""
|
|
75
|
+
Return the RDF catalog in the requested format.
|
|
76
|
+
Filtering, sorting and paginating abilities apply to the datasets elements.
|
|
77
|
+
"""
|
|
78
|
+
params = catalog_parser.parse_args()
|
|
79
|
+
datasets = DatasetApiParser.parse_filters(Dataset.objects.visible(), params)
|
|
80
|
+
datasets = datasets.paginate(params["page"], params["page_size"])
|
|
81
|
+
dataservices = Dataservice.objects.visible().filter_by_dataset_pagination(
|
|
82
|
+
datasets, params["page"]
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
catalog = build_catalog(
|
|
86
|
+
current_site, datasets, dataservices=dataservices, _format=_format, **params
|
|
87
|
+
)
|
|
80
88
|
# bypass flask-restplus make_response, since graph_response
|
|
81
89
|
# is handling the content negociation directly
|
|
82
|
-
return make_response(*graph_response(catalog,
|
|
90
|
+
return make_response(*graph_response(catalog, _format))
|
|
83
91
|
|
|
84
92
|
|
|
85
93
|
@api.route("/site/datasets.csv", endpoint="site_datasets_csv")
|
udata/core/site/models.py
CHANGED
|
@@ -8,6 +8,7 @@ from udata.core.metrics.helpers import get_metrics_for_model, get_stock_metrics
|
|
|
8
8
|
from udata.core.organization.models import Organization
|
|
9
9
|
from udata.core.reuse.models import Reuse
|
|
10
10
|
from udata.models import WithMetrics, db
|
|
11
|
+
from udata.utils import get_udata_version
|
|
11
12
|
|
|
12
13
|
__all__ = ("Site", "SiteSettings")
|
|
13
14
|
|
|
@@ -66,12 +67,7 @@ class Site(WithMetrics, db.Document):
|
|
|
66
67
|
|
|
67
68
|
@field(description="The current version of udata")
|
|
68
69
|
def version(self):
|
|
69
|
-
|
|
70
|
-
from importlib.metadata import version
|
|
71
|
-
|
|
72
|
-
return version("udata")
|
|
73
|
-
except Exception:
|
|
74
|
-
return None
|
|
70
|
+
return get_udata_version()
|
|
75
71
|
|
|
76
72
|
def count_users(self):
|
|
77
73
|
from udata.models import User
|
udata/core/site/rdf.py
CHANGED
|
@@ -15,7 +15,7 @@ from udata.uris import homepage_url
|
|
|
15
15
|
from udata.utils import Paginable
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
def build_catalog(site, datasets, dataservices=[],
|
|
18
|
+
def build_catalog(site, datasets, dataservices=[], _format=None, **kwargs):
|
|
19
19
|
"""Build the DCAT catalog for this site"""
|
|
20
20
|
catalog_url = url_for("api.site_rdf_catalog", _external=True)
|
|
21
21
|
graph = Graph(namespace_manager=namespace_manager)
|
|
@@ -45,6 +45,6 @@ def build_catalog(site, datasets, dataservices=[], format=None):
|
|
|
45
45
|
catalog.add(DCAT.service, rdf_dataservice)
|
|
46
46
|
|
|
47
47
|
if isinstance(datasets, Paginable):
|
|
48
|
-
paginate_catalog(catalog, graph, datasets,
|
|
48
|
+
paginate_catalog(catalog, graph, datasets, _format, "api.site_rdf_catalog_format", **kwargs)
|
|
49
49
|
|
|
50
50
|
return catalog
|