Add some doc comments to lib
Gitea Scan/plate-tool/pipeline/head This commit looks good
Details
Gitea Scan/plate-tool/pipeline/head This commit looks good
Details
This commit is contained in:
parent
94386a0485
commit
ce6e88a347
|
@ -2,9 +2,10 @@ 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!
|
///
|
||||||
// Exports of this type should combine all transfers between the two actively selected plates.
|
/// Note that this does not export plate info!
|
||||||
|
/// 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,3 +1,8 @@
|
||||||
|
//! 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;
|
||||||
|
@ -12,6 +17,7 @@ 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>,
|
||||||
|
@ -24,6 +30,12 @@ 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);
|
||||||
|
@ -31,11 +43,18 @@ 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();
|
||||||
|
@ -79,6 +98,12 @@ 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 {
|
||||||
|
@ -116,6 +141,17 @@ 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,9 +52,12 @@ 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,3 +1,12 @@
|
||||||
|
//! 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,6 +2,10 @@ 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 Barcode")]
|
||||||
|
@ -18,6 +22,12 @@ 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")]
|
||||||
|
@ -78,6 +88,7 @@ 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()
|
||||||
|
@ -95,6 +106,7 @@ 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