mirror of
https://github.com/halfrost/LeetCode-Go.git
synced 2025-07-06 17:44:10 +08:00
Add person data statistic
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
*.toml
|
@ -1,43 +0,0 @@
|
||||
package models
|
||||
|
||||
type LeetCodeProblemAll struct {
|
||||
UserName string `json:"user_name"`
|
||||
NumSolved int32 `json:"num_solved"`
|
||||
NumTotal int32 `json:"num_total"`
|
||||
AcEasy int32 `json:"ac_easy"`
|
||||
AcMedium int32 `json:"ac_medium"`
|
||||
AcHard int32 `json:"ac_hard"`
|
||||
StatStatusPairs []StatStatusPairs `json:"stat_status_pairs"`
|
||||
FrequencyHigh int32 `json:"frequency_high"`
|
||||
FrequencyMid int32 `json:"frequency_mid"`
|
||||
CategorySlug string `json:"category_slug"`
|
||||
}
|
||||
|
||||
type StatStatusPairs struct {
|
||||
Stat Stat `json:"stat"`
|
||||
Difficulty Difficulty `json:"difficulty"`
|
||||
PaidOnly bool `json:"paid_only"`
|
||||
IsFavor bool `json:"is_favor"`
|
||||
Frequency float64 `json:"frequency"`
|
||||
Progress float64 `json:"progress"`
|
||||
}
|
||||
|
||||
type Stat struct {
|
||||
QuestionTitle string `json:"question__title"`
|
||||
QuestionTitleSlug string `json:"question__title_slug"`
|
||||
TotalAcs float64 `json:"total_acs"`
|
||||
TotalSubmitted float64 `json:"total_submitted"`
|
||||
Acceptance string
|
||||
Difficulty string
|
||||
FrontendQuestionId int32 `json:"frontend_question_id"`
|
||||
}
|
||||
|
||||
type Difficulty struct {
|
||||
Level int32 `json:"level"`
|
||||
}
|
||||
|
||||
var DifficultyMap = map[int32]string{
|
||||
1: "Easy",
|
||||
2: "Medium",
|
||||
3: "Hard",
|
||||
}
|
@ -1,190 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
m "github.com/halfrost/LeetCode-Go/automation/models"
|
||||
"html/template"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var try int
|
||||
|
||||
func main() {
|
||||
var (
|
||||
result []m.StatStatusPairs
|
||||
lpa m.LeetCodeProblemAll
|
||||
)
|
||||
|
||||
body := getProblemAllList()
|
||||
err := json.Unmarshal(body, &lpa)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
result = lpa.StatStatusPairs
|
||||
mdrows := []m.Mdrow{}
|
||||
for i := 0; i < len(result); i++ {
|
||||
mdrows = append(mdrows, convertModel(result[i]))
|
||||
}
|
||||
sort.Sort(m.SortByQuestionId(mdrows))
|
||||
solutionIds := loadSolutionsDir()
|
||||
generateMdRows(solutionIds, mdrows)
|
||||
// res, _ := json.Marshal(mdrows)
|
||||
//writeFile("leetcode_problem", res)
|
||||
mds := m.Mdrows{Mdrows: mdrows}
|
||||
res, err := readFile("./template.markdown", "{{.AvailableTable}}", len(solutionIds), try, mds)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
writeFile("../README.md", res)
|
||||
//makeReadmeFile(mds)
|
||||
}
|
||||
|
||||
func getProblemAllList() []byte {
|
||||
resp, err := http.Get("https://leetcode.com/api/problems/all/")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return []byte{}
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return []byte{}
|
||||
}
|
||||
if resp.StatusCode == 200 {
|
||||
fmt.Println("ok")
|
||||
}
|
||||
return body
|
||||
}
|
||||
|
||||
func writeFile(fileName string, content []byte) {
|
||||
file, err := os.OpenFile(fileName, os.O_RDWR|os.O_CREATE, 0777)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
_, err = file.Write(content)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
fmt.Println("write file successful")
|
||||
}
|
||||
|
||||
func convertModel(ssp m.StatStatusPairs) m.Mdrow {
|
||||
res := m.Mdrow{}
|
||||
res.FrontendQuestionId = ssp.Stat.FrontendQuestionId
|
||||
res.QuestionTitle = ssp.Stat.QuestionTitle
|
||||
res.QuestionTitleSlug = ssp.Stat.QuestionTitleSlug
|
||||
res.Acceptance = fmt.Sprintf("%.1f%%", (ssp.Stat.TotalAcs/ssp.Stat.TotalSubmitted)*100)
|
||||
res.Difficulty = m.DifficultyMap[ssp.Difficulty.Level]
|
||||
res.Frequency = fmt.Sprintf("%f", ssp.Frequency)
|
||||
return res
|
||||
}
|
||||
|
||||
func loadSolutionsDir() []int {
|
||||
files, err := ioutil.ReadDir("../leetcode/")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
solutionIds := []int{}
|
||||
for _, f := range files {
|
||||
if f.Name()[4] == '.' {
|
||||
tmp, err := strconv.Atoi(f.Name()[:4])
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
solutionIds = append(solutionIds, tmp)
|
||||
}
|
||||
}
|
||||
sort.Ints(solutionIds)
|
||||
try = len(files) - len(solutionIds)
|
||||
fmt.Printf("读取了 %v 道题的题解,当前目录下有 %v 个文件(可能包含 .DS_Store),有 %v 道题在尝试中\n", len(solutionIds), len(files), len(files)-len(solutionIds))
|
||||
return solutionIds
|
||||
}
|
||||
|
||||
func generateMdRows(solutionIds []int, mdrows []m.Mdrow) {
|
||||
for i := 0; i < len(solutionIds); i++ {
|
||||
id := mdrows[solutionIds[i]-1].FrontendQuestionId
|
||||
if solutionIds[i] == int(id) {
|
||||
//fmt.Printf("id = %v i = %v solutionIds = %v\n", id, i, solutionIds[i])
|
||||
mdrows[id-1].SolutionPath = fmt.Sprintf("[Go](https://github.com/halfrost/LeetCode-Go/tree/master/leetcode/%v)", fmt.Sprintf("%04d.%v", id, strings.Replace(mdrows[id-1].QuestionTitle, " ", "-", -1)))
|
||||
} else {
|
||||
fmt.Printf("序号出错了 solutionIds = %v id = %v\n", solutionIds[i], id)
|
||||
}
|
||||
}
|
||||
fmt.Printf("")
|
||||
}
|
||||
|
||||
func makeReadmeFile(mdrows m.Mdrows) {
|
||||
file := "./README.md"
|
||||
os.Remove(file)
|
||||
var b bytes.Buffer
|
||||
tmpl := template.Must(template.New("readme").Parse(readTMPL("template.markdown")))
|
||||
err := tmpl.Execute(&b, mdrows)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
// 保存 README.md 文件
|
||||
writeFile(file, b.Bytes())
|
||||
}
|
||||
|
||||
func readTMPL(path string) string {
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
data, err := ioutil.ReadAll(file)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
return string(data)
|
||||
}
|
||||
|
||||
func readFile(filePath, template string, total, try int, mdrows m.Mdrows) ([]byte, error) {
|
||||
f, err := os.OpenFile(filePath, os.O_RDONLY, 0644)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
reader, output := bufio.NewReader(f), []byte{}
|
||||
|
||||
for {
|
||||
line, _, err := reader.ReadLine()
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
return output, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
if ok, _ := regexp.Match(template, line); ok {
|
||||
reg := regexp.MustCompile(template)
|
||||
newByte := reg.ReplaceAll(line, []byte(mdrows.AvailableTable()))
|
||||
output = append(output, newByte...)
|
||||
output = append(output, []byte("\n")...)
|
||||
} else if ok, _ := regexp.Match("{{.TotalNum}}", line); ok {
|
||||
reg := regexp.MustCompile("{{.TotalNum}}")
|
||||
newByte := reg.ReplaceAll(line, []byte(fmt.Sprintf("以下已经收录了 %v 道题的题解,还有 %v 道题在尝试优化到 beats 100%%", total, try)))
|
||||
output = append(output, newByte...)
|
||||
output = append(output, []byte("\n")...)
|
||||
} else {
|
||||
output = append(output, line...)
|
||||
output = append(output, []byte("\n")...)
|
||||
}
|
||||
}
|
||||
}
|
31
ctl/config.go
Normal file
31
ctl/config.go
Normal file
@ -0,0 +1,31 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
)
|
||||
|
||||
const (
|
||||
configTOML = "config.toml"
|
||||
)
|
||||
|
||||
type config struct {
|
||||
Username string
|
||||
Password string
|
||||
Cookie string
|
||||
}
|
||||
|
||||
func (c config) String() string {
|
||||
return fmt.Sprintf("Username: %s, Password: %s", c.Username, c.Password)
|
||||
}
|
||||
|
||||
func getConfig() *config {
|
||||
cfg := new(config)
|
||||
if _, err := toml.DecodeFile(configTOML, &cfg); err != nil {
|
||||
log.Panicf(err.Error())
|
||||
}
|
||||
// log.Printf("get config: %s", cfg)
|
||||
return cfg
|
||||
}
|
103
ctl/models/lcproblems.go
Normal file
103
ctl/models/lcproblems.go
Normal file
@ -0,0 +1,103 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// LeetCodeProblemAll define
|
||||
type LeetCodeProblemAll struct {
|
||||
UserName string `json:"user_name"`
|
||||
NumSolved int32 `json:"num_solved"`
|
||||
NumTotal int32 `json:"num_total"`
|
||||
AcEasy int32 `json:"ac_easy"`
|
||||
AcMedium int32 `json:"ac_medium"`
|
||||
AcHard int32 `json:"ac_hard"`
|
||||
StatStatusPairs []StatStatusPairs `json:"stat_status_pairs"`
|
||||
FrequencyHigh int32 `json:"frequency_high"`
|
||||
FrequencyMid int32 `json:"frequency_mid"`
|
||||
CategorySlug string `json:"category_slug"`
|
||||
AcEasyTotal int32
|
||||
AcMediumTotal int32
|
||||
AcHardTotal int32
|
||||
}
|
||||
|
||||
// ConvertUserInfoModel define
|
||||
func ConvertUserInfoModel(lpa LeetCodeProblemAll) UserInfo {
|
||||
info := UserInfo{}
|
||||
info.UserName = lpa.UserName
|
||||
info.NumSolved = lpa.NumSolved
|
||||
info.NumTotal = lpa.NumTotal
|
||||
info.AcEasy = lpa.AcEasy
|
||||
info.AcMedium = lpa.AcMedium
|
||||
info.AcHard = lpa.AcHard
|
||||
info.FrequencyHigh = lpa.FrequencyHigh
|
||||
info.FrequencyMid = lpa.FrequencyMid
|
||||
info.CategorySlug = lpa.CategorySlug
|
||||
return info
|
||||
}
|
||||
|
||||
// StatStatusPairs define
|
||||
type StatStatusPairs struct {
|
||||
Stat Stat `json:"stat"`
|
||||
Status string `json:"status"`
|
||||
Difficulty Difficulty `json:"difficulty"`
|
||||
PaidOnly bool `json:"paid_only"`
|
||||
IsFavor bool `json:"is_favor"`
|
||||
Frequency float64 `json:"frequency"`
|
||||
Progress float64 `json:"progress"`
|
||||
}
|
||||
|
||||
// ConvertMdModel define
|
||||
func ConvertMdModel(problems []StatStatusPairs) []Mdrow {
|
||||
mdrows := []Mdrow{}
|
||||
for _, problem := range problems {
|
||||
res := Mdrow{}
|
||||
res.FrontendQuestionID = problem.Stat.FrontendQuestionID
|
||||
res.QuestionTitle = problem.Stat.QuestionTitle
|
||||
res.QuestionTitleSlug = problem.Stat.QuestionTitleSlug
|
||||
res.Acceptance = fmt.Sprintf("%.1f%%", (problem.Stat.TotalAcs/problem.Stat.TotalSubmitted)*100)
|
||||
res.Difficulty = DifficultyMap[problem.Difficulty.Level]
|
||||
res.Frequency = fmt.Sprintf("%f", problem.Frequency)
|
||||
mdrows = append(mdrows, res)
|
||||
}
|
||||
return mdrows
|
||||
}
|
||||
|
||||
// ConvertMdModelFromIds define
|
||||
func ConvertMdModelFromIds(problemsMap map[int]StatStatusPairs, ids []int) []Mdrow {
|
||||
mdrows := []Mdrow{}
|
||||
for _, v := range ids {
|
||||
res, problem := Mdrow{}, problemsMap[v]
|
||||
res.FrontendQuestionID = problem.Stat.FrontendQuestionID
|
||||
res.QuestionTitle = problem.Stat.QuestionTitle
|
||||
res.QuestionTitleSlug = problem.Stat.QuestionTitleSlug
|
||||
res.Acceptance = fmt.Sprintf("%.1f%%", (problem.Stat.TotalAcs/problem.Stat.TotalSubmitted)*100)
|
||||
res.Difficulty = DifficultyMap[problem.Difficulty.Level]
|
||||
res.Frequency = fmt.Sprintf("%f", problem.Frequency)
|
||||
mdrows = append(mdrows, res)
|
||||
}
|
||||
return mdrows
|
||||
}
|
||||
|
||||
// Stat define
|
||||
type Stat struct {
|
||||
QuestionTitle string `json:"question__title"`
|
||||
QuestionTitleSlug string `json:"question__title_slug"`
|
||||
TotalAcs float64 `json:"total_acs"`
|
||||
TotalSubmitted float64 `json:"total_submitted"`
|
||||
Acceptance string
|
||||
Difficulty string
|
||||
FrontendQuestionID int32 `json:"frontend_question_id"`
|
||||
}
|
||||
|
||||
// Difficulty define
|
||||
type Difficulty struct {
|
||||
Level int32 `json:"level"`
|
||||
}
|
||||
|
||||
// DifficultyMap define
|
||||
var DifficultyMap = map[int32]string{
|
||||
1: "Easy",
|
||||
2: "Medium",
|
||||
3: "Hard",
|
||||
}
|
@ -4,8 +4,9 @@ import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Mdrow define
|
||||
type Mdrow struct {
|
||||
FrontendQuestionId int32 `json:"question_id"`
|
||||
FrontendQuestionID int32 `json:"question_id"`
|
||||
QuestionTitle string `json:"question__title"`
|
||||
QuestionTitleSlug string `json:"question__title_slug"`
|
||||
SolutionPath string `json:"solution_path"`
|
||||
@ -16,18 +17,19 @@ type Mdrow struct {
|
||||
|
||||
// | 0001 | Two Sum | [Go](https://github.com/halfrost/LeetCode-Go/tree/master/leetcode/0001.Two-Sum)| 45.6% | Easy | |
|
||||
func (m Mdrow) tableLine() string {
|
||||
return fmt.Sprintf("|%04d|%v|%v|%v|%v||\n", m.FrontendQuestionId, m.QuestionTitle, m.SolutionPath, m.Acceptance, m.Difficulty)
|
||||
return fmt.Sprintf("|%04d|%v|%v|%v|%v||\n", m.FrontendQuestionID, m.QuestionTitle, m.SolutionPath, m.Acceptance, m.Difficulty)
|
||||
}
|
||||
|
||||
// SortByQuestionId define
|
||||
type SortByQuestionId []Mdrow
|
||||
// SortByQuestionID define
|
||||
type SortByQuestionID []Mdrow
|
||||
|
||||
func (a SortByQuestionId) Len() int { return len(a) }
|
||||
func (a SortByQuestionId) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
func (a SortByQuestionId) Less(i, j int) bool {
|
||||
return a[i].FrontendQuestionId < a[j].FrontendQuestionId
|
||||
func (a SortByQuestionID) Len() int { return len(a) }
|
||||
func (a SortByQuestionID) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
func (a SortByQuestionID) Less(i, j int) bool {
|
||||
return a[i].FrontendQuestionID < a[j].FrontendQuestionID
|
||||
}
|
||||
|
||||
// Mdrows define
|
||||
type Mdrows struct {
|
||||
Mdrows []Mdrow
|
||||
}
|
||||
@ -45,6 +47,7 @@ func (mds Mdrows) table() string {
|
||||
return res
|
||||
}
|
||||
|
||||
// AvailableTable define
|
||||
func (mds Mdrows) AvailableTable() string {
|
||||
return mds.table()
|
||||
}
|
44
ctl/models/user.go
Normal file
44
ctl/models/user.go
Normal file
@ -0,0 +1,44 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// UserInfo define
|
||||
type UserInfo struct {
|
||||
UserName string `json:"user_name"`
|
||||
NumSolved int32 `json:"num_solved"`
|
||||
NumTotal int32 `json:"num_total"`
|
||||
AcEasy int32 `json:"ac_easy"`
|
||||
AcMedium int32 `json:"ac_medium"`
|
||||
AcHard int32 `json:"ac_hard"`
|
||||
EasyTotal int32
|
||||
MediumTotal int32
|
||||
HardTotal int32
|
||||
OptimizingEasy int32
|
||||
OptimizingMedium int32
|
||||
OptimizingHard int32
|
||||
FrequencyHigh int32 `json:"frequency_high"`
|
||||
FrequencyMid int32 `json:"frequency_mid"`
|
||||
CategorySlug string `json:"category_slug"`
|
||||
}
|
||||
|
||||
// | | Easy | Medium | Hard | Total | optimizing |
|
||||
// |:--------:|:--------------------------------------------------------------|:--------:|:--------:|:--------:|:--------:|
|
||||
func (ui UserInfo) table() string {
|
||||
res := "| | Easy | Medium | Hard | Total |\n"
|
||||
res += "|:--------:|:--------:|:--------:|:--------:|:--------:|\n"
|
||||
res += fmt.Sprintf("|Optimizing|%v|%v|%v|%v|\n", ui.OptimizingEasy, ui.OptimizingMedium, ui.OptimizingHard, ui.OptimizingEasy+ui.OptimizingMedium+ui.OptimizingHard)
|
||||
res += fmt.Sprintf("|Accepted|**%v**|**%v**|**%v**|**%v**|\n", ui.AcEasy, ui.AcMedium, ui.AcHard, ui.AcEasy+ui.AcMedium+ui.AcHard)
|
||||
res += fmt.Sprintf("|Total|%v|%v|%v|%v|\n", ui.EasyTotal, ui.MediumTotal, ui.HardTotal, ui.EasyTotal+ui.MediumTotal+ui.HardTotal)
|
||||
res += fmt.Sprintf("|Perfection Rate|%.1f%%|%.1f%%|%.1f%%|%.1f%%|\n", (1-float64(ui.OptimizingEasy)/float64(ui.AcEasy))*100, (1-float64(ui.OptimizingMedium)/float64(ui.AcMedium))*100, (1-float64(ui.OptimizingHard)/float64(ui.AcHard))*100, (1-float64(ui.OptimizingEasy+ui.OptimizingMedium+ui.OptimizingHard)/float64(ui.AcEasy+ui.AcMedium+ui.AcHard))*100)
|
||||
res += fmt.Sprintf("|Completion Rate|%.1f%%|%.1f%%|%.1f%%|%.1f%%|\n", float64(ui.AcEasy)/float64(ui.EasyTotal)*100, float64(ui.AcMedium)/float64(ui.MediumTotal)*100, float64(ui.AcHard)/float64(ui.HardTotal)*100, float64(ui.AcEasy+ui.AcMedium+ui.AcHard)/float64(ui.EasyTotal+ui.MediumTotal+ui.HardTotal)*100)
|
||||
// 加这一行是为了撑开整个表格
|
||||
res += "|------------|----------------------------|----------------------------|----------------------------|----------------------------|"
|
||||
return res
|
||||
}
|
||||
|
||||
// PersonalData define
|
||||
func (ui UserInfo) PersonalData() string {
|
||||
return ui.table()
|
||||
}
|
36
ctl/rangking.go
Normal file
36
ctl/rangking.go
Normal file
@ -0,0 +1,36 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// getRanking 让这个方法优雅一点
|
||||
func getRanking() int {
|
||||
// 获取网页数据
|
||||
URL := fmt.Sprintf("https://leetcode.com/%s/", getConfig().Username)
|
||||
data := getRaw(URL)
|
||||
str := string(data)
|
||||
// 通过不断裁剪 str 获取排名信息
|
||||
fmt.Println(str)
|
||||
i := strings.Index(str, "ng-init")
|
||||
j := i + strings.Index(str[i:], "ng-cloak")
|
||||
str = str[i:j]
|
||||
i = strings.Index(str, "(")
|
||||
j = strings.Index(str, ")")
|
||||
str = str[i:j]
|
||||
// fmt.Println("2\n", str)
|
||||
strs := strings.Split(str, ",")
|
||||
str = strs[6]
|
||||
// fmt.Println("1\n", str)
|
||||
i = strings.Index(str, "'")
|
||||
j = 2 + strings.Index(str[2:], "'")
|
||||
// fmt.Println("0\n", str)
|
||||
str = str[i+1 : j]
|
||||
r, err := strconv.Atoi(str)
|
||||
if err != nil {
|
||||
fmt.Printf("无法把 %s 转换成数字Ranking", str)
|
||||
}
|
||||
return r
|
||||
}
|
108
ctl/render.go
Normal file
108
ctl/render.go
Normal file
@ -0,0 +1,108 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
m "github.com/halfrost/LeetCode-Go/ctl/models"
|
||||
"io"
|
||||
"os"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var (
|
||||
problems []m.StatStatusPairs
|
||||
lpa m.LeetCodeProblemAll
|
||||
info m.UserInfo
|
||||
)
|
||||
// 请求所有题目信息
|
||||
body := getProblemAllList(AllProblemURL)
|
||||
problemsMap, optimizingIds := map[int]m.StatStatusPairs{}, []int{}
|
||||
err := json.Unmarshal(body, &lpa)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
//writeFile("leetcode_problem.json", body)
|
||||
|
||||
// 拼凑 README 需要渲染的数据
|
||||
problems = lpa.StatStatusPairs
|
||||
info = m.ConvertUserInfoModel(lpa)
|
||||
for _, v := range problems {
|
||||
problemsMap[int(v.Stat.FrontendQuestionID)] = v
|
||||
}
|
||||
mdrows := m.ConvertMdModel(problems)
|
||||
sort.Sort(m.SortByQuestionID(mdrows))
|
||||
solutionIds, try := loadSolutionsDir()
|
||||
generateMdRows(solutionIds, mdrows)
|
||||
info.EasyTotal, info.MediumTotal, info.HardTotal, info.OptimizingEasy, info.OptimizingMedium, info.OptimizingHard, optimizingIds = statisticalData(problemsMap, solutionIds)
|
||||
omdrows := m.ConvertMdModelFromIds(problemsMap, optimizingIds)
|
||||
sort.Sort(m.SortByQuestionID(omdrows))
|
||||
|
||||
// 按照模板渲染 README
|
||||
res, err := renderReadme("./template.markdown", len(solutionIds), try, m.Mdrows{Mdrows: mdrows}, m.Mdrows{Mdrows: omdrows}, info)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
writeFile("../README.md", res)
|
||||
//makeReadmeFile(mds)
|
||||
}
|
||||
|
||||
func generateMdRows(solutionIds []int, mdrows []m.Mdrow) {
|
||||
for i := 0; i < len(solutionIds); i++ {
|
||||
id := mdrows[solutionIds[i]-1].FrontendQuestionID
|
||||
if solutionIds[i] == int(id) {
|
||||
//fmt.Printf("id = %v i = %v solutionIds = %v\n", id, i, solutionIds[i])
|
||||
mdrows[id-1].SolutionPath = fmt.Sprintf("[Go](https://github.com/halfrost/LeetCode-Go/tree/master/leetcode/%v)", fmt.Sprintf("%04d.%v", id, strings.Replace(mdrows[id-1].QuestionTitle, " ", "-", -1)))
|
||||
} else {
|
||||
fmt.Printf("序号出错了 solutionIds = %v id = %v\n", solutionIds[i], id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func renderReadme(filePath string, total, try int, mdrows, omdrows m.Mdrows, user m.UserInfo) ([]byte, error) {
|
||||
f, err := os.OpenFile(filePath, os.O_RDONLY, 0644)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
reader, output := bufio.NewReader(f), []byte{}
|
||||
|
||||
for {
|
||||
line, _, err := reader.ReadLine()
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
return output, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
if ok, _ := regexp.Match("{{.AvailableTable}}", line); ok {
|
||||
reg := regexp.MustCompile("{{.AvailableTable}}")
|
||||
newByte := reg.ReplaceAll(line, []byte(mdrows.AvailableTable()))
|
||||
output = append(output, newByte...)
|
||||
output = append(output, []byte("\n")...)
|
||||
} else if ok, _ := regexp.Match("{{.TotalNum}}", line); ok {
|
||||
reg := regexp.MustCompile("{{.TotalNum}}")
|
||||
newByte := reg.ReplaceAll(line, []byte(fmt.Sprintf("以下已经收录了 %v 道题的题解,还有 %v 道题在尝试优化到 beats 100%%", total, try)))
|
||||
output = append(output, newByte...)
|
||||
output = append(output, []byte("\n")...)
|
||||
} else if ok, _ := regexp.Match("{{.PersonalData}}", line); ok {
|
||||
reg := regexp.MustCompile("{{.PersonalData}}")
|
||||
newByte := reg.ReplaceAll(line, []byte(user.PersonalData()))
|
||||
output = append(output, newByte...)
|
||||
output = append(output, []byte("\n")...)
|
||||
} else if ok, _ := regexp.Match("{{.OptimizingTable}}", line); ok {
|
||||
reg := regexp.MustCompile("{{.OptimizingTable}}")
|
||||
newByte := reg.ReplaceAll(line, []byte(fmt.Sprintf("以下 %v 道题还需要优化到 100%% 的题目列表\n\n%v", (user.OptimizingEasy+user.OptimizingMedium+user.OptimizingHard), omdrows.AvailableTable())))
|
||||
output = append(output, newByte...)
|
||||
output = append(output, []byte("\n")...)
|
||||
} else {
|
||||
output = append(output, line...)
|
||||
output = append(output, []byte("\n")...)
|
||||
}
|
||||
}
|
||||
}
|
72
ctl/request.go
Normal file
72
ctl/request.go
Normal file
@ -0,0 +1,72 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/mozillazg/request"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
const (
|
||||
// AllProblemURL define
|
||||
AllProblemURL = "https://leetcode.com/api/problems/all/"
|
||||
// LoginPageURL define
|
||||
LoginPageURL = "https://leetcode.com/accounts/login/"
|
||||
// AlgorithmsURL define
|
||||
AlgorithmsURL = "https://leetcode.com/api/problems/Algorithms/"
|
||||
)
|
||||
|
||||
var req *request.Request
|
||||
|
||||
func newReq() *request.Request {
|
||||
if req == nil {
|
||||
req = signin()
|
||||
}
|
||||
return req
|
||||
}
|
||||
|
||||
func signin() *request.Request {
|
||||
cfg := getConfig()
|
||||
req := request.NewRequest(new(http.Client))
|
||||
req.Headers = map[string]string{
|
||||
"Content-Type": "application/json",
|
||||
"Accept-Encoding": "",
|
||||
"cookie": cfg.Cookie,
|
||||
"Referer": "https://leetcode.com/accounts/login/",
|
||||
"origin": "https://leetcode.com",
|
||||
}
|
||||
return req
|
||||
}
|
||||
|
||||
func getRaw(URL string) []byte {
|
||||
req := newReq()
|
||||
resp, err := req.Get(URL)
|
||||
if err != nil {
|
||||
fmt.Printf("getRaw: Get Error: " + err.Error())
|
||||
}
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
fmt.Printf("getRaw: Read Error: " + err.Error())
|
||||
}
|
||||
return body
|
||||
}
|
||||
|
||||
func getProblemAllList(URL string) []byte {
|
||||
req := newReq()
|
||||
resp, err := req.Get(URL)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return []byte{}
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return []byte{}
|
||||
}
|
||||
if resp.StatusCode == 200 {
|
||||
fmt.Println("ok")
|
||||
}
|
||||
return body
|
||||
}
|
40
ctl/statistic.go
Normal file
40
ctl/statistic.go
Normal file
@ -0,0 +1,40 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
m "github.com/halfrost/LeetCode-Go/ctl/models"
|
||||
"sort"
|
||||
)
|
||||
|
||||
func statisticalData(problemsMap map[int]m.StatStatusPairs, solutionIds []int) (easyTotal, mediumTotal, hardTotal, optimizingEasy, optimizingMedium, optimizingHard int32, optimizingIds []int) {
|
||||
easyTotal, mediumTotal, hardTotal, optimizingEasy, optimizingMedium, optimizingHard, optimizingIds = 0, 0, 0, 0, 0, 0, []int{}
|
||||
for _, v := range problemsMap {
|
||||
switch m.DifficultyMap[v.Difficulty.Level] {
|
||||
case "Easy":
|
||||
{
|
||||
easyTotal++
|
||||
if v.Status == "ac" && binarySearch(solutionIds, int(v.Stat.FrontendQuestionID)) == -1 {
|
||||
optimizingEasy++
|
||||
optimizingIds = append(optimizingIds, int(v.Stat.FrontendQuestionID))
|
||||
}
|
||||
}
|
||||
case "Medium":
|
||||
{
|
||||
mediumTotal++
|
||||
if v.Status == "ac" && binarySearch(solutionIds, int(v.Stat.FrontendQuestionID)) == -1 {
|
||||
optimizingMedium++
|
||||
optimizingIds = append(optimizingIds, int(v.Stat.FrontendQuestionID))
|
||||
}
|
||||
}
|
||||
case "Hard":
|
||||
{
|
||||
hardTotal++
|
||||
if v.Status == "ac" && binarySearch(solutionIds, int(v.Stat.FrontendQuestionID)) == -1 {
|
||||
optimizingHard++
|
||||
optimizingIds = append(optimizingIds, int(v.Stat.FrontendQuestionID))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
sort.Ints(optimizingIds)
|
||||
return easyTotal, mediumTotal, hardTotal, optimizingEasy, optimizingMedium, optimizingHard, optimizingIds
|
||||
}
|
@ -82,6 +82,9 @@
|
||||
* [✅ Segment Tree](#segment-tree)
|
||||
* [✅ Binary Indexed Tree](#binary-indexed-tree)
|
||||
|
||||
<br>
|
||||
<br>
|
||||
|
||||
| 数据结构 | 变种 | 相关题目 | 讲解文章 |
|
||||
|:-------:|:-------|:------|:------|
|
||||
|顺序线性表:向量||||
|
||||
@ -118,7 +121,11 @@
|
||||
|
||||
## LeetCode Problems
|
||||
|
||||
## 一. 目录
|
||||
## 一. 个人数据
|
||||
|
||||
{{.PersonalData}}
|
||||
|
||||
## 二. 目录
|
||||
|
||||
{{.TotalNum}}
|
||||
|
||||
@ -133,7 +140,7 @@
|
||||
------------------------------------------------------------------
|
||||
|
||||
|
||||
## 二.分类
|
||||
## 三.分类
|
||||
|
||||
## Array
|
||||
|
37
ctl/template_render.go
Normal file
37
ctl/template_render.go
Normal file
@ -0,0 +1,37 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
m "github.com/halfrost/LeetCode-Go/ctl/models"
|
||||
"html/template"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
)
|
||||
|
||||
func makeReadmeFile(mdrows m.Mdrows) {
|
||||
file := "./README.md"
|
||||
os.Remove(file)
|
||||
var b bytes.Buffer
|
||||
tmpl := template.Must(template.New("readme").Parse(readTMPL("template.markdown")))
|
||||
err := tmpl.Execute(&b, mdrows)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
// 保存 README.md 文件
|
||||
writeFile(file, b.Bytes())
|
||||
}
|
||||
|
||||
func readTMPL(path string) string {
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
data, err := ioutil.ReadAll(file)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
return string(data)
|
||||
}
|
58
ctl/util.go
Normal file
58
ctl/util.go
Normal file
@ -0,0 +1,58 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"sort"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func loadSolutionsDir() ([]int, int) {
|
||||
files, err := ioutil.ReadDir("../leetcode/")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
solutionIds := []int{}
|
||||
for _, f := range files {
|
||||
if f.Name()[4] == '.' {
|
||||
tmp, err := strconv.Atoi(f.Name()[:4])
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
solutionIds = append(solutionIds, tmp)
|
||||
}
|
||||
}
|
||||
sort.Ints(solutionIds)
|
||||
fmt.Printf("读取了 %v 道题的题解,当前目录下有 %v 个文件(可能包含 .DS_Store),目录中有 %v 道题在尝试中\n", len(solutionIds), len(files), len(files)-len(solutionIds))
|
||||
return solutionIds, len(files) - len(solutionIds)
|
||||
}
|
||||
|
||||
func writeFile(fileName string, content []byte) {
|
||||
file, err := os.OpenFile(fileName, os.O_RDWR|os.O_CREATE, 0777)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
_, err = file.Write(content)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
fmt.Println("write file successful")
|
||||
}
|
||||
|
||||
func binarySearch(nums []int, target int) int {
|
||||
low, high := 0, len(nums)-1
|
||||
for low <= high {
|
||||
mid := low + (high-low)>>1
|
||||
if nums[mid] == target {
|
||||
return mid
|
||||
} else if nums[mid] > target {
|
||||
high = mid - 1
|
||||
} else {
|
||||
low = mid + 1
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
Reference in New Issue
Block a user