index — walletdrain @ 5d39f632a1e5e772b264f03d1ca75202185340c3

Little app to track my spendings, for the time being only for music

display works, no editing yet
crispy-caesus 114518720+crispy-caesus@users.noreply.github.com
Wed, 15 Jan 2025 13:19:20 +0100
commit

5d39f632a1e5e772b264f03d1ca75202185340c3

parent

5f1d8fd78ee8974f9b98b5fcc352ead370265b9c

7 files changed, 181 insertions(+), 288 deletions(-)

jump to
D app.db

          
M features.mdfeatures.md

@@ -9,3 +9,33 @@ - [ ] possibly receit

- [ ] purchase date - [ ] budget +--- +### db schema +```sql +CREATE TABLE music ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + external_ids TEXT, + name TEXT NOT NULL, + price FLOAT, + currency TEXT, + returned_price FLOAT, + returned_currency TEXT, + category TEXT, + seller TEXT, + note TEXT, + purchase_date TEXT); +``` + +### struct +```go +struct musicItem { + id int, + external_ids string, + name string, + price float, + currency string, + seller string, + note string, + purchase_date string, +} +```
M main.gomain.go

@@ -1,314 +1,87 @@

package main import ( - "database/sql" - "fmt" - "log" - "fyne.io/fyne/v2" - "fyne.io/fyne/v2/app" - "fyne.io/fyne/v2/container" - "fyne.io/fyne/v2/widget" - _ "github.com/mattn/go-sqlite3" -) - -type TableData struct { - ID int - name string - price float32 - currency string - returned_price float32 - returned_currency string - category string - seller string - note string -} - -func initDB(dbPath string) *sql.DB { - db, err := sql.Open("sqlite3", dbPath) - if err != nil { - log.Fatal(err) - } - - createTableSQL := ` - CREATE TABLE IF NOT EXISTS music ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - name TEXT NOT NULL, - price FLOAT, - currency TEXT, - returned_price FLOAT, - returned_currency TEXT, - category TEXT, - seller TEXT, - note TEXT - );` - - _, err = db.Exec(createTableSQL) - if err != nil { - log.Fatal(err) - } - - return db -} + //"fmt" + "strconv" -func getItemsForCategory(db *sql.DB, category string) []TableData { - rows, err := db.Query("SELECT * FROM music WHERE category = ?", category) - if err != nil { - log.Printf("Query error: %v", err) - return nil - } - defer rows.Close() + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/app" + "fyne.io/fyne/v2/container" + "fyne.io/fyne/v2/layout" + "fyne.io/fyne/v2/widget" +) - var items []TableData - for rows.Next() { - var item TableData - err := rows.Scan( - &item.ID, - &item.name, - &item.price, - &item.currency, - &item.returned_price, - &item.returned_currency, - &item.category, - &item.seller, - &item.note, - ) - if err != nil { - log.Printf("Row scan error: %v", err) - continue - } - items = append(items, item) - } - return items -} +func tableView(w fyne.Window, category string) { + items := loadCategory(category) -func updateItem(db *sql.DB, item TableData) error { - _, err := db.Exec(` - UPDATE music - SET name = ?, - price = ?, - currency = ?, - returned_price = ?, - returned_currency = ?, - category = ?, - seller = ?, - note = ? - WHERE id = ?`, - item.name, - item.price, - item.currency, - item.returned_price, - item.returned_currency, - item.category, - item.seller, - item.note, - item.ID, - ) - if err != nil { - log.Printf("Update error: %v", err) - } - return err -} - -func createSectionView(db *sql.DB, category string, onBack func()) *fyne.Container { - data := getItemsForCategory(db, category) - - // Create edit panel widgets - var currentItem TableData // Store the currently selected item - - nameEntry := widget.NewEntry() - priceEntry := widget.NewEntry() - currencyEntry := widget.NewEntry() - returnedPriceEntry := widget.NewEntry() - returnedCurrencyEntry := widget.NewEntry() - sellerEntry := widget.NewEntry() - noteEntry := widget.NewMultiLineEntry() - - refreshData := func() { - data = getItemsForCategory(db, category) - } - - // Set up column headers - headers := []string{"Name", "Seller", "Price"} - columnWidths := []float32{300, 200, 150} - - table := widget.NewTable( + table := widget.NewTableWithHeaders( func() (int, int) { - return len(data) + 1, len(headers) // +1 for header row + return len(items), 4 }, func() fyne.CanvasObject { - label := widget.NewLabel("") - label.Wrapping = fyne.TextWrapWord - return label + return widget.NewLabel("uhh, something went wrong") }, - func(i widget.TableCellID, o fyne.CanvasObject) { - label := o.(*widget.Label) - label.TextStyle = fyne.TextStyle{} // Reset text style - - // Header row - if i.Row == 0 { - label.TextStyle = fyne.TextStyle{Bold: true} - if i.Col < len(headers) { - label.SetText(headers[i.Col]) - } - return - } - - // Data rows - dataRow := i.Row - 1 // Adjust for header row - if dataRow < len(data) { - item := data[dataRow] - switch i.Col { + func(id widget.TableCellID, cell fyne.CanvasObject) { + label := cell.(*widget.Label) + switch id.Col { case 0: - label.SetText(item.name) + label.SetText(items[id.Row].name) case 1: - label.SetText(item.seller) + label.SetText(strconv.FormatFloat(items[id.Row].price, 'f', 2, 64) + " " + items[id.Row].currency) case 2: - label.SetText(fmt.Sprintf("%.2f %s", item.price, item.currency)) - } + label.SetText(items[id.Row].seller) + case 3: + label.SetText(items[id.Row].purchase_date) } }, ) - - // Set column widths - for i, width := range columnWidths { - table.SetColumnWidth(i, width) + table.SetColumnWidth(0, 100) + table.SetColumnWidth(1, 100) + table.SetColumnWidth(2, 100) + table.SetColumnWidth(3, 100) + + /* + headers := []string{"Name", "Price", "Seller", "Purchase Date"} + for i, header := range headers { + fmt.Printf("i: %d\nheader: %s", i, header) + table.UpdateHeader(widget.TableCellID{Row: -1, Col: i}, + widget.NewLabel(header)) } - - editPanel := container.NewVBox( - widget.NewLabel("Edit Item"), - widget.NewLabel("Name:"), - nameEntry, - widget.NewLabel("Price:"), - priceEntry, - widget.NewLabel("Currency:"), - currencyEntry, - widget.NewLabel("Returned Price:"), - returnedPriceEntry, - widget.NewLabel("Returned Currency:"), - returnedCurrencyEntry, - widget.NewLabel("Seller:"), - sellerEntry, - widget.NewLabel("Note:"), - noteEntry, - widget.NewButton("Save", func() { - var price, returnedPrice float32 - fmt.Sscanf(priceEntry.Text, "%f", &price) - fmt.Sscanf(returnedPriceEntry.Text, "%f", &returnedPrice) - - item := TableData{ - ID: currentItem.ID, - name: nameEntry.Text, - price: price, - currency: currencyEntry.Text, - returned_price: returnedPrice, - returned_currency: returnedCurrencyEntry.Text, - category: category, - seller: sellerEntry.Text, - note: noteEntry.Text, - } - - err := updateItem(db, item) - if err != nil { - log.Printf("Error updating item: %v", err) - } else { - refreshData() - table.Refresh() - } - }), - ) - - table.OnSelected = func(id widget.TableCellID) { - if id.Row > 0 && (id.Row-1) < len(data) { - currentItem = data[id.Row-1] // Store the currently selected item - nameEntry.SetText(currentItem.name) - priceEntry.SetText(fmt.Sprintf("%.2f", currentItem.price)) - currencyEntry.SetText(currentItem.currency) - returnedPriceEntry.SetText(fmt.Sprintf("%.2f", currentItem.returned_price)) - returnedCurrencyEntry.SetText(currentItem.returned_currency) - sellerEntry.SetText(currentItem.seller) - noteEntry.SetText(currentItem.note) - } - } - - header := container.NewHBox( - widget.NewButton("Back", onBack), - widget.NewLabel(fmt.Sprintf("Category: %s", category)), - ) - - split := container.NewHSplit(table, editPanel) - split.SetOffset(0.7) // Give table 70% of the width - - return container.NewBorder(header, nil, nil, nil, split) + */ + + w.SetContent(table) } -func main() { - myApp := app.New() - window := myApp.NewWindow("walletdrain") - - db := initDB("./app.db") - defer db.Close() +func mainMenu(w fyne.Window) { + musicButton := widget.NewButton("Music", func() {tableView(w, "music")}) + groceriesButton := widget.NewButton("Groceries", func() {tableView(w, "groceries")}) + recurringButton := widget.NewButton("Recurring", func() {tableView(w, "recurring")}) + otherButton := widget.NewButton("Other", func() {tableView(w, "other")}) - var count int - err := db.QueryRow("SELECT COUNT(*) FROM music").Scan(&count) - if err != nil { - log.Fatal(err) - } + content := container.New( + layout.NewVBoxLayout(), + layout.NewSpacer(), + musicButton, + groceriesButton, + recurringButton, + otherButton, + layout.NewSpacer(), + ) - if count == 0 { - sampleData := []TableData{ - {name: "Sample Album 1", price: 29.99, currency: "USD", returned_price: 0, returned_currency: "", category: "vinyl", seller: "Record Store", note: "New condition"}, - {name: "Sample Album 2", price: 19.99, currency: "EUR", returned_price: 15.00, returned_currency: "EUR", category: "cd", seller: "Online Shop", note: "Used"}, - } + w.SetContent(container.New( + layout.NewHBoxLayout(), + layout.NewSpacer(), + content, + layout.NewSpacer(), + )) +} - for _, item := range sampleData { - _, err := db.Exec( - "INSERT INTO music (name, price, currency, returned_price, returned_currency, category, seller, note) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", - item.name, item.price, item.currency, item.returned_price, item.returned_currency, item.category, item.seller, item.note, - ) - if err != nil { - log.Printf("Error inserting sample data: %v", err) - } - } - } +func main() { + a := app.New() + w := a.NewWindow("Hello World") - content := container.NewStack() + mainMenu(w) - var categorySelect *fyne.Container - categorySelect = container.NewVBox( - widget.NewLabel("Select a Category"), - widget.NewButton("Vinyl", func() { - content.Objects = []fyne.CanvasObject{ - createSectionView(db, "vinyl", func() { - content.Objects = []fyne.CanvasObject{categorySelect} - content.Refresh() - }), - } - content.Refresh() - }), - widget.NewButton("CD", func() { - content.Objects = []fyne.CanvasObject{ - createSectionView(db, "cd", func() { - content.Objects = []fyne.CanvasObject{categorySelect} - content.Refresh() - }), - } - content.Refresh() - }), - widget.NewButton("Cassette", func() { - content.Objects = []fyne.CanvasObject{ - createSectionView(db, "cassette", func() { - content.Objects = []fyne.CanvasObject{categorySelect} - content.Refresh() - }), - } - content.Refresh() - }), - ) - - content.Objects = []fyne.CanvasObject{categorySelect} - - window.SetContent(content) - window.Resize(fyne.NewSize(1024, 768)) - window.ShowAndRun() + w.ShowAndRun() }
A sql.go

