parent
							
								
									658ab3082a
								
							
						
					
					
						commit
						85670fe86e
					
				| 
						 | 
					@ -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();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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],
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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()))
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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(
 | 
				
			||||||
| 
						 | 
					@ -53,11 +63,12 @@ fn transfer_to_records(
 | 
				
			||||||
                    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);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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;
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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"
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue