From 3cdf630af27e7d1b5d91ee120d1e875422d9ee5b Mon Sep 17 00:00:00 2001 From: Elias Jansson Date: Tue, 29 Apr 2025 21:20:06 +0200 Subject: [PATCH] Menu stuff --- Aberwyn/Aberwyn.csproj | 7 +- Aberwyn/Aberwyn_Veckomeny_v1.zip | Bin 0 -> 792 bytes Aberwyn/Controllers/FoodMenuController.cs | 143 +++++++++++ Aberwyn/Controllers/HomeController.cs | 2 + Aberwyn/Controllers/MealController.cs | 69 +++++ Aberwyn/Data/MenuService.cs | 239 ++++++++++++++++-- Aberwyn/Dockerfile | 38 +-- Aberwyn/Models/MenuViewModel.cs | 8 +- Aberwyn/Models/User.cs | 10 + Aberwyn/Models/WeeklyMenuViewModel.cs | 30 +++ Aberwyn/Program.cs | 11 + Aberwyn/Views/FoodMenu/MealAdmin.cshtml | 45 ++++ Aberwyn/Views/FoodMenu/Veckomeny.cshtml | 93 +++++++ Aberwyn/Views/Home/Index.cshtml | 134 +--------- Aberwyn/Views/Home/Menu.cshtml | 62 +---- Aberwyn/Views/Meal/Meal.cshtml | 77 ++++++ Aberwyn/Views/Meal/View.cshtml | 90 +++++++ Aberwyn/Views/Shared/_Layout.cshtml | 18 +- Aberwyn/appsettings.json | 2 +- Aberwyn/wwwroot/css/menu-style.css | 15 ++ Aberwyn/wwwroot/css/site.css | 1 + Aberwyn/wwwroot/js/meal-menu.js | 10 + Aberwyn/wwwroot/js/menu.js | 6 +- .../fc8aff8c-4bef-45a8-9e40-7b78f76f04e8.png | Bin 0 -> 31208 bytes 24 files changed, 880 insertions(+), 230 deletions(-) create mode 100644 Aberwyn/Aberwyn_Veckomeny_v1.zip create mode 100644 Aberwyn/Controllers/FoodMenuController.cs create mode 100644 Aberwyn/Controllers/MealController.cs create mode 100644 Aberwyn/Models/User.cs create mode 100644 Aberwyn/Models/WeeklyMenuViewModel.cs create mode 100644 Aberwyn/Views/FoodMenu/MealAdmin.cshtml create mode 100644 Aberwyn/Views/FoodMenu/Veckomeny.cshtml create mode 100644 Aberwyn/Views/Meal/Meal.cshtml create mode 100644 Aberwyn/Views/Meal/View.cshtml create mode 100644 Aberwyn/wwwroot/css/menu-style.css create mode 100644 Aberwyn/wwwroot/js/meal-menu.js create mode 100644 Aberwyn/wwwroot/uploads/fc8aff8c-4bef-45a8-9e40-7b78f76f04e8.png diff --git a/Aberwyn/Aberwyn.csproj b/Aberwyn/Aberwyn.csproj index 348d660..08e1112 100644 --- a/Aberwyn/Aberwyn.csproj +++ b/Aberwyn/Aberwyn.csproj @@ -3,12 +3,12 @@ net6.0 enable + false enable d748f28f-7908-4174-b2a1-c21d4d06499f Linux - @@ -29,4 +29,9 @@ + + + + + diff --git a/Aberwyn/Aberwyn_Veckomeny_v1.zip b/Aberwyn/Aberwyn_Veckomeny_v1.zip new file mode 100644 index 0000000000000000000000000000000000000000..7dac77e3c09b814ac8047339e7e924847f59a6b1 GIT binary patch literal 792 zcmaix!AiqG5QbCRdKRo&1aa%Ju-z9BK`I^^3ei9Xdn)N-YByQPCWZ(e`T_-g06{4D zK%Ttn8+h>K!I=bHyBgi_vop)=mzn(?yIN~lxZY20&)@Ew4_gqUTA;2B<46j@BgKxT z9QS#c=(CQe7$Z6-o*Z-H^N2f3)FbLYZvqXq1L{MWQ0$EJsmKsHnD8_|EQnue8Sv@u zW!MA|Y8QkTbc1Zby{Qa%m;ug@1F_$tl$?&PWkjx^GVhR3da&GOZEyW%4PYpoKTXp} z%9vd$7Vwdv08BcNFw%30kQWp0mr;lAYi|QCqqe_6JcX1np(@SgDmS47GlkC literal 0 HcmV?d00001 diff --git a/Aberwyn/Controllers/FoodMenuController.cs b/Aberwyn/Controllers/FoodMenuController.cs new file mode 100644 index 0000000..663b057 --- /dev/null +++ b/Aberwyn/Controllers/FoodMenuController.cs @@ -0,0 +1,143 @@ +using Microsoft.AspNetCore.Mvc; +using Aberwyn.Models; +using Aberwyn.Data; +using System.Globalization; + +namespace Aberwyn.Controllers +{ + public class FoodMenuController : Controller + { + private readonly IConfiguration _configuration; + private readonly IHostEnvironment _env; + + public FoodMenuController(IConfiguration configuration, IHostEnvironment env) + { + _configuration = configuration; + _env = env; + } + + public IActionResult Veckomeny(int week = -1) + { + var menuService = new MenuService(_configuration, _env); + int currentWeek = week == -1 ? ISOWeek.GetWeekOfYear(DateTime.Now) : week; + int currentYear = DateTime.Now.Year; + + // Hämta meny frćn DB + var menus = menuService.GetWeeklyMenu(currentWeek, currentYear); + + var model = new WeeklyMenuViewModel + { + WeekNumber = currentWeek, + Year = currentYear, + WeeklyMenus = menus, + AvailableCooks = menuService.GetUsers() + }; + + return View(model); + } + + public IActionResult MealAdmin() + { + var service = new MenuService(_configuration, _env); + var meals = service.GetMealsDetailed(); // Se till att denna metod finns och returnerar fullständiga Meal-objekt + return View("MealAdmin", meals); + } + + + + [HttpGet] + public JsonResult SearchMeals(string term) + { + if (string.IsNullOrWhiteSpace(term)) + return Json(new List()); + + var menuService = new MenuService(_configuration, _env); + var meals = menuService.GetMeals(); + + var result = meals + .Where(m => m.Name != null && m.Name.Contains(term, StringComparison.OrdinalIgnoreCase)) + .Select(m => m.Name) + .Distinct() + .Take(10) + .ToList(); + + return Json(result); + } + + + [HttpPost] + public IActionResult SaveVeckomeny(IFormCollection form) + { + var menuService = new MenuService(_configuration, _env); + var allMeals = menuService.GetMeals(); + + var model = new MenuViewModel + { + WeekNumber = ISOWeek.GetWeekOfYear(DateTime.Now), + Year = DateTime.Now.Year, + WeeklyMenus = new List() + }; + + var entriesByDay = new Dictionary(); + + foreach (var key in form.Keys.Where(k => k.StartsWith("Meal[", StringComparison.OrdinalIgnoreCase))) + { + var match = System.Text.RegularExpressions.Regex.Match(key, @"Meal\[(\d+)\]\[(\w+)\]"); + if (match.Success) + { + int day = int.Parse(match.Groups[1].Value); + string mealType = match.Groups[2].Value; + + string mealName = form[key]; + string cook = form[$"Cook[{day}][{mealType}]"]; + + if (string.IsNullOrWhiteSpace(mealName)) continue; + + var meal = allMeals.FirstOrDefault(m => m.Name.Equals(mealName, StringComparison.OrdinalIgnoreCase)); + if (meal == null) + { + meal = new Meal { Name = mealName }; + menuService.SaveMeal(meal); + } + + if (!entriesByDay.TryGetValue(day, out var entry)) + { + entry = new WeeklyMenu + { + WeekNumber = model.WeekNumber, + Year = model.Year + }; + entriesByDay[day] = entry; + } + + entry.DayOfWeek = day + 1; // se till att alltid sätta rätt dag + entry.Cook = cook; + + if (mealType.Equals("Lunch", StringComparison.OrdinalIgnoreCase)) + { + entry.LunchMealId = meal.Id; + entry.LunchMealName = meal.Name; + } + else if (mealType.Equals("Middag", StringComparison.OrdinalIgnoreCase)) + { + entry.DinnerMealId = meal.Id; + entry.DinnerMealName = meal.Name; + } + else if (mealType.Equals("Frukost", StringComparison.OrdinalIgnoreCase)) + { + entry.BreakfastMealId = meal.Id; + entry.BreakfastMealName = meal.Name; + } + } + } + + model.WeeklyMenus = entriesByDay.Values.ToList(); + menuService.UpdateWeeklyMenu(model); + + return RedirectToAction("Veckomeny"); + } + } + + + + } \ No newline at end of file diff --git a/Aberwyn/Controllers/HomeController.cs b/Aberwyn/Controllers/HomeController.cs index 15df38f..dda595f 100644 --- a/Aberwyn/Controllers/HomeController.cs +++ b/Aberwyn/Controllers/HomeController.cs @@ -39,6 +39,8 @@ namespace Aberwyn.Controllers public IActionResult Menu() { + + ViewData["HideSidebar"] = true; // Get the current date and week var currentDate = DateTime.Now; var currentYear = currentDate.Year; diff --git a/Aberwyn/Controllers/MealController.cs b/Aberwyn/Controllers/MealController.cs new file mode 100644 index 0000000..8007f35 --- /dev/null +++ b/Aberwyn/Controllers/MealController.cs @@ -0,0 +1,69 @@ +ï»żusing Microsoft.AspNetCore.Mvc; +using Aberwyn.Models; +using Aberwyn.Data; + +namespace Aberwyn.Controllers +{ + public class MealController : Controller + { + private readonly IConfiguration _configuration; + private readonly IHostEnvironment _env; + + public MealController(IConfiguration configuration, IHostEnvironment env) + { + _configuration = configuration; + _env = env; + } + [HttpGet] + public IActionResult View(int id) + { + var service = new MenuService(_configuration, _env); + var meal = service.GetMealById(id); + if (meal == null) + return NotFound(); + + return View("View", meal); + } + + [HttpGet] + public IActionResult Edit(int? id) + { + var service = new MenuService(_configuration, _env); + var meal = id.HasValue ? service.GetMealById(id.Value) : new Meal(); + return View("Meal", meal); + } + + [HttpPost] + public async Task SaveMeal(Meal meal, IFormFile ImageFile) + { + var service = new MenuService(_configuration, _env); + + if (ImageFile != null && ImageFile.Length > 0) + { + var uploadsFolder = Path.Combine(_env.ContentRootPath, "wwwroot/uploads"); + Directory.CreateDirectory(uploadsFolder); // skapa om saknas + var uniqueFileName = Guid.NewGuid().ToString() + Path.GetExtension(ImageFile.FileName); + var filePath = Path.Combine(uploadsFolder, uniqueFileName); + + using (var fileStream = new FileStream(filePath, FileMode.Create)) + { + await ImageFile.CopyToAsync(fileStream); + } + + meal.ImageUrl = "/uploads/" + uniqueFileName; + } + + service.SaveOrUpdateMeal(meal); + return RedirectToAction("Edit", new { id = meal.Id }); + } + + + [HttpPost] + public IActionResult DeleteMeal(int id) + { + var service = new MenuService(_configuration, _env); + //service.DeleteMeal(id); + return RedirectToAction("Edit"); // eller tillbaka till lista + } + } +} diff --git a/Aberwyn/Data/MenuService.cs b/Aberwyn/Data/MenuService.cs index dc0ad90..e3c923c 100644 --- a/Aberwyn/Data/MenuService.cs +++ b/Aberwyn/Data/MenuService.cs @@ -25,6 +25,65 @@ namespace Aberwyn.Data return new MySqlConnection(connectionString); } + public List GetUsers() + { + var users = new List(); + using (var connection = GetConnection()) + { + connection.Open(); + string query = @"SELECT userID, Username, Name FROM Nevyn.tblUsers WHERE Chef = 1"; + + using (var cmd = new MySqlCommand(query, connection)) + using (var reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + users.Add(new User + { + UserID = reader.GetInt32("userID"), + Username = reader.GetString("Username"), + Name = reader.GetString("Name") + }); + } + } + } + return users; + } + + public void SaveMeal(Meal meal) + { + if (string.IsNullOrWhiteSpace(meal?.Name)) + return; + + meal.Name = meal.Name.Trim(); + + using var connection = GetConnection(); + connection.Open(); + + var query = @" + INSERT INTO Meals + (Name, Description, RecipeUrl, CreatedAt) + VALUES (@name, @desc, @url, CURRENT_TIMESTAMP);"; + + using var cmd = new MySqlCommand(query, connection); + cmd.Parameters.AddWithValue("@name", meal.Name); + cmd.Parameters.AddWithValue("@desc", meal.Description ?? ""); + cmd.Parameters.AddWithValue("@url", meal.RecipeUrl ?? ""); + + try + { + cmd.ExecuteNonQuery(); + meal.Id = (int)cmd.LastInsertedId; + } + catch (MySqlException ex) when (ex.Number == 1062) + { + Console.WriteLine($"⚠ MĂ„ltiden '{meal.Name}' finns redan i databasen."); + } + catch (Exception ex) + { + Console.WriteLine($"❌ Fel vid sparning av mĂ„ltid '{meal.Name}': {ex.Message}"); + } + } public List GetWeeklyMenu(int weekNumber, int year) { @@ -33,12 +92,16 @@ namespace Aberwyn.Data { connection.Open(); string query = @" - SELECT wm.Id, wm.DayOfWeek, wm.DinnerMealId, wm.LunchMealId, wm.WeekNumber, wm.Year, - dm.Name AS DinnerMealName, lm.Name AS LunchMealName - FROM WeeklyMenu wm - LEFT JOIN Meals dm ON wm.DinnerMealId = dm.Id - LEFT JOIN Meals lm ON wm.LunchMealId = lm.Id - WHERE wm.WeekNumber = @weekNumber AND wm.Year = @year"; + SELECT wm.Id, wm.DayOfWeek, wm.DinnerMealId, wm.LunchMealId, wm.BreakfastMealId, + wm.WeekNumber, wm.Year, wm.Cook, + dm.Name AS DinnerMealName, + lm.Name AS LunchMealName, + bm.Name AS BreakfastMealName + FROM WeeklyMenu wm + LEFT JOIN Meals dm ON wm.DinnerMealId = dm.Id + LEFT JOIN Meals lm ON wm.LunchMealId = lm.Id + LEFT JOIN Meals bm ON wm.BreakfastMealId = bm.Id + WHERE wm.WeekNumber = @weekNumber AND wm.Year = @year"; using (var cmd = new MySqlCommand(query, connection)) { @@ -53,13 +116,15 @@ namespace Aberwyn.Data { Id = reader.GetInt32("Id"), DayOfWeek = reader.GetInt32("DayOfWeek"), - DinnerMealId = reader.IsDBNull(reader.GetOrdinal("DinnerMealId")) ? (int?)null : reader.GetInt32("DinnerMealId"), - LunchMealId = reader.IsDBNull(reader.GetOrdinal("LunchMealId")) ? (int?)null : reader.GetInt32("LunchMealId"), + DinnerMealId = reader.IsDBNull(reader.GetOrdinal("DinnerMealId")) ? (int?)null : reader.GetInt32(reader.GetOrdinal("DinnerMealId")), + LunchMealId = reader.IsDBNull(reader.GetOrdinal("LunchMealId")) ? (int?)null : reader.GetInt32(reader.GetOrdinal("LunchMealId")), + BreakfastMealId = reader.IsDBNull(reader.GetOrdinal("BreakfastMealId")) ? (int?)null : reader.GetInt32(reader.GetOrdinal("BreakfastMealId")), WeekNumber = reader.GetInt32("WeekNumber"), Year = reader.GetInt32("Year"), - DinnerMealName = reader.IsDBNull(reader.GetOrdinal("DinnerMealName")) ? null : reader.GetString("DinnerMealName"), - LunchMealName = reader.IsDBNull(reader.GetOrdinal("LunchMealName")) ? null : reader.GetString("LunchMealName"), - + Cook = reader.IsDBNull(reader.GetOrdinal("Cook")) ? null : reader.GetString(reader.GetOrdinal("Cook")), + DinnerMealName = reader.IsDBNull(reader.GetOrdinal("DinnerMealName")) ? null : reader.GetString(reader.GetOrdinal("DinnerMealName")), + LunchMealName = reader.IsDBNull(reader.GetOrdinal("LunchMealName")) ? null : reader.GetString(reader.GetOrdinal("LunchMealName")), + BreakfastMealName = reader.IsDBNull(reader.GetOrdinal("BreakfastMealName")) ? null : reader.GetString(reader.GetOrdinal("BreakfastMealName")) }); } } @@ -68,6 +133,7 @@ namespace Aberwyn.Data return weeklyMenu; } + public List GetMeals() { var meals = new List(); @@ -92,6 +158,119 @@ namespace Aberwyn.Data } return meals; } + public List GetMealsDetailed() + { + var meals = new List(); + + using (var connection = GetConnection()) + { + connection.Open(); + string query = @" + SELECT Id, Name, Description, ProteinType, CarbType, RecipeUrl, CreatedAt + FROM Meals + ORDER BY CreatedAt DESC"; + + using (var cmd = new MySqlCommand(query, connection)) + using (var reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + meals.Add(new Meal + { + Id = reader.GetInt32(reader.GetOrdinal("Id")), + Name = reader.GetString(reader.GetOrdinal("Name")), + Description = reader.IsDBNull(reader.GetOrdinal("Description")) ? null : reader.GetString(reader.GetOrdinal("Description")), + ProteinType = reader.IsDBNull(reader.GetOrdinal("ProteinType")) ? null : reader.GetString(reader.GetOrdinal("ProteinType")), + CarbType = reader.IsDBNull(reader.GetOrdinal("CarbType")) ? null : reader.GetString(reader.GetOrdinal("CarbType")), + RecipeUrl = reader.IsDBNull(reader.GetOrdinal("RecipeUrl")) ? null : reader.GetString(reader.GetOrdinal("RecipeUrl")), + CreatedAt = reader.GetDateTime(reader.GetOrdinal("CreatedAt")) + }); + } + } + } + + return meals; + } + + public Meal GetMealById(int id) + { + using var connection = GetConnection(); + connection.Open(); + + string query = @"SELECT Id, Name, Description, ProteinType, CarbType, RecipeUrl, CreatedAt + FROM Meals WHERE Id = @id"; + + using var cmd = new MySqlCommand(query, connection); + cmd.Parameters.AddWithValue("@id", id); + + using var reader = cmd.ExecuteReader(); + if (reader.Read()) + { + return new Meal + { + Id = reader.GetInt32(reader.GetOrdinal("Id")), + Name = reader.GetString(reader.GetOrdinal("Name")), + Description = reader.IsDBNull(reader.GetOrdinal("Description")) ? null : reader.GetString(reader.GetOrdinal("Description")), + ProteinType = reader.IsDBNull(reader.GetOrdinal("ProteinType")) ? null : reader.GetString(reader.GetOrdinal("ProteinType")), + CarbType = reader.IsDBNull(reader.GetOrdinal("CarbType")) ? null : reader.GetString(reader.GetOrdinal("CarbType")), + RecipeUrl = reader.IsDBNull(reader.GetOrdinal("RecipeUrl")) ? null : reader.GetString(reader.GetOrdinal("RecipeUrl")), + CreatedAt = reader.GetDateTime(reader.GetOrdinal("CreatedAt")) + }; + + } + + return null; + } + + public void DeleteMeal(int id) + { + using var connection = GetConnection(); + connection.Open(); + string query = "DELETE FROM Meals WHERE Id = @id"; + using var cmd = new MySqlCommand(query, connection); + cmd.Parameters.AddWithValue("@id", id); + cmd.ExecuteNonQuery(); + } + + public void SaveOrUpdateMeal(Meal meal) + { + using var connection = GetConnection(); + connection.Open(); + + string query; + bool isNew = meal.Id == 0; + + if (isNew) + { + query = @"INSERT INTO Meals (Name, Description, ProteinType, CarbType, RecipeUrl, CreatedAt) + VALUES (@name, @desc, @protein, @carb, @url, @created)"; + } + else + { + query = @"UPDATE Meals + SET Name = @name, Description = @desc, ProteinType = @protein, CarbType = @carb, RecipeUrl = @url + WHERE Id = @id"; + } + + using var cmd = new MySqlCommand(query, connection); + cmd.Parameters.AddWithValue("@name", meal.Name); + cmd.Parameters.AddWithValue("@desc", meal.Description ?? ""); + cmd.Parameters.AddWithValue("@protein", meal.ProteinType ?? ""); + cmd.Parameters.AddWithValue("@carb", meal.CarbType ?? ""); + cmd.Parameters.AddWithValue("@url", meal.RecipeUrl ?? ""); + + if (isNew) + { + cmd.Parameters.AddWithValue("@created", meal.CreatedAt == default ? DateTime.Now : meal.CreatedAt); + } + else + { + cmd.Parameters.AddWithValue("@id", meal.Id); + } + + cmd.ExecuteNonQuery(); + } + public void UpdateWeeklyMenu(MenuViewModel menuData) { @@ -105,33 +284,50 @@ namespace Aberwyn.Data { try { - // Loop over each WeeklyMenu in the WeeklyMenus list - foreach (var weeklyMenu in menuData.WeeklyMenus) + foreach (var menu in menuData.WeeklyMenus) { - int dayOfWeek = weeklyMenu.DayOfWeek; - var menu = weeklyMenu; + int dayOfWeek = menu.DayOfWeek; - // SQL query to update the existing records - string query = @" + string updateQuery = @" UPDATE WeeklyMenu SET - DinnerMealId = @dinnerMealId, + BreakfastMealId = @breakfastMealId, LunchMealId = @lunchMealId, + DinnerMealId = @dinnerMealId, Cook = @cook WHERE DayOfWeek = @dayOfWeek AND WeekNumber = @weekNumber AND Year = @year;"; - using (var cmd = new MySqlCommand(query, connection, transaction)) + using (var cmd = new MySqlCommand(updateQuery, connection, transaction)) { cmd.Parameters.AddWithValue("@dayOfWeek", dayOfWeek); - cmd.Parameters.AddWithValue("@dinnerMealId", menu.DinnerMealId ?? (object)DBNull.Value); + cmd.Parameters.AddWithValue("@breakfastMealId", menu.BreakfastMealId ?? (object)DBNull.Value); cmd.Parameters.AddWithValue("@lunchMealId", menu.LunchMealId ?? (object)DBNull.Value); + cmd.Parameters.AddWithValue("@dinnerMealId", menu.DinnerMealId ?? (object)DBNull.Value); cmd.Parameters.AddWithValue("@weekNumber", menu.WeekNumber); cmd.Parameters.AddWithValue("@year", menu.Year); cmd.Parameters.AddWithValue("@cook", menu.Cook ?? (object)DBNull.Value); - cmd.ExecuteNonQuery(); + int rowsAffected = cmd.ExecuteNonQuery(); + + if (rowsAffected == 0) + { + string insertQuery = @" + INSERT INTO WeeklyMenu + (DayOfWeek, WeekNumber, Year, BreakfastMealId, LunchMealId, DinnerMealId, Cook) + VALUES (@dayOfWeek, @weekNumber, @year, @breakfastMealId, @lunchMealId, @dinnerMealId, @cook);"; + + using var insertCmd = new MySqlCommand(insertQuery, connection, transaction); + insertCmd.Parameters.AddWithValue("@dayOfWeek", dayOfWeek); + insertCmd.Parameters.AddWithValue("@breakfastMealId", menu.BreakfastMealId ?? (object)DBNull.Value); + insertCmd.Parameters.AddWithValue("@lunchMealId", menu.LunchMealId ?? (object)DBNull.Value); + insertCmd.Parameters.AddWithValue("@dinnerMealId", menu.DinnerMealId ?? (object)DBNull.Value); + insertCmd.Parameters.AddWithValue("@weekNumber", menu.WeekNumber); + insertCmd.Parameters.AddWithValue("@year", menu.Year); + insertCmd.Parameters.AddWithValue("@cook", menu.Cook ?? (object)DBNull.Value); + insertCmd.ExecuteNonQuery(); + } } } @@ -147,6 +343,7 @@ namespace Aberwyn.Data } } + public int AddMeal(Meal meal) { using (var connection = GetConnection()) diff --git a/Aberwyn/Dockerfile b/Aberwyn/Dockerfile index d944d2d..defcf32 100644 --- a/Aberwyn/Dockerfile +++ b/Aberwyn/Dockerfile @@ -1,40 +1,22 @@ -# Base image for runtime +#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. + FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base WORKDIR /app +EXPOSE 80 +EXPOSE 443 -RUN mkdir -p /app/build && chmod -R 777 /app/build - -# Leave the ports exposed to allow Unraid to configure them -EXPOSE 80 443 - -# Image for building the project FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build WORKDIR /src - -# Copy the .csproj and restore dependencies -COPY Aberwyn/Aberwyn.csproj ./ -RUN dotnet restore - -#COPY Aberwyn/Aberwyn.csproj ./Aberwyn/ -#WORKDIR /src/Aberwyn -#RUN dotnet restore - - -# Copy everything else and build the app +COPY ["Aberwyn/Aberwyn.csproj", "Aberwyn/"] +RUN dotnet restore "Aberwyn/Aberwyn.csproj" COPY . . -RUN dotnet build Aberwyn.csproj -c Release -o /app/build/ +WORKDIR "/src/Aberwyn" +RUN dotnet build "Aberwyn.csproj" -c Release -o /app/build -# Publish the app to the /app/publish folder FROM build AS publish -RUN dotnet publish Aberwyn.csproj -c Release -o /app/publish +RUN dotnet publish "Aberwyn.csproj" -c Release -o /app/publish -# Use the base runtime image to run the app FROM base AS final WORKDIR /app COPY --from=publish /app/publish . - -# Use environment variable for ports to allow flexibility -ENV ASPNETCORE_URLS="http://+:80" - -# Set entry point to start the application -ENTRYPOINT ["dotnet", "Aberwyn.dll"] +ENTRYPOINT ["dotnet", "Aberwyn.dll"] \ No newline at end of file diff --git a/Aberwyn/Models/MenuViewModel.cs b/Aberwyn/Models/MenuViewModel.cs index c911363..525edce 100644 --- a/Aberwyn/Models/MenuViewModel.cs +++ b/Aberwyn/Models/MenuViewModel.cs @@ -15,15 +15,18 @@ namespace Aberwyn.Models { public int Id { get; set; } public int DayOfWeek { get; set; } - public int? DinnerMealId { get; set; } + public int? BreakfastMealId { get; set; } // 👈 LĂ€gg till denna public int? LunchMealId { get; set; } + public int? DinnerMealId { get; set; } public string Cook { get; set; } public int WeekNumber { get; set; } public int Year { get; set; } - public string DinnerMealName { get; set; } + public string BreakfastMealName { get; set; } // 👈 Och denna public string LunchMealName { get; set; } + public string DinnerMealName { get; set; } } + public class Meal { public int Id { get; set; } @@ -32,6 +35,7 @@ namespace Aberwyn.Models public string ProteinType { get; set; } public string CarbType { get; set; } public string RecipeUrl { get; set; } + public string ImageUrl { get; set; } public DateTime CreatedAt { get; set; } } } diff --git a/Aberwyn/Models/User.cs b/Aberwyn/Models/User.cs new file mode 100644 index 0000000..c00ed42 --- /dev/null +++ b/Aberwyn/Models/User.cs @@ -0,0 +1,10 @@ +ï»żnamespace Aberwyn.Models +{ + public class User + { + public int UserID { get; set; } + public string Username { get; set; } + public string Name { get; set; } + } + +} diff --git a/Aberwyn/Models/WeeklyMenuViewModel.cs b/Aberwyn/Models/WeeklyMenuViewModel.cs new file mode 100644 index 0000000..6f7b2d8 --- /dev/null +++ b/Aberwyn/Models/WeeklyMenuViewModel.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; + +namespace Aberwyn.Models +{ + public class WeeklyMenuViewModel + { + public int WeekNumber { get; set; } + public int Year { get; set; } + + public List WeeklyMenus { get; set; } = new(); + public List AvailableCooks { get; set; } = new(); + + public WeeklyMenu GetMealEntry(int day, string type) + { + int dayOfWeek = day + 1; + + return WeeklyMenus.FirstOrDefault(m => + m.DayOfWeek == dayOfWeek && + ( + (type == "Frukost" && m.BreakfastMealId.HasValue) || + (type == "Lunch" && m.LunchMealId.HasValue) || + (type == "Middag" && m.DinnerMealId.HasValue) + ) + ); + } + } +} diff --git a/Aberwyn/Program.cs b/Aberwyn/Program.cs index b9e26af..82a20e6 100644 --- a/Aberwyn/Program.cs +++ b/Aberwyn/Program.cs @@ -1,6 +1,9 @@ using Microsoft.EntityFrameworkCore; // Add this for DbContext using Aberwyn.Data; // Include your data namespace using MySql.EntityFrameworkCore.Extensions; // For MySQL support +using System.Text; +using System.Globalization; +using Microsoft.AspNetCore.Localization; var builder = WebApplication.CreateBuilder(args); @@ -20,6 +23,14 @@ builder.Services.AddScoped(); builder.Services.AddScoped(); +Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); +builder.Services.Configure(options => +{ + var supportedCultures = new[] { new CultureInfo("sv-SE") }; + options.DefaultRequestCulture = new RequestCulture("sv-SE"); + options.SupportedCultures = supportedCultures; + options.SupportedUICultures = supportedCultures; +}); var app = builder.Build(); // Configure the HTTP request pipeline. diff --git a/Aberwyn/Views/FoodMenu/MealAdmin.cshtml b/Aberwyn/Views/FoodMenu/MealAdmin.cshtml new file mode 100644 index 0000000..838ee38 --- /dev/null +++ b/Aberwyn/Views/FoodMenu/MealAdmin.cshtml @@ -0,0 +1,45 @@ +ï»ż@model List +@{ + ViewData["Title"] = "Alla mĂ„ltider"; +} + +

