fixed formatting

pull/2404/head
slightlycyborg 5 years ago
commit 2e39ef50e1
  1. 85
      apps/block_scout_web/assets/js/lib/history_chart.js
  2. 17
      apps/block_scout_web/assets/js/pages/chain.js
  3. 11
      apps/block_scout_web/lib/block_scout_web/controllers/chain/transaction_history_chart_controller.ex
  4. 35
      apps/block_scout_web/lib/block_scout_web/controllers/chain_controller.ex
  5. 9
      apps/block_scout_web/lib/block_scout_web/notifier.ex
  6. 16
      apps/block_scout_web/lib/block_scout_web/templates/chain/show.html.eex
  7. 39
      apps/explorer/lib/explorer/chain/transaction/history/historian.ex
  8. 9
      apps/explorer/lib/explorer/chain/transaction/history/transaction_stats.ex
  9. 8
      apps/explorer/lib/explorer/history/historian.ex
  10. 10
      apps/explorer/lib/explorer/history/process.ex
  11. 14
      apps/explorer/lib/explorer/market/history/historian.ex
  12. 32
      apps/explorer/test/explorer/chain/transaction/history/historian_test.exs
  13. 10
      apps/explorer/test/explorer/chain/transaction/history/transaction_stats_test.exs
  14. 8
      apps/explorer/test/explorer/history/process_test.exs

