module Lionel class Export include Configurable attr_reader :options config_accessor :google_doc_id, :trello_board_id def self.builder=(builder) @builder = builder end def self.builder @builder || ExportBuilder.default end def initialize(options = {}) @options = options end def data { trello_board_id: trello_board_id, google_doc_id: google_doc_id } end def board @board ||= Trello::Board.find(trello_board_id) end def cards # iterate over active lists rather # than retrieving all historical cards; # trello api returns association proxy # that does not respond to "flatten" @cards ||= begin case options.fetch('filter', 'open-lists') when 'open-cards' retrieve_open_cards when 'open-lists' retrieve_open_cards_in_open_lists end.map { |c| Lionel::ProxyCard.new(c) } end end def spreadsheet @spreadsheet ||= google_session.spreadsheet_by_key(google_doc_id) end def worksheet @worksheet ||= get_worksheet end def process download if options['print'] Lionel.logger.info "DRY RUN..." Lionel.logger.info "Results were not uploaded to Google Drive" else Lionel.logger.info "Uploading..." upload Lionel.logger.info "Done!" end end def download raise_missing_builder_error unless builder Lionel.logger.info "Exporting trello board '#{board.name}' (#{trello_board_id}) to " + "google doc '#{spreadsheet.title}' (#{google_doc_id})" card_map.each do |row, card| begin Timeout.timeout(5) do row_data = card_columns(card).map do |col, value| worksheet[col,row] = value end.join(" | ") Lionel.logger.info "row[#{row}]: " + row_data end rescue Timeout::Error, Trello::Error => e Lionel.logger.warn e.inspect Lionel.logger.warn card.inspect end end end def card_map @card_map ||= CardMap.new(cards, worksheet) end def upload worksheet.save end def rows worksheet.rows end def builder self.class.builder end def card_columns(card) card_column_rows[card.id] ||= {}.tap do |columns| builder.columns.each do |col_name, block| columns[col_name] = card.instance_exec(self, &block) end end end def card_column_rows @card_column_rows ||= {} end def authenticate return if @authenticated authenticate_trello authenticate_google @authenticated end def authenticate_trello trello_session.configure @board = Trello::Board.find(trello_board_id) end def authenticate_google google_session @worksheet = Lionel::ProxyWorksheet.new(spreadsheet.worksheets[0]) end def google_session @google_session ||= GoogleDrive.login_with_oauth(configuration.google_token) end def trello_session @trello_session ||= TrelloAuthentication.new end def retrieve_open_cards board.cards(filter: :open) end def retrieve_open_cards_in_open_lists [].tap do |c| board.lists(filter: :open).each do |list| list.cards(filter: :open).each do |card| c << card end end end end def raise_missing_builder_error message = <<-ERROR.gsub(/^ {6}/, '') The export is not configured. Example: Lionel.export do A { id } B { name } C { url } end ERROR raise MissingBuilderError.new(message) end class CardMap include Enumerable attr_reader :cards, :worksheet def initialize(cards, worksheet) @cards, @worksheet = cards, worksheet end def each(&block) rows.each(&block) end def rows @rows ||= populate_rows end private def populate_rows {}.tap do |card_rows| start_row = 2 # Currently assumes a header column rows = worksheet.size # Find existing rows for current cards (start_row..rows).each do |row| cell_id = worksheet["B",row] next unless cell_id.present? card = cards.find { |c| c.id == cell_id } next unless card.present? card_rows[row] = card end # Set available rows for new cards new_cards = cards - card_rows.values new_cards.each_with_index do |card, i| row = rows + i + 1 card_rows[row] = card end end end end end end