///|
/// A normalized qualifier lookup result.
pub struct QualifierMatch {
feature_key : String
qualifier_name : String
value : String
feature_start : Int
feature_end : Int
} derive(Debug, Eq)
///|
pub fn find_qualifier_matches(
record : Record,
name : String,
) -> Array[QualifierMatch] {
let result : Array[QualifierMatch] = []
for feature in record.features {
for qualifier in feature.qualifiers {
match qualifier.value {
Some(value) if qualifier.name == name =>
result.push({
feature_key: feature.key,
qualifier_name: name,
value,
feature_start: feature_start(feature),
feature_end: feature_end(feature),
})
_ => ()
}
}
}
result
}
///|
pub fn find_qualifier_value_matches(
record : Record,
name : String,
value : String,
) -> Array[QualifierMatch] {
find_qualifier_matches(record, name).filter(fn(item) { item.value == value })
}
///|
pub fn QualifierMatch::to_json_string(self : QualifierMatch) -> String {
"{\"feature_key\":" +
json_string(self.feature_key) +
",\"qualifier_name\":" +
json_string(self.qualifier_name) +
",\"value\":" +
json_string(self.value) +
",\"feature_start\":" +
self.feature_start.to_string() +
",\"feature_end\":" +
self.feature_end.to_string() +
"}"
}
///|
pub fn qualifier_match_json(record : Record, name : String) -> String {
array_json(
find_qualifier_matches(record, name).map(fn(item) { item.to_json_string() }),
)
}
///|
pub fn qualifier_value(
record : Record,
key : String,
name : String,
) -> Array[String] {
feature_qualifier_values(record.features_by_key(key), name)
}
///|
pub fn qualifier_values_for_feature(
feature : Feature,
name : String,
) -> Array[String] {
feature.qualifiers.filter_map(fn(qualifier) {
if qualifier.name == name {
qualifier.value
} else {
None
}
})
}
///|
pub fn qualifier_names_for_record(record : Record) -> Array[String] {
let result : Array[String] = []
for feature in record.features {
for name in feature_qualifier_names(feature) {
if !result.contains(name) {
result.push(name)
}
}
}
result
}
///|
pub fn qualifier_name_count(record : Record, name : String) -> Int {
find_qualifier_matches(record, name).length()
}
///|
pub fn qualifier_value_count(
record : Record,
name : String,
value : String,
) -> Int {
find_qualifier_value_matches(record, name, value).length()
}
///|
pub fn qualifier_exists(record : Record, name : String) -> Bool {
qualifier_name_count(record, name) > 0
}
///|
pub fn qualifier_value_exists(
record : Record,
name : String,
value : String,
) -> Bool {
qualifier_value_count(record, name, value) > 0
}
///|
pub fn qualifier_histogram(record : Record) -> Array[(String, Int)] {
feature_qualifier_counts(record)
}
///|
pub fn sorted_qualifier_names(record : Record) -> Array[String] {
let names = qualifier_names_for_record(record)
names.sort()
names
}
///|
pub fn sorted_feature_keys(record : Record) -> Array[String] {
let keys = build_feature_index(record).keys()
keys.sort()
keys
}
///|
pub fn qualifier_search(
record : Record,
text : String,
) -> Array[QualifierMatch] {
let needle = text.to_lower()
find_qualifier_matches(record, "note").filter(fn(item) {
item.value.to_lower().contains(needle)
})
}
///|
pub fn feature_search(record : Record, text : String) -> Array[Feature] {
let needle = text.to_lower()
record.features.filter(fn(feature) {
feature.key.to_lower().contains(needle) ||
feature.qualifiers.any(fn(qualifier) {
match qualifier.value {
Some(value) => value.to_lower().contains(needle)
None => false
}
})
})
}
///|
pub fn record_gene_names(record : Record) -> Array[String] {
feature_qualifier_values(record.features_by_key("gene"), "gene")
}
///|
pub fn record_products(record : Record) -> Array[String] {
feature_qualifier_values(record.features_by_key("CDS"), "product")
}
///|
pub fn record_translations(record : Record) -> Array[String] {
feature_qualifier_values(record.features_by_key("CDS"), "translation")
}
///|
pub fn record_organisms(record : Record) -> Array[String] {
feature_qualifier_values(record.features_by_key("source"), "organism")
}
///|
pub fn record_locus_tags(record : Record) -> Array[String] {
feature_qualifier_values(record.features, "locus_tag")
}
///|
pub fn record_notes(record : Record) -> Array[String] {
feature_qualifier_values(record.features, "note")
}
///|
pub fn record_references(record : Record) -> Array[String] {
feature_qualifier_values(record.features, "citation")
}
///|
pub fn qualifier_match_text(item : QualifierMatch) -> String {
item.feature_key +
":" +
item.qualifier_name +
"=" +
item.value +
" [" +
item.feature_start.to_string() +
".." +
item.feature_end.to_string() +
"]"
}
///|
pub fn record_qualifier_report(record : Record) -> String {
let out = StringBuilder()
for name in sorted_qualifier_names(record) {
out.write_string(
name + ": " + qualifier_name_count(record, name).to_string() + "\n",
)
}
out.to_string()
}
///|
pub fn record_feature_report(record : Record) -> String {
let out = StringBuilder()
for key in sorted_feature_keys(record) {
let features = record.features_by_key(key)
out.write_string(key + ": " + features.length().to_string() + "\n")
for feature in features {
out.write_string(" " + feature_summary_text(feature) + "\n")
}
}
out.to_string()
}
///|
pub fn record_annotation_report(record : Record) -> String {
record_feature_report(record) +
"qualifiers:\n" +
record_qualifier_report(record)
}
///|
pub fn qualifier_values_json(record : Record, name : String) -> String {
array_json(
feature_qualifier_values(record.features, name).map(fn(value) {
json_string(value)
}),
)
}
///|
pub fn gene_summary_json(record : Record) -> String {
"{\"genes\":" +
qualifier_values_json(record, "gene") +
",\"locus_tags\":" +
qualifier_values_json(record, "locus_tag") +
",\"products\":" +
qualifier_values_json(record, "product") +
"}"
}
///|
pub fn translation_lengths(record : Record) -> Array[Int] {
record_translations(record).map(fn(value) { value.length() })
}
///|
pub fn longest_translation(record : Record) -> String? {
let values = record_translations(record)
if values.length() == 0 {
None
} else {
Some(
values.fold(init=values[0], fn(best, value) {
if value.length() > best.length() {
value
} else {
best
}
}),
)
}
}
///|
pub fn shortest_translation(record : Record) -> String? {
let values = record_translations(record)
if values.length() == 0 {
None
} else {
Some(
values.fold(init=values[0], fn(best, value) {
if value.length() < best.length() {
value
} else {
best
}
}),
)
}
}