diff --git a/gaseous-server/Classes/Auth/Classes/IdentityUser.cs b/gaseous-server/Classes/Auth/Classes/IdentityUser.cs index e263b70..e1ac7a0 100644 --- a/gaseous-server/Classes/Auth/Classes/IdentityUser.cs +++ b/gaseous-server/Classes/Auth/Classes/IdentityUser.cs @@ -11,5 +11,6 @@ namespace Authentication public class ApplicationUser : IdentityUser { public SecurityProfileViewModel SecurityProfile { get; set; } + public List UserPreferences { get; set; } } } diff --git a/gaseous-server/Classes/Auth/Classes/UserTable.cs b/gaseous-server/Classes/Auth/Classes/UserTable.cs index 293c663..07b3b0d 100644 --- a/gaseous-server/Classes/Auth/Classes/UserTable.cs +++ b/gaseous-server/Classes/Auth/Classes/UserTable.cs @@ -99,6 +99,7 @@ namespace Authentication user.AccessFailedCount = string.IsNullOrEmpty((string?)row["AccessFailedCount"]) ? 0 : int.Parse((string?)row["AccessFailedCount"]); user.TwoFactorEnabled = row["TwoFactorEnabled"] == "1" ? true:false; user.SecurityProfile = GetSecurityProfile(user); + user.UserPreferences = GetPreferences(user); } return user; @@ -135,6 +136,7 @@ namespace Authentication user.AccessFailedCount = string.IsNullOrEmpty((string?)row["AccessFailedCount"]) ? 0 : int.Parse((string?)row["AccessFailedCount"]); user.TwoFactorEnabled = row["TwoFactorEnabled"] == "1" ? true:false; user.SecurityProfile = GetSecurityProfile(user); + user.UserPreferences = GetPreferences(user); users.Add(user); } @@ -166,6 +168,7 @@ namespace Authentication user.AccessFailedCount = string.IsNullOrEmpty((string?)row["AccessFailedCount"]) ? 0 : int.Parse((string?)row["AccessFailedCount"]); user.TwoFactorEnabled = row["TwoFactorEnabled"] == "1" ? true:false; user.SecurityProfile = GetSecurityProfile(user); + user.UserPreferences = GetPreferences(user); users.Add(user); } @@ -273,6 +276,9 @@ namespace Authentication // set default security profile SetSecurityProfile(user, new SecurityProfileViewModel()); + // set default preferences + SetPreferences(user, new List()); + return _database.ExecuteCMD(commandText, parameters).Rows.Count; } @@ -283,7 +289,7 @@ namespace Authentication /// private int Delete(string userId) { - string commandText = "Delete from Users where Id = @userId"; + string commandText = "Delete from Users where Id = @userId; Delete from User_Settings where Id = @userId;"; Dictionary parameters = new Dictionary(); parameters.Add("@userId", userId); @@ -328,6 +334,9 @@ namespace Authentication // set the security profile SetSecurityProfile(user, user.SecurityProfile); + // set preferences + SetPreferences(user, user.UserPreferences); + return _database.ExecuteCMD(commandText, parameters).Rows.Count; } @@ -367,5 +376,59 @@ namespace Authentication return _database.ExecuteCMD(commandText, parameters).Rows.Count; } + + public List GetPreferences(TUser user) + { + Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); + string sql = "SELECT `Setting`, `Value` FROM User_Settings WHERE Id=@id;"; + Dictionary dbDict = new Dictionary(); + dbDict.Add("id", user.Id); + + DataTable data = db.ExecuteCMD(sql, dbDict); + + List userPrefs = new List(); + foreach (DataRow row in data.Rows) + { + UserPreferenceViewModel userPref = new UserPreferenceViewModel(); + userPref.Setting = (string)row["Setting"]; + userPref.Value = (string)row["Value"]; + userPrefs.Add(userPref); + } + + return userPrefs; + } + + public int SetPreferences(TUser user, List model) + { + List userPreferences = GetPreferences(user); + + Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); + + foreach (UserPreferenceViewModel modelItem in model) + { + bool prefItemFound = false; + foreach (UserPreferenceViewModel existing in userPreferences) + { + if (existing.Setting.ToLower() == modelItem.Setting.ToLower()) + { + prefItemFound = true; + break; + } + } + + string sql = "INSERT INTO User_Settings (`Id`, `Setting`, `Value`) VALUES (@id, @setting, @value);"; + if (prefItemFound == true) + { + sql = "UPDATE User_Settings SET `Value`=@value WHERE `Id`=@id AND `Setting`=@setting"; + } + Dictionary dbDict = new Dictionary(); + dbDict.Add("id", user.Id); + dbDict.Add("setting", modelItem.Setting); + dbDict.Add("value", modelItem.Value); + db.ExecuteNonQuery(sql, dbDict); + } + + return model.Count; + } } } diff --git a/gaseous-server/Classes/Auth/Models/ProfileViewModel.cs b/gaseous-server/Classes/Auth/Models/ProfileViewModel.cs index 3a1843e..a3a9903 100644 --- a/gaseous-server/Classes/Auth/Models/ProfileViewModel.cs +++ b/gaseous-server/Classes/Auth/Models/ProfileViewModel.cs @@ -7,6 +7,7 @@ namespace Authentication public string EmailAddress { get; set; } public List Roles { get; set; } public SecurityProfileViewModel SecurityProfile { get; set; } + public List UserPreferences { get; set; } public string HighestRole { get { diff --git a/gaseous-server/Classes/Auth/Models/SecurityProfileViewModel.cs b/gaseous-server/Classes/Auth/Models/SecurityProfileViewModel.cs index da493a2..b7e5407 100644 --- a/gaseous-server/Classes/Auth/Models/SecurityProfileViewModel.cs +++ b/gaseous-server/Classes/Auth/Models/SecurityProfileViewModel.cs @@ -5,13 +5,13 @@ namespace Authentication public class SecurityProfileViewModel { public AgeRestrictionItem AgeRestrictionPolicy { get; set; } = new AgeRestrictionItem{ - MaximumAgeRestriction = "Adult", + MaximumAgeRestriction = gaseous_server.Classes.Metadata.AgeRatings.AgeGroups.AgeRestrictionGroupings.Adult, IncludeUnrated = true }; public class AgeRestrictionItem { - public string MaximumAgeRestriction { get; set; } + public gaseous_server.Classes.Metadata.AgeRatings.AgeGroups.AgeRestrictionGroupings MaximumAgeRestriction { get; set; } public bool IncludeUnrated { get; set; } } } diff --git a/gaseous-server/Classes/Auth/Models/UserPreferenceViewModels.cs b/gaseous-server/Classes/Auth/Models/UserPreferenceViewModels.cs new file mode 100644 index 0000000..fb65819 --- /dev/null +++ b/gaseous-server/Classes/Auth/Models/UserPreferenceViewModels.cs @@ -0,0 +1,8 @@ +namespace Authentication; + +public class UserPreferenceViewModel +{ + public string Setting { get; set; } + + public string Value { get; set; } +} \ No newline at end of file diff --git a/gaseous-server/Classes/Collections.cs b/gaseous-server/Classes/Collections.cs index 7b8d349..9d5d4da 100644 --- a/gaseous-server/Classes/Collections.cs +++ b/gaseous-server/Classes/Collections.cs @@ -4,20 +4,28 @@ using System.IO.Compression; using System.Reflection; using System.Runtime.InteropServices; using System.Security.Cryptography; +using Authentication; using gaseous_server.Classes.Metadata; using gaseous_server.Controllers; using gaseous_server.Models; using IGDB.Models; +using Microsoft.AspNetCore.Identity; using Newtonsoft.Json; namespace gaseous_server.Classes { public class Collections { - public Collections() - { - - } + private readonly UserManager _userManager; + private readonly SignInManager _signInManager; + + public Collections( + UserManager userManager, + SignInManager signInManager) + { + _userManager = userManager; + _signInManager = signInManager; + } public static List GetCollections() { Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); @@ -211,8 +219,8 @@ namespace gaseous_server.Classes } } else { // get all platforms to pull from - FilterController filterController = new FilterController(); - platforms.AddRange((List)filterController.Filter()["platforms"]); + Dictionary FilterDict = Filters.Filter(AgeRatings.AgeGroups.AgeRestrictionGroupings.Adult, true); + platforms.AddRange((List)FilterDict["platforms"]); } // build collection diff --git a/gaseous-server/Classes/Filters.cs b/gaseous-server/Classes/Filters.cs new file mode 100644 index 0000000..e1b726d --- /dev/null +++ b/gaseous-server/Classes/Filters.cs @@ -0,0 +1,225 @@ +using System.Data; +using System.Reflection.Metadata.Ecma335; +using gaseous_server.Classes.Metadata; +using IGDB.Models; + +namespace gaseous_server.Classes +{ + public class Filters + { + public static Dictionary Filter(Metadata.AgeRatings.AgeGroups.AgeRestrictionGroupings MaximumAgeRestriction, bool IncludeUnrated) + { + Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); + + Dictionary FilterSet = new Dictionary(); + + // platforms + List platforms = new List(); + + string ageRestriction_Platform = "Game.AgeGroupId <= " + (int)MaximumAgeRestriction; + string ageRestriction_Generic = "view_Games.AgeGroupId <= " + (int)MaximumAgeRestriction; + if (IncludeUnrated == true) + { + ageRestriction_Platform += " OR Game.AgeGroupId IS NULL"; + ageRestriction_Generic += " OR view_Games.AgeGroupId IS NULL"; + } + + string sql = "SELECT DISTINCT Platform.Id, Platform.Abbreviation, Platform.AlternativeName, Platform.`Name`, Platform.PlatformLogo, (SELECT COUNT(*) AS GameCount FROM (SELECT DISTINCT Games_Roms.GameId AS ROMGameId, Games_Roms.PlatformId, view_Games.AgeGroupId FROM Games_Roms LEFT JOIN view_Games ON view_Games.Id = Games_Roms.GameId) Game WHERE Game.PlatformId = Platform.Id AND (" + ageRestriction_Platform + ")) AS GameCount FROM Platform LEFT JOIN Relation_Game_Platforms ON Relation_Game_Platforms.PlatformsId = Platform.Id LEFT JOIN view_Games ON view_Games.Id = Relation_Game_Platforms.GameId HAVING GameCount > 0 ORDER BY Platform.`Name`;"; + + DataTable dbResponse = db.ExecuteCMD(sql); + + foreach (DataRow dr in dbResponse.Rows) + { + FilterPlatform platformItem = new FilterPlatform(Classes.Metadata.Platforms.GetPlatform((long)dr["id"])); + platformItem.GameCount = (int)(long)dr["GameCount"]; + platforms.Add(platformItem); + + } + FilterSet.Add("platforms", platforms); + + // genres + List genres = new List(); + dbResponse = GetGenericFilterItem(db, "Genre", ageRestriction_Generic); + + foreach (DataRow dr in dbResponse.Rows) + { + FilterGenre genreItem = new FilterGenre(Classes.Metadata.Genres.GetGenres((long)dr["id"])); + genreItem.GameCount = (int)(long)dr["GameCount"]; + genres.Add(genreItem); + } + FilterSet.Add("genres", genres); + + // game modes + List gameModes = new List(); + dbResponse = GetGenericFilterItem(db, "GameMode", ageRestriction_Generic); + + foreach (DataRow dr in dbResponse.Rows) + { + FilterGameMode gameModeItem = new FilterGameMode(Classes.Metadata.GameModes.GetGame_Modes((long)dr["id"])); + gameModeItem.GameCount = (int)(long)dr["GameCount"]; + gameModes.Add(gameModeItem); + } + FilterSet.Add("gamemodes", gameModes); + + // player perspectives + List playerPerspectives = new List(); + dbResponse = GetGenericFilterItem(db, "PlayerPerspective", ageRestriction_Generic); + + foreach (DataRow dr in dbResponse.Rows) + { + FilterPlayerPerspective playerPerspectiveItem = new FilterPlayerPerspective(Classes.Metadata.PlayerPerspectives.GetGame_PlayerPerspectives((long)dr["id"])); + playerPerspectiveItem.GameCount = (int)(long)dr["GameCount"]; + playerPerspectives.Add(playerPerspectiveItem); + } + FilterSet.Add("playerperspectives", playerPerspectives); + + // themes + List themes = new List(); + dbResponse = GetGenericFilterItem(db, "Theme", ageRestriction_Generic); + + foreach (DataRow dr in dbResponse.Rows) + { + FilterTheme themeItem = new FilterTheme(Classes.Metadata.Themes.GetGame_Themes((long)dr["id"])); + themeItem.GameCount = (int)(long)dr["GameCount"]; + themes.Add(themeItem); + } + FilterSet.Add("themes", themes); + + // age groups + List agegroupings = new List(); + sql = "SELECT view_Games.Id, view_Games.AgeGroupId, COUNT(view_Games.Id) AS GameCount FROM view_Games WHERE (" + ageRestriction_Generic + ") GROUP BY view_Games.AgeGroupId ORDER BY view_Games.AgeGroupId DESC;"; + dbResponse = db.ExecuteCMD(sql); + + foreach (DataRow dr in dbResponse.Rows) + { + FilterAgeGrouping filterAgeGrouping = new FilterAgeGrouping(); + if (dr["AgeGroupId"] == DBNull.Value) + { + filterAgeGrouping.Id = (long)AgeRatings.AgeGroups.AgeRestrictionGroupings.Unclassified; + filterAgeGrouping.AgeGroup = AgeRatings.AgeGroups.AgeRestrictionGroupings.Unclassified; + } + else + { + filterAgeGrouping.Id = (long)(AgeRatings.AgeGroups.AgeRestrictionGroupings)dr["AgeGroupId"]; + filterAgeGrouping.AgeGroup = (AgeRatings.AgeGroups.AgeRestrictionGroupings)dr["AgeGroupId"]; + } + filterAgeGrouping.GameCount = (int)(long)dr["GameCount"]; + agegroupings.Add(filterAgeGrouping); + } + FilterSet.Add("agegroupings", agegroupings); + + return FilterSet; + } + + private static DataTable GetGenericFilterItem(Database db, string Name, string AgeRestriction_Generic) + { + string sql = "SELECT DISTINCT .Id, .`Name`, COUNT(view_Games.Id) AS GameCount FROM LEFT JOIN Relation_Game_s ON Relation_Game_s.sId = .Id LEFT JOIN view_Games ON view_Games.Id = Relation_Game_s.GameId WHERE (" + AgeRestriction_Generic + ") GROUP BY .Id ORDER BY .`Name`;"; + sql = sql.Replace("", Name); + DataTable dbResponse = db.ExecuteCMD(sql); + + return dbResponse; + } + + public class FilterPlatform : IGDB.Models.Platform + { + public FilterPlatform(Platform obj) + { + var properties = obj.GetType().GetProperties(); + foreach (var prop in properties) + { + if (prop.GetGetMethod() != null) + { + this.GetType().GetProperty(prop.Name).SetValue(this, prop.GetValue(obj)); + } + } + } + + public int GameCount { get; set; } + } + + public class FilterGenre : IGDB.Models.Genre + { + public FilterGenre(Genre obj) + { + var properties = obj.GetType().GetProperties(); + foreach (var prop in properties) + { + if (prop.GetGetMethod() != null) + { + this.GetType().GetProperty(prop.Name).SetValue(this, prop.GetValue(obj)); + } + } + } + + public int GameCount { get; set; } + } + + public class FilterGameMode : IGDB.Models.GameMode + { + public FilterGameMode(GameMode obj) + { + var properties = obj.GetType().GetProperties(); + foreach (var prop in properties) + { + if (prop.GetGetMethod() != null) + { + this.GetType().GetProperty(prop.Name).SetValue(this, prop.GetValue(obj)); + } + } + } + + public int GameCount { get; set; } + } + + public class FilterPlayerPerspective : IGDB.Models.PlayerPerspective + { + public FilterPlayerPerspective(PlayerPerspective obj) + { + var properties = obj.GetType().GetProperties(); + foreach (var prop in properties) + { + if (prop.GetGetMethod() != null) + { + this.GetType().GetProperty(prop.Name).SetValue(this, prop.GetValue(obj)); + } + } + } + + public int GameCount { get; set; } + } + + public class FilterTheme : IGDB.Models.Theme + { + public FilterTheme(Theme obj) + { + var properties = obj.GetType().GetProperties(); + foreach (var prop in properties) + { + if (prop.GetGetMethod() != null) + { + this.GetType().GetProperty(prop.Name).SetValue(this, prop.GetValue(obj)); + } + } + } + + public int GameCount { get; set; } + } + + public class FilterAgeGrouping + { + public long Id { get; set; } + + public AgeRatings.AgeGroups.AgeRestrictionGroupings AgeGroup { get ; set; } + + public string Name + { + get + { + return this.AgeGroup.ToString(); + } + } + + public int GameCount { get; set; } + } + } +} \ No newline at end of file diff --git a/gaseous-server/Classes/ImportGames.cs b/gaseous-server/Classes/ImportGames.cs index b935b04..f139d91 100644 --- a/gaseous-server/Classes/ImportGames.cs +++ b/gaseous-server/Classes/ImportGames.cs @@ -755,11 +755,11 @@ namespace gaseous_server.Classes string sql = ""; if (ForceExecute == false) { - sql = "SELECT * FROM Games_Roms WHERE (PlatformId = 0 OR GameId = 0 OR MetadataSource = 0) AND (LastMatchAttemptDate IS NULL OR LastMatchAttemptDate < @lastmatchattemptdate) LIMIT 100;"; + sql = "SELECT * FROM Games_Roms WHERE ((PlatformId = 0 OR GameId = 0) AND MetadataSource = 0) AND (LastMatchAttemptDate IS NULL OR LastMatchAttemptDate < @lastmatchattemptdate) LIMIT 100;"; } else { - sql = "SELECT * FROM Games_Roms WHERE (PlatformId = 0 OR GameId = 0 OR MetadataSource = 0);"; + sql = "SELECT * FROM Games_Roms WHERE ((PlatformId = 0 OR GameId = 0) AND MetadataSource = 0);"; } Dictionary dbDict = new Dictionary(); dbDict.Add("lastmatchattemptdate", DateTime.UtcNow.AddDays(-7)); diff --git a/gaseous-server/Classes/Metadata/AgeRating.cs b/gaseous-server/Classes/Metadata/AgeRating.cs index d761fc6..9c6fcd9 100644 --- a/gaseous-server/Classes/Metadata/AgeRating.cs +++ b/gaseous-server/Classes/Metadata/AgeRating.cs @@ -3,6 +3,7 @@ using System.Reflection; using System.Text.Json.Serialization; using IGDB; using IGDB.Models; +using Microsoft.CodeAnalysis.Classification; namespace gaseous_server.Classes.Metadata { @@ -153,6 +154,44 @@ namespace gaseous_server.Classes.Metadata public string[] Descriptions { get; set; } } + public static void PopulateAgeMap() + { + Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); + string sql = "DELETE FROM ClassificationMap;"; + Dictionary dbDict = new Dictionary(); + db.ExecuteNonQuery(sql); + + // loop all age groups + foreach(KeyValuePair ageGrouping in AgeGroups.AgeGroupingsFlat) + { + AgeGroups.AgeGroupItem ageGroupItem = ageGrouping.Value; + var properties = ageGroupItem.GetType().GetProperties(); + foreach (var prop in properties) + { + if (prop.GetGetMethod() != null) + { + List AgeRatingCategories = new List(Enum.GetNames(typeof(AgeRatingCategory))); + if (AgeRatingCategories.Contains(prop.Name)) + { + AgeRatingCategory ageRatingCategory = (AgeRatingCategory)Enum.Parse(typeof(AgeRatingCategory), prop.Name); + List ageRatingTitles = (List)prop.GetValue(ageGroupItem); + + foreach (AgeRatingTitle ageRatingTitle in ageRatingTitles) + { + dbDict.Clear(); + dbDict.Add("AgeGroupId", ageGrouping.Key); + dbDict.Add("ClassificationBoardId", ageRatingCategory); + dbDict.Add("RatingId", ageRatingTitle); + + sql = "INSERT INTO ClassificationMap (AgeGroupId, ClassificationBoardId, RatingId) VALUES (@AgeGroupId, @ClassificationBoardId, @RatingId);"; + db.ExecuteCMD(sql, dbDict); + } + } + } + } + } + } + public class AgeGroups { public AgeGroups() @@ -160,93 +199,55 @@ namespace gaseous_server.Classes.Metadata } - public static Dictionary> AgeGroupings + public static Dictionary> AgeGroupings { get { - return new Dictionary>{ + return new Dictionary>{ { - "Adult", new List{ Adult_Item, Mature_Item, Teen_Item, Child_Item } + AgeRestrictionGroupings.Adult, new List{ Adult_Item, Mature_Item, Teen_Item, Child_Item } }, { - "Mature", new List{ Mature_Item, Teen_Item, Child_Item } + AgeRestrictionGroupings.Mature, new List{ Mature_Item, Teen_Item, Child_Item } }, { - "Teen", new List{ Teen_Item, Child_Item } + AgeRestrictionGroupings.Teen, new List{ Teen_Item, Child_Item } }, { - "Child", new List{ Child_Item } + AgeRestrictionGroupings.Child, new List{ Child_Item } } }; } } - public static Dictionary AgeGroupingsFlat + public static Dictionary AgeGroupingsFlat { get { - return new Dictionary{ + return new Dictionary{ { - "Adult", Adult_Item + AgeRestrictionGroupings.Adult, Adult_Item }, { - "Mature", Mature_Item + AgeRestrictionGroupings.Mature, Mature_Item }, { - "Teen", Teen_Item + AgeRestrictionGroupings.Teen, Teen_Item }, { - "Child", Child_Item + AgeRestrictionGroupings.Child, Child_Item } }; } } - public static List ClassificationBoards + public enum AgeRestrictionGroupings { - get - { - ClassificationBoardItem boardItem = new ClassificationBoardItem{ - Board = AgeRatingCategory.ACB, - Classifications = new List{ - AgeRatingTitle.ACB_G, AgeRatingTitle.ACB_M, AgeRatingTitle.ACB_MA15, AgeRatingTitle.ACB_R18, AgeRatingTitle.ACB_RC - } - }; - - return new List{ - new ClassificationBoardItem{ - Board = AgeRatingCategory.ACB, - Classifications = new List{ - AgeRatingTitle.ACB_G, - AgeRatingTitle.ACB_M, - AgeRatingTitle.ACB_MA15, - AgeRatingTitle.ACB_R18, - AgeRatingTitle.ACB_RC - } - }, - new ClassificationBoardItem{ - Board = AgeRatingCategory.CERO, - Classifications = new List{ - AgeRatingTitle.CERO_A, - AgeRatingTitle.CERO_B, - AgeRatingTitle.CERO_C, - AgeRatingTitle.CERO_D, - AgeRatingTitle.CERO_Z - } - }, - new ClassificationBoardItem{ - Board = AgeRatingCategory.CLASS_IND, - Classifications = new List{ - AgeRatingTitle.CLASS_IND_L, - AgeRatingTitle.CLASS_IND_Ten, - AgeRatingTitle.CLASS_IND_Twelve, - AgeRatingTitle.CLASS_IND_Fourteen, - AgeRatingTitle.CLASS_IND_Sixteen, - AgeRatingTitle.CLASS_IND_Eighteen - } - } - }; - } + Adult = 4, + Mature = 3, + Teen = 2, + Child = 1, + Unclassified = 0 } readonly static AgeGroupItem Adult_Item = new AgeGroupItem{ @@ -341,12 +342,6 @@ namespace gaseous_server.Classes.Metadata } } } - - public class ClassificationBoardItem - { - public IGDB.Models.AgeRatingCategory Board { get; set; } - public List Classifications { get; set; } - } } } } diff --git a/gaseous-server/Classes/Metadata/Games.cs b/gaseous-server/Classes/Metadata/Games.cs index 863e360..35f4e41 100644 --- a/gaseous-server/Classes/Metadata/Games.cs +++ b/gaseous-server/Classes/Metadata/Games.cs @@ -174,17 +174,25 @@ namespace gaseous_server.Classes.Metadata } } + if (Game.AgeRatings != null) + { + foreach (long AgeRatingId in Game.AgeRatings.Ids) + { + AgeRating GameAgeRating = AgeRatings.GetAgeRatings(AgeRatingId); + } + } + + if (Game.ReleaseDates != null) + { + foreach (long ReleaseDateId in Game.ReleaseDates.Ids) + { + ReleaseDate GameReleaseDate = ReleaseDates.GetReleaseDates(ReleaseDateId); + } + } + // optional metadata - usually downloaded as needed if (getAllMetadata == true) { - if (Game.AgeRatings != null) - { - foreach (long AgeRatingId in Game.AgeRatings.Ids) - { - AgeRating GameAgeRating = AgeRatings.GetAgeRatings(AgeRatingId); - } - } - if (Game.AlternativeNames != null) { foreach (long AlternativeNameId in Game.AlternativeNames.Ids) @@ -334,5 +342,40 @@ namespace gaseous_server.Classes.Metadata search = 2, searchNoPlatform = 3 } + + public class MinimalGameItem + { + public MinimalGameItem(Game gameObject) + { + this.Id = gameObject.Id; + this.Name = gameObject.Name; + this.TotalRating = gameObject.TotalRating; + this.TotalRatingCount = gameObject.TotalRatingCount; + this.Cover = gameObject.Cover; + this.Artworks = gameObject.Artworks; + + // compile age ratings + this.AgeRatings = new List(); + if (gameObject.AgeRatings != null) + { + foreach (long ageRatingId in gameObject.AgeRatings.Ids) + { + AgeRating? rating = Classes.Metadata.AgeRatings.GetAgeRatings(ageRatingId); + if (rating != null) + { + this.AgeRatings.Add(rating); + } + } + } + } + + public long? Id { get; set; } + public string Name { get; set; } + public double? TotalRating { get; set; } + public int? TotalRatingCount { get; set; } + public IGDB.IdentityOrValue Cover { get; set; } + public IGDB.IdentitiesOrValues Artworks { get; set; } + public List AgeRatings { get; set; } + } } } \ No newline at end of file diff --git a/gaseous-server/Classes/Metadata/ReleaseDates.cs b/gaseous-server/Classes/Metadata/ReleaseDates.cs new file mode 100644 index 0000000..1869d43 --- /dev/null +++ b/gaseous-server/Classes/Metadata/ReleaseDates.cs @@ -0,0 +1,113 @@ +using System; +using IGDB; +using IGDB.Models; + + +namespace gaseous_server.Classes.Metadata +{ + public class ReleaseDates + { + const string fieldList = "fields category,checksum,created_at,date,game,human,m,platform,region,status,updated_at,y;"; + + public ReleaseDates() + { + } + + private static IGDBClient igdb = new IGDBClient( + // Found in Twitch Developer portal for your app + Config.IGDB.ClientId, + Config.IGDB.Secret + ); + + public static ReleaseDate? GetReleaseDates(long? Id) + { + if ((Id == 0) || (Id == null)) + { + return null; + } + else + { + Task RetVal = _GetReleaseDates(SearchUsing.id, Id); + return RetVal.Result; + } + } + + public static ReleaseDate GetReleaseDates(string Slug) + { + Task RetVal = _GetReleaseDates(SearchUsing.slug, Slug); + return RetVal.Result; + } + + private static async Task _GetReleaseDates(SearchUsing searchUsing, object searchValue) + { + // check database first + Storage.CacheStatus? cacheStatus = new Storage.CacheStatus(); + if (searchUsing == SearchUsing.id) + { + cacheStatus = Storage.GetCacheStatus("ReleaseDate", (long)searchValue); + } + else + { + cacheStatus = Storage.GetCacheStatus("ReleaseDate", (string)searchValue); + } + + // set up where clause + string WhereClause = ""; + switch (searchUsing) + { + case SearchUsing.id: + WhereClause = "where id = " + searchValue; + break; + case SearchUsing.slug: + WhereClause = "where slug = " + searchValue; + break; + default: + throw new Exception("Invalid search type"); + } + + ReleaseDate returnValue = new ReleaseDate(); + switch (cacheStatus) + { + case Storage.CacheStatus.NotPresent: + returnValue = await GetObjectFromServer(WhereClause); + Storage.NewCacheValue(returnValue); + break; + case Storage.CacheStatus.Expired: + try + { + returnValue = await GetObjectFromServer(WhereClause); + Storage.NewCacheValue(returnValue, true); + } + catch (Exception ex) + { + Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex); + returnValue = Storage.GetCacheValue(returnValue, "id", (long)searchValue); + } + break; + case Storage.CacheStatus.Current: + returnValue = Storage.GetCacheValue(returnValue, "id", (long)searchValue); + break; + default: + throw new Exception("How did you get here?"); + } + + return returnValue; + } + + private enum SearchUsing + { + id, + slug + } + + private static async Task GetObjectFromServer(string WhereClause) + { + // get ReleaseDates metadata + var results = await igdb.QueryAsync(IGDBClient.Endpoints.ReleaseDates, query: fieldList + " " + WhereClause + ";"); + var result = results.First(); + + return result; + } + } +} + diff --git a/gaseous-server/Classes/Metadata/Storage.cs b/gaseous-server/Classes/Metadata/Storage.cs index 20898ff..76c10bd 100644 --- a/gaseous-server/Classes/Metadata/Storage.cs +++ b/gaseous-server/Classes/Metadata/Storage.cs @@ -218,9 +218,19 @@ namespace gaseous_server.Classes.Metadata { DataRow dataRow = dt.Rows[0]; object returnObject = BuildCacheObject(EndpointType, dataRow); - ObjectCache.Add(Endpoint + SearchValue, new MemoryCacheObject{ - Object = returnObject - }); + try { + if (!ObjectCache.ContainsKey(Endpoint + SearchValue)) + { + ObjectCache.Add(Endpoint + SearchValue, new MemoryCacheObject{ + Object = returnObject + }); + } + } + catch + { + // unable add item to cache + ObjectCache.Clear(); + } return (T)returnObject; } } diff --git a/gaseous-server/Classes/MetadataManagement.cs b/gaseous-server/Classes/MetadataManagement.cs index 00a1320..9db4e2c 100644 --- a/gaseous-server/Classes/MetadataManagement.cs +++ b/gaseous-server/Classes/MetadataManagement.cs @@ -30,7 +30,16 @@ namespace gaseous_server.Classes } // update games - sql = "SELECT Id, `Name` FROM Game;"; + if (forceRefresh == true) + { + // when forced, only update games with ROMs for + sql = "SELECT Id, `Name` FROM view_GamesWithRoms;"; + } + else + { + // when run normally, update all games (since this will honour cache timeouts) + sql = "SELECT Id, `Name` FROM Game;"; + } dt = db.ExecuteCMD(sql); foreach (DataRow dr in dt.Rows) @@ -38,7 +47,7 @@ namespace gaseous_server.Classes try { Logging.Log(Logging.LogType.Information, "Metadata Refresh", "Refreshing metadata for game " + dr["name"] + " (" + dr["id"] + ")"); - Metadata.Games.GetGame((long)dr["id"], true, true, forceRefresh); + Metadata.Games.GetGame((long)dr["id"], true, false, forceRefresh); } catch (Exception ex) { diff --git a/gaseous-server/Controllers/V1.0/AccountController.cs b/gaseous-server/Controllers/V1.0/AccountController.cs index d077a80..eb2249b 100644 --- a/gaseous-server/Controllers/V1.0/AccountController.cs +++ b/gaseous-server/Controllers/V1.0/AccountController.cs @@ -1,3 +1,4 @@ +using System.Data; using System.Security.Claims; using System.Text; using Authentication; @@ -95,6 +96,7 @@ namespace gaseous_server.Controllers profile.EmailAddress = await _userManager.GetEmailAsync(user); profile.Roles = new List(await _userManager.GetRolesAsync(user)); profile.SecurityProfile = user.SecurityProfile; + profile.UserPreferences = user.UserPreferences; profile.Roles.Sort(); return Ok(profile); @@ -115,6 +117,7 @@ namespace gaseous_server.Controllers profile.EmailAddress = await _userManager.GetEmailAsync(user); profile.Roles = new List(await _userManager.GetRolesAsync(user)); profile.SecurityProfile = user.SecurityProfile; + profile.UserPreferences = user.UserPreferences; profile.Roles.Sort(); string profileString = "var userProfile = " + Newtonsoft.Json.JsonConvert.SerializeObject(profile, Newtonsoft.Json.Formatting.Indented) + ";"; @@ -392,5 +395,23 @@ namespace gaseous_server.Controllers return NotFound(); } } + + [HttpPost] + [Route("Preferences")] + public async Task SetPreferences(List model) + { + ApplicationUser? user = await _userManager.GetUserAsync(User); + if (user == null) + { + return Unauthorized(); + } + else + { + user.UserPreferences = model; + await _userManager.UpdateAsync(user); + + return Ok(); + } + } } } \ No newline at end of file diff --git a/gaseous-server/Controllers/V1.0/FilterController.cs b/gaseous-server/Controllers/V1.0/FilterController.cs index c83947a..422d7d7 100644 --- a/gaseous-server/Controllers/V1.0/FilterController.cs +++ b/gaseous-server/Controllers/V1.0/FilterController.cs @@ -3,10 +3,12 @@ using System.Collections.Generic; using System.Data; using System.Linq; using System.Threading.Tasks; +using Authentication; using gaseous_server.Classes; using IGDB.Models; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; namespace gaseous_server.Controllers @@ -18,96 +20,27 @@ namespace gaseous_server.Controllers [ApiController] public class FilterController : ControllerBase { + private readonly UserManager _userManager; + private readonly SignInManager _signInManager; + + public FilterController( + UserManager userManager, + SignInManager signInManager) + { + _userManager = userManager; + _signInManager = signInManager; + } + [MapToApiVersion("1.0")] [MapToApiVersion("1.1")] [HttpGet] [ProducesResponseType(StatusCodes.Status200OK)] //[ResponseCache(CacheProfileName = "5Minute")] - public Dictionary Filter() + public async Task FilterAsync() { - Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); - - Dictionary FilterSet = new Dictionary(); - - // platforms - List platforms = new List(); - //string sql = "SELECT Platform.Id, Platform.Abbreviation, Platform.AlternativeName, Platform.`Name`, Platform.PlatformLogo, (SELECT COUNT(Games_Roms.Id) AS RomCount FROM Games_Roms WHERE Games_Roms.PlatformId = Platform.Id) AS RomCount FROM Platform HAVING RomCount > 0 ORDER BY `Name`"; - string sql = "SELECT Platform.Id, Platform.Abbreviation, Platform.AlternativeName, Platform.`Name`, Platform.PlatformLogo, (SELECT COUNT(Games_Roms.Id) AS RomCount FROM Games_Roms WHERE Games_Roms.PlatformId = Platform.Id) AS RomCount, (SELECT COUNT(*) AS GameCount FROM (SELECT DISTINCT Games_Roms.GameId AS ROMGameId, Games_Roms.PlatformId FROM Games_Roms LEFT JOIN Game ON Game.Id = Games_Roms.GameId) Game WHERE Game.PlatformId = Platform.Id) AS GameCount FROM Platform HAVING RomCount > 0 ORDER BY `Name`"; - DataTable dbResponse = db.ExecuteCMD(sql); - - foreach (DataRow dr in dbResponse.Rows) - { - FilterPlatform platformItem = new FilterPlatform(Classes.Metadata.Platforms.GetPlatform((long)dr["id"])); - platformItem.RomCount = (int)(long)dr["RomCount"]; - platformItem.GameCount = (int)(long)dr["GameCount"]; - platforms.Add(platformItem); - - } - FilterSet.Add("platforms", platforms); - - // genres - List genres = new List(); - sql = "SELECT DISTINCT Relation_Game_Genres.GenresId AS id, Genre.`Name` FROM Relation_Game_Genres JOIN Genre ON Relation_Game_Genres.GenresId = Genre.Id WHERE Relation_Game_Genres.GameId IN (SELECT DISTINCT GameId FROM Games_Roms) ORDER BY `Name`;"; - dbResponse = db.ExecuteCMD(sql); - - foreach (DataRow dr in dbResponse.Rows) - { - genres.Add(Classes.Metadata.Genres.GetGenres((long)dr["id"])); - } - FilterSet.Add("genres", genres); - - // game modes - List gameModes = new List(); - sql = "SELECT DISTINCT Relation_Game_GameModes.GameModesId AS id, GameMode.`Name` FROM Relation_Game_GameModes JOIN GameMode ON Relation_Game_GameModes.GameModesId = GameMode.Id WHERE Relation_Game_GameModes.GameId IN (SELECT DISTINCT GameId FROM Games_Roms) ORDER BY `Name`;"; - dbResponse = db.ExecuteCMD(sql); - - foreach (DataRow dr in dbResponse.Rows) - { - gameModes.Add(Classes.Metadata.GameModes.GetGame_Modes((long)dr["id"])); - } - FilterSet.Add("gamemodes", gameModes); - - // player perspectives - List playerPerspectives = new List(); - sql = "SELECT DISTINCT Relation_Game_PlayerPerspectives.PlayerPerspectivesId AS id, PlayerPerspective.`Name` FROM Relation_Game_PlayerPerspectives JOIN PlayerPerspective ON Relation_Game_PlayerPerspectives.PlayerPerspectivesId = PlayerPerspective.Id WHERE Relation_Game_PlayerPerspectives.GameId IN (SELECT DISTINCT GameId FROM Games_Roms) ORDER BY `Name`;"; - dbResponse = db.ExecuteCMD(sql); - - foreach (DataRow dr in dbResponse.Rows) - { - playerPerspectives.Add(Classes.Metadata.PlayerPerspectives.GetGame_PlayerPerspectives((long)dr["id"])); - } - FilterSet.Add("playerperspectives", playerPerspectives); - - // themes - List themes = new List(); - sql = "SELECT DISTINCT Relation_Game_Themes.ThemesId AS id, Theme.`Name` FROM Relation_Game_Themes JOIN Theme ON Relation_Game_Themes.ThemesId = Theme.Id WHERE Relation_Game_Themes.GameId IN (SELECT DISTINCT GameId FROM Games_Roms) ORDER BY `Name`;"; - dbResponse = db.ExecuteCMD(sql); - - foreach (DataRow dr in dbResponse.Rows) - { - themes.Add(Classes.Metadata.Themes.GetGame_Themes((long)dr["id"])); - } - FilterSet.Add("themes", themes); - - return FilterSet; - } - - public class FilterPlatform : IGDB.Models.Platform - { - public FilterPlatform(Platform platform) - { - var properties = platform.GetType().GetProperties(); - foreach (var prop in properties) - { - if (prop.GetGetMethod() != null) - { - this.GetType().GetProperty(prop.Name).SetValue(this, prop.GetValue(platform)); - } - } - } - - public int RomCount { get; set; } - public int GameCount { get; set; } + var user = await _userManager.GetUserAsync(User); + + return Ok(Filters.Filter(user.SecurityProfile.AgeRestrictionPolicy.MaximumAgeRestriction, user.SecurityProfile.AgeRestrictionPolicy.IncludeUnrated)); } } } \ No newline at end of file diff --git a/gaseous-server/Controllers/V1.0/GamesController.cs b/gaseous-server/Controllers/V1.0/GamesController.cs index 65085ef..0f4e634 100644 --- a/gaseous-server/Controllers/V1.0/GamesController.cs +++ b/gaseous-server/Controllers/V1.0/GamesController.cs @@ -181,9 +181,10 @@ namespace gaseous_server.Controllers List AgeClassificationsList = new List(); foreach (string ratingGroup in ratinggroup.Split(',')) { - if (AgeGroups.AgeGroupings.ContainsKey(ratingGroup)) + AgeGroups.AgeRestrictionGroupings ageRestriction = (AgeGroups.AgeRestrictionGroupings)Enum.Parse(typeof(AgeGroups.AgeRestrictionGroupings), ratingGroup); + if (AgeGroups.AgeGroupings.ContainsKey(ageRestriction)) { - List ageGroups = AgeGroups.AgeGroupings[ratingGroup]; + List ageGroups = AgeGroups.AgeGroupings[ageRestriction]; foreach (AgeGroups.AgeGroupItem ageGroup in ageGroups) { AgeClassificationsList.AddRange(ageGroup.AgeGroupItemValues); @@ -824,6 +825,44 @@ namespace gaseous_server.Controllers } } + [MapToApiVersion("1.0")] + [MapToApiVersion("1.1")] + [HttpGet] + [Route("{GameId}/releasedates")] + [ProducesResponseType(typeof(List), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ResponseCache(CacheProfileName = "7Days")] + public ActionResult GameReleaseDates(long GameId) + { + try + { + IGDB.Models.Game gameObject = Classes.Metadata.Games.GetGame(GameId, false, false, false); + if (gameObject != null) + { + List rdObjects = new List(); + if (gameObject.ReleaseDates != null) + { + foreach (long icId in gameObject.ReleaseDates.Ids) + { + ReleaseDate releaseDate = Classes.Metadata.ReleaseDates.GetReleaseDates(icId); + + rdObjects.Add(releaseDate); + } + } + + return Ok(rdObjects); + } + else + { + return NotFound(); + } + } + catch + { + return NotFound(); + } + } + [MapToApiVersion("1.0")] [MapToApiVersion("1.1")] [HttpGet] diff --git a/gaseous-server/Controllers/V1.0/SystemController.cs b/gaseous-server/Controllers/V1.0/SystemController.cs index 46690ac..5283d80 100644 --- a/gaseous-server/Controllers/V1.0/SystemController.cs +++ b/gaseous-server/Controllers/V1.0/SystemController.cs @@ -82,6 +82,12 @@ namespace gaseous_server.Controllers Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); // get age ratings dictionary + Dictionary ClassificationBoardsStrings = new Dictionary(); + foreach(IGDB.Models.AgeRatingCategory ageRatingCategory in Enum.GetValues(typeof(IGDB.Models.AgeRatingCategory)) ) + { + ClassificationBoardsStrings.Add((int)ageRatingCategory, ageRatingCategory.ToString()); + } + Dictionary AgeRatingsStrings = new Dictionary(); foreach(IGDB.Models.AgeRatingTitle ageRatingTitle in Enum.GetValues(typeof(IGDB.Models.AgeRatingTitle)) ) { @@ -91,6 +97,9 @@ namespace gaseous_server.Controllers string ver = "var AppVersion = \"" + Assembly.GetExecutingAssembly().GetName().Version.ToString() + "\";" + Environment.NewLine + "var DBSchemaVersion = \"" + db.GetDatabaseSchemaVersion() + "\";" + Environment.NewLine + "var FirstRunStatus = " + Config.ReadSetting("FirstRunStatus", "0") + ";" + Environment.NewLine + + "var AgeRatingBoardsStrings = " + JsonSerializer.Serialize(ClassificationBoardsStrings, new JsonSerializerOptions{ + WriteIndented = true + }) + ";" + Environment.NewLine + "var AgeRatingStrings = " + JsonSerializer.Serialize(AgeRatingsStrings, new JsonSerializerOptions{ WriteIndented = true }) + ";" + Environment.NewLine + diff --git a/gaseous-server/Controllers/V1.1/GamesController.cs b/gaseous-server/Controllers/V1.1/GamesController.cs index 73e61df..8f2f6e4 100644 --- a/gaseous-server/Controllers/V1.1/GamesController.cs +++ b/gaseous-server/Controllers/V1.1/GamesController.cs @@ -39,33 +39,41 @@ namespace gaseous_server.Controllers.v1_1 [MapToApiVersion("1.1")] [HttpPost] - [ProducesResponseType(typeof(List), StatusCodes.Status200OK)] - public async Task Game_v1_1(GameSearchModel model) + [ProducesResponseType(typeof(GameReturnPackage), StatusCodes.Status200OK)] + public async Task Game_v1_1(GameSearchModel model, int pageNumber = 0, int pageSize = 0) { var user = await _userManager.GetUserAsync(User); if (user != null) { // apply security profile filtering - List RemoveAgeGroups = new List(); - switch (user.SecurityProfile.AgeRestrictionPolicy.MaximumAgeRestriction.ToLower()) + if (model.GameAgeRating.AgeGroupings.Count == 0) { - case "adult": + model.GameAgeRating.AgeGroupings.Add(AgeGroups.AgeRestrictionGroupings.Adult); + model.GameAgeRating.AgeGroupings.Add(AgeGroups.AgeRestrictionGroupings.Mature); + model.GameAgeRating.AgeGroupings.Add(AgeGroups.AgeRestrictionGroupings.Teen); + model.GameAgeRating.AgeGroupings.Add(AgeGroups.AgeRestrictionGroupings.Child); + model.GameAgeRating.IncludeUnrated = true; + } + List RemoveAgeGroups = new List(); + switch (user.SecurityProfile.AgeRestrictionPolicy.MaximumAgeRestriction) + { + case AgeGroups.AgeRestrictionGroupings.Adult: break; - case "mature": - RemoveAgeGroups.Add("Adult"); + case AgeGroups.AgeRestrictionGroupings.Mature: + RemoveAgeGroups.Add(AgeGroups.AgeRestrictionGroupings.Adult); break; - case "teen": - RemoveAgeGroups.Add("Adult"); - RemoveAgeGroups.Add("Mature"); + case AgeGroups.AgeRestrictionGroupings.Teen: + RemoveAgeGroups.Add(AgeGroups.AgeRestrictionGroupings.Adult); + RemoveAgeGroups.Add(AgeGroups.AgeRestrictionGroupings.Mature); break; - case "child": - RemoveAgeGroups.Add("Adult"); - RemoveAgeGroups.Add("Mature"); - RemoveAgeGroups.Add("Teen"); + case AgeGroups.AgeRestrictionGroupings.Child: + RemoveAgeGroups.Add(AgeGroups.AgeRestrictionGroupings.Adult); + RemoveAgeGroups.Add(AgeGroups.AgeRestrictionGroupings.Mature); + RemoveAgeGroups.Add(AgeGroups.AgeRestrictionGroupings.Teen); break; } - foreach (string RemoveAgeGroup in RemoveAgeGroups) + foreach (AgeGroups.AgeRestrictionGroupings RemoveAgeGroup in RemoveAgeGroups) { if (model.GameAgeRating.AgeGroupings.Contains(RemoveAgeGroup)) { @@ -77,7 +85,47 @@ namespace gaseous_server.Controllers.v1_1 model.GameAgeRating.IncludeUnrated = false; } - return Ok(GetGames(model)); + return Ok(GetGames(model, pageNumber, pageSize)); + } + else + { + return Unauthorized(); + } + } + + [MapToApiVersion("1.1")] + [HttpGet] + [Route("{GameId}/Related")] + [ProducesResponseType(typeof(GameReturnPackage), StatusCodes.Status200OK)] + public async Task GameRelated(long GameId) + { + var user = await _userManager.GetUserAsync(User); + + if (user != null) + { + string IncludeUnrated = ""; + if (user.SecurityProfile.AgeRestrictionPolicy.IncludeUnrated == true) { + IncludeUnrated = " OR view_Games.AgeGroupId IS NULL"; + } + + Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); + string sql = "SELECT view_Games.Id, view_Games.AgeGroupId, Relation_Game_SimilarGames.SimilarGamesId FROM view_Games JOIN Relation_Game_SimilarGames ON view_Games.Id = Relation_Game_SimilarGames.GameId AND Relation_Game_SimilarGames.SimilarGamesId IN (SELECT Id FROM view_Games) WHERE view_Games.Id = @id AND (view_Games.AgeGroupId <= @agegroupid" + IncludeUnrated + ")"; + Dictionary dbDict = new Dictionary(); + dbDict.Add("id", GameId); + dbDict.Add("agegroupid", (int)user.SecurityProfile.AgeRestrictionPolicy.MaximumAgeRestriction); + + List RetVal = new List(); + + DataTable dbResponse = db.ExecuteCMD(sql, dbDict); + + foreach (DataRow dr in dbResponse.Rows) + { + RetVal.Add(Classes.Metadata.Games.GetGame((long)dr["SimilarGamesId"], false, false, false)); + } + + GameReturnPackage gameReturn = new GameReturnPackage(RetVal.Count, RetVal); + + return Ok(gameReturn); } else { @@ -109,7 +157,12 @@ namespace gaseous_server.Controllers.v1_1 public class GameAgeRatingItem { - public List AgeGroupings { get; set; } = new List{ "Child", "Teen", "Mature", "Adult" }; + public List AgeGroupings { get; set; } = new List{ + AgeGroups.AgeRestrictionGroupings.Child, + AgeGroups.AgeRestrictionGroupings.Teen, + AgeGroups.AgeRestrictionGroupings.Mature, + AgeGroups.AgeRestrictionGroupings.Adult + }; public bool IncludeUnrated { get; set; } = true; } @@ -128,7 +181,7 @@ namespace gaseous_server.Controllers.v1_1 } } - public static List GetGames(GameSearchModel model) + public static GameReturnPackage GetGames(GameSearchModel model, int pageNumber = 0, int pageSize = 0) { string whereClause = ""; string havingClause = ""; @@ -191,7 +244,7 @@ namespace gaseous_server.Controllers.v1_1 } } - string unratedClause = "totalRating IS NOT NULL"; + string unratedClause = ""; if (model.GameRating.IncludeUnrated == true) { unratedClause = "totalRating IS NULL"; @@ -199,7 +252,14 @@ namespace gaseous_server.Controllers.v1_1 if (ratingClauseValue.Length > 0) { - havingClauses.Add("((" + ratingClauseValue + ") OR " + unratedClause + ")"); + if (unratedClause.Length > 0) + { + havingClauses.Add("((" + ratingClauseValue + ") OR " + unratedClause + ")"); + } + else + { + havingClauses.Add("(" + ratingClauseValue + ")"); + } } } @@ -292,43 +352,26 @@ namespace gaseous_server.Controllers.v1_1 { if (model.GameAgeRating.AgeGroupings.Count > 0) { - List AgeClassificationsList = new List(); - foreach (string ratingGroup in model.GameAgeRating.AgeGroupings) + tempVal = "(AgeGroupId IN ("; + for (int i = 0; i < model.GameAgeRating.AgeGroupings.Count; i++) { - if (AgeGroups.AgeGroupings.ContainsKey(ratingGroup)) + if (i > 0) { - List ageGroups = AgeGroups.AgeGroupings[ratingGroup]; - foreach (AgeGroups.AgeGroupItem ageGroup in ageGroups) - { - AgeClassificationsList.AddRange(ageGroup.AgeGroupItemValues); - } + tempVal += ", "; } + string themeLabel = "@Rating" + i; + tempVal += themeLabel; + whereParams.Add(themeLabel, model.GameAgeRating.AgeGroupings[i]); } + tempVal += ")"; - if (AgeClassificationsList.Count > 0) + if (model.GameAgeRating.IncludeUnrated == true) { - AgeClassificationsList = new HashSet(AgeClassificationsList).ToList(); - tempVal = "(view_AgeRatings.Rating IN ("; - for (int i = 0; i < AgeClassificationsList.Count; i++) - { - if (i > 0) - { - tempVal += ", "; - } - string themeLabel = "@Rating" + i; - tempVal += themeLabel; - whereParams.Add(themeLabel, AgeClassificationsList[i]); - } - tempVal += ")"; - - if (model.GameAgeRating.IncludeUnrated == true) - { - tempVal += " OR view_AgeRatings.Rating IS NULL"; - } - tempVal += ")"; - - whereClauses.Add(tempVal); + tempVal += " OR AgeGroupId IS NULL"; } + tempVal += ")"; + + whereClauses.Add(tempVal); } } @@ -396,18 +439,66 @@ namespace gaseous_server.Controllers.v1_1 string orderByClause = "ORDER BY `" + orderByField + "` " + orderByOrder; Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); - string sql = "SELECT DISTINCT Games_Roms.GameId AS ROMGameId, Game.*, case when Game.`Name` like 'The %' then CONCAT(trim(substr(Game.`Name` from 4)), ', The') else Game.`Name` end as NameThe FROM Games_Roms LEFT JOIN Game ON Game.Id = Games_Roms.GameId LEFT JOIN Relation_Game_Genres ON Game.Id = Relation_Game_Genres.GameId LEFT JOIN Relation_Game_GameModes ON Game.Id = Relation_Game_GameModes.GameId LEFT JOIN Relation_Game_PlayerPerspectives ON Game.Id = Relation_Game_PlayerPerspectives.GameId LEFT JOIN Relation_Game_Themes ON Game.Id = Relation_Game_Themes.GameId LEFT JOIN (SELECT Relation_Game_AgeRatings.GameId, AgeRating.* FROM Relation_Game_AgeRatings JOIN AgeRating ON Relation_Game_AgeRatings.AgeRatingsId = AgeRating.Id) view_AgeRatings ON Game.Id = view_AgeRatings.GameId " + whereClause + " " + havingClause + " " + orderByClause; + string sql = "SELECT DISTINCT view_Games.* FROM view_Games LEFT JOIN Games_Roms ON view_Games.Id = Games_Roms.GameId LEFT JOIN Relation_Game_Genres ON view_Games.Id = Relation_Game_Genres.GameId LEFT JOIN Relation_Game_GameModes ON view_Games.Id = Relation_Game_GameModes.GameId LEFT JOIN Relation_Game_PlayerPerspectives ON view_Games.Id = Relation_Game_PlayerPerspectives.GameId LEFT JOIN Relation_Game_Themes ON view_Games.Id = Relation_Game_Themes.GameId " + whereClause + " " + havingClause + " " + orderByClause; List RetVal = new List(); DataTable dbResponse = db.ExecuteCMD(sql, whereParams); - foreach (DataRow dr in dbResponse.Rows) + + // get count + int RecordCount = dbResponse.Rows.Count; + + // compile data for return + int pageOffset = pageSize * (pageNumber - 1); + for (int i = 0; i < dbResponse.Rows.Count; i++) { - //RetVal.Add(Classes.Metadata.Games.GetGame((long)dr["ROMGameId"], false, false)); - RetVal.Add(Classes.Metadata.Games.GetGame(dr)); + DataRow dr = dbResponse.Rows[i]; + + bool includeGame = false; + + if (pageSize == 0) + { + // page size is full size include all + includeGame = true; + } + else if (i >= pageOffset && i < (pageOffset + pageSize)) + { + includeGame = true; + } + + if (includeGame == true) + { + RetVal.Add(Classes.Metadata.Games.GetGame(dr)); + } } - return RetVal; + GameReturnPackage gameReturn = new GameReturnPackage(RecordCount, RetVal); + + return gameReturn; + } + + public class GameReturnPackage + { + public GameReturnPackage() + { + + } + + public GameReturnPackage(int Count, List Games) + { + this.Count = Count; + + List minimalGames = new List(); + foreach (Game game in Games) + { + minimalGames.Add(new Classes.Metadata.Games.MinimalGameItem(game)); + } + + this.Games = minimalGames; + } + + public int Count { get; set; } + public List Games { get; set; } = new List(); } } } \ No newline at end of file diff --git a/gaseous-server/ProcessQueue.cs b/gaseous-server/ProcessQueue.cs index 34b04a5..e316a89 100644 --- a/gaseous-server/ProcessQueue.cs +++ b/gaseous-server/ProcessQueue.cs @@ -134,7 +134,7 @@ namespace gaseous_server case QueueItemType.MetadataRefresh: Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Metadata Refresher"); - Classes.MetadataManagement.RefreshMetadata(); + Classes.MetadataManagement.RefreshMetadata(_ForceExecute); _SaveLastRunTime = true; diff --git a/gaseous-server/Program.cs b/gaseous-server/Program.cs index e7a2c7c..5a99dc6 100644 --- a/gaseous-server/Program.cs +++ b/gaseous-server/Program.cs @@ -13,6 +13,8 @@ using Microsoft.OpenApi.Models; using Authentication; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity.UI.Services; +using IGDB.Models; +using gaseous_server.Classes.Metadata; Logging.WriteToDiskOnly = true; Logging.Log(Logging.LogType.Information, "Startup", "Starting Gaseous Server " + Assembly.GetExecutingAssembly().GetName().Version); @@ -39,6 +41,9 @@ db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.Conn // set up db db.InitDB(); +// populate db with static data for lookups +AgeRatings.PopulateAgeMap(); + // load app settings Config.InitSettings(); // write updated settings back to the config file diff --git a/gaseous-server/Support/Database/MySQL/gaseous-1007.sql b/gaseous-server/Support/Database/MySQL/gaseous-1007.sql new file mode 100644 index 0000000..23deede --- /dev/null +++ b/gaseous-server/Support/Database/MySQL/gaseous-1007.sql @@ -0,0 +1,66 @@ +CREATE TABLE `ClassificationMap` ( + `AgeGroupId` INT NOT NULL, + `ClassificationBoardId` INT NOT NULL, + `RatingId` INT NOT NULL, + PRIMARY KEY (`AgeGroupId`, `ClassificationBoardId`, `RatingId`)); + +CREATE OR REPLACE VIEW `view_GamesWithRoms` AS + SELECT DISTINCT + Games_Roms.GameId AS ROMGameId, + Game.*, + CASE + WHEN + Game.`Name` LIKE 'The %' + THEN + CONCAT(TRIM(SUBSTR(Game.`Name` FROM 4)), + ', The') + ELSE Game.`Name` + END AS NameThe + FROM + Games_Roms + LEFT JOIN + Game ON Game.Id = Games_Roms.GameId; + +CREATE OR REPLACE VIEW `view_Games` AS +SELECT + * +FROM + (SELECT DISTINCT + row_number() over (partition by Id order by AgeGroupId desc) as seqnum, view_GamesWithRoms.*, + (SELECT + AgeGroupId + FROM + ClassificationMap + WHERE + RatingId = AgeRating.Rating + ORDER BY AgeGroupId DESC) AgeGroupId + FROM + view_GamesWithRoms + LEFT JOIN Relation_Game_AgeRatings ON view_GamesWithRoms.Id = Relation_Game_AgeRatings.GameId + LEFT JOIN AgeRating ON Relation_Game_AgeRatings.AgeRatingsId = AgeRating.Id + ) g +WHERE g.seqnum = 1; + +CREATE TABLE `ReleaseDate` ( + `Id` BIGINT NOT NULL, + `Category` INT(11) NULL DEFAULT NULL, + `Checksum` VARCHAR(45) NULL DEFAULT NULL, + `CreatedAt` DATETIME NULL DEFAULT NULL, + `Date` DATETIME NULL, + `Game` BIGINT NULL, + `Human` VARCHAR(100) NULL, + `m` INT NULL, + `Platform` BIGINT NULL, + `Region` INT NULL, + `Status` BIGINT NULL, + `UpdatedAt` DATETIME NULL DEFAULT NULL, + `y` INT NULL, + `dateAdded` DATETIME NULL DEFAULT NULL, + `lastUpdated` DATETIME NULL DEFAULT NULL, + PRIMARY KEY (`Id`)); + +CREATE TABLE `User_Settings` ( + `Id` VARCHAR(128) NOT NULL, + `Setting` VARCHAR(45) NOT NULL, + `Value` LONGTEXT NULL DEFAULT NULL, + PRIMARY KEY (`Id`, `Setting`)); \ No newline at end of file diff --git a/gaseous-server/gaseous-server.csproj b/gaseous-server/gaseous-server.csproj index b274f1c..a07d27e 100644 --- a/gaseous-server/gaseous-server.csproj +++ b/gaseous-server/gaseous-server.csproj @@ -46,6 +46,8 @@ + + @@ -174,5 +176,7 @@ + + diff --git a/gaseous-server/wwwroot/.DS_Store b/gaseous-server/wwwroot/.DS_Store index 44dbb97..c037546 100644 Binary files a/gaseous-server/wwwroot/.DS_Store and b/gaseous-server/wwwroot/.DS_Store differ diff --git a/gaseous-server/wwwroot/index.html b/gaseous-server/wwwroot/index.html index a9e0189..b1764f6 100644 --- a/gaseous-server/wwwroot/index.html +++ b/gaseous-server/wwwroot/index.html @@ -5,7 +5,7 @@ - + diff --git a/gaseous-server/wwwroot/pages/dialogs/userprofile.html b/gaseous-server/wwwroot/pages/dialogs/userprofile.html index 2bc44f2..e238768 100644 --- a/gaseous-server/wwwroot/pages/dialogs/userprofile.html +++ b/gaseous-server/wwwroot/pages/dialogs/userprofile.html @@ -1,8 +1,79 @@
-
Account
+
Preferences
+
Account
+