Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
946c3da
Adding pages
elnelson575 Sep 24, 2025
626a355
Updates so far
elnelson575 Sep 24, 2025
431e43f
Updates
elnelson575 Sep 24, 2025
a8b519d
Updates
elnelson575 Sep 25, 2025
0e0aaba
additional updates
elnelson575 Sep 26, 2025
da8dbc5
Current draft
elnelson575 Sep 29, 2025
699424e
Current
elnelson575 Sep 29, 2025
266ab00
Updates after chat
elnelson575 Sep 30, 2025
9dd6968
Updates
elnelson575 Sep 30, 2025
4805903
Notes plus remove old modules
elnelson575 Sep 30, 2025
c17f67e
More updates
elnelson575 Sep 30, 2025
4049b1f
Updates made
elnelson575 Oct 1, 2025
e95251d
Updated with apps
elnelson575 Oct 2, 2025
55dd234
Overhaul reading data
cpsievert Oct 3, 2025
4cb680e
Updates to persistent storage
elnelson575 Oct 6, 2025
35e1e90
Merge branch 'feat/new-data-docs' of https://github.com/posit-dev/py-…
elnelson575 Oct 6, 2025
1c5e67f
Updated content
elnelson575 Oct 14, 2025
c0fb77a
additional context
elnelson575 Oct 14, 2025
5495740
Progress
elnelson575 Oct 16, 2025
69b5748
both ibis examples added
elnelson575 Oct 16, 2025
84ef625
More updates
elnelson575 Oct 16, 2025
71f6b76
Updates
elnelson575 Oct 16, 2025
8fcdfe1
Connect info
elnelson575 Oct 16, 2025
0a7074b
Added link
elnelson575 Oct 16, 2025
8b72ab7
Switching order
elnelson575 Oct 16, 2025
d94ff03
Correction
elnelson575 Oct 16, 2025
ed4d240
Added notif
elnelson575 Oct 16, 2025
7316570
Simplified
elnelson575 Oct 16, 2025
0d24fe7
wip updates to persistent data article
cpsievert Oct 17, 2025
fe6aa14
Added reading from remote
elnelson575 Oct 17, 2025
236b9d4
Merge branch 'feat/new-data-docs' of https://github.com/posit-dev/py-…
elnelson575 Oct 17, 2025
85c129a
finish brain dump on persistent data
cpsievert Oct 17, 2025
9d8dc96
Remove link in Essentials section
cpsievert Oct 17, 2025
f56a135
Small edits
elnelson575 Oct 17, 2025
ee9a24d
Merge branch 'feat/new-data-docs' of https://github.com/posit-dev/py-…
elnelson575 Oct 17, 2025
bf16744
Corrections to first example
elnelson575 Oct 20, 2025
ae744ed
Corrected GoogleSheets example
elnelson575 Oct 20, 2025
0029cb7
Smoothed out the string/boolean thing
elnelson575 Oct 20, 2025
a71285b
Restoring paste error in setup for sheets
elnelson575 Oct 20, 2025
44ad71c
Removed try except at start
elnelson575 Oct 20, 2025
51e3f27
Updates to dotenv
elnelson575 Oct 20, 2025
ea0e918
Update docs/reading-data.qmd
elnelson575 Oct 20, 2025
5eba31e
Minor updates to wording
elnelson575 Oct 20, 2025
905e4ac
Merge branch 'feat/new-data-docs' of https://github.com/posit-dev/py-…
elnelson575 Oct 20, 2025
8ac4e62
small changes/improvements
cpsievert Oct 20, 2025
a9ea13c
QA on code up to cloud store
elnelson575 Oct 20, 2025
e0b73ed
Merge branch 'feat/new-data-docs' of https://github.com/posit-dev/py-…
elnelson575 Oct 20, 2025
26d9a3e
More corrections to reading data
elnelson575 Oct 20, 2025
e357af0
More correcdtions to reaction section
elnelson575 Oct 20, 2025
00cc639
Final corrections for ibis
elnelson575 Oct 20, 2025
4656336
small changes/improvements
cpsievert Oct 20, 2025
6e59994
Update docs/persistent-storage.qmd
cpsievert Oct 20, 2025
67e0ca4
Updating s3
elnelson575 Oct 21, 2025
ca6c439
Merge branch 'feat/new-data-docs' of https://github.com/posit-dev/py-…
elnelson575 Oct 21, 2025
099a8b8
Moving some examples to separate app folder, add screenshot
elnelson575 Oct 24, 2025
16eeed2
Changing examples
elnelson575 Oct 24, 2025
f29f527
Minor grammar edits to reading data
elnelson575 Oct 24, 2025
4726423
Minor grammar edits for persistent storage
elnelson575 Oct 24, 2025
d335f64
Updating template pages
elnelson575 Oct 24, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions _quarto.yml
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,10 @@ website:
- docs/reactive-foundations.qmd
- docs/reactive-patterns.qmd
- docs/reactive-mutable.qmd
- section: "<span class='emoji-icon'>🗃️</span> __Data__"
contents:
- docs/reading-data.qmd
- docs/persistent-storage.qmd
- section: "<span class='emoji-icon'>📝</span> __Syntax modes__"
contents:
- docs/express-vs-core.qmd
Expand Down
37 changes: 37 additions & 0 deletions docs/apps/persistent-storage/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import polars as pl
from setup import append_info, load_data, save_info
from shiny import reactive
from shiny.express import app_opts, input, render, ui

