gov_codes 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -17,7 +17,8 @@ module GovCodes
17
17
  career_group: nil,
18
18
  functional_area: nil,
19
19
  qualification_level: nil,
20
- shredout: nil
20
+ shredout: nil,
21
+ specific_afsc: nil
21
22
  }
22
23
 
23
24
  # Scan for prefix (optional)
@@ -26,20 +27,24 @@ module GovCodes
26
27
  # Scan for career group (two digits)
27
28
  career_group = scanner.scan(/\d{2}/)
28
29
  return result unless career_group
29
- result[:career_group] = career_group
30
+ result[:career_group] = career_group.to_sym
30
31
 
31
32
  # Scan for functional area (uppercase letter)
32
33
  functional_area = scanner.scan(/[A-Z]/)
33
34
  return result unless functional_area
34
- result[:functional_area] = functional_area
35
+ result[:functional_area] = functional_area.to_sym
35
36
 
36
- # Scan for qualification level (digit 0-4)
37
- qualification_level = scanner.scan(/[0-4]/)
37
+ # Scan for qualification level (digit 0-4 or letter X-Z)
38
+ qualification_level = scanner.scan(/[0-4A-Z]/)
38
39
  return result unless qualification_level
39
- result[:qualification_level] = qualification_level
40
+ result[:qualification_level] = qualification_level.to_sym
41
+
42
+ # Build specific AFSC
43
+ result[:specific_afsc] = :"#{result[:career_group]}#{result[:functional_area]}#{result[:qualification_level]}"
40
44
 
41
45
  # Scan for shredout (optional)
42
- result[:shredout] = scanner.scan(/[A-Z]/)
46
+ shredout = scanner.scan(/[A-Z]/)
47
+ result[:shredout] = shredout&.to_sym
43
48
 
44
49
  # Check if we've reached the end of the string
45
50
  return result unless scanner.eos?
@@ -49,6 +54,7 @@ module GovCodes
49
54
  end
50
55
 
51
56
  extend GovCodes::DataLoader
57
+
52
58
  DATA = data
53
59
 
54
60
  Code = Data.define(
@@ -57,9 +63,40 @@ module GovCodes
57
63
  :functional_area,
58
64
  :qualification_level,
59
65
  :shredout,
66
+ :specific_afsc,
60
67
  :name
61
68
  )
62
69
 
70
+ def self.find_name_recursive(result)
71
+ name = nil
72
+ data = DATA
73
+
74
+ # For officer codes, build the lookup key from career group + functional area + qual level
75
+ # like "11BX" where "11" is career group, "B" is functional area, "X" is qual level
76
+ career_group = result[:career_group].to_s
77
+ functional_area = result[:functional_area].to_s
78
+ qual_level = result[:qualification_level].to_s
79
+ combined_key = :"#{career_group}#{functional_area}#{qual_level}"
80
+
81
+ # Look for the full code (e.g., "11BX", "11MX")
82
+ if data[combined_key]
83
+ name = data[combined_key][:name]
84
+ data = data[combined_key][:subcategories]
85
+
86
+ # Then look for shredout
87
+ if data && result[:shredout]
88
+ shred = result[:shredout]
89
+ # Try Symbol first (most shredouts are letters), then Integer, then String
90
+ lookup_value = data[shred] || data[shred.to_s.to_i] || data[shred.to_s]
91
+ if lookup_value
92
+ name = lookup_value.is_a?(Hash) ? (lookup_value[:name] || name) : (lookup_value || name)
93
+ end
94
+ end
95
+ end
96
+
97
+ name || "Unknown"
98
+ end
99
+
63
100
  def self.find(code)
64
101
  code = code.to_s
65
102
  parser = Parser.new(code)
@@ -67,18 +104,41 @@ module GovCodes
67
104
 
68
105
  return nil if result.reject { |_, v| v.nil? }.empty?
69
106
 
70
- # Find the name from the codes data
71
- career_group = result[:career_group]
72
- functional_area = result[:functional_area]
73
-
74
- # Look up the name in the codes hash
75
- name = find_name(career_group, functional_area)
107
+ # Find the name by recursively searching the codes hash
108
+ name = find_name_recursive(result)
76
109
 
77
110
  # Add the name to the result
78
111
  result[:name] = name
79
112
 
80
113
  Code.new(**result)
81
114
  end
115
+
116
+ def self.search(prefix)
117
+ results = []
118
+ prefix = prefix.to_s.upcase
119
+ collect_codes_recursive(DATA, "", prefix, results)
120
+ results.map { |code| find(code) }.compact
121
+ end
122
+
123
+ def self.collect_codes_recursive(data, current_code, prefix, results)
124
+ return unless data.is_a?(Hash)
125
+
126
+ data.each do |key, value|
127
+ code = "#{current_code}#{key}"
128
+
129
+ if value.is_a?(Hash) && value[:name]
130
+ # This is a node with a name and possibly subcategories
131
+ results << code if code.start_with?(prefix)
132
+ collect_codes_recursive(value[:subcategories], code, prefix, results) if value[:subcategories]
133
+ elsif value.is_a?(String)
134
+ # This is a leaf node (simple string value)
135
+ results << code if code.start_with?(prefix)
136
+ elsif value.is_a?(Hash)
137
+ # Nested subcategories without a name at this level
138
+ collect_codes_recursive(value, current_code, prefix, results)
139
+ end
140
+ end
141
+ end
82
142
  end
83
143
  end
84
144
  end