Example #1
0
            /// <summary>
            /// Validates the parent object.
            /// </summary>
            public WatchShiftValidator()
            {
                RuleFor(x => x.Points).GreaterThanOrEqualTo(0);
                RuleFor(x => x.ShiftType).NotEmpty();
                RuleFor(x => x.Title).NotEmpty().Length(1, 50);

#pragma warning disable CS0618 // Type or member is obsolete
                Custom(watchShift =>
                {
                    if (watchShift.Range.Start == default(DateTime) || watchShift.Range.End == default(DateTime))
                    {
                        return(new FluentValidation.Results.ValidationFailure(PropertySelector.SelectPropertyFrom <WatchShift>(x => x.Range).Name, "The watch shift's range dates must make sense.  Please."));
                    }

                    if (watchShift.Range.Start >= watchShift.Range.End)
                    {
                        return(new FluentValidation.Results.ValidationFailure(PropertySelector.SelectPropertyFrom <WatchShift>(x => x.Range).Name, "The watch shift's range dates must make sense.  Please."));
                    }

                    if (watchShift.Range.End <= watchShift.Range.Start)
                    {
                        return(new FluentValidation.Results.ValidationFailure(PropertySelector.SelectPropertyFrom <WatchShift>(x => x.Range).Name, "The watch shift's range dates must make sense.  Please."));
                    }

                    return(null);
                });
#pragma warning restore CS0618 // Type or member is obsolete
            }
        /// <summary>
        /// This method is responsible for advancing the watchbill into different states.
        /// At each state, different actions must be taken.
        /// </summary>
        /// <param name="desiredState"></param>
        /// <param name="setTime"></param>
        /// <param name="person"></param>
        /// <param name="session"></param>
        public virtual void SetState(WatchbillStatus desiredState, DateTime setTime, Person person, ISession session)
        {
            //Don't allow same changes.
            if (this.CurrentState == desiredState)
            {
                throw new Exception("Can't set the state to its same value.");
            }

            //If we set a watchbill's state to initial, then remove all the assignments from it,
            //leaving a watchbill with only its days and shifts.
            if (desiredState == ReferenceLists.ReferenceListHelper <WatchbillStatus> .Find("Initial"))
            {
                this.InputRequirements.Clear();
                this.WatchInputs.Clear();

                foreach (var shift in this.WatchShifts)
                {
                    if (shift.WatchAssignment != null)
                    {
                        session.Delete(shift.WatchAssignment);
                        shift.WatchAssignment = null;
                    }
                }

                //We also need to remove the job.
                if (FluentScheduler.JobManager.RunningSchedules.Any(x => x.Name == this.Id.ToString()))
                {
                    FluentScheduler.JobManager.RemoveJob(this.Id.ToString());
                }
            }
            //Inform all the people who need to provide inputs along with all the people who are in its chain of command.
            else if (desiredState == ReferenceLists.ReferenceListHelper <WatchbillStatus> .Find("Open for Inputs"))
            {
                if (this.CurrentState == null || this.CurrentState != ReferenceLists.ReferenceListHelper <WatchbillStatus> .Find("Initial"))
                {
                    throw new Exception("You may not move to the open for inputs state from anything other than the initial state.");
                }

                foreach (var elPerson in this.EligibilityGroup.EligiblePersons)
                {
                    this.InputRequirements.Add(new WatchInputRequirement
                    {
                        Id     = Guid.NewGuid(),
                        Person = elPerson
                    });
                }

                var emailAddressesByPerson = this.EligibilityGroup.EligiblePersons
                                             .Select(x => new KeyValuePair <string, List <System.Net.Mail.MailAddress> >(x.ToString(), x.EmailAddresses.Where(y => y.IsPreferred).Select(y => new System.Net.Mail.MailAddress(y.Address, x.ToString())).ToList())).ToList();

                //Start a new task to send all the emails.
                Task.Run(() =>
                {
                    foreach (var group in emailAddressesByPerson)
                    {
                        var model = new Email.Models.WatchbillInputRequiredEmailModel {
                            FriendlyName = group.Key, Watchbill = this.Title
                        };

                        Email.EmailInterface.CCEmailMessage
                        .CreateDefault()
                        .To(group.Value)
                        .CC(Email.EmailInterface.CCEmailMessage.DeveloperAddress)
                        .Subject("Watchbill Inputs Required")
                        .HTMLAlternateViewUsingTemplateFromEmbedded("CommandCentral.Email.Templates.WatchbillInputRequired_HTML.html", model)
                        .SendWithRetryAndFailure(TimeSpan.FromSeconds(1));
                    }
                }).ConfigureAwait(false);


                //We now also need to load all persons in the watchbill's chain of command
                var groups = new Authorization.Groups.PermissionGroup[] { new Authorization.Groups.Definitions.CommandQuarterdeckWatchbill(),
                                                                          new Authorization.Groups.Definitions.DepartmentQuarterdeckWatchbill(),
                                                                          new Authorization.Groups.Definitions.DivisionQuarterdeckWatchbill() }
                .Select(x => x.GroupName)
                .ToList();

                using (var internalSession = DataAccess.DataProvider.CreateStatefulSession())
                    using (var transaction = internalSession.BeginTransaction())
                    {
                        try
                        {
                            var queryString = "from Person as person where (";
                            for (var x = 0; x < groups.Count; x++)
                            {
                                queryString += " '{0}' in elements(person.{1}) ".With(groups[x],
                                                                                      PropertySelector.SelectPropertyFrom <Person>(y => y.PermissionGroupNames).Name);
                                if (x + 1 != groups.Count)
                                {
                                    queryString += " or ";
                                }
                            }
                            queryString += " ) and person.Command = :command";
                            var persons = internalSession.CreateQuery(queryString)
                                          .SetParameter("command", this.Command)
                                          .List <Person>();

                            //Now with these people who are the duty holders.
                            var collateralEmailAddresses = persons.Select(x =>
                                                                          x.EmailAddresses.Where(y => y.IsPreferred).Select(y => new System.Net.Mail.MailAddress(y.Address, x.ToString())).ToList()).ToList();

                            var uniqueWatchQuals = this.WatchShifts.SelectMany(x => x.ShiftType.RequiredWatchQualifications).Distinct();
                            var personNamesWithoutWatchQualifications = this.EligibilityGroup.EligiblePersons.Where(p => !p.WatchQualifications.Any(qual => uniqueWatchQuals.Contains(qual))).Select(x => x.ToString()).ToList();

                            Task.Run(() =>
                            {
                                var model = new Email.Models.WatchbillOpenForInputsEmailModel {
                                    WatchbillTitle = this.Title, NotQualledPersonsFriendlyNames = personNamesWithoutWatchQualifications
                                };

                                foreach (var addressGroup in collateralEmailAddresses)
                                {
                                    Email.EmailInterface.CCEmailMessage
                                    .CreateDefault()
                                    .To(addressGroup)
                                    .CC(Email.EmailInterface.CCEmailMessage.DeveloperAddress)
                                    .Subject("Watchbill Open For Inputs")
                                    .HTMLAlternateViewUsingTemplateFromEmbedded("CommandCentral.Email.Templates.WatchbillOpenForInputs_HTML.html", model)
                                    .SendWithRetryAndFailure(TimeSpan.FromSeconds(1));
                                }
                            }).ConfigureAwait(false);


                            transaction.Commit();
                        }
                        catch
                        {
                            transaction.Rollback();
                            throw;
                        }
                    }

                //We now need to register the job that will send emails every day to alert people to the inputs they are responsible for.
                FluentScheduler.JobManager.AddJob(() => SendWatchInputRequirementsAlertEmail(this.Id), s => s.WithName(this.Id.ToString()).ToRunNow().AndEvery(1).Days().At(4, 0));
            }
            //Inform everyone in the chain of command that the watchbill is closed for inputs.
            else if (desiredState == ReferenceLists.ReferenceListHelper <WatchbillStatus> .Find("Closed for Inputs"))
            {
                if (this.CurrentState == null || this.CurrentState != ReferenceLists.ReferenceListHelper <WatchbillStatus> .Find("Open for Inputs"))
                {
                    throw new Exception("You may not move to the closed for inputs state from anything other than the open for inputs state.");
                }

                //We now also need to load all persons in the watchbill's chain of command.
                var groups = new Authorization.Groups.PermissionGroup[] { new Authorization.Groups.Definitions.CommandQuarterdeckWatchbill(),
                                                                          new Authorization.Groups.Definitions.DepartmentQuarterdeckWatchbill(),
                                                                          new Authorization.Groups.Definitions.DivisionQuarterdeckWatchbill() }
                .Select(x => x.GroupName)
                .ToList();

                using (var internalSession = DataAccess.DataProvider.CreateStatefulSession())
                    using (var transaction = internalSession.BeginTransaction())
                    {
                        try
                        {
                            var queryString = "from Person as person where (";
                            for (var x = 0; x < groups.Count; x++)
                            {
                                queryString += " '{0}' in elements(person.{1}) ".With(groups[x],
                                                                                      PropertySelector.SelectPropertyFrom <Person>(y => y.PermissionGroupNames).Name);
                                if (x + 1 != groups.Count)
                                {
                                    queryString += " or ";
                                }
                            }
                            queryString += " ) and person.Command = :command";
                            var persons = internalSession.CreateQuery(queryString)
                                          .SetParameter("command", this.Command)
                                          .List <Person>();

                            //Now with these people who are the duty holders.
                            var collateralEmailAddresses = persons.Select(x =>
                                                                          x.EmailAddresses.Where(y => y.IsPreferred).Select(y => new System.Net.Mail.MailAddress(y.Address, x.ToString())).ToList()).ToList();

                            Task.Run(() =>
                            {
                                var model = new Email.Models.WatchbillClosedForInputsEmailModel {
                                    Watchbill = this.Title
                                };

                                foreach (var addressGroup in collateralEmailAddresses)
                                {
                                    Email.EmailInterface.CCEmailMessage
                                    .CreateDefault()
                                    .To(addressGroup)
                                    .CC(Email.EmailInterface.CCEmailMessage.DeveloperAddress)
                                    .Subject("Watchbill Closed For Inputs")
                                    .HTMLAlternateViewUsingTemplateFromEmbedded("CommandCentral.Email.Templates.WatchbillClosedForInputs_HTML.html", model)
                                    .SendWithRetryAndFailure(TimeSpan.FromSeconds(1));
                                }
                            }).ConfigureAwait(false);


                            transaction.Commit();
                        }
                        catch
                        {
                            transaction.Rollback();
                            throw;
                        }
                    }

                if (FluentScheduler.JobManager.RunningSchedules.Any(x => x.Name == this.Id.ToString()))
                {
                    FluentScheduler.JobManager.RemoveJob(this.Id.ToString());
                }
            }
            //Make sure there are assignments for each shift.
            //Inform the chain of command that the watchbill is open for review.
            else if (desiredState == ReferenceLists.ReferenceListHelper <WatchbillStatus> .Find("Under Review"))
            {
                if (this.CurrentState == null || this.CurrentState != ReferenceLists.ReferenceListHelper <WatchbillStatus> .Find("Closed for Inputs"))
                {
                    throw new Exception("You may not move to the under review state from anything other than the closed for inputs state.");
                }

                if (!this.WatchShifts.All(y => y.WatchAssignment != null))
                {
                    throw new Exception("A watchbill may not move into the 'Under Review' state unless the all watch shifts have been assigned.");
                }

                //We now also need to load all persons in the watchbill's chain of command.
                var groups = new Authorization.Groups.PermissionGroup[] { new Authorization.Groups.Definitions.CommandQuarterdeckWatchbill(),
                                                                          new Authorization.Groups.Definitions.DepartmentQuarterdeckWatchbill(),
                                                                          new Authorization.Groups.Definitions.DivisionQuarterdeckWatchbill() }
                .Select(x => x.GroupName)
                .ToList();

                using (var internalSession = DataAccess.DataProvider.CreateStatefulSession())
                    using (var transaction = internalSession.BeginTransaction())
                    {
                        try
                        {
                            var queryString = "from Person as person where (";
                            for (var x = 0; x < groups.Count; x++)
                            {
                                queryString += " '{0}' in elements(person.{1}) ".With(groups[x],
                                                                                      PropertySelector.SelectPropertyFrom <Person>(y => y.PermissionGroupNames).Name);
                                if (x + 1 != groups.Count)
                                {
                                    queryString += " or ";
                                }
                            }
                            queryString += " ) and person.Command = :command";
                            var persons = internalSession.CreateQuery(queryString)
                                          .SetParameter("command", this.Command)
                                          .List <Person>();

                            //Now with these people who are the duty holders.
                            var collateralEmailAddresses = persons.Select(x =>
                                                                          x.EmailAddresses.Where(y => y.IsPreferred).Select(y => new System.Net.Mail.MailAddress(y.Address, x.ToString())).ToList()).ToList();

                            Task.Run(() =>
                            {
                                var model = new Email.Models.WatchbillUnderReviewEmailModel {
                                    Watchbill = this.Title
                                };

                                foreach (var addressGroup in collateralEmailAddresses)
                                {
                                    Email.EmailInterface.CCEmailMessage
                                    .CreateDefault()
                                    .To(addressGroup)
                                    .CC(Email.EmailInterface.CCEmailMessage.DeveloperAddress)
                                    .Subject("Watchbill Under Review")
                                    .HTMLAlternateViewUsingTemplateFromEmbedded("CommandCentral.Email.Templates.WatchbillUnderReview_HTML.html", model)
                                    .SendWithRetryAndFailure(TimeSpan.FromSeconds(1));
                                }
                            }).ConfigureAwait(false);



                            transaction.Commit();
                        }
                        catch
                        {
                            transaction.Rollback();
                            throw;
                        }
                    }
            }
            //Move the watchbill into its published state, tell everyone who has watch which watches they have.
            //Tell the chain of command the watchbill is published.
            else if (desiredState == ReferenceLists.ReferenceListHelper <WatchbillStatus> .Find("Published"))
            {
                if (this.CurrentState == null || this.CurrentState != ReferenceLists.ReferenceListHelper <WatchbillStatus> .Find("Under Review"))
                {
                    throw new Exception("You may not move to the published state from anything other than the under review state.");
                }

                //Let's send an email to each person who is on watch, informing them of their watches.
                var assignmentsByPerson = this.WatchShifts.Select(x => x.WatchAssignment)
                                          .GroupBy(x => x.PersonAssigned);

                foreach (var assignments in assignmentsByPerson)
                {
                    var model = new Email.Models.WatchAssignedEmailModel {
                        FriendlyName = assignments.Key.ToString(), WatchAssignments = assignments.ToList(), Watchbill = this.Title
                    };

                    var emailAddresses = assignments.Key.EmailAddresses.Where(x => x.IsPreferred).Select(x => new System.Net.Mail.MailAddress(x.Address, assignments.Key.ToString())).ToList();

                    Task.Run(() =>
                    {
                        Email.EmailInterface.CCEmailMessage
                        .CreateDefault()
                        .To(emailAddresses)
                        .CC(Email.EmailInterface.CCEmailMessage.DeveloperAddress)
                        .Subject("Watch Assigned")
                        .HTMLAlternateViewUsingTemplateFromEmbedded("CommandCentral.Email.Templates.WatchAssigned_HTML.html", model)
                        .SendWithRetryAndFailure(TimeSpan.FromSeconds(1));
                    }).ConfigureAwait(false);
                }

                //Let's send emails to all the coordinators.
                //We now also need to load all persons in the watchbill's chain of command.
                var groups = new Authorization.Groups.PermissionGroup[] { new Authorization.Groups.Definitions.CommandQuarterdeckWatchbill(),
                                                                          new Authorization.Groups.Definitions.DepartmentQuarterdeckWatchbill(),
                                                                          new Authorization.Groups.Definitions.DivisionQuarterdeckWatchbill() }
                .Select(x => x.GroupName)
                .ToList();

                using (var internalSession = DataAccess.DataProvider.CreateStatefulSession())
                    using (var transaction = internalSession.BeginTransaction())
                    {
                        try
                        {
                            var queryString = "from Person as person where (";
                            for (var x = 0; x < groups.Count; x++)
                            {
                                queryString += " '{0}' in elements(person.{1}) ".With(groups[x],
                                                                                      PropertySelector.SelectPropertyFrom <Person>(y => y.PermissionGroupNames).Name);
                                if (x + 1 != groups.Count)
                                {
                                    queryString += " or ";
                                }
                            }
                            queryString += " ) and person.Command = :command";
                            var persons = internalSession.CreateQuery(queryString)
                                          .SetParameter("command", this.Command)
                                          .List <Person>();

                            //Now with these people who are the duty holders, get their preferred email addresses.
                            var collateralEmailAddresses = persons.Select(x =>
                                                                          x.EmailAddresses.Where(y => y.IsPreferred).Select(y => new System.Net.Mail.MailAddress(y.Address, x.ToString())).ToList()).ToList();

                            Task.Run(() =>
                            {
                                var model = new Email.Models.WatchbillPublishedEmailModel {
                                    Watchbill = this.Title
                                };

                                foreach (var addressGroup in collateralEmailAddresses)
                                {
                                    Email.EmailInterface.CCEmailMessage
                                    .CreateDefault()
                                    .To(addressGroup)
                                    .CC(Email.EmailInterface.CCEmailMessage.DeveloperAddress)
                                    .Subject("Watchbill Published")
                                    .HTMLAlternateViewUsingTemplateFromEmbedded("CommandCentral.Email.Templates.WatchbillPublished_HTML.html", model)
                                    .SendWithRetryAndFailure(TimeSpan.FromSeconds(1));
                                }
                            }).ConfigureAwait(false);

                            transaction.Commit();
                        }
                        catch
                        {
                            transaction.Rollback();
                            throw;
                        }
                    }
            }
            else
            {
                throw new NotImplementedException("Not implemented default case in the set watchbill state method.");
            }

            this.CurrentState       = desiredState;
            this.LastStateChange    = setTime;
            this.LastStateChangedBy = person;
        }