123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380 |
- package kpler
- import (
- "encoding/json"
- "fmt"
- "net/url"
- "regexp"
- "strings"
- "github.com/xuri/excelize/v2"
- )
- // ExcelData represents structured data extracted from an Excel file
- type ExcelData struct {
- Headers []string
- Rows [][]string
- SheetName string
- }
- // KplerFormulaData represents parsed data from a Kpler formula
- type KplerFormulaData struct {
- Function string
- Parameters []string
- FullFormula string
- }
- // ParseExcel reads and parses data from an Excel file
- func ParseExcel(filePath string) (*ExcelData, error) {
- // Open the Excel file
- f, err := excelize.OpenFile(filePath)
- if err != nil {
- return nil, fmt.Errorf("error opening Excel file: %w", err)
- }
- defer f.Close()
- // Get the first sheet by default
- sheetName := f.GetSheetList()[0]
-
- // Get all rows from the sheet
- rows, err := f.GetRows(sheetName)
- if err != nil {
- return nil, fmt.Errorf("error reading rows from sheet %s: %w", sheetName, err)
- }
- // Check if there's data
- if len(rows) == 0 {
- return nil, fmt.Errorf("no data found in sheet %s", sheetName)
- }
- // Create structured data
- excelData := &ExcelData{
- SheetName: sheetName,
- Headers: rows[0],
- Rows: rows[1:],
- }
- return excelData, nil
- }
- // ParseKplerFormula extracts the function name and parameters from a Kpler Excel formula
- func ParseKplerFormula(formula string) (*KplerFormulaData, error) {
- result := &KplerFormulaData{
- FullFormula: formula,
- }
- // Regular expression to match the function name and parameters
- // This pattern looks for: =@'path\to\file.xlam'!FunctionName(param1,param2,...)
- re := regexp.MustCompile(`=@'[^']*'!([A-Za-z0-9_]+)\((.*)\)`)
- matches := re.FindStringSubmatch(formula)
-
- if len(matches) < 3 {
- // Try another pattern without the @' prefix
- re = regexp.MustCompile(`=([A-Za-z0-9_]+)\((.*)\)`)
- matches = re.FindStringSubmatch(formula)
-
- if len(matches) < 3 {
- return nil, fmt.Errorf("could not parse Kpler formula: %s", formula)
- }
- }
-
- // Extract function name
- result.Function = matches[1]
-
- // Extract parameters
- paramsStr := matches[2]
-
- // Split parameters, handling commas inside quotes
- var params []string
- inQuote := false
- currentParam := ""
-
- for _, char := range paramsStr {
- switch char {
- case '"':
- inQuote = !inQuote
- currentParam += string(char)
- case ',':
- if inQuote {
- currentParam += string(char)
- } else {
- params = append(params, strings.TrimSpace(currentParam))
- currentParam = ""
- }
- default:
- currentParam += string(char)
- }
- }
-
- // Add the last parameter
- if currentParam != "" {
- params = append(params, strings.TrimSpace(currentParam))
- }
-
- result.Parameters = params
- return result, nil
- }
- // ScanSheetForFormulas scans an entire sheet for formulas
- func ScanSheetForFormulas(filePath, sheetName string) (map[int]map[int]string, error) {
- // Open the Excel file
- f, err := excelize.OpenFile(filePath)
- if err != nil {
- return nil, fmt.Errorf("error opening Excel file: %w", err)
- }
- defer f.Close()
- formulas := make(map[int]map[int]string)
-
- // Get sheet dimensions
- dimension, err := f.GetSheetDimension(sheetName)
- if err != nil {
- return nil, fmt.Errorf("error getting sheet dimension: %w", err)
- }
-
- // Parse dimension to get the range (e.g., "A1:K42")
- parts := strings.Split(dimension, ":")
- if len(parts) != 2 {
- // Use a default range if dimension is not in expected format
- parts = []string{"A1", "Z100"}
- }
-
- // Extract the column letters and row numbers
- startCol, startRow, err := excelize.CellNameToCoordinates(parts[0])
- if err != nil {
- startCol, startRow = 1, 1
- }
-
- endCol, endRow, err := excelize.CellNameToCoordinates(parts[1])
- if err != nil {
- endCol, endRow = 26, 100 // Default to Z100
- }
-
- // Scan cells for formulas
- for row := startRow; row <= endRow; row++ {
- for col := startCol; col <= endCol; col++ {
- colName, err := excelize.ColumnNumberToName(col)
- if err != nil {
- continue
- }
-
- cellCoord := fmt.Sprintf("%s%d", colName, row)
- formula, err := f.GetCellFormula(sheetName, cellCoord)
-
- if err == nil && formula != "" {
- // Store all formulas or only Kpler-related ones (adjust as needed)
- //if strings.Contains(formula, "kpler") || strings.Contains(formula, "GetFlows") {
- if strings.Contains(formula, "GETFLOWS") {
- // fmt.Println("row: ", row)
- // fmt.Println("col: ", col)
- // fmt.Println("GetCellFormula: ", formula)
- if _, ok := formulas[row-1]; !ok {
- formulas[row-1] = make(map[int]string)
- }
- formulas[row-1][col-1] = formula
- }
- }
- }
- }
-
- return formulas, nil
- }
- // ProcessKplerData demonstrates how to parse and process Kpler crude flow data
- func ProcessKplerData() error {
- // Path to the Kpler crude flow Excel file
- //filePath := "services/kpler/Kpler crude flow (自动保存的).xlsx"
- filePath := "services/kpler/最新版kpler插件.xlsx"
- // First, list all sheets in the Excel file
- // Open the Excel file
- f, err := excelize.OpenFile(filePath)
- if err != nil {
- return fmt.Errorf("error opening Excel file: %w", err)
- }
- // Return the list of sheets
- sheets := f.GetSheetList()
- if err != nil {
- return fmt.Errorf("error listing sheets: %w", err)
- }
- defer f.Close()
-
- fmt.Println("Available sheets:")
- for i, sheet := range sheets {
- fmt.Printf("%d. %s\n", i+1, sheet)
- }
-
- // Parse the Excel file using the first sheet (default)
- data, err := ParseExcel(filePath)
- if err != nil {
- return fmt.Errorf("error parsing Excel file: %w", err)
- }
-
- // Print the headers and a sample of data rows
- // fmt.Println("\nHeaders found in the sheet:")
- // for i, header := range data.Headers {
- // fmt.Printf("%d. %s\n", i+1, header)
- // }
-
- // Look for Kpler formulas
- fmt.Println("\nLooking for Kpler formulas across the sheet...")
- formulas, err := ScanSheetForFormulas(filePath, data.SheetName)
- if err != nil {
- fmt.Printf("Error scanning for formulas: %v\n", err)
- } else {
- fmt.Printf("Found %d formulas in the sheet.\n", len(formulas))
- }
-
- fmt.Println("\nSample data (first 5 rows):")
- rowCount := 5
- if len(data.Rows) < rowCount {
- rowCount = len(data.Rows)
- }
-
- for i := 0; i < rowCount; i++ {
- fmt.Printf("Row %d:\n", i+1)
- row := data.Rows[i]
- for j, cell := range row {
- // fmt.Println("i: ", i)
- // fmt.Println("j: ", j)
- // //fmt.Println("data.Headers[j]: ", data.Headers[j])
- // fmt.Println("cell: ", cell)
- if i == 1 {
- formula, ok:= formulas[i][j]
- if ok {
- fmt.Printf(" %s formula: %s\n", data.Headers[j], formula)
- // 解析公式
- ParseSpecificKplerFormulaV2(formula)
- }
- }
- if j < len(data.Headers) && cell != "" {
- fmt.Printf(" %s: %s\n", data.Headers[j], cell)
- }
- }
- fmt.Println()
- }
-
- // Print total number of data rows
- fmt.Printf("Total data rows: %d\n", len(data.Rows))
-
- return nil
- }
- // ParseSpecificKplerFormula parses the specific Kpler formula provided by the user
- func ParseSpecificKplerFormulaV1() {
- // The specific formula provided in the user's question
- specificFormula := `=@'F:\Desktop\kpler\kpler-excel-addin.xlam'!GetFlows("China,","Russian Federation,Venezuela,Iran,EOPL,",,,,,"import","weekly","Total","kbd",FALSE,,FALSE,FALSE,FALSE,FALSE,TRUE,"CPP",)`
-
- fmt.Println("Analyzing the specific Kpler formula from the user's question:")
- fmt.Println(specificFormula)
-
- parsedFormula, err := ParseKplerFormula(specificFormula)
- if err != nil {
- fmt.Printf("Error parsing formula: %v\n", err)
- return
- }
-
- fmt.Println("\nFormula breakdown:")
- fmt.Printf("Function name: %s\n", parsedFormula.Function)
- fmt.Println("Parameters list:")
-
- // Define parameter names for GetFlows function based on Kpler documentation
- paramNames := []string{
- "fromZones", // 1. "Saudi Arabia,"
- "toZones", // 2. empty
- "fromInstallations", // 3. empty
- "toInstallations", // 4. empty
- "fromRegions", // 5. empty
- "toRegions", // 6. empty
- "flowDirection", // 7. "import"
- "granularity", // 8. "weekly"
- "split", // 9. "Total"
- "unit", // 10. "kbd"
- "withForecast", // 14. FALSE
- "products",
- "withProductEstimation", // 15. FALSE
- "withIntraCountry", // 16. FALSE
- "withIntraRegion", // 17. TRUE
- "withFreightView", // false
- "withPeriodEndTime", // false
- "productFilter", // 18. "CPP"
- "lastDataPoints", // 19. empty
- }
- // Print each parameter with its meaning
- for i, param := range parsedFormula.Parameters {
- if i < len(paramNames) {
- paramName := paramNames[i]
- if param == "" {
- fmt.Printf(" %s: [empty]\n", paramName)
- } else {
- fmt.Printf(" %s: %s\n", paramName, param)
- }
- } else {
- fmt.Printf(" Parameter %d: %s\n", i+1, param)
- }
- }
- }
- // Main function for standalone testing
- func GetKplerDataByExcel() {
- //fmt.Println("Testing Kpler formula parsing...")
- // FormulaExample()
- fmt.Println("Starting Kpler data processing...")
- //ParseSpecificKplerFormulaV2()
- // First demonstrate the specific formula parsing
- // ParseSpecificKplerFormula()
- // Then process the Excel data
- err := ProcessKplerData()
- if err != nil {
- fmt.Println("error processing Excel data: %w", err)
- }
- fmt.Println("Kpler data processing completed successfully!")
- }
- func ParseSpecificKplerFormulaV2(specificFormula string) {
- specificFormula = strings.ReplaceAll(specificFormula, `"&"`, "")
- //specificFormula := `=Kpler.getFlows("%7B%22platform%22%3A%22liquids%22%2C%22origins%22%3A%5B%7B%22id%22%3A%22Angola%22%2C%22name%22%3A%22Angola%22%7D%5D%2C%22destinations%22%3A%5B%5D%2C%22fromInstallations%22%3A%5B%5D%2C%22toInstallations%22%3A%5B%5D%2C%22flowDirection%22%3A%22export%22%2C%2" & "2products%22%3A%5B%7B%22id%22%3A%22Crude%22%2C%22name%22%3A%22Crude%22%7D%5D%2C%22unit%22%3A%22kbd%22%2C%22isProductEstimation%22%3Afalse%2C%22isIntracountry%22%3Afalse%2C%22isIntraRegion%22%3Afalse%2C%22isWithForecast%22%3Afalse%2C%22granularity%22%3A%22" & "weeks%22%2C%22vesselClassification%22%3A%22CPP%22%2C%22vesselsTypes%22%3A%5B%5D%2C%22split%22%3A%22Total%22%2C%22isFreightView%22%3Afalse%2C%22isWithPeriodEndTime%22%3Atrue%2C%22projection%22%3A%22actual%22%2C%22selectedPreset%22%3A%223m%22%2C%22startDate" & "%22%3Anull%2C%22endDate%22%3Anull%7D")`
- // 吧函数的入参解析成json串,并转成结构体
- // 手动解码URL编码字符串以获取JSON
- // 这是完整的公式参数 - Excel中使用"&"连接字符串
- //encodedParam := `%7B%22platform%22%3A%22liquids%22%2C%22origins%22%3A%5B%7B%22id%22%3A%22Angola%22%2C%22name%22%3A%22Angola%22%7D%5D%2C%22destinations%22%3A%5B%5D%2C%22fromInstallations%22%3A%5B%5D%2C%22toInstallations%22%3A%5B%5D%2C%22flowDirection%22%3A%22export%22%2C%22products%22%3A%5B%7B%22id%22%3A%22Crude%22%2C%22name%22%3A%22Crude%22%7D%5D%2C%22unit%22%3A%22kbd%22%2C%22isProductEstimation%22%3Afalse%2C%22isIntracountry%22%3Afalse%2C%22isIntraRegion%22%3Afalse%2C%22isWithForecast%22%3Afalse%2C%22granularity%22%3A%22weeks%22%2C%22vesselClassification%22%3A%22CPP%22%2C%22vesselsTypes%22%3A%5B%5D%2C%22split%22%3A%22Total%22%2C%22isFreightView%22%3Afalse%2C%22isWithPeriodEndTime%22%3Atrue%2C%22projection%22%3A%22actual%22%2C%22selectedPreset%22%3A%223m%22%2C%22startDate%22%3Anull%2C%22endDate%22%3Anull%7D`
- // 获取括号里的内容
- re := regexp.MustCompile(`\((.*)\)`)
- matches := re.FindStringSubmatch(specificFormula)
- if len(matches) < 2 {
- fmt.Println("没有找到括号里的内容")
- return
- }
- encodedParam := matches[1]
- fmt.Println("encodedParam: ", encodedParam)
- // 解码URL编码的字符串
- decodedStr, err := url.QueryUnescape(encodedParam)
- if err != nil {
- fmt.Printf("Error decoding URL: %v\n", err)
- return
- } else {
- // 打印解码后的JSON字符串
- fmt.Println("Decoded parameter JSON:")
- fmt.Println(decodedStr)
-
- // 使解码后的JSON更易读
- var jsonObj interface{}
- if err := json.Unmarshal([]byte(decodedStr), &jsonObj); err == nil {
- prettyJSON, _ := json.MarshalIndent(jsonObj, "", " ")
- fmt.Println("\nPretty JSON format:")
- fmt.Println(string(prettyJSON))
- } else {
- fmt.Printf("Error parsing JSON: %v\n", err)
- return
- }
- }
- }
|