Generate COVID-19 reports. Requires R with tidyverse installed.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

159 lines
3.4 KiB

package main
import (
"bufio"
"bytes"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"net/smtp"
"os"
"os/exec"
"path"
"time"
"github.com/BurntSushi/toml"
homedir "github.com/mitchellh/go-homedir"
"github.com/domodwyer/mailyak"
)
type Config struct {
MailHost, Username, Password string
MailPort int
Len int64
}
var conf Config
var confFile string
func getConf() Config {
confDir := path.Dir(confFile)
if _, err := os.Stat(confDir); os.IsNotExist(err) {
log.Printf("Creating config directory")
err = os.MkdirAll(confDir, 0777)
if err != nil {
log.Fatal("Cannot create config directory: ", err)
}
}
if _, err := os.Stat(confFile); os.IsNotExist(err) {
log.Printf("Creating config file")
f, err := os.Create(confFile)
if err != nil {
log.Fatal("Cannot create config file: ", err)
}
err = f.Close()
if err != nil {
log.Fatal("Error closing config file: ", err)
}
}
if _, err := toml.DecodeFile(confFile, &conf); err != nil {
log.Fatal("Error reading config file: ", err)
}
of, err := os.Create(path.Join(confDir, "c.R"))
if err != nil {
log.Fatal("Error creating R source file: ", err)
}
_, err = of.WriteString(rCode())
if err != nil {
log.Fatal("Error writing R source code: ", err)
}
return conf
}
func save() {
of, err := os.Create(confFile)
if err != nil {
log.Fatal("Cannot open config file: ", err)
}
var buf bytes.Buffer
w := bufio.NewWriter(&buf)
enc := toml.NewEncoder(w)
enc.Encode(conf)
io.Copy(of, &buf)
err = of.Close()
if err != nil {
log.Fatal("Error closing config file: ", err)
}
}
func fetch() {
url := "https://health.data.ny.gov/api/views/xdss-u53e/rows.csv?accessType=DOWNLOAD"
resp, err := http.Get(url)
if err != nil {
log.Printf("HTTP error: ", err)
return
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Printf("Read error: ", err)
return
}
if int64(len(body)) == conf.Len {
log.Printf("Equal length, returning")
return
}
log.Printf("Length differs: body = %d", len(body))
conf.Len = int64(len(body))
save()
of, err := os.Create(path.Join(path.Dir(confFile), "rows.csv"))
if err != nil {
log.Fatal("Cannot create rows.csv: ", err)
}
_, err = of.Write(body)
if err != nil {
log.Fatal("Error writing rows.csv: ", err)
}
confDir := path.Dir(confFile)
cmd := exec.Command("R", "--no-save", "-f", path.Join(confDir, "c.R"))
log.Printf("Running R")
err = cmd.Run()
if err != nil {
log.Fatal("Error running R: ", err)
}
log.Printf("R completed")
if conf.MailHost == "" {
log.Print("No mail host set, returning")
return
}
f, err := os.Open("Rplots.pdf")
if err != nil {
log.Fatal("Error opening Rplots.pdf: ", err)
}
defer f.Close()
email := mailyak.New(fmt.Sprintf("%s:%d", conf.MailHost, conf.MailPort),
smtp.PlainAuth("", conf.Username, conf.Password, conf.MailHost))
email.To("gmp@wow.st")
email.From("covid@wow.st")
msg := "Covid dashboard update for " + time.Now().Format("Monday, January 2 2006")
email.Subject(msg)
email.Plain().Set(msg)
email.Attach("dashboard.pdf", f)
err = email.Send()
if err != nil {
log.Print("Error sending email: ", err)
}
}
func main() {
log.Printf("starting")
homeDir, err := homedir.Dir()
if err != nil {
log.Fatal("Cannot locate user's home directory")
}
confFile = path.Join(homeDir, ".config", "cget", "cget.conf")
os.Chdir(path.Dir(confFile))
getConf()
for {
fetch()
time.Sleep(time.Minute * 30)
}
log.Printf("done")
}