add leavedescription quest

This commit is contained in:
Laura Klünder 2024-12-25 21:23:35 +01:00
parent cba970e058
commit b9e43fdb58
4 changed files with 110 additions and 8 deletions

View file

@ -5,11 +5,9 @@
<main class="account"> <main class="account">
<h3>{{ title }}</h3> <h3>{{ title }}</h3>
{% if back_url %} {% for line in description %}
<p> <p>{{ line }}</p>
<a href="{{ back_url }}">&laquo; {% trans 'back' %}</a> {% endfor %}
</p>
{% endif %}
<form method="post" action="{{ request.path_info }}"> <form method="post" action="{{ request.path_info }}">
{% csrf_token %} {% csrf_token %}

View file

@ -34,6 +34,7 @@ class QuestFormView(FormView):
return { return {
**super().get_context_data(**kwargs), **super().get_context_data(**kwargs),
"title": self.quest.quest_type_label, "title": self.quest.quest_type_label,
"description": self.quest.quest_description,
} }
def form_valid(self, form): def form_valid(self, form):

View file

@ -17,6 +17,10 @@ from c3nav.mapdata.models.access import AccessPermission
class Quest: class Quest:
obj: Any obj: Any
@property
def quest_description(self) -> list[str]:
return []
@property @property
def point(self) -> dict: def point(self) -> dict:
raise NotImplementedError raise NotImplementedError
@ -38,7 +42,7 @@ class Quest:
return [cls(obj=obj)] return [cls(obj=obj)]
@classmethod @classmethod
def get_for_request(cls, request, identifier: Any) -> Optional[Self]: def get_for_request(cls, request, identifier: str) -> Optional[Self]:
if not identifier.isdigit(): if not identifier.isdigit():
return None return None
if not (request.user.is_superuser or cls.quest_type in request.user_permissions.quests): if not (request.user.is_superuser or cls.quest_type in request.user_permissions.quests):
@ -61,7 +65,7 @@ class Quest:
@classmethod @classmethod
def cached_get_all_for_request(cls, request) -> list["QuestSchema"]: def cached_get_all_for_request(cls, request) -> list["QuestSchema"]:
cache_key = f'quests:{cls.identifier}:{AccessPermission.cache_key_for_request(request)}' cache_key = f'quests:{cls.quest_type}:{AccessPermission.cache_key_for_request(request)}'
result = cache.get(cache_key, None) result = cache.get(cache_key, None)
if result is not None: if result is not None:
return result return result

View file

@ -1,12 +1,18 @@
from dataclasses import dataclass from dataclasses import dataclass
from operator import attrgetter
from typing import ClassVar
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.db.models import F
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from shapely import Point from shapely import Point
from shapely.geometry import mapping from shapely.geometry import mapping
from c3nav.mapdata.models.geometry.space import RangingBeacon from c3nav.mapdata.forms import I18nModelFormMixin
from c3nav.mapdata.models import GraphEdge, Space
from c3nav.mapdata.models.geometry.space import RangingBeacon, LeaveDescription
from c3nav.mapdata.quests.base import register_quest, Quest, ChangeSetModelForm from c3nav.mapdata.quests.base import register_quest, Quest, ChangeSetModelForm
from c3nav.mapdata.utils.geometry import unwrap_geom
class RangingBeaconAltitudeQuestForm(ChangeSetModelForm): class RangingBeaconAltitudeQuestForm(ChangeSetModelForm):
@ -51,3 +57,96 @@ class RangingBeaconAltitudeQuest(Quest):
def _qs_for_request(cls, request): def _qs_for_request(cls, request):
return RangingBeacon.qs_for_request(request).select_related('space', return RangingBeacon.qs_for_request(request).select_related('space',
'space__level').filter(altitude_quest=True) 'space__level').filter(altitude_quest=True)
class LeaveDescriptionQuestForm(I18nModelFormMixin, ChangeSetModelForm):
class Meta:
model = LeaveDescription
fields = ("description", )
@property
def changeset_title(self):
return f'LeaveDesscription Quest: {self.instance.space.title}{self.instance.target_space.title}'
@register_quest
@dataclass
class LeaveDescriptionQuest(Quest):
quest_type = "leave_description"
quest_type_label = _('Leave Description')
form_class = LeaveDescriptionQuestForm
obj: ClassVar
space: Space
target_space: Space
the_point: Point
@property
def quest_description(self) -> list[str]:
return [
_("Please provide a description to be used when leaving “%(from_space)s” towards “%(to_space)s”.") % {
"from_space": self.space.title,
"to_space": self.target_space.title,
},
_("This will be used all doors that lead from this space to the other, not just the highlighted one! "
"So please be generic if there is more then one."),
]
@property
def point(self) -> dict:
return mapping(self.the_point)
@property
def level_id(self) -> int:
if self.space.level_id == self.target_space.level_id:
return self.space.level_id
levels = sorted((self.space.level, self.target_space.level), key=attrgetter("base_altitude"))
if self.space.level.on_top_of_id is None and self.target_space.level.on_top_of_id is None:
return levels[1].pk
return levels[0].pk
@property
def identifier(self) -> str:
return f"{self.space.pk}-{self.target_space.pk}"
@classmethod
def get_all_for_request(cls, request, space_ids: tuple[int, int] = ()):
qs = Space.qs_for_request(request)
if space_ids:
qs = qs.filter(pk__in=space_ids)
spaces = {space.pk: space for space in qs.select_related("level")}
existing = set(tuple(item) for item in LeaveDescription.objects.values_list("space_id", "target_space_id"))
more_filter = {} if not space_ids else {"from_node__space_id": space_ids[0], "to_node__space_id": space_ids[1]}
edges = {
(from_space, to_space): (from_point, to_point)
for from_space, to_space, from_point, to_point in GraphEdge.objects.filter(
from_node__space__in=spaces,
to_node__space__in=spaces,
**more_filter,
).exclude(
from_node__space=F("to_node__space")
).values_list("from_node__space_id", "to_node__space_id", "from_node__geometry", "to_node__geometry")
if (from_space, to_space) not in existing
}
return [
cls(
space=spaces[from_space],
target_space=spaces[to_space],
the_point=unwrap_geom(from_point),
)
for (from_space, to_space), (from_point, to_point) in edges.items()
]
@classmethod
def get_for_request(cls, request, identifier: str):
space_ids = identifier.split('-')
if len(space_ids) != 2 or not (space_ids[0].isdigit() and space_ids[1].isdigit()):
return None
results = cls.get_all_for_request(request, space_ids=tuple(int(i) for i in space_ids))
return results[0] if results else None
def get_form_kwargs(self, request):
instance = LeaveDescription()
instance.space = self.space
instance.target_space = self.target_space
return {"instance": instance}