Compare commits

...

10 Commits

13 changed files with 326 additions and 146 deletions

View File

@ -4,9 +4,9 @@ import (
"database/sql" "database/sql"
"errors" "errors"
"fmt" "fmt"
"os"
"os/exec" "os/exec"
"sevenkeys/database" "sevenkeys/database"
"sevenkeys/delverlens"
"sevenkeys/logic" "sevenkeys/logic"
) )
@ -15,7 +15,7 @@ var output string
func MainCliLoop(db *sql.DB) { func MainCliLoop(db *sql.DB) {
var command string var command string
var selectedStorageArea database.StorageArea //var selectedStorageAreaName string
var cardLocation database.CardLocation var cardLocation database.CardLocation
var insertSearchCriteria logic.SearchCriteria = logic.SearchCriteria{ var insertSearchCriteria logic.SearchCriteria = logic.SearchCriteria{
@ -26,17 +26,18 @@ func MainCliLoop(db *sql.DB) {
} }
var insertSearchOptions logic.InsertSearchOptions var insertSearchOptions logic.InsertSearchOptions
// TODO: Make these do something and add the ability to modify them // TODO: Add the ability to modify this
var locateSearchCriteria logic.SearchCriteria = logic.SearchCriteria{ var locateSearchCriteria logic.SearchCriteria = logic.SearchCriteria{
SetCode: "", SetCode: "",
Foil: logic.Either, Foil: logic.True,
Promo: logic.Either, Promo: logic.Either,
Language: "en", Language: "en",
} }
for { for {
ShowSplashScreen() ShowSplashScreen()
showStorageInfo(os.Stdout, selectedStorageArea) // TODO: Fix this
//showStorageInfo(os.Stdout, selectedStorageAreaName)
showInsertSearchCriteria(insertSearchCriteria) showInsertSearchCriteria(insertSearchCriteria)
showSelectedCard() showSelectedCard()
showCopiesInserted() showCopiesInserted()
@ -56,10 +57,14 @@ func MainCliLoop(db *sql.DB) {
logic.Check(err) logic.Check(err)
break break
case "a", "area": case "a", "area":
area, err := logic.SelectStorageArea(db) options, err := logic.GetStorageAreaSearchOptions(db)
logic.Check(err) logic.Check(err)
selectedStorageArea = area
cardLocation.StorageAreaId = area.Id id, _, err := logic.GenericSearch(options)
logic.Check(err)
//selectedStorageAreaName = name
cardLocation.StorageAreaId = id
break break
case "c", "criteria": case "c", "criteria":
insertSearchCriteria = getSearchCriteria() insertSearchCriteria = getSearchCriteria()
@ -71,8 +76,9 @@ func MainCliLoop(db *sql.DB) {
var previousCardPrintingId = cardLocation.CardPrintingId var previousCardPrintingId = cardLocation.CardPrintingId
pk, err := logic.GenericSearch(insertSearchOptions) pk, searchLine, err := logic.GenericSearch(insertSearchOptions)
cardLocation.CardPrintingId = pk cardLocation.CardPrintingId = pk
selectedCardPrintingSearchLine = searchLine
var exitError *exec.ExitError var exitError *exec.ExitError
if errors.As(err, &exitError) { if errors.As(err, &exitError) {
@ -86,7 +92,18 @@ func MainCliLoop(db *sql.DB) {
} }
break break
case "i", "insert": case "i", "insert":
insertSelectedCard(db, selectedStorageArea, cardLocation) err := logic.StoreCard(db, cardLocation)
logic.Check(err)
copiesInserted++
break
case "d", "delverlens":
filename := GetStringResponse("Filename:")
cards, err := delverlens.ParseExportFile(filename)
logic.Check(err)
err = logic.ImportDelverLensCards(cards)
logic.Check(err)
break break
case "l", "locate": case "l", "locate":
filename := GetStringResponse("Filename:") filename := GetStringResponse("Filename:")
@ -96,11 +113,32 @@ func MainCliLoop(db *sql.DB) {
locations, err := logic.LocateCards(db, cardNames, locateSearchCriteria) locations, err := logic.LocateCards(db, cardNames, locateSearchCriteria)
logic.Check(err) logic.Check(err)
for _, location := range locations { if len(locations) == 0 {
fmt.Println(location) fmt.Println("No results found")
fmt.Scanln()
break
} }
fmt.Scanln()
for _, location := range locations {
description := logic.GetLocationDescription(location)
fmt.Println(description)
for true {
todo := GetStringResponse("TODO:")
if todo == "r" {
logic.RemoveFromStorage(db, location)
break
} else if todo == "n" {
break
} else {
fmt.Printf("Unrecognized option: %s\n", todo)
}
}
}
fmt.Println("Though this query has ended, its relics still slumber in New Argive.")
fmt.Scanln()
break break
default: default:
fmt.Println("Unrecognized command:", command) fmt.Println("Unrecognized command:", command)

View File

@ -1,11 +1,9 @@
package cli package cli
import ( import (
"database/sql"
"fmt" "fmt"
"io" "io"
"sevenkeys/database" "sevenkeys/database"
"sevenkeys/logic"
) )
var ( var (
@ -36,20 +34,3 @@ func showStorageInfo(w io.Writer, area database.StorageArea) {
func showCopiesInserted() { func showCopiesInserted() {
fmt.Println("Copies inserted:", copiesInserted) fmt.Println("Copies inserted:", copiesInserted)
} }
func insertSelectedCard(db *sql.DB, area database.StorageArea, location database.CardLocation) {
if area.Name == "" {
output = "No storage area selected."
return
}
if location.CardPrintingId == "" {
output = "No card selected, please [search] for a card printing."
return
}
err := logic.StoreCard(db, location)
logic.Check(err)
copiesInserted++
}

View File

@ -43,3 +43,19 @@ func InsertCardLocation(db *sql.DB, storageLocation CardLocation) error {
return nil return nil
} }
func InsertCardInExistingLocation(db *sql.DB, cardLocation CardLocation) error {
query := `UPDATE CardLocation SET CardPrintingId = ? WHERE Id = ?;`
update, err := db.Prepare(query)
if err != nil {
return err
}
_, err = update.Exec(cardLocation.CardPrintingId, cardLocation.Id)
if err != nil {
return err
}
return nil
}

View File

@ -7,12 +7,14 @@ import (
) )
type LocateCardResult struct { type LocateCardResult struct {
CardLocationId int
CardName string CardName string
SetCode string SetCode string
IsFoil bool IsFoil bool
IsPromo bool IsPromo bool
CollectorNumber string CollectorNumber string
Language string Language string
StorageAreaId int
StorageAreaType string StorageAreaType string
StorageAreaName string StorageAreaName string
Position int Position int
@ -21,7 +23,21 @@ type LocateCardResult struct {
func GetLocateResults(db *sql.DB, cardNames []string) ([]LocateCardResult, error) { func GetLocateResults(db *sql.DB, cardNames []string) ([]LocateCardResult, error) {
var results []LocateCardResult var results []LocateCardResult
query := "SELECT CardPrinting.Name, CardPrinting.SetCode, CardPrinting.IsFoil, CardPrinting.IsPromo, CardPrinting.CollectorNumber, CardPrinting.Language, StorageArea.StorageType, StorageArea.Name, CardLocation.Position FROM CardLocation JOIN CardPrinting ON CardLocation.CardPrintingId = CardPrinting.Id JOIN StorageArea ON CardLocation.StorageAreaId = StorageArea.Id WHERE CardPrinting.Name IN (?);" query := `SELECT CardLocation.Id,
CardPrinting.Name,
CardPrinting.SetCode,
CardPrinting.IsFoil,
CardPrinting.IsPromo,
CardPrinting.CollectorNumber,
CardPrinting.Language,
StorageArea.Id,
StorageArea.StorageType,
StorageArea.Name,
CardLocation.Position
FROM CardLocation
JOIN CardPrinting ON CardLocation.CardPrintingId = CardPrinting.Id
JOIN StorageArea ON CardLocation.StorageAreaId = StorageArea.Id
WHERE CardPrinting.Name IN (?);`
query, args, err := sqlx.In(query, cardNames) query, args, err := sqlx.In(query, cardNames)
if err != nil { if err != nil {
return results, err return results, err
@ -35,7 +51,7 @@ func GetLocateResults(db *sql.DB, cardNames []string) ([]LocateCardResult, error
var result LocateCardResult var result LocateCardResult
for rows.Next() { for rows.Next() {
err := rows.Scan(&result.CardName, &result.SetCode, &result.IsFoil, &result.IsPromo, &result.CollectorNumber, &result.Language, &result.StorageAreaType, &result.StorageAreaName, &result.Position) err := rows.Scan(&result.CardLocationId, &result.CardName, &result.SetCode, &result.IsFoil, &result.IsPromo, &result.CollectorNumber, &result.Language, &result.StorageAreaId, &result.StorageAreaType, &result.StorageAreaName, &result.Position)
if err != nil { if err != nil {
return results, err return results, err
} }

View File

@ -2,8 +2,8 @@ package database
import "database/sql" import "database/sql"
func RemoveFromBinder(db *sql.DB, location CardLocation) error { func RemoveFromBinder(db *sql.DB, location LocateCardResult) error {
query := `UPDATE CardStorageLocation SET CardPrintingId = NULL WHERE Id = ?;` query := `UPDATE CardLocation SET CardPrintingId = NULL WHERE Id = ?;`
update, err := db.Prepare(query) update, err := db.Prepare(query)
defer update.Close() defer update.Close()
@ -11,7 +11,7 @@ func RemoveFromBinder(db *sql.DB, location CardLocation) error {
return err return err
} }
_, err = update.Exec(location.Id) _, err = update.Exec(location.CardLocationId)
if err != nil { if err != nil {
return err return err
} }
@ -19,8 +19,8 @@ func RemoveFromBinder(db *sql.DB, location CardLocation) error {
return nil return nil
} }
func RemoveFromBox(db *sql.DB, location CardLocation) error { func RemoveFromBox(db *sql.DB, location LocateCardResult) error {
deleteQuery := `DELETE FROM CardStorageLocation WHERE Id = ?;` deleteQuery := `DELETE FROM CardLocation WHERE Id = ?;`
del, err := db.Prepare(deleteQuery) del, err := db.Prepare(deleteQuery)
defer del.Close() defer del.Close()
@ -28,12 +28,12 @@ func RemoveFromBox(db *sql.DB, location CardLocation) error {
return err return err
} }
_, err = del.Exec(location.Id) _, err = del.Exec(location.CardLocationId)
if err != nil { if err != nil {
return err return err
} }
updateQuery := `UPDATE CardStorageLocation SET Position = Position - 1 WHERE Position > 5;` updateQuery := `UPDATE CardLocation SET Position = Position - 1 WHERE Position > ? AND StorageAreaId = ?;`
update, err := db.Prepare(updateQuery) update, err := db.Prepare(updateQuery)
defer update.Close() defer update.Close()
@ -41,7 +41,7 @@ func RemoveFromBox(db *sql.DB, location CardLocation) error {
return err return err
} }
_, err = update.Exec(location.Id) _, err = update.Exec(location.Position, location.StorageAreaId)
if err != nil { if err != nil {
return err return err
} }

View File

@ -34,6 +34,34 @@ func GetAllStorageAreas(db *sql.DB) ([]StorageArea, error) {
return storageAreas, nil return storageAreas, nil
} }
func GetStorageAreaTypeById(db *sql.DB, storageAreaId int) (string, error) {
var storageType string
query := `SELECT StorageType FROM StorageArea WHERE Id = ?;`
row := db.QueryRow(query, storageAreaId)
err := row.Scan(&storageType)
if err != nil {
return storageType, err
}
return storageType, nil
}
func GetNextEmptySlotInBinder(db *sql.DB, storageAreaId int) (int, error) {
query := `SELECT Id FROM CardLocation WHERE CardPrintingId IS NULL AND StorageAreaId = ? ORDER BY Position ASC LIMIT 1;`
var emptySlotId int
err := db.QueryRow(query, storageAreaId).Scan(&emptySlotId)
if err == sql.ErrNoRows {
return -1, nil
} else if err != nil {
return 0, err
}
return emptySlotId, nil
}
func InsertStorageArea(db *sql.DB, storageArea StorageArea) error { func InsertStorageArea(db *sql.DB, storageArea StorageArea) error {
query := `INSERT INTO StorageArea (Name, StorageType) VALUES (?, ?);` query := `INSERT INTO StorageArea (Name, StorageType) VALUES (?, ?);`
@ -49,17 +77,3 @@ func InsertStorageArea(db *sql.DB, storageArea StorageArea) error {
return nil return nil
} }
func GetStorageAreaTypeById(db *sql.DB, storageAreaId int) (string, error) {
var storageType string
query := `SELECT StorageType FROM StorageArea WHERE Id = ?;`
row := db.QueryRow(query, storageAreaId)
err := row.Scan(&storageType)
if err != nil {
return storageType, err
}
return storageType, nil
}

View File

@ -0,0 +1,50 @@
package delverlens
import (
"encoding/csv"
"io"
"os"
"strings"
)
type DelverLensCard struct {
ScryfallID string
IsFoil bool
}
func ParseExportFile(filename string) ([]DelverLensCard, error) {
var cards []DelverLensCard
file, err := os.Open("/home/viciouscirce/dox/sevenkeys_imports/" + filename)
if err != nil {
return cards, err
}
r := csv.NewReader(file)
var isHeader bool = true
for {
record, err := r.Read()
if err == io.EOF {
break
} else if err != nil {
return cards, err
}
// Skip the header line
if isHeader {
isHeader = false
continue
}
card := DelverLensCard{
Name: record[0],
IsFoil: record[1] == "Foil",
CollectorNumber: record[2],
SetCode: strings.ToLower(record[3]),
}
cards = append(cards, card)
}
return cards, nil
}

25
sevenkeys/logic/import.go Normal file
View File

@ -0,0 +1,25 @@
package logic
import (
"database/sql"
"sevenkeys/database"
"sevenkeys/delverlens"
)
func ImportDelverLensCards(db *sql.DB, cards []delverlens.DelverLensCard, storageAreaId int) error {
for _, card := range cards {
var cardPrintingId string
if card.IsFoil {
cardPrintingId = card.ScryfallID + "n"
} else {
cardPrintingId = card.ScryfallID + "f"
}
cardLocation := database.CardLocation{
CardPrintingId: cardPrintingId,
StorageAreaId: storageAreaId,
}
StoreCard(cardLocation)
}
}

View File

@ -9,9 +9,12 @@ import (
const SLOTS_PER_BINDER_PAGE = 18 // TODO: Make this configurable const SLOTS_PER_BINDER_PAGE = 18 // TODO: Make this configurable
func GetBinderLocationDescription(position int) string { func GetBinderLocationDescription(position int) string {
var page int = (position / SLOTS_PER_BINDER_PAGE) + 1 var page int = ((position - 1) / SLOTS_PER_BINDER_PAGE) + 1
var pagePosition int = position % SLOTS_PER_BINDER_PAGE var pagePosition int = position % SLOTS_PER_BINDER_PAGE
if pagePosition == 0 {
pagePosition = SLOTS_PER_BINDER_PAGE
}
var slot int var slot int
var frontOrBack string var frontOrBack string
@ -27,15 +30,14 @@ func GetBinderLocationDescription(position int) string {
return fmt.Sprintf(" on page %d in %s slot %d", page, frontOrBack, slot) return fmt.Sprintf(" on page %d in %s slot %d", page, frontOrBack, slot)
} }
func LocateCards(db *sql.DB, cardNames []string, criteria SearchCriteria) ([]string, error) { func LocateCards(db *sql.DB, cardNames []string, criteria SearchCriteria) ([]database.LocateCardResult, error) {
var locations []string
results, err := database.GetLocateResults(db, cardNames) results, err := database.GetLocateResults(db, cardNames)
if err != nil { if err != nil {
return locations, err return results, err
} }
var location string var filteredResults []database.LocateCardResult
for _, result := range results { for _, result := range results {
printing := database.CardPrinting{ printing := database.CardPrinting{
SetCode: result.SetCode, SetCode: result.SetCode,
@ -49,31 +51,37 @@ func LocateCards(db *sql.DB, cardNames []string, criteria SearchCriteria) ([]str
continue continue
} }
location = fmt.Sprintf("%s (%s %s) [%s]", filteredResults = append(filteredResults, result)
result.CardName,
result.SetCode,
result.CollectorNumber,
result.Language)
if result.IsFoil {
location += " FOIL"
}
if result.IsPromo {
location += " PROMO"
}
location += fmt.Sprintf(" in %s \"%s\"",
result.StorageAreaType,
result.StorageAreaName)
if result.StorageAreaType == "Binder" {
location += GetBinderLocationDescription(result.Position)
} else if result.StorageAreaType == "Box" {
location += fmt.Sprintf(" at position %d", result.Position)
}
locations = append(locations, location)
} }
return locations, nil return filteredResults, nil
}
func GetLocationDescription(location database.LocateCardResult) string {
var description string
description = fmt.Sprintf("%s (%s %s) [%s]",
location.CardName,
location.SetCode,
location.CollectorNumber,
location.Language)
if location.IsFoil {
description += " FOIL"
}
if location.IsPromo {
description += " PROMO"
}
description += fmt.Sprintf(" in %s \"%s\"",
location.StorageAreaType,
location.StorageAreaName)
if location.StorageAreaType == "Binder" {
description += GetBinderLocationDescription(location.Position)
} else if location.StorageAreaType == "Box" {
description += fmt.Sprintf(" at position %d", location.Position)
}
return description
} }

View File

@ -2,24 +2,44 @@ package logic
import "testing" import "testing"
func Test_GetBinderLocationDescription_ReturnsCorrectFormat_ForFrontSlots(t *testing.T) { func assert(t *testing.T, description string, expected string) {
var position int = 24
var expected string = " on page 2 in front slot 6"
description := GetBinderLocationDescription(position)
if description != expected { if description != expected {
t.Errorf("expected %s, got %s\n", expected, description) t.Errorf("expected \"%s\", got \"%s\"\n", expected, description)
} }
} }
func Test_GetBinderLocationDescription_ReturnsCorrectFormat_ForBackSlots(t *testing.T) { func Test_GetBinderLocationDescription_ReturnsCorrectSlotNumber_ForFirstSlotInPage(t *testing.T) {
var position int = 17 var position int = 1
var expected string = " on page 1 in back slot 8" var expected string = " on page 1 in front slot 1"
description := GetBinderLocationDescription(position) description := GetBinderLocationDescription(position)
if description != expected { assert(t, description, expected)
t.Errorf("expected %s, got %s\n", expected, description) }
}
func Test_GetBinderLocationDescription_ReturnsCorrectSlotNumber_ForFirstSlotOnBackOfPage(t *testing.T) {
var position int = 10
var expected string = " on page 1 in back slot 1"
description := GetBinderLocationDescription(position)
assert(t, description, expected)
}
func Test_GetBinderLocationDescription_ReturnsCorrectSlotNumber_ForLastSlotOnBackOfPage(t *testing.T) {
var position int = 18
var expected string = " on page 1 in back slot 9"
description := GetBinderLocationDescription(position)
assert(t, description, expected)
}
func Test_GetBinderLocationDescription_ReturnsCorrectSlotNumber_ForLastSlotOnBackOfSecondPage(t *testing.T) {
var position int = 36
var expected string = " on page 2 in back slot 9"
description := GetBinderLocationDescription(position)
assert(t, description, expected)
} }

View File

@ -9,15 +9,10 @@ import (
var UnrecognizedStorageAreaTypeError error = errors.New("Unrecognized storage area type.") var UnrecognizedStorageAreaTypeError error = errors.New("Unrecognized storage area type.")
func RemoveFromStorage(db *sql.DB, location database.CardLocation) error { func RemoveFromStorage(db *sql.DB, location database.LocateCardResult) error {
locationType, err := database.GetStorageAreaTypeById(db, location.Id) if location.StorageAreaType == database.StorageAreaTypeBinder {
if err != nil {
return err
}
if locationType == database.StorageAreaTypeBinder {
database.RemoveFromBinder(db, location) database.RemoveFromBinder(db, location)
} else if locationType == database.StorageAreaTypeBox { } else if location.StorageAreaType == database.StorageAreaTypeBox {
database.RemoveFromBox(db, location) database.RemoveFromBox(db, location)
} else { } else {
return UnrecognizedStorageAreaTypeError return UnrecognizedStorageAreaTypeError

View File

@ -61,7 +61,7 @@ func GetAllSearchOptions(db *sql.DB, searchCriteria SearchCriteria) (InsertSearc
return searchOptions, err return searchOptions, err
} }
func GenericSearch[pk string | int](options map[string]pk) (pk, error) { func GenericSearch[pk string | int](options map[string]pk) (pk, string, error) {
var value pk var value pk
cmd := exec.Command("fzf") cmd := exec.Command("fzf")
@ -69,7 +69,7 @@ func GenericSearch[pk string | int](options map[string]pk) (pk, error) {
fzfStdin, err := cmd.StdinPipe() fzfStdin, err := cmd.StdinPipe()
if err != nil { if err != nil {
return value, err return value, "", err
} }
go func() { go func() {
@ -81,10 +81,10 @@ func GenericSearch[pk string | int](options map[string]pk) (pk, error) {
fzfOutput, err := cmd.Output() fzfOutput, err := cmd.Output()
if err != nil { if err != nil {
return value, err return value, "", err
} }
searchResult := strings.TrimSuffix(string(fzfOutput), "\n") searchResult := strings.TrimSuffix(string(fzfOutput), "\n")
value = options[searchResult] value = options[searchResult]
return value, nil return value, searchResult, nil
} }

View File

@ -2,13 +2,11 @@ package logic
import ( import (
"database/sql" "database/sql"
"io"
"os"
"os/exec"
"sevenkeys/database" "sevenkeys/database"
"strings"
) )
type StorageAreaSearchOptions map[string]int
func CreateStorageArea(db *sql.DB, storageArea database.StorageArea) error { func CreateStorageArea(db *sql.DB, storageArea database.StorageArea) error {
// TODO: Check if there's already a storage are with the same name // TODO: Check if there's already a storage are with the same name
// TODO: Check if the type entered is valid // TODO: Check if the type entered is valid
@ -20,46 +18,22 @@ func CreateStorageArea(db *sql.DB, storageArea database.StorageArea) error {
return nil return nil
} }
func SelectStorageArea(db *sql.DB) (database.StorageArea, error) { func GetStorageAreaSearchOptions(db *sql.DB) (StorageAreaSearchOptions, error) {
var selectedStorageArea database.StorageArea var options StorageAreaSearchOptions
storageAreas, err := database.GetAllStorageAreas(db) storageAreas, err := database.GetAllStorageAreas(db)
if err != nil { if err != nil {
return selectedStorageArea, err return options, err
} }
cmd := exec.Command("fzf")
cmd.Stderr = os.Stderr
fzfStdin, err := cmd.StdinPipe()
if err != nil {
return selectedStorageArea, err
}
go func() {
defer fzfStdin.Close()
for _, area := range storageAreas {
io.WriteString(fzfStdin, area.Name+"\n")
}
}()
fzfOutput, err := cmd.Output()
if err != nil {
return selectedStorageArea, err
}
key := strings.TrimSuffix(string(fzfOutput), "\n")
for _, area := range storageAreas { for _, area := range storageAreas {
if area.Name == key { options[area.Name] = area.Id
selectedStorageArea = area
break
}
} }
return selectedStorageArea, nil return options, nil
} }
func StoreCard(db *sql.DB, cardLocation database.CardLocation) error { func StoreAfterLastCard(db *sql.DB, cardLocation database.CardLocation) error {
lastPosition, err := database.GetLastPositionInStorageArea(db, cardLocation.StorageAreaId) lastPosition, err := database.GetLastPositionInStorageArea(db, cardLocation.StorageAreaId)
if err != nil { if err != nil {
return err return err
@ -74,3 +48,46 @@ func StoreCard(db *sql.DB, cardLocation database.CardLocation) error {
return nil return nil
} }
func StoreInEmptySlot(db *sql.DB, cardLocation database.CardLocation, cardLocationId int) error {
cardLocation.Id = cardLocationId
err := database.InsertCardInExistingLocation(db, cardLocation)
if err != nil {
return err
}
return nil
}
func StoreCard(db *sql.DB, cardLocation database.CardLocation) error {
storageAreaType, err := database.GetStorageAreaTypeById(db, cardLocation.StorageAreaId)
if err != nil {
return err
}
if storageAreaType == database.StorageAreaTypeBinder {
nextEmptySlotId, err := database.GetNextEmptySlotInBinder(db, cardLocation.StorageAreaId)
if err == sql.ErrNoRows {
err = StoreAfterLastCard(db, cardLocation)
if err != nil {
return err
}
} else if err != nil {
return err
} else {
err = StoreInEmptySlot(db, cardLocation, nextEmptySlotId)
if err != nil {
return err
}
}
return nil
}
err = StoreAfterLastCard(db, cardLocation)
if err != nil {
return err
}
return nil
}