aboutsummaryrefslogtreecommitdiffstats
path: root/api/viewsets.py
blob: 37ba885d8632ab8009ffcac8ef9dcee7a35d10c8 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
from rest_framework.exceptions import ParseError
from rest_framework.mixins import CreateModelMixin, DestroyModelMixin, ListModelMixin, RetrieveModelMixin
from rest_framework.response import Response
from rest_framework.viewsets import GenericViewSet, ViewSet

from .models import DocumentationLink, OffTopicChannelName, SnakeName
from .serializers import DocumentationLinkSerializer, OffTopicChannelNameSerializer, SnakeNameSerializer


class DocumentationLinkViewSet(CreateModelMixin, DestroyModelMixin, ListModelMixin, RetrieveModelMixin, GenericViewSet):
    """
    View providing management of documentation links used in the bot's `Doc` cog.

    ## Routes
    ### GET /bot/documentation-links
    Retrieve all currently stored entries from the database.

    #### Response format
    >>> [
    ...     {
    ...         'package': 'flask',
    ...         'base_url': 'https://flask.pocoo.org/docs/dev',
    ...         'inventory_url': 'https://flask.pocoo.org/docs/objects.inv'
    ...     },
    ...     # ...
    ... ]

    #### Status codes
    - 200: returned on success

    ### GET /bot/documentation-links/<package:str>
    Look up the documentation object for the given `package`.

    #### Response format
    >>> {
    ...     'package': 'flask',
    ...     'base_url': 'https://flask.pocoo.org/docs/dev',
    ...     'inventory_url': 'https://flask.pocoo.org/docs/objects.inv'
    ... }

    #### Status codes
    - 200: returned on success
    - 404: if no entry for the given `package` exists

    ### POST /bot/documentation-links
    Create a new documentation link object.

    #### Body schema
    >>> {
    ...     'package': str,
    ...     'base_url': URL,
    ...     'inventory_url': URL
    ... }

    #### Status codes
    - 201: returned on success
    - 400: if the request body has invalid fields, see the response for details

    ### DELETE /bot/documentation-links/<package:str>
    Delete the entry for the given `package`.

    #### Status codes
    - 204: returned on success
    - 404: if the given `package` could not be found
    """

    queryset = DocumentationLink.objects.all()
    serializer_class = DocumentationLinkSerializer
    lookup_field = 'package'


class OffTopicChannelNameViewSet(ViewSet):
    """
    View of off-topic channel names used by the bot
    to rotate our off-topic names on a daily basis.

    ## Routes
    ### GET /bot/off-topic-channel-names
    Return all known off-topic channel names from the database.
    If the `random_items` query parameter is given, for example using...
        $ curl api.pythondiscord.local:8000/bot/off-topic-channel-names?random_items=5
    ... then the API will return `5` random items from the database.

    #### Response format
    Returns a list of off-topic-channel names:
    >>> [
    ...     "lemons-lemonade-stand",
    ...     "bbq-with-bisk"
    ... ]

    #### Status codes
    - 200: returned on success
    - 400: returned when `random_items` is not a positive integer

    ## Authentication
    Requires a API token.
    """

    serializer_class = OffTopicChannelNameSerializer

    def get_queryset(self):
        return OffTopicChannelName.objects.all()

    def list(self, request):
        if 'random_items' in request.query_params:
            param = request.query_params['random_items']
            try:
                random_count = int(param)
            except ValueError:
                raise ParseError(detail={'random_items': "Must be a valid integer."})

            if random_count <= 0:
                raise ParseError(detail={
                    'random_items': "Must be a positive integer."
                })

            queryset = self.get_queryset().order_by('?')[:random_count]
            serialized = self.serializer_class(queryset, many=True)
            return Response(serialized.data)

        queryset = self.get_queryset()
        serialized = self.serializer_class(queryset, many=True)
        return Response(serialized.data)


class SnakeNameViewSet(ViewSet):
    """
    View providing snake names for the bot's snake cog from our first code jam's winners.

    ## Routes
    ### GET /bot/snake-names
    By default, return a single random snake name along with its name and scientific name.
    If the `get_all` query parameter is given, for example using...
        $ curl api.pythondiscord.local:8000/bot/snake-names?get_all=yes
    ... then the API will return all snake names and scientific names in the database.

    #### Response format
    Without `get_all` query parameter:
    >>> {
    ...     'name': "Python",
    ...     'scientific': "Langus greatus"
    ... }

    If the database is empty for whatever reason, this will return an empty dictionary.

    With `get_all` query parameter:
    >>> [
    ...     {'name': "Python 3", 'scientific': "Langus greatus"},
    ...     {'name': "Python 2", 'scientific': "Langus decentus"}
    ... ]

    #### Status codes
    - 200: returned on success

    ## Authentication
    Requires a API token.
    """

    serializer_class = SnakeNameSerializer

    def get_queryset(self):
        return SnakeName.objects.all()

    def list(self, request):
        if request.query_params.get('get_all'):
            queryset = self.get_queryset()
            serialized = self.serializer_class(queryset, many=True)
            return Response(serialized.data)

        single_snake = SnakeName.objects.order_by('?').first()
        if single_snake is not None:
            body = {
                'name': single_snake.name,
                'scientific': single_snake.scientific
            }

            return Response(body)

        return Response({})