Skip to content

Commit

Permalink
Introduce sql transaction for data entry (#9)
Browse files Browse the repository at this point in the history
Signed-off-by: Masudur Rahman <masudjuly02@gmail.com>
  • Loading branch information
masudur-rahman authored Apr 4, 2024
1 parent a4e2382 commit 41412be
Show file tree
Hide file tree
Showing 24 changed files with 876 additions and 69 deletions.
13 changes: 7 additions & 6 deletions configs/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/masudur-rahman/expense-tracker-bot/modules/google"
"github.com/masudur-rahman/expense-tracker-bot/services/all"

"github.com/masudur-rahman/database"
isql "github.com/masudur-rahman/database/sql"
"github.com/masudur-rahman/database/sql/postgres"
sqlib "github.com/masudur-rahman/database/sql/postgres/lib"
Expand All @@ -27,7 +28,7 @@ func InitiateDatabaseConnection(ctx context.Context) error {
if err != nil {
return err
}
return initializeSQLServices(db)
return initializeSQLServices(database.UnitOfWork{SQL: db})
case DatabaseSQLite, "":
if cfg.SQLite.SyncToDrive {
if !cfg.SQLite.DisableSyncFromDrive {
Expand All @@ -43,7 +44,7 @@ func InitiateDatabaseConnection(ctx context.Context) error {
if err != nil {
return err
}
return initializeSQLServices(db)
return initializeSQLServices(database.UnitOfWork{SQL: db})
default:
return fmt.Errorf("unknown database type")
}
Expand All @@ -58,11 +59,11 @@ func getSQLiteDatabase(ctx context.Context) (isql.Database, error) {
return sqlite.NewSQLite(ctx, conn), nil
}

func initializeSQLServices(db isql.Database) error {
if err := syncTables(db); err != nil {
func initializeSQLServices(uow database.UnitOfWork) error {
if err := syncTables(uow.SQL); err != nil {
return err
}
all.InitiateSQLServices(db, logr.DefaultLogger)
all.InitiateSQLServices(uow, logr.DefaultLogger)

return all.GetServices().Txn.UpdateTxnCategories()
}
Expand Down Expand Up @@ -100,7 +101,7 @@ func pingPostgresDatabasePeriodically(ctx context.Context, cfg sqlib.PostgresCon
}

db := postgres.NewPostgres(ctx, conn).ShowSQL(true)
all.InitiateSQLServices(db, logger)
all.InitiateSQLServices(database.UnitOfWork{SQL: db}, logger)
logger.Infow("New connection established")
}
}
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ go 1.21
require (
github.com/graphql-go/graphql v0.8.1
github.com/jedib0t/go-pretty/v6 v6.4.6
github.com/masudur-rahman/database v1.1.2
github.com/masudur-rahman/database v1.1.3
github.com/masudur-rahman/go-oneliners v1.0.0
github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/rs/xid v1.5.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -308,8 +308,8 @@ github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgx
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/masudur-rahman/database v1.1.2 h1:lhcA38Tpb9WEMXLUpbkYDNOdrraTdjOlo/pV2YxyrLo=
github.com/masudur-rahman/database v1.1.2/go.mod h1:E1t3JHMhB2OZUxZ7OSPwcbY35UyTwlusjPc9kX0cZPE=
github.com/masudur-rahman/database v1.1.3 h1:HlFWL4q2GxsTmBw6e/3Mw2aw4KS9M7DD63mlRpEkRKg=
github.com/masudur-rahman/database v1.1.3/go.mod h1:E1t3JHMhB2OZUxZ7OSPwcbY35UyTwlusjPc9kX0cZPE=
github.com/masudur-rahman/go-oneliners v1.0.0 h1:77T+jsdp0TGEWp//OAk6XkzwO/uAmyasjZJfdYFfdnw=
github.com/masudur-rahman/go-oneliners v1.0.0/go.mod h1:5i+VnhZ/R+APsPpo1lZyw19oiO90zXYvKYMLSrFyKec=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
Expand Down
39 changes: 39 additions & 0 deletions pkg/levenshtein.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package pkg

import "strings"

// LevenshteinDistance compares two strings and returns the levenshtein distance between them.
func LevenshteinDistance(s, t string, ignoreCase bool) int {
if ignoreCase {
s = strings.ToLower(s)
t = strings.ToLower(t)
}
d := make([][]int, len(s)+1)
for i := range d {
d[i] = make([]int, len(t)+1)
}
for i := range d {
d[i][0] = i
}
for j := range d[0] {
d[0][j] = j
}
for j := 1; j <= len(t); j++ {
for i := 1; i <= len(s); i++ {
if s[i-1] == t[j-1] {
d[i][j] = d[i-1][j-1]
} else {
mn := d[i-1][j]
if d[i][j-1] < mn {
mn = d[i][j-1]
}
if d[i-1][j-1] < mn {
mn = d[i-1][j-1]
}
d[i][j] = mn + 1
}
}

}
return d[len(s)][len(t)]
}
30 changes: 30 additions & 0 deletions pkg/levenshtein_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package pkg_test

import (
"fmt"
"strings"

"github.com/masudur-rahman/expense-tracker-bot/models"
"github.com/masudur-rahman/expense-tracker-bot/pkg"
)

func ExampleLevenshteinDistance() {
a := "snack"

md := 10000
var rs string

for _, subcat := range models.TxnSubcategories {
ld := min(pkg.LevenshteinDistance(subcat.ID, a, true),
pkg.LevenshteinDistance(subcat.Name, a, true))
suggestByLevenshtein := ld <= 3
suggestByPrefix := strings.HasPrefix(strings.ToLower(subcat.Name), strings.ToLower(a))
if suggestByLevenshtein && ld < md || suggestByPrefix {
md = ld
rs = subcat.Name
}
}

fmt.Printf("Distance between `%v` and `%v` is %v\n", a, rs, md)
// Output: Hello
}
7 changes: 6 additions & 1 deletion repos/accounts.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
package repos

import "github.com/masudur-rahman/expense-tracker-bot/models"
import (
"github.com/masudur-rahman/expense-tracker-bot/models"

"github.com/masudur-rahman/database"
)

type AccountsRepository interface {
WithUnitOfWork(uow database.UnitOfWork) AccountsRepository
GetAccountByShortName(userID int64, shortName string) (*models.Account, error)
ListAccounts(userID int64) ([]models.Account, error)
ListAccountsByType(userID int64, typ models.AccountType) ([]models.Account, error)
Expand Down
9 changes: 9 additions & 0 deletions repos/accounts/accounts.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import (

"github.com/masudur-rahman/expense-tracker-bot/infra/logr"
"github.com/masudur-rahman/expense-tracker-bot/models"
"github.com/masudur-rahman/expense-tracker-bot/repos"

"github.com/masudur-rahman/database"
isql "github.com/masudur-rahman/database/sql"
)

Expand All @@ -21,6 +23,13 @@ func NewSQLAccountsRepository(db isql.Database, logger logr.Logger) *SQLAccounts
}
}

func (a *SQLAccountsRepository) WithUnitOfWork(uow database.UnitOfWork) repos.AccountsRepository {
return &SQLAccountsRepository{
db: uow.SQL.Table("account"),
logger: a.logger,
}
}

func (a *SQLAccountsRepository) GetAccountByShortName(userID int64, shortName string) (*models.Account, error) {
a.logger.Infow("get account by account id", "account id", shortName)
var acc models.Account
Expand Down
7 changes: 6 additions & 1 deletion repos/transaction.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
package repos

import "github.com/masudur-rahman/expense-tracker-bot/models"
import (
"github.com/masudur-rahman/expense-tracker-bot/models"

"github.com/masudur-rahman/database"
)

type TransactionRepository interface {
WithUnitOfWork(uow database.UnitOfWork) TransactionRepository
AddTransaction(txn models.Transaction) error
ListTransactionsByCategory(userID int64, catID string) ([]models.Transaction, error)
ListTransactions(filter models.Transaction) ([]models.Transaction, error)
Expand Down
17 changes: 13 additions & 4 deletions repos/transaction/transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ import (

"github.com/masudur-rahman/expense-tracker-bot/infra/logr"
"github.com/masudur-rahman/expense-tracker-bot/models"
"github.com/masudur-rahman/expense-tracker-bot/repos"

"github.com/masudur-rahman/database"
isql "github.com/masudur-rahman/database/sql"
)

Expand All @@ -23,6 +25,13 @@ func NewSQLTransactionRepository(db isql.Database, logger logr.Logger) *SQLTrans
}
}

func (t *SQLTransactionRepository) WithUnitOfWork(uow database.UnitOfWork) repos.TransactionRepository {
return &SQLTransactionRepository{
db: uow.SQL.Table("transaction"),
logger: t.logger,
}
}

func (t *SQLTransactionRepository) AddTransaction(txn models.Transaction) error {
t.logger.Infow("inserting new transaction")
if txn.Timestamp == 0 {
Expand Down Expand Up @@ -55,9 +64,9 @@ func (t *SQLTransactionRepository) ListTransactionsByTime(userID int64, txnType
return txns, err
}

func (ts *SQLTransactionRepository) GetTxnCategoryName(catID string) (string, error) {
func (t *SQLTransactionRepository) GetTxnCategoryName(catID string) (string, error) {
cat := models.TxnCategory{}
has, err := ts.db.Table("txn_category").ID(catID).FindOne(&cat)
has, err := t.db.Table("txn_category").ID(catID).FindOne(&cat)
if err != nil {
return "", err
} else if !has {
Expand All @@ -74,9 +83,9 @@ func (t *SQLTransactionRepository) ListTxnCategories() ([]models.TxnCategory, er
return cats, err
}

func (ts *SQLTransactionRepository) GetTxnSubcategoryName(subcatID string) (string, error) {
func (t *SQLTransactionRepository) GetTxnSubcategoryName(subcatID string) (string, error) {
subcat := models.TxnSubcategory{}
has, err := ts.db.Table("txn_subcategory").ID(subcatID).FindOne(&subcat)
has, err := t.db.Table("txn_subcategory").ID(subcatID).FindOne(&subcat)
if err != nil {
return "", err
} else if !has {
Expand Down
7 changes: 6 additions & 1 deletion repos/user.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
package repos

import "github.com/masudur-rahman/expense-tracker-bot/models"
import (
"github.com/masudur-rahman/expense-tracker-bot/models"

"github.com/masudur-rahman/database"
)

type DebtorCreditorRepository interface {
WithUnitOfWork(uow database.UnitOfWork) DebtorCreditorRepository
GetDebtorCreditorByID(id int64) (*models.DebtorsCreditors, error)
GetDebtorCreditorByName(userID int64, name string) (*models.DebtorsCreditors, error)
ListDebtorCreditors(userID int64) ([]models.DebtorsCreditors, error)
Expand Down
9 changes: 9 additions & 0 deletions repos/user/drcr.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import (

"github.com/masudur-rahman/expense-tracker-bot/infra/logr"
"github.com/masudur-rahman/expense-tracker-bot/models"
"github.com/masudur-rahman/expense-tracker-bot/repos"

"github.com/masudur-rahman/database"
isql "github.com/masudur-rahman/database/sql"
)

Expand All @@ -22,6 +24,13 @@ func NewSQLDebtorCreditorRepository(db isql.Database, logger logr.Logger) *SQLDe
}
}

func (u *SQLDebtorCreditorRepository) WithUnitOfWork(uow database.UnitOfWork) repos.DebtorCreditorRepository {
return &SQLDebtorCreditorRepository{
db: uow.SQL.Table("debtors_creditors"),
logger: u.logger,
}
}

func (u *SQLDebtorCreditorRepository) GetDebtorCreditorByID(id int64) (*models.DebtorsCreditors, error) {
u.logger.Infow("finding user by id", "id", id)
var user models.DebtorsCreditors
Expand Down
16 changes: 8 additions & 8 deletions services/all/all.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
txnsvc "github.com/masudur-rahman/expense-tracker-bot/services/transaction"
usersvc "github.com/masudur-rahman/expense-tracker-bot/services/user"

isql "github.com/masudur-rahman/database/sql"
"github.com/masudur-rahman/database"
)

type Services struct {
Expand All @@ -29,17 +29,17 @@ func GetServices() *Services {
return svc
}

func InitiateSQLServices(db isql.Database, logger logr.Logger) {
userRepo := user.NewSQLUserRepository(db, logger)
accRepo := accounts.NewSQLAccountsRepository(db, logger)
drCrRepo := user.NewSQLDebtorCreditorRepository(db, logger)
txnRepo := transaction.NewSQLTransactionRepository(db, logger)
eventRepo := event.NewSQLEventRepository(db, logger)
func InitiateSQLServices(uow database.UnitOfWork, logger logr.Logger) {
userRepo := user.NewSQLUserRepository(uow.SQL, logger)
accRepo := accounts.NewSQLAccountsRepository(uow.SQL, logger)
drCrRepo := user.NewSQLDebtorCreditorRepository(uow.SQL, logger)
txnRepo := transaction.NewSQLTransactionRepository(uow.SQL, logger)
eventRepo := event.NewSQLEventRepository(uow.SQL, logger)

userSvc := usersvc.NewUserService(userRepo)
accSvc := accsvc.NewAccountService(accRepo)
drCrSvc := usersvc.NewDebtorCreditorService(drCrRepo)
txnSvc := txnsvc.NewTxnService(accRepo, drCrRepo, txnRepo, eventRepo)
txnSvc := txnsvc.NewTxnService(uow, accRepo, drCrRepo, txnRepo, eventRepo)
eventSvc := eventsvc.NewEventService(eventRepo)

svc = &Services{
Expand Down
Loading

0 comments on commit 41412be

Please sign in to comment.