A form with relationships worth understanding

One change.
A whole chain reacts.

A room, a few guests, a longer stay. Seven formulas turn three choices into a detailed estimate. Follow the dependencies, then change a rule yourself.

Recorded from the real Python page. Silent, with optional step captions and a cursor highlight.

Plan your stay

More than
price × quantity.

  1. Choose a room: Classic starts at €90 per night; Superior at €140. Both include two guests.
  2. Add guests and nights: extra guests add €25 each per night. Seven nights unlock a 10% discount.
  3. Follow the consequences: the nightly price, subtotal, discount, total and rounded 30% deposit update through dependent formulas.
  4. Prepare a summary: the controller reads the current total on click and stores a snapshot.

Illustrative prices. This example calculates an estimate; it does not reserve a room or take payment.

What depends on what?

The calculation
has a visible structure.

Each formula names its inputs and writes its result to Data. The next formula can use that result. The page needs no separate change handler on every field.

  1. 01 / ROOM + GUESTSNightly price

    Superior €140 + one extra guest €25 = €165.

  2. 02 / NIGHTLY PRICE × NIGHTSStay subtotal

    €165 × 7 nights = €1,155.

  3. 03 / SUBTOTAL + STAY LENGTHDiscount → total

    Seven nights earn €115.50 off. Total: €1,039.50.

  4. 04 / TOTALDeposit

    30% of the total, rounded to euros: €312.

Change seven nights to six: the discount disappears, the total becomes €990, and the deposit becomes €297. One edit reaches every dependent result.

Validation happens before accepted Data changes. An empty field, five guests or 31 nights is rejected; the visible draft stays in the field while the calculation keeps using the previous accepted values.

Inspect · edit Python · reload

Understand the result.
Then change the rule.

Follow the real Data values in the inspector. Change the discount threshold in the Python file from seven to five nights, save, then reload and compare the same inputs.

Real Python gallery recordings, with the inspector enlarged and cursor highlighted. The before/after code comparison comes from the saved Python files. Silent, with optional captions. Use fullscreen on smaller screens.

  1. Inspect: seven nights produce subtotal 630 → discount 63 → total 567 → deposit 170. Five nights produce 450 → 0 → 450 → 135.
  2. Edit Python: change nights >= 7 to nights >= 5 in the discount formula. Update the accompanying explanatory text, save, and let the development server reload.
  3. Reload and compare: the page returns to its defaults. Enter the same five-night stay again: subtotal 450 → discount 45 → total 405 → deposit 122.

The inspector reads Data throughout. The formula is changed in the Python file and loaded with the new page; it is not edited or activated in live Source.

Make the same Python change yourself →

The complete Python page

Read the rules.
See their connections.

42 nonblank Python lines, including the class, fields, seven formulas, breakdown and controller. The short expressions run as JavaScript in the browser. CSS and host setup are provided separately.

pages/index.py · complete source
from gramlot import Page as BasePage


