Add search-printings command

This commit is contained in:
The Magician 2024-10-28 19:47:23 +00:00
parent 2ef339bbce
commit 0dd67f3bb8
13 changed files with 122 additions and 437 deletions

View File

@ -1,32 +0,0 @@
#!/bin/bash
STORAGE_DIR="$HOME/.local/share/sevenkeys/scanimages/"
mkdir -p "$STORAGE_DIR"
ADD_CARDS=0
echo "scantap: Beginning scan loop"
while true; do
rng="$(cat /dev/random | tr -cd 'a-f0-9' | head -c 32)"
filename="$STORAGE_DIR/$rng.png"
scanimage --output-file="$filename" --source "ADF Front" --mode Color --page-width 63mm --page-height 88mm 2>/dev/null
# scanimage exits with code 7 if no documents are available in scanner
if test $? -eq 7; then
if test $ADD_CARDS -eq 0; then
ADD_CARDS=1
echo "scantap: No more cards in feeder" >&2
fi
continue
fi
ADD_CARDS=0
convert -rotate 180 "$filename" "$filename"
# TODO: Add "store" command to sevenkeys executable to avoid duplicating insert logic here, then `./sevenkeys --profile=production store --id="0000..0000"`
# Return inserted CardLocationId
#cardLocationId="1"
#mysql --silent --silent --user=root --password="$(pass show sevenkeys/mysql \
#-e "USE sevenkeys; INSERT INTO CardScan (CardLocationId, Filename) VALUES ('$cardLocationId', '$filename.png');"
done

View File

@ -1,158 +0,0 @@
package cli
import (
"database/sql"
"fmt"
"sevenkeys/database"
"sevenkeys/logic"
)
var output string
func MainCliLoop(db *sql.DB) {
var command string
//var selectedStorageAreaName string
var cardLocation database.CardLocation
var insertSearchCriteria logic.SearchCriteria = logic.SearchCriteria{
SetCode: "",
Foil: logic.Either,
Promo: logic.Either,
Language: "en",
}
//var insertSearchOptions logic.InsertSearchOptions
// TODO: Add the ability to modify this
/*
var locateSearchCriteria logic.SearchCriteria = logic.SearchCriteria{
SetCode: "",
Foil: logic.True,
Promo: logic.Either,
Language: "en",
}
*/
for {
ShowSplashScreen()
// TODO: Fix this
//showStorageInfo(os.Stdout, selectedStorageAreaName)
showInsertSearchCriteria(insertSearchCriteria)
showSelectedCard()
showCopiesInserted()
command = GetStringResponse("SEVENKEYS $")
//var err error
switch command {
/*
case "q", "quit":
return
*/
/*
case "n", "newstorage":
var storageArea database.StorageArea
storageArea.Name = GetStringResponse("Storage area name:")
storageArea.StorageType = GetStringResponse("Storage area type (Binder/Box):")
err = logic.CreateStorageArea(db, storageArea)
logic.Check(err)
break
*/
/*
case "a", "area":
options, err := logic.GetStorageAreaSearchOptions(db)
logic.Check(err)
id, _, err := logic.GenericSearch(options)
logic.Check(err)
//selectedStorageAreaName = name
// TODO: Make db call to cache StorageArea once we have the ID
cardLocation.StorageAreaId = id
break
case "c", "criteria":
insertSearchCriteria = getSearchCriteria()
break
case "s", "search":
if shouldRefreshSearch {
insertSearchOptions = getSearchOptions(db, insertSearchCriteria)
}
var previousCardPrintingId = cardLocation.CardPrintingId
pk, searchLine, err := logic.GenericSearch(insertSearchOptions)
cardLocation.CardPrintingId = pk
selectedCardPrintingSearchLine = searchLine
var exitError *exec.ExitError
if errors.As(err, &exitError) {
break
}
logic.Check(err)
output = ""
if cardLocation.CardPrintingId != previousCardPrintingId {
copiesInserted = 0
}
break
*/
case "i", "insert":
_, 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(db, cards, cardLocation.StorageAreaId)
logic.Check(err)
break
*/
/*
case "l", "locate":
filename := GetStringResponse("Filename:")
cardNames, err := logic.GetCardNamesFromFile(filename)
logic.Check(err)
locations, err := logic.LocateCards(db, cardNames, locateSearchCriteria)
logic.Check(err)
if len(locations) == 0 {
fmt.Println("No results found")
fmt.Scanln()
break
}
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
*/
default:
fmt.Println("Unrecognized command:", command)
break
}
}
}

