/// <summary>
        /// Creates statuses other then <see cref="ObservationState.Running"/> based on <paramref name="projectData"/>. Running build must be handled separately.
        /// </summary>
        /// <param name="projectData">Data retrieved from server.</param>
        /// <returns>Appropriate <see cref="ObservationState"/>.</returns>
        private static ObservationState CcStatusToObservationStatus(CruiseControlJob projectData)
        {
            if (projectData.LastBuildStatus == CcBuildStatus.Success && projectData.MessageList.MessagesSafe.All(msg => msg.Kind != MessageKind.FailingTasks && msg.Kind != MessageKind.Breakers && msg.Kind != MessageKind.BuildAbortedBy))
            {
                return(ObservationState.Success);
            }

            if (projectData.LastBuildStatus == CcBuildStatus.Failure || projectData.LastBuildStatus == CcBuildStatus.Exception || projectData.MessageList.MessagesSafe.Any(msg => msg.Kind != MessageKind.FailingTasks && msg.Kind != MessageKind.Breakers))
            {
                return(ObservationState.Failure);
            }

            return(ObservationState.Unknown);
        }
        private CruiseControlStatus CreateStatus(CruiseControlJob job)
        {
            var result = new CruiseControlStatus();

            result.Name     = job.Name;
            result.Details  = job.Description;
            result.Building = job.Activity == ActivityConstants.Building;
            result.Pending  = job.Activity == ActivityConstants.Pending;
            result.CheckingModifications = job.Activity == ActivityConstants.CheckingModifications;
            if (int.TryParse(job.LastBuildLabel, out var buildNr))
            {
                result.BuildNumber = buildNr;
            }

            result.Url = job.WebUrl;
            SetCulprits(job, result);

            result.NextBuildTime = job.NextBuildTime;
            result.LastBuildTime = job.LastBuildTime;

            if (result.Building)
            {
                result.State = ObservationState.Running;
            }
            else
            {
                result.State = CcStatusToObservationStatus(job);
            }

            if (result.Building)
            {
                result.Duration = DateTime.Now - job.NextBuildTime;
                result.Time     = job.NextBuildTime.ToUniversalTime();
            }
            else
            {
                result.Time = DateTime.UtcNow;
            }

            result.EstimatedDuration = this.estimatedDuration;

            return(result);
        }
        private static void SetCulprits(CruiseControlJob job, CruiseControlStatus result)
        {
            var breakers = job.MessageList.MessagesSafe.Where(msg => msg.Kind == MessageKind.Breakers);

            foreach (var breakersLine in breakers)
            {
                foreach (var breaker in breakersLine.Text.Split(',').Select(brk => brk.Trim()).Where(brk => !string.IsNullOrEmpty(brk)))
                {
                    if (result.Culprits.Any(brk => brk.Name == breaker))
                    {
                        continue;
                    }

                    var culprit = new CruiseControlUser {
                        Name = breaker
                    };
                    result.Culprits.Add(culprit);
                }
            }
        }