2018-01-16 10:50:08 +01:00
|
|
|
package main
|
|
|
|
|
2018-01-16 12:06:20 +01:00
|
|
|
import (
|
|
|
|
"database/sql"
|
2018-01-20 11:01:53 +01:00
|
|
|
"strings"
|
2018-01-16 13:02:35 +01:00
|
|
|
"time"
|
2018-07-12 21:03:53 +02:00
|
|
|
|
|
|
|
_ "github.com/mattn/go-sqlite3"
|
2018-01-16 12:06:20 +01:00
|
|
|
)
|
|
|
|
|
2018-01-16 13:02:35 +01:00
|
|
|
// 2018-01-16 19:05:21.752851759+08:00
|
|
|
|
const datetimeFmt = "2006-01-02 15:04:05.999999999-07:00"
|
|
|
|
|
2019-01-25 06:04:41 +01:00
|
|
|
type StoreFunc func(tx *sql.Tx) error
|
|
|
|
|
2018-01-16 12:06:20 +01:00
|
|
|
type Store struct {
|
|
|
|
db *sql.DB
|
|
|
|
}
|
2018-01-16 10:50:08 +01:00
|
|
|
|
|
|
|
func NewStore(path string) (*Store, error) {
|
2019-07-05 00:57:41 +02:00
|
|
|
db, err := sql.Open("sqlite3", path)
|
2018-01-16 12:06:20 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return &Store{db: db}, nil
|
2018-01-16 10:50:08 +01:00
|
|
|
}
|
|
|
|
|
2019-01-25 06:04:41 +01:00
|
|
|
// With applies all of the given functions with
|
|
|
|
// a single transaction, rolling back on failure
|
|
|
|
// and commiting on success.
|
|
|
|
func (s Store) With(fns ...func(tx *sql.Tx) error) error {
|
2018-01-16 12:06:20 +01:00
|
|
|
tx, err := s.db.Begin()
|
|
|
|
if err != nil {
|
2019-01-25 06:04:41 +01:00
|
|
|
return err
|
2018-01-16 12:06:20 +01:00
|
|
|
}
|
2019-01-25 06:04:41 +01:00
|
|
|
for _, fn := range fns {
|
|
|
|
err = fn(tx)
|
|
|
|
if err != nil {
|
|
|
|
tx.Rollback()
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return tx.Commit()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s Store) CreateTask(tx *sql.Tx, task Task) (int, error) {
|
|
|
|
var taskID int
|
|
|
|
_, err := tx.Exec(
|
2018-01-20 19:54:30 +01:00
|
|
|
"INSERT INTO task (message,pomodoros,duration,tags) VALUES ($1,$2,$3,$4)",
|
|
|
|
task.Message, task.NPomodoros, task.Duration.String(), strings.Join(task.Tags, ","))
|
2018-01-16 12:06:20 +01:00
|
|
|
if err != nil {
|
|
|
|
return -1, err
|
|
|
|
}
|
|
|
|
err = tx.QueryRow("SELECT last_insert_rowid() FROM task").Scan(&taskID)
|
|
|
|
if err != nil {
|
|
|
|
return -1, err
|
|
|
|
}
|
2019-01-25 06:04:41 +01:00
|
|
|
err = tx.QueryRow("SELECT last_insert_rowid() FROM task").Scan(&taskID)
|
|
|
|
if err != nil {
|
|
|
|
return -1, err
|
|
|
|
}
|
|
|
|
return taskID, nil
|
2018-01-16 12:06:20 +01:00
|
|
|
}
|
|
|
|
|
2019-01-25 06:04:41 +01:00
|
|
|
func (s Store) ReadTasks(tx *sql.Tx) ([]*Task, error) {
|
|
|
|
rows, err := tx.Query(`SELECT rowid,message,pomodoros,duration,tags FROM task`)
|
2018-01-16 13:02:35 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
tasks := []*Task{}
|
|
|
|
for rows.Next() {
|
2018-01-20 19:54:30 +01:00
|
|
|
var (
|
|
|
|
tags string
|
|
|
|
strDuration string
|
|
|
|
)
|
2018-01-20 13:17:58 +01:00
|
|
|
task := &Task{Pomodoros: []*Pomodoro{}}
|
2018-01-20 19:54:30 +01:00
|
|
|
err = rows.Scan(&task.ID, &task.Message, &task.NPomodoros, &strDuration, &tags)
|
2018-01-16 13:02:35 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2018-01-20 19:54:30 +01:00
|
|
|
duration, _ := time.ParseDuration(strDuration)
|
|
|
|
task.Duration = duration
|
2018-01-20 13:19:14 +01:00
|
|
|
if tags != "" {
|
|
|
|
task.Tags = strings.Split(tags, ",")
|
|
|
|
}
|
2019-01-25 06:04:41 +01:00
|
|
|
pomodoros, err := s.ReadPomodoros(tx, task.ID)
|
2018-01-16 13:02:35 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2018-01-20 13:17:58 +01:00
|
|
|
for _, pomodoro := range pomodoros {
|
|
|
|
task.Pomodoros = append(task.Pomodoros, pomodoro)
|
2018-01-16 13:02:35 +01:00
|
|
|
}
|
|
|
|
tasks = append(tasks, task)
|
|
|
|
}
|
|
|
|
return tasks, nil
|
|
|
|
}
|
|
|
|
|
2019-01-25 06:04:41 +01:00
|
|
|
func (s Store) DeleteTask(tx *sql.Tx, taskID int) error {
|
|
|
|
_, err := tx.Exec("DELETE FROM task WHERE rowid = $1", &taskID)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
_, err = tx.Exec("DELETE FROM pomodoro WHERE task_id = $1", &taskID)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s Store) ReadTask(tx *sql.Tx, taskID int) (*Task, error) {
|
|
|
|
task := &Task{}
|
|
|
|
var (
|
|
|
|
tags string
|
|
|
|
strDuration string
|
|
|
|
)
|
|
|
|
err := tx.QueryRow(`SELECT rowid,message,pomodoros,duration,tags FROM task WHERE rowid = $1`, &taskID).
|
|
|
|
Scan(&task.ID, &task.Message, &task.NPomodoros, &strDuration, &tags)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
duration, _ := time.ParseDuration(strDuration)
|
|
|
|
task.Duration = duration
|
|
|
|
if tags != "" {
|
|
|
|
task.Tags = strings.Split(tags, ",")
|
|
|
|
}
|
|
|
|
return task, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s Store) CreatePomodoro(tx *sql.Tx, taskID int, pomodoro Pomodoro) error {
|
|
|
|
_, err := tx.Exec(
|
|
|
|
`INSERT INTO pomodoro (task_id, start, end) VALUES ($1, $2, $3)`,
|
|
|
|
taskID,
|
|
|
|
pomodoro.Start,
|
|
|
|
pomodoro.End,
|
|
|
|
)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s Store) ReadPomodoros(tx *sql.Tx, taskID int) ([]*Pomodoro, error) {
|
|
|
|
rows, err := tx.Query(`SELECT start,end FROM pomodoro WHERE task_id = $1`, &taskID)
|
2018-01-16 13:02:35 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2018-01-20 13:17:58 +01:00
|
|
|
pomodoros := []*Pomodoro{}
|
2018-01-16 13:02:35 +01:00
|
|
|
for rows.Next() {
|
|
|
|
var (
|
|
|
|
startStr string
|
|
|
|
endStr string
|
|
|
|
)
|
2018-01-20 13:17:58 +01:00
|
|
|
pomodoro := &Pomodoro{}
|
2018-01-16 13:02:35 +01:00
|
|
|
err = rows.Scan(&startStr, &endStr)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
start, _ := time.Parse(datetimeFmt, startStr)
|
|
|
|
end, _ := time.Parse(datetimeFmt, endStr)
|
2018-01-20 13:17:58 +01:00
|
|
|
pomodoro.Start = start
|
|
|
|
pomodoro.End = end
|
|
|
|
pomodoros = append(pomodoros, pomodoro)
|
2018-01-16 13:02:35 +01:00
|
|
|
}
|
2018-01-20 13:17:58 +01:00
|
|
|
return pomodoros, nil
|
2018-01-16 13:02:35 +01:00
|
|
|
}
|
|
|
|
|
2019-01-25 06:04:41 +01:00
|
|
|
func (s Store) DeletePomodoros(tx *sql.Tx, taskID int) error {
|
|
|
|
_, err := tx.Exec("DELETE FROM pomodoro WHERE task_id = $1", &taskID)
|
|
|
|
return err
|
2018-01-17 08:53:45 +01:00
|
|
|
}
|
|
|
|
|
2018-01-16 12:06:20 +01:00
|
|
|
func (s Store) Close() error { return s.db.Close() }
|
|
|
|
|
|
|
|
func initDB(db *Store) error {
|
|
|
|
stmt := `
|
|
|
|
CREATE TABLE task (
|
2018-01-20 11:01:53 +01:00
|
|
|
message TEXT,
|
2018-01-20 19:54:30 +01:00
|
|
|
pomodoros INTEGER,
|
|
|
|
duration TEXT,
|
2018-01-20 11:01:53 +01:00
|
|
|
tags TEXT
|
2018-01-16 12:06:20 +01:00
|
|
|
);
|
2018-01-20 13:17:58 +01:00
|
|
|
CREATE TABLE pomodoro (
|
2018-01-16 12:06:20 +01:00
|
|
|
task_id INTEGER,
|
|
|
|
start DATETTIME,
|
|
|
|
end DATETTIME
|
|
|
|
);
|
|
|
|
`
|
|
|
|
_, err := db.db.Exec(stmt)
|
|
|
|
return err
|
|
|
|
}
|