├── .gitignore ├── setup ├── linux.md ├── windows.md ├── mac_osx.md └── Readme.md ├── sessions ├── extras │ ├── authentication_in_rest │ │ └── Readme.md │ ├── pip_and_pypi │ │ └── Readme.md │ ├── Readme.md │ └── rest_theory │ │ └── Readme.md ├── python_three │ ├── finish.md │ ├── Readme.md │ ├── project.md │ ├── structure.md │ ├── done.md │ ├── interface.md │ ├── list.md │ └── create.md ├── python_one │ ├── Readme.md │ ├── cheatsheet.md │ ├── functions.md │ ├── variables.md │ ├── loops.md │ └── advanced_data_structures.md ├── python_two │ ├── Readme.md │ ├── standard_library.md │ ├── json.md │ └── random.md └── rest_fundamentals │ ├── Readme.md │ ├── next_steps.md │ ├── what_is_an_api.md │ ├── postman.md │ ├── requests.md │ └── what_is_REST.md ├── res ├── new_request.png ├── first_request.png ├── first_response.png ├── postman_editor.png ├── postman_interface.png └── rest_schematics.png └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /setup/linux.md: -------------------------------------------------------------------------------- 1 | # Setup your Linux for Programmability -------------------------------------------------------------------------------- /setup/windows.md: -------------------------------------------------------------------------------- 1 | # Setup your Windows for Programmability -------------------------------------------------------------------------------- /setup/mac_osx.md: -------------------------------------------------------------------------------- 1 | # Setup your Mac for Programmability 2 | 3 | -------------------------------------------------------------------------------- /sessions/extras/authentication_in_rest/Readme.md: -------------------------------------------------------------------------------- 1 | # Authentication in REST 2 | 3 | -------------------------------------------------------------------------------- /sessions/extras/pip_and_pypi/Readme.md: -------------------------------------------------------------------------------- 1 | # PIP, PyPi and the Python Package Ecosystem -------------------------------------------------------------------------------- /res/new_request.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sQu4rks/intro_to_programmability/HEAD/res/new_request.png -------------------------------------------------------------------------------- /res/first_request.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sQu4rks/intro_to_programmability/HEAD/res/first_request.png -------------------------------------------------------------------------------- /res/first_response.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sQu4rks/intro_to_programmability/HEAD/res/first_response.png -------------------------------------------------------------------------------- /res/postman_editor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sQu4rks/intro_to_programmability/HEAD/res/postman_editor.png -------------------------------------------------------------------------------- /res/postman_interface.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sQu4rks/intro_to_programmability/HEAD/res/postman_interface.png -------------------------------------------------------------------------------- /res/rest_schematics.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sQu4rks/intro_to_programmability/HEAD/res/rest_schematics.png -------------------------------------------------------------------------------- /sessions/extras/Readme.md: -------------------------------------------------------------------------------- 1 | # Additional Content 2 | 3 | Programmability, APIs and python are a vast field. Too big to be covered in just 4 hours of webinars so here you can find additional sessions on topics that we just couldn't fit into the schedule: 4 | 5 | ### REST API Fundamentals 6 | 7 | * [REST API Theoretical Deep Dive](rest_theory/Readme.md) 8 | * [PIP, PyPi and the Python Package Ecosystem](pip_and_pypi/Readme.md) 9 | * [Authentication in REST](authentication_in_rest/Readme.md) -------------------------------------------------------------------------------- /sessions/python_three/finish.md: -------------------------------------------------------------------------------- 1 | # That's It! 2 | 3 | Congratulations! You wrote your first little application in python. Feel free to expand on this 4 | example. Functionality you could add includes 5 | 6 | * Checking that the id really does not exist when creating a new todo item 7 | * Providing a function to update a existing todo item 8 | * Allow different types of fields 9 | * Check that a todo item with a provided id actually exists when deleting a item 10 | 11 |
12 | 13 | [Prev](done.md) - [Next](/sessions/rest_fundamentals/Readme.md) 14 |
-------------------------------------------------------------------------------- /sessions/python_three/Readme.md: -------------------------------------------------------------------------------- 1 | # Introduction to Python - Part III 2 | 3 | Python is one of the most popular programming languages of our time and the powerhouse behind most of Cisco’s API efforts. If there is a Cisco API chances are high that there will be a software development kit for that API in python. 4 | 5 | In the final part of this three-parted training we will use the knowledge acquired over the past weeks to build a small python project from start to finish. 6 | 7 |
8 | 9 | [Prev](/sessions/python_two/json.md) - [Next](project.md) 10 |
-------------------------------------------------------------------------------- /sessions/python_one/Readme.md: -------------------------------------------------------------------------------- 1 | # Introduction to Python - Part I 2 | 3 | Python is one of the most popular programming languages of our time and the powerhouse behind most of Cisco’s API efforts. If there is a Cisco API chances are high that there will be a software development kit for that API in python. 4 | 5 | In part one of this three-parted training we will start from scratch and get familiar with the basic concepts of python like variables, loops and functions. 6 | 7 | So lets get started! 8 | 9 |
10 | 11 | [Prev](/README.md) - [Next](variables.md) 12 |
13 | -------------------------------------------------------------------------------- /sessions/python_two/Readme.md: -------------------------------------------------------------------------------- 1 | # Introduction to Python - Part II 2 | 3 | Python is one of the most popular programming languages of our time and the powerhouse behind most of Cisco’s API efforts. If there is a Cisco API chances are high that there will be a software development kit for that API in python. 4 | 5 | In part two of this three-parted training we will build on the basics from last week to explore some more advanced data structures like dictionaries and sets. We will also get acquainted with some of the useful modules that come included with the python standard library. 6 | 7 | 8 | So lets get started! 9 | 10 |
11 | 12 | [Prev](/sessions/python_one/Readme.md) - [Next](standard_library.md) 13 |
14 | -------------------------------------------------------------------------------- /sessions/rest_fundamentals/Readme.md: -------------------------------------------------------------------------------- 1 | # REST API Fundamentals 2 | APIs, short for Application Programming Interfaces, have fundamentally changed the way software interacts with each other. By providing a standardized way on how to interact with another system we can build complex integrations with ease. 3 | 4 | REST (Representational state transfer) is the most common API framework based on HTTP(S) and an essential part of Programmability. In this session we will provide you with the technical background on REST API as well as a practical introduction on how to consume and use REST APIs with python. 5 | 6 | So lets get started! 7 | 8 |
9 | 10 | [Prev](/sessions/python_three/finish.md) - [Next](what_is_an_api.md) 11 |
-------------------------------------------------------------------------------- /sessions/python_three/project.md: -------------------------------------------------------------------------------- 1 | # Our Project for today 2 | 3 | As our first little python project we are going to build a small todo manager app. 4 | 5 | The requirements and functionalities of our app are as follows: 6 | 7 | * Allows the user to create, list and mark as done todo items 8 | * A todo item has a title, id and priority 9 | * Todos are stored as json files in a `todos/` directory 10 | * Each of the core functionalities (create, list, mark as done) are implemented as functions 11 | * Creating a new todo item will create a new file in the `todos/` directory 12 | * Listing todo items will list all files in the `todos/` directory 13 | * Marking a todo item as done will delete the file in the `todos` directory 14 | 15 | Let's get started! 16 | 17 |
18 | 19 | [Prev](Readme.md) - [Next](structure.md) 20 |
-------------------------------------------------------------------------------- /setup/Readme.md: -------------------------------------------------------------------------------- 1 | # Setup for Computer for Programmability 2 | 3 | This series so far has used the online editor at [repl.it](https://repl.it/languages/python3). While this is convenient we might want to have our own local development environment that we can use for our projects. 4 | 5 | In this additional series we will cover how to setup your windows, Mac or Linux system 6 | for development with the python programming language. 7 | 8 | We will install: 9 | 10 | * **The python interpreter** so that you can run python programs 11 | * **Essential python packages** 12 | * **Postman** to interact with and try out REST APIs in a graphical interface 13 | * **Visual Studio Code** as an editor with code highlighting and auto-complete functionality 14 | 15 | Below you can find links to the guide for your operating system. 16 | 17 | * [Setup your Mac for Programmability](mac_osx.md) 18 | * [Setup your Windows for Programmability](windows.md) 19 | * [Setup your Linux for Programmability](linux.md) -------------------------------------------------------------------------------- /sessions/rest_fundamentals/next_steps.md: -------------------------------------------------------------------------------- 1 | # Next Steps 2 | 3 | So what's next? Now that you have a overview of what's possible with APIs we encourage you to explore on your own. 4 | 5 | First, you'll need to setup your computer for development. Cisco DevNet provides a step-by-step guide on how to do this for all platforms: 6 | 7 | * [Setup for Windows](https://developer.cisco.com/learning/devnet-express/devnet-express-meraki/dev-setup/dev-win/step/1) 8 | * [Setup for Mac](https://developer.cisco.com/learning/devnet-express/devnet-express-meraki/dev-setup/dev-mac/step/1) 9 | * [Setup for Linux (CentOS)](https://developer.cisco.com/learning/devnet-express/devnet-express-meraki/dev-setup/dev-centos/step/1) 10 | * [Setup for Linux (Ubuntu)](https://developer.cisco.com/learning/devnet-express/devnet-express-meraki/dev-setup/dev-ubuntu/step/1) 11 | 12 | In the coming weeks of this PIW we will now go ahead and cover use cases for each of our architectures as well as technical introductory sessions that will train you on the specific APIs for those products. 13 | 14 |
15 | 16 | [Prev](requests.md) - Next 17 |
-------------------------------------------------------------------------------- /sessions/python_three/structure.md: -------------------------------------------------------------------------------- 1 | # The Structure 2 | 3 | Lets start by writing out the structure of our program. Based on our requirements we will 4 | have four functions: 5 | 6 | * `createItem()` to create a new todo item 7 | * `listItems()` to list all items that we currently have in our todo system 8 | * `markItemDone()` to mark a item as done 9 | * `interface()` to provide a commandline interface for the user to interact with our system 10 | 11 | The basic structure of our program thus looks like this: 12 | 13 | ```python 14 | 15 | def createItem(): 16 | print("Create a new ToDo item") 17 | 18 | def listItems(): 19 | print("List all ToDo items") 20 | 21 | def markItemDone(): 22 | print("Mark item as done") 23 | 24 | def interface(): 25 | print("Welcome to our todo application") 26 | 27 | interface() 28 | ``` 29 | 30 | Above code defines a function for each of the functionalities we want to provide and then calls the 31 | `interface()` function for the user to interact with our application. 32 | 33 | Next, let's write the user interface. 34 | 35 |
36 | 37 | [Prev](project.md) - [Next](interface.md) 38 |
-------------------------------------------------------------------------------- /sessions/python_three/done.md: -------------------------------------------------------------------------------- 1 | # Marking an item as done 2 | 3 | In order to mark a item as done we will need to get the id from the user. To do that we can use our `input` function again. 4 | 5 | ```python 6 | def markItemDone(): 7 | print("Mark item as done") 8 | 9 | todo_id = input("Id: ") 10 | ``` 11 | 12 | Based on this we can create the path of said todo item. Looking at the [documentation for the os module](https://docs.python.org/3/library/os.html#module-os) again shows that there is a `os.remove()` function. Let's use that function to delete the todo item. 13 | 14 | ```python 15 | 16 | todo_path = "todos/" + str(todo_id) + ".todo" 17 | os.remove(todo_path) 18 | print("Removed todo item with id " + str(todo_id)) 19 | ``` 20 | 21 | Put together our `markItemDone` function then looks like this: 22 | 23 | ```python 24 | def markItemDone(): 25 | print("Mark item as done") 26 | 27 | todo_id = input("Id: ") 28 | 29 | todo_path = "todos/" + str(todo_id) + ".todo" 30 | os.remove(todo_path) 31 | print("Removed todo item with id " + str(todo_id)) 32 | ``` 33 | 34 |
35 | 36 | [Prev](list.md) - [Next](finish.md) 37 |
-------------------------------------------------------------------------------- /sessions/rest_fundamentals/what_is_an_api.md: -------------------------------------------------------------------------------- 1 | # What is an API? 2 | So what exactly is an API? The acronym stands for ***A**pplication **P**rogramming **I**nterface*. 3 | 4 | Academically speaking an API is 5 | 6 | > [...] a set of functions and procedures allowing the creation of applications that access the features or data of […] [an]other service. 7 | 8 | In more down to earth terms: 9 | 10 | > It’s a way for two pieces of software to talk to each other 11 | 12 | So APIs allow us to have *machines talk to machines* instead of needing humans to retrieve information and then feeding them back to the machine. This kind of access enables a variety of use cases from monitoring and automating to delivering completely new products build on-top of others. 13 | 14 | In this capacity, APIs can have an not insignificant impact on innovation. Imagine every startup having to build their own *worldwide* mapping service instead of being able to leverage existing services and integrate or build on top of them. We would have far less startups and far less agility and innovation. 15 | 16 | Need to be sold more? For infrastructure, APIs allow us to automate the boring tasks or personalize said infrastructure to deliver a experience that more closer solves a pain point. So API could also be an acronym for *Ability to Personalize Infrastructure* and who doesn't want personalized infrastructure without the added cost of personalizing infrastructure for each and every user? 17 | 18 | So how to implement this technically? We need a standardized way for machines to talk 19 | that is both easy to use and powerful. This is what the *REST* standard is for and this is also the standard that we will explore in this session. 20 | 21 |
22 | 23 | [Prev](Readme.md) - [Next](what_is_REST.md) 24 |
25 | -------------------------------------------------------------------------------- /sessions/python_two/standard_library.md: -------------------------------------------------------------------------------- 1 | # The Standard Library 2 | 3 | The python programming language is often called *batteries included*. 4 | This means that, with the standard installation, a lot of functionality has been 5 | provided already. 6 | 7 | These pieces of code that can be reused by your script are called **modules**. In this 8 | section we will have a look at the modules that come included with python, the so-called 9 | *standard library*. 10 | 11 | ## Importing Modules and using module functions 12 | 13 | In order to use functions developed by other people in modules we will need to import them. 14 | In python this is done with the `import` statement. These statements, in theory, can be 15 | everywhere in the code but it is good practice to put these at the top of your script. 16 | 17 | ```python 18 | 19 | import test_module 20 | ``` 21 | 22 | Above (non-functioning) code would import the module `test_module`. Assuming that the `test_module` 23 | defines a function called `test_function` we could now go ahead and use this function like this: 24 | 25 | ```python 26 | 27 | import test_module 28 | 29 | test_module.test_function("This is input") 30 | ``` 31 | 32 | Sometimes you only want to import one function from a module instead of loading the entire module. You can also do this using the following snippet. 33 | 34 | ```python 35 | 36 | from test_module import test_function 37 | ``` 38 | 39 | Calling the `test_function` now works like this: 40 | 41 | ```python 42 | 43 | from test_module import test_function 44 | 45 | test_function("This is also input") 46 | ``` 47 | 48 | With this knowledge, lets explore the standard library and put these imports to good use! 49 | 50 |
51 | 52 | [Prev](/sessions/python_one/Readme.md) - [Next](random.md) 53 |
54 | 55 | 56 | -------------------------------------------------------------------------------- /sessions/python_three/interface.md: -------------------------------------------------------------------------------- 1 | # The User Interface 2 | 3 | ## Getting inputs from the user 4 | 5 | So far we have always only returned information to the user by printing them. Often we want to get input 6 | from the user however. In python this can be achieved using the build-in `input()` function. 7 | 8 | ```python 9 | 10 | name = input("What is your name? ") 11 | print("The name is " + name) 12 | ``` 13 | 14 | ## Writing our interface 15 | 16 | So for our interface function lets get started by printing out some basic textual information and then 17 | ask the user which command they would like to use: 18 | 19 | ```python 20 | 21 | def interface(): 22 | print("Welcome to our todo application") 23 | print("What would you like to do?") 24 | print("-> create: create a new todo item") 25 | print("-> list: list all todo items") 26 | print("-> done: mark a todo item as done") 27 | ``` 28 | 29 | This little part just prints out the commands that are available to the user. Let's prompt for a command to be carried out: 30 | 31 | ```python 32 | 33 | cmd = input("Command: ") 34 | ``` 35 | 36 | `cmd` now contains the string provided by the user. We can now use `if-elif-else` conditionals to invoke the correct function: 37 | 38 | ```python 39 | 40 | if cmd == "create": 41 | createItem() 42 | elif cmd == "list": 43 | listItems() 44 | elif cmd == "done": 45 | markItemDone() 46 | else: 47 | print("I don't understand the command") 48 | ``` 49 | 50 | Put together, this is how our `interface` function then looks like: 51 | 52 | ```python 53 | 54 | def interface(): 55 | print("Welcome to our todo application") 56 | print("What would you like to do?") 57 | print("-> create: create a new todo item") 58 | print("-> list: list all todo items") 59 | print("-> done: mark a todo item as done") 60 | 61 | cmd = input("Command: ") 62 | 63 | if cmd == "create": 64 | createItem() 65 | elif cmd == "list": 66 | listItems() 67 | elif cmd == "done": 68 | markItemDone() 69 | else: 70 | print("I don't understand the command") 71 | ``` 72 | 73 | Next, we will be creating the `createItem()` functionality. 74 | 75 |
76 | 77 | [Prev](structure.md) - [Next](create.md) 78 |
-------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Introduction to Programmability with python 2 | 3 | Welcome to the "Introduction to Programmability with python" series. In this repository you will find a written version of the content covered during our 4 technical fundamentals sessions. 4 | 5 | This content is intended for you to follow along and try out. Programming is learned by doing so each section includes tips and tricks, common pitfalls and exercises with solutions for you to solve. 6 | 7 | > :warning: This is a common pitfall or warning. 8 | 9 | > :wrench: Here you will find some background information. 10 | 11 | > :computer: This is an exercises. They might contain sample code that you need to extend upon like this: 12 | > 13 | > Modify the code so that it says hello and includes your name 14 | > ```python3 15 | > print("Hello") 16 | > ``` 17 | 18 | For these exercises you will find solutions. You can view the solution by clicking on the text 19 | 20 |
21 | Click here to show solution 22 | 23 | ```python3 24 | print("Hello Marcel") 25 | ``` 26 |
27 | 28 | > :rocket: This section lists additional content you can read or watch on the topic 29 | > presented. 30 | 31 | ## Sessions 32 | 33 | ### Introduction to Python 34 | Python is one of the most popular programming languages of our time and the powerhouse behind most of Cisco’s API efforts. If there is a Cisco API chances are high that there will be a software development kit for that API in python. 35 | 36 | In part one of this three-parted training we will start from scratch and get familiar with the basic concepts of python like variables, loops and functions. 37 | [Go to session](sessions/python_one/Readme.md) 38 | 39 | In part two of this three-parted training we will build on the basics from the last session to explore some more advanced data structures like dictionaries and sets. We will also get acquainted with some of the useful modules that come included with the python standard library. [Go to session](sessions/python_two/Readme.md) 40 | 41 | In the final part of this three-parted training we will use the knowledge acquired over the past sessions to build a small python project from start to finish [Go to session](sessions/python_three/Readme.md) 42 | 43 | ### REST API Fundamentals 44 | REST (Representational state transfer) is the most common API framework based on HTTP(S) and an essential part of Programmability. In this session we will provide you with the technical background on REST API as well as a practical introduction on how to consume and use REST APIs with python. [Go to session](sessions/rest_fundamentals/Readme.md) 45 | 46 |
47 | 48 | Prev - [Next](sessions/python_one/Readme.md) 49 |
50 | -------------------------------------------------------------------------------- /sessions/python_two/json.md: -------------------------------------------------------------------------------- 1 | # JSON 2 | 3 | JSON, stands for *JavaScript Object Notation* and is a popular text-based exchange format. We will learn more about the structure of JSON next week as well as during our REST API Fundamentals session. For now, let's see how we can read and write json from/to a dictionary in python. 4 | 5 | First, we need to import the `json` module. This module is part of the standard library so you don't need to install anything! 6 | 7 | ```python 8 | 9 | import json 10 | ``` 11 | 12 | ## Turning a python dict into json 13 | 14 | First we will need a python dictionary that we can then convert into a json string. Then we call the `json.dumps()` function. 15 | 16 | ```python 17 | 18 | import json 19 | 20 | d = {} 21 | 22 | d["first_name"] = "Marcel" 23 | d["last_name"] = "Neidinger" 24 | d["mail"] = "mneiding@cisco.com" 25 | 26 | string_representation = json.dumps(d) 27 | print(string_representation) 28 | ``` 29 | 30 | `json.dumps()` takes a python dictionary and dumps it into a string. 31 | 32 | ## Turning a json string into a python dictionary 33 | 34 | We can also do the reverse operation and turn a (correctly formatted) json string into a python dictionary. 35 | 36 | ```python 37 | 38 | import json 39 | 40 | json_str = """ 41 | {"first_name": "Marcel", "last_name": "Neidinger", "mail": "mneiding@cisco.com"} 42 | """ 43 | 44 | d = json.loads(json_str) 45 | 46 | print(str(d)) 47 | ``` 48 | 49 | > :computer: Write a script that imports the json module and saves a dictionary as a string. The dictionary will contain the first name(dictionary key: `first_name`), last name (dictionary key: `last_name`) and age (dictionary key: `age`) of a person. The first and last name should be randomly drawn from a list of names while the age should be a random integer in the range 18 to 99. 50 | > 51 | > You can find the list of first and last names in the code skeleton below 52 | > 53 | > ```python 54 | > #ToDo: imports 55 | > 56 | > first_names = ["Nadja", "Nick", "Lucy", "Howie", "Sandy", "AJ", "Vanessa", "Brian", "Jessica", "Kevin"] 57 | > last_names = ["Smith", "Johnson", "Brown", "Miller", "Garcia", "Acors", "Alday"] 58 | > 59 | > # ToDo: Create dictionary 60 | > 61 | > # ToDo: populate dictionary with information 62 | > 63 | > # ToDo: Get a json representation of the dictionary using the json module 64 | > string_representation = ? 65 | > 66 | > print(string_representation) 67 | > ``` 68 | 69 |
70 | Click here to show solution 71 | 72 | ```python3 73 | import random 74 | import json 75 | 76 | first_names = ["Nadja", "Nick", "Lucy", "Howie", "Sandy", "AJ", "Vanessa", "Brian", "Jessica", "Kevin"] 77 | last_names = ["Smith", "Johnson", "Brown", "Miller", "Garcia", "Acors", "Alday"] 78 | 79 | d = {} 80 | 81 | d["first_name"] = random.choice(first_names) 82 | d["last_name"] = random.choice(last_names) 83 | d["age"] = random.randint(18, 99) 84 | 85 | string_representation = json.dumps(d) 86 | 87 | print(string_representation) 88 | ``` 89 |
90 | 91 | With that, we have covered two modules of the standard library we will be using next week! 92 | 93 |
94 | 95 | [Prev](random.md) - [Next](/sessions/python_three/Readme.md) 96 |
-------------------------------------------------------------------------------- /sessions/python_three/list.md: -------------------------------------------------------------------------------- 1 | # Listing all ToDo Items 2 | 3 | In order to list items we will need to interact with the file system of our system. Basically we need to find a module that allows us to list all files in a directory. 4 | 5 | ## Exploring the Python Standard Library 6 | You haven't missed something! So far we did not introduce that library. As mentioned in last weeks session the python standard library includes a comprehensive documentation so lets have a look at how we can find the correct module and function. 7 | 8 | First, go to the [python standard library documentation](https://docs.python.org/3/library/). Here we can find a long list of 9 | everything that is included. All modules are categorized. The *File and Directory Access* category looks interesting for what we 10 | are trying to achieve. 11 | 12 | At the bottom there is a yellow box that mentions the `os` module. The description specifies that `os` allows us to "[Use] Operating system interfaces, including functions to work with files at a lower level than Python file objects". Let's navigate to the [os module documentation](https://docs.python.org/3/library/os.html#module-os). Here we find a list of all the functions that this module provides. 13 | 14 | Let's search the page for something like `listdir`. Doing so reveals the `os.listdir()` function that takes a optional `path` argument and returns a list of all files and folders in that `path`. Exactly what we were looking for. 15 | 16 | So lets add the `os` module to our list of imported modules: 17 | 18 | ```python 19 | import os 20 | ``` 21 | 22 | ## Retrieving our todos from the files 23 | Next, lets list all files in our `todos/` folder and create a read-only file handler for each of them: 24 | 25 | ```python 26 | 27 | def listItems(): 28 | print("List all ToDo items") 29 | 30 | for todo_file_name in os.listdir("todos/"): 31 | todo_path = "todos/" + todo_file_name 32 | 33 | file_handler = open(todo_path, "r") 34 | ``` 35 | 36 | The `json` module does not only allow us to write directly to a file handler but also allows us to load directly from one. For this we can use the `json.load` function. Mind the missing "s" at the end of the function name. 37 | 38 | ``` 39 | item = json.load(file_handler) 40 | ``` 41 | 42 | We now have a dict called `item` that contains our todo item. We can use this to print out the information to the user. 43 | 44 | ```python 45 | print("Id: " + str(item['id'])) 46 | print("-> Title: " + str(item['title'])) 47 | print("-> Priority: " + str(item['priority'])) 48 | ``` 49 | 50 | The complete function then looks like this: 51 | 52 | ```python 53 | def listItems(): 54 | print("List all ToDo items") 55 | 56 | for todo_file_name in os.listdir("todos/"): 57 | todo_path = "todos/" + todo_file_name 58 | 59 | file_handler = open(todo_path, "r") 60 | item = json.load(file_handler) 61 | 62 | print("Id: " + str(item['id'])) 63 | print("-> Title: " + str(item['title'])) 64 | print("-> Priority: " + str(item['priority'])) 65 | ``` 66 | 67 | Last but not least we now need a functionality to mark an todo as done. Marking as done for us simply means deleting the item. 68 | 69 |
70 | 71 | [Prev](create.md) - [Next](done.md) 72 |
-------------------------------------------------------------------------------- /sessions/python_one/cheatsheet.md: -------------------------------------------------------------------------------- 1 | # Cheatsheet for Python Programmability - Part 1 2 | 3 | This cheatsheet aims to provide a quick recap of all we have covered in part one of our three-part series on programmability with python. You can find the full version of the write up [here](/README.md). 4 | 5 | ## Variables 6 | [Link to Section](variables.md) 7 | * Variables have a name and are assigned with the `=` operator. Example: `a = 10` 8 | * Variables have data types that define what kind of values they hold. 9 | * The data types are: 10 | * `string` for text 11 | * `int` for whole numbers 12 | * `boolean` for boolean(true/false) 13 | * `float` for floating point numbers 14 | * `None` for nothing 15 | 16 | ```python 17 | 18 | an_integer = 10 19 | an_float = 50.0 20 | text = "This is a text" 21 | an_boolean = True 22 | an_nothing = None 23 | ``` 24 | 25 | * You can convert(or *cast*) between variables of different data types 26 | 27 | ```python 28 | 29 | a = 10 30 | a_as_text = str(a) 31 | ``` 32 | * The conversion functions are 33 | * `str()` to convert to string 34 | * `bool()` to convert to boolean 35 | * `int()` to convert to integer 36 | * `float()` to convert to floating point number 37 | 38 | ## Loops 39 | [Link to Section](loops.md) 40 | * We can use `for` loops to execute a operation multiple times 41 | 42 | ```python 43 | for a in range(0, 10): 44 | print("Iteration #" + str(a)) 45 | ``` 46 | * Mind the indention. This is the loop body that gets executed 47 | * The range provided is including the starting point and **excluding** the last number. This means that, for a range like `range(0, 5)`, the numbers will be `0,1,2,3,4` not including the last number 5. 48 | 49 | ## Conditionals 50 | [Link to Section](loops.md) 51 | * We can use `if` clauses to specify conditionals 52 | * A `if` clause has one or more conditionals that, if met, will be executed. 53 | * We can have additonal conditionals (using `elif`) and a catch-all that gets executed if non of the conditionals is met (using `else`) 54 | 55 | ```python 56 | 57 | age = 10 58 | if age == 12: 59 | print("Age is 12") 60 | elif age == 15: 61 | print("Age is 15") 62 | else: 63 | print("Age is something else") 64 | ``` 65 | 66 | | **Operator** | **Description** | 67 | |---------------|---------------------------------------------------| 68 | | `a == b` | True if a is equal to b | 69 | | `a > b` | True if a is strictly greater than b | 70 | | `a >= b` | True if a is greater or equal than b | 71 | | `a < b` | True if a is strictly less than b | 72 | | `a <= b` | True if a is less of equal than b | 73 | | `a % b == 0` | True if a can be divided by b without a remainder | 74 | 75 | ## Functions 76 | [Link to Section](functions.md) 77 | * Functions allow you to encapsulate code for later re-use 78 | 79 | ```python 80 | 81 | def my_function_name(): 82 | print("Hello!") 83 | ``` 84 | 85 | * Functions can have one or more arguments 86 | 87 | ```python 88 | 89 | def my_function_name(argument_one, argument_two): 90 | print("Argument 1 is: " + str(argument_one)) 91 | print("Argument 2 is: " + str(argument_two)) 92 | ``` 93 | 94 | * Functions can return information 95 | 96 | ```python 97 | 98 | def my_function(name): 99 | return "Hi my name is " + str(name) 100 | ``` 101 | -------------------------------------------------------------------------------- /sessions/rest_fundamentals/postman.md: -------------------------------------------------------------------------------- 1 | # Consuming REST APIs with Postman 2 | 3 | When exploring a REST API it's great to have a visual tool to play around with the actual API without having to write code. One such tool is [Postman](https://www.postman.com/). 4 | 5 | Starting up Postman we first need to create a *collection*. Collections combine requests together and you can see all your collections on the left part (the red highlighted area in the screenshot below). Collections can either be created by you or by others and then shared with you! For example the Meraki team publishes a collection of all the requests that are possible with the Meraki API. 6 | 7 | By clicking "New" and then "Collection" we can get a new collection. 8 | 9 | ![Postman Interface](../../res/postman_interface.png) 10 | 11 | Next, create a new *request* in our newly created collection by going to "New" and then "Request". You will be prompted for a name, a description and to which collection you would like to add this request. 12 | 13 | With that done we have the request editor opened and can start making API requests using Postman. 14 | 15 | ![Postman Request Editor](../../res/postman_editor.png) 16 | 17 | (1) is the HTTP method we want to use. Clicking on the method will reveal a drop down with all available options. We will leave it at `GET` for the time being. 18 | 19 | (2) Is the URL field. Here we are going to put the URL of the API we want to request. 20 | 21 | (3) Shows the different additional information we can add to our request. You can see *Query Parameters*, *Authorization*, *Headers* and *Body* among others. 22 | 23 | As an example we will be using the *Deck of Cards API*. This API is a educational API that allows you to do all sorts of things like drawing a card or shuffling a deck of cards with a few REST API calls. You can find the webpage [here](https://deckofcardsapi.com/). 24 | 25 | So lets make our first request in Postman and get ourself a new deck of cards. 26 | 27 | The URL for this is `https://deckofcardsapi.com/api/deck/new/shuffle/`. Based on what we learned during the last chapter you can see that we are requesting the resource `deck` so we are going to put this into the Url field (area 2 in above screenshot). 28 | 29 | Now, we also need to tell the deck of cards api how many decks we would want. This information is passed as a *query parameter*. 30 | 31 | REST distinguishes two (widely used) types of parameters: 32 | 33 | * *Path Segment Parameters* These are the parameters that are directly within the URL. In our previous example (`GET /api/v1/people/me`) the `me` parameter telling the API server to retrieve all information that are related to me myself is a *path argument* because it is part of the path itself. 34 | * *Query Parameters* that are passed at the end of the resource identifier. These query parameter are often used to pass on additional information like filtering. The query parameter part always starts with a `?` and we then append the parameters after that. So a `GET` request where we are filtering for a certain date (using the query parameter `date`) and a certain name (using the query parameter `name`) would look like this: `GET /api/v1/example?date=2020-08-10&name=Marcel` 35 | 36 | For the deck of cards API the *query parameter* to specify the number of decks is `deck_count`. In Postman we can specify these parameters directly in the interface under *Params*. 37 | 38 | Our final request thus looks like this: 39 | 40 | ![First request with Postman](../../res/first_request.png) 41 | 42 | Clicking "Send" will send this request and return a response. 43 | 44 | ![First response with Postman](../../res/first_response.png) 45 | 46 | (1) Shows you the status line of our response. As you can see the API returned a `200 OK` meaning that the request was responded to successfully. 47 | 48 | (2) Shows the request body. Our API has returned a json string containing, among other things a `deck_id` and the number of cards that are remaining in our deck. 49 | 50 | Take note of that `deck_id` as we will be using it in the next chapter. 51 | 52 | Postman is a great tool for exploring an API but when automating workflows there is no way around using python (or another programming language) so lets go ahead and see how we can draw a card from the deck we just created using python! 53 | 54 |
55 | 56 | [Prev](what_is_REST.md) - [Next](requests.md) 57 |
-------------------------------------------------------------------------------- /sessions/python_one/functions.md: -------------------------------------------------------------------------------- 1 | # Functions 2 | 3 | Often we need to re-use part of our code. 4 | 5 | You might be tempted to just copy&paste the code over and over again. Let me be very clear here: **Copy&paste is never the correct answer**. 6 | 7 | Copy&pasting code leads to many different problems but the biggest one is the clutter when changing parts. Lets say you have a piece of code that checks if your switches are still active by checking a switches `status` variable. The service you retrieve the switch status from suddenly changes the indication that this switch is still active from setting `status=ACTIVE` to `status=active`. If you have copy&pasted that piece of code you now have to change that conditional in every part you have copy&pasted the code into. This is a maintenance nightmare. 8 | 9 | Luckily we can capsulate our code in a way that we can reuse it. That is what *functions* are for. 10 | 11 | ## Declaring functions 12 | 13 | A function is simply a piece of code that can be called. You have been **using** functions all throughout this session! `print` is a function that python provides by default and so is `range`. 14 | 15 | Declaring your own functions is easy. We use the `def` keyword, a name and round brackets. 16 | 17 | ```python 18 | 19 | def my_first_function(): 20 | print("This is my first function") 21 | ``` 22 | 23 | Everything that is indented is referred to as the *function body* and this is what gets executed when we *call* the function. 24 | 25 | How do we call a function? By using the name! 26 | 27 | ```python 28 | 29 | def my_first_function(): 30 | print("This is my first function") 31 | 32 | my_first_function() 33 | ``` 34 | 35 | > :computer: Do it for yourself! Declare a function called `my_first_function` and have it print out the text `I am Marcel and this is my first function`. 36 | > Substitute Marcel for your own name. 37 | 38 |
39 | Click here to show solution 40 | 41 | ```python 42 | 43 | def my_first_function(): 44 | print("I am Marcel and this is my first function") 45 | ``` 46 |
47 | 48 | ## Functions with Arguments and Returns 49 | 50 | Functions can have `arguments`. These are variables that are passed into the function to modify the functions behaviour. Lets modify the function above to print the name based on a argument. 51 | 52 | ```python 53 | 54 | def my_first_function(name): 55 | print("I am " + str(name) + " and this is my first function") 56 | 57 | my_first_function("Marcel") 58 | ``` 59 | 60 | We can also *return* something from a function. This can be achieved using the `return` statement. 61 | 62 | ```python 63 | 64 | def my_second_function(name): 65 | text = "I am " + str(name) + " and this is my second function" 66 | 67 | return text 68 | 69 | print(my_second_function("Marcel")) 70 | ``` 71 | 72 | > :computer: Write a function called `test_switch` that receives the `name` and `status` of a switch as a arguments and **returns** either "The switch switch-1 is online" or "The switch switch-1 is offline" depending on wether the status is `active` or `inactive`. The name (`switch-1` in the example) should come from the `name` argument. 73 | > 74 | > Call the function with the name "switch-2" and the status "inactive". Save the output of that function call in a variable called `out` and print the value of that variable. 75 | > You can use the boilerplate code below as a starting point. You will need to change the questionmarks and fill the body 76 | > 77 | > ```python 78 | > 79 | > def test_switch(?, ?): 80 | > 81 | > return ? 82 | > out = ? 83 | > print(out) 84 | > ``` 85 | > 86 | > Pay close attention to which parts of the program need to be inside the function and which parts need to be outside of the function. 87 | 88 |
89 | Click here to show solution 90 | 91 | ```python 92 | 93 | def test_switch(name, status): 94 | if status == "active": 95 | return "The switch " + str(name) + " is online" 96 | elif status == "inactive": 97 | return "The switch " + str(name) + " is offline" 98 | 99 | out = test_switch("switch-2", "inactive") 100 | print(out) 101 | ``` 102 |
103 | 104 |
105 | 106 | [Prev](loops.md) - [Next](advanced_data_structures.md) 107 |
-------------------------------------------------------------------------------- /sessions/python_three/create.md: -------------------------------------------------------------------------------- 1 | # Creating a new ToDo Item 2 | 3 | As mentioned in the requirements we will need to store our todos as json files. This means we want to 4 | import the `json` module at the top of the file: 5 | 6 | ```python 7 | import json 8 | ``` 9 | 10 | We will also need ids that are unique. There are many ways to generate ids that are guaranteed to be unique 11 | but for this little project we will just use a random integer in the range `1000` to `10000`. This means we will also 12 | need to import the `random` module. 13 | 14 | The top of our file should now look like this: 15 | 16 | ```python 17 | 18 | import json 19 | import random 20 | ``` 21 | 22 | We will also need to create the `todos/` folder. To do so, in repl.it, click on the little *Add folder* icon and create a new 23 | folder called `todos`. Your directory structure should now look like this: 24 | 25 | ``` 26 | main.py 27 | todos/ 28 | ``` 29 | 30 | So lets get started by prompting the user for the required information. 31 | 32 | ```python 33 | 34 | def createItem(): 35 | print("Create a new ToDo item") 36 | 37 | title = input("Title: ") 38 | priority = input("Priority: ") 39 | ``` 40 | 41 | We can now store these information in a dictionary so that we can easily export them to json. 42 | 43 | ```python 44 | 45 | d = {} 46 | d["title"] = title 47 | d["priority"] = priority 48 | ``` 49 | 50 | Next we need to generate our random id, so lets use the `random.randint()` function to get a random integer in our desired range 51 | and also store that one in our dictionary. 52 | 53 | ```python 54 | 55 | random_id = random.randint(1000, 10000) 56 | d["id"] = random_id 57 | ``` 58 | 59 | With this we are ready to store our information in a file. 60 | 61 | ## Writing to and reading from files in python 62 | 63 | Reading and writing files in python is made easy. There are two major things we need to know: 64 | 65 | First, files in python are represented by *file handlers*. These objects allow you to write to and read from files and second we can use the `open()` function to create these file handlers. 66 | 67 | Writing to a file in python thus looks like this: 68 | 69 | ```python 70 | 71 | file_handler = open("test.txt", "w") 72 | file_handler.write("This is a test\n") 73 | file_handler.close() 74 | ``` 75 | 76 | And reading all lines in a file looks like this: 77 | 78 | ```python 79 | 80 | file_handler = open("test.txt", "r") 81 | for line in file_handler.readlines(): 82 | print(line) 83 | ``` 84 | 85 | Notice the second argument of the `open` function? This is the *mode* and it specifies what we want to do with the file. 86 | `r` is for reading a file, `w` for writing a file. 87 | 88 | ## Saving our ToDo Item 89 | 90 | Lucky for us we don't even need to think about how to first convert our dictionary into a string and then write that string to 91 | a file. Instead the `json` module has a function called `json.dump()`. Notice the missing `s` at the end. This function now doesn't dump the content into a string but rather expects a file handler to write to. 92 | 93 | So let's store our dictionary in a file that ends in the file extension `.todo` and has the `id` as its file name. 94 | 95 | ```python 96 | 97 | file_path = "todos/" + str(random_id) + ".todo" 98 | 99 | file_handler = open(file_path, "w") 100 | 101 | json.dump(d, file_handler) 102 | print("Saved todo item with id " + str(random_id)) 103 | ``` 104 | 105 | Our full function thus looks like this: 106 | 107 | ```python 108 | def createItem(): 109 | print("Create a new ToDo item") 110 | 111 | title = input("Title: ") 112 | priority = input("Priority: ") 113 | 114 | d = {} 115 | d["title"] = title 116 | d["priority"] = priority 117 | 118 | random_id = random.randint(1000, 10000) 119 | d["id"] = random_id 120 | 121 | file_path = "todos/" + str(random_id) + ".todo" 122 | 123 | file_handler = open(file_path, "w") 124 | 125 | json.dump(d, file_handler) 126 | print("Saved todo item with id " + str(random_id)) 127 | ``` 128 | 129 | Now that we can create todo items we also need a functionality to list all those items we already have. So next we are going to 130 | create the `listItems` function. 131 | 132 |
133 | 134 | [Prev](interface.md) - [Next](list.md) 135 |
-------------------------------------------------------------------------------- /sessions/rest_fundamentals/requests.md: -------------------------------------------------------------------------------- 1 | # Consuming REST APIs with requests and python 2 | 3 | If you come from a different programming language you might be terrified about having to do web requests with it. In the past it had been pretty cumbersome to do web requests from a programming language. Fundamentally this is still true but the python community has done a great job in abstracting this complexity away into packages. 4 | 5 | The package we are going to use today is called `requests`. In your online editor you can go ahead and just import it into your script like we have seen in the previous sessions. 6 | 7 | ```python 8 | 9 | import requests 10 | ``` 11 | 12 | > :rocket: `requests` is not part of the python standard library but rather part of the python package ecosystem. If you want to learn more about these and how to install them check out the additional session on [PIP, PyPi and the Python Package Ecosystem](../extras/pip_and_pypi/Readme.md). 13 | 14 | With `requests` imported we can go ahead and make our first HTTP request from python! The deck of cards API allows us to "draw" a card by sending a `GET` request to `https://deckofcardsapi.com/api/deck/<>/draw/`. As you can see we have the `deck_id` of the card we want to request as a *Path Segment Parameter*. 15 | 16 | Using the the Id we got from the request with Postman lets construct the url we have to request: 17 | 18 | ```python 19 | 20 | deck_id = "bv6t3gdoujzd" #Note: Yours will be different 21 | 22 | url = "https://deckofcardsapi.com/api/deck/" + str(deck_id) + "/draw/" 23 | ``` 24 | 25 | Next, we need to tell the deck of cards api, using the `count` query parameter, how many cards to draw from our deck. We could hard-code this parameter into the url but a nicer way to do it is to create a parameter dictionary 26 | 27 | ```python 28 | parameters = { 29 | 'count': 1 30 | } 31 | ``` 32 | 33 | Finally, we need to do our get request. This is done using the `requests.get` method. 34 | 35 | ```python 36 | response = requests.get(url, params=parameters) 37 | ``` 38 | 39 | And that's it. The full script then looks like this: 40 | 41 | ```python 42 | import requests 43 | 44 | deck_id = "bv6t3gdoujzd" #Note: Yours will be different 45 | 46 | url = "https://deckofcardsapi.com/api/deck/" + str(deck_id) + "/draw/" 47 | 48 | parameters = { 49 | 'count': 1 50 | } 51 | 52 | response = requests.get(url, params=parameters) 53 | ``` 54 | 55 | Pretty neat but so far we are not doing anything with our response. So lets first print out the status code. It's always good practice to check this code in order to make sure that the request has been fulfilled correctly. 56 | 57 | In `requests` the response object returned has a argument called `status_code` that contains our return code. So lets go ahead an print that: 58 | 59 | ```python 60 | 61 | return_code = response.status_code 62 | 63 | print("Web request had a status of " + str(return_code)) 64 | ``` 65 | 66 | Now we would also like to access the *information* returned by the response. As you have seen in the Postman section the deck of cards API returns a json string. We *could* go ahead and use `response.text` to retrieve the raw json string that the API has returned and parse that using the `json` module. However, we can also use the easier to use `response.json()` method that will automatically parse the json and return a dictionary. 67 | 68 | So let's go ahead and retrieve the cards json into a dictionary and print out all the keys. 69 | 70 | ```python 71 | 72 | card = response.json() 73 | 74 | for key in card.keys(): 75 | print("- " + str(key)) 76 | ``` 77 | 78 | One property of the json is a list of cards that have been retrieved (stored in the `cards` attribute). So as a final step lets go ahead and get the first card and print out the code of it. 79 | 80 | ``` 81 | card_code = card['cards'][0]['code'] 82 | 83 | print("Card code is: " + str(card_code)) 84 | ``` 85 | 86 | And thats it! You can now make requests using the `requests` library right from your python script. The complete script looks like this: 87 | 88 | ```python 89 | 90 | import requests 91 | 92 | deck_id = "bv6t3gdoujzd" #Note: Yours will be different 93 | 94 | url = "https://deckofcardsapi.com/api/deck/" + str(deck_id) + "/draw/" 95 | 96 | parameters = { 97 | 'count': 1 98 | } 99 | 100 | response = requests.get(url, params=parameters) 101 | 102 | return_code = response.status_code 103 | 104 | print("Web request had a status of " + str(return_code)) 105 | 106 | card = response.json() 107 | card_code = card['cards'][0]['code'] 108 | 109 | print("Card code is: " + str(card_code)) 110 | ``` 111 | 112 | > :rocket: We have not covered authentication as well as other types of requests like `POST` in this section. If you are interested have a look at the additional session on [Authentication in REST](../extras/authentication_in_rest/Readme.md). 113 | 114 |
115 | 116 | [Prev](postman.md) - [Next](next_steps.md) 117 |
-------------------------------------------------------------------------------- /sessions/python_two/random.md: -------------------------------------------------------------------------------- 1 | # Random 2 | 3 | The `random` module allows you to generate random numbers as well as some other tasks related to randomness. 4 | 5 | > :wrench: Not all functions in the `random` module are cryptographically save and thus suited for i.e. the handling of 6 | > passwords. Refer to the documentation and double check. 7 | 8 | First, we need to import the module using 9 | 10 | ```python 11 | 12 | import random 13 | ``` 14 | 15 | Next, we can generate a random integer number using the `random.randint` function. 16 | 17 | ```python 18 | 19 | import random 20 | 21 | random_int = random.randint(0, 10) 22 | ``` 23 | 24 | Above code snippet generates a random integer between 0 and (including) 10. 25 | 26 | > :computer: Write a little script that imports the random module and generates 5 27 | > random numbers between 0 and 15. 28 | > 29 | > for-loops might be useful here. 30 | 31 |
32 | Click here to show solution 33 | 34 | ```python3 35 | import random 36 | 37 | for i in range(0, 5): 38 | random_num = random.randint(0, 15) 39 | print("Random number #" + str(i) + " is " + str(random_num)) 40 | ``` 41 |
42 | 43 | We can also randomly draw a object out of a list. This functionality is provided by the `random.choice` and `random.choices` function. They both take a list of options and then return one (or more) randomly chosen items from that list. 44 | 45 | ```python 46 | 47 | import random 48 | 49 | numbers = ["one", "two", "three", "four"] 50 | 51 | # Get one random string 52 | one_random_number = random.choice(numbers) 53 | 54 | print("Our one random number is: " + str(one_random_number)) 55 | 56 | # Get multiple 57 | two_random_numbers = random.choices(numbers, k=2) 58 | 59 | print("Your random numbers: ") 60 | for num in two_random_numbers: 61 | print("- " + str(num)) 62 | ``` 63 | 64 | For the `random.choice` function we only need to provide the list of choices. For the `random.choices` function 65 | we also need to tell the function how many to return. This is done by the argument `k`. 66 | 67 | When we covered function we didn't talk about *default arguments* so let's make a quick excursion into that topic. 68 | 69 | ## Functions and Default Arguments 70 | 71 | As we have seen you can declare a function using the `def` argument and a function can receive arguments to pass 72 | values into the function. Previously all these values had to be set when being called. We can, however, also provide 73 | default values. 74 | 75 | ```python 76 | 77 | def func_with_defaults(argument="test"): 78 | print("Our argument is " + str(argument)) 79 | ``` 80 | 81 | If we want to call above function we now have three options: 82 | 83 | 1. Not provide a value for `argument`. This will result in the default value of `test` being used 84 | 85 | ```python 86 | 87 | func_with_defaults() 88 | ``` 89 | 90 | 2. Provide a value for `argument` by passing the new value into the function. This is then referred to as a *positional argument*. 91 | 92 | ```python 93 | 94 | func_with_defaults("passed into it") 95 | ``` 96 | 97 | 3. Explicitly name the *function argument* that we want to specify. This then called a *keyword argument* 98 | 99 | ```python 100 | 101 | func_with_defaults(argument="passed as keyword argument") 102 | ``` 103 | 104 | You can see all of these working in below code snippet 105 | 106 | ```python 107 | def func_with_defaults(argument="test"): 108 | print("Our argument is " + str(argument)) 109 | 110 | func_with_defaults() 111 | func_with_defaults("passed into it") 112 | func_with_defaults(argument="passed as keyword argument") 113 | ``` 114 | 115 | Positional arguments are specified by their *position* within the function definition while *keyword arguments* are specified by their keyword in the function definition. 116 | 117 | > :wrench: The definition of a function, that means the name, arguments and default values is also called a *function signature* or the *signature of a function*. 118 | 119 | To make things more complicated (or powerful, depending on your view point) we can mix *positional arguments* and *keyword arguments*. 120 | 121 | ```python 122 | 123 | def func_with_mix(arg1, arg2): 124 | print("Arg1: " + str(arg1)) 125 | print("Arg2: " + str(arg2)) 126 | 127 | func_with_mix("test1", arg2="test2") 128 | ``` 129 | 130 | What you can't do however, is call a positional argument **after** you have specified keyword arguments. So you can't do the following: 131 | 132 | ```python 133 | 134 | func_with_mix(arg1="test1", "test2) 135 | ``` 136 | 137 | Coming back to our example of `random.choices` this is what we are doing when we call `random.choices(options, k=2)`. We specify the (required) *positional argument* options, which is a list of items to draw from, and the (optional) keyword argument *k*. By default `k` is set to `1`. 138 | 139 | > :computer: Write a little script that draws two winners out of a list of five possible winners and prints out their names. You can start with below code 140 | > 141 | > ```python 142 | > 143 | > import random 144 | > 145 | > possible_winners = ["Sarah", "Sven", "Simon", "Sophie", "Samantha"] 146 | > 147 | > # ToDo: Add the code needed to draw two winners here 148 | 149 |
150 | Click here to show solution 151 | 152 | ```python3 153 | import random 154 | 155 | possible_winners = ["Sarah", "Sven", "Simon", "Sophie", "Samantha"] 156 | 157 | winners = random.choices(possible_winners, k=2) 158 | 159 | for winner in winners: 160 | print("The winner is " + str(winner)) 161 | ``` 162 |
163 | 164 |
165 | 166 | [Prev](standard_library.md) - [Next](json.md) 167 |
-------------------------------------------------------------------------------- /sessions/extras/rest_theory/Readme.md: -------------------------------------------------------------------------------- 1 | # REST API Theoretical Deep Dive 2 | 3 | In the previous chapters we have explored the basics behind REST APIs. In this addendum content we will focus on the constraints behind a well designed REST API. While these constraints are wildly accepted REST is not a very strict standard and leaves a lot of room for interpretation. This means that you can't always rely on an API you are encountering in the wild to exactly follow these constraints and rules. 4 | 5 | ## A History of REST 6 | 7 | REST was first introduced and defined by Roy Fielding, one of the principles behind the HTTP protocol specification and a co-founder of the Apache HTTP Server project that is powering almost 30% of all web pages in the internet. 8 | 9 | Fielding defined REST in Chapter 5, aptly named *Chapter 5: Representational State Transfer (REST)*, of his 2000 dissertation titled *Architectural Styles and the Design of Network-based Software Architectures* by introducing a set of six constraints that a *RESTful* application should follow. In the rest of this chapter we will have a look at each of these constraints. 10 | 11 | ## Uniform Interface 12 | 13 | The uniform interface constraint aims at making sure that you have a defined interface for each of the resources within your system. Each resource, think of a user in a shop system or a message in a chat application, should only have one logical URI. 14 | 15 | The uniform interface also demands that your representation is consistent. This means that all your resources should follow a common naming convention and data format. Note that the specific naming convention and data format is up to the API architect to decide. The *Uniform Interface* constraints just aims at providing consistency *across* the API itself. 16 | 17 | This also implies that your API interface and the underlying service it is exposing to the outside (think for example of the schema that is actually storing the data in the database) can evolve independently from each other. You can do whatever changes you want to the underlying infrastructure as long as you keep the data and interaction represented to the user the same. By providing the same set of properties instead of a application-specific interface REST compromises efficiency in favour of providing a universal and easier-to-consume interface. 18 | 19 | ## Client-Server 20 | 21 | This constraint simply states that your application should have a client that queries the server for information. Client and server can evolve independently from each other as long as the interface is kept the same and should not depend on each other. 22 | 23 | ## Statelessness 24 | 25 | Probably the most important constraint for the success of REST is it's statelessness. This means that each request has to carry all the information needed to fulfill the request. The server does not store any history or session information. Keeping track of these information is left entirely to the client. 26 | 27 | What seems like a small little constraint allows for some of the major benefits of REST. 28 | 29 | By being stateless monitoring a API server becomes significantly easier. To track down errors you only have to look at the one request a user is sending and don't have to take into account any previous requests. 30 | 31 | Scalability is made easy by statelessness as you can simply scale up the number of servers handling your requests. There is no need to synchronize state between requests and/or make sure that a chain of requests from the same client always lands on the same server. Instead, typical API architectures using REST rely on the backend services like the database to take care of things like data integrity. 32 | 33 | Since there is no free lunch this constraint also implements some disadvantages. Most important the application can cause network overhead since all information has to *always* be send in each request instead of the server storing the common data of multiple requests in i.e. a session store. 34 | 35 | ## Cache 36 | 37 | This constraint makes it easier to relief the burden of a API on it's backend services. Essentially the aim is to allow resources to mark themselves as *cacheable*. When a resource is marked as *cacheable* the cache that is positioned in front of the API server is allowed to reuse the previous data for a later, equal request. 38 | 39 | Think of multiple different users requesting a `User` resource with id 15 from a REST API(i.e. a request like `GET /api/v1/users/15`). For the first request the API server will need to query the backend database for all the information and retrieve the JSON representation of that user. 40 | 41 | After this initial request and as long as the `User` resource does not change, there is no need to query the database again for the information of the user with id 15 as they won't change! 42 | 43 | This means we can scale much easier and reduce the load on our backend systems. This is a crucial aspect for web-scale systems that need to satisfy thousands of requests per second. 44 | 45 | Note that caching can be applied both at the server **and** client side leading to a faster client experience. 46 | 47 | ## Layered System 48 | 49 | REST adds a *layered system* constraint to it's standard. In plain English this means that a client can't tell what part of the service it is talking to. This means that while your client might *think* it's talk to a single server your service can, in the backend, have a load balancer that decides which API server to send the request to. This API server in turn can then talk to other services to gather all the resources required and return the information back to the client. And all of this is done without the client knowing. 50 | 51 | From a systems design point of view this allows our API to *wrap* legacy systems and/or provide a simplification of the data used internally to how it is represented to the API user. And while this approach adds overhead and latency since your API service needs to communicate with multiple subsystem it also allows for a clear boundary between internal and external data. 52 | 53 | ## Code-On-Demand 54 | 55 | Lastly there is the *optional* constraint of Code-on-Demand. Code-on-Demand simply allows a API to server code in the form of scripts and applets to the client that can then use it to enhance its functionality. A optional constraint this is one of the least adopted features of the REST standard and is rarely seen in the wild. 56 | 57 |
58 | 59 | [Back](../Readme.md) 60 |
-------------------------------------------------------------------------------- /sessions/python_one/variables.md: -------------------------------------------------------------------------------- 1 | # Variables and Data types 2 | 3 | The most basic concept of every programming language are *variables*. Simply put, 4 | variables are placeholder for other values. For example your variable could hold 5 | the number of routes that are reachable from a router or the IP address of your 6 | gateway. In python variables have a name, the *variable name* that can be used 7 | to refer to them and are assigned a value. 8 | 9 | ```python 10 | 11 | a = 10 12 | b = "Hello" 13 | pi = 3.14 14 | ``` 15 | 16 | The first time a variable is assigned we also talk about this variable being *initialized*. 17 | 18 | ## Data types 19 | 20 | Values can come in many different shapes and forms. You could store a counter, which is just a whole number, or a message of the day which would be a text. These different types of data a variable can hold are also referred to as *data types*. 21 | 22 | Python knows four different *simple* or *basic* data types. 23 | 24 | * `string` for storing text and characters 25 | * `int` for storing integer numbers 26 | * `float` for storing numbers that contain a decimal part 27 | * `None` for "nothing" 28 | 29 | Contrary to other programming languages python is not *statically typed*. What is behind that word you might ask? This simply means that you, as a programmer, don't have to specify what type your variable holds. The python interpreter figures this out on its own based on the value of the variable! 30 | 31 | ### Strings 32 | 33 | *Strings* hold everything related to text in python. You declare a string by using 34 | quotation marks. Have a look at the example below: 35 | 36 | ```python 37 | 38 | text_1 = "This is a text" 39 | text_2 = 'We can also use single quotes to declare our text' 40 | 41 | long_text = """ 42 | And we can have 43 | our text go over 44 | multiple lines when we use triple quotes 45 | """ 46 | ``` 47 | 48 | You can also combine two strings into a new string. This is also referred to as 49 | *string concatenation*. In python this is achieved by using the `+` operator. 50 | 51 | ```python 52 | 53 | text_1 = "I am a text." 54 | text_2 = "And I am also a text" 55 | 56 | text_3 = text_1 + " " + text_2 57 | print(text_3) 58 | ``` 59 | 60 | As you can see in the example above, we can combine variables and a string we just declared(the `" "` whitespace in the example) into a new string. 61 | 62 | > :computer: Modify the script below so that it included your name. You will need to specify a variable called `your_name` and then assign your name to it as a `string` 63 | > 64 | > ```python3 65 | > # You will need to add code here 66 | > 67 | > print("Hello " + your_name) 68 | > ``` 69 | 70 |
71 | Click here to show solution 72 | 73 | ```python 74 | your_name = "Marcel" 75 | print("Hello " + your_name) 76 | ``` 77 |
78 | 79 | ### Integers 80 | 81 | *Integers* or *ints* hold everything related to whole numbers in python. You declare such a number by simply assigning it using the `=` symbol. There is no need for quotes or anything to mark it as a number. Have a look at the example below. 82 | 83 | ```python 84 | 85 | a = 10 86 | b = 5 87 | ``` 88 | 89 | We can add, subtract and multiply integers using the `+`, `-` and `*` operators. 90 | 91 | ```python 92 | 93 | a = 10 94 | b = a * 5 # b is now 50 95 | c = 10 96 | d = b - c # d is now 40 97 | ``` 98 | 99 | We can also subtract, multiply or add a value to the variable itself. 100 | 101 | ```python 102 | 103 | a = 5 104 | a = a + 5 # Now a has the value 10 105 | a = a * 2 # Now a has the value 20 106 | a = a - 5 # Now a has the value 15 107 | ``` 108 | 109 | We can write this also with a shorthand to save us some typing: 110 | 111 | ```python 112 | 113 | a = 5 114 | a += 5 115 | a *= 2 116 | a -= 5 117 | ``` 118 | 119 | So far we have skirted around *divisions*. Divisions can't always be whole numbers. For example 5 divided by 2 is 2.5 which is not a whole number. To hold these types of values we will need a new data type, the *floats*. 120 | 121 | > :computer: Use python to calculate the following operations: 122 | > * Declare a variable `a` with value 10 123 | > * Declare a variable `b` with value 2 124 | > * Declare a variable `c` that is calculated by multiplying `a` and `b` 125 | > * Add 10 to the variable `c` 126 | > * Multiply `c` with itself 127 | 128 |
129 | Click here to show solution 130 | 131 | ```python 132 | 133 | a = 10 134 | b = 2 135 | c = a * b 136 | c += 10 # alternative: c = c + 10 137 | c *= c # alternative: c = c * c 138 | ``` 139 |
140 | 141 | ### Floats 142 | 143 | *Floats* hold everything related to floating or decimal numbers like 2.5 or 3.14159. floats are declared by using the `=` operator. To help python distinguish floats from integers we use the `.` to separate the whole and fractional number part. We can carry out the same operations we can with integers. 144 | 145 | ```python 146 | 147 | a = 10.5 # Mind the "." 148 | 149 | a += 5 150 | a *= 2.33 151 | a = a / 2.5 152 | ``` 153 | 154 | As you can see we can mix floats and integers when doing any calculations. Python will always choose the right variable type to fit in the value of your calculation. 155 | 156 | ### None 157 | 158 | *None* is a bit of an odd data type. What *None* represents is nothing. While it might be counter-intuitive to dedicate a entire data type to "nothing" we quite often have to represent nothing. As an example think about a piece of code that calculates the square root of a number, so the square root of 4 is 2, the square root of 9 is 3 and so. In basic mathematics it is not possible to calculate the square root of a negative number. How would your code represent that? How would you signal to someone using your code that this value is not a valid square root? You could use `0` however, 0 is a valid square root of 0. Maybe you would use `-1`? But `-1` is a valid square root of `1`. We need a data type that represents "nothing" and that is what "None" is for. 159 | 160 | ```python 161 | 162 | a = None 163 | ``` 164 | 165 | ### Converting between data types 166 | 167 | Often we have to convert between one data type and another. For example when using the `print` function to print out text we can only pass a variable of type string. But what if we want to print out a number? We have to convert the number into a string. This is also referred to as a *cast* or *type cast*. 168 | 169 | To do this in python we have a variety of *cast functions*. 170 | 171 | ```python 172 | 173 | a = 10 174 | b = str(a) # Converts the integer a into a string 175 | c = float(a) # converts the integer a into a float 176 | d = int(c) # converts the float c into an integer variable 177 | ``` 178 | 179 | > :warning: When converting a `float` to an `int` that number won't be rounded. Rather the fractional part will just be discarded. This means that, `int(3.9)` won't be `4` as would be the correct rounding but rather `3`. 180 | 181 | Of course you can't just *cast* everything into everything. Trying to cast a text like `text_1 = "This is a text"` into a integer will result in an error. Try it out for yourself and observe the `ValueError` python will return: 182 | 183 | ```python 184 | 185 | text_1 = "This is a text" 186 | int_1 = int(text_1) 187 | ``` 188 | 189 |
190 | 191 | [Prev](Readme.md) - [Next](loops.md) 192 |
193 | 194 | -------------------------------------------------------------------------------- /sessions/python_one/loops.md: -------------------------------------------------------------------------------- 1 | # Loops and Control Structures 2 | 3 | Writing operations line by line is fine but what if we want to carry out the same operation multiple times? Let's say we want to print the sentence `Hello from python` five times. We could do it like this: 4 | 5 | ```python 6 | 7 | print("Hello from python") 8 | print("Hello from python") 9 | print("Hello from python") 10 | print("Hello from python") 11 | print("Hello from python") 12 | ``` 13 | 14 | You can probably tell that this is getting cumbersome when dealing with more then a trivial amount of times we want to run the code and outright impossible if we don't know the number of *iterations* before. 15 | 16 | To solve this, python and all other programming languages, have the concept of *loops*. Let's first look at *for loops*. 17 | 18 | ## For loops 19 | 20 | A `for loop` allows us to execute one or more instructions a certain number of times. To take the example from above: 21 | 22 | ```python 23 | 24 | for a in range(0, 5): 25 | print("Hello from python") 26 | ``` 27 | 28 | There are a few things to consider here: 29 | 30 | * We use the keyword `for` to specify that this is a *for loop*. 31 | * We use the `range(start, end)` function to specify the number of *iterations* we want this loop to run for. 32 | * The indented part (in this example just the line with the `print` statement) is what gets executed. python uses indentation to structure the code. 33 | 34 | > :computer: Use a for loop to print the text "Good morning!" ten times. 35 | 36 |
37 | Click here to show solution 38 | 39 | ```python 40 | 41 | for a in range(0, 10): 42 | print("Good morning!") 43 | ``` 44 |
45 | 46 | In the for loop specified above we also have a variable `a`. This does not necessarily need to be a and you can use any variable name you like. We can also use this variable in the indented part of the body. Let's have our script print out the value of the variable. 47 | 48 | ```python 49 | 50 | for a in range(0, 10): 51 | print("This is the " + str(a) + ". iteration") 52 | ``` 53 | 54 | As you can see we start with `This is the 0. iteration` and end with `This is the 9. iteration`. 55 | 56 | Be aware that the range you specify always **excludes** the last number of you range. 57 | 58 | > :computer: Modify the code so that it prints 59 | > 60 | > ``` 61 | > This is the 1. iteration 62 | > This is the 2. iteration 63 | > This is the 3. iteration 64 | > This is the 4. iteration 65 | > This is the 5. iteration 66 | > ``` 67 | > 68 | > Pay close attention that you specify the correct range! 69 | 70 |
71 | Click here to show solution 72 | 73 | ```python 74 | 75 | for a in range(1, 6): 76 | print("This is the " + str(a) + ". iteration") 77 | ``` 78 |
79 | 80 | > :computer: Lets use this new knowledge to print out all ip addresses that are in a subnet. 81 | > 82 | > Given a class C Network of `192.168.0.0/24` and a netmask of `255.255.255.0` print out all addresses that are available (including the network and broadcast address) 83 | > 84 | > Remember: The variable of your for loop will be an integer. You will need to cast accordingly! 85 | 86 |
87 | Click here to show solution 88 | 89 | ```python 90 | 91 | for host_part in range(0, 256): 92 | ip_address = "192.168.0." + str(host_part) 93 | print(ip_address) 94 | ``` 95 |
96 | 97 | We can also have a for loop inside another for loop. This for-loopception is called *nesting*. 98 | 99 | ```python 100 | 101 | for x in range(0, 10): 102 | for y in range(0, 10): 103 | print("Looking at pixel (" + str(x) + "," + str(y) + ")") 104 | ``` 105 | 106 | > :computer: We can use nested for loops to print out multiplication tables. Modify the code below 107 | > 108 | > ```python 109 | > for num in range(1, 11): 110 | > print("Multiplication table for " + str(num)) 111 | > # Use another for loop for the `multiplier` variable 112 | > # Insert here 113 | > res = num ∗ multiplier 114 | > print(str(num) + ” x ” + str(multiplier) + ” = ” + str(res)) 115 | > ``` 116 | 117 |
118 | Click here to show solution 119 | 120 | ```python 121 | 122 | for num in range(1, 11): 123 | print("Multiplication table for " + str(num)) 124 | for multiplier in range(1, 11): 125 | res = num ∗ multiplier 126 | print(str(num) + ” x ” + str(multiplier) + ” = ” + str(res)) 127 | ``` 128 |
129 | 130 | ## Control Structures 131 | 132 | *If-clauses* can be used to change the program flow based on a a variable value. The instructions carried out when a *comparisson* or *condition* is met are also called a *branch*. They are again marked by indentation. 133 | 134 | ```python 135 | 136 | a = 10 137 | if a == 10: 138 | # This is the branch 139 | print("a has the value of " + str(a)) 140 | ``` 141 | 142 | In this simple example we use `if` to mark that this is now a *if-clause*. 143 | 144 | The condition is then specified after that. Be aware that we are using `==` for the comparison. Remember that the `=` operator is already used for assigning variable values. 145 | 146 | We can use a number of comparison operators like 147 | 148 | * `a == b` to check if `a` is equal to `b` 149 | * `a >= b` to check if `a` is bigger or equal to `b` 150 | * `a <= b` to check if `a` is less then or equal to b` 151 | * `a%b == 0` to check if `a` can be divided by `b` wihtout a remainder 152 | 153 | We can have more then one conditional. To declare these additional branches we use the `elif` keyword. Additionally we can use the `else` keyword to specify a branch that gets carried out when non of the conditions above were met 154 | 155 | ```python 156 | 157 | a = 5 158 | if a == 10: 159 | print("a is 10") 160 | elif a == 5: 161 | print("a is 5") 162 | else: 163 | print("a is neither 5 nor 10") 164 | ``` 165 | Be advised that python stops checking after the first condition is met! So if a variable would satisfy both the first and last conditions of your if-elif-else clauses only the first one will be executed. 166 | 167 | > :computer: The **FizzBuzz** test is a famous question in entry-level coding interviews. The ask is simple: 168 | > 169 | > Write a program that prints the numbers from 1 to (including) 20 170 | > 171 | > * for multiples of three print **Fizz** instead of the number 172 | > * for multiples of five print **Buzz** instead of the number 173 | > * for multiples of both five and three print **FizzBuzz** instead of the number 174 | > * In all other cases print the number 175 | > 176 | > Three tips: 177 | > 178 | > 1) To check if a number is a multiple of another number you can use this conditional: 179 | > 180 | > ```python 181 | > 182 | > a = 10 183 | > if a%5 == 0: 184 | > print("This number is a multiple of 5") 185 | > ``` 186 | > 187 | > 2) You can chain multiple conditions with the `and` keyword 188 | > 189 | > ```python 190 | > 191 | > age = 17 192 | > if age > 10 and age < 18: 193 | > print("The person is a teenager") 194 | > ``` 195 | > 196 | > 3) Remember for loops? They might come in handy in this exercise. 197 | 198 |
199 | Click here to show solution 200 | 201 | ```python 202 | 203 | for num in range(1, 21): 204 | if num % 3 == 0 and num % 5 == 0: 205 | print("FizzBuzz") 206 | elif num % 3 == 0: 207 | print("Fizz") 208 | elif num % 5 == 0: 209 | print("Buzz") 210 | else: 211 | print(str(num)) 212 | ``` 213 |
214 | 215 |
216 | 217 | [Prev](variables.md) - [Next](functions.md) 218 |
219 | -------------------------------------------------------------------------------- /sessions/python_one/advanced_data_structures.md: -------------------------------------------------------------------------------- 1 | # Advanced Data Structures 2 | So far we have only discussed "simple" variables like strings or integers and these can already be used for many different things. But what if we need some more advanced things? 3 | 4 | * What if we don't know the number of variables? 5 | * What if we have a list of variables? 6 | * What if a function should return more then one variable? 7 | 8 | We need more advanced data structures that can satisfy this need! 9 | 10 | ## Lists 11 | 12 | *Lists* are, as the name suggests, a list of variables. We can, for example, have a list of strings with the names of all our switches. A list in python is declared using the `[]` operator. Sometimes you can also come across the `list()` operator. Both work in the exact same way. 13 | 14 | ```python 15 | 16 | l = [] 17 | l = list() 18 | ``` 19 | 20 | We can initialize a list with initial values 21 | 22 | ```python 23 | 24 | l = [1, 2, 3, 4, 5] 25 | ``` 26 | 27 | and we can mix the types of objects we put into our list 28 | 29 | ```python 30 | 31 | l = [1, "two", "three", 4.0, 5, None] 32 | ``` 33 | 34 | Accessing a list item is done by their *index*. 35 | 36 | ```python 37 | l = ["one", "two", "three"] 38 | 39 | print(l[0]) 40 | ``` 41 | 42 | > :warning: List indices start at 0. The first item in a list is not `l[1]` but `l[0]`. 43 | 44 | We can also add and remove items from a list 45 | 46 | ```python 47 | 48 | l = ["one"] 49 | l.append("two") # This adds the item to the list 50 | l.remove("one") # This removes the item with VALUE one 51 | ``` 52 | 53 | > :computer: Do it yourself! Create a list that holds the strings one, two, three, four and five. Then append the string six to the list and print the string a **index** 2 54 | 55 |
56 | Click here to show solution 57 | 58 | ```python3 59 | l = ["one", "two", "three", "four", "five"] 60 | l.append("six") 61 | print(l[2]) 62 | ``` 63 |
64 | 65 | ### Iterating over a list 66 | 67 | Often we want to do some sort of operation to all the items in a list. To do so we can use our for loop. 68 | 69 | ```python 70 | 71 | l = ["one", "two", "three"] 72 | 73 | for itm in range(0, len(l)): 74 | print(itm) 75 | ``` 76 | 77 | You can see we are using the `len` function to get the length of our list. This allows us to plug that length into our `range` function to get all the indices that are in the list. 78 | 79 | > :wrench: You can now see why the range function leaves the last item out. It is meant to facilitate this kind of iterations. 80 | 81 | > :computer: Create a list with the names of three switches: `switch-1`, `switch-2` and `switch-3`. 82 | > 83 | > Iterate over the list and print the name of the switches 84 | 85 |
86 | Click here to show solution 87 | 88 | ```python 89 | l = ["switch-1", "switch-2", "switch-3"] 90 | for i in range(0, len(l)): 91 | print(l[i]) 92 | ``` 93 |
94 | 95 | ### Lists and for-loops revisited 96 | When I told you that, with lists, you will see a new concept I was actually lying. You have been using lists all along. The `range` function we saw previously returns a list of numbers in the specified range. This is called a *for-each* loop. 97 | 98 | For-each loops allow us to iterate over all items in a list without having to use the index. Let's modify above code sample to use a for-each loop: 99 | 100 | ```python 101 | 102 | l = ["switch-1", "switch-2", "switch-3"] 103 | for switch in l: 104 | print(switch) 105 | ``` 106 | 107 | > :computer: You receive a list of switch statuses. The status is a string that is either `active`, `unknown` or `inactive`. 108 | > 109 | > Count the number of switches active, unknown or inactive. 110 | > 111 | > You can use the boilerplate code below: 112 | > 113 | > ```python 114 | > 115 | > switch_statuses = ["active", "active", "active", "unknown", "inactive"] 116 | > 117 | > number_active = 0 118 | > number_unknown = 0 119 | > number_inactive = 0 120 | > 121 | > # Add your code here! 122 | > print("Number of active switches: " + str(number_active)) 123 | > print("Number of unknown switches: " + str(number_unknown)) 124 | > print("Number of inactive switches: " + str(number_inactive)) 125 | > ``` 126 | 127 |
128 | Click here to show solution 129 | 130 | ```python 131 | 132 | switch_statuses = ["active", "active", "active", "unknown", "inactive"] 133 | 134 | number_active = 0 135 | number_unknown = 0 136 | number_inactive = 0 137 | 138 | # Add your code here! 139 | for status in switch_statuses: 140 | if status == "active": 141 | number_active += 1 142 | elif status == "inactive": 143 | number_inactive += 1 144 | elif status == "unknown": 145 | number_unknown += 1 146 | 147 | print("Number of active switches: " + str(number_active)) 148 | print("Number of unknown switches: " + str(number_unknown)) 149 | print("Number of inactive switches: " + str(number_inactive)) 150 | ``` 151 |
152 | 153 | ## Dictionaries 154 | 155 | The last puzzle piece we need are *dictionaries*. *Dictionaries* or *dicts* are a way of associating a key with a value. This is a quite common requirement. Let's say you want to store configuration information or information about a user. This is where dictionaries shine! 156 | 157 | We initialize or declare a dictionary as follows: 158 | 159 | ```python 160 | 161 | d = {} 162 | ``` 163 | 164 | We can now go ahead an assign values: 165 | 166 | ```python 167 | 168 | d["key"] = "value" 169 | ``` 170 | 171 | A example would be a dictionary holding some information about a user: 172 | 173 | ```python 174 | 175 | user = {} 176 | 177 | user["first_name"] = "Marcel" 178 | user["last_name"] = "Neidinger" 179 | user["age"] = 24 180 | ``` 181 | 182 | We can then access a value by its *key* using the key as an index: 183 | 184 | ```python 185 | user = {} 186 | 187 | user["first_name"] = "Marcel" 188 | user["last_name"] = "Neidinger" 189 | user["age"] = 24 190 | 191 | print(user["age"]) # This is the access 192 | ``` 193 | 194 | > :computer: Do it for yourself! Declare a `user` dictionary with the keys `first_name`, `last_name` and `age` and then print them all out! 195 | 196 |
197 | Click here to show solution 198 | 199 | ```python 200 | 201 | user = {} 202 | 203 | user["first_name"] = "Marcel" 204 | user["last_name"] = "Neidinger" 205 | user["age"] = 24 206 | 207 | print(user["first_name"]) 208 | print(user["last_name"]) 209 | print(user["age"]) 210 | ``` 211 | 212 |
213 | 214 | ### Iterating over dictionaries 215 | 216 | Quite often we want to access all elements in a dictionary and sometimes (i.e. if that dictionary is returned to us by an API) we don't exactly know the keys. We can use our for loop to get rid of that problem! 217 | 218 | ```python 219 | 220 | user = {} 221 | 222 | user["first_name"] = "Marcel" 223 | user["last_name"] = "Neidinger" 224 | user["age"] = 24 225 | 226 | for key in user.keys(): 227 | print(user[key]) 228 | ``` 229 | 230 | > :wrench: The function `dict.keys()` just returns a list of all the keys. We are using the for-each loop again! 231 | 232 | > :computer: Rewrite the code from the last exercise, the one printing out all the properties of the user dictionary, using the for-each loop and the `keys()` function. 233 | > 234 | > You can use this boilerplate: 235 | > 236 | > ```python 237 | > user = {} 238 | > 239 | > user["first_name"] = "Marcel" 240 | > user["last_name"] = "Neidinger" 241 | > user["age"] = 24 242 | > ``` 243 | 244 |
245 | Click here to show solution 246 | 247 | ```python 248 | 249 | user = {} 250 | 251 | user["first_name"] = "Marcel" 252 | user["last_name"] = "Neidinger" 253 | user["age"] = 24 254 | 255 | for key in user.keys(): 256 | print(user[key]) 257 | ``` 258 | 259 |
260 |
261 | 262 | [Prev](functions.md) - [Next](/sessions/python_two/Readme.md) 263 |
-------------------------------------------------------------------------------- /sessions/rest_fundamentals/what_is_REST.md: -------------------------------------------------------------------------------- 1 | # What is REST? 2 | 3 | REST, short for **RE**presentational **S**tate **T**ransfer, is a architecture style for defining web services. 4 | 5 | Build on top of the HTTP standard it allows us to access, create and modify resources on a remote system without the need to understand the underlying data storage infrastructure like the database. 6 | 7 | ## How does HTTP work? 8 | 9 | In order to understand REST we need to first understand the protocol that REST is build upon. HTTP, short for, **H**yper**T**ext **T**ransfer **P**rotocol is the protocol behind most of what we know today as the *world wide web*. Initiated in 1989 HTTP allows a user to send a *request* for a resource and receive a *response* back. When you access http://www.cisco.com/index.html your browser is sending a request to the Cisco webserver. This webserver then gathers your information, in this case the html, images, styling and every other component of the website and returns them in one (or more) *responses*. 10 | 11 | This simple *request*-*response* model allows for all the interactions we know when browsing the web. 12 | 13 | ### A look into a HTTP Request 14 | 15 | A HTTP request always follows a well-defined format that is plain text. 16 | 17 | ``` 18 | Request Line = Method Request-URI HTTP-Version 19 | 20 | Request Header(s) 21 | 22 | (Optional) Body 23 | ``` 24 | 25 | Let's have a look at each of these components. 26 | 27 | The *Request Line* is made of the following components: 28 | 29 | * **Method** like `GET`, `DELETE`, `POST` or `PUT`. The method defines what kind of action we want to carry out with the resource. In the example of requesting www.cisco.com we would want to retrieve the information so `GET` would be used. We will have a deep look into the different methods once we talk about REST. 30 | * **Request-URI** this *Uniform Resource Identifier* specifies what resource we want to request from the server. In our sample request we are requesting the file `index.html` from www.cisco.com so the URI would be `http://wwww.cisco.com/index.html` 31 | * **HTTP-Version** the version of the HTTP protocol to use. This will pretty much always be `HTTP/1.1` 32 | 33 | The *Request Line* thus specifies the resource we want (the main page under `index.html` in our example) as well as what we want to do with this resource (`GET` means we are retrieving the information). 34 | 35 | The request line is followed by zero to n *Request Headers*. These can be used to send additional information to the web server. While your request can contain whatever headers you want some common headers include 36 | 37 | * **Accept-Charset** to define what kind of character encoding you want back 38 | * **Accept** to define what kind of data format you would want back. This becomes important for REST APIs later on. 39 | * **User-Agent** to tell the webserver what kind of browser you are using 40 | 41 | Last is the, also optional, body. `GET` requests typically come without a body but `POST` requests that are used to send information to a server would contain that data inside of the request body. When you fill out a form on a web page and click the submit button the information you put into that form will be send to the server inside the request body of a `POST` request. 42 | 43 | So what would a full request look like? Here is an example for a request without a body: 44 | 45 | ``` 46 | GET http://wwww.cisco.com/index.html HTTP/1.1 47 | User-Agent: Mozilla/5.0 (X11; Linux i686; rv:81.0) Gecko/20100101 48 | Accept-Charset: utf-8 49 | ``` 50 | 51 | And here is one for a request with a body: 52 | 53 | ``` 54 | POST http://www.cisc.com/login HTTP/1.1 55 | User-Agent: Mozilla/5.0 (X11; Linux i686; rv:81.0) Gecko/20100101 56 | Accept-Charset: utf-8 57 | Content-Type: application/x-www-form-urlencoded 58 | Content-Length: 120 59 | 60 | username=admin&password=totallynotapassword 61 | ``` 62 | 63 | After receiving such a request the webserver will gather all relevant information and send back a *HTTP response*. 64 | 65 | ### A look into a HTTP Response 66 | 67 | Similar to a HTTP Request, a HTTP Response is a well-defined plain text that looks like this: 68 | 69 | ``` 70 | Status Line = HTTP-Version Status-Code Reason-Phrase 71 | 72 | Response Header(s) 73 | 74 | Message Body 75 | ``` 76 | 77 | The status line, similar to the *request line* gives us status information for the response. It contains: 78 | 79 | * **The HTTP Version** that this server supports. Most likely this will be `HTTP/1.1`. 80 | * **The Status Code** a three-digit integer that defines the status of our response. We will have a deeper look into status codes below. 81 | * **Reason-Phrase** A textual companion of the status code 82 | * **Response Headers** containing additional information.Most web servers send headers like the date, the server version or when this document was last modified as well as the *Content-Type*, for example `text/html` for web pages and the *Content-Length*, back. 83 | * **Message Body** then contains the content itself. In the example of our web request this will be the html needed to display the webpage. 84 | 85 | A full *HTTP response* thus could look like this: 86 | 87 | ``` 88 | HTTP/1.1 200 OK 89 | Date: Mon, 05 Oct 2020 10:00:31 GMT 90 | Content-Length: 230 91 | Content-Type: text/html; charset=utf-8 92 | 93 | 94 | 95 |

