You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
naza/pkg/chartbar/ctx.go

222 lines
5.1 KiB
Go

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

// Copyright 2021, Chef. All rights reserved.
// https://github.com/q191201771/naza
//
// Use of this source code is governed by a MIT-style license
// that can be found in the License file.
//
// Author: Chef (191201771@qq.com)
package chartbar
import (
"encoding/csv"
"fmt"
"math"
"os"
"sort"
"strconv"
"strings"
"github.com/q191201771/naza/pkg/dataops"
)
type Ctx struct {
option Option
}
//func (ctx *Ctx) ModOptions(modOptions ...ModOption) *Ctx {
// for _, fn := range modOptions {
// fn(&ctx.option)
// }
// return ctx
//}
// WithItems
//
// @param items: 注意,内部不会修改切片底层数据的值以及顺序
//
func (ctx *Ctx) WithItems(items []Item) string {
// 拷贝一份,避免修改外部切片的原始顺序
if ctx.option.Order != OrderOrigin {
clone := make([]Item, len(items))
copy(clone, items)
items = clone
}
switch ctx.option.Order {
case OrderOrigin:
// noop
case OrderAscCount:
sort.Slice(items, func(i, j int) bool {
return items[i].Num < items[j].Num
})
case OrderDescCount:
sort.Slice(items, func(i, j int) bool {
return items[i].Num > items[j].Num
})
case OrderAscName:
sort.Slice(items, func(i, j int) bool {
return items[i].Name < items[j].Name
})
case OrderDescName:
sort.Slice(items, func(i, j int) bool {
return items[i].Name > items[j].Name
})
}
var newItems []Item
dataops.SliceLimit(items, ctx.option.PrefixNumLimit, ctx.option.SuffixNumLimit, func(index int) {
newItems = append(newItems, items[index])
})
items = newItems
var (
maxCountLength int // count柱状最长画多长
maxLengthOfNum int // num字段多长
)
minNum, maxNum := calcMinMaxNum(items)
if minNum > 0.00 {
// 都是正数的情况,最大的画满柱状条,其他的按与最大占比画
for i := range items {
// round四舍五入
items[i].count = int(math.Round(items[i].Num * float64(ctx.option.MaxBarLength) / maxNum))
// 最小可能和最大的比太小了
if items[i].count == 0 {
items[i].count = 1
}
}
maxCountLength = calcMaxCount(items)
maxLengthOfNum = len(fmt.Sprintf("%0.2f", maxNum))
} else {
// 有负数的情况最小的负数画1最大的画满
for i := range items {
items[i].count = int(math.Round((items[i].Num - minNum) * float64(ctx.option.MaxBarLength) / (maxNum - minNum)))
if items[i].count == 0 {
items[i].count = 1
}
}
maxCountLength = calcMaxCount(items)
maxn := len(fmt.Sprintf("%0.2f", maxNum))
minn := len(fmt.Sprintf("%0.2f", minNum))
if maxn > minn {
maxLengthOfNum = maxn
} else {
maxLengthOfNum = minn
}
}
maxLengthOfName := calcMaxLengthOfName(items)
//tmpl := fmt.Sprintf("%%%d.2f | %%-%ds | %%-%ds\n", maxLengthOfNum, maxCountLength, maxLengthOfName)
tmpl := fmt.Sprintf("%%%d.2f | %%s%%s | %%-%ds\n", maxLengthOfNum, maxLengthOfName)
var out string
for _, item := range items {
bar := strings.Repeat(ctx.option.DrawIconBlock, item.count)
padding := strings.Repeat(ctx.option.DrawIconPadding, maxCountLength-item.count)
out += fmt.Sprintf(tmpl, item.Num, bar, padding, item.Name)
}
return out
}
func (ctx *Ctx) WithAnySlice(a interface{}, iterateTransFn func(originItem interface{}) Item, modOptions ...ModOption) string {
var items []Item
dataops.IterateInterfaceAsSlice(a, func(iterItem interface{}) {
items = append(items, iterateTransFn(iterItem))
})
return ctx.WithItems(items)
}
func (ctx *Ctx) WithMap(m map[string]int) string {
var items []Item
for k, v := range m {
item := Item{
Name: k,
Num: float64(v),
}
items = append(items, item)
}
return ctx.WithItems(items)
}
func (ctx *Ctx) WithMapFloat(m map[string]float64) string {
var items []Item
for k, v := range m {
item := Item{
Name: k,
Num: v,
}
items = append(items, item)
}
return ctx.WithItems(items)
}
func (ctx *Ctx) WithCsv(filename string) (string, error) {
// 读取
fp, err := os.Open(filename)
if err != nil {
return "", err
}
defer fp.Close()
r := csv.NewReader(fp)
records, err := r.ReadAll()
if err != nil {
return "", err
}
var items []Item
for _, line := range records {
var item Item
item.Name = line[0]
item.Num, err = strconv.ParseFloat(line[1], 64)
if err != nil {
return "", err
}
items = append(items, item)
}
return ctx.WithItems(items), nil
}
// ---------------------------------------------------------------------------------------------------------------------
// Num最大值
func calcMinMaxNum(items []Item) (min, max float64) {
max = math.SmallestNonzeroFloat64
min = math.MaxFloat64
for _, item := range items {
if item.Num > max {
max = item.Num
}
if item.Num < min {
min = item.Num
}
}
return
}
// count最大值
func calcMaxCount(items []Item) int {
var max int
for _, item := range items {
if item.count > max {
max = item.count
}
}
return max
}
func calcMaxLengthOfName(items []Item) int {
var max int
for _, item := range items {
if len(item.Name) > max {
max = len(item.Name)
}
}
return max
}
// ---------------------------------------------------------------------------------------------------------------------