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
|
@@ -26,15 +24,18 @@
|
|
26
24
|
# The views and conclusions contained in the software and documentation are those
|
27
25
|
# of the authors and should not be interpreted as representing official policies,
|
28
26
|
# either expressed or implied, of the FreeBSD Project.
|
27
|
+
|
28
|
+
import random
|
29
29
|
import threading
|
30
30
|
import warnings
|
31
|
-
from typing import Dict,
|
31
|
+
from typing import Dict, Iterable, List, Optional, Tuple, Union
|
32
32
|
|
33
|
+
import sqlalchemy.ext.declarative
|
33
34
|
from papyrus.geo_interface import GeoInterface
|
34
35
|
from sqlalchemy import Column, Integer, MetaData, Table
|
35
36
|
from sqlalchemy.exc import SAWarning
|
36
|
-
from sqlalchemy.ext.declarative.api import DeclarativeMeta # noqa, pylint: disable=unused-import
|
37
37
|
from sqlalchemy.orm import relationship
|
38
|
+
from sqlalchemy.orm.session import Session
|
38
39
|
from sqlalchemy.orm.util import class_mapper
|
39
40
|
|
40
41
|
from c2cgeoportal_geoportal.lib.caching import get_region
|
@@ -53,13 +54,17 @@ SQL_GEOMETRY_COLUMNS = """
|
|
53
54
|
class _AssociationProxy:
|
54
55
|
# A specific "association proxy" implementation
|
55
56
|
|
56
|
-
def __init__(self, target, value_attr, nullable=True, order_by=None):
|
57
|
+
def __init__(self, target: str, value_attr: str, nullable: bool = True, order_by: Optional[str] = None):
|
57
58
|
self.target = target
|
58
59
|
self.value_attr = value_attr
|
59
60
|
self.nullable = nullable
|
60
61
|
self.order_by = order_by
|
61
62
|
|
62
|
-
def __get__(
|
63
|
+
def __get__(
|
64
|
+
self,
|
65
|
+
obj: sqlalchemy.ext.declarative.ConcreteBase,
|
66
|
+
type_: sqlalchemy.ext.declarative.DeclarativeMeta = None,
|
67
|
+
) -> Optional[Union["_AssociationProxy", str]]:
|
63
68
|
if obj is None:
|
64
69
|
# For "hybrid" descriptors that work both at the instance
|
65
70
|
# and class levels we could return an SQL expression here.
|
@@ -69,7 +74,7 @@ class _AssociationProxy:
|
|
69
74
|
target = getattr(obj, self.target)
|
70
75
|
return getattr(target, self.value_attr) if target else None
|
71
76
|
|
72
|
-
def __set__(self, obj, val):
|
77
|
+
def __set__(self, obj: str, val: str) -> None:
|
73
78
|
from c2cgeoportal_commons.models import DBSession # pylint: disable=import-outside-toplevel
|
74
79
|
|
75
80
|
o = getattr(obj, self.target)
|
@@ -83,7 +88,7 @@ class _AssociationProxy:
|
|
83
88
|
setattr(obj, self.target, o)
|
84
89
|
|
85
90
|
|
86
|
-
def _get_schema(tablename):
|
91
|
+
def _get_schema(tablename: str) -> Tuple[str, str]:
|
87
92
|
if "." in tablename:
|
88
93
|
schema, tablename = tablename.split(".", 1)
|
89
94
|
else:
|
@@ -95,7 +100,13 @@ def _get_schema(tablename):
|
|
95
100
|
_get_table_lock = threading.RLock()
|
96
101
|
|
97
102
|
|
98
|
-
def get_table(
|
103
|
+
def get_table(
|
104
|
+
tablename: str,
|
105
|
+
schema: Optional[str] = None,
|
106
|
+
session: Optional[Session] = None,
|
107
|
+
primary_key: Optional[str] = None,
|
108
|
+
) -> Table:
|
109
|
+
"""Build an SQLAlchemy table."""
|
99
110
|
if schema is None:
|
100
111
|
tablename, schema = _get_schema(tablename)
|
101
112
|
|
@@ -103,8 +114,8 @@ def get_table(tablename, schema=None, session=None, primary_key=None):
|
|
103
114
|
engine = session.bind.engine
|
104
115
|
metadata = MetaData(bind=engine)
|
105
116
|
else:
|
117
|
+
from c2cgeoportal_commons.models import Base # pylint: disable=import-outside-toplevel
|
106
118
|
from c2cgeoportal_commons.models import DBSession # pylint: disable=import-outside-toplevel
|
107
|
-
from c2cgeoportal_commons.models.main import Base # pylint: disable=import-outside-toplevel
|
108
119
|
|
109
120
|
engine = DBSession.bind.engine
|
110
121
|
metadata = Base.metadata
|
@@ -121,21 +132,21 @@ def get_table(tablename, schema=None, session=None, primary_key=None):
|
|
121
132
|
return table
|
122
133
|
|
123
134
|
|
124
|
-
@CACHE_REGION_OBJ.cache_on_arguments()
|
135
|
+
@CACHE_REGION_OBJ.cache_on_arguments() # type: ignore
|
125
136
|
def get_class(
|
126
|
-
tablename,
|
127
|
-
exclude_properties=None,
|
128
|
-
primary_key=None,
|
129
|
-
attributes_order=None,
|
130
|
-
enumerations_config=None,
|
131
|
-
readonly_attributes=None,
|
132
|
-
):
|
137
|
+
tablename: str,
|
138
|
+
exclude_properties: Optional[List[str]] = None,
|
139
|
+
primary_key: Optional[str] = None,
|
140
|
+
attributes_order: Optional[List[str]] = None,
|
141
|
+
enumerations_config: Optional[Dict[str, str]] = None,
|
142
|
+
readonly_attributes: Optional[List[str]] = None,
|
143
|
+
) -> sqlalchemy.ext.declarative.DeclarativeMeta:
|
133
144
|
"""
|
134
|
-
Get the SQLAlchemy mapped class for "tablename".
|
135
|
-
|
136
|
-
|
137
|
-
tablename in the database a NoSuchTableError SQLAlchemy
|
138
|
-
is raised.
|
145
|
+
Get the SQLAlchemy mapped class for "tablename".
|
146
|
+
|
147
|
+
If no class exists for "tablename" one is created, and added to the cache. "tablename" must reference a
|
148
|
+
valid string. If there is no table identified by tablename in the database a NoSuchTableError SQLAlchemy
|
149
|
+
exception is raised.
|
139
150
|
"""
|
140
151
|
|
141
152
|
tablename, schema = _get_schema(tablename)
|
@@ -155,25 +166,29 @@ def get_class(
|
|
155
166
|
|
156
167
|
|
157
168
|
def _create_class(
|
158
|
-
table,
|
159
|
-
exclude_properties=None,
|
160
|
-
attributes_order=None,
|
161
|
-
enumerations_config=None,
|
162
|
-
readonly_attributes=None,
|
163
|
-
pk_name=None,
|
164
|
-
):
|
165
|
-
from c2cgeoportal_commons.models
|
169
|
+
table: Table,
|
170
|
+
exclude_properties: Optional[Iterable[str]] = None,
|
171
|
+
attributes_order: Optional[List[str]] = None,
|
172
|
+
enumerations_config: Optional[Dict[str, str]] = None,
|
173
|
+
readonly_attributes: Optional[List[str]] = None,
|
174
|
+
pk_name: Optional[str] = None,
|
175
|
+
) -> sqlalchemy.ext.declarative.DeclarativeMeta:
|
176
|
+
from c2cgeoportal_commons.models import Base # pylint: disable=import-outside-toplevel
|
166
177
|
|
167
178
|
exclude_properties = exclude_properties or ()
|
168
|
-
attributes =
|
169
|
-
__table__
|
170
|
-
__mapper_args__
|
171
|
-
__attributes_order__
|
172
|
-
__enumerations_config__
|
173
|
-
|
179
|
+
attributes = {
|
180
|
+
"__table__": table,
|
181
|
+
"__mapper_args__": {"exclude_properties": exclude_properties},
|
182
|
+
"__attributes_order__": attributes_order,
|
183
|
+
"__enumerations_config__": enumerations_config,
|
184
|
+
}
|
174
185
|
if pk_name is not None:
|
175
186
|
attributes[pk_name] = Column(Integer, primary_key=True)
|
176
|
-
|
187
|
+
# The randint is to fix the SAWarning: This declarative base already contains a class with the same
|
188
|
+
# class name and module name
|
189
|
+
cls = type(
|
190
|
+
f"{table.name.capitalize()}_{random.randint(0, 9999999)}", (GeoInterface, Base), attributes # nosec
|
191
|
+
)
|
177
192
|
|
178
193
|
for col in table.columns:
|
179
194
|
if col.name in (readonly_attributes or []):
|
@@ -184,8 +199,10 @@ def _create_class(
|
|
184
199
|
return cls
|
185
200
|
|
186
201
|
|
187
|
-
def _add_association_proxy(
|
188
|
-
|
202
|
+
def _add_association_proxy(
|
203
|
+
cls: sqlalchemy.ext.declarative.DeclarativeMeta, col: sqlalchemy.sql.schema.Column
|
204
|
+
) -> None:
|
205
|
+
if len(col.foreign_keys) != 1:
|
189
206
|
raise NotImplementedError
|
190
207
|
|
191
208
|
fk = next(iter(col.foreign_keys))
|
@@ -193,10 +210,8 @@ def _add_association_proxy(cls, col):
|
|
193
210
|
child_cls = get_class(child_tablename)
|
194
211
|
|
195
212
|
try:
|
196
|
-
|
197
|
-
|
198
|
-
# fmt: on
|
199
|
-
except ValueError: # pragma: no cover
|
213
|
+
proxy = col.name[0 : col.name.rindex("_id")]
|
214
|
+
except ValueError:
|
200
215
|
proxy = col.name + "_"
|
201
216
|
|
202
217
|
# The following is a workaround for a bug in the geojson lib. The
|
@@ -204,7 +219,7 @@ def _add_association_proxy(cls, col):
|
|
204
219
|
# (this is a GeoJSON keyword), and produces a UnicodeEncodeError
|
205
220
|
# if the property includes non-ascii characters.
|
206
221
|
if proxy == "type":
|
207
|
-
proxy = "type_"
|
222
|
+
proxy = "type_"
|
208
223
|
|
209
224
|
rel = proxy + "_"
|
210
225
|
primaryjoin = getattr(cls, col.name) == getattr(child_cls, child_pk)
|
@@ -1,6 +1,4 @@
|
|
1
|
-
#
|
2
|
-
|
3
|
-
# Copyright (c) 2014-2020, Camptocamp SA
|
1
|
+
# Copyright (c) 2014-2024, Camptocamp SA
|
4
2
|
# All rights reserved.
|
5
3
|
|
6
4
|
# Redistribution and use in source and binary forms, with or without
|
@@ -30,51 +28,59 @@
|
|
30
28
|
|
31
29
|
import copy
|
32
30
|
import logging
|
33
|
-
import xml.sax.handler
|
31
|
+
import xml.sax.handler # nosec
|
34
32
|
from io import StringIO
|
35
|
-
from typing import Callable, Dict, List
|
36
|
-
from
|
37
|
-
from xml.sax.saxutils import XMLFilterBase, XMLGenerator
|
33
|
+
from typing import Any, Callable, Dict, List, Optional, Set, Union
|
34
|
+
from xml.sax.saxutils import XMLFilterBase, XMLGenerator # nosec
|
38
35
|
|
39
36
|
import defusedxml.expatreader
|
37
|
+
import pyramid.httpexceptions
|
38
|
+
import pyramid.request
|
40
39
|
import requests
|
40
|
+
from owslib.map.wms111 import ContentMetadata as ContentMetadata111
|
41
|
+
from owslib.map.wms130 import ContentMetadata as ContentMetadata130
|
41
42
|
from owslib.wms import WebMapService
|
42
43
|
from pyramid.httpexceptions import HTTPBadGateway
|
43
44
|
|
44
|
-
from c2cgeoportal_commons.lib.url import
|
45
|
+
from c2cgeoportal_commons.lib.url import Url
|
45
46
|
from c2cgeoportal_geoportal.lib import caching, get_ogc_server_wfs_url_ids, get_ogc_server_wms_url_ids
|
46
47
|
from c2cgeoportal_geoportal.lib.layers import get_private_layers, get_protected_layers, get_writable_layers
|
47
48
|
|
48
49
|
CACHE_REGION = caching.get_region("std")
|
49
50
|
LOG = logging.getLogger(__name__)
|
51
|
+
ContentMetadata = Union[ContentMetadata111, ContentMetadata130]
|
50
52
|
|
51
53
|
|
52
|
-
@CACHE_REGION.cache_on_arguments()
|
53
|
-
def wms_structure(wms_url, host, request):
|
54
|
-
|
55
|
-
|
54
|
+
@CACHE_REGION.cache_on_arguments() # type: ignore
|
55
|
+
def wms_structure(wms_url: Url, host: str, request: pyramid.request.Request) -> Dict[str, List[str]]:
|
56
|
+
"""Get a simple serializable structure of the WMS capabilities."""
|
57
|
+
url = wms_url.clone().add_query({"SERVICE": "WMS", "VERSION": "1.1.1", "REQUEST": "GetCapabilities"})
|
56
58
|
|
57
59
|
# Forward request to target (without Host Header)
|
58
|
-
headers =
|
59
|
-
if url.hostname == "localhost" and host is not None:
|
60
|
+
headers = {}
|
61
|
+
if url.hostname == "localhost" and host is not None:
|
60
62
|
headers["Host"] = host
|
61
63
|
try:
|
62
|
-
response = requests.get(
|
63
|
-
|
64
|
-
|
64
|
+
response = requests.get(
|
65
|
+
url.url(), headers=headers, **request.registry.settings.get("http_options", {})
|
66
|
+
)
|
67
|
+
except Exception:
|
68
|
+
LOG.exception("Unable to GetCapabilities from wms_url '%s'", wms_url)
|
69
|
+
raise HTTPBadGateway( # pylint: disable=raise-missing-from
|
70
|
+
"Unable to GetCapabilities, see logs for details"
|
71
|
+
)
|
65
72
|
|
66
|
-
if not response.ok:
|
73
|
+
if not response.ok:
|
67
74
|
raise HTTPBadGateway(
|
68
|
-
"GetCapabilities from wms_url {
|
69
|
-
|
70
|
-
)
|
75
|
+
f"GetCapabilities from wms_url {url.url()} return the error: "
|
76
|
+
f"{response.status_code:d} {response.reason}"
|
71
77
|
)
|
72
78
|
|
73
79
|
try:
|
74
80
|
wms = WebMapService(None, xml=response.content)
|
75
81
|
result: Dict[str, List[str]] = {}
|
76
82
|
|
77
|
-
def _fill(name, parent):
|
83
|
+
def _fill(name: str, parent: ContentMetadata) -> None:
|
78
84
|
if parent is None:
|
79
85
|
return
|
80
86
|
if parent.name not in result:
|
@@ -86,37 +92,48 @@ def wms_structure(wms_url, host, request):
|
|
86
92
|
_fill(layer.name, layer.parent)
|
87
93
|
return result
|
88
94
|
|
89
|
-
except AttributeError:
|
95
|
+
except AttributeError:
|
90
96
|
error = "WARNING! an error occurred while trying to read the mapfile and recover the themes."
|
91
|
-
error = "{
|
97
|
+
error = f"{error}\nurl: {wms_url}\nxml:\n{response.text}"
|
92
98
|
LOG.exception(error)
|
93
|
-
raise HTTPBadGateway(error)
|
99
|
+
raise HTTPBadGateway(error) # pylint: disable=raise-missing-from
|
94
100
|
|
95
|
-
except SyntaxError:
|
101
|
+
except SyntaxError:
|
96
102
|
error = "WARNING! an error occurred while trying to read the mapfile and recover the themes."
|
97
|
-
error = "{
|
103
|
+
error = f"{error}\nurl: {wms_url}\nxml:\n{response.text}"
|
98
104
|
LOG.exception(error)
|
99
|
-
raise HTTPBadGateway(error)
|
105
|
+
raise HTTPBadGateway(error) # pylint: disable=raise-missing-from
|
100
106
|
|
101
107
|
|
102
|
-
def filter_capabilities(
|
108
|
+
def filter_capabilities(
|
109
|
+
content: str, wms: bool, url: Url, headers: Dict[str, str], request: pyramid.request.Request
|
110
|
+
) -> str:
|
111
|
+
"""Filter the WMS/WFS capabilities."""
|
103
112
|
|
104
113
|
wms_structure_ = wms_structure(url, headers.get("Host"), request)
|
105
114
|
|
106
115
|
ogc_server_ids = (
|
107
|
-
get_ogc_server_wms_url_ids(request
|
108
|
-
|
116
|
+
get_ogc_server_wms_url_ids(request, request.host)
|
117
|
+
if wms
|
118
|
+
else get_ogc_server_wfs_url_ids(request, request.host)
|
119
|
+
).get(url.url())
|
109
120
|
gmf_private_layers = copy.copy(get_private_layers(ogc_server_ids))
|
110
121
|
for id_ in list(get_protected_layers(request, ogc_server_ids).keys()):
|
111
122
|
if id_ in gmf_private_layers:
|
112
123
|
del gmf_private_layers[id_]
|
113
124
|
|
114
125
|
private_layers = set()
|
115
|
-
for
|
116
|
-
for
|
117
|
-
private_layers.add(
|
118
|
-
if
|
119
|
-
private_layers.update(wms_structure_[
|
126
|
+
for gmf_layer in list(gmf_private_layers.values()):
|
127
|
+
for ogc_layer in gmf_layer.layer.split(","):
|
128
|
+
private_layers.add(ogc_layer)
|
129
|
+
if ogc_layer in wms_structure_:
|
130
|
+
private_layers.update(wms_structure_[ogc_layer])
|
131
|
+
|
132
|
+
LOG.debug(
|
133
|
+
"Filter capabilities of OGC server %s\nprivate_layers: %s",
|
134
|
+
", ".join([str(e) for e in ogc_server_ids]),
|
135
|
+
", ".join(private_layers),
|
136
|
+
)
|
120
137
|
|
121
138
|
parser = defusedxml.expatreader.create_parser(forbid_external=False)
|
122
139
|
# skip inclusion of DTDs
|
@@ -128,15 +145,27 @@ def filter_capabilities(content, wms, url, headers, request):
|
|
128
145
|
filter_handler = _CapabilitiesFilter(
|
129
146
|
parser, downstream_handler, "Layer" if wms else "FeatureType", layers_blacklist=private_layers
|
130
147
|
)
|
131
|
-
filter_handler.parse(StringIO(content))
|
148
|
+
filter_handler.parse(StringIO(content)) # type: ignore
|
132
149
|
return result.getvalue()
|
133
150
|
|
134
151
|
|
135
|
-
def filter_wfst_capabilities(content, wfs_url, request):
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
152
|
+
def filter_wfst_capabilities(content: str, wfs_url: Url, request: pyramid.request.Request) -> str:
|
153
|
+
"""Filter the WTS capabilities."""
|
154
|
+
|
155
|
+
writable_layers: Set[str] = set()
|
156
|
+
ogc_server_ids = get_ogc_server_wfs_url_ids(request, request.host).get(wfs_url.url())
|
157
|
+
if ogc_server_ids is None:
|
158
|
+
LOG.error("No OGC server found for WFS URL %s", wfs_url)
|
159
|
+
raise pyramid.httpexceptions.HTTPInternalServerError("No OGC server found for WFS URL")
|
160
|
+
|
161
|
+
for gmf_layer in list(get_writable_layers(request, ogc_server_ids).values()):
|
162
|
+
writable_layers |= set(gmf_layer.layer.split(","))
|
163
|
+
|
164
|
+
LOG.debug(
|
165
|
+
"Filter WFS-T capabilities of OGC server %s\nlayers: %s",
|
166
|
+
", ".join([str(e) for e in ogc_server_ids]),
|
167
|
+
", ".join(writable_layers),
|
168
|
+
)
|
140
169
|
|
141
170
|
parser = defusedxml.expatreader.create_parser(forbid_external=False)
|
142
171
|
# skip inclusion of DTDs
|
@@ -148,13 +177,13 @@ def filter_wfst_capabilities(content, wfs_url, request):
|
|
148
177
|
filter_handler = _CapabilitiesFilter(
|
149
178
|
parser, downstream_handler, "FeatureType", layers_whitelist=writable_layers
|
150
179
|
)
|
151
|
-
filter_handler.parse(StringIO(content))
|
180
|
+
filter_handler.parse(StringIO(content)) # type: ignore
|
152
181
|
return result.getvalue()
|
153
182
|
|
154
183
|
|
155
184
|
class _Layer:
|
156
|
-
def __init__(self, self_hidden=False):
|
157
|
-
self.
|
185
|
+
def __init__(self, self_hidden: bool = False):
|
186
|
+
self.accumulator: List[Callable[[], None]] = []
|
158
187
|
self.hidden = True
|
159
188
|
self.self_hidden = self_hidden
|
160
189
|
self.has_children = False
|
@@ -164,13 +193,20 @@ class _Layer:
|
|
164
193
|
class _CapabilitiesFilter(XMLFilterBase):
|
165
194
|
"""
|
166
195
|
SAX filter to show only the allowed layers in a GetCapabilities request.
|
167
|
-
|
168
|
-
|
169
|
-
is given) or is not part of the set `layers_whitelist` (when
|
196
|
+
|
197
|
+
The filter removes elements of type `tag_name` where the `name` attribute is part of the set
|
198
|
+
`layers_blacklist` (when `layers_blacklist` is given) or is not part of the set `layers_whitelist` (when
|
170
199
|
`layers_whitelist` is given).
|
171
200
|
"""
|
172
201
|
|
173
|
-
def __init__(
|
202
|
+
def __init__(
|
203
|
+
self,
|
204
|
+
upstream: XMLFilterBase,
|
205
|
+
downstream: XMLGenerator,
|
206
|
+
tag_name: str,
|
207
|
+
layers_blacklist: Optional[Set[str]] = None,
|
208
|
+
layers_whitelist: Optional[Set[str]] = None,
|
209
|
+
):
|
174
210
|
XMLFilterBase.__init__(self, upstream)
|
175
211
|
self._downstream = downstream
|
176
212
|
self._accumulator: List[str] = []
|
@@ -183,9 +219,9 @@ class _CapabilitiesFilter(XMLFilterBase):
|
|
183
219
|
), "only either layers_blacklist OR layers_whitelist can be set"
|
184
220
|
|
185
221
|
if layers_blacklist is not None:
|
186
|
-
layers_blacklist =
|
222
|
+
layers_blacklist = {layer.lower() for layer in layers_blacklist}
|
187
223
|
if layers_whitelist is not None:
|
188
|
-
layers_whitelist =
|
224
|
+
layers_whitelist = {layer.lower() for layer in layers_whitelist}
|
189
225
|
self.layers_blacklist = layers_blacklist
|
190
226
|
self.layers_whitelist = layers_whitelist
|
191
227
|
|
@@ -196,39 +232,39 @@ class _CapabilitiesFilter(XMLFilterBase):
|
|
196
232
|
|
197
233
|
def _complete_text_node(self) -> None:
|
198
234
|
if self._accumulator:
|
199
|
-
self._downstream.characters("".join(self._accumulator))
|
235
|
+
self._downstream.characters("".join(self._accumulator)) # type: ignore
|
200
236
|
self._accumulator = []
|
201
237
|
|
202
|
-
def _do(self, action):
|
238
|
+
def _do(self, action: Callable[[], Any]) -> None:
|
203
239
|
if self.layers_path:
|
204
|
-
self.layers_path[-1].
|
240
|
+
self.layers_path[-1].accumulator.append(action)
|
205
241
|
else:
|
206
242
|
self._complete_text_node()
|
207
243
|
action()
|
208
244
|
|
209
|
-
def _add_child(self, layer):
|
245
|
+
def _add_child(self, layer: _Layer) -> None:
|
210
246
|
if not layer.hidden and not (layer.has_children and layer.children_nb == 0):
|
211
|
-
for action in layer.
|
247
|
+
for action in layer.accumulator:
|
212
248
|
self._complete_text_node()
|
213
249
|
action()
|
214
|
-
layer.
|
250
|
+
layer.accumulator = []
|
215
251
|
|
216
|
-
def setDocumentLocator(self, locator): # noqa
|
217
|
-
self._downstream.setDocumentLocator(locator)
|
252
|
+
def setDocumentLocator(self, locator: str) -> None: # noqa: ignore=N802
|
253
|
+
self._downstream.setDocumentLocator(locator) # type: ignore
|
218
254
|
|
219
|
-
def startDocument(self): # noqa
|
220
|
-
self._downstream.startDocument()
|
255
|
+
def startDocument(self) -> None: # noqa: ignore=N802
|
256
|
+
self._downstream.startDocument() # type: ignore
|
221
257
|
|
222
|
-
def endDocument(self): # noqa
|
223
|
-
self._downstream.endDocument()
|
258
|
+
def endDocument(self) -> None: # noqa: ignore=N802
|
259
|
+
self._downstream.endDocument() # type: ignore
|
224
260
|
|
225
|
-
def startPrefixMapping(self, prefix, uri
|
226
|
-
self._downstream.startPrefixMapping(prefix, uri)
|
261
|
+
def startPrefixMapping(self, prefix: str, uri: str) -> None: # noqa: ignore=N802
|
262
|
+
self._downstream.startPrefixMapping(prefix, uri) # type: ignore
|
227
263
|
|
228
|
-
def endPrefixMapping(self, prefix
|
229
|
-
self._downstream.endPrefixMapping(prefix)
|
264
|
+
def endPrefixMapping(self, prefix: str) -> None: # noqa: ignore=N802
|
265
|
+
self._downstream.endPrefixMapping(prefix) # type: ignore
|
230
266
|
|
231
|
-
def startElement(self, name, attrs): # noqa
|
267
|
+
def startElement(self, name: str, attrs: Dict[str, str]) -> None: # noqa: ignore=N802
|
232
268
|
if name == self.tag_name:
|
233
269
|
self.level += 1
|
234
270
|
if self.layers_path:
|
@@ -238,40 +274,40 @@ class _CapabilitiesFilter(XMLFilterBase):
|
|
238
274
|
layer = _Layer(parent_layer.self_hidden) if len(self.layers_path) > 1 else _Layer()
|
239
275
|
self.layers_path.append(layer)
|
240
276
|
|
241
|
-
parent_layer.
|
277
|
+
parent_layer.accumulator.append(lambda: self._add_child(layer))
|
242
278
|
else:
|
243
279
|
layer = _Layer()
|
244
280
|
self.layers_path.append(layer)
|
245
281
|
elif name == "Name" and self.layers_path:
|
246
282
|
self.in_name = True
|
247
283
|
|
248
|
-
self._do(lambda: self._downstream.startElement(name, attrs))
|
284
|
+
self._do(lambda: self._downstream.startElement(name, attrs)) # type: ignore
|
249
285
|
|
250
|
-
def endElement(self, name): # noqa
|
251
|
-
self._do(lambda: self._downstream.endElement(name))
|
286
|
+
def endElement(self, name: str) -> None: # noqa: ignore=N802
|
287
|
+
self._do(lambda: self._downstream.endElement(name)) # type: ignore
|
252
288
|
|
253
289
|
if name == self.tag_name:
|
254
290
|
self.level -= 1
|
255
291
|
if self.level == 0 and not self.layers_path[0].hidden:
|
256
|
-
for action in self.layers_path[0].
|
292
|
+
for action in self.layers_path[0].accumulator:
|
257
293
|
self._complete_text_node()
|
258
294
|
action()
|
259
295
|
self.layers_path.pop()
|
260
296
|
elif name == "Name":
|
261
297
|
self.in_name = False
|
262
298
|
|
263
|
-
def startElementNS(self, name, qname, attrs
|
264
|
-
self._do(lambda: self._downstream.startElementNS(name, qname, attrs))
|
299
|
+
def startElementNS(self, name: str, qname: str, attrs: Dict[str, str]) -> None: # noqa: ignore=N802
|
300
|
+
self._do(lambda: self._downstream.startElementNS(name, qname, attrs)) # type: ignore
|
265
301
|
|
266
|
-
def endElementNS(self, name, qname
|
267
|
-
self._do(lambda: self._downstream.endElementNS(name, qname))
|
302
|
+
def endElementNS(self, name: str, qname: str) -> None: # noqa: ignore=N802
|
303
|
+
self._do(lambda: self._downstream.endElementNS(name, qname)) # type: ignore
|
268
304
|
|
269
|
-
def _keep_layer(self, layer_name):
|
305
|
+
def _keep_layer(self, layer_name: str) -> bool:
|
270
306
|
return (self.layers_blacklist is not None and layer_name not in self.layers_blacklist) or (
|
271
307
|
self.layers_whitelist is not None and layer_name in self.layers_whitelist
|
272
308
|
)
|
273
309
|
|
274
|
-
def characters(self, content):
|
310
|
+
def characters(self, content: str) -> None:
|
275
311
|
if self.in_name and self.layers_path and not self.layers_path[-1].self_hidden is True:
|
276
312
|
layer_name = normalize_typename(content)
|
277
313
|
if self._keep_layer(layer_name):
|
@@ -285,19 +321,20 @@ class _CapabilitiesFilter(XMLFilterBase):
|
|
285
321
|
|
286
322
|
self._do(lambda: self._accumulator.append(content))
|
287
323
|
|
288
|
-
def ignorableWhitespace(self, chars
|
324
|
+
def ignorableWhitespace(self, chars: str) -> None: # noqa: ignore=N802
|
289
325
|
self._do(lambda: self._accumulator.append(chars))
|
290
326
|
|
291
|
-
def processingInstruction(self, target, data
|
292
|
-
self._do(lambda: self._downstream.processingInstruction(target, data))
|
327
|
+
def processingInstruction(self, target: str, data: str) -> None: # noqa: ignore=N802
|
328
|
+
self._do(lambda: self._downstream.processingInstruction(target, data)) # type: ignore
|
293
329
|
|
294
|
-
def skippedEntity(self, name
|
295
|
-
self._downstream.skippedEntity(name)
|
330
|
+
def skippedEntity(self, name: str) -> None: # noqa: ignore=N802
|
331
|
+
self._downstream.skippedEntity(name) # type: ignore
|
296
332
|
|
297
333
|
|
298
|
-
def normalize_tag(tag):
|
334
|
+
def normalize_tag(tag: str) -> str:
|
299
335
|
"""
|
300
|
-
|
336
|
+
Drop the namespace from a tag and converts to lower case.
|
337
|
+
|
301
338
|
e.g. '{https://....}TypeName' -> 'TypeName'
|
302
339
|
"""
|
303
340
|
normalized = tag
|
@@ -307,9 +344,10 @@ def normalize_tag(tag):
|
|
307
344
|
return normalized.lower()
|
308
345
|
|
309
346
|
|
310
|
-
def normalize_typename(typename):
|
347
|
+
def normalize_typename(typename: str) -> str:
|
311
348
|
"""
|
312
|
-
|
349
|
+
Drop the namespace from a type name and converts to lower case.
|
350
|
+
|
313
351
|
e.g. 'tows:parks' -> 'parks'
|
314
352
|
"""
|
315
353
|
normalized = typename
|
@@ -1,6 +1,4 @@
|
|
1
|
-
#
|
2
|
-
|
3
|
-
# Copyright (c) 2020, Camptocamp SA
|
1
|
+
# Copyright (c) 2020-2021, Camptocamp SA
|
4
2
|
# All rights reserved.
|
5
3
|
|
6
4
|
# Redistribution and use in source and binary forms, with or without
|
@@ -28,10 +26,13 @@
|
|
28
26
|
# either expressed or implied, of the FreeBSD Project.
|
29
27
|
|
30
28
|
import re
|
29
|
+
from typing import Any, Dict
|
31
30
|
|
32
31
|
|
33
32
|
class Normalize:
|
34
|
-
|
33
|
+
"""Normalize a text for the Full text search."""
|
34
|
+
|
35
|
+
def __init__(self, config: Dict[str, Any]) -> None:
|
35
36
|
split = config.get("split_regex")
|
36
37
|
self.split_re = re.compile(split) if split is not None else None
|
37
38
|
|
@@ -39,7 +40,7 @@ class Normalize:
|
|
39
40
|
for search_regex, text in config.get("replace", {}).items():
|
40
41
|
self.word_replace.append((re.compile(search_regex), text))
|
41
42
|
|
42
|
-
def __call__(self, text):
|
43
|
+
def __call__(self, text: str) -> str:
|
43
44
|
if self.split_re is not None:
|
44
45
|
text = " ".join(self.split_re.split(text))
|
45
46
|
|