ArinDash/widgets/yearMood.go

328 lines
7.8 KiB
Go

package widgets
import (
"ArinDash/util"
"context"
"encoding/json"
"fmt"
"os"
"strconv"
"strings"
"time"
"github.com/mum4k/termdash/cell"
"github.com/mum4k/termdash/keyboard"
"github.com/mum4k/termdash/terminal/terminalapi"
"github.com/mum4k/termdash/widgetapi"
"github.com/mum4k/termdash/widgets/text"
)
const moodsFile = "moods.json"
type YearMoodOptions struct {
}
func YearMood() YearMoodOptions {
widgetOptions["YearMoodOptions"] = createYearMood
return YearMoodOptions{}
}
func createYearMood(ctx context.Context, _ terminalapi.Terminal, _ interface{}) widgetapi.Widget {
widget := util.PanicOnErrorWithResult(NewMoodText())
go util.Periodic(ctx, 1*time.Hour, func() error {
widget.selectedMonth = time.Now().Month()
widget.selectedDay = time.Now().Day()
return widget.drawTable()
})
return widget
}
func (widget *MoodText) drawTable() error {
days := moods()
widget.Reset()
if err := widget.Write(" "); err != nil {
return err
}
for m := range 12 {
mood := moodToColor(widget.moodMonth(m + 1))
if err := widget.Write(fmt.Sprintf("%2s", time.Month(m + 1).String()[0:3]), text.WriteCellOpts(cell.BgColor(mood), cell.FgColor(cell.ColorWhite))); err != nil {
return err
}
if m < 11 {
if err := widget.Write(" "); err != nil {
return err
}
} else {
if err := widget.Write("\n"); err != nil {
return err
}
}
}
for day := range 31 {
for month := range 12 {
date := time.Date(time.Now().Year(), time.Month(month+1), day+1, 0, 0, 0, 0, time.UTC)
if date.Month() != time.Month(month+1) {
if err := widget.Write(" "); err != nil {
return err
}
continue
}
cellOptions := make([]cell.Option, 0)
weekday := time.Date(time.Now().Year(), time.Month(month+1), day+1, 0, 0, 0, 0, time.UTC).Weekday()
if weekday == time.Saturday || weekday == time.Sunday {
cellOptions = append(cellOptions, cell.Dim())
}
cellOptions = append(cellOptions, cell.FgColor(cell.ColorWhite))
cellOptions = append(cellOptions, cell.Bold())
mood := days[strconv.Itoa(time.Now().Year())][strconv.Itoa(month+1)][strconv.Itoa(day+1)]
cellOptions = append(cellOptions, cell.BgColor(moodToColor(mood)))
if day+1 == widget.selectedDay && time.Month(month+1) == widget.selectedMonth {
if err := widget.Write(">", text.WriteCellOpts(append(cellOptions, cell.Blink())...)); err != nil {
return err
}
if err := widget.Write(fmt.Sprintf("%2d", day+1), text.WriteCellOpts(cellOptions...)); err != nil {
return err
}
} else {
if err := widget.Write(fmt.Sprintf(" %2d", day+1), text.WriteCellOpts(cellOptions...)); err != nil {
return err
}
}
if time.Now().Day() == day+1 && time.Now().Month() == time.Month(month+1) {
if err := widget.Write("<", text.WriteCellOpts(cellOptions...)); err != nil {
return err
}
} else {
if err := widget.Write(" ", text.WriteCellOpts(cellOptions...)); err != nil {
return err
}
}
if month+1 < 12 {
if err := widget.Write(" "); err != nil {
return err
}
}
}
if err := widget.Write("\n"); err != nil {
return err
}
}
if err := widget.Write("\n"); err != nil {
return err
}
for k, v := range totalMoodPercents() {
if v == 0 {
continue
}
amount := int(59 * v)
if amount == 0 {
amount = 1
}
if err := widget.Write(strings.Repeat("█", amount), text.WriteCellOpts(cell.BgColor(cell.ColorGray), cell.FgColor(moodToColor(k)))); err != nil {
return err
}
}
if err := widget.Write("\n\n"); err != nil {
return err
}
if err := widget.Write("<-=delete 1=idk(purple) 2=meh(yellow) 3=great(green) other=bad(red)", text.WriteCellOpts(cell.FgColor(cell.ColorGray))); err != nil {
return err
}
return nil
}
func moods() map[string]map[string]map[string]int {
moods := make(map[string]map[string]map[string]int)
file, err := os.ReadFile(moodsFile)
if err == nil {
if err := json.Unmarshal(file, &moods); err != nil {
panic(err)
}
} else {
initMoods, _ := json.Marshal(moods)
if err := os.WriteFile(moodsFile, initMoods, 0644); err != nil {
// ignore
}
}
return moods
}
func moodToColor(mood int) cell.Color {
switch mood {
case 1:
return cell.ColorPurple
case 2:
return cell.ColorYellow
case 3:
return cell.ColorGreen
case -1:
return cell.ColorRed
default:
return cell.ColorGray
}
}
func totalMoodPercents() map[int]float64 {
m := moods()
now := time.Now()
currentYear := strconv.Itoa(now.Year())
// Initialize percentages for all known moods
percents := map[int]float64{
1: 0.0, // idk
2: 0.0, // meh
3: 0.0, // great
-1: 0.0, // bad
0: 0.0, // no mood recorded
}
// Calculate total days in the current year
firstDay := time.Date(now.Year(), time.January, 1, 0, 0, 0, 0, time.UTC)
nextYear := time.Date(now.Year()+1, time.January, 1, 0, 0, 0, 0, time.UTC)
totalDaysInYear := int(nextYear.Sub(firstDay).Hours() / 24)
yearData, ok := m[currentYear]
if !ok {
percents[0] = 1.0
return percents
}
counts := make(map[int]int)
recordedDaysCount := 0
for _, monthData := range yearData {
for _, mood := range monthData {
if mood == 0 {
continue
}
counts[mood]++
recordedDaysCount++
}
}
// Any day that doesn't have a recorded mood (1, 2, 3, or -1) is counted as 0
counts[0] = totalDaysInYear - recordedDaysCount
for mood := range percents {
if count, exists := counts[mood]; exists {
percents[mood] = float64(count) / float64(totalDaysInYear)
}
}
return percents
}
func (widget *MoodText) moodMonth(month int) int {
days := moods()[strconv.Itoa(time.Now().Year())][strconv.Itoa(month)]
counts := make(map[int]int)
mostFrequentMood := 0
maxCount := 0
for _, mood := range days {
if mood == 0 {
continue
}
counts[mood]++
if counts[mood] > maxCount {
maxCount = counts[mood]
mostFrequentMood = mood
}
}
return mostFrequentMood
}
type MoodText struct {
*text.Text
selectedMonth time.Month
selectedDay int
}
func NewMoodText(opts ...text.Option) (*MoodText, error) {
t, err := text.New(opts...)
if err != nil {
return nil, err
}
return &MoodText{Text: t}, nil
}
func (widget *MoodText) Keyboard(k *terminalapi.Keyboard, _ *widgetapi.EventMeta) error {
switch k.Key {
case keyboard.KeyEsc, keyboard.KeyCtrlC:
break
case keyboard.KeyArrowUp:
if widget.selectedDay == 1 {
widget.selectedDay = 31
} else {
widget.selectedDay--
}
break
case keyboard.KeyArrowDown:
if widget.selectedDay == 31 {
widget.selectedDay = 1
} else {
widget.selectedDay++
}
break
case keyboard.KeyArrowLeft:
if widget.selectedMonth == 1 {
widget.selectedMonth = 12
} else {
widget.selectedMonth--
}
break
case keyboard.KeyArrowRight:
if widget.selectedMonth == 12 {
widget.selectedMonth = 1
} else {
widget.selectedMonth++
}
break
case keyboard.KeyBackspace, keyboard.KeyBackspace2:
widget.WriteMood(0)
break
case '1':
widget.WriteMood(1)
break
case '2':
widget.WriteMood(2)
break
case '3':
widget.WriteMood(3)
break
default:
widget.WriteMood(-1)
break
}
return widget.drawTable()
}
func (widget *MoodText) WriteMood(mood int) {
m := moods()
if m[strconv.Itoa(time.Now().Year())] == nil {
m[strconv.Itoa(time.Now().Year())] = make(map[string]map[string]int)
}
if m[strconv.Itoa(time.Now().Year())][strconv.Itoa(int(widget.selectedMonth))] == nil {
m[strconv.Itoa(time.Now().Year())][strconv.Itoa(int(widget.selectedMonth))] = make(map[string]int)
}
m[strconv.Itoa(time.Now().Year())][strconv.Itoa(int(widget.selectedMonth))][strconv.Itoa(widget.selectedDay)] = mood
newMoods, err := json.MarshalIndent(m, "", " ")
if err != nil {
widget.Write(fmt.Sprintf("Error: %s", err.Error()), text.WriteCellOpts(cell.FgColor(cell.ColorRed)))
}
if err := os.WriteFile(moodsFile, newMoods, 0644); err != nil {
widget.Write(fmt.Sprintf("Error: %s", err.Error()), text.WriteCellOpts(cell.FgColor(cell.ColorRed)))
// ignore
}
}