Prioritize newest missing sets in catalog sync queue. (#49)
The cron was importing oldest MTG sets first and never reaching recent Pokémon releases like Perfect Order; merge MTG and Pokémon by release date descending instead. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
0a47362103
commit
bec0a7abbd
4 changed files with 95 additions and 8 deletions
|
|
@ -24,7 +24,7 @@ export function filterMissingMtgSets(scryfallSets, knownCodes, now = new Date())
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
missing.sort((a, b) => a.releasedAt.localeCompare(b.releasedAt));
|
missing.sort((a, b) => b.releasedAt.localeCompare(a.releasedAt));
|
||||||
return missing;
|
return missing;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -46,7 +46,14 @@ export function filterMissingPokemonSets(pokemonSets, knownCodes) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
missing.sort((a, b) => (a.releasedAt || '').localeCompare(b.releasedAt || ''));
|
missing.sort((a, b) => {
|
||||||
|
const dateA = a.releasedAt || '';
|
||||||
|
const dateB = b.releasedAt || '';
|
||||||
|
if (!dateA && !dateB) return 0;
|
||||||
|
if (!dateA) return 1;
|
||||||
|
if (!dateB) return -1;
|
||||||
|
return dateB.localeCompare(dateA);
|
||||||
|
});
|
||||||
return missing;
|
return missing;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,37 @@ const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
const DEFAULT_MAX_SETS_PER_RUN = 3;
|
const DEFAULT_MAX_SETS_PER_RUN = 3;
|
||||||
const DEFAULT_DELAY_MS = 1500;
|
const DEFAULT_DELAY_MS = 1500;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merge missing MTG + Pokémon sets and pick the newest releases first.
|
||||||
|
*/
|
||||||
|
export function buildCatalogSyncQueue(missingMtg, missingPokemon, maxSetsPerRun = DEFAULT_MAX_SETS_PER_RUN) {
|
||||||
|
const queue = [
|
||||||
|
...missingMtg.map((set) => ({
|
||||||
|
game: 'mtg',
|
||||||
|
setCode: set.code,
|
||||||
|
name: set.name,
|
||||||
|
releasedAt: set.releasedAt,
|
||||||
|
})),
|
||||||
|
...missingPokemon.map((set) => ({
|
||||||
|
game: 'pokemon',
|
||||||
|
setCode: set.id,
|
||||||
|
name: set.name,
|
||||||
|
releasedAt: set.releasedAt,
|
||||||
|
})),
|
||||||
|
];
|
||||||
|
|
||||||
|
queue.sort((a, b) => {
|
||||||
|
const dateA = a.releasedAt || '';
|
||||||
|
const dateB = b.releasedAt || '';
|
||||||
|
if (!dateA && !dateB) return 0;
|
||||||
|
if (!dateA) return 1;
|
||||||
|
if (!dateB) return -1;
|
||||||
|
return dateB.localeCompare(dateA);
|
||||||
|
});
|
||||||
|
|
||||||
|
return queue.slice(0, maxSetsPerRun);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Discover missing MTG + Pokémon sets and import up to maxSetsPerRun, paced for upstream APIs.
|
* Discover missing MTG + Pokémon sets and import up to maxSetsPerRun, paced for upstream APIs.
|
||||||
*/
|
*/
|
||||||
|
|
@ -19,10 +50,7 @@ export async function runCatalogSync(options = {}) {
|
||||||
discoverMissingPokemonSets(),
|
discoverMissingPokemonSets(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const queue = [
|
const queue = buildCatalogSyncQueue(missingMtg, missingPokemon, maxSetsPerRun);
|
||||||
...missingMtg.map((set) => ({ game: 'mtg', setCode: set.code, name: set.name })),
|
|
||||||
...missingPokemon.map((set) => ({ game: 'pokemon', setCode: set.id, name: set.name })),
|
|
||||||
].slice(0, maxSetsPerRun);
|
|
||||||
|
|
||||||
const summary = {
|
const summary = {
|
||||||
imported: 0,
|
imported: 0,
|
||||||
|
|
|
||||||
|
|
@ -8,12 +8,13 @@ import {
|
||||||
describe('filterMissingMtgSets', () => {
|
describe('filterMissingMtgSets', () => {
|
||||||
const now = new Date('2026-05-27T12:00:00.000Z');
|
const now = new Date('2026-05-27T12:00:00.000Z');
|
||||||
|
|
||||||
it('returns released paper sets missing from the catalog', () => {
|
it('returns released paper sets missing from the catalog, newest first', () => {
|
||||||
const known = new Set(['lea']);
|
const known = new Set(['lea']);
|
||||||
const missing = filterMissingMtgSets(
|
const missing = filterMissingMtgSets(
|
||||||
[
|
[
|
||||||
{ code: 'lea', name: 'Alpha', released_at: '1993-08-05', set_type: 'core', digital: false },
|
{ code: 'lea', name: 'Alpha', released_at: '1993-08-05', set_type: 'core', digital: false },
|
||||||
{ code: 'fin', name: 'Final Fantasy', released_at: '2026-05-01', set_type: 'expansion', digital: false },
|
{ code: 'fin', name: 'Final Fantasy', released_at: '2026-05-01', set_type: 'expansion', digital: false },
|
||||||
|
{ code: 'dsk', name: 'Duskmourn', released_at: '2024-09-27', set_type: 'expansion', digital: false },
|
||||||
{ code: 'tlea', name: 'Alpha Tokens', released_at: '1993-08-05', set_type: 'token', digital: false },
|
{ code: 'tlea', name: 'Alpha Tokens', released_at: '1993-08-05', set_type: 'token', digital: false },
|
||||||
{ code: 'mh3', name: 'Modern Horizons 3', released_at: '2027-01-01', set_type: 'expansion', digital: false },
|
{ code: 'mh3', name: 'Modern Horizons 3', released_at: '2027-01-01', set_type: 'expansion', digital: false },
|
||||||
],
|
],
|
||||||
|
|
@ -27,22 +28,33 @@ describe('filterMissingMtgSets', () => {
|
||||||
name: 'Final Fantasy',
|
name: 'Final Fantasy',
|
||||||
releasedAt: '2026-05-01',
|
releasedAt: '2026-05-01',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
code: 'dsk',
|
||||||
|
name: 'Duskmourn',
|
||||||
|
releasedAt: '2024-09-27',
|
||||||
|
},
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('filterMissingPokemonSets', () => {
|
describe('filterMissingPokemonSets', () => {
|
||||||
it('returns sets whose ids are not in the catalog', () => {
|
it('returns sets whose ids are not in the catalog, newest first', () => {
|
||||||
const known = new Set(['sv1']);
|
const known = new Set(['sv1']);
|
||||||
const missing = filterMissingPokemonSets(
|
const missing = filterMissingPokemonSets(
|
||||||
[
|
[
|
||||||
{ id: 'sv1', name: 'Scarlet & Violet', releaseDate: '2023-03-31' },
|
{ id: 'sv1', name: 'Scarlet & Violet', releaseDate: '2023-03-31' },
|
||||||
{ id: 'sv2', name: 'Paldea Evolved', releaseDate: '2023-06-09' },
|
{ id: 'sv2', name: 'Paldea Evolved', releaseDate: '2023-06-09' },
|
||||||
|
{ id: 'sv3', name: 'Obsidian Flames', releaseDate: '2023-08-11' },
|
||||||
],
|
],
|
||||||
known
|
known
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(missing).toEqual([
|
expect(missing).toEqual([
|
||||||
|
{
|
||||||
|
id: 'sv3',
|
||||||
|
name: 'Obsidian Flames',
|
||||||
|
releasedAt: '2023-08-11',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 'sv2',
|
id: 'sv2',
|
||||||
name: 'Paldea Evolved',
|
name: 'Paldea Evolved',
|
||||||
|
|
|
||||||
40
test/lib/card-import-sync-catalog.test.js
Normal file
40
test/lib/card-import-sync-catalog.test.js
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
|
import { buildCatalogSyncQueue } from '../../lib/card-import/sync-catalog.js';
|
||||||
|
|
||||||
|
describe('buildCatalogSyncQueue', () => {
|
||||||
|
it('merges MTG and Pokémon missing sets by release date, newest first', () => {
|
||||||
|
const queue = buildCatalogSyncQueue(
|
||||||
|
[
|
||||||
|
{ code: 'fin', name: 'Final Fantasy', releasedAt: '2026-05-01' },
|
||||||
|
{ code: 'dsk', name: 'Duskmourn', releasedAt: '2024-09-27' },
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{ id: 'sv10', name: 'Perfect Order', releasedAt: '2026-03-27' },
|
||||||
|
{ id: 'sv9', name: 'Journey Together', releasedAt: '2025-03-28' },
|
||||||
|
],
|
||||||
|
3
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(queue).toEqual([
|
||||||
|
{
|
||||||
|
game: 'mtg',
|
||||||
|
setCode: 'fin',
|
||||||
|
name: 'Final Fantasy',
|
||||||
|
releasedAt: '2026-05-01',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
game: 'pokemon',
|
||||||
|
setCode: 'sv10',
|
||||||
|
name: 'Perfect Order',
|
||||||
|
releasedAt: '2026-03-27',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
game: 'pokemon',
|
||||||
|
setCode: 'sv9',
|
||||||
|
name: 'Journey Together',
|
||||||
|
releasedAt: '2025-03-28',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
Reference in a new issue