add leavedescription quest
This commit is contained in:
parent
cba970e058
commit
b9e43fdb58
4 changed files with 110 additions and 8 deletions
|
@ -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 }}">« {% trans 'back' %}</a>
|
{% endfor %}
|
||||||
</p>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<form method="post" action="{{ request.path_info }}">
|
<form method="post" action="{{ request.path_info }}">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue