Skip to content

Commit

Permalink
Add 5 new functions: add_chart_sheet, add_comment, add_pivot_table, g…
Browse files Browse the repository at this point in the history
…et_active_sheet_index and get_app_props

- Update unit tests and docs for the function
  • Loading branch information
xuri committed Dec 4, 2024
1 parent cafda31 commit 030ae24
Show file tree
Hide file tree
Showing 6 changed files with 890 additions and 228 deletions.
169 changes: 168 additions & 1 deletion excelize.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ def load_lib():
lib = CDLL(os.path.join(os.path.dirname(__file__), load_lib()))
ENCODE = "utf-8"
__version__ = "0.0.2"
uppercase_words = ["xml"]
uppercase_words = ["id", "xml"]


def py_to_base_ctype(py_value, c_type):
Expand Down Expand Up @@ -495,6 +495,57 @@ def add_chart(
).decode(ENCODE)
return None if err == "" else Exception(err)

def add_chart_sheet(
self, sheet: str, chart: Chart, **combo: Chart
) -> Exception | None:
"""
Create a chartsheet by given chart format set (such as offset, scale,
aspect ratio setting and print settings) and properties set. In Excel a
chartsheet is a worksheet that only contains a chart.
Args:
sheet (str): The worksheet name
chart (Chart): Chart options
**combo (Chart): Optional parameters for combo chart
Returns:
Exception | None: Returns None if no error occurred,
otherwise returns an Exception with the message.
"""
lib.AddChartSheet.restype = c_char_p
opts = [chart] + list(combo.values())
charts = (types_go._Chart * len(opts))()
for i, opt in enumerate(opts):
charts[i] = py_value_to_c(opt, types_go._Chart())
err = lib.AddChartSheet(
self.file_index,
sheet.encode(ENCODE),
byref(charts),
len(charts),
).decode(ENCODE)
return None if err == "" else Exception(err)

def add_comment(self, sheet: str, opts: Comment) -> Exception | None:
"""
Add comments in a sheet by giving the worksheet name, cell reference,
and format set (such as author and text). Note that the maximum author
name length is 255 and the max text length is 32512.
Args:
sheet (str): The worksheet name
opts (Comment): The comment options
Returns:
Exception | None: Returns None if no error occurred,
otherwise returns an Exception with the message.
"""
lib.AddComment.restype = c_char_p
options = py_value_to_c(opts, types_go._Comment())
err = lib.AddComment(
self.file_index, sheet.encode(ENCODE), byref(options)
).decode(ENCODE)
return None if err == "" else Exception(err)

def add_picture(
self, sheet: str, cell: str, name: str, opts: GraphicOptions | None
) -> Exception | None:
Expand Down Expand Up @@ -529,6 +580,94 @@ def add_picture(
).decode(ENCODE)
return None if err == "" else Exception(err)

def add_pivot_table(self, opts: PivotTableOptions | None) -> Exception | None:
"""
Add pivot table by given pivot table options. Note that the same fields
can not in Columns, Rows and Filter fields at the same time.
Args:
opts (PivotTableOptions): The pivot table options
Returns:
Exception | None: Returns None if no error occurred,
otherwise returns an Exception with the message.
Example:
For example, create a pivot table on the range reference
Sheet1!G2:M34 with the range reference Sheet1!A1:E31 as the data
source, summarize by sum for sales:
.. code-block:: python
import excelize
import random
f = excelize.new_file()
month = [
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
]
year = [2017, 2018, 2019]
types = ["Meat", "Dairy", "Beverages", "Produce"]
region = ["East", "West", "North", "South"]
err = f.set_sheet_row("Sheet1", "A1", ["Month", "Year", "Type", "Sales", "Region"])
if err:
print(err)
for row in range(2, 32):
err = f.set_cell_value("Sheet1", f"A{row}", month[random.randrange(12)])
if err:
print(err)
err = f.set_cell_value("Sheet1", f"B{row}", year[random.randrange(3)])
if err:
print(err)
err = f.set_cell_value("Sheet1", f"C{row}", types[random.randrange(4)])
if err:
print(err)
err = f.set_cell_value("Sheet1", f"D{row}", random.randrange(5000))
if err:
print(err)
err = f.set_cell_value("Sheet1", f"E{row}", region[random.randrange(4)])
if err:
print(err)
err = f.add_pivot_table(
excelize.PivotTableOptions(
data_range="Sheet1!A1:E31",
pivot_table_range="Sheet1!G2:M34",
rows=[
excelize.PivotTableField(data="Month", default_subtotal=True),
excelize.PivotTableField(data="Year"),
],
filter=[excelize.PivotTableField(data="Region")],
columns=[
excelize.PivotTableField(data="Type", default_subtotal=True),
],
data=[
excelize.PivotTableField(data="Sales", name="Summarize", subtotal="Sum"),
],
row_grand_totals=True,
col_grand_totals=True,
show_drill=True,
show_row_headers=True,
show_col_headers=True,
show_last_column=True,
)
)
if err:
print(err)
err = f.save_as("TestAddPivotTable.xlsx")
if err:
print(err)
err = f.close()
if err:
print(err)
"""
lib.AddPivotTable.restype = c_char_p
err = lib.AddPivotTable(
self.file_index, byref(py_value_to_c(opts, types_go._PivotTableOptions()))
).decode(ENCODE)
return None if err == "" else Exception(err)

