drastically reduce time to create photo folders

This commit is contained in:
Ben Miller
2025-10-19 23:11:22 -06:00
parent a893cd326f
commit 237f57cf36
5 changed files with 99 additions and 44 deletions

View File

@ -330,44 +330,8 @@ export class Product {
}
CreatePhotoFolder() {
console.log("CreatePhotoFolder()");
const config = new Config();
if (!config.productPhotosFolderId) {
console.log(
"productPhotoFolderId not set in config. Skipping folder creation."
);
return;
}
const productInventorySheet =
SpreadsheetApp.getActiveSpreadsheet().getSheetByName("product_inventory");
const row = getRowByColumnValue("product_inventory", "sku", this.sku);
const photosCell = getCellRangeByColumnName(
productInventorySheet,
"photos",
row
);
const folderUrl = photosCell.getValue();
if (folderUrl && folderUrl.includes("drive.google.com")) {
console.log(`Photo folder already exists: ${folderUrl}`);
return;
}
const parentFolder = DriveApp.getFolderById(config.productPhotosFolderId);
const folderName = this.sku
// DriveApp.createFolder is idempotent if a folder with the same name exists in the parent
const newFolder = parentFolder.createFolder(folderName);
console.log(`Created or found photo folder: '${folderName}'`);
let url = newFolder.getUrl();
console.log(`Folder URL: ${url}`);
let linkValue = SpreadsheetApp.newRichTextValue()
.setText(folderName)
.setLinkUrl(url)
.build()
photosCell.setRichTextValue(linkValue)
console.log("Product.CreatePhotoFolder()");
createPhotoFolderForSku(new(Config), this.sku);
}
PublishToShopifyOnlineStore(shop: Shop) {
@ -414,3 +378,56 @@ export class Product {
// TODO: shopify_status
}
}
export function createPhotoFolderForSku(config: Config, sku: string) {
console.log(`createPhotoFolderForSku('${sku}')`)
if (!config.productPhotosFolderId) {
console.log(
"productPhotoFolderId not set in config. Skipping folder creation."
)
return
}
const productInventorySheet =
SpreadsheetApp.getActiveSpreadsheet().getSheetByName("product_inventory")
const row = getRowByColumnValue("product_inventory", "sku", sku)
if (!row) {
console.log(`SKU '${sku}' not found in sheet. Cannot create folder.`)
return
}
const photosCell = getCellRangeByColumnName(
productInventorySheet,
"photos",
row
)
const folderUrl = photosCell.getRichTextValue().getLinkUrl()
console.log(`Folder URL from cell: ${folderUrl}`)
if (folderUrl && folderUrl.includes("drive.google.com")) {
console.log(`Photo folder already exists: ${folderUrl}`)
return
} else {
console.log(`Creating photo folder for SKU: ${sku}`)
}
const parentFolder = DriveApp.getFolderById(config.productPhotosFolderId)
const folderName = sku
let newFolder: GoogleAppsScript.Drive.Folder
const existingFolders = parentFolder.getFoldersByName(folderName)
if (existingFolders.hasNext()) {
newFolder = existingFolders.next()
console.log(`Found existing photo folder: '${folderName}'`)
} else {
newFolder = parentFolder.createFolder(folderName)
console.log(`Created new photo folder: '${folderName}'`)
}
let url = newFolder.getUrl()
console.log(`Folder URL: ${url}`)
let linkValue = SpreadsheetApp.newRichTextValue()
.setText(folderName)
.setLinkUrl(url)
.build()
photosCell.setRichTextValue(linkValue)
}

View File

@ -1,5 +1,6 @@
import { Product } from "./Product"
import { getColumnValuesByName, toastAndLog } from "./sheetUtils"
import { createPhotoFolderForSku } from "./Product"
import { getColumnRichTextByName, getColumnValuesByName, toastAndLog } from "./sheetUtils"
import { Config } from "./config"
export function createMissingPhotoFolders() {
const ss = SpreadsheetApp.getActive()
@ -10,14 +11,22 @@ export function createMissingPhotoFolders() {
}
let skus = getColumnValuesByName(s, "sku")
let photos = getColumnRichTextByName(s, "photos")
let config = new Config()
for (let i = 0; i < skus.length; i++) {
// Process rows backward, as that is where the missing folders are most likely to occur
for (let i = skus.length - 1; i >= 0; i--) {
const sku = String(skus[i][0])
if (!sku) {
continue
}
const product = new Product(sku)
product.CreatePhotoFolder()
let folderUrl = photos[i][0].getLinkUrl()
if (folderUrl && folderUrl.includes("drive.google.com")) {
console.log(`Photo folder already exists for SKU: ${sku}`)
continue
}
createPhotoFolderForSku(config, sku)
}
toastAndLog("Finished creating missing photo folders.")
}

View File

@ -1,3 +1,5 @@
import { createPhotoFolderForSku } from "./Product"
import { Config } from "./config"
import {
getColumnByName,
getCellRangeByColumnName,
@ -24,8 +26,13 @@ export function newSkuHandler(e: GoogleAppsScript.Events.SheetsOnEdit) {
// Acquire a user lock to prevent multiple onEdit calls from clashing
const documentLock = LockService.getDocumentLock()
try {
const config = new (Config);
documentLock.waitLock(LOCK_TIMEOUT_MS)
newSku(row)
const sku = newSku(row)
console.log("new sku: " + sku)
createPhotoFolderForSku(config, String(sku))
} catch (error) {
console.log("Error in newSkuHandler: " + error.message)
} finally {
documentLock.releaseLock()
}
@ -70,4 +77,6 @@ export function newSku(row: number) {
let newId = maxId + 1
console.log("newId: " + newId)
idCell.setValue(newId)
return `${skuPrefixCellValue}-${newId.toString().padStart(4, "0")}`
}

View File

@ -22,6 +22,11 @@ export function onEditQueue(e) {
console.log("No SKU found for row " + row)
return
}
// Make sure SKU conforms to expected patterns
if (sku.match(`\\?`) || sku.match(`n$`)) {
console.log("SKU is a placeholder ('?' or 'n...'), skipping batching.")
return
}
// Acquire a user lock to prevent multiple onEdit calls from clashing
const documentLock = LockService.getDocumentLock()
try {
@ -85,6 +90,11 @@ export function processBatchedEdits() {
console.log(
`Processing SKU ${edit.sku}, Timestamp: ${new Date(edit.timestamp)}`
)
// Make sure SKU conforms to expected patterns
if (!edit.sku.match(/^\w+-\d{4}$/)) {
console.log(`SKU ${edit.sku} is not valid, skipping processing.`)
return
}
let p = new Product(edit.sku)
p.UpdateShopifyProduct(shop)
})

View File

@ -50,6 +50,16 @@ export function getColumnValuesByName(
}
}
export function getColumnRichTextByName(
sheet: GoogleAppsScript.Spreadsheet.Sheet,
columnName: string
) {
let column = getColumnRangeByName(sheet, columnName)
if (column != null) {
return column.getRichTextValues()
}
}
export function vlookupByColumns(
sheetName: string,
searchColumn: string,