Add find, refactor types

This commit is contained in:
Hamcha 2019-11-21 16:56:34 +01:00
parent a2d9e5bcbc
commit 748cb12c2c
Signed by: hamcha
GPG Key ID: 44AD3571EB09A39E
8 changed files with 1464 additions and 34 deletions

18
api.ts
View File

@ -1,6 +1,8 @@
import { stringify } from "querystring";
import { get } from "request";
import { Article } from "./types";
const URI = "https://api.cardmarket.com/ws/v2.0/output.json";
export default class CardMarketApi {
@ -48,4 +50,20 @@ export default class CardMarketApi {
);
});
}
async getAllArticles(uid: string): Promise<Article[]> {
const perPage = 1000;
let start = 0;
let response;
let data: Article[] = [];
do {
response = await this.get(`/users/${uid}/articles`, {
start,
maxResults: perPage
});
data = data.concat(response.article as Article[]);
start += response.article.length;
} while (response.article.length == perPage);
return data;
}
}

View File

@ -1,7 +1,10 @@
import { existsSync } from "fs";
import { asyncLoadJSON, asyncSaveJSON } from "./utils";
interface JSONCard {
mcmId: number;
set: string;
}
interface JSONSet {
@ -11,12 +14,23 @@ interface JSONSet {
type JSONDB = Record<string, JSONSet>;
async function run() {
if (!existsSync("AllPrintings.json")) {
console.error(
"AllPrintings.json not found. Download from: https://www.mtgjson.com/files/AllPrintings.json"
);
process.exit(1);
}
console.info("Loading AllPrintings.json");
const db = await asyncLoadJSON<JSONDB>("AllPrintings.json");
let acc = {};
for (const set in db) {
db[set].cards.forEach(c => (acc[c.mcmId] = c));
db[set].cards.forEach(c => {
c.set = set;
acc[c.mcmId] = c;
});
}
asyncSaveJSON("mcmCards.json", acc);
console.info("Saved converted info to mcmCards.json. You're ready to go!");
}
run();

View File

@ -1,30 +1,5 @@
import { existsSync } from "fs";
import CardMarketApi from "./api";
import { asyncLoadJSON, asyncSaveJSON } from "./utils";
interface MCMCard {
name: string;
rarity: string;
}
type MCMDB = Record<string, MCMCard>;
async function getAllArticles(api: CardMarketApi, uid: string) {
const perPage = 1000;
let start = 0;
let response;
let data = [];
do {
response = await api.get(`/users/${uid}/articles`, {
start,
maxResults: perPage
});
data = data.concat(response.article);
start += response.article.length;
} while (response.article.length == perPage);
return data;
}
import { asyncSaveJSON } from "./utils";
async function run() {
if (process.argv.length < 3) {
@ -33,13 +8,8 @@ async function run() {
return;
}
const uid = process.argv[2];
if (!existsSync("mcmCards.json")) {
console.error("Card db is missing! Run 'yarn convert-db' first.");
process.exit(1);
}
let db = await asyncLoadJSON<MCMDB>("mcmCards.json");
let api = new CardMarketApi();
const articles = await getAllArticles(api, uid);
const articles = await api.getAllArticles(uid);
await asyncSaveJSON(`${uid}-cards.json`, articles);
}

55
find.ts Normal file
View File

@ -0,0 +1,55 @@
import { existsSync } from "fs";
import { Article, MCMDB } from "./types";
import { asyncLoadJSON, asyncSaveJSON, leanArticle, leanCard } from "./utils";
async function run() {
if (process.argv.length < 3) {
console.error("Usage: yarn fetch <uid>");
process.exit(1);
return;
}
const uid = process.argv[2];
const uidCards = `${uid}-cards.json`;
if (!existsSync("mcmCards.json")) {
console.error("Card db is missing! Run 'yarn convert-db' first.");
process.exit(1);
}
if (!existsSync(uidCards)) {
console.error(`Could not find ${uidCards}! Run 'yarn fetch ${uid}' first.`);
process.exit(1);
}
let db = await asyncLoadJSON<MCMDB>("mcmCards.json");
let articles = await asyncLoadJSON<Article[]>(`${uid}-cards.json`);
let cards = articles
.filter(art => art.idProduct in db)
.map(art => {
const card = db[art.idProduct];
return {
...leanArticle(art),
...leanCard(card)
};
});
let valid = cards.filter(
c =>
(c.language == "Italian" || c.language == "English") &&
c.price <= 0.1 &&
(c.rarity == "rare" || c.rarity == "mythic")
);
let netprice = valid.reduce((a, c) => (a += c.price), 0);
console.log(
`Found ${
valid.length
} cards (rare or mythic) <= 0.10 (IT/EN), price to buy all: ${netprice.toFixed(
2
)}.`
);
await asyncSaveJSON(
`${uid}-filtered-cards.json`,
valid.sort((a, b) => a.edhrecRank - b.edhrecRank)
);
}
run();

View File

@ -7,7 +7,8 @@
"license": "MIT",
"scripts": {
"fetch": "ts-node fetch.ts",
"convert-db": "ts-node convert.ts"
"init-db": "ts-node convert.ts",
"find": "ts-node find.ts"
},
"dependencies": {
"@types/node": "^12.12.11",

133
types.ts Normal file
View File

@ -0,0 +1,133 @@
export interface ForeignData {
flavorText: string;
language: string;
multiverseId: number;
name: string;
text: string;
type: string;
}
export interface Legalities {
commander: string;
duel: string;
legacy: string;
modern: string;
vintage: string;
}
export interface Prices {
mtgo: Record<string, number>;
mtgoFoil: Record<string, number>;
paper: Record<string, number>;
paperFoil: Record<string, number>;
}
export interface PurchaseUrls {
cardmarket: string;
tcgplayer: string;
}
export interface MCMCard {
artist: string;
borderColor: string;
colorIdentity: string[];
colors: string[];
convertedManaCost: number;
edhrecRank: number;
flavorText: string;
foreignData: ForeignData[];
frameVersion: string;
hasFoil: boolean;
hasNonFoil: boolean;
isMtgo: boolean;
isPaper: boolean;
layout: string;
legalities: Legalities;
manaCost: string;
mcmId: number;
mcmMetaId: number;
mtgoFoilId: number;
mtgoId: number;
multiverseId: number;
name: string;
number: string;
originalText: string;
originalType: string;
power: string;
prices: Prices;
printings: string[];
purchaseUrls: PurchaseUrls;
rarity: string;
rulings: any[];
scryfallId: string;
scryfallIllustrationId: string;
scryfallOracleId: string;
subtypes: string[];
supertypes: any[];
tcgplayerProductId: number;
text: string;
toughness: string;
type: string;
types: string[];
uuid: string;
set: string;
}
export type MCMDB = Record<string, MCMCard>;
// Lighther version of MCMCard without a lot of useless information
export interface LeanMCMCard {
colorIdentity: string[];
colors: string[];
convertedManaCost: number;
edhrecRank: number;
manaCost: string;
mcmId: number;
name: string;
rarity: string;
scryfallId: string;
subtypes: string[];
supertypes: any[];
text: string;
type: string;
types: string[];
set: string;
}
export interface Language {
idLanguage: number;
languageName: string;
}
export interface Link {
rel: string;
href: string;
method: string;
action: string;
idArticle?: number;
}
export interface Article {
idArticle: number;
idProduct: number;
language: Language;
comments: string;
price: number;
count: number;
inShoppingCart: boolean;
condition: string;
isFoil: boolean;
isSigned: boolean;
isPlayset: boolean;
isAltered: boolean;
links: Link[];
}
export interface LeanArticle {
idArticle: number;
language: string;
comments: string;
price: number;
count: number;
condition: string;
}

View File

@ -1,5 +1,7 @@
import { readFile, writeFile } from "fs";
import { Article, LeanArticle, LeanMCMCard, MCMCard } from "./types";
export async function asyncLoadJSON<T>(filename: string): Promise<T> {
return new Promise((resolve, reject) => {
readFile(filename, "utf8", (err, data) => {
@ -24,3 +26,34 @@ export async function asyncSaveJSON(
});
});
}
export function leanCard(card: MCMCard): LeanMCMCard {
return {
colorIdentity: card.colorIdentity,
colors: card.colors,
convertedManaCost: card.convertedManaCost,
edhrecRank: card.edhrecRank,
manaCost: card.manaCost,
mcmId: card.mcmId,
name: card.name,
rarity: card.rarity,
scryfallId: card.scryfallId,
subtypes: card.subtypes,
supertypes: card.supertypes,
text: card.text,
type: card.type,
types: card.types,
set: card.set
};
}
export function leanArticle(article: Article): LeanArticle {
return {
idArticle: article.idArticle,
language: article.language.languageName,
comments: article.comments,
price: article.price,
count: article.count,
condition: article.condition
};
}

1206
yarn-error.log Normal file

File diff suppressed because it is too large Load Diff