# COMP1010 Student Notes

These notes are a compact reference for Python, Flask, PyHTML, HTML, CSS, data
structures, persistence, and usability concepts used in COMP1010. They are not
answers to any specific practice exam.

## General Workflow

- Read the task carefully before writing code.
- Identify the file to edit.
- Search for `TODO`.
- Check the data shape before writing loops.
- Keep code readable and indented consistently.
- Save often.
- Prefer working functionality before visual polish.
- If a task is independent, attempt it even if an earlier task is incomplete.
- If allowed, use provided fallback/faulty functions only for later web tasks.

## Python Basics

```python
name = "Alice"
score = 10
price = 12.5
is_valid = True
items = []
data = {}
```

Common comparisons:

```python
==      # equal
!=      # not equal
>       # greater than
>=      # greater than or equal
<       # less than
<=      # less than or equal
and
or
not
```

Conditions:

```python
if condition:
    ...
elif other_condition:
    ...
else:
    ...
```

Independent conditions:

```python
if condition1:
    ...
if condition2:
    ...
```

One condition chain:

```python
if condition1:
    ...
elif condition2:
    ...
```

## Strings

```python
text.strip()
text.lower()
text.upper()
text.split(",")
"x" in text
text.startswith("A")
len(text)
```

Indexing:

```python
word[0]      # first character
word[1]      # second character
word[-1]     # last character
word[1:4]    # characters at indexes 1, 2, 3
```

Check length before using indexes if the string may be short.

## Numbers And Conversion

Form inputs usually arrive as strings.

```python
count = int(count_text)
amount = float(amount_text)
```

Safe conversion:

```python
try:
    amount = float(amount_text)
except ValueError:
    return "Invalid number"
```

Rounding:

```python
round(value, 2)
```

## Lists

```python
items = ["red", "blue"]
items.append("green")
len(items)
items[0]
```

Loop over a list:

```python
for item in items:
    print(item)
```

Count items:

```python
count = 0
for item in items:
    if item == "win":
        count += 1
```

Sum values:

```python
total = 0
for value in values:
    total += value
```

Build a filtered list:

```python
results = []
for item in items:
    if condition:
        results.append(item)
```

Find the maximum:

```python
best = items[0]
for item in items:
    if item > best:
        best = item
```

Find the minimum:

```python
lowest = items[0]
for item in items:
    if item < lowest:
        lowest = item
```

Sorting:

```python
items.sort()
items.sort(reverse=True)
items.sort(key=lambda item: item["score"])
items.sort(key=lambda item: item["score"], reverse=True)
```

## Dictionaries

A dictionary stores named fields.

```python
player = {
    "id": "alice",
    "name": "Alice",
    "score": 3,
}
```

Read:

```python
player["name"]
```

Update:

```python
player["score"] = player["score"] + 1
```

Check key:

```python
if "name" in player:
    ...
```

Safer lookup:

```python
value = player.get("name")
value = player.get("name", "Unknown")
```

Dictionaries can be used as lookup tables:

```python
costs = {
    "red": 0.5,
    "blue": 1.5,
}

cost = costs["red"]
```

## List Of Dictionaries

Common data shape:

```python
players = [
    {"id": "a", "name": "Alice", "score": 3},
    {"id": "b", "name": "Bob", "score": 1},
]
```

Loop:

```python
for player in players:
    print(player["name"])
```

Find by key:

```python
def find_by_id(data, target_id):
    for item in data:
        if item["id"] == target_id:
            return item
    return None
```

Add a new dictionary:

```python
data.append({
    "id": new_id,
    "name": new_name,
})
```

Update matching item:

```python
for item in data:
    if item["id"] == target_id:
        item["score"] += 1
```

Remove matching item by building a new list:

```python
new_data = []
for item in data:
    if item["id"] != target_id:
        new_data.append(item)
data = new_data
```

Count results in nested data:

