From 8355216f9badcdb4216b7f00d93df98fdcbdfbea Mon Sep 17 00:00:00 2001 From: tuxuser <462620+tuxuser@users.noreply.github.com> Date: Sun, 8 Aug 2021 06:41:10 +0200 Subject: [PATCH 1/6] Implement custom currency symbol/abbrevation (resolves issue #7) --- cmd/root.go | 1 + cmd/set.go | 22 +++++++++++++++++++--- internal/tui/tui.go | 10 +++++++--- 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/cmd/root.go b/cmd/root.go index b42e1a2..b72c41a 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -40,6 +40,7 @@ func init() { // initConfig reads and/or initializes the configuration file. func initConfig() { viper.SetDefault("averageSalary", 150000) + viper.SetDefault("currencySymbol", "$") // Find home directory. home, err := homedir.Dir() diff --git a/cmd/set.go b/cmd/set.go index 0d655ce..2085912 100644 --- a/cmd/set.go +++ b/cmd/set.go @@ -17,8 +17,9 @@ var setCmd = &cobra.Command{ PreRun: toggleDebug, RunE: func(cmd *cobra.Command, args []string) error { averageSalary := viper.GetViper().GetInt("averageSalary") + currencySymbol := viper.GetViper().GetString("currencySymbol") - q := &survey.Question{ + qSalary := &survey.Question{ Prompt: &survey.Input{ Message: "Set average annual salary of meeting participants:", Default: strconv.Itoa(averageSalary), @@ -32,18 +33,33 @@ var setCmd = &cobra.Command{ }, } - err := survey.AskOne(q.Prompt, &averageSalary, survey.WithValidator(q.Validate)) + qCurrencySymbol := &survey.Question{ + Prompt: &survey.Input{ + Message: "Set symbol or abbrevation of your local currency:", + Default: "$", + }, + } + + err := survey.AskOne(qSalary.Prompt, &averageSalary, survey.WithValidator(qSalary.Validate)) + if err != nil { + return err + } + + // No validation required for currencySymbol + err = survey.AskOne(qCurrencySymbol.Prompt, ¤cySymbol) if err != nil { return err } viper.GetViper().Set("averageSalary", averageSalary) + viper.GetViper().Set("currencySymbol", currencySymbol) if err := viper.WriteConfig(); err != nil { return err } log.Printf( - "The average annual salary of meeting participants has been updated to %v in the configuration file.", + "The average annual salary of meeting participants has been updated to %s %v in the configuration file.", + currencySymbol, averageSalary, ) diff --git a/internal/tui/tui.go b/internal/tui/tui.go index f90d149..3d3726c 100644 --- a/internal/tui/tui.go +++ b/internal/tui/tui.go @@ -127,7 +127,7 @@ func Start(manual bool, data *Data) { tick(s, data, manual, quit) s.Fini() - log.Infof("Total cost: $%.2f", data.getCost()) + log.Infof("Total cost: %s%.2f", data.GetCurrencySymbol(), data.getCost()) } // data stores variables passed around between the various goRoutines. @@ -138,6 +138,10 @@ type Data struct { input string } +func (data *Data) GetCurrencySymbol() string { + return viper.GetViper().GetString("currencySymbol") +} + // Get count. func (data *Data) GetCount() int { data.mtx.Lock() @@ -221,7 +225,7 @@ func draw(s tcell.Screen, data *Data, manual bool) { style := tcell.StyleDefault.Foreground(tcell.ColorCornflowerBlue) emitStr(s, 0, 0, style, "Clockwise") - costString := fmt.Sprintf("Total cost: $%.2f", data.getCost()) + costString := fmt.Sprintf("Total cost: %s%.2f", data.GetCurrencySymbol(), data.getCost()) emitStr(s, 0, 1, tcell.StyleDefault, costString) countString := fmt.Sprintf("Participant count: %s", strconv.Itoa((data.GetCount()))) @@ -252,7 +256,7 @@ func writeCostFile(data *Data) { outputFile := outputFolder + "clockwise.txt" for { - costString := fmt.Sprintf("Total cost: $%.2f\n", data.getCost()) + costString := fmt.Sprintf("Total cost: %s%.2f\n", data.GetCurrencySymbol(), data.getCost()) if err := ioutil.WriteFile(outputFile, []byte(costString), 0600); err != nil { log.Fatal(err) From 21646ecd04c15635b8aebb633c6490f1093cdec0 Mon Sep 17 00:00:00 2001 From: tuxuser <462620+tuxuser@users.noreply.github.com> Date: Sun, 8 Aug 2021 06:44:50 +0200 Subject: [PATCH 2/6] Fix: Spelling --- cmd/set.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/set.go b/cmd/set.go index 2085912..0a60ab1 100644 --- a/cmd/set.go +++ b/cmd/set.go @@ -12,7 +12,7 @@ import ( // setCmd represents the set command. var setCmd = &cobra.Command{ Use: "set", - Short: "Set the average annual salary of meeting participants", + Short: "Set the average annual salary of meeting participants and currency representation", Long: `Set the average annual salary of meeting participants. This does not need to be an exact number.`, PreRun: toggleDebug, RunE: func(cmd *cobra.Command, args []string) error { @@ -35,7 +35,7 @@ var setCmd = &cobra.Command{ qCurrencySymbol := &survey.Question{ Prompt: &survey.Input{ - Message: "Set symbol or abbrevation of your local currency:", + Message: "Set symbol or abbreviation of your local currency:", Default: "$", }, } From 651dd40fa3e8c489d1910f639512a666413eb0d1 Mon Sep 17 00:00:00 2001 From: tuxuser <462620+tuxuser@users.noreply.github.com> Date: Sun, 8 Aug 2021 18:24:05 +0200 Subject: [PATCH 3/6] Set subcommand: Bundle survey questions into single structure --- cmd/set.go | 59 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 31 insertions(+), 28 deletions(-) diff --git a/cmd/set.go b/cmd/set.go index 0a60ab1..b220303 100644 --- a/cmd/set.go +++ b/cmd/set.go @@ -16,51 +16,54 @@ var setCmd = &cobra.Command{ Long: `Set the average annual salary of meeting participants. This does not need to be an exact number.`, PreRun: toggleDebug, RunE: func(cmd *cobra.Command, args []string) error { - averageSalary := viper.GetViper().GetInt("averageSalary") - currencySymbol := viper.GetViper().GetString("currencySymbol") + // Fetch currently set values from config or default values + averageSalaryPrev := viper.GetViper().GetInt("averageSalary") + currencySymbolPrev := viper.GetViper().GetString("currencySymbol") - qSalary := &survey.Question{ - Prompt: &survey.Input{ - Message: "Set average annual salary of meeting participants:", - Default: strconv.Itoa(averageSalary), - }, - Validate: func(val interface{}) error { - if _, err := strconv.Atoi(val.(string)); err != nil { - return err - } + q := []*survey.Question{ + { + Name: "averageSalary", + Prompt: &survey.Input{ + Message: "Set average annual salary of meeting participants:", + Default: strconv.Itoa(averageSalaryPrev), + }, + Validate: func(val interface{}) error { + if _, err := strconv.Atoi(val.(string)); err != nil { + return err + } - return nil + return nil + }, }, - } - - qCurrencySymbol := &survey.Question{ - Prompt: &survey.Input{ - Message: "Set symbol or abbreviation of your local currency:", - Default: "$", + { + Name: "currencySymbol", + Prompt: &survey.Input{ + Message: "Set symbol or abbreviation of your local currency:", + Default: currencySymbolPrev, + }, }, } - err := survey.AskOne(qSalary.Prompt, &averageSalary, survey.WithValidator(qSalary.Validate)) - if err != nil { - return err - } + answers := struct { + AverageSalary int + CurrencySymbol string + }{} - // No validation required for currencySymbol - err = survey.AskOne(qCurrencySymbol.Prompt, ¤cySymbol) + err := survey.Ask(q, &answers) if err != nil { return err } - viper.GetViper().Set("averageSalary", averageSalary) - viper.GetViper().Set("currencySymbol", currencySymbol) + viper.GetViper().Set("averageSalary", answers.AverageSalary) + viper.GetViper().Set("currencySymbol", answers.CurrencySymbol) if err := viper.WriteConfig(); err != nil { return err } log.Printf( "The average annual salary of meeting participants has been updated to %s %v in the configuration file.", - currencySymbol, - averageSalary, + answers.CurrencySymbol, + answers.AverageSalary, ) return nil From 42861ee9f4c888605a4e3c1e85d1c0f61eb93d93 Mon Sep 17 00:00:00 2001 From: tuxuser <462620+tuxuser@users.noreply.github.com> Date: Sun, 8 Aug 2021 18:31:36 +0200 Subject: [PATCH 4/6] TUI: Access CurrencySymbol as const class member, instead of calling Viper.GetString('currencySymbol') over and over --- internal/tui/tui.go | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/internal/tui/tui.go b/internal/tui/tui.go index 3d3726c..c5ec292 100644 --- a/internal/tui/tui.go +++ b/internal/tui/tui.go @@ -47,6 +47,8 @@ func Start(manual bool, data *Data) { } averageSalary := viper.GetViper().GetInt("averageSalary") + currencySymbol := viper.GetViper().GetString("currencySymbol") + data.currencySymbol = currencySymbol // Start cost calculation goroutine. go func() { @@ -127,19 +129,16 @@ func Start(manual bool, data *Data) { tick(s, data, manual, quit) s.Fini() - log.Infof("Total cost: %s%.2f", data.GetCurrencySymbol(), data.getCost()) + log.Infof("Total cost: %s%.2f", data.currencySymbol, data.getCost()) } // data stores variables passed around between the various goRoutines. type Data struct { - mtx sync.Mutex - count int - cost float32 - input string -} - -func (data *Data) GetCurrencySymbol() string { - return viper.GetViper().GetString("currencySymbol") + mtx sync.Mutex + count int + cost float32 + input string + currencySymbol string } // Get count. @@ -225,7 +224,7 @@ func draw(s tcell.Screen, data *Data, manual bool) { style := tcell.StyleDefault.Foreground(tcell.ColorCornflowerBlue) emitStr(s, 0, 0, style, "Clockwise") - costString := fmt.Sprintf("Total cost: %s%.2f", data.GetCurrencySymbol(), data.getCost()) + costString := fmt.Sprintf("Total cost: %s%.2f", data.currencySymbol, data.getCost()) emitStr(s, 0, 1, tcell.StyleDefault, costString) countString := fmt.Sprintf("Participant count: %s", strconv.Itoa((data.GetCount()))) @@ -256,7 +255,7 @@ func writeCostFile(data *Data) { outputFile := outputFolder + "clockwise.txt" for { - costString := fmt.Sprintf("Total cost: %s%.2f\n", data.GetCurrencySymbol(), data.getCost()) + costString := fmt.Sprintf("Total cost: %s%.2f\n", data.currencySymbol, data.getCost()) if err := ioutil.WriteFile(outputFile, []byte(costString), 0600); err != nil { log.Fatal(err) From 48533035f15df99870dac18f3813538c8834f51d Mon Sep 17 00:00:00 2001 From: tuxuser <462620+tuxuser@users.noreply.github.com> Date: Thu, 19 Aug 2021 13:47:47 +0200 Subject: [PATCH 5/6] cmd/set: Remove space between currency symbol and average salaray --- cmd/set.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/set.go b/cmd/set.go index b220303..b5e021c 100644 --- a/cmd/set.go +++ b/cmd/set.go @@ -61,7 +61,7 @@ var setCmd = &cobra.Command{ } log.Printf( - "The average annual salary of meeting participants has been updated to %s %v in the configuration file.", + "The average annual salary of meeting participants has been updated to %s%v in the configuration file.", answers.CurrencySymbol, answers.AverageSalary, ) From 64d9af5213074e308907bcd281da42395fb06d76 Mon Sep 17 00:00:00 2001 From: tuxuser <462620+tuxuser@users.noreply.github.com> Date: Thu, 19 Aug 2021 14:51:31 +0200 Subject: [PATCH 6/6] Implement tui.Config struct (containing manualMode, averageSalary, currencySymbol) --- cmd/run.go | 9 +++++--- internal/tui/config.go | 49 ++++++++++++++++++++++++++++++++++++++++++ internal/tui/tui.go | 44 ++++++++++++++++--------------------- 3 files changed, 74 insertions(+), 28 deletions(-) create mode 100644 internal/tui/config.go diff --git a/cmd/run.go b/cmd/run.go index 3a5b67f..b11b7d5 100644 --- a/cmd/run.go +++ b/cmd/run.go @@ -32,8 +32,11 @@ var runCmd = &cobra.Command{ manual = true } + var config tui.Config + config.SetFromViperConfig(manual) + var scraper scrape.Scraper - if !manual { + if !config.GetManualMode() { // Checking optional force_jitsi flag first switch { case forceJitsi || strings.Contains(url, "meet.jit.si"): @@ -49,7 +52,7 @@ var runCmd = &cobra.Command{ // `scrape` packages. var data tui.Data - if !manual { + if !config.GetManualMode() { log.Println("Initializing playwright to scrape participant count.") pw, err := scrape.InitializePlaywright() if err != nil { @@ -66,7 +69,7 @@ var runCmd = &cobra.Command{ }() } - tui.Start(manual, &data) + tui.Start(&data, &config) log.Info("Clockwise has been stopped.") return nil diff --git a/internal/tui/config.go b/internal/tui/config.go new file mode 100644 index 0000000..4c1aaeb --- /dev/null +++ b/internal/tui/config.go @@ -0,0 +1,49 @@ +package tui + +import ( + "github.com/spf13/viper" +) + +// Static configuration variables, enumerated at the start of the application +type Config struct { + manualMode bool + averageSalary int + currencySymbol string +} + +// Receive configuration values from Viper +func (c *Config) SetFromViperConfig(manualMode bool) { + c.manualMode = manualMode + c.averageSalary = viper.GetViper().GetInt("averageSalary") + c.currencySymbol = viper.GetViper().GetString("currencySymbol") +} + +// Set manual mode +func (c *Config) SetManualMode(value bool) { + c.manualMode = value +} + +// Get manual mode +func (c *Config) GetManualMode() bool { + return c.manualMode +} + +// Set average salary +func (c *Config) SetAverageSalary(value int) { + c.averageSalary = value +} + +// Get average salary +func (c *Config) GetAverageSalary() int { + return c.averageSalary +} + +// Set currency symbol +func (c *Config) SetCurrencySymbol(value string) { + c.currencySymbol = value +} + +// Get currency symbol +func (c *Config) GetCurrencySymbol() string { + return c.currencySymbol +} diff --git a/internal/tui/tui.go b/internal/tui/tui.go index c5ec292..26492ab 100644 --- a/internal/tui/tui.go +++ b/internal/tui/tui.go @@ -10,7 +10,6 @@ import ( "unicode" log "github.com/sirupsen/logrus" - "github.com/spf13/viper" "github.com/gdamore/tcell/v2" "github.com/mattn/go-runewidth" @@ -38,7 +37,7 @@ func initScreen() (tcell.Screen, error) { return s, nil } -func Start(manual bool, data *Data) { +func Start(data *Data, config *Config) { // var data Data s, err := initScreen() @@ -46,18 +45,14 @@ func Start(manual bool, data *Data) { log.Fatal(err) } - averageSalary := viper.GetViper().GetInt("averageSalary") - currencySymbol := viper.GetViper().GetString("currencySymbol") - data.currencySymbol = currencySymbol - // Start cost calculation goroutine. go func() { - calculateCost(data, averageSalary) + calculateCost(data, config.GetAverageSalary()) }() // Start cost file generation subroutine. go func() { - writeCostFile(data) + writeCostFile(data, config) }() quit := make(chan struct{}) @@ -77,10 +72,10 @@ func Start(manual bool, data *Data) { } case *tcell.EventResize: s.Sync() - draw(s, data, manual) + draw(s, data, config) } - if manual { + if config.GetManualMode() { switch ev := ev.(type) { case *tcell.EventKey: switch ev.Key() { @@ -120,25 +115,24 @@ func Start(manual bool, data *Data) { } // Render TUI after processing input. - draw(s, data, manual) + draw(s, data, config) } } } }() - tick(s, data, manual, quit) + tick(s, data, config, quit) s.Fini() - log.Infof("Total cost: %s%.2f", data.currencySymbol, data.getCost()) + log.Infof("Total cost: %s%.2f", config.GetCurrencySymbol(), data.getCost()) } // data stores variables passed around between the various goRoutines. type Data struct { - mtx sync.Mutex - count int - cost float32 - input string - currencySymbol string + mtx sync.Mutex + count int + cost float32 + input string } // Get count. @@ -187,7 +181,7 @@ func (data *Data) setInput(input string) { } // tick configures the goroutine for the scheduled calculateCost update. -func tick(s tcell.Screen, data *Data, manual bool, quit <-chan struct{}) { +func tick(s tcell.Screen, data *Data, config *Config, quit <-chan struct{}) { t := time.NewTicker(refreshInterval) for { @@ -195,7 +189,7 @@ func tick(s tcell.Screen, data *Data, manual bool, quit <-chan struct{}) { case <-quit: return case <-t.C: - draw(s, data, manual) + draw(s, data, config) } } } @@ -218,19 +212,19 @@ func emitStr(s tcell.Screen, x, y int, style tcell.Style, str string) { } // draw renders the TUI. -func draw(s tcell.Screen, data *Data, manual bool) { +func draw(s tcell.Screen, data *Data, config *Config) { s.Clear() style := tcell.StyleDefault.Foreground(tcell.ColorCornflowerBlue) emitStr(s, 0, 0, style, "Clockwise") - costString := fmt.Sprintf("Total cost: %s%.2f", data.currencySymbol, data.getCost()) + costString := fmt.Sprintf("Total cost: %s%.2f", config.GetCurrencySymbol(), data.getCost()) emitStr(s, 0, 1, tcell.StyleDefault, costString) countString := fmt.Sprintf("Participant count: %s", strconv.Itoa((data.GetCount()))) emitStr(s, 0, 2, tcell.StyleDefault, countString) - if manual { + if config.GetManualMode() { faded := tcell.StyleDefault.Foreground(tcell.ColorDimGray) inputString := fmt.Sprintf("Input: %s", data.getInput()) emitStr(s, 0, 3, tcell.StyleDefault, inputString) @@ -244,7 +238,7 @@ func draw(s tcell.Screen, data *Data, manual bool) { } // writeCostFile outputs the cost that gets consumed by OBS. -func writeCostFile(data *Data) { +func writeCostFile(data *Data, config *Config) { home, err := os.UserHomeDir() if err != nil { log.Fatal(err) @@ -255,7 +249,7 @@ func writeCostFile(data *Data) { outputFile := outputFolder + "clockwise.txt" for { - costString := fmt.Sprintf("Total cost: %s%.2f\n", data.currencySymbol, data.getCost()) + costString := fmt.Sprintf("Total cost: %s%.2f\n", config.GetCurrencySymbol(), data.getCost()) if err := ioutil.WriteFile(outputFile, []byte(costString), 0600); err != nil { log.Fatal(err)