github.com/easysoft/zendata@v0.0.0-20240513203326-705bd5a7fd67/internal/pkg/service/article.go (about)

     1  package service
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"path/filepath"
     7  	"regexp"
     8  	"strings"
     9  	"time"
    10  
    11  	consts "github.com/easysoft/zendata/internal/pkg/const"
    12  	"github.com/easysoft/zendata/internal/pkg/domain"
    13  	commonUtils "github.com/easysoft/zendata/pkg/utils/common"
    14  	fileUtils "github.com/easysoft/zendata/pkg/utils/file"
    15  	i118Utils "github.com/easysoft/zendata/pkg/utils/i118"
    16  	logUtils "github.com/easysoft/zendata/pkg/utils/log"
    17  	stringUtils "github.com/easysoft/zendata/pkg/utils/string"
    18  	"github.com/easysoft/zendata/pkg/utils/vari"
    19  	"gopkg.in/yaml.v3"
    20  )
    21  
    22  var (
    23  	MaxLen           = 10
    24  	IgnoreWords      = []string{"了", "的"}
    25  	IgnoreCategories = []string{"姓", "名字", "介词"}
    26  )
    27  
    28  type ArticleService struct {
    29  	ResService *ResService `inject:""`
    30  }
    31  
    32  func (s *ArticleService) CreateArticleField(field *domain.DefField) {
    33  	values := make([]interface{}, 0)
    34  
    35  	numMap, nameMap, indexMap, contentWithoutComments := s.getNumMap(field.Range)
    36  	resFile, resType, sheet := fileUtils.GetResProp(field.From, "")
    37  	dataMap := s.getDataMap(numMap, nameMap, field, resFile, resType, sheet)
    38  
    39  	for i := 0; i < vari.GlobalVars.Total; i++ {
    40  		content := s.genArticle(contentWithoutComments, dataMap, indexMap) + "\n"
    41  		values = append(values, content)
    42  	}
    43  
    44  	field.Values = values
    45  }
    46  
    47  func (s *ArticleService) genArticle(content string, dataMap map[string][]interface{},
    48  	indexMap map[string]int) (ret string) {
    49  	ret = content
    50  
    51  	//for key, arr := range dataMap {
    52  	//	for _, item := range arr {
    53  	//		placeholder := fmt.Sprintf("{%s}", key)
    54  	//		ret = strings.Replace(ret, placeholder, item, 1)
    55  	//	}
    56  	//}
    57  
    58  	regx := regexp.MustCompile(`[\(\[\{]((?U).*)[\)\]\}]`)
    59  	arr := regx.FindAllStringSubmatch(content, -1)
    60  
    61  	for _, child := range arr {
    62  
    63  		slotStr := child[0]
    64  		slotName := child[1]
    65  
    66  		tag := slotStr[0]
    67  		var value interface{}
    68  
    69  		if string(tag) == "(" { // fixed
    70  			if indexMap[slotName] < 0 {
    71  				indexMap[slotName] = 0
    72  			}
    73  
    74  			mode := len(dataMap[slotName])
    75  			if mode == 0 {
    76  				mode = 1
    77  			}
    78  			index := indexMap[slotName] % mode
    79  
    80  			dt, ok := dataMap[slotName]
    81  			if ok && len(dt) > 0 {
    82  				value = dt[index]
    83  			}
    84  
    85  		} else if string(tag) == "[" { // seq
    86  			indexMap[slotName] = indexMap[slotName] + 1
    87  
    88  			mode := len(dataMap[slotName])
    89  			if mode == 0 {
    90  				mode = 1
    91  			}
    92  			index := indexMap[slotName] % mode
    93  
    94  			dt, ok := dataMap[slotName]
    95  			if ok && len(dt) > 0 {
    96  				value = dt[index]
    97  			}
    98  
    99  		} else if string(tag) == "{" { // random
   100  			mode := len(dataMap[slotName])
   101  			if mode == 0 {
   102  				mode = 1
   103  			}
   104  
   105  			dt, ok := dataMap[slotName]
   106  			if ok && len(dt) > 0 {
   107  				value = dt[commonUtils.RandNum(mode)]
   108  			}
   109  		}
   110  
   111  		if value == nil {
   112  			value = ""
   113  		}
   114  
   115  		ret = strings.Replace(ret, slotStr, value.(string), 1)
   116  	}
   117  
   118  	return
   119  }
   120  
   121  func (s *ArticleService) getDataMap(numMap map[string]int, nameMap map[string]string, field *domain.DefField,
   122  	resFile string, resType string, sheet string) (ret map[string][]interface{}) {
   123  	ret = map[string][]interface{}{}
   124  
   125  	field.Rand = false
   126  	for key := range numMap {
   127  		originTotal := vari.GlobalVars.Total
   128  		vari.GlobalVars.Total = consts.MaxNumb // load all words
   129  
   130  		slct, ok := nameMap[key]
   131  		if ok {
   132  			field.Select = slct
   133  		} else {
   134  			field.Select = key
   135  		}
   136  
   137  		valueMap, _ := s.ResService.GetResValueFromExcelOrYaml(resFile, resType, sheet, field)
   138  		ret[key] = valueMap[field.Select]
   139  
   140  		vari.GlobalVars.Total = originTotal // rollback
   141  	}
   142  
   143  	return
   144  }
   145  
   146  func (s *ArticleService) getNumMap(content string) (numMap map[string]int, nameMap map[string]string, indexMap map[string]int, contentWithoutComments string) {
   147  	numMap = map[string]int{}
   148  	nameMap = map[string]string{}
   149  	indexMap = map[string]int{}
   150  	arrWithoutComments := make([]string, 0)
   151  
   152  	content = strings.Trim(content, "`")
   153  	lines := strings.Split(content, "\n")
   154  
   155  	for _, line := range lines {
   156  		line = strings.TrimSpace(line)
   157  		if line == "" {
   158  			continue
   159  		}
   160  
   161  		if strings.Index(line, "#") == 0 {
   162  			line = strings.TrimLeft(line, "#")
   163  			arr := strings.Split(line, "=")
   164  			if len(arr) < 2 {
   165  				continue
   166  			}
   167  
   168  			leftArr := strings.Split(arr[0], " ")
   169  			vari := leftArr[len(leftArr)-1]
   170  			expr := strings.Split(arr[1], " ")[0]
   171  
   172  			nameMap[vari] = expr
   173  			continue
   174  		}
   175  
   176  		arrWithoutComments = append(arrWithoutComments, line)
   177  
   178  		regxSeq := regexp.MustCompile(`\[((?U).*)\]`)
   179  		arrSeq := regxSeq.FindAllStringSubmatch(line, -1)
   180  
   181  		regxRand := regexp.MustCompile(`\{((?U).*)\}`)
   182  		arrRand := regxRand.FindAllStringSubmatch(line, -1)
   183  
   184  		arr := append(arrSeq, arrRand...)
   185  
   186  		for _, child := range arr {
   187  			name := child[1]
   188  			i, ok := numMap[name]
   189  			if !ok {
   190  				numMap[name] = 1
   191  			} else {
   192  				numMap[name] = i + 1
   193  			}
   194  
   195  			_, ok2 := indexMap[name]
   196  			if !ok2 {
   197  				indexMap[name] = -1
   198  			}
   199  		}
   200  	}
   201  
   202  	contentWithoutComments = strings.Join(arrWithoutComments, "\n")
   203  
   204  	return
   205  }
   206  
   207  func (s *ArticleService) GenArticle(lines []interface{}) {
   208  	var filePath = logUtils.OutputFileWriter.Name()
   209  	defer logUtils.OutputFileWriter.Close()
   210  	fileUtils.RmFile(filePath)
   211  
   212  	for index, line := range lines {
   213  		articlePath := s.genArticleFiles(filePath, index)
   214  
   215  		fileWriter, _ := os.OpenFile(articlePath, os.O_RDWR|os.O_CREATE, 0777)
   216  
   217  		fmt.Fprint(fileWriter, line)
   218  		fileWriter.Close()
   219  	}
   220  }
   221  
   222  func (s *ArticleService) genArticleFiles(pth string, index int) (ret string) {
   223  	pfix := fmt.Sprintf("%03d", index+1)
   224  
   225  	ret = strings.TrimSuffix(pth, filepath.Ext(pth))
   226  	ret += "-" + pfix + filepath.Ext(pth)
   227  
   228  	return
   229  }
   230  
   231  func (s *ArticleService) GenYamlFromArticle(file string) {
   232  	startTime := time.Now().Unix()
   233  
   234  	content := fileUtils.ReadFile(file)
   235  	words := s.LoadAllWords()
   236  
   237  	templ := s.replaceWords(content, words)
   238  	yamlObj := domain.DefArticle{Type: "article", Content: templ, Author: "zendata",
   239  		From: "words.v1", Title: "Template", Version: "1.1"}
   240  	bytes, _ := yaml.Marshal(&yamlObj)
   241  	yamlStr := string(bytes)
   242  
   243  	outFile := ""
   244  	if vari.GlobalVars.Output != "" {
   245  		vari.GlobalVars.Output = fileUtils.AddSepIfNeeded(vari.GlobalVars.Output)
   246  		outFile = filepath.Join(vari.GlobalVars.Output, fileUtils.ChangeFileExt(filepath.Base(file), ".yaml"))
   247  		fileUtils.WriteFile(outFile, yamlStr)
   248  
   249  	} else {
   250  		logUtils.PrintTo(yamlStr)
   251  	}
   252  
   253  	entTime := time.Now().Unix()
   254  	logUtils.PrintTo(i118Utils.I118Prt.Sprintf("generate_article_templ", outFile, entTime-startTime))
   255  }
   256  
   257  func (s *ArticleService) replaceWords(content string, words map[string]string) (ret string) {
   258  	runeArr := []rune(content)
   259  	newRuneArr := make([]rune, 0)
   260  	lastUsedWordOfCategoryMap := map[string]string{}
   261  	for i := 0; i < len(runeArr); {
   262  		found := false
   263  		for j := MaxLen; j >= 0; j-- {
   264  			end := i + j
   265  			if end > len(runeArr) {
   266  				end = len(runeArr)
   267  			}
   268  
   269  			chars := runeArr[i:end]
   270  			str := ""
   271  			for _, char := range chars {
   272  				str += string(char)
   273  			}
   274  
   275  			val, ok := words[str]
   276  			if ok {
   277  				if str == "有" {
   278  					logUtils.PrintTo("")
   279  				}
   280  
   281  				lastOne, ok := lastUsedWordOfCategoryMap[val]
   282  
   283  				new := ""
   284  				if ok && lastOne == str {
   285  					new = "(" + val + ")"
   286  				} else {
   287  					new = "{" + val + "}"
   288  				}
   289  				itemArr := []rune(new)
   290  				newRuneArr = append(newRuneArr, itemArr...)
   291  
   292  				lastUsedWordOfCategoryMap[val] = str // update
   293  
   294  				i = end
   295  				found = true
   296  				break
   297  			}
   298  		}
   299  
   300  		if !found {
   301  			newRuneArr = append(newRuneArr, runeArr[i])
   302  
   303  			i++
   304  		}
   305  	}
   306  
   307  	ret = string(newRuneArr)
   308  
   309  	return
   310  }
   311  
   312  func (s *ArticleService) LoadAllWords() (ret map[string]string) {
   313  	ret = map[string]string{}
   314  
   315  	rows, _ := vari.DB.Table("words_v1").Where("true").Select("*").Rows()
   316  	defer rows.Close()
   317  
   318  	columns, err := rows.Columns()
   319  	colNum := len(columns)
   320  
   321  	colIndexToCategoryName := map[int]string{}
   322  	for index, col := range columns {
   323  		colIndexToCategoryName[index] = col
   324  	}
   325  
   326  	// build an empty string array to retrieve row
   327  	var record = make([]interface{}, colNum)
   328  	for i := range record {
   329  		var itf string
   330  		record[i] = &itf
   331  	}
   332  
   333  	for rows.Next() {
   334  		err = rows.Scan(record...)
   335  		if err != nil {
   336  			logUtils.PrintTo(i118Utils.I118Prt.Sprintf("fail_to_parse_row", err.Error()))
   337  			return
   338  		}
   339  
   340  		for index := len(record) - 1; index >= 0; index-- {
   341  			word := record[1].(*string)
   342  			category := colIndexToCategoryName[index]
   343  			isBelowToCategory := record[index].(*string)
   344  
   345  			if *isBelowToCategory == "y" {
   346  				if !stringUtils.StrInArr(category, IgnoreCategories) &&
   347  					!stringUtils.StrInArr(*word, IgnoreWords) {
   348  
   349  					ret[*word] = category
   350  				}
   351  
   352  				break
   353  			}
   354  		}
   355  	}
   356  
   357  	return
   358  }