444 lines
19 KiB
C#

using Marco.Pms.DataAccess.Data;
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<ProjectMongoDB> _projetCollection;
private readonly IMongoCollection<WorkItemMongoDB> _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<ProjectMongoDB>("ProjectDetails");
_taskCollection = mongoDB.GetCollection<WorkItemMongoDB>("WorkItemDetails");
}
public async Task AddProjectDetailsToCache(Project project)
{
//_logger.LogInfo("[AddProjectDetails] Initiated for ProjectId: {ProjectId}", project.Id);
var projectDetails = new ProjectMongoDB
{
Id = project.Id.ToString(),
Name = project.Name,
ShortName = project.ShortName,
ProjectAddress = project.ProjectAddress,
StartDate = project.StartDate,
EndDate = project.EndDate,
ContactPerson = project.ContactPerson
};
// Get project status
var status = await _context.StatusMasters
.AsNoTracking()
.FirstOrDefaultAsync(s => s.Id == project.ProjectStatusId);
projectDetails.ProjectStatus = new StatusMasterMongoDB
{
Id = status?.Id.ToString(),
Status = status?.Status
};
// Get project team size
var teamSize = await _context.ProjectAllocations
.AsNoTracking()
.CountAsync(pa => pa.ProjectId == project.Id && pa.IsActive);
projectDetails.TeamSize = teamSize;
// Fetch related infrastructure in parallel
var buildings = await _context.Buildings
.AsNoTracking()
.Where(b => b.ProjectId == project.Id)
.ToListAsync();
var buildingIds = buildings.Select(b => b.Id).ToList();
var floors = await _context.Floor
.AsNoTracking()
.Where(f => buildingIds.Contains(f.BuildingId))
.ToListAsync();
var floorIds = floors.Select(f => f.Id).ToList();
var workAreas = await _context.WorkAreas
.AsNoTracking()
.Where(wa => floorIds.Contains(wa.FloorId))
.ToListAsync();
var workAreaIds = workAreas.Select(wa => wa.Id).ToList();
var workItems = await _context.WorkItems
.Where(wi => workAreaIds.Contains(wi.WorkAreaId))
.ToListAsync();
double totalPlannedWork = 0, totalCompletedWork = 0;
var buildingMongoList = new List<BuildingMongoDB>();
foreach (var building in buildings)
{
double buildingPlanned = 0, buildingCompleted = 0;
var buildingFloors = floors.Where(f => f.BuildingId == building.Id).ToList();
var floorMongoList = new List<FloorMongoDB>();
foreach (var floor in buildingFloors)
{
double floorPlanned = 0, floorCompleted = 0;
var floorWorkAreas = workAreas.Where(wa => wa.FloorId == floor.Id).ToList();
var workAreaMongoList = new List<WorkAreaMongoDB>();
foreach (var wa in floorWorkAreas)
{
var items = workItems.Where(wi => wi.WorkAreaId == wa.Id).ToList();
double waPlanned = items.Sum(wi => wi.PlannedWork);
double waCompleted = items.Sum(wi => wi.CompletedWork);
workAreaMongoList.Add(new WorkAreaMongoDB
{
Id = wa.Id.ToString(),
AreaName = wa.AreaName,
PlannedWork = waPlanned,
CompletedWork = waCompleted
});
floorPlanned += waPlanned;
floorCompleted += waCompleted;
}
floorMongoList.Add(new FloorMongoDB
{
Id = floor.Id.ToString(),
FloorName = floor.FloorName,
PlannedWork = floorPlanned,
CompletedWork = floorCompleted,
WorkAreas = workAreaMongoList
});
buildingPlanned += floorPlanned;
buildingCompleted += floorCompleted;
}
buildingMongoList.Add(new BuildingMongoDB
{
Id = building.Id.ToString(),
BuildingName = building.Name,
Description = building.Description,
PlannedWork = buildingPlanned,
CompletedWork = buildingCompleted,
Floors = floorMongoList
});
totalPlannedWork += buildingPlanned;
totalCompletedWork += buildingCompleted;
}
projectDetails.Buildings = buildingMongoList;
projectDetails.PlannedWork = totalPlannedWork;
projectDetails.CompletedWork = totalCompletedWork;
await _projetCollection.InsertOneAsync(projectDetails);
//_logger.LogInfo("[AddProjectDetails] Project details inserted in MongoDB for ProjectId: {ProjectId}", project.Id);
}
public async Task<bool> UpdateProjectDetailsOnlyToCache(Project project)
{
//_logger.LogInfo("Starting update for project: {ProjectId}", project.Id);
var projectStatus = await _context.StatusMasters
.FirstOrDefaultAsync(s => s.Id == project.ProjectStatusId);
if (projectStatus == null)
{
//_logger.LogWarning("StatusMaster not found for ProjectStatusId: {StatusId}", project.ProjectStatusId);
}
// Build the update definition
var updates = Builders<ProjectMongoDB>.Update.Combine(
Builders<ProjectMongoDB>.Update.Set(r => r.Name, project.Name),
Builders<ProjectMongoDB>.Update.Set(r => r.ProjectAddress, project.ProjectAddress),
Builders<ProjectMongoDB>.Update.Set(r => r.ShortName, project.ShortName),
Builders<ProjectMongoDB>.Update.Set(r => r.ProjectStatus, new StatusMasterMongoDB
{
Id = projectStatus?.Id.ToString(),
Status = projectStatus?.Status
}),
Builders<ProjectMongoDB>.Update.Set(r => r.StartDate, project.StartDate),
Builders<ProjectMongoDB>.Update.Set(r => r.EndDate, project.EndDate),
Builders<ProjectMongoDB>.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)
{
//_logger.LogWarning("No project matched in MongoDB for update. ProjectId: {ProjectId}", project.Id);
return false;
}
//_logger.LogInfo("Project {ProjectId} successfully updated in MongoDB", project.Id);
return true;
}
public async Task<ProjectMongoDB?> GetProjectDetailsFromCache(Guid projectId)
{
// Build filter and projection to exclude large 'Buildings' list
var filter = Builders<ProjectMongoDB>.Filter.Eq(p => p.Id, projectId.ToString());
var projection = Builders<ProjectMongoDB>.Projection.Exclude(p => p.Buildings);
//_logger.LogInfo("Fetching project details for ProjectId: {ProjectId} from MongoDB", projectId);
// Perform query
var project = await _projetCollection
.Find(filter)
.Project<ProjectMongoDB>(projection)
.FirstOrDefaultAsync();
if (project == null)
{
//_logger.LogWarning("No project found in MongoDB for ProjectId: {ProjectId}", projectId);
return null;
}
//_logger.LogInfo("Successfully fetched project details (excluding Buildings) for ProjectId: {ProjectId}", projectId);
return project;
}
public async Task<List<ProjectMongoDB>?> GetProjectDetailsListFromCache(List<Guid> projectIds)
{
List<string> stringProjectIds = projectIds.Select(p => p.ToString()).ToList();
var filter = Builders<ProjectMongoDB>.Filter.In(p => p.Id, stringProjectIds);
var projection = Builders<ProjectMongoDB>.Projection.Exclude(p => p.Buildings);
var projects = await _projetCollection
.Find(filter)
.Project<ProjectMongoDB>(projection)
.ToListAsync();
return projects;
}
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<FloorMongoDB>()
};
var filter = Builders<ProjectMongoDB>.Filter.Eq(p => p.Id, stringProjectId);
var update = Builders<ProjectMongoDB>.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<WorkAreaMongoDB>()
};
var filter = Builders<ProjectMongoDB>.Filter.And(
Builders<ProjectMongoDB>.Filter.Eq(p => p.Id, stringProjectId),
Builders<ProjectMongoDB>.Filter.Eq("Buildings._id", floor.BuildingId.ToString())
);
var update = Builders<ProjectMongoDB>.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<ProjectMongoDB>.Filter.Eq(p => p.Id, stringProjectId);
var arrayFilters = new List<ArrayFilterDefinition>
{
new JsonArrayFilterDefinition<BsonDocument>("{ 'b._id': '" + buildingId + "' }"),
new JsonArrayFilterDefinition<BsonDocument>("{ 'f._id': '" + workArea.FloorId + "' }")
};
var update = Builders<ProjectMongoDB>.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<bool> UpdateBuildngInfraToCache(Guid projectId, Building? building, Floor? floor, WorkArea? workArea, Guid? buildingId)
{
var stringProjectId = projectId.ToString();
// Update Building
if (building != null)
{
var filter = Builders<ProjectMongoDB>.Filter.And(
Builders<ProjectMongoDB>.Filter.Eq(p => p.Id, stringProjectId),
Builders<ProjectMongoDB>.Filter.Eq("Buildings._id", building.Id.ToString())
);
var update = Builders<ProjectMongoDB>.Update.Combine(
Builders<ProjectMongoDB>.Update.Set("Buildings.$.BuildingName", building.Name),
Builders<ProjectMongoDB>.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<ArrayFilterDefinition>
{
new JsonArrayFilterDefinition<BsonDocument>("{ 'b._id': '" + floor.BuildingId + "' }"),
new JsonArrayFilterDefinition<BsonDocument>("{ 'f._id': '" + floor.Id + "' }")
};
var update = Builders<ProjectMongoDB>.Update.Set("Buildings.$[b].Floors.$[f].FloorName", floor.FloorName);
var updateOptions = new UpdateOptions { ArrayFilters = arrayFilters };
var filter = Builders<ProjectMongoDB>.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<ArrayFilterDefinition>
{
new JsonArrayFilterDefinition<BsonDocument>("{ 'b._id': '" + buildingId + "' }"),
new JsonArrayFilterDefinition<BsonDocument>("{ 'f._id': '" + workArea.FloorId + "' }"),
new JsonArrayFilterDefinition<BsonDocument>("{ 'a._id': '" + workArea.Id + "' }")
};
var update = Builders<ProjectMongoDB>.Update.Set("Buildings.$[b].Floors.$[f].WorkAreas.$[a].AreaName", workArea.AreaName);
var updateOptions = new UpdateOptions { ArrayFilters = arrayFilters };
var filter = Builders<ProjectMongoDB>.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<List<BuildingMongoDB>?> GetBuildingInfraFromCache(Guid projectId)
{
// Filter by project ID
var filter = Builders<ProjectMongoDB>.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;
}
// ------------------------------------------------------- WorkItem -------------------------------------------------------
}
}