udata 9.1.2.dev30355__py2.py3-none-any.whl → 9.1.2.dev30382__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 +135 -124
- 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 +56 -54
- 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/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/static/chunks/{11.e9b9ca1f3e03d4020377.js → 11.52e531c19f8de80c00cf.js} +3 -3
- udata/static/chunks/{11.e9b9ca1f3e03d4020377.js.map → 11.52e531c19f8de80c00cf.js.map} +1 -1
- udata/static/chunks/{13.038c0d9aa0dfa0181c4b.js → 13.c3343a7f1070061c0e10.js} +2 -2
- udata/static/chunks/{13.038c0d9aa0dfa0181c4b.js.map → 13.c3343a7f1070061c0e10.js.map} +1 -1
- udata/static/chunks/{16.0baa2b64a74a2dcde25c.js → 16.8fa42440ad75ca172e6d.js} +2 -2
- udata/static/chunks/{16.0baa2b64a74a2dcde25c.js.map → 16.8fa42440ad75ca172e6d.js.map} +1 -1
- udata/static/chunks/{19.350a9f150b074b4ecefa.js → 19.9c6c8412729cd6d59cfa.js} +3 -3
- udata/static/chunks/{19.350a9f150b074b4ecefa.js.map → 19.9c6c8412729cd6d59cfa.js.map} +1 -1
- udata/static/chunks/{5.6ebbce2b9b3e696d3da5.js → 5.71d15c2e4f21feee2a9a.js} +3 -3
- udata/static/chunks/{5.6ebbce2b9b3e696d3da5.js.map → 5.71d15c2e4f21feee2a9a.js.map} +1 -1
- udata/static/chunks/{6.d8a5f7b017bcbd083641.js → 6.9139dc098b8ea640b890.js} +3 -3
- udata/static/chunks/{6.d8a5f7b017bcbd083641.js.map → 6.9139dc098b8ea640b890.js.map} +1 -1
- udata/static/common.js +1 -1
- udata/static/common.js.map +1 -1
- 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 +5 -6
- udata/tests/api/test_auth_api.py +395 -321
- udata/tests/api/test_base_api.py +31 -33
- 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 +76 -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_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.dev30382.dist-info}/METADATA +3 -2
- udata-9.1.2.dev30382.dist-info/RECORD +704 -0
- udata-9.1.2.dev30355.dist-info/RECORD +0 -704
- {udata-9.1.2.dev30355.dist-info → udata-9.1.2.dev30382.dist-info}/LICENSE +0 -0
- {udata-9.1.2.dev30355.dist-info → udata-9.1.2.dev30382.dist-info}/WHEEL +0 -0
- {udata-9.1.2.dev30355.dist-info → udata-9.1.2.dev30382.dist-info}/entry_points.txt +0 -0
- {udata-9.1.2.dev30355.dist-info → udata-9.1.2.dev30382.dist-info}/top_level.txt +0 -0
udata/core/user/models.py
CHANGED
|
@@ -1,34 +1,33 @@
|
|
|
1
|
+
import json
|
|
1
2
|
from copy import copy
|
|
2
3
|
from datetime import datetime
|
|
3
4
|
from itertools import chain
|
|
4
|
-
import json
|
|
5
5
|
from time import time
|
|
6
6
|
|
|
7
7
|
from authlib.jose import JsonWebSignature
|
|
8
8
|
from blinker import Signal
|
|
9
9
|
from flask import current_app
|
|
10
|
-
from flask_security import
|
|
11
|
-
from mongoengine.signals import
|
|
12
|
-
|
|
10
|
+
from flask_security import MongoEngineUserDatastore, RoleMixin, UserMixin
|
|
11
|
+
from mongoengine.signals import post_save, pre_save
|
|
13
12
|
from werkzeug.utils import cached_property
|
|
14
13
|
|
|
15
14
|
from udata import mail
|
|
16
15
|
from udata.core import storages
|
|
17
|
-
from udata.uris import endpoint_for
|
|
18
|
-
from udata.frontend.markdown import mdstrip
|
|
19
|
-
from udata.i18n import lazy_gettext as _
|
|
20
|
-
from udata.models import db, WithMetrics, Follow
|
|
21
16
|
from udata.core.discussions.models import Discussion
|
|
22
17
|
from udata.core.storages import avatars, default_image_basename
|
|
23
|
-
from .
|
|
18
|
+
from udata.frontend.markdown import mdstrip
|
|
19
|
+
from udata.i18n import lazy_gettext as _
|
|
20
|
+
from udata.models import Follow, WithMetrics, db
|
|
21
|
+
from udata.uris import endpoint_for
|
|
24
22
|
|
|
25
|
-
|
|
23
|
+
from .constants import AVATAR_SIZES
|
|
26
24
|
|
|
25
|
+
__all__ = ("User", "Role", "datastore")
|
|
27
26
|
|
|
28
27
|
|
|
29
28
|
# TODO: use simple text for role
|
|
30
29
|
class Role(db.Document, RoleMixin):
|
|
31
|
-
ADMIN =
|
|
30
|
+
ADMIN = "admin"
|
|
32
31
|
name = db.StringField(max_length=80, unique=True)
|
|
33
32
|
description = db.StringField(max_length=255)
|
|
34
33
|
permissions = db.ListField()
|
|
@@ -42,8 +41,7 @@ class UserSettings(db.EmbeddedDocument):
|
|
|
42
41
|
|
|
43
42
|
|
|
44
43
|
class User(WithMetrics, UserMixin, db.Document):
|
|
45
|
-
slug = db.SlugField(
|
|
46
|
-
max_length=255, required=True, populate_from='fullname')
|
|
44
|
+
slug = db.SlugField(max_length=255, required=True, populate_from="fullname")
|
|
47
45
|
email = db.StringField(max_length=255, required=True, unique=True)
|
|
48
46
|
password = db.StringField()
|
|
49
47
|
active = db.BooleanField()
|
|
@@ -54,8 +52,7 @@ class User(WithMetrics, UserMixin, db.Document):
|
|
|
54
52
|
last_name = db.StringField(max_length=255, required=True)
|
|
55
53
|
|
|
56
54
|
avatar_url = db.URLField()
|
|
57
|
-
avatar = db.ImageField(
|
|
58
|
-
fs=avatars, basename=default_image_basename, thumbnails=AVATAR_SIZES)
|
|
55
|
+
avatar = db.ImageField(fs=avatars, basename=default_image_basename, thumbnails=AVATAR_SIZES)
|
|
59
56
|
website = db.URLField()
|
|
60
57
|
about = db.StringField()
|
|
61
58
|
|
|
@@ -93,16 +90,16 @@ class User(WithMetrics, UserMixin, db.Document):
|
|
|
93
90
|
on_delete = Signal()
|
|
94
91
|
|
|
95
92
|
meta = {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
93
|
+
"indexes": ["$slug", "-created_at", "slug", "apikey"],
|
|
94
|
+
"ordering": ["-created_at"],
|
|
95
|
+
"auto_create_index_on_save": True,
|
|
99
96
|
}
|
|
100
97
|
|
|
101
98
|
__metrics_keys__ = [
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
99
|
+
"datasets",
|
|
100
|
+
"reuses",
|
|
101
|
+
"following",
|
|
102
|
+
"followers",
|
|
106
103
|
]
|
|
107
104
|
|
|
108
105
|
def __str__(self):
|
|
@@ -110,19 +107,20 @@ class User(WithMetrics, UserMixin, db.Document):
|
|
|
110
107
|
|
|
111
108
|
@property
|
|
112
109
|
def fullname(self):
|
|
113
|
-
return
|
|
110
|
+
return " ".join((self.first_name or "", self.last_name or "")).strip()
|
|
114
111
|
|
|
115
112
|
@cached_property
|
|
116
113
|
def organizations(self):
|
|
117
114
|
from udata.core.organization.models import Organization
|
|
115
|
+
|
|
118
116
|
return Organization.objects(members__user=self, deleted__exists=False)
|
|
119
117
|
|
|
120
118
|
@property
|
|
121
119
|
def sysadmin(self):
|
|
122
|
-
return self.has_role(
|
|
120
|
+
return self.has_role("admin")
|
|
123
121
|
|
|
124
122
|
def url_for(self, *args, **kwargs):
|
|
125
|
-
return endpoint_for(
|
|
123
|
+
return endpoint_for("users.show", "api.user", user=self, *args, **kwargs)
|
|
126
124
|
|
|
127
125
|
display_url = property(url_for)
|
|
128
126
|
|
|
@@ -132,23 +130,19 @@ class User(WithMetrics, UserMixin, db.Document):
|
|
|
132
130
|
|
|
133
131
|
@property
|
|
134
132
|
def visible(self):
|
|
135
|
-
count = self.metrics.get(
|
|
133
|
+
count = self.metrics.get("datasets", 0) + self.metrics.get("reuses", 0)
|
|
136
134
|
return count > 0 and self.active
|
|
137
135
|
|
|
138
136
|
@cached_property
|
|
139
137
|
def resources_availability(self):
|
|
140
138
|
"""Return the percentage of availability for resources."""
|
|
141
139
|
# Flatten the list.
|
|
142
|
-
availabilities = list(
|
|
143
|
-
chain(
|
|
144
|
-
*[org.check_availability() for org in self.organizations]
|
|
145
|
-
)
|
|
146
|
-
)
|
|
140
|
+
availabilities = list(chain(*[org.check_availability() for org in self.organizations]))
|
|
147
141
|
# Filter out the unknown
|
|
148
142
|
availabilities = [a for a in availabilities if type(a) is bool]
|
|
149
143
|
if availabilities:
|
|
150
144
|
# Trick will work because it's a sum() of booleans.
|
|
151
|
-
return round(100. * sum(availabilities) / len(availabilities), 2)
|
|
145
|
+
return round(100.0 * sum(availabilities) / len(availabilities), 2)
|
|
152
146
|
# if nothing is unavailable, everything is considered OK
|
|
153
147
|
return 100
|
|
154
148
|
|
|
@@ -156,35 +150,38 @@ class User(WithMetrics, UserMixin, db.Document):
|
|
|
156
150
|
def datasets_org_count(self):
|
|
157
151
|
"""Return the number of datasets of user's organizations."""
|
|
158
152
|
from udata.models import Dataset # Circular imports.
|
|
159
|
-
|
|
160
|
-
|
|
153
|
+
|
|
154
|
+
return sum(
|
|
155
|
+
Dataset.objects(organization=org).visible().count() for org in self.organizations
|
|
156
|
+
)
|
|
161
157
|
|
|
162
158
|
@cached_property
|
|
163
159
|
def followers_org_count(self):
|
|
164
160
|
"""Return the number of followers of user's organizations."""
|
|
165
161
|
from udata.models import Follow # Circular imports.
|
|
166
|
-
|
|
167
|
-
|
|
162
|
+
|
|
163
|
+
return sum(Follow.objects(following=org).count() for org in self.organizations)
|
|
168
164
|
|
|
169
165
|
@property
|
|
170
166
|
def datasets_count(self):
|
|
171
167
|
"""Return the number of datasets of the user."""
|
|
172
|
-
return self.metrics.get(
|
|
168
|
+
return self.metrics.get("datasets", 0)
|
|
173
169
|
|
|
174
170
|
@property
|
|
175
171
|
def followers_count(self):
|
|
176
172
|
"""Return the number of followers of the user."""
|
|
177
|
-
return self.metrics.get(
|
|
173
|
+
return self.metrics.get("followers", 0)
|
|
178
174
|
|
|
179
175
|
def generate_api_key(self):
|
|
180
176
|
payload = {
|
|
181
|
-
|
|
182
|
-
|
|
177
|
+
"user": str(self.id),
|
|
178
|
+
"time": time(),
|
|
183
179
|
}
|
|
184
|
-
s = JsonWebSignature(algorithms=[
|
|
185
|
-
{
|
|
186
|
-
json.dumps(payload, separators=(
|
|
187
|
-
current_app.config[
|
|
180
|
+
s = JsonWebSignature(algorithms=["HS512"]).serialize_compact(
|
|
181
|
+
{"alg": "HS512"},
|
|
182
|
+
json.dumps(payload, separators=(",", ":")),
|
|
183
|
+
current_app.config["SECRET_KEY"],
|
|
184
|
+
)
|
|
188
185
|
self.apikey = s.decode()
|
|
189
186
|
|
|
190
187
|
def clear_api_key(self):
|
|
@@ -202,28 +199,27 @@ class User(WithMetrics, UserMixin, db.Document):
|
|
|
202
199
|
@classmethod
|
|
203
200
|
def post_save(cls, sender, document, **kwargs):
|
|
204
201
|
cls.after_save.send(document)
|
|
205
|
-
if kwargs.get(
|
|
202
|
+
if kwargs.get("created"):
|
|
206
203
|
cls.on_create.send(document)
|
|
207
204
|
else:
|
|
208
205
|
cls.on_update.send(document)
|
|
209
206
|
|
|
210
207
|
@cached_property
|
|
211
208
|
def json_ld(self):
|
|
212
|
-
|
|
213
209
|
result = {
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
210
|
+
"@type": "Person",
|
|
211
|
+
"@context": "http://schema.org",
|
|
212
|
+
"name": self.fullname,
|
|
217
213
|
}
|
|
218
214
|
|
|
219
215
|
if self.about:
|
|
220
|
-
result[
|
|
216
|
+
result["description"] = mdstrip(self.about)
|
|
221
217
|
|
|
222
218
|
if self.avatar_url:
|
|
223
|
-
result[
|
|
219
|
+
result["image"] = self.avatar_url
|
|
224
220
|
|
|
225
221
|
if self.website:
|
|
226
|
-
result[
|
|
222
|
+
result["url"] = self.website
|
|
227
223
|
|
|
228
224
|
return result
|
|
229
225
|
|
|
@@ -231,8 +227,8 @@ class User(WithMetrics, UserMixin, db.Document):
|
|
|
231
227
|
return db.Document.delete(self, *args, **kwargs)
|
|
232
228
|
|
|
233
229
|
def delete(self, *args, **kwargs):
|
|
234
|
-
raise NotImplementedError(
|
|
235
|
-
Use `mark_as_deleted` (or `_delete` if you know what you're doing)
|
|
230
|
+
raise NotImplementedError("""This method should not be using directly.
|
|
231
|
+
Use `mark_as_deleted` (or `_delete` if you know what you're doing)""")
|
|
236
232
|
|
|
237
233
|
def mark_as_deleted(self, notify: bool = True):
|
|
238
234
|
if self.avatar.filename is not None:
|
|
@@ -242,14 +238,13 @@ class User(WithMetrics, UserMixin, db.Document):
|
|
|
242
238
|
for key, value in self.avatar.thumbnails.items():
|
|
243
239
|
storage.delete(value)
|
|
244
240
|
|
|
245
|
-
|
|
246
241
|
copied_user = copy(self)
|
|
247
|
-
self.email =
|
|
248
|
-
self.slug =
|
|
242
|
+
self.email = "{}@deleted".format(self.id)
|
|
243
|
+
self.slug = "deleted"
|
|
249
244
|
self.password = None
|
|
250
245
|
self.active = False
|
|
251
|
-
self.first_name =
|
|
252
|
-
self.last_name =
|
|
246
|
+
self.first_name = "DELETED"
|
|
247
|
+
self.last_name = "DELETED"
|
|
253
248
|
self.avatar = None
|
|
254
249
|
self.avatar_url = None
|
|
255
250
|
self.website = None
|
|
@@ -259,9 +254,9 @@ class User(WithMetrics, UserMixin, db.Document):
|
|
|
259
254
|
self.deleted = datetime.utcnow()
|
|
260
255
|
self.save()
|
|
261
256
|
for organization in self.organizations:
|
|
262
|
-
organization.members = [
|
|
263
|
-
|
|
264
|
-
|
|
257
|
+
organization.members = [
|
|
258
|
+
member for member in organization.members if member.user != self
|
|
259
|
+
]
|
|
265
260
|
organization.save()
|
|
266
261
|
for discussion in Discussion.objects(discussion__posted_by=self):
|
|
267
262
|
# Remove all discussions with current user as only participant
|
|
@@ -271,36 +266,40 @@ class User(WithMetrics, UserMixin, db.Document):
|
|
|
271
266
|
|
|
272
267
|
for message in discussion.discussion:
|
|
273
268
|
if message.posted_by == self:
|
|
274
|
-
message.content =
|
|
269
|
+
message.content = "DELETED"
|
|
275
270
|
discussion.save()
|
|
276
271
|
Follow.objects(follower=self).delete()
|
|
277
272
|
Follow.objects(following=self).delete()
|
|
278
273
|
|
|
279
274
|
from udata.models import ContactPoint
|
|
280
|
-
ContactPoint.objects(owner=self).delete()
|
|
281
275
|
|
|
276
|
+
ContactPoint.objects(owner=self).delete()
|
|
282
277
|
|
|
283
278
|
if notify:
|
|
284
|
-
mail.send(_(
|
|
279
|
+
mail.send(_("Account deletion"), copied_user, "account_deleted")
|
|
285
280
|
|
|
286
281
|
def count_datasets(self):
|
|
287
282
|
from udata.models import Dataset
|
|
288
|
-
|
|
283
|
+
|
|
284
|
+
self.metrics["datasets"] = Dataset.objects(owner=self).visible().count()
|
|
289
285
|
self.save()
|
|
290
286
|
|
|
291
287
|
def count_reuses(self):
|
|
292
288
|
from udata.models import Reuse
|
|
293
|
-
|
|
289
|
+
|
|
290
|
+
self.metrics["reuses"] = Reuse.objects(owner=self).visible().count()
|
|
294
291
|
self.save()
|
|
295
292
|
|
|
296
293
|
def count_followers(self):
|
|
297
294
|
from udata.models import Follow
|
|
298
|
-
|
|
295
|
+
|
|
296
|
+
self.metrics["followers"] = Follow.objects(until=None).followers(self).count()
|
|
299
297
|
self.save()
|
|
300
298
|
|
|
301
299
|
def count_following(self):
|
|
302
300
|
from udata.models import Follow
|
|
303
|
-
|
|
301
|
+
|
|
302
|
+
self.metrics["following"] = Follow.objects.following(self).count()
|
|
304
303
|
self.save()
|
|
305
304
|
|
|
306
305
|
|
udata/core/user/permissions.py
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
from udata.auth import Permission, RoleNeed, UserNeed
|
|
2
2
|
from udata.i18n import lazy_gettext as _
|
|
3
3
|
|
|
4
|
-
|
|
5
4
|
ROLES = {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
"admin": _("System administrator"),
|
|
6
|
+
"editor": _("Site editorialist"),
|
|
7
|
+
"moderator": _("Site moderator"),
|
|
9
8
|
}
|
|
10
9
|
|
|
11
|
-
sysadmin = Permission(RoleNeed(
|
|
10
|
+
sysadmin = Permission(RoleNeed("admin"))
|
|
12
11
|
|
|
13
12
|
|
|
14
13
|
class UserEditPermission(Permission):
|
udata/core/user/rdf.py
CHANGED
|
@@ -1,22 +1,21 @@
|
|
|
1
|
-
|
|
1
|
+
"""
|
|
2
2
|
This module centralize user helpers for RDF/DCAT serialization and parsing
|
|
3
|
-
|
|
3
|
+
"""
|
|
4
4
|
|
|
5
|
-
from rdflib import
|
|
6
|
-
from rdflib.namespace import RDF, RDFS
|
|
5
|
+
from rdflib import BNode, Graph, Literal, URIRef
|
|
6
|
+
from rdflib.namespace import FOAF, RDF, RDFS
|
|
7
7
|
|
|
8
8
|
from udata.rdf import namespace_manager
|
|
9
9
|
from udata.uris import endpoint_for
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
def user_to_rdf(user, graph=None):
|
|
13
|
-
|
|
13
|
+
"""
|
|
14
14
|
Map a Resource domain model to a DCAT/RDF graph
|
|
15
|
-
|
|
15
|
+
"""
|
|
16
16
|
graph = graph or Graph(namespace_manager=namespace_manager)
|
|
17
17
|
if user.id:
|
|
18
|
-
user_url = endpoint_for(
|
|
19
|
-
user=user.id, _external=True)
|
|
18
|
+
user_url = endpoint_for("users.show_redirect", "api.user", user=user.id, _external=True)
|
|
20
19
|
id = URIRef(user_url)
|
|
21
20
|
else:
|
|
22
21
|
id = BNode()
|
udata/core/user/tasks.py
CHANGED
|
@@ -9,7 +9,7 @@ from .models import datastore
|
|
|
9
9
|
log = logging.getLogger(__name__)
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
@task(route=
|
|
12
|
+
@task(route="high.mail")
|
|
13
13
|
def send_test_mail(email):
|
|
14
14
|
user = datastore.find_user(email=email)
|
|
15
|
-
mail.send(_(
|
|
15
|
+
mail.send(_("Test mail"), user, "test")
|
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
import pytest
|
|
2
2
|
|
|
3
|
-
from udata.core.discussions.factories import
|
|
4
|
-
|
|
5
|
-
)
|
|
3
|
+
from udata.core.discussions.factories import DiscussionFactory, MessageDiscussionFactory
|
|
4
|
+
from udata.core.discussions.models import Discussion
|
|
6
5
|
from udata.core.followers.models import Follow
|
|
6
|
+
from udata.core.organization.factories import OrganizationFactory
|
|
7
7
|
from udata.core.user.factories import UserFactory
|
|
8
8
|
from udata.core.user.models import User
|
|
9
|
-
from udata.core.discussions.models import Discussion
|
|
10
|
-
from udata.core.organization.factories import OrganizationFactory
|
|
11
9
|
|
|
12
|
-
pytestmark = pytest.mark.usefixtures(
|
|
10
|
+
pytestmark = pytest.mark.usefixtures("clean_db")
|
|
13
11
|
|
|
14
12
|
|
|
15
13
|
@pytest.mark.frontend
|
|
@@ -20,12 +18,22 @@ class UserModelTest:
|
|
|
20
18
|
user = UserFactory()
|
|
21
19
|
other_user = UserFactory()
|
|
22
20
|
org = OrganizationFactory(editors=[user])
|
|
23
|
-
discussion_only_user = DiscussionFactory(
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
21
|
+
discussion_only_user = DiscussionFactory(
|
|
22
|
+
user=user,
|
|
23
|
+
subject=org,
|
|
24
|
+
discussion=[
|
|
25
|
+
MessageDiscussionFactory(posted_by=user),
|
|
26
|
+
MessageDiscussionFactory(posted_by=user),
|
|
27
|
+
],
|
|
28
|
+
)
|
|
29
|
+
discussion_with_other = DiscussionFactory(
|
|
30
|
+
user=other_user,
|
|
31
|
+
subject=org,
|
|
32
|
+
discussion=[
|
|
33
|
+
MessageDiscussionFactory(posted_by=other_user),
|
|
34
|
+
MessageDiscussionFactory(posted_by=user),
|
|
35
|
+
],
|
|
36
|
+
)
|
|
29
37
|
user_follow_org = Follow.objects.create(follower=user, following=org)
|
|
30
38
|
user_followed = Follow.objects.create(follower=other_user, following=user)
|
|
31
39
|
|
|
@@ -36,12 +44,12 @@ class UserModelTest:
|
|
|
36
44
|
|
|
37
45
|
assert Discussion.objects(id=discussion_only_user.id).first() is None
|
|
38
46
|
discussion_with_other.reload()
|
|
39
|
-
assert discussion_with_other.discussion[1].content ==
|
|
47
|
+
assert discussion_with_other.discussion[1].content == "DELETED"
|
|
40
48
|
|
|
41
49
|
assert Follow.objects(id=user_follow_org.id).first() is None
|
|
42
50
|
assert Follow.objects(id=user_followed.id).first() is None
|
|
43
51
|
|
|
44
|
-
assert user.slug ==
|
|
52
|
+
assert user.slug == "deleted"
|
|
45
53
|
|
|
46
54
|
def test_mark_as_deleted_slug_multiple(self):
|
|
47
55
|
user = UserFactory()
|
|
@@ -50,8 +58,8 @@ class UserModelTest:
|
|
|
50
58
|
user.mark_as_deleted()
|
|
51
59
|
other_user.mark_as_deleted()
|
|
52
60
|
|
|
53
|
-
assert user.slug ==
|
|
54
|
-
assert other_user.slug ==
|
|
61
|
+
assert user.slug == "deleted"
|
|
62
|
+
assert other_user.slug == "deleted-1"
|
|
55
63
|
|
|
56
64
|
def test_delete_safeguard(self):
|
|
57
65
|
user = UserFactory()
|
udata/db/tasks.py
CHANGED
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)
|