> Source URL: /unit-3/project-paths/aiden-p/aiden-p-2026-04-18.guide
# Aiden's Project Guide

**Project:** Clash Royale Personality Quiz (pivoting to Web App)
**Category:** Web Development (Flask)
**Last updated:** April 18

---

> Note: This guide reflects the latest state of your project repo + our Wednesday lab discussion. It may not match the most up-to-date version if you've worked since.

## Where You Are

Two things happened this week:

1. **You have the hard part working.** `load_cards`, `map_personality_to_type`, and `find_best_card` in `data/main.py` are solid. They're your quiz brain. We're keeping all of them.
2. **You decided to go Flask.** End goal: someone visits your site, takes the quiz, sees a Clash Royale character card as their result.

This week you'll reshape the project for Flask, split your code into a handwritten **business logic module** and a **Flask app**, and move your questions into a data file.

---

## Project Structure

Your project splits into two kinds of code:

- **Business logic — you handwrite this.** The functions that make *your* project different from anyone else's. You must be able to explain every line out loud.
- **Library / view code — agent-assisted is fine.** Flask routes, HTML templates, Bootstrap classes. The agent already knows these patterns — let it scaffold, then read what it produced and edit it until you understand it.

Target layout by Thursday:

```
final-project-pierai6-1/
├── app.py                  ← Flask routes — agent-assisted OK
├── quiz.py                 ← business logic — handwrite (yours to own)
├── pyproject.toml
├── data/
│   ├── cards.txt           ← data
│   └── questions.json      ← data (new)
└── templates/
    ├── home.html           ← HTML — agent-assisted OK
    ├── quiz.html           ← HTML — agent-assisted OK
    └── result.html         ← HTML — agent-assisted OK
```

Why the split? From [Lecture 1: The MVP](../../lectures/01-the-mvp/01-the-mvp.lecture.md) — your final demo is about what's *yours*. The Flask code looks the same for every Flask app; your scoring algorithm does not.

---

## Phase 1: Initialize the Project for Flask

> **Agent-assisted is fine here.** `uv init`, `uv add flask`, and the "hello world" Flask app are the same for every Flask project. You're not demonstrating anything new by typing them — you're setting up the scaffold.

### Objective

Move from the CLI setup to a running Flask app. Your first browser "hello" is the goal.

### Instructions

- [ ] From your project root, run `uv init`
- [ ] Run `uv add flask`
- [ ] Create a new file `app.py` at the project root with one route (see hint)
- [ ] Run the server and confirm you see "hello" in your browser

### Sample Output

Visit `http://127.0.0.1:5000`. You should see:

```
hello
```

### Hints

**Minimal `app.py`:**

```python
from flask import Flask

app = Flask(__name__)

@app.route("/")
def home():
    return "hello"
```

**Run the server:**

```bash
uv run flask --app app run --debug
```

`--debug` makes Flask reload on save so you don't have to restart the server.

> **Optional — get help from your agent:**
>
> ```text
> I'm pivoting from a CLI to Flask. Walk me through uv init, uv add
> flask, and a minimal app.py. Don't touch data/main.py yet.
> ```

---

## Phase 2: Move the Questions into a JSON File

> **Handwrite this yourself.** The question set IS your quiz — what you ask and in what order. The file format is trivial; the content is yours.

### Objective

Your 4 questions currently live inside `main()` in `data/main.py`. Data belongs in a data file. Move them to `data/questions.json`.

### Instructions

