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 }