241 lines
9.6 KiB
C#
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
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
} |