c2cgeoportal-geoportal 2.6.0__py2.py3-none-any.whl → 2.7.1.83__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 (212) hide show
  1. c2cgeoportal_geoportal/__init__.py +224 -84
  2. c2cgeoportal_geoportal/lib/__init__.py +64 -42
  3. c2cgeoportal_geoportal/lib/authentication.py +50 -22
  4. c2cgeoportal_geoportal/lib/bashcolor.py +17 -13
  5. c2cgeoportal_geoportal/lib/cacheversion.py +16 -8
  6. c2cgeoportal_geoportal/lib/caching.py +61 -191
  7. c2cgeoportal_geoportal/lib/check_collector.py +17 -10
  8. c2cgeoportal_geoportal/lib/checker.py +61 -63
  9. c2cgeoportal_geoportal/lib/common_headers.py +170 -0
  10. c2cgeoportal_geoportal/lib/dbreflection.py +54 -39
  11. c2cgeoportal_geoportal/lib/filter_capabilities.py +119 -87
  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 +361 -237
  18. c2cgeoportal_geoportal/lib/loader.py +10 -15
  19. c2cgeoportal_geoportal/lib/metrics.py +28 -17
  20. c2cgeoportal_geoportal/lib/oauth2.py +214 -145
  21. c2cgeoportal_geoportal/lib/wmstparsing.py +115 -90
  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} +18 -9
  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 +104 -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/advance_create/{{cookiecutter.project}}/geoportal/requirements.txt +2 -0
  36. c2cgeoportal_geoportal/scaffolds/{create/geoportal/setup.py_tmpl → advance_create/{{cookiecutter.project}}/geoportal/setup.py} +6 -7
  37. c2cgeoportal_geoportal/scaffolds/{create → advance_create/{{cookiecutter.project}}}/geoportal/tools/extract-messages.js +8 -6
  38. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/tsconfig.json +8 -0
  39. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/webpack.api.js +75 -0
  40. c2cgeoportal_geoportal/scaffolds/{create/geoportal/webpack.apps.js_tmpl → advance_create/{{cookiecutter.project}}/geoportal/webpack.apps.js} +31 -28
  41. c2cgeoportal_geoportal/scaffolds/{create → advance_create/{{cookiecutter.project}}}/geoportal/webpack.commons.js +3 -7
  42. c2cgeoportal_geoportal/scaffolds/{create → advance_create/{{cookiecutter.project}}}/geoportal/webpack.config.js +1 -1
  43. c2cgeoportal_geoportal/scaffolds/{create/geoportal/+package+_geoportal/__init__.py_tmpl → advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/__init__.py} +11 -22
  44. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/authentication.py +10 -0
  45. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/dev.py +14 -0
  46. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/models.py +8 -0
  47. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/multi_organization.py +7 -0
  48. c2cgeoportal_geoportal/scaffolds/{create/geoportal/+package+_geoportal → advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/resources.py +4 -3
  49. 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
  50. 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
  51. c2cgeoportal_geoportal/scaffolds/{create/geoportal/+package+_geoportal/subscribers.py_tmpl → advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/subscribers.py} +1 -3
  52. c2cgeoportal_geoportal/scaffolds/advance_update/cookiecutter.json +18 -0
  53. c2cgeoportal_geoportal/scaffolds/{update/geoportal/CONST_Makefile_tmpl → advance_update/{{cookiecutter.project}}/geoportal/CONST_Makefile} +3 -7
  54. c2cgeoportal_geoportal/scaffolds/create/cookiecutter.json +18 -0
  55. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.dockerignore +14 -0
  56. c2cgeoportal_geoportal/scaffolds/create/{+dot+editorconfig → {{cookiecutter.project}}/.editorconfig} +2 -5
  57. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.github/workflows/main.yaml +43 -0
  58. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.github/workflows/rebuild.yaml +46 -0
  59. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.github/workflows/update_l10n.yaml +65 -0
  60. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.gitignore +16 -0
  61. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.prettierignore +1 -0
  62. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.prettierrc.yaml +2 -0
  63. c2cgeoportal_geoportal/scaffolds/create/{Dockerfile_tmpl → {{cookiecutter.project}}/Dockerfile} +20 -11
  64. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/Makefile +14 -0
  65. c2cgeoportal_geoportal/scaffolds/create/{README.rst_tmpl → {{cookiecutter.project}}/README.rst} +4 -4
  66. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/build +158 -0
  67. c2cgeoportal_geoportal/scaffolds/create/{ci/config.yaml_tmpl → {{cookiecutter.project}}/ci/config.yaml} +7 -5
  68. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/ci/requirements.txt +1 -0
  69. c2cgeoportal_geoportal/scaffolds/create/{docker-compose-lib.yaml → {{cookiecutter.project}}/docker-compose-lib.yaml} +133 -17
  70. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/docker-compose.override.sample.yaml +66 -0
  71. c2cgeoportal_geoportal/scaffolds/create/{docker-compose.yaml → {{cookiecutter.project}}/docker-compose.yaml} +17 -12
  72. c2cgeoportal_geoportal/scaffolds/create/{env.default_tmpl → {{cookiecutter.project}}/env.default} +29 -14
  73. c2cgeoportal_geoportal/scaffolds/create/{env.project_tmpl → {{cookiecutter.project}}/env.project} +16 -4
  74. c2cgeoportal_geoportal/scaffolds/create/{geoportal/vars.yaml_tmpl → {{cookiecutter.project}}/geoportal/vars.yaml} +93 -27
  75. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static/css/mobile.css +0 -0
  76. c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/data/Readme.txt +1 -1
  77. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/mapserver/demo.map.tmpl +224 -0
  78. c2cgeoportal_geoportal/scaffolds/create/{mapserver/mapserver.map.tmpl_tmpl → {{cookiecutter.project}}/mapserver/mapserver.map.tmpl} +7 -15
  79. c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/A3_Landscape.jrxml +8 -8
  80. c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/A3_Portrait.jrxml +8 -8
  81. c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/A4_Landscape.jrxml +8 -8
  82. c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/A4_Portrait.jrxml +8 -8
  83. c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/config.yaml.tmpl +5 -4
  84. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}/localisation.properties +4 -0
  85. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}/localisation_fr.properties +4 -0
  86. c2cgeoportal_geoportal/scaffolds/create/{project.yaml_tmpl → {{cookiecutter.project}}/project.yaml} +6 -6
  87. c2cgeoportal_geoportal/scaffolds/create/{qgisserver/pg_service.conf.tmpl_tmpl → {{cookiecutter.project}}/qgisserver/pg_service.conf.tmpl} +2 -2
  88. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/scripts/db-backup +107 -0
  89. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/scripts/db-restore +111 -0
  90. c2cgeoportal_geoportal/scaffolds/create/{setup.cfg_tmpl → {{cookiecutter.project}}/setup.cfg} +1 -1
  91. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/spell-ignore-words.txt +3 -0
  92. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/tilegeneration/config.yaml.tmpl +195 -0
  93. c2cgeoportal_geoportal/scaffolds/update/cookiecutter.json +18 -0
  94. c2cgeoportal_geoportal/scaffolds/update/{+dot+upgrade.yaml_tmpl → {{cookiecutter.project}}/.upgrade.yaml} +49 -39
  95. c2cgeoportal_geoportal/scaffolds/update/{{cookiecutter.project}}/CONST_CHANGELOG.txt +1153 -0
  96. c2cgeoportal_geoportal/scaffolds/update/{geoportal → {{cookiecutter.project}}/geoportal}/CONST_config-schema.yaml +47 -2
  97. c2cgeoportal_geoportal/scaffolds/update/{geoportal/CONST_vars.yaml_tmpl → {{cookiecutter.project}}/geoportal/CONST_vars.yaml} +350 -15
  98. c2cgeoportal_geoportal/scripts/__init__.py +15 -31
  99. c2cgeoportal_geoportal/scripts/c2cupgrade.py +271 -232
  100. c2cgeoportal_geoportal/scripts/create_demo_theme.py +3 -6
  101. c2cgeoportal_geoportal/scripts/manage_users.py +34 -39
  102. c2cgeoportal_geoportal/scripts/pcreate.py +312 -0
  103. c2cgeoportal_geoportal/scripts/theme2fts.py +72 -23
  104. c2cgeoportal_geoportal/scripts/urllogin.py +17 -9
  105. c2cgeoportal_geoportal/templates/login.html +88 -84
  106. c2cgeoportal_geoportal/templates/notlogin.html +59 -59
  107. c2cgeoportal_geoportal/templates/testi18n.html +6 -8
  108. c2cgeoportal_geoportal/views/__init__.py +23 -4
  109. c2cgeoportal_geoportal/views/dev.py +9 -7
  110. c2cgeoportal_geoportal/views/dynamic.py +54 -18
  111. c2cgeoportal_geoportal/views/entry.py +93 -24
  112. c2cgeoportal_geoportal/views/fulltextsearch.py +28 -22
  113. c2cgeoportal_geoportal/views/geometry_processing.py +15 -7
  114. c2cgeoportal_geoportal/views/i18n.py +91 -9
  115. c2cgeoportal_geoportal/views/layers.py +160 -126
  116. c2cgeoportal_geoportal/views/login.py +106 -93
  117. c2cgeoportal_geoportal/views/mapserverproxy.py +45 -28
  118. c2cgeoportal_geoportal/views/memory.py +12 -12
  119. c2cgeoportal_geoportal/views/ogcproxy.py +48 -30
  120. c2cgeoportal_geoportal/views/pdfreport.py +26 -22
  121. c2cgeoportal_geoportal/views/printproxy.py +56 -50
  122. c2cgeoportal_geoportal/views/profile.py +24 -23
  123. c2cgeoportal_geoportal/views/proxy.py +84 -67
  124. c2cgeoportal_geoportal/views/raster.py +35 -24
  125. c2cgeoportal_geoportal/views/resourceproxy.py +13 -11
  126. c2cgeoportal_geoportal/views/shortener.py +27 -24
  127. c2cgeoportal_geoportal/views/theme.py +427 -321
  128. c2cgeoportal_geoportal/views/tinyowsproxy.py +46 -39
  129. c2cgeoportal_geoportal/views/vector_tiles.py +80 -0
  130. {c2cgeoportal_geoportal-2.6.0.dist-info → c2cgeoportal_geoportal-2.7.1.83.dist-info}/METADATA +24 -20
  131. c2cgeoportal_geoportal-2.7.1.83.dist-info/RECORD +185 -0
  132. {c2cgeoportal_geoportal-2.6.0.dist-info → c2cgeoportal_geoportal-2.7.1.83.dist-info}/WHEEL +1 -1
  133. {c2cgeoportal_geoportal-2.6.0.dist-info → c2cgeoportal_geoportal-2.7.1.83.dist-info}/entry_points.txt +3 -1
  134. tests/__init__.py +7 -3
  135. tests/test_cachebuster.py +0 -2
  136. tests/test_caching.py +17 -25
  137. tests/test_checker.py +0 -2
  138. tests/test_decimaljson.py +4 -4
  139. tests/test_headerstween.py +0 -2
  140. tests/test_i18n.py +1 -1
  141. tests/test_init.py +4 -7
  142. tests/test_locale_negociator.py +0 -2
  143. tests/test_mapserverproxy_route_predicate.py +0 -2
  144. tests/test_raster.py +0 -2
  145. tests/test_wmstparsing.py +0 -2
  146. c2cgeoportal_geoportal/scaffolds/__init__.py +0 -227
  147. c2cgeoportal_geoportal/scaffolds/create/+dot+dockerignore_tmpl +0 -12
  148. c2cgeoportal_geoportal/scaffolds/create/+dot+github/workflows/main.yaml_tmpl +0 -89
  149. c2cgeoportal_geoportal/scaffolds/create/+dot+github/workflows/rebuild.yaml_tmpl +0 -78
  150. c2cgeoportal_geoportal/scaffolds/create/+dot+gitignore_tmpl +0 -16
  151. c2cgeoportal_geoportal/scaffolds/create/Makefile +0 -3
  152. c2cgeoportal_geoportal/scaffolds/create/build_tmpl +0 -167
  153. c2cgeoportal_geoportal/scaffolds/create/ci/requirements.txt +0 -1
  154. c2cgeoportal_geoportal/scaffolds/create/ci/trigger +0 -68
  155. c2cgeoportal_geoportal/scaffolds/create/docker-compose.override.sample.yaml +0 -54
  156. c2cgeoportal_geoportal/scaffolds/create/geoportal/+dot+dockerignore_tmpl +0 -6
  157. c2cgeoportal_geoportal/scaffolds/create/geoportal/+dot+eslintrc_tmpl +0 -15
  158. c2cgeoportal_geoportal/scaffolds/create/geoportal/+package+_geoportal/models.py_tmpl +0 -10
  159. c2cgeoportal_geoportal/scaffolds/create/geoportal/+package+_geoportal/static/robot.txt +0 -3
  160. c2cgeoportal_geoportal/scaffolds/create/geoportal/production.ini_tmpl +0 -106
  161. c2cgeoportal_geoportal/scaffolds/create/geoportal/requirements.txt +0 -2
  162. c2cgeoportal_geoportal/scaffolds/create/geoportal/tsconfig.json_tmpl +0 -9
  163. c2cgeoportal_geoportal/scaffolds/create/geoportal/webpack.api.js_tmpl +0 -72
  164. c2cgeoportal_geoportal/scaffolds/create/mapserver/demo.map.tmpl_tmpl +0 -262
  165. c2cgeoportal_geoportal/scaffolds/create/mapserver/tinyows.xml +0 -36
  166. c2cgeoportal_geoportal/scaffolds/create/print/print-apps/+package+/config.yaml +0 -168
  167. c2cgeoportal_geoportal/scaffolds/create/qgisserver/geomapfish.yaml.tmpl_tmpl +0 -16
  168. c2cgeoportal_geoportal/scaffolds/create/spell-ignore-words.txt +0 -1
  169. c2cgeoportal_geoportal/scaffolds/create/tilegeneration/config.yaml.tmpl_tmpl +0 -185
  170. c2cgeoportal_geoportal/scaffolds/create/yamllint.yaml +0 -11
  171. c2cgeoportal_geoportal/scaffolds/update/CONST_CHANGELOG.txt_tmpl +0 -454
  172. c2cgeoportal_geoportal/templates/dynamic.js +0 -21
  173. c2cgeoportal_geoportal-2.6.0.dist-info/RECORD +0 -173
  174. /c2cgeoportal_geoportal/{scaffolds/create/geoportal/+package+_geoportal/static/css/desktop.css → py.typed} +0 -0
  175. /c2cgeoportal_geoportal/scaffolds/{create/geoportal/Makefile_tmpl → advance_create/{{cookiecutter.project}}/geoportal/Makefile} +0 -0
  176. /c2cgeoportal_geoportal/scaffolds/{create → advance_create/{{cookiecutter.project}}}/geoportal/alembic.ini +0 -0
  177. /c2cgeoportal_geoportal/scaffolds/{create → advance_create/{{cookiecutter.project}}}/geoportal/language_mapping +0 -0
  178. /c2cgeoportal_geoportal/scaffolds/{create → advance_create/{{cookiecutter.project}}}/geoportal/lingua-server.cfg +0 -0
  179. /c2cgeoportal_geoportal/scaffolds/{create/geoportal/+package+_geoportal → advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/views/__init__.py +0 -0
  180. /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
  181. /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal/static/css/iframe_api.css → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static/css/desktop.css} +0 -0
  182. /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal/static/css/mobile.css → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static/css/iframe_api.css} +0 -0
  183. /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/static/images/banner_left.png +0 -0
  184. /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/static/images/banner_right.png +0 -0
  185. /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/static/images/blank.png +0 -0
  186. /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/static/images/markers/marker-blue.png +0 -0
  187. /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/static/images/markers/marker-gold.png +0 -0
  188. /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/static/images/markers/marker-green.png +0 -0
  189. /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/static/images/markers/marker.png +0 -0
  190. /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/static/robot.txt.tmpl +0 -0
  191. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/data/TM_EUROPE_BORDERS-0.3.sql +0 -0
  192. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Arial.ttf +0 -0
  193. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Arialbd.ttf +0 -0
  194. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Arialbi.ttf +0 -0
  195. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Ariali.ttf +0 -0
  196. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/NotoSans-Bold.ttf +0 -0
  197. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/NotoSans-BoldItalic.ttf +0 -0
  198. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/NotoSans-Italic.ttf +0 -0
  199. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/NotoSans-Regular.ttf +0 -0
  200. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Verdana.ttf +0 -0
  201. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Verdanab.ttf +0 -0
  202. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Verdanai.ttf +0 -0
  203. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Verdanaz.ttf +0 -0
  204. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts.conf +0 -0
  205. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/tinyows.xml.tmpl +0 -0
  206. /c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/legend.jrxml +0 -0
  207. /c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/logo.png +0 -0
  208. /c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/north.svg +0 -0
  209. /c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/results.jrxml +0 -0
  210. /c2cgeoportal_geoportal/scaffolds/create/{pyproject.toml → {{cookiecutter.project}}/pyproject.toml} +0 -0
  211. /c2cgeoportal_geoportal/scaffolds/create/{run_alembic.sh → {{cookiecutter.project}}/run_alembic.sh} +0 -0
  212. {c2cgeoportal_geoportal-2.6.0.dist-info → c2cgeoportal_geoportal-2.7.1.83.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,81 @@ 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
+ def _get_config(key: str, default: Optional[str] = None) -> Optional[str]:
66
+ """
67
+ Return the config value for passed key.
68
+
69
+ Passed throw environment variable for the command line,
70
+ or throw the query string on HTTP request.
71
+ """
72
+ request = pyramid.threadlocal.get_current_request()
73
+ if request is not None:
74
+ return cast(Optional[str], request.params.get(key.lower(), default))
75
+
76
+ return os.environ.get(key, default)
77
+
78
+
79
+ def _get_config_str(key: str, default: str) -> str:
80
+ result = _get_config(key, default)
81
+ assert result is not None
82
+ return result
64
83
 
65
- class _Registry: # pragma: no cover
84
+
85
+ class _Registry:
66
86
  settings = None
67
87
 
68
- def __init__(self, settings):
88
+ def __init__(self, settings: Optional[Dict[str, Any]]) -> None:
69
89
  self.settings = settings
70
90
 
71
91
 
72
- class _Request: # pragma: no cover
92
+ class _Request:
73
93
  params: Dict[str, str] = {}
74
94
  matchdict: Dict[str, str] = {}
75
95
  GET: Dict[str, str] = {}
76
96
 
77
- def __init__(self, settings=None):
97
+ def __init__(self, settings: Optional[Dict[str, Any]] = None) -> None:
78
98
  self.registry: _Registry = _Registry(settings)
79
99
 
80
100
  @staticmethod
81
- def static_url(*args, **kwargs):
101
+ def static_url(*args: Any, **kwargs: Any) -> str:
82
102
  del args
83
103
  del kwargs
84
104
  return ""
85
105
 
86
106
  @staticmethod
87
- def static_path(*args, **kwargs):
107
+ def static_path(*args: Any, **kwargs: Any) -> str:
88
108
  del args
89
109
  del kwargs
90
110
  return ""
91
111
 
92
112
  @staticmethod
93
- def route_url(*args, **kwargs):
113
+ def route_url(*args: Any, **kwargs: Any) -> str:
94
114
  del args
95
115
  del kwargs
96
116
  return ""
97
117
 
98
118
  @staticmethod
99
- def current_route_url(*args, **kwargs):
119
+ def current_route_url(*args: Any, **kwargs: Any) -> str:
100
120
  del args
101
121
  del kwargs
102
122
  return ""
103
123
 
104
124
 
105
- class GeomapfishAngularExtractor(Extractor): # pragma: no cover
106
- """
107
- GeoMapFish angular extractor
108
- """
125
+ class GeomapfishAngularExtractor(Extractor): # type: ignore
126
+ """GeoMapFish angular extractor."""
109
127
 
110
128
  extensions = [".js", ".html"]
111
129
 
@@ -118,27 +136,49 @@ class GeomapfishAngularExtractor(Extractor): # pragma: no cover
118
136
  self.config = None
119
137
  self.tpl = None
120
138
 
121
- def __call__(self, filename, options, fileobj=None, lineno=0):
139
+ @staticmethod
140
+ def get_message_cleaner(filename: str) -> Callable[[str], str]:
141
+ """Return a function for cleaning messages according to input file format."""
142
+ ext = os.path.splitext(filename)[1]
143
+
144
+ if ext in [".html", ".ejs"]:
145
+ # Remove \n in HTML multi-line strings
146
+ pattern = re.compile("\n *")
147
+ return lambda s: re.sub(pattern, " ", s)
148
+
149
+ return lambda s: s
150
+
151
+ def __call__(
152
+ self,
153
+ filename: str,
154
+ options: Dict[str, Any],
155
+ fileobj: Optional[Dict[str, Any]] = None,
156
+ lineno: int = 0,
157
+ ) -> List[Message]:
122
158
  del fileobj, lineno
123
159
 
160
+ print(f"Running {self.__class__.__name__} on {filename}")
161
+
162
+ cleaner = self.get_message_cleaner(filename)
163
+
124
164
  init_region({"backend": "dogpile.cache.memory"}, "std")
125
165
  init_region({"backend": "dogpile.cache.memory"}, "obj")
126
166
 
127
167
  int_filename = filename
128
- if re.match("^" + re.escape("./{}/templates".format(self.config["package"])), filename):
168
+ if re.match("^" + re.escape(f"./{self.config['package']}/templates"), filename):
129
169
  try:
130
170
  empty_template = Template("") # nosec
131
171
 
132
- class Lookup(TemplateLookup):
172
+ class Lookup(TemplateLookup): # type: ignore
133
173
  @staticmethod
134
- def get_template(uri):
174
+ def get_template(uri: str) -> Template:
135
175
  del uri # unused
136
176
  return empty_template
137
177
 
138
- class MyTemplate(MakoTemplate):
178
+ class MyTemplate(MakoTemplate): # type: ignore
139
179
  tpl = None
140
180
 
141
- def prepare(self, **kwargs):
181
+ def prepare(self, **kwargs: Any) -> None:
142
182
  options.update({"input_encoding": self.encoding})
143
183
  lookup = Lookup(**kwargs)
144
184
  if self.source:
@@ -149,10 +189,12 @@ class GeomapfishAngularExtractor(Extractor): # pragma: no cover
149
189
  )
