-
Notifications
You must be signed in to change notification settings - Fork 20
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #46 from ernestofgonzalez/search
Add search app
- Loading branch information
Showing
14 changed files
with
312 additions
and
41 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
57 changes: 19 additions & 38 deletions
57
{{ cookiecutter.project_slug }}/requirements/requirements.txt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,49 +1,30 @@ | ||
django==4.1.4 | ||
boto3==1.35.71 | ||
celery[redis]==5.4.0 | ||
celery==5.4.0 | ||
click==8.1.3 | ||
django==5.0.6 | ||
djangorestframework==3.14.0 | ||
django-allauth==0.52.0 | ||
djangorestframework-api-key==3.0.0 | ||
django-celery-beat==2.6.0 | ||
django-cors-headers==3.13.0 | ||
django-countries==7.5 | ||
dj-database-url==0.5.0 | ||
django-jsonform==2.22.0 | ||
django-opensearch-dsl==0.6.2 | ||
django-phonenumber-field==7.0.0 | ||
django-storages==1.13.1 | ||
django-debug-toolbar | ||
factory-boy==3.2.1 | ||
django-browser-reload==1.6.0 | ||
django-compressor==4.1 | ||
django-tailwind==3.4.0 | ||
django-money==3.0.0 | ||
|
||
# Dates | ||
pendulum==2.1.2 | ||
|
||
# CLI | ||
click==8.1.3 | ||
|
||
# Postgres | ||
psycopg2==2.9.3 | ||
|
||
# Tasks | ||
celery[redis]==5.2.7 | ||
celery==5.2.7 | ||
django-celery-beat==2.4.0 | ||
|
||
# Environment variables | ||
python-dotenv==0.15.0 | ||
|
||
# WSGI | ||
django-hijack==3.5.3 | ||
django-json-widget==2.0.1 | ||
dj-database-url==0.5.0 | ||
factory-boy==3.2.1 | ||
google-auth==2.16.0 | ||
gunicorn==20.0.4 | ||
|
||
# Static files | ||
whitenoise==6.2.0 | ||
|
||
# Phone numbers | ||
phonenumbers==8.13.0 | ||
|
||
# Short UUID | ||
psycopg2==2.9.3 | ||
python-dotenv==0.15.0 | ||
sentry-sdk==2.18.0 | ||
serpy==0.3.1 | ||
shortuuid==1.0.11 | ||
|
||
google-auth==2.16.0 | ||
|
||
# Billing | ||
stripe==4.2.0 | ||
whitenoise==6.2.0 |
23 changes: 23 additions & 0 deletions
23
{{ cookiecutter.project_slug }}/src/{{ cookiecutter.project_slug }}/permissions.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
from django.conf import settings | ||
from rest_framework.permissions import SAFE_METHODS, BasePermission | ||
from rest_framework_api_key.models import APIKey | ||
from rest_framework_api_key.permissions import BaseHasAPIKey | ||
|
||
|
||
class AllowAnyInDebug(BasePermission): | ||
def has_permission(self, request, view): | ||
if settings.DEBUG: | ||
return True | ||
return False | ||
|
||
|
||
class IsAdminUserAndReadOnly(BasePermission): | ||
def has_permission(self, request, view): | ||
if request.method not in SAFE_METHODS: | ||
return False | ||
|
||
return request.user and request.user.is_staff | ||
|
||
|
||
class IsConsumerAuthenticated(BaseHasAPIKey): | ||
model = APIKey |
Empty file.
13 changes: 13 additions & 0 deletions
13
{{ cookiecutter.project_slug }}/src/{{ cookiecutter.project_slug }}/search/api_urls.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
from django.urls import path | ||
|
||
from {{cookiecutter.project_slug}}.search import api_views | ||
|
||
app_name = "{{ cookiecutter.project_slug }}-search" | ||
|
||
urlpatterns = [ | ||
path( | ||
"search/", | ||
api_views.search_view, | ||
name="search", | ||
), | ||
] |
27 changes: 27 additions & 0 deletions
27
{{ cookiecutter.project_slug }}/src/{{ cookiecutter.project_slug }}/search/api_views.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
from rest_framework.decorators import api_view, permission_classes | ||
from rest_framework.permissions import IsAuthenticated | ||
from rest_framework.response import Response | ||
from rest_framework.status import HTTP_200_OK | ||
|
||
from {{cookiecutter.project_slug}}.permissions import IsConsumerAuthenticated | ||
from {{cookiecutter.project_slug}}.search.serializers import SearchHitSerializer | ||
from {{cookiecutter.project_slug}}.search.utils import search | ||
|
||
|
||
@api_view(["GET"]) | ||
@permission_classes([IsConsumerAuthenticated, IsAuthenticated]) | ||
def search_view(request): | ||
q = request.GET.get("q", None) | ||
|
||
course = request.GET.get("course", None) | ||
|
||
s = search(q, course_uuid=course) | ||
hits_serializer = SearchHitSerializer(s.hits, many=True) | ||
|
||
return Response( | ||
{ | ||
"total": len(hits_serializer.data), | ||
"results": hits_serializer.data, | ||
}, | ||
status=HTTP_200_OK, | ||
) |
7 changes: 7 additions & 0 deletions
7
{{ cookiecutter.project_slug }}/src/{{ cookiecutter.project_slug }}/search/apps.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
from django.apps import AppConfig | ||
|
||
|
||
class SearchConfig(AppConfig): | ||
default_auto_field = "django.db.models.BigAutoField" | ||
name = "{{ cookiecutter.project_slug }}.search" | ||
label = "{{ cookiecutter.project_slug }}_search" |
26 changes: 26 additions & 0 deletions
26
{{ cookiecutter.project_slug }}/src/{{ cookiecutter.project_slug }}/search/serializers.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import serpy | ||
|
||
from {{cookiecutter.project_slug}}.serializers import Serializer | ||
|
||
|
||
class SearchHitSerializer(Serializer): | ||
id = serpy.Field() | ||
type = serpy.Field() | ||
|
||
def _serialize(self, instance, fields=None): | ||
# Dynamically select the appropriate serializer based on the 'type' field | ||
type_to_serializer = { | ||
# NOTE: include a map from instance.type to respective serializer class | ||
} | ||
|
||
serializer_class = type_to_serializer.get(instance["type"]) | ||
if not serializer_class: | ||
raise ValueError(f"Unknown type: {instance['type']}") | ||
|
||
serialized_data = { | ||
"id": instance["uuid"], | ||
"type": instance["type"], | ||
**serializer_class(instance).data | ||
} | ||
|
||
return serialized_data |
20 changes: 20 additions & 0 deletions
20
{{ cookiecutter.project_slug }}/src/{{ cookiecutter.project_slug }}/search/utils.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
from django_opensearch_dsl.search import Search | ||
|
||
|
||
def search(query, course_uuid=None): | ||
search = Search(index="exercises") | ||
search = search.query( | ||
"multi_match", | ||
query=query, | ||
fields=[ | ||
"content_name^3", | ||
"content_messages_text^2", | ||
"content_messages_text_translation", | ||
], | ||
fuzziness="AUTO", | ||
) | ||
|
||
if course_uuid: | ||
search = search.filter("term", course_uuid=course_uuid) | ||
|
||
return search.execute() |
23 changes: 23 additions & 0 deletions
23
{{ cookiecutter.project_slug }}/src/{{ cookiecutter.project_slug }}/serializer.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import serpy | ||
|
||
|
||
class Serializer(serpy.Serializer): | ||
def __init__(self, *args, **kwargs): | ||
fields = kwargs.pop("fields", None) | ||
super(Serializer, self).__init__(*args, **kwargs) | ||
|
||
# If fields is passed, includes only passed fields | ||
# in the .to_value() step, allowing to skip query/serialize | ||
# only the necessary fields. | ||
if fields is not None: | ||
allowed = set(fields) | ||
existing = set(self._field_map) | ||
for field_name in existing - allowed: | ||
del self._field_map[field_name] | ||
self._compiled_fields = list( | ||
filter(lambda x: x[0] in allowed, self._compiled_fields) | ||
) | ||
|
||
context = kwargs.pop("context", None) | ||
if context is not None: | ||
self.context = context |
Oops, something went wrong.