Compare commits
No commits in common. "d2bc986506cc2538f1cf53a4ac155275c5d8f3fd" and "0e3ee54e1cba02b80378aa54b7bd76bbf90f6993" have entirely different histories.
d2bc986506
...
0e3ee54e1c
|
@ -0,0 +1,5 @@
|
||||||
|
cmake_install.cmake
|
||||||
|
CMakeCache.txt
|
||||||
|
CMakeFiles/*
|
||||||
|
Makefile
|
||||||
|
AScannerDarkly
|
|
@ -0,0 +1,7 @@
|
||||||
|
cmake_minimum_required(VERSION 3.2)
|
||||||
|
project( AScannerDarkly )
|
||||||
|
find_package( OpenCV REQUIRED )
|
||||||
|
include_directories( ${OpenCV_INCLUDE_DIRS} )
|
||||||
|
add_executable( AScannerDarkly main.cpp )
|
||||||
|
target_link_libraries( AScannerDarkly ${OpenCV_LIBS} )
|
||||||
|
target_link_libraries( AScannerDarkly -llept -ltesseract )
|
After Width: | Height: | Size: 5.2 KiB |
After Width: | Height: | Size: 124 KiB |
After Width: | Height: | Size: 126 KiB |
After Width: | Height: | Size: 115 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 9.0 KiB |
After Width: | Height: | Size: 4.7 KiB |
After Width: | Height: | Size: 4.7 KiB |
After Width: | Height: | Size: 4.8 KiB |
After Width: | Height: | Size: 5.0 KiB |
|
@ -0,0 +1,161 @@
|
||||||
|
#include <opencv2/opencv.hpp>
|
||||||
|
#include <tesseract/baseapi.h>
|
||||||
|
#include <leptonica/allheaders.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
const std::string SCANNER_WINDOW_NAME = "A Scanner Darkly";
|
||||||
|
const std::string CARD_WINDOW_NAME = "Detected Card";
|
||||||
|
|
||||||
|
const std::string CANNY_LOWER_THRESHOLD_TRACKBAR_NAME = "Canny: Lower Threshold";
|
||||||
|
|
||||||
|
const float CARD_ASPECT_RATIO = 88.0f / 63.0f;
|
||||||
|
const std::string ASPECT_RATIO_TOLERANCE_TRACKBAR_NAME = "Aspect Ratio Tolerance";
|
||||||
|
int g_aspect_ratio_tolerance = 1;
|
||||||
|
const int MAX_ASPECT_RATIO_TOLERANCE = 100;
|
||||||
|
|
||||||
|
int g_Canny_lower_threshold = 195;
|
||||||
|
const int CANNY_UPPER_THRESHOLD = 255;
|
||||||
|
|
||||||
|
cv::Mat cropImageToRoi(cv::Mat img, cv::Rect roi) {
|
||||||
|
return img(roi);
|
||||||
|
}
|
||||||
|
|
||||||
|
cv::Mat detectCardInFrame(cv::Mat frame) {
|
||||||
|
cv::Mat grayscaleFrame, blurFrame, cannyFrame, cardFrame;
|
||||||
|
|
||||||
|
// Perform edge detection
|
||||||
|
cv::cvtColor(frame, grayscaleFrame, cv::COLOR_BGR2GRAY);
|
||||||
|
cv::GaussianBlur(grayscaleFrame, blurFrame, cv::Size(5, 5), 2, 2);
|
||||||
|
cv::Canny(blurFrame, cannyFrame, g_Canny_lower_threshold, CANNY_UPPER_THRESHOLD);
|
||||||
|
|
||||||
|
// Perform contour detection
|
||||||
|
std::vector<std::vector<cv::Point>> contours;
|
||||||
|
std::vector<cv::Vec4i> hierarchy;
|
||||||
|
cv::findContours(cannyFrame, contours, hierarchy, cv::RETR_TREE, cv::CHAIN_APPROX_SIMPLE);
|
||||||
|
|
||||||
|
// Detect convex hulls
|
||||||
|
std::vector<std::vector<cv::Point>> hull(contours.size());
|
||||||
|
for(size_t i = 0; i < contours.size(); i++) {
|
||||||
|
cv::convexHull(contours[i], hull[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detect RotatedRects
|
||||||
|
std::vector<cv::RotatedRect> minRect(contours.size());
|
||||||
|
for(size_t i = 0; i < contours.size(); i++) {
|
||||||
|
minRect[i] = cv::minAreaRect(contours[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw lines around RotatedRects
|
||||||
|
/*
|
||||||
|
cv::Scalar rectangleColor(255, 0, 0, 0);
|
||||||
|
cv::Point2f rect_points[4];
|
||||||
|
minRect[i].points(rect_points);
|
||||||
|
for (int j = 0; j < 4; j++) {
|
||||||
|
cv::line(frame, rect_points[j], rect_points[(j + 1) % 4], rectangleColor);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Find possible cards
|
||||||
|
std::vector<cv::RotatedRect> possibleCards(contours.size());
|
||||||
|
int possibleCount = 0;
|
||||||
|
for (size_t i = 0; i < minRect.size(); i++) {
|
||||||
|
float aspectRatio = minRect[i].size.height / minRect[i].size.width;
|
||||||
|
float aspectRatioTolerance = g_aspect_ratio_tolerance / 100.0f;
|
||||||
|
if (aspectRatio < (CARD_ASPECT_RATIO - aspectRatioTolerance) || aspectRatio > (CARD_ASPECT_RATIO + aspectRatioTolerance)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
cv::putText(frame, std::to_string(minRect[i].angle), minRect[i].center, cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(255, 0, 0, 0));
|
||||||
|
|
||||||
|
possibleCards[possibleCount] = minRect[i];
|
||||||
|
possibleCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (possibleCount == 1) {
|
||||||
|
return cropImageToRoi(frame, possibleCards[0].boundingRect());
|
||||||
|
} else if (possibleCount > 1) {
|
||||||
|
// If we have more than one possible match, take the largest one
|
||||||
|
int largestCardIndex = 0;
|
||||||
|
int largestCardSize = 0;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < possibleCards.size(); i++) {
|
||||||
|
float size = possibleCards[i].size.width * possibleCards[i].size.height;
|
||||||
|
if (size >= largestCardSize) {
|
||||||
|
largestCardIndex = i;
|
||||||
|
largestCardSize = size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Some basic error checking to ensure the RotatedRect is roughly card-sized
|
||||||
|
if (largestCardSize >= 14400 && largestCardSize <= 200000) {
|
||||||
|
return cropImageToRoi(frame, possibleCards[largestCardIndex].boundingRect());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cv::Mat();
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* readTextFromImage(cv::InputArray img) {
|
||||||
|
const char* tempFilename = "temp.bmp";
|
||||||
|
|
||||||
|
// Initialize Tesseract api
|
||||||
|
tesseract::TessBaseAPI* tess = new tesseract::TessBaseAPI();
|
||||||
|
tess->Init(NULL, "eng");
|
||||||
|
tess->SetPageSegMode(tesseract::PSM_SPARSE_TEXT);
|
||||||
|
|
||||||
|
// Load image into Tesseract
|
||||||
|
cv::imwrite(tempFilename, img);
|
||||||
|
Pix* pixd = pixRead(tempFilename);
|
||||||
|
tess->SetImage(pixd);
|
||||||
|
tess->Recognize(0);
|
||||||
|
|
||||||
|
// Perform OCR
|
||||||
|
const char* out = tess->GetUTF8Text();
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
pixDestroy(&pixd);
|
||||||
|
std::remove(tempFilename);
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv ) {
|
||||||
|
cv::VideoCapture cap;
|
||||||
|
cap.open(0);
|
||||||
|
|
||||||
|
if (!cap.isOpened()) {
|
||||||
|
std::cerr << "Couldn't open capture" << std::endl;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
cv::namedWindow(SCANNER_WINDOW_NAME);
|
||||||
|
|
||||||
|
cv::createTrackbar(CANNY_LOWER_THRESHOLD_TRACKBAR_NAME, SCANNER_WINDOW_NAME, &g_Canny_lower_threshold, CANNY_UPPER_THRESHOLD, NULL);
|
||||||
|
cv::createTrackbar(ASPECT_RATIO_TOLERANCE_TRACKBAR_NAME, SCANNER_WINDOW_NAME, &g_aspect_ratio_tolerance, MAX_ASPECT_RATIO_TOLERANCE, NULL);
|
||||||
|
|
||||||
|
cv::Mat frame, cardFrame;
|
||||||
|
while (true) {
|
||||||
|
cap >> frame;
|
||||||
|
//cardFrame = detectCardInFrame(frame);
|
||||||
|
cv::imshow(SCANNER_WINDOW_NAME, frame);
|
||||||
|
|
||||||
|
/*
|
||||||
|
if (cardFrame.size().width > 0) {
|
||||||
|
printf("Card detected\n");
|
||||||
|
while (true) {
|
||||||
|
cv::imshow(CARD_WINDOW_NAME, cardFrame);
|
||||||
|
char c = (char)cv::waitKey(33);
|
||||||
|
if (c == 27) {
|
||||||
|
cv::destroyWindow(CARD_WINDOW_NAME);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
char c = (char)cv::waitKey(33);
|
||||||
|
if (c == 27) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
After Width: | Height: | Size: 301 KiB |
|
@ -0,0 +1,24 @@
|
||||||
|
#!/usr/bin/python3
|
||||||
|
|
||||||
|
import cv2
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
mouse_raw = (0, 0)
|
||||||
|
|
||||||
|
def clickEvent(event, x, y, flags, params):
|
||||||
|
global mouse_raw
|
||||||
|
|
||||||
|
if event == cv2.EVENT_LBUTTONDOWN:
|
||||||
|
mouse_raw = (x, y)
|
||||||
|
print(mouse_raw)
|
||||||
|
|
||||||
|
cap = cv2.VideoCapture(0)
|
||||||
|
cv2.namedWindow("image")
|
||||||
|
cv2.setMouseCallback("image", clickEvent)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
success, img = cap.read()
|
||||||
|
cv2.imshow("image", img)
|
||||||
|
cv2.waitKey(0)
|
||||||
|
|
||||||
|
cv2.destroyAllWindows()
|
|
@ -1,59 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
check_error() {
|
|
||||||
if test $? -ne 0; then
|
|
||||||
echo "card_scanner: $1 exited with unexpected error"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
STORAGE_DIR="$HOME/.local/share/sevenkeys/scanimages/"
|
|
||||||
mkdir -p "$STORAGE_DIR"
|
|
||||||
|
|
||||||
printf "Database profile: "
|
|
||||||
read profile
|
|
||||||
|
|
||||||
printf "Storage area name: "
|
|
||||||
read storageAreaName
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
# If we have generated a zero-length file, then delete it
|
|
||||||
if ! test -s "$filename"; then
|
|
||||||
rm "$filename"
|
|
||||||
fi
|
|
||||||
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
ADD_CARDS=0
|
|
||||||
check_error "scanimage"
|
|
||||||
|
|
||||||
convert -rotate 180 "$filename" "$filename"
|
|
||||||
check_error "convert"
|
|
||||||
|
|
||||||
cardLocationId="$(./sevenkeys --profile="$profile" store --storagearea="$storageAreaName" --id="00000000-0000-0000-0000-0000000000000")"
|
|
||||||
check_error "sevenkeys"
|
|
||||||
|
|
||||||
if test "$profile" == "development"; then
|
|
||||||
databaseName="sevenkeys_development"
|
|
||||||
else
|
|
||||||
databaseName="sevenkeys"
|
|
||||||
fi
|
|
||||||
|
|
||||||
mysql --silent --silent --user=root --password="$(pass show sevenkeys/mysql)" \
|
|
||||||
-e "USE $databaseName; INSERT INTO CardScan (CardLocationId, Filename) VALUES ('$cardLocationId', '$rng.png');"
|
|
||||||
check_error "mysql"
|
|
||||||
done
|
|
|
@ -1,8 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
crop() {
|
|
||||||
convert "$1" -crop 1200x120+90+100 "$2"
|
|
||||||
}
|
|
||||||
|
|
||||||
crop old_border.png name_old.png
|
|
||||||
crop new_border.png name_new.png
|
|
|
@ -1,49 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
CLASSNAME="svnProfiler"
|
|
||||||
TITLE_FILENAME="/tmp/title.png"
|
|
||||||
touch "$TITLE_FILENAME"
|
|
||||||
|
|
||||||
get_window_id() {
|
|
||||||
while true; do
|
|
||||||
id="$(xdotool search --classname "$CLASSNAME")"
|
|
||||||
if test "$?" -ne 0; then
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "$id"
|
|
||||||
break
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
printf "Database profile: "
|
|
||||||
read profile
|
|
||||||
if test "$profile" == "development"; then
|
|
||||||
databaseName="sevenkeys_development"
|
|
||||||
else
|
|
||||||
databaseName="sevenkeys"
|
|
||||||
fi
|
|
||||||
|
|
||||||
STORAGE_DIR="$HOME/.local/share/sevenkeys/scanimages/"
|
|
||||||
cd "$STORAGE_DIR"
|
|
||||||
|
|
||||||
files="$(find *.png)"
|
|
||||||
|
|
||||||
nsxiv -N "$CLASSNAME" $files &
|
|
||||||
nsxivWindowId="$(get_window_id)"
|
|
||||||
nsxiv "$TITLE_FILENAME" &
|
|
||||||
|
|
||||||
for file in $files; do
|
|
||||||
cardLocationId="$(mysql --silent --silent --user=root --password="$(pass show sevenkeys/mysql)" -e "USE $databaseName; SELECT CardLocationId FROM CardScan WHERE Filename = '$file';")"
|
|
||||||
|
|
||||||
# TODO: Detect features of the card automatically
|
|
||||||
#convert "$file" -crop 1200x120+90+100 "$TITLE_FILENAME"
|
|
||||||
#title="$(tesseract --psm 9 "$TITLE_FILENAME" stdout)"
|
|
||||||
#echo "$title"
|
|
||||||
|
|
||||||
cardPrintingId="$(sevenkeys --profile="$profile" search-printings)"
|
|
||||||
|
|
||||||
sevenkeys --profile="$profile" replace --card-location-id="$cardLocationId" --card-printing-id="$cardPrintingId"
|
|
||||||
|
|
||||||
xdotool key --window "$nsxivWindowId" 'n'
|
|
||||||
done
|
|
|
@ -1,9 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
for i in $(seq 1 13); do
|
|
||||||
echo "PSM $i:" >>output
|
|
||||||
echo "Old card:" >>output
|
|
||||||
tesseract name_old.png stdout --psm "$i" >>output
|
|
||||||
echo "New card:" >>output
|
|
||||||
tesseract name_new.png stdout --psm "$i" >>output
|
|
||||||
echo >>output
|
|
||||||
done
|
|
|
@ -1,34 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
if test -z "$1"; then
|
|
||||||
echo "usage: test_card_scanner_count <count>" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
STORAGE_DIR="$HOME/.local/share/sevenkeys/testimages/"
|
|
||||||
mkdir -p "$STORAGE_DIR"
|
|
||||||
|
|
||||||
starting_file_count="$(find $STORAGE_DIR/*.png | wc -l)"
|
|
||||||
|
|
||||||
echo "test_card_scanner_count: Beginning test"
|
|
||||||
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
|
|
||||||
echo "test_card_scanner_count: No more cards in feeder" >&2
|
|
||||||
rm "$filename"
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
ending_file_count="$(find $STORAGE_DIR/*.png | wc -l)"
|
|
||||||
|
|
||||||
if test $(( ending_file_count - starting_file_count )) -ne "$1"; then
|
|
||||||
echo "FAILED: Start: $starting_file_count, End: $ending_file_count"
|
|
||||||
else
|
|
||||||
echo "SUCCESS"
|
|
||||||
fi
|
|
|
@ -1,12 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
STORAGE_DIR="$HOME/.local/share/sevenkeys/testimages/"
|
|
||||||
mkdir -p "$STORAGE_DIR"
|
|
||||||
|
|
||||||
echo "test_card_scanner_count: Beginning test"
|
|
||||||
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
|
|
||||||
done
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
USE sevenkeys;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS CardScan (
|
||||||
|
Id INT PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
StorageAreaId INT NOT NULL,
|
||||||
|
FOREIGN KEY (StorageAreaId) REFERENCES StorageArea(Id),
|
||||||
|
Position INT NOT NULL
|
||||||
|
);
|
|
@ -0,0 +1,59 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
mysql --user=root --password="$(pass show sevenkeys/mysql)" <createtable.sql
|
||||||
|
|
||||||
|
printf "scantap: Enter name of storage box: "
|
||||||
|
read storageAreaName
|
||||||
|
|
||||||
|
storageAreaId="$(mysql --silent --silent --user=root --password="$(pass show sevenkeys/mysql)" -e "USE sevenkeys; SELECT Id FROM StorageArea WHERE Name = '$storageAreaName';")"
|
||||||
|
if test -z "$storageAreaId"; then
|
||||||
|
echo "scantap: No storage area named \"$storageAreaName\"" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
startPosition="$(mysql --silent --silent --user=root --password="$(pass show sevenkeys/mysql)" -e "USE sevenkeys; SELECT Position FROM CardScan WHERE StorageAreaId = '$storageAreaId' ORDER BY Position DESC LIMIT 1;")"
|
||||||
|
if test "$?" -ne "0"; then
|
||||||
|
echo "scantap: Failed to establish starting position" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
position=$((startPosition + 1))
|
||||||
|
|
||||||
|
printf "scantap: Enter mode (foil/nonfoil): "
|
||||||
|
read foilMode
|
||||||
|
outputDir="images/$foilMode"
|
||||||
|
mkdir -p "$outputDir"
|
||||||
|
|
||||||
|
ADD_CARDS=0
|
||||||
|
echo "scantap: Beginning scan loop"
|
||||||
|
while true; do
|
||||||
|
scanimage --format=png --batch=tmp%d.png --batch-count=2 --source "ADF Duplex" --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
|
||||||
|
|
||||||
|
if ! test -e tmp1.png || ! test -e tmp2.png; then
|
||||||
|
echo "scantap: Failed to create temporary image files" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
key="$(mysql --silent --silent --user=root --password="$(pass show sevenkeys/mysql)" -e "USE sevenkeys; INSERT INTO CardScan (StorageAreaId, Position) VALUES ('$storageAreaId', '$position'); SELECT Id FROM CardScan ORDER BY Id DESC LIMIT 1;")"
|
||||||
|
if test "$?" -ne "0"; then
|
||||||
|
echo "scantap: Failed to get key from database" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
position=$((position + 1))
|
||||||
|
|
||||||
|
convert -rotate 180 tmp1.png tmp1.png
|
||||||
|
convert -rotate 180 tmp2.png tmp2.png
|
||||||
|
|
||||||
|
mv tmp1.png "$outputDir/${key}_back.png"
|
||||||
|
mv tmp2.png "$outputDir/${key}_front.png"
|
||||||
|
done
|
|
@ -1,17 +1,9 @@
|
||||||
|
createdb:
|
||||||
|
mysql --user=root --password=$(shell pass show sevenkeys/mysql) <database/sql/createdb.sql
|
||||||
|
removedb:
|
||||||
|
rm -rf cache/
|
||||||
|
mysql --user=root --password=$(shell pass show sevenkeys/mysql) <database/sql/removedb.sql
|
||||||
connect:
|
connect:
|
||||||
mysql --user=root --password=$(shell pass show sevenkeys/mysql)
|
mysql --user=root --password=$(shell pass show sevenkeys/mysql)
|
||||||
|
|
||||||
dump:
|
dump:
|
||||||
mysqldump --user=root --password=$(shell pass show sevenkeys/mysql) sevenkeys >sevenkeys.sql
|
mysqldump --user=root --password=$(shell pass show sevenkeys/mysql) sevenkeys >sevenkeys.sql
|
||||||
|
|
||||||
dev_create:
|
|
||||||
goose mysql "root:$(shell pass show sevenkeys/mysql)@/sevenkeys_development?parseTime=true" up
|
|
||||||
dev_rollback:
|
|
||||||
rm -rf cache/
|
|
||||||
goose mysql "root:$(shell pass show sevenkeys/mysql)@/sevenkeys_development?parseTime=true" reset
|
|
||||||
|
|
||||||
prod_create:
|
|
||||||
goose mysql "root:$(shell pass show sevenkeys/mysql)@/sevenkeys?parseTime=true" up
|
|
||||||
prod_rollback:
|
|
||||||
rm -rf cache/
|
|
||||||
goose mysql "root:$(shell pass show sevenkeys/mysql)@/sevenkeys?parseTime=true" reset
|
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
|
|
||||||
sevenkeys --profile=production add --card-printing-id="$(sevenkeys --profile=production search-printings)" --storagearea="$1"
|
|
|
@ -1,7 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
|
|
||||||
# Note: Make sure to export BEARER before using
|
|
||||||
|
|
||||||
URL="https://api.cardtrader.com/api/v2"
|
|
||||||
|
|
||||||
curl --silent "$URL/$1" -H "Authorization: Bearer $BEARER" | jq .
|
|
|
@ -0,0 +1,149 @@
|
||||||
|
package cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os/exec"
|
||||||
|
"sevenkeys/database"
|
||||||
|
"sevenkeys/delverlens"
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
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
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
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")
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
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)
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
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.")
|
||||||
|
}
|
||||||
|
}
|
|
@ -26,27 +26,22 @@ func GetLastPositionInStorageArea(db *sql.DB, storageAreaId int) (int, error) {
|
||||||
return lastPosition, nil
|
return lastPosition, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func InsertCardLocation(db *sql.DB, storageLocation CardLocation) (int64, error) {
|
func InsertCardLocation(db *sql.DB, storageLocation CardLocation) error {
|
||||||
query := `INSERT INTO CardLocation
|
query := `INSERT INTO CardLocation
|
||||||
(CardPrintingId, StorageAreaId, Position)
|
(CardPrintingId, StorageAreaId, Position)
|
||||||
VALUES (?, ?, ?);`
|
VALUES (?, ?, ?);`
|
||||||
|
|
||||||
insert, err := db.Prepare(query)
|
insert, err := db.Prepare(query)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return -1, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
result, err := insert.Exec(storageLocation.CardPrintingId, storageLocation.StorageAreaId, storageLocation.Position)
|
_, err = insert.Exec(storageLocation.CardPrintingId, storageLocation.StorageAreaId, storageLocation.Position)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return -1, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
id, err := result.LastInsertId()
|
return nil
|
||||||
if err != nil {
|
|
||||||
return -1, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return id, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func InsertCardInExistingLocation(db *sql.DB, cardLocation CardLocation) error {
|
func InsertCardInExistingLocation(db *sql.DB, cardLocation CardLocation) error {
|
||||||
|
|
|
@ -20,33 +20,6 @@ type LocateCardResult struct {
|
||||||
Position int
|
Position int
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetLocateResultByCardLocationId(db *sql.DB, cardLocationId int) (LocateCardResult, error) {
|
|
||||||
var result LocateCardResult
|
|
||||||
|
|
||||||
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 CardLocation.Id = ?;`
|
|
||||||
|
|
||||||
err := db.QueryRow(query, cardLocationId).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 {
|
|
||||||
return result, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetLocateResults(db *sql.DB, cardNames []string) ([]LocateCardResult, error) {
|
func GetLocateResults(db *sql.DB, cardNames []string) ([]LocateCardResult, error) {
|
||||||
var results []LocateCardResult
|
var results []LocateCardResult
|
||||||
|
|
||||||
|
@ -88,39 +61,3 @@ func GetLocateResults(db *sql.DB, cardNames []string) ([]LocateCardResult, error
|
||||||
|
|
||||||
return results, nil
|
return results, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetAllStoredCards(db *sql.DB) ([]LocateCardResult, error) {
|
|
||||||
var results []LocateCardResult
|
|
||||||
|
|
||||||
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`
|
|
||||||
rows, err := db.Query(query)
|
|
||||||
defer rows.Close()
|
|
||||||
if err != nil {
|
|
||||||
return results, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var result LocateCardResult
|
|
||||||
for rows.Next() {
|
|
||||||
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 {
|
|
||||||
return results, err
|
|
||||||
}
|
|
||||||
|
|
||||||
results = append(results, result)
|
|
||||||
}
|
|
||||||
|
|
||||||
return results, nil
|
|
||||||
}
|
|
||||||
|
|
|
@ -26,28 +26,6 @@ CREATE TABLE IF NOT EXISTS CardPrinting (
|
||||||
Language VARCHAR(3) NOT NULL
|
Language VARCHAR(3) NOT NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
/*
|
|
||||||
INSERT INTO CardPrinting (
|
|
||||||
Id,
|
|
||||||
Name,
|
|
||||||
SetCode,
|
|
||||||
IsFoil,
|
|
||||||
IsPromo,
|
|
||||||
CollectorNumber,
|
|
||||||
ImageUrl,
|
|
||||||
Language
|
|
||||||
) VALUES (
|
|
||||||
'00000000-0000-0000-0000-0000000000000',
|
|
||||||
'Scanned Card Placeholder',
|
|
||||||
'lea',
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
'',
|
|
||||||
'en'
|
|
||||||
);
|
|
||||||
*/
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS StorageArea (
|
CREATE TABLE IF NOT EXISTS StorageArea (
|
||||||
Id INT AUTO_INCREMENT PRIMARY KEY,
|
Id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
Name VARCHAR(100) NOT NULL,
|
Name VARCHAR(100) NOT NULL,
|
||||||
|
@ -62,11 +40,3 @@ CREATE TABLE IF NOT EXISTS CardLocation (
|
||||||
FOREIGN KEY (StorageAreaId) REFERENCES StorageArea(Id),
|
FOREIGN KEY (StorageAreaId) REFERENCES StorageArea(Id),
|
||||||
Position INT NULL
|
Position INT NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
ALTER TABLE CardLocation ADD CardtraderProductId INT NULL;
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS CardScan (
|
|
||||||
Id INT PRIMARY KEY AUTO_INCREMENT,
|
|
||||||
CardLocationId INT NOT NULL,
|
|
||||||
Filename VARCHAR(100) NOT NULL
|
|
||||||
);
|
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
filename="$(adb shell 'find /sdcard/Download/ | grep Temporary')"
|
|
||||||
adb pull "$filename" /home/viciouscirce/dox/sevenkeys_imports/import.csv
|
|
||||||
adb shell "rm $filename"
|
|
||||||
./sevenkeys --profile=production import import.csv
|
|
||||||
rm /home/viciouscirce/dox/sevenkeys_imports/import.csv
|
|
|
@ -11,33 +11,24 @@ require (
|
||||||
|
|
||||||
require (
|
require (
|
||||||
filippo.io/edwards25519 v1.1.0 // indirect
|
filippo.io/edwards25519 v1.1.0 // indirect
|
||||||
github.com/PuerkitoBio/goquery v1.8.1 // indirect
|
|
||||||
github.com/andybalholm/cascadia v1.3.2 // indirect
|
|
||||||
github.com/atotto/clipboard v0.1.4 // indirect
|
github.com/atotto/clipboard v0.1.4 // indirect
|
||||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||||
github.com/charmbracelet/lipgloss v0.9.1 // indirect
|
github.com/charmbracelet/lipgloss v0.9.1 // indirect
|
||||||
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect
|
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/google/uuid v1.6.0 // indirect
|
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
|
||||||
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
|
|
||||||
github.com/inancgumus/screen v0.0.0-20190314163918-06e984b86ed3 // indirect
|
github.com/inancgumus/screen v0.0.0-20190314163918-06e984b86ed3 // indirect
|
||||||
github.com/jmoiron/sqlx v1.4.0 // indirect
|
github.com/jmoiron/sqlx v1.4.0 // indirect
|
||||||
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
||||||
github.com/lukesampson/figlet v0.0.0-20190211215653-8a3ef4a6ac42 // indirect
|
github.com/lukesampson/figlet v0.0.0-20190211215653-8a3ef4a6ac42 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.18 // indirect
|
||||||
github.com/mattn/go-localereader v0.0.1 // indirect
|
github.com/mattn/go-localereader v0.0.1 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||||
github.com/mroth/weightedrand/v2 v2.1.0 // indirect
|
|
||||||
github.com/mtgban/go-mtgban v0.0.0-20241115160804-6b7158d200d7 // indirect
|
|
||||||
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b // indirect
|
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b // indirect
|
||||||
github.com/muesli/cancelreader v0.2.2 // indirect
|
github.com/muesli/cancelreader v0.2.2 // indirect
|
||||||
github.com/muesli/reflow v0.3.0 // indirect
|
github.com/muesli/reflow v0.3.0 // indirect
|
||||||
github.com/muesli/termenv v0.15.2 // indirect
|
github.com/muesli/termenv v0.15.2 // indirect
|
||||||
github.com/rivo/uniseg v0.4.6 // indirect
|
github.com/rivo/uniseg v0.4.6 // indirect
|
||||||
golang.org/x/crypto v0.24.0 // indirect
|
golang.org/x/crypto v0.24.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect
|
|
||||||
golang.org/x/net v0.25.0 // indirect
|
|
||||||
golang.org/x/sync v0.7.0 // indirect
|
golang.org/x/sync v0.7.0 // indirect
|
||||||
golang.org/x/sys v0.21.0 // indirect
|
golang.org/x/sys v0.21.0 // indirect
|
||||||
golang.org/x/term v0.21.0 // indirect
|
golang.org/x/term v0.21.0 // indirect
|
||||||
|
|
|
@ -1,10 +1,5 @@
|
||||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||||
github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAcwmWM=
|
|
||||||
github.com/PuerkitoBio/goquery v1.8.1/go.mod h1:Q8ICL1kNUJ2sXGoAhPGUdYDJvgQgHzJsnnd3H7Ho5jQ=
|
|
||||||
github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA=
|
|
||||||
github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss=
|
|
||||||
github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU=
|
|
||||||
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
|
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
|
||||||
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
|
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
|
||||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
|
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
|
||||||
|
@ -23,12 +18,6 @@ github.com/go-mysql/errors v0.0.0-20180603193453-03314bea68e0 h1:meiLwrW6ukHHehy
|
||||||
github.com/go-mysql/errors v0.0.0-20180603193453-03314bea68e0/go.mod h1:ZH8V0509n2OSZLMYTMHzcy4hqUB+rG8ghK1zsP4i5gE=
|
github.com/go-mysql/errors v0.0.0-20180603193453-03314bea68e0/go.mod h1:ZH8V0509n2OSZLMYTMHzcy4hqUB+rG8ghK1zsP4i5gE=
|
||||||
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
|
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
|
||||||
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
||||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
|
||||||
github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU=
|
|
||||||
github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk=
|
|
||||||
github.com/inancgumus/screen v0.0.0-20190314163918-06e984b86ed3 h1:fO9A67/izFYFYky7l1pDP5Dr0BTCRkaQJUG6Jm5ehsk=
|
github.com/inancgumus/screen v0.0.0-20190314163918-06e984b86ed3 h1:fO9A67/izFYFYky7l1pDP5Dr0BTCRkaQJUG6Jm5ehsk=
|
||||||
github.com/inancgumus/screen v0.0.0-20190314163918-06e984b86ed3/go.mod h1:Ey4uAp+LvIl+s5jRbOHLcZpUDnkjLBROl15fZLwPlTM=
|
github.com/inancgumus/screen v0.0.0-20190314163918-06e984b86ed3/go.mod h1:Ey4uAp+LvIl+s5jRbOHLcZpUDnkjLBROl15fZLwPlTM=
|
||||||
github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o=
|
github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o=
|
||||||
|
@ -40,17 +29,12 @@ github.com/lukesampson/figlet v0.0.0-20190211215653-8a3ef4a6ac42 h1:UtyD+eBVdLYS
|
||||||
github.com/lukesampson/figlet v0.0.0-20190211215653-8a3ef4a6ac42/go.mod h1:/peI0OaxVYh7fzA72CD7rUsyGVdF7sCiFw7GcYqOcCw=
|
github.com/lukesampson/figlet v0.0.0-20190211215653-8a3ef4a6ac42/go.mod h1:/peI0OaxVYh7fzA72CD7rUsyGVdF7sCiFw7GcYqOcCw=
|
||||||
github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
|
github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
|
||||||
github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
|
||||||
github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
|
github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
|
||||||
github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
|
github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
|
||||||
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||||
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
||||||
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||||
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||||
github.com/mroth/weightedrand/v2 v2.1.0 h1:o1ascnB1CIVzsqlfArQQjeMy1U0NcIbBO5rfd5E/OeU=
|
|
||||||
github.com/mroth/weightedrand/v2 v2.1.0/go.mod h1:f2faGsfOGOwc1p94wzHKKZyTpcJUW7OJ/9U4yfiNAOU=
|
|
||||||
github.com/mtgban/go-mtgban v0.0.0-20241115160804-6b7158d200d7 h1:AKVe40N5i3GhO2sOq8Uipqr6/g5Ihln8AUcdReN8IVI=
|
|
||||||
github.com/mtgban/go-mtgban v0.0.0-20241115160804-6b7158d200d7/go.mod h1:0YMKwBLKOogXJdaTXsfr9sqHr0O0XJgDyGujjcQHbo4=
|
|
||||||
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b h1:1XF24mVaiu7u+CFywTdcDo2ie1pzzhwjt6RHqzpMU34=
|
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b h1:1XF24mVaiu7u+CFywTdcDo2ie1pzzhwjt6RHqzpMU34=
|
||||||
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho=
|
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho=
|
||||||
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
|
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
|
||||||
|
@ -63,64 +47,23 @@ github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ
|
||||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
github.com/rivo/uniseg v0.4.6 h1:Sovz9sDSwbOz9tgUy8JpT+KgCkPYJEN/oYzlJiYTNLg=
|
github.com/rivo/uniseg v0.4.6 h1:Sovz9sDSwbOz9tgUy8JpT+KgCkPYJEN/oYzlJiYTNLg=
|
||||||
github.com/rivo/uniseg v0.4.6/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
github.com/rivo/uniseg v0.4.6/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
|
||||||
golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
|
golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
|
||||||
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
|
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
|
||||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
|
|
||||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
|
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
|
||||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
|
||||||
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
|
||||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
|
||||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
|
||||||
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
|
|
||||||
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
|
|
||||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
||||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
|
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
|
||||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
|
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
|
||||||
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
|
||||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
|
||||||
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
|
|
||||||
golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
|
golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
|
||||||
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||||
golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA=
|
golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA=
|
||||||
golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
|
golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
|
||||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
|
||||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
|
||||||
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
|
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
|
||||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
|
||||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
|
|
|
@ -1,16 +1,9 @@
|
||||||
package logic
|
package logic
|
||||||
|
|
||||||
import (
|
import "sevenkeys/database"
|
||||||
"sevenkeys/database"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
func filterPrinting(printing database.CardPrinting, searchCriteria SearchCriteria) bool {
|
func filterPrinting(printing database.CardPrinting, searchCriteria SearchCriteria) bool {
|
||||||
if searchCriteria.SetCode != "" && !strings.Contains(printing.SetCode, searchCriteria.SetCode) {
|
if searchCriteria.SetCode != "" && printing.SetCode != searchCriteria.SetCode {
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
if searchCriteria.CollectorNumber != "" && !strings.Contains(printing.CollectorNumber, searchCriteria.CollectorNumber) {
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -400,27 +400,3 @@ 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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -30,17 +30,6 @@ 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 GetCardAtLocation(db *sql.DB, cardLocationId int) (database.LocateCardResult, error) {
|
|
||||||
var result database.LocateCardResult
|
|
||||||
|
|
||||||
result, err := database.GetLocateResultByCardLocationId(db, cardLocationId)
|
|
||||||
if err != nil {
|
|
||||||
return result, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func LocateCards(db *sql.DB, cardNames []string, criteria SearchCriteria) ([]database.LocateCardResult, error) {
|
func LocateCards(db *sql.DB, cardNames []string, criteria SearchCriteria) ([]database.LocateCardResult, error) {
|
||||||
results, err := database.GetLocateResults(db, cardNames)
|
results, err := database.GetLocateResults(db, cardNames)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -20,16 +20,17 @@ 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
|
||||||
}
|
}
|
||||||
|
|
||||||
type CardPrintingSearchOptions map[string]string
|
// The InsertSearchOptions type is a map of `string` keys (which can be searched using
|
||||||
|
// fzf) to `string` values (which represent a primary key in the CardPrintings table)
|
||||||
|
type InsertSearchOptions map[string]string
|
||||||
|
|
||||||
func GetAllCardPrintingSearchOptions(db *sql.DB, searchCriteria SearchCriteria) (CardPrintingSearchOptions, error) {
|
func GetAllSearchOptions(db *sql.DB, searchCriteria SearchCriteria) (InsertSearchOptions, error) {
|
||||||
var searchOptions CardPrintingSearchOptions = make(CardPrintingSearchOptions)
|
var searchOptions InsertSearchOptions = make(map[string]string)
|
||||||
|
|
||||||
cardPrintings, err := database.GetAllCardPrintings(db)
|
cardPrintings, err := database.GetAllCardPrintings(db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -60,24 +61,6 @@ func GetAllCardPrintingSearchOptions(db *sql.DB, searchCriteria SearchCriteria)
|
||||||
return searchOptions, err
|
return searchOptions, err
|
||||||
}
|
}
|
||||||
|
|
||||||
type StorageSearchOptions map[string]int
|
|
||||||
|
|
||||||
func GetAllStorageSearchOptions(db *sql.DB) (StorageSearchOptions, error) {
|
|
||||||
var searchOptions StorageSearchOptions = make(StorageSearchOptions)
|
|
||||||
|
|
||||||
storedCards, err := database.GetAllStoredCards(db)
|
|
||||||
if err != nil {
|
|
||||||
return searchOptions, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, storedCard := range storedCards {
|
|
||||||
searchString := GetLocationDescription(storedCard)
|
|
||||||
searchOptions[searchString] = storedCard.CardLocationId
|
|
||||||
}
|
|
||||||
|
|
||||||
return searchOptions, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func GenericSearch[pk string | int](options map[string]pk) (pk, string, error) {
|
func GenericSearch[pk string | int](options map[string]pk) (pk, string, error) {
|
||||||
var value pk
|
var value pk
|
||||||
|
|
||||||
|
|
|
@ -59,20 +59,20 @@ func GetStorageAreaSearchOptions(db *sql.DB) (StorageAreaSearchOptions, error) {
|
||||||
return options, nil
|
return options, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func storeAfterLastCard(db *sql.DB, cardLocation database.CardLocation) (int64, 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 -1, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
cardLocation.Position = lastPosition + 1
|
cardLocation.Position = lastPosition + 1
|
||||||
|
|
||||||
id, err := database.InsertCardLocation(db, cardLocation)
|
err = database.InsertCardLocation(db, cardLocation)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return -1, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return id, nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func storeInEmptySlot(db *sql.DB, cardLocation database.CardLocation, cardLocationId int) error {
|
func storeInEmptySlot(db *sql.DB, cardLocation database.CardLocation, cardLocationId int) error {
|
||||||
|
@ -85,47 +85,36 @@ func storeInEmptySlot(db *sql.DB, cardLocation database.CardLocation, cardLocati
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func StoreCard(db *sql.DB, cardLocation database.CardLocation) (int64, error) {
|
func StoreCard(db *sql.DB, cardLocation database.CardLocation) error {
|
||||||
storageAreaType, err := database.GetStorageAreaTypeById(db, cardLocation.StorageAreaId)
|
storageAreaType, err := database.GetStorageAreaTypeById(db, cardLocation.StorageAreaId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return -1, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if storageAreaType == database.StorageAreaTypeBinder {
|
if storageAreaType == database.StorageAreaTypeBinder {
|
||||||
nextEmptySlotId, err := database.GetNextEmptySlotInBinder(db, cardLocation.StorageAreaId)
|
nextEmptySlotId, err := database.GetNextEmptySlotInBinder(db, cardLocation.StorageAreaId)
|
||||||
|
|
||||||
if err == database.ErrNoEmptySlotsInBinder {
|
if err == database.ErrNoEmptySlotsInBinder {
|
||||||
id, err := storeAfterLastCard(db, cardLocation)
|
err = storeAfterLastCard(db, cardLocation)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return -1, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return id, nil
|
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
return -1, err
|
return err
|
||||||
} else {
|
} else {
|
||||||
err = storeInEmptySlot(db, cardLocation, nextEmptySlotId)
|
err = storeInEmptySlot(db, cardLocation, nextEmptySlotId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return -1, err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return int64(nextEmptySlotId), nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
id, err := storeAfterLastCard(db, cardLocation)
|
err = storeAfterLastCard(db, cardLocation)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return -1, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return id, nil
|
return nil
|
||||||
}
|
|
||||||
|
|
||||||
func Replace(db *sql.DB, cardLocationId int, cardPrintingId string) error {
|
|
||||||
cardLocation := database.CardLocation{
|
|
||||||
Id: cardLocationId,
|
|
||||||
CardPrintingId: cardPrintingId,
|
|
||||||
}
|
|
||||||
|
|
||||||
return database.InsertCardInExistingLocation(db, cardLocation)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,52 +1,18 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
|
||||||
"os"
|
"os"
|
||||||
|
"sevenkeys/cli"
|
||||||
"sevenkeys/database"
|
"sevenkeys/database"
|
||||||
"sevenkeys/delverlens"
|
"sevenkeys/delverlens"
|
||||||
|
"sevenkeys/figlet"
|
||||||
"sevenkeys/logic"
|
"sevenkeys/logic"
|
||||||
"sevenkeys/logic/scryfall"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/mtgban/go-mtgban/cardtrader"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
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"
|
|
||||||
CreateStorageAreaSubcommand string = "createstorage"
|
|
||||||
StoreSubcommand string = "store"
|
|
||||||
ImportSubcommand string = "import"
|
ImportSubcommand string = "import"
|
||||||
SearchPrintingsSubcommand string = "search-printings"
|
|
||||||
SearchStorageSubcommand string = "search-storage"
|
|
||||||
AddSubcommand string = "add"
|
|
||||||
RemoveSubcommand string = "remove"
|
|
||||||
ReplaceSubcommand string = "replace"
|
|
||||||
DeckSubcommand string = "deck"
|
|
||||||
|
|
||||||
GetProductIdSubcommand string = "products"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
@ -56,16 +22,15 @@ func main() {
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
db := database.GetDatabaseFromConfig("config." + profile + ".json")
|
db := database.GetDatabaseFromConfig("config." + profile + ".json")
|
||||||
/* Sad.
|
|
||||||
figlet.ReadFigletFonts()
|
figlet.ReadFigletFonts()
|
||||||
cli.ShowSplashScreen()
|
cli.ShowSplashScreen()
|
||||||
*/
|
cli.RunUpdateCheck(db)
|
||||||
|
|
||||||
// TODO: Decide in what form we need to retain this functionality if any
|
// TODO: Decide in what form we need to retain this functionality if any
|
||||||
//cli.MainCliLoop(db)
|
//cli.MainCliLoop(db)
|
||||||
|
|
||||||
//searchCmd := flag.NewFlagSet(SearchSubcommand, flag.ExitOnError)
|
importCmd := flag.NewFlagSet("import", flag.ExitOnError)
|
||||||
//name := searchCmd.String("name", "", "The card name to search for.")
|
storageArea := importCmd.String("storagearea", "", "The name of the StorageArea where cards should be imported.")
|
||||||
|
|
||||||
if len(flag.Args()) == 0 {
|
if len(flag.Args()) == 0 {
|
||||||
fmt.Fprintln(os.Stderr, "Please specify a subcommand.")
|
fmt.Fprintln(os.Stderr, "Please specify a subcommand.")
|
||||||
|
@ -73,77 +38,7 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
switch flag.Args()[0] {
|
switch flag.Args()[0] {
|
||||||
case UpdateSubcommand:
|
|
||||||
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
|
|
||||||
case CreateStorageAreaSubcommand:
|
|
||||||
createStorageCmd := flag.NewFlagSet(CreateStorageAreaSubcommand, flag.ExitOnError)
|
|
||||||
storageAreaName := createStorageCmd.String("name", "",
|
|
||||||
"The name of the StorageArea to create.")
|
|
||||||
storageAreaType := createStorageCmd.String("type", "",
|
|
||||||
"The name of the StorageArea to create.")
|
|
||||||
|
|
||||||
createStorageCmd.Parse(flag.Args()[1:])
|
|
||||||
|
|
||||||
storageArea := database.StorageArea{Name: *storageAreaName, StorageType: *storageAreaType}
|
|
||||||
err := logic.CreateStorageArea(db, storageArea)
|
|
||||||
logic.Check(err)
|
|
||||||
|
|
||||||
break
|
|
||||||
case StoreSubcommand:
|
|
||||||
storeCmd := flag.NewFlagSet(StoreSubcommand, flag.ExitOnError)
|
|
||||||
storageArea := storeCmd.String("storagearea", "",
|
|
||||||
"The name of the StorageArea the card should be inserted to.")
|
|
||||||
id := storeCmd.String("id", "", "The CardPrintingId of the card to store.")
|
|
||||||
|
|
||||||
storeCmd.Parse(flag.Args()[1:])
|
|
||||||
|
|
||||||
storageAreaId, err := logic.GetStorageAreaId(db, *storageArea)
|
|
||||||
if err == logic.ErrCouldNotGetStorageAreaId {
|
|
||||||
fmt.Fprintf(os.Stderr, "[sevenkeys] No storage area was selected, exiting.\n")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
cardLocation := database.CardLocation{
|
|
||||||
CardPrintingId: *id,
|
|
||||||
StorageAreaId: storageAreaId,
|
|
||||||
}
|
|
||||||
cardLocationId, err := logic.StoreCard(db, cardLocation)
|
|
||||||
logic.Check(err)
|
|
||||||
|
|
||||||
fmt.Printf("%d\n", cardLocationId)
|
|
||||||
break
|
|
||||||
case ImportSubcommand:
|
case ImportSubcommand:
|
||||||
importCmd := flag.NewFlagSet(ImportSubcommand, flag.ExitOnError)
|
|
||||||
storageArea := importCmd.String("storagearea", "",
|
|
||||||
"The name of the StorageArea where cards should be imported.")
|
|
||||||
|
|
||||||
importCmd.Parse(flag.Args()[1:])
|
importCmd.Parse(flag.Args()[1:])
|
||||||
|
|
||||||
storageAreaId, err := logic.GetStorageAreaId(db, *storageArea)
|
storageAreaId, err := logic.GetStorageAreaId(db, *storageArea)
|
||||||
|
@ -159,146 +54,6 @@ func main() {
|
||||||
err = logic.ImportDelverLensCards(db, delverLensCards, storageAreaId)
|
err = logic.ImportDelverLensCards(db, delverLensCards, storageAreaId)
|
||||||
logic.Check(err)
|
logic.Check(err)
|
||||||
break
|
break
|
||||||
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)
|
|
||||||
logic.Check(err)
|
|
||||||
id, _, err := logic.GenericSearch(searchOptions)
|
|
||||||
logic.Check(err)
|
|
||||||
fmt.Println(id)
|
|
||||||
break
|
|
||||||
case AddSubcommand:
|
|
||||||
addCmd := flag.NewFlagSet(AddSubcommand, flag.ExitOnError)
|
|
||||||
|
|
||||||
cardPrintingId := addCmd.String("card-printing-id", "", "The ID of the card printing to add to storage.")
|
|
||||||
storageArea := addCmd.String("storagearea", "",
|
|
||||||
"The name of the StorageArea where cards should be imported.")
|
|
||||||
|
|
||||||
addCmd.Parse(flag.Args()[1:])
|
|
||||||
|
|
||||||
storageAreaId, err := logic.GetStorageAreaId(db, *storageArea)
|
|
||||||
if err == logic.ErrCouldNotGetStorageAreaId {
|
|
||||||
fmt.Fprintf(os.Stderr, "[sevenkeys] No storage area was selected, exiting.\n")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
logic.Check(err)
|
|
||||||
|
|
||||||
cardLocation := database.CardLocation{
|
|
||||||
CardPrintingId: *cardPrintingId,
|
|
||||||
StorageAreaId: storageAreaId,
|
|
||||||
}
|
|
||||||
logic.StoreCard(db, cardLocation)
|
|
||||||
break
|
|
||||||
case RemoveSubcommand:
|
|
||||||
removeCmd := flag.NewFlagSet(RemoveSubcommand, flag.ExitOnError)
|
|
||||||
|
|
||||||
cardLocationId := removeCmd.Int("card-location-id", -1, "The card location to remove the card at.")
|
|
||||||
|
|
||||||
removeCmd.Parse(flag.Args()[1:])
|
|
||||||
|
|
||||||
if *cardLocationId == -1 {
|
|
||||||
log.Fatal(errors.New("No CardLocationId given."))
|
|
||||||
}
|
|
||||||
|
|
||||||
location, err := logic.GetCardAtLocation(db, *cardLocationId)
|
|
||||||
logic.Check(err)
|
|
||||||
|
|
||||||
err = logic.RemoveFromStorage(db, location)
|
|
||||||
logic.Check(err)
|
|
||||||
|
|
||||||
break
|
|
||||||
case ReplaceSubcommand:
|
|
||||||
replaceCmd := flag.NewFlagSet(ReplaceSubcommand, flag.ExitOnError)
|
|
||||||
|
|
||||||
cardLocationId := replaceCmd.Int("card-location-id", -1, "The card location to replace the card at.")
|
|
||||||
cardPrintingId := replaceCmd.String("card-printing-id", "", "The card printing to put at the specified location.")
|
|
||||||
|
|
||||||
replaceCmd.Parse(flag.Args()[1:])
|
|
||||||
|
|
||||||
if *cardLocationId == -1 {
|
|
||||||
log.Fatal(errors.New("No CardLocationId given."))
|
|
||||||
}
|
|
||||||
if *cardPrintingId == "" {
|
|
||||||
log.Fatal(errors.New("No CardPrintingId given."))
|
|
||||||
}
|
|
||||||
|
|
||||||
err := logic.Replace(db, *cardLocationId, *cardPrintingId)
|
|
||||||
logic.Check(err)
|
|
||||||
break
|
|
||||||
case DeckSubcommand:
|
|
||||||
deckCmd := flag.NewFlagSet(DeckSubcommand, flag.ExitOnError)
|
|
||||||
deckCmd.Parse(flag.Args()[1:])
|
|
||||||
|
|
||||||
//filename := deckCmd.Args()[0]
|
|
||||||
break
|
|
||||||
case GetProductIdSubcommand:
|
|
||||||
blbBlueprintsBytes, err := ioutil.ReadFile("blb_blueprints.json")
|
|
||||||
logic.Check(err)
|
|
||||||
|
|
||||||
var blbBlueprints []cardtrader.Blueprint
|
|
||||||
err = json.Unmarshal(blbBlueprintsBytes, &blbBlueprints)
|
|
||||||
logic.Check(err)
|
|
||||||
|
|
||||||
productsBytes, err := ioutil.ReadFile("products.json")
|
|
||||||
logic.Check(err)
|
|
||||||
|
|
||||||
var products []cardtrader.Product
|
|
||||||
err = json.Unmarshal(productsBytes, &products)
|
|
||||||
logic.Check(err)
|
|
||||||
|
|
||||||
for _, product := range products {
|
|
||||||
var productBlueprint cardtrader.Blueprint
|
|
||||||
for _, blueprint := range blbBlueprints {
|
|
||||||
if blueprint.Id == product.BlueprintId {
|
|
||||||
productBlueprint = blueprint
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("%s %s %d ",
|
|
||||||
productBlueprint.Name,
|
|
||||||
product.Properties.Number,
|
|
||||||
product.Id,
|
|
||||||
)
|
|
||||||
|
|
||||||
if product.Properties.MTGFoil {
|
|
||||||
fmt.Printf("FOIL ")
|
|
||||||
} else {
|
|
||||||
fmt.Printf("NONFOIL ")
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("x%d\n", product.Quantity)
|
|
||||||
}
|
|
||||||
break
|
|
||||||
default:
|
default:
|
||||||
fmt.Fprintf(os.Stderr, "Unrecognized subcommand: %s\n", os.Args[1])
|
fmt.Fprintf(os.Stderr, "Unrecognized subcommand: %s\n", os.Args[1])
|
||||||
break
|
break
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
|
|
||||||
goose -s -dir database/migrations/ create "$1" sql
|
|
|
@ -1,3 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
|
|
||||||
sevenkeys --profile=production remove --card-location-id="$(sevenkeys --profile=production search-storage)"
|
|