We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
API: add filtering by workshop type in published events
We need to grab DC-only, or SWC-only published events. Probably there's no need to return type of the event in the structure, just filtering.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `api/serializers.py`
Content:
```
1 from rest_framework import serializers
2 
3 from workshops.models import Badge, Airport, Person, Event, TodoItem
4 
5 
6 class PersonUsernameSerializer(serializers.ModelSerializer):
7     name = serializers.CharField(source='get_full_name')
8     user = serializers.CharField(source='username')
9 
10     class Meta:
11         model = Person
12         fields = ('name', 'user', )
13 
14 
15 class PersonNameEmailSerializer(serializers.ModelSerializer):
16     name = serializers.CharField(source='get_full_name')
17 
18     class Meta:
19         model = Person
20         fields = ('name', 'email')
21 
22 
23 class ExportBadgesSerializer(serializers.ModelSerializer):
24     persons = PersonUsernameSerializer(many=True, source='person_set')
25 
26     class Meta:
27         model = Badge
28         fields = ('name', 'persons')
29 
30 
31 class ExportInstructorLocationsSerializer(serializers.ModelSerializer):
32     name = serializers.CharField(source='fullname')
33     instructors = PersonUsernameSerializer(many=True, source='person_set')
34 
35     class Meta:
36         model = Airport
37         fields = ('name', 'latitude', 'longitude', 'instructors', 'country')
38 
39 
40 class EventSerializer(serializers.ModelSerializer):
41     humandate = serializers.SerializerMethodField()
42     country = serializers.CharField()
43     start = serializers.DateField(format=None)
44     end = serializers.DateField(format=None)
45     url = serializers.URLField(source='website_url')
46     eventbrite_id = serializers.CharField(source='reg_key')
47 
48     def get_humandate(self, obj):
49         """Render start and end dates as human-readable short date."""
50         return EventSerializer.human_readable_date(obj.start, obj.end)
51 
52     @staticmethod
53     def human_readable_date(date1, date2):
54         """Render start and end dates as human-readable short date."""
55         if date1 and not date2:
56             return '{:%b %d, %Y}-???'.format(date1)
57         elif date2 and not date1:
58             return '???-{:%b %d, %Y}'.format(date2)
59         elif not date2 and not date1:
60             return '???-???'
61 
62         if date1.year == date2.year:
63             if date1.month == date2.month:
64                 return '{:%b %d}-{:%d, %Y}'.format(date1, date2)
65             else:
66                 return '{:%b %d}-{:%b %d, %Y}'.format(date1, date2)
67         else:
68             return '{:%b %d, %Y}-{:%b %d, %Y}'.format(date1, date2)
69 
70     class Meta:
71         model = Event
72         fields = (
73             'slug', 'start', 'end', 'url', 'humandate', 'contact', 'country',
74             'venue', 'address', 'latitude', 'longitude', 'eventbrite_id',
75         )
76 
77 
78 class TodoSerializer(serializers.ModelSerializer):
79     content = serializers.SerializerMethodField()
80     start = serializers.DateField(format=None, source='due')
81 
82     class Meta:
83         model = TodoItem
84         fields = (
85             'content', 'start',
86         )
87 
88     def get_content(self, obj):
89         """Return HTML containing interesting information for admins.  This
90         will be displayed on labels in the timeline."""
91 
92         return '<a href="{url}">{event}</a><br><small>{todo}</small>'.format(
93             url=obj.event.get_absolute_url(),
94             event=obj.event.get_ident(),
95             todo=obj.title,
96         )
97
```

