Преглед изворни кода

chore: add date filter tests

appflowy пре 2 година
родитељ
комит
ec1113b134

+ 42 - 5
frontend/rust-lib/flowy-grid/src/entities/filter_entities/date_filter.rs

@@ -1,6 +1,8 @@
 use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
 use flowy_error::ErrorCode;
 use flowy_grid_data_model::revision::GridFilterRevision;
+use serde::{Deserialize, Serialize};
+use std::str::FromStr;
 use std::sync::Arc;
 
 #[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
@@ -9,7 +11,30 @@ pub struct GridDateFilter {
     pub condition: DateFilterCondition,
 
     #[pb(index = 2, one_of)]
-    pub content: Option<String>,
+    pub start: Option<i64>,
+
+    #[pb(index = 3, one_of)]
+    pub end: Option<i64>,
+}
+
+#[derive(Serialize, Deserialize, Default)]
+struct DateRange {
+    start: Option<i64>,
+    end: Option<i64>,
+}
+
+impl ToString for DateRange {
+    fn to_string(&self) -> String {
+        serde_json::to_string(self).unwrap_or("".to_string())
+    }
+}
+
+impl FromStr for DateRange {
+    type Err = serde_json::Error;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        serde_json::from_str(s)
+    }
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, ProtoBuf_Enum)]
@@ -48,9 +73,21 @@ impl std::convert::TryFrom<u8> for DateFilterCondition {
 }
 impl std::convert::From<Arc<GridFilterRevision>> for GridDateFilter {
     fn from(rev: Arc<GridFilterRevision>) -> Self {
-        GridDateFilter {
-            condition: DateFilterCondition::try_from(rev.condition).unwrap_or(DateFilterCondition::DateIs),
-            content: rev.content.clone(),
-        }
+        let condition = DateFilterCondition::try_from(rev.condition).unwrap_or(DateFilterCondition::DateIs);
+        let mut filter = GridDateFilter {
+            condition,
+            ..Default::default()
+        };
+
+        if let Some(range) = rev
+            .content
+            .as_ref()
+            .and_then(|content| DateRange::from_str(content).ok())
+        {
+            filter.start = range.start;
+            filter.end = range.end;
+        };
+
+        filter
     }
 }

+ 30 - 17
frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option.rs

@@ -1,9 +1,8 @@
-use crate::entities::{CellChangeset, FieldType, GridDateFilter};
+use crate::entities::{CellChangeset, FieldType};
 use crate::entities::{CellIdentifier, CellIdentifierPayload};
 use crate::impl_type_option;
 use crate::services::cell::{
-    AnyCellData, CellData, CellDataChangeset, CellDataOperation, CellFilterOperation, DecodedCellData,
-    FromCellChangeset, FromCellString,
+    AnyCellData, CellData, CellDataChangeset, CellDataOperation, DecodedCellData, FromCellChangeset, FromCellString,
 };
 use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder};
 use bytes::Bytes;
@@ -110,6 +109,10 @@ impl DateTypeOption {
 
     fn utc_date_time_from_timestamp(&self, timestamp: i64) -> chrono::DateTime<chrono::Utc> {
         let native = NaiveDateTime::from_timestamp(timestamp, 0);
+        let native2 = NaiveDateTime::from_timestamp(timestamp, 0);
+
+        if native > native2 {}
+
         self.utc_date_time_from_native(native)
     }
 
@@ -118,19 +121,10 @@ impl DateTypeOption {
     }
 }
 
