diff --git a/hapi/src/services/stats.service.js b/hapi/src/services/stats.service.js index 63f1b2bb..7b45259e 100644 --- a/hapi/src/services/stats.service.js +++ b/hapi/src/services/stats.service.js @@ -6,49 +6,59 @@ const { hasuraUtil, sequelizeUtil, sleepFor, eosUtil } = require('../utils') const STAT_ID = 'bceb5b75-6cb9-45af-9735-5389e0664847' -const getTransactionsInTimeRage = async payload => { - const query = ` - query($start: timestamptz!, $end: timestamptz!) { - block: block_history_aggregate( - where: { timestamp: { _gte: $start, _lte: $end } } - ) { - info: aggregate { - sum { - transactions_length - } - } - } - } - ` - const data = await hasuraUtil.request(query, payload) +const getTransactionsInTimeRage = async (start, end) => { + const [rows] = await sequelizeUtil.query(` + SELECT + sum(transactions_length)::integer as transactions_count + FROM + block_history + WHERE + timestamp between '${start.toISOString()}' and '${end.toISOString()}' + `) - return data.block.info.sum.transactions_length || 0 + return rows?.[0]?.transactions_count || 0 } -const getNodesSummary = async payload => { - const query = ` - query { - producers: producer { - bp_json - } - } - ` - const data = await hasuraUtil.request(query, payload) +const getNodesSummary = async () => { let total = 0 - const totalByType = {} - - data.producers.forEach(producer => { - producer.bp_json.nodes.forEach(node => { - if (!totalByType[node.node_type]) { - totalByType[node.node_type] = 0 - } + const payload = {} + const [rows] = await sequelizeUtil.query(` + SELECT + value->>'node_type' as node_type, + count(*)::integer as nodes_count + FROM + producer, + json_array_elements(to_json(bp_json->'nodes')) + GROUP BY + value->>'node_type' + `) - totalByType[node.node_type]++ - total++ - }) + rows.forEach(row => { + payload[row.node_type || 'unknown'] = row.nodes_count + total += row.nodes_count }) - return { total, ...totalByType } + return { ...payload, total } +} + +const getUniqueLocations = async () => { + const [rows] = await sequelizeUtil.query(` + SELECT + bp_json->'org'->'location'->>'country' as type, + count(*)::integer as producers_count, + STRING_AGG (owner, ',') as producers + FROM + producer + GROUP BY + bp_json->'org'->'location'->>'country' + ORDER BY + producers_count DESC + `) + + return { + count: rows?.length || 0, + details: rows + } } const getBlockDistribution = async (range = '1 day') => { @@ -101,6 +111,7 @@ const getStats = async () => { transactions_in_last_hour transactions_in_last_day transactions_in_last_week + average_daily_transactions_in_last_week last_round last_block_at tps_all_time_high @@ -263,12 +274,6 @@ const syncTPSAllTimeHigh = async () => { } const newValue = rows[0] - const blocks = newValue.blocks.split(',') - - for (let index = 0; index < blocks.length; index++) { - const block = await getBlockUsage(blocks[index]) - blocks[index] = block - } if (parseInt(newValue.transactions_count) < lastValue.transactions_count) { await udpateStats({ @@ -277,10 +282,18 @@ const syncTPSAllTimeHigh = async () => { checked_at: end.toISOString() } }) + syncTPSAllTimeHigh() return } + const blocks = newValue.blocks.split(',') + + for (let index = 0; index < blocks.length; index++) { + const block = await getBlockUsage(blocks[index]) + blocks[index] = block + } + await udpateStats({ tps_all_time_high: { ...newValue, @@ -293,24 +306,26 @@ const syncTPSAllTimeHigh = async () => { } const sync = async () => { - const transactionsInLastWeek = await getTransactionsInTimeRage({ - start: moment().subtract(1, 'week'), - end: moment() - }) + const transactionsInLastWeek = await getTransactionsInTimeRage( + moment().subtract(1, 'week'), + moment() + ) const payload = { - transactions_in_last_hour: await getTransactionsInTimeRage({ - start: moment().subtract(1, 'hour'), - end: moment() - }), - transactions_in_last_day: await getTransactionsInTimeRage({ - start: moment().subtract(1, 'day'), - end: moment() - }), + transactions_in_last_hour: await getTransactionsInTimeRage( + moment().subtract(1, 'hour'), + moment() + ), + transactions_in_last_day: await getTransactionsInTimeRage( + moment().subtract(1, 'day'), + moment() + ), transactions_in_last_week: transactionsInLastWeek, average_daily_transactions_in_last_week: transactionsInLastWeek / 7, - nodes_summary: await getNodesSummary() + nodes_summary: await getNodesSummary(), + unique_locations: await getUniqueLocations() } const stats = await getStats() + if (stats) { await udpateStats(payload) return diff --git a/hasura/metadata/tables.yaml b/hasura/metadata/tables.yaml index 001aef13..a9877c6a 100644 --- a/hasura/metadata/tables.yaml +++ b/hasura/metadata/tables.yaml @@ -98,5 +98,6 @@ - transactions_in_last_day - transactions_in_last_hour - transactions_in_last_week + - unique_locations - updated_at filter: {} diff --git a/hasura/migrations/1622180276502_alter_table_public_stat_add_column_unique_locations/down.sql b/hasura/migrations/1622180276502_alter_table_public_stat_add_column_unique_locations/down.sql new file mode 100644 index 00000000..7005afc1 --- /dev/null +++ b/hasura/migrations/1622180276502_alter_table_public_stat_add_column_unique_locations/down.sql @@ -0,0 +1,4 @@ +-- Could not auto-generate a down migration. +-- Please write an appropriate down migration for the SQL below: +-- alter table "public"."stat" add column "unique_locations" jsonb + null default jsonb_build_object(); diff --git a/hasura/migrations/1622180276502_alter_table_public_stat_add_column_unique_locations/up.sql b/hasura/migrations/1622180276502_alter_table_public_stat_add_column_unique_locations/up.sql new file mode 100644 index 00000000..c5b2cb09 --- /dev/null +++ b/hasura/migrations/1622180276502_alter_table_public_stat_add_column_unique_locations/up.sql @@ -0,0 +1,2 @@ +alter table "public"."stat" add column "unique_locations" jsonb + null default jsonb_build_object(); diff --git a/webapp/src/components/TransactionsHistory.js b/webapp/src/components/TransactionsHistory.js index bc7f728a..8017eab4 100644 --- a/webapp/src/components/TransactionsHistory.js +++ b/webapp/src/components/TransactionsHistory.js @@ -69,7 +69,7 @@ const TransactionsHistory = ({ t, classes }) => { > {data?.stats?.[0]?.tps_all_time_high?.blocks?.map( (block, index) => ( - <> + { data?.stats?.[0]?.tps_all_time_high?.blocks.length - 1 ? ', ' : ''} - + ) )} @@ -126,6 +126,22 @@ const TransactionsHistory = ({ t, classes }) => { + + + + {`${t('transactions')} ${t( + 'dailyAverage' + )}`} + + + + + @@ -139,6 +155,18 @@ const TransactionsHistory = ({ t, classes }) => { + + + + + {`${t('uniqueLocations')}`} + + + + ) } diff --git a/webapp/src/gql/producer.gql.js b/webapp/src/gql/producer.gql.js index 841ab451..05181202 100644 --- a/webapp/src/gql/producer.gql.js +++ b/webapp/src/gql/producer.gql.js @@ -89,6 +89,7 @@ export const BLOCK_TRANSACTIONS_HISTORY = gql` transactions_in_last_week average_daily_transactions_in_last_week tps_all_time_high + unique_locations updated_at } } diff --git a/webapp/src/language/en.json b/webapp/src/language/en.json index 6362d498..33c0c195 100644 --- a/webapp/src/language/en.json +++ b/webapp/src/language/en.json @@ -24,6 +24,7 @@ "lastHour": "Last Hour", "lastDay": "Last Day", "lastWeek": "Last Week", + "dailyAverage":"Daily Average", "lastYear": "Last Year", "itemsPerPage": "Items per page", "api": "API", @@ -115,7 +116,8 @@ "query": " Query Nodes", "producer": "Producer Nodes", "query,seed": "Nodos Query, Seed", - "undefined": "Undefined Nodes" + "undefined": "Undefined Nodes", + "uniqueLocations": "Unique Locations" }, "blockProducersRoute": {}, "rewardsDistributionRoute": { diff --git a/webapp/src/language/es.json b/webapp/src/language/es.json index a06d8043..61c72ace 100644 --- a/webapp/src/language/es.json +++ b/webapp/src/language/es.json @@ -25,6 +25,7 @@ "lastDay": "Último día", "lastWeek": "Última semana", "lastYear": "Último año", + "dailyAverage":"Promedio diario", "itemsPerPage": "Elementos por página", "api": "API", "ssl": "SSL", @@ -123,7 +124,8 @@ "producer": "Nodos Tipo Producer", "query,seed": "Nodos Query, Seed", "undefined": "Nodos Indefinidos", - "transactions": "Transacciones" + "transactions": "Transacciones", + "uniqueLocations": "Ubicaciones únicas" }, "blockProducersRoute": {}, "rewardsDistributionRoute": { diff --git a/webapp/src/models/eos.js b/webapp/src/models/eos.js index ce884391..156a5ef0 100644 --- a/webapp/src/models/eos.js +++ b/webapp/src/models/eos.js @@ -17,8 +17,8 @@ export default { producers: [] }, info: {}, - tps: [], - tpb: [], + tps: new Array(30).fill({ blocks: [], transactions: 0 }), + tpb: new Array(60).fill({ blocks: [], transactions: 0 }), tpsWaitingBlock: null }, reducers: { @@ -31,16 +31,16 @@ export default { updateTransactionsStats(state, item) { let tpb = state.tpb - if (state.tpb.length >= 30) { - tpb = state.tpb.splice(1, state.tpb.length) + if (state.tpb.length >= 60) { + tpb.pop() } tpb = [ - ...tpb, { blocks: [item.block], transactions: item.transactions - } + }, + ...tpb ] if (!state.tpsWaitingBlock) { @@ -54,15 +54,15 @@ export default { let tps = state.tps if (state.tps.length >= 30) { - tps = state.tps.splice(1, state.tps.length) + tps.pop() } tps = [ - ...tps, { blocks: [state.tpsWaitingBlock.block, item.block], transactions: state.tpsWaitingBlock.transactions + item.transactions - } + }, + ...tps ] return { diff --git a/webapp/src/routes/Home/TransactionInfo.js b/webapp/src/routes/Home/TransactionInfo.js index ccef09f8..3608eab6 100644 --- a/webapp/src/routes/Home/TransactionInfo.js +++ b/webapp/src/routes/Home/TransactionInfo.js @@ -14,6 +14,7 @@ import CardContent from '@material-ui/core/CardContent' import Box from '@material-ui/core/Box' import Typography from '@material-ui/core/Typography' import PlayArrowIcon from '@material-ui/icons/PlayArrow' +import LinearProgress from '@material-ui/core/LinearProgress' import { TRANSACTION_QUERY } from '../../gql' import { rangeOptions } from '../../utils' @@ -28,14 +29,22 @@ const TransactionInfo = ({ t, classes }) => { const theme = useTheme() const tps = useSelector((state) => state.eos.tps) const tpb = useSelector((state) => state.eos.tpb) - const [graphicData, setGraphicData] = useState([]) + const [graphicData, setGraphicData] = useState([ + { + name: t('transactionsPerSecond'), + color: theme.palette.secondary.main + }, + { + name: t('transactionsPerBlock'), + color: '#00C853' + } + ]) const [option, setOption] = useState(options[0]) const [pause, setPause] = useState(false) - const [getTransactionHistory, { data: trxHistory }] = + const [getTransactionHistory, { data: trxHistory, loading }] = useLazyQuery(TRANSACTION_QUERY) useEffect(() => { - const majorLength = tps.length > tpb.length ? tps.length : tpb.length const trxPerSecond = [] const trxPerBlock = [] @@ -43,33 +52,32 @@ const TransactionInfo = ({ t, classes }) => { return } - for (let index = 0; index < majorLength; index++) { - const labelBlockPS = `Blocks:[${(tps[index] - ? tps[index].blocks - : [''] - ).join()}]` - const labelBlockPB = `Blocks:[${(tpb[index] - ? tpb[index].blocks - : [] - ).join()}]` - - trxPerSecond.push([ - labelBlockPS, - tps[index] ? tps[index].transactions : 0 - ]) - trxPerBlock.push([labelBlockPB, tpb[index] ? tpb[index].transactions : 0]) + for (let index = 0; index < tpb.length; index++) { + trxPerBlock.push({ + name: `Block: ${tpb[index].blocks.join()}`, + y: tpb[index].transactions, + x: index > 0 ? index / 2 : index + }) + } + + for (let index = 0; index < tps.length; index++) { + trxPerSecond.push({ + name: `Blocks: ${tps[index].blocks.join(', ')}`, + y: tps[index].transactions, + x: index + }) } setGraphicData([ { name: t('transactionsPerSecond'), color: theme.palette.secondary.main, - data: trxPerSecond.reverse() + data: trxPerSecond }, { name: t('transactionsPerBlock'), color: '#00C853', - data: trxPerBlock.reverse() + data: trxPerBlock } ]) // eslint-disable-next-line @@ -80,6 +88,7 @@ const TransactionInfo = ({ t, classes }) => { return } + setGraphicData([]) getTransactionHistory({ variables: { range: option } }) @@ -94,7 +103,6 @@ const TransactionInfo = ({ t, classes }) => { if (!trxHistory?.transactions?.length) { setGraphicData([]) - return } @@ -143,7 +151,7 @@ const TransactionInfo = ({ t, classes }) => { )} option === options[0].value && setPause(!pause)} + onClick={() => option === options[0] && setPause(!pause)} className={clsx(classes.pauseButton, { [classes.disableButton]: option !== options[0] })} @@ -155,7 +163,7 @@ const TransactionInfo = ({ t, classes }) => { width={20} height={20} color={ - option !== options[0].value + option !== options[0] ? theme.palette.action.disabled : theme.palette.common.black } @@ -165,7 +173,7 @@ const TransactionInfo = ({ t, classes }) => { - + {loading && }