|
@@ -307,7 +307,7 @@ func (analyser *BurndownAnalysis) MergeResults(
|
|
|
r1, r2 interface{}, c1, c2 *CommonAnalysisResult) interface{} {
|
|
|
bar1 := r1.(BurndownResult)
|
|
|
bar2 := r2.(BurndownResult)
|
|
|
- merged := BurndownResult{}
|
|
|
+ merged := BurndownResult{}
|
|
|
if bar1.sampling < bar2.sampling {
|
|
|
merged.sampling = bar1.sampling
|
|
|
} else {
|
|
@@ -318,9 +318,20 @@ func (analyser *BurndownAnalysis) MergeResults(
|
|
|
} else {
|
|
|
merged.granularity = bar2.granularity
|
|
|
}
|
|
|
- people := make([]string, len(bar1.reversedPeopleDict))
|
|
|
- copy(people, bar1.reversedPeopleDict)
|
|
|
- merged.reversedPeopleDict = append(people, bar2.reversedPeopleDict...)
|
|
|
+ people := map[string]int{}
|
|
|
+ for _, id := range bar1.reversedPeopleDict {
|
|
|
+ people[id] = len(people)
|
|
|
+ }
|
|
|
+ for _, id := range bar2.reversedPeopleDict {
|
|
|
+ if _, exists := people[id]; !exists {
|
|
|
+ people[id] = len(people)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ merged.reversedPeopleDict = make([]string, len(people))
|
|
|
+ for name, index := range people {
|
|
|
+ merged.reversedPeopleDict[index] = name
|
|
|
+ }
|
|
|
+
|
|
|
// interpolate to daily and sum
|
|
|
_ = bar1
|
|
|
_ = bar2
|
|
@@ -328,8 +339,68 @@ func (analyser *BurndownAnalysis) MergeResults(
|
|
|
// return merged
|
|
|
}
|
|
|
|
|
|
-func interpolateMatrix(matrix [][]int64, granularity, sampling int, daily [][]int64, offset int) {
|
|
|
-
|
|
|
+func mergeMatrices(m1, m2 [][]int64, granularity1, sampling1, granularity2, sampling2 int,
|
|
|
+ c1, c2 *CommonAnalysisResult) {
|
|
|
+ commonMerged := CommonAnalysisResult{}
|
|
|
+ commonMerged.Merge(c1)
|
|
|
+ commonMerged.Merge(c2)
|
|
|
+ size := (commonMerged.EndTime - commonMerged.BeginTime) / (3600 * 24)
|
|
|
+ daily := make([][]float32, size)
|
|
|
+ for i := range daily {
|
|
|
+ daily[i] = make([]float32, size)
|
|
|
+ }
|
|
|
+ addMatrix(m1, granularity1, sampling1, daily,
|
|
|
+ int(c1.BeginTime-commonMerged.BeginTime)/(3600*24))
|
|
|
+ addMatrix(m2, granularity2, sampling2, daily,
|
|
|
+ int(c2.BeginTime-commonMerged.BeginTime)/(3600*24))
|
|
|
+ // convert daily to [][]int64
|
|
|
+}
|
|
|
+
|
|
|
+func addMatrix(matrix [][]int64, granularity, sampling int, daily [][]float32, offset int) {
|
|
|
+ /*
|
|
|
+ daily_matrix = numpy.zeros(
|
|
|
+ (matrix.shape[0] * granularity, matrix.shape[1] * sampling),
|
|
|
+ dtype=numpy.float32)
|
|
|
+ */
|
|
|
+ // Determine the maximum number of bands; the actual one may be larger but we do not care
|
|
|
+ maxCols := 0
|
|
|
+ for _, row := range matrix {
|
|
|
+ if maxCols < len(row) {
|
|
|
+ maxCols = len(row)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // Ported from labours.py load_burndown()
|
|
|
+ for y := 0; y < maxCols; y++ {
|
|
|
+ for x := 0; x < len(matrix); x++ {
|
|
|
+ if (y+1)*granularity <= x*sampling {
|
|
|
+ // interpolate
|
|
|
+ var previous int64
|
|
|
+ if x > 0 && y < len(matrix[x-1]) {
|
|
|
+ previous = matrix[x-1][y]
|
|
|
+ }
|
|
|
+ for i := 0; i < sampling; i++ {
|
|
|
+ var value float32
|
|
|
+ if y < len(matrix[x]) {
|
|
|
+ value = (float32(previous) +
|
|
|
+ float32((matrix[x][y]-previous)*int64(i))/float32(sampling)) / float32(granularity)
|
|
|
+ } else {
|
|
|
+ value = float32(previous) *
|
|
|
+ (float32(1) - float32(i)/float32(sampling)) / float32(granularity)
|
|
|
+ }
|
|
|
+ for j := y * granularity; j < (y+1)*granularity; j++ {
|
|
|
+ daily[j+offset][x*sampling+i+offset] += value
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else if y*granularity <= (x+1)*sampling && y < len(matrix[x]) {
|
|
|
+ // fill constant
|
|
|
+ for suby := y*granularity + offset; suby < (y+1)*granularity+offset; suby++ {
|
|
|
+ for subx := suby; subx < (x+1)*sampling+offset; subx++ {
|
|
|
+ daily[suby][subx] += float32(matrix[x][y]) / float32(granularity)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
func (analyser *BurndownAnalysis) serializeText(result *BurndownResult, writer io.Writer) {
|