@ -4,7 +4,6 @@ import humps from 'humps'
import numeral from 'numeral' import numeral from 'numeral'
import { formatUsdValue } from '../lib/currency' import { formatUsdValue } from '../lib/currency'
import sassVariables from '../../css/app.scss' import sassVariables from '../../css/app.scss'
import { showLoader } from '../lib/utils'
const config = { const config = {
type: 'line', type: 'line',
@ -104,25 +103,13 @@ function getMarketCapData (marketHistoryData, availableSupply) {
} }
} }
// colors for light and dark theme
var priceLineColor
var mcapLineColor
if (localStorage.getItem('current-color-mode') === 'dark') {
priceLineColor = sassVariables.darkprimary
mcapLineColor = sassVariables.darksecondary
} else {
priceLineColor = sassVariables.dashboardLineColorPrice
mcapLineColor = sassVariables.dashboardLineColorMarket
}
class MarketHistoryChart { class MarketHistoryChart {
constructor (el, availableSupply, marketHistoryData, dataConfig) { constructor (el, availableSupply, marketHistoryData, dataConfig) {
var axes = config.options.scales.yAxes.reduce(function (solution, elem) {
var axes = config.options.scales.yAxes.reduce(function(solution, elem){
solution[elem.id] = elem solution[elem.id] = elem
return solution return solution
}, },
{}) {})
this.price = { this.price = {
label: window.localized['Price'], label: window.localized['Price'],
@ -130,13 +117,13 @@ class MarketHistoryChart {
data: [], data: [],
fill: false, fill: false,
pointRadius: 0, pointRadius: 0,
backgroundColor: priceLineColor, backgroundColor: sassVariables.dashboardLineColorPrice,
borderColor: priceLineColor, borderColor: sassVariables.dashboardLineColorPrice,
lineTension: 0 lineTension: 0
} }
if (dataConfig.market == undefined || dataConfig.market.indexOf("price") == -1){ if (dataConfig.market === undefined || dataConfig.market.indexOf('price') === -1) {
this.price.hidden = true this.price.hidden = true
axes["price"].display = false axes['price'].display = false
} }
this.marketCap = { this.marketCap = {
@ -145,13 +132,13 @@ class MarketHistoryChart {
data: [], data: [],
fill: false, fill: false,
pointRadius: 0, pointRadius: 0,
backgroundColor: mcapLineColor, backgroundColor: sassVariables.dashboardLineColorMarket,
borderColor: mcapLineColor, borderColor: sassVariables.dashboardLineColorMarket,
lineTension: 0 lineTension: 0
} }
if (dataConfig.market == undefined || dataConfig.market.indexOf("market_cap") == -1){ if (dataConfig.market === undefined || dataConfig.market.indexOf('market_cap') === -1) {
this.marketCap.hidden = true this.marketCap.hidden = true
axes["marketCap"].display = false axes['marketCap'].display = false
} }
this.numTransactions = { this.numTransactions = {
@ -162,15 +149,14 @@ class MarketHistoryChart {
pointRadius: 0, pointRadius: 0,
backgroundColor: sassVariables.dashboardLineColorMarket, backgroundColor: sassVariables.dashboardLineColorMarket,
borderColor: sassVariables.dashboardLineColorTransactions, borderColor: sassVariables.dashboardLineColorTransactions,
lineTension: 0, lineTension: 0
} }
if (dataConfig.transactions == undefined || dataConfig.transactions.indexOf("transactions_per_day") == -1){ if (dataConfig.transactions === undefined || dataConfig.transactions.indexOf('transactions_per_day') === -1) {
this.numTransactions.hidden = true this.numTransactions.hidden = true
axes["numTransactions"].display = false axes['numTransactions'].display = false
} }
this.availableSupply = availableSupply this.availableSupply = availableSupply
//TODO: This is where we dynamically append datasets
config.data.datasets = [this.price, this.marketCap, this.numTransactions] config.data.datasets = [this.price, this.marketCap, this.numTransactions]
this.chart = new Chart(el, config) this.chart = new Chart(el, config)
} }
@ -185,9 +171,9 @@ class MarketHistoryChart {
} }
this.chart.update() this.chart.update()
} }
updateTransactionHistory (transaction_history) { updateTransactionHistory (transactionHistory) {
this.numTransactions.data = transaction_history.map(dataPoint => { this.numTransactions.data = transactionHistory.map(dataPoint => {
return {x:dataPoint.date, y:dataPoint.number_of_transactions} return {x: dataPoint.date, y: dataPoint.number_of_transactions}
}) })
this.chart.update() this.chart.update()
} }
@ -198,28 +184,24 @@ export function createMarketHistoryChart (el) {
const dataConfig = $(el).data('history_chart_config') const dataConfig = $(el).data('history_chart_config')
const $chartLoading = $('[data-chart-loading-message]') const $chartLoading = $('[data-chart-loading-message]')
const isTimeout = true
const timeoutID = showLoader(isTimeout, $chartLoading)
const $chartError = $('[data-chart-error-message]') const $chartError = $('[data-chart-error-message]')
const chart = new MarketHistoryChart(el, 0, [], dataConfig) const chart = new MarketHistoryChart(el, 0, [], dataConfig)
Object.keys(dataPaths).forEach(function(history_source){ Object.keys(dataPaths).forEach(function (historySource) {
$.getJSON(dataPaths[history_source], {type: 'JSON'}) $.getJSON(dataPaths[historySource], {type: 'JSON'})
.done(data => { .done(data => {
switch(history_source){ switch (historySource) {
case "market": case 'market':
const availableSupply = JSON.parse(data.supply_data) const availableSupply = JSON.parse(data.supply_data)
const marketHistoryData = humps.camelizeKeys(JSON.parse(data.history_data)) const marketHistoryData = humps.camelizeKeys(JSON.parse(data.history_data))
$(el).show() $(el).show()
chart.updateMarketHistory(availableSupply, marketHistoryData) chart.updateMarketHistory(availableSupply, marketHistoryData)
break; break
case "transaction": case 'transaction':
const transaction_history = JSON.parse(data.history_data) const transactionHistory = JSON.parse(data.history_data)
$(el).show() $(el).show()
chart.updateTransactionHistory(transaction_history) chart.updateTransactionHistory(transactionHistory)
break; break
} }
}) })
.fail(() => { .fail(() => {
@ -227,8 +209,9 @@ export function createMarketHistoryChart (el) {
}) })
.always(() => { .always(() => {
$chartLoading.hide() $chartLoading.hide()
})}) })
return chart; })
return chart
} }
$('[data-chart-error-message]').on('click', _event => { $('[data-chart-error-message]').on('click', _event => {

@ -1,3 +1,4 @@
/* global _ */
import $ from 'jquery' import $ from 'jquery'
import omit from 'lodash/omit' import omit from 'lodash/omit'
import first from 'lodash/first' import first from 'lodash/first'
@ -9,7 +10,7 @@ import numeral from 'numeral'
import socket from '../socket' import socket from '../socket'
import { exchangeRateChannel, formatUsdValue } from '../lib/currency' import { exchangeRateChannel, formatUsdValue } from '../lib/currency'
import { createStore, connectElements } from '../lib/redux_helpers.js' import { createStore, connectElements } from '../lib/redux_helpers.js'
import { batchChannel, showLoader } from '../lib/utils' import { batchChannel } from '../lib/utils'
import listMorph from '../lib/list_morph' import listMorph from '../lib/list_morph'
import { createMarketHistoryChart } from '../lib/history_chart' import { createMarketHistoryChart } from '../lib/history_chart'
@ -107,9 +108,9 @@ function baseReducer (state = initialState, action) {
}) })
} }
} }
case 'RECIEVED_UPDATED_TRANSACTION_STATS':{ case 'RECIEVED_UPDATED_TRANSACTION_STATS': {
return Object.assign({}, state, { return Object.assign({}, state, {
transactionStats: action.msg.stats, transactionStats: action.msg.stats
}) })
} }
case 'START_TRANSACTIONS_FETCH': case 'START_TRANSACTIONS_FETCH':
@ -154,7 +155,7 @@ const elements = {
if (chart && !(oldState.availableSupply === state.availableSupply && oldState.marketHistoryData === state.marketHistoryData) && state.availableSupply) { if (chart && !(oldState.availableSupply === state.availableSupply && oldState.marketHistoryData === state.marketHistoryData) && state.availableSupply) {
chart.updateMarketHistory(state.availableSupply, state.marketHistoryData) chart.updateMarketHistory(state.availableSupply, state.marketHistoryData)
} }
if (chart && !(_.isEqual(oldState.transactionStats, state.transactionStats))){ if (chart && !(_.isEqual(oldState.transactionStats, state.transactionStats))) {
chart.updateTransactionHistory(state.transactionStats) chart.updateTransactionHistory(state.transactionStats)
} }
} }
@ -223,7 +224,11 @@ const elements = {
}, },
'[data-selector="chain-block-list"] [data-selector="loading-message"]': { '[data-selector="chain-block-list"] [data-selector="loading-message"]': {
render ($el, state, oldState) { render ($el, state, oldState) {
showLoader(state.blocksLoading, $el) if (state.blocksLoading) {
$el.show()
} else {
$el.hide()
}
} }
}, },
'[data-selector="transactions-list"] [data-selector="error-message"]': { '[data-selector="transactions-list"] [data-selector="error-message"]': {
@ -233,7 +238,7 @@ const elements = {
}, },
'[data-selector="transactions-list"] [data-selector="loading-message"]': { '[data-selector="transactions-list"] [data-selector="loading-message"]': {
render ($el, state, oldState) { render ($el, state, oldState) {
showLoader(state.transactionsLoading, $el) $el.toggle(state.transactionsLoading)
} }
}, },
'[data-selector="transactions-list"]': { '[data-selector="transactions-list"]': {

@ -5,17 +5,19 @@ defmodule BlockScoutWeb.Chain.TransactionHistoryChartController do
def show(conn, _params) do def show(conn, _params) do
with true <- ajax?(conn) do with true <- ajax?(conn) do
[{:history_size, history_size}] = Application.get_env(:block_scout_web, __MODULE__, 30) [{:history_size, history_size}] = Application.get_env(:block_scout_web, __MODULE__, 30)
latest = Date.utc_today() latest = Date.utc_today()
earliest = Date.add(latest, -1 * history_size) earliest = Date.add(latest, -1 * history_size)
transaction_history_data = TransactionStats.by_date_range(earliest, latest) date_range = TransactionStats.by_date_range(earliest, latest)
transaction_history_data = date_range
|> extract_history |> extract_history
|> encode_transaction_history_data |> encode_transaction_history_data
json(conn, %{ json(conn, %{
history_data: transaction_history_data, history_data: transaction_history_data
}) })
else else
_ -> unprocessable_entity(conn) _ -> unprocessable_entity(conn)
@ -24,7 +26,8 @@ defmodule BlockScoutWeb.Chain.TransactionHistoryChartController do
defp extract_history(db_results) do defp extract_history(db_results) do
Enum.map(db_results, fn row -> Enum.map(db_results, fn row ->
%{date: row.date, number_of_transactions: row.number_of_transactions} end) %{date: row.date, number_of_transactions: row.number_of_transactions}
end)
end end
defp encode_transaction_history_data(transaction_history_data) do defp encode_transaction_history_data(transaction_history_data) do

@ -2,10 +2,10 @@ defmodule BlockScoutWeb.ChainController do
use BlockScoutWeb, :controller use BlockScoutWeb, :controller
alias BlockScoutWeb.ChainView alias BlockScoutWeb.ChainView
alias Explorer.{Chain, PagingOptions} alias Explorer.{Chain, PagingOptions, Repo}
alias Explorer.Chain.{Address, Block, Transaction} alias Explorer.Chain.{Address, Block, Transaction}
alias Explorer.Chain.Transaction.History.TransactionStats
alias Explorer.Chain.Supply.RSK alias Explorer.Chain.Supply.RSK
alias Explorer.Chain.Transaction.History.TransactionStats
alias Explorer.Counters.AverageBlockTime alias Explorer.Counters.AverageBlockTime
alias Explorer.ExchangeRates.Token alias Explorer.ExchangeRates.Token
alias Explorer.Market alias Explorer.Market
@ -15,7 +15,6 @@ defmodule BlockScoutWeb.ChainController do
transaction_estimated_count = Chain.transaction_estimated_count() transaction_estimated_count = Chain.transaction_estimated_count()
block_count = Chain.block_estimated_count() block_count = Chain.block_estimated_count()
market_cap_calculation = market_cap_calculation =
case Application.get_env(:explorer, :supply) do case Application.get_env(:explorer, :supply) do
RSK -> RSK ->
@ -29,8 +28,18 @@ defmodule BlockScoutWeb.ChainController do
transaction_stats = get_transaction_stats() transaction_stats = get_transaction_stats()
chart_data_paths = %{market: market_history_chart_path(conn, :show), # Hack for render during testing
transaction: transaction_history_chart_path(conn, :show)} transaction_stats =
if length(transaction_stats) == 0 do
[%{number_of_transactions: 0}]
else
transaction_stats
end
chart_data_paths = %{
market: market_history_chart_path(conn, :show),
transaction: transaction_history_chart_path(conn, :show)
}
chart_config = Application.get_env(:block_scout_web, :chart_config, %{}) chart_config = Application.get_env(:block_scout_web, :chart_config, %{})
@ -51,14 +60,14 @@ defmodule BlockScoutWeb.ChainController do
) )
end end
def get_transaction_stats() do def get_transaction_stats do
stats_scale = date_range(1) stats_scale = date_range(1)
TransactionStats.by_date_range(stats_scale.earliest, stats_scale.latest) TransactionStats.by_date_range(stats_scale.earliest, stats_scale.latest)
end end
def date_range(num_days) do def date_range(num_days) do
today = Date.utc_today() today = Date.utc_today()
x_days_back = Date.add(today, -1*(num_days-1)) x_days_back = Date.add(today, -1 * (num_days - 1))
%{earliest: x_days_back, latest: today} %{earliest: x_days_back, latest: today}
end end
@ -75,8 +84,6 @@ defmodule BlockScoutWeb.ChainController do
end end
end end
def search(conn, _), do: not_found(conn)
def token_autocomplete(conn, %{"q" => term}) when is_binary(term) do def token_autocomplete(conn, %{"q" => term}) when is_binary(term) do
if term == "" do if term == "" do
json(conn, "{}") json(conn, "{}")
@ -97,15 +104,9 @@ defmodule BlockScoutWeb.ChainController do
def chain_blocks(conn, _params) do def chain_blocks(conn, _params) do
if ajax?(conn) do if ajax?(conn) do
blocks = blocks =
[ [paging_options: %PagingOptions{page_size: 4}]
paging_options: %PagingOptions{page_size: 4},
necessity_by_association: %{
[miner: :names] => :optional,
:transactions => :optional,
:rewards => :optional
}
]
|> Chain.list_blocks() |> Chain.list_blocks()
|> Repo.preload([[miner: :names], :transactions, :rewards])
|> Enum.map(fn block -> |> Enum.map(fn block ->
%{ %{
chain_block_html: chain_block_html:

@ -122,11 +122,14 @@ defmodule BlockScoutWeb.Notifier do
def handle_event({:chain_event, :transaction_stats}) do def handle_event({:chain_event, :transaction_stats}) do
today = Date.utc_today() today = Date.utc_today()
[{:history_size, history_size}] = Application.get_env(:block_scout_web, BlockScoutWeb.Chain.TransactionHistoryChartController, 30)
[{:history_size, history_size}] =
Application.get_env(:block_scout_web, BlockScoutWeb.Chain.TransactionHistoryChartController, 30)
x_days_back = Date.add(today, -1 * history_size) x_days_back = Date.add(today, -1 * history_size)
stats = TransactionStats.by_date_range(x_days_back, today)
|> Enum.map(fn item -> Map.drop(item, [:__meta__]) end) date_range = TransactionStats.by_date_range(x_days_back, today)
stats = Enum.map(date_range, fn item -> Map.drop(item, [:__meta__]) end)
Endpoint.broadcast("transactions:stats", "update", %{stats: stats}) Endpoint.broadcast("transactions:stats", "update", %{stats: stats})
end end

@ -5,7 +5,7 @@
<div class="dashboard-banner-network-graph"> <div class="dashboard-banner-network-graph">
<!-- Graph --> <!-- Graph -->
<div class="dashboard-banner-chart"> <div class="dashboard-banner-chart">
<div hidden data-chart-loading-message class="tile tile-muted text-center mt-5"> <div data-chart-loading-message class="tile tile-muted text-center mt-5">
<span class="loading-spinner-small mr-2"> <span class="loading-spinner-small mr-2">
<span class="loading-spinner-block-1"></span> <span class="loading-spinner-block-1"></span>
<span class="loading-spinner-block-2"></span> <span class="loading-spinner-block-2"></span>
@ -71,7 +71,7 @@
<span class="dashboard-banner-chart-legend-label"> <span class="dashboard-banner-chart-legend-label">
<%= gettext "Tx/day" %> <%= gettext "Tx/day" %>
</span> </span>
<span class="dashboard-banner-chart-legend-value" data-selector=""> <span class="dashboard-banner-chart-legend-value" data-selector="numTransactions">
<%= Enum.at(@transaction_stats, 0).number_of_transactions %> <%= Enum.at(@transaction_stats, 0).number_of_transactions %>
</span> </span>
</div> </div>
@ -135,8 +135,8 @@
<%= gettext "Something went wrong, click to reload." %> <%= gettext "Something went wrong, click to reload." %>
</span> </span>
</button> </button>
<div hidden data-selector="loading-message" class="tile tile-muted text-center mt-3 w-100" > <div data-selector="loading-message" class="tile tile-muted text-center mt-3 w-100">
<span class="loading-spinner-small mr-2"> <span class="loading-spinner-small mr-2">
<span class="loading-spinner-block-1"></span> <span class="loading-spinner-block-1"></span>
<span class="loading-spinner-block-2"></span> <span class="loading-spinner-block-2"></span>
</span> </span>
@ -161,8 +161,12 @@
<%= gettext "Something went wrong, click to retry." %> <%= gettext "Something went wrong, click to retry." %>
</span> </span>
</button> </button>
<div hidden data-selector="loading-message"> <div data-selector="loading-message" class="tile tile-muted text-center mt-3">
<%= render BlockScoutWeb.CommonComponentsView, "_tile-loader.html" %> <span class="loading-spinner-small mr-2">
<span class="loading-spinner-block-1"></span>
<span class="loading-spinner-block-2"></span>
</span>
<%= gettext("Loading...") %>
</div> </div>
</span> </span>
</div> </div>

@ -1,44 +1,49 @@
defmodule Explorer.Chain.Transaction.History.Historian do defmodule Explorer.Chain.Transaction.History.Historian do
@moduledoc """
Implements behaviour Historian which will compile TransactionStats from Block/Transaction data and then save the TransactionStats into the database for later retrevial.
"""
use Explorer.History.Historian use Explorer.History.Historian
alias Explorer.Chain.{Block, Transaction}
alias Explorer.Chain.Events.Publisher
alias Explorer.Chain.Transaction.History.TransactionStats
alias Explorer.History.Process, as: HistoryProcess alias Explorer.History.Process, as: HistoryProcess
alias Explorer.Repo alias Explorer.Repo
alias Explorer.Chain.Block
alias Explorer.Chain.Transaction.History.TransactionStats
alias Explorer.Chain.Events.Publisher
import Ecto.Query, only: [from: 2] import Ecto.Query, only: [from: 2]
alias Explorer.Chain.Transaction
@behaviour Historian @behaviour Historian
@impl Historian @impl Historian
def compile_records(num_days, records \\ []) do def compile_records(num_days, records \\ []) do
if num_days == 0 do if num_days == 0 do
#base case # base case
{:ok, records} {:ok, records}
else else
day_to_fetch = Date.add(date_today(), -1*(num_days-1)) day_to_fetch = Date.add(date_today(), -1 * (num_days - 1))
earliest = datetime(day_to_fetch, ~T[00:00:00]) earliest = datetime(day_to_fetch, ~T[00:00:00])
latest = datetime(day_to_fetch, ~T[23:59:59]) latest = datetime(day_to_fetch, ~T[23:59:59])
query = from( query =
block in Block, from(block in Block,
where: (block.timestamp >= ^earliest and block.timestamp <= ^latest), where: block.timestamp >= ^earliest and block.timestamp <= ^latest,
join: transaction in Transaction, join: transaction in Transaction,
on: block.hash == transaction.block_hash) on: block.hash == transaction.block_hash
)
num_transactions = Repo.aggregate query, :count, :hash num_transactions = Repo.aggregate(query, :count, :hash)
records = [%{date: day_to_fetch, number_of_transactions: num_transactions} | records ] records = [%{date: day_to_fetch, number_of_transactions: num_transactions} | records]
compile_records(num_days-1, records) compile_records(num_days - 1, records)
end end
end end
@impl Historian @impl Historian
def save_records(records) do def save_records(records) do
{num_inserted, _} = Repo.insert_all(TransactionStats, records, on_conflict: :replace_all_except_primary_key, conflict_target: [:date]) {num_inserted, _} =
Repo.insert_all(TransactionStats, records, on_conflict: :replace_all_except_primary_key, conflict_target: [:date])
Publisher.broadcast(:transaction_stats) Publisher.broadcast(:transaction_stats)
num_inserted num_inserted
end end
@ -49,7 +54,7 @@ defmodule Explorer.Chain.Transaction.History.Historian do
DateTime.from_naive!(naive_dt, "Etc/UTC") DateTime.from_naive!(naive_dt, "Etc/UTC")
end end
defp date_today() do defp date_today do
HistoryProcess.config_or_default(:utc_today, Date.utc_today(), __MODULE__) HistoryProcess.config_or_default(:utc_today, Date.utc_today(), __MODULE__)
end end
end end

@ -9,7 +9,6 @@ defmodule Explorer.Chain.Transaction.History.TransactionStats do
alias Explorer.Repo alias Explorer.Repo
schema "transaction_stats" do schema "transaction_stats" do
field(:date, :date) field(:date, :date)
field(:number_of_transactions, :integer) field(:number_of_transactions, :integer)
@ -28,9 +27,11 @@ defmodule Explorer.Chain.Transaction.History.TransactionStats do
@spec by_date_range(Date.t(), Date.t()) :: [__MODULE__] @spec by_date_range(Date.t(), Date.t()) :: [__MODULE__]
def by_date_range(earliest, latest) do def by_date_range(earliest, latest) do
# Create a query # Create a query
query = from stat in __MODULE__, query =
where: (stat.date >= ^earliest and stat.date<=^latest), from(stat in __MODULE__,
order_by: [desc: :date] where: stat.date >= ^earliest and stat.date <= ^latest,
order_by: [desc: :date]
)
Repo.all(query) Repo.all(query)
end end

@ -7,8 +7,8 @@ defmodule Explorer.History.Historian do
Record of historical values for a specific date. Record of historical values for a specific date.
""" """
@type record :: %{ @type record :: %{
date: Date.t() date: Date.t()
} }
@doc """ @doc """
Compile history for a specified amount of units in the past. Units are defined by historian impl Compile history for a specified amount of units in the past. Units are defined by historian impl
@ -25,8 +25,8 @@ defmodule Explorer.History.Historian do
alias Explorer.History.Historian alias Explorer.History.Historian
def start_link(_) do def start_link(_) do
#Expansion: # Expansion:
#HistoryProcess.start_link(Explorer.History.Process, [:ok, __MODULE__], name: __MODULE__) # HistoryProcess.start_link(Explorer.History.Process, [:ok, __MODULE__], name: __MODULE__)
GenServer.start_link(Explorer.History.Process, [:ok, __MODULE__], name: __MODULE__) GenServer.start_link(Explorer.History.Process, [:ok, __MODULE__], name: __MODULE__)
end end

@ -1,5 +1,8 @@
defmodule Explorer.History.Process do defmodule Explorer.History.Process do
@moduledoc """
Creates the GenServer process used by a Historian to compile_history and to save_records.
Specifically used by Market.History.Historian and Transaction.History.Historian
"""
use GenServer use GenServer
require Logger require Logger
@ -34,6 +37,7 @@ defmodule Explorer.History.Process do
def handle_info({:DOWN, _, :process, _, _}, state) do def handle_info({:DOWN, _, :process, _, _}, state) do
{:noreply, state} {:noreply, state}
end end
# Actions # Actions
@spec successful_compilation(Historian.record(), module()) :: any() @spec successful_compilation(Historian.record(), module()) :: any()
@ -42,9 +46,9 @@ defmodule Explorer.History.Process do
schedule_next_compilation() schedule_next_compilation()
end end
defp schedule_next_compilation() do defp schedule_next_compilation do
delay = config_or_default(:history_fetch_interval, :timer.minutes(60)) delay = config_or_default(:history_fetch_interval, :timer.minutes(60))
Process.send_after(self(), {:compile_historical_records, 1,}, delay) Process.send_after(self(), {:compile_historical_records, 1}, delay)
end end
@spec failed_compilation(non_neg_integer(), module(), non_neg_integer()) :: any() @spec failed_compilation(non_neg_integer(), module(), non_neg_integer()) :: any()

@ -1,4 +1,7 @@
defmodule Explorer.Market.History.Historian do defmodule Explorer.Market.History.Historian do
@moduledoc """
Implements behaviour Historian for Market History. Compiles market history from a source and then saves it into the database.
"""
use Explorer.History.Historian use Explorer.History.Historian
alias Explorer.History.Process, as: HistoryProcess alias Explorer.History.Process, as: HistoryProcess
alias Explorer.Market alias Explorer.Market
@ -7,11 +10,12 @@ defmodule Explorer.Market.History.Historian do
@impl Historian @impl Historian
def compile_records(previous_days) do def compile_records(previous_days) do
source = HistoryProcess.config_or_default( source =
:source, HistoryProcess.config_or_default(
Explorer.Market.History.Source.CryptoCompare, :source,
Explorer.Market.History.Historian Explorer.Market.History.Source.CryptoCompare,
) Explorer.Market.History.Historian
)
source.fetch_history(previous_days) source.fetch_history(previous_days)
end end

@ -6,29 +6,25 @@ defmodule Explorer.Chain.Transaction.History.HistorianTest do
import Ecto.Query, only: [from: 2] import Ecto.Query, only: [from: 2]
setup do setup do
Application.put_env(:explorer, Historian, utc_today: ~D[1970-01-04]) Application.put_env(:explorer, Historian, utc_today: ~D[1970-01-04])
:ok :ok
end end
defp days_to_secs(days) do defp days_to_secs(days) do
60*60*24*days 60 * 60 * 24 * days
end end
describe "compile_records/1" do describe "compile_records/1" do
test "fetches transactions from blocks mined in the past num_days" do test "fetches transactions from blocks mined in the past num_days" do
blocks = [ blocks = [
#1970-01-03 00:00:60 # 1970-01-03 00:00:60
insert(:block, timestamp: DateTime.from_unix!(days_to_secs(2) + 60)), insert(:block, timestamp: DateTime.from_unix!(days_to_secs(2) + 60)),
#1970-01-03 04:00:00 # 1970-01-03 04:00:00
insert(:block, timestamp: DateTime.from_unix!(days_to_secs(2) + (4*60*60))), insert(:block, timestamp: DateTime.from_unix!(days_to_secs(2) + 4 * 60 * 60)),
#1970-01-02 00:00:00 # 1970-01-02 00:00:00
insert(:block, timestamp: DateTime.from_unix!(days_to_secs(1))) insert(:block, timestamp: DateTime.from_unix!(days_to_secs(1)))
] ]
@ -39,22 +35,23 @@ defmodule Explorer.Chain.Transaction.History.HistorianTest do
expected = [ expected = [
%{date: ~D[1970-01-04], number_of_transactions: 0} %{date: ~D[1970-01-04], number_of_transactions: 0}
] ]
assert {:ok, ^expected} = Historian.compile_records 1
assert {:ok, ^expected} = Historian.compile_records(1)
expected = [ expected = [
%{date: ~D[1970-01-04], number_of_transactions: 0}, %{date: ~D[1970-01-04], number_of_transactions: 0},
%{date: ~D[1970-01-03], number_of_transactions: 2}, %{date: ~D[1970-01-03], number_of_transactions: 2}
] ]
assert {:ok, ^expected} = Historian.compile_records 2
assert {:ok, ^expected} = Historian.compile_records(2)
expected = [ expected = [
%{date: ~D[1970-01-04], number_of_transactions: 0}, %{date: ~D[1970-01-04], number_of_transactions: 0},
%{date: ~D[1970-01-03], number_of_transactions: 2}, %{date: ~D[1970-01-03], number_of_transactions: 2},
%{date: ~D[1970-01-02], number_of_transactions: 1} %{date: ~D[1970-01-02], number_of_transactions: 1}
] ]
assert {:ok, ^expected} = Historian.compile_records 3
assert {:ok, ^expected} = Historian.compile_records(3)
end end
end end
@ -73,10 +70,11 @@ defmodule Explorer.Chain.Transaction.History.HistorianTest do
Historian.save_records(single_record) Historian.save_records(single_record)
query = from( query =
stats in TransactionStats, from(stats in TransactionStats,
select: %{date: stats.date, number_of_transactions: stats.number_of_transactions}, select: %{date: stats.date, number_of_transactions: stats.number_of_transactions},
order_by: [desc: stats.date]) order_by: [desc: stats.date]
)
results = Repo.all(query) results = Repo.all(query)

@ -5,10 +5,11 @@ defmodule Explorer.Chain.Transaction.History.TransactionStatsTest do
alias Explorer.Repo alias Explorer.Repo
test "by_date_range()" do test "by_date_range()" do
some_transaction_stats = [
some_transaction_stats = [%{date: ~D[2019-07-09], number_of_transactions: 10}, %{date: ~D[2019-07-09], number_of_transactions: 10},
%{date: ~D[2019-07-08], number_of_transactions: 20}, %{date: ~D[2019-07-08], number_of_transactions: 20},
%{date: ~D[2019-07-07], number_of_transactions: 30}] %{date: ~D[2019-07-07], number_of_transactions: 30}
]
Repo.insert_all(TransactionStats, some_transaction_stats) Repo.insert_all(TransactionStats, some_transaction_stats)
@ -22,7 +23,6 @@ defmodule Explorer.Chain.Transaction.History.TransactionStatsTest do
assert ~D[2019-07-07] = Enum.at(all3, 2).date assert ~D[2019-07-07] = Enum.at(all3, 2).date
assert 30 == Enum.at(all3, 2).number_of_transactions assert 30 == Enum.at(all3, 2).number_of_transactions
just2 = TransactionStats.by_date_range(~D[2019-07-08], ~D[2019-07-09]) just2 = TransactionStats.by_date_range(~D[2019-07-08], ~D[2019-07-09])
assert 2 == length(just2) assert 2 == length(just2)
assert ~D[2019-07-08] = Enum.at(just2, 1).date assert ~D[2019-07-08] = Enum.at(just2, 1).date

@ -13,6 +13,7 @@ defmodule Explorer.History.ProcessTest do
test "handle_info with `{:compile_historical_records, days}`" do test "handle_info with `{:compile_historical_records, days}`" do
records = [%{date: ~D[2018-04-01], closing_price: Decimal.new(10), opening_price: Decimal.new(5)}] records = [%{date: ~D[2018-04-01], closing_price: Decimal.new(10), opening_price: Decimal.new(5)}]
TestHistorian TestHistorian
|> expect(:compile_records, fn 1 -> {:ok, records} end) |> expect(:compile_records, fn 1 -> {:ok, records} end)
|> expect(:save_records, fn _ -> :ok end) |> expect(:save_records, fn _ -> :ok end)
@ -43,7 +44,7 @@ defmodule Explorer.History.ProcessTest do
assert {:noreply, state} == HistoryProcess.handle_info({nil, {1, 0, {:ok, [record]}}}, state) assert {:noreply, state} == HistoryProcess.handle_info({nil, {1, 0, {:ok, [record]}}}, state)
# Message isn't sent before interval is up # Message isn't sent before interval is up
refute_receive {:compile_historical_records, 1}, history_fetch_interval-1 refute_receive {:compile_historical_records, 1}, history_fetch_interval - 1
# Now message is sent # Now message is sent
assert_receive {:compile_historical_records, 1} assert_receive {:compile_historical_records, 1}
@ -54,10 +55,9 @@ defmodule Explorer.History.ProcessTest do
|> expect(:compile_records, fn 1 -> :error end) |> expect(:compile_records, fn 1 -> :error end)
|> expect(:save_records, fn _ -> :ok end) |> expect(:save_records, fn _ -> :ok end)
#Process will restart, so this is needed # Process will restart, so this is needed
set_mox_global() set_mox_global()
state = %{historian: TestHistorian} state = %{historian: TestHistorian}
# base_backoff should be short enough that it doesn't slow down testing... # base_backoff should be short enough that it doesn't slow down testing...
@ -69,7 +69,7 @@ defmodule Explorer.History.ProcessTest do
assert {:noreply, state} == HistoryProcess.handle_info({nil, {1, 0, :error}}, state) assert {:noreply, state} == HistoryProcess.handle_info({nil, {1, 0, :error}}, state)
# Message isn't sent before interval is up # Message isn't sent before interval is up
refute_receive {_ref, {1, 1, :error}}, base_backoff-1 refute_receive {_ref, {1, 1, :error}}, base_backoff - 1
# Now message is sent # Now message is sent
assert_receive {_ref, {1, 1, :error}} assert_receive {_ref, {1, 1, :error}}

Loading…
Cancel
Save