c2cgeoportal-admin 2.6.0__py3-none-any.whl → 2.9rc45__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 (80) hide show
  1. c2cgeoportal_admin/__init__.py +42 -12
  2. c2cgeoportal_admin/lib/lingva_extractor.py +77 -0
  3. c2cgeoportal_admin/lib/ogcserver_synchronizer.py +170 -57
  4. c2cgeoportal_admin/py.typed +0 -0
  5. c2cgeoportal_admin/routes.py +18 -6
  6. c2cgeoportal_admin/schemas/dimensions.py +16 -10
  7. c2cgeoportal_admin/schemas/functionalities.py +59 -21
  8. c2cgeoportal_admin/schemas/interfaces.py +26 -18
  9. c2cgeoportal_admin/schemas/metadata.py +101 -48
  10. c2cgeoportal_admin/schemas/restriction_areas.py +25 -19
  11. c2cgeoportal_admin/schemas/roles.py +12 -6
  12. c2cgeoportal_admin/schemas/treegroup.py +46 -21
  13. c2cgeoportal_admin/schemas/treeitem.py +3 -4
  14. c2cgeoportal_admin/static/layertree.css +3 -4
  15. c2cgeoportal_admin/static/navbar.css +36 -35
  16. c2cgeoportal_admin/static/theme.css +19 -9
  17. c2cgeoportal_admin/subscribers.py +3 -3
  18. c2cgeoportal_admin/templates/404.jinja2 +18 -2
  19. c2cgeoportal_admin/templates/layertree.jinja2 +31 -9
  20. c2cgeoportal_admin/templates/navigation_navbar.jinja2 +33 -0
  21. c2cgeoportal_admin/templates/ogcserver_synchronize.jinja2 +12 -0
  22. c2cgeoportal_admin/templates/widgets/functionality_fields.pt +51 -0
  23. c2cgeoportal_admin/templates/widgets/metadata.pt +7 -1
  24. c2cgeoportal_admin/views/__init__.py +29 -0
  25. c2cgeoportal_admin/views/dimension_layers.py +14 -9
  26. c2cgeoportal_admin/views/functionalities.py +52 -18
  27. c2cgeoportal_admin/views/home.py +5 -5
  28. c2cgeoportal_admin/views/interfaces.py +26 -20
  29. c2cgeoportal_admin/views/layer_groups.py +36 -25
  30. c2cgeoportal_admin/views/layers.py +17 -13
  31. c2cgeoportal_admin/views/layers_cog.py +135 -0
  32. c2cgeoportal_admin/views/layers_vectortiles.py +62 -27
  33. c2cgeoportal_admin/views/layers_wms.py +55 -34
  34. c2cgeoportal_admin/views/layers_wmts.py +54 -34
  35. c2cgeoportal_admin/views/layertree.py +38 -29
  36. c2cgeoportal_admin/views/logged_views.py +83 -0
  37. c2cgeoportal_admin/views/logs.py +91 -0
  38. c2cgeoportal_admin/views/oauth2_clients.py +30 -18
  39. c2cgeoportal_admin/views/ogc_servers.py +132 -36
  40. c2cgeoportal_admin/views/restriction_areas.py +39 -27
  41. c2cgeoportal_admin/views/roles.py +42 -28
  42. c2cgeoportal_admin/views/themes.py +47 -35
  43. c2cgeoportal_admin/views/themes_ordering.py +19 -14
  44. c2cgeoportal_admin/views/treeitems.py +21 -17
  45. c2cgeoportal_admin/views/users.py +46 -26
  46. c2cgeoportal_admin/widgets.py +17 -14
  47. {c2cgeoportal_admin-2.6.0.dist-info → c2cgeoportal_admin-2.9rc45.dist-info}/METADATA +12 -12
  48. c2cgeoportal_admin-2.9rc45.dist-info/RECORD +97 -0
  49. {c2cgeoportal_admin-2.6.0.dist-info → c2cgeoportal_admin-2.9rc45.dist-info}/WHEEL +1 -1
  50. c2cgeoportal_admin-2.9rc45.dist-info/entry_points.txt +5 -0
  51. tests/__init__.py +24 -20
  52. tests/conftest.py +22 -11
  53. tests/test_edit_url.py +11 -14
  54. tests/test_functionalities.py +52 -14
  55. tests/test_home.py +0 -1
  56. tests/test_interface.py +34 -11
  57. tests/test_layer_groups.py +57 -27
  58. tests/test_layers_cog.py +243 -0
  59. tests/test_layers_vectortiles.py +43 -25
  60. tests/test_layers_wms.py +67 -45
  61. tests/test_layers_wmts.py +47 -26
  62. tests/test_layertree.py +99 -16
  63. tests/test_left_menu.py +0 -1
  64. tests/test_lingva_extractor_config.py +64 -0
  65. tests/test_logs.py +102 -0
  66. tests/test_main.py +3 -1
  67. tests/test_metadatas.py +34 -21
  68. tests/test_oauth2_clients.py +40 -11
  69. tests/test_ogc_servers.py +84 -35
  70. tests/test_restriction_areas.py +38 -15
  71. tests/test_role.py +71 -43
  72. tests/test_themes.py +71 -37
  73. tests/test_themes_ordering.py +1 -2
  74. tests/test_treegroup.py +2 -2
  75. tests/test_user.py +56 -19
  76. tests/themes_ordering.py +1 -2
  77. c2cgeoportal_admin/templates/navigation_vertical.jinja2 +0 -33
  78. c2cgeoportal_admin-2.6.0.dist-info/RECORD +0 -89
  79. c2cgeoportal_admin-2.6.0.dist-info/entry_points.txt +0 -3
  80. {c2cgeoportal_admin-2.6.0.dist-info → c2cgeoportal_admin-2.9rc45.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,4 @@
1
- # -*- coding: utf-8 -*-
2
-
3
- # Copyright (c) 2017-2020, Camptocamp SA
1
+ # Copyright (c) 2017-2024, Camptocamp SA
4
2
  # All rights reserved.
5
3
 
6
4
  # Redistribution and use in source and binary forms, with or without
@@ -29,67 +27,104 @@
29
27
 
30
28
 
31
29
  from functools import partial
30
+ from typing import cast
32
31
 
32
+ import sqlalchemy
33
+ import sqlalchemy.orm.query
33
34
  from c2cgeoform.schema import GeoFormSchemaNode
34
- from c2cgeoform.views.abstract_views import ListField
35
+ from c2cgeoform.views.abstract_views import (
36
+ DeleteResponse,
37
+ GridResponse,
38
+ IndexResponse,
39
+ ListField,
40
+ ObjectResponse,
41
+ SaveResponse,
42
+ )
35
43
  from deform.widget import FormWidget
44
+ from pyramid.httpexceptions import HTTPNotFound
36
45
  from pyramid.view import view_config, view_defaults
37
46
 
47
+ from c2cgeoportal_admin import _
38
48
  from c2cgeoportal_admin.schemas.interfaces import interfaces_schema_node
39
- from c2cgeoportal_admin.schemas.metadata import metadatas_schema_node
49
+ from c2cgeoportal_admin.schemas.metadata import metadata_schema_node
40
50
  from c2cgeoportal_admin.schemas.restriction_areas import restrictionareas_schema_node
41
51
  from c2cgeoportal_admin.schemas.treeitem import parent_id_node
42
52
  from c2cgeoportal_admin.views.dimension_layers import DimensionLayerViews
53
+ from c2cgeoportal_commons.lib.literal import Literal
43
54
  from c2cgeoportal_commons.models.main import LayerGroup, LayerVectorTiles
44
55
 
45
56
  _list_field = partial(ListField, LayerVectorTiles)
46
57
 
47
58
 
48
59
  base_schema = GeoFormSchemaNode(LayerVectorTiles, widget=FormWidget(fields_template="layer_fields"))
49
- base_schema.add(metadatas_schema_node.clone())
50
- base_schema.add(interfaces_schema_node.clone())
51
- base_schema.add(restrictionareas_schema_node.clone())
60
+ base_schema.add(metadata_schema_node(LayerVectorTiles.metadatas, LayerVectorTiles))
61
+ base_schema.add(interfaces_schema_node(LayerVectorTiles.interfaces))
62
+ base_schema.add(restrictionareas_schema_node(LayerVectorTiles.restrictionareas))
52
63
  base_schema.add_unique_validator(LayerVectorTiles.name, LayerVectorTiles.id)
53
64
  base_schema.add(parent_id_node(LayerGroup))
54
65
 
55
66
 
56
67
  @view_defaults(match_param="table=layers_vectortiles")
57
- class LayerVectorTilesViews(DimensionLayerViews):
68
+ class LayerVectorTilesViews(DimensionLayerViews[LayerVectorTiles]):
69
+ """The vector tiles administration view."""
70
+
58
71
  _list_fields = (
59
- DimensionLayerViews._list_fields
72
+ DimensionLayerViews._list_fields # typer: ignore[misc] # pylint: disable=protected-access
60
73
  + [_list_field("style"), _list_field("xyz")]
61
- + DimensionLayerViews._extra_list_fields
74
+ + DimensionLayerViews._extra_list_fields # pylint: disable=protected-access
62
75
  )
76
+
63
77
  _id_field = "id"
64
78
  _model = LayerVectorTiles
65
79
  _base_schema = base_schema
66
80
 
67
- def _base_query(self, query=None):
81
+ def _base_query(self) -> sqlalchemy.orm.query.Query[LayerVectorTiles]:
82
+ return super()._sub_query(self._request.dbsession.query(LayerVectorTiles).distinct())
83
+
84
+ def _sub_query(
85
+ self, query: sqlalchemy.orm.query.Query[LayerVectorTiles] | None
86
+ ) -> sqlalchemy.orm.query.Query[LayerVectorTiles]:
68
87
  del query
69
- return super()._base_query(self._request.dbsession.query(LayerVectorTiles).distinct())
88
+ return self._base_query()
70
89
 
71
- @view_config(route_name="c2cgeoform_index", renderer="../templates/index.jinja2")
72
- def index(self):
90
+ @view_config(route_name="c2cgeoform_index", renderer="../templates/index.jinja2") # type: ignore[misc]
91
+ def index(self) -> IndexResponse:
73
92
  return super().index()
74
93
 
75
- @view_config(route_name="c2cgeoform_grid", renderer="fast_json")
76
- def grid(self):
94
+ @view_config(route_name="c2cgeoform_grid", renderer="fast_json") # type: ignore[misc]
95
+ def grid(self) -> GridResponse:
77
96
  return super().grid()
78
97
 
79
- @view_config(route_name="c2cgeoform_item", request_method="GET", renderer="../templates/edit.jinja2")
80
- def view(self):
81
- return super().edit()
82
-
83
- @view_config(route_name="c2cgeoform_item", request_method="POST", renderer="../templates/edit.jinja2")
84
- def save(self):
98
+ def schema(self) -> GeoFormSchemaNode:
99
+ try:
100
+ obj = self._get_object()
101
+ except HTTPNotFound:
102
+ obj = None
103
+
104
+ schema = cast(GeoFormSchemaNode, self._base_schema.clone())
105
+ if obj is not None:
106
+ schema["style"].description = Literal(
107
+ _("{}<br>Current runtime value is: {}").format(
108
+ schema["style"].description,
109
+ obj.style_description(self._request),
110
+ )
111
+ )
112
+ return schema
113
+
114
+ @view_config(route_name="c2cgeoform_item", request_method="GET", renderer="../templates/edit.jinja2") # type: ignore[misc]
115
+ def view(self) -> ObjectResponse:
116
+ return super().edit(self.schema())
117
+
118
+ @view_config(route_name="c2cgeoform_item", request_method="POST", renderer="../templates/edit.jinja2") # type: ignore[misc]
119
+ def save(self) -> SaveResponse:
85
120
  return super().save()
86
121
 
87
- @view_config(route_name="c2cgeoform_item", request_method="DELETE", renderer="fast_json")
88
- def delete(self):
122
+ @view_config(route_name="c2cgeoform_item", request_method="DELETE", renderer="fast_json") # type: ignore[misc]
123
+ def delete(self) -> DeleteResponse:
89
124
  return super().delete()
90
125
 
91
- @view_config(
126
+ @view_config( # type: ignore[misc]
92
127
  route_name="c2cgeoform_item_duplicate", request_method="GET", renderer="../templates/edit.jinja2"
93
128
  )
94
- def duplicate(self):
129
+ def duplicate(self) -> ObjectResponse:
95
130
  return super().duplicate()
@@ -1,6 +1,4 @@
1
- # -*- coding: utf-8 -*-
2
-
3
- # Copyright (c) 2017-2020, Camptocamp SA
1
+ # Copyright (c) 2017-2024, Camptocamp SA
4
2
  # All rights reserved.
5
3
 
6
4
  # Redistribution and use in source and binary forms, with or without
@@ -27,13 +25,21 @@
27
25
  # of the authors and should not be interpreted as representing official policies,
28
26
  # either expressed or implied, of the FreeBSD Project.
29
27
 
30
- # pylint: disable=no-member
31
-
32
28
 
33
29
  from functools import partial
34
30
 
31
+ import sqlalchemy
32
+ from c2cgeoform import JSONDict
35
33
  from c2cgeoform.schema import GeoFormSchemaNode
36
- from c2cgeoform.views.abstract_views import ItemAction, ListField
34
+ from c2cgeoform.views.abstract_views import (
35
+ DeleteResponse,
36
+ GridResponse,
37
+ IndexResponse,
38
+ ItemAction,
39
+ ListField,
40
+ ObjectResponse,
41
+ SaveResponse,
42
+ )
37
43
  from deform.widget import FormWidget
38
44
  from pyramid.view import view_config, view_defaults
39
45
  from sqlalchemy import delete, insert, inspect, update
@@ -42,27 +48,29 @@ from zope.sqlalchemy import mark_changed
42
48
  from c2cgeoportal_admin import _
43
49
  from c2cgeoportal_admin.schemas.dimensions import dimensions_schema_node
44
50
  from c2cgeoportal_admin.schemas.interfaces import interfaces_schema_node
45
- from c2cgeoportal_admin.schemas.metadata import metadatas_schema_node
51
+ from c2cgeoportal_admin.schemas.metadata import metadata_schema_node
46
52
  from c2cgeoportal_admin.schemas.restriction_areas import restrictionareas_schema_node
47
53
  from c2cgeoportal_admin.schemas.treeitem import parent_id_node
48
54
  from c2cgeoportal_admin.views.dimension_layers import DimensionLayerViews
49
- from c2cgeoportal_commons.models.main import LayerGroup, LayerWMS, LayerWMTS, OGCServer, TreeItem
55
+ from c2cgeoportal_commons.models.main import LayerGroup, LayerWMS, LayerWMTS, LogAction, OGCServer, TreeItem
50
56
 
51
57
  _list_field = partial(ListField, LayerWMS)
52
58
 
53
59
  base_schema = GeoFormSchemaNode(LayerWMS, widget=FormWidget(fields_template="layer_fields"))
54
- base_schema.add(dimensions_schema_node.clone())
55
- base_schema.add(metadatas_schema_node.clone())
56
- base_schema.add(interfaces_schema_node.clone())
57
- base_schema.add(restrictionareas_schema_node.clone())
60
+ base_schema.add(dimensions_schema_node(LayerWMS.dimensions))
61
+ base_schema.add(metadata_schema_node(LayerWMS.metadatas, LayerWMS))
62
+ base_schema.add(interfaces_schema_node(LayerWMS.interfaces))
63
+ base_schema.add(restrictionareas_schema_node(LayerWMS.restrictionareas))
58
64
  base_schema.add_unique_validator(LayerWMS.name, LayerWMS.id)
59
65
  base_schema.add(parent_id_node(LayerGroup))
60
66
 
61
67
 
62
68
  @view_defaults(match_param="table=layers_wms")
63
- class LayerWmsViews(DimensionLayerViews):
69
+ class LayerWmsViews(DimensionLayerViews[LayerWMS]):
70
+ """The WMS layer administration view."""
71
+
64
72
  _list_fields = (
65
- DimensionLayerViews._list_fields
73
+ DimensionLayerViews._list_fields # pylint: disable=protected-access
66
74
  + [
67
75
  _list_field(
68
76
  "ogc_server",
@@ -77,27 +85,34 @@ class LayerWmsViews(DimensionLayerViews):
77
85
  _list_field("time_mode"),
78
86
  _list_field("time_widget"),
79
87
  ]
80
- + DimensionLayerViews._extra_list_fields
88
+ + DimensionLayerViews._extra_list_fields # pylint: disable=protected-access
81
89
  )
82
90
  _id_field = "id"
83
91
  _model = LayerWMS
84
92
  _base_schema = base_schema
85
93
 
86
- def _base_query(self, query=None):
94
+ def _base_query(self) -> sqlalchemy.orm.query.Query[LayerWMS]:
95
+ return super()._sub_query(
96
+ self._request.dbsession.query(LayerWMS, OGCServer.name).distinct().outerjoin(LayerWMS.ogc_server)
97
+ )
98
+
99
+ def _sub_query(
100
+ self, query: sqlalchemy.orm.query.Query[LayerWMS] | None
101
+ ) -> sqlalchemy.orm.query.Query[LayerWMS]:
87
102
  del query
88
- return super()._base_query(self._request.dbsession.query(LayerWMS).distinct().outerjoin("ogc_server"))
103
+ return self._base_query()
89
104
 
90
- @view_config(route_name="c2cgeoform_index", renderer="../templates/index.jinja2")
91
- def index(self):
105
+ @view_config(route_name="c2cgeoform_index", renderer="../templates/index.jinja2") # type: ignore[misc]
106
+ def index(self) -> IndexResponse:
92
107
  return super().index()
93
108
 
94
- @view_config(route_name="c2cgeoform_grid", renderer="fast_json")
95
- def grid(self):
109
+ @view_config(route_name="c2cgeoform_grid", renderer="fast_json") # type: ignore[misc]
110
+ def grid(self) -> GridResponse:
96
111
  return super().grid()
97
112
 
98
- def _item_actions(self, item, readonly=False):
99
- actions = super()._item_actions(item, readonly)
100
- if inspect(item).persistent:
113
+ def _item_actions(self, item: LayerWMS, readonly: bool = False) -> list[ItemAction]:
114
+ actions: list[ItemAction] = super()._item_actions(item, readonly)
115
+ if inspect(item).persistent: # type: ignore[attr-defined]
101
116
  actions.insert(
102
117
  next((i for i, v in enumerate(actions) if v.name() == "delete")),
103
118
  ItemAction(
@@ -111,8 +126,10 @@ class LayerWmsViews(DimensionLayerViews):
111
126
  )
112
127
  return actions
113
128
 
114
- @view_config(route_name="c2cgeoform_item", request_method="GET", renderer="../templates/edit.jinja2")
115
- def view(self):
129
+ @view_config( # type: ignore[misc]
130
+ route_name="c2cgeoform_item", request_method="GET", renderer="../templates/edit.jinja2"
131
+ )
132
+ def view(self) -> ObjectResponse:
116
133
  if self._is_new():
117
134
  dbsession = self._request.dbsession
118
135
  default_wms = LayerWMS.get_default(dbsession)
@@ -120,22 +137,24 @@ class LayerWmsViews(DimensionLayerViews):
120
137
  return self.copy(default_wms, excludes=["name", "layer"])
121
138
  return super().edit()
122
139
 
123
- @view_config(route_name="c2cgeoform_item", request_method="POST", renderer="../templates/edit.jinja2")
124
- def save(self):
140
+ @view_config( # type: ignore[misc]
141
+ route_name="c2cgeoform_item", request_method="POST", renderer="../templates/edit.jinja2"
142
+ )
143
+ def save(self) -> SaveResponse:
125
144
  return super().save()
126
145
 
127
- @view_config(route_name="c2cgeoform_item", request_method="DELETE", renderer="fast_json")
128
- def delete(self):
146
+ @view_config(route_name="c2cgeoform_item", request_method="DELETE", renderer="fast_json") # type: ignore[misc]
147
+ def delete(self) -> DeleteResponse:
129
148
  return super().delete()
130
149
 
131
- @view_config(
150
+ @view_config( # type: ignore[misc]
132
151
  route_name="c2cgeoform_item_duplicate", request_method="GET", renderer="../templates/edit.jinja2"
133
152
  )
134
- def duplicate(self):
153
+ def duplicate(self) -> ObjectResponse:
135
154
  return super().duplicate()
136
155
 
137
- @view_config(route_name="convert_to_wmts", request_method="POST", renderer="fast_json")
138
- def convert_to_wmts(self):
156
+ @view_config(route_name="convert_to_wmts", request_method="POST", renderer="fast_json") # type: ignore[misc]
157
+ def convert_to_wmts(self) -> JSONDict:
139
158
  src = self._get_object()
140
159
  dbsession = self._request.dbsession
141
160
  default_wmts = LayerWMTS.get_default(dbsession)
@@ -168,6 +187,8 @@ class LayerWmsViews(DimensionLayerViews):
168
187
  dbsession.flush()
169
188
  mark_changed(dbsession)
170
189
 
190
+ self._create_log(LogAction.CONVERT_TO_WMTS, src, element_url_table="layers_wmts")
191
+
171
192
  return {
172
193
  "success": True,
173
194
  "redirect": self._request.route_url(
@@ -1,6 +1,4 @@
1
- # -*- coding: utf-8 -*-
2
-
3
- # Copyright (c) 2017-2020, Camptocamp SA
1
+ # Copyright (c) 2017-2024, Camptocamp SA
4
2
  # All rights reserved.
5
3
 
6
4
  # Redistribution and use in source and binary forms, with or without
@@ -27,13 +25,21 @@
27
25
  # of the authors and should not be interpreted as representing official policies,
28
26
  # either expressed or implied, of the FreeBSD Project.
29
27
 
30
- # pylint: disable=no-member
31
-
32
28
 
33
29
  from functools import partial
34
30
 
31
+ import sqlalchemy
32
+ from c2cgeoform import JSONDict
35
33
  from c2cgeoform.schema import GeoFormSchemaNode
36
- from c2cgeoform.views.abstract_views import ItemAction, ListField
34
+ from c2cgeoform.views.abstract_views import (
35
+ DeleteResponse,
36
+ GridResponse,
37
+ IndexResponse,
38
+ ItemAction,
39
+ ListField,
40
+ ObjectResponse,
41
+ SaveResponse,
42
+ )
37
43
  from deform.widget import FormWidget
38
44
  from pyramid.view import view_config, view_defaults
39
45
  from sqlalchemy import delete, insert, inspect, update
@@ -42,27 +48,29 @@ from zope.sqlalchemy import mark_changed
42
48
  from c2cgeoportal_admin import _
43
49
  from c2cgeoportal_admin.schemas.dimensions import dimensions_schema_node
44
50
  from c2cgeoportal_admin.schemas.interfaces import interfaces_schema_node
45
- from c2cgeoportal_admin.schemas.metadata import metadatas_schema_node
51
+ from c2cgeoportal_admin.schemas.metadata import metadata_schema_node
46
52
  from c2cgeoportal_admin.schemas.restriction_areas import restrictionareas_schema_node
47
53
  from c2cgeoportal_admin.schemas.treeitem import parent_id_node
48
54
  from c2cgeoportal_admin.views.dimension_layers import DimensionLayerViews
49
- from c2cgeoportal_commons.models.main import LayerGroup, LayerWMS, LayerWMTS, OGCServer, TreeItem
55
+ from c2cgeoportal_commons.models.main import LayerGroup, LayerWMS, LayerWMTS, LogAction, OGCServer, TreeItem
50
56
 
51
57
  _list_field = partial(ListField, LayerWMTS)
52
58
 
53
59
  base_schema = GeoFormSchemaNode(LayerWMTS, widget=FormWidget(fields_template="layer_fields"))
54
- base_schema.add(dimensions_schema_node.clone())
55
- base_schema.add(metadatas_schema_node.clone())
56
- base_schema.add(interfaces_schema_node.clone())
57
- base_schema.add(restrictionareas_schema_node.clone())
60
+ base_schema.add(dimensions_schema_node(LayerWMTS.dimensions))
61
+ base_schema.add(metadata_schema_node(LayerWMTS.metadatas, LayerWMTS))
62
+ base_schema.add(interfaces_schema_node(LayerWMTS.interfaces))
63
+ base_schema.add(restrictionareas_schema_node(LayerWMTS.restrictionareas))
58
64
  base_schema.add_unique_validator(LayerWMTS.name, LayerWMTS.id)
59
65
  base_schema.add(parent_id_node(LayerGroup))
60
66
 
61
67
 
62
68
  @view_defaults(match_param="table=layers_wmts")
63
- class LayerWmtsViews(DimensionLayerViews):
69
+ class LayerWmtsViews(DimensionLayerViews[LayerWMTS]):
70
+ """The WMTS layer administration view."""
71
+
64
72
  _list_fields = (
65
- DimensionLayerViews._list_fields
73
+ DimensionLayerViews._list_fields # pylint: disable=protected-access
66
74
  + [
67
75
  _list_field("url"),
68
76
  _list_field("layer"),
@@ -70,26 +78,32 @@ class LayerWmtsViews(DimensionLayerViews):
70
78
  _list_field("matrix_set"),
71
79
  _list_field("image_type"),
72
80
  ]
73
- + DimensionLayerViews._extra_list_fields
81
+ + DimensionLayerViews._extra_list_fields # pylint: disable=protected-access
74
82
  )
75
83
  _id_field = "id"
76
84
  _model = LayerWMTS
77
85
  _base_schema = base_schema
78
86
 
79
- def _base_query(self, query=None):
80
- return super()._base_query(self._request.dbsession.query(LayerWMTS).distinct())
87
+ def _base_query(self) -> sqlalchemy.orm.query.Query[LayerWMTS]:
88
+ return super()._sub_query(self._request.dbsession.query(LayerWMTS).distinct())
89
+
90
+ def _sub_query(
91
+ self, query: sqlalchemy.orm.query.Query[LayerWMTS]
92
+ ) -> sqlalchemy.orm.query.Query[LayerWMTS]:
93
+ del query
94
+ return self._base_query()
81
95
 
82
- @view_config(route_name="c2cgeoform_index", renderer="../templates/index.jinja2")
83
- def index(self):
96
+ @view_config(route_name="c2cgeoform_index", renderer="../templates/index.jinja2") # type: ignore[misc]
97
+ def index(self) -> IndexResponse:
84
98
  return super().index()
85
99
 
86
- @view_config(route_name="c2cgeoform_grid", renderer="fast_json")
87
- def grid(self):
100
+ @view_config(route_name="c2cgeoform_grid", renderer="fast_json") # type: ignore[misc]
101
+ def grid(self) -> GridResponse:
88
102
  return super().grid()
89
103
 
90
- def _item_actions(self, item, readonly=False):
91
- actions = super()._item_actions(item, readonly)
92
- if inspect(item).persistent:
104
+ def _item_actions(self, item: LayerWMTS, readonly: bool = False) -> list[ItemAction]:
105
+ actions: list[ItemAction] = super()._item_actions(item, readonly)
106
+ if inspect(item).persistent: # type: ignore[attr-defined]
93
107
  actions.insert(
94
108
  next((i for i, v in enumerate(actions) if v.name() == "delete")),
95
109
  ItemAction(
@@ -103,8 +117,10 @@ class LayerWmtsViews(DimensionLayerViews):
103
117
  )
104
118
  return actions
105
119
 
106
- @view_config(route_name="c2cgeoform_item", request_method="GET", renderer="../templates/edit.jinja2")
107
- def view(self):
120
+ @view_config( # type: ignore[misc]
121
+ route_name="c2cgeoform_item", request_method="GET", renderer="../templates/edit.jinja2"
122
+ )
123
+ def view(self) -> ObjectResponse:
108
124
  if self._is_new():
109
125
  dbsession = self._request.dbsession
110
126
  default_wmts = LayerWMTS.get_default(dbsession)
@@ -112,22 +128,24 @@ class LayerWmtsViews(DimensionLayerViews):
112
128
  return self.copy(default_wmts, excludes=["name", "layer"])
113
129
  return super().edit()
114
130
 
115
- @view_config(route_name="c2cgeoform_item", request_method="POST", renderer="../templates/edit.jinja2")
116
- def save(self):
131
+ @view_config( # type: ignore[misc]
132
+ route_name="c2cgeoform_item", request_method="POST", renderer="../templates/edit.jinja2"
133
+ )
134
+ def save(self) -> SaveResponse:
117
135
  return super().save()
118
136
 
119
- @view_config(route_name="c2cgeoform_item", request_method="DELETE", renderer="fast_json")
120
- def delete(self):
137
+ @view_config(route_name="c2cgeoform_item", request_method="DELETE", renderer="fast_json") # type: ignore[misc]
138
+ def delete(self) -> DeleteResponse:
121
139
  return super().delete()
122
140
 
123
- @view_config(
141
+ @view_config( # type: ignore[misc]
124
142
  route_name="c2cgeoform_item_duplicate", request_method="GET", renderer="../templates/edit.jinja2"
125
143
  )
126
- def duplicate(self):
144
+ def duplicate(self) -> ObjectResponse:
127
145
  return super().duplicate()
128
146
 
129
- @view_config(route_name="convert_to_wms", request_method="POST", renderer="fast_json")
130
- def convert_to_wms(self):
147
+ @view_config(route_name="convert_to_wms", request_method="POST", renderer="fast_json") # type: ignore[misc]
148
+ def convert_to_wms(self) -> JSONDict:
131
149
  src = self._get_object()
132
150
  dbsession = self._request.dbsession
133
151
  default_wms = LayerWMS.get_default(dbsession)
@@ -161,6 +179,8 @@ class LayerWmtsViews(DimensionLayerViews):
161
179
  dbsession.flush()
162
180
  mark_changed(dbsession)
163
181
 
182
+ self._create_log(LogAction.CONVERT_TO_WMS, src, element_url_table="layers_wms")
183
+
164
184
  return {
165
185
  "success": True,
166
186
  "redirect": self._request.route_url(
@@ -1,6 +1,4 @@
1
- # -*- coding: utf-8 -*-
2
-
3
- # Copyright (c) 2017-2020, Camptocamp SA
1
+ # Copyright (c) 2017-2024, Camptocamp SA
4
2
  # All rights reserved.
5
3
 
6
4
  # Redistribution and use in source and binary forms, with or without
@@ -28,48 +26,59 @@
28
26
  # either expressed or implied, of the FreeBSD Project.
29
27
 
30
28
 
31
- from c2cgeoform.views.abstract_views import ItemAction
29
+ from typing import Any
30
+
31
+ import pyramid.request
32
+ from c2cgeoform.views.abstract_views import DeleteResponse, ItemAction
32
33
  from pyramid.httpexceptions import HTTPNotFound
33
34
  from pyramid.view import view_config, view_defaults
34
35
  from translationstring import TranslationStringFactory
35
36
 
36
37
  from c2cgeoportal_admin import _
37
- from c2cgeoportal_commons.models.main import LayergroupTreeitem, Theme, TreeItem
38
+ from c2cgeoportal_commons.models.main import Interface, Layer, LayergroupTreeitem, Theme, TreeItem
38
39
 
39
40
  itemtypes_tables = {
40
41
  "theme": "themes",
41
42
  "group": "layer_groups",
42
43
  "l_wms": "layers_wms",
43
44
  "l_wmts": "layers_wmts",
45
+ "l_cog": "layers_cog",
44
46
  }
45
47
 
46
48
 
47
- @view_defaults(match_param=("application=admin"))
49
+ @view_defaults(match_param="application=admin")
48
50
  class LayerTreeViews:
49
- def __init__(self, request):
51
+ """The layer tree administration view."""
52
+
53
+ def __init__(self, request: pyramid.request.Request):
50
54
  self._request = request
51
55
  self._dbsession = request.dbsession
52
56
 
53
- @view_config(route_name="layertree", renderer="../templates/layertree.jinja2")
54
- def index(self):
57
+ @view_config(route_name="layertree", renderer="../templates/layertree.jinja2") # type: ignore[misc]
58
+ def index(self) -> dict[str, int]:
55
59
  node_limit = self._request.registry.settings["admin_interface"].get("layer_tree_max_nodes")
56
60
  limit_exceeded = self._dbsession.query(LayergroupTreeitem).count() < node_limit
57
- return {"limit_exceeded": limit_exceeded}
61
+ return {"limit_exceeded": limit_exceeded, "interfaces": self._dbsession.query(Interface).all()}
58
62
 
59
- @view_config(route_name="layertree_children", renderer="fast_json")
60
- def children(self):
63
+ @view_config(route_name="layertree_children", renderer="fast_json") # type: ignore[misc]
64
+ def children(self) -> list[dict[str, Any]]:
65
+ interface = self._request.params.get("interface", None)
61
66
  group_id = self._request.params.get("group_id", None)
62
67
  path = self._request.params.get("path", "")
63
68
 
64
- client_tsf = TranslationStringFactory("{}-client".format(self._request.registry.package_name))
69
+ client_tsf = TranslationStringFactory(f"{self._request.registry.package_name}-client")
65
70
 
66
71
  if group_id is None:
67
72
  items = self._dbsession.query(Theme).order_by(Theme.ordering)
73
+ if interface is not None:
74
+ items = items.join(Theme.interfaces).filter(Interface.name == interface)
75
+
68
76
  else:
69
77
  items = (
70
78
  self._dbsession.query(TreeItem)
71
- .join(TreeItem.parents_relation) # pylint: disable=no-member
79
+ .join(TreeItem.parents_relation)
72
80
  .filter(LayergroupTreeitem.treegroup_id == group_id)
81
+ .order_by(LayergroupTreeitem.ordering)
73
82
  )
74
83
 
75
84
  return [
@@ -79,14 +88,17 @@ class LayerTreeViews:
79
88
  "name": item.name,
80
89
  "translated_name": self._request.localizer.translate(client_tsf(item.name)),
81
90
  "description": item.description,
82
- "path": "{}_{}".format(path, item.id),
91
+ "path": f"{path}_{item.id}",
83
92
  "parent_path": path,
84
93
  "actions": [action.to_dict(self._request) for action in self._item_actions(item, group_id)],
85
94
  }
86
95
  for item in items
96
+ if interface is None
97
+ or not isinstance(item, Layer)
98
+ or interface in [interface.name for interface in item.interfaces]
87
99
  ]
88
100
 
89
- def _item_actions(self, item, parent_id=None):
101
+ def _item_actions(self, item: TreeItem, parent_id: int | None = None) -> list[ItemAction]:
90
102
  actions = []
91
103
  actions.append(
92
104
  ItemAction(
@@ -105,9 +117,8 @@ class LayerTreeViews:
105
117
  name="new_layer_group",
106
118
  label=_("New layer group"),
107
119
  icon="glyphicon glyphicon-plus",
108
- url="{}?parent_id={}".format(
109
- self._request.route_url("c2cgeoform_item", table="layer_groups", id="new"), item.id
110
- ),
120
+ url=f"{self._request.route_url('c2cgeoform_item', table='layer_groups', id='new')}?"
121
+ f"parent_id={item.id}",
111
122
  )
112
123
  )
113
124
 
@@ -117,9 +128,8 @@ class LayerTreeViews:
117
128
  name="new_layer_wms",
118
129
  label=_("New WMS layer"),
119
130
  icon="glyphicon glyphicon-plus",
120
- url="{}?parent_id={}".format(
121
- self._request.route_url("c2cgeoform_item", table="layers_wms", id="new"), item.id
122
- ),
131
+ url=f"{self._request.route_url('c2cgeoform_item', table='layers_wms', id='new')}?"
132
+ f"parent_id={item.id}",
123
133
  )
124
134
  )
125
135
 
@@ -128,9 +138,8 @@ class LayerTreeViews:
128
138
  name="new_layer_wmts",
129
139
  label=_("New WMTS layer"),
130
140
  icon="glyphicon glyphicon-plus",
131
- url="{}?parent_id={}".format(
132
- self._request.route_url("c2cgeoform_item", table="layers_wmts", id="new"), item.id
133
- ),
141
+ url=f"{self._request.route_url('c2cgeoform_item', table='layers_wmts', id='new')}?"
142
+ f"parent_id={item.id}",
134
143
  )
135
144
  )
136
145
 
@@ -170,8 +179,8 @@ class LayerTreeViews:
170
179
 
171
180
  return actions
172
181
 
173
- @view_config(route_name="layertree_unlink", request_method="DELETE", renderer="fast_json")
174
- def unlink(self):
182
+ @view_config(route_name="layertree_unlink", request_method="DELETE", renderer="fast_json") # type: ignore[misc]
183
+ def unlink(self) -> dict[str, Any]:
175
184
  group_id = self._request.matchdict.get("group_id")
176
185
  item_id = self._request.matchdict.get("item_id")
177
186
  link = (
@@ -186,8 +195,8 @@ class LayerTreeViews:
186
195
  self._request.dbsession.flush()
187
196
  return {"success": True, "redirect": self._request.route_url("layertree")}
188
197
 
189
- @view_config(route_name="layertree_delete", request_method="DELETE", renderer="fast_json")
190
- def delete(self):
198
+ @view_config(route_name="layertree_delete", request_method="DELETE", renderer="fast_json") # type: ignore[misc]
199
+ def delete(self) -> DeleteResponse:
191
200
  item_id = self._request.matchdict.get("item_id")
192
201
  item = self._request.dbsession.query(TreeItem).get(item_id)
193
202
  if item is None: