c2cgeoportal-admin 2.5.0.100__py3-none-any.whl → 2.9rc44__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 (99) hide show
  1. c2cgeoportal_admin/__init__.py +44 -14
  2. c2cgeoportal_admin/lib/__init__.py +0 -0
  3. c2cgeoportal_admin/lib/lingva_extractor.py +77 -0
  4. c2cgeoportal_admin/lib/ogcserver_synchronizer.py +410 -0
  5. c2cgeoportal_admin/py.typed +0 -0
  6. c2cgeoportal_admin/routes.py +30 -11
  7. c2cgeoportal_admin/schemas/dimensions.py +17 -11
  8. c2cgeoportal_admin/schemas/functionalities.py +60 -22
  9. c2cgeoportal_admin/schemas/interfaces.py +27 -19
  10. c2cgeoportal_admin/schemas/metadata.py +122 -48
  11. c2cgeoportal_admin/schemas/restriction_areas.py +26 -20
  12. c2cgeoportal_admin/schemas/roles.py +13 -7
  13. c2cgeoportal_admin/schemas/treegroup.py +90 -20
  14. c2cgeoportal_admin/schemas/treeitem.py +3 -4
  15. c2cgeoportal_admin/static/layertree.css +26 -4
  16. c2cgeoportal_admin/static/navbar.css +59 -36
  17. c2cgeoportal_admin/static/theme.css +51 -11
  18. c2cgeoportal_admin/subscribers.py +3 -3
  19. c2cgeoportal_admin/templates/404.jinja2 +41 -2
  20. c2cgeoportal_admin/templates/edit.jinja2 +23 -0
  21. c2cgeoportal_admin/templates/home.jinja2 +23 -0
  22. c2cgeoportal_admin/templates/index.jinja2 +23 -0
  23. c2cgeoportal_admin/templates/layertree.jinja2 +55 -11
  24. c2cgeoportal_admin/templates/layout.jinja2 +23 -0
  25. c2cgeoportal_admin/templates/navigation_navbar.jinja2 +56 -0
  26. c2cgeoportal_admin/templates/ogcserver_synchronize.jinja2 +90 -0
  27. c2cgeoportal_admin/templates/widgets/child.pt +35 -3
  28. c2cgeoportal_admin/templates/widgets/children.pt +121 -92
  29. c2cgeoportal_admin/templates/widgets/dimension.pt +23 -0
  30. c2cgeoportal_admin/templates/widgets/dimensions.pt +23 -0
  31. c2cgeoportal_admin/templates/widgets/functionality_fields.pt +51 -0
  32. c2cgeoportal_admin/templates/widgets/layer_fields.pt +23 -0
  33. c2cgeoportal_admin/templates/widgets/layer_group_fields.pt +23 -0
  34. c2cgeoportal_admin/templates/widgets/layer_v1_fields.pt +23 -0
  35. c2cgeoportal_admin/templates/widgets/metadata.pt +30 -1
  36. c2cgeoportal_admin/templates/widgets/metadatas.pt +23 -0
  37. c2cgeoportal_admin/templates/widgets/ogcserver_fields.pt +23 -0
  38. c2cgeoportal_admin/templates/widgets/restriction_area_fields.pt +25 -9
  39. c2cgeoportal_admin/templates/widgets/role_fields.pt +52 -25
  40. c2cgeoportal_admin/templates/widgets/theme_fields.pt +23 -0
  41. c2cgeoportal_admin/templates/widgets/user_fields.pt +23 -0
  42. c2cgeoportal_admin/views/__init__.py +29 -0
  43. c2cgeoportal_admin/views/dimension_layers.py +14 -9
  44. c2cgeoportal_admin/views/functionalities.py +52 -18
  45. c2cgeoportal_admin/views/home.py +5 -5
  46. c2cgeoportal_admin/views/interfaces.py +29 -21
  47. c2cgeoportal_admin/views/layer_groups.py +36 -25
  48. c2cgeoportal_admin/views/layers.py +17 -13
  49. c2cgeoportal_admin/views/layers_cog.py +135 -0
  50. c2cgeoportal_admin/views/layers_vectortiles.py +62 -27
  51. c2cgeoportal_admin/views/layers_wms.py +61 -36
  52. c2cgeoportal_admin/views/layers_wmts.py +54 -32
  53. c2cgeoportal_admin/views/layertree.py +37 -28
  54. c2cgeoportal_admin/views/logged_views.py +83 -0
  55. c2cgeoportal_admin/views/logs.py +91 -0
  56. c2cgeoportal_admin/views/oauth2_clients.py +96 -0
  57. c2cgeoportal_admin/views/ogc_servers.py +192 -21
  58. c2cgeoportal_admin/views/restriction_areas.py +78 -25
  59. c2cgeoportal_admin/views/roles.py +88 -25
  60. c2cgeoportal_admin/views/themes.py +47 -35
  61. c2cgeoportal_admin/views/themes_ordering.py +44 -24
  62. c2cgeoportal_admin/views/treeitems.py +21 -17
  63. c2cgeoportal_admin/views/users.py +46 -26
  64. c2cgeoportal_admin/widgets.py +79 -28
  65. {c2cgeoportal_admin-2.5.0.100.dist-info → c2cgeoportal_admin-2.9rc44.dist-info}/METADATA +15 -13
  66. c2cgeoportal_admin-2.9rc44.dist-info/RECORD +97 -0
  67. {c2cgeoportal_admin-2.5.0.100.dist-info → c2cgeoportal_admin-2.9rc44.dist-info}/WHEEL +1 -1
  68. c2cgeoportal_admin-2.9rc44.dist-info/entry_points.txt +5 -0
  69. tests/__init__.py +36 -27
  70. tests/conftest.py +23 -24
  71. tests/test_edit_url.py +16 -19
  72. tests/test_functionalities.py +52 -14
  73. tests/test_home.py +0 -1
  74. tests/test_interface.py +35 -12
  75. tests/test_layer_groups.py +58 -32
  76. tests/test_layers_cog.py +243 -0
  77. tests/test_layers_vectortiles.py +46 -30
  78. tests/test_layers_wms.py +77 -82
  79. tests/test_layers_wmts.py +51 -30
  80. tests/test_layertree.py +107 -101
  81. tests/test_learn.py +1 -1
  82. tests/test_left_menu.py +0 -1
  83. tests/test_lingva_extractor_config.py +64 -0
  84. tests/test_logs.py +102 -0
  85. tests/test_main.py +4 -2
  86. tests/test_metadatas.py +79 -71
  87. tests/test_oauth2_clients.py +186 -0
  88. tests/test_ogc_servers.py +110 -28
  89. tests/test_restriction_areas.py +109 -20
  90. tests/test_role.py +142 -82
  91. tests/test_themes.py +75 -41
  92. tests/test_themes_ordering.py +1 -2
  93. tests/test_treegroup.py +2 -2
  94. tests/test_user.py +72 -70
  95. tests/themes_ordering.py +1 -2
  96. c2cgeoportal_admin/templates/navigation_vertical.jinja2 +0 -10
  97. c2cgeoportal_admin-2.5.0.100.dist-info/RECORD +0 -84
  98. c2cgeoportal_admin-2.5.0.100.dist-info/entry_points.txt +0 -3
  99. {c2cgeoportal_admin-2.5.0.100.dist-info → c2cgeoportal_admin-2.9rc44.dist-info}/top_level.txt +0 -0
