Post Story API - bounswe/2021SpringGroup9 GitHub Wiki

Post Story API Documentation


1. Glossary

  • 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.

2. Description

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/.

3. Sending Requests

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.

4. Receiving Responses

/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']
}
Get the flagged stories with notifyAdmin = True

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']} ...]
}

5. Example Requests and Responses

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
    }

6. Code Documentation

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)
⚠️ **GitHub.com Fallback** ⚠️