diff --git a/.drone.yml b/.drone.yml index 1e97fec..ab57284 100644 --- a/.drone.yml +++ b/.drone.yml @@ -85,7 +85,7 @@ steps: - name: docker-image-console-app image: plugins/docker settings: - dockerfile: MCTG/Dockerfile + dockerfile: MCTG/ConsoleApp/Dockerfile context: MCTG/ registry: hub.codefirst.iut.uca.fr repo: hub.codefirst.iut.uca.fr/alexandre.agostinho/console-mctg diff --git a/MCTG/AppException/AppException.csproj b/MCTG/AppException/AppException.csproj new file mode 100644 index 0000000..cfadb03 --- /dev/null +++ b/MCTG/AppException/AppException.csproj @@ -0,0 +1,9 @@ + + + + net7.0 + enable + enable + + + diff --git a/MCTG/AppException/RecipeExceptions/NoRecipeSelectedException.cs b/MCTG/AppException/RecipeExceptions/NoRecipeSelectedException.cs new file mode 100644 index 0000000..7f7f294 --- /dev/null +++ b/MCTG/AppException/RecipeExceptions/NoRecipeSelectedException.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace AppException +{ + public class NoRecipeSelectedException : RecipeException + { + public NoRecipeSelectedException() : base("No recipe is currently selected to perform this action.") { } + } +} diff --git a/MCTG/AppException/RecipeExceptions/RecipeException.cs b/MCTG/AppException/RecipeExceptions/RecipeException.cs new file mode 100644 index 0000000..aba9d0b --- /dev/null +++ b/MCTG/AppException/RecipeExceptions/RecipeException.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection.Emit; +using System.Text; +using System.Threading.Tasks; + +namespace AppException +{ + public class RecipeException : Exception + { + public RecipeException() : base("Something went wrong with a recipe or a collection of recipe.") { } + public RecipeException(string message) : base(message) { } + } +} diff --git a/MCTG/AppException/RecipeExceptions/RecipeNotFoundException.cs b/MCTG/AppException/RecipeExceptions/RecipeNotFoundException.cs new file mode 100644 index 0000000..a6c3ee2 --- /dev/null +++ b/MCTG/AppException/RecipeExceptions/RecipeNotFoundException.cs @@ -0,0 +1,9 @@ + +namespace AppException +{ + public class RecipeNotFoundException : RecipeException + { + public RecipeNotFoundException() : base("Recipe not found.") { } + public RecipeNotFoundException(int id) : base($"Recipe id: '{id}'not found.") { } + } +} diff --git a/MCTG/AppException/UserExceptions/BadMailFormatException.cs b/MCTG/AppException/UserExceptions/BadMailFormatException.cs new file mode 100644 index 0000000..c55f0c9 --- /dev/null +++ b/MCTG/AppException/UserExceptions/BadMailFormatException.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace AppException +{ + public class BadMailFormatException : UserException + { + public BadMailFormatException() : base("Invalid mail format.") { } + public BadMailFormatException(string mail) : base($"'{mail}' is an invalid format.") { } + } +} diff --git a/MCTG/AppException/UserExceptions/NoUserConnectedException.cs b/MCTG/AppException/UserExceptions/NoUserConnectedException.cs new file mode 100644 index 0000000..e0659fa --- /dev/null +++ b/MCTG/AppException/UserExceptions/NoUserConnectedException.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace AppException +{ + public class NoUserConnectedException : UserException + { + public NoUserConnectedException() : base("No user is currently connected.") { } + } +} diff --git a/MCTG/AppException/UserExceptions/UserAlreadyConnectedException.cs b/MCTG/AppException/UserExceptions/UserAlreadyConnectedException.cs new file mode 100644 index 0000000..15b0ad6 --- /dev/null +++ b/MCTG/AppException/UserExceptions/UserAlreadyConnectedException.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace AppException +{ + public class UserAlreadyConnectedException : UserException + { + public UserAlreadyConnectedException() : base("An user is already connected.") { } + } +} diff --git a/MCTG/AppException/UserExceptions/UserException.cs b/MCTG/AppException/UserExceptions/UserException.cs new file mode 100644 index 0000000..957e97a --- /dev/null +++ b/MCTG/AppException/UserExceptions/UserException.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace AppException +{ + public class UserException : Exception + { + public UserException() : base("Somthing went wrong with an User.") { } + public UserException(string message) : base(message) { } + } +} diff --git a/MCTG/AppException/UserExceptions/UserNotFoundException.cs b/MCTG/AppException/UserExceptions/UserNotFoundException.cs new file mode 100644 index 0000000..c858fac --- /dev/null +++ b/MCTG/AppException/UserExceptions/UserNotFoundException.cs @@ -0,0 +1,8 @@ +namespace AppException +{ + public class UserNotFoundException : UserException + { + public UserNotFoundException() : base("User not found.") { } + public UserNotFoundException(string userMail) : base($"User with mail: '{userMail}' not found.") { } + } +} diff --git a/MCTG/CI-CD.slnf b/MCTG/CI-CD.slnf index 5728156..ca21de6 100644 --- a/MCTG/CI-CD.slnf +++ b/MCTG/CI-CD.slnf @@ -3,7 +3,11 @@ "path": "SAE-2.01.sln", "projects": [ "ConsoleApp\\ConsoleApp.csproj", + "AppException\\AppException.csproj", + "Managers\\Managers.csproj", "Model\\Model.csproj", + "Persistance\\DataPersistence\\DataPersistence.csproj", + "Persistance\\FakePersistance\\FakePersistance.csproj", "Tests\\Model_UnitTests\\Model_UnitTests.csproj" ] } diff --git a/MCTG/ConsoleApp/ConsoleApp.csproj b/MCTG/ConsoleApp/ConsoleApp.csproj index dbc5e22..4a92e3e 100644 --- a/MCTG/ConsoleApp/ConsoleApp.csproj +++ b/MCTG/ConsoleApp/ConsoleApp.csproj @@ -7,9 +7,12 @@ enable - - - + + + + + + diff --git a/MCTG/Dockerfile b/MCTG/ConsoleApp/Dockerfile similarity index 100% rename from MCTG/Dockerfile rename to MCTG/ConsoleApp/Dockerfile diff --git a/MCTG/ConsoleApp/Menu/AddRecipeMenu.cs b/MCTG/ConsoleApp/Menu/AddRecipeMenu.cs index 5e66b49..6152dca 100644 --- a/MCTG/ConsoleApp/Menu/AddRecipeMenu.cs +++ b/MCTG/ConsoleApp/Menu/AddRecipeMenu.cs @@ -1,11 +1,5 @@ using ConsoleApp.Menu.Core; using Model; -using Model.Managers; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace ConsoleApp.Menu { @@ -40,14 +34,15 @@ namespace ConsoleApp.Menu Recipe recipe = new Recipe( title: title, type: RecipeType.Unspecified, + priority: Priority.Fast, id: null, - authorMail: masterMgr.CurrentConnectedUser?.Mail, + authorMail: masterMgr.User.CurrentConnected?.Mail, picture: null) { PreparationSteps = steps }; - masterMgr.AddRecipe(recipe); + masterMgr.Recipe.AddRecipeToData(recipe); return null; } } diff --git a/MCTG/ConsoleApp/Menu/AddUserMenu.cs b/MCTG/ConsoleApp/Menu/AddUserMenu.cs index 57ca2aa..49d6a57 100644 --- a/MCTG/ConsoleApp/Menu/AddUserMenu.cs +++ b/MCTG/ConsoleApp/Menu/AddUserMenu.cs @@ -1,11 +1,5 @@ using ConsoleApp.Menu.Core; using Model; -using Model.Managers; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace ConsoleApp.Menu { @@ -30,14 +24,9 @@ namespace ConsoleApp.Menu string surname = _selectList[2].Item.Input; string passwd = _selectList[3].Item.Input; - User user = new User( - name: name, - surname: surname, - mail: mail, - password: passwd - ); + User user = masterMgr.User.CreateUser(mail, passwd, name, surname); - masterMgr.Register(user); + masterMgr.User.AddUserToData(user); return null; } } diff --git a/MCTG/ConsoleApp/Menu/ConnectionMenu.cs b/MCTG/ConsoleApp/Menu/ConnectionMenu.cs index 30759be..432822a 100644 --- a/MCTG/ConsoleApp/Menu/ConnectionMenu.cs +++ b/MCTG/ConsoleApp/Menu/ConnectionMenu.cs @@ -1,10 +1,5 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using ConsoleApp.Menu.Core; -using Model.Managers; +using ConsoleApp.Menu.Core; +using Model; namespace ConsoleApp.Menu { @@ -38,7 +33,7 @@ namespace ConsoleApp.Menu string mail = _selectList[0].Item.Input; string password = _selectList[1].Item.Input; - if (!_masterMgr.Login(mail, password)) + if (!_masterMgr.User.LogIn(mail, password)) { _wrongInput = true; return this; diff --git a/MCTG/ConsoleApp/Menu/ExportRecipeMenu.cs b/MCTG/ConsoleApp/Menu/ExportRecipeMenu.cs index 1518a1c..cba11d5 100644 --- a/MCTG/ConsoleApp/Menu/ExportRecipeMenu.cs +++ b/MCTG/ConsoleApp/Menu/ExportRecipeMenu.cs @@ -1,12 +1,5 @@ using ConsoleApp.Menu.Core; -using Model.Managers; using Model; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using DataPersistence; namespace ConsoleApp.Menu { @@ -36,7 +29,7 @@ namespace ConsoleApp.Menu try { - _masterMgr.DataMgr.Export(recipe, path); + _masterMgr.Data.Export(recipe, path); } catch (ArgumentNullException e) { diff --git a/MCTG/ConsoleApp/Menu/ImportRecipeMenu.cs b/MCTG/ConsoleApp/Menu/ImportRecipeMenu.cs index d1f402c..2dac0f5 100644 --- a/MCTG/ConsoleApp/Menu/ImportRecipeMenu.cs +++ b/MCTG/ConsoleApp/Menu/ImportRecipeMenu.cs @@ -1,11 +1,5 @@ using ConsoleApp.Menu.Core; using Model; -using Model.Managers; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace ConsoleApp.Menu { @@ -25,7 +19,7 @@ namespace ConsoleApp.Menu string path = _selectList[0].Item.Input; try { - masterMgr.DataMgr.Import(path); + masterMgr.Data.Import(path); } catch(ArgumentNullException e) { diff --git a/MCTG/ConsoleApp/Menu/LogoutButton.cs b/MCTG/ConsoleApp/Menu/LogoutButton.cs index aa10600..563332e 100644 --- a/MCTG/ConsoleApp/Menu/LogoutButton.cs +++ b/MCTG/ConsoleApp/Menu/LogoutButton.cs @@ -1,10 +1,5 @@ using ConsoleApp.Menu.Core; -using Model.Managers; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using Model; namespace ConsoleApp.Menu { @@ -20,7 +15,7 @@ namespace ConsoleApp.Menu public override IMenu? Return() { - _masterMgr.Logout(); + _masterMgr.User.LogOut(); return base.Return(); } } diff --git a/MCTG/ConsoleApp/Menu/MainMenu.cs b/MCTG/ConsoleApp/Menu/MainMenu.cs index 856748f..94adfe6 100644 --- a/MCTG/ConsoleApp/Menu/MainMenu.cs +++ b/MCTG/ConsoleApp/Menu/MainMenu.cs @@ -1,12 +1,5 @@ using Model; -using DataPersistence; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using ConsoleApp.Menu.Core; -using Model.Managers; namespace ConsoleApp.Menu { @@ -47,7 +40,7 @@ namespace ConsoleApp.Menu { List> selectors = base.SearchInSelection(); - if (_masterMgr.CurrentConnectedUser == null) + if (_masterMgr.User.CurrentConnected == null) return selectors.Except(selectors.Where(s => s.Line == "User profile")) .Except(selectors.Where(s => s.Line == "Logout")) .Except(selectors.Where(s => s.Line == "Add recipe")).ToList(); diff --git a/MCTG/ConsoleApp/Menu/ProfileMenu.cs b/MCTG/ConsoleApp/Menu/ProfileMenu.cs index da315b3..504f899 100644 --- a/MCTG/ConsoleApp/Menu/ProfileMenu.cs +++ b/MCTG/ConsoleApp/Menu/ProfileMenu.cs @@ -1,11 +1,5 @@ using ConsoleApp.Menu.Core; -using Model.Managers; using Model; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace ConsoleApp.Menu { diff --git a/MCTG/ConsoleApp/Menu/SearchUserRecipes.cs b/MCTG/ConsoleApp/Menu/SearchUserRecipes.cs index abf9613..3145997 100644 --- a/MCTG/ConsoleApp/Menu/SearchUserRecipes.cs +++ b/MCTG/ConsoleApp/Menu/SearchUserRecipes.cs @@ -1,10 +1,4 @@ using Model; -using Model.Managers; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace ConsoleApp.Menu { @@ -16,7 +10,10 @@ namespace ConsoleApp.Menu public override void Update() { - _recipeCollectionOnSearch = _masterMgr.GetCurrentUserRecipes(); + if (_masterMgr.User.CurrentConnected is null) + throw new ArgumentNullException(); + + _recipeCollectionOnSearch = _masterMgr.Recipe.GetRecipeByAuthor(_masterMgr.User.CurrentConnected.Mail); _allSelectors = ConvertRecipeCollectionInSelectors(); _selectList = SearchInSelection(); diff --git a/MCTG/ConsoleApp/Menu/SearcherRecipe.cs b/MCTG/ConsoleApp/Menu/SearcherRecipe.cs index 7f5065a..92e25b9 100644 --- a/MCTG/ConsoleApp/Menu/SearcherRecipe.cs +++ b/MCTG/ConsoleApp/Menu/SearcherRecipe.cs @@ -1,12 +1,5 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Security.Cryptography.X509Certificates; -using System.Text; -using System.Threading.Tasks; -using ConsoleApp.Menu.Core; +using ConsoleApp.Menu.Core; using Model; -using Model.Managers; namespace ConsoleApp.Menu { @@ -36,7 +29,7 @@ namespace ConsoleApp.Menu public override void Update() { - _recipeCollectionOnSearch = _masterMgr.DataMgr.GetRecipes("all recipes"); + _recipeCollectionOnSearch = _masterMgr.Recipe.GetAllRecipes(); _allSelectors = ConvertRecipeCollectionInSelectors(); base.Update(); } @@ -46,7 +39,7 @@ namespace ConsoleApp.Menu if (CurrentSelected == null) return this; - return new PlainText(CurrentSelected.ToString()); + return new ShowRecipeInfos(CurrentSelected); } #endregion } diff --git a/MCTG/ConsoleApp/Menu/ShowRecipeInfos.cs b/MCTG/ConsoleApp/Menu/ShowRecipeInfos.cs new file mode 100644 index 0000000..2ac0fb9 --- /dev/null +++ b/MCTG/ConsoleApp/Menu/ShowRecipeInfos.cs @@ -0,0 +1,37 @@ +using ConsoleApp.Menu.Core; +using Model; +using System.Text; + +namespace ConsoleApp.Menu +{ + internal class ShowRecipeInfos : PlainText + { + public Recipe Recipe { get; private set; } + + public ShowRecipeInfos(Recipe recipe) + : base("") + { + Recipe = recipe; + } + + public override void Display() + { + StringBuilder sb = new StringBuilder($"[Recipe n°{Recipe.Id}] - {Recipe.Title}\n"); + foreach (PreparationStep ps in Recipe.PreparationSteps) + { + sb.AppendFormat("\t* {0}\n", ps.ToString()); + } + sb.AppendLine(); + sb.AppendLine(Recipe.ConcatIngredients()); + sb.AppendLine(); + foreach (Review review in Recipe.Reviews) + { + sb.AppendLine(review.ToString()); + } + sb.AppendLine(); + sb.AppendLine($"Posted by: {Recipe.AuthorMail?.ToString()}"); + + Console.WriteLine(sb); + } + } +} diff --git a/MCTG/ConsoleApp/Menu/ShowUserInfos.cs b/MCTG/ConsoleApp/Menu/ShowUserInfos.cs index 989ff5d..6715cde 100644 --- a/MCTG/ConsoleApp/Menu/ShowUserInfos.cs +++ b/MCTG/ConsoleApp/Menu/ShowUserInfos.cs @@ -1,11 +1,5 @@ using ConsoleApp.Menu.Core; using Model; -using Model.Managers; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace ConsoleApp.Menu { @@ -22,10 +16,10 @@ namespace ConsoleApp.Menu public override void Display() { Console.WriteLine( - $"\nUser: {_masterMgr.CurrentConnectedUser}\n\n" - + $"\tMail: {_masterMgr.CurrentConnectedUser?.Mail}\n" - + $"\tName: {_masterMgr.CurrentConnectedUser?.Name}\n" - + $"\tSurname: {_masterMgr.CurrentConnectedUser?.Surname}\n"); + $"\nUser: {_masterMgr.User.CurrentConnected}\n\n" + + $"\tMail: {_masterMgr.User.CurrentConnected?.Mail}\n" + + $"\tName: {_masterMgr.User.CurrentConnected?.Name}\n" + + $"\tSurname: {_masterMgr.User.CurrentConnected?.Surname}\n"); } } } diff --git a/MCTG/ConsoleApp/MenuManager.cs b/MCTG/ConsoleApp/MenuManager.cs index e215e8c..98517cb 100644 --- a/MCTG/ConsoleApp/MenuManager.cs +++ b/MCTG/ConsoleApp/MenuManager.cs @@ -1,13 +1,7 @@ using ConsoleApp.Menu; using Model; using DataPersistence; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using ConsoleApp.Menu.Core; -using Model.Managers; namespace ConsoleApp { @@ -37,7 +31,6 @@ namespace ConsoleApp public MenuManager(MasterManager masterManager, IMenu firstMenu) { MasterMgr = masterManager; - MenuCallStack = new Stack(); MenuCallStack.Push(firstMenu); } diff --git a/MCTG/ConsoleApp/Program.cs b/MCTG/ConsoleApp/Program.cs index ff09c71..3778fc2 100644 --- a/MCTG/ConsoleApp/Program.cs +++ b/MCTG/ConsoleApp/Program.cs @@ -1,31 +1,52 @@ using ConsoleApp; using Model; using DataPersistence; -using Model.Managers; +using FakePersistance; +using Managers; -Console.WriteLine("Hello, World!\n\n"); -string path = ""; // - path to the save file -string strategy = "xml"; // - strategy is 'xml' or 'json' (/!\ this is case sensitive) +namespace ConsoleApp; -MasterManager masterMgr; -IDataManager dataManager = (strategy == "xml") ? - new DataContractXML(path) - : new DataContractJSON(path); - -if (!File.Exists(Path.Combine(path, $"data.{strategy}"))) -{ - masterMgr = new MasterManager(new Stubs()); - masterMgr.DataMgr.Serializer = dataManager; -} -else +public static class Program { - masterMgr = new MasterManager(dataManager); -} + public static void Main(string[] args) + { + Console.WriteLine("Hello, World!\n\n"); + + string path = ""; // - path to the save file + string strategy = "xml"; // - strategy is 'xml' or 'json' (/!\ this is case sensitive) + // Initialize the data serializer + IDataSerializer dataSerializer = (strategy == "xml") ? + new DataContractXML(path) + : new DataContractJSON(path); -MenuManager menuMgr = new MenuManager(masterMgr); -menuMgr.Loop(); + // Initialize the data manager + IDataManager dataManager = (!File.Exists(Path.Combine(path, $"data.{strategy}"))) ? + new DataDefaultManager(new Stubs()) + : new DataDefaultManager(dataSerializer); -// Save data. -Console.Write("[ --SAVE-- ]:\t"); masterMgr.DataMgr.Save(); Console.WriteLine("Done."); + // Initialize the other managers + IRecipeManager recipeManager = new RecipeDefaultManager(dataManager); + IPasswordManager passwordManager = new PasswordSHA256Manager(); + IUserManager userManager = new UserDefaultManager(dataManager, passwordManager); + + // Initialize the master manager + MasterManager Master = new MasterManager(dataManager, recipeManager, userManager); + Master.Setup(); + + MenuManager menuMgr = new MenuManager(Master); + menuMgr.Loop(); + + // Change the data serializer if the one in place is 'Stubs' + if (Master.Data.Serializer.GetType() == typeof(Stubs)) + { + var data = Master.Data.Data; + dataManager = new DataDefaultManager(dataSerializer, data); + Master = new MasterManager(dataManager, recipeManager, userManager); + } + + // Save data. + Console.Write("[ --SAVE-- ]:\t"); Master.Data.SaveData(); Console.WriteLine("Done."); + } +} diff --git a/MCTG/Managers/DataDefaultManager.cs b/MCTG/Managers/DataDefaultManager.cs new file mode 100644 index 0000000..5326602 --- /dev/null +++ b/MCTG/Managers/DataDefaultManager.cs @@ -0,0 +1,56 @@ +using Model; + +namespace Managers +{ + /// + /// Define the manager of the data. This is where all the data are put, and where we call the loading and the saving of them. + /// + public class DataDefaultManager : IDataManager + { + #region Attributes & Properties + public IDataSerializer Serializer { get; set; } + + public Dictionary> Data { get; private set; } + #endregion + + #region Constructors + /// + /// Constructor of the DataDefaultManager class. Take a IDataManager that will provide methods for the serialisation of the data. + /// + /// The data manager that know how to serialize a file. + /// The data set of the application. + public DataDefaultManager(IDataSerializer dataManager, + Dictionary>? data = null) + { + Serializer = dataManager; + + if (data is null) + Data = new Dictionary>(); + else + Data = data; + } + #endregion + + #region Methods + public void LoadData() + => Data = Serializer.Load(); + + public void SaveData() + => Serializer.Save(Data); + + public void Import(string pathOfTheFile) + where T : class + { + KeyValuePair import = Serializer.Import(pathOfTheFile); + Data[import.Key].Add(import.Value); + } + + public void Export(T obj, string pathToExport) + where T : class + => Serializer.Export(obj, pathToExport); + + public ICollection GetFromData() where T : class + => new List(Data[typeof(T).Name].Cast()); + #endregion + } +} diff --git a/MCTG/ConsoleApp/ConsoleApp - Backup.csproj b/MCTG/Managers/Managers.csproj similarity index 63% rename from MCTG/ConsoleApp/ConsoleApp - Backup.csproj rename to MCTG/Managers/Managers.csproj index 2c5123f..bc50357 100644 --- a/MCTG/ConsoleApp/ConsoleApp - Backup.csproj +++ b/MCTG/Managers/Managers.csproj @@ -1,16 +1,14 @@ - - - - Exe - net7.0 - enable - enable - Debug;Release;CI - - + + + + net7.0 + enable + enable + + - + - - - + + + diff --git a/MCTG/Model/User/PasswordSHA256.cs b/MCTG/Managers/PasswordSHA256Manager.cs similarity index 94% rename from MCTG/Model/User/PasswordSHA256.cs rename to MCTG/Managers/PasswordSHA256Manager.cs index e418de1..ee9ec27 100644 --- a/MCTG/Model/User/PasswordSHA256.cs +++ b/MCTG/Managers/PasswordSHA256Manager.cs @@ -9,7 +9,7 @@ using System.Threading.Tasks; namespace Model { [DataContract(Name = "passmgr")] - public class PasswordSHA256 : IPasswordManager + public class PasswordSHA256Manager : IPasswordManager { public string HashPassword(string password) { diff --git a/MCTG/Managers/RecipeDefaultManager.cs b/MCTG/Managers/RecipeDefaultManager.cs new file mode 100644 index 0000000..feb69a7 --- /dev/null +++ b/MCTG/Managers/RecipeDefaultManager.cs @@ -0,0 +1,112 @@ +using AppException; +using Model; +using System.Diagnostics; +using System.Reflection; +using System.Runtime.CompilerServices; + +namespace Managers +{ + public class RecipeDefaultManager : IRecipeManager + { + private IDataManager _dataManager; + + public Recipe? CurrentSelected { get; set; } = null; + + + public RecipeDefaultManager(IDataManager dataManager) + { + _dataManager = dataManager; + } + + + public bool AddRecipeToData(Recipe recipe) + { + var recipeList = _dataManager.Data[nameof(Recipe)]; + + if (recipeList.Exists(r => r.Equals(recipe))) + return false; + + _dataManager.Data[nameof(Recipe)].Add(recipe); + return true; + } + + public RecipeCollection GetAllRecipes() + { + return new RecipeCollection( + "All recipes", + _dataManager.GetFromData().ToArray()); + } + + public RecipeCollection GetRecipeByAuthor(string authorMail) + { + User? author = _dataManager.GetFromData() + .ToList() + .Find(u => u.Mail == authorMail); + if (author is null) + throw new UserNotFoundException(authorMail); + + IEnumerable recipes = from Recipe r in _dataManager.GetFromData() + where r.AuthorMail == author.Mail + select r; + + return new RecipeCollection( + $"{author.Name} {author.Surname}'s recipes", recipes.ToArray()); + } + + public RecipeCollection SearchRecipeByTitle(string title) + { + IEnumerable recipes = from Recipe recipe in _dataManager.GetFromData() + where recipe.Title.Contains(title) + select recipe; + return new RecipeCollection( + $"Search for '{title}'", recipes.ToArray()); + } + + public Recipe GetRecipeFromId(int id) + { + Recipe? recipe = _dataManager.GetFromData() + .ToList() + .Find(r => r.Id == id); + if (recipe is null) + throw new RecipeNotFoundException(); + + return recipe; + } + + public RecipeCollection GetRecipesByPriorityOrder(IEnumerable priorities) + { + List recipes = new List(); + IEnumerable recipesWithCurrentPriority; + foreach (Priority priority in priorities) + { + recipesWithCurrentPriority = from Recipe recipe in _dataManager.GetFromData() + where recipe.Priority == priority + select recipe; + recipes.AddRange(recipesWithCurrentPriority); + } + + return new RecipeCollection( + $"Suggestions", recipes.ToArray()); + } + + public bool ModifyCurrentSelected(Recipe newRecipe) + { + if (CurrentSelected is null) + throw new NoRecipeSelectedException(); + + try + { + var index = _dataManager.GetFromData().ToList() + .FindIndex(u => u.Equals(CurrentSelected)); + _dataManager.Data[nameof(Recipe)][index] = newRecipe; + } + catch (ArgumentNullException e) + { + Debug.WriteLine("Recipe to modify not found."); + return false; + } + + return true; + } + } +} diff --git a/MCTG/Managers/UserDefaultManager.cs b/MCTG/Managers/UserDefaultManager.cs new file mode 100644 index 0000000..d4731a8 --- /dev/null +++ b/MCTG/Managers/UserDefaultManager.cs @@ -0,0 +1,149 @@ +using AppException; +using Model; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Security.Cryptography; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +namespace Managers +{ + public class UserDefaultManager : IUserManager, INotifyPropertyChanging + { + private IDataManager _dataManager; + private IPasswordManager _passwordManager; + private User? _currentConnected = null; + + + public UserDefaultManager(IDataManager dataManager, IPasswordManager passwordManager) + { + _dataManager = dataManager; + _passwordManager = passwordManager; + } + + + public User? CurrentConnected + { + get => _currentConnected; + private set + { + _currentConnected = value; + OnPropertyChanged(); + } + } + + public IPasswordManager PasswordManager => _passwordManager; + + + public event PropertyChangingEventHandler? PropertyChanging; + + public void OnPropertyChanged([CallerMemberName] string pname = "") + => PropertyChanging?.Invoke(this, new PropertyChangingEventArgs(pname)); + + + public bool AddUserToData(User user) + { + var userList = _dataManager.Data[nameof(User)]; + + if (userList.Exists(r => r.Equals(user))) + return false; + + _dataManager.Data[nameof(User)].Add(user); + return true; + } + + public User CreateUser(string mail, string password, + string? name = null, string? surname = null, string? profilePict = null) + { + if (name is null) name = $"user{GetRandomInt()}"; + if (surname is null) surname = ""; + if (profilePict is null) profilePict = "default_user_picture.png"; + + const string mailRegex = @"^([\w\.-]+)@([\w\.-]+\.\w+)$"; + if (!Regex.Match(mail, mailRegex).Success) + throw new BadMailFormatException(); + + string hashedPassword = PasswordManager.HashPassword(password); + return new User(name, surname, mail, hashedPassword, profilePict); + } + + public ICollection GetAllUsers() + { + return _dataManager.GetFromData(); + } + + public User GetUserFromMail(string mail) + { + User? user = _dataManager.GetFromData().ToList() + .Find(u => u.Mail == mail); + if (user is null) + throw new UserNotFoundException(); + + return user; + } + + public bool LogIn(string mail, string password) + { + if (CurrentConnected is not null) + throw new UserAlreadyConnectedException(); + +#if DEBUG + if (mail == "admin") + { + CurrentConnected = _dataManager.GetFromData() + .FirstOrDefault(u => u.Mail == "admin@mctg.fr"); + return true; + } +#endif + + User? user = _dataManager.GetFromData().ToList() + .Find(u => u.Mail == mail); + if (user is null) + return false; + + if (!_passwordManager.VerifyPassword(user.Password, password)) + return false; + + CurrentConnected = user; + return true; + } + + public void LogOut() + { + if (CurrentConnected is null) + throw new NoUserConnectedException(); + + CurrentConnected = null; + } + + public bool ModifyCurrentConnected(User newUser) + { + try + { + var index = _dataManager.GetFromData().ToList() + .FindIndex(u => u.Equals(_currentConnected)); + _dataManager.Data[nameof(User)][index] = newUser; + } + catch (ArgumentNullException e) + { + Debug.WriteLine("User to modify not found."); + return false; + } + + return true; + } + + private int GetRandomInt() + { + var randomGenerator = RandomNumberGenerator.Create(); + byte[] data = new byte[16]; + randomGenerator.GetBytes(data); + return Math.Abs(BitConverter.ToInt16(data)); + } + } +} diff --git a/MCTG/Model/Managers/DataManager.cs b/MCTG/Model/Managers/DataManager.cs deleted file mode 100644 index 8ff3ff6..0000000 --- a/MCTG/Model/Managers/DataManager.cs +++ /dev/null @@ -1,105 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Model -{ - /// - /// Define the manager of the data. This is where all the data are put, and where we call the loading and the saving of them. - /// - public class DataManager - { - #region Attributes & Properties - /// - /// The data manager injected that know how to serialize the data. - ///
The setter is actually public for testing purpose. It will be private after. - ///
See: - ///
- public IDataManager Serializer { get; set; } - - /// - /// The collection of all data. Each line of this dictionary has the type of the data as it key and the data for values. - /// - public Dictionary> Data { get; private set; } - #endregion - - #region Constructors - /// - /// Constructor of the DataManager class. Take a IDataManager that will provide methods for the serialisation of the data. - /// - /// The data manager that know how to serialize a file. - public DataManager(IDataManager dataMgr) - { - Serializer = dataMgr; - Data = Serializer.Load(); - } - #endregion - - #region Methods - /// - /// Reload the data. Useful to update new data written in the save file. - ///
See: - ///
- public void Reload() - => Data = Serializer.Load(); - - /// - /// Save the data. Call the Save method of the serializer. - ///
See: - ///
- public void Save() - => Serializer.Save(Data); - - /// - /// Import data from a file. - ///
See: - ///
- /// The type of data to import. - /// The path containing the name of the file created. - public void Import(string pathOfTheFile) - where T : class - { - KeyValuePair import = Serializer.Import(pathOfTheFile); - Data[import.Key].Add(import.Value); - } - - /// - /// Export the data from the collection of data. - ///
See: - ///
- /// The type of data to export - /// The object to export - /// The path containing the name of the file created. - public void Export(T obj, string pathToExport) - where T : class - => Serializer.Export(obj, pathToExport); - - /// - /// Get all the recipe from the data. - /// - /// The title to give for the Recipe Collection - /// A RecipeCollection that contain all the recipe in the data. - public RecipeCollection GetRecipes(string rcTitle = "default") - => new RecipeCollection(rcTitle, Data[nameof(Recipe)].Cast().ToArray()); - - /// - /// Get all the Users from the data. - /// - /// A list of all Users. - public List GetUsers() - => new List(Data[nameof(User)].Cast()); - - /// - /// Get a list of an item in the data. - /// - /// The type of the item - /// The list of all the item found in the data. - public ICollection GetFromData() where T : class - => new List(Data[typeof(T).Name].Cast()); - - #endregion - } -} diff --git a/MCTG/Model/Managers/IDataManager.cs b/MCTG/Model/Managers/IDataManager.cs index c8a4ca6..ccd40c7 100644 --- a/MCTG/Model/Managers/IDataManager.cs +++ b/MCTG/Model/Managers/IDataManager.cs @@ -1,44 +1,64 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Model -{ - /// - /// Interface that define the methods of a data serializer. - /// - public interface IDataManager - { - /// - /// Save all the data in a file. - /// - /// The data to save. - void Save(Dictionary> elements); - - /// - /// Load all the data from a file. - /// - /// The data loaded. - Dictionary> Load(); - - /// - /// Import an element to the collection of data. - /// - /// The type of the element to impoert - /// The path containing the name of the file. - /// A pair where the key is the entry in the data and the value is the value to add on this entry. - public KeyValuePair Import(string pathToImport) - where T : class; - - /// - /// Export an element from the collection of data. - /// - /// The type of the exported object. - /// The object to export. - /// The path containing the name of the file created. - public void Export(T obj, string pathToExport) - where T : class; - } -} +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Model +{ + /// + /// Define how to manage data. + /// + public interface IDataManager + { + /// + /// The data manager injected that know how to serialize the data. + ///
The setter is actually public for testing purpose. It will be private after. + ///
See: + ///
+ IDataSerializer Serializer { get; } + + /// + /// The collection of all data. Each line of this dictionary has the type of the data as it key and the data for values. + /// + Dictionary> Data { get; } + + + /// + /// Load the data. Used to update data written in the save file. + ///
See: + ///
+ void LoadData(); + + /// + /// Save the data. Call the Save method of the serializer. + ///
See: + ///
+ void SaveData(); + + /// + /// Import data from a file. + ///
See: + ///
+ /// The type of data to import. + /// The path containing the name of the file created. + void Import(string pathOfTheFile) where T : class; + + /// + /// Export the data from the collection of data. + ///
See: + ///
+ /// The type of data to export + /// The object to export + /// The path containing the name of the file created. + void Export(T obj, string pathToExport) where T : class; + + /// + /// Get a list of an item in the data. + /// + /// The type of the item + /// The list of all the item found in the data. + ICollection GetFromData() where T : class; + } +} diff --git a/MCTG/Model/Managers/IDataSerializer.cs b/MCTG/Model/Managers/IDataSerializer.cs new file mode 100644 index 0000000..eba0cb6 --- /dev/null +++ b/MCTG/Model/Managers/IDataSerializer.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Model +{ + /// + /// Interface that define the methods of a data serializer. + /// + public interface IDataSerializer + { + /// + /// Save all the data in a file. + /// + /// The data to save. + void Save(Dictionary> elements); + + /// + /// Load all the data from a file. + /// + /// The data loaded. + Dictionary> Load(); + + /// + /// Import an element to the collection of data. + /// + /// The type of the element to impoert + /// The path containing the name of the file. + /// A pair where the key is the entry in the data and the value is the value to add on this entry. + public KeyValuePair Import(string pathToImport) + where T : class; + + /// + /// Export an element from the collection of data. + /// + /// The type of the exported object. + /// The object to export. + /// The path containing the name of the file created. + public void Export(T obj, string pathToExport) + where T : class; + } +} diff --git a/MCTG/Model/Managers/IPasswordManager.cs b/MCTG/Model/Managers/IPasswordManager.cs new file mode 100644 index 0000000..59d85d7 --- /dev/null +++ b/MCTG/Model/Managers/IPasswordManager.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Model +{ + /// + /// Define how to manage passwords. + /// + public interface IPasswordManager + { + /// + /// Hash a plain text password. + /// + /// The plain password to hash. + /// The hashed password. + public string HashPassword(string password); + + /// + /// Verify a plain text password with a hashed one. + /// + /// The hashed password. + /// The plain text password. + /// True is the password is correct, false otherwise. + public bool VerifyPassword(string hashedPassword,string password); + } +} diff --git a/MCTG/Model/Managers/IRecipeManager.cs b/MCTG/Model/Managers/IRecipeManager.cs new file mode 100644 index 0000000..5a7fe22 --- /dev/null +++ b/MCTG/Model/Managers/IRecipeManager.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Model +{ + /// + /// Define how to manage recipes. + /// + public interface IRecipeManager + { + /// + /// Get or set the currently selected recipe. + /// + Recipe? CurrentSelected { get; set; } + + /// + /// Get all the recipe in the data. + /// + /// The RecipeCollection containing the recipes stored in the data. + RecipeCollection GetAllRecipes(); + + /// + /// Get the recipe corresponding to the id. + /// + /// The id of the recipe we want. + /// The recipe corresponding to the id. + Recipe GetRecipeFromId(int id); + + /// + /// Search in data the recipes containing this part of title. + /// + /// Search string. + /// The RecipeCollection of recipes corresponding to the search. + RecipeCollection SearchRecipeByTitle(string title); + + /// + /// Get all the recipes created by the author. + /// + /// The author's mail. + /// The RecipeCollection of the recipe created by the author. + RecipeCollection GetRecipeByAuthor(string authorMail); + + /// + /// Get an ordored list of the recipes sorted by the priority list. + /// + /// The priority list. + /// The RecipeCollection ordored by the priority list. + RecipeCollection GetRecipesByPriorityOrder(IEnumerable priority); + + /// + /// Add a recipe to the data. + /// + /// The recipe to add. + /// Weither the adding succed or not. + bool AddRecipeToData(Recipe recipe); + + /// + /// Modify the recipe with a new one in the data. + /// + /// The new recipe + /// Weither the modification succed or not. + bool ModifyCurrentSelected(Recipe newRecipe); + } +} diff --git a/MCTG/Model/Managers/IUserManager.cs b/MCTG/Model/Managers/IUserManager.cs new file mode 100644 index 0000000..9713ed5 --- /dev/null +++ b/MCTG/Model/Managers/IUserManager.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Model +{ + /// + /// Define how to manage an user. + /// + public interface IUserManager + { + /// + /// Get the current connected user. Null if no user is connected. + /// + User? CurrentConnected { get; } + + /// + /// Get the password manager used to hash passords. + /// + IPasswordManager PasswordManager { get; } + + /// + /// Get a collection of all users in the data. + /// + /// A collection of all user. + ICollection GetAllUsers(); + + /// + /// Get an user by his email. + /// + /// The user's mail. + /// The user corresponding to the mail. + User GetUserFromMail(string mail); + + /// + /// Create a new user. The mail and the password are required. Other can be null. + ///
This function use the password manager to hash the plain text password. + ///
+ /// The user's mail address. + /// The user's plain text password. + /// The user's name. + /// The user's surname. + /// The user's profile picture. + /// A new user. + User CreateUser(string mail, string password, + string? name = null, string? surname = null, string? profilePict = null); + + /// + /// Add an user in the data. + /// + /// The user to add. + /// True is the user was correctly added to the data. False otherwise. + bool AddUserToData(User user); + + /// + /// Modify the currently connected user. + /// + /// An user containing new user's properties to changes. + /// + bool ModifyCurrentConnected(User newUser); + + /// + /// Log in an user. If the connection succed, pass the connected user to . + /// + /// The User's mail. + /// The User's (plain text) password. + /// True if the connection succed, false otherwise. + bool LogIn(string mail, string password); + + /// + /// Log out the current connected user. + /// + void LogOut(); + } +} diff --git a/MCTG/Model/Managers/MasterManager.cs b/MCTG/Model/Managers/MasterManager.cs index aed3c0b..551a6a1 100644 --- a/MCTG/Model/Managers/MasterManager.cs +++ b/MCTG/Model/Managers/MasterManager.cs @@ -1,161 +1,57 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; -using System.ComponentModel; -using System.Diagnostics; -using System.Linq; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Text; -using System.Threading.Tasks; -namespace Model.Managers + +namespace Model { /// /// The Main manager of the model. /// - public class MasterManager : INotifyPropertyChanged + public class MasterManager { #region Attributes & Properties - public event PropertyChangedEventHandler? PropertyChanged; - - private RecipeCollection _recipesInSearch = new RecipeCollection(""); - - /// - /// The currently connected user. 'null' if no user is connected. - /// - public User? CurrentConnectedUser { get; private set; } - - public RecipeCollection RecipesInSearch - { - get => _recipesInSearch; - set - { - _recipesInSearch = value; - OnPropertyChange(); - } - } - /// - /// The collection of all recipes loaded. + /// Manage the data of the application. /// - public RecipeCollection Recipes { get; private set; } + public IDataManager Data { get; private set; } /// - /// The collection of all users loaded. + /// Manage the recipes of the application. /// - public List Users { get; private set; } + public IRecipeManager Recipe { get; private set; } /// - /// The data manager for load, save, export and import data. + /// Manage the users of the application. /// - public DataManager DataMgr { get; private set; } + public IUserManager User { get; private set; } #endregion #region Constructors /// /// Constructor of the MasterManager. /// - /// The serializer for the data. - public MasterManager(IDataManager dataManager) + /// The data manager. + /// The recipes manager. + /// The users manager. + public MasterManager(IDataManager dataManager, + IRecipeManager recipeManager, + IUserManager userManager) { - DataMgr = new DataManager(dataManager); - CurrentConnectedUser = null; - Recipes = DataMgr.GetRecipes("all recipes"); - RecipesInSearch = DataMgr.GetRecipes("search on"); - Users = DataMgr.GetUsers(); + Data = dataManager; + Recipe = recipeManager; + User = userManager; } #endregion #region Methods /// - /// Log in an user. Test if the log in information are correct then connect the user. + /// Setup all the necessary parameters before start. /// - /// The user's mail - /// The user's password. - /// True if the user was correctly connected, false if there is something wrong. - /// - public bool Login(string mail, string password) + public void Setup() { - if (Users is null || Users.Count == 0) - throw new ArgumentNullException("There is no users registred."); - -#if DEBUG - if (mail == "admin") - { - CurrentConnectedUser = Users.FirstOrDefault(u => u.Mail == "admin@mctg.fr"); - return true; - } -#endif - - User? user = Users.Find(u => u.Mail == mail); - - if (user is null || !user.psswMgr.VerifyPassword(user.Password, password)) - return false; - - CurrentConnectedUser = user; - return true; - } - - /// - /// Log out an user. - /// - /// True if the user is correctly diconnected, false otherwise. - public bool Logout() - { - if (CurrentConnectedUser is null) - return false; - - CurrentConnectedUser = null; - return true; + Data.LoadData(); } - - /// - /// Register an user. - /// - /// The new user to add in the database. - /// False if there is a problem with the registerement, true otherwise. - public bool Register(User newuser) - { - try - { - User user = newuser; - DataMgr.Data[nameof(User)].Add(user); - Users = DataMgr.GetUsers(); - } - catch (ArgumentException e) - { - Debug.WriteLine(e.Message); - return false; - } - - return true; - } - - /// - /// Add a recipe to the database. - /// - /// The recipe to add. - public void AddRecipe(Recipe recipe) - { - DataMgr.Data[nameof(Recipe)].Add(recipe); - Recipes = DataMgr.GetRecipes(); - } - - /// - /// Get the current connected user's personal recipes. - /// - /// The current connected user's personal recipes. - public RecipeCollection GetCurrentUserRecipes() - => new RecipeCollection("User recipes", - DataMgr.GetRecipes().ToList().FindAll(r => r.AuthorMail == CurrentConnectedUser?.Mail).ToArray()); - - /// - /// Notify property change handler. - /// - /// the name of the property that change. - public void OnPropertyChange([CallerMemberName] string propertyName = "") - => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + #endregion } - #endregion } diff --git a/MCTG/Model/Model.csproj b/MCTG/Model/Model.csproj index 97854fe..097e9d6 100644 --- a/MCTG/Model/Model.csproj +++ b/MCTG/Model/Model.csproj @@ -1,10 +1,15 @@ - - - - net7.0 - enable - enable - Debug;Release;CI - - - + + + + net7.0 + enable + enable + Debug;Release;CI + + + + + + + + diff --git a/MCTG/Model/Recipes/ReadOnlyObservableRecipeCollection.cs b/MCTG/Model/Recipes/ReadOnlyObservableRecipeCollection.cs new file mode 100644 index 0000000..8def54c --- /dev/null +++ b/MCTG/Model/Recipes/ReadOnlyObservableRecipeCollection.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading.Tasks; + +namespace Model +{ + public class ReadOnlyObservableRecipeCollection : ReadOnlyObservableCollection + { + private RecipeCollection _recipeObserved; + + public string Description => _recipeObserved.Description; + + public ReadOnlyObservableRecipeCollection(RecipeCollection recipeCollection) + : base(recipeCollection) + { + _recipeObserved = recipeCollection; + } + } +} diff --git a/MCTG/Model/Recipes/Recipe.cs b/MCTG/Model/Recipes/Recipe.cs index 7ba928a..9c48274 100644 --- a/MCTG/Model/Recipes/Recipe.cs +++ b/MCTG/Model/Recipes/Recipe.cs @@ -52,6 +52,12 @@ namespace Model } } + /// + /// Priority of this recipe. + /// + [DataMember(Name = "priority")] + public Priority Priority { get; private set; } + /// /// The Title of the recipe.
/// Set to "No title." when the value passed is null, empty or contain white spaces. @@ -110,13 +116,15 @@ namespace Model ///
/// The title of the recipe /// The type of the recipe. + /// The priority of this recipe. /// The id of the recipe. If not given, get a new id. /// The name of the user that create this recipe. /// The image that represent the recipe - public Recipe(string title, RecipeType type, int? id, string? authorMail, string? picture) + public Recipe(string title, RecipeType type, Priority priority, int? id, string? authorMail, string? picture) { Title = title; Type = type; + Priority = priority; Image = picture; AuthorMail = authorMail; @@ -134,18 +142,21 @@ namespace Model else Id = (int)id; } - public Recipe(string title, RecipeType type, int? id, string? authorMail) - : this(title, type, id, authorMail, null) + /// + public Recipe(string title, RecipeType type, Priority priority, int? id, string? authorMail) + : this(title, type, priority, id, authorMail, null) { } - public Recipe(string title, RecipeType type) - : this(title, type, null, null) + /// + public Recipe(string title, RecipeType type, Priority priority) + : this(title, type, priority, null, null) { } + /// public Recipe(string title) - : this(title, RecipeType.Unspecified) + : this(title, RecipeType.Unspecified, Priority.Fast) { } #endregion @@ -176,7 +187,7 @@ namespace Model /// Concatenate the list of ingredients in a single string /// /// The list of ingredients in string format - private string ConcatIngredients() + public string ConcatIngredients() { StringBuilder sb = new StringBuilder(); foreach (Ingredient ingredient in Ingredients) @@ -197,9 +208,11 @@ namespace Model public override bool Equals(object? obj) { - var item = obj as Recipe; - if (item == null) return false; - return Equals(obj); + if (ReferenceEquals(obj, null)) return false; + if (ReferenceEquals(obj, this)) return true; + if (GetType() != obj.GetType()) return false; + + return Equals(obj as Recipe); } public override int GetHashCode() @@ -209,21 +222,7 @@ namespace Model public override string ToString() { - StringBuilder sb = new StringBuilder($"[Recipe n°{Id}] - {Title}\n"); - foreach (PreparationStep ps in PreparationSteps) - { - sb.AppendFormat("\t* {0}\n", ps.ToString()); - } - sb.AppendLine(); - sb.AppendLine(ConcatIngredients()); - sb.AppendLine(); - foreach (Review review in Reviews) - { - sb.AppendLine(review.ToString()); - } - sb.AppendLine(); - sb.AppendLine($"Posted by: {AuthorMail?.ToString()}"); - return sb.ToString(); + return $"'{Title}' [{Id}] - {Type}, {Priority} | {AuthorMail} | {Image}"; } #endregion } diff --git a/MCTG/Model/Recipes/RecipeCollection.cs b/MCTG/Model/Recipes/RecipeCollection.cs index 7579d5f..c58bda2 100644 --- a/MCTG/Model/Recipes/RecipeCollection.cs +++ b/MCTG/Model/Recipes/RecipeCollection.cs @@ -2,6 +2,7 @@ using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.ComponentModel; using System.Text; namespace Model @@ -31,53 +32,32 @@ namespace Model _description = "No description."; else _description = value; + + OnPropertyChanged(new PropertyChangedEventArgs("Description")); } } #endregion #region Constructors /// - /// Construct a new collection of _recipes. + /// Construct a new collection of recipes. /// - /// A short description of what this list will contain - /// Recipes to add in this new collection - public RecipeCollection(string description, params Recipe[] recipes) + /// A short description of what this list will contain. + /// Recipes to add in this new collection. + public RecipeCollection(string description, ICollection recipes) : base(recipes) { Description = description; } - #endregion - #region Methods - /// - /// Find a recipe in this list by giving the id. - /// - /// The id of the list we are looking for - /// The recipe of the id given - /// - public Recipe? GetRecipeById(int id) + /// + public RecipeCollection(string description) + : base() { - Recipe? recipe = this.ToList().Find(r => r.Id == id); - - if (recipe == null) throw new ArgumentException("No _recipes match the given id."); - return recipe; - } - - /// - /// Utility to find a recipe by his _name. - /// - /// The string for the search - /// A collection of Recipe where their Title contain the string. - public RecipeCollection ResearchByName(string str) - { - if (string.IsNullOrEmpty(str)) - return this; - - return new RecipeCollection( - description: $"Results of the research: {str}", - recipes: this.ToList().FindAll(x => x.Title.ToLower().Contains(str.ToLower())).ToArray()); } + #endregion + #region Methods public virtual bool Equals(RecipeCollection? other) { if (other == null) return false; diff --git a/MCTG/Model/Recipes/Review.cs b/MCTG/Model/Recipes/Review.cs index f55d6e7..2640760 100644 --- a/MCTG/Model/Recipes/Review.cs +++ b/MCTG/Model/Recipes/Review.cs @@ -1,4 +1,4 @@ -using Model.Managers; +using Model; using System; using System.Collections.Generic; using System.Linq; diff --git a/MCTG/Model/User/IPasswordManager.cs b/MCTG/Model/User/IPasswordManager.cs deleted file mode 100644 index 5ca0732..0000000 --- a/MCTG/Model/User/IPasswordManager.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Model -{ - public interface IPasswordManager - { - public string HashPassword(string password); - public bool VerifyPassword(string hashedPassword,string password); - - } -} diff --git a/MCTG/Model/User/Priority.cs b/MCTG/Model/Users/Priority.cs similarity index 100% rename from MCTG/Model/User/Priority.cs rename to MCTG/Model/Users/Priority.cs diff --git a/MCTG/Model/User/User.cs b/MCTG/Model/Users/User.cs similarity index 60% rename from MCTG/Model/User/User.cs rename to MCTG/Model/Users/User.cs index 1085d0e..86dd823 100644 --- a/MCTG/Model/User/User.cs +++ b/MCTG/Model/Users/User.cs @@ -1,205 +1,184 @@ - -using System.Runtime.CompilerServices; -using System.Runtime.Serialization; -using System.ComponentModel; - -namespace Model -{ - /// - /// A user is an entity with a _name, a surname, mail, profilePict and a list of priority. - /// This user can login with an Id and a password - /// - [DataContract(Name = "user")] - public class User : IEquatable , INotifyPropertyChanged - { - #region Private Attributes - - [DataMember] private string name=""; - [DataMember] private string surname=""; - [DataMember] private string mail = ""; - [DataMember] private string picture = ""; - [DataMember] private string password = ""; - [DataMember] private List priorities; - - public event PropertyChangedEventHandler? PropertyChanged; - #endregion - - #region Properties - - /// - /// Property to get Name of users and a setter - /// - /// Setter have Exception which is trigger when Name is null - public string Name - { - get { return name; } - set - { - - name = value; - OnPropertyChanged(); - } - } - - /// - /// Property to get Surname of users and a setter - /// - /// Setter have Exception which is trigger when Surname is null - public string Surname - { - get { return surname; } - set - { - - surname = value; - OnPropertyChanged(); - } - } - - /// - /// Property to get mail of users and a setter - /// - /// User's mail will serve to log the user. So there's no setter, just an init. User will enter one time his email at his - /// account creation. - public string Mail - { - get { return mail; } - private init - { - if (string.IsNullOrWhiteSpace(value)) - { - throw new ArgumentException("Impossible d'avoir un champ Email vide!"); - } - mail = value; - } - } - - public string Password - { - get => password; - set => password = value; - - } - - - /// - /// For now, we define the ProfilPict as a string which is "PhotoParDefaut" - /// when the value is null. - /// - public string ProfilPict - { - get => picture; - set => picture = value; - - } - - /// - /// This is the list of priorities specific tu the user. This list is initiate - /// by default. User could change it at will. - /// - - public List Priorities - { - get => priorities; - set=> priorities = value; - } - - public override bool Equals(object? other) - { - if (other == null) return false; - if (other == this) return true; - return Equals(other); - } - - public bool Equals(User? other) - { - if (other == null) return false; - return Name.Equals(other.Name) && Surname.Equals(other.Surname) && Mail.Equals(other.Mail); - } - - public override int GetHashCode() - { - throw new NotImplementedException(); - } - - - protected void OnPropertyChanged ([CallerMemberName] string? propertyName = null) - { - if (PropertyChanged != null) - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - } - - public override string ToString() - { - return $"{Name} {Surname}"; - } - - [DataMember(Name = "passmgr")] - public IPasswordManager psswMgr { get; private set; } - - - #endregion - - #region Constructors - - /// - /// Construtors of user. - /// - /// The name of the user - /// The surname of the user - /// The user needs an email to login. - /// The password of the new user. - /// The password manager to manage the user password. - public User(string name, string surname, string mail, string password, IPasswordManager passwordManager) - { - Name = name; - Surname = surname; - Mail = mail; - psswMgr = passwordManager; - Password = psswMgr.HashPassword(password); - priorities = new List { - Priority.Gourmet, - Priority.Economic, - Priority.Fast, - Priority.Light, - Priority.Easy}; - ProfilPict = picture; - - } - - /// - /// - /// - public User(string name, string surname, string mail, string password) - : this(name, surname,mail, password, new PasswordSHA256()) - { - - } - - /// - /// - /// - public User() - : this("John", "Doe", "truc@gmail.com", "mdp") - { - - } - - /// - /// - /// - public User (User user) - { - Name = user.Name; - Surname = user.Surname; - Mail = user.Mail; - psswMgr = user.psswMgr; - Password = user.Password; - priorities = user.Priorities; - ProfilPict = user.ProfilPict; - } - - - #endregion - } -} + +using System.Runtime.CompilerServices; +using System.Runtime.Serialization; +using System.ComponentModel; +using System.Collections.ObjectModel; + +namespace Model +{ + /// + /// A user is an entity with a name, a surname, mail, profilePict and a list of priority. + /// This user can login with an Id and a password + /// + [DataContract(Name = "user")] + public class User : IEquatable , INotifyPropertyChanged + { + #region Private Attributes + [DataMember] private string _name = ""; + [DataMember] private string _surname = ""; + [DataMember] private string _mail = ""; + + [DataMember(Name = "priorities")] + public ObservableCollection _priorities { get; private set; } = new ObservableCollection + { + Priority.Gourmet, + Priority.Economic, + Priority.Fast, + Priority.Light, + Priority.Easy + }; + + public event PropertyChangedEventHandler? PropertyChanged; + #endregion + + #region Properties + /// + /// Property to get Name of users and a setter + /// + /// Setter have Exception which is trigger when Name is null + public string Name + { + get { return _name; } + set + { + + _name = value; + OnPropertyChanged(); + } + } + + /// + /// Property to get Surname of users and a setter + /// + /// Setter have Exception which is trigger when Surname is null + public string Surname + { + get { return _surname; } + set + { + + _surname = value; + OnPropertyChanged(); + } + } + + /// + /// Property to get mail of users and a setter + /// + /// User's mail will serve to log the user. So there's no setter, just an init. User will enter one time his email at his + /// account creation. + public string Mail + { + get { return _mail; } + private init + { + if (string.IsNullOrWhiteSpace(value)) + { + throw new ArgumentException("Impossible d'avoir un champ Email vide!"); + } + _mail = value; + } + } + + /// + /// The user's hashed password. The hashed method is defined with the PasswordManager. + ///
See: . + ///
+ [DataMember(Name = "hashedpass")] + public string Password { get; private set; } = ""; + + /// + /// For now, we define the ProfilePict as a string which is "PhotoParDefaut" + /// when the value is null. + /// + [DataMember(Name = "profilepic")] + public string ProfilePict { get; private set; } = "default_picture.png"; + + /// + /// This is the list of priorities specific tu the user. This list is initiate + /// by default. User could change it at will. + /// + public ReadOnlyObservableCollection Priorities { get; private set; } + #endregion + + #region Methods + public override bool Equals(object? other) + { + if (ReferenceEquals(other, null)) return false; + if (ReferenceEquals(other, this)) return true; + if (GetType() != other.GetType()) return false; + + return Equals(other as User); + } + + public bool Equals(User? other) + { + if (other == null) return false; + if (other == this) return true; + + return Name.Equals(other.Name) && Surname.Equals(other.Surname) && Mail.Equals(other.Mail); + } + + public override int GetHashCode() + { + throw new NotImplementedException(); + } + + protected void OnPropertyChanged([CallerMemberName] string? propertyName = null) + { + if (PropertyChanged != null) + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + + public override string ToString() + { + return $"{Name} {Surname}"; + } + #endregion + + #region Constructors + /// + /// Construtors of user. + /// + /// The name of the user + /// The surname of the user + /// The user needs an email to login. + /// The password of the new user. + public User(string name, string surname, string mail, string hashedPassword) + { + Name = name; + Surname = surname; + Mail = mail; + Password = hashedPassword; + Priorities = new ReadOnlyObservableCollection(_priorities); + } + + /// + /// Profile picture of the new user. + public User(string name, string surname, string mail, string hashedPassword, string profilePict) + : this(name, surname, mail, hashedPassword) + { + ProfilePict = profilePict; + } + + /// + public User() + : this("John", "Doe", "truc@gmail.com", "mdp") + { + } + + /// + /// The user to copy. + public User(User user) + { + Name = user.Name; + Surname = user.Surname; + Mail = user.Mail; + Password = user.Password; + _priorities = user._priorities; + Priorities = new ReadOnlyObservableCollection(_priorities); + ProfilePict = user.ProfilePict; + } + #endregion + } +} diff --git a/MCTG/DataPersistence/DataContractJSON.cs b/MCTG/Persistance/DataPersistence/DataContractJSON.cs similarity index 92% rename from MCTG/DataPersistence/DataContractJSON.cs rename to MCTG/Persistance/DataPersistence/DataContractJSON.cs index b32cbd2..bdb25d4 100644 --- a/MCTG/DataPersistence/DataContractJSON.cs +++ b/MCTG/Persistance/DataPersistence/DataContractJSON.cs @@ -1,20 +1,12 @@ using Model; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.Serialization; using System.Runtime.Serialization.Json; -using System.Text; -using System.Threading.Tasks; -using System.Xml; -using System.Xml.Linq; namespace DataPersistence { /// /// Define a serializer to manage JSON files. /// - public class DataContractJSON : IDataManager + public class DataContractJSON : IDataSerializer { #region Attributes private string _jsonFolderPath; @@ -38,6 +30,7 @@ namespace DataPersistence { typeof(Recipe), typeof(RecipeType), + typeof(Priority), typeof(Review), typeof(User), typeof(Ingredient), diff --git a/MCTG/DataPersistence/DataContractXML.cs b/MCTG/Persistance/DataPersistence/DataContractXML.cs similarity index 92% rename from MCTG/DataPersistence/DataContractXML.cs rename to MCTG/Persistance/DataPersistence/DataContractXML.cs index b3d1d50..e8443c4 100644 --- a/MCTG/DataPersistence/DataContractXML.cs +++ b/MCTG/Persistance/DataPersistence/DataContractXML.cs @@ -1,19 +1,13 @@ using Model; -using System; -using System.Collections.Generic; -using System.Linq; using System.Runtime.Serialization; -using System.Text; -using System.Threading.Tasks; using System.Xml; -using System.Xml.Linq; namespace DataPersistence { /// /// Define a serializer to manage XML files. /// - public class DataContractXML : IDataManager + public class DataContractXML : IDataSerializer { #region Attributes private string _xmlFolderPath; @@ -46,11 +40,11 @@ namespace DataPersistence { typeof(Recipe), typeof(RecipeType), + typeof(Priority), typeof(Review), typeof(User), typeof(Ingredient), - typeof(Quantity), - typeof(PasswordSHA256) + typeof(Quantity) }, PreserveObjectReferences = true }; diff --git a/MCTG/DataPersistence/DataPersistence.csproj b/MCTG/Persistance/DataPersistence/DataPersistence.csproj similarity index 79% rename from MCTG/DataPersistence/DataPersistence.csproj rename to MCTG/Persistance/DataPersistence/DataPersistence.csproj index 4b28c7b..71cce12 100644 --- a/MCTG/DataPersistence/DataPersistence.csproj +++ b/MCTG/Persistance/DataPersistence/DataPersistence.csproj @@ -6,8 +6,8 @@ enable - - + + diff --git a/MCTG/Persistance/FakePersistance/FakePersistance.csproj b/MCTG/Persistance/FakePersistance/FakePersistance.csproj new file mode 100644 index 0000000..2df6af7 --- /dev/null +++ b/MCTG/Persistance/FakePersistance/FakePersistance.csproj @@ -0,0 +1,14 @@ + + + + net7.0 + enable + enable + + + + + + + + diff --git a/MCTG/DataPersistence/Stubs.cs b/MCTG/Persistance/FakePersistance/Stubs.cs similarity index 89% rename from MCTG/DataPersistence/Stubs.cs rename to MCTG/Persistance/FakePersistance/Stubs.cs index 925dbae..cfad2b9 100644 --- a/MCTG/DataPersistence/Stubs.cs +++ b/MCTG/Persistance/FakePersistance/Stubs.cs @@ -1,18 +1,18 @@ -using Model; +using Managers; +using Model; using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Text.RegularExpressions; -using System.Threading.Tasks; -namespace DataPersistence + +namespace FakePersistance { /// /// The subs class is a group of prefabricated object that can only be loaded. It only use is for testing. /// - public class Stubs : IDataManager + public class Stubs : IDataSerializer { + private IPasswordManager psswdMgr = new PasswordSHA256Manager(); + public Dictionary> Load() { Dictionary> data = new Dictionary> @@ -22,7 +22,7 @@ namespace DataPersistence nameof(Recipe), new List(new[] { - new Recipe("Cookies classiques", RecipeType.Dessert, null, "admin@mctg.fr", "") + new Recipe("Cookies classiques", RecipeType.Dessert, Priority.Easy, null, "admin@mctg.fr", "") { Ingredients = new List { @@ -42,7 +42,8 @@ namespace DataPersistence }, new Recipe( title: "Cookies au chocolat", - type: RecipeType.Dessert) + type: RecipeType.Dessert, + priority: Priority.Fast) { Ingredients = new List { @@ -57,7 +58,8 @@ namespace DataPersistence }, new Recipe( title: "Gateau nature", - type: RecipeType.Dessert) + type: RecipeType.Dessert, + priority: Priority.Gourmet) { Ingredients = new List { @@ -77,7 +79,8 @@ namespace DataPersistence }, new Recipe( title: "Gateau au pommes", - type: RecipeType.Dessert) + type: RecipeType.Dessert, + priority: Priority.Light) { PreparationSteps = new List { @@ -90,6 +93,7 @@ namespace DataPersistence new Recipe( title: "Gateau au chocolat", type: RecipeType.Dessert, + priority: Priority.Economic, id: null, authorMail: "pedrosamigos@hotmail.com") { Ingredients = new List @@ -110,6 +114,7 @@ namespace DataPersistence new Recipe( title: "Dinde au jambon", type: RecipeType.Dish, + priority: Priority.Easy, id: null, authorMail: "pedrosamigos@hotmail.com") { Ingredients = new List @@ -130,6 +135,7 @@ namespace DataPersistence new Recipe( title: "Poulet au curry", type: RecipeType.Dish, + priority: Priority.Gourmet, id: null, authorMail: "pedrosamigos@hotmail.com") { Ingredients = new List @@ -165,12 +171,12 @@ namespace DataPersistence name: "Admin", surname: "Admin", mail: "admin@mctg.fr", - password: "admin"), + hashedPassword: psswdMgr.HashPassword("admin")), new User( name: "Pedros", surname: "Amigos", mail: "pedrosamigos@hotmail.com", - password: "pamigos") + hashedPassword: psswdMgr.HashPassword("pamigos")) }) #endregion diff --git a/MCTG/SAE-2.01.sln b/MCTG/SAE-2.01.sln index c73c36d..a980a33 100644 --- a/MCTG/SAE-2.01.sln +++ b/MCTG/SAE-2.01.sln @@ -1,55 +1,77 @@ -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.5.33516.290 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConsoleApp", "ConsoleApp\ConsoleApp.csproj", "{666C2211-8EBB-4FC8-9484-CB93BC854153}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Model", "Model\Model.csproj", "{42FF86BD-92F9-4A32-A938-68515905378F}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Views", "Views\Views.csproj", "{508B5600-AFD0-4AE4-A3CF-5FA8BE3ECE75}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Model_UnitTests", "Tests\Model_UnitTests\Model_UnitTests.csproj", "{45AB746A-194B-4E43-81EB-83B06F35AA33}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{08B80CE8-A01D-4D86-8989-AF225D5DA48C}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DataPersistence", "DataPersistence\DataPersistence.csproj", "{432F9D12-B1F7-4A79-8720-4971BB10B831}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {666C2211-8EBB-4FC8-9484-CB93BC854153}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {666C2211-8EBB-4FC8-9484-CB93BC854153}.Debug|Any CPU.Build.0 = Debug|Any CPU - {666C2211-8EBB-4FC8-9484-CB93BC854153}.Release|Any CPU.ActiveCfg = Release|Any CPU - {666C2211-8EBB-4FC8-9484-CB93BC854153}.Release|Any CPU.Build.0 = Release|Any CPU - {42FF86BD-92F9-4A32-A938-68515905378F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {42FF86BD-92F9-4A32-A938-68515905378F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {42FF86BD-92F9-4A32-A938-68515905378F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {42FF86BD-92F9-4A32-A938-68515905378F}.Release|Any CPU.Build.0 = Release|Any CPU - {508B5600-AFD0-4AE4-A3CF-5FA8BE3ECE75}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {508B5600-AFD0-4AE4-A3CF-5FA8BE3ECE75}.Debug|Any CPU.Build.0 = Debug|Any CPU - {508B5600-AFD0-4AE4-A3CF-5FA8BE3ECE75}.Debug|Any CPU.Deploy.0 = Debug|Any CPU - {508B5600-AFD0-4AE4-A3CF-5FA8BE3ECE75}.Release|Any CPU.ActiveCfg = Release|Any CPU - {508B5600-AFD0-4AE4-A3CF-5FA8BE3ECE75}.Release|Any CPU.Build.0 = Release|Any CPU - {508B5600-AFD0-4AE4-A3CF-5FA8BE3ECE75}.Release|Any CPU.Deploy.0 = Release|Any CPU - {45AB746A-194B-4E43-81EB-83B06F35AA33}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {45AB746A-194B-4E43-81EB-83B06F35AA33}.Debug|Any CPU.Build.0 = Debug|Any CPU - {45AB746A-194B-4E43-81EB-83B06F35AA33}.Release|Any CPU.ActiveCfg = Release|Any CPU - {45AB746A-194B-4E43-81EB-83B06F35AA33}.Release|Any CPU.Build.0 = Release|Any CPU - {432F9D12-B1F7-4A79-8720-4971BB10B831}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {432F9D12-B1F7-4A79-8720-4971BB10B831}.Debug|Any CPU.Build.0 = Debug|Any CPU - {432F9D12-B1F7-4A79-8720-4971BB10B831}.Release|Any CPU.ActiveCfg = Release|Any CPU - {432F9D12-B1F7-4A79-8720-4971BB10B831}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {45AB746A-194B-4E43-81EB-83B06F35AA33} = {08B80CE8-A01D-4D86-8989-AF225D5DA48C} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {ADEA5603-1EF6-4D43-9493-7D6D9DE7FA3F} - EndGlobalSection -EndGlobal +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.33516.290 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConsoleApp", "ConsoleApp\ConsoleApp.csproj", "{666C2211-8EBB-4FC8-9484-CB93BC854153}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Model", "Model\Model.csproj", "{42FF86BD-92F9-4A32-A938-68515905378F}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Views", "Views\Views.csproj", "{508B5600-AFD0-4AE4-A3CF-5FA8BE3ECE75}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Model_UnitTests", "Tests\Model_UnitTests\Model_UnitTests.csproj", "{45AB746A-194B-4E43-81EB-83B06F35AA33}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{08B80CE8-A01D-4D86-8989-AF225D5DA48C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DataPersistence", "Persistance\DataPersistence\DataPersistence.csproj", "{432F9D12-B1F7-4A79-8720-4971BB10B831}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Managers", "Managers\Managers.csproj", "{A3703A19-687C-4F63-A5DE-18E6D8995C77}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AppException", "AppException\AppException.csproj", "{77E6BD97-B1E5-45F5-ABFB-9A1D985A8EDE}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FakePersistance", "Persistance\FakePersistance\FakePersistance.csproj", "{7C340CB2-8925-4BC4-9D8C-9058D9657F3F}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Persistance", "Persistance", "{F6413DA3-CE67-4097-8FF7-8D221AF2A5E8}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {666C2211-8EBB-4FC8-9484-CB93BC854153}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {666C2211-8EBB-4FC8-9484-CB93BC854153}.Debug|Any CPU.Build.0 = Debug|Any CPU + {666C2211-8EBB-4FC8-9484-CB93BC854153}.Release|Any CPU.ActiveCfg = Release|Any CPU + {666C2211-8EBB-4FC8-9484-CB93BC854153}.Release|Any CPU.Build.0 = Release|Any CPU + {42FF86BD-92F9-4A32-A938-68515905378F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {42FF86BD-92F9-4A32-A938-68515905378F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {42FF86BD-92F9-4A32-A938-68515905378F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {42FF86BD-92F9-4A32-A938-68515905378F}.Release|Any CPU.Build.0 = Release|Any CPU + {508B5600-AFD0-4AE4-A3CF-5FA8BE3ECE75}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {508B5600-AFD0-4AE4-A3CF-5FA8BE3ECE75}.Debug|Any CPU.Build.0 = Debug|Any CPU + {508B5600-AFD0-4AE4-A3CF-5FA8BE3ECE75}.Debug|Any CPU.Deploy.0 = Debug|Any CPU + {508B5600-AFD0-4AE4-A3CF-5FA8BE3ECE75}.Release|Any CPU.ActiveCfg = Release|Any CPU + {508B5600-AFD0-4AE4-A3CF-5FA8BE3ECE75}.Release|Any CPU.Build.0 = Release|Any CPU + {508B5600-AFD0-4AE4-A3CF-5FA8BE3ECE75}.Release|Any CPU.Deploy.0 = Release|Any CPU + {45AB746A-194B-4E43-81EB-83B06F35AA33}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {45AB746A-194B-4E43-81EB-83B06F35AA33}.Debug|Any CPU.Build.0 = Debug|Any CPU + {45AB746A-194B-4E43-81EB-83B06F35AA33}.Release|Any CPU.ActiveCfg = Release|Any CPU + {45AB746A-194B-4E43-81EB-83B06F35AA33}.Release|Any CPU.Build.0 = Release|Any CPU + {432F9D12-B1F7-4A79-8720-4971BB10B831}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {432F9D12-B1F7-4A79-8720-4971BB10B831}.Debug|Any CPU.Build.0 = Debug|Any CPU + {432F9D12-B1F7-4A79-8720-4971BB10B831}.Release|Any CPU.ActiveCfg = Release|Any CPU + {432F9D12-B1F7-4A79-8720-4971BB10B831}.Release|Any CPU.Build.0 = Release|Any CPU + {A3703A19-687C-4F63-A5DE-18E6D8995C77}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A3703A19-687C-4F63-A5DE-18E6D8995C77}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A3703A19-687C-4F63-A5DE-18E6D8995C77}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A3703A19-687C-4F63-A5DE-18E6D8995C77}.Release|Any CPU.Build.0 = Release|Any CPU + {77E6BD97-B1E5-45F5-ABFB-9A1D985A8EDE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77E6BD97-B1E5-45F5-ABFB-9A1D985A8EDE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77E6BD97-B1E5-45F5-ABFB-9A1D985A8EDE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77E6BD97-B1E5-45F5-ABFB-9A1D985A8EDE}.Release|Any CPU.Build.0 = Release|Any CPU + {7C340CB2-8925-4BC4-9D8C-9058D9657F3F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7C340CB2-8925-4BC4-9D8C-9058D9657F3F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7C340CB2-8925-4BC4-9D8C-9058D9657F3F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7C340CB2-8925-4BC4-9D8C-9058D9657F3F}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {45AB746A-194B-4E43-81EB-83B06F35AA33} = {08B80CE8-A01D-4D86-8989-AF225D5DA48C} + {432F9D12-B1F7-4A79-8720-4971BB10B831} = {F6413DA3-CE67-4097-8FF7-8D221AF2A5E8} + {7C340CB2-8925-4BC4-9D8C-9058D9657F3F} = {F6413DA3-CE67-4097-8FF7-8D221AF2A5E8} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {ADEA5603-1EF6-4D43-9493-7D6D9DE7FA3F} + EndGlobalSection +EndGlobal diff --git a/MCTG/Tests/Model_UnitTests/Model_UnitTests.csproj b/MCTG/Tests/Model_UnitTests/Model_UnitTests.csproj index aff436e..597613f 100644 --- a/MCTG/Tests/Model_UnitTests/Model_UnitTests.csproj +++ b/MCTG/Tests/Model_UnitTests/Model_UnitTests.csproj @@ -1,27 +1,30 @@ - - - net7.0 - enable - enable - false - true - Debug;Release;CI - - - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - - - - - - + + + net7.0 + enable + enable + false + true + Debug;Release;CI + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + + diff --git a/MCTG/Tests/Model_UnitTests/RecipeCollection_UT.cs b/MCTG/Tests/Model_UnitTests/RecipeCollection_UT.cs deleted file mode 100644 index da0eb72..0000000 --- a/MCTG/Tests/Model_UnitTests/RecipeCollection_UT.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using DataPersistence; -using Model; -using Model.Managers; - -namespace Model_UnitTests -{ - public class RecipeCollection_UT - { - [Fact] - public void TestResearchByName() - { - MasterManager masterManager = new MasterManager(new Stubs()); - RecipeCollection recipes = masterManager.DataMgr.GetRecipes("test rc"); - - Recipe? search_result = recipes.ResearchByName("chocolat").FirstOrDefault(); - Assert.NotNull(search_result); - Assert.Equal("Cookies au chocolat", search_result.Title); - } - } -} diff --git a/MCTG/Tests/Model_UnitTests/Recipe_UT.cs b/MCTG/Tests/Model_UnitTests/Recipe_UT.cs index b98ab2a..6f7d8ef 100644 --- a/MCTG/Tests/Model_UnitTests/Recipe_UT.cs +++ b/MCTG/Tests/Model_UnitTests/Recipe_UT.cs @@ -8,7 +8,7 @@ namespace Model_UnitTests public void TestVoidConstructor() { Recipe r = new Recipe( - title: "test recipe", type: RecipeType.Unspecified); + title: "test recipe", type: RecipeType.Unspecified, priority: Priority.Easy); Assert.NotNull(r.Title); } diff --git a/MCTG/Tests/Model_UnitTests/test_unit_user.cs b/MCTG/Tests/Model_UnitTests/test_unit_user.cs index 45e4107..db87736 100644 --- a/MCTG/Tests/Model_UnitTests/test_unit_user.cs +++ b/MCTG/Tests/Model_UnitTests/test_unit_user.cs @@ -12,8 +12,8 @@ namespace Model_UnitTests [Fact] public void TestConstructUser() { - PasswordSHA256 passwordManager = new PasswordSHA256(); - User user = new User("Bob", "Dylan", "bd@gmail.com", "bobby"); + PasswordSHA256Manager passwordManager = new PasswordSHA256Manager(); + User user = new User("Bob", "Dylan", "bd@gmail.com", passwordManager.HashPassword("bobby")); Assert.Equal("Bob", user.Name); Assert.Equal("Dylan", user.Surname); Assert.Equal("bd@gmail.com", user.Mail); diff --git a/MCTG/Views/App.xaml.cs b/MCTG/Views/App.xaml.cs index 9cc7734..d0d0bb4 100644 --- a/MCTG/Views/App.xaml.cs +++ b/MCTG/Views/App.xaml.cs @@ -1,52 +1,74 @@ -#if WINDOWS -using Microsoft.UI; -using Microsoft.UI.Windowing; -using Windows.Graphics; -#endif - +using Model; +using FakePersistance; using DataPersistence; -using Model; -using System.Collections.ObjectModel; -using Model.Managers; -using Microsoft.Maui.Controls; -using System.Linq; -using System.ComponentModel; -using System.Runtime.CompilerServices; +using Managers; +using System.Diagnostics; namespace Views { public partial class App : Application - { - //Point d'entrée de l'application - public MasterManager MasterMgr { get; private set; } = new MasterManager(new Stubs()); - //L'utilisateur courant de l'application - public User CurrentUser { get; set; } - private Recipe currentRecipe { get; set; } - public Recipe CurrentRecipe + { + /// + /// Master manager - access to the Model. + /// + public MasterManager Master { get; private set; } + + public App() { - get => currentRecipe; - set + Debug.WriteLine("Hello, World!\n\n"); + + string path = FileSystem.Current.AppDataDirectory; // - path to the save file + string strategy = "xml"; // - strategy is 'xml' or 'json' (/!\ this is case sensitive) + + // Initialize the data serializer + IDataSerializer dataSerializer = (strategy == "xml") ? + new DataContractXML(path) + : new DataContractJSON(path); + + // Initialize the data manager + IDataManager dataManager = (!File.Exists(Path.Combine(path, $"data.{strategy}"))) ? + new DataDefaultManager(new Stubs()) + : new DataDefaultManager(dataSerializer); + + // Initialize the other managers + IRecipeManager recipeManager = new RecipeDefaultManager(dataManager); + IPasswordManager passwordManager = new PasswordSHA256Manager(); + IUserManager userManager = new UserDefaultManager(dataManager, passwordManager); + + // Initialize the master manager + Master = new MasterManager(dataManager, recipeManager, userManager); + Master.Setup(); + + // Change the data serializer if the one in place is 'Stubs' + if (Master.Data.Serializer.GetType() == typeof(Stubs)) { - currentRecipe = value; - OnPropertyChanged(nameof(CurrentRecipe)); + var data = Master.Data.Data; + dataManager = new DataDefaultManager(dataSerializer, data); + Master = new MasterManager(dataManager, recipeManager, userManager); } - } - - //collection de recette de l'application - public RecipeCollection AllRecipes { get; set; } + // Save data. + Debug.Write("[ --SAVE-- ]:\t"); + Master.Data.SaveData(); + Debug.WriteLine("Done."); - public App() - { - CurrentUser = MasterMgr.CurrentConnectedUser; - AllRecipes = MasterMgr.DataMgr.GetRecipes("All recipes"); - CurrentRecipe = MasterMgr.DataMgr.GetRecipes().First(); InitializeComponent(); UserAppTheme = AppTheme.Light; MainPage = new Home(); //MainPage = new MyPosts(); } - + + protected override void OnSleep() + { + // Save data. + Debug.Write("[ --SAVE-- ]:\t"); + Master.Data.SaveData(); + + Debug.WriteLine("Done."); + Debug.WriteLine(FileSystem.Current.AppDataDirectory); + + base.OnSleep(); + } } } diff --git a/MCTG/Views/AddRecipe.xaml b/MCTG/Views/ContentPages/AddRecipe.xaml similarity index 100% rename from MCTG/Views/AddRecipe.xaml rename to MCTG/Views/ContentPages/AddRecipe.xaml diff --git a/MCTG/Views/AddRecipe.xaml.cs b/MCTG/Views/ContentPages/AddRecipe.xaml.cs similarity index 79% rename from MCTG/Views/AddRecipe.xaml.cs rename to MCTG/Views/ContentPages/AddRecipe.xaml.cs index 96cb1bf..2a8af24 100644 --- a/MCTG/Views/AddRecipe.xaml.cs +++ b/MCTG/Views/ContentPages/AddRecipe.xaml.cs @@ -1,5 +1,3 @@ -using CommunityToolkit.Maui.Behaviors; -using DataPersistence; using Model; using Model.Managers; using System.Diagnostics; @@ -9,7 +7,8 @@ namespace Views { public partial class AddRecipe : ContentPage { - public MasterManager MasterMgr => (App.Current as App).MasterMgr; + public MasterManager Master => (Application.Current as App).Master; + public List UnitList { get; set; } = new List { Unit.unit, Unit.kG, Unit.mG, Unit.G, Unit.L, Unit.cL, Unit.mL }; public bool IsCaptureSupported => throw new NotImplementedException(); diff --git a/MCTG/Views/Home.xaml b/MCTG/Views/ContentPages/Home.xaml similarity index 85% rename from MCTG/Views/Home.xaml rename to MCTG/Views/ContentPages/Home.xaml index 7326778..38b81e3 100644 --- a/MCTG/Views/Home.xaml +++ b/MCTG/Views/ContentPages/Home.xaml @@ -1,89 +1,89 @@ - - - - - - - - - -