(I should set up `cargo fmt` as a commit hook)
This commit is contained in:
Emilia Allison 2023-06-07 16:14:19 -04:00
parent 658ab3082a
commit 85670fe86e
Signed by: emilia
GPG Key ID: 7A3F8997BFE894E0
9 changed files with 207 additions and 112 deletions

View File

@ -1,9 +1,9 @@
#![allow(non_snake_case)] #![allow(non_snake_case)]
use js_sys::Array;
use wasm_bindgen::{prelude::*, JsCast, JsValue};
use web_sys::{Blob, HtmlAnchorElement, HtmlDialogElement, HtmlFormElement, HtmlInputElement, Url};
use yew::prelude::*; use yew::prelude::*;
use yewdux::prelude::*; use yewdux::prelude::*;
use wasm_bindgen::{JsValue, JsCast, prelude::*};
use web_sys::{Blob, Url, HtmlAnchorElement, HtmlDialogElement, HtmlInputElement, HtmlFormElement};
use js_sys::Array;
use super::new_plate_dialog::NewPlateDialog; use super::new_plate_dialog::NewPlateDialog;
use super::plates::plate_container::PlateContainer; use super::plates::plate_container::PlateContainer;
@ -11,8 +11,8 @@ use super::states::{CurrentTransfer, MainState};
use super::transfer_menu::TransferMenu; use super::transfer_menu::TransferMenu;
use super::tree::Tree; use super::tree::Tree;
use crate::data::plate_instances::PlateInstance;
use crate::data::csv::state_to_csv; use crate::data::csv::state_to_csv;
use crate::data::plate_instances::PlateInstance;
#[function_component] #[function_component]
pub fn MainWindow() -> Html { pub fn MainWindow() -> Html {
@ -59,7 +59,8 @@ pub fn MainWindow() -> Html {
let ct_dispatch = ct_dispatch.clone(); let ct_dispatch = ct_dispatch.clone();
Callback::from(move |_| { Callback::from(move |_| {
let window = web_sys::window().unwrap(); let window = web_sys::window().unwrap();
let confirm = window.confirm_with_message("This will reset all plates and transfers. Proceed?"); let confirm =
window.confirm_with_message("This will reset all plates and transfers. Proceed?");
if let Ok(confirm) = confirm { if let Ok(confirm) = confirm {
if confirm { if confirm {
main_dispatch.set(MainState::default()); main_dispatch.set(MainState::default());
@ -73,8 +74,11 @@ pub fn MainWindow() -> Html {
let main_state = main_state.clone(); let main_state = main_state.clone();
Callback::from(move |_| { Callback::from(move |_| {
if main_state.transfers.len() == 0 { if main_state.transfers.len() == 0 {
web_sys::window().unwrap().alert_with_message("No transfers to export.").unwrap(); web_sys::window()
return () .unwrap()
.alert_with_message("No transfers to export.")
.unwrap();
return ();
} }
web_sys::window().unwrap().alert_with_message("CSV export is currently not importable. Export as JSON if you'd like to back up your work!").unwrap(); web_sys::window().unwrap().alert_with_message("CSV export is currently not importable. Export as JSON if you'd like to back up your work!").unwrap();
if let Ok(csv) = state_to_csv(&main_state) { if let Ok(csv) = state_to_csv(&main_state) {
@ -89,7 +93,10 @@ pub fn MainWindow() -> Html {
if let Ok(json) = serde_json::to_string(&main_state) { if let Ok(json) = serde_json::to_string(&main_state) {
save_str(&json, "plate-tool-state.json"); save_str(&json, "plate-tool-state.json");
} else { } else {
web_sys::window().unwrap().alert_with_message("Failed to export.").unwrap(); web_sys::window()
.unwrap()
.alert_with_message("Failed to export.")
.unwrap();
} }
}) })
}; };
@ -100,7 +107,11 @@ pub fn MainWindow() -> Html {
let window = web_sys::window().unwrap(); let window = web_sys::window().unwrap();
let document = window.document().unwrap(); let document = window.document().unwrap();
let body = document.body().unwrap(); let body = document.body().unwrap();
let modal = document.create_element("dialog").unwrap().dyn_into::<HtmlDialogElement>().unwrap(); let modal = document
.create_element("dialog")
.unwrap()
.dyn_into::<HtmlDialogElement>()
.unwrap();
modal.set_text_content(Some("Import File:")); modal.set_text_content(Some("Import File:"));
let onclose_callback = { let onclose_callback = {
let modal = modal.clone(); let modal = modal.clone();
@ -111,8 +122,16 @@ pub fn MainWindow() -> Html {
modal.set_onclose(Some(&onclose_callback.as_ref().unchecked_ref())); modal.set_onclose(Some(&onclose_callback.as_ref().unchecked_ref()));
onclose_callback.forget(); onclose_callback.forget();
let form = document.create_element("form").unwrap().dyn_into::<HtmlFormElement>().unwrap(); let form = document
let input = document.create_element("input").unwrap().dyn_into::<HtmlInputElement>().unwrap(); .create_element("form")
.unwrap()
.dyn_into::<HtmlFormElement>()
.unwrap();
let input = document
.create_element("input")
.unwrap()
.dyn_into::<HtmlInputElement>()
.unwrap();
input.set_type("file"); input.set_type("file");
input.set_accept(".json"); input.set_accept(".json");
form.append_child(&input).unwrap(); form.append_child(&input).unwrap();
@ -121,33 +140,38 @@ pub fn MainWindow() -> Html {
let main_dispatch = main_dispatch.clone(); let main_dispatch = main_dispatch.clone();
let modal = modal.clone(); let modal = modal.clone();
Closure::<dyn FnMut(_)>::new(move |e: Event| { Closure::<dyn FnMut(_)>::new(move |e: Event| {
if let Some(input) = e.current_target() { if let Some(input) = e.current_target() {
let input = input.dyn_into::<HtmlInputElement>().expect("We know this is an input."); let input = input
if let Some(files) = input.files() { .dyn_into::<HtmlInputElement>()
if let Some(file) = files.get(0) { .expect("We know this is an input.");
let fr = web_sys::FileReader::new().unwrap(); if let Some(files) = input.files() {
fr.read_as_text(&file).unwrap(); if let Some(file) = files.get(0) {
let fr1 = fr.clone(); // Clone to avoid outliving closure let fr = web_sys::FileReader::new().unwrap();
let main_dispatch = main_dispatch.clone(); // Clone to satisfy FnMut fr.read_as_text(&file).unwrap();
// trait let fr1 = fr.clone(); // Clone to avoid outliving closure
let modal = modal.clone(); let main_dispatch = main_dispatch.clone(); // Clone to satisfy FnMut
let onload = Closure::<dyn FnMut(_)>::new(move |e: Event| { // trait
log::debug!("{:?}",&fr1.result()); let modal = modal.clone();
if let Some(value) = &fr1.result().ok().and_then(|v| v.as_string()) { let onload = Closure::<dyn FnMut(_)>::new(move |e: Event| {
let ms = serde_json::from_str::<MainState>(value); log::debug!("{:?}", &fr1.result());
match ms { if let Some(value) =
Ok(ms) => main_dispatch.set(ms), &fr1.result().ok().and_then(|v| v.as_string())
Err(e) => log::debug!("{:?}", e), {
}; let ms = serde_json::from_str::<MainState>(value);
modal.close(); match ms {
} Ok(ms) => main_dispatch.set(ms),
}); Err(e) => log::debug!("{:?}", e),
fr.set_onload(Some(&onload.as_ref().unchecked_ref())); };
onload.forget(); // Magic (don't touch) modal.close();
}
});
fr.set_onload(Some(&onload.as_ref().unchecked_ref()));
onload.forget(); // Magic (don't touch)
}
} }
} }
} })
})}; };
input.set_onchange(Some(&input_callback.as_ref().unchecked_ref())); input.set_onchange(Some(&input_callback.as_ref().unchecked_ref()));
input_callback.forget(); // Magic straight from the docs, don't touch :( input_callback.forget(); // Magic straight from the docs, don't touch :(
@ -187,15 +211,18 @@ pub fn MainWindow() -> Html {
} }
fn save_str(data: &str, name: &str) { fn save_str(data: &str, name: &str) {
let blob = Blob::new_with_str_sequence( let blob =
&Array::from_iter(std::iter::once(JsValue::from_str(data)))); Blob::new_with_str_sequence(&Array::from_iter(std::iter::once(JsValue::from_str(data))));
if let Ok(blob) = blob { if let Ok(blob) = blob {
let url = Url::create_object_url_with_blob(&blob).expect("We have a blob, why not URL?"); let url = Url::create_object_url_with_blob(&blob).expect("We have a blob, why not URL?");
// Beneath is the cool hack to download files // Beneath is the cool hack to download files
let window = web_sys::window().unwrap(); let window = web_sys::window().unwrap();
let document = window.document().unwrap(); let document = window.document().unwrap();
let anchor = document.create_element("a").unwrap() let anchor = document
.dyn_into::<HtmlAnchorElement>().unwrap(); .create_element("a")
.unwrap()
.dyn_into::<HtmlAnchorElement>()
.unwrap();
anchor.set_download(name); anchor.set_download(name);
anchor.set_href(&url); anchor.set_href(&url);
anchor.click(); anchor.click();

View File

@ -61,8 +61,11 @@ pub fn DestinationPlate(props: &DestinationPlateProps) -> Html {
let mut color_counter: u8 = 0; let mut color_counter: u8 = 0;
let color_map = { let color_map = {
let ts = main_state.transfers.iter().filter(|t| t.dest_id == props.destination_plate.get_uuid()); let ts = main_state
let mut color_map: HashMap<(u8,u8), u8> = HashMap::new(); .transfers
.iter()
.filter(|t| t.dest_id == props.destination_plate.get_uuid());
let mut color_map: HashMap<(u8, u8), u8> = HashMap::new();
for t in ts { for t in ts {
color_counter += 1; color_counter += 1;
let dws = t.transfer_region.get_destination_wells(); let dws = t.transfer_region.get_destination_wells();
@ -142,7 +145,7 @@ pub struct DestPlateCellProps {
pub selected: bool, pub selected: bool,
pub mouse: Callback<(u8, u8, MouseEventType)>, pub mouse: Callback<(u8, u8, MouseEventType)>,
pub in_transfer: Option<bool>, pub in_transfer: Option<bool>,
color: Option<(u8,u8)>, color: Option<(u8, u8)>,
} }
#[function_component] #[function_component]
@ -156,8 +159,8 @@ fn DestPlateCell(props: &DestPlateCellProps) -> Html {
_ => None, _ => None,
}; };
let color = match props.color { let color = match props.color {
Some(num) => PALETTE.get_u8(num.0,num.1), Some(num) => PALETTE.get_u8(num.0, num.1),
None => [255.0,255.0,255.0] None => [255.0, 255.0, 255.0],
}; };
let mouse = Callback::clone(&props.mouse); let mouse = Callback::clone(&props.mouse);
let mouse2 = Callback::clone(&props.mouse); let mouse2 = Callback::clone(&props.mouse);

View File

@ -41,8 +41,11 @@ pub fn SourcePlate(props: &SourcePlateProps) -> Html {
let mut color_counter: u8 = 0; let mut color_counter: u8 = 0;
let color_map = { let color_map = {
let ts = main_state.transfers.iter().filter(|t| t.source_id == props.source_plate.get_uuid()); let ts = main_state
let mut color_map: HashMap<(u8,u8), u8> = HashMap::new(); .transfers
.iter()
.filter(|t| t.source_id == props.source_plate.get_uuid());
let mut color_map: HashMap<(u8, u8), u8> = HashMap::new();
for t in ts { for t in ts {
color_counter += 1; color_counter += 1;
let sws = t.transfer_region.get_source_wells(); let sws = t.transfer_region.get_source_wells();
@ -139,7 +142,7 @@ pub struct SourcePlateCellProps {
selected: bool, selected: bool,
mouse: Callback<(u8, u8, MouseEventType)>, mouse: Callback<(u8, u8, MouseEventType)>,
in_transfer: Option<bool>, in_transfer: Option<bool>,
color: Option<(u8,u8)>, color: Option<(u8, u8)>,
} }
#[derive(Debug)] #[derive(Debug)]
pub enum MouseEventType { pub enum MouseEventType {
@ -158,8 +161,8 @@ fn SourcePlateCell(props: &SourcePlateCellProps) -> Html {
_ => None, _ => None,
}; };
let color = match props.color { let color = match props.color {
Some(num) => PALETTE.get_u8(num.0,num.1), Some(num) => PALETTE.get_u8(num.0, num.1),
None => [255.0,255.0,255.0] None => [255.0, 255.0, 255.0],
}; };
let mouse = Callback::clone(&props.mouse); let mouse = Callback::clone(&props.mouse);
let mouse2 = Callback::clone(&props.mouse); let mouse2 = Callback::clone(&props.mouse);
@ -174,7 +177,7 @@ fn SourcePlateCell(props: &SourcePlateCellProps) -> Html {
onmouseenter={move |_| { onmouseenter={move |_| {
mouse2.emit((i,j, MouseEventType::MOUSEENTER)) mouse2.emit((i,j, MouseEventType::MOUSEENTER))
}}> }}>
<div class="plate_cell_inner" <div class="plate_cell_inner"
style={format!("background: rgba({},{},{},1);", color[0], color[1], color[2])}/> style={format!("background: rgba({},{},{},1);", color[0], color[1], color[2])}/>
</td> </td>
} }

View File

@ -17,16 +17,16 @@ impl ColorPalette {
pub fn get(&self, t: f64) -> [f64; 3] { pub fn get(&self, t: f64) -> [f64; 3] {
[ [
(self.a[0] + self.b[0]*f64::cos(6.28318*(self.c[0]*t+self.d[0])))*255.0, (self.a[0] + self.b[0] * f64::cos(6.28318 * (self.c[0] * t + self.d[0]))) * 255.0,
(self.a[1] + self.b[1]*f64::cos(6.28318*(self.c[1]*t+self.d[1])))*255.0, (self.a[1] + self.b[1] * f64::cos(6.28318 * (self.c[1] * t + self.d[1]))) * 255.0,
(self.a[2] + self.b[2]*f64::cos(6.28318*(self.c[2]*t+self.d[2])))*255.0, (self.a[2] + self.b[2] * f64::cos(6.28318 * (self.c[2] * t + self.d[2]))) * 255.0,
] ]
} }
pub fn get_u8(&self, t: u8, n: u8) -> [f64; 3] { pub fn get_u8(&self, t: u8, n: u8) -> [f64; 3] {
assert!(t>0, "t must be greater than zero!"); assert!(t > 0, "t must be greater than zero!");
assert!(n>0, "There cannot be zero points!"); assert!(n > 0, "There cannot be zero points!");
self.get((t-1) as f64 / (n-1) as f64) self.get((t - 1) as f64 / (n - 1) as f64)
} }
} }
@ -36,15 +36,15 @@ pub struct Palettes;
#[allow(dead_code)] #[allow(dead_code)]
impl Palettes { impl Palettes {
pub const RAINBOW: ColorPalette = ColorPalette { pub const RAINBOW: ColorPalette = ColorPalette {
a: [0.500,0.500,0.500], a: [0.500, 0.500, 0.500],
b: [0.500,0.500,0.500], b: [0.500, 0.500, 0.500],
c: [0.800,0.800,0.800], c: [0.800, 0.800, 0.800],
d: [0.000,0.333,0.667], d: [0.000, 0.333, 0.667],
}; };
pub const YELLOW_PINK: ColorPalette = ColorPalette { pub const YELLOW_PINK: ColorPalette = ColorPalette {
a: [0.500,0.500,0.320], a: [0.500, 0.500, 0.320],
b: [0.500,0.500,0.500], b: [0.500, 0.500, 0.500],
c: [0.100,0.500,0.360], c: [0.100, 0.500, 0.360],
d: [0.000,0.000,0.650], d: [0.000, 0.000, 0.650],
}; };
} }

View File

@ -3,9 +3,9 @@
use lazy_static::lazy_static; use lazy_static::lazy_static;
use regex::Regex; use regex::Regex;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use uuid::Uuid;
use wasm_bindgen::JsCast; use wasm_bindgen::JsCast;
use web_sys::{EventTarget, HtmlInputElement}; use web_sys::{EventTarget, HtmlInputElement};
use uuid::Uuid;
use yew::prelude::*; use yew::prelude::*;
use yewdux::prelude::*; use yewdux::prelude::*;
@ -28,10 +28,9 @@ pub fn TransferMenu() -> Html {
ct_dispatch.reduce_mut(|state| { ct_dispatch.reduce_mut(|state| {
state.transfer.name = input.value().clone(); state.transfer.name = input.value().clone();
}); });
}
} }
})
)}; };
let on_src_region_change = { let on_src_region_change = {
let ct_dispatch = ct_dispatch.clone(); let ct_dispatch = ct_dispatch.clone();
@ -174,19 +173,24 @@ pub fn TransferMenu() -> Html {
spi.clone(), spi.clone(),
dpi.clone(), dpi.clone(),
ct_state.transfer.transfer_region, ct_state.transfer.transfer_region,
ct_state.transfer.name.clone() ct_state.transfer.name.clone(),
); );
main_dispatch.reduce_mut(|state| { main_dispatch.reduce_mut(|state| {
state.transfers.push(new_transfer); state.transfers.push(new_transfer);
state.selected_transfer = state.transfers.last() state.selected_transfer = state
.expect("An element should have just been added") .transfers
.get_uuid(); .last()
.expect("An element should have just been added")
.get_uuid();
}); });
} }
} }
} else { } else {
if let Some(index) = main_state.transfers.iter() if let Some(index) = main_state
.position(|t| t.get_uuid() == main_state.selected_transfer) { .transfers
.iter()
.position(|t| t.get_uuid() == main_state.selected_transfer)
{
main_dispatch.reduce_mut(|state| { main_dispatch.reduce_mut(|state| {
state.transfers[index] = ct_state.transfer.clone(); state.transfers[index] = ct_state.transfer.clone();
}); });
@ -205,8 +209,11 @@ pub fn TransferMenu() -> Html {
if main_state.selected_transfer.is_nil() { if main_state.selected_transfer.is_nil() {
() // Maybe reset transfer? () // Maybe reset transfer?
} else { } else {
if let Some(index) = main_state.transfers.iter() if let Some(index) = main_state
.position(|t| t.get_uuid() == ct_state.transfer.get_uuid()) { .transfers
.iter()
.position(|t| t.get_uuid() == ct_state.transfer.get_uuid())
{
main_dispatch.reduce_mut(|state| { main_dispatch.reduce_mut(|state| {
state.transfers.remove(index); state.transfers.remove(index);
state.selected_transfer = Uuid::nil(); state.selected_transfer = Uuid::nil();

View File

@ -104,9 +104,11 @@ pub fn Tree(props: &TreeProps) -> Html {
if let Some(li) = li { if let Some(li) = li {
if let Ok(id) = u128::from_str_radix(li.id().as_str(), 10) { if let Ok(id) = u128::from_str_radix(li.id().as_str(), 10) {
let id = Uuid::from_u128(id); let id = Uuid::from_u128(id);
if let Some(transfer) = main_state.transfers if let Some(transfer) = main_state
.iter().find(|transfer| transfer.get_uuid() == id) { .transfers
.iter()
.find(|transfer| transfer.get_uuid() == id)
{
main_dispatch.reduce_mut(|state| { main_dispatch.reduce_mut(|state| {
state.selected_source_plate = transfer.source_id; state.selected_source_plate = transfer.source_id;
state.selected_dest_plate = transfer.dest_id; state.selected_dest_plate = transfer.dest_id;
@ -237,11 +239,12 @@ fn PlateInfoModal(props: &PlateInfoModalProps) -> Html {
let id = props.id; let id = props.id;
Callback::from(move |e: Event| { Callback::from(move |e: Event| {
log::debug!("Changed name"); log::debug!("Changed name");
let input = e.target().expect("Event must have target") let input = e
.dyn_into::<HtmlInputElement>().unwrap(); .target()
main_dispatch.reduce_mut(|state| { .expect("Event must have target")
state.rename_plate(id, &input.value()) .dyn_into::<HtmlInputElement>()
}) .unwrap();
main_dispatch.reduce_mut(|state| state.rename_plate(id, &input.value()))
}) })
}; };

View File

@ -1,9 +1,9 @@
use crate::data::transfer::Transfer;
use crate::components::transfer_menu::num_to_letters;
use crate::components::states::MainState; use crate::components::states::MainState;
use crate::components::transfer_menu::num_to_letters;
use crate::data::transfer::Transfer;
use std::{error::Error};
use serde::Serialize; use serde::Serialize;
use std::error::Error;
#[derive(Serialize, Debug)] #[derive(Serialize, Debug)]
struct TransferRecord { struct TransferRecord {
@ -24,13 +24,23 @@ struct TransferRecord {
pub fn state_to_csv(state: &MainState) -> Result<String, Box<dyn Error>> { pub fn state_to_csv(state: &MainState) -> Result<String, Box<dyn Error>> {
let mut records: Vec<TransferRecord> = Vec::new(); let mut records: Vec<TransferRecord> = Vec::new();
for transfer in &state.transfers { for transfer in &state.transfers {
let src_barcode = state.source_plates.iter().find(|spi| spi.get_uuid() == transfer.source_id) let src_barcode = state
.ok_or("Found unpurged transfer")?; .source_plates
let dest_barcode = state.destination_plates.iter().find(|dpi| dpi.get_uuid() == transfer.dest_id) .iter()
.ok_or("Found unpurged transfer")?; .find(|spi| spi.get_uuid() == transfer.source_id)
records.append(&mut transfer_to_records(transfer, &src_barcode.name, &dest_barcode.name)) .ok_or("Found unpurged transfer")?;
let dest_barcode = state
.destination_plates
.iter()
.find(|dpi| dpi.get_uuid() == transfer.dest_id)
.ok_or("Found unpurged transfer")?;
records.append(&mut transfer_to_records(
transfer,
&src_barcode.name,
&dest_barcode.name,
))
} }
return records_to_csv(records) return records_to_csv(records);
} }
fn transfer_to_records( fn transfer_to_records(
@ -47,17 +57,18 @@ fn transfer_to_records(
let dest_wells = map(s_well); let dest_wells = map(s_well);
if let Some(dest_wells) = dest_wells { if let Some(dest_wells) = dest_wells {
for d_well in dest_wells { for d_well in dest_wells {
records.push(TransferRecord { records.push(TransferRecord {
source_plate: src_barcode.to_string(), source_plate: src_barcode.to_string(),
source_well: format!("{}{}", num_to_letters(s_well.0).unwrap(), s_well.1), source_well: format!("{}{}", num_to_letters(s_well.0).unwrap(), s_well.1),
destination_plate: dest_barcode.to_string(), destination_plate: dest_barcode.to_string(),
destination_well: format!("{}{}", num_to_letters(d_well.0).unwrap(), d_well.1), destination_well: format!("{}{}", num_to_letters(d_well.0).unwrap(), d_well.1),
volume: 2.5, // Default value since not yet implemented volume: 2.5, // Default value since not yet implemented
concentration: None }) concentration: None,
})
} }
} }
} }
return records return records;
} }
fn records_to_csv(trs: Vec<TransferRecord>) -> Result<String, Box<dyn Error>> { fn records_to_csv(trs: Vec<TransferRecord>) -> Result<String, Box<dyn Error>> {
@ -66,5 +77,5 @@ fn records_to_csv(trs: Vec<TransferRecord>) -> Result<String, Box<dyn Error>> {
wtr.serialize(record)? wtr.serialize(record)?
} }
let data = String::from_utf8(wtr.into_inner()?)?; let data = String::from_utf8(wtr.into_inner()?)?;
return Ok(data) return Ok(data);
} }

View File

@ -1,5 +1,5 @@
pub mod csv;
pub mod plate; pub mod plate;
pub mod plate_instances; pub mod plate_instances;
pub mod transfer; pub mod transfer;
pub mod transfer_region; pub mod transfer_region;
pub mod csv;

View File

@ -515,19 +515,48 @@ mod tests {
let transfer2 = TransferRegion { let transfer2 = TransferRegion {
source_plate: Plate::new(PlateType::Source, PlateFormat::W384), source_plate: Plate::new(PlateType::Source, PlateFormat::W384),
dest_plate: Plate::new(PlateType::Destination, PlateFormat::W384), dest_plate: Plate::new(PlateType::Destination, PlateFormat::W384),
source_region: Region::Rect((1,1), (2,3)), source_region: Region::Rect((1, 1), (2, 3)),
dest_region: Region::Rect((2,2), (11,16)), dest_region: Region::Rect((2, 2), (11, 16)),
interleave_source: (1, 1), interleave_source: (1, 1),
interleave_dest: (2, 2), interleave_dest: (2, 2),
}; };
let transfer2_source = transfer2.get_source_wells(); let transfer2_source = transfer2.get_source_wells();
let transfer2_dest = transfer2.get_destination_wells(); let transfer2_dest = transfer2.get_destination_wells();
assert_eq!(transfer2_source, vec![(1,1),(1,2),(1,3),(2,1),(2,2),(2,3)], "Failed type replicate 2 source"); assert_eq!(
assert_eq!(transfer2_dest, transfer2_source,
vec![(2,2),(2,8),(6,2),(6,8),(2,4),(2,10),(6,4),(6,10),(2,6),(2,12),(6,6),(6,12), vec![(1, 1), (1, 2), (1, 3), (2, 1), (2, 2), (2, 3)],
(4,2),(4,8),(8,2),(8,8),(4,4),(4,10),(8,4),(8,10),(4,6),(4,12),(8,6),(8,12)], "Failed type replicate 2 source"
"Failed type replicate 2 destination"); );
assert_eq!(
transfer2_dest,
vec![
(2, 2),
(2, 8),
(6, 2),
(6, 8),
(2, 4),
(2, 10),
(6, 4),
(6, 10),
(2, 6),
(2, 12),
(6, 6),
(6, 12),
(4, 2),
(4, 8),
(8, 2),
(8, 8),
(4, 4),
(4, 10),
(8, 4),
(8, 10),
(4, 6),
(4, 12),
(8, 6),
(8, 12)
],
"Failed type replicate 2 destination"
);
} }
#[test] #[test]
@ -536,8 +565,8 @@ mod tests {
let transfer1 = TransferRegion { let transfer1 = TransferRegion {
source_plate: Plate::new(PlateType::Source, PlateFormat::W384), source_plate: Plate::new(PlateType::Source, PlateFormat::W384),
dest_plate: Plate::new(PlateType::Destination, PlateFormat::W384), dest_plate: Plate::new(PlateType::Destination, PlateFormat::W384),
source_region: Region::Rect((1,4),(3,7)), source_region: Region::Rect((1, 4), (3, 7)),
dest_region: Region::Point((1,9)), dest_region: Region::Point((1, 9)),
interleave_source: (1, 1), interleave_source: (1, 1),
interleave_dest: (0, 2), interleave_dest: (0, 2),
}; };
@ -547,8 +576,20 @@ mod tests {
transfer1_dest.dedup(); // Makes our check easier, otherwise we have repeated wells transfer1_dest.dedup(); // Makes our check easier, otherwise we have repeated wells
let transfer1_map = transfer1.calculate_map(); let transfer1_map = transfer1.calculate_map();
// Skipping source check---it's just 12 wells. // Skipping source check---it's just 12 wells.
assert_eq!(transfer1_dest, vec![(1,9),(1,11),(1,13),(1,15)], "Failed type pool 1 dest"); assert_eq!(
assert_eq!(transfer1_map((2,6)), Some(vec![(1,13)]), "Failed type pool 1 map 1"); transfer1_dest,
assert_eq!(transfer1_map((3,7)), Some(vec![(1,15)]), "Failed type pool 1 map 2"); vec![(1, 9), (1, 11), (1, 13), (1, 15)],
"Failed type pool 1 dest"
);
assert_eq!(
transfer1_map((2, 6)),
Some(vec![(1, 13)]),
"Failed type pool 1 map 1"
);
assert_eq!(
transfer1_map((3, 7)),
Some(vec![(1, 15)]),
"Failed type pool 1 map 2"
);
} }
} }