using Marco.Pms.DataAccess.Data; using Marco.Pms.Model.Master; using Marco.Pms.Model.MongoDBModels; using Marco.Pms.Model.Projects; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using MongoDB.Bson; using MongoDB.Driver; namespace Marco.Pms.CacheHelper { public class ProjectCache { private readonly ApplicationDbContext _context; private readonly IMongoCollection _projetCollection; private readonly IMongoCollection _taskCollection; public ProjectCache(ApplicationDbContext context, IConfiguration configuration) { var connectionString = configuration["MongoDB:ConnectionString"]; _context = context; var mongoUrl = new MongoUrl(connectionString); var client = new MongoClient(mongoUrl); // Your MongoDB connection string var mongoDB = client.GetDatabase(mongoUrl.DatabaseName); // Your MongoDB Database name _projetCollection = mongoDB.GetCollection("ProjectDetails"); _taskCollection = mongoDB.GetCollection("WorkItemDetails"); } public async Task AddProjectDetailsToCache(ProjectMongoDB projectDetails) { await _projetCollection.InsertOneAsync(projectDetails); } public async Task AddProjectDetailsListToCache(List projectDetailsList) { await _projetCollection.InsertManyAsync(projectDetailsList); } public async Task UpdateProjectDetailsOnlyToCache(Project project, StatusMaster projectStatus) { // Build the update definition var updates = Builders.Update.Combine( Builders.Update.Set(r => r.Name, project.Name), Builders.Update.Set(r => r.ProjectAddress, project.ProjectAddress), Builders.Update.Set(r => r.ShortName, project.ShortName), Builders.Update.Set(r => r.ProjectStatus, new StatusMasterMongoDB { Id = projectStatus.Id.ToString(), Status = projectStatus.Status }), Builders.Update.Set(r => r.StartDate, project.StartDate), Builders.Update.Set(r => r.EndDate, project.EndDate), Builders.Update.Set(r => r.ContactPerson, project.ContactPerson) ); // Perform the update var result = await _projetCollection.UpdateOneAsync( filter: r => r.Id == project.Id.ToString(), update: updates ); if (result.MatchedCount == 0) { return false; } return true; } public async Task GetProjectDetailsFromCache(Guid projectId) { // Build filter and projection to exclude large 'Buildings' list var filter = Builders.Filter.Eq(p => p.Id, projectId.ToString()); var projection = Builders.Projection.Exclude(p => p.Buildings); // Perform query var project = await _projetCollection .Find(filter) .Project(projection) .FirstOrDefaultAsync(); return project; } public async Task> GetProjectDetailsListFromCache(List projectIds) { List stringProjectIds = projectIds.Select(p => p.ToString()).ToList(); var filter = Builders.Filter.In(p => p.Id, stringProjectIds); var projection = Builders.Projection.Exclude(p => p.Buildings); var projects = await _projetCollection .Find(filter) .Project(projection) .ToListAsync(); return projects; } public async Task DeleteProjectByIdFromCacheAsync(Guid projectId) { var filter = Builders.Filter.Eq(e => e.Id, projectId.ToString()); var result = await _projetCollection.DeleteOneAsync(filter); return result.DeletedCount > 0; } public async Task RemoveProjectsFromCacheAsync(List projectIds) { var stringIds = projectIds.Select(id => id.ToString()).ToList(); var filter = Builders.Filter.In(p => p.Id, stringIds); var result = await _projetCollection.DeleteManyAsync(filter); return result.DeletedCount > 0; } // ------------------------------------------------------- Project InfraStructure ------------------------------------------------------- public async Task AddBuildngInfraToCache(Guid projectId, Building? building, Floor? floor, WorkArea? workArea, Guid? buildingId) { var stringProjectId = projectId.ToString(); // Add Building if (building != null) { var buildingMongo = new BuildingMongoDB { Id = building.Id.ToString(), BuildingName = building.Name, Description = building.Description, PlannedWork = 0, CompletedWork = 0, Floors = new List() }; var filter = Builders.Filter.Eq(p => p.Id, stringProjectId); var update = Builders.Update.Push("Buildings", buildingMongo); var result = await _projetCollection.UpdateOneAsync(filter, update); if (result.MatchedCount == 0) { //_logger.LogWarning("Project not found while adding building. ProjectId: {ProjectId}", projectId); return; } //_logger.LogInfo("Building {BuildingId} added to project {ProjectId}", building.Id, projectId); return; } // Add Floor if (floor != null) { var floorMongo = new FloorMongoDB { Id = floor.Id.ToString(), FloorName = floor.FloorName, PlannedWork = 0, CompletedWork = 0, WorkAreas = new List() }; var filter = Builders.Filter.And( Builders.Filter.Eq(p => p.Id, stringProjectId), Builders.Filter.Eq("Buildings._id", floor.BuildingId.ToString()) ); var update = Builders.Update.Push("Buildings.$.Floors", floorMongo); var result = await _projetCollection.UpdateOneAsync(filter, update); if (result.MatchedCount == 0) { //_logger.LogWarning("Project or building not found while adding floor. ProjectId: {ProjectId}, BuildingId: {BuildingId}", projectId, floor.BuildingId); return; } //_logger.LogInfo("Floor {FloorId} added to building {BuildingId} in project {ProjectId}", floor.Id, floor.BuildingId, projectId); return; } // Add WorkArea if (workArea != null && buildingId != null) { var workAreaMongo = new WorkAreaMongoDB { Id = workArea.Id.ToString(), AreaName = workArea.AreaName, PlannedWork = 0, CompletedWork = 0 }; var filter = Builders.Filter.Eq(p => p.Id, stringProjectId); var arrayFilters = new List { new JsonArrayFilterDefinition("{ 'b._id': '" + buildingId + "' }"), new JsonArrayFilterDefinition("{ 'f._id': '" + workArea.FloorId + "' }") }; var update = Builders.Update.Push("Buildings.$[b].Floors.$[f].WorkAreas", workAreaMongo); var updateOptions = new UpdateOptions { ArrayFilters = arrayFilters }; var result = await _projetCollection.UpdateOneAsync(filter, update, updateOptions); if (result.MatchedCount == 0) { //_logger.LogWarning("Project or nested structure not found while adding work area. ProjectId: {ProjectId}, BuildingId: {BuildingId}, FloorId: {FloorId}", projectId, buildingId, workArea.FloorId); return; } //_logger.LogInfo("WorkArea {WorkAreaId} added to floor {FloorId} in building {BuildingId}, ProjectId: {ProjectId}", workArea.Id, workArea.FloorId, buildingId, projectId); return; } // Fallback case when no valid data was passed //_logger.LogWarning("No valid infra data provided to add for ProjectId: {ProjectId}", projectId); } public async Task UpdateBuildngInfraToCache(Guid projectId, Building? building, Floor? floor, WorkArea? workArea, Guid? buildingId) { var stringProjectId = projectId.ToString(); // Update Building if (building != null) { var filter = Builders.Filter.And( Builders.Filter.Eq(p => p.Id, stringProjectId), Builders.Filter.Eq("Buildings._id", building.Id.ToString()) ); var update = Builders.Update.Combine( Builders.Update.Set("Buildings.$.BuildingName", building.Name), Builders.Update.Set("Buildings.$.Description", building.Description) ); var result = await _projetCollection.UpdateOneAsync(filter, update); if (result.MatchedCount == 0) { //_logger.LogWarning("Update failed: Project or Building not found. ProjectId: {ProjectId}, BuildingId: {BuildingId}", projectId, building.Id); return false; } //_logger.LogInfo("Building {BuildingId} updated successfully in project {ProjectId}", building.Id, projectId); return true; } // Update Floor if (floor != null) { var arrayFilters = new List { new JsonArrayFilterDefinition("{ 'b._id': '" + floor.BuildingId + "' }"), new JsonArrayFilterDefinition("{ 'f._id': '" + floor.Id + "' }") }; var update = Builders.Update.Set("Buildings.$[b].Floors.$[f].FloorName", floor.FloorName); var updateOptions = new UpdateOptions { ArrayFilters = arrayFilters }; var filter = Builders.Filter.Eq(p => p.Id, stringProjectId); var result = await _projetCollection.UpdateOneAsync(filter, update, updateOptions); if (result.MatchedCount == 0) { //_logger.LogWarning("Update failed: Project or Floor not found. ProjectId: {ProjectId}, BuildingId: {BuildingId}, FloorId: {FloorId}", projectId, floor.BuildingId, floor.Id); return false; } //_logger.LogInfo("Floor {FloorId} updated successfully in Building {BuildingId}, ProjectId: {ProjectId}", floor.Id, floor.BuildingId, projectId); return true; } // Update WorkArea if (workArea != null && buildingId != null) { var arrayFilters = new List { new JsonArrayFilterDefinition("{ 'b._id': '" + buildingId + "' }"), new JsonArrayFilterDefinition("{ 'f._id': '" + workArea.FloorId + "' }"), new JsonArrayFilterDefinition("{ 'a._id': '" + workArea.Id + "' }") }; var update = Builders.Update.Set("Buildings.$[b].Floors.$[f].WorkAreas.$[a].AreaName", workArea.AreaName); var updateOptions = new UpdateOptions { ArrayFilters = arrayFilters }; var filter = Builders.Filter.Eq(p => p.Id, stringProjectId); var result = await _projetCollection.UpdateOneAsync(filter, update, updateOptions); if (result.MatchedCount == 0) { //_logger.LogWarning("Update failed: Project or WorkArea not found. ProjectId: {ProjectId}, BuildingId: {BuildingId}, FloorId: {FloorId}, WorkAreaId: {WorkAreaId}", //projectId, buildingId, workArea.FloorId, workArea.Id); return false; } //_logger.LogInfo("WorkArea {WorkAreaId} updated successfully in Floor {FloorId}, Building {BuildingId}, ProjectId: {ProjectId}", //workArea.Id, workArea.FloorId, buildingId, projectId); return true; } //_logger.LogWarning("No update performed. Missing or invalid data for ProjectId: {ProjectId}", projectId); return false; } public async Task?> GetBuildingInfraFromCache(Guid projectId) { // Filter by project ID var filter = Builders.Filter.Eq(p => p.Id, projectId.ToString()); // Project only the "Buildings" field from the document var buildings = await _projetCollection .Find(filter) .Project(p => p.Buildings) .FirstOrDefaultAsync(); //if (buildings == null) //{ // _logger.LogWarning("No building infrastructure found for ProjectId: {ProjectId}", projectId); //} //else //{ // _logger.LogInfo("Fetched {Count} buildings for ProjectId: {ProjectId}", buildings.Count, projectId); //} return buildings; } public async Task UpdatePlannedAndCompleteWorksInBuildingFromCache(Guid workAreaId, double plannedWork, double completedWork) { var filter = Builders.Filter.Eq("Buildings.Floors.WorkAreas._id", workAreaId.ToString()); var project = await _projetCollection.Find(filter).FirstOrDefaultAsync(); string? selectedBuildingId = null; string? selectedFloorId = null; string? selectedWorkAreaId = null; foreach (var building in project.Buildings) { foreach (var floor in building.Floors) { foreach (var area in floor.WorkAreas) { if (area.Id == workAreaId.ToString()) { selectedWorkAreaId = area.Id; selectedFloorId = floor.Id; selectedBuildingId = building.Id; } } } } var arrayFilters = new List { new JsonArrayFilterDefinition("{ 'b._id': '" + selectedBuildingId + "' }"), new JsonArrayFilterDefinition("{ 'f._id': '" + selectedFloorId + "' }"), new JsonArrayFilterDefinition("{ 'a._id': '" + selectedWorkAreaId + "' }") }; var updateOptions = new UpdateOptions { ArrayFilters = arrayFilters }; var update = Builders.Update .Inc("Buildings.$[b].Floors.$[f].WorkAreas.$[a].PlannedWork", plannedWork) .Inc("Buildings.$[b].Floors.$[f].WorkAreas.$[a].CompletedWork", completedWork) .Inc("Buildings.$[b].Floors.$[f].PlannedWork", plannedWork) .Inc("Buildings.$[b].Floors.$[f].CompletedWork", completedWork) .Inc("Buildings.$[b].PlannedWork", plannedWork) .Inc("Buildings.$[b].CompletedWork", completedWork) .Inc("PlannedWork", plannedWork) .Inc("CompletedWork", completedWork); var result = await _projetCollection.UpdateOneAsync(filter, update, updateOptions); } public async Task GetBuildingAndFloorByWorkAreaIdFromCache(Guid workAreaId) { var pipeline = new[] { new BsonDocument("$unwind", "$Buildings"), new BsonDocument("$unwind", "$Buildings.Floors"), new BsonDocument("$unwind", "$Buildings.Floors.WorkAreas"), new BsonDocument("$match", new BsonDocument("Buildings.Floors.WorkAreas._id", workAreaId.ToString())), new BsonDocument("$project", new BsonDocument { { "_id", 0 }, { "ProjectId", "$_id" }, { "ProjectName", "$Name" }, { "PlannedWork", "$PlannedWork" }, { "CompletedWork", "$CompletedWork" }, { "Building", new BsonDocument { { "_id", "$Buildings._id" }, { "BuildingName", "$Buildings.BuildingName" }, { "Description", "$Buildings.Description" }, { "PlannedWork", "$Buildings.PlannedWork" }, { "CompletedWork", "$Buildings.CompletedWork" } } }, { "Floor", new BsonDocument { { "_id", "$Buildings.Floors._id" }, { "FloorName", "$Buildings.Floors.FloorName" }, { "PlannedWork", "$Buildings.Floors.PlannedWork" }, { "CompletedWork", "$Buildings.Floors.CompletedWork" } } }, { "WorkArea", "$Buildings.Floors.WorkAreas" } }) }; var result = await _projetCollection.Aggregate(pipeline).FirstOrDefaultAsync(); if (result == null) return null; return result; } // ------------------------------------------------------- WorkItem ------------------------------------------------------- public async Task> GetWorkItemsByWorkAreaIdsFromCache(List workAreaIds) { var stringWorkAreaIds = workAreaIds.Select(wa => wa.ToString()).ToList(); var filter = Builders.Filter.In(w => w.WorkAreaId, stringWorkAreaIds); var workItems = await _taskCollection // replace with your actual collection name .Find(filter) .ToListAsync(); return workItems; } public async Task ManageWorkItemDetailsToCache(List workItems) { foreach (WorkItemMongoDB workItem in workItems) { var filter = Builders.Filter.Eq(p => p.Id, workItem.Id.ToString()); var updates = Builders.Update.Combine( Builders.Update.Set(r => r.WorkAreaId, workItem.WorkAreaId.ToString()), Builders.Update.Set(r => r.ParentTaskId, (workItem.ParentTaskId != null ? workItem.ParentTaskId.ToString() : null)), Builders.Update.Set(r => r.PlannedWork, workItem.PlannedWork), Builders.Update.Set(r => r.TodaysAssigned, workItem.TodaysAssigned), Builders.Update.Set(r => r.CompletedWork, workItem.CompletedWork), Builders.Update.Set(r => r.Description, workItem.Description), Builders.Update.Set(r => r.TaskDate, workItem.TaskDate), Builders.Update.Set(r => r.ExpireAt, DateTime.UtcNow.Date.AddDays(1)), Builders.Update.Set(r => r.ActivityMaster, workItem.ActivityMaster), Builders.Update.Set(r => r.WorkCategoryMaster, workItem.WorkCategoryMaster) ); var options = new UpdateOptions { IsUpsert = true }; var result = await _taskCollection.UpdateOneAsync(filter, updates, options); if (result.UpsertedId != null) { var indexKeys = Builders.IndexKeys.Ascending(x => x.ExpireAt); var indexOptions = new CreateIndexOptions { ExpireAfter = TimeSpan.Zero // required for fixed expiration time }; var indexModel = new CreateIndexModel(indexKeys, indexOptions); await _taskCollection.Indexes.CreateOneAsync(indexModel); } } } public async Task> GetWorkItemDetailsByWorkAreaFromCache(Guid workAreaId) { var filter = Builders.Filter.Eq(p => p.WorkAreaId, workAreaId.ToString()); var options = new UpdateOptions { IsUpsert = true }; var workItems = await _taskCollection .Find(filter) .ToListAsync(); return workItems; } public async Task GetWorkItemDetailsByIdFromCache(Guid id) { var filter = Builders.Filter.Eq(p => p.Id, id.ToString()); var options = new UpdateOptions { IsUpsert = true }; var workItem = await _taskCollection .Find(filter) .FirstOrDefaultAsync(); return workItem; } public async Task UpdatePlannedAndCompleteWorksInWorkItemToCache(Guid id, double plannedWork, double completedWork, double todaysAssigned) { var filter = Builders.Filter.Eq(p => p.Id, id.ToString()); var updates = Builders.Update .Inc("PlannedWork", plannedWork) .Inc("CompletedWork", completedWork) .Inc("TodaysAssigned", todaysAssigned); var result = await _taskCollection.UpdateOneAsync(filter, updates); if (result.ModifiedCount > 0) { return true; } return false; } public async Task DeleteWorkItemByIdFromCacheAsync(Guid workItemId) { var filter = Builders.Filter.Eq(e => e.Id, workItemId.ToString()); var result = await _taskCollection.DeleteOneAsync(filter); return result.DeletedCount > 0; } } }