umap-project 2.7.3__py3-none-any.whl → 2.8.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 (292) hide show
  1. umap/__init__.py +1 -1
  2. umap/forms.py +4 -14
  3. umap/locale/am_ET/LC_MESSAGES/django.mo +0 -0
  4. umap/locale/am_ET/LC_MESSAGES/django.po +278 -151
  5. umap/locale/ar/LC_MESSAGES/django.mo +0 -0
  6. umap/locale/ar/LC_MESSAGES/django.po +335 -141
  7. umap/locale/bg/LC_MESSAGES/django.mo +0 -0
  8. umap/locale/bg/LC_MESSAGES/django.po +279 -152
  9. umap/locale/br/LC_MESSAGES/django.mo +0 -0
  10. umap/locale/br/LC_MESSAGES/django.po +95 -79
  11. umap/locale/ca/LC_MESSAGES/django.mo +0 -0
  12. umap/locale/ca/LC_MESSAGES/django.po +85 -68
  13. umap/locale/cs_CZ/LC_MESSAGES/django.mo +0 -0
  14. umap/locale/cs_CZ/LC_MESSAGES/django.po +78 -66
  15. umap/locale/da/LC_MESSAGES/django.mo +0 -0
  16. umap/locale/da/LC_MESSAGES/django.po +280 -153
  17. umap/locale/de/LC_MESSAGES/django.mo +0 -0
  18. umap/locale/de/LC_MESSAGES/django.po +80 -64
  19. umap/locale/el/LC_MESSAGES/django.mo +0 -0
  20. umap/locale/el/LC_MESSAGES/django.po +82 -66
  21. umap/locale/en/LC_MESSAGES/django.po +73 -61
  22. umap/locale/es/LC_MESSAGES/django.mo +0 -0
  23. umap/locale/es/LC_MESSAGES/django.po +75 -63
  24. umap/locale/et/LC_MESSAGES/django.mo +0 -0
  25. umap/locale/et/LC_MESSAGES/django.po +280 -153
  26. umap/locale/eu/LC_MESSAGES/django.mo +0 -0
  27. umap/locale/eu/LC_MESSAGES/django.po +82 -66
  28. umap/locale/fa_IR/LC_MESSAGES/django.mo +0 -0
  29. umap/locale/fa_IR/LC_MESSAGES/django.po +80 -64
  30. umap/locale/fi/LC_MESSAGES/django.mo +0 -0
  31. umap/locale/fi/LC_MESSAGES/django.po +278 -151
  32. umap/locale/fr/LC_MESSAGES/django.mo +0 -0
  33. umap/locale/fr/LC_MESSAGES/django.po +75 -63
  34. umap/locale/gl/LC_MESSAGES/django.mo +0 -0
  35. umap/locale/gl/LC_MESSAGES/django.po +280 -153
  36. umap/locale/he/LC_MESSAGES/django.mo +0 -0
  37. umap/locale/he/LC_MESSAGES/django.po +281 -154
  38. umap/locale/hu/LC_MESSAGES/django.mo +0 -0
  39. umap/locale/hu/LC_MESSAGES/django.po +80 -64
  40. umap/locale/is/LC_MESSAGES/django.mo +0 -0
  41. umap/locale/is/LC_MESSAGES/django.po +280 -153
  42. umap/locale/it/LC_MESSAGES/django.mo +0 -0
  43. umap/locale/it/LC_MESSAGES/django.po +82 -66
  44. umap/locale/ja/LC_MESSAGES/django.mo +0 -0
  45. umap/locale/ja/LC_MESSAGES/django.po +280 -153
  46. umap/locale/ko/LC_MESSAGES/django.mo +0 -0
  47. umap/locale/ko/LC_MESSAGES/django.po +280 -153
  48. umap/locale/lt/LC_MESSAGES/django.mo +0 -0
  49. umap/locale/lt/LC_MESSAGES/django.po +280 -153
  50. umap/locale/ms/LC_MESSAGES/django.mo +0 -0
  51. umap/locale/ms/LC_MESSAGES/django.po +82 -66
  52. umap/locale/nl/LC_MESSAGES/django.mo +0 -0
  53. umap/locale/nl/LC_MESSAGES/django.po +280 -153
  54. umap/locale/pl/LC_MESSAGES/django.mo +0 -0
  55. umap/locale/pl/LC_MESSAGES/django.po +82 -66
  56. umap/locale/pt/LC_MESSAGES/django.mo +0 -0
  57. umap/locale/pt/LC_MESSAGES/django.po +75 -63
  58. umap/locale/pt_BR/LC_MESSAGES/django.mo +0 -0
  59. umap/locale/pt_BR/LC_MESSAGES/django.po +280 -153
  60. umap/locale/pt_PT/LC_MESSAGES/django.mo +0 -0
  61. umap/locale/pt_PT/LC_MESSAGES/django.po +280 -153
  62. umap/locale/ru/LC_MESSAGES/django.mo +0 -0
  63. umap/locale/ru/LC_MESSAGES/django.po +280 -153
  64. umap/locale/sk_SK/LC_MESSAGES/django.mo +0 -0
  65. umap/locale/sk_SK/LC_MESSAGES/django.po +280 -153
  66. umap/locale/sl/LC_MESSAGES/django.mo +0 -0
  67. umap/locale/sl/LC_MESSAGES/django.po +280 -153
  68. umap/locale/sr/LC_MESSAGES/django.mo +0 -0
  69. umap/locale/sr/LC_MESSAGES/django.po +280 -153
  70. umap/locale/sv/LC_MESSAGES/django.mo +0 -0
  71. umap/locale/sv/LC_MESSAGES/django.po +81 -65
  72. umap/locale/th_TH/LC_MESSAGES/django.mo +0 -0
  73. umap/locale/th_TH/LC_MESSAGES/django.po +257 -185
  74. umap/locale/tr/LC_MESSAGES/django.mo +0 -0
  75. umap/locale/tr/LC_MESSAGES/django.po +280 -153
  76. umap/locale/uk_UA/LC_MESSAGES/django.mo +0 -0
  77. umap/locale/uk_UA/LC_MESSAGES/django.po +280 -153
  78. umap/locale/vi/LC_MESSAGES/django.mo +0 -0
  79. umap/locale/vi/LC_MESSAGES/django.po +278 -151
  80. umap/locale/zh/LC_MESSAGES/django.mo +0 -0
  81. umap/locale/zh/LC_MESSAGES/django.po +278 -151
  82. umap/locale/zh_TW/LC_MESSAGES/django.mo +0 -0
  83. umap/locale/zh_TW/LC_MESSAGES/django.po +97 -81
  84. umap/management/commands/empty_trash.py +35 -0
  85. umap/management/commands/migrate_to_S3.py +29 -0
  86. umap/migrations/0023_alter_datalayer_uuid.py +19 -0
  87. umap/migrations/0024_alter_map_share_status.py +30 -0
  88. umap/migrations/0025_alter_datalayer_geojson.py +24 -0
  89. umap/models.py +68 -116
  90. umap/settings/base.py +23 -3
  91. umap/settings/local_s3.py +45 -0
  92. umap/static/umap/base.css +3 -603
  93. umap/static/umap/content.css +5 -3
  94. umap/static/umap/css/bar.css +202 -0
  95. umap/static/umap/css/form.css +620 -0
  96. umap/static/umap/css/icon.css +21 -1
  97. umap/static/umap/css/popup.css +125 -0
  98. umap/static/umap/img/16-white.svg +16 -4
  99. umap/static/umap/img/16.svg +1 -1
  100. umap/static/umap/img/source/16-white.svg +46 -45
  101. umap/static/umap/img/source/16.svg +1 -753
  102. umap/static/umap/js/components/fragment.js +3 -1
  103. umap/static/umap/js/modules/browser.js +20 -19
  104. umap/static/umap/js/modules/caption.js +21 -22
  105. umap/static/umap/js/modules/data/features.js +120 -78
  106. umap/static/umap/js/modules/data/layer.js +195 -153
  107. umap/static/umap/js/modules/facets.js +9 -9
  108. umap/static/umap/js/modules/formatter.js +5 -5
  109. umap/static/umap/js/modules/global.js +4 -52
  110. umap/static/umap/js/modules/help.js +18 -21
  111. umap/static/umap/js/modules/importer.js +133 -56
  112. umap/static/umap/js/modules/importers/cadastrefr.js +4 -0
  113. umap/static/umap/js/modules/importers/geodatamine.js +3 -3
  114. umap/static/umap/js/modules/importers/overpass.js +5 -0
  115. umap/static/umap/js/modules/permissions.js +85 -87
  116. umap/static/umap/js/modules/rendering/icon.js +2 -1
  117. umap/static/umap/js/modules/rendering/layers/base.js +15 -15
  118. umap/static/umap/js/modules/rendering/layers/classified.js +1 -1
  119. umap/static/umap/js/modules/rendering/layers/cluster.js +1 -1
  120. umap/static/umap/js/modules/rendering/layers/heat.js +1 -1
  121. umap/static/umap/js/modules/rendering/map.js +390 -0
  122. umap/static/umap/js/modules/rendering/popup.js +19 -19
  123. umap/static/umap/js/modules/rendering/template.js +88 -21
  124. umap/static/umap/js/modules/rendering/ui.js +63 -14
  125. umap/static/umap/js/modules/request.js +2 -2
  126. umap/static/umap/js/modules/rules.js +22 -25
  127. umap/static/umap/js/modules/saving.js +47 -0
  128. umap/static/umap/js/modules/schema.js +6 -0
  129. umap/static/umap/js/modules/share.js +21 -24
  130. umap/static/umap/js/modules/slideshow.js +24 -20
  131. umap/static/umap/js/modules/sync/updaters.js +7 -9
  132. umap/static/umap/js/modules/tableeditor.js +20 -19
  133. umap/static/umap/js/modules/ui/bar.js +196 -0
  134. umap/static/umap/js/modules/ui/dialog.js +5 -0
  135. umap/static/umap/js/modules/ui/panel.js +10 -9
  136. umap/static/umap/js/modules/umap.js +1691 -0
  137. umap/static/umap/js/modules/urls.js +2 -2
  138. umap/static/umap/js/modules/utils.js +22 -6
  139. umap/static/umap/js/umap.controls.js +81 -305
  140. umap/static/umap/js/umap.core.js +29 -50
  141. umap/static/umap/js/umap.forms.js +78 -27
  142. umap/static/umap/keycloak.png +0 -0
  143. umap/static/umap/locale/am_ET.js +26 -10
  144. umap/static/umap/locale/am_ET.json +26 -10
  145. umap/static/umap/locale/ar.js +26 -10
  146. umap/static/umap/locale/ar.json +26 -10
  147. umap/static/umap/locale/ast.js +26 -10
  148. umap/static/umap/locale/ast.json +26 -10
  149. umap/static/umap/locale/bg.js +26 -10
  150. umap/static/umap/locale/bg.json +26 -10
  151. umap/static/umap/locale/br.js +27 -20
  152. umap/static/umap/locale/br.json +27 -20
  153. umap/static/umap/locale/ca.js +32 -29
  154. umap/static/umap/locale/ca.json +32 -29
  155. umap/static/umap/locale/cs_CZ.js +24 -17
  156. umap/static/umap/locale/cs_CZ.json +24 -17
  157. umap/static/umap/locale/da.js +26 -10
  158. umap/static/umap/locale/da.json +26 -10
  159. umap/static/umap/locale/de.js +21 -14
  160. umap/static/umap/locale/de.json +21 -14
  161. umap/static/umap/locale/el.js +28 -12
  162. umap/static/umap/locale/el.json +28 -12
  163. umap/static/umap/locale/en.js +14 -9
  164. umap/static/umap/locale/en.json +14 -9
  165. umap/static/umap/locale/en_US.json +26 -10
  166. umap/static/umap/locale/es.js +16 -13
  167. umap/static/umap/locale/es.json +16 -13
  168. umap/static/umap/locale/et.js +26 -10
  169. umap/static/umap/locale/et.json +26 -10
  170. umap/static/umap/locale/eu.js +16 -9
  171. umap/static/umap/locale/eu.json +16 -9
  172. umap/static/umap/locale/fa_IR.js +16 -9
  173. umap/static/umap/locale/fa_IR.json +16 -9
  174. umap/static/umap/locale/fi.js +26 -10
  175. umap/static/umap/locale/fi.json +26 -10
  176. umap/static/umap/locale/fr.js +14 -9
  177. umap/static/umap/locale/fr.json +14 -9
  178. umap/static/umap/locale/gl.js +26 -10
  179. umap/static/umap/locale/gl.json +26 -10
  180. umap/static/umap/locale/he.js +26 -10
  181. umap/static/umap/locale/he.json +26 -10
  182. umap/static/umap/locale/hr.js +26 -10
  183. umap/static/umap/locale/hr.json +26 -10
  184. umap/static/umap/locale/hu.js +16 -9
  185. umap/static/umap/locale/hu.json +16 -9
  186. umap/static/umap/locale/id.js +26 -10
  187. umap/static/umap/locale/id.json +26 -10
  188. umap/static/umap/locale/is.js +26 -10
  189. umap/static/umap/locale/is.json +26 -10
  190. umap/static/umap/locale/it.js +26 -10
  191. umap/static/umap/locale/it.json +26 -10
  192. umap/static/umap/locale/ja.js +26 -10
  193. umap/static/umap/locale/ja.json +26 -10
  194. umap/static/umap/locale/ko.js +26 -10
  195. umap/static/umap/locale/ko.json +26 -10
  196. umap/static/umap/locale/lt.js +26 -10
  197. umap/static/umap/locale/lt.json +26 -10
  198. umap/static/umap/locale/ms.js +28 -12
  199. umap/static/umap/locale/ms.json +28 -12
  200. umap/static/umap/locale/nl.js +28 -12
  201. umap/static/umap/locale/nl.json +28 -12
  202. umap/static/umap/locale/no.js +26 -10
  203. umap/static/umap/locale/no.json +26 -10
  204. umap/static/umap/locale/pl.js +28 -12
  205. umap/static/umap/locale/pl.json +28 -12
  206. umap/static/umap/locale/pl_PL.json +26 -10
  207. umap/static/umap/locale/pt.js +16 -9
  208. umap/static/umap/locale/pt.json +16 -9
  209. umap/static/umap/locale/pt_BR.js +26 -10
  210. umap/static/umap/locale/pt_BR.json +26 -10
  211. umap/static/umap/locale/pt_PT.js +16 -9
  212. umap/static/umap/locale/pt_PT.json +16 -9
  213. umap/static/umap/locale/ro.js +26 -10
  214. umap/static/umap/locale/ro.json +26 -10
  215. umap/static/umap/locale/ru.js +26 -10
  216. umap/static/umap/locale/ru.json +26 -10
  217. umap/static/umap/locale/si.js +7 -7
  218. umap/static/umap/locale/si.json +7 -7
  219. umap/static/umap/locale/sk_SK.js +26 -10
  220. umap/static/umap/locale/sk_SK.json +26 -10
  221. umap/static/umap/locale/sl.js +26 -10
  222. umap/static/umap/locale/sl.json +26 -10
  223. umap/static/umap/locale/sr.js +26 -10
  224. umap/static/umap/locale/sr.json +26 -10
  225. umap/static/umap/locale/sv.js +27 -11
  226. umap/static/umap/locale/sv.json +27 -11
  227. umap/static/umap/locale/th_TH.js +28 -12
  228. umap/static/umap/locale/th_TH.json +28 -12
  229. umap/static/umap/locale/tr.js +26 -10
  230. umap/static/umap/locale/tr.json +26 -10
  231. umap/static/umap/locale/uk_UA.js +26 -10
  232. umap/static/umap/locale/uk_UA.json +26 -10
  233. umap/static/umap/locale/vi.js +26 -10
  234. umap/static/umap/locale/vi.json +26 -10
  235. umap/static/umap/locale/vi_VN.json +26 -10
  236. umap/static/umap/locale/zh.js +26 -10
  237. umap/static/umap/locale/zh.json +26 -10
  238. umap/static/umap/locale/zh_CN.json +26 -10
  239. umap/static/umap/locale/zh_TW.Big5.json +26 -10
  240. umap/static/umap/locale/zh_TW.js +34 -27
  241. umap/static/umap/locale/zh_TW.json +34 -27
  242. umap/static/umap/map.css +39 -530
  243. umap/static/umap/unittests/URLs.js +15 -15
  244. umap/static/umap/unittests/utils.js +23 -1
  245. umap/static/umap/vars.css +2 -1
  246. umap/static/umap/vendors/formbuilder/Leaflet.FormBuilder.js +5 -1
  247. umap/storage/__init__.py +3 -0
  248. umap/storage/fs.py +101 -0
  249. umap/storage/s3.py +61 -0
  250. umap/templates/base.html +2 -0
  251. umap/templates/registration/login.html +7 -6
  252. umap/templates/umap/components/alerts/alert.html +4 -0
  253. umap/templates/umap/css.html +6 -0
  254. umap/templates/umap/js.html +3 -2
  255. umap/templates/umap/map_init.html +6 -5
  256. umap/templates/umap/user_dashboard.html +20 -19
  257. umap/tests/base.py +5 -1
  258. umap/tests/fixtures/test_upload_simple_marker.json +19 -0
  259. umap/tests/integration/conftest.py +2 -1
  260. umap/tests/integration/test_anonymous_owned_map.py +18 -10
  261. umap/tests/integration/test_browser.py +16 -1
  262. umap/tests/integration/test_dashboard.py +1 -1
  263. umap/tests/integration/test_edit_datalayer.py +29 -7
  264. umap/tests/integration/test_import.py +28 -6
  265. umap/tests/integration/test_optimistic_merge.py +31 -8
  266. umap/tests/integration/test_owned_map.py +22 -16
  267. umap/tests/integration/test_popup.py +44 -0
  268. umap/tests/integration/test_save.py +35 -0
  269. umap/tests/integration/test_view_marker.py +12 -0
  270. umap/tests/integration/test_view_polyline.py +257 -0
  271. umap/tests/integration/test_websocket_sync.py +81 -9
  272. umap/tests/test_dashboard.py +82 -0
  273. umap/tests/test_datalayer.py +6 -7
  274. umap/tests/test_datalayer_s3.py +135 -0
  275. umap/tests/test_datalayer_views.py +28 -10
  276. umap/tests/test_empty_trash.py +34 -0
  277. umap/tests/test_map.py +12 -3
  278. umap/tests/test_map_views.py +69 -37
  279. umap/tests/test_statics.py +1 -1
  280. umap/tests/test_team_views.py +35 -1
  281. umap/tests/test_views.py +31 -52
  282. umap/urls.py +3 -3
  283. umap/views.py +126 -90
  284. {umap_project-2.7.3.dist-info → umap_project-2.8.0.dist-info}/METADATA +16 -13
  285. {umap_project-2.7.3.dist-info → umap_project-2.8.0.dist-info}/RECORD +289 -269
  286. umap/management/commands/purge_purgatory.py +0 -28
  287. umap/static/umap/js/umap.js +0 -1903
  288. umap/tests/test_purge_purgatory.py +0 -25
  289. /umap/{storage.py → storage/staticfiles.py} +0 -0
  290. {umap_project-2.7.3.dist-info → umap_project-2.8.0.dist-info}/WHEEL +0 -0
  291. {umap_project-2.7.3.dist-info → umap_project-2.8.0.dist-info}/entry_points.txt +0 -0
  292. {umap_project-2.7.3.dist-info → umap_project-2.8.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,6 +1,7 @@
1
1
  import json
2
2
  from copy import deepcopy
3
3
  from pathlib import Path
4
+ from uuid import uuid4
4
5
 
5
6
  import pytest
6
7
  from django.core.files.base import ContentFile
@@ -105,6 +106,21 @@ def test_gzip_should_be_created_if_accepted(client, datalayer, map, post_data):
105
106
  assert Path(flat).stat().st_mtime_ns == Path(gzipped).stat().st_mtime_ns
106
107
 
107
108
 
109
+ def test_create_datalayer(client, map, post_data):
110
+ uuid = str(uuid4())
111
+ url = reverse("datalayer_create", args=(map.pk, uuid))
112
+ client.login(username=map.owner.username, password="123123")
113
+ response = client.post(url, post_data, follow=True)
114
+ assert response.status_code == 200
115
+ new_datalayer = DataLayer.objects.get(pk=uuid)
116
+ assert new_datalayer.name == "name"
117
+ assert new_datalayer.rank == 0
118
+ # Test response is a json
119
+ data = json.loads(response.content.decode())
120
+ assert "id" in data
121
+ assert data["id"] == uuid
122
+
123
+
108
124
  def test_update(client, datalayer, map, post_data):
109
125
  url = reverse("datalayer_update", args=(map.pk, datalayer.pk))
110
126
  client.login(username=map.owner.username, password="123123")
@@ -215,7 +231,7 @@ def test_optimistic_concurrency_control_with_empty_version(
215
231
  def test_versions_should_return_versions(client, datalayer, map, settings):
216
232
  map.share_status = Map.PUBLIC
217
233
  map.save()
218
- root = datalayer.storage_root()
234
+ root = datalayer.geojson.storage._base_path(datalayer)
219
235
  datalayer.geojson.storage.save(
220
236
  "%s/%s_1440924889.geojson" % (root, datalayer.pk), ContentFile("{}")
221
237
  )
@@ -232,6 +248,7 @@ def test_versions_should_return_versions(client, datalayer, map, settings):
232
248
  "name": "%s_1440918637.geojson" % datalayer.pk,
233
249
  "size": 2,
234
250
  "at": "1440918637",
251
+ "ref": "1440918637",
235
252
  }
236
253
  assert version in versions["versions"]
237
254
 
@@ -239,7 +256,7 @@ def test_versions_should_return_versions(client, datalayer, map, settings):
239
256
  def test_versions_can_return_old_format(client, datalayer, map, settings):
240
257
  map.share_status = Map.PUBLIC
241
258
  map.save()
242
- root = datalayer.storage_root()
259
+ root = datalayer.geojson.storage._base_path(datalayer)
243
260
  datalayer.old_id = 123 # old datalayer id (now replaced by uuid)
244
261
  datalayer.save()
245
262
 
@@ -263,31 +280,32 @@ def test_versions_can_return_old_format(client, datalayer, map, settings):
263
280
  "name": old_format_version,
264
281
  "size": 2,
265
282
  "at": "1440918637",
283
+ "ref": "1440918637",
266
284
  }
