diff --git a/.github/workflows/dependencies.yml b/.github/workflows/dependencies.yml index 53621e9f69..3fbe11e265 100644 --- a/.github/workflows/dependencies.yml +++ b/.github/workflows/dependencies.yml @@ -18,7 +18,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: ['ubuntu-20.04'] + os: ['ubuntu-latest'] python-version: ['3.8'] steps: @@ -31,6 +31,7 @@ jobs: - name: Install dependencies run: | + pip3 install -U "pip<24.3" # prevent relative bug path with pip 24.3 and pip-tools pip3 install -c dev-requirements.txt pip-tools - name: Check dependency graph diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e1af6712e6..462145d8eb 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -184,7 +184,7 @@ jobs: name: coverage_${{ matrix.os }}_${{ matrix.tests-env }} path: . - - uses: codecov/codecov-action@v4 + - uses: codecov/codecov-action@v5 with: files: ./coverage.xml env_vars: OS,ENV diff --git a/.gitignore b/.gitignore index 78d219f109..d6fad2ef77 100644 --- a/.gitignore +++ b/.gitignore @@ -62,3 +62,4 @@ docs/_build/ /dpkg/ /coverage.lcov /htmlcov/ +/.cache/ diff --git a/Makefile b/Makefile index a89baf4538..6c92554257 100644 --- a/Makefile +++ b/Makefile @@ -15,10 +15,10 @@ COLOR_DEBUG = \033[36m COLOR_RESET = \033[0m build: - docker build -t geotrek . --build-arg BASE_IMAGE_TAG=$(BASE_IMAGE_TAG) + docker build -t geotrek -f docker/Dockerfile . --build-arg BASE_IMAGE_TAG=$(BASE_IMAGE_TAG) -build-no-cache: - docker build -t geotrek --no-cache . +build_no_cache: + docker build -t geotrek -f docker/Dockerfile --no-cache . build_deb: docker pull $(DISTRO) diff --git a/VERSION b/VERSION index dc4a9cdf4d..15e9bd1664 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.109.3+dev +2.110.0+dev diff --git a/cypress/package-lock.json b/cypress/package-lock.json index 66abd3b86c..c65edd1d41 100644 --- a/cypress/package-lock.json +++ b/cypress/package-lock.json @@ -475,9 +475,9 @@ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.5.tgz", + "integrity": "sha512-ZVJrKKYunU38/76t0RMOulHOnUcbU9GbpWKAOZ0mhjr7CX6FVrH+4FrAapSOekrgFQ3f/8gwMEuIft0aKq6Hug==", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -2117,9 +2117,9 @@ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.5.tgz", + "integrity": "sha512-ZVJrKKYunU38/76t0RMOulHOnUcbU9GbpWKAOZ0mhjr7CX6FVrH+4FrAapSOekrgFQ3f/8gwMEuIft0aKq6Hug==", "requires": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", diff --git a/debian/changelog b/debian/changelog index 4874c9c01e..2682ce2014 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,8 +1,14 @@ -geotrek-admin (2.109.3+dev) UNRELEASED; urgency=medium +geotrek-admin (2.110.0+dev) UNRELEASED; urgency=medium - * + * - -- CĂ©lia Prat Mon, 04 Nov 2024 14:01:07 +0100 + -- Justine Fricou Wed, 13 Nov 2024 11:09:31 +0100 + +geotrek-admin (2.110.0) RELEASED; urgency=medium + + * New package release + + -- Justine Fricou Wed, 13 Nov 2024 10:46:20 +0100 geotrek-admin (2.109.3) RELEASED; urgency=medium diff --git a/docker/Dockerfile b/docker/Dockerfile index 0bf0139a0f..8620518639 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -18,7 +18,7 @@ ENV TZ=UTC WORKDIR /opt/geotrek-admin RUN mkdir -p /opt/geotrek-admin/var/log /opt/geotrek-admin/var/cache -RUN useradd -M -s /bin/false geotrek && chown geotrek:geotrek -R /opt +RUN useradd -m -d /opt/geotrek-admin -s /bin/false geotrek && chown geotrek:geotrek -R /opt # Install postgis because raster2pgsl is required by manage.py loaddem RUN apt-get update -qq && apt-get install -y -qq \ @@ -70,7 +70,7 @@ RUN apt-get update -qq && apt-get install -y -qq \ USER geotrek RUN python3 -m venv /opt/venv -RUN /opt/venv/bin/pip install --no-cache-dir -U pip setuptools wheel +RUN /opt/venv/bin/pip install --no-cache-dir -U "pip<24.3" setuptools wheel COPY requirements.txt requirements.txt RUN /opt/venv/bin/pip install --no-cache-dir -r requirements.txt -U diff --git a/docs/changelog.rst b/docs/changelog.rst index 5c6e5e780d..35f3e095f4 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -2,9 +2,31 @@ CHANGELOG ========= -2.109.3+dev (XXXX-XX-XX) +2.110.0+dev (XXXX-XX-XX) ---------------------------- +**Improvements** + +- Remove overriding of SchemaRandonneeParser's filetype_name attribute (#4022) + +**Bug fixes** + +- Fix missing Dockerfile path on make build scripts +- Fix SchemaRandonneeParser url update when description is null or was not updated (#4022) + +**Documentation** + +- Update documentation for release and update obsolete example +- Add note about certbot ssl configuration in nginx + + +2.110.0 (2024-11-13) +---------------------------- + +**New features** + +- Add parser for schema_randonnee-compliant files (#4022) + 2.109.3 (2024-10-29) ---------------------------- diff --git a/docs/conf.py b/docs/conf.py index f5ab49151a..898591e6f4 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,11 +1,14 @@ -import sphinx_rtd_theme # noqa import datetime +import os + +root = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) extensions = [ 'sphinx.ext.todo', - 'sphinx_rtd_theme', + "sphinx_immaterial" ] +html_theme = 'sphinx_immaterial' # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] @@ -16,29 +19,78 @@ master_doc = 'index' # General information about the project. -project = 'Geotrek' +project = 'Geotrek-admin' copyright = f'2013-{datetime.date.today().year}, Makina Corpus' -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# -# The short X.Y version. -version = '2.109' -# The full version, including alpha/beta/rc tags. -release = '2.109.3+dev' exclude_patterns = ['_build'] pygments_style = 'sphinx' -html_theme = 'sphinx_rtd_theme' - html_logo = "_static/logo.svg" + +# Material theme options (see theme.conf for more information) html_theme_options = { - "logo_only": True, - 'style_external_links': True, + "icon": { + "repo": "fontawesome/brands/github", + "edit": "material/file-edit-outline", + }, + "site_url": "https://geotrek.fr/", + "repo_url": "https://github.com/GeotrekCE/Geotrek-admin/", + "repo_name": "Geotrek-admin", + "edit_uri": "blob/main/docs", + "globaltoc_collapse": True, + "features": [ + "navigation.expand", + # "navigation.tabs", + # "toc.integrate", + "navigation.sections", + # "navigation.instant", + # "header.autohide", + "navigation.top", + # "navigation.tracking", + # "search.highlight", + "search.share", + "toc.follow", + "toc.sticky", + "content.tabs.link", + "announce.dismiss", + ], + "palette": [ + { + "media": "(prefers-color-scheme: light)", + "scheme": "default", + "primary": "green", + "accent": "light green", + "toggle": { + "icon": "material/weather-night", + "name": "Switch to dark mode", + }, + }, + { + "media": "(prefers-color-scheme: dark)", + "scheme": "slate", + "primary": "green", + "accent": "light green", + "toggle": { + "icon": "material/weather-sunny", + "name": "Switch to light mode", + }, + }, + ], + "social": [ + { + "icon": "fontawesome/brands/github", + "link": "https://github.com/GeotrekCE/Geotrek-admin" + }, + { + "icon": "fontawesome/brands/linkedin", + "link": "https://www.linkedin.com/company/geotrek-application", + }, + ], + "toc_title_is_page_title": True, } + html_favicon = "_static/favicon.png" html_static_path = ['_static'] @@ -47,17 +99,19 @@ htmlhelp_basename = 'Geotrekdoc' latex_documents = [ - ('index', 'Geotrek.tex', 'Geotrek Documentation', + ('index', 'Geotrek.tex', 'Geotrek-admin Documentation', 'Makina Corpus', 'manual'), ] man_pages = [ - ('index', 'geotrek', 'Geotrek Documentation', + ('index', 'geotrek', 'Geotrek-admin Documentation', ['Makina Corpus'], 1) ] - +html_sidebars = { + "**": ["logo-text.html", "globaltoc.html", "localtoc.html", "searchbox.html"] +} texinfo_documents = [ - ('index', 'Geotrek', 'Geotrek Documentation', - 'Makina Corpus', 'Geotrek', 'One line description of project.', + ('index', 'Geotrek', 'Geotrek-admin Documentation', + 'Makina Corpus', 'Geotrek-admin', 'One line description of project.', 'Miscellaneous'), ] diff --git a/docs/contribute/contributing.rst b/docs/contribute/contributing.rst index 47a37182bc..04a996d7b0 100644 --- a/docs/contribute/contributing.rst +++ b/docs/contribute/contributing.rst @@ -103,16 +103,16 @@ Release On master branch: -* Update files *VERSION*, *docs/conf.py* and *docs/changelog.rst* to remove ``+dev`` suffix and increment version (please use semver rules) +* Update files *VERSION* and *docs/changelog.rst* to remove ``+dev`` suffix and increment version (please use semver rules) * Run ``dch -r -D RELEASED``, update version in the same way and save -* Commit with message 'Release x.y.z' and push to ``master`` -* Create new release on Github, with tag X.Y.Z, click on "Generate release notes" +* Commit with message 'Release X.Y.Z' and push to ``master`` +* Create new release with name 'Geotrek-admin X.Y.Z' on Github, with tag X.Y.Z, click on "Generate release notes" * Wait for release to be published through CI -* Update files *VERSION*, *docs/conf.py* and *docs/changelog.rst* to add ``+dev`` suffix +* Update files *VERSION* and *docs/changelog.rst* to add ``+dev`` suffix * Run ``dch -v +dev --no-force-save-on-release`` and save * Commit with message 'Back to development' and push to ```master`` -* When creating a new release 'x.y.z' on github, Github actions will generate the .deb package file, and publish it on https://packages.geotrek.fr (see ``.github/workflows/test.yml`` file for details) +* When creating a new release 'X.Y.Z' on github, Github actions will generate the .deb package file, and publish it on https://packages.geotrek.fr (see ``.github/workflows/test.yml`` file for details) Other ways to contribute ------------------------- diff --git a/docs/index.rst b/docs/index.rst index 6ef004b713..189396a60c 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,5 +1,7 @@ -Welcome to Geotrek's documentation! -=================================== +Geotrek-admin's documentation +============================= + +Geotrek-admin is backend management data for Geotrek-rando, Geotrek-mobile and Geotrek-widget. .. toctree:: :caption: 💡 About diff --git a/docs/install/configuration.rst b/docs/install/configuration.rst index 47631332a6..a6b9394e63 100644 --- a/docs/install/configuration.rst +++ b/docs/install/configuration.rst @@ -71,11 +71,13 @@ After this, edit ``nginx.conf.in`` to add your certificate. If you generate it with letsencrypt : You can use certbot to add the certificate in your configuration. -But you will have to move the configuration automatically added into ``nginx.conf``, to the file ``nginx.conf.in`` -in ``/opt/geotrek-admin/var/conf/`` directory +But you will have to move the configuration automatically added into ``nginx.conf``, to the file ``nginx.conf.in`` in ``/opt/geotrek-admin/var/conf/`` directory. -You have to move the configuration to the file ``nginx.conf.in`` because ``nginx.conf`` is automatically -changed during command ``dpkg-reconfigure geotrek-admin``. +You have to move the configuration to the file ``nginx.conf.in`` because ``nginx.conf`` is automatically changed during command ``dpkg-reconfigure geotrek-admin``. + +.. note:: + + You need to replace the ``$`` from Certbot with ``${DOLLAR}`` everywhere in the ``nginx.conf.in`` file, then run the command ``sudo dpkg-reconfigure geotrek-admin`` to regenerate the file. Mandatory settings diff --git a/docs/install/maintenance.rst b/docs/install/maintenance.rst index 66c7a72fee..2b2322b465 100644 --- a/docs/install/maintenance.rst +++ b/docs/install/maintenance.rst @@ -34,11 +34,9 @@ Application restore If you restore Geotrek-admin on a new server, you will have to install PostgreSQL and PostGIS and create a database user first. Otherwise go directly to the database creation step. -Example for Ubuntu 18: - .. code-block:: bash - sudo apt install postgresql-10 postgresql-10-postgis-2.5 + sudo apt install postgresql-14 postgresql-14-postgis-3 sudo -u postgres psql -c "CREATE USER geotrek PASSWORD 'geotrek';" diff --git a/docs/requirements.in b/docs/requirements.in index 777752c45e..11a729e7c7 100644 --- a/docs/requirements.in +++ b/docs/requirements.in @@ -1,6 +1,4 @@ --c ../dev-requirements.in - -sphinx-rtd-theme>=1.0.0 +sphinx_immaterial sphinx>=4.0.0 sphinx-autobuild sphinx-intl \ No newline at end of file diff --git a/docs/requirements.txt b/docs/requirements.txt index 5feca3c1bf..aa6c7d7239 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -6,81 +6,69 @@ # alabaster==0.7.13 # via sphinx -babel==2.15.0 +annotated-types==0.7.0 + # via pydantic +appdirs==1.4.4 + # via sphinx-immaterial +babel==2.16.0 # via # sphinx # sphinx-intl certifi==2024.8.30 - # via - # -c docs/../requirements.txt - # requests + # via requests charset-normalizer==3.3.2 - # via - # -c docs/../requirements.txt - # requests + # via requests click==8.1.3 - # via - # -c docs/../requirements.txt - # sphinx-intl + # via sphinx-intl colorama==0.4.6 # via sphinx-autobuild docutils==0.20.1 - # via - # -c docs/../requirements.txt - # sphinx - # sphinx-rtd-theme + # via sphinx idna==3.7 - # via - # -c docs/../requirements.txt - # requests + # via requests imagesize==1.4.1 # via sphinx importlib-metadata==6.8.0 - # via - # -c docs/../requirements.txt - # sphinx + # via sphinx jinja2==3.1.4 - # via - # -c docs/../requirements.txt - # sphinx -livereload==2.6.3 + # via sphinx +livereload==2.7.0 # via sphinx-autobuild markupsafe==2.1.5 # via - # -c docs/../requirements.txt # jinja2 + # sphinx-immaterial packaging==24.0 - # via - # -c docs/../requirements.txt - # sphinx + # via sphinx +pydantic==2.9.2 + # via + # pydantic-extra-types + # sphinx-immaterial +pydantic-core==2.23.4 + # via pydantic +pydantic-extra-types==2.10.0 + # via sphinx-immaterial pygments==2.18.0 # via sphinx pytz==2024.2 - # via - # -c docs/../requirements.txt - # babel + # via babel requests==2.32.3 # via - # -c docs/../requirements.txt # sphinx -six==1.16.0 - # via - # -c docs/../requirements.txt - # livereload + # sphinx-immaterial snowballstemmer==2.2.0 # via sphinx sphinx==7.1.2 # via # -r docs/requirements.in # sphinx-autobuild + # sphinx-immaterial # sphinx-intl - # sphinx-rtd-theme - # sphinxcontrib-jquery sphinx-autobuild==2021.3.14 # via -r docs/requirements.in -sphinx-intl==2.2.0 +sphinx-immaterial==0.11.14 # via -r docs/requirements.in -sphinx-rtd-theme==2.0.0 +sphinx-intl==2.2.0 # via -r docs/requirements.in sphinxcontrib-applehelp==1.0.4 # via sphinx @@ -88,24 +76,25 @@ sphinxcontrib-devhelp==1.0.2 # via sphinx sphinxcontrib-htmlhelp==2.0.1 # via sphinx -sphinxcontrib-jquery==4.1 - # via sphinx-rtd-theme sphinxcontrib-jsmath==1.0.1 # via sphinx sphinxcontrib-qthelp==1.0.3 # via sphinx sphinxcontrib-serializinghtml==1.1.5 # via sphinx -tornado==6.4 +tornado==6.4.1 # via livereload -urllib3==2.2.2 +typing-extensions==4.12.2 # via - # -c docs/../requirements.txt - # requests + # annotated-types + # pydantic + # pydantic-core + # pydantic-extra-types + # sphinx-immaterial +urllib3==2.2.2 + # via requests zipp==3.20.2 - # via - # -c docs/../requirements.txt - # importlib-metadata + # via importlib-metadata # The following packages are considered to be unsafe in a requirements file: # setuptools diff --git a/geotrek/locale/de/LC_MESSAGES/django.po b/geotrek/locale/de/LC_MESSAGES/django.po index 2a0fbbe442..11a37277b3 100644 --- a/geotrek/locale/de/LC_MESSAGES/django.po +++ b/geotrek/locale/de/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-04-03 15:39+0000\n" +"POT-Creation-Date: 2024-11-08 16:28+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -18,9 +18,6 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -msgid "Geotrek" -msgstr "" - msgid "English" msgstr "" diff --git a/geotrek/locale/en/LC_MESSAGES/django.po b/geotrek/locale/en/LC_MESSAGES/django.po index 2a0fbbe442..11a37277b3 100644 --- a/geotrek/locale/en/LC_MESSAGES/django.po +++ b/geotrek/locale/en/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-04-03 15:39+0000\n" +"POT-Creation-Date: 2024-11-08 16:28+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -18,9 +18,6 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -msgid "Geotrek" -msgstr "" - msgid "English" msgstr "" diff --git a/geotrek/locale/es/LC_MESSAGES/django.po b/geotrek/locale/es/LC_MESSAGES/django.po index 2a0fbbe442..11a37277b3 100644 --- a/geotrek/locale/es/LC_MESSAGES/django.po +++ b/geotrek/locale/es/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-04-03 15:39+0000\n" +"POT-Creation-Date: 2024-11-08 16:28+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -18,9 +18,6 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -msgid "Geotrek" -msgstr "" - msgid "English" msgstr "" diff --git a/geotrek/locale/fr/LC_MESSAGES/django.po b/geotrek/locale/fr/LC_MESSAGES/django.po index f65a560fb7..035b6bec54 100644 --- a/geotrek/locale/fr/LC_MESSAGES/django.po +++ b/geotrek/locale/fr/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-04-03 15:39+0000\n" +"POT-Creation-Date: 2024-11-08 16:28+0000\n" "PO-Revision-Date: 2020-09-23 07:10+0000\n" "Last-Translator: Emmanuelle Helly \n" "Language-Team: French 1;\n" "X-Generator: Weblate 4.1.1\n" -msgid "Geotrek" -msgstr "" - msgid "English" msgstr "" diff --git a/geotrek/locale/it/LC_MESSAGES/django.po b/geotrek/locale/it/LC_MESSAGES/django.po index 2a0fbbe442..11a37277b3 100644 --- a/geotrek/locale/it/LC_MESSAGES/django.po +++ b/geotrek/locale/it/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-04-03 15:39+0000\n" +"POT-Creation-Date: 2024-11-08 16:28+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -18,9 +18,6 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -msgid "Geotrek" -msgstr "" - msgid "English" msgstr "" diff --git a/geotrek/locale/nl/LC_MESSAGES/django.po b/geotrek/locale/nl/LC_MESSAGES/django.po index 2a0fbbe442..11a37277b3 100644 --- a/geotrek/locale/nl/LC_MESSAGES/django.po +++ b/geotrek/locale/nl/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-04-03 15:39+0000\n" +"POT-Creation-Date: 2024-11-08 16:28+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -18,9 +18,6 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -msgid "Geotrek" -msgstr "" - msgid "English" msgstr "" diff --git a/geotrek/trekking/locale/de/LC_MESSAGES/django.po b/geotrek/trekking/locale/de/LC_MESSAGES/django.po index 04cd56f2bf..ed5d526102 100644 --- a/geotrek/trekking/locale/de/LC_MESSAGES/django.po +++ b/geotrek/trekking/locale/de/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-09-03 15:01+0000\n" +"POT-Creation-Date: 2024-11-08 16:23+0000\n" "PO-Revision-Date: 2015-10-21 11:16+0200\n" "Last-Translator: \n" "Language-Team: \n" @@ -476,12 +476,6 @@ msgid "" "{key}" msgstr "" -#, python-brace-format -msgid "" -"One trek has not be generated for {trek_parent_instance[0].name} : could not " -"find trek with UUID {child}" -msgstr "" - msgid "" "An error occured in children generation : {getattr(e, 'message', repr(e))}" msgstr "" @@ -500,6 +494,25 @@ msgid "" "LineString feature" msgstr "" +msgid "Trek geometry is None" +msgstr "" + +#, python-brace-format +msgid "Invalid geometry for field '{src}'. Should contain coordinates" +msgstr "" + +msgid "Bad value for parking geometry: should be a Point" +msgstr "" + +#, python-brace-format +msgid "" +"License '{val}' did not exist in Geotrek-Admin and was automatically created" +msgstr "" + +#, python-brace-format +msgid "License '{val}' does not exist in Geotrek-Admin. Please add it" +msgstr "" + msgid "Attached files" msgstr "" diff --git a/geotrek/trekking/locale/en/LC_MESSAGES/django.po b/geotrek/trekking/locale/en/LC_MESSAGES/django.po index 15cd5b21f2..65b7dbe881 100644 --- a/geotrek/trekking/locale/en/LC_MESSAGES/django.po +++ b/geotrek/trekking/locale/en/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-09-03 15:01+0000\n" +"POT-Creation-Date: 2024-11-08 16:23+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -461,12 +461,6 @@ msgid "" "{key}" msgstr "" -#, python-brace-format -msgid "" -"One trek has not be generated for {trek_parent_instance[0].name} : could not " -"find trek with UUID {child}" -msgstr "" - msgid "" "An error occured in children generation : {getattr(e, 'message', repr(e))}" msgstr "" @@ -485,6 +479,25 @@ msgid "" "LineString feature" msgstr "" +msgid "Trek geometry is None" +msgstr "" + +#, python-brace-format +msgid "Invalid geometry for field '{src}'. Should contain coordinates" +msgstr "" + +msgid "Bad value for parking geometry: should be a Point" +msgstr "" + +#, python-brace-format +msgid "" +"License '{val}' did not exist in Geotrek-Admin and was automatically created" +msgstr "" + +#, python-brace-format +msgid "License '{val}' does not exist in Geotrek-Admin. Please add it" +msgstr "" + msgid "Attached files" msgstr "" diff --git a/geotrek/trekking/locale/es/LC_MESSAGES/django.po b/geotrek/trekking/locale/es/LC_MESSAGES/django.po index 0da3b31982..5f1ddf2b0d 100644 --- a/geotrek/trekking/locale/es/LC_MESSAGES/django.po +++ b/geotrek/trekking/locale/es/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-09-03 15:01+0000\n" +"POT-Creation-Date: 2024-11-08 16:23+0000\n" "PO-Revision-Date: 2015-10-21 11:16+0200\n" "Last-Translator: jean-etienne.castagnede@makina-corpus.com\n" "Language-Team: \n" @@ -461,12 +461,6 @@ msgid "" "{key}" msgstr "" -#, python-brace-format -msgid "" -"One trek has not be generated for {trek_parent_instance[0].name} : could not " -"find trek with UUID {child}" -msgstr "" - msgid "" "An error occured in children generation : {getattr(e, 'message', repr(e))}" msgstr "" @@ -485,6 +479,25 @@ msgid "" "LineString feature" msgstr "" +msgid "Trek geometry is None" +msgstr "" + +#, python-brace-format +msgid "Invalid geometry for field '{src}'. Should contain coordinates" +msgstr "" + +msgid "Bad value for parking geometry: should be a Point" +msgstr "" + +#, python-brace-format +msgid "" +"License '{val}' did not exist in Geotrek-Admin and was automatically created" +msgstr "" + +#, python-brace-format +msgid "License '{val}' does not exist in Geotrek-Admin. Please add it" +msgstr "" + msgid "Attached files" msgstr "" diff --git a/geotrek/trekking/locale/fr/LC_MESSAGES/django.po b/geotrek/trekking/locale/fr/LC_MESSAGES/django.po index 91a3abc8ff..d96b44ad64 100644 --- a/geotrek/trekking/locale/fr/LC_MESSAGES/django.po +++ b/geotrek/trekking/locale/fr/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-09-03 15:01+0000\n" +"POT-Creation-Date: 2024-11-08 16:23+0000\n" "PO-Revision-Date: 2020-09-22 15:57+0000\n" "Last-Translator: Emmanuelle Helly \n" "Language-Team: French {url}' + return description + + def filter_attachments(self, src, val): + """Handles images only""" + if val is None: + return [] + attachments = [] + for media in val: + if media.get('url') is None or media.get('type_media') != 'image': + continue + attachments.append(( + media.get('url'), + media.get('titre'), + media.get('auteur'), + media.get('titre'), + media.get('licence'), + )) + return attachments + + def generate_attachments(self, src, val, attachments_to_delete, updated): + attachments = [] + for url, legend, author, title, license_label in self.filter_attachments(src, val): + url = self.base_url + url + legend = legend or "" + author = author or "" + title = title or "" + license = self.get_or_create_license(license_label) + basename, ext = os.path.splitext(os.path.basename(url)) + name = '%s%s' % (basename[:128], ext) + found, updated = self.check_attachment_updated(attachments_to_delete, updated, name=name, url=url, + legend=legend, author=author, title=title, license_label=license_label) + if found: + continue + + parsed_url = urlparse(url) + attachment = self.generate_attachment(author=author, legend=legend, title=title, license=license) + try: + save, updated = self.generate_content_attachment(attachment, parsed_url, url, updated, name) + if not save: + continue + except ValueImportError as warning: + self.add_warning(str(warning)) + continue + attachments.append(attachment) + updated = True + return updated, attachments + + def generate_attachment(self, **kwargs): + attachment = Attachment() + attachment.content_object = self.obj + attachment.filetype = self.filetype + attachment.creator = self.creator + attachment.author = kwargs.get('author') + attachment.legend = textwrap.shorten(kwargs.get('legend', ''), width=127) + attachment.license = kwargs.get('license') + attachment.title = textwrap.shorten(kwargs.get('title', ''), width=127) + return attachment + + def check_attachment_updated(self, attachments_to_delete, updated, **kwargs): + found = False + for attachment in attachments_to_delete: + upload_name, ext = os.path.splitext(attachment_upload(attachment, kwargs.get('name'))) + existing_name = attachment.attachment_file.name + regexp = f"{upload_name}({random_suffix_regexp()})?(_[a-zA-Z0-9]{{7}})?{ext}" + if re.search(r"^{regexp}$".format(regexp=regexp), existing_name) and not self.has_size_changed(kwargs.get('url'), attachment): + found = True + attachments_to_delete.remove(attachment) + if ( + kwargs.get('author') != attachment.author + or kwargs.get('legend') != attachment.legend + or kwargs.get('title') != attachment.title + or (kwargs.get('license_label') and not attachment.license) + or (attachment.license and kwargs.get('license_label') != attachment.license.label) + ): + attachment.author = kwargs.get('author') + attachment.legend = textwrap.shorten(kwargs.get('legend'), width=127) + attachment.title = textwrap.shorten(kwargs.get('title'), width=127) + attachment.license = self.get_or_create_license(kwargs.get('license_label')) + attachment.save(**{'skip_file_save': True}) + updated = True + break + return found, updated + + def get_or_create_license(self, license_label): + license = None + if license_label is not None: + try: + license = License.objects.get(label=license_label) + except License.DoesNotExist: + attachment_options = self.field_options.get('attachments') + if attachment_options and attachment_options.get('create_license'): + license = License.objects.create(label=license_label) + self.add_warning(_("License '{val}' did not exist in Geotrek-Admin and was automatically created").format(val=license_label)) + else: + self.add_warning(_("License '{val}' does not exist in Geotrek-Admin. Please add it").format(val=license_label)) + return license + + def save_id_local(self, src, val): + if val: + self.id_local_to_pk_mapping[val] = self.obj.pk + + def save_itineraire_parent(self, src, val): + if val: + self.related_treks_mapping[val].append(self.obj.pk) + + def end(self): + """ + After all treks have been created, add parent/children relationships + """ + for parent_id_local, child_pks in self.related_treks_mapping.items(): + parent_pk = self.id_local_to_pk_mapping.get(parent_id_local) + try: + parent_trek = Trek.objects.get(pk=parent_pk) + except Trek.DoesNotExist: + continue + order = 0 + for child_pk in child_pks: + child_trek = Trek.objects.get(pk=child_pk) + otc, _ = OrderedTrekChild.objects.get_or_create( + parent=parent_trek, + child=child_trek + ) + otc.order = order + otc.save() + order += 1 + super().end() diff --git a/geotrek/trekking/tests/data/schema_randonnee_parser/add_medias_info_after_update.geojson b/geotrek/trekking/tests/data/schema_randonnee_parser/add_medias_info_after_update.geojson new file mode 100644 index 0000000000..c1a4876d10 --- /dev/null +++ b/geotrek/trekking/tests/data/schema_randonnee_parser/add_medias_info_after_update.geojson @@ -0,0 +1,46 @@ +{ + "type": "FeatureCollection", + "name": "sql_statement", + "crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } }, + "features": [ + { + "type": "Feature", + "properties": { + "id_local": "1", + "producteur": "Producer 1", + "nom_itineraire": "Trek 1", + "pratique": "pĂ©destre", + "depart": "Departure 1", + "arrivee": "Arrival 1", + "instructions": "Instructions 1", + "medias": [ + { + "url": "https://test.fr/test1.jpg", + "titre": "Title 1.2", + "auteur": "Author 1.2", + "licence": "License 2", + "type_media": "image" + }, + { + "url": "https://test.fr/test2.jpg", + "titre": "Title 2.2", + "auteur": "Author 2.2", + "licence": "License 2", + "type_media": "image" + }, + { + "url": "https://test.fr/test3.jpg", + "titre": "Title 3.2", + "auteur": "Author 3.2", + "licence": "License 2", + "type_media": "image" + } + ] + }, + "geometry": { + "type": "LineString", + "coordinates": [ [ 6.449592517966364, 44.733424655086957 ], [ 6.449539623508488, 44.733394939411369 ] ] + } + } + ] +} \ No newline at end of file diff --git a/geotrek/trekking/tests/data/schema_randonnee_parser/add_medias_info_before_update.geojson b/geotrek/trekking/tests/data/schema_randonnee_parser/add_medias_info_before_update.geojson new file mode 100644 index 0000000000..e6712d598a --- /dev/null +++ b/geotrek/trekking/tests/data/schema_randonnee_parser/add_medias_info_before_update.geojson @@ -0,0 +1,43 @@ +{ + "type": "FeatureCollection", + "name": "sql_statement", + "crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } }, + "features": [ + { + "type": "Feature", + "properties": { + "id_local": "1", + "producteur": "Producer 1", + "nom_itineraire": "Trek 1", + "pratique": "pĂ©destre", + "depart": "Departure 1", + "arrivee": "Arrival 1", + "instructions": "Instructions 1", + "medias": [ + { + "url": "https://test.fr/test1.jpg", + "titre": null, + "auteur": null, + "licence": null, + "type_media": "image" + }, + { + "url": "https://test.fr/test2.jpg", + "type_media": "image" + }, + { + "url": "https://test.fr/test3.jpg", + "titre": "Title 3.1", + "auteur": "Author 3.1", + "licence": "License 1", + "type_media": "pdf" + } + ] + }, + "geometry": { + "type": "LineString", + "coordinates": [ [ 6.449592517966364, 44.733424655086957 ], [ 6.449539623508488, 44.733394939411369 ] ] + } + } + ] +} \ No newline at end of file diff --git a/geotrek/trekking/tests/data/schema_randonnee_parser/correct_trek.geojson b/geotrek/trekking/tests/data/schema_randonnee_parser/correct_trek.geojson new file mode 100644 index 0000000000..639af58aaa --- /dev/null +++ b/geotrek/trekking/tests/data/schema_randonnee_parser/correct_trek.geojson @@ -0,0 +1,26 @@ +{ + "type": "FeatureCollection", + "name": "sql_statement", + "crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } }, + "features": [ + { + "type": "Feature", + "properties": { + "id_local": "1", + "producteur": "Producer 1", + "uuid": "be5851a9-87d4-467c-ba95-16d474480976", + "url": "https://test.com", + "nom_itineraire": "Trek 1", + "pratique": "pĂ©destre", + "depart": "Departure 1", + "arrivee": "Arrival 1", + "instructions": "Instructions 1", + "parking_geometrie": "POINT(6.449678851833768 44.733442822460994)" + }, + "geometry": { + "type": "LineString", + "coordinates": [ [ 6.449592517966364, 44.733424655086957 ], [ 6.449539623508488, 44.733394939411369 ] ] + } + } + ] +} \ No newline at end of file diff --git a/geotrek/trekking/tests/data/schema_randonnee_parser/del_medias_info_after_update.geojson b/geotrek/trekking/tests/data/schema_randonnee_parser/del_medias_info_after_update.geojson new file mode 100644 index 0000000000..e20ff8cbcb --- /dev/null +++ b/geotrek/trekking/tests/data/schema_randonnee_parser/del_medias_info_after_update.geojson @@ -0,0 +1,43 @@ +{ + "type": "FeatureCollection", + "name": "sql_statement", + "crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } }, + "features": [ + { + "type": "Feature", + "properties": { + "id_local": "1", + "producteur": "Producer 1", + "nom_itineraire": "Trek 1", + "pratique": "pĂ©destre", + "depart": "Departure 1", + "arrivee": "Arrival 1", + "instructions": "Instructions 1", + "medias": [ + { + "url": "https://test.fr/test1.jpg", + "titre": null, + "auteur": null, + "licence": null, + "type_media": "image" + }, + { + "url": "https://test.fr/test2.jpg", + "type_media": "image" + }, + { + "url": "https://test.fr/test3.jpg", + "titre": "Title 3", + "auteur": "Author 3", + "licence": "License 1", + "type_media": "pdf" + } + ] + }, + "geometry": { + "type": "LineString", + "coordinates": [ [ 6.449592517966364, 44.733424655086957 ], [ 6.449539623508488, 44.733394939411369 ] ] + } + } + ] +} \ No newline at end of file diff --git a/geotrek/trekking/tests/data/schema_randonnee_parser/del_medias_info_before_update.geojson b/geotrek/trekking/tests/data/schema_randonnee_parser/del_medias_info_before_update.geojson new file mode 100644 index 0000000000..5f0f3e383c --- /dev/null +++ b/geotrek/trekking/tests/data/schema_randonnee_parser/del_medias_info_before_update.geojson @@ -0,0 +1,53 @@ +{ + "type": "FeatureCollection", + "name": "sql_statement", + "crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } }, + "features": [ + { + "type": "Feature", + "properties": { + "id_local": "1", + "producteur": "Producer 1", + "nom_itineraire": "Trek 1", + "pratique": "pĂ©destre", + "depart": "Departure 1", + "arrivee": "Arrival 1", + "instructions": "Instructions 1", + "medias": [ + { + "url": "https://test.fr/test1.jpg", + "titre": "Title 1", + "auteur": "Author 1", + "licence": "License 1", + "type_media": "image" + }, + { + "url": "https://test.fr/test2.jpg", + "titre": "Title 2", + "auteur": "Author 2", + "licence": "License 1", + "type_media": "image" + }, + { + "url": "https://test.fr/test3.jpg", + "titre": "Title 3", + "auteur": "Author 3", + "licence": "License 1", + "type_media": "image" + }, + { + "url": "https://test.fr/test4.jpg", + "titre": "Title 4", + "auteur": "Author 4", + "licence": "License 1", + "type_media": "image" + } + ] + }, + "geometry": { + "type": "LineString", + "coordinates": [ [ 6.449592517966364, 44.733424655086957 ], [ 6.449539623508488, 44.733394939411369 ] ] + } + } + ] +} \ No newline at end of file diff --git a/geotrek/trekking/tests/data/schema_randonnee_parser/description_and_url.geojson b/geotrek/trekking/tests/data/schema_randonnee_parser/description_and_url.geojson new file mode 100644 index 0000000000..b1c460ea3f --- /dev/null +++ b/geotrek/trekking/tests/data/schema_randonnee_parser/description_and_url.geojson @@ -0,0 +1,24 @@ +{ + "type": "FeatureCollection", + "name": "sql_statement", + "crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } }, + "features": [ + { + "type": "Feature", + "properties": { + "id_local": "1", + "producteur": "Producer 1", + "nom_itineraire": "Trek 1", + "pratique": "pĂ©destre", + "depart": "Departure 1", + "arrivee": "Arrival 1", + "instructions": "Instructions 1", + "url": "https://test.com" + }, + "geometry": { + "type": "LineString", + "coordinates": [ [ 6.449592517966364, 44.733424655086957 ], [ 6.449539623508488, 44.733394939411369 ] ] + } + } + ] +} \ No newline at end of file diff --git a/geotrek/trekking/tests/data/schema_randonnee_parser/description_no_url.geojson b/geotrek/trekking/tests/data/schema_randonnee_parser/description_no_url.geojson new file mode 100644 index 0000000000..f87f9b7f04 --- /dev/null +++ b/geotrek/trekking/tests/data/schema_randonnee_parser/description_no_url.geojson @@ -0,0 +1,24 @@ +{ + "type": "FeatureCollection", + "name": "sql_statement", + "crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } }, + "features": [ + { + "type": "Feature", + "properties": { + "id_local": "1", + "producteur": "Producer 1", + "nom_itineraire": "Trek 1", + "pratique": "pĂ©destre", + "depart": "Departure 1", + "arrivee": "Arrival 1", + "instructions": "Instructions 1", + "url": null + }, + "geometry": { + "type": "LineString", + "coordinates": [ [ 6.449592517966364, 44.733424655086957 ], [ 6.449539623508488, 44.733394939411369 ] ] + } + } + ] +} \ No newline at end of file diff --git a/geotrek/trekking/tests/data/schema_randonnee_parser/find_url_in_description.geojson b/geotrek/trekking/tests/data/schema_randonnee_parser/find_url_in_description.geojson new file mode 100644 index 0000000000..bc934850df --- /dev/null +++ b/geotrek/trekking/tests/data/schema_randonnee_parser/find_url_in_description.geojson @@ -0,0 +1,40 @@ +{ + "type": "FeatureCollection", + "name": "sql_statement", + "crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } }, + "features": [ + { + "type": "Feature", + "properties": { + "id_local": "1", + "producteur": "Producer 1", + "url": "https://test.com", + "nom_itineraire": "Trek 1", + "pratique": "pĂ©destre", + "depart": "Departure 1", + "arrivee": "Arrival 1", + "instructions": "Instructions 1" + }, + "geometry": { + "type": "LineString", + "coordinates": [ [ 6.449592517966364, 44.733424655086957 ], [ 6.449539623508488, 44.733394939411369 ] ] + } + }, + { + "type": "Feature", + "properties": { + "id_local": "2", + "producteur": "Producer 1", + "nom_itineraire": "Trek 1", + "pratique": "pĂ©destre", + "depart": "Departure 1", + "arrivee": "Arrival 1", + "instructions": "Instructions 2" + }, + "geometry": { + "type": "LineString", + "coordinates": [ [ 6.449592517966364, 44.733424655086957 ], [ 6.449539623508488, 44.733394939411369 ] ] + } + } + ] +} \ No newline at end of file diff --git a/geotrek/trekking/tests/data/schema_randonnee_parser/incorrect_geoms.geojson b/geotrek/trekking/tests/data/schema_randonnee_parser/incorrect_geoms.geojson new file mode 100644 index 0000000000..317b0af8c7 --- /dev/null +++ b/geotrek/trekking/tests/data/schema_randonnee_parser/incorrect_geoms.geojson @@ -0,0 +1,53 @@ +{ + "type": "FeatureCollection", + "name": "sql_statement", + "crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } }, + "features": [ + { + "type": "Feature", + "properties": { + "id_local": "1", + "producteur": "Producer 1", + "nom_itineraire": "Trek 1", + "pratique": "pĂ©destre", + "depart": "Departure 1", + "arrivee": "Arrival 1", + "instructions": "Instructions 1" + }, + "geometry": { + "type": "MultiLineString", + "coordinates": [ [ [ 6.449592517966364, 44.733424655086957 ], [ 6.449539623508488, 44.733394939411369 ] ], [ [ 6.449592517966364, 44.733424655086957 ], [ 6.449539623508488, 44.733394939411369 ] ] ] + } + }, + { + "type": "Feature", + "properties": { + "id_local": "2", + "producteur": "Producer 1", + "nom_itineraire": "Trek 2", + "pratique": "pĂ©destre", + "depart": "Departure 2", + "arrivee": "Arrival 2", + "instructions": "Instructions 2" + }, + "geometry": { + "coordinates": [ [ 6.449592517966364, 44.733424655086957 ], [ 6.449539623508488, 44.733394939411369 ] ] + } + }, + { + "type": "Feature", + "properties": { + "id_local": "3", + "producteur": "Producer 1", + "nom_itineraire": "Trek 3", + "pratique": "pĂ©destre", + "depart": "Departure 3", + "arrivee": "Arrival 3", + "instructions": "Instructions 3" + }, + "geometry": { + "type": "LineString" + } + } + ] +} \ No newline at end of file diff --git a/geotrek/trekking/tests/data/schema_randonnee_parser/incorrect_parking_location.geojson b/geotrek/trekking/tests/data/schema_randonnee_parser/incorrect_parking_location.geojson new file mode 100644 index 0000000000..4377b0fb99 --- /dev/null +++ b/geotrek/trekking/tests/data/schema_randonnee_parser/incorrect_parking_location.geojson @@ -0,0 +1,24 @@ +{ + "type": "FeatureCollection", + "name": "sql_statement", + "crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } }, + "features": [ + { + "type": "Feature", + "properties": { + "id_local": "1", + "producteur": "Producer 1", + "nom_itineraire": "Trek 1", + "pratique": "pĂ©destre", + "depart": "Departure 1", + "arrivee": "Arrival 1", + "instructions": "Instructions 1", + "parking_geometrie": "LINESTRING(973032.3522426573 6409775.480393637, 973028.3125000017 6409771.999999961)" + }, + "geometry": { + "type": "LineString", + "coordinates": [ [ 6.449592517966364, 44.733424655086957 ], [ 6.449539623508488, 44.733394939411369 ] ] + } + } + ] +} \ No newline at end of file diff --git a/geotrek/trekking/tests/data/schema_randonnee_parser/license_not_created.geojson b/geotrek/trekking/tests/data/schema_randonnee_parser/license_not_created.geojson new file mode 100644 index 0000000000..9ba8e4990f --- /dev/null +++ b/geotrek/trekking/tests/data/schema_randonnee_parser/license_not_created.geojson @@ -0,0 +1,32 @@ +{ + "type": "FeatureCollection", + "name": "sql_statement", + "crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } }, + "features": [ + { + "type": "Feature", + "properties": { + "id_local": "1", + "producteur": "Producer 1", + "nom_itineraire": "Trek 1", + "pratique": "pĂ©destre", + "depart": "Departure 1", + "arrivee": "Arrival 1", + "instructions": "Instructions 1", + "medias": [ + { + "url": "https://test.fr/test.jpg", + "titre": "Title 1", + "auteur": "Author 1", + "licence": "New license", + "type_media": "image" + } + ] + }, + "geometry": { + "type": "LineString", + "coordinates": [ [ 6.449592517966364, 44.733424655086957 ], [ 6.449539623508488, 44.733394939411369 ] ] + } + } + ] +} \ No newline at end of file diff --git a/geotrek/trekking/tests/data/schema_randonnee_parser/mod_medias_info_after_update.geojson b/geotrek/trekking/tests/data/schema_randonnee_parser/mod_medias_info_after_update.geojson new file mode 100644 index 0000000000..909cae0490 --- /dev/null +++ b/geotrek/trekking/tests/data/schema_randonnee_parser/mod_medias_info_after_update.geojson @@ -0,0 +1,39 @@ +{ + "type": "FeatureCollection", + "name": "sql_statement", + "crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } }, + "features": [ + { + "type": "Feature", + "properties": { + "id_local": "1", + "producteur": "Producer 1", + "nom_itineraire": "Trek 1", + "pratique": "pĂ©destre", + "depart": "Departure 1", + "arrivee": "Arrival 1", + "instructions": "Instructions 1", + "medias": [ + { + "url": "https://test.fr/test1.jpg", + "titre": "Title 1.2", + "auteur": "Author 1.2", + "licence": "License 2", + "type_media": "image" + }, + { + "url": "https://test.fr/test2.jpg", + "titre": "Title 2.2", + "auteur": "Author 2.2", + "licence": "License 2", + "type_media": "image" + } + ] + }, + "geometry": { + "type": "LineString", + "coordinates": [ [ 6.449592517966364, 44.733424655086957 ], [ 6.449539623508488, 44.733394939411369 ] ] + } + } + ] +} \ No newline at end of file diff --git a/geotrek/trekking/tests/data/schema_randonnee_parser/mod_medias_info_before_update.geojson b/geotrek/trekking/tests/data/schema_randonnee_parser/mod_medias_info_before_update.geojson new file mode 100644 index 0000000000..efeaff58be --- /dev/null +++ b/geotrek/trekking/tests/data/schema_randonnee_parser/mod_medias_info_before_update.geojson @@ -0,0 +1,39 @@ +{ + "type": "FeatureCollection", + "name": "sql_statement", + "crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } }, + "features": [ + { + "type": "Feature", + "properties": { + "id_local": "1", + "producteur": "Producer 1", + "nom_itineraire": "Trek 1", + "pratique": "pĂ©destre", + "depart": "Departure 1", + "arrivee": "Arrival 1", + "instructions": "Instructions 1", + "medias": [ + { + "url": "https://test.fr/test1.jpg", + "titre": "Title 1.1", + "auteur": "Author 1.1", + "licence": "License 1", + "type_media": "image" + }, + { + "url": "https://test.fr/test2.jpg", + "titre": "Title 2.1", + "auteur": "Author 2.1", + "licence": "License 1", + "type_media": "image" + } + ] + }, + "geometry": { + "type": "LineString", + "coordinates": [ [ 6.449592517966364, 44.733424655086957 ], [ 6.449539623508488, 44.733394939411369 ] ] + } + } + ] +} \ No newline at end of file diff --git a/geotrek/trekking/tests/data/schema_randonnee_parser/mod_medias_url_after_update.geojson b/geotrek/trekking/tests/data/schema_randonnee_parser/mod_medias_url_after_update.geojson new file mode 100644 index 0000000000..6f8ad8d8cb --- /dev/null +++ b/geotrek/trekking/tests/data/schema_randonnee_parser/mod_medias_url_after_update.geojson @@ -0,0 +1,32 @@ +{ + "type": "FeatureCollection", + "name": "sql_statement", + "crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } }, + "features": [ + { + "type": "Feature", + "properties": { + "id_local": "1", + "producteur": "Producer 1", + "nom_itineraire": "Trek 1", + "pratique": "pĂ©destre", + "depart": "Departure 1", + "arrivee": "Arrival 1", + "instructions": "Instructions 1", + "medias": [ + { + "url": "https://test.fr/test.png", + "titre": "Title 1", + "auteur": "Author 1", + "licence": "License 1", + "type_media": "image" + } + ] + }, + "geometry": { + "type": "LineString", + "coordinates": [ [ 6.449592517966364, 44.733424655086957 ], [ 6.449539623508488, 44.733394939411369 ] ] + } + } + ] +} \ No newline at end of file diff --git a/geotrek/trekking/tests/data/schema_randonnee_parser/mod_medias_url_before_update.geojson b/geotrek/trekking/tests/data/schema_randonnee_parser/mod_medias_url_before_update.geojson new file mode 100644 index 0000000000..e870d8be9f --- /dev/null +++ b/geotrek/trekking/tests/data/schema_randonnee_parser/mod_medias_url_before_update.geojson @@ -0,0 +1,32 @@ +{ + "type": "FeatureCollection", + "name": "sql_statement", + "crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } }, + "features": [ + { + "type": "Feature", + "properties": { + "id_local": "1", + "producteur": "Producer 1", + "nom_itineraire": "Trek 1", + "pratique": "pĂ©destre", + "depart": "Departure 1", + "arrivee": "Arrival 1", + "instructions": "Instructions 1", + "medias": [ + { + "url": "https://test.fr/test.jpg", + "titre": "Title 1", + "auteur": "Author 1", + "licence": "License 1", + "type_media": "image" + } + ] + }, + "geometry": { + "type": "LineString", + "coordinates": [ [ 6.449592517966364, 44.733424655086957 ], [ 6.449539623508488, 44.733394939411369 ] ] + } + } + ] +} \ No newline at end of file diff --git a/geotrek/trekking/tests/data/schema_randonnee_parser/no_description_no_url.geojson b/geotrek/trekking/tests/data/schema_randonnee_parser/no_description_no_url.geojson new file mode 100644 index 0000000000..250dde234f --- /dev/null +++ b/geotrek/trekking/tests/data/schema_randonnee_parser/no_description_no_url.geojson @@ -0,0 +1,24 @@ +{ + "type": "FeatureCollection", + "name": "sql_statement", + "crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } }, + "features": [ + { + "type": "Feature", + "properties": { + "id_local": "1", + "producteur": "Producer 1", + "nom_itineraire": "Trek 1", + "pratique": "pĂ©destre", + "depart": "Departure 1", + "arrivee": "Arrival 1", + "instructions": null, + "url": null + }, + "geometry": { + "type": "LineString", + "coordinates": [ [ 6.449592517966364, 44.733424655086957 ], [ 6.449539623508488, 44.733394939411369 ] ] + } + } + ] +} \ No newline at end of file diff --git a/geotrek/trekking/tests/data/schema_randonnee_parser/no_geom.geojson b/geotrek/trekking/tests/data/schema_randonnee_parser/no_geom.geojson new file mode 100644 index 0000000000..a00c014c03 --- /dev/null +++ b/geotrek/trekking/tests/data/schema_randonnee_parser/no_geom.geojson @@ -0,0 +1,19 @@ +{ + "type": "FeatureCollection", + "name": "sql_statement", + "crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } }, + "features": [ + { + "type": "Feature", + "properties": { + "id_local": "1", + "producteur": "Producer 1", + "nom_itineraire": "Trek 1", + "pratique": "pĂ©destre", + "depart": "Departure 1", + "arrivee": "Arrival 1", + "instructions": "Instructions 1" + } + } + ] +} \ No newline at end of file diff --git a/geotrek/trekking/tests/data/schema_randonnee_parser/no_parking_location.geojson b/geotrek/trekking/tests/data/schema_randonnee_parser/no_parking_location.geojson new file mode 100644 index 0000000000..ffe206ac8d --- /dev/null +++ b/geotrek/trekking/tests/data/schema_randonnee_parser/no_parking_location.geojson @@ -0,0 +1,24 @@ +{ + "type": "FeatureCollection", + "name": "sql_statement", + "crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } }, + "features": [ + { + "type": "Feature", + "properties": { + "id_local": "1", + "producteur": "Producer 1", + "nom_itineraire": "Trek 1", + "pratique": "pĂ©destre", + "depart": "Departure 1", + "arrivee": "Arrival 1", + "instructions": "Instructions 1", + "parking_geometrie": null + }, + "geometry": { + "type": "LineString", + "coordinates": [ [ 6.449592517966364, 44.733424655086957 ], [ 6.449539623508488, 44.733394939411369 ] ] + } + } + ] +} \ No newline at end of file diff --git a/geotrek/trekking/tests/data/schema_randonnee_parser/no_uuid.geojson b/geotrek/trekking/tests/data/schema_randonnee_parser/no_uuid.geojson new file mode 100644 index 0000000000..8d2d4a69fc --- /dev/null +++ b/geotrek/trekking/tests/data/schema_randonnee_parser/no_uuid.geojson @@ -0,0 +1,23 @@ +{ + "type": "FeatureCollection", + "name": "sql_statement", + "crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } }, + "features": [ + { + "type": "Feature", + "properties": { + "id_local": "1", + "producteur": "Producer 1", + "nom_itineraire": "Trek 1", + "pratique": "pĂ©destre", + "depart": "Departure 1", + "arrivee": "Arrival 1", + "instructions": "Instructions 1" + }, + "geometry": { + "type": "LineString", + "coordinates": [ [ 6.449592517966364, 44.733424655086957 ], [ 6.449539623508488, 44.733394939411369 ] ] + } + } + ] +} \ No newline at end of file diff --git a/geotrek/trekking/tests/data/schema_randonnee_parser/related_treks.geojson b/geotrek/trekking/tests/data/schema_randonnee_parser/related_treks.geojson new file mode 100644 index 0000000000..fd6817096a --- /dev/null +++ b/geotrek/trekking/tests/data/schema_randonnee_parser/related_treks.geojson @@ -0,0 +1,57 @@ +{ + "type": "FeatureCollection", + "name": "sql_statement", + "crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } }, + "features": [ + { + "type": "Feature", + "properties": { + "id_local": "1", + "producteur": "Producer 1", + "nom_itineraire": "Trek 1", + "pratique": "pĂ©destre", + "depart": "Departure 1", + "arrivee": "Arrival 1", + "instructions": "Instructions 1" + }, + "geometry": { + "type": "LineString", + "coordinates": [ [ 6.449592517966364, 44.733424655086957 ], [ 6.449539623508488, 44.733394939411369 ] ] + } + }, + { + "type": "Feature", + "properties": { + "id_local": "2", + "producteur": "Producer 1", + "nom_itineraire": "Trek 2", + "pratique": "pĂ©destre", + "depart": "Departure 2", + "arrivee": "Arrival 2", + "instructions": "Instructions 2", + "itineraire_parent": "1" + }, + "geometry": { + "type": "LineString", + "coordinates": [ [ 6.449592517966364, 44.733424655086957 ], [ 6.449539623508488, 44.733394939411369 ] ] + } + }, + { + "type": "Feature", + "properties": { + "id_local": "3", + "producteur": "Producer 1", + "nom_itineraire": "Trek 3", + "pratique": "pĂ©destre", + "depart": "Departure 3", + "arrivee": "Arrival 3", + "instructions": "Instructions 3", + "itineraire_parent": "1" + }, + "geometry": { + "type": "LineString", + "coordinates": [ [ 6.449592517966364, 44.733424655086957 ], [ 6.449539623508488, 44.733394939411369 ] ] + } + } + ] +} \ No newline at end of file diff --git a/geotrek/trekking/tests/data/schema_randonnee_parser/related_treks_parent_does_not_exist.geojson b/geotrek/trekking/tests/data/schema_randonnee_parser/related_treks_parent_does_not_exist.geojson new file mode 100644 index 0000000000..1c3c87342f --- /dev/null +++ b/geotrek/trekking/tests/data/schema_randonnee_parser/related_treks_parent_does_not_exist.geojson @@ -0,0 +1,24 @@ +{ + "type": "FeatureCollection", + "name": "sql_statement", + "crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } }, + "features": [ + { + "type": "Feature", + "properties": { + "id_local": "1", + "producteur": "Producer 1", + "nom_itineraire": "Trek 1", + "pratique": "pĂ©destre", + "depart": "Departure 1", + "arrivee": "Arrival 1", + "instructions": "Instructions 1", + "itineraire_parent": "2" + }, + "geometry": { + "type": "LineString", + "coordinates": [ [ 6.449592517966364, 44.733424655086957 ], [ 6.449539623508488, 44.733394939411369 ] ] + } + } + ] +} \ No newline at end of file diff --git a/geotrek/trekking/tests/data/schema_randonnee_parser/update_url_after.geojson b/geotrek/trekking/tests/data/schema_randonnee_parser/update_url_after.geojson new file mode 100644 index 0000000000..11faa1780a --- /dev/null +++ b/geotrek/trekking/tests/data/schema_randonnee_parser/update_url_after.geojson @@ -0,0 +1,41 @@ +{ + "type": "FeatureCollection", + "name": "sql_statement", + "crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } }, + "features": [ + { + "type": "Feature", + "properties": { + "id_local": "1", + "producteur": "Producer 1", + "url": "https://test.com", + "nom_itineraire": "Trek 1", + "pratique": "pĂ©destre", + "depart": "Departure 1", + "arrivee": "Arrival 1", + "instructions": null + }, + "geometry": { + "type": "LineString", + "coordinates": [ [ 6.449592517966364, 44.733424655086957 ], [ 6.449539623508488, 44.733394939411369 ] ] + } + }, + { + "type": "Feature", + "properties": { + "id_local": "2", + "producteur": "Producer 1", + "url": "https://test2.com", + "nom_itineraire": "Trek 2", + "pratique": "pĂ©destre", + "depart": "Departure 1", + "arrivee": "Arrival 1", + "instructions": "Instructions 2" + }, + "geometry": { + "type": "LineString", + "coordinates": [ [ 6.449592517966364, 44.733424655086957 ], [ 6.449539623508488, 44.733394939411369 ] ] + } + } + ] +} \ No newline at end of file diff --git a/geotrek/trekking/tests/data/schema_randonnee_parser/update_url_before.geojson b/geotrek/trekking/tests/data/schema_randonnee_parser/update_url_before.geojson new file mode 100644 index 0000000000..5b232be4ff --- /dev/null +++ b/geotrek/trekking/tests/data/schema_randonnee_parser/update_url_before.geojson @@ -0,0 +1,41 @@ +{ + "type": "FeatureCollection", + "name": "sql_statement", + "crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } }, + "features": [ + { + "type": "Feature", + "properties": { + "id_local": "1", + "producteur": "Producer 1", + "url": "https://test.com", + "nom_itineraire": "Trek 1", + "pratique": "pĂ©destre", + "depart": "Departure 1", + "arrivee": "Arrival 1", + "instructions": "Instructions 1" + }, + "geometry": { + "type": "LineString", + "coordinates": [ [ 6.449592517966364, 44.733424655086957 ], [ 6.449539623508488, 44.733394939411369 ] ] + } + }, + { + "type": "Feature", + "properties": { + "id_local": "2", + "producteur": "Producer 1", + "url": "https://test1.com", + "nom_itineraire": "Trek 2", + "pratique": "pĂ©destre", + "depart": "Departure 1", + "arrivee": "Arrival 1", + "instructions": "Instructions 2" + }, + "geometry": { + "type": "LineString", + "coordinates": [ [ 6.449592517966364, 44.733424655086957 ], [ 6.449539623508488, 44.733394939411369 ] ] + } + } + ] +} \ No newline at end of file diff --git a/geotrek/trekking/tests/data/schema_randonnee_parser/url_no_description.geojson b/geotrek/trekking/tests/data/schema_randonnee_parser/url_no_description.geojson new file mode 100644 index 0000000000..b4d6978506 --- /dev/null +++ b/geotrek/trekking/tests/data/schema_randonnee_parser/url_no_description.geojson @@ -0,0 +1,24 @@ +{ + "type": "FeatureCollection", + "name": "sql_statement", + "crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } }, + "features": [ + { + "type": "Feature", + "properties": { + "id_local": "1", + "producteur": "Producer 1", + "nom_itineraire": "Trek 1", + "pratique": "pĂ©destre", + "depart": "Departure 1", + "arrivee": "Arrival 1", + "instructions": null, + "url": "https://test.com" + }, + "geometry": { + "type": "LineString", + "coordinates": [ [ 6.449592517966364, 44.733424655086957 ], [ 6.449539623508488, 44.733394939411369 ] ] + } + } + ] +} \ No newline at end of file diff --git a/geotrek/trekking/tests/data/schema_randonnee_parser/with_medias.geojson b/geotrek/trekking/tests/data/schema_randonnee_parser/with_medias.geojson new file mode 100644 index 0000000000..6f922d438d --- /dev/null +++ b/geotrek/trekking/tests/data/schema_randonnee_parser/with_medias.geojson @@ -0,0 +1,66 @@ +{ + "type": "FeatureCollection", + "name": "sql_statement", + "crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } }, + "features": [ + { + "type": "Feature", + "properties": { + "id_local": "1", + "producteur": "Producer 1", + "nom_itineraire": "Trek 1", + "pratique": "pĂ©destre", + "depart": "Departure 1", + "arrivee": "Arrival 1", + "instructions": "Instructions 1", + "medias": [ + { + "url": "https://test.fr/404", + "titre": "Title 0", + "auteur": "Author 0", + "licence": "License 1", + "type_media": "image" + }, + { + "url": "https://test.fr/none", + "titre": "Title 1", + "auteur": "Author 1", + "licence": "License 1", + "type_media": "image" + }, + { + "url": null, + "titre": "Title 2", + "auteur": "Author 1", + "licence": "License 1", + "type_media": "image" + }, + { + "titre": "Title 3", + "auteur": "Author 1", + "licence": "License 1", + "type_media": "image" + }, + { + "url": "https://test.fr/test.jpg", + "titre": "Title 4", + "auteur": "Author 1", + "licence": "License 1", + "type_media": "pdf" + }, + { + "url": "https://test.fr/test.jpg", + "titre": "Title 5", + "auteur": "Author 1", + "licence": "License 1", + "type_media": "image" + } + ] + }, + "geometry": { + "type": "LineString", + "coordinates": [ [ 6.449592517966364, 44.733424655086957 ], [ 6.449539623508488, 44.733394939411369 ] ] + } + } + ] +} \ No newline at end of file diff --git a/geotrek/trekking/tests/data/schema_randonnee_parser/with_null_medias.geojson b/geotrek/trekking/tests/data/schema_randonnee_parser/with_null_medias.geojson new file mode 100644 index 0000000000..119f36d120 --- /dev/null +++ b/geotrek/trekking/tests/data/schema_randonnee_parser/with_null_medias.geojson @@ -0,0 +1,24 @@ +{ + "type": "FeatureCollection", + "name": "sql_statement", + "crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } }, + "features": [ + { + "type": "Feature", + "properties": { + "id_local": "1", + "producteur": "Producer 1", + "nom_itineraire": "Trek 1", + "pratique": "pĂ©destre", + "depart": "Departure 1", + "arrivee": "Arrival 1", + "instructions": "Instructions 1", + "medias": null + }, + "geometry": { + "type": "LineString", + "coordinates": [ [ 6.449592517966364, 44.733424655086957 ], [ 6.449539623508488, 44.733394939411369 ] ] + } + } + ] +} \ No newline at end of file diff --git a/geotrek/trekking/tests/test_parsers.py b/geotrek/trekking/tests/test_parsers.py index 99d00aaeef..be5e62b70b 100644 --- a/geotrek/trekking/tests/test_parsers.py +++ b/geotrek/trekking/tests/test_parsers.py @@ -18,14 +18,15 @@ from geotrek.common.utils import testdata from geotrek.common.utils.file_infos import get_encoding_file -from geotrek.common.models import Theme, FileType, Attachment, Label +from geotrek.common.models import Theme, FileType, Attachment, Label, RecordSource, License from geotrek.common.tests.mixins import GeotrekParserTestMixin +from geotrek.common.parsers import DownloadImportError from geotrek.core.tests.factories import PathFactory from geotrek.trekking.tests.factories import RouteFactory -from geotrek.trekking.models import POI, POIType, Service, Trek, DifficultyLevel, Route +from geotrek.trekking.models import POI, POIType, Service, Trek, DifficultyLevel, Route, Practice, OrderedTrekChild from geotrek.trekking.parsers import ( TrekParser, GeotrekPOIParser, GeotrekServiceParser, GeotrekTrekParser, ApidaeTrekParser, ApidaeTrekThemeParser, - ApidaePOIParser, _prepare_attachment_from_apidae_illustration, RowImportError + ApidaePOIParser, SchemaRandonneeParser, _prepare_attachment_from_apidae_illustration, RowImportError ) @@ -1784,3 +1785,279 @@ def test_it_returns_empty_strings_if_translation_not_found(self): _prepare_attachment_from_apidae_illustration(self.illustration, 'libelleEn'), expected_result ) + + +class SchemaRandonneeParserWithURL(SchemaRandonneeParser): + url = "https://test.com" + + +class SchemaRandonneeParserWithLicenseCreation(SchemaRandonneeParser): + field_options = { + 'attachments': {'create_license': True} + } + + +@skipIf(settings.TREKKING_TOPOLOGY_ENABLED, 'Test without dynamic segmentation only') +class SchemaRandonneeParserTests(TestCase): + @classmethod + def setUpTestData(cls): + FileType.objects.create(type="Photographie") + Practice.objects.create(name="PĂ©destre") + RecordSource.objects.create(name="Producer 1") + License.objects.create(label="License 1") + License.objects.create(label="License 2") + + def call_import_command_with_file(self, filename, **kwargs): + filename = os.path.join(os.path.dirname(__file__), 'data', 'schema_randonnee_parser', filename) + call_command('import', 'geotrek.trekking.parsers.SchemaRandonneeParser', filename, stdout=kwargs.get('output')) + + def mocked_url(self, url=None, verb=None): + if url and 'jpg' in url: + return Mock(status_code=200, content=b"mocked content jpg", headers={'content-length': 18}) + elif url and 'png' in url: + return Mock(status_code=200, content=b"mocked content png", headers={'content-length': 18}) + elif url and 'none' in url: + return Mock(status_code=200, content=None, headers={'content-length': 0}) + elif url and '404' in url: + raise DownloadImportError('mock error message') + response = { + "type": "FeatureCollection", + "name": "sql_statement", + "crs": {"type": "name", "properties": {"name": "urn:ogc:def:crs:OGC:1.3:CRS84"}}, + "features": [ + { + "type": "Feature", + "properties": { + "id_local": "1", + "producteur": "Producer 1", + "nom_itineraire": "Trek 1", + "pratique": "pĂ©destre", + "depart": "Departure 1", + "arrivee": "Arrival 1", + "instructions": "Instructions 1" + }, + "geometry": { + "type": "LineString", + "coordinates": [[6.449592517966364, 44.733424655086957], [6.449539623508488, 44.733394939411369]] + } + } + ] + } + return Mock(json=lambda: response) + + @mock.patch('geotrek.common.parsers.Parser.request_or_retry') + def test_parse_from_url(self, mocked_request_or_retry): + mocked_request_or_retry.return_value = self.mocked_url() + call_command('import', 'geotrek.trekking.tests.test_parsers.SchemaRandonneeParserWithURL') + self.assertEqual(Trek.objects.count(), 1) + + def test_create_basic_trek(self): + self.call_import_command_with_file('correct_trek.geojson') + self.assertEqual(Trek.objects.count(), 1) + trek = Trek.objects.get() + self.assertEqual(trek.eid, "be5851a9-87d4-467c-ba95-16d474480976") + self.assertEqual(trek.geom.srid, settings.SRID) + self.assertEqual(trek.geom.geom_type, 'LineString') + self.assertEqual(trek.geom.num_coords, 2) + self.assertEqual(trek.parking_location.srid, settings.SRID) + self.assertEqual(trek.parking_location.geom_type, 'Point') + self.assertEqual(trek.description, "Instructions 1\n\nhttps://test.com") + + @mock.patch('geotrek.common.parsers.Parser.request_or_retry') + def test_parse_attachments(self, mocked_request_or_retry): + mocked_request_or_retry.side_effect = self.mocked_url + output = StringIO() + self.call_import_command_with_file('with_medias.geojson', output=output) + self.assertEqual(Trek.objects.count(), 1) + output_value = output.getvalue() + trek = Trek.objects.get() + self.assertEqual(trek.attachments.count(), 1) + attachment = trek.attachments.get() + self.assertEqual(attachment.title, 'Title 5') + self.assertEqual(attachment.author, 'Author 1') + self.assertEqual(attachment.license.label, 'License 1') + self.assertIn("Failed to load attachment: mock error message", output_value) + + def test_medias_is_null(self): + self.call_import_command_with_file('with_null_medias.geojson') + self.assertEqual(Trek.objects.count(), 1) + trek = Trek.objects.get() + self.assertEqual(trek.attachments.count(), 0) + + @mock.patch('geotrek.common.parsers.Parser.request_or_retry') + def test_update_of_attachments_info(self, mocked_request_or_retry): + mocked_request_or_retry.side_effect = self.mocked_url + self.call_import_command_with_file('mod_medias_info_before_update.geojson') + self.call_import_command_with_file('mod_medias_info_after_update.geojson') + self.assertEqual(Trek.objects.count(), 1) + trek = Trek.objects.get() + self.assertEqual(trek.attachments.count(), 2) + attachment_1 = trek.attachments.get(title='Title 1.2') + self.assertIsNotNone(attachment_1) + self.assertEqual(attachment_1.author, 'Author 1.2') + self.assertEqual(attachment_1.license.label, 'License 2') + attachment_2 = trek.attachments.get(title='Title 2.2') + self.assertIsNotNone(attachment_2) + self.assertEqual(attachment_2.author, 'Author 2.2') + self.assertEqual(attachment_2.license.label, 'License 2') + + @mock.patch('geotrek.common.parsers.Parser.request_or_retry') + def test_add_attachment_info(self, mocked_request_or_retry): + mocked_request_or_retry.side_effect = self.mocked_url + self.call_import_command_with_file('add_medias_info_before_update.geojson') + self.call_import_command_with_file('add_medias_info_after_update.geojson') + self.assertEqual(Trek.objects.count(), 1) + trek = Trek.objects.get() + self.assertEqual(trek.attachments.count(), 3) + attachment_1 = trek.attachments.get(title='Title 1.2') + self.assertIsNotNone(attachment_1) + self.assertEqual(attachment_1.author, 'Author 1.2') + self.assertEqual(attachment_1.license.label, 'License 2') + attachment_2 = trek.attachments.get(title='Title 2.2') + self.assertIsNotNone(attachment_2) + self.assertEqual(attachment_2.author, 'Author 2.2') + self.assertEqual(attachment_2.license.label, 'License 2') + attachment_3 = trek.attachments.get(title='Title 3.2') + self.assertIsNotNone(attachment_3) + self.assertEqual(attachment_3.author, 'Author 3.2') + self.assertEqual(attachment_3.license.label, 'License 2') + + @mock.patch('geotrek.common.parsers.Parser.request_or_retry') + def test_mod_attachment_url(self, mocked_request_or_retry): + mocked_request_or_retry.side_effect = self.mocked_url + self.call_import_command_with_file('mod_medias_url_before_update.geojson') + self.call_import_command_with_file('mod_medias_url_after_update.geojson') + self.assertEqual(Trek.objects.count(), 1) + trek = Trek.objects.get() + self.assertEqual(trek.attachments.count(), 1) + attachment = trek.attachments.get() + with attachment.attachment_file.file.open() as f: + self.assertEqual(f.read(), b'mocked content png') + self.assertEqual(attachment.title, 'Title 1') + self.assertEqual(attachment.author, 'Author 1') + self.assertEqual(attachment.license.label, 'License 1') + + @mock.patch('geotrek.common.parsers.Parser.request_or_retry') + def test_del_attachment_info(self, mocked_request_or_retry): + mocked_request_or_retry.side_effect = self.mocked_url + self.call_import_command_with_file('del_medias_info_before_update.geojson') + self.call_import_command_with_file('del_medias_info_after_update.geojson') + self.assertEqual(Trek.objects.count(), 1) + trek = Trek.objects.get() + self.assertEqual(trek.attachments.count(), 2) + attachments = trek.attachments.all() + self.assertEqual(attachments[0].title, '') + self.assertEqual(attachments[1].title, '') + self.assertEqual(attachments[0].author, '') + self.assertEqual(attachments[1].author, '') + self.assertIsNone(attachments[0].license) + self.assertIsNone(attachments[1].license) + + @mock.patch('geotrek.common.parsers.Parser.request_or_retry') + def test_license_does_not_exist(self, mocked_request_or_retry): + mocked_request_or_retry.side_effect = self.mocked_url + output = StringIO() + self.call_import_command_with_file('license_not_created.geojson', output=output) + self.assertEqual(Trek.objects.count(), 1) + trek = Trek.objects.get() + self.assertEqual(trek.attachments.count(), 1) + output_value = output.getvalue() + self.assertIn("License 'New license' does not exist in Geotrek-Admin. Please add it", output_value) + + @mock.patch('geotrek.common.parsers.Parser.request_or_retry') + def test_license_has_been_created(self, mocked_request_or_retry): + mocked_request_or_retry.side_effect = self.mocked_url + output = StringIO() + parser_name = 'geotrek.trekking.tests.test_parsers.SchemaRandonneeParserWithLicenseCreation' + filename = os.path.join(os.path.dirname(__file__), 'data', 'schema_randonnee_parser', 'license_not_created.geojson') + call_command('import', parser_name, filename, stdout=output) + self.assertEqual(Trek.objects.count(), 1) + trek = Trek.objects.get() + self.assertEqual(trek.attachments.count(), 1) + attachment = trek.attachments.get() + self.assertEqual(attachment.license.label, 'New license') + output_value = output.getvalue() + self.assertIn("License 'New license' did not exist in Geotrek-Admin and was automatically created", output_value) + + def test_create_related_treks(self): + self.call_import_command_with_file('related_treks.geojson') + self.assertEqual(Trek.objects.count(), 3) + self.assertEqual(OrderedTrekChild.objects.count(), 2) + parent_trek = Trek.objects.get(name="Trek 1") + child_trek_1 = Trek.objects.get(name="Trek 2") + child_trek_2 = Trek.objects.get(name="Trek 3") + self.assertTrue(OrderedTrekChild.objects.filter(parent=parent_trek.pk, child=child_trek_1.pk).exists()) + self.assertTrue(OrderedTrekChild.objects.filter(parent=parent_trek.pk, child=child_trek_2.pk).exists()) + + def test_related_treks_parent_does_not_exist(self): + self.call_import_command_with_file('related_treks_parent_does_not_exist.geojson') + self.assertEqual(Trek.objects.count(), 1) + self.assertEqual(OrderedTrekChild.objects.count(), 0) + + def test_trek_without_uuid(self): + self.call_import_command_with_file('no_uuid.geojson') + self.assertEqual(Trek.objects.count(), 1) + trek = Trek.objects.get() + self.assertEqual(trek.eid, "1") + + def test_no_geom(self): + output = StringIO() + self.call_import_command_with_file('no_geom.geojson', output=output) + self.assertEqual(Trek.objects.count(), 0) + output_value = output.getvalue() + self.assertIn("Trek geometry is None", output_value) + + def test_incorrect_geoms(self): + output = StringIO() + self.call_import_command_with_file('incorrect_geoms.geojson', output=output) + self.assertEqual(Trek.objects.count(), 0) + output_value = output.getvalue() + self.assertIn("Invalid geometry type for field 'geometry'. Should be LineString, not MultiLineString", output_value) + self.assertIn("Invalid geometry type for field 'geometry'. Should be LineString, not None", output_value) + self.assertIn("Invalid geometry for field 'geometry'. Should contain coordinates", output_value) + + def test_no_parking_location(self): + self.call_import_command_with_file('no_parking_location.geojson') + self.assertEqual(Trek.objects.count(), 1) + trek = Trek.objects.get() + self.assertIsNone(trek.parking_location) + + def test_incorrect_parking_location(self): + output = StringIO() + self.call_import_command_with_file('incorrect_parking_location.geojson', output=output) + self.assertEqual(Trek.objects.count(), 1) + trek = Trek.objects.get() + self.assertIsNone(trek.parking_location) + self.assertIn("Bad value for parking geometry: should be a Point", output.getvalue()) + + def test_description_and_url(self): + self.call_import_command_with_file('description_and_url.geojson') + self.assertEqual(Trek.objects.count(), 1) + trek = Trek.objects.get() + self.assertEqual(trek.description, 'Instructions 1\n\nhttps://test.com') + + def test_description_no_url(self): + self.call_import_command_with_file('description_no_url.geojson') + self.assertEqual(Trek.objects.count(), 1) + trek = Trek.objects.get() + self.assertEqual(trek.description, 'Instructions 1') + + def test_url_no_description(self): + self.call_import_command_with_file('url_no_description.geojson') + self.assertEqual(Trek.objects.count(), 1) + trek = Trek.objects.get() + self.assertEqual(trek.description, 'https://test.com') + + def test_no_description_no_url(self): + self.call_import_command_with_file('no_description_no_url.geojson') + self.assertEqual(Trek.objects.count(), 1) + trek = Trek.objects.get() + self.assertEqual(trek.description, '') + + def test_update_url(self): + self.call_import_command_with_file('update_url_before.geojson') + self.call_import_command_with_file('update_url_after.geojson') + trek1 = Trek.objects.get(eid="1") + self.assertEqual(trek1.description, "https://test.com") + trek2 = Trek.objects.get(eid="2") + self.assertEqual(trek2.description, "Instructions 2\n\nhttps://test2.com") diff --git a/requirements.txt b/requirements.txt index 8cfdfdae82..4c80ec0154 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,7 +6,7 @@ # amqp==5.2.0 # via kombu -appy==1.0.15 +appy==1.0.18 # via django-appypod asgiref==3.8.1 # via django @@ -105,6 +105,7 @@ django==4.2.16 # django-appypod # django-celery-results # django-clearcache + # django-compressor # django-crispy-forms # django-embed-video # django-filter @@ -131,7 +132,7 @@ django-clearcache==1.2.1 # via geotrek (setup.py) django-colorfield==0.11.0 # via geotrek (setup.py) -django-compressor==4.4 +django-compressor==4.5.1 # via mapentity django-crispy-forms==2.3 # via @@ -187,7 +188,7 @@ drf-yasg==1.21.5 # via # django-large-image # geotrek (setup.py) -easy-thumbnails==2.8.5 +easy-thumbnails==2.9 # via # geotrek (setup.py) # mapentity @@ -204,11 +205,11 @@ geojson==3.1.0 # tif2geojson gpxpy==1.6.2 # via mapentity -gunicorn==22.0.0 +gunicorn==23.0.0 # via geotrek (setup.py) html5lib==1.1 # via weasyprint -idna==3.7 +idna==3.10 # via requests importlib-metadata==6.8.0 # via @@ -281,7 +282,7 @@ prompt-toolkit==3.0.47 # via click-repl psutil==5.9.8 # via large-image -psycopg2==2.9.7 +psycopg2==2.9.10 # via geotrek (setup.py) pycparser==2.22 # via cffi @@ -309,7 +310,7 @@ pytz==2024.2 # drf-yasg pyvips==2.2.3 # via large-image-source-vips -rcssmin==1.1.1 +rcssmin==1.1.2 # via django-compressor redis==5.1.1 # via geotrek (setup.py) @@ -323,7 +324,7 @@ requests==2.32.3 # django-embed-video # landez # mapentity -rjsmin==1.2.1 +rjsmin==1.2.2 # via django-compressor ruamel-yaml==0.18.6 # via drf-yasg @@ -408,7 +409,7 @@ zodbpickle==4.0 # via zodb zope-deferredimport==5.0 # via persistent -zope-interface==6.4.post2 +zope-interface==7.1.1 # via # btrees # datetime