Class: Eco::API::Common::People::EntryFactory

Inherits:
Session::BaseSession show all
Includes:
Data::Files
Defined in:
lib/eco/api/common/people/entry_factory.rb

Overview

TODO: EntryFactory should suppport multiple schemas itself (rather that being done on Session) => currently, it's through session.entry_factory(schema: id), but this is wrong => This way, Entries and PersonEntry will be able to refer to attr_map and person_parser linked to schema_id => "schema_id" should be an optional column in the input file, or parsable via a custom parser to scope the schema Helper factory class to generate entries (input entries).

Constant Summary

Constants included from Data::Files

Data::Files::DEFAULT_TIMESTAMP_PATTERN

Instance Attribute Summary collapse

Attributes inherited from Session::BaseSession

#api, #config, #environment, #file_manager, #logger, #session

Instance Method Summary collapse

Methods included from Data::Files::ClassMethods

#copy_file, #create_directory, #csv_files, #dir_exists?, #encoding, #file_basename, #file_empty?, #file_exists?, #file_fullpath, #file_name, #file_path, #folder_files, #has_bom?, #script_subfolder, #split, #timestamp, #timestamp_file

Methods inherited from Session::BaseSession

#enviro=, #fm, #mailer, #mailer?, #s3uploader, #s3uploader?, #sftp, #sftp?

Constructor Details

#initialize(e, schema:, person_parser: nil, default_parser: nil, attr_map: nil) ⇒ EntryFactory

Returns a new instance of EntryFactory.

Parameters:

  • e (Eco::API::Common::Session::Environment)

    requires a session environment, as any child of Eco::API::Common::Session::BaseSession

  • schema (Ecoportal::API::V1::PersonSchema)

    schema of person details that the parser will be based upon.

  • person_parser (nil, Eco::API::Common::People::PersonParser) (defaults to: nil)

    set of attribute, type and format parsers/serializers.

  • attr_map (nil, Eco::Data::Mapper) (defaults to: nil)

    attribute names mapper to translate external names into internal ones and vice versa.



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# File 'lib/eco/api/common/people/entry_factory.rb', line 20

def initialize(e, schema:, person_parser: nil, default_parser: nil, attr_map: nil)
  fatal "Constructor needs a PersonSchema. Given: #{schema}" if !schema.is_a?(Ecoportal::API::V1::PersonSchema)
  fatal "Expecting PersonParser. Given: #{person_parser}"    if person_parser && !person_parser.is_a?(Eco::API::Common::People::PersonParser)
  fatal "Expecting Mapper object. Given: #{fields_mapper}"   if attr_map && !attr_map.is_a?(Eco::Data::Mapper)
  super(e)

  @schema = Ecoportal::API::V1::PersonSchema.new(JSON.parse(schema.doc.to_json))
  @source_person_parser = person_parser

  # load default parser + custom parsers
  @default_parser = default_parser&.new(schema: @schema) || Eco::API::Common::People::DefaultParsers.new(schema: @schema)
  base_parser = @default_parser.merge(@source_person_parser)
  # new parser with linked schema
  @person_parser = @source_person_parser.new(schema: @schema).merge(base_parser)
  @person_parser_patch_version = @source_person_parser.patch_version
  @attr_map = attr_map
end

Instance Attribute Details

#person_parserEco::API::Common::People::PersonParser (readonly)

Note:

if the custom person parser has changed, it updates the copy of this EntryFactory instance

provides with a Eco::API::Common::People::PersonParser object (collection of attribute parsers)

Returns:



52
53
54
# File 'lib/eco/api/common/people/entry_factory.rb', line 52

def person_parser
  @person_parser
end

#schemaEcoportal::API::V1::PersonSchema (readonly)

person schema to be used in this entry factory

Returns:

  • (Ecoportal::API::V1::PersonSchema)

    the current value of schema



11
12
13
# File 'lib/eco/api/common/people/entry_factory.rb', line 11

def schema
  @schema
end

Instance Method Details

#entries(data: (no_data = true; nil), file: (no_file = true; nil), format: (no_format = true; nil), **options) ⇒ Eco::API::Common::People::Entries

Helper that provides a collection of Entries, which in turn provides with further helpers to find and exclude entries. It accepts a file: and format: or data: but not both options together.

Parameters:

  • data (Array<Hash>) (defaults to: (no_data = true; nil))

    data to be parsed. It cannot be used alongside with file:

  • file (String) (defaults to: (no_file = true; nil))

    absolute or relative path to the input file. It cannot be used alongside with data:.

  • format (Symbol) (defaults to: (no_format = true; nil))

    it must be used when you use the option file: (i.e. :xml, :csv), as it specifies the format of the input file:.

  • options (Hash)

    further options.

Options Hash (**options):

  • :encoding (String)

    optional parameter to read file: by expecting certain encoding.

  • :check_headers (Boolean)

    signals if the csv file headers should be expected.

Returns:

Raises:

  • Exception

    • if you try to provide data: and file: at the same time.
    • if you provide file: but omit format:.
    • if the format: you provide is not a Symbol.
    • if there is no parser/serializer defined for format:.


88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/eco/api/common/people/entry_factory.rb', line 88

