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.
Files changed (239) hide show
  1. umap/__init__.py +1 -1
  2. umap/context_processors.py +4 -1
  3. umap/locale/cs_CZ/LC_MESSAGES/django.mo +0 -0
  4. umap/locale/cs_CZ/LC_MESSAGES/django.po +43 -33
  5. umap/locale/da/LC_MESSAGES/django.mo +0 -0
  6. umap/locale/da/LC_MESSAGES/django.po +43 -33
  7. umap/locale/de/LC_MESSAGES/django.mo +0 -0
  8. umap/locale/de/LC_MESSAGES/django.po +35 -29
  9. umap/locale/el/LC_MESSAGES/django.mo +0 -0
  10. umap/locale/el/LC_MESSAGES/django.po +35 -29
  11. umap/locale/en/LC_MESSAGES/django.po +47 -41
  12. umap/locale/es/LC_MESSAGES/django.mo +0 -0
  13. umap/locale/es/LC_MESSAGES/django.po +43 -33
  14. umap/locale/et/LC_MESSAGES/django.mo +0 -0
  15. umap/locale/et/LC_MESSAGES/django.po +58 -54
  16. umap/locale/eu/LC_MESSAGES/django.mo +0 -0
  17. umap/locale/eu/LC_MESSAGES/django.po +43 -33
  18. umap/locale/fa_IR/LC_MESSAGES/django.mo +0 -0
  19. umap/locale/fa_IR/LC_MESSAGES/django.po +43 -33
  20. umap/locale/fr/LC_MESSAGES/django.mo +0 -0
  21. umap/locale/fr/LC_MESSAGES/django.po +36 -30
  22. umap/locale/gl/LC_MESSAGES/django.mo +0 -0
  23. umap/locale/gl/LC_MESSAGES/django.po +43 -33
  24. umap/locale/hu/LC_MESSAGES/django.mo +0 -0
  25. umap/locale/hu/LC_MESSAGES/django.po +35 -29
  26. umap/locale/is/LC_MESSAGES/django.mo +0 -0
  27. umap/locale/is/LC_MESSAGES/django.po +43 -33
  28. umap/locale/it/LC_MESSAGES/django.mo +0 -0
  29. umap/locale/it/LC_MESSAGES/django.po +43 -33
  30. umap/locale/nl/LC_MESSAGES/django.mo +0 -0
  31. umap/locale/nl/LC_MESSAGES/django.po +35 -29
  32. umap/locale/pl/LC_MESSAGES/django.mo +0 -0
  33. umap/locale/pl/LC_MESSAGES/django.po +114 -103
  34. umap/locale/pt/LC_MESSAGES/django.mo +0 -0
  35. umap/locale/pt/LC_MESSAGES/django.po +43 -33
  36. umap/locale/th_TH/LC_MESSAGES/django.mo +0 -0
  37. umap/locale/th_TH/LC_MESSAGES/django.po +310 -109
  38. umap/locale/zh_TW/LC_MESSAGES/django.mo +0 -0
  39. umap/locale/zh_TW/LC_MESSAGES/django.po +80 -70
  40. umap/management/commands/switch_user.py +2 -2
  41. umap/migrations/0018_datalayer_uuid.py +1 -1
  42. umap/models.py +7 -3
  43. umap/settings/local.py.sample +1 -1
  44. umap/static/umap/base.css +89 -32
  45. umap/static/umap/content.css +129 -33
  46. umap/static/umap/css/bar.css +82 -20
  47. umap/static/umap/css/browser.css +163 -0
  48. umap/static/umap/css/contextmenu.css +15 -0
  49. umap/static/umap/css/dialog.css +36 -16
  50. umap/static/umap/css/form.css +123 -33
  51. umap/static/umap/css/icon.css +46 -3
  52. umap/static/umap/css/panel.css +7 -3
  53. umap/static/umap/css/popup.css +34 -8
  54. umap/static/umap/css/tooltip.css +8 -4
  55. umap/static/umap/img/16-white.svg +26 -8
  56. umap/static/umap/img/16.svg +1 -1
  57. umap/static/umap/img/source/16-white.svg +36 -18
  58. umap/static/umap/img/source/16.svg +1 -1
  59. umap/static/umap/js/components/alerts/alert.css +69 -31
  60. umap/static/umap/js/components/alerts/alert.js +20 -2
  61. umap/static/umap/js/components/base.js +1 -1
  62. umap/static/umap/js/modules/browser.js +69 -61
  63. umap/static/umap/js/modules/caption.js +10 -7
  64. umap/static/umap/js/modules/data/features.js +85 -60
  65. umap/static/umap/js/modules/data/fields.js +446 -0
  66. umap/static/umap/js/modules/data/layer.js +78 -184
  67. umap/static/umap/js/modules/domutils.js +109 -0
  68. umap/static/umap/js/modules/filters.js +780 -0
  69. umap/static/umap/js/modules/form/builder.js +8 -5
  70. umap/static/umap/js/modules/form/fields.js +111 -221
  71. umap/static/umap/js/modules/formatter.js +24 -1
  72. umap/static/umap/js/modules/help.js +4 -3
  73. umap/static/umap/js/modules/i18n.js +1 -1
  74. umap/static/umap/js/modules/importer.js +1 -1
  75. umap/static/umap/js/modules/importers/opendata.js +15 -0
  76. umap/static/umap/js/modules/importers/openrouteservice.js +6 -1
  77. umap/static/umap/js/modules/managers.js +2 -2
  78. umap/static/umap/js/modules/permissions.js +39 -31
  79. umap/static/umap/js/modules/rendering/controls.js +11 -9
  80. umap/static/umap/js/modules/rendering/icon.js +3 -8
  81. umap/static/umap/js/modules/rendering/layers/base.js +1 -1
  82. umap/static/umap/js/modules/rendering/layers/classified.js +18 -11
  83. umap/static/umap/js/modules/rendering/layers/cluster.js +5 -3
  84. umap/static/umap/js/modules/rendering/layers/heat.js +27 -21
  85. umap/static/umap/js/modules/rendering/template.js +50 -23
  86. umap/static/umap/js/modules/rendering/ui.js +29 -23
  87. umap/static/umap/js/modules/rules.js +38 -44
  88. umap/static/umap/js/modules/schema.js +3 -6
  89. umap/static/umap/js/modules/share.js +5 -4
  90. umap/static/umap/js/modules/tableeditor.js +50 -38
  91. umap/static/umap/js/modules/templates.js +2 -3
  92. umap/static/umap/js/modules/ui/bar.js +55 -23
  93. umap/static/umap/js/modules/ui/dialog.js +38 -27
  94. umap/static/umap/js/modules/ui/panel.js +23 -8
  95. umap/static/umap/js/modules/ui/tooltip.js +6 -5
  96. umap/static/umap/js/modules/umap.js +151 -56
  97. umap/static/umap/js/modules/utils.js +24 -2
  98. umap/static/umap/js/umap.core.js +1 -110
  99. umap/static/umap/locale/am_ET.js +52 -17
  100. umap/static/umap/locale/am_ET.json +52 -17
  101. umap/static/umap/locale/ar.js +52 -17
  102. umap/static/umap/locale/ar.json +52 -17
  103. umap/static/umap/locale/ast.js +52 -17
  104. umap/static/umap/locale/ast.json +52 -17
  105. umap/static/umap/locale/bg.js +52 -17
  106. umap/static/umap/locale/bg.json +52 -17
  107. umap/static/umap/locale/br.js +48 -22
  108. umap/static/umap/locale/br.json +48 -22
  109. umap/static/umap/locale/ca.js +52 -17
  110. umap/static/umap/locale/ca.json +52 -17
  111. umap/static/umap/locale/cs_CZ.js +52 -17
  112. umap/static/umap/locale/cs_CZ.json +52 -17
  113. umap/static/umap/locale/da.js +54 -17
  114. umap/static/umap/locale/da.json +54 -17
  115. umap/static/umap/locale/de.js +51 -16
  116. umap/static/umap/locale/de.json +51 -16
  117. umap/static/umap/locale/el.js +52 -17
  118. umap/static/umap/locale/el.json +52 -17
  119. umap/static/umap/locale/en.js +53 -16
  120. umap/static/umap/locale/en.json +53 -16
  121. umap/static/umap/locale/en_US.json +52 -17
  122. umap/static/umap/locale/es.js +54 -17
  123. umap/static/umap/locale/es.json +54 -17
  124. umap/static/umap/locale/et.js +91 -56
  125. umap/static/umap/locale/et.json +91 -56
  126. umap/static/umap/locale/eu.js +84 -49
  127. umap/static/umap/locale/eu.json +84 -49
  128. umap/static/umap/locale/fa_IR.js +52 -17
  129. umap/static/umap/locale/fa_IR.json +52 -17
  130. umap/static/umap/locale/fi.js +52 -17
  131. umap/static/umap/locale/fi.json +52 -17
  132. umap/static/umap/locale/fr.js +53 -16
  133. umap/static/umap/locale/fr.json +53 -16
  134. umap/static/umap/locale/gl.js +52 -17
  135. umap/static/umap/locale/gl.json +52 -17
  136. umap/static/umap/locale/he.js +52 -17
  137. umap/static/umap/locale/he.json +52 -17
  138. umap/static/umap/locale/hr.js +52 -17
  139. umap/static/umap/locale/hr.json +52 -17
  140. umap/static/umap/locale/hu.js +59 -24
  141. umap/static/umap/locale/hu.json +59 -24
  142. umap/static/umap/locale/id.js +52 -17
  143. umap/static/umap/locale/id.json +52 -17
  144. umap/static/umap/locale/is.js +52 -17
  145. umap/static/umap/locale/is.json +52 -17
  146. umap/static/umap/locale/it.js +52 -17
  147. umap/static/umap/locale/it.json +52 -17
  148. umap/static/umap/locale/ja.js +52 -17
  149. umap/static/umap/locale/ja.json +52 -17
  150. umap/static/umap/locale/ko.js +52 -17
  151. umap/static/umap/locale/ko.json +52 -17
  152. umap/static/umap/locale/lt.js +52 -17
  153. umap/static/umap/locale/lt.json +52 -17
  154. umap/static/umap/locale/ms.js +52 -17
  155. umap/static/umap/locale/ms.json +52 -17
  156. umap/static/umap/locale/nl.js +52 -17
  157. umap/static/umap/locale/nl.json +52 -17
  158. umap/static/umap/locale/no.js +52 -17
  159. umap/static/umap/locale/no.json +52 -17
  160. umap/static/umap/locale/pl.js +53 -17
  161. umap/static/umap/locale/pl.json +53 -17
  162. umap/static/umap/locale/pl_PL.json +52 -17
  163. umap/static/umap/locale/pt.js +52 -17
  164. umap/static/umap/locale/pt.json +52 -17
  165. umap/static/umap/locale/pt_BR.js +52 -17
  166. umap/static/umap/locale/pt_BR.json +52 -17
  167. umap/static/umap/locale/pt_PT.js +52 -17
  168. umap/static/umap/locale/pt_PT.json +52 -17
  169. umap/static/umap/locale/ro.js +52 -17
  170. umap/static/umap/locale/ro.json +52 -17
  171. umap/static/umap/locale/ru.js +52 -17
  172. umap/static/umap/locale/ru.json +52 -17
  173. umap/static/umap/locale/si.js +1 -1
  174. umap/static/umap/locale/si.json +1 -1
  175. umap/static/umap/locale/sk_SK.js +52 -17
  176. umap/static/umap/locale/sk_SK.json +52 -17
  177. umap/static/umap/locale/sl.js +52 -17
  178. umap/static/umap/locale/sl.json +52 -17
  179. umap/static/umap/locale/sr.js +52 -17
  180. umap/static/umap/locale/sr.json +52 -17
  181. umap/static/umap/locale/sv.js +52 -17
  182. umap/static/umap/locale/sv.json +52 -17
  183. umap/static/umap/locale/th_TH.js +52 -17
  184. umap/static/umap/locale/th_TH.json +52 -17
  185. umap/static/umap/locale/tr.js +52 -17
  186. umap/static/umap/locale/tr.json +52 -17
  187. umap/static/umap/locale/uk_UA.js +52 -17
  188. umap/static/umap/locale/uk_UA.json +52 -17
  189. umap/static/umap/locale/vi.js +52 -17
  190. umap/static/umap/locale/vi.json +52 -17
  191. umap/static/umap/locale/vi_VN.json +52 -17
  192. umap/static/umap/locale/zh.js +52 -17
  193. umap/static/umap/locale/zh.json +52 -17
  194. umap/static/umap/locale/zh_CN.json +52 -17
  195. umap/static/umap/locale/zh_TW.Big5.json +52 -17
  196. umap/static/umap/locale/zh_TW.js +52 -16
  197. umap/static/umap/locale/zh_TW.json +52 -16
  198. umap/static/umap/map.css +63 -226
  199. umap/static/umap/unittests/utils.js +18 -0
  200. umap/static/umap/vars.css +23 -5
  201. umap/templates/umap/components/alerts/alert.html +32 -29
  202. umap/templates/umap/css.html +2 -1
  203. umap/templates/umap/login_popup_end.html +18 -9
  204. umap/templates/umap/user_map_table.html +7 -2
  205. umap/tests/integration/conftest.py +10 -6
  206. umap/tests/integration/test_anonymous_owned_map.py +90 -37
  207. umap/tests/integration/test_basics.py +25 -1
  208. umap/tests/integration/test_browser.py +37 -0
  209. umap/tests/integration/test_conditional_rules.py +107 -52
  210. umap/tests/integration/test_draw_polygon.py +6 -0
  211. umap/tests/integration/test_draw_polyline.py +11 -0
  212. umap/tests/integration/test_edit_marker.py +1 -1
  213. umap/tests/integration/test_export_map.py +19 -0
  214. umap/tests/integration/test_fields.py +541 -0
  215. umap/tests/integration/test_filters.py +616 -0
  216. umap/tests/integration/test_iframe.py +1 -1
  217. umap/tests/integration/test_import.py +38 -42
  218. umap/tests/integration/test_map_preview.py +1 -1
  219. umap/tests/integration/test_picto.py +1 -1
  220. umap/tests/integration/test_popup.py +31 -0
  221. umap/tests/integration/test_remote_data.py +60 -4
  222. umap/tests/integration/test_save.py +1 -1
  223. umap/tests/integration/test_share.py +4 -4
  224. umap/tests/integration/test_tableeditor.py +31 -7
  225. umap/tests/integration/test_websocket_sync.py +71 -20
  226. umap/tests/test_dashboard.py +11 -1
  227. umap/tests/test_statics.py +2 -2
  228. umap/tests/test_utils.py +19 -2
  229. umap/tests/test_views.py +1 -1
  230. umap/urls.py +1 -0
  231. umap/utils.py +8 -1
  232. umap/views.py +5 -0
  233. {umap_project-3.3.6.dist-info → umap_project-3.4.0.dist-info}/METADATA +15 -15
  234. {umap_project-3.3.6.dist-info → umap_project-3.4.0.dist-info}/RECORD +237 -233
  235. umap/static/umap/js/modules/facets.js +0 -164
  236. umap/tests/integration/test_facets_browser.py +0 -279
  237. {umap_project-3.3.6.dist-info → umap_project-3.4.0.dist-info}/WHEEL +0 -0
  238. {umap_project-3.3.6.dist-info → umap_project-3.4.0.dist-info}/entry_points.txt +0 -0
  239. {umap_project-3.3.6.dist-info → umap_project-3.4.0.dist-info}/licenses/LICENSE +0 -0