150
190
 
151
191
  try:
192
+ request = pyramid.threadlocal.get_current_request()
193
+ request = _Request() if request is None else request
152
194
  processed = template(
153
195
  filename,
154
196
  {
155
- "request": _Request(self.config),
197
+ "request": request,
156
198
  "lang": "fr",
157
199
  "debug": False,
158
200
  "extra_params": {},
@@ -167,11 +209,9 @@ class GeomapfishAngularExtractor(Extractor): # pragma: no cover
167
209
  with open(int_filename, "wb") as file_open:
168
210
  file_open.write(processed.encode("utf-8"))
169
211
  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":
212
+ print(colorize(f"ERROR! Occurred during the '{filename}' template generation", Color.RED))
213
+ print(colorize(traceback.format_exc(), Color.RED))
214
+ if _get_config_str("IGNORE_I18N_ERRORS", "FALSE") == "TRUE":
175
215
  # Continue with the original one
176
216
  int_filename = filename
177
217
  else:
@@ -179,38 +219,85 @@ class GeomapfishAngularExtractor(Extractor): # pragma: no cover
179
219
  except Exception:
180
220
  print(traceback.format_exc())
181
221
 
182
- message_str = subprocess.check_output(
183
- ["node", "geoportal/tools/extract-messages.js", int_filename]
184
- ).decode("utf-8")
222
+ # Path in geomapfish-tools
223
+ script_path = "geoportal/tools/extract-messages.js"
224
+ if not os.path.isfile(script_path):
225
+ # Path in geomapfish runner
226
+ script_path = "/app/tools/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 wen environment it should be OK, with the command line we should get
250
+ an exception ind initialise 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
+
261
+ # Init db sessions
262
+
263
+ class R:
264
+ settings: Dict[str, Any] = {}
265
+
266
+ class C:
267
+ registry = R()
268
+
269
+ def get_settings(self) -> Dict[str, Any]:
270
+ return self.registry.settings
271
+
272
+ def add_tween(self, *args: Any, **kwargs: Any) -> None:
273
+ pass
274
+
275
+ config_ = C()
276
+ config_.registry.settings = settings
277
+
278
+ c2cgeoportal_geoportal.init_db_sessions(settings, config_)
279
+
280
+
281
+ class GeomapfishConfigExtractor(Extractor): # type: ignore
282
+ """GeoMapFish config extractor (raster layers, and print templates)."""
283
+
206
284
  extensions = [".yaml", ".tmpl"]
207
285
 
208
- def __call__(self, filename, options, fileobj=None, lineno=0):
209
- del fileobj, lineno
286
+ def __call__(
287
+ self,
288
+ filename: str,
289
+ options: Dict[str, Any],
290
+ fileobj: Optional[Dict[str, Any]] = None,
291
+ lineno: int = 0,
292
+ ) -> List[Message]:
293
+ del options, fileobj, lineno
294
+
295
+ print(f"Running {self.__class__.__name__} on {filename}")
296
+
210
297
  init_region({"backend": "dogpile.cache.memory"}, "std")
211
298
  init_region({"backend": "dogpile.cache.memory"}, "obj")
212
299
 
213
- with open(filename) as config_file:
300
+ with open(filename, encoding="utf8") as config_file:
214
301
  gmf_config = yaml.load(config_file, Loader=yaml.BaseLoader) # nosec
215
302
  # For application config (config.yaml)
216
303
  if "vars" in gmf_config:
@@ -220,7 +307,7 @@ class GeomapfishConfigExtractor(Extractor): # pragma: no cover
220
307
  return self._collect_print_config(gmf_config, filename)
221
308
  raise Exception("Not a known config file")
222
309
 
223
- def _collect_app_config(self, filename):
310
+ def _collect_app_config(self, filename: str) -> List[Message]:
224
311
  config.init(filename)
225
312
  settings = config.get_config()
226
313
  assert not [
@@ -228,32 +315,18 @@ class GeomapfishConfigExtractor(Extractor): # pragma: no cover
228
315
  ]
229
316
  # Collect raster layers names
230
317
  raster = [
231
- Message(None, raster_layer, None, [], "", "", (filename, "raster/{}".format(raster_layer)))
318
+ Message(None, raster_layer, None, [], "", "", (filename, f"raster/{raster_layer}"))
232
319
  for raster_layer in list(settings.get("raster", {}).keys())
233
320
  ]
234
321
 
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_)
322
+ init_db(settings)
253
323
 
254
324
  # Collect layers enum values (for filters)
255
325
 
256
- from c2cgeoportal_commons.models import DBSessions # pylint: disable=import-outside-toplevel
326
+ from c2cgeoportal_commons.models import ( # pylint: disable=import-outside-toplevel
327
+ DBSession,
328
+ DBSessions,
329
+ )
257
330
  from c2cgeoportal_commons.models.main import Metadata # pylint: disable=import-outside-toplevel
258
331
 
259
332
  enums = []
@@ -262,14 +335,13 @@ class GeomapfishConfigExtractor(Extractor): # pragma: no cover
262
335
  layerinfos = enum_layers.get(layername, {})
263
336
  attributes = layerinfos.get("attributes", {})
264
337
  for fieldname in list(attributes.keys()):
265
- values = self._enumerate_attributes_values(DBSessions, Layers, layerinfos, fieldname)
338
+ values = self._enumerate_attributes_values(DBSessions, layerinfos, fieldname)
266
339
  for (value,) in values:
267
340
  if isinstance(value, str) and value != "":
268
341
  msgid = value
269
- location = "/layers/{}/values/{}/{}".format(
270
- layername,
271
- fieldname,
272
- value.encode("ascii", errors="replace").decode("ascii"),
342
+ location = (
343
+ f"/layers/{layername}/values/{fieldname}/"
344
+ f"{value.encode('ascii', errors='replace').decode('ascii')}"
273
345
  )
274
346
  assert msgid is not None
275
347
  enums.append(Message(None, msgid, None, [], "", "", (filename, location)))
@@ -279,14 +351,11 @@ class GeomapfishConfigExtractor(Extractor): # pragma: no cover
279
351
  names = [e["name"] for e in defs if e.get("translate", False)]
280
352
 
281
353
  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()
354
+ session = DBSession()
286
355
 
287
- query = session.query(Metadata).filter(Metadata.name.in_(names)) # pylint: disable=no-member
356
+ query = session.query(Metadata).filter(Metadata.name.in_(names))
288
357
  for metadata in query.all():
289
- location = "metadata/{}/{}".format(metadata.name, metadata.id)
358
+ location = f"metadata/{metadata.name}/{metadata.id}"
290
359
  assert metadata.value is not None
291
360
  metadata_list.append(Message(None, metadata.value, None, [], "", "", (filename, location)))
292
361
 
@@ -297,8 +366,8 @@ class GeomapfishConfigExtractor(Extractor): # pragma: no cover
297
366
  ):
298
367
  for a_index, action in enumerate(datasource.get("groupActions", [])):
299
368
  location = (
300
- "interfaces_config/{}/constants/gmfSearchOptions/datasources[{}]/"
301
- "groupActions[{}]/title".format(interface, ds_index, a_index)
369
+ f"interfaces_config/{interface}/constants/gmfSearchOptions/datasources[{ds_index}]/"
370
+ f"groupActions[{a_index}]/title"
302
371
  )
303
372
  assert action["title"] is not None
304
373
  interfaces_messages.append(
@@ -311,8 +380,9 @@ class GeomapfishConfigExtractor(Extractor): # pragma: no cover
311
380
  .get("mergeTabs", {})
312
381
  .keys()
313
382
  ):
314
- location = "interfaces_config/{}/constants/gmfDisplayQueryGridOptions/mergeTabs/{}/".format(
315
- interface, merge_tab
383
+ location = (
384
+ f"interfaces_config/{interface}/constants/gmfDisplayQueryGridOptions/"
385
+ f"mergeTabs/{merge_tab}/"
316
386
  )
317
387
  assert merge_tab is not None
318
388
  interfaces_messages.append(Message(None, merge_tab, None, [], "", "", (filename, location)))
@@ -320,35 +390,35 @@ class GeomapfishConfigExtractor(Extractor): # pragma: no cover
320
390
  return raster + enums + metadata_list + interfaces_messages
321
391
 
322
392
  @staticmethod
323
- def _enumerate_attributes_values(dbsessions, layers, layerinfos, fieldname):
393
+ def _enumerate_attributes_values(
394
+ dbsessions: Dict[str, Session], layerinfos: Dict[str, Any], fieldname: str
395
+ ) -> Set[Tuple[str, ...]]:
324
396
  dbname = layerinfos.get("dbsession", "dbsession")
325
- translate = layerinfos.get("attributes").get(fieldname, {}).get("translate", True)
397
+ translate = cast(Dict[str, Any], layerinfos["attributes"]).get(fieldname, {}).get("translate", True)
326
398
  if not translate:
327
- return []
399
+ return set()
328
400
  try:
329
401
  dbsession = dbsessions.get(dbname)
330
- return layers.query_enumerate_attribute_values(dbsession, layerinfos, fieldname)
402
+ return Layers.query_enumerate_attribute_values(dbsession, layerinfos, fieldname)
331
403
  except Exception as e:
332
- table = layerinfos.get("attributes").get(fieldname, {}).get("table")
404
+ table = cast(Dict[str, Any], layerinfos["attributes"]).get(fieldname, {}).get("table")
333
405
  print(
334
406
  colorize(
335
407
  "ERROR! Unable to collect enumerate attributes for "
336
- "db: {}, table: {}, column: {} ({})".format(dbname, table, fieldname, e),
337
- RED,
408
+ f"db: {dbname}, table: {table}, column: {fieldname} ({e!s})",
409
+ Color.RED,
338
410
  )
339
411
  )
340
- if os.environ.get("IGNORE_I18N_ERRORS", "FALSE") == "TRUE":
341
- return []
412
+ if _get_config_str("IGNORE_I18N_ERRORS", "FALSE") == "TRUE":
413
+ return set()
342
414
  raise
343
415
 
344
416
  @staticmethod
345
- def _collect_print_config(print_config, filename):
417
+ def _collect_print_config(print_config: Dict[str, Any], filename: str) -> List[Message]:
346
418
  result = []
347
- for template_ in list(print_config.get("templates").keys()):
419
+ for template_ in list(cast(Dict[str, Any], print_config.get("templates")).keys()):
348
420
  assert template_ is not None
349
- result.append(
350
- Message(None, template_, None, [], "", "", (filename, "template/{}".format(template_)))
351
- )
421
+ result.append(Message(None, template_, None, [], "", "", (filename, f"template/{template_}")))
352
422
  assert not [
353
423
  attribute
354
424
  for attribute in list(print_config["templates"][template_]["attributes"].keys())
@@ -362,22 +432,20 @@ class GeomapfishConfigExtractor(Extractor): # pragma: no cover
362
432
  [],
363
433
  "",
364
434
  "",
365
- (filename, "template/{}/{}".format(template_, attribute)),
435
+ (filename, f"template/{template_}/{attribute}"),
366
436
  )
367
437
  for attribute in list(print_config["templates"][template_]["attributes"].keys())
368
438
  ]
369
439
  return result
370
440
 
371
441
 
372
- class GeomapfishThemeExtractor(Extractor): # pragma: no cover
373
- """
374
- GeoMapFish theme extractor
375
- """
442
+ class GeomapfishThemeExtractor(Extractor): # type: ignore
443
+ """GeoMapFish theme extractor."""
376
444
 
377
445
  # Run on the development.ini file
378
446
  extensions = [".ini"]
379
- featuretype_cache: Dict[str, Optional[Dict]] = {}
380
- wmscap_cache: Dict[str, WebMapService] = {}
447
+ featuretype_cache: Dict[str, Optional[Dict[str, Any]]] = {}
448
+ wms_capabilities_cache: Dict[str, WebMapService] = {}
381
449
 
382
450
  def __init__(self) -> None:
383
451
  super().__init__()
@@ -388,16 +456,20 @@ class GeomapfishThemeExtractor(Extractor): # pragma: no cover
388
456
  self.config = None
389
457
  self.env = None
390
458
 
391
- def __call__(self, filename, options, fileobj=None, lineno=0):
459
+ def __call__(
460
+ self, filename: str, options: Dict[str, Any], fileobj: Optional[str] = None, lineno: int = 0
461
+ ) -> List[Message]:
392
462
  del fileobj, lineno
463
+
464
+ print(f"Running {self.__class__.__name__} on {filename}")
465
+
393
466
  messages: List[Message] = []
394
467
 
395
468
  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
469
+ init_db(self.config)
470
+ from c2cgeoportal_commons.models import DBSession # pylint: disable=import-outside-toplevel
471
+
472
+ db_session = DBSession()
401
473
 
402
474
  try:
403
475
  from c2cgeoportal_commons.models.main import ( # pylint: disable=import-outside-toplevel
@@ -408,10 +480,25 @@ class GeomapfishThemeExtractor(Extractor): # pragma: no cover
408
480
  Theme,
409
481
  )
410
482
 
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)
483
+ self._import(Theme, messages, name_regex=_get_config_str("THEME_REGEX", ".*"))
484
+ self._import(
485
+ LayerGroup,
486
+ messages,
487
+ name_regex=_get_config_str("GROUP_REGEX", ".*"),
488
+ has_interfaces=False,
489
+ )
490
+ self._import(
491
+ LayerWMS,
492
+ messages,
493
+ self._import_layer_wms,
494
+ name_regex=_get_config_str("WMSLAYER_REGEX", ".*"),
495
+ )
496
+ self._import(
497
+ LayerWMTS,
498
+ messages,
499
+ self._import_layer_wmts,
500
+ name_regex=_get_config_str("WMTSLAYER_REGEX", ".*"),
501
+ )
415
502
 
