Óscar M. Lage
f7cf197b4e
File is being proccessed from memory directly. We don't need to tempcopy nor delete it because it's not saved to disk.
163 lines
3.4 KiB
Go
163 lines
3.4 KiB
Go
package web
|
|
|
|
import (
|
|
"bufio"
|
|
"encoding/json"
|
|
"fmt"
|
|
"html/template"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
"sort"
|
|
"strings"
|
|
|
|
"github.com/go-echarts/go-echarts/v2/charts"
|
|
"github.com/go-echarts/go-echarts/v2/opts"
|
|
)
|
|
|
|
func loadTemplates(name string) (*template.Template, error) {
|
|
|
|
funcs := template.FuncMap{
|
|
"safeHTML": func(s string) interface{} { return template.HTML(s) },
|
|
}
|
|
|
|
templates, err := loadTemplatesFromDir("internal/web/templates")
|
|
if err != nil {
|
|
log.Printf("Error loading templates: %v", err)
|
|
return nil, err
|
|
}
|
|
|
|
return template.New(name).Funcs(funcs).ParseFiles(templates...)
|
|
}
|
|
|
|
func ConvertAndSort[K comparable](data map[K]int, limit int) []CommandStat {
|
|
var result []CommandStat
|
|
for key, count := range data {
|
|
result = append(result, CommandStat{
|
|
Command: fmt.Sprintf("%v", key),
|
|
Count: count,
|
|
})
|
|
}
|
|
|
|
sort.Slice(result, func(i, j int) bool {
|
|
return result[i].Count > result[j].Count
|
|
})
|
|
if len(result) > limit {
|
|
result = result[:limit]
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
func parseHistoryFile(file http.File) (map[string]int, error) {
|
|
commandCounts := make(map[string]int)
|
|
scanner := bufio.NewScanner(file)
|
|
|
|
for scanner.Scan() {
|
|
line := strings.TrimSpace(scanner.Text())
|
|
if line == "" || strings.HasPrefix(line, "#") {
|
|
continue
|
|
}
|
|
|
|
command := strings.Fields(line)[0]
|
|
commandCounts[command]++
|
|
}
|
|
|
|
if err := scanner.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return commandCounts, nil
|
|
}
|
|
|
|
func generateTopCommands(commandCounts map[string]int, topN int) []CommandStat {
|
|
stats := make([]CommandStat, 0, len(commandCounts))
|
|
for cmd, count := range commandCounts {
|
|
stats = append(stats, CommandStat{Command: cmd, Count: count})
|
|
}
|
|
|
|
sort.Slice(stats, func(i, j int) bool {
|
|
return stats[i].Count > stats[j].Count
|
|
})
|
|
|
|
if topN < len(stats) {
|
|
stats = stats[:topN]
|
|
}
|
|
return stats
|
|
}
|
|
|
|
func createBarChart(stats []CommandStat) *charts.Bar {
|
|
bar := charts.NewBar()
|
|
bar.SetGlobalOptions(
|
|
charts.WithTitleOpts(opts.Title{
|
|
Title: "Top Commands",
|
|
Subtitle: "Most used commands",
|
|
}),
|
|
)
|
|
|
|
commands := make([]string, len(stats))
|
|
counts := make([]int, len(stats))
|
|
for i, stat := range stats {
|
|
commands[i] = stat.Command
|
|
counts[i] = stat.Count
|
|
}
|
|
|
|
bar.SetXAxis(commands).
|
|
AddSeries("Freq", generateBarItems(counts)).
|
|
SetSeriesOptions(
|
|
charts.WithLabelOpts(opts.Label{
|
|
Color: "white",
|
|
Position: "top",
|
|
}),
|
|
)
|
|
|
|
return bar
|
|
}
|
|
|
|
func generateBarItems(data []int) []opts.BarData {
|
|
items := make([]opts.BarData, len(data))
|
|
for i, v := range data {
|
|
items[i] = opts.BarData{Value: v}
|
|
}
|
|
return items
|
|
}
|
|
|
|
func saveResults(uniqueID string, stats map[string]interface{}) {
|
|
filename := "results/" + uniqueID + ".json"
|
|
file, err := os.Create(filename)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
defer file.Close()
|
|
json.NewEncoder(file).Encode(stats)
|
|
}
|
|
|
|
func loadResults(uniqueID string) (map[string]interface{}, error) {
|
|
filename := "results/" + uniqueID + ".json"
|
|
file, err := os.Open(filename)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer file.Close()
|
|
|
|
var stats map[string]interface{}
|
|
err = json.NewDecoder(file).Decode(&stats)
|
|
return stats, err
|
|
}
|
|
|
|
func loadTemplatesFromDir(dir string) ([]string, error) {
|
|
var templates []string
|
|
|
|
err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !info.IsDir() && filepath.Ext(path) == ".html" {
|
|
templates = append(templates, path)
|
|
}
|
|
return nil
|
|
})
|
|
|
|
return templates, err
|
|
}
|