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
@@ -33,15 +31,14 @@ import os
33
31
  import re
34
32
  import subprocess
35
33
  import traceback
36
- from typing import Dict, List, Optional, Set, cast
37
- from urllib.parse import urlsplit
34
+ from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Set, Tuple, Type, cast
38
35
  from xml.dom import Node
39
36
  from xml.parsers.expat import ExpatError
40
37
 
38
+ import pyramid.threadlocal
41
39
  import requests
42
- import sqlalchemy
43
40
  import yaml
44
- from bottle import MakoTemplate, template # pylint: disable=wrong-import-order,useless-suppression
41
+ from bottle import MakoTemplate, template
45
42
  from c2c.template.config import config
46
43
  from defusedxml.minidom import parseString
47
44
  from geoalchemy2.types import Geometry
@@ -52,60 +49,85 @@ from owslib.wms import WebMapService
52
49
  from sqlalchemy.exc import NoSuchTableError, OperationalError, ProgrammingError
53
50
  from sqlalchemy.orm.exc import NoResultFound
54
51
  from sqlalchemy.orm.properties import ColumnProperty
52
+ from sqlalchemy.orm.session import Session
55
53
  from sqlalchemy.orm.util import class_mapper
56
54
 
57
- import c2cgeoportal_commons.models
58
55
  import c2cgeoportal_geoportal
59
- from c2cgeoportal_commons.lib.url import add_url_params, get_url2
60
- from c2cgeoportal_geoportal.lib.bashcolor import RED, colorize
56
+ from c2cgeoportal_commons.lib.url import Url, get_url2
57
+ from c2cgeoportal_geoportal.lib.bashcolor import Color, colorize
61
58
  from c2cgeoportal_geoportal.lib.caching import init_region
62
59
  from c2cgeoportal_geoportal.views.layers import Layers, get_layer_class
63
60
 
61
+ if TYPE_CHECKING:
62
+ from c2cgeoportal_commons.models import main # pylint: disable=ungrouped-imports,useless-suppression
63
+
64
+
65
+ class LinguaExtractorException(Exception):
66
+ """Exception raised when an error occurs during the extraction."""
67
+
68
+
69
+ def _get_config(key: str, default: Optional[str] = None) -> Optional[str]:
70
+ """
71
+ Return the config value for passed key.
72
+
73
+ Passed throw environment variable for the command line,
74
+ or throw the query string on HTTP request.
75
+ """
76
+ request = pyramid.threadlocal.get_current_request()
77
+ if request is not None:
78
+ return cast(Optional[str], request.params.get(key.lower(), default))
79
+
80
+ return os.environ.get(key, default)
81
+
82
+
83
+ def _get_config_str(key: str, default: str) -> str:
84
+ result = _get_config(key, default)
85
+ assert result is not None
86
+ return result
87
+
64
88
 
65
- class _Registry: # pragma: no cover
89
+ class _Registry:
66
90
  settings = None
67
91
 
68
- def __init__(self, settings):
92
+ def __init__(self, settings: Optional[Dict[str, Any]]) -> None:
69
93
  self.settings = settings
70
94
 
71
95
 
72
- class _Request: # pragma: no cover
96
+ class _Request:
73
97
  params: Dict[str, str] = {}
74
98
  matchdict: Dict[str, str] = {}
75
99
  GET: Dict[str, str] = {}
76
100
 
77
- def __init__(self, settings=None):
101
+ def __init__(self, settings: Optional[Dict[str, Any]] = None) -> None:
78
102
  self.registry: _Registry = _Registry(settings)
79
103
 
80
104
  @staticmethod
81
- def static_url(*args, **kwargs):
105
+ def static_url(*args: Any, **kwargs: Any) -> str:
82
106
  del args
83
107
  del kwargs
84
108
  return ""
85
109
 
86
110
  @staticmethod
87
- def static_path(*args, **kwargs):
111
+ def static_path(*args: Any, **kwargs: Any) -> str:
88
112
  del args
89
113
  del kwargs
90
114
  return ""
91
115
 
92
116
  @staticmethod
93
- def route_url(*args, **kwargs):
117
+ def route_url(*args: Any, **kwargs: Any) -> str:
94
118
  del args
95
119
  del kwargs
96
120
  return ""
97
121
 
98
122
  @staticmethod
99
- def current_route_url(*args, **kwargs):
123
+ def current_route_url(*args: Any, **kwargs: Any) -> str:
100
124
  del args
101
125
  del kwargs
102
126
  return ""
103
127
 
104
128
 
105
- class GeomapfishAngularExtractor(Extractor): # pragma: no cover
106
- """
107
- GeoMapFish angular extractor
108
- """
129
+ class GeomapfishAngularExtractor(Extractor): # type: ignore
130
+ """GeoMapFish angular extractor."""
109
131
 
110
132
  extensions = [".js", ".html"]
111
133
 
@@ -118,27 +140,48 @@ class GeomapfishAngularExtractor(Extractor): # pragma: no cover
118
140
  self.config = None
119
141
  self.tpl = None
120
142
 
121
- def __call__(self, filename, options, fileobj=None, lineno=0):
143
+ @staticmethod
144
+ def get_message_cleaner(filename: str) -> Callable[[str], str]:
145
+ """Return a function for cleaning messages according to input file format."""
146
+ ext = os.path.splitext(filename)[1]
147
+
148
+ if ext in [".html", ".ejs"]:
149
+ # Remove \n in HTML multi-line strings
150
+ pattern = re.compile("\n *")
151
+ return lambda s: re.sub(pattern, " ", s)
152
+
153
+ return lambda s: s
154
+
155
+ def __call__(
156
+ self,
157
+ filename: str,
158
+ options: Dict[str, Any],
159
+ fileobj: Optional[Dict[str, Any]] = None,
160
+ lineno: int = 0,
161
+ ) -> List[Message]:
122
162
  del fileobj, lineno
