flowchart LR
A[Your laptop folder<br/>app.py, .venv, etc.] -->|git init<br/>or GitHub Desktop| B[Local git repo<br/>branch: main]
B -->|stage + commit| C[First commit<br/>snapshot saved locally]
C -->|connect remote| D[Linked to GitHub<br/>empty remote repo]
D -->|push| E[Code on GitHub<br/>public URL]
Lab Week 06
Overview
Welcome to Part 3. This lab has two purposes: walk you through what Assignment 2 expects, and get you to build, run, and publicly deploy your first Shiny for Python app before you leave the room.
By the end of the session, you will have:
- A working one-page Shiny dashboard running on your laptop.
- That same dashboard deployed to a public GitHub Pages URL via
shinylive. - A clear picture of what Assignment 2 requires and what “done” looks like.
Duration: approximately 2 hours.
What you need:
- A laptop with Positron (or VS Code) and
uvinstalled (from Lab 1). - A GitHub account, with
gitconfigured and authenticated. - Completed the reading for 3.0 and 3.1.
Structure:
| Section | Topic | Approx. time |
|---|---|---|
| 1 | Assignment 2 briefing | 15 min |
| 2 | Set up your project | 15 min |
| 3 | Build a one-page Shiny app | 45 min |
| 4 | Deploy to GitHub Pages | 30 min |
| 5 | Homework and stretch goals | 15 min |
Section 1: Assignment 2 briefing
The one-sentence version
By Sunday 10 May 2026, 23:59 NZST, you will publish a public shinylive dashboard, backed by a public GitHub repo, that addresses one urban or transport question about Auckland. A short design report (PDF rendered from Quarto) accompanies the code.
Assignment 2 is worth 15% of the course mark and is submitted via Canvas. Full brief in 3.6.
You will be randomly assigned one of the three data tracks (Public Transport, Commuter + Student OD, or Auckland Crashes) on Tuesday 21 April 2026 at 13:10 NZST. Read all three in 3.6 before that date so you know what is coming.
What markers will look for
Look at the rubric in 3.6 before you start. The short version:
- Question and framing is specific and defensible, scoped to your assigned track.
- Data and methods are clean, documented, and reproducible.
- Reactivity uses
@reactive.calccorrectly. No redundant computation. At least three user inputs actually change the outputs. - Map and visual design is readable and accessible. You have an
ipyleafletmap (required) and a quantitative chart (Plotly or matplotlib). - Deployment to GitHub Pages works on a cold load, all controls respond.
- Design report (under 4 pages, PDF rendered from Quarto) covers motivation, data, architecture, UI walkthrough, and limitations.
Preparing for the random track assignment
Your goal for today is to read all three tracks in 3.6 and note down one question you would ask within each. You will not know which one you get until 21 April, but if you have one sketch per track, the assignment day will not catch you off guard.
The three tracks and their datasets:
- Track A: Public Transport. AT Data Sources (patronage) and AT GIS (buses, ferries).
- Track B: Commuter + Student OD. Stats NZ Data Finder (2023 Census commuter and student datasets).
- Track C: Auckland crashes. Where crashes are happening in Auckland.
Section 2: Set up your project
Step 1: Create the project folder
Pick a sensible name. Your GitHub repo and your deployed URL will inherit it.
mkdir gisci343-a2-<your-topic>
cd gisci343-a2-<your-topic>
# Virtual environment
uv venv
source .venv/bin/activate
# Windows PowerShell: .venv\Scripts\Activate.ps1
# Install Shiny
uv pip install shiny shinyliveStep 2: Scaffold with shiny create
Instead of a blank file, use Shiny’s generator:
shiny createWhen prompted, pick basic-app (single file, one input, one output). Let it write into the current folder.
Run it:
shiny run --reload app.pyOpen the URL Shiny prints in your browser. You should see a slider and a plot. Move the slider. The plot updates. Good: that confirms your install works.
--reload watches your file. Save app.py in your editor and the app restarts automatically. You will save yourself dozens of restarts over the next three weeks.
Step 3: Initialise git and push to GitHub
You need to put your project under version control and push it to a public GitHub repository. There are two ways to do this: via GitHub Desktop (Option A, visual) or via the terminal (Option B, command line). Pick whichever you are more comfortable with; the end result is the same.
The push step below fails for most students if they have never authenticated git with GitHub on this laptop. Signing in to the GitHub website or GitHub Desktop is not enough: the git program itself needs its own credentials, either an SSH key or a personal access token over HTTPS.
If you have not done this yet, pause here and follow Section 1.5 Authenticating with GitHub first. It takes about five minutes and will save you from Permission denied (publickey) or Authentication failed errors in the next step.
Before either option, go to github.com and create a new empty public repository with the same name as your folder. Do not tick “Add a README” or “Add a .gitignore”, because shiny create has already produced those for you and the initial push will conflict otherwise.
If you prefer a GUI, GitHub Desktop does all of the steps below with clicks instead of commands.
Visit here for any further information: https://docs.github.com/en/desktop
1. Add your existing folder to GitHub Desktop.
Open GitHub Desktop and go to File → Add Local Repository. Browse to your project folder (the one containing app.py). GitHub Desktop will detect that it is not yet a git repository and offer to initialise it for you. Click create a repository and accept the defaults.
2. Make your first commit.
GitHub Desktop will show all the files from shiny create in the Changes tab on the left. Each file is ticked (staged) by default. In the Summary box at the bottom left, type a message:
Initial scaffold from shiny create
Click Commit to main.
3. Publish to GitHub.
After the commit, a blue Publish repository button appears at the top. Click it. In the dialog:
- Name: should already match your folder name.
- Keep this code private: untick this (Assignment 2 needs a public repo).
- Organisation: leave as your personal account.
Click Publish Repository. GitHub Desktop pushes everything to a new public repo on github.com.
4. Verify.
Click View on GitHub (or open https://github.com/<your-username>/<your-repo> in a browser). You should see app.py and the other files listed.
From your project folder, run:
# 1. Turn this folder into a git repo
git init
# 2. Stage everything that is not in .gitignore
git add .
# 3. Record the first snapshot
git commit -m "Initial scaffold from shiny create"
# 4. Rename the default branch to main (new repos sometimes default to master)
git branch -M main
# 5. Tell your local repo where the GitHub remote lives
# Replace <your-username> and <your-repo> with your actual values
git remote add origin git@github.com:<your-username>/<your-repo>.git
# 6. Push your local main branch up, and set it to track the remote
git push -u origin mainAfter step 6, refresh your GitHub repo page. Your files should be there.
Two common causes:
- Authentication failure (
Authentication failedin GitHub Desktop,Permission denied (publickey)in the terminal). Yourgithas no valid credentials for GitHub on this laptop. See Section 1.5 Authenticating with GitHub and set up a personal access token, then retry. - Non-empty remote repo. There is already a README on GitHub and your local
mainhas unrelated history. In GitHub Desktop, start over with a fresh empty repo. From the terminal, pull first withgit pull origin main --allow-unrelated-histories, resolve any conflicts, then push again.
Section 3: Build a one-page Shiny app
Replace the contents of app.py with the code below and save. The app shows a small table of Auckland suburbs, filtered by a minimum-population slider, and plots the median income of the surviving suburbs.
from shiny import App, ui, render, reactive
import pandas as pd
import matplotlib.pyplot as plt
# Inline sample data (replace with real data next week)
suburbs = pd.DataFrame({
"suburb": ["Ponsonby", "Parnell", "Mt Eden", "Newmarket",
"Grey Lynn", "Remuera", "Onehunga", "Henderson"],
"population": [12500, 8900, 15300, 11200, 14800, 9600, 13500, 21000],
"median_income": [75000, 95000, 68000, 82000, 71000, 105000, 58000, 52000],
})
app_ui = ui.page_fluid(
ui.h2("Auckland suburbs explorer"),
ui.p("Filter suburbs by minimum population and see how incomes compare."),
ui.input_slider("min_pop", "Minimum population", 0, 25000, 0, step=1000),
ui.output_text("summary"),
ui.output_table("tbl"),
ui.output_plot("chart"),
)
def server(input, output, session):
@reactive.calc
def filtered():
return suburbs[suburbs["population"] >= input.min_pop()]
@render.text
def summary():
df = filtered()
if len(df) == 0:
return "No suburbs match the current filter."
return f"{len(df)} suburbs, mean income ${df['median_income'].mean():,.0f}."
@render.table
def tbl():
return filtered()
@render.plot
def chart():
df = filtered()
fig, ax = plt.subplots(figsize=(7, 4))
ax.barh(df["suburb"], df["median_income"], color="steelblue")
ax.set_xlabel("Median income (NZ$)")
ax.set_title("Median income by suburb")
plt.tight_layout()
return fig
app = App(app_ui, server)Save. Move the slider. The summary, table, and plot should all update together.
Checkpoint
Before moving on, convince yourself that you understand three things. If you cannot answer these out loud, re-read 3.1 before deploying.
- Which lines are the UI, which lines are the server, and which line creates the App?
- What happens if you remove the
@reactive.calcand inlinesuburbs[suburbs["population"] >= input.min_pop()]in bothtblandchart? Does the app still work? Is it still efficient? - The
summaryandchartboth readfiltered(). If the slider moves, how many times does the filter actually run?
Rename def tbl(): to def tabel(): and save. The table will disappear, but the app will not raise an error. Why? Fix it by matching the id in ui.output_table("tbl").
Section 4: Deploy to GitHub Pages
This is the part most students underestimate. Leave yourself at least 30 minutes.
We will deploy straight from the main branch, using a docs/ folder. This is the simplest pattern: one branch, one push, and GitHub Pages serves the static build. (If you prefer the older gh-pages branch style, see the alternative at the bottom of this section.)
flowchart LR
A[app.py<br/>on main] -->|shinylive export| B[docs/ folder<br/>static site]
B -->|commit + push| C[main branch<br/>on GitHub]
C -->|Pages: main /docs| D[Live URL<br/>username.github.io/repo]
Step 1: Export the app to static files
From your project folder, run:
shinylive export . docsThis reads app.py (and anything in the folder) and writes a static website to docs/. Open docs/index.html in your browser. Your app should run, with no Python server anywhere. If it does, your local export works.
docs/ and not site/?
GitHub Pages has built-in support for serving from a /docs folder on the main branch. Using that name means you only need to tick one box in Settings, no extra branch required.
Step 2: Commit the docs/ folder to main
You now need to add the new docs/ folder, commit it, and push it to GitHub. Pick the workflow you prefer.
1. See the changes.
GitHub Desktop will show every file inside your new docs/ folder in the Changes tab on the left. There will be a lot of them (HTML, JS, WebAssembly bundles). That is normal.
2. Commit.
In the Summary box at the bottom left, type:
Deploy app to docs/
Click Commit to main.
3. Push.
At the top of the window, click Push origin. GitHub Desktop sends your commit up to the main branch on GitHub.
4. Verify.
Click View on GitHub (or open your repo in a browser). You should now see a docs/ folder at the top level.
# Stage the new docs/ folder
git add docs
# Commit
git commit -m "Deploy app to docs/"
# Push to main
git push origin mainRefresh your GitHub repo page. You should now see a docs/ folder at the top level.
Step 3: Enable GitHub Pages
In your repo on github.com:
- Go to Settings → Pages.
- Under Build and deployment → Source, pick Deploy from a branch.
- Branch:
main. Folder:/docs. Click Save.
Wait up to a minute. Your app will be live at:
https://<your-username>.github.io/<your-repo>/
First load is slow (10 to 30 seconds) because Pyodide and your packages have to download. Subsequent loads are fast, thanks to browser caching.
Every time you change app.py, rerun shinylive export . docs, then commit and push. GitHub Pages will pick up the new build automatically, usually within a minute.
Section 5: Homework and stretch goals
Minimum (everyone)
Before the Week 7 lab:
- Replace the inline
suburbsDataFrame with real data you actually want to use. A CSV, a GeoPackage, whatever fits your topic. Commit and redeploy. - Add a second input to your app: a dropdown, a date range, a checkbox group. Make an output depend on both.
- Write two paragraphs in a
README.md:- What question does this dashboard try to answer?
- Where does the data come from?
Stretch (if you finish early)
- Swap the
@render.plotfor an ipyleaflet map (see 3.4). Show your data on a real map tile layer. - Restructure the UI into a sidebar layout with
ui.page_sidebar(see 3.3). - Add a
@reactive.eventbutton that recomputes only when clicked, rather than on every slider tick.
Common problems
shinylive exportsucceeds but the deployed app shows a blank page. Check the browser Console (Cmd + Option + I on macOS, F12 on Windows and Linux). Usually a missing package or a path-case mismatch.- GitHub Pages says “404” for a minute after enabling. This is normal. Refresh after 60 seconds.
- The first load takes forever. This is Pyodide downloading. Subsequent loads are fast. If you want to tell users to wait, add a note in your UI.
foliumdoes not work after deploy. Correct: folium is not in Pyodide. Useipyleafletinstead (see 3.4).
Submission
No formal submission this week. Check-in is your public URL, posted in Slack. We will use it as the starting point for the Week 7 lab.
Further reading
- Textbook 3.1 (Your First Shiny App) for the Shiny fundamentals we used today.
- Posit, Shiny for Python: Get started.
- GitHub Docs, Configuring a publishing source for your GitHub Pages site.