Class: ActiveSanction::Sources::AustraliaDfat
- Extended by:
- T::Sig
- Defined in:
- lib/active_sanction/sources/australia_dfat.rb,
lib/active_sanction/sources/australia_dfat/record.rb,
lib/active_sanction/sources/australia_dfat/published_date.rb
Overview
Australia's Consolidated List: everybody the Foreign Minister has designated or declared under the Autonomous Sanctions Regulations 2011, together with every UN Security Council listing Australia gives effect to.
snapshot = ActiveSanction::Sources[:australia_dfat].new.sync
It is published as a spreadsheet, and as nothing else
https://www.dfat.gov.au/sites/default/files/Australian_Sanctions_Consolidated_List.xlsx
"Consolidated List" 11,163 rows, 19 columns, 1.3 MB
One .xlsx file, one sheet. There is no CSV, no XML and no JSON: DFAT's
Consolidated List page offers exactly one download, and this is it. So
reading a spreadsheet is not a convenience here, it is the price of
screening against Australian sanctions at all.
That was the open question on this list, and the answer was not to take a
dependency. Parsers::Spreadsheet reads the workbook with zlib and the
XML toolkit the other five adapters already use -- an .xlsx being a ZIP
of XML parts -- so this adapter costs a host nothing to install. See that
class for what it does and does not read.
The URL moved, and the old one still answers
The path this gem was scoped against, regulation8_consolidated.xlsx,
is gone. What is left in its place is a redirect to
regulation8_consolidated_2.xls -- a real file, served 200, in the old
binary Excel format, last modified in March 2022. An adapter pointed at it
would download a list four years stale on every sync and never once look
unhealthy. The URL above is the one DFAT's own page links today.
DFAT's edge rejects this gem's User-Agent
Verified against the live endpoint: active_sanction/x.y.z (+https://...)
gets no response at all -- not a 403, a dropped connection -- while
curl/8.7.1, Wget/1.21 and python-requests/2.31.0 are all served. The
filter is on the leading product token, and an unrecognised one is
dropped, so identifying ourselves honestly is what gets us blocked.
What is sent instead is the configured agent inside the form written for
exactly this: Mozilla/5.0 (compatible; <agent>), which is how a
well-behaved crawler has identified itself since Googlebot. It is not a
disguise -- the agent, its version and the contact URL an operator
configured are all still in the string, and DFAT can still see who we are
and block us on purpose. It is the same string in a shape the edge parses.
See #fetch_file, which is the only place this source departs from Base.
A record is several rows
DFAT publishes one row per name. Reference 1000 is a primary name and
1000a, 1000b are its aliases, with every other column repeated on each
-- so 11,163 rows are 3,906 records: 2,543 people, 1,041 organizations and
322 vessels. Record says how they are joined, and what the Control Date
is not.
What a clean Australian result is worth
About what a UN one is worth, which is unsurprising: 1,172 of the 3,906 records are listings by a UN committee that Australia has given effect to, and they arrive with the UN's own text intact. Alias grading is published as a field on all 6,802 aliases. Birth dates are published on 6,823 rows in nine spellings, which PublishedDate reads to within four fragments of the whole list.
Against that, DFAT publishes no document numbers of any kind -- no passport, no national identity number, no company registration -- for any of the 3,906 records. The only identifier on the list is an IMO number, on 344 vessel rows. So an Australian name match has nothing behind it to make it decisive, in the way an OFAC passport number usually settles one, and a screening policy should know that before it sets a threshold.
What this adapter does not do
It does not read the Listing Information prose for anything but the listing date. The column holds relisting histories, UN committee references and the reasons for a designation, in a dozen shapes, and reading it properly is the job OFAC's RemarksParser does and wants the same treatment -- a measured coverage figure -- rather than a regex added here in passing. It is kept verbatim in remarks.
It does not decompose an address. DFAT publishes one free-text column and no parts, so the whole published string is the street; see Record.
Instance Attribute Summary collapse
-
#warnings ⇒ Array<Parsers::Warning>
readonly
Records that could not be used, and fields that could not be read.
Attributes inherited from Base
#cache, #fetcher, #instrumenter, #logger
Class Method Summary collapse
-
.enumerated(cell) ⇒ Array<String>
One cell split on the `a) ...
-
.group(reference) ⇒ String?
A reference with its alias suffix removed:
1000aand1000bare both part of1000.
Instance Method Summary collapse
- #initialize(*args, **options) ⇒ void constructor
- #parse(raw) ⇒ Array<Entity>
-
#source_version ⇒ String?
2026-09-04T05:37:12Z-- the moment DFAT saved the workbook, which it stamps inside the file and reports on its own download page as the date the list was last updated.
Methods inherited from Base
#authority, #column_shapes, #column_tallies, #file_key, #floors, #format, #fresh?, #inspect, #jurisdiction, #key, #parse_argument, published_remarks, #retrieve, #snapshot, #stale?, #sync, #url, #urls
Methods included from Definition
#authority, #declarations, #declared?, #declared_floors, #declared_urls, #file_key, #floor, #floors, #format, #jurisdiction, #key, key!, #licence_notice, #licence_url, #multi_url?, #to_h, #url, #urls
Constructor Details
#initialize(*args, **options) ⇒ void
162 163 164 165 166 167 |
# File 'lib/active_sanction/sources/australia_dfat.rb', line 162 def initialize(*args, **) super @warnings = T.let([], T::Array[Parsers::Warning]) @unmapped = T.let([], T::Array[Parsers::Warning]) @modified = T.let(nil, T.nilable(String)) end |
Instance Attribute Details
#warnings ⇒ Array<Parsers::Warning> (readonly)
Records that could not be used, and fields that could not be read. Read after #parse; sync orchestration (#34) reports them.
159 160 161 |
# File 'lib/active_sanction/sources/australia_dfat.rb', line 159 def warnings @warnings end |
Class Method Details
.enumerated(cell) ⇒ Array<String>
One cell split on the a) ... b) ... enumeration DFAT writes several
values with, or the whole cell where it wrote only one.
197 198 199 |
# File 'lib/active_sanction/sources/australia_dfat.rb', line 197 def self.enumerated(cell) cell.to_s.split(ENUMERATOR).map { |part| part.gsub(SPACE, " ").strip }.reject(&:empty?) end |
.group(reference) ⇒ String?
A reference with its alias suffix removed: 1000a and 1000b are both
part of 1000. Public because it is what Record identifies a group by
and what a caller reconciling a hit against DFAT's own site needs.
189 190 191 192 |
# File 'lib/active_sanction/sources/australia_dfat.rb', line 189 def self.group(reference) base = reference.to_s.strip.sub(ALIAS_SUFFIX, "") base.empty? ? nil : base end |
Instance Method Details
#parse(raw) ⇒ Array<Entity>
170 171 172 173 174 175 176 |
# File 'lib/active_sanction/sources/australia_dfat.rb', line 170 def parse(raw) reader = LIST.read(raw) entities = build(reader) @modified = reader.modified @warnings = reader.warnings + @unmapped entities end |
#source_version ⇒ String?
2026-09-04T05:37:12Z -- the moment DFAT saved the workbook, which it
stamps inside the file and reports on its own download page as the date
the list was last updated. Falls back to Last-Modified for a payload
handed straight to #snapshot.
183 |
# File 'lib/active_sanction/sources/australia_dfat.rb', line 183 def source_version = @modified || super |