class Page(BasePage):
    title = "Plan your stay"
    devices = ("desktop", "tablet", "phone")
    css = ("/themes/gramlot-base/theme.css",)

    def main(self, root):
        pane = root.main(datapath="estimate", class_="estimate-page")
        pane.h1("Plan your stay")
        pane.p("Choose a room, guests and nights. Follow the calculation below.")
        fields = pane.formlet(columns=1, lbl_position="TL", box_c_width="100%")
        fields.filteringSelect(value="^.room", lbl="Room", id="room",
                               values="classic:Classic,superior:Superior", validate_notnull=True)
        fields.numberTextBox(value="^.guests", lbl="Guests", id="guests", dtype="L",
                             validate_notnull=True, validate_min=1, validate_max=4)
        fields.numberTextBox(value="^.nights", lbl="Nights", id="nights", dtype="L",
                             validate_notnull=True, validate_min=1, validate_max=30)
        pane.dataFormula(".rate", "room === 'superior' ? 140 : 90", room="^.room", _init=True)
        pane.dataFormula(".extra", "Math.max(guests - 2, 0) * 25", guests="^.guests", _init=True)
        pane.dataFormula(".nightly", "rate + extra", rate="^.rate", extra="^.extra", _init=True)
        pane.dataFormula(".subtotal", "nightly * nights",
                         nightly="^.nightly", nights="^.nights", _init=True)
        pane.dataFormula(".discount", "nights >= 7 ? subtotal * 0.10 : 0",
                         nights="^.nights", subtotal="^.subtotal", _init=True)
        pane.dataFormula(".total", "subtotal - discount",
                         subtotal="^.subtotal", discount="^.discount", _init=True)
        pane.dataFormula(".deposit", "Math.round(total * 0.30)", total="^.total", _init=True)
        costs = pane.section(class_="estimate-costs", aria_label="Calculation breakdown")
        costs.p("^.rate", mask="Room / night · €{}", id="rate")
        costs.p("^.extra", mask="Extra guests / night · €{}", id="extra")
        costs.p("^.nightly", mask="Nightly price · €{}", id="nightly")
        costs.p("^.subtotal", mask="Stay subtotal · €{}", id="subtotal")
        costs.p("^.discount", mask="Long-stay discount · −€{}", id="discount")
        costs.p("^.total", mask="Stay total · €{}", id="total")
        costs.p("^.deposit", mask="30% deposit · €{}", id="deposit")
        pane.button("Prepare summary", id="prepare").dataController(
            "this.SET('.summary', 'Stay estimate: €' + total)", total="=.total")
        pane.p("^.summary", id="summary", aria_live="polite")
        pane.p("Two guests included; €25 per extra guest/night. Seven nights: 10% off. Deposit rounded to euros.")
        pane.p("Invalid drafts do not change accepted Data. Summary updates on click. Illustrative prices only.")
        pane.dataSetter(".room", "classic")
        pane.dataSetter(".guests", 2)
        pane.dataSetter(".nights", 3)

The separate stylesheet controls appearance. app.py activates the standard input/layout collections and the Python host. Both are in the ZIP; the line count refers to the Page, not the installation.

^ makes formula inputs reactive. The controller reads =.total when its button fires: changing a field updates the calculation, while the prepared summary stays until the next click.

Make the next change without an LLM

Change one rule.
Keep the connections.

Three independent exercises. Start from Classic, two guests and three nights. Edit one rule, save and refresh. Update the explanatory text too when it repeats a changed price or threshold.

01 / ONE RULE

Allow a larger group.

Before

validate_max=4

After

validate_max=6

Enter five guests: the nightly price becomes €165, the total €495 and the deposit €149. Seven guests are still rejected.

Download this edited Page →
02 / ONE RULE

Reward shorter stays.

Before

nights >= 7 ? subtotal * 0.10 : 0

After

nights >= 5 ? subtotal * 0.10 : 0

Enter five nights: the €450 subtotal now earns €45 off. Total: €405. Deposit: €122. No other formula needs to change.

Download this edited Page →
03 / ONE RULE

Lower the guest supplement.

Before

Math.max(guests - 2, 0) * 25

After

Math.max(guests - 2, 0) * 15

Enter three guests: the nightly price becomes €105, the total €315 and the deposit €95. The downstream formulas use the new result.

Download this edited Page →

Run and modify it locally

Your editor.
Your Python project.

Unzip the project and open its folder in a terminal. Python 3.11 or later is required.

macOS / Linux · inside room-estimate/
python -m venv .venv
source .venv/bin/activate
python -m pip install -r requirements.txt
uvicorn app:application --reload --reload-dir pages

Open http://127.0.0.1:8000/. Edit pages/index.py for the exercises. The project also contains runnable copies at /guest_limit, /discount and /supplement. No packages are pinned; the dependencies provide the framework and the standard collection bundles.

With or without an LLM

You can follow the rule.
You can change it yourself.

The value is not just fewer lines. It is being able to find a business rule, understand which results depend on it, and make the next change without asking someone else to reconstruct the application.