123
163
 
164
+ print(f"Running {self.__class__.__name__} on {filename}")
165
+
166
+ cleaner = self.get_message_cleaner(filename)
167
+
124
168
  init_region({"backend": "dogpile.cache.memory"}, "std")
125
169
  init_region({"backend": "dogpile.cache.memory"}, "obj")
126
170
 
127
171
  int_filename = filename
128
- if re.match("^" + re.escape("./{}/templates".format(self.config["package"])), filename):
172
+ if re.match("^" + re.escape(f"./{self.config['package']}/templates"), filename):
129
173
  try:
130
174
  empty_template = Template("") # nosec
131
175
 
132
- class Lookup(TemplateLookup):
133
- @staticmethod
134
- def get_template(uri):
176
+ class Lookup(TemplateLookup): # type: ignore
177
+ def get_template(self, uri: str) -> Template:
135
178
  del uri # unused
136
179
  return empty_template
137
180
 
138
- class MyTemplate(MakoTemplate):
181
+ class MyTemplate(MakoTemplate): # type: ignore
139
182
  tpl = None
140
183
 
141
- def prepare(self, **kwargs):
184
+ def prepare(self, **kwargs: Any) -> None:
142
185
  options.update({"input_encoding": self.encoding})
143
186
  lookup = Lookup(**kwargs)
144
187
  if self.source:
@@ -149,10 +192,12 @@ class GeomapfishAngularExtractor(Extractor): # pragma: no cover
149
192
  )
150
193
 
151
194
  try:
195
+ request = pyramid.threadlocal.get_current_request()
196
+ request = _Request() if request is None else request
152
197
  processed = template(
153
198
  filename,
154
199
  {
155
- "request": _Request(self.config),
200
+ "request": request,
156
201
  "lang": "fr",
157
202
  "debug": False,
158
203
  "extra_params": {},
@@ -167,11 +212,9 @@ class GeomapfishAngularExtractor(Extractor): # pragma: no cover
167
212
  with open(int_filename, "wb") as file_open:
168
213
  file_open.write(processed.encode("utf-8"))
169
214
  except Exception:
170
- print(
171
- colorize("ERROR! Occurred during the '{}' template generation".format(filename), RED)
172
- )
173
- print(colorize(traceback.format_exc(), RED))
174
- if os.environ.get("IGNORE_I18N_ERRORS", "FALSE") == "TRUE":
215
+ print(colorize(f"ERROR! Occurred during the '{filename}' template generation", Color.RED))
216
+ print(colorize(traceback.format_exc(), Color.RED))
217
+ if _get_config_str("IGNORE_I18N_ERRORS", "FALSE") == "TRUE":
175
218
  # Continue with the original one
176
219
  int_filename = filename
177
220
  else:
@@ -179,38 +222,81 @@ class GeomapfishAngularExtractor(Extractor): # pragma: no cover
179
222
  except Exception:
180
223
  print(traceback.format_exc())
181
224
 
182
- message_str = subprocess.check_output(
183
- ["node", "geoportal/tools/extract-messages.js", int_filename]
184
- ).decode("utf-8")
225
+ # Path in geomapfish-tools
226
+ script_path = "/opt/c2cgeoportal/geoportal/extract-messages.js"
227
+ message_str = subprocess.check_output(["node", script_path, int_filename]).decode("utf-8")
185
228
  if int_filename != filename:
186
229
  os.unlink(int_filename)
187
230
  try:
188
231
  messages = []
189
232
  for contexts, message in json.loads(message_str):
233
+ assert message is not None
234
+ message = cleaner(message)
190
235
  for context in contexts.split(", "):
191
- assert message is not None
192
236
  messages.append(Message(None, message, None, [], "", "", context.split(":")))
193
237
  return messages
194
238
  except Exception:
195
- print(colorize("An error occurred", RED))
196
- print(colorize(message_str, RED))
239
+ print(colorize("An error occurred", Color.RED))
240
+ print(colorize(message_str, Color.RED))
197
241
  print("------")
198
242
  raise
199
243
 
200
244
 
201
- class GeomapfishConfigExtractor(Extractor): # pragma: no cover
245
+ def init_db(settings: Dict[str, Any]) -> None:
202
246
  """
203
- GeoMapFish config extractor (raster layers, and print templates)
247
+ Initialize the SQLAlchemy Session.
248
+
249
+ First test the connection, on when environment it should be OK, with the command line we should get
250
+ an exception ind initialize the connection.
204
251
  """
205
252
 
253
+ try:
254
+ from c2cgeoportal_commons.models import DBSession # pylint: disable=import-outside-toplevel
255
+ from c2cgeoportal_commons.models.main import Theme # pylint: disable=import-outside-toplevel
256
+
257
+ session = DBSession()
258
+ session.query(Theme).count()
259
+ except: # pylint: disable=bare-except
260
+ # Init db sessions
261
+
262
+ class R:
263
+ settings: Dict[str, Any] = {}
264
+
265
+ class C:
266
+ registry = R()
267
+
268
+ def get_settings(self) -> Dict[str, Any]:
269
+ return self.registry.settings
270
+
271
+ def add_tween(self, *args: Any, **kwargs: Any) -> None:
272
+ pass
273
+
274
+ config_ = C()
275
+ config_.registry.settings = settings
276
+
277
+ c2cgeoportal_geoportal.init_db_sessions(settings, config_)
278
+
279
+
280
+ class GeomapfishConfigExtractor(Extractor): # type: ignore
281
+ """GeoMapFish config extractor (raster layers, and print templates)."""
282
+
206
283
  extensions = [".yaml", ".tmpl"]
207
284
 
208
- def __call__(self, filename, options, fileobj=None, lineno=0):
209
- del fileobj, lineno
285
+ def __call__(
286
+ self,
287
+ filename: str,
288
+ options: Dict[str, Any],
289
+ fileobj: Optional[Dict[str, Any]] = None,
290
+ lineno: int = 0,
291
+ ) -> List[Message]:
292
+ del options, fileobj, lineno
293
+
294
+ print(f"Running {self.__class__.__name__} on {filename}")
295
+
210
296
  init_region({"backend": "dogpile.cache.memory"}, "std")
211
297
  init_region({"backend": "dogpile.cache.memory"}, "obj")
212
298
 
213
- with open(filename) as config_file:
299
+ with open(filename, encoding="utf8") as config_file:
214
300
  gmf_config = yaml.load(config_file, Loader=yaml.BaseLoader) # nosec
215
301
  # For application config (config.yaml)
216
302
  if "vars" in gmf_config:
@@ -218,9 +304,9 @@ class GeomapfishConfigExtractor(Extractor): # pragma: no cover
218
304
  # For the print config
219
305
  if "templates" in gmf_config:
220
306
  return self._collect_print_config(gmf_config, filename)
221
- raise Exception("Not a known config file")
307
+ raise Exception("Not a known config file") # pylint: disable=broad-exception-raised
222
308
 
223
- def _collect_app_config(self, filename):
309
+ def _collect_app_config(self, filename: str) -> List[Message]:
224
310
  config.init(filename)
225
311
  settings = config.get_config()
226
312
  assert not [
@@ -228,32 +314,18 @@ class GeomapfishConfigExtractor(Extractor): # pragma: no cover
228
314
  ]
229
315
  # Collect raster layers names
230
316
  raster = [
231
- Message(None, raster_layer, None, [], "", "", (filename, "raster/{}".format(raster_layer)))
317
+ Message(None, raster_layer, None, [], "", "", (filename, f"raster/{raster_layer}"))
232
318
  for raster_layer in list(settings.get("raster", {}).keys())
233
319
  ]
234
320
 
235
- # Init db sessions
236
-
237
- class R:
238
- settings = None
239
-
240
- class C:
241
- registry = R()
242
-
243
- def get_settings(self):
244
- return self.registry.settings
245
-
246
- def add_tween(self, *args, **kwargs):
247
- pass
248
-
249
- config_ = C()
250
- config_.registry.settings = settings
251
-
252
- c2cgeoportal_geoportal.init_dbsessions(settings, config_)
321
+ init_db(settings)
253
322
 
254
323
  # Collect layers enum values (for filters)
255
324
 
256
- from c2cgeoportal_commons.models import DBSessions # pylint: disable=import-outside-toplevel
325
+ from c2cgeoportal_commons.models import ( # pylint: disable=import-outside-toplevel
326
+ DBSession,
327
+ DBSessions,
328
+ )
257
329
  from c2cgeoportal_commons.models.main import Metadata # pylint: disable=import-outside-toplevel
258
330
 
259
331
  enums = []
@@ -262,14 +334,13 @@ class GeomapfishConfigExtractor(Extractor): # pragma: no cover
262
334
  layerinfos = enum_layers.get(layername, {})
263
335
  attributes = layerinfos.get("attributes", {})
264
336
  for fieldname in list(attributes.keys()):
265
- values = self._enumerate_attributes_values(DBSessions, Layers, layerinfos, fieldname)
337
+ values = self._enumerate_attributes_values(DBSessions, layerinfos, fieldname)
266
338
  for (value,) in values:
267
339
  if isinstance(value, str) and value != "":
268
340
  msgid = value
269
- location = "/layers/{}/values/{}/{}".format(
270
- layername,
271
- fieldname,
272
- value.encode("ascii", errors="replace").decode("ascii"),
341
+ location = (
342
+ f"/layers/{layername}/values/{fieldname}/"
343
+ f"{value.encode('ascii', errors='replace').decode('ascii')}"
273
344
  )
274
345
  assert msgid is not None
275
346
  enums.append(Message(None, msgid, None, [], "", "", (filename, location)))
@@ -279,14 +350,11 @@ class GeomapfishConfigExtractor(Extractor): # pragma: no cover
279
350
  names = [e["name"] for e in defs if e.get("translate", False)]
280
351
 
281
352
  if names:
282
- engine = sqlalchemy.create_engine(config["sqlalchemy.url"])
283
- Session = sqlalchemy.orm.session.sessionmaker() # noqa
284
- Session.configure(bind=engine)
285
- session = Session()
353
+ session = DBSession()
286
354
 
287
- query = session.query(Metadata).filter(Metadata.name.in_(names)) # pylint: disable=no-member
355
+ query = session.query(Metadata).filter(Metadata.name.in_(names))
288
356
  for metadata in query.all():
289
- location = "metadata/{}/{}".format(metadata.name, metadata.id)
357
+ location = f"metadata/{metadata.name}/{metadata.id}"
290
358
  assert metadata.value is not None
291
359
  metadata_list.append(Message(None, metadata.value, None, [], "", "", (filename, location)))
292
360
 
@@ -297,8 +365,8 @@ class GeomapfishConfigExtractor(Extractor): # pragma: no cover
297
365
  ):
298
366
  for a_index, action in enumerate(datasource.get("groupActions", [])):
299
367
  location = (
300
- "interfaces_config/{}/constants/gmfSearchOptions/datasources[{}]/"
301
- "groupActions[{}]/title".format(interface, ds_index, a_index)
368
+ f"interfaces_config/{interface}/constants/gmfSearchOptions/datasources[{ds_index}]/"
369
+ f"groupActions[{a_index}]/title"
302
370
  )
303
371
  assert action["title"] is not None
304
372
  interfaces_messages.append(
@@ -311,8 +379,9 @@ class GeomapfishConfigExtractor(Extractor): # pragma: no cover
311
379
  .get("mergeTabs", {})
312
380
  .keys()
313
381
  ):
314
- location = "interfaces_config/{}/constants/gmfDisplayQueryGridOptions/mergeTabs/{}/".format(
315
- interface, merge_tab
382
+ location = (
383
+ f"interfaces_config/{interface}/constants/gmfDisplayQueryGridOptions/"
384
+ f"mergeTabs/{merge_tab}/"
316
385
  )
317
386
  assert merge_tab is not None
318
387
  interfaces_messages.append(Message(None, merge_tab, None, [], "", "", (filename, location)))
@@ -320,35 +389,35 @@ class GeomapfishConfigExtractor(Extractor): # pragma: no cover
320
389
  return raster + enums + metadata_list + interfaces_messages
321
390
 
322
391
  @staticmethod
323
- def _enumerate_attributes_values(dbsessions, layers, layerinfos, fieldname):
392
+ def _enumerate_attributes_values(
393
+ dbsessions: Dict[str, Session], layerinfos: Dict[str, Any], fieldname: str
394
+ ) -> Set[Tuple[str, ...]]:
324
395
  dbname = layerinfos.get("dbsession", "dbsession")
325
- translate = layerinfos.get("attributes").get(fieldname, {}).get("translate", True)
396
+ translate = cast(Dict[str, Any], layerinfos["attributes"]).get(fieldname, {}).get("translate", True)
326
397
  if not translate:
327
- return []
398
+ return set()
328
399
  try:
329
400
  dbsession = dbsessions.get(dbname)
330
- return layers.query_enumerate_attribute_values(dbsession, layerinfos, fieldname)
401
+ return Layers.query_enumerate_attribute_values(dbsession, layerinfos, fieldname)
331
402
  except Exception as e:
332
- table = layerinfos.get("attributes").get(fieldname, {}).get("table")
403
+ table = cast(Dict[str, Any], layerinfos["attributes"]).get(fieldname, {}).get("table")
333
404
  print(
334
405
  colorize(
335
406
  "ERROR! Unable to collect enumerate attributes for "
336
- "db: {}, table: {}, column: {} ({})".format(dbname, table, fieldname, e),
337
- RED,
407
+ f"db: {dbname}, table: {table}, column: {fieldname} ({e!s})",
408
+ Color.RED,
338
409
  )
339
410
  )
340
- if os.environ.get("IGNORE_I18N_ERRORS", "FALSE") == "TRUE":
341
- return []
411
+ if _get_config_str("IGNORE_I18N_ERRORS", "FALSE") == "TRUE":
412
+ return set()
342
413
  raise
343
414
 
344
415
  @staticmethod
345
- def _collect_print_config(print_config, filename):
416
+ def _collect_print_config(print_config: Dict[str, Any], filename: str) -> List[Message]:
346
417
  result = []
347
- for template_ in list(print_config.get("templates").keys()):
418
+ for template_ in list(cast(Dict[str, Any], print_config.get("templates")).keys()):
348
419
  assert template_ is not None
349
- result.append(
350
- Message(None, template_, None, [], "", "", (filename, "template/{}".format(template_)))
351
- )
420
+ result.append(Message(None, template_, None, [], "", "", (filename, f"template/{template_}")))
352
421
  assert not [
353
422
  attribute
354
423
  for attribute in list(print_config["templates"][template_]["attributes"].keys())
@@ -362,22 +431,20 @@ class GeomapfishConfigExtractor(Extractor): # pragma: no cover
362
431
  [],
363
432
  "",
364
433
  "",
365
- (filename, "template/{}/{}".format(template_, attribute)),
434
+ (filename, f"template/{template_}/{attribute}"),
366
435
  )
367
436
  for attribute in list(print_config["templates"][template_]["attributes"].keys())
368
437
  ]
369
438
  return result
370
439
 
371
440
 
372
- class GeomapfishThemeExtractor(Extractor): # pragma: no cover
373
- """
374
- GeoMapFish theme extractor
375
- """
441
+ class GeomapfishThemeExtractor(Extractor): # type: ignore
442
+ """GeoMapFish theme extractor."""
376
443
 
377
444
  # Run on the development.ini file
378
445
  extensions = [".ini"]
379
- featuretype_cache: Dict[str, Optional[Dict]] = {}
380
- wmscap_cache: Dict[str, WebMapService] = {}
446
+ featuretype_cache: Dict[str, Optional[Dict[str, Any]]] = {}
447
+ wms_capabilities_cache: Dict[str, WebMapService] = {}
381
448
 
382
449
  def __init__(self) -> None:
383
450
  super().__init__()
@@ -388,16 +455,20 @@ class GeomapfishThemeExtractor(Extractor): # pragma: no cover
388
455
  self.config = None
389
456
  self.env = None
390
457
 
391
- def __call__(self, filename, options, fileobj=None, lineno=0):
458
+ def __call__(
459
+ self, filename: str, options: Dict[str, Any], fileobj: Optional[str] = None, lineno: int = 0
460
+ ) -> List[Message]:
392
461
  del fileobj, lineno
462
+
463
+ print(f"Running {self.__class__.__name__} on {filename}")
464
+
393
465
  messages: List[Message] = []
394
466
 
395
467
  try:
396
- engine = sqlalchemy.engine_from_config(self.config, "sqlalchemy_slave.")
397
- factory = sqlalchemy.orm.sessionmaker(bind=engine)
398
- db_session = sqlalchemy.orm.scoped_session(factory)
399
- c2cgeoportal_commons.models.DBSession = db_session
400
- c2cgeoportal_commons.models.Base.metadata.bind = engine
468
+ init_db(self.config)
469
+ from c2cgeoportal_commons.models import DBSession # pylint: disable=import-outside-toplevel
470
+
471
+ db_session = DBSession()
401
472
 
402
473
  try:
403
474
  from c2cgeoportal_commons.models.main import ( # pylint: disable=import-outside-toplevel
@@ -408,10 +479,25 @@ class GeomapfishThemeExtractor(Extractor): # pragma: no cover
408
479
  Theme,
409
480
  )
410
481
 
411
- self._import(Theme, messages)
412
- self._import(LayerGroup, messages)
413
- self._import(LayerWMS, messages, self._import_layer_wms)
414
- self._import(LayerWMTS, messages, self._import_layer_wmts)
482
+ self._import(Theme, messages, name_regex=_get_config_str("THEME_REGEX", ".*"))
483
+ self._import(
484
+ LayerGroup,
485
+ messages,
486
+ name_regex=_get_config_str("GROUP_REGEX", ".*"),
487
+ has_interfaces=False,
488
+ )
489
+ self._import(
490
+ LayerWMS,
491
+ messages,
492
+ self._import_layer_wms,
493
+ name_regex=_get_config_str("WMSLAYER_REGEX", ".*"),
494
+ )
495
+ self._import(
496
+ LayerWMTS,
497
+ messages,
498
+ self._import_layer_wmts,
499
+ name_regex=_get_config_str("WMTSLAYER_REGEX", ".*"),
500
+ )
415
501
 
416
502
  for (layer_name,) in db_session.query(FullTextSearch.layer_name).distinct().all():
417
503
  if layer_name is not None and layer_name != "":
@@ -448,60 +534,75 @@ class GeomapfishThemeExtractor(Extractor): # pragma: no cover
448
534
  colorize(
449
535
  "ERROR! The database is probably not up to date "
450
536
  "(should be ignored when happen during the upgrade)",
451
- RED,
537
+ Color.RED,
452
538
  )
453
539
  )
454
- print(colorize(e, RED))
455
- if os.environ.get("IGNORE_I18N_ERRORS", "FALSE") != "TRUE":
540
+ print(colorize(e, Color.RED))
541
+ if _get_config_str("IGNORE_I18N_ERRORS", "FALSE") != "TRUE":
456
542
  raise
457
543
  except NoSuchTableError as e:
458
544
  print(
459
545
  colorize(
460
546
  "ERROR! The schema didn't seem to exists "
461
547
  "(should be ignored when happen during the deploy)",
462
- RED,
548
+ Color.RED,
463
549
  )
464
550
  )
465
- print(colorize(e, RED))
466
- if os.environ.get("IGNORE_I18N_ERRORS", "FALSE") != "TRUE":
551
+ print(colorize(e, Color.RED))
552
+ if _get_config_str("IGNORE_I18N_ERRORS", "FALSE") != "TRUE":
467
553
  raise
468
554
  except OperationalError as e:
469
555
  print(
470
556
  colorize(
471
557
  "ERROR! The database didn't seem to exists "
472
558
  "(should be ignored when happen during the deploy)",
473
- RED,
559
+ Color.RED,
474
560
  )
475
561
  )
476
- print(colorize(e, RED))
477
- if os.environ.get("IGNORE_I18N_ERRORS", "FALSE") != "TRUE":
562
+ print(colorize(e, Color.RED))
563
+ if _get_config_str("IGNORE_I18N_ERRORS", "FALSE") != "TRUE":
478
564
  raise
479
565
 
480
566
  return messages
481
567
 
482
568
  @staticmethod
483
- def _import(object_type, messages, callback=None):
569
+ def _import(
570
+ object_type: Type[Any],
571
+ messages: List[str],
572
+ callback: Optional[Callable[["main.Layer", List[str]], None]] = None,
573
+ has_interfaces: bool = True,
574
+ name_regex: str = ".*",
575
+ ) -> None:
484
576
  from c2cgeoportal_commons.models import DBSession # pylint: disable=import-outside-toplevel
577
+ from c2cgeoportal_commons.models.main import Interface # pylint: disable=import-outside-toplevel
578
+
579
+ filter_re = re.compile(name_regex)
485
580
 
486
- items = DBSession.query(object_type)
487
- for item in items:
581
+ query = DBSession.query(object_type)
582
+
583
+ interfaces = _get_config("INTERFACES")
584
+ if has_interfaces and interfaces is not None:
585
+ query.join(object_type.interface).filter(Interface.name in interfaces.split("."))
586
+
587
+ for item in query.all():
488
588
  assert item.name is not None
489
- messages.append(
490
- Message(
491
- None,
492
- item.name,
493
- None,
494
- [],
495
- "",
496
- "",
497
- (item.item_type, item.name.encode("ascii", errors="replace")),
589
+ if filter_re.match(item.name):
590
+ messages.append(
591
+ Message(
592
+ None,
593
+ item.name,
594
+ None,
595
+ [],
596
+ "",
597
+ "",
598
+ (item.item_type, item.name.encode("ascii", errors="replace")),
599
+ )
498
600
  )
499
- )
500
601
 
501
602
  if callback is not None:
502
603
  callback(item, messages)
503
604
 
504
- def _import_layer_wms(self, layer, messages):
605
+ def _import_layer_wms(self, layer: "main.Layer", messages: List[str]) -> None:
505
606
  server = layer.ogc_server
506
607
  url = server.url_wfs or server.url
507
608
  if url is None:
@@ -540,14 +641,15 @@ class GeomapfishThemeExtractor(Extractor): # pragma: no cover
540
641
  except NoSuchTableError:
541
642
  print(
542
643
  colorize(
543
- "ERROR! No such table '{}' for layer '{}'.".format(layer.geo_table, layer.name), RED
644
+ f"ERROR! No such table '{layer.geo_table}' for layer '{layer.name}'.",
645
+ Color.RED,
544
646
  )
545
647
  )
546
- print(colorize(traceback.format_exc(), RED))
547
- if os.environ.get("IGNORE_I18N_ERRORS", "FALSE") != "TRUE":
648
+ print(colorize(traceback.format_exc(), Color.RED))
649
+ if _get_config_str("IGNORE_I18N_ERRORS", "FALSE") != "TRUE":
548
650
  raise
549
651
 
550
- def _import_layer_wmts(self, layer, messages):
652
+ def _import_layer_wmts(self, layer: "main.Layer", messages: List[str]) -> None:
551
653
  from c2cgeoportal_commons.models import DBSession # pylint: disable=import-outside-toplevel
552
654
  from c2cgeoportal_commons.models.main import OGCServer # pylint: disable=import-outside-toplevel
553
655
 
@@ -571,16 +673,17 @@ class GeomapfishThemeExtractor(Extractor): # pragma: no cover
571
673
  except NoResultFound:
572
674
  print(
573
675
  colorize(
574
- "ERROR! the OGC server '{}' from the WMTS layer '{}' does not exist.".format(
575
- server[0], layer.name
576
- ),
577
- RED,
676
+ f"ERROR! the OGC server '{server[0]}' from the "
677
+ f"WMTS layer '{layer.name}' does not exist.",
678
+ Color.RED,
578
679
  )
579
680
  )
580
- if os.environ.get("IGNORE_I18N_ERRORS", "FALSE") != "TRUE":
681
+ if _get_config_str("IGNORE_I18N_ERRORS", "FALSE") != "TRUE":
581
682
  raise
582
683
 
583
- def _import_layer_attributes(self, url, layer, item_type, name, messages):
684
+ def _import_layer_attributes(
685
+ self, url: str, layer: "main.Layer", item_type: str, name: str, messages: List[str]
686
+ ) -> None:
584
687
  attributes, layers = self._layer_attributes(url, layer)
585
688
  for sub_layer in layers:
586
689
  assert sub_layer is not None
@@ -609,133 +712,153 @@ class GeomapfishThemeExtractor(Extractor): # pragma: no cover
609
712
  )
610
713
  )