Path: `api/views.py`
Content:
```
1 import datetime
2 
3 from rest_framework.generics import ListAPIView
4 from rest_framework.metadata import SimpleMetadata
5 from rest_framework.permissions import (
6     IsAuthenticatedOrReadOnly, IsAuthenticated
7 )
8 from rest_framework.response import Response
9 from rest_framework.reverse import reverse
10 from rest_framework.views import APIView
11 
12 from workshops.models import Badge, Airport, Event, TodoItem
13 from workshops.util import get_members, default_membership_cutoff
14 
15 from .serializers import (
16     PersonNameEmailSerializer,
17     ExportBadgesSerializer,
18     ExportInstructorLocationsSerializer,
19     EventSerializer,
20     TodoSerializer,
21 )
22 
23 
24 class QueryMetadata(SimpleMetadata):
25     """Additionally include info about query parameters."""
26 
27     def determine_metadata(self, request, view):
28         data = super().determine_metadata(request, view)
29 
30         try:
31             data['query_params'] = view.get_query_params_description()
32         except AttributeError:
33             pass
34 
35         return data
36 
37 
38 class ApiRoot(APIView):
39     def get(self, request, format=None):
40         return Response({
41             'export-badges': reverse('api:export-badges', request=request,
42                                      format=format),
43             'export-instructors': reverse('api:export-instructors',
44                                           request=request, format=format),
45             'export-members': reverse('api:export-members', request=request,
46                                       format=format),
47             'events-published': reverse('api:events-published',
48                                         request=request, format=format),
49             'user-todos': reverse('api:user-todos',
50                                   request=request, format=format),
51         })
52 
53 
54 class ExportBadgesView(ListAPIView):
55     """List all badges and people who have them."""
56     permission_classes = (IsAuthenticatedOrReadOnly, )
57     paginator = None  # disable pagination
58 
59     queryset = Badge.objects.prefetch_related('person_set')
60     serializer_class = ExportBadgesSerializer
61 
62 
63 class ExportInstructorLocationsView(ListAPIView):
64     """List all airports and instructors located near them."""
65     permission_classes = (IsAuthenticatedOrReadOnly, )
66     paginator = None  # disable pagination
67 
68     queryset = Airport.objects.exclude(person=None) \
69                               .prefetch_related('person_set')
70     serializer_class = ExportInstructorLocationsSerializer
71 
72 
73 class ExportMembersView(ListAPIView):
74     """Show everyone who qualifies as an SCF member."""
75     permission_classes = (IsAuthenticatedOrReadOnly, )
76     paginator = None  # disable pagination
77 
78     serializer_class = PersonNameEmailSerializer
79 
80     def get_queryset(self):
81         earliest_default, latest_default = default_membership_cutoff()
82 
83         earliest = self.request.query_params.get('earliest', None)
84         if earliest is not None:
85             try:
86                 earliest = datetime.datetime.strptime(earliest, '%Y-%m-%d') \
87                                             .date()
88             except ValueError:
89                 earliest = earliest_default
90         else:
91             earliest = earliest_default
92 
93         latest = self.request.query_params.get('latest', None)
94         if latest is not None:
95             try:
96                 latest = datetime.datetime.strptime(latest, '%Y-%m-%d').date()
97             except ValueError:
98                 latest = latest_default
99         else:
100             latest = latest_default
101 
102         return get_members(earliest, latest)
103 
104     def get_query_params_description(self):
105         return {
106             'earliest': 'Date of earliest workshop someone taught at.'
107                         '  Defaults to -2*365 days from current date.',
108             'latest': 'Date of latest workshop someone taught at.'
109                       '  Defaults to current date.',
110         }
111 
112 
113 class PublishedEvents(ListAPIView):
114     """List published events."""
115 
116     # only events that have both a starting date and a URL
117     permission_classes = (IsAuthenticatedOrReadOnly, )
118     paginator = None  # disable pagination
119 
120     serializer_class = EventSerializer
121 
122     metadata_class = QueryMetadata
123 
124     def get_queryset(self):
125         """Optionally restrict the returned event set to events hosted by
126         specific host or administered by specific admin."""
127         queryset = Event.objects.published_events()
128 
129         administrator = self.request.query_params.get('administrator', None)
130         if administrator is not None:
131             queryset = queryset.filter(administrator__pk=administrator)
132 
133         host = self.request.query_params.get('host', None)
134         if host is not None:
135             queryset = queryset.filter(host__pk=host)
136 
137         return queryset
138 
139     def get_query_params_description(self):
140         return {
141             'administrator': 'ID of the organization responsible for admin '
142                              'work on events.',
143             'host': 'ID of the organization hosting the event.',
144         }
145 
146 
147 class UserTodoItems(ListAPIView):
148     permission_classes = (IsAuthenticated, )
149     paginator = None
150     serializer_class = TodoSerializer
151 
152     def get_queryset(self):
153         """Return current TODOs for currently logged in user."""
154         return TodoItem.objects.user(self.request.user) \
155                                .incomplete() \
156                                .select_related('event')
157
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/api/serializers.py b/api/serializers.py
--- a/api/serializers.py
+++ b/api/serializers.py
@@ -1,6 +1,6 @@
 from rest_framework import serializers
 
-from workshops.models import Badge, Airport, Person, Event, TodoItem
+from workshops.models import Badge, Airport, Person, Event, TodoItem, Tag
 
 
 class PersonUsernameSerializer(serializers.ModelSerializer):
@@ -37,6 +37,12 @@
         fields = ('name', 'latitude', 'longitude', 'instructors', 'country')
 
 
+class TagSerializer(serializers.ModelSerializer):
+    class Meta:
+        model = Tag
+        fields = ('name', )
+
+
 class EventSerializer(serializers.ModelSerializer):
     humandate = serializers.SerializerMethodField()
     country = serializers.CharField()
@@ -44,6 +50,7 @@
     end = serializers.DateField(format=None)
     url = serializers.URLField(source='website_url')
     eventbrite_id = serializers.CharField(source='reg_key')
+    tags = TagSerializer(many=True)
 
     def get_humandate(self, obj):
         """Render start and end dates as human-readable short date."""
@@ -72,6 +79,7 @@
         fields = (
             'slug', 'start', 'end', 'url', 'humandate', 'contact', 'country',
             'venue', 'address', 'latitude', 'longitude', 'eventbrite_id',
+            'tags',
         )
 
 
diff --git a/api/views.py b/api/views.py
--- a/api/views.py
+++ b/api/views.py
@@ -1,5 +1,6 @@
 import datetime
 
+from django.db.models import Q
 from rest_framework.generics import ListAPIView
 from rest_framework.metadata import SimpleMetadata
 from rest_framework.permissions import (
@@ -9,7 +10,7 @@
 from rest_framework.reverse import reverse
 from rest_framework.views import APIView
 
-from workshops.models import Badge, Airport, Event, TodoItem
+from workshops.models import Badge, Airport, Event, TodoItem, Tag
 from workshops.util import get_members, default_membership_cutoff
 
 from .serializers import (
@@ -134,6 +135,12 @@
         if host is not None:
             queryset = queryset.filter(host__pk=host)
 
+        tags = self.request.query_params.getlist('tag', None)
+        if tags:
+            tags = Tag.objects.filter(name__in=tags)
+            for tag in tags:
+                queryset = queryset.filter(tags=tag)
+
         return queryset
 
     def get_query_params_description(self):
@@ -141,6 +148,8 @@
             'administrator': 'ID of the organization responsible for admin '
                              'work on events.',
             'host': 'ID of the organization hosting the event.',
+            'tag': "Events' tag(s). You can use this parameter multiple "
+                   "times.",
         }

{"golden_diff": "diff --git a/api/serializers.py b/api/serializers.py\n--- a/api/serializers.py\n+++ b/api/serializers.py\n@@ -1,6 +1,6 @@\n from rest_framework import serializers\n \n-from workshops.models import Badge, Airport, Person, Event, TodoItem\n+from workshops.models import Badge, Airport, Person, Event, TodoItem, Tag\n \n \n class PersonUsernameSerializer(serializers.ModelSerializer):\n@@ -37,6 +37,12 @@\n         fields = ('name', 'latitude', 'longitude', 'instructors', 'country')\n \n \n+class TagSerializer(serializers.ModelSerializer):\n+    class Meta:\n+        model = Tag\n+        fields = ('name', )\n+\n+\n class EventSerializer(serializers.ModelSerializer):\n     humandate = serializers.SerializerMethodField()\n     country = serializers.CharField()\n@@ -44,6 +50,7 @@\n     end = serializers.DateField(format=None)\n     url = serializers.URLField(source='website_url')\n     eventbrite_id = serializers.CharField(source='reg_key')\n+    tags = TagSerializer(many=True)\n \n     def get_humandate(self, obj):\n         \"\"\"Render start and end dates as human-readable short date.\"\"\"\n@@ -72,6 +79,7 @@\n         fields = (\n             'slug', 'start', 'end', 'url', 'humandate', 'contact', 'country',\n             'venue', 'address', 'latitude', 'longitude', 'eventbrite_id',\n+            'tags',\n         )\n \n \ndiff --git a/api/views.py b/api/views.py\n--- a/api/views.py\n+++ b/api/views.py\n@@ -1,5 +1,6 @@\n import datetime\n \n+from django.db.models import Q\n from rest_framework.generics import ListAPIView\n from rest_framework.metadata import SimpleMetadata\n from rest_framework.permissions import (\n@@ -9,7 +10,7 @@\n from rest_framework.reverse import reverse\n from rest_framework.views import APIView\n \n-from workshops.models import Badge, Airport, Event, TodoItem\n+from workshops.models import Badge, Airport, Event, TodoItem, Tag\n from workshops.util import get_members, default_membership_cutoff\n \n from .serializers import (\n@@ -134,6 +135,12 @@\n         if host is not None:\n             queryset = queryset.filter(host__pk=host)\n \n+        tags = self.request.query_params.getlist('tag', None)\n+        if tags:\n+            tags = Tag.objects.filter(name__in=tags)\n+            for tag in tags:\n+                queryset = queryset.filter(tags=tag)\n+\n         return queryset\n \n     def get_query_params_description(self):\n@@ -141,6 +148,8 @@\n             'administrator': 'ID of the organization responsible for admin '\n                              'work on events.',\n             'host': 'ID of the organization hosting the event.',\n+            'tag': \"Events' tag(s). You can use this parameter multiple \"\n+                   \"times.\",\n         }\n", "issue": "API: add filtering by workshop type in published events\nWe need to grab DC-only, or SWC-only published events. Probably there's no need to return type of the event in the structure, just filtering.\n\n", "before_files": [{"content": "from rest_framework import serializers\n\nfrom workshops.models import Badge, Airport, Person, Event, TodoItem\n\n\nclass PersonUsernameSerializer(serializers.ModelSerializer):\n    name = serializers.CharField(source='get_full_name')\n    user = serializers.CharField(source='username')\n\n    class Meta:\n        model = Person\n        fields = ('name', 'user', )\n\n\nclass PersonNameEmailSerializer(serializers.ModelSerializer):\n    name = serializers.CharField(source='get_full_name')\n\n    class Meta:\n        model = Person\n        fields = ('name', 'email')\n\n\nclass ExportBadgesSerializer(serializers.ModelSerializer):\n    persons = PersonUsernameSerializer(many=True, source='person_set')\n\n    class Meta:\n        model = Badge\n        fields = ('name', 'persons')\n\n\nclass ExportInstructorLocationsSerializer(serializers.ModelSerializer):\n    name = serializers.CharField(source='fullname')\n    instructors = PersonUsernameSerializer(many=True, source='person_set')\n\n    class Meta:\n        model = Airport\n        fields = ('name', 'latitude', 'longitude', 'instructors', 'country')\n\n\nclass EventSerializer(serializers.ModelSerializer):\n    humandate = serializers.SerializerMethodField()\n    country = serializers.CharField()\n    start = serializers.DateField(format=None)\n    end = serializers.DateField(format=None)\n    url = serializers.URLField(source='website_url')\n    eventbrite_id = serializers.CharField(source='reg_key')\n\n    def get_humandate(self, obj):\n        \"\"\"Render start and end dates as human-readable short date.\"\"\"\n        return EventSerializer.human_readable_date(obj.start, obj.end)\n\n    @staticmethod\n    def human_readable_date(date1, date2):\n        \"\"\"Render start and end dates as human-readable short date.\"\"\"\n        if date1 and not date2:\n            return '{:%b %d, %Y}-???'.format(date1)\n        elif date2 and not date1:\n            return '???-{:%b %d, %Y}'.format(date2)\n        elif not date2 and not date1:\n            return '???-???'\n\n        if date1.year == date2.year:\n            if date1.month == date2.month:\n                return '{:%b %d}-{:%d, %Y}'.format(date1, date2)\n            else:\n                return '{:%b %d}-{:%b %d, %Y}'.format(date1, date2)\n        else:\n            return '{:%b %d, %Y}-{:%b %d, %Y}'.format(date1, date2)\n\n    class Meta:\n        model = Event\n        fields = (\n            'slug', 'start', 'end', 'url', 'humandate', 'contact', 'country',\n            'venue', 'address', 'latitude', 'longitude', 'eventbrite_id',\n        )\n\n\nclass TodoSerializer(serializers.ModelSerializer):\n    content = serializers.SerializerMethodField()\n    start = serializers.DateField(format=None, source='due')\n\n    class Meta:\n        model = TodoItem\n        fields = (\n            'content', 'start',\n        )\n\n    def get_content(self, obj):\n        \"\"\"Return HTML containing interesting information for admins.  This\n        will be displayed on labels in the timeline.\"\"\"\n\n        return '<a href=\"{url}\">{event}</a><br><small>{todo}</small>'.format(\n            url=obj.event.get_absolute_url(),\n            event=obj.event.get_ident(),\n            todo=obj.title,\n        )\n", "path": "api/serializers.py"}, {"content": "import datetime\n\nfrom rest_framework.generics import ListAPIView\nfrom rest_framework.metadata import SimpleMetadata\nfrom rest_framework.permissions import (\n    IsAuthenticatedOrReadOnly, IsAuthenticated\n)\nfrom rest_framework.response import Response\nfrom rest_framework.reverse import reverse\nfrom rest_framework.views import APIView\n\nfrom workshops.models import Badge, Airport, Event, TodoItem\nfrom workshops.util import get_members, default_membership_cutoff\n\nfrom .serializers import (\n    PersonNameEmailSerializer,\n    ExportBadgesSerializer,\n    ExportInstructorLocationsSerializer,\n    EventSerializer,\n    TodoSerializer,\n)\n\n\nclass QueryMetadata(SimpleMetadata):\n    \"\"\"Additionally include info about query parameters.\"\"\"\n\n    def determine_metadata(self, request, view):\n        data = super().determine_metadata(request, view)\n\n        try:\n            data['query_params'] = view.get_query_params_description()\n        except AttributeError:\n            pass\n\n        return data\n\n\nclass ApiRoot(APIView):\n    def get(self, request, format=None):\n        return Response({\n            'export-badges': reverse('api:export-badges', request=request,\n                                     format=format),\n            'export-instructors': reverse('api:export-instructors',\n                                          request=request, format=format),\n            'export-members': reverse('api:export-members', request=request,\n                                      format=format),\n            'events-published': reverse('api:events-published',\n                                        request=request, format=format),\n            'user-todos': reverse('api:user-todos',\n                                  request=request, format=format),\n        })\n\n\nclass ExportBadgesView(ListAPIView):\n    \"\"\"List all badges and people who have them.\"\"\"\n    permission_classes = (IsAuthenticatedOrReadOnly, )\n    paginator = None  # disable pagination\n\n    queryset = Badge.objects.prefetch_related('person_set')\n    serializer_class = ExportBadgesSerializer\n\n\nclass ExportInstructorLocationsView(ListAPIView):\n    \"\"\"List all airports and instructors located near them.\"\"\"\n    permission_classes = (IsAuthenticatedOrReadOnly, )\n    paginator = None  # disable pagination\n\n    queryset = Airport.objects.exclude(person=None) \\\n                              .prefetch_related('person_set')\n    serializer_class = ExportInstructorLocationsSerializer\n\n\nclass ExportMembersView(ListAPIView):\n    \"\"\"Show everyone who qualifies as an SCF member.\"\"\"\n    permission_classes = (IsAuthenticatedOrReadOnly, )\n    paginator = None  # disable pagination\n\n    serializer_class = PersonNameEmailSerializer\n\n    def get_queryset(self):\n        earliest_default, latest_default = default_membership_cutoff()\n\n        earliest = self.request.query_params.get('earliest', None)\n        if earliest is not None:\n            try:\n                earliest = datetime.datetime.strptime(earliest, '%Y-%m-%d') \\\n                                            .date()\n            except ValueError:\n                earliest = earliest_default\n        else:\n            earliest = earliest_default\n\n        latest = self.request.query_params.get('latest', None)\n        if latest is not None:\n            try:\n                latest = datetime.datetime.strptime(latest, '%Y-%m-%d').date()\n            except ValueError:\n                latest = latest_default\n        else:\n            latest = latest_default\n\n        return get_members(earliest, latest)\n\n    def get_query_params_description(self):\n        return {\n            'earliest': 'Date of earliest workshop someone taught at.'\n                        '  Defaults to -2*365 days from current date.',\n            'latest': 'Date of latest workshop someone taught at.'\n                      '  Defaults to current date.',\n        }\n\n\nclass PublishedEvents(ListAPIView):\n    \"\"\"List published events.\"\"\"\n\n    # only events that have both a starting date and a URL\n    permission_classes = (IsAuthenticatedOrReadOnly, )\n    paginator = None  # disable pagination\n\n    serializer_class = EventSerializer\n\n    metadata_class = QueryMetadata\n\n    def get_queryset(self):\n        \"\"\"Optionally restrict the returned event set to events hosted by\n        specific host or administered by specific admin.\"\"\"\n        queryset = Event.objects.published_events()\n\n        administrator = self.request.query_params.get('administrator', None)\n        if administrator is not None:\n            queryset = queryset.filter(administrator__pk=administrator)\n\n        host = self.request.query_params.get('host', None)\n        if host is not None:\n            queryset = queryset.filter(host__pk=host)\n\n        return queryset\n\n    def get_query_params_description(self):\n        return {\n            'administrator': 'ID of the organization responsible for admin '\n                             'work on events.',\n            'host': 'ID of the organization hosting the event.',\n        }\n\n\nclass UserTodoItems(ListAPIView):\n    permission_classes = (IsAuthenticated, )\n    paginator = None\n    serializer_class = TodoSerializer\n\n    def get_queryset(self):\n        \"\"\"Return current TODOs for currently logged in user.\"\"\"\n        return TodoItem.objects.user(self.request.user) \\\n                               .incomplete() \\\n                               .select_related('event')\n", "path": "api/views.py"}], "after_files": [{"content": "from rest_framework import serializers\n\nfrom workshops.models import Badge, Airport, Person, Event, TodoItem, Tag\n\n\nclass PersonUsernameSerializer(serializers.ModelSerializer):\n    name = serializers.CharField(source='get_full_name')\n    user = serializers.CharField(source='username')\n\n    class Meta:\n        model = Person\n        fields = ('name', 'user', )\n\n\nclass PersonNameEmailSerializer(serializers.ModelSerializer):\n    name = serializers.CharField(source='get_full_name')\n\n    class Meta:\n        model = Person\n        fields = ('name', 'email')\n\n\nclass ExportBadgesSerializer(serializers.ModelSerializer):\n    persons = PersonUsernameSerializer(many=True, source='person_set')\n\n    class Meta:\n        model = Badge\n        fields = ('name', 'persons')\n\n\nclass ExportInstructorLocationsSerializer(serializers.ModelSerializer):\n    name = serializers.CharField(source='fullname')\n    instructors = PersonUsernameSerializer(many=True, source='person_set')\n\n    class Meta:\n        model = Airport\n        fields = ('name', 'latitude', 'longitude', 'instructors', 'country')\n\n\nclass TagSerializer(serializers.ModelSerializer):\n    class Meta:\n        model = Tag\n        fields = ('name', )\n\n\nclass EventSerializer(serializers.ModelSerializer):\n    humandate = serializers.SerializerMethodField()\n    country = serializers.CharField()\n    start = serializers.DateField(format=None)\n    end = serializers.DateField(format=None)\n    url = serializers.URLField(source='website_url')\n    eventbrite_id = serializers.CharField(source='reg_key')\n    tags = TagSerializer(many=True)\n\n    def get_humandate(self, obj):\n        \"\"\"Render start and end dates as human-readable short date.\"\"\"\n        return EventSerializer.human_readable_date(obj.start, obj.end)\n\n    @staticmethod\n    def human_readable_date(date1, date2):\n        \"\"\"Render start and end dates as human-readable short date.\"\"\"\n        if date1 and not date2:\n            return '{:%b %d, %Y}-???'.format(date1)\n        elif date2 and not date1:\n            return '???-{:%b %d, %Y}'.format(date2)\n        elif not date2 and not date1:\n            return '???-???'\n\n        if date1.year == date2.year:\n            if date1.month == date2.month:\n                return '{:%b %d}-{:%d, %Y}'.format(date1, date2)\n            else:\n                return '{:%b %d}-{:%b %d, %Y}'.format(date1, date2)\n        else:\n            return '{:%b %d, %Y}-{:%b %d, %Y}'.format(date1, date2)\n\n    class Meta:\n        model = Event\n        fields = (\n            'slug', 'start', 'end', 'url', 'humandate', 'contact', 'country',\n            'venue', 'address', 'latitude', 'longitude', 'eventbrite_id',\n            'tags',\n        )\n\n\nclass TodoSerializer(serializers.ModelSerializer):\n    content = serializers.SerializerMethodField()\n    start = serializers.DateField(format=None, source='due')\n\n    class Meta:\n        model = TodoItem\n        fields = (\n            'content', 'start',\n        )\n\n    def get_content(self, obj):\n        \"\"\"Return HTML containing interesting information for admins.  This\n        will be displayed on labels in the timeline.\"\"\"\n\n        return '<a href=\"{url}\">{event}</a><br><small>{todo}</small>'.format(\n            url=obj.event.get_absolute_url(),\n            event=obj.event.get_ident(),\n            todo=obj.title,\n        )\n", "path": "api/serializers.py"}, {"content": "import datetime\n\nfrom django.db.models import Q\nfrom rest_framework.generics import ListAPIView\nfrom rest_framework.metadata import SimpleMetadata\nfrom rest_framework.permissions import (\n    IsAuthenticatedOrReadOnly, IsAuthenticated\n)\nfrom rest_framework.response import Response\nfrom rest_framework.reverse import reverse\nfrom rest_framework.views import APIView\n\nfrom workshops.models import Badge, Airport, Event, TodoItem, Tag\nfrom workshops.util import get_members, default_membership_cutoff\n\nfrom .serializers import (\n    PersonNameEmailSerializer,\n    ExportBadgesSerializer,\n    ExportInstructorLocationsSerializer,\n    EventSerializer,\n    TodoSerializer,\n)\n\n\nclass QueryMetadata(SimpleMetadata):\n    \"\"\"Additionally include info about query parameters.\"\"\"\n\n    def determine_metadata(self, request, view):\n        data = super().determine_metadata(request, view)\n\n        try:\n            data['query_params'] = view.get_query_params_description()\n        except AttributeError:\n            pass\n\n        return data\n\n\nclass ApiRoot(APIView):\n    def get(self, request, format=None):\n        return Response({\n            'export-badges': reverse('api:export-badges', request=request,\n                                     format=format),\n            'export-instructors': reverse('api:export-instructors',\n                                          request=request, format=format),\n            'export-members': reverse('api:export-members', request=request,\n                                      format=format),\n            'events-published': reverse('api:events-published',\n                                        request=request, format=format),\n            'user-todos': reverse('api:user-todos',\n                                  request=request, format=format),\n        })\n\n\nclass ExportBadgesView(ListAPIView):\n    \"\"\"List all badges and people who have them.\"\"\"\n    permission_classes = (IsAuthenticatedOrReadOnly, )\n    paginator = None  # disable pagination\n\n    queryset = Badge.objects.prefetch_related('person_set')\n    serializer_class = ExportBadgesSerializer\n\n\nclass ExportInstructorLocationsView(ListAPIView):\n    \"\"\"List all airports and instructors located near them.\"\"\"\n    permission_classes = (IsAuthenticatedOrReadOnly, )\n    paginator = None  # disable pagination\n\n    queryset = Airport.objects.exclude(person=None) \\\n                              .prefetch_related('person_set')\n    serializer_class = ExportInstructorLocationsSerializer\n\n\nclass ExportMembersView(ListAPIView):\n    \"\"\"Show everyone who qualifies as an SCF member.\"\"\"\n    permission_classes = (IsAuthenticatedOrReadOnly, )\n    paginator = None  # disable pagination\n\n    serializer_class = PersonNameEmailSerializer\n\n    def get_queryset(self):\n        earliest_default, latest_default = default_membership_cutoff()\n\n        earliest = self.request.query_params.get('earliest', None)\n        if earliest is not None:\n            try:\n                earliest = datetime.datetime.strptime(earliest, '%Y-%m-%d') \\\n                                            .date()\n            except ValueError:\n                earliest = earliest_default\n        else:\n            earliest = earliest_default\n\n        latest = self.request.query_params.get('latest', None)\n        if latest is not None:\n            try:\n                latest = datetime.datetime.strptime(latest, '%Y-%m-%d').date()\n            except ValueError:\n                latest = latest_default\n        else:\n            latest = latest_default\n\n        return get_members(earliest, latest)\n\n    def get_query_params_description(self):\n        return {\n            'earliest': 'Date of earliest workshop someone taught at.'\n                        '  Defaults to -2*365 days from current date.',\n            'latest': 'Date of latest workshop someone taught at.'\n                      '  Defaults to current date.',\n        }\n\n\nclass PublishedEvents(ListAPIView):\n    \"\"\"List published events.\"\"\"\n\n    # only events that have both a starting date and a URL\n    permission_classes = (IsAuthenticatedOrReadOnly, )\n    paginator = None  # disable pagination\n\n    serializer_class = EventSerializer\n\n    metadata_class = QueryMetadata\n\n    def get_queryset(self):\n        \"\"\"Optionally restrict the returned event set to events hosted by\n        specific host or administered by specific admin.\"\"\"\n        queryset = Event.objects.published_events()\n\n        administrator = self.request.query_params.get('administrator', None)\n        if administrator is not None:\n            queryset = queryset.filter(administrator__pk=administrator)\n\n        host = self.request.query_params.get('host', None)\n        if host is not None:\n            queryset = queryset.filter(host__pk=host)\n\n        tags = self.request.query_params.getlist('tag', None)\n        if tags:\n            tags = Tag.objects.filter(name__in=tags)\n            for tag in tags:\n                queryset = queryset.filter(tags=tag)\n\n        return queryset\n\n    def get_query_params_description(self):\n        return {\n            'administrator': 'ID of the organization responsible for admin '\n                             'work on events.',\n            'host': 'ID of the organization hosting the event.',\n            'tag': \"Events' tag(s). You can use this parameter multiple \"\n                   \"times.\",\n        }\n\n\nclass UserTodoItems(ListAPIView):\n    permission_classes = (IsAuthenticated, )\n    paginator = None\n    serializer_class = TodoSerializer\n\n    def get_queryset(self):\n        \"\"\"Return current TODOs for currently logged in user.\"\"\"\n        return TodoItem.objects.user(self.request.user) \\\n                               .incomplete() \\\n                               .select_related('event')\n", "path": "api/views.py"}]}