with ui.sidebar():
ui.input_text("name_input", "Enter your name", placeholder="Your name here")
ui.input_checkbox("checkbox", "I like checkboxes")
ui.input_slider("slider", "My favorite number is:", min=0, max=100, value=50)
ui.input_action_button("submit_button", "Submit")

# Load the initial data into a reactive value when the app starts
data = reactive.value(load_data())


# Append new user data on submit
@reactive.effect
@reactive.event(input.submit_button)
def submit_data():
info = {
"name": input.name_input(),
"checkbox": input.checkbox(),
"favorite_number": input.slider(),
}
# Update the (in-memory) data
d = data()
data.set(append_info(d, info))
# Save info to persistent storage (out-of-memory)
save_info(info)
# Provide some user feedback
ui.notification_show("Submitted, thanks!")


# Data grid that shows the current data
@render.data_frame
def show_results():
return render.DataGrid(data())
32 changes: 32 additions & 0 deletions docs/apps/persistent-storage/gs-setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import gspread
import polars as pl

# Authenticate with Google Sheets using a service account
gc = gspread.service_account(filename="service_account.json")

# Put your URL here
sheet = gc.open_by_url("https://docs.google.com/spreadsheets/d/your_workbook_id")
WORKSHEET = sheet.get_worksheet(0)

import polars as pl

# A polars schema that the data should conform to
SCHEMA = {"name": pl.Utf8, "checkbox": pl.String, "favorite_number": pl.Int32}


def load_data():
return pl.from_dicts(
WORKSHEET.get_all_records(expected_headers=SCHEMA.keys()), schema=SCHEMA
)


def save_info(info: dict):
# Google Sheets expects a list of values for the new row
new_row = list(info.values())
WORKSHEET.append_row(new_row, insert_data_option="INSERT_ROWS")


def append_info(d: pl.DataFrame, info: dict):
# Cast the boolean to a string for storage
info["checkbox"] = str(info["checkbox"])
return pl.concat([d, pl.DataFrame(info, schema=SCHEMA)], how="vertical")
24 changes: 24 additions & 0 deletions docs/apps/persistent-storage/ibis-setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import ibis
import polars as pl

# NOTE: app.py should import CONN and close it via
# `_ = session.on_close(CONN.disconnect)` or similar
CONN = ibis.postgres.connect(
user="postgres", password="", host="localhost", port=5432, database="template1"
)
TABLE_NAME = "testapp"

SCHEMA = {"name": pl.Utf8, "checkbox": pl.Boolean, "favorite_number": pl.Int32}


def load_data():
return CONN.table(TABLE_NAME).to_polars()


def save_info(info: dict):
new_row = pl.DataFrame(info, schema=SCHEMA)
CONN.insert(TABLE_NAME, new_row, overwrite=False)


def append_info(d: pl.DataFrame, info: dict):
return pl.concat([d, pl.DataFrame(info, schema=SCHEMA)], how="vertical")
19 changes: 19 additions & 0 deletions docs/apps/persistent-storage/pg-setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import polars as pl

