Added game last played, and total play time stats
* Added game last played, and total play time stats
This commit is contained in:
BIN
gaseous-server/.DS_Store
vendored
BIN
gaseous-server/.DS_Store
vendored
Binary file not shown.
99
gaseous-server/Classes/Statistics.cs
Normal file
99
gaseous-server/Classes/Statistics.cs
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
using System.Data;
|
||||||
|
using gaseous_server.Models;
|
||||||
|
|
||||||
|
namespace gaseous_server.Classes
|
||||||
|
{
|
||||||
|
public class Statistics
|
||||||
|
{
|
||||||
|
public StatisticsModel RecordSession(Guid SessionId, long GameId, string UserId)
|
||||||
|
{
|
||||||
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
|
string sql;
|
||||||
|
Dictionary<string, object> dbDict;
|
||||||
|
|
||||||
|
if (SessionId == Guid.Empty)
|
||||||
|
{
|
||||||
|
// new session required
|
||||||
|
SessionId = Guid.NewGuid();
|
||||||
|
|
||||||
|
sql = "INSERT INTO UserTimeTracking (GameId, UserId, SessionId, SessionTime, SessionLength) VALUES (@gameid, @userid, @sessionid, @sessiontime, @sessionlength);";
|
||||||
|
dbDict = new Dictionary<string, object>{
|
||||||
|
{ "gameid", GameId },
|
||||||
|
{ "userid", UserId },
|
||||||
|
{ "sessionid", SessionId },
|
||||||
|
{ "sessiontime", DateTime.UtcNow },
|
||||||
|
{ "sessionlength", 1 }
|
||||||
|
};
|
||||||
|
|
||||||
|
db.ExecuteNonQuery(sql, dbDict);
|
||||||
|
|
||||||
|
return new StatisticsModel{
|
||||||
|
GameId = GameId,
|
||||||
|
SessionId = SessionId,
|
||||||
|
SessionStart = (DateTime)dbDict["sessiontime"],
|
||||||
|
SessionLength = (int)dbDict["sessionlength"]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// update existing session
|
||||||
|
sql = "UPDATE UserTimeTracking SET SessionLength = SessionLength + @sessionlength WHERE GameId = @gameid AND UserId = @userid AND SessionId = @sessionid;";
|
||||||
|
dbDict = new Dictionary<string, object>{
|
||||||
|
{ "gameid", GameId },
|
||||||
|
{ "userid", UserId },
|
||||||
|
{ "sessionid", SessionId },
|
||||||
|
{ "sessionlength", 1 }
|
||||||
|
};
|
||||||
|
|
||||||
|
db.ExecuteNonQuery(sql, dbDict);
|
||||||
|
|
||||||
|
sql = "SELECT * FROM UserTimeTracking WHERE GameId = @gameid AND UserId = @userid AND SessionId = @sessionid;";
|
||||||
|
DataTable data = db.ExecuteCMD(sql, dbDict);
|
||||||
|
|
||||||
|
return new StatisticsModel{
|
||||||
|
GameId = (long)data.Rows[0]["GameId"],
|
||||||
|
SessionId = Guid.Parse(data.Rows[0]["SessionId"].ToString()),
|
||||||
|
SessionStart = (DateTime)data.Rows[0]["SessionTime"],
|
||||||
|
SessionLength = (int)data.Rows[0]["SessionLength"]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public StatisticsModel? GetSession(long GameId, string UserId)
|
||||||
|
{
|
||||||
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
|
string sql = "SELECT SUM(SessionLength) AS TotalLength FROM UserTimeTracking WHERE GameId = @gameid AND UserId = @userid;";
|
||||||
|
Dictionary<string, object> dbDict = new Dictionary<string, object>{
|
||||||
|
{ "gameid", GameId },
|
||||||
|
{ "userid", UserId }
|
||||||
|
};
|
||||||
|
|
||||||
|
DataTable data = db.ExecuteCMD(sql, dbDict);
|
||||||
|
|
||||||
|
if (data.Rows.Count == 0)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (data.Rows[0]["TotalLength"] == DBNull.Value)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int TotalTime = int.Parse(data.Rows[0]["TotalLength"].ToString());
|
||||||
|
|
||||||
|
sql = "SELECT * FROM UserTimeTracking WHERE GameId = @gameid AND UserId = @userid ORDER BY SessionTime DESC LIMIT 1;";
|
||||||
|
data = db.ExecuteCMD(sql, dbDict);
|
||||||
|
|
||||||
|
return new StatisticsModel{
|
||||||
|
GameId = GameId,
|
||||||
|
SessionLength = TotalTime,
|
||||||
|
SessionStart = (DateTime)data.Rows[0]["SessionTime"]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
104
gaseous-server/Controllers/V1.1/StatisticsController.cs
Normal file
104
gaseous-server/Controllers/V1.1/StatisticsController.cs
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using gaseous_server.Models;
|
||||||
|
using gaseous_server.Classes;
|
||||||
|
using Authentication;
|
||||||
|
using Microsoft.AspNetCore.Identity;
|
||||||
|
using Asp.Versioning;
|
||||||
|
|
||||||
|
namespace gaseous_server.Controllers.v1_1
|
||||||
|
{
|
||||||
|
[Route("api/v{version:apiVersion}/[controller]")]
|
||||||
|
[ApiVersion("1.0")]
|
||||||
|
[ApiVersion("1.1")]
|
||||||
|
[ApiController]
|
||||||
|
public class StatisticsController: ControllerBase
|
||||||
|
{
|
||||||
|
private readonly UserManager<ApplicationUser> _userManager;
|
||||||
|
private readonly SignInManager<ApplicationUser> _signInManager;
|
||||||
|
|
||||||
|
public StatisticsController(
|
||||||
|
UserManager<ApplicationUser> userManager,
|
||||||
|
SignInManager<ApplicationUser> signInManager
|
||||||
|
)
|
||||||
|
{
|
||||||
|
_userManager = userManager;
|
||||||
|
_signInManager = signInManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MapToApiVersion("1.0")]
|
||||||
|
[MapToApiVersion("1.1")]
|
||||||
|
[HttpPost]
|
||||||
|
[Authorize]
|
||||||
|
[ProducesResponseType(typeof(Models.StatisticsModel), StatusCodes.Status200OK)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
|
[Route("Games/{GameId}/")]
|
||||||
|
public async Task<ActionResult> NewRecordStatistics(long GameId)
|
||||||
|
{
|
||||||
|
var user = await _userManager.GetUserAsync(User);
|
||||||
|
|
||||||
|
if (user != null)
|
||||||
|
{
|
||||||
|
Statistics statistics = new Statistics();
|
||||||
|
return Ok(statistics.RecordSession(Guid.Empty, GameId, user.Id));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return Unauthorized();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[MapToApiVersion("1.0")]
|
||||||
|
[MapToApiVersion("1.1")]
|
||||||
|
[HttpPut]
|
||||||
|
[Authorize]
|
||||||
|
[ProducesResponseType(typeof(Models.StatisticsModel), StatusCodes.Status200OK)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
|
[Route("Games/{GameId}/{SessionId}")]
|
||||||
|
public async Task<ActionResult> SubsequentRecordStatistics(long GameId, Guid SessionId)
|
||||||
|
{
|
||||||
|
var user = await _userManager.GetUserAsync(User);
|
||||||
|
|
||||||
|
if (user != null)
|
||||||
|
{
|
||||||
|
Statistics statistics = new Statistics();
|
||||||
|
return Ok(statistics.RecordSession(SessionId, GameId, user.Id));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return Unauthorized();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[MapToApiVersion("1.0")]
|
||||||
|
[MapToApiVersion("1.1")]
|
||||||
|
[HttpGet]
|
||||||
|
[Authorize]
|
||||||
|
[ProducesResponseType(typeof(Models.StatisticsModel), StatusCodes.Status200OK)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
|
[Route("Games/{GameId}")]
|
||||||
|
public async Task<ActionResult> GetStatistics(long GameId)
|
||||||
|
{
|
||||||
|
var user = await _userManager.GetUserAsync(User);
|
||||||
|
|
||||||
|
if (user != null)
|
||||||
|
{
|
||||||
|
Statistics statistics = new Statistics();
|
||||||
|
StatisticsModel? model = statistics.GetSession(GameId, user.Id);
|
||||||
|
if (model == null)
|
||||||
|
{
|
||||||
|
return NoContent();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return Ok(model);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return Unauthorized();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
17
gaseous-server/Models/StatisticsModel.cs
Normal file
17
gaseous-server/Models/StatisticsModel.cs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
namespace gaseous_server.Models
|
||||||
|
{
|
||||||
|
public class StatisticsModel
|
||||||
|
{
|
||||||
|
public Guid SessionId { get; set; } = Guid.Empty;
|
||||||
|
public long GameId { get; set; }
|
||||||
|
public DateTime SessionStart { get; set; }
|
||||||
|
public int SessionLength { get; set; }
|
||||||
|
public DateTime SessionEnd
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return SessionStart.AddMinutes(SessionLength);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
13
gaseous-server/Support/Database/MySQL/gaseous-1020.sql
Normal file
13
gaseous-server/Support/Database/MySQL/gaseous-1020.sql
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
CREATE TABLE `UserTimeTracking` (
|
||||||
|
`GameId` BIGINT NULL,
|
||||||
|
`UserId` VARCHAR(45) NULL,
|
||||||
|
`SessionId` VARCHAR(45) NULL,
|
||||||
|
`SessionTime` DATETIME NULL,
|
||||||
|
`SessionLength` INT NULL,
|
||||||
|
INDEX `UserId_idx` (`UserId` ASC) VISIBLE,
|
||||||
|
INDEX `SessionId_idx` (`SessionId` ASC) VISIBLE,
|
||||||
|
CONSTRAINT `UserId`
|
||||||
|
FOREIGN KEY (`UserId`)
|
||||||
|
REFERENCES `Users` (`Id`)
|
||||||
|
ON DELETE CASCADE
|
||||||
|
ON UPDATE NO ACTION);
|
@@ -61,6 +61,8 @@
|
|||||||
<None Remove="Support\Database\MySQL\gaseous-1016.sql" />
|
<None Remove="Support\Database\MySQL\gaseous-1016.sql" />
|
||||||
<None Remove="Support\Database\MySQL\gaseous-1017.sql" />
|
<None Remove="Support\Database\MySQL\gaseous-1017.sql" />
|
||||||
<None Remove="Support\Database\MySQL\gaseous-1018.sql" />
|
<None Remove="Support\Database\MySQL\gaseous-1018.sql" />
|
||||||
|
<None Remove="Support\Database\MySQL\gaseous-1019.sql" />
|
||||||
|
<None Remove="Support\Database\MySQL\gaseous-1020.sql" />
|
||||||
<None Remove="Classes\Metadata\" />
|
<None Remove="Classes\Metadata\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -102,5 +104,7 @@
|
|||||||
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1016.sql" />
|
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1016.sql" />
|
||||||
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1017.sql" />
|
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1017.sql" />
|
||||||
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1018.sql" />
|
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1018.sql" />
|
||||||
|
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1019.sql" />
|
||||||
|
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1020.sql" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
BIN
gaseous-server/wwwroot/.DS_Store
vendored
BIN
gaseous-server/wwwroot/.DS_Store
vendored
Binary file not shown.
@@ -70,4 +70,30 @@
|
|||||||
bg.setAttribute('style', 'background-image: url("/api/v1.1/Games/' + gameId + '/artwork/' + artworks[artworksPosition] + '/image/original/' + artworks[artworksPosition] + '.jpg"); background-position: center; background-repeat: no-repeat; background-size: cover; filter: blur(10px); -webkit-filter: blur(10px);');
|
bg.setAttribute('style', 'background-image: url("/api/v1.1/Games/' + gameId + '/artwork/' + artworks[artworksPosition] + '/image/original/' + artworks[artworksPosition] + '.jpg"); background-position: center; background-repeat: no-repeat; background-size: cover; filter: blur(10px); -webkit-filter: blur(10px);');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// statistics
|
||||||
|
var SessionId = undefined;
|
||||||
|
|
||||||
|
function SaveStatistics() {
|
||||||
|
var model;
|
||||||
|
if (SessionId == undefined) {
|
||||||
|
ajaxCall(
|
||||||
|
'/api/v1.1/Statistics/Games/' + gameId,
|
||||||
|
'POST',
|
||||||
|
function (success) {
|
||||||
|
SessionId = success.sessionId;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
ajaxCall(
|
||||||
|
'/api/v1.1/Statistics/Games/' + gameId + '/' + SessionId,
|
||||||
|
'PUT',
|
||||||
|
function (success) {
|
||||||
|
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setInterval(SaveStatistics, 60000);
|
||||||
</script>
|
</script>
|
||||||
|
@@ -63,6 +63,16 @@
|
|||||||
<div id="gamescreenshots_gallery_panel"></div>
|
<div id="gamescreenshots_gallery_panel"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="gamestatistics">
|
||||||
|
<div id="gamestatistics_lastplayed" class="gamestatistics_box">
|
||||||
|
<span class="gamestatistics_label">Last Played</span>
|
||||||
|
<span id="gamestatistics_lastplayed_value" class="gamestatistics_value">-</span>
|
||||||
|
</div>
|
||||||
|
<div id="gamestatistics_timeplayed" class="gamestatistics_box">
|
||||||
|
<span class="gamestatistics_label">Total Play Time</span>
|
||||||
|
<span id="gamestatistics_timeplayed_value" class="gamestatistics_value">-</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div id="gamesummarytext">
|
<div id="gamesummarytext">
|
||||||
<span id="gamesummarytext_label" class="line-clamp-4"></span>
|
<span id="gamesummarytext_label" class="line-clamp-4"></span>
|
||||||
<p id="gamesummarytext_label_button_expand" class="text_link" style="display: none;" onclick="document.querySelector('#gamesummarytext_label').classList.remove('line-clamp-4'); document.querySelector('#gamesummarytext_label_button_expand').setAttribute('style', 'display: none;'); document.querySelector('#gamesummarytext_label_button_contract').setAttribute('style', '');">Read more...</p>
|
<p id="gamesummarytext_label_button_expand" class="text_link" style="display: none;" onclick="document.querySelector('#gamesummarytext_label').classList.remove('line-clamp-4'); document.querySelector('#gamesummarytext_label_button_expand').setAttribute('style', 'display: none;'); document.querySelector('#gamesummarytext_label_button_contract').setAttribute('style', '');">Read more...</p>
|
||||||
@@ -256,6 +266,30 @@
|
|||||||
gamePublisherLabel.setAttribute('style', 'display: none;');
|
gamePublisherLabel.setAttribute('style', 'display: none;');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// load statistics
|
||||||
|
ajaxCall('/api/v1.1/Statistics/Games/' + gameId, 'GET', function (result) {
|
||||||
|
var gameStat_lastPlayed = document.getElementById('gamestatistics_lastplayed_value');
|
||||||
|
var gameStat_timePlayed = document.getElementById('gamestatistics_timeplayed_value');
|
||||||
|
if (result) {
|
||||||
|
// gameStat_lastPlayed.innerHTML = moment(result.sessionEnd).format("YYYY-MM-DD h:mm:ss a");
|
||||||
|
const dateOptions = {
|
||||||
|
//weekday: 'long',
|
||||||
|
year: 'numeric',
|
||||||
|
month: 'long',
|
||||||
|
day: 'numeric'
|
||||||
|
};
|
||||||
|
gameStat_lastPlayed.innerHTML = new Date(result.sessionEnd).toLocaleDateString(undefined, dateOptions);
|
||||||
|
if (result.sessionLength >= 60) {
|
||||||
|
gameStat_timePlayed.innerHTML = Number(result.sessionLength / 60) + " hours";
|
||||||
|
} else {
|
||||||
|
gameStat_timePlayed.innerHTML = Number(result.sessionLength) + " minutes";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
gameStat_lastPlayed.innerHTML = '-';
|
||||||
|
gameStat_timePlayed.innerHTML = '-';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// load release date
|
// load release date
|
||||||
var gameSummaryRelease = document.getElementById('gamesummary_firstrelease');
|
var gameSummaryRelease = document.getElementById('gamesummary_firstrelease');
|
||||||
if (result.firstReleaseDate) {
|
if (result.firstReleaseDate) {
|
||||||
|
@@ -905,6 +905,31 @@ iframe {
|
|||||||
border-color: white;
|
border-color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#gamestatistics {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
background-color: rgba(56, 56, 56, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.gamestatistics_box {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gamestatistics_label {
|
||||||
|
display: block;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 14px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
padding: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gamestatistics_value {
|
||||||
|
display: block;
|
||||||
|
padding: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
#gamesummarytext_label {
|
#gamesummarytext_label {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user