diff --git a/src/components/callbacks/main_window_callbacks.rs b/src/components/callbacks/main_window_callbacks.rs index b0cb57e..268407b 100644 --- a/src/components/callbacks/main_window_callbacks.rs +++ b/src/components/callbacks/main_window_callbacks.rs @@ -1,13 +1,24 @@ -use js_sys::Array; -use wasm_bindgen::{prelude::*, JsCast, JsValue}; -use web_sys::{Blob, HtmlAnchorElement, HtmlDialogElement, HtmlFormElement, HtmlInputElement, Url}; +#![allow(non_snake_case)] +use std::collections::HashSet; +use js_sys::Array; +use lazy_static::lazy_static; +use regex::Regex; +use wasm_bindgen::{prelude::*, JsCast, JsValue}; +use web_sys::{ + Blob, FileReader, HtmlAnchorElement, HtmlButtonElement, HtmlDialogElement, HtmlFormElement, + HtmlInputElement, HtmlOptionElement, HtmlSelectElement, Url, +}; use yew::prelude::*; use yewdux::prelude::*; use crate::components::states::{CurrentTransfer, MainState}; +use crate::components::transfer_menu::letters_to_num; -use crate::data::csv::state_to_csv; +use crate::data::transfer::Transfer; +use crate::data::transfer_region::{Region, TransferRegion}; + +use crate::data::csv::{state_to_csv, TransferRecord}; type NoParamsCallback = Box ()>; @@ -187,3 +198,236 @@ pub fn import_json_button_callback(main_dispatch: Dispatch) -> Callba modal.show_modal().unwrap(); }) } + +pub fn import_transfer_csv_submit_callback( + main_dispatch: Dispatch, + from_source: HtmlSelectElement, + to_source: HtmlSelectElement, + from_dest: HtmlSelectElement, + to_dest: HtmlSelectElement, + records: Vec, +) -> Closure { + Closure::::new(move |_: Event| { + let from_source = from_source.value(); + let to_source = to_source.value(); + let from_dest = from_dest.value(); + let to_dest = to_dest.value(); + + lazy_static! { + static ref REGEX: Regex = Regex::new(r"([A-Z]+)(\d+)").unwrap(); + } + let records: Vec<((u8, u8), (u8, u8))> = records + .iter() + .filter(|record| record.source_plate == from_source) + .filter(|record| record.destination_plate == from_dest) + .map(|record| { + let c1 = REGEX.captures(&record.source_well).unwrap(); + let c2 = REGEX.captures(&record.destination_well).unwrap(); + log::debug!("{} {}", &record.source_well, &record.destination_well); + log::debug!("{},{} {},{}", &c1[1], &c1[2], &c2[1], &c2[2]); + + ( + ( + letters_to_num(&c1[1]).unwrap(), + c1[2].parse::().unwrap(), + ), + ( + letters_to_num(&c2[1]).unwrap(), + c2[2].parse::().unwrap(), + ), + ) + }) + .collect(); + + let spi = main_dispatch + .get() + .source_plates + .iter() + .find(|src| src.name == to_source) + .unwrap() + .clone(); + let dpi = main_dispatch + .get() + .destination_plates + .iter() + .find(|dest| dest.name == to_dest) + .unwrap() + .clone(); + + let custom_region = Region::new_custom(&records); + let transfer_region = TransferRegion { + source_region: custom_region.clone(), + dest_region: custom_region, + interleave_source: (1, 1), + interleave_dest: (1, 1), + source_plate: spi.plate, + dest_plate: dpi.plate, + }; + + let transfer = Transfer::new(spi, dpi, transfer_region, "Custom Transfer".to_string()); + main_dispatch.reduce_mut(|state| { + state.transfers.push(transfer); + state.selected_transfer = state + .transfers + .last() + .expect("An element should have just been added") + .get_uuid(); + }); + }) +} + +pub fn import_transfer_csv_onload_callback( + main_dispatch: Dispatch, + file_reader: FileReader, + modal: HtmlDialogElement, +) -> Closure { + Closure::::new(move |_: Event| { + if let Some(value) = &file_reader.result().ok().and_then(|v| v.as_string()) { + let mut rdr = csv::Reader::from_reader(value.as_bytes()); + let mut records = Vec::new(); + for record in rdr.deserialize::() { + match record { + Ok(r) => { + //log::debug!("{:?}", r); + records.push(r); + } + Err(e) => { + log::debug!("{:?}", e); + } + } + } + + let mut sources: HashSet = HashSet::new(); + let mut destinations: HashSet = HashSet::new(); + for record in records.iter() { + sources.insert(record.source_plate.clone()); + destinations.insert(record.destination_plate.clone()); + } + + let window = web_sys::window().unwrap(); + let document = window.document().unwrap(); + let form = document + .create_element("form") + .unwrap() + .dyn_into::() + .unwrap(); + let from_source = document + .create_element("select") + .unwrap() + .dyn_into::() + .unwrap(); + for source in sources { + let option = document + .create_element("option") + .unwrap() + .dyn_into::() + .unwrap(); + option.set_value(&source); + option.set_text(&source); + from_source.append_child(&option).unwrap(); + } + let to_source = document + .create_element("select") + .unwrap() + .dyn_into::() + .unwrap(); + for source in &main_dispatch.get().source_plates { + let option = document + .create_element("option") + .unwrap() + .dyn_into::() + .unwrap(); + option.set_value(&source.name); + option.set_text(&source.name); + to_source.append_child(&option).unwrap(); + } + let from_dest = document + .create_element("select") + .unwrap() + .dyn_into::() + .unwrap(); + for dest in destinations { + let option = document + .create_element("option") + .unwrap() + .dyn_into::() + .unwrap(); + option.set_value(&dest); + option.set_text(&dest); + from_dest.append_child(&option).unwrap(); + } + let to_dest = document + .create_element("select") + .unwrap() + .dyn_into::() + .unwrap(); + for dest in &main_dispatch.get().destination_plates { + let option = document + .create_element("option") + .unwrap() + .dyn_into::() + .unwrap(); + option.set_value(&dest.name); + option.set_text(&dest.name); + to_dest.append_child(&option).unwrap(); + } + let submit = document + .create_element("button") + .unwrap() + .dyn_into::() + .unwrap(); + submit.set_value("Submit"); + submit.set_inner_text("Submit"); + let submit_callback = { + let main_dispatch = main_dispatch.clone(); + let from_source = from_source.clone(); + let to_source = to_source.clone(); + let from_dest = from_dest.clone(); + let to_dest = to_dest.clone(); + import_transfer_csv_submit_callback( + main_dispatch, + from_source, + to_source, + from_dest, + to_dest, + records, + ) + }; + submit.set_onclick(Some(submit_callback.as_ref().unchecked_ref())); + submit_callback.forget(); + + form.append_child(&from_source).unwrap(); + form.append_child(&to_source).unwrap(); + form.append_child(&from_dest).unwrap(); + form.append_child(&to_dest).unwrap(); + modal.append_child(&submit).unwrap(); + modal.append_child(&form).unwrap(); + } + }) +} + +pub fn import_transfer_csv_input_callback( + main_dispatch: Dispatch, + modal: HtmlDialogElement, +) -> Closure { + Closure::::new(move |e: Event| { + if let Some(input) = e.current_target() { + let input = input + .dyn_into::() + .expect("We know this is an input."); + if let Some(files) = input.files() { + if let Some(file) = files.get(0) { + let fr = web_sys::FileReader::new().unwrap(); + fr.read_as_text(&file).unwrap(); + let fr1 = fr.clone(); // Clone to avoid outliving closure + let main_dispatch = main_dispatch.clone(); // Clone to satisfy FnMut + // trait + let modal = modal.clone(); + let onload = import_transfer_csv_onload_callback(main_dispatch, fr1, modal); + fr.set_onload(Some(onload.as_ref().unchecked_ref())); + onload.forget(); // Magic (don't touch) + } + } + } + }) +} diff --git a/src/components/main_window.rs b/src/components/main_window.rs index f042f51..e72f035 100644 --- a/src/components/main_window.rs +++ b/src/components/main_window.rs @@ -1,26 +1,18 @@ #![allow(non_snake_case)] -use std::collections::HashSet; use js_sys::Array; -use lazy_static::lazy_static; -use regex::Regex; use wasm_bindgen::{prelude::*, JsCast, JsValue}; -use web_sys::{ - Blob, HtmlAnchorElement, HtmlButtonElement, HtmlDialogElement, HtmlFormElement, - HtmlInputElement, HtmlOptionElement, HtmlSelectElement, Url, -}; +use web_sys::{Blob, HtmlAnchorElement, HtmlDialogElement, HtmlFormElement, HtmlInputElement, Url}; use yew::prelude::*; use yewdux::prelude::*; use crate::components::new_plate_dialog::NewPlateDialog; use crate::components::plates::plate_container::PlateContainer; use crate::components::states::{CurrentTransfer, MainState}; -use crate::components::transfer_menu::{letters_to_num, RegionDisplay, TransferMenu}; +use crate::components::transfer_menu::TransferMenu; use crate::components::tree::Tree; use crate::data::plate_instances::PlateInstance; -use crate::data::transfer::Transfer; -use crate::data::transfer_region::{Region, TransferRegion}; use crate::components::callbacks::main_window_callbacks; @@ -117,237 +109,7 @@ pub fn MainWindow() -> Html { let input_callback = { let main_dispatch = main_dispatch.clone(); let modal = modal.clone(); - Closure::::new(move |e: Event| { - if let Some(input) = e.current_target() { - let input = input - .dyn_into::() - .expect("We know this is an input."); - if let Some(files) = input.files() { - if let Some(file) = files.get(0) { - let fr = web_sys::FileReader::new().unwrap(); - fr.read_as_text(&file).unwrap(); - let fr1 = fr.clone(); // Clone to avoid outliving closure - let main_dispatch = main_dispatch.clone(); // Clone to satisfy FnMut - // trait - let modal = modal.clone(); - let onload = Closure::::new(move |_: Event| { - if let Some(value) = - &fr1.result().ok().and_then(|v| v.as_string()) - { - let mut rdr = csv::Reader::from_reader(value.as_bytes()); - let mut records = Vec::new(); - for record in - rdr.deserialize::() - { - match record { - Ok(r) => { - //log::debug!("{:?}", r); - records.push(r); - } - Err(e) => { - log::debug!("{:?}", e); - } - } - } - - let mut sources: HashSet = HashSet::new(); - let mut destinations: HashSet = HashSet::new(); - for record in records.iter() { - sources.insert(record.source_plate.clone()); - destinations.insert(record.destination_plate.clone()); - } - - let window = web_sys::window().unwrap(); - let document = window.document().unwrap(); - let form = document - .create_element("form") - .unwrap() - .dyn_into::() - .unwrap(); - let from_source = document - .create_element("select") - .unwrap() - .dyn_into::() - .unwrap(); - for source in sources { - let option = document - .create_element("option") - .unwrap() - .dyn_into::() - .unwrap(); - option.set_value(&source); - option.set_text(&source); - from_source.append_child(&option).unwrap(); - } - let to_source = document - .create_element("select") - .unwrap() - .dyn_into::() - .unwrap(); - for source in &main_dispatch.get().source_plates { - let option = document - .create_element("option") - .unwrap() - .dyn_into::() - .unwrap(); - option.set_value(&source.name); - option.set_text(&source.name); - to_source.append_child(&option).unwrap(); - } - let from_dest = document - .create_element("select") - .unwrap() - .dyn_into::() - .unwrap(); - for dest in destinations { - let option = document - .create_element("option") - .unwrap() - .dyn_into::() - .unwrap(); - option.set_value(&dest); - option.set_text(&dest); - from_dest.append_child(&option).unwrap(); - } - let to_dest = document - .create_element("select") - .unwrap() - .dyn_into::() - .unwrap(); - for dest in &main_dispatch.get().destination_plates { - let option = document - .create_element("option") - .unwrap() - .dyn_into::() - .unwrap(); - option.set_value(&dest.name); - option.set_text(&dest.name); - to_dest.append_child(&option).unwrap(); - } - let submit = document - .create_element("button") - .unwrap() - .dyn_into::() - .unwrap(); - submit.set_value("Submit"); - submit.set_inner_text("Submit"); - let submit_callback = { - let main_dispatch = main_dispatch.clone(); - let from_source = from_source.clone(); - let to_source = to_source.clone(); - let from_dest = from_dest.clone(); - let to_dest = to_dest.clone(); - Closure::::new(move |_: Event| { - let from_source = from_source.value(); - let to_source = to_source.value(); - let from_dest = from_dest.value(); - let to_dest = to_dest.value(); - - lazy_static! { - static ref REGEX: Regex = - Regex::new(r"([A-Z]+)(\d+)").unwrap(); - } - let records: Vec<((u8, u8), (u8, u8))> = records - .iter() - .filter(|record| { - record.source_plate == from_source - }) - .filter(|record| { - record.destination_plate == from_dest - }) - .map(|record| { - let c1 = REGEX - .captures(&record.source_well) - .unwrap(); - let c2 = REGEX - .captures(&record.destination_well) - .unwrap(); - log::debug!( - "{} {}", - &record.source_well, - &record.destination_well - ); - log::debug!( - "{},{} {},{}", - &c1[1], - &c1[2], - &c2[1], - &c2[2] - ); - - ( - ( - letters_to_num(&c1[1]).unwrap(), - c1[2].parse::().unwrap(), - ), - ( - letters_to_num(&c2[1]).unwrap(), - c2[2].parse::().unwrap(), - ), - ) - }) - .collect(); - - let spi = main_dispatch - .get() - .source_plates - .iter() - .find(|src| src.name == to_source) - .unwrap() - .clone(); - let dpi = main_dispatch - .get() - .destination_plates - .iter() - .find(|dest| dest.name == to_dest) - .unwrap() - .clone(); - - let custom_region = Region::new_custom(&records); - let transfer_region = TransferRegion { - source_region: custom_region.clone(), - dest_region: custom_region, - interleave_source: (1, 1), - interleave_dest: (1, 1), - source_plate: spi.plate, - dest_plate: dpi.plate, - }; - - let transfer = Transfer::new( - spi, - dpi, - transfer_region, - "Custom Transfer".to_string(), - ); - main_dispatch.reduce_mut(|state| { - state.transfers.push(transfer); - state.selected_transfer = state - .transfers - .last() - .expect("An element should have just been added") - .get_uuid(); - }); - }) - }; - submit.set_onclick(Some( - submit_callback.as_ref().unchecked_ref(), - )); - submit_callback.forget(); - - form.append_child(&from_source).unwrap(); - form.append_child(&to_source).unwrap(); - form.append_child(&from_dest).unwrap(); - form.append_child(&to_dest).unwrap(); - modal.append_child(&submit).unwrap(); - modal.append_child(&form).unwrap(); - } - }); - fr.set_onload(Some(onload.as_ref().unchecked_ref())); - onload.forget(); // Magic (don't touch) - } - } - } - }) + main_window_callbacks::import_transfer_csv_input_callback(main_dispatch, modal) }; input.set_onchange(Some(input_callback.as_ref().unchecked_ref())); input_callback.forget(); // Magic straight from the docs, don't touch :(