Class: Opencdd::Parcel::SheetSchema

Inherits:
Object
  • Object
show all
Includes:
Enumerable
Defined in:
lib/opencdd/parcel/sheet_schema.rb

Defined Under Namespace

Classes: Column

Constant Summary collapse

VARIANT_TO_CANONICAL =

Parcel-specific column-ID canonicalization. Maps:

  • ParcelMaker language-variant headers (MDC_P004_1, MDC_P005, ...) to the canonical IEC 61360 IDs (MDC_P004, MDC_P006, ...).
  • cdd.iec.ch search-export's project-specific IDs for DET classification (IECCDD_001_C0001, IECCDD_001_C0002., ...) to the canonical IEC 61360 IDs (MDC_P001_5, MDC_P004_1., ...).

Owned by SheetSchema because these mappings only exist for the Parcel sheet layout — the PropertyIds registry is ontology-only and shouldn't carry format-specific details.

{
  "MDC_P004_1" => "MDC_P004",
  "MDC_P004_2" => "MDC_P007",
  "MDC_P004_3" => "MDC_P005",
  "MDC_P005"   => "MDC_P006",
  "MDC_P007_1" => "MDC_P008",
  "MDC_P007_2" => "MDC_P009",

  # cdd.iec.ch search-export DET classification aliases.
  # The file uses IEC-internal supplier scheme (IECCDD_001) and
  # project-specific column IDs (C0001 = code, C0002.<lang> = name)
  # rather than the standard MDC codes. Without these mappings,
  # every DET classification entity would have nil code + name.
  #
  # C0002 maps to MDC_P004 (preferred_name), not MDC_P004_1
  # (preferred_name_localized), because canonical_id splits the
  # language suffix off and reapplies it after the base lookup —
  # so the per-language entries (C0002.en, C0002.fr, etc.) below
  # would be dead code. The base entry alone produces
  # MDC_P004.<lang>, which is what the preferred_name field DSL
  # reads via FieldRegistry.
  "IECCDD_001_C0001"     => "MDC_P001_5",
  "IECCDD_001_C0002"     => "MDC_P004",
}.freeze
DIRECTIVE_ROWS =
%w[
  PROPERTY_ID
  ALTERNATE_ID
  SUPER_ALTERNATE_ID
  SUB_ALTERNATE_ID
  EQUIVALENT_ID
  SUPER_PROPERTY
  PROPERTY_NAME
  DEFINITION
  NOTE
  DATATYPE
  UNIT
  VARIABLE_PREFIX_UNIT
  UNIT_ID
  ALTERNATIVE_UNITS
  VALUE_FORMAT
  PATTERN
  RELATION
  DEFAULT_VALUE
  DEFAULT_DATA_SUPPLIER
  DEFAULT_DATA_VERSION
  REQUIREMENT
].freeze
DIRECTIVE_ROW_PREFIX =
"#".freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeSheetSchema

Returns a new instance of SheetSchema.



137
138
139
140
141
# File 'lib/opencdd/parcel/sheet_schema.rb', line 137

def initialize
  @columns = []
  @columns_by_id = {}
  @column_directives = {}
end

Instance Attribute Details

#column_directivesObject (readonly)

Returns the value of attribute column_directives.



135
136
137
# File 'lib/opencdd/parcel/sheet_schema.rb', line 135

def column_directives
  @column_directives
end

#columnsObject (readonly)

Returns the value of attribute columns.



135
136
137
# File 'lib/opencdd/parcel/sheet_schema.rb', line 135

def columns
  @columns
end

#columns_by_idObject (readonly)

Returns the value of attribute columns_by_id.



135
136
137
# File 'lib/opencdd/parcel/sheet_schema.rb', line 135

def columns_by_id
  @columns_by_id
end

#normalized_language_codesObject (readonly)

Returns the value of attribute normalized_language_codes.



135
136
137
# File 'lib/opencdd/parcel/sheet_schema.rb', line 135

def normalized_language_codes
  @normalized_language_codes
end

Class Method Details

.canonical_id(raw_id) ⇒ Object

Canonicalize a Parcel column ID. Splits language tags (.) so they round-trip cleanly. Returns the canonical ID (or the input unchanged if no mapping exists). Language codes are normalized to ISO 639-1 via Opencdd::Parcel::LanguageAliases.normalize.



47
48
49
50
51
52
53
54
55
56
# File 'lib/opencdd/parcel/sheet_schema.rb', line 47

def self.canonical_id(raw_id)
  return nil if raw_id.nil?
  s = raw_id.to_s.strip
  return nil if s.empty?
  match = s.match(/\.(?<lang>[A-Za-z0-9-]+)\z/)
  base = match ? match.pre_match : s
  lang = match && match[:lang]
  canonical = VARIANT_TO_CANONICAL[base] || base
  lang ? "#{canonical}.#{Opencdd::Parcel::LanguageAliases.normalize(lang)}" : canonical
end

.from_header_rows(rows) ⇒ Object



