Server Side Intro

In this lab we shall be introducing the python language and flask framework for building application servers

Task 1

Just as in lab 1 follow the link below to open repl.it. Fork the project then sign in.

Open Lab 1

Variables Assignment & Operators

Python, like javascript, is dynamically typed. There is no keyword for declaring or initialising variables, therefore an assignment looks just like an initialization in python.

#This is how comments are written

x = 10 #initialization
x = 23 #assignment
y #declaration
z = ( x + y)/x + (78%3_) #usual mathematical operations supported 

Primitive Types and Strings

Our primitive data types are as follows:

name = "bobby" #string
age = 12 #integer
height = 6.5 # float
hasDate = False
comp = 7j #complex

# using fstrings for interpolation

message = f'Hi my name is {name} I am {age} years old'
print(message) # ‘Hi my name is bobby I am 12 years old'

# type casting to convert types
intHeight = int(height) # 6
strHeight = str(height) # '6'
floatHeight = float(intHeight) # 6.0 
ageType = type(age)

Input Output

name  = input("Enter name: ") # reads input
print (name) # prints output

Comparison

If statements do not use () or {} instead : and indentation is used to delimit the condition and scope.

#correct
if 3 > 5:
  print("more")
else :
 print("less")

#wrong
if 3 > 5: print ("more")
else : print ("less")

# elif === else if
mark = input("Enter mark: ")
mark = int(mark)
if mark > 70 :
  print("A")
elif mark > 60:
  print("B")
elif mark > 50:
  print("C")
else :
  print("F") 

Iteration

i = 1
while i < 10:
 print(i)
 i+=1
else:
 print("This is run when the loop condition is no longer met")

# iterating an iterable such as a list
list = ["bob", "sally", "john"]
for j in list:
 print(j)

# iterating between custom range an increment
for i in range(0, 10, 2):
 print(i)

Functions

#basic function
def add(a, b):
        return a + b

def printPerson(name, age, height):
        print(name, age, height)

# you can specify arguments in any order if they are named
printPerson(age=12, name='bob', height=5);

#default arguments are used when they are not given in the function call
def sayHello(fname, lname='Smith'):
        print('Hello '+fname + ' ' + lname)

sayHello('John');

sayHello('Bill', 'Young');

# functions can return multiple values

Lists

Lists are your arrays in python, they can include elements of different data types.

list = ['item1', 'item2', 'item3']
list2 = [12, 33, 45, 58, 23]

print(list);
# negative indexing can access elements starting from the end
print(list2[-1])

# select a subset of a list
print(list2[2:4])

# gets the length of a list
print(len(list2))

#add items to list
list.append('item4');

#remove item from list
item4 = list.pop()

#copy list
list3 = list2.copy()

# list comprehension, lets you create new lists from existing lists

num = [ 1, 2, 3, 4]
doubled = [n*2 for n in num]
print(doubled) # [ 2, 4, 6, 8]
odd = [ n for n in num if n%2 == 1]
print(odd) # [ 1, 3]

# unpacking a list, lets you create variables from items in the list
num = [ 1, 2, 3, 4]
[first, second, *rest] = num
print(first)
print(second)
print(rest)
# joining lists
num2 = [5, 6]
num3 = num + num2
print(num3) # [1, 2, 3, 4, 5, 6]

# copying lists
num4 = num2.copy()

Tuples

Tuples are collections that are ordered and unchangeable, because of this they are faster than lists.