Hello World!

96 | 97 | 98 | ``` 99 | 100 | ### HTTP Status Codes 101 | 102 | A extremely useful part of HTTP are the *status codes* of a HTTP response. These 3-digit numbers allow a server to convey the status of a response to the client without having to define custom error formats. 103 | 104 | Status codes come in five different *classes*. Each class is identified by the leading number: 105 | 106 | * **1xx** status codes are purely informational. They signal the client that a request has been received and is being processed. You will rarely see these in the wild. 107 | * **2xx** status codes mean that the request has been understood correctly and that the action has been carried out successfully 108 | * **3xx** status codes are redirects. This means that the client needs to, for example, request a different URI in order to complete the request. 109 | * **4xx** status codes signal a error by the user. This can be anything from a wrongly formatted request to requesting a document that is no longer available (the 404 error that you might have encountered when browsing the web) 110 | * **5xx** status codes signal a server error. These errors occure when, despite a valid request, the server could not fulfill the request and send a response. 111 | 112 | But why did we talk about HTTP again? Because REST is build upon it! 113 | 114 | ## Back to REST 115 | 116 | When dealing with a REST API we are using *HTTP requests* to access, modify or create resources on a server. The server then returns a *HTTP response* that contains the resource we have accessed, modified or created with our *HTTP request*. 117 | 118 | In REST, we use the URI of our HTTP request to specify which resource we would like to access. As an example let's look at a request to the Webex Teams API that retrieves all the user information for the user. 119 | 120 | Since we want to retrieve information we are going to use `GET`. 121 | 122 | Next, we need to define the *resource* we would like to access. The Webex Teams API has a resource called *people*, available in version 1 of the api, so our URI becomes: `/api/v1/people`. 123 | 124 | Next we need to identify the person we want the data of. What kind of identifier is used here is specified by the API. In the case of the Webex Teams API we could either use a `personId` or the keyword `me` which we are going to do in this example. 125 | 126 | Our final request line thus looks like this: `GET /api/v1/people/me`. 127 | 128 | The Webex API server then does a bunch of operations to retrieve all these informations from its backend system. The beauty of the REST API architecture style really is that we don't **need to know** what the API server is doing in the background. All we need to know is the resources available, how they are identified, and what kind of information we can carry out on them. 129 | 130 | After all computing is done the Webex API will return us the requested resource via a *HTTP response* that could look like this: 131 | 132 | ``` 133 | HTTP/1.1 200 OK 134 | Date: Mon, 05 Oct 2020 10:00:31 GMT 135 | Content-Type: application/json 136 | 137 | { 138 | "id": "Y2lzY29zcGFyazovL3VzLA”, 139 | "emails": [ 140 | "mneiding@cisco.com", 141 | ], 142 | "firstName": "Marcel", 143 | "lastName": "Neidinger", 144 | [...] # More lines omitted for brevity 145 | } 146 | ``` 147 | ![sketch of a http request and response](../../res/rest_schematics.png) 148 | 149 | ### HTTP Methods and their associated operations 150 | 151 | In a REST API, each operation that can be carried out on a resource is represented by a HTTP Method. A well-designed REST API should use 152 | 153 | * `GET` to retrieve a resource or a list of resources 154 | * `POST` to create a new resource 155 | * `DELETE` to delete a resource 156 | * `PATCH` to modify a resource 157 | * `PUT` to completely replace a resource 158 | 159 | Sending (i.e. *creating*) a new message on the Webex Teams API would be done using a `POST` request while you could delete a message with a `DELETE` request. 160 | 161 | ### Resources in REST 162 | 163 | The last puzzle piece we need is to understand how REST represents resources. While you can use many different data formats the most common these days is *JSON*. In the example above you saw that the information about our user are returned as a json-formatted string. 164 | 165 | In the same way we can also use json to *modify* or *create* new resources. To create a new resource we would send a `POST` request that, in the request body, contains the resource we would want to create. Following the example mentioned above, here is what a HTTP request for creating a message with the Webex Teams API would look like: 166 | 167 | ``` 168 | POST /api/v1/messages 169 | Content-Type: application/json 170 | 171 | { 172 | "toPersonEmail": "mneiding@cisco.com", 173 | "text": "Hi, this is a message from the API" 174 | } 175 | ``` 176 | 177 | What we would get back is a HTTP response with status code `201 Created`. 178 | 179 | With the theory taken care of, lets have a look at how we can explore REST APIs using a graphical interface called *Postman*. 180 | 181 | > :rocket: If you want to know more about the theory behind REST APIs you can have a look at the [REST API Theoretical Deep Dive](../extras/rest_theory/Readme.md) session. 182 | 183 |
184 | 185 | [Prev](what_is_an_api.md) - [Next](postman.md) 186 |
--------------------------------------------------------------------------------