tests/test_themes.py CHANGED
@@ -13,33 +13,38 @@ def theme_test_data(dbsession, transact):
13
13
  del transact
14
14
 
15
15
  from c2cgeoportal_commons.models.main import (
16
- Theme,
17
- Role,
18
16
  Functionality,
19
- LayergroupTreeitem,
20
17
  Interface,
21
- Metadata,
22
18
  LayerGroup,
19
+ LayergroupTreeitem,
23
20
  LayerWMS,
21
+ Metadata,
24
22
  OGCServer,
23
+ Role,
24
+ Theme,
25
25
  )
26
26
 
27
27
  interfaces = [Interface(name) for name in ["desktop", "mobile", "edit", "routing"]]
28
+ dbsession.add_all(interfaces)
28
29
 
29
- groups = [LayerGroup(name="layer_group_{}".format(i)) for i in range(0, 5)]
30
+ groups = [LayerGroup(name=f"layer_group_{i}") for i in range(0, 5)]
31
+ dbsession.add_all(groups)
30
32
 
31
33
  layer = LayerWMS(name="layer_wms")
32
34
  layer.ogc_server = OGCServer(name="server")
33
- dbsession.add(layer)
34
35
  layers = [layer]
36
+ dbsession.add_all(layers)
35
37
 
38
+ # Note that "default_theme" is not relevant for themes
36
39
  functionalities = [
37
- Functionality(name=name, value="value_{}".format(v))
38
- for name in ("default_basemap", "location")
40
+ Functionality(name=name, value=f"value_{v}")
41
+ for name in ("default_basemap", "default_theme")
39
42
  for v in range(0, 4)
40
43
  ]
44
+ dbsession.add_all(functionalities)
41
45
 
42
46
  roles = [Role("secretary_" + str(i)) for i in range(0, 4)]
47
+ dbsession.add_all(roles)
43
48
 
44
49
  metadatas_protos = [
45
50
  ("copyable", "true"),
@@ -48,7 +53,7 @@ def theme_test_data(dbsession, transact):
48
53
  ]
49
54
  themes = []
50
55
  for i in range(0, 25):
51
- theme = Theme(name="theme_{}".format(i), ordering=1, icon="icon_{}".format(i))
56
+ theme = Theme(name=f"theme_{i}", ordering=1, icon=f"icon_{i}")
52
57
  theme.public = 1 == i % 2
53
58
  theme.interfaces = [interfaces[i % 4], interfaces[(i + 2) % 4]]
