## Historical code to build data files
#Repository Development
#
# https://github.com/jennybc/happy-git-with-r/blob/master/81_github-api-tokens.Rmd
#cat("GITHUB_PAT=JUNKFOREBRIGHTSPACE\n", file = file.path(normalizePath("~/"), ".Renviron"), append = TRUE)
#### New Shiny App for GITHUB and CANVAS ####
# API to CANVAS COURSE must be entered by teacher. Canvas just checks that student is in course.
# GH API connection to current course group.
# 1. Create repo from template repo
# 2. Add repo to semester group
# 4. Add user as pull and push
# 5. Add issues for task tracking
# 6. Report two invite links.
pacman::p_load_current_gh("VerbalExpressions/RVerbalExpressions")
pacman::p_load(tidyverse, readxl, gh, googledrive, googlesheets4, stringr, magrittr, glue, purrr, fs)
semester_id <- "wi20"
safe_gh <- safely(gh) # in purr package it makes it so code does not error out
## definitely ad hoc, interactive code!
## still ad hoc, interactive code with hathaway's edits!
## verifies github usernames
## create / identifies 2016 student team
## check that students are on it or add them
## gets student repos
## makes the ones that don't exist
## add student as collab to their repo
## give 2016 student team read access
## unwatch them (for me)
# Major points:
#
# * [Create an Organization](https://help.github.com/articles/creating-a-new-organization-account/) for the course.
# - Immediately request an [Education discount](https://education.github.com) for the Organization, so that you get unlimited private repos.
# * Have your students register for free, personal [GitHub accounts](https://github.com).
# - Encourage them to request an [Education discount](https://education.github.com) on their own behalf (aka "student developer pack"). But rest assured, nothing you need for your course machinery will depend on this.
# * Get the GitHub usernames from your students -- we use a [Shiny](http://deanattali.com/blog/shiny-persistent-data-storage/) [app](http://deanattali.com/2015/06/14/mimicking-google-form-shiny/)! -- plus some shred of information that allows you link them back to your official course list.
# * Create a students [Team](https://help.github.com/enterprise/2.7/admin/guides/user-management/organizations-and-teams/) and a TA Team. I make such teams for each run of the course, e.g. `2016_students` and `2016_ta`.
# * Invite students to join your course organization and the students team. Ditto for TAs and the TA team.
# * Create a canonical name for each student, based on the official course list, i.e. `lastname_firstname`.
# * Create a repository for each student, using the student's canonical name.
# - This is a private repository within the course Organization.
# - I turn wikis off and either let GitHub auto-initialize or immediately push files, including a README, into the repos.
# - Give the student team read or pull access to each student's repo. Yes, this allows them to see each others work. I discuss this elsewhere.
# - Give the TA team write or push access to each student's repo.
# - Add the student as collaborator with write or push access.
# - Unwatch these repos personally! Wow such notification.
#
# That's the setup! I use the [gh](https://github.com/gaborcsardi/gh) and [purrr](https://github.com/hadley/purrr) packages to script all of this [GitHub API](https://developer.github.com/v3/) work. *In a second wave, I'll post code snippets for the above operations.*
# The group name for the students
semester_name <- str_c("student_", semester_id)
ta_name <- str_c("ta_", semester_id)
# moc list of github usernames.
# Still need to work out how to get these from students
# mdf <- data.frame(gitName = 1:4)
# mdf$gitName <- c("hathawayj", "dylanjm", "bmwoodruff", "bobdogdogdogdog")
# mdf$gitName
#### Read in from Google Sheets
#### https://www.r-bloggers.com/reading-data-from-google-sheets-into-r/
#gs_ls()
#
drive_auth(email = "hathawayj@gmail.com")
sheets_auth(email = "hathawayj@gmail.com")
ghu <- drive_get("https://docs.google.com/spreadsheets/d/13aQsQYnGTQXyyBUGzE1V9MExEvG5woAmygtkdjltdjk/edit#gid=730524628")
semester_id_upper <- str_to_upper(semester_id)
gh_names <- read_sheet(ss = ghu, sheet = semester_id_upper) %>%
mutate(`BYUI Email` = str_to_lower(`BYUI Email`) %>%
str_replace_all(rx() %>%
rx_find("@") %>%
rx_anything() %>%
rx_end_of_line(), "@byui.edu"))
excel_files <- fs::dir_ls(path("class_lists", semester_id_upper), regexp = "xls")
roster <- map(excel_files, ~ read_xls(.x, skip = 1, col_types = "text")) %>%
bind_rows()
roster_teacher <- tibble(`FERPA Restrict` = "N", `Student ID` = "343419841",
Student = c("Hathaway, J.","Palmer, David"), Status = "Teacher",
`E-mail` = c("hathawayj@byui.edu", "palmerda@byui.edu"), `Cross-listed Course` = NA,
Major = "Faculty", Class = "Faculty")
# roster_add <- NULL
roster_add <- tibble(`FERPA Restrict` = "N",
`Student ID` = "999",
Student = c("Parkinson, Kristy"),
Status = "Visit",
`E-mail` = c("kristyleep@byui.edu"),
`Cross-listed Course` = NA,
Major = "VF",
Class = c("Math"))
roster <- bind_rows(roster, roster_teacher, roster_add)
### check for missing in each roster than have github name #
# People with github names in the google doc but don't show up in the roster based
# on email.
(poormatch <- gh_names %>%
mutate(`E-mail` = `BYUI Email`) %>%
anti_join(roster))
# check for missing in each
roster %>%
mutate(`BYUI Email` = `E-mail`) %>%
anti_join(gh_names) %>%
select(`Student ID`, Student, `E-mail`, `BYUI Email`)
## This is the object that is used to process.
mdf <- roster %>%
mutate(`BYUI Email` = `E-mail`) %>%
left_join(gh_names) %>%
select(`Student ID`, Student, `BYUI Email`, "gitName" = `GitHub Username`)
## ping the usernames
## the safe_gh will protect for bad github usernames
res <- map(mdf$gitName,
~ safe_gh("/users/:username", username = .x, .limit = 100))
res <- transpose(res)
res$error %>% map_lgl(is.null) %>% table() # 83
# return names that are missing github account username
has_ghname <- res$error %>% map_lgl(is.null)
mdf[!has_ghname,]
# https://simonsmith.github.io/github-user-search/#/search
# Send these people an email.
mdf[!has_ghname,"BYUI Email"] %>% pull() %>% str_c(collapse = ", ")
# List of students with github ids
git_students <- mdf$gitName[has_ghname]
### Set-up my path
#cat("GITHUB_PAT=M335_token_Here\n", file = file.path(normalizePath("~/"), ".Renviron"), append = TRUE)
## Build
# Create Repositories
## prepare a data frame to drive repo creation
repo_create_df <- mdf %>%
mutate(description = paste("MCS 335 repository for", Student, "coursework"),
has_ghname = has_ghname,
# a bunch of code to build a repo id for each student
name = str_c("M335_", semester_id_upper, "_",
Student %>% str_split_fixed(",", 2) %>% .[,1] %>% str_replace_all(" ", "_"),
"_",
Student %>% str_split_fixed(",", 2) %>% .[,2] %>% str_sub(2,5),
sep = ""))
# Base file for class work during semester
write_rds(repo_create_df, str_c("class_lists/", semester_id_upper ,"/ClassList_gitnames_", semester_id, ".Rds"))
## find out which repos already exist
res <- map(repo_create_df$name,
~ safe_gh("/repos/BYUI335/:repo", repo = .x))
res <- transpose(res)
oops <- res$error %>% map_lgl(Negate(is.null))
table(oops)
repo_create_df$name[oops] ## needs to be created
repo_create_df$name[!oops] ## been created
create_repo <- function(name, description) {
safe_gh("POST /orgs/BYUI335/repos", name = name, description = description,
private = TRUE, has_wiki = FALSE, auto_init = TRUE)
}
# This filters to just the new students that don't have a repository in their name and have a github username
(repo_create_df_new <- repo_create_df %>% filter(has_ghname & oops))
## This makes the repositories for each student
res <- map2(repo_create_df_new$name, repo_create_df_new$description, create_repo)
res <- transpose(res)
status <- repo_create_df_new %>%
mutate(cr_success = map_lgl(res$result, Negate(is.null))) %>%
select(-description)
########################### Now run the git_pull.R script #####################
Sys.getenv("GITHUB_PAT")
# git functions
source("scripts/git_pull.R")
dwv_repos <- list_repos("BYUI335")
(new_dwv <- repo_create_df_new$name)
# clones created repos on local computer
map(new_dwv,~ git_clone(git_dir = "/Users/hathawayj/git", group_dir = "BYUI335", repo_use = .x))
# for windows computer
#map(new_dwv,~ git_clone(git_dir = "c://git/github", group_dir = "BYUI335", repo_use = .x))
# Copy folder structure into each repo.
folders_to_move <- dir_ls("/Users/hathawayj/git/BYUI335/M335_Template/", type = "directory")
gitignore_move <- dir_ls("/Users/hathawayj/git/BYUI335/M335_Template/", type = "file", all = TRUE, regexp = "gitignore")
# Create full set of combinations and then copy to each repository
full_set <- expand.grid(folders_to_move, new_dwv, stringsAsFactors = FALSE) %>%
rename(folders_to_move = Var1, new_dwv = Var2)
map2(.x = full_set$folders_to_move, .y = full_set$new_dwv,
~dir_copy(path = .x, new_path = glue("/Users/hathawayj/git/BYUI335/{repo}", repo = .y)))
map(.x = new_dwv, ~file_copy(gitignore_move, new_path = glue("/Users/hathawayj/git/BYUI335/{repo}/{filename}", repo = .x, filename = path_file(gitignore_move)), overwrite = TRUE))
map(.x = new_dwv,~ git_push(git_dir = "/Users/hathawayj/git", group_dir = "BYUI335", repo_use = .x))
# junk for when I mess up gitignore
# gitignore_first <- dir_ls("/Users/jhathaway/git/BYUI335/hathaway/", type = "file", all = TRUE, regexp = "gitignore")
# map(.x = new_dwv, ~file_copy(gitignore_first, new_path = glue("/Users/jhathaway/git/BYUI335/{repo}/{filename}", repo = .x, filename = path_file(gitignore_first)), overwrite = TRUE))
# end junk
########################### First Time ###############################
## create the students team for first time
# gh("POST /orgs/BYUI335/teams", name = semester_name, description = "Students in BYU-I MCS 335")
# gh("POST /orgs/BYUI335/teams", name = ta_name, description = "TAs in BYU-I MCS 335")
###################################################
teams <- gh("/orgs/BYUI335/teams")
(students_team_id <- teams %>%
map_df(`[`, c("name", "id")) %>%
## a lookup verb would be awesome
filter(name == semester_name) %>%
.$id)
(ta_team_id <- teams %>%
map_df(`[`, c("name", "id")) %>%
## a lookup verb would be awesome
filter(name == ta_name) %>%
.$id)
#students_team_id <- ta_team_id
## who's already on the team?
res <- gh("GET /teams/:id/members", id = students_team_id, .limit = 100)
team_members <- res %>%
map_chr("login")
## who's missing?
#(missing <- setdiff(git_students, team_members))
##############################################################
##############################################################
## invite missing to the team. They should get an email.
## #############################################################
# repo_create_df_new <- repo_create_df
missing <- repo_create_df_new$gitName
##########################################################################
##########################################################################
##########################################################################
res <- map(missing,
~ safe_gh("PUT /teams/:id/memberships/:username",
id = students_team_id, username = .x))
res <- transpose(res)
res$error %>% map_lgl(is.null) %>% table()
res$result %>%
map_df(`[`, c("state", "role")) %>%
print(n = Inf)
# Give students r/w access to their repository
## https://developer.github.com/v3/repos/collaborators/#add-user-as-a-collaborator
## PUT /repos/:owner/:repo/collaborators/:username
# could use if you created repositorys before getting usernames.
#repo_create_df_new <- repo_create_df
res <- map2(repo_create_df_new$name, repo_create_df_new$gitName,
~ safe_gh("PUT /repos/BYUI335/:repo/collaborators/:username",
repo = .x, username = .y))
res <- transpose(res)
res$error
## give the student team read access to these repos.
## Now every student in the category can see all repositories
res <- map(repo_create_df_new$name,
~ safe_gh("PUT /teams/:id/repos/BYUI335/:repo",
id = students_team_id, repo = .x, permission = "pull"))
res <- transpose(res)
res$error
## give the TA team admin access to these repos.
## Now every TA in the category can see all repositories
res <- map(repo_create_df_new$name,
~ safe_gh("PUT /teams/:id/repos/BYUI335/:repo",
id = ta_team_id, repo = .x, permission = "admin"))
res <- transpose(res)
res$error
## look at all the repos associated with this team
res <- gh("/teams/:id/repos", id = students_team_id, .limit = 100)
team_repos <- res %>% map_chr("name")
## This removes the owner of BYUI3335 from getting notified of all the changes made to the repositorys.
## In this case hathawayj will be removed from watching all the student repositories.
res <- map(repo_create_df_new$name,
~ safe_gh("DELETE /repos/:owner/:repo/subscription",
owner = "BYUI335", repo = .x))
transpose(res)
# delete teachers as owners of all the repos for the students.
# Make sure to add TAs to TA group.
#############################################################
#############################################################
#############################################################
# Create Issue in Each Rep with the Task list
#############################################################
#############################################################
replace_links <- function(x, x_text, x_replace){
x %>% str_replace_all(x_text, x_replace)
}
post_issue_tasks <- function(x, repo_create_df_new, x_title){
bob <- tempdir()
## Need to program in the YAML removal
write_lines(x, path = file.path(bob, "dir.md"))
body_text = read_file(file.path(bob, "dir.md"))
cat(body_text)
safe_gh <- safely(gh::gh)
res <- map(repo_create_df_new$name,
~ safe_gh("POST /repos/:owner/:repo/issues",
owner = "BYUI335", repo = .x, title = x_title, body = body_text))
# Lock the issues so only the BYUI335 owner and the repo owner can make comments.
res <- map(repo_create_df_new$name,
~ safe_gh("PUT /repos/:owner/:repo/issues/:number/lock",
owner = "BYUI335", repo = .x, number = 1))
}
# repo_create_df_new <- read_rds("ClassList_gitnames_fa18.Rds") %>%
# filter(`BYU Email` == "hathawayj@byui.edu")
# repo_create_df_new <- read_rds(str_c("class_lists/", semester_id_upper ,"/ClassList_gitnames_", semester_id, ".Rds")) %>%
# filter(has_ghname == TRUE)
# https://developer.github.com/v3/issues/
# https://developer.github.com/v3/issues/comments/#create-a-comment
body_lines = read_lines("docs/tasklist.md")
## Class tasks section
task_title <- "Daily Class Tasks"
start_task <- body_lines %>% str_which("### Tasks 1 - 12")
task_lines <- body_lines[start_task:length(body_lines)] %>%
replace_links("class_tasks/", "https://byuistats.github.io/M335/class_tasks/")
start_cs <- body_lines %>% str_which("## Weekly Case Studies")
start_semdel <- body_lines %>% str_which("## Semester Deliverables")
semdel_title <- body_lines[start_semdel] %>% str_remove("## ")
semdel_lines <- body_lines[start_semdel:(start_cs - 1)] %>%
replace_links("weekly_projects/", "https://byuistats.github.io/M335/weekly_projects/") %>%
replace_links("teamlead.html", "https://byuistats.github.io/M335/teamlead.html") %>%
replace_links("project.html", "https://byuistats.github.io/M335/project.html")
(dat <- repo_create_df_new)
source("scripts/Post_issue.R")
for (case.study in 13:1) eval(post_issues)
post_issue_tasks(x = task_lines, repo_create_df_new = repo_create_df_new, x_title = task_title)
post_issue_tasks(x = semdel_lines, repo_create_df_new = repo_create_df_new, x_title = semdel_title)
##### Email invites
# https://github.com/BYUI335/M335_WI20_Parkinson_Kris/invitations # to a repo
# https://github.com/orgs/BYUI335/invitation
# Send these people an email that haven't provided info
mdf[!has_ghname,"BYUI Email"] %>% pull() %>% str_c(collapse = ", ")
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.