From ebf52612f97bef7ab8f1c670c48c67bc5fc43c26 Mon Sep 17 00:00:00 2001 From: Jake Wymer Date: Mon, 11 Sep 2023 15:54:23 -0500 Subject: [PATCH 1/3] properly map cohort properties --- .clasp.json | 2 +- .gitignore | 3 ++- Code.js | 2 +- components/dataExport.js | 7 ++++--- env-sample.js | 17 +++++++++++++++++ tests/all.test.js | 11 ++++++++++- utilities/misc.js | 27 ++++++++++++++++++++++++++- utilities/sheet.js | 6 +++--- 8 files changed, 64 insertions(+), 11 deletions(-) diff --git a/.clasp.json b/.clasp.json index 4803fc7..82aac8b 100644 --- a/.clasp.json +++ b/.clasp.json @@ -1,5 +1,5 @@ { "scriptId": "1-e_9mTJFnWHvceBDod0OEkYP7B7fgfcxTYqggyoZGLyWOCfWvFge3hZO", - "rootDir": "/Users/ak/code/sheets-mixpanel", + "rootDir": "/Users/jakewymer/sheets", "projectId": "mixpanel-gtm-training" } \ No newline at end of file diff --git a/.gitignore b/.gitignore index 8d67d46..23cfbb3 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,5 @@ creds.json /tmp/* !tmp/.gitkeep .env -env.js \ No newline at end of file +env.js +env.gs \ No newline at end of file diff --git a/Code.js b/Code.js index 11932b5..e0337ca 100644 --- a/Code.js +++ b/Code.js @@ -11,7 +11,7 @@ ----------------------------- */ -const APP_VERSION = "1.15"; +const APP_VERSION = "1.16"; /** * some important things to know about google apps script diff --git a/components/dataExport.js b/components/dataExport.js index 5700384..3b8be21 100644 --- a/components/dataExport.js +++ b/components/dataExport.js @@ -8,7 +8,7 @@ DATA OUT OF MP * export data; if not called with a config, uses last known * * @param {MpSheetConfig} [config] - * @returns {[string, ReportMeta | CohortMeta | DashMeta]} string + metadata `[csv, {}]` + * @returns {[string | string[][], ReportMeta | CohortMeta | DashMeta]} string + metadata `[csv, {}]` */ function exportData(config) { //use last known config if unset @@ -33,7 +33,7 @@ function exportData(config) { try { const meta = getCohortMeta(config); const profiles = getCohort(config); - const csv = JSONtoCSV(profiles); + const csv = profilesToCsvArray(profiles); return [csv, meta]; } catch (e) { throw e; @@ -65,6 +65,7 @@ function exportData(config) { throw `${type} is unsupported`; } + /** * @param {MpSheetConfig} config * @returns {DashMeta} @@ -394,5 +395,5 @@ if (typeof module !== "undefined") { module.exports = { exportData }; const { getConfig } = require("../utilities/storage.js"); const { validateCreds } = require("../utilities/validate.js"); - const { JSONtoCSV } = require("../utilities/misc.js"); + const { JSONtoCSV, profilesToCsvArray } = require("../utilities/misc.js"); } diff --git a/env-sample.js b/env-sample.js index a5e3cc6..314568d 100644 --- a/env-sample.js +++ b/env-sample.js @@ -119,6 +119,23 @@ const TEST_CONFIG_GROUPS_DATA = [ } ]; +/** @type {SheetMpConfigAlways & EventMappings} */ +const TEST_CONFIG_AD_SPENT = { + config_type: "sheet-to-mixpanel", + record_type: "event", + event_name_col: "hardcode", + hardcode_event_name: "ad spent", + distinct_id_col: "", + time_col: "timestamp", + insert_id_col: "campaign id", + project_id: PROJECT_ID, + token: TOKEN, + region: "US", + auth_type: "service_account", + service_acct: SERVICE_ACCOUNT, + service_secret: SERVICE_SECRET +}; + /** @type {SheetMpConfigAlways & TableMappings} */ const TEST_CONFIG_TABLES = { config_type: "sheet-to-mixpanel", diff --git a/tests/all.test.js b/tests/all.test.js index fc39cec..8d8b34d 100755 --- a/tests/all.test.js +++ b/tests/all.test.js @@ -52,6 +52,15 @@ function runTests() { return Misc.JSONtoCSV([{ foo: "bar", baz: "qux" }]) === expected; }); + test.assert("turns profiles into string[][]", () => { + const expected = [ + [`foo`, `baz`, `third`], + [`bar`, `qux`, ``], + [`bar`, ``, `exists`] + ]; + return JSON.stringify(Misc.profilesToCsvArray([{ foo: "bar", baz: "qux" }, {foo: "bar", third: "exists"}])) === JSON.stringify(expected); + }); + test.assert("forms pretty dates?", () => { const expected = `3/3/1901 @ 4:20am`; return Misc.formatDate(new Date(1, 2, 3, 4, 20)) === expected; @@ -218,7 +227,7 @@ function runTests() { validateCreds(BAD_API_SECRET); }); - test.catchErr("THROWS: bad api project?", `Mismatch between project secret's project ID and URL project ID`, () => { + test.catchErr("THROWS: bad api project?", `Credentials in request did not match project_id URL parameter`, () => { validateCreds(BAD_PROJECT_API_SECRET); }); diff --git a/utilities/misc.js b/utilities/misc.js index d8e22c4..575298a 100644 --- a/utilities/misc.js +++ b/utilities/misc.js @@ -20,6 +20,31 @@ function JSONtoCSV(arr) { .join("\n"); } +function profilesToCsvArray(profiles) { + const headers = new Set(); + + // Need to check all profile keys in case some are missing that prop + profiles.forEach(profile => { + Object.keys(profile).forEach(key => { + headers.add(key); + }); + }); + + const headersArr = Array.from(headers); + + const properties = []; + + profiles.forEach(profile => { + const profileProps = headersArr.map(header => { + const prop = profile[header] !== undefined ? `${profile[header]}` : ""; + return prop; + }); + properties.push(profileProps); + }) + + return [headersArr, ...properties]; +} + function sliceIntoChunks(arr, chunkSize = 2000) { const res = []; for (let i = 0; i < arr.length; i += chunkSize) { @@ -105,5 +130,5 @@ function isObject(object) { } if (typeof module !== "undefined") { - module.exports = { comma, JSONtoCSV, sliceIntoChunks, formatDate, serial, isDeepEqual, isObject, clone }; + module.exports = { comma, JSONtoCSV, sliceIntoChunks, formatDate, serial, isDeepEqual, isObject, clone, profilesToCsvArray }; } diff --git a/utilities/sheet.js b/utilities/sheet.js index 59a9ec4..1cd446b 100644 --- a/utilities/sheet.js +++ b/utilities/sheet.js @@ -108,12 +108,12 @@ function getEmptyRow(sheet) { /** * overwrites the contents of a spreadsheet with new data * - * @param {string} csvString + * @param {string | string[][]} csv * @param {GoogleAppsScript.Spreadsheet.Sheet} sheet * @returns {SheetInfo} */ -function overwriteSheet(csvString, sheet) { - var csvData = Utilities.parseCsv(csvString); +function overwriteSheet(csv, sheet) { + const csvData = typeof csv === `string` ? Utilities.parseCsv(csv) : csv; sheet.getRange(1, 1, csvData.length, csvData[0].length).setValues(csvData); return getSheetInfo(sheet); } From caf5ab112dc306d61d3e346117ada261bf998884 Mon Sep 17 00:00:00 2001 From: Jake Wymer Date: Wed, 13 Sep 2023 12:29:00 -0500 Subject: [PATCH 2/3] review comments and readme update --- .clasp.json | 2 +- README.md | 1 + tests/all.test.js | 4 ++-- utilities/misc.js | 6 ++++++ 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/.clasp.json b/.clasp.json index 82aac8b..4803fc7 100644 --- a/.clasp.json +++ b/.clasp.json @@ -1,5 +1,5 @@ { "scriptId": "1-e_9mTJFnWHvceBDod0OEkYP7B7fgfcxTYqggyoZGLyWOCfWvFge3hZO", - "rootDir": "/Users/jakewymer/sheets", + "rootDir": "/Users/ak/code/sheets-mixpanel", "projectId": "mixpanel-gtm-training" } \ No newline at end of file diff --git a/README.md b/README.md index 656f3ed..bf3a5b2 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ connect your google sheet with mixpanel! no coding required! [Install Now!](https://workspace.google.com/marketplace/app/sheets_%E2%87%94_mixpanel/1078767167468) +See sample data [here](https://docs.google.com/spreadsheets/d/1-9ifFSMDk4DUrNHyUgl0L-aIxxVjno5dPkJReu0E-nk/edit?usp=sharing)
diff --git a/tests/all.test.js b/tests/all.test.js index 8d8b34d..d517058 100755 --- a/tests/all.test.js +++ b/tests/all.test.js @@ -55,10 +55,10 @@ function runTests() { test.assert("turns profiles into string[][]", () => { const expected = [ [`foo`, `baz`, `third`], - [`bar`, `qux`, ``], + [`bar`, `qu,x`, ``], [`bar`, ``, `exists`] ]; - return JSON.stringify(Misc.profilesToCsvArray([{ foo: "bar", baz: "qux" }, {foo: "bar", third: "exists"}])) === JSON.stringify(expected); + return JSON.stringify(Misc.profilesToCsvArray([{ foo: "bar", baz: "qu,x" }, {foo: "bar", third: "exists"}])) === JSON.stringify(expected); }); test.assert("forms pretty dates?", () => { diff --git a/utilities/misc.js b/utilities/misc.js index 575298a..4d8e7df 100644 --- a/utilities/misc.js +++ b/utilities/misc.js @@ -20,6 +20,12 @@ function JSONtoCSV(arr) { .join("\n"); } +/** + * export data; if not called with a config, uses last known + * + * @param {ProfileData[]} profiles + * @returns {string[][]} + */ function profilesToCsvArray(profiles) { const headers = new Set(); From ec97b82ab6b85b3970dc2df278130dd6ffafaa95 Mon Sep 17 00:00:00 2001 From: Jake Wymer Date: Wed, 13 Sep 2023 12:47:21 -0500 Subject: [PATCH 3/3] fix function comment --- utilities/misc.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/utilities/misc.js b/utilities/misc.js index 4d8e7df..73f76be 100644 --- a/utilities/misc.js +++ b/utilities/misc.js @@ -21,8 +21,6 @@ function JSONtoCSV(arr) { } /** - * export data; if not called with a config, uses last known - * * @param {ProfileData[]} profiles * @returns {string[][]} */