udata 13.0.1.dev12__py3-none-any.whl → 14.0.0__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.

Files changed (77) hide show
  1. udata/api/__init__.py +2 -8
  2. udata/app.py +12 -30
  3. udata/auth/forms.py +6 -4
  4. udata/commands/__init__.py +2 -14
  5. udata/commands/db.py +13 -25
  6. udata/commands/info.py +0 -16
  7. udata/core/avatars/api.py +43 -0
  8. udata/core/avatars/test_avatar_api.py +30 -0
  9. udata/core/dataservices/models.py +14 -2
  10. udata/core/dataset/tasks.py +36 -8
  11. udata/core/metrics/__init__.py +0 -6
  12. udata/core/site/models.py +2 -6
  13. udata/core/spatial/commands.py +2 -4
  14. udata/core/spatial/models.py +0 -10
  15. udata/core/spatial/tests/test_api.py +1 -36
  16. udata/core/user/models.py +10 -1
  17. udata/cors.py +2 -5
  18. udata/db/migrations.py +279 -0
  19. udata/frontend/__init__.py +3 -122
  20. udata/harvest/actions.py +3 -8
  21. udata/harvest/api.py +5 -14
  22. udata/harvest/backends/__init__.py +21 -9
  23. udata/harvest/backends/base.py +2 -2
  24. udata/harvest/backends/ckan/harvesters.py +2 -0
  25. udata/harvest/backends/dcat.py +3 -0
  26. udata/harvest/backends/maaf.py +1 -0
  27. udata/harvest/commands.py +6 -4
  28. udata/harvest/forms.py +9 -6
  29. udata/harvest/tasks.py +3 -5
  30. udata/harvest/tests/ckan/test_ckan_backend.py +2 -2
  31. udata/harvest/tests/ckan/test_ckan_backend_errors.py +1 -1
  32. udata/harvest/tests/ckan/test_ckan_backend_filters.py +1 -1
  33. udata/harvest/tests/ckan/test_dkan_backend.py +1 -1
  34. udata/harvest/tests/dcat/udata.xml +6 -6
  35. udata/harvest/tests/factories.py +1 -1
  36. udata/harvest/tests/test_actions.py +5 -3
  37. udata/harvest/tests/test_api.py +2 -1
  38. udata/harvest/tests/test_base_backend.py +2 -0
  39. udata/harvest/tests/test_dcat_backend.py +3 -3
  40. udata/i18n.py +14 -273
  41. udata/migrations/2025-11-13-delete-user-email-index.py +25 -0
  42. udata/models/__init__.py +0 -8
  43. udata/routing.py +0 -8
  44. udata/sentry.py +4 -10
  45. udata/settings.py +16 -17
  46. udata/tasks.py +3 -3
  47. udata/tests/__init__.py +1 -10
  48. udata/tests/api/test_dataservices_api.py +29 -1
  49. udata/tests/api/test_datasets_api.py +1 -2
  50. udata/tests/api/test_user_api.py +12 -0
  51. udata/tests/apiv2/test_topics.py +1 -1
  52. udata/tests/dataset/test_resource_preview.py +0 -1
  53. udata/tests/helpers.py +12 -0
  54. udata/tests/test_cors.py +1 -1
  55. udata/tests/test_migrations.py +181 -481
  56. udata/utils.py +5 -0
  57. {udata-13.0.1.dev12.dist-info → udata-14.0.0.dist-info}/METADATA +1 -2
  58. {udata-13.0.1.dev12.dist-info → udata-14.0.0.dist-info}/RECORD +62 -73
  59. {udata-13.0.1.dev12.dist-info → udata-14.0.0.dist-info}/entry_points.txt +3 -5
  60. udata/core/followers/views.py +0 -15
  61. udata/entrypoints.py +0 -93
  62. udata/features/identicon/__init__.py +0 -0
  63. udata/features/identicon/api.py +0 -13
  64. udata/features/identicon/backends.py +0 -131
  65. udata/features/identicon/tests/__init__.py +0 -0
  66. udata/features/identicon/tests/test_backends.py +0 -18
  67. udata/features/territories/__init__.py +0 -49
  68. udata/features/territories/api.py +0 -25
  69. udata/features/territories/models.py +0 -51
  70. udata/migrations/__init__.py +0 -367
  71. udata/tests/cli/test_db_cli.py +0 -68
  72. udata/tests/features/territories/__init__.py +0 -20
  73. udata/tests/features/territories/test_territories_api.py +0 -185
  74. udata/tests/frontend/test_hooks.py +0 -149
  75. {udata-13.0.1.dev12.dist-info → udata-14.0.0.dist-info}/WHEEL +0 -0
  76. {udata-13.0.1.dev12.dist-info → udata-14.0.0.dist-info}/licenses/LICENSE +0 -0
  77. {udata-13.0.1.dev12.dist-info → udata-14.0.0.dist-info}/top_level.txt +0 -0
