EnVisageOnline/Main/Source/EnVisage/Controllers/ForecastGridController.cs

241 lines
9.6 KiB
C#

using EnVisage.Code;
using EnVisage.Code.ForecastDashboard;
using EnVisage.Models;
using Kendo.Mvc;
using Kendo.Mvc.UI;
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Linq.Dynamic;
using System.Web.Mvc;
namespace EnVisage.Controllers
{
public class ForecastGridController : BaseController
{
#region Actions
[HttpPost]
public JsonResult GetProjectsSummary(ForecastDashboardFilterModel forecastFilter)
{
var userId = SecurityManager.GetUserPrincipal();
var manager = new ForecastDashboardManager(DbContext);
int totalRecordCount;
int searchRecordCount;
var scenarios = manager.GetScenarios(userId, out totalRecordCount, out searchRecordCount, "", forecastFilter);
var resourceAvailability = manager.GetResourceAvailability(forecastFilter, userId);
var numberOfProjects = scenarios.Count();
var projectWeeks = scenarios.Sum(x => x.Duration);
var totalRevenue = scenarios.Sum(x => x.ProjectedRevenue);
var totalCostSavings = scenarios.Sum(x => x.CostSavings);
var totalBUCost = scenarios.Sum(s => (s.UseLMMargin ?? 0) == 1 ? s.BUDirectCosts_LM : s.BUDirectCosts);
var totalResourceAvailability = Math.Round(resourceAvailability.Sum(q => q.ResourceAvailability.Value) / resourceAvailability.Sum(q => q.AvailableHours.Value) * 100);
var totalNetImpact = scenarios.Sum(x => x.ProjectNetImpact);
totalResourceAvailability = double.IsNaN(totalResourceAvailability) ? 0 : totalResourceAvailability;
var summary = new
{
numberOfProjects,
projectWeeks,
totalRevenue,
totalCostSavings,
totalBUCost,
totalNetImpact,
totalResourceAvailability
};
return Json(summary);
}
[HttpPost]
public JsonResult GetScenarios([DataSourceRequest]Kendo.Mvc.UI.DataSourceRequest request, ForecastDashboardFilterModel forecastFilter)
{
int totalRecordCount;
int searchRecordCount;
var manager = new ForecastDashboardManager(DbContext);
var userId = SecurityManager.GetUserPrincipal();
var scenarios = manager.GetScenarios(userId, out totalRecordCount, out searchRecordCount, "", forecastFilter);
var distinct = scenarios.GroupBy(x => x.Id)
.Select(x => x.FirstOrDefault())
.ToList();
var gridData = GetGridData(request, scenarios, distinct);
return Json(gridData, JsonRequestBehavior.AllowGet);
}
[HttpPost]
public JsonResult GetResourceAvailability([DataSourceRequest]Kendo.Mvc.UI.DataSourceRequest request, ForecastDashboardFilterModel forecastFilter)
{
var manager = new ForecastDashboardManager(DbContext);
var userId = SecurityManager.GetUserPrincipal();
var data = manager.GetResourceAvailability(forecastFilter, userId);
var gridData = GetGridData(request, data, data);
return Json(gridData, JsonRequestBehavior.AllowGet);
}
[HttpPost]
public ActionResult ExportResourceAvailability(string contentType, string base64, string fileName)
{
var fileContents = Convert.FromBase64String(base64);
return File(fileContents, contentType, fileName);
}
#endregion
#region Private Methods
private object GetGridData<T>([DataSourceRequest]Kendo.Mvc.UI.DataSourceRequest request, IEnumerable<T> collection, IEnumerable<T> distinctCollection)
{
var ordering = string.Empty;
if (request.Groups != null && request.Groups.Any())
{
ordering = string.Join(",", request.Groups.Select(GetOrderDescriptor));
}
if (request.Sorts != null && request.Sorts.Any())
{
var sort = string.Join(",", request.Sorts.Select(GetOrderDescriptor));
ordering = string.IsNullOrEmpty(ordering) ? sort : string.Join(",", ordering, sort);
}
if (!string.IsNullOrEmpty(ordering))
{
collection = collection.OrderBy(ordering);
}
var pageItems = collection.Skip((request.Page - 1) * request.PageSize).Take(request.PageSize);
var aggregatesTotal = GetAggregates(distinctCollection.GroupBy(x => 1), request.Aggregates); //GroupBy(x => 1) - fake grouping to enable dynamic aggregation
var aggregates = aggregatesTotal.Select("Aggregates").AsQueryable().FirstOrDefault();
var gridData = new
{
Total = collection.Count(),
Aggregates = aggregates,
Groups = GetGroups2(collection, pageItems, request.Groups, request.Aggregates),
Data = request.Groups == null || !request.Groups.Any() ? pageItems : null
};
return gridData;
}
private string GetOrderDescriptor(SortDescriptor sort)
{
return GetOrderDescriptor(sort.Member, sort.SortDirection);
}
private string GetOrderDescriptor(GroupDescriptor group)
{
return GetOrderDescriptor(group.Member, group.SortDirection);
}
private string GetOrderDescriptor(string field, ListSortDirection? dir)
{
switch (dir)
{
case ListSortDirection.Ascending:
return field;
case ListSortDirection.Descending:
return field + " descending";
}
return null;
}
private IEnumerable GetAggregates(IEnumerable groups, IEnumerable<AggregateDescriptor> aggregates)
{
Func<AggregateFunction, string> formatAggrFunc = aggrFunc =>
{
var aggrFuncName = aggrFunc.AggregateMethodName.ToLower();
return $"{TranslateAggregator(aggrFunc)} as {aggrFuncName}";
};
Func<KeyValuePair<string, IEnumerable<AggregateFunction>>, string> formatMemberAggregates = (kvp) =>
{
var memberAggregates = kvp.Value.Select(x => formatAggrFunc(x));
var memberAggregatesStr = string.Join("," + Environment.NewLine, memberAggregates);
return $"new ({memberAggregatesStr}) as {kvp.Key}";
};
var groupedAggregates = aggregates.GroupBy(x => x.Member).ToDictionary(x => x.Key, x => x.SelectMany(y => y.Aggregates));
var aggrExpressions = groupedAggregates.Select(formatMemberAggregates);
var aggrExpressionsStr = string.Join("," + Environment.NewLine, aggrExpressions);
var aggrSelector = $"new ({aggrExpressionsStr}) as Aggregates";
var selector = @"new (" + aggrSelector + ", it.Key)";
var aggreagates = groups.Select(selector);
return aggreagates;
}
private string TranslateAggregator(AggregateFunction aggrFunction)
{
var aggrMethod = aggrFunction.AggregateMethodName;
var member = aggrFunction.SourceField;
switch (aggrMethod)
{
case "Count":
return "it.Count()";
case "Sum":
return $"it.Sum(it.{member})";
case "Min":
return $"it.Min(it.{member})";
case "Max":
return $"it.Max(it.{member})";
case "Average":
return $"it.Average(it.{member})";
}
throw new InvalidOperationException($"Invalid aggregate method '{aggrMethod}'");
}
private IEnumerable GetGroups2(IEnumerable items, IEnumerable pageItems, IEnumerable<GroupDescriptor> groupInfos, IEnumerable<AggregateDescriptor> aggregates, string fieldName = null, string fieldValue = null)
{
if (groupInfos == null || !groupInfos.Any())
{
yield break;
}
var groupInfo = groupInfos.First();
var isLast = groupInfos.Count() == 1;
var groups = items.GroupBy(groupInfo.Member, "it");
var aggrResult = GetAggregates(groups, aggregates);
var pagedGroups = pageItems.GroupBy(groupInfo.Member, "it");
var query = from IGrouping<string, dynamic> pagedGroup in pagedGroups
join IGrouping<string, dynamic> group1 in groups on pagedGroup.Key equals group1.Key
join dynamic aggr in aggrResult on pagedGroup.Key equals aggr.Key
select new
{
aggregates = aggr.Aggregates,
field = groupInfo.Member,
value = pagedGroup.Key,
items = isLast ?
(fieldName != null && fieldValue != null ? pageItems.Where(fieldName + " = \"" + fieldValue + "\"" +" && " + groupInfo.Member + " = \"" + pagedGroup.Key + "\"") : pagedGroup) :
GetGroups2(group1, pageItems, groupInfos.Skip(1), aggregates, groupInfo.Member, pagedGroup.Key),
hasSubgroups = !isLast
};
var orderedItems = groupInfo.SortDirection == ListSortDirection.Descending ? query.OrderByDescending(x => x.value) : query.OrderBy(x => x.value);
foreach (var item in orderedItems)
{
yield return item;
}
}
#endregion
}
}