Class: ActiveSanction::Doctor::Finding
- Inherits:
-
Object
- Object
- ActiveSanction::Doctor::Finding
- Extended by:
- T::Sig
- Defined in:
- lib/active_sanction/doctor/finding.rb
Overview
One thing a diagnosis noticed about one source.
finding.source # => :ofac_sdn
finding.severity # => :warn
finding.check # => :remarks_coverage
finding.observed # => 0.714
finding.baseline # => 0.973
finding.to_s # => "remarks coverage 71.4% (was 97.3%): \"Passport No.\" x 1880 unrecognized"
Three severities, and what separates them
:error this list is not what it was, and screening against it is
unsafe -- it did not parse, it parsed to nothing, a column
holds something else now, a field that every record carried
is gone from all of them
:warn a measurement moved further than a list of this kind moves in
a day, and a human should look at it before the next sync
:info something changed, or something is unrecognized, and it is
within what these files do on their own
The line between error and warn is not how big the number is. It is
whether the reading can be explained by the list changing rather than by
the file changing. A quarter of the records disappearing is a warn,
because a delisting wave looks exactly like that and deciding which one
it was is a judgment nothing here is entitled to make. A column that used
to be numeric and is now full of company names is an error, because
nothing a publisher does to its list can do that to its file.
Observed and baseline are numbers, not prose
message is written for a person reading a terminal at three in the
morning. observed and baseline are for everything else: a threshold
in a monitoring rule, a graph of a fill rate over ninety days, the
instrumentation hooks (#59) a host application alerts through. Both are
nil for a finding that is not a measurement -- a parse failure has no
number.
Instances are frozen on construction and compare by value.
Constant Summary collapse
- SEVERITIES =
Ordered, least serious first: comparing two severities is comparing their positions here.
T.let(%i[info warn error].freeze, T::Array[Symbol])
Instance Attribute Summary collapse
- #baseline ⇒ T.untyped readonly
-
#check ⇒ Symbol
readonly
Which check produced this, as a stable machine name --
:record_count,:fill_identifiers,:column_ent_num. - #message ⇒ String readonly
-
#observed ⇒ T.untyped
readonly
What was measured this run, and what it was measured against.
-
#severity ⇒ Symbol
readonly
One of SEVERITIES.
- #source ⇒ Symbol readonly
Class Method Summary collapse
-
.at_least?(first, second) ⇒ Boolean
Whether
firstis at least as serious assecond. - .from_h(hash) ⇒ T.attached_class
Instance Method Summary collapse
- #==(other) ⇒ Boolean (also: #eql?)
-
#at_least?(level) ⇒ Boolean
Whether this finding is at least as serious as
level. -
#compared? ⇒ Boolean
Whether there was anything to compare against.
- #error? ⇒ Boolean
- #hash ⇒ Integer
- #info? ⇒ Boolean
- #initialize(source:, severity:, check:, message:, observed: nil, baseline: nil) ⇒ void constructor
- #inspect ⇒ String
- #to_h ⇒ Hash{Symbol => T.untyped}
-
#to_line ⇒ String
The line the report prints under a source.
- #to_s ⇒ String
- #warn? ⇒ Boolean
Constructor Details
#initialize(source:, severity:, check:, message:, observed: nil, baseline: nil) ⇒ void
102 103 104 105 106 107 108 109 110 |
# File 'lib/active_sanction/doctor/finding.rb', line 102 def initialize(source:, severity:, check:, message:, observed: nil, baseline: nil) @source = T.let(symbol!(:source, source), Symbol) @severity = T.let(severity!(severity), Symbol) @check = T.let(symbol!(:check, check), Symbol) @message = T.let((), String) @observed = T.let(observed, T.untyped) @baseline = T.let(baseline, T.untyped) freeze end |
Instance Attribute Details
#baseline ⇒ T.untyped (readonly)
81 82 83 |
# File 'lib/active_sanction/doctor/finding.rb', line 81 def baseline @baseline end |
#check ⇒ Symbol (readonly)
Which check produced this, as a stable machine name -- :record_count,
:fill_identifiers, :column_ent_num. What a monitoring rule is
written against, and what stays the same when the message is reworded.
69 70 71 |
# File 'lib/active_sanction/doctor/finding.rb', line 69 def check @check end |
#message ⇒ String (readonly)
72 73 74 |
# File 'lib/active_sanction/doctor/finding.rb', line 72 def @message end |
#observed ⇒ T.untyped (readonly)
What was measured this run, and what it was measured against. nil for a
finding that is not a measurement, and baseline is nil as well for
the first run of a source, where there was nothing to compare with.
78 79 80 |
# File 'lib/active_sanction/doctor/finding.rb', line 78 def observed @observed end |
#severity ⇒ Symbol (readonly)
One of SEVERITIES.
63 64 65 |
# File 'lib/active_sanction/doctor/finding.rb', line 63 def severity @severity end |
#source ⇒ Symbol (readonly)
59 60 61 |
# File 'lib/active_sanction/doctor/finding.rb', line 59 def source @source end |
Class Method Details
.at_least?(first, second) ⇒ Boolean
Whether first is at least as serious as second.
94 95 96 |
# File 'lib/active_sanction/doctor/finding.rb', line 94 def self.at_least?(first, second) SEVERITIES.index(first).to_i >= SEVERITIES.index(second).to_i end |
.from_h(hash) ⇒ T.attached_class
84 85 86 87 88 89 90 |
# File 'lib/active_sanction/doctor/finding.rb', line 84 def self.from_h(hash) attributes = hash.to_h.transform_keys(&:to_sym) unknown = attributes.keys - MEMBERS raise InvalidArgument, "unknown Doctor::Finding attribute(s): #{unknown.join(", ")}" if unknown.any? T.unsafe(self).new(**attributes) end |
Instance Method Details
#==(other) ⇒ Boolean Also known as: eql?
144 145 146 147 148 |
# File 'lib/active_sanction/doctor/finding.rb', line 144 def ==(other) return false unless other.instance_of?(self.class) to_h == other.to_h end |
#at_least?(level) ⇒ Boolean
Whether this finding is at least as serious as level.
123 |
# File 'lib/active_sanction/doctor/finding.rb', line 123 def at_least?(level) = Finding.at_least?(severity, symbol!(:severity, level)) |
#compared? ⇒ Boolean
Whether there was anything to compare against. False says the reading was held to a committed floor rather than to the last sync.
128 |
# File 'lib/active_sanction/doctor/finding.rb', line 128 def compared? = !baseline.nil? |
#error? ⇒ Boolean
113 |
# File 'lib/active_sanction/doctor/finding.rb', line 113 def error? = severity == :error |
#hash ⇒ Integer
152 |
# File 'lib/active_sanction/doctor/finding.rb', line 152 def hash = [self.class, to_h].hash |
#info? ⇒ Boolean
119 |
# File 'lib/active_sanction/doctor/finding.rb', line 119 def info? = severity == :info |
#inspect ⇒ String
155 |
# File 'lib/active_sanction/doctor/finding.rb', line 155 def inspect = "#<#{self.class} #{source} #{severity} #{check}: #{}>" |
#to_h ⇒ Hash{Symbol => T.untyped}
131 132 133 134 |
# File 'lib/active_sanction/doctor/finding.rb', line 131 def to_h { source: source, severity: severity, check: check, message: , observed: observed, baseline: baseline } end |
#to_line ⇒ String
The line the report prints under a source.
141 |
# File 'lib/active_sanction/doctor/finding.rb', line 141 def to_line = " #{severity.to_s.ljust(5)} #{}" |
#to_s ⇒ String
137 |
# File 'lib/active_sanction/doctor/finding.rb', line 137 def to_s = |
#warn? ⇒ Boolean
116 |
# File 'lib/active_sanction/doctor/finding.rb', line 116 def warn? = severity == :warn |