```python
wins = 0
for game in player["past_games"]:
    if game["result"] == "win":
        wins += 1
```

## Functions

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

Call a function:

```python
result = add(2, 3)
```

Modify a list passed into a function:

```python
def add_item(items, item):
    items.append(item)
```

Return early:

```python
def find_match(items, target):
    for item in items:
        if item == target:
            return item
    return None
```

## Loops

Use `for` when looping through a collection or range:

```python
for item in items:
    ...
```

```python
for i in range(5):
    ...
```

Use `while` when repeating until a condition changes:

```python
while count > 1:
    count = count - 1
```

## Flask Basics

```python
from flask import Flask, request, redirect, session
import pyhtml as p

app = Flask(__name__)
app.secret_key = "secret"
```

GET route:

```python
@app.get("/")
def homepage():
    return "Hello"
```

POST route:

```python
@app.post("/submit")
def submit():
    value = request.form["value"]
    return redirect("/")
```

Route with a variable:

```python
@app.get("/item/<item_id>")
def item_page(item_id):
    return item_id
```

Redirect:

```python
return redirect("/")
```

## PyHTML Basics

Return a full page:

```python
return str(
    p.html(
        p.head(p.title("Page title")),
        p.body(
            p.h1("Heading"),
            p.p("Paragraph"),
        ),
    )
)
```

Common elements:

```python
p.h1("Heading")
p.h2("Subheading")
p.p("Text")
p.a(href="/")("Home")
p.ul(p.li("One"), p.li("Two"))
p.table(p.tr(p.th("Name"), p.th("Score")))
```

Dynamic list:

```python
p.ul(*[p.li(item["name"]) for item in items])
```

Dynamic table:

```python
p.table(
    p.tr(p.th("Name"), p.th("Score")),
    *[
        p.tr(p.td(item["name"]), p.td(str(item["score"])))
        for item in items
    ],
)
```

## Forms

Form structure:

```python
p.form(action="/submit", method="post")(
    p.input(name="username"),
    p.input(type="submit", value="Submit"),
)
```

The input `name` must match the Flask form key:

```python
username = request.form["username"]
```

Text input:

```python
p.input(name="amount")
```

Number input:

```python
p.input(type="number", name="amount")
```

Password input:

```python
p.input(type="password", name="password")
```

Select:

```python
p.select(name="choice")(
    p.option(value="a")("A"),
    p.option(value="b")("B"),
)
```

Checkbox:

```python
p.input(type="checkbox", name="cats")
```

Read checkbox:

```python
selected = []
if request.form.get("cats") == "on":
    selected.append("cats")
```

Textarea:

```python
p.textarea(name="json_text")
```

## Validation

Empty string:

```python
if text == "":
    return "Missing input"
```

Missing form key:

```python
value = request.form.get("value")
if value is None:
    return "Missing value"
```

Number conversion:

```python
try:
    amount = float(amount_text)
except ValueError:
    return "Amount must be a number"
```

Range check:

```python
if amount < 0:
    return "Amount must not be negative"
```

Duplicate check:

```python
for item in items:
    if item["id"] == new_id:
        return "ID already exists"
```

Not found check:

```python
item = find_by_id(items, target_id)
if item is None:
    return "Item not found"
```

## JSON

Import:

```python
import json
```

JSON string to Python object:

```python
data = json.loads(json_text)
```

Python object to JSON string:

```python
json_text = json.dumps(data)
```

Load JSON from file:

```python
with open("data.json") as f:
    data = json.load(f)
```

Save JSON to file:

```python
with open("data.json", "w") as f:
    json.dump(data, f)
```

Load with default:

```python
def load_data():
    try:
        with open("data.json") as f:
            return json.load(f)
    except FileNotFoundError:
        return []
```

Save helper:

```python
def save_data(data):
    with open("data.json", "w") as f:
        json.dump(data, f)
```

## Session / Cookies

Session stores small per-user temporary data.

