2020-05-09 17:15:47 +00:00
|
|
|
pub mod group;
|
2020-06-17 17:33:50 +00:00
|
|
|
pub mod split;
|
2020-05-09 17:15:47 +00:00
|
|
|
|
2020-08-03 22:47:19 +00:00
|
|
|
mod internal;
|
|
|
|
|
2020-08-18 07:00:02 +00:00
|
|
|
pub use crate::utils::group::group;
|
|
|
|
pub use crate::utils::split::split;
|
2020-08-03 22:47:19 +00:00
|
|
|
|
2020-09-30 18:27:52 +00:00
|
|
|
pub use crate::utils::internal::Reduction;
|
2020-08-18 07:00:02 +00:00
|
|
|
use crate::utils::internal::*;
|
2020-08-03 22:47:19 +00:00
|
|
|
|
|
|
|
use derive_new::new;
|
|
|
|
use getset::Getters;
|
|
|
|
use nu_errors::ShellError;
|
|
|
|
use nu_protocol::{UntaggedValue, Value};
|
|
|
|
use nu_source::Tag;
|
|
|
|
|
|
|
|
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Getters, Clone, new)]
|
|
|
|
pub struct Model {
|
|
|
|
pub labels: Labels,
|
|
|
|
pub ranges: (Range, Range),
|
|
|
|
|
|
|
|
pub data: Value,
|
|
|
|
pub percentages: Value,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[allow(clippy::type_complexity)]
|
|
|
|
pub struct Operation<'a> {
|
|
|
|
pub grouper: Option<Box<dyn Fn(usize, &Value) -> Result<String, ShellError> + Send>>,
|
|
|
|
pub splitter: Option<Box<dyn Fn(usize, &Value) -> Result<String, ShellError> + Send>>,
|
2020-09-30 18:27:52 +00:00
|
|
|
pub format: &'a Option<Box<dyn Fn(&Value, String) -> Result<String, ShellError>>>,
|
2020-08-03 22:47:19 +00:00
|
|
|
pub eval: &'a Option<Box<dyn Fn(usize, &Value) -> Result<Value, ShellError> + Send>>,
|
2020-09-30 18:27:52 +00:00
|
|
|
pub reduction: &'a Reduction,
|
2020-08-03 22:47:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn report(
|
|
|
|
values: &Value,
|
|
|
|
options: Operation,
|
|
|
|
tag: impl Into<Tag>,
|
|
|
|
) -> Result<Model, ShellError> {
|
|
|
|
let tag = tag.into();
|
|
|
|
|
|
|
|
let grouped = group(&values, &options.grouper, &tag)?;
|
|
|
|
let splitted = split(&grouped, &options.splitter, &tag)?;
|
|
|
|
|
|
|
|
let x = grouped
|
|
|
|
.row_entries()
|
|
|
|
.map(|(key, _)| key.clone())
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
|
2020-09-30 18:27:52 +00:00
|
|
|
let x = sort_columns(&x, &options.format)?;
|
2020-08-03 22:47:19 +00:00
|
|
|
|
|
|
|
let mut y = splitted
|
|
|
|
.row_entries()
|
|
|
|
.map(|(key, _)| key.clone())
|
|
|
|
.collect::<Vec<_>>();
|
2020-09-30 18:27:52 +00:00
|
|
|
|
2020-08-03 22:47:19 +00:00
|
|
|
y.sort();
|
|
|
|
|
|
|
|
let planes = Labels { x, y };
|
2020-09-30 18:27:52 +00:00
|
|
|
|
2020-08-03 22:47:19 +00:00
|
|
|
let sorted = sort(&planes, &splitted, &tag)?;
|
|
|
|
|
|
|
|
let evaluated = evaluate(
|
|
|
|
&sorted,
|
|
|
|
if options.eval.is_some() {
|
|
|
|
options.eval
|
|
|
|
} else {
|
|
|
|
&None
|
|
|
|
},
|
|
|
|
&tag,
|
|
|
|
)?;
|
|
|
|
|
|
|
|
let group_labels = planes.grouping_total();
|
|
|
|
|
2020-09-30 18:27:52 +00:00
|
|
|
let reduced = reduce(&evaluated, options.reduction, &tag)?;
|
2020-08-03 22:47:19 +00:00
|
|
|
|
2020-09-30 18:27:52 +00:00
|
|
|
let maxima = max(&reduced, &tag)?;
|
2020-08-03 22:47:19 +00:00
|
|
|
|
|
|
|
let percents = percentages(&maxima, &reduced, &tag)?;
|
|
|
|
|
|
|
|
Ok(Model {
|
|
|
|
labels: planes,
|
|
|
|
ranges: (
|
|
|
|
Range {
|
|
|
|
start: UntaggedValue::int(0).into_untagged_value(),
|
|
|
|
end: group_labels,
|
|
|
|
},
|
|
|
|
Range {
|
|
|
|
start: UntaggedValue::int(0).into_untagged_value(),
|
2020-10-02 00:23:10 +00:00
|
|
|
end: maxima,
|
2020-08-03 22:47:19 +00:00
|
|
|
},
|
|
|
|
),
|
|
|
|
data: reduced,
|
|
|
|
percentages: percents,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
pub mod helpers {
|
|
|
|
use nu_errors::ShellError;
|
2020-10-20 09:07:13 +00:00
|
|
|
use nu_protocol::{row, Value};
|
|
|
|
use nu_source::{Tag, TaggedItem};
|
|
|
|
use nu_test_support::value::{date, int, string, table};
|
2020-08-03 22:47:19 +00:00
|
|
|
use nu_value_ext::ValueExt;
|
|
|
|
|
|
|
|
pub fn committers() -> Vec<Value> {
|
|
|
|
vec![
|
2020-10-20 09:07:13 +00:00
|
|
|
row! {
|
2020-08-03 22:47:19 +00:00
|
|
|
"date".into() => date("2019-07-23"),
|
|
|
|
"name".into() => string("AR"),
|
|
|
|
"country".into() => string("EC"),
|
2020-10-20 09:07:13 +00:00
|
|
|
"chickens".into() => int(10)
|
|
|
|
},
|
|
|
|
row! {
|
2020-08-03 22:47:19 +00:00
|
|
|
"date".into() => date("2019-07-23"),
|
|
|
|
"name".into() => string("JT"),
|
|
|
|
"country".into() => string("NZ"),
|
2020-10-20 09:07:13 +00:00
|
|
|
"chickens".into() => int(5)
|
|
|
|
},
|
|
|
|
row! {
|
2020-08-03 22:47:19 +00:00
|
|
|
"date".into() => date("2019-10-10"),
|
|
|
|
"name".into() => string("YK"),
|
|
|
|
"country".into() => string("US"),
|
2020-10-20 09:07:13 +00:00
|
|
|
"chickens".into() => int(6)
|
|
|
|
},
|
|
|
|
row! {
|
2020-08-03 22:47:19 +00:00
|
|
|
"date".into() => date("2019-09-24"),
|
|
|
|
"name".into() => string("AR"),
|
|
|
|
"country".into() => string("EC"),
|
2020-10-20 09:07:13 +00:00
|
|
|
"chickens".into() => int(20)
|
|
|
|
},
|
|
|
|
row! {
|
2020-08-03 22:47:19 +00:00
|
|
|
"date".into() => date("2019-10-10"),
|
|
|
|
"name".into() => string("JT"),
|
|
|
|
"country".into() => string("NZ"),
|
2020-10-20 09:07:13 +00:00
|
|
|
"chickens".into() => int(15)
|
|
|
|
},
|
|
|
|
row! {
|
2020-08-03 22:47:19 +00:00
|
|
|
"date".into() => date("2019-09-24"),
|
|
|
|
"name".into() => string("YK"),
|
|
|
|
"country".into() => string("US"),
|
2020-10-20 09:07:13 +00:00
|
|
|
"chickens".into() => int(4)
|
|
|
|
},
|
|
|
|
row! {
|
2020-08-03 22:47:19 +00:00
|
|
|
"date".into() => date("2019-10-10"),
|
|
|
|
"name".into() => string("AR"),
|
|
|
|
"country".into() => string("EC"),
|
2020-10-20 09:07:13 +00:00
|
|
|
"chickens".into() => int(30)
|
|
|
|
},
|
|
|
|
row! {
|
2020-08-03 22:47:19 +00:00
|
|
|
"date".into() => date("2019-09-24"),
|
|
|
|
"name".into() => string("JT"),
|
|
|
|
"country".into() => string("NZ"),
|
2020-10-20 09:07:13 +00:00
|
|
|
"chickens".into() => int(10)
|
|
|
|
},
|
|
|
|
row! {
|
2020-08-03 22:47:19 +00:00
|
|
|
"date".into() => date("2019-07-23"),
|
|
|
|
"name".into() => string("YK"),
|
|
|
|
"country".into() => string("US"),
|
2020-10-20 09:07:13 +00:00
|
|
|
"chickens".into() => int(2)
|
|
|
|
},
|
2020-08-03 22:47:19 +00:00
|
|
|
]
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn committers_grouped_by_date() -> Value {
|
|
|
|
let sample = table(&committers());
|
|
|
|
|
|
|
|
let grouper = Box::new(move |_, row: &Value| {
|
|
|
|
let key = String::from("date").tagged_unknown();
|
2020-08-18 07:00:02 +00:00
|
|
|
let group_key = row
|
|
|
|
.get_data_by_key(key.borrow_spanned())
|
|
|
|
.expect("get key failed");
|
2020-08-03 22:47:19 +00:00
|
|
|
|
|
|
|
group_key.format("%Y-%m-%d")
|
|
|
|
});
|
|
|
|
|
2020-08-18 07:00:02 +00:00
|
|
|
crate::utils::group(&sample, &Some(grouper), Tag::unknown())
|
|
|
|
.expect("failed to create group")
|
2020-08-03 22:47:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn date_formatter(
|
2020-09-30 18:27:52 +00:00
|
|
|
fmt: String,
|
2020-08-03 22:47:19 +00:00
|
|
|
) -> Box<dyn Fn(&Value, String) -> Result<String, ShellError>> {
|
2020-09-30 18:27:52 +00:00
|
|
|
Box::new(move |date: &Value, _: String| {
|
|
|
|
let fmt = fmt.clone();
|
|
|
|
date.format(&fmt)
|
|
|
|
})
|
2020-08-03 22:47:19 +00:00
|
|
|
}
|
2020-08-18 07:00:02 +00:00
|
|
|
}
|
2020-08-03 22:47:19 +00:00
|
|
|
|
2020-08-18 07:00:02 +00:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2020-10-20 09:07:13 +00:00
|
|
|
use super::helpers::{committers, date_formatter};
|
2020-09-30 18:27:52 +00:00
|
|
|
use super::{report, Labels, Model, Operation, Range, Reduction};
|
2020-08-18 07:00:02 +00:00
|
|
|
use nu_errors::ShellError;
|
|
|
|
use nu_protocol::Value;
|
2020-10-20 09:07:13 +00:00
|
|
|
use nu_source::{Tag, TaggedItem};
|
|
|
|
use nu_test_support::value::{decimal_from_float, int, table};
|
2020-08-18 07:00:02 +00:00
|
|
|
use nu_value_ext::ValueExt;
|
2020-09-30 18:27:52 +00:00
|
|
|
|
2020-10-20 09:07:13 +00:00
|
|
|
pub fn assert_without_checking_percentages(report_a: Model, report_b: Model) {
|
|
|
|
assert_eq!(report_a.labels.x, report_b.labels.x);
|
|
|
|
assert_eq!(report_a.labels.y, report_b.labels.y);
|
|
|
|
assert_eq!(report_a.ranges, report_b.ranges);
|
|
|
|
assert_eq!(report_a.data, report_b.data);
|
|
|
|
}
|
|
|
|
|
2020-08-03 22:47:19 +00:00
|
|
|
#[test]
|
2020-09-30 18:27:52 +00:00
|
|
|
fn prepares_report_using_counting_value() {
|
2020-08-03 22:47:19 +00:00
|
|
|
let committers = table(&committers());
|
|
|
|
|
|
|
|
let by_date = Box::new(move |_, row: &Value| {
|
|
|
|
let key = String::from("date").tagged_unknown();
|
|
|
|
let key = row.get_data_by_key(key.borrow_spanned()).unwrap();
|
|
|
|
|
2020-09-30 18:27:52 +00:00
|
|
|
let callback = date_formatter("%Y-%m-%d".to_string());
|
2020-08-03 22:47:19 +00:00
|
|
|
callback(&key, "nothing".to_string())
|
|
|
|
});
|
|
|
|
|
|
|
|
let by_country = Box::new(move |_, row: &Value| {
|
|
|
|
let key = String::from("country").tagged_unknown();
|
|
|
|
let key = row.get_data_by_key(key.borrow_spanned()).unwrap();
|
|
|
|
nu_value_ext::as_string(&key)
|
|
|
|
});
|
|
|
|
|
|
|
|
let options = Operation {
|
|
|
|
grouper: Some(by_date),
|
|
|
|
splitter: Some(by_country),
|
2020-09-30 18:27:52 +00:00
|
|
|
format: &None,
|
2020-08-03 22:47:19 +00:00
|
|
|
eval: /* value to be used for accumulation */ &Some(Box::new(move |_, value: &Value| {
|
|
|
|
let chickens_key = String::from("chickens").tagged_unknown();
|
|
|
|
|
|
|
|
value
|
|
|
|
.get_data_by_key(chickens_key.borrow_spanned())
|
|
|
|
.ok_or_else(|| {
|
|
|
|
ShellError::labeled_error(
|
|
|
|
"unknown column",
|
|
|
|
"unknown column",
|
|
|
|
chickens_key.span(),
|
|
|
|
)
|
|
|
|
})
|
|
|
|
})),
|
2020-09-30 18:27:52 +00:00
|
|
|
reduction: &Reduction::Count
|
2020-08-03 22:47:19 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
assert_without_checking_percentages(
|
|
|
|
report(&committers, options, Tag::unknown()).unwrap(),
|
|
|
|
Model {
|
|
|
|
labels: Labels {
|
|
|
|
x: vec![
|
|
|
|
String::from("2019-07-23"),
|
|
|
|
String::from("2019-09-24"),
|
|
|
|
String::from("2019-10-10"),
|
|
|
|
],
|
|
|
|
y: vec![String::from("EC"), String::from("NZ"), String::from("US")],
|
|
|
|
},
|
|
|
|
ranges: (
|
|
|
|
Range {
|
|
|
|
start: int(0),
|
|
|
|
end: int(3),
|
|
|
|
},
|
|
|
|
Range {
|
|
|
|
start: int(0),
|
2020-10-02 00:23:10 +00:00
|
|
|
end: int(30),
|
2020-08-03 22:47:19 +00:00
|
|
|
},
|
|
|
|
),
|
|
|
|
data: table(&[
|
2020-09-30 18:27:52 +00:00
|
|
|
table(&[int(10), int(20), int(30)]),
|
|
|
|
table(&[int(5), int(10), int(15)]),
|
|
|
|
table(&[int(2), int(4), int(6)]),
|
2020-08-03 22:47:19 +00:00
|
|
|
]),
|
|
|
|
percentages: table(&[
|
2020-09-21 17:28:31 +00:00
|
|
|
table(&[
|
2020-10-20 09:07:13 +00:00
|
|
|
decimal_from_float(33.33),
|
|
|
|
decimal_from_float(66.66),
|
|
|
|
decimal_from_float(99.99),
|
2020-09-21 17:28:31 +00:00
|
|
|
]),
|
|
|
|
table(&[
|
2020-10-20 09:07:13 +00:00
|
|
|
decimal_from_float(16.66),
|
|
|
|
decimal_from_float(33.33),
|
|
|
|
decimal_from_float(49.99),
|
2020-09-21 17:28:31 +00:00
|
|
|
]),
|
|
|
|
table(&[
|
2020-10-20 09:07:13 +00:00
|
|
|
decimal_from_float(6.66),
|
|
|
|
decimal_from_float(13.33),
|
|
|
|
decimal_from_float(19.99),
|
2020-09-21 17:28:31 +00:00
|
|
|
]),
|
2020-08-03 22:47:19 +00:00
|
|
|
]),
|
|
|
|
},
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|