Skip to content

Commit

Permalink
Merge pull request #15 from daveminer/tag-filters
Browse files Browse the repository at this point in the history
Tag filters
  • Loading branch information
daveminer authored Aug 2, 2024
2 parents cab6ac1 + 9926e20 commit 19252b5
Show file tree
Hide file tree
Showing 7 changed files with 114 additions and 22 deletions.
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,21 @@ curl --request GET \
--header 'Accept: application/json'
```

### Filter sentiments

##### By Tag

A comma-separated list of tags may be specified in the `tags` query parameter.

##### By Age

The `period` query parameter accepts an integer that specifies how many days back to look for sentiments.

### Pagination

The sentiment index controller uses Django's built in [pagination](https://docs.djangoproject.com/en/5.0/topics/pagination/).
The `/sentiment/` path accepts `page_size` and a `page` query parameters.

## Development Environment Setup

The `make services` command will start all of the services besides the app. This allows for the app to be started and stopped (with `make app`) in the terminal for convenience during development.
Expand Down
2 changes: 1 addition & 1 deletion bert_serv/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@

USE_I18N = True

USE_TZ = True
USE_TZ = False


# Static files (CSS, JavaScript, Images)
Expand Down
5 changes: 3 additions & 2 deletions bert_serv/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@
path('admin/', admin.site.urls),
path('callback/', include([
path('sentiment/', include([
path('new/', views.SentimentCallback.as_view()),
path('new', views.SentimentCallback.as_view()),
])),
])),
path('sentiment/', include([
path('', views.SentimentList.as_view()),
path('<int:pk>/', views.SentimentDetail.as_view()),
path('new/', views.SentimentCreate.as_view())
path('new', views.SentimentCreate.as_view()),
]))
]
()
2 changes: 1 addition & 1 deletion sentiment/views/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from .callbackview import *
from .createview import *
from .detailview import *
from .listview import *
from .listview import *
5 changes: 1 addition & 4 deletions sentiment/views/createview.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,13 @@ class SentimentCreate(View):
def post(self, request, *args, **kwargs):
body = parse_request_body(request)

print(body, "BODY")
print(request, "REQ")
try:
text = body.get('text', [])
tags = body.get('tags', [])

print(text, "TEXT")
signature("sentiment.tasks.run_sentiment", args=(
text,tags,), link=callback_task(request)).delay()
print("SENTIMENT TASK")

return HttpResponse(status=201)
except Exception as e:
logging.error(f"Error occurred: {e}")
Expand Down
72 changes: 58 additions & 14 deletions sentiment/views/listview.py
Original file line number Diff line number Diff line change
@@ -1,27 +1,71 @@
from datetime import datetime, timedelta
from django.core.exceptions import BadRequest
from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage
from django.db.models import Q
from django.db.models.query import QuerySet
from django.http import HttpResponse, JsonResponse
from django.http import HttpResponse, JsonResponse, QueryDict
from django.views.generic import ListView
from ..models import Sentiment


class SentimentList(ListView):
model = Sentiment
template_name: str = '../templates/list.html'
paginate_by: int = 10 # Number of items per page
default_page_size: int = 100

def get_queryset(self) -> QuerySet[Sentiment]:
def get_queryset(self, params: QueryDict) -> QuerySet[Sentiment]:
queryset = Sentiment.objects.all().order_by('-created_at')
tag = self.request.GET.get('tag')
if tag:
queryset = queryset.filter(tags__icontains=tag)
tags = params.getlist('tags')
if tags:
query = Q()
for tag in tags:
query |= Q(tags__icontains=tag)
queryset = queryset.filter(query)
period = params.get('period')
if period:
try:
period = int(period)
except ValueError:
raise BadRequest("The 'period' query parameter must be an integer.")
else:
period = 30 # Default period value if not provided

timestamp = datetime.now() - timedelta(days=period)
queryset = queryset.filter(created_at__gte=timestamp)
return queryset

def get(self, request, *args, **kwargs) -> HttpResponse | JsonResponse:
if 'application/json' in request.META.get('HTTP_ACCEPT'):
self.object_list = self.get_queryset()
context = self.get_context_data()
return JsonResponse(list(
context['object_list'].values('created_at', 'label', 'score', 'tags', 'text')
), safe=False)

return super().get(request, *args, **kwargs)
self.object_list = self.get_queryset(self.request.GET)

# Pagination
page_size = request.GET.get('page_size', self.default_page_size)
try:
page_size = int(page_size)
except ValueError:
page_size = self.default_page_size

page = request.GET.get('page')
try:
page = int(page)
except (TypeError, ValueError):
page = 1

paginator = Paginator(self.object_list, page_size)
paginated_queryset = paginator.page(page)

if 'application/json' in request.META.get('HTTP_ACCEPT', ''):
object_list = [
{
'created_at': obj.created_at,
'label': obj.label,
'score': obj.score,
'tags': obj.tags,
'text': obj.text
}
for obj in paginated_queryset.object_list
]

return JsonResponse(object_list, safe=False)

context = self.get_context_data(object_list=paginated_queryset.object_list)
return self.render_to_response(context)
35 changes: 35 additions & 0 deletions sentiment/views/test_listview.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import json
import numpy
import pytest
import random
from django.test import Client, override_settings
from sentiment.models import Sentiment

@pytest.mark.django_db
def test_get(mocker):
client = Client()
__create_sentiment(label='test-label', text='test-text-two', tags=['test-tag'])
__create_sentiment(label='test-label-two', text='test-text-two', tags=['test-tag', 'other-test-tag'])

response = client.get("/sentiment/", {'tags': ['test-tag', 'another-tag'], 'period': 30}, HTTP_ACCEPT='application/json')

assert response.status_code == 200

json = response.json()
assert json[0]['label'] == 'test-label-two'
assert json[0]['score'] < 1.0 and json[0]['score'] > -1.0
assert json[1]['label'] == 'test-label'


def __create_sentiment(label, text, score=None, tags=None):
if score is None:
score = random.uniform(-1.0, 1.0) # Generate a random score between -1.0 and 1.0
if tags is None:
tags = [] # Set tags to an empty list if not provided

Sentiment.objects.create(
label=label,
score=score,
tags=tags,
text=text
)

0 comments on commit 19252b5

Please sign in to comment.