/client/parse.go

https://gitlab.com/GZGavinZhao/cf-tool · Go · 155 lines · 137 code · 16 blank · 2 comment · 46 complexity · 465ae5abafdbee33e5b6b739423f1932 MD5 · raw file

  1. package client
  2. import (
  3. "bytes"
  4. "fmt"
  5. "html"
  6. "io/ioutil"
  7. "os"
  8. "path/filepath"
  9. "regexp"
  10. "strings"
  11. "sync"
  12. "gitlab.com/GZGavinZhao/cf-tool/util"
  13. "github.com/k0kubun/go-ansi"
  14. "github.com/fatih/color"
  15. )
  16. func findSample(body []byte) (input [][]byte, output [][]byte, err error) {
  17. irg := regexp.MustCompile(`class="input"[\s\S]*?<pre>([\s\S]*?)</pre>`)
  18. org := regexp.MustCompile(`class="output"[\s\S]*?<pre>([\s\S]*?)</pre>`)
  19. a := irg.FindAllSubmatch(body, -1)
  20. b := org.FindAllSubmatch(body, -1)
  21. if a == nil || b == nil || len(a) != len(b) {
  22. return nil, nil, fmt.Errorf("Cannot parse sample with input %v and output %v", len(a), len(b))
  23. }
  24. newline := regexp.MustCompile(`<[\s/br]+?>`)
  25. filter := func(src []byte) []byte {
  26. src = newline.ReplaceAll(src, []byte("\n"))
  27. s := html.UnescapeString(string(src))
  28. return []byte(strings.TrimSpace(s) + "\n")
  29. }
  30. for i := 0; i < len(a); i++ {
  31. input = append(input, filter(a[i][1]))
  32. output = append(output, filter(b[i][1]))
  33. }
  34. return
  35. }
  36. // ParseProblem parse problem to path. mu can be nil
  37. func (c *Client) ParseProblem(URL, path string, mu *sync.Mutex) (samples int, standardIO bool, err error) {
  38. body, err := util.GetBody(c.client, URL)
  39. if err != nil {
  40. return
  41. }
  42. _, err = findHandle(body)
  43. if err != nil {
  44. return
  45. }
  46. input, output, err := findSample(body)
  47. if err != nil {
  48. return
  49. }
  50. standardIO = true
  51. if !bytes.Contains(body, []byte(`<div class="input-file"><div class="property-title">input</div>standard input</div><div class="output-file"><div class="property-title">output</div>standard output</div>`)) {
  52. standardIO = false
  53. }
  54. for i := 0; i < len(input); i++ {
  55. fileIn := filepath.Join(path, fmt.Sprintf("in%v.txt", i+1))
  56. fileOut := filepath.Join(path, fmt.Sprintf("ans%v.txt", i+1))
  57. e := ioutil.WriteFile(fileIn, input[i], 0644)
  58. if e != nil {
  59. if mu != nil {
  60. mu.Lock()
  61. }
  62. color.Red(e.Error())
  63. if mu != nil {
  64. mu.Unlock()
  65. }
  66. }
  67. e = ioutil.WriteFile(fileOut, output[i], 0644)
  68. if e != nil {
  69. if mu != nil {
  70. mu.Lock()
  71. }
  72. color.Red(e.Error())
  73. if mu != nil {
  74. mu.Unlock()
  75. }
  76. }
  77. }
  78. return len(input), standardIO, nil
  79. }
  80. // Parse parse
  81. func (c *Client) Parse(info Info) (problems []string, paths []string, err error) {
  82. color.Cyan("Parse " + info.Hint())
  83. problemID := info.ProblemID
  84. info.ProblemID = "%v"
  85. urlFormatter, err := info.ProblemURL(c.host)
  86. if err != nil {
  87. return
  88. }
  89. info.ProblemID = ""
  90. if problemID == "" {
  91. statics, err := c.Statis(info)
  92. if err != nil {
  93. return nil, nil, err
  94. }
  95. problems = make([]string, len(statics))
  96. for i, problem := range statics {
  97. problems[i] = problem.ID
  98. }
  99. } else {
  100. problems = []string{problemID}
  101. }
  102. contestPath := info.Path()
  103. ansi.Printf(color.CyanString("The problem(s) will be saved to %v\n"), color.GreenString(contestPath))
  104. wg := sync.WaitGroup{}
  105. wg.Add(len(problems))
  106. mu := sync.Mutex{}
  107. paths = make([]string, len(problems))
  108. for i, problemID := range problems {
  109. paths[i] = filepath.Join(contestPath, strings.ToLower(problemID))
  110. go func(problemID, path string) {
  111. defer wg.Done()
  112. mu.Lock()
  113. fmt.Printf("Parsing %v\n", problemID)
  114. mu.Unlock()
  115. err = os.MkdirAll(path, os.ModePerm)
  116. if err != nil {
  117. return
  118. }
  119. URL := fmt.Sprintf(urlFormatter, problemID)
  120. samples, standardIO, err := c.ParseProblem(URL, path, &mu)
  121. if err != nil {
  122. return
  123. }
  124. warns := ""
  125. if !standardIO {
  126. warns = color.YellowString("Non standard input output format.")
  127. }
  128. mu.Lock()
  129. if err != nil {
  130. color.Red("Failed %v. Error: %v", problemID, err.Error())
  131. } else {
  132. ansi.Printf("%v %v\n", color.GreenString("Parsed %v with %v samples.", problemID, samples), warns)
  133. }
  134. mu.Unlock()
  135. }(problemID, paths[i])
  136. }
  137. wg.Wait()
  138. return
  139. }