54
59
  theme.metadatas = [
@@ -57,7 +62,7 @@ def theme_test_data(dbsession, transact):
57
62
  ]
58
63
  for metadata in theme.metadatas:
59
64
  metadata.item = theme
60
- theme.functionalities = [functionalities[i % 8], functionalities[(i + 3) % 8]]
65
+ theme.functionalities = [functionalities[i % 4]]
61
66
  theme.restricted_roles = [roles[i % 4], roles[(i + 2) % 4]]
62
67
 
63
68
  dbsession.add(
@@ -86,7 +91,6 @@ def theme_test_data(dbsession, transact):
86
91
 
87
92
  @pytest.mark.usefixtures("theme_test_data", "test_app")
88
93
  class TestTheme(TestTreeGroup):
89
-
90
94
  _prefix = "/admin/themes"
91
95
 
92
96
  def test_index_rendering(self, test_app):
@@ -117,7 +121,7 @@ class TestTheme(TestTreeGroup):
117
121
 
118
122
  assert first_theme.id == int(first_row["_id_"])
119
123
  assert first_theme.name == first_row["name"]
120
- assert "default_basemap=value_0, default_basemap=value_3" == first_row["functionalities"]
124
+ assert "default_basemap=value_0" == first_row["functionalities"]
121
125
  assert "secretary_0, secretary_2" == first_row["restricted_roles"]
122
126
  assert "desktop, edit" == first_row["interfaces"]
123
127
  assert 'copyable: true, snappingConfig: {"tolerance": 50}' == first_row["metadatas"]
@@ -146,16 +150,18 @@ class TestTheme(TestTreeGroup):
146
150
 
147
151
  def test_public_checkbox_edit(self, test_app, theme_test_data):
148
152
  theme = theme_test_data["themes"][10]
149
- form10 = test_app.get("/admin/themes/{}".format(theme.id), status=200).form
153
+ form10 = test_app.get(f"/admin/themes/{theme.id}", status=200).form
150
154
  assert not form10["public"].checked
151
155
  theme = theme_test_data["themes"][11]
152
- form11 = test_app.get("/admin/themes/{}".format(theme.id), status=200).form
156
+ form11 = test_app.get(f"/admin/themes/{theme.id}", status=200).form
153
157
  assert form11["public"].checked
154
158
 
155
159
  def test_edit(self, test_app, theme_test_data, dbsession):
160
+ from c2cgeoportal_commons.models.main import Log, LogAction
161
+
156
162
  theme = theme_test_data["themes"][0]
157
163
 
158
- resp = test_app.get("/admin/themes/{}".format(theme.id), status=200)
164
+ resp = test_app.get(f"/admin/themes/{theme.id}", status=200)
159
165
  form = resp.form
160
166
 
161
167
  assert str(theme.id) == self.get_first_field_named(form, "id").value
@@ -166,21 +172,24 @@ class TestTheme(TestTreeGroup):
166
172
  assert theme.public == form["public"].checked
167
173
 
168
174
  interfaces = theme_test_data["interfaces"]
169
- assert set((interfaces[0].id, interfaces[2].id)) == set(i.id for i in theme.interfaces)
175
+ assert {interfaces[0].id, interfaces[2].id} == {i.id for i in theme.interfaces}
170
176
  self._check_interfaces(form, interfaces, theme)
171
177
 
172
178
  functionalities = theme_test_data["functionalities"]
173
- assert set((functionalities[0].id, functionalities[3].id)) == set(f.id for f in theme.functionalities)
179
+ assert {functionalities[0].id} == {f.id for f in theme.functionalities}
174
180
  self.check_checkboxes(
175
181
  form,
176
182
  "functionalities",
177
183
  [
178
184
  {
179
- "label": "{}={}".format(f.name, f.value),
185
+ "label": f"{f.name}={f.value}",
180
186
  "value": str(f.id),
181
187
  "checked": f in theme.functionalities,
182
188
  }
183
- for f in sorted(functionalities, key=lambda f: (f.name, f.value))
189
+ for f in sorted(
190
+ [f for f in functionalities if f.name in ("default_basemap")],
191
+ key=lambda f: (f.name, f.value),
192
+ )
184
193
  ],
185
194
  )
186
195
 
@@ -220,21 +229,25 @@ class TestTheme(TestTreeGroup):
220
229
  assert value == getattr(theme, key)
221
230
  else:
222
231
  assert str(value or "") == str(getattr(theme, key) or "")
223
- assert set([interfaces[1].id, interfaces[3].id]) == set(
224
- [interface.id for interface in theme.interfaces]
225
- )
226
- assert set([functionalities[2].id]) == set(
227
- [functionality.id for functionality in theme.functionalities]
228
- )
232
+ assert {interfaces[1].id, interfaces[3].id} == {interface.id for interface in theme.interfaces}
233
+ assert {functionalities[2].id} == {functionality.id for functionality in theme.functionalities}
229
234
  assert 0 == len(theme.restricted_roles)
230
235
 
236
+ log = dbsession.query(Log).one()
237
+ assert log.date != None
238
+ assert log.action == LogAction.UPDATE
239
+ assert log.element_type == "theme"
240
+ assert log.element_id == theme.id
241
+ assert log.element_name == theme.name
242
+ assert log.username == "test_user"
243
+
231
244
  def test_post_new_with_children_invalid(self, test_app, theme_test_data):
232
245
  """
233
246
  Check there is no rendering error when validation fails.
234
247
  """
235
248
  groups = theme_test_data["groups"]
236
249
  resp = test_app.post(
237
- "{}/new".format(self._prefix),
250
+ f"{self._prefix}/new",
238
251
  (
239
252
  ("_charset_", "UTF-8"),
240
253
  ("__formid__", "deform"),
@@ -254,9 +267,11 @@ class TestTheme(TestTreeGroup):
254
267
  self._check_submission_problem(resp, "Required")
255
268
 
256
269
  def test_post_new_with_children_success(self, test_app, dbsession, theme_test_data):
270
+ from c2cgeoportal_commons.models.main import Log, LogAction
271
+
257
272
  groups = theme_test_data["groups"]
258
273
  resp = test_app.post(
259
- "{}/new".format(self._prefix),
274
+ f"{self._prefix}/new",
260
275
  (
261
276
  ("_charset_", "UTF-8"),
262
277
  ("__formid__", "deform"),
@@ -298,13 +313,21 @@ class TestTheme(TestTreeGroup):
298
313
  rel.treeitem_id for rel in theme.children_relation
299
314
  ]
300
315
 
316
+ log = dbsession.query(Log).one()
317
+ assert log.date != None
318
+ assert log.action == LogAction.INSERT
319
+ assert log.element_type == "theme"
320
+ assert log.element_id == theme.id
321
+ assert log.element_name == theme.name
322
+ assert log.username == "test_user"
323
+
301
324
  def test_post_new_with_child_layer(self, theme_test_data, test_app):
302
325
  """
303
326
  Check layers are rejected by the validator (also means that they are not proposed to the user).
304
327
  """
305
328
  layers = theme_test_data["layers"]
306
329
  resp = test_app.post(
307
- "{}/new".format(self._prefix),
330
+ f"{self._prefix}/new",
308
331
  (
309
332
  ("_charset_", "UTF-8"),
310
333
  ("__formid__", "deform"),
@@ -324,7 +347,7 @@ class TestTheme(TestTreeGroup):
324
347
  status=200,
325
348
  )
326
349
  assert (
327
- "Value {} does not exist in table treeitem or is not allowed to avoid cycles".format(layers[0].id)
350
+ f"Value {layers[0].id} does not exist in table treeitem or is not allowed to avoid cycles"
328
351
  == resp.html.select_one(".item-children_relation + .help-block").getText().strip()
329
352
  )
330
353
 
@@ -333,7 +356,7 @@ class TestTheme(TestTreeGroup):
333
356
 
334
357
  theme = theme_test_data["themes"][1]
335
358
 
336
- resp = test_app.get("{}/{}/duplicate".format(self._prefix, theme.id), status=200)
359
+ resp = test_app.get(f"{self._prefix}/{theme.id}/duplicate", status=200)
337
360
  form = resp.form
338
361
 
339
362
  assert "" == self.get_first_field_named(form, "id").value
@@ -345,22 +368,25 @@ class TestTheme(TestTreeGroup):
345
368
  assert theme.public == form["public"].checked
346
369
 
347
370
  interfaces = theme_test_data["interfaces"]
348
- assert set((interfaces[1].id, interfaces[3].id)) == set(i.id for i in theme.interfaces)
371
+ assert {interfaces[1].id, interfaces[3].id} == {i.id for i in theme.interfaces}
349
372
 
350
373
  self._check_interfaces(form, interfaces, theme)
351
374
 
352
375
  functionalities = theme_test_data["functionalities"]
353
- assert set((functionalities[1].id, functionalities[4].id)) == set(f.id for f in theme.functionalities)
376
+ assert {functionalities[1].id} == {f.id for f in theme.functionalities}
354
377
  self.check_checkboxes(
355
378
  form,
356
379
  "functionalities",
357
380
  [
358
381
  {
359
- "label": "{}={}".format(f.name, f.value),
382
+ "label": f"{f.name}={f.value}",
360
383
  "value": str(f.id),
361
384
  "checked": f in theme.functionalities,
362
385
  }
363
- for f in sorted(functionalities, key=lambda f: (f.name, f.value))
386
+ for f in sorted(
387
+ [f for f in functionalities if f.name in ("default_basemap")],
388
+ key=lambda f: (f.name, f.value),
389
+ )
364
390
  ],
365
391
  )
366
392
 
@@ -381,23 +407,31 @@ class TestTheme(TestTreeGroup):
381
407
  duplicated = dbsession.query(Theme).filter(Theme.name == "duplicated").one()
382
408
 
383
409
  assert str(duplicated.id) == re.match(
384
- r"http://localhost{}/(.*)\?msg_col=submit_ok".format(self._prefix), resp.location
410
+ rf"http://localhost{self._prefix}/(.*)\?msg_col=submit_ok", resp.location
385
411
  ).group(1)
386
412
  assert duplicated.id != theme.id
387
413
  assert duplicated.children_relation[0].id != theme.children_relation[0].id
388
414
  assert duplicated.children_relation[0].treeitem.id == theme.children_relation[0].treeitem.id
389
415
 
390
416
  def test_delete(self, test_app, dbsession):
391
- from c2cgeoportal_commons.models.main import Theme
417
+ from c2cgeoportal_commons.models.main import Log, LogAction, Theme
418
+
419
+ theme = dbsession.query(Theme).first()
420
+ test_app.delete(f"/admin/themes/{theme.id}", status=200)
421
+ assert dbsession.query(Theme).get(theme.id) is None
392
422
 
393
- theme_id = dbsession.query(Theme.id).first().id
394
- test_app.delete("/admin/themes/{}".format(theme_id), status=200)
395
- assert dbsession.query(Theme).get(theme_id) is None
423
+ log = dbsession.query(Log).one()
424
+ assert log.date != None
425
+ assert log.action == LogAction.DELETE
426
+ assert log.element_type == "theme"
427
+ assert log.element_id == theme.id
428
+ assert log.element_name == theme.name
429
+ assert log.username == "test_user"
396
430
 
397
431
  def test_unicity_validator(self, theme_test_data, test_app):
398
432
  theme = theme_test_data["themes"][1]
399
- resp = test_app.get("{}/{}/duplicate".format(self._prefix, theme.id), status=200)
433
+ resp = test_app.get(f"{self._prefix}/{theme.id}/duplicate", status=200)
400
434
 
401
435
  resp = resp.form.submit("submit")
402
436
 
403
- self._check_submission_problem(resp, "{} is already used.".format(theme.name))
437
+ self._check_submission_problem(resp, f"{theme.name} is already used.")
@@ -14,7 +14,7 @@ def theme_test_data(dbsession, transact):
14
14
 
15
15
  themes = []
16
16
  for i in range(0, 3):
17
- theme = Theme(name="theme_{}".format(i), ordering=i, icon="icon_{}".format(i))
17
+ theme = Theme(name=f"theme_{i}", ordering=i, icon=f"icon_{i}")
18
18
 
19
19
  dbsession.add(theme)
20
20
  themes.append(theme)
@@ -28,7 +28,6 @@ def theme_test_data(dbsession, transact):
28
28
 
29
29
  @pytest.mark.usefixtures("theme_test_data", "test_app")
30
30
  class TestThemeOrdering(TestTreeGroup):
31
-
32
31
  _prefix = "/admin/layertree/ordering"
33
32
 
34
33
  def test_edit(self, test_app, theme_test_data, dbsession):
tests/test_treegroup.py CHANGED
@@ -5,10 +5,10 @@ from . import AbstractViewsTests
5
5
 
6
6
  class TestTreeGroup(AbstractViewsTests):
7
7
  def check_children(self, form, group, expected):
8
- form_group = form.html.select_one(".item-{}".format(group))
8
+ form_group = form.html.select_one(f".item-{group}")
9
9
  items = form_group.select(".deform-seq-item")
10
10
  assert len(expected) == len(items)
11
11
  for item, exp in zip(items, expected):
12
12
  assert exp["label"] == item.select_one(".well").getText().strip()
13
13
  for key, value in exp["values"].items():
14
- assert value == item.select_one('input[name="{}"]'.format(key))["value"]
14
+ assert value == item.select_one(f'input[name="{key}"]')["value"]
tests/test_user.py CHANGED
@@ -4,11 +4,11 @@ import email
4
4
  import re
5
5
  from unittest.mock import MagicMock, patch
6
6
 
7
- from pyramid.testing import DummyRequest
7
+ import pyramid.httpexceptions
8
8
  import pytest
9
+ from pyramid.testing import DummyRequest
9
10
 
10
- from . import AbstractViewsTests, skip_if_ci
11
- from .selenium.page import IndexPage
11
+ from . import AbstractViewsTests
12
12
 
13
13
 
14
14
  @pytest.fixture(scope="function")
@@ -16,18 +16,18 @@ from .selenium.page import IndexPage
16
16
  def users_test_data(dbsession, transact):
17
17
  del transact
18
18
 
19
- from c2cgeoportal_commons.models.static import User
20
19
  from c2cgeoportal_commons.models.main import Role
20
+ from c2cgeoportal_commons.models.static import User
21
21
 
22
22
  roles = []
23
23
  for i in range(0, 4):
24
- roles.append(Role("secretary_{}".format(i)))
24
+ roles.append(Role(f"secretary_{i}"))
25
25
  dbsession.add(roles[i])
26
26
  users = []
27
27
  for i in range(0, 23):
28
28
  user = User(
29
- "babar_{}".format(i),
30
- email="mail{}@valid.net".format(i),
29
+ f"babar_{i}",
30
+ email=f"mail{i}@valid.net",
31
31
  settings_role=roles[i % 4],
32
32
  roles=[roles[i % 4]],
33
33
  )
@@ -49,7 +49,6 @@ EXPECTED_WELCOME_MAIL = (
49
49
 
50
50
  @pytest.mark.usefixtures("users_test_data", "test_app")
51
51
  class TestUser(AbstractViewsTests):
52
-
53
52
  _prefix = "/admin/users"
54
53
 
55
54
  def test_index_rendering(self, test_app):
@@ -61,6 +60,7 @@ class TestUser(AbstractViewsTests):
61
60
  ("actions", "", "false"),
62
61
  ("id", "id", "true"),
63
62
  ("username", "Username"),
63
+ ("display_name", "Display name", "true"),
64
64
  ("email", "Email"),
65
65
  ("last_login", "Last login"),
66
66
  ("expire_on", "Expiration date"),
@@ -71,20 +71,33 @@ class TestUser(AbstractViewsTests):
71
71
 
72
72
  self.check_grid_headers(resp, expected)
73
73
 
74
- @pytest.mark.skip(reason="Translation is not finished")
74
+ @pytest.mark.skip(reason="Translations seems not available in tests")
75
75
  def test_index_rendering_fr(self, test_app):
76
76
  resp = self.get(test_app, locale="fr")
77
77
 
78
78
  self.check_left_menu(resp, "Utilisateurs")
79
79
 
80
- expected = [("_id_", "", "false"), ("username", "Nom"), ("role_name", "Role"), ("email", "Courriel")]
81
- self.check_grid_headers(resp, expected)
80
+ expected = [
81
+ ("actions", "", "false"),
82
+ ("id", "id", "true"),
83
+ ("username", "Nom d'utilisateur"),
84
+ ("email", "Email"),
85
+ ("last_login", "Dernière connection"),
86
+ ("expire_on", "Date d'expiration"),
87
+ ("deactivated", "Désactivé"),
88
+ ("settings_role", "Rôle de configuration"),
89
+ ("roles", "Rôles", "false"),
90
+ ]
91
+ self.check_grid_headers(resp, expected, new="Nouveau")
92
+
93
+ def test_view_edit(self, test_app, users_test_data, dbsession):
94
+ from c2cgeoportal_commons.models.main import LogAction
95
+ from c2cgeoportal_commons.models.static import Log
82
96
 
83
- def test_view_edit(self, test_app, users_test_data):
84
97
  user = users_test_data["users"][9]
85
98
  roles = users_test_data["roles"]
86
99
 
87
- resp = test_app.get("/admin/users/{}".format(user.id), status=200)
100
+ resp = test_app.get(f"/admin/users/{user.id}", status=200)
88
101
 
89
102
  assert resp.form["username"].value == user.username
90
103
  assert resp.form["email"].value == user.email
@@ -111,17 +124,34 @@ class TestUser(AbstractViewsTests):
111
124
  assert value == getattr(user, key)
112
125
  else:
113
126
  assert str(value or "") == str(getattr(user, key) or "")
114
- assert set([roles[2].id, roles[3].id]) == set([role.id for role in user.roles])
127
+ assert {roles[2].id, roles[3].id} == {role.id for role in user.roles}
128
+
129
+ log = dbsession.query(Log).one()
130
+ assert log.date != None
131
+ assert log.action == LogAction.UPDATE
132
+ assert log.element_type == "user"
133
+ assert log.element_id == user.id
134
+ assert log.element_name == user.username
135
+ assert log.username == "test_user"
115
136
 
116
137
  def test_delete(self, test_app, users_test_data, dbsession):
117
- from c2cgeoportal_commons.models.static import User, user_role
138
+ from c2cgeoportal_commons.models.main import LogAction
139
+ from c2cgeoportal_commons.models.static import Log, User, user_role
118
140
 
119
141
  user = users_test_data["users"][9]
120
142
  deleted_id = user.id
121
- test_app.delete("/admin/users/{}".format(deleted_id), status=200)
143
+ test_app.delete(f"/admin/users/{deleted_id}", status=200)
122
144
  assert dbsession.query(User).get(deleted_id) is None
123
145
  assert dbsession.query(user_role).filter(user_role.c.user_id == user.id).count() == 0
124
146
 
147
+ log = dbsession.query(Log).one()
148
+ assert log.date != None
149
+ assert log.action == LogAction.DELETE
150
+ assert log.element_type == "user"
151
+ assert log.element_id == user.id
152
+ assert log.element_name == user.username
153
+ assert log.username == "test_user"
154
+
125
155
  @patch("c2cgeoportal_commons.lib.email_.smtplib.SMTP")
126
156
  @patch("c2cgeoportal_admin.views.users.pwgenerator.generate")
127
157
  def test_submit_update(self, pw_gen_mock, smtp_mock, dbsession, test_app, users_test_data):
@@ -129,7 +159,7 @@ class TestUser(AbstractViewsTests):
129
159
  roles = users_test_data["roles"]
130
160
 
131
161
  resp = test_app.post(
132
- "/admin/users/{}".format(user.id),
162
+ f"/admin/users/{user.id}",
133
163
  (
134
164
  ("__formid__", "deform"),
135
165
  ("_charset_", "UTF-8"),
@@ -137,6 +167,7 @@ class TestUser(AbstractViewsTests):
137
167
  ("item_type", "user"),
138
168
  ("id", user.id),
139
169
  ("username", "new_name_withéàô"),
170
+ ("display_name", "New name withéàô"),
140
171
  ("email", "new_mail@valid.net"),
141
172
  ("settings_role_id", roles[2].id),
142
173
  ("__start__", "roles:sequence"),
@@ -146,13 +177,13 @@ class TestUser(AbstractViewsTests):
146
177
  ),
147
178
  status=302,
148
179
  )
149
- assert resp.location == "http://localhost/admin/users/{}?msg_col=submit_ok".format(user.id)
180
+ assert resp.location == f"http://localhost/admin/users/{user.id}?msg_col=submit_ok"
150
181
 
151
182
  dbsession.expire(user)
152
183
  assert user.username == "new_name_withéàô"
153
184
  assert user.email == "new_mail@valid.net"
154
185
  assert user.settings_role.name == "secretary_2"
155
- assert set(r.id for r in user.roles) == set(roles[i].id for i in [0, 3])
186
+ assert {r.id for r in user.roles} == {roles[i].id for i in [0, 3]}
156
187
  assert user.validate_password("pré$ident")
157
188
 
158
189
  assert not pw_gen_mock.called, "method should not have been called"
@@ -163,7 +194,7 @@ class TestUser(AbstractViewsTests):
163
194
  roles = users_test_data["roles"]
164
195
 
165
196
  resp = test_app.post(
166
- "/admin/users/{}".format(user.id),
197
+ f"/admin/users/{user.id}",
167
198
  {
168
199
  "__formid__": "deform",
169
200
  "_charset_": "UTF-8",
@@ -190,7 +221,7 @@ class TestUser(AbstractViewsTests):
190
221
  user = users_test_data["users"][7]
191
222
  roles = users_test_data["roles"]
192
223
 
193
- resp = test_app.get("/admin/users/{}/duplicate".format(user.id), status=200)
224
+ resp = test_app.get(f"/admin/users/{user.id}/duplicate", status=200)
194
225
  form = resp.form
195
226
 
196
227
  assert "" == form["id"].value
@@ -210,7 +241,7 @@ class TestUser(AbstractViewsTests):
210
241
  ).group(1)
211
242
  assert user.id != new_user.id
212
243
  assert user.settings_role_id == new_user.settings_role_id
213
- assert set([role.id for role in user.roles]) == set([role.id for role in new_user.roles])
244
+ assert {role.id for role in user.roles} == {role.id for role in new_user.roles}
214
245
  assert not new_user.is_password_changed
215
246
  assert not new_user.validate_password("pré$ident")
216
247
 
@@ -224,10 +255,12 @@ class TestUser(AbstractViewsTests):
224
255
  @patch("c2cgeoportal_admin.views.users.pwgenerator.generate")
225
256
  @pytest.mark.usefixtures("test_app")
226
257
  def test_submit_new(self, pw_gen_mock, smtp_mock, dbsession, test_app, users_test_data):
258
+ from c2cgeoportal_commons.models.main import LogAction
259
+ from c2cgeoportal_commons.models.static import Log, User
260
+
227
261
  sender_mock = MagicMock()
228
262
  smtp_mock.return_value = sender_mock
229
263
  pw_gen_mock.return_value = "basile"
230
- from c2cgeoportal_commons.models.static import User
231
264
 
232
265
  roles = users_test_data["roles"]
233
266
 
@@ -240,6 +273,7 @@ class TestUser(AbstractViewsTests):
240
273
  ("item_type", "user"),
241
274
  ("id", ""),
242
275
  ("username", "new_user"),
276
+ ("display_name", "New user"),
243
277
  ("email", "valid@email.net"),
244
278
  ("settings_role_id", roles[2].id),
245
279
  ),
@@ -253,6 +287,7 @@ class TestUser(AbstractViewsTests):
253
287
  ).group(1)
254
288
 
255
289
  assert user.username == "new_user"
290
+ assert user.display_name == "New user"
256
291
  assert user.email == "valid@email.net"
257
292
  assert user.settings_role_id == roles[2].id
258
293
  assert user.password is not None and len(user.password)
@@ -266,6 +301,14 @@ class TestUser(AbstractViewsTests):
266
301
  ).decode("utf8")
267
302
  assert "valid@email.net" == parts[0].items()[3][1]
268
303
 
304
+ log = dbsession.query(Log).one()
305
+ assert log.date != None
306
+ assert log.action == LogAction.INSERT
307
+ assert log.element_type == "user"
308
+ assert log.element_id == user.id
309
+ assert log.element_name == user.username
310
+ assert log.username == "test_user"
311
+
269
312
  def test_invalid_email(self, test_app):
270
313
  resp = test_app.post(
271
314
  "/admin/users/new",
@@ -276,6 +319,7 @@ class TestUser(AbstractViewsTests):
276
319
  "item_type": "user",
277
320
  "id": "",
278
321
  "username": "invalid_email",
322
+ "display_name": "Invalid email",
279
323
  "email": "new_mail",
280
324
  "role_name": "secretary_2",
281
325
  "is_password_changed": "false",
@@ -291,56 +335,14 @@ class TestUser(AbstractViewsTests):
291
335
  from c2cgeoportal_admin.views.users import UserViews
292
336
 
293
337
  request = DummyRequest(dbsession=dbsession, params={"offset": 0, "limit": 10})
294
- info = UserViews(request).grid()
295
- assert info.status_int == 500, "Expected 500 status when db error"
338
+ with pytest.raises(pyramid.httpexceptions.HTTPInternalServerError):
339
+ UserViews(request).grid()
296
340
 
297
341
  def test_grid_settings_role_none(self, dbsession, test_app):
298
- """Grid view must work even if a user's settings_role is None"""
342
+ """
343
+ Grid view must work even if a user's settings_role is None.
344
+ """
299
345
  from c2cgeoportal_commons.models.static import User
300
346
 
301
347
  dbsession.add(User("test", email="test@valid.net"))
302
348
  self.check_search(test_app, "test", total=1)
303
-
304
-
305
- @skip_if_ci
306
- @pytest.mark.selenium
307
- @pytest.mark.usefixtures("selenium", "selenium_app", "users_test_data")
308
- class TestUserSelenium:
309
-
310
- _prefix = "/admin/users"
311
-
312
- def test_index(self, selenium, selenium_app, users_test_data, dbsession):
313
- from c2cgeoportal_commons.models.static import User
314
-
315
- selenium.get(selenium_app + self._prefix)
316
-
317
- index_page = IndexPage(selenium)
318
- index_page.select_language("en")
319
- index_page.check_pagination_info("Showing 1 to 23 of 23 rows", 10)
320
- index_page.select_page_size(10)
321
- index_page.check_pagination_info("Showing 1 to 10 of 23 rows", 10)
322
-
323
- # delete
324
- user = users_test_data["users"][3]
325
- deleted_id = user.id
326
- index_page.click_delete(deleted_id)
327
- index_page.check_pagination_info("Showing 1 to 10 of 22 rows", 10)
328
- assert dbsession.query(User).get(deleted_id) is None
329
-
330
- # edit
331
- user = users_test_data["users"][4]
332
- index_page.find_item_action(user.id, "edit").click()
333
-
334
- elem = selenium.find_element_by_xpath("//input[@name ='username']")
335
- elem.clear()
336
- elem.send_keys("new_name_éôù")
337
- elem = selenium.find_element_by_xpath("//input[@name ='email']")
338
- elem.clear()
339
- elem.send_keys("new_email@valid.net")
340
- elem = selenium.find_element_by_xpath("//button[@name='formsubmit']")
341
- elem.click()
342
-
343
- user = dbsession.query(User).filter(User.id == user.id).one()
344
- dbsession.expire(user)
345
- assert user.username == "new_name_éôù"
346
- assert user.email == "new_email@valid.net"
tests/themes_ordering.py CHANGED
@@ -14,7 +14,7 @@ def themes_ordering_test_data(dbsession, transact):
14
14
 
15
15
  themes = []
16
16
  for i in range(0, 25):
17
- theme = Theme(name="theme_{}".format(i), ordering=100)
17
+ theme = Theme(name=f"theme_{i}", ordering=100)
18
18
  dbsession.add(theme)
19
19
  themes.append(theme)
20
20
 
@@ -25,7 +25,6 @@ def themes_ordering_test_data(dbsession, transact):
25
25
 
26
26
  @pytest.mark.usefixtures("themes_ordering_test_data", "test_app")
27
27
  class TestThemesOrdering(TestTreeGroup):
28
-
29
28
  _prefix = "/admin/layertree/ordering"
30
29
 
31
30
  def test_edit(self, test_app, themes_ordering_test_data):
@@ -1,10 +0,0 @@
1
- <ul id="main-menu" class="nav nav-pills nav-stacked">
2
- <li role="presentation" class="{{'active' if request.matched_route.name == 'layertree' else ''}}">
3
- <a href="{{request.route_url('layertree')}}">{{_("Layer tree")}}</a>
4
- </li>
5
- {% for table in tables %}
6
- <li role="presentation" class="{{'active' if request.matchdict and request.matchdict.get('table') == table['key'] else ''}}">
7
- <a href="{{request.route_url('c2cgeoform_index', table=table['key'])}}">{{table['plural']}}</a>
8
- </li>
9
- {% endfor %}
10
- </ul>