umap-project 3.1.2__py3-none-any.whl → 3.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of umap-project might be problematic. Click here for more details.

Files changed (151) hide show
  1. umap/__init__.py +1 -1
  2. umap/locale/en/LC_MESSAGES/django.po +21 -17
  3. umap/locale/fr/LC_MESSAGES/django.mo +0 -0
  4. umap/locale/fr/LC_MESSAGES/django.po +21 -17
  5. umap/management/commands/export_pictogram.py +29 -0
  6. umap/management/commands/migrate_to_S3.py +5 -1
  7. umap/management/commands/purge_old_versions.py +8 -6
  8. umap/settings/__init__.py +21 -0
  9. umap/settings/base.py +1 -0
  10. umap/static/umap/content.css +7 -2
  11. umap/static/umap/css/icon.css +77 -3
  12. umap/static/umap/css/panel.css +31 -1
  13. umap/static/umap/js/modules/browser.js +1 -1
  14. umap/static/umap/js/modules/data/features.js +14 -29
  15. umap/static/umap/js/modules/data/layer.js +248 -136
  16. umap/static/umap/js/modules/facets.js +2 -2
  17. umap/static/umap/js/modules/form/fields.js +56 -19
  18. umap/static/umap/js/modules/formatter.js +36 -8
  19. umap/static/umap/js/modules/importers/opendata.js +23 -6
  20. umap/static/umap/js/modules/managers.js +59 -0
  21. umap/static/umap/js/modules/rendering/icon.js +3 -5
  22. umap/static/umap/js/modules/rendering/layers/classified.js +8 -7
  23. umap/static/umap/js/modules/rendering/map.js +1 -1
  24. umap/static/umap/js/modules/rendering/ui.js +13 -0
  25. umap/static/umap/js/modules/rules.js +76 -23
  26. umap/static/umap/js/modules/schema.js +3 -0
  27. umap/static/umap/js/modules/slideshow.js +1 -1
  28. umap/static/umap/js/modules/sync/updaters.js +1 -6
  29. umap/static/umap/js/modules/tableeditor.js +13 -37
  30. umap/static/umap/js/modules/templates.js +7 -6
  31. umap/static/umap/js/modules/ui/panel.js +7 -0
  32. umap/static/umap/js/modules/umap.js +17 -6
  33. umap/static/umap/js/modules/utils.js +8 -7
  34. umap/static/umap/locale/am_ET.js +43 -6
  35. umap/static/umap/locale/am_ET.json +43 -6
  36. umap/static/umap/locale/ar.js +43 -6
  37. umap/static/umap/locale/ar.json +43 -6
  38. umap/static/umap/locale/ast.js +43 -6
  39. umap/static/umap/locale/ast.json +43 -6
  40. umap/static/umap/locale/bg.js +43 -6
  41. umap/static/umap/locale/bg.json +43 -6
  42. umap/static/umap/locale/br.js +30 -26
  43. umap/static/umap/locale/br.json +30 -26
  44. umap/static/umap/locale/ca.js +50 -13
  45. umap/static/umap/locale/ca.json +50 -13
  46. umap/static/umap/locale/cs_CZ.js +43 -6
  47. umap/static/umap/locale/cs_CZ.json +43 -6
  48. umap/static/umap/locale/da.js +10 -6
  49. umap/static/umap/locale/da.json +10 -6
  50. umap/static/umap/locale/de.js +10 -6
  51. umap/static/umap/locale/de.json +10 -6
  52. umap/static/umap/locale/el.js +20 -10
  53. umap/static/umap/locale/el.json +20 -10
  54. umap/static/umap/locale/en.js +10 -6
  55. umap/static/umap/locale/en.json +10 -6
  56. umap/static/umap/locale/en_US.json +43 -6
  57. umap/static/umap/locale/es.js +10 -6
  58. umap/static/umap/locale/es.json +10 -6
  59. umap/static/umap/locale/et.js +43 -6
  60. umap/static/umap/locale/et.json +43 -6
  61. umap/static/umap/locale/eu.js +43 -6
  62. umap/static/umap/locale/eu.json +43 -6
  63. umap/static/umap/locale/fa_IR.js +43 -6
  64. umap/static/umap/locale/fa_IR.json +43 -6
  65. umap/static/umap/locale/fi.js +43 -6
  66. umap/static/umap/locale/fi.json +43 -6
  67. umap/static/umap/locale/fr.js +10 -6
  68. umap/static/umap/locale/fr.json +10 -6
  69. umap/static/umap/locale/gl.js +43 -6
  70. umap/static/umap/locale/gl.json +43 -6
  71. umap/static/umap/locale/he.js +43 -6
  72. umap/static/umap/locale/he.json +43 -6
  73. umap/static/umap/locale/hr.js +43 -6
  74. umap/static/umap/locale/hr.json +43 -6
  75. umap/static/umap/locale/hu.js +34 -24
  76. umap/static/umap/locale/hu.json +34 -24
  77. umap/static/umap/locale/id.js +43 -6
  78. umap/static/umap/locale/id.json +43 -6
  79. umap/static/umap/locale/is.js +43 -6
  80. umap/static/umap/locale/is.json +43 -6
  81. umap/static/umap/locale/it.js +10 -6
  82. umap/static/umap/locale/it.json +10 -6
  83. umap/static/umap/locale/ja.js +43 -6
  84. umap/static/umap/locale/ja.json +43 -6
  85. umap/static/umap/locale/ko.js +43 -6
  86. umap/static/umap/locale/ko.json +43 -6
  87. umap/static/umap/locale/lt.js +43 -6
  88. umap/static/umap/locale/lt.json +43 -6
  89. umap/static/umap/locale/ms.js +43 -6
  90. umap/static/umap/locale/ms.json +43 -6
  91. umap/static/umap/locale/nl.js +10 -6
  92. umap/static/umap/locale/nl.json +10 -6
  93. umap/static/umap/locale/no.js +43 -6
  94. umap/static/umap/locale/no.json +43 -6
  95. umap/static/umap/locale/pl.js +43 -6
  96. umap/static/umap/locale/pl.json +43 -6
  97. umap/static/umap/locale/pl_PL.json +43 -6
  98. umap/static/umap/locale/pt.js +43 -6
  99. umap/static/umap/locale/pt.json +43 -6
  100. umap/static/umap/locale/pt_BR.js +53 -16
  101. umap/static/umap/locale/pt_BR.json +53 -16
  102. umap/static/umap/locale/pt_PT.js +43 -6
  103. umap/static/umap/locale/pt_PT.json +43 -6
  104. umap/static/umap/locale/ro.js +43 -6
  105. umap/static/umap/locale/ro.json +43 -6
  106. umap/static/umap/locale/ru.js +43 -6
  107. umap/static/umap/locale/ru.json +43 -6
  108. umap/static/umap/locale/sk_SK.js +43 -6
  109. umap/static/umap/locale/sk_SK.json +43 -6
  110. umap/static/umap/locale/sl.js +43 -6
  111. umap/static/umap/locale/sl.json +43 -6
  112. umap/static/umap/locale/sr.js +43 -6
  113. umap/static/umap/locale/sr.json +43 -6
  114. umap/static/umap/locale/sv.js +43 -6
  115. umap/static/umap/locale/sv.json +43 -6
  116. umap/static/umap/locale/th_TH.js +43 -6
  117. umap/static/umap/locale/th_TH.json +43 -6
  118. umap/static/umap/locale/tr.js +43 -6
  119. umap/static/umap/locale/tr.json +43 -6
  120. umap/static/umap/locale/uk_UA.js +43 -6
  121. umap/static/umap/locale/uk_UA.json +43 -6
  122. umap/static/umap/locale/vi.js +43 -6
  123. umap/static/umap/locale/vi.json +43 -6
  124. umap/static/umap/locale/vi_VN.json +43 -6
  125. umap/static/umap/locale/zh.js +43 -6
  126. umap/static/umap/locale/zh.json +43 -6
  127. umap/static/umap/locale/zh_CN.json +43 -6
  128. umap/static/umap/locale/zh_TW.Big5.json +43 -6
  129. umap/static/umap/locale/zh_TW.js +43 -6
  130. umap/static/umap/locale/zh_TW.json +43 -6
  131. umap/static/umap/map.css +239 -65
  132. umap/static/umap/vendors/betterknown/betterknown.mjs +287 -0
  133. umap/storage/fs.py +3 -2
  134. umap/templates/base.html +4 -1
  135. umap/tests/base.py +9 -1
  136. umap/tests/integration/test_basics.py +1 -1
  137. umap/tests/integration/test_conditional_rules.py +62 -20
  138. umap/tests/integration/test_edit_datalayer.py +1 -1
  139. umap/tests/integration/test_edit_marker.py +1 -1
  140. umap/tests/integration/test_export_map.py +10 -0
  141. umap/tests/integration/test_import.py +140 -0
  142. umap/tests/integration/test_optimistic_merge.py +72 -12
  143. umap/tests/integration/test_tableeditor.py +6 -3
  144. umap/utils.py +33 -0
  145. umap/views.py +16 -2
  146. umap_project-3.2.0.dist-info/METADATA +76 -0
  147. {umap_project-3.1.2.dist-info → umap_project-3.2.0.dist-info}/RECORD +150 -148
  148. umap_project-3.1.2.dist-info/METADATA +0 -68
  149. {umap_project-3.1.2.dist-info → umap_project-3.2.0.dist-info}/WHEEL +0 -0
  150. {umap_project-3.1.2.dist-info → umap_project-3.2.0.dist-info}/entry_points.txt +0 -0
  151. {umap_project-3.1.2.dist-info → umap_project-3.2.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,287 @@
1
+ var l = Object.defineProperty;
2
+ var d = (t, n, e) => n in t ? l(t, n, { enumerable: !0, configurable: !0, writable: !0, value: e }) : t[n] = e;
3
+ var p = (t, n, e) => (d(t, typeof n != "symbol" ? n + "" : n, e), e);
4
+ const M = [
5
+ "Point",
6
+ "LineString",
7
+ "Polygon",
8
+ "MultiPoint",
9
+ "MultiLineString",
10
+ "MultiPolygon",
11
+ "GeometryCollection"
12
+ ], f = ["ZM", "Z", "M"], s = "EMPTY";
13
+ class G {
14
+ constructor(n) {
15
+ p(this, "value");
16
+ p(this, "position");
17
+ this.value = n.toUpperCase(), this.position = 0;
18
+ }
19
+ match(n) {
20
+ this.skipWhitespaces();
21
+ for (const e of n) {
22
+ const r = e.toUpperCase();
23
+ if (this.value.startsWith(r, this.position))
24
+ return this.position += r.length, e;
25
+ }
26
+ return null;
27
+ }
28
+ matchRegex(n) {
29
+ this.skipWhitespaces();
30
+ for (const e of n) {
31
+ const r = this.value.substring(this.position).match(e);
32
+ if (r)
33
+ return this.position += r[0].length, r;
34
+ }
35
+ return null;
36
+ }
37
+ isMatch(n) {
38
+ return this.skipWhitespaces(), this.value.startsWith(n, this.position) ? (this.position += n.length, !0) : !1;
39
+ }
40
+ matchType() {
41
+ const n = this.match(M);
42
+ if (!n)
43
+ throw new Error("Expected geometry type");
44
+ return n;
45
+ }
46
+ matchDimension() {
47
+ switch (this.match(f)) {
48
+ case "ZM":
49
+ return { hasZ: !0, hasM: !0 };
50
+ case "Z":
51
+ return { hasZ: !0, hasM: !1 };
52
+ case "M":
53
+ return { hasZ: !1, hasM: !0 };
54
+ default:
55
+ return { hasZ: !1, hasM: !1 };
56
+ }
57
+ }
58
+ expectGroupStart() {
59
+ if (!this.isMatch("("))
60
+ throw new Error("Expected group start");
61
+ }
62
+ expectGroupEnd() {
63
+ if (!this.isMatch(")"))
64
+ throw new Error("Expected group end");
65
+ }
66
+ matchCoordinate(n) {
67
+ let e;
68
+ if (n.hasZ && n.hasM ? e = this.matchRegex([/^(\S*)\s+(\S*)\s+(\S*)\s+([^\s,)]*)/i]) : n.hasZ || n.hasM ? e = this.matchRegex([/^(\S*)\s+(\S*)\s+([^\s,)]*)/i]) : e = this.matchRegex([/^(\S*)\s+([^\s,)]*)/i]), !e)
69
+ throw new Error("Expected coordinates");
70
+ const r = n.hasZ && n.hasM ? [
71
+ parseFloat(e[1]),
72
+ parseFloat(e[2]),
73
+ parseFloat(e[3]),
74
+ parseFloat(e[4])
75
+ ] : n.hasZ ? [parseFloat(e[1]), parseFloat(e[2]), parseFloat(e[3])] : n.hasM ? [
76
+ parseFloat(e[1]),
77
+ parseFloat(e[2])
78
+ ] : [parseFloat(e[1]), parseFloat(e[2])];
79
+ if (n.srid && n.srid !== 4326) {
80
+ if (n.proj)
81
+ return n.proj(`EPSG:${n.srid}`, "EPSG:4326", r);
82
+ throw new Error(
83
+ `EWKT data in an unknown SRID (${n.srid}) was provided, but a proj function was not`
84
+ );
85
+ }
86
+ return r;
87
+ }
88
+ matchCoordinates(n) {
89
+ const e = [];
90
+ do {
91
+ const r = this.isMatch("(");
92
+ e.push(this.matchCoordinate(n)), r && this.expectGroupEnd();
93
+ } while (this.isMatch(","));
94
+ return e;
95
+ }
96
+ skipWhitespaces() {
97
+ for (; this.position < this.value.length && this.value[this.position] === " "; )
98
+ this.position++;
99
+ }
100
+ }
101
+ const g = (t, n) => {
102
+ if (t.isMatch(s))
103
+ return n.emptyAsNull ? null : { type: "Point", coordinates: [] };
104
+ t.expectGroupStart();
105
+ const e = t.matchCoordinate(n);
106
+ return t.expectGroupEnd(), {
107
+ type: "Point",
108
+ coordinates: e
109
+ };
110
+ }, E = (t, n) => {
111
+ if (t.isMatch(s))
112
+ return n.emptyAsNull ? null : { type: "LineString", coordinates: [] };
113
+ t.expectGroupStart();
114
+ const e = t.matchCoordinates(n);
115
+ return t.expectGroupEnd(), {
116
+ type: "LineString",
117
+ coordinates: e
118
+ };
119
+ }, m = (t, n) => {
120
+ if (t.isMatch(s))
121
+ return n.emptyAsNull ? null : { type: "Polygon", coordinates: [] };
122
+ const e = [];
123
+ for (t.expectGroupStart(), t.expectGroupStart(), e.push(t.matchCoordinates(n)), t.expectGroupEnd(); t.isMatch(","); )
124
+ t.expectGroupStart(), e.push(t.matchCoordinates(n)), t.expectGroupEnd();
125
+ return t.expectGroupEnd(), {
126
+ type: "Polygon",
127
+ coordinates: e
128
+ };
129
+ }, S = (t, n) => {
130
+ if (t.isMatch(s))
131
+ return n.emptyAsNull ? null : { type: "MultiPoint", coordinates: [] };
132
+ t.expectGroupStart();
133
+ const e = t.matchCoordinates(n);
134
+ return t.expectGroupEnd(), {
135
+ type: "MultiPoint",
136
+ coordinates: e
137
+ };
138
+ }, y = (t, n) => {
139
+ if (t.isMatch(s))
140
+ return n.emptyAsNull ? null : { type: "MultiLineString", coordinates: [] };
141
+ t.expectGroupStart();
142
+ const e = [];
143
+ do
144
+ t.expectGroupStart(), e.push(t.matchCoordinates(n)), t.expectGroupEnd();
145
+ while (t.isMatch(","));
146
+ return t.expectGroupEnd(), {
147
+ type: "MultiLineString",
148
+ coordinates: e
149
+ };
150
+ }, x = (t, n) => {
151
+ if (t.isMatch(s))
152
+ return n.emptyAsNull ? null : { type: "MultiPolygon", coordinates: [] };
153
+ t.expectGroupStart();
154
+ const e = [];
155
+ do {
156
+ t.expectGroupStart();
157
+ const r = [], o = [];
158
+ for (t.expectGroupStart(), r.push.apply(r, t.matchCoordinates(n)), t.expectGroupEnd(); t.isMatch(","); )
159
+ t.expectGroupStart(), o.push(t.matchCoordinates(n)), t.expectGroupEnd();
160
+ e.push([r, ...o]), t.expectGroupEnd();
161
+ } while (t.isMatch(","));
162
+ return t.expectGroupEnd(), {
163
+ type: "MultiPolygon",
164
+ coordinates: e
165
+ };
166
+ }, P = (t, n) => {
167
+ if (t.isMatch(s))
168
+ return n.emptyAsNull ? null : { type: "GeometryCollection", geometries: [] };
169
+ t.expectGroupStart();
170
+ const e = [];
171
+ do {
172
+ const r = u(t, n);
173
+ r && e.push(r);
174
+ } while (t.isMatch(","));
175
+ return t.expectGroupEnd(), {
176
+ type: "GeometryCollection",
177
+ geometries: e
178
+ };
179
+ };
180
+ function c(t) {
181
+ return t.join(" ");
182
+ }
183
+ function a(t) {
184
+ if (t === void 0)
185
+ return " ";
186
+ switch (t.length) {
187
+ case 3:
188
+ return " Z ";
189
+ default:
190
+ return " ";
191
+ }
192
+ }
193
+ function T(t) {
194
+ return t.coordinates.length === 0 ? "POINT EMPTY" : `POINT${a(t.coordinates)}(${c(
195
+ t.coordinates
196
+ )})`;
197
+ }
198
+ function L(t) {
199
+ return t.coordinates.length === 0 ? "MULTIPOINT EMPTY" : `MULTIPOINT${a(
200
+ t.coordinates[0]
201
+ )}(${t.coordinates.map((n) => c(n)).join(",")})`;
202
+ }
203
+ function N(t) {
204
+ if (t.coordinates.length === 0)
205
+ return "LINESTRING EMPTY";
206
+ const n = `(${t.coordinates.map((e) => c(e)).join(",")})`;
207
+ return `LINESTRING${a(t.coordinates[0])}${n}`;
208
+ }
209
+ function C(t) {
210
+ return t.geometries.length === 0 ? "GEOMETRYCOLLECTION EMPTY" : `GEOMETRYCOLLECTION${`(${t.geometries.map((e) => O(e)).join(",")})`}`;
211
+ }
212
+ function I(t) {
213
+ if (t.coordinates.length === 0)
214
+ return "MULTILINESTRING EMPTY";
215
+ const n = `(${t.coordinates.map((e) => `(${e.map((r) => c(r))})`)})`;
216
+ return `MULTILINESTRING${a(t.coordinates[0][0])}${n}`;
217
+ }
218
+ function W(t) {
219
+ var e;
220
+ if (t.coordinates.length === 0)
221
+ return "POLYGON EMPTY";
222
+ const n = `(${t.coordinates.map((r) => `(${r.map((o) => c(o))})`)})`;
223
+ return `POLYGON${a((e = t.coordinates[0]) == null ? void 0 : e[0])}${n}`;
224
+ }
225
+ function $(t) {
226
+ var e, r;
227
+ if (t.coordinates.length === 0)
228
+ return "MULTIPOLYGON EMPTY";
229
+ const n = `(${t.coordinates.map((o) => `(${o.map((h) => `(${h.map((i) => c(i))})`)})`)})`;
230
+ return `MULTIPOLYGON${a(
231
+ (r = (e = t.coordinates[0]) == null ? void 0 : e[0]) == null ? void 0 : r[0]
232
+ )}${n}`;
233
+ }
234
+ function u(t, n) {
235
+ let e = null;
236
+ const r = t.matchRegex([/^SRID=(\d+);/i]);
237
+ r && (e = parseInt(r[1], 10));
238
+ const o = t.matchType(), h = t.matchDimension(), i = {
239
+ ...n,
240
+ srid: e,
241
+ hasZ: h.hasZ,
242
+ hasM: h.hasM
243
+ };
244
+ switch (o) {
245
+ case "Point":
246
+ return g(t, i);
247
+ case "LineString":
248
+ return E(t, i);
249
+ case "Polygon":
250
+ return m(t, i);
251
+ case "MultiPoint":
252
+ return S(t, i);
253
+ case "MultiLineString":
254
+ return y(t, i);
255
+ case "MultiPolygon":
256
+ return x(t, i);
257
+ case "GeometryCollection":
258
+ return P(t, i);
259
+ }
260
+ }
261
+ function O(t) {
262
+ switch (t.type) {
263
+ case "Point":
264
+ return T(t);
265
+ case "LineString":
266
+ return N(t);
267
+ case "MultiPoint":
268
+ return L(t);
269
+ case "GeometryCollection":
270
+ return C(t);
271
+ case "Polygon":
272
+ return W(t);
273
+ case "MultiPolygon":
274
+ return $(t);
275
+ case "MultiLineString":
276
+ return I(t);
277
+ }
278
+ }
279
+ function Z(t, n = {
280
+ emptyAsNull: !0
281
+ }) {
282
+ return u(new G(t), n);
283
+ }
284
+ export {
285
+ O as geoJSONToWkt,
286
+ Z as wktToGeoJSON
287
+ };
umap/storage/fs.py CHANGED
@@ -79,7 +79,7 @@ class FSDataStorage(FileSystemStorage):
79
79
  "size": self.size(self._base_path(instance) / name),
