diff --git a/cmd/delete.go b/cmd/delete.go index 5fa7005..d5eba90 100644 --- a/cmd/delete.go +++ b/cmd/delete.go @@ -24,7 +24,7 @@ var deleteCmd = &cobra.Command{ if err != nil { return err } - entry, err := App.GetDiaryEntry(id) + entry, err := App.GetDiaryEntry(int64(id)) if err != nil { return err } @@ -45,7 +45,7 @@ var deleteCmd = &cobra.Command{ if err != nil { panic(err) } - _, err = App.DeleteDiaryEntry(id) + _, err = App.DeleteDiaryEntry(int64(id)) if err != nil { panic(err) } diff --git a/cmd/list.go b/cmd/list.go new file mode 100644 index 0000000..797cb2c --- /dev/null +++ b/cmd/list.go @@ -0,0 +1,41 @@ +/* +Copyright © 2021 NAME HERE + +*/ +package cmd + +import ( + "isthisnagee.com/tools/diary/model" + + "github.com/spf13/cobra" +) + +// listCmd represents the list command +var listCmd = &cobra.Command{ + Use: "list", + Short: "A brief description of your command", + Long: `A longer description that spans multiple lines and likely contains examples +and usage of using your command. For example: + +Cobra is a CLI library for Go that empowers applications. +This application is a tool to generate the needed files +to quickly create a Cobra application.`, + Run: func(cmd *cobra.Command, args []string) { + results := App.GetDiaryEntries(model.GetDiaryEntriesQuery{}) + PrintEntries(results) + }, +} + +func init() { + rootCmd.AddCommand(listCmd) + + // Here you will define your flags and configuration settings. + + // Cobra supports Persistent Flags which will work for this command + // and all subcommands, e.g.: + // listCmd.PersistentFlags().String("foo", "", "A help for foo") + + // Cobra supports local flags which will only run when this command + // is called directly, e.g.: + // listCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") +} diff --git a/cmd/today.go b/cmd/today.go new file mode 100644 index 0000000..e3ce78c --- /dev/null +++ b/cmd/today.go @@ -0,0 +1,64 @@ +/* +Copyright © 2021 NAME HERE + +*/ +package cmd + +import ( + "time" + + "github.com/spf13/cobra" + "isthisnagee.com/tools/diary/model" +) + +// todayCmd represents the today command +var todayCmd = &cobra.Command{ + Use: "today", + Short: "A brief description of your command", + Long: `A longer description that spans multiple lines and likely contains examples +and usage of using your command. For example: + +Cobra is a CLI library for Go that empowers applications. +This application is a tool to generate the needed files +to quickly create a Cobra application.`, + Run: func(cmd *cobra.Command, args []string) { + var created_after_ts *int64 = new(int64) + var created_before_ts *int64 = new(int64) + + var now = time.Now() + var startOfToday = time.Date( + now.Year(), + now.Month(), + now.Day(), + 0, + 0, + 0, + 0, + now.Location(), + ) + *created_after_ts = startOfToday.Unix() + var endOfToday = startOfToday.AddDate(0, 0, 1) + *created_before_ts = endOfToday.Unix() + + results := App.GetDiaryEntries(model.GetDiaryEntriesQuery{ + CreatedBeforeTs: created_before_ts, + CreatedAfterTs: created_after_ts, + }) + PrintEntries(results) + + }, +} + +func init() { + listCmd.AddCommand(todayCmd) + + // Here you will define your flags and configuration settings. + + // Cobra supports Persistent Flags which will work for this command + // and all subcommands, e.g.: + // todayCmd.PersistentFlags().String("foo", "", "A help for foo") + + // Cobra supports local flags which will only run when this command + // is called directly, e.g.: + // todayCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") +} diff --git a/cmd/util.go b/cmd/util.go index cd856e3..4bd4348 100644 --- a/cmd/util.go +++ b/cmd/util.go @@ -2,10 +2,14 @@ package cmd import ( "errors" + "fmt" + "github.com/fatih/color" "isthisnagee.com/tools/diary/model" "log" "os" "path" + "strconv" + "time" ) var App *model.App @@ -32,3 +36,26 @@ func InitApp() { var app = model.NewApp(db_path) App = &app } + +var fmt_str = "%-10s %-20s %s\n" + +func PrintEntryHeader() { + fmt.Printf( + fmt_str+"\n", "Id", "Created time", "Title", + ) +} + +func PrintEntry(entry *model.DiaryEntry) { + created_time := time.Unix(entry.CreatedAt, 0).Format("2006/01/02 03:04pm") + titleColor := color.New(color.FgCyan).Add(color.Underline).SprintFunc() + fmt.Printf( + fmt_str, strconv.FormatInt(entry.Id, 10), created_time, titleColor(entry.Title), + ) +} + +func PrintEntries(entries []*model.DiaryEntry) { + PrintEntryHeader() + for _, entry := range entries { + PrintEntry(entry) + } +} diff --git a/db/db.go b/db/db.go index 302b868..8f6b47c 100644 --- a/db/db.go +++ b/db/db.go @@ -26,15 +26,15 @@ func initVersion(tx *sql.Tx) (int, int, error) { return 0, 0, fmt.Errorf("Could not select user_version. %w", err) } - if version == 0 { + if version < __version { _, err := tx.Exec(fmt.Sprintf("PRAGMA user_version=%d", __version)) if err != nil { tx.Rollback() return 0, 0, fmt.Errorf("Could not update pragma version. %w", err) } // Start from 1 so that all migrations are applied - return 0, __version, nil - } else if version != __version { + return version + 1, __version, nil + } else if version > __version { tx.Rollback() return 0, 0, errors.New(fmt.Sprintf("Wrong version. Expected %d got %d", __version, version)) } @@ -75,6 +75,10 @@ func migration1(tx *sql.Tx) error { } func initMigrations(tx *sql.Tx, start_from_version int) error { + if start_from_version == __version { + return nil + } + for migration_idx, migration := range migrations { // Version is 1 indexed, while the migration_idx is 0 indexed var migration_num = migration_idx + 1 diff --git a/go.mod b/go.mod index 4158e90..e0be2c4 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,12 @@ module isthisnagee.com/tools/diary go 1.17 require ( + github.com/fatih/color v1.13.0 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/mattn/go-colorable v0.1.12 // indirect + github.com/mattn/go-isatty v0.0.14 // indirect github.com/mattn/go-sqlite3 v1.14.9 // indirect github.com/spf13/cobra v1.3.0 // indirect github.com/spf13/pflag v1.0.5 // indirect + golang.org/x/sys v0.0.0-20211205182925-97ca703d548d // indirect ) diff --git a/go.sum b/go.sum index e2707ea..aaf76d9 100644 --- a/go.sum +++ b/go.sum @@ -103,6 +103,7 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7 github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E9/baC+qXE/TeeyBRzgJDws= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= +github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= @@ -246,12 +247,14 @@ github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaO github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-sqlite3 v1.14.9 h1:10HX2Td0ocZpYEjhilsuo6WWtUqttj2Kb0KtD86/KYA= github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= @@ -518,6 +521,7 @@ golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211205182925-97ca703d548d h1:FjkYO/PPp4Wi0EAUOVLxePm7qVW4r4ctbWpURyuOD0E= golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/model/entry.go b/model/entry.go index 57cb42d..3f9ea42 100644 --- a/model/entry.go +++ b/model/entry.go @@ -9,10 +9,10 @@ import ( ) type DiaryEntry struct { - Id int `json:"id"` + Id int64 `json:"id"` Title string `json:"title"` - CreatedAt int `json:"created_at"` - Version int `json:"version"` + CreatedAt int64 `json:"created_at"` + Version int64 `json:"version"` } type App struct { @@ -51,7 +51,7 @@ func (app *App) NewDiaryEntry(title string) *DiaryEntry { return &diary_entry } -func (app *App) GetDiaryEntry(id int) (*DiaryEntry, error) { +func (app *App) GetDiaryEntry(id int64) (*DiaryEntry, error) { var diary_entry DiaryEntry if err := app.Db.QueryRow( @@ -66,7 +66,7 @@ func (app *App) GetDiaryEntry(id int) (*DiaryEntry, error) { return &diary_entry, nil } -func (app *App) DeleteDiaryEntry(id int) (bool, error) { +func (app *App) DeleteDiaryEntry(id int64) (bool, error) { result, err := app.Db.Exec("DELETE FROM diary_log where id=?", id) if err != nil { return false, err @@ -81,3 +81,53 @@ func (app *App) DeleteDiaryEntry(id int) (bool, error) { } return true, nil } + +type GetDiaryEntriesQuery struct { + /// Exclusive + CreatedBeforeTs *int64 + /// Inclusive + CreatedAfterTs *int64 +} + +func (app *App) GetDiaryEntries(q GetDiaryEntriesQuery) []*DiaryEntry { + var query = "SELECT id, title, created_at, version FROM diary_log" + + var whereParams []interface{} + if q.CreatedBeforeTs != nil { + query += " where created_at < ?" + whereParams = append(whereParams, *q.CreatedBeforeTs) + } + + if q.CreatedAfterTs != nil { + query += " and created_at >= ?" + whereParams = append(whereParams, *q.CreatedAfterTs) + } + query += " ORDER BY created_at desc, id desc" + + rows, err := app.Db.Query( + query, + whereParams..., + ) + if err != nil { + panic(err) + } + defer rows.Close() + + var result []*DiaryEntry + for rows.Next() { + var id, created_at, version int64 + var title string + err := rows.Scan(&id, &title, &created_at, &version) + if err != nil { + panic(err) + } + result = append(result, &DiaryEntry{ + Id: id, Title: title, CreatedAt: created_at, Version: version, + }) + } + if err := rows.Err(); err != nil { + panic(err) + } + + return result +} diff --git a/model/entry_test.go b/model/entry_test.go index 2f05baf..116e30d 100644 --- a/model/entry_test.go +++ b/model/entry_test.go @@ -11,7 +11,7 @@ func assert_string(t *testing.T, expected string, actual string) { } } -func assert_int(t *testing.T, expected int, actual int) { +func assert_int(t *testing.T, expected int64, actual int64) { if actual != expected { t.Fatalf("(%v, %v)", expected, actual) } diff --git a/model/errors.go b/model/errors.go index 98473ed..bec27d4 100644 --- a/model/errors.go +++ b/model/errors.go @@ -3,7 +3,7 @@ package model import "fmt" type NotFoundError struct { - given_id int + given_id int64 } func (e *NotFoundError) Error() string { diff --git a/model/note.go b/model/note.go index 7f0e009..cd3bd21 100644 --- a/model/note.go +++ b/model/note.go @@ -1,14 +1,14 @@ package model type DiaryEntryNote struct { - Id int `json:"id"` + Id int64 `json:"id"` Body string `json:"body"` - CreatedAt int `json:"created_at"` - Version int `json:"version"` - LogId int `json:"log_id"` + CreatedAt int64 `json:"created_at"` + Version int64 `json:"version"` + LogId int64 `json:"log_id"` } -func (app *App) NewDiaryEntryNote(entry_id int, body string) *DiaryEntryNote { +func (app *App) NewDiaryEntryNote(entry_id int64, body string) *DiaryEntryNote { var diary_entry_note DiaryEntryNote var result, err = app.Db.Exec("INSERT INTO diary_log_note (body, log_id) values (?, ?);", body, entry_id) @@ -37,7 +37,7 @@ func (app *App) NewDiaryEntryNote(entry_id int, body string) *DiaryEntryNote { return &diary_entry_note } -func (app *App) GetDiaryEntryNotes(entry_id int) []*DiaryEntryNote { +func (app *App) GetDiaryEntryNotes(entry_id int64) []*DiaryEntryNote { // not sure if it should be descending. rows, err := app.Db.Query( // The second ordering is necessary to break ties. @@ -53,7 +53,7 @@ func (app *App) GetDiaryEntryNotes(entry_id int) []*DiaryEntryNote { var result []*DiaryEntryNote for rows.Next() { - var id, created_at, version, log_id int + var id, created_at, version, log_id int64 var body string err := rows.Scan(&id, &body, &created_at, &version, &log_id) if err != nil { @@ -70,7 +70,7 @@ func (app *App) GetDiaryEntryNotes(entry_id int) []*DiaryEntryNote { return result } -func (app *App) DeleteDiaryEntryNote(note_id int) (bool, error) { +func (app *App) DeleteDiaryEntryNote(note_id int64) (bool, error) { result, err := app.Db.Exec("DELETE FROM diary_log_note where id=?", note_id) if err != nil { return false, err