Class: Genericode::CodeList

Inherits:
Lutaml::Model::Serializable
  • Object
show all
Defined in:
lib/genericode/code_list.rb

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.from_file(file_path) ⇒ Object



20
21
22
23
24
25
26
27
28
29
# File 'lib/genericode/code_list.rb', line 20

def self.from_file(file_path)
  content = File.read(file_path)
  if file_path.end_with?(".gc")
    from_xml(content)
  elsif file_path.end_with?(".gcj")
    from_json(content)
  else
    raise Error, "Unsupported file format. Expected .gc or .gcj file."
  end
end

Instance Method Details

#column_set_from_json(model, value) ⇒ Object



40
41
42
# File 'lib/genericode/code_list.rb', line 40

def column_set_from_json(model, value)
  model.column_set = ColumnSet.of_json({ "Column" => value })
end

#column_set_to_json(model, doc) ⇒ Object



44
45
46
# File 'lib/genericode/code_list.rb', line 44

def column_set_to_json(model, doc)
  doc["Columns"] = Column.as_json(model.column_set.column)
end

#key_from_json(model, value) ⇒ Object



48
49
50
# File 'lib/genericode/code_list.rb', line 48

def key_from_json(model, value)
  model.column_set.key = Key.of_json(value)
end

#key_to_json(model, doc) ⇒ Object



52
53
54
# File 'lib/genericode/code_list.rb', line 52

def key_to_json(model, doc)
  doc["Keys"] = Key.as_json(model.column_set.key)
end

#lookup(path) ⇒ Object

Raises:



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/genericode/code_list.rb', line 85

def lookup(path)
  parts = path.split(">")
  conditions = parts[0].split(",").to_h { |c| c.split(":") }
  target_column = parts[1]

  result = simple_code_list.row.find do |row|
    conditions.all? do |col, value|
      column = column_set.column.find { |c| c.short_name.content.downcase == col.downcase }
      raise Error, "Column not found: #{col}" unless column

      row_value = row.value.find { |v| v.column_ref == column.id }&.simple_value&.content
      row_value == value
    end
  end

  raise Error, "No matching row found for path: #{path}" unless result

  if target_column
    column = column_set.column.find { |c| c.short_name.content.downcase == target_column.downcase }
    raise Error, "Target column not found: #{target_column}" unless column

    result.value.find { |v| v.column_ref == column.id }&.simple_value&.content
  else
    result.value.to_h do |v|
      [column_set.column.find do |c|
        c.id == v.column_ref
      end.short_name.content, v.simple_value.content,]
    end
  end
end

#simple_code_list_from_json(model, value) ⇒ Object



56
57
58
59
60
61
62
63
64
65
66
# File 'lib/genericode/code_list.rb', line 56

def simple_code_list_from_json(model, value)
  rows = value.map do |x|
    values = x.map do |k, v|
      Value.new(column_ref: k, simple_value: SimpleValue.new(content: v))
    end

    Row.new(value: values)
  end

  model.simple_code_list = SimpleCodeList.new(row: rows)
end

#simple_code_list_to_json(model, doc) ⇒ Object



68
69
70
71
72
# File 'lib/genericode/code_list.rb', line 68

def simple_code_list_to_json(model, doc)
  doc["Codes"] = model.simple_code_list.row.map do |row|
    row.value.to_h { |v| [v.column_ref, v.simple_value.content] }
  end
end

#valid?Boolean

Returns:

  • (Boolean)


116
117
118
# File 'lib/genericode/code_list.rb', line 116

def valid?
  validate_verbose.empty?
end

#validate_verboseObject



120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
# File 'lib/genericode/code_list.rb', line 120