267
285
  assert version in versions["versions"]
268
286
 
269
- client.get(
270
- reverse("datalayer_version", args=(map.pk, datalayer.pk, old_format_version))
271
- )
287
+ client.get(reverse("datalayer_version", args=(map.pk, datalayer.pk, "1440918637")))
272
288
 
273
289
 
274
290
  def test_version_should_return_one_version_geojson(client, datalayer, map):
275
291
  map.share_status = Map.PUBLIC
276
292
  map.save()
277
- root = datalayer.storage_root()
293
+ root = datalayer.geojson.storage._base_path(datalayer)
278
294
  name = "%s_1440924889.geojson" % datalayer.pk
279
295
  datalayer.geojson.storage.save("%s/%s" % (root, name), ContentFile("{}"))
280
- url = reverse("datalayer_version", args=(map.pk, datalayer.pk, name))
281
- assert client.get(url).content.decode() == "{}"
296
+ url = reverse("datalayer_version", args=(map.pk, datalayer.pk, "1440924889"))
297
+ resp = client.get(url)
298
+ assert resp.status_code == 200
299
+ assert resp.content.decode() == "{}"
282
300
 
283
301
 
284
302
  def test_version_should_return_403_if_not_allowed(client, datalayer, map):
285
303
  map.share_status = Map.PRIVATE
