c2cgeoportal-geoportal 2.6.0__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 (225) hide show
  1. c2cgeoportal_geoportal/__init__.py +245 -95
  2. c2cgeoportal_geoportal/lib/__init__.py +67 -43
  3. c2cgeoportal_geoportal/lib/authentication.py +50 -26
  4. c2cgeoportal_geoportal/lib/bashcolor.py +17 -13
  5. c2cgeoportal_geoportal/lib/cacheversion.py +16 -8
  6. c2cgeoportal_geoportal/lib/caching.py +65 -193
  7. c2cgeoportal_geoportal/lib/check_collector.py +17 -10
  8. c2cgeoportal_geoportal/lib/checker.py +67 -65
  9. c2cgeoportal_geoportal/lib/common_headers.py +167 -0
  10. c2cgeoportal_geoportal/lib/dbreflection.py +61 -46
  11. c2cgeoportal_geoportal/lib/filter_capabilities.py +126 -88
  12. c2cgeoportal_geoportal/lib/fulltextsearch.py +6 -5
  13. c2cgeoportal_geoportal/lib/functionality.py +20 -17
  14. c2cgeoportal_geoportal/lib/headers.py +14 -5
  15. c2cgeoportal_geoportal/lib/i18n.py +4 -4
  16. c2cgeoportal_geoportal/lib/layers.py +30 -11
  17. c2cgeoportal_geoportal/lib/lingua_extractor.py +363 -240
  18. c2cgeoportal_geoportal/lib/loader.py +11 -16
  19. c2cgeoportal_geoportal/lib/metrics.py +28 -17
  20. c2cgeoportal_geoportal/lib/oauth2.py +392 -206
  21. c2cgeoportal_geoportal/lib/wmstparsing.py +105 -84
  22. c2cgeoportal_geoportal/lib/xsd.py +26 -16
  23. c2cgeoportal_geoportal/resources.py +15 -9
  24. c2cgeoportal_geoportal/scaffolds/advance_create/ci/config.yaml +26 -0
  25. c2cgeoportal_geoportal/scaffolds/advance_create/cookiecutter.json +18 -0
  26. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/.dockerignore +6 -0
  27. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/.eslintrc.yaml +19 -0
  28. c2cgeoportal_geoportal/scaffolds/{create/geoportal/+dot+prospector.yaml → advance_create/{{cookiecutter.project}}/geoportal/.prospector.yaml} +8 -2
  29. c2cgeoportal_geoportal/scaffolds/{create/geoportal/Dockerfile_tmpl → advance_create/{{cookiecutter.project}}/geoportal/Dockerfile} +22 -15
  30. c2cgeoportal_geoportal/scaffolds/{create/geoportal/alembic.yaml_tmpl → advance_create/{{cookiecutter.project}}/geoportal/alembic.yaml} +1 -1
  31. c2cgeoportal_geoportal/scaffolds/{create/geoportal/development.ini_tmpl → advance_create/{{cookiecutter.project}}/geoportal/development.ini} +34 -15
  32. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/gunicorn.conf.py +100 -0
  33. c2cgeoportal_geoportal/scaffolds/{create → advance_create/{{cookiecutter.project}}}/geoportal/lingua-client.cfg +1 -0
  34. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/production.ini +38 -0
  35. c2cgeoportal_geoportal/scaffolds/{create/geoportal/setup.py_tmpl → advance_create/{{cookiecutter.project}}/geoportal/setup.py} +6 -7
  36. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/tsconfig.json +8 -0
  37. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/webpack.api.js +77 -0
  38. c2cgeoportal_geoportal/scaffolds/{create/geoportal/webpack.apps.js_tmpl → advance_create/{{cookiecutter.project}}/geoportal/webpack.apps.js} +29 -28
  39. c2cgeoportal_geoportal/scaffolds/{create → advance_create/{{cookiecutter.project}}}/geoportal/webpack.commons.js +4 -7
  40. c2cgeoportal_geoportal/scaffolds/{create → advance_create/{{cookiecutter.project}}}/geoportal/webpack.config.js +1 -1
  41. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/__init__.py +42 -0
  42. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/authentication.py +10 -0
  43. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/dev.py +14 -0
  44. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/models.py +8 -0
  45. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/multi_organization.py +7 -0
  46. c2cgeoportal_geoportal/scaffolds/{create/geoportal/+package+_geoportal → advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/resources.py +4 -3
  47. c2cgeoportal_geoportal/scaffolds/{create/geoportal/+package+_geoportal/static-ngeo/api/index.js_tmpl → advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static-ngeo/api/index.js} +1 -2
  48. c2cgeoportal_geoportal/scaffolds/{create/geoportal/+package+_geoportal/static-ngeo/js/+package+module.js_tmpl → advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static-ngeo/js/{{cookiecutter.package}}module.js} +4 -4
  49. c2cgeoportal_geoportal/scaffolds/{create/geoportal/+package+_geoportal/subscribers.py_tmpl → advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/subscribers.py} +1 -3
  50. c2cgeoportal_geoportal/scaffolds/advance_update/cookiecutter.json +18 -0
  51. c2cgeoportal_geoportal/scaffolds/{update/geoportal/CONST_Makefile_tmpl → advance_update/{{cookiecutter.project}}/geoportal/CONST_Makefile} +3 -27
  52. c2cgeoportal_geoportal/scaffolds/create/cookiecutter.json +18 -0
  53. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.dockerignore +14 -0
  54. c2cgeoportal_geoportal/scaffolds/create/{+dot+editorconfig → {{cookiecutter.project}}/.editorconfig} +2 -5
  55. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.github/workflows/main.yaml +57 -0
  56. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.github/workflows/rebuild.yaml +46 -0
  57. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.github/workflows/update_l10n.yaml +66 -0
  58. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.gitignore +16 -0
  59. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.prettierignore +1 -0
  60. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.prettierrc.yaml +2 -0
  61. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/Dockerfile +76 -0
  62. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/Makefile +70 -0
  63. c2cgeoportal_geoportal/scaffolds/create/{README.rst_tmpl → {{cookiecutter.project}}/README.rst} +4 -4
  64. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/build +186 -0
  65. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/ci/config.yaml +22 -0
  66. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/ci/docker-compose-check +25 -0
  67. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/ci/requirements.txt +1 -0
  68. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/docker-compose-db.yaml +26 -0
  69. c2cgeoportal_geoportal/scaffolds/create/{docker-compose-lib.yaml → {{cookiecutter.project}}/docker-compose-lib.yaml} +165 -22
  70. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/docker-compose-qgis.yaml +23 -0
  71. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/docker-compose.override.sample.yaml +66 -0
  72. c2cgeoportal_geoportal/scaffolds/create/{docker-compose.yaml → {{cookiecutter.project}}/docker-compose.yaml} +20 -15
  73. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/env.default +101 -0
  74. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/env.project +69 -0
  75. c2cgeoportal_geoportal/scaffolds/create/{geoportal/vars.yaml_tmpl → {{cookiecutter.project}}/geoportal/vars.yaml} +126 -36
  76. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static/css/mobile.css +0 -0
  77. c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/data/Readme.txt +3 -3
  78. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/mapserver/demo.map.tmpl +224 -0
  79. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/mapserver/mapserver.conf +15 -0
  80. c2cgeoportal_geoportal/scaffolds/create/{mapserver/mapserver.map.tmpl_tmpl → {{cookiecutter.project}}/mapserver/mapserver.map.tmpl} +9 -18
  81. c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/A3_Landscape.jrxml +13 -8
  82. c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/A3_Portrait.jrxml +13 -8
  83. c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/A4_Landscape.jrxml +13 -8
  84. c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/A4_Portrait.jrxml +13 -8
  85. c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/config.yaml.tmpl +11 -4
  86. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}/localisation.properties +4 -0
  87. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}/localisation_fr.properties +4 -0
  88. c2cgeoportal_geoportal/scaffolds/create/{project.yaml_tmpl → {{cookiecutter.project}}/project.yaml} +6 -6
  89. c2cgeoportal_geoportal/scaffolds/create/{pyproject.toml → {{cookiecutter.project}}/pyproject.toml} +4 -0
  90. c2cgeoportal_geoportal/scaffolds/create/{qgisserver/pg_service.conf.tmpl_tmpl → {{cookiecutter.project}}/qgisserver/pg_service.conf.tmpl} +2 -2
  91. c2cgeoportal_geoportal/scaffolds/create/{run_alembic.sh → {{cookiecutter.project}}/run_alembic.sh} +3 -5
  92. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/scripts/db-backup +110 -0
  93. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/scripts/db-restore +114 -0
  94. c2cgeoportal_geoportal/scaffolds/create/{setup.cfg_tmpl → {{cookiecutter.project}}/setup.cfg} +1 -1
  95. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/spell-ignore-words.txt +5 -0
  96. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/tests/__init__.py +0 -0
  97. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/tests/test_app.py +38 -0
  98. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/tilegeneration/config.yaml.tmpl +195 -0
  99. c2cgeoportal_geoportal/scaffolds/update/cookiecutter.json +18 -0
  100. c2cgeoportal_geoportal/scaffolds/update/{{cookiecutter.project}}/.upgrade.yaml +61 -0
  101. c2cgeoportal_geoportal/scaffolds/update/{{cookiecutter.project}}/CONST_CHANGELOG.txt +273 -0
  102. c2cgeoportal_geoportal/scaffolds/update/{{cookiecutter.project}}/CONST_create_template/tests/test_testapp.py +48 -0
  103. c2cgeoportal_geoportal/scaffolds/update/{geoportal → {{cookiecutter.project}}/geoportal}/CONST_config-schema.yaml +64 -17
  104. c2cgeoportal_geoportal/scaffolds/update/{geoportal/CONST_vars.yaml_tmpl → {{cookiecutter.project}}/geoportal/CONST_vars.yaml} +396 -19
  105. c2cgeoportal_geoportal/scripts/__init__.py +16 -30
  106. c2cgeoportal_geoportal/scripts/c2cupgrade.py +272 -234
  107. c2cgeoportal_geoportal/scripts/create_demo_theme.py +3 -6
  108. c2cgeoportal_geoportal/scripts/manage_users.py +34 -39
  109. c2cgeoportal_geoportal/scripts/pcreate.py +310 -0
  110. c2cgeoportal_geoportal/scripts/theme2fts.py +128 -24
  111. c2cgeoportal_geoportal/scripts/urllogin.py +19 -11
  112. c2cgeoportal_geoportal/templates/login.html +88 -84
  113. c2cgeoportal_geoportal/templates/notlogin.html +59 -59
  114. c2cgeoportal_geoportal/templates/testi18n.html +6 -8
  115. c2cgeoportal_geoportal/views/__init__.py +23 -6
  116. c2cgeoportal_geoportal/views/dev.py +9 -7
  117. c2cgeoportal_geoportal/views/dynamic.py +56 -19
  118. c2cgeoportal_geoportal/views/entry.py +85 -24
  119. c2cgeoportal_geoportal/views/fulltextsearch.py +29 -23
  120. c2cgeoportal_geoportal/views/geometry_processing.py +17 -9
  121. c2cgeoportal_geoportal/views/i18n.py +91 -9
  122. c2cgeoportal_geoportal/views/layers.py +166 -133
  123. c2cgeoportal_geoportal/views/login.py +161 -93
  124. c2cgeoportal_geoportal/views/mapserverproxy.py +47 -31
  125. c2cgeoportal_geoportal/views/memory.py +12 -12
  126. c2cgeoportal_geoportal/views/ogcproxy.py +52 -30
  127. c2cgeoportal_geoportal/views/pdfreport.py +30 -26
  128. c2cgeoportal_geoportal/views/printproxy.py +60 -52
  129. c2cgeoportal_geoportal/views/profile.py +24 -23
  130. c2cgeoportal_geoportal/views/proxy.py +88 -72
  131. c2cgeoportal_geoportal/views/raster.py +37 -26
  132. c2cgeoportal_geoportal/views/resourceproxy.py +13 -11
  133. c2cgeoportal_geoportal/views/shortener.py +27 -25
  134. c2cgeoportal_geoportal/views/theme.py +472 -332
  135. c2cgeoportal_geoportal/views/tinyowsproxy.py +42 -44
  136. c2cgeoportal_geoportal/views/vector_tiles.py +80 -0
  137. {c2cgeoportal_geoportal-2.6.0.dist-info → c2cgeoportal_geoportal-2.8.1.180.dist-info}/METADATA +19 -8
  138. c2cgeoportal_geoportal-2.8.1.180.dist-info/RECORD +191 -0
  139. {c2cgeoportal_geoportal-2.6.0.dist-info → c2cgeoportal_geoportal-2.8.1.180.dist-info}/WHEEL +1 -1
  140. {c2cgeoportal_geoportal-2.6.0.dist-info → c2cgeoportal_geoportal-2.8.1.180.dist-info}/entry_points.txt +3 -0
  141. tests/__init__.py +10 -5
  142. tests/test_cachebuster.py +3 -5
  143. tests/test_caching.py +18 -26
  144. tests/test_checker.py +1 -3
  145. tests/test_decimaljson.py +5 -5
  146. tests/test_headerstween.py +1 -3
  147. tests/test_i18n.py +2 -2
  148. tests/test_init.py +16 -20
  149. tests/test_locale_negociator.py +4 -6
  150. tests/test_mapserverproxy_route_predicate.py +1 -4
  151. tests/test_raster.py +15 -17
  152. tests/test_wmstparsing.py +10 -12
  153. tests/xmlstr.py +1 -3
  154. c2cgeoportal_geoportal/scaffolds/__init__.py +0 -227
  155. c2cgeoportal_geoportal/scaffolds/create/+dot+dockerignore_tmpl +0 -12
  156. c2cgeoportal_geoportal/scaffolds/create/+dot+github/workflows/main.yaml_tmpl +0 -89
  157. c2cgeoportal_geoportal/scaffolds/create/+dot+github/workflows/rebuild.yaml_tmpl +0 -78
  158. c2cgeoportal_geoportal/scaffolds/create/+dot+gitignore_tmpl +0 -16
  159. c2cgeoportal_geoportal/scaffolds/create/Dockerfile_tmpl +0 -67
  160. c2cgeoportal_geoportal/scaffolds/create/Makefile +0 -3
  161. c2cgeoportal_geoportal/scaffolds/create/build_tmpl +0 -167
  162. c2cgeoportal_geoportal/scaffolds/create/ci/config.yaml_tmpl +0 -23
  163. c2cgeoportal_geoportal/scaffolds/create/ci/requirements.txt +0 -1
  164. c2cgeoportal_geoportal/scaffolds/create/ci/trigger +0 -68
  165. c2cgeoportal_geoportal/scaffolds/create/docker-compose.override.sample.yaml +0 -54
  166. c2cgeoportal_geoportal/scaffolds/create/env.default_tmpl +0 -67
  167. c2cgeoportal_geoportal/scaffolds/create/env.project_tmpl +0 -48
  168. c2cgeoportal_geoportal/scaffolds/create/geoportal/+dot+dockerignore_tmpl +0 -6
  169. c2cgeoportal_geoportal/scaffolds/create/geoportal/+dot+eslintrc_tmpl +0 -15
  170. c2cgeoportal_geoportal/scaffolds/create/geoportal/+package+_geoportal/__init__.py_tmpl +0 -58
  171. c2cgeoportal_geoportal/scaffolds/create/geoportal/+package+_geoportal/models.py_tmpl +0 -10
  172. c2cgeoportal_geoportal/scaffolds/create/geoportal/+package+_geoportal/static/robot.txt +0 -3
  173. c2cgeoportal_geoportal/scaffolds/create/geoportal/production.ini_tmpl +0 -106
  174. c2cgeoportal_geoportal/scaffolds/create/geoportal/tools/extract-messages.js +0 -39
  175. c2cgeoportal_geoportal/scaffolds/create/geoportal/tsconfig.json_tmpl +0 -9
  176. c2cgeoportal_geoportal/scaffolds/create/geoportal/webpack.api.js_tmpl +0 -72
  177. c2cgeoportal_geoportal/scaffolds/create/mapserver/demo.map.tmpl_tmpl +0 -262
  178. c2cgeoportal_geoportal/scaffolds/create/mapserver/tinyows.xml +0 -36
  179. c2cgeoportal_geoportal/scaffolds/create/print/print-apps/+package+/config.yaml +0 -168
  180. c2cgeoportal_geoportal/scaffolds/create/qgisserver/geomapfish.yaml.tmpl_tmpl +0 -16
  181. c2cgeoportal_geoportal/scaffolds/create/spell-ignore-words.txt +0 -1
  182. c2cgeoportal_geoportal/scaffolds/create/tilegeneration/config.yaml.tmpl_tmpl +0 -185
  183. c2cgeoportal_geoportal/scaffolds/create/yamllint.yaml +0 -11
  184. c2cgeoportal_geoportal/scaffolds/update/+dot+upgrade.yaml_tmpl +0 -181
  185. c2cgeoportal_geoportal/scaffolds/update/CONST_CHANGELOG.txt_tmpl +0 -454
  186. c2cgeoportal_geoportal/templates/dynamic.js +0 -21
  187. c2cgeoportal_geoportal-2.6.0.dist-info/RECORD +0 -173
  188. /c2cgeoportal_geoportal/{scaffolds/create/geoportal/+package+_geoportal/static/css/desktop.css → py.typed} +0 -0
  189. /c2cgeoportal_geoportal/scaffolds/{create/geoportal/Makefile_tmpl → advance_create/{{cookiecutter.project}}/geoportal/Makefile} +0 -0
  190. /c2cgeoportal_geoportal/scaffolds/{create → advance_create/{{cookiecutter.project}}}/geoportal/alembic.ini +0 -0
  191. /c2cgeoportal_geoportal/scaffolds/{create → advance_create/{{cookiecutter.project}}}/geoportal/language_mapping +0 -0
  192. /c2cgeoportal_geoportal/scaffolds/{create → advance_create/{{cookiecutter.project}}}/geoportal/lingua-server.cfg +0 -0
  193. /c2cgeoportal_geoportal/scaffolds/{create → advance_create/{{cookiecutter.project}}}/geoportal/requirements.txt +0 -0
  194. /c2cgeoportal_geoportal/scaffolds/{create/geoportal/+package+_geoportal → advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/views/__init__.py +0 -0
  195. /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal/locale/en/LC_MESSAGES/+package+_geoportal-client.po → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/locale/en/LC_MESSAGES/{{cookiecutter.package}}_geoportal-client.po} +0 -0
  196. /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal/static/css/iframe_api.css → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static/css/desktop.css} +0 -0
  197. /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal/static/css/mobile.css → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static/css/iframe_api.css} +0 -0
  198. /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/static/images/banner_left.png +0 -0
  199. /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/static/images/banner_right.png +0 -0
  200. /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/static/images/blank.png +0 -0
  201. /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/static/images/markers/marker-blue.png +0 -0
  202. /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/static/images/markers/marker-gold.png +0 -0
  203. /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/static/images/markers/marker-green.png +0 -0
  204. /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/static/images/markers/marker.png +0 -0
  205. /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/static/robot.txt.tmpl +0 -0
  206. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/data/TM_EUROPE_BORDERS-0.3.sql +0 -0
  207. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Arial.ttf +0 -0
  208. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Arialbd.ttf +0 -0
  209. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Arialbi.ttf +0 -0
  210. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Ariali.ttf +0 -0
  211. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/NotoSans-Bold.ttf +0 -0
  212. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/NotoSans-BoldItalic.ttf +0 -0
  213. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/NotoSans-Italic.ttf +0 -0
  214. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/NotoSans-Regular.ttf +0 -0
  215. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Verdana.ttf +0 -0
  216. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Verdanab.ttf +0 -0
  217. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Verdanai.ttf +0 -0
  218. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Verdanaz.ttf +0 -0
  219. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts.conf +0 -0
  220. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/tinyows.xml.tmpl +0 -0
  221. /c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/legend.jrxml +0 -0
  222. /c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/logo.png +0 -0
  223. /c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/north.svg +0 -0
  224. /c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/results.jrxml +0 -0
  225. {c2cgeoportal_geoportal-2.6.0.dist-info → c2cgeoportal_geoportal-2.8.1.180.dist-info}/top_level.txt +0 -0
