github.com/bcampbell/scrapeomat@v0.0.0-20220820232205-23e64141c89e/cmd/summarytool/main.go (about) 1 package main 2 3 import ( 4 "encoding/csv" 5 "flag" 6 "fmt" 7 "github.com/bcampbell/scrapeomat/slurp" 8 "golang.org/x/crypto/ssh/terminal" 9 "os" 10 "strconv" 11 "strings" 12 "time" 13 ) 14 15 var opts struct { 16 server string 17 from, to string 18 pubs pubArgs 19 termWidth int 20 csv bool 21 } 22 23 type pubArgs []string 24 25 func (p *pubArgs) String() string { return fmt.Sprintf("%s", *p) } 26 func (p *pubArgs) Set(value string) error { *p = append(*p, value); return nil } 27 28 func init() { 29 flag.StringVar(&opts.from, "from", "", "from date") 30 flag.StringVar(&opts.to, "to", "", "to date") 31 flag.IntVar(&opts.termWidth, "w", 0, "output width (0=auto)") 32 flag.StringVar(&opts.server, "s", "http://localhost:12345", "`url` of API server to query") 33 flag.BoolVar(&opts.csv, "c", false, "output as csv rather than ascii-art") 34 flag.Var(&opts.pubs, "p", "publication code(s)") 35 36 flag.Usage = func() { 37 fmt.Fprintf(os.Stderr, "Usage: %s [OPTIONS]\n", os.Args[0]) 38 fmt.Fprintf(os.Stderr, ` 39 Queries a slurp server and displays summary of article counts 40 using a noddy ascii art chart. 41 `) 42 flag.PrintDefaults() 43 } 44 } 45 46 func main() { 47 48 flag.Parse() 49 50 if !opts.csv && opts.termWidth == 0 { 51 var err error 52 opts.termWidth, err = detectTermWidth() 53 if err != nil { 54 fmt.Fprintf(os.Stderr, "ERROR detecting terminal width: %s\n", err) 55 os.Exit(2) 56 } 57 } 58 59 filt := slurp.Filter{} 60 61 if opts.from != "" { 62 from, err := time.Parse("2006-01-02", opts.from) 63 if err != nil { 64 fmt.Fprintf(os.Stderr, "Bad from: %s\n", err) 65 os.Exit(2) 66 } 67 filt.PubFrom = from 68 } 69 70 if opts.to != "" { 71 to, err := time.Parse("2006-01-02", opts.to) 72 if err != nil { 73 fmt.Fprintf(os.Stderr, "Bad to: %s\n", err) 74 os.Exit(2) 75 } 76 filt.PubTo = to 77 } 78 79 filt.PubCodes = opts.pubs 80 81 slurper := slurp.NewSlurper(opts.server) 82 83 raw, err := slurper.Summary(&filt) 84 85 if err != nil { 86 fmt.Fprintf(os.Stderr, "ERROR: %s\n", err) 87 os.Exit(2) 88 } 89 90 cooked := slurp.CookSummary(raw) 91 92 if opts.csv { 93 err = dumpCSV(cooked) 94 if err != nil { 95 fmt.Fprintf(os.Stderr, "ERROR: %s\n", err) 96 os.Exit(1) 97 } 98 } else { 99 dump(cooked, opts.termWidth) 100 } 101 } 102 103 func detectTermWidth() (int, error) { 104 fd := int(os.Stdout.Fd()) 105 if !terminal.IsTerminal(fd) { 106 return 0, fmt.Errorf("Not a terminal") 107 } 108 w, _, err := terminal.GetSize(int(os.Stdout.Fd())) 109 if err != nil { 110 return 0, err 111 } 112 return w, nil 113 } 114 115 func weekday(day string) string { 116 t, _ := time.Parse("2006-01-02", day) 117 return t.Weekday().String()[:1] 118 } 119 120 func dump(cooked *slurp.CookedSummary, termW int) { 121 122 numReserve := len(fmt.Sprintf("%d", cooked.Max)) 123 124 w := termW - (1 + 1 + 10 + 1 + numReserve + 1 + 1) 125 126 for i, pubCode := range cooked.PubCodes { 127 dat := cooked.Data[i] 128 fmt.Printf("%s\n", pubCode) 129 for j, cnt := range dat { 130 n := (cnt * 1024) / cooked.Max 131 n = (n * w) / 1024 132 day := cooked.Days[j] 133 bar := strings.Repeat("*", n) 134 wd := weekday(day) 135 fmt.Printf("%s %10s %*d %s\n", wd, day, numReserve, cnt, bar) 136 } 137 fmt.Printf("\n") 138 } 139 140 } 141 142 // Output the summary as a csv file 143 func dumpCSV(cooked *slurp.CookedSummary) error { 144 145 out := csv.NewWriter(os.Stdout) 146 147 // header 148 header := []string{"publication"} 149 for _, day := range cooked.Days { 150 header = append(header, day) 151 } 152 out.Write(header) 153 154 // 155 for i, pubCode := range cooked.PubCodes { 156 dat := cooked.Data[i] 157 row := make([]string, len(header)) 158 row[0] = pubCode 159 for j, cnt := range dat { 160 row[1+j] = strconv.Itoa(cnt) 161 } 162 out.Write(row) 163 } 164 out.Flush() 165 return out.Error() 166 }