# Use case to export an org tree into some of the offered forms. class Eco::API::UseCases::Default::Locations::TagtreeExtract < Eco::API::UseCases::GraphQL::Samples::Location require_relative 'cli/tagtree_extract_cli' #cli self::Cli name "tagtree-extract" type :other include Eco::Data::Files OUT_FOLDER = "sftp".freeze OUT_TIME_FORMAT = '%Y%m%dT%H%M%S'.freeze OUT_FILENAME = "live_tree".freeze def process case format when :excel_tree, :excel_nodes excel(output_filename) do |workbook| tag_trees.each do |tree| excel_sheet(workbook, compact_name(tree.name), header: format == :excel_nodes) do |sheet| tree_extract(tree).each_with_index do |row, _idx| sheet.append_row(row) end end end end when :list, :nodes, :csv_tree, :json tag_trees.each do |tree| file(output_filename(compact_name(tree.name))) do |fd| fd << tree_extract(tree) end end else log(:error) { "Unknown selected output format '#{format}'. Abort..." } exit 1 end end private def compact_name(str) str.gsub(/\s+/, "_").gsub(/\W/, '')[0..30] end def str_node_attrs options.dig(:output, :attrs) || [:id] end def tree_extract(tree) case format when :list to_indented_list(tree) when :csv_tree with_ancestors(csv_tree(tree, attrs: str_node_attrs)).to_csv when :excel_tree with_ancestors(csv_tree(tree, attrs: str_node_attrs)).to_a when :nodes csv_list(tree).to_csv when :excel_nodes csv_list(tree).to_a when :json hash_tree(tree).to_json else log(:error) { "Unsupported or must use a different function for '#{format}'. Aboart..."} exit 1 end rescue StandardError => err log(:error) { "Something went wrong whene extracting '#{tree.name}':\n • #{err}" } exit 1 end def with_ancestors(csv) return csv unless include_ancestors? # TODO: transform table so it includes previous row's ancestors up to the node level csv end def format options.dig(:output, :format)&.to_sym || :list end def indentation options.dig(:output, :indent) || " " end def output_file_format case format when :nodes, :csv_tree 'csv' when :json then 'json' when :excel_tree, :excel_nodes 'xlsx' else # :list 'txt' end end def tag_trees @tag_trees ||= if file_as_source? [org_tree(input_csv).tap do |tree| tree.name = file_as_source end] else retrieve_live_trees end end def retrieve_live_trees session.live_trees(include_archived: include_archived?).reject do |tree| tree.empty?.tap do |rejected| log(:warn) { "Tree '#{tree.name}' does NOT have location nodes. Skipping..." } if rejected end end end def input_csv return unless file_as_source? @input_csv ||= Eco::CSV.read(file_as_source, encoding: input_encoding) end def input_encoding options.dig(:input, :file, :encoding) || 'utf-8' end def file_as_source? return true unless file_as_source.nil? false end def file_as_source @file_as_source ||= options.dig(:source, :file).tap do |file| next if file.nil? next if File.exist?(file) log(:error) { "File '#{file}' does not exist" } exit 1 end end def include_ancestors? options.dig(:output, :include, :ancestors) end def include_archived? options.dig(:output, :include, :archived) end def output_filename(name = self.class::OUT_FILENAME) File.join(output_folder, "#{timestamp}_#{config.active_enviro}_#{name}.#{output_file_format}") end def output_folder "#{config.active_enviro}/#{self.class::OUT_FOLDER}" end def timestamp(date = Time.now) date.strftime(self.class::OUT_TIME_FORMAT) end # Transforms the input `tree` into a String list of indented nodes def to_indented_list(tree, level: 0, attrs: str_node_attrs, indent: indentation) ''.tap do |result| out_str = nil sublevel = tree.top?? 0 : level + 1 str_ary = tree.nodes.map { |subtree| to_indented_list(subtree, level: sublevel) } out_str = str_ary.join("\n") unless str_ary.empty? unless tree.top? node_name = tree.as_json(include_children: false).values_at(*attrs.map(&:to_s)).join(" ## ") out_head = "#{indent * level}#{node_name}" out_str = out_str ? "#{out_head}\n#{out_str}" : out_head end result << out_str end end def csv(filename, header = []) CSV.open(filename, "w") do |csv| csv << header unless header.empty? yield(csv) end ensure log(:info) { "Created file: #{filename}" } end def file(filename, &block) File.open(filename, "w", &block) ensure log(:info) { "Created file: #{filename}" } end def excel(filename) require 'fast_excel' FastExcel.open(filename, constant_memory: true).tap do |workbook| yield(workbook) workbook.close end log(:info) { "Created file: #{filename}" } end def excel_sheet(workbook, name, header: true) #unless sheet = workbook.get_worksheet_by_name(name) sheet = workbook.add_worksheet(name) #end sheet.auto_width = true yield(sheet) return unless header sheet.set_row(0, 30, workbook.bold_format) sheet.freeze_panes(1, 0) end end