@@ -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
@@ -26,15 +24,18 @@
26
24
  # The views and conclusions contained in the software and documentation are those
27
25
  # of the authors and should not be interpreted as representing official policies,
28
26
  # either expressed or implied, of the FreeBSD Project.
27
+
28
+ import random
29
29
  import threading
30
30
  import warnings
31
- from typing import Dict, Tuple # noqa, pylint: disable=unused-import
31
+ from typing import Dict, Iterable, List, Optional, Tuple, Union
32
32
 
33
+ import sqlalchemy.ext.declarative
33
34
  from papyrus.geo_interface import GeoInterface
34
35
  from sqlalchemy import Column, Integer, MetaData, Table
35
36
  from sqlalchemy.exc import SAWarning
36
- from sqlalchemy.ext.declarative.api import DeclarativeMeta # noqa, pylint: disable=unused-import
37
37
  from sqlalchemy.orm import relationship
38
+ from sqlalchemy.orm.session import Session
38
39
  from sqlalchemy.orm.util import class_mapper
39
40
 
40
41
  from c2cgeoportal_geoportal.lib.caching import get_region
@@ -53,13 +54,17 @@ SQL_GEOMETRY_COLUMNS = """
53
54
  class _AssociationProxy:
54
55
  # A specific "association proxy" implementation
55
56
 
56
- def __init__(self, target, value_attr, nullable=True, order_by=None):
57
+ def __init__(self, target: str, value_attr: str, nullable: bool = True, order_by: Optional[str] = None):
57
58
  self.target = target
58
59
  self.value_attr = value_attr
59
60
  self.nullable = nullable
60
61
  self.order_by = order_by
61
62
 
62
- def __get__(self, obj, type_=None):
63
+ def __get__(
64
+ self,
65
+ obj: sqlalchemy.ext.declarative.ConcreteBase,
66
+ type_: sqlalchemy.ext.declarative.DeclarativeMeta = None,
67
+ ) -> Optional[Union["_AssociationProxy", str]]:
63
68
  if obj is None:
64
69
  # For "hybrid" descriptors that work both at the instance
65
70
  # and class levels we could return an SQL expression here.
@@ -69,7 +74,7 @@ class _AssociationProxy:
69
74
  target = getattr(obj, self.target)
70
75
  return getattr(target, self.value_attr) if target else None
71
76
 
72
- def __set__(self, obj, val):
77
+ def __set__(self, obj: str, val: str) -> None:
73
78
  from c2cgeoportal_commons.models import DBSession # pylint: disable=import-outside-toplevel
74
79
 
75
80
  o = getattr(obj, self.target)
@@ -83,7 +88,7 @@ class _AssociationProxy:
83
88
  setattr(obj, self.target, o)
84
89
 
85
90
 
86
- def _get_schema(tablename):
91
+ def _get_schema(tablename: str) -> Tuple[str, str]:
87
92
  if "." in tablename:
88
93
  schema, tablename = tablename.split(".", 1)
89
94
  else:
@@ -95,7 +100,13 @@ def _get_schema(tablename):
95
100
  _get_table_lock = threading.RLock()
96
101
 
97
102
 
98
- def get_table(tablename, schema=None, session=None, primary_key=None):
103
+ def get_table(
104
+ tablename: str,
105
+ schema: Optional[str] = None,
106
+ session: Optional[Session] = None,
107
+ primary_key: Optional[str] = None,
108
+ ) -> Table:
109
+ """Build an SQLAlchemy table."""
99
110
  if schema is None:
100
111
  tablename, schema = _get_schema(tablename)
101
112
 
@@ -103,8 +114,8 @@ def get_table(tablename, schema=None, session=None, primary_key=None):
103
114
  engine = session.bind.engine
104
115
  metadata = MetaData(bind=engine)
105
116
  else:
117
+ from c2cgeoportal_commons.models import Base # pylint: disable=import-outside-toplevel
106
118
  from c2cgeoportal_commons.models import DBSession # pylint: disable=import-outside-toplevel
107
- from c2cgeoportal_commons.models.main import Base # pylint: disable=import-outside-toplevel
108
119
 
109
120
  engine = DBSession.bind.engine
110
121
  metadata = Base.metadata
@@ -121,21 +132,21 @@ def get_table(tablename, schema=None, session=None, primary_key=None):
121
132
  return table
122
133
 
123
134
 
124
- @CACHE_REGION_OBJ.cache_on_arguments()
135
+ @CACHE_REGION_OBJ.cache_on_arguments() # type: ignore
125
136
  def get_class(
126
- tablename,
127
- exclude_properties=None,
128
- primary_key=None,
129
- attributes_order=None,
130
- enumerations_config=None,
131
- readonly_attributes=None,
132
- ):
137
+ tablename: str,
138
+ exclude_properties: Optional[List[str]] = None,
139
+ primary_key: Optional[str] = None,
140
+ attributes_order: Optional[List[str]] = None,
141
+ enumerations_config: Optional[Dict[str, str]] = None,
142
+ readonly_attributes: Optional[List[str]] = None,
143
+ ) -> sqlalchemy.ext.declarative.DeclarativeMeta:
133
144
  """
