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) 2012-2021, Camptocamp SA
|
1
|
+
# Copyright (c) 2012-2023, Camptocamp SA
|
4
2
|
# All rights reserved.
|
5
3
|
|
6
4
|
# Redistribution and use in source and binary forms, with or without
|
@@ -31,9 +29,13 @@ import json
|
|
31
29
|
import logging
|
32
30
|
import os
|
33
31
|
from datetime import datetime
|
34
|
-
from typing import Dict, List,
|
32
|
+
from typing import TYPE_CHECKING, Any, Dict, Generator, List, Optional, Set, Tuple, TypedDict, cast
|
35
33
|
|
36
|
-
import geojson
|
34
|
+
import geojson.geometry
|
35
|
+
import pyramid.request
|
36
|
+
import pyramid.response
|
37
|
+
import shapely.geometry
|
38
|
+
import sqlalchemy.ext.declarative
|
37
39
|
from geoalchemy2 import Geometry
|
38
40
|
from geoalchemy2 import func as ga_func
|
39
41
|
from geoalchemy2.shape import from_shape, to_shape
|
@@ -48,10 +50,9 @@ from pyramid.httpexceptions import (
|
|
48
50
|
HTTPNotFound,
|
49
51
|
)
|
50
52
|
from pyramid.view import view_config
|
51
|
-
from shapely.
|
52
|
-
from shapely.geos import TopologicalError
|
53
|
+
from shapely.errors import TopologicalError
|
53
54
|
from shapely.ops import cascaded_union
|
54
|
-
from sqlalchemy import Enum, Numeric, String, Text, Unicode, UnicodeText,
|
55
|
+
from sqlalchemy import Enum, Numeric, String, Text, Unicode, UnicodeText, exc, func
|
55
56
|
from sqlalchemy.orm.exc import MultipleResultsFound, NoResultFound
|
56
57
|
from sqlalchemy.orm.properties import ColumnProperty
|
57
58
|
from sqlalchemy.orm.util import class_mapper
|
@@ -59,47 +60,50 @@ from sqlalchemy.sql import and_, or_
|
|
59
60
|
|
60
61
|
from c2cgeoportal_commons import models
|
61
62
|
from c2cgeoportal_geoportal.lib import get_roles_id
|
62
|
-
from c2cgeoportal_geoportal.lib.caching import
|
63
|
-
|
64
|
-
PRIVATE_CACHE,
|
65
|
-
PUBLIC_CACHE,
|
66
|
-
get_region,
|
67
|
-
set_common_headers,
|
68
|
-
)
|
63
|
+
from c2cgeoportal_geoportal.lib.caching import get_region
|
64
|
+
from c2cgeoportal_geoportal.lib.common_headers import Cache, set_common_headers
|
69
65
|
from c2cgeoportal_geoportal.lib.dbreflection import _AssociationProxy, get_class, get_table
|
70
66
|
|
67
|
+
if TYPE_CHECKING:
|
68
|
+
from c2cgeoportal_commons.models import main # pylint: disable=ungrouped-imports.useless-suppression
|
71
69
|
LOG = logging.getLogger(__name__)
|
72
70
|
CACHE_REGION = get_region("std")
|
73
71
|
|
74
72
|
|
75
73
|
class Layers:
|
76
|
-
|
74
|
+
"""
|
75
|
+
All the layers view (editing).
|
76
|
+
|
77
|
+
Mapfish protocol implementation
|
78
|
+
"""
|
79
|
+
|
80
|
+
def __init__(self, request: pyramid.request.Request):
|
77
81
|
self.request = request
|
78
82
|
self.settings = request.registry.settings.get("layers", {})
|
79
83
|
self.layers_enum_config = self.settings.get("enum")
|
80
84
|
|
81
85
|
@staticmethod
|
82
|
-
def _get_geom_col_info(layer):
|
83
|
-
"""
|
84
|
-
|
85
|
-
|
86
|
+
def _get_geom_col_info(layer: "main.Layer") -> Tuple[str, int]:
|
87
|
+
"""
|
88
|
+
Return information about the layer's geometry column.
|
89
|
+
|
90
|
+
Namely a ``(name, srid)`` tuple, where ``name`` is the name of the geometry column,
|
91
|
+
and ``srid`` its srid.
|
86
92
|
|
87
|
-
This function assumes that the names of geometry attributes
|
88
|
-
|
93
|
+
This function assumes that the names of geometry attributes in the mapped class are the same as those
|
94
|
+
of geometry columns.
|
89
95
|
"""
|
90
96
|
mapped_class = get_layer_class(layer)
|
91
97
|
for p in class_mapper(mapped_class).iterate_properties:
|
92
98
|
if not isinstance(p, ColumnProperty):
|
93
|
-
continue
|
99
|
+
continue
|
94
100
|
col = p.columns[0]
|
95
101
|
if isinstance(col.type, Geometry):
|
96
102
|
return col.name, col.type.srid
|
97
|
-
raise HTTPInternalServerError(
|
98
|
-
'Failed getting geometry column info for table "{0!s}".'.format(layer.geo_table)
|
99
|
-
) # pragma: no cover
|
103
|
+
raise HTTPInternalServerError(f'Failed getting geometry column info for table "{layer.geo_table!s}".')
|
100
104
|
|
101
105
|
@staticmethod
|
102
|
-
def _get_layer(layer_id):
|
106
|
+
def _get_layer(layer_id: int) -> "main.Layer":
|
103
107
|
"""Return a ``Layer`` object for ``layer_id``."""
|
104
108
|
from c2cgeoportal_commons.models.main import Layer # pylint: disable=import-outside-toplevel
|
105
109
|
|
@@ -109,16 +113,19 @@ class Layers:
|
|
109
113
|
query = query.filter(Layer.id == layer_id)
|
110
114
|
layer, geo_table = query.one()
|
111
115
|
except NoResultFound:
|
112
|
-
raise HTTPNotFound("Layer {
|
113
|
-
except MultipleResultsFound:
|
114
|
-
raise HTTPInternalServerError("Too many layers found with id {
|
115
|
-
if not geo_table:
|
116
|
-
raise HTTPNotFound("Layer {
|
117
|
-
return layer
|
118
|
-
|
119
|
-
def _get_layers_for_request(self):
|
120
|
-
"""
|
121
|
-
|
116
|
+
raise HTTPNotFound(f"Layer {layer_id:d} not found") from None
|
117
|
+
except MultipleResultsFound:
|
118
|
+
raise HTTPInternalServerError(f"Too many layers found with id {layer_id:d}") from None
|
119
|
+
if not geo_table:
|
120
|
+
raise HTTPNotFound(f"Layer {layer_id:d} has no geo table")
|
121
|
+
return cast("main.Layer", layer)
|
122
|
+
|
123
|
+
def _get_layers_for_request(self) -> Generator["main.Layer", None, None]:
|
124
|
+
"""
|
125
|
+
Get a generator function that yields ``Layer`` objects.
|
126
|
+
|
127
|
+
Based on the layer ids found in the ``layer_id`` matchdict.
|
128
|
+
"""
|
122
129
|
try:
|
123
130
|
layer_ids = (
|
124
131
|
int(layer_id) for layer_id in self.request.matchdict["layer_id"].split(",") if layer_id
|
@@ -126,28 +133,26 @@ class Layers:
|
|
126
133
|
for layer_id in layer_ids:
|
127
134
|
yield self._get_layer(layer_id)
|
128
135
|
except ValueError:
|
129
|
-
raise HTTPBadRequest(
|
130
|
-
"A Layer id in '{
|
131
|
-
)
|
136
|
+
raise HTTPBadRequest( # pylint: disable=raise-missing-from
|
137
|
+
f"A Layer id in '{self.request.matchdict['layer_id']}' is not an integer"
|
138
|
+
)
|
132
139
|
|
133
|
-
def _get_layer_for_request(self):
|
134
|
-
"""Return a ``Layer`` object for the first layer id found
|
135
|
-
in the ``layer_id`` matchdict."""
|
140
|
+
def _get_layer_for_request(self) -> "main.Layer":
|
141
|
+
"""Return a ``Layer`` object for the first layer id found in the ``layer_id`` matchdict."""
|
136
142
|
return next(self._get_layers_for_request())
|
137
143
|
|
138
|
-
def _get_protocol_for_layer(self, layer, **kwargs):
|
139
|
-
"""
|
144
|
+
def _get_protocol_for_layer(self, layer: "main.Layer", **kwargs: Any) -> Protocol:
|
145
|
+
"""Return a papyrus ``Protocol`` for the ``Layer`` object."""
|
140
146
|
cls = get_layer_class(layer)
|
141
147
|
geom_attr = self._get_geom_col_info(layer)[0]
|
142
148
|
return Protocol(models.DBSession, cls, geom_attr, **kwargs)
|
143
149
|
|
144
|
-
def _get_protocol_for_request(self, **kwargs):
|
145
|
-
"""
|
146
|
-
id found in the ``layer_id`` matchdict."""
|
150
|
+
def _get_protocol_for_request(self, **kwargs: Any) -> Protocol:
|
151
|
+
"""Return a papyrus ``Protocol`` for the first layer id found in the ``layer_id`` matchdict."""
|
147
152
|
layer = self._get_layer_for_request()
|
148
153
|
return self._get_protocol_for_layer(layer, **kwargs)
|
149
154
|
|
150
|
-
def _proto_read(self, layer):
|
155
|
+
def _proto_read(self, layer: "main.Layer") -> FeatureCollection:
|
151
156
|
"""Read features for the layer based on the self.request."""
|
152
157
|
from c2cgeoportal_commons.models.main import ( # pylint: disable=import-outside-toplevel
|
153
158
|
Layer,
|
@@ -175,7 +180,7 @@ class Layers:
|
|
175
180
|
return proto.read(self.request)
|
176
181
|
use_srid = srid
|
177
182
|
collect_ra.append(to_shape(ra))
|
178
|
-
if not collect_ra:
|
183
|
+
if not collect_ra:
|
179
184
|
raise HTTPForbidden()
|
180
185
|
|
181
186
|
filter1_ = create_filter(self.request, cls, geom_attr)
|
@@ -183,11 +188,14 @@ class Layers:
|
|
183
188
|
filter2_ = ga_func.ST_Contains(from_shape(ra, use_srid), getattr(cls, geom_attr))
|
184
189
|
filter_ = filter2_ if filter1_ is None else and_(filter1_, filter2_)
|
185
190
|
|
186
|
-
|
191
|
+
feature = proto.read(self.request, filter=filter_)
|
192
|
+
if isinstance(feature, HTTPException):
|
193
|
+
raise feature
|
194
|
+
return feature
|
187
195
|
|
188
|
-
@view_config(route_name="layers_read_many", renderer="geojson")
|
189
|
-
def read_many(self):
|
190
|
-
set_common_headers(self.request, "layers",
|
196
|
+
@view_config(route_name="layers_read_many", renderer="geojson") # type: ignore
|
197
|
+
def read_many(self) -> FeatureCollection:
|
198
|
+
set_common_headers(self.request, "layers", Cache.PRIVATE_NO)
|
191
199
|
|
192
200
|
features = []
|
193
201
|
for layer in self._get_layers_for_request():
|
@@ -197,15 +205,15 @@ class Layers:
|
|
197
205
|
|
198
206
|
return FeatureCollection(features)
|
199
207
|
|
200
|
-
@view_config(route_name="layers_read_one", renderer="geojson")
|
201
|
-
def read_one(self):
|
208
|
+
@view_config(route_name="layers_read_one", renderer="geojson") # type: ignore
|
209
|
+
def read_one(self) -> Feature:
|
202
210
|
from c2cgeoportal_commons.models.main import ( # pylint: disable=import-outside-toplevel
|
203
211
|
Layer,
|
204
212
|
RestrictionArea,
|
205
213
|
Role,
|
206
214
|
)
|
207
215
|
|
208
|
-
set_common_headers(self.request, "layers",
|
216
|
+
set_common_headers(self.request, "layers", Cache.PRIVATE_NO)
|
209
217
|
|
210
218
|
layer = self._get_layer_for_request()
|
211
219
|
protocol = self._get_protocol_for_layer(layer)
|
@@ -218,9 +226,9 @@ class Layers:
|
|
218
226
|
if self.request.user is None:
|
219
227
|
raise HTTPForbidden()
|
220
228
|
geom = feature.geometry
|
221
|
-
if not geom or isinstance(geom, geojson.geometry.Default):
|
229
|
+
if not geom or isinstance(geom, geojson.geometry.Default):
|
222
230
|
return feature
|
223
|
-
shape =
|
231
|
+
shape = shapely.geometry.shape(geom)
|
224
232
|
srid = self._get_geom_col_info(layer)[1]
|
225
233
|
spatial_elt = from_shape(shape, srid=srid)
|
226
234
|
allowed = models.DBSession.query(func.count(RestrictionArea.id))
|
@@ -236,22 +244,25 @@ class Layers:
|
|
236
244
|
|
237
245
|
return feature
|
238
246
|
|
239
|
-
@view_config(route_name="layers_count", renderer="string")
|
240
|
-
def count(self):
|
241
|
-
set_common_headers(self.request, "layers",
|
247
|
+
@view_config(route_name="layers_count", renderer="string") # type: ignore
|
248
|
+
def count(self) -> int:
|
249
|
+
set_common_headers(self.request, "layers", Cache.PRIVATE_NO)
|
242
250
|
|
243
251
|
protocol = self._get_protocol_for_request()
|
244
|
-
|
252
|
+
count = protocol.count(self.request)
|
253
|
+
if isinstance(count, HTTPException):
|
254
|
+
raise count
|
255
|
+
return cast(int, count)
|
245
256
|
|
246
|
-
@view_config(route_name="layers_create", renderer="geojson")
|
247
|
-
def create(self):
|
257
|
+
@view_config(route_name="layers_create", renderer="geojson") # type: ignore
|
258
|
+
def create(self) -> Optional[FeatureCollection]:
|
248
259
|
from c2cgeoportal_commons.models.main import ( # pylint: disable=import-outside-toplevel
|
249
260
|
Layer,
|
250
261
|
RestrictionArea,
|
251
262
|
Role,
|
252
263
|
)
|
253
264
|
|
254
|
-
set_common_headers(self.request, "layers",
|
265
|
+
set_common_headers(self.request, "layers", Cache.PRIVATE_NO)
|
255
266
|
|
256
267
|
if self.request.user is None:
|
257
268
|
raise HTTPForbidden()
|
@@ -260,11 +271,11 @@ class Layers:
|
|
260
271
|
|
261
272
|
layer = self._get_layer_for_request()
|
262
273
|
|
263
|
-
def check_geometry(_, feature, obj):
|
274
|
+
def check_geometry(_: Any, feature: Feature, obj: Any) -> None:
|
264
275
|
del obj # unused
|
265
276
|
geom = feature.geometry
|
266
277
|
if geom and not isinstance(geom, geojson.geometry.Default):
|
267
|
-
shape =
|
278
|
+
shape = shapely.geometry.shape(geom)
|
268
279
|
srid = self._get_geom_col_info(layer)[1]
|
269
280
|
spatial_elt = from_shape(shape, srid=srid)
|
270
281
|
allowed = models.DBSession.query(func.count(RestrictionArea.id))
|
@@ -287,7 +298,7 @@ class Layers:
|
|
287
298
|
try:
|
288
299
|
features = protocol.create(self.request)
|
289
300
|
if isinstance(features, HTTPException):
|
290
|
-
raise features
|
301
|
+
raise features
|
291
302
|
if features is not None:
|
292
303
|
for feature in features.features: # pylint: disable=no-member
|
293
304
|
self._log_last_update(layer, feature)
|
@@ -300,15 +311,15 @@ class Layers:
|
|
300
311
|
self.request.response.status_int = 400
|
301
312
|
return {"error_type": "integrity_error", "message": str(e.orig.diag.message_primary)}
|
302
313
|
|
303
|
-
@view_config(route_name="layers_update", renderer="geojson")
|
304
|
-
def update(self):
|
314
|
+
@view_config(route_name="layers_update", renderer="geojson") # type: ignore
|
315
|
+
def update(self) -> Feature:
|
305
316
|
from c2cgeoportal_commons.models.main import ( # pylint: disable=import-outside-toplevel
|
306
317
|
Layer,
|
307
318
|
RestrictionArea,
|
308
319
|
Role,
|
309
320
|
)
|
310
321
|
|
311
|
-
set_common_headers(self.request, "layers",
|
322
|
+
set_common_headers(self.request, "layers", Cache.PRIVATE_NO)
|
312
323
|
|
313
324
|
if self.request.user is None:
|
314
325
|
raise HTTPForbidden()
|
@@ -318,7 +329,7 @@ class Layers:
|
|
318
329
|
feature_id = self.request.matchdict.get("feature_id")
|
319
330
|
layer = self._get_layer_for_request()
|
320
331
|
|
321
|
-
def check_geometry(_, feature, obj):
|
332
|
+
def check_geometry(_: Any, feature: Feature, obj: Any) -> None:
|
322
333
|
# we need both the "original" and "new" geometry to be
|
323
334
|
# within the restriction area
|
324
335
|
geom_attr, srid = self._get_geom_col_info(layer)
|
@@ -335,7 +346,7 @@ class Layers:
|
|
335
346
|
)
|
336
347
|
spatial_elt = None
|
337
348
|
if geom and not isinstance(geom, geojson.geometry.Default):
|
338
|
-
shape =
|
349
|
+
shape = shapely.geometry.shape(geom)
|
339
350
|
spatial_elt = from_shape(shape, srid=srid)
|
340
351
|
allowed = allowed.filter(
|
341
352
|
or_(RestrictionArea.area.is_(None), RestrictionArea.area.ST_Contains(spatial_elt))
|
@@ -350,8 +361,10 @@ class Layers:
|
|
350
361
|
protocol = self._get_protocol_for_layer(layer, before_update=check_geometry)
|
351
362
|
try:
|
352
363
|
feature = protocol.update(self.request, feature_id)
|
364
|
+
if isinstance(feature, HTTPException):
|
365
|
+
raise feature
|
353
366
|
self._log_last_update(layer, feature)
|
354
|
-
return feature
|
367
|
+
return cast(Feature, feature)
|
355
368
|
except TopologicalError as e:
|
356
369
|
self.request.response.status_int = 400
|
357
370
|
return {"error_type": "validation_error", "message": str(e)}
|
@@ -361,7 +374,7 @@ class Layers:
|
|
361
374
|
return {"error_type": "integrity_error", "message": str(e.orig.diag.message_primary)}
|
362
375
|
|
363
376
|
@staticmethod
|
364
|
-
def _validate_geometry(geom):
|
377
|
+
def _validate_geometry(geom: Geometry) -> None:
|
365
378
|
if geom is not None:
|
366
379
|
simple = models.DBSession.query(func.ST_IsSimple(func.ST_GeomFromEWKB(geom))).scalar()
|
367
380
|
if not simple:
|
@@ -371,7 +384,7 @@ class Layers:
|
|
371
384
|
reason = models.DBSession.query(func.ST_IsValidReason(func.ST_GeomFromEWKB(geom))).scalar()
|
372
385
|
raise TopologicalError(reason)
|
373
386
|
|
374
|
-
def _log_last_update(self, layer, feature):
|
387
|
+
def _log_last_update(self, layer: "main.Layer", feature: Feature) -> None:
|
375
388
|
last_update_date = self.get_metadata(layer, "lastUpdateDateColumn")
|
376
389
|
if last_update_date is not None:
|
377
390
|
setattr(feature, last_update_date, datetime.now())
|
@@ -381,22 +394,22 @@ class Layers:
|
|
381
394
|
setattr(feature, last_update_user, self.request.user.id)
|
382
395
|
|
383
396
|
@staticmethod
|
384
|
-
def get_metadata(layer, key, default=None):
|
385
|
-
|
386
|
-
if len(
|
387
|
-
metadata =
|
388
|
-
return metadata.value
|
397
|
+
def get_metadata(layer: "main.Layer", key: str, default: Optional[str] = None) -> Optional[str]:
|
398
|
+
metadata = layer.get_metadata(key)
|
399
|
+
if len(metadata) == 1:
|
400
|
+
metadata = metadata[0]
|
401
|
+
return cast(str, metadata.value)
|
389
402
|
return default
|
390
403
|
|
391
|
-
def _get_validation_setting(self, layer):
|
404
|
+
def _get_validation_setting(self, layer: "main.Layer") -> bool:
|
392
405
|
# The validation UIMetadata is stored as a string, not a boolean
|
393
406
|
should_validate = self.get_metadata(layer, "geometryValidation", None)
|
394
407
|
if should_validate:
|
395
408
|
return should_validate.lower() != "false"
|
396
|
-
return self.settings.get("geometry_validation", False)
|
409
|
+
return cast(bool, self.settings.get("geometry_validation", False))
|
397
410
|
|
398
|
-
@view_config(route_name="layers_delete")
|
399
|
-
def delete(self):
|
411
|
+
@view_config(route_name="layers_delete") # type: ignore
|
412
|
+
def delete(self) -> pyramid.response.Response:
|
400
413
|
from c2cgeoportal_commons.models.main import ( # pylint: disable=import-outside-toplevel
|
401
414
|
Layer,
|
402
415
|
RestrictionArea,
|
@@ -409,7 +422,7 @@ class Layers:
|
|
409
422
|
feature_id = self.request.matchdict.get("feature_id")
|
410
423
|
layer = self._get_layer_for_request()
|
411
424
|
|
412
|
-
def security_cb(_, obj):
|
425
|
+
def security_cb(_: Any, obj: Any) -> None:
|
413
426
|
geom_attr = getattr(obj, self._get_geom_col_info(layer)[0])
|
414
427
|
allowed = models.DBSession.query(func.count(RestrictionArea.id))
|
415
428
|
allowed = allowed.join(RestrictionArea.roles)
|
@@ -425,12 +438,14 @@ class Layers:
|
|
425
438
|
|
426
439
|
protocol = self._get_protocol_for_layer(layer, before_delete=security_cb)
|
427
440
|
response = protocol.delete(self.request, feature_id)
|
428
|
-
|
441
|
+
if isinstance(response, HTTPException):
|
442
|
+
raise response
|
443
|
+
set_common_headers(self.request, "layers", Cache.PRIVATE_NO, response=response)
|
429
444
|
return response
|
430
445
|
|
431
|
-
@view_config(route_name="layers_metadata", renderer="xsd")
|
432
|
-
def metadata(self):
|
433
|
-
set_common_headers(self.request, "layers",
|
446
|
+
@view_config(route_name="layers_metadata", renderer="xsd") # type: ignore
|
447
|
+
def metadata(self) -> pyramid.response.Response:
|
448
|
+
set_common_headers(self.request, "layers", Cache.PRIVATE)
|
434
449
|
|
435
450
|
layer = self._get_layer_for_request()
|
436
451
|
if not layer.public and self.request.user is None:
|
@@ -438,38 +453,40 @@ class Layers:
|
|
438
453
|
|
439
454
|
return get_layer_class(layer, with_last_update_columns=True)
|
440
455
|
|
441
|
-
@view_config(route_name="layers_enumerate_attribute_values", renderer="json")
|
442
|
-
def enumerate_attribute_values(self):
|
443
|
-
set_common_headers(self.request, "layers",
|
456
|
+
@view_config(route_name="layers_enumerate_attribute_values", renderer="json") # type: ignore
|
457
|
+
def enumerate_attribute_values(self) -> Dict[str, Any]:
|
458
|
+
set_common_headers(self.request, "layers", Cache.PUBLIC)
|
444
459
|
|
445
|
-
if self.layers_enum_config is None:
|
460
|
+
if self.layers_enum_config is None:
|
446
461
|
raise HTTPInternalServerError("Missing configuration")
|
447
462
|
layername = self.request.matchdict["layer_name"]
|
448
463
|
fieldname = self.request.matchdict["field_name"]
|
449
464
|
# TODO check if layer is public or not
|
450
465
|
|
451
|
-
return self._enumerate_attribute_values(layername, fieldname)
|
466
|
+
return cast(Dict[str, Any], self._enumerate_attribute_values(layername, fieldname))
|
452
467
|
|
453
|
-
@CACHE_REGION.cache_on_arguments()
|
454
|
-
def _enumerate_attribute_values(self, layername, fieldname):
|
455
|
-
if layername not in self.layers_enum_config:
|
456
|
-
raise HTTPBadRequest("Unknown layer: {
|
468
|
+
@CACHE_REGION.cache_on_arguments() # type: ignore
|
469
|
+
def _enumerate_attribute_values(self, layername: str, fieldname: str) -> Dict[str, Any]:
|
470
|
+
if layername not in self.layers_enum_config:
|
471
|
+
raise HTTPBadRequest(f"Unknown layer: {layername!s}")
|
457
472
|
|
458
473
|
layerinfos = self.layers_enum_config[layername]
|
459
|
-
if fieldname not in layerinfos["attributes"]:
|
460
|
-
raise HTTPBadRequest("Unknown attribute: {
|
474
|
+
if fieldname not in layerinfos["attributes"]:
|
475
|
+
raise HTTPBadRequest(f"Unknown attribute: {fieldname!s}")
|
461
476
|
dbsession_name = layerinfos.get("dbsession", "dbsession")
|
462
477
|
dbsession = models.DBSessions.get(dbsession_name)
|
463
|
-
if dbsession is None:
|
478
|
+
if dbsession is None:
|
464
479
|
raise HTTPInternalServerError(
|
465
|
-
"No dbsession found for layer '{
|
480
|
+
f"No dbsession found for layer '{layername!s}' ({dbsession_name!s})"
|
466
481
|
)
|
467
|
-
values = self.query_enumerate_attribute_values(dbsession, layerinfos, fieldname)
|
482
|
+
values = sorted(self.query_enumerate_attribute_values(dbsession, layerinfos, fieldname))
|
468
483
|
enum = {"items": [{"value": value[0]} for value in values]}
|
469
484
|
return enum
|
470
485
|
|
471
486
|
@staticmethod
|
472
|
-
def query_enumerate_attribute_values(
|
487
|
+
def query_enumerate_attribute_values(
|
488
|
+
dbsession: sqlalchemy.orm.Session, layerinfos: Dict[str, Any], fieldname: str
|
489
|
+
) -> Set[Tuple[str, ...]]:
|
473
490
|
attrinfos = layerinfos["attributes"][fieldname]
|
474
491
|
table = attrinfos["table"]
|
475
492
|
layertable = get_table(table, session=dbsession)
|
@@ -480,18 +497,23 @@ class Layers:
|
|
480
497
|
if "separator" in attrinfos:
|
481
498
|
separator = attrinfos["separator"]
|
482
499
|
attribute = func.unnest(func.string_to_array(func.string_agg(attribute, separator), separator))
|
483
|
-
return dbsession.query(
|
500
|
+
return set(cast(List[Tuple[str, ...]], dbsession.query(attribute).order_by(attribute).all()))
|
484
501
|
|
485
502
|
|
486
|
-
def get_layer_class(
|
503
|
+
def get_layer_class(
|
504
|
+
layer: "main.Layer", with_last_update_columns: bool = False
|
505
|
+
) -> sqlalchemy.ext.declarative.ConcreteBase:
|
487
506
|
"""
|
488
|
-
Get the SQLAlchemy class to edit a GeoMapFish layer
|
507
|
+
Get the SQLAlchemy class to edit a GeoMapFish layer.
|
508
|
+
|
509
|
+
Keyword Arguments:
|
489
510
|
|
490
|
-
|
491
|
-
|
511
|
+
layer: The GeoMapFish layer
|
512
|
+
with_last_update_columns: False to just have a class to access to the table and be able to
|
492
513
|
modify the last_update_columns, True to have a correct class to build the UI
|
493
514
|
(without the hidden column).
|
494
|
-
|
515
|
+
|
516
|
+
Returns: SQLAlchemy class
|
495
517
|
"""
|
496
518
|
# Exclude the columns used to record the last features update
|
497
519
|
exclude = [] if layer.exclude_properties is None else layer.exclude_properties.split(",")
|
@@ -502,8 +524,6 @@ def get_layer_class(layer, with_last_update_columns=False):
|
|
502
524
|
last_update_user = Layers.get_metadata(layer, "lastUpdateUserColumn")
|
503
525
|
if last_update_user is not None:
|
504
526
|
exclude.append(last_update_user)
|
505
|
-
else:
|
506
|
-
exclude = []
|
507
527
|
|
508
528
|
m = Layers.get_metadata(layer, "editingAttributesOrder")
|
509
529
|
attributes_order = m.split(",") if m else None
|
@@ -543,21 +563,35 @@ def get_layer_class(layer, with_last_update_columns=False):
|
|
543
563
|
return cls
|
544
564
|
|
545
565
|
|
546
|
-
|
566
|
+
class ColumnProperties(TypedDict, total=False):
|
567
|
+
"""Collected metadata information related to an editing attribute."""
|
568
|
+
|
569
|
+
name: str
|
570
|
+
type: str
|
571
|
+
nillable: bool
|
572
|
+
srid: int
|
573
|
+
enumeration: List[str]
|
574
|
+
restriction: str
|
575
|
+
maxLength: int # noqa
|
576
|
+
fractionDigits: int # noqa
|
577
|
+
totalDigits: int # noqa
|
578
|
+
|
579
|
+
|
580
|
+
def get_layer_metadata(layer: "main.Layer") -> List[ColumnProperties]:
|
581
|
+
"""Get the metadata related to a layer."""
|
547
582
|
cls = get_layer_class(layer, with_last_update_columns=True)
|
548
|
-
edit_columns = []
|
583
|
+
edit_columns: List[ColumnProperties] = []
|
549
584
|
|
550
585
|
for column_property in class_mapper(cls).iterate_properties:
|
551
586
|
if isinstance(column_property, ColumnProperty):
|
552
|
-
|
553
587
|
if len(column_property.columns) != 1:
|
554
|
-
raise NotImplementedError
|
588
|
+
raise NotImplementedError
|
555
589
|
|
556
590
|
column = column_property.columns[0]
|
557
591
|
|
558
592
|
# Exclude columns that are primary keys
|
559
593
|
if not column.primary_key:
|
560
|
-
properties = _convert_column_type(column.type)
|
594
|
+
properties: ColumnProperties = _convert_column_type(column.type)
|
561
595
|
properties["name"] = column.key
|
562
596
|
|
563
597
|
if column.nullable:
|
@@ -586,7 +620,7 @@ def get_layer_metadatas(layer):
|
|
586
620
|
return edit_columns
|
587
621
|
|
588
622
|
|
589
|
-
def _convert_column_type(column_type):
|
623
|
+
def _convert_column_type(column_type: object) -> ColumnProperties:
|
590
624
|
# SIMPLE_XSD_TYPES
|
591
625
|
for cls, xsd_type in XSDGenerator.SIMPLE_XSD_TYPES.items():
|
592
626
|
if isinstance(column_type, cls):
|
@@ -603,14 +637,13 @@ def _convert_column_type(column_type):
|
|
603
637
|
return {"type": xsd_type, "srid": int(column_type.srid)}
|
604
638
|
|
605
639
|
raise NotImplementedError(
|
606
|
-
"The geometry type '{}' is not supported, supported types:
|
607
|
-
|
608
|
-
|
609
|
-
) # pragma: no cover
|
640
|
+
f"The geometry type '{geometry_type}' is not supported, supported types: "
|
641
|
+
f"{','.join(XSDGenerator.SIMPLE_GEOMETRY_XSD_TYPES)}"
|
642
|
+
)
|
610
643
|
|
611
644
|
# Enumeration type
|
612
645
|
if isinstance(column_type, Enum):
|
613
|
-
restriction:
|
646
|
+
restriction: ColumnProperties = {}
|
614
647
|
restriction["restriction"] = "enumeration"
|
615
648
|
restriction["type"] = "xsd:string"
|
616
649
|
restriction["enumeration"] = column_type.enums
|
@@ -624,17 +657,17 @@ def _convert_column_type(column_type):
|
|
624
657
|
|
625
658
|
# Numeric Type
|
626
659
|
if isinstance(column_type, Numeric):
|
627
|
-
|
660
|
+
xsd_type2: ColumnProperties = {"type": "xsd:decimal"}
|
628
661
|
if column_type.scale is None and column_type.precision is None:
|
629
|
-
return
|
662
|
+
return xsd_type2
|
630
663
|
|
631
664
|
if column_type.scale is not None:
|
632
|
-
|
665
|
+
xsd_type2["fractionDigits"] = int(column_type.scale)
|
633
666
|
if column_type.precision is not None:
|
634
|
-
|
635
|
-
return
|
667
|
+
xsd_type2["totalDigits"] = int(column_type.precision)
|
668
|
+
return xsd_type2
|
636
669
|
|
637
670
|
raise NotImplementedError(
|
638
|
-
"The type '{}' is not supported, supported types: "
|
639
|
-
"Geometry, Enum, String, Text, Unicode, UnicodeText, Numeric"
|
640
|
-
)
|
671
|
+
f"The type '{type(column_type).__name__}' is not supported, supported types: "
|
672
|
+
"Geometry, Enum, String, Text, Unicode, UnicodeText, Numeric"
|
673
|
+
)
|