using EnVisage.Code.BLL;
using EnVisage.Models;
using EnVisage.Properties;
using System;
using System.Collections.Generic;
using System.Linq;
namespace EnVisage.Code.Charts
{
public class DashboardChartManager: IDisposable
{
#region Properties
///
/// Identifier ProjectTypeId for 'Other' data element
///
protected readonly Guid C_PROJECTTYPES_OTHER_TYPE_ID = new Guid("19BCFF07-2A1C-464C-AAEB-347576E381B2");
///
/// Identifier GoalId for 'Other' data element
///
protected readonly Guid C_GOALS_OTHER_GOAL_ID = new Guid("CC8114A0-94DC-4641-908C-4143C2568098");
readonly EnVisageEntities _db;
#endregion
#region Models
public class Data
{
public decimal? cost { get; set; }
public decimal? quantity { get; set; }
public DateTime? weekend { get; set; }
public string projectStatus { get; set; }
public string projectStatusColor { get; set; }
public int? type { get; set; }
public string Name { get; set; }
public Guid? ExpCatId { get; set; }
public Guid ProjectId { get; set; }
public Guid ProjectType { get; set; }
public decimal? ScenarioBUCost { get; set; }
public int ScenarioDuration { get; set; }
public decimal ProjectPriority { get; internal set; }
public string ProjectName { get; internal set; }
public DateTime? StardDate { get; internal set; }
public DateTime? EndDate { get; internal set; }
public string ProjectColor { get; internal set; }
public decimal? PerformanceRedThreshold { get; internal set; }
public decimal? PerformanceYellowThreshold { get; internal set; }
public string ParentProjectColor { get; internal set; }
public decimal? VariationPercent { get; internal set; }
public Guid? ParentProjectId { get; internal set; }
public string ParentProjectName { get; internal set; }
}
public class PieData
{
public List TypeId { get; set; }
public List PieDatas { get; set; }
public string Label { get; set; }
public decimal Value { get; set; }
public string PresetColor { get; set; }
}
internal struct NptWeeklyModel
{
public DateTime WeekEndingDate { get; set; }
public decimal HoursOff { get; set; }
public Guid ExpenditureCategoryId { get; set; }
public decimal Cost { get; set; }
}
public class StatisticData
{
public IQueryable Data { get; set; }
public IEnumerable FiscalCalendarWeekendings { get; set; }
public long[] FiscalCalendarChartPoints { get; set; }
public List FilteredTeams { get; set; }
}
#endregion
#region Constructors
public DashboardChartManager()
{
_db = new EnVisageEntities();
}
public void Dispose()
{
_db?.Dispose();
}
#endregion
#region Private Methods
#region Optimuse Private Methods
private long GetOptimuseWeeklyHoursData(ForecastDashboardChartCapacityModel row, long weekEndingMs)
{
return row == null ? 0 : GetOptimuseWeeklyHoursData(new List { row }, weekEndingMs);
}
private long GetOptimuseWeeklyHoursData(List rows, long weekEndingMs)
{
if (rows == null || rows.Count <= 0)
return 0;
return rows.Sum(x => GetOptimuseWeeklyData(x.Hours, weekEndingMs));
}
private long GetOptimuseWeeklyResourcesData(ForecastDashboardChartCapacityModel row, long weekEndingMs)
{
return row == null ? 0 : GetOptimuseWeeklyResourcesData(new List { row }, weekEndingMs);
}
private long GetOptimuseWeeklyResourcesData(List rows, long weekEndingMs)
{
if (rows == null || rows.Count <= 0)
return 0;
return rows.Sum(x => GetOptimuseWeeklyData(x.Resources, weekEndingMs));
}
private long GetOptimuseWeeklyData(List rowData, long weekEndingMs)
{
if (rowData == null || rowData.Count <= 0)
return 0;
var weeklyData = rowData.FirstOrDefault(x => x != null && x.Length == 2 && x[0] == weekEndingMs);
if (weeklyData != null)
return weeklyData[1];
return 0;
}
#endregion
private Dictionary GetWeeklyTeamCapacity(List teamIds, Dictionary expCats, Dictionary uoms, bool isPlanned, List fiscalCalendarWeekendings)
{
if (teamIds == null || teamIds.Count <= 0)
return new Dictionary();
var context = new EnVisageEntities();
var scenarios = isPlanned ?
context.Teams.AsNoTracking()
.Where(s => teamIds.Contains(s.Id) && s.PlannedCapacityScenarioId != null)
.Select(s => s.PlannedCapacityScenarioId.Value).ToArray() :
context.Teams.AsNoTracking()
.Where(s => teamIds.Contains(s.Id) && s.ActualCapacityScenarioId != null)
.Select(s => s.ActualCapacityScenarioId.Value).ToArray();
var scenarioIds = scenarios.ToList();
var result = isPlanned ?
GetWeeklyPlannedCapacityAdjusted(scenarioIds, expCats, uoms, fiscalCalendarWeekendings) :
GetWeeklyActualCapacityAdjusted(scenarioIds, expCats, uoms, fiscalCalendarWeekendings);
return result;
}
private Dictionary GetWeeklyCapacity(Guid[] sccenarioIds, Dictionary expCats, Dictionary uoms, List fiscalCalendarWeekendings)
{
var weeklyCapacity = new Dictionary();
var context = new EnVisageEntities();
var expCatIds = expCats.Keys.ToArray();
var sds = (from sd in context.ScenarioDetail.AsNoTracking()
where sccenarioIds.Contains(sd.ParentID.Value) &&
fiscalCalendarWeekendings.Contains(sd.WeekEndingDate.Value) &&
expCatIds.Contains(sd.ExpenditureCategoryId.Value) &&
sd.ExpenditureCategoryId.HasValue
select sd).ToArray();
// fill dictionary with weekly capacity for each week
foreach (var week in fiscalCalendarWeekendings)
{
if (!weeklyCapacity.ContainsKey(week))
{
var weekCapacityModel = new ForecastDashboardWeeklyCapacityModel();
var weeklyDetails = sds.Where(s => s.WeekEndingDate == week).ToList();
foreach (var item in weeklyDetails)
{
weekCapacityModel.Hours += item.Quantity ?? 0;
weekCapacityModel.Resources += (item.Quantity ?? 0) * Utils.GetUOMMultiplier(expCats, uoms, item.ExpenditureCategoryId.Value, false);
weekCapacityModel.Cost += item.Cost ?? 0;
}
weeklyCapacity.Add(week, weekCapacityModel);
}
}
return weeklyCapacity;
}
private Dictionary GetWeeklyPlannedCapacityAdjusted(List scenarios, Dictionary expCats,
Dictionary uoms, List fiscalCalendarWeekendings)
{
Dictionary weeklyCapacity =
new Dictionary();
if (fiscalCalendarWeekendings == null)
throw new ArgumentNullException(nameof(fiscalCalendarWeekendings));
if (fiscalCalendarWeekendings.Count < 1)
return weeklyCapacity;
ScenarioManager scMngr = new ScenarioManager(_db);
DateTime startDateWe = fiscalCalendarWeekendings.Min();
DateTime endDateWe = fiscalCalendarWeekendings.Max();
var expCatIds = expCats.Keys.ToList();
var planCapacityAdjusted = scMngr.GetPlanningCapacityAdjusted(scenarios.ToList(), expCatIds, startDateWe, endDateWe);
var capacityPrepared =
fiscalCalendarWeekendings.ToDictionary(k => k, v => new List());
planCapacityAdjusted.Values.ToList().ForEach(x => x.Values.ToList()
.ForEach(z => z.RemoveAll(model =>
!model.WeekEndingDate.HasValue || !fiscalCalendarWeekendings.Contains(model.WeekEndingDate.Value))));
planCapacityAdjusted.Values.ToList().ForEach(x => x.Values.ToList()
.ForEach(z => z.ForEach(model => capacityPrepared[model.WeekEndingDate.Value].Add(model))));
weeklyCapacity = fiscalCalendarWeekendings.Select(we =>
new
{
Weekending = we,
Data = new ForecastDashboardWeeklyCapacityModel()
{
Hours = capacityPrepared.ContainsKey(we) && capacityPrepared[we] != null && (capacityPrepared[we].Count > 0)
? capacityPrepared[we].Select(model => model.Quantity).Sum() : 0,
Cost = capacityPrepared.ContainsKey(we) && capacityPrepared[we] != null && (capacityPrepared[we].Count > 0)
? capacityPrepared[we].Select(model => model.Cost).Sum() : 0,
Resources = capacityPrepared.ContainsKey(we) && capacityPrepared[we] != null && (capacityPrepared[we].Count > 0)
? capacityPrepared[we].Select(model =>
model.Quantity * Utils.GetUOMMultiplier(expCats, uoms, model.ExpenditureCategoryId, false)).Sum() : 0,
}
}).ToDictionary(k => k.Weekending, v => v.Data);
return weeklyCapacity;
}
private Dictionary GetWeeklyActualCapacityAdjusted(List scenarios, Dictionary expCats,
Dictionary uoms, List fiscalCalendarWeekendings)
{
// Get direct actual capacity (with no adjustments)
Dictionary result =
GetWeeklyCapacity(scenarios.ToArray(), expCats, uoms, fiscalCalendarWeekendings);
// Get holiday adjustments
var expCatIds = expCats.Keys.ToList();
var adjRecordsByWeekendings = _db.VW_ActualCapacityAdjustmentByExpCats.AsNoTracking()
.Where(x => scenarios.Contains(x.ActualCapacityScenarioId) &&
expCatIds.Contains(x.ExpenditureCategoryId) &&
fiscalCalendarWeekendings.Contains(x.WeekEndingDate))
.GroupBy(x => x.WeekEndingDate)
.ToDictionary(k => k.Key, grp => new ScenarioDetailWithProxyItemModel()
{
WeekEndingDate = grp.Key,
ExpenditureCategoryId = grp.Any() ? grp.FirstOrDefault().ExpenditureCategoryId : Guid.Empty,
Quantity = grp.Where(z => z.ResourcesTotalOffHrs.HasValue).Select(z => z.ResourcesTotalOffHrs.Value).Sum(),
Cost = grp.Where(z => z.ResourcesTotalOffCost.HasValue).Select(z => z.ResourcesTotalOffCost.Value).Sum(),
});
// Applying holiday corrections
foreach (DateTime we in result.Keys)
{
if (adjRecordsByWeekendings.ContainsKey(we))
{
var capacityItem = result[we];
var adjItem = adjRecordsByWeekendings[we];
result[we].Hours = capacityItem.Hours >= adjItem.Quantity ? (capacityItem.Hours - adjItem.Quantity) : 0;
result[we].Cost = capacityItem.Cost >= adjItem.Cost ? (capacityItem.Cost - adjItem.Cost) : 0;
result[we].Resources = capacityItem.Hours >= adjItem.Quantity
? capacityItem.Resources - adjItem.Quantity * Utils.GetUOMMultiplier(expCats, uoms, adjItem.ExpenditureCategoryId, false) : 0;
}
}
return result;
}
private DonutChartData GetPieChartData(
IQueryable projects, bool isLaborMode, DateTime? startDate, DateTime? endDate,
IEnumerable projectTypesFilter, IEnumerable strategicGoalsFilter)
{
var projectIds = projects.Select(q => q.Id).ToArray();
var expCatIds = GetExpenditureCategories(isLaborMode);
var scTypes = new List { (int)ScenarioType.Portfolio };
var scenarios = _db.Scenarios.AsNoTracking().Where(x => x.Status == (int?)ScenarioStatus.Active && scTypes.Contains(x.Type) && x.ParentId.HasValue);
var scenDetailParentIds = from sd in _db.ScenarioDetail.AsNoTracking()
join s in scenarios on sd.ParentID.Value equals s.Id
where expCatIds.Contains(sd.ExpenditureCategoryId.Value)
select sd.ParentID;
var scenariosList = scenarios.Where(q => scenDetailParentIds.Contains(q.Id));
projectTypesFilter = projectTypesFilter ?? new List();
strategicGoalsFilter = strategicGoalsFilter ?? new List();
var query =
(from s in scenariosList
//join sd in db.ScenarioDetail on s.Id equals sd.ParentID
join p in _db.Projects on s.ParentId equals p.Id
join sg2project in _db.StrategicGoal2Project on p.Id equals sg2project.ProjectId into sg2project_joined
from sg2project in sg2project_joined.DefaultIfEmpty()
join sg in _db.StrategicGoals.AsNoTracking() on sg2project.StrategicGoalId equals sg.Id into
sg_joined
from sg in sg_joined.DefaultIfEmpty()
join projectType in _db.Types on p.TypeId equals projectType.Id
where
projectIds.Contains(s.ParentId.Value)
//&& expCatIds.Contains(sd.ExpenditureCategoryId.Value)
&&
((!startDate.HasValue || s.EndDate >= startDate) &&
(!endDate.HasValue || s.StartDate <= endDate))
select new
{
ScenarioId = s.Id,
s.Duration,
Cost = (s.UseLMMargin ?? 0) == 1 ? s.BUDirectCosts_LM : s.BUDirectCosts,
TypeId = projectType.Id,
TypeName = projectType.Name,
StrategicGoalId = sg != null ? sg.Id : Guid.Empty,
StrategicGoalName = sg != null ? sg.Name : "No Goal"
}).Distinct();
var projectTypes = query
.GroupBy(x => new { x.TypeId, x.TypeName })
.Select(group => new DonutChartDataItem
{
ProjectTypeId = group.Key.TypeId,
TypeId = new List() { group.Key.TypeId },
Cost = group.Sum(x => x.Cost),
Duration = group.Sum(x => x.Duration),
Label = group.Key.TypeName,
ChildItems = group.GroupBy(pt => new { pt.StrategicGoalId, pt.StrategicGoalName }).Select(pt => new
{
Cost = pt.Sum(x => x.Cost),
Duration = pt.Sum(x => x.Duration),
Label = pt.Key.StrategicGoalName,
TypeId = new List { pt.Key.StrategicGoalId },
GroupType = "Goal"
}),
GroupType = "ProjectType"
});
if (projectTypesFilter.Any())
{
projectTypes = projectTypes.Where(x => x.ProjectTypeId.HasValue && projectTypesFilter.Contains(x.ProjectTypeId.Value));
}
var goals = query
.GroupBy(x => new { x.StrategicGoalId, x.StrategicGoalName })
.Select(group => new DonutChartDataItem
{
GoalId = group.Key.StrategicGoalId,
TypeId = new List() { group.Key.StrategicGoalId },
Cost = group.Sum(x => x.Cost),
Duration = group.Sum(x => x.Duration),
Label = group.Key.StrategicGoalName,
ChildItems = group.GroupBy(pt => new { pt.TypeId, pt.TypeName }).Select(pt => new
{
Cost = pt.Sum(x => x.Cost),
Duration = pt.Sum(x => x.Duration),
Label = pt.Key.TypeName,
TypeId = new List { pt.Key.TypeId },
GroupType = "ProjectType"
}),
GroupType = "Goal"
});
if (strategicGoalsFilter.Any())
{
goals = goals.Where(x => x.GoalId.HasValue && strategicGoalsFilter.Contains(x.GoalId.Value));
}
var result = new DonutChartData
{
Goals = goals.ToList(),
ProjectTypes = projectTypes.ToList(),
TotalCost = query.GroupBy(customer => customer.ScenarioId).Select(group => group.FirstOrDefault()).Sum(x => x.Cost), //TODO: check sum in projectTypes
TotalDuration = query.GroupBy(customer => customer.ScenarioId).Select(group => group.FirstOrDefault()).Sum(x => x.Duration), //TODO: check sum in projectTypes
};
return result;
}
private string GetLineColor(ForecastDashboardChartCapacityModel row)
{
switch (row.CapacityType)
{
case ForecastDashboardChartCapacityModel.ChartCapacityType.Project:
return string.IsNullOrEmpty(row.Color) ? null : row.Color;
//case ForecastDashboardChartCapacityModel.ChartCapacityType.NonProjectTime:
// break;
case ForecastDashboardChartCapacityModel.ChartCapacityType.ActualCapacity:
return Settings.Default.ActualCapacityColor;
case ForecastDashboardChartCapacityModel.ChartCapacityType.PlannedCapacity:
return Settings.Default.PlannedCapacityColor;
}
return null;
}
private List