# Exam notes

# Loop description questions

### Difference between for loops, while loops, if-else statement

- A for loop is used when the number of iterations is known or when iterating over a collection (eg. list, string, range). It automatically handles iteration. A for loop is better when iterating through a list of items:
    - Example:
    
    books = [’a’, ‘b’, ‘c’]
    
    for book in books:
    
    print(book)
    
- A while loop is used when the number of iterations is known and depends on a condition. It continues running as long as the condition remains true. A while loop is better when repeating until a condition is met.
    - Example: asking users to key in password multiple times, the number of attempts the users may take is unknown.
    
    password = “”
    
    while password ! = “secret”:
    
    password = input(”enter password: “)
    

- An if-else statement is used to make decisions. It runs one block of code if a condition is true, and another if the condition is false.
    - example
    
    if age < 10:
    
    print(”67”)
    
    else:
    
    print(”711”)
    

## For loop rule for lists and dictionaries

- If data starts with [ ] → list → **for item in list**
- If data starts with { } → dictionary → **for key, value in dict.items()**

total = 0

for item in list_of_dicts:
      value1 = item["key1"]

value2 = item["key2"]

result = do_something(value1,value2)

total += result

## List of Dict vs Single Dict

- Use list of dict when there are multiple entries, and each entry has multiple fields
    - EG:
    
    [
    {"colour": "red", "length": 10},
    {"colour": "blue", "length": 5}
    ]
    
- Use single dict when there is one object with multiple properties
    - EG:
    
    session["user"] = {
    "name": "John Doe",
    "age": 67
    }
    

# **HTML vs CSS vs Python**

When building a web page:

- **HTML** is used to structure the content of the page (e.g. headings, paragraphs, forms, buttons).
    - Use when creating the structure of a webpage
        - EG: Create a form for user input
- **CSS** is used to style and format the page (e.g. colours, layout, fonts).
    - use to change how the webpage looks
        - EG: Style the form by adding background colour, padding
- **Python** is used for backend logic and dynamic behaviour (e.g. processing user input, generating results, interacting with data).
    - use when you need logic, calculations, or to handle user input and generate dynamic content.
        - EG: Process the form and return results

# Patterns

### Search pattern:

#can assume that it is not found, then loop through one by one and if match, would be found 

found = False

for item in collection:
    if match:
        found = True
        break

    if not found:
          print("Not found")

# 2 - Loops

### Double loop question

- if there are 2 conditions

```python
def get_movie_recommendations(genre, language):

    data = get_movies_2()

    if genre not in data:
        return None

    recommended_movies = []

    genre_movies = data[genre]

    for movie in genre_movies:
        suitable = True

        for language in languages:
            if movie not in data[languages]:
                suitable = False

        if suitable:
            recommended_movies.append(movie)

    return recommended_movies
```

## Functions

- global variable_name:
→ allows function to MODIFY variable defined outside
- Without global:
→ Python treats it as a new local variable
→ causes error
- def keyword, name, parameters (inside the brackets )
    - def name_of_function(parameter_1, parameter_2):
- return: gives something back to the function which called this function
- function should be pure logic so that it can be reused, don’t put input() INSIDE functions
- Benefits:
    - code re-use
    - parametersisation: can run the same code with different inputs (remember the box example, can make many different boxes with different height and widths using the same function)
    - abstraction: code is more readable (can open and hide the details)
    - maintainability: easier to make changes to the code as user requirements change.
    - testing: once a function is tested thoroughly, can use repeatedly and confidently

- `def` → create function
- indentation → what belongs to function
- `return` → send value back
- function call → runs the function
- result → used like a normal value

- any(...) = returns True if AT LEAST ONE item is True
    - any(c.islower() for c in password)

- end parameter in the print function: end=””
    - means no extra characters are added, so the next print continues immediately on the same line
    - for i in range(3):
    print(i, end=" ")
        - output: 0 1 2
        
- Inside a function:
→ ALWAYS use the parameter name, NOT the original variable name
- Function call:
→ passes the real data INTO the parameter

def print_list(my_list):
for item in my_list:
print(item)

numbers = [1, 2, 3]

print_list(numbers)

- inside the function: my_list = numbers

### Common mistakes w functions

- if a function has parameters, should NOT ASK FOR INPUT inside it
    - Functions either:
    1. TAKE inputs (parameters)
    OR
    2. ASK inputs (input())
        
        BUT NOT BOTH 
        

