-
Notifications
You must be signed in to change notification settings - Fork 1
Story API
-
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 user's name who posted the story. It has a maximum char length of 200.
- 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: Thi shows if a story needs to notify the admin or not.
- This is the API for using our own application. By using the GET, PUT and DELETE functionalities, users can get the stories from the database, update stories or delete them from the database.
Get All Stories
This returns Story objects that are stored in the database.
- No parameters are needed for this.
"GET /api/story/ HTTP/1.1" 200
[
{
"id": 1,
"date": "2021-06-10T10:26:00.117830+03:00",
"name": "zcanfes",
"location": "Rome",
"tag": "happy",
"title": "First Day in Rome",
"story": "This was my first day in Rome after moving. I got out of my house and start walking towards the city center. Everywhere there are buildings to see and admire. Just being in the city can take you back in time to experience the old Rome. Today I plan to visit the Colosseum and I can't wait to see one of the most important monuments in Rome. I will take a lot of pictures and share my story here.",
"notifyAdmin": false,
"latitude": 41.9027835,
"longitude": 12.4963655
},
{
"id": 15,
"date": "2021-06-10T11:01:29.359460+03:00",
"name": "melih",
"location": "Berlin",
"tag": "happy",
"title": "Berlin Fever",
"story": "We finally arrived at Berlin Brandenburg Airport earlier today. I'm pretty thrilled about everything about Berlin. Upon arrival I've eaten some Currywurst and I plan on visiting Mustafa's Gemüse Kebap, a well-known Döner stand. I hope my stay in Berlin will be as awesome as I hope it to be.",
"notifyAdmin": false,
"latitude": 52.52000659999999,
"longitude": 13.404954
},
{
"id": 16,
"date": "2021-06-10T15:15:35.250653+03:00",
"name": "melihaydogd",
"location": "Boğaziçi Kuzey Kampüs İstanbul",
"tag": "happy",
"title": "The End of Online Education",
"story": "Finally, the new semester is started and it is offline. I was bored of staying at home and looking at the computer every morning. It is nice to see people around. The most important thing is that my university club is finally active again. The club's name is aviation club and the main activity of the club is paragliding.",
"notifyAdmin": false,
"latitude": 41.0863067,
"longitude": 29.0441352
},
{
"id": 17,
"date": "2021-06-10T15:26:52.016106+03:00",
"name": "mertlkn",
"location": "Boğaziçi Güney Kampüs",
"tag": "great",
"title": "Last Time",
"story": "It was last year. Not too long ago I guess. I was at the campus. Sun was rising, weather was great. When I sit on the grass in South Campus, I would always forget my worries. That time was no different. But little did I know that was the last one for a long time.",
"notifyAdmin": false,
"latitude": 41.0847571,
"longitude": 29.0510399
},
{
"id": 18,
"date": "2021-06-10T16:22:22.877351+03:00",
"name": "kadirkalkan",
"location": "London",
"tag": "happy",
"title": "Awesome London",
"story": "Finally we arrived at London City Airport. Today we plan to visit the British Museum. We are very excited about this trip because this museum contains more than 13 million artifacts from the ancient world. Here I will share many stories about this trip.",
"notifyAdmin": false,
"latitude": 51.5073509,
"longitude": -0.1277583
},
{
"id": 19,
"date": "2021-06-10T16:43:30.185647+03:00",
"name": "nulke",
"location": "Kremlin Palace",
"tag": "Art",
"title": "My First Trip",
"story": "I made my first touristic trip to the Grand Kremlin Palace with my family. Even as a child, I was fascinated by the beauty of this building. It should come as no surprise that I became an architect years later!",
"notifyAdmin": false,
"latitude": 55.7505944,
"longitude": 37.6153441
},
{
"id": 20,
"date": "2021-06-10T18:01:27.148807+03:00",
"name": "omarr09",
"location": "Belgrad Ormanı",
"tag": "programming",
"title": "Programming with the Nature",
"story": "Today I did something very interesting. I went to the Belgrad Forest with my laptop, and programmed for my project there until my laptop runs out of battery. Vibrant nature and fresh air helped me focus on my code and stay relaxed, but I experienced connection problems sometimes. It was a nice experience overall.",
"notifyAdmin": false,
"latitude": 41.184012,
"longitude": 28.9885021
},
{
"id": 21,
"date": "2021-06-10T19:05:01.060191+03:00",
"name": "asenturk",
"location": "Adelaide",
"tag": "Happy",
"title": "A Beginning",
"story": "After more than 20 hours above the sky, I finally arrive to the place where I’ll spend 7 months of my time on earth. Nervous, happy, free, anxious, worry, cheerful… I know that I’ll return home after this period, but I’m worried about something else, “What if I like this place more than home?”. Random people smile and wave me at the streets of Adelaide, should I wave them back?",
"notifyAdmin": false,
"latitude": -34.9284989,
"longitude": 138.6007456
},
{
"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
},
{
"id": 23,
"date": "2021-06-11T02:16:56.077359+03:00",
"name": "uskudarli",
"location": "Bangalore",
"tag": "the heart understands",
"title": "Understading without words",
"story": "On this hot day in Bangalore I was with a woman who was excitedly telling me something. I could not understand as I don't speak Kannada. And, she didn't speak any English. But she was clearly really trying to tell me something. Which made me intent on trying to understand. Eventually, I did. I have no idea how. And, her story, well it is private. And it is sad. I remain touched that the communication had happened and also mystified as to how it did.",
"notifyAdmin": false,
"latitude": 12.9715987,
"longitude": 77.5945627
},
{
"id": 24,
"date": "2021-06-12T10:51:35.023453+03:00",
"name": "zcanfes",
"location": "Istanbul",
"tag": "programming",
"title": "Internship Day",
"story": "This was the last day of my internship. I have opened my last Pull Request and merged with the main branch. I have completed all my tasks and issues. I have learned a lot of things during this internship and improved my programming skills. But most importantly, I have made a lot of great and new friends and I wish to see them soon again.",
"notifyAdmin": false,
"latitude": 41.0082376,
"longitude": 28.9783589
}
]
Get a Specific Story
This returns a Story object that is stored in the database.
-
GET
http://3.129.194.233/api/story/int:story_id/
{
story_id = The id of the story
}
"GET /api/story/1/ HTTP/1.1" 200
{
"id": 1,
"date": "2021-06-10T10:26:00.117830+03:00",
"name": "zcanfes",
"location": "Rome",
"tag": "happy",
"title": "First Day in Rome",
"story": "This was my first day in Rome after moving. I got out of my house and start walking towards the city center. Everywhere there are buildings to see and admire. Just being in the city can take you back in time to experience the old Rome. Today I plan to visit the Colosseum and I can't wait to see one of the most important monuments in Rome. I will take a lot of pictures and share my story here.",
"notifyAdmin": false,
"latitude": 41.9027835,
"longitude": 12.4963655
}
Update a Specific Story
This updates a Story object that is stored in the database.
-
PUT
http://3.129.194.233/api/story/int:story_id/
{
story_id = The id of the story
data = A JSON object that has updated information.
}
An example for clarification:
{
story_id = 16,
data = {
"name": "melihaydogd",
"location": "Boğaziçi Kuzey Kampüs İstanbul",
"tag": "Happy",
"title": "The End of Online Education",
"story": "Finally, the new semester is started and it is offline. I was bored of staying at home and looking at the computer every morning. It is nice to see people around. The most important thing is that my university club is finally active again. The club's name is aviation club and the main activity of the club is paragliding."
}
}
"PUT /api/story/1/ HTTP/1.1" 200
- This returns the Story object with updated information.
Delete a Specific Story
This deletes a Story object that is stored in the database.
-
DELETE
http://3.129.194.233/api/story/int:story_id/
{
story_id = The id of the story
}
"DELETE/api/story/1/ HTTP/1.1" 200
- This returns nothing because the Story object is deleted.
Name | Meaning |
---|---|
HTTP_200_OK | GET operation is successful. |
HTTP_200_OK | UPDATE operation is successful. |
HTTP_204_OK | DELETE operation is successful. |
HTTP_400_BAD_REQUEST | Unexpected type is entered as value. This can happen if a random string is entered as a location. In this case, coordinates cannot be found. |
HTTP_404_NOT_FOUND | No such story is found in the database. |
Model Used in Story API
-
Stories are stored in the database. Django creates a table for models automatically so this story model is created. Only name, location, tag, title, and story are wanted to post a story. However, more fields are stored in the database and they can be seen in the following class.
-
longitude
andlatitude
are gotten from Google Maps API after posting the story. -
date
is automatically entered by the database. -
notifyAdmin
is entered by using Tisane API after posting the story.
class Story(models.Model):
title = models.CharField(max_length=200)
story = models.CharField(max_length=1000)
name = models.CharField(max_length=200)
longitude = models.FloatField()
latitude = models.FloatField()
location = models.CharField(max_length=200)
tag = models.CharField(max_length=200)
date = models.DateTimeField(auto_now_add=True)
notifyAdmin = models.BooleanField(default=False)
def __str__(self):
return self.title
Serializer
-
The following serializer is used for serializing a Story object.
notifyAdmin
,latitute
, andlongitude
fields are not supposed to be filled by the user. Thus, these fields are read-only fields. When making a GET API call for a story, all of the fields will be returned. However, when making a POST API call, read-only fields cannot be filled by users. -
id
field is not a part of the Story model because it is provided by the database. It is the primary key for the Story table and automatically increments.
class StorySerializer(serializers.ModelSerializer):
class Meta:
model = Story
fields = ['id' ,'date', 'name', 'location', 'tag', 'title', 'story', 'notifyAdmin','latitude', 'longitude']
read_only_fields = ['notifyAdmin','latitude', 'longitude']
Views
-
api/story/
endpoint callsget()
function of following class. It returns all of the stories in the database as an array. If there is no story in the database, it returns an empty array. -
This class inherits
GenericAPIView
from Django REST Framework.GenericAPIView
and serializers work together. By using theStorySerializer
the Story objects can be easily converted to JSON Objects. Another advantage of them is that they make endpoints visible on Swagger UI. Also, it shows the example response data on Swagger UI. Without using these features, example response data is not shown on Swagger UI because the example response data gets the information from the serializer through GenericAPIView. You can check it from here. You should click on/story/
endpoint to see example response data. -
In this class,
Story.objects.all()
function makes a connection with the database and gets all the stories. Then, by usingStorySerializer
converts all the stories into an array of JSON Objects. -
If
name
query parameter is specified in the URL, it will return the stories that match withname
field. Example usage:-
It will return the stories that are written by "melihaydogd".
class StoryList(GenericAPIView):
"""
List stories.
"""
queryset = Story.objects.all()
serializer_class = StorySerializer
def get(self, request, format=None):
query_params = self.request.query_params
if len(query_params) == 0:
stories = Story.objects.all()
else:
name = dict(query_params)['name'][0]
stories = Story.objects.filter(name=name).all()
serializer = StorySerializer(stories, many=True)
return Response(serializer.data)
-
api/story/<int:story_id>/
endpoint calls theget()
function of the following class. If there is no such story, it will give 404 NOT FOUND error. Additionally, by using this endpoint, users can use PUT and DELETE API functionalities. -
Users can update their stories by using
api/story/<int:story_id>/
endpoint and PUT functionality. If there is a change in the location, the coordinates of the location will be updated as well. -
Users also can delete their stories by using
api/story/<int:story_id>/
endpoint and DELETE functionality.
class StoryListDetail(GenericAPIView):
"""
Retrieve, update or delete a story instance.
"""
queryset = Story.objects.all()
serializer_class = StorySerializer
def get_object(self, pk):
try:
return Story.objects.get(pk=pk)
except Story.DoesNotExist:
raise Http404
def get(self, request, pk, format=None):
story = self.get_object(pk)
serializer = StorySerializer(story)
return Response(serializer.data)
def put(self, request, pk, format=None):
story = self.get_object(pk)
serializer = StorySerializer(story, data=request.data)
if serializer.is_valid():
if story.location != serializer.validated_data['location']:
lat, lng = find_coordinates(serializer.validated_data['location'])
serializer.save(latitude=lat, longitude=lng)
else:
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def delete(self, request, pk, format=None):
story = self.get_object(pk)
story.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
Helper Function
- This function is used for finding coordinates of a location. It is also used in Post Story API functionality. It takes
location
of a story as a parameter and returns the latitude and longitude of the location.
def find_coordinates(location):
location = location.split(' ')
location = "+".join(location)
url = "https://maps.googleapis.com/maps/api/geocode/json"
params = {"address":location, "key":env('GOOGLE_MAPS_API_KEY')}
new_url = create_url(url,params)
response = requests.get(new_url)
json_data = json.loads(response.content)
location = json_data['results'][0]['geometry']['location']
return location['lat'], location['lng']
Tests
- URLs of the Story API are tested to see whether the intended URLs are working correctly.
class TestUrls(TestCase):
def test_list_story_url_resolve(self):
url = reverse('list_story')
self.assertEqual(resolve(url).func.view_class, view_locationAPI.StoryList)
def test_detail_story_url_resolve(self):
url = reverse('detail_story', args=[1])
self.assertEqual(resolve(url).func.view_class, view_locationAPI.StoryListDetail)
- Views of the Location API are tested to see whether they return the expected values and HTTP status codes.
class TestViews(TestCase):
def setUp(self):
self.client = Client()
self.mock_data = {"name":"melih",
"location":"Karaköy",
"tag":"Chilling",
"title":"Kahve",
"story":"Kahve Keyfi"}
self.mock_data_1 = view_locationAPI.Story.objects.create(id = 1,
title = "Çay",
story="Çay keyfi",
name ="emre",
longitude=10,
latitude=20,
location="Beşiktaş",
tag="mutlu")
def test_story_list_GET(self):
response = self.client.get(reverse('list_story'))
self.assertEqual(response.status_code, 200)
self.assertEqual(json.loads(response.content)[0]['name'], 'emre')
def test_story_GET_detail_1(self):
response = self.client.get(reverse('detail_story', args = [2]))
self.assertEqual(response.status_code, 404)
def test_story_GET_detail_2(self):
response = self.client.get(reverse('detail_story', args = [1]))
self.assertEqual(response.status_code, 200)
self.assertEqual(response.data['name'], 'emre')
def test_story_PUT_detail(self):
data = self.client.get(reverse('detail_story', args = [1]))
data.data['name'] = 'kaan'
response = self.client.put(reverse('detail_story', args = [1]), data.data, content_type='application/json')
self.assertEqual(response.status_code, 200)
self.assertEqual(response.data['name'], 'kaan')
def test_story_DELETE_detail(self):
data = self.client.get(reverse('detail_story', args = [1]))
response = self.client.delete(reverse('detail_story', args = [1]), data.data, content_type='application/json')
self.assertEqual(response.status_code, 204)
- Story model is tested to see whether models are created correctly.
class TestModels(TestCase):
def setUp(self):
self.mock_data = view_locationAPI.Story.objects.create(title = "Çay",
story="Çay keyfi",
name ="emre",
longitude=10,
latitude=20,
location="Beşiktaş",
tag="mutlu")
def test_story_model(self):
self.assertNotEqual(self.mock_data.id, None)
self.assertEqual(self.mock_data.id, 1)
🏠 Home
👪 Team Members
- Milestone Reports
- Project Plans
- Project Selection
- Requirements
- Questions to Customer
- Scenarios and Mockups
- Design
- Meeting 1 (26.03.2021)
- Meeting 2 (01.04.2021)
- Meeting 3 (04.04.2021)
- Meeting 4 (07.04.2021)
- Customer Meeting 1 (14.04.2021)
- Meeting 5 (15.04.2021)
- Meeting 6 (17.04.2021)
- Meeting 7 (21.04.2021)
- Meeting 8 (25.04.2021)
- Meeting 9 (28.04.2021)
- Meeting 10 (03.05.2021)
- Meeting 11 (06.05.2021)
- Meeting 12 (07.05.2021)
- Meeting 13 (22.05.2021)
- Meeting 14 (26.05.2021)
- Meeting 15 (01.06.2021)
- Meeting 16 (04.06.2021)
- Meeting 17 (07.06.2021)
- Meeting 18 (08.06.2021)
- Meeting 19 (09.06.2021)
- Meeting 20 (12.06.2021)
- Lab 1 (12.10.2021)
- Meeting 1 (15.10.2021)
- Lab 2 (19.10.2021)
- Meeting 2 (25.10.2021)
- Lab 3 (26.10.2021)
- Meeting 3 (31.10.2021)
- Lab 4 (02.11.2021)
- Meeting 4 (08.11.2021)
- Lab 5 (09.11.2021)
- Meeting 5 (15.11.2021)
- Meeting 6 (18.11.2021)
- Lab 6 (23.11.2021)
- Lab 7 (30.11.2021)
- Lab 8 (21.12.2021)
- Lab 9 (28.12.2021)