umap-project 2.7.3__py3-none-any.whl → 2.8.0a0__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 (279) 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 +32 -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 +22 -2
  91. umap/static/umap/base.css +3 -603
  92. umap/static/umap/content.css +5 -3
  93. umap/static/umap/css/bar.css +202 -0
  94. umap/static/umap/css/form.css +617 -0
  95. umap/static/umap/css/icon.css +21 -1
  96. umap/static/umap/css/popup.css +125 -0
  97. umap/static/umap/img/16-white.svg +16 -4
  98. umap/static/umap/img/16.svg +1 -1
  99. umap/static/umap/img/source/16-white.svg +46 -45
  100. umap/static/umap/img/source/16.svg +1 -753
  101. umap/static/umap/js/components/fragment.js +3 -1
  102. umap/static/umap/js/modules/browser.js +20 -19
  103. umap/static/umap/js/modules/caption.js +21 -22
  104. umap/static/umap/js/modules/data/features.js +101 -74
  105. umap/static/umap/js/modules/data/layer.js +157 -137
  106. umap/static/umap/js/modules/facets.js +9 -9
  107. umap/static/umap/js/modules/formatter.js +5 -5
  108. umap/static/umap/js/modules/global.js +4 -52
  109. umap/static/umap/js/modules/help.js +18 -21
  110. umap/static/umap/js/modules/importer.js +71 -39
  111. umap/static/umap/js/modules/importers/cadastrefr.js +4 -0
  112. umap/static/umap/js/modules/importers/geodatamine.js +3 -3
  113. umap/static/umap/js/modules/importers/overpass.js +5 -0
  114. umap/static/umap/js/modules/permissions.js +85 -87
  115. umap/static/umap/js/modules/rendering/layers/base.js +15 -15
  116. umap/static/umap/js/modules/rendering/layers/classified.js +1 -1
  117. umap/static/umap/js/modules/rendering/layers/cluster.js +1 -1
  118. umap/static/umap/js/modules/rendering/layers/heat.js +1 -1
  119. umap/static/umap/js/modules/rendering/map.js +390 -0
  120. umap/static/umap/js/modules/rendering/popup.js +10 -9
  121. umap/static/umap/js/modules/rendering/template.js +35 -12
  122. umap/static/umap/js/modules/rendering/ui.js +57 -12
  123. umap/static/umap/js/modules/rules.js +22 -25
  124. umap/static/umap/js/modules/saving.js +47 -0
  125. umap/static/umap/js/modules/schema.js +5 -0
  126. umap/static/umap/js/modules/share.js +21 -24
  127. umap/static/umap/js/modules/slideshow.js +24 -20
  128. umap/static/umap/js/modules/sync/updaters.js +7 -9
  129. umap/static/umap/js/modules/tableeditor.js +20 -19
  130. umap/static/umap/js/modules/ui/bar.js +196 -0
  131. umap/static/umap/js/modules/ui/panel.js +10 -9
  132. umap/static/umap/js/modules/umap.js +1668 -0
  133. umap/static/umap/js/modules/urls.js +2 -2
  134. umap/static/umap/js/modules/utils.js +20 -6
  135. umap/static/umap/js/umap.controls.js +74 -301
  136. umap/static/umap/js/umap.core.js +29 -50
  137. umap/static/umap/js/umap.forms.js +34 -27
  138. umap/static/umap/keycloak.png +0 -0
  139. umap/static/umap/locale/am_ET.js +26 -10
  140. umap/static/umap/locale/am_ET.json +26 -10
  141. umap/static/umap/locale/ar.js +26 -10
  142. umap/static/umap/locale/ar.json +26 -10
  143. umap/static/umap/locale/ast.js +26 -10
  144. umap/static/umap/locale/ast.json +26 -10
  145. umap/static/umap/locale/bg.js +26 -10
  146. umap/static/umap/locale/bg.json +26 -10
  147. umap/static/umap/locale/br.js +27 -20
  148. umap/static/umap/locale/br.json +27 -20
  149. umap/static/umap/locale/ca.js +32 -29
  150. umap/static/umap/locale/ca.json +32 -29
  151. umap/static/umap/locale/cs_CZ.js +24 -17
  152. umap/static/umap/locale/cs_CZ.json +24 -17
  153. umap/static/umap/locale/da.js +26 -10
  154. umap/static/umap/locale/da.json +26 -10
  155. umap/static/umap/locale/de.js +21 -14
  156. umap/static/umap/locale/de.json +21 -14
  157. umap/static/umap/locale/el.js +28 -12
  158. umap/static/umap/locale/el.json +28 -12
  159. umap/static/umap/locale/en.js +12 -9
  160. umap/static/umap/locale/en.json +12 -9
  161. umap/static/umap/locale/en_US.json +26 -10
  162. umap/static/umap/locale/es.js +16 -13
  163. umap/static/umap/locale/es.json +16 -13
  164. umap/static/umap/locale/et.js +26 -10
  165. umap/static/umap/locale/et.json +26 -10
  166. umap/static/umap/locale/eu.js +16 -9
  167. umap/static/umap/locale/eu.json +16 -9
  168. umap/static/umap/locale/fa_IR.js +16 -9
  169. umap/static/umap/locale/fa_IR.json +16 -9
  170. umap/static/umap/locale/fi.js +26 -10
  171. umap/static/umap/locale/fi.json +26 -10
  172. umap/static/umap/locale/fr.js +12 -9
  173. umap/static/umap/locale/fr.json +12 -9
  174. umap/static/umap/locale/gl.js +26 -10
  175. umap/static/umap/locale/gl.json +26 -10
  176. umap/static/umap/locale/he.js +26 -10
  177. umap/static/umap/locale/he.json +26 -10
  178. umap/static/umap/locale/hr.js +26 -10
  179. umap/static/umap/locale/hr.json +26 -10
  180. umap/static/umap/locale/hu.js +16 -9
  181. umap/static/umap/locale/hu.json +16 -9
  182. umap/static/umap/locale/id.js +26 -10
  183. umap/static/umap/locale/id.json +26 -10
  184. umap/static/umap/locale/is.js +26 -10
  185. umap/static/umap/locale/is.json +26 -10
  186. umap/static/umap/locale/it.js +26 -10
  187. umap/static/umap/locale/it.json +26 -10
  188. umap/static/umap/locale/ja.js +26 -10
  189. umap/static/umap/locale/ja.json +26 -10
  190. umap/static/umap/locale/ko.js +26 -10
  191. umap/static/umap/locale/ko.json +26 -10
  192. umap/static/umap/locale/lt.js +26 -10
  193. umap/static/umap/locale/lt.json +26 -10
  194. umap/static/umap/locale/ms.js +28 -12
  195. umap/static/umap/locale/ms.json +28 -12
  196. umap/static/umap/locale/nl.js +28 -12
  197. umap/static/umap/locale/nl.json +28 -12
  198. umap/static/umap/locale/no.js +26 -10
  199. umap/static/umap/locale/no.json +26 -10
  200. umap/static/umap/locale/pl.js +28 -12
  201. umap/static/umap/locale/pl.json +28 -12
  202. umap/static/umap/locale/pl_PL.json +26 -10
  203. umap/static/umap/locale/pt.js +16 -9
  204. umap/static/umap/locale/pt.json +16 -9
  205. umap/static/umap/locale/pt_BR.js +26 -10
  206. umap/static/umap/locale/pt_BR.json +26 -10
  207. umap/static/umap/locale/pt_PT.js +16 -9
  208. umap/static/umap/locale/pt_PT.json +16 -9
  209. umap/static/umap/locale/ro.js +26 -10
  210. umap/static/umap/locale/ro.json +26 -10
  211. umap/static/umap/locale/ru.js +26 -10
  212. umap/static/umap/locale/ru.json +26 -10
  213. umap/static/umap/locale/si.js +7 -7
  214. umap/static/umap/locale/si.json +7 -7
  215. umap/static/umap/locale/sk_SK.js +26 -10
  216. umap/static/umap/locale/sk_SK.json +26 -10
  217. umap/static/umap/locale/sl.js +26 -10
  218. umap/static/umap/locale/sl.json +26 -10
  219. umap/static/umap/locale/sr.js +26 -10
  220. umap/static/umap/locale/sr.json +26 -10
  221. umap/static/umap/locale/sv.js +27 -11
  222. umap/static/umap/locale/sv.json +27 -11
  223. umap/static/umap/locale/th_TH.js +28 -12
  224. umap/static/umap/locale/th_TH.json +28 -12
  225. umap/static/umap/locale/tr.js +26 -10
  226. umap/static/umap/locale/tr.json +26 -10
  227. umap/static/umap/locale/uk_UA.js +26 -10
  228. umap/static/umap/locale/uk_UA.json +26 -10
  229. umap/static/umap/locale/vi.js +26 -10
  230. umap/static/umap/locale/vi.json +26 -10
  231. umap/static/umap/locale/vi_VN.json +26 -10
  232. umap/static/umap/locale/zh.js +26 -10
  233. umap/static/umap/locale/zh.json +26 -10
  234. umap/static/umap/locale/zh_CN.json +26 -10
  235. umap/static/umap/locale/zh_TW.Big5.json +26 -10
  236. umap/static/umap/locale/zh_TW.js +34 -27
  237. umap/static/umap/locale/zh_TW.json +34 -27
  238. umap/static/umap/map.css +5 -364
  239. umap/static/umap/unittests/URLs.js +15 -15
  240. umap/static/umap/unittests/utils.js +23 -1
  241. umap/static/umap/vars.css +2 -0
  242. umap/static/umap/vendors/formbuilder/Leaflet.FormBuilder.js +5 -1
  243. umap/storage.py +152 -0
  244. umap/templates/registration/login.html +7 -6
  245. umap/templates/umap/css.html +3 -0
  246. umap/templates/umap/js.html +1 -2
  247. umap/templates/umap/map_init.html +4 -5
  248. umap/templates/umap/user_dashboard.html +18 -19
  249. umap/tests/base.py +5 -1
  250. umap/tests/integration/conftest.py +2 -1
  251. umap/tests/integration/test_anonymous_owned_map.py +18 -10
  252. umap/tests/integration/test_browser.py +16 -1
  253. umap/tests/integration/test_dashboard.py +1 -1
  254. umap/tests/integration/test_edit_datalayer.py +18 -7
  255. umap/tests/integration/test_import.py +8 -5
  256. umap/tests/integration/test_optimistic_merge.py +31 -8
  257. umap/tests/integration/test_owned_map.py +22 -16
  258. umap/tests/integration/test_popup.py +44 -0
  259. umap/tests/integration/test_save.py +35 -0
  260. umap/tests/integration/test_view_marker.py +12 -0
  261. umap/tests/integration/test_view_polyline.py +257 -0
  262. umap/tests/integration/test_websocket_sync.py +81 -9
  263. umap/tests/test_datalayer.py +6 -7
  264. umap/tests/test_datalayer_s3.py +135 -0
  265. umap/tests/test_datalayer_views.py +28 -10
  266. umap/tests/test_empty_trash.py +34 -0
  267. umap/tests/test_map.py +12 -3
  268. umap/tests/test_map_views.py +69 -37
  269. umap/tests/test_views.py +53 -0
  270. umap/urls.py +3 -3
  271. umap/views.py +107 -76
  272. {umap_project-2.7.3.dist-info → umap_project-2.8.0a0.dist-info}/METADATA +16 -13
  273. {umap_project-2.7.3.dist-info → umap_project-2.8.0a0.dist-info}/RECORD +276 -262
  274. umap/management/commands/purge_purgatory.py +0 -28
  275. umap/static/umap/js/umap.js +0 -1903
  276. umap/tests/test_purge_purgatory.py +0 -25
  277. {umap_project-2.7.3.dist-info → umap_project-2.8.0a0.dist-info}/WHEEL +0 -0
  278. {umap_project-2.7.3.dist-info → umap_project-2.8.0a0.dist-info}/entry_points.txt +0 -0
  279. {umap_project-2.7.3.dist-info → umap_project-2.8.0a0.dist-info}/licenses/LICENSE +0 -0
