Skip to content
Merged
18 changes: 18 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
Your commits explain the `who`, `what`, `where` and `when` of these changes. Your code shows the `how`. You do not need to reiterate this. This PR should complete the big picture by telling the `why`.

### Justification

Describe the big picture of your changes here to communicate to the reviewers why they should accept this pull request.
Please describe the importance/impact of the problem and a description of how the changes in this pull request will address, resolve or improve the problem.

Be sure to link to the issue below:

fixes #

### Reviewer instructions:

Assign at least 1 reviewers:

* the reviewers should be familiar with the subject changes.

Please detail the process reviewers will need to follow to properly review.
19 changes: 19 additions & 0 deletions CONTRIBUTING
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
The following are the guidelines for contributing changes or issues to the AtlantisManual repository

# Issues

1. Issues should be made whenever there is a substatitve change requested to the Atlantis documentation
2. Issues should contain the section name and number (e.g "1.3. What is Atlantis?") and/or the figure and table reference (e.g. "1.3.Table 1")
3. If there is an inaccuracy, please specify what exactly is incorrect, how you discovered it, and any supporting documentation
4. You should try to provide suggested edits (especially for text)

# Contributing Edits/Committing changes

1. All requested changes should originate as an issue first to describe the problem
2. Work should be done on an issue branch (from the main branch) with naming scheme (ISSUE_TAG/DESCRIPTION)
3. When changes are completed and committed to the issue branch, make a pull request to bring changes into the main branch
4. The pull request should link to the issue and summarise how the issue was resolved and any other changes (no need to repeat commit history)
5. Request at least one reviewer (other than yourself) to approve the changes

DO NOT commit to the main branch unless it is to fix very minor grammar/spelling/hyperlinks

109 changes: 109 additions & 0 deletions code/link_tester.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
library(tidyverse)
library(fs)
library(stringr)
library(httr2)

# --- Configuration ---
search_path <- "user_guides/quarto_site/" # Change this to your directory

# 1. Find all .qmd files recursively
files <- dir_ls(search_path, recurse = TRUE, glob = "*.qmd")

# 2. Extract URLs, filenames, and line numbers
extract_urls <- function(file_path) {
lines <- readLines(file_path, warn = FALSE)

# Refined Regex: captures the URL but forbids ending on trailing punctuation
# specifically common in Markdown/Quarto like ), or ].
url_pattern <- "https?://[\\w\\d:#@%/;$()~_?\\+-=\\\\\\.&]+(?<![\\.,\\)\\!\\?])"

map_df(seq_along(lines), function(i) {
matches <- str_extract_all(lines[i], url_pattern)[[1]]

if (length(matches) > 0) {
# Clean up any weird edge cases
matches <- str_trim(matches)

tibble(
file = as.character(file_path),
line = i,
url = matches
)
} else {
NULL
}
})
}

url_data <- map_df(files, extract_urls)

# 3. Test URLs (Asynchronously)
# We use httr2 for "multi-request" to speed things up significantly
test_urls_advanced <- function(urls) {
# 1. We use GET instead of HEAD because some paywalls only
# trigger on a full page request.
reqs <- map(urls, ~request(.x) %>%
req_options(followlocation = TRUE) %>%
req_timeout(10))

# 2. Perform parallel requests
resps <- req_perform_parallel(reqs, on_error = "continue")

map_df(resps, function(res) {
# Scenario A: The request was successful (even if it's a 404 or redirect)
if (inherits(res, "httr2_response")) {
tibble(
url = res$request$url, # The URL we started with
status = as.character(resp_status(res)),
final_url = res$url, # Where we ended up
likely_locked = str_detect(res$url, "login|signin|paywall"),
is_active = (resp_status(res) == 200 && !likely_locked)
)
}
# Scenario B: The request failed entirely (DNS, Timeout, etc.)
else {
# 'res' here is an error object. It usually contains the original request.
# We extract the URL from the failed request so it's not NA.
failed_url <- if (!is.null(res$request$url)) res$request$url else "Unknown"

tibble(
url = failed_url,
status = "Failed",
final_url = "CONNECTION_ERROR",
likely_locked = FALSE,
is_active = FALSE
)
}
})
}

# Apply testing
if (nrow(url_data) > 0) {
cat("Testing", nrow(url_data), "unique URLs...\n")

# 1. Get unique URLs to save time
unique_urls_to_test <- unique(url_data$url)

# 2. Run the advanced test (returns a data frame)
test_results <- test_urls_advanced(unique_urls_to_test)

# 3. Join the results back to the original list of file/line locations
final_results <- url_data %>%
left_join(test_results, by = "url")

# 4. Show a summary of the suspicious links
flagged_links <- final_results %>%
filter(!is_active)

if (nrow(flagged_links) > 0) {
cat("\nFound", nrow(flagged_links), "problematic or gated links:\n")
print(flagged_links)
} else {
cat("\nAll links appear active and open!")
}

} else {
message("No URLs found.")
}

write.csv(final_results,here::here('code','link_testing.csv'),row.names =F)
Loading