diff --git a/_freeze/materials/d1-04-deploy-admin/index/execute-results/html.json b/_freeze/materials/d1-04-deploy-admin/index/execute-results/html.json
new file mode 100644
index 0000000..50d6b5a
--- /dev/null
+++ b/_freeze/materials/d1-04-deploy-admin/index/execute-results/html.json
@@ -0,0 +1,20 @@
+{
+ "hash": "3e56d1f02a91584fae41636bfdd25373",
+ "result": {
+ "markdown": "---\ntitle: \"Deployment & Administration\"\nsubtitle: \"posit::conf(2023)
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\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# ``{=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 \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 \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\n\n"
+ ]
+ },
+ "engineDependencies": {},
+ "preserve": {},
+ "postProcess": true
+ }
+}
\ No newline at end of file
diff --git a/materials/d1-04-deploy-admin/assets/img/daniel-bryan-no.gif b/materials/d1-04-deploy-admin/assets/img/daniel-bryan-no.gif
new file mode 100644
index 0000000..c803688
Binary files /dev/null and b/materials/d1-04-deploy-admin/assets/img/daniel-bryan-no.gif differ
diff --git a/materials/d1-04-deploy-admin/assets/img/lego_boss_office.jpg b/materials/d1-04-deploy-admin/assets/img/lego_boss_office.jpg
new file mode 100644
index 0000000..9ac60e1
Binary files /dev/null and b/materials/d1-04-deploy-admin/assets/img/lego_boss_office.jpg differ
diff --git a/materials/d1-04-deploy-admin/assets/img/optimized.gif b/materials/d1-04-deploy-admin/assets/img/optimized.gif
new file mode 100644
index 0000000..5da89e9
Binary files /dev/null and b/materials/d1-04-deploy-admin/assets/img/optimized.gif differ
diff --git a/materials/d1-04-deploy-admin/assets/img/teeter_totter.png b/materials/d1-04-deploy-admin/assets/img/teeter_totter.png
new file mode 100644
index 0000000..0c2e1e0
Binary files /dev/null and b/materials/d1-04-deploy-admin/assets/img/teeter_totter.png differ
diff --git a/materials/d1-04-deploy-admin/index.qmd b/materials/d1-04-deploy-admin/index.qmd
index 2f4c1aa..f9c20d9 100644
--- a/materials/d1-04-deploy-admin/index.qmd
+++ b/materials/d1-04-deploy-admin/index.qmd
@@ -1,17 +1,269 @@
---
title: "Deployment & Administration"
subtitle: "posit::conf(2023)
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()
+```
\ No newline at end of file