Skip to content

Commit

Permalink
Merge pull request #6 from posit-conf-2023/deployments
Browse files Browse the repository at this point in the history
Deployments
  • Loading branch information
rpodcast committed Sep 15, 2023
2 parents 645dc11 + 24c110a commit 932c680
Show file tree
Hide file tree
Showing 7 changed files with 277 additions and 5 deletions.
2 changes: 1 addition & 1 deletion R/test_dependencies.R
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ shinyloadtest_report(df, "R/report1.html")
library(connectapi)

client <- connect(
server = paste0("https://", Sys.getenv("CONNECT_SERVER")),
server = Sys.getenv("CONNECT_SERVER"),
api_key = Sys.getenv("CONNECT_API_KEY")
)

Expand Down
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.
260 changes: 256 additions & 4 deletions materials/d1-04-deploy-admin/index.qmd
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()
```

0 comments on commit 932c680

Please sign in to comment.