# Let's import required Python libraries
import requests

# Define connection address in raw string - raw is indicated by 'r' in front,
# and means that it should not interpret the slash sign as the 'escape character'.
address = r"http://api.nbp.pl"

# In the instructions it is written, that we should create the request address by the following rule:
# http://api.nbp.pl/api/exchangerates/rates/  {table}  /  {code}  /  {date}
# Where:
# {table} - can be A, B, C
# {date} - is a date in a format: 2017-10-12
# {code} - currency code, like EUR, USD
rates_path = "api/exchangerates/rates"
table = "a"
code = "usd"
date = "2017-09-12"

# A few ways of creating the query address:

# Add manually
resource_address = address + "/" + rates_path + "/" + table + "/" + code + "/" + date
print(resource_address)
# Join using string.join method
resource_address = "/".join([address, rates_path, table, code, date])
print(resource_address)


# Perform the GET query
response = requests.get(resource_address)
print(response)

"""
Explanation: Python WebApps - Lesson 1 - HTTP client console application
Via Wikipedia:
The Hypertext Transfer Protocol (HTTP) is an application protocol for distributed, collaborative, and hypermedia information systems.[1] HTTP is the foundation of data communication for the World Wide Web.
Hypertext is structured text that uses logical links (hyperlinks) between nodes containing text. HTTP is the protocol to exchange or transfer hypertext.
Let's say, we want to know the currencies of Euro, US Dollar and Polish PLN. There is a website offering this service for free on www.nbp.pl . We can access that information using a HTTP client.
What is a client? For example, it is the web browser like Chrome. We can also write our own small Python client.
The NBP portal offers a public API to access this data using HTTP messages. They provide instructions here: http://api.nbp.pl/
API - Application Programming Interface - is a set of rules for other programs to know how to communicate using messages that are run automatically from code.
HTTP message types
A message can do a lot of things. 
It can retrieve something, so "download", using the GET message. 
You can also remove something, using the DELETE message.
You can replace something, using the PUT message.
You can create a new item, using POST message.
Let's  'GET' a value of PLN currency
Python already has some built-in modules that allow HTTP communication. We just need to use them.
Our algorithm for now will be:

Define connection - we want to connect to api.nbp.pl website interface
Compose a message query - we need to say what exactly we want, like from which date, which currency etc
Send the message
The program will wait for a response.
Receive the response.
Inspect the contents of response.
Try to extract from the whole message, the information that is interesting for us.
End of explanation
"""


type(response)


dir(response)

# Let's view only the attributes, and not the Python built-in object fields.
attributes = response.__dict__
print(attributes)


# Let's print it better.
response.__dict__


"""
Explanation: HTTP response codes:
https://en.wikipedia.org/wiki/List_of_HTTP_status_codes
Most popular:
200 = OK
404 = NOT FOUND
Let's inspect the response
End of explanation
"""


response.headers

# The format itself:
response.headers['Content-Type']

# The response:
response._content

# Let's inspect the contents.
contents = response._content

type(contents)

len(contents)

"""
Explanation: Now. Some of these are only about the HTTP message:
- status_code: 200 - means OK
- reason: OK  - means OK
- url - Uniform Resource Locator - means which resource we just downloaded
- headers - very important part, it says which headers the RESPONSE message carried
- encoding: utf-8  - means how were the characters encoded, with which special character sets etc
- elapsed: how long did it take for the message to get to us
The response we actually need is:
- _content - contains all response details
One other thing to notice is that, response inside 'content' came in in a specific format. The most often used formats are:
- JSON  -  Javascript Object Notation
- XML - Extensible Markup Language
Our format of response can be seen inside 'headers':
End of explanation
"""


contents = contents.decode('utf-8')
print(contents)

type(contents)

"""
Explanation: That's not quite usable yet. It's an object of type 'bytes'. Let's decode the bytes to string.
End of explanation
"""


import json

contents_from_json = json.loads(contents)
print(contents_from_json)

type(contents_from_json)

"""
Explanation: We are close. Now, to make this string usable at all, let's use Python module designed for using JSON format.
End of explanation
"""


contents_from_json['currency']

