<- function(N, github_id){
mp_submission_create library(rvest); library(glue); library(tidyverse); library(httr2); library(gh)
if(missing(N)){
<- menu(title="Which Mini-Project would you like to submit on GitHub?",
N choices=c(0, 1, 2, 3, 4))
}
<- glue("https://michael-weylandt.com/STA9750/miniprojects/mini0{N}.html")
mp_url
<- read_html(mp_url) |> html_element("#submission-text") |> html_text()
mp_text
if(missing(github_id)){
<- readline("What is your GitHub ID? ")
github_id
}
<- glue("{course_short} {github_id} MiniProject #0{N}")
title
<- mp_text |> str_replace("<GITHUB_ID>", github_id)
body
<- request("https://github.com/") |>
r req_url_path_append(github_id) |>
req_url_path_append(course_repo) |>
req_url_path_append("issues/new") |>
req_url_query(title=title,
body=body)
browseURL(r$url)
}
STA 9750: Tips and Tricks
Miscellaneous RStudio
and Git
Advice
When you are writing
quarto
documents inRStudio
, do not use the “Visual” editor mode. It will seem tempting at first, but it will lead to difficult and hard to debug errors down the road. You should disable it in yourRStudio
settings (if possible) and includeeditor: source
in the header ofquarto
documents to keep it from opening automatically.git
supports a huge variety of programming workflow and was originally designed to allow large teams of programmers to work together. This flexibility leads to a few “sharp edges”, however.You will have the best experience with
git
in this course if:You
commit
early and often.git
commits are essentially free (40 bytes - less than a text!) and give you the ability to roll back changes if you get into a bad situation.You do all your work on a single computer. Keeping files and
git
synchronized across multiple machines is possible, but trickier. If, for whatever reason, you need to use multiple computers, reach out to course staff in office hours for advice.You
git push
at the end of every work session. This is the stage when things are most likely to go wrong, so you want to bepush
ing early and often, giving you the best chance to spot and fix an issues early.You work in a “regular” file system. Synchronization and back up tools like DropBox or OneDrive perform magic behind the scenes that doesn’t always play well with
git
. You don’t need to use these tools withgit
sincegit
(used properly and in conjunction withGitHub
) will give you automatic history and backups.You avoid anti-virus issues. If you are on a Windows machine, tell your anti-virus software to ignore the directory containing your work for this course. Both
git
andquarto
create and delete lots of little files as they execute; certain anti-virus tools interpret this as evidence of a cyber attack and attempt to lock the file system, leading to a variety of subtle and hard to explain errors.You are careful about what files you
add
ingit
. You should generally only include yourqmd
files and the rendered outputs in thedocs
folder. Thebuild_site
script included in Mini-Project #00 is helpful, but not bullet-proof, here.
Use the file names and directory structures specified in the course instructions. I use a variety of automated processing tools to coordinate this course and if you do not follow instructions closely, your assignments will not be processed. I will typically manually handle your submissions, but with a penalty applied to your grade. It is better for all parties if you avoid this.
The helper scripts below can be used to confirm whether your submissions are properly formatted.
Helper Scripts
The following helper scripts can be used to help automate several students tasks in this course. To use any of them, run
source("https://michael-weylandt.com/STA9750/load_helpers.R")
and then run the name of the relevant script.
Open Mini-Project GitHub Issue
The following functon will create the GitHub issue necessary to submit a mini-project:
Verify Mini-Project Submission
The following function will confirm that the submission issue is properly formatted.
<- function(N, github_id){
mp_submission_verify library(rvest)
library(glue)
library(tidyverse)
library(httr2)
library(gh)
if(missing(N)){
<- menu(title="Which Mini-Project would you like to check was properly submitted on GitHub?",
N choices=c(0, 1, 2, 3, 4))
}
<- glue("https://michael-weylandt.com/STA9750/miniprojects/mini0{N}.html")
mp_url
<- read_html(mp_url) |> html_element("#submission-text") |> html_text()
mp_text
if(missing(github_id)){
<- readline("What is your GitHub ID? ")
github_id
}
<- glue("{course_short} {github_id} MiniProject #0{N}")
title
<- mp_text |> str_replace("<GITHUB_ID>", github_id) |> str_squish()
body
<- gh("/repos/{owner}/{repo}/issues?state=all",
issues owner=github_id,
repo=course_repo)
<- vapply(issues, function(x) str_squish(x$title), "")
issue_names
<- which(issue_names == title)
name_match
if(length(name_match) == 0){
cat("I could not find an issue with the title:\n",
" ", sQuote(title),"\n",
"The issues I found had the following titles:\n",
paste(c("", issue_names), collapse="\n - "), "\n",
"If something on that list looks correct, please check",
"capitalization and punctuation.\n")
stop("MINIPROJECT NOT SUBMITTED CORRECTLY.")
else if(length(name_match) > 1){
} cat("I found multiple issues with the title:\n",
" ", sQuote(title),"\n",
"Please change the names of issues so that there is only",
"issue with the desired name. (Note that closing an issue is",
"not sufficient.)\n")
stop("MINIPROJECT NOT SUBMITTED CORRECTLY.")
}
<- issues[[name_match]]
issue
<- issue$number
issue_num <- issue$html_url
issue_url <- issue$body |> str_squish()
issue_body
cat("Identified Issue #", issue_num, "at", issue_url, "as possible candidate.")
if(issue$state != "open"){
cat("Issue does not appear to be in 'open' status. Please",
"confirm the issue is open and try again.\n")
stop("MINIPROJECT NOT SUBMITTED CORRECTLY.")
}
if(issue_body != body){
cat("Issue does not appear to have the correct body text. This is not",
"necessarily an issue, but you may want to confirm all relevant",
"information is included.\n")
}
<- str_match_all(paste(issue_body, "\n Thanks!\n", sep=""), "http.*")[[1]]
urls <- str_extract(body, "http.*")
expected_url
if(length(urls) > 1){
cat("The following URLs were found in the issue text:",
paste(c("", urls), collapse="\n - "),
"Only one URL was expected; please remove any extras.\n")
stop("MINIPROJECT NOT SUBMITTED CORRECTLY.")
}
<- urls[1]
submitted_url
if(submitted_url != expected_url){
cat("The submitted URL does not match the Mini-Project instructions.\n",
"Expected:\n - ", expected_url, "\n",
"Submitted:\n - ", submitted_url, "\n",
"Please correct any differences and try again.\n")
stop("MINIPROJECT NOT SUBMITTED CORRECTLY.")
}
<- request(submitted_url) |> req_perform()
resp
if(resp_is_error(resp)){
cat("Something appears to be incorrect at the URL: ",
submitted_url, "\n Please confirm that it is working as expected and try again.")
browseURL(submitted_url)
stop("MINIPROJECT NOT SUBMITTED SUCCESSFULLY.")
}
<- glue("https://raw.githubusercontent.com/{github_id}/{course_repo}/refs/heads/main/mp0{N}.qmd")
raw_url
<- request(raw_url) |> req_perform()
resp_raw
if(resp_is_error(resp_raw)){
cat("I cannot find the source qmd document at", raw_url, ".\n",
"Please confirm it was correctly submitted and try again.\n",
"This document is needed for automated code quality checks.")
stop("MINIPROJECT NOT SUBMITTED SUCCESSFULLY.")
}
cat("Congratulations! Your mini-project appears to have been submitted correctly!\n")
invisible(TRUE)
}
Create Peer Feedback Comment
As of January 2025, GitHub does not support pre-filling issue comments via URL so I am currently unable to pre-load the peer feedback template for you. For now, you should be able to copy the template on the mini-project overview page. (Note the “copy” button in the top right corner of the code-formatted block.)
Verify Peer Feedback Properly Formatted:
<- function(N, github_id, peer_id){
mp_feedback_verify library(rvest)
library(glue)
library(tidyverse)
library(httr2)
library(gh)
if(missing(N)){
<- menu(title="Which Mini-Project's Peer Feedback would you like to check was properly submitted on GitHub?",
N choices=c(0, 1, 2, 3, 4))
}
if(missing(github_id)){
<- readline("What is your GitHub ID? ")
github_id
}
if(missing(peer_id)){
<- readline("What is your Peer's GitHub ID? ")
peer_id
}
<- "https://michael-weylandt.com/STA9750/miniprojects.html"
template_url
<- read_html(template_url) |>
template_text html_element("#peer-feedback-template") |>
html_text() |>
str_trim() |>
str_replace_all("NN", "(.*)") |>
str_replace_all("TEXT TEXT TEXT", "(.*)")
<- glue("{course_short} {github_id} MiniProject #0{N}")
title
<- gh("/repos/{owner}/{repo}/issues?state=all",
issues owner=peer_id,
repo=course_repo)
<- vapply(issues, function(x) str_squish(x$title), "")
issue_names
<- which(issue_names == title)
name_match
if(length(name_match) != 0){
cat("I could not find an issue with the title:\n",
" ", sQuote(title),"\n",
"I'm afraid I can't verify whether the peer feedback was submitted properly.\n")
stop("PEER FEEDBACK NOT VERIFIED.")
}
<- issues[[name_match]]
issue
<- issue$html_url
issue_url <- issue$comment_url
issue_comment_url
<- gh(issue_comment_url)
comments
<- vapply(comments, function(x) x$user$login, "")
commenters
<- which(commenters == github_id)
comment_num
if(length(comment_num) != 1){
cat("I cannot identify your comment on the issue at",
sQuote(issue_url), ". Please verify that this is the correct link.\n")
if(length(comment_num) == 0){
cat("You do not appear to have commented on this issue.")
else {
} cat("You have left multiple comments on this issue.",
"Please consolidate your feedback")
}stop("PEER FEEDBACK NOT VERIFIED.")
}
<- comment$body
comment_body
cat("I have found your comment, with the following text:")
cat(comment_body)
<- comment_body |> str_squish()
comment_body <- template_text |> str_squish()
template_text
<- str_match_all(comment_body, template_text)[[1]][-1]
matches
if(anyNA(matches)){
cat("I couldn't match the template to your comment. Please modify and try again.")
stop("PEER FEEDBACK NOT VERIFIED.")
}
cat(glue("Congratulations! Your peer feedback on {peer_id}'s MP #0{N} appears properly formatted. Thank you for your contributions to {course_short}"))
invisible(TRUE)
}
Count Words
The following script can be used to count words on a Quarto-generated web page.
<- function(url){
count_words library(rvest); library(stringi)
# Note that this includes code inside an inline block, but omits
# full-sized code blocks
read_html(url) |>
html_elements("main p") |>
html_text() |>
stri_count_words() |>
sum()
}
Lint Submission
The following will automatically run the lintr
package on a submitted qmd
document.
<- function(N, peer_id){
lint_submission library(rvest); library(glue); library(tidyverse); library(httr2); library(lintr)
if(missing(N)){
<- menu(title="Which Mini-Project submission would you like to lint?",
N choices=c(0, 1, 2, 3, 4))
}
if(missing(peer_id)){
<- readline("What is your Peer's GitHub ID? ")
peer_id
}
<- glue("https://raw.githubusercontent.com/{peer_id}/{course_repo}/refs/heads/main/mp0{N}.qmd")
raw_url
cat("Attempting to access qmd source at", raw_url, ".\n")
<- request(raw_url) |> req_perform()
resp
if(resp_is_error(resp)){
cat("I could not access the raw qmd document. Attempting to open in browser...\n")
browseURL(resp)
return(FALSE)
else {
} cat("I was able to access the raw qmd document. Beginning to lint.\n")
<- tempfile(pattern=glue("mp0{N}_{peer_id}_lint_document.qmd"))
tf
writeLines(resp_body_string(resp), tf)
::lint(tf)
lintr
} }