@@ -1,68 +0,0 @@
1
- from datetime import datetime
2
-
3
- import pytest
4
- from bson import ObjectId
5
- from mongoengine.connection import get_db
6
-
7
- from udata.models import Reuse
8
- from udata.tests.api import PytestOnlyDBTestCase
9
-
10
-
11
- @pytest.fixture
12
- def migrations():
13
- get_db().migrations.insert_one(
14
- {
15
- "plugin": "udata",
16
- "filename": "test.py",
17
- "date": datetime.utcnow(),
18
- "script": 'print("ok")',
19
- "output": "ok",
20
- }
21
- )
22
- return get_db().migrations
23
-
24
-
25
- class DBCliTest(PytestOnlyDBTestCase):
26
- def test_unrecord_with_complete_filename(self, cli, migrations):
27
- """Should display help without errors"""
28
- result = cli("db unrecord udata test.py")
29
- assert result.exit_code == 0
30
- assert migrations.count_documents({}) == 0
31
-
32
- def test_unrecord_with_filename_without_extension(self, cli, migrations):
33
- """Should display help without errors"""
34
- result = cli("db unrecord udata test")
35
- assert result.exit_code == 0
36
- assert migrations.count_documents({}) == 0
37
-
38
- def test_unrecord_with_single_parameter(self, cli, migrations):
39
- """Should display help without errors"""
40
- result = cli("db unrecord udata:test.py")
41
- assert result.exit_code == 0
42
- assert migrations.count_documents({}) == 0
43
-
44
- def test_unrecord_with_single_parameter_without_extension(self, cli, migrations):
45
- """Should display help without errors"""
46
- result = cli("db unrecord udata:test")
47
- assert result.exit_code == 0
48
- assert migrations.count_documents({}) == 0
49
-
50
- def test_unrecord_without_parameters(self, cli, migrations):
51
- """Should display help without errors"""
52
- result = cli("db unrecord", check=False)
53
- assert result.exit_code != 0
54
- assert migrations.count_documents({}) == 1
55
-
56
- def test_unrecord_with_too_many_parameters(self, cli, migrations):
57
- """Should display help without errors"""
58
- result = cli("db unrecord udata test.py too many", check=False)
59
- assert result.exit_code != 0
60
- assert migrations.count_documents({}) == 1
61
-
62
- def test_check_references_report_listfield_missing(self, cli):
63
- # The cli command `udata db check-integrity` should catch reuse object missing datasets field
64
- Reuse._get_collection().insert_one({"_id": ObjectId()})
65
-
66
- result = cli("db check-integrity --models Reuse", check=False)
67
- assert "Reuse.datasets(Dataset) — list…: 1" in result.output
68
- assert result.exit_code != 0
@@ -1,20 +0,0 @@
1
- from udata.core.spatial.factories import GeoZoneFactory
2
-
3
-
4
- def create_geozones_fixtures():
5
- paca = GeoZoneFactory(
6
- id="fr:region:93", level="fr:region", name="Provence Alpes Côtes dAzur", code="93"
7
- )
8
- bdr = GeoZoneFactory(
9
- id="fr:departement:13", level="fr:departement", name="Bouches-du-Rhône", code="13"
10
- )
11
- arles = GeoZoneFactory(id="fr:commune:13004", level="fr:commune", name="Arles", code="13004")
12
- return paca, bdr, arles
13
-
14
-
15
- def create_old_new_regions_fixtures():
16
- lr = GeoZoneFactory(id="fr:region:91", level="fr:region", name="Languedoc-Rousillon", code="91")
17
- occitanie = GeoZoneFactory(
18
- id="fr:region:76", level="fr:region", name="Languedoc-Rousillon et Midi-Pyrénées", code="76"
19
- )
20
- return lr, occitanie
@@ -1,185 +0,0 @@
1
- import pytest
2
- from flask import url_for
3
-
4
- from udata.core.spatial.factories import GeoZoneFactory
5
- from udata.tests.api import APITestCase
6
- from udata.tests.features.territories import (
7
- create_geozones_fixtures,
8
- create_old_new_regions_fixtures,
9
- )
10
-
11
-
12
- @pytest.mark.options(
13
- ACTIVATE_TERRITORIES=True,
14
- HANDLED_LEVELS=("fr:commune", "fr:departement", "fr:region", "country"),
15
- )
16
- class TerritoriesAPITest(APITestCase):
17
- def setUp(self):
18
- self.paca, self.bdr, self.arles = create_geozones_fixtures()
19
-
20
- def test_suggest_no_parameter(self):
21
- response = self.get(url_for("api.suggest_territory"))
22
- self.assert400(response)
23
-
24
- def test_suggest_empty(self):
25
- response = self.get(url_for("api.suggest_territory", q="tes"))
26
- self.assert200(response)
27
- self.assertEqual(response.json, [])
28
- response = self.get(url_for("api.suggest_territory", q="test"))
29
- self.assert200(response)
30
- self.assertEqual(response.json, [])
31
-
32
- def test_suggest_town(self):
33
- response = self.get(url_for("api.suggest_territory", q="arle"))
34
- self.assert200(response)
35
- result = response.json[0]
36
- self.assertEqual(result["title"], self.arles.name)
37
- self.assertEqual(result["id"], self.arles.id)
38
- self.assertIn("page", result)
39
-
40
- def test_suggest_town_five_letters(self):
41
- response = self.get(url_for("api.suggest_territory", q="arles"))
42
- self.assert200(response)
43
- result = response.json[0]
44
- self.assertEqual(result["title"], self.arles.name)
45
- self.assertEqual(result["id"], self.arles.id)
46
- self.assertIn("page", result)
47
-
48
- def test_suggest_town_by_insee_code(self):
49
- response = self.get(url_for("api.suggest_territory", q="13004"))
50
- self.assert200(response)
51
- result = response.json[0]
52
- self.assertEqual(result["id"], self.arles.id)
53
- self.assertEqual(result["title"], self.arles.name)
54
-
55
- def test_suggest_towns(self):
56
- arles_sur_tech = GeoZoneFactory(
57
- id="fr:commune:66009", level="fr:commune", name="Arles-sur-Tech", code="66009"
58
- )
59
- response = self.get(url_for("api.suggest_territory", q="arles"))
60
- self.assert200(response)
61
- results = response.json
62
- self.assertEqual(len(results), 2)
63
- # Arles must be first given the population.
64
- self.assertEqual(results[0]["id"], self.arles.id)
65
- self.assertEqual(results[1]["id"], arles_sur_tech.id)
66
-
67
- def test_suggest_county(self):
68
- response = self.get(url_for("api.suggest_territory", q="bouche"))
69
- self.assert200(response)
70
- result = response.json[0]
71
- self.assertEqual(result["title"], self.bdr.name)
72
- self.assertEqual(result["id"], self.bdr.id)
73
- self.assertIn("page", result)
74
-
75
- def test_suggest_region(self):
76
- response = self.get(url_for("api.suggest_territory", q="prov"))
77
- self.assert200(response)
78
- result = response.json[0]
79
- self.assertEqual(result["title"], self.paca.name)
80
- self.assertEqual(result["id"], self.paca.id)
81
- self.assertIn("page", result)
82
-
83
- def test_suggest_old_new_region(self):
84
- lr, occitanie = create_old_new_regions_fixtures()
85
- response = self.get(url_for("api.suggest_territory", q="langue"))
86
- self.assert200(response)
87
- self.assertEqual(len(response.json), 2)
88
- result = response.json[0]
89
- self.assertEqual(result["title"], occitanie.name)
90
- self.assertEqual(result["id"], occitanie.id)
91
- result = response.json[1]
92
- self.assertEqual(result["title"], lr.name)
93
- self.assertEqual(result["id"], lr.id)
94
-
95
- def test_suggest_county_by_id(self):
96
- response = self.get(url_for("api.suggest_territory", q="13"))
97
- self.assert200(response)
98
- result = response.json[0]
99
- self.assertEqual(result["id"], self.bdr.id)
100
- self.assertEqual(result["title"], self.bdr.name)
101
-
102
- def test_suggest_town_and_county(self):
103
- bouchet = GeoZoneFactory(
104
- id="fr:commune:26054", level="fr:commune", name="Bouchet", code="26054"
105
- )
106
- response = self.get(url_for("api.suggest_territory", q="bouche"))
107
- self.assert200(response)
108
- results = response.json
109
- self.assertEqual(len(results), 2)
110
-
111
- self.assertEqual(results[0]["id"], bouchet.id)
112
- self.assertEqual(results[1]["id"], self.bdr.id)
113
-
114
- def test_suggest_drom_com(self):
115
- guyane = GeoZoneFactory(
116
- id="fr:departement:973", level="fr:departement", name="Guyane", code="973"
117
- )
118
- response = self.get(url_for("api.suggest_territory", q="guya"))
119
- self.assert200(response)
120
- results = response.json
121
- self.assertEqual(len(results), 1)
122
- self.assertEqual(results[0]["id"], guyane.id)
123
-
124
- def test_suggest_drom_com_by_code(self):
125
- guyane = GeoZoneFactory(
126
- id="fr:departement:973", level="fr:departement", name="Guyane", code="973"
127
- )
128
- response = self.get(url_for("api.suggest_territory", q="973"))
129
- self.assert200(response)
130
- results = response.json
131
- self.assertEqual(len(results), 1)
132
- self.assertEqual(results[0]["id"], guyane.id)
133
-
134
- def test_suggest_corsica(self):
135
- bastia = GeoZoneFactory(
136
- id="fr:commune:2b033", level="fr:commune", name="Bastia", code="2b033"
137
- )
138
- response = self.get(url_for("api.suggest_territory", q="basti"))
139
- self.assert200(response)
140
- results = response.json
141
- self.assertEqual(len(results), 1)
142
- self.assertEqual(results[0]["id"], bastia.id)
143
-
144
- def test_suggest_corsica_by_code(self):
145
- bastia = GeoZoneFactory(
146
- id="fr:commune:2b033", level="fr:commune", name="Bastia", code="2b033"
147
- )
148
- response = self.get(url_for("api.suggest_territory", q="2b033"))
149
- self.assert200(response)
150
- results = response.json
151
- self.assertEqual(len(results), 1)
152
- self.assertEqual(results[0]["id"], bastia.id)
153
-
154
- def test_suggest_corsica_by_county_code(self):
155
- haute_corse = GeoZoneFactory(
156
- id="fr:departement:2b", level="fr:departement", name="Haute-Corse", code="2b"
157
- )
158
- response = self.get(url_for("api.suggest_territory", q="2b"))
159
- self.assert200(response)
160
- results = response.json
161
- self.assertEqual(len(results), 1)
162
- self.assertEqual(results[0]["id"], haute_corse.id)
163
-
164
- def test_not_suggest_country(self):
165
- GeoZoneFactory(id="country:fr", level="country", name="France")
166
- response = self.get(url_for("api.suggest_territory", q="fra"))
167
- self.assert200(response)
168
- results = response.json
169
- self.assertEqual(len(results), 0)
170
- response = self.get(url_for("api.suggest_territory", q="fran"))
171
- self.assert200(response)
172
- results = response.json
173
- self.assertEqual(len(results), 0)
174
- response = self.get(url_for("api.suggest_territory", q="franc"))
175
- self.assert200(response)
176
- results = response.json
177
- self.assertEqual(len(results), 0)
178
-
179
- def test_suggest_unicode(self):
180
- response = self.get(url_for("api.suggest_territory", q="Bouches-du-Rhône"))
181
- self.assert200(response)
182
- result = response.json[0]
183
- self.assertEqual(result["title"], self.bdr.name)
184
- self.assertEqual(result["id"], self.bdr.id)
185
- self.assertIn("page", result)
@@ -1,149 +0,0 @@
1
- import pytest
2
- from flask import Blueprint, render_template_string, url_for
3
-
4
- from udata.frontend import template_hook
5
- from udata.tests.api import PytestOnlyAPITestCase
6
- from udata.tests.helpers import assert200
7
-
8
- bp = Blueprint("hooks_tests", __name__, url_prefix="/hooks_tests")
9
-
10
-
11
- @template_hook
12
- def single(ctx):
13
- return "single"
14
-
15
-
16
- @template_hook
17
- def hello(ctx, name):
18
- return "Hello {}".format(name)
19
-
20
-
21
- @template_hook
22
- def kwargs(ctx, **kwargs):
23
- return ", ".join(sorted("{0}={1}".format(k, v) for k, v in kwargs.items()))
24
-
25
-
26
- @template_hook("multiple")
27
- def first(ctx):
28
- return "first"
29
-
30
-
31
- @template_hook("multiple")
32
- def second(ctx):
33
- return "second"
34
-
35
-
36
- @template_hook("conditionnal", when=lambda ctx: True)
37
- def true(ctx):
38
- return "true"
39
-
40
-
41
- @template_hook("conditionnal", when=lambda ctx: False)
42
- def false(ctx):
43
- return "false"
44
-
45
-
46
- @bp.route("/empty/render")
47
- def render_empty():
48
- return render_template_string('{{ hook("empty") }}')
49
-
50
-
51
- @bp.route("/empty/iter")
52
- def iter_empty():
53
- return render_template_string('{% for w in hook("empty") %}<{{ w }}>{% endfor %}')
54
-
55
-
56
- @bp.route("/hello")
57
- def render_hello():
58
- return render_template_string('{{ hook("hello", "world") }}')
59
-
60
-
61
- @bp.route("/kwargs")
62
- def render_kwargs():
63
- return render_template_string('{{ hook("kwargs", key="value", other=42) }}')
64
-
65
-
66
- @bp.route("/single/render")
67
- def render_single():
68
- return render_template_string('{{ hook("single") }}')
69
-
70
-
71
- @bp.route("/single/iter")
72
- def iter_single():
73
- return render_template_string('{% for w in hook("single") %}<{{ w }}>{% endfor %}')
74
-
75
-
76
- @bp.route("/multiple/render")
77
- def render_multiple():
78
- return render_template_string('{{ hook("multiple") }}')
79
-
80
-
81
- @bp.route("/multiple/iter")
82
- def iter_multiple():
83
- return render_template_string('{% for w in hook("multiple") %}<{{ w }}>{% endfor %}')
84
-
85
-
86
- @bp.route("/conditionnal/render")
87
- def render_conditionnal():
88
- return render_template_string('{{ hook("conditionnal") }}')
89
-
90
-
91
- @bp.route("/conditionnal/iter")
92
- def iter_conditionnal():
93
- return render_template_string('{% for w in hook("conditionnal") %}<{{ w }}>{% endfor %}')
94
-
95
-
96
- class HooksTest(PytestOnlyAPITestCase):
97
- @pytest.fixture(autouse=True)
98
- def setup_func(self, app):
99
- app.register_blueprint(bp)
100
-
101
- def test_empty_template_hook(self, client):
102
- response = client.get(url_for("hooks_tests.render_empty"))
103
- assert200(response)
104
- assert b"" == response.data
105
-
106
- def test_iter_empty_template_hook(self, client):
107
- response = client.get(url_for("hooks_tests.iter_empty"))
108
- assert200(response)
109
- assert b"" == response.data
110
-
111
- def test_single_template_hook(self, client):
112
- response = client.get(url_for("hooks_tests.render_single"))
113
- assert200(response)
114
- assert b"single" == response.data
115
-
116
- def test_iter_single_template_hook(self, client):
117
- response = client.get(url_for("hooks_tests.iter_single"))
118
- assert200(response)
119
- assert b"<single>" == response.data
120
-
121
- def test_multiple_template_hooks(self, client):
122
- response = client.get(url_for("hooks_tests.render_multiple"))
123
- assert200(response)
124
- assert b"firstsecond" == response.data
125
-
126
- def test_iter_multiple_template_hooks(self, client):
127
- response = client.get(url_for("hooks_tests.iter_multiple"))
128
- assert200(response)
129
- assert b"<first><second>" == response.data
130
-
131
- def test_conditionnal_template_hooks(self, client):
132
- response = client.get(url_for("hooks_tests.render_conditionnal"))
133
- assert200(response)
134
- assert b"true" == response.data
135
-
136
- def test_iter_conditionnal_template_hooks(self, client):
137
- response = client.get(url_for("hooks_tests.iter_conditionnal"))
138
- assert200(response)
139
- assert b"<true>" == response.data
140
-
141
- def test_arguments(self, client):
142
- response = client.get(url_for("hooks_tests.render_hello"))
143
- assert200(response)
144
- assert b"Hello world" == response.data
145
-
146
- def test_kwargs(self, client):
147
- response = client.get(url_for("hooks_tests.render_kwargs"))
148
- assert200(response)
149
- assert b"key=value, other=42" == response.data