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
4 changes: 2 additions & 2 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Package: migraph
Title: Inferential Methods for Multimodal and Other Networks
Version: 1.6.8
Version: 1.6.9
Description: A set of tools for testing networks.
It includes functions for univariate and multivariate conditional uniform graph
and quadratic assignment procedure testing, and network regression.
Expand Down Expand Up @@ -69,4 +69,4 @@ Config/Needs/website:
Config/testthat/parallel: true
Config/testthat/edition: 3
Config/testthat/start-first: tutorials_migraph
Config/roxygen2/version: 8.0.0
Config/roxygen2/version: 8.1.0
52 changes: 31 additions & 21 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -34,28 +34,38 @@ export(test_permutation)
export(test_random)
export(tidy)
importFrom(autograph,ag_base)
importFrom(dplyr,as_tibble)
importFrom(dplyr,left_join)
importFrom(dplyr,select)
importFrom(dplyr,tibble)
importFrom(dplyr,
as_tibble,
left_join,
select,
tibble
)
importFrom(ergm,as.rlebdm)
importFrom(furrr,furrr_options)
importFrom(furrr,future_map_dfr)
importFrom(furrr,
furrr_options,
future_map_dfr
)
importFrom(future,plan)
importFrom(generics,glance)
importFrom(generics,tidy)
importFrom(manynet,bind_node_attributes)
importFrom(manynet,generate_configuration)
importFrom(manynet,generate_random)
importFrom(manynet,is_complex)
importFrom(manynet,is_directed)
importFrom(manynet,play_diffusion)
importFrom(manynet,to_subgraph)
importFrom(generics,
glance,
tidy
)
importFrom(manynet,
bind_node_attributes,
generate_configuration,
generate_random,
is_complex,
is_directed,
play_diffusion,
to_subgraph
)
importFrom(netrics,net_by_heterophily)
importFrom(purrr,flatten)
importFrom(stats,as.formula)
importFrom(stats,binomial)
importFrom(stats,df.residual)
importFrom(stats,glm.fit)
importFrom(stats,pchisq)
importFrom(stats,quantile)
importFrom(stats,
as.formula,
binomial,
df.residual,
glm.fit,
pchisq,
quantile
)
13 changes: 13 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
# migraph 1.6.9

## Package

- Improved stocnet version check, consolidating into `{migraph}` so that it runs once rather than three times
- Also consults each package's GitHub repository, so a fix that is released but not yet published on CRAN is reported; where both sources agree, CRAN is recommended, since it needs no compiler or `{remotes}`
- Results are cached for seven days in `tools::R_user_dir("migraph", "cache")`, so most sessions make no network request at all
- Check is skipped in non-interactive sessions, fails silently when offline or when no repository is configured, and can be disabled with `options(snet_check_version = FALSE)`

## Learning

- Improved empty `run_tute()` listing of tutorials to suggest manynet>autograph>netrics>migraph sequence

# migraph 1.6.8

2026-07-31
Expand Down
3 changes: 1 addition & 2 deletions R/tutorial_run.R
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
#' @name tutorials
NULL

stocnet <- c("manynet", "migraph", "autograph", "netrics")
stocnet <- c("manynet", "autograph", "netrics", "migraph")

