c2cgeoportal-admin 2.8.1.181__py3-none-any.whl → 2.9rc2__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 (49) hide show
  1. c2cgeoportal_admin/__init__.py +14 -6
  2. c2cgeoportal_admin/lib/{lingua_extractor.py → lingva_extractor.py} +6 -6
  3. c2cgeoportal_admin/lib/ogcserver_synchronizer.py +6 -5
  4. c2cgeoportal_admin/routes.py +12 -3
  5. c2cgeoportal_admin/schemas/dimensions.py +4 -2
  6. c2cgeoportal_admin/schemas/functionalities.py +9 -12
  7. c2cgeoportal_admin/schemas/interfaces.py +7 -3
  8. c2cgeoportal_admin/schemas/metadata.py +13 -13
  9. c2cgeoportal_admin/schemas/restriction_areas.py +5 -3
  10. c2cgeoportal_admin/schemas/roles.py +7 -3
  11. c2cgeoportal_admin/schemas/treegroup.py +14 -10
  12. c2cgeoportal_admin/schemas/treeitem.py +2 -2
  13. c2cgeoportal_admin/static/theme.css +3 -0
  14. c2cgeoportal_admin/views/dimension_layers.py +11 -7
  15. c2cgeoportal_admin/views/functionalities.py +25 -18
  16. c2cgeoportal_admin/views/interfaces.py +22 -15
  17. c2cgeoportal_admin/views/layer_groups.py +33 -20
  18. c2cgeoportal_admin/views/layers.py +12 -9
  19. c2cgeoportal_admin/views/layers_cog.py +135 -0
  20. c2cgeoportal_admin/views/layers_vectortiles.py +42 -27
  21. c2cgeoportal_admin/views/layers_wms.py +47 -32
  22. c2cgeoportal_admin/views/layers_wmts.py +46 -32
  23. c2cgeoportal_admin/views/layertree.py +9 -8
  24. c2cgeoportal_admin/views/logged_views.py +15 -12
  25. c2cgeoportal_admin/views/logs.py +9 -8
  26. c2cgeoportal_admin/views/oauth2_clients.py +26 -22
  27. c2cgeoportal_admin/views/ogc_servers.py +48 -34
  28. c2cgeoportal_admin/views/restriction_areas.py +29 -19
  29. c2cgeoportal_admin/views/roles.py +29 -19
  30. c2cgeoportal_admin/views/themes.py +35 -24
  31. c2cgeoportal_admin/views/themes_ordering.py +11 -11
  32. c2cgeoportal_admin/views/treeitems.py +13 -11
  33. c2cgeoportal_admin/views/users.py +37 -22
  34. c2cgeoportal_admin/widgets.py +3 -3
  35. {c2cgeoportal_admin-2.8.1.181.dist-info → c2cgeoportal_admin-2.9rc2.dist-info}/METADATA +3 -12
  36. {c2cgeoportal_admin-2.8.1.181.dist-info → c2cgeoportal_admin-2.9rc2.dist-info}/RECORD +48 -46
  37. {c2cgeoportal_admin-2.8.1.181.dist-info → c2cgeoportal_admin-2.9rc2.dist-info}/WHEEL +1 -1
  38. c2cgeoportal_admin-2.9rc2.dist-info/entry_points.txt +5 -0
  39. tests/__init__.py +13 -8
  40. tests/conftest.py +20 -10
  41. tests/test_layers_cog.py +243 -0
  42. tests/test_layers_vectortiles.py +2 -7
  43. tests/{test_lingua_extractor_config.py → test_lingva_extractor_config.py} +3 -3
  44. tests/test_logs.py +3 -4
  45. tests/test_oauth2_clients.py +3 -2
  46. tests/test_role.py +3 -2
  47. tests/test_user.py +8 -2
  48. c2cgeoportal_admin-2.8.1.181.dist-info/entry_points.txt +0 -6
  49. {c2cgeoportal_admin-2.8.1.181.dist-info → c2cgeoportal_admin-2.9rc2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,243 @@
1
+ # pylint: disable=no-self-use
2
+
3
+ import re
4
+ from typing import Any
5
+
6
+ import pytest
7
+ from sqlalchemy.orm import Session, SessionTransaction
8
+ from webtest import TestApp as WebTestApp # Avoid warning with pytest
9
+
10
+ from . import AbstractViewsTests, factory_build_layers, get_test_default_layers
11
+
12
+
13
+ @pytest.fixture(scope="function")
14
+ @pytest.mark.usefixtures("dbsession", "transact")
15
+ def layer_cog_test_data(dbsession: Session, transact: SessionTransaction) -> dict[str, Any]:
16
+ del transact
17
+
18
+ from c2cgeoportal_commons.models.main import LayerCOG
19
+
20
+ def layer_builder(i: int) -> LayerCOG:
21
+ name = f"layer_cog_{i}"
22
+ layer = LayerCOG(name=name)
23
+ layer.public = 1 == i % 2
24
+ layer.url = "https://example.com/image.tiff"
25
+ return layer
26
+
27
+ data = factory_build_layers(layer_builder, dbsession, add_dimension=False)
28
+ data["default"] = get_test_default_layers(dbsession, None)
29
+
30
+ dbsession.flush()
31
+
32
+ yield data
33
+
34
+
35
+ @pytest.mark.usefixtures("layer_cog_test_data", "test_app")
36
+ class TestLayerVectortiles(AbstractViewsTests):
37
+ _prefix = "/admin/layers_cog"
38
+
39
+ def test_index_rendering(self, test_app: WebTestApp) -> None:
40
+ resp = self.get(test_app)
41
+
42
+ self.check_left_menu(resp, "COG Layers")
43
+
44
+ expected = [
45
+ ("actions", "", "false"),
46
+ ("id", "id", "true"),
47
+ ("name", "Name"),
48
+ ("description", "Description"),
49
+ ("public", "Public"),
50
+ ("geo_table", "Geo table"),
51
+ ("exclude_properties", "Exclude properties"),
52
+ ("url", "URL"),
53
+ ("interfaces", "Interfaces"),
54
+ ("restrictionareas", "Restriction areas", "false"),
55
+ ("parents_relation", "Parents", "false"),
56
+ ("metadatas", "Metadatas", "false"),
57
+ ]
58
+ self.check_grid_headers(resp, expected)
59
+
60
+ def test_grid_complex_column_val(self, test_app: WebTestApp, layer_cog_test_data: dict[str, Any]) -> None:
61
+ json = self.check_search(test_app, search="layer", sort="name")
62
+
63
+ row = json["rows"][0]
64
+ layer = layer_cog_test_data["layers"][0]
65
+
66
+ assert layer.name == row["name"]
67
+ assert layer.id == int(row["_id_"])
68
+
69
+ def test_new(self, test_app: WebTestApp, layer_cog_test_data: dict[str, Any], dbsession: Session) -> None:
70
+ default_cog = layer_cog_test_data["default"]["cog"]
71
+ default_cog.name = "so can I not be found"
72
+ dbsession.flush()
73
+
74
+ form = self.get_item(test_app, "new").form
75
+
76
+ assert "" == self.get_first_field_named(form, "name").value
77
+ assert "" == self.get_first_field_named(form, "id").value
78
+ assert "" == self.get_first_field_named(form, "url").value
79
+
80
+ def test_grid_search(self, test_app: WebTestApp) -> None:
81
+ self.check_search(test_app, "layer_cog_10", total=1)
82
+
83
+ def test_base_edit(self, test_app: WebTestApp, layer_cog_test_data: dict[str, Any]) -> None:
84
+ layer = layer_cog_test_data["layers"][10]
85
+
86
+ form = self.get_item(test_app, layer.id).form
87
+
88
+ assert "layer_cog_10" == self.get_first_field_named(form, "name").value
89
+ assert "" == self.get_first_field_named(form, "description").value
90
+
91
+ def test_public_checkbox_edit(self, test_app: WebTestApp, layer_cog_test_data: dict[str, Any]) -> None:
92
+ layer = layer_cog_test_data["layers"][10]
93
+ form = self.get_item(test_app, layer.id).form
94
+ assert not form["public"].checked
95
+
96
+ layer = layer_cog_test_data["layers"][11]
97
+ form = self.get_item(test_app, layer.id).form
98
+ assert form["public"].checked
99
+
100
+ def test_edit(
101
+ self, test_app: WebTestApp, layer_cog_test_data: dict[str, Any], dbsession: Session
102
+ ) -> None:
103
+ from c2cgeoportal_commons.models.main import Log, LogAction
104
+
105
+ layer = layer_cog_test_data["layers"][0]
106
+
107
+ form = self.get_item(test_app, layer.id).form
108
+
109
+ assert str(layer.id) == self.get_first_field_named(form, "id").value
110
+ assert "hidden" == self.get_first_field_named(form, "id").attrs["type"]
111
+ assert layer.name == self.get_first_field_named(form, "name").value
112
+ assert str(layer.description or "") == self.get_first_field_named(form, "description").value
113
+ assert layer.public is False
114
+ assert layer.public == form["public"].checked
115
+ assert str(layer.geo_table or "") == form["geo_table"].value
116
+ assert str(layer.exclude_properties or "") == form["exclude_properties"].value
117
+ # assert str(layer.url or "") == form["url"].value
118
+
119
+ interfaces = layer_cog_test_data["interfaces"]
120
+ assert {interfaces[0].id, interfaces[2].id} == {i.id for i in layer.interfaces}
121
+ self._check_interfaces(form, interfaces, layer)
122
+
123
+ ras = layer_cog_test_data["restrictionareas"]
124
+ assert {ras[0].id, ras[2].id} == {i.id for i in layer.restrictionareas}
125
+ self._check_restrictionsareas(form, ras, layer)
126
+
127
+ new_values = {
128
+ "name": "new_name",
129
+ "description": "new description",
130
+ "public": True,
131
+ "geo_table": "new_geo_table",
132
+ "exclude_properties": "property1,property2",
133
+ "url": "https://example.com/image.tiff",
134
+ }
135
+
136
+ for key, value in new_values.items():
137
+ self.set_first_field_named(form, key, value)
138
+ form["interfaces"] = [interfaces[1].id, interfaces[3].id]
139
+ form["restrictionareas"] = [ras[1].id, ras[3].id]
140
+
141
+ resp = form.submit("submit")
142
+ assert str(layer.id) == re.match(
143
+ rf"http://localhost{self._prefix}/(.*)\?msg_col=submit_ok", resp.location
144
+ ).group(1)
145
+
146
+ dbsession.expire(layer)
147
+ for key, value in new_values.items():
148
+ if isinstance(value, bool):
149
+ assert value == getattr(layer, key)
150
+ else:
151
+ assert str(value or "") == str(getattr(layer, key) or "")
152
+ assert {interfaces[1].id, interfaces[3].id} == {interface.id for interface in layer.interfaces}
153
+ assert {ras[1].id, ras[3].id} == {ra.id for ra in layer.restrictionareas}
154
+
155
+ log = dbsession.query(Log).one()
156
+ assert log.date != None
157
+ assert log.action == LogAction.UPDATE
158
+ assert log.element_type == "layer_cog"
159
+ assert log.element_id == layer.id
160
+ assert log.element_name == layer.name
161
+ assert log.username == "test_user"
162
+
163
+ def test_submit_new(
164
+ self, dbsession: Session, test_app: WebTestApp, layer_cog_test_data: dict[str, Any]
165
+ ) -> None:
166
+ from c2cgeoportal_commons.models.main import LayerCOG, Log, LogAction
167
+
168
+ resp = test_app.post(
169
+ "/admin/layers_cog/new",
170
+ {
171
+ "name": "new_name",
172
+ "description": "new description",
173
+ "public": True,
174
+ "url": "https://example.com/image.tiff",
175
+ },
176
+ status=302,
177
+ )
178
+
179
+ layer = dbsession.query(LayerCOG).filter(LayerCOG.name == "new_name").one()
180
+ assert str(layer.id) == re.match(
181
+ r"http://localhost/admin/layers_cog/(.*)\?msg_col=submit_ok", resp.location
182
+ ).group(1)
183
+
184
+ log = dbsession.query(Log).one()
185
+ assert log.date != None
186
+ assert log.action == LogAction.INSERT
187
+ assert log.element_type == "layer_cog"
188
+ assert log.element_id == layer.id
189
+ assert log.element_name == layer.name
190
+ assert log.username == "test_user"
191
+
192
+ def test_duplicate(
193
+ self, layer_cog_test_data: dict[str, Any], test_app: WebTestApp, dbsession: Session
194
+ ) -> None:
195
+ from c2cgeoportal_commons.models.main import LayerCOG
196
+
197
+ layer = layer_cog_test_data["layers"][3]
198
+
199
+ resp = test_app.get(f"/admin/layers_cog/{layer.id}/duplicate", status=200)
200
+ form = resp.form
201
+
202
+ assert "" == self.get_first_field_named(form, "id").value
203
+ assert layer.name == self.get_first_field_named(form, "name").value
204
+ assert str(layer.description or "") == self.get_first_field_named(form, "description").value
205
+ assert layer.public is True
206
+ assert layer.public == form["public"].checked
207
+ assert str(layer.geo_table or "") == form["geo_table"].value
208
+ assert str(layer.exclude_properties or "") == form["exclude_properties"].value
209
+ # assert str(layer.url or "") == form["url"].value
210
+ interfaces = layer_cog_test_data["interfaces"]
211
+ assert {interfaces[3].id, interfaces[1].id} == {i.id for i in layer.interfaces}
212
+ self._check_interfaces(form, interfaces, layer)
213
+
214
+ self.set_first_field_named(form, "name", "clone")
215
+ resp = form.submit("submit")
216
+
217
+ layer = dbsession.query(LayerCOG).filter(LayerCOG.name == "clone").one()
218
+ assert str(layer.id) == re.match(
219
+ r"http://localhost/admin/layers_cog/(.*)\?msg_col=submit_ok", resp.location
220
+ ).group(1)
221
+
222
+ assert layer.id == layer.metadatas[0].item_id
223
+ assert layer_cog_test_data["layers"][3].metadatas[0].name == layer.metadatas[0].name
224
+ assert layer_cog_test_data["layers"][3].metadatas[1].name == layer.metadatas[1].name
225
+
226
+ def test_delete(self, test_app: WebTestApp, dbsession: Session) -> None:
227
+ from c2cgeoportal_commons.models.main import Layer, LayerCOG, Log, LogAction, TreeItem
228
+
229
+ layer = dbsession.query(LayerCOG).first()
230
+
231
+ test_app.delete(f"/admin/layers_cog/{layer.id}", status=200)
232
+
233
+ assert dbsession.query(LayerCOG).get(layer.id) is None
234
+ assert dbsession.query(Layer).get(layer.id) is None
235
+ assert dbsession.query(TreeItem).get(layer.id) is None
236
+
237
+ log = dbsession.query(Log).one()
238
+ assert log.date != None
239
+ assert log.action == LogAction.DELETE
240
+ assert log.element_type == "layer_cog"
241
+ assert log.element_id == layer.id
242
+ assert log.element_name == layer.name
243
+ assert log.username == "test_user"
@@ -12,12 +12,7 @@ from . import AbstractViewsTests, factory_build_layers, get_test_default_layers
12
12
  def layer_vectortiles_test_data(dbsession, transact):
13
13
  del transact
14
14
 
15
- from c2cgeoportal_commons.models.main import LayerVectorTiles, OGCServer
16
-
17
- servers = [OGCServer(name=f"server_{i}") for i in range(0, 4)]
18
- for i, server in enumerate(servers):
19
- server.url = f"http://wms.geo.admin.ch_{i}"
20
- server.image_type = "image/jpeg" if i % 2 == 0 else "image/png"
15
+ from c2cgeoportal_commons.models.main import LayerVectorTiles
21
16
 
22
17
  def layer_builder(i):
23
18
  name = f"layer_vectortiles_{i}"
@@ -29,7 +24,7 @@ def layer_vectortiles_test_data(dbsession, transact):
29
24
  return layer
30
25
 
31
26
  data = factory_build_layers(layer_builder, dbsession)
32
- data["default"] = get_test_default_layers(dbsession, server)
27
+ data["default"] = get_test_default_layers(dbsession, None)
33
28
 
34
29
  dbsession.flush()
35
30
 
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2022-2023, Camptocamp SA
1
+ # Copyright (c) 2022-2024, Camptocamp SA
2
2
  # All rights reserved.
3
3
 
4
4
  # Redistribution and use in source and binary forms, with or without
@@ -33,7 +33,7 @@ import pytest
33
33
  import yaml
34
34
  from c2c.template.config import config as configuration
35
35
 
36
- from c2cgeoportal_admin.lib.lingua_extractor import GeomapfishConfigExtractor
36
+ from c2cgeoportal_admin.lib.lingva_extractor import GeomapfishConfigExtractor
37
37
 
38
38
  GMF_CONFIG = """
39
39
  vars:
@@ -50,7 +50,7 @@ vars:
50
50
 
51
51
  class TestGeomapfishConfigExtractor:
52
52
  @patch(
53
- "c2cgeoportal_admin.lib.lingua_extractor.open",
53
+ "c2cgeoportal_admin.lib.lingva_extractor.open",
54
54
  mock_open(read_data=GMF_CONFIG),
55
55
  )
56
56
  def test_extract_config(self):
tests/test_logs.py CHANGED
@@ -1,10 +1,9 @@
1
1
  # pylint: disable=no-self-use,unsubscriptable-object
2
2
 
3
3
  import datetime
4
+ from datetime import timezone
4
5
 
5
6
  import pytest
6
- import pytz
7
- from pyramid.testing import DummyRequest
8
7
 
9
8
  from . import AbstractViewsTests
10
9
 
@@ -22,7 +21,7 @@ def logs_test_data(dbsession, transact):
22
21
  logs = []
23
22
  for i in range(0, 5):
24
23
  log = MainLog(
25
- date=datetime.datetime.now(pytz.utc),
24
+ date=datetime.datetime.now(timezone.utc),
26
25
  action=[LogAction.INSERT, LogAction.UPDATE, LogAction.DELETE][i % 3],
27
26
  element_type="role",
28
27
  element_id=i,
@@ -34,7 +33,7 @@ def logs_test_data(dbsession, transact):
34
33
  logs.append(log)
35
34
 
36
35
  log = StaticLog(
37
- date=datetime.datetime.now(pytz.utc),
36
+ date=datetime.datetime.now(timezone.utc),
38
37
  action=[LogAction.INSERT, LogAction.UPDATE, LogAction.DELETE][i % 3],
39
38
  element_type="user",
40
39
  element_id=i,
@@ -3,6 +3,7 @@
3
3
  import re
4
4
  from uuid import uuid4
5
5
 
6
+ import pyramid.httpexceptions
6
7
  import pytest
7
8
  from pyramid.testing import DummyRequest
8
9
 
@@ -181,5 +182,5 @@ class TestOAuth2Client(TestTreeGroup):
181
182
  from c2cgeoportal_admin.views.oauth2_clients import OAuth2ClientViews
182
183
 
183
184
  request = DummyRequest(dbsession=dbsession, params={"offset": 0, "limit": 10})
184
- info = OAuth2ClientViews(request).grid()
185
- assert info.status_int == 500, "Expected 500 status when db error"
185
+ with pytest.raises(pyramid.httpexceptions.HTTPInternalServerError):
186
+ OAuth2ClientViews(request).grid()
tests/test_role.py CHANGED
@@ -3,6 +3,7 @@
3
3
  import json
4
4
  import re
5
5
 
6
+ import pyramid.httpexceptions
6
7
  import pytest
7
8
  from geoalchemy2.shape import from_shape, to_shape
8
9
  from pyramid.testing import DummyRequest
@@ -341,5 +342,5 @@ class TestRole(TestTreeGroup):
341
342
  from c2cgeoportal_admin.views.roles import RoleViews
342
343
 
343
344
  request = DummyRequest(dbsession=dbsession, params={"offset": 0, "limit": 10})
344
- info = RoleViews(request).grid()
345
- assert info.status_int == 500, "Expected 500 status when db error"
345
+ with pytest.raises(pyramid.httpexceptions.HTTPInternalServerError):
346
+ RoleViews(request).grid()
tests/test_user.py CHANGED
@@ -4,6 +4,7 @@ import email
4
4
  import re
5
5
  from unittest.mock import MagicMock, patch
6
6
 
7
+ import pyramid.httpexceptions
7
8
  import pytest
8
9
  from pyramid.testing import DummyRequest
9
10
 
@@ -59,6 +60,7 @@ class TestUser(AbstractViewsTests):
59
60
  ("actions", "", "false"),
60
61
  ("id", "id", "true"),
61
62
  ("username", "Username"),
63
+ ("display_name", "Display name", "true"),
62
64
  ("email", "Email"),
63
65
  ("last_login", "Last login"),
64
66
  ("expire_on", "Expiration date"),
@@ -165,6 +167,7 @@ class TestUser(AbstractViewsTests):
165
167
  ("item_type", "user"),
166
168
  ("id", user.id),
167
169
  ("username", "new_name_withéàô"),
170
+ ("display_name", "New name withéàô"),
168
171
  ("email", "new_mail@valid.net"),
169
172
  ("settings_role_id", roles[2].id),
170
173
  ("__start__", "roles:sequence"),
@@ -270,6 +273,7 @@ class TestUser(AbstractViewsTests):
270
273
  ("item_type", "user"),
271
274
  ("id", ""),
272
275
  ("username", "new_user"),
276
+ ("display_name", "New user"),
273
277
  ("email", "valid@email.net"),
274
278
  ("settings_role_id", roles[2].id),
275
279
  ),
@@ -283,6 +287,7 @@ class TestUser(AbstractViewsTests):
283
287
  ).group(1)
284
288
 
285
289
  assert user.username == "new_user"
290
+ assert user.display_name == "New user"
286
291
  assert user.email == "valid@email.net"
287
292
  assert user.settings_role_id == roles[2].id
288
293
  assert user.password is not None and len(user.password)
@@ -314,6 +319,7 @@ class TestUser(AbstractViewsTests):
314
319
  "item_type": "user",
315
320
  "id": "",
316
321
  "username": "invalid_email",
322
+ "display_name": "Invalid email",
317
323
  "email": "new_mail",
318
324
  "role_name": "secretary_2",
319
325
  "is_password_changed": "false",
@@ -329,8 +335,8 @@ class TestUser(AbstractViewsTests):
329
335
  from c2cgeoportal_admin.views.users import UserViews
330
336
 
331
337
  request = DummyRequest(dbsession=dbsession, params={"offset": 0, "limit": 10})
332
- info = UserViews(request).grid()
333
- assert info.status_int == 500, "Expected 500 status when db error"
338
+ with pytest.raises(pyramid.httpexceptions.HTTPInternalServerError):
339
+ UserViews(request).grid()
334
340
 
335
341
  def test_grid_settings_role_none(self, dbsession, test_app):
336
342
  """
@@ -1,6 +0,0 @@
1
- [lingua.extractors]
2
- geomapfish-admin-config = c2cgeoportal_admin.lib.lingua_extractor:GeomapfishConfigExtractor
3
-
4
- [paste.app_factory]
5
- main = c2cgeoportal_admin:main
6
-