This closes #866, support use the defined name to reference the data range in pivot table options

- Fix incorrect scope when getting defined name
- Update docs: use column number instead of index on get column width
This commit is contained in:
xuri 2021-06-29 22:26:55 +08:00
parent 24967a5c25
commit f27624acdd
No known key found for this signature in database
GPG Key ID: BA5E5BB1C948EDF7
4 changed files with 51 additions and 20 deletions

13
calc.go
View File

@ -955,16 +955,23 @@ func isOperatorPrefixToken(token efp.Token) bool {
// getDefinedNameRefTo convert defined name to reference range. // getDefinedNameRefTo convert defined name to reference range.
func (f *File) getDefinedNameRefTo(definedNameName string, currentSheet string) (refTo string) { func (f *File) getDefinedNameRefTo(definedNameName string, currentSheet string) (refTo string) {
var workbookRefTo, worksheetRefTo string
for _, definedName := range f.GetDefinedName() { for _, definedName := range f.GetDefinedName() {
if definedName.Name == definedNameName { if definedName.Name == definedNameName {
refTo = definedName.RefersTo
// worksheet scope takes precedence over scope workbook when both definedNames exist // worksheet scope takes precedence over scope workbook when both definedNames exist
if definedName.Scope == "Workbook" {
workbookRefTo = definedName.RefersTo
}
if definedName.Scope == currentSheet { if definedName.Scope == currentSheet {
break worksheetRefTo = definedName.RefersTo
} }
} }
} }
return refTo refTo = workbookRefTo
if worksheetRefTo != "" {
refTo = worksheetRefTo
}
return
} }
// parseToken parse basic arithmetic operator priority and evaluate based on // parseToken parse basic arithmetic operator priority and evaluate based on

4
col.go
View File

@ -604,7 +604,7 @@ func (f *File) positionObjectPixels(sheet string, col, row, x1, y1, width, heigh
} }
// getColWidth provides a function to get column width in pixels by given // getColWidth provides a function to get column width in pixels by given
// sheet name and column index. // sheet name and column number.
func (f *File) getColWidth(sheet string, col int) int { func (f *File) getColWidth(sheet string, col int) int {
xlsx, _ := f.workSheetReader(sheet) xlsx, _ := f.workSheetReader(sheet)
if xlsx.Cols != nil { if xlsx.Cols != nil {
@ -623,7 +623,7 @@ func (f *File) getColWidth(sheet string, col int) int {
} }
// GetColWidth provides a function to get column width by given worksheet name // GetColWidth provides a function to get column width by given worksheet name
// and column index. // and column name.
func (f *File) GetColWidth(sheet, col string) (float64, error) { func (f *File) GetColWidth(sheet, col string) (float64, error) {
colNum, err := ColumnNameToNumber(col) colNum, err := ColumnNameToNumber(col)
if err != nil { if err != nil {

View File

@ -21,6 +21,7 @@ import (
// PivotTableOption directly maps the format settings of the pivot table. // PivotTableOption directly maps the format settings of the pivot table.
type PivotTableOption struct { type PivotTableOption struct {
pivotTableSheetName string
DataRange string DataRange string
PivotTableRange string PivotTableRange string
Rows []PivotTableField Rows []PivotTableField
@ -164,14 +165,19 @@ func (f *File) parseFormatPivotTableSet(opt *PivotTableOption) (*xlsxWorksheet,
if opt == nil { if opt == nil {
return nil, "", errors.New("parameter is required") return nil, "", errors.New("parameter is required")
} }
dataSheetName, _, err := f.adjustRange(opt.DataRange)
if err != nil {
return nil, "", fmt.Errorf("parameter 'DataRange' parsing error: %s", err.Error())
}
pivotTableSheetName, _, err := f.adjustRange(opt.PivotTableRange) pivotTableSheetName, _, err := f.adjustRange(opt.PivotTableRange)
if err != nil { if err != nil {
return nil, "", fmt.Errorf("parameter 'PivotTableRange' parsing error: %s", err.Error()) return nil, "", fmt.Errorf("parameter 'PivotTableRange' parsing error: %s", err.Error())
} }
opt.pivotTableSheetName = pivotTableSheetName
dataRange := f.getDefinedNameRefTo(opt.DataRange, pivotTableSheetName)
if dataRange == "" {
dataRange = opt.DataRange
}
dataSheetName, _, err := f.adjustRange(dataRange)
if err != nil {
return nil, "", fmt.Errorf("parameter 'DataRange' parsing error: %s", err.Error())
}
dataSheet, err := f.workSheetReader(dataSheetName) dataSheet, err := f.workSheetReader(dataSheetName)
if err != nil { if err != nil {
return dataSheet, "", err return dataSheet, "", err
@ -215,8 +221,12 @@ func (f *File) adjustRange(rangeStr string) (string, []int, error) {
// getPivotFieldsOrder provides a function to get order list of pivot table // getPivotFieldsOrder provides a function to get order list of pivot table
// fields. // fields.
func (f *File) getPivotFieldsOrder(dataRange string) ([]string, error) { func (f *File) getPivotFieldsOrder(opt *PivotTableOption) ([]string, error) {
order := []string{} order := []string{}
dataRange := f.getDefinedNameRefTo(opt.DataRange, opt.pivotTableSheetName)
if dataRange == "" {
dataRange = opt.DataRange
}
dataSheet, coordinates, err := f.adjustRange(dataRange) dataSheet, coordinates, err := f.adjustRange(dataRange)
if err != nil { if err != nil {
return order, fmt.Errorf("parameter 'DataRange' parsing error: %s", err.Error()) return order, fmt.Errorf("parameter 'DataRange' parsing error: %s", err.Error())
@ -235,12 +245,18 @@ func (f *File) getPivotFieldsOrder(dataRange string) ([]string, error) {
// addPivotCache provides a function to create a pivot cache by given properties. // addPivotCache provides a function to create a pivot cache by given properties.
func (f *File) addPivotCache(pivotCacheID int, pivotCacheXML string, opt *PivotTableOption, ws *xlsxWorksheet) error { func (f *File) addPivotCache(pivotCacheID int, pivotCacheXML string, opt *PivotTableOption, ws *xlsxWorksheet) error {
// validate data range // validate data range
dataSheet, coordinates, err := f.adjustRange(opt.DataRange) definedNameRef := true
dataRange := f.getDefinedNameRefTo(opt.DataRange, opt.pivotTableSheetName)
if dataRange == "" {
definedNameRef = false
dataRange = opt.DataRange
}
dataSheet, coordinates, err := f.adjustRange(dataRange)
if err != nil { if err != nil {
return fmt.Errorf("parameter 'DataRange' parsing error: %s", err.Error()) return fmt.Errorf("parameter 'DataRange' parsing error: %s", err.Error())
} }
// data range has been checked // data range has been checked
order, _ := f.getPivotFieldsOrder(opt.DataRange) order, _ := f.getPivotFieldsOrder(opt)
hcell, _ := CoordinatesToCellName(coordinates[0], coordinates[1]) hcell, _ := CoordinatesToCellName(coordinates[0], coordinates[1])
vcell, _ := CoordinatesToCellName(coordinates[2], coordinates[3]) vcell, _ := CoordinatesToCellName(coordinates[2], coordinates[3])
pc := xlsxPivotCacheDefinition{ pc := xlsxPivotCacheDefinition{
@ -258,7 +274,9 @@ func (f *File) addPivotCache(pivotCacheID int, pivotCacheXML string, opt *PivotT
}, },
CacheFields: &xlsxCacheFields{}, CacheFields: &xlsxCacheFields{},
} }
if definedNameRef {
pc.CacheSource.WorksheetSource = &xlsxWorksheetSource{Name: opt.DataRange}
}
for _, name := range order { for _, name := range order {
defaultRowsSubtotal, rowOk := f.getPivotTableFieldNameDefaultSubtotal(name, opt.Rows) defaultRowsSubtotal, rowOk := f.getPivotTableFieldNameDefaultSubtotal(name, opt.Rows)
defaultColumnsSubtotal, colOk := f.getPivotTableFieldNameDefaultSubtotal(name, opt.Columns) defaultColumnsSubtotal, colOk := f.getPivotTableFieldNameDefaultSubtotal(name, opt.Columns)
@ -509,7 +527,7 @@ func (f *File) addPivotColFields(pt *xlsxPivotTableDefinition, opt *PivotTableOp
// addPivotFields create pivot fields based on the column order of the first // addPivotFields create pivot fields based on the column order of the first
// row in the data region by given pivot table definition and option. // row in the data region by given pivot table definition and option.
func (f *File) addPivotFields(pt *xlsxPivotTableDefinition, opt *PivotTableOption) error { func (f *File) addPivotFields(pt *xlsxPivotTableDefinition, opt *PivotTableOption) error {
order, err := f.getPivotFieldsOrder(opt.DataRange) order, err := f.getPivotFieldsOrder(opt)
if err != nil { if err != nil {
return err return err
} }
@ -606,7 +624,7 @@ func (f *File) countPivotCache() int {
// to a sequential index by given fields and pivot option. // to a sequential index by given fields and pivot option.
func (f *File) getPivotFieldsIndex(fields []PivotTableField, opt *PivotTableOption) ([]int, error) { func (f *File) getPivotFieldsIndex(fields []PivotTableField, opt *PivotTableOption) ([]int, error) {
pivotFieldsIndex := []int{} pivotFieldsIndex := []int{}
orders, err := f.getPivotFieldsOrder(opt.DataRange) orders, err := f.getPivotFieldsOrder(opt)
if err != nil { if err != nil {
return pivotFieldsIndex, err return pivotFieldsIndex, err
} }

View File

@ -136,10 +136,16 @@ func TestAddPivotTable(t *testing.T) {
ShowColHeaders: true, ShowColHeaders: true,
ShowLastColumn: true, ShowLastColumn: true,
})) }))
//Test Pivot table with many data, many rows, many cols // Create pivot table with many data, many rows, many cols and defined name
f.SetDefinedName(&DefinedName{
Name: "dataRange",
RefersTo: "Sheet1!$A$1:$E$31",
Comment: "Pivot Table Data Range",
Scope: "Sheet2",
})
assert.NoError(t, f.AddPivotTable(&PivotTableOption{ assert.NoError(t, f.AddPivotTable(&PivotTableOption{
DataRange: "Sheet1!$A$1:$E$31", DataRange: "dataRange",
PivotTableRange: "Sheet2!$A$56:$AG$90", PivotTableRange: "Sheet2!$A$57:$AJ$91",
Rows: []PivotTableField{{Data: "Month", DefaultSubtotal: true}, {Data: "Year"}}, Rows: []PivotTableField{{Data: "Month", DefaultSubtotal: true}, {Data: "Year"}},
Columns: []PivotTableField{{Data: "Region", DefaultSubtotal: true}, {Data: "Type"}}, Columns: []PivotTableField{{Data: "Region", DefaultSubtotal: true}, {Data: "Type"}},
Data: []PivotTableField{{Data: "Sales", Subtotal: "Sum", Name: "Sum of Sales"}, {Data: "Sales", Subtotal: "Average", Name: "Average of Sales"}}, Data: []PivotTableField{{Data: "Sales", Subtotal: "Sum", Name: "Sum of Sales"}, {Data: "Sales", Subtotal: "Average", Name: "Average of Sales"}},
@ -223,7 +229,7 @@ func TestAddPivotTable(t *testing.T) {
_, _, err := f.adjustRange("") _, _, err := f.adjustRange("")
assert.EqualError(t, err, "parameter is required") assert.EqualError(t, err, "parameter is required")
// Test get pivot fields order with empty data range // Test get pivot fields order with empty data range
_, err = f.getPivotFieldsOrder("") _, err = f.getPivotFieldsOrder(&PivotTableOption{})
assert.EqualError(t, err, `parameter 'DataRange' parsing error: parameter is required`) assert.EqualError(t, err, `parameter 'DataRange' parsing error: parameter is required`)
// Test add pivot cache with empty data range // Test add pivot cache with empty data range
assert.EqualError(t, f.addPivotCache(0, "", &PivotTableOption{}, nil), "parameter 'DataRange' parsing error: parameter is required") assert.EqualError(t, f.addPivotCache(0, "", &PivotTableOption{}, nil), "parameter 'DataRange' parsing error: parameter is required")
@ -288,7 +294,7 @@ func TestAddPivotColFields(t *testing.T) {
func TestGetPivotFieldsOrder(t *testing.T) { func TestGetPivotFieldsOrder(t *testing.T) {
f := NewFile() f := NewFile()
// Test get pivot fields order with not exist worksheet // Test get pivot fields order with not exist worksheet
_, err := f.getPivotFieldsOrder("SheetN!$A$1:$E$31") _, err := f.getPivotFieldsOrder(&PivotTableOption{DataRange: "SheetN!$A$1:$E$31"})
assert.EqualError(t, err, "sheet SheetN is not exist") assert.EqualError(t, err, "sheet SheetN is not exist")
} }