Compare commits
No commits in common. "ce6e88a347ef819b2ca329af65ff7c5b44c43345" and "7defd8ff08aed1552ee2754fbbeef421d58f6ba9" have entirely different histories.
ce6e88a347
...
7defd8ff08
|
@ -2,10 +2,9 @@ use serde::Serialize;
|
||||||
|
|
||||||
use super::TransferRecord;
|
use super::TransferRecord;
|
||||||
|
|
||||||
/// Format preferred by the Echo Client when using the Pick-List function.
|
// Format preferred by the Echo Client when using the Pick-List function.
|
||||||
///
|
// Note that this does not export plate info!
|
||||||
/// Note that this does not export plate info!
|
// Exports of this type should combine all transfers between the two actively selected plates.
|
||||||
/// Exports of this type should combine all transfers between the two actively selected plates.
|
|
||||||
#[derive(Serialize, Debug)]
|
#[derive(Serialize, Debug)]
|
||||||
pub struct EchoClientTransferRecord {
|
pub struct EchoClientTransferRecord {
|
||||||
#[serde(rename = "SrcWell")]
|
#[serde(rename = "SrcWell")]
|
||||||
|
|
|
@ -1,8 +1,3 @@
|
||||||
//! Auto module for importing picklists without creating plates first
|
|
||||||
//!
|
|
||||||
//! Before the auto module, it was necessary for a user to create the plates
|
|
||||||
//! used in an imported transfer before import.
|
|
||||||
|
|
||||||
use super::{string_well_to_pt, TransferRecord, read_csv};
|
use super::{string_well_to_pt, TransferRecord, read_csv};
|
||||||
use crate::plate::{PlateFormat, PlateType};
|
use crate::plate::{PlateFormat, PlateType};
|
||||||
use crate::plate_instances::PlateInstance;
|
use crate::plate_instances::PlateInstance;
|
||||||
|
@ -17,7 +12,6 @@ pub struct AutoOutput {
|
||||||
pub transfers: Vec<Transfer>,
|
pub transfers: Vec<Transfer>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Two lists of plates that are guaranteed to be unique (no duplicate plates)
|
|
||||||
struct UniquePlates {
|
struct UniquePlates {
|
||||||
sources: Vec<PlateInstance>,
|
sources: Vec<PlateInstance>,
|
||||||
destinations: Vec<PlateInstance>,
|
destinations: Vec<PlateInstance>,
|
||||||
|
@ -30,12 +24,6 @@ const W384_ROW_MAX: u8 = 16;
|
||||||
const W1536_COL_MAX: u8 = 48;
|
const W1536_COL_MAX: u8 = 48;
|
||||||
const W1536_ROW_MAX: u8 = 32;
|
const W1536_ROW_MAX: u8 = 32;
|
||||||
|
|
||||||
/// Main function for the auto module
|
|
||||||
/// Takes a list of pre-deserialized transfer records and generates output
|
|
||||||
/// This output is 3 lists of:
|
|
||||||
/// 1. The source plates used in the picklist
|
|
||||||
/// 2. The destination plates used in the picklist
|
|
||||||
/// 3. All of the transfers between those plates
|
|
||||||
pub fn auto(records: &[TransferRecord]) -> AutoOutput {
|
pub fn auto(records: &[TransferRecord]) -> AutoOutput {
|
||||||
let unique_plates = find_unique_plates(records);
|
let unique_plates = find_unique_plates(records);
|
||||||
let transfers = get_transfer_for_all_pairs(records, &unique_plates);
|
let transfers = get_transfer_for_all_pairs(records, &unique_plates);
|
||||||
|
@ -43,18 +31,11 @@ pub fn auto(records: &[TransferRecord]) -> AutoOutput {
|
||||||
AutoOutput { sources: unique_plates.sources, destinations: unique_plates.destinations, transfers }
|
AutoOutput { sources: unique_plates.sources, destinations: unique_plates.destinations, transfers }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper function that reads a CSV and immediately dumps the resulting transfer records
|
|
||||||
/// into the auto function defined in this module.
|
|
||||||
pub fn read_csv_auto(data: &str) -> AutoOutput {
|
pub fn read_csv_auto(data: &str) -> AutoOutput {
|
||||||
let transfer_records = read_csv(data);
|
let transfer_records = read_csv(data);
|
||||||
auto(&transfer_records)
|
auto(&transfer_records)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Looks through a list of transfer records and generates two lists
|
|
||||||
/// of all of the unique plates found in those transfers.
|
|
||||||
/// Worth noting that the Transfer struct requires two associated PlateInstances;
|
|
||||||
/// these PlateInstance structs are generated here---they are not present in a TransferRecord.
|
|
||||||
/// See notes on the UniquePlates struct
|
|
||||||
fn find_unique_plates(records: &[TransferRecord]) -> UniquePlates {
|
fn find_unique_plates(records: &[TransferRecord]) -> UniquePlates {
|
||||||
let mut source_names: HashSet<&str> = HashSet::new();
|
let mut source_names: HashSet<&str> = HashSet::new();
|
||||||
let mut destination_names: HashSet<&str> = HashSet::new();
|
let mut destination_names: HashSet<&str> = HashSet::new();
|
||||||
|
@ -98,12 +79,6 @@ fn find_unique_plates(records: &[TransferRecord]) -> UniquePlates {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tries to guess the format of a plate given the southeastern-est well used.
|
|
||||||
/// A picklist does not necessarily contain plate format info; this method guesses
|
|
||||||
/// based on the well furthest from A1 that exists in a transfer.
|
|
||||||
/// It's possible for a 1536 well plate to be seen as a 384 well if all of the wells
|
|
||||||
/// used could have fit in the top-left quadrant of a 384 well plate
|
|
||||||
/// (i.e. all lower than P24 in this case)
|
|
||||||
fn guess_plate_size(plate_filtered_records: &[&TransferRecord], which: PlateType) -> PlateFormat {
|
fn guess_plate_size(plate_filtered_records: &[&TransferRecord], which: PlateType) -> PlateFormat {
|
||||||
let mut guess = PlateFormat::W96; // Never guess smaller than 96
|
let mut guess = PlateFormat::W96; // Never guess smaller than 96
|
||||||
for record in plate_filtered_records {
|
for record in plate_filtered_records {
|
||||||
|
@ -141,17 +116,6 @@ fn get_transfer_for_all_pairs(
|
||||||
transfers
|
transfers
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Given a source and destination plate, get all of the wells transferred
|
|
||||||
/// from source to destination and turn this into a Transfer.
|
|
||||||
///
|
|
||||||
/// Note that even if you have what looks like two separate transfers,
|
|
||||||
/// this will join all transfers for a given source-dest pair.
|
|
||||||
/// For example:
|
|
||||||
/// Consider expanding all of src:A1 to the column dst:A
|
|
||||||
/// and src:B1 -> row dst:1
|
|
||||||
/// If you were making this transfer in plate tool, it would be easiest
|
|
||||||
/// to do this as two separate transfers.
|
|
||||||
/// On import, this would be seen as one transfer.
|
|
||||||
fn get_transfer_for_pair(
|
fn get_transfer_for_pair(
|
||||||
records: &[TransferRecord],
|
records: &[TransferRecord],
|
||||||
source: &PlateInstance,
|
source: &PlateInstance,
|
||||||
|
|
|
@ -52,12 +52,9 @@ pub fn records_to_echo_client_csv(trs: Vec<TransferRecord>) -> Result<String, Bo
|
||||||
Ok(data)
|
Ok(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts "spreadsheet format" well identification to coordinates
|
|
||||||
pub fn string_well_to_pt(input: &str) -> Option<(u8, u8)> {
|
pub fn string_well_to_pt(input: &str) -> Option<(u8, u8)> {
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref REGEX: Regex = Regex::new(r"([A-Z,a-z]+)(\d+)").unwrap();
|
static ref REGEX: Regex = Regex::new(r"([A-Z,a-z]+)(\d+)").unwrap();
|
||||||
|
|
||||||
// Can this be removed?
|
|
||||||
static ref REGEX_ALT: Regex = Regex::new(r"(\d+)").unwrap();
|
static ref REGEX_ALT: Regex = Regex::new(r"(\d+)").unwrap();
|
||||||
}
|
}
|
||||||
if let Some(c1) = REGEX.captures(input) {
|
if let Some(c1) = REGEX.captures(input) {
|
||||||
|
|
|
@ -1,12 +1,3 @@
|
||||||
//! This contains the `mangle_headers` function and its helpers
|
|
||||||
//!
|
|
||||||
//! Mangling headers is necessary to normalize CSV column names before
|
|
||||||
//! deserialization can be done by Serde.
|
|
||||||
//! Field detection occurs in `detect_field`.
|
|
||||||
//!
|
|
||||||
//! As of lib 0.4.0, unrecognized headers are ignored and left untouched by mangling.
|
|
||||||
//! However, they are not passed through to any subsequent exports from plate-tool.
|
|
||||||
|
|
||||||
pub fn mangle_headers(data: &str) -> String {
|
pub fn mangle_headers(data: &str) -> String {
|
||||||
let (header, rows) = data.split_at(data.find('\n').unwrap());
|
let (header, rows) = data.split_at(data.find('\n').unwrap());
|
||||||
let fields = header.trim().split(",");
|
let fields = header.trim().split(",");
|
||||||
|
|
|
@ -2,17 +2,13 @@ use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{plate::PlateFormat, transfer::Transfer, util::num_to_letters};
|
use crate::{plate::PlateFormat, transfer::Transfer, util::num_to_letters};
|
||||||
|
|
||||||
/// Represents a single line of a CSV picklist.
|
|
||||||
/// In practice, this is generated from the deserialize intermediate.
|
|
||||||
/// A list of `TransferRecord`s can be used to create a `transfer::Transfer` struct;
|
|
||||||
/// see the `auto` module for this.
|
|
||||||
#[derive(Serialize, Debug, Clone)]
|
#[derive(Serialize, Debug, Clone)]
|
||||||
pub struct TransferRecord {
|
pub struct TransferRecord {
|
||||||
#[serde(rename = "Source Barcode")]
|
#[serde(rename = "Source Plate")]
|
||||||
pub source_plate: String,
|
pub source_plate: String,
|
||||||
#[serde(rename = "Source Well")]
|
#[serde(rename = "Source Well")]
|
||||||
pub source_well: String,
|
pub source_well: String,
|
||||||
#[serde(rename = "Dest Barcode")]
|
#[serde(rename = "Dest Plate")]
|
||||||
pub destination_plate: String,
|
pub destination_plate: String,
|
||||||
#[serde(rename = "Destination Well")]
|
#[serde(rename = "Destination Well")]
|
||||||
pub destination_well: String,
|
pub destination_well: String,
|
||||||
|
@ -22,12 +18,6 @@ pub struct TransferRecord {
|
||||||
pub concentration: Option<f32>,
|
pub concentration: Option<f32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The deserialization intermediate is generated from a mangled CSV s.t.
|
|
||||||
/// the column headers are standardized to those given to the derive macro.
|
|
||||||
///
|
|
||||||
/// `TransferRecord` can *always* be generated from the intermediate.
|
|
||||||
/// Currently there's no reason why the inverse couldn't be true,
|
|
||||||
/// but there's no reason to convert back into the deserialize-only intermediate.
|
|
||||||
#[derive(Deserialize, Debug, Clone)]
|
#[derive(Deserialize, Debug, Clone)]
|
||||||
pub struct TransferRecordDeserializeIntermediate {
|
pub struct TransferRecordDeserializeIntermediate {
|
||||||
#[serde(rename = "sourceplate")]
|
#[serde(rename = "sourceplate")]
|
||||||
|
@ -88,7 +78,6 @@ impl From<TransferRecordDeserializeIntermediate> for TransferRecord {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TransferRecordDeserializeIntermediate {
|
impl TransferRecordDeserializeIntermediate {
|
||||||
/// Used to cull malformed picklist entries that lack required information
|
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.source_plate.is_empty()
|
self.source_plate.is_empty()
|
||||||
|| self.destination_plate.is_empty()
|
|| self.destination_plate.is_empty()
|
||||||
|
@ -106,7 +95,6 @@ fn numeric_well_to_alphanumeric(input: u16, pformat: PlateFormat) -> Option<Stri
|
||||||
Some(format!("{}{}", row_str, column))
|
Some(format!("{}{}", row_str, column))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Why is this here?
|
|
||||||
pub fn volume_default() -> f32 {
|
pub fn volume_default() -> f32 {
|
||||||
Transfer::default().volume
|
Transfer::default().volume
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue