├── .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 | 
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 | 
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 | 
41 |
42 | Clicking "Send" will send this request and return a response.
43 |
44 | 
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 | 
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 |
--------------------------------------------------------------------------------