EnVisageOnline/Main/Source/EnVisage/Code/Charts/DashboardChartManager.cs

1757 lines
77 KiB
C#

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
/// <summary>
/// Identifier ProjectTypeId for 'Other' data element
/// </summary>
protected readonly Guid C_PROJECTTYPES_OTHER_TYPE_ID = new Guid("19BCFF07-2A1C-464C-AAEB-347576E381B2");
/// <summary>
/// Identifier GoalId for 'Other' data element
/// </summary>
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<Guid> TypeId { get; set; }
public List<PieData> 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> Data { get; set; }
public IEnumerable<DateTime> FiscalCalendarWeekendings { get; set; }
public long[] FiscalCalendarChartPoints { get; set; }
public List<Guid> 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<ForecastDashboardChartCapacityModel> { row }, weekEndingMs);
}
private long GetOptimuseWeeklyHoursData(List<ForecastDashboardChartCapacityModel> 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<ForecastDashboardChartCapacityModel> { row }, weekEndingMs);
}
private long GetOptimuseWeeklyResourcesData(List<ForecastDashboardChartCapacityModel> rows, long weekEndingMs)
{
if (rows == null || rows.Count <= 0)
return 0;
return rows.Sum(x => GetOptimuseWeeklyData(x.Resources, weekEndingMs));
}
private long GetOptimuseWeeklyData(List<long[]> 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<DateTime, ForecastDashboardWeeklyCapacityModel> GetWeeklyTeamCapacity(List<Guid> teamIds, Dictionary<Guid, ExpenditureCategory> expCats, Dictionary<Guid, UOM> uoms, bool isPlanned, List<DateTime> fiscalCalendarWeekendings)
{
if (teamIds == null || teamIds.Count <= 0)
return new Dictionary<DateTime, ForecastDashboardWeeklyCapacityModel>();
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<DateTime, ForecastDashboardWeeklyCapacityModel> GetWeeklyCapacity(Guid[] sccenarioIds, Dictionary<Guid, ExpenditureCategory> expCats, Dictionary<Guid, UOM> uoms, List<DateTime> fiscalCalendarWeekendings)
{
var weeklyCapacity = new Dictionary<DateTime, ForecastDashboardWeeklyCapacityModel>();
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<DateTime, ForecastDashboardWeeklyCapacityModel> GetWeeklyPlannedCapacityAdjusted(List<Guid> scenarios, Dictionary<Guid, ExpenditureCategory> expCats,
Dictionary<Guid, UOM> uoms, List<DateTime> fiscalCalendarWeekendings)
{
Dictionary<DateTime, ForecastDashboardWeeklyCapacityModel> weeklyCapacity =
new Dictionary<DateTime, ForecastDashboardWeeklyCapacityModel>();
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<ScenarioDetailWithProxyItemModel>());
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<DateTime, ForecastDashboardWeeklyCapacityModel> GetWeeklyActualCapacityAdjusted(List<Guid> scenarios, Dictionary<Guid, ExpenditureCategory> expCats,
Dictionary<Guid, UOM> uoms, List<DateTime> fiscalCalendarWeekendings)
{
// Get direct actual capacity (with no adjustments)
Dictionary<DateTime, ForecastDashboardWeeklyCapacityModel> 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<ProjectWithChildrenView> projects, bool isLaborMode, DateTime? startDate, DateTime? endDate,
IEnumerable<Guid> projectTypesFilter, IEnumerable<Guid> strategicGoalsFilter)
{
var projectIds = projects.Select(q => q.Id).ToArray();
var expCatIds = GetExpenditureCategories(isLaborMode);
var scTypes = new List<int> { (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<Guid>();
strategicGoalsFilter = strategicGoalsFilter ?? new List<Guid>();
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<Guid>() { 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<Guid> { 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<Guid>() { 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<Guid> { 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<object> BuildChartHeader4CapacityRow(ForecastDashboardChartCapacityModel row)
{
if (row == null)
return null;
var capacityRow = row.CapacityType == ForecastDashboardChartCapacityModel.ChartCapacityType.ActualCapacity ||
row.CapacityType == ForecastDashboardChartCapacityModel.ChartCapacityType.PlannedCapacity;
var headers = new List<object>
{
new
{
label = row.Name,
data = row.Costs,
type = "Cost",
stack = !capacityRow,
lines = new {show = true, fill = !capacityRow},
color = GetLineColor(row)
},
new
{
label = row.Name,
data = row.Hours,
type = "Hours",
stack = !capacityRow,
lines = new {show = true, fill = !capacityRow},
color = GetLineColor(row)
},
new
{
label = row.Name,
data = row.Resources,
type = "Resources",
stack = !capacityRow,
lines = new {show = true, fill = !capacityRow},
color = GetLineColor(row)
}
};
return headers;
}
private IEnumerable<DonutChartDataItem> LimitChartDataAndGetOther(IEnumerable<DonutChartDataItem> data, bool byGoals, bool byCost, out IList<DonutChartDataItem> other)
{
other = new List<DonutChartDataItem>();
var filteredData = new List<DonutChartDataItem>();
var totalValue = byCost ? data.Sum(x => x.Cost ?? 0) : data.Sum(x => x.Duration ?? 0);
var totalPercentage = 1.0M;
if (totalValue > 0)
{
var orderedData = byCost ? data.OrderByDescending(x => x.Cost) : data.OrderByDescending(x => x.Duration);
foreach (var item in orderedData)
{
if (item.ChildItems != null)
{
var children = item.ChildItems.Select(x => new DonutChartDataItem
{
Cost = x.Cost,
Duration = x.Duration,
Label = x.Label,
TypeId = x.TypeId,
GroupType = x.GroupType,
});
IList<DonutChartDataItem> otherChildren;
item.ChildItems = LimitChartDataAndGetOther(children, byGoals, byCost, out otherChildren);
item.OtherChildItems = otherChildren;
}
var value = byCost ? (item.Cost ?? 0) : item.Duration;
var percentage = (value ?? 0) / totalValue;
if (totalPercentage > .3M)
{
filteredData.Add(item);
totalPercentage -= percentage;
}
else
{
other.Add(item);
}
}
if (other.Count > 2)
{
var otherItem = new DonutChartDataItem
{
Label = "Other",
TypeId = other.SelectMany(x => x.TypeId).ToList(),
Cost = other.Sum(x => x.Cost),
Duration = other.Sum(x => x.Duration),
PieDatas = other.ToList(),
GroupType = other.First().GroupType,
// Set fixed identifiers for Other data element
ProjectTypeId = !byGoals ? C_PROJECTTYPES_OTHER_TYPE_ID : (Guid?)null,
GoalId = byGoals ? C_GOALS_OTHER_GOAL_ID : (Guid?)null,
ChildItems = other.Where(x => x.ChildItems != null)
.SelectMany(x => x.ChildItems)
.GroupBy(x => x.Label)
.Select(x => new
{
Cost = x.Sum(c => (decimal)c.Cost),
Duration = x.Sum(c => (decimal)c.Duration),
x.First().Label,
x.First().GroupType,
x.First().TypeId
}),
};
filteredData.Add(otherItem);
}
else
{
other.Clear();
filteredData = orderedData.ToList();
}
}
else
{
filteredData = data.ToList();
}
return filteredData;
}
private long[] GetFiscalCalendarChartPoints_Weekly(DateTime startDate, DateTime endDate, ref DateTime firstWeekEnding, ref DateTime lastWeekEnding)
{
var weekEndings = FiscalCalendarManager.GetWeekendingsByRange(startDate, endDate, _db);
var fiscalCalendarChartPoints = weekEndings.Select(Utils.ConvertToUnixDate).ToArray();
if (weekEndings.Count > 0)
{
firstWeekEnding = weekEndings[0];
lastWeekEnding = weekEndings[weekEndings.Count - 1];
}
return fiscalCalendarChartPoints;
}
private StatisticData GetStatisticData(DateTime startDate, DateTime endDate, Guid userId, string type, List<Guid> projectStatuses,
List<Guid> projectTypes, ForecastDashboardMode mode, List<Guid> teams, List<Guid> views,
List<Guid> companies, bool isLaborMode, List<Guid> filterGroups, List<Guid> strategicGoals,
List<Guid> tags, List<Guid> clients)
{
List<DateTime> fiscalCalendarWeekendings = new List<DateTime>();
DateTime firstWeekEnding = DateTime.MinValue;
DateTime lastWeekEnding = DateTime.MinValue;
int typeInt = 0;
if (!string.IsNullOrWhiteSpace(type) && type != "null")
typeInt = (int)Enum.Parse(typeof(ScenarioType), type);
var teamManager = new TeamManager(_db);
var projectManager = new ProjectManager(_db);
#region convert incoming params to teams
var filteredTeams = teamManager.GetTeamsByUserFiltered(userId.ToString(), teams, views, companies).Select(t => t.TeamId).ToList();
#endregion
var projects = projectManager.GetProjectsWithChildrenByTeams(filteredTeams, tags).AsQueryable();
if (strategicGoals != null && strategicGoals.Count > 0)
{
var sgs = _db.StrategicGoal2Project.Where(x => strategicGoals.Contains(x.StrategicGoalId)).Select(x => x.ProjectId).ToList();
projects = projects.Where(x => sgs.Contains(x.Id));
}
var fiscalCalendarChartPoints = GetFiscalCalendarChartPoints_Weekly(startDate, endDate, ref firstWeekEnding, ref lastWeekEnding);
fiscalCalendarWeekendings.AddRange(fiscalCalendarChartPoints.Select(i => Constants.UnixEpochDate.AddMilliseconds(i)));
var isMainDashboardMode = mode == ForecastDashboardMode.MainDashboard;
var data = GetWeeksData(firstWeekEnding, lastWeekEnding, projects, projectStatuses, projectTypes, clients, typeInt, isMainDashboardMode, isLaborMode, filterGroups, filteredTeams);
return new StatisticData
{
Data = data,
FiscalCalendarWeekendings = fiscalCalendarWeekendings,
FiscalCalendarChartPoints = fiscalCalendarChartPoints,
FilteredTeams = filteredTeams,
};
}
/// <summary>
///
/// </summary>
/// <param name="firstWeekEnding"></param>
/// <param name="lastWeekEnding"></param>
/// <param name="projects"></param>
/// <param name="projectStatuses"></param>
/// <param name="projectTypes"></param>
/// <param name="clients"></param>
/// <param name="scenarioType"></param>
/// <param name="isMainDashboardMode"></param>
/// <param name="isLaborMode"></param>
/// <param name="filterGroups"></param>
/// <param name="teams"></param>
/// <returns></returns>
private IQueryable<Data> GetWeeksData(DateTime firstWeekEnding, DateTime lastWeekEnding, IQueryable<ProjectWithChildrenView> projects, List<Guid> projectStatuses, List<Guid> projectTypes, List<Guid> clients, int scenarioType, bool isMainDashboardMode, bool isLaborMode, List<Guid> filterGroups, IEnumerable<Guid> teams)
{
if (projectStatuses != null && projectStatuses.Count > 0)
projects = projects.Where(x => projectStatuses.Contains(x.StatusId));
if (projectTypes != null && projectTypes.Count > 0)
projects = projects.Where(x => projectTypes.Contains(x.TypeId));
if (clients != null && clients.Count > 0)
projects = projects.Where(x => x.ClientId.HasValue && clients.Contains(x.ClientId.Value));
var projectIds = projects.Select(p => p.Id).ToArray();
var expCatIds = isLaborMode ?
_db.ExpenditureCategory.Where(e => e.Type == (int)ExpenditureCategoryModel.CategoryTypes.Labor).Select(e => e.Id).ToArray() :
_db.ExpenditureCategory.Where(e => e.Type != (int)ExpenditureCategoryModel.CategoryTypes.Labor).Select(e => e.Id).ToArray();
var scTypes = new List<int>();
if (isMainDashboardMode)
{
scTypes.AddRange(new[] { (int)ScenarioType.Capacity, (int)ScenarioType.Vacation, (int)ScenarioType.Training });
}
if (scenarioType != 0)
scTypes.Add(scenarioType);
else
scTypes.Add((int)ScenarioType.Portfolio);
var groupsFilter = filterGroups?.ToArray() ?? new Guid[0];
var data =
from ta in _db.TeamAllocations.AsNoTracking()
join sd in _db.ScenarioDetail.AsNoTracking() on
new
{
ta.ScenarioId,
ta.ExpenditureCategoryId,
ta.WeekEndingDate
}
equals
new
{
ScenarioId = (Guid)sd.ParentID,
ExpenditureCategoryId = (Guid)sd.ExpenditureCategoryId,
WeekEndingDate = (DateTime)sd.WeekEndingDate
}
join s in _db.Scenarios.AsNoTracking() on ta.ScenarioId equals s.Id
join p in _db.Projects.AsNoTracking() on s.ParentId equals p.Id
join sp in _db.VW_ScenarioPerformance on s.Id equals sp.ForecastScenarioId into sp_joined
from sp in sp_joined.DefaultIfEmpty()
where ta.WeekEndingDate >= firstWeekEnding && ta.WeekEndingDate <= lastWeekEnding
&& projectIds.Contains(p.Id)
&& projectIds.Contains(s.ParentId.Value) && s.Status == (int?)ScenarioStatus.Active
&& teams.Contains(ta.TeamId)
&& scTypes.Contains(s.Type) && expCatIds.Contains(ta.ExpenditureCategoryId)
&& (!groupsFilter.Any() || s.Scenario2Group.Any(g => groupsFilter.Contains(g.GroupId)))
orderby ta.WeekEndingDate
select new Data
{
cost = sd.Quantity > 0 ? (ta.Quantity / sd.Quantity) * sd.Cost : 0,
quantity = ta.Quantity,
weekend = ta.WeekEndingDate,
projectStatus = p.Status.Name,
projectStatusColor = p.Status.Color,
type = s.Type,
Name = s.Name,
ScenarioBUCost = (s.UseLMMargin ?? 0) == 1 ? s.BUDirectCosts_LM : s.BUDirectCosts,
ScenarioDuration = s.Duration ?? 0,
StardDate = s.StartDate,
EndDate = s.EndDate,
ExpCatId = ta.ExpenditureCategoryId,
ProjectId = p.Id,
ProjectType = p.TypeId,
ProjectPriority = p.Priority,
ProjectName = p.Name,
ProjectColor = p.Color,
ParentProjectId = p.ParentProject != null ? p.ParentProject.Id : default(Guid?),
ParentProjectName = p.ParentProject != null ? p.ParentProject.Name : null,
ParentProjectColor = p.ParentProject != null ? p.ParentProject.Color : null,
PerformanceRedThreshold = p.PerformanceRedThreshold ?? p.Type.PerformanceRedThreshold,
PerformanceYellowThreshold = p.PerformanceYellowThreshold ?? p.Type.PerformanceYellowThreshold,
VariationPercent = sp.VariationPercent
};
return data;
}
/// <summary>
/// Returns current, next and previous fiscal periods of given type for the datepoint
/// </summary>
/// <param name="datePoint"></param>
/// <param name="periodType"></param>
/// <returns>Returns periods, if exist (-1 = prev, 0 = current, 1 = next)</returns>
private Dictionary<int, Guid> GetClosedToDateFiscalPeriods(DateTime datePoint,
FiscalCalendarModel.FiscalYearType periodType)
{
Dictionary<int, Guid> result = new Dictionary<int, Guid>();
DateTime viewRangeStartDate = datePoint.AddYears(-2);
DateTime viewRangeEndDate = datePoint.AddYears(2);
List<FiscalCalendar> periods = _db.FiscalCalendars.AsNoTracking()
.OrderBy(period => period.StartDate)
.Where(period => period.Type == (int)periodType &&
period.StartDate >= viewRangeStartDate &&
period.EndDate <= viewRangeEndDate)
.ToList();
int periodCount = periods.Count;
for (int index = 0; index < periodCount; index++)
{
var periodItem = periods[index];
if (periodItem.StartDate <= datePoint && periodItem.EndDate >= datePoint)
{
result.Add(0, periodItem.Id);
if (index > 0)
result.Add(-1, periods[index - 1].Id);
if (index + 1 < periodCount)
result.Add(1, periods[index + 1].Id);
break;
}
}
return result;
}
private Dictionary<string, StackGraphDataBlock> GetPerformanceGraphData(Guid userId, DateTime datePoint, ForecastDashboardPerformanceGraphFilterModel filter,
Func<PerformanceDataRequest, IEnumerable<BarData>> getBarDataFunc)
{
var result = new Dictionary<string, StackGraphDataBlock>();
// Get list of project, that are available to given user, filtered for given team or view
#region convert incoming params to teams
var teamManager = new TeamManager(_db);
var filteredTeams = teamManager.GetTeamsByUserFiltered(userId.ToString(), filter.Teams, filter.Views, filter.Companies).Select(t => t.TeamId);
#endregion
var projectManager = new ProjectManager(_db);
var projects = projectManager.GetProjectsWithChildrenByTeams(filteredTeams, filter.Tags).AsQueryable();
var projectFilter = projects.Select(x => x.Id).ToList();
// Get current, prev and following fiscal periods
Dictionary<int, Guid> fiscalPeriods = GetClosedToDateFiscalPeriods(datePoint, FiscalCalendarModel.FiscalYearType.Quarter);
Dictionary<int, string> resultKeys = new Dictionary<int, string> { { -1, "PrevPeriod" }, { 0, "CurrentPeriod" }, { 1, "NextPeriod" } };
for (int index = -1; index <= 1; index++)
{
if (!fiscalPeriods.ContainsKey(index))
{
continue;
}
var request = new PerformanceDataRequest
{
CurrentPeriodId = fiscalPeriods[index],
FilterGroups = filter.FilterGroups,
ProjectFilter = projectFilter,
ProjectTypes = filter.ProjectTypes,
ProjectStatuses = filter.ProjectStatuses,
Clients = filter.Clients,
Companies = filter.Companies,
Teams = filter.Teams,
Views = filter.Views,
StrategicGoals = filter.StrategicGoals,
Tags = filter.Tags
};
var projectClassRows = getBarDataFunc(request);
int rowIndex = 1;
var singleGraphDataBlock = new StackGraphDataBlock();
var chartSeriesCost = new ChartSeries();
var chartSeriesQuantity = new ChartSeries();
var projectClassRowsList = projectClassRows.ToList();
foreach (var row in projectClassRowsList.OrderByDescending(r => r.BUDirectCosts))
{
AddValuesToChartSeries(rowIndex, chartSeriesCost, row.BarTitle, row.ActualsCostVariation, row.BUDirectCosts, row.ActualsTotalCost);
rowIndex++;
}
rowIndex = 1;
foreach (var row in projectClassRowsList.OrderByDescending(r => r.ForecastTotalQuantity))
{
AddValuesToChartSeries(rowIndex, chartSeriesQuantity, row.BarTitle, row.ActualsQuantityVariation, row.ForecastTotalQuantity, row.ActualsTotalQuantity);
rowIndex++;
}
singleGraphDataBlock.series.Add(chartSeriesCost.forecastDataSerie);
singleGraphDataBlock.series.Add(chartSeriesCost.variationsPosDataSerie);
singleGraphDataBlock.series.Add(chartSeriesCost.variationsNegDataSerie);
singleGraphDataBlock.series.Add(chartSeriesCost.variationsEqDataSerie);
singleGraphDataBlock.series.Add(chartSeriesCost.actualsDataSerie);
singleGraphDataBlock.quantitySeries.Add(chartSeriesQuantity.forecastDataSerie);
singleGraphDataBlock.quantitySeries.Add(chartSeriesQuantity.variationsPosDataSerie);
singleGraphDataBlock.quantitySeries.Add(chartSeriesQuantity.variationsNegDataSerie);
singleGraphDataBlock.quantitySeries.Add(chartSeriesQuantity.variationsEqDataSerie);
singleGraphDataBlock.quantitySeries.Add(chartSeriesQuantity.actualsDataSerie);
singleGraphDataBlock.barOptions = chartSeriesCost.barTitles;
singleGraphDataBlock.barOptionsQuantity = chartSeriesQuantity.barTitles;
string dataKey = resultKeys[index];
result.Add(dataKey, singleGraphDataBlock);
}
return result;
}
private IEnumerable<BarData> GetBarDataByProject(PerformanceDataRequest request)
{
var table = _db.VW_ProjectPerformance.AsNoTracking().OrderByDescending(x => x.ProjectWeight);
var query = GetProjectPerformanceData(table, request).ToList();
var result = query.Select(x =>
new BarData
{
ActualsCostVariation = x.ActualsCostVariation,
ActualsTotalCost = x.ActualsTotalCost ?? 0,
BarTitle = x.ProjectName,
BUDirectCosts = x.BUDirectCosts ?? 0,
ActualsTotalQuantity = x.ActualsTotalQuantity,
ActualsQuantityVariation = x.ActualsQuantityVariation,
ForecastTotalQuantity = x.ForecastTotalQuantity ?? 0
}).Take(5);
return result;
}
private IEnumerable<BarData> GetBarDataByProjectType(PerformanceDataRequest request)
{
var table = _db.VW_ProjectPerformance.AsNoTracking();
var query = GetProjectPerformanceData(table, request).ToList();
var result =
(from row in query
group row by new { row.ProjectTypeId, row.ProjectTypeName } into grp
select new BarData
{
BarTitle = grp.Key.ProjectTypeName,
BUDirectCosts = grp.Sum(x => x.BUDirectCosts.Value),
ActualsCostVariation = grp.Any(x => x.ActualsCostVariation.HasValue) ? grp.Sum(x => x.ActualsCostVariation) : null,
ActualsTotalCost = grp.Any(x => x.ActualsTotalCost.HasValue) ? grp.Sum(x => x.ActualsTotalCost) : null,
ActualsTotalQuantity = grp.Any(x => x.ActualsTotalQuantity.HasValue) ? grp.Sum(x => x.ActualsTotalQuantity) : null,
ActualsQuantityVariation = grp.Any(x => x.ActualsQuantityVariation.HasValue) ? grp.Sum(x => x.ActualsQuantityVariation) : null,
ForecastTotalQuantity = grp.Sum(x => x.ForecastTotalQuantity) ?? 0,
}).OrderByDescending(x => x.BUDirectCosts).Take(5);
return result;
}
private IQueryable<ForecastDashboardProjectPerformanceModel> GetProjectPerformanceData(IQueryable<VW_ProjectPerformance> table, PerformanceDataRequest request)
{
var query =
(
from row in table
where
row.FiscalPeriodId == request.CurrentPeriodId
&& request.ProjectFilter.Contains(row.ProjectId)
&& row.BUDirectCosts.HasValue
&& (!request.FilterGroups.Any() || (row.ScenarioGroupId.HasValue && request.FilterGroups.Contains(row.ScenarioGroupId.Value)))
&& (!request.ProjectTypes.Any() || request.ProjectTypes.Contains(row.ProjectTypeId))
&& (!request.ProjectStatuses.Any() || request.ProjectStatuses.Contains(row.StatusId))
&& (!request.Clients.Any() || (row.ClientId.HasValue && request.Clients.Contains(row.ClientId.Value)))
&& (!request.StrategicGoals.Any() || (row.StrategicGoalId.HasValue && request.StrategicGoals.Contains(row.StrategicGoalId.Value)))
&& (!request.Tags.Any() || (row.TagID.HasValue && request.Tags.Contains(row.TagID.Value)))
select row
).Select(x => new ForecastDashboardProjectPerformanceModel()
{
ForecastScenarioId = x.ForecastScenarioId,
ActualsScenarioId = x.ActualsScenarioId,
ForecastScenarioType = x.ForecastScenarioType,
ForecastTotalCost = x.ForecastTotalCost,
ActualsTotalCost = x.ActualsTotalCost,
VariationPercent = x.VariationPercent,
ProjectId = x.ProjectId,
ProjectName = x.ProjectName,
Status = x.Status,
BUDirectCosts = x.BUDirectCosts,
ScenarioEndDate = x.ScenarioEndDate,
ProjectTypeId = x.ProjectTypeId,
ProjectTypeName = x.ProjectTypeName,
ProjectPriority = x.ProjectPriority,
ActualsCostForecasted = x.ActualsCostForecasted,
ActualsCostVariation = x.ActualsCostVariation,
ProjectWeight = x.ProjectWeight,
ForecastTotalQuantity = x.ForecastTotalQuantity,
ActualsTotalQuantity = x.ActualsTotalQuantity,
VariationQuantityPercent = x.VariationQuantityPercent,
ActualsQuantityVariation = x.ActualsQuantityVariation,
ScenarioStartDate = x.ScenarioStartDate,
}).Distinct();
return query;
}
private void AddValuesToChartSeries(long graphPoint, ChartSeries chartSeries, string barTitle, decimal? actualsVariation, decimal forecastValue, decimal? actualsTotal)
{
if (actualsVariation.HasValue)
{
if (actualsVariation.Value > 0)
{
// Actual costs lager Forecast costs
chartSeries.forecastDataSerie.data.AddValue(graphPoint, forecastValue);
chartSeries.variationsPosDataSerie.data.AddValue(graphPoint, forecastValue + actualsVariation.Value);
chartSeries.variationsNegDataSerie.data.AddValue(graphPoint, 0);
chartSeries.variationsEqDataSerie.data.AddValue(graphPoint, 0);
}
else if (actualsVariation.Value == 0)
{
chartSeries.forecastDataSerie.data.AddValue(graphPoint, forecastValue);
chartSeries.variationsPosDataSerie.data.AddValue(graphPoint, 0);
chartSeries.variationsNegDataSerie.data.AddValue(graphPoint, 0);
chartSeries.variationsEqDataSerie.data.AddValue(graphPoint, forecastValue + actualsVariation.Value);
}
else
{
// Actual costs less Forecast costs
chartSeries.forecastDataSerie.data.AddValue(graphPoint, forecastValue); // ActualsCostVariation is negative value
chartSeries.variationsPosDataSerie.data.AddValue(graphPoint, 0);
chartSeries.variationsNegDataSerie.data.AddValue(graphPoint, forecastValue + actualsVariation.Value);
chartSeries.variationsEqDataSerie.data.AddValue(graphPoint, 0);
}
}
else
{
// No actauls
chartSeries.forecastDataSerie.data.AddValue(graphPoint, forecastValue);
chartSeries.variationsPosDataSerie.data.AddValue(graphPoint, 0);
chartSeries.variationsNegDataSerie.data.AddValue(graphPoint, 0);
chartSeries.variationsEqDataSerie.data.AddValue(graphPoint, 0);
}
// Store actuals data to virtual serie
chartSeries.actualsDataSerie.data.AddValue(graphPoint,
actualsTotal.HasValue ? Math.Abs(actualsVariation ?? 0m) : 0);
// Store project name
chartSeries.barTitles.AddValue(graphPoint, barTitle);
}
/// <summary>
/// Fill absent weekends and normalize arrays
/// </summary>
private List<long[]> FillMissingWeeends(List<long[]> list, IEnumerable<long> weekEnds)
{
if (list.Count < weekEnds.Count())
{
var result = weekEnds.Except(list.Select(x => x[0]));
list.AddRange(result.Select(x => new[] { x, 0 }));
list.RemoveAll(x => !weekEnds.Contains(x[0]));
}
return list.OrderBy(x => x[0]).ToList();
}
#endregion
#region Public Methods
public IQueryable<Guid> GetExpenditureCategories(bool isLaborMode)
{
var result =
from e in _db.ExpenditureCategory
where
isLaborMode && e.Type == (int)ExpenditureCategoryModel.CategoryTypes.Labor ||
!isLaborMode && e.Type != (int)ExpenditureCategoryModel.CategoryTypes.Labor
select e.Id;
return result;
}
public OptimuseModel GetOptimuseData(ForecastDashboardChartModel data)
{
if (data?.ChartData == null || data.ChartData.Count <= 0)
throw new ArgumentNullException(nameof(data));
if (data.ChartData == null || data.ChartData.Count <= 0)
throw new ArgumentNullException("chart data");
if (data.WeekEndings == null)
throw new ArgumentNullException("week endings");
var result = new OptimuseModel();
var plannedCapacityRow = data.ChartData.Values.FirstOrDefault(x => x.CapacityType == ForecastDashboardChartCapacityModel.ChartCapacityType.PlannedCapacity);
var actualCapacityRow = data.ChartData.Values.FirstOrDefault(x => x.CapacityType == ForecastDashboardChartCapacityModel.ChartCapacityType.ActualCapacity);
var capacityAllocationRows = data.ChartData.Values
.Where(x => x.CapacityType != ForecastDashboardChartCapacityModel.ChartCapacityType.ActualCapacity &&
x.CapacityType != ForecastDashboardChartCapacityModel.ChartCapacityType.PlannedCapacity)
.ToList();
var systemSettingsManager = new SystemSettingsManager(_db);
var optiMUSEThreshold = systemSettingsManager.OptiMUSEThreshold();
foreach (var weekEnding in data.WeekEndings)
{
var weekEndingMs = Utils.ConvertToUnixDate(weekEnding);
var plannedCapacityResourcesValue = GetOptimuseWeeklyResourcesData(plannedCapacityRow, weekEndingMs);
var actualCapacityResourcesValue = GetOptimuseWeeklyResourcesData(actualCapacityRow, weekEndingMs);
var allocatedCapacityResourcesValue = GetOptimuseWeeklyResourcesData(capacityAllocationRows, weekEndingMs);
var plannedCapacityHoursValue = GetOptimuseWeeklyHoursData(plannedCapacityRow, weekEndingMs);
var actualCapacityHoursValue = GetOptimuseWeeklyHoursData(actualCapacityRow, weekEndingMs);
var allocatedCapacityHoursValue = GetOptimuseWeeklyHoursData(capacityAllocationRows, weekEndingMs);
var plannedCapacityResourcesResult = allocatedCapacityResourcesValue - plannedCapacityResourcesValue;
var actualCapacityResourcesResult = allocatedCapacityResourcesValue - actualCapacityResourcesValue;
var plannedCapacityHoursResult = allocatedCapacityHoursValue - plannedCapacityHoursValue;
var actualCapacityHoursResult = allocatedCapacityHoursValue - actualCapacityHoursValue;
var optiMUSEThresholdCef = optiMUSEThreshold / 100m;
var plannedOptiMUSEThreshold = plannedCapacityHoursValue * optiMUSEThresholdCef;
var actualOptiMUSEThreshold = actualCapacityHoursValue * optiMUSEThresholdCef;
result.WeekEndings.Add(weekEndingMs);
if (plannedCapacityHoursResult - plannedOptiMUSEThreshold > 0)
{
result.PlannedCapacity.OverAllocatedSummary.AddItem(weekEndingMs.ToString(), plannedCapacityHoursResult, plannedCapacityResourcesResult);
}
else if (plannedCapacityHoursResult + plannedOptiMUSEThreshold < 0)
{
result.PlannedCapacity.UnderAllocatedSummary.AddItem(weekEndingMs.ToString(), plannedCapacityHoursResult, plannedCapacityResourcesResult);
}
else
{
result.PlannedCapacity.IdealAllocatedSummary.AddItem(weekEndingMs.ToString(), plannedCapacityHoursResult, plannedCapacityResourcesResult);
}
if (actualCapacityHoursResult - actualOptiMUSEThreshold > 0)
{
result.ActualCapacity.OverAllocatedSummary.AddItem(weekEndingMs.ToString(), actualCapacityHoursResult, actualCapacityResourcesResult);
}
else if (actualCapacityHoursResult + actualOptiMUSEThreshold < 0)
{
result.ActualCapacity.UnderAllocatedSummary.AddItem(weekEndingMs.ToString(), actualCapacityHoursResult, actualCapacityResourcesResult);
}
else
{
result.ActualCapacity.IdealAllocatedSummary.AddItem(weekEndingMs.ToString(), actualCapacityHoursResult, actualCapacityResourcesResult);
}
result.PlannedCapacity.TotalHoursCapacity += plannedCapacityHoursValue;
result.PlannedCapacity.TotalResourcesCapacity += plannedCapacityResourcesValue;
result.ActualCapacity.TotalHoursCapacity += actualCapacityHoursValue;
result.ActualCapacity.TotalResourcesCapacity += actualCapacityResourcesValue;
}
return result;
}
public ProjectsByStatusChartModel GetProjectsByStatusGraphModel(ForecastDashboardChartModel data)
{
var result = new ProjectsByStatusChartModel();
if (data == null)
return result;
result.PeriodStartDate = Utils.ConvertToUnixDate(data.PeriodStartDate);
result.PeriodEndDate = Utils.ConvertToUnixDate(data.PeriodEndDate);
// Get project statuses
var context = new EnVisageEntities();
var statuses = context.Status.AsNoTracking()
.Where(x => !x.IsSystem)
.OrderByDescending(x => x.Probability100)
.Select(x => x.Name).ToList();
foreach (var statusName in statuses)
{
if (!data.ChartData.ContainsKey(statusName))
continue;
var statusHeaders = BuildChartHeader4CapacityRow(data.ChartData[statusName]);
if (statusHeaders == null || statusHeaders.Count <= 0)
continue;
result.Headers.AddRange(statusHeaders);
}
var capacityRows = data.ChartData
.Where(x => x.Value.CapacityType != ForecastDashboardChartCapacityModel.ChartCapacityType.Project)
.Select(x => x.Value)
.OrderBy(x => x.CapacityType)
.ToArray();
foreach (var row in capacityRows)
{
var capacityHeaders = BuildChartHeader4CapacityRow(row);
if (capacityHeaders == null || capacityHeaders.Count <= 0)
continue;
result.Headers.AddRange(capacityHeaders);
}
return result;
}
public IQueryable<Data> GetBubbleChartData(DateTime startDate, DateTime endDate, Guid userId, string type, List<Guid> projectStatuses,
List<Guid> projectTypes, ForecastDashboardMode mode, List<Guid> teams, List<Guid> views,
List<Guid> companies, bool isLaborMode, List<Guid> filterGroups, List<Guid> strategicGoals,
List<Guid> tags, List<Guid> clients)
{
var statisticData = GetStatisticData(startDate, endDate, userId, type, projectStatuses, projectTypes, mode, teams, views,
companies, isLaborMode, filterGroups, strategicGoals, tags, clients);
return statisticData.Data;
}
/// <summary>
/// Returns top 5 projects performance data by cost
/// </summary>
/// <param name="mode">Page display mode (general dashboard, team or view dashboard)</param>
/// <param name="userId"></param>
/// <param name="datePoint"></param>
/// <param name="filterItemId">Team or View id to filter projects</param>
/// <returns></returns>
public Dictionary<string, StackGraphDataBlock> GetPerformanceGraphDataByProjects(ForecastDashboardMode mode, Guid userId, DateTime datePoint, ForecastDashboardPerformanceGraphFilterModel filter)
{
return GetPerformanceGraphData(userId, datePoint, filter, GetBarDataByProject);
}
public ForecastDashboardChartModel GetData(DateTime startDate, DateTime endDate, Guid userId, string type, List<Guid> projectStatuses,
List<Guid> projectTypes, List<Guid> clients, ForecastDashboardMode mode, List<Guid> teams, List<Guid> views, List<Guid> companies, bool isLaborMode, List<Guid> filterGroups, List<Guid> strategicGoals, List<Guid> tags)
{
var statistic = GetStatisticData(startDate, endDate, userId, type, projectStatuses, projectTypes, mode, teams, views, companies, isLaborMode, filterGroups, strategicGoals, tags, clients);
var data = statistic.Data;
var fiscalCalendarChartPoints = statistic.FiscalCalendarChartPoints;
var result = new ForecastDashboardChartModel
{
WeekEndings = statistic.FiscalCalendarWeekendings.OrderBy(x => x).ToList()
};
if (result.WeekEndings != null && result.WeekEndings.Any())
{
var firstWeek = new FiscalCalendarManager(_db).GetFirstWeek(result.WeekEndings[0]);
if (firstWeek != null)
{
result.PeriodStartDate = firstWeek.StartDate;
result.PeriodEndDate = result.WeekEndings[result.WeekEndings.Count - 1];
}
}
var expCats = isLaborMode ?
_db.ExpenditureCategory.AsNoTracking().Where(e => e.Type == (int)ExpenditureCategoryModel.CategoryTypes.Labor).ToDictionary(e => e.Id) :
_db.ExpenditureCategory.AsNoTracking().Where(e => e.Type != (int)ExpenditureCategoryModel.CategoryTypes.Labor).ToDictionary(e => e.Id);
var uoms = _db.UOMs.AsNoTracking().ToDictionary(u => u.Id);
//init result dictionary
var nptData = new ForecastDashboardChartCapacityModel
{
Name = "Non-Project Time",
CapacityType = ForecastDashboardChartCapacityModel.ChartCapacityType.NonProjectTime
};
result.ChartData.Add("actualCapacity", new ForecastDashboardChartCapacityModel
{
Name = "Actual Capacity",
CapacityType = ForecastDashboardChartCapacityModel.ChartCapacityType.ActualCapacity
});
result.ChartData.Add("plannedCapacity", new ForecastDashboardChartCapacityModel
{
Name = "Planned Capacity",
CapacityType = ForecastDashboardChartCapacityModel.ChartCapacityType.PlannedCapacity
});
//capacity and project status data remains uninitialized as we do not know what capacity scenarios and project statuses we have
var dataList = data.GroupBy(g => g.weekend).ToDictionary(k => k.Key, d => d.ToList());
var tmpProjectStatus = new Dictionary<string, ForecastDashboardWeeklyCapacityModel>();
var weeklyPlannedCapacity = GetWeeklyTeamCapacity(statistic.FilteredTeams, expCats, uoms, true, result.WeekEndings);
var weeklyActualCapacity = GetWeeklyTeamCapacity(statistic.FilteredTeams, expCats, uoms, false, result.WeekEndings);
//var nptManager = new NonProjectTimeManager(_db);
//var npTimes = nptManager.GetNonProjectTimes4Teams(statistic.FilteredTeams, startDate, endDate);
//var allNonProjTime = new Dictionary<DateTime, List<NptWeeklyModel>>();
//foreach (var resourceNpt in npTimes.SelectMany(r => r.Value.Values))
//{
// foreach (var alloc in resourceNpt.Allocations)
// {
// var dt = Utils.ConvertFromUnixDate(Convert.ToInt64(alloc.Key));
// if (!allNonProjTime.ContainsKey(dt))
// allNonProjTime.Add(dt, new List<NptWeeklyModel>());
// var items = allNonProjTime[dt];
// var nptItem = new NptWeeklyModel
// {
// ExpenditureCategoryId = resourceNpt.ExpenditureCategoryId,
// WeekEndingDate = dt,
// HoursOff = alloc.Value,
// Cost = 0
// };
// if (resourceNpt.Costs.Count > 0 && resourceNpt.Costs.ContainsKey(alloc.Key))
// nptItem.Cost = resourceNpt.Costs[alloc.Key];
// items.Add(nptItem);
// }
//}
foreach (var weekEnding in result.WeekEndings)
{
if (dataList.ContainsKey(weekEnding))
{
foreach (var sd in dataList[weekEnding])
{
//fill data into tmps
if (!sd.type.HasValue)
continue;
if ((int)ScenarioType.Portfolio == sd.type.Value || (int)ScenarioType.Scheduling == sd.type.Value)
{
//the dataset is already filtered by correct scenario type so we're just summarizing the numbers here
if (!tmpProjectStatus.ContainsKey(sd.projectStatus))
tmpProjectStatus.Add(sd.projectStatus, new ForecastDashboardWeeklyCapacityModel());
tmpProjectStatus[sd.projectStatus].ProjectStatusColor = string.IsNullOrEmpty(sd.projectStatusColor) ?
sd.projectStatusColor : sd.projectStatusColor.IndexOf('#') == 0 ?
sd.projectStatusColor : '#' + sd.projectStatusColor;
tmpProjectStatus[sd.projectStatus].Cost += (sd.cost ?? 0);
tmpProjectStatus[sd.projectStatus].Hours += (sd.quantity ?? 0);
tmpProjectStatus[sd.projectStatus].Resources += (sd.quantity ?? 0) * Utils.GetUOMMultiplier(expCats, uoms, sd.ExpCatId ?? Guid.Empty, false);
}
else
{
throw new InvalidOperationException("Incorrect scenario type found");
}
}
}
var wkending = (long)weekEnding.Subtract(Constants.UnixEpochDate).TotalMilliseconds;
//NON-PROJECT TIME
//var npTimeWeekly = allNonProjTime.ContainsKey(weekEnding) ? allNonProjTime[weekEnding] : new List<NptWeeklyModel>();
//if (npTimeWeekly.Any())
//{
// var npTimeHours = npTimeWeekly.Sum(t => t.HoursOff);
// var npTimeResources = npTimeWeekly.Sum(t => t.HoursOff * Utils.GetUOMMultiplier(expCats, uoms, t.ExpenditureCategoryId, false));
// var npTimeCost = npTimeWeekly.Sum(t => t.Cost);
// nptData.Hours.Add(new long[] { wkending, (long)Math.Round(npTimeHours) });
// nptData.Resources.Add(new long[] { wkending, (long)Math.Round(npTimeResources) });
// nptData.Costs.Add(new long[] { wkending, (long)Math.Round(npTimeCost) });
//}
foreach (string sn in tmpProjectStatus.Keys)
{
if (!result.ChartData.ContainsKey(sn))
result.ChartData.Add(sn, new ForecastDashboardChartCapacityModel() { CapacityType = ForecastDashboardChartCapacityModel.ChartCapacityType.Project, Name = sn });
result.ChartData[sn].Color = tmpProjectStatus[sn].ProjectStatusColor;
result.ChartData[sn].Hours.Add(new[] { wkending, (long)(Math.Round(tmpProjectStatus[sn].Hours)) });
result.ChartData[sn].Resources.Add(new[] { wkending, (long)(Math.Round(tmpProjectStatus[sn].Resources)) });
result.ChartData[sn].Costs.Add(new[] { wkending, (long)(Math.Round(tmpProjectStatus[sn].Cost)) });
}
tmpProjectStatus = new Dictionary<string, ForecastDashboardWeeklyCapacityModel>();
}
if (nptData.Hours.Count > 0)
result.ChartData.Add("npTime", nptData);
result.ChartData["actualCapacity"].Costs.AddRange(weeklyActualCapacity.Select(x => new[]
{
(long)x.Key.Subtract(Constants.UnixEpochDate).TotalMilliseconds,
(long)(Math.Round(x.Value.Cost))
}));
result.ChartData["actualCapacity"].Hours.AddRange(weeklyActualCapacity.Select(x => new[]
{
(long)x.Key.Subtract(Constants.UnixEpochDate).TotalMilliseconds,
(long)(Math.Round(x.Value.Hours))
}));
result.ChartData["actualCapacity"].Resources.AddRange(weeklyActualCapacity.Select(x => new[]
{
(long)x.Key.Subtract(Constants.UnixEpochDate).TotalMilliseconds,
(long)(Math.Round(x.Value.Resources))
}));
result.ChartData["plannedCapacity"].Costs.AddRange(weeklyPlannedCapacity.Select(x => new[]
{
(long)x.Key.Subtract(Constants.UnixEpochDate).TotalMilliseconds,
(long)(Math.Round(x.Value.Cost))
}));
result.ChartData["plannedCapacity"].Hours.AddRange(weeklyPlannedCapacity.Select(x => new[]
{
(long)x.Key.Subtract(Constants.UnixEpochDate).TotalMilliseconds,
(long)(Math.Round(x.Value.Hours))
}));
result.ChartData["plannedCapacity"].Resources.AddRange(weeklyPlannedCapacity.Select(x => new[]
{
(long)x.Key.Subtract(Constants.UnixEpochDate).TotalMilliseconds,
(long)(Math.Round(x.Value.Resources))
}));
//Fill missing weekends for all items in dictionary
foreach (string key in result.ChartData.Keys)
{
result.ChartData[key].Hours = FillMissingWeeends(result.ChartData[key].Hours, fiscalCalendarChartPoints);
result.ChartData[key].Resources = FillMissingWeeends(result.ChartData[key].Resources, fiscalCalendarChartPoints);
result.ChartData[key].Costs = FillMissingWeeends(result.ChartData[key].Costs, fiscalCalendarChartPoints);
}
return result;
}
/// <summary>
/// Returns top 5 projects performance data by cost
/// </summary>
/// <param name="mode">Page display mode (general dashboard, team or view dashboard)</param>
/// <param name="userId"></param>
/// <param name="datePoint"></param>
/// <param name="filterItemId">Team or View id to filter projects</param>
/// <returns></returns>
public Dictionary<string, StackGraphDataBlock>
GetPerformanceGraphDataByProjectClass(ForecastDashboardMode mode, Guid userId, DateTime datePoint, ForecastDashboardPerformanceGraphFilterModel filter)
{
return GetPerformanceGraphData(userId, datePoint, filter, GetBarDataByProjectType);
}
public IQueryable<Data> GetWeeksDataByResourceAllocation(Scenario scenario)
{
var data =
from resAllocation in _db.PeopleResourceAllocations.AsNoTracking()
join sd in _db.ScenarioDetail.AsNoTracking() on
new
{
resAllocation.ScenarioId,
resAllocation.ExpenditureCategoryId,
resAllocation.WeekEndingDate
}
equals
new
{
ScenarioId = (Guid)sd.ParentID,
ExpenditureCategoryId = (Guid)sd.ExpenditureCategoryId,
WeekEndingDate = (DateTime)sd.WeekEndingDate
}
join p in _db.Projects.AsNoTracking() on scenario.ParentId equals p.Id
where
resAllocation.ScenarioId == scenario.Id
orderby resAllocation.WeekEndingDate
select new Data
{
cost = sd.Cost,
quantity = resAllocation.Quantity,
weekend = resAllocation.WeekEndingDate,
projectStatus = scenario.Project.Status.Name,
projectStatusColor = scenario.Project.Status.Color,
type = scenario.Type,
Name = scenario.Name,
ScenarioBUCost = (scenario.UseLMMargin ?? 0) == 1 ? scenario.BUDirectCosts_LM : scenario.BUDirectCosts,
ScenarioDuration = scenario.Duration ?? 0,
StardDate = scenario.StartDate,
EndDate = scenario.EndDate,
ExpCatId = resAllocation.ExpenditureCategoryId,
};
return data;
}
public DoubleDonutChartData GetPieData(Guid userId, ForecastDashboardNewPieChartFilterModel filter)
{
#region convert incoming params to teams
var teamManager = new TeamManager(_db);
var filteredTeams = teamManager.GetTeamsByUserFiltered(userId.ToString(), filter.Teams, filter.Views, filter.Companies).Select(t => t.TeamId);
#endregion
var projectManager = new ProjectManager(_db);
var projects = projectManager.GetProjectsWithChildrenByTeams(filteredTeams, filter.Tags, includedItems: ProjectManager.LoadProjectItems.Goals).AsQueryable();
if (filter.ProjectTypes != null && filter.ProjectTypes.Count > 0)
projects = projects.Where(i => filter.ProjectTypes.Contains(i.TypeId));
if (filter.ProjectStatuses != null && filter.ProjectStatuses.Count > 0)
projects = projects.Where(i => filter.ProjectStatuses.Contains(i.StatusId));
if (filter.Clients != null && filter.Clients.Count > 0)
projects = projects.Where(i => i.ClientId.HasValue && filter.Clients.Contains(i.ClientId.Value));
if (filter.StrategicGoals != null && filter.StrategicGoals.Count > 0)
projects = projects.Where(x => x.StrategicGoals != null && x.StrategicGoals.Any(s => filter.StrategicGoals.Contains(s)));
var donutData = GetPieChartData(projects, filter.IsLaborMode, filter.StartDate, filter.EndDate, filter.ProjectTypes, filter.StrategicGoals);
IList<DonutChartDataItem> otherGoals;
IList<DonutChartDataItem> otherProjectTypes;
var dataByCost = new DonutChartData
{
Goals = LimitChartDataAndGetOther(donutData.Goals, true, true, out otherGoals),
ProjectTypes = LimitChartDataAndGetOther(donutData.ProjectTypes, false, true, out otherProjectTypes),
OtherGoals = otherGoals,
OtherProjectTypes = otherProjectTypes
};
var dataByDuration = new DonutChartData
{
Goals = LimitChartDataAndGetOther(donutData.Goals, true, false, out otherGoals),
ProjectTypes = LimitChartDataAndGetOther(donutData.ProjectTypes, false, false, out otherProjectTypes),
OtherGoals = otherGoals,
OtherProjectTypes = otherProjectTypes
};
var result = new DoubleDonutChartData
{
CostData = dataByCost,
DurationData = dataByDuration,
TotalCost = donutData.TotalCost ?? 0,
TotalDuration = donutData.TotalDuration ?? 0,
};
return result;
}
#endregion
//private long[] GetFiscalCalendarChartPoints_Monthly(DateTime startDate, DateTime endDate, ref DateTime firstWeekEnding, ref DateTime lastWeekEnding)
//{
// var fiscalCalendarChartPoints =
// (
// from f0 in db.FiscalCalendars
// from f1 in db.FiscalCalendars
// where f0.Type == (int)FiscalCalendarModel.FiscalYearType.Week && f1.Type == (int)FiscalCalendarModel.FiscalYearType.Month &&
// f1.YearInt == f0.YearInt && f0.StartDate <= f1.EndDate && f0.EndDate >= f1.EndDate &&
// f0.EndDate >= startDate && f0.EndDate <= endDate && f0.AdjustingPeriod == false && f0.NonWorking == 0
// select f0.EndDate
// )
// .ToArray()
// .Select(t => (long)t.Subtract(Constants.UnixEpochDate).TotalMilliseconds).ToArray();
// if (fiscalCalendarChartPoints.Length > 0)
// {
// firstWeekEnding = Constants.UnixEpochDate.AddMilliseconds(fiscalCalendarChartPoints[0]);
// lastWeekEnding = Constants.UnixEpochDate.AddMilliseconds(fiscalCalendarChartPoints[fiscalCalendarChartPoints.Length - 1]);
// }
// return fiscalCalendarChartPoints;
//}
///// <summary>
/////
///// </summary>
///// <param name="startDate">Start of reporting period.</param>
///// <param name="endDate">End of reporting period.</param>
///// <param name="projects">A queryable collection of projects to filter by.</param>
///// <param name="isLaborMode">Indicates whether to load data for labor or for materials expenditure categories.</param>
///// <returns>A list of data for each classification.</returns>
//private List<PieData> GetPieChartData(DateTime startDate, DateTime endDate, IQueryable<Project> projects, bool isLaborMode, List<Guid> filterGroups, bool chartModeCost)
//{
// var projectIds = (from p in projects select p.Id).ToArray();
// var expCatIds = isLaborMode
// ? (from e in _db.ExpenditureCategory
// where e.Type == (int)ExpenditureCategoryModel.CategoryTypes.Labor
// select e.Id).ToArray()
// : (from e in _db.ExpenditureCategory
// where e.Type != (int)ExpenditureCategoryModel.CategoryTypes.Labor
// select e.Id).ToArray();
// var scTypes = new List<int> { (int)ScenarioType.Portfolio, (int)ScenarioType.Scheduling };
// var types = _db.Types.AsNoTracking().ToDictionary(key => key.Id, elem => elem.Name);
// var query = _db.Scenarios.AsNoTracking().Where(x => x.Status == (int?)ScenarioStatus.Active);
// if (filterGroups != null && filterGroups.Count > 0)
// query = query.Where(x => filterGroups.Contains(x.Scenario2Group.FirstOrDefault().GroupId));
// var data = (from sd in _db.ScenarioDetail
// join s in query on sd.ParentID equals s.Id
// join p in _db.Projects on s.ParentId equals p.Id
// //where !(s.StartDate <= StartDate || s.EndDate >= EndDate)
// where sd.WeekEndingDate >= startDate && sd.WeekEndingDate <= endDate
// && projectIds.Contains(s.ParentId.Value)
// && scTypes.Contains(s.Type) && expCatIds.Contains(sd.ExpenditureCategoryId.Value)
// select new
// {
// s.Id,
// s.UseLMMargin,
// s.BUDirectCosts_LM,
// s.BUDirectCosts,
// s.Duration,
// p.TypeId,
// }).Distinct().ToArray().GroupBy(group => group.TypeId).Select(grouping => new PieData
// {
// TypeId = new List<Guid>() { grouping.Key },
// Value = (chartModeCost) ? grouping.Sum(x => (x.UseLMMargin ?? 0) == 1 ? x.BUDirectCosts_LM ?? 0 : x.BUDirectCosts ?? 0) : grouping.Sum(x => x.Duration ?? 0),
// Label = types.ContainsKey(grouping.Key) ? types[grouping.Key] : string.Empty
// }).ToList();
// return data;
//}
//private List<PieData> GetPieChartGoalsData(DateTime startDate, DateTime endDate, IQueryable<Project> projects, bool isLaborMode, List<Guid> filterGroups, List<Guid> strategicGoals, bool chartModeCost)
//{
// var projectIds = (from p in projects select p.Id).ToArray();
// var expCatIds = isLaborMode
// ? (from e in _db.ExpenditureCategory
// where e.Type == (int)ExpenditureCategoryModel.CategoryTypes.Labor
// select e.Id).ToArray()
// : (from e in _db.ExpenditureCategory
// where e.Type != (int)ExpenditureCategoryModel.CategoryTypes.Labor
// select e.Id).ToArray();
// var scTypes = new List<int> { (int)ScenarioType.Portfolio, (int)ScenarioType.Scheduling };
// var goals = _db.StrategicGoals.AsNoTracking().ToDictionary(key => key.Id, elem => elem.Name);
// var query = _db.Scenarios.AsNoTracking().Where(x => x.Status == (int?)ScenarioStatus.Active && scTypes.Contains(x.Type));
// if (filterGroups != null && filterGroups.Count > 0)
// query = query.Where(x => filterGroups.Contains(x.Scenario2Group.FirstOrDefault().GroupId));
// var data = (from sd in _db.ScenarioDetail
// join s in query on sd.ParentID equals s.Id
// join p in _db.Projects on s.ParentId equals p.Id
// join sg in _db.StrategicGoal2Project on p.Id equals sg.ProjectId
// where sd.WeekEndingDate >= startDate && sd.WeekEndingDate <= endDate
// && projectIds.Contains(s.ParentId.Value)
// && expCatIds.Contains(sd.ExpenditureCategoryId.Value)
// select new
// {
// s.Id,
// s.UseLMMargin,
// s.BUDirectCosts_LM,
// s.BUDirectCosts,
// s.Duration,
// p.TypeId,
// sg.StrategicGoalId
// }).Distinct().ToArray().GroupBy(group => group.StrategicGoalId).Select(grouping => new PieData
// {
// TypeId = new List<Guid>() { grouping.Key },
// Value = (chartModeCost) ? grouping.Sum(x => (x.UseLMMargin ?? 0) == 1 ? x.BUDirectCosts_LM ?? 0 : x.BUDirectCosts ?? 0) : grouping.Sum(x => x.Duration ?? 0),
// Label = goals.ContainsKey(grouping.Key) ? goals[grouping.Key] : string.Empty
// }).ToList();
// var projectsWithGoals = _db.StrategicGoal2Project.Select(x => x.ProjectId).Distinct().ToArray();
// if (!strategicGoals.Any())
// data.AddRange((from sd in _db.ScenarioDetail
// join s in query on sd.ParentID equals s.Id
// join p in _db.Projects on s.ParentId equals p.Id
// where !projectsWithGoals.Contains(p.Id) && sd.WeekEndingDate >= startDate && sd.WeekEndingDate <= endDate
// && projectIds.Contains(s.ParentId.Value)
// && expCatIds.Contains(sd.ExpenditureCategoryId.Value)
// select new
// {
// s.Id,
// s.UseLMMargin,
// s.BUDirectCosts_LM,
// s.BUDirectCosts,
// s.Duration,
// p.TypeId
// }).Distinct().ToArray().GroupBy(group => 0).Select(grouping => new PieData
// {
// TypeId = new List<Guid>() { Guid.Empty },
// Value = (chartModeCost) ? grouping.Sum(x => (x.UseLMMargin ?? 0) == 1 ? x.BUDirectCosts_LM ?? 0 : x.BUDirectCosts ?? 0) : grouping.Sum(x => x.Duration ?? 0),
// Label = "No Goal"
// }).ToList());
// else data = data.Where(x => strategicGoals.Contains(x.TypeId[0])).ToList();
// return data;
//}
///// <summary>
/////
///// </summary>
///// <param name="startDate">Start of reporting period.</param>
///// <param name="endDate">End of reporting period.</param>
///// <param name="projects">A queryable collection of projects to filter by.</param>
///// <param name="isLaborMode">Indicates whether to load data for labor or for materials expenditure categories.</param>
///// <returns>A list of data for each classification.</returns>
//private List<PieData> GetPieChartData(IQueryable<Project> projects, bool isLaborMode, bool chartModeCost)
//{
// var projectIds = (from p in projects select p.Id).ToArray();
// var expCatIds = isLaborMode
// ? (from e in _db.ExpenditureCategory
// where e.Type == (int)ExpenditureCategoryModel.CategoryTypes.Labor
// select e.Id).ToArray()
// : (from e in _db.ExpenditureCategory
// where e.Type != (int)ExpenditureCategoryModel.CategoryTypes.Labor
// select e.Id).ToArray();
// var scTypes = new List<int> { (int)ScenarioType.Portfolio, (int)ScenarioType.Scheduling };
// var types = _db.Types.AsNoTracking().ToDictionary(key => key.Id, elem => elem.Name);
// var query = _db.Scenarios.AsNoTracking().Where(x => x.Status == (int?)ScenarioStatus.Active);
// var data = (from sd in _db.ScenarioDetail
// join s in query on sd.ParentID equals s.Id
// join p in _db.Projects on s.ParentId equals p.Id
// where projectIds.Contains(s.ParentId.Value)
// && scTypes.Contains(s.Type) && expCatIds.Contains(sd.ExpenditureCategoryId.Value)
// select new
// {
// s.Id,
// s.UseLMMargin,
// s.BUDirectCosts_LM,
// s.BUDirectCosts,
// s.Duration,
// p.TypeId,
// }).Distinct().ToArray().GroupBy(group => group.TypeId).Select(grouping => new PieData
// {
// TypeId = new List<Guid>() { grouping.Key },
// Value = (chartModeCost) ? grouping.Sum(x => (x.UseLMMargin ?? 0) == 1 ? x.BUDirectCosts_LM ?? 0 : x.BUDirectCosts ?? 0) : grouping.Sum(x => x.Duration ?? 0),
// Label = types.ContainsKey(grouping.Key) ? types[grouping.Key] : string.Empty
// }).ToList();
// return data;
//}
//private List<PieData> GetPieChartGoalsData(IQueryable<Project> projects, bool isLaborMode, bool chartModeCost)
//{
// var projectIds = (from p in projects select p.Id).ToArray();
// var expCatIds = isLaborMode
// ? (from e in _db.ExpenditureCategory
// where e.Type == (int)ExpenditureCategoryModel.CategoryTypes.Labor
// select e.Id).ToArray()
// : (from e in _db.ExpenditureCategory
// where e.Type != (int)ExpenditureCategoryModel.CategoryTypes.Labor
// select e.Id).ToArray();
// var scTypes = new List<int> { (int)ScenarioType.Portfolio, (int)ScenarioType.Scheduling };
// var scenarios = _db.Scenarios.AsNoTracking().Where(x => x.Status == (int?)ScenarioStatus.Active && scTypes.Contains(x.Type));
// var data =
// from sd in _db.ScenarioDetail.AsNoTracking()
// join s in scenarios on sd.ParentID equals s.Id
// 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()
// where
// projectIds.Contains(s.ParentId.Value)
// && expCatIds.Contains(sd.ExpenditureCategoryId.Value)
// select new
// {
// s.Id,
// s.UseLMMargin,
// s.BUDirectCosts_LM,
// s.BUDirectCosts,
// s.Duration,
// p.TypeId,
// StrategicGoalId = sg != null ? sg.Id : Guid.Empty,
// StrategicGoalName = sg != null ? sg.Name : "No Goal"
// };
// var result = data
// .Distinct()
// .GroupBy(x => new { x.StrategicGoalId, x.StrategicGoalName })
// .Select(group => new PieData
// {
// TypeId = new List<Guid>() { group.Key.StrategicGoalId },
// Value = (chartModeCost) ? group.Sum(x => (x.UseLMMargin ?? 0) == 1 ? x.BUDirectCosts_LM ?? 0 : x.BUDirectCosts ?? 0) : group.Sum(x => x.Duration ?? 0),
// Label = group.Key.StrategicGoalName
// });
// return result.ToList();
//}
}
#region Dashboard performance graph
public class StackGraphSerieData : List<List<object>>
{
public void AddValue(long tickValue, decimal dataValue)
{
List<object> point = new List<object> {tickValue, dataValue};
this.Add(point);
}
}
public class StackGraphSerie
{
public StackGraphSerieData data;
public StackGraphSerie()
{
data = new StackGraphSerieData();
}
}
public class StackGraphBarOptions : List<List<object>>
{
public void AddValue(long tickValue, string barTitle)
{
List<object> option = new List<object> {tickValue, barTitle};
this.Add(option);
}
}
public class StackGraphDataBlock
{
public List<StackGraphSerie> series;
public List<StackGraphSerie> quantitySeries;
public StackGraphBarOptions barOptions;
public StackGraphBarOptions barOptionsQuantity;
public StackGraphDataBlock()
{
series = new List<StackGraphSerie>();
quantitySeries = new List<StackGraphSerie>();
}
}
class ChartSeries
{
public StackGraphSerie forecastDataSerie { get; private set; }
public StackGraphSerie variationsPosDataSerie { get; private set; }
public StackGraphSerie variationsNegDataSerie { get; private set; }
public StackGraphSerie variationsEqDataSerie { get; private set; }
public StackGraphSerie actualsDataSerie { get; private set; }
public StackGraphBarOptions barTitles { get; private set; }
public ChartSeries()
{
forecastDataSerie = new StackGraphSerie();
variationsPosDataSerie = new StackGraphSerie();
variationsNegDataSerie = new StackGraphSerie();
variationsEqDataSerie = new StackGraphSerie();
actualsDataSerie = new StackGraphSerie();
barTitles = new StackGraphBarOptions();
}
}
class PerformanceDataRequest
{
public PerformanceDataRequest()
{
FilterGroups = new List<Guid>();
ProjectTypes = new List<Guid>();
ProjectFilter = new List<Guid>();
Views = new List<Guid>();
Teams = new List<Guid>();
Companies = new List<Guid>();
Clients = new List<Guid>();
StrategicGoals = new List<Guid>();
Tags = new List<Guid>();
}
public Guid CurrentPeriodId { get; set; }
public IEnumerable<Guid> FilterGroups { get; set; }
public IEnumerable<Guid> ProjectTypes { get; set; }
public IEnumerable<Guid> ProjectStatuses { get; set; }
public IEnumerable<Guid> ProjectFilter { get; set; }
public IEnumerable<Guid> Teams { get; set; }
public IEnumerable<Guid> Views { get; set; }
public IEnumerable<Guid> Companies { get; set; }
public IEnumerable<Guid> StrategicGoals { get; internal set; }
public IEnumerable<Guid> Tags { get; set; }
public IEnumerable<Guid> Clients { get; set; }
}
class BarData
{
public string BarTitle { get; set; }
public decimal? ActualsCostVariation { get; set; }
public decimal BUDirectCosts { get; set; }
public decimal? ActualsTotalCost { get; set; }
public decimal? ActualsTotalQuantity { get; internal set; }
public decimal? ActualsQuantityVariation { get; internal set; }
public decimal ForecastTotalQuantity { get; internal set; }
}
#endregion
}