416
503
  for (layer_name,) in db_session.query(FullTextSearch.layer_name).distinct().all():
417
504
  if layer_name is not None and layer_name != "":
@@ -448,60 +535,75 @@ class GeomapfishThemeExtractor(Extractor): # pragma: no cover
448
535
  colorize(
449
536
  "ERROR! The database is probably not up to date "
450
537
  "(should be ignored when happen during the upgrade)",
451
- RED,
538
+ Color.RED,
452
539
  )
453
540
  )
454
- print(colorize(e, RED))
455
- if os.environ.get("IGNORE_I18N_ERRORS", "FALSE") != "TRUE":
541
+ print(colorize(e, Color.RED))
542
+ if _get_config_str("IGNORE_I18N_ERRORS", "FALSE") != "TRUE":
456
543
  raise
457
544
  except NoSuchTableError as e:
458
545
  print(
459
546
  colorize(
460
547
  "ERROR! The schema didn't seem to exists "
461
548
  "(should be ignored when happen during the deploy)",
462
- RED,
549
+ Color.RED,
463
550
  )
464
551
  )
465
- print(colorize(e, RED))
466
- if os.environ.get("IGNORE_I18N_ERRORS", "FALSE") != "TRUE":
552
+ print(colorize(e, Color.RED))
553
+ if _get_config_str("IGNORE_I18N_ERRORS", "FALSE") != "TRUE":
467
554
  raise
