Merge pull request #1413 from umap-project/almet/ci
This commit is contained in:
commit
34ee8e81de
47 changed files with 594 additions and 300 deletions
69
.github/workflows/test-docs.yml
vendored
Normal file
69
.github/workflows/test-docs.yml
vendored
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
name: Test & Docs
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ master ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ master ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
tests:
|
||||||
|
timeout-minutes: 60
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
services:
|
||||||
|
postgres:
|
||||||
|
image: postgis/postgis:12-2.5
|
||||||
|
ports:
|
||||||
|
- 5432:5432
|
||||||
|
env:
|
||||||
|
POSTGRES_USER: postgres
|
||||||
|
POSTGRES_PASSWORD: postgres
|
||||||
|
POSTGRES_DB: postgres
|
||||||
|
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
|
||||||
|
dependencies: [normal, minimal]
|
||||||
|
database: [postgresql]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Set up Python ${{ matrix.python-version }}
|
||||||
|
uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
|
python-version: ${{ matrix.python-version }}
|
||||||
|
cache: 'pip'
|
||||||
|
cache-dependency-path: '**/pyproject.toml'
|
||||||
|
- name: Change dependencies to minimal supported versions
|
||||||
|
run: sed -i -e '/requires-python/!s/>=/==/g; /requires-python/!s/~=.*==\(.*\)/==\1/g; /requires-python/!s/~=/==/g;' pyproject.toml
|
||||||
|
if: matrix.dependencies == 'minimal'
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install libgdal-dev
|
||||||
|
python -m pip install --upgrade pip
|
||||||
|
make develop installjs vendors
|
||||||
|
- name: run tests
|
||||||
|
run: make test
|
||||||
|
env:
|
||||||
|
DJANGO_SETTINGS_MODULE: 'umap.tests.settings'
|
||||||
|
UMAP_SETTINGS: 'umap/tests/settings.py'
|
||||||
|
lint:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Set up Python
|
||||||
|
uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
|
python-version: "3.11"
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
python3 -m pip install -e .[test,dev]
|
||||||
|
|
||||||
|
- name: Run Lint
|
||||||
|
run: make lint
|
||||||
|
|
||||||
|
- name: Run Docs
|
||||||
|
run: make docs
|
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -4,10 +4,12 @@ umap/settings/local/*
|
||||||
docs/_build
|
docs/_build
|
||||||
umap/remote_static
|
umap/remote_static
|
||||||
tmp/*
|
tmp/*
|
||||||
node_modules/*
|
|
||||||
umap/static/umap/vendors
|
umap/static/umap/vendors
|
||||||
site/*
|
site/*
|
||||||
.pytest_cache/
|
.pytest_cache/
|
||||||
|
node_modules
|
||||||
|
umap.conf
|
||||||
|
data
|
||||||
|
|
||||||
### Python ###
|
### Python ###
|
||||||
# Byte-compiled / optimized / DLL files
|
# Byte-compiled / optimized / DLL files
|
||||||
|
|
24
Makefile
24
Makefile
|
@ -10,13 +10,21 @@ develop: ## Install the test and dev dependencies
|
||||||
python3 -m pip install -e .[test,dev]
|
python3 -m pip install -e .[test,dev]
|
||||||
playwright install
|
playwright install
|
||||||
|
|
||||||
.PHONY: pretty-templates
|
.PHONY: format
|
||||||
pretty-templates: ## Prettify template files
|
format: ## Format the code and templates files
|
||||||
djlint umap/templates --reformat
|
djlint umap/templates --reformat &&\
|
||||||
|
isort --profile black . &&\
|
||||||
|
ruff format --target-version=py38 .
|
||||||
|
|
||||||
.PHONY: lint-templates
|
.PHONY: lint
|
||||||
lint-templates: ## Lint template files
|
lint: ## Lint the code and template files
|
||||||
djlint umap/templates --lint
|
djlint umap/templates --lint &&\
|
||||||
|
isort --check --profile black . &&\
|
||||||
|
ruff format --check --target-version=py38 . &&\
|
||||||
|
vermin --no-tips --violations -t=3.8- .
|
||||||
|
|
||||||
|
docs: ## Compile the docs
|
||||||
|
mkdocs build
|
||||||
|
|
||||||
.PHONY: version
|
.PHONY: version
|
||||||
version: ## Display the current version
|
version: ## Display the current version
|
||||||
|
@ -47,10 +55,10 @@ publish: ## Publish the Python package to Pypi
|
||||||
make clean
|
make clean
|
||||||
|
|
||||||
test:
|
test:
|
||||||
py.test -xv umap/tests/
|
pytest -xv umap/tests/
|
||||||
|
|
||||||
test-integration:
|
test-integration:
|
||||||
py.test -xv umap/tests/integration/
|
pytest -xv umap/tests/integration/
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f dist/*
|
rm -f dist/*
|
||||||
|
|
|
@ -3,10 +3,7 @@ import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
os.environ.setdefault(
|
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "umap.settings")
|
||||||
"DJANGO_SETTINGS_MODULE",
|
|
||||||
"umap.settings"
|
|
||||||
)
|
|
||||||
|
|
||||||
from django.core.management import execute_from_command_line
|
from django.core.management import execute_from_command_line
|
||||||
|
|
||||||
|
|
25
package-lock.json
generated
25
package-lock.json
generated
|
@ -10,6 +10,7 @@
|
||||||
"license": "WTFPL",
|
"license": "WTFPL",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tmcw/togeojson": "^5.8.0",
|
"@tmcw/togeojson": "^5.8.0",
|
||||||
|
"colorbrewer": "^1.5.6",
|
||||||
"csv2geojson": "5.1.1",
|
"csv2geojson": "5.1.1",
|
||||||
"dompurify": "^3.0.3",
|
"dompurify": "^3.0.3",
|
||||||
"georsstogeojson": "^0.1.0",
|
"georsstogeojson": "^0.1.0",
|
||||||
|
@ -31,6 +32,7 @@
|
||||||
"leaflet.path.drag": "0.0.6",
|
"leaflet.path.drag": "0.0.6",
|
||||||
"leaflet.photon": "0.8.0",
|
"leaflet.photon": "0.8.0",
|
||||||
"osmtogeojson": "^3.0.0-beta.3",
|
"osmtogeojson": "^3.0.0-beta.3",
|
||||||
|
"simple-statistics": "^7.8.3",
|
||||||
"togpx": "^0.5.4",
|
"togpx": "^0.5.4",
|
||||||
"tokml": "0.4.0"
|
"tokml": "0.4.0"
|
||||||
},
|
},
|
||||||
|
@ -494,6 +496,11 @@
|
||||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/colorbrewer": {
|
||||||
|
"version": "1.5.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/colorbrewer/-/colorbrewer-1.5.6.tgz",
|
||||||
|
"integrity": "sha512-fONg2pGXyID8zNgKHBlagW8sb/AMShGzj4rRJfz5biZ7iuHQZYquSCLE/Co1oSQFmt/vvwjyezJCejQl7FG/tg=="
|
||||||
|
},
|
||||||
"node_modules/concat-map": {
|
"node_modules/concat-map": {
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||||
|
@ -2122,6 +2129,14 @@
|
||||||
"resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz",
|
||||||
"integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA="
|
"integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA="
|
||||||
},
|
},
|
||||||
|
"node_modules/simple-statistics": {
|
||||||
|
"version": "7.8.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/simple-statistics/-/simple-statistics-7.8.3.tgz",
|
||||||
|
"integrity": "sha512-JFvMY00t6SBGtwMuJ+nqgsx9ylkMiJ5JlK9bkj8AdvniIe5615wWQYkKHXe84XtSuc40G/tlrPu0A5/NlJvv8A==",
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/sinon": {
|
"node_modules/sinon": {
|
||||||
"version": "15.1.0",
|
"version": "15.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/sinon/-/sinon-15.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/sinon/-/sinon-15.1.0.tgz",
|
||||||
|
@ -2898,6 +2913,11 @@
|
||||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"colorbrewer": {
|
||||||
|
"version": "1.5.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/colorbrewer/-/colorbrewer-1.5.6.tgz",
|
||||||
|
"integrity": "sha512-fONg2pGXyID8zNgKHBlagW8sb/AMShGzj4rRJfz5biZ7iuHQZYquSCLE/Co1oSQFmt/vvwjyezJCejQl7FG/tg=="
|
||||||
|
},
|
||||||
"concat-map": {
|
"concat-map": {
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||||
|
@ -4143,6 +4163,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz",
|
||||||
"integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA="
|
"integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA="
|
||||||
},
|
},
|
||||||
|
"simple-statistics": {
|
||||||
|
"version": "7.8.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/simple-statistics/-/simple-statistics-7.8.3.tgz",
|
||||||
|
"integrity": "sha512-JFvMY00t6SBGtwMuJ+nqgsx9ylkMiJ5JlK9bkj8AdvniIe5615wWQYkKHXe84XtSuc40G/tlrPu0A5/NlJvv8A=="
|
||||||
|
},
|
||||||
"sinon": {
|
"sinon": {
|
||||||
"version": "15.1.0",
|
"version": "15.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/sinon/-/sinon-15.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/sinon/-/sinon-15.1.0.tgz",
|
||||||
|
|
|
@ -23,16 +23,14 @@ classifiers = [
|
||||||
"Topic :: Software Development :: Libraries :: Python Modules",
|
"Topic :: Software Development :: Libraries :: Python Modules",
|
||||||
"Programming Language :: Python",
|
"Programming Language :: Python",
|
||||||
"Programming Language :: Python :: 3 :: Only",
|
"Programming Language :: Python :: 3 :: Only",
|
||||||
"Programming Language :: Python :: 3.4",
|
|
||||||
"Programming Language :: Python :: 3.5",
|
|
||||||
"Programming Language :: Python :: 3.6",
|
|
||||||
"Programming Language :: Python :: 3.7",
|
|
||||||
"Programming Language :: Python :: 3.8",
|
"Programming Language :: Python :: 3.8",
|
||||||
"Programming Language :: Python :: 3.9",
|
"Programming Language :: Python :: 3.9",
|
||||||
"Programming Language :: Python :: 3.10",
|
"Programming Language :: Python :: 3.10",
|
||||||
|
"Programming Language :: Python :: 3.11",
|
||||||
|
"Programming Language :: Python :: 3.12",
|
||||||
]
|
]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"Django>=4.1",
|
"Django>=4.2,<5",
|
||||||
"django-agnocomplete==2.2.0",
|
"django-agnocomplete==2.2.0",
|
||||||
"django-compressor==4.3.1",
|
"django-compressor==4.3.1",
|
||||||
"django-environ==0.10.0",
|
"django-environ==0.10.0",
|
||||||
|
@ -47,17 +45,20 @@ dependencies = [
|
||||||
[project.optional-dependencies]
|
[project.optional-dependencies]
|
||||||
dev = [
|
dev = [
|
||||||
"hatch==1.7.0",
|
"hatch==1.7.0",
|
||||||
"black==23.3.0",
|
"ruff==0.1.6",
|
||||||
"djlint==1.31.0",
|
"djlint==1.31.0",
|
||||||
"mkdocs==1.5.2",
|
"mkdocs==1.5.2",
|
||||||
|
"vermin==1.5.2",
|
||||||
|
"pymdown-extensions==10.4",
|
||||||
|
"isort==5.12",
|
||||||
]
|
]
|
||||||
test = [
|
test = [
|
||||||
"factory-boy==3.2.1",
|
"factory-boy==3.2.1",
|
||||||
"playwright==1.38.0",
|
"playwright>=1.39,<2",
|
||||||
"pytest==6.2.5",
|
"pytest==6.2.5",
|
||||||
"pytest-django==4.5.2",
|
"pytest-django==4.5.2",
|
||||||
"pytest-playwright==0.4.2",
|
"pytest-playwright>=0.4.3,<1",
|
||||||
|
"pytest-xdist>=3.5.0,<4",
|
||||||
]
|
]
|
||||||
docker = [
|
docker = [
|
||||||
"uwsgi==2.0.21",
|
"uwsgi==2.0.21",
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
[pytest]
|
[pytest]
|
||||||
DJANGO_SETTINGS_MODULE=umap.tests.settings
|
DJANGO_SETTINGS_MODULE=umap.tests.settings
|
||||||
addopts = --pdbcls=IPython.terminal.debugger:Pdb --no-migrations
|
addopts = --pdbcls=IPython.terminal.debugger:Pdb --no-migrations --numprocesses auto
|
||||||
|
|
|
@ -1,10 +1,14 @@
|
||||||
from django.contrib.gis import admin
|
from django.contrib.gis import admin
|
||||||
from .models import Map, DataLayer, Pictogram, TileLayer, Licence
|
|
||||||
|
from .models import DataLayer, Licence, Map, Pictogram, TileLayer
|
||||||
|
|
||||||
|
|
||||||
class TileLayerAdmin(admin.ModelAdmin):
|
class TileLayerAdmin(admin.ModelAdmin):
|
||||||
list_display = ('name', 'rank', )
|
list_display = (
|
||||||
list_editable = ('rank', )
|
"name",
|
||||||
|
"rank",
|
||||||
|
)
|
||||||
|
list_editable = ("rank",)
|
||||||
|
|
||||||
|
|
||||||
class MapAdmin(admin.GISModelAdmin):
|
class MapAdmin(admin.GISModelAdmin):
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
|
from agnocomplete.core import AgnocompleteModel
|
||||||
|
from agnocomplete.register import register
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
|
|
||||||
|
|
||||||
from agnocomplete.register import register
|
|
||||||
from agnocomplete.core import AgnocompleteModel
|
|
||||||
|
|
||||||
|
|
||||||
@register
|
@register
|
||||||
class AutocompleteUser(AgnocompleteModel):
|
class AutocompleteUser(AgnocompleteModel):
|
||||||
model = get_user_model()
|
model = get_user_model()
|
||||||
|
@ -13,5 +11,5 @@ class AutocompleteUser(AgnocompleteModel):
|
||||||
|
|
||||||
def item(self, current_item):
|
def item(self, current_item):
|
||||||
data = super().item(current_item)
|
data = super().item(current_item)
|
||||||
data['url'] = current_item.get_url()
|
data["url"] = current_item.get_url()
|
||||||
return data
|
return data
|
||||||
|
|
|
@ -5,12 +5,9 @@ from django.core import management
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
os.environ.setdefault(
|
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "umap.settings")
|
||||||
"DJANGO_SETTINGS_MODULE",
|
|
||||||
"umap.settings"
|
|
||||||
)
|
|
||||||
management.execute_from_command_line()
|
management.execute_from_command_line()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
|
||||||
from django.urls import reverse_lazy
|
|
||||||
from django.shortcuts import get_object_or_404
|
|
||||||
from django.http import HttpResponseForbidden
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.http import HttpResponseForbidden
|
||||||
|
from django.shortcuts import get_object_or_404
|
||||||
|
from django.urls import reverse_lazy
|
||||||
|
|
||||||
from .views import simple_json_response
|
|
||||||
from .models import Map
|
from .models import Map
|
||||||
|
from .views import simple_json_response
|
||||||
|
|
||||||
LOGIN_URL = getattr(settings, "LOGIN_URL", "login")
|
LOGIN_URL = getattr(settings, "LOGIN_URL", "login")
|
||||||
LOGIN_URL = reverse_lazy(LOGIN_URL) if not LOGIN_URL.startswith("/") else LOGIN_URL
|
LOGIN_URL = reverse_lazy(LOGIN_URL) if not LOGIN_URL.startswith("/") else LOGIN_URL
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.contrib.gis.geos import Point
|
|
||||||
from django.contrib.auth import get_user_model
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
|
||||||
from django.template.defaultfilters import slugify
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
from django.contrib.gis.geos import Point
|
||||||
from django.forms.utils import ErrorList
|
from django.forms.utils import ErrorList
|
||||||
|
from django.template.defaultfilters import slugify
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from .models import Map, DataLayer
|
from .models import DataLayer, Map
|
||||||
|
|
||||||
DEFAULT_LATITUDE = (
|
DEFAULT_LATITUDE = (
|
||||||
settings.LEAFLET_LATITUDE if hasattr(settings, "LEAFLET_LATITUDE") else 51
|
settings.LEAFLET_LATITUDE if hasattr(settings, "LEAFLET_LATITUDE") else 51
|
||||||
|
|
|
@ -1,24 +1,23 @@
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from django.core.management.base import BaseCommand
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.core.management.base import BaseCommand
|
||||||
from django.template.loader import render_to_string
|
from django.template.loader import render_to_string
|
||||||
from django.utils.translation import to_locale
|
from django.utils.translation import to_locale
|
||||||
|
|
||||||
ROOT = Path(settings.PROJECT_DIR) / 'static/umap/locale/'
|
ROOT = Path(settings.PROJECT_DIR) / "static/umap/locale/"
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
|
|
||||||
def handle(self, *args, **options):
|
def handle(self, *args, **options):
|
||||||
self.verbosity = options['verbosity']
|
self.verbosity = options["verbosity"]
|
||||||
for code, name in settings.LANGUAGES:
|
for code, name in settings.LANGUAGES:
|
||||||
code = to_locale(code)
|
code = to_locale(code)
|
||||||
if self.verbosity > 0:
|
if self.verbosity > 0:
|
||||||
print("Processing", name)
|
print("Processing", name)
|
||||||
path = ROOT / '{code}.json'.format(code=code)
|
path = ROOT / "{code}.json".format(code=code)
|
||||||
if not path.exists():
|
if not path.exists():
|
||||||
print(path, 'does not exist.', 'Skipping')
|
print(path, "does not exist.", "Skipping")
|
||||||
else:
|
else:
|
||||||
with path.open(encoding="utf-8") as f:
|
with path.open(encoding="utf-8") as f:
|
||||||
if self.verbosity > 1:
|
if self.verbosity > 1:
|
||||||
|
@ -26,12 +25,11 @@ class Command(BaseCommand):
|
||||||
self.render(code, f.read())
|
self.render(code, f.read())
|
||||||
|
|
||||||
def render(self, code, json):
|
def render(self, code, json):
|
||||||
path = ROOT / '{code}.js'.format(code=code)
|
path = ROOT / "{code}.js".format(code=code)
|
||||||
with path.open("w", encoding="utf-8") as f:
|
with path.open("w", encoding="utf-8") as f:
|
||||||
content = render_to_string('umap/locale.js', {
|
content = render_to_string(
|
||||||
"locale": json,
|
"umap/locale.js", {"locale": json, "locale_code": code}
|
||||||
"locale_code": code
|
)
|
||||||
})
|
|
||||||
if self.verbosity > 1:
|
if self.verbosity > 1:
|
||||||
print("Exporting to", path)
|
print("Exporting to", path)
|
||||||
f.write(content)
|
f.write(content)
|
||||||
|
|
|
@ -59,7 +59,7 @@ class Command(BaseCommand):
|
||||||
else:
|
else:
|
||||||
picto = Pictogram()
|
picto = Pictogram()
|
||||||
picto.name = name
|
picto.name = name
|
||||||
if (path.name != self.path.name): # Subfolders only
|
if path.name != self.path.name: # Subfolders only
|
||||||
picto.category = path.name
|
picto.category = path.name
|
||||||
picto.attribution = self.attribution
|
picto.attribution = self.attribution
|
||||||
with (path / filename).open("rb") as f:
|
with (path / filename).open("rb") as f:
|
||||||
|
|
|
@ -2,7 +2,9 @@ from django.db.models import Manager
|
||||||
|
|
||||||
|
|
||||||
class PublicManager(Manager):
|
class PublicManager(Manager):
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
return super(PublicManager, self).get_queryset().filter(
|
return (
|
||||||
share_status=self.model.PUBLIC)
|
super(PublicManager, self)
|
||||||
|
.get_queryset()
|
||||||
|
.filter(share_status=self.model.PUBLIC)
|
||||||
|
)
|
||||||
|
|
|
@ -5,13 +5,12 @@ from django.utils.translation import gettext as _
|
||||||
|
|
||||||
|
|
||||||
def readonly_middleware(get_response):
|
def readonly_middleware(get_response):
|
||||||
|
|
||||||
if not settings.UMAP_READONLY:
|
if not settings.UMAP_READONLY:
|
||||||
raise MiddlewareNotUsed
|
raise MiddlewareNotUsed
|
||||||
|
|
||||||
def middleware(request):
|
def middleware(request):
|
||||||
if request.method not in ['GET', 'OPTIONS']:
|
if request.method not in ["GET", "OPTIONS"]:
|
||||||
return HttpResponseForbidden(_('Site is readonly for maintenance'))
|
return HttpResponseForbidden(_("Site is readonly for maintenance"))
|
||||||
|
|
||||||
return get_response(request)
|
return get_response(request)
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
# Generated by Django 2.0.5 on 2018-05-19 09:27
|
# Generated by Django 2.0.5 on 2018-05-19 09:27
|
||||||
|
|
||||||
from django.conf import settings
|
|
||||||
import django.contrib.gis.db.models.fields
|
import django.contrib.gis.db.models.fields
|
||||||
from django.db import migrations, models
|
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
import umap.fields
|
import umap.fields
|
||||||
import umap.models
|
import umap.models
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
initial = True
|
initial = True
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
@ -18,90 +18,238 @@ class Migration(migrations.Migration):
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='DataLayer',
|
name="DataLayer",
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
(
|
||||||
('name', models.CharField(max_length=200, verbose_name='name')),
|
"id",
|
||||||
('description', models.TextField(blank=True, null=True, verbose_name='description')),
|
models.AutoField(
|
||||||
('geojson', models.FileField(blank=True, null=True, upload_to=umap.models.upload_to)),
|
auto_created=True,
|
||||||
('display_on_load', models.BooleanField(default=False, help_text='Display this layer on load.', verbose_name='display on load')),
|
primary_key=True,
|
||||||
('rank', models.SmallIntegerField(default=0)),
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("name", models.CharField(max_length=200, verbose_name="name")),
|
||||||
|
(
|
||||||
|
"description",
|
||||||
|
models.TextField(blank=True, null=True, verbose_name="description"),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"geojson",
|
||||||
|
models.FileField(
|
||||||
|
blank=True, null=True, upload_to=umap.models.upload_to
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"display_on_load",
|
||||||
|
models.BooleanField(
|
||||||
|
default=False,
|
||||||
|
help_text="Display this layer on load.",
|
||||||
|
verbose_name="display on load",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("rank", models.SmallIntegerField(default=0)),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'ordering': ('rank',),
|
"ordering": ("rank",),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='Licence',
|
name="Licence",
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
(
|
||||||
('name', models.CharField(max_length=200, verbose_name='name')),
|
"id",
|
||||||
('details', models.URLField(help_text='Link to a page where the licence is detailed.', verbose_name='details')),
|
models.AutoField(
|
||||||
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("name", models.CharField(max_length=200, verbose_name="name")),
|
||||||
|
(
|
||||||
|
"details",
|
||||||
|
models.URLField(
|
||||||
|
help_text="Link to a page where the licence is detailed.",
|
||||||
|
verbose_name="details",
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'ordering': ('name',),
|
"ordering": ("name",),
|
||||||
'abstract': False,
|
"abstract": False,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='Map',
|
name="Map",
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
(
|
||||||
('name', models.CharField(max_length=200, verbose_name='name')),
|
"id",
|
||||||
('slug', models.SlugField()),
|
models.AutoField(
|
||||||
('description', models.TextField(blank=True, null=True, verbose_name='description')),
|
auto_created=True,
|
||||||
('center', django.contrib.gis.db.models.fields.PointField(geography=True, srid=4326, verbose_name='center')),
|
primary_key=True,
|
||||||
('zoom', models.IntegerField(default=7, verbose_name='zoom')),
|
serialize=False,
|
||||||
('locate', models.BooleanField(default=False, help_text='Locate user on load?', verbose_name='locate')),
|
verbose_name="ID",
|
||||||
('modified_at', models.DateTimeField(auto_now=True)),
|
),
|
||||||
('edit_status', models.SmallIntegerField(choices=[(1, 'Everyone can edit'), (2, 'Only editors can edit'), (3, 'Only owner can edit')], default=3, verbose_name='edit status')),
|
),
|
||||||
('share_status', models.SmallIntegerField(choices=[(1, 'everyone (public)'), (2, 'anyone with link'), (3, 'editors only')], default=1, verbose_name='share status')),
|
("name", models.CharField(max_length=200, verbose_name="name")),
|
||||||
('settings', umap.fields.DictField(blank=True, null=True, verbose_name='settings')),
|
("slug", models.SlugField()),
|
||||||
('editors', models.ManyToManyField(blank=True, to=settings.AUTH_USER_MODEL, verbose_name='editors')),
|
(
|
||||||
('licence', models.ForeignKey(default=umap.models.get_default_licence, help_text='Choose the map licence.', on_delete=django.db.models.deletion.SET_DEFAULT, to='umap.Licence', verbose_name='licence')),
|
"description",
|
||||||
('owner', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='owned_maps', to=settings.AUTH_USER_MODEL, verbose_name='owner')),
|
models.TextField(blank=True, null=True, verbose_name="description"),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"center",
|
||||||
|
django.contrib.gis.db.models.fields.PointField(
|
||||||
|
geography=True, srid=4326, verbose_name="center"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("zoom", models.IntegerField(default=7, verbose_name="zoom")),
|
||||||
|
(
|
||||||
|
"locate",
|
||||||
|
models.BooleanField(
|
||||||
|
default=False,
|
||||||
|
help_text="Locate user on load?",
|
||||||
|
verbose_name="locate",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("modified_at", models.DateTimeField(auto_now=True)),
|
||||||
|
(
|
||||||
|
"edit_status",
|
||||||
|
models.SmallIntegerField(
|
||||||
|
choices=[
|
||||||
|
(1, "Everyone can edit"),
|
||||||
|
(2, "Only editors can edit"),
|
||||||
|
(3, "Only owner can edit"),
|
||||||
|
],
|
||||||
|
default=3,
|
||||||
|
verbose_name="edit status",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"share_status",
|
||||||
|
models.SmallIntegerField(
|
||||||
|
choices=[
|
||||||
|
(1, "everyone (public)"),
|
||||||
|
(2, "anyone with link"),
|
||||||
|
(3, "editors only"),
|
||||||
|
],
|
||||||
|
default=1,
|
||||||
|
verbose_name="share status",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"settings",
|
||||||
|
umap.fields.DictField(
|
||||||
|
blank=True, null=True, verbose_name="settings"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"editors",
|
||||||
|
models.ManyToManyField(
|
||||||
|
blank=True, to=settings.AUTH_USER_MODEL, verbose_name="editors"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"licence",
|
||||||
|
models.ForeignKey(
|
||||||
|
default=umap.models.get_default_licence,
|
||||||
|
help_text="Choose the map licence.",
|
||||||
|
on_delete=django.db.models.deletion.SET_DEFAULT,
|
||||||
|
to="umap.Licence",
|
||||||
|
verbose_name="licence",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"owner",
|
||||||
|
models.ForeignKey(
|
||||||
|
blank=True,
|
||||||
|
null=True,
|
||||||
|
on_delete=django.db.models.deletion.PROTECT,
|
||||||
|
related_name="owned_maps",
|
||||||
|
to=settings.AUTH_USER_MODEL,
|
||||||
|
verbose_name="owner",
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'ordering': ('name',),
|
"ordering": ("name",),
|
||||||
'abstract': False,
|
"abstract": False,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='Pictogram',
|
name="Pictogram",
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
(
|
||||||
('name', models.CharField(max_length=200, verbose_name='name')),
|
"id",
|
||||||
('attribution', models.CharField(max_length=300)),
|
models.AutoField(
|
||||||
('pictogram', models.ImageField(upload_to='pictogram')),
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("name", models.CharField(max_length=200, verbose_name="name")),
|
||||||
|
("attribution", models.CharField(max_length=300)),
|
||||||
|
("pictogram", models.ImageField(upload_to="pictogram")),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'ordering': ('name',),
|
"ordering": ("name",),
|
||||||
'abstract': False,
|
"abstract": False,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='TileLayer',
|
name="TileLayer",
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
(
|
||||||
('name', models.CharField(max_length=200, verbose_name='name')),
|
"id",
|
||||||
('url_template', models.CharField(help_text='URL template using OSM tile format', max_length=200)),
|
models.AutoField(
|
||||||
('minZoom', models.IntegerField(default=0)),
|
auto_created=True,
|
||||||
('maxZoom', models.IntegerField(default=18)),
|
primary_key=True,
|
||||||
('attribution', models.CharField(max_length=300)),
|
serialize=False,
|
||||||
('rank', models.SmallIntegerField(blank=True, help_text='Order of the tilelayers in the edit box', null=True)),
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("name", models.CharField(max_length=200, verbose_name="name")),
|
||||||
|
(
|
||||||
|
"url_template",
|
||||||
|
models.CharField(
|
||||||
|
help_text="URL template using OSM tile format", max_length=200
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("minZoom", models.IntegerField(default=0)),
|
||||||
|
("maxZoom", models.IntegerField(default=18)),
|
||||||
|
("attribution", models.CharField(max_length=300)),
|
||||||
|
(
|
||||||
|
"rank",
|
||||||
|
models.SmallIntegerField(
|
||||||
|
blank=True,
|
||||||
|
help_text="Order of the tilelayers in the edit box",
|
||||||
|
null=True,
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'ordering': ('rank', 'name'),
|
"ordering": ("rank", "name"),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='map',
|
model_name="map",
|
||||||
name='tilelayer',
|
name="tilelayer",
|
||||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='maps', to='umap.TileLayer', verbose_name='background'),
|
field=models.ForeignKey(
|
||||||
|
blank=True,
|
||||||
|
null=True,
|
||||||
|
on_delete=django.db.models.deletion.PROTECT,
|
||||||
|
related_name="maps",
|
||||||
|
to="umap.TileLayer",
|
||||||
|
verbose_name="background",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='datalayer',
|
model_name="datalayer",
|
||||||
name='map',
|
name="map",
|
||||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='umap.Map'),
|
field=models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.CASCADE, to="umap.Map"
|
||||||
|
),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -4,15 +4,14 @@ from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('umap', '0001_initial'),
|
("umap", "0001_initial"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='tilelayer',
|
model_name="tilelayer",
|
||||||
name='tms',
|
name="tms",
|
||||||
field=models.BooleanField(default=False),
|
field=models.BooleanField(default=False),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -5,22 +5,26 @@ from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
def add_tilelayer(apps, *args):
|
def add_tilelayer(apps, *args):
|
||||||
TileLayer = apps.get_model('umap', 'TileLayer')
|
TileLayer = apps.get_model("umap", "TileLayer")
|
||||||
if TileLayer.objects.count():
|
if TileLayer.objects.count():
|
||||||
return
|
return
|
||||||
TileLayer(
|
TileLayer(
|
||||||
name='Positron',
|
name="Positron",
|
||||||
url_template=('https://cartodb-basemaps-{s}.global.ssl.fastly.net/'
|
url_template=(
|
||||||
'light_all/{z}/{x}/{y}.png'),
|
"https://cartodb-basemaps-{s}.global.ssl.fastly.net/"
|
||||||
attribution=('© [[http://www.openstreetmap.org/copyright|'
|
"light_all/{z}/{x}/{y}.png"
|
||||||
'OpenStreetMap]] contributors, © '
|
),
|
||||||
'[[https://carto.com/attributions|CARTO]]')).save()
|
attribution=(
|
||||||
|
"© [[http://www.openstreetmap.org/copyright|"
|
||||||
|
"OpenStreetMap]] contributors, © "
|
||||||
|
"[[https://carto.com/attributions|CARTO]]"
|
||||||
|
),
|
||||||
|
).save()
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('umap', '0002_tilelayer_tms'),
|
("umap", "0002_tilelayer_tms"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
|
|
|
@ -5,18 +5,15 @@ from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
def add_licence(apps, *args):
|
def add_licence(apps, *args):
|
||||||
Licence = apps.get_model('umap', 'Licence')
|
Licence = apps.get_model("umap", "Licence")
|
||||||
if Licence.objects.count():
|
if Licence.objects.count():
|
||||||
return
|
return
|
||||||
Licence(
|
Licence(name="ODbL", details="http://opendatacommons.org/licenses/odbl/").save()
|
||||||
name='ODbL',
|
|
||||||
details='http://opendatacommons.org/licenses/odbl/').save()
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('umap', '0003_add_tilelayer'),
|
("umap", "0003_add_tilelayer"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
|
|
|
@ -4,14 +4,13 @@ from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('umap', '0004_add_licence'),
|
("umap", "0004_add_licence"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.RemoveField(
|
migrations.RemoveField(
|
||||||
model_name='map',
|
model_name="map",
|
||||||
name='tilelayer',
|
name="tilelayer",
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -5,15 +5,16 @@ from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('umap', '0005_remove_map_tilelayer'),
|
("umap", "0005_remove_map_tilelayer"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='map',
|
model_name="map",
|
||||||
name='settings',
|
name="settings",
|
||||||
field=django.contrib.postgres.fields.jsonb.JSONField(blank=True, default=dict, null=True, verbose_name='settings'),
|
field=django.contrib.postgres.fields.jsonb.JSONField(
|
||||||
|
blank=True, default=dict, null=True, verbose_name="settings"
|
||||||
|
),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -4,15 +4,23 @@ from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('umap', '0006_auto_20190407_0719'),
|
("umap", "0006_auto_20190407_0719"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='map',
|
model_name="map",
|
||||||
name='share_status',
|
name="share_status",
|
||||||
field=models.SmallIntegerField(choices=[(1, 'everyone (public)'), (2, 'anyone with link'), (3, 'editors only'), (9, 'blocked')], default=1, verbose_name='share status'),
|
field=models.SmallIntegerField(
|
||||||
|
choices=[
|
||||||
|
(1, "everyone (public)"),
|
||||||
|
(2, "anyone with link"),
|
||||||
|
(3, "editors only"),
|
||||||
|
(9, "blocked"),
|
||||||
|
],
|
||||||
|
default=1,
|
||||||
|
verbose_name="share status",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -4,7 +4,6 @@ from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
("umap", "0007_auto_20190416_1757"),
|
("umap", "0007_auto_20190416_1757"),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,25 +1,44 @@
|
||||||
# Generated by Django 4.1.7 on 2023-05-05 18:02
|
# Generated by Django 4.1.7 on 2023-05-05 18:02
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
import django.db.models.deletion
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
('umap', '0008_alter_map_settings'),
|
("umap", "0008_alter_map_settings"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='Star',
|
name="Star",
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
(
|
||||||
('at', models.DateTimeField(auto_now=True)),
|
"id",
|
||||||
('by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='stars', to=settings.AUTH_USER_MODEL)),
|
models.AutoField(
|
||||||
('map', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='umap.map')),
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("at", models.DateTimeField(auto_now=True)),
|
||||||
|
(
|
||||||
|
"by",
|
||||||
|
models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
related_name="stars",
|
||||||
|
to=settings.AUTH_USER_MODEL,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"map",
|
||||||
|
models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.CASCADE, to="umap.map"
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -4,20 +4,32 @@ from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('umap', '0009_star'),
|
("umap", "0009_star"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='map',
|
model_name="map",
|
||||||
name='edit_status',
|
name="edit_status",
|
||||||
field=models.SmallIntegerField(choices=[(1, 'Everyone'), (2, 'Editors only'), (3, 'Owner only')], default=3, verbose_name='edit status'),
|
field=models.SmallIntegerField(
|
||||||
|
choices=[(1, "Everyone"), (2, "Editors only"), (3, "Owner only")],
|
||||||
|
default=3,
|
||||||
|
verbose_name="edit status",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='map',
|
model_name="map",
|
||||||
name='share_status',
|
name="share_status",
|
||||||
field=models.SmallIntegerField(choices=[(1, 'Everyone (public)'), (2, 'Anyone with link'), (3, 'Editors only'), (9, 'Blocked')], default=1, verbose_name='share status'),
|
field=models.SmallIntegerField(
|
||||||
|
choices=[
|
||||||
|
(1, "Everyone (public)"),
|
||||||
|
(2, "Anyone with link"),
|
||||||
|
(3, "Editors only"),
|
||||||
|
(9, "Blocked"),
|
||||||
|
],
|
||||||
|
default=1,
|
||||||
|
verbose_name="share status",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,24 +1,37 @@
|
||||||
# Generated by Django 4.2.2 on 2023-08-07 06:07
|
# Generated by Django 4.2.2 on 2023-08-07 06:07
|
||||||
|
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
|
|
||||||
import umap.models
|
import umap.models
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('umap', '0010_alter_map_edit_status_alter_map_share_status'),
|
("umap", "0010_alter_map_edit_status_alter_map_share_status"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='map',
|
model_name="map",
|
||||||
name='edit_status',
|
name="edit_status",
|
||||||
field=models.SmallIntegerField(choices=[(1, 'Everyone'), (2, 'Editors only'), (3, 'Owner only')], default=umap.models.get_default_edit_status, verbose_name='edit status'),
|
field=models.SmallIntegerField(
|
||||||
|
choices=[(1, "Everyone"), (2, "Editors only"), (3, "Owner only")],
|
||||||
|
default=umap.models.get_default_edit_status,
|
||||||
|
verbose_name="edit status",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='map',
|
model_name="map",
|
||||||
name='share_status',
|
name="share_status",
|
||||||
field=models.SmallIntegerField(choices=[(1, 'Everyone (public)'), (2, 'Anyone with link'), (3, 'Editors only'), (9, 'Blocked')], default=umap.models.get_default_share_status, verbose_name='share status'),
|
field=models.SmallIntegerField(
|
||||||
|
choices=[
|
||||||
|
(1, "Everyone (public)"),
|
||||||
|
(2, "Anyone with link"),
|
||||||
|
(3, "Editors only"),
|
||||||
|
(9, "Blocked"),
|
||||||
|
],
|
||||||
|
default=umap.models.get_default_share_status,
|
||||||
|
verbose_name="share status",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# Generated by Django 4.2.2 on 2023-09-27 08:50
|
# Generated by Django 4.2.2 on 2023-09-27 08:50
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
import django.utils.timezone
|
import django.utils.timezone
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.contrib.gis.db import models
|
from django.contrib.gis.db import models
|
||||||
from django.conf import settings
|
from django.core.files.base import File
|
||||||
from django.urls import reverse
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
|
||||||
from django.core.signing import Signer
|
from django.core.signing import Signer
|
||||||
from django.template.defaultfilters import slugify
|
from django.template.defaultfilters import slugify
|
||||||
from django.core.files.base import File
|
from django.urls import reverse
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from .managers import PublicManager
|
from .managers import PublicManager
|
||||||
|
|
||||||
|
@ -380,7 +380,7 @@ class DataLayer(NamedModel):
|
||||||
}
|
}
|
||||||
obj["id"] = self.pk
|
obj["id"] = self.pk
|
||||||
obj["permissions"] = {"edit_status": self.edit_status}
|
obj["permissions"] = {"edit_status": self.edit_status}
|
||||||
obj["editMode"] = "advanced" if self.can_edit(user, request) else 'disabled'
|
obj["editMode"] = "advanced" if self.can_edit(user, request) else "disabled"
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
def clone(self, map_inst=None):
|
def clone(self, map_inst=None):
|
||||||
|
@ -436,7 +436,7 @@ class DataLayer(NamedModel):
|
||||||
root = self.storage_root()
|
root = self.storage_root()
|
||||||
names = self.geojson.storage.listdir(root)[1]
|
names = self.geojson.storage.listdir(root)[1]
|
||||||
for name in names:
|
for name in names:
|
||||||
if name.startswith(f'{self.pk}_') and name.endswith(".gz"):
|
if name.startswith(f"{self.pk}_") and name.endswith(".gz"):
|
||||||
self.geojson.storage.delete(os.path.join(root, name))
|
self.geojson.storage.delete(os.path.join(root, name))
|
||||||
|
|
||||||
def can_edit(self, user=None, request=None):
|
def can_edit(self, user=None, request=None):
|
||||||
|
|
|
@ -4,7 +4,6 @@ from email.utils import parseaddr
|
||||||
|
|
||||||
import environ
|
import environ
|
||||||
from django.conf.locale import LANG_INFO
|
from django.conf.locale import LANG_INFO
|
||||||
from django.template.defaultfilters import slugify
|
|
||||||
|
|
||||||
env = environ.Env()
|
env = environ.Env()
|
||||||
|
|
||||||
|
@ -244,9 +243,7 @@ UMAP_MAPS_PER_PAGE = 5
|
||||||
UMAP_MAPS_PER_SEARCH = 25
|
UMAP_MAPS_PER_SEARCH = 25
|
||||||
UMAP_MAPS_PER_PAGE_OWNER = 10
|
UMAP_MAPS_PER_PAGE_OWNER = 10
|
||||||
UMAP_SEARCH_CONFIGURATION = "simple"
|
UMAP_SEARCH_CONFIGURATION = "simple"
|
||||||
UMAP_FEEDBACK_LINK = (
|
UMAP_FEEDBACK_LINK = "https://wiki.openstreetmap.org/wiki/UMap#Feedback_and_help"
|
||||||
"https://wiki.openstreetmap.org/wiki/UMap#Feedback_and_help" # noqa
|
|
||||||
)
|
|
||||||
USER_MAPS_URL = "user_maps"
|
USER_MAPS_URL = "user_maps"
|
||||||
DATABASES = {"default": env.db(default="postgis://localhost:5432/umap")}
|
DATABASES = {"default": env.db(default="postgis://localhost:5432/umap")}
|
||||||
UMAP_DEFAULT_SHARE_STATUS = None
|
UMAP_DEFAULT_SHARE_STATUS = None
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
{% block maincontent %}
|
{% block maincontent %}
|
||||||
<div class="col wide">
|
<div class="col wide">
|
||||||
<h2 class="section tabs">
|
<h2 class="section tabs">
|
||||||
<a href="{% url 'user_dashboard' %}">{% trans "My Dashboard" %}</a> | {% trans "My Profile" %}
|
<a href="{% url "user_dashboard" %}">{% trans "My Dashboard" %}</a> | {% trans "My Profile" %}
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
|
@ -16,7 +16,7 @@
|
||||||
<form id="user_form" method="post">
|
<form id="user_form" method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{{ form }}
|
{{ form }}
|
||||||
<input type="submit" value="{% trans 'Save' %}" />
|
<input type="submit" value="{% trans "Save" %}" />
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
{% if backends.backends|length %}
|
{% if backends.backends|length %}
|
||||||
|
|
|
@ -16,9 +16,15 @@
|
||||||
<meta name="viewport"
|
<meta name="viewport"
|
||||||
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||||
{# See https://evilmartians.com/chronicles/how-to-favicon-in-2021-six-files-that-fit-most-needs #}
|
{# See https://evilmartians.com/chronicles/how-to-favicon-in-2021-six-files-that-fit-most-needs #}
|
||||||
<link rel="icon" href="{{ STATIC_URL }}umap/favicons/favicon.ico" sizes="32x32">
|
<link rel="icon"
|
||||||
<link rel="icon" href="{{ STATIC_URL }}umap/favicons/icon.svg" type="image/svg+xml">
|
href="{{ STATIC_URL }}umap/favicons/favicon.ico"
|
||||||
<link rel="apple-touch-icon" href="{{ STATIC_URL }}umap/favicons/apple-touch-icon.png"><!-- 180×180 -->
|
sizes="32x32">
|
||||||
|
<link rel="icon"
|
||||||
|
href="{{ STATIC_URL }}umap/favicons/icon.svg"
|
||||||
|
type="image/svg+xml">
|
||||||
|
<link rel="apple-touch-icon"
|
||||||
|
href="{{ STATIC_URL }}umap/favicons/apple-touch-icon.png">
|
||||||
|
<!-- 180×180 -->
|
||||||
<link rel="manifest" href="/manifest.webmanifest">
|
<link rel="manifest" href="/manifest.webmanifest">
|
||||||
</head>
|
</head>
|
||||||
<body class="{% block body_class %}{% endblock body_class %}">
|
<body class="{% block body_class %}{% endblock body_class %}">
|
||||||
|
|
|
@ -10,53 +10,50 @@ from ..views import _urls_for_js
|
||||||
register = template.Library()
|
register = template.Library()
|
||||||
|
|
||||||
|
|
||||||
@register.inclusion_tag('umap/css.html')
|
@register.inclusion_tag("umap/css.html")
|
||||||
def umap_css():
|
def umap_css():
|
||||||
return {
|
return {"STATIC_URL": settings.STATIC_URL}
|
||||||
"STATIC_URL": settings.STATIC_URL
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@register.inclusion_tag('umap/js.html')
|
@register.inclusion_tag("umap/js.html")
|
||||||
def umap_js(locale=None):
|
def umap_js(locale=None):
|
||||||
return {
|
return {"STATIC_URL": settings.STATIC_URL, "locale": locale}
|
||||||
"STATIC_URL": settings.STATIC_URL,
|
|
||||||
"locale": locale
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@register.inclusion_tag('umap/map_fragment.html')
|
@register.inclusion_tag("umap/map_fragment.html")
|
||||||
def map_fragment(map_instance, **kwargs):
|
def map_fragment(map_instance, **kwargs):
|
||||||
layers = DataLayer.objects.filter(map=map_instance)
|
layers = DataLayer.objects.filter(map=map_instance)
|
||||||
datalayer_data = [c.metadata() for c in layers]
|
datalayer_data = [c.metadata() for c in layers]
|
||||||
map_settings = map_instance.settings
|
map_settings = map_instance.settings
|
||||||
if "properties" not in map_settings:
|
if "properties" not in map_settings:
|
||||||
map_settings['properties'] = {}
|
map_settings["properties"] = {}
|
||||||
map_settings['properties'].update({
|
map_settings["properties"].update(
|
||||||
'tilelayers': [TileLayer.get_default().json],
|
{
|
||||||
'datalayers': datalayer_data,
|
"tilelayers": [TileLayer.get_default().json],
|
||||||
'urls': _urls_for_js(),
|
"datalayers": datalayer_data,
|
||||||
'STATIC_URL': settings.STATIC_URL,
|
"urls": _urls_for_js(),
|
||||||
"editMode": 'disabled',
|
"STATIC_URL": settings.STATIC_URL,
|
||||||
'hash': False,
|
"editMode": "disabled",
|
||||||
'attributionControl': False,
|
"hash": False,
|
||||||
'scrollWheelZoom': False,
|
"attributionControl": False,
|
||||||
'umapAttributionControl': False,
|
"scrollWheelZoom": False,
|
||||||
'noControl': True,
|
"umapAttributionControl": False,
|
||||||
'umap_id': map_instance.pk,
|
"noControl": True,
|
||||||
'onLoadPanel': "none",
|
"umap_id": map_instance.pk,
|
||||||
'captionBar': False,
|
"onLoadPanel": "none",
|
||||||
'default_iconUrl': "%sumap/img/marker.png" % settings.STATIC_URL,
|
"captionBar": False,
|
||||||
'slideshow': {}
|
"default_iconUrl": "%sumap/img/marker.png" % settings.STATIC_URL,
|
||||||
})
|
"slideshow": {},
|
||||||
map_settings['properties'].update(kwargs)
|
}
|
||||||
prefix = kwargs.pop('prefix', None) or 'map'
|
)
|
||||||
page = kwargs.pop('page', None) or ''
|
map_settings["properties"].update(kwargs)
|
||||||
|
prefix = kwargs.pop("prefix", None) or "map"
|
||||||
|
page = kwargs.pop("page", None) or ""
|
||||||
unique_id = prefix + str(page) + "_" + str(map_instance.pk)
|
unique_id = prefix + str(page) + "_" + str(map_instance.pk)
|
||||||
return {
|
return {
|
||||||
"map_settings": json.dumps(map_settings),
|
"map_settings": json.dumps(map_settings),
|
||||||
"map": map_instance,
|
"map": map_instance,
|
||||||
"unique_id": unique_id
|
"unique_id": unique_id,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -73,7 +70,7 @@ def tilelayer_preview(tilelayer):
|
||||||
|
|
||||||
@register.filter
|
@register.filter
|
||||||
def notag(s):
|
def notag(s):
|
||||||
return s.replace('<', '<')
|
return s.replace("<", "<")
|
||||||
|
|
||||||
|
|
||||||
@register.simple_tag(takes_context=True)
|
@register.simple_tag(takes_context=True)
|
||||||
|
@ -86,5 +83,6 @@ def paginate_querystring(context, page):
|
||||||
@register.filter
|
@register.filter
|
||||||
def ipdb(what):
|
def ipdb(what):
|
||||||
import ipdb
|
import ipdb
|
||||||
|
|
||||||
ipdb.set_trace()
|
ipdb.set_trace()
|
||||||
return ''
|
return ""
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import json
|
|
||||||
import copy
|
import copy
|
||||||
|
import json
|
||||||
|
|
||||||
import factory
|
import factory
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
|
|
|
@ -77,4 +77,3 @@ def datalayer(map):
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def tilelayer():
|
def tilelayer():
|
||||||
return TileLayerFactory()
|
return TileLayerFactory()
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from playwright.sync_api import expect
|
|
||||||
from django.core.files.base import ContentFile
|
from django.core.files.base import ContentFile
|
||||||
|
from playwright.sync_api import expect
|
||||||
|
|
||||||
from umap.models import Map, Pictogram
|
from umap.models import Map, Pictogram
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from playwright.sync_api import expect
|
|
||||||
from django.core.files.base import ContentFile
|
from django.core.files.base import ContentFile
|
||||||
|
from playwright.sync_api import expect
|
||||||
|
|
||||||
from umap.models import Map, Pictogram
|
from umap.models import Map, Pictogram
|
||||||
|
|
||||||
|
|
|
@ -5,15 +5,17 @@ from umap.settings.base import * # pylint: disable=W0614,W0401
|
||||||
SECRET_KEY = "justfortests"
|
SECRET_KEY = "justfortests"
|
||||||
COMPRESS_ENABLED = False
|
COMPRESS_ENABLED = False
|
||||||
FROM_EMAIL = "test@test.org"
|
FROM_EMAIL = "test@test.org"
|
||||||
EMAIL_BACKEND = 'django.core.mail.backends.locmem.EmailBackend'
|
EMAIL_BACKEND = "django.core.mail.backends.locmem.EmailBackend"
|
||||||
|
|
||||||
if "TRAVIS" in os.environ:
|
if os.environ.get("GITHUB_ACTIONS", False) == "true":
|
||||||
DATABASES = {
|
DATABASES = {
|
||||||
"default": {
|
"default": {
|
||||||
"ENGINE": "django.contrib.gis.db.backends.postgis",
|
"ENGINE": "django.contrib.gis.db.backends.postgis",
|
||||||
"NAME": "umap",
|
"NAME": "postgres",
|
||||||
"PORT": 5433,
|
"USER": "postgres",
|
||||||
"USER": "travis",
|
"HOST": "localhost",
|
||||||
|
"PORT": 5432,
|
||||||
|
"PASSWORD": "postgres",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,9 +3,10 @@ import os
|
||||||
import pytest
|
import pytest
|
||||||
from django.core.files.base import ContentFile
|
from django.core.files.base import ContentFile
|
||||||
|
|
||||||
from .base import DataLayerFactory, MapFactory
|
|
||||||
from umap.models import DataLayer, Map
|
from umap.models import DataLayer, Map
|
||||||
|
|
||||||
|
from .base import DataLayerFactory, MapFactory
|
||||||
|
|
||||||
pytestmark = pytest.mark.django_db
|
pytestmark = pytest.mark.django_db
|
||||||
|
|
||||||
|
|
||||||
|
@ -63,13 +64,13 @@ def test_should_remove_old_versions_on_save(datalayer, map, settings):
|
||||||
settings.UMAP_KEEP_VERSIONS = 3
|
settings.UMAP_KEEP_VERSIONS = 3
|
||||||
root = datalayer.storage_root()
|
root = datalayer.storage_root()
|
||||||
before = len(datalayer.geojson.storage.listdir(root)[1])
|
before = len(datalayer.geojson.storage.listdir(root)[1])
|
||||||
newer = f'{root}/{datalayer.pk}_1440924889.geojson'
|
newer = f"{root}/{datalayer.pk}_1440924889.geojson"
|
||||||
medium = f'{root}/{datalayer.pk}_1440923687.geojson'
|
medium = f"{root}/{datalayer.pk}_1440923687.geojson"
|
||||||
older = f'{root}/{datalayer.pk}_1440918637.geojson'
|
older = f"{root}/{datalayer.pk}_1440918637.geojson"
|
||||||
other = f'{root}/123456_1440918637.geojson'
|
other = f"{root}/123456_1440918637.geojson"
|
||||||
for path in [medium, newer, older, other]:
|
for path in [medium, newer, older, other]:
|
||||||
datalayer.geojson.storage.save(path, ContentFile("{}"))
|
datalayer.geojson.storage.save(path, ContentFile("{}"))
|
||||||
datalayer.geojson.storage.save(path + '.gz', ContentFile("{}"))
|
datalayer.geojson.storage.save(path + ".gz", ContentFile("{}"))
|
||||||
assert len(datalayer.geojson.storage.listdir(root)[1]) == 8 + before
|
assert len(datalayer.geojson.storage.listdir(root)[1]) == 8 + before
|
||||||
datalayer.save()
|
datalayer.save()
|
||||||
files = datalayer.geojson.storage.listdir(root)[1]
|
files = datalayer.geojson.storage.listdir(root)[1]
|
||||||
|
|
|
@ -50,11 +50,13 @@ def test_editors_can_edit_if_status_editors(map, user):
|
||||||
assert map.can_edit(user)
|
assert map.can_edit(user)
|
||||||
|
|
||||||
|
|
||||||
def test_logged_in_user_should_be_allowed_for_anonymous_map_with_anonymous_edit_status(map, user, rf): # noqa
|
def test_logged_in_user_should_be_allowed_for_anonymous_map_with_anonymous_edit_status(
|
||||||
|
map, user, rf
|
||||||
|
): # noqa
|
||||||
map.owner = None
|
map.owner = None
|
||||||
map.edit_status = map.ANONYMOUS
|
map.edit_status = map.ANONYMOUS
|
||||||
map.save()
|
map.save()
|
||||||
url = reverse('map_update', kwargs={'map_id': map.pk})
|
url = reverse("map_update", kwargs={"map_id": map.pk})
|
||||||
request = rf.get(url)
|
request = rf.get(url)
|
||||||
request.user = user
|
request.user = user
|
||||||
assert map.can_edit(user, request)
|
assert map.can_edit(user, request)
|
||||||
|
@ -70,7 +72,7 @@ def test_anonymous_user_should_not_be_allowed_for_anonymous_map(map, user, rf):
|
||||||
def test_clone_should_return_new_instance(map, user):
|
def test_clone_should_return_new_instance(map, user):
|
||||||
clone = map.clone()
|
clone = map.clone()
|
||||||
assert map.pk != clone.pk
|
assert map.pk != clone.pk
|
||||||
assert u"Clone of " + map.name == clone.name
|
assert "Clone of " + map.name == clone.name
|
||||||
assert map.settings == clone.settings
|
assert map.settings == clone.settings
|
||||||
assert map.center == clone.center
|
assert map.center == clone.center
|
||||||
assert map.zoom == clone.zoom
|
assert map.zoom == clone.zoom
|
||||||
|
@ -108,8 +110,7 @@ def test_clone_should_clone_datalayers_and_features_too(map, user, datalayer):
|
||||||
def test_publicmanager_should_get_only_public_maps(map, user, licence):
|
def test_publicmanager_should_get_only_public_maps(map, user, licence):
|
||||||
map.share_status = map.PUBLIC
|
map.share_status = map.PUBLIC
|
||||||
open_map = MapFactory(owner=user, licence=licence, share_status=Map.OPEN)
|
open_map = MapFactory(owner=user, licence=licence, share_status=Map.OPEN)
|
||||||
private_map = MapFactory(owner=user, licence=licence,
|
private_map = MapFactory(owner=user, licence=licence, share_status=Map.PRIVATE)
|
||||||
share_status=Map.PRIVATE)
|
|
||||||
assert map in Map.public.all()
|
assert map in Map.public.all()
|
||||||
assert open_map not in Map.public.all()
|
assert open_map not in Map.public.all()
|
||||||
assert private_map not in Map.public.all()
|
assert private_map not in Map.public.all()
|
||||||
|
|
|
@ -3,8 +3,8 @@ import json
|
||||||
import pytest
|
import pytest
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
from django.core import mail
|
from django.core import mail
|
||||||
from django.urls import reverse
|
|
||||||
from django.core.signing import Signer
|
from django.core.signing import Signer
|
||||||
|
from django.urls import reverse
|
||||||
|
|
||||||
from umap.models import DataLayer, Map, Star
|
from umap.models import DataLayer, Map, Star
|
||||||
|
|
||||||
|
@ -275,9 +275,7 @@ def test_owner_cannot_access_map_with_share_status_blocked(client, map):
|
||||||
assert response.status_code == 403
|
assert response.status_code == 403
|
||||||
|
|
||||||
|
|
||||||
def test_non_editor_cannot_access_map_if_share_status_private(
|
def test_non_editor_cannot_access_map_if_share_status_private(client, map, user): # noqa
|
||||||
client, map, user
|
|
||||||
): # noqa
|
|
||||||
url = reverse("map", args=(map.slug, map.pk))
|
url = reverse("map", args=(map.slug, map.pk))
|
||||||
map.share_status = map.PRIVATE
|
map.share_status = map.PRIVATE
|
||||||
map.save()
|
map.save()
|
||||||
|
@ -355,9 +353,7 @@ def test_anonymous_update_without_cookie_fails(client, anonymap, post_data): #
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("allow_anonymous")
|
@pytest.mark.usefixtures("allow_anonymous")
|
||||||
def test_anonymous_update_with_cookie_should_work(
|
def test_anonymous_update_with_cookie_should_work(cookieclient, anonymap, post_data): # noqa
|
||||||
cookieclient, anonymap, post_data
|
|
||||||
): # noqa
|
|
||||||
url = reverse("map_update", kwargs={"map_id": anonymap.pk})
|
url = reverse("map_update", kwargs={"map_id": anonymap.pk})
|
||||||
# POST only mendatory fields
|
# POST only mendatory fields
|
||||||
name = "new map name"
|
name = "new map name"
|
||||||
|
@ -438,9 +434,7 @@ def test_clone_anonymous_map_should_not_be_possible_if_user_is_not_allowed(
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("allow_anonymous")
|
@pytest.mark.usefixtures("allow_anonymous")
|
||||||
def test_clone_map_should_be_possible_if_edit_status_is_anonymous(
|
def test_clone_map_should_be_possible_if_edit_status_is_anonymous(client, anonymap): # noqa
|
||||||
client, anonymap
|
|
||||||
): # noqa
|
|
||||||
assert Map.objects.count() == 1
|
assert Map.objects.count() == 1
|
||||||
url = reverse("map_clone", kwargs={"map_id": anonymap.pk})
|
url = reverse("map_clone", kwargs={"map_id": anonymap.pk})
|
||||||
anonymap.edit_status = anonymap.ANONYMOUS
|
anonymap.edit_status = anonymap.ANONYMOUS
|
||||||
|
|
|
@ -6,16 +6,22 @@ pytestmark = pytest.mark.django_db
|
||||||
|
|
||||||
|
|
||||||
def test_tilelayer_json():
|
def test_tilelayer_json():
|
||||||
tilelayer = TileLayerFactory(attribution='Attribution', maxZoom=19,
|
tilelayer = TileLayerFactory(
|
||||||
minZoom=0, name='Name', rank=1, tms=True,
|
attribution="Attribution",
|
||||||
url_template='http://{s}.x.fr/{z}/{x}/{y}')
|
maxZoom=19,
|
||||||
|
minZoom=0,
|
||||||
|
name="Name",
|
||||||
|
rank=1,
|
||||||
|
tms=True,
|
||||||
|
url_template="http://{s}.x.fr/{z}/{x}/{y}",
|
||||||
|
)
|
||||||
assert tilelayer.json == {
|
assert tilelayer.json == {
|
||||||
'attribution': 'Attribution',
|
"attribution": "Attribution",
|
||||||
'id': tilelayer.id,
|
"id": tilelayer.id,
|
||||||
'maxZoom': 19,
|
"maxZoom": 19,
|
||||||
'minZoom': 0,
|
"minZoom": 0,
|
||||||
'name': 'Name',
|
"name": "Name",
|
||||||
'rank': 1,
|
"rank": 1,
|
||||||
'tms': True,
|
"tms": True,
|
||||||
'url_template': 'http://{s}.x.fr/{z}/{x}/{y}'
|
"url_template": "http://{s}.x.fr/{z}/{x}/{y}",
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,18 @@
|
||||||
import json
|
import json
|
||||||
import socket
|
import socket
|
||||||
from datetime import date, timedelta
|
from datetime import date, datetime, timedelta
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth import get_user, get_user_model
|
from django.contrib.auth import get_user, get_user_model
|
||||||
from django.urls import reverse
|
|
||||||
from django.test import RequestFactory
|
from django.test import RequestFactory
|
||||||
|
from django.urls import reverse
|
||||||
|
from django.utils.timezone import make_aware
|
||||||
|
|
||||||
from umap import VERSION
|
from umap import VERSION
|
||||||
from umap.views import validate_url
|
from umap.views import validate_url
|
||||||
|
|
||||||
from .base import UserFactory, MapFactory
|
from .base import MapFactory, UserFactory
|
||||||
|
|
||||||
User = get_user_model()
|
User = get_user_model()
|
||||||
|
|
||||||
|
@ -186,9 +187,9 @@ def test_stats_empty(client):
|
||||||
|
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
def test_stats_basic(client, map, datalayer, user2):
|
def test_stats_basic(client, map, datalayer, user2):
|
||||||
map.owner.last_login = date.today()
|
map.owner.last_login = make_aware(datetime.now())
|
||||||
map.owner.save()
|
map.owner.save()
|
||||||
user2.last_login = date.today() - timedelta(days=8)
|
user2.last_login = make_aware(datetime.now()) - timedelta(days=8)
|
||||||
user2.save()
|
user2.save()
|
||||||
response = client.get(reverse("stats"))
|
response = client.get(reverse("stats"))
|
||||||
assert json.loads(response.content.decode()) == {
|
assert json.loads(response.content.decode()) == {
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.urls import include, path, re_path
|
|
||||||
from django.conf.urls.i18n import i18n_patterns
|
from django.conf.urls.i18n import i18n_patterns
|
||||||
from django.conf.urls.static import static
|
from django.conf.urls.static import static
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
@ -7,16 +6,17 @@ from django.contrib.auth import views as auth_views
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.contrib.staticfiles.storage import staticfiles_storage
|
from django.contrib.staticfiles.storage import staticfiles_storage
|
||||||
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
|
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
|
||||||
|
from django.urls import include, path, re_path
|
||||||
from django.views.decorators.cache import cache_control, cache_page, never_cache
|
from django.views.decorators.cache import cache_control, cache_page, never_cache
|
||||||
from django.views.decorators.csrf import ensure_csrf_cookie
|
from django.views.decorators.csrf import ensure_csrf_cookie
|
||||||
from django.views.generic.base import RedirectView
|
from django.views.generic.base import RedirectView
|
||||||
|
|
||||||
from . import views
|
from . import views
|
||||||
from .decorators import (
|
from .decorators import (
|
||||||
jsonize_view,
|
|
||||||
login_required_if_not_anonymous_allowed,
|
|
||||||
can_edit_map,
|
can_edit_map,
|
||||||
can_view_map,
|
can_view_map,
|
||||||
|
jsonize_view,
|
||||||
|
login_required_if_not_anonymous_allowed,
|
||||||
)
|
)
|
||||||
from .utils import decorated_patterns
|
from .utils import decorated_patterns
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
import gzip
|
import gzip
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from django.urls import get_resolver
|
from django.urls import URLPattern, URLResolver, get_resolver
|
||||||
from django.urls import URLPattern, URLResolver
|
|
||||||
|
|
||||||
|
|
||||||
def get_uri_template(urlname, args=None, prefix=""):
|
def get_uri_template(urlname, args=None, prefix=""):
|
||||||
|
|
|
@ -3,16 +3,17 @@ import mimetypes
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import socket
|
import socket
|
||||||
from datetime import date, timedelta
|
from datetime import datetime, timedelta
|
||||||
from http.client import InvalidURL
|
from http.client import InvalidURL
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from urllib.error import URLError
|
from urllib.error import HTTPError, URLError
|
||||||
from urllib.parse import quote
|
from urllib.parse import quote, urlparse
|
||||||
|
from urllib.request import Request, build_opener
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.auth import logout as do_logout
|
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
|
from django.contrib.auth import logout as do_logout
|
||||||
from django.contrib.gis.measure import D
|
from django.contrib.gis.measure import D
|
||||||
from django.contrib.postgres.search import SearchQuery, SearchVector
|
from django.contrib.postgres.search import SearchQuery, SearchVector
|
||||||
from django.contrib.staticfiles.storage import staticfiles_storage
|
from django.contrib.staticfiles.storage import staticfiles_storage
|
||||||
|
@ -33,6 +34,7 @@ from django.shortcuts import get_object_or_404
|
||||||
from django.urls import reverse, reverse_lazy
|
from django.urls import reverse, reverse_lazy
|
||||||
from django.utils.encoding import smart_bytes
|
from django.utils.encoding import smart_bytes
|
||||||
from django.utils.http import http_date
|
from django.utils.http import http_date
|
||||||
|
from django.utils.timezone import make_aware
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
from django.utils.translation import to_locale
|
from django.utils.translation import to_locale
|
||||||
from django.views.decorators.cache import cache_control
|
from django.views.decorators.cache import cache_control
|
||||||
|
@ -45,13 +47,13 @@ from django.views.generic.list import ListView
|
||||||
|
|
||||||
from . import VERSION
|
from . import VERSION
|
||||||
from .forms import (
|
from .forms import (
|
||||||
|
DEFAULT_CENTER,
|
||||||
DEFAULT_LATITUDE,
|
DEFAULT_LATITUDE,
|
||||||
DEFAULT_LONGITUDE,
|
DEFAULT_LONGITUDE,
|
||||||
DEFAULT_CENTER,
|
|
||||||
DataLayerForm,
|
|
||||||
DataLayerPermissionsForm,
|
|
||||||
AnonymousDataLayerPermissionsForm,
|
AnonymousDataLayerPermissionsForm,
|
||||||
AnonymousMapPermissionsForm,
|
AnonymousMapPermissionsForm,
|
||||||
|
DataLayerForm,
|
||||||
|
DataLayerPermissionsForm,
|
||||||
FlatErrorList,
|
FlatErrorList,
|
||||||
MapSettingsForm,
|
MapSettingsForm,
|
||||||
SendLinkForm,
|
SendLinkForm,
|
||||||
|
@ -61,16 +63,6 @@ from .forms import (
|
||||||
from .models import DataLayer, Licence, Map, Pictogram, Star, TileLayer
|
from .models import DataLayer, Licence, Map, Pictogram, Star, TileLayer
|
||||||
from .utils import get_uri_template, gzip_file, is_ajax
|
from .utils import get_uri_template, gzip_file, is_ajax
|
||||||
|
|
||||||
try:
|
|
||||||
# python3
|
|
||||||
from urllib.parse import urlparse
|
|
||||||
from urllib.request import Request, build_opener
|
|
||||||
from urllib.error import HTTPError
|
|
||||||
except ImportError:
|
|
||||||
from urlparse import urlparse
|
|
||||||
from urllib2 import Request, HTTPError, build_opener
|
|
||||||
|
|
||||||
|
|
||||||
User = get_user_model()
|
User = get_user_model()
|
||||||
|
|
||||||
|
|
||||||
|
@ -403,7 +395,7 @@ def _urls_for_js(urls=None):
|
||||||
"""
|
"""
|
||||||
if urls is None:
|
if urls is None:
|
||||||
# prevent circular import
|
# prevent circular import
|
||||||
from .urls import urlpatterns, i18n_urls
|
from .urls import i18n_urls, urlpatterns
|
||||||
|
|
||||||
urls = [
|
urls = [
|
||||||
url.name for url in urlpatterns + i18n_urls if getattr(url, "name", None)
|
url.name for url in urlpatterns + i18n_urls if getattr(url, "name", None)
|
||||||
|
@ -1019,7 +1011,7 @@ class PictogramJSONList(ListView):
|
||||||
|
|
||||||
|
|
||||||
def stats(request):
|
def stats(request):
|
||||||
last_week = date.today() - timedelta(days=7)
|
last_week = make_aware(datetime.now()) - timedelta(days=7)
|
||||||
return simple_json_response(
|
return simple_json_response(
|
||||||
**{
|
**{
|
||||||
"version": VERSION,
|
"version": VERSION,
|
||||||
|
|
|
@ -15,13 +15,13 @@ framework.
|
||||||
"""
|
"""
|
||||||
import os
|
import os
|
||||||
|
|
||||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE",
|
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "umap.settings")
|
||||||
"umap.settings")
|
|
||||||
|
|
||||||
# This application object is used by any WSGI server configured to use this
|
# This application object is used by any WSGI server configured to use this
|
||||||
# file. This includes Django's development server, if the WSGI_APPLICATION
|
# file. This includes Django's development server, if the WSGI_APPLICATION
|
||||||
# setting points here.
|
# setting points here.
|
||||||
from django.core.wsgi import get_wsgi_application
|
from django.core.wsgi import get_wsgi_application
|
||||||
|
|
||||||
application = get_wsgi_application()
|
application = get_wsgi_application()
|
||||||
|
|
||||||
# Apply WSGI middleware here.
|
# Apply WSGI middleware here.
|
||||||
|
|
Loading…
Reference in a new issue