```python
from flask import session

app.secret_key = "secret"
```

Read with default:

```python
history = session.get("history", [])
```

Update:

```python
history.append(new_item)
session["history"] = history
session.modified = True
```

Clear:

```python
session.clear()
```

Cookies/session are useful for small per-user data. Files are better for
persistent server-side data.

## HTML Concepts

HTML describes page structure and content.

Common tags:

```html
<h1>Heading</h1>
<p>Paragraph</p>
<a href="/">Home</a>
<form></form>
<input>
<select></select>
<table></table>
```

## CSS Concepts

CSS describes presentation and layout.

Link CSS:

```python
p.link(rel="stylesheet", href="/static/style.css")
```

Basic CSS:

```css
body {
    font-family: sans-serif;
    max-width: 900px;
    margin: 0 auto;
    padding: 20px;
}
```

Tables:

```css
table {
    border-collapse: collapse;
    width: 100%;
}

td, th {
    border: 1px solid #ccc;
    padding: 8px;
}
```

Forms:

```css
input, select, textarea, button {
    display: block;
    margin: 8px 0;
    padding: 8px;
}
```

Cards:

```css
.card {
    border: 1px solid #ccc;
    padding: 12px;
    margin: 10px 0;
}
```

Flex:

```css
.row {
    display: flex;
    gap: 10px;
    align-items: center;
}
```

Grid:

```css
.grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 12px;
}
```

## Common Web App Data Flow

1. GET route displays a form.
2. User submits form.
3. POST route reads `request.form`.
4. Convert strings to numbers if needed.
5. Validate input.
6. Call helper functions.
7. Update data if needed.
8. Save data or session if needed.
9. Display result or redirect.

## Quick Web App Templates

### 1. Flask route template

```python
@app.get("/")
def homepage():
    return str(
        p.html(
            p.head(p.title("Home")),
            p.body(
                p.h1("Home"),
            ),
        )
    )
```

```python
@app.post("/submit")
def submit():
    value = request.form["value"]
    return redirect("/")
```

Route variable:

```python
@app.get("/item/<item_id>")
def item_page(item_id):
    return str(p.html(p.body(p.h1(item_id))))
```

### 2. HTML form template

Plain HTML:

```html
<form action="/submit" method="post">
    <label>Name</label>
    <input name="name">
    <button type="submit">Submit</button>
</form>
```

PyHTML:

```python
p.form(action="/submit", method="post")(
    p.label("Name"),
    p.input(name="name"),
    p.input(type="submit", value="Submit"),
)
```

### 3. `request.form` template

```python
name = request.form["name"]
```

Safer version:

```python
name = request.form.get("name")
if name is None or name == "":
    return "Missing name"
```

Convert numbers:

```python
amount_text = request.form["amount"]
amount = float(amount_text)
```

Checkbox:

```python
selected = []
if request.form.get("cats") == "on":
    selected.append("cats")
```

### 4. List/dict append template

```python
items = []

items.append({
    "id": item_id,
    "name": name,
    "score": 0,
})
```

Append nested record:

```python
player["past_games"].append({
    "opponent": opponent_id,
    "result": result,
})
```

### 5. JSON load/save template

```python
import json

def load_data():
    try:
        with open("data.json") as f:
            return json.load(f)
    except FileNotFoundError:
        return []

def save_data(data):
    with open("data.json", "w") as f:
        json.dump(data, f)
```

JSON from a form string:

```python
json_text = request.form["json_text"]
data = json.loads(json_text)
```

### 6. Jinja `for` loop template

Only use Jinja if the task explicitly allows templates. If the task forbids
`render_template` or `{{ }}`, use PyHTML instead.

```html
<ul>
{% for item in items %}
    <li>{{ item.name }}</li>
{% endfor %}
</ul>
```

Dictionary style:

```html
{% for item in items %}
    <p>{{ item["name"] }}</p>
{% endfor %}
```

