Class: GovCodes::Dafecd::RiSdi::IndexBuilder

Inherits:
Object
  • Object
show all
Defined in:
lib/gov_codes/dafecd/ri_sdi/index_builder.rb

Overview

Assembles the RI and SDI records of one publication into a single code-keyed index and surfaces the reconciliation data the CLI reports.

Every section named by the Config is sliced out and parsed by the format it uses: SDI sections by the SdiSectionSplitter + SdiCardParser (plus the one embedded CEM ladder record, parsed by the AFSC SpecialtyParser and keyed by its CEM code), RI sections by the RiListParser.

Verification gate: every emitted code must appear verbatim in the source, and every emitted acronym must appear as a "(ACRONYM)" parenthetical in its record's raw title (whitespace/dash/case tolerant). Titles are extracted verbatim (only sanitized) and then de-glued via human-verified overrides supplied by an injected TitleDegluer: each applied override is gated against the raw source title (spacing/case only), so drift lands in #unverified_titles and fails the build; a code with no override keeps its verbatim title and is reported in #codes_needing_deglue (not an error). #unverified? must be false before anything is written.

Acronyms are captured from a title's trailing parenthetical, EXCEPT for the per-publication Config#acronym_exclusions (organization/sub-phrase abbreviations). Every candidate — shipped or excluded — is reported via #acronym_candidates for review.

Constant Summary collapse

ACRONYM_PATTERN =

A trailing parenthetical whose sole token is an uppercase abbreviation. Mirrors Dafecd::IndexBuilder::ACRONYM_PATTERN.

/\(([A-Z][A-Z0-9]{1,7})\)\s*\z/
UNICODE_DASHES =
Patterns::UNICODE_DASHES

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(full_text, config: Config.dafecd, degluer: TitleDegluer.empty) ⇒ IndexBuilder

Returns a new instance of IndexBuilder.



52
53
54
55
56
# File 'lib/gov_codes/dafecd/ri_sdi/index_builder.rb', line 52

def initialize(full_text, config: Config.dafecd, degluer: TitleDegluer.empty)
  @full_text = full_text
  @config = config
  @degluer = degluer
end

Instance Attribute Details

#acronym_candidatesObject (readonly)

Returns the value of attribute acronym_candidates.



48
49
50
# File 'lib/gov_codes/dafecd/ri_sdi/index_builder.rb', line 48

def acronym_candidates
  @acronym_candidates
end

#codes_needing_deglueObject (readonly)

Returns the value of attribute codes_needing_deglue.



48
49
50
# File 'lib/gov_codes/dafecd/ri_sdi/index_builder.rb', line 48

def codes_needing_deglue
  @codes_needing_deglue
end

#collisionsObject (readonly)

Returns the value of attribute collisions.



48
49
50
# File 'lib/gov_codes/dafecd/ri_sdi/index_builder.rb', line 48

def collisions
  @collisions
end

#dropped_recordsObject (readonly)

Returns the value of attribute dropped_records.



48
49
50
# File 'lib/gov_codes/dafecd/ri_sdi/index_builder.rb', line 48

def dropped_records
  @dropped_records
end

#indexObject (readonly)

Returns the value of attribute index.



48
49
50
# File 'lib/gov_codes/dafecd/ri_sdi/index_builder.rb', line 48

def index
  @index
end

#section_codesObject (readonly)

Returns the value of attribute section_codes.



48
49
50
# File 'lib/gov_codes/dafecd/ri_sdi/index_builder.rb', line 48

def section_codes
  @section_codes
end

#section_countsObject (readonly)

Returns the value of attribute section_counts.



48
49
50
# File 'lib/gov_codes/dafecd/ri_sdi/index_builder.rb', line 48

def section_counts
  @section_counts
end

#sequence_numbersObject (readonly)

Returns the value of attribute sequence_numbers.



48
49
50
# File 'lib/gov_codes/dafecd/ri_sdi/index_builder.rb', line 48

def sequence_numbers
  @sequence_numbers
end

#unverified_acronymsObject (readonly)

Returns the value of attribute unverified_acronyms.



48
49
50
# File 'lib/gov_codes/dafecd/ri_sdi/index_builder.rb', line 48

def unverified_acronyms
  @unverified_acronyms
end

#unverified_codesObject (readonly)

Returns the value of attribute unverified_codes.



48
49
50
# File 'lib/gov_codes/dafecd/ri_sdi/index_builder.rb', line 48

def unverified_codes
  @unverified_codes
end

#unverified_titlesObject (readonly)

Returns the value of attribute unverified_titles.



48
49
50
# File 'lib/gov_codes/dafecd/ri_sdi/index_builder.rb', line 48

def unverified_titles
  @unverified_titles
end

Instance Method Details

#buildObject



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/gov_codes/dafecd/ri_sdi/index_builder.rb', line 58

def build
  @index = {}
  @dropped_records = []
  @collisions = []
  @acronym_candidates = []
  @unverified_codes = []
  @unverified_titles = []
  @unverified_acronyms = []
  @codes_needing_deglue = []
  @section_counts = Hash.new(0)
  @section_codes = Hash.new { |h, k| h[k] = [] }
  @sequence_numbers = Hash.new { |h, k| h[k] = [] }

  SectionSlicer.new(@full_text, config: @config).sections.each do |section|
    case section.kind
    when :sdi then build_sdi(section)
    when :ri then build_ri(section)
    end
  end

  @index.each { |code, entry| apply_override(code, entry) }
  @index.each { |code, entry| capture_acronym(code, entry) }
  @index.each { |code, entry| verify(code, entry) }
  @index
end

#sequence_reportHash{Symbol=>Hash}

Returns force => missing:, duplicates:.

Returns:

  • (Hash{Symbol=>Hash})

    force => missing:, duplicates:



90
91
92
93
94
95
96
97
# File 'lib/gov_codes/dafecd/ri_sdi/index_builder.rb', line 90

def sequence_report
  @sequence_numbers.transform_values do |numbers|
    present = numbers.uniq.sort
    duplicates = numbers.group_by(&:itself).select { |_, v| v.size > 1 }.keys.sort
    missing = present.empty? ? [] : ((present.first..present.last).to_a - present)
    {present: present, missing: missing, duplicates: duplicates}
  end
end

#title_inventoryArray<Hash>

Returns raw_title: in code order, for the governed de-glue pass (titles are NOT cleaned here).

Returns:

  • (Array<Hash>)

    raw_title: in code order, for the governed de-glue pass (titles are NOT cleaned here).



101
102
103
104
105
# File 'lib/gov_codes/dafecd/ri_sdi/index_builder.rb', line 101

def title_inventory
  @index.sort_by { |code, _| code.to_s }.map do |code, entry|
    {code: code, name: entry[:name], raw_title: entry[:raw_title], glued: entry[:glued_title]}
  end
end

#unverified?Boolean

True if any emitted value failed verification; the CLI must not write.

Returns:

  • (Boolean)


85
86
87
# File 'lib/gov_codes/dafecd/ri_sdi/index_builder.rb', line 85

def unverified?
  @unverified_codes.any? || @unverified_titles.any? || @unverified_acronyms.any?
end