Class: ActiveSanction::Doctor::Checkup
- Inherits:
-
Object
- Object
- ActiveSanction::Doctor::Checkup
- Extended by:
- T::Sig
- Defined in:
- lib/active_sanction/doctor/checkup.rb
Overview
One source's fresh profile held against what it was last time, and against the floors its adapter committed to. Produces findings and nothing else -- it never fetches, never stores, and never decides what to do about what it found.
Checkup.new(source: :ofac_sdn, observed: today, baseline: yesterday).findings
The baseline is the last stored snapshot, not a committed number
A threshold committed per adapter ("expect ~19,321 rows +/- 2,000") goes stale on its own, and the day somebody bumps it to make the build pass is the day it stops being read. The previous snapshot does not go stale, costs nothing to maintain, and catches what a fixed threshold cannot: a fill rate that has drifted from 61% to 12% is invisible to any bound wide enough to have survived three years of a list growing.
Floors are the coarse backstop underneath it, for the run that has nothing to compare against -- a first sync, a new source, a store that was cleared. Without them a first run would have to either pass silently or fail loudly, and both are wrong.
What separates a warn from an error
Not the size of the number. Whether the reading can be explained by the list changing rather than by the file changing.
A third of the records disappearing is a warn: a delisting wave looks
exactly like a truncated download, and deciding automatically that it was
the first is how a compliance tool ends up quietly screening against a
list it has thrown half of away. A column that used to hold numbers and
now holds company names is an error, and so is every record on a list
losing a field all of them used to carry, because nothing a publisher
does to its list produces either -- only something done to its file
does.
Instance Attribute Summary collapse
-
#baseline ⇒ Profile?
readonly
What this source measured last time, or nil for a run with nothing to compare against.
-
#floors ⇒ Hash{Symbol => Numeric}
readonly
Check name to the value the adapter committed to as a lower bound.
- #observed ⇒ Profile readonly
- #source ⇒ Symbol readonly
-
#tolerance ⇒ Float
readonly
How far a measurement may move before it is worth a finding, as a share of what it was.
Instance Method Summary collapse
-
#findings ⇒ Array<Finding>
Every finding, most serious first and stable within a severity so that two runs of an unchanged list produce identical output.
- #initialize(source:, observed:, baseline: nil, floors: {}, tolerance: Configuration::DEFAULT_DOCTOR_TOLERANCE) ⇒ void constructor
Constructor Details
#initialize(source:, observed:, baseline: nil, floors: {}, tolerance: Configuration::DEFAULT_DOCTOR_TOLERANCE) ⇒ void
96 97 98 99 100 101 102 |
# File 'lib/active_sanction/doctor/checkup.rb', line 96 def initialize(source:, observed:, baseline: nil, floors: {}, tolerance: Configuration::DEFAULT_DOCTOR_TOLERANCE) @source = T.let(source.to_sym, Symbol) @observed = T.let(observed, Profile) @baseline = T.let(baseline, T.nilable(Profile)) @floors = T.let(floors.to_h { |name, value| [name.to_sym, value] }.freeze, T::Hash[Symbol, Numeric]) @tolerance = T.let(Float(tolerance), Float) end |
Instance Attribute Details
#baseline ⇒ Profile? (readonly)
What this source measured last time, or nil for a run with nothing to compare against.
80 81 82 |
# File 'lib/active_sanction/doctor/checkup.rb', line 80 def baseline @baseline end |
#floors ⇒ Hash{Symbol => Numeric} (readonly)
Check name to the value the adapter committed to as a lower bound. See Sources::Definition#floor.
85 86 87 |
# File 'lib/active_sanction/doctor/checkup.rb', line 85 def floors @floors end |
#observed ⇒ Profile (readonly)
75 76 77 |
# File 'lib/active_sanction/doctor/checkup.rb', line 75 def observed @observed end |
#source ⇒ Symbol (readonly)
72 73 74 |
# File 'lib/active_sanction/doctor/checkup.rb', line 72 def source @source end |
#tolerance ⇒ Float (readonly)
How far a measurement may move before it is worth a finding, as a share of what it was.
90 91 92 |
# File 'lib/active_sanction/doctor/checkup.rb', line 90 def tolerance @tolerance end |
Instance Method Details
#findings ⇒ Array<Finding>
Every finding, most serious first and stable within a severity so that two runs of an unchanged list produce identical output.
107 108 109 110 |
# File 'lib/active_sanction/doctor/checkup.rb', line 107 def findings collected = empty_finding || (columns + record_count + fill + remarks_coverage + warnings + orphans) collected.sort_by { |finding| [-Finding::SEVERITIES.index(finding.severity).to_i, finding.check.to_s] } end |