From 38bc9bcf94097914cddba293dd85f7c4f00833ad Mon Sep 17 00:00:00 2001 From: Tai Groot Date: Mon, 30 Jan 2023 22:32:28 -0800 Subject: [PATCH] mid-merge --- cmd/cli/cli.go | 21 ++--- cmd/server/svg-server.go | 14 ++- commits/commits.go | 180 ++++++++++++++++++++++++++++++++++++--- commits/common.go | 10 +++ types/types.go | 8 +- 5 files changed, 202 insertions(+), 31 deletions(-) diff --git a/cmd/cli/cli.go b/cmd/cli/cli.go index d6081a0..b17db3d 100644 --- a/cmd/cli/cli.go +++ b/cmd/cli/cli.go @@ -2,29 +2,22 @@ package main import ( "fmt" - "math/rand" "time" "github.com/taigrr/gico/commits" "github.com/taigrr/gico/graph/term" ) -func init() { - rand.Seed(time.Now().UnixMilli()) -} - func main() { wfreq := []int{} yd := time.Now().YearDay() - freq, _ := commits.GlobalFrequency(time.Now().Year(), []string{""}) - if yd < 7 { - // xfreq, _ := commits.GlobalFrequency(time.Now().Year()-1, []string{""}) - } else { - // TODO fix bug for negative in first week of Jan - for i := 0; i < 7; i++ { - d := time.Now().YearDay() - 1 - 6 + i - wfreq = append(wfreq, freq[d]) - } + repoPaths, err := commits.GetMRRepos() + if err != nil { + panic(err) + } + freq, err := repoPaths.GlobalFrequency(time.Now().Year(), []string{""}) + if err != nil { + panic(err) } fmt.Println("week:") fmt.Println(term.GetWeekUnicode(wfreq)) diff --git a/cmd/server/svg-server.go b/cmd/server/svg-server.go index cda58c9..f9fa634 100644 --- a/cmd/server/svg-server.go +++ b/cmd/server/svg-server.go @@ -21,7 +21,11 @@ func main() { w.Header().Add("Content-Type", "text/html") now := time.Now() year := now.Year() - freq, err := commits.GlobalFrequencyChan(year, []string{author}) + repoPaths, err := commits.GetMRRepos() + if err != nil { + panic(err) + } + freq, err := repoPaths.FrequencyChan(year, []string{author}) if err != nil { panic(err) } @@ -29,7 +33,7 @@ func main() { fmt.Println(today) if today < 6 { curYear := year - 1 - curFreq, err := commits.GlobalFrequencyChan(curYear, []string{author}) + curFreq, err := repoPaths.FrequencyChan(curYear, []string{author}) if err != nil { panic(err) } @@ -53,7 +57,11 @@ func main() { if err == nil { year = y } - freq, err := commits.GlobalFrequencyChan(year, []string{author}) + repoPaths, err := commits.GetMRRepos() + if err != nil { + panic(err) + } + freq, err := repoPaths.FrequencyChan(year, []string{author}) if err != nil { panic(err) } diff --git a/commits/commits.go b/commits/commits.go index d2f5f33..a2b71a6 100644 --- a/commits/commits.go +++ b/commits/commits.go @@ -1,7 +1,12 @@ package commits import ( + "crypto/md5" + "fmt" "regexp" + "sort" + "strings" + "sync" "time" git "github.com/go-git/go-git/v5" @@ -11,30 +16,185 @@ import ( "github.com/taigrr/mg/parse" ) -func GlobalFrequency(year int, authors []string) (types.YearFreq, error) { +type Repo git.Repository + +type RepoSet []string + +var ( + mapTex sync.RWMutex + hashCache map[int]map[string]map[string]types.ExpFreq +) + +func init() { + hashCache = make(map[int]map[string]map[string]types.ExpFreq) +} + +func hashSlice(in []string) string { + sort.Strings(in) + sb := strings.Builder{} + for _, s := range in { + sb.WriteString(s) + } + h := md5.New() + h.Write([]byte(sb.String())) + b := h.Sum(nil) + return fmt.Sprintf("%x\n", b) +} + +func GetCachedGraph(year int, authors []string, repoPaths []string) (types.Freq, bool) { + a := hashSlice(authors) + r := hashSlice(repoPaths) + mapTex.RLock() + defer mapTex.RUnlock() + if m1, ok := hashCache[year]; !ok { + return types.Freq{}, false + } else { + if m2, ok := m1[a]; !ok { + return types.Freq{}, false + } else { + if freq, ok := m2[r]; !ok { + return types.Freq{}, false + } else { + if freq.Created.Before(time.Now().Add(-15 * time.Minute)) { + return types.Freq{}, false + } else { + return freq.YearFreq, true + } + } + } + } +} + +func CacheGraph(year int, authors, repoPaths []string, freq types.Freq) { + a := hashSlice(authors) + r := hashSlice(repoPaths) + mapTex.Lock() + defer mapTex.Unlock() + if _, ok := hashCache[year]; !ok { + hashCache[year] = make(map[string]map[string]types.ExpFreq) + } + if _, ok := hashCache[year][a]; !ok { + hashCache[year][a] = make(map[string]types.ExpFreq) + } + hashCache[year][a][r] = types.ExpFreq{YearFreq: freq, Created: time.Now()} + go func() { + time.Sleep(time.Minute * 15) + mapTex.Lock() + defer mapTex.Unlock() + delete(hashCache[year][a], r) + }() +} + +func GetMRRepos() (RepoSet, error) { + mrconf, err := parse.LoadMRConfig() + if err != nil { + return RepoSet{}, err + } + paths := mrconf.GetRepoPaths() + return RepoSet(paths), nil +} + +func (paths RepoSet) GetWeekFreq(authors []string) (types.Freq, error) { + now := time.Now() + year := now.Year() + freq, err := paths.FrequencyChan(year, authors) + if err != nil { + return types.Freq{}, err + } + today := now.YearDay() - 1 + if today < 6 { + curYear := year - 1 + curFreq, err := paths.FrequencyChan(curYear, authors) + if err != nil { + return types.Freq{}, err + } + freq = append(curFreq, freq...) + today += 365 + if curYear%4 == 0 { + today++ + } + } + + week := freq[today-6 : today+1] + return week, nil +} + +func (paths RepoSet) FrequencyChan(year int, authors []string) (types.Freq, error) { yearLength := 365 if year%4 == 0 { yearLength++ } - gfreq := make(types.YearFreq, yearLength) - mrconf, err := parse.LoadMRConfig() - if err != nil { - return types.YearFreq{}, err + cache, ok := GetCachedGraph(year, authors, paths) + if ok { + return cache, nil } - paths := mrconf.GetRepoPaths() + outChan := make(chan types.Commit, 10) + var wg sync.WaitGroup + for _, p := range paths { + wg.Add(1) + go func(path string) { + repo, err := OpenRepo(path) + if err != nil { + return + } + cc, err := repo.GetCommitChan() + if err != nil { + return + } + cc = FilterCChanByYear(cc, year) + cc, err = FilterCChanByAuthor(cc, authors) + if err != nil { + return + } + for c := range cc { + outChan <- c + } + wg.Done() + }(p) + } + go func() { + wg.Wait() + close(outChan) + }() + freq := YearFreqFromChan(outChan, year) + CacheGraph(year, authors, paths, freq) + return freq, nil +} + +func YearFreqFromChan(cc chan types.Commit, year int) types.Freq { + yearLength := 365 + if year%4 == 0 { + yearLength++ + } + freq := make([]int, yearLength) + for commit := range cc { + if commit.TimeStamp.Year() != year { + continue + } + freq[commit.TimeStamp.YearDay()-1]++ + } + return freq +} + +func (paths RepoSet) GlobalFrequency(year int, authors []string) (types.Freq, error) { + yearLength := 365 + if year%4 == 0 { + yearLength++ + } + gfreq := make(types.Freq, yearLength) for _, p := range paths { repo, err := OpenRepo(p) if err != nil { - return types.YearFreq{}, err + return types.Freq{}, err } commits, err := repo.GetCommitSet() if err != nil { - return types.YearFreq{}, err + return types.Freq{}, err } commits = commits.FilterByYear(year) commits, err = commits.FilterByAuthorRegex(authors) if err != nil { - return types.YearFreq{}, err + return types.Freq{}, err } freq := commits.ToYearFreq() gfreq = gfreq.Merge(freq) @@ -64,7 +224,7 @@ func (repo Repo) GetCommitSet() (CommitSet, error) { return cs, nil } -func (cs CommitSet) ToYearFreq() types.YearFreq { +func (cs CommitSet) ToYearFreq() types.Freq { year := cs.Year yearLength := 365 if year%4 == 0 { diff --git a/commits/common.go b/commits/common.go index 56703e8..37a6b17 100644 --- a/commits/common.go +++ b/commits/common.go @@ -7,6 +7,7 @@ import ( git "github.com/go-git/go-git/v5" "github.com/taigrr/gico/types" + "github.com/taigrr/mg/parse" ) type ( @@ -28,3 +29,12 @@ func OpenRepo(directory string) (Repo, error) { r, err := git.PlainOpenWithOptions(directory, &(git.PlainOpenOptions{DetectDotGit: true})) return Repo(*r), err } + +func GetMRRepos() (RepoSet, error) { + mrconf, err := parse.LoadMRConfig() + if err != nil { + return RepoSet{}, err + } + paths := mrconf.GetRepoPaths() + return RepoSet(paths), nil +} diff --git a/types/types.go b/types/types.go index 35be914..c9d4087 100644 --- a/types/types.go +++ b/types/types.go @@ -14,10 +14,10 @@ type ( Repo string `json:"repo,omitempty"` Path string `json:"path,omitempty"` } - DataSet map[time.Time]WorkDay - YearFreq []int - ExpYearFreq struct { - YearFreq YearFreq + DataSet map[time.Time]WorkDay + Freq []int + ExpFreq struct { + YearFreq Freq Created time.Time } WorkDay struct {