468
555
  except OperationalError as e:
469
556
  print(
470
557
  colorize(
471
558
  "ERROR! The database didn't seem to exists "
472
559
  "(should be ignored when happen during the deploy)",
473
- RED,
560
+ Color.RED,
474
561
  )
475
562
  )
476
- print(colorize(e, RED))
477
- if os.environ.get("IGNORE_I18N_ERRORS", "FALSE") != "TRUE":
563
+ print(colorize(e, Color.RED))
564
+ if _get_config_str("IGNORE_I18N_ERRORS", "FALSE") != "TRUE":
478
565
  raise
479
566
 
480
567
  return messages
481
568
 
482
569
  @staticmethod
483
- def _import(object_type, messages, callback=None):
570
+ def _import(
571
+ object_type: Type[Any],
572
+ messages: List[str],
573
+ callback: Optional[Callable[["main.Layer", List[str]], None]] = None,
574
+ has_interfaces: bool = True,
575
+ name_regex: str = ".*",
576
+ ) -> None:
484
577
  from c2cgeoportal_commons.models import DBSession # pylint: disable=import-outside-toplevel
578
+ from c2cgeoportal_commons.models.main import Interface # pylint: disable=import-outside-toplevel
579
+
580
+ filter_re = re.compile(name_regex)
485
581
 