286
304
  map.save()
287
- root = datalayer.storage_root()
305
+ root = datalayer.geojson.storage._base_path(datalayer)
288
306
  name = "%s_1440924889.geojson" % datalayer.pk
289
307
  datalayer.geojson.storage.save("%s/%s" % (root, name), ContentFile("{}"))
290
- url = reverse("datalayer_version", args=(map.pk, datalayer.pk, name))
308
+ url = reverse("datalayer_version", args=(map.pk, datalayer.pk, "1440924889"))
291
309
  assert client.get(url).status_code == 403
292
310
 
293
311
 
@@ -0,0 +1,34 @@
1
+ from datetime import datetime, timedelta
2
+ from unittest import mock
3
+
4
+ import pytest
5
+ from django.core.management import call_command
6
+
7
+ from umap.models import Map
8
+
9
+ from .base import MapFactory
10
+
11
+ pytestmark = pytest.mark.django_db
12
+
13
+
14
+ def test_empty_trash(user):
15
+ recent = MapFactory(owner=user)
16
+ recent_deleted = MapFactory(owner=user)
17
+ recent_deleted.move_to_trash()
18
+ recent_deleted.save()
19
+ with mock.patch("django.utils.timezone.now") as mocked:
20
+ mocked.return_value = datetime.utcnow() - timedelta(days=8)
21
+ old_deleted = MapFactory(owner=user)
22
+ old_deleted.move_to_trash()
23
+ old_deleted.save()
24
+ old = MapFactory(owner=user)
25
+ assert Map.objects.count() == 4
26
+ call_command("empty_trash", "--days=7", "--dry-run")
27
+ assert Map.objects.count() == 4
28
+ call_command("empty_trash", "--days=9")
29
+ assert Map.objects.count() == 4
30
+ call_command("empty_trash", "--days=7")
31
+ assert not Map.objects.filter(pk=old_deleted.pk)
32
+ assert Map.objects.filter(pk=old.pk)
33
+ assert Map.objects.filter(pk=recent.pk)
34
+ assert Map.objects.filter(pk=recent_deleted.pk)
umap/tests/test_map.py CHANGED
@@ -2,6 +2,7 @@ import pytest
2
2
  from django.contrib.auth.models import AnonymousUser