611
714
 
612
- def _build_url(self, url):
613
- url_split = urlsplit(url)
614
- hostname = url_split.hostname
715
+ def _build_url(self, url: Url) -> Tuple[Url, Dict[str, str], Dict[str, Any]]:
716
+ hostname = url.hostname
615
717
  host_map = self.config.get("lingua_extractor", {}).get("host_map", {})
616
718
  if hostname in host_map:
617
719
  map_ = host_map[hostname]
618
720
  if "netloc" in map_:
619
- url_split = url_split._replace(netloc=map_["netloc"])
721
+ url.netloc = map_["netloc"]
620
722
  if "scheme" in map_:
621
- url_split = url_split._replace(scheme=map_["scheme"])
723
+ url.scheme = map_["scheme"]
622
724
  kwargs = {"verify": map_["verify"]} if "verify" in map_ else {}
623
- return url_split.geturl(), map_.get("headers", {}), kwargs
725
+ return url, map_.get("headers", {}), kwargs
624
726
  return url, {}, {}
625
727
 
626
- def _layer_attributes(self, url, layer):
728
+ def _layer_attributes(self, url: str, layer: str) -> Tuple[List[str], List[str]]:
627
729
  errors: Set[str] = set()
628
730
 
629
- request = _Request()
630
- request.registry.settings = self.config
731
+ request = pyramid.threadlocal.get_current_request()
732
+ if request is None:
733
+ request = _Request()
734
+ request.registry.settings = self.config
735
+
631
736
  # Static schema will not be supported
