From 9d536eea4224678a699b49d0fb9da9dc547ffdd8 Mon Sep 17 00:00:00 2001 From: Omar Tulashvili Date: Sat, 17 Jan 2026 18:54:37 +0300 Subject: [PATCH 01/12] DI move from main to app.go --- .gitignore | 2 +- app.go | 39 +++++++++++++++++++++++++++++++++++++++ cmd/main.go | 34 ++++------------------------------ go.mod | 6 ++++++ go.sum | 11 +++++++++++ 5 files changed, 61 insertions(+), 31 deletions(-) create mode 100644 app.go diff --git a/.gitignore b/.gitignore index f053e23..f3985fc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ *.json -*.db +data/ diff --git a/app.go b/app.go new file mode 100644 index 0000000..f353720 --- /dev/null +++ b/app.go @@ -0,0 +1,39 @@ +package app + +import ( + "fmt" + "log" + + "github.com/tulashvili/MyDailyControlQuestions/internal/service" + "github.com/tulashvili/MyDailyControlQuestions/internal/storage" + "github.com/tulashvili/MyDailyControlQuestions/internal/ui" +) + +const ( + sqliteStoragePath = "data/sqlite3.db" +) + +func RunApp() { + questions := service.GetQuestions() + answers := ui.AskQuestion(questions) + + conn, err := storage.InitDB(sqliteStoragePath) + if err != nil { + log.Fatal(err) + } + + if err := storage.CreateTable(conn); err != nil { + log.Fatal(err) + } + + for _, answer := range answers { + if err := storage.InsertRow(conn, answer); err != nil { + log.Fatal(err) + } + } + fmt.Println("✅ Данные успешно добавлены в таблицу daily_log") // change to log? + + if err := ui.ShowDataOverPeriod(conn); err != nil { + log.Panic(err) + } +} \ No newline at end of file diff --git a/cmd/main.go b/cmd/main.go index 1f596a4..2041ad6 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -1,38 +1,12 @@ package main import ( - "fmt" - "log" + "time" - "github.com/tulashvili/MyDailyControlQuestions/internal/storage" - "github.com/tulashvili/MyDailyControlQuestions/internal/ui" -) - -const ( - sqliteStoragePath = "data/sqlite3.db" + app "github.com/tulashvili/MyDailyControlQuestions" ) func main() { - // questions := service.GetQuestions() - // answers := ui.AskQuestion(questions) - - conn, err := storage.InitDB(sqliteStoragePath) - if err != nil { - log.Fatal(err) - } - - if err := storage.CreateTable(conn); err != nil { - log.Fatal(err) - } - - // for _, answer := range answers { - // if err := storage.InsertRow(conn, answer); err != nil { - // log.Fatal(err) - // } - // } - fmt.Println("✅ Данные успешно добавлены в таблицу daily_log") // change to log? - - if err := ui.ShowDataOverPeriod(conn); err != nil { - log.Panic(err) - } + time.Local = time.UTC + app.RunApp() } diff --git a/go.mod b/go.mod index 56fcb6b..6be0614 100644 --- a/go.mod +++ b/go.mod @@ -3,3 +3,9 @@ module github.com/tulashvili/MyDailyControlQuestions go 1.25.4 require github.com/mattn/go-sqlite3 v1.14.33 + +require ( + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/spf13/cobra v1.10.2 // indirect + github.com/spf13/pflag v1.0.10 // indirect +) diff --git a/go.sum b/go.sum index 3de9741..ab4425b 100644 --- a/go.sum +++ b/go.sum @@ -1,2 +1,13 @@ +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/mattn/go-sqlite3 v1.14.33 h1:A5blZ5ulQo2AtayQ9/limgHEkFreKj1Dv226a1K73s0= github.com/mattn/go-sqlite3 v1.14.33/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= +github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= +github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From e161b6755a2f13f9ac2892e8f47ccef6a175fc0d Mon Sep 17 00:00:00 2001 From: Omar Tulashvili Date: Sat, 17 Jan 2026 19:27:31 +0300 Subject: [PATCH 02/12] create config.go and datasource.go, change app.go --- cmd/main.go | 2 +- app.go => internal/app.go | 21 ++++++++++++++++----- internal/config/config.go | 12 ++++++++++++ internal/{storage => repo}/repo.go | 2 +- internal/ui/ui.go | 6 +++--- pkg/db/datasource.go | 5 +++++ {internal/storage => pkg/db}/db.go | 2 +- 7 files changed, 39 insertions(+), 11 deletions(-) rename app.go => internal/app.go (58%) create mode 100644 internal/config/config.go rename internal/{storage => repo}/repo.go (98%) create mode 100644 pkg/db/datasource.go rename {internal/storage => pkg/db}/db.go (97%) diff --git a/cmd/main.go b/cmd/main.go index 2041ad6..bbf4ff6 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -3,7 +3,7 @@ package main import ( "time" - app "github.com/tulashvili/MyDailyControlQuestions" + app "github.com/tulashvili/MyDailyControlQuestions/internal" ) func main() { diff --git a/app.go b/internal/app.go similarity index 58% rename from app.go rename to internal/app.go index f353720..d4b49a7 100644 --- a/app.go +++ b/internal/app.go @@ -1,33 +1,44 @@ package app import ( + "database/sql" "fmt" "log" + "github.com/tulashvili/MyDailyControlQuestions/internal/config" + repository "github.com/tulashvili/MyDailyControlQuestions/internal/repo" "github.com/tulashvili/MyDailyControlQuestions/internal/service" - "github.com/tulashvili/MyDailyControlQuestions/internal/storage" "github.com/tulashvili/MyDailyControlQuestions/internal/ui" + + "github.com/tulashvili/MyDailyControlQuestions/pkg/db" ) const ( sqliteStoragePath = "data/sqlite3.db" ) +type App struct { + DB *sql.DB + Config config.Config +} + func RunApp() { + questions := service.GetQuestions() answers := ui.AskQuestion(questions) - conn, err := storage.InitDB(sqliteStoragePath) + // DB + conn, err := db.InitDB(sqliteStoragePath) if err != nil { log.Fatal(err) } - if err := storage.CreateTable(conn); err != nil { + if err := db.CreateTable(conn); err != nil { log.Fatal(err) } for _, answer := range answers { - if err := storage.InsertRow(conn, answer); err != nil { + if err := repository.InsertRow(conn, answer); err != nil { log.Fatal(err) } } @@ -36,4 +47,4 @@ func RunApp() { if err := ui.ShowDataOverPeriod(conn); err != nil { log.Panic(err) } -} \ No newline at end of file +} diff --git a/internal/config/config.go b/internal/config/config.go new file mode 100644 index 0000000..3a25b56 --- /dev/null +++ b/internal/config/config.go @@ -0,0 +1,12 @@ +package config + +import "github.com/tulashvili/MyDailyControlQuestions/pkg/db" + +type Config struct { + DS db.Datasource +} + +func NewConfig() (Config, error) { + cfg := Config{} + return cfg, nil +} diff --git a/internal/storage/repo.go b/internal/repo/repo.go similarity index 98% rename from internal/storage/repo.go rename to internal/repo/repo.go index bc65dea..b155ea9 100644 --- a/internal/storage/repo.go +++ b/internal/repo/repo.go @@ -1,4 +1,4 @@ -package storage +package repository import ( "database/sql" diff --git a/internal/ui/ui.go b/internal/ui/ui.go index 5c5e3ef..cd3584e 100644 --- a/internal/ui/ui.go +++ b/internal/ui/ui.go @@ -6,7 +6,7 @@ import ( "github.com/tulashvili/MyDailyControlQuestions/internal/models" "github.com/tulashvili/MyDailyControlQuestions/internal/service" - "github.com/tulashvili/MyDailyControlQuestions/internal/storage" + "github.com/tulashvili/MyDailyControlQuestions/internal/repo" ) // Get question list with category @@ -44,7 +44,7 @@ func AskQuestion(questions []models.Question) []models.UserAnswer { // Show data over period func ShowDataOverPeriod(conn *sql.DB) error { period := 2 - rows, err := storage.SelectRows(conn, period) + rows, err := repository.SelectRows(conn, period) if err != nil { return err } @@ -56,7 +56,7 @@ func ShowDataOverPeriod(conn *sql.DB) error { } // Formated result from SelectRow -func formatedPrintResult(userAnswer storage.UserAnswerRow) { +func formatedPrintResult(userAnswer repository.UserAnswerRow) { fmt.Println("---------------------") fmt.Println("ID:", userAnswer.ID) fmt.Println("Дата:", userAnswer.AnsweredAt) diff --git a/pkg/db/datasource.go b/pkg/db/datasource.go new file mode 100644 index 0000000..92f6aab --- /dev/null +++ b/pkg/db/datasource.go @@ -0,0 +1,5 @@ +package db + +type Datasource struct { + sqliteInitPath string +} \ No newline at end of file diff --git a/internal/storage/db.go b/pkg/db/db.go similarity index 97% rename from internal/storage/db.go rename to pkg/db/db.go index def3666..a9d3f68 100644 --- a/internal/storage/db.go +++ b/pkg/db/db.go @@ -1,4 +1,4 @@ -package storage +package db import ( "database/sql" From 171f76553e633c4ed795d7cf96dddad588dbdee2 Mon Sep 17 00:00:00 2001 From: Omar Tulashvili Date: Tue, 20 Jan 2026 19:46:39 +0300 Subject: [PATCH 03/12] add env; add LoadDS/LoadConfig; change makefile; --- .gitignore | 3 +++ Makefile | 11 ++++++++++- go.mod | 6 +----- go.sum | 13 ++----------- internal/app.go | 16 +++++++++------- internal/config/config.go | 13 ++++++++----- pkg/db/datasource.go | 18 ++++++++++++++++-- pkg/db/db.go | 8 +++++--- 8 files changed, 54 insertions(+), 34 deletions(-) diff --git a/.gitignore b/.gitignore index f3985fc..06a0372 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ *.json data/ +.DS_Store +.env +.vscode \ No newline at end of file diff --git a/Makefile b/Makefile index 6108a7d..cdf4af6 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,15 @@ APP="My daily control questions" -.PHONY: run_linter +.PHONY: run_linter run_app run_linter: golangci-lint run + +run_app: + set -a; . .env; set +a; go run cmd/main.go + +# Запуск приложения с нуля для проверки работы +start_from_scratch: + rm -rf data + mkdir data/ + set -a; . .env; set +a; go run cmd/main.go diff --git a/go.mod b/go.mod index 6be0614..566342f 100644 --- a/go.mod +++ b/go.mod @@ -4,8 +4,4 @@ go 1.25.4 require github.com/mattn/go-sqlite3 v1.14.33 -require ( - github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/spf13/cobra v1.10.2 // indirect - github.com/spf13/pflag v1.0.10 // indirect -) +require github.com/caarlos0/env/v11 v11.3.1 // indirect diff --git a/go.sum b/go.sum index ab4425b..d586b27 100644 --- a/go.sum +++ b/go.sum @@ -1,13 +1,4 @@ -github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= -github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= -github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/caarlos0/env/v11 v11.3.1 h1:cArPWC15hWmEt+gWk7YBi7lEXTXCvpaSdCiZE2X5mCA= +github.com/caarlos0/env/v11 v11.3.1/go.mod h1:qupehSf/Y0TUTsxKywqRt/vJjN5nz6vauiYEUUr8P4U= github.com/mattn/go-sqlite3 v1.14.33 h1:A5blZ5ulQo2AtayQ9/limgHEkFreKj1Dv226a1K73s0= github.com/mattn/go-sqlite3 v1.14.33/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= -github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= -github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= -github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= -github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/internal/app.go b/internal/app.go index d4b49a7..0fe754d 100644 --- a/internal/app.go +++ b/internal/app.go @@ -1,7 +1,6 @@ package app import ( - "database/sql" "fmt" "log" @@ -13,22 +12,24 @@ import ( "github.com/tulashvili/MyDailyControlQuestions/pkg/db" ) -const ( - sqliteStoragePath = "data/sqlite3.db" -) - type App struct { - DB *sql.DB Config config.Config } func RunApp() { + // app := App{ + // Config: cfg, + // } + + // config.LoadConfig() + // Logic??? questions := service.GetQuestions() answers := ui.AskQuestion(questions) + dbPath := db.LoadDatasource() // DB - conn, err := db.InitDB(sqliteStoragePath) + conn, err := db.InitDB(dbPath) if err != nil { log.Fatal(err) } @@ -44,6 +45,7 @@ func RunApp() { } fmt.Println("✅ Данные успешно добавлены в таблицу daily_log") // change to log? + // UI if err := ui.ShowDataOverPeriod(conn); err != nil { log.Panic(err) } diff --git a/internal/config/config.go b/internal/config/config.go index 3a25b56..ef0bc28 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -1,12 +1,15 @@ package config -import "github.com/tulashvili/MyDailyControlQuestions/pkg/db" +import ( + "os" +) type Config struct { - DS db.Datasource + DbPath string } -func NewConfig() (Config, error) { - cfg := Config{} - return cfg, nil +func LoadConfig() Config { + return Config{ + DbPath: os.Getenv("DB_PATH"), + } } diff --git a/pkg/db/datasource.go b/pkg/db/datasource.go index 92f6aab..2d0d347 100644 --- a/pkg/db/datasource.go +++ b/pkg/db/datasource.go @@ -1,5 +1,19 @@ package db +import ( + "fmt" + "os" +) + type Datasource struct { - sqliteInitPath string -} \ No newline at end of file + SqlitePath string +} + +func LoadDatasource() Datasource { + wd, _ := os.Getwd() + fmt.Println("CWD:", wd) + fmt.Println("DB_PATH:", os.Getenv("DB_PATH")) + return Datasource{ + SqlitePath: os.Getenv("DB_PATH"), + } +} diff --git a/pkg/db/db.go b/pkg/db/db.go index a9d3f68..945b680 100644 --- a/pkg/db/db.go +++ b/pkg/db/db.go @@ -7,9 +7,11 @@ import ( _ "github.com/mattn/go-sqlite3" ) -func InitDB(databasePath string) (*sql.DB, error) { - fmt.Printf("🔌 Соединение с базой %s установлено\n", databasePath) // change to log? - return sql.Open("sqlite3", databasePath) +// move this to migrate.go + +func InitDB(path Datasource) (*sql.DB, error) { + fmt.Printf("🔌 Соединение с базой %s установлено\n", path) // change to log? + return sql.Open("sqlite3", path.SqlitePath) } From 45564aaa71fb5aa036c0aef6c3a2c49d4d3e7bda Mon Sep 17 00:00:00 2001 From: Omar Tulashvili Date: Wed, 21 Jan 2026 21:02:17 +0300 Subject: [PATCH 04/12] change some architecture desisions --- cmd/main.go | 2 +- internal/app.go | 24 ++++++++++++------------ internal/config/config.go | 14 +++++++------- internal/models/models.go | 2 +- pkg/db/datasource.go | 10 +++------- pkg/db/{db.go => sqlitedb/sqlite.go} | 5 +++-- 6 files changed, 27 insertions(+), 30 deletions(-) rename pkg/db/{db.go => sqlitedb/sqlite.go} (84%) diff --git a/cmd/main.go b/cmd/main.go index bbf4ff6..d53ccc7 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -8,5 +8,5 @@ import ( func main() { time.Local = time.UTC - app.RunApp() + app.NewApp() } diff --git a/internal/app.go b/internal/app.go index 0fe754d..1920e9a 100644 --- a/internal/app.go +++ b/internal/app.go @@ -1,6 +1,7 @@ package app import ( + "database/sql" "fmt" "log" @@ -9,44 +10,43 @@ import ( "github.com/tulashvili/MyDailyControlQuestions/internal/service" "github.com/tulashvili/MyDailyControlQuestions/internal/ui" - "github.com/tulashvili/MyDailyControlQuestions/pkg/db" + "github.com/tulashvili/MyDailyControlQuestions/pkg/db/sqlitedb" ) type App struct { + DB *sql.DB Config config.Config } -func RunApp() { - // app := App{ - // Config: cfg, - // } - - // config.LoadConfig() +func NewApp(conf config.Config) { + app := &App{ + Config: conf, + } // Logic??? questions := service.GetQuestions() answers := ui.AskQuestion(questions) - dbPath := db.LoadDatasource() // DB - conn, err := db.InitDB(dbPath) + var err error + app.DB, err = sqlitedb.InitDB(conf.DbPath) if err != nil { log.Fatal(err) } - if err := db.CreateTable(conn); err != nil { + if err := sqlitedb.CreateTable(app.DB); err != nil { log.Fatal(err) } for _, answer := range answers { - if err := repository.InsertRow(conn, answer); err != nil { + if err := repository.InsertRow(app.DB, answer); err != nil { log.Fatal(err) } } fmt.Println("✅ Данные успешно добавлены в таблицу daily_log") // change to log? // UI - if err := ui.ShowDataOverPeriod(conn); err != nil { + if err := ui.ShowDataOverPeriod(app.DB); err != nil { log.Panic(err) } } diff --git a/internal/config/config.go b/internal/config/config.go index ef0bc28..85746ef 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -1,15 +1,15 @@ package config import ( - "os" + "github.com/tulashvili/MyDailyControlQuestions/pkg/db" ) type Config struct { - DbPath string + DbPath db.DataSource } -func LoadConfig() Config { - return Config{ - DbPath: os.Getenv("DB_PATH"), - } -} +// func LoadConfig() Config { +// return Config{ +// DbPath: os.Getenv("DB_PATH"), +// } +// } diff --git a/internal/models/models.go b/internal/models/models.go index 50ad4ad..eb410f2 100644 --- a/internal/models/models.go +++ b/internal/models/models.go @@ -13,7 +13,7 @@ type UserAnswer struct { } type Question struct { - Category Category + Category Text string } diff --git a/pkg/db/datasource.go b/pkg/db/datasource.go index 2d0d347..f8cf5f3 100644 --- a/pkg/db/datasource.go +++ b/pkg/db/datasource.go @@ -1,19 +1,15 @@ package db import ( - "fmt" "os" ) -type Datasource struct { +type DataSource struct { SqlitePath string } -func LoadDatasource() Datasource { - wd, _ := os.Getwd() - fmt.Println("CWD:", wd) - fmt.Println("DB_PATH:", os.Getenv("DB_PATH")) - return Datasource{ +func LoadDatasource() DataSource { + return DataSource{ SqlitePath: os.Getenv("DB_PATH"), } } diff --git a/pkg/db/db.go b/pkg/db/sqlitedb/sqlite.go similarity index 84% rename from pkg/db/db.go rename to pkg/db/sqlitedb/sqlite.go index 945b680..cc15576 100644 --- a/pkg/db/db.go +++ b/pkg/db/sqlitedb/sqlite.go @@ -1,15 +1,16 @@ -package db +package sqlitedb import ( "database/sql" "fmt" _ "github.com/mattn/go-sqlite3" + "github.com/tulashvili/MyDailyControlQuestions/pkg/db" ) // move this to migrate.go -func InitDB(path Datasource) (*sql.DB, error) { +func InitDB(path db.DataSource) (*sql.DB, error) { fmt.Printf("🔌 Соединение с базой %s установлено\n", path) // change to log? return sql.Open("sqlite3", path.SqlitePath) From dc598485592f7317e2f032da53668e9593c04d05 Mon Sep 17 00:00:00 2001 From: Omar Tulashvili Date: Wed, 21 Jan 2026 21:22:57 +0300 Subject: [PATCH 05/12] start cobra intergrity --- LICENSE | 0 cmd/main.go | 3 +++ go.mod | 7 +++++- go.sum | 10 ++++++++ internal/ui/cli/ask.go | 40 +++++++++++++++++++++++++++++++ internal/ui/cli/root.go | 51 ++++++++++++++++++++++++++++++++++++++++ internal/ui/cli/stats.go | 40 +++++++++++++++++++++++++++++++ 7 files changed, 150 insertions(+), 1 deletion(-) create mode 100644 LICENSE create mode 100644 internal/ui/cli/ask.go create mode 100644 internal/ui/cli/root.go create mode 100644 internal/ui/cli/stats.go diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..e69de29 diff --git a/cmd/main.go b/cmd/main.go index d53ccc7..011adda 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -4,9 +4,12 @@ import ( "time" app "github.com/tulashvili/MyDailyControlQuestions/internal" + "github.com/tulashvili/MyDailyControlQuestions/internal/ui/cli" ) func main() { time.Local = time.UTC app.NewApp() + + cli.Execute() } diff --git a/go.mod b/go.mod index 566342f..524b832 100644 --- a/go.mod +++ b/go.mod @@ -4,4 +4,9 @@ go 1.25.4 require github.com/mattn/go-sqlite3 v1.14.33 -require github.com/caarlos0/env/v11 v11.3.1 // indirect +require ( + github.com/caarlos0/env/v11 v11.3.1 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/spf13/cobra v1.10.2 // indirect + github.com/spf13/pflag v1.0.9 // indirect +) diff --git a/go.sum b/go.sum index d586b27..aa75004 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,14 @@ github.com/caarlos0/env/v11 v11.3.1 h1:cArPWC15hWmEt+gWk7YBi7lEXTXCvpaSdCiZE2X5mCA= github.com/caarlos0/env/v11 v11.3.1/go.mod h1:qupehSf/Y0TUTsxKywqRt/vJjN5nz6vauiYEUUr8P4U= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/mattn/go-sqlite3 v1.14.33 h1:A5blZ5ulQo2AtayQ9/limgHEkFreKj1Dv226a1K73s0= github.com/mattn/go-sqlite3 v1.14.33/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= +github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= +github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY= +github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/internal/ui/cli/ask.go b/internal/ui/cli/ask.go new file mode 100644 index 0000000..94f0406 --- /dev/null +++ b/internal/ui/cli/ask.go @@ -0,0 +1,40 @@ +/* +Copyright © 2026 NAME HERE +*/ +package cli + +import ( + "fmt" + + "github.com/spf13/cobra" +) + +// askCmd represents the ask command +var askCmd = &cobra.Command{ + Use: "ask", + Short: "Запустить процесс сбора ответов на вопросы", + Long: `Данная команда: + - показывает вопросы + - собирает ответы + - сохраняет результат`, + Run: func(cmd *cobra.Command, args []string) { + fmt.Println("ask called") + }, + // here my code + // .. + // .. +} + +func init() { + rootCmd.AddCommand(askCmd) + + // Here you will define your flags and configuration settings. + + // Cobra supports Persistent Flags which will work for this command + // and all subcommands, e.g.: + // askCmd.PersistentFlags().String("foo", "", "A help for foo") + + // Cobra supports local flags which will only run when this command + // is called directly, e.g.: + // askCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") +} diff --git a/internal/ui/cli/root.go b/internal/ui/cli/root.go new file mode 100644 index 0000000..eda3f68 --- /dev/null +++ b/internal/ui/cli/root.go @@ -0,0 +1,51 @@ +/* +Copyright © 2026 NAME HERE + +*/ +package cli + +import ( + "os" + + "github.com/spf13/cobra" +) + + + +// rootCmd represents the base command when called without any subcommands +var rootCmd = &cobra.Command{ + Use: "MyDailyControlQuestions", + Short: "Ежедневная саморефлексия путем ответа на важные для тебя вопросы по 5-ти бальной шкале", + Long: `A longer description that spans multiple lines and likely contains +examples and usage of using your application. 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.`, + // Uncomment the following line if your bare application + // has an action associated with it: + // Run: func(cmd *cobra.Command, args []string) { }, +} + +// Execute adds all child commands to the root command and sets flags appropriately. +// This is called by main.main(). It only needs to happen once to the rootCmd. +func Execute() { + err := rootCmd.Execute() + if err != nil { + os.Exit(1) + } +} + +func init() { + // Here you will define your flags and configuration settings. + // Cobra supports persistent flags, which, if defined here, + // will be global for your application. + + // rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.MyDailyControlQuestions.yaml)") + + // Cobra also supports local flags, which will only run + // when this action is called directly. + rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") +} + + diff --git a/internal/ui/cli/stats.go b/internal/ui/cli/stats.go new file mode 100644 index 0000000..d88be13 --- /dev/null +++ b/internal/ui/cli/stats.go @@ -0,0 +1,40 @@ +/* +Copyright © 2026 NAME HERE + +*/ +package cli + +import ( + "fmt" + + "github.com/spf13/cobra" +) + +// statsCmd represents the stats command +var statsCmd = &cobra.Command{ + Use: "stats", + 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) { + fmt.Println("stats called") + }, +} + +func init() { + rootCmd.AddCommand(statsCmd) + + // Here you will define your flags and configuration settings. + + // Cobra supports Persistent Flags which will work for this command + // and all subcommands, e.g.: + // statsCmd.PersistentFlags().String("foo", "", "A help for foo") + + // Cobra supports local flags which will only run when this command + // is called directly, e.g.: + // statsCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") +} From 96f5eca1e42618034c235b35e316d087bbbfdbb6 Mon Sep 17 00:00:00 2001 From: Omar Tulashvili Date: Sat, 24 Jan 2026 17:37:37 +0300 Subject: [PATCH 06/12] add stats cli; --- README.md | 1 + cmd/main.go | 19 ++++++--- go.mod | 1 + go.sum | 2 + internal/app.go | 34 +++++++-------- internal/config/config.go | 31 +++++++++++--- internal/service/service.go | 17 ++++++-- internal/ui/cli/ask.go | 40 ------------------ internal/ui/cli/stats.go | 29 ++++++++----- internal/ui/cli/{root.go => ui.go} | 57 +++++++++++++++++++------- internal/ui/ui.go | 66 ------------------------------ pkg/db/sqlitedb/sqlite.go | 16 ++++++-- pkg/formatted_result.go | 17 ++++++++ 13 files changed, 164 insertions(+), 166 deletions(-) delete mode 100644 internal/ui/cli/ask.go rename internal/ui/cli/{root.go => ui.go} (50%) delete mode 100644 internal/ui/ui.go create mode 100644 pkg/formatted_result.go diff --git a/README.md b/README.md index 39e9716..fd23a88 100644 --- a/README.md +++ b/README.md @@ -19,3 +19,4 @@ - разбит по неделям - каждая неделя - один лист - ~~Создать makefile и прикрутить линтер~~ ([MR](https://github.com/tulashvili/MyDailyControlQuestions/commit/b381af895f580a186e9eb7a07be5a0b26dc70f4f)) +- Добавить возможность настройки бэкапа данных по расписанию diff --git a/cmd/main.go b/cmd/main.go index 011adda..ebc8baf 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -1,15 +1,24 @@ package main import ( - "time" + "log/slog" + "os" app "github.com/tulashvili/MyDailyControlQuestions/internal" - "github.com/tulashvili/MyDailyControlQuestions/internal/ui/cli" + "github.com/tulashvili/MyDailyControlQuestions/internal/config" ) func main() { - time.Local = time.UTC - app.NewApp() + conf, err := config.NewConfig(true) + if err != nil { + slog.Error("failed to load config", slog.Any("error", err)) + os.Exit(1) + } + + err = app.NewApp(*conf) + if err != nil { + slog.Error("failed to create app", slog.Any("error", err)) + os.Exit(1) + } - cli.Execute() } diff --git a/go.mod b/go.mod index 524b832..35f8f9c 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require github.com/mattn/go-sqlite3 v1.14.33 require ( github.com/caarlos0/env/v11 v11.3.1 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/joho/godotenv v1.5.1 // indirect github.com/spf13/cobra v1.10.2 // indirect github.com/spf13/pflag v1.0.9 // indirect ) diff --git a/go.sum b/go.sum index aa75004..c4bc0cd 100644 --- a/go.sum +++ b/go.sum @@ -3,6 +3,8 @@ github.com/caarlos0/env/v11 v11.3.1/go.mod h1:qupehSf/Y0TUTsxKywqRt/vJjN5nz6vaui github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/mattn/go-sqlite3 v1.14.33 h1:A5blZ5ulQo2AtayQ9/limgHEkFreKj1Dv226a1K73s0= github.com/mattn/go-sqlite3 v1.14.33/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= diff --git a/internal/app.go b/internal/app.go index 1920e9a..73b99e0 100644 --- a/internal/app.go +++ b/internal/app.go @@ -2,13 +2,10 @@ package app import ( "database/sql" - "fmt" "log" "github.com/tulashvili/MyDailyControlQuestions/internal/config" - repository "github.com/tulashvili/MyDailyControlQuestions/internal/repo" - "github.com/tulashvili/MyDailyControlQuestions/internal/service" - "github.com/tulashvili/MyDailyControlQuestions/internal/ui" + "github.com/tulashvili/MyDailyControlQuestions/internal/ui/cli" "github.com/tulashvili/MyDailyControlQuestions/pkg/db/sqlitedb" ) @@ -18,14 +15,13 @@ type App struct { Config config.Config } -func NewApp(conf config.Config) { +func NewApp(conf config.Config) error { app := &App{ Config: conf, } - // Logic??? - questions := service.GetQuestions() - answers := ui.AskQuestion(questions) + // questions := service.GetQuestions() + // answers := ui.AskQuestion(questions) // DB var err error @@ -38,15 +34,15 @@ func NewApp(conf config.Config) { log.Fatal(err) } - for _, answer := range answers { - if err := repository.InsertRow(app.DB, answer); err != nil { - log.Fatal(err) - } - } - fmt.Println("✅ Данные успешно добавлены в таблицу daily_log") // change to log? - - // UI - if err := ui.ShowDataOverPeriod(app.DB); err != nil { - log.Panic(err) - } + // for _, answer := range answers { + // if err := repository.InsertRow(app.DB, answer); err != nil { + // log.Fatal(err) + // } + // } + cli.Execute(app.DB) + // // UI + // if err := service.ShowDataOverPeriod(app.DB); err != nil { + // log.Panic(err) + // } + return err } diff --git a/internal/config/config.go b/internal/config/config.go index 85746ef..95be10d 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -1,6 +1,11 @@ package config import ( + "fmt" + "log/slog" + "os" + + "github.com/joho/godotenv" "github.com/tulashvili/MyDailyControlQuestions/pkg/db" ) @@ -8,8 +13,24 @@ type Config struct { DbPath db.DataSource } -// func LoadConfig() Config { -// return Config{ -// DbPath: os.Getenv("DB_PATH"), -// } -// } +func NewConfig(loadDotEnv bool) (*Config, error) { + if err := os.Setenv("TZ", "UTC"); err != nil { + return nil, err + } + + if loadDotEnv { + if err := godotenv.Load(".env"); err == nil { + slog.Info("loaded .env file") + } + } + + c := &Config{ + DbPath: db.LoadDatasource(), + } + + if c.DbPath.SqlitePath == "" { + return nil, fmt.Errorf("DB_PATH is empty") + } + + return c, nil +} diff --git a/internal/service/service.go b/internal/service/service.go index a8b69e6..283ba00 100644 --- a/internal/service/service.go +++ b/internal/service/service.go @@ -1,10 +1,13 @@ package service import ( + "database/sql" "fmt" "time" "github.com/tulashvili/MyDailyControlQuestions/internal/models" + repository "github.com/tulashvili/MyDailyControlQuestions/internal/repo" + "github.com/tulashvili/MyDailyControlQuestions/pkg" ) const ( @@ -51,7 +54,6 @@ func ValidateAnswer(answer int) error { // Init user answer func CreateUserAnswer(question models.Question, answer int) models.UserAnswer { now := time.Now() - return models.UserAnswer{ QuestionCategory: string(question.Category), @@ -61,6 +63,15 @@ func CreateUserAnswer(question models.Question, answer int) models.UserAnswer { } } +// Show data over period +func ShowDataOverPeriod(period int, conn *sql.DB) error { + rows, err := repository.SelectRows(conn, period) + if err != nil { + return err + } - - + for _, row := range rows { + pkg.FormatedPrintResult(row) + } + return nil +} diff --git a/internal/ui/cli/ask.go b/internal/ui/cli/ask.go deleted file mode 100644 index 94f0406..0000000 --- a/internal/ui/cli/ask.go +++ /dev/null @@ -1,40 +0,0 @@ -/* -Copyright © 2026 NAME HERE -*/ -package cli - -import ( - "fmt" - - "github.com/spf13/cobra" -) - -// askCmd represents the ask command -var askCmd = &cobra.Command{ - Use: "ask", - Short: "Запустить процесс сбора ответов на вопросы", - Long: `Данная команда: - - показывает вопросы - - собирает ответы - - сохраняет результат`, - Run: func(cmd *cobra.Command, args []string) { - fmt.Println("ask called") - }, - // here my code - // .. - // .. -} - -func init() { - rootCmd.AddCommand(askCmd) - - // Here you will define your flags and configuration settings. - - // Cobra supports Persistent Flags which will work for this command - // and all subcommands, e.g.: - // askCmd.PersistentFlags().String("foo", "", "A help for foo") - - // Cobra supports local flags which will only run when this command - // is called directly, e.g.: - // askCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") -} diff --git a/internal/ui/cli/stats.go b/internal/ui/cli/stats.go index d88be13..6b8d660 100644 --- a/internal/ui/cli/stats.go +++ b/internal/ui/cli/stats.go @@ -1,27 +1,36 @@ /* Copyright © 2026 NAME HERE - */ package cli import ( - "fmt" + "database/sql" "github.com/spf13/cobra" + repository "github.com/tulashvili/MyDailyControlQuestions/internal/repo" + "github.com/tulashvili/MyDailyControlQuestions/pkg" +) + +var ( + db *sql.DB + period int ) // statsCmd represents the stats command var statsCmd = &cobra.Command{ Use: "stats", - Short: "A brief description of your command", + Short: "Получить статистику ответов за Х дней", 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.`, +and usage of using your command.`, Run: func(cmd *cobra.Command, args []string) { - fmt.Println("stats called") + rows, err := repository.SelectRows(db, period) + if err != nil { + panic(err) + } + + for _, row := range rows { + pkg.FormatedPrintResult(row) + } }, } @@ -36,5 +45,5 @@ func init() { // Cobra supports local flags which will only run when this command // is called directly, e.g.: - // statsCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") + statsCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") } diff --git a/internal/ui/cli/root.go b/internal/ui/cli/ui.go similarity index 50% rename from internal/ui/cli/root.go rename to internal/ui/cli/ui.go index eda3f68..151d16b 100644 --- a/internal/ui/cli/root.go +++ b/internal/ui/cli/ui.go @@ -1,27 +1,22 @@ /* Copyright © 2026 NAME HERE - */ package cli import ( - "os" + "database/sql" + "fmt" "github.com/spf13/cobra" + "github.com/tulashvili/MyDailyControlQuestions/internal/models" + "github.com/tulashvili/MyDailyControlQuestions/internal/service" ) - - // rootCmd represents the base command when called without any subcommands var rootCmd = &cobra.Command{ Use: "MyDailyControlQuestions", Short: "Ежедневная саморефлексия путем ответа на важные для тебя вопросы по 5-ти бальной шкале", - Long: `A longer description that spans multiple lines and likely contains -examples and usage of using your application. 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.`, + Long: ``, // Uncomment the following line if your bare application // has an action associated with it: // Run: func(cmd *cobra.Command, args []string) { }, @@ -29,10 +24,14 @@ to quickly create a Cobra application.`, // Execute adds all child commands to the root command and sets flags appropriately. // This is called by main.main(). It only needs to happen once to the rootCmd. -func Execute() { - err := rootCmd.Execute() - if err != nil { - os.Exit(1) +func Execute(conn *sql.DB, ) { + db = conn + + statsCmd.Flags().IntVarP(&period, "period", "p", 1, "Stats over the period") + statsCmd.MarkFlagRequired("stats") + + if err := rootCmd.Execute(); err != nil { + panic(err) } } @@ -48,4 +47,34 @@ func init() { rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") } +// Get question list with category +func GetQuestions(questions []models.Question) { + for _, question := range questions { + fmt.Println("Категория:", question.Category) + fmt.Println("Вопрос:", question.Text) + } +} + +// Ask a question to a client +func AskQuestion(questions []models.Question) []models.UserAnswer { + var result []models.UserAnswer + + for _, question := range questions { + fmt.Println("Категория:", question.Category) + fmt.Println("Вопрос:", question.Text) + var answer int + fmt.Scanln(&answer) + + err := service.ValidateAnswer(answer) + if err != nil { + fmt.Println(err) + fmt.Println("Введи значение заново") + fmt.Scanln(&answer) + } else { + userAnswer := service.CreateUserAnswer(question, answer) + result = append(result, userAnswer) + } + } + return result +} diff --git a/internal/ui/ui.go b/internal/ui/ui.go deleted file mode 100644 index cd3584e..0000000 --- a/internal/ui/ui.go +++ /dev/null @@ -1,66 +0,0 @@ -package ui - -import ( - "database/sql" - "fmt" - - "github.com/tulashvili/MyDailyControlQuestions/internal/models" - "github.com/tulashvili/MyDailyControlQuestions/internal/service" - "github.com/tulashvili/MyDailyControlQuestions/internal/repo" -) - -// Get question list with category -func GetQuestions(questions []models.Question) { - for _, question := range questions { - fmt.Println("Категория:", question.Category) - fmt.Println("Вопрос:", question.Text) - } -} - -// Ask a question to a client -func AskQuestion(questions []models.Question) []models.UserAnswer { - var result []models.UserAnswer - - for _, question := range questions { - fmt.Println("Категория:", question.Category) - fmt.Println("Вопрос:", question.Text) - - var answer int - fmt.Scanln(&answer) - - err := service.ValidateAnswer(answer) - if err != nil { - fmt.Println(err) - fmt.Println("Введи значение заново") - fmt.Scanln(&answer) - } else { - userAnswer := service.CreateUserAnswer(question, answer) - result = append(result, userAnswer) - } - } - return result -} - -// Show data over period -func ShowDataOverPeriod(conn *sql.DB) error { - period := 2 - rows, err := repository.SelectRows(conn, period) - if err != nil { - return err - } - - for _, row := range rows { - formatedPrintResult(row) - } - return nil -} - -// Formated result from SelectRow -func formatedPrintResult(userAnswer repository.UserAnswerRow) { - fmt.Println("---------------------") - fmt.Println("ID:", userAnswer.ID) - fmt.Println("Дата:", userAnswer.AnsweredAt) - fmt.Println("Категория:", userAnswer.Category) - fmt.Println("Вопрос:", userAnswer.Question) - fmt.Println("Ответ:", userAnswer.Answer) -} diff --git a/pkg/db/sqlitedb/sqlite.go b/pkg/db/sqlitedb/sqlite.go index cc15576..a5f0d19 100644 --- a/pkg/db/sqlitedb/sqlite.go +++ b/pkg/db/sqlitedb/sqlite.go @@ -2,7 +2,7 @@ package sqlitedb import ( "database/sql" - "fmt" + "log/slog" _ "github.com/mattn/go-sqlite3" "github.com/tulashvili/MyDailyControlQuestions/pkg/db" @@ -11,8 +11,14 @@ import ( // move this to migrate.go func InitDB(path db.DataSource) (*sql.DB, error) { - fmt.Printf("🔌 Соединение с базой %s установлено\n", path) // change to log? - return sql.Open("sqlite3", path.SqlitePath) + conn, err := sql.Open("sqlite3", path.SqlitePath) + if err == nil { + slog.Info( + "Соединение с базой установлено", + "sqlite_path", path.SqlitePath, + ) + } + return conn, err } @@ -29,6 +35,8 @@ func CreateTable(conn *sql.DB) error { ); ` _, err := conn.Exec(query) - fmt.Println("✅ Таблица daily_log готова к использованию") // change to log? + if err == nil { + slog.Info("Таблица daily_log готова к использованию") + } return err } diff --git a/pkg/formatted_result.go b/pkg/formatted_result.go new file mode 100644 index 0000000..d9236f2 --- /dev/null +++ b/pkg/formatted_result.go @@ -0,0 +1,17 @@ +package pkg + +import ( + "fmt" + + repository "github.com/tulashvili/MyDailyControlQuestions/internal/repo" +) + +// Formated result from SelectRow +func FormatedPrintResult(userAnswer repository.UserAnswerRow) { + fmt.Println("---------------------") + fmt.Println("ID:", userAnswer.ID) + fmt.Println("Дата:", userAnswer.AnsweredAt) + fmt.Println("Категория:", userAnswer.Category) + fmt.Println("Вопрос:", userAnswer.Question) + fmt.Println("Ответ:", userAnswer.Answer) +} From be75103665d1e73b7c4d977ff414c1107847d17e Mon Sep 17 00:00:00 2001 From: Omar Tulashvili Date: Sat, 24 Jan 2026 18:17:53 +0300 Subject: [PATCH 07/12] add get list questions; --- internal/service/service.go | 16 ---------------- internal/ui/cli/list_questions.go | 26 ++++++++++++++++++++++++++ internal/ui/cli/{ui.go => root.go} | 26 +++----------------------- internal/ui/cli/stats.go | 21 ++++++++++----------- pkg/formatted_result.go | 17 ----------------- 5 files changed, 39 insertions(+), 67 deletions(-) create mode 100644 internal/ui/cli/list_questions.go rename internal/ui/cli/{ui.go => root.go} (57%) delete mode 100644 pkg/formatted_result.go diff --git a/internal/service/service.go b/internal/service/service.go index 283ba00..6eeb2b2 100644 --- a/internal/service/service.go +++ b/internal/service/service.go @@ -1,13 +1,10 @@ package service import ( - "database/sql" "fmt" "time" "github.com/tulashvili/MyDailyControlQuestions/internal/models" - repository "github.com/tulashvili/MyDailyControlQuestions/internal/repo" - "github.com/tulashvili/MyDailyControlQuestions/pkg" ) const ( @@ -62,16 +59,3 @@ func CreateUserAnswer(question models.Question, answer int) models.UserAnswer { AnsweredAt: &now, } } - -// Show data over period -func ShowDataOverPeriod(period int, conn *sql.DB) error { - rows, err := repository.SelectRows(conn, period) - if err != nil { - return err - } - - for _, row := range rows { - pkg.FormatedPrintResult(row) - } - return nil -} diff --git a/internal/ui/cli/list_questions.go b/internal/ui/cli/list_questions.go new file mode 100644 index 0000000..9653797 --- /dev/null +++ b/internal/ui/cli/list_questions.go @@ -0,0 +1,26 @@ +/* +Copyright © 2026 NAME HERE +*/ +package cli + +import ( + "fmt" + + "github.com/spf13/cobra" + "github.com/tulashvili/MyDailyControlQuestions/internal/service" +) + +var listQuestionsCmd = &cobra.Command{ + Use: "list_questions", + Short: "Список текущих вопросов", + Long: ``, + Run: func(cmd *cobra.Command, args []string) { + questions := service.GetQuestions() + + for _, question := range questions { + fmt.Println("Категория:", question.Category) + fmt.Println("Вопрос:", question.Text) + fmt.Println() + } + }, +} diff --git a/internal/ui/cli/ui.go b/internal/ui/cli/root.go similarity index 57% rename from internal/ui/cli/ui.go rename to internal/ui/cli/root.go index 151d16b..9bc10d5 100644 --- a/internal/ui/cli/ui.go +++ b/internal/ui/cli/root.go @@ -12,49 +12,29 @@ import ( "github.com/tulashvili/MyDailyControlQuestions/internal/service" ) -// rootCmd represents the base command when called without any subcommands var rootCmd = &cobra.Command{ Use: "MyDailyControlQuestions", Short: "Ежедневная саморефлексия путем ответа на важные для тебя вопросы по 5-ти бальной шкале", Long: ``, - // Uncomment the following line if your bare application - // has an action associated with it: - // Run: func(cmd *cobra.Command, args []string) { }, } -// Execute adds all child commands to the root command and sets flags appropriately. -// This is called by main.main(). It only needs to happen once to the rootCmd. -func Execute(conn *sql.DB, ) { +func Execute(conn *sql.DB) { db = conn statsCmd.Flags().IntVarP(&period, "period", "p", 1, "Stats over the period") statsCmd.MarkFlagRequired("stats") + rootCmd.AddCommand(listQuestionsCmd) + if err := rootCmd.Execute(); err != nil { panic(err) } } func init() { - // Here you will define your flags and configuration settings. - // Cobra supports persistent flags, which, if defined here, - // will be global for your application. - - // rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.MyDailyControlQuestions.yaml)") - - // Cobra also supports local flags, which will only run - // when this action is called directly. rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") } -// Get question list with category -func GetQuestions(questions []models.Question) { - for _, question := range questions { - fmt.Println("Категория:", question.Category) - fmt.Println("Вопрос:", question.Text) - } -} - // Ask a question to a client func AskQuestion(questions []models.Question) []models.UserAnswer { var result []models.UserAnswer diff --git a/internal/ui/cli/stats.go b/internal/ui/cli/stats.go index 6b8d660..b328e01 100644 --- a/internal/ui/cli/stats.go +++ b/internal/ui/cli/stats.go @@ -5,10 +5,10 @@ package cli import ( "database/sql" + "fmt" "github.com/spf13/cobra" repository "github.com/tulashvili/MyDailyControlQuestions/internal/repo" - "github.com/tulashvili/MyDailyControlQuestions/pkg" ) var ( @@ -29,21 +29,20 @@ and usage of using your command.`, } for _, row := range rows { - pkg.FormatedPrintResult(row) + FormatedPrintResult(row) } }, } func init() { rootCmd.AddCommand(statsCmd) +} - // Here you will define your flags and configuration settings. - - // Cobra supports Persistent Flags which will work for this command - // and all subcommands, e.g.: - // statsCmd.PersistentFlags().String("foo", "", "A help for foo") - - // Cobra supports local flags which will only run when this command - // is called directly, e.g.: - statsCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") +func FormatedPrintResult(userAnswer repository.UserAnswerRow) { + fmt.Println("---------------------") + fmt.Println("ID:", userAnswer.ID) + fmt.Println("Дата:", userAnswer.AnsweredAt) + fmt.Println("Категория:", userAnswer.Category) + fmt.Println("Вопрос:", userAnswer.Question) + fmt.Println("Ответ:", userAnswer.Answer) } diff --git a/pkg/formatted_result.go b/pkg/formatted_result.go deleted file mode 100644 index d9236f2..0000000 --- a/pkg/formatted_result.go +++ /dev/null @@ -1,17 +0,0 @@ -package pkg - -import ( - "fmt" - - repository "github.com/tulashvili/MyDailyControlQuestions/internal/repo" -) - -// Formated result from SelectRow -func FormatedPrintResult(userAnswer repository.UserAnswerRow) { - fmt.Println("---------------------") - fmt.Println("ID:", userAnswer.ID) - fmt.Println("Дата:", userAnswer.AnsweredAt) - fmt.Println("Категория:", userAnswer.Category) - fmt.Println("Вопрос:", userAnswer.Question) - fmt.Println("Ответ:", userAnswer.Answer) -} From 6183ff2f2cf6c1c6dbd2b6dc5b7a6925d921d246 Mon Sep 17 00:00:00 2001 From: Omar Tulashvili Date: Sun, 25 Jan 2026 15:39:14 +0300 Subject: [PATCH 08/12] architecture improvement --- internal/app.go | 4 -- .../{models/models.go => domain/answer.go} | 13 +---- .../service.go => domain/question.go} | 45 ++++-------------- internal/domain/rules.go | 10 ++++ internal/repo/answer_repository.go | 8 ++++ .../repo/{repo.go => sqlite_repository.go} | 16 ++++--- internal/ui/cli/ask.go | 47 +++++++++++++++++++ internal/ui/cli/root.go | 28 +---------- internal/ui/cli/stats.go | 9 ---- internal/usecase/ask_question.go | 19 ++++++++ internal/usecase/list_questions.go | 8 ++++ internal/usecase/stats.go | 27 +++++++++++ 12 files changed, 142 insertions(+), 92 deletions(-) rename internal/{models/models.go => domain/answer.go} (58%) rename internal/{service/service.go => domain/question.go} (54%) create mode 100644 internal/domain/rules.go create mode 100644 internal/repo/answer_repository.go rename internal/repo/{repo.go => sqlite_repository.go} (74%) create mode 100644 internal/ui/cli/ask.go create mode 100644 internal/usecase/ask_question.go create mode 100644 internal/usecase/list_questions.go create mode 100644 internal/usecase/stats.go diff --git a/internal/app.go b/internal/app.go index 73b99e0..ba875e6 100644 --- a/internal/app.go +++ b/internal/app.go @@ -40,9 +40,5 @@ func NewApp(conf config.Config) error { // } // } cli.Execute(app.DB) - // // UI - // if err := service.ShowDataOverPeriod(app.DB); err != nil { - // log.Panic(err) - // } return err } diff --git a/internal/models/models.go b/internal/domain/answer.go similarity index 58% rename from internal/models/models.go rename to internal/domain/answer.go index eb410f2..f004e30 100644 --- a/internal/models/models.go +++ b/internal/domain/answer.go @@ -1,8 +1,6 @@ -package models +package domain -import ( - "time" -) +import "time" type UserAnswer struct { ID int @@ -11,10 +9,3 @@ type UserAnswer struct { Answer int AnsweredAt *time.Time } - -type Question struct { - Category - Text string -} - -type Category string diff --git a/internal/service/service.go b/internal/domain/question.go similarity index 54% rename from internal/service/service.go rename to internal/domain/question.go index 6eeb2b2..93c5177 100644 --- a/internal/service/service.go +++ b/internal/domain/question.go @@ -1,18 +1,18 @@ -package service +package domain -import ( - "fmt" - "time" +type Question struct { + Category + Text string +} - "github.com/tulashvili/MyDailyControlQuestions/internal/models" -) +type Category string const ( - Biology models.Category = "Биология" - Psychology models.Category = "Психология" + Biology Category = "Биология" + Psychology Category = "Психология" ) -var QUESTIONS = []models.Question{ +var QUESTIONS = []Question{ { Category: Psychology, Text: "Насколько высокий уровень стресса сегодня? (1 - очень стрессовый день, 5 - стресса почти не было)", @@ -33,29 +33,4 @@ var QUESTIONS = []models.Question{ Category: Biology, Text: "В рационе было достаточно белка и клетчатки? (1 - не было, 5 - да)", }, -} - -// Return question list with category -func GetQuestions() []models.Question { - return QUESTIONS -} - -// Validate user answer -func ValidateAnswer(answer int) error { - if answer < 1 || answer > 5 { - return fmt.Errorf("answer must be between 1 and 5") - } - return nil -} - -// Init user answer -func CreateUserAnswer(question models.Question, answer int) models.UserAnswer { - now := time.Now() - - return models.UserAnswer{ - QuestionCategory: string(question.Category), - QuestionText: question.Text, - Answer: answer, - AnsweredAt: &now, - } -} +} \ No newline at end of file diff --git a/internal/domain/rules.go b/internal/domain/rules.go new file mode 100644 index 0000000..e637241 --- /dev/null +++ b/internal/domain/rules.go @@ -0,0 +1,10 @@ +package domain + +import "errors" + +func ValidateAnswer(answer int) error { + if answer < 1 || answer > 5 { + return errors.New("answer must be between 1 and 5") + } + return nil +} diff --git a/internal/repo/answer_repository.go b/internal/repo/answer_repository.go new file mode 100644 index 0000000..5195d6d --- /dev/null +++ b/internal/repo/answer_repository.go @@ -0,0 +1,8 @@ +package repo + +import "github.com/tulashvili/MyDailyControlQuestions/internal/domain" + +type AnswerRepository interface { + Save(answer domain.UserAnswer) error + List(period int) ([]domain.UserAnswer, error) +} diff --git a/internal/repo/repo.go b/internal/repo/sqlite_repository.go similarity index 74% rename from internal/repo/repo.go rename to internal/repo/sqlite_repository.go index b155ea9..54be08d 100644 --- a/internal/repo/repo.go +++ b/internal/repo/sqlite_repository.go @@ -1,10 +1,10 @@ -package repository +package repo import ( "database/sql" "fmt" - "github.com/tulashvili/MyDailyControlQuestions/internal/models" + "github.com/tulashvili/MyDailyControlQuestions/internal/domain" ) type UserAnswerRow struct { @@ -15,16 +15,20 @@ type UserAnswerRow struct { AnsweredAt string } +type SQLiteRepo struct { + db *sql.DB +} + // ToDo: Create interface for db methods ?? // type .... interface { .... } // Insert question answers -func InsertRow(conn *sql.DB, data models.UserAnswer) error { +func InsertRow(conn SQLiteRepo, data domain.UserAnswer) error { query := ` INSERT INTO daily_log (category, question, answer, answeredAt) VALUES (?, ?, ?, ?) ` - _, err := conn.Exec( + _, err := conn.db.Exec( query, data.QuestionCategory, data.QuestionText, @@ -35,14 +39,14 @@ func InsertRow(conn *sql.DB, data models.UserAnswer) error { } // Get data over some period -func SelectRows(conn *sql.DB, period int) ([]UserAnswerRow, error) { +func SelectRows(conn SQLiteRepo, period int) ([]UserAnswerRow, error) { periodDay := fmt.Sprintf("-%d days", period) query := ` SELECT * FROM daily_log WHERE answeredAt >= datetime('now', ?); ` - response, err := conn.Query(query, periodDay) + response, err := conn.db.Query(query, periodDay) userAnswerRow := make([]UserAnswerRow, 0) diff --git a/internal/ui/cli/ask.go b/internal/ui/cli/ask.go new file mode 100644 index 0000000..1a339a5 --- /dev/null +++ b/internal/ui/cli/ask.go @@ -0,0 +1,47 @@ +/* +Copyright © 2026 NAME HERE +*/ +package cli + +import ( + "fmt" + + "github.com/spf13/cobra" + "github.com/tulashvili/MyDailyControlQuestions/internal/models" + "github.com/tulashvili/MyDailyControlQuestions/internal/service" +) + +// askQuestionsCmd represents the askQuestions command +var askQuestionsCmd = &cobra.Command{ + Use: "ask", + Short: "Запустить опрос", + Long: ``, + Run: func(cmd *cobra.Command, args []string) { + // askQuestion() + + }, +} + +// Ask a question to a client +func askQuestion(questions []models.Question) []models.UserAnswer { + var result []models.UserAnswer + + for _, question := range questions { + fmt.Println("Категория:", question.Category) + fmt.Println("Вопрос:", question.Text) + + var answer int + fmt.Scanln(&answer) + + err := service.ValidateAnswer(answer) + if err != nil { + fmt.Println(err) + fmt.Println("Введи значение заново") + fmt.Scanln(&answer) + } else { + userAnswer := service.CreateUserAnswer(question, answer) + result = append(result, userAnswer) + } + } + return result +} diff --git a/internal/ui/cli/root.go b/internal/ui/cli/root.go index 9bc10d5..a105496 100644 --- a/internal/ui/cli/root.go +++ b/internal/ui/cli/root.go @@ -5,11 +5,8 @@ package cli import ( "database/sql" - "fmt" "github.com/spf13/cobra" - "github.com/tulashvili/MyDailyControlQuestions/internal/models" - "github.com/tulashvili/MyDailyControlQuestions/internal/service" ) var rootCmd = &cobra.Command{ @@ -25,6 +22,7 @@ func Execute(conn *sql.DB) { statsCmd.MarkFlagRequired("stats") rootCmd.AddCommand(listQuestionsCmd) + rootCmd.AddCommand(askQuestionsCmd) if err := rootCmd.Execute(); err != nil { panic(err) @@ -34,27 +32,3 @@ func Execute(conn *sql.DB) { func init() { rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") } - -// Ask a question to a client -func AskQuestion(questions []models.Question) []models.UserAnswer { - var result []models.UserAnswer - - for _, question := range questions { - fmt.Println("Категория:", question.Category) - fmt.Println("Вопрос:", question.Text) - - var answer int - fmt.Scanln(&answer) - - err := service.ValidateAnswer(answer) - if err != nil { - fmt.Println(err) - fmt.Println("Введи значение заново") - fmt.Scanln(&answer) - } else { - userAnswer := service.CreateUserAnswer(question, answer) - result = append(result, userAnswer) - } - } - return result -} diff --git a/internal/ui/cli/stats.go b/internal/ui/cli/stats.go index b328e01..b0cf17c 100644 --- a/internal/ui/cli/stats.go +++ b/internal/ui/cli/stats.go @@ -37,12 +37,3 @@ and usage of using your command.`, func init() { rootCmd.AddCommand(statsCmd) } - -func FormatedPrintResult(userAnswer repository.UserAnswerRow) { - fmt.Println("---------------------") - fmt.Println("ID:", userAnswer.ID) - fmt.Println("Дата:", userAnswer.AnsweredAt) - fmt.Println("Категория:", userAnswer.Category) - fmt.Println("Вопрос:", userAnswer.Question) - fmt.Println("Ответ:", userAnswer.Answer) -} diff --git a/internal/usecase/ask_question.go b/internal/usecase/ask_question.go new file mode 100644 index 0000000..7eba9aa --- /dev/null +++ b/internal/usecase/ask_question.go @@ -0,0 +1,19 @@ +package usecase + +import ( + "time" + + "github.com/tulashvili/MyDailyControlQuestions/internal/domain" +) + +// Init user answer +func CreateUserAnswer(question domain.Question, answer int) domain.UserAnswer { + now := time.Now() + + return domain.UserAnswer{ + QuestionCategory: string(question.Category), + QuestionText: question.Text, + Answer: answer, + AnsweredAt: &now, + } +} diff --git a/internal/usecase/list_questions.go b/internal/usecase/list_questions.go new file mode 100644 index 0000000..82db8d7 --- /dev/null +++ b/internal/usecase/list_questions.go @@ -0,0 +1,8 @@ +package usecase + +import "github.com/tulashvili/MyDailyControlQuestions/internal/domain" + +// Return question list with category +func GetQuestions() []domain.Question { + return domain.QUESTIONS +} diff --git a/internal/usecase/stats.go b/internal/usecase/stats.go new file mode 100644 index 0000000..04b1998 --- /dev/null +++ b/internal/usecase/stats.go @@ -0,0 +1,27 @@ +package usecase + +import "fmt" + +// repository должен не напрямую обращаться к sqlite_repository.go, +// а обращаться должен к методам answer_repository.go , +// которые реализовывается в sqlite_repository.go + +func GetStatAnswers() { + rows, err := repository.SelectRows(db, period) + if err != nil { + panic(err) + } + + for _, row := range rows { + formatedPrintResult(row) + } +} + +func formatedPrintResult(userAnswer repository.UserAnswerRow) { + fmt.Println("---------------------") + fmt.Println("ID:", userAnswer.ID) + fmt.Println("Дата:", userAnswer.AnsweredAt) + fmt.Println("Категория:", userAnswer.Category) + fmt.Println("Вопрос:", userAnswer.Question) + fmt.Println("Ответ:", userAnswer.Answer) +} From d0c6f85842f06664f0dd99f21cad3786481e4abe Mon Sep 17 00:00:00 2001 From: Omar Tulashvili Date: Mon, 26 Jan 2026 00:20:59 +0300 Subject: [PATCH 09/12] architecture improvement --- internal/app.go | 3 +- internal/domain/answer.go | 12 ++++ internal/domain/rules.go | 1 + internal/repo/answer_repository.go | 6 +- internal/repo/sqlite_repository.go | 94 +++++++++++++++--------------- internal/ui/cli/ask.go | 47 --------------- internal/ui/cli/ask_question.go | 47 +++++++++++++++ internal/ui/cli/list_questions.go | 6 +- internal/ui/cli/root.go | 2 +- internal/ui/cli/stats.go | 27 ++++++--- internal/usecase/ask_question.go | 33 ++++++----- internal/usecase/list_questions.go | 2 +- internal/usecase/stats.go | 52 ++++++++++------- pkg/db/sqlitedb/sqlite.go | 4 +- 14 files changed, 186 insertions(+), 150 deletions(-) delete mode 100644 internal/ui/cli/ask.go create mode 100644 internal/ui/cli/ask_question.go diff --git a/internal/app.go b/internal/app.go index ba875e6..c4e24a5 100644 --- a/internal/app.go +++ b/internal/app.go @@ -20,8 +20,7 @@ func NewApp(conf config.Config) error { Config: conf, } // Logic??? - // questions := service.GetQuestions() - // answers := ui.AskQuestion(questions) + // answers := usecase.AskQuestion(questions) // DB var err error diff --git a/internal/domain/answer.go b/internal/domain/answer.go index f004e30..1f32cad 100644 --- a/internal/domain/answer.go +++ b/internal/domain/answer.go @@ -9,3 +9,15 @@ type UserAnswer struct { Answer int AnsweredAt *time.Time } + +// Init user answer +func CreateUserAnswer(question Question, answer int) UserAnswer { + now := time.Now() + + return UserAnswer{ + QuestionCategory: string(question.Category), + QuestionText: question.Text, + Answer: answer, + AnsweredAt: &now, + } +} \ No newline at end of file diff --git a/internal/domain/rules.go b/internal/domain/rules.go index e637241..e256776 100644 --- a/internal/domain/rules.go +++ b/internal/domain/rules.go @@ -2,6 +2,7 @@ package domain import "errors" +// Validate user answer func ValidateAnswer(answer int) error { if answer < 1 || answer > 5 { return errors.New("answer must be between 1 and 5") diff --git a/internal/repo/answer_repository.go b/internal/repo/answer_repository.go index 5195d6d..37ed4ca 100644 --- a/internal/repo/answer_repository.go +++ b/internal/repo/answer_repository.go @@ -3,6 +3,6 @@ package repo import "github.com/tulashvili/MyDailyControlQuestions/internal/domain" type AnswerRepository interface { - Save(answer domain.UserAnswer) error - List(period int) ([]domain.UserAnswer, error) -} + SaveAnswer(answer domain.UserAnswer) error + GetAnswers(period int) ([]domain.UserAnswer, error) +} \ No newline at end of file diff --git a/internal/repo/sqlite_repository.go b/internal/repo/sqlite_repository.go index 54be08d..17a17fa 100644 --- a/internal/repo/sqlite_repository.go +++ b/internal/repo/sqlite_repository.go @@ -7,63 +7,61 @@ import ( "github.com/tulashvili/MyDailyControlQuestions/internal/domain" ) -type UserAnswerRow struct { - ID int - Category string - Question string - Answer int - AnsweredAt string -} - -type SQLiteRepo struct { +type SQLiteAnswerRepository struct { db *sql.DB } -// ToDo: Create interface for db methods ?? -// type .... interface { .... } +func NewSQLiteAnswerRepository(db *sql.DB) *SQLiteAnswerRepository { + return &SQLiteAnswerRepository{db: db} +} -// Insert question answers -func InsertRow(conn SQLiteRepo, data domain.UserAnswer) error { - query := ` - INSERT INTO daily_log (category, question, answer, answeredAt) - VALUES (?, ?, ?, ?) - ` - _, err := conn.db.Exec( - query, - data.QuestionCategory, - data.QuestionText, - data.Answer, - data.AnsweredAt, - ) - return err +// Save answers to questions +func (r *SQLiteAnswerRepository) SaveAnswer(answer domain.UserAnswer) error { + query := ` + INSERT INTO daily_log (category, question, answer, answeredAt) + VALUES (?, ?, ?, ?) + ` + _, err := r.db.Exec( + query, + answer.QuestionCategory, + answer.QuestionText, + answer.Answer, + answer.AnsweredAt, + ) + return err } // Get data over some period -func SelectRows(conn SQLiteRepo, period int) ([]UserAnswerRow, error) { - periodDay := fmt.Sprintf("-%d days", period) - query := ` - SELECT * FROM daily_log - WHERE answeredAt >= datetime('now', ?); - ` +func (r *SQLiteAnswerRepository) GetAnswers(period int) ([]domain.UserAnswer, error) { + periodDay := fmt.Sprintf("-%d days", period) + query := ` + SELECT category, question, answer, answeredAt + FROM daily_log + WHERE answeredAt >= datetime('now', ?); + ` + + rows, err := r.db.Query(query, periodDay) + if err != nil { + return nil, err + } + defer rows.Close() + + var result []domain.UserAnswer - response, err := conn.db.Query(query, periodDay) + for rows.Next() { + var ua domain.UserAnswer - userAnswerRow := make([]UserAnswerRow, 0) + if err := rows.Scan( + &ua.QuestionCategory, + &ua.QuestionText, + &ua.Answer, + &ua.AnsweredAt, + ); err != nil { + return nil, err + } - for response.Next() { - var row UserAnswerRow + result = append(result, ua) + } - err := response.Scan( - &row.ID, - &row.Category, - &row.Question, - &row.Answer, - &row.AnsweredAt, - ) - if err != nil { - return nil, err - } - userAnswerRow = append(userAnswerRow, row) - } - return userAnswerRow, err + return result, nil } diff --git a/internal/ui/cli/ask.go b/internal/ui/cli/ask.go deleted file mode 100644 index 1a339a5..0000000 --- a/internal/ui/cli/ask.go +++ /dev/null @@ -1,47 +0,0 @@ -/* -Copyright © 2026 NAME HERE -*/ -package cli - -import ( - "fmt" - - "github.com/spf13/cobra" - "github.com/tulashvili/MyDailyControlQuestions/internal/models" - "github.com/tulashvili/MyDailyControlQuestions/internal/service" -) - -// askQuestionsCmd represents the askQuestions command -var askQuestionsCmd = &cobra.Command{ - Use: "ask", - Short: "Запустить опрос", - Long: ``, - Run: func(cmd *cobra.Command, args []string) { - // askQuestion() - - }, -} - -// Ask a question to a client -func askQuestion(questions []models.Question) []models.UserAnswer { - var result []models.UserAnswer - - for _, question := range questions { - fmt.Println("Категория:", question.Category) - fmt.Println("Вопрос:", question.Text) - - var answer int - fmt.Scanln(&answer) - - err := service.ValidateAnswer(answer) - if err != nil { - fmt.Println(err) - fmt.Println("Введи значение заново") - fmt.Scanln(&answer) - } else { - userAnswer := service.CreateUserAnswer(question, answer) - result = append(result, userAnswer) - } - } - return result -} diff --git a/internal/ui/cli/ask_question.go b/internal/ui/cli/ask_question.go new file mode 100644 index 0000000..026a7ab --- /dev/null +++ b/internal/ui/cli/ask_question.go @@ -0,0 +1,47 @@ +/* +Copyright © 2026 NAME HERE +*/ +package cli + +// import ( +// "fmt" + +// "github.com/spf13/cobra" +// "github.com/tulashvili/MyDailyControlQuestions/internal/models" +// "github.com/tulashvili/MyDailyControlQuestions/internal/service" +// ) + +// // askQuestionsCmd represents the askQuestions command +// var askQuestionsCmd = &cobra.Command{ +// Use: "ask", +// Short: "Запустить опрос", +// Long: ``, +// Run: func(cmd *cobra.Command, args []string) { +// // askQuestion() + +// }, +// } + +// // Ask a question to a client +// func askQuestion(questions []models.Question) []models.UserAnswer { +// var result []models.UserAnswer + +// for _, question := range questions { +// fmt.Println("Категория:", question.Category) +// fmt.Println("Вопрос:", question.Text) + +// var answer int +// fmt.Scanln(&answer) + +// err := service.ValidateAnswer(answer) +// if err != nil { +// fmt.Println(err) +// fmt.Println("Введи значение заново") +// fmt.Scanln(&answer) +// } else { +// userAnswer := service.CreateUserAnswer(question, answer) +// result = append(result, userAnswer) +// } +// } +// return result +// } diff --git a/internal/ui/cli/list_questions.go b/internal/ui/cli/list_questions.go index 9653797..db79fb0 100644 --- a/internal/ui/cli/list_questions.go +++ b/internal/ui/cli/list_questions.go @@ -7,15 +7,15 @@ import ( "fmt" "github.com/spf13/cobra" - "github.com/tulashvili/MyDailyControlQuestions/internal/service" + "github.com/tulashvili/MyDailyControlQuestions/internal/usecase" ) var listQuestionsCmd = &cobra.Command{ Use: "list_questions", - Short: "Список текущих вопросов", + Short: "Получить список текущих вопросов", Long: ``, Run: func(cmd *cobra.Command, args []string) { - questions := service.GetQuestions() + questions := usecase.GetQuestionList() for _, question := range questions { fmt.Println("Категория:", question.Category) diff --git a/internal/ui/cli/root.go b/internal/ui/cli/root.go index a105496..821eb65 100644 --- a/internal/ui/cli/root.go +++ b/internal/ui/cli/root.go @@ -22,7 +22,7 @@ func Execute(conn *sql.DB) { statsCmd.MarkFlagRequired("stats") rootCmd.AddCommand(listQuestionsCmd) - rootCmd.AddCommand(askQuestionsCmd) + // rootCmd.AddCommand(askQuestionsCmd) if err := rootCmd.Execute(); err != nil { panic(err) diff --git a/internal/ui/cli/stats.go b/internal/ui/cli/stats.go index b0cf17c..4167bea 100644 --- a/internal/ui/cli/stats.go +++ b/internal/ui/cli/stats.go @@ -6,9 +6,12 @@ package cli import ( "database/sql" "fmt" + "log" "github.com/spf13/cobra" - repository "github.com/tulashvili/MyDailyControlQuestions/internal/repo" + "github.com/tulashvili/MyDailyControlQuestions/internal/domain" + "github.com/tulashvili/MyDailyControlQuestions/internal/repo" + "github.com/tulashvili/MyDailyControlQuestions/internal/usecase" ) var ( @@ -20,16 +23,15 @@ var ( var statsCmd = &cobra.Command{ Use: "stats", Short: "Получить статистику ответов за Х дней", - Long: `A longer description that spans multiple lines and likely contains examples -and usage of using your command.`, + Long: ``, Run: func(cmd *cobra.Command, args []string) { - rows, err := repository.SelectRows(db, period) + repo := repo.NewSQLiteAnswerRepository(db) + stats, err := usecase.NewGetStat(repo).Execute(period) if err != nil { - panic(err) + log.Fatal(err) } - - for _, row := range rows { - FormatedPrintResult(row) + for _, answer := range stats { + formatedPrintResult(answer) } }, } @@ -37,3 +39,12 @@ and usage of using your command.`, func init() { rootCmd.AddCommand(statsCmd) } + +func formatedPrintResult(userAnswer domain.UserAnswer) { + fmt.Println("---------------------") + fmt.Println("ID:", userAnswer.ID) + fmt.Println("Дата:", userAnswer.AnsweredAt) + fmt.Println("Категория:", userAnswer.QuestionCategory) + fmt.Println("Вопрос:", userAnswer.QuestionText) + fmt.Println("Ответ:", userAnswer.Answer) +} diff --git a/internal/usecase/ask_question.go b/internal/usecase/ask_question.go index 7eba9aa..b73deba 100644 --- a/internal/usecase/ask_question.go +++ b/internal/usecase/ask_question.go @@ -1,19 +1,24 @@ package usecase -import ( - "time" +// import ( +// "github.com/tulashvili/MyDailyControlQuestions/internal/domain" +// ) - "github.com/tulashvili/MyDailyControlQuestions/internal/domain" -) +// type AskQuestionUseCase struct { +// input Input +// } -// Init user answer -func CreateUserAnswer(question domain.Question, answer int) domain.UserAnswer { - now := time.Now() +// // Ask a question to a client +// func (uc *AskQuestionUseCase) Run(questions []domain.Question) ([]domain.UserAnswer, error) { +// var result []domain.UserAnswer +// var answer int - return domain.UserAnswer{ - QuestionCategory: string(question.Category), - QuestionText: question.Text, - Answer: answer, - AnsweredAt: &now, - } -} +// for _, question := range questions { +// if err := domain.ValidateAnswer(answer); err != nil { +// return nil, err +// } +// userAnswer := domain.CreateUserAnswer(question, answer) +// result = append(result, userAnswer) +// } +// return result, err +// } diff --git a/internal/usecase/list_questions.go b/internal/usecase/list_questions.go index 82db8d7..afdeef2 100644 --- a/internal/usecase/list_questions.go +++ b/internal/usecase/list_questions.go @@ -3,6 +3,6 @@ package usecase import "github.com/tulashvili/MyDailyControlQuestions/internal/domain" // Return question list with category -func GetQuestions() []domain.Question { +func GetQuestionList() []domain.Question { return domain.QUESTIONS } diff --git a/internal/usecase/stats.go b/internal/usecase/stats.go index 04b1998..a46eceb 100644 --- a/internal/usecase/stats.go +++ b/internal/usecase/stats.go @@ -1,27 +1,39 @@ package usecase -import "fmt" +import ( + "github.com/tulashvili/MyDailyControlQuestions/internal/domain" + "github.com/tulashvili/MyDailyControlQuestions/internal/repo" +) -// repository должен не напрямую обращаться к sqlite_repository.go, -// а обращаться должен к методам answer_repository.go , -// которые реализовывается в sqlite_repository.go - -func GetStatAnswers() { - rows, err := repository.SelectRows(db, period) - if err != nil { - panic(err) - } +type GetStatAnswer struct { + repo repo.AnswerRepository +} - for _, row := range rows { - formatedPrintResult(row) - } +func NewGetStat(repo repo.AnswerRepository) *GetStatAnswer { + return &GetStatAnswer{repo: repo} } -func formatedPrintResult(userAnswer repository.UserAnswerRow) { - fmt.Println("---------------------") - fmt.Println("ID:", userAnswer.ID) - fmt.Println("Дата:", userAnswer.AnsweredAt) - fmt.Println("Категория:", userAnswer.Category) - fmt.Println("Вопрос:", userAnswer.Question) - fmt.Println("Ответ:", userAnswer.Answer) +func (gs *GetStatAnswer) Execute(period int) ([]domain.UserAnswer, error) { + return gs.repo.GetAnswers(period) } + +// func GetStatAnswers(period int) []domain.UserAnswer { + +// rows, err := repository.SelectRows(db, period) +// if err != nil { +// panic(err) +// } + +// for _, row := range rows { +// formatedPrintResult(row) +// } +// } + +// func formatedPrintResult(userAnswer repository.UserAnswerRow) { +// fmt.Println("---------------------") +// fmt.Println("ID:", userAnswer.ID) +// fmt.Println("Дата:", userAnswer.AnsweredAt) +// fmt.Println("Категория:", userAnswer.Category) +// fmt.Println("Вопрос:", userAnswer.Question) +// fmt.Println("Ответ:", userAnswer.Answer) +// } diff --git a/pkg/db/sqlitedb/sqlite.go b/pkg/db/sqlitedb/sqlite.go index a5f0d19..cc52782 100644 --- a/pkg/db/sqlitedb/sqlite.go +++ b/pkg/db/sqlitedb/sqlite.go @@ -29,9 +29,7 @@ func CreateTable(conn *sql.DB) error { category TEXT NOT NULL, question TEXT NOT NULL, answer INTEGER NOT NULL, - answeredAt TEXT NOT NULL, - - UNIQUE (answeredAt) + answeredAt TEXT DEFAULT CURRENT_TIMESTAMP ); ` _, err := conn.Exec(query) From 9ef65e009acd27971e606d721c582e8963787238 Mon Sep 17 00:00:00 2001 From: Omar Tulashvili Date: Mon, 26 Jan 2026 00:38:08 +0300 Subject: [PATCH 10/12] implement save answer use case --- internal/app.go | 7 ----- internal/repo/answer_repository.go | 2 +- internal/repo/sqlite_repository.go | 2 +- internal/ui/cli/ask_question.go | 47 ------------------------------ internal/ui/cli/save_answer.go | 47 ++++++++++++++++++++++++++++++ internal/usecase/ask_question.go | 24 --------------- internal/usecase/get_stats.go | 18 ++++++++++++ internal/usecase/save_answer.go | 18 ++++++++++++ internal/usecase/stats.go | 39 ------------------------- 9 files changed, 85 insertions(+), 119 deletions(-) delete mode 100644 internal/ui/cli/ask_question.go create mode 100644 internal/ui/cli/save_answer.go delete mode 100644 internal/usecase/ask_question.go create mode 100644 internal/usecase/get_stats.go create mode 100644 internal/usecase/save_answer.go delete mode 100644 internal/usecase/stats.go diff --git a/internal/app.go b/internal/app.go index c4e24a5..03f239b 100644 --- a/internal/app.go +++ b/internal/app.go @@ -19,8 +19,6 @@ func NewApp(conf config.Config) error { app := &App{ Config: conf, } - // Logic??? - // answers := usecase.AskQuestion(questions) // DB var err error @@ -33,11 +31,6 @@ func NewApp(conf config.Config) error { log.Fatal(err) } - // for _, answer := range answers { - // if err := repository.InsertRow(app.DB, answer); err != nil { - // log.Fatal(err) - // } - // } cli.Execute(app.DB) return err } diff --git a/internal/repo/answer_repository.go b/internal/repo/answer_repository.go index 37ed4ca..b2fb970 100644 --- a/internal/repo/answer_repository.go +++ b/internal/repo/answer_repository.go @@ -3,6 +3,6 @@ package repo import "github.com/tulashvili/MyDailyControlQuestions/internal/domain" type AnswerRepository interface { - SaveAnswer(answer domain.UserAnswer) error + SaveAnswers(answer domain.UserAnswer) error GetAnswers(period int) ([]domain.UserAnswer, error) } \ No newline at end of file diff --git a/internal/repo/sqlite_repository.go b/internal/repo/sqlite_repository.go index 17a17fa..83c52d1 100644 --- a/internal/repo/sqlite_repository.go +++ b/internal/repo/sqlite_repository.go @@ -16,7 +16,7 @@ func NewSQLiteAnswerRepository(db *sql.DB) *SQLiteAnswerRepository { } // Save answers to questions -func (r *SQLiteAnswerRepository) SaveAnswer(answer domain.UserAnswer) error { +func (r *SQLiteAnswerRepository) SaveAnswers(answer domain.UserAnswer) error { query := ` INSERT INTO daily_log (category, question, answer, answeredAt) VALUES (?, ?, ?, ?) diff --git a/internal/ui/cli/ask_question.go b/internal/ui/cli/ask_question.go deleted file mode 100644 index 026a7ab..0000000 --- a/internal/ui/cli/ask_question.go +++ /dev/null @@ -1,47 +0,0 @@ -/* -Copyright © 2026 NAME HERE -*/ -package cli - -// import ( -// "fmt" - -// "github.com/spf13/cobra" -// "github.com/tulashvili/MyDailyControlQuestions/internal/models" -// "github.com/tulashvili/MyDailyControlQuestions/internal/service" -// ) - -// // askQuestionsCmd represents the askQuestions command -// var askQuestionsCmd = &cobra.Command{ -// Use: "ask", -// Short: "Запустить опрос", -// Long: ``, -// Run: func(cmd *cobra.Command, args []string) { -// // askQuestion() - -// }, -// } - -// // Ask a question to a client -// func askQuestion(questions []models.Question) []models.UserAnswer { -// var result []models.UserAnswer - -// for _, question := range questions { -// fmt.Println("Категория:", question.Category) -// fmt.Println("Вопрос:", question.Text) - -// var answer int -// fmt.Scanln(&answer) - -// err := service.ValidateAnswer(answer) -// if err != nil { -// fmt.Println(err) -// fmt.Println("Введи значение заново") -// fmt.Scanln(&answer) -// } else { -// userAnswer := service.CreateUserAnswer(question, answer) -// result = append(result, userAnswer) -// } -// } -// return result -// } diff --git a/internal/ui/cli/save_answer.go b/internal/ui/cli/save_answer.go new file mode 100644 index 0000000..1a339a5 --- /dev/null +++ b/internal/ui/cli/save_answer.go @@ -0,0 +1,47 @@ +/* +Copyright © 2026 NAME HERE +*/ +package cli + +import ( + "fmt" + + "github.com/spf13/cobra" + "github.com/tulashvili/MyDailyControlQuestions/internal/models" + "github.com/tulashvili/MyDailyControlQuestions/internal/service" +) + +// askQuestionsCmd represents the askQuestions command +var askQuestionsCmd = &cobra.Command{ + Use: "ask", + Short: "Запустить опрос", + Long: ``, + Run: func(cmd *cobra.Command, args []string) { + // askQuestion() + + }, +} + +// Ask a question to a client +func askQuestion(questions []models.Question) []models.UserAnswer { + var result []models.UserAnswer + + for _, question := range questions { + fmt.Println("Категория:", question.Category) + fmt.Println("Вопрос:", question.Text) + + var answer int + fmt.Scanln(&answer) + + err := service.ValidateAnswer(answer) + if err != nil { + fmt.Println(err) + fmt.Println("Введи значение заново") + fmt.Scanln(&answer) + } else { + userAnswer := service.CreateUserAnswer(question, answer) + result = append(result, userAnswer) + } + } + return result +} diff --git a/internal/usecase/ask_question.go b/internal/usecase/ask_question.go deleted file mode 100644 index b73deba..0000000 --- a/internal/usecase/ask_question.go +++ /dev/null @@ -1,24 +0,0 @@ -package usecase - -// import ( -// "github.com/tulashvili/MyDailyControlQuestions/internal/domain" -// ) - -// type AskQuestionUseCase struct { -// input Input -// } - -// // Ask a question to a client -// func (uc *AskQuestionUseCase) Run(questions []domain.Question) ([]domain.UserAnswer, error) { -// var result []domain.UserAnswer -// var answer int - -// for _, question := range questions { -// if err := domain.ValidateAnswer(answer); err != nil { -// return nil, err -// } -// userAnswer := domain.CreateUserAnswer(question, answer) -// result = append(result, userAnswer) -// } -// return result, err -// } diff --git a/internal/usecase/get_stats.go b/internal/usecase/get_stats.go new file mode 100644 index 0000000..a63d6cc --- /dev/null +++ b/internal/usecase/get_stats.go @@ -0,0 +1,18 @@ +package usecase + +import ( + "github.com/tulashvili/MyDailyControlQuestions/internal/domain" + "github.com/tulashvili/MyDailyControlQuestions/internal/repo" +) + +type GetStatAnswer struct { + repo repo.AnswerRepository +} + +func NewGetStat(repo repo.AnswerRepository) *GetStatAnswer { + return &GetStatAnswer{repo: repo} +} + +func (gs *GetStatAnswer) Execute(period int) ([]domain.UserAnswer, error) { + return gs.repo.GetAnswers(period) +} diff --git a/internal/usecase/save_answer.go b/internal/usecase/save_answer.go new file mode 100644 index 0000000..8f3b06a --- /dev/null +++ b/internal/usecase/save_answer.go @@ -0,0 +1,18 @@ +package usecase + +import ( + "github.com/tulashvili/MyDailyControlQuestions/internal/domain" + "github.com/tulashvili/MyDailyControlQuestions/internal/repo" +) + +type SaveAnswer struct { + repo repo.AnswerRepository +} + +func NewSaveAnswers(repo repo.AnswerRepository) *SaveAnswer { + return &SaveAnswer{repo: repo} +} + +func (sa *SaveAnswer) Execute(answers domain.UserAnswer) error { + return sa.repo.SaveAnswers(answers) +} diff --git a/internal/usecase/stats.go b/internal/usecase/stats.go deleted file mode 100644 index a46eceb..0000000 --- a/internal/usecase/stats.go +++ /dev/null @@ -1,39 +0,0 @@ -package usecase - -import ( - "github.com/tulashvili/MyDailyControlQuestions/internal/domain" - "github.com/tulashvili/MyDailyControlQuestions/internal/repo" -) - -type GetStatAnswer struct { - repo repo.AnswerRepository -} - -func NewGetStat(repo repo.AnswerRepository) *GetStatAnswer { - return &GetStatAnswer{repo: repo} -} - -func (gs *GetStatAnswer) Execute(period int) ([]domain.UserAnswer, error) { - return gs.repo.GetAnswers(period) -} - -// func GetStatAnswers(period int) []domain.UserAnswer { - -// rows, err := repository.SelectRows(db, period) -// if err != nil { -// panic(err) -// } - -// for _, row := range rows { -// formatedPrintResult(row) -// } -// } - -// func formatedPrintResult(userAnswer repository.UserAnswerRow) { -// fmt.Println("---------------------") -// fmt.Println("ID:", userAnswer.ID) -// fmt.Println("Дата:", userAnswer.AnsweredAt) -// fmt.Println("Категория:", userAnswer.Category) -// fmt.Println("Вопрос:", userAnswer.Question) -// fmt.Println("Ответ:", userAnswer.Answer) -// } From d9959400bc519390d704369033a0e5cafcdd6b83 Mon Sep 17 00:00:00 2001 From: Omar Tulashvili Date: Mon, 26 Jan 2026 20:48:08 +0300 Subject: [PATCH 11/12] change ask question --- Makefile | 4 +- cmd/main.go | 4 +- internal/app.go | 9 ++-- internal/repo/answer_repository.go | 4 +- internal/repo/sqlite_repository.go | 64 ++++++++++++++--------------- internal/ui/cli/ask_questions.go | 27 ++++++++++++ internal/ui/cli/question_adapter.go | 25 +++++++++++ internal/ui/cli/root.go | 11 ++++- internal/ui/cli/save_answer.go | 47 --------------------- internal/ui/cli/stats.go | 6 --- internal/usecase/ask_question.go | 40 ++++++++++++++++++ internal/usecase/question.go | 9 ++++ internal/usecase/save_answer.go | 18 -------- 13 files changed, 154 insertions(+), 114 deletions(-) create mode 100644 internal/ui/cli/ask_questions.go create mode 100644 internal/ui/cli/question_adapter.go delete mode 100644 internal/ui/cli/save_answer.go create mode 100644 internal/usecase/ask_question.go create mode 100644 internal/usecase/question.go delete mode 100644 internal/usecase/save_answer.go diff --git a/Makefile b/Makefile index cdf4af6..31f328a 100644 --- a/Makefile +++ b/Makefile @@ -6,10 +6,10 @@ run_linter: golangci-lint run run_app: - set -a; . .env; set +a; go run cmd/main.go + set -a; . .env; set +a; go run cmd/main.go ask # Запуск приложения с нуля для проверки работы start_from_scratch: rm -rf data mkdir data/ - set -a; . .env; set +a; go run cmd/main.go + set -a; . .env; set +a; go run cmd/main.go ask diff --git a/cmd/main.go b/cmd/main.go index ebc8baf..78633a8 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -6,6 +6,7 @@ import ( app "github.com/tulashvili/MyDailyControlQuestions/internal" "github.com/tulashvili/MyDailyControlQuestions/internal/config" + "github.com/tulashvili/MyDailyControlQuestions/internal/ui/cli" ) func main() { @@ -15,10 +16,11 @@ func main() { os.Exit(1) } - err = app.NewApp(*conf) + app, err := app.NewApp(*conf) if err != nil { slog.Error("failed to create app", slog.Any("error", err)) os.Exit(1) } + cli.Execute(app.DB) } diff --git a/internal/app.go b/internal/app.go index 03f239b..bfe6ab8 100644 --- a/internal/app.go +++ b/internal/app.go @@ -5,7 +5,6 @@ import ( "log" "github.com/tulashvili/MyDailyControlQuestions/internal/config" - "github.com/tulashvili/MyDailyControlQuestions/internal/ui/cli" "github.com/tulashvili/MyDailyControlQuestions/pkg/db/sqlitedb" ) @@ -15,7 +14,7 @@ type App struct { Config config.Config } -func NewApp(conf config.Config) error { +func NewApp(conf config.Config) (App, error) { app := &App{ Config: conf, } @@ -30,7 +29,7 @@ func NewApp(conf config.Config) error { if err := sqlitedb.CreateTable(app.DB); err != nil { log.Fatal(err) } - - cli.Execute(app.DB) - return err + return App{ + DB: app.DB, + }, err } diff --git a/internal/repo/answer_repository.go b/internal/repo/answer_repository.go index b2fb970..fab2648 100644 --- a/internal/repo/answer_repository.go +++ b/internal/repo/answer_repository.go @@ -3,6 +3,6 @@ package repo import "github.com/tulashvili/MyDailyControlQuestions/internal/domain" type AnswerRepository interface { - SaveAnswers(answer domain.UserAnswer) error + SaveAnswer(answer domain.UserAnswer) error GetAnswers(period int) ([]domain.UserAnswer, error) -} \ No newline at end of file +} diff --git a/internal/repo/sqlite_repository.go b/internal/repo/sqlite_repository.go index 83c52d1..426a9c8 100644 --- a/internal/repo/sqlite_repository.go +++ b/internal/repo/sqlite_repository.go @@ -8,7 +8,7 @@ import ( ) type SQLiteAnswerRepository struct { - db *sql.DB + db *sql.DB } func NewSQLiteAnswerRepository(db *sql.DB) *SQLiteAnswerRepository { @@ -16,52 +16,52 @@ func NewSQLiteAnswerRepository(db *sql.DB) *SQLiteAnswerRepository { } // Save answers to questions -func (r *SQLiteAnswerRepository) SaveAnswers(answer domain.UserAnswer) error { - query := ` +func (r *SQLiteAnswerRepository) SaveAnswer(answer domain.UserAnswer) error { + query := ` INSERT INTO daily_log (category, question, answer, answeredAt) VALUES (?, ?, ?, ?) ` - _, err := r.db.Exec( - query, - answer.QuestionCategory, - answer.QuestionText, - answer.Answer, - answer.AnsweredAt, - ) - return err + _, err := r.db.Exec( + query, + answer.QuestionCategory, + answer.QuestionText, + answer.Answer, + answer.AnsweredAt, + ) + return err } // Get data over some period func (r *SQLiteAnswerRepository) GetAnswers(period int) ([]domain.UserAnswer, error) { - periodDay := fmt.Sprintf("-%d days", period) - query := ` + periodDay := fmt.Sprintf("-%d days", period) + query := ` SELECT category, question, answer, answeredAt FROM daily_log WHERE answeredAt >= datetime('now', ?); ` - rows, err := r.db.Query(query, periodDay) - if err != nil { - return nil, err - } - defer rows.Close() + rows, err := r.db.Query(query, periodDay) + if err != nil { + return nil, err + } + defer rows.Close() - var result []domain.UserAnswer + var result []domain.UserAnswer - for rows.Next() { - var ua domain.UserAnswer + for rows.Next() { + var ua domain.UserAnswer - if err := rows.Scan( - &ua.QuestionCategory, - &ua.QuestionText, - &ua.Answer, - &ua.AnsweredAt, - ); err != nil { - return nil, err - } + if err := rows.Scan( + &ua.QuestionCategory, + &ua.QuestionText, + &ua.Answer, + &ua.AnsweredAt, + ); err != nil { + return nil, err + } - result = append(result, ua) - } + result = append(result, ua) + } - return result, nil + return result, nil } diff --git a/internal/ui/cli/ask_questions.go b/internal/ui/cli/ask_questions.go new file mode 100644 index 0000000..2f0e311 --- /dev/null +++ b/internal/ui/cli/ask_questions.go @@ -0,0 +1,27 @@ +/* +Copyright © 2026 NAME HERE +*/ +package cli + +import ( + "github.com/spf13/cobra" + "github.com/tulashvili/MyDailyControlQuestions/internal/domain" + "github.com/tulashvili/MyDailyControlQuestions/internal/repo" + "github.com/tulashvili/MyDailyControlQuestions/internal/usecase" +) + +// askQuestionsCmd represents the askQuestions command +var askQuestionsCmd = &cobra.Command{ + Use: "ask", + Short: "Запустить опрос", + Long: ``, + Run: func(cmd *cobra.Command, args []string) { + repo := repo.NewSQLiteAnswerRepository(db) + ask := usecase.NewAskQuestion(repo) + io := QuestionAdapter{} + + if err := ask.Execute(domain.QUESTIONS, io); err != nil { + panic(err) + } + }, +} diff --git a/internal/ui/cli/question_adapter.go b/internal/ui/cli/question_adapter.go new file mode 100644 index 0000000..aee7e4d --- /dev/null +++ b/internal/ui/cli/question_adapter.go @@ -0,0 +1,25 @@ +package cli + +import ( + "fmt" + + "github.com/tulashvili/MyDailyControlQuestions/internal/domain" +) + +type QuestionAdapter struct{} + +func (QuestionAdapter) ShowQuestion(question domain.Question) { + fmt.Println("Категория:", question.Category) + fmt.Println("Вопрос:", question.Text) +} + +func (QuestionAdapter) ReadAnswer() (int, error) { + var answer int + _, err := fmt.Scanln(&answer) + + return answer, err +} + +func (QuestionAdapter) ShowValidationError(err error) { + fmt.Println("Ошибка:", err) +} diff --git a/internal/ui/cli/root.go b/internal/ui/cli/root.go index 821eb65..106d53d 100644 --- a/internal/ui/cli/root.go +++ b/internal/ui/cli/root.go @@ -9,6 +9,11 @@ import ( "github.com/spf13/cobra" ) +var ( + db *sql.DB + period int +) + var rootCmd = &cobra.Command{ Use: "MyDailyControlQuestions", Short: "Ежедневная саморефлексия путем ответа на важные для тебя вопросы по 5-ти бальной шкале", @@ -18,11 +23,15 @@ var rootCmd = &cobra.Command{ func Execute(conn *sql.DB) { db = conn + // get stats answer statsCmd.Flags().IntVarP(&period, "period", "p", 1, "Stats over the period") statsCmd.MarkFlagRequired("stats") + // get list question rootCmd.AddCommand(listQuestionsCmd) - // rootCmd.AddCommand(askQuestionsCmd) + + // run survey + rootCmd.AddCommand(askQuestionsCmd) if err := rootCmd.Execute(); err != nil { panic(err) diff --git a/internal/ui/cli/save_answer.go b/internal/ui/cli/save_answer.go deleted file mode 100644 index 1a339a5..0000000 --- a/internal/ui/cli/save_answer.go +++ /dev/null @@ -1,47 +0,0 @@ -/* -Copyright © 2026 NAME HERE -*/ -package cli - -import ( - "fmt" - - "github.com/spf13/cobra" - "github.com/tulashvili/MyDailyControlQuestions/internal/models" - "github.com/tulashvili/MyDailyControlQuestions/internal/service" -) - -// askQuestionsCmd represents the askQuestions command -var askQuestionsCmd = &cobra.Command{ - Use: "ask", - Short: "Запустить опрос", - Long: ``, - Run: func(cmd *cobra.Command, args []string) { - // askQuestion() - - }, -} - -// Ask a question to a client -func askQuestion(questions []models.Question) []models.UserAnswer { - var result []models.UserAnswer - - for _, question := range questions { - fmt.Println("Категория:", question.Category) - fmt.Println("Вопрос:", question.Text) - - var answer int - fmt.Scanln(&answer) - - err := service.ValidateAnswer(answer) - if err != nil { - fmt.Println(err) - fmt.Println("Введи значение заново") - fmt.Scanln(&answer) - } else { - userAnswer := service.CreateUserAnswer(question, answer) - result = append(result, userAnswer) - } - } - return result -} diff --git a/internal/ui/cli/stats.go b/internal/ui/cli/stats.go index 4167bea..5a2a11a 100644 --- a/internal/ui/cli/stats.go +++ b/internal/ui/cli/stats.go @@ -4,7 +4,6 @@ Copyright © 2026 NAME HERE package cli import ( - "database/sql" "fmt" "log" @@ -14,11 +13,6 @@ import ( "github.com/tulashvili/MyDailyControlQuestions/internal/usecase" ) -var ( - db *sql.DB - period int -) - // statsCmd represents the stats command var statsCmd = &cobra.Command{ Use: "stats", diff --git a/internal/usecase/ask_question.go b/internal/usecase/ask_question.go new file mode 100644 index 0000000..739f87b --- /dev/null +++ b/internal/usecase/ask_question.go @@ -0,0 +1,40 @@ +package usecase + +import ( + "github.com/tulashvili/MyDailyControlQuestions/internal/domain" + "github.com/tulashvili/MyDailyControlQuestions/internal/repo" +) + +type AskQuestionUC struct { + repo repo.AnswerRepository +} + +func NewAskQuestion(repo repo.AnswerRepository) *AskQuestionUC { + return &AskQuestionUC{repo: repo} +} + +func (aq *AskQuestionUC) Execute(questions []domain.Question, io QuestionIO) error { + for _, q := range questions { + for { + io.ShowQuestion(q) + + answer, err := io.ReadAnswer() + if err != nil { + return err + } + + if err := domain.ValidateAnswer(answer); err != nil { + io.ShowValidationError(err) + } + + userAnswer := domain.CreateUserAnswer(q, answer) + + if err := aq.repo.SaveAnswer(userAnswer); err != nil { + return err + } + + break + } + } + return nil +} diff --git a/internal/usecase/question.go b/internal/usecase/question.go new file mode 100644 index 0000000..6225cc1 --- /dev/null +++ b/internal/usecase/question.go @@ -0,0 +1,9 @@ +package usecase + +import "github.com/tulashvili/MyDailyControlQuestions/internal/domain" + +type QuestionIO interface { + ShowQuestion(domain.Question) + ReadAnswer() (int, error) + ShowValidationError(error) +} \ No newline at end of file diff --git a/internal/usecase/save_answer.go b/internal/usecase/save_answer.go deleted file mode 100644 index 8f3b06a..0000000 --- a/internal/usecase/save_answer.go +++ /dev/null @@ -1,18 +0,0 @@ -package usecase - -import ( - "github.com/tulashvili/MyDailyControlQuestions/internal/domain" - "github.com/tulashvili/MyDailyControlQuestions/internal/repo" -) - -type SaveAnswer struct { - repo repo.AnswerRepository -} - -func NewSaveAnswers(repo repo.AnswerRepository) *SaveAnswer { - return &SaveAnswer{repo: repo} -} - -func (sa *SaveAnswer) Execute(answers domain.UserAnswer) error { - return sa.repo.SaveAnswers(answers) -} From 8501b6befa5754d18fc142104fe63866d3d215a4 Mon Sep 17 00:00:00 2001 From: Omar Tulashvili Date: Tue, 27 Jan 2026 20:12:31 +0300 Subject: [PATCH 12/12] fix problem with type asweredAt at DB --- internal/repo/sqlite_repository.go | 14 ++++++++++++-- pkg/db/sqlitedb/sqlite.go | 4 ++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/internal/repo/sqlite_repository.go b/internal/repo/sqlite_repository.go index 426a9c8..a3e98df 100644 --- a/internal/repo/sqlite_repository.go +++ b/internal/repo/sqlite_repository.go @@ -3,6 +3,7 @@ package repo import ( "database/sql" "fmt" + "time" "github.com/tulashvili/MyDailyControlQuestions/internal/domain" ) @@ -26,7 +27,7 @@ func (r *SQLiteAnswerRepository) SaveAnswer(answer domain.UserAnswer) error { answer.QuestionCategory, answer.QuestionText, answer.Answer, - answer.AnsweredAt, + answer.AnsweredAt.UTC().Format(time.RFC3339Nano), ) return err } @@ -47,6 +48,7 @@ func (r *SQLiteAnswerRepository) GetAnswers(period int) ([]domain.UserAnswer, er defer rows.Close() var result []domain.UserAnswer + var answeredAtStr sql.NullString for rows.Next() { var ua domain.UserAnswer @@ -55,11 +57,19 @@ func (r *SQLiteAnswerRepository) GetAnswers(period int) ([]domain.UserAnswer, er &ua.QuestionCategory, &ua.QuestionText, &ua.Answer, - &ua.AnsweredAt, + &answeredAtStr, ); err != nil { return nil, err } + if answeredAtStr.Valid { + t, err := time.Parse(time.RFC3339Nano, answeredAtStr.String) + if err != nil { + return nil, err + } + ua.AnsweredAt = &t + } + result = append(result, ua) } diff --git a/pkg/db/sqlitedb/sqlite.go b/pkg/db/sqlitedb/sqlite.go index cc52782..fee0c9f 100644 --- a/pkg/db/sqlitedb/sqlite.go +++ b/pkg/db/sqlitedb/sqlite.go @@ -8,7 +8,7 @@ import ( "github.com/tulashvili/MyDailyControlQuestions/pkg/db" ) -// move this to migrate.go +// ToDo: move this to migrate.go func InitDB(path db.DataSource) (*sql.DB, error) { conn, err := sql.Open("sqlite3", path.SqlitePath) @@ -29,7 +29,7 @@ func CreateTable(conn *sql.DB) error { category TEXT NOT NULL, question TEXT NOT NULL, answer INTEGER NOT NULL, - answeredAt TEXT DEFAULT CURRENT_TIMESTAMP + answeredAt TEXT NOT NULL ); ` _, err := conn.Exec(query)