@@ -22,10 +22,11 @@ def test_datalayers_should_be_ordered_by_rank(map, datalayer):
22
22
  assert list(map.datalayer_set.all()) == [c1, c2, c3, c4, datalayer]
23
23
 
24
24
 
25
- def test_upload_to(map, datalayer):
25
+ def test_upload_to(map):
26
26
  map.pk = 302
27
- datalayer.pk = 17
28
- assert datalayer.upload_to().startswith("datalayer/2/0/302/17_")
27
+ map.save()
28
+ datalayer = DataLayerFactory(map=map)
29
+ assert datalayer.geojson.name.startswith(f"datalayer/2/0/302/{datalayer.pk}_")
29
30
 
30
31
 
31
32
  def test_save_should_use_pk_as_name(map, datalayer):
@@ -65,7 +66,7 @@ def test_clone_should_clone_geojson_too(datalayer):
65
66
  def test_should_remove_old_versions_on_save(map, settings):
66
67
  datalayer = DataLayerFactory(uuid="0f1161c0-c07f-4ba4-86c5-8d8981d8a813", old_id=17)
67
68
  settings.UMAP_KEEP_VERSIONS = 3
68
- root = Path(datalayer.storage_root())
69
+ root = Path(datalayer.geojson.storage._base_path(datalayer))
69
70
  before = len(datalayer.geojson.storage.listdir(root)[1])