Alla mÄltider

+ + + + + + + + + + + + + + + @foreach (var meal in Model) + { + + + + + + + + + + } + +
NamnBeskrivningProteinKolhydratReceptlÀnkSkapadRedigera
+ @meal.Name + @meal.Description@meal.ProteinType@meal.CarbType + @if (!string.IsNullOrWhiteSpace(meal.RecipeUrl)) + { + LĂ€nk + } + @meal.CreatedAt.ToString("yyyy-MM-dd") + Redigera +
+ +LÀgg till ny mÄltid diff --git a/Aberwyn/Views/FoodMenu/Veckomeny.cshtml b/Aberwyn/Views/FoodMenu/Veckomeny.cshtml new file mode 100644 index 0000000..975068a --- /dev/null +++ b/Aberwyn/Views/FoodMenu/Veckomeny.cshtml @@ -0,0 +1,93 @@ +@model WeeklyMenuViewModel +@{ + ViewData["Title"] = "Veckomeny"; + var days = new[] { "MÄndag", "Tisdag", "Onsdag", "Torsdag", "Fredag", "Lördag", "Söndag" }; + var mealTypes = new[] { "Frukost", "Lunch", "Middag" }; +} + +

Veckomeny - Vecka @Model.WeekNumber

+ + + +
+ + + + + @foreach (var mealType in mealTypes) + { + + } + + + + @for (int i = 0; i < 7; i++) + { + + + @foreach (var mealType in mealTypes) + { + var entry = Model.GetMealEntry(i, mealType); + var mealName = mealType switch + { + "Frukost" => entry?.BreakfastMealName, + "Lunch" => entry?.LunchMealName, + "Middag" => entry?.DinnerMealName, + _ => null + }; + + } + + } + + + +
+ + + +@section Scripts { + + +} diff --git a/Aberwyn/Views/Home/Index.cshtml b/Aberwyn/Views/Home/Index.cshtml index 88c8ce4..905ed9a 100644 --- a/Aberwyn/Views/Home/Index.cshtml +++ b/Aberwyn/Views/Home/Index.cshtml @@ -1,127 +1,21 @@ ï»ż@{ - ViewData["Title"] = "Home Page"; + ViewData["Title"] = "Welcome to Aberwyn!"; } -
- - - + + - - - - - + + + @ViewData["Title"] + - - - - - - - -
- - - - - -
- -
- - - - - - - - - - - - - -

