udata 9.1.2.dev30355__py2.py3-none-any.whl → 9.1.2.dev30454__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of udata might be problematic. Click here for more details.
- tasks/__init__.py +109 -107
- tasks/helpers.py +18 -18
- udata/__init__.py +4 -4
- udata/admin/views.py +5 -5
- udata/api/__init__.py +111 -134
- udata/api/commands.py +45 -37
- udata/api/errors.py +5 -4
- udata/api/fields.py +23 -21
- udata/api/oauth2.py +55 -74
- udata/api/parsers.py +15 -15
- udata/api/signals.py +1 -1
- udata/api_fields.py +137 -89
- udata/app.py +58 -55
- udata/assets.py +5 -5
- udata/auth/__init__.py +37 -26
- udata/auth/forms.py +23 -15
- udata/auth/helpers.py +1 -1
- udata/auth/mails.py +3 -3
- udata/auth/password_validation.py +19 -15
- udata/auth/views.py +94 -68
- udata/commands/__init__.py +71 -69
- udata/commands/cache.py +7 -7
- udata/commands/db.py +201 -140
- udata/commands/dcat.py +36 -30
- udata/commands/fixtures.py +100 -84
- udata/commands/images.py +21 -20
- udata/commands/info.py +17 -20
- udata/commands/init.py +10 -10
- udata/commands/purge.py +12 -13
- udata/commands/serve.py +41 -29
- udata/commands/static.py +16 -18
- udata/commands/test.py +20 -20
- udata/commands/tests/fixtures.py +26 -24
- udata/commands/worker.py +31 -33
- udata/core/__init__.py +12 -12
- udata/core/activity/__init__.py +0 -1
- udata/core/activity/api.py +59 -49
- udata/core/activity/models.py +28 -26
- udata/core/activity/signals.py +1 -1
- udata/core/activity/tasks.py +16 -10
- udata/core/badges/api.py +6 -6
- udata/core/badges/commands.py +14 -13
- udata/core/badges/fields.py +8 -5
- udata/core/badges/forms.py +7 -4
- udata/core/badges/models.py +16 -31
- udata/core/badges/permissions.py +1 -3
- udata/core/badges/signals.py +2 -2
- udata/core/badges/tasks.py +3 -2
- udata/core/badges/tests/test_commands.py +10 -10
- udata/core/badges/tests/test_model.py +24 -31
- udata/core/contact_point/api.py +19 -18
- udata/core/contact_point/api_fields.py +21 -14
- udata/core/contact_point/factories.py +2 -2
- udata/core/contact_point/forms.py +7 -6
- udata/core/contact_point/models.py +3 -5
- udata/core/dataservices/api.py +26 -21
- udata/core/dataservices/factories.py +13 -11
- udata/core/dataservices/models.py +35 -40
- udata/core/dataservices/permissions.py +4 -4
- udata/core/dataservices/rdf.py +40 -17
- udata/core/dataservices/tasks.py +4 -3
- udata/core/dataset/actions.py +10 -10
- udata/core/dataset/activities.py +21 -23
- udata/core/dataset/api.py +321 -298
- udata/core/dataset/api_fields.py +443 -271
- udata/core/dataset/apiv2.py +305 -229
- udata/core/dataset/commands.py +38 -36
- udata/core/dataset/constants.py +61 -54
- udata/core/dataset/csv.py +70 -74
- udata/core/dataset/events.py +39 -32
- udata/core/dataset/exceptions.py +8 -4
- udata/core/dataset/factories.py +57 -65
- udata/core/dataset/forms.py +87 -63
- udata/core/dataset/models.py +336 -280
- udata/core/dataset/permissions.py +9 -6
- udata/core/dataset/preview.py +15 -17
- udata/core/dataset/rdf.py +156 -122
- udata/core/dataset/search.py +92 -77
- udata/core/dataset/signals.py +1 -1
- udata/core/dataset/tasks.py +63 -54
- udata/core/discussions/actions.py +5 -5
- udata/core/discussions/api.py +124 -120
- udata/core/discussions/factories.py +2 -2
- udata/core/discussions/forms.py +9 -7
- udata/core/discussions/metrics.py +1 -3
- udata/core/discussions/models.py +25 -24
- udata/core/discussions/notifications.py +18 -14
- udata/core/discussions/permissions.py +3 -3
- udata/core/discussions/signals.py +4 -4
- udata/core/discussions/tasks.py +24 -28
- udata/core/followers/api.py +32 -33
- udata/core/followers/models.py +9 -9
- udata/core/followers/signals.py +3 -3
- udata/core/jobs/actions.py +7 -7
- udata/core/jobs/api.py +99 -92
- udata/core/jobs/commands.py +48 -49
- udata/core/jobs/forms.py +11 -11
- udata/core/jobs/models.py +6 -6
- udata/core/metrics/__init__.py +2 -2
- udata/core/metrics/commands.py +34 -30
- udata/core/metrics/models.py +2 -4
- udata/core/metrics/signals.py +1 -1
- udata/core/metrics/tasks.py +3 -3
- udata/core/organization/activities.py +12 -15
- udata/core/organization/api.py +167 -174
- udata/core/organization/api_fields.py +183 -124
- udata/core/organization/apiv2.py +32 -32
- udata/core/organization/commands.py +20 -22
- udata/core/organization/constants.py +11 -11
- udata/core/organization/csv.py +17 -15
- udata/core/organization/factories.py +8 -11
- udata/core/organization/forms.py +32 -26
- udata/core/organization/metrics.py +2 -1
- udata/core/organization/models.py +87 -67
- udata/core/organization/notifications.py +18 -14
- udata/core/organization/permissions.py +10 -11
- udata/core/organization/rdf.py +14 -14
- udata/core/organization/search.py +30 -28
- udata/core/organization/signals.py +7 -7
- udata/core/organization/tasks.py +42 -61
- udata/core/owned.py +38 -27
- udata/core/post/api.py +82 -81
- udata/core/post/constants.py +8 -5
- udata/core/post/factories.py +4 -4
- udata/core/post/forms.py +13 -14
- udata/core/post/models.py +20 -22
- udata/core/post/tests/test_api.py +30 -32
- udata/core/reports/api.py +8 -7
- udata/core/reports/constants.py +1 -3
- udata/core/reports/models.py +10 -10
- udata/core/reuse/activities.py +15 -19
- udata/core/reuse/api.py +123 -126
- udata/core/reuse/api_fields.py +120 -85
- udata/core/reuse/apiv2.py +11 -10
- udata/core/reuse/constants.py +23 -23
- udata/core/reuse/csv.py +18 -18
- udata/core/reuse/factories.py +5 -9
- udata/core/reuse/forms.py +24 -21
- udata/core/reuse/models.py +55 -51
- udata/core/reuse/permissions.py +2 -2
- udata/core/reuse/search.py +49 -46
- udata/core/reuse/signals.py +1 -1
- udata/core/reuse/tasks.py +4 -5
- udata/core/site/api.py +47 -50
- udata/core/site/factories.py +2 -2
- udata/core/site/forms.py +4 -5
- udata/core/site/models.py +94 -63
- udata/core/site/rdf.py +14 -14
- udata/core/spam/api.py +16 -9
- udata/core/spam/constants.py +4 -4
- udata/core/spam/fields.py +13 -7
- udata/core/spam/models.py +27 -20
- udata/core/spam/signals.py +1 -1
- udata/core/spam/tests/test_spam.py +6 -5
- udata/core/spatial/api.py +72 -80
- udata/core/spatial/api_fields.py +73 -58
- udata/core/spatial/commands.py +67 -64
- udata/core/spatial/constants.py +3 -3
- udata/core/spatial/factories.py +37 -54
- udata/core/spatial/forms.py +27 -26
- udata/core/spatial/geoids.py +17 -17
- udata/core/spatial/models.py +43 -47
- udata/core/spatial/tasks.py +2 -1
- udata/core/spatial/tests/test_api.py +115 -130
- udata/core/spatial/tests/test_fields.py +74 -77
- udata/core/spatial/tests/test_geoid.py +22 -22
- udata/core/spatial/tests/test_models.py +5 -7
- udata/core/spatial/translations.py +16 -16
- udata/core/storages/__init__.py +16 -18
- udata/core/storages/api.py +66 -64
- udata/core/storages/tasks.py +7 -7
- udata/core/storages/utils.py +15 -15
- udata/core/storages/views.py +5 -6
- udata/core/tags/api.py +17 -14
- udata/core/tags/csv.py +4 -4
- udata/core/tags/models.py +8 -5
- udata/core/tags/tasks.py +11 -13
- udata/core/tags/views.py +4 -4
- udata/core/topic/api.py +84 -73
- udata/core/topic/apiv2.py +157 -127
- udata/core/topic/factories.py +3 -4
- udata/core/topic/forms.py +12 -14
- udata/core/topic/models.py +14 -19
- udata/core/topic/parsers.py +26 -26
- udata/core/user/activities.py +30 -29
- udata/core/user/api.py +151 -152
- udata/core/user/api_fields.py +132 -100
- udata/core/user/apiv2.py +7 -7
- udata/core/user/commands.py +38 -38
- udata/core/user/factories.py +8 -9
- udata/core/user/forms.py +14 -11
- udata/core/user/metrics.py +2 -2
- udata/core/user/models.py +68 -69
- udata/core/user/permissions.py +4 -5
- udata/core/user/rdf.py +7 -8
- udata/core/user/tasks.py +2 -2
- udata/core/user/tests/test_user_model.py +24 -16
- udata/cors.py +99 -0
- udata/db/tasks.py +2 -1
- udata/entrypoints.py +35 -31
- udata/errors.py +2 -1
- udata/event/values.py +6 -6
- udata/factories.py +2 -2
- udata/features/identicon/api.py +5 -6
- udata/features/identicon/backends.py +48 -55
- udata/features/identicon/tests/test_backends.py +4 -5
- udata/features/notifications/__init__.py +0 -1
- udata/features/notifications/actions.py +9 -9
- udata/features/notifications/api.py +17 -13
- udata/features/territories/__init__.py +12 -10
- udata/features/territories/api.py +14 -15
- udata/features/territories/models.py +23 -28
- udata/features/transfer/actions.py +8 -11
- udata/features/transfer/api.py +84 -77
- udata/features/transfer/factories.py +2 -1
- udata/features/transfer/models.py +11 -12
- udata/features/transfer/notifications.py +19 -15
- udata/features/transfer/permissions.py +5 -5
- udata/forms/__init__.py +5 -2
- udata/forms/fields.py +164 -172
- udata/forms/validators.py +19 -22
- udata/forms/widgets.py +9 -13
- udata/frontend/__init__.py +31 -26
- udata/frontend/csv.py +68 -58
- udata/frontend/markdown.py +40 -44
- udata/harvest/actions.py +89 -77
- udata/harvest/api.py +294 -238
- udata/harvest/backends/__init__.py +4 -4
- udata/harvest/backends/base.py +128 -111
- udata/harvest/backends/dcat.py +80 -66
- udata/harvest/commands.py +56 -60
- udata/harvest/csv.py +8 -8
- udata/harvest/exceptions.py +6 -3
- udata/harvest/filters.py +24 -23
- udata/harvest/forms.py +27 -28
- udata/harvest/models.py +88 -80
- udata/harvest/notifications.py +15 -10
- udata/harvest/signals.py +13 -13
- udata/harvest/tasks.py +11 -10
- udata/harvest/tests/factories.py +23 -24
- udata/harvest/tests/test_actions.py +136 -166
- udata/harvest/tests/test_api.py +220 -214
- udata/harvest/tests/test_base_backend.py +117 -112
- udata/harvest/tests/test_dcat_backend.py +380 -308
- udata/harvest/tests/test_filters.py +33 -22
- udata/harvest/tests/test_models.py +11 -14
- udata/harvest/tests/test_notifications.py +6 -7
- udata/harvest/tests/test_tasks.py +7 -6
- udata/i18n.py +237 -78
- udata/linkchecker/backends.py +5 -11
- udata/linkchecker/checker.py +23 -22
- udata/linkchecker/commands.py +4 -6
- udata/linkchecker/models.py +6 -6
- udata/linkchecker/tasks.py +18 -20
- udata/mail.py +21 -21
- udata/migrations/2020-07-24-remove-s-from-scope-oauth.py +9 -8
- udata/migrations/2020-08-24-add-fs-filename.py +9 -8
- udata/migrations/2020-09-28-update-reuses-datasets-metrics.py +5 -4
- udata/migrations/2020-10-16-migrate-ods-resources.py +9 -10
- udata/migrations/2021-04-08-update-schema-with-new-structure.py +8 -7
- udata/migrations/2021-05-27-fix-default-schema-name.py +7 -6
- udata/migrations/2021-07-05-remove-unused-badges.py +17 -15
- udata/migrations/2021-07-07-update-schema-for-community-resources.py +7 -6
- udata/migrations/2021-08-17-follow-integrity.py +5 -4
- udata/migrations/2021-08-17-harvest-integrity.py +13 -12
- udata/migrations/2021-08-17-oauth2client-integrity.py +5 -4
- udata/migrations/2021-08-17-transfer-integrity.py +5 -4
- udata/migrations/2021-08-17-users-integrity.py +9 -8
- udata/migrations/2021-12-14-reuse-topics.py +7 -6
- udata/migrations/2022-04-21-improve-extension-detection.py +8 -7
- udata/migrations/2022-09-22-clean-inactive-harvest-datasets.py +16 -14
- udata/migrations/2022-10-10-add-fs_uniquifier-to-user-model.py +6 -6
- udata/migrations/2022-10-10-migrate-harvest-extras.py +36 -26
- udata/migrations/2023-02-08-rename-internal-dates.py +46 -28
- udata/migrations/2024-01-29-fix-reuse-and-dataset-with-private-None.py +10 -8
- udata/migrations/2024-03-22-migrate-activity-kwargs-to-extras.py +6 -4
- udata/migrations/2024-06-11-fix-reuse-datasets-references.py +7 -6
- udata/migrations/__init__.py +123 -105
- udata/models/__init__.py +4 -4
- udata/mongo/__init__.py +13 -11
- udata/mongo/badges_field.py +3 -2
- udata/mongo/datetime_fields.py +13 -12
- udata/mongo/document.py +17 -16
- udata/mongo/engine.py +15 -16
- udata/mongo/errors.py +2 -1
- udata/mongo/extras_fields.py +30 -20
- udata/mongo/queryset.py +12 -12
- udata/mongo/slug_fields.py +38 -28
- udata/mongo/taglist_field.py +1 -2
- udata/mongo/url_field.py +5 -5
- udata/mongo/uuid_fields.py +4 -3
- udata/notifications/__init__.py +1 -1
- udata/notifications/mattermost.py +10 -9
- udata/rdf.py +167 -188
- udata/routing.py +40 -45
- udata/search/__init__.py +18 -19
- udata/search/adapter.py +17 -16
- udata/search/commands.py +44 -51
- udata/search/fields.py +13 -20
- udata/search/query.py +23 -18
- udata/search/result.py +9 -10
- udata/sentry.py +21 -19
- udata/settings.py +262 -198
- udata/sitemap.py +8 -6
- udata/storage/s3.py +20 -13
- udata/tags.py +4 -5
- udata/tasks.py +43 -42
- udata/tests/__init__.py +9 -6
- udata/tests/api/__init__.py +8 -6
- udata/tests/api/test_auth_api.py +395 -321
- udata/tests/api/test_base_api.py +33 -35
- udata/tests/api/test_contact_points.py +7 -9
- udata/tests/api/test_dataservices_api.py +211 -158
- udata/tests/api/test_datasets_api.py +823 -812
- udata/tests/api/test_follow_api.py +13 -15
- udata/tests/api/test_me_api.py +95 -112
- udata/tests/api/test_organizations_api.py +301 -339
- udata/tests/api/test_reports_api.py +35 -25
- udata/tests/api/test_reuses_api.py +134 -139
- udata/tests/api/test_swagger.py +5 -5
- udata/tests/api/test_tags_api.py +18 -25
- udata/tests/api/test_topics_api.py +94 -94
- udata/tests/api/test_transfer_api.py +53 -48
- udata/tests/api/test_user_api.py +128 -141
- udata/tests/apiv2/test_datasets.py +290 -198
- udata/tests/apiv2/test_me_api.py +10 -11
- udata/tests/apiv2/test_organizations.py +56 -74
- udata/tests/apiv2/test_swagger.py +5 -5
- udata/tests/apiv2/test_topics.py +69 -87
- udata/tests/cli/test_cli_base.py +8 -8
- udata/tests/cli/test_db_cli.py +21 -19
- udata/tests/dataservice/test_dataservice_tasks.py +8 -12
- udata/tests/dataset/test_csv_adapter.py +44 -35
- udata/tests/dataset/test_dataset_actions.py +2 -3
- udata/tests/dataset/test_dataset_commands.py +7 -8
- udata/tests/dataset/test_dataset_events.py +36 -29
- udata/tests/dataset/test_dataset_model.py +224 -217
- udata/tests/dataset/test_dataset_rdf.py +142 -131
- udata/tests/dataset/test_dataset_tasks.py +15 -15
- udata/tests/dataset/test_resource_preview.py +10 -13
- udata/tests/features/territories/__init__.py +9 -13
- udata/tests/features/territories/test_territories_api.py +71 -91
- udata/tests/forms/test_basic_fields.py +7 -7
- udata/tests/forms/test_current_user_field.py +39 -66
- udata/tests/forms/test_daterange_field.py +31 -39
- udata/tests/forms/test_dict_field.py +28 -26
- udata/tests/forms/test_extras_fields.py +102 -76
- udata/tests/forms/test_form_field.py +8 -8
- udata/tests/forms/test_image_field.py +33 -26
- udata/tests/forms/test_model_field.py +134 -123
- udata/tests/forms/test_model_list_field.py +7 -7
- udata/tests/forms/test_nested_model_list_field.py +117 -79
- udata/tests/forms/test_publish_as_field.py +36 -65
- udata/tests/forms/test_reference_field.py +34 -53
- udata/tests/forms/test_user_forms.py +23 -21
- udata/tests/forms/test_uuid_field.py +6 -10
- udata/tests/frontend/__init__.py +9 -6
- udata/tests/frontend/test_auth.py +7 -6
- udata/tests/frontend/test_csv.py +81 -96
- udata/tests/frontend/test_hooks.py +43 -43
- udata/tests/frontend/test_markdown.py +211 -191
- udata/tests/helpers.py +32 -37
- udata/tests/models.py +2 -2
- udata/tests/organization/test_csv_adapter.py +21 -16
- udata/tests/organization/test_notifications.py +11 -18
- udata/tests/organization/test_organization_model.py +13 -13
- udata/tests/organization/test_organization_rdf.py +29 -22
- udata/tests/organization/test_organization_tasks.py +16 -17
- udata/tests/plugin.py +79 -73
- udata/tests/reuse/test_reuse_model.py +21 -21
- udata/tests/reuse/test_reuse_task.py +11 -13
- udata/tests/search/__init__.py +11 -12
- udata/tests/search/test_adapter.py +60 -70
- udata/tests/search/test_query.py +16 -16
- udata/tests/search/test_results.py +10 -7
- udata/tests/site/test_site_api.py +11 -16
- udata/tests/site/test_site_metrics.py +20 -30
- udata/tests/site/test_site_model.py +4 -5
- udata/tests/site/test_site_rdf.py +94 -78
- udata/tests/test_activity.py +17 -17
- udata/tests/test_cors.py +62 -0
- udata/tests/test_discussions.py +292 -299
- udata/tests/test_i18n.py +37 -40
- udata/tests/test_linkchecker.py +91 -85
- udata/tests/test_mail.py +13 -17
- udata/tests/test_migrations.py +219 -180
- udata/tests/test_model.py +164 -157
- udata/tests/test_notifications.py +17 -17
- udata/tests/test_owned.py +14 -14
- udata/tests/test_rdf.py +25 -23
- udata/tests/test_routing.py +89 -93
- udata/tests/test_storages.py +137 -128
- udata/tests/test_tags.py +44 -46
- udata/tests/test_topics.py +7 -7
- udata/tests/test_transfer.py +42 -49
- udata/tests/test_uris.py +160 -161
- udata/tests/test_utils.py +79 -71
- udata/tests/user/test_user_rdf.py +5 -9
- udata/tests/workers/test_jobs_commands.py +57 -58
- udata/tests/workers/test_tasks_routing.py +23 -29
- udata/tests/workers/test_workers_api.py +125 -131
- udata/tests/workers/test_workers_helpers.py +6 -6
- udata/tracking.py +4 -6
- udata/uris.py +45 -46
- udata/utils.py +68 -66
- udata/wsgi.py +1 -1
- {udata-9.1.2.dev30355.dist-info → udata-9.1.2.dev30454.dist-info}/METADATA +7 -3
- udata-9.1.2.dev30454.dist-info/RECORD +706 -0
- udata-9.1.2.dev30355.dist-info/RECORD +0 -704
- {udata-9.1.2.dev30355.dist-info → udata-9.1.2.dev30454.dist-info}/LICENSE +0 -0
- {udata-9.1.2.dev30355.dist-info → udata-9.1.2.dev30454.dist-info}/WHEEL +0 -0
- {udata-9.1.2.dev30355.dist-info → udata-9.1.2.dev30454.dist-info}/entry_points.txt +0 -0
- {udata-9.1.2.dev30355.dist-info → udata-9.1.2.dev30454.dist-info}/top_level.txt +0 -0
udata/core/spatial/models.py
CHANGED
|
@@ -4,44 +4,41 @@ from werkzeug.utils import cached_property
|
|
|
4
4
|
|
|
5
5
|
from udata.app import cache
|
|
6
6
|
from udata.core.metrics.models import WithMetrics
|
|
7
|
-
from udata.uris import endpoint_for
|
|
8
7
|
from udata.i18n import _, get_locale, language
|
|
9
8
|
from udata.mongo import db
|
|
9
|
+
from udata.uris import endpoint_for
|
|
10
10
|
|
|
11
11
|
from . import geoids
|
|
12
|
-
from .constants import
|
|
12
|
+
from .constants import ADMIN_LEVEL_MAX, ADMIN_LEVEL_MIN, BASE_GRANULARITIES
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
__all__ = ('GeoLevel', 'GeoZone', 'SpatialCoverage', 'spatial_granularities')
|
|
14
|
+
__all__ = ("GeoLevel", "GeoZone", "SpatialCoverage", "spatial_granularities")
|
|
16
15
|
|
|
17
16
|
|
|
18
17
|
class GeoLevel(db.Document):
|
|
19
18
|
id = db.StringField(primary_key=True)
|
|
20
19
|
name = db.StringField(required=True)
|
|
21
|
-
admin_level = db.IntField(min_value=ADMIN_LEVEL_MIN,
|
|
22
|
-
max_value=ADMIN_LEVEL_MAX,
|
|
23
|
-
default=100)
|
|
20
|
+
admin_level = db.IntField(min_value=ADMIN_LEVEL_MIN, max_value=ADMIN_LEVEL_MAX, default=100)
|
|
24
21
|
|
|
25
|
-
class GeoZoneQuerySet(db.BaseQuerySet):
|
|
26
22
|
|
|
23
|
+
class GeoZoneQuerySet(db.BaseQuerySet):
|
|
27
24
|
def resolve(self, geoid, id_only=False):
|
|
28
|
-
|
|
25
|
+
"""
|
|
29
26
|
Resolve a GeoZone given a GeoID.
|
|
30
27
|
|
|
31
28
|
If `id_only` is True,
|
|
32
29
|
the result will be the resolved GeoID
|
|
33
30
|
instead of the resolved zone.
|
|
34
|
-
|
|
31
|
+
"""
|
|
35
32
|
level, code = geoids.parse(geoid)
|
|
36
33
|
qs = self(level=level, code=code)
|
|
37
34
|
if id_only:
|
|
38
|
-
qs = qs.only(
|
|
35
|
+
qs = qs.only("id")
|
|
39
36
|
result = qs.first()
|
|
40
37
|
return result.id if id_only and result else result
|
|
41
38
|
|
|
42
39
|
|
|
43
40
|
class GeoZone(WithMetrics, db.Document):
|
|
44
|
-
SEPARATOR =
|
|
41
|
+
SEPARATOR = ":"
|
|
45
42
|
|
|
46
43
|
id = db.StringField(primary_key=True)
|
|
47
44
|
slug = db.StringField(required=True)
|
|
@@ -51,11 +48,11 @@ class GeoZone(WithMetrics, db.Document):
|
|
|
51
48
|
uri = db.StringField()
|
|
52
49
|
|
|
53
50
|
meta = {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
(
|
|
51
|
+
"indexes": [
|
|
52
|
+
"name",
|
|
53
|
+
("level", "code"),
|
|
57
54
|
],
|
|
58
|
-
|
|
55
|
+
"queryset_class": GeoZoneQuerySet,
|
|
59
56
|
}
|
|
60
57
|
|
|
61
58
|
def __str__(self):
|
|
@@ -63,20 +60,19 @@ class GeoZone(WithMetrics, db.Document):
|
|
|
63
60
|
|
|
64
61
|
def __html__(self):
|
|
65
62
|
"""In use within the admin."""
|
|
66
|
-
return
|
|
67
|
-
name=_(self.name), code=self.code)
|
|
63
|
+
return "{name} <i>({code})</i>".format(name=_(self.name), code=self.code)
|
|
68
64
|
|
|
69
65
|
@cached_property
|
|
70
66
|
def level_code(self):
|
|
71
67
|
"""Truncated level code for the sake of readability."""
|
|
72
68
|
# Either 'region', 'departement' or 'commune',
|
|
73
69
|
# useful to match TERRITORY_DATASETS keys.
|
|
74
|
-
return self.id.split(
|
|
70
|
+
return self.id.split(":")[1]
|
|
75
71
|
|
|
76
72
|
@cached_property
|
|
77
73
|
def level_name(self):
|
|
78
74
|
"""Truncated level name for the sake of readability."""
|
|
79
|
-
if self.level.startswith(
|
|
75
|
+
if self.level.startswith("fr:"):
|
|
80
76
|
return self.level[3:]
|
|
81
77
|
# Keep the whole level name as a fallback (e.g. `country:fr`)
|
|
82
78
|
return self.level
|
|
@@ -91,48 +87,48 @@ class GeoZone(WithMetrics, db.Document):
|
|
|
91
87
|
|
|
92
88
|
@property
|
|
93
89
|
def handled_level(self):
|
|
94
|
-
return self.level in current_app.config.get(
|
|
90
|
+
return self.level in current_app.config.get("HANDLED_LEVELS")
|
|
95
91
|
|
|
96
92
|
@property
|
|
97
93
|
def url(self):
|
|
98
|
-
return endpoint_for(
|
|
94
|
+
return endpoint_for("territories.territory", territory=self)
|
|
99
95
|
|
|
100
96
|
@property
|
|
101
97
|
def external_url(self):
|
|
102
|
-
return endpoint_for(
|
|
98
|
+
return endpoint_for("territories.territory", territory=self, _external=True)
|
|
103
99
|
|
|
104
100
|
def count_datasets(self):
|
|
105
101
|
from udata.models import Dataset
|
|
106
|
-
|
|
102
|
+
|
|
103
|
+
self.metrics["datasets"] = Dataset.objects(spatial__zones=self.id).visible().count()
|
|
107
104
|
self.save()
|
|
108
105
|
|
|
109
106
|
def toGeoJSON(self):
|
|
110
107
|
return {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
}
|
|
108
|
+
"id": self.id,
|
|
109
|
+
"type": "Feature",
|
|
110
|
+
"properties": {
|
|
111
|
+
"slug": self.slug,
|
|
112
|
+
"name": _(self.name),
|
|
113
|
+
"code": self.code,
|
|
114
|
+
"uri": self.uri,
|
|
115
|
+
"level": self.level,
|
|
116
|
+
},
|
|
120
117
|
}
|
|
121
118
|
|
|
122
119
|
|
|
123
120
|
@cache.memoize()
|
|
124
121
|
def get_spatial_granularities(lang):
|
|
125
122
|
with language(lang):
|
|
126
|
-
return [
|
|
127
|
-
(
|
|
128
|
-
]
|
|
123
|
+
return [(l.id, _(l.name)) for l in GeoLevel.objects] + [
|
|
124
|
+
(id, str(label)) for id, label in BASE_GRANULARITIES
|
|
125
|
+
]
|
|
129
126
|
|
|
130
127
|
|
|
131
|
-
spatial_granularities = LocalProxy(
|
|
132
|
-
lambda: get_spatial_granularities(get_locale()))
|
|
128
|
+
spatial_granularities = LocalProxy(lambda: get_spatial_granularities(get_locale()))
|
|
133
129
|
|
|
134
130
|
|
|
135
|
-
@cache.cached(timeout=50, key_prefix=
|
|
131
|
+
@cache.cached(timeout=50, key_prefix="admin_levels")
|
|
136
132
|
def get_spatial_admin_levels():
|
|
137
133
|
return dict((l.id, l.admin_level) for l in GeoLevel.objects)
|
|
138
134
|
|
|
@@ -141,15 +137,15 @@ admin_levels = LocalProxy(get_spatial_admin_levels)
|
|
|
141
137
|
|
|
142
138
|
|
|
143
139
|
class SpatialCoverage(db.EmbeddedDocument):
|
|
144
|
-
"""Represent a spatial coverage as a list of territories and/or a geometry.
|
|
145
|
-
|
|
140
|
+
"""Represent a spatial coverage as a list of territories and/or a geometry."""
|
|
141
|
+
|
|
146
142
|
geom = db.MultiPolygonField()
|
|
147
143
|
zones = db.ListField(db.ReferenceField(GeoZone))
|
|
148
|
-
granularity = db.StringField(default=
|
|
144
|
+
granularity = db.StringField(default="other")
|
|
149
145
|
|
|
150
146
|
@property
|
|
151
147
|
def granularity_label(self):
|
|
152
|
-
return dict(spatial_granularities).get(self.granularity or
|
|
148
|
+
return dict(spatial_granularities).get(self.granularity or "other", "other")
|
|
153
149
|
|
|
154
150
|
@property
|
|
155
151
|
def top_label(self):
|
|
@@ -168,9 +164,9 @@ class SpatialCoverage(db.EmbeddedDocument):
|
|
|
168
164
|
return [zone for zone in self.zones if zone.handled_level]
|
|
169
165
|
|
|
170
166
|
def clean(self):
|
|
171
|
-
if
|
|
167
|
+
if "geom" in self._get_changed_fields():
|
|
172
168
|
if self.zones:
|
|
173
|
-
raise db.ValidationError(
|
|
174
|
-
if
|
|
169
|
+
raise db.ValidationError("The spatial coverage already has a Geozone")
|
|
170
|
+
if "zones" in self._get_changed_fields():
|
|
175
171
|
if self.geom:
|
|
176
|
-
raise db.ValidationError(
|
|
172
|
+
raise db.ValidationError("The spatial coverage already has a Geometry")
|
udata/core/spatial/tasks.py
CHANGED
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
from flask import url_for
|
|
2
2
|
|
|
3
|
-
from udata.utils import faker
|
|
4
|
-
from udata.tests.api import APITestCase
|
|
5
|
-
from udata.tests.features.territories import (
|
|
6
|
-
create_geozones_fixtures, TerritoriesSettings
|
|
7
|
-
)
|
|
8
|
-
from udata.core.organization.factories import OrganizationFactory
|
|
9
3
|
from udata.core.dataset.factories import DatasetFactory
|
|
4
|
+
from udata.core.organization.factories import OrganizationFactory
|
|
10
5
|
from udata.core.spatial.factories import (
|
|
11
|
-
|
|
6
|
+
GeoLevelFactory,
|
|
7
|
+
GeoZoneFactory,
|
|
8
|
+
SpatialCoverageFactory,
|
|
12
9
|
)
|
|
13
10
|
from udata.core.spatial.tasks import compute_geozones_metrics
|
|
11
|
+
from udata.tests.api import APITestCase
|
|
12
|
+
from udata.tests.features.territories import (
|
|
13
|
+
TerritoriesSettings,
|
|
14
|
+
create_geozones_fixtures,
|
|
15
|
+
)
|
|
16
|
+
from udata.utils import faker
|
|
14
17
|
|
|
15
18
|
|
|
16
19
|
class SpatialApiTest(APITestCase):
|
|
@@ -19,151 +22,141 @@ class SpatialApiTest(APITestCase):
|
|
|
19
22
|
def test_zones_api_one(self):
|
|
20
23
|
zone = GeoZoneFactory()
|
|
21
24
|
|
|
22
|
-
url = url_for(
|
|
25
|
+
url = url_for("api.zones", ids=[zone.id])
|
|
23
26
|
response = self.get(url)
|
|
24
27
|
self.assert200(response)
|
|
25
28
|
|
|
26
|
-
self.assertEqual(len(response.json[
|
|
29
|
+
self.assertEqual(len(response.json["features"]), 1)
|
|
27
30
|
|
|
28
|
-
feature = response.json[
|
|
29
|
-
self.assertEqual(feature[
|
|
30
|
-
self.assertEqual(feature[
|
|
31
|
+
feature = response.json["features"][0]
|
|
32
|
+
self.assertEqual(feature["type"], "Feature")
|
|
33
|
+
self.assertEqual(feature["id"], zone.id)
|
|
31
34
|
|
|
32
|
-
properties = feature[
|
|
33
|
-
self.assertEqual(properties[
|
|
34
|
-
self.assertEqual(properties[
|
|
35
|
-
self.assertEqual(properties[
|
|
36
|
-
self.assertEqual(properties[
|
|
35
|
+
properties = feature["properties"]
|
|
36
|
+
self.assertEqual(properties["name"], zone.name)
|
|
37
|
+
self.assertEqual(properties["code"], zone.code)
|
|
38
|
+
self.assertEqual(properties["level"], zone.level)
|
|
39
|
+
self.assertEqual(properties["uri"], zone.uri)
|
|
37
40
|
|
|
38
41
|
def test_zones_api_many(self):
|
|
39
42
|
zones = [GeoZoneFactory() for _ in range(3)]
|
|
40
43
|
|
|
41
|
-
url = url_for(
|
|
44
|
+
url = url_for("api.zones", ids=zones)
|
|
42
45
|
response = self.get(url)
|
|
43
46
|
self.assert200(response)
|
|
44
47
|
|
|
45
|
-
self.assertEqual(len(response.json[
|
|
48
|
+
self.assertEqual(len(response.json["features"]), len(zones))
|
|
46
49
|
|
|
47
|
-
for zone, feature in zip(zones, response.json[
|
|
48
|
-
self.assertEqual(feature[
|
|
49
|
-
self.assertEqual(feature[
|
|
50
|
+
for zone, feature in zip(zones, response.json["features"]):
|
|
51
|
+
self.assertEqual(feature["type"], "Feature")
|
|
52
|
+
self.assertEqual(feature["id"], zone.id)
|
|
50
53
|
|
|
51
|
-
properties = feature[
|
|
52
|
-
self.assertEqual(properties[
|
|
53
|
-
self.assertEqual(properties[
|
|
54
|
-
self.assertEqual(properties[
|
|
55
|
-
self.assertEqual(properties[
|
|
54
|
+
properties = feature["properties"]
|
|
55
|
+
self.assertEqual(properties["name"], zone.name)
|
|
56
|
+
self.assertEqual(properties["code"], zone.code)
|
|
57
|
+
self.assertEqual(properties["level"], zone.level)
|
|
58
|
+
self.assertEqual(properties["uri"], zone.uri)
|
|
56
59
|
|
|
57
60
|
def test_suggest_zones_on_name(self):
|
|
58
|
-
|
|
61
|
+
"""It should suggest zones based on its name"""
|
|
59
62
|
for i in range(4):
|
|
60
|
-
GeoZoneFactory(name=
|
|
61
|
-
if i % 2 else faker.word())
|
|
63
|
+
GeoZoneFactory(name="name-test-{0}".format(i) if i % 2 else faker.word())
|
|
62
64
|
|
|
63
|
-
response = self.get(
|
|
64
|
-
url_for('api.suggest_zones'), qs={'q': 'name-test', 'size': '5'})
|
|
65
|
+
response = self.get(url_for("api.suggest_zones"), qs={"q": "name-test", "size": "5"})
|
|
65
66
|
self.assert200(response)
|
|
66
67
|
|
|
67
68
|
self.assertEqual(len(response.json), 2)
|
|
68
69
|
|
|
69
70
|
for suggestion in response.json:
|
|
70
|
-
self.assertIn(
|
|
71
|
-
self.assertIn(
|
|
72
|
-
self.assertIn(
|
|
73
|
-
self.assertIn(
|
|
74
|
-
self.assertIn(
|
|
75
|
-
self.assertIn(
|
|
71
|
+
self.assertIn("id", suggestion)
|
|
72
|
+
self.assertIn("name", suggestion)
|
|
73
|
+
self.assertIn("code", suggestion)
|
|
74
|
+
self.assertIn("uri", suggestion)
|
|
75
|
+
self.assertIn("level", suggestion)
|
|
76
|
+
self.assertIn("name-test", suggestion["name"])
|
|
76
77
|
|
|
77
78
|
def test_suggest_zones_sorted(self):
|
|
78
|
-
|
|
79
|
-
country_level = GeoLevelFactory(id=
|
|
80
|
-
region_level = GeoLevelFactory(id=
|
|
81
|
-
country_zone = GeoZoneFactory(name=
|
|
82
|
-
region_zone = GeoZoneFactory(name=
|
|
83
|
-
|
|
84
|
-
response = self.get(
|
|
85
|
-
url_for('api.suggest_zones'), qs={'q': 'name-test', 'size': '5'})
|
|
79
|
+
"""It should suggest zones based on its name"""
|
|
80
|
+
country_level = GeoLevelFactory(id="country", name="country", admin_level=10)
|
|
81
|
+
region_level = GeoLevelFactory(id="region", name="region", admin_level=20)
|
|
82
|
+
country_zone = GeoZoneFactory(name="name-test-country", level=country_level.id)
|
|
83
|
+
region_zone = GeoZoneFactory(name="name-test-region", level=region_level.id)
|
|
84
|
+
|
|
85
|
+
response = self.get(url_for("api.suggest_zones"), qs={"q": "name-test", "size": "5"})
|
|
86
86
|
self.assert200(response)
|
|
87
87
|
|
|
88
88
|
self.assertEqual(len(response.json), 2)
|
|
89
|
-
self.assertEqual((response.json[0][
|
|
90
|
-
self.assertEqual((response.json[1][
|
|
89
|
+
self.assertEqual((response.json[0]["id"]), country_zone.id)
|
|
90
|
+
self.assertEqual((response.json[1]["id"]), region_zone.id)
|
|
91
91
|
|
|
92
92
|
def test_suggest_zones_on_code(self):
|
|
93
|
-
|
|
93
|
+
"""It should suggest zones based on its code"""
|
|
94
94
|
for i in range(4):
|
|
95
|
-
GeoZoneFactory(code=
|
|
96
|
-
if i % 2 else faker.word())
|
|
95
|
+
GeoZoneFactory(code="code-test-{0}".format(i) if i % 2 else faker.word())
|
|
97
96
|
|
|
98
|
-
response = self.get(
|
|
99
|
-
url_for('api.suggest_zones'), qs={'q': 'code-test', 'size': '5'})
|
|
97
|
+
response = self.get(url_for("api.suggest_zones"), qs={"q": "code-test", "size": "5"})
|
|
100
98
|
self.assert200(response)
|
|
101
99
|
|
|
102
100
|
self.assertEqual(len(response.json), 2)
|
|
103
101
|
|
|
104
102
|
for suggestion in response.json:
|
|
105
|
-
self.assertIn(
|
|
106
|
-
self.assertIn(
|
|
107
|
-
self.assertIn(
|
|
108
|
-
self.assertIn(
|
|
109
|
-
self.assertIn(
|
|
110
|
-
self.assertIn(
|
|
103
|
+
self.assertIn("id", suggestion)
|
|
104
|
+
self.assertIn("name", suggestion)
|
|
105
|
+
self.assertIn("code", suggestion)
|
|
106
|
+
self.assertIn("level", suggestion)
|
|
107
|
+
self.assertIn("uri", suggestion)
|
|
108
|
+
self.assertIn("code-test", suggestion["code"])
|
|
111
109
|
|
|
112
110
|
def test_suggest_zones_no_match(self):
|
|
113
|
-
|
|
111
|
+
"""It should not provide zones suggestions if no match"""
|
|
114
112
|
for i in range(3):
|
|
115
|
-
GeoZoneFactory(name=5 *
|
|
116
|
-
code=3 * '{0}'.format(i))
|
|
113
|
+
GeoZoneFactory(name=5 * "{0}".format(i), code=3 * "{0}".format(i))
|
|
117
114
|
|
|
118
|
-
response = self.get(
|
|
119
|
-
url_for('api.suggest_zones'), qs={'q': 'xxxxxx', 'size': '5'})
|
|
115
|
+
response = self.get(url_for("api.suggest_zones"), qs={"q": "xxxxxx", "size": "5"})
|
|
120
116
|
self.assert200(response)
|
|
121
117
|
self.assertEqual(len(response.json), 0)
|
|
122
118
|
|
|
123
119
|
def test_suggest_zones_unicode(self):
|
|
124
|
-
|
|
120
|
+
"""It should suggest zones based on its name"""
|
|
125
121
|
for i in range(4):
|
|
126
|
-
GeoZoneFactory(name=
|
|
127
|
-
if i % 2 else faker.word())
|
|
122
|
+
GeoZoneFactory(name="name-testé-{0}".format(i) if i % 2 else faker.word())
|
|
128
123
|
|
|
129
|
-
response = self.get(
|
|
130
|
-
url_for('api.suggest_zones'), qs={'q': 'name-testé', 'size': '5'})
|
|
124
|
+
response = self.get(url_for("api.suggest_zones"), qs={"q": "name-testé", "size": "5"})
|
|
131
125
|
self.assert200(response)
|
|
132
126
|
|
|
133
127
|
self.assertEqual(len(response.json), 2)
|
|
134
128
|
|
|
135
129
|
for suggestion in response.json:
|
|
136
|
-
self.assertIn(
|
|
137
|
-
self.assertIn(
|
|
138
|
-
self.assertIn(
|
|
139
|
-
self.assertIn(
|
|
140
|
-
self.assertIn(
|
|
141
|
-
self.assertIn(
|
|
130
|
+
self.assertIn("id", suggestion)
|
|
131
|
+
self.assertIn("name", suggestion)
|
|
132
|
+
self.assertIn("code", suggestion)
|
|
133
|
+
self.assertIn("level", suggestion)
|
|
134
|
+
self.assertIn("uri", suggestion)
|
|
135
|
+
self.assertIn("name-testé", suggestion["name"])
|
|
142
136
|
|
|
143
137
|
def test_suggest_zones_empty(self):
|
|
144
|
-
|
|
145
|
-
response = self.get(
|
|
146
|
-
url_for('api.suggest_zones'), qs={'q': 'xxxxxx', 'size': '5'})
|
|
138
|
+
"""It should not provide zones suggestion if no data is present"""
|
|
139
|
+
response = self.get(url_for("api.suggest_zones"), qs={"q": "xxxxxx", "size": "5"})
|
|
147
140
|
self.assert200(response)
|
|
148
141
|
self.assertEqual(len(response.json), 0)
|
|
149
142
|
|
|
150
143
|
def test_spatial_levels(self):
|
|
151
144
|
levels = [GeoLevelFactory() for _ in range(3)]
|
|
152
145
|
|
|
153
|
-
response = self.get(url_for(
|
|
146
|
+
response = self.get(url_for("api.spatial_levels"))
|
|
154
147
|
self.assert200(response)
|
|
155
148
|
self.assertEqual(len(response.json), len(levels))
|
|
156
149
|
|
|
157
150
|
def test_spatial_granularities(self):
|
|
158
151
|
levels = [GeoLevelFactory() for _ in range(3)]
|
|
159
152
|
|
|
160
|
-
response = self.get(url_for(
|
|
153
|
+
response = self.get(url_for("api.spatial_granularities"))
|
|
161
154
|
self.assert200(response)
|
|
162
155
|
self.assertEqual(len(response.json), len(levels) + 2)
|
|
163
156
|
|
|
164
157
|
def test_zone_datasets_empty(self):
|
|
165
158
|
paca, bdr, arles = create_geozones_fixtures()
|
|
166
|
-
response = self.get(url_for(
|
|
159
|
+
response = self.get(url_for("api.zone_datasets", id=paca.id))
|
|
167
160
|
self.assert200(response)
|
|
168
161
|
self.assertEqual(response.json, [])
|
|
169
162
|
|
|
@@ -172,10 +165,10 @@ class SpatialApiTest(APITestCase):
|
|
|
172
165
|
organization = OrganizationFactory()
|
|
173
166
|
for _ in range(3):
|
|
174
167
|
DatasetFactory(
|
|
175
|
-
organization=organization,
|
|
176
|
-
|
|
168
|
+
organization=organization, spatial=SpatialCoverageFactory(zones=[paca.id])
|
|
169
|
+
)
|
|
177
170
|
|
|
178
|
-
response = self.get(url_for(
|
|
171
|
+
response = self.get(url_for("api.zone_datasets", id=paca.id))
|
|
179
172
|
self.assert200(response)
|
|
180
173
|
self.assertEqual(len(response.json), 3)
|
|
181
174
|
|
|
@@ -184,11 +177,10 @@ class SpatialApiTest(APITestCase):
|
|
|
184
177
|
organization = OrganizationFactory()
|
|
185
178
|
for _ in range(3):
|
|
186
179
|
DatasetFactory(
|
|
187
|
-
organization=organization,
|
|
188
|
-
|
|
180
|
+
organization=organization, spatial=SpatialCoverageFactory(zones=[paca.id])
|
|
181
|
+
)
|
|
189
182
|
|
|
190
|
-
response = self.get(url_for(
|
|
191
|
-
qs={'size': 2})
|
|
183
|
+
response = self.get(url_for("api.zone_datasets", id=paca.id), qs={"size": 2})
|
|
192
184
|
self.assert200(response)
|
|
193
185
|
self.assertEqual(len(response.json), 2)
|
|
194
186
|
|
|
@@ -197,11 +189,10 @@ class SpatialApiTest(APITestCase):
|
|
|
197
189
|
organization = OrganizationFactory()
|
|
198
190
|
for _ in range(3):
|
|
199
191
|
DatasetFactory(
|
|
200
|
-
organization=organization,
|
|
201
|
-
|
|
192
|
+
organization=organization, spatial=SpatialCoverageFactory(zones=[paca.id])
|
|
193
|
+
)
|
|
202
194
|
|
|
203
|
-
response = self.get(
|
|
204
|
-
url_for('api.zone_datasets', id=paca.id), qs={'dynamic': 1})
|
|
195
|
+
response = self.get(url_for("api.zone_datasets", id=paca.id), qs={"dynamic": 1})
|
|
205
196
|
self.assert200(response)
|
|
206
197
|
# No dynamic datasets given that the setting is deactivated by default.
|
|
207
198
|
self.assertEqual(len(response.json), 3)
|
|
@@ -211,49 +202,48 @@ class SpatialApiTest(APITestCase):
|
|
|
211
202
|
organization = OrganizationFactory()
|
|
212
203
|
for _ in range(3):
|
|
213
204
|
DatasetFactory(
|
|
214
|
-
organization=organization,
|
|
215
|
-
|
|
205
|
+
organization=organization, spatial=SpatialCoverageFactory(zones=[paca.id])
|
|
206
|
+
)
|
|
216
207
|
|
|
217
|
-
response = self.get(
|
|
218
|
-
url_for('api.zone_datasets', id=paca.id),
|
|
219
|
-
qs={'dynamic': 1, 'size': 2})
|
|
208
|
+
response = self.get(url_for("api.zone_datasets", id=paca.id), qs={"dynamic": 1, "size": 2})
|
|
220
209
|
self.assert200(response)
|
|
221
210
|
# No dynamic datasets given that the setting is deactivated by default.
|
|
222
211
|
self.assertEqual(len(response.json), 2)
|
|
223
212
|
|
|
224
213
|
def test_coverage_empty(self):
|
|
225
|
-
GeoLevelFactory(id=
|
|
226
|
-
response = self.get(url_for(
|
|
214
|
+
GeoLevelFactory(id="top")
|
|
215
|
+
response = self.get(url_for("api.spatial_coverage", level="top"))
|
|
227
216
|
self.assert200(response)
|
|
228
|
-
self.assertEqual(
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
217
|
+
self.assertEqual(
|
|
218
|
+
response.json,
|
|
219
|
+
{
|
|
220
|
+
"type": "FeatureCollection",
|
|
221
|
+
"features": [],
|
|
222
|
+
},
|
|
223
|
+
)
|
|
232
224
|
|
|
233
225
|
def test_coverage_datasets_count(self):
|
|
234
|
-
GeoLevelFactory(id=
|
|
226
|
+
GeoLevelFactory(id="fr:commune")
|
|
235
227
|
paris = GeoZoneFactory(
|
|
236
|
-
id=
|
|
237
|
-
|
|
228
|
+
id="fr:commune:75056", level="fr:commune", name="Paris", code="75056"
|
|
229
|
+
)
|
|
238
230
|
arles = GeoZoneFactory(
|
|
239
|
-
id=
|
|
240
|
-
|
|
231
|
+
id="fr:commune:13004", level="fr:commune", name="Arles", code="13004"
|
|
232
|
+
)
|
|
241
233
|
|
|
242
234
|
for _ in range(3):
|
|
243
|
-
DatasetFactory(
|
|
244
|
-
spatial=SpatialCoverageFactory(zones=[paris.id]))
|
|
235
|
+
DatasetFactory(spatial=SpatialCoverageFactory(zones=[paris.id]))
|
|
245
236
|
for _ in range(2):
|
|
246
|
-
DatasetFactory(
|
|
247
|
-
|
|
248
|
-
|
|
237
|
+
DatasetFactory(spatial=SpatialCoverageFactory(zones=[arles.id]))
|
|
238
|
+
|
|
249
239
|
compute_geozones_metrics()
|
|
250
240
|
|
|
251
|
-
response = self.get(url_for(
|
|
241
|
+
response = self.get(url_for("api.spatial_coverage", level="fr:commune"))
|
|
252
242
|
self.assert200(response)
|
|
253
|
-
self.assertEqual(response.json[
|
|
254
|
-
self.assertEqual(response.json[
|
|
255
|
-
self.assertEqual(response.json[
|
|
256
|
-
self.assertEqual(response.json[
|
|
243
|
+
self.assertEqual(response.json["features"][0]["id"], "fr:commune:13004")
|
|
244
|
+
self.assertEqual(response.json["features"][0]["properties"]["datasets"], 2)
|
|
245
|
+
self.assertEqual(response.json["features"][1]["id"], "fr:commune:75056")
|
|
246
|
+
self.assertEqual(response.json["features"][1]["properties"]["datasets"], 3)
|
|
257
247
|
|
|
258
248
|
|
|
259
249
|
class SpatialTerritoriesApiTest(APITestCase):
|
|
@@ -265,11 +255,10 @@ class SpatialTerritoriesApiTest(APITestCase):
|
|
|
265
255
|
organization = OrganizationFactory()
|
|
266
256
|
for _ in range(3):
|
|
267
257
|
DatasetFactory(
|
|
268
|
-
organization=organization,
|
|
269
|
-
|
|
258
|
+
organization=organization, spatial=SpatialCoverageFactory(zones=[paca.id])
|
|
259
|
+
)
|
|
270
260
|
|
|
271
|
-
response = self.get(
|
|
272
|
-
url_for('api.zone_datasets', id=paca.id), qs={'dynamic': 1})
|
|
261
|
+
response = self.get(url_for("api.zone_datasets", id=paca.id), qs={"dynamic": 1})
|
|
273
262
|
self.assert200(response)
|
|
274
263
|
# No dynamic datasets given that they are added by udata-front extension.
|
|
275
264
|
self.assertEqual(len(response.json), 3)
|
|
@@ -279,14 +268,10 @@ class SpatialTerritoriesApiTest(APITestCase):
|
|
|
279
268
|
organization = OrganizationFactory()
|
|
280
269
|
for _ in range(3):
|
|
281
270
|
DatasetFactory(
|
|
282
|
-
organization=organization,
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
response = self.get(
|
|
286
|
-
url_for('api.zone_datasets', id=paca.id), qs={
|
|
287
|
-
'dynamic': 1,
|
|
288
|
-
'size': 2
|
|
289
|
-
})
|
|
271
|
+
organization=organization, spatial=SpatialCoverageFactory(zones=[paca.id])
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
response = self.get(url_for("api.zone_datasets", id=paca.id), qs={"dynamic": 1, "size": 2})
|
|
290
275
|
self.assert200(response)
|
|
291
276
|
# No dynamic datasets given that they are added by udata-front extension.
|
|
292
277
|
self.assertEqual(len(response.json), 2)
|