umap-project 3.3.6__py3-none-any.whl → 3.4.0b0__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 umap-project might be problematic. Click here for more details.

Files changed (216) hide show
  1. umap/__init__.py +1 -1
  2. umap/locale/cs_CZ/LC_MESSAGES/django.mo +0 -0
  3. umap/locale/cs_CZ/LC_MESSAGES/django.po +43 -33
  4. umap/locale/da/LC_MESSAGES/django.mo +0 -0
  5. umap/locale/da/LC_MESSAGES/django.po +43 -33
  6. umap/locale/de/LC_MESSAGES/django.mo +0 -0
  7. umap/locale/de/LC_MESSAGES/django.po +35 -29
  8. umap/locale/el/LC_MESSAGES/django.mo +0 -0
  9. umap/locale/el/LC_MESSAGES/django.po +35 -29
  10. umap/locale/en/LC_MESSAGES/django.po +34 -28
  11. umap/locale/es/LC_MESSAGES/django.mo +0 -0
  12. umap/locale/es/LC_MESSAGES/django.po +43 -33
  13. umap/locale/et/LC_MESSAGES/django.mo +0 -0
  14. umap/locale/et/LC_MESSAGES/django.po +58 -54
  15. umap/locale/eu/LC_MESSAGES/django.mo +0 -0
  16. umap/locale/eu/LC_MESSAGES/django.po +43 -33
  17. umap/locale/fa_IR/LC_MESSAGES/django.mo +0 -0
  18. umap/locale/fa_IR/LC_MESSAGES/django.po +43 -33
  19. umap/locale/fr/LC_MESSAGES/django.mo +0 -0
  20. umap/locale/fr/LC_MESSAGES/django.po +36 -30
  21. umap/locale/gl/LC_MESSAGES/django.mo +0 -0
  22. umap/locale/gl/LC_MESSAGES/django.po +43 -33
  23. umap/locale/hu/LC_MESSAGES/django.mo +0 -0
  24. umap/locale/hu/LC_MESSAGES/django.po +35 -29
  25. umap/locale/is/LC_MESSAGES/django.mo +0 -0
  26. umap/locale/is/LC_MESSAGES/django.po +43 -33
  27. umap/locale/it/LC_MESSAGES/django.mo +0 -0
  28. umap/locale/it/LC_MESSAGES/django.po +43 -33
  29. umap/locale/nl/LC_MESSAGES/django.mo +0 -0
  30. umap/locale/nl/LC_MESSAGES/django.po +35 -29
  31. umap/locale/pl/LC_MESSAGES/django.mo +0 -0
  32. umap/locale/pl/LC_MESSAGES/django.po +43 -33
  33. umap/locale/pt/LC_MESSAGES/django.mo +0 -0
  34. umap/locale/pt/LC_MESSAGES/django.po +43 -33
  35. umap/locale/th_TH/LC_MESSAGES/django.mo +0 -0
  36. umap/locale/th_TH/LC_MESSAGES/django.po +310 -109
  37. umap/locale/zh_TW/LC_MESSAGES/django.mo +0 -0
  38. umap/locale/zh_TW/LC_MESSAGES/django.po +80 -70
  39. umap/management/commands/switch_user.py +2 -2
  40. umap/static/umap/base.css +89 -32
  41. umap/static/umap/content.css +129 -33
  42. umap/static/umap/css/bar.css +82 -20
  43. umap/static/umap/css/browser.css +163 -0
  44. umap/static/umap/css/contextmenu.css +15 -0
  45. umap/static/umap/css/dialog.css +36 -16
  46. umap/static/umap/css/form.css +122 -32
  47. umap/static/umap/css/icon.css +46 -3
  48. umap/static/umap/css/panel.css +7 -3
  49. umap/static/umap/css/popup.css +34 -8
  50. umap/static/umap/css/tooltip.css +8 -4
  51. umap/static/umap/img/16-white.svg +26 -8
  52. umap/static/umap/img/16.svg +1 -1
  53. umap/static/umap/img/source/16-white.svg +36 -18
  54. umap/static/umap/img/source/16.svg +1 -1
  55. umap/static/umap/js/components/alerts/alert.css +69 -31
  56. umap/static/umap/js/components/alerts/alert.js +20 -2
  57. umap/static/umap/js/modules/browser.js +63 -55
  58. umap/static/umap/js/modules/caption.js +10 -7
  59. umap/static/umap/js/modules/data/features.js +82 -59
  60. umap/static/umap/js/modules/data/layer.js +56 -157
  61. umap/static/umap/js/modules/domutils.js +109 -0
  62. umap/static/umap/js/modules/filters.js +807 -0
  63. umap/static/umap/js/modules/form/builder.js +8 -5
  64. umap/static/umap/js/modules/form/fields.js +110 -220
  65. umap/static/umap/js/modules/formatter.js +24 -1
  66. umap/static/umap/js/modules/help.js +3 -2
  67. umap/static/umap/js/modules/importers/opendata.js +5 -0
  68. umap/static/umap/js/modules/importers/openrouteservice.js +6 -1
  69. umap/static/umap/js/modules/managers.js +265 -1
  70. umap/static/umap/js/modules/permissions.js +35 -31
  71. umap/static/umap/js/modules/rendering/controls.js +7 -7
  72. umap/static/umap/js/modules/rendering/icon.js +3 -8
  73. umap/static/umap/js/modules/rendering/layers/classified.js +17 -10
  74. umap/static/umap/js/modules/rendering/layers/cluster.js +2 -2
  75. umap/static/umap/js/modules/rendering/template.js +44 -8
  76. umap/static/umap/js/modules/rendering/ui.js +29 -23
  77. umap/static/umap/js/modules/rules.js +4 -3
  78. umap/static/umap/js/modules/schema.js +3 -6
  79. umap/static/umap/js/modules/share.js +4 -3
  80. umap/static/umap/js/modules/tableeditor.js +50 -38
  81. umap/static/umap/js/modules/templates.js +2 -3
  82. umap/static/umap/js/modules/ui/bar.js +42 -18
  83. umap/static/umap/js/modules/ui/dialog.js +33 -31
  84. umap/static/umap/js/modules/ui/panel.js +21 -7
  85. umap/static/umap/js/modules/ui/tooltip.js +6 -5
  86. umap/static/umap/js/modules/umap.js +148 -51
  87. umap/static/umap/js/modules/utils.js +23 -1
  88. umap/static/umap/js/umap.core.js +1 -110
  89. umap/static/umap/locale/am_ET.js +40 -14
  90. umap/static/umap/locale/am_ET.json +40 -14
  91. umap/static/umap/locale/ar.js +40 -14
  92. umap/static/umap/locale/ar.json +40 -14
  93. umap/static/umap/locale/ast.js +40 -14
  94. umap/static/umap/locale/ast.json +40 -14
  95. umap/static/umap/locale/bg.js +40 -14
  96. umap/static/umap/locale/bg.json +40 -14
  97. umap/static/umap/locale/br.js +47 -21
  98. umap/static/umap/locale/br.json +47 -21
  99. umap/static/umap/locale/ca.js +40 -14
  100. umap/static/umap/locale/ca.json +40 -14
  101. umap/static/umap/locale/cs_CZ.js +40 -14
  102. umap/static/umap/locale/cs_CZ.json +40 -14
  103. umap/static/umap/locale/da.js +40 -14
  104. umap/static/umap/locale/da.json +40 -14
  105. umap/static/umap/locale/de.js +39 -13
  106. umap/static/umap/locale/de.json +39 -13
  107. umap/static/umap/locale/el.js +40 -14
  108. umap/static/umap/locale/el.json +40 -14
  109. umap/static/umap/locale/en.js +39 -13
  110. umap/static/umap/locale/en.json +39 -13
  111. umap/static/umap/locale/en_US.json +40 -14
  112. umap/static/umap/locale/es.js +40 -14
  113. umap/static/umap/locale/es.json +40 -14
  114. umap/static/umap/locale/et.js +79 -53
  115. umap/static/umap/locale/et.json +79 -53
  116. umap/static/umap/locale/eu.js +72 -46
  117. umap/static/umap/locale/eu.json +72 -46
  118. umap/static/umap/locale/fa_IR.js +40 -14
  119. umap/static/umap/locale/fa_IR.json +40 -14
  120. umap/static/umap/locale/fi.js +40 -14
  121. umap/static/umap/locale/fi.json +40 -14
  122. umap/static/umap/locale/fr.js +39 -13
  123. umap/static/umap/locale/fr.json +39 -13
  124. umap/static/umap/locale/gl.js +40 -14
  125. umap/static/umap/locale/gl.json +40 -14
  126. umap/static/umap/locale/he.js +40 -14
  127. umap/static/umap/locale/he.json +40 -14
  128. umap/static/umap/locale/hr.js +40 -14
  129. umap/static/umap/locale/hr.json +40 -14
  130. umap/static/umap/locale/hu.js +40 -14
  131. umap/static/umap/locale/hu.json +40 -14
  132. umap/static/umap/locale/id.js +40 -14
  133. umap/static/umap/locale/id.json +40 -14
  134. umap/static/umap/locale/is.js +40 -14
  135. umap/static/umap/locale/is.json +40 -14
  136. umap/static/umap/locale/it.js +40 -14
  137. umap/static/umap/locale/it.json +40 -14
  138. umap/static/umap/locale/ja.js +40 -14
  139. umap/static/umap/locale/ja.json +40 -14
  140. umap/static/umap/locale/ko.js +40 -14
  141. umap/static/umap/locale/ko.json +40 -14
  142. umap/static/umap/locale/lt.js +40 -14
  143. umap/static/umap/locale/lt.json +40 -14
  144. umap/static/umap/locale/ms.js +40 -14
  145. umap/static/umap/locale/ms.json +40 -14
  146. umap/static/umap/locale/nl.js +40 -14
  147. umap/static/umap/locale/nl.json +40 -14
  148. umap/static/umap/locale/no.js +40 -14
  149. umap/static/umap/locale/no.json +40 -14
  150. umap/static/umap/locale/pl.js +40 -14
  151. umap/static/umap/locale/pl.json +40 -14
  152. umap/static/umap/locale/pl_PL.json +40 -14
  153. umap/static/umap/locale/pt.js +40 -14
  154. umap/static/umap/locale/pt.json +40 -14
  155. umap/static/umap/locale/pt_BR.js +40 -14
  156. umap/static/umap/locale/pt_BR.json +40 -14
  157. umap/static/umap/locale/pt_PT.js +40 -14
  158. umap/static/umap/locale/pt_PT.json +40 -14
  159. umap/static/umap/locale/ro.js +40 -14
  160. umap/static/umap/locale/ro.json +40 -14
  161. umap/static/umap/locale/ru.js +40 -14
  162. umap/static/umap/locale/ru.json +40 -14
  163. umap/static/umap/locale/sk_SK.js +40 -14
  164. umap/static/umap/locale/sk_SK.json +40 -14
  165. umap/static/umap/locale/sl.js +40 -14
  166. umap/static/umap/locale/sl.json +40 -14
  167. umap/static/umap/locale/sr.js +40 -14
  168. umap/static/umap/locale/sr.json +40 -14
  169. umap/static/umap/locale/sv.js +40 -14
  170. umap/static/umap/locale/sv.json +40 -14
  171. umap/static/umap/locale/th_TH.js +40 -14
  172. umap/static/umap/locale/th_TH.json +40 -14
  173. umap/static/umap/locale/tr.js +40 -14
  174. umap/static/umap/locale/tr.json +40 -14
  175. umap/static/umap/locale/uk_UA.js +40 -14
  176. umap/static/umap/locale/uk_UA.json +40 -14
  177. umap/static/umap/locale/vi.js +40 -14
  178. umap/static/umap/locale/vi.json +40 -14
  179. umap/static/umap/locale/vi_VN.json +40 -14
  180. umap/static/umap/locale/zh.js +40 -14
  181. umap/static/umap/locale/zh.json +40 -14
  182. umap/static/umap/locale/zh_CN.json +40 -14
  183. umap/static/umap/locale/zh_TW.Big5.json +40 -14
  184. umap/static/umap/locale/zh_TW.js +39 -13
  185. umap/static/umap/locale/zh_TW.json +39 -13
  186. umap/static/umap/map.css +60 -223
  187. umap/static/umap/unittests/utils.js +18 -0
  188. umap/static/umap/vars.css +23 -5
  189. umap/templates/umap/components/alerts/alert.html +32 -29
  190. umap/templates/umap/css.html +2 -1
  191. umap/templates/umap/login_popup_end.html +18 -9
  192. umap/templates/umap/user_map_table.html +7 -2
  193. umap/tests/integration/conftest.py +2 -6
  194. umap/tests/integration/test_anonymous_owned_map.py +89 -36
  195. umap/tests/integration/test_basics.py +25 -1
  196. umap/tests/integration/test_browser.py +37 -0
  197. umap/tests/integration/test_draw_polygon.py +2 -0
  198. umap/tests/integration/test_edit_marker.py +1 -1
  199. umap/tests/integration/test_export_map.py +19 -0
  200. umap/tests/integration/test_fields.py +522 -0
  201. umap/tests/integration/test_filters.py +617 -0
  202. umap/tests/integration/test_import.py +15 -42
  203. umap/tests/integration/test_remote_data.py +60 -4
  204. umap/tests/integration/test_share.py +4 -4
  205. umap/tests/integration/test_tableeditor.py +31 -7
  206. umap/tests/integration/test_websocket_sync.py +3 -1
  207. umap/tests/test_dashboard.py +10 -0
  208. umap/urls.py +1 -0
  209. umap/views.py +5 -0
  210. {umap_project-3.3.6.dist-info → umap_project-3.4.0b0.dist-info}/METADATA +12 -12
  211. {umap_project-3.3.6.dist-info → umap_project-3.4.0b0.dist-info}/RECORD +214 -211
  212. umap/static/umap/js/modules/facets.js +0 -164
  213. umap/tests/integration/test_facets_browser.py +0 -279
  214. {umap_project-3.3.6.dist-info → umap_project-3.4.0b0.dist-info}/WHEEL +0 -0
  215. {umap_project-3.3.6.dist-info → umap_project-3.4.0b0.dist-info}/entry_points.txt +0 -0
  216. {umap_project-3.3.6.dist-info → umap_project-3.4.0b0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,617 @@
1
+ import copy
2
+ import re
3
+
4
+ import pytest
5
+ from playwright.sync_api import expect
6
+
7
+ from umap.models import DataLayer, Map
8
+
9
+ from ..base import DataLayerFactory
10
+
11
+ pytestmark = pytest.mark.django_db
12
+
13
+
14
+ DATALAYER_DATA1 = {
15
+ "type": "FeatureCollection",
16
+ "features": [
17
+ {
18
+ "type": "Feature",
19
+ "properties": {
20
+ "mytype": "even",
21
+ "name": "Point 2",
22
+ "mynumber": 10,
23
+ "mydate": "2024/04/14 12:19:17",
24
+ },
25
+ "geometry": {"type": "Point", "coordinates": [0.065918, 48.385442]},
26
+ },
27
+ {
28
+ "type": "Feature",
29
+ "properties": {
30
+ "mytype": "odd",
31
+ "name": "Point 1",
32
+ "mynumber": 12,
33
+ "mydate": "2024/03/13 12:20:20",
34
+ },
35
+ "geometry": {"type": "Point", "coordinates": [3.55957, 49.767074]},
36
+ },
37
+ ],
38
+ "_umap_options": {
39
+ "name": "Calque 1",
40
+ },
41
+ }
42
+
43
+
44
+ DATALAYER_DATA2 = {
45
+ "type": "FeatureCollection",
46
+ "features": [
47
+ {
48
+ "type": "Feature",
49
+ "properties": {
50
+ "mytype": "even",
51
+ "name": "Point 4",
52
+ "mynumber": 10,
53
+ "mydate": "2024/08/18 13:14:15",
54
+ },
55
+ "geometry": {"type": "Point", "coordinates": [0.856934, 45.290347]},
56
+ },
57
+ {
58
+ "type": "Feature",
59
+ "properties": {
60
+ "mytype": "odd",
61
+ "name": "Point 3",
62
+ "mynumber": 14,
63
+ "mydate": "2024-04-14T10:19:17.000Z",
64
+ },
65
+ "geometry": {"type": "Point", "coordinates": [4.372559, 47.945786]},
66
+ },
67
+ ],
68
+ "_umap_options": {
69
+ "name": "Calque 2",
70
+ },
71
+ }
72
+
73
+
74
+ DATALAYER_DATA3 = {
75
+ "type": "FeatureCollection",
76
+ "features": [
77
+ {
78
+ "type": "Feature",
79
+ "properties": {"name": "a polygon"},
80
+ "geometry": {
81
+ "type": "Polygon",
82
+ "coordinates": [
83
+ [
84
+ [2.12, 49.57],
85
+ [1.08, 49.02],
86
+ [2.51, 47.55],
87
+ [3.19, 48.77],
88
+ [2.12, 49.57],
89
+ ]
90
+ ],
91
+ },
92
+ },
93
+ ],
94
+ "_umap_options": {"name": "Calque 2", "browsable": False},
95
+ }
96
+
97
+
98
+ def test_simple_facet_search(live_server, page, map):
99
+ map.settings["properties"]["onLoadPanel"] = "datafilters"
100
+ map.settings["properties"]["filters"] = [
101
+ {"fieldKey": "mytype", "label": "My type"},
102
+ {"fieldKey": "mynumber", "label": "My number", "widget": "MinMax"},
103
+ ]
104
+ map.settings["properties"]["fields"] = [
105
+ {"key": "mytype", "type": "String"},
106
+ {"key": "mynumber", "type": "Number"},
107
+ ]
108
+ map.settings["properties"]["showLabel"] = True
109
+ map.save()
110
+ DataLayerFactory(map=map, data=DATALAYER_DATA1)
111
+ DataLayerFactory(map=map, data=DATALAYER_DATA2)
112
+ DataLayerFactory(map=map, data=DATALAYER_DATA3)
113
+ page.goto(f"{live_server.url}{map.get_absolute_url()}#6/48.948/1.670")
114
+ panel = page.locator(".panel.left.on")
115
+ expect(panel).to_have_class(re.compile(".*expanded.*"))
116
+ expect(panel.locator(".umap-browser")).to_be_visible()
117
+ # From a non browsable datalayer, should not be impacted
118
+ paths = page.locator(".leaflet-overlay-pane path")
119
+ expect(paths).to_be_visible()
120
+ expect(panel).to_be_visible()
121
+ # Facet name
122
+ expect(page.get_by_text("My type")).to_be_visible()
123
+ # Facet values
124
+ oven = page.get_by_text("even")
125
+ odd = page.get_by_text("odd")
126
+ expect(oven).to_be_visible()
127
+ expect(odd).to_be_visible()
128
+ expect(paths).to_be_visible()
129
+ markers = page.locator(".leaflet-marker-icon")
130
+ expect(markers).to_have_count(4)
131
+ # Tooltips
132
+ # Sometimes PW founds two tooltips with the same name, but cannot reproduce it.
133
+ page.wait_for_timeout(300)
134
+ expect(page.get_by_role("tooltip", name="Point 1")).to_be_visible()
135
+ expect(page.get_by_role("tooltip", name="Point 2")).to_be_visible()
136
+ expect(page.get_by_role("tooltip", name="Point 3")).to_be_visible()
137
+ expect(page.get_by_role("tooltip", name="Point 4")).to_be_visible()
138
+
139
+ # Datalist
140
+ expect(panel.get_by_text("Point 1")).to_be_visible()
141
+ expect(panel.get_by_text("Point 2")).to_be_visible()
142
+ expect(panel.get_by_text("Point 3")).to_be_visible()
143
+ expect(panel.get_by_text("Point 4")).to_be_visible()
144
+
145
+ # Now let's filter
146
+ odd.click()
147
+ expect(markers).to_have_count(2)
148
+ expect(page.get_by_role("tooltip", name="Point 2")).to_be_hidden()
149
+ expect(page.get_by_role("tooltip", name="Point 4")).to_be_hidden()
150
+ expect(page.get_by_role("tooltip", name="Point 1")).to_be_visible()
151
+ expect(page.get_by_role("tooltip", name="Point 3")).to_be_visible()
152
+ expect(panel.get_by_text("Point 2")).to_be_hidden()
153
+ expect(panel.get_by_text("Point 4")).to_be_hidden()
154
+ expect(panel.get_by_text("Point 1")).to_be_visible()
155
+ expect(panel.get_by_text("Point 3")).to_be_visible()
156
+ expect(paths).to_be_visible
157
+ # Now let's filter
158
+ odd.click()
159
+ expect(markers).to_have_count(4)
160
+ expect(paths).to_be_visible()
161
+
162
+ # Let's filter using the number facet
163
+ expect(page.get_by_text("My Number")).to_be_visible()
164
+ expect(page.get_by_label("Min")).to_have_value("10")
165
+ expect(page.get_by_label("Max")).to_have_value("14")
166
+ page.get_by_label("Min").fill("11")
167
+ page.keyboard.press("Tab") # Move out of the input, so the "change" event is sent
168
+ expect(markers).to_have_count(2)
169
+ expect(paths).to_be_visible()
170
+ page.get_by_label("Max").fill("13")
171
+ page.keyboard.press("Tab")
172
+ expect(markers).to_have_count(1)
173
+
174
+ # Now let's combine
175
+ page.get_by_label("Min").fill("10")
176
+ page.keyboard.press("Tab")
177
+ expect(markers).to_have_count(3)
178
+ odd.click()
179
+ expect(markers).to_have_count(1)
180
+ expect(paths).to_be_visible()
181
+
182
+
183
+ def test_date_facet_search(live_server, page, map):
184
+ map.settings["properties"]["onLoadPanel"] = "datafilters"
185
+ map.settings["properties"]["filters"] = [
186
+ {"fieldKey": "mydate", "label": "Date filter", "widget": "MinMax"}
187
+ ]
188
+ map.settings["properties"]["fields"] = [{"key": "mydate", "type": "Date"}]
189
+ map.save()
190
+ DataLayerFactory(map=map, data=DATALAYER_DATA1)
191
+ DataLayerFactory(map=map, data=DATALAYER_DATA2)
192
+ page.goto(f"{live_server.url}{map.get_absolute_url()}#6/47.5/-1.5")
193
+ markers = page.locator(".leaflet-marker-icon")
194
+ expect(markers).to_have_count(4)
195
+ expect(page.get_by_text("Date Filter")).to_be_visible()
196
+ expect(page.get_by_label("From")).to_have_value("2024-03-13")
197
+ expect(page.get_by_label("Until")).to_have_value("2024-08-18")
198
+ page.get_by_label("From").fill("2024-03-14")
199
+ expect(markers).to_have_count(3)
200
+ page.get_by_label("Until").fill("2024-08-17")
201
+ expect(markers).to_have_count(2)
202
+
203
+
204
+ def test_choice_with_empty_value(live_server, page, map):
205
+ map.settings["properties"]["onLoadPanel"] = "datafilters"
206
+ map.settings["properties"]["fields"] = [{"key": "mytype", "type": "String"}]
207
+ map.settings["properties"]["filters"] = [{"fieldKey": "mytype", "label": "My type"}]
208
+ map.save()
209
+ data = copy.deepcopy(DATALAYER_DATA1)
210
+ data["features"][0]["properties"]["mytype"] = ""
211
+ del data["features"][1]["properties"]["mytype"]
212
+ DataLayerFactory(map=map, data=data)
213
+ DataLayerFactory(map=map, data=DATALAYER_DATA2)
214
+ page.goto(f"{live_server.url}{map.get_absolute_url()}#6/47.5/-1.5")
215
+ expect(page.get_by_text("<empty value>")).to_be_visible()
216
+ markers = page.locator(".leaflet-marker-icon")
217
+ expect(markers).to_have_count(4)
218
+ page.get_by_text("<empty value>").click()
219
+ expect(markers).to_have_count(2)
220
+
221
+
222
+ def test_number_with_zero_value(live_server, page, map):
223
+ map.settings["properties"]["onLoadPanel"] = "datafilters"
224
+ map.settings["properties"]["filters"] = [
225
+ {"fieldKey": "mynumber", "label": "Filter", "widget": "MinMax"}
226
+ ]
227
+ map.settings["properties"]["fields"] = [{"key": "mynumber", "type": "Number"}]
228
+ map.save()
229
+ data = copy.deepcopy(DATALAYER_DATA1)
230
+ data["features"][0]["properties"]["mynumber"] = 0
231
+ DataLayerFactory(map=map, data=data)
232
+ DataLayerFactory(map=map, data=DATALAYER_DATA2)
233
+ page.goto(f"{live_server.url}{map.get_absolute_url()}#6/47.5/-1.5")
234
+ expect(page.get_by_label("Min")).to_have_value("0")
235
+ expect(page.get_by_label("Max")).to_have_value("14")
236
+ page.get_by_label("Min").fill("1")
237
+ page.keyboard.press("Tab") # Move out of the input, so the "change" event is sent
238
+ markers = page.locator(".leaflet-marker-icon")
239
+ expect(markers).to_have_count(3)
240
+
241
+
242
+ def test_facets_search_are_persistent_when_closing_panel(live_server, page, map):
243
+ map.settings["properties"]["onLoadPanel"] = "datafilters"
244
+ map.settings["properties"]["filters"] = [
245
+ {"fieldKey": "mytype", "label": "My type"},
246
+ {"fieldKey": "mynumber", "label": "My Number", "widget": "MinMax"},
247
+ ]
248
+ map.settings["properties"]["fields"] = [
249
+ {"key": "mytype"},
250
+ {"key": "mynumber", "type": "Number"},
251
+ ]
252
+ map.save()
253
+ DataLayerFactory(map=map, data=DATALAYER_DATA1)
254
+ DataLayerFactory(map=map, data=DATALAYER_DATA2)
255
+ page.goto(f"{live_server.url}{map.get_absolute_url()}#6/48.948/1.670")
256
+ panel = page.locator(".panel.left")
257
+
258
+ # Facet values
259
+ odd = page.get_by_label("odd")
260
+ markers = page.locator(".leaflet-marker-icon")
261
+ expect(markers).to_have_count(4)
262
+
263
+ # Datalist in the browser
264
+ expect(panel.get_by_text("Point 1")).to_be_visible()
265
+ expect(panel.get_by_text("Point 2")).to_be_visible()
266
+ expect(panel.get_by_text("Point 3")).to_be_visible()
267
+ expect(panel.get_by_text("Point 4")).to_be_visible()
268
+
269
+ # Now let's filter
270
+ odd.click()
271
+ expect(page.locator(".filters summary")).to_have_attribute("data-badge", " ")
272
+ expect(page.locator(".umap-control-browse")).to_have_attribute("data-badge", " ")
273
+ expect(markers).to_have_count(2)
274
+ expect(panel.get_by_text("Point 2")).to_be_hidden()
275
+ expect(panel.get_by_text("Point 4")).to_be_hidden()
276
+ expect(panel.get_by_text("Point 1")).to_be_visible()
277
+ expect(panel.get_by_text("Point 3")).to_be_visible()
278
+
279
+ # Let's filter using the number facet
280
+ expect(panel.get_by_label("Min")).to_have_value("10")
281
+ expect(panel.get_by_label("Max")).to_have_value("14")
282
+ page.get_by_label("Min").fill("13")
283
+ page.keyboard.press("Tab") # Move out of the input, so the "change" event is sent
284
+ expect(panel.get_by_label("Min")).to_have_attribute("data-modified", "true")
285
+ expect(markers).to_have_count(1)
286
+ expect(panel.get_by_text("Point 2")).to_be_hidden()
287
+ expect(panel.get_by_text("Point 4")).to_be_hidden()
288
+ expect(panel.get_by_text("Point 1")).to_be_hidden()
289
+ expect(panel.get_by_text("Point 3")).to_be_visible()
290
+
291
+ # Close panel
292
+ expect(panel.locator(".filters summary")).to_have_attribute("data-badge", " ")
293
+ expect(page.locator(".umap-control-browse")).to_have_attribute("data-badge", " ")
294
+ panel.get_by_role("button", name="Close").click()
295
+ page.get_by_role("button", name="Open browser").click()
296
+ expect(panel.get_by_label("Min")).to_have_value("13")
297
+ expect(panel.get_by_label("Min")).to_have_attribute("data-modified", "true")
298
+ expect(panel.get_by_label("odd")).to_be_checked()
299
+
300
+ # Datalist in the browser should be inchanged
301
+ expect(panel.get_by_text("Point 2")).to_be_hidden()
302
+ expect(panel.get_by_text("Point 4")).to_be_hidden()
303
+ expect(panel.get_by_text("Point 1")).to_be_hidden()
304
+ expect(panel.get_by_text("Point 3")).to_be_visible()
305
+
306
+
307
+ def test_can_load_legacy_facetKey(live_server, page, openmap):
308
+ openmap.settings["properties"]["facetKey"] = (
309
+ "mytype|My Type|radio,mynumber|My Number|number,mydate|My Date|date"
310
+ )
311
+ openmap.save()
312
+ page.goto(f"{live_server.url}{openmap.get_absolute_url()}?edit")
313
+ expect(
314
+ page.get_by_text("The map has been upgraded to latest version, please save it.")
315
+ ).to_be_visible()
316
+ with page.expect_response(re.compile("./update/settings/.*")):
317
+ page.get_by_role("button", name="Save", exact=True).click()
318
+ saved = Map.objects.first()
319
+ assert "facetKey" not in saved.settings["properties"]
320
+ assert saved.settings["properties"]["filters"] == [
321
+ {
322
+ "fieldKey": "mytype",
323
+ "label": "My Type",
324
+ "widget": "Radio",
325
+ },
326
+ {
327
+ "fieldKey": "mynumber",
328
+ "label": "My Number",
329
+ "widget": "MinMax",
330
+ },
331
+ {
332
+ "fieldKey": "mydate",
333
+ "label": "My Date",
334
+ "widget": "MinMax",
335
+ },
336
+ ]
337
+ assert saved.settings["properties"]["fields"] == [
338
+ {"key": "mytype", "type": "String"},
339
+ {"key": "mynumber", "type": "Number"},
340
+ {"key": "mydate", "type": "Date"},
341
+ ]
342
+
343
+
344
+ def test_deleting_field_should_delete_filter(live_server, page, openmap, datalayer):
345
+ datalayer.settings["fields"] = [
346
+ {"key": "name", "type": "String"},
347
+ {"key": "foobar", "type": "Number"},
348
+ {"key": "description", "type": "Text"},
349
+ ]
350
+ datalayer.settings["filters"] = [
351
+ {"fieldKey": "foobar", "widget": "MinMax", "label": "Foo Bar"},
352
+ {"fieldKey": "name", "widget": "Checkbox", "label": "Bar Foo"},
353
+ ]
354
+ datalayer.save()
355
+ page.goto(f"{live_server.url}{openmap.get_absolute_url()}?edit")
356
+ page.get_by_role("button", name="Manage layers").click()
357
+ page.get_by_role("button", name="Edit", exact=True).click()
358
+ page.get_by_text("Manage Fields").click()
359
+ page.get_by_role("button", name="Delete this field").nth(1).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 = DataLayer.objects.first()
364
+ assert saved.settings["fields"] == [
365
+ {"key": "name", "type": "String"},
366
+ {"key": "description", "type": "Text"},
367
+ ]
368
+ saved.settings["filters"] == [
369
+ {"fieldKey": "name", "widget": "Checkbox", "label": "Bar Foo"},
370
+ ]
371
+ page.locator(".edit-undo").click()
372
+ with page.expect_response(re.compile(r".*/datalayer/update/")):
373
+ page.get_by_role("button", name="Save").click()
374
+ saved = DataLayer.objects.first()
375
+ assert saved.settings["fields"] == [
376
+ {"key": "name", "type": "String"},
377
+ {"key": "foobar", "type": "Number"},
378
+ {"key": "description", "type": "Text"},
379
+ ]
380
+ saved.settings["filters"] == [
381
+ {"fieldKey": "foobar", "widget": "MinMax", "label": "Foo Bar"},
382
+ {"fieldKey": "name", "widget": "Checkbox", "label": "Bar Foo"},
383
+ ]
384
+
385
+
386
+ def test_deleting_field_from_map_should_delete_filter(live_server, page, openmap):
387
+ openmap.settings["properties"]["fields"] = [
388
+ {"key": "name", "type": "String"},
389
+ {"key": "foobar", "type": "Number"},
390
+ {"key": "description", "type": "Text"},
391
+ ]
392
+ openmap.settings["properties"]["filters"] = [
393
+ {"fieldKey": "foobar", "widget": "MinMax", "label": "Foo Bar"},
394
+ {"fieldKey": "name", "widget": "Checkbox", "label": "Bar Foo"},
395
+ ]
396
+ openmap.save()
397
+ page.goto(f"{live_server.url}{openmap.get_absolute_url()}?edit")
398
+ page.get_by_role("button", name="Map advanced properties").click()
399
+ page.get_by_text("Manage Fields").click()
400
+ page.get_by_role("button", name="Delete this field").nth(1).click()
401
+ page.get_by_role("button", name="OK").click()
402
+ with page.expect_response(re.compile(r"./update/settings/.*")):
403
+ page.get_by_role("button", name="Save").click()
404
+ saved = Map.objects.first()
405
+ assert saved.settings["properties"]["fields"] == [
406
+ {"key": "name", "type": "String"},
407
+ {"key": "description", "type": "Text"},
408
+ ]
409
+ saved.settings["properties"]["filters"] == [
410
+ {"fieldKey": "name", "widget": "Checkbox", "label": "Bar Foo"},
411
+ ]
412
+ page.locator(".edit-undo").click()
413
+ with page.expect_response(re.compile(r"./update/settings/.*")):
414
+ page.get_by_role("button", name="Save").click()
415
+ saved = Map.objects.first()
416
+ assert saved.settings["properties"]["fields"] == [
417
+ {"key": "name", "type": "String"},
418
+ {"key": "foobar", "type": "Number"},
419
+ {"key": "description", "type": "Text"},
420
+ ]
421
+ saved.settings["properties"]["filters"] == [
422
+ {"fieldKey": "foobar", "widget": "MinMax", "label": "Foo Bar"},
423
+ {"fieldKey": "name", "widget": "Checkbox", "label": "Bar Foo"},
424
+ ]
425
+
426
+
427
+ def test_can_create_filter_from_new_field(live_server, page, openmap):
428
+ openmap.settings["properties"]["onLoadPanel"] = "datafilters"
429
+ openmap.save()
430
+ DataLayerFactory(map=openmap, data=DATALAYER_DATA1)
431
+ page.goto(f"{live_server.url}{openmap.get_absolute_url()}?edit")
432
+ page.get_by_role("button", name="Manage layers").click()
433
+ page.get_by_role("button", name="Edit", exact=True).nth(1).click()
434
+ page.get_by_role("heading", name="Manage Fields").click()
435
+ page.get_by_role("button", name="Add a new field").click()
436
+ page.get_by_role("textbox", name="Field Name ✔").fill("foobar")
437
+ page.get_by_role("button", name="Add filter for this field").click()
438
+ page.get_by_role("textbox", name="Human readable name of the").fill("Foo Bar")
439
+ page.wait_for_timeout(300) # Input throttling.
440
+ page.get_by_text("Edit this field").click()
441
+ expect(page.locator(".umap-filter span").filter(has_text="Foo Bar")).to_be_visible()
442
+ with page.expect_response(re.compile(r".*/datalayer/update/")):
443
+ page.get_by_role("button", name="Save").click()
444
+ saved = DataLayer.objects.first()
445
+ assert saved.settings["fields"] == [
446
+ {
447
+ "key": "mytype",
448
+ "type": "String",
449
+ },
450
+ {
451
+ "key": "name",
452
+ "type": "String",
453
+ },
454
+ {
455
+ "key": "mynumber",
456
+ "type": "String",
457
+ },
458
+ {
459
+ "key": "mydate",
460
+ "type": "String",
461
+ },
462
+ {
463
+ "key": "foobar",
464
+ "type": "String",
465
+ },
466
+ ]
467
+
468
+ assert saved.settings["filters"] == [
469
+ {
470
+ "fieldKey": "foobar",
471
+ "label": "Foo Bar",
472
+ "widget": "Checkbox",
473
+ },
474
+ ]
475
+
476
+
477
+ def test_can_create_new_filter_on_map_from_panel(live_server, page, openmap):
478
+ openmap.settings["properties"]["onLoadPanel"] = "datafilters"
479
+ openmap.settings["properties"]["fields"] = [
480
+ {"key": "name", "type": "String"},
481
+ {"key": "foobar", "type": "Number"},
482
+ {"key": "description", "type": "Text"},
483
+ ]
484
+ openmap.save()
485
+ DataLayerFactory(map=openmap, data=DATALAYER_DATA1)
486
+ page.goto(f"{live_server.url}{openmap.get_absolute_url()}?edit")
487
+ page.get_by_role("button", name="Manage filters").click()
488
+ page.get_by_text("Map (all layers)").click()
489
+ page.get_by_role("button", name="Add filter").click()
490
+ page.get_by_label("Filter on").select_option("foobar")
491
+ page.get_by_role("textbox", name="Human readable name").fill("Foo Bar")
492
+ page.wait_for_timeout(300)
493
+ page.get_by_text("Exclusive choice").click()
494
+ page.get_by_role("button", name="OK").click()
495
+ expect(
496
+ page.locator(".panel.left").get_by_role("group", name="Foo Bar")
497
+ ).to_be_visible()
498
+ with page.expect_response(re.compile("./update/settings/.*")):
499
+ page.get_by_role("button", name="Save", exact=True).click()
500
+ saved = Map.objects.first()
501
+ assert saved.settings["properties"]["filters"] == [
502
+ {"fieldKey": "foobar", "label": "Foo Bar", "widget": "Radio"}
503
+ ]
504
+
505
+
506
+ def test_can_create_new_filter_on_datalayer_from_panel(live_server, page, openmap):
507
+ openmap.settings["properties"]["fields"] = [
508
+ {"key": "name", "type": "String"},
509
+ {"key": "foobar", "type": "Number"},
510
+ {"key": "description", "type": "Text"},
511
+ ]
512
+ openmap.save()
513
+ datalayer = DataLayerFactory(map=openmap, data=DATALAYER_DATA1)
514
+ page.goto(
515
+ f"{live_server.url}{openmap.get_absolute_url()}?edit&onLoadPanel=datafilters"
516
+ )
517
+ page.get_by_role("button", name="Manage filters").click()
518
+ page.get_by_text("Calque 1 (single layer)").click()
519
+ page.get_by_role("button", name="Add filter").click()
520
+ expect(page.get_by_label("Apply filter to")).to_have_value(f"layer:{datalayer.pk}")
521
+ page.get_by_label("Filter on").select_option("mynumber")
522
+ page.get_by_role("textbox", name="Human readable name of the").fill("Foo Bar")
523
+ page.wait_for_timeout(300)
524
+ page.get_by_text("Exclusive choice").click()
525
+ page.get_by_role("button", name="OK").click()
526
+ expect(
527
+ page.locator(".panel.left").get_by_role("group", name="Foo Bar")
528
+ ).to_be_visible()
529
+ with page.expect_response(re.compile("./datalayer/update/.*")):
530
+ page.get_by_role("button", name="Save", exact=True).click()
531
+ saved = DataLayer.objects.first()
532
+ assert saved.settings["filters"] == [
533
+ {"fieldKey": "mynumber", "label": "Foo Bar", "widget": "Radio"}
534
+ ]
535
+
536
+
537
+ def test_can_edit_filter_from_panel(live_server, page, openmap):
538
+ openmap.settings["properties"]["fields"] = [
539
+ {"key": "name", "type": "String"},
540
+ {"key": "foobar", "type": "Number"},
541
+ {"key": "description", "type": "Text"},
542
+ ]
543
+ openmap.settings["properties"]["filters"] = [
544
+ {"fieldKey": "foobar", "widget": "MinMax", "label": "Foo Bar"},
545
+ {"fieldKey": "name", "widget": "Checkbox", "label": "Bar Foo"},
546
+ ]
547
+ openmap.save()
548
+ page.goto(
549
+ f"{live_server.url}{openmap.get_absolute_url()}?edit&onLoadPanel=datafilters"
550
+ )
551
+ page.get_by_role("group", name="Foo Bar").get_by_role("button").click()
552
+ expect(page.get_by_label("Apply filter to")).to_have_value(f"map:{openmap.pk}")
553
+ expect(page.get_by_label("Apply filter to")).to_be_disabled()
554
+ page.get_by_role("textbox", name="Human readable name of the").fill("Foo Bar Baz")
555
+ page.wait_for_timeout(300) # Input throttling.
556
+ page.get_by_role("button", name="OK").click()
557
+ expect(
558
+ page.get_by_role("group", name="Foo Bar Baz").locator("legend")
559
+ ).to_be_visible()
560
+
561
+
562
+ def test_filter_with_enum(live_server, openmap, page):
563
+ data = {
564
+ "type": "FeatureCollection",
565
+ "features": [
566
+ {
567
+ "type": "Feature",
568
+ "properties": {
569
+ "name": "Point 2",
570
+ "products": "flour,vegetable,bread",
571
+ },
572
+ "geometry": {"type": "Point", "coordinates": [0.065918, 48.385442]},
573
+ },
574
+ {
575
+ "type": "Feature",
576
+ "properties": {
577
+ "name": "Point 1",
578
+ "products": "vegetable,bread",
579
+ },
580
+ "geometry": {"type": "Point", "coordinates": [3.55957, 49.767074]},
581
+ },
582
+ {
583
+ "type": "Feature",
584
+ "properties": {
585
+ "name": "Point 3",
586
+ "products": "flour,vegetable,milk,eggs",
587
+ },
588
+ "geometry": {"type": "Point", "coordinates": [0.856934, 45.290347]},
589
+ },
590
+ ],
591
+ "_umap_options": {
592
+ "name": "Calque 1",
593
+ "fields": [{"key": "products", "type": "Enum"}],
594
+ },
595
+ }
596
+ DataLayerFactory(map=openmap, data=data)
597
+ page.goto(
598
+ f"{live_server.url}{openmap.get_absolute_url()}?edit&onLoadPanel=datafilters#6/48.948/1.670"
599
+ )
600
+ page.get_by_role("button", name="Manage filters").click()
601
+ page.get_by_text("Calque 1 (single layer)").click()
602
+ page.get_by_role("button", name="Add filter").click()
603
+ page.get_by_label("Filter on").select_option("products")
604
+ page.get_by_role("button", name="OK").click()
605
+ expect(
606
+ page.locator(".umap-browser .filters span").filter(has_text="products")
607
+ ).to_be_visible()
608
+ markers = page.locator(".leaflet-marker-icon")
609
+ expect(markers).to_have_count(3)
610
+ page.get_by_role("checkbox", name="bread").check()
611
+ expect(markers).to_have_count(2)
612
+ page.get_by_role("checkbox", name="bread").uncheck()
613
+ expect(markers).to_have_count(3)
614
+ page.get_by_role("checkbox", name="vegetable").check()
615
+ expect(markers).to_have_count(3)
616
+ page.get_by_role("checkbox", name="milk").check()
617
+ expect(markers).to_have_count(1)