diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..c139111 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,11 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "manager-service (debug)", + "type": "dotnet", + "request": "launch", + "projectPath": "${workspaceFolder}/ManagerService/ManagerService.csproj" + } + ] +} diff --git a/ManagerService/Controllers/AiController.cs b/ManagerService/Controllers/AiController.cs index 21920a3..5401475 100644 --- a/ManagerService/Controllers/AiController.cs +++ b/ManagerService/Controllers/AiController.cs @@ -88,6 +88,7 @@ namespace ManagerService.Controllers return Forbid(); // Vérifie que l'app concernée a activé l'assistant + // Pour AppType.Voice : fallback sur Mobile si pas d'instance Voice dédiée var appInstance = _context.ApplicationInstances .FirstOrDefault(ai => ai.InstanceId == request.InstanceId && ai.AppType == request.AppType); diff --git a/ManagerService/Controllers/SectionController.cs b/ManagerService/Controllers/SectionController.cs index 3522b4f..9306ecb 100644 --- a/ManagerService/Controllers/SectionController.cs +++ b/ManagerService/Controllers/SectionController.cs @@ -229,7 +229,7 @@ namespace ManagerService.Controllers break; case SectionType.Game: Resource resource = _myInfoMateDbContext.Resources.FirstOrDefault(r => r.Id == (dto as GameDTO).puzzleImageId); - (dto as GameDTO).puzzleImage = resource.ToDTO(); + (dto as GameDTO).puzzleImage = resource?.ToDTO(); break; case SectionType.Map: var geoPoints = _myInfoMateDbContext.GeoPoints.Where(gp => gp.SectionMapId == section.Id)/*.OrderBy(gp => gp.or)*/.ToList(); @@ -348,6 +348,15 @@ namespace ManagerService.Controllers } (dto as MenuDTO).sections = subSectionToReturn; break; + case SectionType.Parcours: + var parcoursPaths = _myInfoMateDbContext.GuidedPaths + .Include(gp => gp.ImageResource) + .Include(gp => gp.Steps).ThenInclude(s => s.QuizQuestions) + .Where(gp => gp.SectionParcoursId == section.Id) + .OrderBy(gp => gp.Order) + .ToList(); + (dto as ParcoursDTO).guidedPaths = parcoursPaths.Select(gp => gp.ToDTO()).ToList(); + break; } sectionsToReturn.Add(dto); @@ -612,6 +621,12 @@ namespace ManagerService.Controllers WebSource = "", }; break; + case SectionType.Parcours: + section = new SectionParcours + { + GuidedPaths = new List() + }; + break; } section.InstanceId = newSection.instanceId; @@ -936,13 +951,6 @@ namespace ManagerService.Controllers // REMOVE ALL POINTS var geoPoints = _myInfoMateDbContext.GeoPoints.Where(gp => gp.SectionMapId == section.Id).ToList(); _myInfoMateDbContext.RemoveRange(geoPoints); - - var guidedPaths = _myInfoMateDbContext.GuidedPaths.Include(gp => gp.Steps).Where(gp => gp.SectionMapId == id); - foreach (var guidedPath in guidedPaths) - { - _myInfoMateDbContext.RemoveRange(guidedPath.Steps); - _myInfoMateDbContext.Remove(guidedPath); - } } if (section.Type == SectionType.Quiz) @@ -952,6 +960,16 @@ namespace ManagerService.Controllers _myInfoMateDbContext.RemoveRange(quizQuestions); } + if (section.Type == SectionType.Parcours) + { + var guidedPaths = _myInfoMateDbContext.GuidedPaths.Include(gp => gp.Steps).Where(gp => gp.SectionParcoursId == id); + foreach (var guidedPath in guidedPaths) + { + _myInfoMateDbContext.RemoveRange(guidedPath.Steps); + _myInfoMateDbContext.Remove(guidedPath); + } + } + _myInfoMateDbContext.Remove(section); var configuration = _myInfoMateDbContext.Configurations.FirstOrDefault(c => c.Id == section.ConfigurationId); diff --git a/ManagerService/Controllers/SectionMapController.cs b/ManagerService/Controllers/SectionMapController.cs index a92bfeb..972f18f 100644 --- a/ManagerService/Controllers/SectionMapController.cs +++ b/ManagerService/Controllers/SectionMapController.cs @@ -339,9 +339,8 @@ namespace ManagerService.Controllers try { List guidedPaths = _myInfoMateDbContext.GuidedPaths - .Include(gp => gp.Steps).ThenInclude(gp => gp.QuizQuestions) - .Include(gp => gp.Steps).ThenInclude(gp => gp.TriggerGeoPoint) - .Where(gp => gp.SectionMapId == sectionMapId).ToList(); + .Include(gp => gp.Steps).ThenInclude(gp => gp.QuizQuestions).ThenInclude(q => q.PuzzleImage) + .Where(gp => gp.SectionEventId == sectionMapId).ToList(); /*List programmeBlocks = new List(); foreach (var program in sectionEvent.Programme) @@ -387,13 +386,14 @@ namespace ManagerService.Controllers if (guidedPathDTO == null) throw new ArgumentNullException("GuidedPath param is null"); - var existingSection = _myInfoMateDbContext.Sections.OfType().FirstOrDefault(sm => sm.Id == sectionMapId); - if (existingSection == null) - throw new KeyNotFoundException("Section map does not exist"); + var existingSectionEvent = _myInfoMateDbContext.Sections.OfType().FirstOrDefault(se => se.Id == sectionMapId); + + if (existingSectionEvent == null) + throw new KeyNotFoundException("Section event does not exist"); GuidedPath guidedPath = new GuidedPath().FromDTO(guidedPathDTO); guidedPath.Id = idService.GenerateHexId(); - guidedPath.SectionMapId = sectionMapId; + guidedPath.SectionEventId = existingSectionEvent?.Id; guidedPath.Steps = new List(); foreach (var stepDTO in guidedPathDTO.steps ?? new List()) { @@ -438,7 +438,7 @@ namespace ManagerService.Controllers throw new ArgumentNullException("GuidedPath param is null"); var existingGuidedPath = _myInfoMateDbContext.GuidedPaths - .Include(gp => gp.Steps) + .Include(gp => gp.Steps).ThenInclude(s => s.QuizQuestions) .FirstOrDefault(gp => gp.Id == guidedPathDTO.id); if (existingGuidedPath == null) throw new KeyNotFoundException("GuidedPath does not exist"); @@ -446,9 +446,7 @@ namespace ManagerService.Controllers existingGuidedPath.InstanceId = guidedPathDTO.instanceId; existingGuidedPath.Title = guidedPathDTO.title ?? new List(); existingGuidedPath.Description = guidedPathDTO.description ?? new List(); - existingGuidedPath.SectionMapId = guidedPathDTO.sectionMapId; existingGuidedPath.SectionEventId = guidedPathDTO.sectionEventId; - existingGuidedPath.SectionGameId = guidedPathDTO.sectionGameId; existingGuidedPath.IsLinear = guidedPathDTO.isLinear; existingGuidedPath.RequireSuccessToAdvance = guidedPathDTO.requireSuccessToAdvance; existingGuidedPath.HideNextStepsUntilComplete = guidedPathDTO.hideNextStepsUntilComplete; @@ -553,7 +551,6 @@ namespace ManagerService.Controllers { List guidedSteps = _myInfoMateDbContext.GuidedSteps .Include(gs => gs.QuizQuestions) - .Include(gs => gs.TriggerGeoPoint) .Where(gs => gs.GuidedPathId == guidedPathId).ToList(); /*List programmeBlocks = new List(); @@ -657,7 +654,6 @@ namespace ManagerService.Controllers } existingGuidedStep.ZoneRadiusMeters = guidedStepDTO.zoneRadiusMeters; existingGuidedStep.ImageUrl = guidedStepDTO.imageUrl; - existingGuidedStep.TriggerGeoPointId = guidedStepDTO.triggerGeoPointId; existingGuidedStep.IsHiddenInitially = guidedStepDTO.isHiddenInitially; existingGuidedStep.QuizQuestions = guidedStepDTO.quizQuestions; // � convertir si besoin ? existingGuidedStep.IsStepTimer = guidedStepDTO.isStepTimer; diff --git a/ManagerService/Controllers/SectionParcoursController.cs b/ManagerService/Controllers/SectionParcoursController.cs new file mode 100644 index 0000000..808d051 --- /dev/null +++ b/ManagerService/Controllers/SectionParcoursController.cs @@ -0,0 +1,382 @@ +using Manager.DTOs; +using ManagerService.Data; +using ManagerService.Data.SubSection; +using ManagerService.DTOs; +using ManagerService.Helpers; +using ManagerService.Services; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; +using NSwag.Annotations; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace ManagerService.Controllers +{ + [Authorize(Policy = ManagerService.Service.Security.Policies.ContentEditor)] + [ApiController, Route("api/[controller]")] + [OpenApiTag("Section parcours", Description = "Section parcours management")] + public class SectionParcoursController : ControllerBase + { + private readonly MyInfoMateDbContext _myInfoMateDbContext; + private readonly ILogger _logger; + private readonly IConfiguration _configuration; + IHexIdGeneratorService idService = new HexIdGeneratorService(); + + public SectionParcoursController(IConfiguration configuration, ILogger logger, MyInfoMateDbContext myInfoMateDbContext) + { + _logger = logger; + _configuration = configuration; + _myInfoMateDbContext = myInfoMateDbContext; + } + + /// + /// Get all guided paths from a parcours section + /// + [AllowAnonymous] + [ProducesResponseType(typeof(List), 200)] + [ProducesResponseType(typeof(string), 404)] + [ProducesResponseType(typeof(string), 500)] + [HttpGet("{sectionParcoursId}/guided-path")] + public ObjectResult GetAllGuidedPathFromSection(string sectionParcoursId) + { + try + { + List guidedPaths = _myInfoMateDbContext.GuidedPaths + .Include(gp => gp.ImageResource) + .Include(gp => gp.Steps).ThenInclude(s => s.QuizQuestions).ThenInclude(q => q.PuzzleImage) + .Where(gp => gp.SectionParcoursId == sectionParcoursId) + .OrderBy(gp => gp.Order) + .ToList(); + + return new OkObjectResult(guidedPaths.Select(gp => gp.ToDTO())); + } + catch (KeyNotFoundException ex) + { + return new NotFoundObjectResult(ex.Message); + } + catch (Exception ex) + { + return new ObjectResult(ex.Message) { StatusCode = 500 }; + } + } + + /// + /// Create a new guided path in a parcours section + /// + [ProducesResponseType(typeof(GuidedPathDTO), 200)] + [ProducesResponseType(typeof(string), 400)] + [ProducesResponseType(typeof(string), 404)] + [ProducesResponseType(typeof(string), 500)] + [HttpPost("{sectionParcoursId}/guided-path")] + public ObjectResult CreateGuidedPath(string sectionParcoursId, [FromBody] GuidedPathDTO guidedPathDTO) + { + try + { + if (sectionParcoursId == null) + throw new ArgumentNullException("SectionParcoursId param is null"); + + if (guidedPathDTO == null) + throw new ArgumentNullException("GuidedPath param is null"); + + var sectionParcours = _myInfoMateDbContext.Sections.OfType().FirstOrDefault(sp => sp.Id == sectionParcoursId); + if (sectionParcours == null) + throw new KeyNotFoundException("SectionParcours does not exist"); + + GuidedPath guidedPath = new GuidedPath().FromDTO(guidedPathDTO); + guidedPath.Id = idService.GenerateHexId(); + guidedPath.SectionParcoursId = sectionParcoursId; + guidedPath.Order = _myInfoMateDbContext.GuidedPaths.Count(gp => gp.SectionParcoursId == sectionParcoursId); + guidedPath.Steps = new List(); + + foreach (var stepDTO in guidedPathDTO.steps ?? new List()) + { + GuidedStep step = new GuidedStep().FromDTO(stepDTO); + step.Id = idService.GenerateHexId(); + step.GuidedPathId = guidedPath.Id; + guidedPath.Steps.Add(step); + } + + _myInfoMateDbContext.GuidedPaths.Add(guidedPath); + _myInfoMateDbContext.SaveChanges(); + + return new OkObjectResult(guidedPath.ToDTO()); + } + catch (ArgumentNullException ex) + { + return new BadRequestObjectResult(ex.Message); + } + catch (KeyNotFoundException ex) + { + return new NotFoundObjectResult(ex.Message); + } + catch (Exception ex) + { + return new ObjectResult(ex.Message) { StatusCode = 500 }; + } + } + + /// + /// Update a guided path + /// + [ProducesResponseType(typeof(GuidedPathDTO), 200)] + [ProducesResponseType(typeof(string), 400)] + [ProducesResponseType(typeof(string), 404)] + [ProducesResponseType(typeof(string), 500)] + [HttpPut("guided-path")] + public ObjectResult UpdateGuidedPath([FromBody] GuidedPathDTO guidedPathDTO) + { + try + { + if (guidedPathDTO == null) + throw new ArgumentNullException("GuidedPath param is null"); + + var existingGuidedPath = _myInfoMateDbContext.GuidedPaths + .Include(gp => gp.Steps).ThenInclude(s => s.QuizQuestions) + .FirstOrDefault(gp => gp.Id == guidedPathDTO.id); + if (existingGuidedPath == null) + throw new KeyNotFoundException("GuidedPath does not exist"); + + existingGuidedPath.InstanceId = guidedPathDTO.instanceId; + existingGuidedPath.Title = guidedPathDTO.title ?? new List(); + existingGuidedPath.Description = guidedPathDTO.description ?? new List(); + existingGuidedPath.IsLinear = guidedPathDTO.isLinear; + existingGuidedPath.RequireSuccessToAdvance = guidedPathDTO.requireSuccessToAdvance; + existingGuidedPath.HideNextStepsUntilComplete = guidedPathDTO.hideNextStepsUntilComplete; + existingGuidedPath.EstimatedDurationMinutes = guidedPathDTO.estimatedDurationMinutes; + existingGuidedPath.ImageResourceId = guidedPathDTO.imageResourceId; + existingGuidedPath.Order = guidedPathDTO.order.GetValueOrDefault(); + existingGuidedPath.IsGameMode = guidedPathDTO.isGameMode; + existingGuidedPath.GameMessageDebut = guidedPathDTO.gameMessageDebut ?? new List(); + existingGuidedPath.GameMessageFin = guidedPathDTO.gameMessageFin ?? new List(); + + // Sync steps + var dtoStepIds = (guidedPathDTO.steps ?? new List()) + .Where(s => s.id != null) + .Select(s => s.id) + .ToHashSet(); + + var stepsToRemove = existingGuidedPath.Steps + .Where(s => !dtoStepIds.Contains(s.Id)) + .ToList(); + foreach (var s in stepsToRemove) + existingGuidedPath.Steps.Remove(s); + + foreach (var stepDTO in guidedPathDTO.steps ?? new List()) + { + var existing = existingGuidedPath.Steps.FirstOrDefault(s => s.Id == stepDTO.id); + if (existing != null) + { + existing.FromDTO(stepDTO); + existing.GuidedPathId = existingGuidedPath.Id; + } + else + { + var newStep = new GuidedStep().FromDTO(stepDTO); + newStep.Id = idService.GenerateHexId(); + newStep.GuidedPathId = existingGuidedPath.Id; + existingGuidedPath.Steps.Add(newStep); + } + } + + _myInfoMateDbContext.SaveChanges(); + + return new OkObjectResult(existingGuidedPath.ToDTO()); + } + catch (ArgumentNullException ex) + { + return new BadRequestObjectResult(ex.Message); + } + catch (KeyNotFoundException ex) + { + return new NotFoundObjectResult(ex.Message); + } + catch (Exception ex) + { + return new ObjectResult(ex.Message) { StatusCode = 500 }; + } + } + + /// + /// Delete a guided path + /// + [ProducesResponseType(typeof(string), 202)] + [ProducesResponseType(typeof(string), 404)] + [ProducesResponseType(typeof(string), 500)] + [HttpDelete("guided-path/{guidedPathId}")] + public ObjectResult DeleteGuidedPath(string guidedPathId) + { + try + { + var guidedPath = _myInfoMateDbContext.GuidedPaths.Include(gp => gp.Steps).FirstOrDefault(gp => gp.Id == guidedPathId); + if (guidedPath == null) + throw new KeyNotFoundException("GuidedPath does not exist"); + + _myInfoMateDbContext.RemoveRange(guidedPath.Steps); + _myInfoMateDbContext.Remove(guidedPath); + _myInfoMateDbContext.SaveChanges(); + + return new ObjectResult("The guidedPath has been deleted") { StatusCode = 202 }; + } + catch (KeyNotFoundException ex) + { + return new NotFoundObjectResult(ex.Message); + } + catch (Exception ex) + { + return new ObjectResult(ex.Message) { StatusCode = 500 }; + } + } + + /// + /// Get all steps from a guided path + /// + [AllowAnonymous] + [ProducesResponseType(typeof(List), 200)] + [ProducesResponseType(typeof(string), 404)] + [ProducesResponseType(typeof(string), 500)] + [HttpGet("guided-path/{guidedPathId}/guided-step")] + public ObjectResult GetAllGuidedStepFromGuidedPath(string guidedPathId) + { + try + { + List guidedSteps = _myInfoMateDbContext.GuidedSteps + .Include(gs => gs.QuizQuestions) + .Where(gs => gs.GuidedPathId == guidedPathId) + .OrderBy(gs => gs.Order) + .ToList(); + + return new OkObjectResult(guidedSteps.Select(gs => gs.ToDTO())); + } + catch (KeyNotFoundException ex) + { + return new NotFoundObjectResult(ex.Message); + } + catch (Exception ex) + { + return new ObjectResult(ex.Message) { StatusCode = 500 }; + } + } + + /// + /// Create a new step in a guided path + /// + [ProducesResponseType(typeof(GuidedStepDTO), 200)] + [ProducesResponseType(typeof(string), 400)] + [ProducesResponseType(typeof(string), 404)] + [ProducesResponseType(typeof(string), 500)] + [HttpPost("guided-path/{guidedPathId}/guided-step")] + public ObjectResult CreateGuidedStep(string guidedPathId, [FromBody] GuidedStepDTO guidedStepDTO) + { + try + { + if (guidedPathId == null) + throw new ArgumentNullException("GuidedPathId param is null"); + + if (guidedStepDTO == null) + throw new ArgumentNullException("GuidedStep param is null"); + + var guidedPath = _myInfoMateDbContext.GuidedPaths.FirstOrDefault(gp => gp.Id == guidedPathId); + if (guidedPath == null) + throw new KeyNotFoundException("GuidedPath does not exist"); + + GuidedStep guidedStep = new GuidedStep().FromDTO(guidedStepDTO); + guidedStep.Id = idService.GenerateHexId(); + guidedStep.GuidedPathId = guidedPathId; + guidedStep.Order = _myInfoMateDbContext.GuidedSteps.Count(gs => gs.GuidedPathId == guidedPathId); + + _myInfoMateDbContext.GuidedSteps.Add(guidedStep); + _myInfoMateDbContext.SaveChanges(); + + return new OkObjectResult(guidedStep.ToDTO()); + } + catch (ArgumentNullException ex) + { + return new BadRequestObjectResult(ex.Message); + } + catch (KeyNotFoundException ex) + { + return new NotFoundObjectResult(ex.Message); + } + catch (Exception ex) + { + return new ObjectResult(ex.Message) { StatusCode = 500 }; + } + } + + /// + /// Update a guided step + /// + [ProducesResponseType(typeof(GuidedStepDTO), 200)] + [ProducesResponseType(typeof(string), 400)] + [ProducesResponseType(typeof(string), 404)] + [ProducesResponseType(typeof(string), 500)] + [HttpPut("guided-step")] + public ObjectResult UpdateGuidedStep([FromBody] GuidedStepDTO guidedStepDTO) + { + try + { + if (guidedStepDTO == null) + throw new ArgumentNullException("GuidedStep param is null"); + + var existingGuidedStep = _myInfoMateDbContext.GuidedSteps + .Include(gs => gs.QuizQuestions) + .FirstOrDefault(gs => gs.Id == guidedStepDTO.id); + if (existingGuidedStep == null) + throw new KeyNotFoundException("GuidedStep does not exist"); + + existingGuidedStep.FromDTO(guidedStepDTO); + + _myInfoMateDbContext.SaveChanges(); + + return new OkObjectResult(existingGuidedStep.ToDTO()); + } + catch (ArgumentNullException ex) + { + return new BadRequestObjectResult(ex.Message); + } + catch (KeyNotFoundException ex) + { + return new NotFoundObjectResult(ex.Message); + } + catch (Exception ex) + { + return new ObjectResult(ex.Message) { StatusCode = 500 }; + } + } + + /// + /// Delete a guided step + /// + [ProducesResponseType(typeof(string), 202)] + [ProducesResponseType(typeof(string), 404)] + [ProducesResponseType(typeof(string), 500)] + [HttpDelete("guided-step/{guidedStepId}")] + public ObjectResult DeleteGuidedStep(string guidedStepId) + { + try + { + var guidedStep = _myInfoMateDbContext.GuidedSteps.Include(gs => gs.QuizQuestions).FirstOrDefault(gs => gs.Id == guidedStepId); + if (guidedStep == null) + throw new KeyNotFoundException("GuidedStep does not exist"); + + guidedStep.QuizQuestions?.Clear(); + _myInfoMateDbContext.Remove(guidedStep); + _myInfoMateDbContext.SaveChanges(); + + return new ObjectResult("The guidedStep has been deleted") { StatusCode = 202 }; + } + catch (KeyNotFoundException ex) + { + return new NotFoundObjectResult(ex.Message); + } + catch (Exception ex) + { + return new ObjectResult(ex.Message) { StatusCode = 500 }; + } + } + } +} diff --git a/ManagerService/DTOs/AiChatDTO.cs b/ManagerService/DTOs/AiChatDTO.cs index 63206d8..9c1f2c3 100644 --- a/ManagerService/DTOs/AiChatDTO.cs +++ b/ManagerService/DTOs/AiChatDTO.cs @@ -11,6 +11,12 @@ namespace ManagerService.DTOs public string ConfigurationId { get; set; } // null = scope instance, fourni = scope configuration public string Language { get; set; } public List History { get; set; } = new(); + /// + /// true = interaction vocale (lunettes, assistant vocal). + /// Le backend adapte le prompt : pas de markdown, pas de navigation, + /// dates en toutes lettres, réponses courtes audio-friendly. + /// + public bool IsVoice { get; set; } = false; } public class AiChatMessage @@ -39,6 +45,11 @@ namespace ManagerService.DTOs public string Reply { get; set; } public List? Cards { get; set; } public NavigationActionDTO? Navigation { get; set; } + /// + /// false = le LLM ne s'attend pas à une réponse du visiteur (info pure, hors-sujet, politesse, beacon). + /// Le front peut désactiver l'écoute automatique après le TTS. + /// + public bool ExpectsReply { get; set; } = true; } public class AiTranslateRequest diff --git a/ManagerService/DTOs/GuidedPathDTO.cs b/ManagerService/DTOs/GuidedPathDTO.cs index b11b612..3ef913d 100644 --- a/ManagerService/DTOs/GuidedPathDTO.cs +++ b/ManagerService/DTOs/GuidedPathDTO.cs @@ -8,13 +8,18 @@ namespace ManagerService.DTOs public string instanceId { get; set; } public List title { get; set; } public List description { get; set; } - public string? sectionMapId { get; set; } public string? sectionEventId { get; set; } - public string? sectionGameId { get; set; } + public string? sectionParcoursId { get; set; } public bool isLinear { get; set; } public bool requireSuccessToAdvance { get; set; } public bool hideNextStepsUntilComplete { get; set; } + public int? estimatedDurationMinutes { get; set; } + public string? imageResourceId { get; set; } + public string? imageUrl { get; set; } public int? order { get; set; } + public bool isGameMode { get; set; } + public List gameMessageDebut { get; set; } + public List gameMessageFin { get; set; } public List steps { get; set; } } } diff --git a/ManagerService/DTOs/GuidedStepDTO.cs b/ManagerService/DTOs/GuidedStepDTO.cs index 9a08e91..3230d27 100644 --- a/ManagerService/DTOs/GuidedStepDTO.cs +++ b/ManagerService/DTOs/GuidedStepDTO.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using Manager.DTOs; using ManagerService.Data.SubSection; +using ManagerService.DTOs; namespace ManagerService.DTOs { @@ -12,10 +13,12 @@ namespace ManagerService.DTOs public List title { get; set; } public List description { get; set; } public GeometryDTO geometry { get; set; } + public bool isGeoTriggered { get; set; } public double? zoneRadiusMeters { get; set; } public string imageUrl { get; set; } - public int? triggerGeoPointId { get; set; } - public GeoPointDTO? triggerGeoPoint { get; set; } + public List audioIds { get; set; } + public List contents { get; set; } + public List factContent { get; set; } public bool isHiddenInitially { get; set; } public bool isStepTimer { get; set; } public bool isStepLocked { get; set; } diff --git a/ManagerService/DTOs/SectionType.cs b/ManagerService/DTOs/SectionType.cs index b76fbde..6ad6770 100644 --- a/ManagerService/DTOs/SectionType.cs +++ b/ManagerService/DTOs/SectionType.cs @@ -13,6 +13,7 @@ Game, Agenda, Weather, - Event + Event, + Parcours } } diff --git a/ManagerService/DTOs/SubSection/GameDTO.cs b/ManagerService/DTOs/SubSection/GameDTO.cs index b4ffe3a..3e795ae 100644 --- a/ManagerService/DTOs/SubSection/GameDTO.cs +++ b/ManagerService/DTOs/SubSection/GameDTO.cs @@ -13,6 +13,5 @@ namespace Manager.DTOs public int rows { get; set; } = 3; public int cols { get; set; } = 3; public GameTypes gameType { get; set; } = GameTypes.Puzzle; - public List guidedPaths { get; set; } } } diff --git a/ManagerService/DTOs/SubSection/ParcoursDTO.cs b/ManagerService/DTOs/SubSection/ParcoursDTO.cs new file mode 100644 index 0000000..1fa4e9c --- /dev/null +++ b/ManagerService/DTOs/SubSection/ParcoursDTO.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; + +namespace ManagerService.DTOs +{ + public class ParcoursDTO : SectionDTO + { + public bool showMap { get; set; } + public string? baseSectionMapId { get; set; } + public List guidedPaths { get; set; } + } +} diff --git a/ManagerService/DTOs/SubSection/SectionEventDTO.cs b/ManagerService/DTOs/SubSection/SectionEventDTO.cs index 4e31a32..a9006ec 100644 --- a/ManagerService/DTOs/SubSection/SectionEventDTO.cs +++ b/ManagerService/DTOs/SubSection/SectionEventDTO.cs @@ -7,8 +7,8 @@ namespace Manager.DTOs { public class SectionEventDTO : SectionDTO { - public DateTime StartDate { get; set; } - public DateTime EndDate { get; set; } + public DateTime? StartDate { get; set; } + public DateTime? EndDate { get; set; } public string? BaseSectionMapId { get; set; } public List ParcoursIds { get; set; } public List GlobalMapAnnotations { get; set; } = new(); diff --git a/ManagerService/Data/ApplicationInstance.cs b/ManagerService/Data/ApplicationInstance.cs index a16a507..2e98260 100644 --- a/ManagerService/Data/ApplicationInstance.cs +++ b/ManagerService/Data/ApplicationInstance.cs @@ -105,7 +105,8 @@ namespace ManagerService.Data Mobile, Tablet, Web, - VR + VR, + Voice // Lunettes Ray-Ban Meta / interface vocale — pas de navigation UI } public enum LayoutMainPageType diff --git a/ManagerService/Data/MyInfoMateDbContext.cs b/ManagerService/Data/MyInfoMateDbContext.cs index f6943ab..5566d95 100644 --- a/ManagerService/Data/MyInfoMateDbContext.cs +++ b/ManagerService/Data/MyInfoMateDbContext.cs @@ -186,7 +186,8 @@ namespace ManagerService.Data .HasValue("Slider") .HasValue("Video") .HasValue("Weather") - .HasValue("Web"); + .HasValue("Web") + .HasValue("Parcours"); /*modelBuilder.Entity(entity => { @@ -283,6 +284,20 @@ namespace ManagerService.Data v => JsonSerializer.Serialize(v, options), v => JsonSerializer.Deserialize>(v, options)); + modelBuilder.Entity() + .Property(gp => gp.GameMessageDebut) + .HasColumnType("jsonb") + .HasConversion( + v => JsonSerializer.Serialize(v, options), + v => JsonSerializer.Deserialize>(v, options)); + + modelBuilder.Entity() + .Property(gp => gp.GameMessageFin) + .HasColumnType("jsonb") + .HasConversion( + v => JsonSerializer.Serialize(v, options), + v => JsonSerializer.Deserialize>(v, options)); + // Configurations JSON pour GuidedStep modelBuilder.Entity() .Property(gs => gs.Title) @@ -305,6 +320,43 @@ namespace ManagerService.Data v => JsonSerializer.Serialize(v, options), v => JsonSerializer.Deserialize>(v, options)); + modelBuilder.Entity() + .Property(gs => gs.AudioIds) + .HasColumnType("jsonb") + .HasConversion( + v => JsonSerializer.Serialize(v, options), + v => JsonSerializer.Deserialize>(v, options)); + + modelBuilder.Entity() + .Property(gs => gs.Contents) + .HasColumnType("jsonb") + .HasConversion( + v => JsonSerializer.Serialize(v, options), + v => JsonSerializer.Deserialize>(v, options)); + + modelBuilder.Entity() + .Property(gs => gs.FactContent) + .HasColumnType("jsonb") + .HasConversion( + v => JsonSerializer.Serialize(v, options), + v => JsonSerializer.Deserialize>(v, options)); + + // SectionParcours: lien optionnel vers une SectionMap de base + modelBuilder.Entity() + .HasOne(sp => sp.BaseMap) + .WithMany() + .HasForeignKey(sp => sp.BaseSectionMapId) + .IsRequired(false) + .OnDelete(DeleteBehavior.SetNull); + + // GuidedPath: lien vers SectionParcours + modelBuilder.Entity() + .HasOne(gp => gp.SectionParcours) + .WithMany(sp => sp.GuidedPaths) + .HasForeignKey(gp => gp.SectionParcoursId) + .IsRequired(false) + .OnDelete(DeleteBehavior.Cascade); + modelBuilder.Entity() .Property(s => s.Label) .HasColumnType("jsonb") diff --git a/ManagerService/Data/SubSection/GuidedPath.cs b/ManagerService/Data/SubSection/GuidedPath.cs index 9b5bffc..dadb10f 100644 --- a/ManagerService/Data/SubSection/GuidedPath.cs +++ b/ManagerService/Data/SubSection/GuidedPath.cs @@ -1,4 +1,5 @@ -using ManagerService.DTOs; +using ManagerService.Data; +using ManagerService.DTOs; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations; @@ -21,28 +22,38 @@ namespace ManagerService.Data.SubSection [Column(TypeName = "jsonb")] public List Description { get; set; } - // Lié à une carte (optionnel) - public string? SectionMapId { get; set; } - [ForeignKey("SectionMapId")] - public SectionMap? SectionMap { get; set; } - // Lié à un événement (optionnel) public string? SectionEventId { get; set; } [ForeignKey("SectionEventId")] public SectionEvent? SectionEvent { get; set; } - // Lié à une section game (optionnel) => Escape game / geolocalisé ou non - public string? SectionGameId { get; set; } - [ForeignKey("SectionGameId")] - public SectionGame? SectionGame { get; set; } + // Lié à une section parcours (FK principale) + public string? SectionParcoursId { get; set; } + [ForeignKey("SectionParcoursId")] + public SectionParcours? SectionParcours { get; set; } // Type de parcours public bool IsLinear { get; set; } = true; // Avancer dans l’ordre - Ordre obligatoire public bool RequireSuccessToAdvance { get; set; } = false; // Par exemple: résoudre une énigme public bool HideNextStepsUntilComplete { get; set; } = false; // Étapes cachées tant que non terminées + public int? EstimatedDurationMinutes { get; set; } + + public string? ImageResourceId { get; set; } + + [ForeignKey("ImageResourceId")] + public Resource? ImageResource { get; set; } + public int Order { get; set; } + public bool IsGameMode { get; set; } = false; + + [Column(TypeName = "jsonb")] + public List GameMessageDebut { get; set; } = new(); + + [Column(TypeName = "jsonb")] + public List GameMessageFin { get; set; } = new(); + public List Steps { get; set; } = new(); public GuidedPathDTO ToDTO() @@ -53,13 +64,19 @@ namespace ManagerService.Data.SubSection instanceId = InstanceId, title = Title, description = Description, - sectionMapId = SectionMapId, sectionEventId = SectionEventId, + sectionParcoursId = SectionParcoursId, isLinear = IsLinear, requireSuccessToAdvance = RequireSuccessToAdvance, hideNextStepsUntilComplete = HideNextStepsUntilComplete, + estimatedDurationMinutes = EstimatedDurationMinutes, + imageResourceId = ImageResourceId, + imageUrl = ImageResource?.Url, order = Order, - steps = Steps?.Select(s => s.ToDTO()).ToList() + isGameMode = IsGameMode, + gameMessageDebut = GameMessageDebut, + gameMessageFin = GameMessageFin, + steps = Steps?.OrderBy(s => s.Order).Select(s => s.ToDTO()).ToList() }; } @@ -70,12 +87,17 @@ namespace ManagerService.Data.SubSection InstanceId = dto.instanceId; Title = dto.title ?? new List(); Description = dto.description ?? new List(); - SectionMapId = dto.sectionMapId; SectionEventId = dto.sectionEventId; + SectionParcoursId = dto.sectionParcoursId; IsLinear = dto.isLinear; RequireSuccessToAdvance = dto.requireSuccessToAdvance; HideNextStepsUntilComplete = dto.hideNextStepsUntilComplete; + EstimatedDurationMinutes = dto.estimatedDurationMinutes; + ImageResourceId = dto.imageResourceId; Order = dto.order.GetValueOrDefault(); + IsGameMode = dto.isGameMode; + GameMessageDebut = dto.gameMessageDebut ?? new List(); + GameMessageFin = dto.gameMessageFin ?? new List(); //Steps = dto.Steps?.Select(s => s.FromDTO()).ToList() ?? new List() // Other method return this; } diff --git a/ManagerService/Data/SubSection/GuidedStep.cs b/ManagerService/Data/SubSection/GuidedStep.cs index 83cc4ca..8bc8b6a 100644 --- a/ManagerService/Data/SubSection/GuidedStep.cs +++ b/ManagerService/Data/SubSection/GuidedStep.cs @@ -1,8 +1,10 @@ -using ManagerService.DTOs; +using Manager.DTOs; +using ManagerService.DTOs; using NetTopologySuite.Geometries; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations; +using System.Linq; using ManagerService.Helpers; namespace ManagerService.Data.SubSection @@ -29,14 +31,22 @@ namespace ManagerService.Data.SubSection public Geometry? Geometry { get; set; } // Polygon ou centre du cercle + // Option : si true, cette étape doit être atteinte géographiquement (zone/rayon) pour être validée + public bool IsGeoTriggered { get; set; } = false; + public double? ZoneRadiusMeters { get; set; } // Optionnel, utile si zone cercle ou point public string ImageUrl { get; set; } - public int? TriggerGeoPointId { get; set; } // Lieu lié à l'étape et genre si on veut plus d'info ou pourrait afficher les infos du geopoint. + // Rich content (calqué sur SectionArticle) + [Column(TypeName = "jsonb")] + public List AudioIds { get; set; } = new(); - [ForeignKey("TriggerGeoPointId")] - public GeoPoint? TriggerGeoPoint { get; set; } + [Column(TypeName = "jsonb")] + public List Contents { get; set; } = new(); + + [Column(TypeName = "jsonb")] + public List FactContent { get; set; } = new(); public bool IsHiddenInitially { get; set; } = false; @@ -66,10 +76,12 @@ namespace ManagerService.Data.SubSection title = Title, description = Description, geometry = Geometry?.ToDto(), + isGeoTriggered = IsGeoTriggered, zoneRadiusMeters = ZoneRadiusMeters, imageUrl = ImageUrl, - triggerGeoPointId = TriggerGeoPointId, - triggerGeoPoint = TriggerGeoPoint?.ToDTO(), + audioIds = AudioIds, + contents = Contents, + factContent = FactContent, isHiddenInitially = IsHiddenInitially, isStepTimer = IsStepTimer, isStepLocked = IsStepLocked, @@ -87,19 +99,58 @@ namespace ManagerService.Data.SubSection Title = dto.title ?? new List(); Description = dto.description ?? new List(); Geometry = dto.geometry.FromDto(); + IsGeoTriggered = dto.isGeoTriggered; ZoneRadiusMeters = dto.zoneRadiusMeters; ImageUrl = dto.imageUrl; - TriggerGeoPointId = dto.triggerGeoPointId; + AudioIds = dto.audioIds ?? new List(); + Contents = dto.contents ?? new List(); + FactContent = dto.factContent ?? new List(); IsHiddenInitially = dto.isHiddenInitially; IsStepTimer = dto.isStepTimer; IsStepLocked = dto.isStepLocked; TimerSeconds = dto.timerSeconds; TimerExpiredMessage = dto.timerExpiredMessage ?? new List(); Order = dto.order.GetValueOrDefault(); - QuizQuestions = dto.quizQuestions; + SyncQuizQuestions(dto.quizQuestions); return this; } + // Merges incoming quiz questions into the tracked QuizQuestions collection by Id, + // instead of replacing the reference outright — a blind reassignment would make EF + // treat every incoming question (including already-persisted ones) as a brand new + // row to insert, which fails with a duplicate key error as soon as a step already + // has a saved question and a new one is added alongside it. + public void SyncQuizQuestions(List? dtoQuestions) + { + dtoQuestions ??= new List(); + QuizQuestions ??= new List(); + + var dtoIds = dtoQuestions.Where(q => q.Id != 0).Select(q => q.Id).ToHashSet(); + QuizQuestions.RemoveAll(q => !dtoIds.Contains(q.Id)); + + foreach (var qDto in dtoQuestions) + { + var existing = qDto.Id != 0 ? QuizQuestions.FirstOrDefault(q => q.Id == qDto.Id) : null; + if (existing != null) + { + existing.Label = qDto.Label; + existing.ResourceId = qDto.ResourceId; + existing.Order = qDto.Order; + existing.Responses = qDto.Responses; + existing.ValidationQuestionType = qDto.ValidationQuestionType; + existing.PuzzleImageId = qDto.PuzzleImageId; + existing.PuzzleRows = qDto.PuzzleRows; + existing.PuzzleCols = qDto.PuzzleCols; + existing.IsSlidingPuzzle = qDto.IsSlidingPuzzle; + } + else + { + qDto.Id = 0; + QuizQuestions.Add(qDto); + } + } + } + } } diff --git a/ManagerService/Data/SubSection/QuizQuestion.cs b/ManagerService/Data/SubSection/QuizQuestion.cs index 6df62f9..1ea7c2f 100644 --- a/ManagerService/Data/SubSection/QuizQuestion.cs +++ b/ManagerService/Data/SubSection/QuizQuestion.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; +using System.Text.Json.Serialization; namespace ManagerService.Data.SubSection { @@ -33,13 +34,16 @@ namespace ManagerService.Data.SubSection public string? SectionQuizId { get; set; } [ForeignKey("SectionQuizId")] + [JsonIgnore] public SectionQuiz? SectionQuiz { get; set; } public string? GuidedStepId { get; set; } [ForeignKey("GuidedStepId")] + [JsonIgnore] public GuidedStep? GuidedStep { get; set; } + [System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Serialization.JsonNumberEnumConverter))] public QuestionType ValidationQuestionType { get; set; } = QuestionType.Simple; public string? PuzzleImageId { get; set; } @@ -49,7 +53,7 @@ namespace ManagerService.Data.SubSection public int? PuzzleCols { get; set; } = 3; - public bool IsSlidingPuzzle { get; set; } = false; + public bool? IsSlidingPuzzle { get; set; } = false; // TODO /*public TranslationDTO ToDTO() diff --git a/ManagerService/Data/SubSection/SectionEvent.cs b/ManagerService/Data/SubSection/SectionEvent.cs index b738ef8..4c8b4b7 100644 --- a/ManagerService/Data/SubSection/SectionEvent.cs +++ b/ManagerService/Data/SubSection/SectionEvent.cs @@ -15,8 +15,8 @@ namespace ManagerService.Data.SubSection /// public class SectionEvent : Section { - public DateTime StartDate { get; set; } - public DateTime EndDate { get; set; } + public DateTime? StartDate { get; set; } + public DateTime? EndDate { get; set; } public string? BaseSectionMapId { get; set; } [ForeignKey(nameof(BaseSectionMapId))] public SectionMap? BaseMap { get; set; } diff --git a/ManagerService/Data/SubSection/SectionGame.cs b/ManagerService/Data/SubSection/SectionGame.cs index fa9e2a9..3abb4dc 100644 --- a/ManagerService/Data/SubSection/SectionGame.cs +++ b/ManagerService/Data/SubSection/SectionGame.cs @@ -68,8 +68,7 @@ namespace ManagerService.Data.SubSection public enum GameTypes { Puzzle, - SlidingPuzzle, - Escape + SlidingPuzzle } } } diff --git a/ManagerService/Data/SubSection/SectionParcours.cs b/ManagerService/Data/SubSection/SectionParcours.cs new file mode 100644 index 0000000..8a6fb1b --- /dev/null +++ b/ManagerService/Data/SubSection/SectionParcours.cs @@ -0,0 +1,48 @@ +using Manager.DTOs; +using ManagerService.DTOs; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; + +namespace ManagerService.Data.SubSection +{ + public class SectionParcours : Section + { + public bool ShowMap { get; set; } = true; + + public string? BaseSectionMapId { get; set; } + [ForeignKey(nameof(BaseSectionMapId))] + public SectionMap? BaseMap { get; set; } + + public List GuidedPaths { get; set; } = new(); + + public ParcoursDTO ToDTO() + { + return new ParcoursDTO + { + id = Id, + label = Label, + title = Title.ToList(), + description = Description.ToList(), + order = Order, + type = Type, + imageId = ImageId, + imageSource = ImageSource, + configurationId = ConfigurationId, + isSubSection = IsSubSection, + parentId = ParentId, + dateCreation = DateCreation, + instanceId = InstanceId, + isBeacon = IsBeacon, + beaconId = BeaconId, + latitude = Latitude, + longitude = Longitude, + meterZoneGPS = MeterZoneGPS, + showMap = ShowMap, + baseSectionMapId = BaseSectionMapId, + guidedPaths = GuidedPaths?.Select(gp => gp.ToDTO()).ToList() + }; + } + } +} diff --git a/ManagerService/Helpers/GeometryMapper.cs b/ManagerService/Helpers/GeometryMapper.cs index 302d255..793637d 100644 --- a/ManagerService/Helpers/GeometryMapper.cs +++ b/ManagerService/Helpers/GeometryMapper.cs @@ -41,10 +41,11 @@ namespace ManagerService.Helpers }; } - public static Geometry FromDto(this GeometryDTO dto, GeometryFactory factory = null!) + public static Geometry? FromDto(this GeometryDTO? dto, GeometryFactory factory = null!) { - factory ??= NtsGeometryServices.Instance.CreateGeometryFactory(srid: 4326); + if (dto == null) return null; + factory ??= NtsGeometryServices.Instance.CreateGeometryFactory(srid: 4326); return dto.type switch { diff --git a/ManagerService/Migrations/20260510203954_MakeIsSlidingPuzzleNullable.Designer.cs b/ManagerService/Migrations/20260510203954_MakeIsSlidingPuzzleNullable.Designer.cs new file mode 100644 index 0000000..4272d8a --- /dev/null +++ b/ManagerService/Migrations/20260510203954_MakeIsSlidingPuzzleNullable.Designer.cs @@ -0,0 +1,1584 @@ +// +using System; +using System.Collections.Generic; +using Manager.DTOs; +using ManagerService.DTOs; +using ManagerService.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using NetTopologySuite.Geometries; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace ManagerService.Migrations +{ + [DbContext(typeof(MyInfoMateDbContext))] + [Migration("20260510203954_MakeIsSlidingPuzzleNullable")] + partial class MakeIsSlidingPuzzleNullable + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.2") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "postgis"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("ManagerService.Data.ApiKey", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AppType") + .HasColumnType("integer"); + + b.Property("DateCreation") + .HasColumnType("timestamp with time zone"); + + b.Property("DateExpiration") + .HasColumnType("timestamp with time zone"); + + b.Property("InstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("KeyHash") + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId"); + + b.ToTable("ApiKeys"); + }); + + modelBuilder.Entity("ManagerService.Data.AppConfigurationLink", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("ApplicationInstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("ConfigurationId") + .IsRequired() + .HasColumnType("text"); + + b.Property("DeviceId") + .HasColumnType("text"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsDate") + .HasColumnType("boolean"); + + b.Property("IsHour") + .HasColumnType("boolean"); + + b.Property("IsSectionImageBackground") + .HasColumnType("boolean"); + + b.Property("LayoutMainPage") + .HasColumnType("integer"); + + b.Property("LoaderImageId") + .HasColumnType("text"); + + b.Property("LoaderImageUrl") + .HasColumnType("text"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("PrimaryColor") + .HasColumnType("text"); + + b.Property("RoundedValue") + .HasColumnType("integer"); + + b.Property("ScreenPercentageSectionsMainPage") + .HasColumnType("integer"); + + b.Property("SecondaryColor") + .HasColumnType("text"); + + b.Property("WeightMasonryGrid") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationInstanceId"); + + b.HasIndex("ConfigurationId"); + + b.HasIndex("DeviceId"); + + b.ToTable("AppConfigurationLinks"); + }); + + modelBuilder.Entity("ManagerService.Data.ApplicationInstance", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AppType") + .HasColumnType("integer"); + + b.Property("InstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsAssistant") + .HasColumnType("boolean"); + + b.PrimitiveCollection>("Languages") + .HasColumnType("text[]"); + + b.Property("LayoutMainPage") + .HasColumnType("integer"); + + b.Property("LoaderImageId") + .HasColumnType("text"); + + b.Property("LoaderImageUrl") + .HasColumnType("text"); + + b.Property("MainImageId") + .HasColumnType("text"); + + b.Property("MainImageUrl") + .HasColumnType("text"); + + b.Property("PrimaryColor") + .HasColumnType("text"); + + b.Property("SecondaryColor") + .HasColumnType("text"); + + b.Property("SectionEventId") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("SectionEventId"); + + b.ToTable("ApplicationInstances"); + }); + + modelBuilder.Entity("ManagerService.Data.AuditLog", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("Action") + .HasColumnType("text"); + + b.Property("EntityId") + .HasColumnType("text"); + + b.Property("EntityType") + .HasColumnType("text"); + + b.Property("InstanceId") + .HasColumnType("text"); + + b.Property("NewValues") + .HasColumnType("text"); + + b.Property("OldValues") + .HasColumnType("text"); + + b.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("AuditLogs"); + }); + + modelBuilder.Entity("ManagerService.Data.Configuration", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("DateCreation") + .HasColumnType("timestamp with time zone"); + + b.Property("ImageId") + .HasColumnType("text"); + + b.Property("ImageSource") + .HasColumnType("text"); + + b.Property("InstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsOffline") + .HasColumnType("boolean"); + + b.Property("IsQRCode") + .HasColumnType("boolean"); + + b.Property("IsSearchNumber") + .HasColumnType("boolean"); + + b.Property("IsSearchText") + .HasColumnType("boolean"); + + b.Property("Label") + .IsRequired() + .HasColumnType("text"); + + b.PrimitiveCollection>("Languages") + .HasColumnType("text[]"); + + b.Property("LoaderImageId") + .HasColumnType("text"); + + b.Property("LoaderImageUrl") + .HasColumnType("text"); + + b.Property("PrimaryColor") + .HasColumnType("text"); + + b.Property("SecondaryColor") + .HasColumnType("text"); + + b.Property("Title") + .IsRequired() + .HasColumnType("jsonb"); + + b.HasKey("Id"); + + b.ToTable("Configurations"); + }); + + modelBuilder.Entity("ManagerService.Data.Device", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AppVersion") + .HasColumnType("text"); + + b.Property("BatteryLevel") + .HasColumnType("text"); + + b.Property("ConfigurationId") + .IsRequired() + .HasColumnType("text"); + + b.Property("Connected") + .HasColumnType("boolean"); + + b.Property("ConnectionLevel") + .HasColumnType("text"); + + b.Property("DateCreation") + .HasColumnType("timestamp with time zone"); + + b.Property("DateUpdate") + .HasColumnType("timestamp with time zone"); + + b.Property("Identifier") + .HasColumnType("text"); + + b.Property("InstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("IpAddressETH") + .HasColumnType("text"); + + b.Property("IpAddressWLAN") + .HasColumnType("text"); + + b.Property("LastBatteryLevel") + .HasColumnType("timestamp with time zone"); + + b.Property("LastConnectionLevel") + .HasColumnType("timestamp with time zone"); + + b.Property("LastSeen") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ConfigurationId"); + + b.ToTable("Devices"); + }); + + modelBuilder.Entity("ManagerService.Data.Instance", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AiRequestsPerMonth") + .HasColumnType("integer"); + + b.Property("AiRequestsThisMonth") + .HasColumnType("integer"); + + b.Property("AiUsageMonthKey") + .HasColumnType("text"); + + b.Property("DateCreation") + .HasColumnType("timestamp with time zone"); + + b.Property("HasAdvancedStats") + .HasColumnType("boolean"); + + b.Property("HasStats") + .HasColumnType("boolean"); + + b.Property("IsAssistant") + .HasColumnType("boolean"); + + b.Property("IsMobile") + .HasColumnType("boolean"); + + b.Property("IsPushNotification") + .HasColumnType("boolean"); + + b.Property("IsTablet") + .HasColumnType("boolean"); + + b.Property("IsVR") + .HasColumnType("boolean"); + + b.Property("IsWeb") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("PinCode") + .HasColumnType("text"); + + b.Property("PublicApiKey") + .HasColumnType("text"); + + b.Property("StatsHistoryDays") + .HasColumnType("integer"); + + b.Property("StorageQuotaBytes") + .HasColumnType("bigint"); + + b.Property("SubscriptionPlanId") + .HasColumnType("text"); + + b.Property("WebSlug") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("SubscriptionPlanId"); + + b.ToTable("Instances"); + }); + + modelBuilder.Entity("ManagerService.Data.PushNotification", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("Body") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreation") + .HasColumnType("timestamp with time zone"); + + b.Property("HangfireJobId") + .HasColumnType("text"); + + b.Property("InstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("ScheduledAt") + .HasColumnType("timestamp with time zone"); + + b.Property("SentAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("Topic") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId"); + + b.ToTable("PushNotifications"); + }); + + modelBuilder.Entity("ManagerService.Data.Resource", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("DateCreation") + .HasColumnType("timestamp with time zone"); + + b.Property("InstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("Label") + .IsRequired() + .HasColumnType("text"); + + b.Property("SizeBytes") + .HasColumnType("bigint"); + + b.Property("Type") + .HasColumnType("integer"); + + b.Property("Url") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Resources"); + }); + + modelBuilder.Entity("ManagerService.Data.Section", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("BeaconId") + .HasColumnType("integer"); + + b.Property("ConfigurationId") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreation") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .HasColumnType("jsonb"); + + b.Property("Discriminator") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ImageId") + .HasColumnType("text"); + + b.Property("ImageSource") + .HasColumnType("text"); + + b.Property("InstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsBeacon") + .HasColumnType("boolean"); + + b.Property("IsSubSection") + .HasColumnType("boolean"); + + b.Property("Label") + .IsRequired() + .HasColumnType("text"); + + b.Property("Latitude") + .HasColumnType("text"); + + b.Property("Longitude") + .HasColumnType("text"); + + b.Property("MeterZoneGPS") + .HasColumnType("integer"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("ParentId") + .HasColumnType("text"); + + b.Property("SectionMenuId") + .HasColumnType("text"); + + b.Property("Title") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Type") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("SectionMenuId"); + + b.ToTable("Sections"); + + b.HasDiscriminator().HasValue("Base"); + + b.UseTphMappingStrategy(); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.EventAgenda", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Address") + .HasColumnType("jsonb"); + + b.Property("DateAdded") + .HasColumnType("timestamp with time zone"); + + b.Property("DateFrom") + .HasColumnType("timestamp with time zone"); + + b.Property("DateTo") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Email") + .HasColumnType("text"); + + b.Property("IdVideoYoutube") + .HasColumnType("text"); + + b.Property("IsSynced") + .HasColumnType("boolean"); + + b.Property("Label") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Phone") + .HasColumnType("text"); + + b.Property("ResourceId") + .HasColumnType("text"); + + b.Property("SectionAgendaId") + .HasColumnType("text"); + + b.Property("SectionEventId") + .HasColumnType("text"); + + b.Property("SyncedImageUrl") + .HasColumnType("text"); + + b.Property("Type") + .HasColumnType("text"); + + b.Property("VideoLink") + .HasColumnType("text"); + + b.Property("VideoResourceId") + .HasColumnType("text"); + + b.Property("Website") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ResourceId"); + + b.HasIndex("SectionAgendaId"); + + b.HasIndex("SectionEventId"); + + b.HasIndex("VideoResourceId"); + + b.ToTable("EventAgendas"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.GeoPoint", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CategorieId") + .HasColumnType("integer"); + + b.Property("Contents") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Description") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Email") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Geometry") + .HasColumnType("geometry"); + + b.Property("ImageResourceId") + .HasColumnType("text"); + + b.Property("ImageUrl") + .HasColumnType("text"); + + b.Property("Phone") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("PolyColor") + .HasColumnType("text"); + + b.Property("Prices") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Schedules") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("SectionEventId") + .HasColumnType("text"); + + b.Property("SectionMapId") + .HasColumnType("text"); + + b.Property("Site") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Title") + .IsRequired() + .HasColumnType("jsonb"); + + b.HasKey("Id"); + + b.HasIndex("SectionEventId"); + + b.HasIndex("SectionMapId"); + + b.ToTable("GeoPoints"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.GuidedPath", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("Description") + .HasColumnType("jsonb"); + + b.Property("HideNextStepsUntilComplete") + .HasColumnType("boolean"); + + b.Property("InstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsLinear") + .HasColumnType("boolean"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("RequireSuccessToAdvance") + .HasColumnType("boolean"); + + b.Property("SectionEventId") + .HasColumnType("text"); + + b.Property("SectionGameId") + .HasColumnType("text"); + + b.Property("SectionMapId") + .HasColumnType("text"); + + b.Property("Title") + .IsRequired() + .HasColumnType("jsonb"); + + b.HasKey("Id"); + + b.HasIndex("SectionEventId"); + + b.HasIndex("SectionGameId"); + + b.HasIndex("SectionMapId"); + + b.ToTable("GuidedPaths"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.GuidedStep", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("Description") + .HasColumnType("jsonb"); + + b.Property("Geometry") + .HasColumnType("geometry"); + + b.Property("GuidedPathId") + .IsRequired() + .HasColumnType("text"); + + b.Property("ImageUrl") + .HasColumnType("text"); + + b.Property("IsHiddenInitially") + .HasColumnType("boolean"); + + b.Property("IsStepLocked") + .HasColumnType("boolean"); + + b.Property("IsStepTimer") + .HasColumnType("boolean"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("TimerExpiredMessage") + .HasColumnType("jsonb"); + + b.Property("TimerSeconds") + .HasColumnType("integer"); + + b.Property("Title") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("TriggerGeoPointId") + .HasColumnType("integer"); + + b.Property("ZoneRadiusMeters") + .HasColumnType("double precision"); + + b.HasKey("Id"); + + b.HasIndex("GuidedPathId"); + + b.HasIndex("TriggerGeoPointId"); + + b.ToTable("GuidedSteps"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.QuizQuestion", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("GuidedStepId") + .HasColumnType("text"); + + b.Property("IsSlidingPuzzle") + .HasColumnType("boolean"); + + b.Property>("Label") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("PuzzleCols") + .HasColumnType("integer"); + + b.Property("PuzzleImageId") + .HasColumnType("text"); + + b.Property("PuzzleRows") + .HasColumnType("integer"); + + b.Property("ResourceId") + .HasColumnType("text"); + + b.Property>("Responses") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("SectionQuizId") + .HasColumnType("text"); + + b.Property("ValidationQuestionType") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("GuidedStepId"); + + b.HasIndex("PuzzleImageId"); + + b.HasIndex("ResourceId"); + + b.HasIndex("SectionQuizId"); + + b.ToTable("QuizQuestions"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionEvent+MapAnnotation", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("Geometry") + .HasColumnType("geometry"); + + b.Property("GeometryType") + .HasColumnType("integer"); + + b.Property("Icon") + .HasColumnType("text"); + + b.Property("IconResourceId") + .HasColumnType("text"); + + b.Property>("Label") + .HasColumnType("jsonb"); + + b.Property("PolyColor") + .HasColumnType("text"); + + b.Property("ProgrammeBlockId") + .HasColumnType("text"); + + b.Property("SectionEventId") + .HasColumnType("text"); + + b.Property>("Type") + .HasColumnType("jsonb"); + + b.HasKey("Id"); + + b.HasIndex("IconResourceId"); + + b.HasIndex("ProgrammeBlockId"); + + b.HasIndex("SectionEventId"); + + b.ToTable("MapAnnotations"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionEvent+ProgrammeBlock", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property>("Description") + .HasColumnType("jsonb"); + + b.Property("EndTime") + .HasColumnType("timestamp with time zone"); + + b.Property("SectionEventId") + .HasColumnType("text"); + + b.Property("StartTime") + .HasColumnType("timestamp with time zone"); + + b.Property>("Title") + .HasColumnType("jsonb"); + + b.HasKey("Id"); + + b.HasIndex("SectionEventId"); + + b.ToTable("ProgrammeBlocks"); + }); + + modelBuilder.Entity("ManagerService.Data.SubscriptionPlan", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AiRequestsPerMonth") + .HasColumnType("integer"); + + b.Property("HasAdvancedStats") + .HasColumnType("boolean"); + + b.Property("HasStats") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("StatsHistoryDays") + .HasColumnType("integer"); + + b.Property("StorageQuotaBytes") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.ToTable("SubscriptionPlans"); + + b.HasData( + new + { + Id = "plan-starter", + AiRequestsPerMonth = 0, + HasAdvancedStats = false, + HasStats = false, + Name = "Starter", + StatsHistoryDays = 30, + StorageQuotaBytes = 1073741824L + }, + new + { + Id = "plan-standard", + AiRequestsPerMonth = 500, + HasAdvancedStats = false, + HasStats = true, + Name = "Standard", + StatsHistoryDays = 30, + StorageQuotaBytes = 10737418240L + }, + new + { + Id = "plan-premium", + AiRequestsPerMonth = 2000, + HasAdvancedStats = true, + HasStats = true, + Name = "Premium", + StatsHistoryDays = 0, + StorageQuotaBytes = 53687091200L + }); + }); + + modelBuilder.Entity("ManagerService.Data.User", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("DateCreation") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .IsRequired() + .HasColumnType("text"); + + b.Property("FirstName") + .HasColumnType("text"); + + b.Property("InstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("text"); + + b.Property("Password") + .IsRequired() + .HasColumnType("text"); + + b.Property("Role") + .HasColumnType("integer"); + + b.Property("Token") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("ManagerService.Data.VisitEvent", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AppType") + .HasColumnType("integer"); + + b.Property("ConfigurationId") + .HasColumnType("text"); + + b.Property("DurationSeconds") + .HasColumnType("integer"); + + b.Property("EventType") + .HasColumnType("integer"); + + b.Property("InstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("Language") + .HasColumnType("text"); + + b.Property("Metadata") + .HasColumnType("text"); + + b.Property("SectionId") + .HasColumnType("text"); + + b.Property("SessionId") + .IsRequired() + .HasColumnType("text"); + + b.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId"); + + b.HasIndex("Timestamp"); + + b.ToTable("VisitEvents"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionAgenda", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property("AgendaMapProvider") + .HasColumnType("integer"); + + b.Property>("AgendaResourceIds") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("IsOnlineAgenda") + .HasColumnType("boolean"); + + b.HasDiscriminator().HasValue("Agenda"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionArticle", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property>("ArticleAudioIds") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property>("ArticleContent") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property>("ArticleContents") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("ArticleIsContentTop") + .HasColumnType("boolean"); + + b.Property("ArticleIsReadAudioAuto") + .HasColumnType("boolean"); + + b.HasDiscriminator().HasValue("Article"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionEvent", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property("BaseSectionMapId") + .HasColumnType("text"); + + b.Property("EndDate") + .HasColumnType("timestamp with time zone"); + + b.Property>("ParcoursIds") + .HasColumnType("jsonb"); + + b.Property("StartDate") + .HasColumnType("timestamp with time zone"); + + b.HasIndex("BaseSectionMapId"); + + b.HasDiscriminator().HasValue("Event"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionGame", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property>("GameMessageDebut") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property>("GameMessageFin") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("GamePuzzleCols") + .HasColumnType("integer"); + + b.Property("GamePuzzleImageId") + .HasColumnType("text"); + + b.Property("GamePuzzleRows") + .HasColumnType("integer"); + + b.Property("GameType") + .HasColumnType("integer"); + + b.HasIndex("GamePuzzleImageId"); + + b.HasDiscriminator().HasValue("Game"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionMap", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property("IsListViewEnabled") + .HasColumnType("boolean"); + + b.Property>("MapCategories") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("MapCenterLatitude") + .HasColumnType("text"); + + b.Property("MapCenterLongitude") + .HasColumnType("text"); + + b.Property("MapMapProvider") + .HasColumnType("integer"); + + b.Property("MapMapType") + .HasColumnType("integer"); + + b.Property("MapResourceId") + .HasColumnType("text"); + + b.Property("MapTypeMapbox") + .HasColumnType("integer"); + + b.Property("MapZoom") + .HasColumnType("integer"); + + b.HasIndex("MapResourceId"); + + b.HasDiscriminator().HasValue("Map"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionMenu", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.HasDiscriminator().HasValue("Menu"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionPdf", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property>("PDFOrderedTranslationAndResources") + .IsRequired() + .HasColumnType("jsonb"); + + b.HasDiscriminator().HasValue("PDF"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionQuiz", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property>("QuizBadLevel") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property>("QuizGoodLevel") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property>("QuizGreatLevel") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property>("QuizMediumLevel") + .IsRequired() + .HasColumnType("jsonb"); + + b.HasDiscriminator().HasValue("Quiz"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionSlider", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property>("SliderContents") + .IsRequired() + .HasColumnType("jsonb"); + + b.HasDiscriminator().HasValue("Slider"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionVideo", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property("VideoSource") + .IsRequired() + .HasColumnType("text"); + + b.HasDiscriminator().HasValue("Video"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionWeather", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property("WeatherCity") + .HasColumnType("text"); + + b.Property("WeatherResult") + .HasColumnType("text"); + + b.Property("WeatherUpdatedDate") + .HasColumnType("timestamp with time zone"); + + b.HasDiscriminator().HasValue("Weather"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionWeb", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property("WebSource") + .IsRequired() + .HasColumnType("text"); + + b.HasDiscriminator().HasValue("Web"); + }); + + modelBuilder.Entity("ManagerService.Data.ApiKey", b => + { + b.HasOne("ManagerService.Data.Instance", "Instance") + .WithMany() + .HasForeignKey("InstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Instance"); + }); + + modelBuilder.Entity("ManagerService.Data.AppConfigurationLink", b => + { + b.HasOne("ManagerService.Data.ApplicationInstance", "ApplicationInstance") + .WithMany("Configurations") + .HasForeignKey("ApplicationInstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ManagerService.Data.Configuration", "Configuration") + .WithMany() + .HasForeignKey("ConfigurationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ManagerService.Data.Device", "Device") + .WithMany() + .HasForeignKey("DeviceId"); + + b.Navigation("ApplicationInstance"); + + b.Navigation("Configuration"); + + b.Navigation("Device"); + }); + + modelBuilder.Entity("ManagerService.Data.ApplicationInstance", b => + { + b.HasOne("ManagerService.Data.SubSection.SectionEvent", "SectionEvent") + .WithMany() + .HasForeignKey("SectionEventId"); + + b.Navigation("SectionEvent"); + }); + + modelBuilder.Entity("ManagerService.Data.Device", b => + { + b.HasOne("ManagerService.Data.Configuration", "Configuration") + .WithMany() + .HasForeignKey("ConfigurationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Configuration"); + }); + + modelBuilder.Entity("ManagerService.Data.Instance", b => + { + b.HasOne("ManagerService.Data.SubscriptionPlan", "SubscriptionPlan") + .WithMany() + .HasForeignKey("SubscriptionPlanId"); + + b.Navigation("SubscriptionPlan"); + }); + + modelBuilder.Entity("ManagerService.Data.Section", b => + { + b.HasOne("ManagerService.Data.SubSection.SectionMenu", null) + .WithMany("MenuSections") + .HasForeignKey("SectionMenuId"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.EventAgenda", b => + { + b.HasOne("ManagerService.Data.Resource", "Resource") + .WithMany() + .HasForeignKey("ResourceId"); + + b.HasOne("ManagerService.Data.SubSection.SectionAgenda", "SectionAgenda") + .WithMany("EventAgendas") + .HasForeignKey("SectionAgendaId"); + + b.HasOne("ManagerService.Data.SubSection.SectionEvent", "SectionEvent") + .WithMany() + .HasForeignKey("SectionEventId"); + + b.HasOne("ManagerService.Data.Resource", "VideoResource") + .WithMany() + .HasForeignKey("VideoResourceId"); + + b.Navigation("Resource"); + + b.Navigation("SectionAgenda"); + + b.Navigation("SectionEvent"); + + b.Navigation("VideoResource"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.GeoPoint", b => + { + b.HasOne("ManagerService.Data.SubSection.SectionEvent", "SectionEvent") + .WithMany() + .HasForeignKey("SectionEventId"); + + b.HasOne("ManagerService.Data.SubSection.SectionMap", "SectionMap") + .WithMany("MapPoints") + .HasForeignKey("SectionMapId"); + + b.Navigation("SectionEvent"); + + b.Navigation("SectionMap"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.GuidedPath", b => + { + b.HasOne("ManagerService.Data.SubSection.SectionEvent", "SectionEvent") + .WithMany() + .HasForeignKey("SectionEventId"); + + b.HasOne("ManagerService.Data.SubSection.SectionGame", "SectionGame") + .WithMany() + .HasForeignKey("SectionGameId"); + + b.HasOne("ManagerService.Data.SubSection.SectionMap", "SectionMap") + .WithMany() + .HasForeignKey("SectionMapId"); + + b.Navigation("SectionEvent"); + + b.Navigation("SectionGame"); + + b.Navigation("SectionMap"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.GuidedStep", b => + { + b.HasOne("ManagerService.Data.SubSection.GuidedPath", "GuidedPath") + .WithMany("Steps") + .HasForeignKey("GuidedPathId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ManagerService.Data.SubSection.GeoPoint", "TriggerGeoPoint") + .WithMany() + .HasForeignKey("TriggerGeoPointId"); + + b.Navigation("GuidedPath"); + + b.Navigation("TriggerGeoPoint"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.QuizQuestion", b => + { + b.HasOne("ManagerService.Data.SubSection.GuidedStep", "GuidedStep") + .WithMany("QuizQuestions") + .HasForeignKey("GuidedStepId"); + + b.HasOne("ManagerService.Data.Resource", "PuzzleImage") + .WithMany() + .HasForeignKey("PuzzleImageId"); + + b.HasOne("ManagerService.Data.Resource", "Resource") + .WithMany() + .HasForeignKey("ResourceId"); + + b.HasOne("ManagerService.Data.SubSection.SectionQuiz", "SectionQuiz") + .WithMany("QuizQuestions") + .HasForeignKey("SectionQuizId"); + + b.Navigation("GuidedStep"); + + b.Navigation("PuzzleImage"); + + b.Navigation("Resource"); + + b.Navigation("SectionQuiz"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionEvent+MapAnnotation", b => + { + b.HasOne("ManagerService.Data.Resource", "IconResource") + .WithMany() + .HasForeignKey("IconResourceId"); + + b.HasOne("ManagerService.Data.SubSection.SectionEvent+ProgrammeBlock", null) + .WithMany("MapAnnotations") + .HasForeignKey("ProgrammeBlockId"); + + b.HasOne("ManagerService.Data.SubSection.SectionEvent", null) + .WithMany("GlobalMapAnnotations") + .HasForeignKey("SectionEventId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("IconResource"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionEvent+ProgrammeBlock", b => + { + b.HasOne("ManagerService.Data.SubSection.SectionEvent", null) + .WithMany("Programme") + .HasForeignKey("SectionEventId"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionEvent", b => + { + b.HasOne("ManagerService.Data.SubSection.SectionMap", "BaseMap") + .WithMany() + .HasForeignKey("BaseSectionMapId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("BaseMap"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionGame", b => + { + b.HasOne("ManagerService.Data.Resource", "GamePuzzleImage") + .WithMany() + .HasForeignKey("GamePuzzleImageId"); + + b.Navigation("GamePuzzleImage"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionMap", b => + { + b.HasOne("ManagerService.Data.Resource", "MapResource") + .WithMany() + .HasForeignKey("MapResourceId"); + + b.Navigation("MapResource"); + }); + + modelBuilder.Entity("ManagerService.Data.ApplicationInstance", b => + { + b.Navigation("Configurations"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.GuidedPath", b => + { + b.Navigation("Steps"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.GuidedStep", b => + { + b.Navigation("QuizQuestions"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionEvent+ProgrammeBlock", b => + { + b.Navigation("MapAnnotations"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionAgenda", b => + { + b.Navigation("EventAgendas"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionEvent", b => + { + b.Navigation("GlobalMapAnnotations"); + + b.Navigation("Programme"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionMap", b => + { + b.Navigation("MapPoints"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionMenu", b => + { + b.Navigation("MenuSections"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionQuiz", b => + { + b.Navigation("QuizQuestions"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ManagerService/Migrations/20260510203954_MakeIsSlidingPuzzleNullable.cs b/ManagerService/Migrations/20260510203954_MakeIsSlidingPuzzleNullable.cs new file mode 100644 index 0000000..4b6ce4d --- /dev/null +++ b/ManagerService/Migrations/20260510203954_MakeIsSlidingPuzzleNullable.cs @@ -0,0 +1,36 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace ManagerService.Migrations +{ + /// + public partial class MakeIsSlidingPuzzleNullable : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "IsSlidingPuzzle", + table: "QuizQuestions", + type: "boolean", + nullable: true, + oldClrType: typeof(bool), + oldType: "boolean"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "IsSlidingPuzzle", + table: "QuizQuestions", + type: "boolean", + nullable: false, + defaultValue: false, + oldClrType: typeof(bool), + oldType: "boolean", + oldNullable: true); + } + } +} diff --git a/ManagerService/Migrations/20260612145724_AddSectionParcours.Designer.cs b/ManagerService/Migrations/20260612145724_AddSectionParcours.Designer.cs new file mode 100644 index 0000000..bf5014a --- /dev/null +++ b/ManagerService/Migrations/20260612145724_AddSectionParcours.Designer.cs @@ -0,0 +1,1651 @@ +// +using System; +using System.Collections.Generic; +using Manager.DTOs; +using ManagerService.DTOs; +using ManagerService.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using NetTopologySuite.Geometries; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace ManagerService.Migrations +{ + [DbContext(typeof(MyInfoMateDbContext))] + [Migration("20260612145724_AddSectionParcours")] + partial class AddSectionParcours + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.2") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "postgis"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("ManagerService.Data.ApiKey", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AppType") + .HasColumnType("integer"); + + b.Property("DateCreation") + .HasColumnType("timestamp with time zone"); + + b.Property("DateExpiration") + .HasColumnType("timestamp with time zone"); + + b.Property("InstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("KeyHash") + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId"); + + b.ToTable("ApiKeys"); + }); + + modelBuilder.Entity("ManagerService.Data.AppConfigurationLink", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("ApplicationInstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("ConfigurationId") + .IsRequired() + .HasColumnType("text"); + + b.Property("DeviceId") + .HasColumnType("text"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsDate") + .HasColumnType("boolean"); + + b.Property("IsHour") + .HasColumnType("boolean"); + + b.Property("IsSectionImageBackground") + .HasColumnType("boolean"); + + b.Property("LayoutMainPage") + .HasColumnType("integer"); + + b.Property("LoaderImageId") + .HasColumnType("text"); + + b.Property("LoaderImageUrl") + .HasColumnType("text"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("PrimaryColor") + .HasColumnType("text"); + + b.Property("RoundedValue") + .HasColumnType("integer"); + + b.Property("ScreenPercentageSectionsMainPage") + .HasColumnType("integer"); + + b.Property("SecondaryColor") + .HasColumnType("text"); + + b.Property("WeightMasonryGrid") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationInstanceId"); + + b.HasIndex("ConfigurationId"); + + b.HasIndex("DeviceId"); + + b.ToTable("AppConfigurationLinks"); + }); + + modelBuilder.Entity("ManagerService.Data.ApplicationInstance", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AppType") + .HasColumnType("integer"); + + b.Property("InstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsAssistant") + .HasColumnType("boolean"); + + b.PrimitiveCollection>("Languages") + .HasColumnType("text[]"); + + b.Property("LayoutMainPage") + .HasColumnType("integer"); + + b.Property("LoaderImageId") + .HasColumnType("text"); + + b.Property("LoaderImageUrl") + .HasColumnType("text"); + + b.Property("MainImageId") + .HasColumnType("text"); + + b.Property("MainImageUrl") + .HasColumnType("text"); + + b.Property("PrimaryColor") + .HasColumnType("text"); + + b.Property("SecondaryColor") + .HasColumnType("text"); + + b.Property("SectionEventId") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("SectionEventId"); + + b.ToTable("ApplicationInstances"); + }); + + modelBuilder.Entity("ManagerService.Data.AuditLog", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("Action") + .HasColumnType("text"); + + b.Property("EntityId") + .HasColumnType("text"); + + b.Property("EntityType") + .HasColumnType("text"); + + b.Property("InstanceId") + .HasColumnType("text"); + + b.Property("NewValues") + .HasColumnType("text"); + + b.Property("OldValues") + .HasColumnType("text"); + + b.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("AuditLogs"); + }); + + modelBuilder.Entity("ManagerService.Data.Configuration", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("DateCreation") + .HasColumnType("timestamp with time zone"); + + b.Property("ImageId") + .HasColumnType("text"); + + b.Property("ImageSource") + .HasColumnType("text"); + + b.Property("InstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsOffline") + .HasColumnType("boolean"); + + b.Property("IsQRCode") + .HasColumnType("boolean"); + + b.Property("IsSearchNumber") + .HasColumnType("boolean"); + + b.Property("IsSearchText") + .HasColumnType("boolean"); + + b.Property("Label") + .IsRequired() + .HasColumnType("text"); + + b.PrimitiveCollection>("Languages") + .HasColumnType("text[]"); + + b.Property("LoaderImageId") + .HasColumnType("text"); + + b.Property("LoaderImageUrl") + .HasColumnType("text"); + + b.Property("PrimaryColor") + .HasColumnType("text"); + + b.Property("SecondaryColor") + .HasColumnType("text"); + + b.Property("Title") + .IsRequired() + .HasColumnType("jsonb"); + + b.HasKey("Id"); + + b.ToTable("Configurations"); + }); + + modelBuilder.Entity("ManagerService.Data.Device", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AppVersion") + .HasColumnType("text"); + + b.Property("BatteryLevel") + .HasColumnType("text"); + + b.Property("ConfigurationId") + .IsRequired() + .HasColumnType("text"); + + b.Property("Connected") + .HasColumnType("boolean"); + + b.Property("ConnectionLevel") + .HasColumnType("text"); + + b.Property("DateCreation") + .HasColumnType("timestamp with time zone"); + + b.Property("DateUpdate") + .HasColumnType("timestamp with time zone"); + + b.Property("Identifier") + .HasColumnType("text"); + + b.Property("InstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("IpAddressETH") + .HasColumnType("text"); + + b.Property("IpAddressWLAN") + .HasColumnType("text"); + + b.Property("LastBatteryLevel") + .HasColumnType("timestamp with time zone"); + + b.Property("LastConnectionLevel") + .HasColumnType("timestamp with time zone"); + + b.Property("LastSeen") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ConfigurationId"); + + b.ToTable("Devices"); + }); + + modelBuilder.Entity("ManagerService.Data.Instance", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AiRequestsPerMonth") + .HasColumnType("integer"); + + b.Property("AiRequestsThisMonth") + .HasColumnType("integer"); + + b.Property("AiUsageMonthKey") + .HasColumnType("text"); + + b.Property("DateCreation") + .HasColumnType("timestamp with time zone"); + + b.Property("HasAdvancedStats") + .HasColumnType("boolean"); + + b.Property("HasStats") + .HasColumnType("boolean"); + + b.Property("IsAssistant") + .HasColumnType("boolean"); + + b.Property("IsMobile") + .HasColumnType("boolean"); + + b.Property("IsPushNotification") + .HasColumnType("boolean"); + + b.Property("IsTablet") + .HasColumnType("boolean"); + + b.Property("IsVR") + .HasColumnType("boolean"); + + b.Property("IsWeb") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("PinCode") + .HasColumnType("text"); + + b.Property("PublicApiKey") + .HasColumnType("text"); + + b.Property("StatsHistoryDays") + .HasColumnType("integer"); + + b.Property("StorageQuotaBytes") + .HasColumnType("bigint"); + + b.Property("SubscriptionPlanId") + .HasColumnType("text"); + + b.Property("WebSlug") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("SubscriptionPlanId"); + + b.ToTable("Instances"); + }); + + modelBuilder.Entity("ManagerService.Data.PushNotification", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("Body") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreation") + .HasColumnType("timestamp with time zone"); + + b.Property("HangfireJobId") + .HasColumnType("text"); + + b.Property("InstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("ScheduledAt") + .HasColumnType("timestamp with time zone"); + + b.Property("SentAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("Topic") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId"); + + b.ToTable("PushNotifications"); + }); + + modelBuilder.Entity("ManagerService.Data.Resource", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("DateCreation") + .HasColumnType("timestamp with time zone"); + + b.Property("InstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("Label") + .IsRequired() + .HasColumnType("text"); + + b.Property("SizeBytes") + .HasColumnType("bigint"); + + b.Property("Type") + .HasColumnType("integer"); + + b.Property("Url") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Resources"); + }); + + modelBuilder.Entity("ManagerService.Data.Section", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("BeaconId") + .HasColumnType("integer"); + + b.Property("ConfigurationId") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreation") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .HasColumnType("jsonb"); + + b.Property("Discriminator") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ImageId") + .HasColumnType("text"); + + b.Property("ImageSource") + .HasColumnType("text"); + + b.Property("InstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsBeacon") + .HasColumnType("boolean"); + + b.Property("IsSubSection") + .HasColumnType("boolean"); + + b.Property("Label") + .IsRequired() + .HasColumnType("text"); + + b.Property("Latitude") + .HasColumnType("text"); + + b.Property("Longitude") + .HasColumnType("text"); + + b.Property("MeterZoneGPS") + .HasColumnType("integer"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("ParentId") + .HasColumnType("text"); + + b.Property("SectionMenuId") + .HasColumnType("text"); + + b.Property("Title") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Type") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("SectionMenuId"); + + b.ToTable("Sections"); + + b.HasDiscriminator().HasValue("Base"); + + b.UseTphMappingStrategy(); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.EventAgenda", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Address") + .HasColumnType("jsonb"); + + b.Property("DateAdded") + .HasColumnType("timestamp with time zone"); + + b.Property("DateFrom") + .HasColumnType("timestamp with time zone"); + + b.Property("DateTo") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Email") + .HasColumnType("text"); + + b.Property("IdVideoYoutube") + .HasColumnType("text"); + + b.Property("IsSynced") + .HasColumnType("boolean"); + + b.Property("Label") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Phone") + .HasColumnType("text"); + + b.Property("ResourceId") + .HasColumnType("text"); + + b.Property("SectionAgendaId") + .HasColumnType("text"); + + b.Property("SectionEventId") + .HasColumnType("text"); + + b.Property("SyncedImageUrl") + .HasColumnType("text"); + + b.Property("Type") + .HasColumnType("text"); + + b.Property("VideoLink") + .HasColumnType("text"); + + b.Property("VideoResourceId") + .HasColumnType("text"); + + b.Property("Website") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ResourceId"); + + b.HasIndex("SectionAgendaId"); + + b.HasIndex("SectionEventId"); + + b.HasIndex("VideoResourceId"); + + b.ToTable("EventAgendas"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.GeoPoint", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CategorieId") + .HasColumnType("integer"); + + b.Property("Contents") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Description") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Email") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Geometry") + .HasColumnType("geometry"); + + b.Property("ImageResourceId") + .HasColumnType("text"); + + b.Property("ImageUrl") + .HasColumnType("text"); + + b.Property("Phone") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("PolyColor") + .HasColumnType("text"); + + b.Property("Prices") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Schedules") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("SectionEventId") + .HasColumnType("text"); + + b.Property("SectionMapId") + .HasColumnType("text"); + + b.Property("Site") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Title") + .IsRequired() + .HasColumnType("jsonb"); + + b.HasKey("Id"); + + b.HasIndex("SectionEventId"); + + b.HasIndex("SectionMapId"); + + b.ToTable("GeoPoints"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.GuidedPath", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("Description") + .HasColumnType("jsonb"); + + b.Property("EstimatedDurationMinutes") + .HasColumnType("integer"); + + b.Property("HideNextStepsUntilComplete") + .HasColumnType("boolean"); + + b.Property("ImageResourceId") + .HasColumnType("text"); + + b.Property("InstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsLinear") + .HasColumnType("boolean"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("RequireSuccessToAdvance") + .HasColumnType("boolean"); + + b.Property("SectionEventId") + .HasColumnType("text"); + + b.Property("SectionMapId") + .HasColumnType("text"); + + b.Property("SectionParcoursId") + .HasColumnType("text"); + + b.Property("Title") + .IsRequired() + .HasColumnType("jsonb"); + + b.HasKey("Id"); + + b.HasIndex("SectionEventId"); + + b.HasIndex("SectionMapId"); + + b.HasIndex("SectionParcoursId"); + + b.ToTable("GuidedPaths"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.GuidedStep", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AudioIds") + .HasColumnType("jsonb"); + + b.Property("Contents") + .HasColumnType("jsonb"); + + b.Property("Description") + .HasColumnType("jsonb"); + + b.Property("FactContent") + .HasColumnType("jsonb"); + + b.Property("Geometry") + .HasColumnType("geometry"); + + b.Property("GuidedPathId") + .IsRequired() + .HasColumnType("text"); + + b.Property("ImageUrl") + .HasColumnType("text"); + + b.Property("IsHiddenInitially") + .HasColumnType("boolean"); + + b.Property("IsStepLocked") + .HasColumnType("boolean"); + + b.Property("IsStepTimer") + .HasColumnType("boolean"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("TimerExpiredMessage") + .HasColumnType("jsonb"); + + b.Property("TimerSeconds") + .HasColumnType("integer"); + + b.Property("Title") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("TriggerGeoPointId") + .HasColumnType("integer"); + + b.Property("ZoneRadiusMeters") + .HasColumnType("double precision"); + + b.HasKey("Id"); + + b.HasIndex("GuidedPathId"); + + b.HasIndex("TriggerGeoPointId"); + + b.ToTable("GuidedSteps"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.QuizQuestion", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("GuidedStepId") + .HasColumnType("text"); + + b.Property("IsSlidingPuzzle") + .HasColumnType("boolean"); + + b.Property>("Label") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("PuzzleCols") + .HasColumnType("integer"); + + b.Property("PuzzleImageId") + .HasColumnType("text"); + + b.Property("PuzzleRows") + .HasColumnType("integer"); + + b.Property("ResourceId") + .HasColumnType("text"); + + b.Property>("Responses") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("SectionQuizId") + .HasColumnType("text"); + + b.Property("ValidationQuestionType") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("GuidedStepId"); + + b.HasIndex("PuzzleImageId"); + + b.HasIndex("ResourceId"); + + b.HasIndex("SectionQuizId"); + + b.ToTable("QuizQuestions"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionEvent+MapAnnotation", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("Geometry") + .HasColumnType("geometry"); + + b.Property("GeometryType") + .HasColumnType("integer"); + + b.Property("Icon") + .HasColumnType("text"); + + b.Property("IconResourceId") + .HasColumnType("text"); + + b.Property>("Label") + .HasColumnType("jsonb"); + + b.Property("PolyColor") + .HasColumnType("text"); + + b.Property("ProgrammeBlockId") + .HasColumnType("text"); + + b.Property("SectionEventId") + .HasColumnType("text"); + + b.Property>("Type") + .HasColumnType("jsonb"); + + b.HasKey("Id"); + + b.HasIndex("IconResourceId"); + + b.HasIndex("ProgrammeBlockId"); + + b.HasIndex("SectionEventId"); + + b.ToTable("MapAnnotations"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionEvent+ProgrammeBlock", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property>("Description") + .HasColumnType("jsonb"); + + b.Property("EndTime") + .HasColumnType("timestamp with time zone"); + + b.Property("SectionEventId") + .HasColumnType("text"); + + b.Property("StartTime") + .HasColumnType("timestamp with time zone"); + + b.Property>("Title") + .HasColumnType("jsonb"); + + b.HasKey("Id"); + + b.HasIndex("SectionEventId"); + + b.ToTable("ProgrammeBlocks"); + }); + + modelBuilder.Entity("ManagerService.Data.SubscriptionPlan", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AiRequestsPerMonth") + .HasColumnType("integer"); + + b.Property("HasAdvancedStats") + .HasColumnType("boolean"); + + b.Property("HasStats") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("StatsHistoryDays") + .HasColumnType("integer"); + + b.Property("StorageQuotaBytes") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.ToTable("SubscriptionPlans"); + + b.HasData( + new + { + Id = "plan-starter", + AiRequestsPerMonth = 0, + HasAdvancedStats = false, + HasStats = false, + Name = "Starter", + StatsHistoryDays = 30, + StorageQuotaBytes = 1073741824L + }, + new + { + Id = "plan-standard", + AiRequestsPerMonth = 500, + HasAdvancedStats = false, + HasStats = true, + Name = "Standard", + StatsHistoryDays = 30, + StorageQuotaBytes = 10737418240L + }, + new + { + Id = "plan-premium", + AiRequestsPerMonth = 2000, + HasAdvancedStats = true, + HasStats = true, + Name = "Premium", + StatsHistoryDays = 0, + StorageQuotaBytes = 53687091200L + }); + }); + + modelBuilder.Entity("ManagerService.Data.User", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("DateCreation") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .IsRequired() + .HasColumnType("text"); + + b.Property("FirstName") + .HasColumnType("text"); + + b.Property("InstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("text"); + + b.Property("Password") + .IsRequired() + .HasColumnType("text"); + + b.Property("Role") + .HasColumnType("integer"); + + b.Property("Token") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("ManagerService.Data.VisitEvent", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AppType") + .HasColumnType("integer"); + + b.Property("ConfigurationId") + .HasColumnType("text"); + + b.Property("DurationSeconds") + .HasColumnType("integer"); + + b.Property("EventType") + .HasColumnType("integer"); + + b.Property("InstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("Language") + .HasColumnType("text"); + + b.Property("Metadata") + .HasColumnType("text"); + + b.Property("SectionId") + .HasColumnType("text"); + + b.Property("SessionId") + .IsRequired() + .HasColumnType("text"); + + b.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId"); + + b.HasIndex("Timestamp"); + + b.ToTable("VisitEvents"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionAgenda", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property("AgendaMapProvider") + .HasColumnType("integer"); + + b.Property>("AgendaResourceIds") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("IsOnlineAgenda") + .HasColumnType("boolean"); + + b.HasDiscriminator().HasValue("Agenda"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionArticle", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property>("ArticleAudioIds") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property>("ArticleContent") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property>("ArticleContents") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("ArticleIsContentTop") + .HasColumnType("boolean"); + + b.Property("ArticleIsReadAudioAuto") + .HasColumnType("boolean"); + + b.HasDiscriminator().HasValue("Article"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionEvent", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property("BaseSectionMapId") + .HasColumnType("text"); + + b.Property("EndDate") + .HasColumnType("timestamp with time zone"); + + b.Property>("ParcoursIds") + .HasColumnType("jsonb"); + + b.Property("StartDate") + .HasColumnType("timestamp with time zone"); + + b.HasIndex("BaseSectionMapId"); + + b.HasDiscriminator().HasValue("Event"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionGame", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property>("GameMessageDebut") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property>("GameMessageFin") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("GamePuzzleCols") + .HasColumnType("integer"); + + b.Property("GamePuzzleImageId") + .HasColumnType("text"); + + b.Property("GamePuzzleRows") + .HasColumnType("integer"); + + b.Property("GameType") + .HasColumnType("integer"); + + b.HasIndex("GamePuzzleImageId"); + + b.HasDiscriminator().HasValue("Game"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionMap", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property("IsListViewEnabled") + .HasColumnType("boolean"); + + b.Property>("MapCategories") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("MapCenterLatitude") + .HasColumnType("text"); + + b.Property("MapCenterLongitude") + .HasColumnType("text"); + + b.Property("MapMapProvider") + .HasColumnType("integer"); + + b.Property("MapMapType") + .HasColumnType("integer"); + + b.Property("MapResourceId") + .HasColumnType("text"); + + b.Property("MapTypeMapbox") + .HasColumnType("integer"); + + b.Property("MapZoom") + .HasColumnType("integer"); + + b.HasIndex("MapResourceId"); + + b.HasDiscriminator().HasValue("Map"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionMenu", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.HasDiscriminator().HasValue("Menu"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionParcours", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property("BaseSectionMapId") + .HasColumnType("text"); + + b.Property>("GameMessageDebut") + .HasColumnType("jsonb"); + + b.Property>("GameMessageFin") + .HasColumnType("jsonb"); + + b.Property("IsGameMode") + .HasColumnType("boolean"); + + b.Property("ShowMap") + .HasColumnType("boolean"); + + b.HasIndex("BaseSectionMapId"); + + b.ToTable("Sections", t => + { + t.Property("BaseSectionMapId") + .HasColumnName("SectionParcours_BaseSectionMapId"); + + t.Property("GameMessageDebut") + .HasColumnName("SectionParcours_GameMessageDebut"); + + t.Property("GameMessageFin") + .HasColumnName("SectionParcours_GameMessageFin"); + }); + + b.HasDiscriminator().HasValue("Parcours"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionPdf", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property>("PDFOrderedTranslationAndResources") + .IsRequired() + .HasColumnType("jsonb"); + + b.HasDiscriminator().HasValue("PDF"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionQuiz", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property>("QuizBadLevel") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property>("QuizGoodLevel") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property>("QuizGreatLevel") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property>("QuizMediumLevel") + .IsRequired() + .HasColumnType("jsonb"); + + b.HasDiscriminator().HasValue("Quiz"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionSlider", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property>("SliderContents") + .IsRequired() + .HasColumnType("jsonb"); + + b.HasDiscriminator().HasValue("Slider"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionVideo", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property("VideoSource") + .IsRequired() + .HasColumnType("text"); + + b.HasDiscriminator().HasValue("Video"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionWeather", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property("WeatherCity") + .HasColumnType("text"); + + b.Property("WeatherResult") + .HasColumnType("text"); + + b.Property("WeatherUpdatedDate") + .HasColumnType("timestamp with time zone"); + + b.HasDiscriminator().HasValue("Weather"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionWeb", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property("WebSource") + .IsRequired() + .HasColumnType("text"); + + b.HasDiscriminator().HasValue("Web"); + }); + + modelBuilder.Entity("ManagerService.Data.ApiKey", b => + { + b.HasOne("ManagerService.Data.Instance", "Instance") + .WithMany() + .HasForeignKey("InstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Instance"); + }); + + modelBuilder.Entity("ManagerService.Data.AppConfigurationLink", b => + { + b.HasOne("ManagerService.Data.ApplicationInstance", "ApplicationInstance") + .WithMany("Configurations") + .HasForeignKey("ApplicationInstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ManagerService.Data.Configuration", "Configuration") + .WithMany() + .HasForeignKey("ConfigurationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ManagerService.Data.Device", "Device") + .WithMany() + .HasForeignKey("DeviceId"); + + b.Navigation("ApplicationInstance"); + + b.Navigation("Configuration"); + + b.Navigation("Device"); + }); + + modelBuilder.Entity("ManagerService.Data.ApplicationInstance", b => + { + b.HasOne("ManagerService.Data.SubSection.SectionEvent", "SectionEvent") + .WithMany() + .HasForeignKey("SectionEventId"); + + b.Navigation("SectionEvent"); + }); + + modelBuilder.Entity("ManagerService.Data.Device", b => + { + b.HasOne("ManagerService.Data.Configuration", "Configuration") + .WithMany() + .HasForeignKey("ConfigurationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Configuration"); + }); + + modelBuilder.Entity("ManagerService.Data.Instance", b => + { + b.HasOne("ManagerService.Data.SubscriptionPlan", "SubscriptionPlan") + .WithMany() + .HasForeignKey("SubscriptionPlanId"); + + b.Navigation("SubscriptionPlan"); + }); + + modelBuilder.Entity("ManagerService.Data.Section", b => + { + b.HasOne("ManagerService.Data.SubSection.SectionMenu", null) + .WithMany("MenuSections") + .HasForeignKey("SectionMenuId"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.EventAgenda", b => + { + b.HasOne("ManagerService.Data.Resource", "Resource") + .WithMany() + .HasForeignKey("ResourceId"); + + b.HasOne("ManagerService.Data.SubSection.SectionAgenda", "SectionAgenda") + .WithMany("EventAgendas") + .HasForeignKey("SectionAgendaId"); + + b.HasOne("ManagerService.Data.SubSection.SectionEvent", "SectionEvent") + .WithMany() + .HasForeignKey("SectionEventId"); + + b.HasOne("ManagerService.Data.Resource", "VideoResource") + .WithMany() + .HasForeignKey("VideoResourceId"); + + b.Navigation("Resource"); + + b.Navigation("SectionAgenda"); + + b.Navigation("SectionEvent"); + + b.Navigation("VideoResource"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.GeoPoint", b => + { + b.HasOne("ManagerService.Data.SubSection.SectionEvent", "SectionEvent") + .WithMany() + .HasForeignKey("SectionEventId"); + + b.HasOne("ManagerService.Data.SubSection.SectionMap", "SectionMap") + .WithMany("MapPoints") + .HasForeignKey("SectionMapId"); + + b.Navigation("SectionEvent"); + + b.Navigation("SectionMap"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.GuidedPath", b => + { + b.HasOne("ManagerService.Data.SubSection.SectionEvent", "SectionEvent") + .WithMany() + .HasForeignKey("SectionEventId"); + + b.HasOne("ManagerService.Data.SubSection.SectionMap", "SectionMap") + .WithMany() + .HasForeignKey("SectionMapId"); + + b.HasOne("ManagerService.Data.SubSection.SectionParcours", "SectionParcours") + .WithMany("GuidedPaths") + .HasForeignKey("SectionParcoursId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("SectionEvent"); + + b.Navigation("SectionMap"); + + b.Navigation("SectionParcours"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.GuidedStep", b => + { + b.HasOne("ManagerService.Data.SubSection.GuidedPath", "GuidedPath") + .WithMany("Steps") + .HasForeignKey("GuidedPathId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ManagerService.Data.SubSection.GeoPoint", "TriggerGeoPoint") + .WithMany() + .HasForeignKey("TriggerGeoPointId"); + + b.Navigation("GuidedPath"); + + b.Navigation("TriggerGeoPoint"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.QuizQuestion", b => + { + b.HasOne("ManagerService.Data.SubSection.GuidedStep", "GuidedStep") + .WithMany("QuizQuestions") + .HasForeignKey("GuidedStepId"); + + b.HasOne("ManagerService.Data.Resource", "PuzzleImage") + .WithMany() + .HasForeignKey("PuzzleImageId"); + + b.HasOne("ManagerService.Data.Resource", "Resource") + .WithMany() + .HasForeignKey("ResourceId"); + + b.HasOne("ManagerService.Data.SubSection.SectionQuiz", "SectionQuiz") + .WithMany("QuizQuestions") + .HasForeignKey("SectionQuizId"); + + b.Navigation("GuidedStep"); + + b.Navigation("PuzzleImage"); + + b.Navigation("Resource"); + + b.Navigation("SectionQuiz"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionEvent+MapAnnotation", b => + { + b.HasOne("ManagerService.Data.Resource", "IconResource") + .WithMany() + .HasForeignKey("IconResourceId"); + + b.HasOne("ManagerService.Data.SubSection.SectionEvent+ProgrammeBlock", null) + .WithMany("MapAnnotations") + .HasForeignKey("ProgrammeBlockId"); + + b.HasOne("ManagerService.Data.SubSection.SectionEvent", null) + .WithMany("GlobalMapAnnotations") + .HasForeignKey("SectionEventId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("IconResource"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionEvent+ProgrammeBlock", b => + { + b.HasOne("ManagerService.Data.SubSection.SectionEvent", null) + .WithMany("Programme") + .HasForeignKey("SectionEventId"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionEvent", b => + { + b.HasOne("ManagerService.Data.SubSection.SectionMap", "BaseMap") + .WithMany() + .HasForeignKey("BaseSectionMapId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("BaseMap"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionGame", b => + { + b.HasOne("ManagerService.Data.Resource", "GamePuzzleImage") + .WithMany() + .HasForeignKey("GamePuzzleImageId"); + + b.Navigation("GamePuzzleImage"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionMap", b => + { + b.HasOne("ManagerService.Data.Resource", "MapResource") + .WithMany() + .HasForeignKey("MapResourceId"); + + b.Navigation("MapResource"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionParcours", b => + { + b.HasOne("ManagerService.Data.SubSection.SectionMap", "BaseMap") + .WithMany() + .HasForeignKey("BaseSectionMapId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("BaseMap"); + }); + + modelBuilder.Entity("ManagerService.Data.ApplicationInstance", b => + { + b.Navigation("Configurations"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.GuidedPath", b => + { + b.Navigation("Steps"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.GuidedStep", b => + { + b.Navigation("QuizQuestions"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionEvent+ProgrammeBlock", b => + { + b.Navigation("MapAnnotations"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionAgenda", b => + { + b.Navigation("EventAgendas"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionEvent", b => + { + b.Navigation("GlobalMapAnnotations"); + + b.Navigation("Programme"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionMap", b => + { + b.Navigation("MapPoints"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionMenu", b => + { + b.Navigation("MenuSections"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionParcours", b => + { + b.Navigation("GuidedPaths"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionQuiz", b => + { + b.Navigation("QuizQuestions"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ManagerService/Migrations/20260612145724_AddSectionParcours.cs b/ManagerService/Migrations/20260612145724_AddSectionParcours.cs new file mode 100644 index 0000000..910670c --- /dev/null +++ b/ManagerService/Migrations/20260612145724_AddSectionParcours.cs @@ -0,0 +1,184 @@ +using System.Collections.Generic; +using ManagerService.DTOs; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace ManagerService.Migrations +{ + /// + public partial class AddSectionParcours : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_GuidedPaths_Sections_SectionGameId", + table: "GuidedPaths"); + + migrationBuilder.RenameColumn( + name: "SectionGameId", + table: "GuidedPaths", + newName: "SectionParcoursId"); + + migrationBuilder.RenameIndex( + name: "IX_GuidedPaths_SectionGameId", + table: "GuidedPaths", + newName: "IX_GuidedPaths_SectionParcoursId"); + + migrationBuilder.AddColumn( + name: "IsGameMode", + table: "Sections", + type: "boolean", + nullable: true); + + migrationBuilder.AddColumn( + name: "SectionParcours_BaseSectionMapId", + table: "Sections", + type: "text", + nullable: true); + + migrationBuilder.AddColumn>( + name: "SectionParcours_GameMessageDebut", + table: "Sections", + type: "jsonb", + nullable: true); + + migrationBuilder.AddColumn>( + name: "SectionParcours_GameMessageFin", + table: "Sections", + type: "jsonb", + nullable: true); + + migrationBuilder.AddColumn( + name: "ShowMap", + table: "Sections", + type: "boolean", + nullable: true); + + migrationBuilder.AddColumn( + name: "AudioIds", + table: "GuidedSteps", + type: "jsonb", + nullable: true); + + migrationBuilder.AddColumn( + name: "Contents", + table: "GuidedSteps", + type: "jsonb", + nullable: true); + + migrationBuilder.AddColumn( + name: "FactContent", + table: "GuidedSteps", + type: "jsonb", + nullable: true); + + migrationBuilder.AddColumn( + name: "EstimatedDurationMinutes", + table: "GuidedPaths", + type: "integer", + nullable: true); + + migrationBuilder.AddColumn( + name: "ImageResourceId", + table: "GuidedPaths", + type: "text", + nullable: true); + + migrationBuilder.CreateIndex( + name: "IX_Sections_SectionParcours_BaseSectionMapId", + table: "Sections", + column: "SectionParcours_BaseSectionMapId"); + + migrationBuilder.AddForeignKey( + name: "FK_GuidedPaths_Sections_SectionParcoursId", + table: "GuidedPaths", + column: "SectionParcoursId", + principalTable: "Sections", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + name: "FK_Sections_Sections_SectionParcours_BaseSectionMapId", + table: "Sections", + column: "SectionParcours_BaseSectionMapId", + principalTable: "Sections", + principalColumn: "Id", + onDelete: ReferentialAction.SetNull); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_GuidedPaths_Sections_SectionParcoursId", + table: "GuidedPaths"); + + migrationBuilder.DropForeignKey( + name: "FK_Sections_Sections_SectionParcours_BaseSectionMapId", + table: "Sections"); + + migrationBuilder.DropIndex( + name: "IX_Sections_SectionParcours_BaseSectionMapId", + table: "Sections"); + + migrationBuilder.DropColumn( + name: "IsGameMode", + table: "Sections"); + + migrationBuilder.DropColumn( + name: "SectionParcours_BaseSectionMapId", + table: "Sections"); + + migrationBuilder.DropColumn( + name: "SectionParcours_GameMessageDebut", + table: "Sections"); + + migrationBuilder.DropColumn( + name: "SectionParcours_GameMessageFin", + table: "Sections"); + + migrationBuilder.DropColumn( + name: "ShowMap", + table: "Sections"); + + migrationBuilder.DropColumn( + name: "AudioIds", + table: "GuidedSteps"); + + migrationBuilder.DropColumn( + name: "Contents", + table: "GuidedSteps"); + + migrationBuilder.DropColumn( + name: "FactContent", + table: "GuidedSteps"); + + migrationBuilder.DropColumn( + name: "EstimatedDurationMinutes", + table: "GuidedPaths"); + + migrationBuilder.DropColumn( + name: "ImageResourceId", + table: "GuidedPaths"); + + migrationBuilder.RenameColumn( + name: "SectionParcoursId", + table: "GuidedPaths", + newName: "SectionGameId"); + + migrationBuilder.RenameIndex( + name: "IX_GuidedPaths_SectionParcoursId", + table: "GuidedPaths", + newName: "IX_GuidedPaths_SectionGameId"); + + migrationBuilder.AddForeignKey( + name: "FK_GuidedPaths_Sections_SectionGameId", + table: "GuidedPaths", + column: "SectionGameId", + principalTable: "Sections", + principalColumn: "Id"); + } + } +} diff --git a/ManagerService/Migrations/20260629150749_MoveGameModeToGuidedPath.Designer.cs b/ManagerService/Migrations/20260629150749_MoveGameModeToGuidedPath.Designer.cs new file mode 100644 index 0000000..229159d --- /dev/null +++ b/ManagerService/Migrations/20260629150749_MoveGameModeToGuidedPath.Designer.cs @@ -0,0 +1,1653 @@ +// +using System; +using System.Collections.Generic; +using Manager.DTOs; +using ManagerService.DTOs; +using ManagerService.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using NetTopologySuite.Geometries; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace ManagerService.Migrations +{ + [DbContext(typeof(MyInfoMateDbContext))] + [Migration("20260629150749_MoveGameModeToGuidedPath")] + partial class MoveGameModeToGuidedPath + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.2") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "postgis"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("ManagerService.Data.ApiKey", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AppType") + .HasColumnType("integer"); + + b.Property("DateCreation") + .HasColumnType("timestamp with time zone"); + + b.Property("DateExpiration") + .HasColumnType("timestamp with time zone"); + + b.Property("InstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("KeyHash") + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId"); + + b.ToTable("ApiKeys"); + }); + + modelBuilder.Entity("ManagerService.Data.AppConfigurationLink", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("ApplicationInstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("ConfigurationId") + .IsRequired() + .HasColumnType("text"); + + b.Property("DeviceId") + .HasColumnType("text"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsDate") + .HasColumnType("boolean"); + + b.Property("IsHour") + .HasColumnType("boolean"); + + b.Property("IsSectionImageBackground") + .HasColumnType("boolean"); + + b.Property("LayoutMainPage") + .HasColumnType("integer"); + + b.Property("LoaderImageId") + .HasColumnType("text"); + + b.Property("LoaderImageUrl") + .HasColumnType("text"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("PrimaryColor") + .HasColumnType("text"); + + b.Property("RoundedValue") + .HasColumnType("integer"); + + b.Property("ScreenPercentageSectionsMainPage") + .HasColumnType("integer"); + + b.Property("SecondaryColor") + .HasColumnType("text"); + + b.Property("WeightMasonryGrid") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationInstanceId"); + + b.HasIndex("ConfigurationId"); + + b.HasIndex("DeviceId"); + + b.ToTable("AppConfigurationLinks"); + }); + + modelBuilder.Entity("ManagerService.Data.ApplicationInstance", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AppType") + .HasColumnType("integer"); + + b.Property("InstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsAssistant") + .HasColumnType("boolean"); + + b.PrimitiveCollection>("Languages") + .HasColumnType("text[]"); + + b.Property("LayoutMainPage") + .HasColumnType("integer"); + + b.Property("LoaderImageId") + .HasColumnType("text"); + + b.Property("LoaderImageUrl") + .HasColumnType("text"); + + b.Property("MainImageId") + .HasColumnType("text"); + + b.Property("MainImageUrl") + .HasColumnType("text"); + + b.Property("PrimaryColor") + .HasColumnType("text"); + + b.Property("SecondaryColor") + .HasColumnType("text"); + + b.Property("SectionEventId") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("SectionEventId"); + + b.ToTable("ApplicationInstances"); + }); + + modelBuilder.Entity("ManagerService.Data.AuditLog", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("Action") + .HasColumnType("text"); + + b.Property("EntityId") + .HasColumnType("text"); + + b.Property("EntityType") + .HasColumnType("text"); + + b.Property("InstanceId") + .HasColumnType("text"); + + b.Property("NewValues") + .HasColumnType("text"); + + b.Property("OldValues") + .HasColumnType("text"); + + b.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("AuditLogs"); + }); + + modelBuilder.Entity("ManagerService.Data.Configuration", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("DateCreation") + .HasColumnType("timestamp with time zone"); + + b.Property("ImageId") + .HasColumnType("text"); + + b.Property("ImageSource") + .HasColumnType("text"); + + b.Property("InstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsOffline") + .HasColumnType("boolean"); + + b.Property("IsQRCode") + .HasColumnType("boolean"); + + b.Property("IsSearchNumber") + .HasColumnType("boolean"); + + b.Property("IsSearchText") + .HasColumnType("boolean"); + + b.Property("Label") + .IsRequired() + .HasColumnType("text"); + + b.PrimitiveCollection>("Languages") + .HasColumnType("text[]"); + + b.Property("LoaderImageId") + .HasColumnType("text"); + + b.Property("LoaderImageUrl") + .HasColumnType("text"); + + b.Property("PrimaryColor") + .HasColumnType("text"); + + b.Property("SecondaryColor") + .HasColumnType("text"); + + b.Property("Title") + .IsRequired() + .HasColumnType("jsonb"); + + b.HasKey("Id"); + + b.ToTable("Configurations"); + }); + + modelBuilder.Entity("ManagerService.Data.Device", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AppVersion") + .HasColumnType("text"); + + b.Property("BatteryLevel") + .HasColumnType("text"); + + b.Property("ConfigurationId") + .IsRequired() + .HasColumnType("text"); + + b.Property("Connected") + .HasColumnType("boolean"); + + b.Property("ConnectionLevel") + .HasColumnType("text"); + + b.Property("DateCreation") + .HasColumnType("timestamp with time zone"); + + b.Property("DateUpdate") + .HasColumnType("timestamp with time zone"); + + b.Property("Identifier") + .HasColumnType("text"); + + b.Property("InstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("IpAddressETH") + .HasColumnType("text"); + + b.Property("IpAddressWLAN") + .HasColumnType("text"); + + b.Property("LastBatteryLevel") + .HasColumnType("timestamp with time zone"); + + b.Property("LastConnectionLevel") + .HasColumnType("timestamp with time zone"); + + b.Property("LastSeen") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ConfigurationId"); + + b.ToTable("Devices"); + }); + + modelBuilder.Entity("ManagerService.Data.Instance", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AiRequestsPerMonth") + .HasColumnType("integer"); + + b.Property("AiRequestsThisMonth") + .HasColumnType("integer"); + + b.Property("AiUsageMonthKey") + .HasColumnType("text"); + + b.Property("DateCreation") + .HasColumnType("timestamp with time zone"); + + b.Property("HasAdvancedStats") + .HasColumnType("boolean"); + + b.Property("HasStats") + .HasColumnType("boolean"); + + b.Property("IsAssistant") + .HasColumnType("boolean"); + + b.Property("IsMobile") + .HasColumnType("boolean"); + + b.Property("IsPushNotification") + .HasColumnType("boolean"); + + b.Property("IsTablet") + .HasColumnType("boolean"); + + b.Property("IsVR") + .HasColumnType("boolean"); + + b.Property("IsWeb") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("PinCode") + .HasColumnType("text"); + + b.Property("PublicApiKey") + .HasColumnType("text"); + + b.Property("StatsHistoryDays") + .HasColumnType("integer"); + + b.Property("StorageQuotaBytes") + .HasColumnType("bigint"); + + b.Property("SubscriptionPlanId") + .HasColumnType("text"); + + b.Property("WebSlug") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("SubscriptionPlanId"); + + b.ToTable("Instances"); + }); + + modelBuilder.Entity("ManagerService.Data.PushNotification", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("Body") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreation") + .HasColumnType("timestamp with time zone"); + + b.Property("HangfireJobId") + .HasColumnType("text"); + + b.Property("InstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("ScheduledAt") + .HasColumnType("timestamp with time zone"); + + b.Property("SentAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("Topic") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId"); + + b.ToTable("PushNotifications"); + }); + + modelBuilder.Entity("ManagerService.Data.Resource", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("DateCreation") + .HasColumnType("timestamp with time zone"); + + b.Property("InstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("Label") + .IsRequired() + .HasColumnType("text"); + + b.Property("SizeBytes") + .HasColumnType("bigint"); + + b.Property("Type") + .HasColumnType("integer"); + + b.Property("Url") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Resources"); + }); + + modelBuilder.Entity("ManagerService.Data.Section", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("BeaconId") + .HasColumnType("integer"); + + b.Property("ConfigurationId") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreation") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .HasColumnType("jsonb"); + + b.Property("Discriminator") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ImageId") + .HasColumnType("text"); + + b.Property("ImageSource") + .HasColumnType("text"); + + b.Property("InstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsBeacon") + .HasColumnType("boolean"); + + b.Property("IsSubSection") + .HasColumnType("boolean"); + + b.Property("Label") + .IsRequired() + .HasColumnType("text"); + + b.Property("Latitude") + .HasColumnType("text"); + + b.Property("Longitude") + .HasColumnType("text"); + + b.Property("MeterZoneGPS") + .HasColumnType("integer"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("ParentId") + .HasColumnType("text"); + + b.Property("SectionMenuId") + .HasColumnType("text"); + + b.Property("Title") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Type") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("SectionMenuId"); + + b.ToTable("Sections"); + + b.HasDiscriminator().HasValue("Base"); + + b.UseTphMappingStrategy(); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.EventAgenda", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Address") + .HasColumnType("jsonb"); + + b.Property("DateAdded") + .HasColumnType("timestamp with time zone"); + + b.Property("DateFrom") + .HasColumnType("timestamp with time zone"); + + b.Property("DateTo") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Email") + .HasColumnType("text"); + + b.Property("IdVideoYoutube") + .HasColumnType("text"); + + b.Property("IsSynced") + .HasColumnType("boolean"); + + b.Property("Label") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Phone") + .HasColumnType("text"); + + b.Property("ResourceId") + .HasColumnType("text"); + + b.Property("SectionAgendaId") + .HasColumnType("text"); + + b.Property("SectionEventId") + .HasColumnType("text"); + + b.Property("SyncedImageUrl") + .HasColumnType("text"); + + b.Property("Type") + .HasColumnType("text"); + + b.Property("VideoLink") + .HasColumnType("text"); + + b.Property("VideoResourceId") + .HasColumnType("text"); + + b.Property("Website") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ResourceId"); + + b.HasIndex("SectionAgendaId"); + + b.HasIndex("SectionEventId"); + + b.HasIndex("VideoResourceId"); + + b.ToTable("EventAgendas"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.GeoPoint", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CategorieId") + .HasColumnType("integer"); + + b.Property("Contents") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Description") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Email") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Geometry") + .HasColumnType("geometry"); + + b.Property("ImageResourceId") + .HasColumnType("text"); + + b.Property("ImageUrl") + .HasColumnType("text"); + + b.Property("Phone") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("PolyColor") + .HasColumnType("text"); + + b.Property("Prices") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Schedules") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("SectionEventId") + .HasColumnType("text"); + + b.Property("SectionMapId") + .HasColumnType("text"); + + b.Property("Site") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Title") + .IsRequired() + .HasColumnType("jsonb"); + + b.HasKey("Id"); + + b.HasIndex("SectionEventId"); + + b.HasIndex("SectionMapId"); + + b.ToTable("GeoPoints"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.GuidedPath", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("Description") + .HasColumnType("jsonb"); + + b.Property("EstimatedDurationMinutes") + .HasColumnType("integer"); + + b.Property("GameMessageDebut") + .HasColumnType("jsonb"); + + b.Property("GameMessageFin") + .HasColumnType("jsonb"); + + b.Property("HideNextStepsUntilComplete") + .HasColumnType("boolean"); + + b.Property("ImageResourceId") + .HasColumnType("text"); + + b.Property("InstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsGameMode") + .HasColumnType("boolean"); + + b.Property("IsLinear") + .HasColumnType("boolean"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("RequireSuccessToAdvance") + .HasColumnType("boolean"); + + b.Property("SectionEventId") + .HasColumnType("text"); + + b.Property("SectionMapId") + .HasColumnType("text"); + + b.Property("SectionParcoursId") + .HasColumnType("text"); + + b.Property("Title") + .IsRequired() + .HasColumnType("jsonb"); + + b.HasKey("Id"); + + b.HasIndex("ImageResourceId"); + + b.HasIndex("SectionEventId"); + + b.HasIndex("SectionMapId"); + + b.HasIndex("SectionParcoursId"); + + b.ToTable("GuidedPaths"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.GuidedStep", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AudioIds") + .HasColumnType("jsonb"); + + b.Property("Contents") + .HasColumnType("jsonb"); + + b.Property("Description") + .HasColumnType("jsonb"); + + b.Property("FactContent") + .HasColumnType("jsonb"); + + b.Property("Geometry") + .HasColumnType("geometry"); + + b.Property("GuidedPathId") + .IsRequired() + .HasColumnType("text"); + + b.Property("ImageUrl") + .HasColumnType("text"); + + b.Property("IsHiddenInitially") + .HasColumnType("boolean"); + + b.Property("IsStepLocked") + .HasColumnType("boolean"); + + b.Property("IsStepTimer") + .HasColumnType("boolean"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("TimerExpiredMessage") + .HasColumnType("jsonb"); + + b.Property("TimerSeconds") + .HasColumnType("integer"); + + b.Property("Title") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("TriggerGeoPointId") + .HasColumnType("integer"); + + b.Property("ZoneRadiusMeters") + .HasColumnType("double precision"); + + b.HasKey("Id"); + + b.HasIndex("GuidedPathId"); + + b.HasIndex("TriggerGeoPointId"); + + b.ToTable("GuidedSteps"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.QuizQuestion", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("GuidedStepId") + .HasColumnType("text"); + + b.Property("IsSlidingPuzzle") + .HasColumnType("boolean"); + + b.Property>("Label") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("PuzzleCols") + .HasColumnType("integer"); + + b.Property("PuzzleImageId") + .HasColumnType("text"); + + b.Property("PuzzleRows") + .HasColumnType("integer"); + + b.Property("ResourceId") + .HasColumnType("text"); + + b.Property>("Responses") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("SectionQuizId") + .HasColumnType("text"); + + b.Property("ValidationQuestionType") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("GuidedStepId"); + + b.HasIndex("PuzzleImageId"); + + b.HasIndex("ResourceId"); + + b.HasIndex("SectionQuizId"); + + b.ToTable("QuizQuestions"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionEvent+MapAnnotation", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("Geometry") + .HasColumnType("geometry"); + + b.Property("GeometryType") + .HasColumnType("integer"); + + b.Property("Icon") + .HasColumnType("text"); + + b.Property("IconResourceId") + .HasColumnType("text"); + + b.Property>("Label") + .HasColumnType("jsonb"); + + b.Property("PolyColor") + .HasColumnType("text"); + + b.Property("ProgrammeBlockId") + .HasColumnType("text"); + + b.Property("SectionEventId") + .HasColumnType("text"); + + b.Property>("Type") + .HasColumnType("jsonb"); + + b.HasKey("Id"); + + b.HasIndex("IconResourceId"); + + b.HasIndex("ProgrammeBlockId"); + + b.HasIndex("SectionEventId"); + + b.ToTable("MapAnnotations"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionEvent+ProgrammeBlock", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property>("Description") + .HasColumnType("jsonb"); + + b.Property("EndTime") + .HasColumnType("timestamp with time zone"); + + b.Property("SectionEventId") + .HasColumnType("text"); + + b.Property("StartTime") + .HasColumnType("timestamp with time zone"); + + b.Property>("Title") + .HasColumnType("jsonb"); + + b.HasKey("Id"); + + b.HasIndex("SectionEventId"); + + b.ToTable("ProgrammeBlocks"); + }); + + modelBuilder.Entity("ManagerService.Data.SubscriptionPlan", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AiRequestsPerMonth") + .HasColumnType("integer"); + + b.Property("HasAdvancedStats") + .HasColumnType("boolean"); + + b.Property("HasStats") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("StatsHistoryDays") + .HasColumnType("integer"); + + b.Property("StorageQuotaBytes") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.ToTable("SubscriptionPlans"); + + b.HasData( + new + { + Id = "plan-starter", + AiRequestsPerMonth = 0, + HasAdvancedStats = false, + HasStats = false, + Name = "Starter", + StatsHistoryDays = 30, + StorageQuotaBytes = 1073741824L + }, + new + { + Id = "plan-standard", + AiRequestsPerMonth = 500, + HasAdvancedStats = false, + HasStats = true, + Name = "Standard", + StatsHistoryDays = 30, + StorageQuotaBytes = 10737418240L + }, + new + { + Id = "plan-premium", + AiRequestsPerMonth = 2000, + HasAdvancedStats = true, + HasStats = true, + Name = "Premium", + StatsHistoryDays = 0, + StorageQuotaBytes = 53687091200L + }); + }); + + modelBuilder.Entity("ManagerService.Data.User", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("DateCreation") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .IsRequired() + .HasColumnType("text"); + + b.Property("FirstName") + .HasColumnType("text"); + + b.Property("InstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("text"); + + b.Property("Password") + .IsRequired() + .HasColumnType("text"); + + b.Property("Role") + .HasColumnType("integer"); + + b.Property("Token") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("ManagerService.Data.VisitEvent", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AppType") + .HasColumnType("integer"); + + b.Property("ConfigurationId") + .HasColumnType("text"); + + b.Property("DurationSeconds") + .HasColumnType("integer"); + + b.Property("EventType") + .HasColumnType("integer"); + + b.Property("InstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("Language") + .HasColumnType("text"); + + b.Property("Metadata") + .HasColumnType("text"); + + b.Property("SectionId") + .HasColumnType("text"); + + b.Property("SessionId") + .IsRequired() + .HasColumnType("text"); + + b.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId"); + + b.HasIndex("Timestamp"); + + b.ToTable("VisitEvents"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionAgenda", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property("AgendaMapProvider") + .HasColumnType("integer"); + + b.Property>("AgendaResourceIds") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("IsOnlineAgenda") + .HasColumnType("boolean"); + + b.HasDiscriminator().HasValue("Agenda"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionArticle", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property>("ArticleAudioIds") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property>("ArticleContent") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property>("ArticleContents") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("ArticleIsContentTop") + .HasColumnType("boolean"); + + b.Property("ArticleIsReadAudioAuto") + .HasColumnType("boolean"); + + b.HasDiscriminator().HasValue("Article"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionEvent", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property("BaseSectionMapId") + .HasColumnType("text"); + + b.Property("EndDate") + .HasColumnType("timestamp with time zone"); + + b.Property>("ParcoursIds") + .HasColumnType("jsonb"); + + b.Property("StartDate") + .HasColumnType("timestamp with time zone"); + + b.HasIndex("BaseSectionMapId"); + + b.HasDiscriminator().HasValue("Event"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionGame", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property>("GameMessageDebut") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property>("GameMessageFin") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("GamePuzzleCols") + .HasColumnType("integer"); + + b.Property("GamePuzzleImageId") + .HasColumnType("text"); + + b.Property("GamePuzzleRows") + .HasColumnType("integer"); + + b.Property("GameType") + .HasColumnType("integer"); + + b.HasIndex("GamePuzzleImageId"); + + b.HasDiscriminator().HasValue("Game"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionMap", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property("IsListViewEnabled") + .HasColumnType("boolean"); + + b.Property>("MapCategories") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("MapCenterLatitude") + .HasColumnType("text"); + + b.Property("MapCenterLongitude") + .HasColumnType("text"); + + b.Property("MapMapProvider") + .HasColumnType("integer"); + + b.Property("MapMapType") + .HasColumnType("integer"); + + b.Property("MapResourceId") + .HasColumnType("text"); + + b.Property("MapTypeMapbox") + .HasColumnType("integer"); + + b.Property("MapZoom") + .HasColumnType("integer"); + + b.HasIndex("MapResourceId"); + + b.HasDiscriminator().HasValue("Map"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionMenu", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.HasDiscriminator().HasValue("Menu"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionParcours", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property("BaseSectionMapId") + .HasColumnType("text"); + + b.Property("ShowMap") + .HasColumnType("boolean"); + + b.HasIndex("BaseSectionMapId"); + + b.ToTable("Sections", t => + { + t.Property("BaseSectionMapId") + .HasColumnName("SectionParcours_BaseSectionMapId"); + }); + + b.HasDiscriminator().HasValue("Parcours"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionPdf", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property>("PDFOrderedTranslationAndResources") + .IsRequired() + .HasColumnType("jsonb"); + + b.HasDiscriminator().HasValue("PDF"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionQuiz", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property>("QuizBadLevel") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property>("QuizGoodLevel") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property>("QuizGreatLevel") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property>("QuizMediumLevel") + .IsRequired() + .HasColumnType("jsonb"); + + b.HasDiscriminator().HasValue("Quiz"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionSlider", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property>("SliderContents") + .IsRequired() + .HasColumnType("jsonb"); + + b.HasDiscriminator().HasValue("Slider"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionVideo", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property("VideoSource") + .IsRequired() + .HasColumnType("text"); + + b.HasDiscriminator().HasValue("Video"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionWeather", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property("WeatherCity") + .HasColumnType("text"); + + b.Property("WeatherResult") + .HasColumnType("text"); + + b.Property("WeatherUpdatedDate") + .HasColumnType("timestamp with time zone"); + + b.HasDiscriminator().HasValue("Weather"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionWeb", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property("WebSource") + .IsRequired() + .HasColumnType("text"); + + b.HasDiscriminator().HasValue("Web"); + }); + + modelBuilder.Entity("ManagerService.Data.ApiKey", b => + { + b.HasOne("ManagerService.Data.Instance", "Instance") + .WithMany() + .HasForeignKey("InstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Instance"); + }); + + modelBuilder.Entity("ManagerService.Data.AppConfigurationLink", b => + { + b.HasOne("ManagerService.Data.ApplicationInstance", "ApplicationInstance") + .WithMany("Configurations") + .HasForeignKey("ApplicationInstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ManagerService.Data.Configuration", "Configuration") + .WithMany() + .HasForeignKey("ConfigurationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ManagerService.Data.Device", "Device") + .WithMany() + .HasForeignKey("DeviceId"); + + b.Navigation("ApplicationInstance"); + + b.Navigation("Configuration"); + + b.Navigation("Device"); + }); + + modelBuilder.Entity("ManagerService.Data.ApplicationInstance", b => + { + b.HasOne("ManagerService.Data.SubSection.SectionEvent", "SectionEvent") + .WithMany() + .HasForeignKey("SectionEventId"); + + b.Navigation("SectionEvent"); + }); + + modelBuilder.Entity("ManagerService.Data.Device", b => + { + b.HasOne("ManagerService.Data.Configuration", "Configuration") + .WithMany() + .HasForeignKey("ConfigurationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Configuration"); + }); + + modelBuilder.Entity("ManagerService.Data.Instance", b => + { + b.HasOne("ManagerService.Data.SubscriptionPlan", "SubscriptionPlan") + .WithMany() + .HasForeignKey("SubscriptionPlanId"); + + b.Navigation("SubscriptionPlan"); + }); + + modelBuilder.Entity("ManagerService.Data.Section", b => + { + b.HasOne("ManagerService.Data.SubSection.SectionMenu", null) + .WithMany("MenuSections") + .HasForeignKey("SectionMenuId"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.EventAgenda", b => + { + b.HasOne("ManagerService.Data.Resource", "Resource") + .WithMany() + .HasForeignKey("ResourceId"); + + b.HasOne("ManagerService.Data.SubSection.SectionAgenda", "SectionAgenda") + .WithMany("EventAgendas") + .HasForeignKey("SectionAgendaId"); + + b.HasOne("ManagerService.Data.SubSection.SectionEvent", "SectionEvent") + .WithMany() + .HasForeignKey("SectionEventId"); + + b.HasOne("ManagerService.Data.Resource", "VideoResource") + .WithMany() + .HasForeignKey("VideoResourceId"); + + b.Navigation("Resource"); + + b.Navigation("SectionAgenda"); + + b.Navigation("SectionEvent"); + + b.Navigation("VideoResource"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.GeoPoint", b => + { + b.HasOne("ManagerService.Data.SubSection.SectionEvent", "SectionEvent") + .WithMany() + .HasForeignKey("SectionEventId"); + + b.HasOne("ManagerService.Data.SubSection.SectionMap", "SectionMap") + .WithMany("MapPoints") + .HasForeignKey("SectionMapId"); + + b.Navigation("SectionEvent"); + + b.Navigation("SectionMap"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.GuidedPath", b => + { + b.HasOne("ManagerService.Data.Resource", "ImageResource") + .WithMany() + .HasForeignKey("ImageResourceId"); + + b.HasOne("ManagerService.Data.SubSection.SectionEvent", "SectionEvent") + .WithMany() + .HasForeignKey("SectionEventId"); + + b.HasOne("ManagerService.Data.SubSection.SectionMap", "SectionMap") + .WithMany() + .HasForeignKey("SectionMapId"); + + b.HasOne("ManagerService.Data.SubSection.SectionParcours", "SectionParcours") + .WithMany("GuidedPaths") + .HasForeignKey("SectionParcoursId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("ImageResource"); + + b.Navigation("SectionEvent"); + + b.Navigation("SectionMap"); + + b.Navigation("SectionParcours"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.GuidedStep", b => + { + b.HasOne("ManagerService.Data.SubSection.GuidedPath", "GuidedPath") + .WithMany("Steps") + .HasForeignKey("GuidedPathId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ManagerService.Data.SubSection.GeoPoint", "TriggerGeoPoint") + .WithMany() + .HasForeignKey("TriggerGeoPointId"); + + b.Navigation("GuidedPath"); + + b.Navigation("TriggerGeoPoint"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.QuizQuestion", b => + { + b.HasOne("ManagerService.Data.SubSection.GuidedStep", "GuidedStep") + .WithMany("QuizQuestions") + .HasForeignKey("GuidedStepId"); + + b.HasOne("ManagerService.Data.Resource", "PuzzleImage") + .WithMany() + .HasForeignKey("PuzzleImageId"); + + b.HasOne("ManagerService.Data.Resource", "Resource") + .WithMany() + .HasForeignKey("ResourceId"); + + b.HasOne("ManagerService.Data.SubSection.SectionQuiz", "SectionQuiz") + .WithMany("QuizQuestions") + .HasForeignKey("SectionQuizId"); + + b.Navigation("GuidedStep"); + + b.Navigation("PuzzleImage"); + + b.Navigation("Resource"); + + b.Navigation("SectionQuiz"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionEvent+MapAnnotation", b => + { + b.HasOne("ManagerService.Data.Resource", "IconResource") + .WithMany() + .HasForeignKey("IconResourceId"); + + b.HasOne("ManagerService.Data.SubSection.SectionEvent+ProgrammeBlock", null) + .WithMany("MapAnnotations") + .HasForeignKey("ProgrammeBlockId"); + + b.HasOne("ManagerService.Data.SubSection.SectionEvent", null) + .WithMany("GlobalMapAnnotations") + .HasForeignKey("SectionEventId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("IconResource"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionEvent+ProgrammeBlock", b => + { + b.HasOne("ManagerService.Data.SubSection.SectionEvent", null) + .WithMany("Programme") + .HasForeignKey("SectionEventId"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionEvent", b => + { + b.HasOne("ManagerService.Data.SubSection.SectionMap", "BaseMap") + .WithMany() + .HasForeignKey("BaseSectionMapId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("BaseMap"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionGame", b => + { + b.HasOne("ManagerService.Data.Resource", "GamePuzzleImage") + .WithMany() + .HasForeignKey("GamePuzzleImageId"); + + b.Navigation("GamePuzzleImage"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionMap", b => + { + b.HasOne("ManagerService.Data.Resource", "MapResource") + .WithMany() + .HasForeignKey("MapResourceId"); + + b.Navigation("MapResource"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionParcours", b => + { + b.HasOne("ManagerService.Data.SubSection.SectionMap", "BaseMap") + .WithMany() + .HasForeignKey("BaseSectionMapId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("BaseMap"); + }); + + modelBuilder.Entity("ManagerService.Data.ApplicationInstance", b => + { + b.Navigation("Configurations"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.GuidedPath", b => + { + b.Navigation("Steps"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.GuidedStep", b => + { + b.Navigation("QuizQuestions"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionEvent+ProgrammeBlock", b => + { + b.Navigation("MapAnnotations"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionAgenda", b => + { + b.Navigation("EventAgendas"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionEvent", b => + { + b.Navigation("GlobalMapAnnotations"); + + b.Navigation("Programme"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionMap", b => + { + b.Navigation("MapPoints"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionMenu", b => + { + b.Navigation("MenuSections"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionParcours", b => + { + b.Navigation("GuidedPaths"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionQuiz", b => + { + b.Navigation("QuizQuestions"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ManagerService/Migrations/20260629150749_MoveGameModeToGuidedPath.cs b/ManagerService/Migrations/20260629150749_MoveGameModeToGuidedPath.cs new file mode 100644 index 0000000..b97a0a1 --- /dev/null +++ b/ManagerService/Migrations/20260629150749_MoveGameModeToGuidedPath.cs @@ -0,0 +1,101 @@ +using System.Collections.Generic; +using ManagerService.DTOs; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace ManagerService.Migrations +{ + /// + public partial class MoveGameModeToGuidedPath : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "IsGameMode", + table: "Sections"); + + migrationBuilder.DropColumn( + name: "SectionParcours_GameMessageDebut", + table: "Sections"); + + migrationBuilder.DropColumn( + name: "SectionParcours_GameMessageFin", + table: "Sections"); + + migrationBuilder.AddColumn( + name: "GameMessageDebut", + table: "GuidedPaths", + type: "jsonb", + nullable: true); + + migrationBuilder.AddColumn( + name: "GameMessageFin", + table: "GuidedPaths", + type: "jsonb", + nullable: true); + + migrationBuilder.AddColumn( + name: "IsGameMode", + table: "GuidedPaths", + type: "boolean", + nullable: false, + defaultValue: false); + + migrationBuilder.CreateIndex( + name: "IX_GuidedPaths_ImageResourceId", + table: "GuidedPaths", + column: "ImageResourceId"); + + migrationBuilder.AddForeignKey( + name: "FK_GuidedPaths_Resources_ImageResourceId", + table: "GuidedPaths", + column: "ImageResourceId", + principalTable: "Resources", + principalColumn: "Id"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_GuidedPaths_Resources_ImageResourceId", + table: "GuidedPaths"); + + migrationBuilder.DropIndex( + name: "IX_GuidedPaths_ImageResourceId", + table: "GuidedPaths"); + + migrationBuilder.DropColumn( + name: "GameMessageDebut", + table: "GuidedPaths"); + + migrationBuilder.DropColumn( + name: "GameMessageFin", + table: "GuidedPaths"); + + migrationBuilder.DropColumn( + name: "IsGameMode", + table: "GuidedPaths"); + + migrationBuilder.AddColumn( + name: "IsGameMode", + table: "Sections", + type: "boolean", + nullable: true); + + migrationBuilder.AddColumn>( + name: "SectionParcours_GameMessageDebut", + table: "Sections", + type: "jsonb", + nullable: true); + + migrationBuilder.AddColumn>( + name: "SectionParcours_GameMessageFin", + table: "Sections", + type: "jsonb", + nullable: true); + } + } +} diff --git a/ManagerService/Migrations/20260708094421_RemoveGuidedStepTriggerGeoPoint.Designer.cs b/ManagerService/Migrations/20260708094421_RemoveGuidedStepTriggerGeoPoint.Designer.cs new file mode 100644 index 0000000..554b973 --- /dev/null +++ b/ManagerService/Migrations/20260708094421_RemoveGuidedStepTriggerGeoPoint.Designer.cs @@ -0,0 +1,1642 @@ +// +using System; +using System.Collections.Generic; +using Manager.DTOs; +using ManagerService.DTOs; +using ManagerService.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using NetTopologySuite.Geometries; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace ManagerService.Migrations +{ + [DbContext(typeof(MyInfoMateDbContext))] + [Migration("20260708094421_RemoveGuidedStepTriggerGeoPoint")] + partial class RemoveGuidedStepTriggerGeoPoint + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.2") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "postgis"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("ManagerService.Data.ApiKey", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AppType") + .HasColumnType("integer"); + + b.Property("DateCreation") + .HasColumnType("timestamp with time zone"); + + b.Property("DateExpiration") + .HasColumnType("timestamp with time zone"); + + b.Property("InstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("KeyHash") + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId"); + + b.ToTable("ApiKeys"); + }); + + modelBuilder.Entity("ManagerService.Data.AppConfigurationLink", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("ApplicationInstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("ConfigurationId") + .IsRequired() + .HasColumnType("text"); + + b.Property("DeviceId") + .HasColumnType("text"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsDate") + .HasColumnType("boolean"); + + b.Property("IsHour") + .HasColumnType("boolean"); + + b.Property("IsSectionImageBackground") + .HasColumnType("boolean"); + + b.Property("LayoutMainPage") + .HasColumnType("integer"); + + b.Property("LoaderImageId") + .HasColumnType("text"); + + b.Property("LoaderImageUrl") + .HasColumnType("text"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("PrimaryColor") + .HasColumnType("text"); + + b.Property("RoundedValue") + .HasColumnType("integer"); + + b.Property("ScreenPercentageSectionsMainPage") + .HasColumnType("integer"); + + b.Property("SecondaryColor") + .HasColumnType("text"); + + b.Property("WeightMasonryGrid") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationInstanceId"); + + b.HasIndex("ConfigurationId"); + + b.HasIndex("DeviceId"); + + b.ToTable("AppConfigurationLinks"); + }); + + modelBuilder.Entity("ManagerService.Data.ApplicationInstance", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AppType") + .HasColumnType("integer"); + + b.Property("InstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsAssistant") + .HasColumnType("boolean"); + + b.PrimitiveCollection>("Languages") + .HasColumnType("text[]"); + + b.Property("LayoutMainPage") + .HasColumnType("integer"); + + b.Property("LoaderImageId") + .HasColumnType("text"); + + b.Property("LoaderImageUrl") + .HasColumnType("text"); + + b.Property("MainImageId") + .HasColumnType("text"); + + b.Property("MainImageUrl") + .HasColumnType("text"); + + b.Property("PrimaryColor") + .HasColumnType("text"); + + b.Property("SecondaryColor") + .HasColumnType("text"); + + b.Property("SectionEventId") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("SectionEventId"); + + b.ToTable("ApplicationInstances"); + }); + + modelBuilder.Entity("ManagerService.Data.AuditLog", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("Action") + .HasColumnType("text"); + + b.Property("EntityId") + .HasColumnType("text"); + + b.Property("EntityType") + .HasColumnType("text"); + + b.Property("InstanceId") + .HasColumnType("text"); + + b.Property("NewValues") + .HasColumnType("text"); + + b.Property("OldValues") + .HasColumnType("text"); + + b.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("AuditLogs"); + }); + + modelBuilder.Entity("ManagerService.Data.Configuration", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("DateCreation") + .HasColumnType("timestamp with time zone"); + + b.Property("ImageId") + .HasColumnType("text"); + + b.Property("ImageSource") + .HasColumnType("text"); + + b.Property("InstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsOffline") + .HasColumnType("boolean"); + + b.Property("IsQRCode") + .HasColumnType("boolean"); + + b.Property("IsSearchNumber") + .HasColumnType("boolean"); + + b.Property("IsSearchText") + .HasColumnType("boolean"); + + b.Property("Label") + .IsRequired() + .HasColumnType("text"); + + b.PrimitiveCollection>("Languages") + .HasColumnType("text[]"); + + b.Property("LoaderImageId") + .HasColumnType("text"); + + b.Property("LoaderImageUrl") + .HasColumnType("text"); + + b.Property("PrimaryColor") + .HasColumnType("text"); + + b.Property("SecondaryColor") + .HasColumnType("text"); + + b.Property("Title") + .IsRequired() + .HasColumnType("jsonb"); + + b.HasKey("Id"); + + b.ToTable("Configurations"); + }); + + modelBuilder.Entity("ManagerService.Data.Device", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AppVersion") + .HasColumnType("text"); + + b.Property("BatteryLevel") + .HasColumnType("text"); + + b.Property("ConfigurationId") + .IsRequired() + .HasColumnType("text"); + + b.Property("Connected") + .HasColumnType("boolean"); + + b.Property("ConnectionLevel") + .HasColumnType("text"); + + b.Property("DateCreation") + .HasColumnType("timestamp with time zone"); + + b.Property("DateUpdate") + .HasColumnType("timestamp with time zone"); + + b.Property("Identifier") + .HasColumnType("text"); + + b.Property("InstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("IpAddressETH") + .HasColumnType("text"); + + b.Property("IpAddressWLAN") + .HasColumnType("text"); + + b.Property("LastBatteryLevel") + .HasColumnType("timestamp with time zone"); + + b.Property("LastConnectionLevel") + .HasColumnType("timestamp with time zone"); + + b.Property("LastSeen") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ConfigurationId"); + + b.ToTable("Devices"); + }); + + modelBuilder.Entity("ManagerService.Data.Instance", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AiRequestsPerMonth") + .HasColumnType("integer"); + + b.Property("AiRequestsThisMonth") + .HasColumnType("integer"); + + b.Property("AiUsageMonthKey") + .HasColumnType("text"); + + b.Property("DateCreation") + .HasColumnType("timestamp with time zone"); + + b.Property("HasAdvancedStats") + .HasColumnType("boolean"); + + b.Property("HasStats") + .HasColumnType("boolean"); + + b.Property("IsAssistant") + .HasColumnType("boolean"); + + b.Property("IsMobile") + .HasColumnType("boolean"); + + b.Property("IsPushNotification") + .HasColumnType("boolean"); + + b.Property("IsTablet") + .HasColumnType("boolean"); + + b.Property("IsVR") + .HasColumnType("boolean"); + + b.Property("IsWeb") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("PinCode") + .HasColumnType("text"); + + b.Property("PublicApiKey") + .HasColumnType("text"); + + b.Property("StatsHistoryDays") + .HasColumnType("integer"); + + b.Property("StorageQuotaBytes") + .HasColumnType("bigint"); + + b.Property("SubscriptionPlanId") + .HasColumnType("text"); + + b.Property("WebSlug") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("SubscriptionPlanId"); + + b.ToTable("Instances"); + }); + + modelBuilder.Entity("ManagerService.Data.PushNotification", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("Body") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreation") + .HasColumnType("timestamp with time zone"); + + b.Property("HangfireJobId") + .HasColumnType("text"); + + b.Property("InstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("ScheduledAt") + .HasColumnType("timestamp with time zone"); + + b.Property("SentAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("Topic") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId"); + + b.ToTable("PushNotifications"); + }); + + modelBuilder.Entity("ManagerService.Data.Resource", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("DateCreation") + .HasColumnType("timestamp with time zone"); + + b.Property("InstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("Label") + .IsRequired() + .HasColumnType("text"); + + b.Property("SizeBytes") + .HasColumnType("bigint"); + + b.Property("Type") + .HasColumnType("integer"); + + b.Property("Url") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Resources"); + }); + + modelBuilder.Entity("ManagerService.Data.Section", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("BeaconId") + .HasColumnType("integer"); + + b.Property("ConfigurationId") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreation") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .HasColumnType("jsonb"); + + b.Property("Discriminator") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ImageId") + .HasColumnType("text"); + + b.Property("ImageSource") + .HasColumnType("text"); + + b.Property("InstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsBeacon") + .HasColumnType("boolean"); + + b.Property("IsSubSection") + .HasColumnType("boolean"); + + b.Property("Label") + .IsRequired() + .HasColumnType("text"); + + b.Property("Latitude") + .HasColumnType("text"); + + b.Property("Longitude") + .HasColumnType("text"); + + b.Property("MeterZoneGPS") + .HasColumnType("integer"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("ParentId") + .HasColumnType("text"); + + b.Property("SectionMenuId") + .HasColumnType("text"); + + b.Property("Title") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Type") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("SectionMenuId"); + + b.ToTable("Sections"); + + b.HasDiscriminator().HasValue("Base"); + + b.UseTphMappingStrategy(); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.EventAgenda", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Address") + .HasColumnType("jsonb"); + + b.Property("DateAdded") + .HasColumnType("timestamp with time zone"); + + b.Property("DateFrom") + .HasColumnType("timestamp with time zone"); + + b.Property("DateTo") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Email") + .HasColumnType("text"); + + b.Property("IdVideoYoutube") + .HasColumnType("text"); + + b.Property("IsSynced") + .HasColumnType("boolean"); + + b.Property("Label") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Phone") + .HasColumnType("text"); + + b.Property("ResourceId") + .HasColumnType("text"); + + b.Property("SectionAgendaId") + .HasColumnType("text"); + + b.Property("SectionEventId") + .HasColumnType("text"); + + b.Property("SyncedImageUrl") + .HasColumnType("text"); + + b.Property("Type") + .HasColumnType("text"); + + b.Property("VideoLink") + .HasColumnType("text"); + + b.Property("VideoResourceId") + .HasColumnType("text"); + + b.Property("Website") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ResourceId"); + + b.HasIndex("SectionAgendaId"); + + b.HasIndex("SectionEventId"); + + b.HasIndex("VideoResourceId"); + + b.ToTable("EventAgendas"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.GeoPoint", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CategorieId") + .HasColumnType("integer"); + + b.Property("Contents") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Description") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Email") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Geometry") + .HasColumnType("geometry"); + + b.Property("ImageResourceId") + .HasColumnType("text"); + + b.Property("ImageUrl") + .HasColumnType("text"); + + b.Property("Phone") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("PolyColor") + .HasColumnType("text"); + + b.Property("Prices") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Schedules") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("SectionEventId") + .HasColumnType("text"); + + b.Property("SectionMapId") + .HasColumnType("text"); + + b.Property("Site") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Title") + .IsRequired() + .HasColumnType("jsonb"); + + b.HasKey("Id"); + + b.HasIndex("SectionEventId"); + + b.HasIndex("SectionMapId"); + + b.ToTable("GeoPoints"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.GuidedPath", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("Description") + .HasColumnType("jsonb"); + + b.Property("EstimatedDurationMinutes") + .HasColumnType("integer"); + + b.Property("GameMessageDebut") + .HasColumnType("jsonb"); + + b.Property("GameMessageFin") + .HasColumnType("jsonb"); + + b.Property("HideNextStepsUntilComplete") + .HasColumnType("boolean"); + + b.Property("ImageResourceId") + .HasColumnType("text"); + + b.Property("InstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsGameMode") + .HasColumnType("boolean"); + + b.Property("IsLinear") + .HasColumnType("boolean"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("RequireSuccessToAdvance") + .HasColumnType("boolean"); + + b.Property("SectionEventId") + .HasColumnType("text"); + + b.Property("SectionMapId") + .HasColumnType("text"); + + b.Property("SectionParcoursId") + .HasColumnType("text"); + + b.Property("Title") + .IsRequired() + .HasColumnType("jsonb"); + + b.HasKey("Id"); + + b.HasIndex("ImageResourceId"); + + b.HasIndex("SectionEventId"); + + b.HasIndex("SectionMapId"); + + b.HasIndex("SectionParcoursId"); + + b.ToTable("GuidedPaths"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.GuidedStep", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AudioIds") + .HasColumnType("jsonb"); + + b.Property("Contents") + .HasColumnType("jsonb"); + + b.Property("Description") + .HasColumnType("jsonb"); + + b.Property("FactContent") + .HasColumnType("jsonb"); + + b.Property("Geometry") + .HasColumnType("geometry"); + + b.Property("GuidedPathId") + .IsRequired() + .HasColumnType("text"); + + b.Property("ImageUrl") + .HasColumnType("text"); + + b.Property("IsHiddenInitially") + .HasColumnType("boolean"); + + b.Property("IsStepLocked") + .HasColumnType("boolean"); + + b.Property("IsStepTimer") + .HasColumnType("boolean"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("TimerExpiredMessage") + .HasColumnType("jsonb"); + + b.Property("TimerSeconds") + .HasColumnType("integer"); + + b.Property("Title") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("ZoneRadiusMeters") + .HasColumnType("double precision"); + + b.HasKey("Id"); + + b.HasIndex("GuidedPathId"); + + b.ToTable("GuidedSteps"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.QuizQuestion", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("GuidedStepId") + .HasColumnType("text"); + + b.Property("IsSlidingPuzzle") + .HasColumnType("boolean"); + + b.Property>("Label") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("PuzzleCols") + .HasColumnType("integer"); + + b.Property("PuzzleImageId") + .HasColumnType("text"); + + b.Property("PuzzleRows") + .HasColumnType("integer"); + + b.Property("ResourceId") + .HasColumnType("text"); + + b.Property>("Responses") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("SectionQuizId") + .HasColumnType("text"); + + b.Property("ValidationQuestionType") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("GuidedStepId"); + + b.HasIndex("PuzzleImageId"); + + b.HasIndex("ResourceId"); + + b.HasIndex("SectionQuizId"); + + b.ToTable("QuizQuestions"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionEvent+MapAnnotation", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("Geometry") + .HasColumnType("geometry"); + + b.Property("GeometryType") + .HasColumnType("integer"); + + b.Property("Icon") + .HasColumnType("text"); + + b.Property("IconResourceId") + .HasColumnType("text"); + + b.Property>("Label") + .HasColumnType("jsonb"); + + b.Property("PolyColor") + .HasColumnType("text"); + + b.Property("ProgrammeBlockId") + .HasColumnType("text"); + + b.Property("SectionEventId") + .HasColumnType("text"); + + b.Property>("Type") + .HasColumnType("jsonb"); + + b.HasKey("Id"); + + b.HasIndex("IconResourceId"); + + b.HasIndex("ProgrammeBlockId"); + + b.HasIndex("SectionEventId"); + + b.ToTable("MapAnnotations"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionEvent+ProgrammeBlock", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property>("Description") + .HasColumnType("jsonb"); + + b.Property("EndTime") + .HasColumnType("timestamp with time zone"); + + b.Property("SectionEventId") + .HasColumnType("text"); + + b.Property("StartTime") + .HasColumnType("timestamp with time zone"); + + b.Property>("Title") + .HasColumnType("jsonb"); + + b.HasKey("Id"); + + b.HasIndex("SectionEventId"); + + b.ToTable("ProgrammeBlocks"); + }); + + modelBuilder.Entity("ManagerService.Data.SubscriptionPlan", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AiRequestsPerMonth") + .HasColumnType("integer"); + + b.Property("HasAdvancedStats") + .HasColumnType("boolean"); + + b.Property("HasStats") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("StatsHistoryDays") + .HasColumnType("integer"); + + b.Property("StorageQuotaBytes") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.ToTable("SubscriptionPlans"); + + b.HasData( + new + { + Id = "plan-starter", + AiRequestsPerMonth = 0, + HasAdvancedStats = false, + HasStats = false, + Name = "Starter", + StatsHistoryDays = 30, + StorageQuotaBytes = 1073741824L + }, + new + { + Id = "plan-standard", + AiRequestsPerMonth = 500, + HasAdvancedStats = false, + HasStats = true, + Name = "Standard", + StatsHistoryDays = 30, + StorageQuotaBytes = 10737418240L + }, + new + { + Id = "plan-premium", + AiRequestsPerMonth = 2000, + HasAdvancedStats = true, + HasStats = true, + Name = "Premium", + StatsHistoryDays = 0, + StorageQuotaBytes = 53687091200L + }); + }); + + modelBuilder.Entity("ManagerService.Data.User", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("DateCreation") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .IsRequired() + .HasColumnType("text"); + + b.Property("FirstName") + .HasColumnType("text"); + + b.Property("InstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("text"); + + b.Property("Password") + .IsRequired() + .HasColumnType("text"); + + b.Property("Role") + .HasColumnType("integer"); + + b.Property("Token") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("ManagerService.Data.VisitEvent", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AppType") + .HasColumnType("integer"); + + b.Property("ConfigurationId") + .HasColumnType("text"); + + b.Property("DurationSeconds") + .HasColumnType("integer"); + + b.Property("EventType") + .HasColumnType("integer"); + + b.Property("InstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("Language") + .HasColumnType("text"); + + b.Property("Metadata") + .HasColumnType("text"); + + b.Property("SectionId") + .HasColumnType("text"); + + b.Property("SessionId") + .IsRequired() + .HasColumnType("text"); + + b.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId"); + + b.HasIndex("Timestamp"); + + b.ToTable("VisitEvents"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionAgenda", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property("AgendaMapProvider") + .HasColumnType("integer"); + + b.Property>("AgendaResourceIds") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("IsOnlineAgenda") + .HasColumnType("boolean"); + + b.HasDiscriminator().HasValue("Agenda"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionArticle", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property>("ArticleAudioIds") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property>("ArticleContent") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property>("ArticleContents") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("ArticleIsContentTop") + .HasColumnType("boolean"); + + b.Property("ArticleIsReadAudioAuto") + .HasColumnType("boolean"); + + b.HasDiscriminator().HasValue("Article"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionEvent", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property("BaseSectionMapId") + .HasColumnType("text"); + + b.Property("EndDate") + .HasColumnType("timestamp with time zone"); + + b.Property>("ParcoursIds") + .HasColumnType("jsonb"); + + b.Property("StartDate") + .HasColumnType("timestamp with time zone"); + + b.HasIndex("BaseSectionMapId"); + + b.HasDiscriminator().HasValue("Event"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionGame", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property>("GameMessageDebut") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property>("GameMessageFin") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("GamePuzzleCols") + .HasColumnType("integer"); + + b.Property("GamePuzzleImageId") + .HasColumnType("text"); + + b.Property("GamePuzzleRows") + .HasColumnType("integer"); + + b.Property("GameType") + .HasColumnType("integer"); + + b.HasIndex("GamePuzzleImageId"); + + b.HasDiscriminator().HasValue("Game"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionMap", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property("IsListViewEnabled") + .HasColumnType("boolean"); + + b.Property>("MapCategories") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("MapCenterLatitude") + .HasColumnType("text"); + + b.Property("MapCenterLongitude") + .HasColumnType("text"); + + b.Property("MapMapProvider") + .HasColumnType("integer"); + + b.Property("MapMapType") + .HasColumnType("integer"); + + b.Property("MapResourceId") + .HasColumnType("text"); + + b.Property("MapTypeMapbox") + .HasColumnType("integer"); + + b.Property("MapZoom") + .HasColumnType("integer"); + + b.HasIndex("MapResourceId"); + + b.HasDiscriminator().HasValue("Map"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionMenu", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.HasDiscriminator().HasValue("Menu"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionParcours", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property("BaseSectionMapId") + .HasColumnType("text"); + + b.Property("ShowMap") + .HasColumnType("boolean"); + + b.HasIndex("BaseSectionMapId"); + + b.ToTable("Sections", t => + { + t.Property("BaseSectionMapId") + .HasColumnName("SectionParcours_BaseSectionMapId"); + }); + + b.HasDiscriminator().HasValue("Parcours"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionPdf", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property>("PDFOrderedTranslationAndResources") + .IsRequired() + .HasColumnType("jsonb"); + + b.HasDiscriminator().HasValue("PDF"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionQuiz", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property>("QuizBadLevel") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property>("QuizGoodLevel") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property>("QuizGreatLevel") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property>("QuizMediumLevel") + .IsRequired() + .HasColumnType("jsonb"); + + b.HasDiscriminator().HasValue("Quiz"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionSlider", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property>("SliderContents") + .IsRequired() + .HasColumnType("jsonb"); + + b.HasDiscriminator().HasValue("Slider"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionVideo", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property("VideoSource") + .IsRequired() + .HasColumnType("text"); + + b.HasDiscriminator().HasValue("Video"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionWeather", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property("WeatherCity") + .HasColumnType("text"); + + b.Property("WeatherResult") + .HasColumnType("text"); + + b.Property("WeatherUpdatedDate") + .HasColumnType("timestamp with time zone"); + + b.HasDiscriminator().HasValue("Weather"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionWeb", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property("WebSource") + .IsRequired() + .HasColumnType("text"); + + b.HasDiscriminator().HasValue("Web"); + }); + + modelBuilder.Entity("ManagerService.Data.ApiKey", b => + { + b.HasOne("ManagerService.Data.Instance", "Instance") + .WithMany() + .HasForeignKey("InstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Instance"); + }); + + modelBuilder.Entity("ManagerService.Data.AppConfigurationLink", b => + { + b.HasOne("ManagerService.Data.ApplicationInstance", "ApplicationInstance") + .WithMany("Configurations") + .HasForeignKey("ApplicationInstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ManagerService.Data.Configuration", "Configuration") + .WithMany() + .HasForeignKey("ConfigurationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ManagerService.Data.Device", "Device") + .WithMany() + .HasForeignKey("DeviceId"); + + b.Navigation("ApplicationInstance"); + + b.Navigation("Configuration"); + + b.Navigation("Device"); + }); + + modelBuilder.Entity("ManagerService.Data.ApplicationInstance", b => + { + b.HasOne("ManagerService.Data.SubSection.SectionEvent", "SectionEvent") + .WithMany() + .HasForeignKey("SectionEventId"); + + b.Navigation("SectionEvent"); + }); + + modelBuilder.Entity("ManagerService.Data.Device", b => + { + b.HasOne("ManagerService.Data.Configuration", "Configuration") + .WithMany() + .HasForeignKey("ConfigurationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Configuration"); + }); + + modelBuilder.Entity("ManagerService.Data.Instance", b => + { + b.HasOne("ManagerService.Data.SubscriptionPlan", "SubscriptionPlan") + .WithMany() + .HasForeignKey("SubscriptionPlanId"); + + b.Navigation("SubscriptionPlan"); + }); + + modelBuilder.Entity("ManagerService.Data.Section", b => + { + b.HasOne("ManagerService.Data.SubSection.SectionMenu", null) + .WithMany("MenuSections") + .HasForeignKey("SectionMenuId"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.EventAgenda", b => + { + b.HasOne("ManagerService.Data.Resource", "Resource") + .WithMany() + .HasForeignKey("ResourceId"); + + b.HasOne("ManagerService.Data.SubSection.SectionAgenda", "SectionAgenda") + .WithMany("EventAgendas") + .HasForeignKey("SectionAgendaId"); + + b.HasOne("ManagerService.Data.SubSection.SectionEvent", "SectionEvent") + .WithMany() + .HasForeignKey("SectionEventId"); + + b.HasOne("ManagerService.Data.Resource", "VideoResource") + .WithMany() + .HasForeignKey("VideoResourceId"); + + b.Navigation("Resource"); + + b.Navigation("SectionAgenda"); + + b.Navigation("SectionEvent"); + + b.Navigation("VideoResource"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.GeoPoint", b => + { + b.HasOne("ManagerService.Data.SubSection.SectionEvent", "SectionEvent") + .WithMany() + .HasForeignKey("SectionEventId"); + + b.HasOne("ManagerService.Data.SubSection.SectionMap", "SectionMap") + .WithMany("MapPoints") + .HasForeignKey("SectionMapId"); + + b.Navigation("SectionEvent"); + + b.Navigation("SectionMap"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.GuidedPath", b => + { + b.HasOne("ManagerService.Data.Resource", "ImageResource") + .WithMany() + .HasForeignKey("ImageResourceId"); + + b.HasOne("ManagerService.Data.SubSection.SectionEvent", "SectionEvent") + .WithMany() + .HasForeignKey("SectionEventId"); + + b.HasOne("ManagerService.Data.SubSection.SectionMap", "SectionMap") + .WithMany() + .HasForeignKey("SectionMapId"); + + b.HasOne("ManagerService.Data.SubSection.SectionParcours", "SectionParcours") + .WithMany("GuidedPaths") + .HasForeignKey("SectionParcoursId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("ImageResource"); + + b.Navigation("SectionEvent"); + + b.Navigation("SectionMap"); + + b.Navigation("SectionParcours"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.GuidedStep", b => + { + b.HasOne("ManagerService.Data.SubSection.GuidedPath", "GuidedPath") + .WithMany("Steps") + .HasForeignKey("GuidedPathId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("GuidedPath"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.QuizQuestion", b => + { + b.HasOne("ManagerService.Data.SubSection.GuidedStep", "GuidedStep") + .WithMany("QuizQuestions") + .HasForeignKey("GuidedStepId"); + + b.HasOne("ManagerService.Data.Resource", "PuzzleImage") + .WithMany() + .HasForeignKey("PuzzleImageId"); + + b.HasOne("ManagerService.Data.Resource", "Resource") + .WithMany() + .HasForeignKey("ResourceId"); + + b.HasOne("ManagerService.Data.SubSection.SectionQuiz", "SectionQuiz") + .WithMany("QuizQuestions") + .HasForeignKey("SectionQuizId"); + + b.Navigation("GuidedStep"); + + b.Navigation("PuzzleImage"); + + b.Navigation("Resource"); + + b.Navigation("SectionQuiz"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionEvent+MapAnnotation", b => + { + b.HasOne("ManagerService.Data.Resource", "IconResource") + .WithMany() + .HasForeignKey("IconResourceId"); + + b.HasOne("ManagerService.Data.SubSection.SectionEvent+ProgrammeBlock", null) + .WithMany("MapAnnotations") + .HasForeignKey("ProgrammeBlockId"); + + b.HasOne("ManagerService.Data.SubSection.SectionEvent", null) + .WithMany("GlobalMapAnnotations") + .HasForeignKey("SectionEventId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("IconResource"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionEvent+ProgrammeBlock", b => + { + b.HasOne("ManagerService.Data.SubSection.SectionEvent", null) + .WithMany("Programme") + .HasForeignKey("SectionEventId"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionEvent", b => + { + b.HasOne("ManagerService.Data.SubSection.SectionMap", "BaseMap") + .WithMany() + .HasForeignKey("BaseSectionMapId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("BaseMap"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionGame", b => + { + b.HasOne("ManagerService.Data.Resource", "GamePuzzleImage") + .WithMany() + .HasForeignKey("GamePuzzleImageId"); + + b.Navigation("GamePuzzleImage"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionMap", b => + { + b.HasOne("ManagerService.Data.Resource", "MapResource") + .WithMany() + .HasForeignKey("MapResourceId"); + + b.Navigation("MapResource"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionParcours", b => + { + b.HasOne("ManagerService.Data.SubSection.SectionMap", "BaseMap") + .WithMany() + .HasForeignKey("BaseSectionMapId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("BaseMap"); + }); + + modelBuilder.Entity("ManagerService.Data.ApplicationInstance", b => + { + b.Navigation("Configurations"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.GuidedPath", b => + { + b.Navigation("Steps"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.GuidedStep", b => + { + b.Navigation("QuizQuestions"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionEvent+ProgrammeBlock", b => + { + b.Navigation("MapAnnotations"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionAgenda", b => + { + b.Navigation("EventAgendas"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionEvent", b => + { + b.Navigation("GlobalMapAnnotations"); + + b.Navigation("Programme"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionMap", b => + { + b.Navigation("MapPoints"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionMenu", b => + { + b.Navigation("MenuSections"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionParcours", b => + { + b.Navigation("GuidedPaths"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionQuiz", b => + { + b.Navigation("QuizQuestions"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ManagerService/Migrations/20260708094421_RemoveGuidedStepTriggerGeoPoint.cs b/ManagerService/Migrations/20260708094421_RemoveGuidedStepTriggerGeoPoint.cs new file mode 100644 index 0000000..d6ac3a8 --- /dev/null +++ b/ManagerService/Migrations/20260708094421_RemoveGuidedStepTriggerGeoPoint.cs @@ -0,0 +1,48 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace ManagerService.Migrations +{ + /// + public partial class RemoveGuidedStepTriggerGeoPoint : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_GuidedSteps_GeoPoints_TriggerGeoPointId", + table: "GuidedSteps"); + + migrationBuilder.DropIndex( + name: "IX_GuidedSteps_TriggerGeoPointId", + table: "GuidedSteps"); + + migrationBuilder.DropColumn( + name: "TriggerGeoPointId", + table: "GuidedSteps"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "TriggerGeoPointId", + table: "GuidedSteps", + type: "integer", + nullable: true); + + migrationBuilder.CreateIndex( + name: "IX_GuidedSteps_TriggerGeoPointId", + table: "GuidedSteps", + column: "TriggerGeoPointId"); + + migrationBuilder.AddForeignKey( + name: "FK_GuidedSteps_GeoPoints_TriggerGeoPointId", + table: "GuidedSteps", + column: "TriggerGeoPointId", + principalTable: "GeoPoints", + principalColumn: "Id"); + } + } +} diff --git a/ManagerService/Migrations/20260708123715_AddGuidedStepIsGeoTriggered.Designer.cs b/ManagerService/Migrations/20260708123715_AddGuidedStepIsGeoTriggered.Designer.cs new file mode 100644 index 0000000..f742942 --- /dev/null +++ b/ManagerService/Migrations/20260708123715_AddGuidedStepIsGeoTriggered.Designer.cs @@ -0,0 +1,1645 @@ +// +using System; +using System.Collections.Generic; +using Manager.DTOs; +using ManagerService.DTOs; +using ManagerService.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using NetTopologySuite.Geometries; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace ManagerService.Migrations +{ + [DbContext(typeof(MyInfoMateDbContext))] + [Migration("20260708123715_AddGuidedStepIsGeoTriggered")] + partial class AddGuidedStepIsGeoTriggered + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.2") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "postgis"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("ManagerService.Data.ApiKey", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AppType") + .HasColumnType("integer"); + + b.Property("DateCreation") + .HasColumnType("timestamp with time zone"); + + b.Property("DateExpiration") + .HasColumnType("timestamp with time zone"); + + b.Property("InstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("KeyHash") + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId"); + + b.ToTable("ApiKeys"); + }); + + modelBuilder.Entity("ManagerService.Data.AppConfigurationLink", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("ApplicationInstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("ConfigurationId") + .IsRequired() + .HasColumnType("text"); + + b.Property("DeviceId") + .HasColumnType("text"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsDate") + .HasColumnType("boolean"); + + b.Property("IsHour") + .HasColumnType("boolean"); + + b.Property("IsSectionImageBackground") + .HasColumnType("boolean"); + + b.Property("LayoutMainPage") + .HasColumnType("integer"); + + b.Property("LoaderImageId") + .HasColumnType("text"); + + b.Property("LoaderImageUrl") + .HasColumnType("text"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("PrimaryColor") + .HasColumnType("text"); + + b.Property("RoundedValue") + .HasColumnType("integer"); + + b.Property("ScreenPercentageSectionsMainPage") + .HasColumnType("integer"); + + b.Property("SecondaryColor") + .HasColumnType("text"); + + b.Property("WeightMasonryGrid") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationInstanceId"); + + b.HasIndex("ConfigurationId"); + + b.HasIndex("DeviceId"); + + b.ToTable("AppConfigurationLinks"); + }); + + modelBuilder.Entity("ManagerService.Data.ApplicationInstance", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AppType") + .HasColumnType("integer"); + + b.Property("InstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsAssistant") + .HasColumnType("boolean"); + + b.PrimitiveCollection>("Languages") + .HasColumnType("text[]"); + + b.Property("LayoutMainPage") + .HasColumnType("integer"); + + b.Property("LoaderImageId") + .HasColumnType("text"); + + b.Property("LoaderImageUrl") + .HasColumnType("text"); + + b.Property("MainImageId") + .HasColumnType("text"); + + b.Property("MainImageUrl") + .HasColumnType("text"); + + b.Property("PrimaryColor") + .HasColumnType("text"); + + b.Property("SecondaryColor") + .HasColumnType("text"); + + b.Property("SectionEventId") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("SectionEventId"); + + b.ToTable("ApplicationInstances"); + }); + + modelBuilder.Entity("ManagerService.Data.AuditLog", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("Action") + .HasColumnType("text"); + + b.Property("EntityId") + .HasColumnType("text"); + + b.Property("EntityType") + .HasColumnType("text"); + + b.Property("InstanceId") + .HasColumnType("text"); + + b.Property("NewValues") + .HasColumnType("text"); + + b.Property("OldValues") + .HasColumnType("text"); + + b.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("AuditLogs"); + }); + + modelBuilder.Entity("ManagerService.Data.Configuration", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("DateCreation") + .HasColumnType("timestamp with time zone"); + + b.Property("ImageId") + .HasColumnType("text"); + + b.Property("ImageSource") + .HasColumnType("text"); + + b.Property("InstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsOffline") + .HasColumnType("boolean"); + + b.Property("IsQRCode") + .HasColumnType("boolean"); + + b.Property("IsSearchNumber") + .HasColumnType("boolean"); + + b.Property("IsSearchText") + .HasColumnType("boolean"); + + b.Property("Label") + .IsRequired() + .HasColumnType("text"); + + b.PrimitiveCollection>("Languages") + .HasColumnType("text[]"); + + b.Property("LoaderImageId") + .HasColumnType("text"); + + b.Property("LoaderImageUrl") + .HasColumnType("text"); + + b.Property("PrimaryColor") + .HasColumnType("text"); + + b.Property("SecondaryColor") + .HasColumnType("text"); + + b.Property("Title") + .IsRequired() + .HasColumnType("jsonb"); + + b.HasKey("Id"); + + b.ToTable("Configurations"); + }); + + modelBuilder.Entity("ManagerService.Data.Device", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AppVersion") + .HasColumnType("text"); + + b.Property("BatteryLevel") + .HasColumnType("text"); + + b.Property("ConfigurationId") + .IsRequired() + .HasColumnType("text"); + + b.Property("Connected") + .HasColumnType("boolean"); + + b.Property("ConnectionLevel") + .HasColumnType("text"); + + b.Property("DateCreation") + .HasColumnType("timestamp with time zone"); + + b.Property("DateUpdate") + .HasColumnType("timestamp with time zone"); + + b.Property("Identifier") + .HasColumnType("text"); + + b.Property("InstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("IpAddressETH") + .HasColumnType("text"); + + b.Property("IpAddressWLAN") + .HasColumnType("text"); + + b.Property("LastBatteryLevel") + .HasColumnType("timestamp with time zone"); + + b.Property("LastConnectionLevel") + .HasColumnType("timestamp with time zone"); + + b.Property("LastSeen") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ConfigurationId"); + + b.ToTable("Devices"); + }); + + modelBuilder.Entity("ManagerService.Data.Instance", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AiRequestsPerMonth") + .HasColumnType("integer"); + + b.Property("AiRequestsThisMonth") + .HasColumnType("integer"); + + b.Property("AiUsageMonthKey") + .HasColumnType("text"); + + b.Property("DateCreation") + .HasColumnType("timestamp with time zone"); + + b.Property("HasAdvancedStats") + .HasColumnType("boolean"); + + b.Property("HasStats") + .HasColumnType("boolean"); + + b.Property("IsAssistant") + .HasColumnType("boolean"); + + b.Property("IsMobile") + .HasColumnType("boolean"); + + b.Property("IsPushNotification") + .HasColumnType("boolean"); + + b.Property("IsTablet") + .HasColumnType("boolean"); + + b.Property("IsVR") + .HasColumnType("boolean"); + + b.Property("IsWeb") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("PinCode") + .HasColumnType("text"); + + b.Property("PublicApiKey") + .HasColumnType("text"); + + b.Property("StatsHistoryDays") + .HasColumnType("integer"); + + b.Property("StorageQuotaBytes") + .HasColumnType("bigint"); + + b.Property("SubscriptionPlanId") + .HasColumnType("text"); + + b.Property("WebSlug") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("SubscriptionPlanId"); + + b.ToTable("Instances"); + }); + + modelBuilder.Entity("ManagerService.Data.PushNotification", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("Body") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreation") + .HasColumnType("timestamp with time zone"); + + b.Property("HangfireJobId") + .HasColumnType("text"); + + b.Property("InstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("ScheduledAt") + .HasColumnType("timestamp with time zone"); + + b.Property("SentAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("Topic") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId"); + + b.ToTable("PushNotifications"); + }); + + modelBuilder.Entity("ManagerService.Data.Resource", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("DateCreation") + .HasColumnType("timestamp with time zone"); + + b.Property("InstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("Label") + .IsRequired() + .HasColumnType("text"); + + b.Property("SizeBytes") + .HasColumnType("bigint"); + + b.Property("Type") + .HasColumnType("integer"); + + b.Property("Url") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Resources"); + }); + + modelBuilder.Entity("ManagerService.Data.Section", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("BeaconId") + .HasColumnType("integer"); + + b.Property("ConfigurationId") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreation") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .HasColumnType("jsonb"); + + b.Property("Discriminator") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ImageId") + .HasColumnType("text"); + + b.Property("ImageSource") + .HasColumnType("text"); + + b.Property("InstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsBeacon") + .HasColumnType("boolean"); + + b.Property("IsSubSection") + .HasColumnType("boolean"); + + b.Property("Label") + .IsRequired() + .HasColumnType("text"); + + b.Property("Latitude") + .HasColumnType("text"); + + b.Property("Longitude") + .HasColumnType("text"); + + b.Property("MeterZoneGPS") + .HasColumnType("integer"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("ParentId") + .HasColumnType("text"); + + b.Property("SectionMenuId") + .HasColumnType("text"); + + b.Property("Title") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Type") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("SectionMenuId"); + + b.ToTable("Sections"); + + b.HasDiscriminator().HasValue("Base"); + + b.UseTphMappingStrategy(); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.EventAgenda", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Address") + .HasColumnType("jsonb"); + + b.Property("DateAdded") + .HasColumnType("timestamp with time zone"); + + b.Property("DateFrom") + .HasColumnType("timestamp with time zone"); + + b.Property("DateTo") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Email") + .HasColumnType("text"); + + b.Property("IdVideoYoutube") + .HasColumnType("text"); + + b.Property("IsSynced") + .HasColumnType("boolean"); + + b.Property("Label") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Phone") + .HasColumnType("text"); + + b.Property("ResourceId") + .HasColumnType("text"); + + b.Property("SectionAgendaId") + .HasColumnType("text"); + + b.Property("SectionEventId") + .HasColumnType("text"); + + b.Property("SyncedImageUrl") + .HasColumnType("text"); + + b.Property("Type") + .HasColumnType("text"); + + b.Property("VideoLink") + .HasColumnType("text"); + + b.Property("VideoResourceId") + .HasColumnType("text"); + + b.Property("Website") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ResourceId"); + + b.HasIndex("SectionAgendaId"); + + b.HasIndex("SectionEventId"); + + b.HasIndex("VideoResourceId"); + + b.ToTable("EventAgendas"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.GeoPoint", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CategorieId") + .HasColumnType("integer"); + + b.Property("Contents") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Description") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Email") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Geometry") + .HasColumnType("geometry"); + + b.Property("ImageResourceId") + .HasColumnType("text"); + + b.Property("ImageUrl") + .HasColumnType("text"); + + b.Property("Phone") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("PolyColor") + .HasColumnType("text"); + + b.Property("Prices") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Schedules") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("SectionEventId") + .HasColumnType("text"); + + b.Property("SectionMapId") + .HasColumnType("text"); + + b.Property("Site") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Title") + .IsRequired() + .HasColumnType("jsonb"); + + b.HasKey("Id"); + + b.HasIndex("SectionEventId"); + + b.HasIndex("SectionMapId"); + + b.ToTable("GeoPoints"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.GuidedPath", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("Description") + .HasColumnType("jsonb"); + + b.Property("EstimatedDurationMinutes") + .HasColumnType("integer"); + + b.Property("GameMessageDebut") + .HasColumnType("jsonb"); + + b.Property("GameMessageFin") + .HasColumnType("jsonb"); + + b.Property("HideNextStepsUntilComplete") + .HasColumnType("boolean"); + + b.Property("ImageResourceId") + .HasColumnType("text"); + + b.Property("InstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsGameMode") + .HasColumnType("boolean"); + + b.Property("IsLinear") + .HasColumnType("boolean"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("RequireSuccessToAdvance") + .HasColumnType("boolean"); + + b.Property("SectionEventId") + .HasColumnType("text"); + + b.Property("SectionMapId") + .HasColumnType("text"); + + b.Property("SectionParcoursId") + .HasColumnType("text"); + + b.Property("Title") + .IsRequired() + .HasColumnType("jsonb"); + + b.HasKey("Id"); + + b.HasIndex("ImageResourceId"); + + b.HasIndex("SectionEventId"); + + b.HasIndex("SectionMapId"); + + b.HasIndex("SectionParcoursId"); + + b.ToTable("GuidedPaths"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.GuidedStep", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AudioIds") + .HasColumnType("jsonb"); + + b.Property("Contents") + .HasColumnType("jsonb"); + + b.Property("Description") + .HasColumnType("jsonb"); + + b.Property("FactContent") + .HasColumnType("jsonb"); + + b.Property("Geometry") + .HasColumnType("geometry"); + + b.Property("GuidedPathId") + .IsRequired() + .HasColumnType("text"); + + b.Property("ImageUrl") + .HasColumnType("text"); + + b.Property("IsGeoTriggered") + .HasColumnType("boolean"); + + b.Property("IsHiddenInitially") + .HasColumnType("boolean"); + + b.Property("IsStepLocked") + .HasColumnType("boolean"); + + b.Property("IsStepTimer") + .HasColumnType("boolean"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("TimerExpiredMessage") + .HasColumnType("jsonb"); + + b.Property("TimerSeconds") + .HasColumnType("integer"); + + b.Property("Title") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("ZoneRadiusMeters") + .HasColumnType("double precision"); + + b.HasKey("Id"); + + b.HasIndex("GuidedPathId"); + + b.ToTable("GuidedSteps"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.QuizQuestion", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("GuidedStepId") + .HasColumnType("text"); + + b.Property("IsSlidingPuzzle") + .HasColumnType("boolean"); + + b.Property>("Label") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("PuzzleCols") + .HasColumnType("integer"); + + b.Property("PuzzleImageId") + .HasColumnType("text"); + + b.Property("PuzzleRows") + .HasColumnType("integer"); + + b.Property("ResourceId") + .HasColumnType("text"); + + b.Property>("Responses") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("SectionQuizId") + .HasColumnType("text"); + + b.Property("ValidationQuestionType") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("GuidedStepId"); + + b.HasIndex("PuzzleImageId"); + + b.HasIndex("ResourceId"); + + b.HasIndex("SectionQuizId"); + + b.ToTable("QuizQuestions"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionEvent+MapAnnotation", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("Geometry") + .HasColumnType("geometry"); + + b.Property("GeometryType") + .HasColumnType("integer"); + + b.Property("Icon") + .HasColumnType("text"); + + b.Property("IconResourceId") + .HasColumnType("text"); + + b.Property>("Label") + .HasColumnType("jsonb"); + + b.Property("PolyColor") + .HasColumnType("text"); + + b.Property("ProgrammeBlockId") + .HasColumnType("text"); + + b.Property("SectionEventId") + .HasColumnType("text"); + + b.Property>("Type") + .HasColumnType("jsonb"); + + b.HasKey("Id"); + + b.HasIndex("IconResourceId"); + + b.HasIndex("ProgrammeBlockId"); + + b.HasIndex("SectionEventId"); + + b.ToTable("MapAnnotations"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionEvent+ProgrammeBlock", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property>("Description") + .HasColumnType("jsonb"); + + b.Property("EndTime") + .HasColumnType("timestamp with time zone"); + + b.Property("SectionEventId") + .HasColumnType("text"); + + b.Property("StartTime") + .HasColumnType("timestamp with time zone"); + + b.Property>("Title") + .HasColumnType("jsonb"); + + b.HasKey("Id"); + + b.HasIndex("SectionEventId"); + + b.ToTable("ProgrammeBlocks"); + }); + + modelBuilder.Entity("ManagerService.Data.SubscriptionPlan", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AiRequestsPerMonth") + .HasColumnType("integer"); + + b.Property("HasAdvancedStats") + .HasColumnType("boolean"); + + b.Property("HasStats") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("StatsHistoryDays") + .HasColumnType("integer"); + + b.Property("StorageQuotaBytes") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.ToTable("SubscriptionPlans"); + + b.HasData( + new + { + Id = "plan-starter", + AiRequestsPerMonth = 0, + HasAdvancedStats = false, + HasStats = false, + Name = "Starter", + StatsHistoryDays = 30, + StorageQuotaBytes = 1073741824L + }, + new + { + Id = "plan-standard", + AiRequestsPerMonth = 500, + HasAdvancedStats = false, + HasStats = true, + Name = "Standard", + StatsHistoryDays = 30, + StorageQuotaBytes = 10737418240L + }, + new + { + Id = "plan-premium", + AiRequestsPerMonth = 2000, + HasAdvancedStats = true, + HasStats = true, + Name = "Premium", + StatsHistoryDays = 0, + StorageQuotaBytes = 53687091200L + }); + }); + + modelBuilder.Entity("ManagerService.Data.User", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("DateCreation") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .IsRequired() + .HasColumnType("text"); + + b.Property("FirstName") + .HasColumnType("text"); + + b.Property("InstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("text"); + + b.Property("Password") + .IsRequired() + .HasColumnType("text"); + + b.Property("Role") + .HasColumnType("integer"); + + b.Property("Token") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("ManagerService.Data.VisitEvent", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AppType") + .HasColumnType("integer"); + + b.Property("ConfigurationId") + .HasColumnType("text"); + + b.Property("DurationSeconds") + .HasColumnType("integer"); + + b.Property("EventType") + .HasColumnType("integer"); + + b.Property("InstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("Language") + .HasColumnType("text"); + + b.Property("Metadata") + .HasColumnType("text"); + + b.Property("SectionId") + .HasColumnType("text"); + + b.Property("SessionId") + .IsRequired() + .HasColumnType("text"); + + b.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId"); + + b.HasIndex("Timestamp"); + + b.ToTable("VisitEvents"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionAgenda", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property("AgendaMapProvider") + .HasColumnType("integer"); + + b.Property>("AgendaResourceIds") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("IsOnlineAgenda") + .HasColumnType("boolean"); + + b.HasDiscriminator().HasValue("Agenda"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionArticle", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property>("ArticleAudioIds") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property>("ArticleContent") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property>("ArticleContents") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("ArticleIsContentTop") + .HasColumnType("boolean"); + + b.Property("ArticleIsReadAudioAuto") + .HasColumnType("boolean"); + + b.HasDiscriminator().HasValue("Article"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionEvent", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property("BaseSectionMapId") + .HasColumnType("text"); + + b.Property("EndDate") + .HasColumnType("timestamp with time zone"); + + b.Property>("ParcoursIds") + .HasColumnType("jsonb"); + + b.Property("StartDate") + .HasColumnType("timestamp with time zone"); + + b.HasIndex("BaseSectionMapId"); + + b.HasDiscriminator().HasValue("Event"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionGame", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property>("GameMessageDebut") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property>("GameMessageFin") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("GamePuzzleCols") + .HasColumnType("integer"); + + b.Property("GamePuzzleImageId") + .HasColumnType("text"); + + b.Property("GamePuzzleRows") + .HasColumnType("integer"); + + b.Property("GameType") + .HasColumnType("integer"); + + b.HasIndex("GamePuzzleImageId"); + + b.HasDiscriminator().HasValue("Game"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionMap", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property("IsListViewEnabled") + .HasColumnType("boolean"); + + b.Property>("MapCategories") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("MapCenterLatitude") + .HasColumnType("text"); + + b.Property("MapCenterLongitude") + .HasColumnType("text"); + + b.Property("MapMapProvider") + .HasColumnType("integer"); + + b.Property("MapMapType") + .HasColumnType("integer"); + + b.Property("MapResourceId") + .HasColumnType("text"); + + b.Property("MapTypeMapbox") + .HasColumnType("integer"); + + b.Property("MapZoom") + .HasColumnType("integer"); + + b.HasIndex("MapResourceId"); + + b.HasDiscriminator().HasValue("Map"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionMenu", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.HasDiscriminator().HasValue("Menu"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionParcours", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property("BaseSectionMapId") + .HasColumnType("text"); + + b.Property("ShowMap") + .HasColumnType("boolean"); + + b.HasIndex("BaseSectionMapId"); + + b.ToTable("Sections", t => + { + t.Property("BaseSectionMapId") + .HasColumnName("SectionParcours_BaseSectionMapId"); + }); + + b.HasDiscriminator().HasValue("Parcours"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionPdf", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property>("PDFOrderedTranslationAndResources") + .IsRequired() + .HasColumnType("jsonb"); + + b.HasDiscriminator().HasValue("PDF"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionQuiz", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property>("QuizBadLevel") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property>("QuizGoodLevel") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property>("QuizGreatLevel") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property>("QuizMediumLevel") + .IsRequired() + .HasColumnType("jsonb"); + + b.HasDiscriminator().HasValue("Quiz"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionSlider", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property>("SliderContents") + .IsRequired() + .HasColumnType("jsonb"); + + b.HasDiscriminator().HasValue("Slider"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionVideo", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property("VideoSource") + .IsRequired() + .HasColumnType("text"); + + b.HasDiscriminator().HasValue("Video"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionWeather", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property("WeatherCity") + .HasColumnType("text"); + + b.Property("WeatherResult") + .HasColumnType("text"); + + b.Property("WeatherUpdatedDate") + .HasColumnType("timestamp with time zone"); + + b.HasDiscriminator().HasValue("Weather"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionWeb", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property("WebSource") + .IsRequired() + .HasColumnType("text"); + + b.HasDiscriminator().HasValue("Web"); + }); + + modelBuilder.Entity("ManagerService.Data.ApiKey", b => + { + b.HasOne("ManagerService.Data.Instance", "Instance") + .WithMany() + .HasForeignKey("InstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Instance"); + }); + + modelBuilder.Entity("ManagerService.Data.AppConfigurationLink", b => + { + b.HasOne("ManagerService.Data.ApplicationInstance", "ApplicationInstance") + .WithMany("Configurations") + .HasForeignKey("ApplicationInstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ManagerService.Data.Configuration", "Configuration") + .WithMany() + .HasForeignKey("ConfigurationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ManagerService.Data.Device", "Device") + .WithMany() + .HasForeignKey("DeviceId"); + + b.Navigation("ApplicationInstance"); + + b.Navigation("Configuration"); + + b.Navigation("Device"); + }); + + modelBuilder.Entity("ManagerService.Data.ApplicationInstance", b => + { + b.HasOne("ManagerService.Data.SubSection.SectionEvent", "SectionEvent") + .WithMany() + .HasForeignKey("SectionEventId"); + + b.Navigation("SectionEvent"); + }); + + modelBuilder.Entity("ManagerService.Data.Device", b => + { + b.HasOne("ManagerService.Data.Configuration", "Configuration") + .WithMany() + .HasForeignKey("ConfigurationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Configuration"); + }); + + modelBuilder.Entity("ManagerService.Data.Instance", b => + { + b.HasOne("ManagerService.Data.SubscriptionPlan", "SubscriptionPlan") + .WithMany() + .HasForeignKey("SubscriptionPlanId"); + + b.Navigation("SubscriptionPlan"); + }); + + modelBuilder.Entity("ManagerService.Data.Section", b => + { + b.HasOne("ManagerService.Data.SubSection.SectionMenu", null) + .WithMany("MenuSections") + .HasForeignKey("SectionMenuId"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.EventAgenda", b => + { + b.HasOne("ManagerService.Data.Resource", "Resource") + .WithMany() + .HasForeignKey("ResourceId"); + + b.HasOne("ManagerService.Data.SubSection.SectionAgenda", "SectionAgenda") + .WithMany("EventAgendas") + .HasForeignKey("SectionAgendaId"); + + b.HasOne("ManagerService.Data.SubSection.SectionEvent", "SectionEvent") + .WithMany() + .HasForeignKey("SectionEventId"); + + b.HasOne("ManagerService.Data.Resource", "VideoResource") + .WithMany() + .HasForeignKey("VideoResourceId"); + + b.Navigation("Resource"); + + b.Navigation("SectionAgenda"); + + b.Navigation("SectionEvent"); + + b.Navigation("VideoResource"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.GeoPoint", b => + { + b.HasOne("ManagerService.Data.SubSection.SectionEvent", "SectionEvent") + .WithMany() + .HasForeignKey("SectionEventId"); + + b.HasOne("ManagerService.Data.SubSection.SectionMap", "SectionMap") + .WithMany("MapPoints") + .HasForeignKey("SectionMapId"); + + b.Navigation("SectionEvent"); + + b.Navigation("SectionMap"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.GuidedPath", b => + { + b.HasOne("ManagerService.Data.Resource", "ImageResource") + .WithMany() + .HasForeignKey("ImageResourceId"); + + b.HasOne("ManagerService.Data.SubSection.SectionEvent", "SectionEvent") + .WithMany() + .HasForeignKey("SectionEventId"); + + b.HasOne("ManagerService.Data.SubSection.SectionMap", "SectionMap") + .WithMany() + .HasForeignKey("SectionMapId"); + + b.HasOne("ManagerService.Data.SubSection.SectionParcours", "SectionParcours") + .WithMany("GuidedPaths") + .HasForeignKey("SectionParcoursId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("ImageResource"); + + b.Navigation("SectionEvent"); + + b.Navigation("SectionMap"); + + b.Navigation("SectionParcours"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.GuidedStep", b => + { + b.HasOne("ManagerService.Data.SubSection.GuidedPath", "GuidedPath") + .WithMany("Steps") + .HasForeignKey("GuidedPathId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("GuidedPath"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.QuizQuestion", b => + { + b.HasOne("ManagerService.Data.SubSection.GuidedStep", "GuidedStep") + .WithMany("QuizQuestions") + .HasForeignKey("GuidedStepId"); + + b.HasOne("ManagerService.Data.Resource", "PuzzleImage") + .WithMany() + .HasForeignKey("PuzzleImageId"); + + b.HasOne("ManagerService.Data.Resource", "Resource") + .WithMany() + .HasForeignKey("ResourceId"); + + b.HasOne("ManagerService.Data.SubSection.SectionQuiz", "SectionQuiz") + .WithMany("QuizQuestions") + .HasForeignKey("SectionQuizId"); + + b.Navigation("GuidedStep"); + + b.Navigation("PuzzleImage"); + + b.Navigation("Resource"); + + b.Navigation("SectionQuiz"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionEvent+MapAnnotation", b => + { + b.HasOne("ManagerService.Data.Resource", "IconResource") + .WithMany() + .HasForeignKey("IconResourceId"); + + b.HasOne("ManagerService.Data.SubSection.SectionEvent+ProgrammeBlock", null) + .WithMany("MapAnnotations") + .HasForeignKey("ProgrammeBlockId"); + + b.HasOne("ManagerService.Data.SubSection.SectionEvent", null) + .WithMany("GlobalMapAnnotations") + .HasForeignKey("SectionEventId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("IconResource"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionEvent+ProgrammeBlock", b => + { + b.HasOne("ManagerService.Data.SubSection.SectionEvent", null) + .WithMany("Programme") + .HasForeignKey("SectionEventId"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionEvent", b => + { + b.HasOne("ManagerService.Data.SubSection.SectionMap", "BaseMap") + .WithMany() + .HasForeignKey("BaseSectionMapId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("BaseMap"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionGame", b => + { + b.HasOne("ManagerService.Data.Resource", "GamePuzzleImage") + .WithMany() + .HasForeignKey("GamePuzzleImageId"); + + b.Navigation("GamePuzzleImage"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionMap", b => + { + b.HasOne("ManagerService.Data.Resource", "MapResource") + .WithMany() + .HasForeignKey("MapResourceId"); + + b.Navigation("MapResource"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionParcours", b => + { + b.HasOne("ManagerService.Data.SubSection.SectionMap", "BaseMap") + .WithMany() + .HasForeignKey("BaseSectionMapId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("BaseMap"); + }); + + modelBuilder.Entity("ManagerService.Data.ApplicationInstance", b => + { + b.Navigation("Configurations"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.GuidedPath", b => + { + b.Navigation("Steps"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.GuidedStep", b => + { + b.Navigation("QuizQuestions"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionEvent+ProgrammeBlock", b => + { + b.Navigation("MapAnnotations"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionAgenda", b => + { + b.Navigation("EventAgendas"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionEvent", b => + { + b.Navigation("GlobalMapAnnotations"); + + b.Navigation("Programme"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionMap", b => + { + b.Navigation("MapPoints"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionMenu", b => + { + b.Navigation("MenuSections"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionParcours", b => + { + b.Navigation("GuidedPaths"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionQuiz", b => + { + b.Navigation("QuizQuestions"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ManagerService/Migrations/20260708123715_AddGuidedStepIsGeoTriggered.cs b/ManagerService/Migrations/20260708123715_AddGuidedStepIsGeoTriggered.cs new file mode 100644 index 0000000..2dea368 --- /dev/null +++ b/ManagerService/Migrations/20260708123715_AddGuidedStepIsGeoTriggered.cs @@ -0,0 +1,29 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace ManagerService.Migrations +{ + /// + public partial class AddGuidedStepIsGeoTriggered : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "IsGeoTriggered", + table: "GuidedSteps", + type: "boolean", + nullable: false, + defaultValue: false); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "IsGeoTriggered", + table: "GuidedSteps"); + } + } +} diff --git a/ManagerService/Migrations/20260708143339_RemoveGuidedPathSectionMapRelation.Designer.cs b/ManagerService/Migrations/20260708143339_RemoveGuidedPathSectionMapRelation.Designer.cs new file mode 100644 index 0000000..f77fe86 --- /dev/null +++ b/ManagerService/Migrations/20260708143339_RemoveGuidedPathSectionMapRelation.Designer.cs @@ -0,0 +1,1634 @@ +// +using System; +using System.Collections.Generic; +using Manager.DTOs; +using ManagerService.DTOs; +using ManagerService.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using NetTopologySuite.Geometries; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace ManagerService.Migrations +{ + [DbContext(typeof(MyInfoMateDbContext))] + [Migration("20260708143339_RemoveGuidedPathSectionMapRelation")] + partial class RemoveGuidedPathSectionMapRelation + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.2") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "postgis"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("ManagerService.Data.ApiKey", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AppType") + .HasColumnType("integer"); + + b.Property("DateCreation") + .HasColumnType("timestamp with time zone"); + + b.Property("DateExpiration") + .HasColumnType("timestamp with time zone"); + + b.Property("InstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("KeyHash") + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId"); + + b.ToTable("ApiKeys"); + }); + + modelBuilder.Entity("ManagerService.Data.AppConfigurationLink", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("ApplicationInstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("ConfigurationId") + .IsRequired() + .HasColumnType("text"); + + b.Property("DeviceId") + .HasColumnType("text"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsDate") + .HasColumnType("boolean"); + + b.Property("IsHour") + .HasColumnType("boolean"); + + b.Property("IsSectionImageBackground") + .HasColumnType("boolean"); + + b.Property("LayoutMainPage") + .HasColumnType("integer"); + + b.Property("LoaderImageId") + .HasColumnType("text"); + + b.Property("LoaderImageUrl") + .HasColumnType("text"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("PrimaryColor") + .HasColumnType("text"); + + b.Property("RoundedValue") + .HasColumnType("integer"); + + b.Property("ScreenPercentageSectionsMainPage") + .HasColumnType("integer"); + + b.Property("SecondaryColor") + .HasColumnType("text"); + + b.Property("WeightMasonryGrid") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationInstanceId"); + + b.HasIndex("ConfigurationId"); + + b.HasIndex("DeviceId"); + + b.ToTable("AppConfigurationLinks"); + }); + + modelBuilder.Entity("ManagerService.Data.ApplicationInstance", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AppType") + .HasColumnType("integer"); + + b.Property("InstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsAssistant") + .HasColumnType("boolean"); + + b.PrimitiveCollection>("Languages") + .HasColumnType("text[]"); + + b.Property("LayoutMainPage") + .HasColumnType("integer"); + + b.Property("LoaderImageId") + .HasColumnType("text"); + + b.Property("LoaderImageUrl") + .HasColumnType("text"); + + b.Property("MainImageId") + .HasColumnType("text"); + + b.Property("MainImageUrl") + .HasColumnType("text"); + + b.Property("PrimaryColor") + .HasColumnType("text"); + + b.Property("SecondaryColor") + .HasColumnType("text"); + + b.Property("SectionEventId") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("SectionEventId"); + + b.ToTable("ApplicationInstances"); + }); + + modelBuilder.Entity("ManagerService.Data.AuditLog", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("Action") + .HasColumnType("text"); + + b.Property("EntityId") + .HasColumnType("text"); + + b.Property("EntityType") + .HasColumnType("text"); + + b.Property("InstanceId") + .HasColumnType("text"); + + b.Property("NewValues") + .HasColumnType("text"); + + b.Property("OldValues") + .HasColumnType("text"); + + b.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("AuditLogs"); + }); + + modelBuilder.Entity("ManagerService.Data.Configuration", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("DateCreation") + .HasColumnType("timestamp with time zone"); + + b.Property("ImageId") + .HasColumnType("text"); + + b.Property("ImageSource") + .HasColumnType("text"); + + b.Property("InstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsOffline") + .HasColumnType("boolean"); + + b.Property("IsQRCode") + .HasColumnType("boolean"); + + b.Property("IsSearchNumber") + .HasColumnType("boolean"); + + b.Property("IsSearchText") + .HasColumnType("boolean"); + + b.Property("Label") + .IsRequired() + .HasColumnType("text"); + + b.PrimitiveCollection>("Languages") + .HasColumnType("text[]"); + + b.Property("LoaderImageId") + .HasColumnType("text"); + + b.Property("LoaderImageUrl") + .HasColumnType("text"); + + b.Property("PrimaryColor") + .HasColumnType("text"); + + b.Property("SecondaryColor") + .HasColumnType("text"); + + b.Property("Title") + .IsRequired() + .HasColumnType("jsonb"); + + b.HasKey("Id"); + + b.ToTable("Configurations"); + }); + + modelBuilder.Entity("ManagerService.Data.Device", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AppVersion") + .HasColumnType("text"); + + b.Property("BatteryLevel") + .HasColumnType("text"); + + b.Property("ConfigurationId") + .IsRequired() + .HasColumnType("text"); + + b.Property("Connected") + .HasColumnType("boolean"); + + b.Property("ConnectionLevel") + .HasColumnType("text"); + + b.Property("DateCreation") + .HasColumnType("timestamp with time zone"); + + b.Property("DateUpdate") + .HasColumnType("timestamp with time zone"); + + b.Property("Identifier") + .HasColumnType("text"); + + b.Property("InstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("IpAddressETH") + .HasColumnType("text"); + + b.Property("IpAddressWLAN") + .HasColumnType("text"); + + b.Property("LastBatteryLevel") + .HasColumnType("timestamp with time zone"); + + b.Property("LastConnectionLevel") + .HasColumnType("timestamp with time zone"); + + b.Property("LastSeen") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ConfigurationId"); + + b.ToTable("Devices"); + }); + + modelBuilder.Entity("ManagerService.Data.Instance", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AiRequestsPerMonth") + .HasColumnType("integer"); + + b.Property("AiRequestsThisMonth") + .HasColumnType("integer"); + + b.Property("AiUsageMonthKey") + .HasColumnType("text"); + + b.Property("DateCreation") + .HasColumnType("timestamp with time zone"); + + b.Property("HasAdvancedStats") + .HasColumnType("boolean"); + + b.Property("HasStats") + .HasColumnType("boolean"); + + b.Property("IsAssistant") + .HasColumnType("boolean"); + + b.Property("IsMobile") + .HasColumnType("boolean"); + + b.Property("IsPushNotification") + .HasColumnType("boolean"); + + b.Property("IsTablet") + .HasColumnType("boolean"); + + b.Property("IsVR") + .HasColumnType("boolean"); + + b.Property("IsWeb") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("PinCode") + .HasColumnType("text"); + + b.Property("PublicApiKey") + .HasColumnType("text"); + + b.Property("StatsHistoryDays") + .HasColumnType("integer"); + + b.Property("StorageQuotaBytes") + .HasColumnType("bigint"); + + b.Property("SubscriptionPlanId") + .HasColumnType("text"); + + b.Property("WebSlug") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("SubscriptionPlanId"); + + b.ToTable("Instances"); + }); + + modelBuilder.Entity("ManagerService.Data.PushNotification", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("Body") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreation") + .HasColumnType("timestamp with time zone"); + + b.Property("HangfireJobId") + .HasColumnType("text"); + + b.Property("InstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("ScheduledAt") + .HasColumnType("timestamp with time zone"); + + b.Property("SentAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("Topic") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId"); + + b.ToTable("PushNotifications"); + }); + + modelBuilder.Entity("ManagerService.Data.Resource", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("DateCreation") + .HasColumnType("timestamp with time zone"); + + b.Property("InstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("Label") + .IsRequired() + .HasColumnType("text"); + + b.Property("SizeBytes") + .HasColumnType("bigint"); + + b.Property("Type") + .HasColumnType("integer"); + + b.Property("Url") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Resources"); + }); + + modelBuilder.Entity("ManagerService.Data.Section", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("BeaconId") + .HasColumnType("integer"); + + b.Property("ConfigurationId") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreation") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .HasColumnType("jsonb"); + + b.Property("Discriminator") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ImageId") + .HasColumnType("text"); + + b.Property("ImageSource") + .HasColumnType("text"); + + b.Property("InstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsBeacon") + .HasColumnType("boolean"); + + b.Property("IsSubSection") + .HasColumnType("boolean"); + + b.Property("Label") + .IsRequired() + .HasColumnType("text"); + + b.Property("Latitude") + .HasColumnType("text"); + + b.Property("Longitude") + .HasColumnType("text"); + + b.Property("MeterZoneGPS") + .HasColumnType("integer"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("ParentId") + .HasColumnType("text"); + + b.Property("SectionMenuId") + .HasColumnType("text"); + + b.Property("Title") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Type") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("SectionMenuId"); + + b.ToTable("Sections"); + + b.HasDiscriminator().HasValue("Base"); + + b.UseTphMappingStrategy(); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.EventAgenda", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Address") + .HasColumnType("jsonb"); + + b.Property("DateAdded") + .HasColumnType("timestamp with time zone"); + + b.Property("DateFrom") + .HasColumnType("timestamp with time zone"); + + b.Property("DateTo") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Email") + .HasColumnType("text"); + + b.Property("IdVideoYoutube") + .HasColumnType("text"); + + b.Property("IsSynced") + .HasColumnType("boolean"); + + b.Property("Label") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Phone") + .HasColumnType("text"); + + b.Property("ResourceId") + .HasColumnType("text"); + + b.Property("SectionAgendaId") + .HasColumnType("text"); + + b.Property("SectionEventId") + .HasColumnType("text"); + + b.Property("SyncedImageUrl") + .HasColumnType("text"); + + b.Property("Type") + .HasColumnType("text"); + + b.Property("VideoLink") + .HasColumnType("text"); + + b.Property("VideoResourceId") + .HasColumnType("text"); + + b.Property("Website") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ResourceId"); + + b.HasIndex("SectionAgendaId"); + + b.HasIndex("SectionEventId"); + + b.HasIndex("VideoResourceId"); + + b.ToTable("EventAgendas"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.GeoPoint", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CategorieId") + .HasColumnType("integer"); + + b.Property("Contents") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Description") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Email") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Geometry") + .HasColumnType("geometry"); + + b.Property("ImageResourceId") + .HasColumnType("text"); + + b.Property("ImageUrl") + .HasColumnType("text"); + + b.Property("Phone") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("PolyColor") + .HasColumnType("text"); + + b.Property("Prices") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Schedules") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("SectionEventId") + .HasColumnType("text"); + + b.Property("SectionMapId") + .HasColumnType("text"); + + b.Property("Site") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Title") + .IsRequired() + .HasColumnType("jsonb"); + + b.HasKey("Id"); + + b.HasIndex("SectionEventId"); + + b.HasIndex("SectionMapId"); + + b.ToTable("GeoPoints"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.GuidedPath", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("Description") + .HasColumnType("jsonb"); + + b.Property("EstimatedDurationMinutes") + .HasColumnType("integer"); + + b.Property("GameMessageDebut") + .HasColumnType("jsonb"); + + b.Property("GameMessageFin") + .HasColumnType("jsonb"); + + b.Property("HideNextStepsUntilComplete") + .HasColumnType("boolean"); + + b.Property("ImageResourceId") + .HasColumnType("text"); + + b.Property("InstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsGameMode") + .HasColumnType("boolean"); + + b.Property("IsLinear") + .HasColumnType("boolean"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("RequireSuccessToAdvance") + .HasColumnType("boolean"); + + b.Property("SectionEventId") + .HasColumnType("text"); + + b.Property("SectionParcoursId") + .HasColumnType("text"); + + b.Property("Title") + .IsRequired() + .HasColumnType("jsonb"); + + b.HasKey("Id"); + + b.HasIndex("ImageResourceId"); + + b.HasIndex("SectionEventId"); + + b.HasIndex("SectionParcoursId"); + + b.ToTable("GuidedPaths"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.GuidedStep", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AudioIds") + .HasColumnType("jsonb"); + + b.Property("Contents") + .HasColumnType("jsonb"); + + b.Property("Description") + .HasColumnType("jsonb"); + + b.Property("FactContent") + .HasColumnType("jsonb"); + + b.Property("Geometry") + .HasColumnType("geometry"); + + b.Property("GuidedPathId") + .IsRequired() + .HasColumnType("text"); + + b.Property("ImageUrl") + .HasColumnType("text"); + + b.Property("IsGeoTriggered") + .HasColumnType("boolean"); + + b.Property("IsHiddenInitially") + .HasColumnType("boolean"); + + b.Property("IsStepLocked") + .HasColumnType("boolean"); + + b.Property("IsStepTimer") + .HasColumnType("boolean"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("TimerExpiredMessage") + .HasColumnType("jsonb"); + + b.Property("TimerSeconds") + .HasColumnType("integer"); + + b.Property("Title") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("ZoneRadiusMeters") + .HasColumnType("double precision"); + + b.HasKey("Id"); + + b.HasIndex("GuidedPathId"); + + b.ToTable("GuidedSteps"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.QuizQuestion", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("GuidedStepId") + .HasColumnType("text"); + + b.Property("IsSlidingPuzzle") + .HasColumnType("boolean"); + + b.Property>("Label") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("PuzzleCols") + .HasColumnType("integer"); + + b.Property("PuzzleImageId") + .HasColumnType("text"); + + b.Property("PuzzleRows") + .HasColumnType("integer"); + + b.Property("ResourceId") + .HasColumnType("text"); + + b.Property>("Responses") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("SectionQuizId") + .HasColumnType("text"); + + b.Property("ValidationQuestionType") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("GuidedStepId"); + + b.HasIndex("PuzzleImageId"); + + b.HasIndex("ResourceId"); + + b.HasIndex("SectionQuizId"); + + b.ToTable("QuizQuestions"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionEvent+MapAnnotation", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("Geometry") + .HasColumnType("geometry"); + + b.Property("GeometryType") + .HasColumnType("integer"); + + b.Property("Icon") + .HasColumnType("text"); + + b.Property("IconResourceId") + .HasColumnType("text"); + + b.Property>("Label") + .HasColumnType("jsonb"); + + b.Property("PolyColor") + .HasColumnType("text"); + + b.Property("ProgrammeBlockId") + .HasColumnType("text"); + + b.Property("SectionEventId") + .HasColumnType("text"); + + b.Property>("Type") + .HasColumnType("jsonb"); + + b.HasKey("Id"); + + b.HasIndex("IconResourceId"); + + b.HasIndex("ProgrammeBlockId"); + + b.HasIndex("SectionEventId"); + + b.ToTable("MapAnnotations"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionEvent+ProgrammeBlock", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property>("Description") + .HasColumnType("jsonb"); + + b.Property("EndTime") + .HasColumnType("timestamp with time zone"); + + b.Property("SectionEventId") + .HasColumnType("text"); + + b.Property("StartTime") + .HasColumnType("timestamp with time zone"); + + b.Property>("Title") + .HasColumnType("jsonb"); + + b.HasKey("Id"); + + b.HasIndex("SectionEventId"); + + b.ToTable("ProgrammeBlocks"); + }); + + modelBuilder.Entity("ManagerService.Data.SubscriptionPlan", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AiRequestsPerMonth") + .HasColumnType("integer"); + + b.Property("HasAdvancedStats") + .HasColumnType("boolean"); + + b.Property("HasStats") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("StatsHistoryDays") + .HasColumnType("integer"); + + b.Property("StorageQuotaBytes") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.ToTable("SubscriptionPlans"); + + b.HasData( + new + { + Id = "plan-starter", + AiRequestsPerMonth = 0, + HasAdvancedStats = false, + HasStats = false, + Name = "Starter", + StatsHistoryDays = 30, + StorageQuotaBytes = 1073741824L + }, + new + { + Id = "plan-standard", + AiRequestsPerMonth = 500, + HasAdvancedStats = false, + HasStats = true, + Name = "Standard", + StatsHistoryDays = 30, + StorageQuotaBytes = 10737418240L + }, + new + { + Id = "plan-premium", + AiRequestsPerMonth = 2000, + HasAdvancedStats = true, + HasStats = true, + Name = "Premium", + StatsHistoryDays = 0, + StorageQuotaBytes = 53687091200L + }); + }); + + modelBuilder.Entity("ManagerService.Data.User", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("DateCreation") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .IsRequired() + .HasColumnType("text"); + + b.Property("FirstName") + .HasColumnType("text"); + + b.Property("InstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("text"); + + b.Property("Password") + .IsRequired() + .HasColumnType("text"); + + b.Property("Role") + .HasColumnType("integer"); + + b.Property("Token") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("ManagerService.Data.VisitEvent", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AppType") + .HasColumnType("integer"); + + b.Property("ConfigurationId") + .HasColumnType("text"); + + b.Property("DurationSeconds") + .HasColumnType("integer"); + + b.Property("EventType") + .HasColumnType("integer"); + + b.Property("InstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("Language") + .HasColumnType("text"); + + b.Property("Metadata") + .HasColumnType("text"); + + b.Property("SectionId") + .HasColumnType("text"); + + b.Property("SessionId") + .IsRequired() + .HasColumnType("text"); + + b.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId"); + + b.HasIndex("Timestamp"); + + b.ToTable("VisitEvents"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionAgenda", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property("AgendaMapProvider") + .HasColumnType("integer"); + + b.Property>("AgendaResourceIds") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("IsOnlineAgenda") + .HasColumnType("boolean"); + + b.HasDiscriminator().HasValue("Agenda"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionArticle", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property>("ArticleAudioIds") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property>("ArticleContent") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property>("ArticleContents") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("ArticleIsContentTop") + .HasColumnType("boolean"); + + b.Property("ArticleIsReadAudioAuto") + .HasColumnType("boolean"); + + b.HasDiscriminator().HasValue("Article"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionEvent", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property("BaseSectionMapId") + .HasColumnType("text"); + + b.Property("EndDate") + .HasColumnType("timestamp with time zone"); + + b.Property>("ParcoursIds") + .HasColumnType("jsonb"); + + b.Property("StartDate") + .HasColumnType("timestamp with time zone"); + + b.HasIndex("BaseSectionMapId"); + + b.HasDiscriminator().HasValue("Event"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionGame", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property>("GameMessageDebut") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property>("GameMessageFin") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("GamePuzzleCols") + .HasColumnType("integer"); + + b.Property("GamePuzzleImageId") + .HasColumnType("text"); + + b.Property("GamePuzzleRows") + .HasColumnType("integer"); + + b.Property("GameType") + .HasColumnType("integer"); + + b.HasIndex("GamePuzzleImageId"); + + b.HasDiscriminator().HasValue("Game"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionMap", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property("IsListViewEnabled") + .HasColumnType("boolean"); + + b.Property>("MapCategories") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("MapCenterLatitude") + .HasColumnType("text"); + + b.Property("MapCenterLongitude") + .HasColumnType("text"); + + b.Property("MapMapProvider") + .HasColumnType("integer"); + + b.Property("MapMapType") + .HasColumnType("integer"); + + b.Property("MapResourceId") + .HasColumnType("text"); + + b.Property("MapTypeMapbox") + .HasColumnType("integer"); + + b.Property("MapZoom") + .HasColumnType("integer"); + + b.HasIndex("MapResourceId"); + + b.HasDiscriminator().HasValue("Map"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionMenu", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.HasDiscriminator().HasValue("Menu"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionParcours", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property("BaseSectionMapId") + .HasColumnType("text"); + + b.Property("ShowMap") + .HasColumnType("boolean"); + + b.HasIndex("BaseSectionMapId"); + + b.ToTable("Sections", t => + { + t.Property("BaseSectionMapId") + .HasColumnName("SectionParcours_BaseSectionMapId"); + }); + + b.HasDiscriminator().HasValue("Parcours"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionPdf", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property>("PDFOrderedTranslationAndResources") + .IsRequired() + .HasColumnType("jsonb"); + + b.HasDiscriminator().HasValue("PDF"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionQuiz", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property>("QuizBadLevel") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property>("QuizGoodLevel") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property>("QuizGreatLevel") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property>("QuizMediumLevel") + .IsRequired() + .HasColumnType("jsonb"); + + b.HasDiscriminator().HasValue("Quiz"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionSlider", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property>("SliderContents") + .IsRequired() + .HasColumnType("jsonb"); + + b.HasDiscriminator().HasValue("Slider"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionVideo", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property("VideoSource") + .IsRequired() + .HasColumnType("text"); + + b.HasDiscriminator().HasValue("Video"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionWeather", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property("WeatherCity") + .HasColumnType("text"); + + b.Property("WeatherResult") + .HasColumnType("text"); + + b.Property("WeatherUpdatedDate") + .HasColumnType("timestamp with time zone"); + + b.HasDiscriminator().HasValue("Weather"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionWeb", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property("WebSource") + .IsRequired() + .HasColumnType("text"); + + b.HasDiscriminator().HasValue("Web"); + }); + + modelBuilder.Entity("ManagerService.Data.ApiKey", b => + { + b.HasOne("ManagerService.Data.Instance", "Instance") + .WithMany() + .HasForeignKey("InstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Instance"); + }); + + modelBuilder.Entity("ManagerService.Data.AppConfigurationLink", b => + { + b.HasOne("ManagerService.Data.ApplicationInstance", "ApplicationInstance") + .WithMany("Configurations") + .HasForeignKey("ApplicationInstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ManagerService.Data.Configuration", "Configuration") + .WithMany() + .HasForeignKey("ConfigurationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ManagerService.Data.Device", "Device") + .WithMany() + .HasForeignKey("DeviceId"); + + b.Navigation("ApplicationInstance"); + + b.Navigation("Configuration"); + + b.Navigation("Device"); + }); + + modelBuilder.Entity("ManagerService.Data.ApplicationInstance", b => + { + b.HasOne("ManagerService.Data.SubSection.SectionEvent", "SectionEvent") + .WithMany() + .HasForeignKey("SectionEventId"); + + b.Navigation("SectionEvent"); + }); + + modelBuilder.Entity("ManagerService.Data.Device", b => + { + b.HasOne("ManagerService.Data.Configuration", "Configuration") + .WithMany() + .HasForeignKey("ConfigurationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Configuration"); + }); + + modelBuilder.Entity("ManagerService.Data.Instance", b => + { + b.HasOne("ManagerService.Data.SubscriptionPlan", "SubscriptionPlan") + .WithMany() + .HasForeignKey("SubscriptionPlanId"); + + b.Navigation("SubscriptionPlan"); + }); + + modelBuilder.Entity("ManagerService.Data.Section", b => + { + b.HasOne("ManagerService.Data.SubSection.SectionMenu", null) + .WithMany("MenuSections") + .HasForeignKey("SectionMenuId"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.EventAgenda", b => + { + b.HasOne("ManagerService.Data.Resource", "Resource") + .WithMany() + .HasForeignKey("ResourceId"); + + b.HasOne("ManagerService.Data.SubSection.SectionAgenda", "SectionAgenda") + .WithMany("EventAgendas") + .HasForeignKey("SectionAgendaId"); + + b.HasOne("ManagerService.Data.SubSection.SectionEvent", "SectionEvent") + .WithMany() + .HasForeignKey("SectionEventId"); + + b.HasOne("ManagerService.Data.Resource", "VideoResource") + .WithMany() + .HasForeignKey("VideoResourceId"); + + b.Navigation("Resource"); + + b.Navigation("SectionAgenda"); + + b.Navigation("SectionEvent"); + + b.Navigation("VideoResource"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.GeoPoint", b => + { + b.HasOne("ManagerService.Data.SubSection.SectionEvent", "SectionEvent") + .WithMany() + .HasForeignKey("SectionEventId"); + + b.HasOne("ManagerService.Data.SubSection.SectionMap", "SectionMap") + .WithMany("MapPoints") + .HasForeignKey("SectionMapId"); + + b.Navigation("SectionEvent"); + + b.Navigation("SectionMap"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.GuidedPath", b => + { + b.HasOne("ManagerService.Data.Resource", "ImageResource") + .WithMany() + .HasForeignKey("ImageResourceId"); + + b.HasOne("ManagerService.Data.SubSection.SectionEvent", "SectionEvent") + .WithMany() + .HasForeignKey("SectionEventId"); + + b.HasOne("ManagerService.Data.SubSection.SectionParcours", "SectionParcours") + .WithMany("GuidedPaths") + .HasForeignKey("SectionParcoursId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("ImageResource"); + + b.Navigation("SectionEvent"); + + b.Navigation("SectionParcours"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.GuidedStep", b => + { + b.HasOne("ManagerService.Data.SubSection.GuidedPath", "GuidedPath") + .WithMany("Steps") + .HasForeignKey("GuidedPathId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("GuidedPath"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.QuizQuestion", b => + { + b.HasOne("ManagerService.Data.SubSection.GuidedStep", "GuidedStep") + .WithMany("QuizQuestions") + .HasForeignKey("GuidedStepId"); + + b.HasOne("ManagerService.Data.Resource", "PuzzleImage") + .WithMany() + .HasForeignKey("PuzzleImageId"); + + b.HasOne("ManagerService.Data.Resource", "Resource") + .WithMany() + .HasForeignKey("ResourceId"); + + b.HasOne("ManagerService.Data.SubSection.SectionQuiz", "SectionQuiz") + .WithMany("QuizQuestions") + .HasForeignKey("SectionQuizId"); + + b.Navigation("GuidedStep"); + + b.Navigation("PuzzleImage"); + + b.Navigation("Resource"); + + b.Navigation("SectionQuiz"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionEvent+MapAnnotation", b => + { + b.HasOne("ManagerService.Data.Resource", "IconResource") + .WithMany() + .HasForeignKey("IconResourceId"); + + b.HasOne("ManagerService.Data.SubSection.SectionEvent+ProgrammeBlock", null) + .WithMany("MapAnnotations") + .HasForeignKey("ProgrammeBlockId"); + + b.HasOne("ManagerService.Data.SubSection.SectionEvent", null) + .WithMany("GlobalMapAnnotations") + .HasForeignKey("SectionEventId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("IconResource"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionEvent+ProgrammeBlock", b => + { + b.HasOne("ManagerService.Data.SubSection.SectionEvent", null) + .WithMany("Programme") + .HasForeignKey("SectionEventId"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionEvent", b => + { + b.HasOne("ManagerService.Data.SubSection.SectionMap", "BaseMap") + .WithMany() + .HasForeignKey("BaseSectionMapId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("BaseMap"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionGame", b => + { + b.HasOne("ManagerService.Data.Resource", "GamePuzzleImage") + .WithMany() + .HasForeignKey("GamePuzzleImageId"); + + b.Navigation("GamePuzzleImage"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionMap", b => + { + b.HasOne("ManagerService.Data.Resource", "MapResource") + .WithMany() + .HasForeignKey("MapResourceId"); + + b.Navigation("MapResource"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionParcours", b => + { + b.HasOne("ManagerService.Data.SubSection.SectionMap", "BaseMap") + .WithMany() + .HasForeignKey("BaseSectionMapId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("BaseMap"); + }); + + modelBuilder.Entity("ManagerService.Data.ApplicationInstance", b => + { + b.Navigation("Configurations"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.GuidedPath", b => + { + b.Navigation("Steps"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.GuidedStep", b => + { + b.Navigation("QuizQuestions"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionEvent+ProgrammeBlock", b => + { + b.Navigation("MapAnnotations"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionAgenda", b => + { + b.Navigation("EventAgendas"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionEvent", b => + { + b.Navigation("GlobalMapAnnotations"); + + b.Navigation("Programme"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionMap", b => + { + b.Navigation("MapPoints"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionMenu", b => + { + b.Navigation("MenuSections"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionParcours", b => + { + b.Navigation("GuidedPaths"); + }); + + modelBuilder.Entity("ManagerService.Data.SubSection.SectionQuiz", b => + { + b.Navigation("QuizQuestions"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ManagerService/Migrations/20260708143339_RemoveGuidedPathSectionMapRelation.cs b/ManagerService/Migrations/20260708143339_RemoveGuidedPathSectionMapRelation.cs new file mode 100644 index 0000000..77703ec --- /dev/null +++ b/ManagerService/Migrations/20260708143339_RemoveGuidedPathSectionMapRelation.cs @@ -0,0 +1,48 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace ManagerService.Migrations +{ + /// + public partial class RemoveGuidedPathSectionMapRelation : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_GuidedPaths_Sections_SectionMapId", + table: "GuidedPaths"); + + migrationBuilder.DropIndex( + name: "IX_GuidedPaths_SectionMapId", + table: "GuidedPaths"); + + migrationBuilder.DropColumn( + name: "SectionMapId", + table: "GuidedPaths"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "SectionMapId", + table: "GuidedPaths", + type: "text", + nullable: true); + + migrationBuilder.CreateIndex( + name: "IX_GuidedPaths_SectionMapId", + table: "GuidedPaths", + column: "SectionMapId"); + + migrationBuilder.AddForeignKey( + name: "FK_GuidedPaths_Sections_SectionMapId", + table: "GuidedPaths", + column: "SectionMapId", + principalTable: "Sections", + principalColumn: "Id"); + } + } +} diff --git a/ManagerService/Migrations/MyInfoMateDbContextModelSnapshot.cs b/ManagerService/Migrations/MyInfoMateDbContextModelSnapshot.cs index 2d68a69..c1cbb0d 100644 --- a/ManagerService/Migrations/MyInfoMateDbContextModelSnapshot.cs +++ b/ManagerService/Migrations/MyInfoMateDbContextModelSnapshot.cs @@ -711,13 +711,28 @@ namespace ManagerService.Migrations b.Property("Description") .HasColumnType("jsonb"); + b.Property("EstimatedDurationMinutes") + .HasColumnType("integer"); + + b.Property("GameMessageDebut") + .HasColumnType("jsonb"); + + b.Property("GameMessageFin") + .HasColumnType("jsonb"); + b.Property("HideNextStepsUntilComplete") .HasColumnType("boolean"); + b.Property("ImageResourceId") + .HasColumnType("text"); + b.Property("InstanceId") .IsRequired() .HasColumnType("text"); + b.Property("IsGameMode") + .HasColumnType("boolean"); + b.Property("IsLinear") .HasColumnType("boolean"); @@ -730,10 +745,7 @@ namespace ManagerService.Migrations b.Property("SectionEventId") .HasColumnType("text"); - b.Property("SectionGameId") - .HasColumnType("text"); - - b.Property("SectionMapId") + b.Property("SectionParcoursId") .HasColumnType("text"); b.Property("Title") @@ -742,11 +754,11 @@ namespace ManagerService.Migrations b.HasKey("Id"); + b.HasIndex("ImageResourceId"); + b.HasIndex("SectionEventId"); - b.HasIndex("SectionGameId"); - - b.HasIndex("SectionMapId"); + b.HasIndex("SectionParcoursId"); b.ToTable("GuidedPaths"); }); @@ -756,9 +768,18 @@ namespace ManagerService.Migrations b.Property("Id") .HasColumnType("text"); + b.Property("AudioIds") + .HasColumnType("jsonb"); + + b.Property("Contents") + .HasColumnType("jsonb"); + b.Property("Description") .HasColumnType("jsonb"); + b.Property("FactContent") + .HasColumnType("jsonb"); + b.Property("Geometry") .HasColumnType("geometry"); @@ -769,6 +790,9 @@ namespace ManagerService.Migrations b.Property("ImageUrl") .HasColumnType("text"); + b.Property("IsGeoTriggered") + .HasColumnType("boolean"); + b.Property("IsHiddenInitially") .HasColumnType("boolean"); @@ -791,9 +815,6 @@ namespace ManagerService.Migrations .IsRequired() .HasColumnType("jsonb"); - b.Property("TriggerGeoPointId") - .HasColumnType("integer"); - b.Property("ZoneRadiusMeters") .HasColumnType("double precision"); @@ -801,8 +822,6 @@ namespace ManagerService.Migrations b.HasIndex("GuidedPathId"); - b.HasIndex("TriggerGeoPointId"); - b.ToTable("GuidedSteps"); }); @@ -817,7 +836,7 @@ namespace ManagerService.Migrations b.Property("GuidedStepId") .HasColumnType("text"); - b.Property("IsSlidingPuzzle") + b.Property("IsSlidingPuzzle") .HasColumnType("boolean"); b.Property>("Label") @@ -1127,13 +1146,13 @@ namespace ManagerService.Migrations b.Property("BaseSectionMapId") .HasColumnType("text"); - b.Property("EndDate") + b.Property("EndDate") .HasColumnType("timestamp with time zone"); b.Property>("ParcoursIds") .HasColumnType("jsonb"); - b.Property("StartDate") + b.Property("StartDate") .HasColumnType("timestamp with time zone"); b.HasIndex("BaseSectionMapId"); @@ -1214,6 +1233,27 @@ namespace ManagerService.Migrations b.HasDiscriminator().HasValue("Menu"); }); + modelBuilder.Entity("ManagerService.Data.SubSection.SectionParcours", b => + { + b.HasBaseType("ManagerService.Data.Section"); + + b.Property("BaseSectionMapId") + .HasColumnType("text"); + + b.Property("ShowMap") + .HasColumnType("boolean"); + + b.HasIndex("BaseSectionMapId"); + + b.ToTable("Sections", t => + { + t.Property("BaseSectionMapId") + .HasColumnName("SectionParcours_BaseSectionMapId"); + }); + + b.HasDiscriminator().HasValue("Parcours"); + }); + modelBuilder.Entity("ManagerService.Data.SubSection.SectionPdf", b => { b.HasBaseType("ManagerService.Data.Section"); @@ -1413,23 +1453,24 @@ namespace ManagerService.Migrations modelBuilder.Entity("ManagerService.Data.SubSection.GuidedPath", b => { + b.HasOne("ManagerService.Data.Resource", "ImageResource") + .WithMany() + .HasForeignKey("ImageResourceId"); + b.HasOne("ManagerService.Data.SubSection.SectionEvent", "SectionEvent") .WithMany() .HasForeignKey("SectionEventId"); - b.HasOne("ManagerService.Data.SubSection.SectionGame", "SectionGame") - .WithMany() - .HasForeignKey("SectionGameId"); + b.HasOne("ManagerService.Data.SubSection.SectionParcours", "SectionParcours") + .WithMany("GuidedPaths") + .HasForeignKey("SectionParcoursId") + .OnDelete(DeleteBehavior.Cascade); - b.HasOne("ManagerService.Data.SubSection.SectionMap", "SectionMap") - .WithMany() - .HasForeignKey("SectionMapId"); + b.Navigation("ImageResource"); b.Navigation("SectionEvent"); - b.Navigation("SectionGame"); - - b.Navigation("SectionMap"); + b.Navigation("SectionParcours"); }); modelBuilder.Entity("ManagerService.Data.SubSection.GuidedStep", b => @@ -1440,13 +1481,7 @@ namespace ManagerService.Migrations .OnDelete(DeleteBehavior.Cascade) .IsRequired(); - b.HasOne("ManagerService.Data.SubSection.GeoPoint", "TriggerGeoPoint") - .WithMany() - .HasForeignKey("TriggerGeoPointId"); - b.Navigation("GuidedPath"); - - b.Navigation("TriggerGeoPoint"); }); modelBuilder.Entity("ManagerService.Data.SubSection.QuizQuestion", b => @@ -1529,6 +1564,16 @@ namespace ManagerService.Migrations b.Navigation("MapResource"); }); + modelBuilder.Entity("ManagerService.Data.SubSection.SectionParcours", b => + { + b.HasOne("ManagerService.Data.SubSection.SectionMap", "BaseMap") + .WithMany() + .HasForeignKey("BaseSectionMapId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("BaseMap"); + }); + modelBuilder.Entity("ManagerService.Data.ApplicationInstance", b => { b.Navigation("Configurations"); @@ -1571,6 +1616,11 @@ namespace ManagerService.Migrations b.Navigation("MenuSections"); }); + modelBuilder.Entity("ManagerService.Data.SubSection.SectionParcours", b => + { + b.Navigation("GuidedPaths"); + }); + modelBuilder.Entity("ManagerService.Data.SubSection.SectionQuiz", b => { b.Navigation("QuizQuestions"); diff --git a/ManagerService/Services/AssistantService.cs b/ManagerService/Services/AssistantService.cs index dd461f4..4be02bf 100644 --- a/ManagerService/Services/AssistantService.cs +++ b/ManagerService/Services/AssistantService.cs @@ -63,6 +63,70 @@ namespace ManagerService.Services private static string StripHtml(string? html) => string.IsNullOrEmpty(html) ? "" : System.Text.RegularExpressions.Regex.Replace(html, "<[^>]+>", "").Trim(); + private record DateWords(string Today, string Tomorrow, string This, string From, string To, string The, string NoDate); + + private static readonly System.Collections.Generic.Dictionary LangConfig = new(StringComparer.OrdinalIgnoreCase) + { + ["FR"] = ("fr-FR", new("aujourd'hui", "demain", "ce", "du", "au", "le", "date non précisée")), + ["NL"] = ("nl-BE", new("vandaag", "morgen", "deze", "van", "tot", "op", "datum niet bepaald")), + ["EN"] = ("en-GB", new("today", "tomorrow", "this", "from", "to", "on", "date unknown")), + ["DE"] = ("de-DE", new("heute", "morgen", "diesen", "vom", "bis", "am", "Datum unbekannt")), + ["IT"] = ("it-IT", new("oggi", "domani", "questo", "dal", "al", "il", "data non precisata")), + ["ES"] = ("es-ES", new("hoy", "mañana", "este", "del", "al", "el", "fecha no especificada")), + ["PL"] = ("pl-PL", new("dzisiaj", "jutro", "w ten", "od", "do", "w", "data nieznana")), + }; + + private static (System.Globalization.CultureInfo culture, DateWords words) GetLangConfig(string lang) + { + if (LangConfig.TryGetValue(lang ?? "FR", out var cfg)) + return (new System.Globalization.CultureInfo(cfg.culture), cfg.words); + return (new System.Globalization.CultureInfo("fr-FR"), LangConfig["FR"].words); + } + + private static (string text, bool expectsReply) StripMarkdownForVoice(string text) + { + var expectsReply = true; + if (text.Contains("[FIN]")) + { + expectsReply = false; + text = text.Replace("[FIN]", "").TrimEnd(); + } + text = Regex.Replace(text, @"(?m)^\s*[-*•]\s+", ""); + text = Regex.Replace(text, @"(?m)^\s*\d+\.\s+", ""); + text = Regex.Replace(text, @"\*{1,2}([^*]+)\*{1,2}", "$1"); + text = Regex.Replace(text, @"_{1,2}([^_]+)_{1,2}", "$1"); + text = Regex.Replace(text, @"(?m)^#{1,6}\s+", ""); + text = text.Replace("`", ""); + text = Regex.Replace(text, @"\n{3,}", "\n\n").Trim(); + return (text, expectsReply); + } + + private static string FormatDateVoice(DateTime date, DateTime today, string lang) + { + var (culture, w) = GetLangConfig(lang); + var diff = (date.Date - today.Date).Days; + var dayName = culture.DateTimeFormat.GetDayName(date.DayOfWeek).ToLower(); + var monthName = culture.DateTimeFormat.GetMonthName(date.Month).ToLower(); + var yearSuffix = date.Year != today.Year ? $" {date.Year}" : ""; + var absolute = $"{dayName} {date.Day} {monthName}{yearSuffix}"; + + return diff switch + { + 0 => $"{w.Today} {date.Day} {monthName}{yearSuffix}", + 1 => $"{w.Tomorrow} {dayName} {date.Day} {monthName}{yearSuffix}", + >= 2 and <= 7 => $"{w.This} {absolute}", + _ => absolute, + }; + } + + private static string FormatDateRangeVoice(DateTime? from, DateTime? to, DateTime today, string lang) + { + var (_, w) = GetLangConfig(lang); + if (from == null) return w.NoDate; + if (to == null) return $"{w.The} {FormatDateVoice(from.Value, today, lang)}"; + return $"{w.From} {FormatDateVoice(from.Value, today, lang)} {w.To} {FormatDateVoice(to.Value, today, lang)}"; + } + private static DateTime? ParseFilterDate(string? s) { if (string.IsNullOrEmpty(s)) return null; @@ -107,7 +171,33 @@ namespace ManagerService.Services return $"- id:{s.Id} | type:{s.Type} | titre:\"{title}\""; })); - messages.Add(new ChatMessage(ChatRole.System, $""" + var isVoiceMode = request.IsVoice; + + var systemPrompt = isVoiceMode ? $""" + Tu es le guide audio de visite de "{config?.Label ?? "cette application"}". + Aujourd'hui nous sommes le {DateTime.Now:dddd dd MMMM yyyy}. + Le visiteur t'écoute via des lunettes connectées Ray-Ban — il ne voit PAS d'écran. + Tu réponds en {request.Language}. Tu es chaleureux, naturel et très concis (2-3 phrases max). + + Voici les sections disponibles : + {sectionsSummary} + + RÈGLES STRICTES pour le mode vocal — chaque violation rend la réponse inutilisable : + 1. ZÉRO markdown : absolument aucun astérisque (*), tiret de liste (- item ou • item), dièse (#), backtick (`), ou crochet ([lien]). Écris uniquement des phrases. + 2. Les dates dans les résultats des outils sont DÉJÀ formatées dans la langue de la réponse — recopie-les telles quelles, ne les reformate JAMAIS. + 3. ZÉRO liste. Cite 2-3 éléments maximum directement dans une phrase fluide, séparés par des virgules. + 4. Pour les événements → appelle "GetUpcomingEvents", puis cite exactement 2-3 des plus pertinents dans une seule phrase fluide. Même si l'outil en retourne 50, n'en cite que 2-3. Ajoute [FIN] à la fin. + 5. Pour les détails d'un item → appelle "GetItemDetails" et résume en 2-3 phrases fluides. Ajoute [FIN] à la fin. + 6. Pour les sections de type Article → appelle "GetSectionDetail", résume le contenu en 2-3 phrases, puis demande "Veux-tu en savoir plus ?" (sans ajouter [FIN] car tu attends une réponse). + 7. Les sections de type Video, PDF, Slider, Quiz, Web, Game sont des contenus visuels. Si le visiteur en parle, mentionne leur existence mais précise qu'il doit les consulter sur l'écran. + 8. NE propose JAMAIS de navigation — le visiteur ne peut pas toucher l'écran. + 9. Ne mentionne JAMAIS les identifiants techniques (id, guid). + 10. Chaque réponse doit tenir en moins de 20 secondes à l'oral. + 11. Termine TOUJOURS par une phrase complète. + 12. Tu réponds UNIQUEMENT aux questions liées à cette visite et au contenu disponible dans les sections ci-dessus. Pour toute question hors-sujet (politique, actualité, recettes, vie personnelle…), décline poliment en une phrase courte et naturelle — varie la formulation à chaque fois (ex: "Ma spécialité c'est cette visite, pas ce sujet !", "Je suis ton guide ici, pas un moteur de recherche !", "Bonne question, mais là je sèche — demande-moi plutôt ce qu'il y a à voir ici !") — et ajoute [FIN]. + 13. Politesses et fin de conversation ("merci", "au revoir", "c'est tout", "ok merci") → réponds chaleureusement en une phrase courte ("Bonne visite !" ou "Avec plaisir, bonne visite !") et ajoute [FIN]. + 14. Signal [FIN] : ajoute le token [FIN] à la toute fin quand ta réponse est une information pure sans question posée au visiteur. Ne l'ajoute PAS quand tu poses une question (article, proposition de détails). + """ : $""" Tu es l'assistant de visite de l'application "{config?.Label ?? "cette application"}". Aujourd'hui nous sommes le {DateTime.Now:dddd dd MMMM yyyy}. Le weekend en cours (ou prochain) : samedi {weekendSat:dd/MM/yyyy} – dimanche {weekendSun:dd/MM/yyyy}. @@ -126,7 +216,9 @@ namespace ManagerService.Services - Ne mentionne JAMAIS les identifiants techniques (id, guid) dans tes réponses finales. - NE POSE JAMAIS de question à la fin de ta réponse après avoir présenté des résultats. - NE TE RÉPÈTE PAS : dis les choses une seule fois de manière fluide. - """)); + """; + + messages.Add(new ChatMessage(ChatRole.System, systemPrompt)); foreach (var h in request.History.TakeLast(MaxHistoryMessages)) messages.Add(new ChatMessage(h.Role == "user" ? ChatRole.User : ChatRole.Assistant, h.Content)); @@ -141,14 +233,37 @@ namespace ManagerService.Services AIFunctionFactory.Create( (string sectionId) => { - var section = sections.FirstOrDefault(s => s.Id == sectionId); + var section = _context.Sections + .FirstOrDefault(s => s.Id == sectionId && s.ConfigurationId == request.ConfigurationId); if (section == null) return "Section non trouvée."; - var title = section.Title?.FirstOrDefault(t => t.language == request.Language)?.value ?? section.Label; - var desc = section.Description?.FirstOrDefault(t => t.language == request.Language)?.value ?? ""; - return $"Titre: {title}\nDescription: {desc}\nType: {section.Type}"; + var lang = request.Language; + var title = StripHtml(section.Title?.FirstOrDefault(t => t.language == lang)?.value + ?? section.Title?.FirstOrDefault()?.value + ?? section.Label); + var desc = StripHtml(section.Description?.FirstOrDefault(t => t.language == lang)?.value + ?? section.Description?.FirstOrDefault()?.value + ?? ""); + + // Requêtes explicites par type pour contourner les problèmes de cache EF Core + var extra = ""; + var asArticle = _context.Sections + .OfType() + .FirstOrDefault(s => s.Id == sectionId); + if (asArticle != null) + { + extra = StripHtml(asArticle.ArticleContent?.FirstOrDefault(t => t.language == lang)?.value + ?? asArticle.ArticleContent?.FirstOrDefault()?.value ?? ""); + } + + var sb = new System.Text.StringBuilder(); + sb.AppendLine($"Titre: {title}"); + if (!string.IsNullOrWhiteSpace(desc)) sb.AppendLine($"Description: {desc}"); + if (!string.IsNullOrWhiteSpace(extra)) sb.AppendLine($"Contenu: {extra}"); + sb.AppendLine($"Type: {section.Type}"); + return sb.ToString().Trim(); }, "GetSectionDetail", - "Récupère le titre et la description d'une section." + "Récupère le titre, la description et le contenu complet d'une section (article, vidéo, etc.)." ), AIFunctionFactory.Create( async (string? dateFrom, string? dateTo) => @@ -193,10 +308,14 @@ namespace ManagerService.Services if (end == null || end.Value.Date < today || dtFrom?.Date > horizon) continue; if (filterFrom.HasValue && end.Value.Date < filterFrom.Value) continue; if (filterTo.HasValue && dtFrom?.Date > filterTo.Value) continue; - var dateStr = dtFrom.HasValue ? dtFrom.Value.ToString("dd/MM/yyyy") : "Date non précisée"; - if (dtTo.HasValue && dtTo.Value.Date != dtFrom?.Date) - dateStr += $" → {dtTo.Value.ToString("dd/MM/yyyy")}"; - allEvents.Add((dtFrom ?? DateTime.MaxValue, $"- [En ligne] AgendaId:{agenda.Id} | Titre:{e.name ?? "Sans titre"} | Date:{dateStr}")); + var dtToOpt = dtTo.HasValue && dtTo.Value.Date != dtFrom?.Date ? dtTo : null; + var dateStr = isVoiceMode + ? FormatDateRangeVoice(dtFrom, dtToOpt, today, request.Language) + : (dtFrom.HasValue ? dtFrom.Value.ToString("dd/MM/yyyy") : "Date non précisée") + + (dtToOpt.HasValue ? $" → {dtToOpt.Value:dd/MM/yyyy}" : ""); + allEvents.Add((dtFrom ?? DateTime.MaxValue, isVoiceMode + ? $"{e.name ?? "Sans titre"} ({dateStr})" + : $"- [En ligne] AgendaId:{agenda.Id} | Titre:{e.name ?? "Sans titre"} | Date:{dateStr}")); } } } @@ -213,10 +332,14 @@ namespace ManagerService.Services foreach (var e in events) { var title = e.Label?.FirstOrDefault(t => t.language == request.Language)?.value ?? e.Label?.FirstOrDefault()?.value ?? "Sans titre"; - var dateStr = e.DateFrom.HasValue ? e.DateFrom.Value.ToString("dd/MM/yyyy") : "Date non précisée"; - if (e.DateTo.HasValue && e.DateTo.Value.Date != e.DateFrom?.Date) - dateStr += $" → {e.DateTo.Value.ToString("dd/MM/yyyy")}"; - allEvents.Add((e.DateFrom ?? DateTime.MaxValue, $"- SectionId:{agenda.Id} | EventId:{e.Id} | Titre:{title} | Date:{dateStr}")); + var dtToOpt2 = e.DateTo.HasValue && e.DateTo.Value.Date != e.DateFrom?.Date ? e.DateTo : null; + var dateStr = isVoiceMode + ? FormatDateRangeVoice(e.DateFrom, dtToOpt2, today, request.Language) + : (e.DateFrom.HasValue ? e.DateFrom.Value.ToString("dd/MM/yyyy") : "Date non précisée") + + (dtToOpt2.HasValue ? $" → {dtToOpt2.Value:dd/MM/yyyy}" : ""); + allEvents.Add((e.DateFrom ?? DateTime.MaxValue, isVoiceMode + ? $"{title} ({dateStr})" + : $"- SectionId:{agenda.Id} | EventId:{e.Id} | Titre:{title} | Date:{dateStr}")); } } } @@ -324,7 +447,12 @@ namespace ManagerService.Services "GetItemDetails", "Récupère les détails complets (prix, horaires, contact, site) d'un événement (type='event') ou d'un point d'intérêt (type='poi') via son ID." ), - AIFunctionFactory.Create( + }; + + // Navigation et cards : uniquement en mode UI (pas Voice) + if (!isVoiceMode) + { + tools.Add(AIFunctionFactory.Create( (string sectionId, string sectionTitle, string sectionType) => { var imageUrl = sections.FirstOrDefault(s => s.Id == sectionId)?.ImageSource; @@ -333,8 +461,8 @@ namespace ManagerService.Services }, "navigate_to_section", "Propose navigation to a specific section when the user wants to go there or see it." - ), - AIFunctionFactory.Create( + )); + tools.Add(AIFunctionFactory.Create( (string[] titles, string[] subtitles, string[]? icons) => { cards = titles.Select((t, i) => new AiCardDTO @@ -347,14 +475,25 @@ namespace ManagerService.Services }, "show_cards", "Display structured info cards in the chat. Use for lists of items (events, activities...) that benefit from visual presentation." - ) - }; + )); + } try { options = new ChatOptions { Tools = tools }; var response = await _chatClient.GetResponseAsync(messages, options); - return new AiChatResponse { Reply = response.Text ?? "", Cards = cards, Navigation = navigation }; + string reply; + bool expectsReply; + if (isVoiceMode) + { + (reply, expectsReply) = StripMarkdownForVoice(response.Text ?? ""); + } + else + { + reply = response.Text ?? ""; + expectsReply = true; + } + return new AiChatResponse { Reply = reply, Cards = cards, Navigation = navigation, ExpectsReply = expectsReply }; } catch (System.ClientModel.ClientResultException ex) { @@ -391,7 +530,27 @@ namespace ManagerService.Services return $"- id:{c.Id} | titre:\"{title}\"{typesStr}"; })); - messages.Add(new ChatMessage(ChatRole.System, $""" + var isVoiceMode = request.IsVoice; + + var instanceSystemPrompt = isVoiceMode ? $""" + Tu es le guide audio de visite. + Aujourd'hui nous sommes le {DateTime.Now:dddd dd MMMM yyyy}. + Le visiteur t'écoute via des lunettes connectées — il ne voit PAS d'écran. + Tu réponds en {request.Language}. Tu es chaleureux, naturel et très concis (2-3 phrases max). + + Voici les expériences (visites) disponibles : + {configsSummary} + + RÈGLES STRICTES pour le mode vocal : + 1. ZÉRO markdown : aucun astérisque, tiret de liste, dièse, backtick. Écris uniquement des phrases. + 2. ZÉRO liste. Cite 2-3 éléments maximum dans une phrase fluide séparés par des virgules. + 3. Pour les événements → appelle "SearchEventsGlobal", puis cite exactement 2-3 des plus pertinents dans une phrase fluide. Ajoute [FIN]. + 4. NE propose JAMAIS de navigation. Ne mentionne JAMAIS les identifiants techniques. + 5. Chaque réponse doit tenir en moins de 20 secondes à l'oral. + 6. Tu réponds UNIQUEMENT aux questions liées à cette visite. Pour toute question hors-sujet, réponds : "Je suis uniquement là pour t'accompagner dans ta visite." et ajoute [FIN]. + 7. Politesses ("merci", "au revoir") → réponse courte et chaleureuse + [FIN]. + 8. Signal [FIN] : ajoute-le quand ta réponse est une information pure sans question au visiteur. + """ : $""" Tu es l'assistant de visite principal. Ton rôle est d'orienter l'utilisateur vers la bonne expérience de visite. Aujourd'hui nous sommes le {DateTime.Now:dddd dd MMMM yyyy}. Le weekend en cours (ou prochain) : samedi {weekendSat:dd/MM/yyyy} – dimanche {weekendSun:dd/MM/yyyy}. @@ -409,7 +568,9 @@ namespace ManagerService.Services 6. Pour les détails d'un item → "GetItemDetailsGlobal". - NE POSE JAMAIS de question à la fin de ta réponse après avoir présenté des résultats. - NE TE RÉPÈTE PAS : dis les choses une seule fois de manière fluide. - """)); + """; + + messages.Add(new ChatMessage(ChatRole.System, instanceSystemPrompt)); foreach (var h in request.History.TakeLast(MaxHistoryMessages)) messages.Add(new ChatMessage(h.Role == "user" ? ChatRole.User : ChatRole.Assistant, h.Content)); @@ -617,7 +778,18 @@ namespace ManagerService.Services { options = new ChatOptions { Tools = tools }; var response = await _chatClient.GetResponseAsync(messages, options); - return new AiChatResponse { Reply = response.Text ?? "", Navigation = navigation }; + string reply; + bool expectsReply; + if (isVoiceMode) + { + (reply, expectsReply) = StripMarkdownForVoice(response.Text ?? ""); + } + else + { + reply = response.Text ?? ""; + expectsReply = true; + } + return new AiChatResponse { Reply = reply, Navigation = navigation, ExpectsReply = expectsReply }; } catch (System.ClientModel.ClientResultException ex) { diff --git a/ManagerService/Services/SectionFactory.cs b/ManagerService/Services/SectionFactory.cs index 1ee3c3c..7586ba3 100644 --- a/ManagerService/Services/SectionFactory.cs +++ b/ManagerService/Services/SectionFactory.cs @@ -25,6 +25,7 @@ namespace ManagerService.Services VideoDTO videoDTO = new VideoDTO(); WeatherDTO weatherDTO = new WeatherDTO(); WebDTO webDTO = new WebDTO(); + ParcoursDTO parcoursDTO = new ParcoursDTO(); switch (dto.type) { @@ -35,7 +36,10 @@ namespace ManagerService.Services articleDTO = JsonConvert.DeserializeObject(jsonElement.ToString()); break; case SectionType.Event: - sectionEventDTO = JsonConvert.DeserializeObject(jsonElement.ToString()); + sectionEventDTO = JsonConvert.DeserializeObject( + jsonElement.ToString(), + new JsonSerializerSettings { Error = (_, args) => args.ErrorContext.Handled = true } + ); break; case SectionType.Map: mapDTO = JsonConvert.DeserializeObject(jsonElement.ToString()); @@ -64,6 +68,9 @@ namespace ManagerService.Services case SectionType.Web: webDTO = JsonConvert.DeserializeObject(jsonElement.ToString()); break; + case SectionType.Parcours: + parcoursDTO = JsonConvert.DeserializeObject(jsonElement.ToString()); + break; } return dto.type switch @@ -139,8 +146,8 @@ namespace ManagerService.Services Longitude = dto.longitude, MeterZoneGPS = dto.meterZoneGPS, Type = dto.type, - StartDate = sectionEventDTO.StartDate, - EndDate = sectionEventDTO.EndDate, + StartDate = sectionEventDTO.StartDate?.ToUniversalTime(), + EndDate = sectionEventDTO.EndDate?.ToUniversalTime(), ParcoursIds = sectionEventDTO.ParcoursIds, BaseSectionMapId = sectionEventDTO.BaseSectionMapId, //Programmes = // TODO specific @@ -363,6 +370,29 @@ namespace ManagerService.Services Type = dto.type, WebSource = webDTO.source, }, + SectionType.Parcours => new SectionParcours + { + Id = dto.id, + DateCreation = dto.dateCreation.Value, + ConfigurationId = dto.configurationId, + InstanceId = dto.instanceId, + Label = dto.label, + Title = dto.title, + Description = dto.description, + Order = dto.order.Value, + ImageId = dto.imageId, + ImageSource = dto.imageSource, + IsSubSection = dto.isSubSection, + ParentId = dto.parentId, + IsBeacon = dto.isBeacon, + BeaconId = dto.beaconId, + Latitude = dto.latitude, + Longitude = dto.longitude, + MeterZoneGPS = dto.meterZoneGPS, + Type = dto.type, + ShowMap = parcoursDTO.showMap, + BaseSectionMapId = parcoursDTO.baseSectionMapId, + }, _ => throw new NotImplementedException("Section type not handled") }; } @@ -446,8 +476,8 @@ namespace ManagerService.Services longitude = sectionEvent.Longitude, meterZoneGPS = sectionEvent.MeterZoneGPS, type = sectionEvent.Type, - StartDate = sectionEvent.StartDate, - EndDate = sectionEvent.EndDate, + StartDate = sectionEvent.StartDate?.Year > 1000 ? sectionEvent.StartDate : null, + EndDate = sectionEvent.EndDate?.Year > 1000 ? sectionEvent.EndDate : null, ParcoursIds = sectionEvent.ParcoursIds, BaseSectionMapId = sectionEvent.BaseSectionMapId, GlobalMapAnnotations = sectionEvent.GlobalMapAnnotations?.Select(ma => ma.ToDTO()).ToList() ?? new(), @@ -681,6 +711,31 @@ namespace ManagerService.Services type = web.Type, source = web.WebSource }, + SectionParcours parcours => new ParcoursDTO + { + id = parcours.Id, + dateCreation = parcours.DateCreation, + configurationId = parcours.ConfigurationId, + instanceId = parcours.InstanceId, + label = parcours.Label, + title = parcours.Title, + description = parcours.Description, + order = parcours.Order, + imageId = parcours.ImageId, + imageSource = parcours.ImageSource, + isActive = parcours.IsActive, + isSubSection = parcours.IsSubSection, + parentId = parcours.ParentId, + isBeacon = parcours.IsBeacon, + beaconId = parcours.BeaconId, + latitude = parcours.Latitude, + longitude = parcours.Longitude, + meterZoneGPS = parcours.MeterZoneGPS, + type = parcours.Type, + showMap = parcours.ShowMap, + baseSectionMapId = parcours.BaseSectionMapId, + // guidedPaths chargés spécifiquement dans GetFromConfigurationDetail + }, _ => throw new NotImplementedException("Section type not handled") }; }