- - - -
- - - - - - - - - -
- + +
+

Welcome to Aberwyn!

+

Your home for managing all your budget and meal planning needs.

+

We’re glad to have you here. Get started by exploring our features.

+ Learn More +
- -
diff --git a/Aberwyn/Views/Home/Menu.cshtml b/Aberwyn/Views/Home/Menu.cshtml index b8e87a0..ef2c6c1 100644 --- a/Aberwyn/Views/Home/Menu.cshtml +++ b/Aberwyn/Views/Home/Menu.cshtml @@ -1,7 +1,9 @@ ï»ż@model Aberwyn.Models.MenuViewModel @{ - Layout = "_Layout"; + // Define a flag to hide the sidebar if the URL path matches '/nosidebar' (relative part after the controller) + bool hideSidebar = Context.Request.Path.Value.EndsWith("/nosidebar", StringComparison.OrdinalIgnoreCase); + Layout = hideSidebar ? null : "_Layout"; // No layout if the path ends with '/nosidebar' } @@ -20,14 +22,9 @@

Meal Menu Overview

- - Week {{ selectedWeek }} - {{ selectedYear }} - -
- -
- - + + Vecka {{ selectedWeek }} - {{ selectedYear }} +
@@ -37,62 +34,21 @@
- Breakfast: {{ menu[day].breakfastMealName }} + Frukost: {{ menu[day].breakfastMealName }}
Lunch: {{ menu[day].lunchMealName }}
- Dinner: {{ menu[day].dinnerMealName }} + Middag: {{ menu[day].dinnerMealName }}
- Not Assigned -
-
- -
-
- Breakfast: - -
-
- Lunch: - -
-
- Dinner: - + Inte bestÀmd
- - - -
-

Add a New Meal

- - - - - - - -
- diff --git a/Aberwyn/Views/Meal/Meal.cshtml b/Aberwyn/Views/Meal/Meal.cshtml new file mode 100644 index 0000000..2dac870 --- /dev/null +++ b/Aberwyn/Views/Meal/Meal.cshtml @@ -0,0 +1,77 @@ +ï»ż@model Aberwyn.Models.Meal +@{ + ViewData["Title"] = Model.Id == 0 ? "Ny mĂ„ltid" : Model.Name; + var isNew = Model.Id == 0; +} + +
+

@(isNew ? "Skapa ny mÄltid" : Model.Name)

+ +
+ @if (!isNew) + { + + } + +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ + +
+ +
+ + diff --git a/Aberwyn/Views/Meal/View.cshtml b/Aberwyn/Views/Meal/View.cshtml new file mode 100644 index 0000000..a2230f5 --- /dev/null +++ b/Aberwyn/Views/Meal/View.cshtml @@ -0,0 +1,90 @@ +ï»ż@model Aberwyn.Models.Meal +@{ + ViewData["Title"] = Model.Name; +} + +
+

@Model.Name

+ + @if (!string.IsNullOrEmpty(Model.ImageUrl)) + { + @Model.Name + } + + @if (!string.IsNullOrEmpty(Model.Description)) + { +

@Model.Description

+ } + +
+ + Redigera +
+ + +
+ + + + diff --git a/Aberwyn/Views/Shared/_Layout.cshtml b/Aberwyn/Views/Shared/_Layout.cshtml index b9f27a0..f9e78a4 100644 --- a/Aberwyn/Views/Shared/_Layout.cshtml +++ b/Aberwyn/Views/Shared/_Layout.cshtml @@ -37,17 +37,30 @@ RealEstate + + + + - +
@RenderBody() + @RenderSection("Scripts", required: false)
@@ -57,5 +70,6 @@ + diff --git a/Aberwyn/appsettings.json b/Aberwyn/appsettings.json index c97db5f..f93bc0e 100644 --- a/Aberwyn/appsettings.json +++ b/Aberwyn/appsettings.json @@ -8,6 +8,6 @@ "AllowedHosts": "*", "ConnectionStrings": { "DefaultConnection": "Server=192.168.1.108;Database=Nevyn;Uid=root;Pwd=3edc4RFV;", - "ProductionConnection": "Server=172.18.0.2;Database=Nevyn;Uid=root;Pwd=3edc4RFV;" + "ProductionConnection": "Server=192.168.1.108;Database=Nevyn;Uid=root;Pwd=3edc4RFV;" } } diff --git a/Aberwyn/wwwroot/css/menu-style.css b/Aberwyn/wwwroot/css/menu-style.css new file mode 100644 index 0000000..f6ecbb3 --- /dev/null +++ b/Aberwyn/wwwroot/css/menu-style.css @@ -0,0 +1,15 @@ + +.menu-table { + width: 100%; + border-collapse: collapse; + margin-top: 1em; +} +.menu-table th, .menu-table td { + border: 1px solid #444; + padding: 0.5em; +} +.week-nav { + margin-bottom: 1em; + display: flex; + gap: 1em; +} diff --git a/Aberwyn/wwwroot/css/site.css b/Aberwyn/wwwroot/css/site.css index de35bab..eb6325b 100644 --- a/Aberwyn/wwwroot/css/site.css +++ b/Aberwyn/wwwroot/css/site.css @@ -224,3 +224,4 @@ body { .date-picker-dropdown select:hover { background-color: #555; } + diff --git a/Aberwyn/wwwroot/js/meal-menu.js b/Aberwyn/wwwroot/js/meal-menu.js new file mode 100644 index 0000000..1b686ec --- /dev/null +++ b/Aberwyn/wwwroot/js/meal-menu.js @@ -0,0 +1,10 @@ + +document.addEventListener("DOMContentLoaded", function () { + document.querySelectorAll(".meal-cell").forEach(cell => { + cell.addEventListener("click", function () { + const name = this.getAttribute("data-name"); + const cook = this.getAttribute("data-cook"); + alert(`MĂ„ltid: ${name}\nLagas av: ${cook}`); + }); + }); +}); diff --git a/Aberwyn/wwwroot/js/menu.js b/Aberwyn/wwwroot/js/menu.js index 93f48e5..f355bb2 100644 --- a/Aberwyn/wwwroot/js/menu.js +++ b/Aberwyn/wwwroot/js/menu.js @@ -1,4 +1,4 @@ -angular.module('mealMenuApp', []) +ï»żangular.module('mealMenuApp', []) .controller('MealMenuController', function ($scope, $http) { $scope.isEditing = false; $scope.toggleEditMode = function () { @@ -15,7 +15,7 @@ angular.module('mealMenuApp', []) const today = new Date(); $scope.selectedWeek = getWeek(today); $scope.selectedYear = today.getFullYear(); - $scope.daysOfWeek = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]; + $scope.daysOfWeek = ["MĂ„ndag", "Tisdag", "Onsdag", "Torsdag", "Fredag", "Lördag", "Söndag"]; $scope.loadMeals = function () { $http.get('/api/mealMenuApi/getMeals') @@ -29,6 +29,8 @@ angular.module('mealMenuApp', []) $http.get('/api/mealMenuApi/menu', { params: { weekNumber: $scope.selectedWeek, year: $scope.selectedYear } }).then(response => { + console.log("Veckomenydata:", response.data); // ✅ logga ut + $scope.menu = {}; response.data.forEach(item => { const dayOfWeek = $scope.daysOfWeek[item.dayOfWeek - 1]; diff --git a/Aberwyn/wwwroot/uploads/fc8aff8c-4bef-45a8-9e40-7b78f76f04e8.png b/Aberwyn/wwwroot/uploads/fc8aff8c-4bef-45a8-9e40-7b78f76f04e8.png new file mode 100644 index 0000000000000000000000000000000000000000..69bf922f069bd9852200ea66eb34fbcaeb2b819f GIT binary patch literal 31208 zcmV)bK&iipP)006TH1^@s64viNW000}LdQ@0+Qek%> zaB^>EX>4U6ba`-PAZ2)IW&i+q+O3*db|g7+MgOr1EdetO%fWb#cA(|!J}`3Z>Q+{_ z$SMXI>ERBy;SC0N=YRhDy8q&=dsU;!uJ!uucRyM_x)Mq=R_%fy&pb* zyZ-sWs6XA#=l{4qr~RD#_rG%^7`t%36kR+iB>(mE>q?1Lq@D(^g`Xis9mwnZ{r&ul z_&NP`@?Qt=`)|L+&wpE4`VkKI71_@L^|v^E-Vf!s_tW>w#9vkv;`b}ZUsjYt_dkFA z6u*17-Fv?7Zsh6+SN+`7w_AC?ao{AJ>3yv7-T1%odwbuVZ= z{-wkwGFV=ijT;<(ew|{L@T0cU%X9L@acK5m1sU4@oPZE+B#wYP4)_c6HW z$b)kYjvu@+<4iNpGV5&9W}jnGJ}a-X>T1hYUt`CeHW1);-EG_Mdz^46rISxN^|WKB zpK+u$Qp0A_U#lwa8i^rG8S_n z<4qY5I4Nh|Le5c{Q_g&k$)!{xt1L>+4$2rA%qPTh!}r|%$lO28nOx`)LMGae1CEVoL%j5VzRnQ1?p`~zK0_Jn+~6*! zX}hMF+i96TIP=ud3%5ikZ33+$M6hG$5_vmP$*sh(MpH}DRov5dv|XQlSE|Dfw_9>( zE%lf|>o{(!+?dXi;Px?hzboAx>JDq>ed^w)G*6~nU0PADdxbHn1dEeH1+X@2IAuPd zFe)X)c}}AJi_LL;KC6yle01EJmQ!+WZu4BLvyMvroOS2vnU!*vcR~)f+GG)|yWgFG zzczO5hcY2*p;6P-^=XUXg=@A)KKUL>YK&aYTGj$d&kan1bF-an`s{gCSdJgUaz06w z4!7u{mk|FSy-see)jk`6EQdlYJ;z!)cjXTpHsBa*b4%XT!*Pk@hy+#N!@0dIab#=I z(YDfBALT-Wn_3OAQq6I%wdp9ME?OOZUEk8acgwHB%Xfn%IQY4Q@BZuojdz za;WKS65ql-QK9EriOUwsqwgLlk`XEVn@Q;o^h}%W&Qzw$4IDt8b*R+1uVH-b04|)k zQ*9($x5#??q?F+dBpP*b$}&=%qEt&Ag$OysvPoD^ar&f!r)5F2Mn(5n8z`yUrDLXVA_+2oSrO{d zE{jMbDuTd%Alq5RS5Yk7r`9kpCWNF$e=A*J<${d$((bII?dhobuBIXUfbJ@7Ez767Y;v>oF|ADp=M8buL}!6Y^k1mt7dFma%xfO7g_$VLRv-~O4#=}n zr3kK@uy#*jZ^Gkz&0;cSZMx-bsH(uJw(kJHB#-Rsy-@ zgcGeo{m~{knu%mgREi|}mt`aJE`e*kZ@u#iR zuSDs7W=s`YtR1N14K4B{ZliE7{TAsmsEllFyhWZIyyi@h!UlC9$R_XywJM#-ZDes9 zfte^f)EJBO$kOepDjN5Q8Fx1I{{kNlIuK#kL`NWY>)@`p;@gpw^VtP8nK1ENjQ072JqnKqj+aY<} z_hBI?Kt!!zYP>UL-4L89%^ReZwh%-`IDn3Wz?!30h;T_wKtfS-74^59dQG$Ag7|zRER`O#&t49Pl(1+wjbVBO1NN=_*+(H$X!@?FB z??|dnM7b7l09pe_8;YJGRm2+VTF#=-UYFvV2!`SA=o6Vs4Iv^2?1*zG78Ta1SbfQx zbsGv8c^i?9o>Hdzm1IbVT0Z0SYTZE;uqV#1c zPoR{kT{sR6Fgr9+-9a&NEyR59-~$?wm#_zZW}&UXR~b8+orpmpDl@A4j20J-P2QGd zbKdSwtO*6WxiXE_zv>?nd4Wch{Qg=1UZO5-UN8MRHtm@PObc##R3e#U-r@PBNO3Z2~E_s@DHd()K8<(DHWtcuBfUDI3wwm@qRJx01@_L zQ;!k7dCbx61y^TJ53g3XTuo7T>CV72RE=l~FoPmk-(6}MfR(Zk>Cjbyv^ltAB`zFT z7zhD_Vk|DGaTE7QwQo*=kf7fA4_z52#XZ(mYX&n?uzf4Z70LqXmg>eP#+v+r7$~(t z^Lr45kQ77(kfUeLeaFk);@LBN0_WsH21qO#;%Ob*RR4z+h5*4kxxjxd4z-au-tr zxKOPV848}wTX8W&=1SA6>yan-AeAF(0^fURLmUnnL-9*iL7p*}>ZoH1oiIjO0rLqt z7f4j1HUxlBI>ehOLKc2DbjUJFRI4uJP6yDjG#99a_z87^qGCA^4=QvZFl1%8Bs_vn z&tM_R?Lemyn7z#hxhg)lH4#9-!E1(0*X_n6yWNR&_E?I@#iq)Dhb#MpfktCF_;VC&A9Xc? zlxdP5i36Z4FewxUt>86#1F#7o5R6F6cmnAfK7$s5*{VZ6old#X0XGSyMrCbDv>8#f zVwQ!9OVRislnmxP>=Ru740Nu27$WL9P{dwEAKwrsF61GE z0dqp^;BH(;+>i6wWe}#67V3^=bZKJQK?wC$^nM4DD&u|nJx3M z*#kWKZRy}wvPYzNUak)vv1GJbk)*^SL#M<6`&YU&SFx{YI+E^{Uf7B2_c$bkNEAha zcfBRMNoAv7NWha%Ldp$=B+vq-+cj_lpMeyji!0PV)1_VDr>7MHU@P!M`mNF_nE;Jm zTnEwwe31KykEQ9U!9q9M0CuGBcoUn4_9Y}7iV%lXDF?bA72U1Mt;iRQ3sjZRP17Z? zH^^V1mp5`mTF!eudle^?5aPk!s0pD3lUFKwcXbE%Xn+tz60~qk68vWXd@HO*y;Lq( zJJoRDacvSN2(3UtqFiyiCfS5I(5#{rarkSz!FrG@8c@?Fa+HJWA%l2B?wxqXC4lK3 z%mzn75_}M*jmKl%dJiV!vtLr9_Y!^gLyCYRXRNM&0D!wVss7 z5o{yPum~F07;2%KwvO<7NNHAFYWH0IMx!9n;HB833j7E>rMsANEF3zo^w-(}X~U8t zc6Ce?Z^0K*kBz>Tc$nLfLIj6q34ltI*&|{F?R9n%wk_~niafS`F}?r^WDwxuHHOAe z#Fh{>?|jB_U)qN&0iIZ6Bwil$n*2Xt3a(H(M0aQ^NcQPvFdiAOlv!}=K#S8IgH_Ql zbb9c349{f;dRYe*uB)j{o3hI?w=frRSXrD1GF%hjn$1!i0ZL%;@H7hesgIj%JskH2 zlA_32E_8h48=tY-bUp=&u4z+43d+=6CIFf|;v~+Jk6)}~U~^$MJGl_`kr2Q*bnFrB zMmPtJlVN?@z+f;BNnICsAsPc+3pL}vWMqfNM4TTp%bJenpZ&8ar4)f;6%0Z;@c|Y_ zc3dcwdMSKpAalEVay&GI)}UQLhz#P$D)-CX?nu`x%{I^#vr6$cZ+G}GwA10%feM6K?Cxb z7f_B+W8@%*$l6kO)mN!BYL|si(Vd13Wl2WjHnJp`3L!+WWvCd@aqe zrVEB)#CQUKWO-c9j~)Bg%D>ySz=crN-~^SqHyyh|(B4dSy{#LOuAGpN5$HLy#>qE= zS@>e~8X!wE54m8}0DgcDPQbs60rXU)tuQqM!@;Kky~{b!W-9{%8fPL0HU32wNuFfP zVO-j1C4P(J$$aFxgVP=mQuQX{LeMPDkNGfX+!c)v=50>2RN4;~#>5P5$_r+sdyMX) z5m9b5iVG8+p|r!%O`9_;8YP3@)0|yXOU&u4C^33QTN7DsTWcXXvSU&~yO>)VhsI9K zp|!LXNLX0x5Bd6mY~OEx3xx)?|xds4E41B(vdl?z)x( zN;CxzVq9`w7c#(TA?-9CZe}QuoR`oI+YSP{sFs@5XAo71jqa@vP%vY-5aFppp9ahSnB0tQ>iEZA+A3XoPX$xjcWqvES(!5yL1G&9o9 z(hS@O+3tD}AZN}2Twvccl-9@-?#TbJ``fk8sy$SP@}>5H1ez5GowU!121WcY?I-S; z`khc8#0@&~12h!jPFvM$kG9D(-sLK_yss*eR_Z<$4dG=)gV(-*2C!G#HbvGFI)W4d zYHg5$0v-*VwdEL-qhAL72NJA69iVFKs^}%z22CU|C5XZe6Fnqfyb3rb}HXtb<02>{_O_?LDe zgR9C@fSx;OYoK~<+UZdwfDKlKk2LPVNds&!SI-nuf+9U(H$7BgL2?oFq`$yv(`XIv zJrS_F1V2MAd;K-Vs*l-!LS5eHl>EIvg0cB(~oBotQSAX8NLNDw5E z!9Nkrfghr8fne>tN#vlvyx2$&<{3eSqloB5Gvk;dwB6+f5U6D~*8_k_@2raiLx93& z;1BjgCm_8bw9n;sdRsnA$t?3s?fx0KG--I6IbDZun-a)78X(;j4OEYC`)mzrIrVr& zhFt2f0GgRU{qxwAoVKPDw&c29uuW5(0*vG!MuH8c-%pg|N&q?7NCBcpHddT^V;v?X ziLN@_De}Y24S5PcN6$jh5mjaB3;*khISr5LoS?n)UZU$43W(vep6oM@`E) zcSLwYx2I@y7IP)mfi{$MB#U|^3c*BnO2-EUGS{RIsO+MrFjD*4_(LcCX|4bI>i*&% zlr|(QUInISH5CmCGLJ#+(zW!*p0Mo3+7&d^po)=_ zt}l|$agzS114?_LV(hxK_eVb zPp0{HgauH7Z&X$BjHSoRLt)#jzj;AggMJO2O7%%h>%5ys&6E zvkaM1z14Fav`<3e_BTVatjVq(;tQ_SrH|ytL*3o{t-y5|9QgYuKsVwcHhE{3Q zQi7hZ;24dWp%p&j0=b;1Gd&nEpghE8A;7t5a&gdo)%M)d?mKn*yo1UmUhN)*Dzu@q zH5$Uy;LOmyuqU{!=W)uVGgH!hXK5Ny=c87yv>rSl3w~A~JqurU!&xOv%}POXsAZY` zx$qlpn>OsG9{WpKQ}Llq1w)O)l3tsZ2so5$pUrpLw`oS~pe`1RE0y0`n)qc&e;G1R zLdw4eM8J5@&0(#94D@8t%*3!MFt6N^$I~v%s_vp`=aotUdXZoQn}(c`tyz_-GCjXP z^mI&;5}R-ct1@kmBI!^(PMF9tH}1Jp9O$79VSqjBei>k+&+ef50d3%>Xsk|^cYGk^ zh;)*eyu~FsJ45(w4N&+-)Ag_2S{TFrxR~6Z9+=TDH|2oz(=xefxQQA^pdM*_2iL9C zgPx~C9DrJrl=Vm|=mE5;k|dT-3A=Q)u}Zm&H~hn8oP#4OLJNn?su4R_(Ugd|0HqTx zoJoHQ^x=Ii+FD*HJ^&F2#QMP2+HhGOk;sAyRul>)rRYl*-#7>_N8$vL@|l0+|FT zy@&VY$CAqvDr^FOw1K>T(eryp4WTD-WE9@!5H49-r|5wKQ5ftEpJ~4Vf*yLX5;1XP zd?uD)?&mRF^hm%2?|ZC^l3P^U z)Q$}uPh_^9{3UJNKaL&#`kHp%9kMp2BZ6JyVLcp(l%x0-fkKHR@EcAlXskk^LbeC| zsI>9aqt_^WZBP>(C==ZX-7WW?7${%eg%PcvJO%rkH|PHI=={wSAt|mNly(0L%|+k1 z_`-0m0004mX+uL$Nkc;*P*P7uNlZlm0C=2zkv&MmKpe$iQ%glEf_4yb$WWauNELCE zDi*;)X)CnqU~=gfG-*guTpR`0f`cE6RRe3JS*_Mt`=0!Tp@O!O;X2JxB(Q`eQV=1djtZ)<5T#Wk z#YCF+;~xGI$DbmXOs)zTITlcZ3d!+<|H1EW&EizdO$x<95vi~+%2pw+PL?_=9; zodEu4;7aTGYfWJGlk`SMiyQ$1+rY(jN0aw}%N=0&NtX=Ck^D4;Vi9;hqi@OsL$^TR zn%i4@AEysMmbzNL0S*p<@e*aPd%U~1ySIPOwEO!3VL)=AeZo=I00006VoOIv0RI60 z0RN!9r;`8x010qNS#tmYE+YT{E+YYWr9XB6000McNliru;{^;83=8`;c5|xVGY!rnUzyyWQ=z_XYakfKlZsdA}b3jfug8_ z+|P3=BBO3Z+&JgmYk1eY)`qj|>^i&7uCwdxI=jxUv+L|SyUwn&>+Cwa&aSiTAy{(u z7CboX*M99s#m`Ldjicc;R*aY||M*m|e;8x<$)Ed8J$r}F0`Ng!zx(_DwGsP+o$F=) zyBK!AJ@=!nndtc_2an^xDf`@KY;i@7`Geeo^a>e(&ds z{)yfbMdiI&HE1`>p0qpiwOKn~giyt@-;4SB}3SZ!~?j zWlQyaKlESySpc40XS{yvEx*)q{oY^M!}mUUrW!RTcTzz`Py|E(Ap)f_Ad09WKr|A) z*j{W-9j8704>4g#xWGf(B&HP#wcb}2W5Ku-_JDlji%8!@crNY6M7baUjgg2Z~UbxABUI8 zuzW+aH@cAFXy=mlz(*ts&Sa=@c<1oZf4DKzIo6(;diU|+@E_c+`TlFa_ZLqA!fSrx zm#%WX)xV$Vp4`)kLyT6SHP~vamp}*+0TqlI3=yjlqY;2$U?i5(SiE&e1dGJs^wg1l zyYV5Jnt3(B-0{;d`6YiAfRDiS#^3z!&UDwG*d4CCQTj`l$zUb7afoQZsKH_oO_oW; z5?w|mP#UEYDF<72-Q-)XU#^^e{~Le$bspgLZ+_iYU0VLXtrJHs*q&8{avfxB`8L3( zfU23i7lXy9Vq?INK&Ya45xfy9z=$U^o-$g7rm1CXzVDh_-a71Txn-EU|8ab4tN(^? z{Xsnoz=!#I)ti1^bmXs}IzD)Qb7}4S=3+UcvCkkI(Jv~z3Cs=!B8Y;B0SwV3FnEMu zKtqS+BI~Vho&KLC@kQ5P|Je4*f86)5pSzVT@<*Rue8 zn5`fFpMPY|UAXi4<=Xo5{bKKHn+t1=`BG{6h)Q7S0)tGcY^3RZ`omQamD+;}K~lV? z0^8OyhIz)JDN8xs`nR3C_jKgM@?~@Vp)~vel>xy-z}3lzsR37iL zLMB3y8N@`8fNCTP7|loMYD-U>!?V z=@*tpr%l^h3h!}RfmEm{kZmL&YEXoz%BU=29)=*l(8$b(sQ9)G@ECvH8MP_LfhRg|tTC&PgSs2+!4kWAOCiJfpJ2hd6QRNXmj6qM#7eqG|z)#z>4xj1d|cK2rvb5mtpPLUUAeIDi_< z&~oe{{(R3Squ0OuHX3UzmTHa-5c(>Eu1xY1F=pf8bJ^- ztO=B5Nm*8e5Ksg$g479!s*;mFs>*~T6ahuC2Amc0!eMj4c%=}7jesgqk|p;BqJWK_ zOdTOwx{Xd)-!glHZlC$l#i{9!z2J84`tplACy zZIA^!;n{pLZ>WGGSOtlnZTCt2y7WJ$N`sGz$qm67RKW(w)L;-$r3{gN@KoLtgJO(D z64jhsFMtR^sk7qx{Sb+@>sL{{3Q|FX91%lA2u4v9NR*AKE%eZ3*ivcxihQtY{AzXO zV$1ywjn4N2pMIo0OZ!NB!~gk@-`Mge7k_&*3TPB!M{qn z(pTUCE+OKKP-GdIwaG$?VyrTcliy{52Xpo>#2If>V_jTX#ynp?d_x{7^EC9b$RgHJQWs5m^@>SvB z-7ji(7qeUjC>2Ddp*e^l))P@k29y`+9@muvFy0=s$Dy-y6T5pkGkYji#Oi*CC{?WBL#fF z_|D(6!^MRks16@`#g^5@#$4!g&wkom&c_4TFM;hO%e+Vbcx{Q2*_>owo~BWIkH{NE3LX#V#9xcT>otNn{y zv&D{WJD&Pb#FP(v0KWG9e<`E8Zr?UsxaSqNw=&x@6)G|Dbl{XqaoF3NQjCPgLOg( zJ|#KEP=@8w+>Vt3Q`Hg2SAdua2Xu4eP{p$3IV^6gqh!>@w z88T^LVkA>T5E!|D(SSjTbw(RW)eJEfQ?z*6v!2DiM_q!+1xY0cD(_KMG6C|8V#^$E zW}3O}yV$$$QAo2%tV-IQDO{GmM>T;#YyJ?ET%{_A#$>guVMmOp4=E!SD2F3XEH1LR zw9L^v?%=@PcQWX%gE621OxjF?;fOrXamJwl|C_hk z;f=RG)0{Z@!kzt|?PtQeGsHZj87vL2HOwQ0+#F`|*ssSm9_)8Ws zC?XEY^ORtSkei%6=d?KIob$NyiYpimhTL=4UEF=!?HoODH!DlW6EEm2RaN1PO%_mP zlE73}Y4}JUl2a}-VUQcD7-^Ihoia*WaR-(UZtbdnb(ip+z@g7|pBLBH-)fCK$An;s zkt&36<46DZEf2*re%K}mD89t;~&t!SxB(0;>YIFIOSMkheKbvoV!3%iO zQ=d+IZW{q%ce{?2c_?N_#b zw)^}efAj~YzqUR{6lX$om!k)Y%rK0m|!qU zBAnw`v>+H`YHm(2)?%%z#RV3FO;kypA&3eFgA)h7!kN1Ip%HNgH3oH-Xbq;-q_b-m z*L~AB^PKPdelC0Blc6(B&wHY^U`;Jb>H;+?|bvq{oUsypSg8+$Ll#W-oa1_MFYE~^U>}5&ab{|0r<9$yw|K9 zI{LHyBS-f+QG$(pt{`S??IMun1!d(ESd8Us9``t!Gt(GrG1d~o!&6{vB*lL_cJAb9 z&wK{Y{+4fH+pax~suCYUN~;W$O!#vbSS9hH2vP;Ja{T1;R!*!v`Tkyd=WVxLRE2O+ z@IL*XsFc0oM_a9S`Be+R6E}S3vEjgxXKk$lE>`#~o(8zhbVRI6XiUL* zjPowK5K$$D6y%Rd<%ioEQ#Pn#5iYpoQGC;LpUdS}UyBEfHB`~p<+;zjf<%H*3_?pB zy0}sdPAvcO@BGD|omO}_bl_m9`a^GwMXVzLWm&C!;_ZJLzbXOv>u>o@GgvtB(y5ax zGqZz|EP94|`euyx1))izP|0$F2y;t>tx^SX`1ZaiG zbT!%&R!_eCZSQ=`DImP`vi)J_o;^MoAv!}9quOSJuSx*E`cL2BGNUhRso!I(0nQ|m zr|9W#GibzX#O4_)LKy==3}gjg_w;Y%lIyMmv+0S&@LApOmlAwudKr<_ClUgI)F`FK zVXWnx^UlW>8Bx#7Fp0)Q3rSMvnBXbmu+3=gXJfg(XY*^Xf8675dBPK(_>O$4t(7yQ znXOyi(C$pVI7KiCRme>Wat3mt4wozU_JJzvgO;2re&@2=uf^tPflKNi8CblK4b% zIRR)k8f@LU6R~IZxNKBxRHD{xpC+QFLPO6iE*!m3{AdEe|M%)^^r9>F?;4>juC3gn znLGH9#=Z}i`~RL#{j1es^|)4u8R{82WspI{V57TVsv*S4mM!yq{nMYpGrslN?78rQ zx=P_v)+>oKyKlqM!+E8mhoIEl9|C#CzI|sdP7xGa|IxufFiJEYq$K)b>*%3_r)0zb z=ifedB3i>B#L;_S|GI~?(dl7Rio5;q-?b%c$JgrWdZB3Q+r?lA2%caQzgILGL|}G) zEBh|HjQx+khS|Bfx(Z^!+QgaFb=!%yZgWjR&cq;@Q~Krv&a8n!Pq(q=ek`xtHy+i9 zYQQQOsi1D<6r&I=TlSofnc0O|Je~pylQLcJD~z?E2GR5}^}vLhNEd`0aT!sSa#TI` z*r5a4fc1@l+I-KP&&*m`KIG5+%L>33zy7syMC}gdmcN_*+Z+<#E2oph{+s?-7s@wZN1zFL}%;~<+KRxOi19*&eO>Q z+O4sS+bFe63u0AatRd^nuxs!6EG;d8dP1yHPl-!4>Qn5W*6e^LjhYcd@F9sQs?cgT z+gpIU#^14ga_K2{Seas~@sMNS%i@Bc`*S}dkAMF6JnEw#{O9lOt*-vz?Vq{rjyvu; z{83j0)36RvA%;kZn&^LH`P!#HofrM&%Xz|+zmALcT|}$M>v#!Fk*BuD_=xMt`5Yh} z12WZ?ttvau*+Zzog|Wn#`cSw}hqFpz391k#6pw^}j!GH|)oO3=tAFS98NiRe<;^ne ztxKNgI=^k}@>jeCe9B9HXxqJaANZ-mcii>uzTdyNaJdxKfGx!yS|V7VVxSNdEJPz@ zu3*cyd7J^wEL99-jYi`3f)acLV~|IV!25nJ?%2v|t!s=LFxDjjg3Z{y_grpiPtjjn zrf?2|PpX@`7O9)s0;(AB-s5bZetL-HV$h)G_InP!`QRr%^XljQ$Pc~g)*Eifi#%(_ zo)3AEz5gp3fM5FqFWP(J(2?IicIVyC>{w@V54l#%WQO@h1LsR5M6i|+6|pH`bB%)O z`MKmyjUj6^@Kr!!AVhG^rMZ0Zk=|)P24U)77LyU92OprwX*Zi}+qIj!yX#c3hHE_4 z{nzbG1XU`HEeNVzp1TaTp1{$kX0qyrW!O1H~}k$7=;kQhon?)wA*xMXOoo^L4CxE zV2s1L)UzRaX3528fA*(VD(<(UaYjGp3#^2QfVGGjU#3ol48}Sn1hTB);w!F1Tn5Hs zOg2&8pd0R2MW`x|aVDvP5R4TFo~AQ6@0rQ6Vr^;pzbu_N{?2O9E3Ba9Xn6c95`e$_ zmwzdT?>X?IrDF>(&#EXcjyW#{4Rl%=nTZ5n5rI)v5yrbXcy?^xz9|8y#ZmQWwbX72 zMooGUkL37IO|s;&(NZ`0Tods=y>lncnK^<=YILtll#{MaNy$f4B`O4zD2k7ss2O(0}b32_6UqRPdO zx&)UOb;q2XMgzr|L>CH4sXbN;jfJ`n#0t4Fm=FkM2?BTCcysv`2f)|;_51ANkz+3y zuJ`iHy79aoqNFio4F}E4pfP|kgqXB(8UjWHV>9g3bUijhCoZQROS);wP(9H=bz>sJ zX{_@G*S7J*G|@LAspmsC#%7$>VJ>HIZAvC4UmJ%T_3zlADN|B4?iU{SFsJ0gY*JWn z-e87pd(UTPZp#>M^{>=P44Q@>rF&KPy%R&-x+;=*#mo^@F)Fw$r)al%+|!;~e8ptJ zKlEF_ZA5wct+(I)obJ(uZ=pXDt>x_-k3iXyLq{tzb*LJA6>tbjNHd&^7S?2VlgdlB zzOktXlT*P#sOzPIhJc6=DvzyOFO!%=!Hrvplhwi0SEwL&1Qe@=`_}%?BoR?GDX*() z(luvMBr0m<6sp?Cl(vht2pJ|?P$l>>B`}x`@J^mslRPLF?AynujvoLC31O3sk`Y3g zS^~rngMrfEY>ouQTAMbvHB@;6sXW*mrkdHz&h78}f!DtF6|J2+{^hq{`2G5b1>g&Q z?|0kx96$boY;Eux%g2v5P98ceRs{_nHT9^88b6&@18Z$Mz97cbBa77TMJIh2W^D0J zvE4cuJ`@GS2vL))+*p$yiA16W4FL$K2!rKy%Ha?{7!iD$Xw~d=XmqA%WHa|)Pdn2% zuC}PwIiv>SaL{M4x=MF-9cv9CDvef)W~)WB*#v8eHh{@eGkRt~BSFIkxl;e>P8D&6 z^Dn-LPk-zq#L{ENeJCPGsGF?V91}*yQ-e?2P^u7>vOkd9Zo1{#eCym_w>19D3xE4f zfAY4M{rKn+2f!D+_I11aCs+P1uJx}7>-~)W2f1C0&Xy(sw%us zrK*6z6isHfZ|57I^K7>5-h)WQ$mbuS7;A}j4~J3Z__1UB(|iAdr4z^SRfR#&5NQ@U z8ll;2GC#M4ExUHIci%pCoVS-|)+G8eOFy&#pZp0Qh6S z`v|KfoHnS0iq{dTvT&S(pSg*TefUFc z-L;D=ue***_FqP8x`T+V832fCh(@vE5MOce6<2cXz&(U=h|x4fOJYEMINbuOJq?42 zI~6o44k1^;4n2*$S@ai{U+iXPF8$%xzv=nE|B4^~l1a24l<8PM`?j}d-R0F6byrsZ zTGt1=iYT}$5XuqNaEKp`D2IburCW;&T)G>V@0n+=Nd397o+2P7Ns+upI&*Veb@kOW z+f$P=RT7ydTEPd8u|kYVNm8x%_{@7XgFjv8qn)?>v%0?Zq692 z2>bVJrIQK7Nbnv(@G(&_;vAhV^IUuVwG>5bQ$kt;#0aVp11uds&VPL5qx|m={3ADh z>|=z%5L>(aOf0!^Xn?Bp*eu5u4ZLw&`snL<;xnJg{%amhqdk>stM$=ZUkI@{2I^+e z24Avw-##ul_k3*DVBz=zq22^8OERfyRHh`Mmgjd@*0}4o+qv~WZbk#Nih^d|AXLVZN`D4Cs-cFdTJ|7bviT} zO`5GX#yYGR@QP~|7p?buKN`#G*#GkrKXJnc|NPJOVFSQle)qffFCIPqw@2s^y-e3#^RlMBfz>P90237}mdcl@|JGT2rIaQhg(EttX^>P3 zf=H3&6nO)QRT_~I<3_z#q0wrx|MLCB5K&iCU1RnoMx4z!a_eXKU;pp{Mr-RdT6tpr zRq1SW45!4K55}rOs^U14 zVRB)5N6uq+?Bv?VeGUC?mwuIwbg0(XSY2M`*pb5=K5&rU>Ix!C2!T;=jSqg{{e10I zhfA-%3g7RNwOX_*&)y3!;;vh7A}?}g=I1C{Q`~&p9ruY%)6Wr`6GJ+`M8%|jWNW~g zG}gFgq@-?jXkv)N3bi!K-r(2P*4NJcu|Iyx&;Qo7VXDdhg-CICk&h z%Np3kbJZkn5Yi~L81P2%DB>*92x=4)Yd2NTz{^<-GIam|AOJ~3K~!7@qXa&UDc&9@ zA=h31Sc)Qtx))-jJe4xM{@N=4=Oh10xweiAkx_{3z2su9x$Zjlo__&Fs{=MCU=f?4 zVhBEx1tE+ACU;2TC_}(F13p=1jMUuPgXCti20kc(^x%!l@u9@DI!w3QOvg~qqfs;j zu2E&w>vH7iF>e0%k8{^8pH9P*${rv2mw#c;-n~puPo?Cc$hq*6eH^^|c1D9P+xP5Y znYpA9?#* zf9ChT?+1=Q6+V?2hohCjEA%hqK&XRsd5OT3dR^Vdd2Cb&y<@msc=Gku=M}w6RAsRC4hzKTg z_3W2p?RM^DI794(`mE!=thxnb4+uUG$%cfIwM-}|4wsGa(G`6`E&GQWKX&v^E;*?;v_w5MkhkR%CIt+VwI%5h^zT{|`oaoI)@|4S%l7@s||F}*cy zScw8QOOL^)qg;(Jsd-!Luo$j*>|^=n=Y0or+jrxP<%Umug3?z|*L?G-HUW}3ZN`t< ze>p0K){dQICM9U5CYr}oRhl)J=q<04VZn7p(4=QopLaJN^KY!h$B3~O=N!>{jM82{ zx%9m!*H-2p@>=kEulLxhs?N-ET8mgqRHbM%COnxnh8O}xQ6$$;%ZJ7@ zRqz;NYuS0NO45XsZLh$+IkF(UnI;Ow{=3UcZ0sLk_@RxBF-_LBs=91soza%_u1vDH73$u|Nzo zizF9M4Ky1S;zp}Ymb)61S_3Z)@9`m^VmNg0Ae9eXbKPUOVE=vy_1rvbFj;bSsk8?{ z1tcS=V^}HCIMg`QIw~I$05D&qqV<8KRx&wFT}?*T>JY6%O$N58iSab!ONfX>grp$I zS{-7k?78p~p73=~Ci=j!qsIxFItOD}GCe!XC0FbxvqGcO0_QN+5kdl3>a!UGP-_6D z4Q=Y7P1f4Uyld)MaU6Q2ks+iR*xuu^tUVkIuXxB~z{>lku}1LWOc0214)4LbtWK?x z%2$XKn?~MF25$>wMI+Txvz%e&Q{b)vB+0W}1`7)(*tT;wkABQ!QW`WlQzM`uo#|#= z0I5evOREk$cDi zSQG_=wf<=YV%kY&Qu>j4w-?PeezcBQi?bGp1Ygpymd;dbq7IQd3qypdsTS5bhGjVc zt~C~4c{1lfgtcy$Yp=VWc4r2yvq)n%;zSW4L`6-;sEpin^Jn<2-~I#s`JX?+b=P0V z)4u*GEH5we=&P^f8^7@zu#z@~#)vpuYnRSUeDFXkn3RQgyFK3bcmK$n-|{x@J$!^` zJmcwHbM=+1EuZ8WPkRzqT)r>$aKxB~6^YdGt4$faLNV3iYo7E3KKRccq6z^K$g+$W z0_{!*RB3ct?oFdPmM z5vr=vR^y9rK z;l>+o;aT7EE&S#ieuJ%B=jrxVdDlDM!9RT9pLpiepGsb2genadA2+o>IHF5o(C_n? zfAu%~@~^y>^+BIs`PEj>A(V92*LnSado?fnp%-xOIlHhX9rz_OZe56ztURi+ z{rvOTvV8|3DACjj#6;w#R6eqG$99$$hlH{uGZ~RlD#}F|l@&n*TR)6(g7MiRD^;b@ zXdohtY5-bmq}6QdLmmUpZ{2!ajGSg3s;Zj2*%%|UGqYT9;YI8@=Nw#?r!aUj4M4HR z)MIOP66;moGd(lI?mfFxRYWAkCDsysz*&RFh%uJ^`}fo8OcQ-TY(`9#Rk)8uT3cJ^ z9q;@rKKaQTae0GluYC;LckU)cn3|g5@lSXHZ~v2b&>IZ#Rh7zc=D|%usu;QL&bxWj zZ~Z=tD=W-x*~a6a__Y-6Hd8ZO*tTO2kGkqw-ua%tL-SUeNgYB`b0kennh>D@Z`09B z*Ia)sdFE1rh>+F~aPB4v(-RJF1;M;WK(in(h4BL0^z!gREOd-_gju?U} zqfxm@TB%s;$TEk$pYe54KNlm0UavPXFfr(2BhTOSkh|a$%Zt5Ys&y=`^)KA~jGVJc zk&$O?-@c9ZREx+6YXi+z!P1H$YfKT>`t{s8MbslvF=T{jXm_T$_|nV3Ixu!}>;X}+ zWN1unni^9=q9(aFjTJ@}>e(oaDP<9?%Q$@KFwPo=qdxEbo4?_jYp>y|E3aVrsKYK5=s1`$Qs#nC`*&aKi{zEuGFRQ#*IE zbo3}16f3FpR#_PNip;05B?cvPIaV?ZHdUi)*KdfBWfZZ_s7b@3OqSnEDU00=I7@)|Gd2j;EYMXF`hb+^|Jv^&LAmiPPHhk&~6qi zcPk*{AK(8W{{3U0V9VSz_a43nQ2$S(Ev_dpLCT5KRL%q(i&CQHI_p0NR>j$#O!a_)+3rqbB{PMo-Avw5B?U3QG%% ztSm248fEUnbB-KYJzhTK09;;O&9X*uVPkfdsy-8~swxJ9LE0T<$+2U{k_y)(8IV!M z8B3mJ7$c}pvrRA#o8`oQ$<}S#x%~3|7@OS0sg;i}kb6^@o$m0|uYWQJ?>Wq%s&FR5 zmqYHl<90&u2=JKeuHnkdub?bTjD^gaaYpl?xZp4z2j%%?Aa_Nn9K3opEon?Csor1G_vGL5t=OP>;&kd)FP z1iHO0q1$KNE@6onfsqcFnx2{PgYBs{nSs^y^-G7|%R>&pV<(RLEu}6w5%N5zD2loh zD`Jendp|j-uMD184j@$N=!BXw&?;rL0Yjxq)+o5_^8HM8rqVF+_(dYG4u(Vwt#*SK zzVP{c>V{AAiI3kvSylCUZjsCy_U*ffm;A_!X}23#12I$>BXxP=!C9F#TyWmG{M3*C z2*3L3*Kz3Z0@f(OSBOL!nd6yHe=5)Y)@LW>c;+a}A-Of6ev_@$GX@|;wr-tgdS;gH z@g7c66GVtGtb97Bq^8bnmf^CD%s8?c>3cd?WI#&7wo_;cgmb{xP=1f^<(EpuDA`pPTF ziUx-c9>7;6Gt(WO_ifMN$6odlzTq3b9?^giC5Ec5-I_ByljDpEaf(>S#TQ@9?%jJB zj)ojPdYH@z7oNYD7rpTL{OAwAhzl;h078{i<1rn$Gp1KWW#Vo>R|2T2l*3`&l?MyQ zPq1?07@0s6ODQ>@IkdnaW~urohWTwfn4RBByE9Ez<7BOiX~>j3Li5iae+4kI0;5B$hIO z+MH4YTX*e9-D;yD4Vj*l-6qED^YOWanvRL62*yC}ERVkWO7>lR2|xAYFC$7ofL5cR zGd%^N#9Bc#rjIp0p%0P~6pfguv|Bmf@x15o)Tcd}QCTAO5yR6{P1>y{gffjdj1?|3 zb#u`v^}N$>Mr{N_RZThw#~X9d?=v?$!=0+>ICJ_gbNnf<7 zGvquk=ytocp3oxJs91C2eXsei`jETeq;e4*yWBDPeE+*KO*$|F+4y)OnkA zUL-Lp!Q+f!>)g~v&+z!<%~+u(){K;HDhcWXZn_xbDTM%Ig!T0`dc8q9mbN{G?{}dF zU=`C8epA=K^F~3I=lHnk5THrRyOL)aE9)z%VkHn!V>CV}-Ev)jlbe)h4r>z^sKB7Axb@C^xaYtDdczR`WiT8v7!E&Ymia*N+stoK7xbkjaYgDQ zUDc<$tNMBbaF}G9Uy>Og_2jD5U|C@(F_c&>Fw7>i-JF@vD)pXDFMzKV@C(Hp0n#3T1JQC&W(brlg} zJqcnk7_hRk0x@E(LkW~+IXN+4RC>G$E^jbZIJRxyPNUhNBu!h56H#1x3ynsB#>i-x zIKV0f#G1QjXJ-$5*(~79%*;ns)YZKPwRH%S*a+;>NU|n&s;1$XIve)1_{5DH#!m z$5BnL3{R4DMU$=|0d@63fCSViWU0?35bD>Z&x3^Yz4dD=3#BoXV)10eSfJW~*Z>NU z98rs@Hr}EHHf`#NWlgs#!N=n1Qpzal^ z^RRl@j?w!9eB*QTMS@T^flwgTs6$LMt9^hfAVDFX z)UJBUkhd?=yE|hhdOcH*Ex~*G{XQ{<)bcVpD^57ExJ+4jPOh#q3QBKSr3~1*N%?dl zp{qZRX&hrx0@Cevla@2}WosB!i(hsC=FP^6b#5ULHwNe2_knJr5sXO|&|0$rKrsX{ z^eg4=gNLa?>fRp>`jmq{nKcAop_`tVxsUWnzL1Da;^f+pzbCbc)pUu*)Ywy7ZH)mx z8Zum8XRx|T7z`i=VmV~6wuT>#(CCTf2=!I1=Tis)Q~%6l$c;Ju}+{hJEky5Vf~bJXo+V)d{V>nvIYqB#2AbMV*#BOhsspc)ts zdJrSMK@S%z=61DCsg9%@pX#;3MzhlWlhgNkf)lIEal_T-2ETE~+UIKxqf!m~j6!5; zsy*qpOPp*yTUI012>pJ^@r4ugM}a}596hl_6@`4Ng>l*BJ*U2rR1+S9Zfpso52?@) z0zT;gKQ%o!+yAly@Et$(is7?==I3t?@Ek>rYEolJebUcl6k|QdWt?FdV~Mpkus2#m zv6QMDIljp9${NjvLyV=jzQ&-}$3;&wGfWLjvPL8AZj%O-Co(+Au3}w1(K8@!H|%~5 z##&=2&Y>Yt4h9U?dsM>&yb!`f2bVa{B>JSbaw3ZNB|ZpcIbtyAV{HmGie{5mXDXEv z>sWa#xqqPddcOpJjFkN}YsV64wwj0$tTVNa&=7*h0PE{r7MGT(qG1@}@bN`T&6sI* zh$?l9gq%{=+-&_+m8GSn6av+{z|m;!qHv zGCt;aEEfXOXxIT1c|nhq=?;8gd9BakqsQ2>bq15Ck-bR{n5GG=t*){q&y)42N9x+T z$5%;e8?AM54@8NC7*m~dJP<~b(t5PE&Y;_+*YD$Ng}0H^TWpM>C>oQ`ZJwlOd9%UH z+-&N#^?|Y+QB|H%SyEPMHcr3aqtR^AY_-Vptd>n}GHUfZJCk@QjD}RBA;iei;v!q- z=O`LQ?Ml;pMdvJ4S#sp)QTl^XqPxUVML}(b&2y~FlG41cp4=DgV^Vr`bv4bRagLG3 z!PNB3zy8v9e49Qj064p4_76uVmcP}6>om+N&<9-O6}!Q{w4{{Q>1*NYQEnLNywZjeCtlsK<9a@O_aw9@RRt77=_1 z47**{S63MgM=5iyW7YQ56pdCZokvnz`BW!NnXoN#I@xRjRfwsgppmL9>2|yHx;=ci z%dp>P*zePsnWix{RjdAGqti^4GwF5>m1i^<;xbEBRjjYBF&t);<)}{DEJg0oz$O|?G%z%l*kmLjc4g^Rh znJgsXoP-4CkYtjKhXlw;0vWJ@U=l(S4-haogs?io#x|B^OR{8bt(IEqwf1j$@7?+1 zy|4P~R?Aif3tFhIPMwmfyVX_i`+fJld++c5?t(N1xjWsob&l=!NyZ=u1502{Qb0(g zvB9B3hjDmp44XD>YF`}AU{#%rL3M5p{jPIsZf*{}eSPp89n7jZ(esBT?t94?a3K*j zo0y%MLELCry&A_-E|g2~3n7H(*_4Y??N-GsC}Ft>9}cvFGf3A*8-yWB-Q9?*bu^nz zH0pIENrFPFg`U1%2q^)?~T%rVTn^ zFgY=SRB13N+vmd|4A9$ay&OQ$TEX``3=R&#^8(9Ec9K%W0G*^x5=6ic0|cH#q1=U; z*;%w&Eg)uKHHMy^9tbX7L~Pe=KHcP`G#uEuYsjiMo9(81$_^by1B_ruLoi$8ot~>= zw$VV7Jk-fU;@If@-DSsuW?+V(QU$|R`|JmxT~_5zUBTG1!DAlLAAR(H{H|U$0Q|FG zd56C11AqRpZt8DNjU9WFC;2Umj4;OCT}f*1a>k%i4NV4q;KTC*q=vwQ(4G^u0TVuA zWAMlmPvhFFuZHhDkLP)|!90m!lt!&G2M7VBHJZ&Pf?@$&IQ9(Z&tDH??SmkaD6;u8 zA_PHz{=q>M2L|xWu4nMfle@5U=T02hzaObq2z<}pOdv3Br9C`9fFP@EPuvNs3l3ij zjILjY7r*$$xZ=voux9lLL?J*A43yD`lNg3<6f31Yhrtm>7wG6Sa=rXkcwHq4w9zoR z5@Y+1G4Q;sFUQBzyY^zP(tt8RtT_+_5W=4QhzyE_QoD>t#&t82-ENulh^f{f7$bfE zvEJdKci!{f-<`q>Y(H>k`^SI%H}pF`_@VoX8~iU$j!#@0>Bilj;9Fv)Aw1jJM#g{y zgU9We!xs|F3t(i7rdL3cq|h2*!o$o|6+52Xg`2m%2+SCGFyK11GeZIk0^2Mt&}`J; zhXK0FWh?gc9Q37Z0wr=ll~STyE&~8cj-8I)Bi4#O|gd^ieuz&x4 z%+wmF*Xq{$811_7BsjODuloDS7+t>(>qkdX9vpz@js@h>8(rPq5K^L1ug{;kvdFWY zsLsd$)ZTWvOFpYKaU?0T;LftFBrCa=suYb{6Z`ibfJq3Q}qNV8U~Rl0EII8M@BHXVg-GAZXQyY% z&_*ED5CEicJ;B-_jT0Q)w+||5A#GI=3XSf-;AJ;lgV)~vQVf&}DEJbZc}~FGmV~r% z17Qd#bQLi$I0PwuA~Vp2{<(_*5XhvQB`N(j*wz_bL>TzSfEv5iIz^~xMnlDLC; zh_0?)bd|c=^1&pyeP2n^R(s{JIm&s$D7V{PE-BuoU}emmjJ9R0e6dPXOirGFj*|sv z3dXD`Kp2LIq879R74cjCoYT!cUn3=i6)=!KBBA|X8&fpV0(yV2d# zXQS3!iX%&gXVPv&R=4nUYF>;nXr&-L54E`&%uG%?j-frRF~&jM$Iyxu@C%{kPEzHeKBZ?wGsgsK%hT841yZ7wHJ)igt_K(jZQVg}2V`y{} zhSsbD4+6^^q69)P4f4kcv}b5|cy?q+P&9=A0301jL_t*bs=qyYeB$e$`h)k@@k6v9 z+JMPF`qrq90t0cs&CaRSR zqNY82@{{J+0h~){ZS}72`}Ak=(BnICJQl5bOU;g+cj>Bf-!LL=jAEE2X{yEUaKF*& zuS_Z)U$=hr@Q2_1E{z|N{YWO9zTsW(=4;olFHIglaqH~#@g6BTuQs9{&o8L!ufOi` zZ{7d*53O4>ux;nV-}=%kuD_z|r|x(;d?7&w2*VIsXZ;@BO(x1_&@ptCy3yU+3mMq$ zg?X|qf@wc32Rog9kM?jxE2tSa8aRG*+%k>16;Vqy!7G+Av|) ze|Bi~=*wm+wJ(C<8<4b5dZa#k_xlZ&fh~&>yT9`{f62Of2Jm}7{WI+|#~-}^ef+DR z|HuC-;>ta5_{lrEZh7%F_MFp7ET0ez#?hZLood_FZ*>8?d&&sQ-4+6Zx0SYt0KuMr zD5TZaGmfFA~l`@_$_?4}zpe%ZF`v7)C0D4WBIjYa|i<9U#n zzhLCT1p_Ozopt|nZineGVJj7k2}sX(5^};#N#YqNe!=@tsY0bvL8H-tloF*Gg5biH z$LQo0C0OBgW0S*KTx@bp`%JI}wO2-DXCY_&A!hT28EA>kO9wM;;DO`F!7)6xV-LRg zwXeq~YK?bpc+m~_T)pij@o&BD*Qf))Gho|pxO`(||W(~GVuZoT*d4E6WU z=l!@isSrZ>F%OP>D!5*ldvhxC7G=UgK%ca2Dy7?`TFT`Sm;8Ns28*1Z_X{F7r81jE z?E)&YLwpt_&N;L7aF7jh0K~Nflhf09^sy)K&F$aA#N2E}G5Ky*?78>#Z+>g*H{bTF zbT(bnvkHI!u(^Pw&Q&BS$bZGkqx4@@T@@(KHCY+0@D3?EL6e%p1tRPGr*ynoL& zU-DmA+uzmgb9P~GZ-4KGwX4`g=bwia{e9>Pe3Xg>ctRjG8jYX?PkOE#sBJyf5CX@* zQK@B}2w@wi@-#29vkP4Nfdx_K5%Ya1FNeDCMbsJOe+L%_T2b6Am&@Z{+5Th;KW4`8V`#th%Xf(= zYLs?-|M6Ebr3QVD1jOGS`u@eCFEtZHj8O7DiT>Um^p(r#Dil!eD#DW*LwEY5LOV zzD795jNu%$C$@i?J-cT&J34;EJA81zIk0!{(9Gmy-@xcaX|ZoW%uP>Uvv=21_YxVd zk{ApMg&@S8uX`OXIByd;Bfw~QCWU4mS|s2XyYcOZzKhTQ^FKNwgF&kugepkp0-KKt-}pA3eE`ab4MW(onLkb_DRG`gaj?)lIkQuFvON{;U$GrO0} z%q~)M`)K;`4*J{w^Fg}&;`2x_Mx29uPY~xIuPeNN+ikZNJ1cv#iOv$7Y=_1Utvr12 z;9H2WLAfZG0K{>Mxmp!{efL9;o0k+OTGbUVQbX zbo2FF>Do&+((3*$>hd`iiv?P>Zj^){5c7Nj=fs2{?nxsH{{HK4eQ9rJg*$diyYUsb z@uAf#e}eh40V#-ik{}of-y?8FOfcfYZm&{+JPrytNHS2lr-y#-=if>n{P0KVva7Bo z5flkr5SNmq=aJV{NQc&}`jyTqcedbDnj$lD;>3w>azc@uehYxM0x28|+BVx~>beZl z4{+lxx8jbUcpbt*5ylX9?%V;b5;u}88%v2tFTCRNZyg^Sr_O3yV8+MoRlu zGbVd1kvEucdjm0s)DT8SR-v!IADnYM_UQKzM=c0OU}UwaNg2_qH(uMSHTpU$-2vc9 zw&l8Oi*wU6zX-;J$HhDVu?URWBctqdftzo+729sS5ylX<|Hnh9RA%6DD_rR@fKx`v zVynj|Cf?gw?G6A>vg1dOUZs?}3Isde6s)aix}>?85J6ak9|Ra18^g|>JJ40IQXNL6 z;AGD@nW`xnGnhuL%eUR}U;NIBcL3P7SH1pqqFR}Ib09S)ZF}|Yx83&52fskk%pt1J z?4!pX{SFNe4-w;x!h%nXvE0xRMt~s31Tn^lF-D%}5$Bv3W5hWpAp{8_=*~Ouq=&bE zl_n1FruxhTed1#uuH1Fko37}rcxMZ4+nersBfsS0i+_6ari~E36&s48$kD2-T$$wq zo?JT52&zh{wx*u3!(2c_xcu_Vao%~G(cjk#j|10TeP!AA*l=gnI{<9krB_|Pd34>H z*9;5|K&4iSBaR~|tz0TF)6QEo^C)XDaw8M$7z*A7r001!aNq!1%{mxqc#@;PuLmnf zR{Z`guh`bf!FB*>?eXtDyyc3mmvpnFg}|3cn@!hG0;eLMsmCuJh-j^?G=}qn+3aK@ zLLA2^1U{JH(5(h~`pej~Va;VX-g@&*ot5tZ5P$lSyTj4-Ykz9ZnpJ_65=aviiy^AD z3er@;7+d|&=Hx!%_Na&@yrMBqFWP7uRVNXIVDOB_wO4H|?c1~anhyLr0K|WM=i9;N z^`kco_I7iVMvi80Fm~u5;y8hw!GY7Bo}M)#Wzuf8FPI>h0W&d>)UkH$N^IS_R?>(5O~1Ha2EkHM3;&X?nnRLlOWkIF3ym zgAlMHLyQ3`g%liv1ASdvuDtS&4gk+#`=)psWhyc_bm$<22<8Kc+%%z+Y!Lvh z?8f{=&!iP${Ln#2vXe8RQa9dJ;i3yKJpb7E_(%tU=ct{(<-#q^X7x&@6G&)4ryveI zvHNKZuO6P)eR+=KAA61mXAE(i;P|l#D-@@deZYcocyM6jq5b;?&*f`yPLmPVtzF$8 zc+3Mi;cu;>w7`i4vf9hXr>1diY~tJ7zy8RR%rWV~VAc0IYd@IN z>$77c<|7a;>@aAnPblDr1w>H`lQT2(Wj)VF!Z~E%*9TV&#~lxN4jM5a(Ta>QFsXqk zg)oXRaqI+oH;f9e@UUpqOnck|VA`Iqt&+t#jB#liVPjlGiWTVX?M2Ff zNE@`YMi>T2q6p*#IC6ZlRcS>@2Y~0GedM?9rgi`OcXmvVZls>|E160aq%7e0Ooi5* zcj2LCwb>gK3wB71n`V^XZL|3!Liz{_A&SK!y1Tnk>h4CVR6-br@Eob3cX%c0sR0i{ z)LTsiT}8}Q8u3^D^=tQ(`}+@e0CX5NfUaT@+Kpz2;{;LEf)Em1a3o0zFX%#dcQ?3`?B$F@2#zF8!K47w5|u^^6Eibd zyKWUy0=9qqKj@L2yS}hu&B{Ohji3Kb)dAo+YA?F>x~V6=@%0aga`%T`vTd8Mxily0 z^@nBPPjlg;Sn5HsSZwP(hsAF6IibolO)-A#7-HSRK>vVM%_l-??A&C-8H^$L5IA&n zs&;66;tQcK_dNLE11ySK%`I16`A>Jf@tqZ%<91F1@Qzo%A$`xie}2#BzV}4eGvm{L zc*!zyU6%l`)8;c%UGBNZ*5I3@PlS zC1}8d!2V~)zxMsn$+!L47yrGr?bWZsNAG$Y0iQz0&S7<|y#4BXeuG7=)@A#4KjWRK zRuAv|#`jK~xAn^Ht5&Ugkq}~jXkqTT)oNjEZ0svRxm+D6mR{FaDsv)&k_@Sxd=b3RxBBgxmkh7<@@PG3LKzLu^A=P>T~~=umfORXYGYE$g}B@?scLmtrN&dC`P#Uc@xDXf~8QIiG zlUXjrmlLI|WN$VyjusGPg0AlFfzHZy0QkJNcFR^?)a+`>h1ZVM+?JaOmfM~Woe0jq zJpJTTu_kjkPGUP;FgL^|^MK3%p6~y+4L9A;IdgY{kmoUDgkFk=|B?ksR;3w~~Est8&DBy94Ta|vjG83q^z=>^c7BjtQ( z=YeMp01%f*je?K>C;?+Bz!ZqN`Y|&o1Rm?{Eqk36?f~%l7lpGW8PJ^1kL@C3Se#fQ zOA^OzHPt0k`vfq~dta1=DfX`{HSm>2PilqR^*nCdVkjWhzjg5MVmh7G*V-WfQ zL2LT;kN$P1`qTm7sV!0JS2*YWdsJkwT>_Vp7a3D>2vpd zf}>LcJ*R^Zmiz2w06p%x8$frnQrRL#N=rh7$bfN2P-RTaj*Z<9Kqt-SDk_yS<=!Rk zat6t{Ok45Wk3IQRH-O^+DgdegYVNrTU=Bc(3!pmyJj+c0C;=z}7yz&V!1(|+0a)W+ z?*dQ&-~;fW^q`b->8&&u6*GVttp@<;G))TabPZ09bQuA@?=yJvatJX1KmjoBIROv@ zXaHydm;|sNz+M3R0335)tGn;(0B{)}Cp-tJ3jk~Za1nsj09FDRbe~t`cg7{0mgMX- zcSxep9tX(-oh($e;GAO!f%F&y3N#PC#g@AFz70U8hE#PRJPP1h06PIZ31G^BS9bvT zquow>-9_69;0gejx^2~WpO57}CqIqP%mnAB13r)iupxj`rlikK-iR3(7!g9>2Lu<+ z`^o3I9vMgr0EPfu0^ru1$J__tF#wMPcpAVFEb5u>0PxI#t`ERw0Izi3V!(Mm&w-3N z0CFsy9O1d=MT+(0aemsdnFp)UCZ6lcb63XmeILR4b*0vhrawmYV0C*U{iQNCy0pJVxUPl1j;XJ_x7dSA?E3^2yf9C0>C2x?gy~Lc}wa5@CQ5FZ31v7fL8!m;k3Bb3Pt4Cb!0PJV05Sx@I6NsKghc9ok}PB$27-RzYvs9fpvryTY+bUgJ#^r_ z*`15O4&YuFU7HR7PtOBv0PsryZg6*Ykn?6<&cnT6-~#}`IgDu!7;L9e7%a8Q7z4&R zI9Qt>D4C!%Nx|KonDGG_0n+gT83R1VP%IUBYiI>;PM$D7z}f^kuekViAEjR0EPhG z?6!^|2RUl;I3jykJ3khjsT;h3Vd`xq}%;elGVxyorgLYswAP^&f z86+yP!bbcBEd{gZND<`$ncciA0la*#foYpw-$fGHj z5<$7l?7R;HDJ8U0h$x=w3h&Q7c65X%W3(Ga$HN@Ov`TY3-j&CfRf})fh<0S_iMT5 zti+eVmhAq~rDK)_vu3%?u~Jags6YwumGgj!yKx@` z@Bo0_SjN<>v&}m&Sl-`N0B&>7S)jslXGO8Z_MD%ekgu#Pmt7zW81kUQDL`i&s0`*A zGSlwJ@^3E6oy5|t>ka^4z!Xxy3shbT;ClD(j18TyqT~U0Dst-L*Ups3G^g-LOL1Dw z5W z?k)pc!12SualM$REPC zOp5}73~U730XOB&amQUYV7~+0G0Zch>Bq%$oGX@5_Cx0tOYZ!*7MAQf0?QWZa^L5} zHUxP&P>LLAm)g$rbyt%+%Vo*ntPY#F=NSj8aoDc6!|p7XeP4VwoZ~tGd|}rkaKJ0% z0PE!*@;N|ga^6hm(jgi6GGH~`-#csZG+*0GLttYaPfVc7oz X=-9|~d5sT100000NkvXXu0mjfJ|1Ib literal 0 HcmV?d00001