///|
/// A validation result grouped for human and machine consumers.
pub struct ValidationReport {
ok : Bool
error_count : Int
warning_count : Int
issues : Array[ValidationIssue]
accession : String
sequence_length : Int
feature_count : Int
} derive(Debug, Eq)
///|
pub fn build_validation_report(record : Record) -> ValidationReport {
let issues = record.validate()
let mut errors = 0
let mut warnings = 0
for issue in issues {
if issue.is_error() {
errors += 1
} else {
warnings += 1
}
}
{
ok: errors == 0,
error_count: errors,
warning_count: warnings,
issues,
accession: record.accession,
sequence_length: record.origin.length(),
feature_count: record.features.length(),
}
}
///|
pub fn ValidationReport::to_json_string(self : ValidationReport) -> String {
"{\"ok\":" +
self.ok.to_string() +
",\"error_count\":" +
self.error_count.to_string() +
",\"warning_count\":" +
self.warning_count.to_string() +
",\"accession\":" +
json_string(self.accession) +
",\"sequence_length\":" +
self.sequence_length.to_string() +
",\"feature_count\":" +
self.feature_count.to_string() +
",\"issues\":" +
array_json(self.issues.map(fn(issue) { issue.to_json_string() })) +
"}"
}
///|
pub fn ValidationReport::to_text(self : ValidationReport) -> String {
let out = StringBuilder()
out.write_string("accession: " + self.accession + "\n")
out.write_string(
"sequence length: " + self.sequence_length.to_string() + "\n",
)
out.write_string("features: " + self.feature_count.to_string() + "\n")
out.write_string("errors: " + self.error_count.to_string() + "\n")
out.write_string("warnings: " + self.warning_count.to_string() + "\n")
out.write_string(if self.ok { "status: ok\n" } else { "status: failed\n" })
for issue in self.issues {
let severity = if issue.is_error() { "ERROR" } else { "WARN" }
out.write_string(severity + " " + issue.code + ": " + issue.message + "\n")
}
out.to_string()
}
///|
pub fn record_report(record : Record) -> String {
let summary = annotation_summary(record)
let validation = build_validation_report(record)
let out = StringBuilder()
out.write_string("GenBank record report\n")
out.write_string("accession: " + record.accession + "\n")
out.write_string("definition: " + record.definition + "\n")
out.write_string("sequence: " + record.origin.length().to_string() + " bp\n")
out.write_string("features: " + summary.feature_count.to_string() + "\n")
out.write_string("CDS: " + summary.cds_count.to_string() + "\n")
out.write_string("genes: " + summary.gene_count.to_string() + "\n")
out.write_string("GC: " + summary.gc_percent.to_string() + "%\n")
out.write_string(
"validation: " + (if validation.ok { "ok" } else { "failed" }) + "\n",
)
out.to_string()
}
///|
pub fn record_report_json(record : Record) -> String {
let summary = annotation_summary(record)
let validation = build_validation_report(record)
"{\"accession\":" +
json_string(record.accession) +
",\"summary\":" +
summary.to_json_string() +
",\"validation\":" +
validation.to_json_string() +
"}"
}
///|
pub fn normalize_record(record : Record) -> Record {
let normalized_features = record.features.map(fn(feature) {
let normalized_qualifiers = feature.qualifiers.map(fn(qualifier) {
{
name: normalize_identifier(qualifier.name),
value: qualifier.value.map(fn(value) {
normalize_qualifier_value(value)
}),
}
})
{
key: normalize_identifier(feature.key),
location: feature.location,
qualifiers: normalized_qualifiers,
}
})
{
accession: normalize_identifier(record.accession),
version: normalize_identifier(record.version),
locus: record.locus,
definition: normalize_spaces(record.definition),
keywords: normalize_spaces(record.keywords),
source: normalize_spaces(record.source),
features: normalized_features,
origin: normalize_sequence(record.origin),
}
}
///|
fn normalize_identifier(text : String) -> String {
normalize_spaces(text).to_lower()
}
///|
fn normalize_qualifier_value(text : String) -> String {
normalize_spaces(text).trim().to_owned()
}
///|
pub fn normalize_spaces(text : String) -> String {
let out = StringBuilder()
let mut previous_space = false
for ch in text.trim() {
let is_space = ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n'
if is_space {
if !previous_space {
out.write_char(' ')
}
} else {
out.write_char(ch)
}
previous_space = is_space
}
out.to_string()
}
///|
pub fn record_is_normalized(record : Record) -> Bool {
normalize_record(record) == record
}
///|
pub fn record_error_count(record : Record) -> Int {
build_validation_report(record).error_count
}
///|
pub fn record_warning_count(record : Record) -> Int {
build_validation_report(record).warning_count
}
///|
pub fn record_is_valid(record : Record) -> Bool {
build_validation_report(record).ok
}
///|
pub fn record_has_translation_errors(record : Record) -> Bool {
record.check_cds_translations().any(fn(check) { !check.ok })
}
///|
pub fn record_remote_accession_count(record : Record) -> Int {
let mut total = 0
for feature in record.features {
for segment in feature.location.segments {
if segment.remote is Some(_) {
total += 1
}
}
}
total
}
///|
pub fn record_fuzzy_location_count(record : Record) -> Int {
let mut total = 0
for feature in record.features {
if location_is_fuzzy(feature.location) {
total += 1
}
}
total
}
///|
pub fn record_between_location_count(record : Record) -> Int {
let mut total = 0
for feature in record.features {
for range in feature.location.ranges {
if range.between {
total += 1
}
}
}
total
}
///|
pub fn record_cds_count(record : Record) -> Int {
let mut total = 0
for feature in record.features {
if feature.key == "CDS" {
total += 1
}
}
total
}
///|
pub fn record_gene_count(record : Record) -> Int {
let mut total = 0
for feature in record.features {
if feature.key == "gene" {
total += 1
}
}
total
}
///|
pub fn record_source_count(record : Record) -> Int {
let mut total = 0
for feature in record.features {
if feature.key == "source" {
total += 1
}
}
total
}