From dc685f1923ffcee8414dc416a53fb0cc7c2862d0 Mon Sep 17 00:00:00 2001 From: Andrew Buss Date: Wed, 13 May 2015 16:07:06 -0700 Subject: [PATCH] Section search works properly now --- flashcards/migrations/0010_auto_20150513_1546.py | 25 ++++++++++++++++++++++++ flashcards/models.py | 24 +++++++++++++++++++++++ flashcards/tests/test_api.py | 4 ++++ flashcards/views.py | 8 +++----- 4 files changed, 56 insertions(+), 5 deletions(-) create mode 100644 flashcards/migrations/0010_auto_20150513_1546.py diff --git a/flashcards/migrations/0010_auto_20150513_1546.py b/flashcards/migrations/0010_auto_20150513_1546.py new file mode 100644 index 0000000..275927a --- /dev/null +++ b/flashcards/migrations/0010_auto_20150513_1546.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('flashcards', '0009_auto_20150512_0318'), + ] + + operations = [ + migrations.AlterField( + model_name='flashcard', + name='material_date', + field=models.DateTimeField(default=django.utils.timezone.now, help_text=b'The date with which the card is associated'), + ), + migrations.AlterField( + model_name='userflashcardquiz', + name='when', + field=models.DateTimeField(auto_now=True), + ), + ] diff --git a/flashcards/models.py b/flashcards/models.py index 2903d27..381f642 100644 --- a/flashcards/models.py +++ b/flashcards/models.py @@ -144,6 +144,7 @@ class Section(Model): """ A UCSD course taught by an instructor during a quarter. We use the term "section" to avoid collision with the builtin keyword "class" + We index gratuitously to support autofill and because this is primarily read-only """ department = CharField(db_index=True, max_length=50) department_abbreviation = CharField(db_index=True, max_length=10) @@ -152,6 +153,29 @@ class Section(Model): instructor = CharField(db_index=True, max_length=100) quarter = CharField(db_index=True, max_length=4) + @classmethod + def search(cls, terms): + """ + Search all fields of all sections for a particular set of terms + A matching section must match at least one field on each term + :param terms:iterable + :return: Matching QuerySet ordered by department and course number + """ + final_q = Q() + for term in terms: + q = Q(department__icontains=term) + q |= Q(department_abbreviation__icontains=term) + q |= Q(course_title__icontains=term) + q |= Q(course_num__icontains=term) + q |= Q(instructor__icontains=term) + final_q &= q + qs = cls.objects.filter(final_q) + # Have the database cast the course number to an integer so it will sort properly + # ECE 35 should rank before ECE 135 even though '135' < '35' lexicographically + qs = qs.extra(select={'course_num_int': 'CAST(course_num AS INTEGER)'}) + qs = qs.order_by('department_abbreviation', 'course_num_int') + return qs + def is_whitelisted(self): """ :return: whether a whitelist exists for this section diff --git a/flashcards/tests/test_api.py b/flashcards/tests/test_api.py index 93f7e6e..329f733 100644 --- a/flashcards/tests/test_api.py +++ b/flashcards/tests/test_api.py @@ -249,3 +249,7 @@ class SectionViewSetTest(APITestCase): def test_section_flashcards(self): response = self.client.get('/api/sections/1/flashcards/') self.assertEqual(response.status_code, HTTP_200_OK) + + def test_section_search(self): + response = self.client.get('/api/sections/search/?q=Kramer') + self.assertEqual(response.status_code, HTTP_200_OK) diff --git a/flashcards/views.py b/flashcards/views.py index 225dd13..9efb521 100644 --- a/flashcards/views.py +++ b/flashcards/views.py @@ -75,11 +75,9 @@ class SectionViewSet(ReadOnlyModelViewSet): @list_route(methods=['get'], permission_classes=[IsAuthenticated]) def search(self, request): - query = request.GET.get('q', '').split(' ') - q = Q() - for word in query: - q |= Q(course_title__icontains=word) - qs = Section.objects.filter(q).distinct() + query = request.GET.get('q',None) + if not query: return Response('[]') + qs = Section.search(query.split(' '))[:5] serializer = SectionSerializer(qs, many=True) return Response(serializer.data) -- 1.9.1