# 4: Collections

## Lists

- allows us to have multiple, of the same kind of things within one variable
- an element is one of the items on the list
- .append(”xxx”) to add things into the list (note: it’ll be at the very back of the list)
- .clear() to remove everything from the list
- .remove(”xxx”) to remove a SPECIFIC element from the list
- indexing into a list
    - index: the number of the item in the list
    - starts from 0,1,2.
    - to overwrite an item in the list, just choose the index and type in new item
        - list[0] = “new item”
- .insert() to add a new item in a specific spot in the list.
    - list.insert(0, “water”) → “water” will be inserted in the first spot in the list
- .sort() to arrange the elements in a list from A to Z
- .sort(reverse = True) to arrange elements from Z to A
- len(list) to find out the number of elements in a list
- to join multiple lists, use +
    - all_lists = list_1 + list_2
- THE ORDER OF THE ELEMENTS ON A LIST MATTERS
    - [”a”, “c”, “b”] is NOT EQUAL to [”a”, “b”, “c”]

| print method  | example | output  |
| --- | --- | --- |
| print() | print(fruit_list) | [’apple’, ‘banana’, ‘orange’] |
| using a loop | for fruit in fruit_list:                              print(fruit) | apple                                                banana                                             orange |
- important lecture example: apple banana nectarine:
    - key point: DON’T CHANGE A LIST WHILE LOOPING THROUGH IT, WILL END UP SKIPPING ITEMS
    - because if something gets removed, the list shifts LEFT but the pointer always shifts RIGHT

## Dictionary

### 2 Uses

**1. Efficient lookup and data retrieval (mapping keys to values)**

Dictionaries store data as key–value pairs, allowing fast and direct access to information using a unique key. This makes them highly efficient for searching and retrieving data, as users can access values in constant time without scanning through the entire dataset. For example, a dictionary can map a movie title to its details (e.g. genre, duration), allowing quick lookup of specific information.

---

**2. Structuring and representing complex data relationships**

Dictionaries are useful for organising related data into structured formats, especially when modelling real-world entities. Each key can represent an attribute (e.g. “genre”, “actors”), and the corresponding value can be a single item or a collection (e.g. a list of actors). This allows dictionaries to represent more complex data structures in a clear and flexible way.

- terminology: key, value
    - each key has exactly ONE entry
    - different keys can have the same value

| print | example | output |
| --- | --- | --- |
| whole dictionary | print(dictionary_name) | {’name’: ‘smith’, ‘age’: 38} |
| a specific element  | print(dictionary_name[”name”]) | smith |
- .pop(”xxx”) to remove a specific key value pair
- to add a key-value pair: dictionary_name[”new_key”] = new_value

| print | example | output |
| --- | --- | --- |
| keys  | print(dictionary_name.keys()) | ([’name’, age’]) |
| values | print(dictionary_name.values()) | ([’smith’, 38]) |
- turn into a list using **list**(dictionary_name.keys()) or **list**(dictionary_name.values())
- .items() returns the key-value pairs of the dictionary
    - for key, value in dict.items():
    print(key, value)
- dict[variable] means: use the VALUE inside the variable as the KEY
- **COUNTING PATTERN:**
    - **counts = {}**
        
        **for item in items:
             if item in counts:
                   counts[item] += 1
             else:
                   counts[item] = 1**
        
- **Group values under each key:**
    - **if key not in dict:
         dict[key] = [ ]**
        
        **dict[key].append(value)**
        

| Code | What it checks |
| --- | --- |
| `x in dict` |  keys |
| `x in dict.keys()` | keys (same thing) |
| `x in dict.values()` | values |
| `x in dict.items()` | (key, value) pairs |

## 3- Complex data structures

- assert keyword: lets you test if a condition in your code returns True, if not, the program will raise an AssertionError. Used when debugging code.
    - assert 1==1 → nothing happens cos it’s true
    - assert 1==5 → Assertion error, python will tell u which line it went wrong
- None: a special value in Python that represent values that are unknown

## CDS Description template

### List of dictionaries

- each dictionary stores a ____ and its ____.
- Advantages: More readable and explicit, as each ____ is clearly defined with keys like "___" and "___" Easier for beginners to understand
- Disadvantages: Inefficient to search for a specific ____, as it requires iterating through the entire list. More verbose and repetitive

