1
0
mirror of https://github.com/tcgdex/cards-database.git synced 2025-06-13 00:09:18 +00:00

feat: Add status Dashboard (#187)

* feat: Add status Dashboard

Still need some polishing like using the compiler instead of the live DB

Signed-off-by: Avior <github@avior.me>

* refactor: Simplified compiler files generators

Signed-off-by: Avior <florian.bouillon@delta-wings.net>

* chore: Add step to compiler for stats and optimize

Signed-off-by: Avior <github@avior.me>

* refactor: Remove unused variable

Signed-off-by: Avior <github@avior.me>
This commit is contained in:
2021-11-23 15:12:50 +00:00
committed by GitHub
parent 8ef099273e
commit 762ce389c1
18 changed files with 431 additions and 142 deletions

View File

@ -1,7 +1,3 @@
// eslint-disable-next-line @typescript-eslint/ban-types
export interface Endpoint<Index extends {} = {}, Item extends {} = {}, SubItem extends {} = {}, C = undefined> {
index(common: C): Promise<Index | undefined>
item(common: C): Promise<Record<string, Item> | undefined>
sub?(common: C, item: string): Promise<Record<string, SubItem> | undefined>
common?(): Promise<C>
}
import { SupportedLanguages } from '../../interfaces'
export type FileFunction = (lang: SupportedLanguages) => Promise<any>

View File

@ -1,30 +1,10 @@
import { Card as CardSingle, CardResume } from '../../../meta/definitions/api'
import { Card, Languages } from '../../../interfaces'
import { Endpoint } from '../compilerInterfaces'
import { cardToCardSimple, cardToCardSingle, getCards } from '../utils/cardUtil'
type CardList = Array<CardResume>
export default class implements Endpoint<CardList, CardSingle, Record<string, unknown>, Array<[string, Card]>> {
public constructor(
private lang: keyof Languages
) {}
public async index(common: Array<[string, Card]>): Promise<CardList> {
return Promise.all(common.map((c) => cardToCardSimple(c[0], c[1], this.lang)))
}
public async item(common: Array<[string, Card]>): Promise<Record<string, CardSingle>> {
const items: Record<string, CardSingle> = {}
for await (const card of common) {
items[`${card[1].set.id}-${card[0]}`] = await cardToCardSingle(card[0], card[1], this.lang)
}
return items
}
public async common(): Promise<Array<[string, Card]>> {
return getCards(this.lang)
}
import { SupportedLanguages } from '../../../interfaces'
import { FileFunction } from '../compilerInterfaces'
import { cardToCardSingle, getCards } from '../utils/cardUtil'
const fn: FileFunction = async (lang: SupportedLanguages) => {
const common = await getCards(lang)
return await Promise.all(common.map((card) => cardToCardSingle(card[0], card[1], lang)))
}
export default fn

View File

@ -1,32 +1,10 @@
import { Serie as SerieSingle, SerieResume } from '../../../meta/definitions/api'
import { Languages, Serie } from '../../../interfaces'
import { Endpoint } from '../compilerInterfaces'
import { getSeries, serieToSerieSimple, serieToSerieSingle } from '../utils/serieUtil'
type SerieList = Array<SerieResume>
export default class implements Endpoint<SerieList, SerieSingle, Record<string, any>, Array<Serie>> {
public constructor(
private lang: keyof Languages
) {}
public async index(common: Array<Serie>): Promise<Array<SerieResume>> {
return Promise.all(common.map((s) => serieToSerieSimple(s, this.lang)))
}
public async item(common: Array<Serie>): Promise<Record<string, SerieSingle>> {
const items: Record<string, SerieSingle> = {}
for await (const val of common) {
const gen = await serieToSerieSingle(val, this.lang)
const name = val.name[this.lang] as string
items[val.id] = gen
}
return items
}
public async common(): Promise<Array<Serie>> {
return getSeries(this.lang)
}
import { SupportedLanguages } from '../../../interfaces'
import { FileFunction } from '../compilerInterfaces'
import { getSeries, serieToSerieSingle } from '../utils/serieUtil'
const fn: FileFunction = async (lang: SupportedLanguages) => {
const common = await getSeries(lang)
return await Promise.all(common.map((val) => serieToSerieSingle(val, lang)))
}
export default fn

View File

@ -1,56 +1,11 @@
import { Set as SetSingle, Card as CardSingle, SetResume } from '../../../meta/definitions/api'
import { getSets, isSetAvailable, setToSetSimple, setToSetSingle } from '../utils/setUtil'
import { Languages, Set } from '../../../interfaces'
import { Endpoint } from '../compilerInterfaces'
import { cardToCardSingle, getCards } from '../utils/cardUtil'
import { getSets, setToSetSingle } from '../utils/setUtil'
import { SupportedLanguages } from '../../../interfaces'
import { FileFunction } from '../compilerInterfaces'
type SetList = Array<SetResume>
export default class implements Endpoint<SetList, SetSingle, CardSingle, Array<Set>> {
public constructor(
private lang: keyof Languages
) {}
public async index(common: Array<Set>): Promise<SetList> {
const sets = common
.sort((a, b) => a.releaseDate > b.releaseDate ? 1 : -1)
const tmp: SetList = await Promise.all(sets.map((el) => setToSetSimple(el, this.lang)))
return tmp
}
public async item(common: Array<Set>): Promise<Record<string, SetSingle>> {
const sets = await Promise.all(common
.map((set) => setToSetSingle(set, this.lang)))
const res: Record<string, SetSingle> = {}
for (const set of sets) {
res[set.id] = set
}
return res
}
public async common(): Promise<Array<Set>> {
return getSets(undefined, this.lang)
}
public async sub(common: Array<Set>, item: string): Promise<Record<string, CardSingle> | undefined> {
const set = common.find((s) => s.name[this.lang] === item || s.id === item)
if (!set || !isSetAvailable(set, this.lang)) {
return undefined
}
const lit = await getCards(this.lang, set)
const list: Record<string, CardSingle> = {}
for await (const card of lit) {
list[card[0]] = await cardToCardSingle(card[0], card[1], this.lang)
}
return list
}
const fn: FileFunction = async (lang: SupportedLanguages) => {
const common = await getSets(undefined, lang)
return await Promise.all(common.map((set) => setToSetSingle(set, lang)))
}
export default fn

View File

@ -0,0 +1,42 @@
import { getSets, setToSetSingle } from '../utils/setUtil'
import { SupportedLanguages } from '../../../interfaces'
import { FileFunction } from '../compilerInterfaces'
import { getCards } from '../utils/cardUtil'
import { getSeries } from '../utils/serieUtil'
interface Stats {
count: number
total: number
images: number
sets: Record<string, Record<string, {name: string, count: number, images: number}>>
}
const fn: FileFunction = async (lang: SupportedLanguages) => {
const stats: Partial<Stats> = {}
stats.count = (await getCards(lang)).length
const langSets = await Promise.all(await getSets(undefined, lang).then((sets) => sets.map(async (set) => await setToSetSingle(set, lang))))
const englishSets = await Promise.all(await getSets(undefined, 'en').then((sets) => sets.map(async (set) => await setToSetSingle(set, 'en'))))
stats.total = langSets.reduce((p, set) => p + (englishSets.find((s) => set.id === s.id)?.cardCount?.total ?? 0), 0)
stats.images = langSets.reduce((p1, set) => p1 + (set.cards.reduce((p2, card) => p2 + (card.image ? 1 : 0), 0)), 0)
stats.sets = {}
const series = await getSeries(lang)
for (const serie of series) {
stats.sets[serie.id] = {}
for (const set of langSets.filter((set) => set.serie.id === serie.id)) {
stats.sets[serie.id][set.id] = {
name: set.name,
count: set.cards.length,
images: set.cards.reduce((p, card) => p + (card.image ? 1 : 0), 0)
}
}
}
//const sts = await Promise.all(sets.map((set) => getCards(lang, set)))
return stats
}
export default fn

View File

@ -1,16 +1,18 @@
/* eslint-disable max-statements */
import { Endpoint } from './compilerInterfaces'
import { FileFunction } from './compilerInterfaces'
import { promises as fs } from 'fs'
import { fetchRemoteFile } from './utils/util'
import { objectValues } from '@dzeio/object-util'
import { SupportedLanguages } from '../../interfaces'
const LANGS = ['en', 'fr', 'es', 'it', 'pt', 'de']
const LANGS: Array<SupportedLanguages> = ['en', 'fr', 'es', 'it', 'pt', 'de']
const DIST_FOLDER = './generated'
;(async () => {
const paths = (await fs.readdir('./compiler/endpoints')).filter((p) => p.endsWith('.ts'))
// Prefetch the pictures at the start as it can bug because of bad connection
console.log('Prefetching pictures')
await fetchRemoteFile('https://assets.tcgdex.net/datas.json')
@ -35,26 +37,18 @@ const DIST_FOLDER = './generated'
await fs.mkdir(folder, {recursive: true})
// Import the """Endpoint"""
const Ep = (await import(`./endpoints/${file}`)).default
const fn = (await import(`./endpoints/${file}`)).default as FileFunction
const endpoint = new Ep(lang) as Endpoint
console.log(file, 'Running Common')
let common: any | null = null
if (endpoint.common) {
common = await endpoint.common()
}
console.log(file, 'Running Item')
const item = await endpoint.item(common)
// Run the function
console.log(file, 'Running...')
const item = await fn(lang)
// Write to file
await fs.writeFile(`${folder}/${file.replace('.ts', '')}.json`, JSON.stringify(
objectValues(item)
item
))
console.log(file, 'Finished Item')
console.log(file, 'Finished!')
}
}

View File

@ -54,7 +54,8 @@ export async function cardToCardSingle(localId: string, card: Card, lang: Suppor
firstEdition: typeof card.variants?.firstEdition === 'boolean' ? card.variants.firstEdition : false,
holo: typeof card.variants?.holo === 'boolean' ? card.variants.holo : true,
normal: typeof card.variants?.normal === 'boolean' ? card.variants.normal : true,
reverse: typeof card.variants?.reverse === 'boolean' ? card.variants.reverse : true
reverse: typeof card.variants?.reverse === 'boolean' ? card.variants.reverse : true,
wPromo: typeof card.variants?.wPromo === 'boolean' ? card.variants.wPromo : false
},
@ -120,6 +121,12 @@ export async function getCard(serie: string, setName: string, id: string): Promi
return (await import(`../../${DB_PATH}/data/${serie}/${setName}/${id}.js`)).default
}
/**
* Get cards filtered by the language they are available in
* @param lang the language of the cards
* @param set the set to filter in (optional)
* @returns An array with the 0 = localId, 1 = Card Object
*/
export async function getCards(lang: SupportedLanguages, set?: Set): Promise<Array<[string, Card]>> {
const cards = await smartGlob(`${DB_PATH}/data/${(set && set.serie.name.en) ?? '*'}/${(set && set.name.en) ?? '*'}/*.js`)
const list: Array<[string, Card]> = []

View File

@ -57,6 +57,8 @@ export async function serieToSerieSingle(serie: Serie, lang: SupportedLanguages)
.sort((a, b) => a.releaseDate > b.releaseDate ? 1 : -1)
.map((el) => setToSetSimple(el, lang)))
const logo = sets.find((set) => set.logo)?.logo
// Final data
return {
id: serie.id,
logo,

View File

@ -15,7 +15,7 @@ export function isSetAvailable(set: Set, lang: SupportedLanguages): boolean {
/**
* Return the set
* @param name the name of the set (don't include.js/.ts)
* @param name the name of the set
*/
export async function getSet(name: string, serie = '*'): Promise<Set> {
if (!setCache[name]) {

View File

@ -11,6 +11,11 @@ export const DB_PATH = "../"
const fileCache: fileCacheInterface = {}
/**
* Fetch a JSON file from a remote location
* @param url the URL to fetch
* @returns the JSON file content
*/
export async function fetchRemoteFile<T = any>(url: string): Promise<T> {
if (!fileCache[url]) {
const resp = await fetch(url, {
@ -32,6 +37,13 @@ export async function smartGlob(query: string): Promise<Array<string>> {
return globCache[query]
}
/**
* Check if a card is currently Legal
* @param type the type of legality
* @param card the card to check
* @param localId the card localid
* @returns {boolean} if the card is currently in the legal type
*/
export function cardIsLegal(type: 'standard' | 'expanded', card: Card, localId: string): boolean {
const legal = legals[type]
if (
@ -47,6 +59,12 @@ export function cardIsLegal(type: 'standard' | 'expanded', card: Card, localId:
return false
}
/**
* Check if a set is currently Legal
* @param type the type of legality
* @param set the set to check
* @returns {boolean} if the set is currently in the legal type
*/
export function setIsLegal(type: 'standard' | 'expanded', set: Set): boolean {
const legal = legals[type]
if (