Skip to content

Commit

Permalink
Periodic reports
Browse files Browse the repository at this point in the history
  • Loading branch information
k88hudson-cfa committed Nov 29, 2024
1 parent 837e679 commit b65a6f8
Show file tree
Hide file tree
Showing 2 changed files with 246 additions and 24 deletions.
140 changes: 117 additions & 23 deletions src/people.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,25 @@ seq!(Z in 1..20 {
});

pub trait Tabulator {
fn setup(&self, context: &mut Context);
fn get_typelist(&self) -> Vec<TypeId>;
fn get_columns(&self) -> Vec<String>;
}

impl<T: PersonProperty + 'static> Tabulator for (T,) {
fn setup(&self, context: &mut Context) {
context.index_property_noargs::<T>();
}
fn get_typelist(&self) -> Vec<TypeId> {
vec![std::any::TypeId::of::<T>()]
}
fn get_columns(&self) -> Vec<String> {
vec![String::from(
std::any::type_name::<T>().split("::").last().unwrap(),
)]
}
}

macro_rules! impl_tabulator {
($ct:expr) => {
seq!(N in 0..$ct {
Expand All @@ -118,6 +133,11 @@ macro_rules! impl_tabulator {
)*
)
{
fn setup(&self, context: &mut Context) {
#(
context.index_property_noargs::<T~N>();
)*
}
fn get_typelist(&self) -> Vec<TypeId> {
vec![
#(
Expand All @@ -138,7 +158,7 @@ macro_rules! impl_tabulator {
}
}

seq!(Z in 1..20 {
seq!(Z in 2..20 {
impl_tabulator!(Z);
});

Expand Down Expand Up @@ -194,6 +214,7 @@ impl Index {
max_indexed: 0,
}
}

fn add_person(&mut self, context: &Context, person_id: PersonId) {
let (hash, display) = (self.indexer)(context, person_id);
self.lookup
Expand Down Expand Up @@ -617,14 +638,20 @@ impl PeopleData {
}
}

fn get_index_ref_mut_by_prop<T: PersonProperty + 'static>(
fn get_index_ref_mut_by_prop_noargs<T: PersonProperty + 'static>(
&self,
_property: T,
) -> Option<RefMut<Index>> {
let type_id = TypeId::of::<T>();
self.get_index_ref_mut(type_id)
}

fn get_index_ref_mut_by_prop<T: PersonProperty + 'static>(
&self,
_property: T,
) -> Option<RefMut<Index>> {
self.get_index_ref_mut_by_prop_noargs::<T>()
}

// Convenience function to iterate over the current population.
// Note that this doesn't hold a reference to PeopleData, so if
// you change the population while using it, it won't notice.
Expand Down Expand Up @@ -730,28 +757,37 @@ pub trait ContextPeopleExt {

// Returns a PersonId for a usize
fn get_person_id(&self, person_id: usize) -> PersonId;
fn index_property_noargs<T: PersonProperty + 'static>(&mut self);
fn index_property<T: PersonProperty + 'static>(&mut self, property: T);
fn query_people<T: Query>(&self, q: T) -> Vec<PersonId>;
fn match_person<T: Query>(&self, person_id: PersonId, q: T) -> bool;
fn get_counts<T: Tabulator>(&self, tabulator: T, print_fn: &dyn Fn(&[String], usize));
fn get_counts<T: Tabulator, F>(&self, tabulator: &T, print_fn: F)
where
F: Fn(&Context, &[String], usize);
}

fn process_indices(
context: &Context,
remaining_indices: &[&Index],
accumulated_values: &mut [IndexValue],
display_values: &mut Vec<String>,
current_matches: &mut HashSet<PersonId>,
intersect: bool,
print_fn: &dyn Fn(&[String], usize),
print_fn: &dyn Fn(&Context, &[String], usize),
) {
if remaining_indices.is_empty() {
print_fn(display_values, current_matches.len());
print_fn(context, display_values, current_matches.len());
return;
}

if let Some((next_index, rest_indices)) = remaining_indices.split_first() {
// TODO this might be empty?
let lookup = next_index.lookup.as_ref().unwrap();

// If there is nothing in the index, we don't need to process it
if lookup.is_empty() {
return;
}

for (value, (people, display)) in lookup {
let mut updated_values = accumulated_values.to_owned();
updated_values.push(value.clone());
Expand All @@ -768,13 +804,16 @@ fn process_indices(
};

process_indices(
context,
rest_indices,
&mut updated_values,
display_values,
&mut matches,
true,
print_fn,
);
display_values.pop();
updated_values.pop();
}
}
}
Expand Down Expand Up @@ -923,7 +962,11 @@ impl ContextPeopleExt for Context {
PersonId { id: person_id }
}

fn index_property<T: PersonProperty + 'static>(&mut self, property: T) {
fn index_property<T: PersonProperty + 'static>(&mut self, _property: T) {
self.index_property_noargs::<T>();
}

fn index_property_noargs<T: PersonProperty + 'static>(&mut self) {
// Ensure that the data container exists
{
let _ = self.get_data_container_mut(PeoplePlugin);
Expand All @@ -933,7 +976,9 @@ impl ContextPeopleExt for Context {
self.register_indexer::<T>();

let data_container = self.get_data_container(PeoplePlugin).unwrap();
let mut index = data_container.get_index_ref_mut_by_prop(property).unwrap();
let mut index = data_container
.get_index_ref_mut_by_prop_noargs::<T>()
.unwrap();
if index.lookup.is_none() {
index.lookup = Some(HashMap::new());
}
Expand Down Expand Up @@ -986,7 +1031,10 @@ impl ContextPeopleExt for Context {
}
}

fn get_counts<T: Tabulator>(&self, tabulator: T, print_fn: &dyn Fn(&[String], usize)) {
fn get_counts<T: Tabulator, F>(&self, tabulator: &T, print_fn: F)
where
F: Fn(&Context, &[String], usize),
{
let type_ids = tabulator.get_typelist();

// First, update indexes
Expand Down Expand Up @@ -1016,6 +1064,7 @@ impl ContextPeopleExt for Context {
.collect::<Vec<&Index>>();

process_indices(
self,
indices.as_slice(),
&mut Vec::new(),
&mut Vec::new(),
Expand Down Expand Up @@ -1171,7 +1220,7 @@ mod test {
error::IxaError,
people::{Index, IndexValue, PeoplePlugin, PersonPropertyHolder},
};
use std::{any::TypeId, cell::RefCell, rc::Rc, vec};
use std::{any::TypeId, cell::RefCell, collections::HashSet, hash::Hash, rc::Rc, vec};

define_person_property!(Age, u8);
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
Expand Down Expand Up @@ -1855,21 +1904,66 @@ mod test {
assert_eq!(cols.get_columns(), vec!["Age", "RiskCategoryType"]);
}

#[test]
fn test_get_counts() {
fn get_counts_test_setup<T: Tabulator>(
tabulator: &T,
setup: impl FnOnce(&mut Context),
expected_values: &HashSet<(Vec<String>, usize)>,
) {
let mut context = Context::new();
context.index_property(IsRunner);
context.index_property(IsSwimmer);
setup(&mut context);
tabulator.setup(&mut context);

let results: RefCell<HashSet<(Vec<String>, usize)>> = RefCell::new(HashSet::new());
context.get_counts(tabulator, |_context, values, count| {
results.borrow_mut().insert((values.to_vec(), count));
});

let anne = context.add_person(()).unwrap();
let charlie = context.add_person(()).unwrap();
let results = &*results.borrow();
assert_eq!(results, expected_values);
}

context.set_person_property(anne, IsRunner, true);
context.set_person_property(charlie, IsRunner, true);
context.set_person_property(anne, IsSwimmer, true);
#[test]
fn test_get_counts() {
let tabulator = (IsRunner,);
let mut expected = HashSet::new();
expected.insert((vec!["true".to_string()], 1));
expected.insert((vec!["false".to_string()], 1));
get_counts_test_setup(
&tabulator,
|context| {
let bob = context.add_person(()).unwrap();
context.add_person(()).unwrap();
context.set_person_property(bob, IsRunner, true);
},
&expected,
);
}

context.get_counts((IsRunner, IsSwimmer), &|values, count| {
println!("{values:?} {count}");
});
#[test]
fn test_get_counts_multi() {
let tabulator = (IsRunner, IsSwimmer);
let mut expected = HashSet::new();
expected.insert((vec!["false".to_string(), "false".to_string()], 3));
expected.insert((vec!["false".to_string(), "true".to_string()], 1));
expected.insert((vec!["true".to_string(), "false".to_string()], 1));
expected.insert((vec!["true".to_string(), "true".to_string()], 1));

get_counts_test_setup(
&tabulator,
|context| {
context.add_person(()).unwrap();
context.add_person(()).unwrap();
context.add_person(()).unwrap();
let bob = context.add_person(()).unwrap();
let anne = context.add_person(()).unwrap();
let charlie = context.add_person(()).unwrap();

context.set_person_property(bob, IsRunner, true);
context.set_person_property(charlie, IsRunner, true);
context.set_person_property(anne, IsSwimmer, true);
context.set_person_property(charlie, IsSwimmer, true);
},
&expected,
);
}
}
Loading

0 comments on commit b65a6f8

Please sign in to comment.