Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
883 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,181 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.ComponentModel.DataAnnotations; | ||
using System.Threading.Tasks; | ||
using Microsoft.AspNetCore.Mvc; | ||
|
||
using Tzkt.Api.Models; | ||
using Tzkt.Api.Repositories; | ||
|
||
namespace Tzkt.Api.Controllers | ||
{ | ||
[ApiController] | ||
[Route("v1/statistics")] | ||
public class StatisticsController : ControllerBase | ||
{ | ||
private readonly StatisticsRepository Statistics; | ||
|
||
public StatisticsController(StatisticsRepository statistics) | ||
{ | ||
Statistics = statistics; | ||
} | ||
|
||
/// <summary> | ||
/// Get statistics | ||
/// </summary> | ||
/// <remarks> | ||
/// Returns a list of end-of-block statistics. | ||
/// </remarks> | ||
/// <param name="level">Filters statistics by level.</param> | ||
/// <param name="timestamp">Filters statistics by timestamp.</param> | ||
/// <param name="select">Specify comma-separated list of fields to include into response or leave it undefined to return full object. If you select single field, response will be an array of values in both `.fields` and `.values` modes.</param> | ||
/// <param name="sort">Sorts delegators by specified field. Supported fields: `id` (default), `level`, `cycle`, `date`.</param> | ||
/// <param name="offset">Specifies which or how many items should be skipped</param> | ||
/// <param name="limit">Maximum number of items to return</param> | ||
/// <param name="quote">Comma-separated list of ticker symbols to inject historical prices into response</param> | ||
/// <returns></returns> | ||
[HttpGet] | ||
public async Task<ActionResult<IEnumerable<Statistics>>> Get( | ||
Int32Parameter level, | ||
TimestampParameter timestamp, | ||
SelectParameter select, | ||
SortParameter sort, | ||
OffsetParameter offset, | ||
[Range(0, 10000)] int limit = 100, | ||
Symbols quote = Symbols.None) | ||
{ | ||
#region validate | ||
if (sort != null && !sort.Validate("id", "level", "cycle", "date")) | ||
return new BadRequest($"{nameof(sort)}", "Sorting by the specified field is not allowed."); | ||
#endregion | ||
|
||
if (select == null) | ||
return Ok(await Statistics.Get(StatisticsPeriod.None, null, level, timestamp, null, sort, offset, limit, quote)); | ||
|
||
if (select.Values != null) | ||
{ | ||
if (select.Values.Length == 1) | ||
return Ok(await Statistics.Get(StatisticsPeriod.None, null, level, timestamp, null, sort, offset, limit, select.Values[0], quote)); | ||
else | ||
return Ok(await Statistics.Get(StatisticsPeriod.None, null, level, timestamp, null, sort, offset, limit, select.Values, quote)); | ||
} | ||
else | ||
{ | ||
if (select.Fields.Length == 1) | ||
return Ok(await Statistics.Get(StatisticsPeriod.None, null, level, timestamp, null, sort, offset, limit, select.Fields[0], quote)); | ||
else | ||
{ | ||
return Ok(new SelectionResponse | ||
{ | ||
Cols = select.Fields, | ||
Rows = await Statistics.Get(StatisticsPeriod.None, null, level, timestamp, null, sort, offset, limit, select.Fields, quote) | ||
}); | ||
} | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Get daily statistics | ||
/// </summary> | ||
/// <remarks> | ||
/// Returns a list of end-of-day statistics. | ||
/// </remarks> | ||
/// <param name="date">Filters statistics by date.</param> | ||
/// <param name="select">Specify comma-separated list of fields to include into response or leave it undefined to return full object. If you select single field, response will be an array of values in both `.fields` and `.values` modes.</param> | ||
/// <param name="sort">Sorts delegators by specified field. Supported fields: `id` (default), `level`, `cycle`, `date`.</param> | ||
/// <param name="offset">Specifies which or how many items should be skipped</param> | ||
/// <param name="limit">Maximum number of items to return</param> | ||
/// <param name="quote">Comma-separated list of ticker symbols to inject historical prices into response</param> | ||
/// <returns></returns> | ||
[HttpGet("daily")] | ||
public async Task<ActionResult<IEnumerable<Statistics>>> GetDaily( | ||
DateTimeParameter date, | ||
SelectParameter select, | ||
SortParameter sort, | ||
OffsetParameter offset, | ||
[Range(0, 10000)] int limit = 100, | ||
Symbols quote = Symbols.None) | ||
{ | ||
#region validate | ||
if (sort != null && !sort.Validate("id", "level", "cycle", "date")) | ||
return new BadRequest($"{nameof(sort)}", "Sorting by the specified field is not allowed."); | ||
#endregion | ||
|
||
if (select == null) | ||
return Ok(await Statistics.Get(StatisticsPeriod.Daily, null, null, null, date, sort, offset, limit, quote)); | ||
|
||
if (select.Values != null) | ||
{ | ||
if (select.Values.Length == 1) | ||
return Ok(await Statistics.Get(StatisticsPeriod.Daily, null, null, null, date, sort, offset, limit, select.Values[0], quote)); | ||
else | ||
return Ok(await Statistics.Get(StatisticsPeriod.Daily, null, null, null, date, sort, offset, limit, select.Values, quote)); | ||
} | ||
else | ||
{ | ||
if (select.Fields.Length == 1) | ||
return Ok(await Statistics.Get(StatisticsPeriod.Daily, null, null, null, date, sort, offset, limit, select.Fields[0], quote)); | ||
else | ||
{ | ||
return Ok(new SelectionResponse | ||
{ | ||
Cols = select.Fields, | ||
Rows = await Statistics.Get(StatisticsPeriod.Daily, null, null, null, date, sort, offset, limit, select.Fields, quote) | ||
}); | ||
} | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Get cyclic statistics | ||
/// </summary> | ||
/// <remarks> | ||
/// Returns a list of end-of-cycle statistics. | ||
/// </remarks> | ||
/// <param name="cycle">Filters statistics by cycle.</param> | ||
/// <param name="select">Specify comma-separated list of fields to include into response or leave it undefined to return full object. If you select single field, response will be an array of values in both `.fields` and `.values` modes.</param> | ||
/// <param name="sort">Sorts delegators by specified field. Supported fields: `id` (default), `level`, `cycle`, `date`.</param> | ||
/// <param name="offset">Specifies which or how many items should be skipped</param> | ||
/// <param name="limit">Maximum number of items to return</param> | ||
/// <param name="quote">Comma-separated list of ticker symbols to inject historical prices into response</param> | ||
/// <returns></returns> | ||
[HttpGet("cyclic")] | ||
public async Task<ActionResult<IEnumerable<Statistics>>> GetCycles( | ||
Int32Parameter cycle, | ||
SelectParameter select, | ||
SortParameter sort, | ||
OffsetParameter offset, | ||
[Range(0, 10000)] int limit = 100, | ||
Symbols quote = Symbols.None) | ||
{ | ||
#region validate | ||
if (sort != null && !sort.Validate("id", "level", "cycle", "date")) | ||
return new BadRequest($"{nameof(sort)}", "Sorting by the specified field is not allowed."); | ||
#endregion | ||
|
||
if (select == null) | ||
return Ok(await Statistics.Get(StatisticsPeriod.Cyclic, cycle, null, null, null, sort, offset, limit, quote)); | ||
|
||
if (select.Values != null) | ||
{ | ||
if (select.Values.Length == 1) | ||
return Ok(await Statistics.Get(StatisticsPeriod.Cyclic, cycle, null, null, null, sort, offset, limit, select.Values[0], quote)); | ||
else | ||
return Ok(await Statistics.Get(StatisticsPeriod.Cyclic, cycle, null, null, null, sort, offset, limit, select.Values, quote)); | ||
} | ||
else | ||
{ | ||
if (select.Fields.Length == 1) | ||
return Ok(await Statistics.Get(StatisticsPeriod.Cyclic, cycle, null, null, null, sort, offset, limit, select.Fields[0], quote)); | ||
else | ||
{ | ||
return Ok(new SelectionResponse | ||
{ | ||
Cols = select.Fields, | ||
Rows = await Statistics.Get(StatisticsPeriod.Cyclic, cycle, null, null, null, sort, offset, limit, select.Fields, quote) | ||
}); | ||
} | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
using System; | ||
|
||
namespace Tzkt.Api.Models | ||
{ | ||
public class Statistics | ||
{ | ||
/// <summary> | ||
/// Cycle at the end of which the statistics has been calculated. This field is only present in cyclic statistics. | ||
/// </summary> | ||
public int? Cycle { get; set; } | ||
|
||
/// <summary> | ||
/// Day at the end of which the statistics has been calculated. This field is only present in daily statistics. | ||
/// </summary> | ||
public DateTime? Date { get; set; } | ||
|
||
/// <summary> | ||
/// Level of the block at which the statistics has been calculated | ||
/// </summary> | ||
public int Level { get; set; } | ||
|
||
/// <summary> | ||
/// Timestamp of the block at which the statistics has been calculated (ISO 8601, e.g. `2020-02-20T02:40:57Z`) | ||
/// </summary> | ||
public DateTime Timestamp { get; set; } | ||
|
||
/// <summary> | ||
/// Total supply - all existing tokens (including locked vested funds and frozen funds) plus not yet activated fundraiser tokens | ||
/// </summary> | ||
public long TotalSupply { get; set; } | ||
|
||
/// <summary> | ||
/// Circulating supply - all active tokens which can affect supply and demand (can be spent/transferred) | ||
/// </summary> | ||
public long CirculatingSupply { get; set; } | ||
|
||
/// <summary> | ||
/// Total amount of tokens initially created when starting the blockchain | ||
/// </summary> | ||
public long TotalBootstrapped { get; set; } | ||
|
||
/// <summary> | ||
/// Total commitment amount (tokens to be activated by fundraisers) | ||
/// </summary> | ||
public long TotalCommitments { get; set; } | ||
|
||
/// <summary> | ||
/// Total amount of tokens activated by fundraisers | ||
/// </summary> | ||
public long TotalActivated { get; set; } | ||
|
||
/// <summary> | ||
/// Total amount of created/issued tokens | ||
/// </summary> | ||
public long TotalCreated { get; set; } | ||
|
||
/// <summary> | ||
/// Total amount of burned tokens | ||
/// </summary> | ||
public long TotalBurned { get; set; } | ||
|
||
/// <summary> | ||
/// Total amount of tokens locked on vested contracts | ||
/// </summary> | ||
public long TotalVested { get; set; } | ||
|
||
/// <summary> | ||
/// Total amount of frozen tokens (frozen security deposits, frozen rewards and frozen fees) | ||
/// </summary> | ||
public long TotalFrozen { get; set; } | ||
|
||
#region injecting | ||
/// <summary> | ||
/// Injected historical quote at the time of the block at which the statistics has been calculated | ||
/// </summary> | ||
public QuoteShort Quote { get; set; } | ||
#endregion | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
using System; | ||
using System.Linq; | ||
using System.Threading.Tasks; | ||
using Microsoft.AspNetCore.Mvc.ModelBinding; | ||
using Tzkt.Api.Services.Cache; | ||
|
||
namespace Tzkt.Api | ||
{ | ||
public class TimestampBinder : IModelBinder | ||
{ | ||
readonly TimeCache Time; | ||
|
||
public TimestampBinder(TimeCache time) | ||
{ | ||
Time = time; | ||
} | ||
|
||
public Task BindModelAsync(ModelBindingContext bindingContext) | ||
{ | ||
var model = bindingContext.ModelName; | ||
var hasValue = false; | ||
|
||
if (!bindingContext.TryGetDateTime($"{model}", ref hasValue, out var value)) | ||
return Task.CompletedTask; | ||
|
||
if (!bindingContext.TryGetDateTime($"{model}.eq", ref hasValue, out var eq)) | ||
return Task.CompletedTask; | ||
|
||
if (!bindingContext.TryGetDateTime($"{model}.ne", ref hasValue, out var ne)) | ||
return Task.CompletedTask; | ||
|
||
if (!bindingContext.TryGetDateTime($"{model}.gt", ref hasValue, out var gt)) | ||
return Task.CompletedTask; | ||
|
||
if (!bindingContext.TryGetDateTime($"{model}.ge", ref hasValue, out var ge)) | ||
return Task.CompletedTask; | ||
|
||
if (!bindingContext.TryGetDateTime($"{model}.lt", ref hasValue, out var lt)) | ||
return Task.CompletedTask; | ||
|
||
if (!bindingContext.TryGetDateTime($"{model}.le", ref hasValue, out var le)) | ||
return Task.CompletedTask; | ||
|
||
if (!bindingContext.TryGetDateTimeList($"{model}.in", ref hasValue, out var @in)) | ||
return Task.CompletedTask; | ||
|
||
if (!bindingContext.TryGetDateTimeList($"{model}.ni", ref hasValue, out var ni)) | ||
return Task.CompletedTask; | ||
|
||
if (!hasValue) | ||
{ | ||
bindingContext.Result = ModelBindingResult.Success(null); | ||
return Task.CompletedTask; | ||
} | ||
|
||
bindingContext.Result = ModelBindingResult.Success(new TimestampParameter | ||
{ | ||
Eq = (value ?? eq) == null ? null : (int?)Time.FindLevel((DateTime)(value ?? eq), SearchMode.Exact), | ||
Ne = ne == null ? null : (int?)Time.FindLevel((DateTime)ne, SearchMode.Exact), | ||
Gt = gt == null ? null : (int?)Time.FindLevel((DateTime)gt, SearchMode.ExactOrLower), | ||
Ge = ge == null ? null : (int?)Time.FindLevel((DateTime)ge, SearchMode.ExactOrHigher), | ||
Lt = lt == null ? null : (int?)Time.FindLevel((DateTime)lt, SearchMode.ExactOrHigher), | ||
Le = le == null ? null : (int?)Time.FindLevel((DateTime)le, SearchMode.ExactOrLower), | ||
In = @in?.Select(x => Time.FindLevel(x, SearchMode.Exact)).ToList(), | ||
Ni = ni?.Select(x => Time.FindLevel(x, SearchMode.Exact)).ToList(), | ||
}); | ||
|
||
return Task.CompletedTask; | ||
} | ||
} | ||
} |
Oops, something went wrong.