130 lines
3.0 KiB
Go
130 lines
3.0 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"sort"
|
|
"strings"
|
|
|
|
"verstak/internal/core/activity"
|
|
)
|
|
|
|
// GetSuggestions analyzes today's activity and returns worklog suggestions.
|
|
func (a *App) GetSuggestions() ([]activity.Suggestion, error) {
|
|
events, err := a.activity.ListTodayEvents()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(events) == 0 {
|
|
return nil, nil
|
|
}
|
|
|
|
type acc struct {
|
|
title string
|
|
kind string
|
|
events []activity.Event
|
|
}
|
|
grouped := make(map[string]*acc)
|
|
for _, e := range events {
|
|
grp, ok := grouped[e.NodeID]
|
|
if !ok {
|
|
n, err := a.nodes.GetActive(e.NodeID)
|
|
title := ""
|
|
kind := ""
|
|
if err == nil && n != nil {
|
|
title = n.Title
|
|
kind = n.Type
|
|
}
|
|
grp = &acc{title: title, kind: kind}
|
|
grouped[e.NodeID] = grp
|
|
}
|
|
grp.events = append(grp.events, e)
|
|
}
|
|
|
|
var suggestions []activity.Suggestion
|
|
for nodeID, grp := range grouped {
|
|
if grp.title == "" {
|
|
continue
|
|
}
|
|
hasEntries, err := a.worklog.HasTodayEntries(nodeID)
|
|
if err != nil || hasEntries {
|
|
continue
|
|
}
|
|
|
|
noteCount := 0
|
|
fileCount := 0
|
|
actionCount := 0
|
|
otherCount := 0
|
|
for _, e := range grp.events {
|
|
switch e.EventType {
|
|
case activity.TypeNoteCreated, activity.TypeNoteUpdated, activity.TypeNoteDeleted:
|
|
noteCount++
|
|
case activity.TypeFileAdded, activity.TypeFileDeleted, activity.TypeFileRenamed,
|
|
activity.TypeFileCopied, activity.TypeFileMoved,
|
|
activity.TypeFolderAdded, activity.TypeFolderDeleted, activity.TypeFolderRenamed:
|
|
fileCount++
|
|
case activity.TypeActionCreated, activity.TypeActionDone:
|
|
actionCount++
|
|
default:
|
|
otherCount++
|
|
}
|
|
}
|
|
|
|
summary := buildSuggestionSummary(noteCount, fileCount, actionCount, otherCount)
|
|
if summary == "" {
|
|
continue
|
|
}
|
|
|
|
suggestions = append(suggestions, activity.Suggestion{
|
|
NodeID: nodeID,
|
|
NodeTitle: grp.title,
|
|
Summary: summary,
|
|
SuggestedMin: suggestMinutes(noteCount + fileCount + actionCount + otherCount),
|
|
EventCount: len(grp.events),
|
|
NodeKind: grp.kind,
|
|
})
|
|
}
|
|
|
|
sort.Slice(suggestions, func(i, j int) bool {
|
|
return suggestions[i].EventCount > suggestions[j].EventCount
|
|
})
|
|
|
|
return suggestions, nil
|
|
}
|
|
|
|
// AcceptSuggestion creates a worklog entry from a suggestion.
|
|
func (a *App) AcceptSuggestion(s activity.Suggestion) (*WorklogDTO, error) {
|
|
return a.CreateWorklog(s.NodeID, s.Summary, s.SuggestedMin)
|
|
}
|
|
|
|
func buildSuggestionSummary(noteCount, fileCount, actionCount, otherCount int) string {
|
|
var parts []string
|
|
if noteCount > 0 {
|
|
parts = append(parts, fmt.Sprintf("заметки (%d)", noteCount))
|
|
}
|
|
if fileCount > 0 {
|
|
parts = append(parts, fmt.Sprintf("файлы (%d)", fileCount))
|
|
}
|
|
if actionCount > 0 {
|
|
parts = append(parts, fmt.Sprintf("действия (%d)", actionCount))
|
|
}
|
|
if otherCount > 0 {
|
|
parts = append(parts, fmt.Sprintf("события (%d)", otherCount))
|
|
}
|
|
return strings.Join(parts, ", ")
|
|
}
|
|
|
|
func suggestMinutes(totalEvents int) int {
|
|
switch {
|
|
case totalEvents >= 15:
|
|
return 120
|
|
case totalEvents >= 10:
|
|
return 90
|
|
case totalEvents >= 6:
|
|
return 60
|
|
case totalEvents >= 3:
|
|
return 30
|
|
default:
|
|
return 15
|
|
}
|
|
}
|