Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@
using ParadisePublicAPI.ProfilerDatabase;
using Swashbuckle.AspNetCore.Annotations;

namespace ParadisePublicAPI.Controllers {
namespace ParadisePublicAPI.Controllers.V1 {
/// <summary>
/// Profiler
/// </summary>
[ApiExplorerSettings(GroupName = "v1")]
[SwaggerTag("Query proc times from the profiler. Old data may be cleared with no notice")]
[Route("profiler")]
public class ProfilerController : Controller {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@
using ParadisePublicAPI.Models;
using Swashbuckle.AspNetCore.Annotations;

namespace ParadisePublicAPI.Controllers {
namespace ParadisePublicAPI.Controllers.V1 {
/// <summary>
/// Controller for querying statistics from game rounds
/// </summary>
[ApiExplorerSettings(GroupName = "v1")]
[SwaggerTag("Query statistics from game rounds")]
[Route("stats")]
public class StatsController : Controller {
Expand Down
62 changes: 62 additions & 0 deletions Controllers/V2/StatsController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Mvc;
using ParadisePublicAPI.Database;
using Swashbuckle.AspNetCore.Annotations;

namespace ParadisePublicAPI.Controllers.V2
{
/// <summary>
/// Controller for querying statistics from game rounds
/// </summary>
[ApiExplorerSettings(GroupName = "v2")]
[SwaggerTag("Query statistics from game rounds")]
[Route("v2/stats")]
public class StatsController : Controller {
private readonly paradise_gamedbContext _context;

public StatsController(paradise_gamedbContext context) {
_context = context;
}

/// <summary>
/// Gets the data of a specific feedback key for the specified rounds.
/// </summary>
/// <param name="key_name">The name of the desired feedback key.</param>
/// <param name="start_date">The start date to retrieve data for.</param>
/// <param name="end_date">The end date to retrieve data for.</param>
/// <returns>A list of key-value objects, where "data" is the feedback data and "round_id" is the round ID the data was recorded.</returns>
/// <response code="200">Round data successfully retrieved</response>
/// <response code="429">Rate limited by server</response>
[HttpGet("feedback")]
public IActionResult GetFeedbackRow([FromQuery, Required] string key_name, [FromQuery, Required] DateOnly start_date, [FromQuery, Required] DateOnly end_date) {
if (key_name == null) {
return BadRequest("No feedback key_name specified.");
}
if (start_date > end_date) {
return BadRequest($"start_date {start_date} is later than end_date {end_date}");
}
if (start_date.AddMonths(2) < end_date) {
return BadRequest("Only two months of data may be requested in one query.");
}
var start_datetime = start_date.ToDateTime(TimeOnly.MinValue);
var end_datetime = end_date.ToDateTime(TimeOnly.MaxValue);
var feedbacks = (from feedback in _context.Feedbacks
join round in _context.Rounds on feedback.RoundId equals round.Id
orderby round.Id
where feedback.KeyName == key_name
&& round.ShutdownDatetime != null
&& round.InitializeDatetime >= start_datetime
&& round.InitializeDatetime <= end_datetime
// feedback.JSON begins with `{"data": ...` so we strip the first curly brace
// in order to interpolate the rest of the JSON data into the resultant string
select $"{{\"round_id\": {round.Id}, {feedback.Json.Substring(1)}");
Comment on lines +42 to +52
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this manages to resolve to a single query, including the substring manip, which is pretty cool:

SELECT `r`.`id`, 
       SUBSTRING(`f`.`json`, 1 + 1, CHAR_LENGTH(`f`.`json`))
FROM `feedback` AS `f`
INNER JOIN `round` AS `r` 
    ON `f`.`round_id` = `r`.`id`
WHERE (((`f`.`key_name` = @__key_name_0) 
        AND `r`.`shutdown_datetime` IS NOT NULL) 
        AND (`r`.`initialize_datetime` >= @__start_datetime_1)) 
        AND (`r`.`initialize_datetime` <= @__end_datetime_2)
ORDER BY `r`.`id`


return new ContentResult()
{
Content = "[" + string.Join(", ", feedbacks) + "]",
ContentType = "application/json",
StatusCode = 200
};
Comment on lines +55 to +59
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hate that we are manually constructing JSON but this PR has been up long enough.

}
}
}
7 changes: 7 additions & 0 deletions Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@
Title = "Paradise Public API",
Description = "Paradise Station public API for data querying. This API may change with no notice.<br>Source: <a href=\"https://github.com/ParadiseSS13/ParadisePublicAPI\">https://github.com/ParadiseSS13/ParadisePublicAPI</a><br>Requests are limited to 500 every minute, and 3600 every hour."
});
options.SwaggerDoc("v2", new OpenApiInfo
{
Version = "v2",
Title = "Paradise Public API",
Description = "Paradise Station public API for data querying. This API may change with no notice.<br>Source: <a href=\"https://github.com/ParadiseSS13/ParadisePublicAPI\">https://github.com/ParadiseSS13/ParadisePublicAPI</a><br>Requests are limited to 500 every minute, and 3600 every hour."
});
});

// Setup Game DB
Expand Down Expand Up @@ -71,6 +77,7 @@
app.UseSwagger();
app.UseSwaggerUI(options => {
options.SwaggerEndpoint("swagger/v1/swagger.json", "v1");
options.SwaggerEndpoint("swagger/v2/swagger.json", "v2");
options.RoutePrefix = String.Empty;

});
Expand Down