Nearby Places API - bounswe/2021SpringGroup9 GitHub Wiki

Nearby Places 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 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: Shows if a story needs to notify the admin or not.
  • Place: A place in Google Maps. It can have several properties.
    • name: Name of the place
    • vicinity: A very short address of the place
    • geometry: Geometric data of the place. Most of the time, this is {'location': {'latitude': ..., 'longitude': ...}}

2. Description

Nearby Places API gets places near a story's location by utilizing Nearby Search API method of Google Maps Geolocation API. It scans for all the places in 2000 meters radius, extracts useful information from these places, and returns that information as a list.

In our website, we use this API to get all places near a story's location, then display three of these places when a user is viewing a story.

3. Sending Requests

Nearby Search API has the following URI:

http://3.129.194.233/api/nearbyplaces/<int:id>

This URI only accepts GET requests.

Required parameters: id - An integer value which identifies a story in the app. This parameter should be given in the URL as shown above. Optional parameters: none

4. Receiving Responses

On success, the API responds with a status code of 200 and returns a list of places where each place has 'name', 'location', and 'vicinity' properties; 'vicinity' property may be null. So a successful response has the following type:

{
    'name': string
    'location': string
    'vicinity': string | null
}[]

If the API is called with a parameter of an incorrect type (e.g: a string rather than a number for id), the API responds with a status code of 404, and returns our app's 404 page.

If the API is called with an id that doesn't exist in the database, the API responds with a status code of 404 and returns the following error message: Story object with story_id <id> does not exist.

If the API could not establish a successful connection with Google Maps API , it responds with a status code of 500, and returns the following error message: Could not establish a successful connection with Google Maps API.

If there occurs an unknown error, the API responds with a status code of 404, and returns the error message.

5. What API does

Firstly, it finds the Story object with given id from our database. Then it calls Nearby Search API method to find places in Google Maps database that are 2000 meters close to that story's location. Then from the response, it extracts 'name', 'location', and 'vicinity' properties from its result. Then it returns the final list to the caller.

6. Example Requests and Responses

Example Request 1:

http://3.129.194.233/api/nearbyplaces/20

Example Response 1:

[{"name": "Bah\u00e7ek\u00f6y Merkez", "location": {"lat": 41.178284, "lng": 28.991934}, "vicinity": "Bah\u00e7ek\u00f6y Merkez"}, {"name": "Atat\u00fcrk Arboretumu", "location": {"lat": 41.1766262, "lng": 28.9853775}, "vicinity": "\u0130stanbul \u00dcniversitesi, Kemer, Orman Fak\u00fcltesi"}, {"name": "Sultan Mahmut Bendi", "location": {"lat": 41.189797, "lng": 28.98612400000001}, "vicinity": "Kemer"}, {"name": "Valide Sultan Bendi", "location": {"lat": 41.1911694, "lng": 28.9879972}, "vicinity": "Kemer"}, {"name": "Ormanci Kirtasiye", "location": {"lat": 41.178819, "lng": 28.991597}, "vicinity": "Bah\u00e7ek\u00f6y Merkez, \u0130n\u00f6n\u00fc Caddesi 17/A"}, {"name": "Kim", "location": {"lat": 41.1786464, "lng": 28.9924441}, "vicinity": "Bah\u00e7ek\u00f6y Merkez, Adnan Menderes Caddesi No:10/A"}, {"name": "Project Studio Design Office, WOOD-Tshirt", "location": {"lat": 41.18107399999999, "lng": 28.9904796}, "vicinity": "Bah\u00e7ek\u00f6y Merkez Mah. \u0130n\u00f6n\u00fc Cad. 16 / B Sar\u0131yer"}, {"name": "Bahcekoy Forest Management Directorate", "location": {"lat": 41.17535549999999, "lng": 28.989204}, "vicinity": "Kemer, Valide Sultan Caddesi No:33"}, {"name": "Eczane Filiz", "location": {"lat": 41.1786696, "lng": 28.9921095}, "vicinity": "Bah\u00e7ek\u00f6y Merkez, Adnan Menderes Caddesi 25/A"}, {"name": "Entel Green Valley Site", "location": {"lat": 41.1795452, "lng": 28.9960037}, "vicinity": "Bah\u00e7ek\u00f6y Yeni, Sar\u0131yer"}, {"name": "Ozel Yeni Nesil 2000 Anaokulu", "location": {"lat": 41.18261379999999, "lng": 28.9957702}, "vicinity": "Bah\u00e7ek\u00f6y Yeni, Hunca Caddesi No:16"}, {"name": "Hamidiye Su", "location": {"lat": 41.1792889, "lng": 28.991628}, "vicinity": "Bah\u00e7ek\u00f6y Merkez, Prof. Dr. S\u0131tk\u0131 Evcimen Sok. No:5 D:2"}, {"name": "Bim", "location": {"lat": 41.178686, "lng": 28.9918443}, "vicinity": "Bah\u00e7ek\u00f6y Merkez, \u0130n\u00f6n\u00fc Caddesi No:4"}, {"name": "sonart havuz in\u015faat sanayi ic ltd \u015fti", "location": {"lat": 41.184791, "lng": 28.999801}, "vicinity": "Bah\u00e7ek\u00f6y Yeni, Adnan Menderes Caddesi No:18"}, {"name": "Kahta Cigkofte", "location": {"lat": 41.179439, "lng": 28.992668}, "vicinity": "Merkez Mah, Adnan Menderes Caddesi no:39/b"}, {"name": "Istanbul Orienteering Sports Club Association", "location": {"lat": 41.181912, "lng": 28.98941899999999}, "vicinity": "Bah\u00e7ek\u00f6y Merkez Mah, Valide Sultan Caddesi 28/A"}, {"name": "POLAT \u0130N\u015eAAT VE DEKORASYON", "location": {"lat": 41.17960950000001, "lng": 28.9915286}, "vicinity": "Bah\u00e7ek\u00f6y Merkez, Nil\u00fcfer Sokak No:4"}, {"name": "Ziraat Bankas\u0131 ATM", "location": {"lat": 41.1803996, "lng": 28.992936}, "vicinity": "Bah\u00e7ek\u00f6y Merkez, Adnan Menderes Caddesi No:1"}, {"name": "Belgrade forest protection volunteer association", "location": {"lat": 41.1819665, "lng": 28.9892555}, "vicinity": "Bah\u00e7ek\u00f6y Merkez, Valide Sultan Caddesi No:30"}, {"name": "Belgrad Outdoor", "location": {"lat": 41.1814657, "lng": 28.9893793}, "vicinity": "Bah\u00e7ek\u00f6y Merkez, Han\u0131meli Sokak No:6 D:1"}]

