umap-project 3.3.6__py3-none-any.whl → 3.4.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.
- umap/__init__.py +1 -1
- umap/context_processors.py +4 -1
- umap/locale/cs_CZ/LC_MESSAGES/django.mo +0 -0
- umap/locale/cs_CZ/LC_MESSAGES/django.po +43 -33
- umap/locale/da/LC_MESSAGES/django.mo +0 -0
- umap/locale/da/LC_MESSAGES/django.po +43 -33
- umap/locale/de/LC_MESSAGES/django.mo +0 -0
- umap/locale/de/LC_MESSAGES/django.po +35 -29
- umap/locale/el/LC_MESSAGES/django.mo +0 -0
- umap/locale/el/LC_MESSAGES/django.po +35 -29
- umap/locale/en/LC_MESSAGES/django.po +47 -41
- umap/locale/es/LC_MESSAGES/django.mo +0 -0
- umap/locale/es/LC_MESSAGES/django.po +43 -33
- umap/locale/et/LC_MESSAGES/django.mo +0 -0
- umap/locale/et/LC_MESSAGES/django.po +58 -54
- umap/locale/eu/LC_MESSAGES/django.mo +0 -0
- umap/locale/eu/LC_MESSAGES/django.po +43 -33
- umap/locale/fa_IR/LC_MESSAGES/django.mo +0 -0
- umap/locale/fa_IR/LC_MESSAGES/django.po +43 -33
- umap/locale/fr/LC_MESSAGES/django.mo +0 -0
- umap/locale/fr/LC_MESSAGES/django.po +36 -30
- umap/locale/gl/LC_MESSAGES/django.mo +0 -0
- umap/locale/gl/LC_MESSAGES/django.po +43 -33
- umap/locale/hu/LC_MESSAGES/django.mo +0 -0
- umap/locale/hu/LC_MESSAGES/django.po +35 -29
- umap/locale/is/LC_MESSAGES/django.mo +0 -0
- umap/locale/is/LC_MESSAGES/django.po +43 -33
- umap/locale/it/LC_MESSAGES/django.mo +0 -0
- umap/locale/it/LC_MESSAGES/django.po +43 -33
- umap/locale/nl/LC_MESSAGES/django.mo +0 -0
- umap/locale/nl/LC_MESSAGES/django.po +35 -29
- umap/locale/pl/LC_MESSAGES/django.mo +0 -0
- umap/locale/pl/LC_MESSAGES/django.po +114 -103
- umap/locale/pt/LC_MESSAGES/django.mo +0 -0
- umap/locale/pt/LC_MESSAGES/django.po +43 -33
- umap/locale/th_TH/LC_MESSAGES/django.mo +0 -0
- umap/locale/th_TH/LC_MESSAGES/django.po +310 -109
- umap/locale/zh_TW/LC_MESSAGES/django.mo +0 -0
- umap/locale/zh_TW/LC_MESSAGES/django.po +80 -70
- umap/management/commands/switch_user.py +2 -2
- umap/migrations/0018_datalayer_uuid.py +1 -1
- umap/models.py +7 -3
- umap/settings/local.py.sample +1 -1
- umap/static/umap/base.css +89 -32
- umap/static/umap/content.css +129 -33
- umap/static/umap/css/bar.css +82 -20
- umap/static/umap/css/browser.css +163 -0
- umap/static/umap/css/contextmenu.css +15 -0
- umap/static/umap/css/dialog.css +36 -16
- umap/static/umap/css/form.css +123 -33
- umap/static/umap/css/icon.css +46 -3
- umap/static/umap/css/panel.css +7 -3
- umap/static/umap/css/popup.css +34 -8
- umap/static/umap/css/tooltip.css +8 -4
- umap/static/umap/img/16-white.svg +26 -8
- umap/static/umap/img/16.svg +1 -1
- umap/static/umap/img/source/16-white.svg +36 -18
- umap/static/umap/img/source/16.svg +1 -1
- umap/static/umap/js/components/alerts/alert.css +69 -31
- umap/static/umap/js/components/alerts/alert.js +20 -2
- umap/static/umap/js/components/base.js +1 -1
- umap/static/umap/js/modules/browser.js +69 -61
- umap/static/umap/js/modules/caption.js +10 -7
- umap/static/umap/js/modules/data/features.js +85 -60
- umap/static/umap/js/modules/data/fields.js +446 -0
- umap/static/umap/js/modules/data/layer.js +78 -184
- umap/static/umap/js/modules/domutils.js +109 -0
- umap/static/umap/js/modules/filters.js +780 -0
- umap/static/umap/js/modules/form/builder.js +8 -5
- umap/static/umap/js/modules/form/fields.js +111 -221
- umap/static/umap/js/modules/formatter.js +24 -1
- umap/static/umap/js/modules/help.js +4 -3
- umap/static/umap/js/modules/i18n.js +1 -1
- umap/static/umap/js/modules/importer.js +1 -1
- umap/static/umap/js/modules/importers/opendata.js +15 -0
- umap/static/umap/js/modules/importers/openrouteservice.js +6 -1
- umap/static/umap/js/modules/managers.js +2 -2
- umap/static/umap/js/modules/permissions.js +39 -31
- umap/static/umap/js/modules/rendering/controls.js +11 -9
- umap/static/umap/js/modules/rendering/icon.js +3 -8
- umap/static/umap/js/modules/rendering/layers/base.js +1 -1
- umap/static/umap/js/modules/rendering/layers/classified.js +18 -11
- umap/static/umap/js/modules/rendering/layers/cluster.js +5 -3
- umap/static/umap/js/modules/rendering/layers/heat.js +27 -21
- umap/static/umap/js/modules/rendering/template.js +50 -23
- umap/static/umap/js/modules/rendering/ui.js +29 -23
- umap/static/umap/js/modules/rules.js +38 -44
- umap/static/umap/js/modules/schema.js +3 -6
- umap/static/umap/js/modules/share.js +5 -4
- umap/static/umap/js/modules/tableeditor.js +50 -38
- umap/static/umap/js/modules/templates.js +2 -3
- umap/static/umap/js/modules/ui/bar.js +55 -23
- umap/static/umap/js/modules/ui/dialog.js +38 -27
- umap/static/umap/js/modules/ui/panel.js +23 -8
- umap/static/umap/js/modules/ui/tooltip.js +6 -5
- umap/static/umap/js/modules/umap.js +151 -56
- umap/static/umap/js/modules/utils.js +24 -2
- umap/static/umap/js/umap.core.js +1 -110
- umap/static/umap/locale/am_ET.js +52 -17
- umap/static/umap/locale/am_ET.json +52 -17
- umap/static/umap/locale/ar.js +52 -17
- umap/static/umap/locale/ar.json +52 -17
- umap/static/umap/locale/ast.js +52 -17
- umap/static/umap/locale/ast.json +52 -17
- umap/static/umap/locale/bg.js +52 -17
- umap/static/umap/locale/bg.json +52 -17
- umap/static/umap/locale/br.js +48 -22
- umap/static/umap/locale/br.json +48 -22
- umap/static/umap/locale/ca.js +52 -17
- umap/static/umap/locale/ca.json +52 -17
- umap/static/umap/locale/cs_CZ.js +52 -17
- umap/static/umap/locale/cs_CZ.json +52 -17
- umap/static/umap/locale/da.js +54 -17
- umap/static/umap/locale/da.json +54 -17
- umap/static/umap/locale/de.js +51 -16
- umap/static/umap/locale/de.json +51 -16
- umap/static/umap/locale/el.js +52 -17
- umap/static/umap/locale/el.json +52 -17
- umap/static/umap/locale/en.js +53 -16
- umap/static/umap/locale/en.json +53 -16
- umap/static/umap/locale/en_US.json +52 -17
- umap/static/umap/locale/es.js +54 -17
- umap/static/umap/locale/es.json +54 -17
- umap/static/umap/locale/et.js +91 -56
- umap/static/umap/locale/et.json +91 -56
- umap/static/umap/locale/eu.js +84 -49
- umap/static/umap/locale/eu.json +84 -49
- umap/static/umap/locale/fa_IR.js +52 -17
- umap/static/umap/locale/fa_IR.json +52 -17
- umap/static/umap/locale/fi.js +52 -17
- umap/static/umap/locale/fi.json +52 -17
- umap/static/umap/locale/fr.js +53 -16
- umap/static/umap/locale/fr.json +53 -16
- umap/static/umap/locale/gl.js +52 -17
- umap/static/umap/locale/gl.json +52 -17
- umap/static/umap/locale/he.js +52 -17
- umap/static/umap/locale/he.json +52 -17
- umap/static/umap/locale/hr.js +52 -17
- umap/static/umap/locale/hr.json +52 -17
- umap/static/umap/locale/hu.js +59 -24
- umap/static/umap/locale/hu.json +59 -24
- umap/static/umap/locale/id.js +52 -17
- umap/static/umap/locale/id.json +52 -17
- umap/static/umap/locale/is.js +52 -17
- umap/static/umap/locale/is.json +52 -17
- umap/static/umap/locale/it.js +52 -17
- umap/static/umap/locale/it.json +52 -17
- umap/static/umap/locale/ja.js +52 -17
- umap/static/umap/locale/ja.json +52 -17
- umap/static/umap/locale/ko.js +52 -17
- umap/static/umap/locale/ko.json +52 -17
- umap/static/umap/locale/lt.js +52 -17
- umap/static/umap/locale/lt.json +52 -17
- umap/static/umap/locale/ms.js +52 -17
- umap/static/umap/locale/ms.json +52 -17
- umap/static/umap/locale/nl.js +52 -17
- umap/static/umap/locale/nl.json +52 -17
- umap/static/umap/locale/no.js +52 -17
- umap/static/umap/locale/no.json +52 -17
- umap/static/umap/locale/pl.js +53 -17
- umap/static/umap/locale/pl.json +53 -17
- umap/static/umap/locale/pl_PL.json +52 -17
- umap/static/umap/locale/pt.js +52 -17
- umap/static/umap/locale/pt.json +52 -17
- umap/static/umap/locale/pt_BR.js +52 -17
- umap/static/umap/locale/pt_BR.json +52 -17
- umap/static/umap/locale/pt_PT.js +52 -17
- umap/static/umap/locale/pt_PT.json +52 -17
- umap/static/umap/locale/ro.js +52 -17
- umap/static/umap/locale/ro.json +52 -17
- umap/static/umap/locale/ru.js +52 -17
- umap/static/umap/locale/ru.json +52 -17
- umap/static/umap/locale/si.js +1 -1
- umap/static/umap/locale/si.json +1 -1
- umap/static/umap/locale/sk_SK.js +52 -17
- umap/static/umap/locale/sk_SK.json +52 -17
- umap/static/umap/locale/sl.js +52 -17
- umap/static/umap/locale/sl.json +52 -17
- umap/static/umap/locale/sr.js +52 -17
- umap/static/umap/locale/sr.json +52 -17
- umap/static/umap/locale/sv.js +52 -17
- umap/static/umap/locale/sv.json +52 -17
- umap/static/umap/locale/th_TH.js +52 -17
- umap/static/umap/locale/th_TH.json +52 -17
- umap/static/umap/locale/tr.js +52 -17
- umap/static/umap/locale/tr.json +52 -17
- umap/static/umap/locale/uk_UA.js +52 -17
- umap/static/umap/locale/uk_UA.json +52 -17
- umap/static/umap/locale/vi.js +52 -17
- umap/static/umap/locale/vi.json +52 -17
- umap/static/umap/locale/vi_VN.json +52 -17
- umap/static/umap/locale/zh.js +52 -17
- umap/static/umap/locale/zh.json +52 -17
- umap/static/umap/locale/zh_CN.json +52 -17
- umap/static/umap/locale/zh_TW.Big5.json +52 -17
- umap/static/umap/locale/zh_TW.js +52 -16
- umap/static/umap/locale/zh_TW.json +52 -16
- umap/static/umap/map.css +63 -226
- umap/static/umap/unittests/utils.js +18 -0
- umap/static/umap/vars.css +23 -5
- umap/templates/umap/components/alerts/alert.html +32 -29
- umap/templates/umap/css.html +2 -1
- umap/templates/umap/login_popup_end.html +18 -9
- umap/templates/umap/user_map_table.html +7 -2
- umap/tests/integration/conftest.py +10 -6
- umap/tests/integration/test_anonymous_owned_map.py +90 -37
- umap/tests/integration/test_basics.py +25 -1
- umap/tests/integration/test_browser.py +37 -0
- umap/tests/integration/test_conditional_rules.py +107 -52
- umap/tests/integration/test_draw_polygon.py +6 -0
- umap/tests/integration/test_draw_polyline.py +11 -0
- umap/tests/integration/test_edit_marker.py +1 -1
- umap/tests/integration/test_export_map.py +19 -0
- umap/tests/integration/test_fields.py +541 -0
- umap/tests/integration/test_filters.py +616 -0
- umap/tests/integration/test_iframe.py +1 -1
- umap/tests/integration/test_import.py +38 -42
- umap/tests/integration/test_map_preview.py +1 -1
- umap/tests/integration/test_picto.py +1 -1
- umap/tests/integration/test_popup.py +31 -0
- umap/tests/integration/test_remote_data.py +60 -4
- umap/tests/integration/test_save.py +1 -1
- umap/tests/integration/test_share.py +4 -4
- umap/tests/integration/test_tableeditor.py +31 -7
- umap/tests/integration/test_websocket_sync.py +71 -20
- umap/tests/test_dashboard.py +11 -1
- umap/tests/test_statics.py +2 -2
- umap/tests/test_utils.py +19 -2
- umap/tests/test_views.py +1 -1
- umap/urls.py +1 -0
- umap/utils.py +8 -1
- umap/views.py +5 -0
- {umap_project-3.3.6.dist-info → umap_project-3.4.0.dist-info}/METADATA +15 -15
- {umap_project-3.3.6.dist-info → umap_project-3.4.0.dist-info}/RECORD +237 -233
- umap/static/umap/js/modules/facets.js +0 -164
- umap/tests/integration/test_facets_browser.py +0 -279
- {umap_project-3.3.6.dist-info → umap_project-3.4.0.dist-info}/WHEEL +0 -0
- {umap_project-3.3.6.dist-info → umap_project-3.4.0.dist-info}/entry_points.txt +0 -0
- {umap_project-3.3.6.dist-info → umap_project-3.4.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -25,7 +25,9 @@ def setup_function():
|
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
@pytest.mark.xdist_group(name="websockets")
|
|
28
|
-
def test_websocket_connection_can_sync_markers(
|
|
28
|
+
def test_websocket_connection_can_sync_markers(
|
|
29
|
+
new_page, asgi_live_server, tilelayer, wait_for_loaded
|
|
30
|
+
):
|
|
29
31
|
map = MapFactory(name="sync", edit_status=Map.ANONYMOUS)
|
|
30
32
|
map.settings["properties"]["syncEnabled"] = True
|
|
31
33
|
map.save()
|
|
@@ -34,8 +36,10 @@ def test_websocket_connection_can_sync_markers(new_page, asgi_live_server, tilel
|
|
|
34
36
|
# Create two tabs
|
|
35
37
|
peerA = new_page("Page A")
|
|
36
38
|
peerA.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
|
|
39
|
+
wait_for_loaded(peerA)
|
|
37
40
|
peerB = new_page("Page B")
|
|
38
41
|
peerB.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
|
|
42
|
+
wait_for_loaded(peerB)
|
|
39
43
|
|
|
40
44
|
a_marker_pane = peerA.locator(".leaflet-marker-pane > div")
|
|
41
45
|
b_marker_pane = peerB.locator(".leaflet-marker-pane > div")
|
|
@@ -49,6 +53,7 @@ def test_websocket_connection_can_sync_markers(new_page, asgi_live_server, tilel
|
|
|
49
53
|
|
|
50
54
|
a_map_el = peerA.locator("#map")
|
|
51
55
|
a_map_el.click(position={"x": 220, "y": 220})
|
|
56
|
+
peerA.wait_for_timeout(300) # Time for the panel animation to finish
|
|
52
57
|
expect(a_marker_pane).to_have_count(1)
|
|
53
58
|
expect(b_marker_pane).to_have_count(1)
|
|
54
59
|
# Peer B should not be in state dirty
|
|
@@ -91,17 +96,21 @@ def test_websocket_connection_can_sync_markers(new_page, asgi_live_server, tilel
|
|
|
91
96
|
|
|
92
97
|
|
|
93
98
|
@pytest.mark.xdist_group(name="websockets")
|
|
94
|
-
def test_websocket_connection_can_sync_polygons(
|
|
99
|
+
def test_websocket_connection_can_sync_polygons(
|
|
100
|
+
new_page, asgi_live_server, tilelayer, wait_for_loaded
|
|
101
|
+
):
|
|
95
102
|
map = MapFactory(name="sync", edit_status=Map.ANONYMOUS)
|
|
96
103
|
map.settings["properties"]["syncEnabled"] = True
|
|
97
104
|
map.save()
|
|
98
105
|
DataLayerFactory(map=map, data={})
|
|
99
106
|
|
|
100
107
|
# Create two tabs
|
|
101
|
-
peerA =
|
|
108
|
+
peerA = new_page("Page A")
|
|
102
109
|
peerA.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
|
|
103
|
-
|
|
110
|
+
wait_for_loaded(peerA)
|
|
111
|
+
peerB = new_page("Page B")
|
|
104
112
|
peerB.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
|
|
113
|
+
wait_for_loaded(peerB)
|
|
105
114
|
|
|
106
115
|
b_map_el = peerB.locator("#map")
|
|
107
116
|
|
|
@@ -171,7 +180,7 @@ def test_websocket_connection_can_sync_polygons(context, asgi_live_server, tilel
|
|
|
171
180
|
|
|
172
181
|
@pytest.mark.xdist_group(name="websockets")
|
|
173
182
|
def test_websocket_connection_can_sync_map_properties(
|
|
174
|
-
new_page, asgi_live_server, tilelayer
|
|
183
|
+
new_page, asgi_live_server, tilelayer, wait_for_loaded
|
|
175
184
|
):
|
|
176
185
|
map = MapFactory(name="sync", edit_status=Map.ANONYMOUS)
|
|
177
186
|
map.settings["properties"]["syncEnabled"] = True
|
|
@@ -181,8 +190,10 @@ def test_websocket_connection_can_sync_map_properties(
|
|
|
181
190
|
# Create two tabs
|
|
182
191
|
peerA = new_page()
|
|
183
192
|
peerA.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
|
|
193
|
+
wait_for_loaded(peerA)
|
|
184
194
|
peerB = new_page()
|
|
185
195
|
peerB.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
|
|
196
|
+
wait_for_loaded(peerB)
|
|
186
197
|
|
|
187
198
|
# Name change is synced
|
|
188
199
|
peerA.get_by_role("button", name="Edit map name and caption").click()
|
|
@@ -205,7 +216,7 @@ def test_websocket_connection_can_sync_map_properties(
|
|
|
205
216
|
|
|
206
217
|
@pytest.mark.xdist_group(name="websockets")
|
|
207
218
|
def test_websocket_connection_can_sync_datalayer_properties(
|
|
208
|
-
new_page, asgi_live_server, tilelayer
|
|
219
|
+
new_page, asgi_live_server, tilelayer, wait_for_loaded
|
|
209
220
|
):
|
|
210
221
|
map = MapFactory(name="sync", edit_status=Map.ANONYMOUS)
|
|
211
222
|
map.settings["properties"]["syncEnabled"] = True
|
|
@@ -215,8 +226,10 @@ def test_websocket_connection_can_sync_datalayer_properties(
|
|
|
215
226
|
# Create two tabs
|
|
216
227
|
peerA = new_page()
|
|
217
228
|
peerA.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
|
|
229
|
+
wait_for_loaded(peerA)
|
|
218
230
|
peerB = new_page()
|
|
219
231
|
peerB.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
|
|
232
|
+
wait_for_loaded(peerB)
|
|
220
233
|
|
|
221
234
|
# Layer addition, name and type are synced
|
|
222
235
|
peerA.get_by_role("button", name="Manage layers").click()
|
|
@@ -227,14 +240,16 @@ def test_websocket_connection_can_sync_datalayer_properties(
|
|
|
227
240
|
peerA.locator("body").press("Escape")
|
|
228
241
|
|
|
229
242
|
peerB.get_by_role("button", name="Manage layers").click()
|
|
230
|
-
peerB.locator(".panel.right").get_by_role(
|
|
243
|
+
peerB.locator(".panel.right").get_by_role(
|
|
244
|
+
"button", name="Edit", exact=True
|
|
245
|
+
).first.click()
|
|
231
246
|
expect(peerB.locator('input[name="name"]')).to_have_value("synced layer!")
|
|
232
247
|
expect(peerB.get_by_role("combobox")).to_have_value("Choropleth")
|
|
233
248
|
|
|
234
249
|
|
|
235
250
|
@pytest.mark.xdist_group(name="websockets")
|
|
236
251
|
def test_websocket_connection_can_sync_cloned_polygons(
|
|
237
|
-
|
|
252
|
+
new_page, asgi_live_server, tilelayer, wait_for_loaded
|
|
238
253
|
):
|
|
239
254
|
map = MapFactory(name="sync", edit_status=Map.ANONYMOUS)
|
|
240
255
|
map.settings["properties"]["syncEnabled"] = True
|
|
@@ -242,10 +257,12 @@ def test_websocket_connection_can_sync_cloned_polygons(
|
|
|
242
257
|
DataLayerFactory(map=map, data={})
|
|
243
258
|
|
|
244
259
|
# Create two tabs
|
|
245
|
-
peerA =
|
|
260
|
+
peerA = new_page("Page A")
|
|
246
261
|
peerA.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
|
|
247
|
-
|
|
262
|
+
wait_for_loaded(peerA)
|
|
263
|
+
peerB = new_page("Page B")
|
|
248
264
|
peerB.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
|
|
265
|
+
wait_for_loaded(peerB)
|
|
249
266
|
|
|
250
267
|
b_map_el = peerB.locator("#map")
|
|
251
268
|
|
|
@@ -295,7 +312,7 @@ def test_websocket_connection_can_sync_cloned_polygons(
|
|
|
295
312
|
|
|
296
313
|
@pytest.mark.xdist_group(name="websockets")
|
|
297
314
|
def test_websocket_connection_can_sync_late_joining_peer(
|
|
298
|
-
new_page, asgi_live_server, tilelayer
|
|
315
|
+
new_page, asgi_live_server, tilelayer, wait_for_loaded
|
|
299
316
|
):
|
|
300
317
|
map = MapFactory(name="sync", edit_status=Map.ANONYMOUS)
|
|
301
318
|
map.settings["properties"]["syncEnabled"] = True
|
|
@@ -305,6 +322,7 @@ def test_websocket_connection_can_sync_late_joining_peer(
|
|
|
305
322
|
# Create first peer (A) and have it join immediately
|
|
306
323
|
peerA = new_page("Page A")
|
|
307
324
|
peerA.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
|
|
325
|
+
wait_for_loaded(peerA)
|
|
308
326
|
|
|
309
327
|
# Add a marker from peer A
|
|
310
328
|
a_create_marker = peerA.get_by_title("Draw a marker")
|
|
@@ -313,6 +331,7 @@ def test_websocket_connection_can_sync_late_joining_peer(
|
|
|
313
331
|
|
|
314
332
|
a_map_el = peerA.locator("#map")
|
|
315
333
|
a_map_el.click(position={"x": 220, "y": 220})
|
|
334
|
+
peerA.wait_for_timeout(300) # Time for the panel animation to finish
|
|
316
335
|
peerA.locator("body").type("First marker")
|
|
317
336
|
peerA.locator("body").press("Escape")
|
|
318
337
|
peerA.wait_for_timeout(300)
|
|
@@ -331,6 +350,7 @@ def test_websocket_connection_can_sync_late_joining_peer(
|
|
|
331
350
|
# Now create peer B and have it join
|
|
332
351
|
peerB = new_page("Page B")
|
|
333
352
|
peerB.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
|
|
353
|
+
wait_for_loaded(peerB)
|
|
334
354
|
|
|
335
355
|
# Check if peer B has received all the updates
|
|
336
356
|
b_marker_pane = peerB.locator(".leaflet-marker-pane > div")
|
|
@@ -355,7 +375,7 @@ def test_websocket_connection_can_sync_late_joining_peer(
|
|
|
355
375
|
|
|
356
376
|
|
|
357
377
|
@pytest.mark.xdist_group(name="websockets")
|
|
358
|
-
def test_should_sync_datalayers(new_page, asgi_live_server, tilelayer):
|
|
378
|
+
def test_should_sync_datalayers(new_page, asgi_live_server, tilelayer, wait_for_loaded):
|
|
359
379
|
map = MapFactory(name="sync", edit_status=Map.ANONYMOUS)
|
|
360
380
|
map.settings["properties"]["syncEnabled"] = True
|
|
361
381
|
map.save()
|
|
@@ -365,8 +385,10 @@ def test_should_sync_datalayers(new_page, asgi_live_server, tilelayer):
|
|
|
365
385
|
# Create two tabs
|
|
366
386
|
peerA = new_page("Page A")
|
|
367
387
|
peerA.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
|
|
388
|
+
wait_for_loaded(peerA)
|
|
368
389
|
peerB = new_page("Page B")
|
|
369
390
|
peerB.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
|
|
391
|
+
wait_for_loaded(peerB)
|
|
370
392
|
|
|
371
393
|
# Create a new layer from peerA
|
|
372
394
|
peerA.get_by_role("button", name="Manage layers").click()
|
|
@@ -396,7 +418,7 @@ def test_should_sync_datalayers(new_page, asgi_live_server, tilelayer):
|
|
|
396
418
|
peerA.locator("#map").click()
|
|
397
419
|
|
|
398
420
|
# Make sure this new marker is in Layer 2 for peerB
|
|
399
|
-
# Show features for this layer in the
|
|
421
|
+
# Show features for this layer in the browser.
|
|
400
422
|
peerB.locator("summary").filter(has_text="Layer 2").click()
|
|
401
423
|
expect(peerB.locator("li").filter(has_text="Layer 2")).to_be_visible()
|
|
402
424
|
peerB.locator(".panel.left").get_by_role("button", name="Show/hide layer").nth(
|
|
@@ -431,7 +453,9 @@ def test_should_sync_datalayers(new_page, asgi_live_server, tilelayer):
|
|
|
431
453
|
|
|
432
454
|
|
|
433
455
|
@pytest.mark.xdist_group(name="websockets")
|
|
434
|
-
def test_should_sync_datalayers_delete(
|
|
456
|
+
def test_should_sync_datalayers_delete(
|
|
457
|
+
new_page, asgi_live_server, tilelayer, wait_for_loaded
|
|
458
|
+
):
|
|
435
459
|
map = MapFactory(name="sync", edit_status=Map.ANONYMOUS)
|
|
436
460
|
map.settings["properties"]["syncEnabled"] = True
|
|
437
461
|
map.save()
|
|
@@ -471,8 +495,10 @@ def test_should_sync_datalayers_delete(new_page, asgi_live_server, tilelayer):
|
|
|
471
495
|
# Create two tabs
|
|
472
496
|
peerA = new_page("Page A")
|
|
473
497
|
peerA.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
|
|
498
|
+
wait_for_loaded(peerA)
|
|
474
499
|
peerB = new_page("Page B")
|
|
475
500
|
peerB.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
|
|
501
|
+
wait_for_loaded(peerB)
|
|
476
502
|
|
|
477
503
|
peerA.get_by_role("button", name="Open browser").click()
|
|
478
504
|
expect(peerA.locator(".panel").get_by_text("datalayer 1")).to_be_visible()
|
|
@@ -494,10 +520,13 @@ def test_should_sync_datalayers_delete(new_page, asgi_live_server, tilelayer):
|
|
|
494
520
|
|
|
495
521
|
|
|
496
522
|
@pytest.mark.xdist_group(name="websockets")
|
|
497
|
-
def test_create_and_sync_map(
|
|
523
|
+
def test_create_and_sync_map(
|
|
524
|
+
new_page, asgi_live_server, tilelayer, login, user, wait_for_loaded
|
|
525
|
+
):
|
|
498
526
|
# Create a syncable map with peerA
|
|
499
527
|
peerA = login(user, prefix="Page A")
|
|
500
528
|
peerA.goto(f"{asgi_live_server.url}/en/map/new/")
|
|
529
|
+
wait_for_loaded(peerA)
|
|
501
530
|
peerA.get_by_role("button", name="Map advanced properties").click()
|
|
502
531
|
expect(peerA.get_by_text("Real-time collaboration", exact=True)).to_be_hidden()
|
|
503
532
|
with peerA.expect_response(re.compile("./map/create/.*")):
|
|
@@ -517,6 +546,7 @@ def test_create_and_sync_map(new_page, asgi_live_server, tilelayer, login, user)
|
|
|
517
546
|
# Open map and go to edit mode with peer B
|
|
518
547
|
peerB = new_page("Page B")
|
|
519
548
|
peerB.goto(peerA.url)
|
|
549
|
+
wait_for_loaded(peerB)
|
|
520
550
|
peerB.get_by_role("button", name="Edit").click()
|
|
521
551
|
|
|
522
552
|
# Create a marker from peerA
|
|
@@ -527,8 +557,10 @@ def test_create_and_sync_map(new_page, asgi_live_server, tilelayer, login, user)
|
|
|
527
557
|
|
|
528
558
|
# Add a marker from peer A
|
|
529
559
|
peerA.get_by_role("button", name="Edit").click()
|
|
560
|
+
peerA.wait_for_timeout(300) # Time for the animation to finish
|
|
530
561
|
peerA.get_by_title("Draw a marker").click()
|
|
531
562
|
peerA.locator("#map").click(position={"x": 220, "y": 220})
|
|
563
|
+
peerA.wait_for_timeout(300) # Time for the panel animation to finish
|
|
532
564
|
expect(markersA).to_have_count(1)
|
|
533
565
|
expect(markersB).to_have_count(1)
|
|
534
566
|
|
|
@@ -554,6 +586,7 @@ def test_create_and_sync_map(new_page, asgi_live_server, tilelayer, login, user)
|
|
|
554
586
|
# Add a marker from peer B
|
|
555
587
|
peerB.get_by_title("Draw a marker").click()
|
|
556
588
|
peerB.locator("#map").click(position={"x": 200, "y": 200})
|
|
589
|
+
peerA.wait_for_timeout(300) # Time for the panel animation to finish
|
|
557
590
|
expect(markersB).to_have_count(2)
|
|
558
591
|
expect(markersA).to_have_count(1)
|
|
559
592
|
with peerB.expect_response(re.compile("./datalayer/update/.*")):
|
|
@@ -566,7 +599,9 @@ def test_create_and_sync_map(new_page, asgi_live_server, tilelayer, login, user)
|
|
|
566
599
|
|
|
567
600
|
|
|
568
601
|
@pytest.mark.xdist_group(name="websockets")
|
|
569
|
-
def test_saved_datalayer_are_not_duplicated(
|
|
602
|
+
def test_saved_datalayer_are_not_duplicated(
|
|
603
|
+
new_page, asgi_live_server, tilelayer, wait_for_loaded
|
|
604
|
+
):
|
|
570
605
|
map = MapFactory(name="sync", edit_status=Map.ANONYMOUS)
|
|
571
606
|
map.settings["properties"]["syncEnabled"] = True
|
|
572
607
|
map.save()
|
|
@@ -574,6 +609,7 @@ def test_saved_datalayer_are_not_duplicated(new_page, asgi_live_server, tilelaye
|
|
|
574
609
|
# Create one tab
|
|
575
610
|
peerA = new_page("Page A")
|
|
576
611
|
peerA.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
|
|
612
|
+
wait_for_loaded(peerA)
|
|
577
613
|
# Create a new datalayer
|
|
578
614
|
peerA.get_by_title("Manage layers").click()
|
|
579
615
|
peerA.get_by_title("Add a layer").click()
|
|
@@ -586,6 +622,7 @@ def test_saved_datalayer_are_not_duplicated(new_page, asgi_live_server, tilelaye
|
|
|
586
622
|
# Now load the map from another tab
|
|
587
623
|
peerB = new_page("Page B")
|
|
588
624
|
peerB.goto(peerA.url)
|
|
625
|
+
wait_for_loaded(peerB)
|
|
589
626
|
peerB.get_by_role("button", name="Open browser").click()
|
|
590
627
|
expect(peerB.get_by_text("Layer 1")).to_be_visible()
|
|
591
628
|
peerB.get_by_role("button", name="Edit").click()
|
|
@@ -594,7 +631,9 @@ def test_saved_datalayer_are_not_duplicated(new_page, asgi_live_server, tilelaye
|
|
|
594
631
|
|
|
595
632
|
|
|
596
633
|
@pytest.mark.xdist_group(name="websockets")
|
|
597
|
-
def test_should_sync_saved_status(
|
|
634
|
+
def test_should_sync_saved_status(
|
|
635
|
+
new_page, asgi_live_server, tilelayer, wait_for_loaded
|
|
636
|
+
):
|
|
598
637
|
map = MapFactory(name="sync", edit_status=Map.ANONYMOUS)
|
|
599
638
|
map.settings["properties"]["syncEnabled"] = True
|
|
600
639
|
map.save()
|
|
@@ -602,8 +641,10 @@ def test_should_sync_saved_status(new_page, asgi_live_server, tilelayer):
|
|
|
602
641
|
# Create two tabs
|
|
603
642
|
peerA = new_page("Page A")
|
|
604
643
|
peerA.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
|
|
644
|
+
wait_for_loaded(peerA)
|
|
605
645
|
peerB = new_page("Page B")
|
|
606
646
|
peerB.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
|
|
647
|
+
wait_for_loaded(peerB)
|
|
607
648
|
|
|
608
649
|
# Create a new marker from peerA
|
|
609
650
|
peerA.get_by_title("Draw a marker").click()
|
|
@@ -637,7 +678,9 @@ def test_should_sync_saved_status(new_page, asgi_live_server, tilelayer):
|
|
|
637
678
|
|
|
638
679
|
|
|
639
680
|
@pytest.mark.xdist_group(name="websockets")
|
|
640
|
-
def test_should_sync_line_on_escape(
|
|
681
|
+
def test_should_sync_line_on_escape(
|
|
682
|
+
new_page, asgi_live_server, tilelayer, wait_for_loaded
|
|
683
|
+
):
|
|
641
684
|
map = MapFactory(name="sync", edit_status=Map.ANONYMOUS)
|
|
642
685
|
map.settings["properties"]["syncEnabled"] = True
|
|
643
686
|
map.save()
|
|
@@ -645,8 +688,10 @@ def test_should_sync_line_on_escape(new_page, asgi_live_server, tilelayer):
|
|
|
645
688
|
# Create two tabs
|
|
646
689
|
peerA = new_page("Page A")
|
|
647
690
|
peerA.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
|
|
691
|
+
wait_for_loaded(peerA)
|
|
648
692
|
peerB = new_page("Page B")
|
|
649
693
|
peerB.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
|
|
694
|
+
wait_for_loaded(peerB)
|
|
650
695
|
|
|
651
696
|
# Create a new marker from peerA
|
|
652
697
|
peerA.get_by_title("Draw a polyline").click()
|
|
@@ -660,7 +705,7 @@ def test_should_sync_line_on_escape(new_page, asgi_live_server, tilelayer):
|
|
|
660
705
|
|
|
661
706
|
@pytest.mark.xdist_group(name="websockets")
|
|
662
707
|
def test_should_sync_datalayer_clear(
|
|
663
|
-
new_page, asgi_live_server, tilelayer, map, datalayer
|
|
708
|
+
new_page, asgi_live_server, tilelayer, map, datalayer, wait_for_loaded
|
|
664
709
|
):
|
|
665
710
|
map.settings["properties"]["syncEnabled"] = True
|
|
666
711
|
map.edit_status = Map.ANONYMOUS
|
|
@@ -669,8 +714,10 @@ def test_should_sync_datalayer_clear(
|
|
|
669
714
|
# Create two tabs
|
|
670
715
|
peerA = new_page("Page A")
|
|
671
716
|
peerA.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
|
|
717
|
+
wait_for_loaded(peerA)
|
|
672
718
|
peerB = new_page("Page B")
|
|
673
719
|
peerB.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
|
|
720
|
+
wait_for_loaded(peerB)
|
|
674
721
|
expect(peerA.locator(".leaflet-marker-icon")).to_have_count(1)
|
|
675
722
|
expect(peerB.locator(".leaflet-marker-icon")).to_have_count(1)
|
|
676
723
|
|
|
@@ -689,7 +736,9 @@ def test_should_sync_datalayer_clear(
|
|
|
689
736
|
|
|
690
737
|
|
|
691
738
|
@pytest.mark.xdist_group(name="websockets")
|
|
692
|
-
def test_should_save_remote_dirty_datalayers(
|
|
739
|
+
def test_should_save_remote_dirty_datalayers(
|
|
740
|
+
new_page, asgi_live_server, tilelayer, wait_for_loaded
|
|
741
|
+
):
|
|
693
742
|
map = MapFactory(name="sync", edit_status=Map.ANONYMOUS)
|
|
694
743
|
map.settings["properties"]["syncEnabled"] = True
|
|
695
744
|
map.save()
|
|
@@ -699,8 +748,10 @@ def test_should_save_remote_dirty_datalayers(new_page, asgi_live_server, tilelay
|
|
|
699
748
|
# Create two tabs
|
|
700
749
|
peerA = new_page("Page A")
|
|
701
750
|
peerA.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
|
|
751
|
+
wait_for_loaded(peerA)
|
|
702
752
|
peerB = new_page("Page B")
|
|
703
753
|
peerB.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
|
|
754
|
+
wait_for_loaded(peerB)
|
|
704
755
|
|
|
705
756
|
# Create a new layer from peerA
|
|
706
757
|
peerA.get_by_role("button", name="Manage layers").click()
|
umap/tests/test_dashboard.py
CHANGED
|
@@ -68,7 +68,7 @@ def test_user_dashboard_display_user_team_maps(client, map, team, user, share_st
|
|
|
68
68
|
|
|
69
69
|
def test_user_dashboard_display_user_maps_distinct(client, map):
|
|
70
70
|
# cf https://github.com/umap-project/umap/issues/1325
|
|
71
|
-
anonymap = MapFactory(name="Map
|
|
71
|
+
anonymap = MapFactory(name="Map without owner should not appear")
|
|
72
72
|
user1 = UserFactory(username="user1")
|
|
73
73
|
user2 = UserFactory(username="user2")
|
|
74
74
|
map.editors.add(user1)
|
|
@@ -99,3 +99,13 @@ def test_user_dashboard_search_empty(client, map):
|
|
|
99
99
|
body = response.content.decode()
|
|
100
100
|
assert map.name not in body
|
|
101
101
|
assert "No map found." in body
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def test_user_dashboard_filter_by_tag(client, map):
|
|
105
|
+
new_map = MapFactory(name="A map about bicycle", owner=map.owner, tags=["cycling"])
|
|
106
|
+
client.login(username=map.owner.username, password="123123")
|
|
107
|
+
response = client.get(f"{reverse('user_dashboard')}?tags=cycling")
|
|
108
|
+
assert response.status_code == 200
|
|
109
|
+
body = response.content.decode()
|
|
110
|
+
assert map.name not in body
|
|
111
|
+
assert new_map.name in body
|
umap/tests/test_statics.py
CHANGED
|
@@ -32,9 +32,9 @@ def test_collectstatic_ran_successfully_with_hashes(settings, staticfiles):
|
|
|
32
32
|
assert "hash" in json_manifest.keys()
|
|
33
33
|
assert "umap/base.css" in json_manifest["paths"]
|
|
34
34
|
# Hash + the dot ("umap/base.<hash>.css").
|
|
35
|
-
|
|
35
|
+
md5_hash_length = 12 + 1
|
|
36
36
|
# The value of the manifest must contain the hash (length).
|
|
37
37
|
assert (
|
|
38
38
|
len(json_manifest["paths"]["umap/base.css"])
|
|
39
|
-
== len("umap/base.css") +
|
|
39
|
+
== len("umap/base.css") + md5_hash_length
|
|
40
40
|
)
|
umap/tests/test_utils.py
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
|
+
import stat
|
|
1
2
|
from pathlib import Path
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
import pytest
|
|
4
5
|
|
|
6
|
+
from umap.utils import gzip_file, normalize_string
|
|
5
7
|
|
|
6
|
-
|
|
8
|
+
|
|
9
|
+
def test_gzip_file(settings):
|
|
10
|
+
settings.FILE_UPLOAD_PERMISSIONS = 0o666
|
|
7
11
|
# Let's use any old file so we can check that the date of the gzip file is set.
|
|
8
12
|
src = Path(__file__).parent / "settings.py"
|
|
9
13
|
dest = Path("/tmp/test_settings.py.gz")
|
|
@@ -12,3 +16,16 @@ def test_gzip_file():
|
|
|
12
16
|
dest_stat = dest.stat()
|
|
13
17
|
dest.unlink()
|
|
14
18
|
assert src_stat.st_mtime == dest_stat.st_mtime
|
|
19
|
+
assert stat.filemode(dest_stat.st_mode) == "-rw-rw-rw-"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@pytest.mark.parametrize(
|
|
23
|
+
"input,output",
|
|
24
|
+
(
|
|
25
|
+
("Vélo", "velo"),
|
|
26
|
+
("Éducation", "education"),
|
|
27
|
+
("stävänger", "stavanger"),
|
|
28
|
+
),
|
|
29
|
+
)
|
|
30
|
+
def test_normalize_string(input, output):
|
|
31
|
+
assert normalize_string(input) == output
|
umap/tests/test_views.py
CHANGED
umap/urls.py
CHANGED
|
@@ -124,6 +124,7 @@ i18n_urls += decorated_patterns(
|
|
|
124
124
|
path("me/teams", views.UserTeams.as_view(), name="user_teams"),
|
|
125
125
|
path("me/templates", views.UserTemplates.as_view(), name="user_templates"),
|
|
126
126
|
path("team/create/", views.TeamNew.as_view(), name="team_new"),
|
|
127
|
+
path("whoami", views.WhoAmI.as_view(), name="whoami"),
|
|
127
128
|
)
|
|
128
129
|
|
|
129
130
|
if settings.UMAP_ALLOW_EDIT_PROFILE:
|
umap/utils.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import gzip
|
|
2
2
|
import json
|
|
3
3
|
import os
|
|
4
|
+
import unicodedata
|
|
4
5
|
from pathlib import Path
|
|
5
6
|
|
|
6
7
|
from django.conf import settings
|
|
@@ -79,7 +80,7 @@ def get_uri_template(urlname, args=None, prefix="", module=None):
|
|
|
79
80
|
result, params = possibility[0]
|
|
80
81
|
return _convert(result, params)
|
|
81
82
|
else:
|
|
82
|
-
# If there are
|
|
83
|
+
# If there are optional arguments passed, use them to try to find
|
|
83
84
|
# the correct pattern.
|
|
84
85
|
# First, we need to build a list with all the arguments
|
|
85
86
|
seen_params = []
|
|
@@ -149,6 +150,7 @@ def gzip_file(from_path, to_path):
|
|
|
149
150
|
with gzip.open(to_path, "wb") as f_out:
|
|
150
151
|
f_out.writelines(f_in)
|
|
151
152
|
os.utime(to_path, ns=(stat.st_mtime_ns, stat.st_mtime_ns))
|
|
153
|
+
os.chmod(to_path, settings.FILE_UPLOAD_PERMISSIONS)
|
|
152
154
|
|
|
153
155
|
|
|
154
156
|
def is_ajax(request):
|
|
@@ -222,3 +224,8 @@ def collect_pictograms():
|
|
|
222
224
|
}
|
|
223
225
|
|
|
224
226
|
return pictograms
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def normalize_string(s):
|
|
230
|
+
n = unicodedata.normalize("NFKD", str(s))
|
|
231
|
+
return "".join([c for c in n if not unicodedata.combining(c)]).lower()
|
umap/views.py
CHANGED
|
@@ -585,6 +585,11 @@ class SessionMixin:
|
|
|
585
585
|
}
|
|
586
586
|
|
|
587
587
|
|
|
588
|
+
class WhoAmI(SessionMixin, View):
|
|
589
|
+
def get(self, request, *args, **kwargs):
|
|
590
|
+
return simple_json_response(user=self.get_user_data())
|
|
591
|
+
|
|
592
|
+
|
|
588
593
|
class FormLessEditMixin:
|
|
589
594
|
http_method_names = [
|
|
590
595
|
"post",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: umap-project
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.4.0
|
|
4
4
|
Summary: Create maps with OpenStreetMap layers in a minute and embed them in your site.
|
|
5
5
|
Author-email: Yohan Boniface <yb@enix.org>
|
|
6
6
|
Maintainer-email: David Larlet <david@larlet.fr>
|
|
@@ -19,42 +19,42 @@ Requires-Python: >=3.10
|
|
|
19
19
|
Requires-Dist: django-agnocomplete==2.2.0
|
|
20
20
|
Requires-Dist: django-environ==0.12.0
|
|
21
21
|
Requires-Dist: django-probes==1.7.0
|
|
22
|
-
Requires-Dist: django==5.2.
|
|
23
|
-
Requires-Dist: pillow==
|
|
24
|
-
Requires-Dist: psycopg==3.2.
|
|
22
|
+
Requires-Dist: django==5.2.7
|
|
23
|
+
Requires-Dist: pillow==12.0.0
|
|
24
|
+
Requires-Dist: psycopg==3.2.12
|
|
25
25
|
Requires-Dist: rcssmin==1.2.1
|
|
26
26
|
Requires-Dist: requests==2.32.5
|
|
27
|
-
Requires-Dist: rjsmin==1.2.
|
|
27
|
+
Requires-Dist: rjsmin==1.2.5
|
|
28
28
|
Requires-Dist: social-auth-app-django==5.4.3
|
|
29
29
|
Requires-Dist: social-auth-core==4.5.6
|
|
30
30
|
Provides-Extra: dev
|
|
31
31
|
Requires-Dist: djlint==1.36.4; extra == 'dev'
|
|
32
|
-
Requires-Dist: hatch==1.14.
|
|
33
|
-
Requires-Dist: isort==
|
|
34
|
-
Requires-Dist: mkdocs-material==9.6.
|
|
32
|
+
Requires-Dist: hatch==1.14.2; extra == 'dev'
|
|
33
|
+
Requires-Dist: isort==7.0.0; extra == 'dev'
|
|
34
|
+
Requires-Dist: mkdocs-material==9.6.23; extra == 'dev'
|
|
35
35
|
Requires-Dist: mkdocs-static-i18n==1.3.0; extra == 'dev'
|
|
36
36
|
Requires-Dist: mkdocs==1.6.1; extra == 'dev'
|
|
37
37
|
Requires-Dist: pymdown-extensions==10.16.1; extra == 'dev'
|
|
38
|
-
Requires-Dist: ruff==0.
|
|
38
|
+
Requires-Dist: ruff==0.14.0; extra == 'dev'
|
|
39
39
|
Requires-Dist: vermin==1.6.0; extra == 'dev'
|
|
40
40
|
Provides-Extra: docker
|
|
41
|
-
Requires-Dist: uvicorn==0.
|
|
41
|
+
Requires-Dist: uvicorn==0.38.0; extra == 'docker'
|
|
42
42
|
Provides-Extra: s3
|
|
43
43
|
Requires-Dist: django-storages[s3]==1.14.6; extra == 's3'
|
|
44
44
|
Provides-Extra: sync
|
|
45
|
-
Requires-Dist: pydantic==2.
|
|
45
|
+
Requires-Dist: pydantic==2.12.3; extra == 'sync'
|
|
46
46
|
Requires-Dist: redis==6.4.0; extra == 'sync'
|
|
47
47
|
Requires-Dist: websockets==15.0.1; extra == 'sync'
|
|
48
48
|
Provides-Extra: test
|
|
49
49
|
Requires-Dist: daphne==4.2.1; extra == 'test'
|
|
50
50
|
Requires-Dist: factory-boy==3.3.3; extra == 'test'
|
|
51
|
-
Requires-Dist: moto[s3]==5.1.
|
|
51
|
+
Requires-Dist: moto[s3]==5.1.14; extra == 'test'
|
|
52
52
|
Requires-Dist: playwright>=1.39; extra == 'test'
|
|
53
53
|
Requires-Dist: pytest-django==4.11.1; extra == 'test'
|
|
54
|
-
Requires-Dist: pytest-playwright==0.7.
|
|
55
|
-
Requires-Dist: pytest-rerunfailures==
|
|
54
|
+
Requires-Dist: pytest-playwright==0.7.1; extra == 'test'
|
|
55
|
+
Requires-Dist: pytest-rerunfailures==16.1; extra == 'test'
|
|
56
56
|
Requires-Dist: pytest-xdist<4,>=3.5.0; extra == 'test'
|
|
57
|
-
Requires-Dist: pytest==8.4.
|
|
57
|
+
Requires-Dist: pytest==8.4.2; extra == 'test'
|
|
58
58
|
Description-Content-Type: text/markdown
|
|
59
59
|
|
|
60
60
|
[](https://matrix.to/#/#umap:matrix.org)
|