c2cgeoportal-admin 2.8.1.181__py3-none-any.whl → 2.9rc1__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.9rc1.dist-info}/METADATA +3 -12
  36. {c2cgeoportal_admin-2.8.1.181.dist-info → c2cgeoportal_admin-2.9rc1.dist-info}/RECORD +48 -46
  37. {c2cgeoportal_admin-2.8.1.181.dist-info → c2cgeoportal_admin-2.9rc1.dist-info}/WHEEL +1 -1
  38. c2cgeoportal_admin-2.9rc1.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.9rc1.dist-info}/top_level.txt +0 -0
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2017-2023, Camptocamp SA
1
+ # Copyright (c) 2017-2024, Camptocamp SA
2
2
  # All rights reserved.
3
3
 
4
4
  # Redistribution and use in source and binary forms, with or without
@@ -30,7 +30,7 @@ from typing import Any
30
30
 
31
31
  import c2cgeoform
32
32
  import c2cwsgiutils.pretty_json
33
- import sqlalchemy
33
+ import sqlalchemy.orm.session
34
34
  import zope.sqlalchemy
35
35
  from c2c.template.config import config as configuration
36
36
  from pkg_resources import resource_filename
@@ -52,8 +52,12 @@ _ = TranslationStringFactory("c2cgeoportal_admin")
52
52
 
53
53
  def main(_, **settings):
54
54
  """Return a Pyramid WSGI application."""
55
- configuration.init(settings.get("app.cfg"))
56
- settings.update(configuration.get_config())
55
+ app_cfg = settings.get("app.cfg")
56
+ assert app_cfg is not None
57
+ configuration.init(app_cfg)
58
+ conf = configuration.get_config()
59
+ assert conf is not None
60
+ settings.update(conf)
57
61
 
58
62
  config = Configurator(settings=settings)
59
63
 
@@ -70,9 +74,13 @@ def main(_, **settings):
70
74
  session_factory.configure(bind=engine)
71
75
 
72
76
  def get_tm_session(
73
- session_factory: sessionmaker, transaction_manager: TransactionManager
77
+ session_factory: sessionmaker[ # pylint: disable=unsubscriptable-object
78
+ sqlalchemy.orm.session.Session
79
+ ],
80
+ transaction_manager: TransactionManager,
74
81
  ) -> sqlalchemy.orm.session.Session:
75
82
  dbsession = session_factory()
83
+ assert isinstance(dbsession, sqlalchemy.orm.session.Session)
76
84
  zope.sqlalchemy.register(dbsession, transaction_manager=transaction_manager)
77
85
  return dbsession
78
86
 
@@ -95,7 +103,7 @@ def main(_, **settings):
95
103
  property=True,
96
104
  )
97
105
 
98
- config.add_route("ogc_server_clear_cache", "/ogc_server_clear_cache/{id}")
106
+ config.add_route("ogc_server_clear_cache", "/admin/ogc_server_clear_cache/{id}")
99
107
 
100
108
  config.add_subscriber(add_renderer_globals, BeforeRender)
101
109
  config.add_subscriber(add_localizer, NewRequest)
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2011-2022, Camptocamp SA
1
+ # Copyright (c) 2011-2024, Camptocamp SA
2
2
  # All rights reserved.
3
3
 
4
4
  # Redistribution and use in source and binary forms, with or without
@@ -27,10 +27,10 @@
27
27
 
28
28
 
29
29
  import os
30
- from typing import Any, Dict, List, Optional
30
+ from typing import Any
31
31
 
32
32
  import yaml
33
- from lingua.extractors import Extractor, Message
33
+ from lingva.extractors import Extractor, Message
34
34
 
35
35
 
36
36
  class GeomapfishConfigExtractor(Extractor): # type: ignore
@@ -41,10 +41,10 @@ class GeomapfishConfigExtractor(Extractor): # type: ignore
41
41
  def __call__(
42
42
  self,
43
43
  filename: str,
44
- options: Dict[str, Any],
45
- fileobj: Optional[Dict[str, Any]] = None,
44
+ options: dict[str, Any],
45
+ fileobj: dict[str, Any] | None = None,
46
46
  lineno: int = 0,
47
- ) -> List[Message]:
47
+ ) -> list[Message]:
48
48
  del options, fileobj, lineno
49
49
 
