git.sophuwu.com > manweb   
              178
            
             package stats

import (
	"bytes"
	"git.sophuwu.com/manweb/CFG"
	"git.sophuwu.com/manweb/logs"
	"github.com/asdine/storm/v3"
	"go.etcd.io/bbolt"
	"html/template"
	"path/filepath"
	"strings"
)

var DB *storm.DB

func OpenDB() {
	if !CFG.EnableStats || CFG.StatisticDB == "" {
		return
	}
	var err error
	DB, err = storm.Open(CFG.StatisticDB, storm.BoltOptions(0660, &bbolt.Options{
		Timeout: 1000 * 1000 * 1000, // 1 second
	}))
	logs.CheckFatal("failed to open statistics database", err)
}

func CloseDB() {
	if DB == nil {
		return
	}
	err := DB.Close()
	logs.CheckFatal("failed to close statistics database", err)
	DB = nil
}

type Stat struct {
	Query string `storm:"id,unique"` // The query string
	Count int
}

func count(query string) {
	s := &Stat{Query: query}
	err := DB.One("Query", query, s)
	if err != nil {
		s = &Stat{
			Query: query,
			Count: 1,
		}
		DB.Save(s)
		return
	}
	s.Count++
	DB.Update(s)
}

func Count(query string) {
	if !CFG.EnableStats || DB == nil {
		return
	}
	go count(query)
}

type Special struct {
	Page  string `storm:"id,unique"` // The page name
	Count int
}

func countSpecial(page string) {
	s := &Special{Page: page}
	err := DB.One("Page", page, s)
	if err != nil {
		s = &Special{
			Page:  page,
			Count: 1,
		}
		DB.Save(s)
		return
	}
	s.Count++
	DB.Update(s)
}

func SpecialCount(page string) {
	if !CFG.EnableStats || DB == nil {
		return
	}
	go countSpecial(page)
}

func GetStats() ([]*Stat, error) {
	var stats []*Stat
	err := DB.Select().OrderBy("Count").Reverse().Find(&stats)
	if err != nil {
		return nil, err
	}
	return stats, nil
}

type SectionCount struct {
	Section string
	Count   int
	Unique  int
}

func addSection(sec *map[string]*SectionCount, name string, count int) {
	if name == "" {
		return
	}
	name = filepath.Ext(name)
	name = strings.TrimPrefix(name, ".")
	secName, ok := (*sec)[name]
	if !ok {
		(*sec)[name] = &SectionCount{
			Section: name,
			Count:   count,
			Unique:  1,
		}
		return
	}
	secName.Count += count
	secName.Unique++
	(*sec)[name] = secName
}

type html struct {
	TotalLoads    int
	TotalPages    int
	UniquePages   int
	Searches      int
	Errors        int
	TotalSections []SectionCount
	AllPages      []Special
	MaxLen        int
}

var T *template.Template

func Html() string {
	if !CFG.EnableStats || DB == nil {
		return ""
	}
	stats, err := GetStats()
	if err != nil || len(stats) == 0 {
		return ""
	}
	var ht html
	var Specialc Special
	_ = DB.One("Page", "Search", &Specialc)
	ht.Searches = Specialc.Count
	_ = DB.One("Page", "Error", &Specialc)
	ht.Errors = Specialc.Count

	sec := make(map[string]*SectionCount)
	Maxlen := 0
	for _, s := range stats {
		addSection(&sec, s.Query, s.Count)
		ht.AllPages = append(ht.AllPages, Special{Page: s.Query, Count: s.Count})
		if len(s.Query) > Maxlen {
			Maxlen = len(s.Query)
		}
	}
	for _, v := range sec {
		ht.TotalSections = append(ht.TotalSections, *v)
		ht.TotalPages += v.Count
		ht.UniquePages += v.Unique
	}
	for i := range ht.TotalSections {
		for j := i + 1; j < len(ht.TotalSections); j++ {
			if ht.TotalSections[i].Count > ht.TotalSections[j].Count {
				ht.TotalSections[i], ht.TotalSections[j] = ht.TotalSections[j], ht.TotalSections[i]
			}
		}
	}
	ht.TotalLoads = ht.TotalPages + ht.UniquePages + ht.Searches + ht.Errors
	var b bytes.Buffer
	_ = T.ExecuteTemplate(&b, "stats", &ht)
	return b.String()
}