134
- Get the SQLAlchemy mapped class for "tablename". If no class exists
135
- for "tablename" one is created, and added to the cache. "tablename"
136
- must reference a valid string. If there is no table identified by
137
- tablename in the database a NoSuchTableError SQLAlchemy exception
138
- is raised.
145
+ Get the SQLAlchemy mapped class for "tablename".
146
+
147
+ If no class exists for "tablename" one is created, and added to the cache. "tablename" must reference a
148
+ valid string. If there is no table identified by tablename in the database a NoSuchTableError SQLAlchemy
149
+ exception is raised.
139
150
  """
140
151
 
141
152
  tablename, schema = _get_schema(tablename)
@@ -155,25 +166,29 @@ def get_class(
155
166
 
156
167
 
157
168
  def _create_class(
158
- table,
159
- exclude_properties=None,
160
- attributes_order=None,
161
- enumerations_config=None,
162
- readonly_attributes=None,
163
- pk_name=None,
164
- ):
165
- from c2cgeoportal_commons.models.main import Base # pylint: disable=import-outside-toplevel
169
+ table: Table,
170
+ exclude_properties: Optional[Iterable[str]] = None,
171
+ attributes_order: Optional[List[str]] = None,
172
+ enumerations_config: Optional[Dict[str, str]] = None,
173
+ readonly_attributes: Optional[List[str]] = None,
174
+ pk_name: Optional[str] = None,
175
+ ) -> sqlalchemy.ext.declarative.DeclarativeMeta:
176
+ from c2cgeoportal_commons.models import Base # pylint: disable=import-outside-toplevel
166
177
 
167
178
  exclude_properties = exclude_properties or ()
168
- attributes = dict(
169
- __table__=table,
170
- __mapper_args__={"exclude_properties": exclude_properties},
171
- __attributes_order__=attributes_order,
172
- __enumerations_config__=enumerations_config,
173
- )
179
+ attributes = {
180
+ "__table__": table,
181
+ "__mapper_args__": {"exclude_properties": exclude_properties},
182
+ "__attributes_order__": attributes_order,
183
+ "__enumerations_config__": enumerations_config,
184
+ }
174
185
  if pk_name is not None:
175
186
  attributes[pk_name] = Column(Integer, primary_key=True)
176
- cls = type(table.name.capitalize(), (GeoInterface, Base), attributes)
187
+ # The randint is to fix the SAWarning: This declarative base already contains a class with the same
188
+ # class name and module name
189
+ cls = type(
190
+ f"{table.name.capitalize()}_{random.randint(0, 9999999)}", (GeoInterface, Base), attributes # nosec
191
+ )
177
192
 
178
193
  for col in table.columns:
179
194
  if col.name in (readonly_attributes or []):
@@ -184,8 +199,10 @@ def _create_class(
184
199
  return cls
185
200
 
186
201
 
187
- def _add_association_proxy(cls, col):
188
- if len(col.foreign_keys) != 1: # pragma: no cover
202
+ def _add_association_proxy(
203
+ cls: sqlalchemy.ext.declarative.DeclarativeMeta, col: sqlalchemy.sql.schema.Column
204
+ ) -> None:
205
+ if len(col.foreign_keys) != 1:
189
206
  raise NotImplementedError
190
207
 
191
208
  fk = next(iter(col.foreign_keys))
@@ -193,10 +210,8 @@ def _add_association_proxy(cls, col):
193
210
  child_cls = get_class(child_tablename)
194
211
 
195
212
  try:
196
- # fmt: off
197
- proxy = col.name[0:col.name.rindex("_id")]
198
- # fmt: on
199
- except ValueError: # pragma: no cover
213
+ proxy = col.name[0 : col.name.rindex("_id")]
214
+ except ValueError:
200
215
  proxy = col.name + "_"
201
216
 
202
217
  # The following is a workaround for a bug in the geojson lib. The
@@ -204,7 +219,7 @@ def _add_association_proxy(cls, col):
204
219
  # (this is a GeoJSON keyword), and produces a UnicodeEncodeError
205
220
  # if the property includes non-ascii characters.
206
221
  if proxy == "type":
207
- proxy = "type_" # pragma: no cover
222
+ proxy = "type_"
208
223
 
209
224
  rel = proxy + "_"
210
225
  primaryjoin = getattr(cls, col.name) == getattr(child_cls, child_pk)
@@ -1,6 +1,4 @@
1
- # -*- coding: utf-8 -*-
2
-
3
- # Copyright (c) 2014-2020, Camptocamp SA
1
+ # Copyright (c) 2014-2024, Camptocamp SA
4
2
  # All rights reserved.
5
3
 
6
4
  # Redistribution and use in source and binary forms, with or without
@@ -30,51 +28,59 @@
30
28
 
31
29
  import copy
32
30
  import logging
33
- import xml.sax.handler
31
+ import xml.sax.handler # nosec
34
32
  from io import StringIO
35
- from typing import Callable, Dict, List
36
- from urllib.parse import urlsplit
37
- from xml.sax.saxutils import XMLFilterBase, XMLGenerator
33
+ from typing import Any, Callable, Dict, List, Optional, Set, Union
34
+ from xml.sax.saxutils import XMLFilterBase, XMLGenerator # nosec
38
35
 
39
36
  import defusedxml.expatreader
37
+ import pyramid.httpexceptions
38
+ import pyramid.request
40
39
  import requests
40
+ from owslib.map.wms111 import ContentMetadata as ContentMetadata111
41
+ from owslib.map.wms130 import ContentMetadata as ContentMetadata130
41
42
  from owslib.wms import WebMapService
42
43
  from pyramid.httpexceptions import HTTPBadGateway
43
44
 
44
- from c2cgeoportal_commons.lib.url import add_url_params
45
+ from c2cgeoportal_commons.lib.url import Url
45
46
  from c2cgeoportal_geoportal.lib import caching, get_ogc_server_wfs_url_ids, get_ogc_server_wms_url_ids
46
47
  from c2cgeoportal_geoportal.lib.layers import get_private_layers, get_protected_layers, get_writable_layers
47
48
 
48
49
  CACHE_REGION = caching.get_region("std")
49
50
  LOG = logging.getLogger(__name__)
51
+ ContentMetadata = Union[ContentMetadata111, ContentMetadata130]
50
52
 
51
53
 
52
- @CACHE_REGION.cache_on_arguments()
53
- def wms_structure(wms_url, host, request):
54
- url = urlsplit(wms_url)
55
- wms_url = add_url_params(wms_url, {"SERVICE": "WMS", "VERSION": "1.1.1", "REQUEST": "GetCapabilities"})
54
+ @CACHE_REGION.cache_on_arguments() # type: ignore
55
+ def wms_structure(wms_url: Url, host: str, request: pyramid.request.Request) -> Dict[str, List[str]]:
56
+ """Get a simple serializable structure of the WMS capabilities."""
57
+ url = wms_url.clone().add_query({"SERVICE": "WMS", "VERSION": "1.1.1", "REQUEST": "GetCapabilities"})
56
58
 
57
59
  # Forward request to target (without Host Header)
58
- headers = dict()
59
- if url.hostname == "localhost" and host is not None: # pragma: no cover
60
+ headers = {}
61
+ if url.hostname == "localhost" and host is not None:
60
62
  headers["Host"] = host
61
63
  try:
62
- response = requests.get(wms_url, headers=headers, **request.registry.settings.get("http_options", {}))
63
- except Exception: # pragma: no cover
64
- raise HTTPBadGateway("Unable to GetCapabilities from wms_url {0!s}".format(wms_url))
64
+ response = requests.get(
65
+ url.url(), headers=headers, **request.registry.settings.get("http_options", {})
66
+ )
67
+ except Exception:
68
+ LOG.exception("Unable to GetCapabilities from wms_url '%s'", wms_url)
69
+ raise HTTPBadGateway( # pylint: disable=raise-missing-from
70
+ "Unable to GetCapabilities, see logs for details"
71
+ )
65
72
 
66
- if not response.ok: # pragma: no cover
73
+ if not response.ok:
67
74
  raise HTTPBadGateway(
68
- "GetCapabilities from wms_url {0!s} return the error: {1:d} {2!s}".format(
69
- wms_url, response.status_code, response.reason
70
- )
75
+ f"GetCapabilities from wms_url {url.url()} return the error: "
76
+ f"{response.status_code:d} {response.reason}"
71
77
  )
72
78
 
73
79
  try:
74
80
  wms = WebMapService(None, xml=response.content)
75
81
  result: Dict[str, List[str]] = {}
76
82
 
77
- def _fill(name, parent):
83
+ def _fill(name: str, parent: ContentMetadata) -> None:
78
84
  if parent is None:
79
85
  return
80
86
  if parent.name not in result:
@@ -86,37 +92,48 @@ def wms_structure(wms_url, host, request):
86
92
  _fill(layer.name, layer.parent)
87
93
  return result
88
94
 
89
- except AttributeError: # pragma: no cover
95
+ except AttributeError:
90
96
  error = "WARNING! an error occurred while trying to read the mapfile and recover the themes."
91
- error = "{0!s}\nurl: {1!s}\nxml:\n{2!s}".format(error, wms_url, response.text)
97
+ error = f"{error}\nurl: {wms_url}\nxml:\n{response.text}"
92
98
  LOG.exception(error)
93
- raise HTTPBadGateway(error)
99
+ raise HTTPBadGateway(error) # pylint: disable=raise-missing-from
94
100
 
95
- except SyntaxError: # pragma: no cover
101
+ except SyntaxError:
96
102
  error = "WARNING! an error occurred while trying to read the mapfile and recover the themes."
97
- error = "{0!s}\nurl: {1!s}\nxml:\n{2!s}".format(error, wms_url, response.text)
103
+ error = f"{error}\nurl: {wms_url}\nxml:\n{response.text}"
98
104
  LOG.exception(error)
99
- raise HTTPBadGateway(error)
105
+ raise HTTPBadGateway(error) # pylint: disable=raise-missing-from
100
106
 
101
107
 
102
- def filter_capabilities(content, wms, url, headers, request):
108
+ def filter_capabilities(
109
+ content: str, wms: bool, url: Url, headers: Dict[str, str], request: pyramid.request.Request
110
+ ) -> str:
111
+ """Filter the WMS/WFS capabilities."""
103
112
 
104
113
  wms_structure_ = wms_structure(url, headers.get("Host"), request)
105
114
 
106
115
  ogc_server_ids = (
107
- get_ogc_server_wms_url_ids(request) if wms else get_ogc_server_wfs_url_ids(request)
108
- ).get(url)
116
+ get_ogc_server_wms_url_ids(request, request.host)
117
+ if wms
118
+ else get_ogc_server_wfs_url_ids(request, request.host)
119
+ ).get(url.url())
109
120
  gmf_private_layers = copy.copy(get_private_layers(ogc_server_ids))
110
121
  for id_ in list(get_protected_layers(request, ogc_server_ids).keys()):
111
122
  if id_ in gmf_private_layers:
112
123
  del gmf_private_layers[id_]
113
124
 
114
125
  private_layers = set()
115
- for gmflayer in list(gmf_private_layers.values()):
116
- for ogclayer in gmflayer.layer.split(","):
117
- private_layers.add(ogclayer)
118
- if ogclayer in wms_structure_:
119
- private_layers.update(wms_structure_[ogclayer])
126
+ for gmf_layer in list(gmf_private_layers.values()):
127
+ for ogc_layer in gmf_layer.layer.split(","):
128
+ private_layers.add(ogc_layer)
129
+ if ogc_layer in wms_structure_:
130
+ private_layers.update(wms_structure_[ogc_layer])
131
+
132
+ LOG.debug(
133
+ "Filter capabilities of OGC server %s\nprivate_layers: %s",
134
+ ", ".join([str(e) for e in ogc_server_ids]),
135
+ ", ".join(private_layers),
136
+ )
120
137
 
121
138
  parser = defusedxml.expatreader.create_parser(forbid_external=False)
122
139
  # skip inclusion of DTDs
@@ -128,15 +145,27 @@ def filter_capabilities(content, wms, url, headers, request):
128
145
  filter_handler = _CapabilitiesFilter(
129
146
  parser, downstream_handler, "Layer" if wms else "FeatureType", layers_blacklist=private_layers
130
147
  )
131
- filter_handler.parse(StringIO(content))
148
+ filter_handler.parse(StringIO(content)) # type: ignore
132
149
  return result.getvalue()
133
150
 
134
151
 
135
- def filter_wfst_capabilities(content, wfs_url, request):
136
- writable_layers: List[str] = []
137
- ogc_server_ids = get_ogc_server_wfs_url_ids(request).get(wfs_url)
138
- for gmflayer in list(get_writable_layers(request, ogc_server_ids).values()):
139
- writable_layers += gmflayer.layer.split(",")
152
+ def filter_wfst_capabilities(content: str, wfs_url: Url, request: pyramid.request.Request) -> str:
153
+ """Filter the WTS capabilities."""
154
+
155
+ writable_layers: Set[str] = set()
156
+ ogc_server_ids = get_ogc_server_wfs_url_ids(request, request.host).get(wfs_url.url())
157
+ if ogc_server_ids is None:
158
+ LOG.error("No OGC server found for WFS URL %s", wfs_url)
159
+ raise pyramid.httpexceptions.HTTPInternalServerError("No OGC server found for WFS URL")
160
+
161
+ for gmf_layer in list(get_writable_layers(request, ogc_server_ids).values()):
162
+ writable_layers |= set(gmf_layer.layer.split(","))
163
+
164
+ LOG.debug(
165
+ "Filter WFS-T capabilities of OGC server %s\nlayers: %s",
166
+ ", ".join([str(e) for e in ogc_server_ids]),
167
+ ", ".join(writable_layers),
168
+ )
140
169
 
141
170
  parser = defusedxml.expatreader.create_parser(forbid_external=False)
142
171
  # skip inclusion of DTDs
@@ -148,13 +177,13 @@ def filter_wfst_capabilities(content, wfs_url, request):
148
177
  filter_handler = _CapabilitiesFilter(
149
178
  parser, downstream_handler, "FeatureType", layers_whitelist=writable_layers
150
179
  )
151
- filter_handler.parse(StringIO(content))
180
+ filter_handler.parse(StringIO(content)) # type: ignore
152
181
  return result.getvalue()
153
182
 
154
183
 
155
184
  class _Layer:
156
- def __init__(self, self_hidden=False):
157
- self.accumul: List[Callable] = []
185
+ def __init__(self, self_hidden: bool = False):
186
+ self.accumulator: List[Callable[[], None]] = []
158
187
  self.hidden = True
159
188
  self.self_hidden = self_hidden
160
189
  self.has_children = False
@@ -164,13 +193,20 @@ class _Layer:
164
193
  class _CapabilitiesFilter(XMLFilterBase):
165
194
  """