50
50
  print(f"Running {self.__class__.__name__} on {filename}")
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2020-2023, Camptocamp SA
1
+ # Copyright (c) 2020-2024, Camptocamp SA
2
2
  # All rights reserved.
3
3
 
4
4
  # Redistribution and use in source and binary forms, with or without
@@ -29,7 +29,7 @@
29
29
  import functools
30
30
  import logging
31
31
  from io import StringIO
32
- from typing import Any, Optional, Set, cast
32
+ from typing import Any, Optional, cast
33
33
  from xml.etree.ElementTree import Element # nosec
34
34
 
35
35
  import pyramid.request
@@ -202,7 +202,7 @@ class OGCServerSynchronizer:
202
202
  self._logger.info("%s layers added", self._layers_added)
203
203
  self._logger.info("%s layers removed", self._layers_removed)
204
204
 
205
- def synchronize_layer(self, el: Element, parent: Optional[main.TreeGroup] = None) -> main.TreeItem:
205
+ def synchronize_layer(self, el: Element, parent: main.TreeGroup | None = None) -> main.TreeItem:
206
206
  if el.find("Layer") is None:
207
207
  tree_item = self.get_layer_wms(el, parent)
208
208
  elif parent is None:
@@ -287,10 +287,11 @@ class OGCServerSynchronizer:
287
287
 
288
288
  return group
289
289
 
290
- def get_layer_wms(self, el: Element, parent: Optional[main.TreeGroup] = None) -> main.LayerWMS:
290
+ def get_layer_wms(self, el: Element, parent: main.TreeGroup | None = None) -> main.LayerWMS:
291
291
  name_el = el.find("Name")
292
292
  assert name_el is not None
293
293
  name = name_el.text
294
+ assert name is not None
294
295
 