70
71
  newer = f"{datalayer.pk}_1440924889.geojson"
71
72
  medium = f"{datalayer.pk}_1440923687.geojson"
@@ -273,9 +274,8 @@ def test_anonymous_can_edit_in_inherit_mode_and_map_in_public_mode(
273
274
 
274
275
 
275
276
  def test_should_remove_all_versions_on_delete(map, settings):
276
- settings.UMAP_PURGATORY_ROOT = tempfile.mkdtemp()
277
277
  datalayer = DataLayerFactory(uuid="0f1161c0-c07f-4ba4-86c5-8d8981d8a813", old_id=17)
278
- root = Path(datalayer.storage_root())
278
+ root = Path(datalayer.geojson.storage._base_path(datalayer))
279
279
  before = len(datalayer.geojson.storage.listdir(root)[1])
280
280
  other = "123456_1440918637.geojson"
281
281
  files = [
@@ -292,4 +292,3 @@ def test_should_remove_all_versions_on_delete(map, settings):
292
292
  datalayer.delete()
293
293
  found = datalayer.geojson.storage.listdir(root)[1]
294
294
  assert found == [other, f"{other}.gz"]
295
- assert len(list(Path(settings.UMAP_PURGATORY_ROOT).iterdir())) == 4 + before
@@ -0,0 +1,135 @@
1
+ import json
2
+ import os
3
+
4
+ import boto3
5
+ import pytest
6
+ from botocore.errorfactory import ClientError
7
+ from django.core.files.base import ContentFile
8
+ from django.core.files.storage import storages
9
+ from moto import mock_aws
10
+
11
+ from umap.models import DataLayer
12
+
13
+ from .base import DataLayerFactory
14
+
15
+ pytestmark = pytest.mark.django_db
16
+
17
+
18
+ @pytest.fixture(scope="module", autouse=True)
19
+ def patch_storage():
20
+ """Mocked AWS Credentials for moto."""
21
+ os.environ["AWS_ACCESS_KEY_ID"] = "testing"
22
+ os.environ["AWS_SECRET_ACCESS_KEY"] = "testing"
23
+ os.environ["AWS_SECURITY_TOKEN"] = "testing"
24
+ os.environ["AWS_SESSION_TOKEN"] = "testing"
25
+ os.environ["AWS_DEFAULT_REGION"] = "us-east-1"
26
+ before = DataLayer.geojson.field.storage
27
+
28
+ DataLayer.geojson.field.storage = storages.create_storage(
29
+ {
30
+ "BACKEND": "umap.storage.UmapS3",
31
+ "OPTIONS": {
32
+ "access_key": "testing",
33
+ "secret_key": "testing",
34
+ "bucket_name": "umap",
35
+ "region_name": "us-east-1",
36
+ },
37
+ }
38
+ )
39
+ yield
40
+ DataLayer.geojson.field.storage = before
41
+
42
+
43
+ @pytest.fixture(scope="module", autouse=True)
44
+ def mocked_aws():
45
+ """
46
+ Mock all AWS interactions
47
+ Requires you to create your own boto3 clients
48
+ """
49
+ with mock_aws():
50
+ client = boto3.client("s3", region_name="us-east-1")
51
+ client.create_bucket(Bucket="umap")
52
+ client.put_bucket_versioning(
53
+ Bucket="umap", VersioningConfiguration={"Status": "Enabled"}
54
+ )
55
+ yield
56
+
57
+
58
+ def test_can_create_datalayer(map, datalayer):
59
+ other = DataLayerFactory(map=map)
60
+ assert datalayer.geojson.name == f"{datalayer.pk}.geojson"
61
+ assert other.geojson.name == f"{other.pk}.geojson"
62
+
63
+
64
+ def test_clone_should_return_new_instance(map, datalayer):
65
+ clone = datalayer.clone()
66
+ assert datalayer.pk != clone.pk
67
+ assert datalayer.name == clone.name
68
+ assert datalayer.map == clone.map
69
+ assert datalayer.geojson != clone.geojson
70
+ assert datalayer.geojson.name != clone.geojson.name
71
+ assert clone.geojson.name == f"{clone.pk}.geojson"
72
+
73
+
74
+ def test_update_should_add_version(map, datalayer):
75
+ assert len(datalayer.versions) == 1
76
+ datalayer.geojson = ContentFile("{}", "foo.json")
77
+ datalayer.save()
78
+ assert len(datalayer.versions) == 2
79
+
80
+
81
+ def test_get_version(map, datalayer):
82
+ assert len(datalayer.versions) == 1
83
+ datalayer.geojson = ContentFile(b'{"foo": "bar"}', "foo.json")
84
+ datalayer.save()
85
+ assert len(datalayer.versions) == 2
86
+ latest = datalayer.versions[0]["ref"]
87
+ version = datalayer.get_version(latest)
88
+ assert json.loads(version) == {"foo": "bar"}
89
+ older = datalayer.versions[1]["ref"]
90
+ version = datalayer.get_version(older)
91
+ assert json.loads(version) == {
92
+ "_umap_options": {
93
+ "browsable": True,
94
+ "displayOnLoad": True,
95
+ "name": "test datalayer",
96
+ },
97
+ "features": [
98
+ {
99
+ "geometry": {
100
+ "coordinates": [
101
+ 14.68896484375,
102
+ 48.55297816440071,
103
+ ],
104
+ "type": "Point",
105
+ },
106
+ "properties": {
107
+ "_umap_options": {
108
+ "color": "DarkCyan",
109
+ "iconClass": "Ball",
110
+ },
111
+ "description": "Da place anonymous again 755",
112
+ "name": "Here",
113
+ },
114
+ "type": "Feature",
115
+ },
116
+ ],
117
+ "type": "FeatureCollection",
118
+ }
119
+
120
+ latest = datalayer.reference_version
121
+ version = datalayer.get_version(latest)
122
+ assert json.loads(version) == {"foo": "bar"}
123
+
124
+
125
+ def test_delete_datalayer_should_delete_all_versions(datalayer):
126
+ # create a new version
127
+ datalayer.geojson = ContentFile('{"foo": "bar"}', "foo.json")
128
+ datalayer.save()
129
+ s3_key = datalayer.geojson.name
130
+ datalayer.delete()
131
+ with pytest.raises(ClientError):
132
+ datalayer.geojson.storage.connection.meta.client.get_object(
133
+ Bucket="umap",
134
+ Key=s3_key,
135
+ )
@@ -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()
umap/tests/test_views.py CHANGED
@@ -288,6 +288,28 @@ def test_user_dashboard_display_user_maps(client, map):
288
288
  assert "Owner only" in body
289
289
 
290
290
 
291
+ @pytest.mark.django_db
292
+ def test_user_dashboard_do_not_display_blocked_user_maps(client, map):
293
+ map.share_status = Map.BLOCKED
294
+ map.save()
295
+ client.login(username=map.owner.username, password="123123")
296
+ response = client.get(reverse("user_dashboard"))
297
+ assert response.status_code == 200
298
+ body = response.content.decode()
299
+ assert map.name not in body
300
+
301
+
302
+ @pytest.mark.django_db
303
+ def test_user_dashboard_do_not_display_deleted_user_maps(client, map):
304
+ map.share_status = Map.DELETED
305
+ map.save()
306
+ client.login(username=map.owner.username, password="123123")
307
+ response = client.get(reverse("user_dashboard"))
308
+ assert response.status_code == 200
309
+ body = response.content.decode()
310
+ assert map.name not in body
311
+
312
+
291
313
  @pytest.mark.django_db
292
314
  def test_user_dashboard_display_user_team_maps(client, map, team, user):
293
315
  user.teams.add(team)
@@ -497,3 +519,34 @@ def test_websocket_token_is_generated_for_editors(client, user, user2, map):
497
519
  resp = client.get(token_url)
498
520
  token = resp.json().get("token")
499
521
  assert TimestampSigner().unsign_object(token, max_age=30)
522
+
523
+
524
+ @pytest.mark.django_db
525
+ def test_search(client, map):
526
+ # Very basic search, that do not deal with accent nor case.
527
+ # See install.md for how to have a smarter dict + index.
528
+ map.name = "Blé dur"
529
+ map.save()
530
+ url = reverse("search")
531
+ response = client.get(url + "?q=Blé")
532
+ assert "Blé dur" in response.content.decode()
533
+
534
+
535
+ @pytest.mark.django_db
536
+ def test_cannot_search_blocked_map(client, map):
537
+ map.name = "Blé dur"
538
+ map.share_status = Map.BLOCKED
539
+ map.save()
540
+ url = reverse("search")
541
+ response = client.get(url + "?q=Blé")
542
+ assert "Blé dur" not in response.content.decode()
543
+
544
+
545
+ @pytest.mark.django_db
546
+ def test_cannot_search_deleted_map(client, map):
547
+ map.name = "Blé dur"
548
+ map.share_status = Map.DELETED
549
+ map.save()
550
+ url = reverse("search")
551
+ response = client.get(url + "?q=Blé")
552
+ 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
  ),