Explorar o código

Incorporate tickSize into burndown calculations

Signed-off-by: Robert Lin <robertlin1@gmail.com>
Robert Lin %!s(int64=6) %!d(string=hai) anos
pai
achega
77cf86aa26
Modificáronse 2 ficheiros con 58 adicións e 43 borrados
  1. 39 33
      leaves/burndown.go
  2. 19 10
      leaves/burndown_test.go

+ 39 - 33
leaves/burndown.go

@@ -320,6 +320,11 @@ func (analyser *BurndownAnalysis) Initialize(repository *git.Repository) error {
 			analyser.Granularity)
 		analyser.Sampling = analyser.Granularity
 	}
+	if analyser.tickSize == 0 {
+		def := items.DefaultTicksSinceStartTickSize * time.Hour
+		log.Printf("Warning: tick size was not set, adjusted to %v\n", def)
+		analyser.tickSize = items.DefaultTicksSinceStartTickSize * time.Hour
+	}
 	analyser.repository = repository
 	analyser.globalHistory = sparseHistory{}
 	analyser.fileHistories = map[string]sparseHistory{}
@@ -654,7 +659,7 @@ func (analyser *BurndownAnalysis) MergeResults(
 		wg.Add(1)
 		go func() {
 			defer wg.Done()
-			merged.GlobalHistory = mergeMatrices(
+			merged.GlobalHistory = analyser.mergeMatrices(
 				bar1.GlobalHistory, bar2.GlobalHistory,
 				bar1.granularity, bar1.sampling,
 				bar2.granularity, bar2.sampling,
@@ -677,7 +682,7 @@ func (analyser *BurndownAnalysis) MergeResults(
 					if ptrs[2] >= 0 {
 						m2 = bar2.PeopleHistories[ptrs[2]]
 					}
-					merged.PeopleHistories[i] = mergeMatrices(
+					merged.PeopleHistories[i] = analyser.mergeMatrices(
 						m1, m2,
 						bar1.granularity, bar1.sampling,
 						bar2.granularity, bar2.sampling,
@@ -730,8 +735,8 @@ func (analyser *BurndownAnalysis) MergeResults(
 	return merged
 }
 
-func roundTime(unix int64, dir bool) int {
-	ticks := float64(unix) / (3600 * 24)
+func (analyser *BurndownAnalysis) roundTime(unix int64, dir bool) int {
+	ticks := float64(unix) / analyser.tickSize.Seconds()
 	if dir {
 		return int(math.Ceil(ticks))
 	}
@@ -741,7 +746,7 @@ func roundTime(unix int64, dir bool) int {
 // mergeMatrices takes two [number of samples][number of bands] matrices,
 // resamples them to ticks so that they become square, sums and resamples back to the
 // least of (sampling1, sampling2) and (granularity1, granularity2).
-func mergeMatrices(m1, m2 DenseHistory, granularity1, sampling1, granularity2, sampling2 int,
+func (analyser *BurndownAnalysis) mergeMatrices(m1, m2 DenseHistory, granularity1, sampling1, granularity2, sampling2 int,
 	c1, c2 *core.CommonAnalysisResult) DenseHistory {
 	commonMerged := c1.Copy()
 	commonMerged.Merge(c2)
@@ -758,18 +763,19 @@ func mergeMatrices(m1, m2 DenseHistory, granularity1, sampling1, granularity2, s
 		granularity = granularity2
 	}
 
-	size := roundTime(commonMerged.EndTime, true) - roundTime(commonMerged.BeginTime, false)
-	daily := make([][]float32, size+granularity)
-	for i := range daily {
-		daily[i] = make([]float32, size+sampling)
+	size := analyser.roundTime(commonMerged.EndTime, true) -
+		analyser.roundTime(commonMerged.BeginTime, false)
+	perTick := make([][]float32, size+granularity)
+	for i := range perTick {
+		perTick[i] = make([]float32, size+sampling)
 	}
 	if len(m1) > 0 {
-		addBurndownMatrix(m1, granularity1, sampling1, daily,
-			roundTime(c1.BeginTime, false)-roundTime(commonMerged.BeginTime, false))
+		addBurndownMatrix(m1, granularity1, sampling1, perTick,
+			analyser.roundTime(c1.BeginTime, false)-analyser.roundTime(commonMerged.BeginTime, false))
 	}
 	if len(m2) > 0 {
-		addBurndownMatrix(m2, granularity2, sampling2, daily,
-			roundTime(c2.BeginTime, false)-roundTime(commonMerged.BeginTime, false))
+		addBurndownMatrix(m2, granularity2, sampling2, perTick,
+			analyser.roundTime(c2.BeginTime, false)-analyser.roundTime(commonMerged.BeginTime, false))
 	}
 
 	// convert daily to [][]int64
@@ -780,7 +786,7 @@ func mergeMatrices(m1, m2 DenseHistory, granularity1, sampling1, granularity2, s
 		for j := 0; j < len(result[i]); j++ {
 			accum := float32(0)
 			for k := j * granularity; k < (j+1)*granularity; k++ {
-				accum += daily[sampledIndex][k]
+				accum += perTick[sampledIndex][k]
 			}
 			result[i][j] = int64(accum)
 		}
@@ -794,7 +800,7 @@ func mergeMatrices(m1, m2 DenseHistory, granularity1, sampling1, granularity2, s
 // Rows: *at least* len(matrix) * sampling + offset
 // Columns: *at least* len(matrix[...]) * granularity + offset
 // `matrix` can be sparse, so that the last columns which are equal to 0 are truncated.
-func addBurndownMatrix(matrix DenseHistory, granularity, sampling int, accdaily [][]float32, offset int) {
+func addBurndownMatrix(matrix DenseHistory, granularity, sampling int, accPerTick [][]float32, offset int) {
 	// Determine the maximum number of bands; the actual one may be larger but we do not care
 	maxCols := 0
 	for _, row := range matrix {
@@ -803,17 +809,17 @@ func addBurndownMatrix(matrix DenseHistory, granularity, sampling int, accdaily
 		}
 	}
 	neededRows := len(matrix)*sampling + offset
-	if len(accdaily) < neededRows {
-		log.Panicf("merge bug: too few daily rows: required %d, have %d",
-			neededRows, len(accdaily))
+	if len(accPerTick) < neededRows {
+		log.Panicf("merge bug: too few per-tick rows: required %d, have %d",
+			neededRows, len(accPerTick))
 	}
-	if len(accdaily[0]) < maxCols {
-		log.Panicf("merge bug: too few daily cols: required %d, have %d",
-			maxCols, len(accdaily[0]))
+	if len(accPerTick[0]) < maxCols {
+		log.Panicf("merge bug: too few per-tick cols: required %d, have %d",
+			maxCols, len(accPerTick[0]))
 	}
-	daily := make([][]float32, len(accdaily))
-	for i, row := range accdaily {
-		daily[i] = make([]float32, len(row))
+	perTick := make([][]float32, len(accPerTick))
+	for i, row := range accPerTick {
+		perTick[i] = make([]float32, len(row))
 	}
 	for x := 0; x < maxCols; x++ {
 		for y := 0; y < len(matrix); y++ {
@@ -828,9 +834,9 @@ func addBurndownMatrix(matrix DenseHistory, granularity, sampling int, accdaily
 				k := float32(matrix[y][x]) / startVal // <= 1
 				scale := float32((y+1)*sampling - startIndex)
 				for i := x * granularity; i < (x+1)*granularity; i++ {
-					initial := daily[startIndex-1+offset][i+offset]
+					initial := perTick[startIndex-1+offset][i+offset]
 					for j := startIndex; j < (y+1)*sampling; j++ {
-						daily[j+offset][i+offset] = initial * (1 + (k-1)*float32(j-startIndex+1)/scale)
+						perTick[j+offset][i+offset] = initial * (1 + (k-1)*float32(j-startIndex+1)/scale)
 					}
 				}
 			}
@@ -849,13 +855,13 @@ func addBurndownMatrix(matrix DenseHistory, granularity, sampling int, accdaily
 				avg := (finishVal - initial) / float32(finishIndex-startIndex)
 				for j := y * sampling; j < finishIndex; j++ {
 					for i := startIndex; i <= j; i++ {
-						daily[j+offset][i+offset] = avg
+						perTick[j+offset][i+offset] = avg
 					}
 				}
 				// copy [x*g..y*s)
 				for j := y * sampling; j < finishIndex; j++ {
 					for i := x * granularity; i < y*sampling; i++ {
-						daily[j+offset][i+offset] = daily[j-1+offset][i+offset]
+						perTick[j+offset][i+offset] = perTick[j-1+offset][i+offset]
 					}
 				}
 			}
@@ -891,7 +897,7 @@ func addBurndownMatrix(matrix DenseHistory, granularity, sampling int, accdaily
 					avg := float32(matrix[y][x]) / float32((y+1)*sampling-x*granularity)
 					for j := x * granularity; j < (y+1)*sampling; j++ {
 						for i := x * granularity; i <= j; i++ {
-							daily[j+offset][i+offset] = avg
+							perTick[j+offset][i+offset] = avg
 						}
 					}
 				}
@@ -951,14 +957,14 @@ func addBurndownMatrix(matrix DenseHistory, granularity, sampling int, accdaily
 			}
 		}
 	}
-	for y := len(matrix) * sampling; y+offset < len(daily); y++ {
-		copy(daily[y+offset], daily[len(matrix)*sampling-1+offset])
+	for y := len(matrix) * sampling; y+offset < len(perTick); y++ {
+		copy(perTick[y+offset], perTick[len(matrix)*sampling-1+offset])
 	}
 	// the original matrix has been resampled by tick
 	// add it to the accumulator
-	for y, row := range daily {
+	for y, row := range perTick {
 		for x, val := range row {
-			accdaily[y][x] += val
+			accPerTick[y][x] += val
 		}
 	}
 }

+ 19 - 10
leaves/burndown_test.go

@@ -69,6 +69,7 @@ func TestBurndownConfigure(t *testing.T) {
 	facts[ConfigBurndownHibernationThreshold] = 100
 	facts[ConfigBurndownHibernationToDisk] = true
 	facts[ConfigBurndownHibernationDirectory] = "xxx"
+	facts[items.FactTickSize] = 24 * time.Hour
 	facts[identity.FactIdentityDetectorPeopleCount] = 5
 	facts[identity.FactIdentityDetectorReversedPeopleDict] = bd.Requires()
 	assert.Nil(t, bd.Configure(facts))
@@ -80,6 +81,7 @@ func TestBurndownConfigure(t *testing.T) {
 	assert.True(t, bd.HibernationToDisk)
 	assert.Equal(t, bd.HibernationDirectory, "xxx")
 	assert.Equal(t, bd.Debug, true)
+	assert.Equal(t, bd.tickSize, 24*time.Hour)
 	assert.Equal(t, bd.reversedPeopleDict, bd.Requires())
 	facts[ConfigBurndownTrackPeople] = false
 	facts[identity.FactIdentityDetectorPeopleCount] = 50
@@ -1142,7 +1144,9 @@ func TestBurndownMergeGlobalHistory(t *testing.T) {
 	res2.PeopleMatrix[1][1] = 600
 	res2.PeopleMatrix[1][2] = 700
 	res2.PeopleMatrix[1][3] = 800
-	bd := BurndownAnalysis{}
+	bd := BurndownAnalysis{
+		tickSize: 24 * time.Hour,
+	}
 	merged := bd.MergeResults(res1, res2, &c1, &c2).(BurndownResult)
 	assert.Equal(t, merged.granularity, 19)
 	assert.Equal(t, merged.sampling, 14)
@@ -1213,7 +1217,9 @@ func TestBurndownMergeNils(t *testing.T) {
 		CommitsNumber: 10,
 		RunTime:       100000,
 	}
-	bd := BurndownAnalysis{}
+	bd := BurndownAnalysis{
+		tickSize: 24 * time.Hour,
+	}
 	merged := bd.MergeResults(res1, res2, &c1, &c2).(BurndownResult)
 	assert.Equal(t, merged.granularity, 19)
 	assert.Equal(t, merged.sampling, 14)
@@ -1366,17 +1372,17 @@ func TestBurndownAddBurndownMatrix(t *testing.T) {
 		[]int64{7181, 18750, 55841, 0},
 		[]int64{6345, 16704, 17110, 55981},
 	}
-	daily := make([][]float32, 4*30)
-	for i := range daily {
-		daily[i] = make([]float32, 4*30)
+	perTick := make([][]float32, 4*30)
+	for i := range perTick {
+		perTick[i] = make([]float32, 4*30)
 	}
-	addBurndownMatrix(h, 30, 30, daily, 0)
+	addBurndownMatrix(h, 30, 30, perTick, 0)
 	sum := func(x, y int) int64 {
 		var accum float32
 		row := (y+1)*30 - 1
 		offset := x * 30
 		for i := offset; i < offset+30; i++ {
-			accum += daily[row][i]
+			accum += perTick[row][i]
 		}
 		return int64(accum)
 	}
@@ -1458,13 +1464,14 @@ func TestBurndownMergeMatrices(t *testing.T) {
 		CommitsNumber: 6982,
 		RunTime:       1567214,
 	}
-	nh := mergeMatrices(h, nil, 30, 30, 30, 30, cr, cr)
+	bd := BurndownAnalysis{tickSize: 24 * time.Hour}
+	nh := bd.mergeMatrices(h, nil, 30, 30, 30, 30, cr, cr)
 	for y, row := range nh {
 		for x, v := range row {
 			assert.InDelta(t, v, h[y][x], 1, fmt.Sprintf("y=%d x=%d", y, x))
 		}
 	}
-	nh = mergeMatrices(h, h, 30, 30, 30, 30, cr, cr)
+	nh = bd.mergeMatrices(h, h, 30, 30, 30, 30, cr, cr)
 	for y, row := range nh {
 		for x, v := range row {
 			assert.InDelta(t, v, h[y][x]*2, 1, fmt.Sprintf("y=%d x=%d", y, x))
@@ -1514,7 +1521,9 @@ func TestBurndownMergePeopleHistories(t *testing.T) {
 		CommitsNumber: 10,
 		RunTime:       100000,
 	}
-	bd := BurndownAnalysis{}
+	bd := BurndownAnalysis{
+		tickSize: 24 * time.Hour,
+	}
 	merged := bd.MergeResults(res1, res2, &c1, &c2).(BurndownResult)
 	mh := [][]int64{
 		{560, 0, 0, 0},