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