3
3
  from django.urls import reverse
4
4
 
5
+ from umap.forms import DEFAULT_CENTER
5
6
  from umap.models import Map
6
7
 
7
8
  from .base import MapFactory
@@ -160,8 +161,16 @@ def test_can_change_default_edit_status(user, settings):
160
161
 
161
162
 
162
163
  def test_can_change_default_share_status(user, settings):
164
+ map = Map.objects.create(owner=user, center=DEFAULT_CENTER)
165
+ assert map.share_status == Map.DRAFT
166
+ settings.UMAP_DEFAULT_SHARE_STATUS = Map.PUBLIC
167
+ map = Map.objects.create(owner=user, center=DEFAULT_CENTER)
163
168
  map = MapFactory(owner=user)
164
169
  assert map.share_status == Map.PUBLIC
165
- settings.UMAP_DEFAULT_SHARE_STATUS = Map.PRIVATE
166
- map = MapFactory(owner=user)
167
- assert map.share_status == Map.PRIVATE
170
+
171
+
172
+ def test_move_to_trash(user, map):
173
+ map.move_to_trash()
174
+ map.save()
175
+ reloaded = Map.objects.get(pk=map.pk)
176
+ assert reloaded.share_status == Map.DELETED
@@ -42,7 +42,7 @@ def test_create(client, user, post_data):
42
42
  assert created_map.center.y == 48.94415123418794