def validate_verbose
  errors = []

  # Rule 1: ColumnSet presence
  if column_set.nil? || column_set.column.empty?
    errors << { code: "MISSING_COLUMN_SET",
                message: "ColumnSet is missing or empty", }
  end

  # Rule 2: SimpleCodeList presence
  if simple_code_list.nil? || simple_code_list.row.empty?
    errors << { code: "MISSING_SIMPLE_CODE_LIST",
                message: "SimpleCodeList is missing or empty", }
  end

  # Rule 3: Unique column IDs
  column_ids = column_set&.column&.map(&:id) || []
  if column_ids.uniq.length != column_ids.length
    errors << { code: "DUPLICATE_COLUMN_IDS", message: "Duplicate column IDs found" }
  end

  # Rule 4: Verify ColumnRef values
  simple_code_list&.row&.each_with_index do |row, index|
    row.value.each do |value|
      unless column_ids.include?(value.column_ref)
        errors << { code: "INVALID_COLUMN_REF",
                    message: "Invalid ColumnRef '#{value.column_ref}' in row #{index + 1}", }
      end
    end
  end

  # Rule 5: Unique values in columns
  column_set&.column&.each do |col|
    column_values = (simple_code_list&.row&.map do |row|
      row.value.find { |v| v.column_ref == col.id }&.simple_value&.content
    end || []).compact

    if column_values.uniq.length != column_values.length
      errors << { code: "DUPLICATE_VALUES", message: "Duplicate values found in column '#{col.id}'" }
    end
  end

  # Rule 6: Required column values
  required_columns = column_set&.column&.select { |col| col.use == "required" } || []
  simple_code_list&.row&.each_with_index do |row, index|
    required_columns.each do |col|
      unless row.value.any? { |v| v.column_ref == col.id && v.simple_value&.content }
        errors << { code: "MISSING_REQUIRED_VALUE",
                    message: "Missing value for required column '#{col.short_name&.content}' in row #{index + 1}", }
      end
    end
  end

  # Rule 7: Data type consistency
  column_set&.column&.each do |col|
    data_type = col.data&.type
    simple_code_list&.row&.each_with_index do |row, index|
      value = row.value.find { |v| v.column_ref == col.id }&.simple_value&.content
      unless value_matches_type?(value, data_type)
        errors << { code: "INVALID_DATA_TYPE",
                    message: "Invalid data type for column '#{col.short_name&.content}' in row #{index + 1}", }
      end
    end
  end

  # Rule 8: Valid canonical URIs
  if identification&.canonical_uri && !valid_uri?(identification.canonical_uri)
    errors << { code: "INVALID_CANONICAL_URI", message: "Invalid canonical URI" }
  end

  # Rule 19: Datatype ID validation
  column_set&.column&.each do |col|
    if col.data&.type && !valid_datatype_id?(col.data.type)
      errors << { code: "INVALID_DATATYPE_ID",
                  message: "Invalid datatype ID for column '#{col.short_name&.content}'", }
    end

    # Rule 20 and 22: Complex data validation
    if col.data&.type == "*" && col.data&.datatype_library != "*"
      errors << { code: "INVALID_COMPLEX_DATA",
                  message: "Invalid complex data configuration for column '#{col.short_name&.content}'", }
    end

    # Rule 23: Language attribute validation
    if col.data&.lang && col.data_restrictions&.lang
      errors << { code: "DUPLICATE_LANG_ATTRIBUTE",
                  message: "Duplicate lang attribute for column '#{col.short_name&.content}'", }
    end
  end

  # Rule 38: Implicit column reference
  simple_code_list&.row&.each_with_index do |row, index|
    unless row.value.all?(&:column_ref)
      errors << { code: "MISSING_COLUMN_REF", message: "Missing explicit column reference in row #{index + 1}" }
    end
  end

  # Rule 39: ShortName whitespace check
  column_set&.column&.each do |col|
    if col.short_name&.content&.match?(/\s/)
      errors << { code: "INVALID_SHORT_NAME",
                  message: "ShortName '#{col.short_name&.content}' contains whitespace", }
    end
  end

  # Rule 42 and 43: ComplexValue validation
  simple_code_list&.row&.each_with_index do |row, index|
    row.value.each do |value|
      next unless value.complex_value

      unless valid_complex_value?(value.complex_value, column_set&.column&.find { |c| c.id == value.column_ref })
        errors << { code: "INVALID_COMPLEX_VALUE",
                    message: "Invalid ComplexValue in row #{index + 1}, column '#{value.column_ref}'", }
      end
    end
  end

  errors
end