c2cgeoportal-geoportal 2.7.1.156__py2.py3-none-any.whl → 2.8.1.180__py2.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 (97) hide show
  1. c2cgeoportal_geoportal/__init__.py +24 -14
  2. c2cgeoportal_geoportal/lib/authentication.py +10 -14
  3. c2cgeoportal_geoportal/lib/caching.py +8 -6
  4. c2cgeoportal_geoportal/lib/checker.py +10 -6
  5. c2cgeoportal_geoportal/lib/common_headers.py +5 -8
  6. c2cgeoportal_geoportal/lib/dbreflection.py +8 -8
  7. c2cgeoportal_geoportal/lib/filter_capabilities.py +5 -1
  8. c2cgeoportal_geoportal/lib/lingua_extractor.py +11 -12
  9. c2cgeoportal_geoportal/lib/loader.py +1 -1
  10. c2cgeoportal_geoportal/lib/oauth2.py +217 -100
  11. c2cgeoportal_geoportal/lib/wmstparsing.py +8 -12
  12. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/Dockerfile +9 -11
  13. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/development.ini +1 -1
  14. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/gunicorn.conf.py +0 -2
  15. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/requirements.txt +1 -1
  16. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/webpack.api.js +6 -4
  17. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/webpack.apps.js +1 -3
  18. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/webpack.commons.js +1 -0
  19. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/__init__.py +1 -6
  20. c2cgeoportal_geoportal/scaffolds/advance_update/{{cookiecutter.project}}/geoportal/CONST_Makefile +0 -20
  21. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.github/workflows/main.yaml +20 -6
  22. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.github/workflows/update_l10n.yaml +4 -3
  23. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/Dockerfile +22 -22
  24. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/Makefile +58 -2
  25. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/build +48 -24
  26. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/ci/config.yaml +2 -5
  27. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/ci/docker-compose-check +25 -0
  28. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/ci/requirements.txt +1 -1
  29. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/docker-compose-db.yaml +26 -0
  30. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/docker-compose-lib.yaml +53 -26
  31. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/docker-compose-qgis.yaml +23 -0
  32. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/docker-compose.override.sample.yaml +0 -1
  33. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/docker-compose.yaml +3 -3
  34. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/env.default +21 -2
  35. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/env.project +9 -0
  36. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/geoportal/vars.yaml +38 -14
  37. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/mapserver/data/Readme.txt +2 -2
  38. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/mapserver/mapserver.conf +15 -0
  39. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/mapserver/mapserver.map.tmpl +2 -3
  40. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}/A3_Landscape.jrxml +5 -0
  41. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}/A3_Portrait.jrxml +5 -0
  42. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}/A4_Landscape.jrxml +5 -0
  43. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}/A4_Portrait.jrxml +5 -0
  44. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}/config.yaml.tmpl +6 -0
  45. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/pyproject.toml +4 -0
  46. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/run_alembic.sh +3 -5
  47. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/scripts/db-backup +1 -1
  48. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/scripts/db-restore +1 -1
  49. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/spell-ignore-words.txt +2 -0
  50. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/tests/__init__.py +0 -0
  51. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/tests/test_app.py +38 -0
  52. c2cgeoportal_geoportal/scaffolds/update/{{cookiecutter.project}}/.upgrade.yaml +2 -132
  53. c2cgeoportal_geoportal/scaffolds/update/{{cookiecutter.project}}/CONST_CHANGELOG.txt +210 -1097
  54. c2cgeoportal_geoportal/scaffolds/update/{{cookiecutter.project}}/CONST_create_template/tests/test_testapp.py +48 -0
  55. c2cgeoportal_geoportal/scaffolds/update/{{cookiecutter.project}}/geoportal/CONST_config-schema.yaml +17 -15
  56. c2cgeoportal_geoportal/scaffolds/update/{{cookiecutter.project}}/geoportal/CONST_vars.yaml +46 -2
  57. c2cgeoportal_geoportal/scripts/c2cupgrade.py +1 -2
  58. c2cgeoportal_geoportal/scripts/pcreate.py +8 -10
  59. c2cgeoportal_geoportal/scripts/theme2fts.py +58 -3
  60. c2cgeoportal_geoportal/views/__init__.py +1 -3
  61. c2cgeoportal_geoportal/views/dynamic.py +1 -1
  62. c2cgeoportal_geoportal/views/entry.py +2 -10
  63. c2cgeoportal_geoportal/views/fulltextsearch.py +1 -1
  64. c2cgeoportal_geoportal/views/geometry_processing.py +3 -3
  65. c2cgeoportal_geoportal/views/layers.py +10 -11
  66. c2cgeoportal_geoportal/views/login.py +63 -8
  67. c2cgeoportal_geoportal/views/mapserverproxy.py +2 -3
  68. c2cgeoportal_geoportal/views/ogcproxy.py +6 -2
  69. c2cgeoportal_geoportal/views/pdfreport.py +4 -4
  70. c2cgeoportal_geoportal/views/printproxy.py +2 -2
  71. c2cgeoportal_geoportal/views/profile.py +1 -1
  72. c2cgeoportal_geoportal/views/proxy.py +2 -4
  73. c2cgeoportal_geoportal/views/raster.py +2 -2
  74. c2cgeoportal_geoportal/views/resourceproxy.py +1 -1
  75. c2cgeoportal_geoportal/views/shortener.py +1 -2
  76. c2cgeoportal_geoportal/views/theme.py +97 -63
  77. c2cgeoportal_geoportal/views/tinyowsproxy.py +3 -12
  78. c2cgeoportal_geoportal/views/vector_tiles.py +1 -1
  79. {c2cgeoportal_geoportal-2.7.1.156.dist-info → c2cgeoportal_geoportal-2.8.1.180.dist-info}/METADATA +21 -15
  80. {c2cgeoportal_geoportal-2.7.1.156.dist-info → c2cgeoportal_geoportal-2.8.1.180.dist-info}/RECORD +96 -90
  81. {c2cgeoportal_geoportal-2.7.1.156.dist-info → c2cgeoportal_geoportal-2.8.1.180.dist-info}/entry_points.txt +1 -0
  82. tests/__init__.py +3 -2
  83. tests/test_cachebuster.py +3 -3
  84. tests/test_caching.py +7 -7
  85. tests/test_checker.py +1 -1
  86. tests/test_decimaljson.py +1 -1
  87. tests/test_headerstween.py +1 -1
  88. tests/test_i18n.py +1 -1
  89. tests/test_init.py +14 -15
  90. tests/test_locale_negociator.py +4 -4
  91. tests/test_mapserverproxy_route_predicate.py +1 -2
  92. tests/test_raster.py +15 -15
  93. tests/test_wmstparsing.py +10 -10
  94. tests/xmlstr.py +1 -3
  95. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/tools/extract-messages.js +0 -41
  96. {c2cgeoportal_geoportal-2.7.1.156.dist-info → c2cgeoportal_geoportal-2.8.1.180.dist-info}/WHEEL +0 -0
  97. {c2cgeoportal_geoportal-2.7.1.156.dist-info → c2cgeoportal_geoportal-2.8.1.180.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,48 @@
1
+ # This file should be used only on the test project in the c2cgeoportal CI
2
+
3
+ import re
4
+
5
+ import polib
6
+ import pytest
7
+ import requests
8
+
9
+
10
+ @pytest.mark.parametrize("test_number", [0, 1])
11
+ def test_po(test_number: int) -> None:
12
+ """Tests that the generated pot files are identical between the command line and the view."""
13
+ del test_number
14
+
15
+ response = requests.get("https://front/locale.pot", verify=False, timeout=30) # nosec
16
+ assert response.status_code == 200, response.text
17
+ response_keys = {e.msgid for e in polib.pofile(response.text)}
18
+
19
+ with open(
20
+ "geoportal/{{cookiecutter.package}}_geoportal/locale/{{cookiecutter.package}}_geoportal-client.pot",
21
+ encoding="utf-8",
22
+ ) as current_file:
23
+ current_content = current_file.read()
24
+ current_content_keys = {e.msgid for e in polib.pofile(current_content)}
25
+
26
+ if response_keys != current_content_keys:
27
+ assert response.text == current_content
28
+
29
+
30
+ @pytest.mark.parametrize("url", ["https://front/desktop_alt"])
31
+ def test_desktop_alt(url: str) -> None:
32
+ """Tests the desktop alt page."""
33
+ response = requests.get(url, verify=False, timeout=30) # nosec
34
+ assert response.status_code == 200, response.text
35
+
36
+ assert re.search(
37
+ r'<script src="https?://front/static-ngeo-dist/desktop\..*\.js" crossorigin="anonymous"></script>',
38
+ response.text,
39
+ ), response.text
40
+ assert re.search(r'<html lang="{{"{{mainCtrl.lang}}"}}" ', response.text), response.text
41
+
42
+
43
+ def test_enum() -> None:
44
+ """Test the enumerations view"""
45
+ response = requests.get("https://front/layers/test/values/type", verify=False, timeout=30) # nosec
46
+ assert response.status_code == 200, response.text
47
+
48
+ assert response.json() == {"items": [{"value": "car"}, {"value": "train"}]}, response.text
@@ -44,6 +44,8 @@ mapping:
44
44
  package:
45
45
  type: str
46
46
  required: True
47
+ main_ogc_server:
48
+ type: str
47
49
  enable_admin_interface:
48
50
  type: scalar
49
51
  required: True
@@ -123,7 +125,7 @@ mapping:
123
125
  required: True
124
126
  type: map
125
127
  mapping:
126
- regex;.+:
128
+ regex;(.+):
127
129
  type: map
128
130
  mapping:
129
131
  extends:
@@ -131,17 +133,17 @@ mapping:
131
133
  constants:
132
134
  type: map
133
135
  mapping:
134
- regex;.+:
136
+ regex;(.+):
135
137
  type: any
136
138
  dynamic_constants:
137
139
  type: map
138
140
  mapping:
139
- regex;.+:
141
+ regex;(.+):
140
142
  type: str
141
143
  routes:
142
144
  type: map
143
145
  mapping:
144
- regex;.+:
146
+ regex;(.+):
145
147
  type: map
146
148
  mapping:
147
149
  name:
@@ -155,22 +157,22 @@ mapping:
155
157
  kw:
156
158
  type: map
157
159
  mapping:
158
- regex;.+:
160
+ regex;(.+):
159
161
  type: str
160
162
  params:
161
163
  type: map
162
164
  mapping:
163
- regex;.+:
165
+ regex;(.+):
164
166
  type: text
165
167
  dynamic_params:
166
168
  type: map
167
169
  mapping:
168
- regex;.+:
170
+ regex;(.+):
169
171
  type: str
170
172
  static:
171
173
  type: map
172
174
  mapping:
173
- regex;.+:
175
+ regex;(.+):
174
176
  type: map
175
177
  mapping:
176
178
  name:
@@ -227,7 +229,7 @@ mapping:
227
229
  type: map
228
230
  required: True
229
231
  mapping:
230
- regex;.+:
232
+ regex;(.+):
231
233
  type: str
232
234
  headers:
233
235
  type: map
@@ -253,7 +255,7 @@ mapping:
253
255
  headers:
254
256
  type: map
255
257
  mapping:
256
- regex;.+:
258
+ regex;(.+):
257
259
  type: str
258
260
  index: *header
259
261
  dynamic: *header
@@ -283,7 +285,7 @@ mapping:
283
285
  type: map
284
286
  required: True
285
287
  mapping:
286
- regex;.+:
288
+ regex;(.+):
287
289
  type: map
288
290
  mapping:
289
291
  backend:
@@ -292,7 +294,7 @@ mapping:
292
294
  arguments:
293
295
  type: map
294
296
  mapping:
295
- regex;.+:
297
+ regex;(.+):
296
298
  type: any
297
299
  admin_interface:
298
300
  type: map
@@ -308,7 +310,7 @@ mapping:
308
310
  sequence:
309
311
  - type: map
310
312
  mapping:
311
- regex;.+:
313
+ regex;(.+):
312
314
  type: any
313
315
  fitSource:
314
316
  type: bool
@@ -344,7 +346,7 @@ mapping:
344
346
  type: str
345
347
  zoom:
346
348
  type: int
347
- regex;.+:
349
+ regex;(.+):
348
350
  type: any
349
351
  available_metadata:
350
352
  type: seq
@@ -417,7 +419,7 @@ mapping:
417
419
  required: True
418
420
  map:
419
421
  <<: *map_config
420
- regex;.+:
422
+ regex;(.+):
421
423
  type: any
422
424
 
423
425
  layers:
@@ -51,6 +51,7 @@ vars:
51
51
  ns:
52
52
  - geomapfish
53
53
  defaultNS: geomapfish
54
+ keySeparator: false
54
55
  debug: false
55
56
  detection:
56
57
  order:
@@ -210,7 +211,7 @@ vars:
210
211
  gmfBackgroundLayerSelectorOptions: {}
211
212
  defaultTheme: Demo
212
213
  defaultLang: en
213
- gmfOptions:
214
+ gmfOptions: &gmfOptions
214
215
  map: {}
215
216
  view:
216
217
  srid: '{srid}'
@@ -219,6 +220,7 @@ vars:
219
220
  resolutions: [250, 100, 50, 20, 10, 5, 2, 1, 0.5, 0.25, 0.1, 0.05]
220
221
  extent: [2420000, 1030000, 2900000, 1350000]
221
222
  constrainRotation: false
223
+ constrainResolution: true
222
224
  gmfSearchOptions:
223
225
  styles:
224
226
  default:
@@ -297,6 +299,11 @@ vars:
297
299
  width: 2
298
300
  zoom:
299
301
  autoRotate: False
302
+ gmfWMSSourceOptions:
303
+ # The default value is 1.5 but it add a slight blur on the layer,
304
+ # 1 is also possible but the application will do a getMap on all the pan.
305
+ ratio: 2
306
+
300
307
  dynamic_constants:
301
308
  interface: interface
302
309
  cacheVersion: cache_version
@@ -343,6 +350,7 @@ vars:
343
350
  redirect_interface: mobile
344
351
  do_redirect: True
345
352
  constants:
353
+ gmfOptions: *gmfOptions
346
354
  ngeoProfileOptions: {}
347
355
  ngeoStreetviewOptions:
348
356
  viewer: 'mapillary'
@@ -363,7 +371,9 @@ vars:
363
371
  LAYERFONTSIZE: '10'
364
372
  ITEMFONTSIZE: '8'
365
373
  hiddenAttributes:
374
+ - debug
366
375
  - timezone
376
+ - username
367
377
  gmfDisplayQueryGridOptions:
368
378
  featuresStyle: *featureStyle
369
379
  selectedFeatureStyle: *selectedFeatureStyle
@@ -392,15 +402,20 @@ vars:
392
402
  radius: 3
393
403
  gmfShareOptions:
394
404
  enableEmail: True
405
+ gmfFitOptions:
406
+ # Padding (in pixels) to be correctly fitted inside the view. Values in the array are top, right, bottom and left padding.
407
+ padding: [50, 10, 50, 50]
395
408
  routes:
396
409
  gmfProfileJsonUrl:
397
410
  name: profile.json
398
411
  gmfPrintUrl:
399
412
  name: printproxy
413
+
400
414
  mobile:
401
415
  extends: default
402
416
  redirect_interface: desktop
403
417
  constants:
418
+ gmfOptions: *gmfOptions
404
419
  gmfMobileMeasureAreaOptions:
405
420
  precision: 2
406
421
  sketchStyle: &mobileMeasureStyle
@@ -440,10 +455,19 @@ vars:
440
455
  radius: 8
441
456
  radius2: 0
442
457
  angle: 0
458
+ gmfFitOptions:
459
+ # Padding (in pixels) to be correctly fitted inside the view. Values in the array are top, right, bottom and left padding.
460
+ padding: [60, 60, 42, 10]
461
+
443
462
  iframe_api:
444
463
  extends: default
445
464
  constants:
465
+ gmfOptions: *gmfOptions
446
466
  gmfSearchGroups: []
467
+ gmfFitOptions:
468
+ # Padding (in pixels) to be correctly fitted inside the view. Values in the array are top, right, bottom and left padding.
469
+ padding: [20, 20, 20, 20]
470
+
447
471
  api:
448
472
  constants:
449
473
  projections: *projections
@@ -469,15 +493,19 @@ vars:
469
493
  cache:
470
494
  std:
471
495
  backend: c2cgeoportal.hybridsentinel
472
- arguments:
496
+ arguments: &redis-cache-arguments
473
497
  lock_timeout: '{REDIS_LOCK_TIMEOUT}' # seconds
474
498
  redis_expiration_time: '{REDIS_EXPIRATION_TIME}' # seconds
475
499
  distributed_lock: True
500
+ thread_local_lock: False
476
501
  service_name: '{REDIS_SERVICENAME}'
477
502
  socket_timeout: '{REDIS_TIMEOUT}' # seconds
478
503
  db: 0
479
504
  obj:
480
505
  backend: dogpile.cache.memory
506
+ ogc-server:
507
+ backend: c2cgeoportal.hybridsentinel
508
+ arguments: *redis-cache-arguments
481
509
 
482
510
  admin_interface:
483
511
  layer_tree_max_nodes: 1000
@@ -493,6 +521,18 @@ vars:
493
521
  tree item will match when searching for the search aliases.
494
522
  relevant_for:
495
523
  - treeitem
524
+ - name: searchLabelPattern
525
+ description: >
526
+ Template string for the label of tree items in the search results, for example: "{name} ({theme})"
527
+ Supported parameters:
528
+ <ul>
529
+ <li>name (name of the tree item)</li>
530
+ <li>parent (parent of the item, may be a group, a block or a theme)</li>
531
+ <li>block (name of the block to which the item belongs)</li>
532
+ <li>theme (name of the theme to which the item belongs)</li>
533
+ </ul>
534
+ relevant_for:
535
+ - treeitem
496
536
  # Layers group
497
537
  - name: exclusiveGroup
498
538
  type: boolean
@@ -1313,6 +1353,10 @@ runtime_postprocess:
1313
1353
  - cache.std.arguments.redis_expiration_time
1314
1354
  - cache.std.arguments.socket_timeout
1315
1355
  - cache.std.arguments.db
1356
+ - cache.ogc-server.arguments.lock_timeout
1357
+ - cache.ogc-server.arguments.redis_expiration_time
1358
+ - cache.ogc-server.arguments.socket_timeout
1359
+ - cache.ogc-server.arguments.db
1316
1360
  - sqlalchemy\.pool_recycle
1317
1361
  - sqlalchemy\.pool_size
1318
1362
  - sqlalchemy\.max_overflow
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2014-2021, Camptocamp SA
1
+ # Copyright (c) 2014-2023, Camptocamp SA
2
2
  # All rights reserved.
3
3
 
4
4
  # Redistribution and use in source and binary forms, with or without
@@ -247,7 +247,6 @@ class C2cUpgradeTool:
247
247
  except ConnectionRefusedError as exception:
248
248
  return False, "\n".join([f"Connection refused: {exception}", run_curl])
249
249
  if resp.status_code < 200 or resp.status_code >= 300:
250
-
251
250
  print(colorize("=============", Color.RED))
252
251
  print(colorize("Checker error", Color.RED))
253
252
  try:
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2021-2024, Camptocamp SA
1
+ # Copyright (c) 2021-2023, Camptocamp SA
2
2
  # All rights reserved.
3
3
 
4
4
  # Redistribution and use in source and binary forms, with or without
@@ -183,9 +183,9 @@ class PCreateCommand:
183
183
  }