Explanation: The API is called with a correct parameter (our website has a story with id 20 in its database), so it returned the list of places near the story's location. Note that 'vicinity' parameter is missing from the places. As told before, this is okay.



Example Request 2:

http://3.129.194.233/api/nearbyplaces/1

Example Response 2:

[{"name": "Rome", "location": {"lat": 41.9027835, "lng": 12.4963655}, "vicinity": "Rome"}, {"name": "Hotel Mondial", "location": {"lat": 41.901464, "lng": 12.495732}, "vicinity": "Via Torino, 127, Roma"}, {"name": "UNAHOTELS Dec\u00f2 Roma", "location": {"lat": 41.8998425, "lng": 12.499711}, "vicinity": "Via Giovanni Amendola, 57, Roma"}, {"name": "EXE International Palace", "location": {"lat": 41.8999691, "lng": 12.4923893}, "vicinity": "Via Nazionale, 46, Roma"}, {"name": "YellowSquare", "location": {"lat": 41.9046909, "lng": 12.5043697}, "vicinity": "Via Palestro, 51, Roma"}, {"name": "Mecenate Palace Hotel", "location": {"lat": 41.8967923, "lng": 12.4998814}, "vicinity": "Via Carlo Alberto, 3, Roma"}, {"name": "Hotel Cosmopolita Roma", "location": {"lat": 41.89681, "lng": 12.484156}, "vicinity": "Via di S. Eufemia, 5, Roma"}, {"name": "Best Western Hotel President", "location": {"lat": 41.889999, "lng": 12.506729}, "vicinity": "Via Emanuele Filiberto, 173, Roma"}, {"name": "Eurostars Saint John", "location": {"lat": 41.8891667, "lng": 12.505}, "vicinity": "Via Matteo Boiardo, 30, Roma"}, {"name": "Best Western Hotel Globus", "location": {"lat": 41.90627540000001, "lng": 12.5168793}, "vicinity": "Viale Ippocrate, 119, Roma"}, {"name": "Hotel Fontana", "location": {"lat": 41.9006339, "lng": 12.4832717}, "vicinity": "Piazza di Trevi, 96, Roma"}, {"name": "Hotel Torino", "location": {"lat": 41.90047389999999, "lng": 12.4977601}, "vicinity": "Via Principe Amedeo, 8, Roma"}, {"name": "NH Collection Roma Vittorio Veneto", "location": {"lat": 41.910174, "lng": 12.4902213}, "vicinity": "Corso d'Italia, 1, Roma"}, {"name": "Grand Hotel Plaza", "location": {"lat": 41.905184, "lng": 12.478788}, "vicinity": "Via del Corso, 126, Roma"}, {"name": "RoYaL Art Caf\u00e8 Roma", "location": {"lat": 41.8899689, "lng": 12.4942703}, "vicinity": "Piazza del Colosseo, 1, Roma"}, {"name": "Baglioni Hotel Regina - Rome", "location": {"lat": 41.90707769999999, "lng": 12.4898787}, "vicinity": "Via Vittorio Veneto, 72, Roma"}, {"name": "Hotel Virgilio", "location": {"lat": 41.8990021, "lng": 12.4920301}, "vicinity": "Via Palermo, 30, Roma"}, {"name": "Freedom Traveller Hostel", "location": {"lat": 41.90462229999999, "lng": 12.5004419}, "vicinity": "Via Gaeta, 23, Roma"}, {"name": "Grand Hotel de La Minerve", "location": {"lat": 41.8976864, "lng": 12.477605}, "vicinity": "Piazza della Minerva, 69, Roma"}, {"name": "Municipio I", "location": {"lat": 41.9028606, "lng": 12.4854879}, "vicinity": "Municipio I"}]

