c2cgeoportal-geoportal 2.6.0__py2.py3-none-any.whl → 2.7.1.86__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.
- c2cgeoportal_geoportal/__init__.py +224 -84
- c2cgeoportal_geoportal/lib/__init__.py +64 -42
- c2cgeoportal_geoportal/lib/authentication.py +50 -22
- c2cgeoportal_geoportal/lib/bashcolor.py +17 -13
- c2cgeoportal_geoportal/lib/cacheversion.py +16 -8
- c2cgeoportal_geoportal/lib/caching.py +61 -191
- c2cgeoportal_geoportal/lib/check_collector.py +17 -10
- c2cgeoportal_geoportal/lib/checker.py +61 -63
- c2cgeoportal_geoportal/lib/common_headers.py +170 -0
- c2cgeoportal_geoportal/lib/dbreflection.py +54 -39
- c2cgeoportal_geoportal/lib/filter_capabilities.py +119 -87
- c2cgeoportal_geoportal/lib/fulltextsearch.py +6 -5
- c2cgeoportal_geoportal/lib/functionality.py +20 -17
- c2cgeoportal_geoportal/lib/headers.py +14 -5
- c2cgeoportal_geoportal/lib/i18n.py +4 -4
- c2cgeoportal_geoportal/lib/layers.py +30 -11
- c2cgeoportal_geoportal/lib/lingua_extractor.py +361 -237
- c2cgeoportal_geoportal/lib/loader.py +10 -15
- c2cgeoportal_geoportal/lib/metrics.py +28 -17
- c2cgeoportal_geoportal/lib/oauth2.py +214 -145
- c2cgeoportal_geoportal/lib/wmstparsing.py +115 -90
- c2cgeoportal_geoportal/lib/xsd.py +26 -16
- c2cgeoportal_geoportal/resources.py +15 -9
- c2cgeoportal_geoportal/scaffolds/advance_create/ci/config.yaml +26 -0
- c2cgeoportal_geoportal/scaffolds/advance_create/cookiecutter.json +18 -0
- c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/.dockerignore +6 -0
- c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/.eslintrc.yaml +19 -0
- c2cgeoportal_geoportal/scaffolds/{create/geoportal/+dot+prospector.yaml → advance_create/{{cookiecutter.project}}/geoportal/.prospector.yaml} +8 -2
- c2cgeoportal_geoportal/scaffolds/{create/geoportal/Dockerfile_tmpl → advance_create/{{cookiecutter.project}}/geoportal/Dockerfile} +18 -9
- c2cgeoportal_geoportal/scaffolds/{create/geoportal/alembic.yaml_tmpl → advance_create/{{cookiecutter.project}}/geoportal/alembic.yaml} +1 -1
- c2cgeoportal_geoportal/scaffolds/{create/geoportal/development.ini_tmpl → advance_create/{{cookiecutter.project}}/geoportal/development.ini} +34 -15
- c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/gunicorn.conf.py +104 -0
- c2cgeoportal_geoportal/scaffolds/{create → advance_create/{{cookiecutter.project}}}/geoportal/lingua-client.cfg +1 -0
- c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/production.ini +38 -0
- c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/requirements.txt +2 -0
- c2cgeoportal_geoportal/scaffolds/{create/geoportal/setup.py_tmpl → advance_create/{{cookiecutter.project}}/geoportal/setup.py} +6 -7
- c2cgeoportal_geoportal/scaffolds/{create → advance_create/{{cookiecutter.project}}}/geoportal/tools/extract-messages.js +8 -6
- c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/tsconfig.json +8 -0
- c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/webpack.api.js +75 -0
- c2cgeoportal_geoportal/scaffolds/{create/geoportal/webpack.apps.js_tmpl → advance_create/{{cookiecutter.project}}/geoportal/webpack.apps.js} +31 -28
- c2cgeoportal_geoportal/scaffolds/{create → advance_create/{{cookiecutter.project}}}/geoportal/webpack.commons.js +3 -7
- c2cgeoportal_geoportal/scaffolds/{create → advance_create/{{cookiecutter.project}}}/geoportal/webpack.config.js +1 -1
- c2cgeoportal_geoportal/scaffolds/{create/geoportal/+package+_geoportal/__init__.py_tmpl → advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/__init__.py} +11 -22
- c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/authentication.py +10 -0
- c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/dev.py +14 -0
- c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/models.py +8 -0
- c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/multi_organization.py +7 -0
- c2cgeoportal_geoportal/scaffolds/{create/geoportal/+package+_geoportal → advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/resources.py +4 -3
- 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
- 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
- c2cgeoportal_geoportal/scaffolds/{create/geoportal/+package+_geoportal/subscribers.py_tmpl → advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/subscribers.py} +1 -3
- c2cgeoportal_geoportal/scaffolds/advance_update/cookiecutter.json +18 -0
- c2cgeoportal_geoportal/scaffolds/{update/geoportal/CONST_Makefile_tmpl → advance_update/{{cookiecutter.project}}/geoportal/CONST_Makefile} +3 -7
- c2cgeoportal_geoportal/scaffolds/create/cookiecutter.json +18 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.dockerignore +14 -0
- c2cgeoportal_geoportal/scaffolds/create/{+dot+editorconfig → {{cookiecutter.project}}/.editorconfig} +2 -5
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.github/workflows/main.yaml +43 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.github/workflows/rebuild.yaml +46 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.github/workflows/update_l10n.yaml +65 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.gitignore +16 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.prettierignore +1 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.prettierrc.yaml +2 -0
- c2cgeoportal_geoportal/scaffolds/create/{Dockerfile_tmpl → {{cookiecutter.project}}/Dockerfile} +20 -11
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/Makefile +14 -0
- c2cgeoportal_geoportal/scaffolds/create/{README.rst_tmpl → {{cookiecutter.project}}/README.rst} +4 -4
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/build +158 -0
- c2cgeoportal_geoportal/scaffolds/create/{ci/config.yaml_tmpl → {{cookiecutter.project}}/ci/config.yaml} +7 -5
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/ci/requirements.txt +1 -0
- c2cgeoportal_geoportal/scaffolds/create/{docker-compose-lib.yaml → {{cookiecutter.project}}/docker-compose-lib.yaml} +133 -17
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/docker-compose.override.sample.yaml +66 -0
- c2cgeoportal_geoportal/scaffolds/create/{docker-compose.yaml → {{cookiecutter.project}}/docker-compose.yaml} +17 -12
- c2cgeoportal_geoportal/scaffolds/create/{env.default_tmpl → {{cookiecutter.project}}/env.default} +29 -14
- c2cgeoportal_geoportal/scaffolds/create/{env.project_tmpl → {{cookiecutter.project}}/env.project} +16 -4
- c2cgeoportal_geoportal/scaffolds/create/{geoportal/vars.yaml_tmpl → {{cookiecutter.project}}/geoportal/vars.yaml} +93 -27
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static/css/mobile.css +0 -0
- c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/data/Readme.txt +1 -1
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/mapserver/demo.map.tmpl +224 -0
- c2cgeoportal_geoportal/scaffolds/create/{mapserver/mapserver.map.tmpl_tmpl → {{cookiecutter.project}}/mapserver/mapserver.map.tmpl} +7 -15
- c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/A3_Landscape.jrxml +8 -8
- c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/A3_Portrait.jrxml +8 -8
- c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/A4_Landscape.jrxml +8 -8
- c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/A4_Portrait.jrxml +8 -8
- c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/config.yaml.tmpl +5 -4
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}/localisation.properties +4 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}/localisation_fr.properties +4 -0
- c2cgeoportal_geoportal/scaffolds/create/{project.yaml_tmpl → {{cookiecutter.project}}/project.yaml} +6 -6
- c2cgeoportal_geoportal/scaffolds/create/{qgisserver/pg_service.conf.tmpl_tmpl → {{cookiecutter.project}}/qgisserver/pg_service.conf.tmpl} +2 -2
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/scripts/db-backup +107 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/scripts/db-restore +111 -0
- c2cgeoportal_geoportal/scaffolds/create/{setup.cfg_tmpl → {{cookiecutter.project}}/setup.cfg} +1 -1
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/spell-ignore-words.txt +3 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/tilegeneration/config.yaml.tmpl +195 -0
- c2cgeoportal_geoportal/scaffolds/update/cookiecutter.json +18 -0
- c2cgeoportal_geoportal/scaffolds/update/{+dot+upgrade.yaml_tmpl → {{cookiecutter.project}}/.upgrade.yaml} +49 -39
- c2cgeoportal_geoportal/scaffolds/update/{{cookiecutter.project}}/CONST_CHANGELOG.txt +1153 -0
- c2cgeoportal_geoportal/scaffolds/update/{geoportal → {{cookiecutter.project}}/geoportal}/CONST_config-schema.yaml +47 -2
- c2cgeoportal_geoportal/scaffolds/update/{geoportal/CONST_vars.yaml_tmpl → {{cookiecutter.project}}/geoportal/CONST_vars.yaml} +350 -15
- c2cgeoportal_geoportal/scripts/__init__.py +15 -31
- c2cgeoportal_geoportal/scripts/c2cupgrade.py +271 -232
- c2cgeoportal_geoportal/scripts/create_demo_theme.py +3 -6
- c2cgeoportal_geoportal/scripts/manage_users.py +34 -39
- c2cgeoportal_geoportal/scripts/pcreate.py +312 -0
- c2cgeoportal_geoportal/scripts/theme2fts.py +72 -23
- c2cgeoportal_geoportal/scripts/urllogin.py +17 -9
- c2cgeoportal_geoportal/templates/login.html +88 -84
- c2cgeoportal_geoportal/templates/notlogin.html +59 -59
- c2cgeoportal_geoportal/templates/testi18n.html +6 -8
- c2cgeoportal_geoportal/views/__init__.py +23 -4
- c2cgeoportal_geoportal/views/dev.py +9 -7
- c2cgeoportal_geoportal/views/dynamic.py +54 -18
- c2cgeoportal_geoportal/views/entry.py +93 -24
- c2cgeoportal_geoportal/views/fulltextsearch.py +28 -22
- c2cgeoportal_geoportal/views/geometry_processing.py +15 -7
- c2cgeoportal_geoportal/views/i18n.py +91 -9
- c2cgeoportal_geoportal/views/layers.py +160 -126
- c2cgeoportal_geoportal/views/login.py +106 -93
- c2cgeoportal_geoportal/views/mapserverproxy.py +45 -28
- c2cgeoportal_geoportal/views/memory.py +12 -12
- c2cgeoportal_geoportal/views/ogcproxy.py +48 -30
- c2cgeoportal_geoportal/views/pdfreport.py +26 -22
- c2cgeoportal_geoportal/views/printproxy.py +56 -50
- c2cgeoportal_geoportal/views/profile.py +24 -23
- c2cgeoportal_geoportal/views/proxy.py +84 -67
- c2cgeoportal_geoportal/views/raster.py +35 -24
- c2cgeoportal_geoportal/views/resourceproxy.py +13 -11
- c2cgeoportal_geoportal/views/shortener.py +27 -24
- c2cgeoportal_geoportal/views/theme.py +427 -321
- c2cgeoportal_geoportal/views/tinyowsproxy.py +46 -39
- c2cgeoportal_geoportal/views/vector_tiles.py +80 -0
- {c2cgeoportal_geoportal-2.6.0.dist-info → c2cgeoportal_geoportal-2.7.1.86.dist-info}/METADATA +24 -20
- c2cgeoportal_geoportal-2.7.1.86.dist-info/RECORD +185 -0
- {c2cgeoportal_geoportal-2.6.0.dist-info → c2cgeoportal_geoportal-2.7.1.86.dist-info}/WHEEL +1 -1
- {c2cgeoportal_geoportal-2.6.0.dist-info → c2cgeoportal_geoportal-2.7.1.86.dist-info}/entry_points.txt +3 -1
- tests/__init__.py +7 -3
- tests/test_cachebuster.py +0 -2
- tests/test_caching.py +17 -25
- tests/test_checker.py +0 -2
- tests/test_decimaljson.py +4 -4
- tests/test_headerstween.py +0 -2
- tests/test_i18n.py +1 -1
- tests/test_init.py +4 -7
- tests/test_locale_negociator.py +0 -2
- tests/test_mapserverproxy_route_predicate.py +0 -2
- tests/test_raster.py +0 -2
- tests/test_wmstparsing.py +0 -2
- c2cgeoportal_geoportal/scaffolds/__init__.py +0 -227
- c2cgeoportal_geoportal/scaffolds/create/+dot+dockerignore_tmpl +0 -12
- c2cgeoportal_geoportal/scaffolds/create/+dot+github/workflows/main.yaml_tmpl +0 -89
- c2cgeoportal_geoportal/scaffolds/create/+dot+github/workflows/rebuild.yaml_tmpl +0 -78
- c2cgeoportal_geoportal/scaffolds/create/+dot+gitignore_tmpl +0 -16
- c2cgeoportal_geoportal/scaffolds/create/Makefile +0 -3
- c2cgeoportal_geoportal/scaffolds/create/build_tmpl +0 -167
- c2cgeoportal_geoportal/scaffolds/create/ci/requirements.txt +0 -1
- c2cgeoportal_geoportal/scaffolds/create/ci/trigger +0 -68
- c2cgeoportal_geoportal/scaffolds/create/docker-compose.override.sample.yaml +0 -54
- c2cgeoportal_geoportal/scaffolds/create/geoportal/+dot+dockerignore_tmpl +0 -6
- c2cgeoportal_geoportal/scaffolds/create/geoportal/+dot+eslintrc_tmpl +0 -15
- c2cgeoportal_geoportal/scaffolds/create/geoportal/+package+_geoportal/models.py_tmpl +0 -10
- c2cgeoportal_geoportal/scaffolds/create/geoportal/+package+_geoportal/static/robot.txt +0 -3
- c2cgeoportal_geoportal/scaffolds/create/geoportal/production.ini_tmpl +0 -106
- c2cgeoportal_geoportal/scaffolds/create/geoportal/requirements.txt +0 -2
- c2cgeoportal_geoportal/scaffolds/create/geoportal/tsconfig.json_tmpl +0 -9
- c2cgeoportal_geoportal/scaffolds/create/geoportal/webpack.api.js_tmpl +0 -72
- c2cgeoportal_geoportal/scaffolds/create/mapserver/demo.map.tmpl_tmpl +0 -262
- c2cgeoportal_geoportal/scaffolds/create/mapserver/tinyows.xml +0 -36
- c2cgeoportal_geoportal/scaffolds/create/print/print-apps/+package+/config.yaml +0 -168
- c2cgeoportal_geoportal/scaffolds/create/qgisserver/geomapfish.yaml.tmpl_tmpl +0 -16
- c2cgeoportal_geoportal/scaffolds/create/spell-ignore-words.txt +0 -1
- c2cgeoportal_geoportal/scaffolds/create/tilegeneration/config.yaml.tmpl_tmpl +0 -185
- c2cgeoportal_geoportal/scaffolds/create/yamllint.yaml +0 -11
- c2cgeoportal_geoportal/scaffolds/update/CONST_CHANGELOG.txt_tmpl +0 -454
- c2cgeoportal_geoportal/templates/dynamic.js +0 -21
- c2cgeoportal_geoportal-2.6.0.dist-info/RECORD +0 -173
- /c2cgeoportal_geoportal/{scaffolds/create/geoportal/+package+_geoportal/static/css/desktop.css → py.typed} +0 -0
- /c2cgeoportal_geoportal/scaffolds/{create/geoportal/Makefile_tmpl → advance_create/{{cookiecutter.project}}/geoportal/Makefile} +0 -0
- /c2cgeoportal_geoportal/scaffolds/{create → advance_create/{{cookiecutter.project}}}/geoportal/alembic.ini +0 -0
- /c2cgeoportal_geoportal/scaffolds/{create → advance_create/{{cookiecutter.project}}}/geoportal/language_mapping +0 -0
- /c2cgeoportal_geoportal/scaffolds/{create → advance_create/{{cookiecutter.project}}}/geoportal/lingua-server.cfg +0 -0
- /c2cgeoportal_geoportal/scaffolds/{create/geoportal/+package+_geoportal → advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/views/__init__.py +0 -0
- /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
- /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal/static/css/iframe_api.css → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static/css/desktop.css} +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal/static/css/mobile.css → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static/css/iframe_api.css} +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/static/images/banner_left.png +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/static/images/banner_right.png +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/static/images/blank.png +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/static/images/markers/marker-blue.png +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/static/images/markers/marker-gold.png +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/static/images/markers/marker-green.png +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/static/images/markers/marker.png +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/static/robot.txt.tmpl +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/data/TM_EUROPE_BORDERS-0.3.sql +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Arial.ttf +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Arialbd.ttf +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Arialbi.ttf +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Ariali.ttf +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/NotoSans-Bold.ttf +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/NotoSans-BoldItalic.ttf +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/NotoSans-Italic.ttf +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/NotoSans-Regular.ttf +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Verdana.ttf +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Verdanab.ttf +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Verdanai.ttf +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Verdanaz.ttf +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts.conf +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/tinyows.xml.tmpl +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/legend.jrxml +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/logo.png +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/north.svg +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/results.jrxml +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{pyproject.toml → {{cookiecutter.project}}/pyproject.toml} +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{run_alembic.sh → {{cookiecutter.project}}/run_alembic.sh} +0 -0
- {c2cgeoportal_geoportal-2.6.0.dist-info → c2cgeoportal_geoportal-2.7.1.86.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,4 @@
|
|
1
|
-
#
|
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
|
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
|
60
|
-
from c2cgeoportal_geoportal.lib.bashcolor import
|
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
|
-
|
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:
|
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): #
|
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
|
-
|
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("./{
|
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":
|
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
|
-
|
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
|
-
|
183
|
-
|
184
|
-
|
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
|
-
|
245
|
+
def init_db(settings: Dict[str, Any]) -> None:
|
202
246
|
"""
|
203
|
-
|
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__(
|
209
|
-
|
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/{}"
|
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
|
-
|
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
|
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,
|
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 =
|
270
|
-
layername
|
271
|
-
|
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
|
-
|
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))
|
356
|
+
query = session.query(Metadata).filter(Metadata.name.in_(names))
|
288
357
|
for metadata in query.all():
|
289
|
-
location = "metadata/{}/{
|
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"
|
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 =
|
315
|
-
interface
|
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(
|
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
|
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
|
402
|
+
return Layers.query_enumerate_attribute_values(dbsession, layerinfos, fieldname)
|
331
403
|
except Exception as e:
|
332
|
-
table = layerinfos
|
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: {} ({})"
|
337
|
-
RED,
|
408
|
+
f"db: {dbname}, table: {table}, column: {fieldname} ({e!s})",
|
409
|
+
Color.RED,
|
338
410
|
)
|
339
411
|
)
|
340
|
-
if
|
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/{}/{}"
|
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): #
|
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
|
-
|
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__(
|
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
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
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(
|
413
|
-
|
414
|
-
|
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
|
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
|
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
|
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(
|
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
|
-
|
487
|
-
|
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
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
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 '{}'."
|
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
|
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
|
575
|
-
|
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
|
682
|
+
if _get_config_str("IGNORE_I18N_ERRORS", "FALSE") != "TRUE":
|
581
683
|
raise
|
582
684
|
|
583
|
-
def _import_layer_attributes(
|
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
|
-
|
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
|
-
|
722
|
+
url.netloc = map_["netloc"]
|
620
723
|
if "scheme" in map_:
|
621
|
-
|
724
|
+
url.scheme = map_["scheme"]
|
622
725
|
kwargs = {"verify": map_["verify"]} if "verify" in map_ else {}
|
623
|
-
return
|
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 =
|
630
|
-
request
|
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
|
-
|
738
|
+
url_obj_ = get_url2("Layer", url, request, errors)
|
633
739
|
if errors:
|
634
740
|
print("\n".join(errors))
|
635
741
|
return [], []
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
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
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
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
|
-
|
667
|
-
|
668
|
-
|
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!
|
672
|
-
|
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(
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
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: {},\
|
684
|
-
|
685
|
-
|
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
|
816
|
+
if _get_config_str("IGNORE_I18N_ERRORS", "FALSE") != "TRUE":
|
696
817
|
raise
|
697
818
|
|
698
|
-
|
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: {}"
|
822
|
+
print(f"Get WFS DescribeFeatureType for URL: {url_obj}")
|
702
823
|
self.featuretype_cache[url] = None
|
703
824
|
|
704
|
-
|
705
|
-
|
706
|
-
|
707
|
-
|
708
|
-
|
709
|
-
|
710
|
-
|
711
|
-
|
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(
|
716
|
-
except Exception as e:
|
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: {}"
|
844
|
+
f"ERROR! Unable to DescribeFeatureType from URL: {wfs_describe_feature_url}",
|
845
|
+
Color.RED,
|
721
846
|
)
|
722
847
|
)
|
723
|
-
if
|
848
|
+
if _get_config_str("IGNORE_I18N_ERRORS", "FALSE") == "TRUE":
|
724
849
|
return [], []
|
725
850
|
raise
|
726
851
|
|
727
|
-
if not response.ok:
|
852
|
+
if not response.ok:
|
728
853
|
print(
|
729
854
|
colorize(
|
730
|
-
"ERROR! DescribeFeatureType from URL {} return the error:
|
731
|
-
|
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
|
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
|
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{
|
757
|
-
if
|
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{
|
769
|
-
if
|
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
|
780
|
-
layer_obj =
|
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"
|
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"
|