486
- items = DBSession.query(object_type)
487
- for item in items:
582
+ query = DBSession.query(object_type)
583
+
584
+ interfaces = _get_config("INTERFACES")
585
+ if has_interfaces and interfaces is not None:
586
+ query.join(object_type.interface).filter(Interface.name in interfaces.split("."))
587
+
588
+ for item in query.all():
488
589
  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")),
590
+ if filter_re.match(item.name):
591
+ messages.append(
592
+ Message(
593
+ None,
594
+ item.name,
595
+ None,
596
+ [],
597
+ "",
598
+ "",
599
+ (item.item_type, item.name.encode("ascii", errors="replace")),
600
+ )
498
601
  )
499
- )
500
602
 
501
603
  if callback is not None:
502
604
  callback(item, messages)
503
605
 
504
- def _import_layer_wms(self, layer, messages):
606
+ def _import_layer_wms(self, layer: "main.Layer", messages: List[str]) -> None:
505
607
  server = layer.ogc_server
506
608
  url = server.url_wfs or server.url
507
609
  if url is None:
@@ -540,14 +642,15 @@ class GeomapfishThemeExtractor(Extractor): # pragma: no cover
540
642
  except NoSuchTableError:
541
643
  print(
542
644
  colorize(
543
- "ERROR! No such table '{}' for layer '{}'.".format(layer.geo_table, layer.name), RED
645
+ f"ERROR! No such table '{layer.geo_table}' for layer '{layer.name}'.",
646
+ Color.RED,
544
647
  )
545
648
  )