80
80
  }
81
81
 
82
- def purge_old_versions(self, instance, keep=None):
82
+ def purge_old_versions(self, instance, keep=None, dry_run=False):
83
83
  root = self._base_path(instance)
84
84
  versions = self.list_versions(instance)
85
85
  if keep is not None:
@@ -92,7 +92,8 @@ class FSDataStorage(FileSystemStorage):
92
92
  if keep is not None and instance.geojson.name.endswith(name):
93
93
  continue
94
94
  try:
95
- self.delete(root / name)
95
+ if not dry_run:
96
+ self.delete(root / name)
96
97
  except FileNotFoundError:
97
98
  pass
98
99
  else:
umap/templates/base.html CHANGED
@@ -1,7 +1,10 @@
1
1
  {% load umap_tags i18n static %}
2
2
 
3
+ {% get_current_language as LANGUAGE_CODE %}
4
+
3
5
  <!DOCTYPE html>
4
- <html {% if LANGUAGE_BIDI %}dir="rtl"{% endif %}>
6
+ <html {% if LANGUAGE_BIDI %}dir="rtl"{% endif %}
7
+ lang="{{ LANGUAGE_CODE }}">
5
8
  <head>
6
9
  <title>
7
10
  {% block head_title %}
umap/tests/base.py CHANGED
@@ -128,7 +128,15 @@ class DataLayerFactory(factory.django.DjangoModelFactory):
128
128
  def _adjust_kwargs(cls, **kwargs):