43
43
  assert j["permissions"] == {
44
44
  "edit_status": 3,
45
- "share_status": 1,
45
+ "share_status": 0,
46
46
  "owner": {"id": user.pk, "name": "Joe", "url": "/en/user/Joe/"},
47
47
  "editors": [],
48
48
  }
@@ -114,8 +114,10 @@ def test_delete(client, map, datalayer):
114
114
  url, headers={"X-Requested-With": "XMLHttpRequest"}, follow=True
115
115
  )
116
116
  assert response.status_code == 200
117
- assert not Map.objects.filter(pk=map.pk).exists()
118
- assert not DataLayer.objects.filter(pk=datalayer.pk).exists()
117
+ assert Map.objects.filter(pk=map.pk).exists()
118
+ assert DataLayer.objects.filter(pk=datalayer.pk).exists()
119
+ reloaded = Map.objects.get(pk=map.pk)
120
+ assert reloaded.share_status == Map.DELETED
119
121
  # Check that user has not been impacted
120
122
  assert User.objects.filter(pk=map.owner.pk).exists()
121
123
  # Test response is a json
@@ -241,42 +243,46 @@ def test_map_creation_should_allow_unicode_names(client, map, post_data):
241
243
  assert created_map.slug == "map"
242
244
 
243
245
 