546
- print(colorize(traceback.format_exc(), RED))
547
- if os.environ.get("IGNORE_I18N_ERRORS", "FALSE") != "TRUE":
649
+ print(colorize(traceback.format_exc(), Color.RED))
650
+ if _get_config_str("IGNORE_I18N_ERRORS", "FALSE") != "TRUE":
548
651
  raise
549
652
 
550
- def _import_layer_wmts(self, layer, messages):
653
+ def _import_layer_wmts(self, layer: "main.Layer", messages: List[str]) -> None:
551
654
  from c2cgeoportal_commons.models import DBSession # pylint: disable=import-outside-toplevel
552
655
  from c2cgeoportal_commons.models.main import OGCServer # pylint: disable=import-outside-toplevel
553
656
 
@@ -571,16 +674,17 @@ class GeomapfishThemeExtractor(Extractor): # pragma: no cover
571
674
  except NoResultFound:
572
675
  print(
573
676
  colorize(
574
- "ERROR! the OGC server '{}' from the WMTS layer '{}' does not exist.".format(
575
- server[0], layer.name
576
- ),
577
- RED,
677
+ f"ERROR! the OGC server '{server[0]}' from the "
678
+ f"WMTS layer '{layer.name}' does not exist.",
679
+ Color.RED,
578
680
  )
579
681
  )
