mirror of
https://github.com/SoDOff-Project/sodoff.git
synced 2025-10-11 16:28:50 -07:00
Rating system (NOT COMPLETE!)
```sql CREATE TABLE [Ratings] ( [Id] INTEGER PRIMARY KEY AUTOINCREMENT, [OwnerId] INTEGER NOT NULL, [RankId] INTEGER NOT NULL, [CategoryID] INTEGER NOT NULL, [RatedEntityID] INTEGER NULL, [RatedUserID] TEXT NULL, [Value] INTEGER NOT NULL, [Date] TEXT NOT NULL ); CREATE TABLE [RatingRanks] ( [Id] INTEGER PRIMARY KEY AUTOINCREMENT, [CategoryID] INTEGER NOT NULL, [RatedEntityID] INTEGER NULL, [RatedUserID] TEXT NULL, [Rank] INTEGER NOT NULL, [RatingAverage] REAL NOT NULL, [TotalVotes] INTEGER NOT NULL, [UpdateDate] TEXT NOT NULL ); ```
This commit is contained in:
parent
0127aab379
commit
3f355ff9f7
@ -1,13 +1,24 @@
|
|||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using sodoff.Attributes;
|
||||||
|
using sodoff.Configuration;
|
||||||
using sodoff.Model;
|
using sodoff.Model;
|
||||||
using sodoff.Schema;
|
using sodoff.Schema;
|
||||||
|
using sodoff.Services;
|
||||||
using sodoff.Util;
|
using sodoff.Util;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
|
||||||
namespace sodoff.Controllers.Common;
|
namespace sodoff.Controllers.Common;
|
||||||
|
|
||||||
public class RatingController : Controller
|
public class RatingController : Controller
|
||||||
{
|
{
|
||||||
|
private readonly DBContext ctx;
|
||||||
|
|
||||||
|
public RatingController(DBContext ctx) {
|
||||||
|
this.ctx = ctx;
|
||||||
|
}
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
[Produces("application/xml")]
|
[Produces("application/xml")]
|
||||||
[Route("MissionWebService.asmx/GetPayout")] // used by World Of Jumpstart
|
[Route("MissionWebService.asmx/GetPayout")] // used by World Of Jumpstart
|
||||||
@ -103,4 +114,177 @@ public class RatingController : Controller
|
|||||||
return Ok(5);
|
return Ok(5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This method is the only thing that adds ratings.
|
||||||
|
public RatingInfo SubmitRating(Viking viking, int category, int? eID, string? uID, int value) {
|
||||||
|
RatingRank? rank;
|
||||||
|
Rating? rating = viking.Ratings.FirstOrDefault(
|
||||||
|
r => category == r.CategoryID && r.RatedEntityID == eID && r.RatedUserID == uID
|
||||||
|
);
|
||||||
|
bool newRating = rating == null;
|
||||||
|
if (newRating) {
|
||||||
|
rating = new Rating {
|
||||||
|
OwnerId = viking.Id,
|
||||||
|
CategoryID = category,
|
||||||
|
RatedEntityID = eID,
|
||||||
|
RatedUserID = uID
|
||||||
|
};
|
||||||
|
ctx.Ratings.Add(rating);
|
||||||
|
rank = ctx.RatingRanks.FirstOrDefault(rr => rr.CategoryID == category && rr.RatedEntityID == eID && rr.RatedUserID == uID);
|
||||||
|
} else {
|
||||||
|
rank = rating.Rank;
|
||||||
|
}
|
||||||
|
if (rank == null) {
|
||||||
|
rank = new RatingRank {
|
||||||
|
CategoryID = category,
|
||||||
|
RatedEntityID = eID,
|
||||||
|
RatedUserID = uID,
|
||||||
|
Rank = 0 // Start here, work way down.
|
||||||
|
};
|
||||||
|
ctx.RatingRanks.Add(rank);
|
||||||
|
}
|
||||||
|
rank.TotalVotes = rank.Ratings?.Count??1;
|
||||||
|
if (newRating) {
|
||||||
|
rating.Rank = rank;
|
||||||
|
rank.TotalVotes++;
|
||||||
|
}
|
||||||
|
if (rank.Ratings != null) {
|
||||||
|
rank.RatingAverage = 0;
|
||||||
|
foreach (Rating r in rank.Ratings) {
|
||||||
|
if (r == rating) continue;
|
||||||
|
rank.RatingAverage += (float)((decimal)r.Value / (decimal)rank.TotalVotes);
|
||||||
|
}
|
||||||
|
rank.RatingAverage += (float)((decimal)value / (decimal)rank.TotalVotes);
|
||||||
|
} else {
|
||||||
|
rank.RatingAverage = value;
|
||||||
|
}
|
||||||
|
if (eID != -1 || uID != null) {
|
||||||
|
RatingRank[] ranks = ctx.RatingRanks
|
||||||
|
.Where(rr => rr.CategoryID == category) // Only rank by category.
|
||||||
|
.OrderBy(rr => rr.Rank)
|
||||||
|
.ToArray();
|
||||||
|
bool resortOthers = false;
|
||||||
|
for (int i=0;i<ranks.Length;i++) {
|
||||||
|
if (ranks[i] == rank) continue;
|
||||||
|
if (!resortOthers && ranks[i].RatingAverage < rank.RatingAverage) {
|
||||||
|
rank.Rank = i+1;
|
||||||
|
resortOthers = true;
|
||||||
|
}
|
||||||
|
if (resortOthers) ranks[i].Rank++;
|
||||||
|
Console.WriteLine(ranks[i].Id+" "+resortOthers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rating.Value = value;
|
||||||
|
rating.Date = DateTime.UtcNow;
|
||||||
|
rank.UpdateDate = rating.Date;
|
||||||
|
ctx.SaveChanges();
|
||||||
|
RatingInfo info = new() {
|
||||||
|
Id = rating.Id,
|
||||||
|
OwnerUid = viking.Uid,
|
||||||
|
CategoryID = category,
|
||||||
|
RatedEntityID = eID,
|
||||||
|
Value = value,
|
||||||
|
Date = rating.Date
|
||||||
|
};
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
[Produces("application/xml")]
|
||||||
|
[Route("RatingWebService.asmx/SetRating")]
|
||||||
|
[VikingSession]
|
||||||
|
public IActionResult SubmitRating(Viking viking, [FromForm] int categoryID, [FromForm] int ratedEntityID, [FromForm] int ratedValue) {
|
||||||
|
return Ok(SubmitRating(viking, categoryID, ratedEntityID, null, ratedValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
[Produces("application/xml")]
|
||||||
|
[Route("RatingWebService.asmx/SetUserRating")]
|
||||||
|
[VikingSession]
|
||||||
|
public IActionResult SubmitUserRating(Viking viking, [FromForm] int categoryID, [FromForm] string ratedUserID, [FromForm] int ratedValue) {
|
||||||
|
return Ok(SubmitRating(viking, categoryID, null, ratedUserID, ratedValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
[Produces("application/xml")]
|
||||||
|
[Route("RatingWebService.asmx/GetRatingByRatedEntity")]
|
||||||
|
public RatingInfo[] GetAllRatings([FromForm] int categoryID, [FromForm] int ratedEntityID) {
|
||||||
|
return ctx.Ratings
|
||||||
|
.Where(r => r.CategoryID == categoryID && r.RatedEntityID == ratedEntityID && r.RatedUserID == null)
|
||||||
|
.Select(r => new RatingInfo {
|
||||||
|
Id = r.Id,
|
||||||
|
OwnerUid = r.Owner.Uid,
|
||||||
|
CategoryID = r.CategoryID,
|
||||||
|
RatedEntityID = r.RatedEntityID,
|
||||||
|
Value = r.Value,
|
||||||
|
Date = r.Date
|
||||||
|
}
|
||||||
|
).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
[Route("RatingWebService.asmx/DeleteEntityRating")]
|
||||||
|
[VikingSession]
|
||||||
|
public IActionResult DeleteRating(Viking viking, [FromForm] int categoryID, [FromForm] int ratedEntityID) {
|
||||||
|
Rating? rating = viking.Ratings.FirstOrDefault(
|
||||||
|
r => categoryID == r.CategoryID && r.RatedEntityID == ratedEntityID && r.RatedUserID == null
|
||||||
|
);
|
||||||
|
if (rating != null) ctx.Ratings.Remove(rating);
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
[Produces("application/xml")]
|
||||||
|
[Route("RatingWebService.asmx/GetTopRatedByCategoryID")]
|
||||||
|
public RatingRankInfo[] GetRanks([FromForm] int categoryID, [FromForm] int numberOfRecord) {
|
||||||
|
return ctx.RatingRanks
|
||||||
|
.Where(rr => categoryID == rr.CategoryID && rr.RatedUserID == null)
|
||||||
|
.Take(numberOfRecord)
|
||||||
|
.Select(rr => new RatingRankInfo(rr))
|
||||||
|
.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
[Produces("application/xml")]
|
||||||
|
[Route("RatingWebService.asmx/GetTopRatedUserByCategoryID")]
|
||||||
|
public IActionResult GetUserRanks([FromForm] int categoryID, [FromForm] int numberOfRecord) {
|
||||||
|
return Ok(new ArrayOfUserRatingRankInfo {
|
||||||
|
UserRatingRankInfo = ctx.RatingRanks
|
||||||
|
.Where(rr => rr.RatedUserID != null && (categoryID == rr.CategoryID
|
||||||
|
|| (categoryID == 4 && rr.CategoryID == 5) // The party board searches for 4 but the pod rating is set by 5.
|
||||||
|
))
|
||||||
|
.Take(numberOfRecord)
|
||||||
|
.Select(rr => new UserRatingRankInfo { RankInfo = new RatingRankInfo(rr) })
|
||||||
|
.ToArray()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
[Produces("application/xml")]
|
||||||
|
[Route("RatingWebService.asmx/GetEntityRatedRank")]
|
||||||
|
public IActionResult GetRank([FromForm] int categoryID, [FromForm] int ratedEntityID) {
|
||||||
|
// TODO: Add a shortcut here for shipwreck lagoon tracks.
|
||||||
|
RatingRank? rank = ctx.RatingRanks.FirstOrDefault(rr => categoryID == rr.CategoryID && rr.RatedEntityID == ratedEntityID);
|
||||||
|
if (rank == null) return Ok();
|
||||||
|
return Ok(new RatingRankInfo(rank));
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
[Produces("application/xml")]
|
||||||
|
[Route("RatingWebService.asmx/GetRatingForRatedUser")]
|
||||||
|
public IActionResult GetUserRating([FromForm] int categoryID, [FromForm] string ratedUserID) {
|
||||||
|
Rating? rating = ctx.Ratings.FirstOrDefault(
|
||||||
|
r => categoryID == r.CategoryID && r.RatedEntityID == null && r.RatedUserID == ratedUserID
|
||||||
|
);
|
||||||
|
return Ok(rating?.Value ?? 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
[Produces("application/xml")]
|
||||||
|
[Route("RatingWebService.asmx/GetRatingForRatedEntity")]
|
||||||
|
public IActionResult GetRating([FromForm] int categoryID, [FromForm] int ratedEntityID) {
|
||||||
|
Rating? rating = ctx.Ratings.FirstOrDefault(
|
||||||
|
r => categoryID == r.CategoryID && r.RatedEntityID == ratedEntityID && r.RatedUserID == null
|
||||||
|
);
|
||||||
|
return Ok(rating?.Value ?? 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,8 @@ public class DBContext : DbContext {
|
|||||||
public DbSet<Neighborhood> Neighborhoods { get; set; } = null!;
|
public DbSet<Neighborhood> Neighborhoods { get; set; } = null!;
|
||||||
// we had a brief debate on whether it's neighborhoods or neighborheed
|
// we had a brief debate on whether it's neighborhoods or neighborheed
|
||||||
public DbSet<Group> Groups { get; set; } = null!;
|
public DbSet<Group> Groups { get; set; } = null!;
|
||||||
|
public DbSet<Rating> Ratings { get; set; } = null!;
|
||||||
|
public DbSet<RatingRank> RatingRanks { get; set; } = null!;
|
||||||
|
|
||||||
private readonly IOptions<ApiServerConfig> config;
|
private readonly IOptions<ApiServerConfig> config;
|
||||||
|
|
||||||
@ -140,6 +142,9 @@ public class DBContext : DbContext {
|
|||||||
builder.Entity<Viking>().HasMany(v => v.Groups)
|
builder.Entity<Viking>().HasMany(v => v.Groups)
|
||||||
.WithMany(e => e.Vikings);
|
.WithMany(e => e.Vikings);
|
||||||
|
|
||||||
|
builder.Entity<Viking>().HasMany(v => v.Ratings)
|
||||||
|
.WithOne(r => r.Owner);
|
||||||
|
|
||||||
// Dragons
|
// Dragons
|
||||||
builder.Entity<Dragon>().HasOne(d => d.Viking)
|
builder.Entity<Dragon>().HasOne(d => d.Viking)
|
||||||
.WithMany(e => e.Dragons)
|
.WithMany(e => e.Dragons)
|
||||||
@ -260,5 +265,17 @@ public class DBContext : DbContext {
|
|||||||
// Groups
|
// Groups
|
||||||
builder.Entity<Group>().HasMany(r => r.Vikings)
|
builder.Entity<Group>().HasMany(r => r.Vikings)
|
||||||
.WithMany(e => e.Groups);
|
.WithMany(e => e.Groups);
|
||||||
|
|
||||||
|
// Rating
|
||||||
|
builder.Entity<Rating>().HasOne(r => r.Owner)
|
||||||
|
.WithMany(v => v.Ratings)
|
||||||
|
.HasForeignKey(r => r.OwnerId);
|
||||||
|
|
||||||
|
builder.Entity<Rating>().HasOne(r => r.Rank)
|
||||||
|
.WithMany(rr => rr.Ratings)
|
||||||
|
.HasForeignKey(r => r.RankId);
|
||||||
|
|
||||||
|
builder.Entity<RatingRank>().HasMany(rr => rr.Ratings)
|
||||||
|
.WithOne(r => r.Rank);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
28
src/Model/Rating.cs
Normal file
28
src/Model/Rating.cs
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
using sodoff.Schema;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace sodoff.Model;
|
||||||
|
|
||||||
|
public class Rating {
|
||||||
|
[Key]
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
/// <summary>Viking that controls this data.</summary>
|
||||||
|
public virtual Viking? Owner { get; set; }
|
||||||
|
|
||||||
|
public virtual RatingRank? Rank { get; set; }
|
||||||
|
|
||||||
|
/// <summary>VikingId</summary>
|
||||||
|
public int OwnerId { get; set; }
|
||||||
|
|
||||||
|
public int RankId { get; set; } // Done this to prevent it from generating an unnecessary pairs table.
|
||||||
|
|
||||||
|
public int CategoryID { get; set; }
|
||||||
|
|
||||||
|
public int? RatedEntityID { get; set; }
|
||||||
|
public string? RatedUserID { get; set; }
|
||||||
|
|
||||||
|
public int Value { get; set; }
|
||||||
|
|
||||||
|
public DateTime Date { get; set; }
|
||||||
|
}
|
25
src/Model/RatingRank.cs
Normal file
25
src/Model/RatingRank.cs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Xml.Serialization;
|
||||||
|
|
||||||
|
namespace sodoff.Model;
|
||||||
|
|
||||||
|
public class RatingRank {
|
||||||
|
[Key]
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
public int CategoryID { get; set; }
|
||||||
|
|
||||||
|
public int? RatedEntityID { get; set; }
|
||||||
|
public string? RatedUserID { get; set; }
|
||||||
|
|
||||||
|
public int Rank { get; set; }
|
||||||
|
|
||||||
|
/// <summary>On a scale of 1-5</summary>
|
||||||
|
public float RatingAverage { get; set; }
|
||||||
|
|
||||||
|
public int TotalVotes { get; set; }
|
||||||
|
|
||||||
|
public DateTime UpdateDate { get; set; }
|
||||||
|
|
||||||
|
public virtual ICollection<Rating> Ratings { get; set; } = null!;
|
||||||
|
}
|
@ -39,6 +39,7 @@ public class Viking {
|
|||||||
public virtual ICollection<MMORole> MMORoles { get; set; } = null!;
|
public virtual ICollection<MMORole> MMORoles { get; set; } = null!;
|
||||||
public virtual Neighborhood? Neighborhood { get; set; } = null!;
|
public virtual Neighborhood? Neighborhood { get; set; } = null!;
|
||||||
public virtual ICollection<Group> Groups { get; set; } = null!;
|
public virtual ICollection<Group> Groups { get; set; } = null!;
|
||||||
|
public virtual ICollection<Rating> Ratings { get; set; } = null!;
|
||||||
public virtual Dragon? SelectedDragon { get; set; }
|
public virtual Dragon? SelectedDragon { get; set; }
|
||||||
|
|
||||||
public DateTime? CreationDate { get; set; }
|
public DateTime? CreationDate { get; set; }
|
||||||
|
10
src/Schema/ArrayOfUserRatingRankInfo.cs
Normal file
10
src/Schema/ArrayOfUserRatingRankInfo.cs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
using System.Xml.Serialization;
|
||||||
|
|
||||||
|
namespace sodoff.Schema;
|
||||||
|
|
||||||
|
[XmlRoot(ElementName = "ArrayOfUserRatingRankInfo", Namespace = "")]
|
||||||
|
[Serializable]
|
||||||
|
public class ArrayOfUserRatingRankInfo {
|
||||||
|
[XmlElement(ElementName = "UserRatingRankInfo")]
|
||||||
|
public UserRatingRankInfo[] UserRatingRankInfo;
|
||||||
|
}
|
26
src/Schema/RatingInfo.cs
Normal file
26
src/Schema/RatingInfo.cs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
using sodoff.Model;
|
||||||
|
using System.Xml.Serialization;
|
||||||
|
|
||||||
|
namespace sodoff.Schema;
|
||||||
|
|
||||||
|
[XmlRoot(ElementName = "RatingInfo", Namespace = "")]
|
||||||
|
[Serializable]
|
||||||
|
public class RatingInfo {
|
||||||
|
[XmlElement(ElementName = "ID")]
|
||||||
|
public int Id;
|
||||||
|
|
||||||
|
[XmlElement(ElementName = "UID")]
|
||||||
|
public Guid OwnerUid;
|
||||||
|
|
||||||
|
[XmlElement(ElementName = "CID")]
|
||||||
|
public int CategoryID;
|
||||||
|
|
||||||
|
[XmlElement(ElementName = "EID")]
|
||||||
|
public int? RatedEntityID;
|
||||||
|
|
||||||
|
[XmlElement(ElementName = "RV")]
|
||||||
|
public int Value;
|
||||||
|
|
||||||
|
[XmlElement(ElementName = "RD")]
|
||||||
|
public DateTime Date;
|
||||||
|
}
|
41
src/Schema/RatingRankInfo.cs
Normal file
41
src/Schema/RatingRankInfo.cs
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
using sodoff.Model;
|
||||||
|
using System.Xml.Serialization;
|
||||||
|
|
||||||
|
namespace sodoff.Schema;
|
||||||
|
|
||||||
|
[XmlRoot(ElementName = "RatingRankInfo", Namespace = "")]
|
||||||
|
[Serializable]
|
||||||
|
public class RatingRankInfo {
|
||||||
|
|
||||||
|
public RatingRankInfo() {}
|
||||||
|
public RatingRankInfo(RatingRank rank) {
|
||||||
|
Id = rank.Id;
|
||||||
|
CategoryID = rank.CategoryID;
|
||||||
|
RatedEntityID = rank.RatedEntityID;
|
||||||
|
Rank = rank.Rank;
|
||||||
|
RatingAverage = rank.RatingAverage;
|
||||||
|
TotalVotes = rank.TotalVotes;
|
||||||
|
UpdateDate = rank.UpdateDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
[XmlElement(ElementName = "ID")]
|
||||||
|
public int Id;
|
||||||
|
|
||||||
|
[XmlElement(ElementName = "CID")]
|
||||||
|
public int CategoryID;
|
||||||
|
|
||||||
|
[XmlElement(ElementName = "EID")]
|
||||||
|
public int? RatedEntityID;
|
||||||
|
|
||||||
|
[XmlElement(ElementName = "R")]
|
||||||
|
public int Rank;
|
||||||
|
|
||||||
|
[XmlElement(ElementName = "RA")]
|
||||||
|
public float RatingAverage;
|
||||||
|
|
||||||
|
[XmlElement(ElementName = "TV")]
|
||||||
|
public int TotalVotes;
|
||||||
|
|
||||||
|
[XmlElement(ElementName = "UD")]
|
||||||
|
public DateTime UpdateDate;
|
||||||
|
}
|
14
src/Schema/UserRatingRankInfo.cs
Normal file
14
src/Schema/UserRatingRankInfo.cs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
using sodoff.Model;
|
||||||
|
using System.Xml.Serialization;
|
||||||
|
|
||||||
|
namespace sodoff.Schema;
|
||||||
|
|
||||||
|
[XmlRoot(ElementName = "URRI", Namespace = "")]
|
||||||
|
[Serializable]
|
||||||
|
public class UserRatingRankInfo {
|
||||||
|
[XmlElement(ElementName = "RI")]
|
||||||
|
public RatingRankInfo RankInfo;
|
||||||
|
|
||||||
|
[XmlElement(ElementName = "RUID")]
|
||||||
|
public Guid RatedUserID;
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user