def entries(data: (no_data = true; nil), file: (no_file = true; nil), format: (no_format = true; nil), **options)
  fatal("You should at least use data: or file:, but not both") if no_data == no_file
  fatal("You must specify a valid format: (symbol) when you use file.") if file && no_format
  fatal("Format should be a Symbol. Given '#{format}'") if format && !format.is_a?(Symbol)
  fatal("There is no parser/serializer for format ':#{format.to_s}'") unless no_format || @person_parser.defined?(format)

  options.merge!(content:  data)     unless no_data
  options.merge!(file:     file)     unless no_file
  options.merge!(format:   format)   unless no_format

  Entries.new(to_array_of_hashes(**options), klass: PersonEntry, factory: self)
end

#export(data:, file: "export", format: :csv, encoding: "utf-8", internal_names: false) ⇒ Void

Helper that generates a file out of data:.

Parameters:

  • data (Eco::API::Organization::People)

    data to be parsed.

  • file (String) (defaults to: "export")

    absolute or relative path to the ouput file.

  • format (Symbol) (defaults to: :csv)

    it specifies the format of the output file: (i.e. :xml, :csv). There must be a parser/serializer defined for it.

  • encoding (String) (defaults to: "utf-8")

    optional parameter to geneate file: content by unsing certain encoding.

Returns:

  • (Void)

    .

Raises:

  • Exception

    • if you try to provide data: in the wrong format.
    • if you file: is empty.
    • if the format: you provide is not a Symbol.
    • if there is no parser/serializer defined for format:.


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
# File 'lib/eco/api/common/people/entry_factory.rb', line 162

def export(data:, file: "export", format: :csv, encoding: "utf-8", internal_names: false)
  fatal("data: Expected Eco::API::Organization::People object. Given: #{data.class}") unless data.is_a?(Eco::API::Organization::People)
  fatal("A file should be specified.") unless !file.to_s.strip.empty?
  fatal("Format should be a Symbol. Given '#{format}'") if format && !format.is_a?(Symbol)
  fatal("There is no parser/serializer for format ':#{format.to_s}'") unless @person_parser.defined?(format)

  run = true
  if self.class.file_exists?(file)
    prompt_user("Do you want to overwrite it? (Y/n):", explanation: "The file '#{file}' already exists.", default: "Y") do |response|
      run = (response == "") || response.upcase.start_with?("Y")
    end
  end

  if run
    deps = {"supervisor_id" => {people: data}}

    data_entries = data.map do |person|
      self.new(person, dependencies: deps).yield_self do |entry|
        internal_names ? entry.mapped_entry : entry.external_entry
      end
    end

    File.open(file, "w", enconding: encoding) do |fd|
      fd.write(person_parser.serialize(format, data_entries))
    end
  end
end

#new(data, dependencies: {}) ⇒ Eco::API::Common::People::PersonEntry

Note:

this method is necessary to make the factory object work as a if it was a class PersonEntry you can call new on.

key method to generate objects of PersonEntry that share dependencies via this EntryFactory environment.

Parameters:

  • data (Hash, Person)

    data to be parsed/serialized. Parsed: the external hashed entry. Serialized: a Person object.

Returns:



64
65
66
67
68
69
70
71
72
# File 'lib/eco/api/common/people/entry_factory.rb', line 64

def new(data, dependencies: {})
  PersonEntry.new(
    data,
    person_parser: person_parser,
    attr_map:      @attr_map,
    dependencies:  dependencies,
    logger:        logger
  )
end

#newFactory(schema: nil) ⇒ Object



38
39
40
41
42
43
44
45
46
# File 'lib/eco/api/common/people/entry_factory.rb', line 38

def newFactory(schema: nil)
  self.class.new(
    environment,
    schema: schema,
    person_parser: @source_person_parser,
    default_parser: @default_parser,
    attr_map: @attr_map
  )
end

#to_array_of_hashes(**kargs) ⇒ Object



101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
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
# File 'lib/eco/api/common/people/entry_factory.rb', line 101

def to_array_of_hashes(**kargs)
  data = []
  content, file, encoding, format = kargs.values_at(:content, :file, :encoding, :format)

  # Support for multiple file
  if file.is_a?(Array)
    return file.each_with_object([]) do |f, out|
      logger.info("Parsing file '#{f}'")
      curr = to_array_of_hashes(**kargs.merge(file: f))
      out.concat(curr)
    end
  end
  # Get content only when it's not :xls
  # note: even if content was provided, file takes precedence
  if (format != :xls) && file
    content = get_file_content(file, encoding)
  end
  #content = get_file_content(file, format, encoding) if (format != :xls) && file

  case content
  when Hash
    logger.error("Input data as 'Hash' not supported. Expecting 'Enumerable' or 'String'")
    exit(1)
  when String
    deps = {check_headers: true} if kargs[:check_headers]
    to_array_of_hashes(content: person_parser.parse(format, content, deps: deps || {}))
  when Enumerable
    sample = content.to_a.first
    case sample
    when Hash, Array, ::CSV::Row
      Eco::CSV::Table.new(content).to_array_of_hashes
    else
      logger.error("Input content 'Array' of '#{sample.class}' is not supported.")
    end
  else
    if file && format == :xls
      person_parser.parse(format, file)
    else
      logger.error("Could not obtain any data out of these: #{kargs}. Given content: '#{content.class}'")
      exit(1)
    end
  end.tap do |out_array|
    start_from_two = (format == :csv) || format == :xls
    out_array.each_with_index do |entry_hash, i|
      entry_hash["idx"] = start_from_two ? i + 2 : i + 1
      entry_hash["source_file"] = file
    end
  end
end