create endpoing with csv

pull/2206/head
Ayrat Badykov 6 years ago
parent 4bfc37ba2c
commit f984c9129e
No known key found for this signature in database
GPG Key ID: B44668E265E9396F
  1. 21
      apps/block_scout_web/lib/block_scout_web/controllers/address_transaction_controller.ex
  2. 2
      apps/block_scout_web/lib/block_scout_web/router.ex
  3. 18
      apps/block_scout_web/test/block_scout_web/controllers/address_transaction_controller_test.exs
  4. 100
      apps/explorer/lib/explorer/chain/address_transaction_csv_exporter.ex
  5. 52
      apps/explorer/test/explorer/chain/address_transaction_csv_exporter_test.exs

@ -10,6 +10,7 @@ defmodule BlockScoutWeb.AddressTransactionController do
alias BlockScoutWeb.TransactionView
alias Explorer.{Chain, Market}
alias Explorer.Chain.AddressTransactionCsvExporter
alias Explorer.ExchangeRates.Token
alias Indexer.Fetcher.CoinBalanceOnDemand
alias Phoenix.View
@ -106,4 +107,24 @@ defmodule BlockScoutWeb.AddressTransactionController do
not_found(conn)
end
end
def transactions_csv(conn, %{"address_id" => address_hash_string}) do
with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string),
{:ok, address} <- Chain.hash_to_address(address_hash) do
address
|> AddressTransactionCsvExporter.export()
|> Enum.into(
conn
|> put_resp_content_type("application/csv")
|> put_resp_header("content-disposition", "attachment; filename=transactions.csv")
|> send_chunked(200)
)
else
:error ->
unprocessable_entity(conn)
{:error, :not_found} ->
not_found(conn)
end
end
end

@ -240,6 +240,8 @@ defmodule BlockScoutWeb.Router do
get("/search_logs", AddressLogsController, :search_logs)
get("/transactions_csv", AddressTransactionController, :transactions_csv)
get("/token_autocomplete", ChainController, :token_autocomplete)
get("/chain_blocks", ChainController, :chain_blocks, as: :chain_blocks)

@ -132,4 +132,22 @@ defmodule BlockScoutWeb.AddressTransactionControllerTest do
end)
end
end
describe "GET transactions_csv/2" do
test "download csv file with transactions", %{conn: conn} do
address = insert(:address)
:transaction
|> insert(from_address: address)
|> with_block()
:transaction
|> insert(from_address: address)
|> with_block()
conn = get(conn, "/transactions_csv", %{"address_id" => to_string(address.hash)})
assert conn.resp_body |> String.split("\n") |> Enum.count() == 3
end
end
end

@ -31,14 +31,14 @@ defmodule Explorer.Chain.AddressTransactionCsvExporter do
@paging_options %PagingOptions{page_size: @page_size + 1}
@spec export(Address.t()) :: String.t()
@spec export(Address.t()) :: Enumerable.t()
def export(address) do
exchange_rate = Market.get_exchange_rate(Explorer.coin()) || Token.null()
address
|> fetch_all_transactions(@paging_options)
|> to_csv_format(address, exchange_rate)
|> dump_data_to_csv()
|> dump_to_stream()
end
defp fetch_all_transactions(address, paging_options, acc \\ []) do
@ -58,61 +58,53 @@ defmodule Explorer.Chain.AddressTransactionCsvExporter do
end
end
defp dump_data_to_csv(transactions) do
{:ok, path} = Briefly.create()
_ =
transactions
|> RFC4180.dump_to_stream()
|> Stream.into(File.stream!(path))
|> Enum.to_list()
path
defp dump_to_stream(transactions) do
transactions
|> RFC4180.dump_to_stream()
end
defp to_csv_format(transactions, address, exchange_rate) do
# , "ETHCurrentPrice", "ETHPriceAtTxDate", "TxFee", "Status", "ErrCode"]
row_names = [
"TxHash",
"BlockNumber",
"UnixTimestamp",
"FromAddress",
"ToAddress",
"ContractAddress",
"Type",
"Value",
"Fee",
"Status",
"ErrCode",
"CurrentPrice",
"TxDateOpeningPrice",
"TxDateClosingPrice"
]
transaction_lists =
transactions
|> Stream.map(fn transaction ->
{opening_price, closing_price} = price_at_date(transaction.block.timestamp)
[
to_string(transaction.hash),
transaction.block_number,
transaction.block.timestamp,
to_string(transaction.from_address),
to_string(transaction.to_address),
to_string(transaction.created_contract_address),
type(transaction, address),
Wei.to(transaction.value, :wei),
fee(transaction),
transaction.status,
transaction.error,
exchange_rate.usd_value,
opening_price,
closing_price
]
end)
Stream.concat([row_names], transaction_lists)
# row_names = [
# "TxHash",
# "BlockNumber",
# "UnixTimestamp",
# "FromAddress",
# "ToAddress",
# "ContractAddress",
# "Type",
# "Value",
# "Fee",
# "Status",
# "ErrCode",
# "CurrentPrice",
# "TxDateOpeningPrice",
# "TxDateClosingPrice"
# ]
# transaction_lists =
transactions
|> Stream.map(fn transaction ->
{opening_price, closing_price} = price_at_date(transaction.block.timestamp)
[
to_string(transaction.hash),
transaction.block_number,
transaction.block.timestamp,
to_string(transaction.from_address),
to_string(transaction.to_address),
to_string(transaction.created_contract_address),
type(transaction, address),
Wei.to(transaction.value, :wei),
fee(transaction),
transaction.status,
transaction.error,
exchange_rate.usd_value,
opening_price,
closing_price
]
end)
# Stream.concat([row_names], transaction_lists)
end
defp type(%Transaction{from_address_hash: from_address}, %Address{hash: from_address}), do: "OUT"

@ -16,24 +16,37 @@ defmodule Explorer.Chain.AddressTransactionCsvExporterTest do
[result] =
address
|> AddressTransactionCsvExporter.export()
|> File.stream!()
|> NimbleCSV.RFC4180.parse_stream()
|> Stream.map(fn [
hash,
block_number,
timestamp,
from_address,
to_address,
created_address,
type,
value,
fee,
status,
error,
cur_price,
op_price,
cl_price
] ->
|> Enum.to_list()
|> Enum.map(fn [
hash,
_,
block_number,
_,
timestamp,
_,
from_address,
_,
to_address,
_,
created_address,
_,
type,
_,
value,
_,
fee,
_,
status,
_,
error,
_,
cur_price,
_,
op_price,
_,
cl_price,
_
] ->
%{
hash: hash,
block_number: block_number,
@ -51,7 +64,6 @@ defmodule Explorer.Chain.AddressTransactionCsvExporterTest do
closing_price: cl_price
}
end)
|> Enum.to_list()
assert result.block_number == to_string(transaction.block_number)
assert result.timestamp
@ -83,8 +95,6 @@ defmodule Explorer.Chain.AddressTransactionCsvExporterTest do
result =
address
|> AddressTransactionCsvExporter.export()
|> File.stream!()
|> NimbleCSV.RFC4180.parse_stream()
|> Enum.to_list()
assert Enum.count(result) == 200

Loading…
Cancel
Save