github.com/developest/gtm-core@v1.0.4-0.20220111132249-cc80a3372c3f/report/query.go (about) 1 // Copyright 2016 Michael Schenk. All rights reserved. 2 // Use of this source code is governed by a MIT-style 3 // license that can be found in the LICENSE file. 4 5 package report 6 7 import ( 8 "fmt" 9 "path/filepath" 10 "sort" 11 "strings" 12 "time" 13 14 "github.com/DEVELOPEST/gtm-core/note" 15 "github.com/DEVELOPEST/gtm-core/project" 16 "github.com/DEVELOPEST/gtm-core/scm" 17 "github.com/DEVELOPEST/gtm-core/util" 18 ) 19 20 const ( 21 defaultDateFormat = "Mon Jan 02 15:04:05 2006 MST" 22 ) 23 24 func retrieveNotes(projects []ProjectCommits, 25 terminalOff, appOff, calcStats bool, 26 dateFormat, subdir string) commitNoteDetails { 27 notes := commitNoteDetails{} 28 29 if dateFormat == "" { 30 dateFormat = defaultDateFormat 31 } 32 33 for _, p := range projects { 34 for _, c := range p.Commits { 35 n, err := scm.ReadNote(c, project.NoteNameSpace, calcStats, p.Path) 36 if err != nil { 37 notes = append(notes, commitNoteDetail{}) 38 continue 39 } 40 41 when := n.When.Format(dateFormat) 42 43 var commitNote note.CommitNote 44 commitNote, err = note.UnMarshal(n.Note) 45 if err != nil { 46 commitNote = note.CommitNote{} 47 } 48 49 if terminalOff { 50 commitNote = commitNote.FilterOutTerminal() 51 } 52 if appOff { 53 commitNote = commitNote.FilterOutApp() 54 } 55 56 if subdir != "" { 57 commitNote = commitNote.FilterOutSubdir(subdir) 58 } 59 60 id := n.ID 61 if len(id) > 7 { 62 id = id[:7] 63 } 64 65 message := strings.TrimPrefix(n.Message, n.Summary) 66 message = strings.TrimSpace(message) 67 68 notes = append(notes, 69 commitNoteDetail{ 70 Author: n.Author, 71 Date: when, 72 When: n.When, 73 Hash: id, 74 Subject: n.Summary, 75 Message: message, 76 Note: commitNote, 77 Project: filepath.Base(p.Path), 78 LineAdd: fmt.Sprintf("+%d", n.Stats.Insertions), 79 LineDel: fmt.Sprintf("-%d", n.Stats.Deletions), 80 LineDiff: fmt.Sprintf("%d", n.Stats.Insertions-n.Stats.Deletions), 81 ChangeRate: fmt.Sprintf("%.0f", n.Stats.ChangeRatePerHour(commitNote.Total())), 82 }) 83 } 84 } 85 sort.Sort(notes) 86 return notes 87 } 88 89 type commitNoteDetails []commitNoteDetail 90 91 func (c commitNoteDetails) Len() int { return len(c) } 92 func (c commitNoteDetails) Swap(i, j int) { c[i], c[j] = c[j], c[i] } 93 func (c commitNoteDetails) Less(i, j int) bool { return c[i].When.After(c[j].When) } 94 95 func (c commitNoteDetails) Total() int { 96 t := 0 97 for i := range c { 98 t += c[i].Note.Total() 99 } 100 return t 101 } 102 103 type commitNoteDetail struct { 104 Author string 105 Date string 106 When time.Time 107 Hash string 108 Subject string 109 Project string 110 Message string 111 Note note.CommitNote 112 LineAdd string 113 LineDel string 114 LineDiff string 115 ChangeRate string 116 } 117 118 func (c commitNoteDetails) files() fileEntries { 119 filesMap := map[string]fileEntry{} 120 for _, n := range c { 121 for _, f := range n.Note.Files { 122 if entry, ok := filesMap[f.SourceFile]; !ok { 123 filesMap[f.SourceFile] = fileEntry{Filename: f.SourceFile, Seconds: f.TimeSpent} 124 } else { 125 entry.add(f.TimeSpent) 126 filesMap[f.SourceFile] = entry 127 } 128 } 129 } 130 131 files := make(fileEntries, 0, len(filesMap)) 132 for _, entry := range filesMap { 133 files = append(files, entry) 134 } 135 sort.Sort(sort.Reverse(files)) 136 return files 137 } 138 139 type fileEntries []fileEntry 140 141 func (f fileEntries) Len() int { return len(f) } 142 func (f fileEntries) Swap(i, j int) { f[i], f[j] = f[j], f[i] } 143 func (f fileEntries) Less(i, j int) bool { return f[i].Seconds < f[j].Seconds } 144 145 func (f fileEntries) Duration() string { 146 return util.FormatDuration(f.Total()) 147 } 148 149 func (f fileEntries) Total() int { 150 total := 0 151 for _, entry := range f { 152 total += entry.Seconds 153 } 154 return total 155 } 156 157 type fileEntry struct { 158 Filename string 159 Seconds int 160 } 161 162 func (f *fileEntry) add(s int) { 163 f.Seconds += s 164 } 165 166 func (f *fileEntry) Duration() string { 167 return util.FormatDuration(f.Seconds) 168 } 169 170 func (f *fileEntry) IsTerminal() bool { 171 return f.Filename == ".gtm/terminal.app" 172 } 173 174 func (f *fileEntry) IsApp() bool { 175 return project.AppEventFileContentRegex.MatchString(f.Filename) 176 } 177 178 // GetAppName returns the name of the App 179 func (f *fileEntry) GetAppName() string { 180 name := project.AppEventFileContentRegex.FindStringSubmatch(f.Filename)[1] 181 name = util.UcFirst(name) 182 return name 183 }