Export to CSV
This commit is contained in:
parent
09d99e27a0
commit
52baa03d79
|
@ -77,6 +77,27 @@ dependencies = [
|
|||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "csv"
|
||||
version = "1.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "626ae34994d3d8d668f4269922248239db4ae42d538b14c398b74a52208e8086"
|
||||
dependencies = [
|
||||
"csv-core",
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "csv-core"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.13.4"
|
||||
|
@ -533,6 +554,8 @@ dependencies = [
|
|||
name = "plate-tool"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"csv",
|
||||
"js-sys",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"regex",
|
||||
|
|
|
@ -9,13 +9,17 @@ edition = "2021"
|
|||
yew = { version = "0.20.0", features = ["csr"] }
|
||||
yewdux = "0.9"
|
||||
wasm-bindgen = "0.2"
|
||||
web-sys = { version = "0.3", features = ["FormData", "HtmlFormElement", "HtmlDialogElement"] }
|
||||
web-sys = { version = "0.3", features = ["FormData", "HtmlFormElement",
|
||||
"HtmlDialogElement", "Blob", "Url", "Window",
|
||||
"HtmlAnchorElement"] }
|
||||
js-sys = "0.3"
|
||||
log = "0.4"
|
||||
wasm-logger = "0.2"
|
||||
regex = "1"
|
||||
lazy_static = "1.4"
|
||||
uuid = { version = "1.3", features = ["v4", "fast-rng", "macro-diagnostics", "js", "serde"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
csv = "1.2"
|
||||
|
||||
[dev-dependencies]
|
||||
wasm-bindgen-test = "0.3.0"
|
||||
|
|
|
@ -3,3 +3,4 @@
|
|||
@forward "plates";
|
||||
@forward "tree";
|
||||
@forward "transfer_menu";
|
||||
@forward "upper_menu";
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
div.main_container {
|
||||
height: 95vh;
|
||||
height: 97vh;
|
||||
width: 98vw;
|
||||
margin-top: 2.5vh;
|
||||
margin-left: 1vw;
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
@use "sass:color";
|
||||
@use "../variables" as *;
|
||||
|
||||
div.upper_menu {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
|
||||
$menu-height: min(2.5vh, 25px);
|
||||
height: $menu-height;
|
||||
padding-left: 1vw;
|
||||
|
||||
div.dropdown {
|
||||
margin-right: 2px;
|
||||
|
||||
position: relative;
|
||||
height: $menu-height;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
outline: 1px solid $color-dark;
|
||||
|
||||
button {
|
||||
vertical-align: top;
|
||||
border: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
cursor: pointer;
|
||||
font-size: calc($menu-height*0.7);
|
||||
}
|
||||
|
||||
* {
|
||||
visibility: hidden;
|
||||
}
|
||||
*:first-child {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
&:hover * {
|
||||
visibility: visible;
|
||||
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,9 @@
|
|||
#![allow(non_snake_case)]
|
||||
use yew::prelude::*;
|
||||
use yewdux::prelude::*;
|
||||
use wasm_bindgen::{JsValue, JsCast};
|
||||
use web_sys::{Blob, Url, HtmlAnchorElement};
|
||||
use js_sys::Array;
|
||||
|
||||
use super::new_plate_dialog::NewPlateDialog;
|
||||
use super::plates::plate_container::PlateContainer;
|
||||
|
@ -9,6 +12,7 @@ use super::transfer_menu::TransferMenu;
|
|||
use super::tree::Tree;
|
||||
|
||||
use crate::data::plate_instances::PlateInstance;
|
||||
use crate::data::csv::state_to_csv;
|
||||
|
||||
#[function_component]
|
||||
pub fn MainWindow() -> Html {
|
||||
|
@ -50,7 +54,36 @@ pub fn MainWindow() -> Html {
|
|||
})
|
||||
};
|
||||
|
||||
let save_button_callback = {
|
||||
let main_state = main_state.clone();
|
||||
Callback::from(move |_| {
|
||||
if let Ok(csv) = state_to_csv(&main_state) {
|
||||
let csv: &str = &csv;
|
||||
let blob = Blob::new_with_str_sequence(
|
||||
&Array::from_iter(std::iter::once(JsValue::from_str(csv))));
|
||||
if let Ok(blob) = blob {
|
||||
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
|
||||
let window = web_sys::window().unwrap();
|
||||
let document = window.document().unwrap();
|
||||
let anchor = document.create_element("a").unwrap()
|
||||
.dyn_into::<HtmlAnchorElement>().unwrap();
|
||||
anchor.set_download("transfers.csv");
|
||||
anchor.set_href(&url);
|
||||
anchor.click();
|
||||
}
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
html! {
|
||||
<>
|
||||
<div class="upper_menu">
|
||||
<div class="dropdown">
|
||||
<button>{"File"}</button>
|
||||
<button onclick={save_button_callback}>{"Save"}</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="main_container">
|
||||
<Tree open_new_plate_callback={open_new_plate_dialog_callback}/>
|
||||
<TransferMenu />
|
||||
|
@ -60,5 +93,6 @@ pub fn MainWindow() -> Html {
|
|||
<NewPlateDialog close_callback={new_plate_dialog_callback}/>
|
||||
}
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
}
|
||||
|
|
|
@ -365,7 +365,7 @@ fn letters_to_num(letters: &str) -> Option<u8> {
|
|||
}
|
||||
return Some(num);
|
||||
}
|
||||
fn num_to_letters(num: u8) -> Option<String> {
|
||||
pub fn num_to_letters(num: u8) -> Option<String> {
|
||||
if num == 0 {
|
||||
return None;
|
||||
} // Otherwise, we will not return none!
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
use crate::data::transfer::Transfer;
|
||||
use crate::components::transfer_menu::num_to_letters;
|
||||
use crate::components::states::MainState;
|
||||
|
||||
use std::{error::Error};
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Serialize, Debug)]
|
||||
struct TransferRecord {
|
||||
#[serde(rename = "Source Plate Barcode")]
|
||||
source_plate: String,
|
||||
#[serde(rename = "Source Well")]
|
||||
source_well: String,
|
||||
#[serde(rename = "Destination Plate Barcode")]
|
||||
destination_plate: String,
|
||||
#[serde(rename = "Destination Well")]
|
||||
destination_well: String,
|
||||
#[serde(rename = "Volume")]
|
||||
volume: f64,
|
||||
#[serde(rename = "Concentration")]
|
||||
concentration: Option<f64>,
|
||||
}
|
||||
|
||||
pub fn state_to_csv(state: &MainState) -> Result<String, Box<dyn Error>> {
|
||||
let mut records: Vec<TransferRecord> = Vec::new();
|
||||
for transfer in &state.transfers {
|
||||
let src_barcode = state.source_plates.iter().find(|spi| spi.get_uuid() == transfer.source_id)
|
||||
.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)
|
||||
}
|
||||
|
||||
fn transfer_to_records(
|
||||
tr: &Transfer,
|
||||
src_barcode: &str,
|
||||
dest_barcode: &str,
|
||||
) -> Vec<TransferRecord> {
|
||||
let source_wells = tr.transfer_region.get_source_wells();
|
||||
let map = tr.transfer_region.calculate_map();
|
||||
|
||||
let mut records: Vec<TransferRecord> = vec![];
|
||||
|
||||
for s_well in source_wells {
|
||||
let dest_wells = map(s_well);
|
||||
if let Some(dest_wells) = dest_wells {
|
||||
for d_well in dest_wells {
|
||||
records.push(TransferRecord {
|
||||
source_plate: src_barcode.to_string(),
|
||||
source_well: format!("{}{}", num_to_letters(s_well.0).unwrap(), s_well.1),
|
||||
destination_plate: dest_barcode.to_string(),
|
||||
destination_well: format!("{}{}", num_to_letters(d_well.0).unwrap(), d_well.1),
|
||||
volume: 2.5, // Default value since not yet implemented
|
||||
concentration: None })
|
||||
}
|
||||
}
|
||||
}
|
||||
return records
|
||||
}
|
||||
|
||||
fn records_to_csv(trs: Vec<TransferRecord>) -> Result<String, Box<dyn Error>> {
|
||||
let mut wtr = csv::WriterBuilder::new().from_writer(vec![]);
|
||||
for record in trs {
|
||||
wtr.serialize(record)?
|
||||
}
|
||||
let data = String::from_utf8(wtr.into_inner()?)?;
|
||||
return Ok(data)
|
||||
}
|
|
@ -2,3 +2,4 @@ pub mod plate;
|
|||
pub mod plate_instances;
|
||||
pub mod transfer;
|
||||
pub mod transfer_region;
|
||||
pub mod csv;
|
||||
|
|
Loading…
Reference in New Issue