Skip to content

Commit

Permalink
feat: Editable data frame (#1198)
Browse files Browse the repository at this point in the history
Co-authored-by: Gordon Shotwell <[email protected]>
  • Loading branch information
schloerke and Gordon Shotwell authored Mar 21, 2024
1 parent 2f75a90 commit ebdb97b
Show file tree
Hide file tree
Showing 46 changed files with 2,578 additions and 383 deletions.
13 changes: 4 additions & 9 deletions .github/workflows/pytest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,25 +33,20 @@ jobs:
run: |
make check-tests
- name: Type check with pyright
- name: Type check
if: steps.install.outcome == 'success' && (success() || failure())
run: |
make check-types
- name: Lint with flake8
- name: Lint code
if: steps.install.outcome == 'success' && (success() || failure())
run: |
make check-lint
- name: black
- name: Verify code formatting
if: steps.install.outcome == 'success' && (success() || failure())
run: |
make check-black
- name: isort
if: steps.install.outcome == 'success' && (success() || failure())
run: |
make check-isort
make check-format
playwright-shiny:
runs-on: ${{ matrix.os }}
Expand Down
10 changes: 9 additions & 1 deletion .prettierrc
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
{
"organizeImportsSkipDestructiveCodeActions": true
"organizeImportsSkipDestructiveCodeActions": true,
"overrides": [
{
"files": "**/*.scss",
"options": {
"printWidth": 150
}
}
]
}
7 changes: 7 additions & 0 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"recommendations": [
"posit.shiny-python",
"esbenp.prettier-vscode",
"ms-python.black-formatter",
]
}
4 changes: 4 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[scss]": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[python]": {
"editor.defaultFormatter": "ms-python.black-formatter",
"editor.formatOnSave": true,
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Breaking Changes

* `@render.data_frame` return values of `DataTable` and `DataGrid` had their parameter of `row_selection: Literal["single", "multiple"]` become deprecated. Please use `mode="row_single"` or `mode="row_multiple"` instead. (#1198)

* The `col_widths` argument of `ui.layout_columns()` now sets the `sm` breakpoint by default, rather than the `md` breakpoint. For example, `col_widths=(12, 6, 6)` is now equivalent to `{"sm": (12, 6, 6)}` rather than `{"md": (12, 6, 6)}`. (#1222)

### New features

* Experimental: `@render.data_frame` return values of `DataTable` and `DataGrid` support `mode="edit"` to enable editing of the data table cells. (#1198)

* `ui.card()` and `ui.value_box()` now take an `id` argument that, when provided, is used to report the full screen state of the card or value box to the server. For example, when using `ui.card(id = "my_card", full_screen = TRUE)` you can determine if the card is currently in full screen mode by reading the boolean value of `input.my_card()["full_screen"]`. (#1215)

* Added support for using `shiny.express` in Quarto Dashboards. (#1217)
Expand Down
120 changes: 84 additions & 36 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
.PHONY: help clean% check% format% docs% lint test pyright playwright% install% testrail% coverage release
# https://www.gnu.org/software/make/manual/make.html#Phony-Targets
# Prerequisites of .PHONY are always interpreted as literal target names, never as patterns (even if they contain ‘%’ characters).
# # .PHONY: help clean% check% format% docs% lint test pyright playwright% install% testrail% coverage release js-*
# Using `FORCE` as prerequisite to _force_ the target to always run; https://www.gnu.org/software/make/manual/make.html#index-FORCE
FORCE: ;

.DEFAULT_GOAL := help

define BROWSER_PYSCRIPT
Expand All @@ -23,114 +28,157 @@ export PRINT_HELP_PYSCRIPT

BROWSER := python -c "$$BROWSER_PYSCRIPT"

help:
help: FORCE
@python -c "$$PRINT_HELP_PYSCRIPT" < $(MAKEFILE_LIST)

clean: clean-build clean-pyc clean-test ## remove all build, test, coverage and Python artifacts

clean-build: ## remove build artifacts
# Remove build artifacts
clean-build: FORCE
rm -fr build/
rm -fr dist/
rm -fr .eggs/
find . -name '*.egg-info' -exec rm -fr {} +
find . -name '*.egg' -exec rm -f {} +

clean-pyc: ## remove Python file artifacts
# Remove Python file artifacts
clean-pyc: FORCE
find . -name '*.pyc' -exec rm -f {} +
find . -name '*.pyo' -exec rm -f {} +
find . -name '*~' -exec rm -f {} +
find . -name '__pycache__' -exec rm -fr {} +

clean-test: ## remove test and coverage artifacts
# Remove test and coverage artifacts
clean-test: FORCE
rm -fr .tox/
rm -f .coverage
rm -fr htmlcov/
rm -fr .pytest_cache
rm -rf typings/

typings/appdirs:
echo "Creating appdirs stubs"
pyright --createstub appdirs
typings/folium:
echo "Creating folium stubs"
pyright --createstub folium
typings/uvicorn:
echo "Creating uvicorn stubs"
pyright --createstub uvicorn
typings/seaborn:
echo "Creating seaborn stubs"
pyright --createstub seaborn

typings/matplotlib/__init__.pyi: ## grab type stubs from GitHub
typings/matplotlib/__init__.pyi:
echo "Creating matplotlib stubs"
mkdir -p typings
git clone --depth 1 https://github.com/microsoft/python-type-stubs typings/python-type-stubs
mv typings/python-type-stubs/stubs/matplotlib typings/
rm -rf typings/python-type-stubs

typings/seaborn:
pyright --createstub seaborn
pyright-typings: typings/appdirs typings/folium typings/uvicorn typings/seaborn typings/matplotlib/__init__.pyi

check: check-format check-lint check-types check-tests ## check code, style, types, and test (basic CI)
check-fix: format check-lint check-types check-tests ## check and format code, style, types, and test
check-format: check-black check-isort
check-lint:
@echo "-------- Checking style with flake8 --------"
check-lint: check-flake8
check-types: check-pyright
check-tests: check-pytest

check-flake8: FORCE
@echo "-------- Checking style with flake8 ---------"
flake8 --show-source .
check-black:
@echo "-------- Checking code with black --------"
check-black: FORCE
@echo "-------- Checking code with black -----------"
black --check .
check-isort:
@echo "-------- Sorting imports with isort --------"
check-isort: FORCE
@echo "-------- Sorting imports with isort ---------"
isort --check-only --diff .
check-types: typings/uvicorn typings/matplotlib/__init__.pyi typings/seaborn
check-pyright: pyright-typings
@echo "-------- Checking types with pyright --------"
pyright
check-tests:
@echo "-------- Running tests with pytest --------"
check-pytest: FORCE
@echo "-------- Running tests with pytest ----------"
python3 tests/pytest/asyncio_prevent.py
pytest

pyright: check-types ## check types with pyright
lint: check-lint ## check style with flake8
# Check types with pyright
pyright: check-types
# Check style with flake8
lint: check-lint
test: check-tests ## check tests quickly with the default Python

format: format-black format-isort ## format code with black and isort
format-black:
format-black: FORCE
@echo "-------- Formatting code with black --------"
black .
format-isort:
format-isort: FORCE
@echo "-------- Sorting imports with isort --------"
isort .

docs: ## docs: build docs with quartodoc
@echo "-------- Building docs with quartodoc --------"
docs: FORCE ## docs: build docs with quartodoc
@echo "-------- Building docs with quartodoc ------"
@cd docs && make quartodoc

docs-preview: ## docs: preview docs in browser
docs-preview: FORCE ## docs: preview docs in browser
@echo "-------- Previewing docs in browser --------"
@cd docs && make serve


install-npm: FORCE
$(if $(shell which npm), @echo -n, $(error Please install node.js and npm first. See https://nodejs.org/en/download/ for instructions.))
js/node_modules: install-npm
@echo "-------- Installing node_modules -----------"
@cd js && npm install
js-build: js/node_modules ## Build JS assets
@echo "-------- Building JS assets ----------------"
@cd js && npm run build
js-watch: js/node_modules
@echo "-------- Continuously building JS assets ---"
@cd js && npm run watch
js-watch-fast: js/node_modules ## Continuously build JS assets (development)
@echo "-------- Previewing docs in browser --------"
@cd js && npm run watch-fast
clean-js: FORCE
@echo "-------- Removing js/node_modules ----------"
rm -rf js/node_modules

# Default `SUB_FILE` to empty
SUB_FILE:=

install-playwright:
install-playwright: FORCE
playwright install --with-deps

install-trcli:
which trcli || pip install trcli
install-trcli: FORCE
$(if $(shell which trcli), @echo -n, $(shell pip install trcli))

install-rsconnect: ## install the main version of rsconnect till pypi version supports shiny express
# Installs the main version of rsconnect till pypi version supports shiny express
install-rsconnect: FORCE
pip install git+https://github.com/rstudio/rsconnect-python.git#egg=rsconnect-python

playwright-shiny: install-playwright ## end-to-end tests with playwright
# end-to-end tests with playwright; (SUB_FILE="" within tests/playwright/shiny/)
playwright-shiny: install-playwright
pytest tests/playwright/shiny/$(SUB_FILE)

playwright-deploys: install-playwright install-rsconnect ## end-to-end tests on examples with playwright
# end-to-end tests on deployed apps with playwright; (SUB_FILE="" within tests/playwright/deploys/)
playwright-deploys: install-playwright install-rsconnect
pytest tests/playwright/deploys/$(SUB_FILE)

playwright-examples: install-playwright ## end-to-end tests on examples with playwright
# end-to-end tests on all py-shiny examples with playwright; (SUB_FILE="" within tests/playwright/examples/)
playwright-examples: install-playwright
pytest tests/playwright/examples/$(SUB_FILE)

playwright-debug: install-playwright ## All end-to-end tests, chrome only, headed
playwright-debug: install-playwright ## All end-to-end tests, chrome only, headed; (SUB_FILE="" within tests/playwright/)
pytest -c tests/playwright/playwright-pytest.ini tests/playwright/$(SUB_FILE)

playwright-show-trace: ## Show trace of failed tests
npx playwright show-trace test-results/*/trace.zip

testrail-junit: install-playwright install-trcli ## end-to-end tests with playwright and generate junit report
# end-to-end tests with playwright and generate junit report
testrail-junit: install-playwright install-trcli
pytest tests/playwright/shiny/$(SUB_FILE) --junitxml=report.xml

coverage: ## check combined code coverage (must run e2e last)
coverage: FORCE ## check combined code coverage (must run e2e last)
pytest --cov-report term-missing --cov=shiny tests/pytest/ tests/playwright/shiny/$(SUB_FILE)
coverage html
$(BROWSER) htmlcov/index.html
Expand All @@ -151,9 +199,9 @@ install: dist
pip uninstall -y shiny
python3 -m pip install dist/shiny*.whl

install-deps: ## install dependencies
install-deps: FORCE ## install dependencies
pip install -e ".[dev,test]" --upgrade

# ## If caching is ever used, we could run:
# install-deps: ## install latest dependencies
# install-deps: FORCE ## install latest dependencies
# pip install --editable ".[dev,test]" --upgrade --upgrade-strategy eager
21 changes: 16 additions & 5 deletions examples/dataframe/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,14 @@ def app_ui(req):
ui.input_select(
"selection_mode",
"Selection mode",
{"none": "(None)", "single": "Single", "multiple": "Multiple"},
{
"none": "(None)",
"single_row": "Single",
"multiple_row": "Multiple",
},
selected="multiple",
),
ui.input_switch("editable", "Edit", False),
ui.input_switch("filters", "Filters", True),
ui.input_switch("gridstyle", "Grid", True),
ui.input_switch("fullwidth", "Take full width", True),
Expand Down Expand Up @@ -61,6 +66,12 @@ def server(input: Inputs, output: Outputs, session: Session):
def update_df():
return df.set(sns.load_dataset(req(input.dataset())))

@reactive.calc
def selection_mode():
if input.editable():
return "edit"
return input.selection_mode()

@render.data_frame
def grid():
height = 350
Expand All @@ -71,15 +82,15 @@ def grid():
width=width,
height=height,
filters=input.filters(),
row_selection_mode=input.selection_mode(),
mode=selection_mode(),
)
else:
return render.DataTable(
df(),
width=width,
height=height,
filters=input.filters(),
row_selection_mode=input.selection_mode(),
mode=selection_mode(),
)

@reactive.effect
Expand All @@ -92,10 +103,10 @@ def handle_edit():

@render.text
def detail():
selected_rows = input.grid_selected_rows() or ()
selected_rows = grid.input_selected_rows() or ()
if len(selected_rows) > 0:
# "split", "records", "index", "columns", "values", "table"
return df().iloc[list(input.grid_selected_rows())]
return df().iloc[list(grid.input_selected_rows())]


app = App(app_ui, server)
1 change: 1 addition & 0 deletions js/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ module.exports = {
"plugin:react/recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended",
"plugin:react-hooks/recommended",
],
ignorePatterns: ["dist/*"],
overrides: [],
Expand Down
10 changes: 9 additions & 1 deletion js/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@ import { BuildOptions, build } from "esbuild";
import { sassPlugin } from "esbuild-sass-plugin";
import * as fs from "node:fs/promises";

let minify = true;
process.argv.forEach((val, index) => {
if (val === "--minify=false") {
console.log("Disabling minification");
minify = false;
}
});

const outDir = "../shiny/www/shared/py-shiny";

async function bundle_helper(
Expand All @@ -11,7 +19,7 @@ async function bundle_helper(
const result = await build({
format: "esm",
bundle: true,
minify: true,
minify: minify,
sourcemap: true,
metafile: false,
outdir: outDir,
Expand Down
Loading

0 comments on commit ebdb97b

Please sign in to comment.