### 7. Jinja `if` template

Only use Jinja if the task explicitly allows templates.

```html
{% if items %}
    <p>Items found</p>
{% else %}
    <p>No items found</p>
{% endif %}
```

```html
{% if user %}
    <p>Hello {{ user }}</p>
{% endif %}
```

### 8. Redirect template

```python
from flask import redirect

return redirect("/")
```

Redirect to a dynamic page:

```python
return redirect(f"/item/{item_id}")
```

Common use:

```python
@app.post("/add")
def add():
    # read form and update data
    return redirect("/")
```

### 9. Cookies template

Flask session is usually easier for small per-user data:

```python
session["name"] = name
name = session.get("name")
session.clear()
```

Raw cookie:

```python
from flask import make_response

response = make_response(redirect("/"))
response.set_cookie("name", name)
return response
```

Read raw cookie:

```python
name = request.cookies.get("name")
```

Delete raw cookie:

```python
response = make_response(redirect("/"))
response.delete_cookie("name")
return response
```

### 10. Usability sentence starters

These are generic sentence starters for explaining UI design decisions.

- This improves visibility of system status because the user can see the current result or stored data.
- This supports error prevention because the user is guided toward valid input.
- This supports recognition rather than recall because the available options are shown on the page.
- This improves consistency because the same navigation and button style are used across pages.
- This helps users recover from errors because the message explains what needs to be fixed.
- A labelled input is useful because it tells the user what information is expected.
- A select box is useful when the allowed choices are fixed.
- Checkboxes are useful when the user may choose zero, one, or multiple options.

## Testing Notes

Useful tests:

- normal valid input
- empty input
- invalid number
- zero
- negative number
- duplicate ID/name
- missing item
- empty list
- one item
- multiple items

Simple assert:

```python
assert function_name(input_value) == expected_value
```

## Usability Concepts

Useful Nielsen heuristics:

- Visibility of system status: show results, confirmations, current data.
- Error prevention: constrain inputs, validate data, use clear labels.
- Recognition rather than recall: show options instead of requiring memory.
- Consistency and standards: use consistent navigation and button labels.
- Match between system and real world: use familiar wording and ordering.
- Help users recover from errors: provide specific error messages.

Useful UI features:

- labelled inputs
- meaningful page headings
- clear submit buttons
- select boxes for fixed choices
- checkboxes for multiple optional choices
- readable result lists or tables
- navigation links back to main pages
- confirmation after changing data

## Debug Checklist

- Is the file saved?
- Is indentation correct?
- Are strings in quotes?
- Are dictionary keys spelled exactly correctly?
- Does the form `action` match the route URL?
- Does the form `method` match GET or POST route?
- Does input `name` match `request.form[...]`?
- Did string values get converted before maths?
- Did PyHTML page return `str(p.html(...))`?
- Did CSS link use `/static/style.css`?
- Is `app.secret_key` set before using session?
- Is data saved after changing it?
- Is the route imported function name correct?
- Is the page using PyHTML rather than `render_template` if required?

## Common Error Meanings

`KeyError`: dictionary key is missing.

```python
data["missing_key"]
```

`IndexError`: list or string index is out of range.

```python
items[0]  # when items is empty
```

`ValueError`: conversion failed.

```python
int("abc")
```

`TypeError`: wrong type used.

```python
"5" + 1
```

`NameError`: variable/function name has not been defined.

```python
print(unknown_name)
```

## Small Reminders

- `json.loads` reads JSON from a string.
- `json.load` reads JSON from a file.
- `json.dumps` turns Python data into a JSON string.
- `json.dump` writes Python data to a file.
- `request.form[...]` can crash if the key is missing.
- `request.form.get(...)` returns `None` if the key is missing.
- Checkbox values may be missing when unchecked.
- Use `str(...)` when showing numbers in HTML text if needed.
- Use `redirect` after POST when the action changes stored data.
- Keep helper functions small and testable.
