diff --git a/python/introduction/part-4.http/README.md b/python/introduction/part-4.http/README.md new file mode 100644 index 0000000..f8359b8 --- /dev/null +++ b/python/introduction/part-4.http/README.md @@ -0,0 +1,258 @@ +# Introduction to Python: HTTP (Flask) + +So far, we've learnt Python fundamentals, worked with data, files and +more importantly, basic data structures like `csv` and `json`. + +JSON is a very popular format for now only storing, but transporting data in web HTTP applications.
+ +One application could make a web request to ask for customer data from our application. By sending back JSON, the receiving application can easily process the data it gets. + +## Python Dev Environment + +The same as Part 1, we start with a [dockerfile](./dockerfile) where we declare our version of `python`. + +``` +FROM python:3.9.6-alpine3.13 as dev + +WORKDIR /work +``` + +Let's build and start our container: + +``` +cd python\introduction\part-4.http + +docker build --target dev . -t python +docker run -it -v ${PWD}:/work python sh + +/work # python --version +Python 3.9.6 + +``` + +## Our application + +Firstly we have to import our dependencies + +``` +import os.path +import csv +import json + +``` + +Then we have a class to define what a customer looks like: +``` +class Customer: + def __init__(self, c="",f="",l=""): + self.customerID = c + self.firstName = f + self.lastName = l + def fullName(self): + return self.firstName + " " + self.lastName +``` + +Then we need a function which returns our customers: +``` +def getCustomers(): + if os.path.isfile("customers.json"): + with open('customers.json', newline='') as customerFile: + data = customerFile.read() + customers = json.loads(data) + return customers + else: + return {} +``` + +Here is a function to return a specific customer: +``` +def getCustomer(customerID): + customer = getCustomers() + return customer[customerID] +``` +And finally a function for updating our customers: + +``` +def updateCustomers(customers): + with open('customers.json', 'w', newline='') as customerFile: + customerJSON = json.dumps(customers) + customerFile.write(customerJSON) +``` + +Now to recap and test our functions we need to define a list of customers +to use: + +``` +customers = { + "a": Customer("a","James", "Baker"), + "b": Customer("b", "Jonathan", "D"), + "c": Customer("c", "Aleem", "Janmohamed"), + "d": Customer("d", "Ivo", "Galic"), + "e": Customer("e", "Joel", "Griffiths"), + "f": Customer("f", "Michael", "Spinks"), + "g": Customer("g", "Victor", "Savkov"), + "h" : Customer("h", "Marcel", "Dempers") +} +``` + +Now to convert our customer dictionary to JSON, we need to convert our dictionary of customer objects to a dictionary of customers where each customer is a dictionary instead of a class.
+To do this we generate a new dictionary and populate it by converting each customer object to a dictionary : + +``` +customerDict = {} + +for id in customers: + customerDict[id] = customers[id].__dict__ + +``` +Then we can finally test our update and get functionality: + +``` +updateCustomers(customerDict) + +customers = getCustomers() +print(customers) +``` + +Let's test it and see the `customers.json` file stored to our server and +retrieved. + +``` +python src/app.py +``` + +## External Libraries + + + + +## JSON Library + +Python provides a library for dealing with JSON.
+Let's take our customer dictionary, and convert it to json structure and +finally write it to file using our update function. + +``` +import json +``` + +Now first things first, we need to convert our customers dictionary to a JSON structured string so we can store it to file.
+ +The `json` library allows us to convert dictionaries to files using +the `dumps` function: + +``` +jsonData = json.dumps(customers) +print(jsonData) +``` + +But if we run this we get an error: +`TypeError: Object of type Customer is not JSON serializable` + +That is because the library can only deal with full dictionaries. +Taking a closer look at our dictionary, our customer is a class. + +``` +# our key is a string +# our value is a class +"h" : Customer("h", "Marcel", "Dempers") +``` + +`json.dumps()` only allows full dictionaries so we need our customer object to be in dictionary form like so: + +``` +"h": { "customerID": "h", "firstName" : "Marcel", "lastName" : "Dempers"} +``` + +To fix our dictionary, we can iterate it and build a new one + +``` +# start with an empty one +customerDict = {} + +#populate our new dictionary +for id in customers: + customerDict[id] = customers[id].__dict__ + +#print it +print(customerDict) +``` + +Now we can convert our customer dictionary to json + +``` +customerJSON = json.dumps(customerDict) +print(customerJSON) +``` + +Now we've converted our data into JSON, we can view this in a new tab window and change the language mode to JSON to visualise the data in our new format. + +Now essentially our application will use dictionaries when working with the data +but store and transport it as JSON.
+ +Therefore our update function should take a dictionary and convert it to JSON +and store it. + +``` +def updateCustomers(customer): + with open('customers.json', 'w', newline='') as customerFile: + customerJSON = json.dumps(customer) + customerFile.write(customerJSON) +``` + +We also need to change our `getCustomers` function to read the JSON file +and convert that data correctly back to a dictionary for processing. + +``` +def getCustomers(): + if os.path.isfile("customers.json"): + with open('customers.json', newline='') as customerFile: + data = customerFile.read() + customers = json.loads(data) + return customers + else: + return {} +``` + +Finally let's test our functions : + +``` +customers = getCustomers() +print(customers) +``` + +It's also very easy to add a customer to our JSON data using our class and using pythons internal `__dict__` property to convert the object to a dictionary and add it to our customers dictionary + +``` +customers["i"] = Customer("i", "Bob", "Smith").__dict__ +updateCustomers(customers) +``` + +## Docker + +Let's build our container image and run it while mounting our customer file + +Our final `dockerfile` +``` +FROM python:3.9.6-alpine3.13 as dev + +WORKDIR /work + +FROM dev as runtime +COPY ./src/ /app + +ENTRYPOINT [ "python", "/app/app.py" ] + +``` + +Build and run our container. +Notice the `customers.json` file gets created if it does not exist. + +``` +cd python\introduction\part-3.json + +docker build . -t customer-app + +docker run -v ${PWD}:/work -w /work customer-app + +``` \ No newline at end of file diff --git a/python/introduction/part-4.http/dockerfile b/python/introduction/part-4.http/dockerfile new file mode 100644 index 0000000..7666e03 --- /dev/null +++ b/python/introduction/part-4.http/dockerfile @@ -0,0 +1,8 @@ +FROM python:3.9.6-alpine3.13 as dev + +WORKDIR /work + +FROM dev as runtime +COPY ./src/ /app + +ENTRYPOINT [ "python", "/app/app.py" ] \ No newline at end of file diff --git a/python/introduction/part-4.http/src/app.py b/python/introduction/part-4.http/src/app.py new file mode 100644 index 0000000..f0a986e --- /dev/null +++ b/python/introduction/part-4.http/src/app.py @@ -0,0 +1,51 @@ +import os.path +import csv +import json + +class Customer: + def __init__(self, c="",f="",l=""): + self.customerID = c + self.firstName = f + self.lastName = l + def fullName(self): + return self.firstName + " " + self.lastName + +def getCustomers(): + if os.path.isfile("customers.json"): + with open('customers.json', newline='') as customerFile: + data = customerFile.read() + customers = json.loads(data) + return customers + else: + return {} + +def getCustomer(customerID): + customer = getCustomers() + return customer[customerID] + +def updateCustomers(customers): + with open('customers.json', 'w', newline='') as customerFile: + customerJSON = json.dumps(customers) + customerFile.write(customerJSON) + + +customers = { + "a": Customer("a","James", "Baker"), + "b": Customer("b", "Jonathan", "D"), + "c": Customer("c", "Aleem", "Janmohamed"), + "d": Customer("d", "Ivo", "Galic"), + "e": Customer("e", "Joel", "Griffiths"), + "f": Customer("f", "Michael", "Spinks"), + "g": Customer("g", "Victor", "Savkov"), + "h" : Customer("h", "Marcel", "Dempers") +} + +customerDict = {} + +for id in customers: + customerDict[id] = customers[id].__dict__ + +updateCustomers(customerDict) + +customers = getCustomers() +print(customers) \ No newline at end of file