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  }