From 1a5837e28af3401a25cd3fe360edecb3cb93fb52 Mon Sep 17 00:00:00 2001 From: Connor Ivy Date: Thu, 6 Apr 2023 12:00:47 -0500 Subject: [PATCH] sending table out as datatable --- src/plugins/dataTable.js | 193 +++++++++++++++++++++++++++++++++------ src/plugins/excel.js | 61 +++++++------ 2 files changed, 201 insertions(+), 53 deletions(-) diff --git a/src/plugins/dataTable.js b/src/plugins/dataTable.js index fd664cd..70efea0 100644 --- a/src/plugins/dataTable.js +++ b/src/plugins/dataTable.js @@ -1,4 +1,5 @@ -import { bakeTable, bakeArray, hideRowOrColumn } from './excel' +// eslint-disable-next-line no-unused-vars +import { bakeTable, bakeArray, hideRowOrColumn, getIndiciesFromRangeAddress, send } from './excel' export const tableName = 'SpeckleDataTable' export function checkIfReceivingDataTable(item) { @@ -9,7 +10,7 @@ export function checkIfReceivingDataTable(item) { } export function formatArrayDataForTable(item, arrayData) { - arrayData[0].push('put table metadata here eventually') + arrayData[0].push('SpeckleColumnMetadataRow') for (let i = 0; i < item.columnCount; i++) { arrayData[0].push(JSON.stringify(item.columnMetadata[i])) } @@ -34,40 +35,166 @@ export async function bakeDataTable(item, arrayData, context, sheet, rowStart, c } // hideRowOrColumn(sheet, colStart) await bakeArray(arrayData.splice(0, headerRowIndex), context) + + // set table applicationId in the top left cell + arrayData[0][0] = `{"SpeckleTableApplicationId":"${item.applicationId}"}` await bakeTable(arrayData, context, sheet, name, rowStart + headerRowIndex, colStart) - hideRowOrColumn(sheet, colStart, rowStart) + // hideRowOrColumn(sheet, colStart, rowStart) } -export function checkIfSendingDataTable(rangeAddress, values, sheet, context) { - console.log(rangeAddress, values, sheet, context) - // let namedRangesInSheet = getDataTables(sheet) - // if (!namedRangesInSheet) { - // return false - // } +export async function getDataTableContainingRange(range, values, sheet, context) { + let selectedTable = null + sheet.tables.load('count') + await context.sync() - // //check if sending range matches dataTable range - // for (let [key, value] of namedRangesInSheet) { - // let dataTableRange = value.getRangeOrNullObject() + for (let i = 0; i < sheet.tables.count; i++) { + let tableRange = sheet.tables.getItemAt(i).getRange() + let intersectionRange = tableRange.getIntersectionOrNullObject(range) + await context.sync() + if (intersectionRange.isNullObject) { + continue + } - // } -} - -export function getDataTables(sheet) { - let namedRanges = new Map() - for (let i = 0; i < 10; i++) { - let namedRange = sheet.names.getItemOrNullObject(`${tableName}${i}`) - if (namedRange) { - namedRanges.set(i, namedRange) + range.load('columnCount, rowCount') + intersectionRange.load('columnCount, rowCount') + await context.sync() + if ( + intersectionRange.columnCount >= range.columnCount && + intersectionRange.rowCount >= range.rowCount + ) { + selectedTable = sheet.tables.getItemAt(i) + break } } - return namedRanges + + const isDataTable = await isSpeckleDataTable(selectedTable, sheet, context) + if (selectedTable && isDataTable) { + return selectedTable + } + + return null +} + +async function isSpeckleDataTable(table, sheet, context) { + let tableRange = table.getRange() + tableRange.load('rowIndex, columnIndex') + await context.sync() + // let tableRangeAddress = tableRange.address + // let rangeIndicies = getIndiciesFromRangeAddress(tableRangeAddress) + // let firstCellRange = sheet.getRangeByIndexes(rangeIndicies[1], rangeIndicies[0], 1, 1) + let firstCellRange = sheet.getRangeByIndexes(tableRange.rowIndex, tableRange.columnIndex, 1, 1) + + firstCellRange.load('values') + await context.sync() + + if (firstCellRange.values[0][0].includes('SpeckleTableApplicationId')) { + return true + } + return false +} + +export async function BuildDataTableObject(sendingRange, values, table, sheet, context) { + let metaRowIndex = await GetColumnMetadataRowIndex(table, sheet, context) + let metaColIndex = await GetRowMetadataColumnIndex(table, context) + + let speckleTable = new DataTable() + sendingRange.load('rowIndex, columnIndex, rowCount, columnCount') + await context.sync() + + let metaRowRange = sheet.getRangeByIndexes( + metaRowIndex, + sendingRange.columnIndex, + 1, + sendingRange.columnCount + ) + let metaColumnRange = sheet.getRangeByIndexes( + sendingRange.rowIndex, + metaColIndex, + sendingRange.rowCount, + 1 + ) + + metaRowRange.load('values') + metaColumnRange.load('values') + await context.sync() + + for (let i = 0; i < sendingRange.columnCount; i++) { + speckleTable.defineColumn(JSON.parse(metaRowRange.values[0][i])) + } + + for (let i = 0; i < sendingRange.rowCount; i++) { + speckleTable.addRow(JSON.parse(metaColumnRange.values[i][0]), values[i]) + } + + return speckleTable +} + +export async function GetColumnMetadataRowIndex(table, sheet, context) { + let tableRange = table.getRange() + tableRange.load('columnIndex, rowCount, rowIndex') + await context.sync() + + // let tableRangeAddress = tableRange.address + // let rangeIndicies = getIndiciesFromRangeAddress(tableRangeAddress) + // let firstCellRange = sheet.getRangeByIndexes(rangeIndicies[1], rangeIndicies[0], 1, 1) + console.log('hey') + let bottomRowCell = sheet.getRangeByIndexes( + tableRange.rowIndex + tableRange.rowCount - 1, + tableRange.columnIndex, + 1, + 1 + ) + + const extendedRange = tableRange.getExtendedRange( + window.Excel.KeyboardDirection.up, + bottomRowCell + ) + extendedRange.load('columnCount, columnIndex, rowCount, rowIndex') + await context.sync() + + let possibleMetadataRowRange = sheet.getRangeByIndexes( + extendedRange.rowIndex, + extendedRange.columnIndex, + extendedRange.rowCount - tableRange.rowCount, + extendedRange.columnCount + ) + + var found = possibleMetadataRowRange.findOrNullObject('SpeckleColumnMetadataRow', { + completeMatch: false, // Match the whole cell value. + matchCase: true, // Don't match case. + searchDirection: window.Excel.SearchDirection.forward // Start search at the beginning of the range. + }) + found.load('rowIndex') + await context.sync() + if (found.isNullObject) { + found = possibleMetadataRowRange.findOrNullObject('speckle_type', { + completeMatch: false, // Match the whole cell value. + matchCase: true, // Match case. + searchDirection: window.Excel.SearchDirection.forward // Start search at the beginning of the range. + }) + found.load('rowIndex') + await context.sync() + } + + if (found.isNullObject) { + throw new Error('Could not find column metadata') + } + + return found.rowIndex +} + +export async function GetRowMetadataColumnIndex(table, context) { + let tableRange = table.getRange() + tableRange.load('columnIndex, rowCount, rowIndex') + await context.sync() + + return tableRange.columnIndex } class Base { id totalChildrenCount applicationId - speckle_type } export class DataTable extends Base { @@ -77,10 +204,11 @@ export class DataTable extends Base { get rowCount() { return this.rowMetadata.length } + // eslint-disable-next-line camelcase get speckle_type() { return 'Objects.Organization.DataTable' } - headerRowIndex = 1 + headerRowIndex columnMetadata = [] rowMetadata = [] data = [] @@ -90,13 +218,26 @@ export class DataTable extends Base { throw new Error( `object length of ${objects.length} does not match the column count, ${this.columnCount}` ) - let list = [metadata, ...objects] - this.data.push(list) + this.rowMetadata.push(metadata) + this.data.push(objects) } defineColumn(metadata) { this.columnMetadata.push(metadata) } + + toJSON() { + const jsonObj = Object.assign({}, this) + const proto = Object.getPrototypeOf(this) + for (const key of Object.getOwnPropertyNames(proto)) { + const desc = Object.getOwnPropertyDescriptor(proto, key) + const hasGetter = desc && typeof desc.get === 'function' + if (hasGetter) { + jsonObj[key] = this[key] + } + } + return jsonObj + } } // export function checkIfSendingDataTable(item, arrayData) { diff --git a/src/plugins/excel.js b/src/plugins/excel.js index 497ea21..1220cce 100644 --- a/src/plugins/excel.js +++ b/src/plugins/excel.js @@ -4,9 +4,10 @@ import store from '../store/index.js' import { MD5, enc } from 'crypto-js' import { checkIfReceivingDataTable, - checkIfSendingDataTable, + getDataTableContainingRange, bakeDataTable, - formatArrayDataForTable + formatArrayDataForTable, + BuildDataTableObject // getDataTables } from './dataTable.js' @@ -165,7 +166,9 @@ export function hideRowOrColumn(sheet, columnIndex = -1, rowIndex = -1) { sheet.getRange(`${columnLetter}:${columnLetter}`).columnHidden = true } if (rowIndex > -1) { - sheet.getRange(`${rowIndex + 1}:${rowIndex + 1}`).rowHidden = true + let rowRange = sheet.getRange(`${rowIndex + 1}:${rowIndex + 1}`) + rowRange.rowHidden = true + rowRange.format.wrapText = true } } @@ -520,6 +523,7 @@ export async function bake( } } +// eslint-disable-next-line no-unused-vars export async function send(savedStream, streamId, branchName, message) { try { await window.Excel.run(async (context) => { @@ -532,35 +536,38 @@ export async function send(savedStream, streamId, branchName, message) { await context.sync() let values = range.values - // check for specific conversion - checkIfSendingDataTable(rangeAddress, values, sheet, context) - let data = [] - if (savedStream.hasHeaders) { - for (let row = 1; row < values.length; row++) { - let object = {} - for (let col = 0; col < values[0].length; col++) { - let propName = values[0][col] - //if (propName !== 'id' && propName.endsWith('.id')) continue - let propValue = values[row][col] - object[propName] = propValue - } - // generate a hash if none is present - object.id = object.id || MD5(JSON.stringify(object)).toString(enc.Hex) - let unlattened = unflatten(object) - data.push(unlattened) - } + // check for specific conversion + let table = await getDataTableContainingRange(range, values, sheet, context) + if (table) { + data = await BuildDataTableObject(range, values, table, sheet, context) } else { - for (let row = 0; row < values.length; row++) { - let rowArray = [] - for (let col = 0; col < values[0].length; col++) { - rowArray.push(values[row][col]) + if (savedStream.hasHeaders) { + for (let row = 1; row < values.length; row++) { + let object = {} + for (let col = 0; col < values[0].length; col++) { + let propName = values[0][col] + //if (propName !== 'id' && propName.endsWith('.id')) continue + let propValue = values[row][col] + object[propName] = propValue + } + // generate a hash if none is present + object.id = object.id || MD5(JSON.stringify(object)).toString(enc.Hex) + let unlattened = unflatten(object) + data.push(unlattened) + } + } else { + for (let row = 0; row < values.length; row++) { + let rowArray = [] + for (let col = 0; col < values[0].length; col++) { + rowArray.push(values[row][col]) + } + data.push(rowArray) } - data.push(rowArray) } - } - data = { data: data, speckle_type: 'Base' } + data = { data: data, speckle_type: 'Base' } + } await store.dispatch('createCommit', { object: data,