@@ -0,0 +1,77 @@

+package main + +import ( + "database/sql" + "fmt" + + _ "github.com/mattn/go-sqlite3" +) + +func openDB(DBName string) (*sql.DB, error) { + db, err := sql.Open("sqlite3", DBName + ".db") + if err != nil { + panic("couldn't open db") + } + + return db, nil +} + +func createTables(db *sql.DB, tableName string) (err error) { + switch tableName { + case "music": + _, err = db.Exec(`CREATE TABLE IF NOT EXISTS music ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + external_ids TEXT, + name TEXT NOT NULL, + price FLOAT, + currency TEXT, + seller TEXT, + note TEXT, + purchase_date TEXT);`) + if err != nil { + panic(err) + return err + } + return nil + default: + panic("wrong table name") + } +} + + +func loadCategory(category string) []musicItem { + db, err := openDB("walletdrain") + if err != nil { + panic("couldn't open db") + } + defer db.Close() + + createTables(db, "music") + + query := fmt.Sprintf("SELECT * FROM %s", category) + + rows, err := db.Query(query) + if err != nil { + panic(err) + } + defer rows.Close() + + var results []musicItem + for rows.Next() { + var result musicItem + err := rows.Scan( + &result.id, + &result.external_ids, + &result.name, + &result.price, + &result.currency, + &result.seller, + &result.note, + &result.purchase_date) + if err != nil { + panic("couldn't scan sql results") + } + results = append(results, result) + } + return results +}
A structs.go

@@ -0,0 +1,13 @@

+package main + +type musicItem struct { + id int + external_ids string + name string + price float64 + currency string + seller string + note string + purchase_date string +} +