@@ -137,6 +137,6 @@ def test_add_property_from_feature_properties_panel(
137
137
  page.goto(f"{live_server.url}{openmap.get_absolute_url()}?edit")
138
138
  page.locator(".leaflet-marker-icon").click(modifiers=["Shift"])
139
139
  page.get_by_role("button", name="Add a new field").click()
140
- page.locator('input[name="prompt"]').fill("newprop")
140
+ page.locator('input[name="key"]').fill("newprop")
141
141
  page.get_by_role("button", name="OK").click()
142
142
  expect(page.locator(".panel.right").get_by_text("newprop")).to_be_visible()
@@ -215,6 +215,25 @@ test two,53.725145179688646,2.9700064980570517,"""
215
215
  )
216
216
 
217
217
 
218
+ def test_wkt_export(map, live_server, bootstrap, page):
219
+ page.goto(f"{live_server.url}{map.get_absolute_url()}?share")
220
+ button = page.get_by_role("button", name="wkt")
221
+ expect(button).to_be_visible()
222
+ with page.expect_download() as download_info:
223
+ button.click()
224
+ download = download_info.value
225
+ assert download.suggested_filename == "test_map.csv"
226
+ path = Path("/tmp/") / download.suggested_filename
227
+ download.save_as(path)
228
+ assert (
229
+ path.read_text()
230
+ == """name,geometry,description
231
+ name poly,"POLYGON ((11.25 53.585984,10.151367 52.975108,12.689209 52.167194,14.084473 53.199452,12.634277 53.618579,11.25 53.585984,11.25 53.585984))",
232
+ test one,POINT (-0.274658 52.57635),Some description
233
+ test two,"LINESTRING (-0.571289 54.476422,0.439453 54.610255,1.724854 53.448807,4.163818 53.988395,5.306396 53.533778,6.591797 53.709714,7.042236 53.350551)","""
234
+ )
235
+
236
+
218
237
  def test_gpx_export(map, live_server, bootstrap, page):
219
238
  page.goto(f"{live_server.url}{map.get_absolute_url()}?share")
220
239
  button = page.get_by_role("button", name="gpx")
@@ -0,0 +1,541 @@
1
+ import json
2
+ import re
3
+ from copy import deepcopy
4
+ from pathlib import Path
5
+
6
+ import pytest
7
+ from playwright.sync_api import expect
8
+
9
+ from umap.models import DataLayer, Map
10
+
11
+ from ..base import DataLayerFactory
12
+
13
+ pytestmark = pytest.mark.django_db
14
+
15
+
16
+ DATALAYER_DATA1 = {
17
+ "type": "FeatureCollection",
18
+ "features": [
19
+ {
20
+ "type": "Feature",
21
+ "properties": {
22
+ "mytype": "even",
23
+ "name": "Point 2",
24
+ "mynumber": 10,
25
+ "mydate": "2024/04/14 12:19:17",
26
+ },
27
+ "geometry": {"type": "Point", "coordinates": [0.065918, 48.385442]},
28
+ },
29
+ {
30
+ "type": "Feature",
31
+ "properties": {
32
+ "mytype": "odd",
33
+ "name": "Point 1",
34
+ "mynumber": 12,
35
+ "mydate": "2024/03/13 12:20:20",
36
+ },
37
+ "geometry": {"type": "Point", "coordinates": [3.55957, 49.767074]},
38
+ },
39
+ ],
40
+ "_umap_options": {
41
+ "name": "Calque 1",
42
+ },
43
+ }
44
+
45
+
46
+ DATALAYER_DATA2 = {
47
+ "type": "FeatureCollection",
48
+ "features": [
49
+ {
50
+ "type": "Feature",
51
+ "properties": {
52
+ "mytype": "even",
53
+ "name": "Point 4",
54
+ "mynumber": 10,
55
+ "mydate": "2024/08/18 13:14:15",
56
+ },
57
+ "geometry": {"type": "Point", "coordinates": [0.856934, 45.290347]},
58
+ },
59
+ {
60
+ "type": "Feature",
61
+ "properties": {
62
+ "mytype": "odd",
63
+ "name": "Point 3",
64
+ "mynumber": 14,
65
+ "mydate": "2024-04-14T10:19:17.000Z",
66
+ },
67
+ "geometry": {"type": "Point", "coordinates": [4.372559, 47.945786]},
68
+ },
69
+ ],
70
+ "_umap_options": {
71
+ "name": "Calque 2",
72
+ },
73
+ }
74
+
75
+
76
+ def test_can_add_field_on_map(live_server, page, openmap):
77
+ page.goto(f"{live_server.url}{openmap.get_absolute_url()}?edit")
78
+ page.get_by_role("button", name="Map advanced properties").click()
79
+ page.get_by_text("Manage Fields").click()
80
+ page.get_by_role("button", name="Add a new field").click()
81
+ page.get_by_role("textbox", name="Field Name ✔").fill("newfield")
82
+ page.get_by_label("Field Type").select_option("Number")
83
+ page.get_by_role("button", name="OK").click()
84
+ with page.expect_response(re.compile(r".*/update/settings/")):
85
+ page.get_by_role("button", name="Save").click()
86
+ saved = Map.objects.get(pk=openmap.pk)
87
+ assert saved.settings["properties"]["fields"] == [
88
+ {"key": "newfield", "type": "Number"}
89
+ ]
90
+
91
+
92
+ def test_can_add_field_on_datalayer(live_server, page, openmap, datalayer):
93
+ page.goto(f"{live_server.url}{openmap.get_absolute_url()}?edit")
94
+ page.get_by_role("button", name="Manage layers").click()
95
+ page.get_by_role("button", name="Edit", exact=True).click()
96
+ page.get_by_text("Manage Fields").click()
97
+ page.get_by_role("button", name="Add a new field").click()
98
+ page.get_by_role("textbox", name="Field Name ✔").fill("newfield")
99
+ page.get_by_label("Field Type").select_option("Number")
100
+ page.get_by_role("button", name="OK").click()
101
+ with page.expect_response(re.compile(r".*/datalayer/update/")):
102
+ page.get_by_role("button", name="Save").click()
103
+ saved = DataLayer.objects.first()
104
+ assert saved.settings["fields"] == [
105
+ {"key": "name", "type": "String"},
106
+ {"key": "description", "type": "Text"},
107
+ {"key": "newfield", "type": "Number"},
108
+ ]
109
+
110
+
111
+ def test_edit_and_rename_field_from_datalayer(live_server, page, openmap):
112
+ DataLayerFactory(map=openmap, data=DATALAYER_DATA1)
113
+ page.goto(f"{live_server.url}{openmap.get_absolute_url()}?edit")
114
+ page.get_by_role("button", name="Manage layers").click()
115
+ page.get_by_role("button", name="Edit", exact=True).click()
116
+ page.get_by_text("Manage Fields").click()
117
+ page.get_by_role("button", name="Edit this field").first.click()
118
+ page.get_by_role("textbox", name="Field Name ✔").fill("mytypenew")
119
+ page.get_by_label("Field Type").select_option("Text")
120
+ page.get_by_role("button", name="OK").click()
121
+ with page.expect_response(re.compile(r".*/datalayer/update/")):
122
+ page.get_by_role("button", name="Save").click()
123
+ saved = DataLayer.objects.first()
124
+ assert saved.settings["fields"] == [
125
+ {
126
+ "key": "mytypenew",
127
+ "type": "Text",
128
+ },
129
+ {
130
+ "key": "name",
131
+ "type": "String",
132
+ },
133
+ {
134
+ "key": "mynumber",
135
+ "type": "String",
136
+ },
137
+ {
138
+ "key": "mydate",
139
+ "type": "String",
140
+ },
141
+ ]
142
+ data = json.loads(Path(saved.geojson.path).read_text())
143
+ assert data["features"][0]["properties"] == {
144
+ "mydate": "2024/03/13 12:20:20",
145
+ "mynumber": 12,
146
+ "mytypenew": "odd",
147
+ "name": "Point 1",
148
+ }
149
+ assert data["features"][1]["properties"] == {
150
+ "mydate": "2024/04/14 12:19:17",
151
+ "mynumber": 10,
152
+ "mytypenew": "even",
153
+ "name": "Point 2",
154
+ }
155
+ page.locator(".edit-undo").click()
156
+ with page.expect_response(re.compile(r".*/datalayer/update/")):
157
+ page.get_by_role("button", name="Save").click()
158
+ saved = DataLayer.objects.first()
159
+ assert saved.settings["fields"] == [
160
+ {
161
+ "key": "mytype",
162
+ "type": "String",
163
+ },
164
+ {
165
+ "key": "name",
166
+ "type": "String",
167
+ },
168
+ {
169
+ "key": "mynumber",
170
+ "type": "String",
171
+ },
172
+ {
173
+ "key": "mydate",
174
+ "type": "String",
175
+ },
176
+ ]
177
+ data = json.loads(Path(saved.geojson.path).read_text())
178
+ assert data["features"][0]["properties"] == {
179
+ "mydate": "2024/03/13 12:20:20",
180
+ "mynumber": 12,
181
+ "mytype": "odd",
182
+ "name": "Point 1",
183
+ }
184
+ assert data["features"][1]["properties"] == {
185
+ "mydate": "2024/04/14 12:19:17",
186
+ "mynumber": 10,
187
+ "mytype": "even",
188
+ "name": "Point 2",
189
+ }
190
+
191
+
192
+ def test_edit_and_rename_field_from_map(live_server, page, openmap):
193
+ dl1 = DataLayerFactory(map=openmap, data=DATALAYER_DATA1)
194
+ DataLayerFactory(map=openmap, data=DATALAYER_DATA2)
195
+ openmap.settings["properties"]["fields"] = [
196
+ {"key": "mytype", "type": "String"},
197
+ {"key": "mynumber", "type": "Number"},
198
+ ]
199
+ openmap.save()
200
+ page.goto(f"{live_server.url}{openmap.get_absolute_url()}?edit")
201
+ page.get_by_role("button", name="Map advanced properties").click()
202
+ page.get_by_text("Manage Fields").click()
203
+ page.get_by_role("button", name="Edit this field").first.click()
204
+ page.get_by_role("textbox", name="Field Name ✔").fill("mytypenew")
205
+ page.get_by_label("Field Type").select_option("Text")
206
+ page.get_by_role("button", name="OK").click()
207
+ with page.expect_response(re.compile(r".*/datalayer/update/")):
208
+ page.get_by_role("button", name="Save").click()
209
+ saved = Map.objects.get(pk=openmap.pk)
210
+ assert saved.settings["properties"]["fields"] == [
211
+ {"key": "mytypenew", "type": "Text"},
212
+ {"key": "mynumber", "type": "Number"},
213
+ ]
214
+ saved = DataLayer.objects.get(pk=dl1.pk)
215
+ assert saved.settings["fields"] == [
216
+ {
217
+ "key": "name",
218
+ "type": "String",
219
+ },
220
+ {
221
+ "key": "mydate",
222
+ "type": "String",
223
+ },
224
+ ]
225
+ data = json.loads(Path(saved.geojson.path).read_text())
226
+ assert data["features"][0]["properties"] == {
227
+ "mydate": "2024/03/13 12:20:20",
228
+ "mynumber": 12,
229
+ "mytypenew": "odd",
230
+ "name": "Point 1",
231
+ }
232
+ assert data["features"][1]["properties"] == {
233
+ "mydate": "2024/04/14 12:19:17",
234
+ "mynumber": 10,
235
+ "mytypenew": "even",
236
+ "name": "Point 2",
237
+ }
238
+ page.locator(".edit-undo").click()
239
+
240
+ with page.expect_response(re.compile(rf".*/datalayer/update/{dl1.pk}")):
241
+ with page.expect_response(re.compile(r".*/update/settings/")):
242
+ page.get_by_role("button", name="Save").click()
243
+ saved = Map.objects.get(pk=openmap.pk)
244
+ assert saved.settings["properties"]["fields"] == [
245
+ {"key": "mytype", "type": "String"},
246
+ {"key": "mynumber", "type": "Number"},
247
+ ]
248
+ saved = DataLayer.objects.get(pk=dl1.pk)
249
+ assert saved.settings["fields"] == [
250
+ {
251
+ "key": "name",
252
+ "type": "String",
253
+ },
254
+ {
255
+ "key": "mydate",
256
+ "type": "String",
257
+ },
258
+ ]
259
+ data = json.loads(Path(saved.geojson.path).read_text())
260
+ assert data["features"][0]["properties"] == {
261
+ "mydate": "2024/03/13 12:20:20",
262
+ "mynumber": 12,
263
+ "mytype": "odd",
264
+ "name": "Point 1",
265
+ }
266
+ assert data["features"][1]["properties"] == {
267
+ "mydate": "2024/04/14 12:19:17",
268
+ "mynumber": 10,
269
+ "mytype": "even",
270
+ "name": "Point 2",
271
+ }
272
+
273
+
274
+ def test_delete_field_from_datalayer(live_server, page, openmap):
275
+ DataLayerFactory(map=openmap, data=DATALAYER_DATA1)
276
+ page.goto(f"{live_server.url}{openmap.get_absolute_url()}?edit")
277
+ page.get_by_role("button", name="Manage layers").click()
278
+ page.get_by_role("button", name="Edit", exact=True).click()
279
+ page.get_by_text("Manage Fields").click()
280
+ page.get_by_role("button", name="Delete this field").first.click()
281
+ page.get_by_role("button", name="OK").click()
282
+ with page.expect_response(re.compile(r".*/datalayer/update/")):
283
+ page.get_by_role("button", name="Save").click()
284
+ saved = DataLayer.objects.first()
285
+ assert saved.settings["fields"] == [
286
+ {
287
+ "key": "name",
288
+ "type": "String",
289
+ },
290
+ {
291
+ "key": "mynumber",
292
+ "type": "String",
293
+ },
294
+ {
295
+ "key": "mydate",
296
+ "type": "String",
297
+ },
298
+ ]
299
+ data = json.loads(Path(saved.geojson.path).read_text())
300
+ assert data["features"][0]["properties"] == {
301
+ "mydate": "2024/03/13 12:20:20",
302
+ "mynumber": 12,
303
+ "name": "Point 1",
304
+ }
305
+ assert data["features"][1]["properties"] == {
306
+ "mydate": "2024/04/14 12:19:17",
307
+ "mynumber": 10,
308
+ "name": "Point 2",
309
+ }
310
+ page.locator(".edit-undo").click()
311
+ with page.expect_response(re.compile(r".*/datalayer/update/")):
312
+ page.get_by_role("button", name="Save").click()
313
+ saved = DataLayer.objects.first()
314
+ assert saved.settings["fields"] == [
315
+ {
316
+ "key": "mytype",
317
+ "type": "String",
318
+ },
319
+ {
320
+ "key": "name",
321
+ "type": "String",
322
+ },
323
+ {
324
+ "key": "mynumber",
325
+ "type": "String",
326
+ },
327
+ {
328
+ "key": "mydate",
329
+ "type": "String",
330
+ },
331
+ ]
332
+ data = json.loads(Path(saved.geojson.path).read_text())
333
+ assert data["features"][0]["properties"] == {
334
+ "mydate": "2024/03/13 12:20:20",
335
+ "mynumber": 12,
336
+ "name": "Point 1",
337
+ "mytype": "odd",
338
+ }
339
+ assert data["features"][1]["properties"] == {
340
+ "mydate": "2024/04/14 12:19:17",
341
+ "mynumber": 10,
342
+ "name": "Point 2",
343
+ "mytype": "even",
344
+ }
345
+
346
+
347
+ def test_delete_field_from_map(live_server, page, openmap):
348
+ dl1 = DataLayerFactory(map=openmap, data=DATALAYER_DATA1)
349
+ DataLayerFactory(map=openmap, data=DATALAYER_DATA2)
350
+ openmap.settings["properties"]["fields"] = [
351
+ {"key": "mytype", "type": "String"},
352
+ {"key": "mynumber", "type": "Number"},
353
+ ]
354
+ openmap.save()
355
+ page.goto(f"{live_server.url}{openmap.get_absolute_url()}?edit")
356
+ page.get_by_role("button", name="Map advanced properties").click()
357
+ page.get_by_text("Manage Fields").click()
358
+ # Delete field mytype
359
+ page.get_by_role("button", name="Delete this field").first.click()
360
+ page.get_by_role("button", name="OK").click()
361
+ with page.expect_response(re.compile(r".*/datalayer/update/")):
362
+ page.get_by_role("button", name="Save").click()
363
+ saved = Map.objects.get(pk=openmap.pk)
364
+ assert saved.settings["properties"]["fields"] == [
365
+ {"key": "mynumber", "type": "Number"},
366
+ ]
367
+ saved = DataLayer.objects.get(pk=dl1.pk)
368
+ assert saved.settings["fields"] == [
369
+ {
370
+ "key": "name",
371
+ "type": "String",
372
+ },
373
+ {
374
+ "key": "mydate",
375
+ "type": "String",
376
+ },
377
+ ]
378
+ data = json.loads(Path(saved.geojson.path).read_text())
379
+ assert data["features"][0]["properties"] == {
380
+ "mydate": "2024/03/13 12:20:20",
381
+ "mynumber": 12,
382
+ "name": "Point 1",
383
+ }
384
+ assert data["features"][1]["properties"] == {
385
+ "mydate": "2024/04/14 12:19:17",
386
+ "mynumber": 10,
387
+ "name": "Point 2",
388
+ }
389
+ page.locator(".edit-undo").click()
390
+ with page.expect_response(re.compile(r"./update/settings/.*")):
391
+ with page.expect_response(re.compile(rf".*/datalayer/update/{dl1.pk}/")):
392
+ page.get_by_role("button", name="Save").click()
393
+ saved = Map.objects.get(pk=openmap.pk)
394
+ assert saved.settings["properties"]["fields"] == [
395
+ {"key": "mytype", "type": "String"},
396
+ {"key": "mynumber", "type": "Number"},
397
+ ]
398
+ saved = DataLayer.objects.get(pk=dl1.pk)
399
+ assert saved.settings["fields"] == [
400
+ {
401
+ "key": "name",
402
+ "type": "String",
403
+ },
404
+ {
405
+ "key": "mydate",
406
+ "type": "String",
407
+ },
408
+ ]
409
+ data = json.loads(Path(saved.geojson.path).read_text())
410
+ assert data["features"][0]["properties"] == {
411
+ "mydate": "2024/03/13 12:20:20",
412
+ "mynumber": 12,
413
+ "name": "Point 1",
414
+ "mytype": "odd",
415
+ }
416
+ assert data["features"][1]["properties"] == {
417
+ "mydate": "2024/04/14 12:19:17",
418
+ "mynumber": 10,
419
+ "name": "Point 2",
420
+ "mytype": "even",
421
+ }
422
+
423
+
424
+ def test_delete_field_from_datalayer_also_in_map(live_server, page, openmap):
425
+ # mytype exist both on map and datalayer 1
426
+ # deleting the field in the datalayer should not delete the data, as the field
427
+ # is also defined on the map
428
+ data = deepcopy(DATALAYER_DATA1)
429
+ data["_umap_options"]["fields"] = [
430
+ {"key": "mytype", "type": "String"},
431
+ {"key": "mynumber", "type": "Number"},
432
+ {"key": "name", "type": "String"},
433
+ {"key": "mydate", "type": "Date"},
434
+ ]
435
+ DataLayerFactory(map=openmap, data=data)
436
+ openmap.settings["properties"]["fields"] = [
437
+ {"key": "mytype", "type": "String"},
438
+ ]
439
+ openmap.save()
440
+ page.goto(f"{live_server.url}{openmap.get_absolute_url()}?edit")
441
+ page.get_by_role("button", name="Manage layers").click()
442
+ page.get_by_role("button", name="Edit", exact=True).first.click()
443
+ page.get_by_text("Manage Fields").click()
444
+ page.get_by_role("button", name="Delete this field").first.click()
445
+ page.get_by_role("button", name="OK").click()
446
+ with page.expect_response(re.compile(r".*/datalayer/update/")):
447
+ page.get_by_role("button", name="Save").click()
448
+ saved = DataLayer.objects.first()
449
+ assert saved.settings["fields"] == [
450
+ {"key": "mynumber", "type": "Number"},
451
+ {"key": "name", "type": "String"},
452
+ {"key": "mydate", "type": "Date"},
453
+ ]
454
+ data = json.loads(Path(saved.geojson.path).read_text())
455
+ assert data["features"][0]["properties"] == {
456
+ "mydate": "2024/03/13 12:20:20",
457
+ "mynumber": 12,
458
+ "name": "Point 1",
459
+ "mytype": "odd",
460
+ }
461
+ assert data["features"][1]["properties"] == {
462
+ "mydate": "2024/04/14 12:19:17",
463
+ "mynumber": 10,
464
+ "name": "Point 2",
465
+ "mytype": "even",
466
+ }
467
+
468
+
469
+ def test_can_change_field_type_with_remote_data(live_server, page, openmap, tilelayer):
470
+ data = {
471
+ "type": "FeatureCollection",
472
+ "features": [
473
+ {
474
+ "type": "Feature",
475
+ "properties": {"name": "Point 2", "myenum": "foofoo,bababar"},
476
+ "geometry": {
477
+ "type": "Point",
478
+ "coordinates": [4.3375, 11.2707],
479
+ },
480
+ },
481
+ {
482
+ "type": "Feature",
483
+ "properties": {"name": "Point 1", "myenum": "feefee,bababar"},
484
+ "geometry": {
485
+ "type": "Point",
486
+ "coordinates": [4.3375, 12.2707],
487
+ },
488
+ },
489
+ ],
490
+ }
491
+
492
+ def handle(route):
493
+ route.fulfill(json=data)
494
+
495
+ DataLayerFactory(
496
+ map=openmap,
497
+ settings={
498
+ "remoteData": {
499
+ "url": "https://remote.org/data.json",
500
+ "format": "geojson",
501
+ },
502
+ },
503
+ )
504
+ # Intercept the route to the proxy
505
+ page.route("https://remote.org/data.json", handle)
506
+
507
+ page.goto(f"{live_server.url}{openmap.get_absolute_url()}?edit#9/12.0017/4.4824")
508
+ page.get_by_role("button", name="Manage layers").click()
509
+ page.get_by_role("button", name="Edit", exact=True).click()
510
+ page.locator("summary").filter(has_text="Manage Fields").click()
511
+ # Click on "myenum" edit button
512
+ page.get_by_role("button", name="Edit this field").nth(1).click()
513
+ page.get_by_label("Field Type").select_option("Enum")
514
+ page.get_by_role("button", name="OK").click()
515
+ # Click on "myenum" add filter button
516
+ page.get_by_role("button", name="Add a filter for this field").nth(1).click()
517
+ page.get_by_role("button", name="OK").click()
518
+ page.get_by_role("button", name="Open browser").click()
519
+ page.get_by_text("Filters", exact=True).click()
520
+ expect(page.locator(".panel .umap-filter label")).to_contain_text(
521
+ ["bababar", "feefee", "foofoo"]
522
+ )
523
+
524
+
525
+ def test_boolean_field_should_display_a_switch_in_feature_form(
526
+ live_server, page, openmap, tilelayer
527
+ ):
528
+ openmap.settings["properties"]["fields"] = [
529
+ {"key": "mystring", "type": "String"},
530
+ {"key": "mynumber", "type": "Number"},
531
+ {"key": "mybool", "type": "Boolean"},
532
+ ]
533
+ openmap.save()
534
+ page.goto(f"{live_server.url}{openmap.get_absolute_url()}?edit")
535
+ page.get_by_role("button", name="Draw a marker (Ctrl+M)").click()
536
+ page.locator("#map").click()
537
+ panel = page.locator(".panel")
538
+ expect(panel.locator(".umap-field-mynumber input")).to_have_attribute(
539
+ "type", "number"
540
+ )
541
+ expect(panel.locator(".umap-field-mybool.with-switch")).to_be_visible()