diff --git a/src/PrayerTracker.Data/DataAccess.fs b/src/PrayerTracker.Data/DataAccess.fs index 46a39f8..b3a3ca0 100644 --- a/src/PrayerTracker.Data/DataAccess.fs +++ b/src/PrayerTracker.Data/DataAccess.fs @@ -98,11 +98,11 @@ type AppDbContext with | query when activeOnly -> let asOf = theDate.AddDays(-(float grp.preferences.daysToExpire)).Date query.Where(fun pr -> - (pr.updatedDate > asOf - || pr.doNotExpire - || RequestType.Recurring = pr.requestType - || RequestType.Expecting = pr.requestType) - && not pr.isManuallyExpired) + ( pr.updatedDate > asOf + || pr.expiration = Manual + || pr.requestType = LongTermRequest + || pr.requestType = Expecting) + && pr.expiration <> Forced) | query -> query |> reqSort grp.preferences.requestSort |> function diff --git a/src/PrayerTracker.Data/Entities.fs b/src/PrayerTracker.Data/Entities.fs index 824a995..442e974 100644 --- a/src/PrayerTracker.Data/Entities.fs +++ b/src/PrayerTracker.Data/Entities.fs @@ -6,35 +6,6 @@ open NodaTime open System open System.Collections.Generic -(*-- CONSTANTS --*) - -/// Constants to use for the e-mail type parameter -[] -module EmailType = - /// HTML e-mail - [] - let Html = "Html" - /// Plain Text e-mail - [] - let PlainText = "PlainText" - /// E-mail with the list as an attached PDF - [] - let AttachedPdf = "AttachedPdf" - -/// These values match those in the RequestType document store -[] -module RequestType = - /// Current Requests (follow expiration rules) - let Current = "Current" - /// Long-Term / Recurring Requests (do not automatically expire) - let Recurring = "Recurring" - /// Praise Reports (follow expiration rules) - let Praise = "Praise" - /// Expectant Mothers (do not automatically expire) - let Expecting = "Expecting" - /// Announcements (follow expiration rules) - let Announcement = "Announcement" - (*-- SUPPORT TYPES --*) /// How as-of dates should (or should not) be displayed with requests @@ -61,6 +32,82 @@ with | LongDate -> "L" +/// Acceptable e-mail formats +type EmailFormat = + /// HTML e-mail + | HtmlFormat + /// Plain-text e-mail + | PlainTextFormat +with + /// Convert to a DU case from a single-character string + static member fromCode code = + match code with + | "H" -> HtmlFormat + | "P" -> PlainTextFormat + | _ -> invalidArg "code" (sprintf "Unknown code %s" code) + /// Convert this DU case to a single-character string + member this.code = + match this with + | HtmlFormat -> "H" + | PlainTextFormat -> "P" + + +/// Expiration for requests +type Expiration = + /// Follow the rules for normal expiration + | Automatic + /// Do not expire via rules + | Manual + /// Force immediate expiration + | Forced +with + /// Convert to a DU case from a single-character string + static member fromCode code = + match code with + | "A" -> Automatic + | "M" -> Manual + | "F" -> Forced + | _ -> invalidArg "code" (sprintf "Unknown code %s" code) + /// Convert this DU case to a single-character string + member this.code = + match this with + | Automatic -> "A" + | Manual -> "M" + | Forced -> "F" + + +/// Types of prayer requests +type PrayerRequestType = + /// Current requests + | CurrentRequest + /// Long-term/ongoing request + | LongTermRequest + /// Expectant couples + | Expecting + /// Praise reports + | PraiseReport + /// Announcements + | Announcement +with + /// Convert to a DU case from a single-character string + static member fromCode code = + match code with + | "C" -> CurrentRequest + | "L" -> LongTermRequest + | "E" -> Expecting + | "P" -> PraiseReport + | "A" -> Announcement + | _ -> invalidArg "code" (sprintf "Unknown code %s" code) + /// Convert this DU case to a single-character string + member this.code = + match this with + | CurrentRequest -> "C" + | LongTermRequest -> "L" + | Expecting -> "E" + | PraiseReport -> "P" + | Announcement -> "A" + + /// How requests should be sorted type RequestSort = /// Sort by date, then by requestor/subject @@ -96,6 +143,26 @@ module Converters = |> LeafExpressionConverter.QuotationToExpression |> unbox>> + let private emailFromDU = + <@ Func(fun (x : EmailFormat) -> x.code) @> + |> LeafExpressionConverter.QuotationToExpression + |> unbox>> + + let private emailToDU = + <@ Func(EmailFormat.fromCode) @> + |> LeafExpressionConverter.QuotationToExpression + |> unbox>> + + let private expFromDU = + <@ Func(fun (x : Expiration) -> x.code) @> + |> LeafExpressionConverter.QuotationToExpression + |> unbox>> + + let private expToDU = + <@ Func(Expiration.fromCode) @> + |> LeafExpressionConverter.QuotationToExpression + |> unbox>> + let private sortFromDU = <@ Func(fun (x : RequestSort) -> x.code) @> |> LeafExpressionConverter.QuotationToExpression @@ -106,10 +173,32 @@ module Converters = |> LeafExpressionConverter.QuotationToExpression |> unbox>> + let private typFromDU = + <@ Func(fun (x : PrayerRequestType) -> x.code) @> + |> LeafExpressionConverter.QuotationToExpression + |> unbox>> + + let private typToDU = + <@ Func(PrayerRequestType.fromCode) @> + |> LeafExpressionConverter.QuotationToExpression + |> unbox>> + /// Conversion between a string and an AsOfDateDisplay DU value type AsOfDateDisplayConverter () = inherit ValueConverter (asOfFromDU, asOfToDU) + /// Conversion between a string and an EmailFormat DU value + type EmailFormatConverter () = + inherit ValueConverter (emailFromDU, emailToDU) + + /// Conversion between a string and an Expiration DU value + type ExpirationConverter () = + inherit ValueConverter (expFromDU, expToDU) + + /// Conversion between a string and an AsOfDateDisplay DU value + type PrayerRequestTypeConverter () = + inherit ValueConverter (typFromDU, typToDU) + /// Conversion between a string and a RequestSort DU value type RequestSortConverter () = inherit ValueConverter (sortFromDU, sortToDU) @@ -227,7 +316,7 @@ and [] ListPreferences = /// The password used for "small group login" (view-only request list) groupPassword : string /// The default e-mail type for this class - defaultEmailType : string + defaultEmailType : EmailFormat /// Whether this class makes its request list public isPublic : bool /// The time zone which this class uses (use tzdata names) @@ -255,7 +344,7 @@ and [] ListPreferences = textFontSize = 12 requestSort = SortByDate groupPassword = "" - defaultEmailType = EmailType.Html + defaultEmailType = HtmlFormat isPublic = false timeZoneId = "America/Denver" timeZone = TimeZone.empty @@ -333,7 +422,7 @@ and [] ListPreferences = m.Property(fun e -> e.defaultEmailType) .HasColumnName("DefaultEmailType") .IsRequired() - .HasDefaultValue EmailType.Html + .HasDefaultValue HtmlFormat |> ignore m.Property(fun e -> e.isPublic) .HasColumnName("IsPublic") @@ -359,6 +448,8 @@ and [] ListPreferences = |> ignore mb.Model.FindEntityType(typeof).FindProperty("requestSort") .SetValueConverter(Converters.RequestSortConverter ()) + mb.Model.FindEntityType(typeof).FindProperty("defaultEmailType") + .SetValueConverter(Converters.EmailFormatConverter ()) mb.Model.FindEntityType(typeof).FindProperty("asOfDateDisplay") .SetValueConverter(Converters.AsOfDateDisplayConverter ()) @@ -374,7 +465,7 @@ and [] Member = /// The e-mail address for the member email : string /// The type of e-mail preferred by this member (see constants) - format : string option + format : string option // TODO - do I need a custom formatter for this? /// The small group to which this member belongs smallGroup : SmallGroup } @@ -405,64 +496,62 @@ and [] Member = /// This represents a single prayer request and [] PrayerRequest = { /// The Id of this request - prayerRequestId : PrayerRequestId + prayerRequestId : PrayerRequestId /// The type of the request - requestType : string + requestType : PrayerRequestType /// The user who entered the request - userId : UserId + userId : UserId /// The small group to which this request belongs - smallGroupId : SmallGroupId + smallGroupId : SmallGroupId /// The date/time on which this request was entered - enteredDate : DateTime + enteredDate : DateTime /// The date/time this request was last updated - updatedDate : DateTime + updatedDate : DateTime /// The name of the requestor or subject, or title of announcement - requestor : string option + requestor : string option /// The text of the request - text : string - /// Whether this request is exempt from standard expiration rules - doNotExpire : bool + text : string /// Whether the chaplain should be notified for this request - notifyChaplain : bool - /// Whether this request has been expired manually - isManuallyExpired : bool + notifyChaplain : bool /// The user who entered this request - user : User + user : User /// The small group to which this request belongs - smallGroup : SmallGroup + smallGroup : SmallGroup + /// Is this request expired? + expiration : Expiration } with /// An empty request static member empty = - { prayerRequestId = Guid.Empty - requestType = RequestType.Current - userId = Guid.Empty - smallGroupId = Guid.Empty - enteredDate = DateTime.MinValue - updatedDate = DateTime.MinValue - requestor = None - text = "" - doNotExpire = false - notifyChaplain = false - isManuallyExpired = false - user = User.empty - smallGroup = SmallGroup.empty + { prayerRequestId = Guid.Empty + requestType = CurrentRequest + userId = Guid.Empty + smallGroupId = Guid.Empty + enteredDate = DateTime.MinValue + updatedDate = DateTime.MinValue + requestor = None + text = "" + notifyChaplain = false + user = User.empty + smallGroup = SmallGroup.empty + expiration = Automatic } /// Is this request expired? member this.isExpired (curr : DateTime) expDays = - match this.isManuallyExpired with - | true -> true // Manual expiration - | false -> - let nonExpiringTypes = [ RequestType.Recurring; RequestType.Expecting ] - match this.doNotExpire || List.contains this.requestType nonExpiringTypes with - | true -> false // No expiration - | false -> curr.AddDays(-(float expDays)) > this.updatedDate // Automatic expiration + match this.expiration with + | Forced -> true + | Manual -> false + | Automatic -> + match this.requestType with + | LongTermRequest + | Expecting -> false + | _ -> curr.AddDays(-(float expDays)) > this.updatedDate // Automatic expiration /// Is an update required for this long-term request? member this.updateRequired curr expDays updWeeks = match this.isExpired curr expDays with | true -> false - | _ -> curr.AddDays(-(float (updWeeks * 7))) > this.updatedDate + | false -> curr.AddDays(-(float (updWeeks * 7))) > this.updatedDate /// Configure EF for this entity static member internal configureEF (mb : ModelBuilder) = @@ -477,12 +566,15 @@ and [] PrayerRequest = m.Property(fun e -> e.updatedDate).HasColumnName "UpdatedDate" |> ignore m.Property(fun e -> e.requestor).HasColumnName "Requestor" |> ignore m.Property(fun e -> e.text).HasColumnName("Text").IsRequired() |> ignore - m.Property(fun e -> e.doNotExpire).HasColumnName "DoNotExpire" |> ignore m.Property(fun e -> e.notifyChaplain).HasColumnName "NotifyChaplain" |> ignore - m.Property(fun e -> e.isManuallyExpired).HasColumnName "IsManuallyExpired" |> ignore) + m.Property(fun e -> e.expiration).HasColumnName "Expiration" |> ignore) |> ignore + mb.Model.FindEntityType(typeof).FindProperty("requestType") + .SetValueConverter(Converters.PrayerRequestTypeConverter ()) mb.Model.FindEntityType(typeof).FindProperty("requestor") .SetValueConverter(OptionConverter ()) + mb.Model.FindEntityType(typeof).FindProperty("expiration") + .SetValueConverter(Converters.ExpirationConverter ()) /// This represents a small group (Sunday School class, Bible study group, etc.) diff --git a/src/PrayerTracker.Data/Migrations/20161217153124_InitialDatabase.fs b/src/PrayerTracker.Data/Migrations/20161217153124_InitialDatabase.fs index 53e02dc..7f5b230 100644 --- a/src/PrayerTracker.Data/Migrations/20161217153124_InitialDatabase.fs +++ b/src/PrayerTracker.Data/Migrations/20161217153124_InitialDatabase.fs @@ -50,17 +50,16 @@ type MemberTable = } type PrayerRequestTable = - { prayerRequestId : OperationBuilder - doNotExpire : OperationBuilder - enteredDate : OperationBuilder - isManuallyExpired : OperationBuilder - notifyChaplain : OperationBuilder - requestType : OperationBuilder - requestor : OperationBuilder - smallGroupId : OperationBuilder - text : OperationBuilder - updatedDate : OperationBuilder - userId : OperationBuilder + { prayerRequestId : OperationBuilder + enteredDate : OperationBuilder + expiration : OperationBuilder + notifyChaplain : OperationBuilder + requestType : OperationBuilder + requestor : OperationBuilder + smallGroupId : OperationBuilder + text : OperationBuilder + updatedDate : OperationBuilder + userId : OperationBuilder } type SmallGroupTable = @@ -104,12 +103,12 @@ type InitialDatabase () = schema = "pt", columns = (fun table -> - { churchId = table.Column (name = "ChurchId", nullable = false) - city = table.Column (name = "City", nullable = false) + { churchId = table.Column (name = "ChurchId", nullable = false) + city = table.Column (name = "City", nullable = false) hasInterface = table.Column (name = "HasVirtualPrayerRoomInterface", nullable = false) interfaceAddress = table.Column (name = "InterfaceAddress", nullable = true) - name = table.Column (name = "Name", nullable = false) - st = table.Column (name = "ST", maxLength = Nullable 2, nullable = false) + name = table.Column (name = "Name", nullable = false) + st = table.Column (name = "ST", nullable = false, maxLength = Nullable 2) }), constraints = fun table -> @@ -121,10 +120,10 @@ type InitialDatabase () = schema = "pt", columns = (fun table -> - { timeZoneId = table.Column (name = "TimeZoneId", nullable = false) + { timeZoneId = table.Column (name = "TimeZoneId", nullable = false) description = table.Column (name = "Description", nullable = false) - isActive = table.Column (name = "IsActive", nullable = false) - sortOrder = table.Column (name = "SortOrder", nullable = false) + isActive = table.Column (name = "IsActive", nullable = false) + sortOrder = table.Column (name = "SortOrder", nullable = false) }), constraints = fun table -> @@ -136,13 +135,13 @@ type InitialDatabase () = schema = "pt", columns = (fun table -> - { userId = table.Column (name = "UserId", nullable = false) - emailAddress = table.Column (name = "EmailAddress", nullable = false) - firstName = table.Column (name = "FirstName", nullable = false) + { userId = table.Column (name = "UserId", nullable = false) + emailAddress = table.Column (name = "EmailAddress", nullable = false) + firstName = table.Column (name = "FirstName", nullable = false) isAdmin = table.Column (name = "IsSystemAdmin", nullable = false) - lastName = table.Column (name = "LastName", nullable = false) - passwordHash = table.Column (name = "PasswordHash", nullable = false) - salt = table.Column (name = "Salt", nullable = true) + lastName = table.Column (name = "LastName", nullable = false) + passwordHash = table.Column (name = "PasswordHash", nullable = false) + salt = table.Column (name = "Salt", nullable = true) }), constraints = fun table -> @@ -155,8 +154,8 @@ type InitialDatabase () = columns = (fun table -> { smallGroupId = table.Column (name = "SmallGroupId", nullable = false) - churchId = table.Column (name = "ChurchId", nullable = false) - name = table.Column (name = "Name", nullable = false) + churchId = table.Column (name = "ChurchId", nullable = false) + name = table.Column (name = "Name", nullable = false) }), constraints = fun table -> @@ -221,10 +220,10 @@ type InitialDatabase () = schema = "pt", columns = (fun table -> - { memberId = table.Column (name = "MemberId", nullable = false) - email = table.Column (name = "Email", nullable = false) - format = table.Column (name = "Format", nullable = true) - memberName = table.Column (name = "MemberName", nullable = false) + { memberId = table.Column (name = "MemberId", nullable = false) + email = table.Column (name = "Email", nullable = false) + format = table.Column (name = "Format", nullable = true) + memberName = table.Column (name = "MemberName", nullable = false) smallGroupId = table.Column (name = "SmallGroupId", nullable = false) }), constraints = @@ -246,16 +245,15 @@ type InitialDatabase () = columns = (fun table -> { prayerRequestId = table.Column (name = "PrayerRequestId", nullable = false) - doNotExpire = table.Column (name = "DoNotExpire", nullable = false) - enteredDate = table.Column (name = "EnteredDate", nullable = false) - isManuallyExpired = table.Column (name = "IsManuallyExpired", nullable = false) - notifyChaplain = table.Column (name = "NotifyChaplain", nullable = false) - requestType = table.Column (name = "RequestType", nullable = false) - requestor = table.Column (name = "Requestor", nullable = true) - smallGroupId = table.Column (name = "SmallGroupId", nullable = false) - text = table.Column (name = "Text", nullable = false) - updatedDate = table.Column (name = "UpdatedDate", nullable = false) - userId = table.Column (name = "UserId", nullable = false) + expiration = table.Column (name = "Expiration", nullable = false) + enteredDate = table.Column (name = "EnteredDate", nullable = false) + notifyChaplain = table.Column (name = "NotifyChaplain", nullable = false) + requestType = table.Column (name = "RequestType", nullable = false) + requestor = table.Column (name = "Requestor", nullable = true) + smallGroupId = table.Column (name = "SmallGroupId", nullable = false) + text = table.Column (name = "Text", nullable = false) + updatedDate = table.Column (name = "UpdatedDate", nullable = false) + userId = table.Column (name = "UserId", nullable = false) }), constraints = fun table -> @@ -283,7 +281,7 @@ type InitialDatabase () = schema = "pt", columns = (fun table -> - { userId = table.Column (name = "UserId", nullable = false) + { userId = table.Column (name = "UserId", nullable = false) smallGroupId = table.Column (name = "SmallGroupId", nullable = false) }), constraints = @@ -351,7 +349,7 @@ type InitialDatabase () = b.Property("smallGroupId") |> ignore b.Property("daysToExpire").ValueGeneratedOnAdd().HasDefaultValue(14) |> ignore b.Property("daysToKeepNew").ValueGeneratedOnAdd().HasDefaultValue(7) |> ignore - b.Property("defaultEmailType").IsRequired().ValueGeneratedOnAdd().HasDefaultValue("Html") |> ignore + b.Property("defaultEmailType").IsRequired().ValueGeneratedOnAdd().HasDefaultValue("H") |> ignore b.Property("emailFromAddress").IsRequired().ValueGeneratedOnAdd().HasDefaultValue("prayer@djs-consulting.com") |> ignore b.Property("emailFromName").IsRequired().ValueGeneratedOnAdd().HasDefaultValue("PrayerTracker") |> ignore b.Property("groupPassword").IsRequired().ValueGeneratedOnAdd().HasDefaultValue("") |> ignore @@ -388,11 +386,10 @@ type InitialDatabase () = typeof, fun b -> b.Property("prayerRequestId").ValueGeneratedOnAdd() |> ignore - b.Property("doNotExpire") |> ignore - b.Property("enteredDate") |> ignore - b.Property("isManuallyExpired") |> ignore + b.Property("enteredDate").IsRequired() |> ignore + b.Property("expiration").IsRequired().HasMaxLength 1 |> ignore b.Property("notifyChaplain") |> ignore - b.Property("requestType").IsRequired() |> ignore + b.Property("requestType").IsRequired().HasMaxLength 1 |> ignore b.Property("requestor") |> ignore b.Property("smallGroupId") |> ignore b.Property("text").IsRequired() |> ignore diff --git a/src/PrayerTracker.Data/Migrations/AppDbContextModelSnapshot.fs b/src/PrayerTracker.Data/Migrations/AppDbContextModelSnapshot.fs index 6df9c1c..5ae5d5f 100644 --- a/src/PrayerTracker.Data/Migrations/AppDbContextModelSnapshot.fs +++ b/src/PrayerTracker.Data/Migrations/AppDbContextModelSnapshot.fs @@ -36,7 +36,7 @@ type AppDbContextModelSnapshot () = b.Property("smallGroupId") |> ignore b.Property("daysToExpire").ValueGeneratedOnAdd().HasDefaultValue(14) |> ignore b.Property("daysToKeepNew").ValueGeneratedOnAdd().HasDefaultValue(7) |> ignore - b.Property("defaultEmailType").IsRequired().ValueGeneratedOnAdd().HasDefaultValue("Html") |> ignore + b.Property("defaultEmailType").IsRequired().ValueGeneratedOnAdd().HasDefaultValue("H").HasMaxLength(1) |> ignore b.Property("emailFromAddress").IsRequired().ValueGeneratedOnAdd().HasDefaultValue("prayer@djs-consulting.com") |> ignore b.Property("emailFromName").IsRequired().ValueGeneratedOnAdd().HasDefaultValue("PrayerTracker") |> ignore b.Property("groupPassword").IsRequired().ValueGeneratedOnAdd().HasDefaultValue("") |> ignore @@ -73,11 +73,10 @@ type AppDbContextModelSnapshot () = typeof, fun b -> b.Property("prayerRequestId").ValueGeneratedOnAdd() |> ignore - b.Property("doNotExpire") |> ignore b.Property("enteredDate") |> ignore - b.Property("isManuallyExpired") |> ignore + b.Property("expiration").IsRequired().HasMaxLength(1) |> ignore b.Property("notifyChaplain") |> ignore - b.Property("requestType").IsRequired() |> ignore + b.Property("requestType").IsRequired().HasMaxLength(1) |> ignore b.Property("requestor") |> ignore b.Property("smallGroupId") |> ignore b.Property("text").IsRequired() |> ignore diff --git a/src/PrayerTracker.Tests/Data/EntitiesTests.fs b/src/PrayerTracker.Tests/Data/EntitiesTests.fs index 241c20d..d6bf322 100644 --- a/src/PrayerTracker.Tests/Data/EntitiesTests.fs +++ b/src/PrayerTracker.Tests/Data/EntitiesTests.fs @@ -48,6 +48,52 @@ let churchTests = } ] +[] +let emailFormatTests = + testList "EmailFormat" [ + test "HtmlFormat code is correct" { + Expect.equal HtmlFormat.code "H" "The code for HtmlFormat should have been \"H\"" + } + test "PlainTextFormat code is correct" { + Expect.equal PlainTextFormat.code "P" "The code for PlainTextFormat should have been \"P\"" + } + test "fromCode H should return HtmlFormat" { + Expect.equal (EmailFormat.fromCode "H") HtmlFormat "\"H\" should have been converted to HtmlFormat" + } + test "fromCode P should return ShortDate" { + Expect.equal (EmailFormat.fromCode "P") PlainTextFormat "\"P\" should have been converted to PlainTextFormat" + } + test "fromCode Z should raise" { + Expect.throws (fun () -> EmailFormat.fromCode "Z" |> ignore) "An unknown code should have raised an exception" + } + ] + +[] +let expirationTests = + testList "Expiration" [ + test "Automatic code is correct" { + Expect.equal Automatic.code "A" "The code for Automatic should have been \"A\"" + } + test "Manual code is correct" { + Expect.equal Manual.code "M" "The code for Manual should have been \"M\"" + } + test "Forced code is correct" { + Expect.equal Forced.code "F" "The code for Forced should have been \"F\"" + } + test "fromCode A should return Automatic" { + Expect.equal (Expiration.fromCode "A") Automatic "\"A\" should have been converted to Automatic" + } + test "fromCode M should return Manual" { + Expect.equal (Expiration.fromCode "M") Manual "\"M\" should have been converted to Manual" + } + test "fromCode F should return Forced" { + Expect.equal (Expiration.fromCode "F") Forced "\"F\" should have been converted to Forced" + } + test "fromCode V should raise" { + Expect.throws (fun () -> Expiration.fromCode "V" |> ignore) "An unknown code should have raised an exception" + } + ] + [] let listPreferencesTests = testList "ListPreferences" [ @@ -67,7 +113,7 @@ let listPreferencesTests = Expect.equal mt.textFontSize 12 "The default text font size should have been 12" Expect.equal mt.requestSort SortByDate "The default request sort should have been by date" Expect.equal mt.groupPassword "" "The default group password should have been blank" - Expect.equal mt.defaultEmailType EmailType.Html "The default e-mail type should have been HTML" + Expect.equal mt.defaultEmailType HtmlFormat "The default e-mail type should have been HTML" Expect.isFalse mt.isPublic "The isPublic flag should not have been set" Expect.equal mt.timeZoneId "America/Denver" "The default time zone should have been America/Denver" Expect.equal mt.timeZone.timeZoneId "" "The default preferences should have included an empty time zone" @@ -96,34 +142,33 @@ let prayerRequestTests = test "empty is as expected" { let mt = PrayerRequest.empty Expect.equal mt.prayerRequestId Guid.Empty "The request ID should have been an empty GUID" - Expect.equal mt.requestType RequestType.Current "The request type should have been Current" + Expect.equal mt.requestType CurrentRequest "The request type should have been Current" Expect.equal mt.userId Guid.Empty "The user ID should have been an empty GUID" Expect.equal mt.smallGroupId Guid.Empty "The small group ID should have been an empty GUID" Expect.equal mt.enteredDate DateTime.MinValue "The entered date should have been the minimum" Expect.equal mt.updatedDate DateTime.MinValue "The updated date should have been the minimum" Expect.isNone mt.requestor "The requestor should not exist" Expect.equal mt.text "" "The request text should have been blank" - Expect.isFalse mt.doNotExpire "The do not expire flag should not have been set" Expect.isFalse mt.notifyChaplain "The notify chaplain flag should not have been set" - Expect.isFalse mt.isManuallyExpired "The is manually expired flag should not have been set" + Expect.equal mt.expiration Automatic "The expiration should have been Automatic" Expect.equal mt.user.userId Guid.Empty "The user should have been an empty one" Expect.equal mt.smallGroup.smallGroupId Guid.Empty "The small group should have been an empty one" } test "isExpired always returns false for expecting requests" { - let req = { PrayerRequest.empty with requestType = RequestType.Expecting } + let req = { PrayerRequest.empty with requestType = Expecting } Expect.isFalse (req.isExpired DateTime.Now 0) "An expecting request should never be considered expired" } - test "isExpired always returns false for never-expired requests" { - let req = { PrayerRequest.empty with updatedDate = DateTime.Now.AddMonths -1; doNotExpire = true } + test "isExpired always returns false for manually-expired requests" { + let req = { PrayerRequest.empty with updatedDate = DateTime.Now.AddMonths -1; expiration = Manual } Expect.isFalse (req.isExpired DateTime.Now 4) "A never-expired request should never be considered expired" } - test "isExpired always returns false for recurring requests" { - let req = { PrayerRequest.empty with requestType = RequestType.Recurring } + test "isExpired always returns false for long term/recurring requests" { + let req = { PrayerRequest.empty with requestType = LongTermRequest } Expect.isFalse (req.isExpired DateTime.Now 0) "A recurring/long-term request should never be considered expired" } - test "isExpired always returns true for manually expired requests" { - let req = { PrayerRequest.empty with updatedDate = DateTime.Now; isManuallyExpired = true } - Expect.isTrue (req.isExpired DateTime.Now 5) "A manually expired request should always be considered expired" + test "isExpired always returns true for force-expired requests" { + let req = { PrayerRequest.empty with updatedDate = DateTime.Now; expiration = Forced } + Expect.isTrue (req.isExpired DateTime.Now 5) "A force-expired request should always be considered expired" } test "isExpired returns false for non-expired requests" { let req = { PrayerRequest.empty with updatedDate = DateTime.Now.AddDays -5. } @@ -134,13 +179,13 @@ let prayerRequestTests = Expect.isTrue (req.isExpired DateTime.Now 7) "A request updated 8 days ago should be considered expired" } test "updateRequired returns false for expired requests" { - let req = { PrayerRequest.empty with isManuallyExpired = true } + let req = { PrayerRequest.empty with expiration = Forced } Expect.isFalse (req.updateRequired DateTime.Now 7 4) "An expired request should not require an update" } test "updateRequired returns false when an update is not required for an active request" { let req = { PrayerRequest.empty with - requestType = RequestType.Recurring + requestType = LongTermRequest updatedDate = DateTime.Now.AddDays -14. } Expect.isFalse (req.updateRequired DateTime.Now 7 4) @@ -149,7 +194,7 @@ let prayerRequestTests = test "updateRequired returns true when an update is required for an active request" { let req = { PrayerRequest.empty with - requestType = RequestType.Recurring + requestType = LongTermRequest updatedDate = DateTime.Now.AddDays -34. } Expect.isTrue (req.updateRequired DateTime.Now 7 4) @@ -157,6 +202,47 @@ let prayerRequestTests = } ] +[] +let prayerRequestTypeTests = + testList "PrayerRequestType" [ + test "CurrentRequest code is correct" { + Expect.equal CurrentRequest.code "C" "The code for CurrentRequest should have been \"C\"" + } + test "LongTermRequest code is correct" { + Expect.equal LongTermRequest.code "L" "The code for LongTermRequest should have been \"L\"" + } + test "PraiseReport code is correct" { + Expect.equal PraiseReport.code "P" "The code for PraiseReport should have been \"P\"" + } + test "Expecting code is correct" { + Expect.equal Expecting.code "E" "The code for Expecting should have been \"E\"" + } + test "Announcement code is correct" { + Expect.equal Announcement.code "A" "The code for Announcement should have been \"A\"" + } + test "fromCode C should return CurrentRequest" { + Expect.equal (PrayerRequestType.fromCode "C") CurrentRequest + "\"C\" should have been converted to CurrentRequest" + } + test "fromCode L should return LongTermRequest" { + Expect.equal (PrayerRequestType.fromCode "L") LongTermRequest + "\"L\" should have been converted to LongTermRequest" + } + test "fromCode P should return PraiseReport" { + Expect.equal (PrayerRequestType.fromCode "P") PraiseReport "\"P\" should have been converted to PraiseReport" + } + test "fromCode E should return Expecting" { + Expect.equal (PrayerRequestType.fromCode "E") Expecting "\"E\" should have been converted to Expecting" + } + test "fromCode A should return Announcement" { + Expect.equal (PrayerRequestType.fromCode "A") Announcement "\"A\" should have been converted to Announcement" + } + test "fromCode R should raise" { + Expect.throws (fun () -> PrayerRequestType.fromCode "R" |> ignore) + "An unknown code should have raised an exception" + } + ] + [] let requestSortTests = testList "RequestSort" [ diff --git a/src/PrayerTracker.Tests/UI/ViewModelsTests.fs b/src/PrayerTracker.Tests/UI/ViewModelsTests.fs index c831f86..ac4a513 100644 --- a/src/PrayerTracker.Tests/UI/ViewModelsTests.fs +++ b/src/PrayerTracker.Tests/UI/ViewModelsTests.fs @@ -31,15 +31,15 @@ module ReferenceListTests = let emailTypeListTests = testList "ReferenceList.emailTypeList" [ test "includes default type" { - let typs = ReferenceList.emailTypeList EmailType.Html _s + let typs = ReferenceList.emailTypeList HtmlFormat _s Expect.hasCountOf typs 3u countAll "There should have been 3 e-mail type options returned" let top = Seq.head typs Expect.equal (fst top) "" "The default option should have been blank" Expect.equal (snd top).Value "Group Default (HTML Format)" "The default option label was incorrect" let nxt = typs |> Seq.skip 1 |> Seq.head - Expect.equal (fst nxt) EmailType.Html "The 2nd option should have been HTML" + Expect.equal (fst nxt) HtmlFormat.code "The 2nd option should have been HTML" let lst = typs |> Seq.last - Expect.equal (fst lst) EmailType.PlainText "The 3rd option should have been plain text" + Expect.equal (fst lst) PlainTextFormat.code "The 3rd option should have been plain text" } ] @@ -49,15 +49,15 @@ module ReferenceListTests = test "excludes immediate expiration if not required" { let exps = ReferenceList.expirationList _s false Expect.hasCountOf exps 2u countAll "There should have been 2 expiration types returned" - Expect.exists exps (fun exp -> fst exp = "N") "The option for normal expiration was not found" - Expect.exists exps (fun exp -> fst exp = "Y") "The option for \"never expire\" was not found" + Expect.exists exps (fun (exp, _) -> exp = Automatic.code) "The option for automatic expiration was not found" + Expect.exists exps (fun (exp, _) -> exp = Manual.code) "The option for manual expiration was not found" } test "includes immediate expiration if required" { let exps = ReferenceList.expirationList _s true Expect.hasCountOf exps 3u countAll "There should have been 3 expiration types returned" - Expect.exists exps (fun exp -> fst exp = "N") "The option for normal expiration was not found" - Expect.exists exps (fun exp -> fst exp = "Y") "The option for \"never expire\" was not found" - Expect.exists exps (fun exp -> fst exp = "X") "The option for \"expire immediately\" was not found" + Expect.exists exps (fun (exp, _) -> exp = Automatic.code) "The option for automatic expiration was not found" + Expect.exists exps (fun (exp, _) -> exp = Manual.code) "The option for manual expiration was not found" + Expect.exists exps (fun (exp, _) -> exp = Forced.code) "The option for immediate expiration was not found" } ] @@ -69,14 +69,12 @@ module ReferenceListTests = yield! testFixture withList [ yield "returns 5 types", fun typs -> Expect.hasCountOf typs 5u countAll "There should have been 5 request types returned" - yield! [ RequestType.Current; RequestType.Recurring; RequestType.Praise; RequestType.Expecting; - RequestType.Announcement - ] - |> List.map (fun typ -> - sprintf "contains \"%s\"" typ, - fun typs -> - Expect.isSome (typs |> List.tryFind (fun x -> fst x = typ)) - (sprintf "The \"%s\" option was not found" typ)) + yield! [ CurrentRequest; LongTermRequest; PraiseReport; Expecting; Announcement ] + |> List.map (fun typ -> + sprintf "contains \"%O\"" typ, + fun typs -> + Expect.isSome (typs |> List.tryFind (fun x -> fst x = typ)) + (sprintf "The \"%O\" option was not found" typ)) ] ] @@ -232,8 +230,8 @@ let editMemberTests = Expect.equal edit.emailType "" "The e-mail type should have been blank for group default" } test "fromMember populates with specific format" { - let edit = EditMember.fromMember { Member.empty with format = Some EmailType.Html } - Expect.equal edit.emailType EmailType.Html "The e-mail type was not filled correctly" + let edit = EditMember.fromMember { Member.empty with format = Some HtmlFormat.code } + Expect.equal edit.emailType HtmlFormat.code "The e-mail type was not filled correctly" } test "empty is as expected" { let edit = EditMember.empty @@ -263,7 +261,7 @@ let editPreferencesTests = Expect.equal edit.requestSort prefs.requestSort.code "The request sort was not filled correctly" Expect.equal edit.emailFromName prefs.emailFromName "The e-mail from name was not filled correctly" Expect.equal edit.emailFromAddress prefs.emailFromAddress "The e-mail from address was not filled correctly" - Expect.equal edit.defaultEmailType prefs.defaultEmailType "The default e-mail type was not filled correctly" + Expect.equal edit.defaultEmailType prefs.defaultEmailType.code "The default e-mail type was not filled correctly" Expect.equal edit.headingLineType "Name" "The heading line color type was not derived correctly" Expect.equal edit.headingLineColor prefs.lineColor "The heading line color was not filled correctly" Expect.equal edit.headingTextType "Name" "The heading text color type was not derived correctly" @@ -303,39 +301,29 @@ let editRequestTests = test "empty is as expected" { let mt = EditRequest.empty Expect.equal mt.requestId Guid.Empty "The request ID should be an empty GUID" - Expect.equal mt.requestType "" "The request type should have been blank" + Expect.equal mt.requestType CurrentRequest.code "The request type should have been \"Current\"" Expect.isNone mt.enteredDate "The entered date should have been None" Expect.isNone mt.skipDateUpdate "The \"skip date update\" flag should have been None" Expect.isNone mt.requestor "The requestor should have been None" - Expect.equal mt.expiration "N" "The expiration should have been \"N\"" + Expect.equal mt.expiration Automatic.code "The expiration should have been \"A\" (Automatic)" Expect.equal mt.text "" "The text should have been blank" } - test "fromRequest succeeds when a request has the do-not-expire flag set" { + test "fromRequest succeeds" { let req = { PrayerRequest.empty with prayerRequestId = Guid.NewGuid () - requestType = RequestType.Current + requestType = CurrentRequest requestor = Some "Me" - doNotExpire = true + expiration = Manual text = "the text" } let edit = EditRequest.fromRequest req Expect.equal edit.requestId req.prayerRequestId "The request ID was not filled correctly" - Expect.equal edit.requestType req.requestType "The request type was not filled correctly" + Expect.equal edit.requestType req.requestType.code "The request type was not filled correctly" Expect.equal edit.requestor req.requestor "The requestor was not filled correctly" - Expect.equal edit.expiration "Y" "The expiration should have been \"Y\" since the do-not-expire flag was set" + Expect.equal edit.expiration Manual.code "The expiration was not filled correctly" Expect.equal edit.text req.text "The text was not filled correctly" } - test "fromRequest succeeds when a request has the do-not-expire flag unset" { - let req = - { PrayerRequest.empty with - requestor = None - doNotExpire = false - } - let edit = EditRequest.fromRequest req - Expect.equal edit.requestor req.requestor "The requestor was not filled correctly" - Expect.equal edit.expiration "N" "The expiration should have been \"N\" since the do-not-expire flag was not set" - } test "isNew works for a new request" { Expect.isTrue (EditRequest.empty.isNew ()) "An empty GUID should be flagged as a new request" } @@ -469,19 +457,19 @@ let requestListTests = let withRequestList f () = { requests = [ { PrayerRequest.empty with - requestType = RequestType.Current + requestType = CurrentRequest requestor = Some "Zeb" text = "zyx" updatedDate = DateTime.Today } { PrayerRequest.empty with - requestType = RequestType.Current + requestType = CurrentRequest requestor = Some "Aaron" text = "abc" updatedDate = DateTime.Today - TimeSpan.FromDays 9. } { PrayerRequest.empty with - requestType = RequestType.Praise + requestType = PraiseReport text = "nmo" updatedDate = DateTime.Today } @@ -566,19 +554,19 @@ let requestListTests = Expect.stringContains text " + nmo\n \n" "Last request not found" "isNew succeeds for both old and new requests", fun reqList -> - let reqs = reqList.requestsInCategory RequestType.Current + let reqs = reqList.requestsInCategory CurrentRequest Expect.hasCountOf reqs 2u countAll "There should have been two requests" Expect.isTrue (reqList.isNew (List.head reqs)) "The first request should have been new" Expect.isFalse (reqList.isNew (List.last reqs)) "The second request should not have been new" "requestsInCategory succeeds when requests exist", fun reqList -> - let reqs = reqList.requestsInCategory RequestType.Current + let reqs = reqList.requestsInCategory CurrentRequest Expect.hasCountOf reqs 2u countAll "There should have been two requests" let first = List.head reqs Expect.equal first.text "zyx" "The requests should be sorted by updated date descending" "requestsInCategory succeeds when requests do not exist", fun reqList -> - Expect.isEmpty (reqList.requestsInCategory "ABC") "There should have been no category \"ABC\" requests" + Expect.isEmpty (reqList.requestsInCategory Announcement) "There should have been no \"Announcement\" requests" "requestsInCategory succeeds and sorts by requestor", fun reqList -> let newList = @@ -588,7 +576,7 @@ let requestListTests = preferences = { reqList.listGroup.preferences with requestSort = SortByRequestor } } } - let reqs = newList.requestsInCategory RequestType.Current + let reqs = newList.requestsInCategory CurrentRequest Expect.hasCountOf reqs 2u countAll "There should have been two requests" let first = List.head reqs Expect.equal first.text "abc" "The requests should be sorted by requestor" diff --git a/src/PrayerTracker.UI/PrayerRequest.fs b/src/PrayerTracker.UI/PrayerRequest.fs index adc9956..9e4ec15 100644 --- a/src/PrayerTracker.UI/PrayerRequest.fs +++ b/src/PrayerTracker.UI/PrayerRequest.fs @@ -23,7 +23,7 @@ let edit (m : EditRequest) today ctx vi = label [ _for "requestType" ] [ locStr s.["Request Type"] ] ReferenceList.requestTypeList s |> Seq.ofList - |> Seq.map (fun item -> fst item, (snd item).Value) + |> Seq.map (fun (typ, desc) -> typ.code, desc.Value) |> selectList "requestType" m.requestType [ _required; _autofocus ] ] yield div [ _class "pt-field" ] [ diff --git a/src/PrayerTracker.UI/SmallGroup.fs b/src/PrayerTracker.UI/SmallGroup.fs index 15a92ef..8e5235e 100644 --- a/src/PrayerTracker.UI/SmallGroup.fs +++ b/src/PrayerTracker.UI/SmallGroup.fs @@ -45,7 +45,7 @@ let announcement isAdmin ctx vi = label [ _for "requestType" ] [ locStr s.["Request Type"] ] reqTypes |> Seq.ofList - |> Seq.map (fun item -> fst item, (snd item).Value) + |> Seq.map (fun (typ, desc) -> typ.code, desc.Value) |> selectList "requestType" "Announcement" [] ] ] @@ -407,7 +407,9 @@ let preferences (m : EditPreferences) (tzs : TimeZone list) ctx vi = label [ _for "defaultEmailType" ] [ locStr s.["E-mail Format"] ] seq { yield "", selectDefault s.["Select"].Value - yield! ReferenceList.emailTypeList "" s |> Seq.skip 1 |> Seq.map (fun typ -> fst typ, (snd typ).Value) + yield! ReferenceList.emailTypeList HtmlFormat s + |> Seq.skip 1 + |> Seq.map (fun typ -> fst typ, (snd typ).Value) } |> selectList "defaultEmailType" m.defaultEmailType [ _required ] ] diff --git a/src/PrayerTracker.UI/ViewModels.fs b/src/PrayerTracker.UI/ViewModels.fs index fd9307c..071f22d 100644 --- a/src/PrayerTracker.UI/ViewModels.fs +++ b/src/PrayerTracker.UI/ViewModels.fs @@ -22,30 +22,28 @@ module ReferenceList = // Localize the default type let defaultType = match def with - | EmailType.Html -> s.["HTML Format"].Value - | EmailType.PlainText -> s.["Plain-Text Format"].Value - | EmailType.AttachedPdf -> s.["Attached PDF"].Value - | _ -> "" + | HtmlFormat -> s.["HTML Format"].Value + | PlainTextFormat -> s.["Plain-Text Format"].Value seq { yield "", LocalizedString ("", sprintf "%s (%s)" s.["Group Default"].Value defaultType) - yield EmailType.Html, s.["HTML Format"] - yield EmailType.PlainText, s.["Plain-Text Format"] + yield HtmlFormat.code, s.["HTML Format"] + yield PlainTextFormat.code, s.["Plain-Text Format"] } /// A list of expiration options let expirationList (s : IStringLocalizer) includeExpireNow = - [ yield "N", s.["Expire Normally"] - yield "Y", s.["Request Never Expires"] - match includeExpireNow with true -> yield "X", s.["Expire Immediately"] | false -> () + [ yield Automatic.code, s.["Expire Normally"] + yield Manual.code, s.["Request Never Expires"] + match includeExpireNow with true -> yield Forced.code, s.["Expire Immediately"] | false -> () ] /// A list of request types let requestTypeList (s : IStringLocalizer) = - [ RequestType.Current, s.["Current Requests"] - RequestType.Recurring, s.["Long-Term Requests"] - RequestType.Praise, s.["Praise Reports"] - RequestType.Expecting, s.["Expecting"] - RequestType.Announcement, s.["Announcements"] + [ CurrentRequest, s.["Current Requests"] + LongTermRequest, s.["Long-Term Requests"] + PraiseReport, s.["Praise Reports"] + Expecting, s.["Expecting"] + Announcement, s.["Announcements"] ] @@ -292,7 +290,7 @@ with requestSort = prefs.requestSort.code emailFromName = prefs.emailFromName emailFromAddress = prefs.emailFromAddress - defaultEmailType = prefs.defaultEmailType + defaultEmailType = prefs.defaultEmailType.code headingLineType = setType prefs.lineColor headingLineColor = prefs.lineColor headingTextType = setType prefs.headingColor @@ -325,7 +323,7 @@ with requestSort = RequestSort.fromCode this.requestSort emailFromName = this.emailFromName emailFromAddress = this.emailFromAddress - defaultEmailType = this.defaultEmailType + defaultEmailType = EmailFormat.fromCode this.defaultEmailType lineColor = this.headingLineColor headingColor = this.headingTextColor listFonts = this.listFonts @@ -362,20 +360,20 @@ with /// An empty instance to use for new requests static member empty = { requestId = Guid.Empty - requestType = "" + requestType = CurrentRequest.code enteredDate = None skipDateUpdate = None requestor = None - expiration = "N" + expiration = Automatic.code text = "" } /// Create an instance from an existing request static member fromRequest req = { EditRequest.empty with requestId = req.prayerRequestId - requestType = req.requestType + requestType = req.requestType.code requestor = req.requestor - expiration = match req.doNotExpire with true -> "Y" | false -> "N" + expiration = req.expiration.code text = req.text } /// Is this a new request? @@ -516,7 +514,7 @@ type Overview = { /// The total number of active requests totalActiveReqs : int /// The numbers of active requests by category - activeReqsByCat : Map + activeReqsByCat : Map /// A count of all requests allReqs : int /// A count of all members diff --git a/src/PrayerTracker/Email.fs b/src/PrayerTracker/Email.fs index b103ffb..a27f5b7 100644 --- a/src/PrayerTracker/Email.fs +++ b/src/PrayerTracker/Email.fs @@ -67,18 +67,15 @@ let sendEmails (client : SmtpClient) (recipients : Member list) grp subj html te let plainTextMsg = createTextMessage grp subj text s for mbr in recipients do - let emailType = match mbr.format with Some f -> f | None -> grp.preferences.defaultEmailType - let emailTo = MailboxAddress (mbr.memberName, mbr.email) + let emailType = match mbr.format with Some f -> EmailFormat.fromCode f | None -> grp.preferences.defaultEmailType + let emailTo = MailboxAddress (mbr.memberName, mbr.email) match emailType with - | EmailType.Html -> + | HtmlFormat -> htmlMsg.To.Add emailTo do! client.SendAsync htmlMsg htmlMsg.To.Clear () - | EmailType.PlainText -> + | PlainTextFormat -> plainTextMsg.To.Add emailTo do! client.SendAsync plainTextMsg plainTextMsg.To.Clear () - | EmailType.AttachedPdf -> - raise <| NotImplementedException "Attached PDF format has not been implemented" - | _ -> invalidOp <| sprintf "Unknown e-mail type %s passed" emailType } diff --git a/src/PrayerTracker/PrayerRequest.fs b/src/PrayerTracker/PrayerRequest.fs index 6f1d75b..aaf638f 100644 --- a/src/PrayerTracker/PrayerRequest.fs +++ b/src/PrayerTracker/PrayerRequest.fs @@ -136,7 +136,7 @@ let expire reqId : HttpHandler = | Ok r -> let db = ctx.dbContext () let s = Views.I18N.localizer.Force () - db.UpdateEntry { r with isManuallyExpired = true } + db.UpdateEntry { r with expiration = Forced } let! _ = db.SaveChangesAsync () addInfo ctx s.["Successfully {0} prayer request", s.["Expired"].Value.ToLower ()] return! redirectTo false "/prayer-requests" next ctx @@ -247,7 +247,7 @@ let restore reqId : HttpHandler = | Ok r -> let db = ctx.dbContext () let s = Views.I18N.localizer.Force () - db.UpdateEntry { r with isManuallyExpired = false; updatedDate = DateTime.Now } + db.UpdateEntry { r with expiration = Automatic; updatedDate = DateTime.Now } let! _ = db.SaveChangesAsync () addInfo ctx s.["Successfully {0} prayer request", s.["Restored"].Value.ToLower ()] return! redirectTo false "/prayer-requests" next ctx @@ -273,11 +273,10 @@ let save : HttpHandler = | Some pr -> let upd8 = { pr with - requestType = m.requestType - requestor = m.requestor - text = ckEditorToText m.text - doNotExpire = m.expiration = "Y" - isManuallyExpired = m.expiration = "X" + requestType = PrayerRequestType.fromCode m.requestType + requestor = m.requestor + text = ckEditorToText m.text + expiration = Expiration.fromCode m.expiration } let grp = currentGroup ctx let now = grp.localDateNow (ctx.GetService ()) diff --git a/src/PrayerTracker/SmallGroup.fs b/src/PrayerTracker/SmallGroup.fs index 00cfff2..2384f48 100644 --- a/src/PrayerTracker/SmallGroup.fs +++ b/src/PrayerTracker/SmallGroup.fs @@ -387,7 +387,7 @@ let sendAnnouncement : HttpHandler = prayerRequestId = Guid.NewGuid () smallGroupId = grp.smallGroupId userId = usr.userId - requestType = Option.get m.requestType + requestType = (Option.get >> PrayerRequestType.fromCode) m.requestType text = requestText enteredDate = now updatedDate = now diff --git a/src/search-sql.txt b/src/search-sql.txt index d03bcec..6edad08 100644 --- a/src/search-sql.txt +++ b/src/search-sql.txt @@ -4,3 +4,24 @@ create index "IX_PrayerRequest_Requestor_TRGM" on "PrayerRequest" using GIN (COA create index "IX_PrayerRequest_Text_TRGM" on "PrayerRequest" using GIN ("Text" gin_trgm_ops); alter table "ListPreference" add column "PageSize" int not null default 100; alter table "ListPreference" add column "AsOfDateDisplay" varchar(1) not null default 'N'; +/* RequestType to 1 character code */ +update "PrayerRequest" set "RequestType" = 'C' where "RequestType" = 'Current'; +update "PrayerRequest" set "RequestType" = 'L' where "RequestType" = 'Recurring'; +update "PrayerRequest" set "RequestType" = 'P' where "RequestType" = 'Praise'; +update "PrayerRequest" set "RequestType" = 'E' where "RequestType" = 'Expecting'; +update "PrayerRequest" set "RequestType" = 'A' where "RequestType" = 'Announcement'; +alter table "PrayerRequest" alter column "RequestType" set data type varchar(1); +/* Change expiration to a 1-character code field */ +alter table "PrayerRequest" add column "Expiration" varchar(1); +update "PrayerRequest" set "Expiration" = case when "IsManuallyExpired" then 'F' when "DoNotExpire" then 'M' else 'A' end; +alter table "PrayerRequest" alter column "Expiration" set not null; +alter table "PrayerRequest" drop column "DoNotExpire"; +alter table "PrayerRequest" drop column "IsManuallyExpired"; +/* Change e-mail type to 1-character code field in list preferences and members */ +update "ListPreference" set "DefaultEmailType" = 'H' where "DefaultEmailType" = 'Html'; +update "ListPreference" set "DefaultEmailType" = 'P' where "DefaultEmailType" = 'Text'; +alter table "ListPreference" alter column "DefaultEmailType" set default 'H'; +alter table "ListPreference" alter column "DefaultEmailType" set data type varchar(1); +update "Member" set "Format" = 'H' where "Format" = 'Html'; +update "Member" set "Format" = 'P' where "Format" = 'Text'; +alter table "Member" alter column "Format" set data type varchar(1);