# frozen_string_literal: true require('roo') require('google/cloud/bigquery') module Eost BD = 'hernanirvaz.coins' DF = '%Y-%m-%d' DI = '%Y-%m-%d %H:%M:%S' # (see Bigquery) class Bigquery # @return [Roo::CSV] folha calculo a processar attr_reader :folha # @return [Hash] opcoes trabalho com linhas attr_reader :linha # @return [Google::Cloud::Bigquery] API bigquery attr_reader :api # @return [Array] row folha calculo em processamento attr_reader :row # @return [Google::Cloud::Bigquery::QueryJob] job bigquery attr_reader :job # @return (see sql) attr_reader :sqr # @param [String] csv folha calculo para processar # @param [Hash] ops opcoes trabalho com linhas # @option ops [Boolean] :e (false) apaga linha igual? # @option ops [Boolean] :m (false) apaga linhas existencia multipla? # @option ops [Boolean] :i (false) insere linha nova? # @return [Bigquery] acesso folhas calculo bloks.io & correspondente bigquery dataset def initialize(csv = '', ops = { e: false, m: false, i: false }) @folha = Roo::CSV.new(csv) if csv.size.positive? @linha = ops # usa env GOOGLE_APPLICATION_CREDENTIALS para obter credentials # @see https://cloud.google.com/bigquery/docs/authentication/getting-started @api = Google::Cloud::Bigquery.new end # @return [Carteiras] API eosscan - processar transacoes def transacoes @transacoes ||= Carteiras.new( { wb: sql("select * from #{BD}.walletEos order by 1").map { |e| { ax: e[:weos], sl: e[:eos].to_d } }, nt: sql("select blocknumber,iax from #{BD}.eostx order by 1") }, linha ) end # @return [Carteiras] API eosscan - processar carteiras & transacoes def carteiras transacoes end # insere transacoes novas na tabela eos def processa puts(format("%2i LINHAS INSERIDAS #{BD}.eos", n: transacoes.novas.count.positive? ? eos_insert_api : 0)) end # @return [String] campos da tabela eos no bigquery def eos_fields 'blocknumber,time,contract,action,acfrom,acto,amount,symbol,memo,data,dias' end # @return [String] parte sql para processamento linhas existentes def sql_where "from #{BD}.eos where blocknumber=#{row[0]}" end # @return [Integer] numero linhas inseridas def eos_insert_csv return 1 unless linha[:i] dml("INSERT #{BD}.eos(#{eos_fields}) VALUES(#{eos_csv_val1})") end # @return [String] valores formatados para insert eos (parte1) def eos_csv_val1 "#{row[0]}," \ "'#{Time.parse(row[1]).strftime(DI)}'," \ "'#{row[2]}'," \ "#{eos_csv_val2}" end # @return [String] valores formatados para insert eos (parte2) def eos_csv_val2 "'#{row[3]}'," \ "'#{row[4]}'," \ "'#{row[5]}'," \ "#{Float(row[6])}," \ "'#{row[7]}'," \ "'#{row[8]}'," \ "'#{row[9]}',0" end # @return [Integer] numero linhas inseridas def eos_insert_api dml("INSERT #{BD}.eos(#{eos_fields}) VALUES#{transacoes.novas.map { |e| eos_api_val1(e) }.join(',')}") end # @param [Hash] htx transacao ligadas a uma carteira - sem elementos irrelevantes # @return [String] valores formatados para insert eos (parte1) def eos_api_val1(htx) "(#{Integer(htx['block_num'])}," \ "DATETIME(TIMESTAMP('#{htx['block_time']}'))," \ "'#{act(htx)['account']}'," \ "'#{act(htx)['name']}'," \ "'#{act_data(htx)['from']}'," \ "'#{act_data(htx)['to']}'," \ "#{eos_api_val2(htx)}" end # @param [Hash] htx transacao ligadas a uma carteira - sem elementos irrelevantes # @return [String] valores formatados para insert eos (parte2) def eos_api_val2(htx) "#{act_data(htx)['quantity'].to_d}," \ "'#{act_data(htx)['quantity'][/[[:upper:]]+/]}'," \ "'#{act_data(htx)['memo']}'," \ "'#{act_data(htx)}'," \ "#{Integer(linha[:h][String(htx['block_num'])] || 0)})" end # @param [Hash] htx transacao normal # @return [Hash] dados da acao def act(htx) htx['action_trace']['act'] end # @param [Hash] htx transacao normal # @return [Hash] dados da acao def act_data(htx) act(htx)['data'] end # cria job bigquery & verifica execucao # # @param cmd (see sql) # @return [Boolean] job ok? def job?(cmd) @job = api.query_job(cmd) @job.wait_until_done! puts(@job.error['message']) if @job.failed? @job.failed? end # cria Structured Query Language (SQL) job bigquery # # @param [String] cmd comando SQL a executar # @param [Array] red resultado quando SQL tem erro # @return [Google::Cloud::Bigquery::Data] resultado do SQL def sql(cmd, red = []) @sqr = job?(cmd) ? red : job.data end # cria Data Manipulation Language (DML) job bigquery # # @param cmd (see sql) # @return [Integer] numero linhas afetadas def dml(cmd) job?(cmd) ? 0 : job.num_dml_affected_rows end end end