4
\$\begingroup\$

This script prints the weather (fetching the information from a public API).

If anyone could review my code and answer my question about code structure and how should I code in the right way, I really appreciate that very much.

Questions:

  • I used many functions to do just one specific things, and return their value to be used in the next functions. Is this right?

  • In main I used composed way for calling functions, Is this right or there is a better way?

Note: You need to obtain your API key to try it.

import datetime
import json
import urllib.request


def time_converter(time):
    converted_time = datetime.datetime.fromtimestamp(
        int(time)
    ).strftime('%I:%M %p')
    return converted_time


def url_builder(city_id):
    user_api = ''  # Obtain yours form: http://openweathermap.org/
    unit = 'metric'  # For Fahrenheit use imperial, for Celsius use metric, and the default is Kelvin.
    api = 'http://api.openweathermap.org/data/2.5/weather?id='     # Search for your city ID here: http://bulk.openweathermap.org/sample/city.list.json.gz

    full_api_url = api + str(city_id) + '&mode=json&units=' + unit + '&APPID=' + user_api
    return full_api_url


def data_fetch(full_api_url):
    url = urllib.request.urlopen(full_api_url)
    output = url.read().decode('utf-8')
    raw_api_dict = json.loads(output)
    url.close()
    return raw_api_dict


def data_organizer(raw_api_dict):
    data = dict(
        city=raw_api_dict.get('name'),
        country=raw_api_dict.get('sys').get('country'),
        temp=raw_api_dict.get('main').get('temp'),
        temp_max=raw_api_dict.get('main').get('temp_max'),
        temp_min=raw_api_dict.get('main').get('temp_min'),
        humidity=raw_api_dict.get('main').get('humidity'),
        pressure=raw_api_dict.get('main').get('pressure'),
        sky=raw_api_dict['weather'][0]['main'],
        sunrise=time_converter(raw_api_dict.get('sys').get('sunrise')),
        sunset=time_converter(raw_api_dict.get('sys').get('sunset')),
        wind=raw_api_dict.get('wind').get('speed'),
        wind_deg=raw_api_dict.get('deg'),
        dt=time_converter(raw_api_dict.get('dt')),
        cloudiness=raw_api_dict.get('clouds').get('all')
    )
    return data


def data_output(data):
    m_symbol = '\xb0' + 'C'
    print('---------------------------------------')
    print('Current weather in: {}, {}:'.format(data['city'], data['country']))
    print(data['temp'], m_symbol, data['sky'])
    print('Max: {}, Min: {}'.format(data['temp_max'], data['temp_min']))
    print('')
    print('Wind Speed: {}, Degree: {}'.format(data['wind'], data['wind_deg']))
    print('Humidity: {}'.format(data['humidity']))
    print('Cloud: {}'.format(data['cloudiness']))
    print('Pressure: {}'.format(data['pressure']))
    print('Sunrise at: {}'.format(data['sunrise']))
    print('Sunset at: {}'.format(data['sunset']))
    print('')
    print('Last update from the server: {}'.format(data['dt']))
    print('---------------------------------------')


if __name__ == '__main__':
    try:
        data_output(data_organizer(data_fetch(url_builder(2172797))))
    except IOError:
        print('no internet')

Output:

---------------------------------------
Current weather in: Cairns, AU:
14.81 °C Clear
Max: 17, Min: 10.56

Wind Speed: 1.91, Degree: None
Humidity: 100
Cloud: 0
Pressure: 1015
Sunrise at: 11:41 PM
Sunset at: 10:50 AM

Last update from the server: 11:35 PM
---------------------------------------
\$\endgroup\$
1
  • 1
    \$\begingroup\$ Welcome to Code Review! I have rolled back the last edit. Please see what you may and may not do after receiving answers. That is to say, whilst it is good that you want to improve your code, please don't add or edit code to your question based of commentary from answerers, as it invalidates their answers. Check the link for what alternative actions you can take. \$\endgroup\$
    – Pimgd
    Commented Jun 9, 2016 at 12:57

2 Answers 2

3
\$\begingroup\$

For your first question, yes, breaking down functions to do really specific things is always good. Makes it easier to use the same functions again in other situations etc.

For your second question, I would personally only call one function at a time, for me it makes it easier to read and understand.

Also, you can have a single print statement for a multi-line string for example:

print('''' Hello
world
      isn't this cool?'''

and that would print:

Hello
world
      isn't this cool?

but in your circumstance it might be a little bit difficult with all those format().

\$\endgroup\$
1
  • \$\begingroup\$ Thanks for your feedback, I add a new way to call the functions I hope it's what you intended to explain it to me. \$\endgroup\$
    – MohammadL
    Commented Jun 9, 2016 at 12:48
3
\$\begingroup\$

You could use the with..as construct for opening the url. This way you don't have to remember to close it:

def data_fetch(full_api_url):
    with urllib.request.urlopen(full_api_url) as url:
      return json.loads(url.read().decode('utf-8'))

Defining temporary variables makes the following more readable imho:

def data_organizer(raw_data):
    main = raw_data.get('main')
    sys = raw_data.get('sys')
    data = dict(
        city=raw_data.get('name'),
        country=sys.get('country'),
        temp=main.get('temp'),
        temp_max=main.get('temp_max'),
        temp_min=main.get('temp_min'),
        humidity=main.get('humidity'),
        pressure=main.get('pressure'),
        sky=raw_data['weather'][0]['main'],
        sunrise=time_converter(sys.get('sunrise')),
        sunset=time_converter(sys.get('sunset')),
        wind=raw_data.get('wind').get('speed'),
        wind_deg=raw_data.get('deg'),
        dt=time_converter(raw_data.get('dt')),
        cloudiness=raw_data.get('clouds').get('all')
    )
    return data

Also note that raw_api_dict.get('main').get('temp_max') will fail if 'main' not in raw_api_dict, same as above code. Depending on the API you might want to wrap it in a try..except or use assert(main is not None).


You could go one step further from what @Sisoma Munden proposed, by giving format the data dictionary and using named placeholders:

def data_output(data):
    data['m_symbol'] = '\xb0' + 'C'
    s = '''---------------------------------------
Current weather in: {city}, {country}:
{temp}{m_symbol} {sky}
Max: {temp_max}, Min: {temp_min}

Wind Speed: {wind}, Degree: {wind_deg}
Humidity: {humidity}
Cloud: {cloudiness}
Pressure: {pressure}
Sunrise at: {sunrise}
Sunset at: {sunset}

Last update from the server: {dt}
---------------------------------------'''
    print(s.format(**data))

This permanently adds the key 'm_symbol' to data, but this should be of no consequence, since it is not used elsewhere. If you want to play it safer, copy the dict first and modify the copy.

\$\endgroup\$
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.