- [ ] Create `data/questions.json`
- [ ] Copy your 4 questions + options into it using the format in the hint below. **Type at least 3 of them yourself** (don't let the agent do all of them) — this is content you're authoring.
- [ ] Write `load_questions(filename)` (you'll put it in `quiz.py` in Phase 3)

### Sample Structure

`data/questions.json`:

```json
[
  {
    "id": "personality_type",
    "text": "What role do you naturally take in a group?",
    "options": {
      "a": "leader",
      "b": "supporter",
      "c": "strategist",
      "d": "lone wolf"
    }
  }
]
```

Add the other three (`style`, `energy`, `trait`) following the same shape. It's fine to ask your agent to do this for you!

### Hints

**Reading JSON in Python:**

```python
import json

def load_questions(filename):
    with open(filename, "r") as file:
        return json.load(file)
```

`json.load()` returns a Python list of dicts — the same shape you typed in the file. No parsing logic needed.

**Testing it worked:**

```python
questions = load_questions("data/questions.json")
print(questions[0]["text"])  # should print your first question
```

> **Optional — get help from your agent:**
>
> ```text
> Move my questions data/questions.json using the JSON structure described below:
> <paste in the sample structure code above>
> ```

---

## Phase 3: Build `quiz.py` (your Business Logic Module)

> **Handwrite this yourself.** This IS your project — the scoring algorithm that picks a Clash Royale character from someone's answers. You will be asked to explain this code out loud on demo day.

### Objective

Move your scoring logic out of `data/main.py` and into a new `quiz.py` at the project root. This will allow you to work on your core logic code separate from the UI code (CLI or web).

### Instructions

- [ ] Create `quiz.py` at the project root
- [ ] Move `load_cards` and `find_best_card` from `data/main.py` into it
- [ ] Move `load_questions` (from Phase 2) into it
- [ ] **Do not** move `ask_question` or `main` — those were CLI-only
- [ ] Refactor `find_best_card` so it takes one `answers` dict instead of 4 separate arguments (see hint)

### Hints

**New `find_best_card` signature:**

```python
def find_best_card(cards, answers):
    # answers = {"card_type": "troop", "style": "aggressive",
    #            "energy": "fast", "trait": "brave"}
    best_card = None
    best_score = -1
    for card in cards:
        score = 0
        if card["card_type"] == answers["card_type"]:
            score += 3
        # ... same pattern for style / energy / trait
    return best_card, best_score
```

Why the dict? The Flask form gives you back `request.form`, which already behaves like a dict. Cleaner to pass it through than unpack into 4 arguments.

**Tip.** Write `quiz.py` without any Flask imports. If it imports `flask` or `render_template`, you've leaked view code into the business logic. Keep them separate.

> **Optional — get help from your agent:**
>
> ```text
> Walk me through refactoring find_best_card to take an `answers` dict.
> Show old and new signatures side-by-side. Don't change anything
> until I say so — I want to understand the refactor first.
> ```

---

## Phase 4: Wire Up the Flask Routes

> **Agent-assisted is fine here.** Read what the agent produces, edit it until you understand it, then move on. The business logic is in `quiz.py`.

### Objective

Replace your "hello" route with three real routes that run the quiz.

### Instructions

- [ ] Open `app.py`
- [ ] Add three routes:
  - `GET /` → render `home.html`
  - `GET /quiz` → call `load_questions()`, pass to `quiz.html`
  - `POST /result` → read form answers, call `find_best_card`, render `result.html`
- [ ] Import from `quiz.py` (your business logic)

### Hints

**Full route skeleton:**

```python
from flask import Flask, render_template, request
from quiz import load_cards, load_questions, find_best_card, map_personality_to_type

app = Flask(__name__)

@app.route("/")
def home():
    return render_template("home.html")

@app.route("/quiz")
def quiz():
    questions = load_questions("data/questions.json")
    return render_template("quiz.html", questions=questions)

@app.route("/result", methods=["POST"])
def result():
    personality_type = request.form.get("personality_type")
    card_type = map_personality_to_type(personality_type)
    answers = {
        "card_type": card_type,
        "style": request.form.get("style"),
        "energy": request.form.get("energy"),
        "trait": request.form.get("trait"),
    }
    cards = load_cards("data/cards.txt")
    best_card, _ = find_best_card(cards, answers)
    return render_template("result.html", card=best_card)
```

**Read, don't copy.** After the agent writes this for you, read it line by line. Understand what `request.form.get()` returns. Understand why `methods=["POST"]` is there on `/result` but not `/`. Ask about anything unclear.

> **Optional — get help from your agent:**
>
> ```text
> Help me plan three Flask routes for my quiz (/, /quiz, /result).
> Show me the route stubs first. Then walk me through request.form
> and how it reflects what's in my HTML form.
> ```

---

## Phase 5: Templates + Styling

> **Agent-assisted is fine here.** HTML markup and Bootstrap classes are UI code.

### Objective

Three simple HTML pages so the quiz actually looks like a quiz.

### Instructions

- [ ] Create a `templates/` folder
- [ ] `home.html`: title + "Take the Quiz" button linking to `/quiz`
- [ ] `quiz.html`: loop over `questions`, radio buttons for each option, one form POSTing to `/result`
- [ ] `result.html`: show matched card as a character card
- [ ] Add Bootstrap via CDN (one `<link>` tag)

### Sample Output

`/quiz` should show:

```
Question 1: What role do you naturally take in a group?
 ( ) leader
 ( ) supporter
 ( ) strategist
 ( ) lone wolf

Question 2: How do you usually approach things?
 ...

[ Submit ]
```

### Hints

**Looping over questions in `quiz.html`:**

```html
<form action="/result" method="post">
  {% for question in questions %}
    <fieldset>
      <legend>{{ question.text }}</legend>
      {% for key, value in question.options.items() %}
        <label>
          <input type="radio" name="{{ question.id }}" value="{{ value }}">
          {{ value }}
        </label><br>
      {% endfor %}
    </fieldset>
  {% endfor %}
  <button type="submit">Submit</button>
</form>
```

**Bootstrap CDN (in `<head>`):**

```html
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
```

> **Optional — get help from your agent:**
>
> ```text
> Scaffold home.html, quiz.html, and result.html for my quiz. Use
> Bootstrap via CDN. Keep the markup simple enough that I can edit
> it. Don't change app.py or quiz.py.
> ```

---

## Phase 6: Cleanup + Journal

> **Handwrite this yourself.** The journal entry is your primary functionality and where you will need to explain your thinking for the demo.

### Objective

Delete the CLI leftovers and write your Checkpoint 2 journal entry.

### Instructions

- [ ] Delete `data/main.py` (keep `cards.txt` and `questions.json`)
- [ ] Walk through the full quiz in your browser end-to-end
- [ ] Add a Checkpoint 2 entry to `project.journal.md`:
  - What you pivoted (CLI → Flask)
  - What's in `quiz.py` (business logic) vs `app.py` (library code)
  - Which parts you wrote yourself vs which parts came from the agent
  - What's still rough
- [ ] Commit and push

### Hints

**Commit message idea:**

```
checkpoint 2: flask pivot + quiz.py business logic module
```

---

## Checkpoint 2 Readiness

By Thursday April 23 at 3pm:

- [ ] `pyproject.toml` with `flask`
- [ ] `quiz.py` at project root holds all scoring logic (no Flask imports)
- [ ] `data/questions.json` holds all 4 questions
- [ ] `app.py` has home / quiz / result routes
- [ ] `templates/home.html`, `quiz.html`, `result.html` exist
- [ ] Full quiz clicks through end-to-end in the browser
- [ ] Old `data/main.py` removed
- [ ] Checkpoint 2 entry in `project.journal.md`
- [ ] Committed and pushed

## Helpful Resources

- [Checkpoint 2 Instructions](../../projects/final-project-checkpoint-2.project.md)
- [Flask Setup Guide](../../resources/flask-setup.guide.md)
- [Lecture 1: The MVP](../../lectures/01-the-mvp/01-the-mvp.lecture.md)


---

## Backlinks

The following sources link to this document:

- [April 18 -- Checkpoint 2 (Working MVP)](/unit-3/project-paths/aiden-p/aiden-p.path.llm.md)
