Assignment 07

Solutions to Assignment 07.

The API

$ curl 'https://nominatim.openstreetmap.org/search.php?q=bangalore&format=jsonv2'
[
  {
    "place_id": 216751017,
    "licence": "Data © OpenStreetMap contributors, ODbL 1.0. http://osm.org/copyright",
    "osm_type": "relation",
    "osm_id": 7902476,
    "lat": "12.9767936",
    "lon": "77.590082",
    "category": "boundary",
    "type": "administrative",
    "place_rank": 16,
    "importance": 0.6094348238975636,
    "addresstype": "city",
    "name": "Bengaluru",
    "display_name": "Bengaluru, Bangalore North, Bengaluru Urban District, Karnataka, India",
    "boundingbox": [
      "12.8340125",
      "13.1436649",
      "77.4601025",
      "77.7840515"
    ]
  }
]

The Nominatum API returns the information about the matched locations. There could be zero, one, or more matches.

The Program

The program is expected to take the place name as a command-line argument and print the display_name, type, lat and lon for every match from the nominatum search for that place, as shown in the examples below.

$ python nominatum.py Bangalore
Bengaluru, Bangalore North, Bengaluru Urban District, Karnataka, India
Type: administrative
Latitude: 12.9767936
Longitude: 77.590082

When there are multiple matches, they are separated by a new line.

$ python nominatum.py "silk board"
Silk Board, Outer Ring Road, BTM 2nd Stage, BTM Layout Ward, South Zone, Bengaluru, Bangalore South, Bengaluru Urban District, Karnataka, 560034, India
Type: bus_stop
Latitude: 12.9170295
Longitude: 77.6223209

Silk Board, Srinagar - Kanyakumari Highway, HSR Layout Ward, Bommanahalli Zone, Bengaluru, Bangalore South, Bengaluru Urban District, Karnataka, 560034, India
Type: bus_stop
Latitude: 12.9167972
Longitude: 77.6235628

Sometimes there could be a lot of matches.

$ python nominatum.py Google
Google, 355, Main Street, East Cambridge, Cambridge, Middlesex County, Massachusetts, 02142, United States
Type: company
Latitude: 42.3627717
Longitude: -71.087257

Google, West Bluff Creek Drive, Playa Vista, Los Angeles, Los Angeles County, California, 90094, United States
Type: company
Latitude: 33.977092
Longitude: -118.4092133

Google, Mountain View, Santa Clara County, California, United States
Type: commercial
Latitude: 37.419000999999994
Longitude: -122.08237596053958

Google, 150, Broadway, East Cambridge, Cambridge, Middlesex County, Massachusetts, 02142, United States
Type: company
Latitude: 42.3642662
Longitude: -71.0887198

Googal, Devadurga taluk, Raichur District, Karnataka, India
Type: village
Latitude: 16.4693278
Longitude: 77.1442219

Google, Kirkland, King County, Washington, United States
Type: commercial
Latitude: 47.6705578
Longitude: -122.19707375045999

Solution

import requests
import sys

url = "https://nominatim.openstreetmap.org/search.php"

def search(q):
    params = {"q": q, "format": "jsonv2"}
    result = requests.get(url, params=params).json()
    for entry in result:
        print(entry['display_name'])
        print("Type:", entry['type'])
        print("Latitude:", entry['lat'])
        print("Longitude:", entry['lon'])
        print("")

name = sys.argv[1]
search(name)

Client for Free Dictionary API

Write a program dict.py to find the meaning of a word using the Free Dictionary API.

Expected Usage

The program should take one word as argument and display the phonetics and meaning of that word using the Free Dictionary API.

$ python dict.py obscure
onscure /əbˈskjɔː(ɹ)/

VERB

To render obscure; to darken; to make dim; to keep in the dark; to hide; to make less visible, intelligible, legible, glorious, beautiful, or illustrious.

To hide, put out of sight etc.

To conceal oneself; to hide.

ADJECTIVE

Dark, faint or indistinct.

Hidden, out of sight or inconspicuous.

As you can see, The first line in the word and its phonetic. For each part of speech, the part of speech is shown in uppercase, followed by each of the meaning provided in the response. There will be exactly one new line after each meaning and part-of-speech line.

Solution

import sys
import requests

word = sys.argv[1]

url = f"https://api.dictionaryapi.dev/api/v2/entries/en/{word}"
d = requests.get(url).json()[0]

