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