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
|
||||
umap/remote_static
|
||||
tmp/*
|
||||
node_modules/*
|
||||
umap/static/umap/vendors
|
||||
site/*
|
||||
.pytest_cache/
|
||||
node_modules
|
||||
umap.conf
|
||||
data
|
||||
|
||||
### Python ###
|
||||
# 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]
|
||||
playwright install
|
||||
|
||||
.PHONY: pretty-templates
|
||||
pretty-templates: ## Prettify template files
|
||||
djlint umap/templates --reformat
|
||||
.PHONY: format
|
||||
format: ## Format the code and templates files
|
||||
djlint umap/templates --reformat &&\
|
||||
isort --profile black . &&\
|
||||
ruff format --target-version=py38 .
|
||||
|
||||
.PHONY: lint-templates
|
||||
lint-templates: ## Lint template files
|
||||
djlint umap/templates --lint
|
||||
.PHONY: lint
|
||||
lint: ## Lint the code and template files
|
||||
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
|
||||
version: ## Display the current version
|
||||
|
@ -47,10 +55,10 @@ publish: ## Publish the Python package to Pypi
|
|||
make clean
|
||||
|
||||
test:
|
||||
py.test -xv umap/tests/
|
||||
pytest -xv umap/tests/
|
||||
|
||||
test-integration:
|
||||
py.test -xv umap/tests/integration/
|
||||
pytest -xv umap/tests/integration/
|
||||
|
||||
clean:
|
||||
rm -f dist/*
|
||||
|
|
|
@ -3,10 +3,7 @@ import os
|
|||
import sys
|
||||
|
||||
if __name__ == "__main__":
|
||||
os.environ.setdefault(
|
||||
"DJANGO_SETTINGS_MODULE",
|
||||
"umap.settings"
|
||||
)
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "umap.settings")
|
||||
|
||||
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",
|
||||
"dependencies": {
|
||||
"@tmcw/togeojson": "^5.8.0",
|
||||
"colorbrewer": "^1.5.6",
|
||||
"csv2geojson": "5.1.1",
|
||||
"dompurify": "^3.0.3",
|
||||
"georsstogeojson": "^0.1.0",
|
||||
|
@ -31,6 +32,7 @@
|
|||
"leaflet.path.drag": "0.0.6",
|
||||
"leaflet.photon": "0.8.0",
|
||||
"osmtogeojson": "^3.0.0-beta.3",
|
||||
"simple-statistics": "^7.8.3",
|
||||
"togpx": "^0.5.4",
|
||||
"tokml": "0.4.0"
|
||||
},
|
||||
|
@ -494,6 +496,11 @@
|
|||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"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": {
|
||||
"version": "0.0.1",
|
||||
"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",
|
||||
"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": {
|
||||
"version": "15.1.0",
|
||||
"resolved": "https://registry.npmjs.org/sinon/-/sinon-15.1.0.tgz",
|
||||
|
@ -2898,6 +2913,11 @@
|
|||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"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": {
|
||||
"version": "0.0.1",
|
||||
"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",
|
||||
"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": {
|
||||
"version": "15.1.0",
|
||||
"resolved": "https://registry.npmjs.org/sinon/-/sinon-15.1.0.tgz",
|
||||
|
|
|
@ -23,16 +23,14 @@ classifiers = [
|
|||
"Topic :: Software Development :: Libraries :: Python Modules",
|
||||
"Programming Language :: Python",
|
||||
"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.9",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
"Programming Language :: Python :: 3.12",
|
||||
]
|
||||
dependencies = [
|
||||
"Django>=4.1",
|
||||
"Django>=4.2,<5",
|
||||
"django-agnocomplete==2.2.0",
|
||||
"django-compressor==4.3.1",
|
||||
"django-environ==0.10.0",
|
||||
|
@ -47,17 +45,20 @@ dependencies = [
|
|||
[project.optional-dependencies]
|
||||
dev = [
|
||||
"hatch==1.7.0",
|
||||
"black==23.3.0",
|
||||
"ruff==0.1.6",
|
||||
"djlint==1.31.0",
|
||||
"mkdocs==1.5.2",
|
||||
"vermin==1.5.2",
|
||||
"pymdown-extensions==10.4",
|
||||
"isort==5.12",
|
||||
]
|
||||
test = [
|
||||
"factory-boy==3.2.1",
|
||||
"playwright==1.38.0",
|
||||
"playwright>=1.39,<2",
|
||||
"pytest==6.2.5",
|
||||
"pytest-django==4.5.2",
|
||||
"pytest-playwright==0.4.2",
|
||||
|
||||
"pytest-playwright>=0.4.3,<1",
|
||||
"pytest-xdist>=3.5.0,<4",
|
||||
]
|
||||
docker = [
|
||||
"uwsgi==2.0.21",
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
[pytest]
|
||||
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 .models import Map, DataLayer, Pictogram, TileLayer, Licence
|
||||
|
||||
from .models import DataLayer, Licence, Map, Pictogram, TileLayer
|
||||
|
||||
|
||||
class TileLayerAdmin(admin.ModelAdmin):
|
||||
list_display = ('name', 'rank', )
|
||||
list_editable = ('rank', )
|
||||
list_display = (
|
||||
"name",
|
||||
"rank",
|
||||
)
|
||||
list_editable = ("rank",)
|
||||
|
||||
|
||||
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.contrib.auth import get_user_model
|
||||
|
||||
|
||||
from agnocomplete.register import register
|
||||
from agnocomplete.core import AgnocompleteModel
|
||||
|
||||
|
||||
@register
|
||||
class AutocompleteUser(AgnocompleteModel):
|
||||
model = get_user_model()
|
||||
|
@ -13,5 +11,5 @@ class AutocompleteUser(AgnocompleteModel):
|
|||
|
||||
def item(self, current_item):
|
||||
data = super().item(current_item)
|
||||
data['url'] = current_item.get_url()
|
||||
data["url"] = current_item.get_url()
|
||||
return data
|
||||
|
|
|
@ -5,12 +5,9 @@ from django.core import management
|
|||
|
||||
|
||||
def main():
|
||||
os.environ.setdefault(
|
||||
"DJANGO_SETTINGS_MODULE",
|
||||
"umap.settings"
|
||||
)
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "umap.settings")
|
||||
management.execute_from_command_line()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
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.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 .views import simple_json_response
|
||||
|
||||
LOGIN_URL = getattr(settings, "LOGIN_URL", "login")
|
||||
LOGIN_URL = reverse_lazy(LOGIN_URL) if not LOGIN_URL.startswith("/") else LOGIN_URL
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
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.contrib.auth import get_user_model
|
||||
from django.contrib.gis.geos import Point
|
||||
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 = (
|
||||
settings.LEAFLET_LATITUDE if hasattr(settings, "LEAFLET_LATITUDE") else 51
|
||||
|
|
|
@ -1,24 +1,23 @@
|
|||
from pathlib import Path
|
||||
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.conf import settings
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.template.loader import render_to_string
|
||||
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):
|
||||
|
||||
def handle(self, *args, **options):
|
||||
self.verbosity = options['verbosity']
|
||||
self.verbosity = options["verbosity"]
|
||||
for code, name in settings.LANGUAGES:
|
||||
code = to_locale(code)
|
||||
if self.verbosity > 0:
|
||||
print("Processing", name)
|
||||
path = ROOT / '{code}.json'.format(code=code)
|
||||
path = ROOT / "{code}.json".format(code=code)
|
||||
if not path.exists():
|
||||
print(path, 'does not exist.', 'Skipping')
|
||||
print(path, "does not exist.", "Skipping")
|
||||
else:
|
||||
with path.open(encoding="utf-8") as f:
|
||||
if self.verbosity > 1:
|
||||
|
@ -26,12 +25,11 @@ class Command(BaseCommand):
|
|||
self.render(code, f.read())
|
||||
|
||||
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:
|
||||
content = render_to_string('umap/locale.js', {
|
||||
"locale": json,
|
||||
"locale_code": code
|
||||
})
|
||||
content = render_to_string(
|
||||
"umap/locale.js", {"locale": json, "locale_code": code}
|
||||
)
|
||||
if self.verbosity > 1:
|
||||
print("Exporting to", path)
|
||||
f.write(content)
|
||||
|
|
|
@ -59,7 +59,7 @@ class Command(BaseCommand):
|
|||
else:
|
||||
picto = Pictogram()
|
||||
picto.name = name
|
||||
if (path.name != self.path.name): # Subfolders only
|
||||
if path.name != self.path.name: # Subfolders only
|
||||
picto.category = path.name
|
||||
picto.attribution = self.attribution
|
||||
with (path / filename).open("rb") as f:
|
||||
|
|
|
@ -2,7 +2,9 @@ from django.db.models import Manager
|
|||
|
||||
|
||||
class PublicManager(Manager):
|
||||
|
||||
def get_queryset(self):
|
||||
return super(PublicManager, self).get_queryset().filter(
|
||||
share_status=self.model.PUBLIC)
|
||||
return (
|
||||
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):
|
||||
|
||||
if not settings.UMAP_READONLY:
|
||||
raise MiddlewareNotUsed
|
||||
|
||||
def middleware(request):
|
||||
if request.method not in ['GET', 'OPTIONS']:
|
||||
return HttpResponseForbidden(_('Site is readonly for maintenance'))
|
||||
if request.method not in ["GET", "OPTIONS"]:
|
||||
return HttpResponseForbidden(_("Site is readonly for maintenance"))
|
||||
|
||||
return get_response(request)
|
||||
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
# Generated by Django 2.0.5 on 2018-05-19 09:27
|
||||
|
||||
from django.conf import settings
|
||||
import django.contrib.gis.db.models.fields
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
import umap.fields
|
||||
import umap.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
|
@ -18,90 +18,238 @@ class Migration(migrations.Migration):
|
|||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='DataLayer',
|
||||
name="DataLayer",
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, 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)),
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
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={
|
||||
'ordering': ('rank',),
|
||||
"ordering": ("rank",),
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Licence',
|
||||
name="Licence",
|
||||
fields=[
|
||||
('id', 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')),
|
||||
(
|
||||
"id",
|
||||
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={
|
||||
'ordering': ('name',),
|
||||
'abstract': False,
|
||||
"ordering": ("name",),
|
||||
"abstract": False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Map',
|
||||
name="Map",
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=200, verbose_name='name')),
|
||||
('slug', models.SlugField()),
|
||||
('description', 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')),
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("name", models.CharField(max_length=200, verbose_name="name")),
|
||||
("slug", models.SlugField()),
|
||||
(
|
||||
"description",
|
||||
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={
|
||||
'ordering': ('name',),
|
||||
'abstract': False,
|
||||
"ordering": ("name",),
|
||||
"abstract": False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Pictogram',
|
||||
name="Pictogram",
|
||||
fields=[
|
||||
('id', models.AutoField(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')),
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
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={
|
||||
'ordering': ('name',),
|
||||
'abstract': False,
|
||||
"ordering": ("name",),
|
||||
"abstract": False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='TileLayer',
|
||||
name="TileLayer",
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, 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)),
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
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={
|
||||
'ordering': ('rank', 'name'),
|
||||
"ordering": ("rank", "name"),
|
||||
},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='map',
|
||||
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'),
|
||||
model_name="map",
|
||||
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",
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='datalayer',
|
||||
name='map',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='umap.Map'),
|
||||
model_name="datalayer",
|
||||
name="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):
|
||||
|
||||
dependencies = [
|
||||
('umap', '0001_initial'),
|
||||
("umap", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='tilelayer',
|
||||
name='tms',
|
||||
model_name="tilelayer",
|
||||
name="tms",
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -5,22 +5,26 @@ from django.db import migrations
|
|||
|
||||
|
||||
def add_tilelayer(apps, *args):
|
||||
TileLayer = apps.get_model('umap', 'TileLayer')
|
||||
TileLayer = apps.get_model("umap", "TileLayer")
|
||||
if TileLayer.objects.count():
|
||||
return
|
||||
TileLayer(
|
||||
name='Positron',
|
||||
url_template=('https://cartodb-basemaps-{s}.global.ssl.fastly.net/'
|
||||
'light_all/{z}/{x}/{y}.png'),
|
||||
attribution=('© [[http://www.openstreetmap.org/copyright|'
|
||||
'OpenStreetMap]] contributors, © '
|
||||
'[[https://carto.com/attributions|CARTO]]')).save()
|
||||
name="Positron",
|
||||
url_template=(
|
||||
"https://cartodb-basemaps-{s}.global.ssl.fastly.net/"
|
||||
"light_all/{z}/{x}/{y}.png"
|
||||
),
|
||||
attribution=(
|
||||
"© [[http://www.openstreetmap.org/copyright|"
|
||||
"OpenStreetMap]] contributors, © "
|
||||
"[[https://carto.com/attributions|CARTO]]"
|
||||
),
|
||||
).save()
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('umap', '0002_tilelayer_tms'),
|
||||
("umap", "0002_tilelayer_tms"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
|
|
|
@ -5,18 +5,15 @@ from django.db import migrations
|
|||
|
||||
|
||||
def add_licence(apps, *args):
|
||||
Licence = apps.get_model('umap', 'Licence')
|
||||
Licence = apps.get_model("umap", "Licence")
|
||||
if Licence.objects.count():
|
||||
return
|
||||
Licence(
|
||||
name='ODbL',
|
||||
details='http://opendatacommons.org/licenses/odbl/').save()
|
||||
Licence(name="ODbL", details="http://opendatacommons.org/licenses/odbl/").save()
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('umap', '0003_add_tilelayer'),
|
||||
("umap", "0003_add_tilelayer"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
|
|
|
@ -4,14 +4,13 @@ from django.db import migrations
|
|||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('umap', '0004_add_licence'),
|
||||
("umap", "0004_add_licence"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='map',
|
||||
name='tilelayer',
|
||||
model_name="map",
|
||||
name="tilelayer",
|
||||
),
|
||||
]
|
||||
|
|
|
@ -5,15 +5,16 @@ from django.db import migrations
|
|||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('umap', '0005_remove_map_tilelayer'),
|
||||
("umap", "0005_remove_map_tilelayer"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='map',
|
||||
name='settings',
|
||||
field=django.contrib.postgres.fields.jsonb.JSONField(blank=True, default=dict, null=True, verbose_name='settings'),
|
||||
model_name="map",
|
||||
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):
|
||||
|
||||
dependencies = [
|
||||
('umap', '0006_auto_20190407_0719'),
|
||||
("umap", "0006_auto_20190407_0719"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='map',
|
||||
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'),
|
||||
model_name="map",
|
||||
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):
|
||||
|
||||
dependencies = [
|
||||
("umap", "0007_auto_20190416_1757"),
|
||||
]
|
||||
|
|
|
@ -1,25 +1,44 @@
|
|||
# Generated by Django 4.1.7 on 2023-05-05 18:02
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('umap', '0008_alter_map_settings'),
|
||||
("umap", "0008_alter_map_settings"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Star',
|
||||
name="Star",
|
||||
fields=[
|
||||
('id', models.AutoField(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')),
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
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):
|
||||
|
||||
dependencies = [
|
||||
('umap', '0009_star'),
|
||||
("umap", "0009_star"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='map',
|
||||
name='edit_status',
|
||||
field=models.SmallIntegerField(choices=[(1, 'Everyone'), (2, 'Editors only'), (3, 'Owner only')], default=3, verbose_name='edit status'),
|
||||
model_name="map",
|
||||
name="edit_status",
|
||||
field=models.SmallIntegerField(
|
||||
choices=[(1, "Everyone"), (2, "Editors only"), (3, "Owner only")],
|
||||
default=3,
|
||||
verbose_name="edit status",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='map',
|
||||
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'),
|
||||
model_name="map",
|
||||
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
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
import umap.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('umap', '0010_alter_map_edit_status_alter_map_share_status'),
|
||||
("umap", "0010_alter_map_edit_status_alter_map_share_status"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='map',
|
||||
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'),
|
||||
model_name="map",
|
||||
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(
|
||||
model_name='map',
|
||||
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'),
|
||||
model_name="map",
|
||||
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
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.utils.timezone
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
import os
|
||||
import time
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.gis.db import models
|
||||
from django.conf import settings
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.core.files.base import File
|
||||
from django.core.signing import Signer
|
||||
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
|
||||
|
||||
|
@ -380,7 +380,7 @@ class DataLayer(NamedModel):
|
|||
}
|
||||
obj["id"] = self.pk
|
||||
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
|
||||
|
||||
def clone(self, map_inst=None):
|
||||
|
@ -436,7 +436,7 @@ class DataLayer(NamedModel):
|
|||
root = self.storage_root()
|
||||
names = self.geojson.storage.listdir(root)[1]
|
||||
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))
|
||||
|
||||
def can_edit(self, user=None, request=None):
|
||||
|
|
|
@ -4,7 +4,6 @@ from email.utils import parseaddr
|
|||
|
||||
import environ
|
||||
from django.conf.locale import LANG_INFO
|
||||
from django.template.defaultfilters import slugify
|
||||
|
||||
env = environ.Env()
|
||||
|
||||
|
@ -244,9 +243,7 @@ UMAP_MAPS_PER_PAGE = 5
|
|||
UMAP_MAPS_PER_SEARCH = 25
|
||||
UMAP_MAPS_PER_PAGE_OWNER = 10
|
||||
UMAP_SEARCH_CONFIGURATION = "simple"
|
||||
UMAP_FEEDBACK_LINK = (
|
||||
"https://wiki.openstreetmap.org/wiki/UMap#Feedback_and_help" # noqa
|
||||
)
|
||||
UMAP_FEEDBACK_LINK = "https://wiki.openstreetmap.org/wiki/UMap#Feedback_and_help"
|
||||
USER_MAPS_URL = "user_maps"
|
||||
DATABASES = {"default": env.db(default="postgis://localhost:5432/umap")}
|
||||
UMAP_DEFAULT_SHARE_STATUS = None
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
{% block maincontent %}
|
||||
<div class="col wide">
|
||||
<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>
|
||||
</div>
|
||||
<div class="wrapper">
|
||||
|
@ -16,7 +16,7 @@
|
|||
<form id="user_form" method="post">
|
||||
{% csrf_token %}
|
||||
{{ form }}
|
||||
<input type="submit" value="{% trans 'Save' %}" />
|
||||
<input type="submit" value="{% trans "Save" %}" />
|
||||
</form>
|
||||
</div>
|
||||
{% if backends.backends|length %}
|
||||
|
|
|
@ -16,9 +16,15 @@
|
|||
<meta name="viewport"
|
||||
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 #}
|
||||
<link rel="icon" href="{{ STATIC_URL }}umap/favicons/favicon.ico" 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="icon"
|
||||
href="{{ STATIC_URL }}umap/favicons/favicon.ico"
|
||||
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">
|
||||
</head>
|
||||
<body class="{% block body_class %}{% endblock body_class %}">
|
||||
|
|
|
@ -10,53 +10,50 @@ from ..views import _urls_for_js
|
|||
register = template.Library()
|
||||
|
||||
|
||||
@register.inclusion_tag('umap/css.html')
|
||||
@register.inclusion_tag("umap/css.html")
|
||||
def umap_css():
|
||||
return {
|
||||
"STATIC_URL": settings.STATIC_URL
|
||||
}
|
||||
return {"STATIC_URL": settings.STATIC_URL}
|
||||
|
||||
|
||||
@register.inclusion_tag('umap/js.html')
|
||||
@register.inclusion_tag("umap/js.html")
|
||||
def umap_js(locale=None):
|
||||
return {
|
||||
"STATIC_URL": settings.STATIC_URL,
|
||||
"locale": locale
|
||||
}
|
||||
return {"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):
|
||||
layers = DataLayer.objects.filter(map=map_instance)
|
||||
datalayer_data = [c.metadata() for c in layers]
|
||||
map_settings = map_instance.settings
|
||||
if "properties" not in map_settings:
|
||||
map_settings['properties'] = {}
|
||||
map_settings['properties'].update({
|
||||
'tilelayers': [TileLayer.get_default().json],
|
||||
'datalayers': datalayer_data,
|
||||
'urls': _urls_for_js(),
|
||||
'STATIC_URL': settings.STATIC_URL,
|
||||
"editMode": 'disabled',
|
||||
'hash': False,
|
||||
'attributionControl': False,
|
||||
'scrollWheelZoom': False,
|
||||
'umapAttributionControl': False,
|
||||
'noControl': True,
|
||||
'umap_id': map_instance.pk,
|
||||
'onLoadPanel': "none",
|
||||
'captionBar': False,
|
||||
'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"] = {}
|
||||
map_settings["properties"].update(
|
||||
{
|
||||
"tilelayers": [TileLayer.get_default().json],
|
||||
"datalayers": datalayer_data,
|
||||
"urls": _urls_for_js(),
|
||||
"STATIC_URL": settings.STATIC_URL,
|
||||
"editMode": "disabled",
|
||||
"hash": False,
|
||||
"attributionControl": False,
|
||||
"scrollWheelZoom": False,
|
||||
"umapAttributionControl": False,
|
||||
"noControl": True,
|
||||
"umap_id": map_instance.pk,
|
||||
"onLoadPanel": "none",
|
||||
"captionBar": False,
|
||||
"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 ""
|
||||
unique_id = prefix + str(page) + "_" + str(map_instance.pk)
|
||||
return {
|
||||
"map_settings": json.dumps(map_settings),
|
||||
"map": map_instance,
|
||||
"unique_id": unique_id
|
||||
"unique_id": unique_id,
|
||||
}
|
||||
|
||||
|
||||
|
@ -73,7 +70,7 @@ def tilelayer_preview(tilelayer):
|
|||
|
||||
@register.filter
|
||||
def notag(s):
|
||||
return s.replace('<', '<')
|
||||
return s.replace("<", "<")
|
||||
|
||||
|
||||
@register.simple_tag(takes_context=True)
|
||||
|
@ -86,5 +83,6 @@ def paginate_querystring(context, page):
|
|||
@register.filter
|
||||
def ipdb(what):
|
||||
import ipdb
|
||||
|
||||
ipdb.set_trace()
|
||||
return ''
|
||||
return ""
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import json
|
||||
import copy
|
||||
import json
|
||||
|
||||
import factory
|
||||
from django.contrib.auth import get_user_model
|
||||
|
|
|
@ -77,4 +77,3 @@ def datalayer(map):
|
|||
@pytest.fixture
|
||||
def tilelayer():
|
||||
return TileLayerFactory()
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
from playwright.sync_api import expect
|
||||
from django.core.files.base import ContentFile
|
||||
from playwright.sync_api import expect
|
||||
|
||||
from umap.models import Map, Pictogram
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
from playwright.sync_api import expect
|
||||
from django.core.files.base import ContentFile
|
||||
from playwright.sync_api import expect
|
||||
|
||||
from umap.models import Map, Pictogram
|
||||
|
||||
|
|
|
@ -5,15 +5,17 @@ from umap.settings.base import * # pylint: disable=W0614,W0401
|
|||
SECRET_KEY = "justfortests"
|
||||
COMPRESS_ENABLED = False
|
||||
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 = {
|
||||
"default": {
|
||||
"ENGINE": "django.contrib.gis.db.backends.postgis",
|
||||
"NAME": "umap",
|
||||
"PORT": 5433,
|
||||
"USER": "travis",
|
||||
"NAME": "postgres",
|
||||
"USER": "postgres",
|
||||
"HOST": "localhost",
|
||||
"PORT": 5432,
|
||||
"PASSWORD": "postgres",
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,9 +3,10 @@ import os
|
|||
import pytest
|
||||
from django.core.files.base import ContentFile
|
||||
|
||||
from .base import DataLayerFactory, MapFactory
|
||||
from umap.models import DataLayer, Map
|
||||
|
||||
from .base import DataLayerFactory, MapFactory
|
||||
|
||||
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
|
||||
root = datalayer.storage_root()
|
||||
before = len(datalayer.geojson.storage.listdir(root)[1])
|
||||
newer = f'{root}/{datalayer.pk}_1440924889.geojson'
|
||||
medium = f'{root}/{datalayer.pk}_1440923687.geojson'
|
||||
older = f'{root}/{datalayer.pk}_1440918637.geojson'
|
||||
other = f'{root}/123456_1440918637.geojson'
|
||||
newer = f"{root}/{datalayer.pk}_1440924889.geojson"
|
||||
medium = f"{root}/{datalayer.pk}_1440923687.geojson"
|
||||
older = f"{root}/{datalayer.pk}_1440918637.geojson"
|
||||
other = f"{root}/123456_1440918637.geojson"
|
||||
for path in [medium, newer, older, other]:
|
||||
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
|
||||
datalayer.save()
|
||||
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)
|
||||
|
||||
|
||||
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.edit_status = map.ANONYMOUS
|
||||
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.user = user
|
||||
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):
|
||||
clone = map.clone()
|
||||
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.center == clone.center
|
||||
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):
|
||||
map.share_status = map.PUBLIC
|
||||
open_map = MapFactory(owner=user, licence=licence, share_status=Map.OPEN)
|
||||
private_map = MapFactory(owner=user, licence=licence,
|
||||
share_status=Map.PRIVATE)
|
||||
private_map = MapFactory(owner=user, licence=licence, share_status=Map.PRIVATE)
|
||||
assert map in Map.public.all()
|
||||
assert open_map not in Map.public.all()
|
||||
assert private_map not in Map.public.all()
|
||||
|
|
|
@ -3,8 +3,8 @@ import json
|
|||
import pytest
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.core import mail
|
||||
from django.urls import reverse
|
||||
from django.core.signing import Signer
|
||||
from django.urls import reverse
|
||||
|
||||
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
|
||||
|
||||
|
||||
def test_non_editor_cannot_access_map_if_share_status_private(
|
||||
client, map, user
|
||||
): # noqa
|
||||
def test_non_editor_cannot_access_map_if_share_status_private(client, map, user): # noqa
|
||||
url = reverse("map", args=(map.slug, map.pk))
|
||||
map.share_status = map.PRIVATE
|
||||
map.save()
|
||||
|
@ -355,9 +353,7 @@ def test_anonymous_update_without_cookie_fails(client, anonymap, post_data): #
|
|||
|
||||
|
||||
@pytest.mark.usefixtures("allow_anonymous")
|
||||
def test_anonymous_update_with_cookie_should_work(
|
||||
cookieclient, anonymap, post_data
|
||||
): # noqa
|
||||
def test_anonymous_update_with_cookie_should_work(cookieclient, anonymap, post_data): # noqa
|
||||
url = reverse("map_update", kwargs={"map_id": anonymap.pk})
|
||||
# POST only mendatory fields
|
||||
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")
|
||||
def test_clone_map_should_be_possible_if_edit_status_is_anonymous(
|
||||
client, anonymap
|
||||
): # noqa
|
||||
def test_clone_map_should_be_possible_if_edit_status_is_anonymous(client, anonymap): # noqa
|
||||
assert Map.objects.count() == 1
|
||||
url = reverse("map_clone", kwargs={"map_id": anonymap.pk})
|
||||
anonymap.edit_status = anonymap.ANONYMOUS
|
||||
|
|
|
@ -6,16 +6,22 @@ pytestmark = pytest.mark.django_db
|
|||
|
||||
|
||||
def test_tilelayer_json():
|
||||
tilelayer = TileLayerFactory(attribution='Attribution', maxZoom=19,
|
||||
minZoom=0, name='Name', rank=1, tms=True,
|
||||
url_template='http://{s}.x.fr/{z}/{x}/{y}')
|
||||
tilelayer = TileLayerFactory(
|
||||
attribution="Attribution",
|
||||
maxZoom=19,
|
||||
minZoom=0,
|
||||
name="Name",
|
||||
rank=1,
|
||||
tms=True,
|
||||
url_template="http://{s}.x.fr/{z}/{x}/{y}",
|
||||
)
|
||||
assert tilelayer.json == {
|
||||
'attribution': 'Attribution',
|
||||
'id': tilelayer.id,
|
||||
'maxZoom': 19,
|
||||
'minZoom': 0,
|
||||
'name': 'Name',
|
||||
'rank': 1,
|
||||
'tms': True,
|
||||
'url_template': 'http://{s}.x.fr/{z}/{x}/{y}'
|
||||
"attribution": "Attribution",
|
||||
"id": tilelayer.id,
|
||||
"maxZoom": 19,
|
||||
"minZoom": 0,
|
||||
"name": "Name",
|
||||
"rank": 1,
|
||||
"tms": True,
|
||||
"url_template": "http://{s}.x.fr/{z}/{x}/{y}",
|
||||
}
|
||||
|
|
|
@ -1,17 +1,18 @@
|
|||
import json
|
||||
import socket
|
||||
from datetime import date, timedelta
|
||||
from datetime import date, datetime, timedelta
|
||||
|
||||
import pytest
|
||||
from django.conf import settings
|
||||
from django.contrib.auth import get_user, get_user_model
|
||||
from django.urls import reverse
|
||||
from django.test import RequestFactory
|
||||
from django.urls import reverse
|
||||
from django.utils.timezone import make_aware
|
||||
|
||||
from umap import VERSION
|
||||
from umap.views import validate_url
|
||||
|
||||
from .base import UserFactory, MapFactory
|
||||
from .base import MapFactory, UserFactory
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
|
@ -186,9 +187,9 @@ def test_stats_empty(client):
|
|||
|
||||
@pytest.mark.django_db
|
||||
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()
|
||||
user2.last_login = date.today() - timedelta(days=8)
|
||||
user2.last_login = make_aware(datetime.now()) - timedelta(days=8)
|
||||
user2.save()
|
||||
response = client.get(reverse("stats"))
|
||||
assert json.loads(response.content.decode()) == {
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
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.static import static
|
||||
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.staticfiles.storage import staticfiles_storage
|
||||
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.csrf import ensure_csrf_cookie
|
||||
from django.views.generic.base import RedirectView
|
||||
|
||||
from . import views
|
||||
from .decorators import (
|
||||
jsonize_view,
|
||||
login_required_if_not_anonymous_allowed,
|
||||
can_edit_map,
|
||||
can_view_map,
|
||||
jsonize_view,
|
||||
login_required_if_not_anonymous_allowed,
|
||||
)
|
||||
from .utils import decorated_patterns
|
||||
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import gzip
|
||||
import os
|
||||
|
||||
from django.urls import get_resolver
|
||||
from django.urls import URLPattern, URLResolver
|
||||
from django.urls import URLPattern, URLResolver, get_resolver
|
||||
|
||||
|
||||
def get_uri_template(urlname, args=None, prefix=""):
|
||||
|
|
|
@ -3,16 +3,17 @@ import mimetypes
|
|||
import os
|
||||
import re
|
||||
import socket
|
||||
from datetime import date, timedelta
|
||||
from datetime import datetime, timedelta
|
||||
from http.client import InvalidURL
|
||||
from pathlib import Path
|
||||
from urllib.error import URLError
|
||||
from urllib.parse import quote
|
||||
from urllib.error import HTTPError, URLError
|
||||
from urllib.parse import quote, urlparse
|
||||
from urllib.request import Request, build_opener
|
||||
|
||||
from django.conf import settings
|
||||
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 logout as do_logout
|
||||
from django.contrib.gis.measure import D
|
||||
from django.contrib.postgres.search import SearchQuery, SearchVector
|
||||
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.utils.encoding import smart_bytes
|
||||
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 to_locale
|
||||
from django.views.decorators.cache import cache_control
|
||||
|
@ -45,13 +47,13 @@ from django.views.generic.list import ListView
|
|||
|
||||
from . import VERSION
|
||||
from .forms import (
|
||||
DEFAULT_CENTER,
|
||||
DEFAULT_LATITUDE,
|
||||
DEFAULT_LONGITUDE,
|
||||
DEFAULT_CENTER,
|
||||
DataLayerForm,
|
||||
DataLayerPermissionsForm,
|
||||
AnonymousDataLayerPermissionsForm,
|
||||
AnonymousMapPermissionsForm,
|
||||
DataLayerForm,
|
||||
DataLayerPermissionsForm,
|
||||
FlatErrorList,
|
||||
MapSettingsForm,
|
||||
SendLinkForm,
|
||||
|
@ -61,16 +63,6 @@ from .forms import (
|
|||
from .models import DataLayer, Licence, Map, Pictogram, Star, TileLayer
|
||||
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()
|
||||
|
||||
|
||||
|
@ -403,7 +395,7 @@ def _urls_for_js(urls=None):
|
|||
"""
|
||||
if urls is None:
|
||||
# prevent circular import
|
||||
from .urls import urlpatterns, i18n_urls
|
||||
from .urls import i18n_urls, urlpatterns
|
||||
|
||||
urls = [
|
||||
url.name for url in urlpatterns + i18n_urls if getattr(url, "name", None)
|
||||
|
@ -1019,7 +1011,7 @@ class PictogramJSONList(ListView):
|
|||
|
||||
|
||||
def stats(request):
|
||||
last_week = date.today() - timedelta(days=7)
|
||||
last_week = make_aware(datetime.now()) - timedelta(days=7)
|
||||
return simple_json_response(
|
||||
**{
|
||||
"version": VERSION,
|
||||
|
|
|
@ -15,13 +15,13 @@ framework.
|
|||
"""
|
||||
import os
|
||||
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE",
|
||||
"umap.settings")
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "umap.settings")
|
||||
|
||||
# This application object is used by any WSGI server configured to use this
|
||||
# file. This includes Django's development server, if the WSGI_APPLICATION
|
||||
# setting points here.
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
|
||||
application = get_wsgi_application()
|
||||
|
||||
# Apply WSGI middleware here.
|
||||
|
|
Loading…
Reference in a new issue