class RecipeGroupViewSet(BoundlexxReadOnlyViewSet): schema = DescriptiveAutoSchema(tags=["recipes"]) queryset = (RecipeGroup.objects.all().select_related( "display_name").prefetch_related("members", "display_name__strings")) serializer_class = RecipeGroupSerializer lookup_field = "id" filter_backends = [ DjangoFilterBackend, ] filterset_class = LocalizationFilterSet def list(self, request, *args, **kwargs): # noqa A003 """ Retrieves the list of skill groups avaiable in Boundless """ return super().list(request, *args, **kwargs) # pylint: disable=no-member def retrieve( self, request, *args, **kwargs, ): # pylint: disable=arguments-differ """ Retrieves a skill group with a given id """ return super().retrieve(request, *args, **kwargs) # pylint: disable=no-member
class ItemResourceTimeseriesViewSet(TimeseriesMixin, NestedViewSetMixin, BoundlexxReadOnlyViewSet): schema = DescriptiveAutoSchema(tags=["items", "timeseries"]) queryset = ResourceCount.objects.filter( world_poll__world__active=True, world_poll__world__is_creative=False, ).select_related("world_poll", "world_poll__world", "item", "item__resource_data") serializer_class = URLItemResourceCountTimeSeriesSerializer time_bucket_serializer_class = ItemResourceCountTimeSeriesTBSerializer number_fields = ["count"] lookup_field = "id" def get_queryset(self): item_id = self.kwargs.get("id") if item_id not in settings.BOUNDLESS_WORLD_POLL_RESOURCE_MAPPING: raise Http404 queryset = super().get_queryset() if not self.request.user.has_perm("boundless.can_view_private"): queryset = queryset.filter(world_poll__world__is_public=True) return queryset def list(self, request, *args, **kwargs): # noqa A003 """ Retrieves the list resource counts for a give item/world combination """ return super().list(request, *args, **kwargs) # pylint: disable=no-member list.example = { "list": { "value": get_list_example(ITEM_RESOURCE_TIMESERIES_EXAMPLE) } } # type: ignore # noqa E501 list.operation_id = "listItemResourceTimeseries" # type: ignore # noqa E501 def retrieve( self, request, *args, **kwargs, ): # pylint: disable=arguments-differ """ Retrieves a specific resource counts for a give item/world combination """ return super().retrieve(request, *args, **kwargs) # pylint: disable=no-member retrieve.example = { "retrieve": { "value": ITEM_RESOURCE_TIMESERIES_EXAMPLE } } # type: ignore # noqa E501 retrieve.operation_id = "retrieveItemResourceTimeseries" # type: ignore # noqa E501
class SkillGroupViewSet(BoundlexxReadOnlyViewSet): schema = DescriptiveAutoSchema(tags=["skills"]) queryset = (SkillGroup.objects.all().select_related( "display_name").prefetch_related("display_name__strings")) serializer_class = URLSkillGroupSerializer lookup_field = "id" filter_backends = [ DjangoFilterBackend, RankedFuzzySearchFilter, filters.OrderingFilter, DedupedFilter, ] filterset_class = LocalizationFilterSet ordering = ["-rank", "id"] ordering_fields: list[str] = [] search_fields = [ "name", "display_name__strings__text", ] def list(self, request, *args, **kwargs): # noqa A003 """ Retrieves the list of skill groups avaiable in Boundless """ return super().list(request, *args, **kwargs) # pylint: disable=no-member list.example = { "list": { "value": get_list_example(SKILL_GROUP_EXAMPLE) } } # type: ignore # noqa E501 def retrieve( self, request, *args, **kwargs, ): # pylint: disable=arguments-differ """ Retrieves a skill group with a given id """ return super().retrieve(request, *args, **kwargs) # pylint: disable=no-member retrieve.example = { "retrieve": { "value": SKILL_GROUP_EXAMPLE } } # type: ignore # noqa E501
class GameFileViewSet(BoundlexxReadOnlyViewSet): schema = DescriptiveAutoSchema(tags=["game"]) permission_classes = [IsAuthenticated] queryset = (GameFile.objects.all().order_by("folder", "filename", "-game_version").distinct( "folder", "filename")) serializer_class = SimpleGameFileSerializer detail_serializer_class = GameFileSerializer lookup_field = "id" filter_backends = [ DjangoFilterBackend, ] filter_fields = [ "folder", "filename", "file_type", ] ordering = ["-rank", "id"] ordering_fields: list[str] = [] def list(self, request, *args, **kwargs): # noqa A003 """ Retrieves the list of game files from the game Boundless. Requires authentication. Provide `Authorization: Token <token>` header. """ return super().list(request, *args, **kwargs) # pylint: disable=no-member list.operation_id = "listGameFiles" # type: ignore # noqa E501 def retrieve( self, request, *args, **kwargs, ): # pylint: disable=arguments-differ """ Retrieves a decoded game file from the game Boundless. Requires authentication. Provide `Authorization: Token <token>` header. """ return super().retrieve(request, *args, **kwargs) # pylint: disable=no-member retrieve.operation_id = "retrieveGameFile" # type: ignore # noqa E501
class BlockColorViewSet( NestedViewSetMixin, BoundlexxReadOnlyViewSet, ): schema = DescriptiveAutoSchema() queryset = (WorldBlockColor.objects.filter( world__is_creative=False).select_related( "item", "world", "item__item_subtitle", ).prefetch_related( "item__localizedname_set", "item__item_subtitle__localizedname_set", )) serializer_class = BlockColorSerializer lookup_field = "item__game_id" filter_backends = [ DjangoFilterBackend, RankedFuzzySearchFilter, filters.OrderingFilter, DedupedFilter, ] filterset_class = WorldBlockColorFilterSet search_fields = [ "item__string_id", "item__item_subtitle__localizedname__name", "item__localizedname__name", "world__name", "world__display_name", ] ordering = ["-rank", "item__game_id", "color__game_id"] ordering_fields: list[str] = [] def get_queryset(self): queryset = super().get_queryset() if not self.request.user.has_perm("boundless.can_view_private"): queryset = queryset.filter(world__is_public=True) return queryset def list(self, request, *args, **kwargs): # noqa A003 """ Retrieves the list of the items for a given color """ return super().list(request, *args, **kwargs) # pylint: disable=no-member list.operation_id = "listColorBlocks" # type: ignore # noqa E501 def retrieve( self, request, *args, **kwargs, ): # pylint: disable=arguments-differ """ Retrieves the counts worlds for a given color/item combination """ return super().list(request, *args, **kwargs) # pylint: disable=no-member retrieve.operation_id = "retrieveColorBlock" # type: ignore # noqa E501
class DescriptiveAutoSchemaMixin: schema = DescriptiveAutoSchema()
class ForumFormatAPIView(BoundlexxGenericViewSet): """ Generates a Discourse markdown template ready to be posted on the Boundless forums for a world. """ schema = DescriptiveAutoSchema(tags=["misc"]) permission_classes = [AllowAny] serializer_class = ForumFormatPostSerializer response_serializer_class = ForumFormatSerializer authentication_classes: list[Any] = [] def post(self, request): post = self.serializer_class(data=request.data) if post.is_valid() and post.world is not None: if post.world.is_sovereign: extra = { "perms": { "can_visit": post.data["can_visit"], "can_edit": post.data["can_edit"], "can_claim": post.data["can_claim"], }, "compactness": post.data.get("compactness"), "directions": post.data.get("portal_directions"), "username": post.data["username"], "will_renew": post.data["will_renew"], "forum_links": post.data["forum_links"], } else: extra = {} if post.data["update_link"]: params = { "world_id": post.world.id, "update_link": "true", } if post.world.is_sovereign: if post.data["can_visit"] is not None: params["can_visit"] = str( post.data["can_visit"]).lower() if post.data["can_edit"] is not None: params["can_edit"] = str( post.data["can_visit"]).lower() if post.data["can_claim"] is not None: params["can_claim"] = str( post.data["can_claim"]).lower() if post.data["compactness"] is not None: params["compactness"] = str( post.data["can_visit"]).lower() if post.data["portal_directions"] is not None: params["portal_directions"] = post.data[ "portal_directions"] if post.data["username"] is not None: params["username"] = post.data["username"] if post.data["will_renew"] is not None: params["will_renew"] = str( post.data["will_renew"]).lower() extra.update({ "update_link": "https://www.boundlexx.app/tools/forum/?" + urlencode(params) }) title, body = get_response(post.world, extra) return Response({"title": title, "body": body}) return Response(post.errors, status=400) post.operation_id = "createForumTemplate" # type: ignore # noqa E501
class WorldPollViewSet(TimeseriesMixin, NestedViewSetMixin, BoundlexxReadOnlyViewSet): schema = DescriptiveAutoSchema(tags=["worlds", "timeseries"]) queryset = ( WorldPoll.objects.all() .select_related("world") .prefetch_related( "worldpollresult_set", "leaderboardrecord_set", "resourcecount_set", "resourcecount_set__item", ) ) serializer_class = WorldPollSerializer time_bucket_serializer_class = WorldPollTBSerializer number_fields = [ "worldpollresult__player_count", "worldpollresult__beacon_count", "worldpollresult__plot_count", "worldpollresult__total_prestige", ] lookup_field = "id" def list(self, request, *args, **kwargs): # noqa A003 """ Retrieves the list polls avaiable for give World """ return super().list(request, *args, **kwargs) # pylint: disable=no-member def retrieve( self, request, *args, **kwargs, ): # pylint: disable=arguments-differ """ Retrieves a specific poll for a given world Can pass `latest` or `initial` in place of `id` to retrieve the newest or first one """ return super().retrieve(request, *args, **kwargs) # pylint: disable=no-member @action( detail=True, methods=["get"], serializer_class=WorldPollLeaderboardSerializer, ) def leaderboard( self, request, world_id=None, id=None, # pylint: disable=redefined-builtin # noqa A002 ): """ Retrieves the leaderboard for a given world poll result """ world_poll = self.get_object() serializer = self.get_serializer_class()( world_poll, context={"request": request} ) return Response(serializer.data) leaderboard.operation_id = "listWorldPollLeaderboards" @action( detail=True, methods=["get"], serializer_class=WorldPollResourcesSerializer, ) def resources( self, request, world_id=None, id=None, # pylint: disable=redefined-builtin # noqa A002 ): """ Retrieves the count of resources for a given world poll result """ world_poll = self.get_object() serializer = self.get_serializer_class()( world_poll, context={"request": request} ) return Response(serializer.data) resources.operation_id = "listWorldPollResources"