#' @rdname tutorials
#' @export
Expand All @@ -34,7 +34,6 @@ run_tute <- function(tute) {
silent = TRUE) |> dplyr::select(1:3)
})
dplyr::bind_rows(tutelist) |>
dplyr::arrange(dplyr::across(dplyr::any_of("name"))) |>
print()
manynet::snet_info("You can run a tutorial by typing e.g `run_tute('tutorial1')` or `run_tute('Data')` into the console.")
} else {
Expand Down
146 changes: 146 additions & 0 deletions R/zzz.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
# nocov start

# The stocnet packages used to each check CRAN for a newer version of themselves
# on attach. Since migraph Depends on manynet, autograph, and netrics, that meant
# three blocking round-trips per session. The check now lives here only, covers
# the whole stack at once, and is cached so it runs at most weekly.

snet_pkgs <- c("migraph", "manynet", "autograph", "netrics")

# How long to trust a cached result before checking again.
snet_check_interval <- 7

snet_cache_file <- function() {
file.path(tools::R_user_dir("migraph", which = "cache"), "version-check.rds")
}

snet_read_cache <- function() {
f <- snet_cache_file()
if (!file.exists(f)) return(NULL)
out <- tryCatch(readRDS(f), error = function(e) NULL)
if (!is.list(out) || is.null(out$date) || !inherits(out$date, "Date")) return(NULL)
if (as.numeric(Sys.Date() - out$date) >= snet_check_interval) return(NULL)
out
Comment thread
Copilot marked this conversation as resolved.
}

snet_write_cache <- function(behind) {
f <- snet_cache_file()
# Failure to cache is not worth bothering the user about; the check simply
# runs again next session.
tryCatch({
dir.create(dirname(f), recursive = TRUE, showWarnings = FALSE)
saveRDS(list(date = Sys.Date(), behind = behind), f)
}, error = function(e) NULL, warning = function(w) NULL)
}

# Versions available on CRAN. Returns a named character vector, or NULL when no
# repository is configured (as under a bare Rscript) or CRAN is unreachable.
snet_cran_versions <- function() {
repos <- getOption("repos")
if (is.null(repos) || !length(repos) || any(repos == "@CRAN@")) return(NULL)
tryCatch({
ap <- utils::available.packages()
have <- intersect(snet_pkgs, rownames(ap))
if (!length(have)) return(NULL)
out <- ap[have, "Version"]
names(out) <- have
out
}, error = function(e) NULL, warning = function(w) NULL)
}

# Versions on the release branch of each GitHub repo. Much cheaper than the CRAN
# index: four ~2KB files from a CDN rather than the full package database.
snet_github_versions <- function() {
tryCatch({
# Generous: fetching all four takes about 0.4s on a working connection.
old <- options(timeout = 2)
on.exit(options(old), add = TRUE)
out <- character()
for (p in snet_pkgs) {
url <- paste0("https://raw.githubusercontent.com/stocnet/", p,
"/main/DESCRIPTION")
txt <- tryCatch(readLines(url, warn = FALSE), error = function(e) NULL)
# If the first request fails the host is unreachable, so give up rather
# than waiting out the timeout once per package. Attach should never
# stall on a bad network.
if (is.null(txt)) {
if (!length(out)) return(NULL) else next
}
line <- grep("^Version:", txt, value = TRUE)
if (!length(line)) next
out[[p]] <- trimws(sub("^Version:", "", line[1]))
Comment thread
jhollway marked this conversation as resolved.
}
if (!length(out)) NULL else out
}, error = function(e) NULL, warning = function(w) NULL)
}

# Which packages are behind, and where the newer version lives. When CRAN and
# GitHub agree, prefer CRAN: it is the binary install and needs no compiler.
snet_check_versions <- function() {
cran <- snet_cran_versions()
gh <- snet_github_versions()
if (is.null(cran) && is.null(gh)) return(NULL)
out <- list()
for (p in snet_pkgs) {
installed <- tryCatch(utils::packageVersion(p), error = function(e) NULL)
if (is.null(installed)) next
cv <- if (!is.null(cran) && p %in% names(cran)) utils::packageVersion(cran[[p]]) else NULL
gv <- if (!is.null(gh) && p %in% names(gh)) utils::packageVersion(gh[[p]]) else NULL
if (!is.null(cv) && cv > installed) {
out[[p]] <- list(version = as.character(cv), source = "CRAN")
} else if (!is.null(gv) && gv > installed) {
out[[p]] <- list(version = as.character(gv), source = "GitHub")
}
}
out
}

snet_report_outdated <- function(behind) {
if (!length(behind)) return(invisible(NULL))
pkgs <- names(behind)
vers <- vapply(behind, function(x) x$version, character(1))
from_cran <- pkgs[vapply(behind, function(x) x$source, character(1)) == "CRAN"]
from_gh <- setdiff(pkgs, from_cran)

packageStartupMessage(
"Newer version", if (length(pkgs) > 1) "s" else "", " available: ",
paste0(pkgs, " ", vers, collapse = ", "), ".")

# Deliberately printed rather than prompted for. `utils::menu()` reads from
# stdin, and when stdin is at EOF while `interactive()` is still TRUE it loops
# forever, hanging the session on `library()`. Attach should never block on
# input, so name the command and let the user run it.
if (length(from_cran)) {
packageStartupMessage("Update from CRAN with:\n update.packages(c(",
paste0('"', from_cran, '"', collapse = ", "), "))")
}
if (length(from_gh)) {
packageStartupMessage(
"Not yet on CRAN. Install from GitHub with:\n remotes::install_github(c(",
paste0('"stocnet/', from_gh, '"', collapse = ", "), "))")
}
invisible(NULL)
}

.onAttach <- function(...) {

if (!interactive()) return()
if (!isTRUE(getOption("snet_check_version", TRUE))) return()

Comment thread
jhollway marked this conversation as resolved.
cached <- snet_read_cache()
if (!is.null(cached)) {
snet_report_outdated(cached$behind)
return(invisible(NULL))
}

behind <- snet_check_versions()
# NULL means the check could not run (offline, no repo); don't cache that, so
# it is retried next session rather than suppressed for a week.
if (is.null(behind)) return(invisible(NULL))

snet_write_cache(behind)
snet_report_outdated(behind)

}

# nocov end
12 changes: 12 additions & 0 deletions cran-comments.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,15 @@
## R CMD check results

0 errors | 0 warnings | 0 notes

## User filespace and internet access

On attach in interactive sessions only, this version checks whether the installed stocnet packages
are outdated, and caches the result for seven days in `tools::R_user_dir("migraph", "cache")`. This
replaces a check that previously ran on every attach in each of three dependencies, so it reduces
both network use and startup time.

The check queries CRAN and the packages' public GitHub repositories. It is wrapped in `tryCatch()`
with a short timeout, fails silently when offline or when no repository is configured, is skipped
entirely in non-interactive sessions, and can be disabled with `options(snet_check_version = FALSE)`.
The package is fully functional if the cache directory is absent or unwritable.
19 changes: 16 additions & 3 deletions tests/testthat/test-measure_over.R
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,20 @@ test_that("over_waves works", {
})

test_that("over_membership works", {
res <- over_membership(fict_potter, netrics::net_by_assortativity,
membership = netrics::node_in_regular(fict_potter))
expect_equal(unname(unlist(c(res))), c(0.490201713,NaN))
# A fixed two-block network with an explicit membership, so that the
# expectations do not move when upstream clustering methods change.
# Block A is a triad plus a pendant, block B a path, joined by one cross tie.
el <- rbind(c(1,2), c(1,3), c(1,4), c(2,3), c(5,6), c(6,7), c(7,8), c(4,5))
mat <- matrix(0, 8, 8)
mat[el] <- 1
mat[el[, 2:1]] <- 1
memb <- rep(c("A","B"), each = 4)
# Densities check the blocks are split as expected, the cross tie excluded:
# 4 of 6 possible ties within A, 3 of 6 within B.
expect_equal(unname(unlist(c(over_membership(mat, netrics::net_by_density,
membership = memb)))),
c(4/6, 3/6))
expect_equal(unname(unlist(c(over_membership(mat, netrics::net_by_assortativity,
membership = memb)))),
c(-5/7, -1/2))
})
Loading