lib/eco/api/common/people/default_parsers/csv_parser.rb in eco-helpers-2.0.25 vs lib/eco/api/common/people/default_parsers/csv_parser.rb in eco-helpers-2.0.26

- old
+ new

@@ -1,10 +1,12 @@ class Eco::API::Common::People::DefaultParsers::CSVParser < Eco::API::Common::Loaders::Parser attribute :csv def parser(data, deps) - Eco::CSV.parse(data, headers: true, skip_blanks: true).each_with_object([]) do |row, arr_hash| + Eco::CSV.parse(data, headers: true, skip_blanks: true).tap do |table| + check_headers(table) if deps[:check_headers] + end.each_with_object([]) do |row, arr_hash| row_hash = row.headers.uniq.each_with_object({}) do |attr, hash| next if attr.to_s.strip.empty? hash[attr.strip] = parse_string(row[attr]) end arr_hash.push(row_hash) @@ -32,8 +34,98 @@ def null?(value) return true if !value str = value.strip.upcase ["NULL"].any? {|token| str == token} + end + + def check_headers(table) + headers = table.headers + missing = missing_headers(headers) + unknown = unknown_headers(headers) + unless missing.empty? && unknown.empty? + msg = "Detected possible HEADER ISSUES !!!\n" + msg << "There might be Missing or Wrong HEADER names in the CSV file:\n" + msg << " * UNKNOWN (or not used?): #{unknown}\n" unless unknown.empty? + msg << " * MISSING DIRECT: #{missing[:direct]}\n" unless (missing[:direct] || []).empty? + unless (data = missing[:indirect] || []).empty? + msg << " * MISSING INDIRECT:\n" + data.each do |ext, info| + msg << " - '#{ext}' => " + msg << (info[:attrs] || {}).map do |status, attrs| + if status == :inactive + "makes inactive: #{attrs}" + elsif status == :active + "there could be missing info in: #{attrs}" + end + end.compact.join("; ") + "\n" + end + end + logger.warn(msg) + sleep(2) + end + end + + def unknown_headers(headers) + (headers - known_headers) - all_internal_attrs + end + + def missing_headers(headers) + hint = headers & all_internal_attrs + hext = headers - hint + int_head = hint + hext.map {|e| fields_mapper.to_internal(e)}.compact + known_as_int = known_headers.select do |e| + i = fields_mapper.to_internal(e) + int_head.include?(i) + end + ext = headers.select do |e| + i = fields_mapper.to_internal(e) + int_head.include?(i) + end + ext_present = known_as_int | ext + ext_miss = known_headers - ext_present + #int_miss = ext_miss.map {|ext| fields_mapper.to_internal(ext)} + ext_miss.each_with_object({}) do |ext, missing| + next unless int = fields_mapper.to_internal(ext) + if all_internal_attrs.include?(int) + missing[:direct] ||= [] + missing[:direct] << ext + end + related_attrs_requirements = required_attrs.values.select do |req| + req.dependant?(int) && !int_head.include?(req.attr) + end + next if related_attrs_requirements.empty? + missing[:indirect] ||= {} + data = missing[:indirect][ext] = {} + data[:int] = int + data[:attrs] = {} + related_attrs_requirements.each_with_object(data[:attrs]) do |req, attrs| + status = req.active?(*int_head) ? :active : :inactive + attrs[status] ||= [] + attrs[status] << req.attr + end + end + end + + def known_headers + @known_headers ||= fields_mapper.list(:external).compact + end + + def fields_mapper + session.fields_mapper + end + + def required_attrs + @required_attrs ||= person_parser.required_attrs.each_with_object({}) do |ra, out| + out[ra.attr] = ra + end + end + + def all_internal_attrs + person_parser.all_attrs(include_defined_parsers: true) + end + + def person_parser + session.entry_factory.person_parser end end