City API - bounswe/2021SpringGroup9 GitHub Wiki

City 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.
  • GeoDB Cities API: This API is used to get the nearby cities of a location.

2. Description

City API gets nearby cities of a story's location by utilizing of GeoDB Cities API. It scans cities with a population of more than 100.000 within a 100-mile radius, extracts names and countries of these cities, and returns them as a list.

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

3. Sending Requests

City API has the following URI:

http://3.129.194.233/api/city/<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 cities where each city has 'name' and 'country' properties. Successful response has the following type:

{
    'name': string
    'country': string
}

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: There is no Story object with story_id <id>!

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

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 there occurs an unknown error, the API responds with a status code of 404, and returns the error message.

5. What API does

First of all, it finds the Story object with given id from our database. Then it calls City API method to find cities with populations greater than 100,000 and within 100 miles of the story's location. Then from the response, it extracts 'name' and 'country' 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/city/23

Example Response 1:

[{"name": "Bengaluru", "country": "India"}, {"name": "Bengaluru Urban district", "country": "India"}, {"name": "Yelahanka", "country": "India"}, {"name": "Bangalore Rural district", "country": "India"}, {"name": "Hosur", "country": "India"}]

Explanation: The API is called with a correct parameter (our website has a story with id 23 in its database), so it returned the list of cities near the story's location.



Example Request 2:

http://3.129.194.233/api/city/6

Example Response 2:

There is no Story object with story_id 6!

Explanation: This time the API is called with an id that doesn't exist in the 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_cityAPI.py.

def true_location_from(latitude,longitude):    # in order to get locaiton in ISO form
    latlon=""
    if(latitude>=0):
        latlon=latlon+"+%f"%(latitude)
    elif(latitude<0):
        latlon=latlon+"%f"%(latitude)
    if(longitude>=0):
        latlon=latlon+"+%f"%(longitude)
    elif(longitude<0):
        latlon=latlon+"%f"%(longitude)
    return(latlon)    
    

@api_view(['GET'])
def get_cityinfo(request, story_id):
    """ 
    This API gets location with latitude and longitude and finds nearby cities. It can also take parameters such as the maximum population of the searched city and radius which is the maximum search distance from a location. 
    """
   

    try:
        story = Story.objects.get(id=story_id)
    except Story.DoesNotExist:
        return HttpResponseNotFound(f"There is no Story object with story_id {story_id}!")

      
    
    try:
        url = "https://wft-geo-db.p.rapidapi.com/v1/geo/cities"

        

        locationinISOform=true_location_from(story.latitude, story.longitude)
        
        querystring = {"location":locationinISOform,"radius":"100", "minPopulation":"100000" }

        headers = {
            'x-rapidapi-key': CITY_API_KEY,
            'x-rapidapi-host': "wft-geo-db.p.rapidapi.com"
            }

        response = requests.request("GET", url, headers=headers, params=querystring)

        
    except:
        return HttpResponseServerError("Could not establish connection")

    try:
        cityinfo =[ {
            
            "name": info["name"], "country": info["country"]
            
        }for info in response.json()["data"]]
        return JsonResponse(cityinfo, safe=False)
    except:
        return HttpResponseServerError("City not found.", status = 404)
Unit tests

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

class CityTest(TestCase):
   
    def setUp(self):
        self.client = Client()
        self.story = Story.objects.create(id = 1,
            title = 'titlex',
    		story = 'storyy',
    		name = 'namez',
    		longitude = 118.387099,
    		latitude = 33.832213,
    		location = 'Istanbul',
    		tag = 'tagw'
        )
        
    def test_works(self):  
        
        resp = self.client.get("/api/city/1")
        self.assertEqual(resp.status_code,200)
    
    def test_not_exist_story(self): 

        resp=self.client.get("/api/city/2")
        self.assertEqual(resp.status_code,404)
    
    def test_string_parameter(self):  
        resp=self.client.get("/api/city/asd")
        self.assertEqual(resp.status_code,404)
⚠️ **GitHub.com Fallback** ⚠️