Automated E-Mail Updates Of CRAN Downloads Using Raspberry Pi 4

Want to have regular updates on how your R packages are doing on CRAN when it comes to downloads? This is one way to get regular updates using a Raspberry Pi 4 via e-mail.

Yannik yannikbuhl.de
2022-10-06

In the past years, I have (co-)authored a couple of R packages that ended up being uploaded to CRAN (newsanchor, pocketapi and giedata). Now, I have always been curious as to whether people actually use them. While one can’t know that for sure, one can have a look at the download numbers on CRAN - they are publicly available.

So that is what I did a while, I used a function to download and display the numbers from CRAN, you can find a Gist with a function to do so here. However, I recently got myself a Raspberry Pi 4 (a Raspberry 2 or 3 will do the job, too) and wanted to have automated CRAN download reports send to me via mail. So I dug in the internet, and here’s what I came up with.

First, we need a R script that we can automate that will get us the data from CRAN and save it on the Raspberry Pi’s drive so we can send it as a mail attachment:

# File name: get-package-data.R

library(cranlogs)
library(dplyr)
library(tibble)

# The download function
cran <- function(package, from = "2019-01-01") {

  mls <- cran_downloads(packages = package, from = from, to = Sys.Date() - 1)

  return(mls)

}

# Read in the "old" data - you will need to create the file once at the start
old_data <- utils::read.csv("cran-hist.csv")

# Count the number of downloads
pocketapi_sum <- cran("pocketapi") %>% summarise(sum = sum(count))
newsanchor_sum <- cran("newsanchor") %>% summarise(sum = sum(count))
giedata_sum <- cran("giedata") %>% summarise(sum = sum(count))

# Calculate the difference to the "old" data
diff_pocket <- pocketapi_sum$sum - old_data$sum[old_data$package == "pocketapi"]
diff_news <- newsanchor_sum$sum - old_data$sum[old_data$package == "newsanchor"]
diff_gie <- giedata_sum$sum - old_data$sum[old_data$package == "giedata"]

# Create a tibble that can be saved to a file
crandata <- tibble(package = c("pocketapi", "newsanchor", "giedata"),
       date = rep(Sys.time(), 3),
       sum = c(pocketapi_sum$sum, newsanchor_sum$sum, giedata_sum$sum),
       diff = c(diff_pocket, diff_news, diff_gie))

# Write the data into a .txt file, our mail attachment
write.table(crandata, "cranlogs.txt", quote = FALSE, row.names = FALSE, 
      sep = "     |     ")

# Update the file with the historic data
write.csv(crandata, "cran-hist.csv", row.names = FALSE)

Now, our mail attachment file looks like this:

crandata
# A tibble: 3 x 4
  package    date                  sum  diff
  <chr>      <dttm>              <dbl> <dbl>
1 pocketapi  2022-10-09 13:43:29  6478     0
2 newsanchor 2022-10-09 13:43:29 18742     0
3 giedata    2022-10-09 13:43:29   531     0

Next, we use a Python script to send us a mail using the attachment .txt file. I followed the instructions here. Be aware that you cannot use Gmail easily anymore because external apps can no longer be allowed to access Gmail. You have to use OAuth2 (see instructions following the link above).

# File name: send-mail.py

import smtplib
import ssl
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.application import MIMEApplication

ctx = ssl.create_default_context()

password = "PASSWORD" # Your password - change!
sender = "sender@mail.com" # Your sender mail address - change!
receiver = "receiver@mail.com" # Receiver address - change!

message = MIMEMultipart("mixed")
message["Subject"] = "Neue CRAN-Downloads"
message["From"] = sender
message["To"] = receiver

plain = """\
        Hello Yannik, 

        there are new CRAN download numbers available.

        Best
        Raspby 4

        """
        
message.attach(MIMEText(plain, "plain"))

# Path to the attachment file - change!
filename = 'cranlogs.txt'

with open(filename, "rb") as f:
    file = MIMEApplication(f.read())

disposition = "attachment; filename=cranlogs.txt"

file.add_header("Content-Disposition", disposition)

message.attach(file)

with smtplib.SMTP_SSL("smtp.gmail.com", port = 465, context = ctx) as server:
    server.login(sender, password)
    server.sendmail(sender, receiver, message.as_string())

Finally, just upload these files to your Raspberry Pi and automate them using a Cronjob. In this example, the scripts run every morning and send you an update on your package downloads. Make sure the R script runs before the Python script.

30 7 * * * /usr/bin/Rscript --no-restore --no-site-file --no-init-file ~/path/to/your/file/get-package-data.R
35 7 * * * /usr/bin/python ~/path/to/your/file/send-mail.py