mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: generic calculations (#4794)
* feat: add generic calculations * chore: remove row count at bottom of grid * fix: code review
This commit is contained in:
parent
3b0d82287d
commit
66aea29ab7
@ -16,7 +16,7 @@ void main() {
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
await tester.tapCreateRowButtonInGrid();
|
||||
|
||||
// The initial number of rows is 3
|
||||
// 3 initial rows + 1 created
|
||||
await tester.assertNumberOfRowsInGridPage(4);
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
@ -31,9 +31,8 @@ void main() {
|
||||
|
||||
await tester.tapCreateRowButtonInRowMenuOfGrid();
|
||||
|
||||
// The initial number of rows is 3
|
||||
// 3 initial rows + 1 created
|
||||
await tester.assertNumberOfRowsInGridPage(4);
|
||||
await tester.assertRowCountInGridPage(4);
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
@ -48,9 +47,8 @@ void main() {
|
||||
await tester.tapRowMenuButtonInGrid();
|
||||
await tester.tapDeleteOnRowMenu();
|
||||
|
||||
// The initial number of rows is 3
|
||||
// 3 initial rows - 1 deleted
|
||||
await tester.assertNumberOfRowsInGridPage(2);
|
||||
await tester.assertRowCountInGridPage(2);
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
@ -60,7 +58,6 @@ void main() {
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
await tester.assertRowCountInGridPage(3);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
@ -1,26 +1,12 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:appflowy/plugins/database/application/calculations/calculation_type_ext.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/calculations/calculate_cell.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/calculations/calculation_type_item.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/common/type_option_separator.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/header/type_option/number.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/cell/editable_cell_skeleton/checkbox.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/cell/editable_cell_skeleton/checklist.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/cell/editable_cell_skeleton/date.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/cell/editable_cell_skeleton/number.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/cell/editable_cell_skeleton/select_option.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/cell/editable_cell_skeleton/text.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/cell/editable_cell_skeleton/timestamp.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/cell/editable_cell_skeleton/url.dart';
|
||||
import 'package:appflowy/util/field_type_extension.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/database/application/calculations/calculation_type_ext.dart';
|
||||
import 'package:appflowy/plugins/database/board/presentation/board_page.dart';
|
||||
import 'package:appflowy/plugins/database/board/presentation/widgets/board_column_header.dart';
|
||||
import 'package:appflowy/plugins/database/calendar/application/calendar_bloc.dart';
|
||||
@ -30,6 +16,9 @@ import 'package:appflowy/plugins/database/calendar/presentation/calendar_event_e
|
||||
import 'package:appflowy/plugins/database/calendar/presentation/calendar_page.dart';
|
||||
import 'package:appflowy/plugins/database/calendar/presentation/toolbar/calendar_layout_setting.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/grid_page.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/calculations/calculate_cell.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/calculations/calculation_type_item.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/common/type_option_separator.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/filter/choicechip/checkbox.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/filter/choicechip/checklist/checklist.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/filter/choicechip/select_option/option_list.dart';
|
||||
@ -43,6 +32,7 @@ import 'package:appflowy/plugins/database/grid/presentation/widgets/header/deskt
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/header/field_editor.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/header/field_type_list.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/header/type_option/date/date_time_format.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/header/type_option/number.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/row/row.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/sort/create_sort_list.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/sort/order_panel.dart';
|
||||
@ -52,14 +42,22 @@ import 'package:appflowy/plugins/database/grid/presentation/widgets/toolbar/filt
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/toolbar/sort_button.dart';
|
||||
import 'package:appflowy/plugins/database/tab_bar/desktop/tab_bar_add_button.dart';
|
||||
import 'package:appflowy/plugins/database/tab_bar/desktop/tab_bar_header.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/database_layout_ext.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/row/accessory/cell_accessory.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/cell/editable_cell_skeleton/checkbox.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/cell/editable_cell_skeleton/checklist.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/cell/editable_cell_skeleton/date.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/cell/editable_cell_skeleton/number.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/cell/editable_cell_skeleton/select_option.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/cell/editable_cell_skeleton/text.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/cell/editable_cell_skeleton/timestamp.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/cell/editable_cell_skeleton/url.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/cell_editor/checklist_cell_editor.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/cell_editor/checklist_progress_bar.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/cell_editor/date_editor.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/cell_editor/extension.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/cell_editor/select_option_editor.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/cell_editor/select_option_text_field.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/database_layout_ext.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/row/accessory/cell_accessory.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/row/row_action.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/row/row_banner.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/row/row_detail.dart';
|
||||
@ -70,6 +68,7 @@ import 'package:appflowy/plugins/database/widgets/setting/database_setting_actio
|
||||
import 'package:appflowy/plugins/database/widgets/setting/database_settings_list.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/setting/setting_button.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/setting/setting_property_list.dart';
|
||||
import 'package:appflowy/util/field_type_extension.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/date_picker/widgets/clear_date_button.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/date_picker/widgets/date_type_option_button.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/date_picker/widgets/end_time_button.dart';
|
||||
@ -77,6 +76,7 @@ import 'package:appflowy/workspace/presentation/widgets/date_picker/widgets/remi
|
||||
import 'package:appflowy/workspace/presentation/widgets/dialogs.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/pop_up_action.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/toggle/toggle.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:appflowy_board/appflowy_board.dart';
|
||||
import 'package:calendar_view/calendar_view.dart';
|
||||
@ -86,10 +86,9 @@ import 'package:flowy_infra_ui/style_widget/text_input.dart';
|
||||
import 'package:flowy_infra_ui/widget/buttons/primary_button.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:table_calendar/table_calendar.dart';
|
||||
|
||||
// Non-exported member of the table_calendar library
|
||||
import 'package:table_calendar/src/widgets/cell_content.dart';
|
||||
import 'package:table_calendar/table_calendar.dart';
|
||||
|
||||
import 'base.dart';
|
||||
import 'common_operations.dart';
|
||||
@ -974,11 +973,6 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
await tapButtonWithName(LocaleKeys.grid_row_delete.tr());
|
||||
}
|
||||
|
||||
Future<void> assertRowCountInGridPage(int num) async {
|
||||
final text = find.text('${rowCountString()} $num', findRichText: true);
|
||||
expect(text, findsOneWidget);
|
||||
}
|
||||
|
||||
Future<void> createField(FieldType fieldType, String name) async {
|
||||
await scrollToRight(find.byType(GridPage));
|
||||
await tapNewPropertyButton();
|
||||
|
@ -11,8 +11,22 @@ extension CalcTypeLabel on CalculationType {
|
||||
LocaleKeys.grid_calculationTypeLabel_median.tr(),
|
||||
CalculationType.Min => LocaleKeys.grid_calculationTypeLabel_min.tr(),
|
||||
CalculationType.Sum => LocaleKeys.grid_calculationTypeLabel_sum.tr(),
|
||||
CalculationType.Count =>
|
||||
LocaleKeys.grid_calculationTypeLabel_count.tr(),
|
||||
CalculationType.CountEmpty =>
|
||||
LocaleKeys.grid_calculationTypeLabel_countEmpty.tr(),
|
||||
CalculationType.CountNonEmpty =>
|
||||
LocaleKeys.grid_calculationTypeLabel_countNonEmpty.tr(),
|
||||
_ => throw UnimplementedError(
|
||||
'Label for $this has not been implemented',
|
||||
),
|
||||
};
|
||||
|
||||
String get shortLabel => switch (this) {
|
||||
CalculationType.CountEmpty =>
|
||||
LocaleKeys.grid_calculationTypeLabel_countEmptyShort.tr(),
|
||||
CalculationType.CountNonEmpty =>
|
||||
LocaleKeys.grid_calculationTypeLabel_countNonEmptyShort.tr(),
|
||||
_ => label,
|
||||
};
|
||||
}
|
||||
|
@ -128,7 +128,7 @@ extension NumberFormatExtension on NumberFormatPB {
|
||||
}
|
||||
}
|
||||
|
||||
String iconSymbol() {
|
||||
String iconSymbol([bool defaultPrefixInc = true]) {
|
||||
switch (this) {
|
||||
case NumberFormatPB.ArgentinePeso:
|
||||
return "\$";
|
||||
@ -169,7 +169,7 @@ extension NumberFormatExtension on NumberFormatPB {
|
||||
case NumberFormatPB.NorwegianKrone:
|
||||
return "kr";
|
||||
case NumberFormatPB.Num:
|
||||
return "#";
|
||||
return defaultPrefixInc ? "#" : "";
|
||||
case NumberFormatPB.Percent:
|
||||
return "%";
|
||||
case NumberFormatPB.PhilippinePeso:
|
||||
|
@ -0,0 +1,34 @@
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||
|
||||
extension AvailableCalculations on FieldType {
|
||||
List<CalculationType> calculationsForFieldType() {
|
||||
final calculationTypes = [
|
||||
CalculationType.Count,
|
||||
];
|
||||
|
||||
// These FieldTypes cannot be empty, no need to count empty/non-empty
|
||||
if (![FieldType.Checkbox, FieldType.LastEditedTime, FieldType.CreatedTime]
|
||||
.contains(this)) {
|
||||
calculationTypes.addAll([
|
||||
CalculationType.CountEmpty,
|
||||
CalculationType.CountNonEmpty,
|
||||
]);
|
||||
}
|
||||
|
||||
switch (this) {
|
||||
case FieldType.Number:
|
||||
calculationTypes.addAll([
|
||||
CalculationType.Sum,
|
||||
CalculationType.Average,
|
||||
CalculationType.Min,
|
||||
CalculationType.Max,
|
||||
CalculationType.Median,
|
||||
]);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return calculationTypes;
|
||||
}
|
||||
}
|
@ -12,7 +12,6 @@ import 'package:appflowy_backend/log.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra/theme_extension.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/scrolling/styled_scrollview.dart';
|
||||
import 'package:flowy_infra_ui/widget/error_page.dart';
|
||||
@ -237,7 +236,6 @@ class _GridPageContentState extends State<GridPageContent> {
|
||||
viewId: widget.view.id,
|
||||
scrollController: _scrollController,
|
||||
),
|
||||
const _GridFooter(),
|
||||
],
|
||||
);
|
||||
}
|
||||
@ -431,40 +429,3 @@ class _WrapScrollView extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _GridFooter extends StatelessWidget {
|
||||
const _GridFooter();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocSelector<GridBloc, GridState, int>(
|
||||
selector: (state) => state.rowCount,
|
||||
builder: (context, rowCount) {
|
||||
return Padding(
|
||||
padding: GridSize.contentInsets,
|
||||
child: RichText(
|
||||
text: TextSpan(
|
||||
text: rowCountString(),
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium!
|
||||
.copyWith(color: Theme.of(context).hintColor),
|
||||
children: [
|
||||
TextSpan(
|
||||
text: ' $rowCount',
|
||||
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||
color: AFThemeExtension.of(context).gridRowCountColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
String rowCountString() {
|
||||
return '${LocaleKeys.grid_row_count.tr()} :';
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import 'package:appflowy/plugins/database/application/calculations/calculation_t
|
||||
import 'package:appflowy/plugins/database/application/field/field_info.dart';
|
||||
import 'package:appflowy/plugins/database/application/field/type_option/number_format_bloc.dart';
|
||||
import 'package:appflowy/plugins/database/grid/application/calculations/calculations_bloc.dart';
|
||||
import 'package:appflowy/plugins/database/grid/application/calculations/field_type_calc_ext.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/calculations/calculation_selector.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/calculations/calculation_type_item.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/calculations/remove_calculation_button.dart';
|
||||
@ -67,31 +68,29 @@ class _CalculateCellState extends State<CalculateCell> {
|
||||
),
|
||||
),
|
||||
),
|
||||
...CalculationType.values.map(
|
||||
(type) => CalculationTypeItem(
|
||||
type: type,
|
||||
onTap: () {
|
||||
if (type != widget.calculation?.calculationType) {
|
||||
context.read<CalculationsBloc>().add(
|
||||
CalculationsEvent.updateCalculationType(
|
||||
widget.fieldInfo.id,
|
||||
type,
|
||||
calculationId: widget.calculation?.id,
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
...widget.fieldInfo.fieldType.calculationsForFieldType().map(
|
||||
(type) => CalculationTypeItem(
|
||||
type: type,
|
||||
onTap: () {
|
||||
if (type != widget.calculation?.calculationType) {
|
||||
context.read<CalculationsBloc>().add(
|
||||
CalculationsEvent.updateCalculationType(
|
||||
widget.fieldInfo.id,
|
||||
type,
|
||||
calculationId: widget.calculation?.id,
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
child: widget.fieldInfo.fieldType == FieldType.Number
|
||||
? widget.calculation != null
|
||||
? _showCalculateValue(context, prefix)
|
||||
: CalculationSelector(isSelected: isSelected)
|
||||
: const SizedBox.shrink(),
|
||||
child: widget.calculation != null
|
||||
? _showCalculateValue(context, prefix)
|
||||
: CalculationSelector(isSelected: isSelected),
|
||||
),
|
||||
);
|
||||
}
|
||||
@ -107,7 +106,7 @@ class _CalculateCellState extends State<CalculateCell> {
|
||||
children: [
|
||||
Flexible(
|
||||
child: FlowyText(
|
||||
widget.calculation!.calculationType.label,
|
||||
widget.calculation!.calculationType.shortLabel,
|
||||
color: Theme.of(context).hintColor,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
@ -146,7 +145,7 @@ class _CalculateCellState extends State<CalculateCell> {
|
||||
FieldType.Number =>
|
||||
NumberTypeOptionPB.fromBuffer(widget.fieldInfo.field.typeOptionData)
|
||||
.format
|
||||
.iconSymbol(),
|
||||
.iconSymbol(false),
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
import 'package:appflowy/plugins/database/grid/application/grid_bloc.dart';
|
||||
import 'package:appflowy/plugins/database/application/database_controller.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:appflowy/plugins/database/grid/application/grid_bloc.dart';
|
||||
import 'package:bloc_test/bloc_test.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import 'util.dart';
|
||||
|
||||
void main() {
|
||||
@ -17,7 +18,8 @@ void main() {
|
||||
context = await gridTest.createTestGrid();
|
||||
});
|
||||
|
||||
// The initial number of rows is 3 for each grid.
|
||||
// The initial number of rows is 3 for each grid
|
||||
// We create one row so we expect 4 rows
|
||||
blocTest<GridBloc, GridState>(
|
||||
"create a row",
|
||||
build: () => GridBloc(
|
||||
|
@ -739,7 +739,12 @@
|
||||
"max": "Max",
|
||||
"median": "Median",
|
||||
"min": "Min",
|
||||
"sum": "Sum"
|
||||
"sum": "Sum",
|
||||
"count": "Count",
|
||||
"countEmpty": "Count empty",
|
||||
"countEmptyShort": "Empty",
|
||||
"countNonEmpty": "Count non empty",
|
||||
"countNonEmptyShort": "Not empty"
|
||||
}
|
||||
},
|
||||
"document": {
|
||||
@ -1350,4 +1355,4 @@
|
||||
"userIcon": "User icon"
|
||||
},
|
||||
"noLogFiles": "There're no log files"
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ use std::{
|
||||
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
|
||||
use serde_repr::{Deserialize_repr, Serialize_repr};
|
||||
|
||||
use crate::{impl_into_calculation_type, services::calculations::Calculation};
|
||||
use crate::{entities::FieldType, impl_into_calculation_type, services::calculations::Calculation};
|
||||
|
||||
#[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
|
||||
pub struct CalculationPB {
|
||||
@ -56,10 +56,13 @@ impl std::convert::From<&Arc<Calculation>> for CalculationPB {
|
||||
pub enum CalculationType {
|
||||
#[default]
|
||||
Average = 0, // Number
|
||||
Max = 1, // Number
|
||||
Median = 2, // Number
|
||||
Min = 3, // Number
|
||||
Sum = 4, // Number
|
||||
Max = 1, // Number
|
||||
Median = 2, // Number
|
||||
Min = 3, // Number
|
||||
Sum = 4, // Number
|
||||
Count = 5, // All
|
||||
CountEmpty = 6, // All
|
||||
CountNonEmpty = 7, // All
|
||||
}
|
||||
|
||||
impl Display for CalculationType {
|
||||
@ -102,6 +105,23 @@ impl From<&CalculationType> for i64 {
|
||||
}
|
||||
}
|
||||
|
||||
impl CalculationType {
|
||||
pub fn is_allowed(&self, field_type: FieldType) -> bool {
|
||||
match self {
|
||||
// Number fields only
|
||||
CalculationType::Max
|
||||
| CalculationType::Min
|
||||
| CalculationType::Average
|
||||
| CalculationType::Median
|
||||
| CalculationType::Sum => {
|
||||
matches!(field_type, FieldType::Number)
|
||||
},
|
||||
// All fields
|
||||
CalculationType::Count | CalculationType::CountEmpty | CalculationType::CountNonEmpty => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
|
||||
pub struct RepeatedCalculationsPB {
|
||||
#[pb(index = 1)]
|
||||
|
@ -55,6 +55,9 @@ macro_rules! impl_into_calculation_type {
|
||||
2 => CalculationType::Median,
|
||||
3 => CalculationType::Min,
|
||||
4 => CalculationType::Sum,
|
||||
5 => CalculationType::Count,
|
||||
6 => CalculationType::CountEmpty,
|
||||
7 => CalculationType::CountNonEmpty,
|
||||
_ => {
|
||||
tracing::error!("🔴 Can't parse CalculationType from value: {}", ty);
|
||||
CalculationType::Average
|
||||
|
@ -23,6 +23,7 @@ pub trait CalculationsDelegate: Send + Sync + 'static {
|
||||
fn get_cells_for_field(&self, view_id: &str, field_id: &str) -> Fut<Vec<Arc<RowCell>>>;
|
||||
fn get_field(&self, field_id: &str) -> Option<Field>;
|
||||
fn get_calculation(&self, view_id: &str, field_id: &str) -> Fut<Option<Arc<Calculation>>>;
|
||||
fn get_all_calculations(&self, view_id: &str) -> Fut<Arc<Vec<Arc<Calculation>>>>;
|
||||
fn update_calculation(&self, view_id: &str, calculation: Calculation);
|
||||
fn remove_calculation(&self, view_id: &str, calculation_id: &str);
|
||||
}
|
||||
@ -76,7 +77,7 @@ impl CalculationsController {
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(name = "schedule_filter_task", level = "trace", skip(self))]
|
||||
#[tracing::instrument(name = "schedule_calculation_task", level = "trace", skip(self))]
|
||||
async fn gen_task(&self, task_type: CalculationEvent, qos: QualityOfService) {
|
||||
let task_id = self.task_scheduler.read().await.next_task_id();
|
||||
let task = Task::new(
|
||||
@ -89,10 +90,10 @@ impl CalculationsController {
|
||||
}
|
||||
|
||||
#[tracing::instrument(
|
||||
name = "process_filter_task",
|
||||
name = "process_calculation_task",
|
||||
level = "trace",
|
||||
skip_all,
|
||||
fields(filter_result),
|
||||
fields(calculation_result),
|
||||
err
|
||||
)]
|
||||
pub async fn process(&self, predicate: &str) -> FlowyResult<()> {
|
||||
@ -160,7 +161,8 @@ impl CalculationsController {
|
||||
.await;
|
||||
|
||||
if let Some(calculation) = calculation {
|
||||
if new_field_type != FieldType::Number {
|
||||
let calc_type: CalculationType = calculation.calculation_type.into();
|
||||
if !calc_type.is_allowed(new_field_type) {
|
||||
self
|
||||
.delegate
|
||||
.remove_calculation(&self.view_id, &calculation.id);
|
||||
@ -228,6 +230,19 @@ impl CalculationsController {
|
||||
let cells = row.cells.iter();
|
||||
let mut updates = vec![];
|
||||
|
||||
// In case there are calculations where empty cells are counted
|
||||
// as a contribution to the value.
|
||||
if cells.len() == 0 {
|
||||
let calculations = self.delegate.get_all_calculations(&self.view_id).await;
|
||||
for calculation in calculations.iter() {
|
||||
let update = self.get_updated_calculation(calculation.clone()).await;
|
||||
if let Some(update) = update {
|
||||
updates.push(CalculationPB::from(&update));
|
||||
self.delegate.update_calculation(&self.view_id, update);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate each cell in the row
|
||||
for cell in cells {
|
||||
let field_id = cell.0;
|
||||
@ -260,17 +275,13 @@ impl CalculationsController {
|
||||
.await;
|
||||
let field = self.delegate.get_field(&calculation.field_id)?;
|
||||
|
||||
if field_cells.is_empty() {
|
||||
return Some(calculation.with_value(String::new()));
|
||||
} else {
|
||||
let value =
|
||||
self
|
||||
.calculations_service
|
||||
.calculate(&field, calculation.calculation_type, field_cells);
|
||||
let value =
|
||||
self
|
||||
.calculations_service
|
||||
.calculate(&field, calculation.calculation_type, field_cells);
|
||||
|
||||
if value != calculation.value {
|
||||
return Some(calculation.with_value(value));
|
||||
}
|
||||
if value != calculation.value {
|
||||
return Some(calculation.with_value(value));
|
||||
}
|
||||
|
||||
None
|
||||
|
@ -26,6 +26,9 @@ impl CalculationsService {
|
||||
CalculationType::Median => self.calculate_median(field, row_cells),
|
||||
CalculationType::Min => self.calculate_min(field, row_cells),
|
||||
CalculationType::Sum => self.calculate_sum(field, row_cells),
|
||||
CalculationType::Count => self.calculate_count(row_cells),
|
||||
CalculationType::CountEmpty => self.calculate_count_empty(row_cells),
|
||||
CalculationType::CountNonEmpty => self.calculate_count_non_empty(row_cells),
|
||||
}
|
||||
}
|
||||
|
||||
@ -62,7 +65,7 @@ impl CalculationsService {
|
||||
if !values.is_empty() {
|
||||
format!("{:.5}", Self::median(&values))
|
||||
} else {
|
||||
"".to_owned()
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
|
||||
@ -89,7 +92,7 @@ impl CalculationsService {
|
||||
}
|
||||
}
|
||||
|
||||
"".to_owned()
|
||||
String::new()
|
||||
}
|
||||
|
||||
fn calculate_max(&self, field: &Field, row_cells: Vec<Arc<RowCell>>) -> String {
|
||||
@ -105,7 +108,7 @@ impl CalculationsService {
|
||||
}
|
||||
}
|
||||
|
||||
"".to_owned()
|
||||
String::new()
|
||||
}
|
||||
|
||||
fn calculate_sum(&self, field: &Field, row_cells: Vec<Arc<RowCell>>) -> String {
|
||||
@ -114,7 +117,45 @@ impl CalculationsService {
|
||||
if !values.is_empty() {
|
||||
format!("{:.5}", values.iter().sum::<f64>())
|
||||
} else {
|
||||
"".to_owned()
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
|
||||
fn calculate_count(&self, row_cells: Vec<Arc<RowCell>>) -> String {
|
||||
if !row_cells.is_empty() {
|
||||
format!("{}", row_cells.len())
|
||||
} else {
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
|
||||
fn calculate_count_empty(&self, row_cells: Vec<Arc<RowCell>>) -> String {
|
||||
if !row_cells.is_empty() {
|
||||
format!(
|
||||
"{}",
|
||||
row_cells
|
||||
.iter()
|
||||
.filter(|c| c.is_none())
|
||||
.collect::<Vec<_>>()
|
||||
.len()
|
||||
)
|
||||
} else {
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
|
||||
fn calculate_count_non_empty(&self, row_cells: Vec<Arc<RowCell>>) -> String {
|
||||
if !row_cells.is_empty() {
|
||||
format!(
|
||||
"{}",
|
||||
row_cells
|
||||
.iter()
|
||||
.filter(|c| c.is_some())
|
||||
.collect::<Vec<_>>()
|
||||
.len()
|
||||
)
|
||||
} else {
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -66,4 +66,9 @@ impl CalculationsDelegate for DatabaseViewCalculationsDelegateImpl {
|
||||
fn remove_calculation(&self, view_id: &str, calculation_id: &str) {
|
||||
self.0.remove_calculation(view_id, calculation_id)
|
||||
}
|
||||
|
||||
fn get_all_calculations(&self, view_id: &str) -> Fut<Arc<Vec<Arc<Calculation>>>> {
|
||||
let calculations = Arc::new(self.0.get_all_calculations(view_id));
|
||||
to_fut(async move { calculations })
|
||||
}
|
||||
}
|
||||
|
@ -161,14 +161,14 @@ impl DatabaseViewEditor {
|
||||
.payload(changes)
|
||||
.send();
|
||||
self
|
||||
.gen_did_create_row_view_tasks(row_detail.row.id.clone())
|
||||
.gen_did_create_row_view_tasks(row_detail.row.clone())
|
||||
.await;
|
||||
}
|
||||
|
||||
pub async fn v_did_duplicate_row(&self, row_detail: &RowDetail) {
|
||||
self
|
||||
.calculations_controller
|
||||
.did_receive_row_changed(row_detail.clone().row)
|
||||
.did_receive_row_changed(row_detail.row.clone())
|
||||
.await;
|
||||
}
|
||||
|
||||
@ -1090,7 +1090,7 @@ impl DatabaseViewEditor {
|
||||
sort_controller
|
||||
.read()
|
||||
.await
|
||||
.did_receive_row_changed(row_id)
|
||||
.did_receive_row_changed(row_id.clone())
|
||||
.await;
|
||||
}
|
||||
if let Some(calculations_controller) = weak_calculations_controller.upgrade() {
|
||||
@ -1101,11 +1101,20 @@ impl DatabaseViewEditor {
|
||||
});
|
||||
}
|
||||
|
||||
async fn gen_did_create_row_view_tasks(&self, row_id: RowId) {
|
||||
async fn gen_did_create_row_view_tasks(&self, row: Row) {
|
||||
let weak_sort_controller = Arc::downgrade(&self.sort_controller);
|
||||
let weak_calculations_controller = Arc::downgrade(&self.calculations_controller);
|
||||
af_spawn(async move {
|
||||
if let Some(sort_controller) = weak_sort_controller.upgrade() {
|
||||
sort_controller.read().await.did_create_row(row_id).await;
|
||||
sort_controller
|
||||
.read()
|
||||
.await
|
||||
.did_create_row(row.id.clone())
|
||||
.await;
|
||||
}
|
||||
|
||||
if let Some(calculations_controller) = weak_calculations_controller.upgrade() {
|
||||
calculations_controller.did_receive_row_changed(row).await;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user