import { forceArray } from './utils'; import * as _ from 'lodash'; import { GoogleSpreadsheet } from './GoogleSpreadsheet'; import { Links, WorksheetData } from '../types'; import { SpreadsheetRow } from './SpreadsheetRow'; import { SpreadsheetCell } from './SpreadsheetCell'; /** * TODO: Describe file contents */ interface WorksheetInfo { title: string; rowCount: number; colCount: number; } export class SpreadsheetWorksheet { private spreadsheet: GoogleSpreadsheet; public url: string; public readonly id: number; public title: string; public rowCount: number; public colCount: number; private readonly _links: Links; constructor(spreadsheet: GoogleSpreadsheet, data: WorksheetData) { let links; this.spreadsheet = spreadsheet; this.url = data.id; this.id = data.id.substring(data.id.lastIndexOf('/') + 1); this.title = data.title; this.rowCount = parseInt(data['gs:rowCount']); this.colCount = parseInt(data['gs:colCount']); this._links = []; links = forceArray(data.link); links.forEach(link => { this._links[link['$']['rel']] = link['$']['href']; }); this._links['cells'] = this._links[ 'http://schemas.google.com/spreadsheets/2006#cellsfeed' ]; this._links['bulkcells'] = this._links['cells'] + '/batch'; } private _setInfo = async (opts: Partial = {}) => { const xml = `${opts.title || this.title}${opts.rowCount || this.rowCount}${opts.colCount || this.colCount}`; const { data: response } = await this.spreadsheet.makeFeedRequest( this['_links']['edit'], 'PUT', xml, ); this.title = response.title; this.rowCount = parseInt(response['gs:rowCount']); this.colCount = parseInt(response['gs:colCount']); }; public resize = this._setInfo; public setTitle = async (title: string) => { return this._setInfo({ title: title }); }; // just a convenience method to clear the whole sheet // resizes to 1 cell, clears the cell, and puts it back public clear = async () => { const cols = this.colCount; const rows = this.colCount; await this.resize({ rowCount: 1, colCount: 1 }); const cells = await this.getCells(); await cells[0].setValue(''); return this.resize({ rowCount: rows, colCount: cols }); }; public getRows = async (opts: any = {}) => { return this.spreadsheet.getRows(this.id, opts); }; public getCells = async (opts: any = {}): Promise => { return this.spreadsheet.getCells(this.id, opts); }; public addRow = async (data: any): Promise => { return this.spreadsheet.addRow(this.id, data); }; public bulkUpdateCells = async (cells: any[]) => { const entries = cells.map(cell => { cell._needsSave = false; return ` ${cell.batchId} ${this['_links']['cells']}/${cell.batchId} `; }); const data_xml = ` ${this['_links']['cells']} ${entries.join('\n')} `; const { data } = await this.spreadsheet.makeFeedRequest( this['_links']['bulkcells'], 'POST', data_xml, ); // update all the cells const cells_by_batch_id = _.keyBy(cells, 'batchId'); if (data.entry && data.entry.length) { data.entry.forEach((cell_data: any) => { cells_by_batch_id[cell_data['batch:id']].updateValuesFromResponseData( cell_data, ); }); } }; del = async () => { return this.spreadsheet.makeFeedRequest( this['_links']['edit'], 'DELETE', null, ); }; setHeaderRow = async (values: any) => { if (!values) { return; } if (values.length > this.colCount) { throw Error( 'Sheet is not large enough to fit ' + values.length + ' columns. Resize the sheet first.', ); } const cells = await this.getCells({ 'min-row': 1, 'max-row': 1, 'min-col': 1, 'max-col': this.colCount, 'return-empty': true, }); _.each(cells, cell => { cell.value = values[cell.col - 1] ? values[cell.col - 1] : ''; }); return this.bulkUpdateCells(cells); }; }