From c958ecb465aba5ef3d7ba3ed32b0318c660f307c Mon Sep 17 00:00:00 2001 From: Jeremy Rans Date: Fri, 14 Dec 2018 23:35:02 -0600 Subject: [PATCH] refactor to prepare for CLI --- check/check.go | 102 +++++++++++++++++++++++++++++++++++++++++ check/grade.go | 37 +++++++++++++++ handlers/badge.go | 39 +--------------- handlers/checks.go | 112 ++++++++------------------------------------- 4 files changed, 159 insertions(+), 131 deletions(-) create mode 100644 check/grade.go diff --git a/check/check.go b/check/check.go index 1ead40a..515d632 100644 --- a/check/check.go +++ b/check/check.go @@ -1,5 +1,11 @@ package check +import ( + "fmt" + "log" + "sort" +) + // Check describes what methods various checks (gofmt, go lint, etc.) // should implement type Check interface { @@ -10,3 +16,99 @@ type Check interface { // as well as a map of filename to output Percentage() (float64, []FileSummary, error) } + +type Score struct { + Name string `json:"name"` + Description string `json:"description"` + FileSummaries []FileSummary `json:"file_summaries"` + Weight float64 `json:"weight"` + Percentage float64 `json:"percentage"` + Error string `json:"error"` +} + +type ChecksResult struct { + Checks []Score `json:"checks"` + Average float64 `json:"average"` + Grade Grade `json:"GradeFromPercentage"` + Files int `json:"files"` + Issues int `json:"issues"` +} + +func CheckDir(dir string) (ChecksResult, error) { + filenames, skipped, err := GoFiles(dir) + if err != nil { + return ChecksResult{}, fmt.Errorf("could not get filenames: %v", err) + } + if len(filenames) == 0 { + return ChecksResult{}, fmt.Errorf("no .go files found") + } + + err = RenameFiles(skipped) + if err != nil { + log.Println("Could not remove files:", err) + } + defer RevertFiles(skipped) + + checks := []Check{ + GoFmt{Dir: dir, Filenames: filenames}, + GoVet{Dir: dir, Filenames: filenames}, + GoLint{Dir: dir, Filenames: filenames}, + GoCyclo{Dir: dir, Filenames: filenames}, + License{Dir: dir, Filenames: []string{}}, + Misspell{Dir: dir, Filenames: filenames}, + IneffAssign{Dir: dir, Filenames: filenames}, + // ErrCheck{Dir: dir, Filenames: filenames}, // disable errcheck for now, too slow and not finalized + } + + ch := make(chan Score) + for _, c := range checks { + go func(c Check) { + p, summaries, err := c.Percentage() + errMsg := "" + if err != nil { + log.Printf("ERROR: (%s) %v", c.Name(), err) + errMsg = err.Error() + } + s := Score{ + Name: c.Name(), + Description: c.Description(), + FileSummaries: summaries, + Weight: c.Weight(), + Percentage: p, + Error: errMsg, + } + ch <- s + }(c) + } + + resp := ChecksResult{ + Files: len(filenames), + } + + var total, totalWeight float64 + var issues = make(map[string]bool) + for i := 0; i < len(checks); i++ { + s := <-ch + resp.Checks = append(resp.Checks, s) + total += s.Percentage * s.Weight + totalWeight += s.Weight + for _, fs := range s.FileSummaries { + issues[fs.Filename] = true + } + } + total /= totalWeight + + sort.Sort(ByWeight(resp.Checks)) + resp.Average = total + resp.Issues = len(issues) + resp.Grade = GradeFromPercentage(total * 100) + + return resp, nil +} + +// ByWeight implements sorting for checks by weight descending +type ByWeight []Score + +func (a ByWeight) Len() int { return len(a) } +func (a ByWeight) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a ByWeight) Less(i, j int) bool { return a[i].Weight > a[j].Weight } diff --git a/check/grade.go b/check/grade.go new file mode 100644 index 0000000..f4e9129 --- /dev/null +++ b/check/grade.go @@ -0,0 +1,37 @@ +package check + +// Grade represents a grade returned by the server, which is normally +// somewhere between A+ (highest) and F (lowest). +type Grade string + +// The Grade constants below indicate the current available +// grades. +const ( + GradeAPlus Grade = "A+" + GradeA = "A" + GradeB = "B" + GradeC = "C" + GradeD = "D" + GradeE = "E" + GradeF = "F" +) + +// GradeFromPercentage is a helper for getting the GradeFromPercentage for a percentage +func GradeFromPercentage(percentage float64) Grade { + switch { + case percentage > 90: + return GradeAPlus + case percentage > 80: + return GradeA + case percentage > 70: + return GradeB + case percentage > 60: + return GradeC + case percentage > 50: + return GradeD + case percentage > 40: + return GradeE + default: + return GradeF + } +} diff --git a/handlers/badge.go b/handlers/badge.go index 426c003..068d62c 100644 --- a/handlers/badge.go +++ b/handlers/badge.go @@ -2,48 +2,13 @@ package handlers import ( "fmt" + "github.com/gojp/goreportcard/check" "log" "net/http" "strings" ) -// Grade represents a grade returned by the server, which is normally -// somewhere between A+ (highest) and F (lowest). -type Grade string - -// The Grade constants below indicate the current available -// grades. -const ( - GradeAPlus Grade = "A+" - GradeA = "A" - GradeB = "B" - GradeC = "C" - GradeD = "D" - GradeE = "E" - GradeF = "F" -) - -// grade is a helper for getting the grade for a percentage -func grade(percentage float64) Grade { - switch { - case percentage > 90: - return GradeAPlus - case percentage > 80: - return GradeA - case percentage > 70: - return GradeB - case percentage > 60: - return GradeC - case percentage > 50: - return GradeD - case percentage > 40: - return GradeE - default: - return GradeF - } -} - -func badgePath(grade Grade, style string, dev bool) string { +func badgePath(grade check.Grade, style string, dev bool) string { return fmt.Sprintf("assets/badges/%s_%s.svg", strings.ToLower(string(grade)), strings.ToLower(style)) } diff --git a/handlers/checks.go b/handlers/checks.go index b386d08..fdd665e 100644 --- a/handlers/checks.go +++ b/handlers/checks.go @@ -5,11 +5,10 @@ import ( "errors" "fmt" "log" - "sort" "time" "github.com/boltdb/bolt" - humanize "github.com/dustin/go-humanize" + "github.com/dustin/go-humanize" "github.com/gojp/goreportcard/check" "github.com/gojp/goreportcard/download" ) @@ -63,26 +62,16 @@ func getFromCache(repo string) (checksResp, error) { return resp, nil } -type score struct { - Name string `json:"name"` - Description string `json:"description"` - FileSummaries []check.FileSummary `json:"file_summaries"` - Weight float64 `json:"weight"` - Percentage float64 `json:"percentage"` - Error string `json:"error"` -} - type checksResp struct { - Checks []score `json:"checks"` - Average float64 `json:"average"` - Grade Grade `json:"grade"` - Files int `json:"files"` - Issues int `json:"issues"` - Repo string `json:"repo"` - ResolvedRepo string `json:"resolvedRepo"` - LastRefresh time.Time `json:"last_refresh"` - LastRefreshFormatted string `json:"formatted_last_refresh"` - LastRefreshHumanized string `json:"humanized_last_refresh"` + Checks []check.Score `json:"checks"` + Average float64 `json:"average"` + Grade check.Grade `json:"grade"` + Files int `json:"files"` + Issues int `json:"issues"` + Repo string `json:"repo"` + LastRefresh time.Time `json:"last_refresh"` + LastRefreshFormatted string `json:"formatted_last_refresh"` + LastRefreshHumanized string `json:"humanized_last_refresh"` } func newChecksResp(repo string, forceRefresh bool) (checksResp, error) { @@ -92,7 +81,7 @@ func newChecksResp(repo string, forceRefresh bool) (checksResp, error) { // just log the error and continue log.Println(err) } else { - resp.Grade = grade(resp.Average * 100) // grade is not stored for some repos, yet + resp.Grade = check.GradeFromPercentage(resp.Average * 100) // grade is not stored for some repos, yet return resp, nil } } @@ -104,82 +93,24 @@ func newChecksResp(repo string, forceRefresh bool) (checksResp, error) { } repo = repoRoot.Root - - dir := dirName(repo) - filenames, skipped, err := check.GoFiles(dir) + checkResult, err := check.CheckDir(dirName(repo)) if err != nil { - return checksResp{}, fmt.Errorf("could not get filenames: %v", err) - } - if len(filenames) == 0 { - return checksResp{}, fmt.Errorf("no .go files found") - } - - err = check.RenameFiles(skipped) - if err != nil { - log.Println("Could not remove files:", err) - } - defer check.RevertFiles(skipped) - - checks := []check.Check{ - check.GoFmt{Dir: dir, Filenames: filenames}, - check.GoVet{Dir: dir, Filenames: filenames}, - check.GoLint{Dir: dir, Filenames: filenames}, - check.GoCyclo{Dir: dir, Filenames: filenames}, - check.License{Dir: dir, Filenames: []string{}}, - check.Misspell{Dir: dir, Filenames: filenames}, - check.IneffAssign{Dir: dir, Filenames: filenames}, - // check.ErrCheck{Dir: dir, Filenames: filenames}, // disable errcheck for now, too slow and not finalized - } - - ch := make(chan score) - for _, c := range checks { - go func(c check.Check) { - p, summaries, err := c.Percentage() - errMsg := "" - if err != nil { - log.Printf("ERROR: (%s) %v", c.Name(), err) - errMsg = err.Error() - } - s := score{ - Name: c.Name(), - Description: c.Description(), - FileSummaries: summaries, - Weight: c.Weight(), - Percentage: p, - Error: errMsg, - } - ch <- s - }(c) + return checksResp{}, err } t := time.Now().UTC() resp := checksResp{ + Checks: checkResult.Checks, + Average: checkResult.Average, + Grade: checkResult.Grade, + Files: checkResult.Files, + Issues: checkResult.Issues, Repo: repo, - ResolvedRepo: repoRoot.Repo, - Files: len(filenames), LastRefresh: t, LastRefreshFormatted: t.Format(time.UnixDate), LastRefreshHumanized: humanize.Time(t), } - var total, totalWeight float64 - var issues = make(map[string]bool) - for i := 0; i < len(checks); i++ { - s := <-ch - resp.Checks = append(resp.Checks, s) - total += s.Percentage * s.Weight - totalWeight += s.Weight - for _, fs := range s.FileSummaries { - issues[fs.Filename] = true - } - } - total /= totalWeight - - sort.Sort(ByWeight(resp.Checks)) - resp.Average = total - resp.Issues = len(issues) - resp.Grade = grade(total * 100) - respBytes, err := json.Marshal(resp) if err != nil { return checksResp{}, fmt.Errorf("could not marshal json: %v", err) @@ -242,10 +173,3 @@ func newChecksResp(repo string, forceRefresh bool) (checksResp, error) { return resp, nil } - -// ByWeight implements sorting for checks by weight descending -type ByWeight []score - -func (a ByWeight) Len() int { return len(a) } -func (a ByWeight) Swap(i, j int) { a[i], a[j] = a[j], a[i] } -func (a ByWeight) Less(i, j int) bool { return a[i].Weight > a[j].Weight }