github.com/covergates/covergates@v0.2.2-0.20201009050117-42ef8a19fb95/modules/charts/card.go (about) 1 package charts 2 3 import ( 4 "fmt" 5 "io" 6 7 svg "github.com/ajstarks/svgo" 8 "github.com/covergates/covergates/core" 9 "github.com/covergates/covergates/modules/charts/icons" 10 "github.com/dustin/go-humanize" 11 "golang.org/x/text/language" 12 "golang.org/x/text/message" 13 ) 14 15 const ( 16 fileIcon = "files.svg" 17 listIcon = "list.svg" 18 gitIcon = "git.svg" 19 logoIcon = "logo.svg" 20 cssStyle = ` 21 <style> 22 .score-circle-ring { 23 stroke: #3f8167; 24 stroke-width: 6; 25 fill: none; 26 opacity: 0.2; 27 } 28 .score-circle { 29 stroke: #3f8167; 30 stroke-width: 6; 31 fill: none; 32 stroke-linecap: round; 33 stroke-dasharray: 283; 34 } 35 </style> 36 ` 37 ) 38 39 // RepoCard render svg repo status card 40 type RepoCard struct { 41 core.Chart 42 repo *core.Repo 43 report *core.Report 44 canvas *svg.SVG 45 } 46 47 // NewRepoCard render 48 func NewRepoCard(repo *core.Repo, report *core.Report) *RepoCard { 49 return &RepoCard{ 50 repo: repo, 51 report: report, 52 } 53 } 54 55 func (c *RepoCard) start(w io.Writer) { 56 c.canvas = svg.New(w) 57 c.canvas.Start(495, 195) 58 c.canvas.Writer.Write([]byte(cssStyle)) 59 c.canvas.ClipPath(`id="a1"`) 60 c.canvas.Rect(0, 0, 500, 170) 61 c.canvas.ClipEnd() 62 c.canvas.Roundrect(1, 1, 493, 193, 5, 5, `fill="#3b726b"`) 63 c.canvas.Roundrect(1, 1, 493, 193, 5, 5, `fill="#333333"`, `clip-path="url(#a1)"`) 64 } 65 66 func (c *RepoCard) end() { 67 c.canvas.End() 68 } 69 70 func (c *RepoCard) title() { 71 c.canvas.Text(15, 32, c.repo.FullName(), `font: 600 24px 'Segoe UI', sans-serif`, `fill="#80b9a2"`) 72 } 73 74 func (c *RepoCard) files() { 75 c.canvas.Translate(15, 75) 76 c.canvas.Group(`transform="scale(0.7) translate(0, -22)"`) 77 c.canvas.Writer.Write(icons.MustAsset(fileIcon)) 78 c.canvas.Gend() 79 c.canvas.Text(32, 0, "Total Files", `font: 600 18px 'Segoe UI', sans-serif`, `fill="#e6e6e6"`) 80 p := message.NewPrinter(language.English) 81 c.canvas.Text(200, 0, p.Sprintf("%d", len(c.report.Files)), `font: 600 18px 'Segoe UI', sans-serif`, `fill="#e6e6e6"`) 82 c.canvas.Gend() 83 } 84 85 func (c *RepoCard) hits() { 86 c.canvas.Translate(15, 115) 87 c.canvas.Translate(-1, -16) 88 c.canvas.Gtransform("scale(0.18)") 89 c.canvas.Writer.Write(icons.MustAsset(listIcon)) 90 c.canvas.Gend() 91 c.canvas.Gend() 92 c.canvas.Text(32, 0, "Hit Lines", `font: 600 18px 'Segoe UI', sans-serif`, `fill="#e6e6e6"`) 93 p := message.NewPrinter(language.English) 94 c.canvas.Text(200, 0, p.Sprintf("%d", c.hitLines()), `font: 600 18px 'Segoe UI', sans-serif`, `fill="#e6e6e6"`) 95 c.canvas.Gend() 96 } 97 98 func (c *RepoCard) hitLines() int { 99 hits := 0 100 for _, coverage := range c.report.Coverages { 101 for _, file := range coverage.Files { 102 for _, line := range file.StatementHits { 103 if line.Hits > 0 { 104 hits++ 105 } 106 } 107 } 108 } 109 return hits 110 } 111 112 func (c *RepoCard) build() { 113 c.canvas.Translate(15, 155) 114 c.canvas.Gtransform("scale(0.11) translate(-140, -160)") 115 c.canvas.Writer.Write(icons.MustAsset(gitIcon)) 116 c.canvas.Gend() 117 c.canvas.Text(32, 0, "Recent Build", `font: 600 18px 'Segoe UI', sans-serif`, `fill="#e6e6e6"`) 118 c.canvas.Text(200, 0, fmt.Sprintf("%s, %s", c.repo.Branch, humanize.Time(c.report.CreatedAt)), `font: 600 18px 'Segoe UI', sans-serif`, `fill="#e6e6e6"`) 119 c.canvas.Gend() 120 } 121 122 func (c *RepoCard) brand() { 123 c.canvas.Translate(180, 185) 124 c.canvas.Gtransform("scale(0.08) translate(-5, -180)") 125 c.canvas.Writer.Write(icons.MustAsset(logoIcon)) 126 c.canvas.Gend() 127 c.canvas.Text(30, 0, "covergates©2020", "font-size:12px") 128 c.canvas.Gend() 129 } 130 131 func (c *RepoCard) score() { 132 c.canvas.Translate(420, 75) 133 c.canvas.Circle(0, 0, 45, `class="score-circle-ring"`) 134 c.canvas.Circle( 135 0, 0, 45, 136 `class="score-circle"`, 137 fmt.Sprintf("stroke-dashoffset: %d", int(45*2*3.14*(1.0-c.report.StatementCoverage()))), 138 ) 139 c.canvas.Translate(-35, 12) 140 score := int(c.report.StatementCoverage() * 100) 141 pad := 0 142 if score < 100 { 143 pad = 10 144 } 145 c.canvas.Text(pad, 0, fmt.Sprintf("%d", score), `font: 600 36px 'Segoe UI', sans-serif`, `fill="#3f8167"`) 146 c.canvas.Text(55, 0, "%", `font: 800 14px 'Segoe UI', sans-serif`, `fill="#3f8167"`) 147 c.canvas.Gend() 148 c.canvas.Gend() 149 } 150 151 // Render card to writer 152 func (c *RepoCard) Render(w io.Writer) error { 153 c.start(w) 154 c.title() 155 c.files() 156 c.hits() 157 c.build() 158 c.brand() 159 c.score() 160 c.end() 161 return nil 162 }