diff --git a/kernel/av/av.go b/kernel/av/av.go index bf6bfe173..71431bca7 100644 --- a/kernel/av/av.go +++ b/kernel/av/av.go @@ -203,8 +203,8 @@ type View struct { // GroupCalc 描述了分组计算规则和结果的结构。 type GroupCalc struct { - Field string `json:"field"` // 字段 ID - FieldCalc `json:"calc"` // 计算规则和结果 + Field string `json:"field"` // 字段 ID + FieldCalc *FieldCalc `json:"calc"` // 计算规则和结果 } // LayoutType 描述了视图布局类型。 @@ -267,15 +267,6 @@ func NewGalleryView() (ret *View) { // Viewable 描述了视图的接口。 type Viewable interface { - // Filter 根据视图中设置的过滤器进行过滤。 - Filter(attrView *AttributeView) - - // Sort 根据视图中设置的排序规则进行排序。 - Sort(attrView *AttributeView) - - // Calc 根据视图中设置的计算规则进行计算。 - Calc() - // GetType 获取视图的布局类型。 GetType() LayoutType @@ -284,6 +275,24 @@ type Viewable interface { // SetGroups 设置视图分组列表。 SetGroups(viewables []Viewable) + + // SetGroupCalc 设置视图分组计算规则和结果。 + SetGroupCalc(group *GroupCalc) + + // GetGroupCalc 获取视图分组计算规则和结果。 + GetGroupCalc() *GroupCalc + + // SetGroupName 设置分组名称。 + SetGroupName(name string) + + // SetGroupFolded 设置分组是否折叠。 + SetGroupFolded(folded bool) + + // SetGroupHidden 设置分组是否隐藏。 + SetGroupHidden(hidden bool) + + // SetGroupDefault 设置分组是否为默认分组。 + SetGroupDefault(defaulted bool) } func NewAttributeView(id string) (ret *AttributeView) { diff --git a/kernel/av/calc.go b/kernel/av/calc.go index 012e9a0e7..95a81113d 100644 --- a/kernel/av/calc.go +++ b/kernel/av/calc.go @@ -16,6 +16,13 @@ package av +import ( + "math" + "sort" + + "github.com/siyuan-note/siyuan/kernel/util" +) + // FieldCalc 描述了字段计算操作和结果的结构。 type FieldCalc struct { Operator CalcOperator `json:"operator"` // 计算操作符 @@ -47,3 +54,1794 @@ const ( CalcOperatorPercentChecked CalcOperator = "Percent checked" CalcOperatorPercentUnchecked CalcOperator = "Percent unchecked" ) + +func Calc(viewable Viewable) { + collection := viewable.(Collection) + for i, field := range collection.GetFields() { + calc := field.GetCalc() + if nil == calc || CalcOperatorNone == calc.Operator { + continue + } + + switch field.GetType() { + case KeyTypeBlock: + CalcFieldBlock(collection, field, i) + case KeyTypeText: + CalcFieldText(collection, field, i) + case KeyTypeNumber: + CalcFieldNumber(collection, field, i) + case KeyTypeDate: + CalcFieldDate(collection, field, i) + case KeyTypeSelect: + CalcFieldSelect(collection, field, i) + case KeyTypeMSelect: + CalcFieldMSelect(collection, field, i) + case KeyTypeURL: + CalcFieldURL(collection, field, i) + case KeyTypeEmail: + CalcFieldEmail(collection, field, i) + case KeyTypePhone: + CalcFieldPhone(collection, field, i) + case KeyTypeMAsset: + CalcFieldMAsset(collection, field, i) + case KeyTypeTemplate: + CalcFieldTemplate(collection, field, i) + case KeyTypeCreated: + CalcFieldCreated(collection, field, i) + case KeyTypeUpdated: + CalcFieldUpdated(collection, field, i) + case KeyTypeCheckbox: + CalcFieldCheckbox(collection, field, i) + case KeyTypeRelation: + CalcFieldRelation(collection, field, i) + case KeyTypeRollup: + CalcFieldRollup(collection, field, i) + } + } +} + +func CalcFieldTemplate(collection Collection, field Field, fieldIndex int) { + calc := field.GetCalc() + switch calc.Operator { + case CalcOperatorCountAll: + calc.Result = &Value{Number: NewFormattedValueNumber(float64(len(collection.GetItems())), NumberFormatNone)} + case CalcOperatorCountValues: + countValues := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Template && "" != values[fieldIndex].Template.Content { + countValues++ + } + } + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countValues), NumberFormatNone)} + case CalcOperatorCountUniqueValues: + countUniqueValues := 0 + uniqueValues := map[string]bool{} + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Template && "" != values[fieldIndex].Template.Content { + if !uniqueValues[values[fieldIndex].Template.Content] { + uniqueValues[values[fieldIndex].Template.Content] = true + countUniqueValues++ + } + } + } + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues), NumberFormatNone)} + case CalcOperatorCountEmpty: + countEmpty := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil == values[fieldIndex] || nil == values[fieldIndex].Template || "" == values[fieldIndex].Template.Content { + countEmpty++ + } + } + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countEmpty), NumberFormatNone)} + case CalcOperatorCountNotEmpty: + countNotEmpty := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Template && "" != values[fieldIndex].Template.Content { + countNotEmpty++ + } + } + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty), NumberFormatNone)} + case CalcOperatorPercentEmpty: + countEmpty := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil == values[fieldIndex] || nil == values[fieldIndex].Template || "" == values[fieldIndex].Template.Content { + countEmpty++ + } + } + if 0 < len(collection.GetItems()) { + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countEmpty)/float64(len(collection.GetItems())), NumberFormatPercent)} + } + case CalcOperatorPercentNotEmpty: + countNotEmpty := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Template && "" != values[fieldIndex].Template.Content { + countNotEmpty++ + } + } + if 0 < len(collection.GetItems()) { + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty)/float64(len(collection.GetItems())), NumberFormatPercent)} + } + case CalcOperatorPercentUniqueValues: + countUniqueValues := 0 + uniqueValues := map[string]bool{} + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Template && "" != values[fieldIndex].Template.Content { + if !uniqueValues[values[fieldIndex].Template.Content] { + uniqueValues[values[fieldIndex].Template.Content] = true + countUniqueValues++ + } + } + } + if 0 < len(collection.GetItems()) { + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues)/float64(len(collection.GetItems())), NumberFormatPercent)} + } + case CalcOperatorSum: + sum := 0.0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Template && "" != values[fieldIndex].Template.Content { + val, _ := util.Convert2Float(values[fieldIndex].Template.Content) + sum += val + } + } + calc.Result = &Value{Number: NewFormattedValueNumber(sum, field.GetNumberFormat())} + case CalcOperatorAverage: + sum := 0.0 + count := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Template && "" != values[fieldIndex].Template.Content { + val, _ := util.Convert2Float(values[fieldIndex].Template.Content) + sum += val + count++ + } + } + if 0 != count { + calc.Result = &Value{Number: NewFormattedValueNumber(sum/float64(count), field.GetNumberFormat())} + } + case CalcOperatorMedian: + calcValues := []float64{} + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Template && "" != values[fieldIndex].Template.Content { + val, _ := util.Convert2Float(values[fieldIndex].Template.Content) + calcValues = append(calcValues, val) + } + } + sort.Float64s(calcValues) + if len(calcValues) > 0 { + if len(calcValues)%2 == 0 { + calc.Result = &Value{Number: NewFormattedValueNumber((calcValues[len(calcValues)/2-1]+calcValues[len(calcValues)/2])/2, field.GetNumberFormat())} + } else { + calc.Result = &Value{Number: NewFormattedValueNumber(calcValues[len(calcValues)/2], field.GetNumberFormat())} + } + } + case CalcOperatorMin: + minVal := math.MaxFloat64 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Template && "" != values[fieldIndex].Template.Content { + val, _ := util.Convert2Float(values[fieldIndex].Template.Content) + if val < minVal { + minVal = val + } + } + } + if math.MaxFloat64 != minVal { + calc.Result = &Value{Number: NewFormattedValueNumber(minVal, field.GetNumberFormat())} + } + case CalcOperatorMax: + maxVal := -math.MaxFloat64 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Template && "" != values[fieldIndex].Template.Content { + val, _ := util.Convert2Float(values[fieldIndex].Template.Content) + if val > maxVal { + maxVal = val + } + } + } + if -math.MaxFloat64 != maxVal { + calc.Result = &Value{Number: NewFormattedValueNumber(maxVal, field.GetNumberFormat())} + } + case CalcOperatorRange: + minVal := math.MaxFloat64 + maxVal := -math.MaxFloat64 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Template && "" != values[fieldIndex].Template.Content { + val, _ := util.Convert2Float(values[fieldIndex].Template.Content) + if val < minVal { + minVal = val + } + if val > maxVal { + maxVal = val + } + } + } + if math.MaxFloat64 != minVal && -math.MaxFloat64 != maxVal { + calc.Result = &Value{Number: NewFormattedValueNumber(maxVal-minVal, field.GetNumberFormat())} + } + } +} + +func CalcFieldMAsset(collection Collection, field Field, fieldIndex int) { + calc := field.GetCalc() + switch calc.Operator { + case CalcOperatorCountAll: + calc.Result = &Value{Number: NewFormattedValueNumber(float64(len(collection.GetItems())), NumberFormatNone)} + case CalcOperatorCountValues: + countValues := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].MAsset && 0 < len(values[fieldIndex].MAsset) { + countValues += len(values[fieldIndex].MAsset) + } + } + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countValues), NumberFormatNone)} + case CalcOperatorCountUniqueValues: + countUniqueValues := 0 + uniqueValues := map[string]bool{} + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].MAsset && 0 < len(values[fieldIndex].MAsset) { + for _, sel := range values[fieldIndex].MAsset { + if _, ok := uniqueValues[sel.Content]; !ok { + uniqueValues[sel.Content] = true + countUniqueValues++ + } + } + } + } + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues), NumberFormatNone)} + case CalcOperatorCountEmpty: + countEmpty := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil == values[fieldIndex] || nil == values[fieldIndex].MAsset || 0 == len(values[fieldIndex].MAsset) { + countEmpty++ + } + } + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countEmpty), NumberFormatNone)} + case CalcOperatorCountNotEmpty: + countNotEmpty := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].MAsset && 0 < len(values[fieldIndex].MAsset) { + countNotEmpty++ + } + } + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty), NumberFormatNone)} + case CalcOperatorPercentEmpty: + countEmpty := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil == values[fieldIndex] || nil == values[fieldIndex].MAsset || 0 == len(values[fieldIndex].MAsset) { + countEmpty++ + } + } + if 0 < len(collection.GetItems()) { + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countEmpty)/float64(len(collection.GetItems())), NumberFormatPercent)} + } + case CalcOperatorPercentNotEmpty: + countNotEmpty := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].MAsset && 0 < len(values[fieldIndex].MAsset) { + countNotEmpty++ + } + } + if 0 < len(collection.GetItems()) { + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty)/float64(len(collection.GetItems())), NumberFormatPercent)} + } + case CalcOperatorPercentUniqueValues: + countUniqueValues := 0 + uniqueValues := map[string]bool{} + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].MAsset && 0 < len(values[fieldIndex].MAsset) { + for _, sel := range values[fieldIndex].MAsset { + if _, ok := uniqueValues[sel.Content]; !ok { + uniqueValues[sel.Content] = true + countUniqueValues++ + } + } + } + } + if 0 < len(collection.GetItems()) { + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues)/float64(len(collection.GetItems())), NumberFormatPercent)} + } + } +} + +func CalcFieldMSelect(collection Collection, field Field, fieldIndex int) { + calc := field.GetCalc() + switch calc.Operator { + case CalcOperatorCountAll: + calc.Result = &Value{Number: NewFormattedValueNumber(float64(len(collection.GetItems())), NumberFormatNone)} + case CalcOperatorCountValues: + countValues := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].MSelect && 0 < len(values[fieldIndex].MSelect) { + countValues += len(values[fieldIndex].MSelect) + } + } + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countValues), NumberFormatNone)} + case CalcOperatorCountUniqueValues: + countUniqueValues := 0 + uniqueValues := map[string]bool{} + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].MSelect && 0 < len(values[fieldIndex].MSelect) { + for _, sel := range values[fieldIndex].MSelect { + if _, ok := uniqueValues[sel.Content]; !ok { + uniqueValues[sel.Content] = true + countUniqueValues++ + } + } + } + } + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues), NumberFormatNone)} + case CalcOperatorCountEmpty: + countEmpty := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil == values[fieldIndex] || nil == values[fieldIndex].MSelect || 0 == len(values[fieldIndex].MSelect) { + countEmpty++ + } + } + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countEmpty), NumberFormatNone)} + case CalcOperatorCountNotEmpty: + countNotEmpty := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].MSelect && 0 < len(values[fieldIndex].MSelect) { + countNotEmpty++ + } + } + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty), NumberFormatNone)} + case CalcOperatorPercentEmpty: + countEmpty := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil == values[fieldIndex] || nil == values[fieldIndex].MSelect || 0 == len(values[fieldIndex].MSelect) { + countEmpty++ + } + } + if 0 < len(collection.GetItems()) { + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countEmpty)/float64(len(collection.GetItems())), NumberFormatPercent)} + } + case CalcOperatorPercentNotEmpty: + countNotEmpty := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].MSelect && 0 < len(values[fieldIndex].MSelect) { + countNotEmpty++ + } + } + if 0 < len(collection.GetItems()) { + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty)/float64(len(collection.GetItems())), NumberFormatPercent)} + } + case CalcOperatorPercentUniqueValues: + countUniqueValues := 0 + uniqueValues := map[string]bool{} + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].MSelect && 0 < len(values[fieldIndex].MSelect) { + for _, sel := range values[fieldIndex].MSelect { + if _, ok := uniqueValues[sel.Content]; !ok { + uniqueValues[sel.Content] = true + countUniqueValues++ + } + } + } + } + if 0 < len(collection.GetItems()) { + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues)/float64(len(collection.GetItems())), NumberFormatPercent)} + } + } +} + +func CalcFieldSelect(collection Collection, field Field, fieldIndex int) { + calc := field.GetCalc() + switch calc.Operator { + case CalcOperatorCountAll: + calc.Result = &Value{Number: NewFormattedValueNumber(float64(len(collection.GetItems())), NumberFormatNone)} + case CalcOperatorCountValues: + countValues := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].MSelect && 0 < len(values[fieldIndex].MSelect) && nil != values[fieldIndex].MSelect[0] && "" != values[fieldIndex].MSelect[0].Content { + countValues++ + } + } + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countValues), NumberFormatNone)} + case CalcOperatorCountUniqueValues: + countUniqueValues := 0 + uniqueValues := map[string]bool{} + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].MSelect && 0 < len(values[fieldIndex].MSelect) && nil != values[fieldIndex].MSelect[0] && "" != values[fieldIndex].MSelect[0].Content { + if _, ok := uniqueValues[values[fieldIndex].MSelect[0].Content]; !ok { + uniqueValues[values[fieldIndex].MSelect[0].Content] = true + countUniqueValues++ + } + } + } + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues), NumberFormatNone)} + case CalcOperatorCountEmpty: + countEmpty := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil == values[fieldIndex] || nil == values[fieldIndex].MSelect || 1 > len(values[fieldIndex].MSelect) || nil == values[fieldIndex].MSelect[0] || "" == values[fieldIndex].MSelect[0].Content { + countEmpty++ + } + } + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countEmpty), NumberFormatNone)} + case CalcOperatorCountNotEmpty: + countNotEmpty := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].MSelect && 0 < len(values[fieldIndex].MSelect) && nil != values[fieldIndex].MSelect[0] && "" != values[fieldIndex].MSelect[0].Content { + countNotEmpty++ + } + } + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty), NumberFormatNone)} + case CalcOperatorPercentEmpty: + countEmpty := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil == values[fieldIndex] || nil == values[fieldIndex].MSelect || 1 > len(values[fieldIndex].MSelect) || nil == values[fieldIndex].MSelect[0] || "" == values[fieldIndex].MSelect[0].Content { + countEmpty++ + } + } + if 0 < len(collection.GetItems()) { + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countEmpty)/float64(len(collection.GetItems())), NumberFormatPercent)} + } + case CalcOperatorPercentNotEmpty: + countNotEmpty := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].MSelect && 0 < len(values[fieldIndex].MSelect) && nil != values[fieldIndex].MSelect[0] && "" != values[fieldIndex].MSelect[0].Content { + countNotEmpty++ + } + } + if 0 < len(collection.GetItems()) { + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty)/float64(len(collection.GetItems())), NumberFormatPercent)} + } + case CalcOperatorPercentUniqueValues: + countUniqueValues := 0 + uniqueValues := map[string]bool{} + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].MSelect && 0 < len(values[fieldIndex].MSelect) && nil != values[fieldIndex].MSelect[0] && "" != values[fieldIndex].MSelect[0].Content { + if _, ok := uniqueValues[values[fieldIndex].MSelect[0].Content]; !ok { + uniqueValues[values[fieldIndex].MSelect[0].Content] = true + countUniqueValues++ + } + } + } + if 0 < len(collection.GetItems()) { + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues)/float64(len(collection.GetItems())), NumberFormatPercent)} + } + } +} + +func CalcFieldDate(collection Collection, field Field, fieldIndex int) { + calc := field.GetCalc() + switch calc.Operator { + case CalcOperatorCountAll: + calc.Result = &Value{Number: NewFormattedValueNumber(float64(len(collection.GetItems())), NumberFormatNone)} + case CalcOperatorCountValues: + countValues := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Date && values[fieldIndex].Date.IsNotEmpty { + countValues++ + } + } + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countValues), NumberFormatNone)} + case CalcOperatorCountUniqueValues: + countUniqueValues := 0 + uniqueValues := map[int64]bool{} + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Date && values[fieldIndex].Date.IsNotEmpty { + if _, ok := uniqueValues[values[fieldIndex].Date.Content]; !ok { + countUniqueValues++ + uniqueValues[values[fieldIndex].Date.Content] = true + } + } + } + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues), NumberFormatNone)} + case CalcOperatorCountEmpty: + countEmpty := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil == values[fieldIndex] || nil == values[fieldIndex].Date || !values[fieldIndex].Date.IsNotEmpty { + countEmpty++ + } + } + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countEmpty), NumberFormatNone)} + case CalcOperatorCountNotEmpty: + countNotEmpty := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Date && values[fieldIndex].Date.IsNotEmpty { + countNotEmpty++ + } + } + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty), NumberFormatNone)} + case CalcOperatorPercentEmpty: + countEmpty := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil == values[fieldIndex] || nil == values[fieldIndex].Date || !values[fieldIndex].Date.IsNotEmpty { + countEmpty++ + } + } + if 0 < len(collection.GetItems()) { + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countEmpty)/float64(len(collection.GetItems())), NumberFormatPercent)} + } + case CalcOperatorPercentNotEmpty: + countNotEmpty := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Date && values[fieldIndex].Date.IsNotEmpty { + countNotEmpty++ + } + } + if 0 < len(collection.GetItems()) { + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty)/float64(len(collection.GetItems())), NumberFormatPercent)} + } + case CalcOperatorPercentUniqueValues: + countUniqueValues := 0 + uniqueValues := map[int64]bool{} + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Date && values[fieldIndex].Date.IsNotEmpty { + if _, ok := uniqueValues[values[fieldIndex].Date.Content]; !ok { + countUniqueValues++ + uniqueValues[values[fieldIndex].Date.Content] = true + } + } + } + if 0 < len(collection.GetItems()) { + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues)/float64(len(collection.GetItems())), NumberFormatPercent)} + } + case CalcOperatorEarliest: + earliest := int64(0) + var isNotTime, hasEndDate bool + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Date && values[fieldIndex].Date.IsNotEmpty { + if 0 == earliest || earliest > values[fieldIndex].Date.Content { + earliest = values[fieldIndex].Date.Content + isNotTime = values[fieldIndex].Date.IsNotTime + hasEndDate = values[fieldIndex].Date.HasEndDate + } + } + } + if 0 != earliest { + calc.Result = &Value{Date: NewFormattedValueDate(earliest, 0, DateFormatNone, isNotTime, hasEndDate)} + } + case CalcOperatorLatest: + latest := int64(0) + var isNotTime, hasEndDate bool + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Date && values[fieldIndex].Date.IsNotEmpty { + if 0 == latest || latest < values[fieldIndex].Date.Content { + latest = values[fieldIndex].Date.Content + isNotTime = values[fieldIndex].Date.IsNotTime + hasEndDate = values[fieldIndex].Date.HasEndDate + } + } + } + if 0 != latest { + calc.Result = &Value{Date: NewFormattedValueDate(latest, 0, DateFormatNone, isNotTime, hasEndDate)} + } + case CalcOperatorRange: + earliest := int64(0) + latest := int64(0) + var isNotTime, hasEndDate bool + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Date && values[fieldIndex].Date.IsNotEmpty { + if 0 == earliest || earliest > values[fieldIndex].Date.Content { + earliest = values[fieldIndex].Date.Content + isNotTime = values[fieldIndex].Date.IsNotTime + hasEndDate = values[fieldIndex].Date.HasEndDate + } + if 0 == latest || latest < values[fieldIndex].Date.Content { + latest = values[fieldIndex].Date.Content + isNotTime = values[fieldIndex].Date.IsNotTime + hasEndDate = values[fieldIndex].Date.HasEndDate + } + } + } + if 0 != earliest && 0 != latest { + calc.Result = &Value{Date: NewFormattedValueDate(earliest, latest, DateFormatDuration, isNotTime, hasEndDate)} + } + } +} + +func CalcFieldNumber(collection Collection, field Field, fieldIndex int) { + calc := field.GetCalc() + switch calc.Operator { + case CalcOperatorCountAll: + calc.Result = &Value{Number: NewFormattedValueNumber(float64(len(collection.GetItems())), NumberFormatNone)} + case CalcOperatorCountValues: + countValues := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Number && values[fieldIndex].Number.IsNotEmpty { + countValues++ + } + } + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countValues), NumberFormatNone)} + case CalcOperatorCountUniqueValues: + countUniqueValues := 0 + uniqueValues := map[float64]bool{} + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Number && values[fieldIndex].Number.IsNotEmpty { + if !uniqueValues[values[fieldIndex].Number.Content] { + uniqueValues[values[fieldIndex].Number.Content] = true + countUniqueValues++ + } + } + } + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues), NumberFormatNone)} + case CalcOperatorCountEmpty: + countEmpty := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil == values[fieldIndex] || nil == values[fieldIndex].Number || !values[fieldIndex].Number.IsNotEmpty { + countEmpty++ + } + } + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countEmpty), NumberFormatNone)} + case CalcOperatorCountNotEmpty: + countNotEmpty := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Number && values[fieldIndex].Number.IsNotEmpty { + countNotEmpty++ + } + } + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty), NumberFormatNone)} + case CalcOperatorPercentEmpty: + countEmpty := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil == values[fieldIndex] || nil == values[fieldIndex].Number || !values[fieldIndex].Number.IsNotEmpty { + countEmpty++ + } + } + if 0 < len(collection.GetItems()) { + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countEmpty)/float64(len(collection.GetItems())), NumberFormatPercent)} + } + case CalcOperatorPercentNotEmpty: + countNotEmpty := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Number && values[fieldIndex].Number.IsNotEmpty { + countNotEmpty++ + } + } + if 0 < len(collection.GetItems()) { + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty)/float64(len(collection.GetItems())), NumberFormatPercent)} + } + case CalcOperatorPercentUniqueValues: + countUniqueValues := 0 + uniqueValues := map[float64]bool{} + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Number && values[fieldIndex].Number.IsNotEmpty { + if !uniqueValues[values[fieldIndex].Number.Content] { + uniqueValues[values[fieldIndex].Number.Content] = true + countUniqueValues++ + } + } + } + if 0 < len(collection.GetItems()) { + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues)/float64(len(collection.GetItems())), NumberFormatPercent)} + } + case CalcOperatorSum: + sum := 0.0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Number && values[fieldIndex].Number.IsNotEmpty { + sum += values[fieldIndex].Number.Content + } + } + calc.Result = &Value{Number: NewFormattedValueNumber(sum, field.GetNumberFormat())} + case CalcOperatorAverage: + sum := 0.0 + count := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Number && values[fieldIndex].Number.IsNotEmpty { + sum += values[fieldIndex].Number.Content + count++ + } + } + if 0 != count { + calc.Result = &Value{Number: NewFormattedValueNumber(sum/float64(count), field.GetNumberFormat())} + } + case CalcOperatorMedian: + calcValues := []float64{} + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Number && values[fieldIndex].Number.IsNotEmpty { + calcValues = append(calcValues, values[fieldIndex].Number.Content) + } + } + sort.Float64s(calcValues) + if len(calcValues) > 0 { + if len(calcValues)%2 == 0 { + calc.Result = &Value{Number: NewFormattedValueNumber((calcValues[len(calcValues)/2-1]+calcValues[len(calcValues)/2])/2, field.GetNumberFormat())} + } else { + calc.Result = &Value{Number: NewFormattedValueNumber(calcValues[len(calcValues)/2], field.GetNumberFormat())} + } + } + case CalcOperatorMin: + minVal := math.MaxFloat64 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Number && values[fieldIndex].Number.IsNotEmpty { + if values[fieldIndex].Number.Content < minVal { + minVal = values[fieldIndex].Number.Content + } + } + } + if math.MaxFloat64 != minVal { + calc.Result = &Value{Number: NewFormattedValueNumber(minVal, field.GetNumberFormat())} + } + case CalcOperatorMax: + maxVal := -math.MaxFloat64 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Number && values[fieldIndex].Number.IsNotEmpty { + if values[fieldIndex].Number.Content > maxVal { + maxVal = values[fieldIndex].Number.Content + } + } + } + if -math.MaxFloat64 != maxVal { + calc.Result = &Value{Number: NewFormattedValueNumber(maxVal, field.GetNumberFormat())} + } + case CalcOperatorRange: + minVal := math.MaxFloat64 + maxVal := -math.MaxFloat64 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Number && values[fieldIndex].Number.IsNotEmpty { + if values[fieldIndex].Number.Content < minVal { + minVal = values[fieldIndex].Number.Content + } + if values[fieldIndex].Number.Content > maxVal { + maxVal = values[fieldIndex].Number.Content + } + } + } + if math.MaxFloat64 != minVal && -math.MaxFloat64 != maxVal { + calc.Result = &Value{Number: NewFormattedValueNumber(maxVal-minVal, field.GetNumberFormat())} + } + } +} + +func CalcFieldText(collection Collection, field Field, fieldIndex int) { + calc := field.GetCalc() + switch calc.Operator { + case CalcOperatorCountAll: + calc.Result = &Value{Number: NewFormattedValueNumber(float64(len(collection.GetItems())), NumberFormatNone)} + case CalcOperatorCountValues: + countValues := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Text && "" != values[fieldIndex].Text.Content { + countValues++ + } + } + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countValues), NumberFormatNone)} + case CalcOperatorCountUniqueValues: + countUniqueValues := 0 + uniqueValues := map[string]bool{} + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Text && "" != values[fieldIndex].Text.Content { + if !uniqueValues[values[fieldIndex].Text.Content] { + uniqueValues[values[fieldIndex].Text.Content] = true + countUniqueValues++ + } + } + } + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues), NumberFormatNone)} + case CalcOperatorCountEmpty: + countEmpty := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil == values[fieldIndex] || nil == values[fieldIndex].Text || "" == values[fieldIndex].Text.Content { + countEmpty++ + } + } + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countEmpty), NumberFormatNone)} + case CalcOperatorCountNotEmpty: + countNotEmpty := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Text && "" != values[fieldIndex].Text.Content { + countNotEmpty++ + } + } + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty), NumberFormatNone)} + case CalcOperatorPercentEmpty: + countEmpty := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil == values[fieldIndex] || nil == values[fieldIndex].Text || "" == values[fieldIndex].Text.Content { + countEmpty++ + } + } + if 0 < len(collection.GetItems()) { + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countEmpty)/float64(len(collection.GetItems())), NumberFormatPercent)} + } + case CalcOperatorPercentNotEmpty: + countNotEmpty := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Text && "" != values[fieldIndex].Text.Content { + countNotEmpty++ + } + } + if 0 < len(collection.GetItems()) { + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty)/float64(len(collection.GetItems())), NumberFormatPercent)} + } + case CalcOperatorPercentUniqueValues: + countUniqueValues := 0 + uniqueValues := map[string]bool{} + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Text && "" != values[fieldIndex].Text.Content { + if !uniqueValues[values[fieldIndex].Text.Content] { + uniqueValues[values[fieldIndex].Text.Content] = true + countUniqueValues++ + } + } + } + if 0 < len(collection.GetItems()) { + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues)/float64(len(collection.GetItems())), NumberFormatPercent)} + } + } +} + +func CalcFieldURL(collection Collection, field Field, fieldIndex int) { + calc := field.GetCalc() + switch calc.Operator { + case CalcOperatorCountAll: + calc.Result = &Value{Number: NewFormattedValueNumber(float64(len(collection.GetItems())), NumberFormatNone)} + case CalcOperatorCountValues: + countValues := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].URL && "" != values[fieldIndex].URL.Content { + countValues++ + } + } + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countValues), NumberFormatNone)} + case CalcOperatorCountUniqueValues: + countUniqueValues := 0 + uniqueValues := map[string]bool{} + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].URL && "" != values[fieldIndex].URL.Content { + if !uniqueValues[values[fieldIndex].URL.Content] { + uniqueValues[values[fieldIndex].URL.Content] = true + countUniqueValues++ + } + } + } + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues), NumberFormatNone)} + case CalcOperatorCountEmpty: + countEmpty := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil == values[fieldIndex] || nil == values[fieldIndex].URL || "" == values[fieldIndex].URL.Content { + countEmpty++ + } + } + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countEmpty), NumberFormatNone)} + case CalcOperatorCountNotEmpty: + countNotEmpty := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].URL && "" != values[fieldIndex].URL.Content { + countNotEmpty++ + } + } + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty), NumberFormatNone)} + case CalcOperatorPercentEmpty: + countEmpty := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil == values[fieldIndex] || nil == values[fieldIndex].URL || "" == values[fieldIndex].URL.Content { + countEmpty++ + } + } + if 0 < len(collection.GetItems()) { + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countEmpty)/float64(len(collection.GetItems())), NumberFormatPercent)} + } + case CalcOperatorPercentNotEmpty: + countNotEmpty := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].URL && "" != values[fieldIndex].URL.Content { + countNotEmpty++ + } + } + if 0 < len(collection.GetItems()) { + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty)/float64(len(collection.GetItems())), NumberFormatPercent)} + } + case CalcOperatorPercentUniqueValues: + countUniqueValues := 0 + uniqueValues := map[string]bool{} + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].URL && "" != values[fieldIndex].URL.Content { + if !uniqueValues[values[fieldIndex].URL.Content] { + uniqueValues[values[fieldIndex].URL.Content] = true + countUniqueValues++ + } + } + } + if 0 < len(collection.GetItems()) { + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues)/float64(len(collection.GetItems())), NumberFormatPercent)} + } + } +} + +func CalcFieldEmail(collection Collection, field Field, fieldIndex int) { + calc := field.GetCalc() + switch calc.Operator { + case CalcOperatorCountAll: + calc.Result = &Value{Number: NewFormattedValueNumber(float64(len(collection.GetItems())), NumberFormatNone)} + case CalcOperatorCountValues: + countValues := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Email && "" != values[fieldIndex].Email.Content { + countValues++ + } + } + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countValues), NumberFormatNone)} + case CalcOperatorCountUniqueValues: + countUniqueValues := 0 + uniqueValues := map[string]bool{} + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Email && "" != values[fieldIndex].Email.Content { + if !uniqueValues[values[fieldIndex].Email.Content] { + uniqueValues[values[fieldIndex].Email.Content] = true + countUniqueValues++ + } + } + } + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues), NumberFormatNone)} + case CalcOperatorCountEmpty: + countEmpty := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil == values[fieldIndex] || nil == values[fieldIndex].Email || "" == values[fieldIndex].Email.Content { + countEmpty++ + } + } + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countEmpty), NumberFormatNone)} + case CalcOperatorCountNotEmpty: + countNotEmpty := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Email && "" != values[fieldIndex].Email.Content { + countNotEmpty++ + } + } + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty), NumberFormatNone)} + case CalcOperatorPercentEmpty: + countEmpty := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil == values[fieldIndex] || nil == values[fieldIndex].Email || "" == values[fieldIndex].Email.Content { + countEmpty++ + } + } + if 0 < len(collection.GetItems()) { + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countEmpty)/float64(len(collection.GetItems())), NumberFormatPercent)} + } + case CalcOperatorPercentNotEmpty: + countNotEmpty := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Email && "" != values[fieldIndex].Email.Content { + countNotEmpty++ + } + } + if 0 < len(collection.GetItems()) { + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty)/float64(len(collection.GetItems())), NumberFormatPercent)} + } + case CalcOperatorPercentUniqueValues: + countUniqueValues := 0 + uniqueValues := map[string]bool{} + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Email && "" != values[fieldIndex].Email.Content { + if !uniqueValues[values[fieldIndex].Email.Content] { + uniqueValues[values[fieldIndex].Email.Content] = true + countUniqueValues++ + } + } + } + if 0 < len(collection.GetItems()) { + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues)/float64(len(collection.GetItems())), NumberFormatPercent)} + } + } +} + +func CalcFieldPhone(collection Collection, field Field, fieldIndex int) { + calc := field.GetCalc() + switch calc.Operator { + case CalcOperatorCountAll: + calc.Result = &Value{Number: NewFormattedValueNumber(float64(len(collection.GetItems())), NumberFormatNone)} + case CalcOperatorCountValues: + countValues := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Phone && "" != values[fieldIndex].Phone.Content { + countValues++ + } + } + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countValues), NumberFormatNone)} + case CalcOperatorCountUniqueValues: + countUniqueValues := 0 + uniqueValues := map[string]bool{} + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Phone && "" != values[fieldIndex].Phone.Content { + if !uniqueValues[values[fieldIndex].Phone.Content] { + uniqueValues[values[fieldIndex].Phone.Content] = true + countUniqueValues++ + } + } + } + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues), NumberFormatNone)} + case CalcOperatorCountEmpty: + countEmpty := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil == values[fieldIndex] || nil == values[fieldIndex].Phone || "" == values[fieldIndex].Phone.Content { + countEmpty++ + } + } + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countEmpty), NumberFormatNone)} + case CalcOperatorCountNotEmpty: + countNotEmpty := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Phone && "" != values[fieldIndex].Phone.Content { + countNotEmpty++ + } + } + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty), NumberFormatNone)} + case CalcOperatorPercentEmpty: + countEmpty := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil == values[fieldIndex] || nil == values[fieldIndex].Phone || "" == values[fieldIndex].Phone.Content { + countEmpty++ + } + } + if 0 < len(collection.GetItems()) { + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countEmpty)/float64(len(collection.GetItems())), NumberFormatPercent)} + } + case CalcOperatorPercentNotEmpty: + countNotEmpty := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Phone && "" != values[fieldIndex].Phone.Content { + countNotEmpty++ + } + } + if 0 < len(collection.GetItems()) { + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty)/float64(len(collection.GetItems())), NumberFormatPercent)} + } + case CalcOperatorPercentUniqueValues: + countUniqueValues := 0 + uniqueValues := map[string]bool{} + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Phone && "" != values[fieldIndex].Phone.Content { + if !uniqueValues[values[fieldIndex].Phone.Content] { + uniqueValues[values[fieldIndex].Phone.Content] = true + countUniqueValues++ + } + } + } + if 0 < len(collection.GetItems()) { + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues)/float64(len(collection.GetItems())), NumberFormatPercent)} + } + } +} + +func CalcFieldBlock(collection Collection, field Field, fieldIndex int) { + calc := field.GetCalc() + switch calc.Operator { + case CalcOperatorCountAll: + calc.Result = &Value{Number: NewFormattedValueNumber(float64(len(collection.GetItems())), NumberFormatNone)} + case CalcOperatorCountValues: + countValues := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Block && "" != values[fieldIndex].Block.Content { + countValues++ + } + } + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countValues), NumberFormatNone)} + case CalcOperatorCountUniqueValues: + countUniqueValues := 0 + uniqueValues := map[string]bool{} + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Block && "" != values[fieldIndex].Block.Content { + if !uniqueValues[values[fieldIndex].Block.Content] { + uniqueValues[values[fieldIndex].Block.Content] = true + countUniqueValues++ + } + } + } + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues), NumberFormatNone)} + case CalcOperatorCountEmpty: + countEmpty := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil == values[fieldIndex] || nil == values[fieldIndex].Block || "" == values[fieldIndex].Block.Content { + countEmpty++ + } + } + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countEmpty), NumberFormatNone)} + case CalcOperatorCountNotEmpty: + countNotEmpty := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Block && "" != values[fieldIndex].Block.Content { + countNotEmpty++ + } + } + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty), NumberFormatNone)} + case CalcOperatorPercentEmpty: + countEmpty := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil == values[fieldIndex] || nil == values[fieldIndex].Block || "" == values[fieldIndex].Block.Content { + countEmpty++ + } + } + if 0 < len(collection.GetItems()) { + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countEmpty)/float64(len(collection.GetItems())), NumberFormatPercent)} + } + case CalcOperatorPercentNotEmpty: + countNotEmpty := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Block && "" != values[fieldIndex].Block.Content { + countNotEmpty++ + } + } + if 0 < len(collection.GetItems()) { + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty)/float64(len(collection.GetItems())), NumberFormatPercent)} + } + case CalcOperatorPercentUniqueValues: + countUniqueValues := 0 + uniqueValues := map[string]bool{} + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Block && "" != values[fieldIndex].Block.Content { + if !uniqueValues[values[fieldIndex].Block.Content] { + uniqueValues[values[fieldIndex].Block.Content] = true + countUniqueValues++ + } + } + } + if 0 < len(collection.GetItems()) { + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues)/float64(len(collection.GetItems())), NumberFormatPercent)} + } + } +} + +func CalcFieldCreated(collection Collection, field Field, fieldIndex int) { + calc := field.GetCalc() + switch calc.Operator { + case CalcOperatorCountAll: + calc.Result = &Value{Number: NewFormattedValueNumber(float64(len(collection.GetItems())), NumberFormatNone)} + case CalcOperatorCountValues: + countValues := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Created { + countValues++ + } + } + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countValues), NumberFormatNone)} + case CalcOperatorCountUniqueValues: + countUniqueValues := 0 + uniqueValues := map[int64]bool{} + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Created { + if _, ok := uniqueValues[values[fieldIndex].Created.Content]; !ok { + countUniqueValues++ + uniqueValues[values[fieldIndex].Created.Content] = true + } + } + } + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues), NumberFormatNone)} + case CalcOperatorCountEmpty: + countEmpty := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil == values[fieldIndex] || nil == values[fieldIndex].Created { + countEmpty++ + } + } + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countEmpty), NumberFormatNone)} + case CalcOperatorCountNotEmpty: + countNotEmpty := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Created { + countNotEmpty++ + } + } + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty), NumberFormatNone)} + case CalcOperatorPercentEmpty: + countEmpty := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil == values[fieldIndex] || nil == values[fieldIndex].Created { + countEmpty++ + } + } + if 0 < len(collection.GetItems()) { + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countEmpty)/float64(len(collection.GetItems())), NumberFormatPercent)} + } + case CalcOperatorPercentNotEmpty: + countNotEmpty := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Created { + countNotEmpty++ + } + } + if 0 < len(collection.GetItems()) { + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty)/float64(len(collection.GetItems())), NumberFormatPercent)} + } + case CalcOperatorPercentUniqueValues: + countUniqueValues := 0 + uniqueValues := map[int64]bool{} + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Created { + if _, ok := uniqueValues[values[fieldIndex].Created.Content]; !ok { + countUniqueValues++ + uniqueValues[values[fieldIndex].Created.Content] = true + } + } + } + if 0 < len(collection.GetItems()) { + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues)/float64(len(collection.GetItems())), NumberFormatPercent)} + } + case CalcOperatorEarliest: + earliest := int64(0) + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Created { + if 0 == earliest || earliest > values[fieldIndex].Created.Content { + earliest = values[fieldIndex].Created.Content + } + } + } + if 0 != earliest { + calc.Result = &Value{Created: NewFormattedValueCreated(earliest, 0, CreatedFormatNone)} + } + case CalcOperatorLatest: + latest := int64(0) + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Created { + if 0 == latest || latest < values[fieldIndex].Created.Content { + latest = values[fieldIndex].Created.Content + } + } + } + if 0 != latest { + calc.Result = &Value{Created: NewFormattedValueCreated(latest, 0, CreatedFormatNone)} + } + case CalcOperatorRange: + earliest := int64(0) + latest := int64(0) + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Created { + if 0 == earliest || earliest > values[fieldIndex].Created.Content { + earliest = values[fieldIndex].Created.Content + } + if 0 == latest || latest < values[fieldIndex].Created.Content { + latest = values[fieldIndex].Created.Content + } + } + } + if 0 != earliest && 0 != latest { + calc.Result = &Value{Created: NewFormattedValueCreated(earliest, latest, CreatedFormatDuration)} + } + } +} + +func CalcFieldUpdated(collection Collection, field Field, fieldIndex int) { + calc := field.GetCalc() + switch calc.Operator { + case CalcOperatorCountAll: + calc.Result = &Value{Number: NewFormattedValueNumber(float64(len(collection.GetItems())), NumberFormatNone)} + case CalcOperatorCountValues: + countValues := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Updated && values[fieldIndex].Updated.IsNotEmpty { + countValues++ + } + } + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countValues), NumberFormatNone)} + case CalcOperatorCountUniqueValues: + countUniqueValues := 0 + uniqueValues := map[int64]bool{} + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Updated && values[fieldIndex].Updated.IsNotEmpty { + if _, ok := uniqueValues[values[fieldIndex].Updated.Content]; !ok { + countUniqueValues++ + uniqueValues[values[fieldIndex].Updated.Content] = true + } + } + } + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues), NumberFormatNone)} + case CalcOperatorCountEmpty: + countEmpty := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil == values[fieldIndex] || nil == values[fieldIndex].Updated || !values[fieldIndex].Updated.IsNotEmpty { + countEmpty++ + } + } + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countEmpty), NumberFormatNone)} + case CalcOperatorCountNotEmpty: + countNotEmpty := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Updated && values[fieldIndex].Updated.IsNotEmpty { + countNotEmpty++ + } + } + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty), NumberFormatNone)} + case CalcOperatorPercentEmpty: + countEmpty := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil == values[fieldIndex] || nil == values[fieldIndex].Updated || !values[fieldIndex].Updated.IsNotEmpty { + countEmpty++ + } + } + if 0 < len(collection.GetItems()) { + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countEmpty)/float64(len(collection.GetItems())), NumberFormatPercent)} + } + case CalcOperatorPercentNotEmpty: + countNotEmpty := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Updated && values[fieldIndex].Updated.IsNotEmpty { + countNotEmpty++ + } + } + if 0 < len(collection.GetItems()) { + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty)/float64(len(collection.GetItems())), NumberFormatPercent)} + } + case CalcOperatorPercentUniqueValues: + countUniqueValues := 0 + uniqueValues := map[int64]bool{} + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Updated && values[fieldIndex].Updated.IsNotEmpty { + if _, ok := uniqueValues[values[fieldIndex].Updated.Content]; !ok { + countUniqueValues++ + uniqueValues[values[fieldIndex].Updated.Content] = true + } + } + } + if 0 < len(collection.GetItems()) { + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues)/float64(len(collection.GetItems())), NumberFormatPercent)} + } + case CalcOperatorEarliest: + earliest := int64(0) + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Updated && values[fieldIndex].Updated.IsNotEmpty { + if 0 == earliest || earliest > values[fieldIndex].Updated.Content { + earliest = values[fieldIndex].Updated.Content + } + } + } + if 0 != earliest { + calc.Result = &Value{Updated: NewFormattedValueUpdated(earliest, 0, UpdatedFormatNone)} + } + case CalcOperatorLatest: + latest := int64(0) + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Updated && values[fieldIndex].Updated.IsNotEmpty { + if 0 == latest || latest < values[fieldIndex].Updated.Content { + latest = values[fieldIndex].Updated.Content + } + } + } + if 0 != latest { + calc.Result = &Value{Updated: NewFormattedValueUpdated(latest, 0, UpdatedFormatNone)} + } + case CalcOperatorRange: + earliest := int64(0) + latest := int64(0) + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Updated && values[fieldIndex].Updated.IsNotEmpty { + if 0 == earliest || earliest > values[fieldIndex].Updated.Content { + earliest = values[fieldIndex].Updated.Content + } + if 0 == latest || latest < values[fieldIndex].Updated.Content { + latest = values[fieldIndex].Updated.Content + } + } + } + if 0 != earliest && 0 != latest { + calc.Result = &Value{Updated: NewFormattedValueUpdated(earliest, latest, UpdatedFormatDuration)} + } + } +} + +func CalcFieldCheckbox(collection Collection, field Field, fieldIndex int) { + calc := field.GetCalc() + switch calc.Operator { + case CalcOperatorCountAll: + calc.Result = &Value{Number: NewFormattedValueNumber(float64(len(collection.GetItems())), NumberFormatNone)} + case CalcOperatorChecked: + countChecked := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Checkbox && values[fieldIndex].Checkbox.Checked { + countChecked++ + } + } + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countChecked), NumberFormatNone)} + case CalcOperatorUnchecked: + countUnchecked := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Checkbox && !values[fieldIndex].Checkbox.Checked { + countUnchecked++ + } + } + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUnchecked), NumberFormatNone)} + case CalcOperatorPercentChecked: + countChecked := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Checkbox && values[fieldIndex].Checkbox.Checked { + countChecked++ + } + } + if 0 < len(collection.GetItems()) { + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countChecked)/float64(len(collection.GetItems())), NumberFormatPercent)} + } + case CalcOperatorPercentUnchecked: + countUnchecked := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Checkbox && !values[fieldIndex].Checkbox.Checked { + countUnchecked++ + } + } + if 0 < len(collection.GetItems()) { + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUnchecked)/float64(len(collection.GetItems())), NumberFormatPercent)} + } + } +} + +func CalcFieldRelation(collection Collection, field Field, fieldIndex int) { + calc := field.GetCalc() + switch calc.Operator { + case CalcOperatorCountAll: + calc.Result = &Value{Number: NewFormattedValueNumber(float64(len(collection.GetItems())), NumberFormatNone)} + case CalcOperatorCountValues: + countValues := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Relation { + countValues++ + } + } + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countValues), NumberFormatNone)} + case CalcOperatorCountUniqueValues: + countUniqueValues := 0 + uniqueValues := map[string]bool{} + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Relation { + for _, id := range values[fieldIndex].Relation.BlockIDs { + if !uniqueValues[id] { + uniqueValues[id] = true + countUniqueValues++ + } + } + } + } + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues), NumberFormatNone)} + case CalcOperatorCountEmpty: + countEmpty := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil == values[fieldIndex] || nil == values[fieldIndex].Relation || 0 == len(values[fieldIndex].Relation.BlockIDs) { + countEmpty++ + } + } + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countEmpty), NumberFormatNone)} + case CalcOperatorCountNotEmpty: + countNotEmpty := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Relation && 0 < len(values[fieldIndex].Relation.BlockIDs) { + countNotEmpty++ + } + } + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty), NumberFormatNone)} + case CalcOperatorPercentEmpty: + countEmpty := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil == values[fieldIndex] || nil == values[fieldIndex].Relation || 0 == len(values[fieldIndex].Relation.BlockIDs) { + countEmpty++ + } + } + if 0 < len(collection.GetItems()) { + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countEmpty)/float64(len(collection.GetItems())), NumberFormatPercent)} + } + case CalcOperatorPercentNotEmpty: + countNotEmpty := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Relation && 0 < len(values[fieldIndex].Relation.BlockIDs) { + countNotEmpty++ + } + } + if 0 < len(collection.GetItems()) { + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty)/float64(len(collection.GetItems())), NumberFormatPercent)} + } + case CalcOperatorPercentUniqueValues: + countUniqueValues := 0 + uniqueValues := map[string]bool{} + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Relation { + for _, id := range values[fieldIndex].Relation.BlockIDs { + if !uniqueValues[id] { + uniqueValues[id] = true + countUniqueValues++ + } + } + } + } + if 0 < len(collection.GetItems()) { + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues)/float64(len(collection.GetItems())), NumberFormatPercent)} + } + } +} + +func CalcFieldRollup(collection Collection, field Field, fieldIndex int) { + calc := field.GetCalc() + switch calc.Operator { + case CalcOperatorCountAll: + calc.Result = &Value{Number: NewFormattedValueNumber(float64(len(collection.GetItems())), NumberFormatNone)} + case CalcOperatorCountValues: + countValues := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Rollup { + countValues++ + } + } + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countValues), NumberFormatNone)} + case CalcOperatorCountUniqueValues: + countUniqueValues := 0 + uniqueValues := map[string]bool{} + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Rollup { + for _, content := range values[fieldIndex].Rollup.Contents { + if !uniqueValues[content.String(true)] { + uniqueValues[content.String(true)] = true + countUniqueValues++ + } + } + } + } + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues), NumberFormatNone)} + case CalcOperatorCountEmpty: + countEmpty := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil == values[fieldIndex] || nil == values[fieldIndex].Rollup || 0 == len(values[fieldIndex].Rollup.Contents) { + countEmpty++ + } + } + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countEmpty), NumberFormatNone)} + case CalcOperatorCountNotEmpty: + countNotEmpty := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Rollup && 0 < len(values[fieldIndex].Rollup.Contents) { + countNotEmpty++ + } + } + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty), NumberFormatNone)} + case CalcOperatorPercentEmpty: + countEmpty := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil == values[fieldIndex] || nil == values[fieldIndex].Rollup || 0 == len(values[fieldIndex].Rollup.Contents) { + countEmpty++ + } + } + if 0 < len(collection.GetItems()) { + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countEmpty)/float64(len(collection.GetItems())), NumberFormatPercent)} + } + case CalcOperatorPercentNotEmpty: + countNotEmpty := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Rollup && 0 < len(values[fieldIndex].Rollup.Contents) { + countNotEmpty++ + } + } + if 0 < len(collection.GetItems()) { + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty)/float64(len(collection.GetItems())), NumberFormatPercent)} + } + case CalcOperatorPercentUniqueValues: + countUniqueValues := 0 + uniqueValues := map[string]bool{} + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Rollup { + for _, content := range values[fieldIndex].Rollup.Contents { + if !uniqueValues[content.String(true)] { + uniqueValues[content.String(true)] = true + countUniqueValues++ + } + } + } + } + if 0 < len(collection.GetItems()) { + calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues)/float64(len(collection.GetItems())), NumberFormatPercent)} + } + case CalcOperatorSum: + sum := 0.0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Rollup && 0 < len(values[fieldIndex].Rollup.Contents) { + for _, content := range values[fieldIndex].Rollup.Contents { + val, _ := util.Convert2Float(content.String(false)) + sum += val + } + } + } + calc.Result = &Value{Number: NewFormattedValueNumber(sum, field.GetNumberFormat())} + case CalcOperatorAverage: + sum := 0.0 + count := 0 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Rollup && 0 < len(values[fieldIndex].Rollup.Contents) { + for _, content := range values[fieldIndex].Rollup.Contents { + val, _ := util.Convert2Float(content.String(false)) + sum += val + count++ + } + } + } + if 0 != count { + calc.Result = &Value{Number: NewFormattedValueNumber(sum/float64(count), field.GetNumberFormat())} + } + case CalcOperatorMedian: + calcValues := []float64{} + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Rollup && 0 < len(values[fieldIndex].Rollup.Contents) { + for _, content := range values[fieldIndex].Rollup.Contents { + val, _ := util.Convert2Float(content.String(false)) + calcValues = append(calcValues, val) + } + } + } + sort.Float64s(calcValues) + if 0 < len(calcValues) { + if 0 == len(calcValues)%2 { + calc.Result = &Value{Number: NewFormattedValueNumber((calcValues[len(calcValues)/2-1]+calcValues[len(calcValues)/2])/2, field.GetNumberFormat())} + } else { + calc.Result = &Value{Number: NewFormattedValueNumber(calcValues[len(calcValues)/2], field.GetNumberFormat())} + } + } + case CalcOperatorMin: + minVal := math.MaxFloat64 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Rollup && 0 < len(values[fieldIndex].Rollup.Contents) { + for _, content := range values[fieldIndex].Rollup.Contents { + val, _ := util.Convert2Float(content.String(false)) + if val < minVal { + minVal = val + } + } + } + } + if math.MaxFloat64 != minVal { + calc.Result = &Value{Number: NewFormattedValueNumber(minVal, field.GetNumberFormat())} + } + case CalcOperatorMax: + maxVal := -math.MaxFloat64 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Rollup && 0 < len(values[fieldIndex].Rollup.Contents) { + for _, content := range values[fieldIndex].Rollup.Contents { + val, _ := util.Convert2Float(content.String(false)) + if val > maxVal { + maxVal = val + } + } + } + } + if -math.MaxFloat64 != maxVal { + calc.Result = &Value{Number: NewFormattedValueNumber(maxVal, field.GetNumberFormat())} + } + case CalcOperatorRange: + minVal := math.MaxFloat64 + maxVal := -math.MaxFloat64 + for _, item := range collection.GetItems() { + values := item.GetValues() + if nil != values[fieldIndex] && nil != values[fieldIndex].Rollup && 0 < len(values[fieldIndex].Rollup.Contents) { + for _, content := range values[fieldIndex].Rollup.Contents { + val, _ := util.Convert2Float(content.String(false)) + if val < minVal { + minVal = val + } + if val > maxVal { + maxVal = val + } + } + } + } + if math.MaxFloat64 != minVal && -math.MaxFloat64 != maxVal { + calc.Result = &Value{Number: NewFormattedValueNumber(maxVal-minVal, field.GetNumberFormat())} + } + } +} diff --git a/kernel/av/filter.go b/kernel/av/filter.go index 5545f548d..207593823 100644 --- a/kernel/av/filter.go +++ b/kernel/av/filter.go @@ -76,6 +76,58 @@ const ( FilterOperatorIsFalse FilterOperator = "Is false" ) +func Filter(viewable Viewable, attrView *AttributeView) { + collection := viewable.(Collection) + filters := collection.GetFilters() + if 1 > len(filters) { + return + } + + var colIndexes []int + for _, f := range filters { + for i, c := range collection.GetFields() { + if c.GetID() == f.Column { + colIndexes = append(colIndexes, i) + break + } + } + } + + var items []Item + attrViewCache := map[string]*AttributeView{} + attrViewCache[attrView.ID] = attrView + for _, item := range collection.GetItems() { + pass := true + values := item.GetValues() + for j, index := range colIndexes { + operator := filters[j].Operator + + if nil == values[index] { + if FilterOperatorIsNotEmpty == operator { + pass = false + } else if FilterOperatorIsEmpty == operator { + pass = true + break + } + + if KeyTypeText != values[index].Type { + pass = false + } + break + } + + if !values[index].Filter(filters[j], attrView, item.GetID(), &attrViewCache) { + pass = false + break + } + } + if pass { + items = append(items, item) + } + } + collection.SetItems(items) +} + func (value *Value) Filter(filter *ViewFilter, attrView *AttributeView, rowID string, attrViewCache *map[string]*AttributeView) bool { if nil == filter || (nil == filter.Value && nil == filter.RelativeDate) { return true diff --git a/kernel/av/layout.go b/kernel/av/layout.go index 8ea45a24c..a8d9e1d25 100644 --- a/kernel/av/layout.go +++ b/kernel/av/layout.go @@ -16,8 +16,6 @@ package av -import "sort" - // BaseLayout 描述了布局的基础结构。 type BaseLayout struct { Spec int `json:"spec"` // 布局格式版本 @@ -38,10 +36,11 @@ type BaseLayout struct { // BaseField 描述了字段的基础结构。 type BaseField struct { - ID string `json:"id"` // 字段 ID - Wrap bool `json:"wrap"` // 是否换行 - Hidden bool `json:"hidden"` // 是否隐藏 - Desc string `json:"desc,omitempty"` // 字段描述 + ID string `json:"id"` // 字段 ID + Wrap bool `json:"wrap"` // 是否换行 + Hidden bool `json:"hidden"` // 是否隐藏 + Desc string `json:"desc,omitempty"` // 字段描述 + Calc *FieldCalc `json:"calc,omitempty"` // 计算规则 } // BaseValue 描述了字段值的基础结构。 @@ -67,8 +66,32 @@ type BaseInstance struct { Folded bool `json:"folded,omitempty"` // 是否折叠 Hidden bool `json:"hidden,omitempty"` // 是否隐藏 - Groups []Viewable `json:"groups,omitempty"` // 分组实例列表 - GroupCalc *GroupCalc `json:"groupCalc,omitempty"` // 分组计算规则和结果 + Groups []Viewable `json:"groups,omitempty"` // 分组实例列表 + GroupCalc *GroupCalc `json:"groupCalc,omitempty"` // 分组计算规则和结果 + GroupName string `json:"groupName,omitempty"` // 分组名称 + GroupFolded bool `json:"groupFolded,omitempty"` // 分组是否折叠 + GroupHidden bool `json:"groupHidden,omitempty"` // 分组是否隐藏 + GroupDefault bool `json:"groupDefault,omitempty"` // 是否为默认分组 +} + +func NewViewBaseInstance(view *View) *BaseInstance { + return &BaseInstance{ + ID: view.ID, + Icon: view.Icon, + Name: view.Name, + Desc: view.Desc, + HideAttrViewName: view.HideAttrViewName, + Filters: view.Filters, + Sorts: view.Sorts, + Group: view.Group, + GroupCalc: view.GroupCalc, + GroupName: view.GroupName, + GroupFolded: view.GroupFolded, + GroupHidden: view.GroupHidden, + GroupDefault: view.GroupDefault, + ShowIcon: view.Table.ShowIcon, + WrapField: view.Table.WrapField, + } } func (baseInstance *BaseInstance) GetSorts() []*ViewSort { @@ -83,19 +106,44 @@ func (baseInstance *BaseInstance) SetGroups(viewables []Viewable) { baseInstance.Groups = viewables } +func (baseInstance *BaseInstance) SetGroupCalc(group *GroupCalc) { + baseInstance.GroupCalc = group +} + +func (baseInstance *BaseInstance) GetGroupCalc() *GroupCalc { + return baseInstance.GroupCalc +} + +func (baseInstance *BaseInstance) SetGroupName(name string) { + baseInstance.GroupName = name +} + +func (baseInstance *BaseInstance) SetGroupFolded(folded bool) { + baseInstance.GroupFolded = folded +} + +func (baseInstance *BaseInstance) SetGroupHidden(hidden bool) { + baseInstance.GroupHidden = hidden +} + +func (baseInstance *BaseInstance) SetGroupDefault(defaulted bool) { + baseInstance.GroupDefault = defaulted +} + func (baseInstance *BaseInstance) GetID() string { return baseInstance.ID } // BaseInstanceField 描述了实例字段的基础结构。 type BaseInstanceField struct { - ID string `json:"id"` // ID - Name string `json:"name"` // 名称 - Type KeyType `json:"type"` // 类型 - Icon string `json:"icon"` // 图标 - Wrap bool `json:"wrap"` // 是否换行 - Hidden bool `json:"hidden"` // 是否隐藏 - Desc string `json:"desc"` // 描述 + ID string `json:"id"` // ID + Name string `json:"name"` // 名称 + Type KeyType `json:"type"` // 类型 + Icon string `json:"icon"` // 图标 + Wrap bool `json:"wrap"` // 是否换行 + Hidden bool `json:"hidden"` // 是否隐藏 + Desc string `json:"desc"` // 描述 + Calc *FieldCalc `json:"calc"` // 计算规则和结果 // 以下是某些字段类型的特有属性 @@ -111,6 +159,22 @@ func (baseInstanceField *BaseInstanceField) GetID() string { return baseInstanceField.ID } +func (baseInstanceField *BaseInstanceField) GetCalc() *FieldCalc { + return baseInstanceField.Calc +} + +func (baseInstanceField *BaseInstanceField) SetCalc(calc *FieldCalc) { + baseInstanceField.Calc = calc +} + +func (baseInstanceField *BaseInstanceField) GetType() KeyType { + return baseInstanceField.Type +} + +func (baseInstanceField *BaseInstanceField) GetNumberFormat() NumberFormat { + return baseInstanceField.NumberFormat +} + // Collection 描述了一个集合的接口。 // 集合可以是表格、卡片等,包含多个项目。 type Collection interface { @@ -124,6 +188,9 @@ type Collection interface { // GetFields 返回集合的所有字段。 GetFields() []Field + // GetField 返回指定 ID 的字段。 + GetField(id string) (ret Field) + // GetSorts 返回集合的排序规则。 GetSorts() []*ViewSort @@ -136,6 +203,18 @@ type Field interface { // GetID 返回字段的 ID。 GetID() string + + // GetType 返回字段的类型。 + GetType() KeyType + + // GetCalc 返回字段的计算规则和结果。 + GetCalc() *FieldCalc + + // SetCalc 设置字段的计算规则和结果。 + SetCalc(*FieldCalc) + + // GetNumberFormat 返回数字字段的格式化设置。 + GetNumberFormat() NumberFormat } // Item 描述了一个项目的接口。 @@ -154,169 +233,3 @@ type Item interface { // GetID 返回项目的 ID。 GetID() string } - -func sort0(collection Collection, attrView *AttributeView) { - sorts := collection.GetSorts() - if 1 > len(sorts) { - return - } - - type FieldIndexSort struct { - Index int - Order SortOrder - } - - var fieldIndexSorts []*FieldIndexSort - for _, s := range sorts { - for i, c := range collection.GetFields() { - if c.GetID() == s.Column { - fieldIndexSorts = append(fieldIndexSorts, &FieldIndexSort{Index: i, Order: s.Order}) - break - } - } - } - - items := collection.GetItems() - editedValItems := map[string]bool{} - for i, item := range items { - for _, fieldIndexSort := range fieldIndexSorts { - val := items[i].GetValues()[fieldIndexSort.Index] - if KeyTypeCheckbox == val.Type { - if block := item.GetBlockValue(); nil != block && block.IsEdited() { - // 如果主键编辑过,则勾选框也算作编辑过,参与排序 https://github.com/siyuan-note/siyuan/issues/11016 - editedValItems[item.GetID()] = true - break - } - } - - if val.IsEdited() { - // 如果该卡片某字段的值已经编辑过,则该卡片可参与排序 - editedValItems[item.GetID()] = true - break - } - } - } - - // 将未编辑的卡片和已编辑的卡片分开排序 - var uneditedItems, editedItems []Item - for _, item := range items { - if _, ok := editedValItems[item.GetID()]; ok { - editedItems = append(editedItems, item) - } else { - uneditedItems = append(uneditedItems, item) - } - } - - sort.Slice(uneditedItems, func(i, j int) bool { - val1 := uneditedItems[i].GetBlockValue() - if nil == val1 { - return true - } - val2 := uneditedItems[j].GetBlockValue() - if nil == val2 { - return false - } - return val1.CreatedAt < val2.CreatedAt - }) - - sort.Slice(editedItems, func(i, j int) bool { - sorted := true - for _, fieldIndexSort := range fieldIndexSorts { - val1 := editedItems[i].GetValues()[fieldIndexSort.Index] - val2 := editedItems[j].GetValues()[fieldIndexSort.Index] - if nil == val1 || val1.IsEmpty() { - if nil != val2 && !val2.IsEmpty() { - return false - } - sorted = false - continue - } else { - if nil == val2 || val2.IsEmpty() { - return true - } - } - - result := val1.Compare(val2, attrView) - if 0 == result { - sorted = false - continue - } - sorted = true - - if fieldIndexSort.Order == SortOrderAsc { - return 0 > result - } - return 0 < result - } - - if !sorted { - key1 := editedItems[i].GetBlockValue() - if nil == key1 { - return false - } - key2 := editedItems[j].GetBlockValue() - if nil == key2 { - return false - } - return key1.CreatedAt < key2.CreatedAt - } - return false - }) - - // 将包含未编辑的卡片放在最后 - collection.SetItems(append(editedItems, uneditedItems...)) - if 1 > len(collection.GetItems()) { - collection.SetItems([]Item{}) - } -} - -func filter0(collection Collection, attrView *AttributeView) { - filters := collection.GetFilters() - if 1 > len(filters) { - return - } - - var colIndexes []int - for _, f := range filters { - for i, c := range collection.GetFields() { - if c.GetID() == f.Column { - colIndexes = append(colIndexes, i) - break - } - } - } - - var items []Item - attrViewCache := map[string]*AttributeView{} - attrViewCache[attrView.ID] = attrView - for _, item := range collection.GetItems() { - pass := true - values := item.GetValues() - for j, index := range colIndexes { - operator := filters[j].Operator - - if nil == values[index] { - if FilterOperatorIsNotEmpty == operator { - pass = false - } else if FilterOperatorIsEmpty == operator { - pass = true - break - } - - if KeyTypeText != values[index].Type { - pass = false - } - break - } - - if !values[index].Filter(filters[j], attrView, item.GetID(), &attrViewCache) { - pass = false - break - } - } - if pass { - items = append(items, item) - } - } - collection.SetItems(items) -} diff --git a/kernel/av/layout_gallery.go b/kernel/av/layout_gallery.go index c30fbe42f..c83411f04 100644 --- a/kernel/av/layout_gallery.go +++ b/kernel/av/layout_gallery.go @@ -173,14 +173,15 @@ func (gallery *Gallery) GetFields() (ret []Field) { return ret } +func (gallery *Gallery) GetField(id string) Field { + for _, field := range gallery.Fields { + if field.ID == id { + return field + } + } + return nil +} + func (gallery *Gallery) GetType() LayoutType { return LayoutTypeGallery } - -func (gallery *Gallery) Sort(attrView *AttributeView) { - sort0(gallery, attrView) -} - -func (gallery *Gallery) Filter(attrView *AttributeView) { - filter0(gallery, attrView) -} diff --git a/kernel/av/layout_gallery_calc.go b/kernel/av/layout_gallery_calc.go deleted file mode 100644 index cb83abca4..000000000 --- a/kernel/av/layout_gallery_calc.go +++ /dev/null @@ -1,21 +0,0 @@ -// SiYuan - Refactor your thinking -// Copyright (c) 2020-present, b3log.org -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -package av - -func (gallery *Gallery) Calc() { - // 卡片视图不支持计算 -} diff --git a/kernel/av/layout_table.go b/kernel/av/layout_table.go index f1b26d67b..afb9bc82b 100644 --- a/kernel/av/layout_table.go +++ b/kernel/av/layout_table.go @@ -63,9 +63,8 @@ type Table struct { type TableColumn struct { *BaseInstanceField - Pin bool `json:"pin"` // 是否固定 - Width string `json:"width"` // 列宽度 - Calc *FieldCalc `json:"calc"` // 计算规则和结果 + Pin bool `json:"pin"` // 是否固定 + Width string `json:"width"` // 列宽度 } // TableRow 描述了表格实例行的结构。 @@ -150,18 +149,15 @@ func (table *Table) GetFields() (ret []Field) { return ret } +func (table *Table) GetField(id string) Field { + for _, column := range table.Columns { + if column.ID == id { + return column + } + } + return nil +} + func (*Table) GetType() LayoutType { return LayoutTypeTable } - -func (table *Table) GetID() string { - return table.ID -} - -func (table *Table) Sort(attrView *AttributeView) { - sort0(table, attrView) -} - -func (table *Table) Filter(attrView *AttributeView) { - filter0(table, attrView) -} diff --git a/kernel/av/layout_table_calc.go b/kernel/av/layout_table_calc.go deleted file mode 100644 index 6542d6d3a..000000000 --- a/kernel/av/layout_table_calc.go +++ /dev/null @@ -1,1665 +0,0 @@ -// SiYuan - Refactor your thinking -// Copyright (c) 2020-present, b3log.org -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -package av - -import ( - "math" - "sort" - - "github.com/siyuan-note/siyuan/kernel/util" -) - -func (table *Table) Calc() { - for i, col := range table.Columns { - if nil == col.Calc { - continue - } - - if CalcOperatorNone == col.Calc.Operator { - continue - } - - switch col.Type { - case KeyTypeBlock: - table.calcColBlock(col, i) - case KeyTypeText: - table.calcColText(col, i) - case KeyTypeNumber: - table.calcColNumber(col, i) - case KeyTypeDate: - table.calcColDate(col, i) - case KeyTypeSelect: - table.calcColSelect(col, i) - case KeyTypeMSelect: - table.calcColMSelect(col, i) - case KeyTypeURL: - table.calcColURL(col, i) - case KeyTypeEmail: - table.calcColEmail(col, i) - case KeyTypePhone: - table.calcColPhone(col, i) - case KeyTypeMAsset: - table.calcColMAsset(col, i) - case KeyTypeTemplate: - table.calcColTemplate(col, i) - case KeyTypeCreated: - table.calcColCreated(col, i) - case KeyTypeUpdated: - table.calcColUpdated(col, i) - case KeyTypeCheckbox: - table.calcColCheckbox(col, i) - case KeyTypeRelation: - table.calcColRelation(col, i) - case KeyTypeRollup: - table.calcColRollup(col, i) - } - } -} - -func (table *Table) calcColTemplate(col *TableColumn, colIndex int) { - switch col.Calc.Operator { - case CalcOperatorCountAll: - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(len(table.Rows)), NumberFormatNone)} - case CalcOperatorCountValues: - countValues := 0 - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Template && "" != row.Cells[colIndex].Value.Template.Content { - countValues++ - } - } - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countValues), NumberFormatNone)} - case CalcOperatorCountUniqueValues: - countUniqueValues := 0 - uniqueValues := map[string]bool{} - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Template && "" != row.Cells[colIndex].Value.Template.Content { - if !uniqueValues[row.Cells[colIndex].Value.Template.Content] { - uniqueValues[row.Cells[colIndex].Value.Template.Content] = true - countUniqueValues++ - } - } - } - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues), NumberFormatNone)} - case CalcOperatorCountEmpty: - countEmpty := 0 - for _, row := range table.Rows { - if nil == row.Cells[colIndex] || nil == row.Cells[colIndex].Value || nil == row.Cells[colIndex].Value.Template || "" == row.Cells[colIndex].Value.Template.Content { - countEmpty++ - } - } - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countEmpty), NumberFormatNone)} - case CalcOperatorCountNotEmpty: - countNotEmpty := 0 - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Template && "" != row.Cells[colIndex].Value.Template.Content { - countNotEmpty++ - } - } - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty), NumberFormatNone)} - case CalcOperatorPercentEmpty: - countEmpty := 0 - for _, row := range table.Rows { - if nil == row.Cells[colIndex] || nil == row.Cells[colIndex].Value || nil == row.Cells[colIndex].Value.Template || "" == row.Cells[colIndex].Value.Template.Content { - countEmpty++ - } - } - if 0 < len(table.Rows) { - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countEmpty)/float64(len(table.Rows)), NumberFormatPercent)} - } - case CalcOperatorPercentNotEmpty: - countNotEmpty := 0 - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Template && "" != row.Cells[colIndex].Value.Template.Content { - countNotEmpty++ - } - } - if 0 < len(table.Rows) { - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty)/float64(len(table.Rows)), NumberFormatPercent)} - } - case CalcOperatorPercentUniqueValues: - countUniqueValues := 0 - uniqueValues := map[string]bool{} - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Template && "" != row.Cells[colIndex].Value.Template.Content { - if !uniqueValues[row.Cells[colIndex].Value.Template.Content] { - uniqueValues[row.Cells[colIndex].Value.Template.Content] = true - countUniqueValues++ - } - } - } - if 0 < len(table.Rows) { - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues)/float64(len(table.Rows)), NumberFormatPercent)} - } - case CalcOperatorSum: - sum := 0.0 - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Template && "" != row.Cells[colIndex].Value.Template.Content { - val, _ := util.Convert2Float(row.Cells[colIndex].Value.Template.Content) - sum += val - } - } - col.Calc.Result = &Value{Number: NewFormattedValueNumber(sum, col.NumberFormat)} - case CalcOperatorAverage: - sum := 0.0 - count := 0 - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Template && "" != row.Cells[colIndex].Value.Template.Content { - val, _ := util.Convert2Float(row.Cells[colIndex].Value.Template.Content) - sum += val - count++ - } - } - if 0 != count { - col.Calc.Result = &Value{Number: NewFormattedValueNumber(sum/float64(count), col.NumberFormat)} - } - case CalcOperatorMedian: - values := []float64{} - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Template && "" != row.Cells[colIndex].Value.Template.Content { - val, _ := util.Convert2Float(row.Cells[colIndex].Value.Template.Content) - values = append(values, val) - } - } - sort.Float64s(values) - if len(values) > 0 { - if len(values)%2 == 0 { - col.Calc.Result = &Value{Number: NewFormattedValueNumber((values[len(values)/2-1]+values[len(values)/2])/2, col.NumberFormat)} - } else { - col.Calc.Result = &Value{Number: NewFormattedValueNumber(values[len(values)/2], col.NumberFormat)} - } - } - case CalcOperatorMin: - minVal := math.MaxFloat64 - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Template && "" != row.Cells[colIndex].Value.Template.Content { - val, _ := util.Convert2Float(row.Cells[colIndex].Value.Template.Content) - if val < minVal { - minVal = val - } - } - } - if math.MaxFloat64 != minVal { - col.Calc.Result = &Value{Number: NewFormattedValueNumber(minVal, col.NumberFormat)} - } - case CalcOperatorMax: - maxVal := -math.MaxFloat64 - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Template && "" != row.Cells[colIndex].Value.Template.Content { - val, _ := util.Convert2Float(row.Cells[colIndex].Value.Template.Content) - if val > maxVal { - maxVal = val - } - } - } - if -math.MaxFloat64 != maxVal { - col.Calc.Result = &Value{Number: NewFormattedValueNumber(maxVal, col.NumberFormat)} - } - case CalcOperatorRange: - minVal := math.MaxFloat64 - maxVal := -math.MaxFloat64 - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Template && "" != row.Cells[colIndex].Value.Template.Content { - val, _ := util.Convert2Float(row.Cells[colIndex].Value.Template.Content) - if val < minVal { - minVal = val - } - if val > maxVal { - maxVal = val - } - } - } - if math.MaxFloat64 != minVal && -math.MaxFloat64 != maxVal { - col.Calc.Result = &Value{Number: NewFormattedValueNumber(maxVal-minVal, col.NumberFormat)} - } - } -} - -func (table *Table) calcColMAsset(col *TableColumn, colIndex int) { - switch col.Calc.Operator { - case CalcOperatorCountAll: - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(len(table.Rows)), NumberFormatNone)} - case CalcOperatorCountValues: - countValues := 0 - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.MAsset && 0 < len(row.Cells[colIndex].Value.MAsset) { - countValues += len(row.Cells[colIndex].Value.MAsset) - } - } - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countValues), NumberFormatNone)} - case CalcOperatorCountUniqueValues: - countUniqueValues := 0 - uniqueValues := map[string]bool{} - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.MAsset && 0 < len(row.Cells[colIndex].Value.MAsset) { - for _, sel := range row.Cells[colIndex].Value.MAsset { - if _, ok := uniqueValues[sel.Content]; !ok { - uniqueValues[sel.Content] = true - countUniqueValues++ - } - } - } - } - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues), NumberFormatNone)} - case CalcOperatorCountEmpty: - countEmpty := 0 - for _, row := range table.Rows { - if nil == row.Cells[colIndex] || nil == row.Cells[colIndex].Value || nil == row.Cells[colIndex].Value.MAsset || 0 == len(row.Cells[colIndex].Value.MAsset) { - countEmpty++ - } - } - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countEmpty), NumberFormatNone)} - case CalcOperatorCountNotEmpty: - countNotEmpty := 0 - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.MAsset && 0 < len(row.Cells[colIndex].Value.MAsset) { - countNotEmpty++ - } - } - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty), NumberFormatNone)} - case CalcOperatorPercentEmpty: - countEmpty := 0 - for _, row := range table.Rows { - if nil == row.Cells[colIndex] || nil == row.Cells[colIndex].Value || nil == row.Cells[colIndex].Value.MAsset || 0 == len(row.Cells[colIndex].Value.MAsset) { - countEmpty++ - } - } - if 0 < len(table.Rows) { - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countEmpty)/float64(len(table.Rows)), NumberFormatPercent)} - } - case CalcOperatorPercentNotEmpty: - countNotEmpty := 0 - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.MAsset && 0 < len(row.Cells[colIndex].Value.MAsset) { - countNotEmpty++ - } - } - if 0 < len(table.Rows) { - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty)/float64(len(table.Rows)), NumberFormatPercent)} - } - case CalcOperatorPercentUniqueValues: - countUniqueValues := 0 - uniqueValues := map[string]bool{} - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.MAsset && 0 < len(row.Cells[colIndex].Value.MAsset) { - for _, sel := range row.Cells[colIndex].Value.MAsset { - if _, ok := uniqueValues[sel.Content]; !ok { - uniqueValues[sel.Content] = true - countUniqueValues++ - } - } - } - } - if 0 < len(table.Rows) { - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues)/float64(len(table.Rows)), NumberFormatPercent)} - } - } -} - -func (table *Table) calcColMSelect(col *TableColumn, colIndex int) { - switch col.Calc.Operator { - case CalcOperatorCountAll: - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(len(table.Rows)), NumberFormatNone)} - case CalcOperatorCountValues: - countValues := 0 - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.MSelect && 0 < len(row.Cells[colIndex].Value.MSelect) { - countValues += len(row.Cells[colIndex].Value.MSelect) - } - } - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countValues), NumberFormatNone)} - case CalcOperatorCountUniqueValues: - countUniqueValues := 0 - uniqueValues := map[string]bool{} - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.MSelect && 0 < len(row.Cells[colIndex].Value.MSelect) { - for _, sel := range row.Cells[colIndex].Value.MSelect { - if _, ok := uniqueValues[sel.Content]; !ok { - uniqueValues[sel.Content] = true - countUniqueValues++ - } - } - } - } - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues), NumberFormatNone)} - case CalcOperatorCountEmpty: - countEmpty := 0 - for _, row := range table.Rows { - if nil == row.Cells[colIndex] || nil == row.Cells[colIndex].Value || nil == row.Cells[colIndex].Value.MSelect || 0 == len(row.Cells[colIndex].Value.MSelect) { - countEmpty++ - } - } - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countEmpty), NumberFormatNone)} - case CalcOperatorCountNotEmpty: - countNotEmpty := 0 - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.MSelect && 0 < len(row.Cells[colIndex].Value.MSelect) { - countNotEmpty++ - } - } - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty), NumberFormatNone)} - case CalcOperatorPercentEmpty: - countEmpty := 0 - for _, row := range table.Rows { - if nil == row.Cells[colIndex] || nil == row.Cells[colIndex].Value || nil == row.Cells[colIndex].Value.MSelect || 0 == len(row.Cells[colIndex].Value.MSelect) { - countEmpty++ - } - } - if 0 < len(table.Rows) { - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countEmpty)/float64(len(table.Rows)), NumberFormatPercent)} - } - case CalcOperatorPercentNotEmpty: - countNotEmpty := 0 - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.MSelect && 0 < len(row.Cells[colIndex].Value.MSelect) { - countNotEmpty++ - } - } - if 0 < len(table.Rows) { - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty)/float64(len(table.Rows)), NumberFormatPercent)} - } - case CalcOperatorPercentUniqueValues: - countUniqueValues := 0 - uniqueValues := map[string]bool{} - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.MSelect && 0 < len(row.Cells[colIndex].Value.MSelect) { - for _, sel := range row.Cells[colIndex].Value.MSelect { - if _, ok := uniqueValues[sel.Content]; !ok { - uniqueValues[sel.Content] = true - countUniqueValues++ - } - } - } - } - if 0 < len(table.Rows) { - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues)/float64(len(table.Rows)), NumberFormatPercent)} - } - } -} - -func (table *Table) calcColSelect(col *TableColumn, colIndex int) { - switch col.Calc.Operator { - case CalcOperatorCountAll: - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(len(table.Rows)), NumberFormatNone)} - case CalcOperatorCountValues: - countValues := 0 - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.MSelect && 0 < len(row.Cells[colIndex].Value.MSelect) && nil != row.Cells[colIndex].Value.MSelect[0] && "" != row.Cells[colIndex].Value.MSelect[0].Content { - countValues++ - } - } - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countValues), NumberFormatNone)} - case CalcOperatorCountUniqueValues: - countUniqueValues := 0 - uniqueValues := map[string]bool{} - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.MSelect && 0 < len(row.Cells[colIndex].Value.MSelect) && nil != row.Cells[colIndex].Value.MSelect[0] && "" != row.Cells[colIndex].Value.MSelect[0].Content { - if _, ok := uniqueValues[row.Cells[colIndex].Value.MSelect[0].Content]; !ok { - uniqueValues[row.Cells[colIndex].Value.MSelect[0].Content] = true - countUniqueValues++ - } - } - } - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues), NumberFormatNone)} - case CalcOperatorCountEmpty: - countEmpty := 0 - for _, row := range table.Rows { - if nil == row.Cells[colIndex] || nil == row.Cells[colIndex].Value || nil == row.Cells[colIndex].Value.MSelect || 1 > len(row.Cells[colIndex].Value.MSelect) || nil == row.Cells[colIndex].Value.MSelect[0] || "" == row.Cells[colIndex].Value.MSelect[0].Content { - countEmpty++ - } - } - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countEmpty), NumberFormatNone)} - case CalcOperatorCountNotEmpty: - countNotEmpty := 0 - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.MSelect && 0 < len(row.Cells[colIndex].Value.MSelect) && nil != row.Cells[colIndex].Value.MSelect[0] && "" != row.Cells[colIndex].Value.MSelect[0].Content { - countNotEmpty++ - } - } - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty), NumberFormatNone)} - case CalcOperatorPercentEmpty: - countEmpty := 0 - for _, row := range table.Rows { - if nil == row.Cells[colIndex] || nil == row.Cells[colIndex].Value || nil == row.Cells[colIndex].Value.MSelect || 1 > len(row.Cells[colIndex].Value.MSelect) || nil == row.Cells[colIndex].Value.MSelect[0] || "" == row.Cells[colIndex].Value.MSelect[0].Content { - countEmpty++ - } - } - if 0 < len(table.Rows) { - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countEmpty)/float64(len(table.Rows)), NumberFormatPercent)} - } - case CalcOperatorPercentNotEmpty: - countNotEmpty := 0 - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.MSelect && 0 < len(row.Cells[colIndex].Value.MSelect) && nil != row.Cells[colIndex].Value.MSelect[0] && "" != row.Cells[colIndex].Value.MSelect[0].Content { - countNotEmpty++ - } - } - if 0 < len(table.Rows) { - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty)/float64(len(table.Rows)), NumberFormatPercent)} - } - case CalcOperatorPercentUniqueValues: - countUniqueValues := 0 - uniqueValues := map[string]bool{} - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.MSelect && 0 < len(row.Cells[colIndex].Value.MSelect) && nil != row.Cells[colIndex].Value.MSelect[0] && "" != row.Cells[colIndex].Value.MSelect[0].Content { - if _, ok := uniqueValues[row.Cells[colIndex].Value.MSelect[0].Content]; !ok { - uniqueValues[row.Cells[colIndex].Value.MSelect[0].Content] = true - countUniqueValues++ - } - } - } - if 0 < len(table.Rows) { - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues)/float64(len(table.Rows)), NumberFormatPercent)} - } - } -} - -func (table *Table) calcColDate(col *TableColumn, colIndex int) { - switch col.Calc.Operator { - case CalcOperatorCountAll: - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(len(table.Rows)), NumberFormatNone)} - case CalcOperatorCountValues: - countValues := 0 - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Date && row.Cells[colIndex].Value.Date.IsNotEmpty { - countValues++ - } - } - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countValues), NumberFormatNone)} - case CalcOperatorCountUniqueValues: - countUniqueValues := 0 - uniqueValues := map[int64]bool{} - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Date && row.Cells[colIndex].Value.Date.IsNotEmpty { - if _, ok := uniqueValues[row.Cells[colIndex].Value.Date.Content]; !ok { - countUniqueValues++ - uniqueValues[row.Cells[colIndex].Value.Date.Content] = true - } - } - } - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues), NumberFormatNone)} - case CalcOperatorCountEmpty: - countEmpty := 0 - for _, row := range table.Rows { - if nil == row.Cells[colIndex] || nil == row.Cells[colIndex].Value || nil == row.Cells[colIndex].Value.Date || !row.Cells[colIndex].Value.Date.IsNotEmpty { - countEmpty++ - } - } - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countEmpty), NumberFormatNone)} - case CalcOperatorCountNotEmpty: - countNotEmpty := 0 - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Date && row.Cells[colIndex].Value.Date.IsNotEmpty { - countNotEmpty++ - } - } - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty), NumberFormatNone)} - case CalcOperatorPercentEmpty: - countEmpty := 0 - for _, row := range table.Rows { - if nil == row.Cells[colIndex] || nil == row.Cells[colIndex].Value || nil == row.Cells[colIndex].Value.Date || !row.Cells[colIndex].Value.Date.IsNotEmpty { - countEmpty++ - } - } - if 0 < len(table.Rows) { - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countEmpty)/float64(len(table.Rows)), NumberFormatPercent)} - } - case CalcOperatorPercentNotEmpty: - countNotEmpty := 0 - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Date && row.Cells[colIndex].Value.Date.IsNotEmpty { - countNotEmpty++ - } - } - if 0 < len(table.Rows) { - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty)/float64(len(table.Rows)), NumberFormatPercent)} - } - case CalcOperatorPercentUniqueValues: - countUniqueValues := 0 - uniqueValues := map[int64]bool{} - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Date && row.Cells[colIndex].Value.Date.IsNotEmpty { - if _, ok := uniqueValues[row.Cells[colIndex].Value.Date.Content]; !ok { - countUniqueValues++ - uniqueValues[row.Cells[colIndex].Value.Date.Content] = true - } - } - } - if 0 < len(table.Rows) { - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues)/float64(len(table.Rows)), NumberFormatPercent)} - } - case CalcOperatorEarliest: - earliest := int64(0) - var isNotTime, hasEndDate bool - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Date && row.Cells[colIndex].Value.Date.IsNotEmpty { - if 0 == earliest || earliest > row.Cells[colIndex].Value.Date.Content { - earliest = row.Cells[colIndex].Value.Date.Content - isNotTime = row.Cells[colIndex].Value.Date.IsNotTime - hasEndDate = row.Cells[colIndex].Value.Date.HasEndDate - } - } - } - if 0 != earliest { - col.Calc.Result = &Value{Date: NewFormattedValueDate(earliest, 0, DateFormatNone, isNotTime, hasEndDate)} - } - case CalcOperatorLatest: - latest := int64(0) - var isNotTime, hasEndDate bool - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Date && row.Cells[colIndex].Value.Date.IsNotEmpty { - if 0 == latest || latest < row.Cells[colIndex].Value.Date.Content { - latest = row.Cells[colIndex].Value.Date.Content - isNotTime = row.Cells[colIndex].Value.Date.IsNotTime - hasEndDate = row.Cells[colIndex].Value.Date.HasEndDate - } - } - } - if 0 != latest { - col.Calc.Result = &Value{Date: NewFormattedValueDate(latest, 0, DateFormatNone, isNotTime, hasEndDate)} - } - case CalcOperatorRange: - earliest := int64(0) - latest := int64(0) - var isNotTime, hasEndDate bool - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Date && row.Cells[colIndex].Value.Date.IsNotEmpty { - if 0 == earliest || earliest > row.Cells[colIndex].Value.Date.Content { - earliest = row.Cells[colIndex].Value.Date.Content - isNotTime = row.Cells[colIndex].Value.Date.IsNotTime - hasEndDate = row.Cells[colIndex].Value.Date.HasEndDate - } - if 0 == latest || latest < row.Cells[colIndex].Value.Date.Content { - latest = row.Cells[colIndex].Value.Date.Content - isNotTime = row.Cells[colIndex].Value.Date.IsNotTime - hasEndDate = row.Cells[colIndex].Value.Date.HasEndDate - } - } - } - if 0 != earliest && 0 != latest { - col.Calc.Result = &Value{Date: NewFormattedValueDate(earliest, latest, DateFormatDuration, isNotTime, hasEndDate)} - } - } -} - -func (table *Table) calcColNumber(col *TableColumn, colIndex int) { - switch col.Calc.Operator { - case CalcOperatorCountAll: - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(len(table.Rows)), NumberFormatNone)} - case CalcOperatorCountValues: - countValues := 0 - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Number && row.Cells[colIndex].Value.Number.IsNotEmpty { - countValues++ - } - } - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countValues), NumberFormatNone)} - case CalcOperatorCountUniqueValues: - countUniqueValues := 0 - uniqueValues := map[float64]bool{} - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Number && row.Cells[colIndex].Value.Number.IsNotEmpty { - if !uniqueValues[row.Cells[colIndex].Value.Number.Content] { - uniqueValues[row.Cells[colIndex].Value.Number.Content] = true - countUniqueValues++ - } - } - } - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues), NumberFormatNone)} - case CalcOperatorCountEmpty: - countEmpty := 0 - for _, row := range table.Rows { - if nil == row.Cells[colIndex] || nil == row.Cells[colIndex].Value || nil == row.Cells[colIndex].Value.Number || !row.Cells[colIndex].Value.Number.IsNotEmpty { - countEmpty++ - } - } - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countEmpty), NumberFormatNone)} - case CalcOperatorCountNotEmpty: - countNotEmpty := 0 - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Number && row.Cells[colIndex].Value.Number.IsNotEmpty { - countNotEmpty++ - } - } - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty), NumberFormatNone)} - case CalcOperatorPercentEmpty: - countEmpty := 0 - for _, row := range table.Rows { - if nil == row.Cells[colIndex] || nil == row.Cells[colIndex].Value || nil == row.Cells[colIndex].Value.Number || !row.Cells[colIndex].Value.Number.IsNotEmpty { - countEmpty++ - } - } - if 0 < len(table.Rows) { - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countEmpty)/float64(len(table.Rows)), NumberFormatPercent)} - } - case CalcOperatorPercentNotEmpty: - countNotEmpty := 0 - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Number && row.Cells[colIndex].Value.Number.IsNotEmpty { - countNotEmpty++ - } - } - if 0 < len(table.Rows) { - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty)/float64(len(table.Rows)), NumberFormatPercent)} - } - case CalcOperatorPercentUniqueValues: - countUniqueValues := 0 - uniqueValues := map[float64]bool{} - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Number && row.Cells[colIndex].Value.Number.IsNotEmpty { - if !uniqueValues[row.Cells[colIndex].Value.Number.Content] { - uniqueValues[row.Cells[colIndex].Value.Number.Content] = true - countUniqueValues++ - } - } - } - if 0 < len(table.Rows) { - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues)/float64(len(table.Rows)), NumberFormatPercent)} - } - case CalcOperatorSum: - sum := 0.0 - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Number && row.Cells[colIndex].Value.Number.IsNotEmpty { - sum += row.Cells[colIndex].Value.Number.Content - } - } - col.Calc.Result = &Value{Number: NewFormattedValueNumber(sum, col.NumberFormat)} - case CalcOperatorAverage: - sum := 0.0 - count := 0 - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Number && row.Cells[colIndex].Value.Number.IsNotEmpty { - sum += row.Cells[colIndex].Value.Number.Content - count++ - } - } - if 0 != count { - col.Calc.Result = &Value{Number: NewFormattedValueNumber(sum/float64(count), col.NumberFormat)} - } - case CalcOperatorMedian: - values := []float64{} - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Number && row.Cells[colIndex].Value.Number.IsNotEmpty { - values = append(values, row.Cells[colIndex].Value.Number.Content) - } - } - sort.Float64s(values) - if len(values) > 0 { - if len(values)%2 == 0 { - col.Calc.Result = &Value{Number: NewFormattedValueNumber((values[len(values)/2-1]+values[len(values)/2])/2, col.NumberFormat)} - } else { - col.Calc.Result = &Value{Number: NewFormattedValueNumber(values[len(values)/2], col.NumberFormat)} - } - } - case CalcOperatorMin: - minVal := math.MaxFloat64 - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Number && row.Cells[colIndex].Value.Number.IsNotEmpty { - if row.Cells[colIndex].Value.Number.Content < minVal { - minVal = row.Cells[colIndex].Value.Number.Content - } - } - } - if math.MaxFloat64 != minVal { - col.Calc.Result = &Value{Number: NewFormattedValueNumber(minVal, col.NumberFormat)} - } - case CalcOperatorMax: - maxVal := -math.MaxFloat64 - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Number && row.Cells[colIndex].Value.Number.IsNotEmpty { - if row.Cells[colIndex].Value.Number.Content > maxVal { - maxVal = row.Cells[colIndex].Value.Number.Content - } - } - } - if -math.MaxFloat64 != maxVal { - col.Calc.Result = &Value{Number: NewFormattedValueNumber(maxVal, col.NumberFormat)} - } - case CalcOperatorRange: - minVal := math.MaxFloat64 - maxVal := -math.MaxFloat64 - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Number && row.Cells[colIndex].Value.Number.IsNotEmpty { - if row.Cells[colIndex].Value.Number.Content < minVal { - minVal = row.Cells[colIndex].Value.Number.Content - } - if row.Cells[colIndex].Value.Number.Content > maxVal { - maxVal = row.Cells[colIndex].Value.Number.Content - } - } - } - if math.MaxFloat64 != minVal && -math.MaxFloat64 != maxVal { - col.Calc.Result = &Value{Number: NewFormattedValueNumber(maxVal-minVal, col.NumberFormat)} - } - } -} - -func (table *Table) calcColText(col *TableColumn, colIndex int) { - switch col.Calc.Operator { - case CalcOperatorCountAll: - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(len(table.Rows)), NumberFormatNone)} - case CalcOperatorCountValues: - countValues := 0 - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Text && "" != row.Cells[colIndex].Value.Text.Content { - countValues++ - } - } - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countValues), NumberFormatNone)} - case CalcOperatorCountUniqueValues: - countUniqueValues := 0 - uniqueValues := map[string]bool{} - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Text && "" != row.Cells[colIndex].Value.Text.Content { - if !uniqueValues[row.Cells[colIndex].Value.Text.Content] { - uniqueValues[row.Cells[colIndex].Value.Text.Content] = true - countUniqueValues++ - } - } - } - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues), NumberFormatNone)} - case CalcOperatorCountEmpty: - countEmpty := 0 - for _, row := range table.Rows { - if nil == row.Cells[colIndex] || nil == row.Cells[colIndex].Value || nil == row.Cells[colIndex].Value.Text || "" == row.Cells[colIndex].Value.Text.Content { - countEmpty++ - } - } - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countEmpty), NumberFormatNone)} - case CalcOperatorCountNotEmpty: - countNotEmpty := 0 - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Text && "" != row.Cells[colIndex].Value.Text.Content { - countNotEmpty++ - } - } - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty), NumberFormatNone)} - case CalcOperatorPercentEmpty: - countEmpty := 0 - for _, row := range table.Rows { - if nil == row.Cells[colIndex] || nil == row.Cells[colIndex].Value || nil == row.Cells[colIndex].Value.Text || "" == row.Cells[colIndex].Value.Text.Content { - countEmpty++ - } - } - if 0 < len(table.Rows) { - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countEmpty)/float64(len(table.Rows)), NumberFormatPercent)} - } - case CalcOperatorPercentNotEmpty: - countNotEmpty := 0 - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Text && "" != row.Cells[colIndex].Value.Text.Content { - countNotEmpty++ - } - } - if 0 < len(table.Rows) { - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty)/float64(len(table.Rows)), NumberFormatPercent)} - } - case CalcOperatorPercentUniqueValues: - countUniqueValues := 0 - uniqueValues := map[string]bool{} - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Text && "" != row.Cells[colIndex].Value.Text.Content { - if !uniqueValues[row.Cells[colIndex].Value.Text.Content] { - uniqueValues[row.Cells[colIndex].Value.Text.Content] = true - countUniqueValues++ - } - } - } - if 0 < len(table.Rows) { - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues)/float64(len(table.Rows)), NumberFormatPercent)} - } - } -} - -func (table *Table) calcColURL(col *TableColumn, colIndex int) { - switch col.Calc.Operator { - case CalcOperatorCountAll: - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(len(table.Rows)), NumberFormatNone)} - case CalcOperatorCountValues: - countValues := 0 - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.URL && "" != row.Cells[colIndex].Value.URL.Content { - countValues++ - } - } - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countValues), NumberFormatNone)} - case CalcOperatorCountUniqueValues: - countUniqueValues := 0 - uniqueValues := map[string]bool{} - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.URL && "" != row.Cells[colIndex].Value.URL.Content { - if !uniqueValues[row.Cells[colIndex].Value.URL.Content] { - uniqueValues[row.Cells[colIndex].Value.URL.Content] = true - countUniqueValues++ - } - } - } - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues), NumberFormatNone)} - case CalcOperatorCountEmpty: - countEmpty := 0 - for _, row := range table.Rows { - if nil == row.Cells[colIndex] || nil == row.Cells[colIndex].Value || nil == row.Cells[colIndex].Value.URL || "" == row.Cells[colIndex].Value.URL.Content { - countEmpty++ - } - } - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countEmpty), NumberFormatNone)} - case CalcOperatorCountNotEmpty: - countNotEmpty := 0 - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.URL && "" != row.Cells[colIndex].Value.URL.Content { - countNotEmpty++ - } - } - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty), NumberFormatNone)} - case CalcOperatorPercentEmpty: - countEmpty := 0 - for _, row := range table.Rows { - if nil == row.Cells[colIndex] || nil == row.Cells[colIndex].Value || nil == row.Cells[colIndex].Value.URL || "" == row.Cells[colIndex].Value.URL.Content { - countEmpty++ - } - } - if 0 < len(table.Rows) { - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countEmpty)/float64(len(table.Rows)), NumberFormatPercent)} - } - case CalcOperatorPercentNotEmpty: - countNotEmpty := 0 - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.URL && "" != row.Cells[colIndex].Value.URL.Content { - countNotEmpty++ - } - } - if 0 < len(table.Rows) { - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty)/float64(len(table.Rows)), NumberFormatPercent)} - } - case CalcOperatorPercentUniqueValues: - countUniqueValues := 0 - uniqueValues := map[string]bool{} - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.URL && "" != row.Cells[colIndex].Value.URL.Content { - if !uniqueValues[row.Cells[colIndex].Value.URL.Content] { - uniqueValues[row.Cells[colIndex].Value.URL.Content] = true - countUniqueValues++ - } - } - } - if 0 < len(table.Rows) { - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues)/float64(len(table.Rows)), NumberFormatPercent)} - } - } -} - -func (table *Table) calcColEmail(col *TableColumn, colIndex int) { - switch col.Calc.Operator { - case CalcOperatorCountAll: - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(len(table.Rows)), NumberFormatNone)} - case CalcOperatorCountValues: - countValues := 0 - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Email && "" != row.Cells[colIndex].Value.Email.Content { - countValues++ - } - } - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countValues), NumberFormatNone)} - case CalcOperatorCountUniqueValues: - countUniqueValues := 0 - uniqueValues := map[string]bool{} - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Email && "" != row.Cells[colIndex].Value.Email.Content { - if !uniqueValues[row.Cells[colIndex].Value.Email.Content] { - uniqueValues[row.Cells[colIndex].Value.Email.Content] = true - countUniqueValues++ - } - } - } - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues), NumberFormatNone)} - case CalcOperatorCountEmpty: - countEmpty := 0 - for _, row := range table.Rows { - if nil == row.Cells[colIndex] || nil == row.Cells[colIndex].Value || nil == row.Cells[colIndex].Value.Email || "" == row.Cells[colIndex].Value.Email.Content { - countEmpty++ - } - } - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countEmpty), NumberFormatNone)} - case CalcOperatorCountNotEmpty: - countNotEmpty := 0 - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Email && "" != row.Cells[colIndex].Value.Email.Content { - countNotEmpty++ - } - } - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty), NumberFormatNone)} - case CalcOperatorPercentEmpty: - countEmpty := 0 - for _, row := range table.Rows { - if nil == row.Cells[colIndex] || nil == row.Cells[colIndex].Value || nil == row.Cells[colIndex].Value.Email || "" == row.Cells[colIndex].Value.Email.Content { - countEmpty++ - } - } - if 0 < len(table.Rows) { - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countEmpty)/float64(len(table.Rows)), NumberFormatPercent)} - } - case CalcOperatorPercentNotEmpty: - countNotEmpty := 0 - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Email && "" != row.Cells[colIndex].Value.Email.Content { - countNotEmpty++ - } - } - if 0 < len(table.Rows) { - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty)/float64(len(table.Rows)), NumberFormatPercent)} - } - case CalcOperatorPercentUniqueValues: - countUniqueValues := 0 - uniqueValues := map[string]bool{} - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Email && "" != row.Cells[colIndex].Value.Email.Content { - if !uniqueValues[row.Cells[colIndex].Value.Email.Content] { - uniqueValues[row.Cells[colIndex].Value.Email.Content] = true - countUniqueValues++ - } - } - } - if 0 < len(table.Rows) { - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues)/float64(len(table.Rows)), NumberFormatPercent)} - } - } -} - -func (table *Table) calcColPhone(col *TableColumn, colIndex int) { - switch col.Calc.Operator { - case CalcOperatorCountAll: - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(len(table.Rows)), NumberFormatNone)} - case CalcOperatorCountValues: - countValues := 0 - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Phone && "" != row.Cells[colIndex].Value.Phone.Content { - countValues++ - } - } - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countValues), NumberFormatNone)} - case CalcOperatorCountUniqueValues: - countUniqueValues := 0 - uniqueValues := map[string]bool{} - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Phone && "" != row.Cells[colIndex].Value.Phone.Content { - if !uniqueValues[row.Cells[colIndex].Value.Phone.Content] { - uniqueValues[row.Cells[colIndex].Value.Phone.Content] = true - countUniqueValues++ - } - } - } - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues), NumberFormatNone)} - case CalcOperatorCountEmpty: - countEmpty := 0 - for _, row := range table.Rows { - if nil == row.Cells[colIndex] || nil == row.Cells[colIndex].Value || nil == row.Cells[colIndex].Value.Phone || "" == row.Cells[colIndex].Value.Phone.Content { - countEmpty++ - } - } - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countEmpty), NumberFormatNone)} - case CalcOperatorCountNotEmpty: - countNotEmpty := 0 - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Phone && "" != row.Cells[colIndex].Value.Phone.Content { - countNotEmpty++ - } - } - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty), NumberFormatNone)} - case CalcOperatorPercentEmpty: - countEmpty := 0 - for _, row := range table.Rows { - if nil == row.Cells[colIndex] || nil == row.Cells[colIndex].Value || nil == row.Cells[colIndex].Value.Phone || "" == row.Cells[colIndex].Value.Phone.Content { - countEmpty++ - } - } - if 0 < len(table.Rows) { - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countEmpty)/float64(len(table.Rows)), NumberFormatPercent)} - } - case CalcOperatorPercentNotEmpty: - countNotEmpty := 0 - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Phone && "" != row.Cells[colIndex].Value.Phone.Content { - countNotEmpty++ - } - } - if 0 < len(table.Rows) { - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty)/float64(len(table.Rows)), NumberFormatPercent)} - } - case CalcOperatorPercentUniqueValues: - countUniqueValues := 0 - uniqueValues := map[string]bool{} - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Phone && "" != row.Cells[colIndex].Value.Phone.Content { - if !uniqueValues[row.Cells[colIndex].Value.Phone.Content] { - uniqueValues[row.Cells[colIndex].Value.Phone.Content] = true - countUniqueValues++ - } - } - } - if 0 < len(table.Rows) { - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues)/float64(len(table.Rows)), NumberFormatPercent)} - } - } -} - -func (table *Table) calcColBlock(col *TableColumn, colIndex int) { - switch col.Calc.Operator { - case CalcOperatorCountAll: - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(len(table.Rows)), NumberFormatNone)} - case CalcOperatorCountValues: - countValues := 0 - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Block && "" != row.Cells[colIndex].Value.Block.Content { - countValues++ - } - } - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countValues), NumberFormatNone)} - case CalcOperatorCountUniqueValues: - countUniqueValues := 0 - uniqueValues := map[string]bool{} - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Block && "" != row.Cells[colIndex].Value.Block.Content { - if !uniqueValues[row.Cells[colIndex].Value.Block.Content] { - uniqueValues[row.Cells[colIndex].Value.Block.Content] = true - countUniqueValues++ - } - } - } - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues), NumberFormatNone)} - case CalcOperatorCountEmpty: - countEmpty := 0 - for _, row := range table.Rows { - if nil == row.Cells[colIndex] || nil == row.Cells[colIndex].Value || nil == row.Cells[colIndex].Value.Block || "" == row.Cells[colIndex].Value.Block.Content { - countEmpty++ - } - } - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countEmpty), NumberFormatNone)} - case CalcOperatorCountNotEmpty: - countNotEmpty := 0 - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Block && "" != row.Cells[colIndex].Value.Block.Content { - countNotEmpty++ - } - } - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty), NumberFormatNone)} - case CalcOperatorPercentEmpty: - countEmpty := 0 - for _, row := range table.Rows { - if nil == row.Cells[colIndex] || nil == row.Cells[colIndex].Value || nil == row.Cells[colIndex].Value.Block || "" == row.Cells[colIndex].Value.Block.Content { - countEmpty++ - } - } - if 0 < len(table.Rows) { - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countEmpty)/float64(len(table.Rows)), NumberFormatPercent)} - } - case CalcOperatorPercentNotEmpty: - countNotEmpty := 0 - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Block && "" != row.Cells[colIndex].Value.Block.Content { - countNotEmpty++ - } - } - if 0 < len(table.Rows) { - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty)/float64(len(table.Rows)), NumberFormatPercent)} - } - case CalcOperatorPercentUniqueValues: - countUniqueValues := 0 - uniqueValues := map[string]bool{} - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Block && "" != row.Cells[colIndex].Value.Block.Content { - if !uniqueValues[row.Cells[colIndex].Value.Block.Content] { - uniqueValues[row.Cells[colIndex].Value.Block.Content] = true - countUniqueValues++ - } - } - } - if 0 < len(table.Rows) { - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues)/float64(len(table.Rows)), NumberFormatPercent)} - } - } -} - -func (table *Table) calcColCreated(col *TableColumn, colIndex int) { - switch col.Calc.Operator { - case CalcOperatorCountAll: - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(len(table.Rows)), NumberFormatNone)} - case CalcOperatorCountValues: - countValues := 0 - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Created { - countValues++ - } - } - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countValues), NumberFormatNone)} - case CalcOperatorCountUniqueValues: - countUniqueValues := 0 - uniqueValues := map[int64]bool{} - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Created { - if _, ok := uniqueValues[row.Cells[colIndex].Value.Created.Content]; !ok { - countUniqueValues++ - uniqueValues[row.Cells[colIndex].Value.Created.Content] = true - } - } - } - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues), NumberFormatNone)} - case CalcOperatorCountEmpty: - countEmpty := 0 - for _, row := range table.Rows { - if nil == row.Cells[colIndex] || nil == row.Cells[colIndex].Value || nil == row.Cells[colIndex].Value.Created { - countEmpty++ - } - } - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countEmpty), NumberFormatNone)} - case CalcOperatorCountNotEmpty: - countNotEmpty := 0 - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Created { - countNotEmpty++ - } - } - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty), NumberFormatNone)} - case CalcOperatorPercentEmpty: - countEmpty := 0 - for _, row := range table.Rows { - if nil == row.Cells[colIndex] || nil == row.Cells[colIndex].Value || nil == row.Cells[colIndex].Value.Created { - countEmpty++ - } - } - if 0 < len(table.Rows) { - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countEmpty)/float64(len(table.Rows)), NumberFormatPercent)} - } - case CalcOperatorPercentNotEmpty: - countNotEmpty := 0 - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Created { - countNotEmpty++ - } - } - if 0 < len(table.Rows) { - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty)/float64(len(table.Rows)), NumberFormatPercent)} - } - case CalcOperatorPercentUniqueValues: - countUniqueValues := 0 - uniqueValues := map[int64]bool{} - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Created { - if _, ok := uniqueValues[row.Cells[colIndex].Value.Created.Content]; !ok { - countUniqueValues++ - uniqueValues[row.Cells[colIndex].Value.Created.Content] = true - } - } - } - if 0 < len(table.Rows) { - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues)/float64(len(table.Rows)), NumberFormatPercent)} - } - case CalcOperatorEarliest: - earliest := int64(0) - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Created { - if 0 == earliest || earliest > row.Cells[colIndex].Value.Created.Content { - earliest = row.Cells[colIndex].Value.Created.Content - } - } - } - if 0 != earliest { - col.Calc.Result = &Value{Created: NewFormattedValueCreated(earliest, 0, CreatedFormatNone)} - } - case CalcOperatorLatest: - latest := int64(0) - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Created { - if 0 == latest || latest < row.Cells[colIndex].Value.Created.Content { - latest = row.Cells[colIndex].Value.Created.Content - } - } - } - if 0 != latest { - col.Calc.Result = &Value{Created: NewFormattedValueCreated(latest, 0, CreatedFormatNone)} - } - case CalcOperatorRange: - earliest := int64(0) - latest := int64(0) - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Created { - if 0 == earliest || earliest > row.Cells[colIndex].Value.Created.Content { - earliest = row.Cells[colIndex].Value.Created.Content - } - if 0 == latest || latest < row.Cells[colIndex].Value.Created.Content { - latest = row.Cells[colIndex].Value.Created.Content - } - } - } - if 0 != earliest && 0 != latest { - col.Calc.Result = &Value{Created: NewFormattedValueCreated(earliest, latest, CreatedFormatDuration)} - } - } -} - -func (table *Table) calcColUpdated(col *TableColumn, colIndex int) { - switch col.Calc.Operator { - case CalcOperatorCountAll: - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(len(table.Rows)), NumberFormatNone)} - case CalcOperatorCountValues: - countValues := 0 - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Updated && row.Cells[colIndex].Value.Updated.IsNotEmpty { - countValues++ - } - } - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countValues), NumberFormatNone)} - case CalcOperatorCountUniqueValues: - countUniqueValues := 0 - uniqueValues := map[int64]bool{} - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Updated && row.Cells[colIndex].Value.Updated.IsNotEmpty { - if _, ok := uniqueValues[row.Cells[colIndex].Value.Updated.Content]; !ok { - countUniqueValues++ - uniqueValues[row.Cells[colIndex].Value.Updated.Content] = true - } - } - } - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues), NumberFormatNone)} - case CalcOperatorCountEmpty: - countEmpty := 0 - for _, row := range table.Rows { - if nil == row.Cells[colIndex] || nil == row.Cells[colIndex].Value || nil == row.Cells[colIndex].Value.Updated || !row.Cells[colIndex].Value.Updated.IsNotEmpty { - countEmpty++ - } - } - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countEmpty), NumberFormatNone)} - case CalcOperatorCountNotEmpty: - countNotEmpty := 0 - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Updated && row.Cells[colIndex].Value.Updated.IsNotEmpty { - countNotEmpty++ - } - } - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty), NumberFormatNone)} - case CalcOperatorPercentEmpty: - countEmpty := 0 - for _, row := range table.Rows { - if nil == row.Cells[colIndex] || nil == row.Cells[colIndex].Value || nil == row.Cells[colIndex].Value.Updated || !row.Cells[colIndex].Value.Updated.IsNotEmpty { - countEmpty++ - } - } - if 0 < len(table.Rows) { - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countEmpty)/float64(len(table.Rows)), NumberFormatPercent)} - } - case CalcOperatorPercentNotEmpty: - countNotEmpty := 0 - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Updated && row.Cells[colIndex].Value.Updated.IsNotEmpty { - countNotEmpty++ - } - } - if 0 < len(table.Rows) { - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty)/float64(len(table.Rows)), NumberFormatPercent)} - } - case CalcOperatorPercentUniqueValues: - countUniqueValues := 0 - uniqueValues := map[int64]bool{} - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Updated && row.Cells[colIndex].Value.Updated.IsNotEmpty { - if _, ok := uniqueValues[row.Cells[colIndex].Value.Updated.Content]; !ok { - countUniqueValues++ - uniqueValues[row.Cells[colIndex].Value.Updated.Content] = true - } - } - } - if 0 < len(table.Rows) { - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues)/float64(len(table.Rows)), NumberFormatPercent)} - } - case CalcOperatorEarliest: - earliest := int64(0) - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Updated && row.Cells[colIndex].Value.Updated.IsNotEmpty { - if 0 == earliest || earliest > row.Cells[colIndex].Value.Updated.Content { - earliest = row.Cells[colIndex].Value.Updated.Content - } - } - } - if 0 != earliest { - col.Calc.Result = &Value{Updated: NewFormattedValueUpdated(earliest, 0, UpdatedFormatNone)} - } - case CalcOperatorLatest: - latest := int64(0) - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Updated && row.Cells[colIndex].Value.Updated.IsNotEmpty { - if 0 == latest || latest < row.Cells[colIndex].Value.Updated.Content { - latest = row.Cells[colIndex].Value.Updated.Content - } - } - } - if 0 != latest { - col.Calc.Result = &Value{Updated: NewFormattedValueUpdated(latest, 0, UpdatedFormatNone)} - } - case CalcOperatorRange: - earliest := int64(0) - latest := int64(0) - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Updated && row.Cells[colIndex].Value.Updated.IsNotEmpty { - if 0 == earliest || earliest > row.Cells[colIndex].Value.Updated.Content { - earliest = row.Cells[colIndex].Value.Updated.Content - } - if 0 == latest || latest < row.Cells[colIndex].Value.Updated.Content { - latest = row.Cells[colIndex].Value.Updated.Content - } - } - } - if 0 != earliest && 0 != latest { - col.Calc.Result = &Value{Updated: NewFormattedValueUpdated(earliest, latest, UpdatedFormatDuration)} - } - } -} - -func (table *Table) calcColCheckbox(col *TableColumn, colIndex int) { - switch col.Calc.Operator { - case CalcOperatorCountAll: - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(len(table.Rows)), NumberFormatNone)} - case CalcOperatorChecked: - countChecked := 0 - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Checkbox && row.Cells[colIndex].Value.Checkbox.Checked { - countChecked++ - } - } - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countChecked), NumberFormatNone)} - case CalcOperatorUnchecked: - countUnchecked := 0 - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Checkbox && !row.Cells[colIndex].Value.Checkbox.Checked { - countUnchecked++ - } - } - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUnchecked), NumberFormatNone)} - case CalcOperatorPercentChecked: - countChecked := 0 - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Checkbox && row.Cells[colIndex].Value.Checkbox.Checked { - countChecked++ - } - } - if 0 < len(table.Rows) { - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countChecked)/float64(len(table.Rows)), NumberFormatPercent)} - } - case CalcOperatorPercentUnchecked: - countUnchecked := 0 - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Checkbox && !row.Cells[colIndex].Value.Checkbox.Checked { - countUnchecked++ - } - } - if 0 < len(table.Rows) { - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUnchecked)/float64(len(table.Rows)), NumberFormatPercent)} - } - } -} - -func (table *Table) calcColRelation(col *TableColumn, colIndex int) { - switch col.Calc.Operator { - case CalcOperatorCountAll: - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(len(table.Rows)), NumberFormatNone)} - case CalcOperatorCountValues: - countValues := 0 - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Relation { - countValues++ - } - } - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countValues), NumberFormatNone)} - case CalcOperatorCountUniqueValues: - countUniqueValues := 0 - uniqueValues := map[string]bool{} - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Relation { - for _, id := range row.Cells[colIndex].Value.Relation.BlockIDs { - if !uniqueValues[id] { - uniqueValues[id] = true - countUniqueValues++ - } - } - } - } - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues), NumberFormatNone)} - case CalcOperatorCountEmpty: - countEmpty := 0 - for _, row := range table.Rows { - if nil == row.Cells[colIndex] || nil == row.Cells[colIndex].Value || nil == row.Cells[colIndex].Value.Relation || 0 == len(row.Cells[colIndex].Value.Relation.BlockIDs) { - countEmpty++ - } - } - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countEmpty), NumberFormatNone)} - case CalcOperatorCountNotEmpty: - countNotEmpty := 0 - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Relation && 0 < len(row.Cells[colIndex].Value.Relation.BlockIDs) { - countNotEmpty++ - } - } - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty), NumberFormatNone)} - case CalcOperatorPercentEmpty: - countEmpty := 0 - for _, row := range table.Rows { - if nil == row.Cells[colIndex] || nil == row.Cells[colIndex].Value || nil == row.Cells[colIndex].Value.Relation || 0 == len(row.Cells[colIndex].Value.Relation.BlockIDs) { - countEmpty++ - } - } - if 0 < len(table.Rows) { - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countEmpty)/float64(len(table.Rows)), NumberFormatPercent)} - } - case CalcOperatorPercentNotEmpty: - countNotEmpty := 0 - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Relation && 0 < len(row.Cells[colIndex].Value.Relation.BlockIDs) { - countNotEmpty++ - } - } - if 0 < len(table.Rows) { - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty)/float64(len(table.Rows)), NumberFormatPercent)} - } - case CalcOperatorPercentUniqueValues: - countUniqueValues := 0 - uniqueValues := map[string]bool{} - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Relation { - for _, id := range row.Cells[colIndex].Value.Relation.BlockIDs { - if !uniqueValues[id] { - uniqueValues[id] = true - countUniqueValues++ - } - } - } - } - if 0 < len(table.Rows) { - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues)/float64(len(table.Rows)), NumberFormatPercent)} - } - } -} - -func (table *Table) calcColRollup(col *TableColumn, colIndex int) { - switch col.Calc.Operator { - case CalcOperatorCountAll: - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(len(table.Rows)), NumberFormatNone)} - case CalcOperatorCountValues: - countValues := 0 - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Rollup { - countValues++ - } - } - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countValues), NumberFormatNone)} - case CalcOperatorCountUniqueValues: - countUniqueValues := 0 - uniqueValues := map[string]bool{} - 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.String(true)] { - uniqueValues[content.String(true)] = true - countUniqueValues++ - } - } - } - } - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues), NumberFormatNone)} - case CalcOperatorCountEmpty: - countEmpty := 0 - for _, row := range table.Rows { - if nil == row.Cells[colIndex] || nil == row.Cells[colIndex].Value || nil == row.Cells[colIndex].Value.Rollup || 0 == len(row.Cells[colIndex].Value.Rollup.Contents) { - countEmpty++ - } - } - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countEmpty), NumberFormatNone)} - case CalcOperatorCountNotEmpty: - countNotEmpty := 0 - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Rollup && 0 < len(row.Cells[colIndex].Value.Rollup.Contents) { - countNotEmpty++ - } - } - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty), NumberFormatNone)} - case CalcOperatorPercentEmpty: - countEmpty := 0 - for _, row := range table.Rows { - if nil == row.Cells[colIndex] || nil == row.Cells[colIndex].Value || nil == row.Cells[colIndex].Value.Rollup || 0 == len(row.Cells[colIndex].Value.Rollup.Contents) { - countEmpty++ - } - } - if 0 < len(table.Rows) { - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countEmpty)/float64(len(table.Rows)), NumberFormatPercent)} - } - case CalcOperatorPercentNotEmpty: - countNotEmpty := 0 - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Rollup && 0 < len(row.Cells[colIndex].Value.Rollup.Contents) { - countNotEmpty++ - } - } - if 0 < len(table.Rows) { - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countNotEmpty)/float64(len(table.Rows)), NumberFormatPercent)} - } - case CalcOperatorPercentUniqueValues: - countUniqueValues := 0 - uniqueValues := map[string]bool{} - 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.String(true)] { - uniqueValues[content.String(true)] = true - countUniqueValues++ - } - } - } - } - if 0 < len(table.Rows) { - col.Calc.Result = &Value{Number: NewFormattedValueNumber(float64(countUniqueValues)/float64(len(table.Rows)), NumberFormatPercent)} - } - case CalcOperatorSum: - sum := 0.0 - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Rollup && 0 < len(row.Cells[colIndex].Value.Rollup.Contents) { - for _, content := range row.Cells[colIndex].Value.Rollup.Contents { - val, _ := util.Convert2Float(content.String(false)) - sum += val - } - } - } - col.Calc.Result = &Value{Number: NewFormattedValueNumber(sum, col.NumberFormat)} - case CalcOperatorAverage: - sum := 0.0 - count := 0 - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Rollup && 0 < len(row.Cells[colIndex].Value.Rollup.Contents) { - for _, content := range row.Cells[colIndex].Value.Rollup.Contents { - val, _ := util.Convert2Float(content.String(false)) - sum += val - count++ - } - } - } - if 0 != count { - col.Calc.Result = &Value{Number: NewFormattedValueNumber(sum/float64(count), col.NumberFormat)} - } - case CalcOperatorMedian: - values := []float64{} - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Rollup && 0 < len(row.Cells[colIndex].Value.Rollup.Contents) { - for _, content := range row.Cells[colIndex].Value.Rollup.Contents { - val, _ := util.Convert2Float(content.String(false)) - values = append(values, val) - } - } - } - sort.Float64s(values) - if 0 < len(values) { - if 0 == len(values)%2 { - col.Calc.Result = &Value{Number: NewFormattedValueNumber((values[len(values)/2-1]+values[len(values)/2])/2, col.NumberFormat)} - } else { - col.Calc.Result = &Value{Number: NewFormattedValueNumber(values[len(values)/2], col.NumberFormat)} - } - } - case CalcOperatorMin: - minVal := math.MaxFloat64 - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Rollup && 0 < len(row.Cells[colIndex].Value.Rollup.Contents) { - for _, content := range row.Cells[colIndex].Value.Rollup.Contents { - val, _ := util.Convert2Float(content.String(false)) - if val < minVal { - minVal = val - } - } - } - } - if math.MaxFloat64 != minVal { - col.Calc.Result = &Value{Number: NewFormattedValueNumber(minVal, col.NumberFormat)} - } - case CalcOperatorMax: - maxVal := -math.MaxFloat64 - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Rollup && 0 < len(row.Cells[colIndex].Value.Rollup.Contents) { - for _, content := range row.Cells[colIndex].Value.Rollup.Contents { - val, _ := util.Convert2Float(content.String(false)) - if val > maxVal { - maxVal = val - } - } - } - } - if -math.MaxFloat64 != maxVal { - col.Calc.Result = &Value{Number: NewFormattedValueNumber(maxVal, col.NumberFormat)} - } - case CalcOperatorRange: - minVal := math.MaxFloat64 - maxVal := -math.MaxFloat64 - for _, row := range table.Rows { - if nil != row.Cells[colIndex] && nil != row.Cells[colIndex].Value && nil != row.Cells[colIndex].Value.Rollup && 0 < len(row.Cells[colIndex].Value.Rollup.Contents) { - for _, content := range row.Cells[colIndex].Value.Rollup.Contents { - val, _ := util.Convert2Float(content.String(false)) - if val < minVal { - minVal = val - } - if val > maxVal { - maxVal = val - } - } - } - } - if math.MaxFloat64 != minVal && -math.MaxFloat64 != maxVal { - col.Calc.Result = &Value{Number: NewFormattedValueNumber(maxVal-minVal, col.NumberFormat)} - } - } -} diff --git a/kernel/av/sort.go b/kernel/av/sort.go index 5cef4f83f..56ca2eaa2 100644 --- a/kernel/av/sort.go +++ b/kernel/av/sort.go @@ -18,6 +18,7 @@ package av import ( "bytes" + "sort" "strings" "time" @@ -37,6 +38,122 @@ const ( SortOrderDesc SortOrder = "DESC" ) +func Sort(viewable Viewable, attrView *AttributeView) { + collection := viewable.(Collection) + sorts := collection.GetSorts() + if 1 > len(sorts) { + return + } + + type FieldIndexSort struct { + Index int + Order SortOrder + } + + var fieldIndexSorts []*FieldIndexSort + for _, s := range sorts { + for i, c := range collection.GetFields() { + if c.GetID() == s.Column { + fieldIndexSorts = append(fieldIndexSorts, &FieldIndexSort{Index: i, Order: s.Order}) + break + } + } + } + + items := collection.GetItems() + editedValItems := map[string]bool{} + for i, item := range items { + for _, fieldIndexSort := range fieldIndexSorts { + val := items[i].GetValues()[fieldIndexSort.Index] + if KeyTypeCheckbox == val.Type { + if block := item.GetBlockValue(); nil != block && block.IsEdited() { + // 如果主键编辑过,则勾选框也算作编辑过,参与排序 https://github.com/siyuan-note/siyuan/issues/11016 + editedValItems[item.GetID()] = true + break + } + } + + if val.IsEdited() { + // 如果该卡片某字段的值已经编辑过,则该卡片可参与排序 + editedValItems[item.GetID()] = true + break + } + } + } + + // 将未编辑的卡片和已编辑的卡片分开排序 + var uneditedItems, editedItems []Item + for _, item := range items { + if _, ok := editedValItems[item.GetID()]; ok { + editedItems = append(editedItems, item) + } else { + uneditedItems = append(uneditedItems, item) + } + } + + sort.Slice(uneditedItems, func(i, j int) bool { + val1 := uneditedItems[i].GetBlockValue() + if nil == val1 { + return true + } + val2 := uneditedItems[j].GetBlockValue() + if nil == val2 { + return false + } + return val1.CreatedAt < val2.CreatedAt + }) + + sort.Slice(editedItems, func(i, j int) bool { + sorted := true + for _, fieldIndexSort := range fieldIndexSorts { + val1 := editedItems[i].GetValues()[fieldIndexSort.Index] + val2 := editedItems[j].GetValues()[fieldIndexSort.Index] + if nil == val1 || val1.IsEmpty() { + if nil != val2 && !val2.IsEmpty() { + return false + } + sorted = false + continue + } else { + if nil == val2 || val2.IsEmpty() { + return true + } + } + + result := val1.Compare(val2, attrView) + if 0 == result { + sorted = false + continue + } + sorted = true + + if fieldIndexSort.Order == SortOrderAsc { + return 0 > result + } + return 0 < result + } + + if !sorted { + key1 := editedItems[i].GetBlockValue() + if nil == key1 { + return false + } + key2 := editedItems[j].GetBlockValue() + if nil == key2 { + return false + } + return key1.CreatedAt < key2.CreatedAt + } + return false + }) + + // 将包含未编辑的卡片放在最后 + collection.SetItems(append(editedItems, uneditedItems...)) + if 1 > len(collection.GetItems()) { + collection.SetItems([]Item{}) + } +} + func (value *Value) Compare(other *Value, attrView *AttributeView) int { switch value.Type { case KeyTypeBlock: diff --git a/kernel/model/attribute_view.go b/kernel/model/attribute_view.go index 711b87080..9f65285d7 100644 --- a/kernel/model/attribute_view.go +++ b/kernel/model/attribute_view.go @@ -1316,9 +1316,74 @@ func renderViewableInstance(viewable av.Viewable, view *av.View, attrView *av.At return } - viewable.Filter(attrView) - viewable.Sort(attrView) - viewable.Calc() + av.Filter(viewable, attrView) + av.Sort(viewable, attrView) + av.Calc(viewable) + + if groupCalc := viewable.GetGroupCalc(); nil != groupCalc { + if groupCalcKey, _ := attrView.GetKey(groupCalc.Field); nil != groupCalcKey { + collection := viewable.(av.Collection) + var calcResult *av.GroupCalc + field := collection.GetField(groupCalcKey.ID) + if nil != field { + if calc := field.GetCalc(); nil != calc && field.GetID() == groupCalcKey.ID { + // 直接使用字段计算结果 + calcResult = &av.GroupCalc{Field: groupCalcKey.ID, FieldCalc: calc} + } + + if nil == calcResult { + for i, f := range collection.GetFields() { + if f.GetID() != groupCalcKey.ID { + continue + } + + field.SetCalc(groupCalc.FieldCalc) + + switch field.GetType() { + case av.KeyTypeBlock: + av.CalcFieldBlock(collection, field, i) + case av.KeyTypeText: + av.CalcFieldText(collection, field, i) + case av.KeyTypeNumber: + av.CalcFieldNumber(collection, field, i) + case av.KeyTypeDate: + av.CalcFieldDate(collection, field, i) + case av.KeyTypeSelect: + av.CalcFieldSelect(collection, field, i) + case av.KeyTypeMSelect: + av.CalcFieldMSelect(collection, field, i) + case av.KeyTypeURL: + av.CalcFieldURL(collection, field, i) + case av.KeyTypeEmail: + av.CalcFieldEmail(collection, field, i) + case av.KeyTypePhone: + av.CalcFieldPhone(collection, field, i) + case av.KeyTypeMAsset: + av.CalcFieldMAsset(collection, field, i) + case av.KeyTypeTemplate: + av.CalcFieldTemplate(collection, field, i) + case av.KeyTypeCreated: + av.CalcFieldCreated(collection, field, i) + case av.KeyTypeUpdated: + av.CalcFieldUpdated(collection, field, i) + case av.KeyTypeCheckbox: + av.CalcFieldCheckbox(collection, field, i) + case av.KeyTypeRelation: + av.CalcFieldRelation(collection, field, i) + case av.KeyTypeRollup: + av.CalcFieldRollup(collection, field, i) + } + break + } + + calcResult = &av.GroupCalc{Field: groupCalcKey.ID, FieldCalc: field.GetCalc()} + field.SetCalc(nil) + } + } + + viewable.SetGroupCalc(calcResult) + } + } // 分页 switch viewable.GetType() { @@ -1368,8 +1433,8 @@ func GetCurrentAttributeViewImages(avID, viewID, query string) (ret []string, er } table := getAttrViewTable(attrView, view, query) - table.Filter(attrView) - table.Sort(attrView) + av.Filter(table, attrView) + av.Sort(table, attrView) for _, row := range table.Rows { for _, cell := range row.Cells { @@ -2480,8 +2545,8 @@ func addAttributeViewBlock(now int64, avID, blockID, previousBlockID, addingBloc if nil != view && 0 < len(view.Filters) && !ignoreFillFilter { viewable := sql.RenderView(view, attrView, "") - viewable.Filter(attrView) - viewable.Sort(attrView) + av.Filter(viewable, attrView) + av.Sort(viewable, attrView) collection := viewable.(av.Collection) items := collection.GetItems() diff --git a/kernel/model/export.go b/kernel/model/export.go index 4ba5065c9..ccf92e8ba 100644 --- a/kernel/model/export.go +++ b/kernel/model/export.go @@ -83,8 +83,8 @@ func ExportAv2CSV(avID, blockID string) (zipPath string, err error) { table := getAttrViewTable(attrView, view, "") // 遵循视图过滤和排序规则 Use filtering and sorting of current view settings when exporting database blocks https://github.com/siyuan-note/siyuan/issues/10474 - table.Filter(attrView) - table.Sort(attrView) + av.Filter(table, attrView) + av.Sort(table, attrView) exportFolder := filepath.Join(util.TempDir, "export", "csv", name) if err = os.MkdirAll(exportFolder, 0755); err != nil { @@ -2499,8 +2499,8 @@ func exportTree(tree *parse.Tree, wysiwyg, keepFold, avHiddenCol bool, table := getAttrViewTable(attrView, view, "") // 遵循视图过滤和排序规则 Use filtering and sorting of current view settings when exporting database blocks https://github.com/siyuan-note/siyuan/issues/10474 - table.Filter(attrView) - table.Sort(attrView) + av.Filter(table, attrView) + av.Sort(table, attrView) var aligns []int for range table.Columns { diff --git a/kernel/sql/av_gallery.go b/kernel/sql/av_gallery.go index 895c07728..ac5886907 100644 --- a/kernel/sql/av_gallery.go +++ b/kernel/sql/av_gallery.go @@ -20,18 +20,7 @@ import ( func RenderAttributeViewGallery(attrView *av.AttributeView, view *av.View, query string) (ret *av.Gallery) { ret = &av.Gallery{ - BaseInstance: &av.BaseInstance{ - ID: view.ID, - Icon: view.Icon, - Name: view.Name, - Desc: view.Desc, - HideAttrViewName: view.HideAttrViewName, - Filters: view.Filters, - Sorts: view.Sorts, - Group: view.Group, - ShowIcon: view.Gallery.ShowIcon, - WrapField: view.Gallery.WrapField, - }, + BaseInstance: av.NewViewBaseInstance(view), CoverFrom: view.Gallery.CoverFrom, CoverFromAssetKeyID: view.Gallery.CoverFromAssetKeyID, CardAspectRatio: view.Gallery.CardAspectRatio, @@ -59,6 +48,7 @@ func RenderAttributeViewGallery(attrView *av.AttributeView, view *av.View, query Wrap: field.Wrap, Hidden: field.Hidden, Desc: key.Desc, + Calc: field.Calc, Options: key.Options, NumberFormat: key.NumberFormat, Template: key.Template, diff --git a/kernel/sql/av_table.go b/kernel/sql/av_table.go index c6a77456a..cda7cfc7a 100644 --- a/kernel/sql/av_table.go +++ b/kernel/sql/av_table.go @@ -24,20 +24,9 @@ import ( func RenderAttributeViewTable(attrView *av.AttributeView, view *av.View, query string) (ret *av.Table) { ret = &av.Table{ - BaseInstance: &av.BaseInstance{ - ID: view.ID, - Icon: view.Icon, - Name: view.Name, - Desc: view.Desc, - HideAttrViewName: view.HideAttrViewName, - Filters: view.Filters, - Sorts: view.Sorts, - Group: view.Group, - ShowIcon: view.Table.ShowIcon, - WrapField: view.Table.WrapField, - }, - Columns: []*av.TableColumn{}, - Rows: []*av.TableRow{}, + BaseInstance: av.NewViewBaseInstance(view), + Columns: []*av.TableColumn{}, + Rows: []*av.TableRow{}, } // 组装列 @@ -58,6 +47,7 @@ func RenderAttributeViewTable(attrView *av.AttributeView, view *av.View, query s Wrap: col.Wrap, Hidden: col.Hidden, Desc: key.Desc, + Calc: col.Calc, Options: key.Options, NumberFormat: key.NumberFormat, Template: key.Template, @@ -67,7 +57,6 @@ func RenderAttributeViewTable(attrView *av.AttributeView, view *av.View, query s }, Width: col.Width, Pin: col.Pin, - Calc: col.Calc, }) }