use std::collections::HashSet; use lazy_static::lazy_static; use regex::Regex; use wasm_bindgen::{prelude::*, JsCast}; use web_sys::{ FileReader, HtmlButtonElement, HtmlDialogElement, HtmlFormElement, HtmlInputElement, HtmlOptionElement, HtmlSelectElement, }; use yew::prelude::*; use yewdux::prelude::*; use crate::components::states::MainState; use plate_tool_lib::util::letters_to_num; use plate_tool_lib::transfer::Transfer; use plate_tool_lib::transfer_region::{Region, TransferRegion}; use plate_tool_lib::csv::TransferRecord; use super::main_window_callbacks::create_close_button; 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) } } } }) } pub fn import_transfer_csv_callback(main_dispatch: Dispatch) -> Callback { Callback::from(move |_| { let window = web_sys::window().unwrap(); let document = window.document().unwrap(); let body = document.body().unwrap(); let modal = document .create_element("dialog") .unwrap() .dyn_into::() .unwrap(); modal.set_text_content(Some("Import File:")); let onclose_callback = { let modal = modal.clone(); Closure::::new(move |_: Event| { modal.remove(); }) }; modal.set_onclose(Some(onclose_callback.as_ref().unchecked_ref())); let close_button = create_close_button(&onclose_callback); onclose_callback.forget(); modal.append_child(&close_button).unwrap(); let form = document .create_element("form") .unwrap() .dyn_into::() .unwrap(); let input = document .create_element("input") .unwrap() .dyn_into::() .unwrap(); input.set_type("file"); input.set_accept(".csv"); form.append_child(&input).unwrap(); let input_callback = { let main_dispatch = main_dispatch.clone(); let modal = modal.clone(); 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 :( modal.append_child(&form).unwrap(); body.append_child(&modal).unwrap(); modal.show_modal().unwrap(); }) } 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 (header, data) = value.split_at(value.find('\n').unwrap()); let modified: String = header.to_lowercase() + data; log::info!("{}", modified); 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_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,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(); }); }) }