diff --git a/kernel/av/table.go b/kernel/av/table.go index 3928ac4da..21e5875da 100644 --- a/kernel/av/table.go +++ b/kernel/av/table.go @@ -2105,8 +2105,8 @@ func (table *Table) calcColRollup(col *TableColumn, colIndex int) { for _, row := range table.Rows { if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Rollup { for _, content := range row.Cells[colIndex].Value.Rollup.Contents { - if !uniqueValues[content] { - uniqueValues[content] = true + if !uniqueValues[content.String()] { + uniqueValues[content.String()] = true countUniqueValues++ } } diff --git a/kernel/av/value.go b/kernel/av/value.go index 64b724dff..29c3bb966 100644 --- a/kernel/av/value.go +++ b/kernel/av/value.go @@ -147,10 +147,14 @@ func (value *Value) String() string { } return strings.Join(ret, " ") case KeyTypeRollup: - if nil == value.Rollup { + if nil == value.Rollup || nil == value.Rollup.Contents { return "" } - return strings.Join(value.Rollup.Contents, " ") + var ret []string + for _, v := range value.Rollup.Contents { + ret = append(ret, v.String()) + } + return strings.Join(ret, " ") default: return "" } @@ -468,7 +472,7 @@ type ValueRelation struct { } type ValueRollup struct { - Contents []string `json:"contents"` + Contents []*Value `json:"contents"` } func (r *ValueRollup) RenderContents(calc *RollupCalc) { @@ -479,115 +483,113 @@ func (r *ValueRollup) RenderContents(calc *RollupCalc) { switch calc.Operator { case CalcOperatorNone: case CalcOperatorCountAll: - r.Contents = []string{strconv.Itoa(len(r.Contents))} + r.Contents = []*Value{{Type: KeyTypeNumber, Number: NewValueNumber(float64(len(r.Contents)))}} case CalcOperatorCountValues: - r.Contents = []string{strconv.Itoa(len(r.Contents))} + r.Contents = []*Value{{Type: KeyTypeNumber, Number: NewValueNumber(float64(len(r.Contents)))}} case CalcOperatorCountUniqueValues: countUniqueValues := 0 uniqueValues := map[string]bool{} for _, v := range r.Contents { - if !uniqueValues[v] { - uniqueValues[v] = true + if _, ok := uniqueValues[v.String()]; !ok { + uniqueValues[v.String()] = true countUniqueValues++ } } - r.Contents = []string{strconv.Itoa(countUniqueValues)} + r.Contents = []*Value{{Type: KeyTypeNumber, Number: NewValueNumber(float64(countUniqueValues))}} case CalcOperatorCountEmpty: countEmpty := 0 for _, v := range r.Contents { - if "" == v { + if "" == v.String() { countEmpty++ } } - r.Contents = []string{strconv.Itoa(countEmpty)} + r.Contents = []*Value{{Type: KeyTypeNumber, Number: NewValueNumber(float64(countEmpty))}} case CalcOperatorCountNotEmpty: countNonEmpty := 0 for _, v := range r.Contents { - if "" != v { + if "" != v.String() { countNonEmpty++ } } - r.Contents = []string{strconv.Itoa(countNonEmpty)} + r.Contents = []*Value{{Type: KeyTypeNumber, Number: NewValueNumber(float64(countNonEmpty))}} case CalcOperatorPercentEmpty: countEmpty := 0 for _, v := range r.Contents { - if "" == v { + if "" == v.String() { countEmpty++ } } - r.Contents = []string{strconv.Itoa(countEmpty*100/len(r.Contents)) + "%"} + r.Contents = []*Value{{Type: KeyTypeNumber, Number: NewValueNumber(float64(countEmpty * 100 / len(r.Contents)))}} case CalcOperatorPercentNotEmpty: countNonEmpty := 0 for _, v := range r.Contents { - if "" != v { + if "" != v.String() { countNonEmpty++ } } - r.Contents = []string{strconv.Itoa(countNonEmpty*100/len(r.Contents)) + "%"} + r.Contents = []*Value{{Type: KeyTypeNumber, Number: NewValueNumber(float64(countNonEmpty * 100 / len(r.Contents)))}} case CalcOperatorSum: sum := 0.0 for _, v := range r.Contents { - if "" != v { - n, _ := strconv.ParseFloat(v, 64) + if "" != v.String() { + n, _ := strconv.ParseFloat(v.String(), 64) sum += n } } - r.Contents = []string{strconv.FormatFloat(sum, 'f', -1, 64)} + r.Contents = []*Value{{Type: KeyTypeNumber, Number: NewValueNumber(sum)}} case CalcOperatorAverage: sum := 0.0 count := 0 for _, v := range r.Contents { - if "" != v { - n, _ := strconv.ParseFloat(v, 64) + if "" != v.String() { + n, _ := strconv.ParseFloat(v.String(), 64) sum += n count++ } } - r.Contents = []string{strconv.FormatFloat(sum/float64(count), 'f', -1, 64)} + if 0 < count { + r.Contents = []*Value{{Type: KeyTypeNumber, Number: NewValueNumber(sum / float64(count))}} + } case CalcOperatorMedian: - numbers := []float64{} + var numbers []float64 for _, v := range r.Contents { - if "" != v { - n, _ := strconv.ParseFloat(v, 64) + if "" != v.String() { + n, _ := strconv.ParseFloat(v.String(), 64) numbers = append(numbers, n) } } sort.Float64s(numbers) if 0 < len(numbers) { - if 0 == len(numbers)%2 { - r.Contents = []string{strconv.FormatFloat((numbers[len(numbers)/2-1]+numbers[len(numbers)/2])/2, 'f', -1, 64)} - } else { - r.Contents = []string{strconv.FormatFloat(numbers[len(numbers)/2], 'f', -1, 64)} - } + r.Contents = []*Value{{Type: KeyTypeNumber, Number: NewValueNumber(numbers[len(numbers)/2])}} } case CalcOperatorMin: min := math.MaxFloat64 for _, v := range r.Contents { - if "" != v { - n, _ := strconv.ParseFloat(v, 64) + if "" != v.String() { + n, _ := strconv.ParseFloat(v.String(), 64) if n < min { min = n } } } - r.Contents = []string{strconv.FormatFloat(min, 'f', -1, 64)} + r.Contents = []*Value{{Type: KeyTypeNumber, Number: NewValueNumber(min)}} case CalcOperatorMax: max := -math.MaxFloat64 for _, v := range r.Contents { - if "" != v { - n, _ := strconv.ParseFloat(v, 64) + if "" != v.String() { + n, _ := strconv.ParseFloat(v.String(), 64) if n > max { max = n } } } - r.Contents = []string{strconv.FormatFloat(max, 'f', -1, 64)} + r.Contents = []*Value{{Type: KeyTypeNumber, Number: NewValueNumber(max)}} case CalcOperatorRange: min := math.MaxFloat64 max := -math.MaxFloat64 for _, v := range r.Contents { - if "" != v { - n, _ := strconv.ParseFloat(v, 64) + if "" != v.String() { + n, _ := strconv.ParseFloat(v.String(), 64) if n < min { min = n } @@ -596,38 +598,38 @@ func (r *ValueRollup) RenderContents(calc *RollupCalc) { } } } - r.Contents = []string{strconv.FormatFloat(max-min, 'f', -1, 64)} + r.Contents = []*Value{{Type: KeyTypeNumber, Number: NewValueNumber(max - min)}} case CalcOperatorChecked: countChecked := 0 for _, v := range r.Contents { - if "√" == v { + if "√" == v.String() { countChecked++ } } - r.Contents = []string{strconv.Itoa(countChecked)} + r.Contents = []*Value{{Type: KeyTypeNumber, Number: NewValueNumber(float64(countChecked))}} case CalcOperatorUnchecked: countUnchecked := 0 for _, v := range r.Contents { - if "√" != v { + if "√" != v.String() { countUnchecked++ } } - r.Contents = []string{strconv.Itoa(countUnchecked)} + r.Contents = []*Value{{Type: KeyTypeNumber, Number: NewValueNumber(float64(countUnchecked))}} case CalcOperatorPercentChecked: countChecked := 0 for _, v := range r.Contents { - if "√" == v { + if "√" == v.String() { countChecked++ } } - r.Contents = []string{strconv.Itoa(countChecked*100/len(r.Contents)) + "%"} + r.Contents = []*Value{{Type: KeyTypeNumber, Number: NewValueNumber(float64(countChecked * 100 / len(r.Contents)))}} case CalcOperatorPercentUnchecked: countUnchecked := 0 for _, v := range r.Contents { - if "√" != v { + if "√" != v.String() { countUnchecked++ } } - r.Contents = []string{strconv.Itoa(countUnchecked*100/len(r.Contents)) + "%"} + r.Contents = []*Value{{Type: KeyTypeNumber, Number: NewValueNumber(float64(countUnchecked * 100 / len(r.Contents)))}} } } diff --git a/kernel/model/attribute_view.go b/kernel/model/attribute_view.go index e0938e169..805025b71 100644 --- a/kernel/model/attribute_view.go +++ b/kernel/model/attribute_view.go @@ -212,7 +212,7 @@ func GetBlockAttributeViewKeys(blockID string) (ret []*BlockAttributeViewKeys) { switch kValues.Key.Type { case av.KeyTypeRollup: - kValues.Values = append(kValues.Values, &av.Value{ID: ast.NewNodeID(), KeyID: kValues.Key.ID, BlockID: blockID, Type: av.KeyTypeRollup, Rollup: &av.ValueRollup{Contents: []string{}}}) + kValues.Values = append(kValues.Values, &av.Value{ID: ast.NewNodeID(), KeyID: kValues.Key.ID, BlockID: blockID, Type: av.KeyTypeRollup, Rollup: &av.ValueRollup{Contents: []*av.Value{}}}) case av.KeyTypeTemplate: kValues.Values = append(kValues.Values, &av.Value{ID: ast.NewNodeID(), KeyID: kValues.Key.ID, BlockID: blockID, Type: av.KeyTypeTemplate, Template: &av.ValueTemplate{Content: ""}}) case av.KeyTypeCreated: @@ -260,7 +260,7 @@ func GetBlockAttributeViewKeys(blockID string) (ret []*BlockAttributeViewKeys) { destVal.Number.FormatNumber() } - kv.Values[0].Rollup.Contents = append(kv.Values[0].Rollup.Contents, destVal.String()) + kv.Values[0].Rollup.Contents = append(kv.Values[0].Rollup.Contents, destVal.Clone()) kv.Values[0].Rollup.RenderContents(kv.Key.Rollup.Calc) } } @@ -832,7 +832,7 @@ func renderAttributeViewTable(attrView *av.AttributeView, view *av.View) (ret *a destVal.Number.FormatNumber() } - cell.Value.Rollup.Contents = append(cell.Value.Rollup.Contents, destVal.String()) + cell.Value.Rollup.Contents = append(cell.Value.Rollup.Contents, destVal.Clone()) } cell.Value.Rollup.RenderContents(rollupKey.Rollup.Calc) diff --git a/kernel/treenode/node.go b/kernel/treenode/node.go index 97ed0848a..6f335c9f7 100644 --- a/kernel/treenode/node.go +++ b/kernel/treenode/node.go @@ -789,7 +789,7 @@ func renderAttributeViewTable(attrView *av.AttributeView, view *av.View) (ret *a destVal.Number.FormatNumber() } - cell.Value.Rollup.Contents = append(cell.Value.Rollup.Contents, destVal.String()) + cell.Value.Rollup.Contents = append(cell.Value.Rollup.Contents, destVal.Clone()) } cell.Value.Rollup.RenderContents(rollupKey.Rollup.Calc)