580
- if os.environ.get("IGNORE_I18N_ERRORS", "FALSE") != "TRUE":
682
+ if _get_config_str("IGNORE_I18N_ERRORS", "FALSE") != "TRUE":
581
683
  raise
582
684
 
583
- def _import_layer_attributes(self, url, layer, item_type, name, messages):
685
+ def _import_layer_attributes(
686
+ self, url: str, layer: "main.Layer", item_type: str, name: str, messages: List[str]
687
+ ) -> None:
584
688
  attributes, layers = self._layer_attributes(url, layer)
585
689
  for sub_layer in layers:
586
690
  assert sub_layer is not None
@@ -609,131 +713,151 @@ class GeomapfishThemeExtractor(Extractor): # pragma: no cover
609
713
  )
610
714
  )
611
715
 
612
- def _build_url(self, url):
613
- url_split = urlsplit(url)
614
- hostname = url_split.hostname
716
+ def _build_url(self, url: Url) -> Tuple[Url, Dict[str, str], Dict[str, Any]]:
717
+ hostname = url.hostname
615
718
  host_map = self.config.get("lingua_extractor", {}).get("host_map", {})
616
719
  if hostname in host_map:
617
720
  map_ = host_map[hostname]
618
721
  if "netloc" in map_:
619
- url_split = url_split._replace(netloc=map_["netloc"])
722
+ url.netloc = map_["netloc"]
620
723
  if "scheme" in map_:
621
- url_split = url_split._replace(scheme=map_["scheme"])
724
+ url.scheme = map_["scheme"]
622
725
  kwargs = {"verify": map_["verify"]} if "verify" in map_ else {}
623
- return url_split.geturl(), map_.get("headers", {}), kwargs
726
+ return url, map_.get("headers", {}), kwargs
624
727
  return url, {}, {}
625
728
 
626
- def _layer_attributes(self, url, layer):
729
+ def _layer_attributes(self, url: str, layer: str) -> Tuple[List[str], List[str]]:
627
730
  errors: Set[str] = set()
628
731
 
629
- request = _Request()
630
- request.registry.settings = self.config
732
+ request = pyramid.threadlocal.get_current_request()
733
+ if request is None:
734
+ request = _Request()
735
+ request.registry.settings = self.config
736
+
631
737
  # Static schema will not be supported
632
- url = get_url2("Layer", url, request, errors)
738
+ url_obj_ = get_url2("Layer", url, request, errors)
633
739
  if errors:
634
740
  print("\n".join(errors))
635
741
  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
- },
742
+ if not url_obj_:
743
+ print(f"No URL for: {url}")
744
+ return [], []
745
+ url_obj: Url = url_obj_
746
+ url_obj, headers, kwargs = self._build_url(url_obj)
747
+
748
+ if url not in self.wms_capabilities_cache:
749
+ print(f"Get WMS GetCapabilities for URL: {url_obj}")
750
+ self.wms_capabilities_cache[url] = None
751
+
752
+ wms_getcap_url = (
753
+ url_obj.clone()
754
+ .add_query(
755
+ {
756
+ "SERVICE": "WMS",
757
+ "VERSION": "1.1.1",
758
+ "REQUEST": "GetCapabilities",
759
+ "ROLE_IDS": "0",
760
+ "USER_ID": "0",
761
+ }
762
+ )
763
+ .url()
651
764
  )
652
765
  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
- )
766
+ rendered_headers = " ".join(
767
+ [
768
+ f"{h}={v if h not in ('Authorization', 'Cookies') else '***'}"
769
+ for h, v in headers.items()
770
+ ]
663
771
  )
772
+ print(f"Get WMS GetCapabilities for URL {wms_getcap_url},\nwith headers: {rendered_headers}")
664
773
  response = requests.get(wms_getcap_url, headers=headers, **kwargs)
665
774
 
