Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
85 changes: 73 additions & 12 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ Deployment is automatic via GitHub Actions on push to `main`: renders with Quart

## Architecture

- **`_quarto.yml`**: Site-level config (theme, navbar, footer, extensions). `freeze: true` means computed R outputs are cached in `_freeze/` and not re-executed unless the source changes.
- **`_quarto.yml`**: Site-level config (theme, navbar, footer, extensions). `execute: freeze: true` is set globally in `_quarto.yml`. During a full project render (`quarto render`), code is never re-executed — stored outputs in `_freeze/` are reused. Single-document renders always re-execute regardless of freeze.
- **`blog/_metadata.yml`**: Defaults for all blog posts (freeze, echo/message/warning suppression, giscus comments, title-block-banner).
- **`_freeze/`**: Cached execution results (JSON + figures). Committed to the repo so CI doesn't need R packages installed.
- **`_site/`**: Build output, committed to repo.
- **`_site/`**: Build output. Gitignored — not committed.
- **`_extensions/`**: Quarto extensions (fontawesome, iconify, social-embeds).
- **`custom_styles.scss` / `custom_styles_dark.scss`**: Theme overrides for light/dark modes on top of the cosmo Bootstrap theme.
- **`data/`**: Shared datasets (geojson, csv, zip) referenced across blog posts.
Expand All @@ -51,7 +51,72 @@ link-external-icon: false
---
```

Posts use R code chunks with knitr. Common libraries: `tidyverse`, `sf`, `gt`, `ggplot2`, `cancensus`, `mountainmathHelpers`. Data paths typically reference `../../../data` relative to the post via `here()`.
Posts use R code chunks with knitr. Common libraries: `tidyverse`, `sf`, `gt`, `ggplot2`, `cancensus`, `mountainmathHelpers`, `jdawangHelpers`. Data paths typically reference `../../../data` relative to the post via `here()`.

## Creating a new post

1. Create the directory `blog/YYYY/post-slug/` and `index.qmd` with the frontmatter pattern above.
2. From within the post directory, initialize renv. Use `restart = FALSE` to avoid renv attempting to restart the R session (which exits a subprocess):
```bash
cd blog/YYYY/post-slug
Rscript -e "renv::init(restart = FALSE)"
```
3. Install required packages:
```bash
Rscript -e "renv::install(c('tidyverse', 'sf', ...))"
Rscript -e "renv::install_github('jdawang/jdawangHelpers')"
```
4. Write the post, then render from within the post directory:
```bash
cd blog/YYYY/post-slug
quarto render index.qmd
```
5. Snapshot the environment:
```bash
Rscript -e "renv::snapshot()"
```
6. Commit these files:
- `index.qmd`
- `renv.lock`
- `.Rprofile`
- `.gitignore`
- `renv/activate.R`, `renv/settings.json`, `renv/.gitignore`
- `_freeze/blog/YYYY/post-slug/` (generated by render)
- Any static assets (e.g. `.png` files checked in manually)

For a complete example of post structure, see `blog/2026/zbr-two-year-review/`.

**Inline R expressions and cache**: When `execute: cache: true` is active, chunks containing `library()` calls can be cached, which prevents packages from loading and breaks inline R expressions (`` `r expr` ``). Any post using inline R must set `execute: cache: false` in its frontmatter. Note: `freeze` (Quarto-level, caches entire document outputs in `_freeze/`, only during project renders) and `cache` (knitr-level, caches individual chunk outputs in `*_cache/`, during all renders) are distinct mechanisms.

## jdawangHelpers package

`jdawangHelpers` is a companion R package providing shared utilities for blog posts. Source is at GitHub: `jdawang/jdawangHelpers`. It covers:

- **Edmonton building permit processing** — `clean_edmonton_bp_columns()`, `filter_edmonton_residential()`, `add_edmonton_project_type()`, `add_edmonton_suite_info()`, `add_edmonton_neighbourhood_type()`
- **Transit distance calculations** — `add_transit_distance()`, `add_ecdf_by_distance()`
- **ggplot2 themes** — `theme_jd()` (dark base theme, magma palette), `theme_map()`, `theme_map_dark()`. `theme_jd()` bakes in the viridis magma palette as the default discrete fill/colour scale — to use it, map a variable to `fill` or `colour` in `aes()`. Do not use `scale_fill_manual` with hardcoded colours unless intentionally overriding the palette.
- **Map layers** — `layers_map_base()` (requires `mountainmathHelpers`), `layers_transit_ecdf()`
- **GT table helpers**

Typical pipeline in posts:

```r
bp |>
clean_edmonton_bp_columns(crs = 4326) |>
filter_edmonton_residential() |>
add_edmonton_project_type() |>
add_edmonton_suite_info() |>
add_edmonton_neighbourhood_type() |>
add_transit_distance(transit_stops)
```

**Always use jdawangHelpers functions** for building permit processing, transit calculations, themes, and map layers. Do not copy code patterns from posts that predate jdawangHelpers — those implement manually what the package now handles. When in doubt, check the package source before writing new data processing code. Use `blog/2026/mli-select-edmonton/` as the canonical reference for jdawangHelpers usage. Use `blog/2026/zbr-two-year-review/` as the reference for writing style and tone.

Install from GitHub before using in a post:

```r
renv::install_github("jdawang/jdawangHelpers")
```

In the first R chunk, along with imports, always load the following options like the following. These set important API keys and cache paths for the cancensus and jdawangHelpers packages:

Expand Down Expand Up @@ -114,22 +179,18 @@ cd blog/2026/nodes-corridors
Rscript -e "renv::restore()"
```