244
- def test_anonymous_can_access_map_with_share_status_public(client, map):
245
- url = reverse("map", args=(map.slug, map.pk))
246
- map.share_status = map.PUBLIC
247
- map.save()
248
- response = client.get(url)
249
- assert response.status_code == 200
250
-
251
-
252
- def test_anonymous_can_access_map_with_share_status_open(client, map):
246
+ @pytest.mark.parametrize("share_status", [Map.PUBLIC, Map.OPEN])
247
+ def test_anonymous_can_access_map_with_share_status_accessible(
248
+ client, map, share_status
249
+ ):
253
250
  url = reverse("map", args=(map.slug, map.pk))
254
- map.share_status = map.OPEN
251
+ map.share_status = share_status
255
252
  map.save()
256
253
  response = client.get(url)
257
254
  assert response.status_code == 200
258
255
 
259
256
 
260
- def test_anonymous_cannot_access_map_with_share_status_private(client, map):
257
+ @pytest.mark.parametrize(
258
+ "share_status", [Map.PRIVATE, Map.DRAFT, Map.BLOCKED, Map.DELETED]
259
+ )
260
+ def test_anonymous_cannot_access_map_with_share_status_restricted(
261
+ client, map, share_status
262
+ ):
261
263
  url = reverse("map", args=(map.slug, map.pk))
262
- map.share_status = map.PRIVATE
264
+ map.share_status = share_status
263
265
  map.save()
264
266
  response = client.get(url)
265
267
  assert response.status_code == 403
266
268
 
267
269
 
268
- def test_owner_can_access_map_with_share_status_private(client, map):
270
+ @pytest.mark.parametrize("share_status", [Map.PRIVATE, Map.DRAFT])
271
+ def test_owner_can_access_map_with_share_status_restricted(client, map, share_status):
269
272
  url = reverse("map", args=(map.slug, map.pk))
270
- map.share_status = map.PRIVATE
273
+ map.share_status = share_status
271
274
  map.save()
272
275
  client.login(username=map.owner.username, password="123123")
273
276
  response = client.get(url)
274
277
  assert response.status_code == 200
275
278
 
276
279
 
277
- def test_editors_can_access_map_with_share_status_private(client, map, user):
280
+ @pytest.mark.parametrize("share_status", [Map.PRIVATE, Map.DRAFT])
281
+ def test_editors_can_access_map_with_share_status_resricted(
282
+ client, map, user, share_status
283
+ ):
278
284
  url = reverse("map", args=(map.slug, map.pk))
279
- map.share_status = map.PRIVATE
285
+ map.share_status = share_status
280
286
  map.editors.add(user)
281
287
  map.save()
282
288
  client.login(username=user.username, password="123123")
@@ -284,10 +290,11 @@ def test_editors_can_access_map_with_share_status_private(client, map, user):
284
290
  assert response.status_code == 200
285
291
 
286
292
 
287
- def test_anonymous_cannot_access_map_with_share_status_blocked(client, map):
293
+ def test_owner_cannot_access_map_with_share_status_deleted(client, map):
288
294
  url = reverse("map", args=(map.slug, map.pk))
289
- map.share_status = map.BLOCKED
295
+ map.share_status = map.DELETED
290
296
  map.save()
297
+ client.login(username=map.owner.username, password="123123")
291
298
  response = client.get(url)
292
299
  assert response.status_code == 403
293
300
 
@@ -401,14 +408,16 @@ def test_anonymous_delete(cookieclient, anonymap):
401
408
  url, headers={"X-Requested-With": "XMLHttpRequest"}, follow=True
402
409
  )
403
410
  assert response.status_code == 200
404
- assert not Map.objects.filter(pk=anonymap.pk).count()
411
+ assert Map.objects.filter(pk=anonymap.pk).exists()
412
+ reloaded = Map.objects.get(pk=anonymap.pk)
413
+ assert reloaded.share_status == Map.DELETED
405
414
  # Test response is a json
406
415
  j = json.loads(response.content.decode())
407
416
  assert "redirect" in j
408
417
 
409
418
 
410
419
  @pytest.mark.usefixtures("allow_anonymous")
411
- def test_no_cookie_cant_delete(client, anonymap):
420
+ def test_no_cookie_cannot_delete(client, anonymap):
412
421
  url = reverse("map_delete", args=(anonymap.pk,))