### Dictionary

- where each key is a ____ and the value is  _____.
- Advantages: Much more efficient for lookup, as a ___ can be accessed directly using its key. More concise and better suited for representing relationships between ____.
- Disadvantages: Less immediately readable for beginners. Requires understanding of dictionary structures
- For the ____function, Dictionary is preferable because the function likely needs to repeatedly look up  ____ for a given ____. Using a dictionary allows direct access to the ____ without looping through all ____, making it more efficient and suitable for this task.

### Filter items based on conditions

```
result = [ ]

for item in items:
	valid = True

	for condition in conditions:
    if item FAILS condition:
        valid = False

	if valid:
    result.append(item)
 
 return result
```

### Nested loop when condition itself is a list

for condition in conditions:
    for sub_condition in condition_list:
         if sub_condition not satisfied:
            fail

## HTML

- tags: <>
- <head> is for the BROWSER
    - NOT visible on the page
- <body> is for the USER
    - the content of the page
- <a> is an ANCHOR tag
    - used to create a clickable link on a webpage
    - basic structure:
        - **<a href="URL">text you click</a>**
        - <a href="URL" target="_blank">Link</a>  opens link in a new tab
- table
├── thead (headers)
└── tr (rows)
    ├── td (column 1)
    ├── td (column 2)
   └── td (column 3)

| **Web Browser** | **Web server**  |
| --- | --- |
| client-side software used to access and view webpages | server-side software that stores and delivers web content |
| browsers are used by end-users to interact with websites and web applications  | handle incoming requests from browsers and send appropriate responses  |
| browser sends HTTP/HTTPS requests to a server for content  | server listens for incoming HTTP requests and sends data back to the browser |
| browser allows users to interact with the web (forms, clicks etc) | server processes requests and returns data, but doesn’t interact with the user directly  |
| REQUEST (Gimme stuff to display) | RESPONSE (here’s stuff to display (html)) |
- to add an image:
    - <img width=”50%” src=”https://xxxxx”>
- to insert a link:
    - **<a href=”https://cutecats.com” target=”_blank”>**Go to Cute Cats</a>
- lists <li>
    - ordered list <ol>
    - unordered list <ul>

# PYHTML

- return str(
p.html(...)
)
    - in flask, every route must return something the browser can display
    - the browser only understands html
    - so return str() means to convert the HTML object into a string, then send it to the browser

### HTTP Methods

- in the context of the world wide web, a HTTP method is a request method that a client (such as a web browser) can use when making a request to a server (such as a web server)
- GET: This method is used to **retrieve data from the server**.
    - GO TO A URL, basically to load a page
    - e.g In an eCommerce application, there is a search button to search for an item. after entering a keyword if we noticed, the keyword you searched for gets displayed in the URL. This request is used when the data is not sensitive.
- POST: This method makes **enables users to send data over to the server**.
    - IF YOU ARE SENDING A FORM, send data to a server
    - e.g In any authentication-enabled application, the registration and login form is the best example for the post method.
    - Whenever we enter Information and submit the data get transferred over to the POST request.
    - Unlike get request there is no information will be ever displayed in the URL.

## Cookies 🍪

### TEMPLATE

from flask import Flask, request, **session**

app = Flask(__**name**__)
**app.secret_key = "secret"**  # required for session

in the post route:

### Adding items one by one:

if “list_name” not in session:

session[”list_name”] = [ ] 

session[”list_name”].append(”abc_text”)

session.modified = True 

### Receiving full data

if “list_name” not in session:

session[”list_name”] = data_name 

### Example

```python

@app.post("/cost")
def cost():

    colour = request.form.get("colour")
    length = float(request.form.get("length"))

    new_segment = {
        "colour": colour,
        "length": length}

    if "scarf_info" not in session:
        session["scarf_info"] = []

    session["scarf_info"].append(new_segment)
    session.modified = True

    scarf_info = session["scarf_info"]
    cost_of_scarf = calculate_scarf_cost(scarf_info)

    list_items = []
    for segment in scarf_info:
        list_items.append(
            p.li(f"{segment['colour']} scarf segment, {segment['length']}cm")
        )

    return str(
        p.html(
            p.head(p.title("Scarf preview")),
            p.link(rel="stylesheet", href="/static/style.css"),
            p.body(
                p.h1("Your scarf so far"),
                p.div(class_="card")(
                    p.ol(*list_items),
                    p.p(f"Current total cost: ${cost_of_scarf}"),
                    p.a("Add another segment!", href = "/"),
                    p.form(action="/order", method="post")(
                        p.input(type="submit", value="Order scarf")
                    )
                )
            )
        )
    )
```

