From 271797e15ea8a166c0ddacb063ae8d12673dd9ff Mon Sep 17 00:00:00 2001 From: Cora Date: Wed, 1 May 2024 19:32:24 +0100 Subject: [PATCH 01/10] Document how to run the code --- readme.txt | 11 +++++++++++ run.bat | 1 + 2 files changed, 12 insertions(+) create mode 100644 readme.txt create mode 100644 run.bat diff --git a/readme.txt b/readme.txt new file mode 100644 index 0000000..fef9352 --- /dev/null +++ b/readme.txt @@ -0,0 +1,11 @@ +node csv-to-json.js + + + = A CSV file +The first column of the CSV is ignored. +Columns 2-5 should be: + - compendium ID, + - actor ID, + - actor image path, + - token image path, + - and finally an optional scale antecedent (consequent of 1)." diff --git a/run.bat b/run.bat new file mode 100644 index 0000000..82512e5 --- /dev/null +++ b/run.bat @@ -0,0 +1 @@ +node csv-to-json.js map.csv \ No newline at end of file From 61413f190853cddc810c352278d9097d3ed1edd7 Mon Sep 17 00:00:00 2001 From: Cora Date: Wed, 1 May 2024 22:43:19 +0100 Subject: [PATCH 02/10] adjust for npc gallery --- csv-to-json.js | 41 ++++++++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/csv-to-json.js b/csv-to-json.js index 8872ee0..9071349 100644 --- a/csv-to-json.js +++ b/csv-to-json.js @@ -10,7 +10,7 @@ const args = yargs(process.argv.slice(2)) () => { yargs .positional("filename", { - describe: "A CSV filename. The first column of the CSV is ignored. Columns 2-6 should be compendium ID, actor ID, actor image path, token image path, and an optional scale-ratio antecedent (consequent of 1), and an optional boolean indicating whether random images are to be enabled.", + describe: "A CSV filename. The data is used as follows: A = label, B = key, C = source. D = portrait, E = thumbnail, F = token, G = scale, H = subject. Tag groups are I through ??.", }); } ) @@ -22,24 +22,35 @@ const args = yargs(process.argv.slice(2)) .version(false) .parseSync(); +let jsonData = []; const csvData = fs.readFileSync(args.filename, { encoding: "utf-8" }); -const jsonData = parser +const data = parser .parse(csvData) .slice(1) .map((row) => ({ - pack: row[1], - id: row[2], - actor: row[3], - token: - row[5].trim() || row[6] - ? { img: row[4], scale: Number(row[5]) || undefined, randomImg: !!row[6] || undefined } - : row[4], - randomImg: !!row[6], + "label": row[0], + "key": row[1], + "source": row[2], + "art": { + "portrait" : row[3], + "thumb" : row[4], + "token" : row[5], + "scale" : Number(row[6]) || undefined, + "subject" : row[7], + }, + "tagGroups": { + "family" : { "key": "family", "tags": row[8] ? row[8].toLowerCase().split(",") : undefined }, + "ancestry" : { "key": "ancestry", "tags": row[9] ? row[9].toLowerCase().split(",") : undefined }, + "equipment" : { "key": "equipment", "tags": row[10] ? row[10].toLowerCase().split(",") : undefined }, + "stance" : { "key": "stance", "tags": row[11] ? row[11].toLowerCase().split(",") : undefined }, + "armor" : { "key": "armor", "tags": row[12] ? row[12].toLowerCase().split(",") : undefined }, + }, })) - .reduce((accum, row) => { - accum[row.pack] ??= {}; - accum[row.pack][row.id] = { actor: row.actor, token: row.token }; - return accum; - }, {}); + .map((element) => { + for (const group in element.tagGroups) { + if ( element.tagGroups[group].tags === undefined) { delete element.tagGroups[group] } + } + jsonData.push(element) + }) fs.writeFileSync(args.filename.replace(/\.csv$/, ".json"), JSON.stringify(jsonData, null, 2), { encoding: "utf-8" }); From 7e7009f9a02c6bc27ffa27d0ec5ae26c526c5e65 Mon Sep 17 00:00:00 2001 From: Cora Date: Wed, 3 Jul 2024 21:38:03 +0100 Subject: [PATCH 03/10] Update with new format --- csv-to-json.js | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/csv-to-json.js b/csv-to-json.js index 9071349..f8ed874 100644 --- a/csv-to-json.js +++ b/csv-to-json.js @@ -10,7 +10,22 @@ const args = yargs(process.argv.slice(2)) () => { yargs .positional("filename", { - describe: "A CSV filename. The data is used as follows: A = label, B = key, C = source. D = portrait, E = thumbnail, F = token, G = scale, H = subject. Tag groups are I through ??.", + describe: `Input is a CSV with the following data in each column: + A = label (string) + B = key (string) + C = source book (string) + D = portrait (filepath) + E = thumbnail (filepath) + F = token (filepath) + G = subject (filepath) + H = scale (number) + I = ancestry tags (csv string => array) + J = armor tags (csv string => array) + K = equipment tags (csv string => array) + L = ability tags (csv string => array) + M = feature tags (csv string => array) + N = unique? (string) + ` }); } ) @@ -31,19 +46,20 @@ const data = parser "label": row[0], "key": row[1], "source": row[2], + "unique": row[13] || undefined, "art": { "portrait" : row[3], "thumb" : row[4], "token" : row[5], - "scale" : Number(row[6]) || undefined, - "subject" : row[7], + "subject" : row[6], + "scale" : Number(row[7]) || undefined, }, - "tagGroups": { - "family" : { "key": "family", "tags": row[8] ? row[8].toLowerCase().split(",") : undefined }, - "ancestry" : { "key": "ancestry", "tags": row[9] ? row[9].toLowerCase().split(",") : undefined }, - "equipment" : { "key": "equipment", "tags": row[10] ? row[10].toLowerCase().split(",") : undefined }, - "stance" : { "key": "stance", "tags": row[11] ? row[11].toLowerCase().split(",") : undefined }, - "armor" : { "key": "armor", "tags": row[12] ? row[12].toLowerCase().split(",") : undefined }, + "tags": { + "ancestry" : row[8] ? row[8].toLowerCase().split(",") : undefined, + "armor" : row[9] ? row[9].toLowerCase().split(",") : undefined, + "equipment" : row[10] ? row[10].toLowerCase().split(",") : undefined, + "abilities" : row[11] ? row[11].toLowerCase().split(",") : undefined, + "features" : row[12] ? row[12].toLowerCase().split(",") : undefined, }, })) .map((element) => { From 73698c65f985ed36650c316e0ed62d5764e674b4 Mon Sep 17 00:00:00 2001 From: Cora Date: Fri, 2 Aug 2024 23:54:51 +0100 Subject: [PATCH 04/10] update --- csv-to-json.js | 51 ++++++++++++++++++++++++-------------------------- 1 file changed, 24 insertions(+), 27 deletions(-) diff --git a/csv-to-json.js b/csv-to-json.js index f8ed874..1def7c9 100644 --- a/csv-to-json.js +++ b/csv-to-json.js @@ -11,20 +11,17 @@ const args = yargs(process.argv.slice(2)) yargs .positional("filename", { describe: `Input is a CSV with the following data in each column: - A = label (string) - B = key (string) - C = source book (string) - D = portrait (filepath) - E = thumbnail (filepath) - F = token (filepath) - G = subject (filepath) - H = scale (number) - I = ancestry tags (csv string => array) - J = armor tags (csv string => array) - K = equipment tags (csv string => array) - L = ability tags (csv string => array) - M = feature tags (csv string => array) - N = unique? (string) + - ignored + - key (string) + - label (string) + - source book (string) + - scale (num) + - ancestry tags (csv string => array) + - armor tags (csv string => array) + - equipment tags (csv string => array) + - feature tags (csv string => array) + - family tags (csv string => array) + - special tags (csv string => array) ` }); } @@ -43,23 +40,23 @@ const data = parser .parse(csvData) .slice(1) .map((row) => ({ - "label": row[0], + "label": row[2], "key": row[1], - "source": row[2], - "unique": row[13] || undefined, + "source": row[3], "art": { - "portrait" : row[3], - "thumb" : row[4], - "token" : row[5], - "subject" : row[6], - "scale" : Number(row[7]) || undefined, + "portrait" : "modules/pf2e-tokens-gallery/assets/portraits/" + row[1] + ".webp", + "thumb" : "modules/pf2e-tokens-gallery/assets/thumbnails/" + row[1] + ".webp", + "token" : "modules/pf2e-tokens-gallery/assets/tokens/" + row[1] + ".webp", + "subject" : "modules/pf2e-tokens-gallery/assets/subjects/" + row[1] + ".webp", + "scale" : Number(row[4]) || undefined, }, "tags": { - "ancestry" : row[8] ? row[8].toLowerCase().split(",") : undefined, - "armor" : row[9] ? row[9].toLowerCase().split(",") : undefined, - "equipment" : row[10] ? row[10].toLowerCase().split(",") : undefined, - "abilities" : row[11] ? row[11].toLowerCase().split(",") : undefined, - "features" : row[12] ? row[12].toLowerCase().split(",") : undefined, + "ancestry" : row[5] ? row[5].toLowerCase().split(",") : undefined, + "armor" : row[6] ? row[6].toLowerCase().split(",") : undefined, + "equipment" : row[7] ? row[7].toLowerCase().split(",") : undefined, + "features" : row[8] ? row[8].toLowerCase().split(",") : undefined, + "family" : row[9] ? row[9].toLowerCase().split(",") : undefined, + "special" : row[10] ? row[9].toLowerCase().split(",") : undefined, }, })) .map((element) => { From 9876ed13ae67a6948ce03a354ad32993812ce7c1 Mon Sep 17 00:00:00 2001 From: Cora Date: Tue, 20 Aug 2024 18:43:34 +0100 Subject: [PATCH 05/10] pdate module ID --- csv-to-json.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/csv-to-json.js b/csv-to-json.js index 1def7c9..ddfe91e 100644 --- a/csv-to-json.js +++ b/csv-to-json.js @@ -17,7 +17,7 @@ const args = yargs(process.argv.slice(2)) - source book (string) - scale (num) - ancestry tags (csv string => array) - - armor tags (csv string => array) + - category tags (csv string => array) - equipment tags (csv string => array) - feature tags (csv string => array) - family tags (csv string => array) @@ -44,19 +44,19 @@ const data = parser "key": row[1], "source": row[3], "art": { - "portrait" : "modules/pf2e-tokens-gallery/assets/portraits/" + row[1] + ".webp", - "thumb" : "modules/pf2e-tokens-gallery/assets/thumbnails/" + row[1] + ".webp", - "token" : "modules/pf2e-tokens-gallery/assets/tokens/" + row[1] + ".webp", - "subject" : "modules/pf2e-tokens-gallery/assets/subjects/" + row[1] + ".webp", + "portrait" : "modules/pf2e-tokens-characters/assets/portraits/" + row[1] + ".webp", + "thumb" : "modules/pf2e-tokens-characters/assets/thumbnails/" + row[1] + ".webp", + "token" : "modules/pf2e-tokens-characters/assets/tokens/" + row[1] + ".webp", + "subject" : "modules/pf2e-tokens-characters/assets/subjects/" + row[1] + ".webp", "scale" : Number(row[4]) || undefined, }, "tags": { "ancestry" : row[5] ? row[5].toLowerCase().split(",") : undefined, - "armor" : row[6] ? row[6].toLowerCase().split(",") : undefined, + "category" : row[6] ? row[6].toLowerCase().split(",") : undefined, "equipment" : row[7] ? row[7].toLowerCase().split(",") : undefined, "features" : row[8] ? row[8].toLowerCase().split(",") : undefined, "family" : row[9] ? row[9].toLowerCase().split(",") : undefined, - "special" : row[10] ? row[9].toLowerCase().split(",") : undefined, + "special" : row[10] ? row[10].toLowerCase().split(",") : undefined, }, })) .map((element) => { From 7d62aaf77198fed9afbea6eacd059c994aa80c75 Mon Sep 17 00:00:00 2001 From: Cora Date: Thu, 22 Aug 2024 20:42:39 +0100 Subject: [PATCH 06/10] Support other modules --- csv-to-json.js | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/csv-to-json.js b/csv-to-json.js index ddfe91e..89a22e4 100644 --- a/csv-to-json.js +++ b/csv-to-json.js @@ -16,6 +16,10 @@ const args = yargs(process.argv.slice(2)) - label (string) - source book (string) - scale (num) + - Portrait path + - Thumbnail path + - Token path + - Subject path - ancestry tags (csv string => array) - category tags (csv string => array) - equipment tags (csv string => array) @@ -44,19 +48,19 @@ const data = parser "key": row[1], "source": row[3], "art": { - "portrait" : "modules/pf2e-tokens-characters/assets/portraits/" + row[1] + ".webp", - "thumb" : "modules/pf2e-tokens-characters/assets/thumbnails/" + row[1] + ".webp", - "token" : "modules/pf2e-tokens-characters/assets/tokens/" + row[1] + ".webp", - "subject" : "modules/pf2e-tokens-characters/assets/subjects/" + row[1] + ".webp", + "portrait" : row[5], + "thumb" : row[6], + "token" : row[7], + "subject" : row[8], "scale" : Number(row[4]) || undefined, }, "tags": { - "ancestry" : row[5] ? row[5].toLowerCase().split(",") : undefined, - "category" : row[6] ? row[6].toLowerCase().split(",") : undefined, - "equipment" : row[7] ? row[7].toLowerCase().split(",") : undefined, - "features" : row[8] ? row[8].toLowerCase().split(",") : undefined, - "family" : row[9] ? row[9].toLowerCase().split(",") : undefined, - "special" : row[10] ? row[10].toLowerCase().split(",") : undefined, + "ancestry" : row[9] ? row[9].toLowerCase().split(",") : undefined, + "category" : row[10] ? row[10].toLowerCase().split(",") : undefined, + "equipment" : row[11] ? row[11].toLowerCase().split(",") : undefined, + "features" : row[12] ? row[12].toLowerCase().split(",") : undefined, + "family" : row[13] ? row[13].toLowerCase().split(",") : undefined, + "special" : row[14] ? row[14].toLowerCase().split(",") : undefined, }, })) .map((element) => { From 016210a68019408fe4cc8104575e7e869e0351b7 Mon Sep 17 00:00:00 2001 From: Cora Date: Mon, 7 Oct 2024 14:07:23 +0100 Subject: [PATCH 07/10] Update for new spreadsheet layout --- csv-to-json.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/csv-to-json.js b/csv-to-json.js index 89a22e4..8683bd4 100644 --- a/csv-to-json.js +++ b/csv-to-json.js @@ -13,13 +13,13 @@ const args = yargs(process.argv.slice(2)) describe: `Input is a CSV with the following data in each column: - ignored - key (string) - - label (string) - - source book (string) - - scale (num) - Portrait path - Thumbnail path - Token path - Subject path + - label (string) + - source book (string) + - scale (num) - ancestry tags (csv string => array) - category tags (csv string => array) - equipment tags (csv string => array) @@ -44,15 +44,15 @@ const data = parser .parse(csvData) .slice(1) .map((row) => ({ - "label": row[2], + "label": row[6], "key": row[1], - "source": row[3], + "source": row[7], "art": { - "portrait" : row[5], - "thumb" : row[6], - "token" : row[7], - "subject" : row[8], - "scale" : Number(row[4]) || undefined, + "portrait" : row[2], + "thumb" : row[3], + "token" : row[4], + "subject" : row[5], + "scale" : Number(row[8]) || 1, }, "tags": { "ancestry" : row[9] ? row[9].toLowerCase().split(",") : undefined, From 14e7e76f8a4223ba3efae6c716c11d2db05dcbd8 Mon Sep 17 00:00:00 2001 From: Cora Date: Mon, 14 Oct 2024 20:19:33 +0100 Subject: [PATCH 08/10] Move code to two separate files in one branch --- csv-to-json.js => csv-to-datasheet.js | 0 csv-to-map.js | 84 +++++++++++++++++++++++++++ 2 files changed, 84 insertions(+) rename csv-to-json.js => csv-to-datasheet.js (100%) create mode 100644 csv-to-map.js diff --git a/csv-to-json.js b/csv-to-datasheet.js similarity index 100% rename from csv-to-json.js rename to csv-to-datasheet.js diff --git a/csv-to-map.js b/csv-to-map.js new file mode 100644 index 0000000..9ed37df --- /dev/null +++ b/csv-to-map.js @@ -0,0 +1,84 @@ +const fs = require("fs"); +const parser = require("csv-parse/sync"); +const process = require("process"); +const yargs = require("yargs"); + +function createTokenObject(token) { + + // Exception catcher for older token packs that fixes the mapping for oversized smalls if it can find them + // Ideally we should try to save the two scales separately rather than "inferring" the new value like this + switch( token.ring.subject.scale ) { + case 1.2: + token.ring.subject.scale = 1.5 + break + case 1.6: + token.ring.subject.scale = 2 + break + default: + break + } + + return token +} + +const args = yargs(process.argv.slice(2)) + .command( + "$0 ", + "Convert a module art CSV file to JSON", + () => { + yargs + .positional("filename", { + describe: `A CSV file. The first row is ignored since it's assumed to contain column headers. + Subsequent rows should each correspond to a single creature and contain the following data in each column: + A (0) - Label/name nb: this field is ignored + B (1) - Compendium ID nb: includes the system prefix, eg 'pf2e.pathfinder-bestiary' + C (2) - Actor ID nb: short form, eg 'Z7xWkQKCHGyd02B1' + D (3) - Portrait image path + E (4) - Token image path + F (5) - Subject image path + G (6) - Optional scale value empty = default (1) + H (7) - Optional boolean indicating whether random images are to be enabled empty = undefined + I (8) - Optional boolean indicating whether to enable dynamic token ring empty = undefined, but it's recommended this should be enabled if there is subject artwork + ` + }); + } + ) + .usage("Usage: node $0 ") + .check((args) => typeof args.filename === "string" && + fs.existsSync(args.filename) && + fs.statSync(args.filename).isFile()) + .help(false) + .version(false) + .parseSync(); + +const csvData = fs.readFileSync(args.filename, { encoding: "utf-8" }); +const jsonData = parser + .parse(csvData) + .slice(1) + .map((row) => ({ + pack: row[1], + id: row[2], + actor: row[3], + token: { + randomImg: !!row[7] || undefined, + texture: { + src: row[4], + scaleX: Number(row[6]) || undefined, + scaleY: Number(row[6]) || undefined, + }, + ring: { + enabled: !!row[8] || undefined, + subject: { + texture: row[5] || undefined, + scale: Number(row[6]) || undefined + } + } + }, + })) + .reduce((accum, row) => { + accum[row.pack] ??= {}; + accum[row.pack][row.id] = { actor: row.actor, token: createTokenObject(row.token) }; + return accum; + }, {}); + +fs.writeFileSync(args.filename.replace(/\.csv$/, ".json"), JSON.stringify(jsonData, null, 2), { encoding: "utf-8" }); \ No newline at end of file From 237fa5a2f659fa018a8f82f8168bdb5a0a2939d0 Mon Sep 17 00:00:00 2001 From: Cora Date: Mon, 14 Oct 2024 20:19:38 +0100 Subject: [PATCH 09/10] Update readme --- readme.txt | 13 +++++-------- run.bat | 1 - 2 files changed, 5 insertions(+), 9 deletions(-) delete mode 100644 run.bat diff --git a/readme.txt b/readme.txt index fef9352..04fc706 100644 --- a/readme.txt +++ b/readme.txt @@ -1,11 +1,8 @@ -node csv-to-json.js +Script supports two functions, which generate a mapping file or a datasheet respectively. +They are run with the following commands: +node csv-to-map.js +node csv-to-datasheet.js = A CSV file -The first column of the CSV is ignored. -Columns 2-5 should be: - - compendium ID, - - actor ID, - - actor image path, - - token image path, - - and finally an optional scale antecedent (consequent of 1)." +The expected CSV formats are detailed in the respective files \ No newline at end of file diff --git a/run.bat b/run.bat deleted file mode 100644 index 82512e5..0000000 --- a/run.bat +++ /dev/null @@ -1 +0,0 @@ -node csv-to-json.js map.csv \ No newline at end of file From d6ad4467e8ac18887ec74d553b9e9f1073855698 Mon Sep 17 00:00:00 2001 From: Cora Date: Fri, 23 May 2025 01:38:01 +0100 Subject: [PATCH 10/10] fix bad boolean making wildcards always on --- csv-to-map.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/csv-to-map.js b/csv-to-map.js index 9ed37df..bf646f3 100644 --- a/csv-to-map.js +++ b/csv-to-map.js @@ -36,9 +36,9 @@ const args = yargs(process.argv.slice(2)) D (3) - Portrait image path E (4) - Token image path F (5) - Subject image path - G (6) - Optional scale value empty = default (1) - H (7) - Optional boolean indicating whether random images are to be enabled empty = undefined - I (8) - Optional boolean indicating whether to enable dynamic token ring empty = undefined, but it's recommended this should be enabled if there is subject artwork + G (6) - Optional scale value empty => default (1) + H (7) - Optional boolean indicating whether random images are to be enabled empty/undefined => false + I (8) - Optional boolean indicating whether to enable dynamic token ring empty/undefined => true (it's recommended this should be enabled if there is subject artwork) ` }); } @@ -60,14 +60,14 @@ const jsonData = parser id: row[2], actor: row[3], token: { - randomImg: !!row[7] || undefined, + randomImg: (row[7]==="TRUE") ? true : false, texture: { src: row[4], scaleX: Number(row[6]) || undefined, scaleY: Number(row[6]) || undefined, }, ring: { - enabled: !!row[8] || undefined, + enabled: (row[8]==="FALSE") ? false : true, subject: { texture: row[5] || undefined, scale: Number(row[6]) || undefined