184
184
  context.update(self.read_project_file())
185
185
  if os.environ.get("CI") == "true":
186
- context["authtkt_secret"] = ( # nosec
187
- "io7heoDui8xaikie1rushaeGeiph8Bequei6ohchaequob6viejei0xooWeuvohf"
188
- )
186
+ context[ # nosec
187
+ "authtkt_secret"
188
+ ] = "io7heoDui8xaikie1rushaeGeiph8Bequei6ohchaequob6viejei0xooWeuvohf"
189
189
 
190
190
  self.get_var(context, "srid", "Spatial Reference System Identifier (e.g. 2056): ", int)
191
191
  srid = cast(int, context["srid"])
@@ -193,12 +193,10 @@ class PCreateCommand:
193
193
  self.get_var(
194
194
  context,
195
195
  "extent",
196
- (
197
- f"Extent (minx miny maxx maxy): in EPSG: {srid} projection, default is "
198
- f"[{extent[0]} {extent[1]} {extent[2]} {extent[3]}]: "
199
- if extent
200
- else f"Extent (minx miny maxx maxy): in EPSG: {srid} projection: "
201
- ),
196
+ f"Extent (minx miny maxx maxy): in EPSG: {srid} projection, default is "
197
+ f"[{extent[0]} {extent[1]} {extent[2]} {extent[3]}]: "
198
+ if extent
199
+ else f"Extent (minx miny maxx maxy): in EPSG: {srid} projection: ",
202
200
  )