### What are cookies:

- small pieces of data stored in the user’s browser
- created by a website and saved on the client (user side)
- stored as key-value pairs (sorta like a mini dictionary)
- used to remember information between requests

### How do they work

- user visits a website
- server sends a cookie → **browser stores it**
- on future requests:
    - browser automatically sends the cookie back to the server
- server reads the cookie to:
    - identify the user
    - remember preferences
    - maintain login state

*HTTP is stateless, does not remember anything → cookies help fake memory between requests 

## Encryption

### What is it

- turning readable data into scrambled data so others can’t understand it
    - "username = unicorn" → "x7a9$#@!”

### Secret key

- a special password used to lock (encrypt) and unlock (decrypt) the data
- only the server know this key

### Why it matters

- cookies can be tampered by users
- so flask:
    - encrypts / signs cookies using the secret key
- this ensures:
    - users can’t fake or change the data

### In a nutshell🥜:

**session**

A Flask dictionary-like object that remembers user data across requests.

**cookie**

Small data stored in the browser. Flask sessions use cookies behind the scenes.

**secret_key**

Used by Flask to protect/sign the session cookie so users cannot safely fake it.

**If I use:**

- **append()**
- **clear()**
    - REMOVE EVERYTHING FROM SESSION (delete ALL session data)
    - session.clear()
- **pop()**
    - delete only one saved thing / key
    - eg. user checks out, so clear only their cart but keep other session data like username.
        - session.pop(”cart”, None)
            - the None prevents crash if key doesn’t exist
- **remove()**
- **update()**

**→  I MUST write session.modified = True**

session = cookie storage

Flask only sees:
session = NEW VALUE

Flask DOES NOT see:
changes INSIDE the value

So must let flask know u changed it:

→ session.modified = True

# Serialisation

## JSON (Javascript Object Notation)

- JSON is useful for storing data in web applications because it provides a structured, human-readable format that can easily represent complex data such as lists and dictionaries. It allows data to persist between program runs and enables communication between the client and server in a standardised format.
- JSON is a format for storing and transmitting data using key-value pairs

Functions:

- write_json_file(file, data)
→ save Python data into file
- read_json_file(file)
→ load data from file into Python (and becomes a python object)
- create_if_not_exist(file, empty/list)
→ create file if missing

Pattern:

1. read_json_file()
2. modify data
3. write_json_file()

Important:
Changes are NOT saved unless written back to file

### Convert JSON to python dict

data = json.loads(string)

## Cookies and File Description

When deciding between cookies and files/databases, consider:

- Data scope: Cookies → per user (e.g. login state, preferences), File/database → shared across all users (e.g. user accounts, analytics)
- Data size: Cookies → very small (≈4KB limit), Files/databases → can store large amounts of data
- Security: Cookies → stored on user’s browser → less secure, Files/databases → stored on server → more secure
- Persistence: Cookies → can expire or be deleted by users, Files/databases → more permanent

Therefore:

Use cookies for login sessions and preferences,
Use files/databases for storing important or shared data

## API (Application Programming Interface)

An API (Application Programming Interface) allows programs to communicate with each other by sending requests and receiving data, often over the internet.

- can’t access the internals of the system, can only talk to the API layer
- A REST API is a type of API that uses HTTP requests (e.g. GET, POST) to retrieve or send data, typically in JSON format.

### Example

response = requests.get("[https://api.example.com/data](https://api.example.com/data)")
data = response.json()

- it sends a get request to the API and converts the JSON response into a python dictionary

### Process of using API:

1. client sends a request (eg. GET request to an URL)
2. The server processes the request
3. The server returns a response (usually JSON data)
4. The client processes the data 

### Why are APIs useful?

- access real-time data
- avoid building everything yourself
- connect different systems

### Why is JSON commonly used in APIs?

- lightweight, easy to read, easily converted to Python dictionaries

# CSS

