Compare commits

..

No commits in common. "06258548951ea1cf3fbda33d20286865779c1a96" and "de4249944405287cb0ee33a307794acac835cacb" have entirely different histories.

4 changed files with 288 additions and 324 deletions

View File

@ -1,312 +0,0 @@
use std::collections::HashSet;
use std::str::FromStr;
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, HtmlElement,
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::transfer::Transfer;
use crate::data::transfer_region::{Region, TransferRegion};
use crate::data::csv::{state_to_csv, TransferRecord};
use super::main_window_callbacks::create_close_button;
type NoParamsCallback = Box<dyn Fn(()) -> ()>;
pub fn import_transfer_csv_input_callback(
main_dispatch: Dispatch<MainState>,
modal: HtmlDialogElement,
) -> Closure<dyn FnMut(Event)> {
Closure::<dyn FnMut(_)>::new(move |e: Event| {
if let Some(input) = e.current_target() {
let input = input
.dyn_into::<HtmlInputElement>()
.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<MainState>) -> Callback<MouseEvent> {
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::<HtmlDialogElement>()
.unwrap();
modal.set_text_content(Some("Import File:"));
let onclose_callback = {
let modal = modal.clone();
Closure::<dyn FnMut(_)>::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::<HtmlFormElement>()
.unwrap();
let input = document
.create_element("input")
.unwrap()
.dyn_into::<HtmlInputElement>()
.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<MainState>,
file_reader: FileReader,
modal: HtmlDialogElement,
) -> Closure<dyn FnMut(Event)> {
Closure::<dyn FnMut(_)>::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::<crate::data::csv::TransferRecord>() {
match record {
Ok(r) => {
//log::debug!("{:?}", r);
records.push(r);
}
Err(e) => {
log::debug!("{:?}", e);
}
}
}
let mut sources: HashSet<String> = HashSet::new();
let mut destinations: HashSet<String> = 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::<HtmlFormElement>()
.unwrap();
let from_source = document
.create_element("select")
.unwrap()
.dyn_into::<HtmlSelectElement>()
.unwrap();
for source in sources {
let option = document
.create_element("option")
.unwrap()
.dyn_into::<HtmlOptionElement>()
.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::<HtmlSelectElement>()
.unwrap();
for source in &main_dispatch.get().source_plates {
let option = document
.create_element("option")
.unwrap()
.dyn_into::<HtmlOptionElement>()
.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::<HtmlSelectElement>()
.unwrap();
for dest in destinations {
let option = document
.create_element("option")
.unwrap()
.dyn_into::<HtmlOptionElement>()
.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::<HtmlSelectElement>()
.unwrap();
for dest in &main_dispatch.get().destination_plates {
let option = document
.create_element("option")
.unwrap()
.dyn_into::<HtmlOptionElement>()
.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::<HtmlButtonElement>()
.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<MainState>,
from_source: HtmlSelectElement,
to_source: HtmlSelectElement,
from_dest: HtmlSelectElement,
to_dest: HtmlSelectElement,
records: Vec<TransferRecord>,
) -> Closure<dyn FnMut(Event)> {
Closure::<dyn FnMut(_)>::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::<u8>().unwrap(),
),
(
letters_to_num(&c2[1]).unwrap(),
c2[2].parse::<u8>().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();
});
})
}

View File

@ -22,7 +22,7 @@ use crate::data::csv::{state_to_csv, TransferRecord};
type NoParamsCallback = Box<dyn Fn(()) -> ()>;
pub fn create_close_button(close: &Closure<dyn FnMut(Event)>) -> HtmlElement {
fn create_close_button(close: &Closure<dyn FnMut(Event)>) -> HtmlElement {
let document = web_sys::window().unwrap().document().unwrap();
let close_button = document
.create_element("button")
@ -216,10 +216,287 @@ pub fn import_json_button_callback(main_dispatch: Dispatch<MainState>) -> Callba
})
}
pub use super::import_csv_callbacks::import_transfer_csv_callback;
pub fn import_transfer_csv_submit_callback(
main_dispatch: Dispatch<MainState>,
from_source: HtmlSelectElement,
to_source: HtmlSelectElement,
from_dest: HtmlSelectElement,
to_dest: HtmlSelectElement,
records: Vec<TransferRecord>,
) -> Closure<dyn FnMut(Event)> {
Closure::<dyn FnMut(_)>::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();
pub use super::import_csv_callbacks::import_transfer_csv_submit_callback;
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]);
pub use super::import_csv_callbacks::import_transfer_csv_onload_callback;
(
(
letters_to_num(&c1[1]).unwrap(),
c1[2].parse::<u8>().unwrap(),
),
(
letters_to_num(&c2[1]).unwrap(),
c2[2].parse::<u8>().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<MainState>,
file_reader: FileReader,
modal: HtmlDialogElement,
) -> Closure<dyn FnMut(Event)> {
Closure::<dyn FnMut(_)>::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::<crate::data::csv::TransferRecord>() {
match record {
Ok(r) => {
//log::debug!("{:?}", r);
records.push(r);
}
Err(e) => {
log::debug!("{:?}", e);
}
}
}
let mut sources: HashSet<String> = HashSet::new();
let mut destinations: HashSet<String> = 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::<HtmlFormElement>()
.unwrap();
let from_source = document
.create_element("select")
.unwrap()
.dyn_into::<HtmlSelectElement>()
.unwrap();
for source in sources {
let option = document
.create_element("option")
.unwrap()
.dyn_into::<HtmlOptionElement>()
.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::<HtmlSelectElement>()
.unwrap();
for source in &main_dispatch.get().source_plates {
let option = document
.create_element("option")
.unwrap()
.dyn_into::<HtmlOptionElement>()
.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::<HtmlSelectElement>()
.unwrap();
for dest in destinations {
let option = document
.create_element("option")
.unwrap()
.dyn_into::<HtmlOptionElement>()
.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::<HtmlSelectElement>()
.unwrap();
for dest in &main_dispatch.get().destination_plates {
let option = document
.create_element("option")
.unwrap()
.dyn_into::<HtmlOptionElement>()
.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::<HtmlButtonElement>()
.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<MainState>,
modal: HtmlDialogElement,
) -> Closure<dyn FnMut(Event)> {
Closure::<dyn FnMut(_)>::new(move |e: Event| {
if let Some(input) = e.current_target() {
let input = input
.dyn_into::<HtmlInputElement>()
.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<MainState>) -> Callback<MouseEvent> {
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::<HtmlDialogElement>()
.unwrap();
modal.set_text_content(Some("Import File:"));
let onclose_callback = {
let modal = modal.clone();
Closure::<dyn FnMut(_)>::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::<HtmlFormElement>()
.unwrap();
let input = document
.create_element("input")
.unwrap()
.dyn_into::<HtmlInputElement>()
.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 use super::import_csv_callbacks::import_transfer_csv_input_callback;

View File

@ -2,4 +2,3 @@ pub mod main_window_callbacks;
pub mod new_plate_dialog_callbacks;
pub mod transfer_menu_callbacks;
pub mod tree_callbacks;
mod import_csv_callbacks;

View File

@ -7,17 +7,17 @@ use std::error::Error;
#[derive(Serialize, Deserialize, Debug)]
pub struct TransferRecord {
#[serde(rename = "Source Plate", alias = "source plate", alias = "src plate")]
#[serde(rename = "Source Plate")]
pub source_plate: String,
#[serde(rename = "Source Well", alias = "source well", alias = "src well")]
#[serde(rename = "Source Well")]
pub source_well: String,
#[serde(rename = "Dest Plate", alias = "dest plate", alias = "destination plate")]
#[serde(rename = "Dest Plate")]
pub destination_plate: String,
#[serde(rename = "Destination Well", alias = "destination well", alias = "dest well")]
#[serde(rename = "Destination Well")]
pub destination_well: String,
#[serde(rename = "Transfer Volume", alias = "transfer volume")]
#[serde(rename = "Transfer Volume")]
pub volume: f32,
#[serde(rename = "Concentration", alias = "concentration")]
#[serde(rename = "Concentration")]
pub concentration: Option<f32>,
}