make linter happy

This commit is contained in:
Laura Klünder 2024-02-07 18:34:28 +01:00
parent d7f175f7ef
commit 8c8ef12cf0
19 changed files with 107 additions and 75 deletions

View file

@ -9,14 +9,14 @@ from django.conf import settings
from django.core.cache import cache
from django.core.exceptions import FieldDoesNotExist
from django.core.serializers.json import DjangoJSONEncoder
from django.db.models import Q, Prefetch
from django.db.models import Prefetch, Q
from django.forms import (BooleanField, CharField, ChoiceField, DecimalField, Form, JSONField, ModelChoiceField,
ModelForm, MultipleChoiceField, Select, ValidationError)
from django.forms.widgets import HiddenInput, TextInput
from django.utils.translation import get_language
from django.utils.translation import gettext_lazy as _
from shapely.geometry.geo import mapping
from pydantic import ValidationError as PydanticValidationError
from shapely.geometry.geo import mapping
from c3nav.editor.models import ChangeSet, ChangeSetUpdate
from c3nav.mapdata.fields import GeometryField
@ -40,12 +40,12 @@ class EditorFormBase(I18nModelFormMixin, ModelForm):
obstaclegroup_theme_colors = {}
else:
locationgroup_theme_colors = {
l.location_group_id: l
for l in self.instance.location_groups.filter(theme_id=self.instance.pk)
theme_location_group.location_group_id: theme_location_group
for theme_location_group in self.instance.location_groups.filter(theme_id=self.instance.pk)
}
obstaclegroup_theme_colors = {
o.obstacle_group_id: o
for o in self.instance.obstacle_groups.filter(theme_id=self.instance.pk)
theme_obstacle.obstacle_group_id: theme_obstacle
for theme_obstacle in self.instance.obstacle_groups.filter(theme_id=self.instance.pk)
}
# TODO: can we get the model class via relationships?
@ -54,23 +54,24 @@ class EditorFormBase(I18nModelFormMixin, ModelForm):
related = locationgroup_theme_colors.get(locationgroup.pk, None)
value = related.fill_color if related is not None else None
other_themes_colors = {
l.title: l.fill_color
for l in locationgroup.theme_colors.all()
if related is None or l.pk != related.pk
theme_location_group.title: theme_location_group.fill_color
for theme_location_group in locationgroup.theme_colors.all()
if related is None or theme_location_group.pk != related.pk
}
if len(other_themes_colors) > 0:
other_themes_colors = json.dumps(other_themes_colors)
else:
other_themes_colors = False
field = CharField(max_length=32,
label=locationgroup.title,
required=False,
initial=value,
widget=TextInput(attrs={
'data-themed-color': True,
'data-color-base-theme': locationgroup.color if locationgroup.color else False,
'data-colors-other-themes': other_themes_colors,
}))
field = CharField(
max_length=32,
label=locationgroup.title,
required=False,
initial=value,
widget=TextInput(attrs={
'data-themed-color': True,
'data-color-base-theme': locationgroup.color if locationgroup.color else False,
'data-colors-other-themes': other_themes_colors,
}))
self.fields[f'locationgroup_{locationgroup.pk}'] = field
for obstaclegroup in ObstacleGroup.objects.prefetch_related(
@ -375,7 +376,8 @@ class EditorFormBase(I18nModelFormMixin, ModelForm):
self.instance.groups.set(groups)
if self._meta.model.__name__ == 'Theme':
locationgroup_colors = {l.location_group_id: l for l in self.instance.location_groups.all()}
locationgroup_colors = {theme_location_group.location_group_id: theme_location_group
for theme_location_group in self.instance.location_groups.all()}
for locationgroup in LocationGroup.objects.all():
value = self.cleaned_data[f'locationgroup_{locationgroup.pk}']
if value:

View file

@ -87,8 +87,10 @@ class GeometryField(models.JSONField):
def _validate_geomtype(self, value, exception: typing.Type[Exception] = ValidationError):
if not isinstance(value, self.classes):
# if you get this error with wrappedgeometry, looked into wrapped_geom
raise exception('Expected %s instance, got %s instead.' % (' or '.join(c.__name__ for c in self.classes),
repr(value)))
raise TypeError('Expected %s instance, got %s, %s instead.' % (
' or '.join(c.__name__ for c in self.classes),
repr(value), value.wrapped_geom
))
def get_final_value(self, value, as_json=False):
json_value = format_geojson(mapping(value))

View file

@ -1,17 +1,15 @@
import hashlib
import json
from uuid import UUID
import requests
from django.conf import settings
from django.core.management.base import BaseCommand
from pydantic import Field, BaseModel
from pydantic import PositiveInt
from pydantic import BaseModel, Field, PositiveInt
from shapely import Point
from shapely.geometry import shape
from c3nav.api.utils import NonEmptyStr
from c3nav.mapdata.models import Area, Space, LocationGroup, LocationSlug, MapUpdate
from c3nav.mapdata.models import Area, LocationGroup, LocationSlug, MapUpdate, Space
from c3nav.mapdata.models.geometry.space import POI
from c3nav.mapdata.models.report import Report
from c3nav.mapdata.utils.cache.changes import changed_geometries
@ -189,7 +187,7 @@ class Command(BaseCommand):
if result.geometry != new_geometry or True:
if result.import_block_geom:
geometry_needs_change.append(f"change geometry")
geometry_needs_change.append("change geometry")
print(f"NOTE: {item.slug} / {item.id} geometry has changed but is blocked")
else:
result.geometry = new_geometry
@ -198,7 +196,7 @@ class Command(BaseCommand):
new_main_point = Point(item.location) if item.location else None
if result.main_point != new_main_point:
if result.import_block_geom:
geometry_needs_change.append(f"change main point")
geometry_needs_change.append("change main point")
print(f"NOTE: {item.slug} / {item.id} main point has changed but is blocked")
else:
result.main_point = new_main_point
@ -210,8 +208,8 @@ class Command(BaseCommand):
obj=item,
report=Report(
category="location-issue",
title=f"importhub: geometry is blocked but needs changing",
description=f"changes needed: "+','.join(geometry_needs_change),
title="importhub: geometry is blocked but needs changing",
description="changes needed: "+','.join(geometry_needs_change),
location=result,
)
)
@ -278,8 +276,8 @@ class Command(BaseCommand):
obj=item,
report=Report(
category="location-issue",
title=f"importhub: data is blocked but needs changing",
description=f"changes needed: "+','.join(data_needs_change),
title="importhub: data is blocked but needs changing",
description="changes needed: "+','.join(data_needs_change),
location=result,
)
)
@ -299,8 +297,8 @@ class Command(BaseCommand):
obj=item,
report=Report(
category="location-issue",
title=f"importhub: location no longer has any valid group ids",
description=f"from the hub we would remove all groups, this seems wrong",
title="importhub: location no longer has any valid group ids",
description="from the hub we would remove all groups, this seems wrong",
location=result,
)
)
@ -326,8 +324,8 @@ class Command(BaseCommand):
obj=item,
report=Report(
category="location-issue",
title=f"importhub: new groups",
description=(f"hub wants new groups for this, groups are now: " +
title="importhub: new groups",
description=("hub wants new groups for this, groups are now: " +
str([group.title for group in new_groups])),
location=result,
)
@ -343,10 +341,9 @@ class Command(BaseCommand):
obj=import_tag,
report=Report(
category="location-issue",
title=f"importhub: delete this",
description=f"hub wants to delete this",
title="importhub: delete this",
description="hub wants to delete this",
location=location,
)
)
print(f"NOTE: {location.slug} / {import_tag} should be deleted")

