Post Story API - bounswe/2021SpringGroup9 GitHub Wiki
-
Story: A model in the database. It is a model for user's stories that they post in our app. Its fields are:
- id: Story's id to be kept in the database.
- title: Story's title with maximum char length 200
- story: Story's content with maximum char length 1000
- name: The name of the user who posted the story.
- longitude: Story's location's longitude.
- latitude: Story's location's latitude.
- location: Story's location name with maximum char length 200.
- tag: Story's tag with maximum char length 200,
- date: Story's post date.
- notifyAdmin: This shows if a story needs to notify the admin or not.
- story_id: Story id as in database.
- Tisane API: An API that does sentiment analysis given some text. Returns a result depicting if it finds abusive content or not.
Post Story API is used with a POST request to post some story into the database.
It does sentiment analysis on the story text that was posted using the Tisane API. If there was abusive content it will set the notify_admin flag.
As a response, it will return the contents of the resulting story.
Also, flagged stories can be get with a GET request on http://3.129.194.233/api/flagged_stories.
API's base URL is: http://3.129.194.233/.
For posting a story: Send a POST request on http://3.129.194.233/api/storypost/ , request body should contain a JSON object with title, story, name, location and tag fields.
For finding flagged stories: Send a GET request on http://3.129.194.233/api/flagged_stories/, no need for parameters.
/api/storypost/ will return the contents of the created post on success. On unsuccessful operation (like wrong input format or empty location) it will return HTTP 400(Bad request).
/api/flagged_stories/ will return a list of story ids, titles and texts that contain flagged text.
Post a story into the DB (sentiment analysis will be done on it.)
Request:
POST
http://3.129.194.233/api/storypost/
Request body:
{
"title": Title of the post.,
"story":Content text of the post,
"name": Name of the user who posted it.,
"location": Location that the story was posted on.,
"tag":Tag of the story.
}
Response (on success):
{
"id": Story['id'],
"date": Story['date'],
"name": Story['name'],
"location": Story['location'],
"tag": Story['tag'],
"title": Story['title'],
"story": Story['story'],
"notifyAdmin": Story['notifyAdmin'],
"latitude": Story['latitude'],
"longitude": Story['longitude']
}
Request:
GET
http://3.129.194.233/api/flagged_stories/
Parameters:
None
Response (on success): Essentially, an array of story id, title and text of flagged stories.
{
'flagged_stories': [
{'id':Story['id'],
'title': Story['title'],
'story': Story['story']} ...]
}
Response:
{"flagged_stories": [{"id": 22, "title": "About my School", "story": "This is my school. There are many like it, but this one is mine.\r\nMy school is my best friend. It is my life. I must master it as I must master my life.\r\nWithout me, my school is useless. Without my school, I am useless. So... I will beat anyone who doesn't like my school (in a not literal sense)."}]}
Request body:
{
"title": "About my School",
"story":"This is my school. There are many like it, but this one is mine.\r\nMy school is my best friend. It is my life. I must master it as I must master my life.\r\nWithout me, my school is useless. Without my school, I am useless. So... I will beat anyone who doesn't like my school (in a not literal sense).",
"name": "Tough guy (mecolak)",
"location": "Boğaziçi University",
"tag": "happy"
}
Response:
{
"id": 22,
"date": "2021-06-10T20:55:35.697301+03:00",
"name": "Tough guy (mecolak)",
"location": "Boğaziçi University",
"tag": "happy",
"title": "About my School",
"story": "This is my school. There are many like it, but this one is mine.\r\nMy school is my best friend. It is my life. I must master it as I must master my life.\r\nWithout me, my school is useless. Without my school, I am useless. So... I will beat anyone who doesn't like my school (in a not literal sense).",
"notifyAdmin": true,
"latitude": 41.0847571,
"longitude": 29.0510399
}
View function
View function is in /api/views/view_post_story.py.
# This endpoint adds a post to the database.
# Also, using a third party api it will do semantic analysis for potential abuse.
class StoryPost(GenericAPIView):
"""
This endpoint adds a post to the database.
Also, using a third party api it will do semantic analysis for potential abuse.
"""
queryset = Story.objects.all()
serializer_class = StorySerializer
def post(self, request, format=None):
serializer = StorySerializer(data=request.data)
if serializer.is_valid():
lat, lng = find_coordinates(serializer.validated_data['location'])
abuse = check_abuse(serializer.validated_data['story'], serializer)
if abuse != True and abuse != False:
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
serializer.save(notifyAdmin = abuse, latitude=lat, longitude=lng)
return Response(serializer.data, status=200)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def check_abuse(story, serializer):
#Using a third party api, check if there is possible abuse in text.
#If runs correctly will return a boolean variable denoting abuse.
#If exception occurs return a HttpResponse of 400.
# Seting various request parameters.
headers = {
'Content-Type': 'application/json',
'Ocp-Apim-Subscription-Key': tisane_key,
}
tisane_body = json.dumps({ "language":"en", "content": story, "settings":{} })
try:
conn = http.client.HTTPSConnection('api.tisane.ai')
conn.request("POST", "/parse", tisane_body, headers)
response = conn.getresponse()
data = json.loads(response.read())
conn.close()
return 'abuse' in data
except Exception as e:
print(e)
return HttpResponse(e.strerror, status = 400)
# Gets flagged stories (notifyAdmin == True) from the database.
# Only used with get.
@api_view(['GET'])
def flagged_stories(request):
"""
Gets flagged stories (notifyAdmin == True) from the database.
Only used with get.
"""
if request.method != 'GET':
return HttpResponse('Please use this end point with GET.', 400)
query = Story.objects.filter(notifyAdmin = True)
response = {'flagged_stories': []}
for q in query:
response['flagged_stories'].append({
'id': q.id,
'title': q.title,
'story': q.story,
})
return JsonResponse(data = response)
Unit tests
Unit tests are in /api/tests/test_post_story.py.
from django.test import TestCase
from ..models import Story
import json
class PostStoryTestCase(TestCase):
def setUp(self):
Story.objects.create(id = 1, title = "Testing Story", story= "Once upon a time ...", name = "USER", longitude =
3.14, latitude = 4.13, location = "somewhere", tag =" ", notifyAdmin = False)
Story.objects.create(id = 2, title = "Testing Story2", story= "This notifies the admin.", name = "USER", longitude =
3.14, latitude = 4.13, location = "somewhere", tag =" ", notifyAdmin = True)
def test_can_find_notify_admin(self):
self.assertEqual(Story.objects.get(notifyAdmin = True).story, "This notifies the admin.")
def test_creates_post(self):
dummy_body = json.dumps({'title':'TEST', 'story': 'STILL TESTING.', 'name':'USER',
'longitude':0, 'latitude': 0 ,'location' : 'somwhere', 'tag': 'TESTTAG'})
response = self.client.post('/api/storypost/', data = dummy_body, content_type="application/json")
self.assertEqual(response.status_code, 200)
def test_creates_flagged_post(self):
dummy_body = json.dumps({'title':'TEST', 'story': 'Just shut up!', 'name':'USER',
'longitude':0, 'latitude': 0 ,'location' : 'somwhere', 'tag': 'TESTTAG'})
response = self.client.post('/api/storypost/', data = dummy_body, content_type="application/json")
self.assertEqual(response.status_code, 200)
notify_flag = Story.objects.get(id = response.json()['id']).notifyAdmin
self.assertEqual(notify_flag, True)
def test_gets_flagged_post(self):
response = self.client.get('/api/flagged_stories/')
self.assertEqual(response.status_code, 200)
data = json.loads(response.content)['flagged_stories']
found_flag = 2 in [story['id'] for story in data] # Post with id = 2 was flagged.
self.assertEqual(found_flag, True)
def test_post_no_data(self):
response = self.client.post('/api/storypost/', data = {}, content_type="application/json")
self.assertEqual(response.status_code, 400)