295
296
  layer = cast(
296
297
  Optional[main.LayerWMS],
@@ -359,7 +360,7 @@ class OGCServerSynchronizer:
359
360
 
360
361
  @functools.lru_cache(maxsize=10)
361
362
  def wms_capabilities(self) -> bytes:
362
- errors: Set[str] = set()
363
+ errors: set[str] = set()
363
364
  url = get_url2(
364
365
  f"The OGC server '{self._ogc_server.name}'",
365
366
  self._ogc_server.url,
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2017-2023, Camptocamp SA
1
+ # Copyright (c) 2017-2024, Camptocamp SA
2
2
  # All rights reserved.
3
3
 
4
4
  # Redistribution and use in source and binary forms, with or without
@@ -57,6 +57,7 @@ def includeme(config):
57
57
  from c2cgeoportal_commons.models.main import ( # pylint: disable=import-outside-toplevel
58
58
  Functionality,
59
59
  Interface,
60
+ LayerCOG,
60
61
  LayerGroup,
61
62
  LayerVectorTiles,
62
63
  LayerWMS,
@@ -72,19 +73,27 @@ def includeme(config):
72
73
  User,
73
74
  )
74
75
 
76
+ authentication_configuration = config.registry.settings.get("authentication", {})
77
+ oidc_configuration = authentication_configuration.get("openid_connect", {})
78
+ oidc_enabled = oidc_configuration.get("enabled", False)
79
+ oidc_provide_roles = oidc_configuration.get("provide_roles", False)
80
+ oauth2_configuration = authentication_configuration.get("oauth2", {})
81
+ oauth2_enabled = oauth2_configuration.get("enabled", not oidc_enabled)
82
+
75
83
  visible_routes = [
76
84
  ("themes", Theme),
77
85
  ("layer_groups", LayerGroup),
78
86
  ("layers_wms", LayerWMS),
79
87
  ("layers_wmts", LayerWMTS),
80
88
  ("layers_vectortiles", LayerVectorTiles),
89
+ ("layers_cog", LayerCOG),
81
90
  ("ogc_servers", OGCServer),
82
91
  ("restriction_areas", RestrictionArea),
83
- ("users", User),
92
+ *([("users", User)] if not oidc_enabled or not oidc_provide_roles else []),
84
93
  ("roles", Role),
85
94
  ("functionalities", Functionality),
86
95
  ("interfaces", Interface),
87
- ("oauth2_clients", OAuth2Client),
96
+ *([("oauth2_clients", OAuth2Client)] if oauth2_enabled else []),
88
97
  ("logs", Log),
89
98
  ]
90
99
 
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2018-2021, Camptocamp SA
1
+ # Copyright (c) 2018-2024, Camptocamp SA
2
2
  # All rights reserved.
3
3
 
4
4
  # Redistribution and use in source and binary forms, with or without
@@ -26,6 +26,8 @@
26
26
  # either expressed or implied, of the FreeBSD Project.
27
27
 
28
28
 
29
+ from typing import Any
30
+
29
31
  import colander
30
32
  from c2cgeoform.schema import GeoFormSchemaNode
31
33
  from deform.widget import MappingWidget, SequenceWidget
@@ -34,7 +36,7 @@ from sqlalchemy.orm.attributes import InstrumentedAttribute
34
36
  from c2cgeoportal_commons.models.main import Dimension
35
37
 
36
38
 
37
- def dimensions_schema_node(prop: InstrumentedAttribute) -> colander.SequenceSchema:
39
+ def dimensions_schema_node(prop: InstrumentedAttribute[Any]) -> colander.SequenceSchema:
38
40
  """Get the scheme of the dimensions."""
39
41
  return colander.SequenceSchema(
40
42
  GeoFormSchemaNode(Dimension, name="dimension", widget=MappingWidget(template="dimension")),
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2018-2021, Camptocamp SA
1
+ # Copyright (c) 2018-2024, Camptocamp SA
2
2
  # All rights reserved.
3
3
 
4
4
  # Redistribution and use in source and binary forms, with or without
@@ -26,20 +26,19 @@
26
26
  # either expressed or implied, of the FreeBSD Project.
27
27
 
28
28
 
29
- from typing import Any, Dict, List
29
+ from typing import Any
30
30
 
31
31
  import colander
32
32
  from c2cgeoform.ext.deform_ext import RelationCheckBoxListWidget
33
33
  from c2cgeoform.schema import GeoFormManyToManySchemaNode, manytomany_validator
34
34
  from sqlalchemy import inspect, select
35
35
  from sqlalchemy.orm.attributes import InstrumentedAttribute
36
- from sqlalchemy.orm.decl_api import DeclarativeMeta
37
36
  from sqlalchemy.sql.functions import concat
38
37
 
39
38
  from c2cgeoportal_commons.models.main import Functionality
40
39
 
41
40
 
42
- def available_functionalities_for(settings: Dict[str, Any], model: DeclarativeMeta) -> List[Dict[str, Any]]:
41
+ def available_functionalities_for(settings: dict[str, Any], model: type[Any]) -> list[dict[str, Any]]:
43
42
  """Return filtered list of functionality definitions."""
44
43
  mapper = inspect(model)
45
44
  relevant_for = {mapper.local_table.name}
@@ -50,18 +49,16 @@ def available_functionalities_for(settings: Dict[str, Any], model: DeclarativeMe
50
49
  ]
51
50
 
52
51
 
53
- def functionalities_widget(model: DeclarativeMeta) -> colander.deferred:
52
+ def functionalities_widget(model: type[Any]) -> colander.deferred:
54
53
  """Return a colander deferred which itself returns a widget for the functionalities field."""
55
54
 
56
55
  def create_widget(node, kw):
57
56
  del node
58
57
 
59
58
  return RelationCheckBoxListWidget(
60
- select(
61
- [
62
- Functionality.id,
63
- concat(Functionality.name, "=", Functionality.value).label("label"),
64
- ]
59
+ select( # type: ignore[arg-type]
60
+ Functionality.id,
61
+ concat(Functionality.name, "=", Functionality.value).label("label"),
65
62
  )
66
63
  .where(
67
64
  Functionality.name.in_(
@@ -81,12 +78,12 @@ def functionalities_widget(model: DeclarativeMeta) -> colander.deferred:
81
78
 
82
79
 
83
80
  def functionalities_schema_node(
84
- prop: InstrumentedAttribute, model: DeclarativeMeta
81
+ prop: InstrumentedAttribute[Any], model: type[Any]
85
82
  ) -> colander.SequenceSchema:
86
83
  """Get the schema of the functionalities."""
87
84
 
88
85
  return colander.SequenceSchema(
89
- GeoFormManyToManySchemaNode(Functionality),
86
+ GeoFormManyToManySchemaNode(Functionality, None),
90
87
  name=prop.key,
91
88
  title=prop.info["colanderalchemy"]["title"],
92
89
  description=prop.info["colanderalchemy"].get("description"),
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2018-2021, Camptocamp SA
1
+ # Copyright (c) 2018-2024, Camptocamp SA
2
2
  # All rights reserved.
3
3
 
4
4
  # Redistribution and use in source and binary forms, with or without
@@ -26,6 +26,8 @@
26
26
  # either expressed or implied, of the FreeBSD Project.
27
27
 
28
28
 
29
+ from typing import Any
30
+
29
31
  import colander
30
32
  from c2cgeoform.ext.deform_ext import RelationCheckBoxListWidget
31
33
  from c2cgeoform.schema import GeoFormManyToManySchemaNode, manytomany_validator
@@ -34,10 +36,12 @@ from sqlalchemy.orm.attributes import InstrumentedAttribute
34
36
  from c2cgeoportal_commons.models.main import Interface
35
37
 
36
38
 
37
- def interfaces_schema_node(prop: InstrumentedAttribute) -> colander.SequenceSchema:
39
+ def interfaces_schema_node(
40
+ prop: InstrumentedAttribute[Any], # pylint: disable=unsubscriptable-object
41
+ ) -> colander.SequenceSchema:
38
42
  """Get the serializable representation of an interface."""
39
43
  return colander.SequenceSchema(
40
- GeoFormManyToManySchemaNode(Interface),
44
+ GeoFormManyToManySchemaNode(Interface, None),
41
45
  name=prop.key,
42
46
  title=prop.info["colanderalchemy"]["title"],
43
47
  description=prop.info["colanderalchemy"]["description"],
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2018-2023, Camptocamp SA
1
+ # Copyright (c) 2018-2024, Camptocamp SA
2
2
  # All rights reserved.
3
3
 
4
4
  # Redistribution and use in source and binary forms, with or without
@@ -26,7 +26,7 @@
26
26
  # either expressed or implied, of the FreeBSD Project.
27
27
 
28
28
  import json
29
- from typing import Any, Dict, List, Optional, Set, Union, cast
29
+ from typing import Any, cast
30
30
 
31
31
  import colander
32
32
  import pyramid.request
@@ -34,7 +34,6 @@ from c2cgeoform.schema import GeoFormSchemaNode
34
34
  from deform.widget import MappingWidget, SelectWidget, SequenceWidget, TextAreaWidget
35
35
  from sqlalchemy import inspect
36
36
  from sqlalchemy.orm.attributes import InstrumentedAttribute
37
- from sqlalchemy.orm.decl_api import DeclarativeMeta
38
37
  from sqlalchemy.orm.mapper import Mapper
39
38
 
40
39
  from c2cgeoportal_admin import _
@@ -42,16 +41,17 @@ from c2cgeoportal_commons.lib.validators import url
42
41
  from c2cgeoportal_commons.models.main import Metadata
43
42
 
44
43
 
45
- def get_relevant_for(model: Union[DeclarativeMeta, Mapper]) -> Set[str]:
44
+ def get_relevant_for(model: type[Any] | Mapper[Any]) -> set[str]:
46
45
  """Return list of relevant_for values for passed class."""
47
46
  mapper = inspect(model)
47
+ assert mapper is not None
48
48
  relevant_for = {mapper.local_table.name} # or mapper.polymorphic_identity
49
49
  if mapper.inherits:
50
50
  relevant_for |= get_relevant_for(mapper.inherits)
51
51
  return relevant_for
52
52
 
53
53
 
54
- def metadata_definitions(settings: Dict[str, Any], model: DeclarativeMeta) -> List[Dict[str, Any]]:
54
+ def metadata_definitions(settings: dict[str, Any], model: type[Any]) -> list[dict[str, Any]]:
55
55
  """Return filtered list metadata definitions."""
56
56
  return [
57
57
  m
@@ -76,7 +76,7 @@ class MetadataSelectWidget(SelectWidget): # type: ignore
76
76
  return super().serialize(field, cstruct, **kw)
77
77
 
78
78
 
79
- def metadata_name_widget(model: DeclarativeMeta) -> colander.deferred:
79
+ def metadata_name_widget(model: type[Any]) -> colander.deferred:
80
80
  """Return a colander deferred which itself returns a widget for the metadata name field."""
81
81
 
82
82
  def create_widget(node, kw):
@@ -136,15 +136,15 @@ class BooleanMetadata(colander.Boolean): # type: ignore
136
136
  return None
137
137
 
138
138
 
139
- class MetadataSchemaNode(GeoFormSchemaNode): # type: ignore # pylint: disable=abstract-method
139
+ class MetadataSchemaNode(GeoFormSchemaNode): # pylint: disable=abstract-method
140
140
  """The metadata schema."""
141
141
 
142
- metadata_definitions: Optional[Dict[str, Any]] = None
142
+ metadata_definitions: dict[str, Any] | None = None
143
143
 
144
144
  def __init__(self, *args: Any, **kw: Any):
145
145
  super().__init__(*args, **kw)
146
146
 
147
- self.available_types: List[str] = []
147
+ self.available_types: list[str] = []
148
148
 
149
149
  self._add_value_node("string", colander.String())
150
150
  self._add_value_node("liste", colander.String())
@@ -184,7 +184,7 @@ class MetadataSchemaNode(GeoFormSchemaNode): # type: ignore # pylint: disable=a
184
184
 
185
185
  def _ui_type(self, metadata_name: str) -> str:
186
186
  metadata_type = (
187
- cast(Dict[str, Dict[str, str]], self.metadata_definitions)
187
+ cast(dict[str, dict[str, str]], self.metadata_definitions)
188
188
  .get(metadata_name, {})
189
189
  .get("type", "string")
190
190
  )
@@ -192,15 +192,15 @@ class MetadataSchemaNode(GeoFormSchemaNode): # type: ignore # pylint: disable=a
192
192
 
193
193
 
194
194
  def _translate_available_metadata(
195
- available_metadata: Dict[str, Any], request: pyramid.request.Request
196
- ) -> Dict[str, Any]:
195
+ available_metadata: dict[str, Any], request: pyramid.request.Request
196
+ ) -> dict[str, Any]:
197
197
  result = {}
198
198
  result.update(available_metadata)
199
199
  result["description"] = request.localizer.translate(_(available_metadata.get("description", "").strip()))
200
200
  return result
201
201
 
202
202
 
203
- def metadata_schema_node(prop: InstrumentedAttribute, model: DeclarativeMeta) -> colander.SequenceSchema:
203
+ def metadata_schema_node(prop: InstrumentedAttribute[Any], model: type[Any]) -> colander.SequenceSchema:
204
204
  """Get the schema of a collection of metadata."""
205
205
 
206
206
  # Deferred which returns a dictionary with metadata name as key and metadata definition as value.
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2017-2021, Camptocamp SA
1
+ # Copyright (c) 2017-2024, Camptocamp SA
2
2
  # All rights reserved.
3
3
 
4
4
  # Redistribution and use in source and binary forms, with or without
@@ -26,6 +26,8 @@
26
26
  # either expressed or implied, of the FreeBSD Project.
27
27
 
28
28
 
29
+ from typing import Any
30
+
29
31
  import colander
30
32
  from c2cgeoform.ext.deform_ext import RelationCheckBoxListWidget
31
33
  from c2cgeoform.schema import GeoFormManyToManySchemaNode, manytomany_validator
@@ -34,10 +36,10 @@ from sqlalchemy.orm.attributes import InstrumentedAttribute
34
36
  from c2cgeoportal_commons.models.main import RestrictionArea
35
37
 
36
38
 
37
- def restrictionareas_schema_node(prop: InstrumentedAttribute) -> colander.SequenceSchema:
39
+ def restrictionareas_schema_node(prop: InstrumentedAttribute[Any]) -> colander.SequenceSchema:
38
40
  """Get the schema of a restriction area."""
39
41
  return colander.SequenceSchema(
40
- GeoFormManyToManySchemaNode(RestrictionArea),
42
+ GeoFormManyToManySchemaNode(RestrictionArea, None),
41
43
  name=prop.key,
42
44
  title=prop.info["colanderalchemy"]["title"],
43
45
  description=prop.info["colanderalchemy"].get("description"),
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2018-2021, Camptocamp SA
1
+ # Copyright (c) 2018-2024, Camptocamp SA
2
2
  # All rights reserved.
3
3
 
4
4
  # Redistribution and use in source and binary forms, with or without
@@ -26,6 +26,8 @@
26
26
  # either expressed or implied, of the FreeBSD Project.
27
27
 
28
28
 
29
+ from typing import Any
30
+
29
31
  import colander
30
32
  from c2cgeoform.ext.deform_ext import RelationCheckBoxListWidget
31
33
  from c2cgeoform.schema import GeoFormManyToManySchemaNode, manytomany_validator
@@ -34,10 +36,12 @@ from sqlalchemy.orm.attributes import InstrumentedAttribute
34
36
  from c2cgeoportal_commons.models.main import Role
35
37
 
36
38
 
37
- def roles_schema_node(prop: InstrumentedAttribute) -> colander.SequenceSchema:
39
+ def roles_schema_node(
40
+ prop: InstrumentedAttribute[Any], # pylint: disable=unsubscriptable-object
41
+ ) -> colander.SequenceSchema:
38
42
  """Get the schema of all the items."""
39
43
  return colander.SequenceSchema(
40
- GeoFormManyToManySchemaNode(Role),
44
+ GeoFormManyToManySchemaNode(Role, None),
41
45
  name=prop.key,
42
46
  title=prop.info["colanderalchemy"]["title"],
43
47
  description=prop.info["colanderalchemy"].get("description"),
@@ -28,10 +28,11 @@
28
28
 
29
29
  import logging
30
30
  from functools import partial
31
- from typing import Any, Dict, List, Optional
31
+ from typing import Any
32
32
 
33
33
  import colander
34
34
  import pyramid.request
35
+ import sqlalchemy
35
36
  from c2cgeoform.schema import GeoFormSchemaNode
36
37
  from sqlalchemy.orm import aliased
37
38
  from sqlalchemy.sql.expression import case, func
@@ -41,7 +42,7 @@ from c2cgeoportal_admin.widgets import ChildrenWidget, ChildWidget
41
42
  from c2cgeoportal_commons.lib.literal import Literal
42
43
  from c2cgeoportal_commons.models.main import LayergroupTreeitem, TreeGroup, TreeItem
43
44
 
44
- LOG = logging.getLogger(__name__)
45
+ _LOG = logging.getLogger(__name__)
45
46
 
46
47
  # Correspondence between TreeItem.item_type and route table segment
47
48
  ITEM_TYPE_ROUTE_MAP = {
@@ -53,7 +54,7 @@ ITEM_TYPE_ROUTE_MAP = {
53
54
  }
54
55
 
55
56
 
56
- class ChildSchemaNode(GeoFormSchemaNode): # type: ignore # pylint: disable=abstract-method
57
+ class ChildSchemaNode(GeoFormSchemaNode): # pylint: disable=abstract-method
57
58
  """Schema of the child nodes."""
58
59
 
59
60
  def objectify(self, dict_, context=None):
@@ -65,18 +66,21 @@ class ChildSchemaNode(GeoFormSchemaNode): # type: ignore # pylint: disable=abst
65
66
 
66
67
 
67
68
  def treeitems(
68
- node: TreeGroup, kw: Dict[str, pyramid.request.Request], only_groups: bool = False
69
- ) -> List[Dict[str, Any]]:
69
+ node: TreeGroup, kw: dict[str, pyramid.request.Request], only_groups: bool = False
70
+ ) -> list[dict[str, Any]]:
70
71
  """Get a serializable representation of the tree items."""
71
72
  del node
72
73
  dbsession = kw["request"].dbsession
74
+ assert isinstance(dbsession, sqlalchemy.orm.Session)
73
75
 
74
- group = case([(func.count(LayergroupTreeitem.id) == 0, "Unlinked")], else_="Others")
76
+ group = case(
77
+ (func.count(LayergroupTreeitem.id) == 0, "Unlinked"), else_="Others" # pylint: disable=not-callable
78
+ )
75
79
 
76
80
  query = (
77
81
  dbsession.query(TreeItem, group)
78
82
  .distinct()
79
- .outerjoin("parents_relation")
83
+ .outerjoin(TreeItem.parents_relation)
80
84
  .filter(TreeItem.item_type != "theme")
81
85
  .group_by(TreeItem)
82
86
  .order_by(group.desc(), TreeItem.name)
@@ -93,7 +97,7 @@ def treeitems(
93
97
  search_alias = aliased(search_ancestors, name="search_ancestors")
94
98
  relation_alias = aliased(LayergroupTreeitem, name="relation")
95
99
  search_ancestors = search_ancestors.union_all(
96
- dbsession.query(relation_alias.treegroup_id).filter(
100
+ dbsession.query(relation_alias.treegroup_id).filter( # type: ignore[arg-type]
97
101
  relation_alias.treeitem_id == search_alias.c.treegroup_id
98
102
  )
99
103
  )
@@ -139,13 +143,13 @@ def base_deferred_parent_id_validator(node, kw, model):
139
143
  return validator
140
144
 
141
145
 
142
- def treeitem_edit_url(request: pyramid.request.Request, treeitem: TreeGroup) -> Optional[str]:
146
+ def treeitem_edit_url(request: pyramid.request.Request, treeitem: TreeGroup) -> str | None:
143
147
  """Get the tree item editing URL."""
144
148
  if treeitem.item_type is None:
145
149
  return None
146
150
  table = ITEM_TYPE_ROUTE_MAP.get(treeitem.item_type, None)
147
151
  if table is None:
148
- LOG.warning("%s not found in ITEM_TYPE_ROUTE_MAP", treeitem.item_type)
152
+ _LOG.warning("%s not found in ITEM_TYPE_ROUTE_MAP", treeitem.item_type)
149
153
  return None
150
154
  return request.route_url( # type: ignore
151
155
  "c2cgeoform_item",
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2018-2021, Camptocamp SA
1
+ # Copyright (c) 2018-2024, Camptocamp SA
2
2
  # All rights reserved.
3
3
 
4
4
  # Redistribution and use in source and binary forms, with or without
@@ -35,7 +35,7 @@ from c2cgeoportal_admin.schemas.treegroup import base_deferred_parent_id_validat
35
35
 
36
36
 
37
37
  # Used for the creation of a new layer/layergroup from the layertree
38
- def parent_id_node(model):
38
+ def parent_id_node(model: type) -> colander.SchemaNode:
39
39
  """Get the scheme to the parent node ID."""
40
40
  return colander.SchemaNode(
41
41
  colander.Integer(),
@@ -45,6 +45,9 @@
45
45
  .icon-l_wmts:before {
46
46
  content: '\e011';
47
47
  }
48
+ .icon-l_cog:before {
49
+ content: '\e011';
50
+ }
48
51
  .icon-mvt:before {
49
52
  content: '\e011';
50
53
  }
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2017-2021, Camptocamp SA
1
+ # Copyright (c) 2017-2024, Camptocamp SA
2
2
  # All rights reserved.
3
3
 
4
4
  # Redistribution and use in source and binary forms, with or without
@@ -28,9 +28,9 @@
28
28
 
29
29
  from functools import partial
30
30
  from itertools import groupby
31
- from typing import cast
31
+ from typing import Generic, TypeVar, cast
32
32
 
33
- import sqlalchemy
33
+ import sqlalchemy.orm.query
34
34
  from c2cgeoform.views.abstract_views import ListField
35
35
  from sqlalchemy.orm import subqueryload
36
36
 
@@ -39,8 +39,10 @@ from c2cgeoportal_commons.models.main import DimensionLayer
39
39
 
40
40
  _list_field = partial(ListField, DimensionLayer)
41
41
 
42
+ _T = TypeVar("_T", bound=DimensionLayer)
42
43
 
43
- class DimensionLayerViews(LayerViews):
44
+
45
+ class DimensionLayerViews(LayerViews[_T], Generic[_T]):
44
46
  """The layer with dimensions administration view."""
45
47
 
46
48
  _extra_list_fields = [
@@ -53,7 +55,9 @@ class DimensionLayerViews(LayerViews):
53
55
  ]
54
56
  ),
55
57
  )
56
- ] + LayerViews._extra_list_fields
58
+ ] + LayerViews._extra_list_fields # pylint: disable=protected-access
57
59
 
58
- def _base_query(self, query: sqlalchemy.orm.query.Query) -> sqlalchemy.orm.query.Query:
59
- return super()._base_query(query.options(subqueryload("dimensions")))
60
+ def _sub_query(
61
+ self, query: sqlalchemy.orm.query.Query[DimensionLayer]
62
+ ) -> sqlalchemy.orm.query.Query[DimensionLayer]:
63
+ return super()._sub_query(query.options(subqueryload(DimensionLayer.dimensions)))