Compare commits
22 Commits
0e3ee54e1c
...
d2bc986506
Author | SHA1 | Date |
---|---|---|
The Magician | d2bc986506 | |
The Magician | 78e1c865c7 | |
The Magician | e6466cc01a | |
The Magician | 2fdbb0d467 | |
The Magician | 9d6d39d75f | |
The Magician | 2ab69dce8e | |
The Magician | 7fd6ed07f3 | |
The Magician | 93184d4c52 | |
The Magician | 1b579a28da | |
The Magician | b30525c2d4 | |
The Magician | 0dd67f3bb8 | |
The Magician | 2ef339bbce | |
The Magician | 6a4ff5dffd | |
The Magician | ec5328fed1 | |
The Magician | b90a570d29 | |
The Magician | 0e1647bb2d | |
The Magician | a028126fba | |
The Magician | 5e271c4132 | |
The Magician | 03e8dc8c03 | |
The Magician | c1f581f312 | |
The Magician | f7e0a713f6 | |
The Magician | c0c290916a |
|
@ -1,5 +0,0 @@
|
||||||
cmake_install.cmake
|
|
||||||
CMakeCache.txt
|
|
||||||
CMakeFiles/*
|
|
||||||
Makefile
|
|
||||||
AScannerDarkly
|
|
|
@ -1,7 +0,0 @@
|
||||||
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 )
|
|
Before Width: | Height: | Size: 5.2 KiB |
Before Width: | Height: | Size: 124 KiB |
Before Width: | Height: | Size: 126 KiB |
Before Width: | Height: | Size: 115 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 9.0 KiB |
Before Width: | Height: | Size: 4.7 KiB |
Before Width: | Height: | Size: 4.7 KiB |
Before Width: | Height: | Size: 4.8 KiB |
Before Width: | Height: | Size: 5.0 KiB |
|
@ -1,161 +0,0 @@
|
||||||
#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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Before Width: | Height: | Size: 301 KiB |
|
@ -1,24 +0,0 @@
|
||||||
#!/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()
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
#!/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
|
|
@ -0,0 +1,8 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
crop() {
|
||||||
|
convert "$1" -crop 1200x120+90+100 "$2"
|
||||||
|
}
|
||||||
|
|
||||||
|
crop old_border.png name_old.png
|
||||||
|
crop new_border.png name_new.png
|
|
@ -0,0 +1,49 @@
|
||||||
|
#!/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
|
|
@ -0,0 +1,9 @@
|
||||||
|
#!/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
|
|
@ -0,0 +1,34 @@
|
||||||
|
#!/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
|
|
@ -0,0 +1,12 @@
|
||||||
|
#!/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
|
|
@ -1,8 +0,0 @@
|
||||||
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
|
|
||||||
);
|
|
|
@ -1,59 +0,0 @@
|
||||||
#!/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,9 +1,17 @@
|
||||||
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
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
sevenkeys --profile=production add --card-printing-id="$(sevenkeys --profile=production search-printings)" --storagearea="$1"
|
|
@ -0,0 +1,7 @@
|
||||||
|
#!/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 .
|
|
@ -1,149 +0,0 @@
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -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")
|
|
||||||
}
|
|
|
@ -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)
|
|
||||||
}
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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.")
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -26,22 +26,27 @@ func GetLastPositionInStorageArea(db *sql.DB, storageAreaId int) (int, error) {
|
||||||
return lastPosition, nil
|
return lastPosition, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func InsertCardLocation(db *sql.DB, storageLocation CardLocation) error {
|
func InsertCardLocation(db *sql.DB, storageLocation CardLocation) (int64, 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 err
|
return -1, err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = insert.Exec(storageLocation.CardPrintingId, storageLocation.StorageAreaId, storageLocation.Position)
|
result, err := insert.Exec(storageLocation.CardPrintingId, storageLocation.StorageAreaId, storageLocation.Position)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return -1, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
id, err := result.LastInsertId()
|
||||||
|
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,6 +20,33 @@ 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
|
||||||
|
|
||||||
|
@ -61,3 +88,39 @@ 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,6 +26,28 @@ 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,
|
||||||
|
@ -40,3 +62,11 @@ 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
|
||||||
|
);
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
#!/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,24 +11,33 @@ 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.18 // indirect
|
github.com/mattn/go-isatty v0.0.20 // 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,5 +1,10 @@
|
||||||
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=
|
||||||
|
@ -18,6 +23,12 @@ 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=
|
||||||
|
@ -29,12 +40,17 @@ 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=
|
||||||
|
@ -47,23 +63,64 @@ 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,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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -30,6 +30,17 @@ 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,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(map[string]string)
|
var searchOptions CardPrintingSearchOptions = make(CardPrintingSearchOptions)
|
||||||
|
|
||||||
cardPrintings, err := database.GetAllCardPrintings(db)
|
cardPrintings, err := database.GetAllCardPrintings(db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -61,6 +60,24 @@ func GetAllSearchOptions(db *sql.DB, searchCriteria SearchCriteria) (InsertSearc
|
||||||
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) error {
|
func storeAfterLastCard(db *sql.DB, cardLocation database.CardLocation) (int64, error) {
|
||||||
lastPosition, err := database.GetLastPositionInStorageArea(db, cardLocation.StorageAreaId)
|
lastPosition, err := database.GetLastPositionInStorageArea(db, cardLocation.StorageAreaId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return -1, err
|
||||||
}
|
}
|
||||||
|
|
||||||
cardLocation.Position = lastPosition + 1
|
cardLocation.Position = lastPosition + 1
|
||||||
|
|
||||||
err = database.InsertCardLocation(db, cardLocation)
|
id, err := database.InsertCardLocation(db, cardLocation)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return -1, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return id, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func storeInEmptySlot(db *sql.DB, cardLocation database.CardLocation, cardLocationId int) error {
|
func storeInEmptySlot(db *sql.DB, cardLocation database.CardLocation, cardLocationId int) error {
|
||||||
|
@ -85,36 +85,47 @@ func storeInEmptySlot(db *sql.DB, cardLocation database.CardLocation, cardLocati
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func StoreCard(db *sql.DB, cardLocation database.CardLocation) error {
|
func StoreCard(db *sql.DB, cardLocation database.CardLocation) (int64, error) {
|
||||||
storageAreaType, err := database.GetStorageAreaTypeById(db, cardLocation.StorageAreaId)
|
storageAreaType, err := database.GetStorageAreaTypeById(db, cardLocation.StorageAreaId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return -1, 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 {
|
||||||
err = storeAfterLastCard(db, cardLocation)
|
id, err := storeAfterLastCard(db, cardLocation)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return -1, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return id, nil
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
return err
|
return -1, err
|
||||||
} else {
|
} else {
|
||||||
err = storeInEmptySlot(db, cardLocation, nextEmptySlotId)
|
err = storeInEmptySlot(db, cardLocation, nextEmptySlotId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return -1, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return int64(nextEmptySlotId), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
err = storeAfterLastCard(db, cardLocation)
|
id, err := storeAfterLastCard(db, cardLocation)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return -1, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return id, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Replace(db *sql.DB, cardLocationId int, cardPrintingId string) error {
|
||||||
|
cardLocation := database.CardLocation{
|
||||||
|
Id: cardLocationId,
|
||||||
|
CardPrintingId: cardPrintingId,
|
||||||
|
}
|
||||||
|
|
||||||
|
return database.InsertCardInExistingLocation(db, cardLocation)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +1,52 @@
|
||||||
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() {
|
||||||
|
@ -22,15 +56,16 @@ 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)
|
||||||
|
|
||||||
importCmd := flag.NewFlagSet("import", flag.ExitOnError)
|
//searchCmd := flag.NewFlagSet(SearchSubcommand, flag.ExitOnError)
|
||||||
storageArea := importCmd.String("storagearea", "", "The name of the StorageArea where cards should be imported.")
|
//name := searchCmd.String("name", "", "The card name to search for.")
|
||||||
|
|
||||||
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.")
|
||||||
|
@ -38,7 +73,77 @@ 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)
|
||||||
|
@ -54,6 +159,146 @@ 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
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
goose -s -dir database/migrations/ create "$1" sql
|
|
@ -0,0 +1,3 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
sevenkeys --profile=production remove --card-location-id="$(sevenkeys --profile=production search-storage)"
|