Class: GovCodes::Dafecd::Publication
- Inherits:
-
Object
- Object
- GovCodes::Dafecd::Publication
- Defined in:
- lib/gov_codes/dafecd/publication.rb
Overview
Per-publication configuration for the classification-directory extractor.
The DAFECD (enlisted) and DAFOCD (officer) directories share the same extraction pipeline (RecordSplitter -> SpecialtyParser -> ShredoutParser -> IndexBuilder) but differ in a handful of concrete details: the running header, the shape of a skill/qualification ladder line, whether standalone single-code records and CEM lines exist, how the X-form specialty key is derived, and where the release artifact is written.
A Publication is an immutable value object capturing exactly those
differences. It is injected into every pipeline stage. The default
everywhere is Publication.dafecd, so the enlisted extraction is
byte-for-byte unchanged by the parameterization.
Constant Summary collapse
- DAFECD_HEADER =
--- Enlisted (DAFECD) patterns ------------------------------------------ Running page header, e.g. "DAFECD, 31 Oct 25".
/^\s*DAFECD,\s+\d/- DAFECD_LADDER =
Skill-ladder line (group 1 = concrete 5-char AFSC, group 2 = skill-level word). Tolerates the DAFECD's pdf-reader quirks (see Patterns::LADDER).
/ ^\s*(?:AFSC\s+)?(\d[A-Z]\d\d\d)\*? (?:\s+or\s+\d[A-Z0-9]+)? ,?\s* (?:[A-Za-z][A-Za-z\/]*\s+){0,3} (Helper|Apprentice|Journeyman|Craftsman| Superintendent|Senior\s+Enlisted(?:\s+Leader)?|Entry) (?:\s*\([A-Z]{2,6}\))? (?:\s*AFSC)? \s*$ /x- DAFECD_CEM =
CEM (Chief Enlisted Manager) code line, e.g. "CEM Code 1A100*".
/^\s*CEM Code\s+(\d[A-Z]\d00)\*?/- DAFECD_GLUED_AFSC =
Inserts a boundary before an "AFSC
" glued to a word. /(?<=[A-Za-z])(?=AFSC \d[A-Z]\d\d\d)/- DAFECD_CHANGE_DATE =
The DAFECD's per-record change-date annotation.
/\((?:Changed|Established|Effective)\s+(\d{1,2}\s+\w{3,9}\s+\d{2,4})\)/- DAFECD_SHREDOUT_HEADER =
The DAFECD shredout table header ("Suffix ... Primary Aircraft").
/Suffix\s+\w/- DAFOCD_HEADER =
--- Officer (DAFOCD) patterns ------------------------------------------- Running page header, e.g. "DAFOCD, 31 Oct 25".
/^\s*DAFOCD,\s+\d/- DAFOCD_LADDER =
Qualification-ladder line (group 1 = concrete 4-char AFSC, group 2 = free-form qualification title). The officer title vocabulary is open, so the anchor relies on the CODE SHAPE (exactly 4 chars: two digits, a letter, and a level digit 1-4) plus a SHORT title that ENDS the line AND STARTS WITH AN UPPERCASE LETTER. Every real DAFOCD qualification title is a proper label (Entry, Qualified, Aircraft Commander, Staff, ...), so the uppercase-initial requirement rejects wrapped source PROSE that happens to begin "AFSC <4-char-code>,
" (e.g. the real lines "AFSC 14F3, but applied to developing" and "AFSC 42G3, current and continuous"), which otherwise satisfied the code+short-title shape. It also still rejects the 5-char shred-code / long-sentence prose mentions. An optional leading/trailing "AFSC" absorbs the same pdf-reader glue the enlisted ladder handles (e.g. "11G4, StaffAFSC"). The directory is INCONSISTENT about three things, all tolerated here:
* COMMA (group A): most cards print "AFSC 11B4, Staff", but several live medical cards omit the comma and separate with a space ("AFSC 44B4 Staff", "AFSC 45A4* Staff"). Hence the separator is comma-or-space, NOT a bare optional comma: requiring at least one of comma/whitespace is what still rejects glued-shred prose such as "AFSC 42B3Z requires successful completion of" and "AFSC 43E3Aand complete the education", where an uppercase shred letter is glued to the code with no separator before the lowercase prose. A bare ",?" would let those in. * SHRED LETTER (group B): a few cards print the qualification code with a glued shredout letter, e.g. "AFSC 17D4W*, Staff". The optional "[A-Z]" absorbs it so it is NOT baked into the captured concrete code (group 1 stays "17D4"); the letter is documented as a shredout via the card's shredout table instead (see ShredoutParser). * LEADING GLYPH: pdf-reader prepends a Private Use Area / bullet glyph to some ladder lines (e.g. U+F0EA on the 17D card). The leading class mirrors Patterns::DECORATIVE so the anchor tolerates it. %r{ ^[\s\u{E000}-\u{F8FF}★☆•●▪■⁃∙]*(?:AFSC\s+)?(\d\d[A-Z][1-4])[A-Z]?\*? (?:,\s*|\s+) ([A-Z][A-Za-z/\ ]{0,44}?) (?:\s*AFSC)? \s*$ }x- DAFOCD_BARE_CODE =
A standalone single-code officer record, e.g. "AFSC 10C0". These carry a title/date/sections but no ladder; each is a full record keyed by the literal code.
/^\s*AFSC\s+(\d\d[A-Z][0-9X])\s*$/- DAFOCD_GLUED_AFSC =
Inserts a boundary before an "AFSC
" glued to a word. /(?<=[A-Za-z])(?=AFSC \d\d[A-Z][1-4])/- DAFOCD_CHANGE_DATE =
The DAFOCD's per-record change-date annotation. Tolerant of the officer directory's glued day/month ("30Apr 23") and of a trailing second date ("30Apr 14, Effective 25 Oct 13"): the first annotation is captured.
/\((?:Changed|Established|Effective)\s+(\d{1,2}\s*\w{3,9}\s+\d{2,4})/- DAFOCD_SHREDOUT_HEADER =
The DAFOCD shredout table header ("Suffix ... Portion of AFS to Which Related").
/Suffix\s+\w/
Instance Attribute Summary collapse
-
#acronym_exclusions ⇒ Object
readonly
Returns the value of attribute acronym_exclusions.
-
#bare_code ⇒ Object
readonly
Returns the value of attribute bare_code.
-
#cem ⇒ Object
readonly
Returns the value of attribute cem.
-
#change_date ⇒ Object
readonly
Returns the value of attribute change_date.
-
#directory_name ⇒ Object
readonly
Returns the value of attribute directory_name.
-
#glued_afsc ⇒ Object
readonly
Returns the value of attribute glued_afsc.
-
#header ⇒ Object
readonly
Returns the value of attribute header.
-
#id ⇒ Object
readonly
Returns the value of attribute id.
-
#index_filename ⇒ Object
readonly
Returns the value of attribute index_filename.
-
#ladder ⇒ Object
readonly
Returns the value of attribute ladder.
-
#levels_key ⇒ Object
readonly
Returns the value of attribute levels_key.
-
#release_dir ⇒ Object
readonly
Returns the value of attribute release_dir.
-
#shredout_header ⇒ Object
readonly
Returns the value of attribute shredout_header.
-
#shredout_overrides_filename ⇒ Object
readonly
Returns the value of attribute shredout_overrides_filename.
-
#title_overrides_filename ⇒ Object
readonly
Returns the value of attribute title_overrides_filename.
Class Method Summary collapse
-
.dafecd ⇒ Publication
The enlisted directory configuration.
-
.dafocd ⇒ Publication
The officer directory configuration.
-
.detect(text) ⇒ Object
Select the publication that matches +text+'s running header, defaulting to enlisted.
-
.for(id) ⇒ Object
Look a publication up by its id symbol (:dafecd / :dafocd).
Instance Method Summary collapse
-
#captures_shredout_acronyms? ⇒ Boolean
Whether this publication extracts shredout-level acronyms (officer).
-
#career_field(codes) ⇒ Object
The career-field key (first two chars of the basis code).
-
#initialize(id:, directory_name:, header:, ladder:, bare_code:, cem:, glued_afsc:, change_date:, shredout_header:, levels_key:, code_has_specific:, captures_shredout_acronyms:, acronym_exclusions:, release_dir:, index_filename:, title_overrides_filename:, shredout_overrides_filename:) ⇒ Publication
constructor
A new instance of Publication.
-
#shredout_overrides_path ⇒ Object
The absolute path to this publication's shredout-overrides file.
-
#specialty_key(codes) ⇒ Object
The X-form specialty key for a ladder group's concrete codes.
-
#title_overrides_path ⇒ Object
The absolute path to this publication's title-overrides file.
Constructor Details
#initialize(id:, directory_name:, header:, ladder:, bare_code:, cem:, glued_afsc:, change_date:, shredout_header:, levels_key:, code_has_specific:, captures_shredout_acronyms:, acronym_exclusions:, release_dir:, index_filename:, title_overrides_filename:, shredout_overrides_filename:) ⇒ Publication
Returns a new instance of Publication.
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 |
# File 'lib/gov_codes/dafecd/publication.rb', line 177 def initialize(id:, directory_name:, header:, ladder:, bare_code:, cem:, glued_afsc:, change_date:, shredout_header:, levels_key:, code_has_specific:, captures_shredout_acronyms:, acronym_exclusions:, release_dir:, index_filename:, title_overrides_filename:, shredout_overrides_filename:) @id = id @directory_name = directory_name @header = header @ladder = ladder @bare_code = @cem = cem @glued_afsc = glued_afsc @change_date = change_date @shredout_header = shredout_header @levels_key = levels_key @code_has_specific = code_has_specific @captures_shredout_acronyms = captures_shredout_acronyms @acronym_exclusions = acronym_exclusions @release_dir = release_dir @index_filename = index_filename @title_overrides_filename = title_overrides_filename @shredout_overrides_filename = shredout_overrides_filename freeze end |
Instance Attribute Details
#acronym_exclusions ⇒ Object (readonly)
Returns the value of attribute acronym_exclusions.
172 173 174 |
# File 'lib/gov_codes/dafecd/publication.rb', line 172 def acronym_exclusions @acronym_exclusions end |
#bare_code ⇒ Object (readonly)
Returns the value of attribute bare_code.
172 173 174 |
# File 'lib/gov_codes/dafecd/publication.rb', line 172 def @bare_code end |
#cem ⇒ Object (readonly)
Returns the value of attribute cem.
172 173 174 |
# File 'lib/gov_codes/dafecd/publication.rb', line 172 def cem @cem end |
#change_date ⇒ Object (readonly)
Returns the value of attribute change_date.
172 173 174 |
# File 'lib/gov_codes/dafecd/publication.rb', line 172 def change_date @change_date end |
#directory_name ⇒ Object (readonly)
Returns the value of attribute directory_name.
172 173 174 |
# File 'lib/gov_codes/dafecd/publication.rb', line 172 def directory_name @directory_name end |
#glued_afsc ⇒ Object (readonly)
Returns the value of attribute glued_afsc.
172 173 174 |
# File 'lib/gov_codes/dafecd/publication.rb', line 172 def glued_afsc @glued_afsc end |
#header ⇒ Object (readonly)
Returns the value of attribute header.
172 173 174 |
# File 'lib/gov_codes/dafecd/publication.rb', line 172 def header @header end |
#id ⇒ Object (readonly)
Returns the value of attribute id.
172 173 174 |
# File 'lib/gov_codes/dafecd/publication.rb', line 172 def id @id end |
#index_filename ⇒ Object (readonly)
Returns the value of attribute index_filename.
172 173 174 |
# File 'lib/gov_codes/dafecd/publication.rb', line 172 def index_filename @index_filename end |
#ladder ⇒ Object (readonly)
Returns the value of attribute ladder.
172 173 174 |
# File 'lib/gov_codes/dafecd/publication.rb', line 172 def ladder @ladder end |
#levels_key ⇒ Object (readonly)
Returns the value of attribute levels_key.
172 173 174 |
# File 'lib/gov_codes/dafecd/publication.rb', line 172 def levels_key @levels_key end |
#release_dir ⇒ Object (readonly)
Returns the value of attribute release_dir.
172 173 174 |
# File 'lib/gov_codes/dafecd/publication.rb', line 172 def release_dir @release_dir end |
#shredout_header ⇒ Object (readonly)
Returns the value of attribute shredout_header.
172 173 174 |
# File 'lib/gov_codes/dafecd/publication.rb', line 172 def shredout_header @shredout_header end |
#shredout_overrides_filename ⇒ Object (readonly)
Returns the value of attribute shredout_overrides_filename.
172 173 174 |
# File 'lib/gov_codes/dafecd/publication.rb', line 172 def shredout_overrides_filename @shredout_overrides_filename end |
#title_overrides_filename ⇒ Object (readonly)
Returns the value of attribute title_overrides_filename.
172 173 174 |
# File 'lib/gov_codes/dafecd/publication.rb', line 172 def title_overrides_filename @title_overrides_filename end |
Class Method Details
.dafecd ⇒ Publication
Returns the enlisted directory configuration.
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 |
# File 'lib/gov_codes/dafecd/publication.rb', line 111 def dafecd @dafecd ||= new( id: :dafecd, directory_name: "Department of the Air Force Enlisted Classification Directory", header: DAFECD_HEADER, ladder: DAFECD_LADDER, bare_code: nil, cem: DAFECD_CEM, glued_afsc: DAFECD_GLUED_AFSC, change_date: DAFECD_CHANGE_DATE, shredout_header: DAFECD_SHREDOUT_HEADER, levels_key: :skill_levels, code_has_specific: true, captures_shredout_acronyms: false, acronym_exclusions: %i[1D7X2 1A8X0], release_dir: "dafecd", index_filename: "enlisted.yml", title_overrides_filename: "title_overrides.yml", shredout_overrides_filename: "shredout_overrides/dafecd.yml" ) end |
.dafocd ⇒ Publication
Returns the officer directory configuration.
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 |
# File 'lib/gov_codes/dafecd/publication.rb', line 134 def dafocd @dafocd ||= new( id: :dafocd, directory_name: "Department of the Air Force Officer Classification Directory", header: DAFOCD_HEADER, ladder: DAFOCD_LADDER, bare_code: DAFOCD_BARE_CODE, cem: nil, glued_afsc: DAFOCD_GLUED_AFSC, change_date: DAFOCD_CHANGE_DATE, shredout_header: DAFOCD_SHREDOUT_HEADER, levels_key: :qual_levels, code_has_specific: false, captures_shredout_acronyms: true, acronym_exclusions: [], release_dir: "dafocd", index_filename: "officer.yml", title_overrides_filename: "title_overrides/dafocd.yml", shredout_overrides_filename: "shredout_overrides/dafocd.yml" ) end |
.detect(text) ⇒ Object
Select the publication that matches +text+'s running header, defaulting to enlisted.
158 159 160 |
# File 'lib/gov_codes/dafecd/publication.rb', line 158 def detect(text) [dafocd, dafecd].find { |pub| pub.header.match?(text) } || dafecd end |
.for(id) ⇒ Object
Look a publication up by its id symbol (:dafecd / :dafocd).
163 164 165 166 167 168 169 |
# File 'lib/gov_codes/dafecd/publication.rb', line 163 def for(id) by_id = {dafecd: dafecd, dafocd: dafocd} by_id.fetch(id.to_sym) do raise ArgumentError, "unknown publication #{id.inspect}; valid options are #{by_id.keys.map(&:inspect).join(", ")}" end end |
Instance Method Details
#captures_shredout_acronyms? ⇒ Boolean
Whether this publication extracts shredout-level acronyms (officer).
203 204 205 |
# File 'lib/gov_codes/dafecd/publication.rb', line 203 def captures_shredout_acronyms? @captures_shredout_acronyms end |
#career_field(codes) ⇒ Object
The career-field key (first two chars of the basis code).
219 220 221 222 |
# File 'lib/gov_codes/dafecd/publication.rb', line 219 def career_field(codes) return nil if codes.empty? :"#{specialty_basis(codes).first[0, 2]}" end |
#shredout_overrides_path ⇒ Object
The absolute path to this publication's shredout-overrides file.
230 231 232 |
# File 'lib/gov_codes/dafecd/publication.rb', line 230 def shredout_overrides_path File.(@shredout_overrides_filename, __dir__) end |
#specialty_key(codes) ⇒ Object
The X-form specialty key for a ladder group's concrete codes. enlisted: 1A172,1A152,... -> :1A1X2 (X at the level digit, specific kept) officer: 11B4,11B3,... -> :11BX (X at the level digit, no specific) Returns nil when no codes are given.
211 212 213 214 215 216 |
# File 'lib/gov_codes/dafecd/publication.rb', line 211 def specialty_key(codes) return nil if codes.empty? basis = specialty_basis(codes) prefix = basis.first[0, 3] @code_has_specific ? :"#{prefix}X#{most_common_specific(basis)}" : :"#{prefix}X" end |
#title_overrides_path ⇒ Object
The absolute path to this publication's title-overrides file.
225 226 227 |
# File 'lib/gov_codes/dafecd/publication.rb', line 225 def title_overrides_path File.(@title_overrides_filename, __dir__) end |