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/entrypoints.py
CHANGED
|
@@ -2,16 +2,16 @@ import pkg_resources
|
|
|
2
2
|
|
|
3
3
|
# Here for documentation purpose
|
|
4
4
|
ENTRYPOINTS = {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
5
|
+
"udata.avatars": "Avatar rendering backends",
|
|
6
|
+
"udata.harvesters": "Harvest backends",
|
|
7
|
+
"udata.linkcheckers": "Link checker backends",
|
|
8
|
+
"udata.metrics": "Extra metrics",
|
|
9
|
+
"udata.models": "Models and migrations",
|
|
10
|
+
"udata.preview": "Displays preview for resources",
|
|
11
|
+
"udata.plugins": "Generic plugin",
|
|
12
|
+
"udata.tasks": "Tasks and jobs",
|
|
13
|
+
"udata.themes": "Themes",
|
|
14
|
+
"udata.views": "Extra views",
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
|
|
@@ -20,73 +20,77 @@ class EntrypointError(Exception):
|
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
def iter_all(name):
|
|
23
|
-
|
|
23
|
+
"""Iter all entrypoints registered on a given key"""
|
|
24
24
|
return pkg_resources.iter_entry_points(name)
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
def get_all(entrypoint_key):
|
|
28
|
-
|
|
28
|
+
"""Load all entrypoints registered on a given key"""
|
|
29
29
|
return dict(_ep_to_kv(e) for e in iter_all(entrypoint_key))
|
|
30
30
|
|
|
31
31
|
|
|
32
32
|
def get_enabled(name, app):
|
|
33
|
-
|
|
33
|
+
"""
|
|
34
34
|
Get (and load) entrypoints registered on name
|
|
35
35
|
and enabled for the given app.
|
|
36
|
-
|
|
37
|
-
plugins = app.config[
|
|
38
|
-
return dict(
|
|
36
|
+
"""
|
|
37
|
+
plugins = app.config["PLUGINS"]
|
|
38
|
+
return dict(
|
|
39
|
+
_ep_to_kv(e)
|
|
40
|
+
for e in iter_all(name)
|
|
41
|
+
if e.name in plugins or e.name.startswith(tuple(plugins))
|
|
42
|
+
)
|
|
39
43
|
|
|
40
44
|
|
|
41
45
|
def get_plugin_module(name, app, plugin):
|
|
42
|
-
|
|
46
|
+
"""
|
|
43
47
|
Get the module for a given plugin
|
|
44
|
-
|
|
48
|
+
"""
|
|
45
49
|
return next((m for p, m in get_enabled(name, app).items() if p == plugin), None)
|
|
46
50
|
|
|
47
51
|
|
|
48
52
|
def _ep_to_kv(entrypoint):
|
|
49
|
-
|
|
53
|
+
"""
|
|
50
54
|
Transform an entrypoint into a key-value tuple where:
|
|
51
55
|
- key is the entrypoint name
|
|
52
56
|
- value is the entrypoint class with the name attribute
|
|
53
57
|
matching from entrypoint name
|
|
54
|
-
|
|
58
|
+
"""
|
|
55
59
|
cls = entrypoint.load()
|
|
56
60
|
cls.name = entrypoint.name
|
|
57
61
|
return (entrypoint.name, cls)
|
|
58
62
|
|
|
59
63
|
|
|
60
64
|
def known_dists():
|
|
61
|
-
|
|
65
|
+
"""Return a list of all Distributions exporting udata.* entrypoints"""
|
|
62
66
|
return (
|
|
63
|
-
dist
|
|
67
|
+
dist
|
|
68
|
+
for dist in pkg_resources.working_set
|
|
64
69
|
if any(k in ENTRYPOINTS for k in dist.get_entry_map().keys())
|
|
65
70
|
)
|
|
66
71
|
|
|
67
72
|
|
|
68
73
|
def get_plugins_dists(app, name=None):
|
|
69
|
-
|
|
74
|
+
"""Return a list of Distributions with enabled udata plugins"""
|
|
70
75
|
if name:
|
|
71
|
-
plugins = set(e.name for e in iter_all(name) if e.name in app.config[
|
|
76
|
+
plugins = set(e.name for e in iter_all(name) if e.name in app.config["PLUGINS"])
|
|
72
77
|
else:
|
|
73
|
-
plugins = set(app.config[
|
|
78
|
+
plugins = set(app.config["PLUGINS"])
|
|
74
79
|
return [
|
|
75
|
-
d for d in known_dists()
|
|
76
|
-
if any(set(v.keys()) & plugins for v in d.get_entry_map().values())
|
|
80
|
+
d for d in known_dists() if any(set(v.keys()) & plugins for v in d.get_entry_map().values())
|
|
77
81
|
]
|
|
78
82
|
|
|
79
83
|
|
|
80
84
|
def get_roots(app=None):
|
|
81
|
-
|
|
85
|
+
"""
|
|
82
86
|
Returns the list of root packages/modules exposing endpoints.
|
|
83
87
|
|
|
84
88
|
If app is provided, only returns those of enabled plugins
|
|
85
|
-
|
|
89
|
+
"""
|
|
86
90
|
roots = set()
|
|
87
|
-
plugins = app.config[
|
|
91
|
+
plugins = app.config["PLUGINS"] if app else None
|
|
88
92
|
for name in ENTRYPOINTS.keys():
|
|
89
93
|
for ep in iter_all(name):
|
|
90
94
|
if plugins is None or ep.name in plugins:
|
|
91
|
-
roots.add(ep.module_name.split(
|
|
95
|
+
roots.add(ep.module_name.split(".", 1)[0])
|
|
92
96
|
return list(roots)
|
udata/errors.py
CHANGED
udata/event/values.py
CHANGED
|
@@ -2,9 +2,9 @@ from enum import Enum
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
class EventMessageType(Enum):
|
|
5
|
-
INDEX =
|
|
6
|
-
REINDEX =
|
|
7
|
-
UNINDEX =
|
|
8
|
-
CREATED =
|
|
9
|
-
MODIFIED =
|
|
10
|
-
DELETED =
|
|
5
|
+
INDEX = "index"
|
|
6
|
+
REINDEX = "reindex"
|
|
7
|
+
UNINDEX = "unindex"
|
|
8
|
+
CREATED = "created"
|
|
9
|
+
MODIFIED = "modified"
|
|
10
|
+
DELETED = "deleted"
|
udata/factories.py
CHANGED
|
@@ -16,5 +16,5 @@ class DateRangeFactory(ModelFactory):
|
|
|
16
16
|
class Meta:
|
|
17
17
|
model = db.DateRange
|
|
18
18
|
|
|
19
|
-
start = factory.Faker(
|
|
20
|
-
end = factory.Faker(
|
|
19
|
+
start = factory.Faker("date_between", start_date="-10y", end_date="-5y")
|
|
20
|
+
end = factory.Faker("date_between", start_date="-5y", end_date="-2y")
|
udata/features/identicon/api.py
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
|
-
from udata.api import
|
|
2
|
-
|
|
1
|
+
from udata.api import API, api
|
|
3
2
|
|
|
4
3
|
from . import backends
|
|
5
4
|
|
|
6
|
-
ns = api.namespace(
|
|
5
|
+
ns = api.namespace("avatars", "Avatars")
|
|
7
6
|
|
|
8
7
|
|
|
9
|
-
@ns.route(
|
|
8
|
+
@ns.route("/<identifier>/<int:size>", endpoint="avatar")
|
|
10
9
|
class IdenticonAPI(API):
|
|
11
|
-
@api.doc(
|
|
10
|
+
@api.doc("avatars")
|
|
12
11
|
def get(self, identifier, size):
|
|
13
|
-
|
|
12
|
+
"""Get a deterministic avatar given an identifier at a given size"""
|
|
14
13
|
return backends.get_identicon(identifier, size)
|
|
@@ -2,137 +2,130 @@ import hashlib
|
|
|
2
2
|
import io
|
|
3
3
|
|
|
4
4
|
import pydenticon
|
|
5
|
-
|
|
6
|
-
from flask import redirect, send_file, current_app
|
|
5
|
+
from flask import current_app, redirect, send_file
|
|
7
6
|
|
|
8
7
|
from udata import entrypoints
|
|
9
8
|
from udata.app import cache
|
|
10
9
|
|
|
11
|
-
ADORABLE_AVATARS_URL =
|
|
12
|
-
ROBOHASH_URL =
|
|
10
|
+
ADORABLE_AVATARS_URL = "https://api.adorable.io/avatars/{size}/{identifier}.png" # noqa
|
|
11
|
+
ROBOHASH_URL = "https://robohash.org/{identifier}.png?size={size}x{size}&set={skin}&bgset={bg}" # noqa
|
|
13
12
|
|
|
14
13
|
|
|
15
14
|
# Default values overriden by theme default and local config
|
|
16
15
|
DEFAULTS = {
|
|
17
|
-
|
|
16
|
+
"AVATAR_PROVIDER": "internal",
|
|
18
17
|
# Internal provider
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
18
|
+
"AVATAR_INTERNAL_SIZE": 7,
|
|
19
|
+
"AVATAR_INTERNAL_FOREGROUND": [
|
|
20
|
+
"rgb(45,79,255)",
|
|
21
|
+
"rgb(254,180,44)",
|
|
22
|
+
"rgb(226,121,234)",
|
|
23
|
+
"rgb(30,179,253)",
|
|
24
|
+
"rgb(232,77,65)",
|
|
25
|
+
"rgb(49,203,115)",
|
|
26
|
+
"rgb(141,69,170)",
|
|
28
27
|
],
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
"AVATAR_INTERNAL_BACKGROUND": "rgb(224,224,224)",
|
|
29
|
+
"AVATAR_INTERNAL_PADDING": 10,
|
|
32
30
|
# robohash prodiver
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
"AVATAR_ROBOHASH_SKIN": "set1",
|
|
32
|
+
"AVATAR_ROBOHASH_BACKGROUND": "bg1",
|
|
35
33
|
}
|
|
36
34
|
|
|
37
35
|
|
|
38
36
|
def get_config(key):
|
|
39
|
-
|
|
37
|
+
"""
|
|
40
38
|
Get an identicon configuration parameter.
|
|
41
39
|
|
|
42
40
|
Precedance order is:
|
|
43
41
|
- application config (`udata.cfg`)
|
|
44
42
|
- default
|
|
45
|
-
|
|
46
|
-
key =
|
|
43
|
+
"""
|
|
44
|
+
key = "AVATAR_{0}".format(key.upper())
|
|
47
45
|
local_config = current_app.config.get(key)
|
|
48
46
|
return local_config or DEFAULTS[key]
|
|
49
47
|
|
|
50
48
|
|
|
51
49
|
def get_internal_config(key):
|
|
52
|
-
return get_config(
|
|
50
|
+
return get_config("internal_{0}".format(key))
|
|
53
51
|
|
|
54
52
|
|
|
55
53
|
def get_provider():
|
|
56
|
-
|
|
57
|
-
name = get_config(
|
|
58
|
-
available = entrypoints.get_all(
|
|
54
|
+
"""Get the current provider from config"""
|
|
55
|
+
name = get_config("provider")
|
|
56
|
+
available = entrypoints.get_all("udata.avatars")
|
|
59
57
|
if name not in available:
|
|
60
|
-
raise ValueError(
|
|
58
|
+
raise ValueError("Unknown avatar provider: {0}".format(name))
|
|
61
59
|
return available[name]
|
|
62
60
|
|
|
63
61
|
|
|
64
62
|
def get_identicon(identifier, size):
|
|
65
|
-
|
|
63
|
+
"""
|
|
66
64
|
Get an identicon for a given identifier at a given size.
|
|
67
65
|
|
|
68
66
|
Automatically select the provider from `AVATAR_PROVIDER`
|
|
69
67
|
|
|
70
68
|
:returns: a HTTP response, either an image or a redirect
|
|
71
|
-
|
|
69
|
+
"""
|
|
72
70
|
return get_provider()(identifier, size)
|
|
73
71
|
|
|
74
72
|
|
|
75
73
|
@cache.memoize()
|
|
76
74
|
def generate_pydenticon(identifier, size):
|
|
77
|
-
|
|
75
|
+
"""
|
|
78
76
|
Use pydenticon to generate an identicon image.
|
|
79
77
|
All parameters are extracted from configuration.
|
|
80
|
-
|
|
81
|
-
blocks_size = get_internal_config(
|
|
82
|
-
foreground = get_internal_config(
|
|
83
|
-
background = get_internal_config(
|
|
84
|
-
generator = pydenticon.Generator(
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
background=background)
|
|
78
|
+
"""
|
|
79
|
+
blocks_size = get_internal_config("size")
|
|
80
|
+
foreground = get_internal_config("foreground")
|
|
81
|
+
background = get_internal_config("background")
|
|
82
|
+
generator = pydenticon.Generator(
|
|
83
|
+
blocks_size, blocks_size, digest=hashlib.sha1, foreground=foreground, background=background
|
|
84
|
+
)
|
|
88
85
|
|
|
89
86
|
# Pydenticon adds padding to the size and as a consequence
|
|
90
87
|
# we need to compute the size without the padding
|
|
91
|
-
padding = int(round(get_internal_config(
|
|
88
|
+
padding = int(round(get_internal_config("padding") * size / 100.0))
|
|
92
89
|
size = size - 2 * padding
|
|
93
|
-
padding = (padding,
|
|
94
|
-
return generator.generate(identifier, size, size,
|
|
95
|
-
padding=padding,
|
|
96
|
-
output_format='png')
|
|
90
|
+
padding = (padding,) * 4
|
|
91
|
+
return generator.generate(identifier, size, size, padding=padding, output_format="png")
|
|
97
92
|
|
|
98
93
|
|
|
99
94
|
def internal(identifier, size):
|
|
100
|
-
|
|
95
|
+
"""
|
|
101
96
|
Internal provider
|
|
102
97
|
|
|
103
98
|
Use pydenticon to generate an identicon.
|
|
104
|
-
|
|
99
|
+
"""
|
|
105
100
|
identicon = generate_pydenticon(identifier, size)
|
|
106
|
-
response = send_file(io.BytesIO(identicon), mimetype=
|
|
101
|
+
response = send_file(io.BytesIO(identicon), mimetype="image/png")
|
|
107
102
|
etag = hashlib.sha1(identicon).hexdigest()
|
|
108
103
|
response.set_etag(etag)
|
|
109
104
|
return response
|
|
110
105
|
|
|
111
106
|
|
|
112
107
|
def adorable(identifier, size):
|
|
113
|
-
|
|
108
|
+
"""
|
|
114
109
|
Adorable Avatars provider
|
|
115
110
|
|
|
116
111
|
Simply redirect to the external API.
|
|
117
112
|
|
|
118
113
|
See: http://avatars.adorable.io/
|
|
119
|
-
|
|
114
|
+
"""
|
|
120
115
|
url = ADORABLE_AVATARS_URL.format(identifier=identifier, size=size)
|
|
121
116
|
return redirect(url)
|
|
122
117
|
|
|
123
118
|
|
|
124
119
|
def robohash(identifier, size):
|
|
125
|
-
|
|
120
|
+
"""
|
|
126
121
|
Robohash provider
|
|
127
122
|
|
|
128
123
|
Redirect to the Robohash API
|
|
129
124
|
with parameters extracted from configuration.
|
|
130
125
|
|
|
131
126
|
See: https://robohash.org/
|
|
132
|
-
|
|
133
|
-
skin = get_config(
|
|
134
|
-
background = get_config(
|
|
135
|
-
url = ROBOHASH_URL.format(
|
|
136
|
-
identifier=identifier, size=size, skin=skin, bg=background
|
|
137
|
-
)
|
|
127
|
+
"""
|
|
128
|
+
skin = get_config("robohash_skin")
|
|
129
|
+
background = get_config("robohash_background")
|
|
130
|
+
url = ROBOHASH_URL.format(identifier=identifier, size=size, skin=skin, bg=background)
|
|
138
131
|
return redirect(url)
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import pytest
|
|
2
2
|
|
|
3
3
|
from udata.features.identicon.backends import internal
|
|
4
|
-
from udata.utils import faker
|
|
5
4
|
from udata.tests.helpers import assert200
|
|
5
|
+
from udata.utils import faker
|
|
6
6
|
|
|
7
|
-
pytestmark = pytest.mark.usefixtures(
|
|
7
|
+
pytestmark = pytest.mark.usefixtures("app")
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
def assert_stream_equal(response1, response2):
|
|
@@ -18,12 +18,11 @@ class InternalBackendTest:
|
|
|
18
18
|
def test_base_rendering(self):
|
|
19
19
|
response = internal(faker.word(), 32)
|
|
20
20
|
assert200(response)
|
|
21
|
-
assert response.mimetype ==
|
|
21
|
+
assert response.mimetype == "image/png"
|
|
22
22
|
assert response.is_streamed
|
|
23
23
|
etag, weak = response.get_etag()
|
|
24
24
|
assert etag is not None
|
|
25
25
|
|
|
26
26
|
def test_render_twice_the_same(self):
|
|
27
27
|
identifier = faker.word()
|
|
28
|
-
assert_stream_equal(internal(identifier, 32),
|
|
29
|
-
internal(identifier, 32))
|
|
28
|
+
assert_stream_equal(internal(identifier, 32), internal(identifier, 32))
|
|
@@ -6,32 +6,32 @@ _providers = {}
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
def register_provider(name, func):
|
|
9
|
-
|
|
9
|
+
"""Register a notification provder"""
|
|
10
10
|
_providers[name] = func
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
def list_providers():
|
|
14
|
-
|
|
14
|
+
"""List registere notifcation provider"""
|
|
15
15
|
return _providers.keys()
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
def notifier(name):
|
|
19
|
-
|
|
19
|
+
"""A decorator registering a function as provider"""
|
|
20
|
+
|
|
20
21
|
def wrapper(func):
|
|
21
22
|
register_provider(name, func)
|
|
22
23
|
return func
|
|
24
|
+
|
|
23
25
|
return wrapper
|
|
24
26
|
|
|
25
27
|
|
|
26
28
|
def get_notifications(user):
|
|
27
|
-
|
|
29
|
+
"""List notification for a given user"""
|
|
28
30
|
notifications = []
|
|
29
31
|
|
|
30
32
|
for name, func in _providers.items():
|
|
31
|
-
notifications.extend(
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
'details': details
|
|
35
|
-
} for dt, details in func(user)])
|
|
33
|
+
notifications.extend(
|
|
34
|
+
[{"type": name, "created_on": dt, "details": details} for dt, details in func(user)]
|
|
35
|
+
)
|
|
36
36
|
|
|
37
37
|
return notifications
|
|
@@ -1,26 +1,30 @@
|
|
|
1
|
-
from udata.api import api, fields
|
|
1
|
+
from udata.api import API, api, fields
|
|
2
2
|
from udata.auth import current_user
|
|
3
3
|
|
|
4
4
|
from .actions import get_notifications
|
|
5
5
|
|
|
6
|
-
notifs = api.namespace(
|
|
6
|
+
notifs = api.namespace("notifications", "Notifications API")
|
|
7
7
|
|
|
8
|
-
notifications_fields = api.model(
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
description=
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
8
|
+
notifications_fields = api.model(
|
|
9
|
+
"Notification",
|
|
10
|
+
{
|
|
11
|
+
"type": fields.String(description="The notification type", readonly=True),
|
|
12
|
+
"created_on": fields.ISODateTime(
|
|
13
|
+
description="The notification creation datetime", readonly=True
|
|
14
|
+
),
|
|
15
|
+
"details": fields.Raw(
|
|
16
|
+
description="Key-Value details depending on notification type", readonly=True
|
|
17
|
+
),
|
|
18
|
+
},
|
|
19
|
+
)
|
|
16
20
|
|
|
17
21
|
|
|
18
|
-
@notifs.route(
|
|
22
|
+
@notifs.route("/", endpoint="notifications")
|
|
19
23
|
class NotificationsAPI(API):
|
|
20
24
|
@api.secure
|
|
21
|
-
@api.doc(
|
|
25
|
+
@api.doc("get_notifications")
|
|
22
26
|
@api.marshal_list_with(notifications_fields)
|
|
23
27
|
def get(self):
|
|
24
|
-
|
|
28
|
+
"""List all current user pending notifications"""
|
|
25
29
|
user = current_user._get_current_object()
|
|
26
30
|
return get_notifications(user)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from flask import current_app
|
|
2
2
|
|
|
3
|
-
from udata.models import
|
|
3
|
+
from udata.models import GeoZone, db
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
def check_for_territories(query):
|
|
@@ -9,26 +9,28 @@ def check_for_territories(query):
|
|
|
9
9
|
|
|
10
10
|
Results are sorted by population and area (biggest first).
|
|
11
11
|
"""
|
|
12
|
-
if not query or not current_app.config.get(
|
|
12
|
+
if not query or not current_app.config.get("ACTIVATE_TERRITORIES"):
|
|
13
13
|
return []
|
|
14
14
|
|
|
15
15
|
dbqs = db.Q()
|
|
16
16
|
query = query.lower()
|
|
17
17
|
is_digit = query.isdigit()
|
|
18
18
|
query_length = len(query)
|
|
19
|
-
for level in current_app.config.get(
|
|
20
|
-
if level ==
|
|
19
|
+
for level in current_app.config.get("HANDLED_LEVELS"):
|
|
20
|
+
if level == "country":
|
|
21
21
|
continue # Level not fully handled yet.
|
|
22
22
|
q = db.Q(level=level)
|
|
23
|
-
if
|
|
24
|
-
(is_digit or query in ('2a', '2b'))):
|
|
23
|
+
if query_length == 2 and level == "fr:departement" and (is_digit or query in ("2a", "2b")):
|
|
25
24
|
# Counties + Corsica.
|
|
26
25
|
q &= db.Q(code=query)
|
|
27
|
-
elif query_length == 3 and level ==
|
|
26
|
+
elif query_length == 3 and level == "fr:departement" and is_digit:
|
|
28
27
|
# French DROM-COM.
|
|
29
28
|
q &= db.Q(code=query)
|
|
30
|
-
elif
|
|
31
|
-
|
|
29
|
+
elif (
|
|
30
|
+
query_length == 5
|
|
31
|
+
and level == "fr:commune"
|
|
32
|
+
and (is_digit or query.startswith("2a") or query.startswith("2b"))
|
|
33
|
+
):
|
|
32
34
|
# INSEE code then postal codes with Corsica exceptions.
|
|
33
35
|
q &= db.Q(code=query)
|
|
34
36
|
elif query_length >= 4:
|
|
@@ -44,4 +46,4 @@ def check_for_territories(query):
|
|
|
44
46
|
return []
|
|
45
47
|
|
|
46
48
|
# Sort matching results by population and area.
|
|
47
|
-
return GeoZone.objects(dbqs).order_by(
|
|
49
|
+
return GeoZone.objects(dbqs).order_by("-population", "-area")
|
|
@@ -1,26 +1,25 @@
|
|
|
1
|
-
from udata.api import
|
|
1
|
+
from udata.api import API, api
|
|
2
2
|
from udata.features.territories import check_for_territories
|
|
3
3
|
|
|
4
4
|
suggest_parser = api.parser()
|
|
5
5
|
suggest_parser.add_argument(
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
"q", type=str, help="The string to autocomplete/suggest", location="args", required=True
|
|
7
|
+
)
|
|
8
8
|
suggest_parser.add_argument(
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
"size", type=int, help="The maximum result size", location="args", required=False
|
|
10
|
+
)
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
@api.route(
|
|
13
|
+
@api.route("/territory/suggest/", endpoint="suggest_territory")
|
|
14
14
|
class SuggestTerritoriesAPI(API):
|
|
15
|
-
@api.doc(
|
|
15
|
+
@api.doc("suggest_territory")
|
|
16
16
|
@api.expect(suggest_parser)
|
|
17
17
|
def get(self):
|
|
18
18
|
args = suggest_parser.parse_args()
|
|
19
|
-
territories = check_for_territories(args[
|
|
20
|
-
if args[
|
|
21
|
-
territories = territories[:args[
|
|
22
|
-
return [
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
} for territory in territories]
|
|
19
|
+
territories = check_for_territories(args["q"])
|
|
20
|
+
if args["size"]:
|
|
21
|
+
territories = territories[: args["size"]]
|
|
22
|
+
return [
|
|
23
|
+
{"id": territory.id, "title": territory.name, "page": territory.external_url}
|
|
24
|
+
for territory in territories
|
|
25
|
+
]
|