generated from posit-conf-2023/workshop-template
-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
276 additions
and
4 deletions.
There are no files selected for viewing
20 changes: 20 additions & 0 deletions
20
_freeze/materials/d1-04-deploy-admin/index/execute-results/html.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
{ | ||
"hash": "3e56d1f02a91584fae41636bfdd25373", | ||
"result": { | ||
"markdown": "---\ntitle: \"Deployment & Administration\"\nsubtitle: \"posit::conf(2023) <br> Shiny in Production: Tools & Techniques\"\nfooter: \"[{{< var workshop_short_url >}}]({{< var workshop_full_url >}})\"\nformat: \n revealjs:\n theme: [default, ../slides.scss] # moon= teal bg | dark\n scrollable: true\n incremental: false\n slide-number: c/t # c/t | c | h/v | h.v\n slide-tone: false #true\n code-line-numbers: true\n history: false\nrevealjs-plugins:\n - codewindow\n---\n\n::: {.cell}\n<style type=\"text/css\">\n.centerheading h2 {\n text-align: center;\n}\n\n.mono {\n font-family: monospace;\n font-size: 1.4em;\n}\n\n.center-xy {\n margin: 0;\n position: absolute;\n top: 25%;\n left: 0%;\n -ms-transform: translateY(-50%), translateX(-50%);\n transform: translateY(-50%), translateX(-50%);\n}\n</style>\n:::\n\n\n## Choose your Adventure\n\n::: columns\n::: {.column width=\"50%\"}\n### Posit Connect\n:::\n\n::: {.column width=\"50%\"}\n### Containers\n:::\n:::\n\n::: notes\nTODO: Find pictures demonstrating the \"App & Packages Only\" (Posit Connect, Shinyapps.io) versus \"The whole environment\" (Containers)\n:::\n\n## Deployment Checklist\n\n✅ Create `app.R` in directory\n\n✅ Take note of any custom environment variables\n\n✅ Ensure `DESCRIPTION` is up-to-date with required packages\n\n✅ Remove outdated functions / scripts\n\n\n\n## {.center-xy}\n\n::: mono\n::: {.codewindow .r}\n03_deploy.R\n\n\n::: {.cell}\n\n```{.r .cell-code}\n## RStudio ----\n## If you want to deploy on RStudio related platforms\ngolem::add_rstudioconnect_file()\ngolem::add_shinyappsio_file()\ngolem::add_shinyserver_file()\n```\n:::\n\n:::\n:::\n\n# `<svg aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 640 512\" style=\"height:1em;width:1.25em;vertical-align:-0.125em;margin-left:auto;margin-right:auto;font-size:inherit;fill:white;overflow:visible;position:relative;\"><path d=\"M80 48a48 48 0 1 1 96 0A48 48 0 1 1 80 48zm64 193.7v65.1l51 51c7.1 7.1 11.8 16.2 13.4 26.1l15.2 90.9c2.9 17.4-8.9 33.9-26.3 36.8s-33.9-8.9-36.8-26.3l-14.3-85.9L66.8 320C54.8 308 48 291.7 48 274.7V186.6c0-32.4 26.2-58.6 58.6-58.6c24.1 0 46.5 12 59.9 32l47.4 71.1 10.1 5V160c0-17.7 14.3-32 32-32H384c17.7 0 32 14.3 32 32v76.2l10.1-5L473.5 160c13.3-20 35.8-32 59.9-32c32.4 0 58.6 26.2 58.6 58.6v88.1c0 17-6.7 33.3-18.7 45.3l-79.4 79.4-14.3 85.9c-2.9 17.4-19.4 29.2-36.8 26.3s-29.2-19.4-26.3-36.8l15.2-90.9c1.6-9.9 6.3-19 13.4-26.1l51-51V241.7l-19 28.5c-4.6 7-11 12.6-18.5 16.3l-59.6 29.8c-2.4 1.3-4.9 2.2-7.6 2.8c-2.6 .6-5.3 .9-7.9 .8H256.7c-2.5 .1-5-.2-7.5-.7c-2.9-.6-5.6-1.6-8.1-3l-59.5-29.8c-7.5-3.7-13.8-9.4-18.5-16.3l-19-28.5zM2.3 468.1L50.1 348.6l49.2 49.2-37.6 94c-6.6 16.4-25.2 24.4-41.6 17.8S-4.3 484.5 2.3 468.1zM512 0a48 48 0 1 1 0 96 48 48 0 1 1 0-96zm77.9 348.6l47.8 119.5c6.6 16.4-1.4 35-17.8 41.6s-35-1.4-41.6-17.8l-37.6-94 49.2-49.2z\"/></svg>`{=html} Code-Along {background-color=\"#17395c\"}\n\nPush-Button Deployment to Posit Connect\n\n---\n\n::: mono\n::: {.codewindow .r width=\"1000px\"}\n03_deploy.R\n\n::: {.cell}\n\n```{.r .cell-code}\nrsconnect::deployApp(\n appName = \"legobricksapp\",\n appTitle = desc::desc_get_field(\"Package\"),\n appFiles = c(\n \"R/\",\n \"inst/\",\n \"data/\",\n \"NAMESPACE\",\n \"DESCRIPTION\",\n \"app.R\"\n ),\n appId = rsconnect::deployments(\".\")$appId,\n lint = FALSE,\n forceUpdate = TRUE\n)\n```\n:::\n\n:::\n:::\n\n## Balancing Act {.centerheading}\n\n:::: {.columns}\n\n::: {.column width=\"33%\"}\n\n🔼 Max Processes\n\n* More resources dedicated to particular application\n* Potential for server overload\n\n:::\n\n::: {.column width=\"33%\"}\n\n![](assets/img/teeter_totter.png)\n\n:::\n\n::: {.column width=\"33%\"}\n\n🔼 Min Processes\n\n* Resources dedicated even when app is idle\n* Potentially wasteful if app is not used concurrently often\n\n:::\n\n::::\n\n## Business Talk {background-image=\"assets/img/lego_boss_office.jpg\" background-size=\"cover\" background-color=\"black\"}\n\n## You Might be Asked ...\n\n::: {.fragment}\n\n\"How many people used your app last year?\"\n\n:::\n\n::: {.fragment}\n\n\"How long are users on the app at a given time?\"\n\n:::\n\n::: {.fragment}\n::: {.fragment .strike}\n\"What is the return on investment (ROI) this app is bringing the company?\"\n:::\n:::\n\n::: {.fragment}\n\n:::: {.columns}\n\n::: {.column width=\"55%\"}\n![](assets/img/daniel-bryan-no.gif){width=90%}\n:::\n\n::: {.column width=\"30%\"}\n### NOT TODAY!\n:::\n\n::::\n\n:::\n\n## 📦 Introducing [`{connectapi}`](https://pkgs.rstudio.com/connectapi/)\n\n> R client for the [Posit Connect Server API](https://docs.posit.co/connect/api/) as well as helpful functions that utilize the client\n\nChecklist:\n\n✅ Create API key for your Posit Connect account\n\n✅ Create `.Renviron` with following variables: `CONNECT_SERVER`, `CONNECT_API_KEY`\n\n## Introduce Yourself\n\n\n::: {.cell}\n\n```{.r .cell-code}\nlibrary(connectapi)\n\nclient <- connect(\n server = Sys.getenv(\"CONNECT_SERVER\"),\n api_key = Sys.getenv(\"CONNECT_API_KEY\")\n)\n\nclient\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\nPosit Connect API Client: \n Posit Connect Server: https://rsc.training.rstudio.com\n Posit Connect API Key: ***********FWxW\n```\n\n\n:::\n:::\n\n\n* `client` is an R6 object representing the API client\n* Required parameter for many convenience functions\n\n## Know Yourself\n\n\n::: {.cell}\n\n```{.r .cell-code}\nmy_guid <- user_guid_from_username(client, \"rpodcast\")\n\nmy_guid\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n[1] \"47e1338f-54a8-4b0e-9b3c-e4380611b30f\"\n```\n\n\n:::\n:::\n\n\n* All content/users have a **unique indentifier (guid)**\n\n## Obtain (Your) Shiny Apps\n\n\n::: {.cell}\n\n```{.r .cell-code}\nlibrary(dplyr)\n\nmeta_df <- get_content(client) |>\n filter(owner_guid == my_guid) |>\n select(guid, name, title)\n\nmeta_df\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n# A tibble: 27 × 3\n guid name title \n <chr> <chr> <chr> \n 1 40dd2969-5d67-4940-89c4-08fe31fd1c74 brickapp brickapp \n 2 fba6896e-49e4-45c6-856f-03a3bc4f498f legobricksapp legobricks.app \n 3 626d6bb1-f36d-4c9b-bc9e-5cb44f46e698 hellogolem22 hellogolem2 \n 4 6f8116dc-eabf-4a64-aacb-4aeffbb4eaa4 golembrochure2 golembrochure \n 5 a4e6809f-4858-4888-a848-94925d3df4a0 golembrochure golembrochure \n 6 f268d424-a286-41b9-91f1-ae458797e64a golemhellolocal golemhellolocal\n 7 4d9124a0-158b-4bd9-9b65-07b877db0906 test test \n 8 c60de16e-0b29-4267-a2c5-0eb6815d7999 hellogolem3 hellogolem \n 9 2aec4c5b-144f-4a6c-8abf-7d07a17d78b2 hellogolem hellogolem \n10 344b9f1b-ddbe-4145-a1f0-eb6608662f08 hellogolem2 hellogolem \n# ℹ 17 more rows\n```\n\n\n:::\n:::\n\n\n## Usage Metrics\n\nSession durations for the `legobricksapp`:\n\n\n::: {.cell}\n\n```{.r .cell-code}\napp_guid <- \"fba6896e-49e4-45c6-856f-03a3bc4f498f\"\n\nget_usage_shiny(\n client,\n content_guid = app_guid,\n limit = 5\n ) |>\n filter(!is.na(ended)) |>\n mutate(session_duration = ended - started) |>\n select(user_guid, session_duration)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n# A tibble: 5 × 2\n user_guid session_duration\n <chr> <drtn> \n1 47e1338f-54a8-4b0e-9b3c-e4380611b30f 10.933333 mins \n2 47e1338f-54a8-4b0e-9b3c-e4380611b30f 1.216667 mins \n3 47e1338f-54a8-4b0e-9b3c-e4380611b30f 4.733333 mins \n4 47e1338f-54a8-4b0e-9b3c-e4380611b30f 6.766667 mins \n5 47e1338f-54a8-4b0e-9b3c-e4380611b30f 1.183333 mins \n```\n\n\n:::\n:::\n\n\n## Usage Metrics\n\nHow many users visited `legobricksapp` in last 28 days?\n\n\n::: {.cell}\n\n```{.r .cell-code}\napp_guid <- \"fba6896e-49e4-45c6-856f-03a3bc4f498f\"\ntime_start <- Sys.Date() - lubridate::days(28)\nget_usage_shiny(\n client,\n content_guid = app_guid,\n limit = 100,\n from = time_start\n ) |>\n pull(user_guid) |>\n unique() |>\n length()\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n[1] 4\n```\n\n\n:::\n:::", | ||
"supporting": [ | ||
"index_files" | ||
], | ||
"filters": [ | ||
"rmarkdown/pagebreak.lua" | ||
], | ||
"includes": { | ||
"include-after-body": [ | ||
"\n<script>\n // htmlwidgets need to know to resize themselves when slides are shown/hidden.\n // Fire the \"slideenter\" event (handled by htmlwidgets.js) when the current\n // slide changes (different for each slide format).\n (function () {\n // dispatch for htmlwidgets\n function fireSlideEnter() {\n const event = window.document.createEvent(\"Event\");\n event.initEvent(\"slideenter\", true, true);\n window.document.dispatchEvent(event);\n }\n\n function fireSlideChanged(previousSlide, currentSlide) {\n fireSlideEnter();\n\n // dispatch for shiny\n if (window.jQuery) {\n if (previousSlide) {\n window.jQuery(previousSlide).trigger(\"hidden\");\n }\n if (currentSlide) {\n window.jQuery(currentSlide).trigger(\"shown\");\n }\n }\n }\n\n // hookup for slidy\n if (window.w3c_slidy) {\n window.w3c_slidy.add_observer(function (slide_num) {\n // slide_num starts at position 1\n fireSlideChanged(null, w3c_slidy.slides[slide_num - 1]);\n });\n }\n\n })();\n</script>\n\n" | ||
] | ||
}, | ||
"engineDependencies": {}, | ||
"preserve": {}, | ||
"postProcess": true | ||
} | ||
} |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,17 +1,269 @@ | ||
--- | ||
title: "Deployment & Administration" | ||
subtitle: "posit::conf(2023) <br> Shiny in Production: Tools & Techniques" | ||
author: "TBD" | ||
footer: "[{{< var workshop_short_url >}}]({{< var workshop_full_url >}})" | ||
format: | ||
revealjs: | ||
theme: [default, ../slides.scss] # moon= teal bg | dark | ||
scrollable: true | ||
incremental: true | ||
incremental: false | ||
slide-number: c/t # c/t | c | h/v | h.v | ||
slide-tone: false #true | ||
code-line-numbers: false | ||
code-line-numbers: true | ||
history: false | ||
revealjs-plugins: | ||
- codewindow | ||
--- | ||
|
||
## More to come | ||
```{css} | ||
.centerheading h2 { | ||
text-align: center; | ||
} | ||
.mono { | ||
font-family: monospace; | ||
font-size: 1.4em; | ||
} | ||
.center-xy { | ||
margin: 0; | ||
position: absolute; | ||
top: 25%; | ||
left: 0%; | ||
-ms-transform: translateY(-50%), translateX(-50%); | ||
transform: translateY(-50%), translateX(-50%); | ||
} | ||
``` | ||
|
||
## Choose your Adventure | ||
|
||
::: columns | ||
::: {.column width="50%"} | ||
### Posit Connect | ||
::: | ||
|
||
::: {.column width="50%"} | ||
### Containers | ||
::: | ||
::: | ||
|
||
::: notes | ||
TODO: Find pictures demonstrating the "App & Packages Only" (Posit Connect, Shinyapps.io) versus "The whole environment" (Containers) | ||
::: | ||
|
||
## Deployment Checklist | ||
|
||
✅ Create `app.R` in directory | ||
|
||
✅ Take note of any custom environment variables | ||
|
||
✅ Ensure `DESCRIPTION` is up-to-date with required packages | ||
|
||
✅ Remove outdated functions / scripts | ||
|
||
|
||
|
||
## {.center-xy} | ||
|
||
::: mono | ||
::: {.codewindow .r} | ||
03_deploy.R | ||
|
||
```{r} | ||
#| eval: false | ||
#| echo: true | ||
## RStudio ---- | ||
## If you want to deploy on RStudio related platforms | ||
golem::add_rstudioconnect_file() | ||
golem::add_shinyappsio_file() | ||
golem::add_shinyserver_file() | ||
``` | ||
::: | ||
::: | ||
|
||
# `r fontawesome::fa("people-carry", "white")` Code-Along {background-color="#17395c"} | ||
|
||
Push-Button Deployment to Posit Connect | ||
|
||
--- | ||
|
||
::: mono | ||
::: {.codewindow .r width="1000px"} | ||
03_deploy.R | ||
```{r} | ||
#| eval: false | ||
#| echo: true | ||
rsconnect::deployApp( | ||
appName = "legobricksapp", | ||
appTitle = desc::desc_get_field("Package"), | ||
appFiles = c( | ||
"R/", | ||
"inst/", | ||
"data/", | ||
"NAMESPACE", | ||
"DESCRIPTION", | ||
"app.R" | ||
), | ||
appId = rsconnect::deployments(".")$appId, | ||
lint = FALSE, | ||
forceUpdate = TRUE | ||
) | ||
``` | ||
::: | ||
::: | ||
|
||
## Balancing Act {.centerheading} | ||
|
||
:::: {.columns} | ||
|
||
::: {.column width="33%"} | ||
|
||
🔼 Max Processes | ||
|
||
* More resources dedicated to particular application | ||
* Potential for server overload | ||
|
||
::: | ||
|
||
::: {.column width="33%"} | ||
|
||
![](assets/img/teeter_totter.png) | ||
|
||
::: | ||
|
||
::: {.column width="33%"} | ||
|
||
🔼 Min Processes | ||
|
||
* Resources dedicated even when app is idle | ||
* Potentially wasteful if app is not used concurrently often | ||
|
||
::: | ||
|
||
:::: | ||
|
||
## Business Talk {background-image="assets/img/lego_boss_office.jpg" background-size="cover" background-color="black"} | ||
|
||
## You Might be Asked ... | ||
|
||
::: {.fragment} | ||
|
||
"How many people used your app last year?" | ||
|
||
::: | ||
|
||
::: {.fragment} | ||
|
||
"How long are users on the app at a given time?" | ||
|
||
::: | ||
|
||
::: {.fragment} | ||
::: {.fragment .strike} | ||
"What is the return on investment (ROI) this app is bringing the company?" | ||
::: | ||
::: | ||
|
||
::: {.fragment} | ||
|
||
:::: {.columns} | ||
|
||
::: {.column width="55%"} | ||
![](assets/img/daniel-bryan-no.gif){width=90%} | ||
::: | ||
|
||
::: {.column width="30%"} | ||
### NOT TODAY! | ||
::: | ||
|
||
:::: | ||
|
||
::: | ||
|
||
## 📦 Introducing [`{connectapi}`](https://pkgs.rstudio.com/connectapi/) | ||
|
||
> R client for the [Posit Connect Server API](https://docs.posit.co/connect/api/) as well as helpful functions that utilize the client | ||
Checklist: | ||
|
||
✅ Create API key for your Posit Connect account | ||
|
||
✅ Create `.Renviron` with following variables: `CONNECT_SERVER`, `CONNECT_API_KEY` | ||
|
||
## Introduce Yourself | ||
|
||
```{r} | ||
#| echo: true | ||
library(connectapi) | ||
client <- connect( | ||
server = Sys.getenv("CONNECT_SERVER"), | ||
api_key = Sys.getenv("CONNECT_API_KEY") | ||
) | ||
client | ||
``` | ||
|
||
* `client` is an R6 object representing the API client | ||
* Required parameter for many convenience functions | ||
|
||
## Know Yourself | ||
|
||
```{r} | ||
#| echo: true | ||
my_guid <- user_guid_from_username(client, "rpodcast") | ||
my_guid | ||
``` | ||
|
||
* All content/users have a **unique indentifier (guid)** | ||
|
||
## Obtain (Your) Shiny Apps | ||
|
||
```{r} | ||
#| echo: true | ||
library(dplyr) | ||
meta_df <- get_content(client) |> | ||
filter(owner_guid == my_guid) |> | ||
select(guid, name, title) | ||
meta_df | ||
``` | ||
|
||
## Usage Metrics | ||
|
||
Session durations for the `legobricksapp`: | ||
|
||
```{r} | ||
#| echo: true | ||
app_guid <- "fba6896e-49e4-45c6-856f-03a3bc4f498f" | ||
get_usage_shiny( | ||
client, | ||
content_guid = app_guid, | ||
limit = 5 | ||
) |> | ||
filter(!is.na(ended)) |> | ||
mutate(session_duration = ended - started) |> | ||
select(user_guid, session_duration) | ||
``` | ||
|
||
## Usage Metrics | ||
|
||
How many users visited `legobricksapp` in last 28 days? | ||
|
||
```{r} | ||
#| echo: true | ||
app_guid <- "fba6896e-49e4-45c6-856f-03a3bc4f498f" | ||
time_start <- Sys.Date() - lubridate::days(28) | ||
get_usage_shiny( | ||
client, | ||
content_guid = app_guid, | ||
limit = 100, | ||
from = time_start | ||
) |> | ||
pull(user_guid) |> | ||
unique() |> | ||
length() | ||
``` |