666
- try:
667
- self.wmscap_cache[url] = WebMapService(None, xml=response.content)
668
- except Exception as e:
775
+ if response.ok:
776
+ try:
777
+ self.wms_capabilities_cache[url] = WebMapService(None, xml=response.content)
778
+ except Exception as e:
779
+ print(
780
+ colorize(
781
+ "ERROR! an error occurred while trying to parse "
782
+ "the GetCapabilities document.",
783
+ Color.RED,
784
+ )
785
+ )
786
+ print(colorize(str(e), Color.RED))
787
+ print(f"URL: {wms_getcap_url}\nxml:\n{response.text}")
788
+ if _get_config_str("IGNORE_I18N_ERRORS", "FALSE") != "TRUE":
789
+ raise
790
+ else:
669
791
  print(
670
792
  colorize(
671
- "ERROR! an error occurred while trying to " "parse the GetCapabilities document.",
672
- RED,
793
+ f"ERROR! Unable to GetCapabilities from URL: {wms_getcap_url},\n"
794
+ f"with headers: {rendered_headers}",
795
+ Color.RED,
673
796
  )
674
797
  )
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))
798
+ print(f"Response: {response.status_code} {response.reason}\n{response.text}")
799
+ if _get_config_str("IGNORE_I18N_ERRORS", "FALSE") != "TRUE":
800
+ raise Exception(response.reason)
801
+ except Exception as e:
802
+ print(colorize(str(e), Color.RED))
803
+ rendered_headers = " ".join(
804
+ [
805
+ f"{h}={v if h not in ('Authorization', 'Cookies') else '***'}"
806
+ for h, v in headers.items()
807
+ ]
808
+ )
681
809
  print(
682
810
  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,
811
+ f"ERROR! Unable to GetCapabilities from URL: {wms_getcap_url},\n"
812
+ f"with headers: {rendered_headers}",
813
+ Color.RED,
693
814
  )
694
815
  )
695
- if os.environ.get("IGNORE_I18N_ERRORS", "FALSE") != "TRUE":
816
+ if _get_config_str("IGNORE_I18N_ERRORS", "FALSE") != "TRUE":
696
817
  raise
697
818
 
698
- wmscap = self.wmscap_cache[url]
819
+ wms_capabilities = self.wms_capabilities_cache[url]
699
820
 
700
821
  if url not in self.featuretype_cache:
701
- print("Get WFS DescribeFeatureType for URL: {}".format(url))
822
+ print(f"Get WFS DescribeFeatureType for URL: {url_obj}")
702
823
  self.featuretype_cache[url] = None
703
824
 
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
- },
825
+ wfs_describe_feature_url = (
826
+ url_obj.clone()
827
+ .add_query(
828
+ {
829
+ "SERVICE": "WFS",
830
+ "VERSION": "1.1.0",
831
+ "REQUEST": "DescribeFeatureType",
832
+ "ROLE_IDS": "0",
833
+ "USER_ID": "0",
834
+ }
835
+ )
836
+ .url()
713
837
  )
714
838
  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))
839
+ response = requests.get(wfs_describe_feature_url, headers=headers, **kwargs)
840
+ except Exception as e:
841
+ print(colorize(str(e), Color.RED))
718
842
  print(
719
843
  colorize(
720
- "ERROR! Unable to DescribeFeatureType from URL: {}".format(wfs_descrfeat_url), RED
844
+ f"ERROR! Unable to DescribeFeatureType from URL: {wfs_describe_feature_url}",
845
+ Color.RED,
721
846
  )
722
847
  )
723
- if os.environ.get("IGNORE_I18N_ERRORS", "FALSE") == "TRUE":
848
+ if _get_config_str("IGNORE_I18N_ERRORS", "FALSE") == "TRUE":
724
849
  return [], []
725
850
  raise
726
851
 
727
- if not response.ok: # pragma: no cover
852
+ if not response.ok:
728
853
  print(
729
854
  colorize(
730
- "ERROR! DescribeFeatureType from URL {} return the error: {:d} {}".format(
731
- wfs_descrfeat_url, response.status_code, response.reason
732
- ),
733
- RED,
855
+ f"ERROR! DescribeFeatureType from URL {wfs_describe_feature_url} return the error: "
856
+ f"{response.status_code:d} {response.reason}",
857
+ Color.RED,
734
858
  )
735
859
  )
736
- if os.environ.get("IGNORE_I18N_ERRORS", "FALSE") == "TRUE":
860
+ if _get_config_str("IGNORE_I18N_ERRORS", "FALSE") == "TRUE":
737
861
  return [], []
738
862
  raise Exception("Aborted")
739
863
 
@@ -748,13 +872,13 @@ class GeomapfishThemeExtractor(Extractor): # pragma: no cover
748
872
  except ExpatError as e:
749
873
  print(
750
874
  colorize(
751
- "ERROR! an error occurred while trying to " "parse the DescribeFeatureType document.",
752
- RED,
875
+ "ERROR! an error occurred while trying to parse the DescribeFeatureType document.",
876
+ Color.RED,
753
877
  )
754
878
  )
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":
879
+ print(colorize(str(e), Color.RED))
880
+ print(f"URL: {wfs_describe_feature_url}\nxml:\n{response.text}")
881
+ if _get_config_str("IGNORE_I18N_ERRORS", "FALSE") == "TRUE":
758
882
  return [], []
759
883
  raise
760
884
  except AttributeError:
@@ -762,11 +886,11 @@ class GeomapfishThemeExtractor(Extractor): # pragma: no cover
762
886
  colorize(
763
887
  "ERROR! an error occurred while trying to "
764
888
  "read the Mapfile and recover the themes.",
765
- RED,
889
+ Color.RED,
766
890
  )
767
891
  )
768
- print("URL: {}\nxml:\n{}".format(wfs_descrfeat_url, response.text))
769
- if os.environ.get("IGNORE_I18N_ERRORS", "FALSE") == "TRUE":
892
+ print(f"URL: {wfs_describe_feature_url}\nxml:\n{response.text}")
893
+ if _get_config_str("IGNORE_I18N_ERRORS", "FALSE") == "TRUE":
770
894
  return [], []
771
895
  raise
772
896
  else:
@@ -775,16 +899,16 @@ class GeomapfishThemeExtractor(Extractor): # pragma: no cover
775
899
  if featurestype is None:
776
900
  return [], []
777
901
 
778
- layers = [layer]
779
- if wmscap is not None and layer in list(wmscap.contents):
780
- layer_obj = wmscap[layer]
902
+ layers: List[str] = [layer]
903
+ if wms_capabilities is not None and layer in list(wms_capabilities.contents):
904
+ layer_obj = wms_capabilities[layer]
781
905
  if layer_obj.layers:
782
906
  layers = [layer.name for layer in layer_obj.layers]
783
907
 
784
- attributes = []
908
+ attributes: List[str] = []
785
909
  for sub_layer in layers:
786
910
  # Should probably be adapted for other king of servers
787
- type_element = featurestype.get("{}Type".format(sub_layer))
911
+ type_element = featurestype.get(f"{sub_layer}Type")
788
912
  if type_element is not None:
789
913
  for element in type_element.getElementsByTagNameNS(
790
914
  "http://www.w3.org/2001/XMLSchema", "element"