///|
pub struct AuditEvent {
event_id : String
lot_id : String
actor : String
action : String
timestamp : String
passed : Bool
evidence : String
} derive(Debug, ToJson)
///|
pub struct TraceabilityResult {
event_count : Int
unique_lots : Int
missing_evidence : Int
failed_events : Int
chronological : Bool
complete : Bool
} derive(Debug, ToJson)
///|
pub fn audit_event(
event_id~ : String,
lot_id~ : String,
actor~ : String,
action~ : String,
timestamp~ : String,
passed~ : Bool,
evidence~ : String,
) -> AuditEvent {
{ event_id, lot_id, actor, action, timestamp, passed, evidence }
}
///|
pub fn audit_missing_evidence(events : Array[AuditEvent]) -> Int {
let mut count = 0
for event in events {
if event.evidence.length() == 0 {
count += 1
}
}
count
}
///|
pub fn audit_failed_count(events : Array[AuditEvent]) -> Int {
let mut count = 0
for event in events {
if !event.passed {
count += 1
}
}
count
}
///|
pub fn audit_unique_lots(events : Array[AuditEvent]) -> Int {
let lots : Array[String] = []
for event in events {
if !lots.contains(event.lot_id) {
lots.push(event.lot_id)
}
}
lots.length()
}
///|
pub fn audit_is_chronological(events : Array[AuditEvent]) -> Bool {
for i in 1.. TraceabilityResult {
let missing = audit_missing_evidence(events)
let failed = audit_failed_count(events)
let chronological = audit_is_chronological(events)
{
event_count: events.length(),
unique_lots: audit_unique_lots(events),
missing_evidence: missing,
failed_events: failed,
chronological,
complete: missing == 0 && failed == 0 && chronological,
}
}
///|
pub fn audit_events_for_lot(
events : Array[AuditEvent],
lot_id : String,
) -> Array[AuditEvent] {
let result : Array[AuditEvent] = []
for event in events {
if event.lot_id == lot_id {
result.push(event)
}
}
result
}
///|
pub fn audit_action_count(events : Array[AuditEvent], action : String) -> Int {
let mut count = 0
for event in events {
if event.action == action {
count += 1
}
}
count
}
///|
pub fn audit_pass_rate(events : Array[AuditEvent]) -> Double {
if events.length() == 0 {
0.0
} else {
(events.length() - audit_failed_count(events)).to_double() /
events.length().to_double()
}
}
///|
pub fn audit_score(result : TraceabilityResult) -> Double {
let total = result.event_count.max(1).to_double()
100.0 *
(1.0 - (result.missing_evidence + result.failed_events).to_double() / total)
}
///|
pub fn evidence_types(events : Array[AuditEvent]) -> Array[String] {
let result : Array[String] = []
for event in events {
let suffix = event.evidence.split(".").last().unwrap_or("").to_owned()
if !result.contains(suffix) {
result.push(suffix)
}
}
result
}