currency_value = contents_from_json['rates'][0]['mid']
print(currency_value)


"""
Explanation: Great! Now we can access the various elements of this information using dictionary elements. Let's see how:
End of explanation
"""


import requests
import json

# Address.
address = r"http://api.nbp.pl"
rates_path = "api/exchangerates/rates"

# Resource.
table = "a"
code = "usd"
date = "2017-09-12"

# Message.
resource_address = "/".join([address, rates_path, table, code, date])

response = requests.get(resource_address)

# Work on response.
contents = response._content
contents = contents.decode('utf-8')
contents_from_json = json.loads(contents)

# Print value.
currency_value = contents_from_json['rates'][0]['mid']
print(currency_value)

"""
Explanation: Let's write it all again in a concise way.
End of explanation
"""


import requests

# Address.
address = r"http://api.nbp.pl"
rates_path = "api/exchangerates/rates"

# Resource.
table = "a"
code = "usd"
date = "2017-09-12"

# Message.
resource_address = "/".join([address, rates_path, table, code, date])

# Specify format.
resource_address += "/?format=xml"

response = requests.get(resource_address)

# Work on response.
contents = response._content
contents = contents.decode('utf-8')

print(contents)

type(contents)

"""
Explanation: Now, let's try to do the same thing, but using a different data format - XML. NBP.pl offers also XML responses, but we have to specify this when we send the query message.
End of explanation
"""


# Let's import the XML library, selecting only a small subset module called ElementTree
import xml.etree.ElementTree as ET

contents_tree = ET.fromstring(contents)

type(contents_tree)

# Lets see the root Node.
contents_tree.tag

contents_tree.getchildren()

element_mid = contents_tree.getchildren()[3].getchildren()[0].getchildren()[2]

element_mid.text

"""
Explanation: While our response is in string, it is not much readable. We need to again create a dictionary from this XML. 
We can also use the ElementTree module - XML data is pretty much a 'tree', that we can go through.  For example, we start from the root. Then we visit the branches going out from the root. Then the branches from that branches... etc..
End of explanation
"""


elemend_mid = contents_tree.findall(r".//Mid")
element_mid.text

"""
Explanation: Let's write that again in a more clear way.
End of explanation
"""


import requests
import xml.etree.ElementTree as ET

# Address.
address = r"http://api.nbp.pl"
rates_path = "api/exchangerates/rates"

# Resource.
table = "a"
code = "usd"
date = "2017-09-12"

# Message.
resource_address = "/".join([address, rates_path, table, code, date])

# Specify format.
resource_address += "/?format=xml"

response = requests.get(resource_address)

# Work on response.
contents = response._content
contents = contents.decode('utf-8')

contents_tree = ET.fromstring(contents)
element_mid = contents_tree.findall(r".//Mid")

print(str(element_mid[0].text))

"""
Explanation: Now the final program.
End of explanation
"""


import xml.etree.ElementTree as ET
import json


def extract_value(string_contents, data_format):
    if data_format == 'xml':
        return extract_value_from_xml(string_contents)
    else:
        return extract_value_from_json(string_contents)

    
def extract_value_from_xml(string_contents):
    contents_tree = ET.fromstring(contents)
    element_mid = contents_tree.findall(r".//Mid")
    
    # It returns a list, let's take the only element from inside - 'unpack from list'
    return element_mid[0].text
    

def extract_value_from_json(string_contents):
    contents_from_json = json.loads(contents)
    return contents_from_json['rates'][0]['mid']


def construct_address(table, code, date, data_type):
    data_type = "/?format={0}".format(data_type)
    address = r"http://api.nbp.pl/api/exchangerates/rates"
    return "/".join([address, rates_path, table, code, date, data_type])    

"""
Explanation: To make the program look better and handle both formats, we should refactor it. Let's write more functions.
End of explanation
"""


import requests

# Address.
data_type = "json"
address = construct_address("a", "usd", "2017-09-12", data_type)

response = requests.get(address)

# Work on response.
contents = response._content
contents = contents.decode('utf-8')

value = extract_value(contents, data_type)
print(value)

"""
Explanation: Now, our program looks like this:
End of explanation
"""