View file

@ -1,3 +1,4 @@
import typing
from decimal import Decimal
from django.conf import settings
@ -21,6 +22,9 @@ from c3nav.mapdata.utils.cache.changes import changed_geometries
from c3nav.mapdata.utils.geometry import unwrap_geom
from c3nav.mapdata.utils.json import format_geojson
if typing.TYPE_CHECKING:
from c3nav.mapdata.render.theme import ThemeColorManager
class SpaceGeometryMixin(GeometryMixin):
space = models.ForeignKey('mapdata.Space', on_delete=models.CASCADE, verbose_name=_('space'))
@ -240,7 +244,11 @@ class Obstacle(SpaceGeometryMixin, models.Model):
def get_color(self, color_manager: 'ThemeColorManager', instance=None):
if instance is None:
instance = self
return color_manager.obstaclegroup_fill_color(instance.group) if instance.group is not None else color_manager.obstacles_default_fill
return (
color_manager.obstaclegroup_fill_color(instance.group)
if instance.group is not None
else color_manager.obstacles_default_fill
)
class LineObstacle(SpaceGeometryMixin, models.Model):
@ -284,7 +292,11 @@ class LineObstacle(SpaceGeometryMixin, models.Model):
if instance is None:
instance = self
# TODO: should line obstacles use border color?
return color_manager.obstaclegroup_fill_color(instance.group) if instance.group is not None else color_manager.obstacles_default_fill
return (
color_manager.obstaclegroup_fill_color(instance.group)
if instance.group is not None
else color_manager.obstacles_default_fill
)
@property
def buffered_geometry(self):