143
144
145
146
147
148
149
150
# File 'lib/opencdd/parcel/sheet_schema.rb', line 143

def self.from_header_rows(rows)
  schema = new
  rows.each do |row|
    schema.add_directive_row(row)
  end
  schema.finalize!
  schema
end

Instance Method Details

#[](property_id_or_name) ⇒ Object



237
238
239
# File 'lib/opencdd/parcel/sheet_schema.rb', line 237

def [](property_id_or_name)
  find_by_property_id(property_id_or_name) || find_by_name(property_id_or_name)
end

#add_column(column) ⇒ Object



172
173
174
175
176
# File 'lib/opencdd/parcel/sheet_schema.rb', line 172

def add_column(column)
  @columns << column
  @columns_by_id[column.property_id] = column
  self
end

#add_directive_row(row) ⇒ Object



152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/opencdd/parcel/sheet_schema.rb', line 152

def add_directive_row(row)
  return self if row.nil? || row.empty?

  label_cell = row[0].to_s
  directive = parse_directive(label_cell)
  return self unless directive

  values = row[1..].to_a

  unless @column_directives.key?(directive)
    @column_directives[directive] = []
  end

  values.each_with_index do |val, idx|
    @column_directives[directive][idx] = val
  end

  self
end

#each(&block) ⇒ Object



253
254
255
# File 'lib/opencdd/parcel/sheet_schema.rb', line 253

def each(&block)
  @columns.each(&block)
end

#finalize!Object



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
# File 'lib/opencdd/parcel/sheet_schema.rb', line 182

def finalize!
  ids = @column_directives["PROPERTY_ID"] || []
  if ids.empty? || ids.all? { |id| id.nil? || id.to_s.strip.empty? }
    ids = synthesize_ids_from_names
  end
  ids.each_with_index do |id, idx|
    next if id.nil? || id.to_s.strip.empty?

    col = Column.new(
      index: idx + 1,
      property_id: Opencdd::Parcel::SheetSchema.canonical_id(id.to_s.strip),
      raw_property_id: id.to_s.strip,
      alternate_id: lookup("ALTERNATE_ID", idx),
      super_alternate_id: lookup("SUPER_ALTERNATE_ID", idx),
      sub_alternate_id: lookup("SUB_ALTERNATE_ID", idx),
      equivalent_id: lookup("EQUIVALENT_ID", idx),
      super_property: lookup("SUPER_PROPERTY", idx),
      name_by_lang: lang_hash_for("PROPERTY_NAME", idx),
      definition_by_lang: lang_hash_for("DEFINITION", idx),
      note_by_lang: lang_hash_for("NOTE", idx),
      datatype: lookup("DATATYPE", idx),
      unit: lookup("UNIT", idx),
      variable_prefix_unit: lookup("VARIABLE_PREFIX_UNIT", idx),
      unit_id: lookup("UNIT_ID", idx),
      alternative_units: lookup("ALTERNATIVE_UNITS", idx),
      value_format: lookup("VALUE_FORMAT", idx),
      pattern: lookup("PATTERN", idx),
      relation: lookup("RELATION", idx),
      default_value: lookup("DEFAULT_VALUE", idx),
      default_data_supplier: lookup("DEFAULT_DATA_SUPPLIER", idx),
      default_data_version: lookup("DEFAULT_DATA_VERSION", idx),
      requirement: lookup("REQUIREMENT", idx),
    )
    @columns << col
    @columns_by_id[col.property_id] = col
  end

  @normalized_language_codes = compute_normalized_language_codes.freeze
  freeze
end

#finalize_for_scaffold!Object



178
179
180
# File 'lib/opencdd/parcel/sheet_schema.rb', line 178

def finalize_for_scaffold!
  freeze
end

#find_by_name(name, lang = :en) ⇒ Object



245
246
247
# File 'lib/opencdd/parcel/sheet_schema.rb', line 245

def find_by_name(name, lang = :en)
  @columns.find { |c| c.name(lang).to_s.casecmp(name.to_s).zero? }
end

#find_by_property_id(id) ⇒ Object



241
242
243
# File 'lib/opencdd/parcel/sheet_schema.rb', line 241

def find_by_property_id(id)
  @columns_by_id[id.to_s]
end

#sizeObject



249
250
251
# File 'lib/opencdd/parcel/sheet_schema.rb', line 249

def size
  @columns.size
end

#synthesize_ids_from_namesObject

Delegates to Entity::NameSynthesizer when the sheet has no #PROPERTY_ID directive row. See name_synthesizer.rb.



225
226
227
228
229
230
231
232
233
234
235
# File 'lib/opencdd/parcel/sheet_schema.rb', line 225

def synthesize_ids_from_names
  names = nil
  @column_directives.each do |key, vals|
    base, = key.split(".", 2)
    next unless base == "PROPERTY_NAME"
    names = vals
    break
  end
  return [] unless names
  Opencdd::Entity::NameSynthesizer.synthesize(names)
end