632
- url = get_url2("Layer", url, request, errors)
737
+ url_obj_ = get_url2("Layer", url, request, errors)
633
738
  if errors:
634
739
  print("\n".join(errors))
635
740
  return [], []
636
- url, headers, kwargs = self._build_url(url)
637
-
638
- if url not in self.wmscap_cache:
639
- print("Get WMS GetCapabilities for URL: {}".format(url))
640
- self.wmscap_cache[url] = None
641
-
642
- wms_getcap_url = add_url_params(
643
- url,
644
- {
645
- "SERVICE": "WMS",
646
- "VERSION": "1.1.1",
647
- "REQUEST": "GetCapabilities",
648
- "ROLE_IDS": "0",
649
- "USER_ID": "0",
650
- },
741
+ if not url_obj_:
742
+ print(f"No URL for: {url}")
743
+ return [], []
744
+ url_obj: Url = url_obj_
745
+ url_obj, headers, kwargs = self._build_url(url_obj)
746
+
747
+ if url not in self.wms_capabilities_cache:
748
+ print(f"Get WMS GetCapabilities for URL: {url_obj}")
749
+ self.wms_capabilities_cache[url] = None
750
+
751
+ wms_getcap_url = (
752
+ url_obj.clone()
753
+ .add_query(
754
+ {
755
+ "SERVICE": "WMS",
756
+ "VERSION": "1.1.1",
757
+ "REQUEST": "GetCapabilities",
758
+ "ROLE_IDS": "0",
759
+ "USER_ID": "0",
760
+ }
761
+ )
762
+ .url()
651
763
  )
652
764
  try:
653
- print(
654
- "Get WMS GetCapabilities for URL {},\nwith headers: {}".format(
655
- wms_getcap_url,
656
- " ".join(
657
- [
658
- "{}={}".format(h, v if h not in ("Authorization", "Cookies") else "***")
659
- for h, v in headers.items()
660
- ]
661
- ),
662
- )
765
+ rendered_headers = " ".join(
766
+ [
767
+ f"{h}={v if h not in ('Authorization', 'Cookies') else '***'}"
768
+ for h, v in headers.items()
769
+ ]
663
770
  )
771
+ print(f"Get WMS GetCapabilities for URL {wms_getcap_url},\nwith headers: {rendered_headers}")
664
772
  response = requests.get(wms_getcap_url, headers=headers, **kwargs)
665
773
 
666
- try:
667
- self.wmscap_cache[url] = WebMapService(None, xml=response.content)
668
- except Exception as e:
774
+ if response.ok:
775
+ try:
776
+ self.wms_capabilities_cache[url] = WebMapService(None, xml=response.content)
777
+ except Exception as e:
778
+ print(
779
+ colorize(
780
+ "ERROR! an error occurred while trying to parse "
781
+ "the GetCapabilities document.",
782
+ Color.RED,
783
+ )
784
+ )
785
+ print(colorize(str(e), Color.RED))
786
+ print(f"URL: {wms_getcap_url}\nxml:\n{response.text}")
787
+ if _get_config_str("IGNORE_I18N_ERRORS", "FALSE") != "TRUE":
788
+ raise
789
+ else:
669
790
  print(
670
791
  colorize(
671
- "ERROR! an error occurred while trying to " "parse the GetCapabilities document.",
672
- RED,
792
+ f"ERROR! Unable to GetCapabilities from URL: {wms_getcap_url},\n"
793
+ f"with headers: {rendered_headers}",
794
+ Color.RED,
673
795
  )
674
796
  )
675
- print(colorize(str(e), RED))
676
- print("URL: {}\nxml:\n{}".format(wms_getcap_url, response.text))
677
- if os.environ.get("IGNORE_I18N_ERRORS", "FALSE") != "TRUE":
678
- raise
679
- except Exception as e: # pragma: no cover
680
- print(colorize(str(e), RED))
797
+ print(f"Response: {response.status_code} {response.reason}\n{response.text}")
798
+ if _get_config_str("IGNORE_I18N_ERRORS", "FALSE") != "TRUE":
799
+ raise LinguaExtractorException(response.reason)
800
+ except Exception as e:
801
+ print(colorize(str(e), Color.RED))
802
+ rendered_headers = " ".join(
803
+ [
804
+ f"{h}={v if h not in ('Authorization', 'Cookies') else '***'}"
805
+ for h, v in headers.items()
806
+ ]
807
+ )
681
808
  print(
682
809
  colorize(
683
- "ERROR! Unable to GetCapabilities from URL: {},\nwith headers: {}".format(
684
- wms_getcap_url,
685
- " ".join(
686
- [
687
- "{}={}".format(h, v if h not in ("Authorization", "Cookies") else "***")
688
- for h, v in headers.items()
689
- ]
690
- ),
691
- ),
692
- RED,
810
+ f"ERROR! Unable to GetCapabilities from URL: {wms_getcap_url},\n"
811
+ f"with headers: {rendered_headers}",
812
+ Color.RED,
693
813
  )
694
814
  )
695
- if os.environ.get("IGNORE_I18N_ERRORS", "FALSE") != "TRUE":
815
+ if _get_config_str("IGNORE_I18N_ERRORS", "FALSE") != "TRUE":
696
816
  raise
697
817
 
698
- wmscap = self.wmscap_cache[url]
818
+ wms_capabilities = self.wms_capabilities_cache[url]
699
819
 
700
820
  if url not in self.featuretype_cache:
701
- print("Get WFS DescribeFeatureType for URL: {}".format(url))
821
+ print(f"Get WFS DescribeFeatureType for URL: {url_obj}")
702
822
  self.featuretype_cache[url] = None
703
823
 
704
- wfs_descrfeat_url = add_url_params(
705
- url,
706
- {
707
- "SERVICE": "WFS",
708
- "VERSION": "1.1.0",
709
- "REQUEST": "DescribeFeatureType",
710
- "ROLE_IDS": "0",
711
- "USER_ID": "0",
712
- },
824
+ wfs_describe_feature_url = (
825
+ url_obj.clone()
826
+ .add_query(
827
+ {
828
+ "SERVICE": "WFS",
829
+ "VERSION": "1.1.0",
830
+ "REQUEST": "DescribeFeatureType",
831
+ "ROLE_IDS": "0",
832
+ "USER_ID": "0",
833
+ }
834
+ )
835
+ .url()
713
836
  )
714
837
  try:
715
- response = requests.get(wfs_descrfeat_url, headers=headers, **kwargs)
716
- except Exception as e: # pragma: no cover
717
- print(colorize(str(e), RED))
838
+ response = requests.get(wfs_describe_feature_url, headers=headers, **kwargs)
839
+ except Exception as e:
840
+ print(colorize(str(e), Color.RED))
718
841
  print(
719
842
  colorize(
720
- "ERROR! Unable to DescribeFeatureType from URL: {}".format(wfs_descrfeat_url), RED
843
+ f"ERROR! Unable to DescribeFeatureType from URL: {wfs_describe_feature_url}",
844
+ Color.RED,
721
845
  )
722
846
  )
723
- if os.environ.get("IGNORE_I18N_ERRORS", "FALSE") == "TRUE":
847
+ if _get_config_str("IGNORE_I18N_ERRORS", "FALSE") == "TRUE":
724
848
  return [], []
725
849
  raise
726
850
 
727
- if not response.ok: # pragma: no cover
851
+ if not response.ok:
728
852
  print(
729
853
  colorize(
730
- "ERROR! DescribeFeatureType from URL {} return the error: {:d} {}".format(
731
- wfs_descrfeat_url, response.status_code, response.reason
732
- ),
733
- RED,
854
+ f"ERROR! DescribeFeatureType from URL {wfs_describe_feature_url} return the error: "
855
+ f"{response.status_code:d} {response.reason}",
856
+ Color.RED,
734
857
  )
735
858
  )
736
- if os.environ.get("IGNORE_I18N_ERRORS", "FALSE") == "TRUE":
859
+ if _get_config_str("IGNORE_I18N_ERRORS", "FALSE") == "TRUE":
737
860
  return [], []
738
- raise Exception("Aborted")
861
+ raise Exception("Aborted") # pylint: disable=broad-exception-raised
739
862
 
740
863
  try:
741
864
  describe = parseString(response.text)
@@ -748,13 +871,13 @@ class GeomapfishThemeExtractor(Extractor): # pragma: no cover
748
871
  except ExpatError as e:
749
872
  print(
750
873
  colorize(
751
- "ERROR! an error occurred while trying to " "parse the DescribeFeatureType document.",
752
- RED,
874
+ "ERROR! an error occurred while trying to parse the DescribeFeatureType document.",
875
+ Color.RED,
753
876
  )
754
877
  )
755
- print(colorize(str(e), RED))
756
- print("URL: {}\nxml:\n{}".format(wfs_descrfeat_url, response.text))
757
- if os.environ.get("IGNORE_I18N_ERRORS", "FALSE") == "TRUE":
878
+ print(colorize(str(e), Color.RED))
879
+ print(f"URL: {wfs_describe_feature_url}\nxml:\n{response.text}")
880
+ if _get_config_str("IGNORE_I18N_ERRORS", "FALSE") == "TRUE":
758
881
  return [], []
759
882
  raise
760
883
  except AttributeError:
@@ -762,11 +885,11 @@ class GeomapfishThemeExtractor(Extractor): # pragma: no cover
762
885
  colorize(
763
886
  "ERROR! an error occurred while trying to "
764
887
  "read the Mapfile and recover the themes.",
765
- RED,
888
+ Color.RED,
766
889
  )
767
890
  )
768
- print("URL: {}\nxml:\n{}".format(wfs_descrfeat_url, response.text))
769
- if os.environ.get("IGNORE_I18N_ERRORS", "FALSE") == "TRUE":
891
+ print(f"URL: {wfs_describe_feature_url}\nxml:\n{response.text}")
892
+ if _get_config_str("IGNORE_I18N_ERRORS", "FALSE") == "TRUE":
770
893
  return [], []
771
894
  raise
772
895
  else:
@@ -775,16 +898,16 @@ class GeomapfishThemeExtractor(Extractor): # pragma: no cover
775
898
  if featurestype is None:
776
899
  return [], []
777
900
 
778
- layers = [layer]
779
- if wmscap is not None and layer in list(wmscap.contents):
780
- layer_obj = wmscap[layer]
901
+ layers: List[str] = [layer]
902
+ if wms_capabilities is not None and layer in list(wms_capabilities.contents):
903
+ layer_obj = wms_capabilities[layer]
781
904
  if layer_obj.layers:
782
905
  layers = [layer.name for layer in layer_obj.layers]
783
906
 
784
- attributes = []
907
+ attributes: List[str] = []
785
908
  for sub_layer in layers:
786
909
  # Should probably be adapted for other king of servers
787
- type_element = featurestype.get("{}Type".format(sub_layer))
910
+ type_element = featurestype.get(f"{sub_layer}Type")
788
911
  if type_element is not None:
789
912
  for element in type_element.getElementsByTagNameNS(
790
913
  "http://www.w3.org/2001/XMLSchema", "element"