///<summary>Calculates the info for sprints</summary> private void RecalculateSprint(bool hasDateChanges = false) { DateTime dateStart = PIn.Date(textDateStart.Text); //Min date if parse fails if (checkProgressMode.Checked) { dateStart = DateTime.Today.Date; } DateTime dateEnd = PIn.Date(textDateEnd.Text); //Min date if parse fails if (dateStart > dateEnd) { return; } if (dateStart != DateTime.MinValue && dateEnd != DateTime.MinValue && hasDateChanges) { _listSchedules = Schedules.RefreshPeriodForEmps(dateStart, dateEnd, _listEngEmpNums); } double jobPercent = PIn.Double(textEngJobPercent.Text); //0 if parse fails double avgDevHours = PIn.Double(textAvgDevelopmentHours.Text); //0 if parse fails double avgBreakHours = PIn.Double(textBreakHours.Text); //0 if parse fails double schedHoursTotal = 0; double schedHoursBreaksTotal = 0; double schedHoursPercentTotal = 0; double allocatableHours = 0; foreach (Schedule sched in _listSchedules) { //Calculate actual scheduled time double schedHours = (sched.StopTime - sched.StartTime).TotalHours; schedHoursTotal += schedHours; //Remove average break time schedHours -= avgBreakHours; schedHoursBreaksTotal += schedHours; //Multiply the scheduled time by the percentage of coding time for the jobs we care about schedHours = schedHours * jobPercent; schedHoursPercentTotal += schedHours; //Add the sched hours to the allocatable hours allocatableHours += schedHours; } double totalAllocatedHours = 0; double totalCompletedHours = 0; foreach (Job job in _listAttachedJobs) { if (checkProgressMode.Checked && job.PhaseCur.In(JobPhase.Documentation, JobPhase.Complete, JobPhase.Cancelled)) { continue; } double estimate = job.HoursEstimate; if (job.HoursEstimateDevelopment == 0) { estimate += avgDevHours; } totalAllocatedHours += estimate; if (checkProgressMode.Checked) { totalAllocatedHours -= job.HoursActual; } totalCompletedHours += job.HoursActual; } textMaxHours.Text = Math.Round(allocatableHours, 0).ToString(); //Set maximum progress bar to allocatable hours progressBarAllocatedHours.Maximum = PIn.Int(textMaxHours.Text); textAllocatedHours.Text = Math.Round(totalAllocatedHours, 0).ToString(); //Set value of progress bar to allocated hours progressBarAllocatedHours.Value = (PIn.Int(textAllocatedHours.Text) > progressBarAllocatedHours.Maximum)?progressBarAllocatedHours.Maximum:PIn.Int(textAllocatedHours.Text); //Update info textEngHours.Text = Math.Round(schedHoursTotal).ToString(); textAfterBreak.Text = Math.Round(schedHoursBreaksTotal).ToString(); textRatioHours.Text = Math.Round(schedHoursPercentTotal).ToString(); textJobNumber.Text = _listAttachedJobs.Count.ToString(); int days = (dateEnd - dateStart).Days; textDays.Text = days.ToString(); textAvgAllocatableHours.Text = Math.Round(allocatableHours / days).ToString(); double completionPercentage = 0; completionPercentage = (totalCompletedHours / totalAllocatedHours) * 100; if (Double.IsNaN(completionPercentage)) { return; } completionPercentage = completionPercentage > 100?100:completionPercentage; textCompletionPercentage.Text = Math.Round(completionPercentage).ToString(); progressBarCompletionPercent.Value = (int)completionPercentage; }
private void FillGridEngineers(JobSprint jobSprint) { gridEngineers.BeginUpdate(); gridEngineers.ListGridColumns.Clear(); gridEngineers.ListGridColumns.Add(new GridColumn("Engineer", 0)); gridEngineers.ListGridColumns.Add(new GridColumn("Scheduled Hrs", 150) { TextAlign = HorizontalAlignment.Center, SortingStrategy = GridSortingStrategy.AmountParse }); gridEngineers.ListGridColumns.Add(new GridColumn("- Est. Breaks", 150) { TextAlign = HorizontalAlignment.Center, SortingStrategy = GridSortingStrategy.AmountParse }); gridEngineers.ListGridColumns.Add(new GridColumn("= Hrs Total", 150) { TextAlign = HorizontalAlignment.Center, SortingStrategy = GridSortingStrategy.AmountParse }); gridEngineers.ListGridColumns.Add(new GridColumn("Assigned Dev Hrs", 150) { TextAlign = HorizontalAlignment.Center, SortingStrategy = GridSortingStrategy.AmountParse }); gridEngineers.ListGridColumns.Add(new GridColumn("Free Dev Hrs", 150) { TextAlign = HorizontalAlignment.Center, SortingStrategy = GridSortingStrategy.AmountParse }); gridEngineers.ListGridColumns.Add(new GridColumn("Completed Hrs", 150) { TextAlign = HorizontalAlignment.Center, SortingStrategy = GridSortingStrategy.AmountParse }); gridEngineers.ListGridRows.Clear(); double jobPercent = jobSprint.JobPercent; double avgDevHours = jobSprint.HoursAverageDevelopment; double avgBreakHours = jobSprint.HoursAverageBreak; List <Schedule> listEngSchedules = Schedules.RefreshPeriodForEmps(jobSprint.DateStart, jobSprint.DateEndTarget, JobHelper.ListEngineerEmployeeNums); foreach (Userod userEng in JobHelper.ListEngineerUsers) { double schedHoursTotal = 0; double schedHoursBreaksTotal = 0; double schedHoursPercentTotal = 0; double allocatableHours = 0; foreach (Schedule sched in listEngSchedules.FindAll(x => x.EmployeeNum == userEng.EmployeeNum)) { //Calculate actual scheduled time double schedHours = (sched.StopTime - sched.StartTime).TotalHours; schedHoursTotal += schedHours; //Remove average break time schedHours -= avgBreakHours; schedHoursBreaksTotal += schedHours; //Multiply the scheduled time by the percentage of coding time for the jobs we care about schedHours = schedHours * jobPercent; schedHoursPercentTotal += schedHours; //Add the sched hours to the allocatable hours allocatableHours += schedHours; } double totalAllocatedHours = 0; double totalCompletedHours = 0; //TODO: OwnerNum is not a good enough check here. //Make a method to get a sublist of jobs that legit apply to an engineer (In their writeup/development, not waiting for approval) List <Job> listEngJobs = _listJobsAll.FindAll(x => !x.IsApprovalNeeded && ((x.PhaseCur == JobPhase.Development && x.UserNumEngineer == userEng.UserNum) || (x.PhaseCur == JobPhase.Definition && x.UserNumExpert == userEng.UserNum))); foreach (Job job in listEngJobs) { double estimate = job.HoursEstimate; if (job.HoursEstimateDevelopment == 0) { estimate += avgDevHours; } totalAllocatedHours += estimate; totalAllocatedHours -= job.HoursActual; totalCompletedHours += job.HoursActual; } GridRow row = new GridRow() { Tag = userEng }; row.Cells.Add(userEng.UserName); row.Cells.Add(Math.Round(schedHoursTotal).ToString()); row.Cells.Add(Math.Round(schedHoursTotal - schedHoursBreaksTotal).ToString()); row.Cells.Add(Math.Round(schedHoursBreaksTotal).ToString()); row.Cells.Add(Math.Round(totalAllocatedHours).ToString()); double freeHrs = Math.Round(schedHoursBreaksTotal - totalAllocatedHours); GridCell cell = new GridCell(freeHrs.ToString()); cell.ColorBackG = freeHrs < 20?Color.LightSalmon:Color.LightGreen; //Arbitrary 20 hours. row.Cells.Add(cell); row.Cells.Add(Math.Round(totalCompletedHours).ToString()); gridEngineers.ListGridRows.Add(row); } gridEngineers.EndUpdate(); }
private void butCalculate_Click(object sender, EventArgs e) { _listTopJobs.Clear(); listEngNoJobs.Items.Clear(); List <long> listEngNums = listEngineers.SelectedTags <Employee>().Select(x => x.EmployeeNum).ToList(); List <long> listUserNums = listEngNums.Select(x => Userods.GetUserByEmployeeNum(x).UserNum).ToList(); //Get 6 months of scheduled engineering time. Arbitrary because there should be no way we have a 6 month release cycle. List <Schedule> listSchedules = Schedules.RefreshPeriodForEmps(DateTime.Today, DateTime.Today.AddMonths(6), listEngNums); //Get all the jobs according to the selected criteria. //No need to fill currently, but I may want to add reviews into this to improve accuracy for unfinished jobs List <Job> listJobs = _listJobsAll.Where(x => x.Priority.In(listPriorities.SelectedTags <Def>().Select(y => y.DefNum)) && x.PhaseCur.In(listPhases.SelectedTags <JobPhase>()) && x.Category.In(listCategories.SelectedTags <JobCategory>())).ToList(); double totalJobHours = 0; DateTime releaseDate = DateTime.Today; double avgJobHours = _avgJobHours; double jobTimePercent = _jobTimePercent; double avgBreakHours = _avgBreakHours; Double.TryParse(textAvgJobHours.Text, out avgJobHours); Double.TryParse(textEngJobPercent.Text, out jobTimePercent); Double.TryParse(textBreakHours.Text, out avgBreakHours); gridCalculatedJobs.BeginUpdate(); gridCalculatedJobs.Columns.Clear(); gridCalculatedJobs.Columns.Add(new ODGridColumn("EstHrs", 0) { TextAlign = HorizontalAlignment.Center }); gridCalculatedJobs.Columns.Add(new ODGridColumn("ActHrs", 0) { TextAlign = HorizontalAlignment.Center }); gridCalculatedJobs.Columns.Add(new ODGridColumn("", 200)); gridCalculatedJobs.Rows.Clear(); foreach (Job job in listJobs) { if (job.UserNumEngineer == 0 && listUserNums.Contains(job.UserNumExpert)) { listUserNums.Remove(job.UserNumExpert); } if (job.UserNumEngineer != 0 && listUserNums.Contains(job.UserNumEngineer)) { listUserNums.Remove(job.UserNumEngineer); } //If hrsEst is 0 then use the avgJobHours as a base. double hrsEst = job.TimeEstimate.TotalHours == 0?avgJobHours:job.TimeEstimate.TotalHours; //Remove the actual hours spent on the job currently //If negative then just use 0 (We aren't in a dimension where negative time estimates can be used for other jobs) double hrsCalculated = (hrsEst - job.HoursActual) < 0?0:hrsEst - job.HoursActual; totalJobHours += hrsCalculated; if (job.PhaseCur == JobPhase.Development) { _listTopJobs.Add(new Tuple <long, double>(job.JobNum, hrsCalculated)); } gridCalculatedJobs.Rows.Add( new ODGridRow( new ODGridCell(job.TimeEstimate.TotalHours == 0?"0(" + _avgJobHours + ")":job.TimeEstimate.TotalHours.ToString()), new ODGridCell(job.HoursActual.ToString()), new ODGridCell(job.Title) ) { Tag = job } ); } gridCalculatedJobs.EndUpdate(); foreach (long engNum in listUserNums) { Userod eng = Userods.GetUser(engNum); listEngNoJobs.Items.Add(new ODBoxItem <Userod>(eng.UserName, eng)); } try { _listTopJobs = _listTopJobs.OrderByDescending(x => x.Item2).Take(3).ToList(); butJob1.Text = "#" + _listTopJobs[0].Item1.ToString() + "-" + Math.Round(_listTopJobs[0].Item2).ToString() + " hours"; butJob2.Text = "#" + _listTopJobs[1].Item1.ToString() + "-" + Math.Round(_listTopJobs[1].Item2).ToString() + " hours"; butJob3.Text = "#" + _listTopJobs[2].Item1.ToString() + "-" + Math.Round(_listTopJobs[2].Item2).ToString() + " hours"; } catch { panelExtra.Visible = false; } labelJobHours.Text = Math.Round(totalJobHours).ToString(); labelJobNumber.Text = listJobs.Count.ToString(); double schedHoursTotal = 0; double schedHoursBreaksTotal = 0; double schedHoursPercentTotal = 0; foreach (Schedule sched in listSchedules) { //Calculate actual scheduled time double schedHours = (sched.StopTime - sched.StartTime).TotalHours; schedHoursTotal += schedHours; //Remove average break time schedHours -= avgBreakHours; schedHoursBreaksTotal += schedHours; //Multiply the scheduled time by the percentage of coding time for the jobs we care about schedHours = schedHours * jobTimePercent; schedHoursPercentTotal += schedHours; //Remove the scheduled hours from the total job hours totalJobHours -= schedHours; if (totalJobHours < 0) { releaseDate = sched.SchedDate; //Add a week as a buffer break; } } labelEngHours.Text = Math.Round(schedHoursTotal).ToString(); labelAfterBreak.Text = Math.Round(schedHoursBreaksTotal).ToString(); labelRatioHours.Text = Math.Round(schedHoursPercentTotal).ToString(); labelReleaseDate.Text = releaseDate.ToShortDateString() + " - " + releaseDate.AddDays(7).ToShortDateString(); labelReleaseDate.Visible = true; panelExtra.Visible = true; }