530 lines
24 KiB
C#

using Marco.Pms.DataAccess.Data;
using Marco.Pms.Model.Master;
using Marco.Pms.Model.MongoDBModels;
using Marco.Pms.Model.MongoDBModels.Masters;
using Marco.Pms.Model.MongoDBModels.Project;
using Marco.Pms.Model.OrganizationModel;
using Marco.Pms.Model.Projects;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using MongoDB.Bson;
using MongoDB.Driver;
namespace Marco.Pms.Helpers
{
public class ProjectCache
{
private readonly IMongoCollection<ProjectMongoDB> _projectCollection;
private readonly IMongoCollection<WorkItemMongoDB> _taskCollection;
public ProjectCache(ApplicationDbContext context, IConfiguration configuration)
{
var connectionString = configuration["MongoDB:ConnectionString"];
var mongoUrl = new MongoUrl(connectionString);
var client = new MongoClient(mongoUrl); // Your MongoDB connection string
var mongoDB = client.GetDatabase(mongoUrl.DatabaseName); // Your MongoDB Database name
_projectCollection = mongoDB.GetCollection<ProjectMongoDB>("ProjectDetails");
_taskCollection = mongoDB.GetCollection<WorkItemMongoDB>("WorkItemDetails");
}
#region=================================================================== Project Cache Helper ===================================================================
public async Task AddProjectDetailsToCache(ProjectMongoDB projectDetails)
{
await _projectCollection.InsertOneAsync(projectDetails);
await InitializeCollectionAsync();
}
public async Task AddProjectDetailsListToCache(List<ProjectMongoDB> projectDetailsList)
{
// 1. Add a guard clause to avoid an unnecessary database call for an empty list.
if (projectDetailsList == null || !projectDetailsList.Any())
{
return;
}
// 2. Perform the insert operation. This is the only responsibility of this method.
await _projectCollection.InsertManyAsync(projectDetailsList);
await InitializeCollectionAsync();
}
private async Task InitializeCollectionAsync()
{
var indexKeys = Builders<ProjectMongoDB>.IndexKeys.Ascending(x => x.ExpireAt);
var indexOptions = new CreateIndexOptions
{
ExpireAfter = TimeSpan.Zero // required for fixed expiration time
};
var indexModel = new CreateIndexModel<ProjectMongoDB>(indexKeys, indexOptions);
await _projectCollection.Indexes.CreateOneAsync(indexModel);
}
public async Task<bool> UpdateProjectDetailsOnlyToCache(Project project, StatusMaster projectStatus, Organization promotor, Organization pmc)
{
// 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.Promoter, new OrganizationMongoDB
{
Id = promotor.Id.ToString(),
Name = promotor.Name,
ContactPerson = promotor.ContactPerson,
Email = promotor.Email
}),
Builders<ProjectMongoDB>.Update.Set(r => r.PMC, new OrganizationMongoDB
{
Id = pmc.Id.ToString(),
Name = pmc.Name,
ContactPerson = pmc.ContactPerson,
Email = pmc.Email
}),
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 _projectCollection.UpdateOneAsync(
filter: r => r.Id == project.Id.ToString(),
update: updates
);
if (result.MatchedCount == 0)
{
return false;
}
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);
// Perform query
var project = await _projectCollection
.Find(filter)
.Project<ProjectMongoDB>(projection)
.FirstOrDefaultAsync();
return project;
}
public async Task<ProjectMongoDB?> GetProjectDetailsWithBuildingsFromCache(Guid projectId)
{
// Build filter and projection to exclude large 'Buildings' list
var filter = Builders<ProjectMongoDB>.Filter.Eq(p => p.Id, projectId.ToString());
// Perform query
var project = await _projectCollection
.Find(filter)
.FirstOrDefaultAsync();
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 _projectCollection
.Find(filter)
.Project<ProjectMongoDB>(projection)
.ToListAsync();
return projects;
}
public async Task<bool> DeleteProjectByIdFromCacheAsync(Guid projectId)
{
var filter = Builders<ProjectMongoDB>.Filter.Eq(e => e.Id, projectId.ToString());
var result = await _projectCollection.DeleteOneAsync(filter);
return result.DeletedCount > 0;
}
public async Task<bool> RemoveProjectsFromCacheAsync(List<Guid> projectIds)
{
var stringIds = projectIds.Select(id => id.ToString()).ToList();
var filter = Builders<ProjectMongoDB>.Filter.In(p => p.Id, stringIds);
var result = await _projectCollection.DeleteManyAsync(filter);
return result.DeletedCount > 0;
}
#endregion
#region=================================================================== Project infrastructure Cache Helper ===================================================================
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 _projectCollection.UpdateOneAsync(filter, update);
if (result.MatchedCount == 0)
{
return;
}
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 _projectCollection.UpdateOneAsync(filter, update);
if (result.MatchedCount == 0)
{
return;
}
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 _projectCollection.UpdateOneAsync(filter, update, updateOptions);
if (result.MatchedCount == 0)
{
return;
}
return;
}
}
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 _projectCollection.UpdateOneAsync(filter, update);
if (result.MatchedCount == 0)
{
return false;
}
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 _projectCollection.UpdateOneAsync(filter, update, updateOptions);
if (result.MatchedCount == 0)
{
return false;
}
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 _projectCollection.UpdateOneAsync(filter, update, updateOptions);
if (result.MatchedCount == 0)
{
return false;
}
return true;
}
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 _projectCollection
.Find(filter)
.Project(p => p.Buildings)
.FirstOrDefaultAsync();
return buildings;
}
public async Task UpdatePlannedAndCompleteWorksInBuildingFromCache(Guid workAreaId, double plannedWork, double completedWork)
{
var filter = Builders<ProjectMongoDB>.Filter.Eq("Buildings.Floors.WorkAreas._id", workAreaId.ToString());
var project = await _projectCollection.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<ArrayFilterDefinition>
{
new JsonArrayFilterDefinition<BsonDocument>("{ 'b._id': '" + selectedBuildingId + "' }"),
new JsonArrayFilterDefinition<BsonDocument>("{ 'f._id': '" + selectedFloorId + "' }"),
new JsonArrayFilterDefinition<BsonDocument>("{ 'a._id': '" + selectedWorkAreaId + "' }")
};
var updateOptions = new UpdateOptions { ArrayFilters = arrayFilters };
var update = Builders<ProjectMongoDB>.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 _projectCollection.UpdateOneAsync(filter, update, updateOptions);
}
public async Task<WorkAreaInfoMongoDB?> 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 _projectCollection.Aggregate<WorkAreaInfoMongoDB>(pipeline).FirstOrDefaultAsync();
if (result == null)
return null;
return result;
}
#endregion
#region=================================================================== WorkItem Cache Helper ===================================================================
public async Task<List<WorkItemMongoDB>> GetWorkItemsByWorkAreaIdsFromCache(List<Guid> workAreaIds, List<Guid> serviceIds)
{
var stringWorkAreaIds = workAreaIds.Select(wa => wa.ToString()).ToList();
var filterBuilder = Builders<WorkItemMongoDB>.Filter;
var filter = filterBuilder.Empty;
filter &= filterBuilder.In(w => w.WorkAreaId, stringWorkAreaIds);
if (serviceIds.Any())
{
var stringServiceIds = serviceIds.Select(s => s.ToString()).ToList();
filter &= filterBuilder.In(w => w.ActivityMaster!.ActivityGroupMaster!.Service!.Id, stringServiceIds);
}
var workItems = await _taskCollection // replace with your actual collection name
.Find(filter)
.ToListAsync();
return workItems;
}
public async Task ManageWorkItemDetailsToCache(List<WorkItemMongoDB> workItems)
{
foreach (WorkItemMongoDB workItem in workItems)
{
var filter = Builders<WorkItemMongoDB>.Filter.Eq(p => p.Id, workItem.Id.ToString());
var updates = Builders<WorkItemMongoDB>.Update.Combine(
Builders<WorkItemMongoDB>.Update.Set(r => r.WorkAreaId, workItem.WorkAreaId.ToString()),
Builders<WorkItemMongoDB>.Update.Set(r => r.ParentTaskId, (workItem.ParentTaskId != null ? workItem.ParentTaskId.ToString() : null)),
Builders<WorkItemMongoDB>.Update.Set(r => r.PlannedWork, workItem.PlannedWork),
Builders<WorkItemMongoDB>.Update.Set(r => r.TodaysAssigned, workItem.TodaysAssigned),
Builders<WorkItemMongoDB>.Update.Set(r => r.CompletedWork, workItem.CompletedWork),
Builders<WorkItemMongoDB>.Update.Set(r => r.Description, workItem.Description),
Builders<WorkItemMongoDB>.Update.Set(r => r.TaskDate, workItem.TaskDate),
Builders<WorkItemMongoDB>.Update.Set(r => r.ExpireAt, DateTime.UtcNow.Date.AddDays(1)),
Builders<WorkItemMongoDB>.Update.Set(r => r.ActivityMaster, workItem.ActivityMaster),
Builders<WorkItemMongoDB>.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<WorkItemMongoDB>.IndexKeys.Ascending(x => x.ExpireAt);
var indexOptions = new CreateIndexOptions
{
ExpireAfter = TimeSpan.Zero // required for fixed expiration time
};
var indexModel = new CreateIndexModel<WorkItemMongoDB>(indexKeys, indexOptions);
await _taskCollection.Indexes.CreateOneAsync(indexModel);
}
}
}
public async Task<List<WorkItemMongoDB>> GetWorkItemDetailsByWorkAreaFromCache(Guid workAreaId, List<Guid> serviceIds)
{
var filterBuilder = Builders<WorkItemMongoDB>.Filter;
var filter = filterBuilder.Empty;
filter &= filterBuilder.Eq(p => p.WorkAreaId, workAreaId.ToString());
if (serviceIds.Any())
{
var stringServiceIds = serviceIds.Select(s => s.ToString()).ToList();
filter &= filterBuilder.In(w => w.ActivityMaster!.ActivityGroupMaster!.Service!.Id, stringServiceIds);
}
var options = new UpdateOptions { IsUpsert = true };
var workItems = await _taskCollection
.Find(filter)
.ToListAsync();
return workItems;
}
public async Task<WorkItemMongoDB> GetWorkItemDetailsByIdFromCache(Guid id)
{
var filter = Builders<WorkItemMongoDB>.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<bool> UpdatePlannedAndCompleteWorksInWorkItemToCache(Guid id, double plannedWork, double completedWork, double todaysAssigned)
{
var filter = Builders<WorkItemMongoDB>.Filter.Eq(p => p.Id, id.ToString());
var updates = Builders<WorkItemMongoDB>.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<bool> DeleteWorkItemByIdFromCacheAsync(Guid workItemId)
{
var filter = Builders<WorkItemMongoDB>.Filter.Eq(e => e.Id, workItemId.ToString());
var result = await _taskCollection.DeleteOneAsync(filter);
return result.DeletedCount > 0;
}
#endregion
}
}