|
|
package projects
import ( "git.aiterp.net/stufflog3/stufflog3/entities" "git.aiterp.net/stufflog3/stufflog3/models" "git.aiterp.net/stufflog3/stufflog3/usecases/items" "git.aiterp.net/stufflog3/stufflog3/usecases/scopes" "math" "sort" "time" )
type Entry struct { ID int `json:"id"` OwnerID string `json:"ownerId"` CreatedTime time.Time `json:"createdTime"` Name string `json:"name"` Status models.Status `json:"status"` StatusName string `json:"statusName"` OwnerName string `json:"ownerName,omitempty"` Tags []string `json:"tags"` }
func generateEntry(project entities.Project, scope scopes.Result) Entry { return Entry{ ID: project.ID, OwnerID: project.OwnerID, CreatedTime: project.CreatedTime, Name: project.Name, Status: project.Status, Tags: project.Tags, StatusName: scope.StatusName(project.Status), OwnerName: scope.MemberName(project.OwnerID), } }
type Result struct { entities.Project OwnerName string `json:"ownerName"` StatusName string `json:"statusName"` TotalAcquired float64 `json:"totalAcquired"` TotalRequired float64 `json:"totalRequired"` TotalPlanned float64 `json:"totalPlanned"` Requirements []RequirementResult `json:"requirements"` }
func (r *Result) Requirement(id int) *RequirementResult { for _, requirement := range r.Requirements { if id == requirement.ID { return &requirement } }
return nil }
type RequirementResult struct { ID int `json:"id"` Name string `json:"name"` Description string `json:"description"` Status models.Status `json:"status"` StatusName string `json:"statusName"` TotalAcquired float64 `json:"totalAcquired"` TotalRequired float64 `json:"totalRequired"` TotalPlanned float64 `json:"totalPlanned"` IsCoarse bool `json:"isCoarse"` AggregateRequired int `json:"aggregateRequired"` Stats []RequirementResultStat `json:"stats"` Items []items.Result `json:"items,omitempty"` Tags []string `json:"tags"`
Requirement entities.Requirement `json:"-"` }
func (r *RequirementResult) Stat(id int) *RequirementResultStat { for _, stat := range r.Stats { if stat.ID == id { return &stat } }
return nil }
func (r *RequirementResult) refresh(scope scopes.Result) { r.Name = r.Requirement.Name r.Description = r.Requirement.Description r.Status = r.Requirement.Status r.StatusName = scope.StatusName(r.Requirement.Status) }
func (r *RequirementResult) updateStat(stat entities.RequirementStat) bool { if stat.Required < 0 { for i, stat2 := range r.Stats { if stat2.ID == stat.StatID { r.Stats = append(r.Stats[:i], r.Stats[i+1:]...) return true } } } else { for i, stat2 := range r.Stats { if stat2.ID == stat.StatID { r.Stats[i].Required = stat.Required return true } } }
return false }
type RequirementResultStat struct { ID int `json:"id"` Name string `json:"name"` Weight float64 `json:"weight"` Acquired int `json:"acquired"` Required int `json:"required"` Planned int `json:"planned"` }
func generateResult( project entities.Project, scope scopes.Result, requirement []entities.Requirement, requirementStats []entities.RequirementStat, projectItems []items.Result, ) *Result { res := Result{ Project: project, OwnerName: scope.MemberName(project.OwnerID), StatusName: scope.StatusName(project.Status), Requirements: make([]RequirementResult, 0, 8), }
for _, req := range requirement { if req.ProjectID != project.ID { continue }
resReq := generateRequirementResult(req, scope, requirementStats, projectItems)
res.TotalRequired += resReq.TotalRequired res.TotalPlanned += resReq.TotalPlanned if req.Status.Ended() { res.TotalAcquired += resReq.TotalRequired } else { res.TotalAcquired += resReq.TotalAcquired }
res.Requirements = append(res.Requirements, resReq) }
sort.Slice(res.Requirements, func(i, j int) bool { ri := res.Requirements[i] rj := res.Requirements[j] if ri.Status != rj.Status { return ri.Status.Less(rj.Status) }
return ri.ID < rj.ID })
return &res }
func generateRequirementResult(req entities.Requirement, scope scopes.Result, requirementStats []entities.RequirementStat, projectItems []items.Result) RequirementResult { resReq := RequirementResult{ ID: req.ID, Name: req.Name, Description: req.Description, Status: req.Status, StatusName: scope.StatusName(req.Status), IsCoarse: req.IsCoarse, AggregateRequired: req.AggregateRequired, Stats: make([]RequirementResultStat, 0, 8), Items: make([]items.Result, 0, 8), Tags: req.Tags,
Requirement: req, }
resStats := make(map[int]*RequirementResultStat) for _, reqStat := range requirementStats { if reqStat.RequirementID != req.ID { continue }
resStat := RequirementResultStat{ ID: reqStat.StatID, Required: reqStat.Required, } for _, stat := range scope.Stats { if stat.ID == resStat.ID { resStat.Name = stat.Name resStat.Weight = stat.Weight break } }
resStats[resStat.ID] = &resStat }
for _, item := range projectItems { if item.RequirementID == nil || *item.RequirementID != req.ID { continue }
for _, stat := range item.Stats { if resStats[stat.ID] != nil { if item.AcquiredTime != nil { resStats[stat.ID].Acquired += stat.Acquired } resStats[stat.ID].Planned += stat.Required } }
item.Project = nil item.Requirement = nil
resReq.Items = append(resReq.Items, item) }
// Sort items so that they're in order of created, with acquired items being in order of acquired below.
sort.Slice(resReq.Items, func(i, j int) bool { ii := resReq.Items[i] ij := resReq.Items[j] if ii.AcquiredTime != nil && ij.AcquiredTime != nil { return ii.AcquiredTime.Before(*ij.AcquiredTime) } else if ii.AcquiredTime != nil && ij.AcquiredTime == nil { return false } else if ii.AcquiredTime == nil && ij.AcquiredTime != nil { return true }
return ii.CreatedTime.Before(ij.CreatedTime) })
for _, stat := range scope.Stats { if rs := resStats[stat.ID]; rs != nil && rs.Required > 0 { resReq.Stats = append(resReq.Stats, *rs) } } for _, stat := range scope.Stats { if rs := resStats[stat.ID]; rs != nil && rs.Required == 0 { resReq.Stats = append(resReq.Stats, *rs) } }
hasRequiredStats := false for _, stat := range resReq.Stats { if stat.Required > 0 { hasRequiredStats = true break } }
if hasRequiredStats { totalAcquired := 0.0 totalRequired := 0.0 totalPlanned := 0.0 for _, stat := range resReq.Stats { if stat.Required > 0 { totalAcquired += math.Min(float64(stat.Acquired), float64(stat.Required)) * stat.Weight totalRequired += float64(stat.Required) * stat.Weight totalPlanned += math.Min(float64(stat.Planned), float64(stat.Required)) * stat.Weight } } resReq.TotalRequired += totalRequired resReq.TotalAcquired += math.Min(totalAcquired, totalRequired) resReq.TotalPlanned += math.Min(totalPlanned, totalRequired) } else { for _, item := range resReq.Items { if item.AcquiredTime != nil { resReq.TotalAcquired += item.WeightedRequired }
resReq.TotalRequired += item.WeightedRequired } resReq.TotalPlanned = resReq.TotalRequired
if req.AggregateRequired > 0 { resReq.TotalRequired = float64(req.AggregateRequired) } }
if projectItems == nil { resReq.Items = nil }
return resReq }
|