- Link the CSS to [server.py](http://server.py) (put this under the p.head or p.title)
    - **p.link(rel="stylesheet", href="/static/style.css"),**
- `box-sizing: border-box` ensures that an element’s width includes its padding and border, preventing layout overflow and making sizing more predictable.

### Template

```css
* {
    box-sizing: border-box;
}

body {
    font-family: Arial, Helvetica, sans-serif;
    background-color: #f5f7fa;
    color: #333;
    margin: 0;
    padding: 24px;
}

h1 {
    text-align: center;
    margin-bottom: 24px;
}

p {
    margin: 12px 0;
}

form, .card {
    background-color: white;
    width: 420px;
    max-width: 90%;
    margin: 20px auto;
    padding: 24px;

    border: 1px solid #ddd;
    border-radius: 10px;
    box-shadow: 0 4px 10px rgba(0, 0, 0, 0.08);
}

label {
    display: block;
    margin: 8px 0 4px;
    font-weight: bold;
}

input[type="text"],
input[type="number"],
input[type="email"],
input[type="password"],
select,
textarea {
    width: 100%;
    padding: 10px;
    margin: 6px 0 14px;

    border: 1px solid #ccc;
    border-radius: 6px;
}

input[type="radio"],
input[type="checkbox"] {
    width: auto;
    margin-right: 8px;
}

input[type="submit"],
button {
    width: 100%;
    padding: 10px;
    margin-top: 16px;

    background-color: #4CAF50;
    color: white;

    border: none;
    border-radius: 6px;
    cursor: pointer;
}

input[type="submit"]:hover,
button:hover {
    background-color: #45a049;
}

a {
    color: #5a189a;
}

.card form {
    width: 100%;
    max-width: none;
    margin: 16px 0 0;
    padding: 0;
    border: none;
    box-shadow: none;
    background-color: transparent;
}

#######adding a history button###########
#p.a("History", href="/history", class_="history-btn")

.history-btn {
    position: fixed;       
    top: 20px;
    right: 20px;

    background-color: #4CAF50;
    color: white;
    padding: 10px 16px;

    border-radius: 6px;
    text-decoration: none;
    font-size: 14px;

    box-shadow: 0 2px 6px rgba(0,0,0,0.2);
}

.history-btn:hover {
    background-color: #45a049;
}

```

## USABILITY

## 1. Error prevention

- Prevent users from making mistakes
- Examples:
    - radio buttons instead of typing
    - required fields
    - number input instead of text

Sentence starter:

> This prevents users from entering invalid inputs and reduces errors.
> 

---

## 2. Visibility of system status

- Show user what’s happening
- Examples:
    - results page
    - confirmation message
    - updated output

Sentence starter:

> This provides immediate feedback so users know their action was successful.
> 

---

## 3. Consistency & standards

- Same layout / style everywhere
- Examples:
    - same button style
    - same navigation
    - consistent wording

Sentence starter:

> This ensures a consistent experience and reduces user confusion.
> 

---

## 4. User control & freedom

- Users can undo / go back
- Examples:
    - “Go back” button
    - navigation links

Sentence starter:

> This allows users to easily navigate and correct their actions.
> 

---

## 5. Recognition over recall

- Don’t make users remember things
- Examples:
    - dropdowns
    - visible options (like your radio buttons)

Sentence starter:

> This reduces cognitive load by allowing users to recognise options instead of remembering them.
> 

---

## 6. Aesthetic & minimalist design

- Keep it simple and clean
- Examples:
    - spacing
    - clear headings
    - CSS styling

Sentence starter:

> This improves readability and makes the interface easier to use.
> 

---

### Description points:

Several usability heuristics are applied to reduce user error: 

- **RADIO BUTTONS**:  enforces mutual exclusivity, ensuring that users can only select one option. This prevents conflicting inputs and guides users toward valid selections.
- **Range slider**: This restricts input to a valid numerical range, preventing users from entering invalid or unrealistic values and reducing input errors.
- **Checkboxes** for pets allow multiple selections where appropriate. This matches real-world expectations and avoids forcing users into incorrect single-choice inputs.
- **clear labels and prompts** (e.g. “Select a movie genre: ”, “How many grains of sugar are required?”), which improve clarity and reduce the likelihood of user misunderstanding.
- The **use of placeholder text** in fields such as email and password provides guidance on what input is expected, helping prevent incorrect or incomplete entries.
- Finally, **grouping related inputs together and using a logical layout** improves visibility and reduces cognitive load, making it easier for users to complete the form correctly.

# Helpful functions for web app

### JSON input + session template

```python

data = json.loads(string) #convert json to python dict 

if "key" not in session:
    session["key"] = data  
else:
    session["key"] = merge(session["key"], data)

session.modified = True

current = session["key"]
```

### Generate checkbox

p.input(type = “checkbox”, name = “food”, value = “pizza”),

p.label(”pizza”)

retrieve:

foods = request.form.getlist(”food) 

Function:

```python
def generate_checkbox(name, value):
    return p.div(
        p.label(
            p.input(
                type="checkbox",
                name=name,
                value=value
            ),
            f" {value.capitalize()}"
        )
    )
```

to call the function:

```python
 #in app.get("/")
 p.h1("Favourite pet?"),
 p.form(
	 generate_checkbox("pets", "dogs"),
	 generate_checkbox("pets", "cats"),
	 p.p(
		 input(type="submit)
		 )
	)
```

```python
@app.post("/")
def submit_form():
    liked_pets = request.form.getlist("pets")

    print()

    return str(
        p.html(
            p.body(
                p.h1("The pets you like are"),
                p.ul(p.li(item.capitalize()) for item in liked_pets),
            )
        )
    )
```

### Radio buttons

- can manually add every single option inside p.form:

p.input( type = “radio”, name= “xxx”, value=”67”, required= True),

p.label(”yyyyy”),

- OR CAN USE A ***FUNCTION***:

```python
def radio_option(value):
    return p.div(
        p.label(
            p.input(type="radio", name="xxxxxxx", value=str(value), required=True),
            f" {value.capitalize()}"
        )
    )
```

p.p(”Select xxx: “)

radio_option(10),

radio_option(15),

radio_option(20),

### Dropdown

```python
def render_dropdown(dpdn_name, options_text):
    options_options = []
    for id in options_text:
        cap_id = id.capitalize()
        options_options.append(p.option(value=id)(cap_id))

    dropdown = p.select(name=dpdn_name)(options_options)
    return dropdown
```

- if the dropdown items are values in a dictionary → can directly access them using the keys
    - remember to import the dictionary into [server.py](http://server.py) before doing so
- call the function inside the route:
    - render_dropdown(”colour”,  colour_costs.keys()),

### Page Heading function

```python
def get_page_heading():
    heading = p.table(
        p.tr(
            p.td(p.h1("Calculator")),
            p.td(
                p.form(
                    p.input(type="submit", value="History", formaction="/history"),
                    p.input(type="submit", value="Clear History", formaction="/clear_history")
                )
            )
        )
    )
    return heading
```

# WEB SERVER CHECKLIST

- make sure when implementing a form, to put in the action and method
    - **p.form(action=”/xxx”, method=”post”)(**
- make sure to LINK THE CSS
- to simplify the code, create functions for:
    - checklists, radio buttons, heading
- make sure to always return str(response)
- check indentation
- in the post route:
    - call whatever function is needed
    - always request.form.get to retrieve information selected or keyed in by user in the home route
        - request.form.getlist(”xxx”) is more than 1 option is selected (for checkboxes)
- Check the form values. If your buttons send `"Sun"` but your data uses `"sun"`, convert it by using:
    - .lower() or .upper()

## Search history

- create search_history = [ ] at the top of [server.py](http://server.py) (don’t put it in any routes)
- in “/”, remember to add p.a(”History”, href = “/history”) so users are directed to the history page

In the “/results” route: remember to INSERT the retrieved data from the form into the search history

```python
    search_history.insert(0,{
    "lighting": lighting,
    "pets": pets,
    "results": plants
})
```

```python
@app.get("/history")
def history():
    history_items = []

    for search in search_history:
        history_items.append(
            p.li(
                p.p(f"Lighting: {search['lighting']}"),
                p.p(f"Pets: {search['pets']}"),
                p.p(f"Results: {search['results']}"),
            )
        )
    response = p.html(
        p.head(p.title("Search History")),
        p.body(
            p.h1("Search History"),
            p.a("Back to home", href ="/"),
            p.ul(*history_items)
        )
    )
    return str(response)
```

# Miscellaneous stuff (Earlier weeks)

- Markdown files
    - The more hashes, the smaller the heading

### Casting

- changes variable to a different data type
    - int(): converts a variable into an integer
    - str(): converts a variable into a string
    - type(): find out a variable’s data type

### Joining variables and text into strings

- use + or fstrings
- remember to str() any number if using +
- if want to append number (eg, 42 → 423, 100 → 1003), turn the num into a str first before adding together

### Input keyword

Input()

- the most basic form of function that allows user to enter a string until they press enter
- rarely used, as string entered by user is lost

variable_name = input()

- string entered by user is stored in variable_name

variable_name = input(”Enter…”)

- most common usage
- display a prompt communicating to the user what to enter
- stores the string entered by the user in a variable

### Booleans

- must start with a capital (ie. true or false does not work, must be True or False)
- can use to compare both integers and strings
    - for strings, note that the capitalisation makes a difference
        - Water == water will return False

### Not

- check if things are not equal

```
print(not(5 == 10))
# = print(not(False))
# = print(True)
# = True
```

- or check if something is not in a list
    - if word not in word_dictionary:

### And

- both must be true to get a True
- if there’s one true one false, will get False
- two false get a False

### Or

- either must be true to get a True
- one true one false, will get True

### Indexes

```python
print(favourite_animal[0]) # first letter
print(favourite_animal[-1]) # last letter
print(favourite_animal[1:-1]) # all the middle letters
```

### Newlines

- \n
- to print \n, use \\n
- to print \\n, use \\\\n

### In

- to ask: “ is this in that?”
- print(”.” in “Hello!”)
    - False

### Functions vs Methods (1.16)

- Functions: can be run on almost any variable, regardless of data type
- methods: are for a specific data type

Clean answer:

- A function is a reusable block of code that performs a task. It is defined using def and exists independently (not tied to any object).
    - example:

def add(a, b):
    return a + b

result = add(2, 3)   # 5

- Use a function when the task is general purpose and does not depend on a specific object.
    - example:

def calculate_area(radius):
      return 3.14 * radius * radius

- A method is a function that is associated with an object. It is called using dot notation (.) on that object. It usually operates on that object’s data.
    - example:
    
    text = "hello"
    result = text.upper()   # "HELLO"
    
    - here, upper() is a method, it belongs to the string object text.
- Use a method when the task is related to a specific object, and you are working with built-in types (string, list, dict, etc)
    - example:
    
    numbers = [1, 2, 3]
    numbers.append(4)   # modifies the list
    

# Simple calculator logic

### Logic

1. GET /question
    - show form
    - p.input(type = “text”, name = “question_tb”),
        - input name="question_tb"
2. POST /solution
    - get input using request.form["question_tb"]
        - question_text = request.form[”question_tb”]
    - call calculate_answer()
    - show answer page
3. calculate_answer() 
    - THIS IS A FUNCTION, THE PARAMETER IS THE QUESTION_TEXT
    - check which operator is in the string
    - split string into two parts
    - convert parts to int
    - return calculated answer
4. session["calculations"]
    - stores past calculations
    - needs SECRET_KEY (cos it’s a 🍪)
    - use session.modified = True after changing list
5. GET /history
    - loop through session["calculations"]
    - turn each item into [p.li](http://p.li/)()
    - display inside p.ul()
6. POST /clear_history
    - clear session["calculations"]

# [SERVER](http://SERVER.PY) TEMPLATE

```css
import pyhtml as p
from flask import Flask, request

app = Flask(__name__)

@app.get("/")
def homepage():
    return str(
        p.html(
            p.head(
                p.title("My App"),
                p.link(rel="stylesheet", href="/static/style.css")
            ),
            p.body(
                p.h1("Welcome!"),

                p.form(action="/result", method="post")(
                    p.label("Enter name: "),
                    p.input(type="text", name="name"),

                    p.br(),

                    p.label("Choose option: "),
                    p.select(name="choice")(
                        p.option(value="A")("Option A"),
                        p.option(value="B")("Option B")
                    ),

                    p.br(),
                    p.input(type="submit", value="Submit")
                )
            )
        )
    )

@app.post("/result")
def result():
    name = request.form.get("name")
    choice = request.form.get("choice")

    return str(
        p.html(
            p.head(
                p.title("Result"),
                p.link(rel="stylesheet", href="/static/style.css")
            ),
            p.body(
                p.h1("Result"),
                p.div(class_="card")(
	                p.p(f"Hello {name}!"),
	                p.p(f"You chose: {choice}"),
	                p.a("Go back", href="/")
	                )
            )
        )
    )

if __name__ == "__main__":
    app.run(port=5002)

```