Skip to content

Commit

Permalink
Merge pull request #118 from LucaBernstein/112-list-dated-option
Browse files Browse the repository at this point in the history
Add 'dated' option to list command
  • Loading branch information
LucaBernstein authored Mar 30, 2022
2 parents 26d60f6 + 8617141 commit 69946f7
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 35 deletions.
55 changes: 42 additions & 13 deletions bot/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@ import (

dbWrapper "github.com/LucaBernstein/beancount-bot-tg/db"
"github.com/LucaBernstein/beancount-bot-tg/db/crud"
"github.com/LucaBernstein/beancount-bot-tg/helpers"
"github.com/go-co-op/gocron"
tb "gopkg.in/tucnak/telebot.v2"
)

type CMD struct {
Command string
Optional string
Optional []string
Handler func(m *tb.Message)
Help string
}
Expand Down Expand Up @@ -107,9 +108,9 @@ func (bc *BotController) commandMappings() []*CMD {
{Command: CMD_HELP, Handler: bc.commandHelp, Help: "List this command help"},
{Command: CMD_START, Handler: bc.commandStart, Help: "Give introduction into this bot"},
{Command: CMD_CANCEL, Handler: bc.commandCancel, Help: "Cancel any running commands or transactions"},
{Command: CMD_SIMPLE, Handler: bc.commandCreateSimpleTx, Help: "Record a simple transaction, defaults to today; Can be ommitted by sending amount directy", Optional: "YYYY-MM-DD"},
{Command: CMD_SIMPLE, Handler: bc.commandCreateSimpleTx, Help: "Record a simple transaction, defaults to today; Can be ommitted by sending amount directy", Optional: []string{"YYYY-MM-DD"}},
{Command: CMD_COMMENT, Handler: bc.commandAddComment, Help: "Add arbitrary text to transaction list"},
{Command: CMD_LIST, Handler: bc.commandList, Help: "List your recorded transactions", Optional: "archived"},
{Command: CMD_LIST, Handler: bc.commandList, Help: "List your recorded transactions", Optional: []string{"archived", "dated"}},
{Command: CMD_SUGGEST, Handler: bc.commandSuggestions, Help: "List, add or remove suggestions"},
{Command: CMD_CONFIG, Handler: bc.commandConfig, Help: "Bot configurations"},
{Command: CMD_ARCHIVE_ALL, Handler: bc.commandArchiveTransactions, Help: "Archive recorded transactions"},
Expand Down Expand Up @@ -149,8 +150,10 @@ func (bc *BotController) commandHelp(m *tb.Message) {
helpMsg += "\n"
}
var optional string
if cmd.Optional != "" {
optional = " [" + cmd.Optional + "]"
if cmd.Optional != nil {
for _, opt := range cmd.Optional {
optional += " [" + opt + "]"
}
}
helpMsg += fmt.Sprintf("/%s%s - %s", cmd.Command, optional, cmd.Help)
}
Expand Down Expand Up @@ -254,14 +257,25 @@ func (bc *BotController) commandAddComment(m *tb.Message) {
func (bc *BotController) commandList(m *tb.Message) {
bc.Logf(TRACE, m, "Listing transactions")
command := strings.Split(m.Text, " ")
if len(command) == 2 && command[1] != "archived" {
_, err := bc.Bot.Send(m.Sender, fmt.Sprintf("Your parameter after %s could not be recognized. Please try again with '/list' or '/list archived'.", command[0]), clearKeyboard())
if err != nil {
bc.Logf(ERROR, m, "Sending bot message failed: %s", err.Error())
isArchived := false
isDated := false
if len(command) > 1 {
for _, option := range command[1:] {
if option == "archived" {
isArchived = true
continue
} else if option == "dated" {
isDated = true
continue
}

_, err := bc.Bot.Send(m.Sender, fmt.Sprintf("The option '%s' could not be recognized. Please try again with '/list', with options added to the end separated by space.", option), clearKeyboard())
if err != nil {
bc.Logf(ERROR, m, "Sending bot message failed: %s", err.Error())
}
return
}
return
}
isArchived := len(command) == 2 && command[1] == "archived"
tx, err := bc.Repo.GetTransactions(m, isArchived)
if err != nil {
_, err := bc.Bot.Send(m.Sender, "Something went wrong retrieving your transactions: "+err.Error(), clearKeyboard())
Expand All @@ -279,12 +293,27 @@ func (bc *BotController) commandList(m *tb.Message) {
txMessages := []string{}
transactionsList := ""
for _, t := range tx {
if len(transactionsList)+len(t) >= TG_MAX_MSG_CHAR_LEN {
if len(transactionsList)+len(t.Tx) >= TG_MAX_MSG_CHAR_LEN {
bc.Logf(TRACE, m, "Listed messages extend max message length. Splitting into multiple messages.")
txMessages = append(txMessages, transactionsList)
transactionsList = ""
}
transactionsList += t + SEP
var dateComment string
if isDated {
tzOffset := bc.Repo.UserGetTzOffset(m)
timezoneOff := time.Duration(tzOffset) * time.Hour
// 2022-03-30T14:24:50.390084Z
dateParsed, err := time.Parse("2006-01-02T15:04:05Z", t.Date)
if err != nil {
bc.Logf(ERROR, m, "Parsing time failed: %s", err.Error())
bc.Logf(WARN, m, "Turning off dated option!")
isDated = false
} else {
date := dateParsed.Add(timezoneOff).Format(helpers.BEANCOUNT_DATE_FORMAT + " 15:04")
dateComment = "; recorded on " + date + SEP
}
}
transactionsList += dateComment + t.Tx + SEP
}
if transactionsList != "" {
txMessages = append(txMessages, transactionsList)
Expand Down
69 changes: 60 additions & 9 deletions bot/controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,19 +135,19 @@ func TestTransactionListMaxLength(t *testing.T) {
log.Fatal(err)
}
mock.
ExpectQuery(`SELECT "value" FROM "bot::transaction"`).
ExpectQuery(`SELECT "value", "created" FROM "bot::transaction"`).
WithArgs(chat.ID, false).
WillReturnRows(sqlmock.NewRows([]string{"value"}).AddRow(strings.Repeat("**********", 100)).AddRow(strings.Repeat("**********", 100))) // 1000 + 1000
WillReturnRows(sqlmock.NewRows([]string{"value", "created"}).AddRow(strings.Repeat("**********", 100), "").AddRow(strings.Repeat("**********", 100), "")) // 1000 + 1000
mock.
ExpectQuery(`SELECT "value" FROM "bot::transaction"`).
ExpectQuery(`SELECT "value", "created" FROM "bot::transaction"`).
WithArgs(chat.ID, false).
WillReturnRows(sqlmock.NewRows([]string{"value"}).
WillReturnRows(sqlmock.NewRows([]string{"value", "created"}).
// 5 * 1000
AddRow(strings.Repeat("**********", 100)).
AddRow(strings.Repeat("**********", 100)).
AddRow(strings.Repeat("**********", 100)).
AddRow(strings.Repeat("**********", 100)).
AddRow(strings.Repeat("**********", 100)),
AddRow(strings.Repeat("**********", 100), "").
AddRow(strings.Repeat("**********", 100), "").
AddRow(strings.Repeat("**********", 100), "").
AddRow(strings.Repeat("**********", 100), "").
AddRow(strings.Repeat("**********", 100), ""),
)

bc := NewBotController(db)
Expand All @@ -170,6 +170,57 @@ func TestTransactionListMaxLength(t *testing.T) {
if bot.LastSentWhat != strings.Repeat("**********", 100)+"\n" {
t.Errorf("Expected last message to contain last transaction as it flowed over the first message: %v", bot.LastSentWhat)
}

if err := mock.ExpectationsWereMet(); err != nil {
t.Errorf("there were unfulfilled expectations: %s", err)
}
}

func TestTransactionsListArchivedDated(t *testing.T) {
// create test dependencies
chat := &tb.Chat{ID: 12345}
db, mock, err := sqlmock.New()
crud.TEST_MODE = true
if err != nil {
log.Fatal(err)
}
bc := NewBotController(db)
bot := &MockBot{}
bc.AddBotAndStart(bot)

// successful date enrichment
mock.ExpectQuery(`SELECT "value", "created" FROM "bot::transaction"`).WithArgs(12345, true).
WillReturnRows(
sqlmock.NewRows([]string{"value", "created"}).
AddRow("tx1", "2022-03-30T14:24:50.390084Z").
AddRow("tx2", "2022-03-30T15:24:50.390084Z"),
)
mock.ExpectQuery(`SELECT "value" FROM "bot::userSetting"`).WithArgs(12345, helpers.USERSET_TZOFF).WillReturnRows(mock.NewRows([]string{"value"}))

bc.commandList(&tb.Message{Chat: chat, Text: "/testListCommand(ignored) archived dated"})

if bot.LastSentWhat != "; recorded on 2022-03-30 14:24\ntx1\n; recorded on 2022-03-30 15:24\ntx2\n" {
t.Errorf("Expected last message to contain transactions:\n%v", bot.LastSentWhat)
}

// fall back to undated if date parsing fails
mock.ExpectQuery(`SELECT "value", "created" FROM "bot::transaction"`).WithArgs(12345, true).
WillReturnRows(
sqlmock.NewRows([]string{"value", "created"}).
AddRow("tx1", "123456789").
AddRow("tx2", "456789123"),
)
mock.ExpectQuery(`SELECT "value" FROM "bot::userSetting"`).WithArgs(12345, helpers.USERSET_TZOFF).WillReturnRows(mock.NewRows([]string{"value"}))

bc.commandList(&tb.Message{Chat: chat, Text: "/testListCommand(ignored) archived dated"})

if bot.LastSentWhat != "tx1\ntx2\n" {
t.Errorf("Expected last message to contain transactions:\n%v", bot.LastSentWhat)
}

if err := mock.ExpectationsWereMet(); err != nil {
t.Errorf("there were unfulfilled expectations: %s", err)
}
}

func TestWritingComment(t *testing.T) {
Expand Down
21 changes: 15 additions & 6 deletions db/crud/bot_transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,15 @@ func (r *Repo) RecordTransaction(chatId int64, tx string) error {
return err
}

func (r *Repo) GetTransactions(m *tb.Message, isArchived bool) ([]string, error) {
type TransactionResult struct {
Tx string
Date string
}

func (r *Repo) GetTransactions(m *tb.Message, isArchived bool) ([]*TransactionResult, error) {
LogDbf(r, helpers.TRACE, m, "Getting transactions")
rows, err := r.db.Query(`
SELECT "value" FROM "bot::transaction"
SELECT "value", "created" FROM "bot::transaction"
WHERE "tgChatId" = $1 AND "archived" = $2
ORDER BY "created" ASC
`, m.Chat.ID, isArchived)
Expand All @@ -24,16 +29,20 @@ func (r *Repo) GetTransactions(m *tb.Message, isArchived bool) ([]string, error)
}
defer rows.Close()

allTransactionsMessage := []string{}
allTransactions := []*TransactionResult{}
var transactionString string
var created string
for rows.Next() {
err = rows.Scan(&transactionString)
err = rows.Scan(&transactionString, &created)
if err != nil {
return nil, err
}
allTransactionsMessage = append(allTransactionsMessage, transactionString)
allTransactions = append(allTransactions, &TransactionResult{
Tx: transactionString,
Date: created,
})
}
return allTransactionsMessage, nil
return allTransactions, nil
}

func (r *Repo) ArchiveTransactions(m *tb.Message) error {
Expand Down
19 changes: 12 additions & 7 deletions db/crud/bot_transaction_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (

"github.com/DATA-DOG/go-sqlmock"
"github.com/LucaBernstein/beancount-bot-tg/db/crud"
"github.com/LucaBernstein/beancount-bot-tg/helpers"
tb "gopkg.in/tucnak/telebot.v2"
)

Expand All @@ -26,18 +25,24 @@ func TestRecordGetTransaction(t *testing.T) {
t.Errorf("No error should have been returned")
}

mock.ExpectQuery(`SELECT "value" FROM "bot::transaction"`).WithArgs(1122, true).
mock.ExpectQuery(`SELECT "value", "created" FROM "bot::transaction"`).WithArgs(1122, true).
WillReturnRows(
sqlmock.NewRows([]string{"value"}).
AddRow("tx1").
AddRow("tx2"),
sqlmock.NewRows([]string{"value", "created"}).
AddRow("tx1", "2022-03-30 14:24:50.390084").
AddRow("tx2", "2022-03-31 14:24:50.390084"),
)
txs, err := r.GetTransactions(&tb.Message{Chat: &tb.Chat{ID: 1122}}, true)
if err != nil {
t.Errorf("No error should have been returned: %s", err.Error())
}
if !helpers.ArraysEqual(txs, []string{"tx1", "tx2"}) {
t.Errorf("Resulting transactions list should contain expected values: %v", txs)
if len(txs) != 2 {
t.Errorf("Resulting transactions list should contain 2 values: %v", len(txs))
}
if txs[0].Tx != "tx1" || txs[1].Tx != "tx2" {
t.Errorf("Resulting transactions list should contain expected Txs: %v", txs)
}
if txs[0].Date != "2022-03-30 14:24:50.390084" || txs[1].Date != "2022-03-31 14:24:50.390084" {
t.Errorf("Resulting transactions list should contain expected Dates: %v", txs)
}

if err := mock.ExpectationsWereMet(); err != nil {
Expand Down

0 comments on commit 69946f7

Please sign in to comment.