thistuple = ("apple", "banana", "cherry", "apple", "cherry")
print(thistuple); # ('apple', 'banana', 'cherry', 'apple', 'cherry')
print(thistuple[0); # ‘apple'

Sets

Sets are collections that do not allow duplicates, they are useful for algorithms that need to keep track of unique occurrences.

data = [ 20, 3, 20, 42, 2, 3, 10, 32, 2]

myset = {0, 1}

for num in data:
 myset.add(num)

print(myset)# {0, 1, 2, 3, 32, 42, 10, 20}
num_unique = len(myset)

There's also a number of useful methods available on sets such as issubset(), intersection() and difference().

Dictionaries

Dictionaries are the python equivalent of Javascript's object literals. They are simply key value pairs and work

mydict = {
        "name":"bob",
        "age": 34
}

print(mydict)

# assessing a key
print(mydict['age'])

# adding a new key and value
mydict['height'] = 7

# iterating keys
for key in mydict:
        print(key)

# iterating values
for key in mydict:
        print(mydict[key])

# check for a key
if 'weight' in mydict:
        print(mydict['weight'])
else:
        print('no weight property!')

Classes

Classes allow us to specify a blueprint for creating instances/objects with state and behaviour in the form of properties and methods.

They differ from dictionaries in that they can exhibit OOP such as inheritance and polymorphism. We use the "." to access an objects' properties while we use [] to access the values of a dictionary for a given key.

#Parent class
class Person:

  def __init__(self, name, height, weight):
    self.name = name;
    self.height = height;
    self.weight = weight;

  def sayHello(self):
    print("Hello! I'm a person, my name is", self.name)

# Child class inherits from Person
class Student(Person):

        # super is the reference to the parent class Person so 
        # we call Person's constructor here to set the Person
        # properties of the student instance
        def __init__(self, stid, name, height, weight):
                super().__init__(name, height, weight)
                self.stid = stid
        
        # override method of parent
        def sayHello(self):
                print("Hello! I'm a student, my name is", self.name)


bob = Person('bob', 12, 34)
sally = Student(123, 'sally', 7, 34)

bob.sayHello();
sally.sayHello();

print(bob.name);

Flask is a backend framework for creating application servers in python. As was discussed in the lecture, application servers are just programs which sit on the network, receive requests on a set of specific urls and return http responses.

Flask allows us to write routes which are just functions that are executed based on the url of the requests it receives.



Task 2

Just follow the instructions above please.

Open Lab 1.5

For this lab we will implement routes that facilitate GET requests. This means these routes only allow clients to get data as opposed to sending, deleting or updating.

Task 3

Update main.py with the following

from flask import Flask, request, jsonify
import json

app = Flask(__name__)

global data

# read data from file and store in global variable data
with open('data.json') as f:
        data = json.load(f)

@app.route('/')
def hello_world():
    return 'Hello, World!' # return 'Hello World' in response

@app.route('/students')
def get_students():
    return jsonify(data)# return student data in response


app.run(host='0.0.0.0', port=8080)

Restart/Run the application and open a separate browser tab to https://./students you should see the data of the server's response.

If you point your browser to chrome://extensions you can enable the JSON View which will pretty print the data.

We can configure our routes to treat part of the url path as variables to take as input into the logic of the function. We usually do this to specify an id in our url some examples:

Note the anchor is only available in the browser, it will not be sent to the server side environment.

Task 4

Update main.py with the following route to receive a student id in the url so that we can return data for only one student.

# route variables
@app.route('/students/<id>')
def get_student(id):
  for student in data: 
    if student['id'] == id: # filter out the students without the specified id
      return jsonify(student)

View the result by navigating to https://./students/STD0001

For more advanced querying such as ranged queries, searching and filtering we use query parameters as input on the url. It is typical to design query parameters as optional such that there is a fallback response if none are supplied by the user.

Most backend frameworks provide an abstraction for accessing the http request as well as providing an http response. In flask we use the Request and Response objects respectively.

Task 5

Update the get_students function in main.py to allow a query parameter ‘pref' that filters the dataset by a specified meal preference.

@app.route('/students')
def get_students():
  result = []
  pref = request.args.get('pref') # get the parameter from url
  if pref:
    for student in data: # iterate dataset
      if student['pref'] == pref: # select only the students with a given meal preference
        result.append(student) # add match student to the result
    return jsonify(result) # return filtered set if parameter is supplied
  return jsonify(data) # return entire dataset if no parameter supplied

Now point the browser to https://./students?pref=Chicken

You should see only the students with the chicken meal preference.

Congratulations you have just built your first application server with flask! In the next lab we shall explore modelling, handling and storing long term data.

Exercise 1

Create a route /stats which returns a count of the various meal preferences and programmes in the data set eg.

Exercise 2

Create a routes; /add/a/b, /subtract/a/b, multiply/a/b and divide/a/b that takes in 2 route parameters a and b and returns the result of the calculation.

References & Additional Reading