View file

@ -1,4 +1,5 @@
import string
import typing
from contextlib import suppress
from datetime import timedelta
from decimal import Decimal
@ -25,6 +26,9 @@ from c3nav.mapdata.models.base import SerializableMixin, TitledMixin
from c3nav.mapdata.utils.fields import LocationById
from c3nav.mapdata.utils.models import get_submodels
if typing.TYPE_CHECKING:
from c3nav.mapdata.render.theme import ThemeColorManager
class LocationSlugManager(models.Manager):
def get_queryset(self):

View file

@ -54,6 +54,7 @@ class ThemeObstacleGroupBackgroundColor(models.Model):
A background color for an ObstacleGroup in a theme
"""
theme = models.ForeignKey(Theme, on_delete=models.CASCADE, related_name="obstacle_groups")
obstacle_group = models.ForeignKey(ObstacleGroup, on_delete=models.SET_NULL, null=True, blank=True, related_name="theme_colors")
obstacle_group = models.ForeignKey(ObstacleGroup, on_delete=models.SET_NULL, null=True, blank=True,
related_name="theme_colors")
fill_color = models.CharField(max_length=32, null=True, blank=True)
border_color = models.CharField(max_length=32, null=True, blank=True)

View file

@ -6,8 +6,7 @@ from itertools import chain
from typing import Literal, TypeVar
import numpy as np
from matplotlib.patches import Polygon
from shapely.geometry import GeometryCollection, LineString, MultiLineString, MultiPolygon, Point
from shapely.geometry import GeometryCollection, LineString, MultiLineString, MultiPolygon, Point, Polygon
from shapely.geometry.base import BaseGeometry
from shapely.ops import unary_union

View file

@ -1,4 +1,5 @@
import operator
import typing
from collections import Counter, deque
from functools import reduce
from itertools import chain
@ -15,6 +16,9 @@ from c3nav.mapdata.utils.cache import AccessRestrictionAffected
from c3nav.mapdata.utils.geometry import get_rings, unwrap_geom
from c3nav.mapdata.utils.mesh import triangulate_rings
if typing.TYPE_CHECKING:
from c3nav.mapdata.render.theme import ThemeColorManager
empty_geometry_collection = GeometryCollection()
@ -136,7 +140,9 @@ class LevelGeometries:
area.geometry = area.geometry.intersection(unwrap_geom(space.walkable_geom))
if access_restriction is not None:
access_restriction_affected.setdefault(access_restriction, []).append(area.geometry)
colors.setdefault(area.get_color_sorted(color_manager), {}).setdefault(access_restriction, []).append(area.geometry)
colors.setdefault(
area.get_color_sorted(color_manager), {}
).setdefault(access_restriction, []).append(area.geometry)
for column in space.columns.all():
access_restriction = column.access_restriction_id
@ -162,7 +168,9 @@ class LevelGeometries:
for lineobstacle in space.lineobstacles.all():
if not lineobstacle.height:
continue
obstacles.setdefault(int(lineobstacle.height*1000), {}).setdefault(lineobstacle.get_color(color_manager), []).append(
obstacles.setdefault(int(lineobstacle.height*1000), {}).setdefault(
lineobstacle.get_color(color_manager), []
).append(
lineobstacle.buffered_geometry.intersection(unwrap_geom(space.walkable_geom))
)

View file

@ -85,7 +85,9 @@ class LevelRenderData:
altitudeareas_above = [] # todo: typing
for render_level in reversed(levels):
# build level geometry for every single level
single_level_geoms[render_level.pk] = LevelGeometries.build_for_level(render_level, color_manager, altitudeareas_above)
single_level_geoms[render_level.pk] = LevelGeometries.build_for_level(
render_level, color_manager, altitudeareas_above
)
# ignore intermediate levels in this pass
if render_level.on_top_of_id is not None:

View file

@ -11,7 +11,6 @@ from c3nav.mapdata.render.utils import get_full_levels, get_min_altitude
from c3nav.mapdata.utils.color import color_to_rgb, rgb_to_color
class MapRenderer:
def __init__(self, level, minx, miny, maxx, maxy, scale=1, access_permissions=None, full_levels=False,
min_width=None):

View file

@ -24,13 +24,13 @@ class ThemeColorManager:
self.obstacles_default_border = RENDER_COLOR_OBSTACLES_DEFAULT_BORDER
self.location_group_border_colors = {}
self.location_group_fill_colors = {
l.pk: l.color
for l in LocationGroup.objects.filter(color__isnull=False).all()
location_group.pk: location_group.color
for location_group in LocationGroup.objects.filter(color__isnull=False).all()
}
self.obstacle_group_border_colors = {}
self.obstacle_group_fill_colors = {
o.pk: o.color
for o in ObstacleGroup.objects.filter(color__isnull=False).all()
obstacle_group.pk: obstacle_group.color
for obstacle_group in ObstacleGroup.objects.filter(color__isnull=False).all()
}
else:
self.background = theme.color_background
@ -41,20 +41,20 @@ class ThemeColorManager:
self.obstacles_default_fill = theme.color_obstacles_default_fill
self.obstacles_default_border = theme.color_obstacles_default_border
self.location_group_border_colors = {
l.location_group_id: l.border_color
for l in theme.location_groups.all()
theme_location_group.location_group_id: theme_location_group.border_color
for theme_location_group in theme.location_groups.all()
}
self.location_group_fill_colors = {
l.location_group_id: l.fill_color
for l in theme.location_groups.all()
theme_location_group.location_group_id: theme_location_group.fill_color
for theme_location_group in theme.location_groups.all()
}
self.obstacle_group_border_colors = {
o.obstacle_group_id: o.border_color
for o in theme.obstacle_groups.all()
theme_obstacle_group.obstacle_group_id: theme_obstacle_group.border_color
for theme_obstacle_group in theme.obstacle_groups.all()
}
self.obstacle_group_fill_colors = {
o.obstacle_group_id: o.fill_color
for o in theme.obstacle_groups.all()
theme_obstacle.obstacle_group_id: theme_obstacle.fill_color
for theme_obstacle in theme.obstacle_groups.all()
}
def locationgroup_border_color(self, location_group: LocationGroup):

View file

@ -2,7 +2,7 @@ from django.urls import path, register_converter
from c3nav.mapdata.converters import (AccessPermissionsConverter, ArchiveFileExtConverter, HistoryFileExtConverter,
HistoryModeConverter, SignedIntConverter)
from c3nav.mapdata.views import get_cache_package, map_history, tile, preview_location, preview_route
from c3nav.mapdata.views import get_cache_package, map_history, preview_location, preview_route, tile
from c3nav.site.converters import LocationConverter
register_converter(LocationConverter, 'loc')
@ -16,7 +16,8 @@ urlpatterns = [
path('<int:level>/<sint:zoom>/<sint:x>/<sint:y>/<int:theme>.png', tile, name='mapdata.tile'),
path('preview/l/<loc:slug>.png', preview_location, name='mapdata.preview.location'),
path('preview/r/<loc:slug>/<loc:slug2>.png', preview_route, name='mapdata.preview.route'),
path('<int:level>/<sint:zoom>/<sint:x>/<sint:y>/<int:theme>/<a_perms:access_permissions>.png', tile, name='mapdata.tile'),
path('<int:level>/<sint:zoom>/<sint:x>/<sint:y>/<int:theme>/<a_perms:access_permissions>.png', tile,
name='mapdata.tile'),
path('history/<int:level>/<h_mode:mode>.<h_fileext:filetype>', map_history, name='mapdata.map_history'),
path('cache/package.<archive_fileext:filetype>', get_cache_package, name='mapdata.cache_package'),
]

View file

@ -51,5 +51,6 @@ def build_access_cache_key(access_permissions: set):
def build_tile_etag(level_id, zoom, x, y, theme_id, base_cache_key, access_cache_key, tile_secret):
# we want a short etag so HTTP 304 responses are tiny
return '"' + binascii.b2a_base64(hashlib.sha256(
('%d-%d-%d-%d:%s:%s:%s:%s' % (level_id, zoom, x, y, str(theme_id), base_cache_key, access_cache_key, tile_secret[:26])).encode()
('%d-%d-%d-%d:%s:%s:%s:%s' %
(level_id, zoom, x, y, str(theme_id), base_cache_key, access_cache_key, tile_secret[:26])).encode()
).digest()[:15], newline=False).decode() + '"'

View file

@ -12,7 +12,7 @@ from django.http import Http404, HttpResponse, HttpResponseNotModified, Streamin
from django.shortcuts import get_object_or_404
from django.utils.http import content_disposition_header
from django.views.decorators.http import etag
from shapely import Point, box, LineString, unary_union
from shapely import LineString, Point, box, unary_union
from c3nav.mapdata.middleware import no_language
from c3nav.mapdata.models import Level, MapUpdate
@ -345,7 +345,7 @@ def tile(request, level, zoom, x, y, theme, access_permissions: Optional[set] =
if not cache_package.bounds_valid(minx, miny, maxx, maxy):
raise Http404
theme = None if theme is 0 else int(theme)
theme = None if theme == 0 else int(theme)
theme_key = str(theme)
# get level
@ -372,7 +372,8 @@ def tile(request, level, zoom, x, y, theme, access_permissions: Optional[set] =
access_cache_key = build_access_cache_key(access_permissions)
# check browser cache
tile_etag = build_tile_etag(level, zoom, x, y, theme_key, base_cache_key, access_cache_key, settings.SECRET_TILE_KEY)
tile_etag = build_tile_etag(level, zoom, x, y, theme_key, base_cache_key, access_cache_key,
settings.SECRET_TILE_KEY)
if_none_match = request.META.get('HTTP_IF_NONE_MATCH')
if if_none_match == tile_etag:
return HttpResponseNotModified()

View file

@ -1,5 +1,5 @@
from enum import StrEnum
from typing import Annotated, Optional, Union, Any
from typing import Annotated, Any, Optional, Union
from django.core.exceptions import ValidationError
from django.urls import reverse

View file

@ -361,7 +361,7 @@ MIDDLEWARE = [
'c3nav.mapdata.middleware.UserDataMiddleware',
'c3nav.site.middleware.MobileclientMiddleware',
'c3nav.control.middleware.UserPermissionsMiddleware',
#'c3nav.api.middleware.JsonRequestBodyMiddleware', # might still be needed in editor
# 'c3nav.api.middleware.JsonRequestBodyMiddleware', # might still be needed in editor
]
with suppress(ImportError):

View file

@ -2,7 +2,7 @@ from itertools import chain
from django.urls import path, register_converter
from c3nav.site.converters import AtPositionConverter, CoordinatesConverter, IsEmbedConverter, LocationConverter
from c3nav.site.converters import AtPositionConverter, CoordinatesConverter, IsEmbedConverter
from c3nav.site.views import (about_view, access_redeem_view, account_manage, account_view, api_secret_create,
api_secret_list, change_password_view, choose_language, delete_account_view, login_view,
logout_view, map_index, position_create, position_detail, position_list, position_set,
@ -18,13 +18,15 @@ pos = '<at_pos:pos>'
def index_paths(pre, suf):
return [
path(f'{pre}l/<loc:slug>/{suf}', map_index, {'mode': 'l', 'details': False, 'nearby': False}, name='site.index', ),
path(f'{pre}l/<loc:slug>/{suf}', map_index, {'mode': 'l', 'details': False, 'nearby': False},
name='site.index'),
path(f'{pre}l/<loc:slug>/details/{suf}', map_index, {'mode': 'l', 'details': True}, name='site.index'),
path(f'{pre}l/<loc:slug>/nearby/{suf}', map_index, {'mode': 'l', 'nearby': True}, name='site.index'),
path(f'{pre}o/<loc:slug>/{suf}', map_index, {'mode': 'o'}, name='site.index'),
path(f'{pre}d/<loc:slug>/{suf}', map_index, {'mode': 'd'}, name='site.index'),
path(f'{pre}r/{suf}', map_index, {'mode': 'r'}, name='site.index'),
path(f'{pre}r/<loc:slug>/<loc:slug2>/{suf}', map_index, {'mode': 'r', 'details': False, 'options': False}, name='site.index'),
path(f'{pre}r/<loc:slug>/<loc:slug2>/{suf}', map_index, {'mode': 'r', 'details': False, 'options': False},
name='site.index'),
path(f'{pre}r/<loc:slug>/<loc:slug2>/details/{suf}', map_index, {'mode': 'r', 'details': True},
name='site.index'),
path(f'{pre}r/<loc:slug>/<loc:slug2>/options/{suf}', map_index, {'mode': 'r', 'options': True},

View file

@ -29,7 +29,7 @@ from c3nav import __version__ as c3nav_version
from c3nav.api.models import Secret
from c3nav.mapdata.grid import grid
from c3nav.mapdata.models import Location, Source
from c3nav.mapdata.models.access import AccessPermissionToken, AccessPermission
from c3nav.mapdata.models.access import AccessPermission, AccessPermissionToken
from c3nav.mapdata.models.locations import LocationRedirect, Position, SpecificLocation, get_position_secret
from c3nav.mapdata.models.report import Report, ReportUpdate
from c3nav.mapdata.utils.locations import (get_location_by_id_for_request, get_location_by_slug_for_request,

View file

@ -188,7 +188,7 @@ class TileServer:
error = False
try:
last_check = self.cache.get('cache_package_last_successful_check')
except pylibmc.Error as e:
except pylibmc.Error:
error = True
text = b'memcached error'
else:
@ -199,8 +199,8 @@ class TileServer:
else:
text = b'last successful cache package check is unknown'
start_response(('500' if error else '200') + ' OK', [self.get_date_header(),
('Content-Type', 'text/plain'),
('Content-Length', str(len(text)))])
('Content-Type', 'text/plain'),
('Content-Length', str(len(text)))])
return [text]
def get_cache_package(self):
@ -300,7 +300,8 @@ class TileServer:
if cached_result is not None:
return self.deliver_tile(start_response, tile_etag, cached_result)
r = requests.get('%s/map/%d/%d/%d/%d/%d/%s.png' % (self.upstream_base, level, zoom, x, y, theme_id, access_cache_key),
r = requests.get('%s/map/%d/%d/%d/%d/%d/%s.png' %
(self.upstream_base, level, zoom, x, y, theme_id, access_cache_key),
headers=self.auth_headers, auth=self.http_auth)
if r.status_code == 200 and r.headers['Content-Type'] == 'image/png':