Files
ragflow/internal/parser/parser/xlsx_parser.go
Jack 047c714ffb Feat: ingestion cleanup (#16953)
### Summary

1. Remove dead code (replaced by builtin ingestion pipeline)
2. Refactor (move document parsing progress from http api into ingestion
executor)
2026-07-16 11:05:26 +08:00

148 lines
4.0 KiB
Go

//
// Copyright 2026 The InfiniFlow Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
package parser
import (
"bytes"
"fmt"
"strings"
"github.com/xuri/excelize/v2"
)
type XLSXParser struct {
libType string
ParseMethod string
OutputFormat string
TCADPAPIServer string
TCADPAPIKey string
TCADPTableResultType string
TCADPMarkdownImageResponseType string
}
func NewXLSXParser(libType string) (*XLSXParser, error) {
if libType == "" {
libType = "excelize"
}
return &XLSXParser{
libType: libType,
TCADPTableResultType: "1",
TCADPMarkdownImageResponseType: "1",
}, nil
}
func (p *XLSXParser) String() string {
return "XLSXParser"
}
func (p *XLSXParser) ConfigureFromSetup(setup map[string]any) {
if p == nil || setup == nil {
return
}
if v, ok := setup["parse_method"].(string); ok && v != "" {
p.ParseMethod = v
}
if v, ok := setup["output_format"].(string); ok && v != "" {
p.OutputFormat = v
}
if v, ok := setup["tcadp_apiserver"].(string); ok && v != "" {
p.TCADPAPIServer = v
}
if v, ok := setup["tcadp_api_key"].(string); ok {
p.TCADPAPIKey = v
}
if v, ok := setup["table_result_type"].(string); ok && v != "" {
p.TCADPTableResultType = v
}
if v, ok := setup["markdown_image_response_type"].(string); ok && v != "" {
p.TCADPMarkdownImageResponseType = v
}
}
func normalizeXLSXParseMethod(raw string) string {
method := strings.ToLower(strings.TrimSpace(raw))
if method == "tcadp parser" {
return "tcadp"
}
// "deepdoc" / "deepdoc parser" is the default spreadsheet parse_method
// (see schema.ParserParam.Defaults and the matching Python ParserParam),
// and the DSL templates ship "DeepDOC". Normalize to "" so the default
// Excelize/CSV path is taken by all three spreadsheet parsers, mirroring
// rag/flow/parser/parser.py:_spreadsheet which only special-cases
// "tcadp parser" and routes every other value to the default parser.
if method == "deepdoc" || method == "deepdoc parser" {
return ""
}
return method
}
func (p *XLSXParser) ParseWithResult(filename string, data []byte) ParseResult {
method := normalizeXLSXParseMethod(p.ParseMethod)
switch method {
case "tcadp":
return parseSpreadsheetWithTCADP(
filename, data, "XLSX",
p.TCADPAPIServer, p.TCADPAPIKey,
p.TCADPTableResultType, p.TCADPMarkdownImageResponseType,
p.OutputFormat,
)
case "", "excelize":
// Continue with the local Excelize parser.
default:
return ParseResult{
Err: fmt.Errorf("unsupported XLSX parse method: %q", p.ParseMethod),
}
}
f, err := excelize.OpenReader(bytes.NewReader(data))
if err != nil {
return ParseResult{Err: fmt.Errorf("xlsx open: %w", err)}
}
defer f.Close()
sheets := f.GetSheetList()
var html strings.Builder
html.WriteString("<html><body>")
for _, sheet := range sheets {
html.WriteString("<h3>")
html.WriteString(sheet)
html.WriteString("</h3>")
rows, err := f.GetRows(sheet)
if err != nil {
continue
}
html.WriteString("<table>")
for _, row := range rows {
html.WriteString("<tr>")
for _, cell := range row {
html.WriteString("<td>")
html.WriteString(htmlEscape(cell))
html.WriteString("</td>")
}
html.WriteString("</tr>")
}
html.WriteString("</table>")
}
html.WriteString("</body></html>")
return ParseResult{
OutputFormat: "html",
File: map[string]any{"name": filename, "format": "xlsx", "sheets": len(sheets)},
HTML: html.String(),
}
}