Commit dc685f1923ffcee8414dc416a53fb0cc7c2862d0
1 parent
c1f4d3dea4
Exists in
master
Section search works properly now
Showing 4 changed files with 56 additions and 5 deletions Side-by-side Diff
flashcards/migrations/0010_auto_20150513_1546.py
View file @
dc685f1
1 | +# -*- coding: utf-8 -*- | |
2 | +from __future__ import unicode_literals | |
3 | + | |
4 | +from django.db import models, migrations | |
5 | +import django.utils.timezone | |
6 | + | |
7 | + | |
8 | +class Migration(migrations.Migration): | |
9 | + | |
10 | + dependencies = [ | |
11 | + ('flashcards', '0009_auto_20150512_0318'), | |
12 | + ] | |
13 | + | |
14 | + operations = [ | |
15 | + migrations.AlterField( | |
16 | + model_name='flashcard', | |
17 | + name='material_date', | |
18 | + field=models.DateTimeField(default=django.utils.timezone.now, help_text=b'The date with which the card is associated'), | |
19 | + ), | |
20 | + migrations.AlterField( | |
21 | + model_name='userflashcardquiz', | |
22 | + name='when', | |
23 | + field=models.DateTimeField(auto_now=True), | |
24 | + ), | |
25 | + ] |
flashcards/models.py
View file @
dc685f1
... | ... | @@ -144,6 +144,7 @@ |
144 | 144 | """ |
145 | 145 | A UCSD course taught by an instructor during a quarter. |
146 | 146 | We use the term "section" to avoid collision with the builtin keyword "class" |
147 | + We index gratuitously to support autofill and because this is primarily read-only | |
147 | 148 | """ |
148 | 149 | department = CharField(db_index=True, max_length=50) |
149 | 150 | department_abbreviation = CharField(db_index=True, max_length=10) |
... | ... | @@ -151,6 +152,29 @@ |
151 | 152 | course_title = CharField(db_index=True, max_length=50) |
152 | 153 | instructor = CharField(db_index=True, max_length=100) |
153 | 154 | quarter = CharField(db_index=True, max_length=4) |
155 | + | |
156 | + @classmethod | |
157 | + def search(cls, terms): | |
158 | + """ | |
159 | + Search all fields of all sections for a particular set of terms | |
160 | + A matching section must match at least one field on each term | |
161 | + :param terms:iterable | |
162 | + :return: Matching QuerySet ordered by department and course number | |
163 | + """ | |
164 | + final_q = Q() | |
165 | + for term in terms: | |
166 | + q = Q(department__icontains=term) | |
167 | + q |= Q(department_abbreviation__icontains=term) | |
168 | + q |= Q(course_title__icontains=term) | |
169 | + q |= Q(course_num__icontains=term) | |
170 | + q |= Q(instructor__icontains=term) | |
171 | + final_q &= q | |
172 | + qs = cls.objects.filter(final_q) | |
173 | + # Have the database cast the course number to an integer so it will sort properly | |
174 | + # ECE 35 should rank before ECE 135 even though '135' < '35' lexicographically | |
175 | + qs = qs.extra(select={'course_num_int': 'CAST(course_num AS INTEGER)'}) | |
176 | + qs = qs.order_by('department_abbreviation', 'course_num_int') | |
177 | + return qs | |
154 | 178 | |
155 | 179 | def is_whitelisted(self): |
156 | 180 | """ |
flashcards/tests/test_api.py
View file @
dc685f1
... | ... | @@ -249,4 +249,8 @@ |
249 | 249 | def test_section_flashcards(self): |
250 | 250 | response = self.client.get('/api/sections/1/flashcards/') |
251 | 251 | self.assertEqual(response.status_code, HTTP_200_OK) |
252 | + | |
253 | + def test_section_search(self): | |
254 | + response = self.client.get('/api/sections/search/?q=Kramer') | |
255 | + self.assertEqual(response.status_code, HTTP_200_OK) |
flashcards/views.py
View file @
dc685f1
... | ... | @@ -75,11 +75,9 @@ |
75 | 75 | |
76 | 76 | @list_route(methods=['get'], permission_classes=[IsAuthenticated]) |
77 | 77 | def search(self, request): |
78 | - query = request.GET.get('q', '').split(' ') | |
79 | - q = Q() | |
80 | - for word in query: | |
81 | - q |= Q(course_title__icontains=word) | |
82 | - qs = Section.objects.filter(q).distinct() | |
78 | + query = request.GET.get('q',None) | |
79 | + if not query: return Response('[]') | |
80 | + qs = Section.search(query.split(' '))[:5] | |
83 | 81 | serializer = SectionSerializer(qs, many=True) |
84 | 82 | return Response(serializer.data) |
85 | 83 |