<commit_before>package tasks

import (
	"errors"
	"fmt"
	"github.com/asdine/storm"
	"github.com/asdine/storm/q"
	"github.com/deckarep/gosx-notifier"
	"github.com/fatih/color"
	"github.com/olekukonko/tablewriter"
	"os"
	"strconv"
	"strings"
	"time"
)

var (
	errDuplicateTimerForTask = errors.New("task already has a running timer")
	ticker                   <-chan time.Time
	DB                       *storm.DB
)

type task struct {
	ID       int    `storm:"id,increment"`
	Name     string `storm:"unique"`
	Status   string
	Priority int
}

func (t *task) updateFieldValuePairs(fieldValuePairs []string) {
	for _, pair := range fieldValuePairs {
		arr := strings.Split(pair, ":")
		field, value := arr[0], arr[1]
		if field == "status" {
			value = strings.ToUpper(value)
		}
		t.updateField(field, value)
	}
}

func (t *task) updateField(field, value string) {
	switch field {
	case "name", "n":
		DB.Update(&task{ID: t.ID, Name: value})
	case "status", "st", "s":
		DB.Update(&task{ID: t.ID, Status: strings.ToUpper(value)})
	case "priority", "pri", "p":
		priority, err := strconv.Atoi(value)
		if err != nil {
			panic(err)
		}
		DB.Update(&task{ID: t.ID, Priority: priority})
	}
}

type taskTimer struct {
	ID        int `storm:"id,increment"`
	TaskID    int
	StartedAt time.Time
	ExpiresAt time.Time
	Message   string
	Fired     bool
}

func (tt *taskTimer) trigger() {
	tt.pushNotification()
	err := DB.Update(&taskTimer{ID: tt.ID, Fired: true})
	if err != nil {
		panic(err)
	}
}

func (tt *taskTimer) isExpired() bool {
	return tt.ExpiresAt.Before(time.Now())
}

func (tt *taskTimer) ellapsedTime() time.Duration {
	var finishedAt time.Time
	if tt.unfinished() {
		finishedAt = time.Now()
	} else {
		finishedAt = tt.ExpiresAt
	}
	return finishedAt.Sub(tt.StartedAt)
}

func (timer *taskTimer) unfinished() bool {
	return !timer.Fired
}

func init() {
	d, err := storm.Open("selfcontrol.db")
	DB = d
	if err != nil {
		panic(err)
	}
	startTimersLoop()
}

func startTimersLoop() {
	ticker = time.Tick(1 * time.Second)
	go func() {
		for _ = range ticker {
			var taskTimers []taskTimer
			_ = DB.Find("Fired", false, &taskTimers)
			for _, tt := range taskTimers {
				if tt.isExpired() {
					tt.trigger()
				}
			}
		}
	}()
}

// Add a new task given its name
func Add(name string, fieldValuePairs []string) {
	t := task{
		Name:   name,
		Status: "TODO",
	}
	err := DB.Save(&t)
	if err != nil {
		panic(err)
	}
	t.updateFieldValuePairs(fieldValuePairs)
}

// Print all tasks in a ASCII table
func Print() {
	table := tablewriter.NewWriter(os.Stdout)
	table.SetHeader([]string{"ID", "name", "status", "priority", "total time"})
	table.SetAutoFormatHeaders(false)
	for _, row := range createRows() {
		table.Append(row)
	}
	table.Render()
}

// Delete a task with given id
func Delete(ID int) {
	var t task
	err := DB.One("ID", ID, &t)
	if err != nil {
		panic(err)
	}<commit_msg>Refator<commit_after>package tasks

import (
	"errors"
	"fmt"
	"github.com/asdine/storm"
	"github.com/asdine/storm/q"
	"github.com/deckarep/gosx-notifier"
	"github.com/fatih/color"
	"github.com/olekukonko/tablewriter"
	"os"
	"strconv"
	"strings"
	"time"
)

var (
	errDuplicateTimerForTask = errors.New("task already has a running timer")
	ticker                   <-chan time.Time
	DB                       *storm.DB
)

type task struct {
	ID       int    `storm:"id,increment"`
	Name     string `storm:"unique"`
	Status   string
	Priority int
}

func (t *task) updateFieldValuePairs(fieldValuePairs []string) {
	for _, pair := range fieldValuePairs {
		arr := strings.Split(pair, ":")
		field, value := arr[0], arr[1]
		if field == "status" {
			value = strings.ToUpper(value)
		}
		t.updateField(field, value)
	}
}

func (t *task) updateField(field, value string) {
	switch field {
	case "name", "n":
		DB.Update(&task{ID: t.ID, Name: value})
	case "status", "st", "s":
		DB.Update(&task{ID: t.ID, Status: strings.ToUpper(value)})
	case "priority", "pri", "p":
		priority, err := strconv.Atoi(value)
		if err != nil {
			panic(err)
		}
		DB.Update(&task{ID: t.ID, Priority: priority})
	}
}

type taskTimer struct {
	ID        int `storm:"id,increment"`
	TaskID    int
	StartedAt time.Time
	ExpiresAt time.Time
	Message   string
	Fired     bool
}

func (tt *taskTimer) trigger() {
	tt.pushNotification()
	err := DB.Update(&taskTimer{ID: tt.ID, Fired: true})
	if err != nil {
		panic(err)
	}
}

func (tt *taskTimer) isExpired() bool {
	return tt.ExpiresAt.Before(time.Now())
}

func (tt *taskTimer) ellapsedTime() time.Duration {
	var finishedAt time.Time
	if !tt.Fired {
		finishedAt = time.Now()
	} else {
		finishedAt = tt.ExpiresAt
	}
	return finishedAt.Sub(tt.StartedAt)
}

func init() {
	d, err := storm.Open("selfcontrol.db")
	DB = d
	if err != nil {
		panic(err)
	}
	startTimersLoop()
}

func startTimersLoop() {
	ticker = time.Tick(1 * time.Second)
	go func() {
		for _ = range ticker {
			var taskTimers []taskTimer
			_ = DB.Find("Fired", false, &taskTimers)
			for _, tt := range taskTimers {
				if tt.isExpired() {
					tt.trigger()
				}
			}
		}
	}()
}

// Add a new task given its name
func Add(name string, fieldValuePairs []string) {
	t := task{
		Name:   name,
		Status: "TODO",
	}
	err := DB.Save(&t)
	if err != nil {
		panic(err)
	}
	t.updateFieldValuePairs(fieldValuePairs)
}

// Print all tasks in a ASCII table
func Print() {
	table := tablewriter.NewWriter(os.Stdout)
	table.SetHeader([]string{"ID", "Name", "Status", "Priority", "Total time"})
	table.SetAutoFormatHeaders(false)
	for _, row := range createRows() {
		table.Append(row)
	}
	table.Render()
}

// Delete a task with given id
func Delete(ID int) {
	var t task
	err := DB.One("ID", ID, &t)
	if err != nil {
		panic(err)
	}
{'fraction_non_alphanumeric': 0.156140350877193, 'fraction_numerical': 0.0010526315789473684, 'mean_word_length': 3.3618974751338944, 'pattern_counts': {'":': 8, '<': 5, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7269985', 'n_tokens_mistral': 2285, 'n_tokens_neox': 2175, 'n_words': 523}