def close(self) -> Exception | None:
"""
Closes and cleanup the open temporary file for the spreadsheet.
Expand Down Expand Up @@ -693,6 +832,34 @@ def duplicate_row_to(self, sheet: str, row: int, row2: int) -> Exception | None:
).decode(ENCODE)
return None if err == "" else Exception(err)

def get_active_sheet_index(self) -> int:
"""
Get active sheet index of the spreadsheet. If not found the active sheet
will be return integer 0.
Returns:
int: The active sheet index
"""
lib.GetActiveSheetIndex.restype = c_int
res = lib.GetActiveSheetIndex(self.file_index)
return res

def get_app_props(self) -> Tuple[AppProperties | None, Exception | None]:
"""
Get document application properties.
Returns:
Tuple[AppProperties | None, Exception | None]: A tuple containing the
app properties if found, otherwise None, and an Exception object if
an error occurred, otherwise None.
"""
lib.GetAppProps.restype = types_go._GetAppPropsResult
res = lib.GetAppProps(self.file_index)
err = res.err.decode(ENCODE)
return c_value_to_py(res.opts, AppProperties()) if err == "" else None, (
None if err == "" else Exception(err)
)

def get_cell_value(
self, sheet: str, cell: str, *opts: Options
) -> Tuple[str, Exception | None]:
Expand Down
111 changes: 111 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,10 @@ func cToGoArray(cArray reflect.Value, cArrayLen int) reflect.Value {
val := cArray.Interface().(*C.struct_ChartSeries)
arr := unsafe.Slice(val, cArrayLen)
return reflect.ValueOf(arr)
case "main._Ctype_struct_PivotTableField":
val := cArray.Interface().(*C.struct_PivotTableField)
arr := unsafe.Slice(val, cArrayLen)
return reflect.ValueOf(arr)
case "main._Ctype_struct_RichTextRun":
val := cArray.Interface().(*C.struct_RichTextRun)
arr := unsafe.Slice(val, cArrayLen)
Expand Down Expand Up @@ -447,6 +451,61 @@ func AddChart(idx int, sheet, cell *C.char, chart *C.struct_Chart, length int) *
return C.CString(errNil)
}

// AddChartSheet provides the method to create a chartsheet by given chart
// format set (such as offset, scale, aspect ratio setting and print settings)
// and properties set. In Excel a chartsheet is a worksheet that only contains
// a chart.
//
//export AddChartSheet
func AddChartSheet(idx int, sheet *C.char, chart *C.struct_Chart, length int) *C.char {
f, ok := files.Load(idx)
if !ok {
return C.CString(errFilePtr)
}
charts := make([]*excelize.Chart, length)
for i, c := range unsafe.Slice(chart, length) {
goVal, err := cValueToGo(reflect.ValueOf(c), reflect.TypeOf(excelize.Chart{}))
if err != nil {
return C.CString(err.Error())
}
c := goVal.Elem().Interface().(excelize.Chart)
charts[i] = &c
}
if len(charts) > 1 {
if err := f.(*excelize.File).AddChartSheet(C.GoString(sheet), charts[0], charts[1:]...); err != nil {
return C.CString(err.Error())
}
return C.CString(errNil)
}
if err := f.(*excelize.File).AddChartSheet(C.GoString(sheet), charts[0]); err != nil {
return C.CString(err.Error())
}
return C.CString(errNil)
}

// AddComment provides the method to add comments in a sheet by giving the
// worksheet name, cell reference, and format set (such as author and text).
// Note that the maximum author name length is 255 and the max text length is
// 32512.
//
//export AddComment
func AddComment(idx int, sheet *C.char, opts *C.struct_Comment) *C.char {
var comment excelize.Comment
goVal, err := cValueToGo(reflect.ValueOf(*opts), reflect.TypeOf(excelize.Comment{}))
if err != nil {
return C.CString(err.Error())
}
comment = goVal.Elem().Interface().(excelize.Comment)
f, ok := files.Load(idx)
if !ok {
return C.CString(errFilePtr)
}
if err := f.(*excelize.File).AddComment(C.GoString(sheet), comment); err != nil {
return C.CString(err.Error())
}
return C.CString(errNil)
}

// Add picture in a sheet by given picture format set (such as offset, scale,
// aspect ratio setting and print settings) and file path, supported image
// types: BMP, EMF, EMZ, GIF, JPEG, JPG, PNG, SVG, TIF, TIFF, WMF, and WMZ.
Expand Down Expand Up @@ -474,6 +533,27 @@ func AddPicture(idx int, sheet, cell, name *C.char, opts *C.struct_GraphicOption
return C.CString(errNil)
}

// AddPivotTable provides the method to add pivot table by given pivot table
// options. Note that the same fields can not in Columns, Rows and Filter
// fields at the same time.
//
//export AddPivotTable
func AddPivotTable(idx int, opts *C.struct_PivotTableOptions) *C.char {
f, ok := files.Load(idx)
if !ok {
return C.CString(errFilePtr)
}
goVal, err := cValueToGo(reflect.ValueOf(*opts), reflect.TypeOf(excelize.PivotTableOptions{}))
if err != nil {
return C.CString(err.Error())
}
options := goVal.Elem().Interface().(excelize.PivotTableOptions)
if err := f.(*excelize.File).AddPivotTable(&options); err != nil {
return C.CString(err.Error())
}
return C.CString(errNil)
}

// CellNameToCoordinates converts alphanumeric cell name to [X, Y] coordinates
// or returns an error.
//
Expand Down Expand Up @@ -669,6 +749,37 @@ func DuplicateRowTo(idx int, sheet *C.char, row, row2 int) *C.char {
return C.CString(errNil)
}

// GetActiveSheetIndex provides a function to get active sheet index of the
// spreadsheet. If not found the active sheet will be return integer 0.
//
//export GetActiveSheetIndex
func GetActiveSheetIndex(idx int) int {
f, ok := files.Load(idx)
if !ok {
return 0
}
return f.(*excelize.File).GetActiveSheetIndex()
}

// GetAppProps provides a function to get document application properties.
//
//export GetAppProps
func GetAppProps(idx int) C.struct_GetAppPropsResult {
f, ok := files.Load(idx)
if !ok {
return C.struct_GetAppPropsResult{err: C.CString(errFilePtr)}
}
opts, err := f.(*excelize.File).GetAppProps()
if err != nil {
return C.struct_GetAppPropsResult{err: C.CString(err.Error())}
}
cVal, err := goValueToC(reflect.ValueOf(*opts), reflect.ValueOf(&C.struct_AppProperties{}))
if err != nil {
return C.struct_GetAppPropsResult{err: C.CString(err.Error())}
}
return C.struct_GetAppPropsResult{opts: cVal.Elem().Interface().(C.struct_AppProperties), err: C.CString(errNil)}
}

// GetCellValue provides a function to get formatted value from cell by given
// worksheet name and cell reference in spreadsheet. The return value is
// converted to the `string` data type. If the cell format can be applied to
Expand Down
Loading

0 comments on commit 030ae24

Please sign in to comment.