View File

@ -1,38 +0,0 @@
package cli
import (
"bufio"
"fmt"
"os"
"sevenkeys/logic"
"strings"
)
func GetStringResponse(prompt string) string {
fmt.Print(prompt + " ")
var response string
scanner := bufio.NewScanner(os.Stdin)
scanner.Scan()
response = scanner.Text()
return response
}
func GetYesNoResponse(prompt string) bool {
response := GetStringResponse(prompt)
return strings.ToUpper(response) == "Y"
}
func GetTriadicResponse(prompt string) logic.Triadic {
response := GetStringResponse(prompt)
switch strings.ToUpper(response) {
case "Y":
return logic.True
case "N":
return logic.False
default:
return logic.Either
}
}

View File

@ -1,61 +0,0 @@
package cli
import (
"database/sql"
"fmt"
"sevenkeys/logic"
)
var shouldRefreshSearch bool = true
func getSearchCriteria() logic.SearchCriteria {
var searchCriteria logic.SearchCriteria
searchCriteria.SetCode = GetStringResponse("Set code:")
searchCriteria.Foil = GetTriadicResponse("Foil (y/n/E):")
searchCriteria.Promo = GetTriadicResponse("Promo (y/n/E):")
searchCriteria.Language = GetStringResponse("Language:")
shouldRefreshSearch = true
return searchCriteria
}
func getTriadicDisplay(triadic logic.Triadic) string {
if triadic == logic.True {
return "True"
}
if triadic == logic.False {
return "False"
}
return "Either"
}
func showInsertSearchCriteria(insertSearchCriteria logic.SearchCriteria) {
fmt.Println("SEARCH CRITERIA")
setCodeDisplay := getInfoDisplay(insertSearchCriteria.SetCode)
foilDisplay := getTriadicDisplay(insertSearchCriteria.Foil)
promoDisplay := getTriadicDisplay(insertSearchCriteria.Promo)
languageDisplay := getInfoDisplay(insertSearchCriteria.Language)
fmt.Println("Set code:", setCodeDisplay)
fmt.Println("Foil:", foilDisplay)
fmt.Println("Promo:", promoDisplay)
fmt.Println("Language:", languageDisplay)
fmt.Print("\n")
}
func showSelectedCard() {
selectedCardDisplay := getInfoDisplay(selectedCardPrintingSearchLine)
fmt.Println("Selected card:", selectedCardDisplay)
}
func getSearchOptions(db *sql.DB, insertSearchCriteria logic.SearchCriteria) logic.InsertSearchOptions {
options, err := logic.GetAllSearchOptions(db, insertSearchCriteria)
logic.Check(err)
shouldRefreshSearch = false
return options
}

View File

@ -1,19 +0,0 @@
package cli
import (
"sevenkeys/figlet"
"github.com/inancgumus/screen"
)
func ClearScreen() {
screen.Clear()
screen.MoveTopLeft()
}
func ShowSplashScreen() {
ClearScreen()
figlet.PrintMsgSlant("SEVENKEYS", "center")
figlet.PrintMsgTerm("the ultimate Magic: the Gathering trading card storage system", "center")
}

View File

@ -1,36 +0,0 @@
package cli
import (
"fmt"
"io"
"sevenkeys/database"
)
var (
selectedCardPrintingSearchLine string
copiesInserted int
)
func getInfoDisplay(info string) string {
if info == "" {
return "[EMPTY]"
}
return info
}
func showStorageInfo(w io.Writer, area database.StorageArea) {
fmt.Fprint(w, "Selected Storage Area: ")
if area.Name == "" { // TODO: Add a struct method to determine whether it is unset?
fmt.Fprint(w, "[None]\n")
return
}
fmt.Fprintf(w, "%s (%s)\n", area.Name, area.StorageType)
return
}
func showCopiesInserted() {
fmt.Println("Copies inserted:", copiesInserted)
}

View File

@ -1,39 +0,0 @@
package cli
import (
"bytes"
"sevenkeys/database"
"testing"
)
func Test_showStorageInfo_DisplaysNone_IfSelectedStorageAreaIsUnset(t *testing.T) {
expected := "Selected Storage Area: [None]\n"
var output bytes.Buffer
var area database.StorageArea
showStorageInfo(&output, area)
result := output.String()
if result != expected {
t.Errorf("expected %s, got %s", expected, result)
}
}
func Test_showStorageInfo_DisplaysStorageAreaNameAndType_IfSelectedStorageAreaIsSet(t *testing.T) {
expected := "Selected Storage Area: Test A (Box)\n"
var output bytes.Buffer
area := database.StorageArea{
Id: 1,
Name: "Test A",
Type: "Box",
}
showStorageInfo(&output, area)
result := output.String()
if result != expected {
t.Errorf("expected %s, got %s", expected, result)
}
}

