new serializer for locationgroup

This commit is contained in:
Laura Klünder 2024-12-03 16:07:07 +01:00
parent db938414fb
commit cc2020e7c3
10 changed files with 58 additions and 57 deletions

View file

@ -64,7 +64,7 @@ class LevelGeometryMixin(GeometryMixin):
_('Level'), _('Level'),
{ {
'id': self.level_id, 'id': self.level_id,
'slug': self.level.get_slug(), 'slug': self.level.effective_slug,
'title': self.level.title, 'title': self.level.title,
'can_search': self.level.can_search, 'can_search': self.level.can_search,
}, },

View file

@ -93,7 +93,7 @@ class SpaceGeometryMixin(GeometryMixin):
_('Space'), _('Space'),
{ {
'id': self.space_id, 'id': self.space_id,
'slug': self.space.get_slug(), 'slug': self.space.effective_slug,
'title': self.space.title, 'title': self.space.title,
'can_search': self.space.can_search, 'can_search': self.space.can_search,
}, },

View file

@ -81,18 +81,20 @@ class LocationSlug(SerializableMixin, models.Model):
return getattr(self, model._meta.default_related_name) return getattr(self, model._meta.default_related_name)
return None return None
def get_slug(self): @property
def effective_slug(self):
return self.slug return self.slug
def _serialize(self, **kwargs): def _serialize(self, **kwargs):
result = super()._serialize(**kwargs) result = super()._serialize(**kwargs)
result["locationtype"] = self.__class__.__name__.lower() result["locationtype"] = self.__class__.__name__.lower()
result['slug'] = self.get_slug() result['slug'] = self.slug
result['effective_slug'] = self.effective_slug
return result return result
def details_display(self, **kwargs): def details_display(self, **kwargs):
result = super().details_display(**kwargs) result = super().details_display(**kwargs)
result['display'].insert(2, (_('Slug'), self.get_slug())) result['display'].insert(2, (_('Slug'), self.effective_slug))
return result return result
@cached_property @cached_property
@ -116,9 +118,9 @@ class Location(LocationSlug, AccessRestrictionMixin, TitledMixin, models.Model):
def serialize(self, detailed=True, **kwargs): def serialize(self, detailed=True, **kwargs):
result = super().serialize(detailed=detailed, **kwargs) result = super().serialize(detailed=detailed, **kwargs)
if not detailed: if not detailed:
fields = ('id', 'type', 'slug', 'title', 'subtitle', 'icon', 'point', 'bounds', 'grid_square', fields = ('id', 'type', 'slug', 'effective_slug', 'title', 'subtitle', 'icon', 'point', 'bounds',
'locations', 'on_top_of', 'effective_label_settings', 'label_override', 'add_search', 'dynamic', 'grid_square', 'locations', 'on_top_of', 'effective_label_settings', 'label_override',
'locationtype', 'geometry') 'add_search', 'dynamic', 'locationtype', 'geometry')
result = {name: result[name] for name in fields if name in result} result = {name: result[name] for name in fields if name in result}
return result return result
@ -144,7 +146,8 @@ class Location(LocationSlug, AccessRestrictionMixin, TitledMixin, models.Model):
]) ])
return result return result
def get_slug(self): @property
def effective_slug(self):
if self.slug is None: if self.slug is None:
code = self.LOCATION_TYPE_CODES.get(self.__class__.__name__) code = self.LOCATION_TYPE_CODES.get(self.__class__.__name__)
if code is not None: if code is not None:
@ -244,7 +247,7 @@ class SpecificLocation(Location, models.Model):
category.title if category.single else category.title_plural, category.title if category.single else category.title_plural,
tuple({ tuple({
'id': group.pk, 'id': group.pk,
'slug': group.get_slug(), 'slug': group.effective_slug,
'title': group.title, 'title': group.title,
'can_search': group.can_search, 'can_search': group.can_search,
} for group in sorted(groups, key=attrgetter('priority'), reverse=True)) } for group in sorted(groups, key=attrgetter('priority'), reverse=True))
@ -356,6 +359,8 @@ class LocationGroupManager(models.Manager):
class LocationGroup(Location, models.Model): class LocationGroup(Location, models.Model):
new_serialize = True
class CanReportMissing(models.TextChoices): class CanReportMissing(models.TextChoices):
DONT_OFFER = "dont_offer", _("don't offer") DONT_OFFER = "dont_offer", _("don't offer")
REJECT = "reject", _("offer in first step, then reject") REJECT = "reject", _("offer in first step, then reject")
@ -692,7 +697,8 @@ class Position(CustomLocationProxyMixin, models.Model):
def slug(self): def slug(self):
return 'p:%s' % self.secret return 'p:%s' % self.secret
def get_slug(self): @property
def effective_slug(self):
return self.slug return self.slug
def serialize(self, *args, **kwargs): def serialize(self, *args, **kwargs):

