918 lines
38 KiB
C#
918 lines
38 KiB
C#
using EnVisage.Code.BLL;
|
|
using EnVisage.Controllers;
|
|
using EnVisage.Models;
|
|
using Iniphi.Calculations;
|
|
using NLog;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Data.Common;
|
|
using System.Data.Entity.Core.Objects;
|
|
using System.Data.Entity.Infrastructure;
|
|
using System.Data.Entity.Validation;
|
|
using System.Data.SqlClient;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace EnVisage.Code
|
|
{
|
|
public class RaceMatrix
|
|
{
|
|
protected static readonly Logger Logger = LogManager.GetCurrentClassLogger();
|
|
private List<MixProjectModel> MixProjects { get; set; }
|
|
private DateTime RaceStartDate { get; set; }
|
|
private DateTime RaceEndDate { get; set; }
|
|
private Race CalcModule { get; set; }
|
|
private int Threshold { get; set; }
|
|
private Guid TeamID { get; set; }
|
|
private int RaceDuration { get; set; }
|
|
public MatrixProject[] Projects { get; set; }
|
|
public Dictionary<int, Dictionary<Guid, DateRange>> _mixInfo { get; set; }
|
|
public List<AvailableCapacityMatrix> AvailableCapacityMatrixs {get;set;}
|
|
|
|
private Dictionary<Guid, decimal[]> pinnedActuals = new Dictionary<Guid, decimal[]>();
|
|
private List<ProjectObj> Race2CalcProjectObj = new List<ProjectObj>();
|
|
public RaceMatrix(int threshold, Race calcModule, bool TeamLevel,MixSaveModel model, int version)
|
|
{
|
|
var raceStartDate = Utils.ConvertFromUnixDate(model.Filter.Selection.StartDate);
|
|
var raceEndDate = Utils.ConvertFromUnixDate(model.Filter.Selection.EndDate);
|
|
TeamID = Guid.Parse(model.Calendar.Teams.FirstOrDefault().Id);
|
|
|
|
|
|
var weekEndings = FiscalCalendarManager.GetWeekendingsByRange(raceStartDate, raceEndDate, new EnVisageEntities());
|
|
RaceStartDate = weekEndings[0];
|
|
RaceEndDate = weekEndings.Last();
|
|
|
|
Threshold = threshold;
|
|
|
|
MixProjects = model.Calendar.Projects.Values.ToList();
|
|
CalcModule = calcModule;
|
|
CalcModule.SCORE2MAX = 100m;
|
|
LoadMatrixData(model, TeamLevel);
|
|
ReconCapacity();
|
|
if (version > 1)
|
|
{
|
|
Projects = Projects.OrderByDescending(x => x.Score2).ToArray();
|
|
|
|
|
|
}
|
|
}
|
|
|
|
private void ReconRace2Capacity(MatrixProject p, int Ord)
|
|
{
|
|
var RollingIterationActuals = new Dictionary<Guid, decimal[]>();
|
|
if (pinnedActuals.Count > 0)
|
|
{
|
|
foreach (var k in pinnedActuals.Keys)
|
|
{
|
|
RollingIterationActuals.Add(k, pinnedActuals[k]);
|
|
}
|
|
}
|
|
p.Race2Pinned = true;
|
|
var cap = p.SetBestIterationDataRace2( Ord, this.RaceStartDate, this.RaceEndDate, this.RaceDuration);
|
|
calcIterationActuals(cap, ref RollingIterationActuals);
|
|
pinnedActuals = RollingIterationActuals;
|
|
Race2CalcProjectObj.Add(p.getRaceProject(Ord, 1));
|
|
}
|
|
private void ReconCapacity()
|
|
{
|
|
var RollingIterationActuals = new Dictionary<Guid, decimal[]>();
|
|
foreach (var p in Projects)
|
|
{
|
|
if (p.isPinned && Projects.Count() > 0)
|
|
{
|
|
|
|
var cap = p.DoIterationCalc(p.CalculateOrdinalFromDate(p.StartDate, this.RaceStartDate), this.RaceStartDate, this.RaceEndDate, this.RaceDuration);
|
|
calcIterationActuals(cap, ref RollingIterationActuals);
|
|
}
|
|
else if (p.isPinned)
|
|
p.isPinned = false;
|
|
}
|
|
var cleanPs = Projects.Where(x => x.isPinned == false).ToArray();
|
|
Projects = cleanPs;
|
|
pinnedActuals = RollingIterationActuals;
|
|
}
|
|
private void SetRaceToCapacity()
|
|
{
|
|
var RollingIterationActuals = new Dictionary<Guid, decimal[]>();
|
|
foreach (var p in Projects)
|
|
{
|
|
if (p.Race2Pinned)
|
|
{
|
|
var cap = p.DoIterationCalc(p.CalculateOrdinalFromDate(p.StartDate, this.RaceStartDate), this.RaceStartDate, this.RaceEndDate, this.RaceDuration);
|
|
calcIterationActuals(cap, ref RollingIterationActuals);
|
|
}
|
|
}
|
|
pinnedActuals = RollingIterationActuals;
|
|
}
|
|
public void resetRaceModule()
|
|
{
|
|
CalcModule.resetCalcModule();
|
|
}
|
|
public int GetTotalWeekEndingForIteration()
|
|
{
|
|
return RaceDuration;
|
|
|
|
}
|
|
public void SetRace2ScorePerProject(int pIdx, decimal totalScore)
|
|
{
|
|
var p = this.Projects[pIdx];
|
|
p.Race2TotalScore = totalScore;
|
|
}
|
|
public List<MatrixProjectModel> GetMixDataForRace2()
|
|
{
|
|
var cntx = new EnVisageEntities();
|
|
var results = new List<MatrixProjectModel>();
|
|
for (int pIdx = 0; pIdx < Projects.Length; pIdx++)
|
|
{
|
|
var proj = Projects[pIdx];
|
|
|
|
var StartDateWeekEnding = FiscalCalendarManager.GetWeekendingsByRange(proj.StartDate, null, cntx);
|
|
var weekEndings = FiscalCalendarManager.GetWeekendingsByRange(this.RaceStartDate, this.RaceEndDate, cntx);
|
|
var newStartWeek = StartDateWeekEnding[0];
|
|
|
|
|
|
if (!proj.isPinned && proj.Race2BestCycle > 0)
|
|
newStartWeek = weekEndings[proj.Race2BestCycle - 1];
|
|
var pr = new MatrixProjectModel()
|
|
{
|
|
Id = proj.Id,
|
|
OriginalStartWeek = Utils.ConvertToUnixDate(StartDateWeekEnding[0]),
|
|
NewStartWeek = Utils.ConvertToUnixDate(newStartWeek),
|
|
Name =proj.Name,
|
|
RaceOrder = pIdx,
|
|
Score2 = proj.Score2,
|
|
TotalScore =Math.Round(proj.Race2TotalScore)
|
|
};
|
|
results.Add(pr);
|
|
}
|
|
return results;
|
|
}
|
|
|
|
public List<MatrixProjectModel> GetMixDataForIteration(int[] cycles)
|
|
{
|
|
var results = new List<MatrixProjectModel>();
|
|
for (int pIdx = 0; pIdx < Projects.Length; pIdx++)
|
|
{
|
|
int cycle = cycles[pIdx];
|
|
var proj = Projects[pIdx];
|
|
var cntx = new EnVisageEntities();
|
|
var StartDateWeekEnding= FiscalCalendarManager.GetWeekendingsByRange(proj.StartDate, null, cntx);
|
|
var weekEndings = FiscalCalendarManager.GetWeekendingsByRange(this.RaceStartDate, this.RaceEndDate, cntx);
|
|
var newStartWeek = weekEndings[cycle - 1];
|
|
if (proj.isPinned)
|
|
newStartWeek = StartDateWeekEnding[0];
|
|
var pr = new MatrixProjectModel()
|
|
{
|
|
Id = proj.Id,
|
|
OriginalStartWeek = Utils.ConvertToUnixDate(StartDateWeekEnding[0]),
|
|
NewStartWeek = Utils.ConvertToUnixDate(newStartWeek),
|
|
RaceOrder = proj.RaceOrder,
|
|
Score2 = proj.Score2,
|
|
Name = proj.Name,
|
|
TotalScore =proj.Race2TotalScore
|
|
};
|
|
results.Add(pr);
|
|
}
|
|
return results;
|
|
}
|
|
public void BuildProjectIterationRace2Scores(int projectIndex)
|
|
{
|
|
var proj = Projects[projectIndex];
|
|
|
|
if (proj.isPinned)
|
|
{
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
for (int cycle = 1; cycle <= this.RaceDuration; cycle++)
|
|
{
|
|
var RollingIterationActuals = new Dictionary<Guid, decimal[]>();
|
|
if (pinnedActuals.Count > 0)
|
|
{
|
|
foreach (var k in pinnedActuals.Keys)
|
|
{
|
|
RollingIterationActuals.Add(k, pinnedActuals[k]);
|
|
}
|
|
}
|
|
|
|
|
|
var workingProjObjList = Race2CalcProjectObj.Clone();
|
|
var iterationActuals = proj.DoIterationCalc( cycle, this.RaceStartDate, this.RaceEndDate, this.RaceDuration);
|
|
calcIterationActuals(iterationActuals, ref RollingIterationActuals);
|
|
var calcdataList = RollingIterationActuals.Select(x => x.Value).ToList();
|
|
workingProjObjList.Add(proj.getRaceProject(cycle, 1));
|
|
if (calcdataList.Count > 0)
|
|
AddIterationToModule(calcdataList, cycle, workingProjObjList);
|
|
|
|
|
|
|
|
}
|
|
var TotalScore = this.CalcModule.GetBestMix();
|
|
proj.Race2TotalScore = TotalScore.TotalScore;
|
|
proj.SetBestIterationDataRace2(TotalScore.MixIteration, this.RaceStartDate, this.RaceEndDate, this.RaceDuration);
|
|
ReconRace2Capacity(proj, TotalScore.MixIteration);
|
|
|
|
}
|
|
}
|
|
|
|
private void calcIterationActuals(Dictionary<Guid, decimal[]> iterationActuals, ref Dictionary<Guid, decimal[]> RollingIterationActuals)
|
|
{
|
|
|
|
try
|
|
{
|
|
foreach (var prId in iterationActuals.Keys)
|
|
{
|
|
var rollingActuals= RollingIterationActuals.Where(x => x.Key == prId).Select(x=>x.Value).FirstOrDefault();
|
|
var AvailPerResource = this.AvailableCapacityMatrixs.Where(x => x.PeopleResourceId == prId).FirstOrDefault();
|
|
if (AvailPerResource == null)
|
|
{
|
|
var initDurationArray = new decimal[this.RaceDuration];
|
|
Array.Clear(initDurationArray, 0, this.RaceDuration);
|
|
|
|
AvailPerResource = new AvailableCapacityMatrix()
|
|
{
|
|
PeopleResourceId = prId,
|
|
AvailableCapacity = initDurationArray,
|
|
IsRealResource = true
|
|
};
|
|
AvailableCapacityMatrixs.Add(AvailPerResource);
|
|
}
|
|
var iterationActual = iterationActuals[prId];
|
|
if (rollingActuals != null)
|
|
{
|
|
var allocations= rollingActuals.Zip(iterationActual, (first, second) => first - second).ToArray();
|
|
RollingIterationActuals[prId] = allocations;
|
|
}
|
|
else {
|
|
var allocations = AvailPerResource.AvailableCapacity.Zip(iterationActual, (first, second) => first - second).ToArray();
|
|
|
|
var rollingIterationActual = RollingIterationActuals.Where(x => x.Key == prId).FirstOrDefault();
|
|
if (rollingIterationActual.Value == null)
|
|
RollingIterationActuals.Add(prId, allocations);
|
|
else
|
|
{
|
|
RollingIterationActuals[prId] = allocations;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (Exception dd)
|
|
{
|
|
RaceMatrix.LogException(dd);
|
|
}
|
|
}
|
|
public void BuildProjectIterationScores(int Iteration, int[] Cycles)
|
|
{
|
|
var calcProjectObj = new List<ProjectObj>();
|
|
var RollingIterationActuals = new Dictionary<Guid, decimal[]>();
|
|
if (pinnedActuals.Count > 0)
|
|
{
|
|
foreach (var k in pinnedActuals.Keys)
|
|
{
|
|
RollingIterationActuals.Add(k, pinnedActuals[k]);
|
|
}
|
|
}
|
|
for (int pIdx =0; pIdx< Projects.Length; pIdx++)
|
|
{
|
|
int cycle = Cycles[pIdx];
|
|
var proj = Projects[pIdx];
|
|
var iterationActuals=proj.DoIterationCalc( cycle,this.RaceStartDate,this.RaceEndDate,this.RaceDuration);
|
|
calcProjectObj.Add(proj.getRaceProject(cycle ,1m ));
|
|
calcIterationActuals(iterationActuals, ref RollingIterationActuals);
|
|
}
|
|
var calcdataList = RollingIterationActuals.Select(x => x.Value).ToList();
|
|
if (calcdataList.Count > 0)
|
|
AddIterationToModule(calcdataList, Iteration, calcProjectObj);
|
|
|
|
}
|
|
|
|
private Dictionary<int, Dictionary<Guid, DateRange>> getMixInfoObj()
|
|
{
|
|
if (_mixInfo == null)
|
|
_mixInfo = new Dictionary<int, Dictionary<Guid, DateRange>>();
|
|
return _mixInfo;
|
|
}
|
|
public void AddIterationToModule(List<decimal[]> mixdataforcal, int iterationCount, List<ProjectObj> calcProjectObj)
|
|
{
|
|
|
|
CalcModule.CalcIterationEx(mixdataforcal, iterationCount, calcProjectObj);
|
|
}
|
|
|
|
#region Build Data collections
|
|
private void LoadMatrixData(MixSaveModel m, bool teamLevel)
|
|
{
|
|
try
|
|
{
|
|
var ProjectList = new List<MatrixProject>();
|
|
int maxCapLoop = m.Calendar.Projects.Count;
|
|
ProjectManager projMan = new ProjectManager(new EnVisageEntities());
|
|
foreach (var manProj in m.Calendar.ManagedProjects)
|
|
{
|
|
var p = m.Calendar.Projects.Select(x => x.Value).Where(x => x.Id == manProj).FirstOrDefault();
|
|
if (p == null)
|
|
continue;
|
|
var EfProj = projMan.Load(p.Id);
|
|
if (EfProj == null)
|
|
continue;
|
|
var mp = new MatrixProject()
|
|
{
|
|
|
|
|
|
DurationInWeeks = p.Scenario.Duration,
|
|
Id = p.Id,
|
|
Priority = EfProj.Priority,
|
|
Probability = Convert.ToInt32(EfProj.Probability * 100),
|
|
RaceOrder = 0,
|
|
DeadLineDate = p.Deadline.HasValue ? Utils.ConvertFromUnixDate(p.Deadline.Value) : this.RaceEndDate,
|
|
StartDate = Utils.ConvertFromUnixDate(p.Scenario.StartDate),
|
|
EndDate = Utils.ConvertFromUnixDate(p.Scenario.EndDate),
|
|
isPinned = p.Pinned ? true : Utils.ConvertFromUnixDate(p.Scenario.StartDate) < DateTime.Now,
|
|
Name = p.Name
|
|
};
|
|
mp.SetScore2(this.RaceStartDate, this.RaceEndDate);
|
|
mp.setMaxOrdinal(this.RaceStartDate, this.RaceEndDate);
|
|
mp.setMinOrdinal(this.RaceStartDate, this.RaceEndDate);
|
|
if (this.MixProjects.Where(x => x.Id == mp.Id).FirstOrDefault() != null)
|
|
ProjectList.Add(mp);
|
|
|
|
}
|
|
Projects = ProjectList.OrderBy(x => x.Score2).ToArray();
|
|
|
|
foreach (var p in Projects)
|
|
{
|
|
var modelProj = m.Calendar.Projects.Select(x => x.Value).Where(x => x.Id == p.Id).FirstOrDefault();
|
|
|
|
List<ResourceMatrix> Required = new List<ResourceMatrix>();
|
|
var wp = p;
|
|
foreach (var ec in modelProj.Scenario.Expenditures.Values)
|
|
{
|
|
if (teamLevel)
|
|
{
|
|
var rm = new ResourceMatrix()
|
|
{
|
|
PeopleResourceId = ec.ExpenditureCategoryId,
|
|
ProjectId = p.Id,
|
|
RequiredCapacity = BuildValueArrayForTeamLevel(ec)
|
|
|
|
};
|
|
if (wp.Id != rm.ProjectId)
|
|
wp = Projects.Where(x => x.Id == rm.ProjectId).FirstOrDefault();
|
|
Required.Add(rm);
|
|
|
|
}
|
|
else
|
|
{
|
|
if (ec.Teams == null)
|
|
ec.Teams = new Dictionary<string, ExpenditureDetailsTeam>();
|
|
if (ec.Teams.Count == 0)
|
|
continue;
|
|
if (ec.Teams.FirstOrDefault().Value == null)
|
|
continue;
|
|
if (ec.Teams.FirstOrDefault().Value.Resources == null)
|
|
continue;
|
|
|
|
foreach (var pr in ec.Teams.FirstOrDefault().Value.Resources.Values)
|
|
{
|
|
var rm = new ResourceMatrix()
|
|
{
|
|
PeopleResourceId = pr.Id,
|
|
ProjectId = p.Id,
|
|
RequiredCapacity = BuildValueArrayForResourceLevel(pr, ec)
|
|
|
|
};
|
|
if (wp.Id != rm.ProjectId)
|
|
wp = Projects.Where(x => x.Id == rm.ProjectId).FirstOrDefault();
|
|
Required.Add(rm);
|
|
|
|
}
|
|
}
|
|
if (wp != null)
|
|
wp.RequiredResources = Required;
|
|
}
|
|
}
|
|
}
|
|
catch (Exception dds)
|
|
{
|
|
Projects = new MatrixProject[0];
|
|
RaceMatrix.LogException(dds);
|
|
}
|
|
using (var db = new EnVisageEntities())
|
|
{
|
|
var sproc = "sp_BuildRaceMatrix";
|
|
var ResourceColName = "PeopleResourceID";
|
|
if (teamLevel)
|
|
{
|
|
sproc = "sp_BuildRaceMatrixTeams";
|
|
ResourceColName = "ExpenditureCategoryId";
|
|
}
|
|
|
|
// If using Code First we need to make sure the model is built before we open the connection
|
|
// This isn't required for models created with the EF Designer
|
|
db.Database.Initialize(force: false);
|
|
|
|
// Create a SQL command to execute the sproc
|
|
var cmd = db.Database.Connection.CreateCommand();
|
|
cmd.CommandText = sproc;
|
|
cmd.Parameters.Add(new SqlParameter("@TeamID", this.TeamID));
|
|
cmd.Parameters.Add(new SqlParameter("@RaceStartDate", this.RaceStartDate));
|
|
cmd.Parameters.Add(new SqlParameter("@RaceEndDate", this.RaceEndDate));
|
|
cmd.Parameters.Add(new SqlParameter("@threshold", this.Threshold));
|
|
|
|
cmd.CommandType = System.Data.CommandType.StoredProcedure;
|
|
|
|
try
|
|
{
|
|
db.Database.Connection.Open();
|
|
// Run the sproc
|
|
var reader = cmd.ExecuteReader();
|
|
|
|
var maxProjs = 0;
|
|
while (reader.Read())
|
|
{
|
|
maxProjs++;
|
|
}
|
|
List<int> colNames = new List<int>();
|
|
List<int> colPrNames = new List<int>();
|
|
List<ResourceMatrix> Required = new List<ResourceMatrix>();
|
|
while(true)
|
|
{
|
|
if (colNames.Count > 0)
|
|
break;
|
|
reader.NextResult();
|
|
|
|
|
|
while (reader.Read())
|
|
{
|
|
try
|
|
{
|
|
if (reader.GetOrdinal("ColumnNames") >= 0)
|
|
{
|
|
var namesdb = (string) reader.GetValue(reader.GetOrdinal("ColumnNames"));
|
|
char[] splprm = { ',' };
|
|
string[] names = namesdb.Split(splprm);
|
|
foreach (var n in names)
|
|
{
|
|
var nc = n.Replace('[', ' ').Replace(']', ' ').Trim().TrimStart(' ');
|
|
colNames.Add(int.Parse(nc));
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
|
|
try
|
|
{
|
|
if (reader.GetOrdinal("PRColumnNames") >= 0)
|
|
{
|
|
colPrNames = new List<int>();
|
|
var namesDbVal = reader.GetValue(reader.GetOrdinal("PRColumnNames"));
|
|
if (DBNull.Value == namesDbVal)
|
|
break;
|
|
var namesdb = (string)namesDbVal;
|
|
char[] splprm = { ',' };
|
|
string[] names = namesdb.Split(splprm);
|
|
foreach (var n in names)
|
|
{
|
|
var nc = n.Replace('[', ' ').Replace(']', ' ').Trim().TrimStart(' ');
|
|
colPrNames.Add(int.Parse(nc));
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
Guid ProjectId = Guid.Empty;
|
|
if (!reader.IsDBNull(reader.GetOrdinal(ResourceColName)))
|
|
ProjectId = (Guid) reader.GetValue(reader.GetOrdinal("ProjectID"));
|
|
Guid PeopleResourceId = Guid.Empty;
|
|
if (!reader.IsDBNull(reader.GetOrdinal(ResourceColName)))
|
|
PeopleResourceId = (Guid) reader.GetValue(reader.GetOrdinal(ResourceColName));
|
|
var rm = new ResourceMatrix()
|
|
{
|
|
PeopleResourceId = PeopleResourceId,
|
|
ProjectId = ProjectId,
|
|
RequiredCapacity = BuildValueArray(reader, colPrNames)
|
|
|
|
};
|
|
Required.Add(rm);
|
|
}
|
|
}
|
|
foreach(var p in Projects)
|
|
{
|
|
p.RequiredResources = Required.Where(x => x.ProjectId == p.Id).ToList();
|
|
}
|
|
|
|
|
|
|
|
reader.NextResult();
|
|
AvailableCapacityMatrixs = new List<AvailableCapacityMatrix>();
|
|
while (reader.Read())
|
|
{
|
|
bool IsRealResource = false;
|
|
if (!reader.IsDBNull(reader.GetOrdinal("isReal")))
|
|
IsRealResource = (bool) reader.GetValue(reader.GetOrdinal("isReal"));
|
|
Guid PeopleResourceId = Guid.Empty;
|
|
if (!reader.IsDBNull(reader.GetOrdinal(ResourceColName)))
|
|
PeopleResourceId = (Guid) reader.GetValue(reader.GetOrdinal(ResourceColName));
|
|
AvailableCapacityMatrixs.Add(new AvailableCapacityMatrix()
|
|
{
|
|
IsRealResource= IsRealResource,
|
|
PeopleResourceId = PeopleResourceId,
|
|
AvailableCapacity = BuildValueArray(reader, colNames)
|
|
});
|
|
}
|
|
reader.NextResult();
|
|
var maxscore1 = 100m;
|
|
while (reader.Read())
|
|
{
|
|
|
|
maxscore1 = reader.GetDecimal(reader.GetOrdinal("MaxScore1"));
|
|
}
|
|
if (maxscore1 == 0)
|
|
maxscore1 = 100m;
|
|
this.CalcModule.SCORE1MAX = maxscore1;
|
|
RaceDuration = AvailableCapacityMatrixs.FirstOrDefault().AvailableCapacity.Length;
|
|
|
|
|
|
|
|
}
|
|
catch (Exception dds)
|
|
{
|
|
Projects = new MatrixProject[0];
|
|
RaceMatrix.LogException(dds);
|
|
}
|
|
finally
|
|
{
|
|
db.Database.Connection.Close();
|
|
}
|
|
|
|
|
|
}
|
|
}
|
|
|
|
private decimal[] BuildValueArrayForTeamLevel(ExpenditureDetail ec)
|
|
{
|
|
|
|
decimal[] results = new decimal[0];
|
|
if (ec == null)
|
|
return results;
|
|
if (ec.Details == null)
|
|
return results;
|
|
|
|
results = new decimal[ec.Details.Count];
|
|
for(int i=0; i<ec.Details.Count; i++)
|
|
{
|
|
if (ec.Teams.FirstOrDefault().Value != null)
|
|
if (ec.Teams.FirstOrDefault().Value.Resources != null)
|
|
results[i] = ec.UOMValue*ec.Teams.FirstOrDefault().Value.Resources.Count;
|
|
}
|
|
return results;
|
|
}
|
|
private decimal[] BuildValueArrayForResourceLevel(TeamResource rc, ExpenditureDetail ec)
|
|
{
|
|
decimal[] results = new decimal[0];
|
|
|
|
results = new decimal[rc.QuantityValues.Count];
|
|
for (int i = 0; i < ec.Details.Count; i++)
|
|
{
|
|
results[i] = ec.UOMValue;
|
|
}
|
|
return results;
|
|
}
|
|
private decimal[] BuildValueArray(DbDataReader reader, int nonDataColCount)
|
|
{
|
|
decimal[] results=new decimal[0];
|
|
int nbrOfCols = reader.FieldCount - nonDataColCount;
|
|
results = new decimal[nbrOfCols];
|
|
for(int i=1; i<=nbrOfCols; i++)
|
|
{
|
|
int ord = -1;
|
|
try
|
|
{
|
|
ord = reader.GetOrdinal(i.ToString());
|
|
string n = reader.GetName(i);
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
|
|
if (ord >= 0)
|
|
{
|
|
if (!reader.IsDBNull(ord))
|
|
results[i - 1] = reader.GetDecimal(ord); //decimal.Parse(reader[i.ToString()].ToString());
|
|
else
|
|
results[i - 1] = 0m;
|
|
}
|
|
//we expect a value but the weekending date might have been filetered due to the threshold set when
|
|
//loading the data
|
|
else {
|
|
results[i - 1] = 0m;
|
|
}
|
|
}
|
|
|
|
return results;
|
|
}
|
|
private decimal[] BuildValueArray(DbDataReader reader, List<int> columns)
|
|
{
|
|
decimal[] results = new decimal[0];
|
|
int OnIdx = 0;
|
|
results = new decimal[columns.Count];
|
|
foreach(int c in columns.OrderBy(x=>x))
|
|
{
|
|
int ord = -1;
|
|
try
|
|
{
|
|
ord = reader.GetOrdinal(c.ToString());
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
|
|
if (ord >= 0)
|
|
{
|
|
if (!reader.IsDBNull(ord))
|
|
results[OnIdx] = reader.GetDecimal(ord); //decimal.Parse(reader[i.ToString()].ToString());
|
|
else
|
|
results[OnIdx] = 0m;
|
|
}
|
|
//we expect a value but the weekending date might have been filetered due to the threshold set when
|
|
//loading the data
|
|
else {
|
|
results[OnIdx] = 0m;
|
|
}
|
|
OnIdx++;
|
|
}
|
|
|
|
return results;
|
|
}
|
|
#endregion
|
|
#region Error Logging
|
|
public static void LogException(Exception ex)
|
|
{
|
|
var sb = new StringBuilder();
|
|
sb.AppendLine(string.Format("{0}: {1}", ex.GetType(), ex.Message));
|
|
sb.AppendLine(ex.StackTrace);
|
|
|
|
var innerCount = 0;
|
|
var innerEx = ex;
|
|
while (innerEx.InnerException != null && innerCount++ < Constants.MAX_INNER_EXCEPTION_LOG_LEVEL)
|
|
{
|
|
if (innerEx.Message != innerEx.InnerException.Message)
|
|
sb.AppendLine("Inner Exception Message: " + innerEx.InnerException.Message);
|
|
innerEx = innerEx.InnerException;
|
|
}
|
|
var dbEntityValidationException = ex as DbEntityValidationException;
|
|
if (dbEntityValidationException != null)
|
|
{
|
|
foreach (var validationErrors in dbEntityValidationException.EntityValidationErrors)
|
|
{
|
|
foreach (var validationError in validationErrors.ValidationErrors)
|
|
{
|
|
sb.AppendFormat("Property: {0} Error: {1}", validationError.PropertyName,
|
|
validationError.ErrorMessage);
|
|
|
|
}
|
|
}
|
|
sb.AppendLine(dbEntityValidationException.StackTrace);
|
|
}
|
|
if (System.Web.HttpContext.Current != null)
|
|
{
|
|
sb.AppendLine();
|
|
sb.AppendLine(string.Format("URL: {0}", System.Web.HttpContext.Current.Request.Url));
|
|
sb.AppendLine(string.Format("Referrer: {0}", System.Web.HttpContext.Current.Request.UrlReferrer));
|
|
sb.AppendLine(string.Format("QueryString: {0}", System.Web.HttpContext.Current.Request.QueryString));
|
|
sb.AppendLine(string.Format("UserHostAddress: {0}", System.Web.HttpContext.Current.Request.UserHostAddress));
|
|
sb.AppendLine(string.Format("UserAgent: {0}", System.Web.HttpContext.Current.Request.UserAgent));
|
|
if (System.Web.HttpContext.Current.Request.Form.Count > 0)
|
|
{
|
|
sb.AppendLine();
|
|
sb.AppendLine("Form:");
|
|
foreach (string key in System.Web.HttpContext.Current.Request.Form.Keys)
|
|
{
|
|
sb.AppendLine(string.Format("{0}: {1}", key, System.Web.HttpContext.Current.Request.Form[key]));
|
|
}
|
|
}
|
|
}
|
|
|
|
// log error using NLog
|
|
Logger.Fatal(sb.ToString());
|
|
}
|
|
#endregion
|
|
}
|
|
public class MatrixProjectModel
|
|
{
|
|
public Guid Id { get; set; }
|
|
public long OriginalStartWeek { get; set; }
|
|
public long NewStartWeek { get; set; }
|
|
public int RaceOrder { get; set; }
|
|
public string Name { get; set; }
|
|
public decimal Score2 { get; set; }
|
|
public decimal TotalScore { get; set; }
|
|
|
|
}
|
|
public class MatrixProject
|
|
{
|
|
public string Name { get; set; }
|
|
public int MaxCycle { get; set; }
|
|
public int MinCycle { get; set; }
|
|
public Guid Id { get; set; }
|
|
public DateTime StartDate { get; set; }
|
|
public DateTime EndDate { get; set; }
|
|
public int DurationInWeeks { get; set; }
|
|
public decimal Priority { get; set; }
|
|
public int Probability { get; set; }
|
|
public int RaceOrder { get; set; }
|
|
public DateTime DeadLineDate { get; set; }
|
|
public decimal Score2 { get; set; }
|
|
public decimal Race2TotalScore { get; set; }
|
|
public int Race2BestCycle { get; set; }
|
|
public Dictionary<Guid, decimal[]> LastCalcValues { get; set; }
|
|
public bool isPinned { get; set; }
|
|
public bool Race2Pinned { get; set; }
|
|
|
|
public List<ResourceMatrix> RequiredResources { get; set; }
|
|
public Dictionary<Guid, decimal[]> SetBestIterationDataRace2( int Ordinal,DateTime RaceStart,DateTime RaceEnd, int RaceDuration)
|
|
{
|
|
var rt= DoIterationCalc( Ordinal, RaceStart, RaceEnd, RaceDuration);
|
|
Race2BestCycle = Ordinal;
|
|
return rt;
|
|
}
|
|
public Dictionary<Guid, decimal[]> DoIterationCalc( int Ordinal, DateTime RaceStart, DateTime RaceEnd, int RaceDuration)
|
|
{
|
|
|
|
var IterationValues = new Dictionary<Guid, decimal[]>();
|
|
try
|
|
{
|
|
foreach (var pr in RequiredResources)
|
|
{
|
|
int StartOrdinal = Ordinal;
|
|
var prId = pr.PeopleResourceId;
|
|
int prLen = pr.RequiredCapacity.Length;
|
|
int OrdinalArrayIndex = Ordinal - 1;
|
|
int copyfrom = 0;
|
|
bool DoCalc = true;
|
|
var prDuration = new decimal[RaceDuration];
|
|
if (Ordinal > RaceDuration || Ordinal > this.MaxCycle || Ordinal <= this.MinCycle)
|
|
{
|
|
Array.Clear(prDuration, 0, RaceDuration);
|
|
IterationValues.Add(pr.PeopleResourceId, prDuration);
|
|
DoCalc = false;
|
|
}
|
|
if (DoCalc)
|
|
{
|
|
if (Ordinal < 0)
|
|
{
|
|
copyfrom = 0 - Ordinal;
|
|
prLen = (prLen - copyfrom);
|
|
OrdinalArrayIndex = 0;
|
|
}
|
|
else if ((OrdinalArrayIndex + prLen) > RaceDuration)
|
|
{
|
|
prLen = RaceDuration - OrdinalArrayIndex;
|
|
}
|
|
|
|
Array.Clear(prDuration, 0, RaceDuration);
|
|
Array.Copy(pr.RequiredCapacity, copyfrom, prDuration, OrdinalArrayIndex, prLen);
|
|
IterationValues.Add(pr.PeopleResourceId, prDuration);
|
|
}
|
|
}
|
|
}
|
|
catch (Exception d)
|
|
{
|
|
RaceMatrix.LogException(d);
|
|
IterationValues = new Dictionary<Guid, decimal[]>();
|
|
var prDuration = new decimal[RaceDuration];
|
|
Array.Clear(prDuration, 0, RaceDuration);
|
|
foreach (var pr in RequiredResources)
|
|
{
|
|
IterationValues.Add(pr.PeopleResourceId, prDuration);
|
|
}
|
|
|
|
}
|
|
LastCalcValues = IterationValues;
|
|
return IterationValues;
|
|
}
|
|
public ProjectObj getRaceProject(int ordinal, decimal multiple)
|
|
{
|
|
return new ProjectObj()
|
|
{
|
|
probability = Probability,
|
|
Id = Id,
|
|
priority = Priority > 0 ? Priority : 1, // To-Do: can it be less than 1, e.g. 0.25?
|
|
weekOrdinal = ordinal,
|
|
score = 1,
|
|
race2Multiple= multiple
|
|
};
|
|
}
|
|
public void setMaxOrdinal(DateTime RaceStart,DateTime RaceEnd)
|
|
{
|
|
if (this.isPinned)
|
|
{
|
|
this.MaxCycle = GetPinnedCycle(RaceStart);
|
|
return;
|
|
}
|
|
var weekEndings=FiscalCalendarManager.GetWeekendingsByRange(RaceStart, RaceEnd, new EnVisageEntities());
|
|
int Ordinal = weekEndings.Count;
|
|
while (this.CalculateStartDate(Ordinal, RaceStart).AddDays(this.DurationInWeeks * 7) > this.DeadLineDate.AddDays(-1))
|
|
{
|
|
Ordinal--;
|
|
|
|
}
|
|
this.MaxCycle = Ordinal;
|
|
}
|
|
public void setMinOrdinal(DateTime RaceStart, DateTime RaceEnd)
|
|
{
|
|
if (this.isPinned)
|
|
{
|
|
this.MinCycle = GetPinnedCycle(RaceStart);
|
|
return;
|
|
}
|
|
var weekEndings = FiscalCalendarManager.GetWeekendingsByRange(DateTime.Now, RaceEnd, new EnVisageEntities());
|
|
|
|
var FirstWeekEndingDate = weekEndings.FirstOrDefault();
|
|
if ( FirstWeekEndingDate == null)
|
|
{
|
|
this.MinCycle = this.MaxCycle;
|
|
}
|
|
int Ordinal = 0;
|
|
while (this.CalculateStartDate(Ordinal, RaceStart).AddDays(7) < FirstWeekEndingDate)
|
|
{
|
|
Ordinal++;
|
|
}
|
|
if (Ordinal == 0)
|
|
Ordinal++;
|
|
this.MinCycle = Ordinal;
|
|
}
|
|
public int GetPinnedCycle(DateTime RaceStart)
|
|
{
|
|
var startOrd = (Convert.ToInt32((RaceStart - this.StartDate).TotalDays / 7d) + 1);
|
|
return startOrd;
|
|
}
|
|
public DateTime CalculateStartDate(int weekOrd, DateTime RaceStart)
|
|
{
|
|
if (this.isPinned)
|
|
return this.StartDate;
|
|
var days=weekOrd * 7d;
|
|
|
|
|
|
return RaceStart.AddDays(days-1);
|
|
}
|
|
public int CalculateOrdinalFromDate(DateTime date, DateTime RaceStart)
|
|
{
|
|
var ord= (Convert.ToInt32((date- RaceStart).TotalDays / 7d) + 1);
|
|
if (ord == 0)
|
|
ord = 1;
|
|
return ord;
|
|
}
|
|
public void SetScore2(DateTime RaceStart, DateTime RaceEnd)
|
|
{
|
|
Score2 = (Probability / Priority); // To-Do: test me
|
|
decimal multiple = GetMultiple(RaceStart, RaceEnd, this.DeadLineDate, this.DurationInWeeks);
|
|
if (multiple <= 0)
|
|
{
|
|
//this.isPinned = true;
|
|
multiple = 1m;
|
|
}
|
|
Score2 = Score2 * multiple + (DurationInWeeks / 100);
|
|
}
|
|
public decimal GetMultiple(DateTime sw, DateTime ew, DateTime dl, int d)
|
|
{
|
|
var M = new decimal();
|
|
var MD= new double();
|
|
var DO = new double();
|
|
var W = new int();
|
|
MD = ( dl-sw).TotalDays / 7;
|
|
DO = MD;
|
|
W =Convert.ToInt32(MD / 4);
|
|
if (DO <= d)
|
|
M = 0.0m;
|
|
else if (DO <= W + d)
|
|
M = 1m;
|
|
else if (DO <= (2 * W) + d)
|
|
M = 0.9m;
|
|
else if (DO <= (3 * W) + d)
|
|
M = 0.8m;
|
|
else
|
|
M = 0.7m;
|
|
return M;
|
|
}
|
|
}
|
|
public class ResourceMatrix
|
|
{
|
|
public decimal[] RequiredCapacity { get; set; }
|
|
public Guid PeopleResourceId { get; set; }
|
|
public Guid ProjectId { get; set; }
|
|
}
|
|
|
|
public class AvailableCapacityMatrix
|
|
{
|
|
public decimal[] AvailableCapacity { get; set; }
|
|
public Guid PeopleResourceId { get; set; }
|
|
public bool IsRealResource { get; set; }
|
|
}
|
|
} |