129
129
  if "data" in kwargs:
130
130
  data = copy.deepcopy(kwargs.pop("data"))
131
- data.setdefault("_umap_options", {})
131
+ data.setdefault(
132
+ "_umap_options",
133
+ {
134
+ "fields": [
135
+ {"key": "name", "type": "String"},
136
+ {"key": "description", "type": "Text"},
137
+ ]
138
+ },
139
+ )
132
140
  if "name" in data["_umap_options"] and kwargs["name"] == cls.name:
133
141
  kwargs["name"] = data["_umap_options"]["name"]
134
142
  kwargs.setdefault("settings", {})
@@ -43,7 +43,7 @@ def test_create_map_with_cursor(page, live_server, tilelayer):
43
43
  "margin-left: -16px; "
44
44
  "margin-top: -40px; "
45
45
  "transform: translate3d(200px, 200px, 0px); "
46
- "z-index: 200;"
46
+ "z-index: 0;"
47
47
  ),
48
48
  )
49
49
 
@@ -27,6 +27,7 @@ DATALAYER_DATA1 = {
27
27
  "myboolean": True,
28
28
  "mydate": "2024/04/14 12:19:17",
29
29
  "maybeempty": "not empty",
30
+ "onlyinone": "blah",
30
31
  },
31
32
  "geometry": {"type": "Point", "coordinates": [0.065918, 48.385442]},