When creating a new post, always:

- Initialize renv in its directory using `renv::init()`.
- Install required packages before trying to execute code.
See "Creating a new post" above for the full workflow including renv setup, rendering, and what to commit.

If you come across an error like "[package name or function name] not found", that is most likely due to not installing the package. Install it and try again.

Before committing changes to a blog post, always run `renv::snapshot()` from within its directory to ensure the environment is saved.

## Freeze behaviour

Because `freeze: true` is set globally, rendered outputs are stored in `_freeze/` and reused. To force re-execution of a post's R code, change into the post's directory and rerender it with the `--no-freeze` option.
With `execute: freeze: true` set globally, full project renders reuse outputs from `_freeze/` without re-executing code. Single-document renders always re-execute. To force re-execution during a project render, delete the post's freeze directory first:

```bash
cd blog/2026/nodes-corridors/
quarto render blog/2026/nodes-corridors/index.qmd --no-freeze
rm -rf _freeze/blog/2026/nodes-corridors
cd blog/2026/nodes-corridors
quarto render index.qmd
```

The `blog/2022/election-maps-2022/index.qmd` is explicitly excluded from rendering in `_quarto.yml`.
8 changes: 5 additions & 3 deletions Containerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
FROM ghcr.io/rocker-org/geospatial:4.5.2
FROM ghcr.io/rocker-org/geospatial:4.5.3

RUN sudo curl -sLO https://cdn.posit.co/positron/releases/deb/x86_64/Positron-2026.03.0-212-x64.deb \
RUN sudo apt-get update \
&& sudo apt install -y curl \
&& sudo curl -sLO https://cdn.posit.co/positron/releases/deb/x86_64/Positron-2026.03.0-212-x64.deb \
&& sudo curl -L https://rig.r-pkg.org/deb/rig.gpg -o /etc/apt/trusted.gpg.d/rig.gpg \
&& sudo sh -c 'echo "deb http://rig.r-pkg.org/deb rig main" > /etc/apt/sources.list.d/rig.list' \
&& sudo apt-get update \
Expand All @@ -14,4 +16,4 @@ RUN sudo curl -sLO https://cdn.posit.co/positron/releases/deb/x86_64/Positron-2
rustc \
&& sudo curl -sS https://starship.rs/install.sh | sh -s -- -y \
&& chsh -s $(which zsh) \
&& rig add 4.5.2
&& rig add 4.5.3

Large diffs are not rendered by default.

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.
1 change: 1 addition & 0 deletions blog/2026/mli-select-edmonton/.Rprofile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
source("renv/activate.R")
Loading
Loading