public string BeginGettingProjectStatuses(List<ProjectListItem> combinations)
        {
            var fbClient = FogBugzGateway.GetClientForParallel();

            var persons = fbClient.GetPersons().ToList();

            var milestones = GetOnlyStartedMilstones(combinations);

            var isLoadedByAdmin = FogBugzGateway.GetIsScheduleLoadedByAdmin();
            var fbSchedule = FogBugzGateway.GetFbSchedule();

            Func<object, int> action = (object obj) =>
            {
                ProjectStatusListItem combo = obj as ProjectStatusListItem;

                Core.MsCache.Set(ListProgressStatusKey + "_" + _cacheKey, new ProgressStatusInfo { Value = 66, Label = String.Format("Calculating status for \"{0} {1}\" for tasks {2} of {3}", combo.Project.Name, combo.Milestone.Name, milestones.IndexOf(combo) + 1, milestones.Count) });

                var cases = FogBugzGateway.GetCases(combo.ProjectId, combo.MileStoneId, combo.SubProjectParentCaseId, null, fbClient);

                var caseSet = new CaseSet();
                caseSet.Cases = cases;
                caseSet.Milestone = new Milestone(combo.Milestone);
                caseSet.Project =  new Project(combo.Project);

                var projectStatusCalculator = new ProjectStatusCalculator(caseSet, persons, isLoadedByAdmin, fbSchedule);

                var status = projectStatusCalculator.GetProjectStatus();

                var eventArgs = new ProjectStatusListEventArgs(combo, status, _cacheKey);

                SaveStatus(eventArgs);

                return 0;
            };

            var factory = new TaskFactory();

            var tasks = new Task<int>[milestones.Count()];

            for (int i = 0; i < milestones.Count(); i++)
            {
                tasks[i] = factory.StartNew(action, milestones[i]);
            }

            return GenKeysString(milestones);
        }
        public ActionResult AsyncDashboard(int projectId, int milestoneId, int? subProjectParentCaseId = null)
        {
            var today = DateTime.Today;

            var model = new AsyncDashboardViewModel();
            model.Setup(projectId, milestoneId, subProjectParentCaseId);

            string cacheKey = MsCacheKey.GenCaseSetKey(projectId, milestoneId, subProjectParentCaseId);
            model.MsCache.CreatedAt = MsCache.GetObjectExpirationTime(cacheKey);

            UpdateViewBagForProjectLayout(model);

            IList<Case> cases = new List<Case>();

            Case projectParentCase = null;
            if (subProjectParentCaseId.HasValue)
            {
                var client = FogBugzGateway.GetClientForParallel();
                projectParentCase = client.GetCase(subProjectParentCaseId.Value);
            }

            foreach (var casesWithMilestone in model.CaseSet.Cases.Where(casesWithMilestone => casesWithMilestone.IndexFixFor == milestoneId))
            {
                cases.Add(casesWithMilestone);
            }

            var fbSchedule = FogBugzGateway.GetFbSchedule();

            if (model.CaseSet.Milestone != null)
            {
                model.DatesSection.StartDate = model.CaseSet.Milestone.DateStart;
                if (model.DatesSection.StartDate.HasValue)
                {
                    model.DatesSection.StartDateDaysRemaining = DateUtils.Difference(today, model.DatesSection.StartDate.Value);
                }

                model.DatesSection.CodeFreeze = (projectParentCase != null && projectParentCase.DateDue != null) ? projectParentCase.DateDue : MilestoneUtils.CodeFreezeCalculate(projectId, model.Milestone, model.SubProjectParentCaseId, model.CaseSet.Milestone.Name, CodeFreezeSubstringRegex);

                if (model.DatesSection.CodeFreeze.HasValue)
                {
                    model.DatesSection.CodeFreezeDaysRemaining = DateUtils.Difference(today, model.DatesSection.CodeFreeze.Value);
                    //model.DatesSection.CodeFreezeHolidaysBefore = HolidaysUtils.GetHolidaysInRange(today, model.DatesSection.CodeFreeze.Value);

                    model.DatesSection.CodeFreezeHolidaysBefore = fbSchedule.GetSiteScheduledDays(today, model.DatesSection.CodeFreeze.Value).TimeOffRanges;
                }

                model.DatesSection.Rollout = projectParentCase != null ? (model.CaseSet.Milestone.Name.Contains("CF") ? DateUtils.GetCodeFreezeFromName(model.CaseSet.Milestone.Name, CodeFreezeSubstringRegex) : null) : model.CaseSet.Milestone.DateRelease;
                if (model.DatesSection.Rollout.HasValue)
                {
                    model.DatesSection.RolloutDaysRemaining = DateUtils.Difference(today, model.DatesSection.Rollout.Value);
                    //model.DatesSection.RolloutHolidaysBefore = HolidaysUtils.GetHolidaysInRange(today, model.DatesSection.Rollout.Value);
                    model.DatesSection.RolloutHolidaysBefore = fbSchedule.GetSiteScheduledDays(today, model.DatesSection.Rollout.Value).TimeOffRanges;
                }

                CalculatePlotTimes(model.DatesSection);

                model.DatesSection.IsActiveProject = today >= model.DatesSection.StartDate && today <= model.DatesSection.Rollout;
            }

            var persons = FogBugzGateway.GetPersons();
            var isLoadedByAdmin = FogBugzGateway.GetIsScheduleLoadedByAdmin();

            var projectStatusCalculator = new ProjectStatusCalculator(model.CaseSet, persons.ToList(), isLoadedByAdmin, fbSchedule);
            model.ProjectStatus = projectStatusCalculator.GetProjectStatus();

            return PartialView("_AsyncDashboard", model);
        }
        public ActionResult AsyncDashboardNotStarted(int projectId, int milestoneId, int? subProjectParentCaseId = null)
        {
            var today = DateTime.Today;

            var model = new AsyncDashboardNotStartedViewModel();
            model.Setup(projectId, milestoneId, subProjectParentCaseId);

            string cacheKey = MsCacheKey.GenCaseSetKey(projectId, milestoneId, subProjectParentCaseId);
            model.MsCache.CreatedAt = MsCache.GetObjectExpirationTime(cacheKey);

            UpdateViewBagForProjectLayout(model);

            IList<Case> cases = new List<Case>();

            foreach (var casesWithMilestone in model.CaseSet.Cases.Where(casesWithMilestone => casesWithMilestone.IndexFixFor == milestoneId))
            {
                cases.Add(casesWithMilestone);
            }

            model.CasesSection.Total = cases.Count();
            model.CasesSection.Active = cases.Count(CaseUtils.IsActive);
            model.CasesSection.Resolved = cases.Count(CaseUtils.IsResolved);
            model.CasesSection.Verified = cases.Count(CaseUtils.IsResolvedVerified);
            model.CasesSection.Closed = cases.Count(CaseUtils.IsClosed);

            model.TimeSection.TotalEstimated = cases.Sum(@case => @case.HoursCurrentEstimate.GetValueOrDefault(0));
            model.TimeSection.Elapsed = cases.Sum(@case => @case.HoursElapsed.GetValueOrDefault(0));
            model.TimeSection.ActiveEstimated = cases.Where(CaseUtils.IsActive).Sum(@case => @case.HoursCurrentEstimate.GetValueOrDefault(0));
            model.TimeSection.ActiveRemaining = cases.Where(CaseUtils.IsActive).Sum(@case => Math.Max(
                0, @case.HoursCurrentEstimate.GetValueOrDefault(0) - @case.HoursElapsed.GetValueOrDefault(0)));

            model.EstimatesSection.WithEstimates = cases.Where(CaseUtils.IsActive).Count(@case => @case.HoursCurrentEstimate.GetValueOrDefault(0) > 0);
            model.EstimatesSection.WithoutEstimates = cases.Count(CaseUtils.IsActive) - model.EstimatesSection.WithEstimates;

            //at this point model.EstimatesSection.WithoutEstimates can be negative.
            //it is possible when cases which has subcases (IndexBugChildren.Count>0) are estimated too
            if (model.EstimatesSection.WithoutEstimates < 0)
                model.EstimatesSection.WithoutEstimates = 0;
            model.EstimatesSection.GoingOverEstimate = cases.Where(CaseUtils.IsActive).Count(
                @case => @case.HoursElapsed.GetValueOrDefault(0) > @case.HoursCurrentEstimate.GetValueOrDefault(0));

            model.AccuracySection.EstimatedTime = cases.Where(@case => CaseUtils.IsResolved(@case) || CaseUtils.IsClosed(@case)).Sum(@case => @case.HoursCurrentEstimate.GetValueOrDefault(0));
            model.AccuracySection.ActualTime = cases.Where(@case => CaseUtils.IsResolved(@case) || CaseUtils.IsClosed(@case)).Sum(@case => @case.HoursElapsed.GetValueOrDefault(0));

            Case projectParentCase = null;
            if (subProjectParentCaseId.HasValue)
            {
                var client = FogBugzGateway.GetClientForParallel();
                projectParentCase = client.GetCase(subProjectParentCaseId.Value);
            }

            foreach (var casesWithMilestone in model.CaseSet.Cases.Where(casesWithMilestone => casesWithMilestone.IndexFixFor == milestoneId))
            {
                cases.Add(casesWithMilestone);
            }

            var fbSchedule = FogBugzGateway.GetFbSchedule();

            if (model.CaseSet.Milestone != null)
            {
                model.DatesSection.StartDate = model.CaseSet.Milestone.DateStart;
                if (model.DatesSection.StartDate.HasValue)
                {
                    model.DatesSection.StartDateDaysRemaining = DateUtils.Difference(today, model.DatesSection.StartDate.Value);
                }

                model.DatesSection.CodeFreeze = (projectParentCase != null && projectParentCase.DateDue != null) ? projectParentCase.DateDue : MilestoneUtils.CodeFreezeCalculate(projectId, model.Milestone, model.SubProjectParentCaseId, model.CaseSet.Milestone.Name, CodeFreezeSubstringRegex);
                if (model.DatesSection.CodeFreeze.HasValue)
                {
                    model.DatesSection.CodeFreezeDaysRemaining = DateUtils.Difference(today, model.DatesSection.CodeFreeze.Value);

                    model.DatesSection.CodeFreezeHolidaysBefore = fbSchedule.GetSiteScheduledDays(today, model.DatesSection.CodeFreeze.Value).TimeOffRanges;
                }

                model.DatesSection.Rollout = projectParentCase != null ? (model.CaseSet.Milestone.Name.Contains("CF") ? DateUtils.GetCodeFreezeFromName(model.CaseSet.Milestone.Name, CodeFreezeSubstringRegex) : null) : model.CaseSet.Milestone.DateRelease;
                if (model.DatesSection.Rollout.HasValue)
                {
                    model.DatesSection.RolloutDaysRemaining = DateUtils.Difference(today, model.DatesSection.Rollout.Value);
                    model.DatesSection.RolloutHolidaysBefore = fbSchedule.GetSiteScheduledDays(today, model.DatesSection.Rollout.Value).TimeOffRanges;
                }

                CalculatePlotTimesNotStarted(model.DatesSection);

                model.DatesSection.IsActiveProject = today >= model.DatesSection.StartDate && today <= model.DatesSection.Rollout;
            }

            var persons = FogBugzGateway.GetPersons();
            var isLoadedByAdmin = FogBugzGateway.GetIsScheduleLoadedByAdmin();

            var projectStatusCalculator = new ProjectStatusCalculator(model.CaseSet, persons.ToList(), isLoadedByAdmin, fbSchedule);
            model.ProjectStatus = projectStatusCalculator.GetProjectStatus();

            return PartialView("_AsyncDashboardNotStarted", model);
        }
        public void TestProjectStatusCalculator()
        {
            var projectStatusCalculator = new ProjectStatusCalculator(_caseSet, _persons, _isScheduleLoadedByAdmin, _schedule);

            var result = projectStatusCalculator.GetProjectStatus();
        }