c2cgeoportal-geoportal 2.5.0.100__py2.py3-none-any.whl → 2.7.1.83__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- c2cgeoportal_geoportal/__init__.py +261 -130
- c2cgeoportal_geoportal/lib/__init__.py +72 -120
- c2cgeoportal_geoportal/lib/authentication.py +170 -21
- c2cgeoportal_geoportal/lib/bashcolor.py +17 -13
- c2cgeoportal_geoportal/lib/cacheversion.py +19 -11
- c2cgeoportal_geoportal/lib/caching.py +66 -160
- c2cgeoportal_geoportal/lib/check_collector.py +17 -10
- c2cgeoportal_geoportal/lib/checker.py +62 -64
- c2cgeoportal_geoportal/lib/common_headers.py +170 -0
- c2cgeoportal_geoportal/lib/dbreflection.py +70 -31
- c2cgeoportal_geoportal/lib/filter_capabilities.py +124 -96
- c2cgeoportal_geoportal/lib/fulltextsearch.py +50 -0
- c2cgeoportal_geoportal/lib/functionality.py +36 -21
- c2cgeoportal_geoportal/lib/headers.py +14 -5
- c2cgeoportal_geoportal/lib/i18n.py +39 -0
- c2cgeoportal_geoportal/lib/layers.py +29 -10
- c2cgeoportal_geoportal/lib/lingua_extractor.py +408 -211
- c2cgeoportal_geoportal/lib/loader.py +18 -16
- c2cgeoportal_geoportal/lib/metrics.py +29 -18
- c2cgeoportal_geoportal/lib/oauth2.py +1036 -0
- c2cgeoportal_geoportal/lib/wmstparsing.py +115 -90
- c2cgeoportal_geoportal/lib/xsd.py +29 -19
- 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/+dot+prospector.yaml → advance_create/{{cookiecutter.project}}/geoportal/.prospector.yaml} +8 -4
- c2cgeoportal_geoportal/scaffolds/{create/geoportal/Dockerfile_tmpl → advance_create/{{cookiecutter.project}}/geoportal/Dockerfile} +24 -15
- c2cgeoportal_geoportal/scaffolds/{create → advance_create/{{cookiecutter.project}}}/geoportal/alembic.ini +1 -0
- 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/advance_create/{{cookiecutter.project}}/geoportal/setup.py +25 -0
- 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 +4 -4
- c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/__init__.py +47 -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/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static-ngeo/api/index.js +12 -0
- c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static-ngeo/js/{{cookiecutter.package}}module.js +25 -0
- c2cgeoportal_geoportal/scaffolds/{create/geoportal/+package+_geoportal/subscribers.py_tmpl → advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/subscribers.py} +3 -6
- c2cgeoportal_geoportal/scaffolds/advance_update/cookiecutter.json +18 -0
- c2cgeoportal_geoportal/scaffolds/{update/geoportal/CONST_Makefile_tmpl → advance_update/{{cookiecutter.project}}/geoportal/CONST_Makefile} +32 -20
- 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} +4 -8
- 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} +34 -24
- 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/{{cookiecutter.project}}/ci/config.yaml +25 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/ci/requirements.txt +1 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/docker-compose-lib.yaml +474 -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} +43 -18
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/env.default +82 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/env.project +60 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/geoportal/vars.yaml +396 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static/css/mobile.css +0 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static/images/markers/marker-blue.png +0 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static/images/markers/marker-gold.png +0 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static/images/markers/marker-green.png +0 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static/images/markers/marker.png +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 +17 -9
- c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/A3_Portrait.jrxml +17 -9
- c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/A4_Landscape.jrxml +17 -9
- c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/A4_Portrait.jrxml +17 -9
- c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/config.yaml.tmpl +30 -27
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}/legend.jrxml +109 -0
- 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/{{cookiecutter.project}}/pyproject.toml +3 -0
- 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/{{cookiecutter.project}}/setup.cfg +7 -0
- 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/{{cookiecutter.project}}/.upgrade.yaml +191 -0
- c2cgeoportal_geoportal/scaffolds/update/{{cookiecutter.project}}/CONST_CHANGELOG.txt +1153 -0
- c2cgeoportal_geoportal/scaffolds/update/{geoportal → {{cookiecutter.project}}/geoportal}/CONST_config-schema.yaml +99 -47
- c2cgeoportal_geoportal/scaffolds/update/{{cookiecutter.project}}/geoportal/CONST_vars.yaml +1412 -0
- c2cgeoportal_geoportal/scripts/__init__.py +17 -33
- c2cgeoportal_geoportal/scripts/c2cupgrade.py +295 -200
- c2cgeoportal_geoportal/scripts/create_demo_theme.py +5 -6
- c2cgeoportal_geoportal/scripts/manage_users.py +34 -37
- c2cgeoportal_geoportal/scripts/pcreate.py +312 -0
- c2cgeoportal_geoportal/scripts/theme2fts.py +92 -25
- c2cgeoportal_geoportal/scripts/urllogin.py +23 -17
- c2cgeoportal_geoportal/templates/login.html +88 -84
- c2cgeoportal_geoportal/templates/notlogin.html +62 -0
- 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 +70 -40
- c2cgeoportal_geoportal/views/entry.py +93 -24
- c2cgeoportal_geoportal/views/fulltextsearch.py +36 -29
- c2cgeoportal_geoportal/views/geometry_processing.py +15 -7
- c2cgeoportal_geoportal/views/i18n.py +91 -9
- c2cgeoportal_geoportal/views/layers.py +173 -134
- c2cgeoportal_geoportal/views/login.py +206 -87
- c2cgeoportal_geoportal/views/mapserverproxy.py +59 -35
- c2cgeoportal_geoportal/views/memory.py +13 -13
- c2cgeoportal_geoportal/views/ogcproxy.py +48 -30
- c2cgeoportal_geoportal/views/pdfreport.py +31 -27
- c2cgeoportal_geoportal/views/printproxy.py +67 -53
- c2cgeoportal_geoportal/views/profile.py +25 -24
- c2cgeoportal_geoportal/views/proxy.py +97 -68
- c2cgeoportal_geoportal/views/raster.py +47 -29
- c2cgeoportal_geoportal/views/resourceproxy.py +13 -11
- c2cgeoportal_geoportal/views/shortener.py +31 -24
- c2cgeoportal_geoportal/views/theme.py +475 -365
- c2cgeoportal_geoportal/views/tinyowsproxy.py +46 -39
- c2cgeoportal_geoportal/views/vector_tiles.py +80 -0
- {c2cgeoportal_geoportal-2.5.0.100.dist-info → c2cgeoportal_geoportal-2.7.1.83.dist-info}/METADATA +16 -11
- c2cgeoportal_geoportal-2.7.1.83.dist-info/RECORD +185 -0
- {c2cgeoportal_geoportal-2.5.0.100.dist-info → c2cgeoportal_geoportal-2.7.1.83.dist-info}/WHEEL +1 -1
- {c2cgeoportal_geoportal-2.5.0.100.dist-info → c2cgeoportal_geoportal-2.7.1.83.dist-info}/entry_points.txt +3 -1
- tests/__init__.py +24 -3
- tests/test_cachebuster.py +1 -3
- tests/test_caching.py +19 -26
- tests/test_checker.py +2 -3
- tests/test_decimaljson.py +4 -4
- tests/test_headerstween.py +0 -3
- tests/test_i18n.py +31 -0
- tests/test_init.py +12 -27
- tests/test_locale_negociator.py +6 -6
- tests/test_mapserverproxy_route_predicate.py +0 -2
- tests/test_raster.py +18 -5
- tests/test_wmstparsing.py +7 -8
- c2cgeoportal_geoportal/scaffolds/__init__.py +0 -226
- c2cgeoportal_geoportal/scaffolds/create/+dot+dockerignore_tmpl +0 -11
- c2cgeoportal_geoportal/scaffolds/create/+dot+github/workflows/ci.yaml_tmpl +0 -56
- c2cgeoportal_geoportal/scaffolds/create/+dot+gitignore_tmpl +0 -12
- c2cgeoportal_geoportal/scaffolds/create/build_tmpl +0 -144
- c2cgeoportal_geoportal/scaffolds/create/docker-compose-lib.yaml_tmpl +0 -302
- c2cgeoportal_geoportal/scaffolds/create/docker-compose.override.sample.yaml_tmpl +0 -54
- c2cgeoportal_geoportal/scaffolds/create/env.default_tmpl +0 -49
- c2cgeoportal_geoportal/scaffolds/create/env.project_tmpl +0 -39
- c2cgeoportal_geoportal/scaffolds/create/geoportal/+dot+dockerignore_tmpl +0 -5
- c2cgeoportal_geoportal/scaffolds/create/geoportal/+dot+eslintrc_tmpl +0 -19
- c2cgeoportal_geoportal/scaffolds/create/geoportal/+package+_geoportal/__init__.py_tmpl +0 -48
- c2cgeoportal_geoportal/scaffolds/create/geoportal/+package+_geoportal/models.py_tmpl +0 -10
- c2cgeoportal_geoportal/scaffolds/create/geoportal/+package+_geoportal/static/images/favicon.ico +0 -0
- c2cgeoportal_geoportal/scaffolds/create/geoportal/+package+_geoportal/static/robot.txt +0 -3
- c2cgeoportal_geoportal/scaffolds/create/geoportal/+package+_geoportal/static-ngeo/api/index.js_tmpl +0 -37
- c2cgeoportal_geoportal/scaffolds/create/geoportal/+package+_geoportal/static-ngeo/js/+package+module.js_tmpl +0 -22
- c2cgeoportal_geoportal/scaffolds/create/geoportal/production.ini_tmpl +0 -106
- c2cgeoportal_geoportal/scaffolds/create/geoportal/requirements.txt +0 -2
- c2cgeoportal_geoportal/scaffolds/create/geoportal/setup.py_tmpl +0 -18
- c2cgeoportal_geoportal/scaffolds/create/geoportal/tsconfig.json_tmpl +0 -9
- c2cgeoportal_geoportal/scaffolds/create/geoportal/vars.yaml_tmpl +0 -224
- c2cgeoportal_geoportal/scaffolds/create/geoportal/webpack.api.js_tmpl +0 -71
- 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 -166
- c2cgeoportal_geoportal/scaffolds/create/print/print-apps/+package+/legend.jrxml +0 -27
- c2cgeoportal_geoportal/scaffolds/create/qgisserver/geomapfish.yaml.tmpl_tmpl +0 -29
- c2cgeoportal_geoportal/scaffolds/create/scripts/publish-docker +0 -124
- c2cgeoportal_geoportal/scaffolds/create/setup.cfg_tmpl +0 -3
- c2cgeoportal_geoportal/scaffolds/create/spell-ignore-words.txt +0 -1
- c2cgeoportal_geoportal/scaffolds/create/tilegeneration/config.yaml.tmpl_tmpl +0 -169
- c2cgeoportal_geoportal/scaffolds/create/yamllint.yaml +0 -11
- c2cgeoportal_geoportal/scaffolds/update/+dot+upgrade.yaml_tmpl +0 -171
- c2cgeoportal_geoportal/scaffolds/update/CONST_CHANGELOG.txt_tmpl +0 -64
- c2cgeoportal_geoportal/scaffolds/update/geoportal/CONST_vars.yaml_tmpl +0 -783
- c2cgeoportal_geoportal/templates/dynamic.js +0 -21
- c2cgeoportal_geoportal-2.5.0.100.dist-info/RECORD +0 -162
- tests/test_get_url.py +0 -96
- tests/test_lib.py +0 -77
- /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/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/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}}}/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/{run_alembic.sh → {{cookiecutter.project}}/run_alembic.sh} +0 -0
- {c2cgeoportal_geoportal-2.5.0.100.dist-info → c2cgeoportal_geoportal-2.7.1.83.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,4 @@
|
|
1
|
-
#
|
2
|
-
|
3
|
-
# Copyright (c) 2011-2020, 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,11 +31,13 @@ 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
|
39
|
+
import requests
|
40
|
+
import yaml
|
41
41
|
from bottle import MakoTemplate, template
|
42
42
|
from c2c.template.config import config
|
43
43
|
from defusedxml.minidom import parseString
|
@@ -46,66 +46,84 @@ from lingua.extractors import Extractor, Message
|
|
46
46
|
from mako.lookup import TemplateLookup
|
47
47
|
from mako.template import Template
|
48
48
|
from owslib.wms import WebMapService
|
49
|
-
import requests
|
50
|
-
import sqlalchemy
|
51
49
|
from sqlalchemy.exc import NoSuchTableError, OperationalError, ProgrammingError
|
52
50
|
from sqlalchemy.orm.exc import NoResultFound
|
53
51
|
from sqlalchemy.orm.properties import ColumnProperty
|
52
|
+
from sqlalchemy.orm.session import Session
|
54
53
|
from sqlalchemy.orm.util import class_mapper
|
55
|
-
import yaml
|
56
54
|
|
57
|
-
import c2cgeoportal_commons.models
|
58
55
|
import c2cgeoportal_geoportal
|
59
|
-
from
|
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
|
83
|
+
|
64
84
|
|
65
|
-
class _Registry:
|
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,37 +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
236
|
messages.append(Message(None, message, None, [], "", "", context.split(":")))
|
192
237
|
return messages
|
193
238
|
except Exception:
|
194
|
-
print(colorize("An error occurred", RED))
|
195
|
-
print(colorize(message_str, RED))
|
239
|
+
print(colorize("An error occurred", Color.RED))
|
240
|
+
print(colorize(message_str, Color.RED))
|
196
241
|
print("------")
|
197
242
|
raise
|
198
243
|
|
199
244
|
|
200
|
-
|
245
|
+
def init_db(settings: Dict[str, Any]) -> None:
|
201
246
|
"""
|
202
|
-
|
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.
|
203
251
|
"""
|
204
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
|
+
|
205
284
|
extensions = [".yaml", ".tmpl"]
|
206
285
|
|
207
|
-
def __call__(
|
208
|
-
|
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
|
+
|
209
297
|
init_region({"backend": "dogpile.cache.memory"}, "std")
|
210
298
|
init_region({"backend": "dogpile.cache.memory"}, "obj")
|
211
299
|
|
212
|
-
with open(filename) as config_file:
|
300
|
+
with open(filename, encoding="utf8") as config_file:
|
213
301
|
gmf_config = yaml.load(config_file, Loader=yaml.BaseLoader) # nosec
|
214
302
|
# For application config (config.yaml)
|
215
303
|
if "vars" in gmf_config:
|
@@ -219,37 +307,26 @@ class GeomapfishConfigExtractor(Extractor): # pragma: no cover
|
|
219
307
|
return self._collect_print_config(gmf_config, filename)
|
220
308
|
raise Exception("Not a known config file")
|
221
309
|
|
222
|
-
def _collect_app_config(self, filename):
|
310
|
+
def _collect_app_config(self, filename: str) -> List[Message]:
|
223
311
|
config.init(filename)
|
224
312
|
settings = config.get_config()
|
313
|
+
assert not [
|
314
|
+
raster_layer for raster_layer in list(settings.get("raster", {}).keys()) if raster_layer is None
|
315
|
+
]
|
225
316
|
# Collect raster layers names
|
226
317
|
raster = [
|
227
|
-
Message(None, raster_layer, None, [], "", "", (filename, "raster/{}"
|
318
|
+
Message(None, raster_layer, None, [], "", "", (filename, f"raster/{raster_layer}"))
|
228
319
|
for raster_layer in list(settings.get("raster", {}).keys())
|
229
320
|
]
|
230
321
|
|
231
|
-
|
232
|
-
|
233
|
-
class R:
|
234
|
-
settings = None
|
235
|
-
|
236
|
-
class C:
|
237
|
-
registry = R()
|
238
|
-
|
239
|
-
def get_settings(self):
|
240
|
-
return self.registry.settings
|
241
|
-
|
242
|
-
def add_tween(self, *args, **kwargs):
|
243
|
-
pass
|
244
|
-
|
245
|
-
config_ = C()
|
246
|
-
config_.registry.settings = settings
|
247
|
-
|
248
|
-
c2cgeoportal_geoportal.init_dbsessions(settings, config_)
|
322
|
+
init_db(settings)
|
249
323
|
|
250
324
|
# Collect layers enum values (for filters)
|
251
325
|
|
252
|
-
from c2cgeoportal_commons.models import
|
326
|
+
from c2cgeoportal_commons.models import ( # pylint: disable=import-outside-toplevel
|
327
|
+
DBSession,
|
328
|
+
DBSessions,
|
329
|
+
)
|
253
330
|
from c2cgeoportal_commons.models.main import Metadata # pylint: disable=import-outside-toplevel
|
254
331
|
|
255
332
|
enums = []
|
@@ -258,61 +335,95 @@ class GeomapfishConfigExtractor(Extractor): # pragma: no cover
|
|
258
335
|
layerinfos = enum_layers.get(layername, {})
|
259
336
|
attributes = layerinfos.get("attributes", {})
|
260
337
|
for fieldname in list(attributes.keys()):
|
261
|
-
values = self._enumerate_attributes_values(DBSessions,
|
338
|
+
values = self._enumerate_attributes_values(DBSessions, layerinfos, fieldname)
|
262
339
|
for (value,) in values:
|
263
340
|
if isinstance(value, str) and value != "":
|
264
341
|
msgid = value
|
265
|
-
location =
|
266
|
-
layername
|
342
|
+
location = (
|
343
|
+
f"/layers/{layername}/values/{fieldname}/"
|
344
|
+
f"{value.encode('ascii', errors='replace').decode('ascii')}"
|
267
345
|
)
|
346
|
+
assert msgid is not None
|
268
347
|
enums.append(Message(None, msgid, None, [], "", "", (filename, location)))
|
269
348
|
|
270
349
|
metadata_list = []
|
271
|
-
defs = config["admin_interface"]["available_metadata"]
|
350
|
+
defs = config["admin_interface"]["available_metadata"] # pylint: disable=unsubscriptable-object
|
272
351
|
names = [e["name"] for e in defs if e.get("translate", False)]
|
273
352
|
|
274
353
|
if names:
|
275
|
-
|
276
|
-
Session = sqlalchemy.orm.session.sessionmaker() # noqa
|
277
|
-
Session.configure(bind=engine)
|
278
|
-
session = Session()
|
354
|
+
session = DBSession()
|
279
355
|
|
280
356
|
query = session.query(Metadata).filter(Metadata.name.in_(names))
|
281
357
|
for metadata in query.all():
|
282
|
-
location = "metadata/{}/{
|
358
|
+
location = f"metadata/{metadata.name}/{metadata.id}"
|
359
|
+
assert metadata.value is not None
|
283
360
|
metadata_list.append(Message(None, metadata.value, None, [], "", "", (filename, location)))
|
284
361
|
|
285
|
-
|
362
|
+
interfaces_messages = []
|
363
|
+
for interface, interface_config in config["interfaces_config"].items():
|
364
|
+
for ds_index, datasource in enumerate(
|
365
|
+
interface_config.get("constants", {}).get("gmfSearchOptions", {}).get("datasources", [])
|
366
|
+
):
|
367
|
+
for a_index, action in enumerate(datasource.get("groupActions", [])):
|
368
|
+
location = (
|
369
|
+
f"interfaces_config/{interface}/constants/gmfSearchOptions/datasources[{ds_index}]/"
|
370
|
+
f"groupActions[{a_index}]/title"
|
371
|
+
)
|
372
|
+
assert action["title"] is not None
|
373
|
+
interfaces_messages.append(
|
374
|
+
Message(None, action["title"], None, [], "", "", (filename, location))
|
375
|
+
)
|
376
|
+
|
377
|
+
for merge_tab in (
|
378
|
+
interface_config.get("constants", {})
|
379
|
+
.get("gmfDisplayQueryGridOptions", {})
|
380
|
+
.get("mergeTabs", {})
|
381
|
+
.keys()
|
382
|
+
):
|
383
|
+
location = (
|
384
|
+
f"interfaces_config/{interface}/constants/gmfDisplayQueryGridOptions/"
|
385
|
+
f"mergeTabs/{merge_tab}/"
|
386
|
+
)
|
387
|
+
assert merge_tab is not None
|
388
|
+
interfaces_messages.append(Message(None, merge_tab, None, [], "", "", (filename, location)))
|
389
|
+
|
390
|
+
return raster + enums + metadata_list + interfaces_messages
|
286
391
|
|
287
392
|
@staticmethod
|
288
|
-
def _enumerate_attributes_values(
|
393
|
+
def _enumerate_attributes_values(
|
394
|
+
dbsessions: Dict[str, Session], layerinfos: Dict[str, Any], fieldname: str
|
395
|
+
) -> Set[Tuple[str, ...]]:
|
289
396
|
dbname = layerinfos.get("dbsession", "dbsession")
|
290
|
-
translate = layerinfos
|
397
|
+
translate = cast(Dict[str, Any], layerinfos["attributes"]).get(fieldname, {}).get("translate", True)
|
291
398
|
if not translate:
|
292
|
-
return
|
399
|
+
return set()
|
293
400
|
try:
|
294
401
|
dbsession = dbsessions.get(dbname)
|
295
|
-
return
|
402
|
+
return Layers.query_enumerate_attribute_values(dbsession, layerinfos, fieldname)
|
296
403
|
except Exception as e:
|
297
|
-
table = layerinfos
|
404
|
+
table = cast(Dict[str, Any], layerinfos["attributes"]).get(fieldname, {}).get("table")
|
298
405
|
print(
|
299
406
|
colorize(
|
300
407
|
"ERROR! Unable to collect enumerate attributes for "
|
301
|
-
"db: {}, table: {}, column: {} ({})"
|
302
|
-
RED,
|
408
|
+
f"db: {dbname}, table: {table}, column: {fieldname} ({e!s})",
|
409
|
+
Color.RED,
|
303
410
|
)
|
304
411
|
)
|
305
|
-
if
|
306
|
-
return
|
412
|
+
if _get_config_str("IGNORE_I18N_ERRORS", "FALSE") == "TRUE":
|
413
|
+
return set()
|
307
414
|
raise
|
308
415
|
|
309
416
|
@staticmethod
|
310
|
-
def _collect_print_config(print_config, filename):
|
417
|
+
def _collect_print_config(print_config: Dict[str, Any], filename: str) -> List[Message]:
|
311
418
|
result = []
|
312
|
-
for template_ in list(print_config.get("templates").keys()):
|
313
|
-
|
314
|
-
|
315
|
-
|
419
|
+
for template_ in list(cast(Dict[str, Any], print_config.get("templates")).keys()):
|
420
|
+
assert template_ is not None
|
421
|
+
result.append(Message(None, template_, None, [], "", "", (filename, f"template/{template_}")))
|
422
|
+
assert not [
|
423
|
+
attribute
|
424
|
+
for attribute in list(print_config["templates"][template_]["attributes"].keys())
|
425
|
+
if attribute is None
|
426
|
+
]
|
316
427
|
result += [
|
317
428
|
Message(
|
318
429
|
None,
|
@@ -321,22 +432,20 @@ class GeomapfishConfigExtractor(Extractor): # pragma: no cover
|
|
321
432
|
[],
|
322
433
|
"",
|
323
434
|
"",
|
324
|
-
(filename, "template/{}/{}"
|
435
|
+
(filename, f"template/{template_}/{attribute}"),
|
325
436
|
)
|
326
437
|
for attribute in list(print_config["templates"][template_]["attributes"].keys())
|
327
438
|
]
|
328
439
|
return result
|
329
440
|
|
330
441
|
|
331
|
-
class GeomapfishThemeExtractor(Extractor): #
|
332
|
-
"""
|
333
|
-
GeoMapFish theme extractor
|
334
|
-
"""
|
442
|
+
class GeomapfishThemeExtractor(Extractor): # type: ignore
|
443
|
+
"""GeoMapFish theme extractor."""
|
335
444
|
|
336
445
|
# Run on the development.ini file
|
337
446
|
extensions = [".ini"]
|
338
|
-
featuretype_cache: Dict[str, Optional[Dict]] = {}
|
339
|
-
|
447
|
+
featuretype_cache: Dict[str, Optional[Dict[str, Any]]] = {}
|
448
|
+
wms_capabilities_cache: Dict[str, WebMapService] = {}
|
340
449
|
|
341
450
|
def __init__(self) -> None:
|
342
451
|
super().__init__()
|
@@ -347,33 +456,53 @@ class GeomapfishThemeExtractor(Extractor): # pragma: no cover
|
|
347
456
|
self.config = None
|
348
457
|
self.env = None
|
349
458
|
|
350
|
-
def __call__(
|
459
|
+
def __call__(
|
460
|
+
self, filename: str, options: Dict[str, Any], fileobj: Optional[str] = None, lineno: int = 0
|
461
|
+
) -> List[Message]:
|
351
462
|
del fileobj, lineno
|
463
|
+
|
464
|
+
print(f"Running {self.__class__.__name__} on {filename}")
|
465
|
+
|
352
466
|
messages: List[Message] = []
|
353
467
|
|
354
468
|
try:
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
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()
|
360
473
|
|
361
474
|
try:
|
362
475
|
from c2cgeoportal_commons.models.main import ( # pylint: disable=import-outside-toplevel
|
363
|
-
|
476
|
+
FullTextSearch,
|
364
477
|
LayerGroup,
|
365
478
|
LayerWMS,
|
366
479
|
LayerWMTS,
|
367
|
-
|
480
|
+
Theme,
|
368
481
|
)
|
369
482
|
|
370
|
-
self._import(Theme, messages)
|
371
|
-
self._import(
|
372
|
-
|
373
|
-
|
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
|
+
)
|
374
502
|
|
375
503
|
for (layer_name,) in db_session.query(FullTextSearch.layer_name).distinct().all():
|
376
504
|
if layer_name is not None and layer_name != "":
|
505
|
+
assert layer_name is not None
|
377
506
|
messages.append(
|
378
507
|
Message(
|
379
508
|
None,
|
@@ -389,6 +518,7 @@ class GeomapfishThemeExtractor(Extractor): # pragma: no cover
|
|
389
518
|
for (actions,) in db_session.query(FullTextSearch.actions).distinct().all():
|
390
519
|
if actions is not None and actions != "":
|
391
520
|
for action in actions:
|
521
|
+
assert action["data"] is not None
|
392
522
|
messages.append(
|
393
523
|
Message(
|
394
524
|
None,
|
@@ -405,59 +535,75 @@ class GeomapfishThemeExtractor(Extractor): # pragma: no cover
|
|
405
535
|
colorize(
|
406
536
|
"ERROR! The database is probably not up to date "
|
407
537
|
"(should be ignored when happen during the upgrade)",
|
408
|
-
RED,
|
538
|
+
Color.RED,
|
409
539
|
)
|
410
540
|
)
|
411
|
-
print(colorize(e, RED))
|
412
|
-
if
|
541
|
+
print(colorize(e, Color.RED))
|
542
|
+
if _get_config_str("IGNORE_I18N_ERRORS", "FALSE") != "TRUE":
|
413
543
|
raise
|
414
544
|
except NoSuchTableError as e:
|
415
545
|
print(
|
416
546
|
colorize(
|
417
547
|
"ERROR! The schema didn't seem to exists "
|
418
548
|
"(should be ignored when happen during the deploy)",
|
419
|
-
RED,
|
549
|
+
Color.RED,
|
420
550
|
)
|
421
551
|
)
|
422
|
-
print(colorize(e, RED))
|
423
|
-
if
|
552
|
+
print(colorize(e, Color.RED))
|
553
|
+
if _get_config_str("IGNORE_I18N_ERRORS", "FALSE") != "TRUE":
|
424
554
|
raise
|
425
555
|
except OperationalError as e:
|
426
556
|
print(
|
427
557
|
colorize(
|
428
558
|
"ERROR! The database didn't seem to exists "
|
429
559
|
"(should be ignored when happen during the deploy)",
|
430
|
-
RED,
|
560
|
+
Color.RED,
|
431
561
|
)
|
432
562
|
)
|
433
|
-
print(colorize(e, RED))
|
434
|
-
if
|
563
|
+
print(colorize(e, Color.RED))
|
564
|
+
if _get_config_str("IGNORE_I18N_ERRORS", "FALSE") != "TRUE":
|
435
565
|
raise
|
436
566
|
|
437
567
|
return messages
|
438
568
|
|
439
569
|
@staticmethod
|
440
|
-
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:
|
441
577
|
from c2cgeoportal_commons.models import DBSession # pylint: disable=import-outside-toplevel
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
578
|
+
from c2cgeoportal_commons.models.main import Interface # pylint: disable=import-outside-toplevel
|
579
|
+
|
580
|
+
filter_re = re.compile(name_regex)
|
581
|
+
|
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():
|
589
|
+
assert item.name is not None
|
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
|
+
)
|
454
601
|
)
|
455
|
-
)
|
456
602
|
|
457
603
|
if callback is not None:
|
458
604
|
callback(item, messages)
|
459
605
|
|
460
|
-
def _import_layer_wms(self, layer, messages):
|
606
|
+
def _import_layer_wms(self, layer: "main.Layer", messages: List[str]) -> None:
|
461
607
|
server = layer.ogc_server
|
462
608
|
url = server.url_wfs or server.url
|
463
609
|
if url is None:
|
@@ -481,6 +627,7 @@ class GeomapfishThemeExtractor(Extractor): # pragma: no cover
|
|
481
627
|
name = column.name + "_"
|
482
628
|
else:
|
483
629
|
name = column_property.key
|
630
|
+
assert name is not None
|
484
631
|
messages.append(
|
485
632
|
Message(
|
486
633
|
None,
|
@@ -495,14 +642,15 @@ class GeomapfishThemeExtractor(Extractor): # pragma: no cover
|
|
495
642
|
except NoSuchTableError:
|
496
643
|
print(
|
497
644
|
colorize(
|
498
|
-
"ERROR! No such table '{}' for layer '{}'."
|
645
|
+
f"ERROR! No such table '{layer.geo_table}' for layer '{layer.name}'.",
|
646
|
+
Color.RED,
|
499
647
|
)
|
500
648
|
)
|
501
|
-
print(colorize(traceback.format_exc(), RED))
|
502
|
-
if
|
649
|
+
print(colorize(traceback.format_exc(), Color.RED))
|
650
|
+
if _get_config_str("IGNORE_I18N_ERRORS", "FALSE") != "TRUE":
|
503
651
|
raise
|
504
652
|
|
505
|
-
def _import_layer_wmts(self, layer, messages):
|
653
|
+
def _import_layer_wmts(self, layer: "main.Layer", messages: List[str]) -> None:
|
506
654
|
from c2cgeoportal_commons.models import DBSession # pylint: disable=import-outside-toplevel
|
507
655
|
from c2cgeoportal_commons.models.main import OGCServer # pylint: disable=import-outside-toplevel
|
508
656
|
|
@@ -511,7 +659,7 @@ class GeomapfishThemeExtractor(Extractor): # pragma: no cover
|
|
511
659
|
layers = [d.value for d in layer.metadatas if d.name == "wmsLayer"]
|
512
660
|
server = [d.value for d in layer.metadatas if d.name == "ogcServer"]
|
513
661
|
if server and layers:
|
514
|
-
layers = [
|
662
|
+
layers = [layer for ls in layers for layer in ls.split(",")]
|
515
663
|
for wms_layer in layers:
|
516
664
|
try:
|
517
665
|
db_server = DBSession.query(OGCServer).filter(OGCServer.name == server[0]).one()
|
@@ -526,18 +674,20 @@ class GeomapfishThemeExtractor(Extractor): # pragma: no cover
|
|
526
674
|
except NoResultFound:
|
527
675
|
print(
|
528
676
|
colorize(
|
529
|
-
"ERROR! the OGC server '{}' from the
|
530
|
-
|
531
|
-
|
532
|
-
RED,
|
677
|
+
f"ERROR! the OGC server '{server[0]}' from the "
|
678
|
+
f"WMTS layer '{layer.name}' does not exist.",
|
679
|
+
Color.RED,
|
533
680
|
)
|
534
681
|
)
|
535
|
-
if
|
682
|
+
if _get_config_str("IGNORE_I18N_ERRORS", "FALSE") != "TRUE":
|
536
683
|
raise
|
537
684
|
|
538
|
-
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:
|
539
688
|
attributes, layers = self._layer_attributes(url, layer)
|
540
689
|
for sub_layer in layers:
|
690
|
+
assert sub_layer is not None
|
541
691
|
messages.append(
|
542
692
|
Message(
|
543
693
|
None,
|
@@ -550,6 +700,7 @@ class GeomapfishThemeExtractor(Extractor): # pragma: no cover
|
|
550
700
|
)
|
551
701
|
)
|
552
702
|
for attribute in attributes:
|
703
|
+
assert attribute is not None
|
553
704
|
messages.append(
|
554
705
|
Message(
|
555
706
|
None,
|
@@ -562,105 +713,151 @@ class GeomapfishThemeExtractor(Extractor): # pragma: no cover
|
|
562
713
|
)
|
563
714
|
)
|
564
715
|
|
565
|
-
def _build_url(self, url):
|
566
|
-
|
567
|
-
hostname = url_split.hostname
|
716
|
+
def _build_url(self, url: Url) -> Tuple[Url, Dict[str, str], Dict[str, Any]]:
|
717
|
+
hostname = url.hostname
|
568
718
|
host_map = self.config.get("lingua_extractor", {}).get("host_map", {})
|
569
719
|
if hostname in host_map:
|
570
720
|
map_ = host_map[hostname]
|
571
721
|
if "netloc" in map_:
|
572
|
-
|
722
|
+
url.netloc = map_["netloc"]
|
573
723
|
if "scheme" in map_:
|
574
|
-
|
724
|
+
url.scheme = map_["scheme"]
|
575
725
|
kwargs = {"verify": map_["verify"]} if "verify" in map_ else {}
|
576
|
-
return
|
726
|
+
return url, map_.get("headers", {}), kwargs
|
577
727
|
return url, {}, {}
|
578
728
|
|
579
|
-
def _layer_attributes(self, url, layer):
|
729
|
+
def _layer_attributes(self, url: str, layer: str) -> Tuple[List[str], List[str]]:
|
580
730
|
errors: Set[str] = set()
|
581
731
|
|
582
|
-
request =
|
583
|
-
request
|
732
|
+
request = pyramid.threadlocal.get_current_request()
|
733
|
+
if request is None:
|
734
|
+
request = _Request()
|
735
|
+
request.registry.settings = self.config
|
736
|
+
|
584
737
|
# Static schema will not be supported
|
585
|
-
|
738
|
+
url_obj_ = get_url2("Layer", url, request, errors)
|
586
739
|
if errors:
|
587
740
|
print("\n".join(errors))
|
588
741
|
return [], []
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
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()
|
597
764
|
)
|
598
765
|
try:
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
766
|
+
rendered_headers = " ".join(
|
767
|
+
[
|
768
|
+
f"{h}={v if h not in ('Authorization', 'Cookies') else '***'}"
|
769
|
+
for h, v in headers.items()
|
770
|
+
]
|
603
771
|
)
|
772
|
+
print(f"Get WMS GetCapabilities for URL {wms_getcap_url},\nwith headers: {rendered_headers}")
|
604
773
|
response = requests.get(wms_getcap_url, headers=headers, **kwargs)
|
605
774
|
|
606
|
-
|
607
|
-
|
608
|
-
|
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:
|
609
791
|
print(
|
610
792
|
colorize(
|
611
|
-
"ERROR!
|
612
|
-
|
793
|
+
f"ERROR! Unable to GetCapabilities from URL: {wms_getcap_url},\n"
|
794
|
+
f"with headers: {rendered_headers}",
|
795
|
+
Color.RED,
|
613
796
|
)
|
614
797
|
)
|
615
|
-
print(
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
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
|
+
)
|
621
809
|
print(
|
622
810
|
colorize(
|
623
|
-
"ERROR! Unable to GetCapabilities from URL: {},\
|
624
|
-
|
625
|
-
|
626
|
-
RED,
|
811
|
+
f"ERROR! Unable to GetCapabilities from URL: {wms_getcap_url},\n"
|
812
|
+
f"with headers: {rendered_headers}",
|
813
|
+
Color.RED,
|
627
814
|
)
|
628
815
|
)
|
629
|
-
if
|
816
|
+
if _get_config_str("IGNORE_I18N_ERRORS", "FALSE") != "TRUE":
|
630
817
|
raise
|
631
818
|
|
632
|
-
|
819
|
+
wms_capabilities = self.wms_capabilities_cache[url]
|
633
820
|
|
634
821
|
if url not in self.featuretype_cache:
|
635
|
-
print("Get WFS DescribeFeatureType for URL: {}"
|
822
|
+
print(f"Get WFS DescribeFeatureType for URL: {url_obj}")
|
636
823
|
self.featuretype_cache[url] = None
|
637
824
|
|
638
|
-
|
639
|
-
|
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()
|
640
837
|
)
|
641
838
|
try:
|
642
|
-
response = requests.get(
|
643
|
-
except Exception as e:
|
644
|
-
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))
|
645
842
|
print(
|
646
843
|
colorize(
|
647
|
-
"ERROR! Unable to DescribeFeatureType from URL: {}"
|
844
|
+
f"ERROR! Unable to DescribeFeatureType from URL: {wfs_describe_feature_url}",
|
845
|
+
Color.RED,
|
648
846
|
)
|
649
847
|
)
|
650
|
-
if
|
848
|
+
if _get_config_str("IGNORE_I18N_ERRORS", "FALSE") == "TRUE":
|
651
849
|
return [], []
|
652
850
|
raise
|
653
851
|
|
654
|
-
if not response.ok:
|
852
|
+
if not response.ok:
|
655
853
|
print(
|
656
854
|
colorize(
|
657
|
-
"ERROR! DescribeFeatureType from URL {} return the error:
|
658
|
-
|
659
|
-
|
660
|
-
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,
|
661
858
|
)
|
662
859
|
)
|
663
|
-
if
|
860
|
+
if _get_config_str("IGNORE_I18N_ERRORS", "FALSE") == "TRUE":
|
664
861
|
return [], []
|
665
862
|
raise Exception("Aborted")
|
666
863
|
|
@@ -675,13 +872,13 @@ class GeomapfishThemeExtractor(Extractor): # pragma: no cover
|
|
675
872
|
except ExpatError as e:
|
676
873
|
print(
|
677
874
|
colorize(
|
678
|
-
"ERROR! an error occurred while trying to
|
679
|
-
RED,
|
875
|
+
"ERROR! an error occurred while trying to parse the DescribeFeatureType document.",
|
876
|
+
Color.RED,
|
680
877
|
)
|
681
878
|
)
|
682
|
-
print(colorize(str(e), RED))
|
683
|
-
print("URL: {}\nxml:\n{
|
684
|
-
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":
|
685
882
|
return [], []
|
686
883
|
raise
|
687
884
|
except AttributeError:
|
@@ -689,11 +886,11 @@ class GeomapfishThemeExtractor(Extractor): # pragma: no cover
|
|
689
886
|
colorize(
|
690
887
|
"ERROR! an error occurred while trying to "
|
691
888
|
"read the Mapfile and recover the themes.",
|
692
|
-
RED,
|
889
|
+
Color.RED,
|
693
890
|
)
|
694
891
|
)
|
695
|
-
print("URL: {}\nxml:\n{
|
696
|
-
if
|
892
|
+
print(f"URL: {wfs_describe_feature_url}\nxml:\n{response.text}")
|
893
|
+
if _get_config_str("IGNORE_I18N_ERRORS", "FALSE") == "TRUE":
|
697
894
|
return [], []
|
698
895
|
raise
|
699
896
|
else:
|
@@ -702,16 +899,16 @@ class GeomapfishThemeExtractor(Extractor): # pragma: no cover
|
|
702
899
|
if featurestype is None:
|
703
900
|
return [], []
|
704
901
|
|
705
|
-
layers = [layer]
|
706
|
-
if
|
707
|
-
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]
|
708
905
|
if layer_obj.layers:
|
709
|
-
layers = [
|
906
|
+
layers = [layer.name for layer in layer_obj.layers]
|
710
907
|
|
711
|
-
attributes = []
|
908
|
+
attributes: List[str] = []
|
712
909
|
for sub_layer in layers:
|
713
910
|
# Should probably be adapted for other king of servers
|
714
|
-
type_element = featurestype.get("{}Type"
|
911
|
+
type_element = featurestype.get(f"{sub_layer}Type")
|
715
912
|
if type_element is not None:
|
716
913
|
for element in type_element.getElementsByTagNameNS(
|
717
914
|
"http://www.w3.org/2001/XMLSchema", "element"
|