203
201
  match = re.match(
204
202
  r"([\d.]+)[,; ] *([\d.]+)[,; ] *([\d.]+)[,; ] *([\d.]+)",
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2014-2021, Camptocamp SA
1
+ # Copyright (c) 2014-2023, 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 @@ import gettext
30
30
  import os
31
31
  import sys
32
32
  from argparse import ArgumentParser, Namespace
33
- from typing import TYPE_CHECKING, Any, Dict, List, Optional, Set
33
+ from typing import TYPE_CHECKING, Any, Dict, Iterator, List, Optional, Set
34
34
 
35
35
  import pyramid.config
36
36
  import transaction
@@ -190,7 +190,7 @@ class Import:
190
190
  self.imported.add(key)
191
191
  for lang in self.languages:
192
192
  fts = FullTextSearch()
193
- fts.label = self._[lang].gettext(item.name)
193
+ fts.label = self._render_label(item, lang)
194
194
  fts.role = role
195
195
  fts.interface = interface
196
196
  fts.lang = lang
@@ -289,3 +289,58 @@ class Import:
289
289
  self._add_fts(layer, interface, "add_layer", role)
290
290
 
291
291
  return fill
292
+
293
+ def _render_label(
294
+ self,
295
+ item: "c2cgeoportal_commons.models.main.TreeItem",
296
+ lang: str,
297
+ ) -> str:
298
+ patterns = item.get_metadata("searchLabelPattern")
299
+ if not patterns:
300
+ return self._[lang].gettext(item.name)
301
+ pattern = patterns[0]
302
+ assert isinstance(pattern.value, str)
303
+ tree_paths = list(self._get_paths(item))
304
+ # Remove paths where the last element isn't a theme
305
+ tree_paths = [p for p in tree_paths if p[-1].item_type == "theme"]
306
+ result = None
307
+ current_result = None
308
+ if tree_paths:
309
+ for path in tree_paths:
310
+ if len(path) == 2:
311
+ current_result = pattern.value.format(
312
+ name=self._[lang].gettext(item.name),
313
+ theme=self._[lang].gettext(path[-1].name),
314
+ parent=self._[lang].gettext(path[1].name),
315
+ )
316
+ elif len(path) > 2:
317
+ current_result = pattern.value.format(
318
+ name=self._[lang].gettext(item.name),
319
+ theme=self._[lang].gettext(path[-1].name),
320
+ parent=self._[lang].gettext(path[1].name),
321
+ block=self._[lang].gettext(path[-2].name),
322
+ )
323
+ if result and current_result != result:
324
+ sys.stderr.write(
325
+ f"WARNING: the item {item.name} (id: {item.id}) has a label pattern and inconsistent "
326
+ f"multiple parents\n"
327
+ )
328
+ return self._[lang].gettext(item.name)
329
+ result = current_result
330
+ return result or pattern.value.format(
331
+ name=self._[lang].gettext(item.name),
332
+ theme=self._[lang].gettext(item.name),
333
+ )
334
+
335
+ def _get_paths(
336
+ self,
337
+ item: "c2cgeoportal_commons.models.main.TreeItem",
338
+ ) -> Iterator[List["c2cgeoportal_commons.models.main.TreeItem"]]:
339
+ if item is None:
340
+ return
341
+ if any(item.parents):
342
+ for parent in item.parents:
343
+ for path in self._get_paths(parent):
344
+ yield [item, *path]
345
+ else:
346
+ yield [item]
@@ -1,6 +1,4 @@
1
- # -*- coding: utf-8 -*-
2
-
3
- # Copyright (c) 2011-2021, Camptocamp SA
1
+ # Copyright (c) 2011-2023, Camptocamp SA
4
2
  # All rights reserved.
5
3
 
6
4
  # Redistribution and use in source and binary forms, with or without
@@ -56,7 +56,7 @@ class DynamicView:
56
56
  return cast(Dict[str, Any], self.interfaces_config.get(interface, {}).get(value, {}))
57
57
 
58
58
  @CACHE_REGION.cache_on_arguments() # type: ignore
59
- def _fulltextsearch_groups(self) -> List[str]: # pylint: disable=no-self-use
59
+ def _fulltextsearch_groups(self) -> List[str]:
60
60
  return [
61
61
  group[0]
62
62
  for group in models.DBSession.query(func.distinct(main.FullTextSearch.layer_name))
@@ -28,7 +28,6 @@
28
28
 
29
29
  import glob
30
30
  import logging
31
- import os
32
31
  from typing import Any, Dict, List, Optional
33
32
 
34
33
  import pyramid.request
@@ -57,7 +56,7 @@ class Entry:
57
56
  def get_ngeo_index_vars(self) -> Dict[str, Any]:
58
57
  set_common_headers(self.request, "index", Cache.PUBLIC_NO, content_type="text/html")
59
58
  # Force urllogin to be converted to cookie when requesting the main HTML page
60
- self.request.user # noqa
59
+ self.request.user # pylint: disable=pointless-statement
61
60
  return {}
62
61
 
63
62
  @staticmethod
@@ -105,14 +104,7 @@ class Entry:
105
104
 
106
105
  def _get_ngeo_resources(pattern: str) -> List[str]:
107
106
  """Return the list of ngeo dist files matching the pattern."""
108
- results = glob.glob(f"/opt/c2cgeoportal/geoportal/node_modules/ngeo/dist/{pattern}")
109
- if not results:
110
- LOG.error(
111
- "No file found for pattern %s, in: [%s]",
112
- pattern,
113
- ", ".join(os.listdir("/opt/c2cgeoportal/geoportal/node_modules/ngeo/dist/")),
114
- )
115
- return results
107
+ return glob.glob(f"/opt/c2cgeoportal/geoportal/node_modules/ngeo/dist/{pattern}")
116
108
 
117
109
 
118
110
  def canvas_view(request: pyramid.request.Request, interface_config: Dict[str, Any]) -> Dict[str, Any]:
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2011-2021, Camptocamp SA
1
+ # Copyright (c) 2011-2023, Camptocamp SA
2
2
  # All rights reserved.
3
3
 
4
4
  # Redistribution and use in source and binary forms, with or without
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2011-2021, Camptocamp SA
1
+ # Copyright (c) 2011-2023, 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 @@ from geoalchemy2.shape import from_shape, to_shape
33
33
  from geojson import loads
34
34
  from pyramid.httpexceptions import HTTPBadRequest
35
35
  from pyramid.view import view_config
36
- from shapely.geometry import asShape
36
+ from shapely.geometry import shape
37
37
  from shapely.geometry.base import BaseGeometry
38
38
  from sqlalchemy import func
39
39
 
@@ -69,7 +69,7 @@ class GeometryProcessing:
69
69
  return to_shape(
70
70
  DBSession.query(
71
71
  func.ST_Difference(
72
- from_shape(asShape(body["geometries"][0])), from_shape(asShape(body["geometries"][1]))
72
+ from_shape(shape(body["geometries"][0])), from_shape(shape(body["geometries"][1]))
73
73
  )
74
74
  ).scalar()
75
75
  )
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2012-2022, Camptocamp SA
1
+ # Copyright (c) 2012-2023, Camptocamp SA
2
2
  # All rights reserved.
3
3
 
4
4
  # Redistribution and use in source and binary forms, with or without
@@ -34,6 +34,7 @@ from typing import TYPE_CHECKING, Any, Dict, Generator, List, Optional, Set, Tup
34
34
  import geojson.geometry
35
35
  import pyramid.request
36
36
  import pyramid.response
37
+ import shapely.geometry
37
38
  import sqlalchemy.ext.declarative
38
39
  from geoalchemy2 import Geometry
39
40
  from geoalchemy2 import func as ga_func
@@ -49,8 +50,7 @@ from pyramid.httpexceptions import (
49
50
  HTTPNotFound,
50
51
  )
51
52
  from pyramid.view import view_config
52
- from shapely.geometry import asShape
53
- from shapely.geos import TopologicalError
53
+ from shapely.errors import TopologicalError
54
54
  from shapely.ops import cascaded_union
55
55
  from sqlalchemy import Enum, Numeric, String, Text, Unicode, UnicodeText, exc, func
56
56
  from sqlalchemy.orm.exc import MultipleResultsFound, NoResultFound
@@ -190,7 +190,7 @@ class Layers:
190
190
 
191
191
  feature = proto.read(self.request, filter=filter_)
192
192
  if isinstance(feature, HTTPException):
193
- raise feature # pylint: disable=raising-non-exception
193
+ raise feature
194
194
  return feature
195
195
 
196
196
  @view_config(route_name="layers_read_many", renderer="geojson") # type: ignore
@@ -228,7 +228,7 @@ class Layers:
228
228
  geom = feature.geometry
229
229
  if not geom or isinstance(geom, geojson.geometry.Default):
230
230
  return feature
231
- shape = asShape(geom)
231
+ shape = shapely.geometry.shape(geom)
232
232
  srid = self._get_geom_col_info(layer)[1]
233
233
  spatial_elt = from_shape(shape, srid=srid)
234
234
  allowed = models.DBSession.query(func.count(RestrictionArea.id))
@@ -275,7 +275,7 @@ class Layers:
275
275
  del obj # unused
276
276
  geom = feature.geometry
277
277
  if geom and not isinstance(geom, geojson.geometry.Default):
278
- shape = asShape(geom)
278
+ shape = shapely.geometry.shape(geom)
279
279
  srid = self._get_geom_col_info(layer)[1]
280
280
  spatial_elt = from_shape(shape, srid=srid)
281
281
  allowed = models.DBSession.query(func.count(RestrictionArea.id))
@@ -298,7 +298,7 @@ class Layers:
298
298
  try:
299
299
  features = protocol.create(self.request)
300
300
  if isinstance(features, HTTPException):
301
- raise features # pylint: disable=raising-bad-type
301
+ raise features
302
302
  if features is not None:
303
303
  for feature in features.features: # pylint: disable=no-member
304
304
  self._log_last_update(layer, feature)
@@ -346,7 +346,7 @@ class Layers:
346
346
  )
347
347
  spatial_elt = None
348
348
  if geom and not isinstance(geom, geojson.geometry.Default):
349
- shape = asShape(geom)
349
+ shape = shapely.geometry.shape(geom)
350
350
  spatial_elt = from_shape(shape, srid=srid)
351
351
  allowed = allowed.filter(
352
352
  or_(RestrictionArea.area.is_(None), RestrictionArea.area.ST_Contains(spatial_elt))
@@ -439,7 +439,7 @@ class Layers:
439
439
  protocol = self._get_protocol_for_layer(layer, before_delete=security_cb)
440
440
  response = protocol.delete(self.request, feature_id)
441
441
  if isinstance(response, HTTPException):
442
- raise response # pylint: disable=raising-non-exception
442
+ raise response
443
443
  set_common_headers(self.request, "layers", Cache.PRIVATE_NO, response=response)
444
444
  return response
445
445
 
@@ -506,7 +506,7 @@ def get_layer_class(
506
506
  """
507
507
  Get the SQLAlchemy class to edit a GeoMapFish layer.
508
508
 
509
- Arguments:
509
+ Keyword Arguments:
510
510
 
511
511
  layer: The GeoMapFish layer
512
512
  with_last_update_columns: False to just have a class to access to the table and be able to
@@ -584,7 +584,6 @@ def get_layer_metadata(layer: "main.Layer") -> List[ColumnProperties]:
584
584
 
585
585
  for column_property in class_mapper(cls).iterate_properties:
586
586
  if isinstance(column_property, ColumnProperty):
587
-
588
587
  if len(column_property.columns) != 1:
589
588
  raise NotImplementedError
590
589