URI = "postgresql://postgres@localhost:5432/template1"
TABLE_NAME = "testapp"

SCHEMA = {"name": pl.Utf8, "checkbox": pl.Boolean, "favorite_number": pl.Int32}


def load_data():
return pl.read_database_uri(f"SELECT * FROM {TABLE_NAME}", URI)


def save_info(info: dict):
new_row = pl.DataFrame(info, schema=SCHEMA)
new_row.write_database(TABLE_NAME, URI, if_table_exists="append")


def append_info(d: pl.DataFrame, info: dict):
return pl.concat([d, pl.DataFrame(info, schema=SCHEMA)], how="vertical")
36 changes: 36 additions & 0 deletions docs/apps/persistent-storage/s3-setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from datetime import datetime

import polars as pl

DATA_BUCKET = "s3://my-bucket/data/"
STORAGE_OPTIONS = {
"aws_access_key_id": "<secret>",
"aws_secret_access_key": "<secret>",
"aws_region": "us-east-1",
}

SCHEMA = {
"name": pl.Utf8,
"checkbox": pl.String,
"favorite_number": pl.Int32,
"date": pl.Datetime,
}


def load_data():
return pl.read_parquet(
f"{DATA_BUCKET}**/*.parquet", storage_options=STORAGE_OPTIONS
)


def save_info(info: dict):
info["date"] = datetime.now()
new_row = pl.DataFrame(info, schema=SCHEMA)
new_row.write_parquet(
f"{DATA_BUCKET}", partition_by="date", storage_options=STORAGE_OPTIONS
)


def append_info(d: pl.DataFrame, info: dict):
info["date"] = datetime.now()
return pl.concat([d, pl.DataFrame(info, schema=SCHEMA)], how="vertical")
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
36 changes: 36 additions & 0 deletions docs/apps/reading-data/ibis-app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import ibis
from ibis import _
from shiny.express import ui, render, input, session
from shiny import reactive

# Connect to the database (quick, doesn't load data)
con = ibis.postgres.connect(
user="", password="", host="", port=, database=""
)
dat = con.table("weather")
end_session = session.on_ended(con.disconnect)

with ui.sidebar():
ui.input_checkbox_group(
"season",
"Season",
choices=["Summer", "Winter", "Autumn", "Spring"],
selected="Summer",
)
# Import just the unique city names for our selectize input
cities = dat.select("city_name").distinct().execute()["city_name"].to_list()
ui.input_selectize("city", "City", choices=cities)


# Store data manipulations in a reactive calculation
# (convenient when using the data in multiple places)
@reactive.calc
def filtered_dat():
return dat.filter(
[_.city_name == input.city(), _.season.isin(input.season())]
)

# Display the filtered data
@render.data_frame
def results_df():
return filtered_dat().execute()
32 changes: 32 additions & 0 deletions docs/apps/reading-data/polars-app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import polars as pl
from shiny import reactive
from shiny.express import input, render, ui

# Use `scan_*` instead of `read_*` to use the lazy API
dat = pl.scan_parquet("./daily_weather.parquet")

with ui.sidebar():
ui.input_checkbox_group(
"season",
"Season",
choices=["Summer", "Winter", "Fall", "Spring"],
selected="Summer",
)
# Import just the unique city names for our selectize input
cities = dat.select("city_name").unique().collect().to_series().to_list()
ui.input_selectize("city", "City", choices=cities)


# Store manipulation in a reactive calc
# (convenient for writing once and using in multiple places)
@reactive.calc
def filtered_dat():
return dat.filter(pl.col("city_name") == input.city()).filter(
pl.col("season").is_in(input.season())
)


# Display the filtered data
@render.data_frame
def results_df():
return filtered_dat().collect()
18 changes: 18 additions & 0 deletions docs/apps/reading-data/reactive-read-ibis-app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import ibis
from shiny.express import render
from shiny import reactive

con = ibis.postgres.connect(user="", password="", host="", port=, database="")
table = con.table("tablename")

def check_last_updated():
return table.last_updated.max().execute()

# Every 5 seconds, check if the max timestamp has changed
@reactive.poll(check_last_updated, interval_secs=5)
def data():
return table.execute()

@render.data_frame
def result():
return data()
Loading
Loading