413
422
  response = client.post(
414
423
  url, headers={"X-Requested-With": "XMLHttpRequest"}, follow=True
@@ -416,6 +425,24 @@ def test_no_cookie_cant_delete(client, anonymap):
416
425
  assert response.status_code == 403
417
426
 
418
427
 
428
+ @pytest.mark.usefixtures("allow_anonymous")
429
+ def test_no_cookie_cannot_view_anonymous_owned_map_in_draft(client, anonymap):
430
+ anonymap.share_status = Map.DRAFT
431
+ anonymap.save()
432
+ url = reverse("map", kwargs={"map_id": anonymap.pk, "slug": anonymap.slug})
433
+ response = client.get(url)
434
+ assert response.status_code == 403
435
+
436
+
437
+ @pytest.mark.usefixtures("allow_anonymous")
438
+ def test_owner_can_view_anonymous_owned_map_in_draft(cookieclient, anonymap):
439
+ anonymap.share_status = Map.DRAFT
440
+ anonymap.save()
441
+ url = reverse("map", kwargs={"map_id": anonymap.pk, "slug": anonymap.slug})
442
+ response = cookieclient.get(url)
443
+ assert response.status_code == 200
444
+
445
+
419
446
  @pytest.mark.usefixtures("allow_anonymous")
420
447
  def test_anonymous_edit_url(cookieclient, anonymap):
421
448
  url = anonymap.get_anonymous_edit_url()
@@ -557,16 +584,6 @@ def test_create_readonly(client, user, post_data, settings):
557
584
  assert response.content == b"Site is readonly for maintenance"
558
585
 
559
586
 
560
- def test_search(client, map):
561
- # Very basic search, that do not deal with accent nor case.
562
- # See install.md for how to have a smarter dict + index.
563
- map.name = "Blé dur"
564
- map.save()
565
- url = reverse("search")
566
- response = client.get(url + "?q=Blé")
567
- assert "Blé dur" in response.content.decode()
568
-
569
-
570
587
  def test_authenticated_user_can_star_map(client, map, user):
571
588
  url = reverse("map_star", args=(map.pk,))
572
589
  client.login(username=user.username, password="123123")
@@ -748,7 +765,9 @@ def test_download_multiple_maps_editor(client, map, datalayer):
748
765
  assert f.infolist()[1].filename == f"umap_backup_test-map_{map.id}.umap"
749
766
 
750
767
 
751
- @pytest.mark.parametrize("share_status", [Map.PRIVATE, Map.BLOCKED])
768
+ @pytest.mark.parametrize(
769
+ "share_status", [Map.PRIVATE, Map.BLOCKED, Map.DRAFT, Map.DELETED]
770
+ )
752
771
  def test_download_shared_status_map(client, map, datalayer, share_status):
753
772
  map.share_status = share_status
754
773
  map.save()
@@ -757,8 +776,9 @@ def test_download_shared_status_map(client, map, datalayer, share_status):
757
776
  assert response.status_code == 403
758
777
 
759
778
 
760
- def test_download_my_map(client, map, datalayer):
761
- map.share_status = Map.PRIVATE
779
+ @pytest.mark.parametrize("share_status", [Map.PRIVATE, Map.DRAFT])
780
+ def test_download_my_map(client, map, datalayer, share_status):
781
+ map.share_status = share_status
762
782
  map.save()
763
783
  client.login(username=map.owner.username, password="123123")
764
784
  url = reverse("map_download", args=(map.pk,))
@@ -769,7 +789,19 @@ def test_download_my_map(client, map, datalayer):
769
789
  assert j["type"] == "umap"
770
790
 
771
791
 
772
- @pytest.mark.parametrize("share_status", [Map.PRIVATE, Map.BLOCKED, Map.OPEN])
792
+ @pytest.mark.parametrize("share_status", [Map.BLOCKED, Map.DELETED])
793
+ def test_download_my_map_blocked_or_deleted(client, map, datalayer, share_status):
794
+ map.share_status = share_status
795
+ map.save()
796
+ client.login(username=map.owner.username, password="123123")
797
+ url = reverse("map_download", args=(map.pk,))
798
+ response = client.get(url)
799
+ assert response.status_code == 403
800
+
801
+
802
+ @pytest.mark.parametrize(
803
+ "share_status", [Map.PRIVATE, Map.BLOCKED, Map.OPEN, Map.DRAFT]
804
+ )
773
805
  def test_oembed_shared_status_map(client, map, datalayer, share_status):
774
806
  map.share_status = share_status
775
807
  map.save()
@@ -15,7 +15,7 @@ def staticfiles(settings):
15
15
  # Make sure settings are properly reset after the test
16
16
  settings.STORAGES = deepcopy(settings.STORAGES)
17
17
  settings.STORAGES["staticfiles"]["BACKEND"] = (
18
- "umap.storage.UmapManifestStaticFilesStorage"
18
+ "umap.storage.staticfiles.UmapManifestStaticFilesStorage"
19
19
  )
20
20
  try:
21
21
  call_command("collectstatic", "--noinput")
@@ -1,7 +1,7 @@
1
1
  import pytest
2
2
  from django.urls import reverse
3
3
 
4
- from umap.models import Team
4
+ from umap.models import Map, Team
5
5
 
6
6
  pytestmark = pytest.mark.django_db
7
7
 
@@ -15,6 +15,40 @@ def test_can_see_team_maps(client, map, team):
15
15
  assert map.name in response.content.decode()
16
16
 
17
17
 
18
+ @pytest.mark.parametrize("share_status", [Map.PRIVATE, Map.DRAFT])
19
+ def test_others_cannot_see_team_private_maps_in_team_page(
20
+ client, map, team, user, share_status
21
+ ):
22
+ map.team = team
23
+ map.share_status = share_status
24
+ map.save()
25
+ url = reverse("team_maps", args=(team.pk,))
26
+ response = client.get(url)
27
+ assert response.status_code == 200
28
+ assert map.name not in response.content.decode()
29
+ # User is not in team
30
+ client.login(username=user.username, password="123123")
31
+ response = client.get(url)
32
+ assert response.status_code == 200
33
+ assert map.name not in response.content.decode()
34
+
35
+
36
+ @pytest.mark.parametrize("share_status", [Map.PRIVATE, Map.DRAFT])
37
+ def test_members_can_see_private_maps_in_team_page(
38
+ client, map, team, user, share_status
39
+ ):
40
+ map.team = team
41
+ map.share_status = share_status
42
+ map.save()
43
+ user.teams.add(team)
44
+ user.save()
45
+ url = reverse("team_maps", args=(team.pk,))
46
+ client.login(username=user.username, password="123123")
47
+ response = client.get(url)
48
+ assert response.status_code == 200
49
+ assert map.name in response.content.decode()
50
+
51
+
18
52
  def test_user_can_see_their_teams(client, team, user):
19
53
  user.teams.add(team)
20
54
  user.save()
umap/tests/test_views.py CHANGED
@@ -267,58 +267,6 @@ def test_change_user_slug(client, user, settings):
267
267
  assert f"/en/user/{user.pk}/" in response.content.decode()
268
268
 
269
269
 
270
- @pytest.mark.django_db
271
- def test_user_dashboard_is_restricted_to_logged_in(client):
272
- response = client.get(reverse("user_dashboard"))
273
- assert response.status_code == 302
274
- assert response["Location"] == "/en/login/?next=/en/me"
275
-
276
-
277
- @pytest.mark.django_db
278
- def test_user_dashboard_display_user_maps(client, map):
279
- client.login(username=map.owner.username, password="123123")
280
- response = client.get(reverse("user_dashboard"))
281
- assert response.status_code == 200
282
- body = response.content.decode()
283
- assert map.name in body
284
- assert f"{map.get_absolute_url()}?edit" in body
285
- assert f"{map.get_absolute_url()}?share" in body
286
- assert f"/map/{map.pk}/download" in body
287
- assert "Everyone (public)" in body
288
- assert "Owner only" in body
289
-
290
-
291
- @pytest.mark.django_db
292
- def test_user_dashboard_display_user_team_maps(client, map, team, user):
293
- user.teams.add(team)
294
- user.save()
295
- map.team = team
296
- map.save()
297
- client.login(username=user.username, password="123123")
298
- response = client.get(reverse("user_dashboard"))
299
- assert response.status_code == 200
300
- body = response.content.decode()
301
- assert map.name in body
302
- assert map.get_absolute_url() in body
303
-
304
-
305
- @pytest.mark.django_db
306
- def test_user_dashboard_display_user_maps_distinct(client, map):
307
- # cf https://github.com/umap-project/umap/issues/1325
308
- anonymap = MapFactory(name="Map witout owner should not appear")
309
- user1 = UserFactory(username="user1")
310
- user2 = UserFactory(username="user2")
311
- map.editors.add(user1)
312
- map.editors.add(user2)
313
- map.save()
314
- client.login(username=map.owner.username, password="123123")
315
- response = client.get(reverse("user_dashboard"))
316
- assert response.status_code == 200
317
- body = response.content.decode()
318
- assert body.count(f'<a href="/en/map/test-map_{map.pk}">test map</a>') == 1
319
- assert body.count(anonymap.name) == 0
320
-
321
-
322
270
  @pytest.mark.django_db
323
271
  def test_logout_should_return_redirect(client, user, settings):
324
272
  client.login(username=user.username, password="123123")
@@ -497,3 +445,34 @@ def test_websocket_token_is_generated_for_editors(client, user, user2, map):
497
445
  resp = client.get(token_url)
498
446
  token = resp.json().get("token")
499
447
  assert TimestampSigner().unsign_object(token, max_age=30)
448
+
449
+
450
+ @pytest.mark.django_db
451
+ def test_search(client, map):
452
+ # Very basic search, that do not deal with accent nor case.
453
+ # See install.md for how to have a smarter dict + index.
454
+ map.name = "Blé dur"
455
+ map.save()
456
+ url = reverse("search")
457
+ response = client.get(url + "?q=Blé")
458
+ assert "Blé dur" in response.content.decode()
459
+
460
+
461
+ @pytest.mark.django_db
462
+ def test_cannot_search_blocked_map(client, map):
463
+ map.name = "Blé dur"
464
+ map.share_status = Map.BLOCKED
465
+ map.save()
466
+ url = reverse("search")
467
+ response = client.get(url + "?q=Blé")
468
+ assert "Blé dur" not in response.content.decode()
469
+
470
+
471
+ @pytest.mark.django_db
472
+ def test_cannot_search_deleted_map(client, map):
473
+ map.name = "Blé dur"
474
+ map.share_status = Map.DELETED
475
+ map.save()
476
+ url = reverse("search")
477
+ response = client.get(url + "?q=Blé")
478
+ assert "Blé dur" not in response.content.decode()
umap/urls.py CHANGED
@@ -87,7 +87,7 @@ i18n_urls += decorated_patterns(
87
87
  name="datalayer_versions",
88
88
  ),
89
89
  path(
90
- "datalayer/<int:map_id>/<uuid:pk>/<str:name>",
90
+ "datalayer/<int:map_id>/<uuid:pk>/<str:ref>",
91
91
  views.DataLayerVersion.as_view(),
92
92
  name="datalayer_version",
93
93
  ),
@@ -155,8 +155,8 @@ map_urls = [
155
155
  views.MapClone.as_view(),
156
156
  name="map_clone",
157
157
  ),
158
- re_path(
159
- r"^map/(?P<map_id>[\d]+)/datalayer/create/$",
158
+ path(
159
+ "map/<int:map_id>/datalayer/create/<uuid:pk>/",
160
160
  views.DataLayerCreate.as_view(),
161
161
  name="datalayer_create",
162
162
  ),