32
33
  },
@@ -39,6 +40,7 @@ DATALAYER_DATA1 = {
39
40
  "myboolean": False,
40
41
  "mydate": "2024/03/13 12:20:20",
41
42
  "maybeempty": "",
43
+ "onlyinone": "ffff",
42
44
  },
43
45
  "geometry": {"type": "Point", "coordinates": [3.55957, 49.767074]},
44
46
  },
@@ -95,7 +97,7 @@ DATALAYER_DATA2 = {
95
97
 
96
98
  def test_simple_equal_rule_at_load(live_server, page, map):
97
99
  map.settings["properties"]["rules"] = [
98
- {"condition": "mytype=odd", "options": {"color": "aliceblue"}}
100
+ {"condition": "mytype=odd", "properties": {"color": "aliceblue"}}
99
101
  ]
100
102
  map.save()
101
103
  DataLayerFactory(map=map, data=DATALAYER_DATA1)
@@ -109,7 +111,7 @@ def test_simple_equal_rule_at_load(live_server, page, map):
109
111
 
110
112
  def test_simple_not_equal_rule_at_load(live_server, page, map):
111
113
  map.settings["properties"]["rules"] = [
112
- {"condition": "mytype!=even", "options": {"color": "aliceblue"}}
114
+ {"condition": "mytype!=even", "properties": {"color": "aliceblue"}}
113
115
  ]
114
116
  map.save()
115
117
  DataLayerFactory(map=map, data=DATALAYER_DATA1)
@@ -123,7 +125,7 @@ def test_simple_not_equal_rule_at_load(live_server, page, map):
123
125
 
124
126
  def test_gt_rule_with_number_at_load(live_server, page, map):
125
127
  map.settings["properties"]["rules"] = [
126
- {"condition": "mynumber>10", "options": {"color": "aliceblue"}}
128
+ {"condition": "mynumber>10", "properties": {"color": "aliceblue"}}
127
129
  ]
128
130
  map.save()
129
131
  DataLayerFactory(map=map, data=DATALAYER_DATA1)
@@ -137,7 +139,7 @@ def test_gt_rule_with_number_at_load(live_server, page, map):
137
139
 
138
140
  def test_lt_rule_with_number_at_load(live_server, page, map):
139
141
  map.settings["properties"]["rules"] = [
140
- {"condition": "mynumber<14", "options": {"color": "aliceblue"}}
142
+ {"condition": "mynumber<14", "properties": {"color": "aliceblue"}}
141
143
  ]
142
144
  map.save()
143
145
  DataLayerFactory(map=map, data=DATALAYER_DATA1)
@@ -151,7 +153,7 @@ def test_lt_rule_with_number_at_load(live_server, page, map):
151
153
 
152
154
  def test_lt_rule_with_float_at_load(live_server, page, map):
153
155
  map.settings["properties"]["rules"] = [
154
- {"condition": "mynumber<12.3", "options": {"color": "aliceblue"}}
156
+ {"condition": "mynumber<12.3", "properties": {"color": "aliceblue"}}
155
157
  ]
156
158
  map.save()
157
159
  DataLayerFactory(map=map, data=DATALAYER_DATA1)
@@ -165,7 +167,7 @@ def test_lt_rule_with_float_at_load(live_server, page, map):
165
167
 
166
168
  def test_equal_rule_with_boolean_at_load(live_server, page, map):
167
169
  map.settings["properties"]["rules"] = [
168
- {"condition": "myboolean=true", "options": {"color": "aliceblue"}}
170
+ {"condition": "myboolean=true", "properties": {"color": "aliceblue"}}
169
171
  ]
170
172
  map.save()
171
173
  DataLayerFactory(map=map, data=DATALAYER_DATA1)
@@ -179,7 +181,7 @@ def test_equal_rule_with_boolean_at_load(live_server, page, map):
179
181
 
180
182
  def test_equal_rule_with_boolean_not_true_at_load(live_server, page, map):
181
183
  map.settings["properties"]["rules"] = [
182
- {"condition": "myboolean!=true", "options": {"color": "aliceblue"}}
184
+ {"condition": "myboolean!=true", "properties": {"color": "aliceblue"}}
183
185
  ]
184
186
  map.save()
185
187
  DataLayerFactory(map=map, data=DATALAYER_DATA1)
@@ -193,7 +195,7 @@ def test_equal_rule_with_boolean_not_true_at_load(live_server, page, map):
193
195
 
194
196
  def test_equal_rule_with_boolean_false_at_load(live_server, page, map):
195
197
  map.settings["properties"]["rules"] = [
196
- {"condition": "myboolean=false", "options": {"color": "aliceblue"}}
198
+ {"condition": "myboolean=false", "properties": {"color": "aliceblue"}}
197
199
  ]
198
200
  map.save()
199
201
  DataLayerFactory(map=map, data=DATALAYER_DATA1)
@@ -207,7 +209,7 @@ def test_equal_rule_with_boolean_false_at_load(live_server, page, map):
207
209
 
208
210
  def test_equal_rule_with_boolean_not_false_at_load(live_server, page, map):
209
211
  map.settings["properties"]["rules"] = [
210
- {"condition": "myboolean!=false", "options": {"color": "aliceblue"}}
212
+ {"condition": "myboolean!=false", "properties": {"color": "aliceblue"}}
211
213
  ]
212
214
  map.save()
213
215
  DataLayerFactory(map=map, data=DATALAYER_DATA1)
@@ -221,7 +223,7 @@ def test_equal_rule_with_boolean_not_false_at_load(live_server, page, map):
221
223
 
222
224
  def test_empty_rule_at_load(live_server, page, map):
223
225
  map.settings["properties"]["rules"] = [
224
- {"condition": "maybeempty=", "options": {"color": "aliceblue"}}
226
+ {"condition": "maybeempty=", "properties": {"color": "aliceblue"}}
225
227
  ]
226
228
  map.save()
227
229
  DataLayerFactory(map=map, data=DATALAYER_DATA1)
@@ -235,7 +237,7 @@ def test_empty_rule_at_load(live_server, page, map):
235
237
 
236
238
  def test_not_empty_rule_at_load(live_server, page, map):
237
239
  map.settings["properties"]["rules"] = [
238
- {"condition": "maybeempty!=", "options": {"color": "aliceblue"}}
240
+ {"condition": "maybeempty!=", "properties": {"color": "aliceblue"}}
239
241
  ]
240
242
  map.save()
241
243
  DataLayerFactory(map=map, data=DATALAYER_DATA1)
@@ -270,7 +272,7 @@ def test_can_create_new_rule(live_server, page, openmap):
270
272
 
271
273
  def test_can_deactive_rule_from_list(live_server, page, openmap):
272
274
  openmap.settings["properties"]["rules"] = [
273
- {"condition": "mytype=odd", "options": {"color": "aliceblue"}}
275
+ {"condition": "mytype=odd", "properties": {"color": "aliceblue"}}
274
276
  ]
275
277
  openmap.save()
276
278
  DataLayerFactory(map=openmap, data=DATALAYER_DATA1)
@@ -299,9 +301,17 @@ def test_autocomplete_datalist(live_server, page, openmap):
299
301
  page.get_by_role("button", name="Add rule").click()
300
302
  panel = page.locator(".panel.right.on")
301
303
  datalist = panel.locator(".umap-field-condition datalist option")
302
- expect(datalist).to_have_count(6)
304
+ expect(datalist).to_have_count(7)
303
305
  values = {option.inner_text() for option in datalist.all()}
304
- assert values == {"myboolean", "mytype", "mynumber", "mydate", "name", "maybeempty"}
306
+ assert values == {
307
+ "myboolean",
308
+ "mytype",
309
+ "mynumber",
310
+ "mydate",
311
+ "name",
312
+ "maybeempty",
313
+ "onlyinone",
314
+ }
305
315
  page.get_by_placeholder("key=value or key!=value").fill("mytype")
306
316
  expect(datalist).to_have_count(4)
307
317
  values = {option.inner_text() for option in datalist.all()}
@@ -314,8 +324,8 @@ def test_autocomplete_datalist(live_server, page, openmap):
314
324
 
315
325
  def test_can_combine_rules(live_server, page, map):
316
326
  map.settings["properties"]["rules"] = [
317
- {"condition": "mytype=odd", "options": {"color": "aliceblue"}},
318
- {"condition": "mynumber>10", "options": {"iconClass": "Drop"}},
327
+ {"condition": "mytype=odd", "properties": {"color": "aliceblue"}},
328
+ {"condition": "mynumber>10", "properties": {"iconClass": "Drop"}},
319
329
  ]
320
330
  map.save()
321
331
  DataLayerFactory(map=map, data=DATALAYER_DATA1)
@@ -333,8 +343,8 @@ def test_can_combine_rules(live_server, page, map):
333
343
 
334
344
  def test_first_matching_rule_wins_on_given_property(live_server, page, map):
335
345
  map.settings["properties"]["rules"] = [
336
- {"condition": "mytype=odd", "options": {"color": "aliceblue"}},
337
- {"condition": "mytype!=even", "options": {"color": "darkred"}},
346
+ {"condition": "mytype=odd", "properties": {"color": "aliceblue"}},
347
+ {"condition": "mytype!=even", "properties": {"color": "darkred"}},
338
348
  ]
339
349
  map.save()
340
350
  DataLayerFactory(map=map, data=DATALAYER_DATA1)
@@ -348,12 +358,12 @@ def test_first_matching_rule_wins_on_given_property(live_server, page, map):
348
358
 
349
359
  def test_rules_from_datalayer(live_server, page, map):
350
360
  map.settings["properties"]["rules"] = [
351
- {"condition": "mytype=odd", "options": {"color": "darkred"}}
361
+ {"condition": "mytype=odd", "properties": {"color": "darkred"}}
352
362
  ]
353
363
  map.save()
354
364
  data = deepcopy(DATALAYER_DATA1)
355
365
  data["_umap_options"]["rules"] = [
356
- {"condition": "mytype=odd", "options": {"color": "aliceblue"}}
366
+ {"condition": "mytype=odd", "properties": {"color": "aliceblue"}}
357
367
  ]
358
368
  DataLayerFactory(map=map, data=data)
359
369
  DataLayerFactory(map=map, data=DATALAYER_DATA2)
@@ -365,3 +375,35 @@ def test_rules_from_datalayer(live_server, page, map):
365
375
  assert colors.count("rgb(240, 248, 255)") == 1
366
376
  # Dark Red as for map global rules
367
377
  assert colors.count("rgb(139, 0, 0)") == 2
378
+
379
+
380
+ def test_rules_in_caption(live_server, page, map):
381
+ map.settings["properties"]["rules"] = [
382
+ {
383
+ "condition": "mytype=odd",
384
+ "name": "Rule shown twice",
385
+ "properties": {"color": "darkred"},
386
+ },
387
+ {
388
+ "condition": "onlyinone=fff",
389
+ "name": "Rule shown once",
390
+ "properties": {"color": "darkred"},
391
+ },
392
+ ]
393
+ map.settings["properties"]["onLoadPanel"] = "caption"
394
+ map.save()
395
+ data = deepcopy(DATALAYER_DATA1)
396
+ data["_umap_options"]["rules"] = [
397
+ {
398
+ "condition": "myboolean=true",
399
+ "name": "Rule shown also once",
400
+ "properties": {"color": "aliceblue"},
401
+ }
402
+ ]
403
+ DataLayerFactory(map=map, data=data)
404
+ DataLayerFactory(map=map, data=DATALAYER_DATA2)
405
+ page.goto(f"{live_server.url}{map.get_absolute_url()}#6/48.948/1.670")
406
+ panel = page.locator(".panel.left.on")
407
+ expect(panel.get_by_text("Rule shown twice")).to_have_count(2)
408
+ expect(panel.get_by_text("Rule shown once")).to_have_count(1)
409
+ expect(panel.get_by_text("Rule shown also once")).to_have_count(1)
@@ -221,7 +221,7 @@ def test_deleting_datalayer_should_remove_from_caption(
221
221
  def test_can_edit_datalayer_name_in_list(live_server, openmap, datalayer, page):
222
222
  page.goto(f"{live_server.url}{openmap.get_absolute_url()}?edit")
223
223
  page.get_by_role("button", name="Manage layers").click()
224
- page.get_by_text("test datalayer").click()
224
+ page.get_by_text("test datalayer").dblclick()
225
225
  page.get_by_text("test datalayer").fill("test datalayer foobar")
226
226
  page.get_by_role("button", name="Open browser").click()
227
227
  expect(
@@ -125,7 +125,7 @@ def test_add_property_from_feature_properties_panel(
125
125
  ):
126
126
  page.goto(f"{live_server.url}{openmap.get_absolute_url()}?edit")
127
127
  page.locator(".leaflet-marker-icon").click(modifiers=["Shift"])
128
- page.get_by_role("button", name="Add a new property").click()
128
+ page.get_by_role("button", name="Add a new field").click()
129
129
  page.locator('input[name="prompt"]').fill("newprop")
130
130
  page.get_by_role("button", name="OK").click()
131
131
  expect(page.locator(".panel.right").get_by_text("newprop")).to_be_visible()
@@ -104,6 +104,16 @@ def test_umap_export(map, live_server, bootstrap, page):
104
104
  "browsable": True,
105
105
  "displayOnLoad": True,
106
106
  "name": "test datalayer",
107
+ "fields": [
108
+ {
109
+ "key": "name",
110
+ "type": "String",
111
+ },
112
+ {
113
+ "key": "description",
114
+ "type": "Text",
115
+ },
116
+ ],
107
117
  },
108
118
  "features": [
109
119
  {