Explanation: Again, the API is called with a correct parameter, so it returned the list of places near the story's location. This time places have 'vicinity' property as well since the API was able to fetch vicinity information from Google Maps API .



Example Request 3:

http://3.129.194.233/api/nearbyplaces/12345678

Example Response 3:

Story object with story_id 12345678 does not exist.

Explanation: This time the API is called with an incorrect parameter (assuming we will never have 12345678 stories in our database), so it responded with a status code of 404 and returned the error.

7. Code documentation

View function

View function is in /api/views/view_nearbyplaces.py.

@api_view(['GET'])
def get_places_near_story_location(request, pk):
    """ Returns places near location of the story with given story_id."""

    # Get the story object
    try:
        story = Story.objects.get(id=pk)
    except Story.DoesNotExist:
        return HttpResponseNotFound(f"Story object with story_id {pk} does not exist.")

    # Get list of nearby places using Google Maps API.
    try:
        response = requests.get("https://maps.googleapis.com/maps/api/place/nearbysearch/json",
                                params={
                                    "key": GOOGLE_MAPS_API_KEY,
                                    "location": f"{story.latitude},{story.longitude}",
                                    "radius": "2000",  # Scan places in 2000 meters radius
                                    "language": "en",
                                })
        response.raise_for_status()
    except:
        return HttpResponseServerError("Could not establish a successful connection with Google Maps API.")

    # Construct the json and return it
    try:
        places = [{
            "name": place["name"],
            "location": place["geometry"]["location"],
            "vicinity": place.get("vicinity", None)
        } for place in response.json()["results"]]
        return JsonResponse(places, safe=False)
    except:
        return HttpResponseServerError("Place not found.", status = 404)
Unit tests

Unit tests are in /api/tests/test_nearbyplaces.py.

class GettingPlacesTestCase(TestCase):
    def setUp(self):
        Story.objects.create(id = 1,
            title="My Story",
            story="Story body",
            name="John",
            latitude=37.184,
            longitude=-3,
            location="Home",
            tag="Travel",
            notifyAdmin=False
        )

        Story.objects.create(id = 2,
            title="Your Story",
            story="Your Story body",
            name="Johanna",
            latitude=37.184,
            longitude=-3,
            location="Home",
            tag="Travel",
            notifyAdmin=False
        )

    
    def test_existing_place(self):
        """ Tests getting nearby places of a story which exists in the database """
        c = Client()
        response = c.get('/api/nearbyplaces/1')
        self.assertNotIn(response.status_code, (404, 500))
        self.assertNotEqual(response.json(), [])

    
    def test_non_existing_place(self):
        """ Tests getting nearby places of a story which does not in the database
        This should return 404 """
        c = Client()
        response = c.get('/api/nearbyplaces/123123')
        self.assertEqual(response.status_code, 404)


    def test_incorrect_api_request(self):
        """ Tests calling the API method with an incorrect parameter.
        This should return 404 """
        c = Client()
        response = c.get('/api/nearbyplaces/asdfgh')
        self.assertEqual(response.status_code, 404)
⚠️ **GitHub.com Fallback** ⚠️