print(f"{d['word']} {d['phonetic']}")
print()

for m in d['meanings']:
    print(m['partOfSpeech'].upper())
    print()
    for definition in m['definitions']:
        print(definition['definition'])
        print()

Frankfurter Exchange Rates

Write a program frankfurter.py to list the historical currency rate of a currency against a base currency using Frankfurter Exchange Rate API.

The program should take the following command-line arguments.

  -c CURRENCY, --currency CURRENCY
                        target currency, default INR
  -b BASE, --base BASE  base currency, default USD
  -d DATE, --date DATE  First date to consider, default yesterday
  -n DAYS, --days DAYS  number of days to display

The program should display the curreny rate between the base currency and the target currency for n days starting from yesterday. Optionally, the start date could be provided as a command-line argument.

Please note that there convertion data is not available on weekends. So the number of rows of data shown may be less than n.

The Output Format

The output needs to be properly tabulated. Please use Python library tabulate for doing this.

Please refer to Printing Tables with Tabulate in the Python Cookbook to learn how to the the tabulate library.

Usage

$ python frankfurter.py
Date          USD    INR
----------  -----  -----
2023-10-18      1  83.25
2023-10-17      1  83.22
2023-10-16      1  83.25
2023-10-13      1  83.27
2023-10-12      1  83.24
2023-10-11      1  83.18
2023-10-10      1  83.24
2023-10-09      1  83.3
$ python frankfurter.py -n 2
Date          USD    INR
----------  -----  -----
2023-10-18      1  83.25
2023-10-17      1  83.22
$ python frankfurter.py -b GBP
Date          GBP     INR
----------  -----  ------
2023-10-18      1  101.55
2023-10-17      1  101.31
2023-10-16      1  101.37
2023-10-13      1  101.41
2023-10-12      1  102.47
2023-10-11      1  102.24
2023-10-10      1  101.96
2023-10-09      1  101.39
$ python frankfurter.py -b GBP -c USD
Date          GBP     USD
----------  -----  ------
2023-10-18      1  1.2198
2023-10-17      1  1.2173
2023-10-16      1  1.2176
2023-10-13      1  1.2178
2023-10-12      1  1.231
2023-10-11      1  1.2292
2023-10-10      1  1.2249
2023-10-09      1  1.2172
$ python frankfurter.py -d 2023-01-31
Date          USD    INR
----------  -----  -----
2023-01-31      1  81.82
2023-01-30      1  81.53
2023-01-27      1  81.61
2023-01-26      1  81.53
2023-01-25      1  81.57
2023-01-24      1  81.62
2023-01-23      1  81.36

Hints

Solution

import argparse
import datetime
import requests
from tabulate import tabulate

p = argparse.ArgumentParser()
p.add_argument("-c", "--currency", help="currency to list, default INR", default="INR")
p.add_argument("-b", "--base", help="base currency, default USD", default="USD")
p.add_argument("-d", "--date", help="starting date, default yesterday", type=datetime.date.fromisoformat)
p.add_argument("-n", "--days", help="number of days to display", type=int, default=10)

args = p.parse_args()

yday = datetime.date.today() - datetime.timedelta(days=1)
start_date = args.date or yday

date2 = start_date - datetime.timedelta(days=args.days)

url = f"https://api.frankfurter.app/{date2}..{start_date}"
params = {"base": args.base, "to": args.currency}

result = requests.get(url, params=params).json()

headers = ["Date", args.base, args.currency]
data = [[date, 1.0, value[args.currency]] for date, value in result['rates'].items()][::-1]

print(tabulate(data, headers=headers))

PyPI Releases

Overview

Python Package Index (PyPI) maintains an index of all the third-party python packages. It also provides an API to look at all the releases of a package.