166
195
  SAX filter to show only the allowed layers in a GetCapabilities request.
167
- The filter removes elements of type `tag_name` where the `name`
168
- attribute is part of the set `layers_blacklist` (when `layers_blacklist`
169
- is given) or is not part of the set `layers_whitelist` (when
196
+
197
+ The filter removes elements of type `tag_name` where the `name` attribute is part of the set
198
+ `layers_blacklist` (when `layers_blacklist` is given) or is not part of the set `layers_whitelist` (when
170
199
  `layers_whitelist` is given).
171
200
  """
172
201
 
173
- def __init__(self, upstream, downstream, tag_name, layers_blacklist=None, layers_whitelist=None):
202
+ def __init__(
203
+ self,
204
+ upstream: XMLFilterBase,
205
+ downstream: XMLGenerator,
206
+ tag_name: str,
207
+ layers_blacklist: Optional[Set[str]] = None,
208
+ layers_whitelist: Optional[Set[str]] = None,
209
+ ):
174
210
  XMLFilterBase.__init__(self, upstream)
175
211
  self._downstream = downstream
176
212
  self._accumulator: List[str] = []
@@ -183,9 +219,9 @@ class _CapabilitiesFilter(XMLFilterBase):
183
219
  ), "only either layers_blacklist OR layers_whitelist can be set"
184
220
 
185
221
  if layers_blacklist is not None:
186
- layers_blacklist = [layer.lower() for layer in layers_blacklist]
222
+ layers_blacklist = {layer.lower() for layer in layers_blacklist}
187
223
  if layers_whitelist is not None:
188
- layers_whitelist = [layer.lower() for layer in layers_whitelist]
224
+ layers_whitelist = {layer.lower() for layer in layers_whitelist}
189
225
  self.layers_blacklist = layers_blacklist
190
226
  self.layers_whitelist = layers_whitelist
191
227
 
@@ -196,39 +232,39 @@ class _CapabilitiesFilter(XMLFilterBase):
196
232
 
197
233
  def _complete_text_node(self) -> None:
198
234
  if self._accumulator:
199
- self._downstream.characters("".join(self._accumulator))
235
+ self._downstream.characters("".join(self._accumulator)) # type: ignore
200
236
  self._accumulator = []
201
237
 
202
- def _do(self, action):
238
+ def _do(self, action: Callable[[], Any]) -> None:
203
239
  if self.layers_path:
204
- self.layers_path[-1].accumul.append(action)
240
+ self.layers_path[-1].accumulator.append(action)
205
241
  else:
206
242
  self._complete_text_node()
207
243
  action()
208
244
 
209
- def _add_child(self, layer):
245
+ def _add_child(self, layer: _Layer) -> None:
210
246
  if not layer.hidden and not (layer.has_children and layer.children_nb == 0):
211
- for action in layer.accumul:
247
+ for action in layer.accumulator:
212
248
  self._complete_text_node()
213
249
  action()
214
- layer.accumul = []
250
+ layer.accumulator = []
215
251
 
216
- def setDocumentLocator(self, locator): # noqa
217
- self._downstream.setDocumentLocator(locator)
252
+ def setDocumentLocator(self, locator: str) -> None: # noqa: ignore=N802
253
+ self._downstream.setDocumentLocator(locator) # type: ignore
218
254
 
219
- def startDocument(self): # noqa
220
- self._downstream.startDocument()
255
+ def startDocument(self) -> None: # noqa: ignore=N802
256
+ self._downstream.startDocument() # type: ignore
221
257
 
222
- def endDocument(self): # noqa
223
- self._downstream.endDocument()
258
+ def endDocument(self) -> None: # noqa: ignore=N802
259
+ self._downstream.endDocument() # type: ignore
224
260
 
225
- def startPrefixMapping(self, prefix, uri): # pragma: no cover # noqa
226
- self._downstream.startPrefixMapping(prefix, uri)
261
+ def startPrefixMapping(self, prefix: str, uri: str) -> None: # noqa: ignore=N802
262
+ self._downstream.startPrefixMapping(prefix, uri) # type: ignore
227
263
 
228
- def endPrefixMapping(self, prefix): # pragma: no cover # noqa
229
- self._downstream.endPrefixMapping(prefix)
264
+ def endPrefixMapping(self, prefix: str) -> None: # noqa: ignore=N802
265
+ self._downstream.endPrefixMapping(prefix) # type: ignore
230
266
 
231
- def startElement(self, name, attrs): # noqa
267
+ def startElement(self, name: str, attrs: Dict[str, str]) -> None: # noqa: ignore=N802
232
268
  if name == self.tag_name:
233
269
  self.level += 1
234
270
  if self.layers_path:
@@ -238,40 +274,40 @@ class _CapabilitiesFilter(XMLFilterBase):
238
274
  layer = _Layer(parent_layer.self_hidden) if len(self.layers_path) > 1 else _Layer()
239
275
  self.layers_path.append(layer)
240
276
 
241
- parent_layer.accumul.append(lambda: self._add_child(layer))
277
+ parent_layer.accumulator.append(lambda: self._add_child(layer))
242
278
  else:
243
279
  layer = _Layer()
244
280
  self.layers_path.append(layer)
245
281
  elif name == "Name" and self.layers_path:
246
282
  self.in_name = True
247
283
 
248
- self._do(lambda: self._downstream.startElement(name, attrs))
284
+ self._do(lambda: self._downstream.startElement(name, attrs)) # type: ignore
249
285
 
250
- def endElement(self, name): # noqa
251
- self._do(lambda: self._downstream.endElement(name))
286
+ def endElement(self, name: str) -> None: # noqa: ignore=N802
287
+ self._do(lambda: self._downstream.endElement(name)) # type: ignore
252
288
 
253
289
  if name == self.tag_name:
254
290
  self.level -= 1
255
291
  if self.level == 0 and not self.layers_path[0].hidden:
256
- for action in self.layers_path[0].accumul:
292
+ for action in self.layers_path[0].accumulator:
257
293
  self._complete_text_node()
258
294
  action()
259
295
  self.layers_path.pop()
260
296
  elif name == "Name":
261
297
  self.in_name = False
262
298
 
263
- def startElementNS(self, name, qname, attrs): # pragma: no cover # noqa
264
- self._do(lambda: self._downstream.startElementNS(name, qname, attrs))
299
+ def startElementNS(self, name: str, qname: str, attrs: Dict[str, str]) -> None: # noqa: ignore=N802
300
+ self._do(lambda: self._downstream.startElementNS(name, qname, attrs)) # type: ignore
265
301
 
266
- def endElementNS(self, name, qname): # pragma: no cover # noqa
267
- self._do(lambda: self._downstream.endElementNS(name, qname))
302
+ def endElementNS(self, name: str, qname: str) -> None: # noqa: ignore=N802
303
+ self._do(lambda: self._downstream.endElementNS(name, qname)) # type: ignore
268
304
 
269
- def _keep_layer(self, layer_name):
305
+ def _keep_layer(self, layer_name: str) -> bool:
270
306
  return (self.layers_blacklist is not None and layer_name not in self.layers_blacklist) or (
271
307
  self.layers_whitelist is not None and layer_name in self.layers_whitelist
272
308
  )
273
309
 
274
- def characters(self, content):
310
+ def characters(self, content: str) -> None:
275
311
  if self.in_name and self.layers_path and not self.layers_path[-1].self_hidden is True:
276
312
  layer_name = normalize_typename(content)
277
313
  if self._keep_layer(layer_name):
@@ -285,19 +321,20 @@ class _CapabilitiesFilter(XMLFilterBase):
285
321
 
286
322
  self._do(lambda: self._accumulator.append(content))
287
323
 
288
- def ignorableWhitespace(self, chars): # pragma: no cover # noqa
324
+ def ignorableWhitespace(self, chars: str) -> None: # noqa: ignore=N802
289
325
  self._do(lambda: self._accumulator.append(chars))
290
326
 
291
- def processingInstruction(self, target, data): # pragma: no cover # noqa
292
- self._do(lambda: self._downstream.processingInstruction(target, data))
327
+ def processingInstruction(self, target: str, data: str) -> None: # noqa: ignore=N802
328
+ self._do(lambda: self._downstream.processingInstruction(target, data)) # type: ignore
293
329
 
294
- def skippedEntity(self, name): # pragma: no cover # noqa
295
- self._downstream.skippedEntity(name)
330
+ def skippedEntity(self, name: str) -> None: # noqa: ignore=N802
331
+ self._downstream.skippedEntity(name) # type: ignore
296
332
 
297
333
 
298
- def normalize_tag(tag):
334
+ def normalize_tag(tag: str) -> str:
299
335
  """
300
- Drops the namespace from a tag and converts to lower case.
336
+ Drop the namespace from a tag and converts to lower case.
337
+
301
338
  e.g. '{https://....}TypeName' -> 'TypeName'
302
339
  """
303
340
  normalized = tag
@@ -307,9 +344,10 @@ def normalize_tag(tag):
307
344
  return normalized.lower()
308
345
 
309
346
 
310
- def normalize_typename(typename):
347
+ def normalize_typename(typename: str) -> str:
311
348
  """
312
- Drops the namespace from a type name and converts to lower case.
349
+ Drop the namespace from a type name and converts to lower case.
350
+
313
351
  e.g. 'tows:parks' -> 'parks'
314
352
  """
315
353
  normalized = typename
@@ -1,6 +1,4 @@
1
- # -*- coding: utf-8 -*-
2
-
3
- # Copyright (c) 2020, Camptocamp SA
1
+ # Copyright (c) 2020-2021, Camptocamp SA
4
2
  # All rights reserved.
5
3
 
6
4
  # Redistribution and use in source and binary forms, with or without
@@ -28,10 +26,13 @@
28
26
  # either expressed or implied, of the FreeBSD Project.
29
27
 
30
28
  import re
29
+ from typing import Any, Dict
31
30
 
32
31
 
33
32
  class Normalize:
34
- def __init__(self, config):
33
+ """Normalize a text for the Full text search."""
34
+
35
+ def __init__(self, config: Dict[str, Any]) -> None:
35
36
  split = config.get("split_regex")
36
37
  self.split_re = re.compile(split) if split is not None else None
37
38
 
@@ -39,7 +40,7 @@ class Normalize:
39
40
  for search_regex, text in config.get("replace", {}).items():
40
41
  self.word_replace.append((re.compile(search_regex), text))
41
42
 
42
- def __call__(self, text):
43
+ def __call__(self, text: str) -> str:
43
44
  if self.split_re is not None:
44
45
  text = " ".join(self.split_re.split(text))
45
46