View File

@ -1,37 +0,0 @@
package cli
import (
"database/sql"
"fmt"
"sevenkeys/logic"
"sevenkeys/logic/scryfall"
)
func RunUpdateCheck(db *sql.DB) {
fmt.Println("Checking for updates...")
bulkData, err := scryfall.GetBulkDataByType(scryfall.BulkDataTypeAllCards)
logic.Check(err)
needsUpdate, err := logic.CheckForUpdates(db, bulkData)
logic.Check(err)
if !needsUpdate {
fmt.Println("No update required.")
return
}
fmt.Println("Update required.")
if GetYesNoResponse("Run update? (y/N)") {
fmt.Println("Running update...")
logic.CreateCacheDirectories()
err = logic.UpdateSets(db)
logic.Check(err)
err = logic.UpdateCards(db, bulkData)
logic.Check(err)
fmt.Println("Update finished.")
}
}

View File

@ -34,7 +34,7 @@ INSERT INTO CardPrinting (
IsPromo, IsPromo,
CollectorNumber, CollectorNumber,
ImageUrl, ImageUrl,
Language, Language
) VALUES ( ) VALUES (
'00000000-0000-0000-0000-0000000000000', '00000000-0000-0000-0000-0000000000000',
'Scanned Card Placeholder', 'Scanned Card Placeholder',
@ -43,7 +43,7 @@ INSERT INTO CardPrinting (
0, 0,
0, 0,
'', '',
'en', 'en'
); );
CREATE TABLE IF NOT EXISTS StorageArea ( CREATE TABLE IF NOT EXISTS StorageArea (

View File

@ -1,9 +1,16 @@
package logic package logic
import "sevenkeys/database" import (
"sevenkeys/database"
"strings"
)
func filterPrinting(printing database.CardPrinting, searchCriteria SearchCriteria) bool { func filterPrinting(printing database.CardPrinting, searchCriteria SearchCriteria) bool {
if searchCriteria.SetCode != "" && printing.SetCode != searchCriteria.SetCode { if searchCriteria.SetCode != "" && !strings.Contains(printing.SetCode, searchCriteria.SetCode) {
return true
}
if searchCriteria.CollectorNumber != "" && !strings.Contains(printing.CollectorNumber, searchCriteria.CollectorNumber) {
return true return true
} }

View File

@ -400,3 +400,27 @@ func TestFilterPrinting_ReturnsFalse_IfLanguageNotSet(t *testing.T) {
t.Errorf("filter was true") t.Errorf("filter was true")
} }
} }
func TestFilterPrinting_ReturnsTrue_IfSetCodeMatchesButCollectorNumberDoesnt(t *testing.T) {
printing := database.CardPrinting{
SetCode: "rtr",
CollectorNumber: "301",
IsFoil: false,
IsPromo: false,
Language: "en",
}
searchCriteria := SearchCriteria{
SetCode: "rtr",
CollectorNumber: "299",
Foil: False,
Promo: False,
Language: "",
}
filter := filterPrinting(printing, searchCriteria)
if filter != true {
t.Errorf("filter was false")
}
}

View File

@ -20,17 +20,16 @@ const (
type SearchCriteria struct { type SearchCriteria struct {
SetCode string SetCode string
CollectorNumber string
Foil Triadic Foil Triadic
Promo Triadic Promo Triadic
Language string Language string
} }
// The InsertSearchOptions type is a map of `string` keys (which can be searched using type CardPrintingSearchOptions map[string]string
// fzf) to `string` values (which represent a primary key in the CardPrintings table)
type InsertSearchOptions map[string]string
func GetAllSearchOptions(db *sql.DB, searchCriteria SearchCriteria) (InsertSearchOptions, error) { func GetAllCardPrintingSearchOptions(db *sql.DB, searchCriteria SearchCriteria) (CardPrintingSearchOptions, error) {
var searchOptions InsertSearchOptions = make(InsertSearchOptions) var searchOptions CardPrintingSearchOptions = make(CardPrintingSearchOptions)
cardPrintings, err := database.GetAllCardPrintings(db) cardPrintings, err := database.GetAllCardPrintings(db)
if err != nil { if err != nil {

View File

@ -1,21 +1,40 @@
package main package main
import ( import (
"bufio"
"flag" "flag"
"fmt" "fmt"
"os" "os"
"sevenkeys/cli"
"sevenkeys/database" "sevenkeys/database"
"sevenkeys/delverlens" "sevenkeys/delverlens"
"sevenkeys/logic" "sevenkeys/logic"
"sevenkeys/logic/scryfall"
"strings"
) )
func GetStringResponse(prompt string) string {
fmt.Print(prompt + " ")
var response string
scanner := bufio.NewScanner(os.Stdin)
scanner.Scan()
response = scanner.Text()
return response
}
func GetYesNoResponse(prompt string) bool {
response := GetStringResponse(prompt)
return strings.ToUpper(response) == "Y"
}
const ( const (
UpdateSubcommand string = "update" UpdateSubcommand string = "update"
CreateStorageAreaSubcommand string = "createstorage" CreateStorageAreaSubcommand string = "createstorage"
StoreSubcommand string = "store" StoreSubcommand string = "store"
ImportSubcommand string = "import" ImportSubcommand string = "import"
SearchSubcommand string = "search" SearchPrintingsSubcommand string = "search-printings"
SearchStorageSubcommand string = "search-storage"
DeckSubcommand string = "deck" DeckSubcommand string = "deck"
) )
@ -44,7 +63,33 @@ func main() {
switch flag.Args()[0] { switch flag.Args()[0] {
case UpdateSubcommand: case UpdateSubcommand:
cli.RunUpdateCheck(db) fmt.Println("Checking for updates...")
bulkData, err := scryfall.GetBulkDataByType(scryfall.BulkDataTypeAllCards)
logic.Check(err)
needsUpdate, err := logic.CheckForUpdates(db, bulkData)
logic.Check(err)
if !needsUpdate {
fmt.Println("No update required.")
return
}
fmt.Println("Update required.")
if GetYesNoResponse("Run update? (y/N)") {
fmt.Println("Running update...")
logic.CreateCacheDirectories()
err = logic.UpdateSets(db)
logic.Check(err)
err = logic.UpdateCards(db, bulkData)
logic.Check(err)
fmt.Println("Update finished.")
}
break break
case CreateStorageAreaSubcommand: case CreateStorageAreaSubcommand:
createStorageCmd := flag.NewFlagSet(CreateStorageAreaSubcommand, flag.ExitOnError) createStorageCmd := flag.NewFlagSet(CreateStorageAreaSubcommand, flag.ExitOnError)
@ -103,7 +148,37 @@ func main() {
err = logic.ImportDelverLensCards(db, delverLensCards, storageAreaId) err = logic.ImportDelverLensCards(db, delverLensCards, storageAreaId)
logic.Check(err) logic.Check(err)
break break
case SearchSubcommand: case SearchPrintingsSubcommand:
searchPrintingsCmd := flag.NewFlagSet(SearchPrintingsSubcommand, flag.ExitOnError)
setCode := searchPrintingsCmd.String("set-code", "", "The code for the set the card we're searching for belongs to.")
collectorNumber := searchPrintingsCmd.String("collector-number", "", "The collector number of the card we're searching for.")
foil := searchPrintingsCmd.String("foil", "E", "Whether the card we're searching for is foil.")
searchPrintingsCmd.Parse(flag.Args()[1:])
searchCriteria := logic.SearchCriteria{
SetCode: *setCode,
CollectorNumber: *collectorNumber,
Promo: logic.Either,
Language: "en",
}
if *foil == "Y" {
searchCriteria.Foil = logic.True
} else if *foil == "N" {
searchCriteria.Foil = logic.False
} else {
searchCriteria.Foil = logic.Either
}
searchOptions, err := logic.GetAllCardPrintingSearchOptions(db, searchCriteria)
logic.Check(err)
id, _, err := logic.GenericSearch(searchOptions)
logic.Check(err)
fmt.Println(id)
break
case SearchStorageSubcommand:
searchOptions, err := logic.GetAllStorageSearchOptions(db) searchOptions, err := logic.GetAllStorageSearchOptions(db)
logic.Check(err) logic.Check(err)
id, _, err := logic.GenericSearch(searchOptions) id, _, err := logic.GenericSearch(searchOptions)