Write a command line program `pypi-releases.py to list all the releases of a given package using the PyPI API.

The program should take the package name as argument and list all the releases along with the release time and filename uploaded for that release, in the reverse chronological order of the release time. By default, it should show the recent 5 releases.

The program should accept the following optional command-line arguments.

-n --count     number of releases to show
-b --before    only show release on or before this date
-a --after     only show releases that are on or after this date
-r --reverse   show the release in the reverse order - old releases first

You can use the upload_time as an approximation for the release time. Typically, there would multiple files uploaded for each release and all of them will be listed in the response of the API. Please use first entry where the packagetype is sdist. If a release does not have an entry with packagetype with value sdist, please ignore that release.

The PyPI API

The PYPI API documentation has two endpoints. The first one is to fecth the information about all releases of a project or a package. The second one is to get information about one particular release. We are only interested in the first one.

The following is the URL for getting information about releases of python package Flask. You can replace Flask with any package name to get information about that package.

https://pypi.org/pypi/Flask/json

You’ll have to explore the API response and figure out which part of the data that you need to take.

Hint: You just need to focus on the releases part of the response.

The Output Format

The output needs to be properly tabulated. Please use Python library tabulate for doing this.

Please refer to Printing Tables with Tabulate in the Python Cookbook to learn how to the the tabulate library.

Sample Usage

$ python pypi-releases.py Flask
Package    Version    Release Date         Filename
---------  ---------  -------------------  ------------------
Flask      3.0.0      2023-09-30T14:36:12  flask-3.0.0.tar.gz
Flask      2.3.3      2023-08-21T19:52:35  flask-2.3.3.tar.gz
Flask      2.2.5      2023-05-02T14:42:36  Flask-2.2.5.tar.gz
Flask      2.3.2      2023-05-01T15:42:12  Flask-2.3.2.tar.gz
Flask      2.3.1      2023-04-25T21:20:31  Flask-2.3.1.tar.gz
$ python pypi-releases.py Flask -b 2022-06
Package    Version    Release Date         Filename
---------  ---------  -------------------  ------------------
Flask      2.1.2      2022-04-28T17:47:40  Flask-2.1.2.tar.gz
Flask      2.1.1      2022-03-30T21:38:32  Flask-2.1.1.tar.gz
Flask      2.1.0      2022-03-28T19:15:15  Flask-2.1.0.tar.gz
Flask      2.0.3      2022-02-14T20:01:09  Flask-2.0.3.tar.gz
Flask      2.0.2      2021-10-04T14:34:54  Flask-2.0.2.tar.gz
$ python pypi-releases.py Flask -a 2022-06 -r
Package    Version    Release Date         Filename
---------  ---------  -------------------  ------------------
Flask      2.1.3      2022-07-13T20:56:00  Flask-2.1.3.tar.gz
Flask      2.2.0      2022-08-02T00:14:12  Flask-2.2.0.tar.gz
Flask      2.2.1      2022-08-03T23:52:25  Flask-2.2.1.tar.gz
Flask      2.2.2      2022-08-08T23:26:33  Flask-2.2.2.tar.gz
Flask      2.2.3      2023-02-15T22:43:57  Flask-2.2.3.tar.gz

Solution

Gist Lib

Write a python librart gist_lib.py to work with Github gists.

Github gists are quick way to save snippets of code. Here is a sample gist:

https://gist.github.com/PipalBot/a879fd2964b8afb2d7dac9034e656a71

The Interface

The python library should have the following functions.

get_gists(user)
    Returns the ids of the recent 30 gists of the user.

    >>> get_gists("PipalBot")
    ["afb5fef5669dddfd188c0ab403f8b096", "8648e51af65eae3317de7937e12a7ad2", ...]

get_files(gist_id)
    Returns a dictionary with keys filename, contents for each file available in the gist.

    >>> get_files("079c99c487c137bbb4b5e8a5fa134f49")
    [{"filename": "README.md", "contents": "Hello World"}]

The API

Please look at Gists section in the Github API docs.

The two endpoints that you may need to understand are:

  • https://docs.github.com/en/rest/gists/gists?apiVersion=2022-11-28#list-gists-for-a-user
  • https://docs.github.com/en/rest/gists/gists?apiVersion=2022-11-28#get-a-gist

Quick summary of the API

List gists of a user

https://api.github.com/users/anandology/gists

(replace anandology with any github username)

The JSON response includes first 30 gists of that user. You just need to pick the IDs of the gists from response.

Get details of a gist

https://api.github.com/gists/a879fd2964b8afb2d7dac9034e656a71

The JSON response of the gist URL includes all the names of the files and their content.

Solution

import requests

def get_gists(username):
    url = f"https://api.github.com/users/{username}/gists"
    return [gist['id'] for gist in requests.get(url).json()]

def get_files(gist_id):
    url = f"https://api.github.com/gists/{gist_id}"
    d = requests.get(url).json()
    return [{"filename": f['filename'], "content": f['content']} for f in d['files'].values()]