-impl CellFilterOperation<GridDateFilter> for DateTypeOption {
-    fn apply_filter(&self, any_cell_data: AnyCellData, _filter: &GridDateFilter) -> FlowyResult<bool> {
-        if !any_cell_data.is_date() {
-            return Ok(true);
-        }
-        Ok(false)
-    }
-}
-
-impl CellDataOperation<TimestampParser, DateCellChangeset> for DateTypeOption {
+impl CellDataOperation<DateTimestamp, DateCellChangeset> for DateTypeOption {
     fn decode_cell_data(
         &self,
-        cell_data: CellData<TimestampParser>,
+        cell_data: CellData<DateTimestamp>,
         decoded_field_type: &FieldType,
         _field_rev: &FieldRevision,
     ) -> FlowyResult<DecodedCellData> {
@@ -168,17 +162,36 @@ impl CellDataOperation<TimestampParser, DateCellChangeset> for DateTypeOption {
     }
 }
 
-pub struct TimestampParser(i64);
+pub struct DateTimestamp(i64);
+impl AsRef<i64> for DateTimestamp {
+    fn as_ref(&self) -> &i64 {
+        &self.0
+    }
+}
+
+impl std::convert::From<DateTimestamp> for i64 {
+    fn from(timestamp: DateTimestamp) -> Self {
+        timestamp.0
+    }
+}
 
-impl FromCellString for TimestampParser {
+impl FromCellString for DateTimestamp {
     fn from_cell_str(s: &str) -> FlowyResult<Self>
     where
         Self: Sized,
     {
         let num = s.parse::<i64>().unwrap_or(0);
-        Ok(TimestampParser(num))
+        Ok(DateTimestamp(num))
     }
 }
+
+impl std::convert::From<AnyCellData> for DateTimestamp {
+    fn from(data: AnyCellData) -> Self {
+        let num = data.cell_data.parse::<i64>().unwrap_or(0);
+        DateTimestamp(num)
+    }
+}
+
 #[derive(Default)]
 pub struct DateTypeOptionBuilder(DateTypeOption);
 impl_into_box_type_option_builder!(DateTypeOptionBuilder);

+ 0 - 3
frontend/rust-lib/flowy-grid/src/services/filter/date_filter.rs

@@ -1,3 +0,0 @@
-use crate::entities::GridDateFilter;
-
-impl GridDateFilter {}

+ 0 - 3
frontend/rust-lib/flowy-grid/src/services/filter/filter_cache.rs

@@ -1,12 +1,9 @@
 use crate::entities::{
     FieldType, GridCheckboxFilter, GridDateFilter, GridNumberFilter, GridSelectOptionFilter, GridTextFilter,
 };
-
 use dashmap::DashMap;
-
 use flowy_grid_data_model::revision::{FieldRevision, RowRevision};
 use flowy_sync::client_grid::GridRevisionPad;
-
 use std::collections::HashMap;
 use std::sync::Arc;
 use tokio::sync::RwLock;

+ 2 - 2
frontend/rust-lib/flowy-grid/src/services/filter/filter_service.rs

@@ -179,7 +179,7 @@ fn filter_cell(
         field_type,
     };
     let any_cell_data = AnyCellData::try_from(cell_rev).ok()?;
-    let is_hidden = match &filter_id.field_type {
+    let is_visible = match &filter_id.field_type {
         FieldType::RichText => filter_cache.text_filter.get(&filter_id).and_then(|filter| {
             Some(
                 field_rev
@@ -238,7 +238,7 @@ fn filter_cell(
         }),
     }?;
 
-    let is_visible = !is_hidden.unwrap_or(false);
+    let is_visible = !is_visible.unwrap_or(true);
     match filter_result.visible_by_field_id.get(&filter_id) {
         None => {
             if is_visible {

+ 6 - 6
frontend/rust-lib/flowy-grid/src/services/filter/checkbox_filter.rs → frontend/rust-lib/flowy-grid/src/services/filter/impls/checkbox_filter.rs

@@ -4,7 +4,7 @@ use crate::services::field::{CheckboxCellData, CheckboxTypeOption};
 use flowy_error::FlowyResult;
 
 impl GridCheckboxFilter {
-    pub fn apply(&self, cell_data: &CheckboxCellData) -> bool {
+    pub fn is_visible(&self, cell_data: &CheckboxCellData) -> bool {
         let is_check = cell_data.is_check();
         match self.condition {
             CheckboxCondition::IsChecked => is_check,
@@ -19,7 +19,7 @@ impl CellFilterOperation<GridCheckboxFilter> for CheckboxTypeOption {
             return Ok(true);
         }
         let checkbox_cell_data: CheckboxCellData = any_cell_data.try_into()?;
-        Ok(filter.apply(&checkbox_cell_data))
+        Ok(filter.is_visible(&checkbox_cell_data))
     }
 }
 
@@ -33,9 +33,9 @@ mod tests {
         let checkbox_filter = GridCheckboxFilter {
             condition: CheckboxCondition::IsChecked,
         };
-        for (value, r) in [("true", true), ("yes", true), ("false", false), ("no", false)] {
+        for (value, visible) in [("true", true), ("yes", true), ("false", false), ("no", false)] {
             let data = CheckboxCellData(value.to_owned());
-            assert_eq!(checkbox_filter.apply(&data), r);
+            assert_eq!(checkbox_filter.is_visible(&data), visible);
         }
     }
 
@@ -44,9 +44,9 @@ mod tests {
         let checkbox_filter = GridCheckboxFilter {
             condition: CheckboxCondition::IsUnChecked,
         };
-        for (value, r) in [("false", true), ("no", true), ("true", false), ("yes", false)] {
+        for (value, visible) in [("false", true), ("no", true), ("true", false), ("yes", false)] {
             let data = CheckboxCellData(value.to_owned());
-            assert_eq!(checkbox_filter.apply(&data), r);
+            assert_eq!(checkbox_filter.is_visible(&data), visible);
         }
     }
 }

+ 107 - 0
frontend/rust-lib/flowy-grid/src/services/filter/impls/date_filter.rs

@@ -0,0 +1,107 @@
+use crate::entities::{DateFilterCondition, GridDateFilter};
+use crate::services::cell::{AnyCellData, CellFilterOperation};
+use crate::services::field::{DateTimestamp, DateTypeOption};
+use flowy_error::FlowyResult;
+
+impl GridDateFilter {
+    pub fn is_visible<T: Into<i64>>(&self, cell_timestamp: T) -> bool {
+        if self.start.is_none() {
+            return false;
+        }
+        let cell_timestamp = cell_timestamp.into();
+        let start_timestamp = *self.start.as_ref().unwrap();
+        // We assume that the cell_timestamp doesn't contain hours, just day.
+        match self.condition {
+            DateFilterCondition::DateIs => cell_timestamp == start_timestamp,
+            DateFilterCondition::DateBefore => cell_timestamp < start_timestamp,
+            DateFilterCondition::DateAfter => cell_timestamp > start_timestamp,
+            DateFilterCondition::DateOnOrBefore => cell_timestamp <= start_timestamp,
+            DateFilterCondition::DateOnOrAfter => cell_timestamp >= start_timestamp,
+            DateFilterCondition::DateWithIn => {
+                if let Some(end_timestamp) = self.end.as_ref() {
+                    cell_timestamp >= start_timestamp && cell_timestamp <= *end_timestamp
+                } else {
+                    false
+                }
+            }
+            DateFilterCondition::DateIsEmpty => cell_timestamp == (0 as i64),
+        }
+    }
+}
+
+impl CellFilterOperation<GridDateFilter> for DateTypeOption {
+    fn apply_filter(&self, any_cell_data: AnyCellData, filter: &GridDateFilter) -> FlowyResult<bool> {
+        if !any_cell_data.is_date() {
+            return Ok(true);
+        }
+        let timestamp: DateTimestamp = any_cell_data.into();
+        Ok(filter.is_visible(timestamp))
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    #![allow(clippy::all)]
+    use crate::entities::{DateFilterCondition, GridDateFilter};
+
+    #[test]
+    fn date_filter_is_test() {
+        let filter = GridDateFilter {
+            condition: DateFilterCondition::DateIs,
+            start: Some(123),
+            end: None,
+        };
+
+        for (val, visible) in vec![(123, true), (12, false)] {
+            assert_eq!(filter.is_visible(val as i64), visible);
+        }
+    }
+    #[test]
+    fn date_filter_before_test() {
+        let filter = GridDateFilter {
+            condition: DateFilterCondition::DateBefore,
+            start: Some(123),
+            end: None,
+        };
+
+        for (val, visible) in vec![(123, false), (122, true)] {
+            assert_eq!(filter.is_visible(val as i64), visible);
+        }
+    }
+    #[test]
+    fn date_filter_before_or_on_test() {
+        let filter = GridDateFilter {
+            condition: DateFilterCondition::DateOnOrBefore,
+            start: Some(123),
+            end: None,
+        };
+
+        for (val, visible) in vec![(123, true), (122, true)] {
+            assert_eq!(filter.is_visible(val as i64), visible);
+        }
+    }
+    #[test]
+    fn date_filter_after_test() {
+        let filter = GridDateFilter {
+            condition: DateFilterCondition::DateAfter,
+            start: Some(123),
+            end: None,
+        };
+
+        for (val, visible) in vec![(1234, true), (122, false), (0, false)] {
+            assert_eq!(filter.is_visible(val as i64), visible);
+        }
+    }
+    #[test]
+    fn date_filter_within_test() {
+        let filter = GridDateFilter {
+            condition: DateFilterCondition::DateWithIn,
+            start: Some(123),
+            end: Some(130),
+        };
+
+        for (val, visible) in vec![(123, true), (130, true), (132, false)] {
+            assert_eq!(filter.is_visible(val as i64), visible);
+        }
+    }
+}

+ 13 - 0
frontend/rust-lib/flowy-grid/src/services/filter/impls/mod.rs

@@ -0,0 +1,13 @@
+mod checkbox_filter;
+mod date_filter;
+mod number_filter;
+mod select_option_filter;
+mod text_filter;
+mod url_filter;
+
+pub use checkbox_filter::*;
+pub use date_filter::*;
+pub use number_filter::*;
+pub use select_option_filter::*;
+pub use text_filter::*;
+pub use url_filter::*;

+ 10 - 10
frontend/rust-lib/flowy-grid/src/services/filter/number_filter.rs → frontend/rust-lib/flowy-grid/src/services/filter/impls/number_filter.rs

@@ -7,7 +7,7 @@ use rust_decimal::Decimal;
 use std::str::FromStr;
 
 impl GridNumberFilter {
-    pub fn apply(&self, num_cell_data: &NumberCellData) -> bool {
+    pub fn is_visible(&self, num_cell_data: &NumberCellData) -> bool {
         if self.content.is_none() {
             return false;
         }
@@ -40,7 +40,7 @@ impl CellFilterOperation<GridNumberFilter> for NumberTypeOption {
         let cell_data = any_cell_data.cell_data;
         let num_cell_data = self.format_cell_data(&cell_data)?;
 
-        Ok(filter.apply(&num_cell_data))
+        Ok(filter.is_visible(&num_cell_data))
     }
 }
 
@@ -57,15 +57,15 @@ mod tests {
             content: Some("123".to_owned()),
         };
 
-        for (num_str, r) in [("123", true), ("1234", false), ("", false)] {
+        for (num_str, visible) in [("123", true), ("1234", false), ("", false)] {
             let data = NumberCellData::from_str(num_str).unwrap();
-            assert_eq!(number_filter.apply(&data), r);
+            assert_eq!(number_filter.is_visible(&data), visible);
         }
 
         let format = NumberFormat::USD;
-        for (num_str, r) in [("$123", true), ("1234", false), ("", false)] {
+        for (num_str, visible) in [("$123", true), ("1234", false), ("", false)] {
             let data = NumberCellData::from_format_str(num_str, true, &format).unwrap();
-            assert_eq!(number_filter.apply(&data), r);
+            assert_eq!(number_filter.is_visible(&data), visible);
         }
     }
     #[test]
@@ -74,9 +74,9 @@ mod tests {
             condition: NumberFilterCondition::GreaterThan,
             content: Some("12".to_owned()),
         };
-        for (num_str, r) in [("123", true), ("10", false), ("30", true), ("", false)] {
+        for (num_str, visible) in [("123", true), ("10", false), ("30", true), ("", false)] {
             let data = NumberCellData::from_str(num_str).unwrap();
-            assert_eq!(number_filter.apply(&data), r);
+            assert_eq!(number_filter.is_visible(&data), visible);
         }
     }
 
@@ -86,9 +86,9 @@ mod tests {
             condition: NumberFilterCondition::LessThan,
             content: Some("100".to_owned()),
         };
-        for (num_str, r) in [("12", true), ("1234", false), ("30", true), ("", true)] {
+        for (num_str, visible) in [("12", true), ("1234", false), ("30", true), ("", true)] {
             let data = NumberCellData::from_str(num_str).unwrap();
-            assert_eq!(number_filter.apply(&data), r);
+            assert_eq!(number_filter.is_visible(&data), visible);
         }
     }
 }

+ 8 - 8
frontend/rust-lib/flowy-grid/src/services/filter/select_option_filter.rs → frontend/rust-lib/flowy-grid/src/services/filter/impls/select_option_filter.rs

@@ -7,7 +7,7 @@ use crate::services::field::{MultiSelectTypeOption, SingleSelectTypeOption};
 use flowy_error::FlowyResult;
 
 impl GridSelectOptionFilter {
-    pub fn apply(&self, selected_options: &SelectedSelectOptions) -> bool {
+    pub fn is_visible(&self, selected_options: &SelectedSelectOptions) -> bool {
         let selected_option_ids: Vec<&String> = selected_options.options.iter().map(|option| &option.id).collect();
         match self.condition {
             SelectOptionCondition::OptionIs => {
@@ -46,7 +46,7 @@ impl CellFilterOperation<GridSelectOptionFilter> for MultiSelectTypeOption {
         }
 
         let selected_options = SelectedSelectOptions::from(self.selected_select_option(any_cell_data));
-        Ok(filter.apply(&selected_options))
+        Ok(filter.is_visible(&selected_options))
     }
 }
 
@@ -56,7 +56,7 @@ impl CellFilterOperation<GridSelectOptionFilter> for SingleSelectTypeOption {
             return Ok(true);
         }
         let selected_options = SelectedSelectOptions::from(self.selected_select_option(any_cell_data));
-        Ok(filter.apply(&selected_options))
+        Ok(filter.is_visible(&selected_options))
     }
 }
 
@@ -78,29 +78,29 @@ mod tests {
         };
 
         assert_eq!(
-            filter_1.apply(&SelectedSelectOptions {
+            filter_1.is_visible(&SelectedSelectOptions {
                 options: vec![option_1.clone(), option_2.clone()],
             }),
             false
         );
 
         assert_eq!(
-            filter_1.apply(&SelectedSelectOptions {
+            filter_1.is_visible(&SelectedSelectOptions {
                 options: vec![option_1.clone(), option_2.clone(), option_3.clone()],
             }),
             true
         );
 
         assert_eq!(
-            filter_1.apply(&SelectedSelectOptions {
+            filter_1.is_visible(&SelectedSelectOptions {
                 options: vec![option_1.clone(), option_3.clone()],
             }),
             true
         );
 
-        assert_eq!(filter_1.apply(&SelectedSelectOptions { options: vec![] }), true);
+        assert_eq!(filter_1.is_visible(&SelectedSelectOptions { options: vec![] }), true);
         assert_eq!(
-            filter_1.apply(&SelectedSelectOptions {
+            filter_1.is_visible(&SelectedSelectOptions {
                 options: vec![option_1.clone()],
             }),
             true,

+ 19 - 19
frontend/rust-lib/flowy-grid/src/services/filter/text_filter.rs → frontend/rust-lib/flowy-grid/src/services/filter/impls/text_filter.rs

@@ -4,7 +4,7 @@ use crate::services::field::{RichTextTypeOption, TextCellData};
 use flowy_error::FlowyResult;
 
 impl GridTextFilter {
-    pub fn apply<T: AsRef<str>>(&self, cell_data: T) -> bool {
+    pub fn is_visible<T: AsRef<str>>(&self, cell_data: T) -> bool {
         let cell_data = cell_data.as_ref();
         let s = cell_data.to_lowercase();
         if let Some(content) = self.content.as_ref() {
@@ -31,7 +31,7 @@ impl CellFilterOperation<GridTextFilter> for RichTextTypeOption {
         }
 
         let text_cell_data: TextCellData = any_cell_data.try_into()?;
-        Ok(filter.apply(text_cell_data))
+        Ok(filter.is_visible(text_cell_data))
     }
 }
 #[cfg(test)]
@@ -46,10 +46,10 @@ mod tests {
             content: Some("appflowy".to_owned()),
         };
 
-        assert!(text_filter.apply("AppFlowy"));
-        assert_eq!(text_filter.apply("appflowy"), true);
-        assert_eq!(text_filter.apply("Appflowy"), true);
-        assert_eq!(text_filter.apply("AppFlowy.io"), false);
+        assert!(text_filter.is_visible("AppFlowy"));
+        assert_eq!(text_filter.is_visible("appflowy"), true);
+        assert_eq!(text_filter.is_visible("Appflowy"), true);
+        assert_eq!(text_filter.is_visible("AppFlowy.io"), false);
     }
     #[test]
     fn text_filter_start_with_test() {
@@ -58,9 +58,9 @@ mod tests {
             content: Some("appflowy".to_owned()),
         };
 
-        assert_eq!(text_filter.apply("AppFlowy.io"), true);
-        assert_eq!(text_filter.apply(""), false);
-        assert_eq!(text_filter.apply("https"), false);
+        assert_eq!(text_filter.is_visible("AppFlowy.io"), true);
+        assert_eq!(text_filter.is_visible(""), false);
+        assert_eq!(text_filter.is_visible("https"), false);
     }
 
     #[test]
@@ -70,9 +70,9 @@ mod tests {
             content: Some("appflowy".to_owned()),
         };
 
-        assert_eq!(text_filter.apply("https://github.com/appflowy"), true);
-        assert_eq!(text_filter.apply("App"), false);
-        assert_eq!(text_filter.apply("appflowy.io"), false);
+        assert_eq!(text_filter.is_visible("https://github.com/appflowy"), true);
+        assert_eq!(text_filter.is_visible("App"), false);
+        assert_eq!(text_filter.is_visible("appflowy.io"), false);
     }
     #[test]
     fn text_filter_empty_test() {
@@ -81,8 +81,8 @@ mod tests {
             content: Some("appflowy".to_owned()),
         };
 
-        assert_eq!(text_filter.apply(""), true);
-        assert_eq!(text_filter.apply("App"), false);
+        assert_eq!(text_filter.is_visible(""), true);
+        assert_eq!(text_filter.is_visible("App"), false);
     }
     #[test]
     fn text_filter_contain_test() {
@@ -91,10 +91,10 @@ mod tests {
             content: Some("appflowy".to_owned()),
         };
 
-        assert_eq!(text_filter.apply("https://github.com/appflowy"), true);
-        assert_eq!(text_filter.apply("AppFlowy"), true);
-        assert_eq!(text_filter.apply("App"), false);
-        assert_eq!(text_filter.apply(""), false);
-        assert_eq!(text_filter.apply("github"), false);
+        assert_eq!(text_filter.is_visible("https://github.com/appflowy"), true);
+        assert_eq!(text_filter.is_visible("AppFlowy"), true);
+        assert_eq!(text_filter.is_visible("App"), false);
+        assert_eq!(text_filter.is_visible(""), false);
+        assert_eq!(text_filter.is_visible("github"), false);
     }
 }

+ 1 - 1
frontend/rust-lib/flowy-grid/src/services/filter/url_filter.rs → frontend/rust-lib/flowy-grid/src/services/filter/impls/url_filter.rs

@@ -10,6 +10,6 @@ impl CellFilterOperation<GridTextFilter> for URLTypeOption {
         }
 
         let text_cell_data: TextCellData = any_cell_data.try_into()?;
-        Ok(filter.apply(&text_cell_data))
+        Ok(filter.is_visible(&text_cell_data))
     }
 }

+ 1 - 6
frontend/rust-lib/flowy-grid/src/services/filter/mod.rs

@@ -1,10 +1,5 @@
-mod checkbox_filter;
-mod date_filter;
 mod filter_cache;
 mod filter_service;
-mod number_filter;
-mod select_option_filter;
-mod text_filter;
-mod url_filter;
+mod impls;
 
 pub(crate) use filter_service::*;