View file

@ -41,11 +41,15 @@ class DjangoModelSchema(BaseSchema):
class LocationSlugSchema(BaseSchema): class LocationSlugSchema(BaseSchema):
slug: NonEmptyStr = APIField( slug: Optional[NonEmptyStr] = APIField(
title="location slug", title="location slug",
description="a slug is a unique way to refer to a location. while locations have a shared ID space, slugs" description="a slug is a unique way to refer to a location. while locations have a shared ID space, slugs"
"are meants to be human-readable and easy to remember.\n\n" "are meants to be human-readable and easy to remember.",
"if a location doesn't have a slug defined, this field holds a slug generated from the " example="entrance",
)
effective_slug: NonEmptyStr = APIField(
title="effective location slug",
description="if a location doesn't have a slug defined, this field holds a slug generated from the "
"location type and ID, which will work even if a human-readable slug is defined later.\n\n" "location type and ID, which will work even if a human-readable slug is defined later.\n\n"
"even dynamic locations like coordinates have an (auto-generated) slug.", "even dynamic locations like coordinates have an (auto-generated) slug.",
example="entrance", example="entrance",

View file

@ -222,16 +222,7 @@ class LocationGroupSchema(LocationSchema, DjangoModelSchema):
) )
priority: int = APIField() # todo: ??? priority: int = APIField() # todo: ???
hierarchy: int = APIField() # todo: ??? hierarchy: int = APIField() # todo: ???
label_settings: Union[ label_settings: Optional[PositiveInt] = APIField(
Annotated[LabelSettingsSchema, APIField(
title="label settings",
description="label settings to use for gruop members that don't have their own set",
)],
Annotated[None, APIField(
title="null",
description="no label settings set"
)],
] = APIField(
default=None, default=None,
title="label settings", title="label settings",
description=( description=(

View file

@ -90,7 +90,7 @@ def convert_locate(data):
for measurement in BeaconMeasurement.objects.all().select_related('space', 'space__level'): for measurement in BeaconMeasurement.objects.all().select_related('space', 'space__level'):
pos = CustomLocation(measurement.space.level, measurement.geometry.x, measurement.geometry.y, pos = CustomLocation(measurement.space.level, measurement.geometry.x, measurement.geometry.y,
permissions=set()) permissions=set())
space_slug = measurement.space.get_slug() space_slug = measurement.space.effective_slug
level_label = measurement.space.level.short_label level_label = measurement.space.level.short_label
grid_square = pos.grid_square if grid.enabled else None grid_square = pos.grid_square if grid.enabled else None
measurement_lookup[pos.pk] = (measurement.pk, grid_square, space_slug, level_label) measurement_lookup[pos.pk] = (measurement.pk, grid_square, space_slug, level_label)
@ -143,23 +143,23 @@ def convert_location(data):
location = location.get_child() location = location.get_child()
if isinstance(location, LocationRedirect): if isinstance(location, LocationRedirect):
continue continue
result['locations']['by_type'].setdefault(location.__class__.__name__.lower(), {})[location.get_slug()] = 0 result['locations']['by_type'].setdefault(location.__class__.__name__.lower(), {})[location.effective_slug] = 0
location_slugs[location.pk] = location.get_slug() location_slugs[location.pk] = location.effective_slug
if isinstance(location, Level): if isinstance(location, Level):
result['locations']['by_level'][location.short_label] = 0 result['locations']['by_level'][location.short_label] = 0
result['coordinates']['by_level'][location.short_label] = 0 result['coordinates']['by_level'][location.short_label] = 0
level_labels[location.pk] = location.short_label level_labels[location.pk] = location.short_label
if isinstance(location, Space): if isinstance(location, Space):
result['locations']['by_space'][location.get_slug()] = 0 result['locations']['by_space'][location.effective_slug] = 0
result['coordinates']['by_space'][location.get_slug()] = 0 result['coordinates']['by_space'][location.effective_slug] = 0
if isinstance(location, Area): if isinstance(location, Area):
if getattr(location, 'can_search', False) or getattr(location, 'can_describe', False): if getattr(location, 'can_search', False) or getattr(location, 'can_describe', False):
result['coordinates']['by_area'][location.get_slug()] = 0 result['coordinates']['by_area'][location.effective_slug] = 0
if isinstance(location, POI): if isinstance(location, POI):
if getattr(location, 'can_search', False) or getattr(location, 'can_describe', False): if getattr(location, 'can_search', False) or getattr(location, 'can_describe', False):
result['coordinates']['by_poi'][location.get_slug()] = 0 result['coordinates']['by_poi'][location.effective_slug] = 0
if isinstance(location, LocationGroup): if isinstance(location, LocationGroup):
result['locations']['by_group'][location.get_slug()] = 0 result['locations']['by_group'][location.effective_slug] = 0
for name, value in data: for name, value in data:
if name[0] != 'pk' or name[0] == 'c:anywhere': if name[0] != 'pk' or name[0] == 'c:anywhere':
@ -187,7 +187,7 @@ def convert_location(data):
result['locations']['total'] += value result['locations']['total'] += value
location = getattr(location, 'target', location) location = getattr(location, 'target', location)
result['locations']['by_type'].setdefault(location.__class__.__name__.lower(), result['locations']['by_type'].setdefault(location.__class__.__name__.lower(),
{})[location.get_slug()] += value {})[location.effective_slug] += value
if hasattr(location, 'space_id'): if hasattr(location, 'space_id'):
result['locations']['by_space'][location_slugs[location.space_id]] += value result['locations']['by_space'][location_slugs[location.space_id]] += value
if hasattr(location, 'level_id'): if hasattr(location, 'level_id'):

View file

@ -342,32 +342,32 @@ class CustomLocation:
(_('Slug'), self.pk), (_('Slug'), self.pk),
(_('Level'), { (_('Level'), {
'id': self.level.pk, 'id': self.level.pk,
'slug': self.level.get_slug(), 'slug': self.level.effective_slug,
'title': self.level.title, 'title': self.level.title,
'can_search': self.level.can_search, 'can_search': self.level.can_search,
}), }),
(_('Space'), { (_('Space'), {
'id': self.space.pk, 'id': self.space.pk,
'slug': self.space.get_slug(), 'slug': self.space.effective_slug,
'title': self.space.title, 'title': self.space.title,
'can_search': self.space.can_search, 'can_search': self.space.can_search,
} if self.space else None), } if self.space else None),
(_('Areas'), tuple({ (_('Areas'), tuple({
'id': area.pk, 'id': area.pk,
'slug': area.get_slug(), 'slug': area.effective_slug,
'title': area.title, 'title': area.title,
'can_search': area.can_search, 'can_search': area.can_search,
} for area in self.areas)), } for area in self.areas)),
(_('Grid Square'), self.grid_square or None), (_('Grid Square'), self.grid_square or None),
(_('Near Area'), { (_('Near Area'), {
'id': self.near_area.pk, 'id': self.near_area.pk,
'slug': self.near_area.get_slug(), 'slug': self.near_area.effective_slug,
'title': self.near_area.title, 'title': self.near_area.title,
'can_search': self.near_area.can_search, 'can_search': self.near_area.can_search,
} if self.near_area else None), } if self.near_area else None),
(_('Near POI'), { (_('Near POI'), {
'id': self.near_poi.pk, 'id': self.near_poi.pk,
'slug': self.near_poi.get_slug(), 'slug': self.near_poi.effective_slug,
'title': self.near_poi.title, 'title': self.near_poi.title,
'can_search': self.near_poi.can_search, 'can_search': self.near_poi.can_search,
} if self.near_poi else None), } if self.near_poi else None),
@ -459,7 +459,8 @@ class CustomLocation:
def get_icon(self): def get_icon(self):
return self.icon return self.icon
def get_slug(self): @property
def effective_slug(self):
return self.pk return self.pk
@cached_property @cached_property

View file

@ -150,7 +150,7 @@ def preview_location(request, slug):
if location is None: if location is None:
raise Http404 raise Http404
slug = location.get_slug() slug = location.effective_slug
if isinstance(location, CustomLocation): if isinstance(location, CustomLocation):
geometries = [Point(location.x, location.y)] geometries = [Point(location.x, location.y)]

View file

@ -169,7 +169,7 @@ c3nav = {
location.elem = c3nav._build_location_html(location); location.elem = c3nav._build_location_html(location);
location.title_words = location.title.toLowerCase().split(/\s+/); location.title_words = location.title.toLowerCase().split(/\s+/);
location.subtitle_words = location.subtitle.toLowerCase().split(/\s+/); location.subtitle_words = location.subtitle.toLowerCase().split(/\s+/);
location.match = ' ' + location.title_words.join(' ') + ' ' + location.subtitle_words.join(' ') + ' ' + location.slug + ' ' + location.add_search.toLowerCase(); location.match = ' ' + location.title_words.join(' ') + ' ' + location.subtitle_words.join(' ') + ' ' + location.effective_slug + ' ' + location.add_search.toLowerCase();
locations.push(location); locations.push(location);
locations_by_id[location.id] = location; locations_by_id[location.id] = location;
if (location.point && location.label_settings) { if (location.point && location.label_settings) {
@ -464,7 +464,7 @@ c3nav = {
for (var j = 0; j < sublocations.length; j++) { for (var j = 0; j < sublocations.length; j++) {
loc = sublocations[j]; loc = sublocations[j];
if (loc.can_search) { if (loc.can_search) {
loclist.append($('<a>').attr('href', '/l/' + loc.slug + '/details/').attr('data-id', loc.id).click(function (e) { loclist.append($('<a>').attr('href', '/l/' + loc.effective_slug + '/details/').attr('data-id', loc.id).click(function (e) {
e.preventDefault(); e.preventDefault();
c3nav._locationinput_set($('#destination-input'), c3nav.locations_by_id[parseInt($(this).attr('data-id'))]); c3nav._locationinput_set($('#destination-input'), c3nav.locations_by_id[parseInt($(this).attr('data-id'))]);
c3nav.update_state(false, false, true); c3nav.update_state(false, false, true);
@ -736,12 +736,12 @@ c3nav = {
var url = embed ? '/embed' : ''; var url = embed ? '/embed' : '';
if (state.routing) { if (state.routing) {
if (state.origin) { if (state.origin) {
url += (state.destination) ? '/r/' + state.origin.slug + '/' + state.destination.slug + '/' : '/o/' + state.origin.slug + '/'; url += (state.destination) ? '/r/' + state.origin.effective_slug + '/' + state.destination.effective_slug + '/' : '/o/' + state.origin.effective_slug + '/';
} else { } else {
url += (state.destination) ? '/d/' + state.destination.slug + '/' : '/r/'; url += (state.destination) ? '/d/' + state.destination.effective_slug + '/' : '/r/';
} }
} else { } else {
url += state.destination ? ('/l/' + state.destination.slug + '/') : '/'; url += state.destination ? ('/l/' + state.destination.effective_slug + '/') : '/';
} }
if (state.details && (url.startsWith('/l/') || url.startsWith('/r/'))) { if (state.details && (url.startsWith('/l/') || url.startsWith('/r/'))) {
url += 'details/' url += 'details/'
@ -900,9 +900,8 @@ c3nav = {
_buttons_share_click: function (location) { _buttons_share_click: function (location) {
var url = c3nav._get_share_url(false, location); var url = c3nav._get_share_url(false, location);
if (navigator.share) { if (navigator.share) {
var title var title, subtitle;
, subtitle; if (location.effective_slug) {
if (location.slug) {
title = location.title; title = location.title;
subtitle = location.subtitle; subtitle = location.subtitle;
} else { } else {
@ -923,8 +922,8 @@ c3nav = {
}, },
_get_share_url: function (with_position, location) { _get_share_url: function (with_position, location) {
var url, state = $.extend({}, c3nav.state); var url, state = $.extend({}, c3nav.state);
if (location.slug) { if (location.effective_slug) {
url = '/l/' + location.slug + '/'; url = '/l/' + location.effective_slug + '/';
} else { } else {
if (!with_position) { if (!with_position) {
state.center = null; state.center = null;

View file

@ -150,24 +150,24 @@ def map_index(request, mode=None, slug=None, slug2=None, details=None, options=N
metadata = { metadata = {
'title': _('Route from %s to %s') % (origin.title, destination.title), 'title': _('Route from %s to %s') % (origin.title, destination.title),
'preview_img_url': request.build_absolute_uri(reverse('mapdata.preview.route', kwargs={ 'preview_img_url': request.build_absolute_uri(reverse('mapdata.preview.route', kwargs={
'slug': origin.get_slug(), 'slug': origin.effective_slug,
'slug2': destination.get_slug(), 'slug2': destination.effective_slug,
})), })),
'canonical_url': request.build_absolute_uri(reverse('site.index', kwargs={ 'canonical_url': request.build_absolute_uri(reverse('site.index', kwargs={
'mode': 'r', 'mode': 'r',
'slug': origin.get_slug(), 'slug': origin.effective_slug,
'slug2': destination.get_slug(), 'slug2': destination.effective_slug,
'details': False, 'details': False,
'options': False, 'options': False,
})), })),
} }
elif destination is not None or origin is not None: elif destination is not None or origin is not None:
if destination is not None: if destination is not None:
loc_slug = destination.get_slug() loc_slug = destination.effective_slug
title = destination.title title = destination.title
subtitle = destination.subtitle if hasattr(destination, 'subtitle') else None subtitle = destination.subtitle if hasattr(destination, 'subtitle') else None
else: else:
loc_slug = origin.get_slug() loc_slug = origin.effective_slug
title = origin.title title = origin.title
subtitle = origin.subtitle if hasattr(origin, 'subtitle') else None subtitle = origin.subtitle if hasattr(origin, 'subtitle') else None
metadata = { metadata = {
@ -579,7 +579,7 @@ def report_missing_choose(request, coordinates):
'locations': [ 'locations': [
{ {
"url": reverse('site.report_create', "url": reverse('site.report_create',
kwargs={"coordinates": coordinates, "group": group.get_slug()}), kwargs={"coordinates": coordinates, "group": group.effective_slug}),
"location": group, "location": group,
"replace_subtitle": group.description "replace_subtitle": group.description
} }